# ARC-03 – Prüfprotokoll: Dublettenerkennung E-Mail Voraussetzung ARC-01 (Mail, Fertig). ## Umsetzung - `mail/internal/dedup/hash.go` — `HashAndBuffer(plaintext io.Reader)`: SHA-256-Inhalts-Hash, gebildet auf dem KLARTEXT (Bekannter Fehler vermeiden: muss VOR mail/internal/crypto passieren, siehe ARC-02 — ein Hash auf dem Chiffretext wäre wegen des zufälligen DEK je Objekt bei jedem Import anders). Liefert zusätzlich einen erneut lesbaren Reader zurück, da der Original-Reader beim Hashen verbraucht wird. - `mail/internal/dedup/store.go` — `Store.Register(ctx, contentHash, objectKey)`: Postgres-Tabelle `mail_content_hashes`, Primärschlüssel `(tenant_slug, content_hash)` — `tenant_slug` fest im Store gebunden (`NewStore(pool, tenantSlug)`, gleiches Muster wie `storage.Service`/`encstorage.Service`), nicht nur Konvention. `ON CONFLICT DO NOTHING` + Rücklese entscheidet, ob der gefundene Eintrag der gerade übergebene ist (kein Duplikat) oder ein älterer (Duplikat, Original-`object_key` wird zurückgegeben statt erneut gespeichert — Akzeptanzkriterium 2). - Kein Umbau: `mail/internal/storage`/`mail/internal/crypto`/ `mail/internal/encstorage` unverändert (`git diff --stat` bleibt für alle drei leer). `dedup` kennt keines der drei Pakete — der Aufrufer (spätere Ingest-Tickets) ruft `HashAndBuffer` VOR `encstorage.Put` auf. ## Prüfungen | # | Prüfung | Ergebnis | |---|---|---| | 1 | Test: dieselbe Nachricht aus zwei Quellen wird als Duplikat erkannt | **bestanden** – `TestRegister_SameMessageFromTwoSourcesIsDuplicate`: gleicher Hash, zwei verschiedene `object_key` ("quelle-1/objekt", "quelle-2/objekt") — zweite Registrierung liefert real `isDuplicate=true` und referenziert das Original `quelle-1/objekt` | | 2 | Test: zwei Mandanten mit identischem Mailinhalt werden nicht fälschlich verknüpft | **bestanden** – `TestRegister_SameContentTwoTenantsNotLinked`: zwei `Store`-Instanzen mit unterschiedlichem `tenantSlug`, IDENTISCHER Hash — beide Registrierungen liefern real `isDuplicate=false`, keine Verknüpfung über die Mandantengrenze | | 3 | Test mit knapp unterschiedlichen Nachrichten bestätigt korrekte Nicht-Erkennung | **bestanden** – `TestHashAndBuffer_SlightlyDifferentContentDifferentHash`: zwei Nachrichten, die sich nur im letzten Zeichen unterscheiden (`.` vs `,`) — real unterschiedlicher SHA-256-Hash | ## Build/Test-Ergebnis (192.168.1.131) ``` go build ./... -> clean go vet ./... -> clean golangci-lint run ./... -> 0 issues TEST_TENANT_DSN=postgresql://nexarch_test:***@localhost:5432/tenant_acme?sslmode=disable \ go test ./... -v -p 1 -> alle Pakete bestanden, inkl. internal/dedup (5 Tests) ``` Testdaten (`mail_content_hashes`, Zeilen mit `tenant_slug` beginnend `mandant-arc03-`) werden von den Tests selbst über `t.Cleanup` entfernt. ## Gesamtergebnis **Bestanden.** Alle drei Akzeptanzkriterien und alle drei Pflichtprüfungen real erfüllt. Entsperrt SRC-01, SRC-02, SRC-07.