@powersync/service-core-tests 0.0.0-dev-20260225160713 → 0.0.0-dev-20260511080634
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 +95 -3
- package/dist/test-utils/MetricsHelper.js +1 -1
- package/dist/test-utils/MetricsHelper.js.map +1 -1
- package/dist/test-utils/StorageDataHelpers.d.ts +8 -0
- package/dist/test-utils/StorageDataHelpers.js +33 -0
- package/dist/test-utils/StorageDataHelpers.js.map +1 -0
- package/dist/test-utils/general-utils.d.ts +22 -3
- package/dist/test-utils/general-utils.js +56 -3
- package/dist/test-utils/general-utils.js.map +1 -1
- package/dist/test-utils/stream_utils.js +2 -2
- package/dist/test-utils/stream_utils.js.map +1 -1
- package/dist/test-utils/test-utils-index.d.ts +1 -0
- package/dist/test-utils/test-utils-index.js +1 -0
- package/dist/test-utils/test-utils-index.js.map +1 -1
- package/dist/tests/register-compacting-tests.d.ts +1 -1
- package/dist/tests/register-compacting-tests.js +514 -587
- package/dist/tests/register-compacting-tests.js.map +1 -1
- package/dist/tests/register-data-storage-checkpoint-tests.d.ts +1 -1
- package/dist/tests/register-data-storage-checkpoint-tests.js +200 -301
- package/dist/tests/register-data-storage-checkpoint-tests.js.map +1 -1
- package/dist/tests/register-data-storage-data-tests.d.ts +2 -2
- package/dist/tests/register-data-storage-data-tests.js +1220 -1041
- package/dist/tests/register-data-storage-data-tests.js.map +1 -1
- package/dist/tests/register-data-storage-parameter-tests.d.ts +1 -1
- package/dist/tests/register-data-storage-parameter-tests.js +681 -632
- package/dist/tests/register-data-storage-parameter-tests.js.map +1 -1
- package/dist/tests/register-migration-tests.js +63 -139
- package/dist/tests/register-migration-tests.js.map +1 -1
- package/dist/tests/register-parameter-compacting-tests.d.ts +1 -1
- package/dist/tests/register-parameter-compacting-tests.js +121 -201
- package/dist/tests/register-parameter-compacting-tests.js.map +1 -1
- package/dist/tests/register-sync-tests.d.ts +2 -1
- package/dist/tests/register-sync-tests.js +974 -1136
- package/dist/tests/register-sync-tests.js.map +1 -1
- package/dist/tests/tests-index.d.ts +4 -4
- package/dist/tests/tests-index.js +4 -4
- package/dist/tests/tests-index.js.map +1 -1
- package/dist/tests/util.d.ts +5 -4
- package/dist/tests/util.js +27 -12
- package/dist/tests/util.js.map +1 -1
- package/package.json +6 -6
- package/src/test-utils/MetricsHelper.ts +1 -1
- package/src/test-utils/StorageDataHelpers.ts +44 -0
- package/src/test-utils/general-utils.ts +81 -4
- package/src/test-utils/stream_utils.ts +2 -2
- package/src/test-utils/test-utils-index.ts +1 -0
- package/src/tests/register-compacting-tests.ts +376 -322
- package/src/tests/register-data-storage-checkpoint-tests.ts +85 -53
- package/src/tests/register-data-storage-data-tests.ts +1076 -563
- package/src/tests/register-data-storage-parameter-tests.ts +631 -327
- package/src/tests/register-parameter-compacting-tests.ts +92 -96
- package/src/tests/register-sync-tests.ts +463 -379
- package/src/tests/tests-index.ts +4 -4
- package/src/tests/util.ts +46 -17
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -1,60 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
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
|
-
import { JwtPayload, storage, updateSyncRulesFromYaml } from '@powersync/service-core';
|
|
54
|
-
import { RequestParameters, ScopedParameterLookup } from '@powersync/service-sync-rules';
|
|
1
|
+
import { CURRENT_STORAGE_VERSION, JwtPayload, storage, updateSyncRulesFromYaml } from '@powersync/service-core';
|
|
2
|
+
import { RequestParameters } 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
|
-
import { bucketRequest
|
|
5
|
+
import { bucketRequest } from '../test-utils/test-utils-index.js';
|
|
58
6
|
/**
|
|
59
7
|
* @example
|
|
60
8
|
* ```TypeScript
|
|
@@ -65,130 +13,123 @@ import { bucketRequest, TEST_TABLE } from './util.js';
|
|
|
65
13
|
*
|
|
66
14
|
* ```
|
|
67
15
|
*/
|
|
68
|
-
export function registerDataStorageParameterTests(
|
|
69
|
-
const
|
|
16
|
+
export function registerDataStorageParameterTests(config) {
|
|
17
|
+
const generateStorageFactory = config.factory;
|
|
18
|
+
const storageVersion = config.storageVersion ?? CURRENT_STORAGE_VERSION;
|
|
70
19
|
test('save and load parameters', async () => {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
const factory = __addDisposableResource(env_1, await generateStorageFactory(), true);
|
|
74
|
-
const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(`
|
|
20
|
+
await using factory = await generateStorageFactory();
|
|
21
|
+
const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(`
|
|
75
22
|
bucket_definitions:
|
|
76
23
|
mybucket:
|
|
77
24
|
parameters:
|
|
78
25
|
- SELECT group_id FROM test WHERE id1 = token_parameters.user_id OR id2 = token_parameters.user_id
|
|
79
26
|
data: []
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
}
|
|
27
|
+
`, {
|
|
28
|
+
storageVersion
|
|
29
|
+
}));
|
|
30
|
+
const bucketStorage = factory.getInstance(syncRules);
|
|
31
|
+
const sync_rules = syncRules.parsed(test_utils.PARSE_OPTIONS).hydratedSyncRules();
|
|
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([{ group_id: 'group1a' }]);
|
|
66
|
+
return parameter_sets;
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
expect(buckets.map((b) => b.bucket)).toEqual([bucketRequest(syncRules, 'mybucket["group1a"]').bucket]);
|
|
124
70
|
});
|
|
125
71
|
test('it should use the latest version', async () => {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
const factory = __addDisposableResource(env_2, await generateStorageFactory(), true);
|
|
129
|
-
const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(`
|
|
72
|
+
await using factory = await generateStorageFactory();
|
|
73
|
+
const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(`
|
|
130
74
|
bucket_definitions:
|
|
131
75
|
mybucket:
|
|
132
76
|
parameters:
|
|
133
77
|
- SELECT group_id FROM test WHERE id = token_parameters.user_id
|
|
134
78
|
data: []
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
}
|
|
79
|
+
`, {
|
|
80
|
+
storageVersion
|
|
81
|
+
}));
|
|
82
|
+
const bucketStorage = factory.getInstance(syncRules);
|
|
83
|
+
const sync_rules = syncRules.parsed(test_utils.PARSE_OPTIONS).hydratedSyncRules();
|
|
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([{ 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([{ group_id: 'group2' }]);
|
|
125
|
+
return parameter_sets;
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
expect(buckets2.map((b) => b.bucket)).toEqual([bucketRequest(syncRules, 'mybucket["group2"]').bucket]);
|
|
186
129
|
});
|
|
187
130
|
test('it should use the latest version after updates', async () => {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
const factory = __addDisposableResource(env_3, await generateStorageFactory(), true);
|
|
191
|
-
const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(`
|
|
131
|
+
await using factory = await generateStorageFactory();
|
|
132
|
+
const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(`
|
|
192
133
|
bucket_definitions:
|
|
193
134
|
mybucket:
|
|
194
135
|
parameters:
|
|
@@ -196,326 +137,293 @@ bucket_definitions:
|
|
|
196
137
|
FROM todos
|
|
197
138
|
WHERE list_id IN token_parameters.list_id
|
|
198
139
|
data: []
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
todo_id: 'todo1'
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
}
|
|
258
|
-
finally {
|
|
259
|
-
const result_3 = __disposeResources(env_3);
|
|
260
|
-
if (result_3)
|
|
261
|
-
await result_3;
|
|
262
|
-
}
|
|
140
|
+
`, { storageVersion }));
|
|
141
|
+
const bucketStorage = factory.getInstance(syncRules);
|
|
142
|
+
const sync_rules = syncRules.parsed(test_utils.PARSE_OPTIONS).hydratedSyncRules();
|
|
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 parameter_sets = await checkpoint.getParameterSets(lookups, 1000);
|
|
187
|
+
expect(parameter_sets.sort((a, b) => a.todo_id.localeCompare(b.todo_id))).toEqual([
|
|
188
|
+
{ todo_id: 'todo1' },
|
|
189
|
+
{ todo_id: 'todo2' }
|
|
190
|
+
]);
|
|
191
|
+
return parameter_sets;
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
expect(buckets.map((b) => b.bucket).sort()).toEqual([
|
|
195
|
+
bucketRequest(syncRules, 'mybucket["todo1"]').bucket,
|
|
196
|
+
bucketRequest(syncRules, 'mybucket["todo2"]').bucket
|
|
197
|
+
]);
|
|
263
198
|
});
|
|
264
199
|
test('save and load parameters with different number types', async () => {
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
const factory = __addDisposableResource(env_4, await generateStorageFactory(), true);
|
|
268
|
-
const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(`
|
|
200
|
+
await using factory = await generateStorageFactory();
|
|
201
|
+
const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(`
|
|
269
202
|
bucket_definitions:
|
|
270
203
|
mybucket:
|
|
271
204
|
parameters:
|
|
272
205
|
- SELECT group_id FROM test WHERE n1 = token_parameters.n1 and f2 = token_parameters.f2 and f3 = token_parameters.f3
|
|
273
206
|
data: []
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
207
|
+
`, {
|
|
208
|
+
storageVersion
|
|
209
|
+
}));
|
|
210
|
+
const bucketStorage = factory.getInstance(syncRules);
|
|
211
|
+
const sync_rules = syncRules.parsed(test_utils.PARSE_OPTIONS).hydratedSyncRules();
|
|
212
|
+
await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
|
|
213
|
+
const testTable = await test_utils.resolveTestTable(writer, 'test', ['id'], config);
|
|
214
|
+
await writer.markAllSnapshotDone('1/1');
|
|
215
|
+
await writer.save({
|
|
216
|
+
sourceTable: testTable,
|
|
217
|
+
tag: storage.SaveOperationTag.INSERT,
|
|
218
|
+
after: {
|
|
219
|
+
id: 't1',
|
|
220
|
+
group_id: 'group1',
|
|
221
|
+
n1: 314n,
|
|
222
|
+
f2: 314,
|
|
223
|
+
f3: 3.14
|
|
224
|
+
},
|
|
225
|
+
afterReplicaId: test_utils.rid('t1')
|
|
226
|
+
});
|
|
227
|
+
await writer.commit('1/1');
|
|
228
|
+
const checkpoint = await bucketStorage.getCheckpoint();
|
|
229
|
+
const testQuery = async (jwtParameters, expectedParameterSets) => {
|
|
230
|
+
const parameters = new RequestParameters(new JwtPayload({ sub: 'u1', parameters: jwtParameters }), {});
|
|
231
|
+
const querier = sync_rules.getBucketParameterQuerier(test_utils.querierOptions(parameters)).querier;
|
|
232
|
+
return await querier.queryDynamicBucketDescriptions({
|
|
233
|
+
async getParameterSets(lookups) {
|
|
234
|
+
const parameter_sets = await checkpoint.getParameterSets(lookups, 1000);
|
|
235
|
+
expect(parameter_sets).toEqual(expectedParameterSets);
|
|
236
|
+
return parameter_sets;
|
|
237
|
+
}
|
|
290
238
|
});
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
]);
|
|
300
|
-
expect(parameters2).toEqual([TEST_PARAMS]);
|
|
301
|
-
const parameters3 = await checkpoint.getParameterSets([ScopedParameterLookup.direct(MYBUCKET_1, [314n, 314, 3])]);
|
|
302
|
-
expect(parameters3).toEqual([]);
|
|
303
|
-
}
|
|
304
|
-
catch (e_4) {
|
|
305
|
-
env_4.error = e_4;
|
|
306
|
-
env_4.hasError = true;
|
|
307
|
-
}
|
|
308
|
-
finally {
|
|
309
|
-
const result_4 = __disposeResources(env_4);
|
|
310
|
-
if (result_4)
|
|
311
|
-
await result_4;
|
|
312
|
-
}
|
|
239
|
+
};
|
|
240
|
+
expect(await testQuery({ n1: 314n, f2: 314, f3: 3.14 }, [{ group_id: 'group1' }])).toMatchObject([
|
|
241
|
+
{ bucket: bucketRequest(syncRules, 'mybucket["group1"]').bucket }
|
|
242
|
+
]);
|
|
243
|
+
expect(await testQuery({ n1: 314, f2: 314n, f3: 3.14 }, [{ group_id: 'group1' }])).toMatchObject([
|
|
244
|
+
{ bucket: bucketRequest(syncRules, 'mybucket["group1"]').bucket }
|
|
245
|
+
]);
|
|
246
|
+
expect(await testQuery({ n1: 314n, f2: 314, f3: 3 }, [])).toEqual([]);
|
|
313
247
|
});
|
|
314
248
|
test('save and load parameters with large numbers', async () => {
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
const factory = __addDisposableResource(env_5, await generateStorageFactory(), true);
|
|
321
|
-
const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(`
|
|
249
|
+
// This ensures serialization / deserialization of "current_data" is done correctly.
|
|
250
|
+
// This specific case tested here cannot happen with postgres in practice, but we still
|
|
251
|
+
// test this to ensure correct deserialization.
|
|
252
|
+
await using factory = await generateStorageFactory();
|
|
253
|
+
const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(`
|
|
322
254
|
bucket_definitions:
|
|
323
255
|
mybucket:
|
|
324
256
|
parameters:
|
|
325
257
|
- SELECT group_id FROM test WHERE n1 = token_parameters.n1
|
|
326
258
|
data: []
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
}
|
|
259
|
+
`, {
|
|
260
|
+
storageVersion
|
|
261
|
+
}));
|
|
262
|
+
const bucketStorage = factory.getInstance(syncRules);
|
|
263
|
+
const sync_rules = syncRules.parsed(test_utils.PARSE_OPTIONS).hydratedSyncRules();
|
|
264
|
+
await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
|
|
265
|
+
const testTable = await test_utils.resolveTestTable(writer, 'test', ['id'], config);
|
|
266
|
+
await writer.markAllSnapshotDone('1/1');
|
|
267
|
+
await writer.save({
|
|
268
|
+
sourceTable: testTable,
|
|
269
|
+
tag: storage.SaveOperationTag.INSERT,
|
|
270
|
+
after: {
|
|
271
|
+
id: 't1',
|
|
272
|
+
group_id: 'group1',
|
|
273
|
+
n1: 1152921504606846976n // 2^60
|
|
274
|
+
},
|
|
275
|
+
afterReplicaId: test_utils.rid('t1')
|
|
276
|
+
});
|
|
277
|
+
await writer.save({
|
|
278
|
+
sourceTable: testTable,
|
|
279
|
+
tag: storage.SaveOperationTag.UPDATE,
|
|
280
|
+
after: {
|
|
281
|
+
id: 't1',
|
|
282
|
+
group_id: 'group1',
|
|
283
|
+
// Simulate a TOAST value, even though it can't happen for values like this
|
|
284
|
+
// in practice.
|
|
285
|
+
n1: undefined
|
|
286
|
+
},
|
|
287
|
+
afterReplicaId: test_utils.rid('t1')
|
|
288
|
+
});
|
|
289
|
+
await writer.commit('1/1');
|
|
290
|
+
const checkpoint = await bucketStorage.getCheckpoint();
|
|
291
|
+
const n1 = 1152921504606846976n;
|
|
292
|
+
const parameters = new RequestParameters(new JwtPayload({ sub: 'u1', parameters: { n1 } }), {});
|
|
293
|
+
const querier = sync_rules.getBucketParameterQuerier(test_utils.querierOptions(parameters)).querier;
|
|
294
|
+
const buckets = await querier.queryDynamicBucketDescriptions({
|
|
295
|
+
getParameterSets: async (lookups) => {
|
|
296
|
+
expect(lookups.map((l) => l.indexKey)).toEqual([[n1]]);
|
|
297
|
+
const parameter_sets = await checkpoint.getParameterSets(lookups, 1000);
|
|
298
|
+
expect(parameter_sets).toEqual([{ group_id: 'group1' }]);
|
|
299
|
+
return parameter_sets;
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
expect(buckets.map((b) => b.bucket)).toEqual([bucketRequest(syncRules, 'mybucket["group1"]').bucket]);
|
|
370
303
|
});
|
|
371
304
|
test('save and load parameters with workspaceId', async () => {
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
const WORKSPACE_TABLE = test_utils.makeTestTable('workspace', ['id']);
|
|
375
|
-
const factory = __addDisposableResource(env_6, await generateStorageFactory(), true);
|
|
376
|
-
const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(`
|
|
305
|
+
await using factory = await generateStorageFactory();
|
|
306
|
+
const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(`
|
|
377
307
|
bucket_definitions:
|
|
378
308
|
by_workspace:
|
|
379
309
|
parameters:
|
|
380
310
|
- SELECT id as workspace_id FROM workspace WHERE
|
|
381
311
|
workspace."userId" = token_parameters.user_id
|
|
382
312
|
data: []
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
env_6.hasError = true;
|
|
421
|
-
}
|
|
422
|
-
finally {
|
|
423
|
-
const result_6 = __disposeResources(env_6);
|
|
424
|
-
if (result_6)
|
|
425
|
-
await result_6;
|
|
426
|
-
}
|
|
313
|
+
`, {
|
|
314
|
+
storageVersion
|
|
315
|
+
}));
|
|
316
|
+
const sync_rules = syncRules.parsed(test_utils.PARSE_OPTIONS).hydratedSyncRules();
|
|
317
|
+
const bucketStorage = factory.getInstance(syncRules);
|
|
318
|
+
await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
|
|
319
|
+
const workspaceTable = await test_utils.resolveTestTable(writer, 'workspace', ['id'], config);
|
|
320
|
+
await writer.markAllSnapshotDone('1/1');
|
|
321
|
+
await writer.save({
|
|
322
|
+
sourceTable: workspaceTable,
|
|
323
|
+
tag: storage.SaveOperationTag.INSERT,
|
|
324
|
+
after: {
|
|
325
|
+
id: 'workspace1',
|
|
326
|
+
userId: 'u1'
|
|
327
|
+
},
|
|
328
|
+
afterReplicaId: test_utils.rid('workspace1')
|
|
329
|
+
});
|
|
330
|
+
await writer.commit('1/1');
|
|
331
|
+
const checkpoint = await bucketStorage.getCheckpoint();
|
|
332
|
+
const parameters = new RequestParameters(new JwtPayload({ sub: 'u1' }), {});
|
|
333
|
+
const querier = sync_rules.getBucketParameterQuerier(test_utils.querierOptions(parameters)).querier;
|
|
334
|
+
const buckets = await querier.queryDynamicBucketDescriptions({
|
|
335
|
+
async getParameterSets(lookups) {
|
|
336
|
+
expect(lookups.map((l) => l.indexKey)).toEqual([['u1']]);
|
|
337
|
+
const parameter_sets = await checkpoint.getParameterSets(lookups, 1000);
|
|
338
|
+
expect(parameter_sets).toEqual([{ workspace_id: 'workspace1' }]);
|
|
339
|
+
return parameter_sets;
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
expect(buckets).toEqual([
|
|
343
|
+
{
|
|
344
|
+
bucket: bucketRequest(syncRules, 'by_workspace["workspace1"]').bucket,
|
|
345
|
+
priority: 3,
|
|
346
|
+
definition: 'by_workspace',
|
|
347
|
+
inclusion_reasons: ['default']
|
|
348
|
+
}
|
|
349
|
+
]);
|
|
427
350
|
});
|
|
428
351
|
test('save and load parameters with dynamic global buckets', async () => {
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
const WORKSPACE_TABLE = test_utils.makeTestTable('workspace');
|
|
432
|
-
const factory = __addDisposableResource(env_7, await generateStorageFactory(), true);
|
|
433
|
-
const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(`
|
|
352
|
+
await using factory = await generateStorageFactory();
|
|
353
|
+
const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(`
|
|
434
354
|
bucket_definitions:
|
|
435
355
|
by_public_workspace:
|
|
436
356
|
parameters:
|
|
437
357
|
- SELECT id as workspace_id FROM workspace WHERE
|
|
438
358
|
workspace.visibility = 'public'
|
|
439
359
|
data: []
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
catch (e_7) {
|
|
504
|
-
env_7.error = e_7;
|
|
505
|
-
env_7.hasError = true;
|
|
506
|
-
}
|
|
507
|
-
finally {
|
|
508
|
-
const result_7 = __disposeResources(env_7);
|
|
509
|
-
if (result_7)
|
|
510
|
-
await result_7;
|
|
511
|
-
}
|
|
360
|
+
`, {
|
|
361
|
+
storageVersion
|
|
362
|
+
}));
|
|
363
|
+
const sync_rules = syncRules.parsed(test_utils.PARSE_OPTIONS).hydratedSyncRules();
|
|
364
|
+
const bucketStorage = factory.getInstance(syncRules);
|
|
365
|
+
await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
|
|
366
|
+
const workspaceTable = await test_utils.resolveTestTable(writer, 'workspace', undefined, config);
|
|
367
|
+
await writer.markAllSnapshotDone('1/1');
|
|
368
|
+
await writer.save({
|
|
369
|
+
sourceTable: workspaceTable,
|
|
370
|
+
tag: storage.SaveOperationTag.INSERT,
|
|
371
|
+
after: {
|
|
372
|
+
id: 'workspace1',
|
|
373
|
+
visibility: 'public'
|
|
374
|
+
},
|
|
375
|
+
afterReplicaId: test_utils.rid('workspace1')
|
|
376
|
+
});
|
|
377
|
+
await writer.save({
|
|
378
|
+
sourceTable: workspaceTable,
|
|
379
|
+
tag: storage.SaveOperationTag.INSERT,
|
|
380
|
+
after: {
|
|
381
|
+
id: 'workspace2',
|
|
382
|
+
visibility: 'private'
|
|
383
|
+
},
|
|
384
|
+
afterReplicaId: test_utils.rid('workspace2')
|
|
385
|
+
});
|
|
386
|
+
await writer.save({
|
|
387
|
+
sourceTable: workspaceTable,
|
|
388
|
+
tag: storage.SaveOperationTag.INSERT,
|
|
389
|
+
after: {
|
|
390
|
+
id: 'workspace3',
|
|
391
|
+
visibility: 'public'
|
|
392
|
+
},
|
|
393
|
+
afterReplicaId: test_utils.rid('workspace3')
|
|
394
|
+
});
|
|
395
|
+
await writer.commit('1/1');
|
|
396
|
+
const checkpoint = await bucketStorage.getCheckpoint();
|
|
397
|
+
const parameters = new RequestParameters(new JwtPayload({ sub: 'unknown' }), {});
|
|
398
|
+
const querier = sync_rules.getBucketParameterQuerier(test_utils.querierOptions(parameters)).querier;
|
|
399
|
+
const buckets = await querier.queryDynamicBucketDescriptions({
|
|
400
|
+
async getParameterSets(lookups) {
|
|
401
|
+
expect(lookups.map((l) => l.indexKey)).toEqual([[]]);
|
|
402
|
+
const parameter_sets = await checkpoint.getParameterSets(lookups, 1000);
|
|
403
|
+
parameter_sets.sort((a, b) => JSON.stringify(a).localeCompare(JSON.stringify(b)));
|
|
404
|
+
expect(parameter_sets).toEqual([{ workspace_id: 'workspace1' }, { workspace_id: 'workspace3' }]);
|
|
405
|
+
return parameter_sets;
|
|
406
|
+
}
|
|
407
|
+
});
|
|
408
|
+
buckets.sort((a, b) => a.bucket.localeCompare(b.bucket));
|
|
409
|
+
expect(buckets).toEqual([
|
|
410
|
+
{
|
|
411
|
+
bucket: bucketRequest(syncRules, 'by_public_workspace["workspace1"]').bucket,
|
|
412
|
+
priority: 3,
|
|
413
|
+
definition: 'by_public_workspace',
|
|
414
|
+
inclusion_reasons: ['default']
|
|
415
|
+
},
|
|
416
|
+
{
|
|
417
|
+
bucket: bucketRequest(syncRules, 'by_public_workspace["workspace3"]').bucket,
|
|
418
|
+
priority: 3,
|
|
419
|
+
definition: 'by_public_workspace',
|
|
420
|
+
inclusion_reasons: ['default']
|
|
421
|
+
}
|
|
422
|
+
]);
|
|
512
423
|
});
|
|
513
424
|
test('multiple parameter queries', async () => {
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
const WORKSPACE_TABLE = test_utils.makeTestTable('workspace');
|
|
517
|
-
const factory = __addDisposableResource(env_8, await generateStorageFactory(), true);
|
|
518
|
-
const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(`
|
|
425
|
+
await using factory = await generateStorageFactory();
|
|
426
|
+
const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(`
|
|
519
427
|
bucket_definitions:
|
|
520
428
|
by_workspace:
|
|
521
429
|
parameters:
|
|
@@ -524,171 +432,152 @@ bucket_definitions:
|
|
|
524
432
|
- SELECT id as workspace_id FROM workspace WHERE
|
|
525
433
|
workspace.user_id = token_parameters.user_id
|
|
526
434
|
data: []
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
])
|
|
597
|
-
|
|
598
|
-
catch (e_8) {
|
|
599
|
-
env_8.error = e_8;
|
|
600
|
-
env_8.hasError = true;
|
|
601
|
-
}
|
|
602
|
-
finally {
|
|
603
|
-
const result_8 = __disposeResources(env_8);
|
|
604
|
-
if (result_8)
|
|
605
|
-
await result_8;
|
|
606
|
-
}
|
|
435
|
+
`, {
|
|
436
|
+
storageVersion
|
|
437
|
+
}));
|
|
438
|
+
const sync_rules = syncRules.parsed(test_utils.PARSE_OPTIONS).hydratedSyncRules();
|
|
439
|
+
const bucketStorage = factory.getInstance(syncRules);
|
|
440
|
+
await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
|
|
441
|
+
const workspaceTable = await test_utils.resolveTestTable(writer, 'workspace', undefined, config);
|
|
442
|
+
await writer.markAllSnapshotDone('1/1');
|
|
443
|
+
await writer.save({
|
|
444
|
+
sourceTable: workspaceTable,
|
|
445
|
+
tag: storage.SaveOperationTag.INSERT,
|
|
446
|
+
after: {
|
|
447
|
+
id: 'workspace1',
|
|
448
|
+
visibility: 'public'
|
|
449
|
+
},
|
|
450
|
+
afterReplicaId: test_utils.rid('workspace1')
|
|
451
|
+
});
|
|
452
|
+
await writer.save({
|
|
453
|
+
sourceTable: workspaceTable,
|
|
454
|
+
tag: storage.SaveOperationTag.INSERT,
|
|
455
|
+
after: {
|
|
456
|
+
id: 'workspace2',
|
|
457
|
+
visibility: 'private'
|
|
458
|
+
},
|
|
459
|
+
afterReplicaId: test_utils.rid('workspace2')
|
|
460
|
+
});
|
|
461
|
+
await writer.save({
|
|
462
|
+
sourceTable: workspaceTable,
|
|
463
|
+
tag: storage.SaveOperationTag.INSERT,
|
|
464
|
+
after: {
|
|
465
|
+
id: 'workspace3',
|
|
466
|
+
user_id: 'u1',
|
|
467
|
+
visibility: 'private'
|
|
468
|
+
},
|
|
469
|
+
afterReplicaId: test_utils.rid('workspace3')
|
|
470
|
+
});
|
|
471
|
+
await writer.save({
|
|
472
|
+
sourceTable: workspaceTable,
|
|
473
|
+
tag: storage.SaveOperationTag.INSERT,
|
|
474
|
+
after: {
|
|
475
|
+
id: 'workspace4',
|
|
476
|
+
user_id: 'u2',
|
|
477
|
+
visibility: 'private'
|
|
478
|
+
},
|
|
479
|
+
afterReplicaId: test_utils.rid('workspace4')
|
|
480
|
+
});
|
|
481
|
+
await writer.commit('1/1');
|
|
482
|
+
const checkpoint = await bucketStorage.getCheckpoint();
|
|
483
|
+
const parameters = new RequestParameters(new JwtPayload({ sub: 'u1' }), {});
|
|
484
|
+
// Test intermediate values - could be moved to sync_rules.test.ts
|
|
485
|
+
const querier = sync_rules.getBucketParameterQuerier(test_utils.querierOptions(parameters)).querier;
|
|
486
|
+
// Test final values - the important part
|
|
487
|
+
const foundLookups = [];
|
|
488
|
+
const parameter_sets = [];
|
|
489
|
+
const buckets = (await querier.queryDynamicBucketDescriptions({
|
|
490
|
+
async getParameterSets(lookups) {
|
|
491
|
+
foundLookups.push(...lookups);
|
|
492
|
+
const output = await checkpoint.getParameterSets(lookups, 1000);
|
|
493
|
+
parameter_sets.push(...output);
|
|
494
|
+
return output;
|
|
495
|
+
}
|
|
496
|
+
})).map((e) => e.bucket);
|
|
497
|
+
// Not testing the scope anymore - the exact format depends on storage version
|
|
498
|
+
expect(foundLookups.map((l) => l.indexKey)).toEqual([[], ['u1']]);
|
|
499
|
+
parameter_sets.sort((a, b) => JSON.stringify(a).localeCompare(JSON.stringify(b)));
|
|
500
|
+
expect(parameter_sets).toEqual([{ workspace_id: 'workspace1' }, { workspace_id: 'workspace3' }]);
|
|
501
|
+
buckets.sort();
|
|
502
|
+
expect(buckets).toEqual([
|
|
503
|
+
bucketRequest(syncRules, 'by_workspace["workspace1"]').bucket,
|
|
504
|
+
bucketRequest(syncRules, 'by_workspace["workspace3"]').bucket
|
|
505
|
+
]);
|
|
607
506
|
});
|
|
608
507
|
test('truncate parameters', async () => {
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
const factory = __addDisposableResource(env_9, await generateStorageFactory(), true);
|
|
612
|
-
const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(`
|
|
508
|
+
await using factory = await generateStorageFactory();
|
|
509
|
+
const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(`
|
|
613
510
|
bucket_definitions:
|
|
614
511
|
mybucket:
|
|
615
512
|
parameters:
|
|
616
513
|
- SELECT group_id FROM test WHERE id1 = token_parameters.user_id OR id2 = token_parameters.user_id
|
|
617
514
|
data: []
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
await
|
|
646
|
-
|
|
515
|
+
`, {
|
|
516
|
+
storageVersion
|
|
517
|
+
}));
|
|
518
|
+
const bucketStorage = factory.getInstance(syncRules);
|
|
519
|
+
const sync_rules = syncRules.parsed(test_utils.PARSE_OPTIONS).hydratedSyncRules();
|
|
520
|
+
await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
|
|
521
|
+
const testTable = await test_utils.resolveTestTable(writer, 'test', ['id'], config);
|
|
522
|
+
await writer.markAllSnapshotDone('1/1');
|
|
523
|
+
await writer.save({
|
|
524
|
+
sourceTable: testTable,
|
|
525
|
+
tag: storage.SaveOperationTag.INSERT,
|
|
526
|
+
after: {
|
|
527
|
+
id: 't2',
|
|
528
|
+
id1: 'user3',
|
|
529
|
+
id2: 'user4',
|
|
530
|
+
group_id: 'group2a'
|
|
531
|
+
},
|
|
532
|
+
afterReplicaId: test_utils.rid('t2')
|
|
533
|
+
});
|
|
534
|
+
await writer.truncate([testTable]);
|
|
535
|
+
await writer.flush();
|
|
536
|
+
const checkpoint = await bucketStorage.getCheckpoint();
|
|
537
|
+
const parameters = new RequestParameters(new JwtPayload({ sub: 'user1' }), {});
|
|
538
|
+
const querier = sync_rules.getBucketParameterQuerier(test_utils.querierOptions(parameters)).querier;
|
|
539
|
+
const buckets = await querier.queryDynamicBucketDescriptions({
|
|
540
|
+
async getParameterSets(lookups) {
|
|
541
|
+
expect(lookups.map((l) => l.indexKey)).toEqual([['user1']]);
|
|
542
|
+
const parameter_sets = await checkpoint.getParameterSets(lookups, 1000);
|
|
543
|
+
expect(parameter_sets).toEqual([]);
|
|
544
|
+
return parameter_sets;
|
|
545
|
+
}
|
|
546
|
+
});
|
|
547
|
+
expect(buckets).toEqual([]);
|
|
647
548
|
});
|
|
648
549
|
test('invalidate cached parsed sync rules', async () => {
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
const bucketStorageFactory = __addDisposableResource(env_10, await generateStorageFactory(), true);
|
|
652
|
-
const syncRules = await bucketStorageFactory.updateSyncRules(updateSyncRulesFromYaml(`
|
|
550
|
+
await using bucketStorageFactory = await generateStorageFactory();
|
|
551
|
+
const syncRules = await bucketStorageFactory.updateSyncRules(updateSyncRulesFromYaml(`
|
|
653
552
|
bucket_definitions:
|
|
654
553
|
by_workspace:
|
|
655
554
|
parameters:
|
|
656
555
|
- SELECT id as workspace_id FROM workspace WHERE
|
|
657
556
|
workspace."userId" = token_parameters.user_id
|
|
658
557
|
data: []
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
env_10.error = e_10;
|
|
679
|
-
env_10.hasError = true;
|
|
680
|
-
}
|
|
681
|
-
finally {
|
|
682
|
-
const result_10 = __disposeResources(env_10);
|
|
683
|
-
if (result_10)
|
|
684
|
-
await result_10;
|
|
685
|
-
}
|
|
558
|
+
`, {
|
|
559
|
+
storageVersion
|
|
560
|
+
}));
|
|
561
|
+
const syncBucketStorage = bucketStorageFactory.getInstance(syncRules);
|
|
562
|
+
const parsedSchema1 = syncBucketStorage.getParsedSyncRules({
|
|
563
|
+
defaultSchema: 'public'
|
|
564
|
+
});
|
|
565
|
+
const parsedSchema2 = syncBucketStorage.getParsedSyncRules({
|
|
566
|
+
defaultSchema: 'public'
|
|
567
|
+
});
|
|
568
|
+
// These should be cached, this will be the same instance
|
|
569
|
+
expect(parsedSchema2).equals(parsedSchema1);
|
|
570
|
+
expect(parsedSchema1.getSourceTables()[0].schema).equals('public');
|
|
571
|
+
const parsedSchema3 = syncBucketStorage.getParsedSyncRules({
|
|
572
|
+
defaultSchema: 'databasename'
|
|
573
|
+
});
|
|
574
|
+
// The cache should not be used
|
|
575
|
+
expect(parsedSchema3).not.equals(parsedSchema2);
|
|
576
|
+
expect(parsedSchema3.getSourceTables()[0].schema).equals('databasename');
|
|
686
577
|
});
|
|
687
578
|
test('sync streams smoke test', async () => {
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
const factory = __addDisposableResource(env_11, await generateStorageFactory(), true);
|
|
691
|
-
const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(`
|
|
579
|
+
await using factory = await generateStorageFactory();
|
|
580
|
+
const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(`
|
|
692
581
|
config:
|
|
693
582
|
edition: 3
|
|
694
583
|
|
|
@@ -698,41 +587,201 @@ streams:
|
|
|
698
587
|
SELECT data.* FROM test AS data, test AS param
|
|
699
588
|
WHERE data.foo = param.bar AND param.baz = auth.user_id()
|
|
700
589
|
`));
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
590
|
+
const bucketStorage = factory.getInstance(syncRules);
|
|
591
|
+
const sync_rules = syncRules.parsed(test_utils.PARSE_OPTIONS).hydratedSyncRules();
|
|
592
|
+
await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
|
|
593
|
+
const testTable = await test_utils.resolveTestTable(writer, 'test', ['id'], config);
|
|
594
|
+
await writer.markAllSnapshotDone('1/1');
|
|
595
|
+
await writer.save({
|
|
596
|
+
sourceTable: testTable,
|
|
597
|
+
tag: storage.SaveOperationTag.INSERT,
|
|
598
|
+
after: {
|
|
599
|
+
baz: 'baz',
|
|
600
|
+
bar: 'bar'
|
|
601
|
+
},
|
|
602
|
+
afterReplicaId: test_utils.rid('t1')
|
|
603
|
+
});
|
|
604
|
+
await writer.commit('1/1');
|
|
605
|
+
const checkpoint = await bucketStorage.getCheckpoint();
|
|
606
|
+
const parameters = new RequestParameters(new JwtPayload({ sub: 'baz' }), {});
|
|
607
|
+
const querier = sync_rules.getBucketParameterQuerier({
|
|
608
|
+
...test_utils.querierOptions(parameters),
|
|
609
|
+
streams: {
|
|
610
|
+
stream: [
|
|
611
|
+
{
|
|
612
|
+
priorityOverride: null,
|
|
613
|
+
parameters: null,
|
|
614
|
+
opaque_id: 123
|
|
615
|
+
}
|
|
616
|
+
]
|
|
617
|
+
}
|
|
618
|
+
}).querier;
|
|
619
|
+
const buckets = await querier.queryDynamicBucketDescriptions({
|
|
620
|
+
async getParameterSets(lookups) {
|
|
621
|
+
expect(lookups.map((l) => l.indexKey)).toEqual([['baz']]);
|
|
622
|
+
const parameter_sets = await checkpoint.getParameterSets(lookups, 1000);
|
|
623
|
+
expect(parameter_sets).toEqual([{ '0': 'bar' }]);
|
|
624
|
+
return parameter_sets;
|
|
625
|
+
}
|
|
626
|
+
});
|
|
627
|
+
console.log('whatabuckets', buckets);
|
|
628
|
+
expect(buckets).toHaveLength(1);
|
|
629
|
+
expect(buckets).toMatchObject([
|
|
630
|
+
{
|
|
631
|
+
bucket: expect.stringMatching(/stream.*\["bar"\]$/),
|
|
632
|
+
definition: 'stream',
|
|
633
|
+
inclusion_reasons: [{ subscription: 123 }],
|
|
634
|
+
priority: 3
|
|
635
|
+
}
|
|
636
|
+
]);
|
|
637
|
+
});
|
|
638
|
+
test('respects parameter limit', async () => {
|
|
639
|
+
await using factory = await generateStorageFactory();
|
|
640
|
+
const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(`
|
|
641
|
+
config:
|
|
642
|
+
edition: 3
|
|
643
|
+
|
|
644
|
+
streams:
|
|
645
|
+
a:
|
|
646
|
+
auto_subscribe: true
|
|
647
|
+
query: SELECT * FROM a WHERE id IN (SELECT id FROM b)
|
|
648
|
+
`, {
|
|
649
|
+
storageVersion
|
|
650
|
+
}));
|
|
651
|
+
const bucketStorage = factory.getInstance(syncRules);
|
|
652
|
+
const sync_rules = syncRules.parsed(test_utils.PARSE_OPTIONS).hydratedSyncRules();
|
|
653
|
+
await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
|
|
654
|
+
const testTable = await test_utils.resolveTestTable(writer, 'b', ['id'], config);
|
|
655
|
+
await writer.markAllSnapshotDone('1/1');
|
|
656
|
+
for (let i = 0; i < 10; i++) {
|
|
657
|
+
await writer.save({
|
|
658
|
+
sourceTable: testTable,
|
|
659
|
+
tag: storage.SaveOperationTag.INSERT,
|
|
660
|
+
after: {
|
|
661
|
+
id: `t${i}`
|
|
662
|
+
},
|
|
663
|
+
afterReplicaId: test_utils.rid(`t${i}`)
|
|
713
664
|
});
|
|
714
|
-
const checkpoint = await bucketStorage.getCheckpoint();
|
|
715
|
-
const parameters = await checkpoint.getParameterSets([
|
|
716
|
-
ScopedParameterLookup.direct({
|
|
717
|
-
lookupName: 'lookup',
|
|
718
|
-
queryId: '0'
|
|
719
|
-
}, ['baz'])
|
|
720
|
-
]);
|
|
721
|
-
expect(parameters).toEqual([
|
|
722
|
-
{
|
|
723
|
-
'0': 'bar'
|
|
724
|
-
}
|
|
725
|
-
]);
|
|
726
|
-
}
|
|
727
|
-
catch (e_11) {
|
|
728
|
-
env_11.error = e_11;
|
|
729
|
-
env_11.hasError = true;
|
|
730
|
-
}
|
|
731
|
-
finally {
|
|
732
|
-
const result_11 = __disposeResources(env_11);
|
|
733
|
-
if (result_11)
|
|
734
|
-
await result_11;
|
|
735
665
|
}
|
|
666
|
+
await writer.commit('1/1');
|
|
667
|
+
const checkpoint = await bucketStorage.getCheckpoint();
|
|
668
|
+
const parameters = new RequestParameters(new JwtPayload({ sub: 'u' }), {});
|
|
669
|
+
const querier = sync_rules.getBucketParameterQuerier(test_utils.querierOptions(parameters)).querier;
|
|
670
|
+
await expect(querier.queryDynamicBucketDescriptions({
|
|
671
|
+
async getParameterSets(lookups) {
|
|
672
|
+
const parameter_sets = await checkpoint.getParameterSets(lookups, 5);
|
|
673
|
+
return parameter_sets;
|
|
674
|
+
}
|
|
675
|
+
})).rejects.toThrow('Too many parameter results (limit was 5)');
|
|
676
|
+
});
|
|
677
|
+
test('sync streams store multiple parameter outputs for a single source row and lookup', async () => {
|
|
678
|
+
await using factory = await generateStorageFactory();
|
|
679
|
+
const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(`
|
|
680
|
+
config:
|
|
681
|
+
edition: 3
|
|
682
|
+
streams:
|
|
683
|
+
chat:
|
|
684
|
+
auto_subscribe: true
|
|
685
|
+
query: |
|
|
686
|
+
SELECT a.*
|
|
687
|
+
FROM a, b, json_each(b.x) x, json_each(b.y) y
|
|
688
|
+
WHERE a.x = x.value AND y.value = auth.user_id()
|
|
689
|
+
`));
|
|
690
|
+
const bucketStorage = factory.getInstance(syncRules);
|
|
691
|
+
const sync_rules = syncRules.parsed(test_utils.PARSE_OPTIONS).hydratedSyncRules();
|
|
692
|
+
await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
|
|
693
|
+
const tableB = await test_utils.resolveTestTable(writer, 'b', ['id'], config);
|
|
694
|
+
const firstState = {
|
|
695
|
+
id: 'id0',
|
|
696
|
+
x: JSON.stringify(['x1', 'x2']),
|
|
697
|
+
y: JSON.stringify(['y1', 'y2'])
|
|
698
|
+
};
|
|
699
|
+
const secondState = {
|
|
700
|
+
id: 'id0',
|
|
701
|
+
x: JSON.stringify(['x2']),
|
|
702
|
+
y: JSON.stringify(['y1', 'y2'])
|
|
703
|
+
};
|
|
704
|
+
const thirdState = {
|
|
705
|
+
id: 'id0',
|
|
706
|
+
x: JSON.stringify(['x2']),
|
|
707
|
+
y: JSON.stringify(['y2'])
|
|
708
|
+
};
|
|
709
|
+
const replicaId = test_utils.rid('id0');
|
|
710
|
+
await writer.markAllSnapshotDone('1/1');
|
|
711
|
+
await writer.save({
|
|
712
|
+
sourceTable: tableB,
|
|
713
|
+
tag: storage.SaveOperationTag.INSERT,
|
|
714
|
+
after: firstState,
|
|
715
|
+
afterReplicaId: replicaId
|
|
716
|
+
});
|
|
717
|
+
await writer.commit('1/1');
|
|
718
|
+
let checkpoint = await bucketStorage.getCheckpoint();
|
|
719
|
+
const parameters = new RequestParameters(new JwtPayload({ sub: 'y1' }), {});
|
|
720
|
+
const querier = sync_rules.getBucketParameterQuerier({
|
|
721
|
+
...test_utils.querierOptions(parameters)
|
|
722
|
+
}).querier;
|
|
723
|
+
let buckets = await querier.queryDynamicBucketDescriptions({
|
|
724
|
+
async getParameterSets(lookups) {
|
|
725
|
+
expect(lookups.map((l) => l.indexKey)).toEqual([['y1']]);
|
|
726
|
+
const parameter_sets = await checkpoint.getParameterSets(lookups, 1000);
|
|
727
|
+
expect(parameter_sets).toEqual([{ '0': 'x1' }, { '0': 'x2' }]);
|
|
728
|
+
return parameter_sets;
|
|
729
|
+
}
|
|
730
|
+
});
|
|
731
|
+
expect(buckets).toMatchObject([
|
|
732
|
+
{
|
|
733
|
+
bucket: expect.stringMatching(/chat.*\["x1"\]$/),
|
|
734
|
+
definition: 'chat',
|
|
735
|
+
inclusion_reasons: ['default'],
|
|
736
|
+
priority: 3
|
|
737
|
+
},
|
|
738
|
+
{
|
|
739
|
+
bucket: expect.stringMatching(/chat.*\["x2"\]$/),
|
|
740
|
+
definition: 'chat',
|
|
741
|
+
inclusion_reasons: ['default'],
|
|
742
|
+
priority: 3
|
|
743
|
+
}
|
|
744
|
+
]);
|
|
745
|
+
// Make the x2 bucket inaccessible
|
|
746
|
+
await writer.save({
|
|
747
|
+
sourceTable: tableB,
|
|
748
|
+
tag: storage.SaveOperationTag.UPDATE,
|
|
749
|
+
before: firstState,
|
|
750
|
+
after: secondState,
|
|
751
|
+
beforeReplicaId: replicaId,
|
|
752
|
+
afterReplicaId: replicaId
|
|
753
|
+
});
|
|
754
|
+
await writer.commit('1/2');
|
|
755
|
+
checkpoint = await bucketStorage.getCheckpoint();
|
|
756
|
+
buckets = await querier.queryDynamicBucketDescriptions({
|
|
757
|
+
async getParameterSets(lookups) {
|
|
758
|
+
expect(lookups.map((l) => l.indexKey)).toEqual([['y1']]);
|
|
759
|
+
const parameter_sets = await checkpoint.getParameterSets(lookups, 1000);
|
|
760
|
+
expect(parameter_sets).toEqual([{ '0': 'x2' }]);
|
|
761
|
+
return parameter_sets;
|
|
762
|
+
}
|
|
763
|
+
});
|
|
764
|
+
expect(buckets.map((bkt) => bkt.bucket)).toStrictEqual([expect.stringContaining('x2')]);
|
|
765
|
+
// Second update, remove user from inputs
|
|
766
|
+
await writer.save({
|
|
767
|
+
sourceTable: tableB,
|
|
768
|
+
tag: storage.SaveOperationTag.UPDATE,
|
|
769
|
+
before: secondState,
|
|
770
|
+
after: thirdState,
|
|
771
|
+
beforeReplicaId: replicaId,
|
|
772
|
+
afterReplicaId: replicaId
|
|
773
|
+
});
|
|
774
|
+
await writer.commit('1/3');
|
|
775
|
+
checkpoint = await bucketStorage.getCheckpoint();
|
|
776
|
+
buckets = await querier.queryDynamicBucketDescriptions({
|
|
777
|
+
async getParameterSets(lookups) {
|
|
778
|
+
expect(lookups.map((l) => l.indexKey)).toEqual([['y1']]);
|
|
779
|
+
const parameter_sets = await checkpoint.getParameterSets(lookups, 1000);
|
|
780
|
+
expect(parameter_sets).toHaveLength(0);
|
|
781
|
+
return parameter_sets;
|
|
782
|
+
}
|
|
783
|
+
});
|
|
784
|
+
expect(buckets).toHaveLength(0);
|
|
736
785
|
});
|
|
737
786
|
}
|
|
738
787
|
//# sourceMappingURL=register-data-storage-parameter-tests.js.map
|