import Testing import Foundation @testable import ShelfProvider private let day: TimeInterval = 86_400 private let now = Date(timeIntervalSince1970: 1_770_000_000) private func item(_ name: String, age: TimeInterval = 0) -> ShelfItem { ShelfItem(url: URL(fileURLWithPath: "/tmp/\(name)"), name: name, size: 1, addedAt: now - age) } @Suite("Namensvergabe in der Ablage") struct ShelfNamingTests { @Test("Ein freier Name bleibt, wie er ist") func freeNameIsKept() { #expect(ShelfNaming.uniqueName(for: "Vertrag.pdf", taken: []) == "Vertrag.pdf") } @Test("Ein belegter Name wird durchnummeriert") func takenNameIsNumbered() { // Zwei Dateien gleichen Namens sind der Normalfall, nicht die Ausnahme: // „IMG_0001.jpg" aus zwei Chats. Überschreiben wäre Datenverlust. #expect(ShelfNaming.uniqueName(for: "Vertrag.pdf", taken: ["Vertrag.pdf"]) == "Vertrag 2.pdf") } @Test("Die Nummer zählt weiter, bis etwas frei ist") func numberingContinues() { let taken: Set = ["Vertrag.pdf", "Vertrag 2.pdf", "Vertrag 3.pdf"] #expect(ShelfNaming.uniqueName(for: "Vertrag.pdf", taken: taken) == "Vertrag 4.pdf") } @Test("Ohne Endung wird trotzdem nummeriert") func noExtension() { #expect(ShelfNaming.uniqueName(for: "README", taken: ["README"]) == "README 2") } @Test("Ein führender Punkt ist keine Endung") func leadingDotIsNotAnExtension() { // Sonst würde aus „.gitignore" ein „ 2.gitignore" mit führendem // Leerzeichen — ein Name, der nirgends gut aussieht. #expect(ShelfNaming.uniqueName(for: ".gitignore", taken: [".gitignore"]) == ".gitignore 2") } @Test("Mehrfache Endungen bleiben zusammen") func doubleExtension() { // `.tar.gz` als `archiv.tar 2.gz` zu zerlegen wäre technisch möglich // und praktisch verwirrend. Nur die letzte Endung zählt. #expect(ShelfNaming.uniqueName(for: "archiv.tar.gz", taken: ["archiv.tar.gz"]) == "archiv.tar 2.gz") } @Test("Ein leerer Name bekommt einen Ersatz") func emptyNameGetsFallback() { #expect(!ShelfNaming.uniqueName(for: "", taken: []).isEmpty) } } @Suite("Automatisches Aufräumen") struct ShelfCleanupTests { @Test("Nichts läuft ab, wenn Aufräumen aus ist") func disabledKeepsEverything() { // Standard ist aus. Eine Ablage, die ungefragt löscht, ist keine // Ablage, sondern ein Papierkorb mit Zeitschaltuhr. let items = [item("alt", age: 400 * day)] #expect(ShelfCleanup.expired(items, now: now, afterDays: nil).isEmpty) } @Test("Was älter als die Frist ist, läuft ab") func olderThanLimitExpires() { let items = [item("alt", age: 31 * day), item("neu", age: 2 * day)] let expired = ShelfCleanup.expired(items, now: now, afterDays: 30) #expect(expired.map(\.name) == ["alt"]) } @Test("Genau auf der Frist bleibt es liegen") func exactlyAtLimitStays() { // Aufgerundet zugunsten des Nutzers: löschen ist unwiderruflich, // einen Tag länger liegen zu lassen ist es nicht. #expect(ShelfCleanup.expired([item("grenze", age: 30 * day)], now: now, afterDays: 30).isEmpty) } @Test("Eine Frist von null löscht nicht sofort alles") func zeroDaysIsTreatedAsOff() { // Sonst wäre ein Fehlgriff im Einstellungsfeld der Verlust der Ablage. #expect(ShelfCleanup.expired([item("neu")], now: now, afterDays: 0).isEmpty) } } @Suite("Reihenfolge in der Ablage") struct ShelfOrderTests { @Test("Das Neueste liegt oben") func newestFirst() { let items = [item("alt", age: 3 * day), item("neu", age: 1 * day), item("mittel", age: 2 * day)] #expect(ShelfItem.sorted(items).map(\.name) == ["neu", "mittel", "alt"]) } @Test("Gleich alte Dateien stehen stabil nach Namen") func tiesBreakByName() { // Ohne festen Zweitschlüssel springt die Liste bei jeder Aktualisierung // umher, weil das Dateisystem keine Reihenfolge zusagt. let items = [item("b"), item("a")] #expect(ShelfItem.sorted(items).map(\.name) == ["a", "b"]) } }