Files
web-terminal/ios/Packages/HostRegistry/Tests/HostRegistryTests/LastSessionStoreTests.swift
Yaojia Wang 95438cdc12 feat(ios): W1 leaf packages — ReconnectMachine/PingScheduler, GateState/AwayDigest, HostRegistry, APIClient+pairing probe
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
2026-07-04 21:53:41 +02:00

92 lines
3.1 KiB
Swift

import Foundation
import Testing
import HostRegistry
// UserDefaultsLastSessionStore over an injectable, per-test-unique suite
// (plan §3.3: ;lastSessionId /)
@Test("lastSessionId 存取:set 后读回同一 UUID")
func setThenGetReturnsSameSessionId() throws {
// Arrange
let suiteName = Fixtures.makeSuiteName()
let defaults = try #require(UserDefaults(suiteName: suiteName))
defer { defaults.removePersistentDomain(forName: suiteName) }
let store = UserDefaultsLastSessionStore(defaults: defaults)
let host = UUID()
let session = UUID()
// Act
store.setLastSessionId(session, host: host)
// Assert
#expect(store.lastSessionId(host: host) == session)
}
@Test("未设置过的 host:lastSessionId 返回 nil")
func unknownHostReturnsNil() throws {
// Arrange
let suiteName = Fixtures.makeSuiteName()
let defaults = try #require(UserDefaults(suiteName: suiteName))
defer { defaults.removePersistentDomain(forName: suiteName) }
let store = UserDefaultsLastSessionStore(defaults: defaults)
// Act / Assert
#expect(store.lastSessionId(host: UUID()) == nil)
}
@Test("set nil:清除既有 lastSessionId")
func settingNilClearsStoredSessionId() throws {
// Arrange
let suiteName = Fixtures.makeSuiteName()
let defaults = try #require(UserDefaults(suiteName: suiteName))
defer { defaults.removePersistentDomain(forName: suiteName) }
let store = UserDefaultsLastSessionStore(defaults: defaults)
let host = UUID()
store.setLastSessionId(UUID(), host: host)
// Act
store.setLastSessionId(nil, host: host)
// Assert
#expect(store.lastSessionId(host: host) == nil)
}
@Test("不同 host 键隔离:互不覆盖")
func distinctHostsDoNotCollide() throws {
// Arrange
let suiteName = Fixtures.makeSuiteName()
let defaults = try #require(UserDefaults(suiteName: suiteName))
defer { defaults.removePersistentDomain(forName: suiteName) }
let store = UserDefaultsLastSessionStore(defaults: defaults)
let hostA = UUID()
let hostB = UUID()
let sessionA = UUID()
let sessionB = UUID()
// Act
store.setLastSessionId(sessionA, host: hostA)
store.setLastSessionId(sessionB, host: hostB)
// Assert
#expect(store.lastSessionId(host: hostA) == sessionA)
#expect(store.lastSessionId(host: hostB) == sessionB)
}
@Test("UserDefaults 中的非 UUID 垃圾值:读回 nil 而非 crash(防御性解析)")
func garbageStoredValueReadsBackAsNil() throws {
// Arrange discover the store's key via the suite's own domain, then poison it
let suiteName = Fixtures.makeSuiteName()
let defaults = try #require(UserDefaults(suiteName: suiteName))
defer { defaults.removePersistentDomain(forName: suiteName) }
let store = UserDefaultsLastSessionStore(defaults: defaults)
let host = UUID()
store.setLastSessionId(UUID(), host: host)
let key = try #require(defaults.persistentDomain(forName: suiteName)?.keys.first)
// Act
defaults.set("not-a-uuid", forKey: key)
// Assert
#expect(store.lastSessionId(host: host) == nil)
}