283 lines
13 KiB
Swift
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)
|
|
}
|
|
}
|