//go:build integration package integration import ( "context" "os" "testing" "time" "github.com/missionworkforce/missionmatch/internal/platform/inference" "github.com/missionworkforce/missionmatch/internal/platform/scrub" ) // megamindClient builds a client to the live MEGAMIND inference plane, or skips the test when the plane // is not configured (MM_MEGAMIND_URL unset), the same pattern the DB integration tests use. func megamindClient(t *testing.T) *inference.Client { t.Helper() url := os.Getenv("MM_MEGAMIND_URL") if url == "" { t.Skip("MM_MEGAMIND_URL unset; skipping the live inference plane test") } c, err := inference.New(inference.Config{BaseURL: url, EmbedModel: "all-minilm", Dimension: 384, Timeout: 60 * time.Second}) if err != nil { t.Fatalf("inference client: %v", err) } return c } // TestMegamindReachableAnd384 proves Phase 0: the inference plane is reachable and its embeddings are // exactly 384 wide, the width the candidates.resume_embedding and skills.embedding columns require. func TestMegamindReachableAnd384(t *testing.T) { c := megamindClient(t) ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) defer cancel() if err := c.Health(ctx); err != nil { t.Fatalf("MEGAMIND health: %v", err) } res, err := c.Embed(ctx, []string{"TS/SCI cleared cloud security engineer", "Kubernetes"}) if err != nil { t.Fatalf("Embed: %v", err) } if len(res.Embeddings) != 2 { t.Fatalf("want 2 vectors, got %d", len(res.Embeddings)) } for i, v := range res.Embeddings { if len(v) != 384 { t.Fatalf("vector %d has width %d, the column is vector(384)", i, len(v)) } } } // TestMegamindTeachBoundaryLive proves the gateway independently enforces the scrub boundary: a clean // craft signal is accepted, a signal carrying a client fact is refused server side. func TestMegamindTeachBoundaryLive(t *testing.T) { c := megamindClient(t) c2, _ := inference.New(inference.Config{BaseURL: os.Getenv("MM_MEGAMIND_URL"), Dimension: 384, Timeout: 20 * time.Second, TeachingEnabled: true}) _ = c ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second) defer cancel() clean := scrub.TeachingSignal{Pattern: "adjacent_skill_substitution_outcome", Band: scrub.BandMedium, Outcome: scrub.OutcomeApproved, Observations: 40, Successes: 31, ModelVersion: "v1.0.0"} if err := c2.Teach(ctx, clean); err != nil { t.Fatalf("the gateway should accept a clean craft signal: %v", err) } // A client fact never even leaves the client (scrub.Marshal refuses it), proving the boundary holds // before transmission. leak := scrub.TeachingSignal{Pattern: "adjacent_skill_substitution_outcome", Band: scrub.BandMedium, Outcome: scrub.OutcomeApproved, Observations: 1, Successes: 1, ModelVersion: "11111111-1111-1111-1111-111111111111"} if err := c2.Teach(ctx, leak); err == nil { t.Fatal("a client fact must be refused at the boundary, never transmitted") } }