This commit is contained in:
2026-08-07 08:33:41 +02:00
parent d088eb0a47
commit 246241fcda
419 changed files with 11924 additions and 203 deletions
+87 -6
View File
@@ -115,19 +115,19 @@ func TestHeuristicResearchAssessmentUsesEnglishQueryAsFallbackAnchor(t *testing.
}
}
func TestNormalizeResearchPlanKeepsOneLanguageUnrestricted(t *testing.T) {
func TestNormalizeResearchPlanRemovesAllDomainRestrictions(t *testing.T) {
plan := normalizeResearchPlan(model.ResearchPlan{Questions: []model.ResearchQuestion{{
GapID: "G1", Question: "Audit logging konfigurieren", Critical: true, ExpectActionable: true,
QueriesDE: []string{"Audit Logging konfigurieren"}, QueriesEN: []string{"configure audit logging"}, PreferredDomains: []string{"docs.example.com"},
QueriesDE: []string{"Audit Logging konfigurieren site:docs.example.com"}, QueriesEN: []string{"configure audit logging site:vendor.example"}, PreferredDomains: []string{"docs.example.com"},
}}}, model.ArticlePlanDecision{}, model.KnowledgeBrief{CriticalGaps: []model.KnowledgeGap{{ID: "G1", Description: "Audit logging konfigurieren"}}}, map[string]bool{}, 6)
if len(plan.Questions) != 1 || len(plan.Questions[0].QueriesDE) != 1 || len(plan.Questions[0].QueriesEN) != 1 {
t.Fatalf("unexpected normalized plan: %+v", plan)
}
if !strings.Contains(plan.Questions[0].QueriesDE[0], "site:docs.example.com") {
t.Fatalf("preferred primary-source query missing: %+v", plan.Questions[0])
if strings.Contains(strings.ToLower(plan.Questions[0].QueriesDE[0]), "site:") || strings.Contains(strings.ToLower(plan.Questions[0].QueriesEN[0]), "site:") {
t.Fatalf("site restriction survived normalization: %+v", plan.Questions[0])
}
if strings.Contains(plan.Questions[0].QueriesEN[0], "site:") {
t.Fatalf("all language variants were over-restricted: %+v", plan.Questions[0])
if len(plan.Questions[0].PreferredDomains) != 0 {
t.Fatalf("preferred domains must be ignored: %+v", plan.Questions[0])
}
}
@@ -216,3 +216,84 @@ func TestFilterKnowledgeBriefReferencesDropsUnsupportedStatements(t *testing.T)
t.Fatalf("unsupported resolution references were not removed: %+v", brief.ResolvedGaps)
}
}
func TestSanitizeSearchQuerySiteFiltersRemovesEveryRestriction(t *testing.T) {
for _, query := range []string{
"forensic evidence handling site:digital-forensics",
"forensic evidence handling site:docs.aws.amazon.com",
"Azure MFA site:learn.microsoft.com",
} {
got := sanitizeSearchQuerySiteFilters(query)
if strings.Contains(strings.ToLower(got), "site:") {
t.Fatalf("site filter remained in %q => %q", query, got)
}
}
}
func TestNormalizeKnowledgeBriefDowngradesEditorialCriticalGap(t *testing.T) {
brief := normalizeKnowledgeBriefForArticle(model.KnowledgeBrief{
Topic: "Mobile Authentication",
Scope: []model.GroundedStatement{{Text: "Gilt für mobile Identitätsprüfungen.", SourceRefs: []string{"S1"}}},
Facts: []model.GroundedStatement{
{Text: "Authentifizierung bestätigt eine Identität.", SourceRefs: []string{"S1"}},
{Text: "Biometrie kann als lokaler Faktor dienen.", SourceRefs: []string{"S2"}},
{Text: "Autorisierung steuert erlaubte Aktionen.", SourceRefs: []string{"S3"}},
},
CriticalGaps: []model.KnowledgeGap{{
ID: "G1", Description: "Die genaue Differenzierung zwischen Mobile Authentication, Mobile Biometric Authentication und Mobile Authorization fehlt.",
Reason: "Eine ausführlichere Abgrenzung wäre hilfreich.",
}},
}, "concept")
if len(brief.CriticalGaps) != 0 || len(brief.OptionalGaps) != 1 || !brief.ReadyForArticle {
t.Fatalf("editorial gap should become a non-blocking open question: %+v", brief)
}
}
func TestNormalizeKnowledgeBriefKeepsSafetyGapCritical(t *testing.T) {
brief := normalizeKnowledgeBriefForArticle(model.KnowledgeBrief{
Scope: []model.GroundedStatement{{Text: "Gilt für Wiederherstellungen.", SourceRefs: []string{"S1"}}},
Facts: []model.GroundedStatement{{Text: "Die Wiederherstellung verändert produktive Daten.", SourceRefs: []string{"S1"}}},
SolutionSteps: []model.GroundedStatement{{Text: "Starten Sie die Wiederherstellung.", SourceRefs: []string{"S1"}}},
CriticalGaps: []model.KnowledgeGap{{
ID: "G1", Description: "Der zwingend erforderliche Rollback-Pfad fehlt.", Reason: "Ohne diese Information droht Datenverlust.",
}},
}, "how_to")
if len(brief.CriticalGaps) != 1 || brief.ReadyForArticle {
t.Fatalf("safety gap must remain blocking: %+v", brief)
}
}
func TestNormalizeKnowledgeBriefClassifiesLegacyEditorialMissingInformationAsOptional(t *testing.T) {
brief := normalizeKnowledgeBriefForArticle(model.KnowledgeBrief{
Scope: []model.GroundedStatement{{Text: "Gilt für Container-Forensik.", SourceRefs: []string{"S1"}}},
Facts: []model.GroundedStatement{
{Text: "Audit-Logs unterstützen die Rekonstruktion.", SourceRefs: []string{"S1"}},
{Text: "Knoten-Logs ergänzen Pod-Metadaten.", SourceRefs: []string{"S2"}},
{Text: "Hashes dokumentieren Integrität.", SourceRefs: []string{"S3"}},
},
MissingInformation: []string{"Eine ausführlichere Definition des Begriffs Baseline fehlt."},
}, "reference")
if len(brief.CriticalGaps) != 0 || len(brief.OptionalGaps) != 1 || !brief.ReadyForArticle {
t.Fatalf("legacy editorial item should not block article: %+v", brief)
}
}
func TestResearchIntentSimilarityUsesSemanticVector(t *testing.T) {
a := []float64{1, 0, 1}
b := []float64{.99, .01, .99}
if sim := researchIntentSimilarity("Azure MFA", a, "Multi-Factor Authentication Entra", b); sim < .99 {
t.Fatalf("expected semantic vector dedupe, got %.4f", sim)
}
}
func TestFormatArticleAnswerUsesConfiguredLanguage(t *testing.T) {
draft := model.KnowledgeArticleDraft{Answer: "Done", Prerequisites: []string{"Admin role"}, Validation: []string{"Check result"}, Troubleshooting: []string{"Review logs"}}
english := formatArticleAnswer(draft, "en-US")
if !strings.Contains(english, "## Prerequisites") || strings.Contains(english, "## Voraussetzungen") {
t.Fatalf("unexpected English section labels: %s", english)
}
german := formatArticleAnswer(draft, "de-DE")
if !strings.Contains(german, "## Voraussetzungen") {
t.Fatalf("unexpected German section labels: %s", german)
}
}