Files
web-terminal/ios/Packages/WireProtocol/Tests/WireProtocolTests/ContractConstantsTests.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

81 lines
3.3 KiB
Swift
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import Foundation
import Testing
import WireProtocol
// T-iOS-3 · 线Tunables§3.2.1 WireConstants
// T-iOS-3
@Test("Tunables 取值与 §3.2.1 表逐项一致")
func tunablesMatchFrozenValueTable() {
#expect(Tunables.pingInterval == .seconds(25))
#expect(Tunables.pongMissLimit == 2)
#expect(Tunables.listPollInterval == .seconds(5)) // public/launcher.ts:30 REFRESH_MS
#expect(Tunables.telemetryStaleTtlMs == 30_000) // public/tabs.ts:45 STATUSLINE_TTL_MS
#expect(Tunables.digestFadeDelay == .seconds(8))
#expect(Tunables.titleMaxLength == 256)
#expect(Tunables.pairingProbeTimeout == .seconds(10)) // §3.4 2026-07-04
#expect(Tunables.maxWSMessageBytes == 16 * 1024 * 1024)
}
@Test("maxWSMessageBytes ≥ 6 × 默认 SCROLLBACK_BYTES(2MiB)JSON 转义最坏膨胀系数)")
func maxWSMessageBytesCoversWorstCaseReplayExpansion() {
// Arrange src/config.ts:39 DEFAULT_SCROLLBACK_BYTES = 2MiB\uXXXX 6×
let defaultScrollbackBytes = 2 * 1024 * 1024
let worstCaseEscapeFactor = 6
// Assert
#expect(Tunables.maxWSMessageBytes >= worstCaseEscapeFactor * defaultScrollbackBytes)
}
@Test("WireConstantswsPath/soft-reset 前缀/spawn 失败码/resize 区间")
func wireConstantsMatchServer() {
#expect(WireConstants.wsPath == "/term") // src/config.ts:41 DEFAULT_WS_PATH
#expect(WireConstants.replaySoftResetPrefix == "\u{1B}[0m") // src/types.ts:167-170
#expect(WireConstants.spawnFailedExitCode == -1) // M4
#expect(WireConstants.resizeRange == 1...1000) // src/protocol.ts:113-115
}
@Test("TransportConnection能力句柄原样保存frames finish = 干净断线")
func transportConnectionHoldsCapabilityHandles() async throws {
// Arrange
actor Recorder {
var sentFrames: [String] = []
var isClosed = false
func recordSend(_ frame: String) { sentFrames.append(frame) }
func recordClose() { isClosed = true }
}
let recorder = Recorder()
let connection = TransportConnection(
frames: AsyncThrowingStream { continuation in
continuation.yield("{\"type\":\"output\",\"data\":\"x\"}")
continuation.finish()
},
send: { await recorder.recordSend($0) },
close: { await recorder.recordClose() }
)
// Act
var received: [String] = []
for try await frame in connection.frames {
received.append(frame)
}
try await connection.send("{\"type\":\"reject\"}")
await connection.close()
// Assert
#expect(received == ["{\"type\":\"output\",\"data\":\"x\"}"])
#expect(await recorder.sentFrames == ["{\"type\":\"reject\"}"])
#expect(await recorder.isClosed)
}
@Test("ClaudeStatus/GateKind rawValue 与服务器字面量一致")
func statusAndGateRawValuesMatchServer() {
#expect(ClaudeStatus.working.rawValue == "working")
#expect(ClaudeStatus.waiting.rawValue == "waiting")
#expect(ClaudeStatus.idle.rawValue == "idle")
#expect(ClaudeStatus.unknown.rawValue == "unknown")
#expect(ClaudeStatus.stuck.rawValue == "stuck")
#expect(GateKind.tool.rawValue == "tool")
#expect(GateKind.plan.rawValue == "plan")
}