package engine import ( "testing" "time" "github.com/local/glpi-neural-brain/internal/graph" "github.com/local/glpi-neural-brain/internal/model" ) func TestBuildAutonomousCandidatesPrioritizesContradictionWithoutEvidence(t *testing.T) { now := time.Now().UTC() snapshot := model.Snapshot{ Nodes: []model.Node{ {ID: "a", Kind: "knowledge", Label: "ZFS Restore", Status: "production", UpdatedAt: now.Add(-400 * 24 * time.Hour), Metadata: map[string]any{"source": "internal-category"}}, {ID: "b", Kind: "knowledge", Label: "ZFS Key Import", Status: "production", UpdatedAt: now, Metadata: map[string]any{"source": "internal-category"}}, }, Edges: []model.Edge{{ID: "e", Source: "a", Target: "b", Type: "contradicts", Status: "accepted"}}, } candidates := buildAutonomousCandidates(snapshot, graph.NodeFilter{Sources: []string{"internal-category"}}, 8) if len(candidates) == 0 { t.Fatal("expected a research candidate") } candidate := candidates[0] if candidate.Priority < .75 { t.Fatalf("expected contradiction/no-evidence candidate to be high priority, got %.3f", candidate.Priority) } if len(candidate.SeedNodeIDs) < 2 { t.Fatalf("expected related production nodes as seeds, got %#v", candidate.SeedNodeIDs) } if got := candidate.Signals["contradictions"]; got != 1 { t.Fatalf("expected contradiction signal, got %#v", got) } } func TestBuildAutonomousCandidatesHonorsExactThinkingSource(t *testing.T) { snapshot := model.Snapshot{Nodes: []model.Node{ {ID: "a", Kind: "knowledge", Label: "Allowed", Status: "production", Metadata: map[string]any{"source": "internal-category"}}, {ID: "b", Kind: "knowledge", Label: "Wrong case", Status: "production", Metadata: map[string]any{"source": "Internal-Category"}}, }} candidates := buildAutonomousCandidates(snapshot, graph.NodeFilter{Sources: []string{"internal-category"}}, 8) for _, candidate := range candidates { if candidate.Topic == "Wrong case" { t.Fatal("candidate source matching must stay exact and case-sensitive") } } } func TestBuildAutonomousQueryQueueUsesBothLanguagesAndRounds(t *testing.T) { queue := buildAutonomousQueryQueue( []string{"Wie wird ein Restore validiert?", "Welche Schlüssel werden benötigt?"}, []string{"ZFS Restore validieren", "ZFS Schlüssel importieren", "ZFS Ersatzsystem"}, []string{"ZFS restore validation", "ZFS key import"}, 3, ) if len(queue) != 5 { t.Fatalf("expected five planned queries, got %d", len(queue)) } languages := map[string]bool{} maxRound := 0 for _, item := range queue { languages[item.Language] = true if item.Round > maxRound { maxRound = item.Round } } if !languages["de-DE"] || !languages["en-US"] { t.Fatalf("expected German and English queries, got %#v", languages) } if maxRound < 2 || maxRound > 3 { t.Fatalf("unexpected round assignment: %d", maxRound) } } func TestAutonomousDedupeKeyIsStableAcrossSeedOrder(t *testing.T) { a := autonomousDedupeKey(" ZFS Restore ", []string{"b", "a"}) b := autonomousDedupeKey("zfs restore", []string{"a", "b"}) if a != b { t.Fatalf("dedupe key should normalize topic spacing/case and seed order: %q != %q", a, b) } }