Neues Paket mail/internal/ingestiontestgate, spiegelt das etablierte Muster aus mail/internal/qagate (QA-03) und mail/internal/importtestgate (IMP-09): ein echtes, ausführbares Gate statt einer nur behaupteten Prüfung. IngestionAndImportPackages listet alle 14 aus QA-02s eigener dependsOn-Liste folgenden Pakete (Protokoll-Server, Fehlerbehandlung/ Logging, Import-Pfade). RunTestSuites führt go test -count=1 -p 1 über alle 14 Pakete aus — -p 1 bewusst, da parallele Testbinaries sich bei den echten QA-07-Lasttests gegenseitig CPU-Kontingent wegnehmen und Latenz-Zielwerte durch reine Testhost-Überlastung verfehlen lassen (real beobachtet, mit -p 1 behoben). ScanForKnownErrorPointTests prüft automatisiert, dass die drei in Akzeptanzkriterium 2 namentlich geforderten Fehlerpunkte (Header-Injection, Anhang-Limit, UIDVALIDITY) je einen passenden Regressionstest im zuständigen Paket haben. Gate-Lauf gegen aktuellen Stand real ausgeführt und mit Zeitstempel dokumentiert: BESTANDEN, 14 Pakete grün, alle drei bekannten Fehlerpunkte abgedeckt. Scanner durch Negativtest bewiesen, dass er eine tatsächlich fehlende Abdeckung real erkennt statt immer "bestanden" zu melden. Pflichtprüfung 3 (Zweitperson bestätigt Gate-Ergebnis unabhängig) bleibt strukturell offen — eine einzelne Sitzung kann keine unabhängige zweite Person sein, im Prüfprotokoll dokumentiert (analog zu ING-10 und QA-04). go build/go vet/golangci-lint clean, gesamtes Mail-Modul regressionsfrei getestet.
120 lines
4.0 KiB
Go
120 lines
4.0 KiB
Go
package ingestiontestgate
|
|
|
|
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/ingestiontestgate — Modulwurzel ist
|
|
// zwei Ebenen darüber.
|
|
return filepath.Join(wd, "..", "..")
|
|
}
|
|
|
|
// TestScanForKnownErrorPointTests_RealPackagesAllCovered ist die
|
|
// geforderte Pflichtprüfung 2: Stichprobe, mindestens ein
|
|
// Regressionstest je bekanntem Fehlerpunkt — automatisiert und
|
|
// reproduzierbar gegen den echten Quelltext ausgeführt.
|
|
func TestScanForKnownErrorPointTests_RealPackagesAllCovered(t *testing.T) {
|
|
root := moduleRoot(t)
|
|
result, err := ScanForKnownErrorPointTests(root)
|
|
if err != nil {
|
|
t.Fatalf("scan: %v", err)
|
|
}
|
|
if !result.Passed {
|
|
for _, r := range result.Results {
|
|
if !r.Covered {
|
|
t.Errorf("bekannter fehlerpunkt %q (%s) hat KEINEN passenden regressionstest in %s gefunden", r.Point.Name, r.Point.Rationale, r.Point.PackageRelDir)
|
|
}
|
|
}
|
|
t.FailNow()
|
|
}
|
|
for _, r := range result.Results {
|
|
t.Logf("%s: %s (%s/%s)", r.Point.Name, r.TestName, r.Point.PackageRelDir, r.FoundInFile)
|
|
}
|
|
}
|
|
|
|
// TestScanForKnownErrorPointTests_DetectsMissingCoverage beweist, dass
|
|
// der Scanner einen tatsächlich fehlenden Regressionstest auch real
|
|
// erkennt (Negativtest) — ein Gate, das nie eine Lücke melden kann,
|
|
// wäre wertlos.
|
|
func TestScanForKnownErrorPointTests_DetectsMissingCoverage(t *testing.T) {
|
|
dir := t.TempDir()
|
|
// Nur EIN Paketverzeichnis anlegen (für "Header-Injection"), die
|
|
// beiden anderen bekannten Fehlerpunkte bleiben absichtlich ohne
|
|
// Verzeichnis/Tests.
|
|
mailerDir := filepath.Join(dir, "internal", "mailer")
|
|
if err := os.MkdirAll(mailerDir, 0o750); err != nil {
|
|
t.Fatalf("verzeichnis anlegen: %v", err)
|
|
}
|
|
content := []byte("package mailer\n\nimport \"testing\"\n\nfunc TestRejectsCRLFInHeader(t *testing.T) {}\n")
|
|
if err := os.WriteFile(filepath.Join(mailerDir, "mailer_test.go"), content, 0o600); err != nil {
|
|
t.Fatalf("testdatei schreiben: %v", err)
|
|
}
|
|
// Leere Verzeichnisse für die anderen beiden Punkte, damit ReadDir
|
|
// nicht mit einem Fehler abbricht.
|
|
if err := os.MkdirAll(filepath.Join(dir, "internal", "mimeparse"), 0o750); err != nil {
|
|
t.Fatalf("verzeichnis anlegen: %v", err)
|
|
}
|
|
if err := os.MkdirAll(filepath.Join(dir, "internal", "folderstate"), 0o750); err != nil {
|
|
t.Fatalf("verzeichnis anlegen: %v", err)
|
|
}
|
|
|
|
result, err := ScanForKnownErrorPointTests(dir)
|
|
if err != nil {
|
|
t.Fatalf("scan: %v", err)
|
|
}
|
|
if result.Passed {
|
|
t.Fatal("erwartete erkannte lücke (2 von 3 fehlerpunkten unabgedeckt), scan meldet bestanden")
|
|
}
|
|
covered := 0
|
|
for _, r := range result.Results {
|
|
if r.Covered {
|
|
covered++
|
|
}
|
|
}
|
|
if covered != 1 {
|
|
t.Fatalf("erwartete genau 1 abgedeckten fehlerpunkt, habe %d", covered)
|
|
}
|
|
}
|
|
|
|
// TestRun_RealGateAgainstCurrentIngestionImportState ist die geforderte
|
|
// Pflichtprüfung 1: Gate-Lauf gegen aktuellen Stand von ING-10/IMP-09
|
|
// dokumentiert — führt das vollständige Gate real gegen den aktuellen
|
|
// Modul-Quelltext aus (inklusive echter Postgres-Integrationstests) und
|
|
// dokumentiert das Ergebnis mit Zeitstempel.
|
|
func TestRun_RealGateAgainstCurrentIngestionImportState(t *testing.T) {
|
|
if os.Getenv("TEST_TENANT_DSN") == "" {
|
|
t.Skip("TEST_TENANT_DSN 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.KnownErrorPoints.Passed {
|
|
t.Fatalf("bekannte fehlerpunkte nicht vollständig abgedeckt: %+v", result.KnownErrorPoints.Results)
|
|
}
|
|
if !result.TestSuite.Passed {
|
|
t.Fatalf("testsuiten fehlgeschlagen, ausgabe:\n%s", result.TestSuite.Output)
|
|
}
|
|
|
|
reportPath := filepath.Join(t.TempDir(), "qa-02-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())
|
|
}
|