package engine import ( "context" "testing" "time" "github.com/local/glpi-neural-brain/internal/graph" "github.com/local/glpi-neural-brain/internal/model" ) func TestAutonomousResearchSemanticDuplicateSurvivesChangingSeedMembership(t *testing.T) { a := model.ResearchTask{Topic: "T1133 / T1078 / T1588.001", SeedNodeIDs: []string{"a", "b", "c"}, Questions: []string{"Wie hängen T1133 T1078 und T1588.001 zusammen?"}} b := model.ResearchTask{Topic: "T1133 / T1078 / T1588.001", SeedNodeIDs: []string{"a", "d"}, Questions: []string{"Welche Abwehrmaßnahmen betreffen T1133, T1078 und T1588.001?"}} if !autonomousResearchSemanticDuplicate(a, b) { t.Fatalf("same ATT&CK opportunity with changed seeds should dedupe, similarity=%.3f", autonomousResearchTaskSimilarity(a, b)) } c := model.ResearchTask{Topic: "Windows Update CBS 0x80D02002", SeedNodeIDs: []string{"x"}} if autonomousResearchSemanticDuplicate(a, c) { t.Fatalf("unrelated opportunities should not dedupe, similarity=%.3f", autonomousResearchTaskSimilarity(a, c)) } } func TestMergeQueuedResearchTaskAndPersistentTotals(t *testing.T) { store, err := graph.Open(t.TempDir()) if err != nil { t.Fatal(err) } defer store.Close() ctx := context.Background() queued, created, err := store.EnqueueResearchTask(ctx, model.ResearchTask{DedupeKey: "a", Topic: "ATT&CK cluster", RequestedBy: "autonomous-scanner", Priority: .7, SeedNodeIDs: []string{"a"}, Questions: []string{"q1"}}, time.Hour) if err != nil || !created { t.Fatalf("enqueue failed created=%v err=%v", created, err) } merged, changed, err := store.MergeQueuedResearchTask(ctx, queued.ID, model.ResearchTask{Topic: "ATT&CK cluster", Priority: .9, SeedNodeIDs: []string{"b"}, Questions: []string{"q2"}}) if err != nil || !changed { t.Fatalf("merge failed changed=%v err=%v", changed, err) } if merged.Priority != .9 || len(merged.SeedNodeIDs) != 2 || len(merged.Questions) != 2 { t.Fatalf("unexpected merged task: %+v", merged) } leased, ok, err := store.LeaseNextResearchTask(ctx, 0, time.Minute) if err != nil || !ok { t.Fatalf("lease failed ok=%v err=%v", ok, err) } if err := store.MarkResearchTaskRunning(ctx, leased.ID); err != nil { t.Fatal(err) } leased.EvidenceCount = 3 leased.ArticleCreated = true if err := store.CompleteResearchTask(ctx, leased); err != nil { t.Fatal(err) } totals, err := store.ResearchTaskPersistentTotals(ctx) if err != nil { t.Fatal(err) } if totals["completed"] != 1 || totals["evidence"] != 3 || totals["articles"] != 1 { t.Fatalf("unexpected persistent totals: %+v", totals) } }