eleven / messenger Public

Clone
git clone https://amadan.net/eleven/messenger

Plain git — no account needed to clone.

Download

Download this file

1import XCTest
2@testable import LChatCore
3
4// 🤖 Regression guard for issue #580 (iOS threading). A stalled main thread let
5// a member's repeated taps on a chat row queue; each ran the list's
6// `path.append(.thread(id))`, so the NavigationStack ended up holding duplicate
7// copies of one chat and "back" surfaced a phantom second copy. The push must
8// be idempotent while that chat is already on top.
9//
10// This can't reproduce the underlying STALL (that lives in the SwiftUI layout
11// engine / gesture system), but it pins the state-machine invariant the stall
12// exposed: N taps on the same chat → exactly one push.
13final class ChatNavGuardTests: XCTestCase {
14
15 // Model the list's onSelect reducer over a stack of pushed thread ids
16 // (empty = the chat list on top). Mirrors ThreadListView's push.
17 private func applyTaps(_ taps: [String], guarded: Bool) -> [String] {
18 var stack: [String] = []
19 for id in taps {
20 if !guarded || ChatNavGuard.shouldPush(threadID: id, topThreadID: stack.last) {
21 stack.append(id)
22 }
23 }
24 return stack
25 }
26
27 // The bug, reproduced: four queued taps on ONE chat push four copies when
28 // unguarded — exactly the "back went to a second copy" report.
29 func testQueuedTapsDuplicateWithoutGuard() {
30 XCTAssertEqual(applyTaps(["T1", "T1", "T1", "T1"], guarded: false),
31 ["T1", "T1", "T1", "T1"])
32 }
33
34 // The fix: the same queued taps collapse to a single push.
35 func testQueuedTapsCollapseWithGuard() {
36 XCTAssertEqual(applyTaps(["T1", "T1", "T1", "T1"], guarded: true), ["T1"])
37 }
38
39 // Guarding must not block legitimate navigation between DIFFERENT chats.
40 func testDistinctChatsStillPush() {
41 XCTAssertEqual(applyTaps(["T1", "T2", "T3"], guarded: true), ["T1", "T2", "T3"])
42 }
43
44 // Re-opening a chat you just left is legitimate — it isn't on top anymore.
45 func testReopenAfterLeavingPushes() {
46 var stack = ["T1"]
47 stack.removeLast() // back to the list
48 if ChatNavGuard.shouldPush(threadID: "T1", topThreadID: stack.last) {
49 stack.append("T1")
50 }
51 XCTAssertEqual(stack, ["T1"])
52 }
53
54 func testShouldPushPredicate() {
55 XCTAssertTrue(ChatNavGuard.shouldPush(threadID: "T1", topThreadID: nil)) // from the list
56 XCTAssertTrue(ChatNavGuard.shouldPush(threadID: "T2", topThreadID: "T1")) // switching chats
57 XCTAssertFalse(ChatNavGuard.shouldPush(threadID: "T1", topThreadID: "T1")) // already on top
58 }
59}
60