233 lines
9.8 KiB
Swift
233 lines
9.8 KiB
Swift
import Foundation
|
|
import Testing
|
|
@testable import CodexBarCore
|
|
|
|
/// Dashboard-path coverage for Codex `additional_rate_limits` (e.g. GPT-5.3-Codex-Spark): the
|
|
/// OpenAI web dashboard usage API decodes the same `wham/usage` JSON as the OAuth path, so Spark
|
|
/// limits must survive the `dashboardAPIData -> DashboardSnapshotComponents -> OpenAIDashboardSnapshot
|
|
/// -> fromAttachedDashboard -> UsageSnapshot.extraRateWindows` chain without disturbing the
|
|
/// existing primary/weekly/credits/plan mapping.
|
|
struct OpenAIDashboardSparkTests {
|
|
private static func response(from json: String) throws -> CodexUsageResponse {
|
|
try JSONDecoder().decode(CodexUsageResponse.self, from: Data(json.utf8))
|
|
}
|
|
|
|
@Test
|
|
func `dashboard api data maps additional spark limit into extra windows`() throws {
|
|
let json = """
|
|
{
|
|
"plan_type": "pro",
|
|
"rate_limit": {
|
|
"primary_window": { "used_percent": 22, "reset_at": 1766948068, "limit_window_seconds": 18000 },
|
|
"secondary_window": { "used_percent": 43, "reset_at": 1767407914, "limit_window_seconds": 604800 }
|
|
},
|
|
"additional_rate_limits": [
|
|
{
|
|
"limit_name": "GPT-5.3-Codex-Spark",
|
|
"metered_feature": "gpt_5_3_codex_spark",
|
|
"rate_limit": {
|
|
"primary_window": { "used_percent": 30, "reset_at": 1766948068, "limit_window_seconds": 18000 },
|
|
"secondary_window": { "used_percent": 100, "reset_at": 1767407914, "limit_window_seconds": 604800 }
|
|
}
|
|
}
|
|
]
|
|
}
|
|
"""
|
|
let response = try Self.response(from: json)
|
|
let apiData = OpenAIDashboardFetcher.dashboardAPIData(from: response)
|
|
// Primary/weekly/credits/plan continue to map exactly as before.
|
|
#expect(apiData.primaryLimit?.usedPercent == 22)
|
|
#expect(apiData.secondaryLimit?.usedPercent == 43)
|
|
#expect(apiData.accountPlan == "pro")
|
|
// Spark surfaces with stable ids/titles for the additional 5-hour and weekly windows.
|
|
#expect(apiData.extraRateWindows.count == 2)
|
|
let spark = try #require(apiData.extraRateWindows.first)
|
|
#expect(spark.id == "codex-spark")
|
|
#expect(spark.title == "Codex Spark 5-hour")
|
|
#expect(spark.window.usedPercent == 30)
|
|
#expect(spark.window.windowMinutes == 300)
|
|
#expect(spark.window.resetsAt != nil)
|
|
let weekly = try #require(apiData.extraRateWindows.last)
|
|
#expect(weekly.id == "codex-spark-weekly")
|
|
#expect(weekly.title == "Codex Spark Weekly")
|
|
#expect(weekly.window.usedPercent == 100)
|
|
#expect(weekly.window.windowMinutes == 10080)
|
|
#expect(weekly.window.resetsAt != nil)
|
|
}
|
|
|
|
@Test
|
|
func `dashboard api data has empty extra windows when additional limits are absent`() throws {
|
|
let json = """
|
|
{
|
|
"rate_limit": {
|
|
"primary_window": { "used_percent": 22, "reset_at": 1766948068, "limit_window_seconds": 18000 }
|
|
}
|
|
}
|
|
"""
|
|
let response = try Self.response(from: json)
|
|
let apiData = OpenAIDashboardFetcher.dashboardAPIData(from: response)
|
|
#expect(apiData.primaryLimit?.usedPercent == 22)
|
|
#expect(apiData.extraRateWindows.isEmpty)
|
|
}
|
|
|
|
@Test
|
|
func `dashboard api data tolerates non array additional limits while keeping primary`() throws {
|
|
let json = """
|
|
{
|
|
"rate_limit": {
|
|
"primary_window": { "used_percent": 22, "reset_at": 1766948068, "limit_window_seconds": 18000 }
|
|
},
|
|
"additional_rate_limits": "unexpected"
|
|
}
|
|
"""
|
|
let response = try Self.response(from: json)
|
|
let apiData = OpenAIDashboardFetcher.dashboardAPIData(from: response)
|
|
#expect(apiData.primaryLimit?.usedPercent == 22)
|
|
#expect(apiData.extraRateWindows.isEmpty)
|
|
}
|
|
|
|
@Test
|
|
func `dashboard api data keeps valid spark when a malformed sibling is present`() throws {
|
|
// Lossy per-element decode (shared with the OAuth path via CodexUsageResponse) means a single
|
|
// malformed entry cannot discard its valid siblings.
|
|
let json = """
|
|
{
|
|
"rate_limit": {
|
|
"primary_window": { "used_percent": 22, "reset_at": 1766948068, "limit_window_seconds": 18000 }
|
|
},
|
|
"additional_rate_limits": [
|
|
"garbage-not-an-object",
|
|
{
|
|
"limit_name": "GPT-5.3-Codex-Spark",
|
|
"metered_feature": "gpt_5_3_codex_spark",
|
|
"rate_limit": {
|
|
"primary_window": { "used_percent": 30, "reset_at": 1766948068, "limit_window_seconds": 18000 }
|
|
}
|
|
},
|
|
42
|
|
]
|
|
}
|
|
"""
|
|
let response = try Self.response(from: json)
|
|
let apiData = OpenAIDashboardFetcher.dashboardAPIData(from: response)
|
|
#expect(apiData.primaryLimit?.usedPercent == 22)
|
|
#expect(apiData.extraRateWindows.count == 1)
|
|
#expect(apiData.extraRateWindows.first?.id == "codex-spark")
|
|
#expect(apiData.extraRateWindows.first?.window.usedPercent == 30)
|
|
}
|
|
|
|
@Test
|
|
func `dashboard snapshot exposes extra rate windows via to usage snapshot`() throws {
|
|
let now = Date(timeIntervalSince1970: 1_766_948_000)
|
|
let snapshot = OpenAIDashboardSnapshot(
|
|
signedInEmail: "codex@example.com",
|
|
codeReviewRemainingPercent: nil,
|
|
creditEvents: [],
|
|
dailyBreakdown: [],
|
|
usageBreakdown: [],
|
|
creditsPurchaseURL: nil,
|
|
primaryLimit: RateWindow(
|
|
usedPercent: 22,
|
|
windowMinutes: 300,
|
|
resetsAt: now.addingTimeInterval(3600),
|
|
resetDescription: nil),
|
|
secondaryLimit: RateWindow(
|
|
usedPercent: 43,
|
|
windowMinutes: 10080,
|
|
resetsAt: now.addingTimeInterval(6 * 24 * 60 * 60),
|
|
resetDescription: nil),
|
|
extraRateWindows: [
|
|
NamedRateWindow(
|
|
id: "codex-spark",
|
|
title: "Codex Spark 5-hour",
|
|
window: RateWindow(
|
|
usedPercent: 30,
|
|
windowMinutes: 300,
|
|
resetsAt: now.addingTimeInterval(60 * 60),
|
|
resetDescription: nil)),
|
|
NamedRateWindow(
|
|
id: "codex-spark-weekly",
|
|
title: "Codex Spark Weekly",
|
|
window: RateWindow(
|
|
usedPercent: 100,
|
|
windowMinutes: 10080,
|
|
resetsAt: now.addingTimeInterval(6 * 24 * 60 * 60),
|
|
resetDescription: nil)),
|
|
],
|
|
updatedAt: now)
|
|
|
|
let usage = try #require(snapshot.toUsageSnapshot(provider: .codex))
|
|
// Primary/weekly behavior preserved.
|
|
#expect(usage.primary?.usedPercent == 22)
|
|
#expect(usage.secondary?.usedPercent == 43)
|
|
// Spark surfaces through UsageSnapshot.extraRateWindows for dashboard-source users.
|
|
let extras = try #require(usage.extraRateWindows)
|
|
#expect(extras.map(\.id) == ["codex-spark", "codex-spark-weekly"])
|
|
#expect(extras.first?.window.usedPercent == 30)
|
|
#expect(extras.last?.window.usedPercent == 100)
|
|
}
|
|
|
|
@Test
|
|
func `dashboard snapshot codable round trips extra rate windows`() throws {
|
|
let now = Date(timeIntervalSince1970: 1_766_948_000)
|
|
let snapshot = OpenAIDashboardSnapshot(
|
|
signedInEmail: nil,
|
|
codeReviewRemainingPercent: nil,
|
|
creditEvents: [],
|
|
dailyBreakdown: [],
|
|
usageBreakdown: [],
|
|
creditsPurchaseURL: nil,
|
|
extraRateWindows: [
|
|
NamedRateWindow(
|
|
id: "codex-spark",
|
|
title: "Codex Spark 5-hour",
|
|
window: RateWindow(
|
|
usedPercent: 30,
|
|
windowMinutes: 300,
|
|
resetsAt: now.addingTimeInterval(60 * 60),
|
|
resetDescription: nil)),
|
|
NamedRateWindow(
|
|
id: "codex-spark-weekly",
|
|
title: "Codex Spark Weekly",
|
|
window: RateWindow(
|
|
usedPercent: 100,
|
|
windowMinutes: 10080,
|
|
resetsAt: now.addingTimeInterval(6 * 24 * 60 * 60),
|
|
resetDescription: nil)),
|
|
],
|
|
updatedAt: now)
|
|
|
|
let encoder = JSONEncoder()
|
|
encoder.dateEncodingStrategy = .iso8601
|
|
let decoder = JSONDecoder()
|
|
decoder.dateDecodingStrategy = .iso8601
|
|
|
|
let data = try encoder.encode(snapshot)
|
|
let decoded = try decoder.decode(OpenAIDashboardSnapshot.self, from: data)
|
|
#expect(decoded.extraRateWindows?.map(\.id) == ["codex-spark", "codex-spark-weekly"])
|
|
#expect(decoded.extraRateWindows?.first?.window.usedPercent == 30)
|
|
#expect(decoded.extraRateWindows?.last?.window.usedPercent == 100)
|
|
}
|
|
|
|
@Test
|
|
func `dashboard snapshot decoder preserves absence of extra rate windows`() throws {
|
|
// Older cached snapshots predate the field; decoding such payloads must yield nil and never
|
|
// throw, so existing dashboard caches keep working.
|
|
let json = """
|
|
{
|
|
"signedInEmail": "codex@example.com",
|
|
"codeReviewRemainingPercent": null,
|
|
"creditEvents": [],
|
|
"dailyBreakdown": [],
|
|
"usageBreakdown": [],
|
|
"creditsPurchaseURL": null,
|
|
"updatedAt": "2026-04-30T19:27:07Z"
|
|
}
|
|
"""
|
|
let decoder = JSONDecoder()
|
|
decoder.dateDecodingStrategy = .iso8601
|
|
let snapshot = try decoder.decode(OpenAIDashboardSnapshot.self, from: Data(json.utf8))
|
|
#expect(snapshot.extraRateWindows == nil)
|
|
}
|
|
}
|