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

283 lines
13 KiB
Swift

import CodexBarCore
import Foundation
import Testing
@testable import CodexBar
@MainActor
extension CodexAccountScopedRefreshTests {
@Test
func `stale stacked projection collapse runs single codex fetch`() async throws {
SettingsStore.codexAccountReconciliationSnapshotCacheIntervalOverrideForTesting = 60
let settings = self.makeSettingsStore(
suite: "CodexAccountScopedRefreshTests-stacked-collapse-single-fetch")
defer {
SettingsStore.codexAccountReconciliationSnapshotCacheIntervalOverrideForTesting = nil
settings._test_liveSystemCodexAccount = nil
settings._test_managedCodexAccountStoreURL = nil
}
settings.refreshFrequency = .manual
settings.multiAccountMenuLayout = .stacked
settings.codexActiveSource = .liveSystem
settings._test_liveSystemCodexAccount = self.liveAccount(
email: "live-collapse@example.com",
identity: .providerAccount(id: "acct-live-collapse"))
let managedAccountID = try #require(UUID(uuidString: "DDDDDDDD-EEEE-FFFF-AAAA-191919191919"))
let managedAccount = ManagedCodexAccount(
id: managedAccountID,
email: "managed-collapse@example.com",
managedHomePath: "/tmp/codex-managed-collapse",
createdAt: 1,
updatedAt: 2,
lastAuthenticatedAt: 2)
let staleStoreURL = try self.makeManagedAccountStoreURL(accounts: [managedAccount])
let emptyStoreURL = try self.makeManagedAccountStoreURL(accounts: [])
defer {
try? FileManager.default.removeItem(at: staleStoreURL)
try? FileManager.default.removeItem(at: emptyStoreURL)
}
settings._test_managedCodexAccountStoreURL = staleStoreURL
let staleReconciliationSnapshot = settings.codexAccountReconciliationSnapshot
#expect(CodexVisibleAccountProjection.make(from: staleReconciliationSnapshot).visibleAccounts.count == 2)
settings._test_managedCodexAccountStoreURL = emptyStoreURL
settings.cachedCodexAccountReconciliationSnapshot = CachedCodexAccountReconciliationSnapshot(
activeSource: .liveSystem,
loadedAt: Date(),
snapshot: staleReconciliationSnapshot)
let store = self.makeUsageStore(settings: settings)
self.installImmediateCodexProvider(
on: store,
snapshot: self.codexSnapshot(email: "live-collapse@example.com", usedPercent: 42))
await store.refreshProvider(.codex, allowDisabled: true)
#expect(store.snapshots[.codex]?.primary?.usedPercent == 42)
#expect(store.codexAccountSnapshots.count == 1)
#expect(store.codexAccountSnapshots.first?.account.email == "live-collapse@example.com")
#expect(store.codexAccountSnapshots.first?.snapshot?.primary?.usedPercent == 42)
}
@Test
func `stacked visible refresh discards selected success after managed auth file is removed`() async throws {
let settings = self.makeSettingsStore(
suite: "CodexAccountScopedRefreshTests-selected-managed-auth-file-removed")
settings.refreshFrequency = .manual
settings.multiAccountMenuLayout = .stacked
let targetID = try #require(UUID(uuidString: "DDDDDDDD-EEEE-FFFF-AAAA-202020202020"))
let siblingID = try #require(UUID(uuidString: "DDDDDDDD-EEEE-FFFF-AAAA-212121212121"))
let targetHome = FileManager.default.temporaryDirectory
.appendingPathComponent("codex-visible-managed-auth-removed-\(UUID().uuidString)", isDirectory: true)
let siblingHome = FileManager.default.temporaryDirectory
.appendingPathComponent(
"codex-visible-managed-auth-removed-sibling-\(UUID().uuidString)",
isDirectory: true)
try Self.writeCodexAuthFile(
homeURL: targetHome,
email: "managed-removed@example.com",
plan: "Pro",
accountId: "acct-managed-removed")
let oldFingerprint = try #require(CodexAuthFingerprint.fingerprint(homePath: targetHome.path))
let targetAccount = ManagedCodexAccount(
id: targetID,
email: "managed-removed@example.com",
providerAccountID: "acct-managed-removed",
workspaceLabel: "Managed Team",
workspaceAccountID: "acct-managed-removed",
authFingerprint: oldFingerprint,
managedHomePath: targetHome.path,
createdAt: 1,
updatedAt: 2,
lastAuthenticatedAt: 2)
let siblingAccount = ManagedCodexAccount(
id: siblingID,
email: "managed-removed-sibling@example.com",
providerAccountID: "acct-managed-removed-sibling",
workspaceLabel: "Sibling Team",
workspaceAccountID: "acct-managed-removed-sibling",
authFingerprint: "sibling-managed-removed",
managedHomePath: siblingHome.path,
createdAt: 1,
updatedAt: 2,
lastAuthenticatedAt: 2)
let storeURL = try self.makeManagedAccountStoreURL(accounts: [targetAccount, siblingAccount])
defer {
settings._test_managedCodexAccountStoreURL = nil
try? FileManager.default.removeItem(at: storeURL)
try? FileManager.default.removeItem(at: targetHome)
try? FileManager.default.removeItem(at: siblingHome)
}
settings._test_managedCodexAccountStoreURL = storeURL
settings.codexActiveSource = .managedAccount(id: targetID)
let snapshotStore = RecordingCodexAccountUsageSnapshotStore(initialSnapshots: [])
let store = UsageStore(
fetcher: UsageFetcher(environment: [:]),
browserDetection: BrowserDetection(cacheTTL: 0),
settings: settings,
codexAccountUsageSnapshotStore: snapshotStore,
startupBehavior: .testing)
let blocker = BlockingCodexFetchStrategy()
let targetHomePath = targetHome.path
let now = Date()
self.installContextualCodexProvider(on: store) { context in
if context.env["CODEX_HOME"] == targetHomePath {
return try await blocker.awaitResult()
}
return UsageSnapshot(
primary: RateWindow(
usedPercent: 9,
windowMinutes: 300,
resetsAt: nil,
resetDescription: nil),
secondary: nil,
updatedAt: now,
identity: ProviderIdentitySnapshot(
providerID: .codex,
accountEmail: "managed-removed-sibling@example.com",
accountOrganization: nil,
loginMethod: "Sibling Team"))
}
let refreshTask = Task { await store.refreshCodexVisibleAccountsForMenu() }
await blocker.waitUntilStarted()
try FileManager.default.removeItem(at: targetHome)
await blocker.resume(with: .success(self.codexSnapshot(email: "managed-removed@example.com", usedPercent: 44)))
await refreshTask.value
#expect(store.snapshots[.codex] == nil)
#expect(store.errors[.codex] == nil)
#expect(!store.codexAccountSnapshots.contains {
$0.account.workspaceAccountID == "acct-managed-removed"
})
#expect(!snapshotStore.storedSnapshots.contains {
$0.account.workspaceAccountID == "acct-managed-removed"
})
}
@Test
func `startup snapshot hydration refreshes managed auth fingerprint with composite disk owner`() throws {
let settings = self.makeSettingsStore(
suite: "CodexAccountScopedRefreshTests-startup-managed-auth-hydration")
settings.refreshFrequency = .manual
settings.multiAccountMenuLayout = .stacked
let accountID = try #require(UUID(uuidString: "DDDDDDDD-EEEE-FFFF-AAAA-222222222222"))
let managedHome = FileManager.default.temporaryDirectory
.appendingPathComponent("codex-visible-managed-startup-\(UUID().uuidString)", isDirectory: true)
try Self.writeCodexAuthFile(
homeURL: managedHome,
email: "managed-startup@example.com",
plan: "Pro",
accountId: "acct-managed-startup")
let oldFingerprint = try #require(CodexAuthFingerprint.fingerprint(homePath: managedHome.path))
let managedAccount = ManagedCodexAccount(
id: accountID,
email: "managed-startup@example.com",
authFingerprint: oldFingerprint,
managedHomePath: managedHome.path,
createdAt: 1,
updatedAt: 2,
lastAuthenticatedAt: 2)
let storeURL = try self.makeManagedAccountStoreURL(accounts: [managedAccount])
let snapshotURL = FileManager.default.temporaryDirectory
.appendingPathComponent("codex-visible-managed-startup-\(UUID().uuidString).json")
defer {
settings._test_managedCodexAccountStoreURL = nil
try? FileManager.default.removeItem(at: storeURL)
try? FileManager.default.removeItem(at: snapshotURL)
try? FileManager.default.removeItem(at: managedHome)
}
settings._test_managedCodexAccountStoreURL = storeURL
settings.codexActiveSource = .managedAccount(id: accountID)
let staleAccount = try #require(settings.codexVisibleAccountProjection.visibleAccounts
.first { $0.storedAccountID == accountID })
#expect(staleAccount.authFingerprint == oldFingerprint)
#expect(staleAccount.workspaceAccountID == "acct-managed-startup")
try Self.writeCodexAuthFile(
homeURL: managedHome,
email: "managed-startup@example.com",
plan: "Team",
accountId: "acct-managed-startup")
let newFingerprint = try #require(CodexAuthFingerprint.fingerprint(homePath: managedHome.path))
#expect(newFingerprint != oldFingerprint)
let freshAccount = CodexVisibleAccount(
id: staleAccount.id,
email: staleAccount.email,
workspaceLabel: staleAccount.workspaceLabel,
workspaceAccountID: staleAccount.workspaceAccountID,
authFingerprint: newFingerprint,
storedAccountID: staleAccount.storedAccountID,
selectionSource: staleAccount.selectionSource,
isActive: staleAccount.isActive,
isLive: staleAccount.isLive,
canReauthenticate: staleAccount.canReauthenticate,
canRemove: staleAccount.canRemove)
let snapshotStore = FileCodexAccountUsageSnapshotStore(fileURL: snapshotURL)
snapshotStore.store([
CodexAccountUsageSnapshot(
account: freshAccount,
snapshot: self.codexSnapshot(email: freshAccount.email, usedPercent: 64),
error: nil,
sourceLabel: "cached"),
])
#expect(snapshotStore.load(for: [staleAccount]).count == 1)
let store = UsageStore(
fetcher: UsageFetcher(environment: [:]),
browserDetection: BrowserDetection(cacheTTL: 0),
settings: settings,
codexAccountUsageSnapshotStore: snapshotStore,
startupBehavior: .testing)
let hydrated = try #require(store.codexAccountSnapshots.first)
#expect(store.codexAccountSnapshots.count == 1)
#expect(hydrated.id == freshAccount.id)
#expect(hydrated.account.authFingerprint == newFingerprint)
#expect(hydrated.snapshot?.primary?.usedPercent == 64)
}
@Test
func `snapshot hydration never crosses members of the same provider workspace`() {
let snapshotURL = FileManager.default.temporaryDirectory
.appendingPathComponent("codex-provider-member-isolation-\(UUID().uuidString).json")
defer { try? FileManager.default.removeItem(at: snapshotURL) }
let priorAccount = CodexVisibleAccount(
id: "shared-row-id",
email: "first-member@example.com",
workspaceAccountID: "workspace-shared-by-members",
storedAccountID: nil,
selectionSource: .liveSystem,
isActive: true,
isLive: true,
canReauthenticate: false,
canRemove: false)
let otherMember = CodexVisibleAccount(
id: "shared-row-id",
email: "second-member@example.com",
workspaceAccountID: "workspace-shared-by-members",
storedAccountID: nil,
selectionSource: .liveSystem,
isActive: true,
isLive: true,
canReauthenticate: false,
canRemove: false)
let snapshotStore = FileCodexAccountUsageSnapshotStore(fileURL: snapshotURL)
snapshotStore.store([
CodexAccountUsageSnapshot(
account: priorAccount,
snapshot: self.codexSnapshot(email: priorAccount.email, usedPercent: 64),
error: nil,
sourceLabel: "cached"),
])
#expect(snapshotStore.load(for: [otherMember]).isEmpty)
}
}