1329 lines · 46.0 KB · Swift
RawDownload
import Commanderimport Foundationimport Testing#if canImport(Darwin)import Darwin#elseimport Glibc#endif@testable import CodexBarCLI@testable import CodexBarCore // Cache state-machine coverage is intentionally kept together for sequence readability.// swiftlint:disable:next type_body_lengthstruct CLIServeRouterTests {    @Test    func `termination monitor handles interactive and hangup signals`() {        #expect(CLITerminationSignalMonitor.signalNumbers == [SIGINT, SIGTERM, SIGHUP])    }     @Test    func `local http parser accepts only loopback host headers`() throws {        let allowedHosts = [            "localhost",            "localhost.",            "localhost:8080",            "127.0.0.1",            "127.0.0.1:8080",            "[::1]",            "[::1]:8080",        ]         for host in allowedHosts {            let request = try Self.parsedRequest(host: host)            #expect(request.host == host)            #expect(request.path == "/usage")        }    }     @Test    func `local http parser rejects hostile missing and duplicate hosts`() {        Self.expectParseFailure(raw: "GET /usage HTTP/1.1\r\n\r\n", .missingHost)        Self.expectParseFailure(raw: "GET /usage HTTP/1.1\r\nHost: evil.test\r\n\r\n", .disallowedHost)        Self.expectParseFailure(raw: "GET /usage HTTP/1.1\r\nHost: localhost, evil.test\r\n\r\n", .disallowedHost)        Self.expectParseFailure(raw: "GET /usage HTTP/1.1\r\nHost: localhost:abc\r\n\r\n", .disallowedHost)        Self.expectParseFailure(            raw: "GET /usage HTTP/1.1\r\nHost: localhost\r\nHost: 127.0.0.1\r\n\r\n",            .duplicateHost)    }     @Test    func `routes health usage and cost endpoints`() throws {        #expect(try CLIServeRouter.route(method: "GET", path: "/health", queryItems: [:]) == .health)        #expect(try CLIServeRouter.route(method: "GET", path: "/usage", queryItems: [:]) == .usage(provider: nil))        #expect(            try CLIServeRouter.route(                method: "GET",                path: "/usage",                queryItems: ["provider": "claude"]) == .usage(provider: "claude"))        #expect(            try CLIServeRouter.route(                method: "GET",                path: "/cost",                queryItems: ["provider": "codex"]) == .cost(provider: "codex"))    }     @Test    func `rejects non get methods`() {        do {            _ = try CLIServeRouter.route(method: "POST", path: "/usage", queryItems: [:])            Issue.record("Expected methodNotAllowed")        } catch let error as CLIServeRouteError {            #expect(error == .methodNotAllowed)        } catch {            Issue.record("Unexpected error: \(error)")        }    }     @Test    func `rejects unknown paths`() {        do {            _ = try CLIServeRouter.route(method: "GET", path: "/missing", queryItems: [:])            Issue.record("Expected notFound")        } catch let error as CLIServeRouteError {            #expect(error == .notFound)        } catch {            Issue.record("Unexpected error: \(error)")        }    }     @Test    func `serve numeric options reject malformed values`() {        #expect(CodexBarCLI.decodeServePort(from: ParsedValues(            positional: [],            options: ["port": ["abc"]],            flags: [])) == nil)        #expect(CodexBarCLI.decodeServePort(from: ParsedValues(            positional: [],            options: ["port": ["0"]],            flags: [])) == nil)        #expect(CodexBarCLI.decodeServePort(from: ParsedValues(            positional: [],            options: ["port": ["65536"]],            flags: [])) == nil)        #expect(CodexBarCLI.decodeServePort(from: ParsedValues(            positional: [],            options: [:],            flags: [])) == 8080)         #expect(CodexBarCLI.decodeServeRefreshInterval(from: ParsedValues(            positional: [],            options: ["refreshInterval": ["later"]],            flags: [])) == nil)        #expect(CodexBarCLI.decodeServeRefreshInterval(from: ParsedValues(            positional: [],            options: ["refreshInterval": ["-1"]],            flags: [])) == nil)        #expect(CodexBarCLI.decodeServeRefreshInterval(from: ParsedValues(            positional: [],            options: ["refreshInterval": ["inf"]],            flags: [])) == nil)        #expect(CodexBarCLI.decodeServeRefreshInterval(from: ParsedValues(            positional: [],            options: ["refreshInterval": ["86401"]],            flags: [])) == 86401)        #expect(CodexBarCLI.decodeServeRefreshInterval(from: ParsedValues(            positional: [],            options: ["refreshInterval": ["86400"]],            flags: [])) == 86400)        #expect(CodexBarCLI.decodeServeRefreshInterval(from: ParsedValues(            positional: [],            options: [:],            flags: [])) == 60)         #expect(CodexBarCLI.decodeServeRequestTimeout(from: ParsedValues(            positional: [],            options: ["requestTimeout": ["soon"]],            flags: [])) == nil)        #expect(CodexBarCLI.decodeServeRequestTimeout(from: ParsedValues(            positional: [],            options: ["requestTimeout": ["-0.5"]],            flags: [])) == nil)        #expect(CodexBarCLI.decodeServeRequestTimeout(from: ParsedValues(            positional: [],            options: ["requestTimeout": ["0"]],            flags: [])) == 0)        #expect(CodexBarCLI.decodeServeRequestTimeout(from: ParsedValues(            positional: [],            options: ["requestTimeout": ["12.5"]],            flags: [])) == 12.5)        #expect(CodexBarCLI.decodeServeRequestTimeout(from: ParsedValues(            positional: [],            options: [:],            flags: [])) == 30)    }     @Test    func `serve help documents request timeout option`() {        let serve = CodexBarCLI.serveHelp(version: "0.0.0")        let root = CodexBarCLI.rootHelp(version: "0.0.0")         #expect(serve.contains("--request-timeout <seconds>"))        #expect(serve.contains("codexbar serve --port 8080 --refresh-interval 60 --request-timeout 30"))        #expect(root.contains("--request-timeout <seconds>"))    }     @Test    func `serve config snapshot reflects provider changes`() throws {        let store = testConfigStore(suiteName: "CLIServeRouterTests-serve-config-freshness-\(UUID().uuidString)")        defer { try? store.deleteIfPresent() }        var firstConfig = CodexBarConfig.makeDefault()        firstConfig.setProviderConfig(ProviderConfig(id: .opencodego, enabled: false))        try store.save(firstConfig)         let firstSnapshot = try CodexBarCLI.loadServeConfigSnapshot(configStore: store)         var secondConfig = firstConfig        secondConfig.setProviderConfig(ProviderConfig(id: .opencodego, enabled: true))        try store.save(secondConfig)        let secondSnapshot = try CodexBarCLI.loadServeConfigSnapshot(configStore: store)         #expect(!firstSnapshot.config.enabledProviders().contains(.opencodego))        #expect(secondSnapshot.config.enabledProviders().contains(.opencodego))        #expect(firstSnapshot.cacheToken != secondSnapshot.cacheToken)        #expect(            CodexBarCLI.serveCacheKey(kind: "usage", provider: nil, configToken: firstSnapshot.cacheToken) !=                CodexBarCLI.serveCacheKey(kind: "usage", provider: nil, configToken: secondSnapshot.cacheToken))    }     @Test    func `serve cache skips provider error payloads`() {        let success = CLILocalHTTPResponse(            status: .ok,            body: Data(#"[{"provider":"codex","source":"local"}]"#.utf8))        let providerError = CLILocalHTTPResponse(            status: .ok,            body: Data(#"[{"provider":"codex","source":"local","error":{"message":"temporary"}}]"#.utf8))        let routeError = CLILocalHTTPResponse(            status: .badRequest,            body: Data(#"{"error":"bad request"}"#.utf8))         #expect(CodexBarCLI.shouldCacheServeResponse(success))        #expect(!CodexBarCLI.shouldCacheServeResponse(providerError))        #expect(!CodexBarCLI.shouldCacheServeResponse(routeError))    }     @Test    func `serve cache uses stable Codex account identities`() {        let storedID = UUID()        let firstProjection = Self.codexVisibleAccount(            id: "email-shaped-id",            workspaceAccountID: "workspace-1",            authFingerprint: "auth-1",            storedAccountID: storedID)        let reshapedProjection = Self.codexVisibleAccount(            id: "managed:\(storedID.uuidString)",            workspaceAccountID: "workspace-1",            authFingerprint: "auth-1",            storedAccountID: storedID)        let replacement = Self.codexVisibleAccount(            id: "email-shaped-id",            workspaceAccountID: "workspace-2",            authFingerprint: "auth-2",            storedAccountID: UUID())        let workspacePeer = Self.codexVisibleAccount(            id: "workspace-peer",            email: "other@example.com",            workspaceAccountID: "workspace-1",            authFingerprint: "auth-3",            storedAccountID: UUID())        let ambiguous = Self.codexVisibleAccount(            id: "email-only",            workspaceAccountID: nil,            authFingerprint: nil,            storedAccountID: nil)        let storedBeforeRefresh = Self.codexVisibleAccount(            id: "stored-before",            workspaceAccountID: nil,            authFingerprint: "old-auth",            storedAccountID: storedID)        let storedAfterRefresh = Self.codexVisibleAccount(            id: "stored-after",            workspaceAccountID: nil,            authFingerprint: "new-auth",            storedAccountID: storedID)         let firstKey = CodexBarCLI.usageCacheAccountKey(            provider: .codex,            account: nil,            codexVisibleAccount: firstProjection)        let reshapedKey = CodexBarCLI.usageCacheAccountKey(            provider: .codex,            account: nil,            codexVisibleAccount: reshapedProjection)        let replacementKey = CodexBarCLI.usageCacheAccountKey(            provider: .codex,            account: nil,            codexVisibleAccount: replacement)        let workspacePeerKey = CodexBarCLI.usageCacheAccountKey(            provider: .codex,            account: nil,            codexVisibleAccount: workspacePeer)        let storedBeforeKey = CodexBarCLI.usageCacheAccountKey(            provider: .codex,            account: nil,            codexVisibleAccount: storedBeforeRefresh)        let storedAfterKey = CodexBarCLI.usageCacheAccountKey(            provider: .codex,            account: nil,            codexVisibleAccount: storedAfterRefresh)         #expect(firstKey == reshapedKey)        #expect(firstKey != replacementKey)        #expect(firstKey != workspacePeerKey)        #expect(storedBeforeKey == storedAfterKey)        #expect(CodexBarCLI.usageCacheAccountKey(            provider: .codex,            account: nil,            codexVisibleAccount: ambiguous) == nil)        #expect(CodexBarCLI.usageCacheAccountKey(            provider: .antigravity,            account: nil,            codexVisibleAccount: nil) == nil)    }     @Test    func `serve cache coalesces concurrent cache misses`() async {        let cache = CLIServeResponseCache()        let counter = ServeTestCounter()         let responses = await withTaskGroup(of: CLILocalHTTPResponse.self) { group -> [CLILocalHTTPResponse] in            for _ in 0..<5 {                group.addTask {                    await CodexBarCLI.cachedServeResponse(                        key: "usage:",                        cache: cache,                        refreshInterval: 60,                        requestTimeout: 1)                    {                        let call = await counter.increment()                        try? await Task.sleep(nanoseconds: 50_000_000)                        return Self.response("[{\"provider\":\"codex\",\"call\":\(call)}]")                    }                }            }             var responses: [CLILocalHTTPResponse] = []            for await response in group {                responses.append(response)            }            return responses        }         #expect(await counter.current() == 1)        #expect(Set(responses.map(Self.bodyString)).count == 1)        #expect(responses.allSatisfy { $0.status == .ok })        #expect(responses.allSatisfy { Self.bodyString($0).contains("\"call\":1") })    }     @Test    func `serve cache prunes expired config token entries`() async throws {        let cache = CLIServeResponseCache()         _ = await CodexBarCLI.cachedServeResponse(            key: "usage::old-config",            cache: cache,            refreshInterval: 0.001)        {            Self.response(#"[{"provider":"codex","config":"old"}]"#)        }        #expect(await cache.cachedEntryCount() == 1)         try await Task.sleep(nanoseconds: 20_000_000)        _ = await CodexBarCLI.cachedServeResponse(            key: "usage::new-config",            cache: cache,            refreshInterval: 60)        {            Self.response(#"[{"provider":"codex","config":"new"}]"#)        }         #expect(await cache.cachedEntryCount() == 1)    }     @Test    func `serve cache does not cache timeouts and recovers on next success`() async {        let cache = CLIServeResponseCache()        let counter = ServeTestCounter()         let timeout = await CodexBarCLI.cachedServeResponse(            key: "usage:",            cache: cache,            refreshInterval: 60,            requestTimeout: 0.01)        {            _ = await counter.increment()            try? await Task.sleep(nanoseconds: 200_000_000)            return Self.response("[{\"provider\":\"codex\",\"call\":1}]")        }         #expect(timeout.status == .gatewayTimeout)        #expect(Self.bodyString(timeout).contains("request timed out"))         let success = await CodexBarCLI.cachedServeResponse(            key: "usage:",            cache: cache,            refreshInterval: 60,            requestTimeout: 1)        {            let call = await counter.increment()            return Self.response("[{\"provider\":\"codex\",\"call\":\(call)}]")        }         #expect(success.status == .ok)        #expect(Self.bodyString(success).contains("\"call\":2"))         let cached = await CodexBarCLI.cachedServeResponse(            key: "usage:",            cache: cache,            refreshInterval: 60,            requestTimeout: 1)        {            let call = await counter.increment()            return Self.response("[{\"provider\":\"codex\",\"call\":\(call)}]")        }         #expect(cached.status == .ok)        #expect(Self.bodyString(cached) == Self.bodyString(success))        #expect(await counter.current() == 2)    }     @Test    func `serve cache resumes coalesced waiters on timeout`() async {        let cache = CLIServeResponseCache()        let counter = ServeTestCounter()         let responses = await withTaskGroup(of: CLILocalHTTPResponse.self) { group -> [CLILocalHTTPResponse] in            for _ in 0..<4 {                group.addTask {                    await CodexBarCLI.cachedServeResponse(                        key: "usage:",                        cache: cache,                        refreshInterval: 60,                        requestTimeout: 0.01)                    {                        _ = await counter.increment()                        try? await Task.sleep(nanoseconds: 200_000_000)                        return Self.response("[{\"provider\":\"codex\"}]")                    }                }            }             var responses: [CLILocalHTTPResponse] = []            for await response in group {                responses.append(response)            }            return responses        }         #expect(await counter.current() == 1)        #expect(responses.count == 4)        #expect(responses.allSatisfy { $0.status == .gatewayTimeout })        #expect(responses.allSatisfy { Self.bodyString($0).contains("request timed out") })    }     @Test    func `serve cache serves last good payload when refresh fails`() async {        let cache = CLIServeResponseCache()        let counter = ServeTestCounter()         let first = await CodexBarCLI.cachedServeResponse(            key: "usage:antigravity",            cache: cache,            refreshInterval: 0.05,            requestTimeout: 1)        {            let call = await counter.increment()            return Self.response("[{\"provider\":\"antigravity\",\"call\":\(call)}]")        }        #expect(first.status == .ok)         // Let the fresh cache entry expire so the next request re-fetches.        try? await Task.sleep(nanoseconds: 100_000_000)         let failed = await CodexBarCLI.cachedServeResponse(            key: "usage:antigravity",            cache: cache,            refreshInterval: 0.05,            requestTimeout: 1)        {            _ = await counter.increment()            return Self.response(                "[{\"provider\":\"antigravity\",\"error\":{\"message\":\"transient\"}}]")        }         // Transient failure is masked by the last good payload.        #expect(failed.status == .ok)        let failedRows = try? Self.jsonRows(failed)        #expect(failedRows?.first?["call"] as? Int == 1)        #expect(await counter.current() == 2)         try? await Task.sleep(nanoseconds: 100_000_000)         let recovered = await CodexBarCLI.cachedServeResponse(            key: "usage:antigravity",            cache: cache,            refreshInterval: 0.05,            requestTimeout: 1)        {            let call = await counter.increment()            return Self.response("[{\"provider\":\"antigravity\",\"call\":\(call)}]")        }         #expect(recovered.status == .ok)        #expect(Self.bodyString(recovered).contains("\"call\":3"))    }     @Test    func `serve cache replaces only failed provider account rows`() async throws {        let cache = CLIServeResponseCache()         _ = await CodexBarCLI.cachedServeResponse(            key: "usage:",            cache: cache,            refreshInterval: 0.05,            requestTimeout: 1)        {            Self.response("""            [              {"provider":"codex","account":"personal","call":1},              {"provider":"antigravity","account":"work","call":1},              {"provider":"antigravity","account":"personal","call":1}            ]            """)        }         try? await Task.sleep(nanoseconds: 100_000_000)         let refreshed = await CodexBarCLI.cachedServeResponse(            key: "usage:",            cache: cache,

Lines 1–500 of 1329.