T-iOS-5: pure reconnect reducer (1s→30s cap, mirrors terminal-session.ts) + 25s PingScheduler, 16 tests
T-iOS-6: gate epoch tracker (rising-edge semantics, canDecide guard) + AwayDigest reducer, 25 tests
T-iOS-7: HostRegistry with SecItemShim keychain seam, 30 tests, 88.1% own-src coverage
T-iOS-8: APIClient (Origin iff-G invariant) + two-step pairing probe, 45 tests, 98.1% coverage
Contract ruling: probe returns Result<HostEndpoint,_> (Host{id,name} built by pairing VM) —
resolves frozen-contract contradiction reported via BLOCKED protocol; adds Tunables.pairingProbeTimeout(10s)
Verified: 178 tests green across 5 packages; coverage gates pass; zero Owns violations
33 lines
971 B
Swift
33 lines
971 B
Swift
import Foundation
|
|
|
|
/// In-memory `HostStore`. Lives in Sources (not Tests) on purpose — plan
|
|
/// T-iOS-7: it doubles for the keychain store in this package's contract
|
|
/// tests AND in the App layer's ViewModel tests (which import HostRegistry).
|
|
///
|
|
/// Actor-isolated so concurrent upserts/removes serialize; the state is a
|
|
/// value-type snapshot replaced wholesale on every change (no in-place
|
|
/// mutation of shared references).
|
|
public actor InMemoryHostStore: HostStore {
|
|
private var hosts: [Host]
|
|
|
|
public init(hosts: [Host] = []) {
|
|
self.hosts = hosts
|
|
}
|
|
|
|
public func loadAll() async throws -> [Host] {
|
|
hosts
|
|
}
|
|
|
|
public func upsert(_ host: Host) async throws -> [Host] {
|
|
let updated = hosts.upserting(host)
|
|
hosts = updated
|
|
return updated
|
|
}
|
|
|
|
public func remove(id: UUID) async throws -> [Host] {
|
|
let updated = hosts.removing(id: id)
|
|
hosts = updated
|
|
return updated
|
|
}
|
|
}
|