//
// OriginGuardTests.swift — T-iOS-16 测试 5：Origin 守卫回归（吸收 spike①④）
//
// plan §5.1 的自动化化身。安全注：任何"为了过 CI 放宽 Origin 断言"= CRITICAL——
// 401/403 的精确断言不许软化成"连接失败即可"。
//   - WS 升级无 Origin → 401（src/server.ts:646-651，默认拒空 Origin）
//   - WS 升级端口失配 Origin → 401（src/http/origin.ts:47-51 端口精确比对；
//     注意 :443/:80 这类默认端口被两侧 new URL() 对称规范化，不是失配案例）
//   - DELETE /live-sessions/:id 无 Origin → 403（G 守卫先于 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)")
    }
}
