import Foundation import Testing @testable import LocalData private func makeLegacyKey(name: String, domain: StorageDomain) -> StorageKey { StorageKey( name: name, domain: domain, security: .none, owner: "Legacy", description: "Legacy key" ) } private func makeModernKey( name: String, domain: StorageDomain, legacyKey: AnyStorageKey? ) -> StorageKey { StorageKey( name: name, domain: domain, security: .keychain(accessibility: .afterFirstUnlock, accessControl: .none), owner: "Modern", description: "Modern key", migration: { destinationKey in guard let legacyKey else { return nil } return AnyStorageMigration( SimpleLegacyMigration(destinationKey: destinationKey, sourceKey: legacyKey) ) } ) } @Suite(.serialized) struct MigrationTests { private let router: StorageRouter init() { let testBaseURL = FileManager.default.temporaryDirectory.appending(path: "MigrationTests-\(UUID().uuidString)") router = StorageRouter( keychain: MockKeychainHelper(), encryption: EncryptionHelper(keychain: MockKeychainHelper()), file: FileStorageHelper(configuration: FileStorageConfiguration(baseURL: testBaseURL)), defaults: UserDefaultsHelper(defaults: UserDefaults(suiteName: "MigrationTests-\(UUID().uuidString)")!) ) } @Test func automaticMigrationFromUserDefaultsToKeychain() async throws { let legacyName = "legacy.user.name" let modernName = "user.name" let suiteName = "MigrationTests.\(UUID().uuidString)" let secretValue = "Matt Bruce" defer { UserDefaults().removePersistentDomain(forName: suiteName) } // 1. Setup legacy data manually in UserDefaults let legacyKey = makeLegacyKey(name: legacyName, domain: .userDefaults(suite: suiteName)) try await router.set(secretValue, for: legacyKey) // Verify it exists in legacy location let existsInLegacy = try await router.exists(legacyKey) #expect(existsInLegacy == true) // 2. Setup modern key with legacy source let modernKey = makeModernKey( name: modernName, domain: .keychain(service: "test.migration"), legacyKey: .key(legacyKey) ) // 3. Trigger automatic migration via GET let migratedValue = try await router.get(modernKey) #expect(migratedValue == secretValue) // 4. Verify data moved // Modern should now exist let existsInModern = try await router.exists(modernKey) #expect(existsInModern == true) // Legacy should be gone let existsInLegacyAfter = try await router.exists(legacyKey) #expect(existsInLegacyAfter == false) } @Test func manualMigrationSweep() async throws { let legacyName = "legacy.manual.key" let modernName = "modern.manual.key" let suiteName = "MigrationTests.Manual.\(UUID().uuidString)" let value = "Manual Data" defer { UserDefaults().removePersistentDomain(forName: suiteName) } // 1. Setup legacy data let legacyKey = makeLegacyKey(name: legacyName, domain: .userDefaults(suite: suiteName)) try await router.set(value, for: legacyKey) // 2. Setup modern key let modernKey = makeModernKey( name: modernName, domain: .userDefaults(suite: suiteName), legacyKey: .key(legacyKey) ) // 3. Trigger manual migration _ = try await router.forceMigration(for: modernKey) // 4. Verify let hasModern = try await router.exists(modernKey) #expect(hasModern == true) let hasLegacy = try await router.exists(legacyKey) #expect(hasLegacy == false) } }