package ucca import ( "context" "fmt" "os" "strings" "testing" ) func TestInKBScope(t *testing.T) { inScope := []string{ "Welche neun Kriterien nennt WP248 fuer ein hohes Risiko?", "Wie greifen CRA und Maschinenverordnung bei einer vernetzten Maschine ineinander?", "Wann ist eine Datenschutz-Folgenabschaetzung erforderlich?", "Welche Anforderungen stellt die DSGVO an die Einwilligung?", "Brauche ich einen Datenschutzbeauftragten?", "Wann muss eine aktiv ausgenutzte Schwachstelle gemeldet werden?", } outScope := []string{ "Welche OWASP-Kontrollen gibt es fuer Authentifizierung?", "Was sagt NIST SP 800-53 zu Access Control?", "Wie funktioniert ISO 27001 Zertifizierung?", "Welche IFRS-Standards gelten fuer Leasing?", } for _, q := range inScope { if !inKBScope(q) { t.Errorf("inKBScope(%q) = false, want true", q) } } for _, q := range outScope { if inKBScope(q) { t.Errorf("inKBScope(%q) = true, want false", q) } } } func TestResolveCollection(t *testing.T) { c := &LegalRAGClient{collection: "bp_compliance_ce", kbSliceCollection: "kb_2026_1_build", kbScopeRoutingEnabled: true} if got := c.resolveCollection("Welche Kriterien nennt WP248?", ""); got != "kb_2026_1_build" { t.Errorf("in-scope default -> %s, want kb_2026_1_build", got) } if got := c.resolveCollection("Was sagt NIST SP 800-53?", ""); got != "bp_compliance_ce" { t.Errorf("out-of-scope default -> %s, want bp_compliance_ce", got) } if got := c.resolveCollection("Welche Kriterien nennt WP248?", "explicit_coll"); got != "explicit_coll" { t.Errorf("explicit request must be honoured -> %s", got) } c.kbScopeRoutingEnabled = false if got := c.resolveCollection("Welche Kriterien nennt WP248?", ""); got != "bp_compliance_ce" { t.Errorf("disabled routing -> %s, want bp_compliance_ce", got) } } // TestKBScopeRoutingE2E (RUN_E2E=1) verifies the routing against the REAL collections: a default // Search() of an in-scope query must hit the KB-2026.1 slice (WP248/MaschVO live there but NOT in // the broad CE pool = clean discriminator); an out-of-scope query stays on CE. func TestKBScopeRoutingE2E(t *testing.T) { if os.Getenv("RUN_E2E") != "1" { t.Skip("set RUN_E2E=1 + QDRANT_URL/OLLAMA_URL/QDRANT_API_KEY") } c := NewLegalRAGClient() cases := []struct { q string wantToken string // expected in top-8 when routed to the slice wantInKB bool }{ {"Welche neun Kriterien nennt WP248 fuer ein voraussichtlich hohes Risiko?", "WP248", true}, {"Welche grundlegenden Sicherheits- und Gesundheitsschutzanforderungen enthaelt Anhang III der Maschinenverordnung?", "MASCH", true}, {"Wie greifen CRA und Maschinenverordnung bei einer vernetzten Maschine ineinander?", "MASCH", true}, {"Was sagt NIST SP 800-53 zu Access Control?", "", false}, } for _, tc := range cases { routed := c.resolveCollection(tc.q, "") res, err := c.Search(context.Background(), tc.q, nil, 8) if err != nil { t.Fatalf("%q: %v", tc.q, err) } codes := map[string]bool{} for _, r := range res { codes[strings.ToUpper(r.RegulationCode)] = true } hit := false if tc.wantToken != "" { for cd := range codes { if strings.Contains(cd, tc.wantToken) { hit = true break } } } col := make([]string, 0, len(codes)) for cd := range codes { col = append(col, cd) } fmt.Printf("inKB=%-5v routed=%-16s wantTok=%-6s found=%-5v | %v\n", tc.wantInKB, routed, tc.wantToken, hit, col) if tc.wantInKB && tc.wantToken != "" && !hit { t.Errorf("%q routed to %s but %s not in top-8 (slice not active?)", tc.q, routed, tc.wantToken) } } }