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// πŸ€– The joiner-side welcome landing (#1376): SpaceClaim marks a fresh
5// member/DM-invite join pending and creates the welcome app-thread, so
6// WelcomeLanding hands the thread to the first screen and the bundled flow
7// asks the joiner's name. These pin the flag semantics (UserDefaults) and the
8// parked-name store that bridges "name accepted" β†’ "instance key arrived".
9@MainActor
10final class WelcomePendingTests: XCTestCase {
11 // A unique origin per test so UserDefaults flags never leak across runs.
12 private func freshInstance() -> SavedInstance {
13 SavedInstance(origin: "https://test-\(UUID().uuidString).invalid",
14 deviceToken: "tok", screenName: "", userPub: "pub", userPriv: "priv")
15 }
16
17 private func welcomeThread(id: String = "t1", hidden: Bool = false,
18 key: String = "k") -> ChatThread {
19 ChatThread(id: id, kind: "self", title: "Eleven", preview: "", lastAt: 0,
20 unread: false, pinned: false, hidden: hidden, muted: nil,
21 members: ["u1"], threadKeyB64: key, avatar: nil,
22 isApp: true, appId: "welcome", glyph: nil, badge: nil)
23 }
24
25 func testMarkPendingArmsAndRearmsTheLanding() async {
26 let inst = freshInstance()
27 XCTAssertFalse(WelcomeLanding.isPending(inst))
28
29 // A previous life of this space (same origin) already landed once β€”
30 // a FRESH join must land again, so markPending re-arms the flag.
31 UserDefaults.standard.set(true, forKey: WelcomeLanding.flagKey(inst))
32 WelcomeLanding.markPending(inst)
33 XCTAssertTrue(WelcomeLanding.isPending(inst))
34 XCTAssertFalse(UserDefaults.standard.bool(forKey: WelcomeLanding.flagKey(inst)))
35
36 // The invalid origin means conversationFinished can't answer "done",
37 // so the thread lands β€” and only ONCE per device (the landed flag).
38 let landed = await WelcomeLanding.threadToLand(instance: inst, threads: [welcomeThread()])
39 XCTAssertEqual(landed?.id, "t1")
40 let again = await WelcomeLanding.threadToLand(instance: inst, threads: [welcomeThread()])
41 XCTAssertNil(again, "the landing is once per device+instance")
42 XCTAssertTrue(WelcomeLanding.isPending(inst),
43 "pending survives the landing β€” reopening resumes the conversation")
44 }
45
46 func testThreadToLandSkipsHiddenAndKeylessThreadsAndNonWelcomeApps() async {
47 let inst = freshInstance()
48 let hidden = welcomeThread(hidden: true)
49 let keyless = welcomeThread(key: "")
50 var other = welcomeThread()
51 other.appId = "todo"
52 let noLanding = await WelcomeLanding.threadToLand(instance: inst,
53 threads: [hidden, keyless, other])
54 XCTAssertNil(noLanding)
55 // And crucially: a no-thread answer must NOT burn the landed flag β€”
56 // the claim's thread can appear one refresh later.
57 let landed = await WelcomeLanding.threadToLand(instance: inst, threads: [welcomeThread()])
58 XCTAssertEqual(landed?.id, "t1")
59 }
60
61 func testPendingLabelParksPeeksAndClears() {
62 let id = "https://label-\(UUID().uuidString).invalid"
63 XCTAssertNil(PendingLabel.peek(instanceID: id))
64 PendingLabel.park("Ada", instanceID: id)
65 XCTAssertEqual(PendingLabel.peek(instanceID: id), "Ada")
66 PendingLabel.park("Grace", instanceID: id) // a newer answer supersedes
67 XCTAssertEqual(PendingLabel.peek(instanceID: id), "Grace")
68 PendingLabel.clear(instanceID: id)
69 XCTAssertNil(PendingLabel.peek(instanceID: id))
70 }
71
72 func testPendingLabelTreatsEmptyAsNothingParked() {
73 let id = "https://label-\(UUID().uuidString).invalid"
74 PendingLabel.park("", instanceID: id)
75 XCTAssertNil(PendingLabel.peek(instanceID: id),
76 "an empty name must never publish as a blank label")
77 PendingLabel.clear(instanceID: id)
78 }
79}
80