Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Sources/SQLiteData/CloudKit/SyncEngine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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) }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#if canImport(CloudKit)
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a new test suite to specifically test joining to the metadatabase when it is attached.

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
46 changes: 46 additions & 0 deletions Tests/SQLiteDataTests/CloudKitTests/SharingTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2753,6 +2753,52 @@
"""
}
}

@available(iOS 17, macOS 14, tvOS 17, watchOS 10, *)
@Test func deleteShare() async throws {
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test was failing prior to the change made in this PR.

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
3 changes: 2 additions & 1 deletion Tests/SQLiteDataTests/Internal/BaseCloudKitTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import os
.dependencies {
$0.currentTime.now = 0
$0.dataManager = InMemoryDataManager()
}
},
.attachMetadatabase(false)
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's not attach the metadatabase in tests by default, and instead opt into it explicitly.

)
class BaseCloudKitTests: @unchecked Sendable {
let userDatabase: UserDatabase
Expand Down
23 changes: 18 additions & 5 deletions Tests/SQLiteDataTests/Internal/CloudKitTestHelpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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<ReturnValue> {
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently when we invoke notify() in tests it doesn't return the results from the modify… call. So I updated this bit of library code to allow for that in future tests that may want to assert on that information directly.

fileprivate let operation: @Sendable () async -> ReturnValue
@discardableResult
func notify() async -> ReturnValue {
await operation()
}
}
Expand All @@ -31,7 +32,12 @@ extension SyncEngine {
scope: CKDatabase.Scope,
saving recordZonesToSave: [CKRecordZone] = [],
deleting recordZoneIDsToDelete: [CKRecordZone.ID] = []
) throws -> ModifyRecordsCallback {
) throws -> ModifyRecordsCallback<
(
saveResults: [CKRecordZone.ID: Result<CKRecordZone, any Error>],
deleteResults: [CKRecordZone.ID: Result<Void, any Error>]
)
> {
let syncEngine = syncEngine(for: scope)

let (saveResults, deleteResults) = try syncEngine.database.modifyRecordZones(
Expand All @@ -52,14 +58,20 @@ extension SyncEngine {
),
syncEngine: syncEngine
)
return (saveResults, deleteResults)
}
}

func modifyRecords(
scope: CKDatabase.Scope,
saving recordsToSave: [CKRecord] = [],
deleting recordIDsToDelete: [CKRecord.ID] = []
) throws -> ModifyRecordsCallback {
) throws -> ModifyRecordsCallback<
(
saveResults: [CKRecord.ID: Result<CKRecord, any Error>],
deleteResults: [CKRecord.ID: Result<Void, any Error>]
)
> {
let syncEngine = syncEngine(for: scope)
let recordsToDeleteByID = Dictionary(
grouping: syncEngine.database.storage.withValue { storage in
Expand Down Expand Up @@ -90,6 +102,7 @@ extension SyncEngine {
),
syncEngine: syncEngine
)
return (saveResults, deleteResults)
}
}

Expand Down