@powersync/service-core-tests 0.0.0-dev-20260313100403 → 0.0.0-dev-20260601140105

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 (55) hide show
  1. package/CHANGELOG.md +91 -5
  2. package/dist/test-utils/AbstractStreamTestContext.d.ts +51 -0
  3. package/dist/test-utils/AbstractStreamTestContext.js +143 -0
  4. package/dist/test-utils/AbstractStreamTestContext.js.map +1 -0
  5. package/dist/test-utils/MetricsHelper.js +1 -1
  6. package/dist/test-utils/MetricsHelper.js.map +1 -1
  7. package/dist/test-utils/StorageDataHelpers.d.ts +8 -0
  8. package/dist/test-utils/StorageDataHelpers.js +33 -0
  9. package/dist/test-utils/StorageDataHelpers.js.map +1 -0
  10. package/dist/test-utils/general-utils.d.ts +3 -8
  11. package/dist/test-utils/general-utils.js +28 -26
  12. package/dist/test-utils/general-utils.js.map +1 -1
  13. package/dist/test-utils/stream_utils.js +2 -2
  14. package/dist/test-utils/stream_utils.js.map +1 -1
  15. package/dist/test-utils/test-utils-index.d.ts +2 -0
  16. package/dist/test-utils/test-utils-index.js +2 -0
  17. package/dist/test-utils/test-utils-index.js.map +1 -1
  18. package/dist/tests/register-compacting-tests.js +491 -627
  19. package/dist/tests/register-compacting-tests.js.map +1 -1
  20. package/dist/tests/register-data-storage-checkpoint-tests.js +192 -304
  21. package/dist/tests/register-data-storage-checkpoint-tests.js.map +1 -1
  22. package/dist/tests/register-data-storage-data-tests.js +1125 -1446
  23. package/dist/tests/register-data-storage-data-tests.js.map +1 -1
  24. package/dist/tests/register-data-storage-parameter-tests.js +816 -629
  25. package/dist/tests/register-data-storage-parameter-tests.js.map +1 -1
  26. package/dist/tests/register-migration-tests.js +63 -139
  27. package/dist/tests/register-migration-tests.js.map +1 -1
  28. package/dist/tests/register-parameter-compacting-tests.js +95 -171
  29. package/dist/tests/register-parameter-compacting-tests.js.map +1 -1
  30. package/dist/tests/register-report-tests.js +22 -22
  31. package/dist/tests/register-report-tests.js.map +1 -1
  32. package/dist/tests/register-sync-tests.js +965 -1155
  33. package/dist/tests/register-sync-tests.js.map +1 -1
  34. package/dist/tests/tests-index.d.ts +4 -4
  35. package/dist/tests/tests-index.js +4 -4
  36. package/dist/tests/tests-index.js.map +1 -1
  37. package/dist/tests/util.d.ts +1 -2
  38. package/dist/tests/util.js +8 -5
  39. package/dist/tests/util.js.map +1 -1
  40. package/package.json +7 -6
  41. package/src/test-utils/AbstractStreamTestContext.ts +179 -0
  42. package/src/test-utils/MetricsHelper.ts +1 -1
  43. package/src/test-utils/StorageDataHelpers.ts +44 -0
  44. package/src/test-utils/general-utils.ts +31 -31
  45. package/src/test-utils/stream_utils.ts +2 -2
  46. package/src/test-utils/test-utils-index.ts +2 -0
  47. package/src/tests/register-data-storage-data-tests.ts +24 -2
  48. package/src/tests/register-data-storage-parameter-tests.ts +519 -67
  49. package/src/tests/register-parameter-compacting-tests.ts +8 -9
  50. package/src/tests/register-report-tests.ts +22 -22
  51. package/src/tests/register-sync-tests.ts +74 -0
  52. package/src/tests/tests-index.ts +4 -4
  53. package/src/tests/util.ts +12 -9
  54. package/tsconfig.json +3 -0
  55. package/tsconfig.tsbuildinfo +1 -1
@@ -1,61 +1,8 @@
1
- var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
2
- if (value !== null && value !== void 0) {
3
- if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
4
- var dispose, inner;
5
- if (async) {
6
- if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
7
- dispose = value[Symbol.asyncDispose];
8
- }
9
- if (dispose === void 0) {
10
- if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
11
- dispose = value[Symbol.dispose];
12
- if (async) inner = dispose;
13
- }
14
- if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
15
- if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
16
- env.stack.push({ value: value, dispose: dispose, async: async });
17
- }
18
- else if (async) {
19
- env.stack.push({ async: true });
20
- }
21
- return value;
22
- };
23
- var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
24
- return function (env) {
25
- function fail(e) {
26
- env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
27
- env.hasError = true;
28
- }
29
- var r, s = 0;
30
- function next() {
31
- while (r = env.stack.pop()) {
32
- try {
33
- if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
34
- if (r.dispose) {
35
- var result = r.dispose.call(r.value);
36
- if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
37
- }
38
- else s |= 1;
39
- }
40
- catch (e) {
41
- fail(e);
42
- }
43
- }
44
- if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
45
- if (env.hasError) throw env.error;
46
- }
47
- return next();
48
- };
49
- })(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
50
- var e = new Error(message);
51
- return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
52
- });
53
1
  import { CURRENT_STORAGE_VERSION, JwtPayload, storage, updateSyncRulesFromYaml } from '@powersync/service-core';
54
- import { RequestParameters, ScopedParameterLookup } from '@powersync/service-sync-rules';
2
+ import { RequestParameters, ScopedParameterLookup, UnscopedParameterLookup } from '@powersync/service-sync-rules';
55
3
  import { expect, test } from 'vitest';
56
4
  import * as test_utils from '../test-utils/test-utils-index.js';
57
5
  import { bucketRequest } from '../test-utils/test-utils-index.js';
58
- import { parameterLookupScope } from './util.js';
59
6
  /**
60
7
  * @example
61
8
  * ```TypeScript
@@ -69,133 +16,120 @@ import { parameterLookupScope } from './util.js';
69
16
  export function registerDataStorageParameterTests(config) {
70
17
  const generateStorageFactory = config.factory;
71
18
  const storageVersion = config.storageVersion ?? CURRENT_STORAGE_VERSION;
72
- const MYBUCKET_1 = parameterLookupScope('mybucket', '1');
73
19
  test('save and load parameters', async () => {
74
- const env_1 = { stack: [], error: void 0, hasError: false };
75
- try {
76
- const factory = __addDisposableResource(env_1, await generateStorageFactory(), true);
77
- const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(`
20
+ await using factory = await generateStorageFactory();
21
+ const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(`
78
22
  bucket_definitions:
79
23
  mybucket:
80
24
  parameters:
81
25
  - SELECT group_id FROM test WHERE id1 = token_parameters.user_id OR id2 = token_parameters.user_id
82
26
  data: []
83
27
  `, {
84
- storageVersion
85
- }));
86
- const bucketStorage = factory.getInstance(syncRules);
87
- const writer = __addDisposableResource(env_1, await bucketStorage.createWriter(test_utils.BATCH_OPTIONS), true);
88
- const testTable = await test_utils.resolveTestTable(writer, 'test', ['id'], config);
89
- await writer.markAllSnapshotDone('1/1');
90
- await writer.save({
91
- sourceTable: testTable,
92
- tag: storage.SaveOperationTag.INSERT,
93
- after: {
94
- id: 't2',
95
- id1: 'user3',
96
- id2: 'user4',
97
- group_id: 'group2a'
98
- },
99
- afterReplicaId: test_utils.rid('t2')
100
- });
101
- await writer.save({
102
- sourceTable: testTable,
103
- tag: storage.SaveOperationTag.INSERT,
104
- after: {
105
- id: 't1',
106
- id1: 'user1',
107
- id2: 'user2',
108
- group_id: 'group1a'
109
- },
110
- afterReplicaId: test_utils.rid('t1')
111
- });
112
- await writer.commit('1/1');
113
- const checkpoint = await bucketStorage.getCheckpoint();
114
- const parameters = await checkpoint.getParameterSets([ScopedParameterLookup.direct(MYBUCKET_1, ['user1'])]);
115
- expect(parameters).toEqual([
116
- {
117
- group_id: 'group1a'
118
- }
119
- ]);
120
- }
121
- catch (e_1) {
122
- env_1.error = e_1;
123
- env_1.hasError = true;
124
- }
125
- finally {
126
- const result_1 = __disposeResources(env_1);
127
- if (result_1)
128
- await result_1;
129
- }
28
+ storageVersion
29
+ }));
30
+ const bucketStorage = factory.getInstance(syncRules);
31
+ const sync_rules = syncRules.parsed(test_utils.PARSE_OPTIONS).hydratedSyncConfig();
32
+ await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
33
+ const testTable = await test_utils.resolveTestTable(writer, 'test', ['id'], config);
34
+ await writer.markAllSnapshotDone('1/1');
35
+ await writer.save({
36
+ sourceTable: testTable,
37
+ tag: storage.SaveOperationTag.INSERT,
38
+ after: {
39
+ id: 't2',
40
+ id1: 'user3',
41
+ id2: 'user4',
42
+ group_id: 'group2a'
43
+ },
44
+ afterReplicaId: test_utils.rid('t2')
45
+ });
46
+ await writer.save({
47
+ sourceTable: testTable,
48
+ tag: storage.SaveOperationTag.INSERT,
49
+ after: {
50
+ id: 't1',
51
+ id1: 'user1',
52
+ id2: 'user2',
53
+ group_id: 'group1a'
54
+ },
55
+ afterReplicaId: test_utils.rid('t1')
56
+ });
57
+ await writer.commit('1/1');
58
+ const checkpoint = await bucketStorage.getCheckpoint();
59
+ const parameters = new RequestParameters(new JwtPayload({ sub: 'user1' }), {});
60
+ const querier = sync_rules.getBucketParameterQuerier(test_utils.querierOptions(parameters)).querier;
61
+ const buckets = await querier.queryDynamicBucketDescriptions({
62
+ async getParameterSets(lookups) {
63
+ expect(lookups.map((l) => l.indexKey)).toEqual([['user1']]);
64
+ const parameter_sets = await checkpoint.getParameterSets(lookups, 1000);
65
+ expect(parameter_sets).toEqual([{ lookup: lookups[0], rows: [{ group_id: 'group1a' }] }]);
66
+ return parameter_sets;
67
+ }
68
+ });
69
+ expect(buckets.map((b) => b.bucket)).toEqual([bucketRequest(syncRules, 'mybucket["group1a"]').bucket]);
130
70
  });
131
71
  test('it should use the latest version', async () => {
132
- const env_2 = { stack: [], error: void 0, hasError: false };
133
- try {
134
- const factory = __addDisposableResource(env_2, await generateStorageFactory(), true);
135
- const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(`
72
+ await using factory = await generateStorageFactory();
73
+ const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(`
136
74
  bucket_definitions:
137
75
  mybucket:
138
76
  parameters:
139
77
  - SELECT group_id FROM test WHERE id = token_parameters.user_id
140
78
  data: []
141
79
  `, {
142
- storageVersion
143
- }));
144
- const bucketStorage = factory.getInstance(syncRules);
145
- const writer = __addDisposableResource(env_2, await bucketStorage.createWriter(test_utils.BATCH_OPTIONS), true);
146
- const testTable = await test_utils.resolveTestTable(writer, 'test', ['id'], config);
147
- await writer.markAllSnapshotDone('1/1');
148
- await writer.save({
149
- sourceTable: testTable,
150
- tag: storage.SaveOperationTag.INSERT,
151
- after: {
152
- id: 'user1',
153
- group_id: 'group1'
154
- },
155
- afterReplicaId: test_utils.rid('user1')
156
- });
157
- await writer.commit('1/1');
158
- const checkpoint1 = await bucketStorage.getCheckpoint();
159
- await writer.save({
160
- sourceTable: testTable,
161
- tag: storage.SaveOperationTag.INSERT,
162
- after: {
163
- id: 'user1',
164
- group_id: 'group2'
165
- },
166
- afterReplicaId: test_utils.rid('user1')
167
- });
168
- await writer.commit('1/2');
169
- const checkpoint2 = await bucketStorage.getCheckpoint();
170
- const parameters = await checkpoint2.getParameterSets([ScopedParameterLookup.direct(MYBUCKET_1, ['user1'])]);
171
- expect(parameters).toEqual([
172
- {
173
- group_id: 'group2'
174
- }
175
- ]);
176
- // Use the checkpoint to get older data if relevant
177
- const parameters2 = await checkpoint1.getParameterSets([ScopedParameterLookup.direct(MYBUCKET_1, ['user1'])]);
178
- expect(parameters2).toEqual([
179
- {
180
- group_id: 'group1'
181
- }
182
- ]);
183
- }
184
- catch (e_2) {
185
- env_2.error = e_2;
186
- env_2.hasError = true;
187
- }
188
- finally {
189
- const result_2 = __disposeResources(env_2);
190
- if (result_2)
191
- await result_2;
192
- }
80
+ storageVersion
81
+ }));
82
+ const bucketStorage = factory.getInstance(syncRules);
83
+ const sync_rules = syncRules.parsed(test_utils.PARSE_OPTIONS).hydratedSyncConfig();
84
+ await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
85
+ const testTable = await test_utils.resolveTestTable(writer, 'test', ['id'], config);
86
+ await writer.markAllSnapshotDone('1/1');
87
+ await writer.save({
88
+ sourceTable: testTable,
89
+ tag: storage.SaveOperationTag.INSERT,
90
+ after: {
91
+ id: 'user1',
92
+ group_id: 'group1'
93
+ },
94
+ afterReplicaId: test_utils.rid('user1')
95
+ });
96
+ await writer.commit('1/1');
97
+ const checkpoint1 = await bucketStorage.getCheckpoint();
98
+ await writer.save({
99
+ sourceTable: testTable,
100
+ tag: storage.SaveOperationTag.INSERT,
101
+ after: {
102
+ id: 'user1',
103
+ group_id: 'group2'
104
+ },
105
+ afterReplicaId: test_utils.rid('user1')
106
+ });
107
+ await writer.commit('1/2');
108
+ const checkpoint2 = await bucketStorage.getCheckpoint();
109
+ const parameters = new RequestParameters(new JwtPayload({ sub: 'user1' }), {});
110
+ const querier = sync_rules.getBucketParameterQuerier(test_utils.querierOptions(parameters)).querier;
111
+ const buckets1 = await querier.queryDynamicBucketDescriptions({
112
+ async getParameterSets(lookups) {
113
+ expect(lookups.map((l) => l.indexKey)).toEqual([['user1']]);
114
+ const parameter_sets = await checkpoint1.getParameterSets(lookups, 1000);
115
+ expect(parameter_sets).toEqual([{ lookup: lookups[0], rows: [{ group_id: 'group1' }] }]);
116
+ return parameter_sets;
117
+ }
118
+ });
119
+ expect(buckets1.map((b) => b.bucket)).toEqual([bucketRequest(syncRules, 'mybucket["group1"]').bucket]);
120
+ const buckets2 = await querier.queryDynamicBucketDescriptions({
121
+ async getParameterSets(lookups) {
122
+ expect(lookups.map((l) => l.indexKey)).toEqual([['user1']]);
123
+ const parameter_sets = await checkpoint2.getParameterSets(lookups, 1000);
124
+ expect(parameter_sets).toEqual([{ lookup: lookups[0], rows: [{ group_id: 'group2' }] }]);
125
+ return parameter_sets;
126
+ }
127
+ });
128
+ expect(buckets2.map((b) => b.bucket)).toEqual([bucketRequest(syncRules, 'mybucket["group2"]').bucket]);
193
129
  });
194
130
  test('it should use the latest version after updates', async () => {
195
- const env_3 = { stack: [], error: void 0, hasError: false };
196
- try {
197
- const factory = __addDisposableResource(env_3, await generateStorageFactory(), true);
198
- const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(`
131
+ await using factory = await generateStorageFactory();
132
+ const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(`
199
133
  bucket_definitions:
200
134
  mybucket:
201
135
  parameters:
@@ -204,186 +138,178 @@ bucket_definitions:
204
138
  WHERE list_id IN token_parameters.list_id
205
139
  data: []
206
140
  `, { storageVersion }));
207
- const bucketStorage = factory.getInstance(syncRules);
208
- const writer = __addDisposableResource(env_3, await bucketStorage.createWriter(test_utils.BATCH_OPTIONS), true);
209
- const table = await test_utils.resolveTestTable(writer, 'todos', ['id', 'list_id'], config);
210
- await writer.markAllSnapshotDone('1/1');
211
- // Create two todos which initially belong to different lists
212
- await writer.save({
213
- sourceTable: table,
214
- tag: storage.SaveOperationTag.INSERT,
215
- after: {
216
- id: 'todo1',
217
- list_id: 'list1'
218
- },
219
- afterReplicaId: test_utils.rid('todo1')
220
- });
221
- await writer.save({
222
- sourceTable: table,
223
- tag: storage.SaveOperationTag.INSERT,
224
- after: {
225
- id: 'todo2',
226
- list_id: 'list2'
227
- },
228
- afterReplicaId: test_utils.rid('todo2')
229
- });
230
- await writer.commit('1/1');
231
- // Update the second todo item to now belong to list 1
232
- await writer.save({
233
- sourceTable: table,
234
- tag: storage.SaveOperationTag.UPDATE,
235
- after: {
236
- id: 'todo2',
237
- list_id: 'list1'
238
- },
239
- afterReplicaId: test_utils.rid('todo2')
240
- });
241
- await writer.commit('1/1');
242
- // We specifically request the todo_ids for both lists.
243
- // There removal operation for the association of `list2`::`todo2` should not interfere with the new
244
- // association of `list1`::`todo2`
245
- const checkpoint = await bucketStorage.getCheckpoint();
246
- const parameters = await checkpoint.getParameterSets([
247
- ScopedParameterLookup.direct(MYBUCKET_1, ['list1']),
248
- ScopedParameterLookup.direct(MYBUCKET_1, ['list2'])
249
- ]);
250
- expect(parameters.sort((a, b) => a.todo_id.localeCompare(b.todo_id))).toEqual([
251
- {
252
- todo_id: 'todo1'
253
- },
254
- {
255
- todo_id: 'todo2'
256
- }
257
- ]);
258
- }
259
- catch (e_3) {
260
- env_3.error = e_3;
261
- env_3.hasError = true;
262
- }
263
- finally {
264
- const result_3 = __disposeResources(env_3);
265
- if (result_3)
266
- await result_3;
267
- }
141
+ const bucketStorage = factory.getInstance(syncRules);
142
+ const sync_rules = syncRules.parsed(test_utils.PARSE_OPTIONS).hydratedSyncConfig();
143
+ await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
144
+ const table = await test_utils.resolveTestTable(writer, 'todos', ['id', 'list_id'], config);
145
+ await writer.markAllSnapshotDone('1/1');
146
+ // Create two todos which initially belong to different lists
147
+ await writer.save({
148
+ sourceTable: table,
149
+ tag: storage.SaveOperationTag.INSERT,
150
+ after: {
151
+ id: 'todo1',
152
+ list_id: 'list1'
153
+ },
154
+ afterReplicaId: test_utils.rid('todo1')
155
+ });
156
+ await writer.save({
157
+ sourceTable: table,
158
+ tag: storage.SaveOperationTag.INSERT,
159
+ after: {
160
+ id: 'todo2',
161
+ list_id: 'list2'
162
+ },
163
+ afterReplicaId: test_utils.rid('todo2')
164
+ });
165
+ await writer.commit('1/1');
166
+ // Update the second todo item to now belong to list 1
167
+ await writer.save({
168
+ sourceTable: table,
169
+ tag: storage.SaveOperationTag.UPDATE,
170
+ after: {
171
+ id: 'todo2',
172
+ list_id: 'list1'
173
+ },
174
+ afterReplicaId: test_utils.rid('todo2')
175
+ });
176
+ await writer.commit('1/1');
177
+ // We specifically request the todo_ids for both lists.
178
+ // There removal operation for the association of `list2`::`todo2` should not interfere with the new
179
+ // association of `list1`::`todo2`
180
+ const checkpoint = await bucketStorage.getCheckpoint();
181
+ const parameters = new RequestParameters(new JwtPayload({ sub: 'u1', parameters: { list_id: ['list1', 'list2'] } }), {});
182
+ const querier = sync_rules.getBucketParameterQuerier(test_utils.querierOptions(parameters)).querier;
183
+ const buckets = await querier.queryDynamicBucketDescriptions({
184
+ async getParameterSets(lookups) {
185
+ expect(lookups.map((l) => JSON.stringify(l.indexKey)).sort()).toEqual(['["list1"]', '["list2"]']);
186
+ const results = await checkpoint.getParameterSets(lookups, 1000);
187
+ const allRows = results.flatMap(({ rows }) => rows);
188
+ expect(allRows.sort((a, b) => a.todo_id.localeCompare(b.todo_id))).toEqual([
189
+ { todo_id: 'todo1' },
190
+ { todo_id: 'todo2' }
191
+ ]);
192
+ return results;
193
+ }
194
+ });
195
+ expect(buckets.map((b) => b.bucket).sort()).toEqual([
196
+ bucketRequest(syncRules, 'mybucket["todo1"]').bucket,
197
+ bucketRequest(syncRules, 'mybucket["todo2"]').bucket
198
+ ]);
268
199
  });
269
200
  test('save and load parameters with different number types', async () => {
270
- const env_4 = { stack: [], error: void 0, hasError: false };
271
- try {
272
- const factory = __addDisposableResource(env_4, await generateStorageFactory(), true);
273
- const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(`
201
+ await using factory = await generateStorageFactory();
202
+ const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(`
274
203
  bucket_definitions:
275
204
  mybucket:
276
205
  parameters:
277
206
  - SELECT group_id FROM test WHERE n1 = token_parameters.n1 and f2 = token_parameters.f2 and f3 = token_parameters.f3
278
207
  data: []
279
208
  `, {
280
- storageVersion
281
- }));
282
- const bucketStorage = factory.getInstance(syncRules);
283
- const writer = __addDisposableResource(env_4, await bucketStorage.createWriter(test_utils.BATCH_OPTIONS), true);
284
- const testTable = await test_utils.resolveTestTable(writer, 'test', ['id'], config);
285
- await writer.markAllSnapshotDone('1/1');
286
- await writer.save({
287
- sourceTable: testTable,
288
- tag: storage.SaveOperationTag.INSERT,
289
- after: {
290
- id: 't1',
291
- group_id: 'group1',
292
- n1: 314n,
293
- f2: 314,
294
- f3: 3.14
295
- },
296
- afterReplicaId: test_utils.rid('t1')
209
+ storageVersion
210
+ }));
211
+ const bucketStorage = factory.getInstance(syncRules);
212
+ const sync_rules = syncRules.parsed(test_utils.PARSE_OPTIONS).hydratedSyncConfig();
213
+ await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
214
+ const testTable = await test_utils.resolveTestTable(writer, 'test', ['id'], config);
215
+ await writer.markAllSnapshotDone('1/1');
216
+ await writer.save({
217
+ sourceTable: testTable,
218
+ tag: storage.SaveOperationTag.INSERT,
219
+ after: {
220
+ id: 't1',
221
+ group_id: 'group1',
222
+ n1: 314n,
223
+ f2: 314,
224
+ f3: 3.14
225
+ },
226
+ afterReplicaId: test_utils.rid('t1')
227
+ });
228
+ await writer.commit('1/1');
229
+ const checkpoint = await bucketStorage.getCheckpoint();
230
+ const testQuery = async (jwtParameters, expectedParameterSets) => {
231
+ const parameters = new RequestParameters(new JwtPayload({ sub: 'u1', parameters: jwtParameters }), {});
232
+ const querier = sync_rules.getBucketParameterQuerier(test_utils.querierOptions(parameters)).querier;
233
+ return await querier.queryDynamicBucketDescriptions({
234
+ async getParameterSets(lookups) {
235
+ const parameter_sets = await checkpoint.getParameterSets(lookups, 1000);
236
+ if (expectedParameterSets.length == 0) {
237
+ expect(parameter_sets).toEqual([]);
238
+ }
239
+ else {
240
+ expect(parameter_sets).toEqual([{ lookup: lookups[0], rows: expectedParameterSets }]);
241
+ }
242
+ return parameter_sets;
243
+ }
297
244
  });
298
- await writer.commit('1/1');
299
- const TEST_PARAMS = { group_id: 'group1' };
300
- const checkpoint = await bucketStorage.getCheckpoint();
301
- const parameters1 = await checkpoint.getParameterSets([
302
- ScopedParameterLookup.direct(MYBUCKET_1, [314n, 314, 3.14])
303
- ]);
304
- expect(parameters1).toEqual([TEST_PARAMS]);
305
- const parameters2 = await checkpoint.getParameterSets([
306
- ScopedParameterLookup.direct(MYBUCKET_1, [314, 314n, 3.14])
307
- ]);
308
- expect(parameters2).toEqual([TEST_PARAMS]);
309
- const parameters3 = await checkpoint.getParameterSets([ScopedParameterLookup.direct(MYBUCKET_1, [314n, 314, 3])]);
310
- expect(parameters3).toEqual([]);
311
- }
312
- catch (e_4) {
313
- env_4.error = e_4;
314
- env_4.hasError = true;
315
- }
316
- finally {
317
- const result_4 = __disposeResources(env_4);
318
- if (result_4)
319
- await result_4;
320
- }
245
+ };
246
+ expect(await testQuery({ n1: 314n, f2: 314, f3: 3.14 }, [{ group_id: 'group1' }])).toMatchObject([
247
+ { bucket: bucketRequest(syncRules, 'mybucket["group1"]').bucket }
248
+ ]);
249
+ expect(await testQuery({ n1: 314, f2: 314n, f3: 3.14 }, [{ group_id: 'group1' }])).toMatchObject([
250
+ { bucket: bucketRequest(syncRules, 'mybucket["group1"]').bucket }
251
+ ]);
252
+ expect(await testQuery({ n1: 314n, f2: 314, f3: 3 }, [])).toEqual([]);
321
253
  });
322
254
  test('save and load parameters with large numbers', async () => {
323
- const env_5 = { stack: [], error: void 0, hasError: false };
324
- try {
325
- // This ensures serialization / deserialization of "current_data" is done correctly.
326
- // This specific case tested here cannot happen with postgres in practice, but we still
327
- // test this to ensure correct deserialization.
328
- const factory = __addDisposableResource(env_5, await generateStorageFactory(), true);
329
- const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(`
255
+ // This ensures serialization / deserialization of "current_data" is done correctly.
256
+ // This specific case tested here cannot happen with postgres in practice, but we still
257
+ // test this to ensure correct deserialization.
258
+ await using factory = await generateStorageFactory();
259
+ const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(`
330
260
  bucket_definitions:
331
261
  mybucket:
332
262
  parameters:
333
263
  - SELECT group_id FROM test WHERE n1 = token_parameters.n1
334
264
  data: []
335
265
  `, {
336
- storageVersion
337
- }));
338
- const bucketStorage = factory.getInstance(syncRules);
339
- const writer = __addDisposableResource(env_5, await bucketStorage.createWriter(test_utils.BATCH_OPTIONS), true);
340
- const testTable = await test_utils.resolveTestTable(writer, 'test', ['id'], config);
341
- await writer.markAllSnapshotDone('1/1');
342
- await writer.save({
343
- sourceTable: testTable,
344
- tag: storage.SaveOperationTag.INSERT,
345
- after: {
346
- id: 't1',
347
- group_id: 'group1',
348
- n1: 1152921504606846976n // 2^60
349
- },
350
- afterReplicaId: test_utils.rid('t1')
351
- });
352
- await writer.save({
353
- sourceTable: testTable,
354
- tag: storage.SaveOperationTag.UPDATE,
355
- after: {
356
- id: 't1',
357
- group_id: 'group1',
358
- // Simulate a TOAST value, even though it can't happen for values like this
359
- // in practice.
360
- n1: undefined
361
- },
362
- afterReplicaId: test_utils.rid('t1')
363
- });
364
- await writer.commit('1/1');
365
- const TEST_PARAMS = { group_id: 'group1' };
366
- const checkpoint = await bucketStorage.getCheckpoint();
367
- const parameters1 = await checkpoint.getParameterSets([
368
- ScopedParameterLookup.direct(MYBUCKET_1, [1152921504606846976n])
369
- ]);
370
- expect(parameters1).toEqual([TEST_PARAMS]);
371
- }
372
- catch (e_5) {
373
- env_5.error = e_5;
374
- env_5.hasError = true;
375
- }
376
- finally {
377
- const result_5 = __disposeResources(env_5);
378
- if (result_5)
379
- await result_5;
380
- }
266
+ storageVersion
267
+ }));
268
+ const bucketStorage = factory.getInstance(syncRules);
269
+ const sync_rules = syncRules.parsed(test_utils.PARSE_OPTIONS).hydratedSyncConfig();
270
+ await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
271
+ const testTable = await test_utils.resolveTestTable(writer, 'test', ['id'], config);
272
+ await writer.markAllSnapshotDone('1/1');
273
+ await writer.save({
274
+ sourceTable: testTable,
275
+ tag: storage.SaveOperationTag.INSERT,
276
+ after: {
277
+ id: 't1',
278
+ group_id: 'group1',
279
+ n1: 1152921504606846976n // 2^60
280
+ },
281
+ afterReplicaId: test_utils.rid('t1')
282
+ });
283
+ await writer.save({
284
+ sourceTable: testTable,
285
+ tag: storage.SaveOperationTag.UPDATE,
286
+ after: {
287
+ id: 't1',
288
+ group_id: 'group1',
289
+ // Simulate a TOAST value, even though it can't happen for values like this
290
+ // in practice.
291
+ n1: undefined
292
+ },
293
+ afterReplicaId: test_utils.rid('t1')
294
+ });
295
+ await writer.commit('1/1');
296
+ const checkpoint = await bucketStorage.getCheckpoint();
297
+ const n1 = 1152921504606846976n;
298
+ const parameters = new RequestParameters(new JwtPayload({ sub: 'u1', parameters: { n1 } }), {});
299
+ const querier = sync_rules.getBucketParameterQuerier(test_utils.querierOptions(parameters)).querier;
300
+ const buckets = await querier.queryDynamicBucketDescriptions({
301
+ getParameterSets: async (lookups) => {
302
+ expect(lookups.map((l) => l.indexKey)).toEqual([[n1]]);
303
+ const parameter_sets = await checkpoint.getParameterSets(lookups, 1000);
304
+ expect(parameter_sets).toEqual([{ lookup: lookups[0], rows: [{ group_id: 'group1' }] }]);
305
+ return parameter_sets;
306
+ }
307
+ });
308
+ expect(buckets.map((b) => b.bucket)).toEqual([bucketRequest(syncRules, 'mybucket["group1"]').bucket]);
381
309
  });
382
310
  test('save and load parameters with workspaceId', async () => {
383
- const env_6 = { stack: [], error: void 0, hasError: false };
384
- try {
385
- const factory = __addDisposableResource(env_6, await generateStorageFactory(), true);
386
- const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(`
311
+ await using factory = await generateStorageFactory();
312
+ const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(`
387
313
  bucket_definitions:
388
314
  by_workspace:
389
315
  parameters:
@@ -391,58 +317,46 @@ bucket_definitions:
391
317
  workspace."userId" = token_parameters.user_id
392
318
  data: []
393
319
  `, {
394
- storageVersion
395
- }));
396
- const sync_rules = syncRules.parsed(test_utils.PARSE_OPTIONS).hydratedSyncRules();
397
- const bucketStorage = factory.getInstance(syncRules);
398
- const writer = __addDisposableResource(env_6, await bucketStorage.createWriter(test_utils.BATCH_OPTIONS), true);
399
- const workspaceTable = await test_utils.resolveTestTable(writer, 'workspace', ['id'], config);
400
- await writer.markAllSnapshotDone('1/1');
401
- await writer.save({
402
- sourceTable: workspaceTable,
403
- tag: storage.SaveOperationTag.INSERT,
404
- after: {
405
- id: 'workspace1',
406
- userId: 'u1'
407
- },
408
- afterReplicaId: test_utils.rid('workspace1')
409
- });
410
- await writer.commit('1/1');
411
- const checkpoint = await bucketStorage.getCheckpoint();
412
- const parameters = new RequestParameters(new JwtPayload({ sub: 'u1' }), {});
413
- const querier = sync_rules.getBucketParameterQuerier(test_utils.querierOptions(parameters)).querier;
414
- const buckets = await querier.queryDynamicBucketDescriptions({
415
- async getParameterSets(lookups) {
416
- expect(lookups).toEqual([ScopedParameterLookup.direct(parameterLookupScope('by_workspace', '1'), ['u1'])]);
417
- const parameter_sets = await checkpoint.getParameterSets(lookups);
418
- expect(parameter_sets).toEqual([{ workspace_id: 'workspace1' }]);
419
- return parameter_sets;
420
- }
421
- });
422
- expect(buckets).toEqual([
423
- {
424
- bucket: bucketRequest(syncRules, 'by_workspace["workspace1"]').bucket,
425
- priority: 3,
426
- definition: 'by_workspace',
427
- inclusion_reasons: ['default']
428
- }
429
- ]);
430
- }
431
- catch (e_6) {
432
- env_6.error = e_6;
433
- env_6.hasError = true;
434
- }
435
- finally {
436
- const result_6 = __disposeResources(env_6);
437
- if (result_6)
438
- await result_6;
439
- }
320
+ storageVersion
321
+ }));
322
+ const sync_rules = syncRules.parsed(test_utils.PARSE_OPTIONS).hydratedSyncConfig();
323
+ const bucketStorage = factory.getInstance(syncRules);
324
+ await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
325
+ const workspaceTable = await test_utils.resolveTestTable(writer, 'workspace', ['id'], config);
326
+ await writer.markAllSnapshotDone('1/1');
327
+ await writer.save({
328
+ sourceTable: workspaceTable,
329
+ tag: storage.SaveOperationTag.INSERT,
330
+ after: {
331
+ id: 'workspace1',
332
+ userId: 'u1'
333
+ },
334
+ afterReplicaId: test_utils.rid('workspace1')
335
+ });
336
+ await writer.commit('1/1');
337
+ const checkpoint = await bucketStorage.getCheckpoint();
338
+ const parameters = new RequestParameters(new JwtPayload({ sub: 'u1' }), {});
339
+ const querier = sync_rules.getBucketParameterQuerier(test_utils.querierOptions(parameters)).querier;
340
+ const buckets = await querier.queryDynamicBucketDescriptions({
341
+ async getParameterSets(lookups) {
342
+ expect(lookups.map((l) => l.indexKey)).toEqual([['u1']]);
343
+ const parameter_sets = await checkpoint.getParameterSets(lookups, 1000);
344
+ expect(parameter_sets).toEqual([{ lookup: lookups[0], rows: [{ workspace_id: 'workspace1' }] }]);
345
+ return parameter_sets;
346
+ }
347
+ });
348
+ expect(buckets).toEqual([
349
+ {
350
+ bucket: bucketRequest(syncRules, 'by_workspace["workspace1"]').bucket,
351
+ priority: 3,
352
+ definition: 'by_workspace',
353
+ inclusion_reasons: ['default']
354
+ }
355
+ ]);
440
356
  });
441
357
  test('save and load parameters with dynamic global buckets', async () => {
442
- const env_7 = { stack: [], error: void 0, hasError: false };
443
- try {
444
- const factory = __addDisposableResource(env_7, await generateStorageFactory(), true);
445
- const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(`
358
+ await using factory = await generateStorageFactory();
359
+ const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(`
446
360
  bucket_definitions:
447
361
  by_public_workspace:
448
362
  parameters:
@@ -450,84 +364,75 @@ bucket_definitions:
450
364
  workspace.visibility = 'public'
451
365
  data: []
452
366
  `, {
453
- storageVersion
454
- }));
455
- const sync_rules = syncRules.parsed(test_utils.PARSE_OPTIONS).hydratedSyncRules();
456
- const bucketStorage = factory.getInstance(syncRules);
457
- const writer = __addDisposableResource(env_7, await bucketStorage.createWriter(test_utils.BATCH_OPTIONS), true);
458
- const workspaceTable = await test_utils.resolveTestTable(writer, 'workspace', undefined, config);
459
- await writer.markAllSnapshotDone('1/1');
460
- await writer.save({
461
- sourceTable: workspaceTable,
462
- tag: storage.SaveOperationTag.INSERT,
463
- after: {
464
- id: 'workspace1',
465
- visibility: 'public'
466
- },
467
- afterReplicaId: test_utils.rid('workspace1')
468
- });
469
- await writer.save({
470
- sourceTable: workspaceTable,
471
- tag: storage.SaveOperationTag.INSERT,
472
- after: {
473
- id: 'workspace2',
474
- visibility: 'private'
475
- },
476
- afterReplicaId: test_utils.rid('workspace2')
477
- });
478
- await writer.save({
479
- sourceTable: workspaceTable,
480
- tag: storage.SaveOperationTag.INSERT,
481
- after: {
482
- id: 'workspace3',
483
- visibility: 'public'
484
- },
485
- afterReplicaId: test_utils.rid('workspace3')
486
- });
487
- await writer.commit('1/1');
488
- const checkpoint = await bucketStorage.getCheckpoint();
489
- const parameters = new RequestParameters(new JwtPayload({ sub: 'unknown' }), {});
490
- const querier = sync_rules.getBucketParameterQuerier(test_utils.querierOptions(parameters)).querier;
491
- const buckets = await querier.queryDynamicBucketDescriptions({
492
- async getParameterSets(lookups) {
493
- expect(lookups).toEqual([ScopedParameterLookup.direct(parameterLookupScope('by_public_workspace', '1'), [])]);
494
- const parameter_sets = await checkpoint.getParameterSets(lookups);
495
- parameter_sets.sort((a, b) => JSON.stringify(a).localeCompare(JSON.stringify(b)));
496
- expect(parameter_sets).toEqual([{ workspace_id: 'workspace1' }, { workspace_id: 'workspace3' }]);
497
- return parameter_sets;
498
- }
499
- });
500
- buckets.sort((a, b) => a.bucket.localeCompare(b.bucket));
501
- expect(buckets).toEqual([
502
- {
503
- bucket: bucketRequest(syncRules, 'by_public_workspace["workspace1"]').bucket,
504
- priority: 3,
505
- definition: 'by_public_workspace',
506
- inclusion_reasons: ['default']
507
- },
508
- {
509
- bucket: bucketRequest(syncRules, 'by_public_workspace["workspace3"]').bucket,
510
- priority: 3,
511
- definition: 'by_public_workspace',
512
- inclusion_reasons: ['default']
513
- }
514
- ]);
515
- }
516
- catch (e_7) {
517
- env_7.error = e_7;
518
- env_7.hasError = true;
519
- }
520
- finally {
521
- const result_7 = __disposeResources(env_7);
522
- if (result_7)
523
- await result_7;
524
- }
367
+ storageVersion
368
+ }));
369
+ const sync_rules = syncRules.parsed(test_utils.PARSE_OPTIONS).hydratedSyncConfig();
370
+ const bucketStorage = factory.getInstance(syncRules);
371
+ await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
372
+ const workspaceTable = await test_utils.resolveTestTable(writer, 'workspace', undefined, config);
373
+ await writer.markAllSnapshotDone('1/1');
374
+ await writer.save({
375
+ sourceTable: workspaceTable,
376
+ tag: storage.SaveOperationTag.INSERT,
377
+ after: {
378
+ id: 'workspace1',
379
+ visibility: 'public'
380
+ },
381
+ afterReplicaId: test_utils.rid('workspace1')
382
+ });
383
+ await writer.save({
384
+ sourceTable: workspaceTable,
385
+ tag: storage.SaveOperationTag.INSERT,
386
+ after: {
387
+ id: 'workspace2',
388
+ visibility: 'private'
389
+ },
390
+ afterReplicaId: test_utils.rid('workspace2')
391
+ });
392
+ await writer.save({
393
+ sourceTable: workspaceTable,
394
+ tag: storage.SaveOperationTag.INSERT,
395
+ after: {
396
+ id: 'workspace3',
397
+ visibility: 'public'
398
+ },
399
+ afterReplicaId: test_utils.rid('workspace3')
400
+ });
401
+ await writer.commit('1/1');
402
+ const checkpoint = await bucketStorage.getCheckpoint();
403
+ const parameters = new RequestParameters(new JwtPayload({ sub: 'unknown' }), {});
404
+ const querier = sync_rules.getBucketParameterQuerier(test_utils.querierOptions(parameters)).querier;
405
+ const buckets = await querier.queryDynamicBucketDescriptions({
406
+ async getParameterSets(lookups) {
407
+ expect(lookups.map((l) => l.indexKey)).toEqual([[]]);
408
+ const parameter_sets = await checkpoint.getParameterSets(lookups, 1000);
409
+ expect(parameter_sets).toHaveLength(1);
410
+ const [{ lookup, rows }] = parameter_sets;
411
+ expect(lookup).toEqual(lookups[0]);
412
+ rows.sort((a, b) => JSON.stringify(a).localeCompare(JSON.stringify(b)));
413
+ expect(rows).toEqual([{ workspace_id: 'workspace1' }, { workspace_id: 'workspace3' }]);
414
+ return parameter_sets;
415
+ }
416
+ });
417
+ buckets.sort((a, b) => a.bucket.localeCompare(b.bucket));
418
+ expect(buckets).toEqual([
419
+ {
420
+ bucket: bucketRequest(syncRules, 'by_public_workspace["workspace1"]').bucket,
421
+ priority: 3,
422
+ definition: 'by_public_workspace',
423
+ inclusion_reasons: ['default']
424
+ },
425
+ {
426
+ bucket: bucketRequest(syncRules, 'by_public_workspace["workspace3"]').bucket,
427
+ priority: 3,
428
+ definition: 'by_public_workspace',
429
+ inclusion_reasons: ['default']
430
+ }
431
+ ]);
525
432
  });
526
433
  test('multiple parameter queries', async () => {
527
- const env_8 = { stack: [], error: void 0, hasError: false };
528
- try {
529
- const factory = __addDisposableResource(env_8, await generateStorageFactory(), true);
530
- const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(`
434
+ await using factory = await generateStorageFactory();
435
+ const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(`
531
436
  bucket_definitions:
532
437
  by_workspace:
533
438
  parameters:
@@ -537,138 +442,124 @@ bucket_definitions:
537
442
  workspace.user_id = token_parameters.user_id
538
443
  data: []
539
444
  `, {
540
- storageVersion
541
- }));
542
- const sync_rules = syncRules.parsed(test_utils.PARSE_OPTIONS).hydratedSyncRules();
543
- const bucketStorage = factory.getInstance(syncRules);
544
- const writer = __addDisposableResource(env_8, await bucketStorage.createWriter(test_utils.BATCH_OPTIONS), true);
545
- const workspaceTable = await test_utils.resolveTestTable(writer, 'workspace', undefined, config);
546
- await writer.markAllSnapshotDone('1/1');
547
- await writer.save({
548
- sourceTable: workspaceTable,
549
- tag: storage.SaveOperationTag.INSERT,
550
- after: {
551
- id: 'workspace1',
552
- visibility: 'public'
553
- },
554
- afterReplicaId: test_utils.rid('workspace1')
555
- });
556
- await writer.save({
557
- sourceTable: workspaceTable,
558
- tag: storage.SaveOperationTag.INSERT,
559
- after: {
560
- id: 'workspace2',
561
- visibility: 'private'
562
- },
563
- afterReplicaId: test_utils.rid('workspace2')
564
- });
565
- await writer.save({
566
- sourceTable: workspaceTable,
567
- tag: storage.SaveOperationTag.INSERT,
568
- after: {
569
- id: 'workspace3',
570
- user_id: 'u1',
571
- visibility: 'private'
572
- },
573
- afterReplicaId: test_utils.rid('workspace3')
574
- });
575
- await writer.save({
576
- sourceTable: workspaceTable,
577
- tag: storage.SaveOperationTag.INSERT,
578
- after: {
579
- id: 'workspace4',
580
- user_id: 'u2',
581
- visibility: 'private'
582
- },
583
- afterReplicaId: test_utils.rid('workspace4')
584
- });
585
- await writer.commit('1/1');
586
- const checkpoint = await bucketStorage.getCheckpoint();
587
- const parameters = new RequestParameters(new JwtPayload({ sub: 'u1' }), {});
588
- // Test intermediate values - could be moved to sync_rules.test.ts
589
- const querier = sync_rules.getBucketParameterQuerier(test_utils.querierOptions(parameters)).querier;
590
- // Test final values - the important part
591
- const foundLookups = [];
592
- const parameter_sets = [];
593
- const buckets = (await querier.queryDynamicBucketDescriptions({
594
- async getParameterSets(lookups) {
595
- foundLookups.push(...lookups);
596
- const output = await checkpoint.getParameterSets(lookups);
597
- parameter_sets.push(...output);
598
- return output;
445
+ storageVersion
446
+ }));
447
+ const sync_rules = syncRules.parsed(test_utils.PARSE_OPTIONS).hydratedSyncConfig();
448
+ const bucketStorage = factory.getInstance(syncRules);
449
+ await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
450
+ const workspaceTable = await test_utils.resolveTestTable(writer, 'workspace', undefined, config);
451
+ await writer.markAllSnapshotDone('1/1');
452
+ await writer.save({
453
+ sourceTable: workspaceTable,
454
+ tag: storage.SaveOperationTag.INSERT,
455
+ after: {
456
+ id: 'workspace1',
457
+ visibility: 'public'
458
+ },
459
+ afterReplicaId: test_utils.rid('workspace1')
460
+ });
461
+ await writer.save({
462
+ sourceTable: workspaceTable,
463
+ tag: storage.SaveOperationTag.INSERT,
464
+ after: {
465
+ id: 'workspace2',
466
+ visibility: 'private'
467
+ },
468
+ afterReplicaId: test_utils.rid('workspace2')
469
+ });
470
+ await writer.save({
471
+ sourceTable: workspaceTable,
472
+ tag: storage.SaveOperationTag.INSERT,
473
+ after: {
474
+ id: 'workspace3',
475
+ user_id: 'u1',
476
+ visibility: 'private'
477
+ },
478
+ afterReplicaId: test_utils.rid('workspace3')
479
+ });
480
+ await writer.save({
481
+ sourceTable: workspaceTable,
482
+ tag: storage.SaveOperationTag.INSERT,
483
+ after: {
484
+ id: 'workspace4',
485
+ user_id: 'u2',
486
+ visibility: 'private'
487
+ },
488
+ afterReplicaId: test_utils.rid('workspace4')
489
+ });
490
+ await writer.commit('1/1');
491
+ const checkpoint = await bucketStorage.getCheckpoint();
492
+ const parameters = new RequestParameters(new JwtPayload({ sub: 'u1' }), {});
493
+ // Test intermediate values - could be moved to sync_rules.test.ts
494
+ const querier = sync_rules.getBucketParameterQuerier(test_utils.querierOptions(parameters)).querier;
495
+ // Test final values - the important part
496
+ const foundLookups = [];
497
+ const parameter_sets = [];
498
+ const buckets = (await querier.queryDynamicBucketDescriptions({
499
+ async getParameterSets(lookups) {
500
+ foundLookups.push(...lookups);
501
+ const output = await checkpoint.getParameterSets(lookups, 1000);
502
+ for (const { rows } of output) {
503
+ parameter_sets.push(...rows);
599
504
  }
600
- })).map((e) => e.bucket);
601
- expect(foundLookups).toEqual([
602
- ScopedParameterLookup.direct(parameterLookupScope('by_workspace', '1'), []),
603
- ScopedParameterLookup.direct(parameterLookupScope('by_workspace', '2'), ['u1'])
604
- ]);
605
- parameter_sets.sort((a, b) => JSON.stringify(a).localeCompare(JSON.stringify(b)));
606
- expect(parameter_sets).toEqual([{ workspace_id: 'workspace1' }, { workspace_id: 'workspace3' }]);
607
- buckets.sort();
608
- expect(buckets).toEqual([
609
- bucketRequest(syncRules, 'by_workspace["workspace1"]').bucket,
610
- bucketRequest(syncRules, 'by_workspace["workspace3"]').bucket
611
- ]);
612
- }
613
- catch (e_8) {
614
- env_8.error = e_8;
615
- env_8.hasError = true;
616
- }
617
- finally {
618
- const result_8 = __disposeResources(env_8);
619
- if (result_8)
620
- await result_8;
621
- }
505
+ return output;
506
+ }
507
+ })).map((e) => e.bucket);
508
+ // Not testing the scope anymore - the exact format depends on storage version
509
+ expect(foundLookups.map((l) => l.indexKey)).toEqual([[], ['u1']]);
510
+ parameter_sets.sort((a, b) => JSON.stringify(a).localeCompare(JSON.stringify(b)));
511
+ expect(parameter_sets).toEqual([{ workspace_id: 'workspace1' }, { workspace_id: 'workspace3' }]);
512
+ buckets.sort();
513
+ expect(buckets).toEqual([
514
+ bucketRequest(syncRules, 'by_workspace["workspace1"]').bucket,
515
+ bucketRequest(syncRules, 'by_workspace["workspace3"]').bucket
516
+ ]);
622
517
  });
623
518
  test('truncate parameters', async () => {
624
- const env_9 = { stack: [], error: void 0, hasError: false };
625
- try {
626
- const factory = __addDisposableResource(env_9, await generateStorageFactory(), true);
627
- const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(`
519
+ await using factory = await generateStorageFactory();
520
+ const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(`
628
521
  bucket_definitions:
629
522
  mybucket:
630
523
  parameters:
631
524
  - SELECT group_id FROM test WHERE id1 = token_parameters.user_id OR id2 = token_parameters.user_id
632
525
  data: []
633
526
  `, {
634
- storageVersion
635
- }));
636
- const bucketStorage = factory.getInstance(syncRules);
637
- const writer = __addDisposableResource(env_9, await bucketStorage.createWriter(test_utils.BATCH_OPTIONS), true);
638
- const testTable = await test_utils.resolveTestTable(writer, 'test', ['id'], config);
639
- await writer.markAllSnapshotDone('1/1');
640
- await writer.save({
641
- sourceTable: testTable,
642
- tag: storage.SaveOperationTag.INSERT,
643
- after: {
644
- id: 't2',
645
- id1: 'user3',
646
- id2: 'user4',
647
- group_id: 'group2a'
648
- },
649
- afterReplicaId: test_utils.rid('t2')
650
- });
651
- await writer.truncate([testTable]);
652
- await writer.flush();
653
- const checkpoint = await bucketStorage.getCheckpoint();
654
- const parameters = await checkpoint.getParameterSets([ScopedParameterLookup.direct(MYBUCKET_1, ['user1'])]);
655
- expect(parameters).toEqual([]);
656
- }
657
- catch (e_9) {
658
- env_9.error = e_9;
659
- env_9.hasError = true;
660
- }
661
- finally {
662
- const result_9 = __disposeResources(env_9);
663
- if (result_9)
664
- await result_9;
665
- }
527
+ storageVersion
528
+ }));
529
+ const bucketStorage = factory.getInstance(syncRules);
530
+ const sync_rules = syncRules.parsed(test_utils.PARSE_OPTIONS).hydratedSyncConfig();
531
+ await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
532
+ const testTable = await test_utils.resolveTestTable(writer, 'test', ['id'], config);
533
+ await writer.markAllSnapshotDone('1/1');
534
+ await writer.save({
535
+ sourceTable: testTable,
536
+ tag: storage.SaveOperationTag.INSERT,
537
+ after: {
538
+ id: 't2',
539
+ id1: 'user3',
540
+ id2: 'user4',
541
+ group_id: 'group2a'
542
+ },
543
+ afterReplicaId: test_utils.rid('t2')
544
+ });
545
+ await writer.truncate([testTable]);
546
+ await writer.flush();
547
+ const checkpoint = await bucketStorage.getCheckpoint();
548
+ const parameters = new RequestParameters(new JwtPayload({ sub: 'user1' }), {});
549
+ const querier = sync_rules.getBucketParameterQuerier(test_utils.querierOptions(parameters)).querier;
550
+ const buckets = await querier.queryDynamicBucketDescriptions({
551
+ async getParameterSets(lookups) {
552
+ expect(lookups.map((l) => l.indexKey)).toEqual([['user1']]);
553
+ const parameter_sets = await checkpoint.getParameterSets(lookups, 1000);
554
+ expect(parameter_sets).toEqual([]);
555
+ return parameter_sets;
556
+ }
557
+ });
558
+ expect(buckets).toEqual([]);
666
559
  });
667
560
  test('invalidate cached parsed sync rules', async () => {
668
- const env_10 = { stack: [], error: void 0, hasError: false };
669
- try {
670
- const bucketStorageFactory = __addDisposableResource(env_10, await generateStorageFactory(), true);
671
- const syncRules = await bucketStorageFactory.updateSyncRules(updateSyncRulesFromYaml(`
561
+ await using bucketStorageFactory = await generateStorageFactory();
562
+ const syncRules = await bucketStorageFactory.updateSyncRules(updateSyncRulesFromYaml(`
672
563
  bucket_definitions:
673
564
  by_workspace:
674
565
  parameters:
@@ -676,40 +567,28 @@ bucket_definitions:
676
567
  workspace."userId" = token_parameters.user_id
677
568
  data: []
678
569
  `, {
679
- storageVersion
680
- }));
681
- const syncBucketStorage = bucketStorageFactory.getInstance(syncRules);
682
- const parsedSchema1 = syncBucketStorage.getParsedSyncRules({
683
- defaultSchema: 'public'
684
- });
685
- const parsedSchema2 = syncBucketStorage.getParsedSyncRules({
686
- defaultSchema: 'public'
687
- });
688
- // These should be cached, this will be the same instance
689
- expect(parsedSchema2).equals(parsedSchema1);
690
- expect(parsedSchema1.getSourceTables()[0].schema).equals('public');
691
- const parsedSchema3 = syncBucketStorage.getParsedSyncRules({
692
- defaultSchema: 'databasename'
693
- });
694
- // The cache should not be used
695
- expect(parsedSchema3).not.equals(parsedSchema2);
696
- expect(parsedSchema3.getSourceTables()[0].schema).equals('databasename');
697
- }
698
- catch (e_10) {
699
- env_10.error = e_10;
700
- env_10.hasError = true;
701
- }
702
- finally {
703
- const result_10 = __disposeResources(env_10);
704
- if (result_10)
705
- await result_10;
706
- }
570
+ storageVersion
571
+ }));
572
+ const syncBucketStorage = bucketStorageFactory.getInstance(syncRules);
573
+ const parsedSchema1 = syncBucketStorage.getParsedSyncRules({
574
+ defaultSchema: 'public'
575
+ });
576
+ const parsedSchema2 = syncBucketStorage.getParsedSyncRules({
577
+ defaultSchema: 'public'
578
+ });
579
+ // These should be cached, this will be the same instance
580
+ expect(parsedSchema2).equals(parsedSchema1);
581
+ expect(parsedSchema1.getSourceTables()[0].schema).equals('public');
582
+ const parsedSchema3 = syncBucketStorage.getParsedSyncRules({
583
+ defaultSchema: 'databasename'
584
+ });
585
+ // The cache should not be used
586
+ expect(parsedSchema3).not.equals(parsedSchema2);
587
+ expect(parsedSchema3.getSourceTables()[0].schema).equals('databasename');
707
588
  });
708
589
  test('sync streams smoke test', async () => {
709
- const env_11 = { stack: [], error: void 0, hasError: false };
710
- try {
711
- const factory = __addDisposableResource(env_11, await generateStorageFactory(), true);
712
- const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(`
590
+ await using factory = await generateStorageFactory();
591
+ const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(`
713
592
  config:
714
593
  edition: 3
715
594
 
@@ -719,39 +598,347 @@ streams:
719
598
  SELECT data.* FROM test AS data, test AS param
720
599
  WHERE data.foo = param.bar AND param.baz = auth.user_id()
721
600
  `));
722
- const bucketStorage = factory.getInstance(syncRules);
723
- const writer = __addDisposableResource(env_11, await bucketStorage.createWriter(test_utils.BATCH_OPTIONS), true);
724
- const testTable = await test_utils.resolveTestTable(writer, 'test', ['id'], config);
725
- await writer.markAllSnapshotDone('1/1');
601
+ const bucketStorage = factory.getInstance(syncRules);
602
+ const sync_rules = syncRules.parsed(test_utils.PARSE_OPTIONS).hydratedSyncConfig();
603
+ await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
604
+ const testTable = await test_utils.resolveTestTable(writer, 'test', ['id'], config);
605
+ await writer.markAllSnapshotDone('1/1');
606
+ await writer.save({
607
+ sourceTable: testTable,
608
+ tag: storage.SaveOperationTag.INSERT,
609
+ after: {
610
+ baz: 'baz',
611
+ bar: 'bar'
612
+ },
613
+ afterReplicaId: test_utils.rid('t1')
614
+ });
615
+ await writer.commit('1/1');
616
+ const checkpoint = await bucketStorage.getCheckpoint();
617
+ const parameters = new RequestParameters(new JwtPayload({ sub: 'baz' }), {});
618
+ const querier = sync_rules.getBucketParameterQuerier({
619
+ ...test_utils.querierOptions(parameters),
620
+ streams: {
621
+ stream: [
622
+ {
623
+ priorityOverride: null,
624
+ parameters: null,
625
+ opaque_id: 123
626
+ }
627
+ ]
628
+ }
629
+ }).querier;
630
+ const buckets = await querier.queryDynamicBucketDescriptions({
631
+ async getParameterSets(lookups) {
632
+ expect(lookups.map((l) => l.indexKey)).toEqual([['baz']]);
633
+ const parameter_sets = await checkpoint.getParameterSets(lookups, 1000);
634
+ expect(parameter_sets).toEqual([{ lookup: lookups[0], rows: [{ '0': 'bar' }] }]);
635
+ return parameter_sets;
636
+ }
637
+ });
638
+ expect(buckets).toHaveLength(1);
639
+ expect(buckets).toMatchObject([
640
+ {
641
+ bucket: expect.stringMatching(/stream.*\["bar"\]$/),
642
+ definition: 'stream',
643
+ inclusion_reasons: [{ subscription: 123 }],
644
+ priority: 3
645
+ }
646
+ ]);
647
+ });
648
+ test('respects parameter limit', async () => {
649
+ await using factory = await generateStorageFactory();
650
+ const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(`
651
+ config:
652
+ edition: 3
653
+
654
+ streams:
655
+ a:
656
+ auto_subscribe: true
657
+ query: SELECT * FROM a WHERE id IN (SELECT id FROM b)
658
+ `, {
659
+ storageVersion
660
+ }));
661
+ const bucketStorage = factory.getInstance(syncRules);
662
+ const sync_rules = syncRules.parsed(test_utils.PARSE_OPTIONS).hydratedSyncConfig();
663
+ await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
664
+ const testTable = await test_utils.resolveTestTable(writer, 'b', ['id'], config);
665
+ await writer.markAllSnapshotDone('1/1');
666
+ for (let i = 0; i < 10; i++) {
726
667
  await writer.save({
727
668
  sourceTable: testTable,
728
669
  tag: storage.SaveOperationTag.INSERT,
729
670
  after: {
730
- baz: 'baz',
731
- bar: 'bar'
671
+ id: `t${i}`
732
672
  },
733
- afterReplicaId: test_utils.rid('t1')
673
+ afterReplicaId: test_utils.rid(`t${i}`)
734
674
  });
735
- await writer.commit('1/1');
736
- const checkpoint = await bucketStorage.getCheckpoint();
737
- const parameters = await checkpoint.getParameterSets([
738
- ScopedParameterLookup.direct(parameterLookupScope('lookup', '0'), ['baz'])
739
- ]);
740
- expect(parameters).toEqual([
741
- {
742
- '0': 'bar'
743
- }
744
- ]);
745
675
  }
746
- catch (e_11) {
747
- env_11.error = e_11;
748
- env_11.hasError = true;
749
- }
750
- finally {
751
- const result_11 = __disposeResources(env_11);
752
- if (result_11)
753
- await result_11;
676
+ await writer.commit('1/1');
677
+ const checkpoint = await bucketStorage.getCheckpoint();
678
+ const parameters = new RequestParameters(new JwtPayload({ sub: 'u' }), {});
679
+ const querier = sync_rules.getBucketParameterQuerier(test_utils.querierOptions(parameters)).querier;
680
+ await expect(querier.queryDynamicBucketDescriptions({
681
+ async getParameterSets(lookups) {
682
+ const parameter_sets = await checkpoint.getParameterSets(lookups, 5);
683
+ return parameter_sets;
684
+ }
685
+ })).rejects.toThrow('Too many parameter results (limit was 5)');
686
+ });
687
+ test('sync streams store multiple parameter outputs for a single source row and lookup', async () => {
688
+ await using factory = await generateStorageFactory();
689
+ const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(`
690
+ config:
691
+ edition: 3
692
+ streams:
693
+ chat:
694
+ auto_subscribe: true
695
+ query: |
696
+ SELECT a.*
697
+ FROM a, b, json_each(b.x) x, json_each(b.y) y
698
+ WHERE a.x = x.value AND y.value = auth.user_id()
699
+ `));
700
+ const bucketStorage = factory.getInstance(syncRules);
701
+ const sync_rules = syncRules.parsed(test_utils.PARSE_OPTIONS).hydratedSyncConfig();
702
+ await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
703
+ const tableB = await test_utils.resolveTestTable(writer, 'b', ['id'], config);
704
+ const firstState = {
705
+ id: 'id0',
706
+ x: JSON.stringify(['x1', 'x2']),
707
+ y: JSON.stringify(['y1', 'y2'])
708
+ };
709
+ const secondState = {
710
+ id: 'id0',
711
+ x: JSON.stringify(['x2']),
712
+ y: JSON.stringify(['y1', 'y2'])
713
+ };
714
+ const thirdState = {
715
+ id: 'id0',
716
+ x: JSON.stringify(['x2']),
717
+ y: JSON.stringify(['y2'])
718
+ };
719
+ const replicaId = test_utils.rid('id0');
720
+ await writer.markAllSnapshotDone('1/1');
721
+ await writer.save({
722
+ sourceTable: tableB,
723
+ tag: storage.SaveOperationTag.INSERT,
724
+ after: firstState,
725
+ afterReplicaId: replicaId
726
+ });
727
+ await writer.commit('1/1');
728
+ let checkpoint = await bucketStorage.getCheckpoint();
729
+ const parameters = new RequestParameters(new JwtPayload({ sub: 'y1' }), {});
730
+ const querier = sync_rules.getBucketParameterQuerier({
731
+ ...test_utils.querierOptions(parameters)
732
+ }).querier;
733
+ let buckets = await querier.queryDynamicBucketDescriptions({
734
+ async getParameterSets(lookups) {
735
+ expect(lookups.map((l) => l.indexKey)).toEqual([['y1']]);
736
+ const parameter_sets = await checkpoint.getParameterSets(lookups, 1000);
737
+ expect(parameter_sets).toEqual([{ lookup: lookups[0], rows: [{ '0': 'x1' }, { '0': 'x2' }] }]);
738
+ return parameter_sets;
739
+ }
740
+ });
741
+ expect(buckets).toMatchObject([
742
+ {
743
+ bucket: expect.stringMatching(/chat.*\["x1"\]$/),
744
+ definition: 'chat',
745
+ inclusion_reasons: ['default'],
746
+ priority: 3
747
+ },
748
+ {
749
+ bucket: expect.stringMatching(/chat.*\["x2"\]$/),
750
+ definition: 'chat',
751
+ inclusion_reasons: ['default'],
752
+ priority: 3
753
+ }
754
+ ]);
755
+ // Make the x2 bucket inaccessible
756
+ await writer.save({
757
+ sourceTable: tableB,
758
+ tag: storage.SaveOperationTag.UPDATE,
759
+ before: firstState,
760
+ after: secondState,
761
+ beforeReplicaId: replicaId,
762
+ afterReplicaId: replicaId
763
+ });
764
+ await writer.commit('1/2');
765
+ checkpoint = await bucketStorage.getCheckpoint();
766
+ buckets = await querier.queryDynamicBucketDescriptions({
767
+ async getParameterSets(lookups) {
768
+ expect(lookups.map((l) => l.indexKey)).toEqual([['y1']]);
769
+ const parameter_sets = await checkpoint.getParameterSets(lookups, 1000);
770
+ expect(parameter_sets).toEqual([{ lookup: lookups[0], rows: [{ '0': 'x2' }] }]);
771
+ return parameter_sets;
772
+ }
773
+ });
774
+ expect(buckets.map((bkt) => bkt.bucket)).toStrictEqual([expect.stringContaining('x2')]);
775
+ // Second update, remove user from inputs
776
+ await writer.save({
777
+ sourceTable: tableB,
778
+ tag: storage.SaveOperationTag.UPDATE,
779
+ before: secondState,
780
+ after: thirdState,
781
+ beforeReplicaId: replicaId,
782
+ afterReplicaId: replicaId
783
+ });
784
+ await writer.commit('1/3');
785
+ checkpoint = await bucketStorage.getCheckpoint();
786
+ buckets = await querier.queryDynamicBucketDescriptions({
787
+ async getParameterSets(lookups) {
788
+ expect(lookups.map((l) => l.indexKey)).toEqual([['y1']]);
789
+ const parameter_sets = await checkpoint.getParameterSets(lookups, 1000);
790
+ expect(parameter_sets).toHaveLength(0);
791
+ return parameter_sets;
792
+ }
793
+ });
794
+ expect(buckets).toHaveLength(0);
795
+ });
796
+ test('can request multiple lookups at once', async () => {
797
+ await using factory = await generateStorageFactory();
798
+ const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(`
799
+ config:
800
+ edition: 3
801
+ streams:
802
+ a:
803
+ auto_subscribe: true
804
+ query: SELECT * FROM a WHERE p IN (SELECT id FROM param_a WHERE u = auth.user_id())
805
+ b:
806
+ auto_subscribe: true
807
+ query: SELECT * FROM b WHERE p IN (SELECT id FROM param_b WHERE u = auth.user_id())
808
+ `));
809
+ const bucketStorage = factory.getInstance(syncRules);
810
+ const parsedSyncRules = syncRules.parsed(test_utils.PARSE_OPTIONS);
811
+ const hydrationState = parsedSyncRules.hydrationState;
812
+ const syncConfig = parsedSyncRules.syncConfigWithErrors.config;
813
+ await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
814
+ const paramATable = await test_utils.resolveTestTable(writer, 'param_a', ['id'], config, 1);
815
+ const paramBTable = await test_utils.resolveTestTable(writer, 'param_b', ['id'], config, 2);
816
+ const replicaId = test_utils.rid('id');
817
+ // Insert the same row into param_a and param_b
818
+ await writer.markAllSnapshotDone('1/1');
819
+ await writer.save({
820
+ sourceTable: paramATable,
821
+ tag: storage.SaveOperationTag.INSERT,
822
+ after: {
823
+ id: 'id',
824
+ u: 'user'
825
+ },
826
+ afterReplicaId: replicaId
827
+ });
828
+ await writer.save({
829
+ sourceTable: paramBTable,
830
+ tag: storage.SaveOperationTag.INSERT,
831
+ after: {
832
+ id: 'id',
833
+ u: 'user'
834
+ },
835
+ afterReplicaId: replicaId
836
+ });
837
+ await writer.commit('1/1');
838
+ function findParameterOnTable(name) {
839
+ for (const source of syncConfig.bucketParameterLookupSources) {
840
+ for (const param of source.getSourceTables()) {
841
+ if (param.name == name)
842
+ return source;
843
+ }
844
+ }
845
+ throw new Error(`Expected parameter index on ${name}`);
846
+ }
847
+ // Run two lookups on separate parameter indexes.
848
+ const lookupA = ScopedParameterLookup.normalized(hydrationState.getParameterIndexLookupScope(findParameterOnTable('param_a')), UnscopedParameterLookup.normalized(['user']));
849
+ const lookupB = ScopedParameterLookup.normalized(hydrationState.getParameterIndexLookupScope(findParameterOnTable('param_b')), UnscopedParameterLookup.normalized(['user']));
850
+ const checkpoint = await bucketStorage.getCheckpoint();
851
+ const parameterSets = await checkpoint.getParameterSets([lookupA, lookupB], 1000);
852
+ const expectedRow = { '0': 'id' };
853
+ let foundLookupA = false, foundLookupB = false;
854
+ // We should get the same row on both, with information on which lookup contributed which row.
855
+ for (const { lookup, rows } of parameterSets) {
856
+ if (lookup === lookupA) {
857
+ foundLookupA = true;
858
+ expect(rows).toStrictEqual([expectedRow]);
859
+ }
860
+ else if (lookup === lookupB) {
861
+ foundLookupB = true;
862
+ expect(rows).toStrictEqual([expectedRow]);
863
+ }
864
+ else {
865
+ throw new Error('unexpected lookup in results');
866
+ }
754
867
  }
868
+ expect(foundLookupA).toBeTruthy();
869
+ expect(foundLookupB).toBeTruthy();
870
+ });
871
+ test('sync streams preserve duplicate downstream lookups with different provenance', async () => {
872
+ await using factory = await generateStorageFactory();
873
+ const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(`
874
+ config:
875
+ edition: 3
876
+ streams:
877
+ stream:
878
+ auto_subscribe: true
879
+ query: |
880
+ SELECT a.*
881
+ FROM a, b, c
882
+ WHERE a.x = b.x
883
+ AND a.z = c.z
884
+ AND b.y = c.y
885
+ AND c.u = auth.user_id()
886
+ `, {
887
+ storageVersion
888
+ }));
889
+ const bucketStorage = factory.getInstance(syncRules);
890
+ const sync_rules = syncRules.parsed(test_utils.PARSE_OPTIONS).hydratedSyncConfig();
891
+ await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
892
+ const tableB = await test_utils.resolveTestTable(writer, 'b', ['id'], config, 1);
893
+ const tableC = await test_utils.resolveTestTable(writer, 'c', ['id'], config, 2);
894
+ await writer.markAllSnapshotDone('1/1');
895
+ await writer.save({
896
+ sourceTable: tableB,
897
+ tag: storage.SaveOperationTag.INSERT,
898
+ after: {
899
+ id: 'b1',
900
+ y: 'shared-y',
901
+ x: 'x-from-shared-y'
902
+ },
903
+ afterReplicaId: test_utils.rid('b1')
904
+ });
905
+ await writer.save({
906
+ sourceTable: tableC,
907
+ tag: storage.SaveOperationTag.INSERT,
908
+ after: {
909
+ id: 'c1',
910
+ u: 'user1',
911
+ y: 'shared-y',
912
+ z: 'z1'
913
+ },
914
+ afterReplicaId: test_utils.rid('c1')
915
+ });
916
+ await writer.save({
917
+ sourceTable: tableC,
918
+ tag: storage.SaveOperationTag.INSERT,
919
+ after: {
920
+ id: 'c2',
921
+ u: 'user1',
922
+ y: 'shared-y',
923
+ z: 'z2'
924
+ },
925
+ afterReplicaId: test_utils.rid('c2')
926
+ });
927
+ await writer.commit('1/1');
928
+ const checkpoint = await bucketStorage.getCheckpoint();
929
+ const parameters = new RequestParameters(new JwtPayload({ sub: 'user1' }), {});
930
+ const querier = sync_rules.getBucketParameterQuerier({
931
+ ...test_utils.querierOptions(parameters)
932
+ }).querier;
933
+ const buckets = await querier.queryDynamicBucketDescriptions({
934
+ async getParameterSets(lookups) {
935
+ return checkpoint.getParameterSets(lookups, 1000);
936
+ }
937
+ });
938
+ expect(buckets.map((bucket) => bucket.bucket).sort()).toStrictEqual([
939
+ expect.stringMatching(/stream.*\["x-from-shared-y","z1"\]$/),
940
+ expect.stringMatching(/stream.*\["x-from-shared-y","z2"\]$/)
941
+ ]);
755
942
  });
756
943
  }
757
944
  //# sourceMappingURL=register-data-storage-parameter-tests.js.map