274 lines
13 KiB
Swift
274 lines
13 KiB
Swift
import CodexBarCore
|
|
import Foundation
|
|
import Testing
|
|
@testable import CodexBar
|
|
|
|
extension CodexAccountScopedRefreshTests {
|
|
@Test
|
|
func `dashboard refresh accepted via unresolved routing fallback during account scoped refresh`() async throws {
|
|
let settings = self.makeSettingsStore(
|
|
suite: "CodexAccountScopedRefreshTests-dashboard-unresolved-routing-fallback")
|
|
let isolatedHome = FileManager.default.temporaryDirectory
|
|
.appendingPathComponent("codex-dashboard-unresolved-routing-\(UUID().uuidString)", isDirectory: true)
|
|
try? FileManager.default.createDirectory(at: isolatedHome, withIntermediateDirectories: true)
|
|
let codexMetadata = try #require(ProviderDescriptorRegistry.metadata[.codex])
|
|
settings.refreshFrequency = .manual
|
|
settings.setProviderEnabled(provider: .codex, metadata: codexMetadata, enabled: true)
|
|
settings.openAIWebAccessEnabled = true
|
|
settings.codexCookieSource = .auto
|
|
settings.codexActiveSource = .liveSystem
|
|
settings._test_liveSystemCodexAccount = nil
|
|
settings._test_codexReconciliationEnvironment = ["CODEX_HOME": isolatedHome.path]
|
|
defer {
|
|
settings._test_codexReconciliationEnvironment = nil
|
|
try? FileManager.default.removeItem(at: isolatedHome)
|
|
}
|
|
|
|
let store = self.makeUsageStore(settings: settings)
|
|
store.lastKnownLiveSystemCodexEmail = nil
|
|
self.installImmediateCodexProvider(
|
|
on: store,
|
|
snapshot: self.codexSnapshot(email: "work@company.com", usedPercent: 12))
|
|
store._test_codexCreditsLoaderOverride = { self.credits(remaining: 55) }
|
|
defer { store._test_codexCreditsLoaderOverride = nil }
|
|
|
|
var observedTargetEmail: String?
|
|
store._test_openAIDashboardLoaderOverride = { accountEmail, _, _, _ in
|
|
observedTargetEmail = accountEmail
|
|
#expect(store.currentCodexOpenAIWebRefreshGuard().source == .liveSystem)
|
|
#expect(store.currentCodexOpenAIWebRefreshGuard().identity == .unresolved)
|
|
return self.dashboard(email: "work@company.com", creditsRemaining: 33, usedPercent: 12)
|
|
}
|
|
defer { store._test_openAIDashboardLoaderOverride = nil }
|
|
|
|
await store.refreshCodexAccountScopedState(allowDisabled: true)
|
|
|
|
#expect(observedTargetEmail == "work@company.com")
|
|
#expect(store.currentCodexOpenAIWebRefreshGuard().identity == .unresolved)
|
|
#expect(store.openAIDashboard?.signedInEmail == "work@company.com")
|
|
#expect(store.lastOpenAIDashboardSnapshot?.signedInEmail == "work@company.com")
|
|
#expect(store.openAIDashboardRequiresLogin == false)
|
|
#expect(store.lastOpenAIDashboardError == nil)
|
|
#expect(store.snapshots[.codex]?.accountEmail(for: .codex) == "work@company.com")
|
|
#expect(store.lastSourceLabels[.codex] == "test-codex")
|
|
#expect(store.credits?.remaining == 55)
|
|
#expect(store.lastCreditsSource == .api)
|
|
}
|
|
|
|
@Test
|
|
func `dashboard fail closed clears dashboard derived usage credits cache and visible dashboard`() async throws {
|
|
OpenAIDashboardCacheStore.clear()
|
|
defer { OpenAIDashboardCacheStore.clear() }
|
|
|
|
let settings = self.makeSettingsStore(suite: "CodexAccountScopedRefreshTests-dashboard-fail-closed-cleanup")
|
|
let managedHome = FileManager.default.temporaryDirectory
|
|
.appendingPathComponent(UUID().uuidString, isDirectory: true)
|
|
defer { try? FileManager.default.removeItem(at: managedHome) }
|
|
try Self.writeCodexAuthFile(
|
|
homeURL: managedHome,
|
|
email: "managed@example.com",
|
|
plan: "pro",
|
|
accountId: "acct-managed")
|
|
|
|
let managedAccount = ManagedCodexAccount(
|
|
id: UUID(),
|
|
email: "managed@example.com",
|
|
managedHomePath: managedHome.path,
|
|
createdAt: 1,
|
|
updatedAt: 1,
|
|
lastAuthenticatedAt: 1)
|
|
let managedStoreURL = try self.makeManagedAccountStoreURL(accounts: [managedAccount])
|
|
defer {
|
|
settings._test_managedCodexAccountStoreURL = nil
|
|
settings._test_activeManagedCodexAccount = nil
|
|
try? FileManager.default.removeItem(at: managedStoreURL)
|
|
}
|
|
|
|
settings.refreshFrequency = .manual
|
|
settings.codexCookieSource = .auto
|
|
settings._test_managedCodexAccountStoreURL = managedStoreURL
|
|
settings._test_activeManagedCodexAccount = managedAccount
|
|
settings.codexActiveSource = .managedAccount(id: managedAccount.id)
|
|
|
|
let store = self.makeUsageStore(settings: settings)
|
|
store._setSnapshotForTesting(
|
|
self.codexSnapshot(email: "managed@example.com", usedPercent: 20),
|
|
provider: .codex)
|
|
store.lastSourceLabels[.codex] = "openai-web"
|
|
let staleCredits = self.credits(remaining: 20)
|
|
store.credits = staleCredits
|
|
store.lastCreditsSnapshot = staleCredits
|
|
store.lastCreditsSnapshotAccountKey = "managed@example.com"
|
|
store.lastCreditsSource = .dashboardWeb
|
|
store.openAIDashboard = self.dashboard(email: "managed@example.com", creditsRemaining: 20, usedPercent: 20)
|
|
store.lastOpenAIDashboardSnapshot = store.openAIDashboard
|
|
OpenAIDashboardCacheStore.save(OpenAIDashboardCache(
|
|
accountEmail: "managed@example.com",
|
|
snapshot: self.dashboard(email: "managed@example.com", creditsRemaining: 20, usedPercent: 20)))
|
|
|
|
await store.applyOpenAIDashboard(
|
|
self.dashboard(email: "other@example.com", creditsRemaining: 9, usedPercent: 35),
|
|
targetEmail: "managed@example.com")
|
|
|
|
#expect(store.openAIDashboard == nil)
|
|
#expect(store.lastOpenAIDashboardSnapshot == nil)
|
|
#expect(store.snapshots[.codex] == nil)
|
|
#expect(store.credits == nil)
|
|
#expect(store.lastCreditsSource == .none)
|
|
#expect(OpenAIDashboardCacheStore.load() == nil)
|
|
#expect(store.openAIDashboardRequiresLogin == true)
|
|
#expect(store.lastOpenAIDashboardError?.contains("OpenAI dashboard signed in as other@example.com") == true)
|
|
}
|
|
|
|
@Test
|
|
func `dashboard fail closed cleanup applies after same account managed token rotation`() async throws {
|
|
OpenAIDashboardCacheStore.clear()
|
|
defer { OpenAIDashboardCacheStore.clear() }
|
|
|
|
let settings = self.makeSettingsStore(
|
|
suite: "CodexAccountScopedRefreshTests-dashboard-fail-closed-token-rotation-cleanup")
|
|
let managedHome = FileManager.default.temporaryDirectory
|
|
.appendingPathComponent(UUID().uuidString, isDirectory: true)
|
|
defer { try? FileManager.default.removeItem(at: managedHome) }
|
|
try Self.writeCodexAuthFile(
|
|
homeURL: managedHome,
|
|
email: "managed@example.com",
|
|
plan: "pro",
|
|
accountId: "acct-managed")
|
|
let oldFingerprint = try #require(CodexAuthFingerprint.fingerprint(homePath: managedHome.path))
|
|
|
|
let managedAccount = ManagedCodexAccount(
|
|
id: UUID(),
|
|
email: "managed@example.com",
|
|
providerAccountID: "acct-managed",
|
|
workspaceLabel: "Managed",
|
|
workspaceAccountID: "acct-managed",
|
|
authFingerprint: oldFingerprint,
|
|
managedHomePath: managedHome.path,
|
|
createdAt: 1,
|
|
updatedAt: 1,
|
|
lastAuthenticatedAt: 1)
|
|
let managedStoreURL = try self.makeManagedAccountStoreURL(accounts: [managedAccount])
|
|
defer {
|
|
settings._test_managedCodexAccountStoreURL = nil
|
|
settings._test_activeManagedCodexAccount = nil
|
|
try? FileManager.default.removeItem(at: managedStoreURL)
|
|
}
|
|
|
|
settings.refreshFrequency = .manual
|
|
settings.codexCookieSource = .auto
|
|
settings._test_managedCodexAccountStoreURL = managedStoreURL
|
|
settings._test_activeManagedCodexAccount = managedAccount
|
|
settings.codexActiveSource = .managedAccount(id: managedAccount.id)
|
|
|
|
let store = self.makeUsageStore(settings: settings)
|
|
let expectedGuard = store.freshCodexOpenAIWebRefreshGuard()
|
|
#expect(expectedGuard.authFingerprint == oldFingerprint)
|
|
store._setSnapshotForTesting(
|
|
self.codexSnapshot(email: "managed@example.com", usedPercent: 20),
|
|
provider: .codex)
|
|
store.lastSourceLabels[.codex] = "openai-web"
|
|
let staleCredits = self.credits(remaining: 20)
|
|
store.credits = staleCredits
|
|
store.lastCreditsSnapshot = staleCredits
|
|
store.lastCreditsSnapshotAccountKey = "managed@example.com"
|
|
store.lastCreditsSource = .dashboardWeb
|
|
store.openAIDashboard = self.dashboard(email: "managed@example.com", creditsRemaining: 20, usedPercent: 20)
|
|
store.lastOpenAIDashboardSnapshot = store.openAIDashboard
|
|
OpenAIDashboardCacheStore.save(OpenAIDashboardCache(
|
|
accountEmail: "managed@example.com",
|
|
snapshot: self.dashboard(email: "managed@example.com", creditsRemaining: 20, usedPercent: 20)))
|
|
|
|
try Self.writeCodexAuthFile(
|
|
homeURL: managedHome,
|
|
email: "managed@example.com",
|
|
plan: "team",
|
|
accountId: "acct-managed")
|
|
let newFingerprint = try #require(CodexAuthFingerprint.fingerprint(homePath: managedHome.path))
|
|
#expect(newFingerprint != oldFingerprint)
|
|
let currentGuard = store.freshCodexOpenAIWebRefreshGuard()
|
|
#expect(currentGuard.identity == expectedGuard.identity)
|
|
#expect(currentGuard.accountKey == expectedGuard.accountKey)
|
|
#expect(currentGuard.authFingerprint == newFingerprint)
|
|
|
|
await store.applyOpenAIDashboard(
|
|
self.dashboard(email: "other@example.com", creditsRemaining: 9, usedPercent: 35),
|
|
targetEmail: "managed@example.com",
|
|
expectedGuard: expectedGuard)
|
|
|
|
#expect(store.openAIDashboard == nil)
|
|
#expect(store.lastOpenAIDashboardSnapshot == nil)
|
|
#expect(store.snapshots[.codex] == nil)
|
|
#expect(store.credits == nil)
|
|
#expect(store.lastCreditsSource == .none)
|
|
#expect(OpenAIDashboardCacheStore.load() == nil)
|
|
#expect(store.openAIDashboardRequiresLogin == true)
|
|
#expect(store.lastOpenAIDashboardError?.contains("OpenAI dashboard signed in as other@example.com") == true)
|
|
}
|
|
|
|
@Test
|
|
func `dashboard fail closed cleanup applies after same live account email changes during token rotation`() async {
|
|
OpenAIDashboardCacheStore.clear()
|
|
defer { OpenAIDashboardCacheStore.clear() }
|
|
|
|
let settings = self.makeSettingsStore(
|
|
suite: "CodexAccountScopedRefreshTests-dashboard-fail-closed-live-email-rotation-cleanup")
|
|
settings.refreshFrequency = .manual
|
|
settings.codexCookieSource = .auto
|
|
settings.codexActiveSource = .liveSystem
|
|
settings._test_liveSystemCodexAccount = ObservedSystemCodexAccount(
|
|
email: "alpha@example.com",
|
|
authFingerprint: "old-token-material",
|
|
codexHomePath: "/Users/test/.codex",
|
|
observedAt: Date(),
|
|
identity: .providerAccount(id: "acct-alpha"))
|
|
defer {
|
|
settings._test_liveSystemCodexAccount = nil
|
|
}
|
|
|
|
let store = self.makeUsageStore(settings: settings)
|
|
let expectedGuard = store.freshCodexOpenAIWebRefreshGuard()
|
|
#expect(expectedGuard.accountKey == "alpha@example.com")
|
|
#expect(expectedGuard.authFingerprint == "old-token-material")
|
|
store._setSnapshotForTesting(
|
|
self.codexSnapshot(email: "alpha@example.com", usedPercent: 20),
|
|
provider: .codex)
|
|
store.lastSourceLabels[.codex] = "openai-web"
|
|
let staleCredits = self.credits(remaining: 20)
|
|
store.credits = staleCredits
|
|
store.lastCreditsSnapshot = staleCredits
|
|
store.lastCreditsSnapshotAccountKey = "alpha@example.com"
|
|
store.lastCreditsSource = .dashboardWeb
|
|
store.openAIDashboard = self.dashboard(email: "alpha@example.com", creditsRemaining: 20, usedPercent: 20)
|
|
store.lastOpenAIDashboardSnapshot = store.openAIDashboard
|
|
OpenAIDashboardCacheStore.save(OpenAIDashboardCache(
|
|
accountEmail: "alpha@example.com",
|
|
snapshot: self.dashboard(email: "alpha@example.com", creditsRemaining: 20, usedPercent: 20)))
|
|
|
|
settings._test_liveSystemCodexAccount = ObservedSystemCodexAccount(
|
|
email: "beta@example.com",
|
|
authFingerprint: "new-token-material",
|
|
codexHomePath: "/Users/test/.codex",
|
|
observedAt: Date(),
|
|
identity: .providerAccount(id: "acct-alpha"))
|
|
let currentGuard = store.freshCodexOpenAIWebRefreshGuard()
|
|
#expect(currentGuard.identity == expectedGuard.identity)
|
|
#expect(currentGuard.accountKey == "beta@example.com")
|
|
#expect(currentGuard.authFingerprint == "new-token-material")
|
|
|
|
await store.applyOpenAIDashboard(
|
|
self.dashboard(email: "alpha@example.com", creditsRemaining: 9, usedPercent: 35),
|
|
targetEmail: "alpha@example.com",
|
|
expectedGuard: expectedGuard)
|
|
|
|
#expect(store.openAIDashboard == nil)
|
|
#expect(store.lastOpenAIDashboardSnapshot == nil)
|
|
#expect(store.snapshots[.codex] == nil)
|
|
#expect(store.credits == nil)
|
|
#expect(store.lastCreditsSource == .none)
|
|
#expect(OpenAIDashboardCacheStore.load() == nil)
|
|
#expect(store.openAIDashboardRequiresLogin == true)
|
|
#expect(store.lastOpenAIDashboardError?.contains("OpenAI dashboard signed in as alpha@example.com") == true)
|
|
}
|
|
}
|