568 lines · 23.3 KB · Swift
RawDownload
import Foundationimport Testing#if canImport(Darwin)import Darwin#elseimport Glibc#endif@testable import CodexBarCLI@testable import CodexBarCore /// Raw-socket coverage for `codexbar serve`: boots the real `CLILocalHTTPServer` on an/// ephemeral port, writes HTTP/1.1 bytes over a plain TCP connection, and asserts on the/// raw response bytes. This exercises header parsing and response serialization on the/// wire instead of calling the router or handlers directly.////// Serialized: every case runs its own server whose accept loop occupies a cooperative/// thread; running them concurrently starves the pool and stalls unrelated suites.@Suite(.serialized)struct CLIServeRawHTTPTests {    @Test    func `raw server serializes status body and extra headers on the wire`() async throws {        try await Self.withServer(handler: { _ in            CLILocalHTTPResponse(                status: .ok,                body: Data(#"{"status":"ok"}"#.utf8),                extraHeaders: [("X-Test", "value")])        }, body: { port in            let response = try await Self.rawExchange(                port: port,                request: "GET /health HTTP/1.1\r\nHost: 127.0.0.1\r\n\r\n")             #expect(response.statusLine == "HTTP/1.1 200 OK")            #expect(response.headerValue("Content-Type") == "application/json; charset=utf-8")            #expect(response.headerValue("Content-Length") == "15")            #expect(response.headerValue("Connection") == "close")            #expect(response.headerValue("X-Test") == "value")            #expect(response.body == #"{"status":"ok"}"#)        })    }     @Test    func `raw server rejects non loopback host headers by default`() async throws {        try await Self.withServer(handler: { _ in Self.okResponse() }, body: { port in            let response = try await Self.rawExchange(                port: port,                request: "GET /health HTTP/1.1\r\nHost: evil.test\r\n\r\n")             #expect(response.statusLine == "HTTP/1.1 403 Forbidden")            #expect(response.body == #"{"error":"forbidden host"}"#)        })    }     @Test    func `raw server accepts a configured non loopback host alongside loopback`() async throws {        try await Self.withServer(            allowedHosts: .loopbackAnd(["dashboard.local"]),            handler: { _ in Self.okResponse() },            body: { port in                let allowed = try await Self.rawExchange(                    port: port,                    request: "GET /health HTTP/1.1\r\nHost: dashboard.local:8080\r\n\r\n")                let loopback = try await Self.rawExchange(                    port: port,                    request: "GET /health HTTP/1.1\r\nHost: 127.0.0.1\r\n\r\n")                let disallowed = try await Self.rawExchange(                    port: port,                    request: "GET /health HTTP/1.1\r\nHost: evil.test\r\n\r\n")                 #expect(allowed.statusLine == "HTTP/1.1 200 OK")                #expect(loopback.statusLine == "HTTP/1.1 200 OK")                #expect(disallowed.statusLine == "HTTP/1.1 403 Forbidden")            })    }     @Test    func `raw server rejects duplicate host headers`() async throws {        try await Self.withServer(handler: { _ in Self.okResponse() }, body: { port in            let response = try await Self.rawExchange(                port: port,                request: "GET /health HTTP/1.1\r\nHost: 127.0.0.1\r\nHost: localhost\r\n\r\n")             #expect(response.statusLine == "HTTP/1.1 400 Bad Request")            #expect(response.headerValue("Cache-Control") == "no-store")        })    }     @Test    func `raw server rejects duplicate authorization headers`() async throws {        try await Self.withServer(handler: { _ in Self.okResponse() }, body: { port in            let response = try await Self.rawExchange(                port: port,                request: [                    "GET /health HTTP/1.1",                    "Host: 127.0.0.1",                    "Authorization: Bearer one",                    "Authorization: Bearer two",                    "",                    "",                ].joined(separator: "\r\n"))             #expect(response.statusLine == "HTTP/1.1 400 Bad Request")            #expect(response.body == #"{"error":"invalid request"}"#)            #expect(response.headerValue("Cache-Control") == "no-store")        })    }     @Test    func `raw server passes the authorization header to the handler`() async throws {        try await Self.withServer(handler: { request in            CLILocalHTTPResponse(                status: .ok,                body: Data((request.authorization ?? "none").utf8),                contentType: "text/plain")        }, body: { port in            let withHeader = try await Self.rawExchange(                port: port,                request: "GET /health HTTP/1.1\r\nHost: 127.0.0.1\r\nAuthorization: Bearer secret\r\n\r\n")            let withoutHeader = try await Self.rawExchange(                port: port,                request: "GET /health HTTP/1.1\r\nHost: 127.0.0.1\r\n\r\n")             #expect(withHeader.body == "Bearer secret")            #expect(withoutHeader.body == "none")        })    }     // MARK: - Dashboard snapshot auth (production handler)     @Test    func `snapshot without credentials returns 401 with challenge and no-store`() async throws {        try await Self.withServeRuntime(token: "secret", body: { port in            let response = try await Self.rawExchange(                port: port,                request: "GET /dashboard/v1/snapshot HTTP/1.1\r\nHost: 127.0.0.1\r\n\r\n")             #expect(response.statusLine == "HTTP/1.1 401 Unauthorized")            #expect(response.headerValue("WWW-Authenticate") == "Bearer")            #expect(response.headerValue("Cache-Control") == "no-store")            #expect(response.body == #"{"error":"unauthorized"}"#)        })    }     @Test    func `snapshot with wrong token returns 401`() async throws {        try await Self.withServeRuntime(token: "secret", body: { port in            let response = try await Self.rawExchange(                port: port,                request: "GET /dashboard/v1/snapshot HTTP/1.1\r\nHost: 127.0.0.1\r\n"                    + "Authorization: Bearer wrong\r\n\r\n")             #expect(response.statusLine == "HTTP/1.1 401 Unauthorized")            #expect(response.headerValue("WWW-Authenticate") == "Bearer")            #expect(response.headerValue("Cache-Control") == "no-store")        })    }     @Test    func `snapshot with correct token returns decodable JSON with no-store`() async throws {        try await Self.withServeRuntime(token: "secret", body: { port in            let response = try await Self.rawExchange(                port: port,                request: "GET /dashboard/v1/snapshot HTTP/1.1\r\nHost: 127.0.0.1\r\n"                    + "Authorization: Bearer secret\r\n\r\n")             #expect(response.statusLine == "HTTP/1.1 200 OK")            #expect(response.headerValue("Cache-Control") == "no-store")            #expect(response.headerValues("Cache-Control").count == 1)            let object = try #require(                JSONSerialization.jsonObject(with: Data(response.body.utf8)) as? [String: Any])            #expect(object["schemaVersion"] as? Int == 1)            #expect((object["providers"] as? [Any])?.isEmpty == true)            #expect(object["host"] is [String: Any])        })    }     @Test    func `snapshot never accepts the token from the query string`() async throws {        try await Self.withServeRuntime(token: "secret", body: { port in            let response = try await Self.rawExchange(                port: port,                request: "GET /dashboard/v1/snapshot?token=secret HTTP/1.1\r\nHost: 127.0.0.1\r\n\r\n")             #expect(response.statusLine == "HTTP/1.1 401 Unauthorized")            #expect(response.headerValue("Cache-Control") == "no-store")        })    }     @Test    func `snapshot with duplicate authorization headers returns 400`() async throws {        try await Self.withServeRuntime(token: "secret", body: { port in            let response = try await Self.rawExchange(                port: port,                request: [                    "GET /dashboard/v1/snapshot HTTP/1.1",                    "Host: 127.0.0.1",                    "Authorization: Bearer secret",                    "Authorization: Bearer secret",                    "",                    "",                ].joined(separator: "\r\n"))             #expect(response.statusLine == "HTTP/1.1 400 Bad Request")            #expect(response.headerValue("Cache-Control") == "no-store")        })    }     @Test    func `snapshot rejects non get methods with 405`() async throws {        try await Self.withServeRuntime(token: "secret", body: { port in            let response = try await Self.rawExchange(                port: port,                request: "POST /dashboard/v1/snapshot HTTP/1.1\r\nHost: 127.0.0.1\r\n"                    + "Authorization: Bearer secret\r\nContent-Length: 0\r\n\r\n")             #expect(response.statusLine == "HTTP/1.1 405 Method Not Allowed")            #expect(response.headerValue("Cache-Control") == "no-store")        })    }     @Test    func `dashboard missing routes return no-store`() async throws {        try await Self.withServeRuntime(token: "secret", body: { port in            let response = try await Self.rawExchange(                port: port,                request: "GET /dashboard/v1/missing HTTP/1.1\r\nHost: 127.0.0.1\r\n\r\n")             #expect(response.statusLine == "HTTP/1.1 404 Not Found")            #expect(response.headerValue("Cache-Control") == "no-store")        })    }     @Test    func `snapshot fails closed when no token is configured`() async throws {        try await Self.withServeRuntime(token: nil, body: { port in            let response = try await Self.rawExchange(                port: port,                request: "GET /dashboard/v1/snapshot HTTP/1.1\r\nHost: 127.0.0.1\r\n"                    + "Authorization: Bearer anything\r\n\r\n")             #expect(response.statusLine == "HTTP/1.1 401 Unauthorized")            #expect(response.headerValue("Cache-Control") == "no-store")        })    }     @Test    func `usage and cost responses carry no-store on the wire`() async throws {        try await Self.withServeRuntime(token: nil, body: { port in            let usage = try await Self.rawExchange(                port: port,                request: "GET /usage HTTP/1.1\r\nHost: 127.0.0.1\r\n\r\n")            let cost = try await Self.rawExchange(                port: port,                request: "GET /cost HTTP/1.1\r\nHost: 127.0.0.1\r\n\r\n")             #expect(usage.statusLine == "HTTP/1.1 200 OK")            #expect(usage.headerValue("Cache-Control") == "no-store")            #expect(usage.headerValues("Cache-Control").count == 1)            // All providers are disabled in this runtime, so /cost rejects the request,            // but even error responses on account-data routes stay uncacheable.            #expect(cost.statusLine == "HTTP/1.1 400 Bad Request")            #expect(cost.headerValue("Cache-Control") == "no-store")        })    }     @Test    func `non-loopback binds gate usage and cost behind the token`() async throws {        try await Self.withServeRuntime(token: "secret", bindHost: "0.0.0.0", body: { port in            let usageDenied = try await Self.rawExchange(                port: port,                request: "GET /usage HTTP/1.1\r\nHost: 127.0.0.1\r\n\r\n")            let costDenied = try await Self.rawExchange(                port: port,                request: "GET /cost HTTP/1.1\r\nHost: 127.0.0.1\r\n\r\n")            let usageAllowed = try await Self.rawExchange(                port: port,                request: "GET /usage HTTP/1.1\r\nHost: 127.0.0.1\r\n"                    + "Authorization: Bearer secret\r\n\r\n")            let health = try await Self.rawExchange(                port: port,                request: "GET /health HTTP/1.1\r\nHost: 127.0.0.1\r\n\r\n")             #expect(usageDenied.statusLine == "HTTP/1.1 401 Unauthorized")            #expect(usageDenied.headerValue("WWW-Authenticate") == "Bearer")            #expect(usageDenied.headerValue("Cache-Control") == "no-store")            #expect(costDenied.statusLine == "HTTP/1.1 401 Unauthorized")            #expect(costDenied.headerValue("Cache-Control") == "no-store")            #expect(usageAllowed.statusLine == "HTTP/1.1 200 OK")            #expect(usageAllowed.headerValue("Cache-Control") == "no-store")            // /health carries no account data and stays open for liveness probes.            #expect(health.statusLine == "HTTP/1.1 200 OK")        })    }     @Test    func `dashboard error responses carry no-store`() async throws {        try await Self.withServeRuntime(token: "secret", rawConfigJSON: "{not json", body: { port in            let response = try await Self.rawExchange(                port: port,                request: "GET /dashboard/v1/snapshot HTTP/1.1\r\nHost: 127.0.0.1\r\n"                    + "Authorization: Bearer secret\r\n\r\n")             #expect(response.statusLine == "HTTP/1.1 500 Internal Server Error")            #expect(response.headerValue("Cache-Control") == "no-store")            #expect(response.headerValues("Cache-Control").count == 1)        })    }     @Test    func `health stays open when a dashboard token is configured`() async throws {        try await Self.withServeRuntime(token: "secret", body: { port in            let response = try await Self.rawExchange(                port: port,                request: "GET /health HTTP/1.1\r\nHost: 127.0.0.1\r\n\r\n")             #expect(response.statusLine == "HTTP/1.1 200 OK")            let object = try #require(                JSONSerialization.jsonObject(with: Data(response.body.utf8)) as? [String: Any])            #expect(object["status"] as? String == "ok")        })    }     @Test    func `snapshot response preserves usage cache metadata`() async throws {        let store = testConfigStore(suiteName: "CLIServeRawHTTPTests-\(UUID().uuidString)")        defer { try? store.deleteIfPresent() }        try store.save(CodexBarConfig(providers: UsageProvider.allCases.map {            ProviderConfig(id: $0, enabled: false)        }))        let runtime = ServeRuntime(            configStore: store,            cache: CLIServeResponseCache(),            providerOperations: CLIServeOperationCoordinator(),            costOperations: CLIServeOperationCoordinator(),            refreshInterval: 60,            requestTimeout: 5,            healthVersion: "0.0.0-test",            dashboardAuth: CLIServeDashboardAuth(bearer: "secret"),            bindHost: "127.0.0.1")        let request = CLILocalHTTPRequest(            method: "GET",            target: "/dashboard/v1/snapshot",            host: "127.0.0.1",            path: "/dashboard/v1/snapshot",            queryItems: [:],            authorization: "Bearer secret")         let response = await CodexBarCLI.handleServeRequest(request, runtime: runtime)         #expect(response.status == .ok)        #expect(response.usageCacheKeys != nil)        #expect(response.usageCacheKeys?.isEmpty == true)    }     // MARK: - Harness     /// Boots the production serve handler with an isolated config store whose providers    /// are all disabled, so snapshot fetches stay local while the full route/auth/cache    /// path is exercised end to end.    ///    /// `bindHost` configures the runtime exactly as `runServe` would for that bind    /// host (a non-loopback value gates every data route); the test listener itself    /// always binds loopback. `rawConfigJSON` replaces the stored config with raw    /// bytes to provoke config-load failures.    static func withServeRuntime(        token: String?,        bindHost: String = "127.0.0.1",        rawConfigJSON: String? = nil,        body: (UInt16) async throws -> Void) async throws    {        let store = testConfigStore(suiteName: "CLIServeRawHTTPTests-\(UUID().uuidString)")        defer { try? store.deleteIfPresent() }        try store.save(CodexBarConfig(providers: UsageProvider.allCases.map {            ProviderConfig(id: $0, enabled: false)        }))        if let rawConfigJSON {            try Data(rawConfigJSON.utf8).write(to: store.fileURL)        }         let runtime = ServeRuntime(            configStore: store,            cache: CLIServeResponseCache(),            providerOperations: CLIServeOperationCoordinator(),            costOperations: CLIServeOperationCoordinator(),            refreshInterval: 60,            requestTimeout: 5,            healthVersion: "0.0.0-test",            dashboardAuth: CLIServeDashboardAuth(bearer: token),            bindHost: bindHost)        try await Self.withServer(            handler: { request in                await CodexBarCLI.handleServeRequest(request, runtime: runtime)            },            body: body)    }     static func okResponse() -> CLILocalHTTPResponse {        CLILocalHTTPResponse(status: .ok, body: Data(#"{"status":"ok"}"#.utf8))    }     /// Runs `body` against a live server bound to an ephemeral loopback port.    static func withServer(        allowedHosts: CLILocalHTTPAllowedHosts = .loopbackOnly,        handler: @escaping CLILocalHTTPServer.Handler,        body: (UInt16) async throws -> Void) async throws    {        let listening = RawHTTPListeningSignal()        let server = CLILocalHTTPServer(            host: "127.0.0.1",            port: 0,            allowedHosts: allowedHosts,            handler: handler)        let task = Task {            try await server.run {                listening.signal()            }        }         await listening.wait()        do {            let port = try #require(server.listeningPort)            try await body(port)        } catch {            server.stop()            _ = try? await task.value            throw error        }        server.stop()        try await task.value    }     struct RawHTTPResponse {        let statusLine: String        let headers: [(String, String)]        let body: String         func headerValue(_ name: String) -> String? {            self.headers.first { $0.0.lowercased() == name.lowercased() }?.1        }         func headerValues(_ name: String) -> [String] {            self.headers.filter { $0.0.lowercased() == name.lowercased() }.map(\.1)        }    }     enum RawHTTPExchangeError: Error {        case connectFailed        case sendFailed        case malformedResponse    }     /// Writes `request` bytes over a fresh TCP connection and reads the raw response to EOF.    /// Runs on a Dispatch thread so the blocking socket calls cannot starve the cooperative    /// pool the server's accept loop and handler tasks run on.    static func rawExchange(port: UInt16, request: String) async throws -> RawHTTPResponse {        try await withCheckedThrowingContinuation { continuation in            DispatchQueue.global().async {                continuation.resume(with: Result {                    try Self.performRawExchange(port: port, request: request)                })            }        }    }     private static func performRawExchange(port: UInt16, request: String) throws -> RawHTTPResponse {        #if canImport(Darwin)        let streamType = SOCK_STREAM        #else        let streamType = Int32(SOCK_STREAM.rawValue)        #endif        let fd = socket(AF_INET, streamType, 0)        guard fd >= 0 else { throw RawHTTPExchangeError.connectFailed }        defer { close(fd) }         var timeout = timeval(tv_sec: 5, tv_usec: 0)        setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, socklen_t(MemoryLayout<timeval>.size))        setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeout, socklen_t(MemoryLayout<timeval>.size))         var address = sockaddr_in()        #if canImport(Darwin)        address.sin_len = UInt8(MemoryLayout<sockaddr_in>.size)        #endif        address.sin_family = sa_family_t(AF_INET)        address.sin_port = port.bigEndian        guard inet_pton(AF_INET, "127.0.0.1", &address.sin_addr) == 1 else {            throw RawHTTPExchangeError.connectFailed        }        let connected = withUnsafePointer(to: &address) { pointer in            pointer.withMemoryRebound(to: sockaddr.self, capacity: 1) { socketAddress in                connect(fd, socketAddress, socklen_t(MemoryLayout<sockaddr_in>.size))            }        }        guard connected == 0 else { throw RawHTTPExchangeError.connectFailed }         let requestData = Data(request.utf8)        let sent = requestData.withUnsafeBytes { rawBuffer -> Int in            guard let base = rawBuffer.baseAddress else { return -1 }            var total = 0            while total < requestData.count {                let count = send(fd, base.advanced(by: total), requestData.count - total, 0)                guard count > 0 else { return -1 }

Lines 1–500 of 568.