package qagate import ( "context" "os" "path/filepath" "testing" ) func moduleRoot(t *testing.T) string { t.Helper() wd, err := os.Getwd() if err != nil { t.Fatalf("arbeitsverzeichnis ermitteln: %v", err) } // gate_test.go liegt in internal/qagate — Modulwurzel ist zwei Ebenen // darüber. return filepath.Join(wd, "..", "..") } // TestScanSearchPathForDynamicSQL_RealSearchPackagePasses ist die // geforderte Pflichtprüfung 2: Codereview-Stichprobe bestätigt // statischen Query-Builder — automatisiert und reproduzierbar gegen den // echten mail/internal/search-Quelltext ausgeführt statt rein manuell // behauptet. func TestScanSearchPathForDynamicSQL_RealSearchPackagePasses(t *testing.T) { root := moduleRoot(t) result, err := ScanSearchPathForDynamicSQL(filepath.Join(root, "internal", "search")) if err != nil { t.Fatalf("scan: %v", err) } if !result.Passed { t.Fatalf("erwartete bestandenen scan, habe verstöße: %v", result.Violations) } } // TestScanSearchPathForDynamicSQL_DetectsRealViolation beweist, dass der // Scanner einen tatsächlichen Verstoß auch real erkennt (Negativtest, // analog zu no_dynamic_sql_test.go) — ein Gate, das nie einen Verstoß // melden kann, wäre wertlos. func TestScanSearchPathForDynamicSQL_DetectsRealViolation(t *testing.T) { dir := t.TempDir() violatingFile := "bad_query.go" content := []byte("package search\n\nimport \"fmt\"\n\nfunc bad(field string) string {\n\treturn fmt.Sprintf(\"WHERE %s = 1\", field)\n}\n") if err := os.WriteFile(filepath.Join(dir, violatingFile), content, 0o600); err != nil { t.Fatalf("testdatei schreiben: %v", err) } result, err := ScanSearchPathForDynamicSQL(dir) if err != nil { t.Fatalf("scan: %v", err) } if result.Passed { t.Fatal("erwartete erkannten verstoß, scan meldet bestanden") } if len(result.Violations) != 1 || result.Violations[0] != violatingFile+": enthält fmt.Sprintf(" { t.Fatalf("unerwartetes verstoßergebnis: %+v", result.Violations) } } // TestScanSearchPathForDynamicSQL_ExemptsDocumentedDDLFile bestätigt, // dass die dokumentierte Ausnahme (reindex.go, Schema-/DDL-Verwaltung, // kein Abfragepfad) NICHT als Verstoß gewertet wird. func TestScanSearchPathForDynamicSQL_ExemptsDocumentedDDLFile(t *testing.T) { dir := t.TempDir() content := []byte("package search\n\nimport \"fmt\"\n\nfunc ddl(name string) string {\n\treturn fmt.Sprintf(\"CREATE TABLE %s (...)\", name)\n}\n") if err := os.WriteFile(filepath.Join(dir, "reindex.go"), content, 0o600); err != nil { t.Fatalf("testdatei schreiben: %v", err) } result, err := ScanSearchPathForDynamicSQL(dir) if err != nil { t.Fatalf("scan: %v", err) } if !result.Passed { t.Fatalf("erwartete bestandenen scan (dokumentierte ausnahme), habe: %v", result.Violations) } } // TestRun_RealGateAgainstCurrentARC08SRC10State ist die geforderte // Pflichtprüfung 1: Gate-Lauf gegen aktuellen Stand von ARC-08/SRC-10 // dokumentiert — führt das vollständige Gate real gegen den aktuellen // Modul-Quelltext aus (inklusive echter Postgres-/Manticore- // Integrationstests, darunter ARC-08s Schlüsselrotation und SRC-10s // OCR-Konfidenzabfrage) und dokumentiert das Ergebnis mit Zeitstempel. func TestRun_RealGateAgainstCurrentARC08SRC10State(t *testing.T) { if os.Getenv("TEST_TENANT_DSN") == "" || os.Getenv("TEST_MANTICORE_URL") == "" { t.Skip("TEST_TENANT_DSN/TEST_MANTICORE_URL nicht gesetzt, Integrationstest übersprungen") } root := moduleRoot(t) ctx := context.Background() result, err := Run(ctx, root) if err != nil { t.Fatalf("gate-lauf: %v", err) } if result.Timestamp.IsZero() { t.Fatal("erwarteter zeitstempel fehlt") } if !result.SQLScan.Passed { t.Fatalf("statischer scan fehlgeschlagen: %v", result.SQLScan.Violations) } if !result.TestSuite.Passed { t.Fatalf("testsuiten fehlgeschlagen, ausgabe:\n%s", result.TestSuite.Output) } reportPath := filepath.Join(t.TempDir(), "qa-03-gate-report.md") if err := os.WriteFile(reportPath, []byte(result.Report()), 0o600); err != nil { t.Fatalf("bericht schreiben: %v", err) } t.Logf("Gate-Bericht (%s):\n%s", reportPath, result.Report()) }