//go:build integration // Phase 5a tenant mission tagging, exercised against a REAL Postgres with RLS in force. It seeds an org // with a candidate whose headline mentions SIGINT, runs the tenant tagging service inside that org's // context (so every read and write goes through RunInTenantTx as the NOBYPASSRLS application role), and // asserts a candidate_mission_tags row with code SIGINT exists for that org and that a second org cannot // see it (RLS isolation). Mission tags are never a scoring input and never reach the eeo schema; this test // exercises the tagging service end to end and the tenancy guarantee, complementing the static // mission_tags_integration_test which exercises the raw upsert. package integration import ( "context" "testing" "github.com/jackc/pgx/v5" "github.com/missionworkforce/missionmatch/internal/modules/federalsignal" "github.com/missionworkforce/missionmatch/internal/platform/database/dbgen" "github.com/missionworkforce/missionmatch/internal/platform/missionclass" ) func TestTenantTagServiceTagsCandidatesUnderRLS(t *testing.T) { orgA, orgB := seedOrg(t), seedOrg(t) // org A candidate whose headline mentions SIGINT, seeded via the migrator so the headline is set. hn := "SIGINT collection analyst" candA, err := dbgen.New(migrator).CreateCandidate(context.Background(), dbgen.CreateCandidateParams{ OrganizationID: orgA, FullName: "Alice Analyst", Headline: &hn, ClearanceLevel: "top_secret", ClearancePoly: "none", ClearanceStatus: "active", Available: true, }) if err != nil { t.Fatalf("seed org A candidate: %v", err) } // An empty AliasIndex: the SIGINT tag comes from the literal keyword in the headline, so this test does // not depend on the curated alias rows being present in the database under test. idx := missionclass.NewAliasIndex(map[string]string{}) svc := federalsignal.NewTenantTagService(federalsignal.NewTenantTagRepo(appDB), idx) // Tag org A's candidates in org A's context. Every read and write runs as the app role under RLS. res, err := svc.TagCandidatesForOrg(ctxOrg(orgA)) if err != nil { t.Fatalf("TagCandidatesForOrg (org A): %v", err) } if res.CandidatesScanned < 1 || res.CandidateTags < 1 { t.Fatalf("org A tagging produced no tags: %+v", res) } // Org A MUST see exactly one SIGINT tag for its candidate, with the keyword mechanism. var code, mechanism string if err := appDB.RunInTenantTx(ctxOrg(orgA), func(ctx context.Context, tx pgx.Tx) error { return tx.QueryRow(ctx, "SELECT mission_code, mechanism FROM candidate_mission_tags WHERE candidate_id = $1 AND mission_code = 'SIGINT'", candA.ID).Scan(&code, &mechanism) }); err != nil { t.Fatalf("org A read SIGINT tag: %v", err) } if code != "SIGINT" || mechanism != missionclass.MechKeyword { t.Fatalf("org A SIGINT tag = code %q mechanism %q, want SIGINT/keyword", code, mechanism) } // Org B MUST NOT see org A's tag: a count under org B's session is zero (RLS default deny). var nB int if err := appDB.RunInTenantTx(ctxOrg(orgB), func(ctx context.Context, tx pgx.Tx) error { return tx.QueryRow(ctx, "SELECT count(*) FROM candidate_mission_tags WHERE candidate_id = $1", candA.ID).Scan(&nB) }); err != nil { t.Fatalf("org B count tags: %v", err) } if nB != 0 { t.Fatalf("ISOLATION BREACH: org B saw %d of org A's candidate mission tags (want 0)", nB) } // And running the tagging service in org B's context tags nothing of org A's: org B has no candidates, // so the read returns zero rows and the write set is empty, proving the service is fully RLS scoped. resB, err := svc.TagCandidatesForOrg(ctxOrg(orgB)) if err != nil { t.Fatalf("TagCandidatesForOrg (org B): %v", err) } if resB.CandidatesScanned != 0 || resB.CandidateTags != 0 { t.Fatalf("org B tagging should scan and tag nothing (org A's candidate is invisible), got %+v", resB) } // Idempotency against the live database: a second run in org A keeps exactly one SIGINT tag. if _, err := svc.TagCandidatesForOrg(ctxOrg(orgA)); err != nil { t.Fatalf("second TagCandidatesForOrg (org A): %v", err) } var nA int if err := appDB.RunInTenantTx(ctxOrg(orgA), func(ctx context.Context, tx pgx.Tx) error { return tx.QueryRow(ctx, "SELECT count(*) FROM candidate_mission_tags WHERE candidate_id = $1 AND mission_code = 'SIGINT'", candA.ID).Scan(&nA) }); err != nil { t.Fatalf("org A recount SIGINT tag: %v", err) } if nA != 1 { t.Fatalf("idempotency broken: org A has %d SIGINT tags after a second run (want 1)", nA) } }