@powersync/service-module-postgres 0.17.1 → 0.18.0

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,44 @@
1
1
  # @powersync/service-module-postgres
2
2
 
3
+ ## 0.18.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 8bd83e8: Introduce storage versions.
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies [15aea77]
12
+ - Updated dependencies [0998251]
13
+ - Updated dependencies [65f3c89]
14
+ - Updated dependencies [1c45667]
15
+ - Updated dependencies [8785a3f]
16
+ - Updated dependencies [8a4c34e]
17
+ - Updated dependencies [b440093]
18
+ - Updated dependencies [d7ff4ad]
19
+ - Updated dependencies [c683322]
20
+ - Updated dependencies [8bd83e8]
21
+ - Updated dependencies [83989b2]
22
+ - Updated dependencies [79a9729]
23
+ - Updated dependencies [5edd95f]
24
+ - @powersync/lib-service-postgres@0.4.22
25
+ - @powersync/service-jpgwire@0.21.13
26
+ - @powersync/service-core@1.20.0
27
+ - @powersync/service-types@0.15.0
28
+ - @powersync/service-sync-rules@0.32.0
29
+ - @powersync/lib-services-framework@0.8.3
30
+
31
+ ## 0.17.2
32
+
33
+ ### Patch Changes
34
+
35
+ - Updated dependencies [a04252d]
36
+ - @powersync/service-sync-rules@0.31.1
37
+ - @powersync/lib-services-framework@0.8.2
38
+ - @powersync/service-jpgwire@0.21.12
39
+ - @powersync/service-core@1.19.2
40
+ - @powersync/lib-service-postgres@0.4.21
41
+
3
42
  ## 0.17.1
4
43
 
5
44
  ### Patch Changes
@@ -28,6 +28,7 @@ export declare const PostgresConnectionConfig: t.Intersection<t.Codec<{
28
28
  reject_ip_ranges?: string[] | undefined;
29
29
  slot_name_prefix?: string | undefined;
30
30
  max_pool_size?: number | undefined;
31
+ connect_timeout?: number | undefined;
31
32
  }, {
32
33
  type: string;
33
34
  id?: string | undefined;
@@ -51,6 +52,7 @@ export declare const PostgresConnectionConfig: t.Intersection<t.Codec<{
51
52
  reject_ip_ranges?: string[] | undefined;
52
53
  slot_name_prefix?: string | undefined;
53
54
  max_pool_size?: number | undefined;
55
+ connect_timeout?: number | undefined;
54
56
  }, string, t.CodecProps>, t.ObjectCodec<{}>>;
55
57
  /**
56
58
  * Config input specified when starting services
@@ -80,5 +82,6 @@ export declare function normalizeConnectionConfig(options: PostgresConnectionCon
80
82
  lookup: import("net").LookupFunction | undefined;
81
83
  client_certificate: string | undefined;
82
84
  client_private_key: string | undefined;
85
+ connect_timeout_ms: number | undefined;
83
86
  max_pool_size: number;
84
87
  };
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
8
- "version": "0.17.1",
8
+ "version": "0.18.0",
9
9
  "main": "dist/index.js",
10
10
  "license": "FSL-1.1-ALv2",
11
11
  "type": "module",
@@ -25,20 +25,20 @@
25
25
  "semver": "^7.5.4",
26
26
  "ts-codec": "^1.3.0",
27
27
  "uuid": "^11.1.0",
28
- "@powersync/lib-service-postgres": "0.4.20",
29
- "@powersync/lib-services-framework": "0.8.1",
30
- "@powersync/service-core": "1.19.1",
31
- "@powersync/service-jpgwire": "0.21.11",
28
+ "@powersync/lib-service-postgres": "0.4.22",
29
+ "@powersync/lib-services-framework": "0.8.3",
30
+ "@powersync/service-core": "1.20.0",
31
+ "@powersync/service-jpgwire": "0.21.13",
32
32
  "@powersync/service-jsonbig": "0.17.12",
33
- "@powersync/service-sync-rules": "0.31.0",
34
- "@powersync/service-types": "0.14.0"
33
+ "@powersync/service-sync-rules": "0.32.0",
34
+ "@powersync/service-types": "0.15.0"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@types/semver": "^7.5.4",
38
- "@powersync/service-core-tests": "0.13.1",
39
- "@powersync/service-module-mongodb-storage": "0.13.1",
40
- "@powersync/lib-service-postgres": "0.4.20",
41
- "@powersync/service-module-postgres-storage": "0.11.1"
38
+ "@powersync/service-core-tests": "0.14.0",
39
+ "@powersync/service-module-mongodb-storage": "0.14.0",
40
+ "@powersync/lib-service-postgres": "0.4.22",
41
+ "@powersync/service-module-postgres-storage": "0.12.0"
42
42
  },
43
43
  "scripts": {
44
44
  "build": "tsc -b",
@@ -1,7 +1,7 @@
1
1
  import { PostgresRouteAPIAdapter } from '@module/api/PostgresRouteAPIAdapter.js';
2
- import { checkpointUserId, createWriteCheckpoint, TestStorageFactory } from '@powersync/service-core';
2
+ import { checkpointUserId, createWriteCheckpoint } from '@powersync/service-core';
3
3
  import { describe, test } from 'vitest';
4
- import { describeWithStorage } from './util.js';
4
+ import { describeWithStorage, StorageVersionTestContext } from './util.js';
5
5
  import { WalStreamTestContext } from './wal_stream_utils.js';
6
6
 
7
7
  import timers from 'node:timers/promises';
@@ -15,9 +15,9 @@ describe('checkpoint tests', () => {
15
15
  describeWithStorage({}, checkpointTests);
16
16
  });
17
17
 
18
- const checkpointTests = (factory: TestStorageFactory) => {
18
+ const checkpointTests = ({ factory, storageVersion }: StorageVersionTestContext) => {
19
19
  test('write checkpoints', { timeout: 50_000 }, async () => {
20
- await using context = await WalStreamTestContext.open(factory);
20
+ await using context = await WalStreamTestContext.open(factory, { storageVersion });
21
21
 
22
22
  await context.updateSyncRules(BASIC_SYNC_RULES);
23
23
  const { pool } = context;
@@ -1,17 +1,21 @@
1
- import { reduceBucket, TestStorageFactory } from '@powersync/service-core';
1
+ import { reduceBucket } from '@powersync/service-core';
2
2
  import { METRICS_HELPER } from '@powersync/service-core-tests';
3
3
  import { SqliteJsonValue } from '@powersync/service-sync-rules';
4
4
  import * as crypto from 'node:crypto';
5
5
  import * as timers from 'timers/promises';
6
6
  import { describe, expect, test } from 'vitest';
7
- import { describeWithStorage } from './util.js';
7
+ import { describeWithStorage, StorageVersionTestContext } from './util.js';
8
8
  import { WalStreamTestContext } from './wal_stream_utils.js';
9
9
 
10
10
  describe('chunked snapshots', () => {
11
11
  describeWithStorage({ timeout: 120_000 }, defineBatchTests);
12
12
  });
13
13
 
14
- function defineBatchTests(factory: TestStorageFactory) {
14
+ function defineBatchTests({ factory, storageVersion }: StorageVersionTestContext) {
15
+ const openContext = (options?: Parameters<typeof WalStreamTestContext.open>[1]) => {
16
+ return WalStreamTestContext.open(factory, { ...options, storageVersion });
17
+ };
18
+
15
19
  // We need to test every supported type, since chunking could be quite sensitive to
16
20
  // how each specific type is handled.
17
21
  test('chunked snapshot edge case (int2)', async () => {
@@ -89,7 +93,7 @@ function defineBatchTests(factory: TestStorageFactory) {
89
93
  // 5. Logical replication picks up the UPDATE above, but it is missing the TOAST column.
90
94
  // 6. We end up with a row that has a missing TOAST column.
91
95
 
92
- await using context = await WalStreamTestContext.open(factory, {
96
+ await using context = await openContext({
93
97
  // We need to use a smaller chunk size here, so that we can run a query in between chunks
94
98
  walStreamOptions: { snapshotChunkLength: 100 }
95
99
  });
@@ -1,14 +1,11 @@
1
- import { storage } from '@powersync/service-core';
2
1
  import { describe, expect, test } from 'vitest';
3
2
  import { populateData } from '../../dist/utils/populate_test_data.js';
4
3
  import { env } from './env.js';
5
- import { describeWithStorage, TEST_CONNECTION_OPTIONS } from './util.js';
4
+ import { describeWithStorage, StorageVersionTestContext, TEST_CONNECTION_OPTIONS } from './util.js';
6
5
  import { WalStreamTestContext } from './wal_stream_utils.js';
7
6
 
8
7
  describe.skipIf(!(env.CI || env.SLOW_TESTS))('batch replication', function () {
9
- describeWithStorage({ timeout: 240_000 }, function (factory) {
10
- defineBatchTests(factory);
11
- });
8
+ describeWithStorage({ timeout: 240_000 }, defineBatchTests);
12
9
  });
13
10
 
14
11
  const BASIC_SYNC_RULES = `bucket_definitions:
@@ -16,9 +13,13 @@ const BASIC_SYNC_RULES = `bucket_definitions:
16
13
  data:
17
14
  - SELECT id, description, other FROM "test_data"`;
18
15
 
19
- function defineBatchTests(factory: storage.TestStorageFactory) {
16
+ function defineBatchTests({ factory, storageVersion }: StorageVersionTestContext) {
17
+ const openContext = (options?: Parameters<typeof WalStreamTestContext.open>[1]) => {
18
+ return WalStreamTestContext.open(factory, { ...options, storageVersion });
19
+ };
20
+
20
21
  test('update large record', async () => {
21
- await using context = await WalStreamTestContext.open(factory);
22
+ await using context = await openContext();
22
23
  // This test generates a large transaction in MongoDB, despite the replicated data
23
24
  // not being that large.
24
25
  // If we don't limit transaction size, we could run into this error:
@@ -41,17 +42,16 @@ function defineBatchTests(factory: storage.TestStorageFactory) {
41
42
 
42
43
  context.startStreaming();
43
44
 
44
- const checkpoint = await context.getCheckpoint({ timeout: 100_000 });
45
+ const checksum = await context.getChecksums(['global[]'], { timeout: 50_000 });
45
46
  const duration = Date.now() - start;
46
47
  const used = Math.round(process.memoryUsage().heapUsed / 1024 / 1024);
47
- const checksum = await context.storage!.getChecksums(checkpoint, ['global[]']);
48
48
  expect(checksum.get('global[]')!.count).toEqual(operation_count);
49
49
  const perSecond = Math.round((operation_count / duration) * 1000);
50
50
  console.log(`${operation_count} ops in ${duration}ms ${perSecond} ops/s. ${used}MB heap`);
51
51
  });
52
52
 
53
53
  test('initial replication performance', async () => {
54
- await using context = await WalStreamTestContext.open(factory);
54
+ await using context = await openContext();
55
55
  // Manual test to check initial replication performance and memory usage
56
56
  await context.updateSyncRules(BASIC_SYNC_RULES);
57
57
  const { pool } = context;
@@ -89,9 +89,8 @@ function defineBatchTests(factory: storage.TestStorageFactory) {
89
89
  await context.replicateSnapshot();
90
90
  context.startStreaming();
91
91
 
92
- const checkpoint = await context.getCheckpoint({ timeout: 100_000 });
92
+ const checksum = await context.getChecksums(['global[]'], { timeout: 100_000 });
93
93
  const duration = Date.now() - start;
94
- const checksum = await context.storage!.getChecksums(checkpoint, ['global[]']);
95
94
  expect(checksum.get('global[]')!.count).toEqual(operation_count);
96
95
  const perSecond = Math.round((operation_count / duration) * 1000);
97
96
  console.log(`${operation_count} ops in ${duration}ms ${perSecond} ops/s.`);
@@ -102,7 +101,7 @@ function defineBatchTests(factory: storage.TestStorageFactory) {
102
101
  });
103
102
 
104
103
  test('large number of operations', async () => {
105
- await using context = await WalStreamTestContext.open(factory);
104
+ await using context = await openContext();
106
105
  // This just tests performance of a large number of operations inside a transaction.
107
106
  await context.updateSyncRules(BASIC_SYNC_RULES);
108
107
  const { pool } = context;
@@ -141,10 +140,9 @@ function defineBatchTests(factory: storage.TestStorageFactory) {
141
140
 
142
141
  context.startStreaming();
143
142
 
144
- const checkpoint = await context.getCheckpoint({ timeout: 50_000 });
143
+ const checksum = await context.getChecksums(['global[]']);
145
144
  const duration = Date.now() - start;
146
145
  const used = Math.round(process.memoryUsage().heapUsed / 1024 / 1024);
147
- const checksum = await context.storage!.getChecksums(checkpoint, ['global[]']);
148
146
  expect(checksum.get('global[]')!.count).toEqual(operationCount);
149
147
  const perSecond = Math.round((operationCount / duration) * 1000);
150
148
  // This number depends on the test machine, so we keep the test significantly
@@ -158,10 +156,8 @@ function defineBatchTests(factory: storage.TestStorageFactory) {
158
156
  const truncateStart = Date.now();
159
157
  await pool.query(`TRUNCATE test_data`);
160
158
 
161
- const checkpoint2 = await context.getCheckpoint({ timeout: 20_000 });
159
+ const checksum2 = await context.getChecksums(['global[]'], { timeout: 20_000 });
162
160
  const truncateDuration = Date.now() - truncateStart;
163
-
164
- const checksum2 = await context.storage!.getChecksums(checkpoint2, ['global[]']);
165
161
  const truncateCount = checksum2.get('global[]')!.count - checksum.get('global[]')!.count;
166
162
  expect(truncateCount).toEqual(numTransactions * perTransaction);
167
163
  const truncatePerSecond = Math.round((truncateCount / truncateDuration) * 1000);
@@ -183,7 +179,7 @@ function defineBatchTests(factory: storage.TestStorageFactory) {
183
179
  // 4. Another document to make sure the internal batching overflows
184
180
  // to a second batch.
185
181
 
186
- await using context = await WalStreamTestContext.open(factory);
182
+ await using context = await openContext();
187
183
  await context.updateSyncRules(`bucket_definitions:
188
184
  global:
189
185
  data:
@@ -227,9 +223,7 @@ function defineBatchTests(factory: storage.TestStorageFactory) {
227
223
  await context.replicateSnapshot();
228
224
 
229
225
  context.startStreaming();
230
-
231
- const checkpoint = await context.getCheckpoint({ timeout: 50_000 });
232
- const checksum = await context.storage!.getChecksums(checkpoint, ['global[]']);
226
+ const checksum = await context.getChecksums(['global[]'], { timeout: 50_000 });
233
227
  expect(checksum.get('global[]')!.count).toEqual((numDocs + 2) * 4);
234
228
  });
235
229
 
@@ -1,27 +1,30 @@
1
1
  import { describe, expect, test } from 'vitest';
2
2
  import { env } from './env.js';
3
- import { describeWithStorage } from './util.js';
3
+ import { describeWithStorage, StorageVersionTestContext } from './util.js';
4
4
  import { WalStreamTestContext } from './wal_stream_utils.js';
5
- import { TestStorageFactory } from '@powersync/service-core';
6
5
  import { METRICS_HELPER } from '@powersync/service-core-tests';
7
6
  import { ReplicationMetric } from '@powersync/service-types';
8
7
  import * as timers from 'node:timers/promises';
9
8
  import { ReplicationAbortedError } from '@powersync/lib-services-framework';
10
9
 
11
10
  describe.skipIf(!(env.CI || env.SLOW_TESTS))('batch replication', function () {
12
- describeWithStorage({ timeout: 240_000 }, function (factory) {
11
+ describeWithStorage({ timeout: 240_000 }, function ({ factory, storageVersion }) {
13
12
  test('resuming initial replication (1)', async () => {
14
13
  // Stop early - likely to not include deleted row in first replication attempt.
15
- await testResumingReplication(factory, 2000);
14
+ await testResumingReplication(factory, storageVersion, 2000);
16
15
  });
17
16
  test('resuming initial replication (2)', async () => {
18
17
  // Stop late - likely to include deleted row in first replication attempt.
19
- await testResumingReplication(factory, 8000);
18
+ await testResumingReplication(factory, storageVersion, 8000);
20
19
  });
21
20
  });
22
21
  });
23
22
 
24
- async function testResumingReplication(factory: TestStorageFactory, stopAfter: number) {
23
+ async function testResumingReplication(
24
+ factory: StorageVersionTestContext['factory'],
25
+ storageVersion: number,
26
+ stopAfter: number
27
+ ) {
25
28
  // This tests interrupting and then resuming initial replication.
26
29
  // We interrupt replication after test_data1 has fully replicated, and
27
30
  // test_data2 has partially replicated.
@@ -33,7 +36,10 @@ async function testResumingReplication(factory: TestStorageFactory, stopAfter: n
33
36
  // have been / have not been replicated at that point is not deterministic.
34
37
  // We do allow for some variation in the test results to account for this.
35
38
 
36
- await using context = await WalStreamTestContext.open(factory, { walStreamOptions: { snapshotChunkLength: 1000 } });
39
+ await using context = await WalStreamTestContext.open(factory, {
40
+ storageVersion,
41
+ walStreamOptions: { snapshotChunkLength: 1000 }
42
+ });
37
43
 
38
44
  await context.updateSyncRules(`bucket_definitions:
39
45
  global:
@@ -84,6 +90,7 @@ async function testResumingReplication(factory: TestStorageFactory, stopAfter: n
84
90
  // Bypass the usual "clear db on factory open" step.
85
91
  await using context2 = await WalStreamTestContext.open(factory, {
86
92
  doNotClear: true,
93
+ storageVersion,
87
94
  walStreamOptions: { snapshotChunkLength: 1000 }
88
95
  });
89
96
 
@@ -20,7 +20,9 @@ describe('PostgresRouteAPIAdapter tests', () => {
20
20
  `);
21
21
 
22
22
  const schema = await api.getConnectionSchema();
23
- expect(schema).toStrictEqual([
23
+ // Ignore any other potential schemas in the test database, for example the 'powersync' schema.
24
+ const filtered = schema.filter((s) => s.name == 'public');
25
+ expect(filtered).toStrictEqual([
24
26
  {
25
27
  name: 'public',
26
28
  tables: [
@@ -2,8 +2,7 @@ import { compareIds, putOp, reduceBucket, removeOp, test_utils } from '@powersyn
2
2
  import * as timers from 'timers/promises';
3
3
  import { describe, expect, test } from 'vitest';
4
4
 
5
- import { storage } from '@powersync/service-core';
6
- import { describeWithStorage } from './util.js';
5
+ import { describeWithStorage, StorageVersionTestContext } from './util.js';
7
6
  import { WalStreamTestContext } from './wal_stream_utils.js';
8
7
 
9
8
  describe('schema changes', { timeout: 20_000 }, function () {
@@ -24,9 +23,12 @@ const PUT_T3 = test_utils.putOp('test_data', { id: 't3', description: 'test3' })
24
23
  const REMOVE_T1 = test_utils.removeOp('test_data', 't1');
25
24
  const REMOVE_T2 = test_utils.removeOp('test_data', 't2');
26
25
 
27
- function defineTests(factory: storage.TestStorageFactory) {
26
+ function defineTests({ factory, storageVersion }: StorageVersionTestContext) {
27
+ const openContext = (options?: Parameters<typeof WalStreamTestContext.open>[1]) => {
28
+ return WalStreamTestContext.open(factory, { ...options, storageVersion });
29
+ };
28
30
  test('re-create table', async () => {
29
- await using context = await WalStreamTestContext.open(factory);
31
+ await using context = await openContext();
30
32
 
31
33
  // Drop a table and re-create it.
32
34
  await context.updateSyncRules(BASIC_SYNC_RULES);
@@ -70,7 +72,7 @@ function defineTests(factory: storage.TestStorageFactory) {
70
72
  });
71
73
 
72
74
  test('add table', async () => {
73
- await using context = await WalStreamTestContext.open(factory);
75
+ await using context = await openContext();
74
76
  // Add table after initial replication
75
77
  await context.updateSyncRules(BASIC_SYNC_RULES);
76
78
  const { pool } = context;
@@ -98,7 +100,7 @@ function defineTests(factory: storage.TestStorageFactory) {
98
100
  });
99
101
 
100
102
  test('rename table (1)', async () => {
101
- await using context = await WalStreamTestContext.open(factory);
103
+ await using context = await openContext();
102
104
  const { pool } = context;
103
105
 
104
106
  await context.updateSyncRules(BASIC_SYNC_RULES);
@@ -136,7 +138,7 @@ function defineTests(factory: storage.TestStorageFactory) {
136
138
  });
137
139
 
138
140
  test('rename table (2)', async () => {
139
- await using context = await WalStreamTestContext.open(factory);
141
+ await using context = await openContext();
140
142
  // Rename table in sync rules -> in sync rules
141
143
  const { pool } = context;
142
144
 
@@ -189,7 +191,7 @@ function defineTests(factory: storage.TestStorageFactory) {
189
191
  });
190
192
 
191
193
  test('rename table (3)', async () => {
192
- await using context = await WalStreamTestContext.open(factory);
194
+ await using context = await openContext();
193
195
  // Rename table in sync rules -> not in sync rules
194
196
 
195
197
  const { pool } = context;
@@ -224,7 +226,7 @@ function defineTests(factory: storage.TestStorageFactory) {
224
226
  });
225
227
 
226
228
  test('change replica id', async () => {
227
- await using context = await WalStreamTestContext.open(factory);
229
+ await using context = await openContext();
228
230
  // Change replica id from default to full
229
231
  // Causes a re-import of the table.
230
232
 
@@ -268,7 +270,7 @@ function defineTests(factory: storage.TestStorageFactory) {
268
270
  });
269
271
 
270
272
  test('change full replica id by adding column', async () => {
271
- await using context = await WalStreamTestContext.open(factory);
273
+ await using context = await openContext();
272
274
  // Change replica id from full by adding column
273
275
  // Causes a re-import of the table.
274
276
  // Other changes such as renaming column would have the same effect
@@ -311,7 +313,7 @@ function defineTests(factory: storage.TestStorageFactory) {
311
313
  });
312
314
 
313
315
  test('change default replica id by changing column type', async () => {
314
- await using context = await WalStreamTestContext.open(factory);
316
+ await using context = await openContext();
315
317
  // Change default replica id by changing column type
316
318
  // Causes a re-import of the table.
317
319
  const { pool } = context;
@@ -348,7 +350,7 @@ function defineTests(factory: storage.TestStorageFactory) {
348
350
  });
349
351
 
350
352
  test('change index id by changing column type', async () => {
351
- await using context = await WalStreamTestContext.open(factory);
353
+ await using context = await openContext();
352
354
  // Change index replica id by changing column type
353
355
  // Causes a re-import of the table.
354
356
  // Secondary functionality tested here is that replica id column order stays
@@ -404,7 +406,7 @@ function defineTests(factory: storage.TestStorageFactory) {
404
406
  });
405
407
 
406
408
  test('add to publication', async () => {
407
- await using context = await WalStreamTestContext.open(factory);
409
+ await using context = await openContext();
408
410
  // Add table to publication after initial replication
409
411
  const { pool } = context;
410
412
 
@@ -447,7 +449,7 @@ function defineTests(factory: storage.TestStorageFactory) {
447
449
  });
448
450
 
449
451
  test('add to publication (not in sync rules)', async () => {
450
- await using context = await WalStreamTestContext.open(factory);
452
+ await using context = await openContext();
451
453
  // Add table to publication after initial replication
452
454
  // Since the table is not in sync rules, it should not be replicated.
453
455
  const { pool } = context;
@@ -474,7 +476,7 @@ function defineTests(factory: storage.TestStorageFactory) {
474
476
  });
475
477
 
476
478
  test('replica identity nothing', async () => {
477
- await using context = await WalStreamTestContext.open(factory);
479
+ await using context = await openContext();
478
480
  // Technically not a schema change, but fits here.
479
481
  // Replica ID works a little differently here - the table doesn't have
480
482
  // one defined, but we generate a unique one for each replicated row.
@@ -521,7 +523,7 @@ function defineTests(factory: storage.TestStorageFactory) {
521
523
  });
522
524
 
523
525
  test('replica identity default without PK', async () => {
524
- await using context = await WalStreamTestContext.open(factory);
526
+ await using context = await openContext();
525
527
  // Same as no replica identity
526
528
  const { pool } = context;
527
529
  await context.updateSyncRules(BASIC_SYNC_RULES);
@@ -573,7 +575,7 @@ function defineTests(factory: storage.TestStorageFactory) {
573
575
  // await new Promise((resolve) => setTimeout(resolve, 100));
574
576
  // await this.snapshotTable(batch, db, result.table);
575
577
  test('table snapshot consistency', async () => {
576
- await using context = await WalStreamTestContext.open(factory);
578
+ await using context = await openContext();
577
579
  const { pool } = context;
578
580
 
579
581
  await context.updateSyncRules(BASIC_SYNC_RULES);
@@ -640,7 +642,7 @@ function defineTests(factory: storage.TestStorageFactory) {
640
642
  });
641
643
 
642
644
  test('custom types', async () => {
643
- await using context = await WalStreamTestContext.open(factory);
645
+ await using context = await openContext();
644
646
 
645
647
  await context.updateSyncRules(`
646
648
  streams:
@@ -7,6 +7,7 @@ import {
7
7
  connectPgPool,
8
8
  describeWithStorage,
9
9
  getClientCheckpoint,
10
+ StorageVersionTestContext,
10
11
  TEST_CONNECTION_OPTIONS
11
12
  } from './util.js';
12
13
 
@@ -14,7 +15,11 @@ import * as pgwire from '@powersync/service-jpgwire';
14
15
  import { SqliteRow } from '@powersync/service-sync-rules';
15
16
 
16
17
  import { PgManager } from '@module/replication/PgManager.js';
17
- import { createCoreReplicationMetrics, initializeCoreReplicationMetrics, storage } from '@powersync/service-core';
18
+ import {
19
+ createCoreReplicationMetrics,
20
+ initializeCoreReplicationMetrics,
21
+ updateSyncRulesFromYaml
22
+ } from '@powersync/service-core';
18
23
  import { METRICS_HELPER, test_utils } from '@powersync/service-core-tests';
19
24
  import * as mongo_storage from '@powersync/service-module-mongodb-storage';
20
25
  import * as postgres_storage from '@powersync/service-module-postgres-storage';
@@ -22,12 +27,12 @@ import * as timers from 'node:timers/promises';
22
27
  import { CustomTypeRegistry } from '@module/types/registry.js';
23
28
 
24
29
  describe.skipIf(!(env.CI || env.SLOW_TESTS))('slow tests', function () {
25
- describeWithStorage({ timeout: 120_000 }, function (factory) {
26
- defineSlowTests(factory);
30
+ describeWithStorage({ timeout: 120_000 }, function ({ factory, storageVersion }) {
31
+ defineSlowTests({ factory, storageVersion });
27
32
  });
28
33
  });
29
34
 
30
- function defineSlowTests(factory: storage.TestStorageFactory) {
35
+ function defineSlowTests({ factory, storageVersion }: StorageVersionTestContext) {
31
36
  let walStream: WalStream | undefined;
32
37
  let connections: PgManager | undefined;
33
38
  let abortController: AbortController | undefined;
@@ -81,7 +86,7 @@ bucket_definitions:
81
86
  data:
82
87
  - SELECT * FROM "test_data"
83
88
  `;
84
- const syncRules = await f.updateSyncRules({ content: syncRuleContent });
89
+ const syncRules = await f.updateSyncRules(updateSyncRulesFromYaml(syncRuleContent, { storageVersion }));
85
90
  const storage = f.getInstance(syncRules);
86
91
  abortController = new AbortController();
87
92
  const options: WalStreamOptions = {
@@ -306,7 +311,8 @@ bucket_definitions:
306
311
  data:
307
312
  - SELECT id, description FROM "test_data"
308
313
  `;
309
- const syncRules = await f.updateSyncRules({ content: syncRuleContent });
314
+
315
+ const syncRules = await f.updateSyncRules(updateSyncRulesFromYaml(syncRuleContent, { storageVersion }));
310
316
  const storage = f.getInstance(syncRules);
311
317
 
312
318
  // 1. Setup some base data that will be replicated in initial replication
package/test/src/util.ts CHANGED
@@ -2,12 +2,18 @@ import { PostgresRouteAPIAdapter } from '@module/api/PostgresRouteAPIAdapter.js'
2
2
  import * as types from '@module/types/types.js';
3
3
  import * as lib_postgres from '@powersync/lib-service-postgres';
4
4
  import { logger } from '@powersync/lib-services-framework';
5
- import { BucketStorageFactory, InternalOpId, TestStorageFactory } from '@powersync/service-core';
5
+ import {
6
+ BucketStorageFactory,
7
+ CURRENT_STORAGE_VERSION,
8
+ InternalOpId,
9
+ LEGACY_STORAGE_VERSION,
10
+ TestStorageFactory
11
+ } from '@powersync/service-core';
6
12
  import * as pgwire from '@powersync/service-jpgwire';
7
13
  import * as mongo_storage from '@powersync/service-module-mongodb-storage';
8
14
  import * as postgres_storage from '@powersync/service-module-postgres-storage';
9
- import { env } from './env.js';
10
15
  import { describe, TestOptions } from 'vitest';
16
+ import { env } from './env.js';
11
17
 
12
18
  export const TEST_URI = env.PG_TEST_URL;
13
19
 
@@ -20,14 +26,34 @@ export const INITIALIZED_POSTGRES_STORAGE_FACTORY = postgres_storage.test_utils.
20
26
  url: env.PG_STORAGE_TEST_URL
21
27
  });
22
28
 
23
- export function describeWithStorage(options: TestOptions, fn: (factory: TestStorageFactory) => void) {
24
- describe.skipIf(!env.TEST_MONGO_STORAGE)(`mongodb storage`, options, function () {
25
- fn(INITIALIZED_MONGO_STORAGE_FACTORY);
26
- });
29
+ const TEST_STORAGE_VERSIONS = [LEGACY_STORAGE_VERSION, CURRENT_STORAGE_VERSION];
30
+
31
+ export interface StorageVersionTestContext {
32
+ factory: TestStorageFactory;
33
+ storageVersion: number;
34
+ }
27
35
 
28
- describe.skipIf(!env.TEST_POSTGRES_STORAGE)(`postgres storage`, options, function () {
29
- fn(INITIALIZED_POSTGRES_STORAGE_FACTORY);
30
- });
36
+ export function describeWithStorage(options: TestOptions, fn: (context: StorageVersionTestContext) => void) {
37
+ const describeFactory = (storageName: string, factory: TestStorageFactory) => {
38
+ describe(`${storageName} storage`, options, function () {
39
+ for (const storageVersion of TEST_STORAGE_VERSIONS) {
40
+ describe(`storage v${storageVersion}`, function () {
41
+ fn({
42
+ factory,
43
+ storageVersion
44
+ });
45
+ });
46
+ }
47
+ });
48
+ };
49
+
50
+ if (env.TEST_MONGO_STORAGE) {
51
+ describeFactory('mongodb', INITIALIZED_MONGO_STORAGE_FACTORY);
52
+ }
53
+
54
+ if (env.TEST_POSTGRES_STORAGE) {
55
+ describeFactory('postgres', INITIALIZED_POSTGRES_STORAGE_FACTORY);
56
+ }
31
57
  }
32
58
 
33
59
  export const TEST_CONNECTION_OPTIONS = types.normalizeConnectionConfig({
@@ -3,6 +3,7 @@ import { expect, test } from 'vitest';
3
3
 
4
4
  import { INITIALIZED_MONGO_STORAGE_FACTORY } from './util.js';
5
5
  import { WalStreamTestContext } from './wal_stream_utils.js';
6
+ import { updateSyncRulesFromYaml } from '@powersync/service-core';
6
7
 
7
8
  test('validate tables', async () => {
8
9
  await using context = await WalStreamTestContext.open(INITIALIZED_MONGO_STORAGE_FACTORY);
@@ -19,7 +20,7 @@ bucket_definitions:
19
20
  - SELECT * FROM "other%"
20
21
  `;
21
22
 
22
- const syncRules = await context.factory.updateSyncRules({ content: syncRuleContent });
23
+ const syncRules = await context.factory.updateSyncRules(updateSyncRulesFromYaml(syncRuleContent));
23
24
 
24
25
  const tablePatterns = syncRules.parsed({ defaultSchema: 'public' }).sync_rules.config.getSourceTables();
25
26
  const tableInfo = await getDebugTablesInfo({