882 lines
39 KiB
Swift
882 lines
39 KiB
Swift
import Foundation
|
|
import Testing
|
|
@testable import CodexBar
|
|
@testable import CodexBarCore
|
|
|
|
@Suite(.serialized)
|
|
@MainActor
|
|
struct CodexManagedOpenAIWebTests {
|
|
@Test
|
|
func `managed codex open A I web uses active managed identity and cache scope`() {
|
|
KeychainCacheStore.setTestStoreForTesting(true)
|
|
defer { KeychainCacheStore.setTestStoreForTesting(false) }
|
|
|
|
let settings = self.makeSettingsStore(suite: "CodexManagedOpenAIWebTests-managed")
|
|
let managedAccount = ManagedCodexAccount(
|
|
id: UUID(),
|
|
email: "managed@example.com",
|
|
managedHomePath: "/tmp/managed-codex-home",
|
|
createdAt: 1,
|
|
updatedAt: 1,
|
|
lastAuthenticatedAt: 1)
|
|
settings._test_activeManagedCodexAccount = managedAccount
|
|
settings.codexActiveSource = .managedAccount(id: managedAccount.id)
|
|
defer { settings._test_activeManagedCodexAccount = nil }
|
|
|
|
let otherAccountID = UUID()
|
|
CookieHeaderCache.store(
|
|
provider: .codex,
|
|
scope: .managedAccount(otherAccountID),
|
|
cookieHeader: "auth=other-account",
|
|
sourceLabel: "Chrome")
|
|
CookieHeaderCache.store(
|
|
provider: .codex,
|
|
cookieHeader: "auth=provider-global",
|
|
sourceLabel: "Safari")
|
|
defer {
|
|
CookieHeaderCache.clear(provider: .codex, scope: .managedAccount(otherAccountID))
|
|
CookieHeaderCache.clear(provider: .codex)
|
|
}
|
|
|
|
let store = UsageStore(
|
|
fetcher: UsageFetcher(environment: [:]),
|
|
browserDetection: BrowserDetection(cacheTTL: 0),
|
|
settings: settings,
|
|
startupBehavior: .testing)
|
|
|
|
#expect(store.codexAccountEmailForOpenAIDashboard() == "managed@example.com")
|
|
#expect(store.codexCookieCacheScopeForOpenAIWeb() == .managedAccount(managedAccount.id))
|
|
#expect(CookieHeaderCache.load(provider: .codex, scope: store.codexCookieCacheScopeForOpenAIWeb()) == nil)
|
|
}
|
|
|
|
@Test
|
|
func `managed codex open A I web targets runtime auth backed email for selected account`() throws {
|
|
let settings = self.makeSettingsStore(suite: "CodexManagedOpenAIWebTests-managed-runtime-email")
|
|
let managedHome = FileManager.default.temporaryDirectory
|
|
.appendingPathComponent(UUID().uuidString, isDirectory: true)
|
|
defer { try? FileManager.default.removeItem(at: managedHome) }
|
|
try Self.writeCodexAuthFile(
|
|
homeURL: managedHome,
|
|
email: "renamed@example.com",
|
|
plan: "pro",
|
|
accountId: "acct-managed")
|
|
|
|
let managedAccount = ManagedCodexAccount(
|
|
id: UUID(),
|
|
email: "legacy@example.com",
|
|
managedHomePath: managedHome.path,
|
|
createdAt: 1,
|
|
updatedAt: 1,
|
|
lastAuthenticatedAt: 1)
|
|
settings._test_activeManagedCodexAccount = managedAccount
|
|
settings.codexActiveSource = .managedAccount(id: managedAccount.id)
|
|
defer { settings._test_activeManagedCodexAccount = nil }
|
|
|
|
let store = UsageStore(
|
|
fetcher: UsageFetcher(environment: [:]),
|
|
browserDetection: BrowserDetection(cacheTTL: 0),
|
|
settings: settings,
|
|
startupBehavior: .testing)
|
|
|
|
#expect(store.codexAccountEmailForOpenAIDashboard() == "renamed@example.com")
|
|
#expect(store.codexAccountEmailForOpenAIDashboard() != managedAccount.email)
|
|
#expect(store.currentCodexOpenAIWebRefreshGuard().accountKey == "renamed@example.com")
|
|
#expect(store.currentCodexOpenAIWebRefreshGuard().identity == .providerAccount(id: "acct-managed"))
|
|
}
|
|
|
|
@Test
|
|
func `live system codex open A I web uses live identity and no managed cache scope`() {
|
|
let settings = self.makeSettingsStore(suite: "CodexManagedOpenAIWebTests-live-system")
|
|
let managedAccount = ManagedCodexAccount(
|
|
id: UUID(),
|
|
email: "managed@example.com",
|
|
managedHomePath: "/tmp/managed-codex-home",
|
|
createdAt: 1,
|
|
updatedAt: 1,
|
|
lastAuthenticatedAt: 1)
|
|
let liveAccount = ObservedSystemCodexAccount(
|
|
email: "system@example.com",
|
|
codexHomePath: "/tmp/live-codex-home",
|
|
observedAt: Date())
|
|
settings._test_activeManagedCodexAccount = managedAccount
|
|
settings._test_liveSystemCodexAccount = liveAccount
|
|
settings.codexActiveSource = .liveSystem
|
|
defer {
|
|
settings._test_activeManagedCodexAccount = nil
|
|
settings._test_liveSystemCodexAccount = nil
|
|
}
|
|
|
|
let store = UsageStore(
|
|
fetcher: UsageFetcher(environment: ["CODEX_HOME": liveAccount.codexHomePath]),
|
|
browserDetection: BrowserDetection(cacheTTL: 0),
|
|
settings: settings,
|
|
startupBehavior: .testing)
|
|
|
|
#expect(store.codexAccountEmailForOpenAIDashboard() == liveAccount.email)
|
|
#expect(store.codexAccountEmailForOpenAIDashboard() != managedAccount.email)
|
|
#expect(store.codexCookieCacheScopeForOpenAIWeb() == nil)
|
|
}
|
|
|
|
@Test
|
|
func `live system codex open A I web does not reuse stale managed snapshot email after source switch`() {
|
|
let settings = self.makeSettingsStore(suite: "CodexManagedOpenAIWebTests-live-system-stale-managed-snapshot")
|
|
let isolatedHome = FileManager.default.temporaryDirectory
|
|
.appendingPathComponent("codex-openai-web-empty-\(UUID().uuidString)", isDirectory: true)
|
|
try? FileManager.default.createDirectory(at: isolatedHome, withIntermediateDirectories: true)
|
|
let managedAccount = ManagedCodexAccount(
|
|
id: UUID(),
|
|
email: "managed@example.com",
|
|
managedHomePath: "/tmp/managed-codex-home",
|
|
createdAt: 1,
|
|
updatedAt: 1,
|
|
lastAuthenticatedAt: 1)
|
|
|
|
settings._test_activeManagedCodexAccount = managedAccount
|
|
settings._test_codexReconciliationEnvironment = ["CODEX_HOME": isolatedHome.path]
|
|
settings.codexActiveSource = .liveSystem
|
|
defer {
|
|
settings._test_activeManagedCodexAccount = nil
|
|
settings._test_codexReconciliationEnvironment = nil
|
|
try? FileManager.default.removeItem(at: isolatedHome)
|
|
}
|
|
|
|
let store = UsageStore(
|
|
fetcher: UsageFetcher(environment: [:]),
|
|
browserDetection: BrowserDetection(cacheTTL: 0),
|
|
settings: settings,
|
|
startupBehavior: .testing)
|
|
store._setSnapshotForTesting(
|
|
UsageSnapshot(
|
|
primary: nil,
|
|
secondary: nil,
|
|
updatedAt: Date(),
|
|
identity: ProviderIdentitySnapshot(
|
|
providerID: .codex,
|
|
accountEmail: managedAccount.email,
|
|
accountOrganization: nil,
|
|
loginMethod: nil)),
|
|
provider: .codex)
|
|
|
|
#expect(store.codexAccountEmailForOpenAIDashboard() == nil)
|
|
#expect(store.codexAccountEmailForOpenAIDashboard() != managedAccount.email)
|
|
#expect(store.codexCookieCacheScopeForOpenAIWeb() == nil)
|
|
}
|
|
|
|
@Test
|
|
func `live system codex open A I web reuses last known live email without allowing any account`() async {
|
|
let settings = self.makeSettingsStore(suite: "CodexManagedOpenAIWebTests-live-system-last-known-email")
|
|
let isolatedHome = FileManager.default.temporaryDirectory
|
|
.appendingPathComponent("codex-openai-web-last-known-\(UUID().uuidString)", isDirectory: true)
|
|
try? FileManager.default.createDirectory(at: isolatedHome, withIntermediateDirectories: true)
|
|
let liveAccount = ObservedSystemCodexAccount(
|
|
email: "live@example.com",
|
|
codexHomePath: "/tmp/live-codex-home",
|
|
observedAt: Date())
|
|
settings._test_liveSystemCodexAccount = liveAccount
|
|
settings._test_codexReconciliationEnvironment = ["CODEX_HOME": isolatedHome.path]
|
|
settings.codexActiveSource = .liveSystem
|
|
|
|
let store = UsageStore(
|
|
fetcher: UsageFetcher(environment: [:]),
|
|
browserDetection: BrowserDetection(cacheTTL: 0),
|
|
settings: settings,
|
|
startupBehavior: .testing)
|
|
|
|
#expect(store.codexAccountEmailForOpenAIDashboard() == liveAccount.email)
|
|
|
|
settings._test_liveSystemCodexAccount = nil
|
|
defer {
|
|
settings._test_liveSystemCodexAccount = nil
|
|
settings._test_codexReconciliationEnvironment = nil
|
|
try? FileManager.default.removeItem(at: isolatedHome)
|
|
}
|
|
|
|
store.openAIDashboard = OpenAIDashboardSnapshot(
|
|
signedInEmail: "managed@example.com",
|
|
codeReviewRemainingPercent: 100,
|
|
creditEvents: [],
|
|
dailyBreakdown: [],
|
|
usageBreakdown: [],
|
|
creditsPurchaseURL: nil,
|
|
updatedAt: Date())
|
|
store.lastOpenAIDashboardCookieImportEmail = "managed-import@example.com"
|
|
|
|
var observedTargetEmail: String?
|
|
var observedAllowAnyAccount: Bool?
|
|
store._test_openAIDashboardCookieImportOverride = { targetEmail, allowAnyAccount, _, _, _ in
|
|
observedTargetEmail = targetEmail
|
|
observedAllowAnyAccount = allowAnyAccount
|
|
return OpenAIDashboardBrowserCookieImporter.ImportResult(
|
|
sourceLabel: "test",
|
|
cookieCount: 1,
|
|
signedInEmail: targetEmail,
|
|
matchesCodexEmail: true)
|
|
}
|
|
defer { store._test_openAIDashboardCookieImportOverride = nil }
|
|
|
|
let targetEmail = store.codexAccountEmailForOpenAIDashboard()
|
|
let imported = await store.importOpenAIDashboardCookiesIfNeeded(targetEmail: targetEmail, force: true)
|
|
|
|
#expect(targetEmail == liveAccount.email)
|
|
#expect(targetEmail != "managed@example.com")
|
|
#expect(targetEmail != "managed-import@example.com")
|
|
#expect(imported == liveAccount.email)
|
|
#expect(observedTargetEmail == liveAccount.email)
|
|
#expect(observedAllowAnyAccount == false)
|
|
}
|
|
|
|
@Test
|
|
func `successful dashboard apply preserves cached open A I web view for same account`() async {
|
|
let settings = self.makeSettingsStore(suite: "CodexManagedOpenAIWebTests-preserve-cache-on-success")
|
|
let managedAccount = ManagedCodexAccount(
|
|
id: UUID(),
|
|
email: "managed@example.com",
|
|
managedHomePath: "/tmp/managed-codex-home",
|
|
createdAt: 1,
|
|
updatedAt: 1,
|
|
lastAuthenticatedAt: 1)
|
|
settings._test_activeManagedCodexAccount = managedAccount
|
|
settings.codexActiveSource = .managedAccount(id: managedAccount.id)
|
|
defer { settings._test_activeManagedCodexAccount = nil }
|
|
|
|
OpenAIDashboardWebsiteDataStore.clearCacheForTesting()
|
|
let cache = OpenAIDashboardWebViewCache.shared
|
|
cache.clearAllForTesting()
|
|
defer {
|
|
cache.clearAllForTesting()
|
|
OpenAIDashboardWebsiteDataStore.clearCacheForTesting()
|
|
}
|
|
|
|
let store = UsageStore(
|
|
fetcher: UsageFetcher(environment: [:]),
|
|
browserDetection: BrowserDetection(cacheTTL: 0),
|
|
settings: settings,
|
|
startupBehavior: .testing)
|
|
let websiteDataStore = OpenAIDashboardWebsiteDataStore.store(forAccountEmail: managedAccount.email)
|
|
cache.cacheEntryForTesting(websiteDataStore: websiteDataStore)
|
|
|
|
#expect(cache.hasCachedEntry(for: websiteDataStore))
|
|
|
|
await store.applyOpenAIDashboard(
|
|
OpenAIDashboardSnapshot(
|
|
signedInEmail: managedAccount.email,
|
|
codeReviewRemainingPercent: 90,
|
|
creditEvents: [],
|
|
dailyBreakdown: [],
|
|
usageBreakdown: [],
|
|
creditsPurchaseURL: nil,
|
|
creditsRemaining: 10,
|
|
accountPlan: "Pro",
|
|
updatedAt: Date()),
|
|
targetEmail: managedAccount.email)
|
|
|
|
#expect(cache.hasCachedEntry(for: websiteDataStore))
|
|
#expect(cache.entryCount == 1)
|
|
}
|
|
|
|
@Test
|
|
func `dashboard refresh does not target stale last known live email`() async {
|
|
let settings = self.makeSettingsStore(suite: "CodexManagedOpenAIWebTests-live-system-refresh-strict-target")
|
|
let isolatedHome = FileManager.default.temporaryDirectory
|
|
.appendingPathComponent("codex-openai-web-refresh-strict-\(UUID().uuidString)", isDirectory: true)
|
|
try? FileManager.default.createDirectory(at: isolatedHome, withIntermediateDirectories: true)
|
|
let liveAccount = ObservedSystemCodexAccount(
|
|
email: "old@example.com",
|
|
codexHomePath: "/tmp/live-codex-home",
|
|
observedAt: Date())
|
|
settings._test_liveSystemCodexAccount = liveAccount
|
|
settings._test_codexReconciliationEnvironment = ["CODEX_HOME": isolatedHome.path]
|
|
settings.codexActiveSource = .liveSystem
|
|
|
|
let store = UsageStore(
|
|
fetcher: UsageFetcher(environment: [:]),
|
|
browserDetection: BrowserDetection(cacheTTL: 0),
|
|
settings: settings,
|
|
startupBehavior: .testing)
|
|
|
|
#expect(store.codexAccountEmailForOpenAIDashboard() == liveAccount.email)
|
|
|
|
settings._test_liveSystemCodexAccount = nil
|
|
defer {
|
|
settings._test_liveSystemCodexAccount = nil
|
|
settings._test_codexReconciliationEnvironment = nil
|
|
try? FileManager.default.removeItem(at: isolatedHome)
|
|
}
|
|
|
|
var observedTargetEmail: String?
|
|
store._test_openAIDashboardLoaderOverride = { accountEmail, _, _, _ in
|
|
observedTargetEmail = accountEmail
|
|
return OpenAIDashboardSnapshot(
|
|
signedInEmail: "new@example.com",
|
|
codeReviewRemainingPercent: 88,
|
|
creditEvents: [],
|
|
dailyBreakdown: [],
|
|
usageBreakdown: [],
|
|
creditsPurchaseURL: nil,
|
|
primaryLimit: RateWindow(usedPercent: 12, windowMinutes: 300, resetsAt: nil, resetDescription: nil),
|
|
secondaryLimit: nil,
|
|
creditsRemaining: 22,
|
|
accountPlan: "Pro",
|
|
updatedAt: Date())
|
|
}
|
|
defer { store._test_openAIDashboardLoaderOverride = nil }
|
|
|
|
let expectedGuard = store.currentCodexOpenAIWebRefreshGuard()
|
|
#expect(expectedGuard.accountKey == nil)
|
|
|
|
await store.refreshOpenAIDashboardIfNeeded(force: true, expectedGuard: expectedGuard)
|
|
|
|
#expect(observedTargetEmail == nil)
|
|
#expect(store.openAIDashboard == nil)
|
|
#expect(store.openAIDashboardRequiresLogin == true)
|
|
#expect(store.lastOpenAIDashboardError?.contains("could not be verified") == true)
|
|
#expect(store.lastKnownLiveSystemCodexEmail == "old@example.com")
|
|
}
|
|
|
|
@Test
|
|
func `dashboard refresh targets usage discovered live email before reconciliation catches up`() async {
|
|
let settings = self.makeSettingsStore(suite: "CodexManagedOpenAIWebTests-live-system-usage-discovered-target")
|
|
let isolatedHome = FileManager.default.temporaryDirectory
|
|
.appendingPathComponent("codex-openai-web-usage-discovered-\(UUID().uuidString)", isDirectory: true)
|
|
try? FileManager.default.createDirectory(at: isolatedHome, withIntermediateDirectories: true)
|
|
settings._test_codexReconciliationEnvironment = ["CODEX_HOME": isolatedHome.path]
|
|
settings.codexActiveSource = .liveSystem
|
|
defer {
|
|
settings._test_codexReconciliationEnvironment = nil
|
|
try? FileManager.default.removeItem(at: isolatedHome)
|
|
}
|
|
|
|
let store = UsageStore(
|
|
fetcher: UsageFetcher(environment: [:]),
|
|
browserDetection: BrowserDetection(cacheTTL: 0),
|
|
settings: settings,
|
|
startupBehavior: .testing)
|
|
store._setSnapshotForTesting(
|
|
UsageSnapshot(
|
|
primary: nil,
|
|
secondary: nil,
|
|
updatedAt: Date(),
|
|
identity: ProviderIdentitySnapshot(
|
|
providerID: .codex,
|
|
accountEmail: "usage@example.com",
|
|
accountOrganization: nil,
|
|
loginMethod: nil)),
|
|
provider: .codex)
|
|
store.lastSourceLabels[.codex] = "codex-cli"
|
|
|
|
var observedTargetEmail: String?
|
|
store._test_openAIDashboardLoaderOverride = { accountEmail, _, _, _ in
|
|
observedTargetEmail = accountEmail
|
|
return OpenAIDashboardSnapshot(
|
|
signedInEmail: "usage@example.com",
|
|
codeReviewRemainingPercent: 88,
|
|
creditEvents: [],
|
|
dailyBreakdown: [],
|
|
usageBreakdown: [],
|
|
creditsPurchaseURL: nil,
|
|
primaryLimit: RateWindow(usedPercent: 12, windowMinutes: 300, resetsAt: nil, resetDescription: nil),
|
|
secondaryLimit: nil,
|
|
creditsRemaining: 22,
|
|
accountPlan: "Pro",
|
|
updatedAt: Date())
|
|
}
|
|
defer { store._test_openAIDashboardLoaderOverride = nil }
|
|
|
|
let expectedGuard = store.currentCodexOpenAIWebRefreshGuard()
|
|
#expect(expectedGuard.accountKey == nil)
|
|
|
|
await store.refreshOpenAIDashboardIfNeeded(force: true, expectedGuard: expectedGuard)
|
|
|
|
#expect(observedTargetEmail == "usage@example.com")
|
|
#expect(store.openAIDashboard?.signedInEmail == "usage@example.com")
|
|
}
|
|
|
|
@Test
|
|
func `usage discovered live email still surfaces open A I web login guidance during reconciliation lag`() async {
|
|
let settings = self.makeSettingsStore(suite: "CodexManagedOpenAIWebTests-live-system-usage-discovered-failure")
|
|
let isolatedHome = FileManager.default.temporaryDirectory
|
|
.appendingPathComponent("codex-openai-web-usage-discovered-failure-\(UUID().uuidString)", isDirectory: true)
|
|
try? FileManager.default.createDirectory(at: isolatedHome, withIntermediateDirectories: true)
|
|
settings._test_codexReconciliationEnvironment = ["CODEX_HOME": isolatedHome.path]
|
|
settings.codexActiveSource = .liveSystem
|
|
defer {
|
|
settings._test_codexReconciliationEnvironment = nil
|
|
try? FileManager.default.removeItem(at: isolatedHome)
|
|
}
|
|
|
|
let store = UsageStore(
|
|
fetcher: UsageFetcher(environment: [:]),
|
|
browserDetection: BrowserDetection(cacheTTL: 0),
|
|
settings: settings,
|
|
startupBehavior: .testing)
|
|
store._setSnapshotForTesting(
|
|
UsageSnapshot(
|
|
primary: nil,
|
|
secondary: nil,
|
|
updatedAt: Date(),
|
|
identity: ProviderIdentitySnapshot(
|
|
providerID: .codex,
|
|
accountEmail: "usage@example.com",
|
|
accountOrganization: nil,
|
|
loginMethod: nil)),
|
|
provider: .codex)
|
|
store.lastSourceLabels[.codex] = "codex-cli"
|
|
|
|
let expectedGuard = store.currentCodexOpenAIWebRefreshGuard()
|
|
#expect(expectedGuard.accountKey == nil)
|
|
|
|
await store.applyOpenAIDashboardLoginRequiredFailure(
|
|
expectedGuard: expectedGuard,
|
|
routingTargetEmail: "usage@example.com")
|
|
|
|
#expect(store.openAIDashboardRequiresLogin == true)
|
|
#expect(store.lastOpenAIDashboardError?.contains("requires a signed-in chatgpt.com session") == true)
|
|
}
|
|
|
|
@Test
|
|
func `open A I web import uses managed account target when live account differs`() async {
|
|
let settings = self.makeSettingsStore(suite: "CodexManagedOpenAIWebTests-targeting-active-vs-live")
|
|
let managedAccount = ManagedCodexAccount(
|
|
id: UUID(),
|
|
email: "managed@example.com",
|
|
managedHomePath: "/tmp/managed-codex-home",
|
|
createdAt: 1,
|
|
updatedAt: 1,
|
|
lastAuthenticatedAt: 1)
|
|
let liveAccount = ObservedSystemCodexAccount(
|
|
email: "system@example.com",
|
|
codexHomePath: "/tmp/live-codex-home",
|
|
observedAt: Date())
|
|
let expectedScope = CookieHeaderCache.Scope.managedAccount(managedAccount.id)
|
|
let expectedEmail = managedAccount.email
|
|
var observedTargetEmail: String?
|
|
var observedScope: CookieHeaderCache.Scope?
|
|
var observedCookieSource: ProviderCookieSource?
|
|
var observedAllowAnyAccount = false
|
|
|
|
settings._test_activeManagedCodexAccount = managedAccount
|
|
settings._test_liveSystemCodexAccount = liveAccount
|
|
settings.codexActiveSource = .managedAccount(id: managedAccount.id)
|
|
defer {
|
|
settings._test_activeManagedCodexAccount = nil
|
|
settings._test_liveSystemCodexAccount = nil
|
|
}
|
|
|
|
let snapshot = UsageSnapshot(
|
|
primary: nil,
|
|
secondary: nil,
|
|
updatedAt: Date(),
|
|
identity: ProviderIdentitySnapshot(
|
|
providerID: .codex,
|
|
accountEmail: liveAccount.email,
|
|
accountOrganization: nil,
|
|
loginMethod: nil))
|
|
|
|
let store = UsageStore(
|
|
fetcher: UsageFetcher(environment: ["CODEX_HOME": liveAccount.codexHomePath]),
|
|
browserDetection: BrowserDetection(cacheTTL: 0),
|
|
settings: settings,
|
|
startupBehavior: .testing)
|
|
store._setSnapshotForTesting(snapshot, provider: .codex)
|
|
store._test_openAIDashboardCookieImportOverride = { targetEmail, allowAnyAccount, cookieSource, scope, _ in
|
|
observedTargetEmail = targetEmail
|
|
observedScope = scope
|
|
observedCookieSource = cookieSource
|
|
observedAllowAnyAccount = allowAnyAccount
|
|
return OpenAIDashboardBrowserCookieImporter.ImportResult(
|
|
sourceLabel: "test",
|
|
cookieCount: 1,
|
|
signedInEmail: targetEmail,
|
|
matchesCodexEmail: targetEmail == expectedEmail)
|
|
}
|
|
defer { store._test_openAIDashboardCookieImportOverride = nil }
|
|
|
|
let importerTarget = store.codexAccountEmailForOpenAIDashboard()
|
|
let imported = await store.importOpenAIDashboardCookiesIfNeeded(targetEmail: importerTarget, force: true)
|
|
|
|
#expect(importerTarget == expectedEmail)
|
|
#expect(importerTarget != liveAccount.email)
|
|
#expect(imported == expectedEmail)
|
|
#expect(observedTargetEmail == expectedEmail)
|
|
#expect(observedScope == expectedScope)
|
|
#expect(observedAllowAnyAccount == false)
|
|
#expect(observedCookieSource == .auto)
|
|
#expect(store.codexCookieCacheScopeForOpenAIWeb() == expectedScope)
|
|
}
|
|
|
|
@Test
|
|
func `open A I web prefers live identity when managed and live share email`() {
|
|
let settings = self.makeSettingsStore(suite: "CodexManagedOpenAIWebTests-same-email-prefers-live")
|
|
let managedAccount = ManagedCodexAccount(
|
|
id: UUID(),
|
|
email: "person@example.com",
|
|
managedHomePath: "/tmp/managed-codex-home",
|
|
createdAt: 1,
|
|
updatedAt: 1,
|
|
lastAuthenticatedAt: 1)
|
|
let liveAccount = ObservedSystemCodexAccount(
|
|
email: "PERSON@example.com",
|
|
codexHomePath: "/tmp/live-codex-home",
|
|
observedAt: Date())
|
|
settings._test_activeManagedCodexAccount = managedAccount
|
|
settings._test_liveSystemCodexAccount = liveAccount
|
|
settings.codexActiveSource = .managedAccount(id: managedAccount.id)
|
|
defer {
|
|
settings._test_activeManagedCodexAccount = nil
|
|
settings._test_liveSystemCodexAccount = nil
|
|
}
|
|
|
|
let store = UsageStore(
|
|
fetcher: UsageFetcher(environment: ["CODEX_HOME": liveAccount.codexHomePath]),
|
|
browserDetection: BrowserDetection(cacheTTL: 0),
|
|
settings: settings,
|
|
startupBehavior: .testing)
|
|
|
|
#expect(settings.codexResolvedActiveSource == .liveSystem)
|
|
#expect(store.codexAccountEmailForOpenAIDashboard() == "person@example.com")
|
|
#expect(store.codexCookieCacheScopeForOpenAIWeb() == nil)
|
|
}
|
|
|
|
@Test
|
|
func `unmanaged codex open A I web falls back to provider global cache scope`() {
|
|
let settings = self.makeSettingsStore(suite: "CodexManagedOpenAIWebTests-unmanaged")
|
|
let store = UsageStore(
|
|
fetcher: UsageFetcher(environment: [:]),
|
|
browserDetection: BrowserDetection(cacheTTL: 0),
|
|
settings: settings,
|
|
startupBehavior: .testing)
|
|
|
|
#expect(store.codexCookieCacheScopeForOpenAIWeb() == nil)
|
|
}
|
|
|
|
@Test
|
|
func `unreadable managed codex store fails closed for open A I web`() async {
|
|
let settings = self.makeSettingsStore(suite: "CodexManagedOpenAIWebTests-unreadable-store")
|
|
settings._test_unreadableManagedCodexAccountStore = true
|
|
settings.codexActiveSource = .managedAccount(id: UUID())
|
|
defer { settings._test_unreadableManagedCodexAccountStore = false }
|
|
|
|
let store = UsageStore(
|
|
fetcher: UsageFetcher(environment: [:]),
|
|
browserDetection: BrowserDetection(cacheTTL: 0),
|
|
settings: settings,
|
|
startupBehavior: .testing)
|
|
|
|
#expect(store.codexCookieCacheScopeForOpenAIWeb() == .managedStoreUnreadable)
|
|
#expect(store.codexAccountEmailForOpenAIDashboard() == nil)
|
|
|
|
let imported = await store.importOpenAIDashboardCookiesIfNeeded(targetEmail: nil, force: true)
|
|
|
|
#expect(imported == nil)
|
|
#expect(store.openAIDashboard == nil)
|
|
#expect(store.openAIDashboardRequiresLogin == true)
|
|
#expect(store.openAIDashboardCookieImportStatus?.contains("Managed Codex account data is unavailable") == true)
|
|
|
|
await store.refreshOpenAIDashboardIfNeeded(force: true)
|
|
|
|
#expect(store.openAIDashboard == nil)
|
|
#expect(store.openAIDashboardRequiresLogin == true)
|
|
#expect(store.lastOpenAIDashboardError?.contains("Managed Codex account data is unavailable") == true)
|
|
}
|
|
|
|
@Test
|
|
func `missing managed codex open A I web target fails closed`() async {
|
|
let settings = self.makeSettingsStore(suite: "CodexManagedOpenAIWebTests-missing-managed-target")
|
|
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-openai-web-\(UUID().uuidString).json")
|
|
let managedStore = FileManagedCodexAccountStore(fileURL: storeURL)
|
|
try? managedStore.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 store = UsageStore(
|
|
fetcher: UsageFetcher(environment: [:]),
|
|
browserDetection: BrowserDetection(cacheTTL: 0),
|
|
settings: settings,
|
|
startupBehavior: .testing)
|
|
var importWasCalled = false
|
|
store._test_openAIDashboardCookieImportOverride = { _, _, _, _, _ in
|
|
importWasCalled = true
|
|
return OpenAIDashboardBrowserCookieImporter.ImportResult(
|
|
sourceLabel: "test",
|
|
cookieCount: 1,
|
|
signedInEmail: "unexpected@example.com",
|
|
matchesCodexEmail: true)
|
|
}
|
|
defer { store._test_openAIDashboardCookieImportOverride = nil }
|
|
store.openAIDashboard = OpenAIDashboardSnapshot(
|
|
signedInEmail: "stale-dashboard@example.com",
|
|
codeReviewRemainingPercent: 100,
|
|
creditEvents: [],
|
|
dailyBreakdown: [],
|
|
usageBreakdown: [],
|
|
creditsPurchaseURL: nil,
|
|
updatedAt: Date())
|
|
store.lastOpenAIDashboardCookieImportEmail = "stale-import@example.com"
|
|
|
|
#expect(store.codexAccountEmailForOpenAIDashboard() == nil)
|
|
#expect(store.codexCookieCacheScopeForOpenAIWeb() != nil)
|
|
#expect(store.codexCookieCacheScopeForOpenAIWeb() != .managedStoreUnreadable)
|
|
|
|
let imported = await store.importOpenAIDashboardCookiesIfNeeded(targetEmail: nil, force: true)
|
|
#expect(imported == nil)
|
|
#expect(importWasCalled == false)
|
|
#expect(store.openAIDashboard == nil)
|
|
#expect(store.openAIDashboardRequiresLogin == true)
|
|
#expect(store.openAIDashboardCookieImportStatus?
|
|
.contains("selected managed Codex account is unavailable") == true)
|
|
|
|
await store.refreshOpenAIDashboardIfNeeded(force: true)
|
|
#expect(importWasCalled == false)
|
|
#expect(store.openAIDashboard == nil)
|
|
#expect(store.lastOpenAIDashboardError?.contains("selected managed Codex account is unavailable") == true)
|
|
}
|
|
|
|
@Test
|
|
func `managed codex mismatch fail closed blocks stale dashboard restoration`() async {
|
|
let settings = self.makeSettingsStore(suite: "CodexManagedOpenAIWebTests-mismatch")
|
|
let managedAccount = ManagedCodexAccount(
|
|
id: UUID(),
|
|
email: "managed@example.com",
|
|
managedHomePath: "/tmp/managed-codex-home",
|
|
createdAt: 1,
|
|
updatedAt: 1,
|
|
lastAuthenticatedAt: 1)
|
|
settings._test_activeManagedCodexAccount = managedAccount
|
|
settings.codexActiveSource = .managedAccount(id: managedAccount.id)
|
|
defer { settings._test_activeManagedCodexAccount = nil }
|
|
|
|
let store = UsageStore(
|
|
fetcher: UsageFetcher(environment: [:]),
|
|
browserDetection: BrowserDetection(cacheTTL: 0),
|
|
settings: settings,
|
|
startupBehavior: .testing)
|
|
let staleSnapshot = OpenAIDashboardSnapshot(
|
|
signedInEmail: managedAccount.email,
|
|
codeReviewRemainingPercent: 100,
|
|
creditEvents: [],
|
|
dailyBreakdown: [],
|
|
usageBreakdown: [],
|
|
creditsPurchaseURL: nil,
|
|
updatedAt: Date())
|
|
|
|
await store.applyOpenAIDashboard(staleSnapshot, targetEmail: managedAccount.email)
|
|
await store.applyOpenAIDashboard(
|
|
OpenAIDashboardSnapshot(
|
|
signedInEmail: "other@example.com",
|
|
codeReviewRemainingPercent: 100,
|
|
creditEvents: [],
|
|
dailyBreakdown: [],
|
|
usageBreakdown: [],
|
|
creditsPurchaseURL: nil,
|
|
updatedAt: Date()),
|
|
targetEmail: managedAccount.email)
|
|
|
|
#expect(store.openAIDashboard == nil)
|
|
#expect(store.lastOpenAIDashboardSnapshot == nil)
|
|
#expect(store.openAIDashboardRequiresLogin == true)
|
|
#expect(store.lastOpenAIDashboardError?.contains("OpenAI dashboard signed in as other@example.com") == true)
|
|
|
|
await store.applyOpenAIDashboardFailure(message: "No dashboard data")
|
|
#expect(store.openAIDashboard == nil)
|
|
#expect(store.lastOpenAIDashboardError == "No dashboard data")
|
|
|
|
await store.applyOpenAIDashboardLoginRequiredFailure()
|
|
#expect(store.openAIDashboard == nil)
|
|
#expect(store.openAIDashboardRequiresLogin == true)
|
|
#expect(store.lastOpenAIDashboardError?.contains("requires a signed-in chatgpt.com session") == true)
|
|
}
|
|
|
|
@Test
|
|
func `managed codex import mismatch fail closed blocks stale dashboard restoration`() async {
|
|
let settings = self.makeSettingsStore(suite: "CodexManagedOpenAIWebTests-import-mismatch")
|
|
let managedAccount = ManagedCodexAccount(
|
|
id: UUID(),
|
|
email: "managed@example.com",
|
|
managedHomePath: "/tmp/managed-codex-home",
|
|
createdAt: 1,
|
|
updatedAt: 1,
|
|
lastAuthenticatedAt: 1)
|
|
settings._test_activeManagedCodexAccount = managedAccount
|
|
defer { settings._test_activeManagedCodexAccount = nil }
|
|
|
|
let store = UsageStore(
|
|
fetcher: UsageFetcher(environment: [:]),
|
|
browserDetection: BrowserDetection(cacheTTL: 0),
|
|
settings: settings,
|
|
startupBehavior: .testing)
|
|
store._test_openAIDashboardCookieImportOverride = { _, _, _, _, _ in
|
|
throw OpenAIDashboardBrowserCookieImporter.ImportError.noMatchingAccount(
|
|
found: [.init(sourceLabel: "Chrome", email: "other@example.com")])
|
|
}
|
|
|
|
let staleSnapshot = OpenAIDashboardSnapshot(
|
|
signedInEmail: managedAccount.email,
|
|
codeReviewRemainingPercent: 100,
|
|
creditEvents: [],
|
|
dailyBreakdown: [],
|
|
usageBreakdown: [],
|
|
creditsPurchaseURL: nil,
|
|
updatedAt: Date())
|
|
await store.applyOpenAIDashboard(staleSnapshot, targetEmail: managedAccount.email)
|
|
|
|
let imported = await store.importOpenAIDashboardCookiesIfNeeded(
|
|
targetEmail: managedAccount.email,
|
|
force: true)
|
|
|
|
#expect(imported == nil)
|
|
#expect(store.openAIDashboard == nil)
|
|
#expect(store.openAIDashboardRequiresLogin == true)
|
|
#expect(
|
|
store.openAIDashboardCookieImportStatus ==
|
|
"OpenAI cookies are for other@example.com, not managed@example.com.")
|
|
|
|
await store.applyOpenAIDashboardFailure(message: "No dashboard data")
|
|
#expect(store.openAIDashboard == nil)
|
|
#expect(store.lastOpenAIDashboardError == "No dashboard data")
|
|
|
|
await store.applyOpenAIDashboardLoginRequiredFailure()
|
|
#expect(store.openAIDashboard == nil)
|
|
#expect(store.openAIDashboardRequiresLogin == true)
|
|
#expect(store.lastOpenAIDashboardError?.contains("requires a signed-in chatgpt.com session") == true)
|
|
}
|
|
|
|
@Test
|
|
func `missing managed target failure handlers do not resurrect stale dashboard state`() async {
|
|
let settings = self.makeSettingsStore(suite: "CodexManagedOpenAIWebTests-missing-target-failure-handlers")
|
|
let isolatedHome = FileManager.default.temporaryDirectory
|
|
.appendingPathComponent("codex-openai-web-missing-target-\(UUID().uuidString)", isDirectory: true)
|
|
try? FileManager.default.createDirectory(at: isolatedHome, withIntermediateDirectories: true)
|
|
settings._test_codexReconciliationEnvironment = ["CODEX_HOME": isolatedHome.path]
|
|
settings.codexActiveSource = .managedAccount(id: UUID())
|
|
defer {
|
|
settings._test_codexReconciliationEnvironment = nil
|
|
try? FileManager.default.removeItem(at: isolatedHome)
|
|
}
|
|
|
|
let store = UsageStore(
|
|
fetcher: UsageFetcher(environment: [:]),
|
|
browserDetection: BrowserDetection(cacheTTL: 0),
|
|
settings: settings,
|
|
startupBehavior: .testing)
|
|
let staleSnapshot = OpenAIDashboardSnapshot(
|
|
signedInEmail: "stale@example.com",
|
|
codeReviewRemainingPercent: 100,
|
|
creditEvents: [],
|
|
dailyBreakdown: [],
|
|
usageBreakdown: [],
|
|
creditsPurchaseURL: nil,
|
|
updatedAt: Date())
|
|
|
|
await store.applyOpenAIDashboard(staleSnapshot, targetEmail: "stale@example.com")
|
|
await store.applyOpenAIDashboardFailure(message: "No dashboard data")
|
|
|
|
#expect(store.openAIDashboard == nil)
|
|
#expect(store.lastOpenAIDashboardSnapshot == nil)
|
|
#expect(store.lastOpenAIDashboardError?.contains("selected managed Codex account is unavailable") == true)
|
|
|
|
await store.applyOpenAIDashboard(staleSnapshot, targetEmail: "stale@example.com")
|
|
await store.applyOpenAIDashboardLoginRequiredFailure()
|
|
|
|
#expect(store.openAIDashboard == nil)
|
|
#expect(store.lastOpenAIDashboardSnapshot == nil)
|
|
#expect(store.openAIDashboardRequiresLogin == true)
|
|
#expect(store.lastOpenAIDashboardError?.contains("selected managed Codex account is unavailable") == true)
|
|
}
|
|
|
|
@Test
|
|
func `managed codex refresh stops after cookie mismatch instead of retrying web view`() async {
|
|
let settings = self.makeSettingsStore(suite: "CodexManagedOpenAIWebTests-mismatch-aborts-retry")
|
|
let managedAccount = ManagedCodexAccount(
|
|
id: UUID(),
|
|
email: "ratulsarna@gmail.com",
|
|
managedHomePath: "/tmp/managed-codex-home",
|
|
createdAt: 1,
|
|
updatedAt: 1,
|
|
lastAuthenticatedAt: 1)
|
|
settings._test_activeManagedCodexAccount = managedAccount
|
|
settings.codexActiveSource = .managedAccount(id: managedAccount.id)
|
|
defer { settings._test_activeManagedCodexAccount = nil }
|
|
|
|
let store = UsageStore(
|
|
fetcher: UsageFetcher(environment: [:]),
|
|
browserDetection: BrowserDetection(cacheTTL: 0),
|
|
settings: settings,
|
|
startupBehavior: .testing)
|
|
|
|
var loaderCalls = 0
|
|
store._test_openAIDashboardLoaderOverride = { _, _, _, _ in
|
|
loaderCalls += 1
|
|
throw OpenAIDashboardFetcher.FetchError.loginRequired
|
|
}
|
|
defer { store._test_openAIDashboardLoaderOverride = nil }
|
|
store._test_openAIDashboardCookieImportOverride = { _, _, _, _, _ in
|
|
throw OpenAIDashboardBrowserCookieImporter.ImportError.noMatchingAccount(
|
|
found: [.init(sourceLabel: "Chrome", email: "rdsarna@gmail.com")])
|
|
}
|
|
defer { store._test_openAIDashboardCookieImportOverride = nil }
|
|
|
|
let expectedGuard = store.currentCodexOpenAIWebRefreshGuard()
|
|
await store.refreshOpenAIDashboardIfNeeded(force: true, expectedGuard: expectedGuard)
|
|
|
|
#expect(loaderCalls == 1)
|
|
#expect(
|
|
store.lastOpenAIDashboardError ==
|
|
"OpenAI cookies are for rdsarna@gmail.com, not ratulsarna@gmail.com. " +
|
|
"Switch chatgpt.com account, then refresh OpenAI cookies.")
|
|
#expect(store.openAIDashboard == nil)
|
|
}
|
|
|
|
@Test
|
|
func `managed codex refresh reports no matching web session without fake account`() async {
|
|
let settings = self.makeSettingsStore(suite: "CodexManagedOpenAIWebTests-no-matching-web-session")
|
|
let managedAccount = ManagedCodexAccount(
|
|
id: UUID(),
|
|
email: "ratulsarna@gmail.com",
|
|
managedHomePath: "/tmp/managed-codex-home",
|
|
createdAt: 1,
|
|
updatedAt: 1,
|
|
lastAuthenticatedAt: 1)
|
|
settings._test_activeManagedCodexAccount = managedAccount
|
|
settings.codexActiveSource = .managedAccount(id: managedAccount.id)
|
|
defer { settings._test_activeManagedCodexAccount = nil }
|
|
|
|
let store = UsageStore(
|
|
fetcher: UsageFetcher(environment: [:]),
|
|
browserDetection: BrowserDetection(cacheTTL: 0),
|
|
settings: settings,
|
|
startupBehavior: .testing)
|
|
|
|
store._test_openAIDashboardLoaderOverride = { _, _, _, _ in
|
|
throw OpenAIDashboardFetcher.FetchError.loginRequired
|
|
}
|
|
defer { store._test_openAIDashboardLoaderOverride = nil }
|
|
store._test_openAIDashboardCookieImportOverride = { _, _, _, _, _ in
|
|
throw OpenAIDashboardBrowserCookieImporter.ImportError.noMatchingAccount(found: [])
|
|
}
|
|
defer { store._test_openAIDashboardCookieImportOverride = nil }
|
|
|
|
let expectedGuard = store.currentCodexOpenAIWebRefreshGuard()
|
|
await store.refreshOpenAIDashboardIfNeeded(force: true, expectedGuard: expectedGuard)
|
|
|
|
#expect(
|
|
store.lastOpenAIDashboardError ==
|
|
"No matching OpenAI web session found for ratulsarna@gmail.com. " +
|
|
"Sign in to chatgpt.com as ratulsarna@gmail.com, then refresh OpenAI cookies.")
|
|
#expect(store.openAIDashboard == nil)
|
|
}
|
|
}
|