162 lines
6.5 KiB
Swift
162 lines
6.5 KiB
Swift
import Foundation
|
|
import Testing
|
|
@testable import LocalData
|
|
|
|
private struct LegacyStringKey: StorageKey {
|
|
typealias Value = String
|
|
let name: String
|
|
let domain: StorageDomain
|
|
let security: SecurityPolicy = .none
|
|
let serializer: Serializer<String> = .json
|
|
let owner: String = "Legacy"
|
|
let description: String = "Legacy key"
|
|
let availability: PlatformAvailability = .all
|
|
let syncPolicy: SyncPolicy = .never
|
|
}
|
|
|
|
private struct DestinationStringKey: StorageKey {
|
|
typealias Value = String
|
|
let name: String
|
|
let domain: StorageDomain
|
|
let security: SecurityPolicy = .keychain(accessibility: .afterFirstUnlock, accessControl: .none)
|
|
let serializer: Serializer<String> = .json
|
|
let owner: String = "Destination"
|
|
let description: String = "Destination key"
|
|
let availability: PlatformAvailability = .all
|
|
let syncPolicy: SyncPolicy = .never
|
|
}
|
|
|
|
private struct SourceStringKey: StorageKey {
|
|
typealias Value = String
|
|
let name: String
|
|
let domain: StorageDomain
|
|
let security: SecurityPolicy = .none
|
|
let serializer: Serializer<String> = .json
|
|
let owner: String = "Source"
|
|
let description: String = "Source key"
|
|
let availability: PlatformAvailability = .all
|
|
let syncPolicy: SyncPolicy = .never
|
|
}
|
|
|
|
private struct DestinationIntKey: StorageKey {
|
|
typealias Value = Int
|
|
let name: String
|
|
let domain: StorageDomain
|
|
let security: SecurityPolicy = .none
|
|
let serializer: Serializer<Int> = .json
|
|
let owner: String = "Destination"
|
|
let description: String = "Destination int key"
|
|
let availability: PlatformAvailability = .all
|
|
let syncPolicy: SyncPolicy = .never
|
|
}
|
|
|
|
@Suite(.serialized)
|
|
struct MigrationProtocolTests {
|
|
private let router: StorageRouter
|
|
|
|
init() {
|
|
let testBaseURL = FileManager.default.temporaryDirectory.appending(path: "MigrationProtocolTests-\(UUID().uuidString)")
|
|
router = StorageRouter(
|
|
keychain: MockKeychainHelper(),
|
|
encryption: EncryptionHelper(keychain: MockKeychainHelper()),
|
|
file: FileStorageHelper(configuration: FileStorageConfiguration(baseURL: testBaseURL)),
|
|
defaults: UserDefaultsHelper(defaults: UserDefaults(suiteName: "MigrationProtocolTests.\(UUID().uuidString)")!)
|
|
)
|
|
}
|
|
|
|
@Test func simpleLegacyMigrationTest() async throws {
|
|
let legacyKey = LegacyStringKey(name: "legacy.simple", domain: .userDefaults(suite: nil))
|
|
let destinationKey = DestinationStringKey(name: "modern.simple", domain: .keychain(service: "test.migration"))
|
|
try await router.set("value", for: legacyKey)
|
|
|
|
let migration = SimpleLegacyMigration(destinationKey: destinationKey, sourceKey: .key(legacyKey))
|
|
let context = MigrationContext(appVersion: "1.0")
|
|
|
|
let shouldMigrate = try await migration.shouldMigrate(using: router, context: context)
|
|
#expect(shouldMigrate == true)
|
|
|
|
let result = try await migration.migrate(using: router, context: context)
|
|
#expect(result.success == true)
|
|
|
|
let migratedValue = try await router.get(destinationKey)
|
|
#expect(migratedValue == "value")
|
|
}
|
|
|
|
@Test func conditionalMigrationTest() async throws {
|
|
let legacyKey = LegacyStringKey(name: "legacy.conditional", domain: .userDefaults(suite: nil))
|
|
let destinationKey = DestinationStringKey(name: "modern.conditional", domain: .keychain(service: "test.migration"))
|
|
try await router.set("value", for: legacyKey)
|
|
|
|
let fallback = AnyStorageMigration(
|
|
SimpleLegacyMigration(destinationKey: destinationKey, sourceKey: .key(legacyKey))
|
|
)
|
|
let migration = AppVersionConditionalMigration(
|
|
destinationKey: destinationKey,
|
|
minAppVersion: "2.0",
|
|
fallbackMigration: fallback
|
|
)
|
|
|
|
let context = MigrationContext(appVersion: "1.0")
|
|
let shouldMigrate = try await migration.shouldMigrate(using: router, context: context)
|
|
#expect(shouldMigrate == true)
|
|
|
|
let result = try await migration.migrate(using: router, context: context)
|
|
#expect(result.success == true)
|
|
}
|
|
|
|
@Test func transformingMigrationTest() async throws {
|
|
let sourceKey = SourceStringKey(name: "legacy.transform", domain: .userDefaults(suite: nil))
|
|
let destinationKey = DestinationIntKey(name: "modern.transform", domain: .userDefaults(suite: nil))
|
|
try await router.set("42", for: sourceKey)
|
|
|
|
let migration = DefaultTransformingMigration(
|
|
destinationKey: destinationKey,
|
|
sourceKey: sourceKey
|
|
) { value in
|
|
guard let intValue = Int(value) else {
|
|
throw MigrationError.transformationFailed("Invalid integer")
|
|
}
|
|
return intValue
|
|
}
|
|
|
|
let result = try await migration.migrate(using: router, context: MigrationContext())
|
|
#expect(result.success == true)
|
|
|
|
let migratedValue = try await router.get(destinationKey)
|
|
#expect(migratedValue == 42)
|
|
}
|
|
|
|
@Test func aggregatingMigrationTest() async throws {
|
|
let sourceKeyA = SourceStringKey(name: "legacy.aggregate.a", domain: .userDefaults(suite: nil))
|
|
let sourceKeyB = SourceStringKey(name: "legacy.aggregate.b", domain: .userDefaults(suite: nil))
|
|
let destinationKey = DestinationStringKey(name: "modern.aggregate", domain: .userDefaults(suite: nil))
|
|
|
|
try await router.set("alpha", for: sourceKeyA)
|
|
try await router.set("beta", for: sourceKeyB)
|
|
|
|
let migration = DefaultAggregatingMigration(
|
|
destinationKey: destinationKey,
|
|
sourceKeys: [.key(sourceKeyA), .key(sourceKeyB)]
|
|
) { sources in
|
|
let values = sources.compactMap { $0.value as? String }
|
|
return values.joined(separator: ",")
|
|
}
|
|
|
|
let result = try await migration.migrate(using: router, context: MigrationContext())
|
|
#expect(result.success == true)
|
|
|
|
let migratedValue = try await router.get(destinationKey)
|
|
#expect(migratedValue.contains("alpha"))
|
|
#expect(migratedValue.contains("beta"))
|
|
}
|
|
|
|
@Test func migrationErrorHandlingTest() async throws {
|
|
let destinationKey = DestinationStringKey(name: "modern.error", domain: .userDefaults(suite: nil))
|
|
let migration = FailingMigration(destinationKey: destinationKey, error: .transformationFailed("Failed"))
|
|
let result = try await migration.migrate(using: router, context: MigrationContext())
|
|
|
|
#expect(result.success == false)
|
|
#expect(result.errors.first == .transformationFailed("Failed"))
|
|
}
|
|
}
|