import Foundationimport Testing@testable import CodexBarCore struct CodexAdditionalRateLimitsTests { @Test func `maps additional spark limit into a named extra rate window`() 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": { "allowed": true, "limit_reached": false, "primary_window": { "used_percent": 30, "reset_at": 1766948068, "limit_window_seconds": 18000, "reset_after_seconds": 12345 }, "secondary_window": { "used_percent": 100, "reset_at": 1767407914, "limit_window_seconds": 604800, "reset_after_seconds": 56789 } } } ] } """ let creds = CodexOAuthCredentials( accessToken: "access", refreshToken: "refresh", idToken: nil, accountId: nil, lastRefresh: Date()) let mapped = try CodexOAuthFetchStrategy._mapUsageForTesting(Data(json.utf8), credentials: creds) let snapshot = try #require(mapped) // Primary/weekly behavior is unchanged. #expect(snapshot.primary?.usedPercent == 22) #expect(snapshot.secondary?.usedPercent == 43) // Spark surfaces as distinct 5-hour and weekly extra windows. let extras = try #require(snapshot.extraRateWindows) #expect(extras.count == 2) let spark = try #require(extras.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(extras.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 `keeps valid spark window when an additional limit sibling is malformed`() throws { let json = """ { "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": [ "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 creds = CodexOAuthCredentials( accessToken: "access", refreshToken: "refresh", idToken: nil, accountId: nil, lastRefresh: Date()) let mapped = try CodexOAuthFetchStrategy._mapUsageForTesting(Data(json.utf8), credentials: creds) let snapshot = try #require(mapped) // Valid primary/weekly fields do not regress. #expect(snapshot.primary?.usedPercent == 22) #expect(snapshot.secondary?.usedPercent == 43) // The malformed siblings are skipped, but the valid Spark entry survives. let extras = try #require(snapshot.extraRateWindows) #expect(extras.count == 1) #expect(extras.first?.id == "codex-spark") #expect(extras.first?.window.usedPercent == 30) } @Test func `keeps primary usage when every additional limit element is malformed`() throws { let json = """ { "rate_limit": { "primary_window": { "used_percent": 22, "reset_at": 1766948068, "limit_window_seconds": 18000 } }, "additional_rate_limits": ["garbage", 1, true] } """ let creds = CodexOAuthCredentials( accessToken: "access", refreshToken: "refresh", idToken: nil, accountId: nil, lastRefresh: Date()) let snapshot = try CodexOAuthFetchStrategy._mapUsageForTesting(Data(json.utf8), credentials: creds) #expect(snapshot?.primary?.usedPercent == 22) #expect(snapshot?.extraRateWindows == nil) } @Test func `omits extra rate windows when additional limits are absent`() throws { let json = """ { "rate_limit": { "primary_window": { "used_percent": 22, "reset_at": 1766948068, "limit_window_seconds": 18000 } } } """ let creds = CodexOAuthCredentials( accessToken: "access", refreshToken: "refresh", idToken: nil, accountId: nil, lastRefresh: Date()) let snapshot = try CodexOAuthFetchStrategy._mapUsageForTesting(Data(json.utf8), credentials: creds) #expect(snapshot?.primary?.usedPercent == 22) #expect(snapshot?.extraRateWindows == nil) } @Test func `tolerates malformed additional limits while keeping primary window`() throws { let json = """ { "rate_limit": { "primary_window": { "used_percent": 22, "reset_at": 1766948068, "limit_window_seconds": 18000 } }, "additional_rate_limits": "unexpected" } """ let creds = CodexOAuthCredentials( accessToken: "access", refreshToken: "refresh", idToken: nil, accountId: nil, lastRefresh: Date()) let snapshot = try CodexOAuthFetchStrategy._mapUsageForTesting(Data(json.utf8), credentials: creds) #expect(snapshot?.primary?.usedPercent == 22) #expect(snapshot?.extraRateWindows == nil) } @Test func `skips additional limits without a usable window`() throws { let json = """ { "rate_limit": { "primary_window": { "used_percent": 22, "reset_at": 1766948068, "limit_window_seconds": 18000 } }, "additional_rate_limits": [ { "limit_name": "GPT-5.3-Codex-Spark", "metered_feature": "gpt_5_3_codex_spark", "rate_limit": null } ] } """ let creds = CodexOAuthCredentials( accessToken: "access", refreshToken: "refresh", idToken: nil, accountId: nil, lastRefresh: Date()) let snapshot = try CodexOAuthFetchStrategy._mapUsageForTesting(Data(json.utf8), credentials: creds) #expect(snapshot?.primary?.usedPercent == 22) #expect(snapshot?.extraRateWindows == nil) } @Test func `maps non spark additional limit using a slugged id and api label`() throws { let now = Date(timeIntervalSince1970: 1_800_000_000) let json = """ [ { "limit_name": "GPT-5.3-Codex-Mini", "metered_feature": "gpt_5_3_codex_mini", "rate_limit": { "allowed": true, "limit_reached": false, "primary_window": { "used_percent": 12, "reset_at": 1766948068, "limit_window_seconds": 18000 } } }, { "limit_name": "GPT-5.3-Codex-Mini", "metered_feature": "gpt_5_3_codex_mini", "rate_limit": { "allowed": true, "limit_reached": false, "primary_window": { "used_percent": 99, "reset_at": 1766948068, "limit_window_seconds": 18000 } } } ] """ let entries = try JSONDecoder().decode([CodexUsageResponse.AdditionalRateLimit].self, from: Data(json.utf8)) let windows = CodexAdditionalRateLimitMapper.extraRateWindows(from: entries, now: now) // Duplicate ids collapse to the first occurrence. #expect(windows.count == 1) let window = try #require(windows.first) #expect(window.id == "codex-gpt-5-3-codex-mini") #expect(window.title == "GPT-5.3-Codex-Mini") #expect(window.window.usedPercent == 12) } @Test func `dedupes split spark entries by window kind`() throws { let now = Date(timeIntervalSince1970: 1_800_000_000) let json = """ [ { "limit_name": "GPT-5.3-Codex-Spark", "metered_feature": "gpt_5_3_codex_spark", "rate_limit": { "allowed": true, "limit_reached": false, "primary_window": { "used_percent": 20, "reset_at": 1766948068, "limit_window_seconds": 18000 } } }, { "limit_name": "GPT-5.3-Codex-Spark Weekly", "metered_feature": "gpt_5_3_codex_spark", "rate_limit": { "allowed": true, "limit_reached": false, "primary_window": { "used_percent": 80, "reset_at": 1767407914, "limit_window_seconds": 604800 } } }, { "limit_name": "GPT-5.3-Codex-Spark Duplicate", "metered_feature": "gpt_5_3_codex_spark", "rate_limit": { "allowed": true, "limit_reached": false, "primary_window": { "used_percent": 99, "reset_at": 1766948068, "limit_window_seconds": 18000 } } } ] """ let entries = try JSONDecoder().decode([CodexUsageResponse.AdditionalRateLimit].self, from: Data(json.utf8)) let windows = CodexAdditionalRateLimitMapper.extraRateWindows(from: entries, now: now) #expect(windows.map(\.id) == ["codex-spark", "codex-spark-weekly"]) #expect(windows.first?.window.usedPercent == 20) #expect(windows.last?.window.usedPercent == 80) }}
import Foundation
import Testing
@testable import CodexBarCore
struct CodexAdditionalRateLimitsTests {
@Test
func `maps additional spark limit into a named extra rate window`() 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": {
"allowed": true,
"limit_reached": false,
"primary_window": {
"used_percent": 30,
"reset_at": 1766948068,
"limit_window_seconds": 18000,
"reset_after_seconds": 12345
},
"secondary_window": {
"used_percent": 100,
"reset_at": 1767407914,
"limit_window_seconds": 604800,
"reset_after_seconds": 56789
}
}
}
]
}
"""
let creds = CodexOAuthCredentials(
accessToken: "access",
refreshToken: "refresh",
idToken: nil,
accountId: nil,
lastRefresh: Date())
let mapped = try CodexOAuthFetchStrategy._mapUsageForTesting(Data(json.utf8), credentials: creds)
let snapshot = try #require(mapped)
// Primary/weekly behavior is unchanged.
#expect(snapshot.primary?.usedPercent == 22)
#expect(snapshot.secondary?.usedPercent == 43)
// Spark surfaces as distinct 5-hour and weekly extra windows.
let extras = try #require(snapshot.extraRateWindows)
#expect(extras.count == 2)
let spark = try #require(extras.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(extras.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 `keeps valid spark window when an additional limit sibling is malformed`() throws {
let json = """
{
"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": [
"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 creds = CodexOAuthCredentials(
accessToken: "access",
refreshToken: "refresh",
idToken: nil,
accountId: nil,
lastRefresh: Date())
let mapped = try CodexOAuthFetchStrategy._mapUsageForTesting(Data(json.utf8), credentials: creds)
let snapshot = try #require(mapped)
// Valid primary/weekly fields do not regress.
#expect(snapshot.primary?.usedPercent == 22)
#expect(snapshot.secondary?.usedPercent == 43)
// The malformed siblings are skipped, but the valid Spark entry survives.
let extras = try #require(snapshot.extraRateWindows)
#expect(extras.count == 1)
#expect(extras.first?.id == "codex-spark")
#expect(extras.first?.window.usedPercent == 30)
}
@Test
func `keeps primary usage when every additional limit element is malformed`() throws {
let json = """
{
"rate_limit": {
"primary_window": { "used_percent": 22, "reset_at": 1766948068, "limit_window_seconds": 18000 }
},
"additional_rate_limits": ["garbage", 1, true]
}
"""
let creds = CodexOAuthCredentials(
accessToken: "access",
refreshToken: "refresh",
idToken: nil,
accountId: nil,
lastRefresh: Date())
let snapshot = try CodexOAuthFetchStrategy._mapUsageForTesting(Data(json.utf8), credentials: creds)
#expect(snapshot?.primary?.usedPercent == 22)
#expect(snapshot?.extraRateWindows == nil)
}
@Test
func `omits extra rate windows when additional limits are absent`() throws {
let json = """
{
"rate_limit": {
"primary_window": {
"used_percent": 22,
"reset_at": 1766948068,
"limit_window_seconds": 18000
}
}
}
"""
let creds = CodexOAuthCredentials(
accessToken: "access",
refreshToken: "refresh",
idToken: nil,
accountId: nil,
lastRefresh: Date())
let snapshot = try CodexOAuthFetchStrategy._mapUsageForTesting(Data(json.utf8), credentials: creds)
#expect(snapshot?.primary?.usedPercent == 22)
#expect(snapshot?.extraRateWindows == nil)
}
@Test
func `tolerates malformed additional limits while keeping primary window`() throws {
let json = """
{
"rate_limit": {
"primary_window": {
"used_percent": 22,
"reset_at": 1766948068,
"limit_window_seconds": 18000
}
},
"additional_rate_limits": "unexpected"
}
"""
let creds = CodexOAuthCredentials(
accessToken: "access",
refreshToken: "refresh",
idToken: nil,
accountId: nil,
lastRefresh: Date())
let snapshot = try CodexOAuthFetchStrategy._mapUsageForTesting(Data(json.utf8), credentials: creds)
#expect(snapshot?.primary?.usedPercent == 22)
#expect(snapshot?.extraRateWindows == nil)
}
@Test
func `skips additional limits without a usable window`() throws {
let json = """
{
"rate_limit": {
"primary_window": {
"used_percent": 22,
"reset_at": 1766948068,
"limit_window_seconds": 18000
}
},
"additional_rate_limits": [
{
"limit_name": "GPT-5.3-Codex-Spark",
"metered_feature": "gpt_5_3_codex_spark",
"rate_limit": null
}
]
}
"""
let creds = CodexOAuthCredentials(
accessToken: "access",
refreshToken: "refresh",
idToken: nil,
accountId: nil,
lastRefresh: Date())
let snapshot = try CodexOAuthFetchStrategy._mapUsageForTesting(Data(json.utf8), credentials: creds)
#expect(snapshot?.primary?.usedPercent == 22)
#expect(snapshot?.extraRateWindows == nil)
}
@Test
func `maps non spark additional limit using a slugged id and api label`() throws {
let now = Date(timeIntervalSince1970: 1_800_000_000)
let json = """
[
{
"limit_name": "GPT-5.3-Codex-Mini",
"metered_feature": "gpt_5_3_codex_mini",
"rate_limit": {
"allowed": true,
"limit_reached": false,
"primary_window": {
"used_percent": 12,
"reset_at": 1766948068,
"limit_window_seconds": 18000
}
}
},
{
"limit_name": "GPT-5.3-Codex-Mini",
"metered_feature": "gpt_5_3_codex_mini",
"rate_limit": {
"allowed": true,
"limit_reached": false,
"primary_window": {
"used_percent": 99,
"reset_at": 1766948068,
"limit_window_seconds": 18000
}
}
}
]
"""
let entries = try JSONDecoder().decode([CodexUsageResponse.AdditionalRateLimit].self, from: Data(json.utf8))
let windows = CodexAdditionalRateLimitMapper.extraRateWindows(from: entries, now: now)
// Duplicate ids collapse to the first occurrence.
#expect(windows.count == 1)
let window = try #require(windows.first)
#expect(window.id == "codex-gpt-5-3-codex-mini")
#expect(window.title == "GPT-5.3-Codex-Mini")
#expect(window.window.usedPercent == 12)
}
@Test
func `dedupes split spark entries by window kind`() throws {
let now = Date(timeIntervalSince1970: 1_800_000_000)
let json = """
[
{
"limit_name": "GPT-5.3-Codex-Spark",
"metered_feature": "gpt_5_3_codex_spark",
"rate_limit": {
"allowed": true,
"limit_reached": false,
"primary_window": {
"used_percent": 20,
"reset_at": 1766948068,
"limit_window_seconds": 18000
}
}
},
{
"limit_name": "GPT-5.3-Codex-Spark Weekly",
"metered_feature": "gpt_5_3_codex_spark",
"rate_limit": {
"allowed": true,
"limit_reached": false,
"primary_window": {
"used_percent": 80,
"reset_at": 1767407914,
"limit_window_seconds": 604800
}
}
},
{
"limit_name": "GPT-5.3-Codex-Spark Duplicate",
"metered_feature": "gpt_5_3_codex_spark",
"rate_limit": {
"allowed": true,
"limit_reached": false,
"primary_window": {
"used_percent": 99,
"reset_at": 1766948068,
"limit_window_seconds": 18000
}
}
}
]
"""
let entries = try JSONDecoder().decode([CodexUsageResponse.AdditionalRateLimit].self, from: Data(json.utf8))
let windows = CodexAdditionalRateLimitMapper.extraRateWindows(from: entries, now: now)
#expect(windows.map(\.id) == ["codex-spark", "codex-spark-weekly"])
#expect(windows.first?.window.usedPercent == 20)
#expect(windows.last?.window.usedPercent == 80)
}
}