krz/hutch
an ios client for sourcehut
clone: git clone https://gitbay.org/krz/hutch.git
v3.8.1: HutchTests/MailingListActivityTests.swift · raw
1import Foundation
2import Testing
3@testable import Hutch
4
5struct MailingListActivityTests {
6
7 @Test
8 func usesTheNewestArrivalOverTheRootTimestamp() {
9 // The case that started this: sr.ht reports thread.updated seven seconds
10 // after the root email on a thread carrying four replies, so the fallback
11 // must lose to real arrival data.
12 let rootInsert = Date(timeIntervalSince1970: 1_000)
13 let newestReply = Date(timeIntervalSince1970: 5_000)
14 let activity = MailingListActivity(newestByRootEmailID: [42: newestReply])
15
16 #expect(activity.lastActivity(rootEmailID: 42, fallback: rootInsert) == newestReply)
17 }
18
19 @Test
20 func fallsBackForThreadsOutsideTheScannedWindow() {
21 // Threads with nothing new are absent from the feed scan; they keep the
22 // root timestamp, which is older than any cutoff and so reads as read.
23 let rootInsert = Date(timeIntervalSince1970: 1_000)
24 let activity = MailingListActivity(newestByRootEmailID: [:])
25
26 #expect(activity.lastActivity(rootEmailID: 42, fallback: rootInsert) == rootInsert)
27 }
28
29 @Test
30 func neverGoesBackwardsFromTheFallback() {
31 // A root inserted after the newest scanned reply must not age the thread
32 // backwards.
33 let rootInsert = Date(timeIntervalSince1970: 9_000)
34 let staleReply = Date(timeIntervalSince1970: 5_000)
35 let activity = MailingListActivity(newestByRootEmailID: [42: staleReply])
36
37 #expect(activity.lastActivity(rootEmailID: 42, fallback: rootInsert) == rootInsert)
38 }
39
40 @Test
41 func tracksThreadsIndependently() {
42 let activity = MailingListActivity(newestByRootEmailID: [
43 1: Date(timeIntervalSince1970: 5_000),
44 2: Date(timeIntervalSince1970: 7_000)
45 ])
46 let fallback = Date(timeIntervalSince1970: 1_000)
47
48 #expect(activity.lastActivity(rootEmailID: 1, fallback: fallback) == Date(timeIntervalSince1970: 5_000))
49 #expect(activity.lastActivity(rootEmailID: 2, fallback: fallback) == Date(timeIntervalSince1970: 7_000))
50 #expect(activity.lastActivity(rootEmailID: 3, fallback: fallback) == fallback)
51 }
52
53 @Test
54 func newMailInAnOldThreadReadsAsUnread() {
55 let suiteName = "MailingListActivityTests-\(UUID().uuidString)"
56 let defaults = UserDefaults(suiteName: suiteName)!
57 defer { defaults.removePersistentDomain(forName: suiteName) }
58
59 let signIn = Date(timeIntervalSince1970: 5_000)
60 InboxReadStateStore.establishBaselineIfNeeded(now: signIn, defaults: defaults)
61
62 // A thread rooted long before sign-in, with a reply after it. Keyed on
63 // thread.updated this reads as read, which was the bug.
64 let rootInsert = Date(timeIntervalSince1970: 1_000)
65 let replyAfterSignIn = Date(timeIntervalSince1970: 6_000)
66 let activity = MailingListActivity(newestByRootEmailID: [42: replyAfterSignIn])
67 let lastActivityAt = activity.lastActivity(rootEmailID: 42, fallback: rootInsert)
68
69 #expect(
70 InboxReadStateStore.isUnread(
71 threadID: "list#old thread",
72 lastActivityAt: lastActivityAt,
73 defaults: defaults
74 )
75 )
76 }
77}