Files
web-terminal/ios/Packages/WireProtocol/Tests/WireProtocolTests/ContractConstantsTests.swift
Yaojia Wang cbaa08daba feat(ios): W0 scaffold + day-1 spike + WireProtocol frozen contract + TestSupport doubles
T-iOS-1: ios/ XcodeGen project (iOS 17, Swift 6 strict concurrency, ATS per PLAN §5.2), 5 SPM package shells, CI skeleton
T-iOS-2: Origin spike vs real server — URLSessionWebSocketTask custom Origin CONFIRMED (no Starscream); 16MiB replay + EMSGSIZE(40) errno correction written back to plan
T-iOS-3: WireProtocol frozen contract, 59 tests, 100% line coverage, cross-impl vectors vs src/protocol.ts via tsx
T-iOS-4: FakeTransport/FakeClock/FakeHTTPTransport doubles
Verify: independent agent re-ran all acceptance — 6/6 PASS
2026-07-04 21:19:30 +02:00

80 lines
3.2 KiB
Swift
Raw 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.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")
}