package storage import ( "strings" "testing" "time" ) // TestArchiveKey_MultipleYearsProduceSeparateYearFolders ist die // geforderte Pflichtprüfung 1 (ARC-04): Import über mehrere Jahre // erzeugt korrekt getrennte Jahresordner. func TestArchiveKey_MultipleYearsProduceSeparateYearFolders(t *testing.T) { sent2019 := time.Date(2019, 3, 1, 0, 0, 0, 0, time.UTC) sent2024 := time.Date(2024, 11, 1, 0, 0, 0, 0, time.UTC) key2019 := ArchiveKey("INBOX", sent2019, "msg-a", 0) key2024 := ArchiveKey("INBOX", sent2024, "msg-b", 0) if !strings.HasPrefix(key2019, "postfach/INBOX/2019/") { t.Fatalf("erwartete jahresordner 2019, habe: %q", key2019) } if !strings.HasPrefix(key2024, "postfach/INBOX/2024/") { t.Fatalf("erwartete jahresordner 2024, habe: %q", key2024) } if key2019 == key2024 { t.Fatalf("erwartete unterschiedliche schlüssel für unterschiedliche jahre") } } // TestArchiveKey_MissingMailboxUsesDocumentedFallback ist die // geforderte Pflichtprüfung 2 (ARC-04): fehlendes Postfach-Attribut // nutzt den dokumentierten Fallback statt eines Ablagefehlers. func TestArchiveKey_MissingMailboxUsesDocumentedFallback(t *testing.T) { key := ArchiveKey("", time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC), "msg-a", 0) if !strings.Contains(key, "/"+FallbackMailboxSegment+"/") { t.Fatalf("erwartete fallback-postfach-segment %q, habe: %q", FallbackMailboxSegment, key) } } // TestArchiveKey_MissingSentAtUsesDocumentedFallback belegt denselben // Fallback-Grundsatz für ein fehlendes (Null-)Sendedatum. func TestArchiveKey_MissingSentAtUsesDocumentedFallback(t *testing.T) { key := ArchiveKey("INBOX", time.Time{}, "msg-a", 0) if !strings.Contains(key, "/"+FallbackYearSegment+"/") { t.Fatalf("erwartete fallback-jahr-segment %q, habe: %q", FallbackYearSegment, key) } } // TestArchiveKey_NeverErrorsOnAnyInput bestätigt, dass ArchiveKey für // KEINE Eingabekombination fehlschlägt (Akzeptanzkriterium 3: // "definierter Fallback-Pfad, nicht Ablagefehler" — ArchiveKey liefert // bewusst keinen error-Rückgabewert, weil es strukturell keinen // Fehlerfall gibt). func TestArchiveKey_NeverErrorsOnAnyInput(t *testing.T) { inputs := []struct { mailbox string sentAt time.Time }{ {"", time.Time{}}, {"Postfach/Mit/Slashes", time.Time{}}, {" ", time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC)}, } for _, in := range inputs { key := ArchiveKey(in.mailbox, in.sentAt, "msg", 0) if key == "" { t.Fatalf("erwartete nicht-leeren schlüssel für eingabe %+v", in) } } } // TestArchiveYearPrefix_FoundationForRetentionWithoutMigration ist die // geforderte Pflichtprüfung für Akzeptanzkriterium 2: die Struktur ist // Grundlage für spätere Retention-Regeln ohne Migration — ein // Retention-Job kann den Präfix EINES Postfach-Jahrs berechnen und // findet darunter GENAU die zuvor mit ArchiveKey abgelegten Schlüssel // desselben Postfachs/Jahrs, ohne dass an den bereits abgelegten // Schlüsseln irgendetwas umbenannt werden müsste. func TestArchiveYearPrefix_FoundationForRetentionWithoutMigration(t *testing.T) { sent2022 := time.Date(2022, 6, 15, 0, 0, 0, 0, time.UTC) key := ArchiveKey("Rechnungen", sent2022, "msg-x", 3) prefix := ArchiveYearPrefix("Rechnungen", 2022) if !strings.HasPrefix(key, prefix) { t.Fatalf("ArchiveKey %q liegt nicht unter dem für retention berechenbaren präfix %q", key, prefix) } // Ein anderes Jahr desselben Postfachs liegt NICHT unter demselben // Präfix — Retention kann Jahre gezielt einzeln adressieren. otherYearPrefix := ArchiveYearPrefix("Rechnungen", 2023) if strings.HasPrefix(key, otherYearPrefix) { t.Fatalf("ArchiveKey %q hätte NICHT unter dem 2023-präfix liegen dürfen", key) } }