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

512 lines
21 KiB
Swift

import CodexBarCore
import Foundation
import Testing
@testable import CodexBar
extension HistoricalUsagePaceTests {
@MainActor
@Test
func `backfill skips when dashboard authority is display only`() async throws {
let store = try Self.makeUsageStoreForBackfillTests(
suite: "HistoricalUsagePaceTests-backfill-display-only",
historyFileURL: Self.makeTempURL())
store._setCodexHistoricalDatasetForTesting(nil)
let snapshotNow = Date(timeIntervalSince1970: 1_770_000_000)
let dashboard = OpenAIDashboardSnapshot(
signedInEmail: "shared@example.com",
codeReviewRemainingPercent: nil,
creditEvents: [],
dailyBreakdown: [],
usageBreakdown: Self.syntheticBreakdown(endingAt: snapshotNow, days: 35, dailyCredits: 10),
creditsPurchaseURL: nil,
primaryLimit: nil,
secondaryLimit: RateWindow(
usedPercent: 50,
windowMinutes: 10080,
resetsAt: snapshotNow.addingTimeInterval(2 * 24 * 60 * 60),
resetDescription: nil),
creditsRemaining: nil,
accountPlan: nil,
updatedAt: snapshotNow)
store.backfillCodexHistoricalFromDashboardIfNeeded(
dashboard,
authorityDecision: CodexDashboardAuthorityDecision(
disposition: .displayOnly,
reason: .sameEmailAmbiguity(email: "shared@example.com"),
allowedEffects: [],
cleanup: Set(CodexDashboardCleanup.allCases)),
attachedAccountEmail: "shared@example.com")
try await Task.sleep(for: .milliseconds(250))
#expect(store.codexHistoricalDataset == nil)
}
@MainActor
@Test
func `backfill skips when dashboard authority fail closes`() async throws {
let store = try Self.makeUsageStoreForBackfillTests(
suite: "HistoricalUsagePaceTests-backfill-fail-closed",
historyFileURL: Self.makeTempURL())
store._setCodexHistoricalDatasetForTesting(nil)
let snapshotNow = Date(timeIntervalSince1970: 1_770_000_000)
let dashboard = OpenAIDashboardSnapshot(
signedInEmail: "wrong@example.com",
codeReviewRemainingPercent: nil,
creditEvents: [],
dailyBreakdown: [],
usageBreakdown: Self.syntheticBreakdown(endingAt: snapshotNow, days: 35, dailyCredits: 10),
creditsPurchaseURL: nil,
primaryLimit: nil,
secondaryLimit: RateWindow(
usedPercent: 50,
windowMinutes: 10080,
resetsAt: snapshotNow.addingTimeInterval(2 * 24 * 60 * 60),
resetDescription: nil),
creditsRemaining: nil,
accountPlan: nil,
updatedAt: snapshotNow)
store.backfillCodexHistoricalFromDashboardIfNeeded(
dashboard,
authorityDecision: CodexDashboardAuthorityDecision(
disposition: .failClosed,
reason: .wrongEmail(expected: "expected@example.com", actual: "wrong@example.com"),
allowedEffects: [],
cleanup: Set(CodexDashboardCleanup.allCases)),
attachedAccountEmail: "expected@example.com")
try await Task.sleep(for: .milliseconds(250))
#expect(store.codexHistoricalDataset == nil)
}
@MainActor
@Test
func `backfill uses dashboard secondary when available`() async throws {
let store = try Self.makeUsageStoreForBackfillTests(
suite: "HistoricalUsagePaceTests-backfill-dashboard-secondary",
historyFileURL: Self.makeTempURL())
store._setCodexHistoricalDatasetForTesting(nil)
let snapshotNow = Date(timeIntervalSince1970: 1_770_000_000)
let staleSnapshot = UsageSnapshot(
primary: nil,
secondary: RateWindow(
usedPercent: 5,
windowMinutes: 10080,
resetsAt: snapshotNow.addingTimeInterval(2 * 24 * 60 * 60),
resetDescription: nil),
tertiary: nil,
providerCost: nil,
updatedAt: snapshotNow.addingTimeInterval(-30 * 60),
identity: nil)
store._setSnapshotForTesting(staleSnapshot, provider: .codex)
let dashboard = OpenAIDashboardSnapshot(
signedInEmail: nil,
codeReviewRemainingPercent: nil,
creditEvents: [],
dailyBreakdown: [],
usageBreakdown: Self.syntheticBreakdown(endingAt: snapshotNow, days: 35, dailyCredits: 10),
creditsPurchaseURL: nil,
primaryLimit: nil,
secondaryLimit: RateWindow(
usedPercent: 50,
windowMinutes: 10080,
resetsAt: snapshotNow.addingTimeInterval(2 * 24 * 60 * 60),
resetDescription: nil),
creditsRemaining: nil,
accountPlan: nil,
updatedAt: snapshotNow)
store.backfillCodexHistoricalFromDashboardIfNeeded(
dashboard,
authorityDecision: CodexDashboardAuthorityDecision(
disposition: .attach,
reason: .trustedEmailMatchNoCompetingOwner,
allowedEffects: [.historicalBackfill],
cleanup: []),
attachedAccountEmail: "attached@example.com")
for _ in 0..<40 {
if (store.codexHistoricalDataset?.weeks.count ?? 0) >= 3 {
break
}
try await Task.sleep(for: .milliseconds(50))
}
#expect((store.codexHistoricalDataset?.weeks.count ?? 0) >= 3)
}
@MainActor
@Test
func `backfill uses normalized dashboard weekly when only primary window is weekly`() async throws {
let store = try Self.makeUsageStoreForBackfillTests(
suite: "HistoricalUsagePaceTests-backfill-normalized-dashboard-weekly",
historyFileURL: Self.makeTempURL())
store._setCodexHistoricalDatasetForTesting(nil)
let snapshotNow = Date(timeIntervalSince1970: 1_770_000_000)
let seededSnapshot = UsageSnapshot(
primary: RateWindow(
usedPercent: 22,
windowMinutes: 300,
resetsAt: snapshotNow.addingTimeInterval(2 * 60 * 60),
resetDescription: nil),
secondary: nil,
tertiary: nil,
updatedAt: snapshotNow,
identity: nil)
store._setSnapshotForTesting(seededSnapshot, provider: .codex)
let dashboard = OpenAIDashboardSnapshot(
signedInEmail: nil,
codeReviewRemainingPercent: nil,
creditEvents: [],
dailyBreakdown: [],
usageBreakdown: Self.syntheticBreakdown(endingAt: snapshotNow, days: 35, dailyCredits: 10),
creditsPurchaseURL: nil,
primaryLimit: RateWindow(
usedPercent: 50,
windowMinutes: 10080,
resetsAt: snapshotNow.addingTimeInterval(2 * 24 * 60 * 60),
resetDescription: nil),
secondaryLimit: nil,
creditsRemaining: nil,
accountPlan: nil,
updatedAt: snapshotNow)
store.backfillCodexHistoricalFromDashboardIfNeeded(
dashboard,
authorityDecision: CodexDashboardAuthorityDecision(
disposition: .attach,
reason: .trustedEmailMatchNoCompetingOwner,
allowedEffects: [.historicalBackfill],
cleanup: []),
attachedAccountEmail: "attached@example.com")
for _ in 0..<40 {
if (store.codexHistoricalDataset?.weeks.count ?? 0) >= 3 {
break
}
try await Task.sleep(for: .milliseconds(50))
}
#expect((store.codexHistoricalDataset?.weeks.count ?? 0) >= 3)
}
@MainActor
@Test
func `backfill uses attached account email from authority instead of dashboard email`() async throws {
let store = try Self.makeUsageStoreForBackfillTests(
suite: "HistoricalUsagePaceTests-backfill-attached-email",
historyFileURL: Self.makeTempURL())
let isolatedHome = FileManager.default.temporaryDirectory
.appendingPathComponent("codex-historical-attached-email-\(UUID().uuidString)", isDirectory: true)
try FileManager.default.createDirectory(at: isolatedHome, withIntermediateDirectories: true)
store.settings._test_liveSystemCodexAccount = nil
store.settings._test_codexReconciliationEnvironment = ["CODEX_HOME": isolatedHome.path]
defer {
store.settings._test_codexReconciliationEnvironment = nil
try? FileManager.default.removeItem(at: isolatedHome)
}
store._setCodexHistoricalDatasetForTesting(nil)
let snapshotNow = Date(timeIntervalSince1970: 1_770_000_000)
let dashboard = OpenAIDashboardSnapshot(
signedInEmail: "dashboard@example.com",
codeReviewRemainingPercent: nil,
creditEvents: [],
dailyBreakdown: [],
usageBreakdown: Self.syntheticBreakdown(endingAt: snapshotNow, days: 35, dailyCredits: 10),
creditsPurchaseURL: nil,
primaryLimit: nil,
secondaryLimit: RateWindow(
usedPercent: 50,
windowMinutes: 10080,
resetsAt: snapshotNow.addingTimeInterval(2 * 24 * 60 * 60),
resetDescription: nil),
creditsRemaining: nil,
accountPlan: nil,
updatedAt: snapshotNow)
store.backfillCodexHistoricalFromDashboardIfNeeded(
dashboard,
authorityDecision: CodexDashboardAuthorityDecision(
disposition: .attach,
reason: .trustedEmailMatchNoCompetingOwner,
allowedEffects: [.historicalBackfill],
cleanup: []),
attachedAccountEmail: "attached@example.com")
for _ in 0..<40 {
if store.codexHistoricalDatasetAccountKey != nil {
break
}
try await Task.sleep(for: .milliseconds(50))
}
#expect(
store.codexHistoricalDatasetAccountKey ==
CodexHistoryOwnership.canonicalEmailHashKey(for: "attached@example.com"))
#expect(
store.codexHistoricalDatasetAccountKey !=
CodexHistoryOwnership.canonicalEmailHashKey(for: "dashboard@example.com"))
}
@Test
func `will last decision uses smoothed probability when risk hidden`() throws {
let now = Date(timeIntervalSince1970: 0)
let windowMinutes = 10080
let duration = TimeInterval(windowMinutes) * 60
let currentResetsAt = now.addingTimeInterval(duration / 2)
let window = RateWindow(
usedPercent: 50,
windowMinutes: windowMinutes,
resetsAt: currentResetsAt,
resetDescription: nil)
let weeks = (0..<4).map { index in
HistoricalWeekProfile(
resetsAt: currentResetsAt.addingTimeInterval(-duration * Double(index + 1)),
windowMinutes: windowMinutes,
curve: Self.linearCurve(end: 100))
}
let pace = try #require(CodexHistoricalPaceEvaluator.evaluate(
window: window,
now: now,
dataset: CodexHistoricalDataset(weeks: weeks)))
#expect(pace.runOutProbability == nil)
let totalWeight = weeks.enumerated().reduce(0.0) { partial, element in
let ageWeeks = currentResetsAt.timeIntervalSince(element.element.resetsAt) / duration
return partial + exp(-ageWeeks / 3.0)
}
let smoothedProbability = (totalWeight + 0.5) / (totalWeight + 1.0)
#expect(pace.willLastToReset == (smoothedProbability < 0.5))
}
@MainActor
@Test
func `usage store falls back to linear when history disabled or insufficient`() throws {
let suite = "HistoricalUsagePaceTests-usage-store"
let defaults = try #require(UserDefaults(suiteName: suite))
defaults.removePersistentDomain(forName: suite)
let settings = SettingsStore(
userDefaults: defaults,
configStore: testConfigStore(suiteName: suite),
zaiTokenStore: NoopZaiTokenStore(),
syntheticTokenStore: NoopSyntheticTokenStore(),
codexCookieStore: InMemoryCookieHeaderStore(),
claudeCookieStore: InMemoryCookieHeaderStore(),
cursorCookieStore: InMemoryCookieHeaderStore(),
opencodeCookieStore: InMemoryCookieHeaderStore(),
factoryCookieStore: InMemoryCookieHeaderStore(),
minimaxCookieStore: InMemoryMiniMaxCookieStore(),
minimaxAPITokenStore: InMemoryMiniMaxAPITokenStore(),
kimiTokenStore: InMemoryKimiTokenStore(),
kimiK2TokenStore: InMemoryKimiK2TokenStore(),
augmentCookieStore: InMemoryCookieHeaderStore(),
ampCookieStore: InMemoryCookieHeaderStore(),
copilotTokenStore: InMemoryCopilotTokenStore(),
tokenAccountStore: InMemoryTokenAccountStore())
settings.historicalTrackingEnabled = true
settings.weeklyProgressWorkDays = 5
let planHistoryStore = testPlanUtilizationHistoryStore(
suiteName: "HistoricalUsagePaceTests-\(UUID().uuidString)")
let store = UsageStore(
fetcher: UsageFetcher(environment: [:]),
browserDetection: BrowserDetection(cacheTTL: 0),
settings: settings,
historicalUsageHistoryStore: HistoricalUsageHistoryStore(fileURL: Self.makeTempURL()),
planUtilizationHistoryStore: planHistoryStore)
store._cancelPlanUtilizationHistoryLoadForTesting()
let now = Date(timeIntervalSince1970: 0)
let window = RateWindow(
usedPercent: 50,
windowMinutes: 10080,
resetsAt: now.addingTimeInterval(4 * 24 * 60 * 60),
resetDescription: nil)
let twoWeeksDataset = CodexHistoricalDataset(weeks: [
HistoricalWeekProfile(
resetsAt: now.addingTimeInterval(-7 * 24 * 60 * 60),
windowMinutes: 10080,
curve: Self.linearCurve(end: 100)),
HistoricalWeekProfile(
resetsAt: now.addingTimeInterval(-14 * 24 * 60 * 60),
windowMinutes: 10080,
curve: Self.linearCurve(end: 100)),
])
store._setCodexHistoricalDatasetForTesting(twoWeeksDataset)
let computed = store.weeklyPace(provider: .codex, window: window, now: now)
let workdayAware = UsagePace.weekly(
window: window,
now: now,
defaultWindowMinutes: 10080,
workDays: 5)
#expect(computed != nil)
#expect(abs((computed?.deltaPercent ?? 0) - (workdayAware?.deltaPercent ?? 0)) < 0.001)
}
@MainActor
@Test
func `usage store preserves historical Codex pace when work days are off`() throws {
let suite = "HistoricalUsagePaceTests-workdays-off-preserve-history-\(UUID().uuidString)"
let store = try Self.makeUsageStoreForBackfillTests(
suite: suite,
historyFileURL: Self.makeTempURL())
store.settings.weeklyProgressWorkDays = nil
let now = Date(timeIntervalSince1970: 0)
let duration = TimeInterval(10080 * 60)
let resetsAt = now.addingTimeInterval(duration / 2)
let window = RateWindow(
usedPercent: 60,
windowMinutes: 10080,
resetsAt: resetsAt,
resetDescription: nil)
let dataset = CodexHistoricalDataset(weeks: (0..<4).map { index in
HistoricalWeekProfile(
resetsAt: resetsAt.addingTimeInterval(-duration * Double(index + 1)),
windowMinutes: 10080,
curve: Self.linearCurve(end: 80))
})
let expected = try #require(CodexHistoricalPaceEvaluator.evaluate(
window: window,
now: now,
dataset: dataset))
let linear = try #require(UsagePace.weekly(window: window, now: now, workDays: nil))
store._setCodexHistoricalDatasetForTesting(
dataset,
accountKey: store.codexOwnershipContext().canonicalKey)
let computed = try #require(store.weeklyPace(provider: .codex, window: window, now: now))
#expect(abs(expected.expectedUsedPercent - linear.expectedUsedPercent) > 0.001)
#expect(abs(computed.expectedUsedPercent - expected.expectedUsedPercent) < 0.001)
#expect(abs(computed.deltaPercent - expected.deltaPercent) < 0.001)
#expect(computed.etaSeconds == expected.etaSeconds)
#expect(computed.willLastToReset == expected.willLastToReset)
#expect(computed.runOutProbability == expected.runOutProbability)
}
@MainActor
@Test
func `usage store preserves historical Codex pace when work days are configured`() throws {
let suite = "HistoricalUsagePaceTests-workdays-preserve-history-\(UUID().uuidString)"
let store = try Self.makeUsageStoreForBackfillTests(
suite: suite,
historyFileURL: Self.makeTempURL())
store.settings.weeklyProgressWorkDays = 5
let now = Date(timeIntervalSince1970: 0)
let duration = TimeInterval(10080 * 60)
let resetsAt = now.addingTimeInterval(duration / 2)
let window = RateWindow(
usedPercent: 60,
windowMinutes: 10080,
resetsAt: resetsAt,
resetDescription: nil)
let weeks = (0..<4).map { index in
HistoricalWeekProfile(
resetsAt: resetsAt.addingTimeInterval(-duration * Double(index + 1)),
windowMinutes: 10080,
curve: Self.linearCurve(end: 80))
}
let dataset = CodexHistoricalDataset(weeks: weeks)
let expected = try #require(CodexHistoricalPaceEvaluator.evaluate(
window: window,
now: now,
dataset: dataset))
store._setCodexHistoricalDatasetForTesting(
dataset,
accountKey: store.codexOwnershipContext().canonicalKey)
let computed = try #require(store.weeklyPace(provider: .codex, window: window, now: now))
#expect(abs(computed.expectedUsedPercent - expected.expectedUsedPercent) < 0.001)
#expect(abs(computed.deltaPercent - expected.deltaPercent) < 0.001)
#expect(computed.etaSeconds == expected.etaSeconds)
#expect(computed.willLastToReset == expected.willLastToReset)
#expect(computed.runOutProbability == expected.runOutProbability)
}
@MainActor
@Test
func `usage store computes linear pace for providers with quota windows`() throws {
let suite = "HistoricalUsagePaceTests-generic-provider-\(UUID().uuidString)"
let store = try Self.makeUsageStoreForBackfillTests(
suite: suite,
historyFileURL: Self.makeTempURL())
let now = Date(timeIntervalSince1970: 0)
let window = RateWindow(
usedPercent: 40,
windowMinutes: 10080,
resetsAt: now.addingTimeInterval(4 * 24 * 60 * 60),
resetDescription: nil)
let pace = store.weeklyPace(provider: .zai, window: window, now: now)
#expect(pace != nil)
#expect(abs((pace?.deltaPercent ?? 0) - (40 - (3.0 / 7.0 * 100.0))) < 0.001)
}
@MainActor
@Test
func `usage store applies configured work days to generic weekly pace`() throws {
let suite = "HistoricalUsagePaceTests-generic-workdays-\(UUID().uuidString)"
let store = try Self.makeUsageStoreForBackfillTests(
suite: suite,
historyFileURL: Self.makeTempURL())
store.settings.weeklyProgressWorkDays = 5
var calendar = Calendar(identifier: .gregorian)
calendar.timeZone = .current
let resetsAt = try #require(calendar.date(from: DateComponents(
calendar: calendar,
timeZone: calendar.timeZone,
year: 2026,
month: 6,
day: 14)))
let now = try #require(calendar.date(from: DateComponents(
calendar: calendar,
timeZone: calendar.timeZone,
year: 2026,
month: 6,
day: 11)))
let window = RateWindow(
usedPercent: 60,
windowMinutes: 10080,
resetsAt: resetsAt,
resetDescription: nil)
let pace = try #require(store.weeklyPace(provider: .zai, window: window, now: now))
#expect(abs(pace.expectedUsedPercent - 60) < 0.001)
#expect(abs(pace.deltaPercent) < 0.001)
}
@MainActor
@Test
func `usage store returns nil pace when generic window lacks explicit duration`() throws {
let suite = "HistoricalUsagePaceTests-no-window-minutes-\(UUID().uuidString)"
let store = try Self.makeUsageStoreForBackfillTests(
suite: suite,
historyFileURL: Self.makeTempURL())
let now = Date(timeIntervalSince1970: 0)
let window = RateWindow(
usedPercent: 40,
windowMinutes: nil,
resetsAt: now.addingTimeInterval(4 * 24 * 60 * 60),
resetDescription: nil)
let pace = store.weeklyPace(provider: .factory, window: window, now: now)
#expect(pace == nil)
}
}