import CodexBarCoreimport Foundationimport Testing@testable import CodexBar @Suite(.serialized)@MainActorstruct CodexAccountPromotionExecutionTests { @Test func `executor import store failure cleans up imported home and maps managed store error`() async throws { let container = try CodexAccountPromotionTestContainer( suiteName: "CodexAccountPromotionExecutionTests-import-cleanup") defer { container.tearDown() } let target = try container.createManagedAccount( persistedEmail: "beta@example.com", authAccountID: "acct-beta") try container.persistAccounts([target]) _ = try container.writeLiveOAuthAuthFile(email: "alpha@example.com", accountID: "acct-alpha") let context = try await self.makeContext(container: container, targetID: target.id) let executor = CodexDisplacedLivePreservationExecutor( store: RecordingManagedCodexAccountStore(base: container.fileStore) { _ in throw PromotionTestError.storeWriteFailed }, homeFactory: container.homeFactory, fileManager: .default) #expect(throws: CodexAccountPromotionError.managedStoreCommitFailed) { try executor.execute(plan: .importNew(reason: .noExistingManagedDestination), context: context) } #expect(try container.managedHomeURLs().count == 1) #expect(try container.loadAccounts().accounts.count == 1) } @Test func `executor refresh failure leaves live auth untouched and keeps copied managed auth`() async throws { let container = try CodexAccountPromotionTestContainer( suiteName: "CodexAccountPromotionExecutionTests-refresh-failure") defer { container.tearDown() } let target = try container.createManagedAccount( persistedEmail: "beta@example.com", authAccountID: "acct-beta") let existingManagedLive = try container.createManagedAccount( persistedEmail: "alpha@example.com", authAccountID: "acct-alpha") try container.persistAccounts([target, existingManagedLive]) let originalManagedAuthData = try container.managedAuthData(for: existingManagedLive) let liveAuthData = try container.writeLiveOAuthAuthFile( email: "alpha@example.com", accountID: "acct-alpha", apiKey: "sk-refreshed-live") let originalLiveAuthData = try #require(try container.liveAuthData()) let context = try await self.makeContext(container: container, targetID: target.id) let plan = CodexDisplacedLivePreservationPlanner().makePlan(context: context) let executor = CodexDisplacedLivePreservationExecutor( store: RecordingManagedCodexAccountStore(base: container.fileStore) { accounts in if accounts.account(id: existingManagedLive.id)? .lastAuthenticatedAt != existingManagedLive.lastAuthenticatedAt { throw PromotionTestError.storeWriteFailed } }, homeFactory: container.homeFactory, fileManager: .default) #expect(throws: CodexAccountPromotionError.managedStoreCommitFailed) { try executor.execute(plan: plan, context: context) } let accounts = try container.loadAccounts().accounts let persistedManagedLive = try #require(accounts.first(where: { $0.id == existingManagedLive.id })) #expect(try container.liveAuthData() == originalLiveAuthData) #expect(try container.managedAuthData(for: persistedManagedLive) != originalManagedAuthData) #expect(try container.managedAuthData(for: persistedManagedLive) == liveAuthData) } @Test func `executor import verifies persisted account after concurrent duplicate collision`() async throws { let container = try CodexAccountPromotionTestContainer( suiteName: "CodexAccountPromotionExecutionTests-import-collision-repair") defer { container.tearDown() } let target = try container.createManagedAccount( persistedEmail: "beta@example.com", authAccountID: "acct-beta") let concurrentID = UUID() let concurrentHomeURL = container.managedHomesURL.appendingPathComponent( concurrentID.uuidString, isDirectory: true) try FileManager.default.createDirectory(at: concurrentHomeURL, withIntermediateDirectories: true) let concurrentManaged = ManagedCodexAccount( id: concurrentID, email: "alpha@example.com", providerAccountID: "acct-alpha", workspaceLabel: "Personal", workspaceAccountID: "acct-alpha", managedHomePath: concurrentHomeURL.path, createdAt: 1, updatedAt: 1, lastAuthenticatedAt: 1) try container.persistAccounts([target]) let liveAuthData = try container.writeLiveOAuthAuthFile(email: "alpha@example.com", accountID: "acct-alpha") let context = try await self.makeContext(container: container, targetID: target.id) let executor = CodexDisplacedLivePreservationExecutor( store: ConcurrentDuplicateManagedCodexAccountStore( base: container.fileStore, concurrentAccount: concurrentManaged), homeFactory: container.homeFactory, fileManager: .default) let result = try executor.execute(plan: .importNew(reason: .noExistingManagedDestination), context: context) #expect(result.displacedLiveDisposition == .alreadyManaged(managedAccountID: concurrentManaged.id)) let accounts = try container.loadAccounts().accounts let repaired = try #require(accounts.first(where: { $0.id == concurrentManaged.id })) #expect(repaired.managedHomePath != concurrentHomeURL.path) #expect(try container.managedAuthData(for: repaired) == liveAuthData) } @Test func `executor refresh filesystem failure maps to managed store error`() async throws { let container = try CodexAccountPromotionTestContainer( suiteName: "CodexAccountPromotionExecutionTests-refresh-filesystem-failure") defer { container.tearDown() } let target = try container.createManagedAccount( persistedEmail: "beta@example.com", authAccountID: "acct-beta") let existingManagedLive = try container.createManagedAccount( persistedEmail: "alpha@example.com", authAccountID: "acct-alpha") try container.persistAccounts([target, existingManagedLive]) _ = try container.writeLiveOAuthAuthFile(email: "alpha@example.com", accountID: "acct-alpha") let context = try await self.makeContext(container: container, targetID: target.id) let plan = CodexDisplacedLivePreservationPlanner().makePlan(context: context) let managedHomeURL = URL(fileURLWithPath: existingManagedLive.managedHomePath, isDirectory: true) try FileManager.default.removeItem(at: managedHomeURL) try Data("blocked".utf8).write(to: managedHomeURL) let executor = CodexDisplacedLivePreservationExecutor( store: container.fileStore, homeFactory: container.homeFactory, fileManager: .default) #expect(throws: CodexAccountPromotionError.managedStoreCommitFailed) { try executor.execute(plan: plan, context: context) } } @Test func `executor legacy import repair ignores provider backed rows with the same email`() async throws { let container = try CodexAccountPromotionTestContainer( suiteName: "CodexAccountPromotionExecutionTests-legacy-import-provider-same-email") defer { container.tearDown() } let target = try container.createManagedAccount( persistedEmail: "beta@example.com", authAccountID: "acct-beta") let providerManaged = try container.createManagedAccount( persistedEmail: "alpha@example.com", authAccountID: "acct-existing") try container.persistAccounts([target, providerManaged]) _ = try container.writeLiveOAuthAuthFile(email: "alpha@example.com") let context = try await self.makeContext(container: container, targetID: target.id) let originalProviderManaged = try #require(try container.loadAccounts().account(id: providerManaged.id)) let executor = CodexDisplacedLivePreservationExecutor( store: DroppingLegacyImportedAccountStore( base: container.fileStore, preservedProviderBackedAccount: originalProviderManaged), homeFactory: container.homeFactory, fileManager: .default) #expect(throws: CodexAccountPromotionError.managedStoreCommitFailed) { try executor.execute(plan: .importNew(reason: .noExistingManagedDestination), context: context) } let persistedProviderManaged = try #require(try container.loadAccounts().account(id: providerManaged.id)) #expect(persistedProviderManaged.providerAccountID == originalProviderManaged.providerAccountID) #expect(persistedProviderManaged.managedHomePath == originalProviderManaged.managedHomePath) } @Test func `executor reject preserves stable error mapping`() async throws { let container = try CodexAccountPromotionTestContainer( suiteName: "CodexAccountPromotionExecutionTests-reject-mapping") defer { container.tearDown() } let target = try container.createManagedAccount( persistedEmail: "beta@example.com", authAccountID: "acct-beta") try container.persistAccounts([target]) let context = try await self.makeContext(container: container, targetID: target.id) let executor = CodexDisplacedLivePreservationExecutor( store: container.fileStore, homeFactory: container.homeFactory, fileManager: .default) #expect(throws: CodexAccountPromotionError.liveAccountAPIKeyOnlyUnsupported) { try executor.execute(plan: .reject(reason: .liveAPIKeyOnlyUnsupported), context: context) } #expect(throws: CodexAccountPromotionError.liveAccountUnreadable) { try executor.execute(plan: .reject(reason: .liveUnreadable), context: context) } #expect(throws: CodexAccountPromotionError.liveAccountMissingIdentityForPreservation) { try executor.execute(plan: .reject(reason: .liveIdentityMissingForPreservation), context: context) } } @Test func `executor rejects target as preservation destination`() async throws { let container = try CodexAccountPromotionTestContainer( suiteName: "CodexAccountPromotionExecutionTests-target-destination") defer { container.tearDown() } let target = try container.createManagedAccount( persistedEmail: "beta@example.com", authAccountID: "acct-beta") try container.persistAccounts([target]) _ = try container.writeLiveOAuthAuthFile(email: "alpha@example.com", accountID: "acct-alpha") let context = try await self.makeContext(container: container, targetID: target.id) let executor = CodexDisplacedLivePreservationExecutor( store: container.fileStore, homeFactory: container.homeFactory, fileManager: .default) #expect(throws: CodexAccountPromotionError.managedStoreCommitFailed) { try executor.execute( plan: .refreshExisting( destination: context.target, reason: .readableHomeIdentityMatch), context: context) } } private func makeContext( container: CodexAccountPromotionTestContainer, targetID: UUID) async throws -> PreparedPromotionContext { let builder = PreparedPromotionContextBuilder( store: container.fileStore, workspaceResolver: container.workspaceResolver, snapshotLoader: SettingsStoreCodexAccountReconciliationSnapshotLoader(settingsStore: container.settings), authMaterialReader: DefaultCodexAuthMaterialReader(), baseEnvironment: container.baseEnvironment, fileManager: .default) return try await builder.build(targetID: targetID) }} private final class ConcurrentDuplicateManagedCodexAccountStore: ManagedCodexAccountStoring, @unchecked Sendable { let base: any ManagedCodexAccountStoring let concurrentAccount: ManagedCodexAccount private var didInjectConcurrentAccount = false init(base: any ManagedCodexAccountStoring, concurrentAccount: ManagedCodexAccount) { self.base = base self.concurrentAccount = concurrentAccount } func loadAccounts() throws -> ManagedCodexAccountSet { if self.didInjectConcurrentAccount == false { self.didInjectConcurrentAccount = true let current = try self.base.loadAccounts() try self.base.storeAccounts(ManagedCodexAccountSet( version: current.version, accounts: current.accounts + [self.concurrentAccount])) } return try self.base.loadAccounts() } func storeAccounts(_ accounts: ManagedCodexAccountSet) throws { try self.base.storeAccounts(accounts) } func ensureFileExists() throws -> URL { try self.base.ensureFileExists() }} private final class DroppingLegacyImportedAccountStore: ManagedCodexAccountStoring, @unchecked Sendable { let base: any ManagedCodexAccountStoring let preservedProviderBackedAccount: ManagedCodexAccount init(base: any ManagedCodexAccountStoring, preservedProviderBackedAccount: ManagedCodexAccount) { self.base = base self.preservedProviderBackedAccount = preservedProviderBackedAccount } func loadAccounts() throws -> ManagedCodexAccountSet { try self.base.loadAccounts() } func storeAccounts(_ accounts: ManagedCodexAccountSet) throws { let filteredAccounts = accounts.accounts.filter { $0.id == self.preservedProviderBackedAccount.id || $0.providerAccountID != nil } try self.base.storeAccounts(ManagedCodexAccountSet( version: accounts.version, accounts: filteredAccounts)) } func ensureFileExists() throws -> URL { try self.base.ensureFileExists() }}
import CodexBarCore
import Foundation
import Testing
@testable import CodexBar
@Suite(.serialized)
@MainActor
struct CodexAccountPromotionExecutionTests {
@Test
func `executor import store failure cleans up imported home and maps managed store error`() async throws {
let container = try CodexAccountPromotionTestContainer(
suiteName: "CodexAccountPromotionExecutionTests-import-cleanup")
defer { container.tearDown() }
let target = try container.createManagedAccount(
persistedEmail: "beta@example.com",
authAccountID: "acct-beta")
try container.persistAccounts([target])
_ = try container.writeLiveOAuthAuthFile(email: "alpha@example.com", accountID: "acct-alpha")
let context = try await self.makeContext(container: container, targetID: target.id)
let executor = CodexDisplacedLivePreservationExecutor(
store: RecordingManagedCodexAccountStore(base: container.fileStore) { _ in
throw PromotionTestError.storeWriteFailed
},
homeFactory: container.homeFactory,
fileManager: .default)
#expect(throws: CodexAccountPromotionError.managedStoreCommitFailed) {
try executor.execute(plan: .importNew(reason: .noExistingManagedDestination), context: context)
}
#expect(try container.managedHomeURLs().count == 1)
#expect(try container.loadAccounts().accounts.count == 1)
}
@Test
func `executor refresh failure leaves live auth untouched and keeps copied managed auth`() async throws {
let container = try CodexAccountPromotionTestContainer(
suiteName: "CodexAccountPromotionExecutionTests-refresh-failure")
defer { container.tearDown() }
let target = try container.createManagedAccount(
persistedEmail: "beta@example.com",
authAccountID: "acct-beta")
let existingManagedLive = try container.createManagedAccount(
persistedEmail: "alpha@example.com",
authAccountID: "acct-alpha")
try container.persistAccounts([target, existingManagedLive])
let originalManagedAuthData = try container.managedAuthData(for: existingManagedLive)
let liveAuthData = try container.writeLiveOAuthAuthFile(
email: "alpha@example.com",
accountID: "acct-alpha",
apiKey: "sk-refreshed-live")
let originalLiveAuthData = try #require(try container.liveAuthData())
let context = try await self.makeContext(container: container, targetID: target.id)
let plan = CodexDisplacedLivePreservationPlanner().makePlan(context: context)
let executor = CodexDisplacedLivePreservationExecutor(
store: RecordingManagedCodexAccountStore(base: container.fileStore) { accounts in
if accounts.account(id: existingManagedLive.id)?
.lastAuthenticatedAt != existingManagedLive.lastAuthenticatedAt
{
throw PromotionTestError.storeWriteFailed
}
},
homeFactory: container.homeFactory,
fileManager: .default)
#expect(throws: CodexAccountPromotionError.managedStoreCommitFailed) {
try executor.execute(plan: plan, context: context)
}
let accounts = try container.loadAccounts().accounts
let persistedManagedLive = try #require(accounts.first(where: { $0.id == existingManagedLive.id }))
#expect(try container.liveAuthData() == originalLiveAuthData)
#expect(try container.managedAuthData(for: persistedManagedLive) != originalManagedAuthData)
#expect(try container.managedAuthData(for: persistedManagedLive) == liveAuthData)
}
@Test
func `executor import verifies persisted account after concurrent duplicate collision`() async throws {
let container = try CodexAccountPromotionTestContainer(
suiteName: "CodexAccountPromotionExecutionTests-import-collision-repair")
defer { container.tearDown() }
let target = try container.createManagedAccount(
persistedEmail: "beta@example.com",
authAccountID: "acct-beta")
let concurrentID = UUID()
let concurrentHomeURL = container.managedHomesURL.appendingPathComponent(
concurrentID.uuidString,
isDirectory: true)
try FileManager.default.createDirectory(at: concurrentHomeURL, withIntermediateDirectories: true)
let concurrentManaged = ManagedCodexAccount(
id: concurrentID,
email: "alpha@example.com",
providerAccountID: "acct-alpha",
workspaceLabel: "Personal",
workspaceAccountID: "acct-alpha",
managedHomePath: concurrentHomeURL.path,
createdAt: 1,
updatedAt: 1,
lastAuthenticatedAt: 1)
try container.persistAccounts([target])
let liveAuthData = try container.writeLiveOAuthAuthFile(email: "alpha@example.com", accountID: "acct-alpha")
let context = try await self.makeContext(container: container, targetID: target.id)
let executor = CodexDisplacedLivePreservationExecutor(
store: ConcurrentDuplicateManagedCodexAccountStore(
base: container.fileStore,
concurrentAccount: concurrentManaged),
homeFactory: container.homeFactory,
fileManager: .default)
let result = try executor.execute(plan: .importNew(reason: .noExistingManagedDestination), context: context)
#expect(result.displacedLiveDisposition == .alreadyManaged(managedAccountID: concurrentManaged.id))
let accounts = try container.loadAccounts().accounts
let repaired = try #require(accounts.first(where: { $0.id == concurrentManaged.id }))
#expect(repaired.managedHomePath != concurrentHomeURL.path)
#expect(try container.managedAuthData(for: repaired) == liveAuthData)
}
@Test
func `executor refresh filesystem failure maps to managed store error`() async throws {
let container = try CodexAccountPromotionTestContainer(
suiteName: "CodexAccountPromotionExecutionTests-refresh-filesystem-failure")
defer { container.tearDown() }
let target = try container.createManagedAccount(
persistedEmail: "beta@example.com",
authAccountID: "acct-beta")
let existingManagedLive = try container.createManagedAccount(
persistedEmail: "alpha@example.com",
authAccountID: "acct-alpha")
try container.persistAccounts([target, existingManagedLive])
_ = try container.writeLiveOAuthAuthFile(email: "alpha@example.com", accountID: "acct-alpha")
let context = try await self.makeContext(container: container, targetID: target.id)
let plan = CodexDisplacedLivePreservationPlanner().makePlan(context: context)
let managedHomeURL = URL(fileURLWithPath: existingManagedLive.managedHomePath, isDirectory: true)
try FileManager.default.removeItem(at: managedHomeURL)
try Data("blocked".utf8).write(to: managedHomeURL)
let executor = CodexDisplacedLivePreservationExecutor(
store: container.fileStore,
homeFactory: container.homeFactory,
fileManager: .default)
#expect(throws: CodexAccountPromotionError.managedStoreCommitFailed) {
try executor.execute(plan: plan, context: context)
}
}
@Test
func `executor legacy import repair ignores provider backed rows with the same email`() async throws {
let container = try CodexAccountPromotionTestContainer(
suiteName: "CodexAccountPromotionExecutionTests-legacy-import-provider-same-email")
defer { container.tearDown() }
let target = try container.createManagedAccount(
persistedEmail: "beta@example.com",
authAccountID: "acct-beta")
let providerManaged = try container.createManagedAccount(
persistedEmail: "alpha@example.com",
authAccountID: "acct-existing")
try container.persistAccounts([target, providerManaged])
_ = try container.writeLiveOAuthAuthFile(email: "alpha@example.com")
let context = try await self.makeContext(container: container, targetID: target.id)
let originalProviderManaged = try #require(try container.loadAccounts().account(id: providerManaged.id))
let executor = CodexDisplacedLivePreservationExecutor(
store: DroppingLegacyImportedAccountStore(
base: container.fileStore,
preservedProviderBackedAccount: originalProviderManaged),
homeFactory: container.homeFactory,
fileManager: .default)
#expect(throws: CodexAccountPromotionError.managedStoreCommitFailed) {
try executor.execute(plan: .importNew(reason: .noExistingManagedDestination), context: context)
}
let persistedProviderManaged = try #require(try container.loadAccounts().account(id: providerManaged.id))
#expect(persistedProviderManaged.providerAccountID == originalProviderManaged.providerAccountID)
#expect(persistedProviderManaged.managedHomePath == originalProviderManaged.managedHomePath)
}
@Test
func `executor reject preserves stable error mapping`() async throws {
let container = try CodexAccountPromotionTestContainer(
suiteName: "CodexAccountPromotionExecutionTests-reject-mapping")
defer { container.tearDown() }
let target = try container.createManagedAccount(
persistedEmail: "beta@example.com",
authAccountID: "acct-beta")
try container.persistAccounts([target])
let context = try await self.makeContext(container: container, targetID: target.id)
let executor = CodexDisplacedLivePreservationExecutor(
store: container.fileStore,
homeFactory: container.homeFactory,
fileManager: .default)
#expect(throws: CodexAccountPromotionError.liveAccountAPIKeyOnlyUnsupported) {
try executor.execute(plan: .reject(reason: .liveAPIKeyOnlyUnsupported), context: context)
}
#expect(throws: CodexAccountPromotionError.liveAccountUnreadable) {
try executor.execute(plan: .reject(reason: .liveUnreadable), context: context)
}
#expect(throws: CodexAccountPromotionError.liveAccountMissingIdentityForPreservation) {
try executor.execute(plan: .reject(reason: .liveIdentityMissingForPreservation), context: context)
}
}
@Test
func `executor rejects target as preservation destination`() async throws {
let container = try CodexAccountPromotionTestContainer(
suiteName: "CodexAccountPromotionExecutionTests-target-destination")
defer { container.tearDown() }
let target = try container.createManagedAccount(
persistedEmail: "beta@example.com",
authAccountID: "acct-beta")
try container.persistAccounts([target])
_ = try container.writeLiveOAuthAuthFile(email: "alpha@example.com", accountID: "acct-alpha")
let context = try await self.makeContext(container: container, targetID: target.id)
let executor = CodexDisplacedLivePreservationExecutor(
store: container.fileStore,
homeFactory: container.homeFactory,
fileManager: .default)
#expect(throws: CodexAccountPromotionError.managedStoreCommitFailed) {
try executor.execute(
plan: .refreshExisting(
destination: context.target,
reason: .readableHomeIdentityMatch),
context: context)
}
}
private func makeContext(
container: CodexAccountPromotionTestContainer,
targetID: UUID)
async throws -> PreparedPromotionContext
{
let builder = PreparedPromotionContextBuilder(
store: container.fileStore,
workspaceResolver: container.workspaceResolver,
snapshotLoader: SettingsStoreCodexAccountReconciliationSnapshotLoader(settingsStore: container.settings),
authMaterialReader: DefaultCodexAuthMaterialReader(),
baseEnvironment: container.baseEnvironment,
fileManager: .default)
return try await builder.build(targetID: targetID)
}
}
private final class ConcurrentDuplicateManagedCodexAccountStore: ManagedCodexAccountStoring, @unchecked Sendable {
let base: any ManagedCodexAccountStoring
let concurrentAccount: ManagedCodexAccount
private var didInjectConcurrentAccount = false
init(base: any ManagedCodexAccountStoring, concurrentAccount: ManagedCodexAccount) {
self.base = base
self.concurrentAccount = concurrentAccount
}
func loadAccounts() throws -> ManagedCodexAccountSet {
if self.didInjectConcurrentAccount == false {
self.didInjectConcurrentAccount = true
let current = try self.base.loadAccounts()
try self.base.storeAccounts(ManagedCodexAccountSet(
version: current.version,
accounts: current.accounts + [self.concurrentAccount]))
}
return try self.base.loadAccounts()
}
func storeAccounts(_ accounts: ManagedCodexAccountSet) throws {
try self.base.storeAccounts(accounts)
}
func ensureFileExists() throws -> URL {
try self.base.ensureFileExists()
}
}
private final class DroppingLegacyImportedAccountStore: ManagedCodexAccountStoring, @unchecked Sendable {
let base: any ManagedCodexAccountStoring
let preservedProviderBackedAccount: ManagedCodexAccount
init(base: any ManagedCodexAccountStoring, preservedProviderBackedAccount: ManagedCodexAccount) {
self.base = base
self.preservedProviderBackedAccount = preservedProviderBackedAccount
}
func loadAccounts() throws -> ManagedCodexAccountSet {
try self.base.loadAccounts()
}
func storeAccounts(_ accounts: ManagedCodexAccountSet) throws {
let filteredAccounts = accounts.accounts.filter {
$0.id == self.preservedProviderBackedAccount.id || $0.providerAccountID != nil
}
try self.base.storeAccounts(ManagedCodexAccountSet(
version: accounts.version,
accounts: filteredAccounts))
}
func ensureFileExists() throws -> URL {
try self.base.ensureFileExists()
}
}