@powersync/service-module-mongodb 0.0.0-dev-20260602073133 → 0.0.0-dev-20260909133214

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/CHANGELOG.md +153 -5
  2. package/dist/api/MongoRouteAPIAdapter.d.ts +4 -2
  3. package/dist/api/MongoRouteAPIAdapter.js +23 -32
  4. package/dist/api/MongoRouteAPIAdapter.js.map +1 -1
  5. package/dist/common/SentinelLSN.d.ts +37 -0
  6. package/dist/common/SentinelLSN.js +59 -0
  7. package/dist/common/SentinelLSN.js.map +1 -0
  8. package/dist/module/MongoModule.js +2 -1
  9. package/dist/module/MongoModule.js.map +1 -1
  10. package/dist/replication/ChangeStream.d.ts +24 -0
  11. package/dist/replication/ChangeStream.js +203 -67
  12. package/dist/replication/ChangeStream.js.map +1 -1
  13. package/dist/replication/ChangeStreamReplicationJob.js +3 -2
  14. package/dist/replication/ChangeStreamReplicationJob.js.map +1 -1
  15. package/dist/replication/ChangeStreamReplicator.js +1 -1
  16. package/dist/replication/ChangeStreamReplicator.js.map +1 -1
  17. package/dist/replication/MongoRelation.d.ts +36 -1
  18. package/dist/replication/MongoRelation.js +133 -18
  19. package/dist/replication/MongoRelation.js.map +1 -1
  20. package/dist/replication/MongoSnapshotter.d.ts +9 -1
  21. package/dist/replication/MongoSnapshotter.js +129 -67
  22. package/dist/replication/MongoSnapshotter.js.map +1 -1
  23. package/dist/replication/RawChangeStream.d.ts +4 -1
  24. package/dist/replication/RawChangeStream.js +6 -10
  25. package/dist/replication/RawChangeStream.js.map +1 -1
  26. package/dist/replication/checkpoints/CheckpointImplementation.d.ts +135 -0
  27. package/dist/replication/checkpoints/CheckpointImplementation.js +22 -0
  28. package/dist/replication/checkpoints/CheckpointImplementation.js.map +1 -0
  29. package/dist/replication/checkpoints/SentinelCheckpointImplementation.d.ts +58 -0
  30. package/dist/replication/checkpoints/SentinelCheckpointImplementation.js +224 -0
  31. package/dist/replication/checkpoints/SentinelCheckpointImplementation.js.map +1 -0
  32. package/dist/replication/checkpoints/TimestampCheckpointImplementation.d.ts +27 -0
  33. package/dist/replication/checkpoints/TimestampCheckpointImplementation.js +116 -0
  34. package/dist/replication/checkpoints/TimestampCheckpointImplementation.js.map +1 -0
  35. package/dist/replication/checkpoints/create-checkpoint-implementation.d.ts +6 -0
  36. package/dist/replication/checkpoints/create-checkpoint-implementation.js +10 -0
  37. package/dist/replication/checkpoints/create-checkpoint-implementation.js.map +1 -0
  38. package/dist/replication/replication-utils.d.ts +6 -0
  39. package/dist/replication/replication-utils.js +19 -2
  40. package/dist/replication/replication-utils.js.map +1 -1
  41. package/dist/types/types.d.ts +7 -2
  42. package/dist/types/types.js +20 -2
  43. package/dist/types/types.js.map +1 -1
  44. package/package.json +11 -21
  45. package/src/api/MongoRouteAPIAdapter.ts +28 -39
  46. package/src/common/SentinelLSN.ts +78 -0
  47. package/src/module/MongoModule.ts +2 -1
  48. package/src/replication/ChangeStream.ts +223 -78
  49. package/src/replication/ChangeStreamReplicationJob.ts +3 -2
  50. package/src/replication/ChangeStreamReplicator.ts +1 -1
  51. package/src/replication/MongoRelation.ts +153 -21
  52. package/src/replication/MongoSnapshotter.ts +148 -73
  53. package/src/replication/RawChangeStream.ts +27 -32
  54. package/src/replication/checkpoints/CheckpointImplementation.ts +167 -0
  55. package/src/replication/checkpoints/SentinelCheckpointImplementation.ts +269 -0
  56. package/src/replication/checkpoints/TimestampCheckpointImplementation.ts +145 -0
  57. package/src/replication/checkpoints/create-checkpoint-implementation.ts +14 -0
  58. package/src/replication/replication-utils.ts +23 -2
  59. package/src/types/types.ts +27 -2
  60. package/test/DOCUMENTDB_TESTING.md +115 -0
  61. package/test/src/DatabaseType.ts +25 -0
  62. package/test/src/change_stream.test.ts +180 -71
  63. package/test/src/change_stream_utils.ts +117 -29
  64. package/test/src/checkpoint_retry.test.ts +134 -0
  65. package/test/src/config.test.ts +34 -0
  66. package/test/src/documentdb_helpers.test.ts +124 -0
  67. package/test/src/documentdb_mode.test.ts +1039 -0
  68. package/test/src/mongo_test.test.ts +15 -5
  69. package/test/src/raw_change_stream.test.ts +147 -126
  70. package/test/src/resume.test.ts +2 -2
  71. package/test/src/resume_token.test.ts +30 -0
  72. package/test/src/slow_tests.test.ts +4 -1
  73. package/test/src/test-timeouts.ts +23 -0
  74. package/test/src/util.ts +14 -32
  75. package/tsconfig.json +1 -4
  76. package/tsconfig.scripts.json +1 -2
  77. package/tsconfig.tsbuildinfo +1 -1
@@ -3,12 +3,14 @@ import { setTimeout } from 'node:timers/promises';
3
3
  import { describe, expect, test, vi } from 'vitest';
4
4
 
5
5
  import { mongo } from '@powersync/lib-service-mongodb';
6
- import { createWriteCheckpoint, storage } from '@powersync/service-core';
6
+ import { createWriteCheckpoint, storage, WriteCheckpointBatcher } from '@powersync/service-core';
7
7
  import { test_utils } from '@powersync/service-core-tests';
8
8
 
9
9
  import { MongoRouteAPIAdapter } from '@module/api/MongoRouteAPIAdapter.js';
10
10
  import { PostImagesOption } from '@module/types/types.js';
11
11
  import { ChangeStreamTestContext } from './change_stream_utils.js';
12
+ import { DATABASE_TYPE, DatabaseType } from './DatabaseType.js';
13
+ import { testTimeout } from './test-timeouts.js';
12
14
  import { describeWithStorage, StorageVersionTestContext, TEST_CONNECTION_OPTIONS } from './util.js';
13
15
 
14
16
  const BASIC_SYNC_RULES = `
@@ -19,7 +21,7 @@ bucket_definitions:
19
21
  `;
20
22
 
21
23
  describe('change stream', () => {
22
- describeWithStorage({ timeout: 20_000 }, defineChangeStreamTests);
24
+ describeWithStorage({ timeout: testTimeout(20_000, { cloudOverride: 40_000 }) }, defineChangeStreamTests);
23
25
  });
24
26
 
25
27
  function defineChangeStreamTests({ factory, storageVersion }: StorageVersionTestContext) {
@@ -28,7 +30,8 @@ function defineChangeStreamTests({ factory, storageVersion }: StorageVersionTest
28
30
  const openContext = (options?: Parameters<typeof ChangeStreamTestContext.open>[1]) => {
29
31
  return ChangeStreamTestContext.open(factory, { ...options, storageVersion });
30
32
  };
31
- test('replicating basic values', async () => {
33
+ // DocumentDB does not support changeStreamPreAndPostImages, which this test requires via postImages: READ_ONLY.
34
+ test.skipIf(DATABASE_TYPE == DatabaseType.DOCUMENTDB)('replicating basic values', async () => {
32
35
  await using context = await openContext({
33
36
  mongoOptions: { postImages: PostImagesOption.READ_ONLY }
34
37
  });
@@ -74,7 +77,10 @@ bucket_definitions:
74
77
  - SELECT _id as id, description, num FROM "test_%"`);
75
78
 
76
79
  await db.createCollection('test_data', {
77
- changeStreamPreAndPostImages: { enabled: false }
80
+ // DocumentDB does not support changeStreamPreAndPostImages, even when
81
+ // explicitly disabled. This test only needs a collection to exercise
82
+ // fatal error recovery, so omit the option in DocumentDB mode.
83
+ ...(DATABASE_TYPE == DatabaseType.MONGODB ? { changeStreamPreAndPostImages: { enabled: false } } : {})
78
84
  });
79
85
  const collection = db.collection('test_data');
80
86
 
@@ -152,7 +158,7 @@ bucket_definitions:
152
158
  await context.updateSyncRules(BASIC_SYNC_RULES);
153
159
 
154
160
  await db.createCollection('test_data', {
155
- changeStreamPreAndPostImages: { enabled: false }
161
+ changeStreamPreAndPostImages: DATABASE_TYPE == DatabaseType.MONGODB ? { enabled: false } : undefined
156
162
  });
157
163
  testId = new mongo.ObjectId();
158
164
  collection = db.collection('test_data');
@@ -167,7 +173,9 @@ bucket_definitions:
167
173
  expect(test_utils.reduceBucket(data).slice(1)).toEqual([]);
168
174
  });
169
175
 
170
- test('updateLookup - no fullDocument available', async () => {
176
+ // DocumentDB always materializes a write-time post-image for updateLookup, so the 'fullDocument unavailable'
177
+ // case this test exercises (update treated as a remove) cannot occur there.
178
+ test.skipIf(DATABASE_TYPE == DatabaseType.DOCUMENTDB)('updateLookup - no fullDocument available', async () => {
171
179
  await using context = await openContext({
172
180
  mongoOptions: { postImages: PostImagesOption.OFF }
173
181
  });
@@ -179,6 +187,9 @@ bucket_definitions:
179
187
  - SELECT _id as id, description, num FROM "test_data"`);
180
188
 
181
189
  await db.createCollection('test_data', {
190
+ // DocumentDB does not support changeStreamPreAndPostImages, even when
191
+ // explicitly disabled. This test only needs a collection to exercise
192
+ // fatal error recovery, so omit the option in DocumentDB mode.
182
193
  changeStreamPreAndPostImages: { enabled: false }
183
194
  });
184
195
  const collection = db.collection('test_data');
@@ -211,7 +222,8 @@ bucket_definitions:
211
222
  ]);
212
223
  });
213
224
 
214
- test('postImages - autoConfigure', async () => {
225
+ // DocumentDB does not support changeStreamPreAndPostImages (post-images).
226
+ test.skipIf(DATABASE_TYPE == DatabaseType.DOCUMENTDB)('postImages - autoConfigure', async () => {
215
227
  // Similar to the above test, but with postImages enabled.
216
228
  // This resolves the consistency issue.
217
229
  await using context = await openContext({
@@ -259,7 +271,8 @@ bucket_definitions:
259
271
  ]);
260
272
  });
261
273
 
262
- test('postImages - on', async () => {
274
+ // DocumentDB does not support changeStreamPreAndPostImages (post-images).
275
+ test.skipIf(DATABASE_TYPE == DatabaseType.DOCUMENTDB)('postImages - on', async () => {
263
276
  // Similar to postImages - autoConfigure, but does not auto-configure.
264
277
  // changeStreamPreAndPostImages must be manually configured.
265
278
  await using context = await openContext({
@@ -408,7 +421,9 @@ bucket_definitions:
408
421
  ]);
409
422
  });
410
423
 
411
- test('replicating dropCollection', async () => {
424
+ // Collection drop events are not currently replicated on DocumentDB: they are not delivered through the
425
+ // cluster-level change stream the way standard MongoDB delivers them, so the drop is not applied as a remove.
426
+ test.skipIf(DATABASE_TYPE == DatabaseType.DOCUMENTDB)('replicating dropCollection', async () => {
412
427
  await using context = await openContext();
413
428
  const { db } = context;
414
429
  const syncRuleContent = `
@@ -440,7 +455,9 @@ bucket_definitions:
440
455
  ]);
441
456
  });
442
457
 
443
- test('replicating renameCollection', async () => {
458
+ // Collection rename events are not currently replicated on DocumentDB: they are not delivered through the
459
+ // cluster-level change stream the way standard MongoDB delivers them.
460
+ test.skipIf(DATABASE_TYPE == DatabaseType.DOCUMENTDB)('replicating renameCollection', async () => {
444
461
  await using context = await openContext();
445
462
  const { db } = context;
446
463
  const syncRuleContent = `
@@ -471,7 +488,9 @@ bucket_definitions:
471
488
  ]);
472
489
  });
473
490
 
474
- test.runIf(supportsConcurrentSnapshots)(
491
+ // Concurrent-snapshot regression test driven by collection drop/recreate DDL events, which DocumentDB does
492
+ // not deliver through the cluster-level change stream the way standard MongoDB does.
493
+ test.runIf(supportsConcurrentSnapshots && DATABASE_TYPE != DatabaseType.DOCUMENTDB)(
475
494
  'collection recreated while queued snapshot is waiting does not stall checkpoints',
476
495
  async () => {
477
496
  // This is a regression test for a specific timing issue in concurrent snapshot logic.
@@ -603,6 +622,79 @@ bucket_definitions:
603
622
  expect(data).toMatchObject([test_utils.putOp('test_data', { id: test_id, description: 'test1' })]);
604
623
  });
605
624
 
625
+ test.runIf(storageVersion >= 3)(
626
+ 'does not resnapshot an unchanged event definition during incremental reprocessing',
627
+ async (testContext) => {
628
+ const eventDefinition = `
629
+ event_definitions:
630
+ checkpoint_requests:
631
+ payloads:
632
+ - SELECT user_id, checkpoint, client_id, CASE WHEN checkpoint_requested_at IS NULL THEN true ELSE false END AS is_legacy FROM checkpoints
633
+ `;
634
+ const syncConfig = (deploymentComment: string) => `
635
+ config:
636
+ edition: 3
637
+ storage_version: 3
638
+ # ${deploymentComment}
639
+
640
+ ${eventDefinition}
641
+ streams:
642
+ global:
643
+ auto_subscribe: true
644
+ queries:
645
+ - SELECT *, _id AS id FROM lists
646
+ - SELECT *, _id AS id FROM todos
647
+ `;
648
+
649
+ let replicationStreamId: number;
650
+ {
651
+ await using context = await openContext();
652
+ const firstStorage = await context.updateSyncRules(syncConfig('initial deployment'));
653
+ if (
654
+ !('incrementalReprocessing' in firstStorage.storageConfig) ||
655
+ firstStorage.storageConfig.incrementalReprocessing !== true
656
+ ) {
657
+ testContext.skip('storage does not support incremental reprocessing');
658
+ }
659
+
660
+ const { db } = context;
661
+ await db.collection('lists').insertOne({ name: 'list' });
662
+ await db.collection('todos').insertOne({ description: 'todo' });
663
+ await db.collection('checkpoints').insertOne({
664
+ user_id: 'user-1',
665
+ checkpoint: 1n,
666
+ client_id: 'client-1',
667
+ checkpoint_requested_at: null
668
+ });
669
+
670
+ replicationStreamId = firstStorage.replicationStreamId;
671
+ await context.replicateSnapshot();
672
+ context.startStreaming();
673
+ await context.getCheckpoint();
674
+ }
675
+
676
+ const resnapshottedCollections: string[] = [];
677
+ await using context = await openContext({
678
+ doNotClear: true,
679
+ streamOptions: {
680
+ snapshotHooks: {
681
+ beforeSnapshotStarted: async (table) => {
682
+ resnapshottedCollections.push(table.name);
683
+ }
684
+ }
685
+ }
686
+ });
687
+
688
+ const processingStorage = await context.updateSyncRules(syncConfig('redeployed without behavioral changes'));
689
+ expect(processingStorage.replicationStreamId).toBe(replicationStreamId);
690
+ expect(await context.factory.getDeployingSyncConfig()).not.toBeNull();
691
+
692
+ await context.replicateSnapshot();
693
+
694
+ expect(resnapshottedCollections).toEqual([]);
695
+ }
696
+ );
697
+
606
698
  test('coalesces standalone checkpoints when backlog is buffered', async () => {
607
699
  await using context = await openContext();
608
700
  await context.updateSyncRules(BASIC_SYNC_RULES);
@@ -625,6 +717,10 @@ bucket_definitions:
625
717
  type: 'mongodb',
626
718
  ...TEST_CONNECTION_OPTIONS
627
719
  });
720
+ const writeCheckpointBatcher = new WriteCheckpointBatcher(
721
+ () => api,
722
+ () => context.factory
723
+ );
628
724
 
629
725
  context.startStreaming();
630
726
 
@@ -641,8 +737,7 @@ bucket_definitions:
641
737
  createWriteCheckpoint({
642
738
  userId: 'test_user',
643
739
  clientId: 'test_client' + i,
644
- api,
645
- storage: context.factory
740
+ batcher: writeCheckpointBatcher
646
741
  })
647
742
  )
648
743
  );
@@ -652,12 +747,14 @@ bucket_definitions:
652
747
 
653
748
  // We need at least 1 commit.
654
749
  expect(commitCount).toBeGreaterThan(0);
655
- // The previous implementation greated 1 commit per checkpoint, which is bad for performance.
750
+ // The previous implementation created 1 commit per checkpoint, which is bad for performance.
656
751
  // We expect a small number here - typically 2-10, but allow for anything less than the total number of checkpoints.
657
752
  expect(commitCount).toBeLessThan(checkpointCount + 1);
658
753
  });
659
754
 
660
- test('large record', async () => {
755
+ test
756
+ // DocumentDBDB does not support changeStreamSplitLargeEvent
757
+ .skipIf(DATABASE_TYPE == DatabaseType.DOCUMENTDB)('large record', async () => {
661
758
  // Test a large update.
662
759
 
663
760
  // Without $changeStreamSplitLargeEvent, we get this error:
@@ -729,75 +826,83 @@ bucket_definitions:
729
826
  expect(data).toMatchObject([]);
730
827
  });
731
828
 
732
- test('postImages - new collection with postImages enabled', async () => {
733
- await using context = await openContext({
734
- mongoOptions: { postImages: PostImagesOption.AUTO_CONFIGURE }
735
- });
736
- const { db } = context;
737
- await context.updateSyncRules(`
829
+ // DocumentDB does not support changeStreamPreAndPostImages (post-images).
830
+ test.skipIf(DATABASE_TYPE == DatabaseType.DOCUMENTDB)(
831
+ 'postImages - new collection with postImages enabled',
832
+ async () => {
833
+ await using context = await openContext({
834
+ mongoOptions: { postImages: PostImagesOption.AUTO_CONFIGURE }
835
+ });
836
+ const { db } = context;
837
+ await context.updateSyncRules(`
738
838
  bucket_definitions:
739
839
  global:
740
840
  data:
741
841
  - SELECT _id as id, description FROM "test_%"`);
742
842
 
743
- await context.replicateSnapshot();
843
+ await context.replicateSnapshot();
744
844
 
745
- await db.createCollection('test_data', {
746
- // enabled: true here - everything should work
747
- changeStreamPreAndPostImages: { enabled: true }
748
- });
749
- const collection = db.collection('test_data');
750
- const result = await collection.insertOne({ description: 'test1' });
751
- const test_id = result.insertedId;
752
- await collection.updateOne({ _id: test_id }, { $set: { description: 'test2' } });
845
+ await db.createCollection('test_data', {
846
+ // enabled: true here - everything should work
847
+ changeStreamPreAndPostImages: { enabled: true }
848
+ });
849
+ const collection = db.collection('test_data');
850
+ const result = await collection.insertOne({ description: 'test1' });
851
+ const test_id = result.insertedId;
852
+ await collection.updateOne({ _id: test_id }, { $set: { description: 'test2' } });
753
853
 
754
- context.startStreaming();
854
+ context.startStreaming();
755
855
 
756
- const data = await context.getBucketData('global[]');
757
- if (data.length == 3) {
758
- expect(data).toMatchObject([
759
- // An extra op here, since this triggers a snapshot in addition to getting the event.
760
- test_utils.putOp('test_data', { id: test_id!.toHexString(), description: 'test2' }),
761
- test_utils.putOp('test_data', { id: test_id!.toHexString(), description: 'test1' }),
762
- test_utils.putOp('test_data', { id: test_id!.toHexString(), description: 'test2' })
763
- ]);
764
- } else {
765
- expect(data).toMatchObject([
766
- test_utils.putOp('test_data', { id: test_id!.toHexString(), description: 'test1' }),
767
- test_utils.putOp('test_data', { id: test_id!.toHexString(), description: 'test2' })
768
- ]);
856
+ const data = await context.getBucketData('global[]');
857
+ if (data.length == 3) {
858
+ expect(data).toMatchObject([
859
+ // An extra op here, since this triggers a snapshot in addition to getting the event.
860
+ test_utils.putOp('test_data', { id: test_id!.toHexString(), description: 'test2' }),
861
+ test_utils.putOp('test_data', { id: test_id!.toHexString(), description: 'test1' }),
862
+ test_utils.putOp('test_data', { id: test_id!.toHexString(), description: 'test2' })
863
+ ]);
864
+ } else {
865
+ expect(data).toMatchObject([
866
+ test_utils.putOp('test_data', { id: test_id!.toHexString(), description: 'test1' }),
867
+ test_utils.putOp('test_data', { id: test_id!.toHexString(), description: 'test2' })
868
+ ]);
869
+ }
769
870
  }
770
- });
871
+ );
771
872
 
772
- test('postImages - new collection with postImages disabled', async () => {
773
- await using context = await openContext({
774
- mongoOptions: { postImages: PostImagesOption.AUTO_CONFIGURE }
775
- });
776
- const { db } = context;
777
- await context.updateSyncRules(`
873
+ // DocumentDB does not support changeStreamPreAndPostImages (post-images).
874
+ test.skipIf(DATABASE_TYPE == DatabaseType.DOCUMENTDB)(
875
+ 'postImages - new collection with postImages disabled',
876
+ async () => {
877
+ await using context = await openContext({
878
+ mongoOptions: { postImages: PostImagesOption.AUTO_CONFIGURE }
879
+ });
880
+ const { db } = context;
881
+ await context.updateSyncRules(`
778
882
  bucket_definitions:
779
883
  global:
780
884
  data:
781
885
  - SELECT _id as id, description FROM "test_data%"`);
782
886
 
783
- await context.replicateSnapshot();
887
+ await context.replicateSnapshot();
784
888
 
785
- await db.createCollection('test_data', {
786
- // enabled: false here, but autoConfigure will enable it.
787
- // Unfortunately, that is too late, and replication must be restarted.
788
- changeStreamPreAndPostImages: { enabled: false }
789
- });
790
- const collection = db.collection('test_data');
791
- const result = await collection.insertOne({ description: 'test1' });
792
- const test_id = result.insertedId;
793
- await collection.updateOne({ _id: test_id }, { $set: { description: 'test2' } });
889
+ await db.createCollection('test_data', {
890
+ // enabled: false here, but autoConfigure will enable it.
891
+ // Unfortunately, that is too late, and replication must be restarted.
892
+ changeStreamPreAndPostImages: { enabled: false }
893
+ });
894
+ const collection = db.collection('test_data');
895
+ const result = await collection.insertOne({ description: 'test1' });
896
+ const test_id = result.insertedId;
897
+ await collection.updateOne({ _id: test_id }, { $set: { description: 'test2' } });
794
898
 
795
- context.startStreaming();
899
+ context.startStreaming();
796
900
 
797
- await expect(() => context.getBucketData('global[]')).rejects.toMatchObject({
798
- message: expect.stringContaining('stream was configured to require a post-image for all update events')
799
- });
800
- });
901
+ await expect(() => context.getBucketData('global[]')).rejects.toMatchObject({
902
+ message: expect.stringContaining('stream was configured to require a post-image for all update events')
903
+ });
904
+ }
905
+ );
801
906
 
802
907
  test('recover from error', async () => {
803
908
  await using context = await openContext();
@@ -809,7 +914,10 @@ bucket_definitions:
809
914
  - SELECT _id as id, description, num FROM "test_data"`);
810
915
 
811
916
  await db.createCollection('test_data', {
812
- changeStreamPreAndPostImages: { enabled: false }
917
+ // DocumentDB does not support changeStreamPreAndPostImages, even when
918
+ // explicitly disabled. This test only needs a collection to exercise
919
+ // fatal error recovery, so omit the option in DocumentDB mode.
920
+ changeStreamPreAndPostImages: DATABASE_TYPE == DatabaseType.MONGODB ? { enabled: false } : undefined
813
921
  });
814
922
 
815
923
  const collection = db.collection('test_data');
@@ -820,9 +928,9 @@ bucket_definitions:
820
928
 
821
929
  // Simulate an error
822
930
  await context.storage!.reportError(new Error('simulated error'));
823
- const syncRules = await context.factory.getActiveSyncRulesContent();
931
+ const syncRules = (await context.factory.getActiveSyncConfig())?.content;
824
932
  expect(syncRules).toBeTruthy();
825
- expect(syncRules?.last_fatal_error).toEqual('simulated error');
933
+ expect((await syncRules?.getSyncConfigStatus())?.last_fatal_error).toEqual('simulated error');
826
934
 
827
935
  // The next checkpoint should clear the error.
828
936
  await context.getCheckpoint();
@@ -830,10 +938,11 @@ bucket_definitions:
830
938
  // Just wait, and check that the error is cleared automatically.
831
939
  await vi.waitUntil(
832
940
  async () => {
833
- const error = (await context.factory.getActiveSyncRulesContent())?.last_fatal_error;
941
+ const syncRules = (await context.factory.getActiveSyncConfig())?.content;
942
+ const error = (await syncRules?.getSyncConfigStatus())?.last_fatal_error;
834
943
  return error == null;
835
944
  },
836
- { timeout: 2_000 }
945
+ { timeout: testTimeout(2_000, { cloudOverride: 5_000 }) }
837
946
  );
838
947
  });
839
948
  }