Skip to content

Commit afc96c1

Browse files
committed
Strengthen ChangeSupersessionTests
1 parent a3049c7 commit afc96c1

1 file changed

Lines changed: 102 additions & 81 deletions

File tree

Tests/SQLiteDataTests/CloudKitTests/ChangeSupersessionTests.swift

Lines changed: 102 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,14 @@
1111
@MainActor
1212
final class ChangeSupersessionTests: BaseCloudKitTests, @unchecked Sendable {
1313
@available(iOS 17, macOS 14, tvOS 17, watchOS 10, *)
14-
@Test func deleteAndReinsertInSingleWrite() async throws {
15-
try await userDatabase.userWrite { db in
16-
try db.seed {
17-
RemindersList(id: 1, title: "Personal")
18-
}
19-
}
20-
try await syncEngine.processPendingRecordZoneChanges(scope: .private)
21-
14+
@Test func insertThenDelete_deletes() async throws {
2215
try await userDatabase.userWrite { db in
16+
try RemindersList.insert { RemindersList(id: 1, title: "Personal") }.execute(db)
2317
try RemindersList.find(1).delete().execute(db)
24-
try RemindersList.insert { RemindersList(id: 1, title: "Renamed") }.execute(db)
2518
}
2619

2720
let pending = syncEngine.private.state.pendingRecordZoneChanges
28-
#expect(pending == [.saveRecord(RemindersList.recordID(for: 1))])
21+
#expect(pending == [.deleteRecord(RemindersList.recordID(for: 1))])
2922

3023
try await syncEngine.processPendingRecordZoneChanges(scope: .private)
3124

@@ -34,16 +27,7 @@
3427
MockCloudContainer(
3528
privateCloudDatabase: MockCloudDatabase(
3629
databaseScope: .private,
37-
storage: [
38-
[0]: CKRecord(
39-
recordID: CKRecord.ID(1:remindersLists/zone/__defaultOwner__),
40-
recordType: "remindersLists",
41-
parent: nil,
42-
share: nil,
43-
id: 1,
44-
title: "Renamed"
45-
)
46-
]
30+
storage: []
4731
),
4832
sharedCloudDatabase: MockCloudDatabase(
4933
databaseScope: .shared,
@@ -55,23 +39,19 @@
5539
}
5640

5741
@available(iOS 17, macOS 14, tvOS 17, watchOS 10, *)
58-
@Test func deleteAndReinsertInSeparateWrites() async throws {
42+
@Test func updateThenDelete_deletes() async throws {
5943
try await userDatabase.userWrite { db in
60-
try db.seed {
61-
RemindersList(id: 1, title: "Personal")
62-
}
44+
try db.seed { RemindersList(id: 1, title: "Original") }
6345
}
6446
try await syncEngine.processPendingRecordZoneChanges(scope: .private)
6547

6648
try await userDatabase.userWrite { db in
49+
try RemindersList.find(1).update { $0.title = "Updated" }.execute(db)
6750
try RemindersList.find(1).delete().execute(db)
6851
}
69-
try await userDatabase.userWrite { db in
70-
try RemindersList.insert { RemindersList(id: 1, title: "Restored") }.execute(db)
71-
}
7252

7353
let pending = syncEngine.private.state.pendingRecordZoneChanges
74-
#expect(pending == [.saveRecord(RemindersList.recordID(for: 1))])
54+
#expect(pending == [.deleteRecord(RemindersList.recordID(for: 1))])
7555

7656
try await syncEngine.processPendingRecordZoneChanges(scope: .private)
7757

@@ -80,16 +60,7 @@
8060
MockCloudContainer(
8161
privateCloudDatabase: MockCloudDatabase(
8262
databaseScope: .private,
83-
storage: [
84-
[0]: CKRecord(
85-
recordID: CKRecord.ID(1:remindersLists/zone/__defaultOwner__),
86-
recordType: "remindersLists",
87-
parent: nil,
88-
share: nil,
89-
id: 1,
90-
title: "Restored"
91-
)
92-
]
63+
storage: []
9364
),
9465
sharedCloudDatabase: MockCloudDatabase(
9566
databaseScope: .shared,
@@ -101,20 +72,21 @@
10172
}
10273

10374
@available(iOS 17, macOS 14, tvOS 17, watchOS 10, *)
104-
@Test func deleteWithoutReinsert_stillDeletes() async throws {
75+
@Test func deleteAndReinsertInSingleWrite_saves() async throws {
10576
try await userDatabase.userWrite { db in
10677
try db.seed {
107-
RemindersList(id: 1, title: "Personal")
78+
RemindersList(id: 1, title: "Original")
10879
}
10980
}
11081
try await syncEngine.processPendingRecordZoneChanges(scope: .private)
11182

11283
try await userDatabase.userWrite { db in
11384
try RemindersList.find(1).delete().execute(db)
85+
try RemindersList.insert { RemindersList(id: 1, title: "Reinserted") }.execute(db)
11486
}
11587

11688
let pending = syncEngine.private.state.pendingRecordZoneChanges
117-
#expect(pending == [.deleteRecord(RemindersList.recordID(for: 1))])
89+
#expect(pending == [.saveRecord(RemindersList.recordID(for: 1))])
11890

11991
try await syncEngine.processPendingRecordZoneChanges(scope: .private)
12092

@@ -123,7 +95,16 @@
12395
MockCloudContainer(
12496
privateCloudDatabase: MockCloudDatabase(
12597
databaseScope: .private,
126-
storage: []
98+
storage: [
99+
[0]: CKRecord(
100+
recordID: CKRecord.ID(1:remindersLists/zone/__defaultOwner__),
101+
recordType: "remindersLists",
102+
parent: nil,
103+
share: nil,
104+
id: 1,
105+
title: "Reinserted"
106+
)
107+
]
127108
),
128109
sharedCloudDatabase: MockCloudDatabase(
129110
databaseScope: .shared,
@@ -135,17 +116,23 @@
135116
}
136117

137118
@available(iOS 17, macOS 14, tvOS 17, watchOS 10, *)
138-
@Test func deleteReinsertThenDeleteAgain_deletes() async throws {
119+
@Test func deleteAndReinsertInSeparateWrites_saves() async throws {
139120
try await userDatabase.userWrite { db in
140-
try db.seed { RemindersList(id: 1, title: "Original") }
121+
try db.seed {
122+
RemindersList(id: 1, title: "Original")
123+
}
141124
}
142125
try await syncEngine.processPendingRecordZoneChanges(scope: .private)
143126

144127
try await userDatabase.userWrite { db in
145128
try RemindersList.find(1).delete().execute(db)
146-
try RemindersList.insert { RemindersList(id: 1, title: "Middle") }.execute(db)
147-
try RemindersList.find(1).delete().execute(db)
148129
}
130+
try await userDatabase.userWrite { db in
131+
try RemindersList.insert { RemindersList(id: 1, title: "Reinserted") }.execute(db)
132+
}
133+
134+
let pending = syncEngine.private.state.pendingRecordZoneChanges
135+
#expect(pending == [.saveRecord(RemindersList.recordID(for: 1))])
149136

150137
try await syncEngine.processPendingRecordZoneChanges(scope: .private)
151138

@@ -154,7 +141,16 @@
154141
MockCloudContainer(
155142
privateCloudDatabase: MockCloudDatabase(
156143
databaseScope: .private,
157-
storage: []
144+
storage: [
145+
[0]: CKRecord(
146+
recordID: CKRecord.ID(1:remindersLists/zone/__defaultOwner__),
147+
recordType: "remindersLists",
148+
parent: nil,
149+
share: nil,
150+
id: 1,
151+
title: "Reinserted"
152+
)
153+
]
158154
),
159155
sharedCloudDatabase: MockCloudDatabase(
160156
databaseScope: .shared,
@@ -166,19 +162,21 @@
166162
}
167163

168164
@available(iOS 17, macOS 14, tvOS 17, watchOS 10, *)
169-
@Test func balancedDeleteReinsertCycles_savesWithFinalValues() async throws {
165+
@Test func updateThenDeleteThenReinsert_saves() async throws {
170166
try await userDatabase.userWrite { db in
171167
try db.seed { RemindersList(id: 1, title: "Original") }
172168
}
173169
try await syncEngine.processPendingRecordZoneChanges(scope: .private)
174170

175171
try await userDatabase.userWrite { db in
172+
try RemindersList.find(1).update { $0.title = "Updated" }.execute(db)
176173
try RemindersList.find(1).delete().execute(db)
177-
try RemindersList.insert { RemindersList(id: 1, title: "Middle") }.execute(db)
178-
try RemindersList.find(1).delete().execute(db)
179-
try RemindersList.insert { RemindersList(id: 1, title: "Final") }.execute(db)
174+
try RemindersList.insert { RemindersList(id: 1, title: "Reinserted") }.execute(db)
180175
}
181176

177+
let pending = syncEngine.private.state.pendingRecordZoneChanges
178+
#expect(pending == [.saveRecord(RemindersList.recordID(for: 1))])
179+
182180
try await syncEngine.processPendingRecordZoneChanges(scope: .private)
183181

184182
assertInlineSnapshot(of: container, as: .customDump) {
@@ -193,7 +191,7 @@
193191
parent: nil,
194192
share: nil,
195193
id: 1,
196-
title: "Final"
194+
title: "Reinserted"
197195
)
198196
]
199197
),
@@ -207,19 +205,21 @@
207205
}
208206

209207
@available(iOS 17, macOS 14, tvOS 17, watchOS 10, *)
210-
@Test func updateThenDelete_deletes() async throws {
208+
@Test func deleteReinsertThenDeleteAgain_deletes() async throws {
211209
try await userDatabase.userWrite { db in
212210
try db.seed { RemindersList(id: 1, title: "Original") }
213211
}
214212
try await syncEngine.processPendingRecordZoneChanges(scope: .private)
215213

216-
try await userDatabase.userWrite { db in
217-
try RemindersList.find(1).update { $0.title = "Updated" }.execute(db)
218-
}
219214
try await userDatabase.userWrite { db in
220215
try RemindersList.find(1).delete().execute(db)
216+
try RemindersList.insert { RemindersList(id: 1, title: "Reinserted") }.execute(db)
217+
try RemindersList.find(1).delete().execute(db)
221218
}
222219

220+
let pending = syncEngine.private.state.pendingRecordZoneChanges
221+
#expect(pending == [.deleteRecord(RemindersList.recordID(for: 1))])
222+
223223
try await syncEngine.processPendingRecordZoneChanges(scope: .private)
224224

225225
assertInlineSnapshot(of: container, as: .customDump) {
@@ -239,22 +239,28 @@
239239
}
240240

241241
@available(iOS 17, macOS 14, tvOS 17, watchOS 10, *)
242-
@Test func updateThenDeleteThenReinsert_savesWithReinsertedValues() async throws {
242+
@Test(.printTimestamps) func twoDeleteReinsertCyclesInSameWrite_propagatesLatestValueAndTimestamp()
243+
async throws
244+
{
243245
try await userDatabase.userWrite { db in
244246
try db.seed { RemindersList(id: 1, title: "Original") }
245247
}
246248
try await syncEngine.processPendingRecordZoneChanges(scope: .private)
247249

248-
try await userDatabase.userWrite { db in
249-
try RemindersList.find(1).update { $0.title = "Updated" }.execute(db)
250-
}
251-
try await userDatabase.userWrite { db in
252-
try RemindersList.find(1).delete().execute(db)
253-
try RemindersList.insert { RemindersList(id: 1, title: "Reinserted") }.execute(db)
250+
try await withDependencies {
251+
$0.currentTime.now = 1
252+
} operation: {
253+
try await userDatabase.userWrite { db in
254+
try RemindersList.find(1).delete().execute(db)
255+
try RemindersList.insert { RemindersList(id: 1, title: "Middle") }.execute(db)
256+
try RemindersList.find(1).delete().execute(db)
257+
try RemindersList.insert { RemindersList(id: 1, title: "Final") }.execute(db)
258+
}
259+
let pending = syncEngine.private.state.pendingRecordZoneChanges
260+
#expect(pending == [.saveRecord(RemindersList.recordID(for: 1))])
261+
try await syncEngine.processPendingRecordZoneChanges(scope: .private)
254262
}
255263

256-
try await syncEngine.processPendingRecordZoneChanges(scope: .private)
257-
258264
assertInlineSnapshot(of: container, as: .customDump) {
259265
"""
260266
MockCloudContainer(
@@ -267,7 +273,10 @@
267273
parent: nil,
268274
share: nil,
269275
id: 1,
270-
title: "Reinserted"
276+
id🗓️: 0,
277+
title: "Final",
278+
title🗓️: 1,
279+
🗓️: 1
271280
)
272281
]
273282
),
@@ -280,29 +289,38 @@
280289
}
281290
}
282291

283-
// A second delete+reinsert cycle should propagate the cycle-2 field values to CloudKit,
284-
// not the stale cycle-1 values. Regression test for stale userModificationTime timestamps.
285292
@available(iOS 17, macOS 14, tvOS 17, watchOS 10, *)
286-
@Test func secondDeleteAndReinsertPropagatesCycle2Values() async throws {
287-
// Seed and sync initial record.
293+
@Test(.printTimestamps) func twoDeleteReinsertCyclesInSeparateBatches_propagatesLatestValueAndTimestamp()
294+
async throws
295+
{
288296
try await userDatabase.userWrite { db in
289297
try db.seed { RemindersList(id: 1, title: "Original") }
290298
}
291299
try await syncEngine.processPendingRecordZoneChanges(scope: .private)
292300

293-
// Cycle 1: delete + reinsert.
294-
try await userDatabase.userWrite { db in
295-
try RemindersList.find(1).delete().execute(db)
296-
try RemindersList.insert { RemindersList(id: 1, title: "Cycle1") }.execute(db)
301+
try await withDependencies {
302+
$0.currentTime.now = 1
303+
} operation: {
304+
try await userDatabase.userWrite { db in
305+
try RemindersList.find(1).delete().execute(db)
306+
try RemindersList.insert { RemindersList(id: 1, title: "Cycle1") }.execute(db)
307+
}
308+
let pending = syncEngine.private.state.pendingRecordZoneChanges
309+
#expect(pending == [.saveRecord(RemindersList.recordID(for: 1))])
310+
try await syncEngine.processPendingRecordZoneChanges(scope: .private)
297311
}
298-
try await syncEngine.processPendingRecordZoneChanges(scope: .private)
299312

300-
// Cycle 2: delete + reinsert with new value.
301-
try await userDatabase.userWrite { db in
302-
try RemindersList.find(1).delete().execute(db)
303-
try RemindersList.insert { RemindersList(id: 1, title: "Cycle2") }.execute(db)
313+
try await withDependencies {
314+
$0.currentTime.now = 2
315+
} operation: {
316+
try await userDatabase.userWrite { db in
317+
try RemindersList.find(1).delete().execute(db)
318+
try RemindersList.insert { RemindersList(id: 1, title: "Cycle2") }.execute(db)
319+
}
320+
let pending = syncEngine.private.state.pendingRecordZoneChanges
321+
#expect(pending == [.saveRecord(RemindersList.recordID(for: 1))])
322+
try await syncEngine.processPendingRecordZoneChanges(scope: .private)
304323
}
305-
try await syncEngine.processPendingRecordZoneChanges(scope: .private)
306324

307325
assertInlineSnapshot(of: container, as: .customDump) {
308326
"""
@@ -316,7 +334,10 @@
316334
parent: nil,
317335
share: nil,
318336
id: 1,
319-
title: "Cycle2"
337+
id🗓️: 0,
338+
title: "Cycle2",
339+
title🗓️: 2,
340+
🗓️: 2
320341
)
321342
]
322343
),

0 commit comments

Comments
 (0)