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

852 lines
36 KiB
Swift

import Foundation
import Testing
@testable import CodexBar
@testable import CodexBarCore
@Suite(.serialized)
@MainActor
struct CodexManagedRoutingTests {
@Test
func `provider registry injects managed home when active source is managed account`() {
let settings = self.makeSettingsStore(suite: "CodexManagedRoutingTests-registry")
let managedAccount = ManagedCodexAccount(
id: UUID(),
email: "managed@example.com",
managedHomePath: "/tmp/codex-managed-home",
createdAt: 1,
updatedAt: 1,
lastAuthenticatedAt: 1)
settings._test_activeManagedCodexAccount = managedAccount
settings.codexActiveSource = .managedAccount(id: managedAccount.id)
defer {
settings._test_activeManagedCodexAccount = nil
}
let codexEnv = ProviderRegistry.makeEnvironment(
base: ["PATH": "/usr/bin"],
provider: .codex,
settings: settings,
tokenOverride: nil)
let claudeEnv = ProviderRegistry.makeEnvironment(
base: ["PATH": "/usr/bin"],
provider: .claude,
settings: settings,
tokenOverride: nil)
#expect(codexEnv["CODEX_HOME"] == managedAccount.managedHomePath)
#expect(claudeEnv["CODEX_HOME"] == nil)
}
@Test
func `provider registry scopes codex environment with source override without persisting selection`() throws {
let settings = self.makeSettingsStore(suite: "CodexManagedRoutingTests-source-override-env")
let managedAccount = ManagedCodexAccount(
id: UUID(),
email: "managed@example.com",
managedHomePath: "/tmp/codex-managed-override-home",
createdAt: 1,
updatedAt: 1,
lastAuthenticatedAt: 1)
let storeURL = FileManager.default.temporaryDirectory
.appendingPathComponent("codex-managed-override-\(UUID().uuidString).json")
let store = FileManagedCodexAccountStore(fileURL: storeURL)
try store.storeAccounts(ManagedCodexAccountSet(
version: FileManagedCodexAccountStore.currentVersion,
accounts: [managedAccount]))
settings._test_managedCodexAccountStoreURL = storeURL
settings.codexActiveSource = .liveSystem
defer {
settings._test_managedCodexAccountStoreURL = nil
try? FileManager.default.removeItem(at: storeURL)
}
let env = ProviderRegistry.makeEnvironment(
base: ["CODEX_HOME": "/Users/example/.codex"],
provider: .codex,
settings: settings,
tokenOverride: nil,
codexActiveSourceOverride: .managedAccount(id: managedAccount.id))
#expect(env["CODEX_HOME"] == managedAccount.managedHomePath)
#expect(settings.codexActiveSource == .liveSystem)
}
@Test
func `provider registry builds codex snapshot with source override without persisting selection`() throws {
let settings = self.makeSettingsStore(suite: "CodexManagedRoutingTests-source-override-snapshot")
let managedAccount = ManagedCodexAccount(
id: UUID(),
email: "managed@example.com",
managedHomePath: "/tmp/codex-managed-override-home",
createdAt: 1,
updatedAt: 1,
lastAuthenticatedAt: 1)
let storeURL = FileManager.default.temporaryDirectory
.appendingPathComponent("codex-managed-snapshot-override-\(UUID().uuidString).json")
let store = FileManagedCodexAccountStore(fileURL: storeURL)
try store.storeAccounts(ManagedCodexAccountSet(
version: FileManagedCodexAccountStore.currentVersion,
accounts: [managedAccount]))
settings._test_managedCodexAccountStoreURL = storeURL
settings.codexActiveSource = .liveSystem
defer {
settings._test_managedCodexAccountStoreURL = nil
try? FileManager.default.removeItem(at: storeURL)
}
let snapshot = ProviderRegistry.makeSettingsSnapshot(
settings: settings,
tokenOverride: nil,
codexActiveSourceOverride: .managedAccount(id: UUID()))
#expect(snapshot.codex?.managedAccountTargetUnavailable == true)
#expect(settings.codexActiveSource == .liveSystem)
}
@Test
func `provider registry preserves ambient live system home when active source is live system`() {
let settings = self.makeSettingsStore(suite: "CodexManagedRoutingTests-live-system-routing")
let managedHomePath = "/tmp/managed-remote-home"
let liveHomePath = "/tmp/system-remote-home"
let managedAccount = ManagedCodexAccount(
id: UUID(),
email: "managed@example.com",
managedHomePath: managedHomePath,
createdAt: 1,
updatedAt: 1,
lastAuthenticatedAt: 1)
let liveSystemAccount = ObservedSystemCodexAccount(
email: "system@example.com",
codexHomePath: liveHomePath,
observedAt: Date())
settings._test_activeManagedCodexAccount = managedAccount
settings._test_liveSystemCodexAccount = liveSystemAccount
settings.codexActiveSource = .liveSystem
defer {
settings._test_activeManagedCodexAccount = nil
settings._test_liveSystemCodexAccount = nil
}
let env = ProviderRegistry.makeEnvironment(
base: ["CODEX_HOME": liveHomePath],
provider: .codex,
settings: settings,
tokenOverride: nil)
#expect(env["CODEX_HOME"] == liveHomePath)
#expect(env["CODEX_HOME"] != managedHomePath)
}
@Test
func `provider registry keeps managed home when live account differs`() {
let settings = self.makeSettingsStore(suite: "CodexManagedRoutingTests-active-vs-live")
let managedHomePath = "/tmp/managed-remote-home"
let liveHomePath = "/tmp/system-remote-home"
let managedAccount = ManagedCodexAccount(
id: UUID(),
email: "managed@example.com",
managedHomePath: managedHomePath,
createdAt: 1,
updatedAt: 1,
lastAuthenticatedAt: 1)
let liveSystemAccount = ObservedSystemCodexAccount(
email: "system@example.com",
codexHomePath: liveHomePath,
observedAt: Date())
settings._test_activeManagedCodexAccount = managedAccount
settings._test_liveSystemCodexAccount = liveSystemAccount
settings.codexActiveSource = .managedAccount(id: managedAccount.id)
defer {
settings._test_activeManagedCodexAccount = nil
settings._test_liveSystemCodexAccount = nil
}
let env = ProviderRegistry.makeEnvironment(
base: ["CODEX_HOME": liveHomePath],
provider: .codex,
settings: settings,
tokenOverride: nil)
#expect(env["CODEX_HOME"] == managedHomePath)
#expect(env["CODEX_HOME"] != liveHomePath)
}
@Test
func `provider registry prefers live system routing when managed and live share email`() {
let settings = self.makeSettingsStore(suite: "CodexManagedRoutingTests-same-email-prefers-live")
let managedHome = FileManager.default.temporaryDirectory.appendingPathComponent(
UUID().uuidString,
isDirectory: true)
let liveHomePath = "/tmp/system-remote-home"
defer { try? FileManager.default.removeItem(at: managedHome) }
let managedAccount = ManagedCodexAccount(
id: UUID(),
email: "person@example.com",
managedHomePath: managedHome.path,
createdAt: 1,
updatedAt: 1,
lastAuthenticatedAt: 1)
let liveSystemAccount = ObservedSystemCodexAccount(
email: "PERSON@example.com",
codexHomePath: liveHomePath,
observedAt: Date())
settings._test_activeManagedCodexAccount = managedAccount
settings._test_liveSystemCodexAccount = liveSystemAccount
settings.codexActiveSource = .managedAccount(id: managedAccount.id)
defer {
settings._test_activeManagedCodexAccount = nil
settings._test_liveSystemCodexAccount = nil
}
let env = ProviderRegistry.makeEnvironment(
base: ["CODEX_HOME": liveHomePath],
provider: .codex,
settings: settings,
tokenOverride: nil)
#expect(settings.codexResolvedActiveSource == .liveSystem)
#expect(env["CODEX_HOME"] == liveHomePath)
#expect(env["CODEX_HOME"] != managedHome.path)
}
@Test
func `provider registry keeps managed routing when same email rows differ by identity strength`() throws {
let settings = self.makeSettingsStore(suite: "CodexManagedRoutingTests-same-email-split-by-identity")
let managedHome = FileManager.default.temporaryDirectory.appendingPathComponent(
UUID().uuidString,
isDirectory: true)
let liveHomePath = "/tmp/system-remote-home"
defer { try? FileManager.default.removeItem(at: managedHome) }
try self.writeCodexAuthFile(
homeURL: managedHome,
email: "person@example.com",
plan: "pro",
accountId: "account-managed")
let managedAccount = ManagedCodexAccount(
id: UUID(),
email: "person@example.com",
managedHomePath: managedHome.path,
createdAt: 1,
updatedAt: 1,
lastAuthenticatedAt: 1)
let liveSystemAccount = ObservedSystemCodexAccount(
email: "PERSON@example.com",
codexHomePath: liveHomePath,
observedAt: Date(),
identity: .emailOnly(normalizedEmail: "person@example.com"))
settings._test_activeManagedCodexAccount = managedAccount
settings._test_liveSystemCodexAccount = liveSystemAccount
settings.codexActiveSource = .managedAccount(id: managedAccount.id)
defer {
settings._test_activeManagedCodexAccount = nil
settings._test_liveSystemCodexAccount = nil
}
let env = ProviderRegistry.makeEnvironment(
base: ["CODEX_HOME": liveHomePath],
provider: .codex,
settings: settings,
tokenOverride: nil)
#expect(settings.codexResolvedActiveSource == .managedAccount(id: managedAccount.id))
#expect(env["CODEX_HOME"] == managedHome.path)
#expect(env["CODEX_HOME"] != liveHomePath)
}
@Test
func `persisted managed source corrects to live system when selected row collapses with live account`() {
let settings = self.makeSettingsStore(suite: "CodexManagedRoutingTests-same-email-persist-correction")
let managedAccount = ManagedCodexAccount(
id: UUID(),
email: "person@example.com",
managedHomePath: "/tmp/managed-remote-home",
createdAt: 1,
updatedAt: 1,
lastAuthenticatedAt: 1)
let liveSystemAccount = ObservedSystemCodexAccount(
email: "PERSON@example.com",
codexHomePath: "/tmp/system-remote-home",
observedAt: Date())
settings._test_activeManagedCodexAccount = managedAccount
settings._test_liveSystemCodexAccount = liveSystemAccount
settings.codexActiveSource = .managedAccount(id: managedAccount.id)
defer {
settings._test_activeManagedCodexAccount = nil
settings._test_liveSystemCodexAccount = nil
}
let corrected = settings.persistResolvedCodexActiveSourceCorrectionIfNeeded()
#expect(corrected)
#expect(settings.codexActiveSource == .liveSystem)
}
@Test
func `codex provider refresh persists live correction for stale managed source`() async {
let settings = self.makeSettingsStore(suite: "CodexManagedRoutingTests-provider-refresh-persists-correction")
let ambientHome = FileManager.default.temporaryDirectory.appendingPathComponent(
UUID().uuidString,
isDirectory: true)
defer { try? FileManager.default.removeItem(at: ambientHome) }
try? self.writeCodexAuthFile(homeURL: ambientHome, email: "live@example.com", plan: "pro")
settings._test_liveSystemCodexAccount = ObservedSystemCodexAccount(
email: "live@example.com",
codexHomePath: ambientHome.path,
observedAt: Date())
settings.codexActiveSource = .managedAccount(id: UUID())
defer { settings._test_liveSystemCodexAccount = nil }
let store = UsageStore(
fetcher: UsageFetcher(environment: ["CODEX_HOME": ambientHome.path]),
browserDetection: BrowserDetection(cacheTTL: 0),
settings: settings,
startupBehavior: .testing)
#expect(settings.codexActiveSource != .liveSystem)
await store.refreshProvider(.codex, allowDisabled: true)
#expect(settings.codexActiveSource == .liveSystem)
}
@Test
func `full refresh persists live correction for stale managed source`() async {
let settings = self.makeSettingsStore(suite: "CodexManagedRoutingTests-full-refresh-persists-correction")
let ambientHome = FileManager.default.temporaryDirectory.appendingPathComponent(
UUID().uuidString,
isDirectory: true)
defer { try? FileManager.default.removeItem(at: ambientHome) }
try? self.writeCodexAuthFile(homeURL: ambientHome, email: "live@example.com", plan: "pro")
settings._test_liveSystemCodexAccount = ObservedSystemCodexAccount(
email: "live@example.com",
codexHomePath: ambientHome.path,
observedAt: Date())
settings.codexActiveSource = .managedAccount(id: UUID())
defer { settings._test_liveSystemCodexAccount = nil }
let store = UsageStore(
fetcher: UsageFetcher(environment: ["CODEX_HOME": ambientHome.path]),
browserDetection: BrowserDetection(cacheTTL: 0),
settings: settings,
startupBehavior: .testing)
#expect(settings.codexActiveSource != .liveSystem)
await store.refresh()
#expect(settings.codexActiveSource == .liveSystem)
}
@Test
func `provider registry fails closed when managed account store is unreadable`() {
let settings = self.makeSettingsStore(suite: "CodexManagedRoutingTests-unreadable-store")
settings._test_unreadableManagedCodexAccountStore = true
settings.codexActiveSource = .managedAccount(id: UUID())
defer { settings._test_unreadableManagedCodexAccountStore = false }
let env = ProviderRegistry.makeEnvironment(
base: ["CODEX_HOME": "/Users/example/.codex"],
provider: .codex,
settings: settings,
tokenOverride: nil)
#expect(env["CODEX_HOME"] != nil)
#expect(env["CODEX_HOME"] != "/Users/example/.codex")
#expect(env["CODEX_HOME"]?.isEmpty == false)
}
@Test
func `provider registry bootstraps live system source instead of inferring managed fallback`() {
let settings = self.makeSettingsStore(suite: "CodexManagedRoutingTests-unreadable-legacy-source")
settings._test_unreadableManagedCodexAccountStore = true
defer { settings._test_unreadableManagedCodexAccountStore = false }
let ambientHome = "/Users/example/.codex"
let env = ProviderRegistry.makeEnvironment(
base: ["CODEX_HOME": ambientHome],
provider: .codex,
settings: settings,
tokenOverride: nil)
let snapshot = settings.codexSettingsSnapshot(tokenOverride: nil)
#expect(env["CODEX_HOME"] == ambientHome)
#expect(settings.providerConfig(for: .codex)?.codexActiveSource == nil)
#expect(snapshot.managedAccountStoreUnreadable == false)
#expect(snapshot.managedAccountTargetUnavailable == false)
}
@Test
func `provider registry fails closed when selected managed source is missing from readable store`() throws {
let settings = self.makeSettingsStore(suite: "CodexManagedRoutingTests-missing-managed-source")
let storedAccount = ManagedCodexAccount(
id: UUID(),
email: "stored@example.com",
managedHomePath: "/tmp/stored-managed-home",
createdAt: 1,
updatedAt: 1,
lastAuthenticatedAt: 1)
let storeURL = FileManager.default.temporaryDirectory
.appendingPathComponent("codex-managed-routing-\(UUID().uuidString).json")
let store = FileManagedCodexAccountStore(fileURL: storeURL)
try store.storeAccounts(ManagedCodexAccountSet(
version: FileManagedCodexAccountStore.currentVersion,
accounts: [storedAccount]))
settings._test_managedCodexAccountStoreURL = storeURL
settings.codexActiveSource = .managedAccount(id: UUID())
settings._test_codexReconciliationEnvironment = ["CODEX_HOME": "/Users/example/.codex"]
defer {
settings._test_codexReconciliationEnvironment = nil
settings._test_managedCodexAccountStoreURL = nil
try? FileManager.default.removeItem(at: storeURL)
}
let ambientHome = "/Users/example/.codex"
let expectedFailClosedPath = ManagedCodexHomeFactory.defaultRootURL()
.appendingPathComponent("managed-store-unreadable", isDirectory: true)
.path
let env = ProviderRegistry.makeEnvironment(
base: ["CODEX_HOME": ambientHome],
provider: .codex,
settings: settings,
tokenOverride: nil)
#expect(env["CODEX_HOME"] == expectedFailClosedPath)
#expect(env["CODEX_HOME"] != ambientHome)
#expect(env["CODEX_HOME"] != storedAccount.managedHomePath)
}
@Test
func `codex settings snapshot marks missing selected managed source as unavailable`() throws {
let settings = self.makeSettingsStore(suite: "CodexManagedRoutingTests-missing-managed-snapshot")
let storedAccount = ManagedCodexAccount(
id: UUID(),
email: "stored@example.com",
managedHomePath: "/tmp/stored-managed-home",
createdAt: 1,
updatedAt: 1,
lastAuthenticatedAt: 1)
let storeURL = FileManager.default.temporaryDirectory
.appendingPathComponent("codex-managed-snapshot-\(UUID().uuidString).json")
let store = FileManagedCodexAccountStore(fileURL: storeURL)
try store.storeAccounts(ManagedCodexAccountSet(
version: FileManagedCodexAccountStore.currentVersion,
accounts: [storedAccount]))
settings._test_managedCodexAccountStoreURL = storeURL
settings.codexActiveSource = .managedAccount(id: UUID())
defer {
settings._test_managedCodexAccountStoreURL = nil
try? FileManager.default.removeItem(at: storeURL)
}
let snapshot = settings.codexSettingsSnapshot(tokenOverride: nil)
#expect(snapshot.managedAccountStoreUnreadable == false)
#expect(snapshot.managedAccountTargetUnavailable == true)
}
@Test
func `codex settings snapshot ignores unreadable added account store when live system is active`() {
let settings = self.makeSettingsStore(suite: "CodexManagedRoutingTests-live-system-snapshot")
settings._test_unreadableManagedCodexAccountStore = true
settings.codexActiveSource = .liveSystem
defer { settings._test_unreadableManagedCodexAccountStore = false }
let snapshot = settings.codexSettingsSnapshot(tokenOverride: nil)
#expect(snapshot.managedAccountStoreUnreadable == false)
#expect(snapshot.managedAccountTargetUnavailable == false)
}
@Test
func `codex settings snapshot keeps unreadable managed store fail closed when live account is present`() {
let settings = self.makeSettingsStore(suite: "CodexManagedRoutingTests-unreadable-store-live-present")
settings._test_unreadableManagedCodexAccountStore = true
settings.codexActiveSource = .managedAccount(id: UUID())
settings._test_liveSystemCodexAccount = ObservedSystemCodexAccount(
email: "live@example.com",
codexHomePath: "/Users/example/.codex",
observedAt: Date(),
identity: .providerAccount(id: "acct-live"))
defer {
settings._test_unreadableManagedCodexAccountStore = false
settings._test_liveSystemCodexAccount = nil
}
let snapshot = settings.codexSettingsSnapshot(tokenOverride: nil)
#expect(snapshot.managedAccountStoreUnreadable == true)
#expect(snapshot.managedAccountTargetUnavailable == false)
}
@Test
func `codex settings snapshot keeps missing managed target fail closed when live account is present`() throws {
let settings = self.makeSettingsStore(suite: "CodexManagedRoutingTests-missing-managed-live-present")
let storedAccount = ManagedCodexAccount(
id: UUID(),
email: "stored@example.com",
managedHomePath: "/tmp/stored-managed-home",
createdAt: 1,
updatedAt: 1,
lastAuthenticatedAt: 1)
let storeURL = FileManager.default.temporaryDirectory
.appendingPathComponent("codex-managed-live-present-\(UUID().uuidString).json")
let store = FileManagedCodexAccountStore(fileURL: storeURL)
try store.storeAccounts(ManagedCodexAccountSet(
version: FileManagedCodexAccountStore.currentVersion,
accounts: [storedAccount]))
settings._test_managedCodexAccountStoreURL = storeURL
settings.codexActiveSource = .managedAccount(id: UUID())
settings._test_liveSystemCodexAccount = ObservedSystemCodexAccount(
email: "live@example.com",
codexHomePath: "/Users/example/.codex",
observedAt: Date(),
identity: .providerAccount(id: "acct-live"))
defer {
settings._test_managedCodexAccountStoreURL = nil
settings._test_liveSystemCodexAccount = nil
try? FileManager.default.removeItem(at: storeURL)
}
let snapshot = settings.codexSettingsSnapshot(tokenOverride: nil)
#expect(snapshot.managedAccountStoreUnreadable == false)
#expect(snapshot.managedAccountTargetUnavailable == true)
}
@Test
func `provider registry ignores debug managed home override without explicit managed source`() throws {
let settings = self.makeSettingsStore(suite: "CodexManagedRoutingTests-debug-home-override")
let managedHome = FileManager.default.temporaryDirectory.appendingPathComponent(
UUID().uuidString,
isDirectory: true)
defer { try? FileManager.default.removeItem(at: managedHome) }
settings._test_activeManagedCodexRemoteHomePath = managedHome.path
defer { settings._test_activeManagedCodexRemoteHomePath = nil }
try self.writeCodexAuthFile(homeURL: managedHome, email: "managed@example.com", plan: "pro")
let ambientHome = "/Users/example/.codex"
let env = ProviderRegistry.makeEnvironment(
base: ["CODEX_HOME": ambientHome],
provider: .codex,
settings: settings,
tokenOverride: nil)
#expect(env["CODEX_HOME"] == ambientHome)
#expect(settings.providerConfig(for: .codex)?.codexActiveSource == nil)
}
@Test
func `provider registry builds codex fetcher scoped to managed home`() throws {
let settings = self.makeSettingsStore(suite: "CodexManagedRoutingTests-registry-fetcher")
let managedHome = FileManager.default.temporaryDirectory.appendingPathComponent(
UUID().uuidString,
isDirectory: true)
defer { try? FileManager.default.removeItem(at: managedHome) }
settings._test_activeManagedCodexRemoteHomePath = managedHome.path
settings.codexActiveSource = .managedAccount(id: UUID())
try self.writeCodexAuthFile(homeURL: managedHome, email: "managed@example.com", plan: "pro")
defer {
settings._test_activeManagedCodexRemoteHomePath = nil
}
let browserDetection = BrowserDetection(cacheTTL: 0)
let specs = ProviderRegistry.shared.specs(
settings: settings,
metadata: ProviderDescriptorRegistry.metadata,
codexFetcher: UsageFetcher(environment: [:]),
claudeFetcher: ClaudeUsageFetcher(browserDetection: browserDetection),
browserDetection: browserDetection)
let context = try #require(specs[.codex]?.makeFetchContext())
let account = context.fetcher.loadAccountInfo()
#expect(account.email == "managed@example.com")
#expect(account.plan == "pro")
}
@Test
func `usage store builds codex fetch context with source override without persisting selection`() throws {
let settings = self.makeSettingsStore(suite: "CodexManagedRoutingTests-usage-source-override")
let managedHome = FileManager.default.temporaryDirectory.appendingPathComponent(
UUID().uuidString,
isDirectory: true)
defer { try? FileManager.default.removeItem(at: managedHome) }
let managedAccount = ManagedCodexAccount(
id: UUID(),
email: "managed@example.com",
managedHomePath: managedHome.path,
createdAt: 1,
updatedAt: 1,
lastAuthenticatedAt: 1)
let storeURL = FileManager.default.temporaryDirectory
.appendingPathComponent("codex-managed-usage-override-\(UUID().uuidString).json")
let managedStore = FileManagedCodexAccountStore(fileURL: storeURL)
try managedStore.storeAccounts(ManagedCodexAccountSet(
version: FileManagedCodexAccountStore.currentVersion,
accounts: [managedAccount]))
try self.writeCodexAuthFile(homeURL: managedHome, email: "override@example.com", plan: "pro")
settings._test_managedCodexAccountStoreURL = storeURL
settings.codexActiveSource = .liveSystem
defer {
settings._test_managedCodexAccountStoreURL = nil
try? FileManager.default.removeItem(at: storeURL)
}
let store = UsageStore(
fetcher: UsageFetcher(environment: [:]),
browserDetection: BrowserDetection(cacheTTL: 0),
settings: settings,
startupBehavior: .testing)
let context = store.makeFetchContext(
provider: .codex,
override: nil,
codexActiveSourceOverride: .managedAccount(id: managedAccount.id))
let account = context.fetcher.loadAccountInfo()
#expect(account.email == "override@example.com")
#expect(account.plan == "pro")
#expect(settings.codexActiveSource == .liveSystem)
}
@Test
func `usage store builds codex token account fetcher scoped to managed home`() throws {
let settings = self.makeSettingsStore(suite: "CodexManagedRoutingTests-usage-store")
let managedHome = FileManager.default.temporaryDirectory.appendingPathComponent(
UUID().uuidString,
isDirectory: true)
defer { try? FileManager.default.removeItem(at: managedHome) }
settings._test_activeManagedCodexRemoteHomePath = managedHome.path
settings.codexActiveSource = .managedAccount(id: UUID())
try self.writeCodexAuthFile(homeURL: managedHome, email: "token@example.com", plan: "team")
defer {
settings._test_activeManagedCodexRemoteHomePath = nil
}
let store = UsageStore(
fetcher: UsageFetcher(environment: [:]),
browserDetection: BrowserDetection(cacheTTL: 0),
settings: settings,
startupBehavior: .testing)
let context = store.makeFetchContext(provider: .codex, override: nil)
let account = context.fetcher.loadAccountInfo()
#expect(account.email == "token@example.com")
#expect(account.plan == "team")
}
@Test
func `usage store builds codex credits fetcher scoped to managed home`() throws {
let settings = self.makeSettingsStore(suite: "CodexManagedRoutingTests-credits-fetcher")
let managedHome = FileManager.default.temporaryDirectory.appendingPathComponent(
UUID().uuidString,
isDirectory: true)
defer { try? FileManager.default.removeItem(at: managedHome) }
settings._test_activeManagedCodexRemoteHomePath = managedHome.path
settings.codexActiveSource = .managedAccount(id: UUID())
try self.writeCodexAuthFile(homeURL: managedHome, email: "credits@example.com", plan: "enterprise")
defer {
settings._test_activeManagedCodexRemoteHomePath = nil
}
let store = UsageStore(
fetcher: UsageFetcher(environment: [:]),
browserDetection: BrowserDetection(cacheTTL: 0),
settings: settings,
startupBehavior: .testing)
let account = store.codexCreditsFetcher().loadAccountInfo()
#expect(account.email == "credits@example.com")
#expect(account.plan == "enterprise")
}
@Test
func `default managed codex identity reader preserves provider account from scoped auth`() throws {
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: "managed-account-id")
let reader = DefaultManagedCodexIdentityReader()
let account = try reader.loadAccountIdentity(homePath: managedHome.path)
#expect(account.email == "managed@example.com")
#expect(account.plan == "pro")
#expect(account.identity == .providerAccount(id: "managed-account-id"))
}
@Test
func `codex O auth strategy availability reads auth from context env`() async throws {
let managedHome = FileManager.default.temporaryDirectory.appendingPathComponent(
UUID().uuidString,
isDirectory: true)
defer { try? FileManager.default.removeItem(at: managedHome) }
let credentials = CodexOAuthCredentials(
accessToken: "access-token",
refreshToken: "refresh-token",
idToken: nil,
accountId: nil,
lastRefresh: Date())
try CodexOAuthCredentialsStore.save(credentials, env: ["CODEX_HOME": managedHome.path])
let strategy = CodexOAuthFetchStrategy()
let available = await strategy.isAvailable(self.makeContext(env: ["CODEX_HOME": managedHome.path]))
#expect(available)
}
@Test
func `codex O auth credentials store loads and saves using explicit env`() throws {
let managedHome = FileManager.default.temporaryDirectory.appendingPathComponent(
UUID().uuidString,
isDirectory: true)
defer { try? FileManager.default.removeItem(at: managedHome) }
let credentials = CodexOAuthCredentials(
accessToken: "access-token",
refreshToken: "refresh-token",
idToken: "id-token",
accountId: "account-id",
lastRefresh: Date())
let env = ["CODEX_HOME": managedHome.path]
try CodexOAuthCredentialsStore.save(credentials, env: env)
let authURL = CodexOAuthCredentialsStore._authFileURLForTesting(env: env)
#expect(authURL.path == managedHome.appendingPathComponent("auth.json").path)
let loaded = try CodexOAuthCredentialsStore.load(env: env)
#expect(loaded.accessToken == credentials.accessToken)
#expect(loaded.refreshToken == credentials.refreshToken)
#expect(loaded.idToken == credentials.idToken)
#expect(loaded.accountId == credentials.accountId)
}
@Test
func `codex no data message uses explicit environment home`() {
let env = ["CODEX_HOME": "/tmp/managed-codex-home"]
let message = CodexProviderDescriptor._noDataMessageForTesting(env: env)
#expect(message.contains("/tmp/managed-codex-home/sessions"))
#expect(message.contains("/tmp/managed-codex-home/archived_sessions"))
}
private func makeContext(env: [String: String]) -> ProviderFetchContext {
let browserDetection = BrowserDetection(cacheTTL: 0)
return ProviderFetchContext(
runtime: .app,
sourceMode: .auto,
includeCredits: false,
webTimeout: 60,
webDebugDumpHTML: false,
verbose: false,
env: env,
settings: nil,
fetcher: UsageFetcher(),
claudeFetcher: ClaudeUsageFetcher(browserDetection: browserDetection),
browserDetection: browserDetection)
}
private func makeSettingsStore(suite: String) -> SettingsStore {
let defaults = UserDefaults(suiteName: suite)!
defaults.removePersistentDomain(forName: suite)
let configStore = testConfigStore(suiteName: suite)
return SettingsStore(
userDefaults: defaults,
configStore: configStore,
zaiTokenStore: InMemoryZaiTokenStore(),
syntheticTokenStore: InMemorySyntheticTokenStore(),
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())
}
private func writeCodexAuthFile(
homeURL: URL,
email: String,
plan: String,
accountId: String? = nil) throws
{
try FileManager.default.createDirectory(at: homeURL, withIntermediateDirectories: true)
var tokens: [String: Any] = [
"accessToken": "access-token",
"refreshToken": "refresh-token",
"idToken": Self.fakeJWT(email: email, plan: plan, accountId: accountId),
]
if let accountId {
tokens["accountId"] = accountId
}
let auth = ["tokens": tokens]
let data = try JSONSerialization.data(withJSONObject: auth)
try data.write(to: homeURL.appendingPathComponent("auth.json"))
}
private static func fakeJWT(email: String, plan: String, accountId: String? = nil) -> String {
let header = (try? JSONSerialization.data(withJSONObject: ["alg": "none"])) ?? Data()
var authClaims: [String: Any] = [
"chatgpt_plan_type": plan,
]
if let accountId {
authClaims["chatgpt_account_id"] = accountId
}
let payload = (try? JSONSerialization.data(withJSONObject: [
"email": email,
"chatgpt_plan_type": plan,
"https://api.openai.com/auth": authClaims,
])) ?? Data()
func base64URL(_ data: Data) -> String {
data.base64EncodedString()
.replacingOccurrences(of: "=", with: "")
.replacingOccurrences(of: "+", with: "-")
.replacingOccurrences(of: "/", with: "_")
}
return "\(base64URL(header)).\(base64URL(payload))."
}
}
private final class InMemoryZaiTokenStore: ZaiTokenStoring, @unchecked Sendable {
func loadToken() throws -> String? {
nil
}
func storeToken(_: String?) throws {}
}
private final class InMemorySyntheticTokenStore: SyntheticTokenStoring, @unchecked Sendable {
func loadToken() throws -> String? {
nil
}
func storeToken(_: String?) throws {}
}