Files
web-terminal/ios/IntegrationTests/OriginGuardTests.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

68 lines
3.5 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.

//
// OriginGuardTests.swift T-iOS-16 5Origin spike
//
// plan §5.1 " CI Origin "= CRITICAL
// 401/403 ""
// - WS Origin 401src/server.ts:646-651 Origin
// - WS Origin 401src/http/origin.ts:47-51
// :443/:80 new URL()
// - DELETE /live-sessions/:id Origin 403G id
// src/server.ts:332-339 requireAllowedOrigin
import Foundation
import Testing
import WireProtocol
@Suite("T-iOS-16 Origin 守卫(真服务器)", .serialized, .timeLimit(.minutes(5)))
struct OriginGuardTests {
@Test("无 Origin 的 WS 升级被 401 拒绝(服务器默认拒空 Origin)")
func upgradeWithoutOriginIsRejectedWith401() async throws {
let server = try await ServerHarness.shared.server()
let client = WSTestClient(server: server, origin: nil)
defer { client.close() }
let failure = await client.handshakeFailure(timeout: HarnessTunables.frameTimeout)
#expect(failure != nil, "无 Origin 升级必须失败")
#expect(client.handshakeStatusCode == 401,
"应收 401(src/server.ts:646-651),实际 status=\(String(describing: client.handshakeStatusCode)) err=\(failure.map(describeError) ?? "nil")")
}
@Test("端口失配的 Origin → 401(端口精确比对;默认端口规范化不是失配案例)")
func portMismatchedOriginIsRejectedWith401() async throws {
let server = try await ServerHarness.shared.server()
let mismatchPort = server.port == HarnessTunables.mismatchedOriginPort
? HarnessTunables.mismatchedOriginPortFallback
: HarnessTunables.mismatchedOriginPort
let badOrigin = "http://127.0.0.1:\(mismatchPort)"
let client = WSTestClient(server: server, origin: badOrigin)
defer { client.close() }
let failure = await client.handshakeFailure(timeout: HarnessTunables.frameTimeout)
#expect(failure != nil, "端口失配 Origin 升级必须失败")
#expect(client.handshakeStatusCode == 401,
"应收 401(src/http/origin.ts:47-51),实际 status=\(String(describing: client.handshakeStatusCode)) err=\(failure.map(describeError) ?? "nil")")
}
@Test("DELETE /live-sessions/:id 无 Origin → 403;带 Origin 的同样请求 → 404(差分证明 403 来自守卫)")
func deleteWithoutOriginIsRejectedWith403() async throws {
// Arrange: id id ,
let server = try await ServerHarness.shared.server()
let bogusId = UUID().uuidString.lowercased()
// Act
let statusWithoutOrigin = try await deleteLiveSession(
server: server, id: bogusId, origin: nil)
let statusWithOrigin = try await deleteLiveSession(
server: server, id: bogusId, origin: server.origin)
// Assert: Origin (403); Origin id (404)
#expect(statusWithoutOrigin == 403,
"无 Origin 的 DELETE 应 403(src/server.ts:332-339),实际 \(statusWithoutOrigin)")
#expect(statusWithOrigin == 404,
"带 Origin 但 id 不存在应 404(src/server.ts:354-359),实际 \(statusWithOrigin)")
}
}