import Testing import Foundation @testable import ClipboardProvider @Suite("Zwischenablage: die Regeln") struct ClipboardRulesTests { private func item(_ fingerprint: String, at seconds: TimeInterval = 0, preview: String = "x") -> ClipboardItem { ClipboardItem(kind: .text, preview: preview, addedAt: Date(timeIntervalSince1970: seconds), sourceBundleID: nil, fingerprint: fingerprint) } // MARK: - Was nie gemerkt werden darf @Test("Ein als verborgen markierter Inhalt wird nicht gemerkt") func concealedIsIgnored() { // Passwortmanager setzen diese Marke. Sie zu missachten hieße, fremde // Passwörter einzusammeln. #expect(!ClipboardRules.shouldRecord( types: ["public.utf8-plain-text", "org.nspasteboard.ConcealedType"], sourceBundleID: nil, excludedApps: [])) } @Test("Durchreichen und Selbsterzeugtes ebenso wenig") func transientAndGeneratedAreIgnored() { #expect(!ClipboardRules.shouldRecord(types: ["org.nspasteboard.TransientType"], sourceBundleID: nil, excludedApps: [])) #expect(!ClipboardRules.shouldRecord(types: ["org.nspasteboard.AutoGeneratedType"], sourceBundleID: nil, excludedApps: [])) } @Test("Gewöhnlicher Text schon") func plainTextIsRecorded() { #expect(ClipboardRules.shouldRecord(types: ["public.utf8-plain-text"], sourceBundleID: "com.apple.TextEdit", excludedApps: [])) } @Test("Aus einem ausgeschlossenen Programm nicht") func excludedAppIsIgnored() { #expect(!ClipboardRules.shouldRecord(types: ["public.utf8-plain-text"], sourceBundleID: "com.agilebits.onepassword7", excludedApps: ["com.agilebits.onepassword7"])) } @Test("Ohne bekanntes Herkunftsprogramm wird gemerkt") func unknownSourceIsRecorded() { // Sonst ginge alles verloren, was aus einem Hintergrunddienst kommt. #expect(ClipboardRules.shouldRecord(types: ["public.utf8-plain-text"], sourceBundleID: nil, excludedApps: ["com.agilebits.onepassword7"])) } // MARK: - Vorschau @Test("Zeilenumbrüche werden zu Leerzeichen") func previewIsOneLine() { #expect(ClipboardRules.preview(for: "erste\nzweite") == "erste zweite") #expect(ClipboardRules.preview(for: "a\t\tb") == "a b") } @Test("Zu langer Text wird gekürzt und bekommt Auslassungspunkte") func previewIsTruncated() { let long = String(repeating: "a", count: 500) let preview = ClipboardRules.preview(for: long) #expect(preview.count == ClipboardRules.previewLength + 1) #expect(preview.hasSuffix("…")) } @Test("Kurzer Text bleibt unangetastet") func shortPreviewIsUnchanged() { #expect(ClipboardRules.preview(for: "kurz") == "kurz") } // MARK: - Aufnehmen @Test("Dasselbe noch einmal kopiert rutscht nach oben, statt sich anzureihen") func duplicateMovesToFront() { let existing = [item("a", at: 10), item("b", at: 20)] let again = item("a", at: 30) let result = ClipboardRules.merge(existing, adding: again) #expect(result.items.map(\.fingerprint) == ["a", "b"]) #expect(result.items.first?.addedAt == Date(timeIntervalSince1970: 30)) // Der alte Eintrag ist verdrängt — seine Datei muss weg. #expect(result.dropped.count == 1) } @Test("Über der Grenze fällt das Älteste heraus") func limitDropsOldest() { let existing = (0..<5).map { item("f\($0)", at: Double(5 - $0)) } let result = ClipboardRules.merge(existing, adding: item("neu", at: 99), limit: 3) #expect(result.items.count == 3) #expect(result.items.first?.fingerprint == "neu") // Verdrängtes wird gemeldet, damit die Dateien nicht liegen bleiben. #expect(result.dropped.count == 3) } @Test("Verdrängtes wird vollständig gemeldet, auch die Wiederholung") func droppedIncludesDuplicate() { let existing = [item("a", at: 1), item("b", at: 2), item("c", at: 3)] let result = ClipboardRules.merge(existing, adding: item("a", at: 4), limit: 2) // „a" einmal als Wiederholung, „c" als das Älteste über der Grenze. #expect(Set(result.dropped.map(\.fingerprint)) == ["a", "c"]) } // MARK: - Verfall @Test("Ohne Frist verfällt nichts") func noExpiryKeepsEverything() { let old = [item("a", at: 0)] #expect(ClipboardRules.expired(old, olderThanDays: nil, now: Date(timeIntervalSince1970: 9_000_000)).isEmpty) #expect(ClipboardRules.expired(old, olderThanDays: 0, now: Date(timeIntervalSince1970: 9_000_000)).isEmpty) } @Test("Was älter ist als die Frist, verfällt") func oldItemsExpire() { let now = Date(timeIntervalSince1970: 10 * 86_400) let items = [item("alt", at: 0), item("neu", at: 9 * 86_400)] let stale = ClipboardRules.expired(items, olderThanDays: 7, now: now) #expect(stale.map(\.fingerprint) == ["alt"]) } // MARK: - Reihenfolge @Test("Neuestes zuerst, und gleich alte in fester Reihenfolge") func sortingIsStable() { let a = item("a", at: 5) let b = item("b", at: 5) let c = item("c", at: 9) let once = ClipboardRules.sorted([a, b, c]) let twice = ClipboardRules.sorted([c, b, a]) #expect(once.first?.fingerprint == "c") // Ohne festen Zweitschlüssel tauschten gleich alte Einträge bei jeder // Anzeige die Plätze. #expect(once.map(\.id) == twice.map(\.id)) } }