From 8e3e754e434bb326a82920880e31722c89d5853d Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Sat, 17 Jan 2026 09:18:04 -0600 Subject: [PATCH] Tests: update tests for AnyStorageKeyTests.swift, AuditTests.swift, LocalDataTests.swift (+13 more) Summary: - Tests: update tests for AnyStorageKeyTests.swift, AuditTests.swift, LocalDataTests.swift (+13 more) Stats: - 16 files changed, 329 insertions(+), 386 deletions(-) --- Tests/LocalDataTests/AnyStorageKeyTests.swift | 22 ++- Tests/LocalDataTests/AuditTests.swift | 46 +++--- Tests/LocalDataTests/LocalDataTests.swift | 52 +++---- .../MigrationAdditionalTests.swift | 131 +++++++++--------- .../MigrationIntegrationTests.swift | 68 +++++---- .../MigrationProtocolTests.swift | 98 +++++++------ Tests/LocalDataTests/MigrationTests.swift | 68 ++++----- .../LocalDataTests/Mocks/MockMigration.swift | 12 +- .../LocalDataTests/ModularRegistryTests.swift | 34 ++--- Tests/LocalDataTests/RouterDomainTests.swift | 47 +++---- Tests/LocalDataTests/RouterErrorTests.swift | 26 ++-- .../LocalDataTests/RouterSecurityTests.swift | 32 +++-- .../LocalDataTests/StorageCatalogTests.swift | 36 ++--- .../StorageKeyDefaultsTests.swift | 18 +-- .../LocalDataTests/SyncIntegrationTests.swift | 23 ++- Tests/TestPlan.xctestplan | 2 +- 16 files changed, 329 insertions(+), 386 deletions(-) diff --git a/Tests/LocalDataTests/AnyStorageKeyTests.swift b/Tests/LocalDataTests/AnyStorageKeyTests.swift index eeabf51..de74ef1 100644 --- a/Tests/LocalDataTests/AnyStorageKeyTests.swift +++ b/Tests/LocalDataTests/AnyStorageKeyTests.swift @@ -4,20 +4,18 @@ import Testing @Suite struct AnyStorageKeyTests { - private struct StringKey: StorageKey { - typealias Value = String - let name: String - let domain: StorageDomain = .userDefaults(suite: nil) - let security: SecurityPolicy = .none - let serializer: Serializer = .json - let owner: String = "Test" - let description: String = "Test" - let availability: PlatformAvailability = .all - let syncPolicy: SyncPolicy = .never + private func makeStringKey(name: String) -> StorageKey { + StorageKey( + name: name, + domain: .userDefaults(suite: nil), + security: .none, + owner: "Test", + description: "Test" + ) } @Test func anyStorageKeyCapturesDescriptor() { - let key = StringKey(name: "test.key") + let key = makeStringKey(name: "test.key") let anyKey = AnyStorageKey.key(key) #expect(anyKey.descriptor.name == "test.key") @@ -27,7 +25,7 @@ import Testing @Test func anyStorageKeyTriggersMigration() async throws { let router = StorageRouter(keychain: MockKeychainHelper()) - let key = StringKey(name: "test.key") + let key = makeStringKey(name: "test.key") let anyKey = AnyStorageKey.key(key) // This will call router.forceMigration(for: key) diff --git a/Tests/LocalDataTests/AuditTests.swift b/Tests/LocalDataTests/AuditTests.swift index ce503f3..ee4a9f4 100644 --- a/Tests/LocalDataTests/AuditTests.swift +++ b/Tests/LocalDataTests/AuditTests.swift @@ -7,31 +7,35 @@ import Testing private struct AuditCatalog: StorageKeyCatalog { var allKeys: [AnyStorageKey] { [ - .key(TestKey(name: "k1", domain: .userDefaults(suite: nil))), - .key(TestKey(name: "k2", domain: .keychain(service: "s"), security: .keychain(accessibility: .afterFirstUnlock, accessControl: .userPresence))), - .key(TestKey(name: "k3", domain: .fileSystem(directory: .documents))), - .key(TestKey(name: "k4", domain: .encryptedFileSystem(directory: .caches))), - .key(TestKey(name: "k5", domain: .appGroupUserDefaults(identifier: "ig"), security: .encrypted(.recommended))) + .key(AuditTests.makeTestKey(name: "k1", domain: .userDefaults(suite: nil))), + .key(AuditTests.makeTestKey( + name: "k2", + domain: .keychain(service: "s"), + security: .keychain(accessibility: .afterFirstUnlock, accessControl: .userPresence) + )), + .key(AuditTests.makeTestKey(name: "k3", domain: .fileSystem(directory: .documents))), + .key(AuditTests.makeTestKey(name: "k4", domain: .encryptedFileSystem(directory: .caches))), + .key(AuditTests.makeTestKey( + name: "k5", + domain: .appGroupUserDefaults(identifier: "ig"), + security: .encrypted(.recommended) + )) ] } } - private struct TestKey: StorageKey { - typealias Value = String - let name: String - let domain: StorageDomain - let security: SecurityPolicy - let serializer: Serializer = .json - let owner: String = "Audit" - let description: String = "Desc" - let availability: PlatformAvailability = .all - let syncPolicy: SyncPolicy = .never - - init(name: String, domain: StorageDomain, security: SecurityPolicy = .none) { - self.name = name - self.domain = domain - self.security = security - } + private static func makeTestKey( + name: String, + domain: StorageDomain, + security: SecurityPolicy = .none + ) -> StorageKey { + StorageKey( + name: name, + domain: domain, + security: security, + owner: "Audit", + description: "Desc" + ) } @Test func renderCatalogText() { diff --git a/Tests/LocalDataTests/LocalDataTests.swift b/Tests/LocalDataTests/LocalDataTests.swift index 74c404c..b18e2e4 100644 --- a/Tests/LocalDataTests/LocalDataTests.swift +++ b/Tests/LocalDataTests/LocalDataTests.swift @@ -2,40 +2,24 @@ import Foundation import Testing @testable import LocalData -private struct TestUserDefaultsKey: StorageKey { - typealias Value = String - - let name: String - let domain: StorageDomain - let security: SecurityPolicy = .none - let serializer: Serializer = .json - let owner: String = "LocalDataTests" - let description: String = "Test-only key for user defaults round-trip." - let availability: PlatformAvailability = .all - let syncPolicy: SyncPolicy = .never - - init(name: String, suiteName: String) { - self.name = name - self.domain = .userDefaults(suite: suiteName) - } +private func makeUserDefaultsKey(name: String, suiteName: String) -> StorageKey { + StorageKey( + name: name, + domain: .userDefaults(suite: suiteName), + security: .none, + owner: "LocalDataTests", + description: "Test-only key for user defaults round-trip." + ) } -private struct TestFileKey: StorageKey { - typealias Value = String - - let name: String - let domain: StorageDomain - let security: SecurityPolicy = .none - let serializer: Serializer = .json - let owner: String = "LocalDataTests" - let description: String = "Test-only key for file system round-trip." - let availability: PlatformAvailability = .all - let syncPolicy: SyncPolicy = .never - - init(name: String, directory: URL) { - self.name = name - self.domain = .fileSystem(directory: .custom(directory)) - } +private func makeFileKey(name: String, directory: URL) -> StorageKey { + StorageKey( + name: name, + domain: .fileSystem(directory: .custom(directory)), + security: .none, + owner: "LocalDataTests", + description: "Test-only key for file system round-trip." + ) } @Suite(.serialized) @@ -50,7 +34,7 @@ struct LocalDataTests { } } - let key = TestUserDefaultsKey(name: "test.string", suiteName: suiteName) + let key = makeUserDefaultsKey(name: "test.string", suiteName: suiteName) let storedValue = "1.0.0" try await router.set(storedValue, for: key) @@ -72,7 +56,7 @@ struct LocalDataTests { try? FileManager.default.removeItem(at: tempDirectory) } - let key = TestFileKey(name: "test.json", directory: tempDirectory) + let key = makeFileKey(name: "test.json", directory: tempDirectory) let storedValue = "payload" try await router.set(storedValue, for: key) diff --git a/Tests/LocalDataTests/MigrationAdditionalTests.swift b/Tests/LocalDataTests/MigrationAdditionalTests.swift index 532f8f8..d81ab88 100644 --- a/Tests/LocalDataTests/MigrationAdditionalTests.swift +++ b/Tests/LocalDataTests/MigrationAdditionalTests.swift @@ -2,72 +2,65 @@ 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 = .json - let owner: String = "Legacy" - let description: String = "Legacy string key" - let availability: PlatformAvailability = .all - let syncPolicy: SyncPolicy = .never +private func makeLegacyStringKey(name: String, domain: StorageDomain) -> StorageKey { + StorageKey( + name: name, + domain: domain, + security: .none, + owner: "Legacy", + description: "Legacy string key" + ) } -private struct ModernStringKey: StorageKey { - typealias Value = String - let name: String - let domain: StorageDomain - let security: SecurityPolicy = .none - let serializer: Serializer = .json - let owner: String = "Modern" - let description: String = "Modern string 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 func makeModernStringKey( + name: String, + domain: StorageDomain, + legacyKey: AnyStorageKey? +) -> StorageKey { + StorageKey( + name: name, + domain: domain, + security: .none, + owner: "Modern", + description: "Modern string key", + migration: { destinationKey in + guard let legacyKey else { return nil } + return AnyStorageMigration( + SimpleLegacyMigration(destinationKey: destinationKey, sourceKey: legacyKey) + ) + } + ) } -private struct PhoneOnlyKey: StorageKey { - typealias Value = String - let name: String - let domain: StorageDomain - let security: SecurityPolicy = .none - let serializer: Serializer = .json - let owner: String = "PhoneOnly" - let description: String = "Phone-only key" - let availability: PlatformAvailability = .phoneOnly - let syncPolicy: SyncPolicy = .never +private func makePhoneOnlyKey(name: String, domain: StorageDomain) -> StorageKey { + StorageKey( + name: name, + domain: domain, + security: .none, + owner: "PhoneOnly", + description: "Phone-only key", + availability: .phoneOnly + ) } -private struct SourceStringKey: StorageKey { - typealias Value = String - let name: String - let domain: StorageDomain - let security: SecurityPolicy = .none - let serializer: Serializer = .json - let owner: String = "Source" - let description: String = "Source key" - let availability: PlatformAvailability = .all - let syncPolicy: SyncPolicy = .never +private func makeSourceStringKey(name: String, domain: StorageDomain) -> StorageKey { + StorageKey( + name: name, + domain: domain, + security: .none, + owner: "Source", + description: "Source key" + ) } -private struct DestinationIntKey: StorageKey { - typealias Value = Int - let name: String - let domain: StorageDomain - let security: SecurityPolicy = .none - let serializer: Serializer = .json - let owner: String = "Destination" - let description: String = "Destination int key" - let availability: PlatformAvailability = .all - let syncPolicy: SyncPolicy = .never +private func makeDestinationIntKey(name: String, domain: StorageDomain) -> StorageKey { + StorageKey( + name: name, + domain: domain, + security: .none, + owner: "Destination", + description: "Destination int key" + ) } @Suite(.serialized) @@ -85,8 +78,8 @@ struct MigrationAdditionalTests { } @Test func migrationHistoryTrackingTest() async throws { - let legacyKey = LegacyStringKey(name: "legacy.history", domain: .userDefaults(suite: nil)) - let modernKey = ModernStringKey( + let legacyKey = makeLegacyStringKey(name: "legacy.history", domain: .userDefaults(suite: nil)) + let modernKey = makeModernStringKey( name: "modern.history", domain: .userDefaults(suite: nil), legacyKey: .key(legacyKey) @@ -95,13 +88,13 @@ struct MigrationAdditionalTests { try await router.set("history", for: legacyKey) _ = try await router.forceMigration(for: modernKey) - let history = router.migrationHistory(for: modernKey) + let history = await router.migrationHistory(for: modernKey) #expect(history != nil) } @Test func migrationFailureKeepsSourceTest() async throws { - let sourceKey = SourceStringKey(name: "legacy.rollback", domain: .userDefaults(suite: nil)) - let destinationKey = DestinationIntKey(name: "modern.rollback", domain: .userDefaults(suite: nil)) + let sourceKey = makeSourceStringKey(name: "legacy.rollback", domain: .userDefaults(suite: nil)) + let destinationKey = makeDestinationIntKey(name: "modern.rollback", domain: .userDefaults(suite: nil)) try await router.set("not-a-number", for: sourceKey) @@ -119,8 +112,8 @@ struct MigrationAdditionalTests { } @Test func watchAvailabilityBlocksMigrationTest() async throws { - let legacyKey = LegacyStringKey(name: "legacy.watch", domain: .userDefaults(suite: nil)) - let destinationKey = PhoneOnlyKey(name: "modern.watch", domain: .userDefaults(suite: nil)) + let legacyKey = makeLegacyStringKey(name: "legacy.watch", domain: .userDefaults(suite: nil)) + let destinationKey = makePhoneOnlyKey(name: "modern.watch", domain: .userDefaults(suite: nil)) let migration = SimpleLegacyMigration(destinationKey: destinationKey, sourceKey: .key(legacyKey)) let deviceInfo = DeviceInfo( @@ -136,8 +129,8 @@ struct MigrationAdditionalTests { } @Test func largeDataMigrationTest() async throws { - let legacyKey = LegacyStringKey(name: "legacy.large", domain: .userDefaults(suite: nil)) - let modernKey = ModernStringKey( + let legacyKey = makeLegacyStringKey(name: "legacy.large", domain: .userDefaults(suite: nil)) + let modernKey = makeModernStringKey( name: "modern.large", domain: .userDefaults(suite: nil), legacyKey: .key(legacyKey) @@ -155,8 +148,8 @@ struct MigrationAdditionalTests { } @Test func typeErasureMigrationTest() async throws { - let legacyKey = LegacyStringKey(name: "legacy.erased", domain: .userDefaults(suite: nil)) - let modernKey = ModernStringKey( + let legacyKey = makeLegacyStringKey(name: "legacy.erased", domain: .userDefaults(suite: nil)) + let modernKey = makeModernStringKey( name: "modern.erased", domain: .userDefaults(suite: nil), legacyKey: .key(legacyKey) diff --git a/Tests/LocalDataTests/MigrationIntegrationTests.swift b/Tests/LocalDataTests/MigrationIntegrationTests.swift index 85593e0..bb8cd64 100644 --- a/Tests/LocalDataTests/MigrationIntegrationTests.swift +++ b/Tests/LocalDataTests/MigrationIntegrationTests.swift @@ -2,36 +2,34 @@ 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 func makeLegacyIntegrationKey(name: String, domain: StorageDomain) -> StorageKey { + StorageKey( + name: name, + domain: domain, + security: .none, + owner: "Legacy", + description: "Legacy integration key" + ) } -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 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 { @@ -53,8 +51,8 @@ struct MigrationIntegrationTests { } @Test func endToEndMigrationTest() async throws { - let legacyKey = LegacyIntegrationKey(name: "legacy.integration", domain: .userDefaults(suite: nil)) - let modernKey = ModernIntegrationKey( + let legacyKey = makeLegacyIntegrationKey(name: "legacy.integration", domain: .userDefaults(suite: nil)) + let modernKey = makeModernIntegrationKey( name: "modern.integration", domain: .keychain(service: "test.migration"), legacyKey: .key(legacyKey) @@ -69,8 +67,8 @@ struct MigrationIntegrationTests { } @Test func migrationRegistrationTest() async throws { - let legacyKey = LegacyIntegrationKey(name: "legacy.catalog", domain: .userDefaults(suite: nil)) - let modernKey = ModernIntegrationKey( + let legacyKey = makeLegacyIntegrationKey(name: "legacy.catalog", domain: .userDefaults(suite: nil)) + let modernKey = makeModernIntegrationKey( name: "modern.catalog", domain: .keychain(service: "test.migration"), legacyKey: .key(legacyKey) @@ -86,8 +84,8 @@ struct MigrationIntegrationTests { } @Test func concurrentMigrationTest() async throws { - let legacyKey = LegacyIntegrationKey(name: "legacy.concurrent", domain: .userDefaults(suite: nil)) - let modernKey = ModernIntegrationKey( + let legacyKey = makeLegacyIntegrationKey(name: "legacy.concurrent", domain: .userDefaults(suite: nil)) + let modernKey = makeModernIntegrationKey( name: "modern.concurrent", domain: .keychain(service: "test.migration"), legacyKey: .key(legacyKey) @@ -105,7 +103,7 @@ struct MigrationIntegrationTests { } @Test func migrationFailureResultTest() async throws { - let destinationKey = ModernIntegrationKey( + let destinationKey = makeModernIntegrationKey( name: "modern.failure", domain: .userDefaults(suite: nil), legacyKey: nil diff --git a/Tests/LocalDataTests/MigrationProtocolTests.swift b/Tests/LocalDataTests/MigrationProtocolTests.swift index 1b446da..d90e25e 100644 --- a/Tests/LocalDataTests/MigrationProtocolTests.swift +++ b/Tests/LocalDataTests/MigrationProtocolTests.swift @@ -2,52 +2,44 @@ 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 = .json - let owner: String = "Legacy" - let description: String = "Legacy key" - let availability: PlatformAvailability = .all - let syncPolicy: SyncPolicy = .never +private func makeLegacyStringKey(name: String, domain: StorageDomain) -> StorageKey { + StorageKey( + name: name, + domain: domain, + security: .none, + owner: "Legacy", + description: "Legacy key" + ) } -private struct DestinationStringKey: 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 = "Destination" - let description: String = "Destination key" - let availability: PlatformAvailability = .all - let syncPolicy: SyncPolicy = .never +private func makeDestinationStringKey(name: String, domain: StorageDomain) -> StorageKey { + StorageKey( + name: name, + domain: domain, + security: .keychain(accessibility: .afterFirstUnlock, accessControl: .none), + owner: "Destination", + description: "Destination key" + ) } -private struct SourceStringKey: StorageKey { - typealias Value = String - let name: String - let domain: StorageDomain - let security: SecurityPolicy = .none - let serializer: Serializer = .json - let owner: String = "Source" - let description: String = "Source key" - let availability: PlatformAvailability = .all - let syncPolicy: SyncPolicy = .never +private func makeSourceStringKey(name: String, domain: StorageDomain) -> StorageKey { + StorageKey( + name: name, + domain: domain, + security: .none, + owner: "Source", + description: "Source key" + ) } -private struct DestinationIntKey: StorageKey { - typealias Value = Int - let name: String - let domain: StorageDomain - let security: SecurityPolicy = .none - let serializer: Serializer = .json - let owner: String = "Destination" - let description: String = "Destination int key" - let availability: PlatformAvailability = .all - let syncPolicy: SyncPolicy = .never +private func makeDestinationIntKey(name: String, domain: StorageDomain) -> StorageKey { + StorageKey( + name: name, + domain: domain, + security: .none, + owner: "Destination", + description: "Destination int key" + ) } @Suite(.serialized) @@ -65,8 +57,11 @@ struct MigrationProtocolTests { } @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")) + let legacyKey = makeLegacyStringKey(name: "legacy.simple", domain: .userDefaults(suite: nil)) + let destinationKey = makeDestinationStringKey( + name: "modern.simple", + domain: .keychain(service: "test.migration") + ) try await router.set("value", for: legacyKey) let migration = SimpleLegacyMigration(destinationKey: destinationKey, sourceKey: .key(legacyKey)) @@ -83,8 +78,11 @@ struct MigrationProtocolTests { } @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")) + let legacyKey = makeLegacyStringKey(name: "legacy.conditional", domain: .userDefaults(suite: nil)) + let destinationKey = makeDestinationStringKey( + name: "modern.conditional", + domain: .keychain(service: "test.migration") + ) try await router.set("value", for: legacyKey) let fallback = AnyStorageMigration( @@ -105,8 +103,8 @@ struct MigrationProtocolTests { } @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)) + let sourceKey = makeSourceStringKey(name: "legacy.transform", domain: .userDefaults(suite: nil)) + let destinationKey = makeDestinationIntKey(name: "modern.transform", domain: .userDefaults(suite: nil)) try await router.set("42", for: sourceKey) let migration = DefaultTransformingMigration( @@ -127,9 +125,9 @@ struct MigrationProtocolTests { } @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)) + let sourceKeyA = makeSourceStringKey(name: "legacy.aggregate.a", domain: .userDefaults(suite: nil)) + let sourceKeyB = makeSourceStringKey(name: "legacy.aggregate.b", domain: .userDefaults(suite: nil)) + let destinationKey = makeDestinationStringKey(name: "modern.aggregate", domain: .userDefaults(suite: nil)) try await router.set("alpha", for: sourceKeyA) try await router.set("beta", for: sourceKeyB) @@ -151,7 +149,7 @@ struct MigrationProtocolTests { } @Test func migrationErrorHandlingTest() async throws { - let destinationKey = DestinationStringKey(name: "modern.error", domain: .userDefaults(suite: nil)) + let destinationKey = makeDestinationStringKey(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()) diff --git a/Tests/LocalDataTests/MigrationTests.swift b/Tests/LocalDataTests/MigrationTests.swift index b2d0e79..5d34f62 100644 --- a/Tests/LocalDataTests/MigrationTests.swift +++ b/Tests/LocalDataTests/MigrationTests.swift @@ -2,42 +2,34 @@ import Foundation import Testing @testable import LocalData -private struct LegacyKey: 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 key" - let availability: PlatformAvailability = .all - let syncPolicy: SyncPolicy = .never +private func makeLegacyKey(name: String, domain: StorageDomain) -> StorageKey { + StorageKey( + name: name, + domain: domain, + security: .none, + owner: "Legacy", + description: "Legacy key" + ) } -private struct ModernKey: 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 key" - let availability: PlatformAvailability = .all - let syncPolicy: SyncPolicy = .never - let legacyKey: AnyStorageKey? - - init(name: String, domain: StorageDomain, legacyKey: AnyStorageKey?) { - self.name = name - self.domain = domain - self.legacyKey = legacyKey - } - - var migration: AnyStorageMigration? { - guard let legacyKey else { return nil } - return AnyStorageMigration( - SimpleLegacyMigration(destinationKey: self, sourceKey: legacyKey) - ) - } +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) @@ -65,7 +57,7 @@ struct MigrationTests { } // 1. Setup legacy data manually in UserDefaults - let legacyKey = LegacyKey(name: legacyName, domain: .userDefaults(suite: suiteName)) + let legacyKey = makeLegacyKey(name: legacyName, domain: .userDefaults(suite: suiteName)) try await router.set(secretValue, for: legacyKey) // Verify it exists in legacy location @@ -73,7 +65,7 @@ struct MigrationTests { #expect(existsInLegacy == true) // 2. Setup modern key with legacy source - let modernKey = ModernKey( + let modernKey = makeModernKey( name: modernName, domain: .keychain(service: "test.migration"), legacyKey: .key(legacyKey) @@ -104,11 +96,11 @@ struct MigrationTests { } // 1. Setup legacy data - let legacyKey = LegacyKey(name: legacyName, domain: .userDefaults(suite: suiteName)) + let legacyKey = makeLegacyKey(name: legacyName, domain: .userDefaults(suite: suiteName)) try await router.set(value, for: legacyKey) // 2. Setup modern key - let modernKey = ModernKey( + let modernKey = makeModernKey( name: modernName, domain: .userDefaults(suite: suiteName), legacyKey: .key(legacyKey) diff --git a/Tests/LocalDataTests/Mocks/MockMigration.swift b/Tests/LocalDataTests/Mocks/MockMigration.swift index 26de09e..d51f413 100644 --- a/Tests/LocalDataTests/Mocks/MockMigration.swift +++ b/Tests/LocalDataTests/Mocks/MockMigration.swift @@ -1,10 +1,8 @@ import Foundation @testable import LocalData -struct MockMigration: StorageMigration { - typealias DestinationKey = Destination - - let destinationKey: Destination +struct MockMigration: StorageMigration { + let destinationKey: StorageKey let shouldSucceed: Bool let shouldMigrateResult: Bool let migrationDelay: TimeInterval @@ -28,10 +26,8 @@ struct MockMigration: StorageMigration { } } -struct FailingMigration: StorageMigration { - typealias DestinationKey = Destination - - let destinationKey: Destination +struct FailingMigration: StorageMigration { + let destinationKey: StorageKey let error: MigrationError func shouldMigrate(using router: StorageRouter, context: MigrationContext) async throws -> Bool { true } diff --git a/Tests/LocalDataTests/ModularRegistryTests.swift b/Tests/LocalDataTests/ModularRegistryTests.swift index 581940f..08324a5 100644 --- a/Tests/LocalDataTests/ModularRegistryTests.swift +++ b/Tests/LocalDataTests/ModularRegistryTests.swift @@ -2,39 +2,35 @@ import Foundation import Testing @testable import LocalData -private struct TestRegistryKey: StorageKey { - typealias Value = String - let name: String - let domain: StorageDomain = .userDefaults(suite: nil) - let security: SecurityPolicy = .none - let serializer: Serializer = .json - let owner: String - let description: String - let availability: PlatformAvailability = .all - let syncPolicy: SyncPolicy = .never - - init(name: String, owner: String = "Test", description: String = "Test") { - self.name = name - self.owner = owner - self.description = description - } +private func makeRegistryKey( + name: String, + owner: String = "Test", + description: String = "Test" +) -> StorageKey { + StorageKey( + name: name, + domain: .userDefaults(suite: nil), + security: .none, + owner: owner, + description: description + ) } private struct CatalogA: StorageKeyCatalog { var allKeys: [AnyStorageKey] { - [.key(TestRegistryKey(name: "key.a", owner: "ModuleA"))] + [.key(makeRegistryKey(name: "key.a", owner: "ModuleA"))] } } private struct CatalogB: StorageKeyCatalog { var allKeys: [AnyStorageKey] { - [.key(TestRegistryKey(name: "key.b", owner: "ModuleB"))] + [.key(makeRegistryKey(name: "key.b", owner: "ModuleB"))] } } private struct CatalogCollision: StorageKeyCatalog { var allKeys: [AnyStorageKey] { - [.key(TestRegistryKey(name: "key.a", owner: "ModuleCollision"))] + [.key(makeRegistryKey(name: "key.a", owner: "ModuleCollision"))] } } diff --git a/Tests/LocalDataTests/RouterDomainTests.swift b/Tests/LocalDataTests/RouterDomainTests.swift index bdf54c2..2926a42 100644 --- a/Tests/LocalDataTests/RouterDomainTests.swift +++ b/Tests/LocalDataTests/RouterDomainTests.swift @@ -16,26 +16,22 @@ import Testing ) } - private struct DomainKey: StorageKey { - typealias Value = String - let name: String - let domain: StorageDomain - let security: SecurityPolicy - let serializer: Serializer = .json - let owner: String = "DomainTests" - let description: String = "Domain test key" - let availability: PlatformAvailability = .all - let syncPolicy: SyncPolicy = .never - - init(name: String, domain: StorageDomain, security: SecurityPolicy = .none) { - self.name = name - self.domain = domain - self.security = security - } + private func makeDomainKey( + name: String, + domain: StorageDomain, + security: SecurityPolicy = .none + ) -> StorageKey { + StorageKey( + name: name, + domain: domain, + security: security, + owner: "DomainTests", + description: "Domain test key" + ) } @Test func domainUserDefaults() async throws { - let key = DomainKey(name: "defaults.key", domain: .userDefaults(suite: nil)) + let key = makeDomainKey(name: "defaults.key", domain: .userDefaults(suite: nil)) try await router.set("value", for: key) #expect(try await router.get(key) == "value") try await router.remove(key) @@ -46,14 +42,14 @@ import Testing // We use a mock configuration to avoid requiring a real app group await router.updateStorageConfiguration(StorageConfiguration(defaultAppGroupIdentifier: "group.test")) - let key = DomainKey(name: "appgroup.defaults.key", domain: .appGroupUserDefaults(identifier: "group.test")) + let key = makeDomainKey(name: "appgroup.defaults.key", domain: .appGroupUserDefaults(identifier: "group.test")) try await router.set("value", for: key) #expect(try await router.get(key) == "value") try await router.remove(key) } @Test func domainKeychain() async throws { - let key = DomainKey( + let key = makeDomainKey( name: "keychain.key", domain: .keychain(service: "test"), security: .keychain(accessibility: .afterFirstUnlock, accessControl: .none) @@ -64,14 +60,14 @@ import Testing } @Test func domainFileSystem() async throws { - let key = DomainKey(name: "file.key", domain: .fileSystem(directory: .documents)) + let key = makeDomainKey(name: "file.key", domain: .fileSystem(directory: .documents)) try await router.set("value", for: key) #expect(try await router.get(key) == "value") try await router.remove(key) } @Test func domainEncryptedFileSystem() async throws { - let key = DomainKey(name: "encfile.key", domain: .encryptedFileSystem(directory: .documents)) + let key = makeDomainKey(name: "encfile.key", domain: .encryptedFileSystem(directory: .documents)) try await router.set("value", for: key) #expect(try await router.get(key) == "value") try await router.remove(key) @@ -80,7 +76,10 @@ import Testing @Test func domainAppGroupFileSystem() async throws { // App blocks usually fail or return nil in tests, but we exercise the path await router.updateStorageConfiguration(StorageConfiguration(defaultAppGroupIdentifier: "group.test")) - let key = DomainKey(name: "appgroup.file.key", domain: .appGroupFileSystem(identifier: "group.test", directory: .documents)) + let key = makeDomainKey( + name: "appgroup.file.key", + domain: .appGroupFileSystem(identifier: "group.test", directory: .documents) + ) do { try await router.set("value", for: key) @@ -94,7 +93,7 @@ import Testing @Test func resolutionFailureService() async throws { // Clear default service await router.updateStorageConfiguration(StorageConfiguration(defaultKeychainService: nil)) - let key = DomainKey( + let key = makeDomainKey( name: "bad.service.key", domain: .keychain(service: nil), security: .keychain(accessibility: .afterFirstUnlock, accessControl: .none) @@ -108,7 +107,7 @@ import Testing @Test func resolutionFailureIdentifier() async throws { // Clear default identifier await router.updateStorageConfiguration(StorageConfiguration(defaultAppGroupIdentifier: nil)) - let key = DomainKey(name: "bad.id.key", domain: .appGroupUserDefaults(identifier: nil)) + let key = makeDomainKey(name: "bad.id.key", domain: .appGroupUserDefaults(identifier: nil)) await #expect(throws: StorageError.invalidAppGroupIdentifier("none")) { try await router.set("value", for: key) diff --git a/Tests/LocalDataTests/RouterErrorTests.swift b/Tests/LocalDataTests/RouterErrorTests.swift index b784674..c5f0a57 100644 --- a/Tests/LocalDataTests/RouterErrorTests.swift +++ b/Tests/LocalDataTests/RouterErrorTests.swift @@ -2,21 +2,19 @@ import Foundation import Testing @testable import LocalData -private struct MockKey: StorageKey { - typealias Value = String - let name: String - let domain: StorageDomain - let security: SecurityPolicy = .none - let serializer: Serializer = .json - let owner: String = "ErrorTests" - let description: String = "Test key" - let availability: PlatformAvailability = .all - let syncPolicy: SyncPolicy = .never +private func makeMockKey(name: String, domain: StorageDomain) -> StorageKey { + StorageKey( + name: name, + domain: domain, + security: .none, + owner: "ErrorTests", + description: "Test key" + ) } private struct PartialCatalog: StorageKeyCatalog { var allKeys: [AnyStorageKey] { - [.key(MockKey(name: "registered.key", domain: .userDefaults(suite: nil)))] + [.key(makeMockKey(name: "registered.key", domain: .userDefaults(suite: nil)))] } } @@ -37,7 +35,7 @@ struct RouterErrorTests { @Test func unregisteredKeyThrows() async throws { try await router.registerCatalog(PartialCatalog()) - let badKey = MockKey(name: "unregistered.key", domain: .userDefaults(suite: nil)) + let badKey = makeMockKey(name: "unregistered.key", domain: .userDefaults(suite: nil)) await #expect(throws: StorageError.unregisteredKey("unregistered.key")) { try await router.set("value", for: badKey) @@ -51,7 +49,7 @@ struct RouterErrorTests { defaultAppGroupIdentifier: nil )) - let appGroupKey = MockKey(name: "appgroup.key", domain: .appGroupUserDefaults(identifier: nil)) + let appGroupKey = makeMockKey(name: "appgroup.key", domain: .appGroupUserDefaults(identifier: nil)) await #expect(throws: StorageError.invalidAppGroupIdentifier("none")) { try await router.set("value", for: appGroupKey) @@ -65,7 +63,7 @@ struct RouterErrorTests { defaultAppGroupIdentifier: "test" )) - let _ = MockKey(name: "keychain.key", domain: .keychain(service: nil)) + let _ = makeMockKey(name: "keychain.key", domain: .keychain(service: nil)) // Note: Keychain security policy must match keychain domain in descriptor // but descriptor is usually created from key. diff --git a/Tests/LocalDataTests/RouterSecurityTests.swift b/Tests/LocalDataTests/RouterSecurityTests.swift index c892bdb..fa5cb4e 100644 --- a/Tests/LocalDataTests/RouterSecurityTests.swift +++ b/Tests/LocalDataTests/RouterSecurityTests.swift @@ -17,20 +17,22 @@ import Security ) } - private struct SecurityKey: StorageKey { - typealias Value = String - let name: String - let domain: StorageDomain - let security: SecurityPolicy - let serializer: Serializer = .json - let owner: String = "SecurityTests" - let description: String = "Security test key" - let availability: PlatformAvailability = .all - let syncPolicy: SyncPolicy = .never + private func makeSecurityKey( + name: String, + domain: StorageDomain, + security: SecurityPolicy + ) -> StorageKey { + StorageKey( + name: name, + domain: domain, + security: security, + owner: "SecurityTests", + description: "Security test key" + ) } @Test func applySecurityNone() async throws { - let key = SecurityKey(name: "none.key", domain: .userDefaults(suite: nil), security: .none) + let key = makeSecurityKey(name: "none.key", domain: .userDefaults(suite: nil), security: .none) let value = "test-value" try await router.set(value, for: key) @@ -39,7 +41,7 @@ import Security } @Test func applySecurityEncryptedAES() async throws { - let key = SecurityKey( + let key = makeSecurityKey( name: "aes.key", domain: .userDefaults(suite: nil), security: .encrypted(.aes256(keyDerivation: .hkdf())) @@ -52,7 +54,7 @@ import Security } @Test func applySecurityEncryptedChaCha() async throws { - let key = SecurityKey( + let key = makeSecurityKey( name: "chacha.key", domain: .userDefaults(suite: nil), security: .encrypted(.chacha20Poly1305(keyDerivation: .hkdf())) @@ -65,7 +67,7 @@ import Security } @Test func applySecurityKeychain() async throws { - let key = SecurityKey( + let key = makeSecurityKey( name: "keychain.key", domain: .keychain(service: "test-service"), security: .keychain(accessibility: .afterFirstUnlock, accessControl: .none) @@ -78,7 +80,7 @@ import Security } @Test func applySecurityPBKDF2() async throws { - let key = SecurityKey( + let key = makeSecurityKey( name: "pbkdf2.key", domain: .userDefaults(suite: nil), security: .encrypted(.aes256(keyDerivation: .pbkdf2())) diff --git a/Tests/LocalDataTests/StorageCatalogTests.swift b/Tests/LocalDataTests/StorageCatalogTests.swift index c3141b7..b4b3c05 100644 --- a/Tests/LocalDataTests/StorageCatalogTests.swift +++ b/Tests/LocalDataTests/StorageCatalogTests.swift @@ -4,22 +4,14 @@ import Testing // MARK: - Test Keys -private struct TestCatalogKey: StorageKey { - typealias Value = String - - let name: String - let domain: StorageDomain = .userDefaults(suite: nil) - let security: SecurityPolicy = .none - let serializer: Serializer = .json - let owner: String = "CatalogTests" - let description: String - let availability: PlatformAvailability = .all - let syncPolicy: SyncPolicy = .never - - init(name: String, description: String = "Test key") { - self.name = name - self.description = description - } +private func makeCatalogKey(name: String, description: String = "Test key") -> StorageKey { + StorageKey( + name: name, + domain: .userDefaults(suite: nil), + security: .none, + owner: "CatalogTests", + description: description + ) } // MARK: - Test Catalogs @@ -27,8 +19,8 @@ private struct TestCatalogKey: StorageKey { private struct ValidCatalog: StorageKeyCatalog { var allKeys: [AnyStorageKey] { [ - .key(TestCatalogKey(name: "valid.key1", description: "First test key")), - .key(TestCatalogKey(name: "valid.key2", description: "Second test key")) + .key(makeCatalogKey(name: "valid.key1", description: "First test key")), + .key(makeCatalogKey(name: "valid.key2", description: "Second test key")) ] } } @@ -36,8 +28,8 @@ private struct ValidCatalog: StorageKeyCatalog { private struct DuplicateNameCatalog: StorageKeyCatalog { var allKeys: [AnyStorageKey] { [ - .key(TestCatalogKey(name: "duplicate.name", description: "First instance")), - .key(TestCatalogKey(name: "duplicate.name", description: "Second instance")) + .key(makeCatalogKey(name: "duplicate.name", description: "First instance")), + .key(makeCatalogKey(name: "duplicate.name", description: "Second instance")) ] } } @@ -49,7 +41,7 @@ private struct EmptyCatalog: StorageKeyCatalog { private struct MissingDescriptionCatalog: StorageKeyCatalog { var allKeys: [AnyStorageKey] { [ - .key(TestCatalogKey(name: "missing.desc", description: " ")) + .key(makeCatalogKey(name: "missing.desc", description: " ")) ] } } @@ -78,7 +70,7 @@ struct StorageCatalogTests { } @Test func descriptorCapturesKeyMetadata() { - let key = TestCatalogKey(name: "metadata.test", description: "Metadata test key") + let key = makeCatalogKey(name: "metadata.test", description: "Metadata test key") let anyKey = AnyStorageKey.key(key) let descriptor = anyKey.descriptor diff --git a/Tests/LocalDataTests/StorageKeyDefaultsTests.swift b/Tests/LocalDataTests/StorageKeyDefaultsTests.swift index 357a0d4..0b947d0 100644 --- a/Tests/LocalDataTests/StorageKeyDefaultsTests.swift +++ b/Tests/LocalDataTests/StorageKeyDefaultsTests.swift @@ -4,19 +4,13 @@ import Testing @Suite struct StorageKeyDefaultsTests { - private struct MinimalKey: StorageKey { - typealias Value = Int - let name: String = "minimal.key" - let domain: StorageDomain = .userDefaults(suite: nil) - let serializer: Serializer = .json - let owner: String = "Test" - let description: String = "Test" - let availability: PlatformAvailability = .all - let syncPolicy: SyncPolicy = .never - } - @Test func defaultSecurityPolicyIsRecommended() { - let key = MinimalKey() + let key = StorageKey( + name: "minimal.key", + domain: .userDefaults(suite: nil), + owner: "Test", + description: "Test" + ) // This exercises the default implementation in StorageKey+Defaults.swift #expect(key.security == .recommended) } diff --git a/Tests/LocalDataTests/SyncIntegrationTests.swift b/Tests/LocalDataTests/SyncIntegrationTests.swift index 818a0c2..8a47513 100644 --- a/Tests/LocalDataTests/SyncIntegrationTests.swift +++ b/Tests/LocalDataTests/SyncIntegrationTests.swift @@ -16,21 +16,20 @@ struct SyncIntegrationTests { ) } - private struct SyncKey: StorageKey { - typealias Value = String - let name: String - let domain: StorageDomain = .userDefaults(suite: nil) - let security: SecurityPolicy = .none - let serializer: Serializer = .json - let owner: String = "SyncTests" - let description: String = "Sync key" - let availability: PlatformAvailability = .all - let syncPolicy: SyncPolicy = .automaticSmall + private static func makeSyncKey(name: String) -> StorageKey { + StorageKey( + name: name, + domain: .userDefaults(suite: nil), + security: .none, + owner: "SyncTests", + description: "Sync key", + syncPolicy: .automaticSmall + ) } private struct SyncCatalog: StorageKeyCatalog { var allKeys: [AnyStorageKey] { - [.key(SyncKey(name: "sync.test.key"))] + [.key(SyncIntegrationTests.makeSyncKey(name: "sync.test.key"))] } } @@ -46,7 +45,7 @@ struct SyncIntegrationTests { try await router.updateFromSync(keyName: keyName, data: data) // 3. Verify it was stored in the local domain - let retrieved: String? = try await router.get(SyncKey(name: keyName)) + let retrieved: String? = try await router.get(Self.makeSyncKey(name: keyName)) #expect(retrieved == expectedValue) } diff --git a/Tests/TestPlan.xctestplan b/Tests/TestPlan.xctestplan index 9433d36..ffc967c 100644 --- a/Tests/TestPlan.xctestplan +++ b/Tests/TestPlan.xctestplan @@ -1,7 +1,7 @@ { "configurations" : [ { - "id" : "D2951487-F388-4A07-A1E8-3A4B179619B9", + "id" : "CB38B4BA-86AE-457E-B74C-31A492DEB330", "name" : "Configuration 1", "options" : {