Kein neues Produktionspaket, Audit- und Test-Kachel über die fünf Ingestion-Module (IMAP, POP3, SMTP, MIME, Folder-State). Zwei konkrete Lücken geschlossen: Neuer tenant_scoping_test.go in allen fünf Paketen: je zwei simulierte Mandanten mit ABSICHTLICH identischen Schlüsseln (Benutzername, Postfachname) — der Realfall, in dem ein fehlendes Scoping-Prädikat am ehesten eine echte Vermischung zeigen würde, statt trivial durch unterschiedliche Schlüssel zu bestehen. IMAP/POP3: zwei unabhängige Serverinstanzen mit je eigenem Store. SMTP: zwei Serverinstanzen, gleichzeitig mit vielen Nachrichten bedient. mimeparse: paralleles Parsen vieler "Mandanten"-Nachrichten (das Paket hat keinen Datenbankzugriff — Tenant-Scoping bedeutet hier: kein geteilter veränderlicher Zustand). folderstate: echte Postgres-Instanz, NextUID/Rebuild für Mandant A dürfen Mandant Bs Zustand nachweislich nicht verändern. mimeparse.ParseTolerant (IMP-02) war zu 0% Zeilenabdeckung vollständig ungetestet — genau der aus known-issues-archivmail.md #4 bekannte Fehler (kritische Ingestion-Logik ohne Tests). Neue tolerant_test.go: ein fehlerhafter Teil reißt die übrigen nicht mit, Gesamtgrößenlimit über alle Teile hinweg, strukturell kaputte Multipart-Hülle liefert weiterhin einen echten Fehler, Nicht-Multipart-Pfad. Abdeckung mimeparse 44,0% -> 76,7%. Testabdeckungsbericht für alle fünf Module dokumentiert, CI-Lauf auf frischem Checkout ohne externe Live-Postfächer verifiziert grün. Pflichtprüfung 3 (Stichprobenreview durch zweite Person) ist durch eine einzelne Sitzung strukturell nicht erfüllbar und bleibt offen — im Prüfprotokoll dokumentiert, Nutzer-Review ausstehend. go build/go vet/golangci-lint clean, gesamtes Mail-Modul (~29 Pakete) regressionsfrei getestet.
63 lines
1.9 KiB
Go
63 lines
1.9 KiB
Go
package mimeparse
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
)
|
|
|
|
// TestTenantScoping_ConcurrentParsesNeverMixContent ist die geforderte
|
|
// Pflichtprüfung (ING-10, Akzeptanzkriterium 2): Tenant-Scoping für den
|
|
// MIME-Ingestion-Pfad. mimeparse hält keinerlei Mandanten-Bezug oder
|
|
// Datenbankzugriff (reine Parsing-Funktion auf einem übergebenen
|
|
// io.Reader) — Tenant-Scoping bedeutet hier konkret: KEIN
|
|
// paketweiter, mandantenübergreifend geteilter veränderlicher Zustand,
|
|
// der bei gleichzeitigem Parsen mehrerer Mandanten-Nachrichten zu einer
|
|
// Vermischung führen könnte. Viele "Mandanten"-Nachrichten werden
|
|
// parallel geparst; jedes Ergebnis darf ausschließlich seinen eigenen
|
|
// Inhalt enthalten.
|
|
func TestTenantScoping_ConcurrentParsesNeverMixContent(t *testing.T) {
|
|
const tenants = 50
|
|
var wg sync.WaitGroup
|
|
errs := make(chan error, tenants)
|
|
|
|
for i := 0; i < tenants; i++ {
|
|
wg.Add(1)
|
|
go func(n int) {
|
|
defer wg.Done()
|
|
marker := fmt.Sprintf("Mandant-%02d-Geheiminhalt", n)
|
|
raw := "Content-Type: text/plain; charset=utf-8\r\n\r\n" + marker
|
|
msg, err := Parse(strings.NewReader(raw), 1<<20)
|
|
if err != nil {
|
|
errs <- fmt.Errorf("mandant %d: parse fehlgeschlagen: %w", n, err)
|
|
return
|
|
}
|
|
if len(msg.Parts) != 1 {
|
|
errs <- fmt.Errorf("mandant %d: erwartete 1 teil, habe %d", n, len(msg.Parts))
|
|
return
|
|
}
|
|
content := string(msg.Parts[0].Content)
|
|
if !strings.Contains(content, marker) {
|
|
errs <- fmt.Errorf("mandant %d: eigener inhalt fehlt: %q", n, content)
|
|
return
|
|
}
|
|
for j := 0; j < tenants; j++ {
|
|
if j == n {
|
|
continue
|
|
}
|
|
fremderMarker := fmt.Sprintf("Mandant-%02d-Geheiminhalt", j)
|
|
if strings.Contains(content, fremderMarker) {
|
|
errs <- fmt.Errorf("mandant %d: fremder inhalt gefunden (mandant %d): %q", n, j, content)
|
|
return
|
|
}
|
|
}
|
|
}(i)
|
|
}
|
|
wg.Wait()
|
|
close(errs)
|
|
for err := range errs {
|
|
t.Error(err)
|
|
}
|
|
}
|