Files
steipete--codexbar/Tests/CodexBarTests/GrokWebBillingFetcherTests.swift
2026-07-13 12:22:33 +08:00

884 lines
35 KiB
Swift

import Foundation
import Testing
@testable import CodexBarCore
@Suite(.serialized)
struct GrokWebBillingFetcherTests {
private final class AttemptCounter: @unchecked Sendable {
private let lock = NSLock()
private var value = 0
func increment() -> Int {
self.lock.lock()
defer { self.lock.unlock() }
self.value += 1
return self.value
}
func current() -> Int {
self.lock.lock()
defer { self.lock.unlock() }
return self.value
}
}
@Test
func `provider exposes cli and web source modes`() {
#expect(GrokProviderDescriptor.descriptor.fetchPlan.sourceModes == [.auto, .cli, .web])
}
@Test
func `descriptor uses Credits label for primary usage window`() {
let metadata = GrokProviderDescriptor.descriptor.metadata
#expect(metadata.sessionLabel == "Credits")
#expect(metadata.weeklyLabel == "On-demand")
#expect(!metadata.supportsOpus)
}
@Test
func `primaryLabel derives Weekly or Monthly from resetsAt`() {
let now = Date()
let in6Days = now.addingTimeInterval(6 * 86400)
let in30Days = now.addingTimeInterval(30 * 86400)
let in90Days = now.addingTimeInterval(90 * 86400)
let lateWeeklyWindow = RateWindow(
usedPercent: 25,
windowMinutes: 7 * 24 * 60,
resetsAt: now.addingTimeInterval(86400),
resetDescription: nil)
#expect(GrokProviderDescriptor.primaryLabel(resetsAt: in6Days, now: now) == "Weekly")
#expect(GrokProviderDescriptor.primaryLabel(resetsAt: in30Days, now: now) == "Monthly")
#expect(GrokProviderDescriptor.primaryLabel(resetsAt: in90Days, now: now) == nil)
#expect(GrokProviderDescriptor.primaryLabel(window: lateWeeklyWindow, now: now) == "Weekly")
#expect(GrokProviderDescriptor.primaryLabel(resetsAt: nil) == nil)
}
@Test
func `cli runtime does not import browser cookies unless explicitly enabled`() {
#expect(GrokWebFetchStrategy.canImportBrowserCookies(runtime: .app, env: [:]))
#expect(!GrokWebFetchStrategy.canImportBrowserCookies(runtime: .cli, env: [:]))
#expect(GrokWebFetchStrategy.canImportBrowserCookies(
runtime: .cli,
env: ["CODEXBAR_ALLOW_BROWSER_COOKIE_IMPORT": "1"]))
}
@Test
func `web strategy tries later browser session when first cookie is stale`() async throws {
let stale = try #require(Self.cookie(name: "sso", value: "stale"))
let valid = try #require(Self.cookie(name: "sso", value: "valid"))
let sessions = [
GrokCookieImporter.SessionInfo(cookies: [stale], sourceLabel: "Chrome Profile 1"),
GrokCookieImporter.SessionInfo(cookies: [valid], sourceLabel: "Chrome Profile 2"),
]
var attemptedHeaders: [String] = []
let result = try await GrokWebFetchStrategy.fetchFirstValidCookieSession(sessions) { cookieHeader, _ in
attemptedHeaders.append(cookieHeader)
guard cookieHeader.contains("valid") else {
throw GrokWebBillingError.requestFailed(401, "stale")
}
return GrokWebBillingSnapshot(
usedPercent: 12,
resetsAt: Date(timeIntervalSince1970: 1_800_000_000))
}
#expect(attemptedHeaders == ["sso=stale", "sso=valid"])
#expect(result.0.usedPercent == 12)
#expect(result.1 == "Chrome Profile 2")
}
@Test
func `cookie authenticated web billing does not reuse auth file identity`() {
#expect(GrokWebFetchStrategy.credentialsForWebBillingSnapshot(
credentials: Self.credentials,
authenticatedByAuthFile: false) == nil)
#expect(GrokWebFetchStrategy.credentialsForWebBillingSnapshot(
credentials: Self.credentials,
authenticatedByAuthFile: true)?
.email == "grok@example.com")
}
@Test
func `parses grok grpc web billing frame`() throws {
let reset = UInt64(1_800_000_000)
let payload = Self.protobufPayload(usedPercent: 42.5, resetEpoch: reset)
let data = Self.grpcFrame(payload)
let snapshot = try GrokWebBillingFetcher.parseGRPCWebResponse(
data,
now: Date(timeIntervalSince1970: 1_799_000_000))
#expect(snapshot.usedPercent == 42.5)
#expect(snapshot.resetsAt == Date(timeIntervalSince1970: TimeInterval(reset)))
}
@Test
func `parses unframed grok billing protobuf payload`() throws {
let hex =
"0a3f0d7f6a9c3f12001a002206088097f3d0062a060880b191d2063a07080215a9389b3f3a07080115d6ea183c" +
"421208011206088097f3d0061a060880b191d206"
let data = try #require(Self.data(hexString: hex))
let snapshot = try GrokWebBillingFetcher.parseGRPCWebResponse(
data,
now: Date(timeIntervalSince1970: 1_780_000_000))
#expect(snapshot.usedPercent == 1.222000002861023)
#expect(snapshot.resetsAt == Date(timeIntervalSince1970: 1_782_864_000))
}
@Test
func `parses unframed zero percent payload that resembles an empty grpc frame`() throws {
let reset = UInt64(1_800_000_000)
let payload = Self.protobufPayload(usedPercent: 0, resetEpoch: reset)
#expect(GrokWebBillingFetcher.grpcWebDataFrames(from: payload).isEmpty)
let snapshot = try GrokWebBillingFetcher.parseGRPCWebResponse(
payload,
now: Date(timeIntervalSince1970: 1_799_000_000))
#expect(snapshot.usedPercent == 0)
#expect(snapshot.resetsAt == Date(timeIntervalSince1970: TimeInterval(reset)))
}
@Test
func `does not treat grpc frame prefix as raw protobuf`() {
#expect(!GrokWebBillingFetcher.looksLikeProtobufPayload(Data([0, 0, 0, 0, 10])))
}
@Test
func `web strategy tries cookie plus bearer before cookie only`() async throws {
let cookie = try #require(Self.cookie(name: "sso", value: "session"))
let sessions = [GrokCookieImporter.SessionInfo(cookies: [cookie], sourceLabel: "Chrome")]
var attempts: [String] = []
let result = try await GrokWebFetchStrategy.fetchFirstValidCookieSession(
sessions,
credentials: Self.credentials)
{ _, authCredentials in
let mode = authCredentials == nil ? "cookie-only" : "cookie+bearer"
attempts.append(mode)
guard mode == "cookie+bearer" else {
throw GrokWebBillingError.requestFailed(401, "needs bearer")
}
return GrokWebBillingSnapshot(usedPercent: 9, resetsAt: nil)
}
#expect(attempts == ["cookie+bearer"])
#expect(result.0.usedPercent == 9)
}
@Test
func `web strategy skips expired bearer for browser cookies`() async throws {
let cookie = try #require(Self.cookie(name: "sso", value: "session"))
let sessions = [GrokCookieImporter.SessionInfo(cookies: [cookie], sourceLabel: "Chrome")]
let expired = GrokCredentials(
accessToken: "expired-token",
refreshToken: nil,
scope: "https://auth.x.ai::client",
authMode: "oidc",
userId: nil,
email: nil,
firstName: nil,
lastName: nil,
teamId: nil,
oidcIssuer: nil,
oidcClientId: nil,
expiresAt: .distantPast,
createTime: nil)
var attempts: [String] = []
_ = try await GrokWebFetchStrategy.fetchFirstValidCookieSession(
sessions,
credentials: expired)
{ _, authCredentials in
attempts.append(authCredentials == nil ? "cookie-only" : "cookie+bearer")
return GrokWebBillingSnapshot(usedPercent: 9, resetsAt: nil)
}
#expect(attempts == ["cookie-only"])
}
@Test
func `web strategy preserves malformed auth file error`() async throws {
let grokHome = FileManager.default.temporaryDirectory
.appendingPathComponent("CodexBar-GrokWebBilling-\(UUID().uuidString)", isDirectory: true)
try FileManager.default.createDirectory(at: grokHome, withIntermediateDirectories: true)
defer { try? FileManager.default.removeItem(at: grokHome) }
try Data("not-json".utf8).write(to: grokHome.appendingPathComponent("auth.json"))
let browserDetection = BrowserDetection(cacheTTL: 0)
let context = ProviderFetchContext(
runtime: .cli,
sourceMode: .web,
includeCredits: true,
webTimeout: 1,
webDebugDumpHTML: false,
verbose: false,
env: ["GROK_HOME": grokHome.path],
settings: nil,
fetcher: UsageFetcher(),
claudeFetcher: ClaudeUsageFetcher(browserDetection: browserDetection),
browserDetection: browserDetection)
await #expect {
_ = try await GrokWebFetchStrategy().fetch(context)
} throws: { error in
guard case GrokCredentialsError.decodeFailed = error else { return false }
return true
}
}
@Test
func `status seven scope failure is not classified as bad credentials`() {
#expect(!GrokWebBillingError.isAuthenticationFailure(
status: 7,
message: "OAuth2 access token lacks the required billing scope"))
}
@Test
func `ignores grpc web trailer frames`() {
let payload = Self.protobufPayload(usedPercent: 12.25, resetEpoch: 1_800_000_001)
let trailer = Data("grpc-status: 0\r\n".utf8)
let data = Self.grpcFrame(payload) + Self.grpcFrame(trailer, flags: 0x80)
let frames = GrokWebBillingFetcher.grpcWebDataFrames(from: data)
#expect(frames == [payload])
}
@Test
func `web fetch turns grpc unauthenticated trailer into reauth guidance`() async throws {
defer {
GrokWebBillingStubURLProtocol.requests = []
GrokWebBillingStubURLProtocol.requestBodies = []
GrokWebBillingStubURLProtocol.handler = nil
}
let config = URLSessionConfiguration.ephemeral
config.protocolClasses = [GrokWebBillingStubURLProtocol.self]
let session = URLSession(configuration: config)
let endpoint = try #require(URL(string: "https://grok.test/grok_api_v2.GrokBuildBilling/GetGrokCreditsConfig"))
let body = Self.grpcFrame(Data("grpc-status: 16\r\ngrpc-message: token%20expired\r\n".utf8), flags: 0x80)
#expect(GrokWebBillingFetcher.grpcWebTrailerFields(from: body)["grpc-status"] == "16")
GrokWebBillingStubURLProtocol.requests = []
GrokWebBillingStubURLProtocol.requestBodies = []
GrokWebBillingStubURLProtocol.handler = { request in
let url = try #require(request.url)
let response = HTTPURLResponse(
url: url,
statusCode: 200,
httpVersion: nil,
headerFields: ["Content-Type": "application/grpc-web+proto"])!
return (response, body)
}
await #expect {
_ = try await GrokWebBillingFetcher.fetch(
credentials: Self.credentials,
session: session,
endpoint: endpoint)
} throws: { error in
error.localizedDescription.contains("grok login")
}
}
@Test
func `web fetch turns grpc unauthenticated headers into reauth guidance`() async throws {
defer {
GrokWebBillingStubURLProtocol.requests = []
GrokWebBillingStubURLProtocol.requestBodies = []
GrokWebBillingStubURLProtocol.handler = nil
}
let config = URLSessionConfiguration.ephemeral
config.protocolClasses = [GrokWebBillingStubURLProtocol.self]
let session = URLSession(configuration: config)
let endpoint = try #require(URL(string: "https://grok.test/grok_api_v2.GrokBuildBilling/GetGrokCreditsConfig"))
GrokWebBillingStubURLProtocol.requests = []
GrokWebBillingStubURLProtocol.requestBodies = []
GrokWebBillingStubURLProtocol.handler = { request in
let url = try #require(request.url)
let response = HTTPURLResponse(
url: url,
statusCode: 200,
httpVersion: nil,
headerFields: [
"Content-Type": "application/grpc-web+proto",
"grpc-status": "16",
"grpc-message": "Invalid%20bearer%20token.",
])!
return (response, Data())
}
await #expect {
_ = try await GrokWebBillingFetcher.fetch(
credentials: Self.credentials,
session: session,
endpoint: endpoint)
} throws: { error in
error.localizedDescription.contains("grok login")
}
}
@Test
func `web fetch turns grpc permission denied bad credentials into reauth guidance`() async throws {
defer {
GrokWebBillingStubURLProtocol.requests = []
GrokWebBillingStubURLProtocol.requestBodies = []
GrokWebBillingStubURLProtocol.handler = nil
}
let config = URLSessionConfiguration.ephemeral
config.protocolClasses = [GrokWebBillingStubURLProtocol.self]
let session = URLSession(configuration: config)
let endpoint = try #require(URL(string: "https://grok.test/grok_api_v2.GrokBuildBilling/GetGrokCreditsConfig"))
let message = "The OAuth2 access token could not be validated. [WKE=unauthenticated:bad-credentials]"
let encodedMessage = message.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? message
let body = Self.grpcFrame(
Data("grpc-status: 7\r\ngrpc-message: \(encodedMessage)\r\n".utf8),
flags: 0x80)
GrokWebBillingStubURLProtocol.requests = []
GrokWebBillingStubURLProtocol.requestBodies = []
GrokWebBillingStubURLProtocol.handler = { request in
let url = try #require(request.url)
let response = HTTPURLResponse(
url: url,
statusCode: 200,
httpVersion: nil,
headerFields: ["Content-Type": "application/grpc-web+proto"])!
return (response, body)
}
await #expect {
_ = try await GrokWebBillingFetcher.fetch(
credentials: Self.credentials,
session: session,
endpoint: endpoint)
} throws: { error in
error.localizedDescription.contains("grok.com") &&
error.localizedDescription.contains("grok login") &&
!error.localizedDescription.contains("status 7")
}
}
@Test
func `rejects reset only billing because it cannot render usage`() {
var payload = Data()
payload.append(0x10) // field 2, varint reset timestamp
payload.append(contentsOf: Self.varint(1_800_000_001))
#expect {
_ = try GrokWebBillingFetcher.parseGRPCWebResponse(Self.grpcFrame(payload))
} throws: { error in
guard case GrokWebBillingError.parseFailed = error else { return false }
return true
}
}
@Test
func `parses grok no usage yet billing response as zero percent`() throws {
let data = Data([
0x00, 0x00, 0x00, 0x00, 0x37, 0x0A, 0x35, 0x12,
0x00, 0x1A, 0x00, 0x22, 0x06, 0x08, 0x80, 0xDA,
0xCF, 0xCF, 0x06, 0x2A, 0x06, 0x08, 0x80, 0x97,
0xF3, 0xD0, 0x06, 0x32, 0x09, 0x0A, 0x05, 0x08,
0xEA, 0x0F, 0x10, 0x04, 0x12, 0x00, 0x32, 0x09,
0x0A, 0x05, 0x08, 0xEA, 0x0F, 0x10, 0x03, 0x12,
0x00, 0x32, 0x09, 0x0A, 0x05, 0x08, 0xEA, 0x0F,
0x10, 0x02, 0x12, 0x00, 0x80, 0x00, 0x00, 0x00,
0x0F, 0x67, 0x72, 0x70, 0x63, 0x2D, 0x73, 0x74,
0x61, 0x74, 0x75, 0x73, 0x3A, 0x30, 0x0D, 0x0A,
])
let snapshot = try GrokWebBillingFetcher.parseGRPCWebResponse(
data,
now: Date(timeIntervalSince1970: 1_768_000_000))
#expect(snapshot.usedPercent == 0)
#expect(snapshot.resetsAt == Date(timeIntervalSince1970: 1_780_272_000))
}
@Test
func `parses omitted zero percent with current billing period`() throws {
let data = Data([
0x00, 0x00, 0x00, 0x00, 0x2A, 0x0A, 0x28, 0x12,
0x00, 0x1A, 0x00, 0x22, 0x06, 0x08, 0x80, 0x97,
0xF3, 0xD0, 0x06, 0x2A, 0x06, 0x08, 0x80, 0xB1,
0x91, 0xD2, 0x06, 0x42, 0x12, 0x08, 0x01, 0x12,
0x06, 0x08, 0x80, 0x97, 0xF3, 0xD0, 0x06, 0x1A,
0x06, 0x08, 0x80, 0xB1, 0x91, 0xD2, 0x06, 0x80,
0x00, 0x00, 0x00, 0x0F, 0x67, 0x72, 0x70, 0x63,
0x2D, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x3A,
0x30, 0x0D, 0x0A,
])
let snapshot = try GrokWebBillingFetcher.parseGRPCWebResponse(
data,
now: Date(timeIntervalSince1970: 1_781_000_000))
#expect(snapshot.usedPercent == 0)
#expect(snapshot.resetsAt == Date(timeIntervalSince1970: 1_782_864_000))
}
@Test
func `uses billing field one instead of earlier unrelated float`() throws {
var payload = Data()
payload.append(0x4D) // field 9, fixed32 unrelated in-range float
var unrelatedBits = Float(7).bitPattern.littleEndian
withUnsafeBytes(of: &unrelatedBits) { payload.append(contentsOf: $0) }
payload.append(0x0D) // field 1, fixed32 billing usage percent
var usageBits = Float(42).bitPattern.littleEndian
withUnsafeBytes(of: &usageBits) { payload.append(contentsOf: $0) }
payload.append(0x10) // field 2, varint reset timestamp
payload.append(contentsOf: Self.varint(1_800_000_001))
let snapshot = try GrokWebBillingFetcher.parseGRPCWebResponse(Self.grpcFrame(payload))
#expect(snapshot.usedPercent == 42)
}
@Test
func `chooses future billing end instead of recent billing start`() throws {
let recentStart = UInt64(1_800_000_000)
let billingEnd = UInt64(1_802_592_000)
var payload = Data()
payload.append(0x0D) // field 1, fixed32 usage percent
var percentBits = Float(33).bitPattern.littleEndian
withUnsafeBytes(of: &percentBits) { payload.append(contentsOf: $0) }
payload.append(0x10) // field 2, varint billing start
payload.append(contentsOf: Self.varint(recentStart))
payload.append(0x18) // field 3, varint billing end
payload.append(contentsOf: Self.varint(billingEnd))
let snapshot = try GrokWebBillingFetcher.parseGRPCWebResponse(
Self.grpcFrame(payload),
now: Date(timeIntervalSince1970: TimeInterval(recentStart + 1800)))
#expect(snapshot.resetsAt == Date(timeIntervalSince1970: TimeInterval(billingEnd)))
}
@Test
func `web fetch posts grpc web request with bearer token`() async throws {
defer {
GrokWebBillingStubURLProtocol.requests = []
GrokWebBillingStubURLProtocol.requestBodies = []
GrokWebBillingStubURLProtocol.handler = nil
}
let config = URLSessionConfiguration.ephemeral
config.protocolClasses = [GrokWebBillingStubURLProtocol.self]
let session = URLSession(configuration: config)
let endpoint = try #require(URL(string: "https://grok.test/grok_api_v2.GrokBuildBilling/GetGrokCreditsConfig"))
let reset = UInt64(1_800_000_002)
GrokWebBillingStubURLProtocol.requests = []
GrokWebBillingStubURLProtocol.requestBodies = []
GrokWebBillingStubURLProtocol.handler = { request in
let url = try #require(request.url)
#expect(url == endpoint)
#expect(request.httpMethod == "POST")
#expect(request.value(forHTTPHeaderField: "Authorization") == "Bearer token-123")
#expect(request.value(forHTTPHeaderField: "Origin") == "https://grok.com")
#expect(request.value(forHTTPHeaderField: "Referer") == "https://grok.com/?_s=usage")
#expect(request.value(forHTTPHeaderField: "Accept") == "*/*")
#expect(request.value(forHTTPHeaderField: "Content-Type") == "application/grpc-web+proto")
#expect(request.value(forHTTPHeaderField: "x-grpc-web") == "1")
#expect(request.timeoutInterval == 15)
let response = HTTPURLResponse(
url: url,
statusCode: 200,
httpVersion: nil,
headerFields: ["Content-Type": "application/grpc-web+proto"])!
let body = Self.grpcFrame(Self.protobufPayload(usedPercent: 55.5, resetEpoch: reset))
return (response, body)
}
let snapshot = try await GrokWebBillingFetcher.fetch(
credentials: Self.credentials,
session: session,
endpoint: endpoint)
#expect(GrokWebBillingStubURLProtocol.requests.count == 1)
#expect(GrokWebBillingStubURLProtocol.requestBodies == [Data([0x00, 0x00, 0x00, 0x00, 0x00])])
#expect(snapshot.usedPercent == 55.5)
#expect(snapshot.resetsAt == Date(timeIntervalSince1970: TimeInterval(reset)))
}
@Test
func `web fetch retries transient grpc timeout once`() async throws {
defer {
GrokWebBillingStubURLProtocol.requests = []
GrokWebBillingStubURLProtocol.requestBodies = []
GrokWebBillingStubURLProtocol.handler = nil
}
let config = URLSessionConfiguration.ephemeral
config.protocolClasses = [GrokWebBillingStubURLProtocol.self]
let session = URLSession(configuration: config)
let endpoint = try #require(URL(string: "https://grok.test/grok_api_v2.GrokBuildBilling/GetGrokCreditsConfig"))
let reset = UInt64(1_800_000_005)
let attempts = AttemptCounter()
GrokWebBillingStubURLProtocol.requests = []
GrokWebBillingStubURLProtocol.requestBodies = []
GrokWebBillingStubURLProtocol.handler = { request in
let attempt = attempts.increment()
let url = try #require(request.url)
let response = HTTPURLResponse(
url: url,
statusCode: 200,
httpVersion: nil,
headerFields: ["Content-Type": "application/grpc-web+proto"])!
if attempt == 1 {
let body = Self.grpcFrame(
Data("grpc-status: 1\r\ngrpc-message: Timeout%20expired\r\n".utf8),
flags: 0x80)
return (response, body)
}
return (response, Self.grpcFrame(Self.protobufPayload(usedPercent: 25, resetEpoch: reset)))
}
let snapshot = try await GrokWebBillingFetcher.fetch(
credentials: Self.credentials,
session: session,
endpoint: endpoint)
#expect(attempts.current() == 2)
#expect(GrokWebBillingStubURLProtocol.requests.count == 2)
#expect(snapshot.usedPercent == 25)
#expect(snapshot.resetsAt == Date(timeIntervalSince1970: TimeInterval(reset)))
}
@Test
func `web fetch retries grpc deadline exceeded without message`() async throws {
defer {
GrokWebBillingStubURLProtocol.requests = []
GrokWebBillingStubURLProtocol.requestBodies = []
GrokWebBillingStubURLProtocol.handler = nil
}
let config = URLSessionConfiguration.ephemeral
config.protocolClasses = [GrokWebBillingStubURLProtocol.self]
let session = URLSession(configuration: config)
let endpoint = try #require(URL(string: "https://grok.test/grok_api_v2.GrokBuildBilling/GetGrokCreditsConfig"))
let attempts = AttemptCounter()
GrokWebBillingStubURLProtocol.requests = []
GrokWebBillingStubURLProtocol.requestBodies = []
GrokWebBillingStubURLProtocol.handler = { request in
let attempt = attempts.increment()
let url = try #require(request.url)
let response = HTTPURLResponse(
url: url,
statusCode: 200,
httpVersion: nil,
headerFields: ["Content-Type": "application/grpc-web+proto"])!
if attempt == 1 {
return (response, Self.grpcFrame(Data("grpc-status: 4\r\n".utf8), flags: 0x80))
}
return (response, Self.grpcFrame(Self.protobufPayload(usedPercent: 25, resetEpoch: 1_800_000_005)))
}
let snapshot = try await GrokWebBillingFetcher.fetch(
credentials: Self.credentials,
session: session,
endpoint: endpoint)
#expect(attempts.current() == 2)
#expect(snapshot.usedPercent == 25)
}
@Test
func `web fetch retries HTTP gateway timeout once`() async throws {
defer {
GrokWebBillingStubURLProtocol.requests = []
GrokWebBillingStubURLProtocol.requestBodies = []
GrokWebBillingStubURLProtocol.handler = nil
}
let config = URLSessionConfiguration.ephemeral
config.protocolClasses = [GrokWebBillingStubURLProtocol.self]
let session = URLSession(configuration: config)
let endpoint = try #require(URL(string: "https://grok.test/grok_api_v2.GrokBuildBilling/GetGrokCreditsConfig"))
let attempts = AttemptCounter()
GrokWebBillingStubURLProtocol.requests = []
GrokWebBillingStubURLProtocol.requestBodies = []
GrokWebBillingStubURLProtocol.handler = { request in
let attempt = attempts.increment()
let url = try #require(request.url)
let statusCode = attempt == 1 ? 504 : 200
let response = HTTPURLResponse(
url: url,
statusCode: statusCode,
httpVersion: nil,
headerFields: ["Content-Type": "application/grpc-web+proto"])!
if attempt == 1 {
return (response, Data("gateway timeout".utf8))
}
return (response, Self.grpcFrame(Self.protobufPayload(usedPercent: 25, resetEpoch: 1_800_000_005)))
}
let snapshot = try await GrokWebBillingFetcher.fetch(
credentials: Self.credentials,
session: session,
endpoint: endpoint)
#expect(attempts.current() == 2)
#expect(snapshot.usedPercent == 25)
}
@Test
func `web fetch can authenticate with browser cookies`() async throws {
defer {
GrokWebBillingStubURLProtocol.requests = []
GrokWebBillingStubURLProtocol.requestBodies = []
GrokWebBillingStubURLProtocol.handler = nil
}
let config = URLSessionConfiguration.ephemeral
config.protocolClasses = [GrokWebBillingStubURLProtocol.self]
let session = URLSession(configuration: config)
let endpoint = try #require(URL(string: "https://grok.test/grok_api_v2.GrokBuildBilling/GetGrokCreditsConfig"))
GrokWebBillingStubURLProtocol.requests = []
GrokWebBillingStubURLProtocol.requestBodies = []
GrokWebBillingStubURLProtocol.handler = { request in
#expect(request.value(forHTTPHeaderField: "Cookie") == "sso=session; sso-rw=session")
#expect(request.value(forHTTPHeaderField: "Authorization") == nil)
#expect(request.value(forHTTPHeaderField: "x-user-agent") == "connect-es/2.1.1")
let response = HTTPURLResponse(
url: endpoint,
statusCode: 200,
httpVersion: nil,
headerFields: ["Content-Type": "application/grpc-web+proto"])!
let body = Self.grpcFrame(Self.protobufPayload(usedPercent: 9, resetEpoch: 1_800_000_004))
return (response, body)
}
let snapshot = try await GrokWebBillingFetcher.fetch(
cookieHeader: "sso=session; sso-rw=session",
session: session,
endpoint: endpoint)
#expect(snapshot.usedPercent == 9)
}
@Test
func `web fetch sends browser cookies with bearer credentials`() async throws {
defer {
GrokWebBillingStubURLProtocol.requests = []
GrokWebBillingStubURLProtocol.requestBodies = []
GrokWebBillingStubURLProtocol.handler = nil
}
let config = URLSessionConfiguration.ephemeral
config.protocolClasses = [GrokWebBillingStubURLProtocol.self]
let session = URLSession(configuration: config)
let endpoint = try #require(URL(string: "https://grok.test/grok_api_v2.GrokBuildBilling/GetGrokCreditsConfig"))
GrokWebBillingStubURLProtocol.requests = []
GrokWebBillingStubURLProtocol.requestBodies = []
GrokWebBillingStubURLProtocol.handler = { request in
#expect(request.value(forHTTPHeaderField: "Cookie") == "sso=session")
#expect(request.value(forHTTPHeaderField: "Authorization") == "Bearer token-123")
let response = HTTPURLResponse(
url: endpoint,
statusCode: 200,
httpVersion: nil,
headerFields: ["Content-Type": "application/grpc-web+proto"])!
return (response, Self.protobufPayload(usedPercent: 9, resetEpoch: 1_800_000_004))
}
let snapshot = try await GrokWebBillingFetcher.fetch(
cookieHeader: "sso=session",
credentials: Self.credentials,
session: session,
endpoint: endpoint)
#expect(snapshot.usedPercent == 9)
}
@Test
func `web fetch turns unauthorized response into reauth guidance`() async throws {
defer {
GrokWebBillingStubURLProtocol.requests = []
GrokWebBillingStubURLProtocol.requestBodies = []
GrokWebBillingStubURLProtocol.handler = nil
}
let config = URLSessionConfiguration.ephemeral
config.protocolClasses = [GrokWebBillingStubURLProtocol.self]
let session = URLSession(configuration: config)
let endpoint = try #require(URL(string: "https://grok.test/grok_api_v2.GrokBuildBilling/GetGrokCreditsConfig"))
GrokWebBillingStubURLProtocol.requests = []
GrokWebBillingStubURLProtocol.requestBodies = []
GrokWebBillingStubURLProtocol.handler = { request in
let url = try #require(request.url)
let response = HTTPURLResponse(
url: url,
statusCode: 401,
httpVersion: nil,
headerFields: ["Content-Type": "text/plain"])!
return (response, Data("unauthorized".utf8))
}
await #expect {
_ = try await GrokWebBillingFetcher.fetch(
credentials: Self.credentials,
session: session,
endpoint: endpoint)
} throws: { error in
error.localizedDescription.contains("grok login")
}
}
@Test
func `usage snapshot maps web billing when cli billing is absent`() {
let snapshot = GrokUsageSnapshot(
billing: nil,
webBilling: GrokWebBillingSnapshot(
usedPercent: 67.25,
resetsAt: Date(timeIntervalSince1970: 1_800_000_003)),
credentials: Self.credentials,
localSummary: nil,
cliVersion: nil,
updatedAt: Date(timeIntervalSince1970: 1_799_000_000))
let usage = snapshot.toUsageSnapshot()
#expect(usage.primary?.usedPercent == 67.25)
#expect(usage.primary?.windowMinutes == nil)
#expect(usage.primary?.resetsAt == Date(timeIntervalSince1970: 1_800_000_003))
#expect(usage.accountEmail(for: .grok) == "grok@example.com")
#expect(usage.loginMethod(for: .grok) == "SuperGrok")
}
private static let credentials = GrokCredentials(
accessToken: "token-123",
refreshToken: "refresh-123",
scope: "https://auth.x.ai::client",
authMode: "oidc",
userId: "user-123",
email: "grok@example.com",
firstName: "G",
lastName: "Rok",
teamId: "team-123",
oidcIssuer: "https://auth.x.ai",
oidcClientId: "client",
expiresAt: Date(timeIntervalSince1970: 1_900_000_000),
createTime: Date(timeIntervalSince1970: 1_799_000_000))
private static func protobufPayload(usedPercent: Float, resetEpoch: UInt64) -> Data {
var data = Data()
data.append(0x0D) // field 1, fixed32
var percentBits = usedPercent.bitPattern.littleEndian
withUnsafeBytes(of: &percentBits) { data.append(contentsOf: $0) }
data.append(0x10) // field 2, varint
data.append(contentsOf: Self.varint(resetEpoch))
return data
}
private static func grpcFrame(_ payload: Data, flags: UInt8 = 0x00) -> Data {
var data = Data([flags])
let length = UInt32(payload.count).bigEndian
withUnsafeBytes(of: length) { data.append(contentsOf: $0) }
data.append(payload)
return data
}
private static func varint(_ value: UInt64) -> [UInt8] {
var remaining = value
var bytes: [UInt8] = []
repeat {
var byte = UInt8(remaining & 0x7F)
remaining >>= 7
if remaining != 0 { byte |= 0x80 }
bytes.append(byte)
} while remaining != 0
return bytes
}
private static func cookie(name: String, value: String) -> HTTPCookie? {
HTTPCookie(properties: [
.domain: "grok.com",
.path: "/",
.name: name,
.value: value,
])
}
private static func data(hexString: String) -> Data? {
var data = Data()
var index = hexString.startIndex
while index < hexString.endIndex {
let next = hexString.index(index, offsetBy: 2, limitedBy: hexString.endIndex) ?? hexString.endIndex
guard let byte = UInt8(hexString[index..<next], radix: 16) else { return nil }
data.append(byte)
index = next
}
return data
}
}
final class GrokWebBillingStubURLProtocol: URLProtocol {
nonisolated(unsafe) static var requests: [URLRequest] = []
nonisolated(unsafe) static var requestBodies: [Data?] = []
nonisolated(unsafe) static var handler: (@Sendable (URLRequest) throws -> (HTTPURLResponse, Data))?
override static func canInit(with _: URLRequest) -> Bool {
true
}
override static func canonicalRequest(for request: URLRequest) -> URLRequest {
request
}
override func startLoading() {
Self.requests.append(self.request)
Self.requestBodies.append(Self.readBody(from: self.request))
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() {}
private static func readBody(from request: URLRequest) -> Data? {
if let body = request.httpBody { return body }
guard let stream = request.httpBodyStream else { return nil }
stream.open()
defer { stream.close() }
var data = Data()
var buffer = [UInt8](repeating: 0, count: 1024)
while stream.hasBytesAvailable {
let count = stream.read(&buffer, maxLength: buffer.count)
if count > 0 {
data.append(buffer, count: count)
} else {
break
}
}
return data
}
}