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

103 lines
4.4 KiB
Swift

import CodexBarCore
import Foundation
import Testing
@testable import CodexBar
@Suite(.serialized)
struct CodexAccountFingerprintReconciliationTests {
@Test
func `active source falls back to identity when auth fingerprint rotated`() throws {
let accountID = try #require(UUID(uuidString: "AAAAAAAA-BBBB-CCCC-DDDD-333333333333"))
let managed = ManagedCodexAccount(
id: accountID,
email: "rotated@example.com",
authFingerprint: "old-auth-json",
managedHomePath: "/tmp/rotated",
createdAt: 1,
updatedAt: 2,
lastAuthenticatedAt: 3)
let live = ObservedSystemCodexAccount(
email: "rotated@example.com",
authFingerprint: "new-auth-json",
codexHomePath: "/Users/test/.codex",
observedAt: Date(),
identity: .emailOnly(normalizedEmail: "rotated@example.com"))
let snapshot = CodexAccountReconciliationSnapshot(
storedAccounts: [managed],
activeStoredAccount: managed,
liveSystemAccount: live,
matchingStoredAccountForLiveSystemAccount: managed,
activeSource: .managedAccount(id: accountID),
hasUnreadableAddedAccountStore: false)
let resolution = CodexActiveSourceResolver.resolve(from: snapshot)
#expect(resolution.resolvedSource == .liveSystem)
#expect(resolution.requiresPersistenceCorrection)
}
@Test
@MainActor
func `auth fingerprint matches live account before semantic duplicate identity`() throws {
let suite = "CodexAccountFingerprintReconciliationTests-auth-fingerprint"
let defaults = try #require(UserDefaults(suiteName: suite))
defaults.removePersistentDomain(forName: suite)
let settings = SettingsStore(
userDefaults: defaults,
configStore: testConfigStore(suiteName: suite),
zaiTokenStore: NoopZaiTokenStore(),
syntheticTokenStore: NoopSyntheticTokenStore())
let firstID = try #require(UUID(uuidString: "AAAAAAAA-BBBB-CCCC-DDDD-111111111111"))
let secondID = try #require(UUID(uuidString: "AAAAAAAA-BBBB-CCCC-DDDD-222222222222"))
let first = ManagedCodexAccount(
id: firstID,
email: "same@example.com",
authFingerprint: "1111",
managedHomePath: "/tmp/first",
createdAt: 1,
updatedAt: 2,
lastAuthenticatedAt: 3)
let second = ManagedCodexAccount(
id: secondID,
email: "same@example.com",
providerAccountID: "account-team",
authFingerprint: "2222",
managedHomePath: "/tmp/second",
createdAt: 1,
updatedAt: 2,
lastAuthenticatedAt: 3)
let storeURL = FileManager.default.temporaryDirectory
.appendingPathComponent("codex-managed-store-\(UUID().uuidString).json")
try Self.writeManagedCodexStore(
ManagedCodexAccountSet(version: FileManagedCodexAccountStore.currentVersion, accounts: [first, second]),
to: storeURL)
settings._test_managedCodexAccountStoreURL = storeURL
settings._test_liveSystemCodexAccount = ObservedSystemCodexAccount(
email: "same@example.com",
authFingerprint: "2222",
codexHomePath: "/Users/test/.codex",
observedAt: Date(),
identity: .emailOnly(normalizedEmail: "same@example.com"))
defer {
settings._test_managedCodexAccountStoreURL = nil
settings._test_liveSystemCodexAccount = nil
try? FileManager.default.removeItem(at: storeURL)
}
let snapshot = settings.codexAccountReconciliationSnapshot
let projection = settings.codexVisibleAccountProjection
#expect(snapshot.matchingStoredAccountForLiveSystemAccount?.id == secondID)
#expect(projection.liveVisibleAccountID == "live:email:same@example.com")
#expect(projection.visibleAccounts.first { $0.storedAccountID == secondID }?.isLive == true)
#expect(projection.visibleAccounts.first { $0.storedAccountID == firstID }?.isLive == false)
}
private static func writeManagedCodexStore(_ accounts: ManagedCodexAccountSet, to storeURL: URL) throws {
let encoder = JSONEncoder()
encoder.outputFormatting = [.sortedKeys]
let data = try encoder.encode(accounts)
try data.write(to: storeURL, options: [.atomic])
}
}