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

197 lines
8.2 KiB
Swift

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)
}
}