Source identity
| Field | Recorded value |
|---|---|
| Source type | GitHub source |
| Original record | Janady13/missionmatch / internal/integration/rls_integration_test.go |
| Date | Not specified in source |
| Repository | Janady13/missionmatch |
| Revision | 720d47fae476b0d38477ae4401697d372b1ea2bc |
| Branch | feature/opportunity-to-outcome |
| Path or archive member | internal/integration/rls_integration_test.go |
| Original SHA-256 | b7701eb5b792d741c542fad76ddbd9909b2bcaa74859eba5ec84f9eec9f02aef |
| Git blob SHA | 5529d961247d89782becdc7b6ca0b8be2fe8e681 |
| Reading-copy SHA-256 | b7701eb5b792d741c542fad76ddbd9909b2bcaa74859eba5ec84f9eec9f02aef |
Every recorded occurrence
These 16 records contain identical original source bytes. Their paths and revisions remain part of the archive.
| Original record | Branch or archive | Revision / identifier | Original location |
|---|---|---|---|
| Janady13/missionmatch / internal/integration/rls_integration_test.go | feature/opportunity-to-outcome | 720d47fae476b0d38477ae4401697d372b1ea2bc | Open original |
| Janady13/missionmatch / internal/integration/rls_integration_test.go | collision-deconfliction | 4ee5a09972608ed89f8377c6665542d5f450f0a6 | Open original |
| Janady13/missionmatch / internal/integration/rls_integration_test.go | ws6-retrieval | 6c56e29231ec05735927fa265c97d517499dfeaf | Open original |
| Janady13/missionmatch / internal/integration/rls_integration_test.go | m5-price-to-win | 41000ad30fff4129fdaf5ec84e15ba14c27d4890 | Open original |
| Janady13/missionmatch / internal/integration/rls_integration_test.go | master | 1e9249840a4302ec82e123b9c9cb6f75badea3e4 | Open original |
| Janady13/missionmatch / internal/integration/rls_integration_test.go | ws8-teaching-loop | c823f0fb6bb2aba0f39001eb6de96f4000d9321f | Open original |
| Janady13/missionmatch / internal/integration/rls_integration_test.go | mag-substrate | b82c7100a492dec67dfd1eda6b3f53def859be35 | Open original |
| Janady13/missionmatch / internal/integration/rls_integration_test.go | time-to-fill-survival | b66e1e505863d4f17741a4b9ede73509e45bd6c6 | Open original |
| Janady13/missionmatch / internal/integration/rls_integration_test.go | graphrag-foundation | b0ddeb9f633fd138d09309d1a17b7d13bc3a67f7 | Open original |
| Janady13/missionmatch / internal/integration/rls_integration_test.go | ws9-bench-ingestion | f585a9ef32fa554663ce1fba9a414a54b6e49cf0 | Open original |
| Janady13/missionmatch / internal/integration/rls_integration_test.go | presentation-readiness | eb574acaa29d71ce4d0f852572e87c275c79fdb7 | Open original |
| Janady13/missionmatch / internal/integration/rls_integration_test.go | vendor-intelligence | fe9796a19c261951d3f2565206c0c1b5ac294465 | Open original |
| Janady13/missionmatch / internal/integration/rls_integration_test.go | ws7-federal-signal | 6a9f6ce9cfb13345ffb47770d4960e7c2d9c17c2 | Open original |
| Janady13/missionmatch / internal/integration/rls_integration_test.go | phase2-connectors | e038d4c687aec16f8bd9bc4d907eea14d91f8704 | Open original |
| Janady13/missionmatch / internal/integration/rls_integration_test.go | feature/aioe-upgrade | 2178e7298d0e5d9af83afa12402fd587f0ba6ed8 | Open original |
| Janady13/missionmatch / internal/integration/rls_integration_test.go | capture-readiness | bae6af7cb2997e819174a9e9cfff5e16067a1975 | Open original |
Recovered source transcript
Download complete source text ↓476 lines · 18.0 KB
//go:build integration
// Package integration runs the data layer against a REAL Postgres 16 + pgvector. It proves at
// runtime the properties the adversarial review could only check statically: tenant isolation,
// default deny, the composite FK cross tenant injection block, numeric(7,4) round trip, audit
// immutability, the EEO privilege boundary, and the hardened pgvector/RRF search path.
//
// Run: go test -tags=integration ./internal/integration/ -v
// Env: MM_APP_DSN, MM_MIGRATOR_DSN (required), MM_COMPLIANCE_DSN (optional).
package integration
import (
"context"
"errors"
"fmt"
"os"
"testing"
"time"
"github.com/google/uuid"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgconn"
"github.com/jackc/pgx/v5/pgxpool"
"github.com/pgvector/pgvector-go"
"github.com/shopspring/decimal"
"github.com/missionworkforce/missionmatch/internal/modules/commandcenter"
"github.com/missionworkforce/missionmatch/internal/modules/matching"
"github.com/missionworkforce/missionmatch/internal/platform/database"
"github.com/missionworkforce/missionmatch/internal/platform/database/dbgen"
"github.com/missionworkforce/missionmatch/internal/platform/embeddings"
"github.com/missionworkforce/missionmatch/internal/platform/tenant"
)
var (
appDB *tenant.DB
migrator *pgxpool.Pool
compliance *pgxpool.Pool
)
func TestMain(m *testing.M) {
ctx := context.Background()
appDSN, migDSN, compDSN := os.Getenv("MM_APP_DSN"), os.Getenv("MM_MIGRATOR_DSN"), os.Getenv("MM_COMPLIANCE_DSN")
if appDSN == "" || migDSN == "" {
fmt.Println("integration: MM_APP_DSN / MM_MIGRATOR_DSN not set, skipping integration tests")
os.Exit(0)
}
pool, err := database.NewPool(ctx, appDSN)
if err != nil {
panic("app pool: " + err.Error())
}
appDB = tenant.New(pool)
if migrator, err = database.NewPool(ctx, migDSN); err != nil {
panic("migrator pool: " + err.Error())
}
if compDSN != "" {
if compliance, err = database.NewPool(ctx, compDSN); err != nil {
panic("compliance pool: " + err.Error())
}
}
os.Exit(m.Run())
}
// ---------- helpers (seed via the BYPASSRLS migrator so we can write across tenants) ----------
func seedOrg(t *testing.T) uuid.UUID {
t.Helper()
id := uuid.New()
slug := "org-" + id.String()[:12]
if _, err := migrator.Exec(context.Background(),
"INSERT INTO organizations (id, name, slug) VALUES ($1,$2,$3)", id, slug, slug); err != nil {
t.Fatalf("seed org: %v", err)
}
return id
}
func seedCandidate(t *testing.T, org uuid.UUID, level, status string, available bool) dbgen.Candidate {
t.Helper()
c, err := dbgen.New(migrator).CreateCandidate(context.Background(), dbgen.CreateCandidateParams{
OrganizationID: org, FullName: "Cand " + level, ClearanceLevel: level, ClearancePoly: "none",
ClearanceStatus: status, Available: available,
})
if err != nil {
t.Fatalf("seed candidate: %v", err)
}
return c
}
func ctxOrg(org uuid.UUID) context.Context { return tenant.WithOrg(context.Background(), org.String()) }
func pgCode(err error) string {
var pgErr *pgconn.PgError
if errors.As(err, &pgErr) {
return pgErr.Code
}
return ""
}
// ---------- tests ----------
func TestCrossTenantReadIsolation(t *testing.T) {
orgA, orgB := seedOrg(t), seedOrg(t)
candA := seedCandidate(t, orgA, "secret", "active", true)
candB := seedCandidate(t, orgB, "secret", "active", true)
var listed []dbgen.Candidate
if err := appDB.RunInTenantTx(ctxOrg(orgA), func(ctx context.Context, tx pgx.Tx) error {
var e error
listed, e = dbgen.New(tx).ListCandidates(ctx, dbgen.ListCandidatesParams{Limit: 1000})
return e
}); err != nil {
t.Fatalf("list: %v", err)
}
sawA, sawB := false, false
for _, c := range listed {
sawA = sawA || c.ID == candA.ID
sawB = sawB || c.ID == candB.ID
}
if !sawA {
t.Fatal("org A must see its own candidate")
}
if sawB {
t.Fatal("ISOLATION BREACH: org A saw org B's candidate")
}
// Direct read of B's candidate under org A's session must return zero rows (RLS hides it).
err := appDB.RunInTenantTx(ctxOrg(orgA), func(ctx context.Context, tx pgx.Tx) error {
_, e := dbgen.New(tx).GetCandidate(ctx, candB.ID)
return e
})
if !errors.Is(err, pgx.ErrNoRows) {
t.Fatalf("cross tenant GetCandidate should be ErrNoRows, got %v", err)
}
}
func TestUnsetGUCDefaultDeny(t *testing.T) {
orgA := seedOrg(t)
seedCandidate(t, orgA, "secret", "active", true)
// App role, NO tenant transaction => GUC unset => current_setting is NULL => policy denies => 0 rows.
var n int
if err := appDB.Pool.QueryRow(context.Background(), "SELECT count(*) FROM candidates").Scan(&n); err != nil {
t.Fatalf("count: %v", err)
}
if n != 0 {
t.Fatalf("default deny breached: unscoped app query saw %d rows (want 0)", n)
}
}
func TestCompositeFKBlocksCrossTenantInjection(t *testing.T) {
orgA, orgB := seedOrg(t), seedOrg(t)
candB := seedCandidate(t, orgB, "secret", "active", true) // victim parent in org B
skill, err := dbgen.New(migrator).UpsertSkill(context.Background(), dbgen.UpsertSkillParams{
Name: "FKProbe-" + uuid.NewString(),
})
if err != nil {
t.Fatalf("seed skill: %v", err)
}
// As org A, try to attach a skill row (org=A) to org B's candidate. RLS WITH CHECK passes (row
// org == session org), but the COMPOSITE FK (organization_id, candidate_id) -> candidates has no
// matching (A, candB) parent, so the write MUST fail with a foreign key violation (23503).
err = appDB.RunInTenantTx(ctxOrg(orgA), func(ctx context.Context, tx pgx.Tx) error {
return dbgen.New(tx).AddCandidateSkill(ctx, dbgen.AddCandidateSkillParams{
OrganizationID: orgA, CandidateID: candB.ID, SkillID: skill.ID,
})
})
if code := pgCode(err); code != "23503" {
t.Fatalf("cross tenant injection NOT blocked: want FK violation 23503, got err=%v code=%q", err, code)
}
}
func TestPerfectScorePersistsViaService(t *testing.T) {
orgA := seedOrg(t)
// Requirement with nothing required => every component scores 100 => total exactly 100.
req, err := dbgen.New(migrator).CreateRequirement(context.Background(), dbgen.CreateRequirementParams{
OrganizationID: orgA, Title: "Open Role", RequiredLevel: "none", RequiredPoly: "none",
})
if err != nil {
t.Fatalf("seed requirement: %v", err)
}
cand := seedCandidate(t, orgA, "secret", "active", true)
svc := matching.NewService(appDB, nil, nil) // exact-only matcher
out, err := svc.GenerateSlate(ctxOrg(orgA), matching.GenerateSlateInput{RequirementID: req.ID, Name: "Perfect Slate"})
if err != nil {
t.Fatalf("GenerateSlate (numeric overflow would surface here): %v", err)
}
if len(out.Members) != 1 || out.Members[0].CandidateID != cand.ID.String() {
t.Fatalf("unexpected members: %+v", out.Members)
}
if out.Members[0].Total != "100.00" || out.Members[0].Band != "green" {
t.Fatalf("perfect candidate should be 100.00/green, got %s/%s", out.Members[0].Total, out.Members[0].Band)
}
// Read back the frozen decision record, proves numeric(7,4) stored 100.0000 (the SQLC-1 fix).
var members []dbgen.SlateMember
if err := appDB.RunInTenantTx(ctxOrg(orgA), func(ctx context.Context, tx pgx.Tx) error {
var e error
members, e = dbgen.New(tx).ListSlateMembers(ctx, out.SlateID)
return e
}); err != nil {
t.Fatalf("read back: %v", err)
}
if len(members) != 1 || !members[0].TotalScore.Equal(decimal.NewFromInt(100)) {
t.Fatalf("persisted total should be 100, got %+v", members)
}
}
func TestAuditAppendOnly(t *testing.T) {
orgA := seedOrg(t)
if err := appDB.RunInTenantTx(ctxOrg(orgA), func(ctx context.Context, tx pgx.Tx) error {
_, e := dbgen.New(tx).InsertAuditLog(ctx, dbgen.InsertAuditLogParams{OrganizationID: &orgA, Action: "test.event"})
return e
}); err != nil {
t.Fatalf("insert audit: %v", err)
}
// App role has no UPDATE/DELETE grant on audit_logs => permission denied (42501).
if _, err := appDB.Pool.Exec(context.Background(), "UPDATE audit_logs SET action='tamper' WHERE organization_id=$1", orgA); pgCode(err) != "42501" {
t.Fatalf("app UPDATE on audit_logs should be permission-denied (42501), got %v", err)
}
// Even the owner (migrator) is stopped by the append-only trigger (raise_exception P0001).
_, err := migrator.Exec(context.Background(), "UPDATE audit_logs SET action='tamper' WHERE organization_id=$1", orgA)
if err == nil {
t.Fatal("append-only trigger failed: owner UPDATE on audit_logs succeeded")
}
if pgCode(err) != "P0001" {
t.Fatalf("owner UPDATE should raise the append-only trigger (P0001), got %v", err)
}
}
func TestEEOPrivilegeBoundary(t *testing.T) {
// The matching app role must have ZERO access to the eeo schema (permission denied 42501).
_, err := appDB.Pool.Exec(context.Background(), "SELECT 1 FROM eeo.candidate_demographics LIMIT 1")
if pgCode(err) != "42501" {
t.Fatalf("app role must be denied EEO access (42501), got %v", err)
}
if compliance != nil {
// The compliance role CAN reach it (no permission error; RLS may legitimately return 0 rows).
if _, err := compliance.Exec(context.Background(), "SELECT 1 FROM eeo.candidate_demographics LIMIT 1"); err != nil {
t.Fatalf("compliance role should access EEO, got %v", err)
}
}
}
func TestHybridSearchPathExecutes(t *testing.T) {
orgA := seedOrg(t)
emb := embeddings.NewDeterministic(384)
names := []string{"Cloud Security Engineer", "Kubernetes Platform Lead", "Network Defense Analyst"}
for _, name := range names {
vec, _ := emb.Embed(context.Background(), name)
pv := pgvector.NewVector(vec)
hn := name
if _, err := dbgen.New(migrator).CreateCandidate(context.Background(), dbgen.CreateCandidateParams{
OrganizationID: orgA, FullName: name, Headline: &hn, ClearanceLevel: "secret",
ClearancePoly: "none", ClearanceStatus: "active", Available: true, ResumeEmbedding: &pv,
}); err != nil {
t.Fatalf("seed search candidate: %v", err)
}
}
qv := pgvector.NewVector(mustEmbed(emb, "cloud security"))
var rows []dbgen.SearchCandidatesHybridRow
err := appDB.RunInTenantTx(ctxOrg(orgA), func(ctx context.Context, tx pgx.Tx) error {
if _, e := tx.Exec(ctx, "SET LOCAL hnsw.ef_search = 200"); e != nil { // PGV-3
return e
}
var e error
rows, e = dbgen.New(tx).SearchCandidatesHybrid(ctx, dbgen.SearchCandidatesHybridParams{
Column1: qv, Limit: 10, WebsearchToTsquery: "cloud security",
})
return e
})
if err != nil {
t.Fatalf("hybrid search executed against live pgvector failed: %v", err)
}
if len(rows) == 0 {
t.Fatal("hybrid search returned no rows (expected the seeded candidates)")
}
for _, r := range rows {
if r.OrganizationID != orgA {
t.Fatalf("hybrid search leaked a foreign-tenant row: %s", r.OrganizationID)
}
}
}
// TestHybridSearchMissionScopeExecutes proves the WS6 mission scoped query (SearchCandidatesHybridMission)
// against live pgvector + full text: when a mission keyword is supplied, only candidates whose full_name,
// headline, or location text matches that mission are returned, and the RLS boundary still holds so a foreign tenant's SIGINT
// candidate never surfaces. The mission filter is text match only (the documented WS6 Option A limit).
func TestHybridSearchMissionScopeExecutes(t *testing.T) {
orgA, orgB := seedOrg(t), seedOrg(t)
emb := embeddings.NewDeterministic(384)
seed := func(org uuid.UUID, name, headline string) {
vec, _ := emb.Embed(context.Background(), name+" "+headline)
pv := pgvector.NewVector(vec)
hn := headline
if _, err := dbgen.New(migrator).CreateCandidate(context.Background(), dbgen.CreateCandidateParams{
OrganizationID: org, FullName: name, Headline: &hn, ClearanceLevel: "top_secret",
ClearancePoly: "none", ClearanceStatus: "active", Available: true, ResumeEmbedding: &pv,
}); err != nil {
t.Fatalf("seed mission candidate %q: %v", name, err)
}
}
// org A: two SIGINT candidates (text contains SIGINT) and two off mission candidates.
seed(orgA, "Alice Analyst", "SIGINT collection and analysis")
seed(orgA, "Bob Signals", "SIGINT systems engineer")
seed(orgA, "Carol Cloud", "Kubernetes and AWS platform")
seed(orgA, "Dave Defender", "network defense operations")
// org B: a SIGINT candidate that must NOT leak into org A's scoped retrieval.
seed(orgB, "Eve Foreign", "SIGINT foreign tenant")
qv := pgvector.NewVector(mustEmbed(emb, "intelligence analyst"))
var rows []dbgen.SearchCandidatesHybridMissionRow
err := appDB.RunInTenantTx(ctxOrg(orgA), func(ctx context.Context, tx pgx.Tx) error {
if _, e := tx.Exec(ctx, "SET LOCAL hnsw.ef_search = 200"); e != nil {
return e
}
var e error
rows, e = dbgen.New(tx).SearchCandidatesHybridMission(ctx, dbgen.SearchCandidatesHybridMissionParams{
QueryVector: qv, RowLimit: 10, TextQuery: "intelligence analyst", MissionQuery: "SIGINT",
})
return e
})
if err != nil {
t.Fatalf("mission scoped hybrid search failed: %v", err)
}
if len(rows) != 2 {
names := make([]string, len(rows))
for i, r := range rows {
names[i] = r.FullName
}
t.Fatalf("mission scope returned %d rows %v, want exactly the 2 SIGINT candidates", len(rows), names)
}
for _, r := range rows {
if r.OrganizationID != orgA {
t.Fatalf("mission scope leaked a foreign-tenant row: %s (%s)", r.FullName, r.OrganizationID)
}
if r.FullName != "Alice Analyst" && r.FullName != "Bob Signals" {
t.Fatalf("mission scope returned an off-mission candidate: %s", r.FullName)
}
}
}
func mustEmbed(e embeddings.Embedder, s string) []float32 {
v, _ := e.Embed(context.Background(), s)
return v
}
func ptrStr(s string) *string { return &s }
// TestCommandCenterRLSIsolation proves the Workforce Command Center never mixes tenants: org A's
// dashboard counts only org A's workforce/opportunities, even though org B has its own seeded data on
// the same cluster. This is the design's #1 risk (RLS boundary escape on an analytics endpoint).
func TestCommandCenterRLSIsolation(t *testing.T) {
ctx := context.Background()
orgA, orgB := seedOrg(t), seedOrg(t)
// org A: exactly 3 cleared, available candidates; NO opportunities.
for i := 0; i < 3; i++ {
seedCandidate(t, orgA, "secret", "active", true)
}
// org B: 5 cleared candidates + a uniquely-named opportunity with one requirement.
for i := 0; i < 5; i++ {
seedCandidate(t, orgB, "top_secret", "active", true)
}
decoyOpp := uuid.New()
if _, err := migrator.Exec(ctx,
`INSERT INTO opportunities (id, organization_id, name, status, value) VALUES ($1,$2,'DECOY-ONLY-OPP','open',1000000)`,
decoyOpp, orgB); err != nil {
t.Fatalf("seed decoy opp: %v", err)
}
if _, err := migrator.Exec(ctx,
`INSERT INTO requirements (organization_id, opportunity_id, title, labor_category, quantity, required_level, required_poly)
VALUES ($1,$2,'Decoy Engineer','Decoy Engineer',2,'secret','none')`,
orgB, decoyOpp); err != nil {
t.Fatalf("seed decoy req: %v", err)
}
svc := commandcenter.NewService(appDB)
now := time.Now()
va, err := svc.CommandCenter(ctxOrg(orgA), now)
if err != nil {
t.Fatalf("command center org A: %v", err)
}
if va.Banner.TotalClearedWorkforce != 3 {
t.Fatalf("org A cleared workforce = %d, want 3 (leak of org B's 5?)", va.Banner.TotalClearedWorkforce)
}
if va.Banner.ActiveRequirements != 0 || len(va.ActiveOpportunities) != 0 {
t.Fatalf("org A saw foreign opportunities/requirements: reqs=%d opps=%d", va.Banner.ActiveRequirements, len(va.ActiveOpportunities))
}
for _, o := range va.ActiveOpportunities {
if o.Name == "DECOY-ONLY-OPP" {
t.Fatal("ISOLATION BREACH: org A saw org B's DECOY-ONLY-OPP")
}
}
vb, err := svc.CommandCenter(ctxOrg(orgB), now)
if err != nil {
t.Fatalf("command center org B: %v", err)
}
if vb.Banner.TotalClearedWorkforce != 5 {
t.Fatalf("org B cleared workforce = %d, want 5", vb.Banner.TotalClearedWorkforce)
}
if vb.Banner.ActiveRequirements != 1 || len(vb.ActiveOpportunities) != 1 || vb.ActiveOpportunities[0].Name != "DECOY-ONLY-OPP" {
t.Fatalf("org B should see exactly its own opportunity, got reqs=%d opps=%d", vb.Banner.ActiveRequirements, len(vb.ActiveOpportunities))
}
}
func TestScreenIntelligenceAndOpportunityFit(t *testing.T) {
ctx := context.Background()
orgA := seedOrg(t)
req, err := dbgen.New(migrator).CreateRequirement(ctx, dbgen.CreateRequirementParams{
OrganizationID: orgA, Title: "Cyber Engineer", CustomerAgency: ptrStr("DISA"),
RequiredLevel: "secret", RequiredPoly: "none", RequiredYears: 5, KeyPersonnel: true,
})
if err != nil {
t.Fatalf("seed requirement: %v", err)
}
cand := seedCandidate(t, orgA, "ts_sci", "active", true)
if _, err := migrator.Exec(ctx,
"UPDATE candidates SET clearance_poly='full_scope', years_experience=10, clearance_granted_at='2023-01-15', clearance_expires_at='2029-01-15' WHERE id=$1",
cand.ID); err != nil {
t.Fatalf("enrich candidate: %v", err)
}
if err := dbgen.New(migrator).AddCandidateCustomerExperience(ctx, dbgen.AddCandidateCustomerExperienceParams{
OrganizationID: orgA, CandidateID: cand.ID, Agency: "DISA",
}); err != nil {
t.Fatalf("add customer experience: %v", err)
}
svc := matching.NewService(appDB, nil, nil)
view, err := svc.Screen(ctxOrg(orgA), cand.ID, req.ID)
if err != nil {
t.Fatalf("Screen: %v", err)
}
if view.MissionReadiness.Status != "Mission Ready" || view.MissionReadiness.Score != "100.00" {
t.Fatalf("mission readiness = %+v", view.MissionReadiness)
}
if len(view.MissionReadiness.Categories) != 6 {
t.Fatalf("expected 6 readiness categories, got %d", len(view.MissionReadiness.Categories))
}
if !view.ClearanceIntel.SCIEligible || !view.ClearanceIntel.FullScopePoly || !view.ClearanceIntel.RecentClassifiedSupport {
t.Fatalf("clearance intel = %+v", view.ClearanceIntel)
}
if view.ClearanceIntel.InvestigationAgeMonths == nil || *view.ClearanceIntel.InvestigationAgeMonths < 30 {
t.Fatalf("expected an investigation age ~41mo, got %v", view.ClearanceIntel.InvestigationAgeMonths)
}
if view.WinImpact.LevelCode != "high" {
t.Fatalf("win impact = %+v", view.WinImpact)
}
keyPersonnel := false
for _, rsn := range view.WinImpact.Reasons {
if rsn == "Matches Key Personnel requirements" {
keyPersonnel = true
}
}
if !keyPersonnel {
t.Fatalf("expected Key Personnel reason, got %v", view.WinImpact.Reasons)
}
fits, err := svc.OpportunitiesForCandidate(ctxOrg(orgA), cand.ID, 10)
if err != nil {
t.Fatalf("OpportunitiesForCandidate: %v", err)
}
if len(fits) == 0 || fits[0].RequirementID != req.ID || fits[0].Status != "Mission Ready" {
t.Fatalf("opportunity fit = %+v", fits)
}
}