eleven / ocho Public

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

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
81// πŸ€– The admin-side twin: a space claimed through an admin-granting setup
82// link lands its admin in Eleven's SETUP conversation β€” whether the space was
83// created natively or on the web and handed over with "Open in the app".
84@MainActor
85final class SetupPendingTests: XCTestCase {
86 private func freshInstance() -> SavedInstance {
87 SavedInstance(origin: "https://setup-\(UUID().uuidString).invalid",
88 deviceToken: "tok", screenName: "", userPub: "pub", userPriv: "priv")
89 }
90
91 private func setupThread(id: String = "s1", app: String = "setup") -> ChatThread {
92 ChatThread(id: id, kind: "self", title: "Eleven", preview: "", lastAt: 0,
93 unread: false, pinned: false, hidden: false, muted: nil,
94 members: ["u1"], threadKeyB64: "k", avatar: nil,
95 isApp: true, appId: app, glyph: nil, badge: nil)
96 }
97
98 func testSetupLandsOnceWhenTheClaimArmedIt() async {
99 let inst = freshInstance()
100 // Unarmed: an admin whose setup was done long ago (before the flow
101 // recorded a finish marker) must never be dragged back into it.
102 let unarmed = await SetupLanding.threadToLand(instance: inst, threads: [setupThread()])
103 XCTAssertNil(unarmed)
104
105 SetupLanding.markPending(inst)
106 XCTAssertTrue(SetupLanding.isPending(inst))
107 let landed = await SetupLanding.threadToLand(instance: inst, threads: [setupThread()])
108 XCTAssertEqual(landed?.id, "s1")
109 let again = await SetupLanding.threadToLand(instance: inst, threads: [setupThread()])
110 XCTAssertNil(again, "the landing is once per device+instance")
111 }
112
113 func testSetupIgnoresOtherAppThreads() async {
114 let inst = freshInstance()
115 SetupLanding.markPending(inst)
116 let none = await SetupLanding.threadToLand(instance: inst,
117 threads: [setupThread(app: "welcome")])
118 XCTAssertNil(none)
119 }
120}
121