import Foundation import Testing @testable import LocalData private func makeLegacyIntegrationKey(name: String, domain: StorageDomain) -> StorageKey { StorageKey( name: name, domain: domain, security: .none, owner: "Legacy", description: "Legacy integration key" ) } private func makeModernIntegrationKey( name: String, domain: StorageDomain, legacyKey: AnyStorageKey? ) -> StorageKey { StorageKey( name: name, domain: domain, security: .keychain(accessibility: .afterFirstUnlock, accessControl: .none), owner: "Modern", description: "Modern integration key", migration: { destinationKey in guard let legacyKey else { return nil } return AnyStorageMigration( SimpleLegacyMigration(destinationKey: destinationKey, sourceKey: legacyKey) ) } ) } private struct IntegrationCatalog: StorageKeyCatalog { let allKeys: [AnyStorageKey] } @Suite(.serialized) struct MigrationIntegrationTests { private let router: StorageRouter init() { let testBaseURL = FileManager.default.temporaryDirectory.appending(path: "MigrationIntegrationTests-\(UUID().uuidString)") router = StorageRouter( keychain: MockKeychainHelper(), encryption: EncryptionHelper(keychain: MockKeychainHelper()), file: FileStorageHelper(configuration: FileStorageConfiguration(baseURL: testBaseURL)), defaults: UserDefaultsHelper(defaults: UserDefaults(suiteName: "MigrationIntegrationTests.\(UUID().uuidString)")!) ) } @Test func endToEndMigrationTest() async throws { let legacyKey = makeLegacyIntegrationKey(name: "legacy.integration", domain: .userDefaults(suite: nil)) let modernKey = makeModernIntegrationKey( name: "modern.integration", domain: .keychain(service: "test.migration"), legacyKey: .key(legacyKey) ) try await router.set("token", for: legacyKey) let migrated = try await router.get(modernKey) #expect(migrated == "token") let legacyExists = try await router.exists(legacyKey) #expect(legacyExists == false) } @Test func migrationRegistrationTest() async throws { let legacyKey = makeLegacyIntegrationKey(name: "legacy.catalog", domain: .userDefaults(suite: nil)) let modernKey = makeModernIntegrationKey( name: "modern.catalog", domain: .keychain(service: "test.migration"), legacyKey: .key(legacyKey) ) try await router.set("catalog", for: legacyKey) let catalog = IntegrationCatalog(allKeys: [.key(modernKey)]) try await router.registerCatalog(catalog, migrateImmediately: true) let migrated = try await router.get(modernKey) #expect(migrated == "catalog") } @Test func concurrentMigrationTest() async throws { let legacyKey = makeLegacyIntegrationKey(name: "legacy.concurrent", domain: .userDefaults(suite: nil)) let modernKey = makeModernIntegrationKey( name: "modern.concurrent", domain: .keychain(service: "test.migration"), legacyKey: .key(legacyKey) ) try await router.set("value", for: legacyKey) async let first: MigrationResult = router.forceMigration(for: modernKey) async let second: MigrationResult = router.forceMigration(for: modernKey) let firstResult = try await first let secondResult = try await second let results = [firstResult, secondResult] #expect(results.contains(where: { $0.success })) } @Test func migrationFailureResultTest() async throws { let destinationKey = makeModernIntegrationKey( name: "modern.failure", domain: .userDefaults(suite: nil), legacyKey: nil ) let migration = FailingMigration(destinationKey: destinationKey, error: .validationFailed("Invalid")) let result = try await migration.migrate(using: router, context: MigrationContext()) #expect(result.success == false) #expect(result.errors.first == .validationFailed("Invalid")) } }