Files
web-terminal/ios/IntegrationTests/ReplayTests.swift
Yaojia Wang 5098643355 feat(ios): W3 UI layer + integration CI + ntfy docs
T-iOS-11: TerminalScreen/KeyBar/TerminalViewModel + KeyByteMap (byte-for-byte keybar.ts, arrows excluded from UIKeyCommand to preserve DECCKM)
T-iOS-12: PairingScreen/VM — confirm-before-network (zero-call assertions), §5.4 four-tier warnings, Host construction per contract ruling
T-iOS-13: SessionListScreen/VM — Tunables-paced polling with leak-free teardown, optimistic kill+rollback, pending via overlay (LiveSessionInfo has no pending field)
T-iOS-14: GateBanner/PlanGateSheet/AwayDigestView/GateViewModel — three-way mapping from SessionCore Affordance single source, tap-epoch guard, per-epoch haptics
T-iOS-16: IntegrationTests vs real Node server (10 tests: origin guards, mirror, kill-close vs exit-frame differential, 16MiB+ESC/C0 replay) + ios.yml own-sources coverage gate (red-once demoed)
T-iOS-17: ios/README.md ntfy chapter (read-only verification, file:line cites)
Verified: 224 unit + 10 integration tests green; 5/5 semantic spot-checks; zero Owns violations
2026-07-05 00:13:14 +02:00

80 lines
4.4 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.

//
// ReplayTests.swift T-iOS-16 3>1 MiB spike +
//
// ring-buffer src/session/session.ts attachWs buffer.snapshot()
// JSON \uXXXX 1-6×src/protocol.ts:186
// - maximumMessageSize=1 MiB withKnownIssue
// "" Tunables.maxWSMessageBytes
// - Tunables.maxWSMessageBytes16 MiB
// - ESC/C0 ~6×
import Foundation
import Testing
import WireProtocol
@Suite("T-iOS-16 回放上限(真服务器)", .serialized, .timeLimit(.minutes(5)))
struct ReplayTests {
@Test(">1MiB 单帧回放 — 默认 1MiB 上限复现失败(known issue);Tunables 16MiB 成功")
func replayOver1MiBFailsAtDefaultLimitAndSucceedsAt16MiB() async throws {
let server = try await ServerHarness.shared.server()
// :attach(null) >1MiB , detach(PTY/ring )
let generator = WSTestClient(server: server, origin: server.origin)
let sessionId = try await attachAndAwaitAttached(client: generator, sessionId: nil)
try await generator.send(shellCommand(
"perl -e 'print \"a\" x \(HarnessTunables.bulkOutputBytes)'"))
let produced = try await accumulateOutputBytes(
client: generator, minBytes: HarnessTunables.bulkOutputBytes)
#expect(produced >= HarnessTunables.bulkOutputBytes)
generator.close()
// : maximumMessageSize(1 MiB)
let defaultClient = WSTestClient(server: server, origin: server.origin, maxMessageBytes: nil)
let defaultResult = await replayResult(
client: defaultClient, sessionId: sessionId, minBytes: HarnessTunables.bulkOutputBytes)
defaultClient.close()
if case .failure(let error) = defaultResult {
#expect(isMessageTooLongError(error),
"失败模式应为 NSPOSIXErrorDomain EMSGSIZE(40)/ENOBUFS(55),实际 \(describeError(error))")
}
withKnownIssue("默认 maximumMessageSize=1MiB 收不下 >1MiB 单帧 ring-buffer 回放 — 平台限制,修法即 Tunables.maxWSMessageBytes=16MiB(若此处'意外通过',说明平台行为变了,须重新评估)") {
_ = try defaultResult.get()
}
// :16 MiB ,attached sessionId
let bigClient = WSTestClient(server: server, origin: server.origin)
let summary = try await replayResult(
client: bigClient, sessionId: sessionId, minBytes: HarnessTunables.bulkOutputBytes).get()
#expect(summary.adoptedSessionId == sessionId)
#expect(summary.totalOutputBytes >= HarnessTunables.bulkOutputBytes)
bigClient.close()
await killSessionBestEffort(server: server, id: sessionId)
}
@Test("对抗: ESC/C0 密集输出(JSON 转义膨胀~6×)回放,16MiB 仍完整")
func escDenseReplayStillSucceedsAt16MiB() async throws {
let server = try await ServerHarness.shared.server()
let generator = WSTestClient(server: server, origin: server.origin)
let sessionId = try await attachAndAwaitAttached(client: generator, sessionId: nil)
// perl \e = ESC(0x1B): 4 JSON 9 ("[0m")
try await generator.send(shellCommand(
"perl -e 'print \"\\e[0m\" x \(HarnessTunables.escSequenceRepeats)'"))
let produced = try await accumulateOutputBytes(
client: generator, minBytes: HarnessTunables.escSequenceRawBytes)
#expect(produced >= HarnessTunables.escSequenceRawBytes)
generator.close()
let bigClient = WSTestClient(server: server, origin: server.origin)
let summary = try await replayResult(
client: bigClient, sessionId: sessionId,
minBytes: HarnessTunables.escSequenceRawBytes).get()
#expect(summary.adoptedSessionId == sessionId)
#expect(summary.totalOutputBytes >= HarnessTunables.escSequenceRawBytes)
#expect(summary.containsSoftReset, "回放应含 ESC[0m(转义序列在回放中原样保留)")
bigClient.close()
await killSessionBestEffort(server: server, id: sessionId)
}
}