import Foundation import Testing @testable import LocalData private struct LegacyIntegrationKey: StorageKey { typealias Value = String let name: String let domain: StorageDomain let security: SecurityPolicy = .none let serializer: Serializer = .json let owner: String = "Legacy" let description: String = "Legacy integration key" let availability: PlatformAvailability = .all let syncPolicy: SyncPolicy = .never } private struct ModernIntegrationKey: StorageKey { typealias Value = String let name: String let domain: StorageDomain let security: SecurityPolicy = .keychain(accessibility: .afterFirstUnlock, accessControl: .none) let serializer: Serializer = .json let owner: String = "Modern" let description: String = "Modern integration key" let availability: PlatformAvailability = .all let syncPolicy: SyncPolicy = .never let legacyKey: AnyStorageKey? var migration: AnyStorageMigration? { guard let legacyKey else { return nil } return AnyStorageMigration( SimpleLegacyMigration(destinationKey: self, 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 = LegacyIntegrationKey(name: "legacy.integration", domain: .userDefaults(suite: nil)) let modernKey = ModernIntegrationKey( 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 = LegacyIntegrationKey(name: "legacy.catalog", domain: .userDefaults(suite: nil)) let modernKey = ModernIntegrationKey( 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 = LegacyIntegrationKey(name: "legacy.concurrent", domain: .userDefaults(suite: nil)) let modernKey = ModernIntegrationKey( 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 = ModernIntegrationKey( 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")) } }