Export der users-Tabellen aus archivmail/archivdms (internal/usermatch) und E-Mail-Abgleich ausschliesslich innerhalb bereits per MAP-02 freigegebener Tenants (tenantapproval.Store.Approved()). Kandidatenliste als CSV, kein automatischer Merge -- Freigabe bleibt MAP-04. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
87 lines
2.7 KiB
Go
87 lines
2.7 KiB
Go
package usermatch
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"testing"
|
|
|
|
"gitea.perlbach24.de/scripte/nexarch/internal/testdb"
|
|
)
|
|
|
|
// Integrationstest gegen eine echte Postgres-Instanz mit frei erfundenen
|
|
// Benutzer-Zeilen (E-Mails wie "synth-user-a@..."), niemals echte
|
|
// archivmail-/archivdms-Kundendaten. Siehe docs/MAP-03-PRUEFPROTOKOLL.md.
|
|
// Ohne TEST_ADMIN_DSN uebersprungen.
|
|
func TestExportUsers_Vollstaendigkeit(t *testing.T) {
|
|
adminDSN := os.Getenv("TEST_ADMIN_DSN")
|
|
if adminDSN == "" {
|
|
t.Skip("TEST_ADMIN_DSN nicht gesetzt, Integrationstest uebersprungen")
|
|
}
|
|
ctx := context.Background()
|
|
|
|
pool := testdb.Pool(t, adminDSN)
|
|
defer pool.Close()
|
|
|
|
if _, err := pool.Exec(ctx, `
|
|
CREATE TABLE IF NOT EXISTS users (
|
|
id BIGINT PRIMARY KEY,
|
|
tenant_id BIGINT NOT NULL,
|
|
email TEXT NOT NULL,
|
|
name TEXT NOT NULL,
|
|
password_hash TEXT NOT NULL DEFAULT ''
|
|
);
|
|
`); err != nil {
|
|
t.Fatalf("schema: %v", err)
|
|
}
|
|
|
|
suffix := testdb.Suffix()
|
|
rows := []ExportedUser{
|
|
{SourceID: 1, TenantSourceID: 10, Email: "synth-user-a-" + suffix + "@synth.example", Name: "Synthetische Nutzerin A", PasswordHash: "hash-a"},
|
|
{SourceID: 2, TenantSourceID: 10, Email: "synth-user-b-" + suffix + "@synth.example", Name: "Synthetischer Nutzer B", PasswordHash: "hash-b"},
|
|
{SourceID: 3, TenantSourceID: 11, Email: "synth-user-c-" + suffix + "@synth.example", Name: "Synthetischer Nutzer C", PasswordHash: "hash-c"},
|
|
}
|
|
for _, r := range rows {
|
|
if _, err := pool.Exec(ctx, `INSERT INTO users (id, tenant_id, email, name, password_hash) VALUES ($1,$2,$3,$4,$5)`,
|
|
r.SourceID, r.TenantSourceID, r.Email, r.Name, r.PasswordHash); err != nil {
|
|
t.Fatalf("insert: %v", err)
|
|
}
|
|
}
|
|
t.Cleanup(func() {
|
|
for _, r := range rows {
|
|
_, _ = pool.Exec(ctx, `DELETE FROM users WHERE email = $1`, r.Email)
|
|
}
|
|
})
|
|
|
|
wantCount, err := CountUsers(ctx, pool)
|
|
if err != nil {
|
|
t.Fatalf("CountUsers: %v", err)
|
|
}
|
|
|
|
got, err := ExportUsers(ctx, pool, "archivmail")
|
|
if err != nil {
|
|
t.Fatalf("ExportUsers: %v", err)
|
|
}
|
|
|
|
// Pruefung 1: Export-Vollstaendigkeit gegen Quelltabellen-Zeilenzahl.
|
|
if len(got) != wantCount {
|
|
t.Fatalf("export unvollstaendig: habe %d zeilen, quelltabelle hat %d", len(got), wantCount)
|
|
}
|
|
|
|
byEmail := make(map[string]ExportedUser, len(got))
|
|
for _, g := range got {
|
|
if g.Source != "archivmail" {
|
|
t.Fatalf("source nicht gesetzt: %+v", g)
|
|
}
|
|
byEmail[g.Email] = g
|
|
}
|
|
for _, r := range rows {
|
|
g, ok := byEmail[r.Email]
|
|
if !ok {
|
|
t.Fatalf("zeile %q fehlt im export", r.Email)
|
|
}
|
|
if g.Name != r.Name || g.TenantSourceID != r.TenantSourceID || g.SourceID != r.SourceID || g.PasswordHash != r.PasswordHash {
|
|
t.Fatalf("zeile %q nicht korrekt exportiert: habe %+v, erwarte %+v", r.Email, g, r)
|
|
}
|
|
}
|
|
}
|