diff --git a/Sources/SQLiteData/CloudKit/SyncEngine.swift b/Sources/SQLiteData/CloudKit/SyncEngine.swift index 438e715d..2b341c14 100644 --- a/Sources/SQLiteData/CloudKit/SyncEngine.swift +++ b/Sources/SQLiteData/CloudKit/SyncEngine.swift @@ -1670,7 +1670,7 @@ } func deleteShare(shareRecordID: CKRecord.ID) async throws { - let shareAndRecordNameAndZone = try await userDatabase.read { db in + let shareAndRecordNameAndZone = try await metadatabase.read { db in try SyncMetadata .where(\.isShared) .select { ($0.share, $0.recordName, $0.zoneName, $0.ownerName) } diff --git a/Tests/SQLiteDataTests/CloudKitTests/AttachedMetadatabaseTests.swift b/Tests/SQLiteDataTests/CloudKitTests/AttachedMetadatabaseTests.swift new file mode 100644 index 00000000..6a199936 --- /dev/null +++ b/Tests/SQLiteDataTests/CloudKitTests/AttachedMetadatabaseTests.swift @@ -0,0 +1,69 @@ +#if canImport(CloudKit) + import CloudKit + import ConcurrencyExtras + import CustomDump + import InlineSnapshotTesting + import OrderedCollections + import SQLiteData + import SQLiteDataTestSupport + import SnapshotTestingCustomDump + import Testing + + extension BaseCloudKitTests { + @MainActor + @Suite(.attachMetadatabase(true)) + final class AttachedMetadatabaseTests: BaseCloudKitTests, @unchecked Sendable { + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) + @Test func basics() async throws { + let remindersList = RemindersList(id: 1, title: "Personal") + try await userDatabase.userWrite { db in + try db.seed { + remindersList + } + } + try await syncEngine.processPendingRecordZoneChanges(scope: .private) + + assertQuery( + RemindersList + .leftJoin(SyncMetadata.all) { $0.syncMetadataID.eq($1.id) }, + database: userDatabase.database + ) { + """ + ┌─────────────────────┬────────────────────────────────────────────────────────────────────┐ + │ RemindersList( │ SyncMetadata( │ + │ id: 1, │ id: SyncMetadata.ID( │ + │ title: "Personal" │ recordPrimaryKey: "1", │ + │ ) │ recordType: "remindersLists" │ + │ │ ), │ + │ │ zoneName: "zone", │ + │ │ ownerName: "__defaultOwner__", │ + │ │ recordName: "1:remindersLists", │ + │ │ parentRecordID: nil, │ + │ │ parentRecordName: nil, │ + │ │ lastKnownServerRecord: CKRecord( │ + │ │ recordID: CKRecord.ID(1:remindersLists/zone/__defaultOwner__), │ + │ │ recordType: "remindersLists", │ + │ │ parent: nil, │ + │ │ share: nil │ + │ │ ), │ + │ │ _lastKnownServerRecordAllFields: CKRecord( │ + │ │ recordID: CKRecord.ID(1:remindersLists/zone/__defaultOwner__), │ + │ │ recordType: "remindersLists", │ + │ │ parent: nil, │ + │ │ share: nil, │ + │ │ id: 1, │ + │ │ title: "Personal" │ + │ │ ), │ + │ │ share: nil, │ + │ │ _isDeleted: false, │ + │ │ hasLastKnownServerRecord: true, │ + │ │ isShared: false, │ + │ │ userModificationTime: 0 │ + │ │ ) │ + └─────────────────────┴────────────────────────────────────────────────────────────────────┘ + """ + } + } + } + } +#endif diff --git a/Tests/SQLiteDataTests/CloudKitTests/SharingTests.swift b/Tests/SQLiteDataTests/CloudKitTests/SharingTests.swift index fc03ab5f..973d8f95 100644 --- a/Tests/SQLiteDataTests/CloudKitTests/SharingTests.swift +++ b/Tests/SQLiteDataTests/CloudKitTests/SharingTests.swift @@ -2753,6 +2753,52 @@ """ } } + + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) + @Test func deleteShare() async throws { + let remindersList = RemindersList(id: 1, title: "Personal") + try await userDatabase.userWrite { db in + try db.seed { + remindersList + } + } + try await syncEngine.processPendingRecordZoneChanges(scope: .private) + + let sharedRecord = try await syncEngine.share(record: remindersList, configure: { _ in }) + + try await syncEngine + .modifyRecords(scope: .private, deleting: [sharedRecord.share.recordID]) + .notify() + + assertQuery(SyncMetadata.select(\.share), database: syncEngine.metadatabase) { + """ + ┌─────┐ + │ nil │ + └─────┘ + """ + } + assertInlineSnapshot(of: container, as: .customDump) { + """ + MockCloudContainer( + privateCloudDatabase: MockCloudDatabase( + databaseScope: .private, + storage: [ + [0]: CKRecord( + recordID: CKRecord.ID(1:remindersLists/zone/__defaultOwner__), + recordType: "remindersLists", + parent: nil, + share: CKReference(recordID: CKRecord.ID(share-1:remindersLists/zone/__defaultOwner__)) + ) + ] + ), + sharedCloudDatabase: MockCloudDatabase( + databaseScope: .shared, + storage: [] + ) + ) + """ + } + } } } #endif diff --git a/Tests/SQLiteDataTests/Internal/BaseCloudKitTests.swift b/Tests/SQLiteDataTests/Internal/BaseCloudKitTests.swift index c7f23d2c..4aec1bc8 100644 --- a/Tests/SQLiteDataTests/Internal/BaseCloudKitTests.swift +++ b/Tests/SQLiteDataTests/Internal/BaseCloudKitTests.swift @@ -11,7 +11,8 @@ import os .dependencies { $0.currentTime.now = 0 $0.dataManager = InMemoryDataManager() - } + }, + .attachMetadatabase(false) ) class BaseCloudKitTests: @unchecked Sendable { let userDatabase: UserDatabase diff --git a/Tests/SQLiteDataTests/Internal/CloudKitTestHelpers.swift b/Tests/SQLiteDataTests/Internal/CloudKitTestHelpers.swift index 29677c2c..70c082c0 100644 --- a/Tests/SQLiteDataTests/Internal/CloudKitTestHelpers.swift +++ b/Tests/SQLiteDataTests/Internal/CloudKitTestHelpers.swift @@ -20,9 +20,10 @@ extension PrimaryKeyedTable where PrimaryKey.QueryOutput: IdentifierStringConver @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) extension SyncEngine { - struct ModifyRecordsCallback { - fileprivate let operation: @Sendable () async -> Void - func notify() async { + struct ModifyRecordsCallback { + fileprivate let operation: @Sendable () async -> ReturnValue + @discardableResult + func notify() async -> ReturnValue { await operation() } } @@ -31,7 +32,12 @@ extension SyncEngine { scope: CKDatabase.Scope, saving recordZonesToSave: [CKRecordZone] = [], deleting recordZoneIDsToDelete: [CKRecordZone.ID] = [] - ) throws -> ModifyRecordsCallback { + ) throws -> ModifyRecordsCallback< + ( + saveResults: [CKRecordZone.ID: Result], + deleteResults: [CKRecordZone.ID: Result] + ) + > { let syncEngine = syncEngine(for: scope) let (saveResults, deleteResults) = try syncEngine.database.modifyRecordZones( @@ -52,6 +58,7 @@ extension SyncEngine { ), syncEngine: syncEngine ) + return (saveResults, deleteResults) } } @@ -59,7 +66,12 @@ extension SyncEngine { scope: CKDatabase.Scope, saving recordsToSave: [CKRecord] = [], deleting recordIDsToDelete: [CKRecord.ID] = [] - ) throws -> ModifyRecordsCallback { + ) throws -> ModifyRecordsCallback< + ( + saveResults: [CKRecord.ID: Result], + deleteResults: [CKRecord.ID: Result] + ) + > { let syncEngine = syncEngine(for: scope) let recordsToDeleteByID = Dictionary( grouping: syncEngine.database.storage.withValue { storage in @@ -90,6 +102,7 @@ extension SyncEngine { ), syncEngine: syncEngine ) + return (saveResults, deleteResults) } }