package rules_test import ( "encoding/json" "os" "path/filepath" "sort" "strconv" "testing" "github.com/netcell-it/deklarix/internal/rules" ) // goldenCase spiegelt eine Datei aus testdata/golden/: die extrahierten // Fakten eines Beispielbeitrags plus die Findings, die das Regelwerk // dafür liefern muss. Das ist das eigentliche Asset des Projekts, nicht // die UI — siehe CLAUDE.md. type goldenCase struct { Name string `json:"name"` Facts rules.Facts `json:"facts"` ExpectedFindings []goldenFinding `json:"expected_findings"` ExpectNeedsClarification bool `json:"expect_needs_clarification"` } type goldenFinding struct { RuleID string `json:"rule_id"` RuleVersion int `json:"rule_version"` Severity string `json:"severity"` } const ( rulesDir = "../../rules" goldenDir = "../../testdata/golden" ) func TestGolden(t *testing.T) { ruleSet, err := rules.Load(os.DirFS(rulesDir)) if err != nil { t.Fatalf("Load rules: %v", err) } entries, err := os.ReadDir(goldenDir) if err != nil { t.Fatalf("read golden dir: %v", err) } found := 0 for _, entry := range entries { if entry.IsDir() || filepath.Ext(entry.Name()) != ".json" { continue } found++ entry := entry t.Run(entry.Name(), func(t *testing.T) { data, err := os.ReadFile(filepath.Join(goldenDir, entry.Name())) if err != nil { t.Fatalf("read %s: %v", entry.Name(), err) } var gc goldenCase if err := json.Unmarshal(data, &gc); err != nil { t.Fatalf("parse %s: %v", entry.Name(), err) } got, needsClarification := rules.Evaluate(ruleSet, gc.Facts) if needsClarification != gc.ExpectNeedsClarification { t.Fatalf("%s: needsClarification = %v, want %v", gc.Name, needsClarification, gc.ExpectNeedsClarification) } gotKeys := make([]string, 0, len(got)) for _, f := range got { gotKeys = append(gotKeys, findingKey(f.RuleID, f.RuleVersion, string(f.Severity))) } wantKeys := make([]string, 0, len(gc.ExpectedFindings)) for _, ef := range gc.ExpectedFindings { wantKeys = append(wantKeys, findingKey(ef.RuleID, ef.RuleVersion, ef.Severity)) } sort.Strings(gotKeys) sort.Strings(wantKeys) if !equalStrings(gotKeys, wantKeys) { t.Fatalf("%s: got findings %v, want %v", gc.Name, gotKeys, wantKeys) } }) } if found == 0 { t.Fatal("no golden cases found in " + goldenDir) } } func findingKey(ruleID string, version int, severity string) string { return ruleID + "/" + strconv.Itoa(version) + "/" + severity } func equalStrings(a, b []string) bool { if len(a) != len(b) { return false } for i := range a { if a[i] != b[i] { return false } } return true }