package smtp import ( "strings" "sync" "testing" ) // TestTenantScoping_ConcurrentServersNeverMixMessages ist die // geforderte Pflichtprüfung (ING-10, Akzeptanzkriterium 2): Tenant- // Scoping für den SMTP-Ingestion-Pfad. Zwei vollständig unabhängige // Server-Instanzen (Mandant A/B), GLEICHZEITIG mit vielen Nachrichten // bedient — jede Instanz bekommt nur ihren eigenen Sink injiziert. // Eine Vermischung würde sich hier als falscher Nachrichteninhalt beim // jeweils anderen Sink zeigen. func TestTenantScoping_ConcurrentServersNeverMixMessages(t *testing.T) { sinkA := &fakeSink{} sinkB := &fakeSink{} addrA, stopA := startTestServer(t, sinkA, defaultMaxMessageBytes) defer stopA() addrB, stopB := startTestServer(t, sinkB, defaultMaxMessageBytes) defer stopB() const perTenant = 20 var wg sync.WaitGroup for i := 0; i < perTenant; i++ { wg.Add(2) go func(n int) { defer wg.Done() sendTenantMessage(t, addrA, "Mandant-A") }(i) go func(n int) { defer wg.Done() sendTenantMessage(t, addrB, "Mandant-B") }(i) } wg.Wait() if sinkA.count() != perTenant { t.Fatalf("mandant A: erwartete %d nachrichten, habe %d", perTenant, sinkA.count()) } if sinkB.count() != perTenant { t.Fatalf("mandant B: erwartete %d nachrichten, habe %d", perTenant, sinkB.count()) } for _, m := range sinkA.accepted { if !strings.Contains(string(m.raw), "Mandant-A") || strings.Contains(string(m.raw), "Mandant-B") { t.Fatalf("mandant A hat fremden/vermischten inhalt bekommen: %q", m.raw) } } for _, m := range sinkB.accepted { if !strings.Contains(string(m.raw), "Mandant-B") || strings.Contains(string(m.raw), "Mandant-A") { t.Fatalf("mandant B hat fremden/vermischten inhalt bekommen: %q", m.raw) } } } func sendTenantMessage(t *testing.T, addr, marker string) { t.Helper() c := dial(t, addr) defer c.close() c.send(t, "EHLO client.example.com") for { line := c.readLine(t) if strings.HasPrefix(line, "250 ") { break } } c.send(t, "MAIL FROM:") c.send(t, "RCPT TO:") c.send(t, "DATA") c.send(t, "Subject: "+marker+"\r\n\r\nInhalt von "+marker+"\r\n.") }