package factcheck import ( "context" "encoding/json" "os" "testing" "aigateway.local/core/internal/platform/config" "aigateway.local/core/internal/platform/database" ) func TestFactCheckPostgreSQLLifecycle(t *testing.T) { databaseURL := os.Getenv("FACTCHECK_TEST_DATABASE_URL") if databaseURL == "" { t.Skip("FACTCHECK_TEST_DATABASE_URL is not set") } ctx := context.Background() pool, err := database.Open(ctx, config.Database{URL: databaseURL, MaxConns: 4}) if err != nil { t.Fatal(err) } defer pool.Close() const actorID = "33333333-3333-4333-8333-333333333333" const knowledgeBaseID = "44444444-4444-4444-8444-444444444444" const eventID = "55555555-5555-4555-8555-555555555555" _, err = pool.Exec(ctx, `INSERT INTO gateway.admin_accounts(id,username,password_hash,role) VALUES($1,'factcheck-test-admin','test','superadmin') ON CONFLICT(id) DO NOTHING`, actorID) if err != nil { t.Fatal(err) } cleanup := func() { _, _ = pool.Exec(ctx, `DELETE FROM gateway.fact_check_events WHERE id=$1`, eventID) _, _ = pool.Exec(ctx, `DELETE FROM gateway.fact_check_policies WHERE scope='global'`) _, _ = pool.Exec(ctx, `DELETE FROM gateway.knowledge_bases WHERE id=$1`, knowledgeBaseID) _, _ = pool.Exec(ctx, `UPDATE gateway.fact_check_settings SET provider_id=NULL,model='',updated_by=NULL`) _, _ = pool.Exec(ctx, `DELETE FROM gateway.admin_accounts WHERE id=$1`, actorID) } cleanup() _, err = pool.Exec(ctx, `INSERT INTO gateway.admin_accounts(id,username,password_hash,role) VALUES($1,'factcheck-test-admin','test','superadmin') ON CONFLICT(id) DO NOTHING`, actorID) if err != nil { t.Fatal(err) } defer cleanup() _, err = pool.Exec(ctx, `INSERT INTO gateway.knowledge_bases(id,name,retrieval_mode,chunk_size,chunk_overlap,created_by) VALUES($1,'factcheck-test-kb','postgres_fts',800,100,$2)`, knowledgeBaseID, actorID) if err != nil { t.Fatal(err) } service := NewService(pool) settings, err := service.SaveSettings(ctx, Settings{TimeoutSeconds: 17}, actorID) if err != nil || settings.TimeoutSeconds != 17 || settings.ProviderID != nil { t.Fatalf("settings=%#v err=%v", settings, err) } policy, err := service.SavePolicy(ctx, Policy{ Scope: "global", Enabled: true, Mode: "sync", Action: "annotate", KnowledgeBaseIDs: []string{knowledgeBaseID}, }, actorID) if err != nil { t.Fatal(err) } if policy.Revision != 1 || policy.TopK != 4 || policy.SupportThreshold != 70 { t.Fatalf("unexpected policy defaults: %#v", policy) } policy.Action = "block" updated, err := service.SavePolicy(ctx, policy, actorID) if err != nil || updated.Revision != 2 || updated.Action != "block" { t.Fatalf("updated=%#v err=%v", updated, err) } claims := json.RawMessage(`[{"text":"Go gateway"}]`) evidence := json.RawMessage(`[{"source":"kb"}]`) _, err = pool.Exec(ctx, `INSERT INTO gateway.fact_check_events (id,policy_id,request_id,model,mode,action,verdict,support_score,latency_ms,question,answer,claims,evidence) VALUES($1,$2,'factcheck-request','test-model','sync','block','supported',92,12,'question','answer',$3,$4)`, eventID, policy.ID, claims, evidence) if err != nil { t.Fatal(err) } events, err := service.Events(ctx, "supported", 10) if err != nil || len(events) != 1 { t.Fatalf("events=%#v err=%v", events, err) } if events[0].Question != "" || events[0].Answer != "" || events[0].Claims != nil { t.Fatalf("event list leaked payload: %#v", events[0]) } detail, err := service.Event(ctx, eventID) if err != nil || detail.Question != "question" || len(detail.Evidence) == 0 { t.Fatalf("detail=%#v err=%v", detail, err) } if err = service.DeletePolicy(ctx, policy.ID); err != nil { t.Fatal(err) } }