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// πŸ€– Round-trips for the local message store: upsert β†’ page, live-echo merge
5// (bare echo must not clobber reactions), delete, and FTS search.
6final class MessageStoreTests: XCTestCase {
7 func testRoundTripMergeAndSearch() async {
8 let store = MessageStore.shared
9 let instance = "test-\(UUID().uuidString)"
10 var p = MessagePayload()
11 p.text = "the quick encrypted fox"
12 p.name = "Ada"
13 await store.upsert(instance: instance, thread: "t1", [
14 .init(id: 1, at: 100, edited: false,
15 reactions: [Reaction(emoji: "πŸ‘", count: 2, mine: true)], payload: p)
16 ])
17 // A live echo (no reactions) must not clobber the stored tally.
18 await store.upsert(instance: instance, thread: "t1", [
19 .init(id: 1, at: 100, edited: false, reactions: [], payload: p)
20 ])
21 var page = await store.page(instance: instance, thread: "t1")
22 XCTAssertEqual(page.count, 1)
23 XCTAssertEqual(page[0].payload.text, "the quick encrypted fox")
24 XCTAssertEqual(page[0].reactions.first?.count, 2)
25
26 let hits = await store.search(instance: instance, query: "encrypted")
27 XCTAssertEqual(hits.first?.message.id, 1)
28
29 await store.delete(instance: instance, thread: "t1", id: 1)
30 page = await store.page(instance: instance, thread: "t1")
31 XCTAssertTrue(page.isEmpty)
32 await store.clear(instance: instance)
33 }
34
35 // suspend() (the 0xdead10cc lock-hygiene close) gates the store until
36 // resume(): a straggler write while suspended must no-op instead of
37 // reopening the db (round two of the kill β€” a detached page-fold upsert
38 // landing after the background close), and resume() restores everything
39 // written before the gate went down.
40 func testSuspendGatesUntilResume() async {
41 let store = MessageStore.shared
42 let instance = "test-\(UUID().uuidString)"
43 var p = MessagePayload()
44 p.text = "written before the suspend"
45 await store.upsert(instance: instance, thread: "t1", [
46 .init(id: 1, at: 100, edited: false, reactions: [], payload: p)
47 ])
48 await store.suspend()
49 // Reads and writes both no-op while the gate is down.
50 var page = await store.page(instance: instance, thread: "t1")
51 XCTAssertTrue(page.isEmpty)
52 p.text = "straggler while suspended"
53 await store.upsert(instance: instance, thread: "t1", [
54 .init(id: 2, at: 200, edited: false, reactions: [], payload: p)
55 ])
56 await store.resume()
57 page = await store.page(instance: instance, thread: "t1")
58 XCTAssertEqual(page.map(\.id), [1])
59 XCTAssertEqual(page.first?.payload.text, "written before the suspend")
60 // And a fresh write after the resume lands as before.
61 p.text = "written after"
62 await store.upsert(instance: instance, thread: "t1", [
63 .init(id: 3, at: 300, edited: false, reactions: [], payload: p)
64 ])
65 let again = await store.page(instance: instance, thread: "t1")
66 XCTAssertEqual(again.count, 2)
67 await store.clear(instance: instance)
68 }
69
70 // The FTS index is keyed by the messages rowid: re-upserting (an edit)
71 // must leave exactly one hit carrying the new text, and the old text
72 // must stop matching.
73 func testEditReindexesSearch() async {
74 let store = MessageStore.shared
75 let instance = "test-\(UUID().uuidString)"
76 var p = MessagePayload()
77 p.text = "the original phrasing"
78 await store.upsert(instance: instance, thread: "t1", [
79 .init(id: 1, at: 100, edited: false, reactions: [], payload: p)
80 ])
81 p.text = "the rewritten phrasing"
82 await store.upsert(instance: instance, thread: "t1", [
83 .init(id: 1, at: 100, edited: true, reactions: [], payload: p)
84 ])
85 let hits = await store.search(instance: instance, query: "rewritten")
86 XCTAssertEqual(hits.count, 1)
87 XCTAssertEqual(hits.first?.message.payload.text, "the rewritten phrasing")
88 let stale = await store.search(instance: instance, query: "original")
89 XCTAssertTrue(stale.isEmpty)
90 await store.clear(instance: instance)
91 }
92
93 // clear() must take the FTS rows with the messages β€” a hit surviving a
94 // sign-out would leak another instance's text into search.
95 func testClearDropsSearchIndex() async {
96 let store = MessageStore.shared
97 let instance = "test-\(UUID().uuidString)"
98 var p = MessagePayload()
99 p.text = "ephemeral zanzibar content"
100 await store.upsert(instance: instance, thread: "t1", [
101 .init(id: 1, at: 100, edited: false, reactions: [], payload: p)
102 ])
103 await store.clear(instance: instance)
104 let hits = await store.search(instance: instance, query: "zanzibar")
105 XCTAssertTrue(hits.isEmpty)
106 }
107}
108