import Foundationimport Testing@testable import CodexBarCore struct AdminAPIUsageLocalDaySelectionTests { @Test func `OpenAI current day includes UTC bucket containing positive timezone morning`() throws { let calendar = try Self.calendar(timeZoneIdentifier: "Australia/Sydney") let now = try Self.date(year: 2026, month: 5, day: 18, hour: 8, timeZoneIdentifier: "Australia/Sydney") let staleUTCStart = try Self.date(year: 2026, month: 5, day: 16, hour: 0, timeZoneIdentifier: "UTC") let overlappingUTCStart = try Self.date(year: 2026, month: 5, day: 17, hour: 0, timeZoneIdentifier: "UTC") let usage = OpenAIAPIUsageSnapshot( daily: [ OpenAIAPIUsageSnapshot.DailyBucket( day: "2026-05-16", startTime: staleUTCStart, endTime: staleUTCStart.addingTimeInterval(86400), costUSD: 9, requests: 9, inputTokens: 900, cachedInputTokens: 90, outputTokens: 90, totalTokens: 990, lineItems: [], models: []), OpenAIAPIUsageSnapshot.DailyBucket( day: "2026-05-17", startTime: overlappingUTCStart, endTime: overlappingUTCStart.addingTimeInterval(86400), costUSD: 2.5, requests: 3, inputTokens: 200, cachedInputTokens: 20, outputTokens: 30, totalTokens: 250, lineItems: [], models: []), ], updatedAt: now) let today = usage.summary(forLocalDayContaining: now, calendar: calendar) #expect(today.costUSD == 2.5) #expect(today.requests == 3) #expect(today.totalTokens == 250) } @Test func `OpenAI current day does not sum adjacent UTC buckets after positive timezone UTC rollover`() throws { let calendar = try Self.calendar(timeZoneIdentifier: "Australia/Sydney") let now = try Self.date(year: 2026, month: 5, day: 18, hour: 16, timeZoneIdentifier: "Australia/Sydney") let previousUTCStart = try Self.date(year: 2026, month: 5, day: 17, hour: 0, timeZoneIdentifier: "UTC") let currentUTCStart = try Self.date(year: 2026, month: 5, day: 18, hour: 0, timeZoneIdentifier: "UTC") let usage = OpenAIAPIUsageSnapshot( daily: [ OpenAIAPIUsageSnapshot.DailyBucket( day: "2026-05-17", startTime: previousUTCStart, endTime: previousUTCStart.addingTimeInterval(86400), costUSD: 2.5, requests: 3, inputTokens: 200, cachedInputTokens: 20, outputTokens: 30, totalTokens: 250, lineItems: [], models: []), OpenAIAPIUsageSnapshot.DailyBucket( day: "2026-05-18", startTime: currentUTCStart, endTime: currentUTCStart.addingTimeInterval(86400), costUSD: 4.5, requests: 5, inputTokens: 400, cachedInputTokens: 40, outputTokens: 50, totalTokens: 490, lineItems: [], models: []), ], updatedAt: now) let today = usage.summary(forLocalDayContaining: now, calendar: calendar) #expect(today.costUSD == 4.5) #expect(today.requests == 5) #expect(today.totalTokens == 490) } @Test func `Claude Admin current day includes UTC bucket containing positive timezone morning`() throws { let calendar = try Self.calendar(timeZoneIdentifier: "Australia/Sydney") let now = try Self.date(year: 2026, month: 5, day: 18, hour: 8, timeZoneIdentifier: "Australia/Sydney") let staleUTCStart = try Self.date(year: 2026, month: 5, day: 16, hour: 0, timeZoneIdentifier: "UTC") let overlappingUTCStart = try Self.date(year: 2026, month: 5, day: 17, hour: 0, timeZoneIdentifier: "UTC") let usage = ClaudeAdminAPIUsageSnapshot( daily: [ ClaudeAdminAPIUsageSnapshot.DailyBucket( day: "2026-05-16", startTime: staleUTCStart, endTime: staleUTCStart.addingTimeInterval(86400), costUSD: 9, inputTokens: 900, cacheCreationInputTokens: 90, cacheReadInputTokens: 45, outputTokens: 90, totalTokens: 1125, costItems: [], models: []), ClaudeAdminAPIUsageSnapshot.DailyBucket( day: "2026-05-17", startTime: overlappingUTCStart, endTime: overlappingUTCStart.addingTimeInterval(86400), costUSD: 2.5, inputTokens: 200, cacheCreationInputTokens: 20, cacheReadInputTokens: 10, outputTokens: 30, totalTokens: 260, costItems: [], models: []), ], updatedAt: now) let today = usage.summary(forLocalDayContaining: now, calendar: calendar) #expect(today.costUSD == 2.5) #expect(today.inputTokens == 200) #expect(today.totalTokens == 260) } @Test func `Claude Admin current day does not sum adjacent UTC buckets after negative timezone UTC rollover`() throws { let calendar = try Self.calendar(timeZoneIdentifier: "America/Los_Angeles") let now = try Self.date(year: 2026, month: 6, day: 22, hour: 20, timeZoneIdentifier: "America/Los_Angeles") let previousUTCStart = try Self.date(year: 2026, month: 6, day: 22, hour: 0, timeZoneIdentifier: "UTC") let currentUTCStart = try Self.date(year: 2026, month: 6, day: 23, hour: 0, timeZoneIdentifier: "UTC") let usage = ClaudeAdminAPIUsageSnapshot( daily: [ ClaudeAdminAPIUsageSnapshot.DailyBucket( day: "2026-06-22", startTime: previousUTCStart, endTime: previousUTCStart.addingTimeInterval(86400), costUSD: 2.5, inputTokens: 200, cacheCreationInputTokens: 20, cacheReadInputTokens: 10, outputTokens: 30, totalTokens: 260, costItems: [], models: []), ClaudeAdminAPIUsageSnapshot.DailyBucket( day: "2026-06-23", startTime: currentUTCStart, endTime: currentUTCStart.addingTimeInterval(86400), costUSD: 4.5, inputTokens: 400, cacheCreationInputTokens: 40, cacheReadInputTokens: 20, outputTokens: 50, totalTokens: 510, costItems: [], models: []), ], updatedAt: now) let today = usage.summary(forLocalDayContaining: now, calendar: calendar) #expect(today.costUSD == 4.5) #expect(today.inputTokens == 400) #expect(today.totalTokens == 510) } private static func calendar(timeZoneIdentifier: String) throws -> Calendar { var calendar = Calendar(identifier: .gregorian) calendar.timeZone = try #require(TimeZone(identifier: timeZoneIdentifier)) return calendar } private static func date( year: Int, month: Int, day: Int, hour: Int, timeZoneIdentifier: String) throws -> Date { var components = DateComponents() components.calendar = Calendar(identifier: .gregorian) components.timeZone = TimeZone(identifier: timeZoneIdentifier) components.year = year components.month = month components.day = day components.hour = hour return try #require(components.date) }}
import Foundation
import Testing
@testable import CodexBarCore
struct AdminAPIUsageLocalDaySelectionTests {
@Test
func `OpenAI current day includes UTC bucket containing positive timezone morning`() throws {
let calendar = try Self.calendar(timeZoneIdentifier: "Australia/Sydney")
let now = try Self.date(year: 2026, month: 5, day: 18, hour: 8, timeZoneIdentifier: "Australia/Sydney")
let staleUTCStart = try Self.date(year: 2026, month: 5, day: 16, hour: 0, timeZoneIdentifier: "UTC")
let overlappingUTCStart = try Self.date(year: 2026, month: 5, day: 17, hour: 0, timeZoneIdentifier: "UTC")
let usage = OpenAIAPIUsageSnapshot(
daily: [
OpenAIAPIUsageSnapshot.DailyBucket(
day: "2026-05-16",
startTime: staleUTCStart,
endTime: staleUTCStart.addingTimeInterval(86400),
costUSD: 9,
requests: 9,
inputTokens: 900,
cachedInputTokens: 90,
outputTokens: 90,
totalTokens: 990,
lineItems: [],
models: []),
OpenAIAPIUsageSnapshot.DailyBucket(
day: "2026-05-17",
startTime: overlappingUTCStart,
endTime: overlappingUTCStart.addingTimeInterval(86400),
costUSD: 2.5,
requests: 3,
inputTokens: 200,
cachedInputTokens: 20,
outputTokens: 30,
totalTokens: 250,
lineItems: [],
models: []),
],
updatedAt: now)
let today = usage.summary(forLocalDayContaining: now, calendar: calendar)
#expect(today.costUSD == 2.5)
#expect(today.requests == 3)
#expect(today.totalTokens == 250)
}
@Test
func `OpenAI current day does not sum adjacent UTC buckets after positive timezone UTC rollover`() throws {
let calendar = try Self.calendar(timeZoneIdentifier: "Australia/Sydney")
let now = try Self.date(year: 2026, month: 5, day: 18, hour: 16, timeZoneIdentifier: "Australia/Sydney")
let previousUTCStart = try Self.date(year: 2026, month: 5, day: 17, hour: 0, timeZoneIdentifier: "UTC")
let currentUTCStart = try Self.date(year: 2026, month: 5, day: 18, hour: 0, timeZoneIdentifier: "UTC")
let usage = OpenAIAPIUsageSnapshot(
daily: [
OpenAIAPIUsageSnapshot.DailyBucket(
day: "2026-05-17",
startTime: previousUTCStart,
endTime: previousUTCStart.addingTimeInterval(86400),
costUSD: 2.5,
requests: 3,
inputTokens: 200,
cachedInputTokens: 20,
outputTokens: 30,
totalTokens: 250,
lineItems: [],
models: []),
OpenAIAPIUsageSnapshot.DailyBucket(
day: "2026-05-18",
startTime: currentUTCStart,
endTime: currentUTCStart.addingTimeInterval(86400),
costUSD: 4.5,
requests: 5,
inputTokens: 400,
cachedInputTokens: 40,
outputTokens: 50,
totalTokens: 490,
lineItems: [],
models: []),
],
updatedAt: now)
let today = usage.summary(forLocalDayContaining: now, calendar: calendar)
#expect(today.costUSD == 4.5)
#expect(today.requests == 5)
#expect(today.totalTokens == 490)
}
@Test
func `Claude Admin current day includes UTC bucket containing positive timezone morning`() throws {
let calendar = try Self.calendar(timeZoneIdentifier: "Australia/Sydney")
let now = try Self.date(year: 2026, month: 5, day: 18, hour: 8, timeZoneIdentifier: "Australia/Sydney")
let staleUTCStart = try Self.date(year: 2026, month: 5, day: 16, hour: 0, timeZoneIdentifier: "UTC")
let overlappingUTCStart = try Self.date(year: 2026, month: 5, day: 17, hour: 0, timeZoneIdentifier: "UTC")
let usage = ClaudeAdminAPIUsageSnapshot(
daily: [
ClaudeAdminAPIUsageSnapshot.DailyBucket(
day: "2026-05-16",
startTime: staleUTCStart,
endTime: staleUTCStart.addingTimeInterval(86400),
costUSD: 9,
inputTokens: 900,
cacheCreationInputTokens: 90,
cacheReadInputTokens: 45,
outputTokens: 90,
totalTokens: 1125,
costItems: [],
models: []),
ClaudeAdminAPIUsageSnapshot.DailyBucket(
day: "2026-05-17",
startTime: overlappingUTCStart,
endTime: overlappingUTCStart.addingTimeInterval(86400),
costUSD: 2.5,
inputTokens: 200,
cacheCreationInputTokens: 20,
cacheReadInputTokens: 10,
outputTokens: 30,
totalTokens: 260,
costItems: [],
models: []),
],
updatedAt: now)
let today = usage.summary(forLocalDayContaining: now, calendar: calendar)
#expect(today.costUSD == 2.5)
#expect(today.inputTokens == 200)
#expect(today.totalTokens == 260)
}
@Test
func `Claude Admin current day does not sum adjacent UTC buckets after negative timezone UTC rollover`() throws {
let calendar = try Self.calendar(timeZoneIdentifier: "America/Los_Angeles")
let now = try Self.date(year: 2026, month: 6, day: 22, hour: 20, timeZoneIdentifier: "America/Los_Angeles")
let previousUTCStart = try Self.date(year: 2026, month: 6, day: 22, hour: 0, timeZoneIdentifier: "UTC")
let currentUTCStart = try Self.date(year: 2026, month: 6, day: 23, hour: 0, timeZoneIdentifier: "UTC")
let usage = ClaudeAdminAPIUsageSnapshot(
daily: [
ClaudeAdminAPIUsageSnapshot.DailyBucket(
day: "2026-06-22",
startTime: previousUTCStart,
endTime: previousUTCStart.addingTimeInterval(86400),
costUSD: 2.5,
inputTokens: 200,
cacheCreationInputTokens: 20,
cacheReadInputTokens: 10,
outputTokens: 30,
totalTokens: 260,
costItems: [],
models: []),
ClaudeAdminAPIUsageSnapshot.DailyBucket(
day: "2026-06-23",
startTime: currentUTCStart,
endTime: currentUTCStart.addingTimeInterval(86400),
costUSD: 4.5,
inputTokens: 400,
cacheCreationInputTokens: 40,
cacheReadInputTokens: 20,
outputTokens: 50,
totalTokens: 510,
costItems: [],
models: []),
],
updatedAt: now)
let today = usage.summary(forLocalDayContaining: now, calendar: calendar)
#expect(today.costUSD == 4.5)
#expect(today.inputTokens == 400)
#expect(today.totalTokens == 510)
}
private static func calendar(timeZoneIdentifier: String) throws -> Calendar {
var calendar = Calendar(identifier: .gregorian)
calendar.timeZone = try #require(TimeZone(identifier: timeZoneIdentifier))
return calendar
}
private static func date(
year: Int,
month: Int,
day: Int,
hour: Int,
timeZoneIdentifier: String) throws -> Date
{
var components = DateComponents()
components.calendar = Calendar(identifier: .gregorian)
components.timeZone = TimeZone(identifier: timeZoneIdentifier)
components.year = year
components.month = month
components.day = day
components.hour = hour
return try #require(components.date)
}
}