447 lines · 19.3 KB · Swift
RawDownload
import CodexBarCoreimport Foundation#if canImport(FoundationNetworking)import FoundationNetworking#endifimport Testing@testable import CodexBarCLI /// Fixtures below were captured verbatim from a locally running Wayfinder gateway/// (`wayfinder-router serve`, two-tier priced config) after routing real traffic.struct WayfinderProviderLinuxTests {    @Test    func `assembles a snapshot from live gateway payloads`() throws {        let snapshot = try Self.makeSnapshot()         #expect(snapshot.gatewayStatus == "ok")        #expect(!snapshot.offline)        #expect(!snapshot.dryRun)        #expect(snapshot.missingKeys.isEmpty)        #expect(snapshot.modelCount == 2)        #expect(snapshot.requests == 14)        #expect(snapshot.tokens == 1028)        #expect(snapshot.priced)        #expect(snapshot.saved == 0.005694)        #expect(snapshot.savedPct == 61.5)        #expect(snapshot.routes.map(\.name) == ["local", "cloud"])        #expect(snapshot.routes.first { $0.name == "local" }?.requests == 10)        #expect(snapshot.routes.first { $0.name == "cloud" }?.requests == 4)        #expect(snapshot.statusLabel == "Local gateway")        #expect(snapshot.gatewaySummary == "ok · 2 models")        #expect(snapshot.displayLines == [            "Gateway: ok · 2 models",            "Routed: local: 10 · cloud: 4",            "Saved: <$0.01 · 61.5% vs highest-cost route",            "Avg decision: 0.1 ms",        ])         let avgMs = try #require(snapshot.avgDecisionMs)        #expect(abs(avgMs - 0.0804) < 0.001)    }     @Test    func `maps the snapshot onto the shared usage snapshot`() throws {        let usage = try Self.makeSnapshot().toUsageSnapshot()         #expect(usage.primary == nil)        #expect(usage.secondary == nil)        #expect(usage.providerCost == nil)        #expect(usage.identity?.providerID == .wayfinder)        #expect(usage.identity?.accountEmail == nil)        #expect(usage.identity?.accountOrganization == "2 models · local gateway")        #expect(usage.identity?.loginMethod == "Local gateway")        #expect(usage.dataConfidence == .exact)    }     @Test    func `degraded health reports the missing key count`() throws {        let snapshot = try Self.makeSnapshot(healthData: Self.healthDegraded)        #expect(snapshot.gatewayStatus == "degraded")        #expect(snapshot.missingKeys == ["cloud"])        #expect(snapshot.statusLabel == "Degraded — 1 key missing")    }     @Test    func `empty savings suppress the routed and saved summaries`() throws {        let snapshot = try Self.makeSnapshot(savingsData: Self.savingsZeros)        #expect(snapshot.requests == 0)        #expect(snapshot.routedSummary == nil)        #expect(snapshot.savedSummary == nil)        #expect(snapshot.toUsageSnapshot().providerCost == nil)    }     @Test    func `unpriced savings never render dollars`() throws {        let snapshot = try Self.makeSnapshot(savingsData: Self.savingsUnpriced)        #expect(!snapshot.priced)        #expect(snapshot.savedSummary == "40% vs highest-cost route")        #expect(snapshot.toUsageSnapshot().providerCost == nil)    }     @Test    func `sub-cent priced savings render below one cent`() throws {        let snapshot = try Self.makeSnapshot()        #expect(snapshot.routedSummary == "local: 10 · cloud: 4")        #expect(snapshot.savedSummary == "<$0.01 · 61.5% vs highest-cost route")        #expect(snapshot.avgDecisionSummary == "0.1 ms")    }     @Test    func `metrics parsing is best effort`() throws {        #expect(WayfinderUsageFetcher._averageDecisionMillisecondsForTesting("") == nil)        #expect(WayfinderUsageFetcher._averageDecisionMillisecondsForTesting("garbage\nlines\n") == nil)        #expect(WayfinderUsageFetcher._averageDecisionMillisecondsForTesting(            "wayfinder_router_decision_latency_seconds_sum 1.5\n") == nil)        #expect(WayfinderUsageFetcher._averageDecisionMillisecondsForTesting(            "wayfinder_router_decision_latency_seconds_sum 1.5\n" +                "wayfinder_router_decision_latency_seconds_count 0\n") == nil)         let labeled = WayfinderUsageFetcher._averageDecisionMillisecondsForTesting(            "wayfinder_router_decision_latency_seconds_sum{route=\"all\"} 2.0\n" +                "wayfinder_router_decision_latency_seconds_count{route=\"all\"} 4\n")        #expect(labeled == 500)         let snapshot = try Self.makeSnapshot(metricsText: nil)        #expect(snapshot.avgDecisionMs == nil)        #expect(snapshot.avgDecisionSummary == nil)    }     @Test    func `endpoint URLs preserve prefixes and trailing slashes`() throws {        func endpoint(_ base: String, _ path: String) throws -> String {            try WayfinderUsageFetcher._endpointURLForTesting(                baseURL: #require(URL(string: base)),                path: path).absoluteString        }        #expect(try endpoint("http://127.0.0.1:8088", "healthz") == "http://127.0.0.1:8088/healthz")        #expect(try endpoint("http://127.0.0.1:8088/", "healthz") == "http://127.0.0.1:8088/healthz")        #expect(try endpoint("https://wayfinder.example.com/wf", "v1/savings") ==            "https://wayfinder.example.com/wf/v1/savings")    }     @Test    func `gateway URL override allows loopback HTTP and rejects remote HTTP`() throws {        let key = WayfinderSettingsReader.baseURLEnvironmentKey         try WayfinderSettingsReader.validateEndpointOverride(environment: [key: "http://127.0.0.1:9090"])        try WayfinderSettingsReader.validateEndpointOverride(environment: [key: "http://localhost:8088"])        try WayfinderSettingsReader.validateEndpointOverride(environment: [key: "https://wayfinder.example.com"])        #expect(WayfinderSettingsReader.baseURL(environment: [key: "http://127.0.0.1:9090"]).absoluteString ==            "http://127.0.0.1:9090")         #expect(throws: WayfinderSettingsError.invalidEndpointOverride(key)) {            try WayfinderSettingsReader.validateEndpointOverride(environment: [key: "http://192.168.1.5:8088"])        }        #expect(throws: WayfinderSettingsError.invalidEndpointOverride(key)) {            try WayfinderSettingsReader.validateEndpointOverride(environment: [key: "http://user@127.0.0.1:8088"])        }        #expect(WayfinderSettingsReader.baseURL(environment: [key: "http://attacker.test"]) ==            WayfinderSettingsReader.defaultBaseURL)        #expect(WayfinderSettingsReader.baseURL(environment: [:]) == WayfinderSettingsReader.defaultBaseURL)    }     @Test    func `dashboard URL follows the configured gateway and preserves its prefix`() {        let key = WayfinderSettingsReader.baseURLEnvironmentKey         #expect(WayfinderSettingsReader.dashboardURL(environment: [:]).absoluteString ==            "http://127.0.0.1:8088/router")        #expect(WayfinderSettingsReader.dashboardURL(            environment: [key: "http://localhost:9191/wayfinder/"]).absoluteString ==            "http://localhost:9191/wayfinder/router")    }     @Test    func `config projects the gateway URL into the fetch environment`() {        let config = ProviderConfig(id: .wayfinder, enterpriseHost: "http://localhost:9099")        let environment = ProviderConfigEnvironment.applyProviderConfigOverrides(            base: [:],            provider: .wayfinder,            config: config)         #expect(environment[WayfinderSettingsReader.baseURLEnvironmentKey] == "http://localhost:9099")        #expect(WayfinderSettingsReader.baseURL(environment: environment).absoluteString == "http://localhost:9099")    }     @Test    func `descriptor is registered`() {        let descriptor = ProviderDescriptorRegistry.descriptor(for: .wayfinder)        #expect(descriptor.metadata.displayName == "Wayfinder")        #expect(descriptor.metadata.cliName == "wayfinder")        #expect(descriptor.cli.aliases.contains("wayfinder-router"))        #expect(!descriptor.metadata.defaultEnabled)    }     @Test    func `usage snapshot preserves Wayfinder detail when cached`() throws {        let snapshot = try Self.makeSnapshot()        let encoded = try JSONEncoder().encode(snapshot.toUsageSnapshot())        let decoded = try JSONDecoder().decode(UsageSnapshot.self, from: encoded)         #expect(decoded.wayfinderUsage == snapshot)        #expect(decoded.identity?.providerID == .wayfinder)    }     @Test    func `fetch polls only the documented read-only endpoints`() async throws {        let log = RequestLog()        let transport = ProviderHTTPTransportHandler { request in            let url = try #require(request.url)            log.append(url)            #expect(request.httpMethod == "GET")            let body: Data = switch url.path {            case "/healthz": Self.healthOK            case "/router/models": Self.models            case "/v1/savings": Self.savings30d            case "/metrics": Data(Self.metricsText.utf8)            default: Data()            }            let response = try #require(HTTPURLResponse(                url: url,                statusCode: 200,                httpVersion: nil,                headerFields: nil))            return (body, response)        }         let snapshot = try await WayfinderUsageFetcher.fetchUsage(            baseURL: #require(URL(string: "http://127.0.0.1:8088")),            transport: transport)         #expect(snapshot.requests == 14)        #expect(log.paths() == ["/healthz", "/router/models", "/v1/savings", "/metrics"])        #expect(log.queries().contains("period=30d"))    }     @Test    func `fetch maps HTTP failures to actionable errors`() async throws {        let failing = ProviderHTTPTransportHandler { _ in            throw URLError(.cannotConnectToHost)        }        await #expect(throws: WayfinderUsageError.gatewayUnreachable) {            _ = try await WayfinderUsageFetcher.fetchUsage(                baseURL: #require(URL(string: "http://127.0.0.1:8088")),                transport: failing)        }         let serverError = ProviderHTTPTransportHandler { request in            let url = try #require(request.url)            let response = try #require(HTTPURLResponse(                url: url,                statusCode: 500,                httpVersion: nil,                headerFields: nil))            return (Data(), response)        }        await #expect(throws: WayfinderUsageError.apiError(500)) {            _ = try await WayfinderUsageFetcher.fetchUsage(                baseURL: #require(URL(string: "http://127.0.0.1:8088")),                transport: serverError)        }    }     @Test    func `required request cancellation remains cancellation`() async throws {        for error in [CancellationError() as any Error, URLError(.cancelled) as any Error] {            let cancelling = ProviderHTTPTransportHandler { _ in throw error }            await #expect(throws: CancellationError.self) {                _ = try await WayfinderUsageFetcher.fetchUsage(                    baseURL: #require(URL(string: "http://127.0.0.1:8088")),                    transport: cancelling)            }        }    }     @Test    func `optional metrics cancellation remains cancellation`() async throws {        let cancelling = ProviderHTTPTransportHandler { request in            let url = try #require(request.url)            if url.path == "/metrics" {                throw CancellationError()            }            let body: Data = switch url.path {            case "/healthz": Self.healthOK            case "/router/models": Self.models            case "/v1/savings": Self.savings30d            default: Data()            }            let response = try #require(HTTPURLResponse(                url: url,                statusCode: 200,                httpVersion: nil,                headerFields: nil))            return (body, response)        }         await #expect(throws: CancellationError.self) {            _ = try await WayfinderUsageFetcher.fetchUsage(                baseURL: #require(URL(string: "http://127.0.0.1:8088")),                transport: cancelling)        }    }     @Test    func `fetch rejects responses from a different origin`() async throws {        let redirecting = ProviderHTTPTransportHandler { _ in            let elsewhere = try #require(URL(string: "http://attacker.test/healthz"))            let response = try #require(HTTPURLResponse(                url: elsewhere,                statusCode: 200,                httpVersion: nil,                headerFields: nil))            return (Self.healthOK, response)        }        await #expect(throws: WayfinderUsageError.unexpectedRedirect) {            _ = try await WayfinderUsageFetcher.fetchUsage(                baseURL: #require(URL(string: "http://127.0.0.1:8088")),                transport: redirecting)        }    }     @Test    func `text CLI renders gateway health routed split savings and latency`() throws {        let output = try CLIRenderer.renderText(            provider: .wayfinder,            snapshot: Self.makeSnapshot().toUsageSnapshot(),            credits: nil,            context: RenderContext(                header: "Wayfinder (api)",                status: nil,                useColor: false,                resetStyle: .countdown))         #expect(output.contains("Gateway: ok · 2 models"))        #expect(output.contains("Routed: local: 10 · cloud: 4"))        #expect(output.contains("Saved: <$0.01 · 61.5% vs highest-cost route"))        #expect(output.contains("Avg decision: 0.1 ms"))        #expect(!output.contains("Cost:"))    }     @Test    func `routed summary reflects request counts regardless of configured model order`() throws {        // The heavier-traffic route ("primary-tier") is configured SECOND in /router/models,        // and the lighter one ("secondary-tier") FIRST — proving nothing in the summary is        // derived from array position (the gateway's config order is not a semantic signal).        let reorderedModels = Data("""        {"models":[{"name":"secondary-tier","endpoint":"http://127.0.0.1:9102/v1",\        "model":"stand-in-large","api_key_env":"RIG_CLOUD_KEY","key_ok":true},\        {"name":"primary-tier","endpoint":"http://127.0.0.1:9101/v1","model":"stand-in-small",\        "api_key_env":null,"key_ok":true}],"dry_run":false}        """.utf8)        let snapshot = try Self.makeSnapshot(modelsData: reorderedModels)         #expect(snapshot.routedSummary == "local: 10 · cloud: 4")        #expect(snapshot.routes.first?.name == "local")    }     @Test    func `routed summary uses the gateway's own route names, not a hardcoded local or cloud label`() throws {        // Route names are whatever the user named their endpoints in the Wayfinder config —        // there is no "local"/"cloud" semantic anywhere in the gateway's JSON.        let customNamedSavings = Data("""        {"period_days":30,"unit":"usd","priced":true,"requests":14,"estimated_requests":0,\        "tokens":1028,"realized":0.003558,"baseline":0.009252,"saved":0.005694,"saved_pct":61.5,\        "by_route":{"groq-8b":{"requests":10,"realized":0.000264,"baseline":0.005958,\        "saved":0.005694,"tokens":662},"openai-o1":{"requests":4,"realized":0.003294,\        "baseline":0.003294,"saved":0.0,"tokens":366}},"by_key":{},\        "price_table_version":"a3db80fd9a78"}        """.utf8)        let snapshot = try Self.makeSnapshot(savingsData: customNamedSavings)        let summary = try #require(snapshot.routedSummary)         #expect(summary == "groq-8b: 10 · openai-o1: 4")        #expect(!summary.contains("local"))        #expect(!summary.contains("cloud"))    }     // MARK: - Helpers     private static func makeSnapshot(        healthData: Data = Self.healthOK,        modelsData: Data = Self.models,        savingsData: Data = Self.savings30d,        metricsText: String? = Self.metricsText) throws -> WayfinderUsageSnapshot    {        try WayfinderUsageFetcher._makeSnapshotForTesting(            healthData: healthData,            modelsData: modelsData,            savingsData: savingsData,            metricsText: metricsText,            updatedAt: Date(timeIntervalSince1970: 1))    }     private final class RequestLog: @unchecked Sendable {        private let lock = NSLock()        private var urls: [URL] = []         func append(_ url: URL) {            self.lock.lock()            defer { self.lock.unlock() }            self.urls.append(url)        }         func paths() -> [String] {            self.lock.lock()            defer { self.lock.unlock() }            return self.urls.map(\.path)        }         func queries() -> [String] {            self.lock.lock()            defer { self.lock.unlock() }            return self.urls.compactMap(\.query)        }    }     // MARK: - Fixtures (captured from a live gateway)     private static let healthOK = Data("""    {"status":"ok","models":["cloud","local"],"offline":false}    """.utf8)     private static let healthDegraded = Data("""    {"status":"degraded","models":["cloud","local"],"offline":false,"missing_keys":["cloud"]}    """.utf8)     private static let models = Data("""    {"models":[{"name":"local","endpoint":"http://127.0.0.1:9101/v1","model":"stand-in-small",\    "api_key_env":null,"key_ok":true},{"name":"cloud","endpoint":"http://127.0.0.1:9102/v1",\    "model":"stand-in-large","api_key_env":"RIG_CLOUD_KEY","key_ok":true}],"dry_run":false}    """.utf8)     private static let savings30d = Data("""    {"period_days":30,"unit":"usd","priced":true,"requests":14,"estimated_requests":0,\    "tokens":1028,"realized":0.003558,"baseline":0.009252,"saved":0.005694,"saved_pct":61.5,\    "by_route":{"cloud":{"requests":4,"realized":0.003294,"baseline":0.003294,"saved":0.0,\    "tokens":366},"local":{"requests":10,"realized":0.000264,"baseline":0.005958,\    "saved":0.005694,"tokens":662}},"by_key":{},"price_table_version":"a3db80fd9a78"}    """.utf8)     private static let savingsZeros = Data("""    {"period_days":30,"unit":"usd","priced":true,"requests":0,"estimated_requests":0,\    "tokens":0,"realized":0.0,"baseline":0.0,"saved":0.0,"saved_pct":0.0,"by_route":{},\    "by_key":{},"price_table_version":"a3db80fd9a78"}    """.utf8)     private static let savingsUnpriced = Data("""    {"period_days":30,"unit":"relative","priced":false,"requests":5,"estimated_requests":0,\    "tokens":420,"realized":1.8,"baseline":3.0,"saved":1.2,"saved_pct":40.0,\    "by_route":{"local":{"requests":4,"realized":0.8,"baseline":2.0,"saved":1.2,"tokens":320},\    "cloud":{"requests":1,"realized":1.0,"baseline":1.0,"saved":0.0,"tokens":100}},\    "by_key":{},"price_table_version":"a3db80fd9a78"}    """.utf8)     private static let metricsText = """    # HELP wayfinder_router_requests_total Routed requests by model and mode.    # TYPE wayfinder_router_requests_total counter    wayfinder_router_requests_total{model="local",mode="scored"} 10    wayfinder_router_requests_total{model="cloud",mode="scored"} 4    # HELP wayfinder_router_decision_latency_seconds Time to score a prompt and pick a model (no model call).    # TYPE wayfinder_router_decision_latency_seconds histogram    wayfinder_router_decision_latency_seconds_bucket{le="0.0001"} 13    wayfinder_router_decision_latency_seconds_bucket{le="0.00025"} 14    wayfinder_router_decision_latency_seconds_bucket{le="+Inf"} 14    wayfinder_router_decision_latency_seconds_sum 0.00112602    wayfinder_router_decision_latency_seconds_count 14    """}