Skip to content

Commit 544850b

Browse files
Make integration test subscription behaviour consistent with JS tests
Make sure that we perform the subscription synchronously so as not to miss events. This is a nuance that I missed when looking at Cursor's generated code in fa255c1. The way in which I've chosen to do this is not particularly elegant (and to be honest Swift doesn't make it very easy to do so) but I think that it's the closest to the JS code, which hopefully will mean Cursor will be able to easily apply this pattern when translating further JS tests.
1 parent 1847ff5 commit 544850b

1 file changed

Lines changed: 30 additions & 46 deletions

File tree

Tests/AblyLiveObjectsTests/JS Integration Tests/ObjectsIntegrationTests.swift

Lines changed: 30 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -88,32 +88,12 @@ func waitFixtureChannelIsReady(_: ARTRealtime) async throws {
8888
try await Task.sleep(nanoseconds: 5 * NSEC_PER_SEC)
8989
}
9090

91-
func waitForMapKeyUpdate(_ map: any LiveMap, _ key: String) async throws {
92-
try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<Void, _>) in
93-
do {
94-
try map.subscribe { update, subscription in
95-
if update.update[key] != nil {
96-
subscription.unsubscribe()
97-
continuation.resume()
98-
}
99-
}
100-
} catch {
101-
continuation.resume(throwing: error)
102-
}
103-
}
91+
func waitForMapKeyUpdate(_ updates: AsyncStream<LiveMapUpdate>, _ key: String) async {
92+
_ = await updates.first { $0.update[key] != nil }
10493
}
10594

106-
func waitForCounterUpdate(_ counter: any LiveCounter) async throws {
107-
try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<Void, _>) in
108-
do {
109-
try counter.subscribe { _, subscription in
110-
subscription.unsubscribe()
111-
continuation.resume()
112-
}
113-
} catch {
114-
continuation.resume(throwing: error)
115-
}
116-
}
95+
func waitForCounterUpdate(_ updates: AsyncStream<LiveCounterUpdate>) async {
96+
_ = await updates.first { _ in true }
11797
}
11898

11999
// I added this @MainActor as an "I don't understand what's going on there; let's try this" when observing that for some reason the setter of setListenerAfterProcessingIncomingMessage was hanging inside `-[ARTSRDelegateController dispatchQueue]`. This seems to avoid it and I have not investigated more deeply 🤷
@@ -321,12 +301,14 @@ private struct ObjectsIntegrationTests {
321301
let objects = ctx.objects
322302

323303
// Create the promise first, before the operations that will trigger it
304+
let objectsCreatedPromiseUpdates1 = try root.updates()
305+
let objectsCreatedPromiseUpdates2 = try root.updates()
324306
async let objectsCreatedPromise: Void = withThrowingTaskGroup(of: Void.self) { group in
325307
group.addTask {
326-
try await waitForMapKeyUpdate(root, "counter")
308+
await waitForMapKeyUpdate(objectsCreatedPromiseUpdates1, "counter")
327309
}
328310
group.addTask {
329-
try await waitForMapKeyUpdate(root, "map")
311+
await waitForMapKeyUpdate(objectsCreatedPromiseUpdates2, "map")
330312
}
331313
while try await group.next() != nil {}
332314
}
@@ -342,15 +324,18 @@ private struct ObjectsIntegrationTests {
342324
_ = try await (setMapPromise, setCounterPromise, objectsCreatedPromise)
343325

344326
// Create the promise first, before the operations that will trigger it
327+
let operationsAppliedPromiseUpdates1 = try map.updates()
328+
let operationsAppliedPromiseUpdates2 = try map.updates()
329+
let operationsAppliedPromiseUpdates3 = try counter.updates()
345330
async let operationsAppliedPromise: Void = withThrowingTaskGroup(of: Void.self) { group in
346331
group.addTask {
347-
try await waitForMapKeyUpdate(map, "anotherKey")
332+
await waitForMapKeyUpdate(operationsAppliedPromiseUpdates1, "anotherKey")
348333
}
349334
group.addTask {
350-
try await waitForMapKeyUpdate(map, "shouldDelete")
335+
await waitForMapKeyUpdate(operationsAppliedPromiseUpdates2, "shouldDelete")
351336
}
352337
group.addTask {
353-
try await waitForCounterUpdate(counter)
338+
await waitForCounterUpdate(operationsAppliedPromiseUpdates3)
354339
}
355340
while try await group.next() != nil {}
356341
}
@@ -393,12 +378,14 @@ private struct ObjectsIntegrationTests {
393378
let client = ctx.client
394379

395380
// Create the promise first, before the operations that will trigger it
381+
let objectsCreatedPromiseUpdates1 = try root.updates()
382+
let objectsCreatedPromiseUpdates2 = try root.updates()
396383
async let objectsCreatedPromise: Void = withThrowingTaskGroup(of: Void.self) { group in
397384
group.addTask {
398-
try await waitForMapKeyUpdate(root, "counter")
385+
await waitForMapKeyUpdate(objectsCreatedPromiseUpdates1, "counter")
399386
}
400387
group.addTask {
401-
try await waitForMapKeyUpdate(root, "map")
388+
await waitForMapKeyUpdate(objectsCreatedPromiseUpdates2, "map")
402389
}
403390
while try await group.next() != nil {}
404391
}
@@ -588,14 +575,15 @@ private struct ObjectsIntegrationTests {
588575
let channelName = ctx.channelName
589576
let channel = ctx.channel
590577

591-
async let counterCreatedPromise: Void = waitForMapKeyUpdate(root, "counter")
578+
let counterCreatedPromiseUpdates = try root.updates()
579+
async let counterCreatedPromise: Void = waitForMapKeyUpdate(counterCreatedPromiseUpdates, "counter")
592580
let counterResult = try await objectsHelper.createAndSetOnMap(
593581
channelName: channelName,
594582
mapObjectId: "root",
595583
key: "counter",
596584
createOp: objectsHelper.counterCreateRestOp(number: 1),
597585
)
598-
_ = try await counterCreatedPromise
586+
_ = await counterCreatedPromise
599587

600588
#expect(try root.get(key: "counter") != nil, "Check counter exists on root before OBJECT_SYNC sequence with \"tombstone=true\"")
601589

@@ -642,25 +630,21 @@ private struct ObjectsIntegrationTests {
642630
let channelName = ctx.channelName
643631
let channel = ctx.channel
644632

645-
async let counterCreatedPromise: Void = waitForMapKeyUpdate(root, "counter")
633+
let counterCreatedPromiseUpdates = try root.updates()
634+
async let counterCreatedPromise: Void = waitForMapKeyUpdate(counterCreatedPromiseUpdates, "counter")
646635
let counterResult = try await objectsHelper.createAndSetOnMap(
647636
channelName: channelName,
648637
mapObjectId: "root",
649638
key: "counter",
650639
createOp: objectsHelper.counterCreateRestOp(number: 1),
651640
)
652-
_ = try await counterCreatedPromise
653-
654-
async let counterSubPromise: Void = withCheckedThrowingContinuation { continuation in
655-
do {
656-
try #require(root.get(key: "counter")?.liveCounterValue).subscribe { update, _ in
657-
#expect(update.amount == -1, "Check counter subscription callback is called with an expected update object after OBJECT_SYNC sequence with \"tombstone=true\"")
658-
continuation.resume()
659-
}
660-
} catch {
661-
continuation.resume(throwing: error)
662-
}
663-
}
641+
_ = await counterCreatedPromise
642+
643+
let counterSubPromiseUpdates = try #require(root.get(key: "counter")?.liveCounterValue).updates()
644+
async let counterSubPromise: Void = {
645+
let update = try await #require(counterSubPromiseUpdates.first { _ in true })
646+
#expect(update.amount == -1, "Check counter subscription callback is called with an expected update object after OBJECT_SYNC sequence with \"tombstone=true\"")
647+
}()
664648

665649
// inject an OBJECT_SYNC message where a counter is now tombstoned
666650
try await objectsHelper.processObjectStateMessageOnChannel(

0 commit comments

Comments
 (0)