TeraSupportingStoreTests.swift (8079B)
1 @testable import TeraApp 2 import XCTest 3 4 final class TeraSupportingStoreTests: XCTestCase { 5 @MainActor 6 func testSearchUsesCurrentContextDeduplicatesAndClearsEmptyQueries() async throws { 7 let backend = SupportingBackend() 8 let client = try await Self.startedClient(backend) 9 let store = TeraSearchStore( 10 runtimeClient: client, 11 clock: .fixed(unixSeconds: 1_800_000_000) 12 ) 13 store.configure(context: Self.context(id: "farm")) 14 store.updateQuery(" carrots ") 15 16 await store.search() 17 18 XCTAssertEqual(store.state, .loaded) 19 XCTAssertEqual(store.results.map(\.id), ["card", "profile"]) 20 let request = await backend.lastSearchRequest() 21 XCTAssertEqual(request?.contextID, "farm") 22 XCTAssertEqual(request?.query, "carrots") 23 store.updateQuery(" ") 24 XCTAssertEqual(store.state, .idle) 25 XCTAssertTrue(store.results.isEmpty) 26 _ = try await client.stop() 27 } 28 29 @MainActor 30 func testSearchContextChangeFencesLateResults() async throws { 31 let backend = SupportingBackend(searchDelayNanoseconds: 40_000_000) 32 let client = try await Self.startedClient(backend) 33 let store = TeraSearchStore( 34 runtimeClient: client, 35 clock: .fixed(unixSeconds: 1_800_000_000) 36 ) 37 store.configure(context: Self.context(id: "first")) 38 store.updateQuery("carrots") 39 40 let search = Task { await store.search() } 41 try await Task.sleep(nanoseconds: 2_000_000) 42 store.configure(context: Self.context(id: "second")) 43 await search.value 44 45 XCTAssertTrue(store.results.isEmpty) 46 XCTAssertEqual(store.state, .idle) 47 _ = try await client.stop() 48 } 49 50 @MainActor 51 func testMePreservesAdoptedProfileFieldsAndCurrentCards() async throws { 52 let backend = SupportingBackend() 53 let client = try await Self.startedClient(backend) 54 let store = TeraMeStore( 55 runtimeClient: client, 56 clock: .fixed(unixSeconds: 1_800_000_000) 57 ) 58 store.configure(context: Self.context(id: "farm")) 59 60 await store.start() 61 62 let snapshot = try XCTUnwrap(store.snapshot) 63 XCTAssertEqual(snapshot.profile?.displayName, "Moss Farm") 64 XCTAssertEqual(snapshot.profile?.about, "Local roots") 65 XCTAssertEqual(snapshot.profile?.nip05, "moss@example.com") 66 XCTAssertEqual(snapshot.profile?.website, "https://moss.example") 67 XCTAssertEqual(snapshot.profile?.lightningAddress, "moss@example.com") 68 XCTAssertEqual(snapshot.cards.map(\.id), ["card"]) 69 store.stop() 70 _ = try await client.stop() 71 } 72 73 func testVisualIdentityIsStableAndKeyBound() { 74 let first = TeraStableVisualIdentity(publicKeyHex: String(repeating: "a", count: 64)) 75 let repeated = TeraStableVisualIdentity(publicKeyHex: String(repeating: "a", count: 64)) 76 let second = TeraStableVisualIdentity(publicKeyHex: String(repeating: "b", count: 64)) 77 78 XCTAssertEqual(first, repeated) 79 XCTAssertNotEqual(first.digestHex, second.digestHex) 80 XCTAssertTrue((0 ..< 12).contains(first.paletteIndex)) 81 } 82 83 @MainActor 84 func testSettingsRoundTripUsesTypedRuntimeAndReportsReconfigurationEffects() async throws { 85 let backend = SupportingBackend() 86 let client = try await Self.startedClient(backend) 87 let store = TeraSettingsStore(runtimeClient: client) 88 89 await store.load(profile: nil) 90 XCTAssertEqual(store.settings?.identity.identities.count, 1) 91 store.networkEnvironment = .physicalDevice 92 store.relays = [ 93 TeraRelayPreference(url: "wss://radroots.org/", access: .readWrite), 94 TeraRelayPreference(url: "wss://read.example/", access: .readOnly), 95 ] 96 store.blossomPrimaryOrigin = "https://blossom.radroots.org" 97 store.allowCellularUploads = false 98 store.mediaCacheMegabytes = 512 99 store.mediaCacheArtifacts = 2000 100 101 let restartRequired = await store.saveSettings() 102 103 XCTAssertTrue(restartRequired) 104 XCTAssertEqual(store.settings?.revision, 2) 105 XCTAssertEqual(store.settings?.relays.map(\.access), [.readWrite, .readOnly]) 106 XCTAssertEqual(store.settings?.mediaCacheBytes, 512 * 1_048_576) 107 XCTAssertEqual(store.settings?.mediaCacheArtifacts, 2000) 108 XCTAssertEqual(store.message, "Settings saved; required changes: runtime restart, outbox requeue, media cache refresh.") 109 XCTAssertNil(store.failureCode) 110 _ = try await client.stop() 111 } 112 113 @MainActor 114 func testInvalidSettingsFailClosedWithoutReplacingLastAcceptedState() async throws { 115 let backend = SupportingBackend() 116 let client = try await Self.startedClient(backend) 117 let store = TeraSettingsStore(runtimeClient: client) 118 await store.load(profile: nil) 119 store.relays = [TeraRelayPreference(url: "https://not-a-relay.example", access: .readWrite)] 120 121 let restartRequired = await store.saveSettings() 122 123 XCTAssertFalse(restartRequired) 124 XCTAssertEqual(store.failureCode, "invalid_relay_endpoint") 125 XCTAssertEqual(store.settings?.revision, 1) 126 let revision = await backend.settingsRevision() 127 XCTAssertEqual(revision, 1) 128 _ = try await client.stop() 129 } 130 131 @MainActor 132 func testProfileEditingUsesDurableTypedStatus() async throws { 133 let backend = SupportingBackend() 134 let client = try await Self.startedClient(backend) 135 let store = TeraSettingsStore(runtimeClient: client) 136 store.profileName = "moss" 137 store.profileDisplayName = "Moss Farm" 138 store.profileAbout = "Local roots" 139 store.profileNip05 = "moss@example.com" 140 141 await store.saveProfile() 142 XCTAssertEqual(store.profileStatus?.state, .queued) 143 await store.advanceProfile() 144 XCTAssertEqual(store.profileStatus?.state, .complete) 145 XCTAssertEqual(store.profileStatus?.settlement?.deliverySatisfied, 1) 146 _ = try await client.stop() 147 } 148 149 static func startedClient(_ backend: SupportingBackend) async throws -> TeraRuntimeClient { 150 let client = TeraRuntimeClient { _ in 151 await TeraRuntimeBackendStart(backend: backend, snapshot: backend.snapshotValue()) 152 } 153 _ = try await client.start(configuration: configuration()) 154 return client 155 } 156 157 private static func configuration() -> TeraRuntimeLaunchConfiguration { 158 TeraRuntimeLaunchConfiguration( 159 applicationSupportDirectory: "/tmp/radroots-supporting-tests", 160 publicKeyHex: String(repeating: "a", count: 64), 161 sourceGenerationHex: String(repeating: "c", count: 64), 162 sourceGenerationCreatedAtUnixMilliseconds: 1, 163 protectedData: .available, 164 networkProfile: .simulator, 165 writableRelays: ["ws://127.0.0.1:7447"], 166 blossom: TeraBlossomEndpointConfiguration( 167 hostKind: .simulator, 168 endpointAuthority: .loopbackDevelopment, 169 primaryOrigin: "http://127.0.0.1:3000", 170 fallbackOrigins: [] 171 ), 172 app: TeraRuntimeAppMetadata( 173 bundleIdentifier: "org.radroots.supporting-tests", 174 version: "0.1.0-alpha", 175 buildNumber: "1", 176 buildSHA: nil 177 ), 178 signerGeneration: "supporting-tests", 179 signer: SupportingSigner(), 180 adoptBootstrapSettings: false 181 ) 182 } 183 184 private static func context(id: String) -> TeraLocalNetwork { 185 TeraLocalNetwork( 186 schemaVersion: 1, 187 id: id, 188 label: "Farm", 189 relayURLs: ["ws://127.0.0.1:7447"], 190 locality: "Metchosin", 191 followedAuthors: [], 192 generation: 1 193 ) 194 } 195 } 196 197 private struct SupportingSigner: TeraRuntimeSigner { 198 func availability() -> TeraRuntimeSignerAvailability { 199 .ready 200 } 201 202 func sign(_: TeraRuntimeSigningRequest) -> TeraRuntimeSigningOutcome { 203 .failed 204 } 205 }