import Foundationimport Testing@testable import CodexBarCore @Suite(.serialized)struct OpenRouterUsageStatsTests { @Test func toUsageSnapshot_usesKeyQuotaForPrimaryWindow() { let snapshot = OpenRouterUsageSnapshot( totalCredits: 50, totalUsage: 45.3895596325, balance: 4.6104403675, usedPercent: 90.779119265, keyLimit: 20, keyUsage: 5, rateLimit: nil, updatedAt: Date(timeIntervalSince1970: 1_739_841_600)) let usage = snapshot.toUsageSnapshot() #expect(usage.primary?.usedPercent == 25) #expect(usage.primary?.resetsAt == nil) #expect(usage.primary?.resetDescription == nil) #expect(usage.openRouterUsage?.keyQuotaStatus == .available) } @Test func toUsageSnapshot_withoutValidKeyLimitOmitsPrimaryWindow() { let snapshot = OpenRouterUsageSnapshot( totalCredits: 50, totalUsage: 45.3895596325, balance: 4.6104403675, usedPercent: 90.779119265, keyLimit: nil, keyUsage: nil, rateLimit: nil, updatedAt: Date(timeIntervalSince1970: 1_739_841_600)) let usage = snapshot.toUsageSnapshot() #expect(usage.primary == nil) #expect(usage.openRouterUsage?.keyQuotaStatus == .unavailable) } @Test func toUsageSnapshot_whenNoLimitConfiguredOmitsPrimaryAndMarksNoLimit() { let snapshot = OpenRouterUsageSnapshot( totalCredits: 50, totalUsage: 45.3895596325, balance: 4.6104403675, usedPercent: 90.779119265, keyDataFetched: true, keyLimit: nil, keyUsage: nil, rateLimit: nil, updatedAt: Date(timeIntervalSince1970: 1_739_841_600)) let usage = snapshot.toUsageSnapshot() #expect(usage.primary == nil) #expect(usage.openRouterUsage?.keyQuotaStatus == .noLimitConfigured) } @Test func sanitizers_redactSensitiveTokenShapes() { let body = """ {"error":"bad token sk-or-v1-abc123","token":"secret-token","authorization":"Bearer sk-or-v1-xyz789"} """ let summary = OpenRouterUsageFetcher._sanitizedResponseBodySummaryForTesting(body) let debugBody = OpenRouterUsageFetcher._redactedDebugResponseBodyForTesting(body) #expect(summary.contains("sk-or-v1-[REDACTED]")) #expect(summary.contains("\"token\":\"[REDACTED]\"")) #expect(!summary.contains("secret-token")) #expect(!summary.contains("sk-or-v1-abc123")) #expect(debugBody?.contains("sk-or-v1-[REDACTED]") == true) #expect(debugBody?.contains("\"token\":\"[REDACTED]\"") == true) #expect(debugBody?.contains("secret-token") == false) #expect(debugBody?.contains("sk-or-v1-xyz789") == false) } @Test func non200FetchThrowsGenericHTTPErrorWithoutBodyDetails() async throws { let registered = URLProtocol.registerClass(OpenRouterStubURLProtocol.self) defer { if registered { URLProtocol.unregisterClass(OpenRouterStubURLProtocol.self) } OpenRouterStubURLProtocol.handler = nil } OpenRouterStubURLProtocol.handler = { request in guard let url = request.url else { throw URLError(.badURL) } let body = #"{"error":"invalid sk-or-v1-super-secret","token":"dont-leak-me"}"# return Self.makeResponse(url: url, body: body, statusCode: 401) } do { _ = try await OpenRouterUsageFetcher.fetchUsage( apiKey: "sk-or-v1-test", environment: ["OPENROUTER_API_URL": "https://openrouter.test/api/v1"]) Issue.record("Expected OpenRouterUsageError.apiError") } catch let error as OpenRouterUsageError { guard case let .apiError(message) = error else { Issue.record("Expected apiError, got: \(error)") return } #expect(message == "HTTP 401") #expect(!message.contains("dont-leak-me")) #expect(!message.contains("sk-or-v1-super-secret")) } } @Test func fetchUsage_setsCreditsTimeoutAndClientHeaders() async throws { let registered = URLProtocol.registerClass(OpenRouterStubURLProtocol.self) defer { if registered { URLProtocol.unregisterClass(OpenRouterStubURLProtocol.self) } OpenRouterStubURLProtocol.handler = nil } OpenRouterStubURLProtocol.handler = { request in guard let url = request.url else { throw URLError(.badURL) } switch url.path { case "/api/v1/credits": #expect(request.timeoutInterval == 15) #expect(request.value(forHTTPHeaderField: "HTTP-Referer") == "https://codexbar.example") #expect(request.value(forHTTPHeaderField: "X-Title") == "CodexBar QA") let body = #"{"data":{"total_credits":100,"total_usage":40}}"# return Self.makeResponse(url: url, body: body, statusCode: 200) case "/api/v1/key": let body = #"{"data":{"limit":20,"usage":0.5,"rate_limit":{"requests":120,"interval":"10s"}}}"# return Self.makeResponse(url: url, body: body, statusCode: 200) default: return Self.makeResponse(url: url, body: "{}", statusCode: 404) } } let usage = try await OpenRouterUsageFetcher.fetchUsage( apiKey: "sk-or-v1-test", environment: [ "OPENROUTER_API_URL": "https://openrouter.test/api/v1", "OPENROUTER_HTTP_REFERER": " https://codexbar.example ", "OPENROUTER_X_TITLE": "CodexBar QA", ]) #expect(usage.totalCredits == 100) #expect(usage.totalUsage == 40) #expect(usage.keyDataFetched) #expect(usage.keyLimit == 20) #expect(usage.keyUsage == 0.5) #expect(usage.keyRemaining == 19.5) #expect(usage.keyUsedPercent == 2.5) #expect(usage.keyQuotaStatus == .available) } @Test func fetchUsage_whenKeyEndpointFailsMarksQuotaUnavailable() async throws { let registered = URLProtocol.registerClass(OpenRouterStubURLProtocol.self) defer { if registered { URLProtocol.unregisterClass(OpenRouterStubURLProtocol.self) } OpenRouterStubURLProtocol.handler = nil } OpenRouterStubURLProtocol.handler = { request in guard let url = request.url else { throw URLError(.badURL) } switch url.path { case "/api/v1/credits": let body = #"{"data":{"total_credits":100,"total_usage":40}}"# return Self.makeResponse(url: url, body: body, statusCode: 200) case "/api/v1/key": return Self.makeResponse(url: url, body: "{}", statusCode: 500) default: return Self.makeResponse(url: url, body: "{}", statusCode: 404) } } let usage = try await OpenRouterUsageFetcher.fetchUsage( apiKey: "sk-or-v1-test", environment: ["OPENROUTER_API_URL": "https://openrouter.test/api/v1"]) #expect(!usage.keyDataFetched) #expect(usage.keyQuotaStatus == .unavailable) } @Test func usageSnapshot_roundTripPersistsOpenRouterUsageMetadata() throws { let openRouter = OpenRouterUsageSnapshot( totalCredits: 50, totalUsage: 45.3895596325, balance: 4.6104403675, usedPercent: 90.779119265, keyDataFetched: true, keyLimit: nil, keyUsage: nil, rateLimit: nil, updatedAt: Date(timeIntervalSince1970: 1_739_841_600)) let snapshot = openRouter.toUsageSnapshot() let encoder = JSONEncoder() let data = try encoder.encode(snapshot) let decoded = try JSONDecoder().decode(UsageSnapshot.self, from: data) #expect(decoded.openRouterUsage?.keyDataFetched == true) #expect(decoded.openRouterUsage?.keyQuotaStatus == .noLimitConfigured) } private static func makeResponse( url: URL, body: String, statusCode: Int = 200) -> (HTTPURLResponse, Data) { let response = HTTPURLResponse( url: url, statusCode: statusCode, httpVersion: "HTTP/1.1", headerFields: ["Content-Type": "application/json"])! return (response, Data(body.utf8)) }} final class OpenRouterStubURLProtocol: URLProtocol { nonisolated(unsafe) static var handler: ((URLRequest) throws -> (HTTPURLResponse, Data))? override static func canInit(with request: URLRequest) -> Bool { request.url?.host == "openrouter.test" } override static func canonicalRequest(for request: URLRequest) -> URLRequest { request } override func startLoading() { guard let handler = Self.handler else { self.client?.urlProtocol(self, didFailWithError: URLError(.badServerResponse)) return } do { let (response, data) = try handler(self.request) self.client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed) self.client?.urlProtocol(self, didLoad: data) self.client?.urlProtocolDidFinishLoading(self) } catch { self.client?.urlProtocol(self, didFailWithError: error) } } override func stopLoading() {}}
import Foundation
import Testing
@testable import CodexBarCore
@Suite(.serialized)
struct OpenRouterUsageStatsTests {
@Test
func toUsageSnapshot_usesKeyQuotaForPrimaryWindow() {
let snapshot = OpenRouterUsageSnapshot(
totalCredits: 50,
totalUsage: 45.3895596325,
balance: 4.6104403675,
usedPercent: 90.779119265,
keyLimit: 20,
keyUsage: 5,
rateLimit: nil,
updatedAt: Date(timeIntervalSince1970: 1_739_841_600))
let usage = snapshot.toUsageSnapshot()
#expect(usage.primary?.usedPercent == 25)
#expect(usage.primary?.resetsAt == nil)
#expect(usage.primary?.resetDescription == nil)
#expect(usage.openRouterUsage?.keyQuotaStatus == .available)
}
@Test
func toUsageSnapshot_withoutValidKeyLimitOmitsPrimaryWindow() {
let snapshot = OpenRouterUsageSnapshot(
totalCredits: 50,
totalUsage: 45.3895596325,
balance: 4.6104403675,
usedPercent: 90.779119265,
keyLimit: nil,
keyUsage: nil,
rateLimit: nil,
updatedAt: Date(timeIntervalSince1970: 1_739_841_600))
let usage = snapshot.toUsageSnapshot()
#expect(usage.primary == nil)
#expect(usage.openRouterUsage?.keyQuotaStatus == .unavailable)
}
@Test
func toUsageSnapshot_whenNoLimitConfiguredOmitsPrimaryAndMarksNoLimit() {
let snapshot = OpenRouterUsageSnapshot(
totalCredits: 50,
totalUsage: 45.3895596325,
balance: 4.6104403675,
usedPercent: 90.779119265,
keyDataFetched: true,
keyLimit: nil,
keyUsage: nil,
rateLimit: nil,
updatedAt: Date(timeIntervalSince1970: 1_739_841_600))
let usage = snapshot.toUsageSnapshot()
#expect(usage.primary == nil)
#expect(usage.openRouterUsage?.keyQuotaStatus == .noLimitConfigured)
}
@Test
func sanitizers_redactSensitiveTokenShapes() {
let body = """
{"error":"bad token sk-or-v1-abc123","token":"secret-token","authorization":"Bearer sk-or-v1-xyz789"}
"""
let summary = OpenRouterUsageFetcher._sanitizedResponseBodySummaryForTesting(body)
let debugBody = OpenRouterUsageFetcher._redactedDebugResponseBodyForTesting(body)
#expect(summary.contains("sk-or-v1-[REDACTED]"))
#expect(summary.contains("\"token\":\"[REDACTED]\""))
#expect(!summary.contains("secret-token"))
#expect(!summary.contains("sk-or-v1-abc123"))
#expect(debugBody?.contains("sk-or-v1-[REDACTED]") == true)
#expect(debugBody?.contains("\"token\":\"[REDACTED]\"") == true)
#expect(debugBody?.contains("secret-token") == false)
#expect(debugBody?.contains("sk-or-v1-xyz789") == false)
}
@Test
func non200FetchThrowsGenericHTTPErrorWithoutBodyDetails() async throws {
let registered = URLProtocol.registerClass(OpenRouterStubURLProtocol.self)
defer {
if registered {
URLProtocol.unregisterClass(OpenRouterStubURLProtocol.self)
}
OpenRouterStubURLProtocol.handler = nil
}
OpenRouterStubURLProtocol.handler = { request in
guard let url = request.url else { throw URLError(.badURL) }
let body = #"{"error":"invalid sk-or-v1-super-secret","token":"dont-leak-me"}"#
return Self.makeResponse(url: url, body: body, statusCode: 401)
}
do {
_ = try await OpenRouterUsageFetcher.fetchUsage(
apiKey: "sk-or-v1-test",
environment: ["OPENROUTER_API_URL": "https://openrouter.test/api/v1"])
Issue.record("Expected OpenRouterUsageError.apiError")
} catch let error as OpenRouterUsageError {
guard case let .apiError(message) = error else {
Issue.record("Expected apiError, got: \(error)")
return
}
#expect(message == "HTTP 401")
#expect(!message.contains("dont-leak-me"))
#expect(!message.contains("sk-or-v1-super-secret"))
}
}
@Test
func fetchUsage_setsCreditsTimeoutAndClientHeaders() async throws {
let registered = URLProtocol.registerClass(OpenRouterStubURLProtocol.self)
defer {
if registered {
URLProtocol.unregisterClass(OpenRouterStubURLProtocol.self)
}
OpenRouterStubURLProtocol.handler = nil
}
OpenRouterStubURLProtocol.handler = { request in
guard let url = request.url else { throw URLError(.badURL) }
switch url.path {
case "/api/v1/credits":
#expect(request.timeoutInterval == 15)
#expect(request.value(forHTTPHeaderField: "HTTP-Referer") == "https://codexbar.example")
#expect(request.value(forHTTPHeaderField: "X-Title") == "CodexBar QA")
let body = #"{"data":{"total_credits":100,"total_usage":40}}"#
return Self.makeResponse(url: url, body: body, statusCode: 200)
case "/api/v1/key":
let body = #"{"data":{"limit":20,"usage":0.5,"rate_limit":{"requests":120,"interval":"10s"}}}"#
return Self.makeResponse(url: url, body: body, statusCode: 200)
default:
return Self.makeResponse(url: url, body: "{}", statusCode: 404)
}
}
let usage = try await OpenRouterUsageFetcher.fetchUsage(
apiKey: "sk-or-v1-test",
environment: [
"OPENROUTER_API_URL": "https://openrouter.test/api/v1",
"OPENROUTER_HTTP_REFERER": " https://codexbar.example ",
"OPENROUTER_X_TITLE": "CodexBar QA",
])
#expect(usage.totalCredits == 100)
#expect(usage.totalUsage == 40)
#expect(usage.keyDataFetched)
#expect(usage.keyLimit == 20)
#expect(usage.keyUsage == 0.5)
#expect(usage.keyRemaining == 19.5)
#expect(usage.keyUsedPercent == 2.5)
#expect(usage.keyQuotaStatus == .available)
}
@Test
func fetchUsage_whenKeyEndpointFailsMarksQuotaUnavailable() async throws {
let registered = URLProtocol.registerClass(OpenRouterStubURLProtocol.self)
defer {
if registered {
URLProtocol.unregisterClass(OpenRouterStubURLProtocol.self)
}
OpenRouterStubURLProtocol.handler = nil
}
OpenRouterStubURLProtocol.handler = { request in
guard let url = request.url else { throw URLError(.badURL) }
switch url.path {
case "/api/v1/credits":
let body = #"{"data":{"total_credits":100,"total_usage":40}}"#
return Self.makeResponse(url: url, body: body, statusCode: 200)
case "/api/v1/key":
return Self.makeResponse(url: url, body: "{}", statusCode: 500)
default:
return Self.makeResponse(url: url, body: "{}", statusCode: 404)
}
}
let usage = try await OpenRouterUsageFetcher.fetchUsage(
apiKey: "sk-or-v1-test",
environment: ["OPENROUTER_API_URL": "https://openrouter.test/api/v1"])
#expect(!usage.keyDataFetched)
#expect(usage.keyQuotaStatus == .unavailable)
}
@Test
func usageSnapshot_roundTripPersistsOpenRouterUsageMetadata() throws {
let openRouter = OpenRouterUsageSnapshot(
totalCredits: 50,
totalUsage: 45.3895596325,
balance: 4.6104403675,
usedPercent: 90.779119265,
keyDataFetched: true,
keyLimit: nil,
keyUsage: nil,
rateLimit: nil,
updatedAt: Date(timeIntervalSince1970: 1_739_841_600))
let snapshot = openRouter.toUsageSnapshot()
let encoder = JSONEncoder()
let data = try encoder.encode(snapshot)
let decoded = try JSONDecoder().decode(UsageSnapshot.self, from: data)
#expect(decoded.openRouterUsage?.keyDataFetched == true)
#expect(decoded.openRouterUsage?.keyQuotaStatus == .noLimitConfigured)
}
private static func makeResponse(
url: URL,
body: String,
statusCode: Int = 200) -> (HTTPURLResponse, Data)
{
let response = HTTPURLResponse(
url: url,
statusCode: statusCode,
httpVersion: "HTTP/1.1",
headerFields: ["Content-Type": "application/json"])!
return (response, Data(body.utf8))
}
}
final class OpenRouterStubURLProtocol: URLProtocol {
nonisolated(unsafe) static var handler: ((URLRequest) throws -> (HTTPURLResponse, Data))?
override static func canInit(with request: URLRequest) -> Bool {
request.url?.host == "openrouter.test"
}
override static func canonicalRequest(for request: URLRequest) -> URLRequest {
request
}
override func startLoading() {
guard let handler = Self.handler else {
self.client?.urlProtocol(self, didFailWithError: URLError(.badServerResponse))
return
}
do {
let (response, data) = try handler(self.request)
self.client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed)
self.client?.urlProtocol(self, didLoad: data)
self.client?.urlProtocolDidFinishLoading(self)
} catch {
self.client?.urlProtocol(self, didFailWithError: error)
}
}
override func stopLoading() {}
}