Quellcode durchsuchen

Test: DeviceAlertsStore per-tier snooze

Extend the existing suite to cover the snooze path (untested before):
active-before/inactive-after expiry, tier isolation, past-until removal,
persistence across reload, init-time prune of expired snoozes, the strict
boundary at the until instant, and that the store itself accepts a critical
snooze (the tier!=critical gate lives in the manager).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
James Woglom vor 2 Wochen
Ursprung
Commit
c1471053db
1 geänderte Dateien mit 107 neuen und 0 gelöschten Zeilen
  1. 107 0
      TrioTests/DeviceAlertsStoreTests.swift

+ 107 - 0
TrioTests/DeviceAlertsStoreTests.swift

@@ -138,4 +138,111 @@ import Testing
         ])
         #expect(store.canDelete(dayOnly))
     }
+
+    // MARK: - Per-tier snooze
+
+    /// Builds a unique UserDefaults suite (UUID), wipes its persistent domain,
+    /// and returns the defaults plus the derived keys — so a test can seed or
+    /// reload across multiple stores on the same backing store.
+    private static func makeSuite() -> (defaults: UserDefaults, configsKey: String, snoozesKey: String) {
+        let suiteName = "DeviceAlertsStoreTests.\(UUID().uuidString)"
+        let defaults = UserDefaults(suiteName: suiteName)!
+        defaults.removePersistentDomain(forName: suiteName)
+        let configsKey = "configs.\(suiteName)"
+        let snoozesKey = "snoozes.\(suiteName)"
+        return (defaults, configsKey, snoozesKey)
+    }
+
+    @Test("Snoozed tier is active before its expiry") func snoozeTimeSensitiveActiveBeforeExpiry() {
+        let now = Date()
+        let store = Self.makeStore()
+        store.snoozeTier(.timeSensitive, until: now.addingTimeInterval(600))
+        #expect(store.isTierSnoozed(.timeSensitive, at: now))
+        #expect(!store.isTierSnoozed(.timeSensitive, at: now.addingTimeInterval(601)))
+    }
+
+    @Test("Snoozing one tier doesn't affect another") func snoozeDoesNotAffectOtherTiers() {
+        let now = Date()
+        let store = Self.makeStore()
+        store.snoozeTier(.timeSensitive, until: now.addingTimeInterval(600))
+        #expect(!store.isTierSnoozed(.normal, at: now))
+    }
+
+    @Test("Snooze with a past until removes the key") func snoozeWithPastUntilRemoves() {
+        let now = Date()
+        let store = Self.makeStore()
+        store.snoozeTier(.timeSensitive, until: now.addingTimeInterval(-1))
+        #expect(!store.isTierSnoozed(.timeSensitive, at: now))
+        #expect(store.tierSnoozes["timeSensitive"] == nil)
+
+        // Live snooze, then a past until should remove the existing key.
+        store.snoozeTier(.timeSensitive, until: now.addingTimeInterval(600))
+        #expect(store.tierSnoozes["timeSensitive"] != nil)
+        store.snoozeTier(.timeSensitive, until: now.addingTimeInterval(-1))
+        #expect(store.tierSnoozes["timeSensitive"] == nil)
+    }
+
+    @Test("Snooze persists across a store reload") func snoozePersistsAcrossStoreReload() {
+        let now = Date()
+        let suite = Self.makeSuite()
+        let store1 = DeviceAlertsStore(
+            defaults: suite.defaults,
+            configsKey: suite.configsKey,
+            snoozesKey: suite.snoozesKey
+        )
+        store1.snoozeTier(.timeSensitive, until: now.addingTimeInterval(3600))
+        let store2 = DeviceAlertsStore(
+            defaults: suite.defaults,
+            configsKey: suite.configsKey,
+            snoozesKey: suite.snoozesKey
+        )
+        #expect(store2.isTierSnoozed(.timeSensitive, at: now))
+    }
+
+    @Test("Expired snooze is pruned on load") func expiredSnoozeIsPrunedOnLoad() {
+        let now = Date()
+        let suite = Self.makeSuite()
+        let expired: [String: Date] = ["timeSensitive": now.addingTimeInterval(-60)]
+        suite.defaults.set(try? JSONEncoder().encode(expired), forKey: suite.snoozesKey)
+        let store = DeviceAlertsStore(
+            defaults: suite.defaults,
+            configsKey: suite.configsKey,
+            snoozesKey: suite.snoozesKey
+        )
+        #expect(store.tierSnoozes.isEmpty)
+        #expect(!store.isTierSnoozed(.timeSensitive, at: now))
+
+        // Companion: seed both an expired and a future entry — only the future
+        // .normal entry should survive the load-time prune.
+        let suite2 = Self.makeSuite()
+        let mixed: [String: Date] = [
+            "timeSensitive": now.addingTimeInterval(-60),
+            "normal": now.addingTimeInterval(3600)
+        ]
+        suite2.defaults.set(try? JSONEncoder().encode(mixed), forKey: suite2.snoozesKey)
+        let store2 = DeviceAlertsStore(
+            defaults: suite2.defaults,
+            configsKey: suite2.configsKey,
+            snoozesKey: suite2.snoozesKey
+        )
+        #expect(store2.tierSnoozes["timeSensitive"] == nil)
+        #expect(store2.tierSnoozes["normal"] != nil)
+        #expect(store2.isTierSnoozed(.normal, at: now))
+    }
+
+    @Test("isTierSnoozed is false exactly at the until instant") func isTierSnoozedFalseExactlyAtUntilInstant() {
+        let now = Date()
+        let until = now.addingTimeInterval(600)
+        let store = Self.makeStore()
+        store.snoozeTier(.timeSensitive, until: until)
+        #expect(!store.isTierSnoozed(.timeSensitive, at: until))
+        #expect(store.isTierSnoozed(.timeSensitive, at: until.addingTimeInterval(-0.001)))
+    }
+
+    @Test("Store accepts a critical snooze") func storeAcceptsCriticalSnooze() {
+        let now = Date()
+        let store = Self.makeStore()
+        store.snoozeTier(.critical, until: now.addingTimeInterval(600))
+        #expect(store.isTierSnoozed(.critical, at: now))
+    }
 }