310 lines · 14.3 KB · Swift
RawDownload
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()    }}