@rotorsoft/act-tck 1.5.0 → 1.5.2
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/README.md +21 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/@types/fixtures/helpers.d.ts +2 -2
- package/dist/@types/fixtures/helpers.d.ts.map +1 -1
- package/dist/@types/index.d.ts +3 -1
- package/dist/@types/index.d.ts.map +1 -1
- package/dist/@types/stability-tck.d.ts +52 -0
- package/dist/@types/stability-tck.d.ts.map +1 -0
- package/dist/@types/store-tck.d.ts.map +1 -1
- package/dist/index.cjs +686 -506
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +673 -502
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -89,7 +89,7 @@ var COUNTER_EVENT_NAMES = [
|
|
|
89
89
|
import { randomUUID } from "crypto";
|
|
90
90
|
var uid = () => randomUUID().slice(0, 8);
|
|
91
91
|
var actor = (name = "tester") => ({ id: randomUUID(), name });
|
|
92
|
-
var
|
|
92
|
+
var make_meta = (opts = {}) => ({
|
|
93
93
|
correlation: opts.correlation ?? randomUUID(),
|
|
94
94
|
causation: opts.stream ? {
|
|
95
95
|
action: {
|
|
@@ -108,13 +108,13 @@ var dec = (amount = 1) => ({
|
|
|
108
108
|
data: { amount }
|
|
109
109
|
});
|
|
110
110
|
var reset = () => ({ name: "Reset", data: {} });
|
|
111
|
-
var
|
|
111
|
+
var seed_stream = async (store, stream, count, correlation) => {
|
|
112
112
|
const out = [];
|
|
113
113
|
for (let i = 0; i < count; i++) {
|
|
114
114
|
const committed = await store.commit(
|
|
115
115
|
stream,
|
|
116
116
|
[inc(1)],
|
|
117
|
-
|
|
117
|
+
make_meta({ correlation, stream })
|
|
118
118
|
);
|
|
119
119
|
out.push(...committed);
|
|
120
120
|
}
|
|
@@ -134,14 +134,14 @@ var LEVELS = ["fatal", "error", "warn", "info", "debug", "trace"];
|
|
|
134
134
|
var runLoggerTck = (options) => {
|
|
135
135
|
describe2(`TCK / Logger / ${options.name}`, () => {
|
|
136
136
|
let logger;
|
|
137
|
-
let
|
|
137
|
+
let original_stdout;
|
|
138
138
|
beforeEach3(() => {
|
|
139
139
|
logger = options.factory();
|
|
140
|
-
|
|
140
|
+
original_stdout = process.stdout.write.bind(process.stdout);
|
|
141
141
|
process.stdout.write = (() => true);
|
|
142
142
|
});
|
|
143
143
|
afterEach2(async () => {
|
|
144
|
-
process.stdout.write =
|
|
144
|
+
process.stdout.write = original_stdout;
|
|
145
145
|
await logger.dispose();
|
|
146
146
|
});
|
|
147
147
|
it2("exposes a non-empty `level` string", () => {
|
|
@@ -168,7 +168,7 @@ var runLoggerTck = (options) => {
|
|
|
168
168
|
expect2(() => logger.info(cyclic, "cycle")).not.toThrow();
|
|
169
169
|
});
|
|
170
170
|
it2("child(bindings) returns a Logger satisfying the same contract", () => {
|
|
171
|
-
const child = logger.child({
|
|
171
|
+
const child = logger.child({ request_id: "abc" });
|
|
172
172
|
expect2(typeof child.level).toBe("string");
|
|
173
173
|
for (const level of LEVELS) {
|
|
174
174
|
expect2(typeof child[level]).toBe("function");
|
|
@@ -188,6 +188,46 @@ var runLoggerTck = (options) => {
|
|
|
188
188
|
});
|
|
189
189
|
};
|
|
190
190
|
|
|
191
|
+
// src/stability-tck.ts
|
|
192
|
+
import { promises as fs } from "fs";
|
|
193
|
+
import path from "path";
|
|
194
|
+
import { describe as describe3, expect as expect3, it as it3 } from "vitest";
|
|
195
|
+
function runStabilityTck(options) {
|
|
196
|
+
describe3(`${options.name} \u2014 public API stability`, () => {
|
|
197
|
+
for (const [subpath, entry2] of Object.entries(options.entryPoints)) {
|
|
198
|
+
const label = subpath || "(root)";
|
|
199
|
+
it3(`stable public surface for ${label}`, async () => {
|
|
200
|
+
const surface = await load_surface(entry2);
|
|
201
|
+
expect3(surface).toMatchSnapshot();
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
async function load_surface(entry2) {
|
|
207
|
+
const visited = /* @__PURE__ */ new Map();
|
|
208
|
+
async function walk(file) {
|
|
209
|
+
const abs = path.resolve(file);
|
|
210
|
+
if (visited.has(abs)) return;
|
|
211
|
+
const content = await fs.readFile(abs, "utf8");
|
|
212
|
+
visited.set(abs, content);
|
|
213
|
+
const re_export = /^\s*(?:import|export)\b[^"';]*\bfrom\s+["'](\.[^"']+)["']/gm;
|
|
214
|
+
for (const m of content.matchAll(re_export)) {
|
|
215
|
+
const rel = m[1].replace(/\.js$/, ".ts");
|
|
216
|
+
const target = path.resolve(path.dirname(abs), rel);
|
|
217
|
+
await walk(target);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
await walk(entry2);
|
|
221
|
+
const sorted = [...visited.entries()].sort(
|
|
222
|
+
([a], [b]) => path.basename(a).localeCompare(path.basename(b))
|
|
223
|
+
);
|
|
224
|
+
return sorted.map(([abs, content]) => {
|
|
225
|
+
const rel = path.basename(abs);
|
|
226
|
+
return `// === ${rel} ===
|
|
227
|
+
${content}`;
|
|
228
|
+
}).join("\n");
|
|
229
|
+
}
|
|
230
|
+
|
|
191
231
|
// src/store-tck.ts
|
|
192
232
|
import {
|
|
193
233
|
act,
|
|
@@ -196,9 +236,9 @@ import {
|
|
|
196
236
|
SNAP_EVENT,
|
|
197
237
|
TOMBSTONE_EVENT
|
|
198
238
|
} from "@rotorsoft/act";
|
|
199
|
-
import { afterAll, beforeAll, describe as
|
|
239
|
+
import { afterAll, beforeAll, describe as describe4, expect as expect4, it as it4 } from "vitest";
|
|
200
240
|
var runStoreTck = (options) => {
|
|
201
|
-
|
|
241
|
+
describe4(`TCK / Store / ${options.name}`, () => {
|
|
202
242
|
let store;
|
|
203
243
|
const caps = { ...options.capabilities };
|
|
204
244
|
beforeAll(async () => {
|
|
@@ -209,103 +249,107 @@ var runStoreTck = (options) => {
|
|
|
209
249
|
afterAll(async () => {
|
|
210
250
|
await store.dispose();
|
|
211
251
|
});
|
|
212
|
-
|
|
213
|
-
|
|
252
|
+
describe4("commit", () => {
|
|
253
|
+
it4("returns committed events with sequenced ids and versions", async () => {
|
|
214
254
|
const s = `commit-seq-${uid()}`;
|
|
215
255
|
const committed = await store.commit(
|
|
216
256
|
s,
|
|
217
257
|
[inc(1), inc(2), dec(3)],
|
|
218
|
-
|
|
219
|
-
);
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
258
|
+
make_meta({ stream: s })
|
|
259
|
+
);
|
|
260
|
+
expect4(committed).toHaveLength(3);
|
|
261
|
+
expect4(committed[0].version).toBe(0);
|
|
262
|
+
expect4(committed[1].version).toBe(1);
|
|
263
|
+
expect4(committed[2].version).toBe(2);
|
|
264
|
+
expect4(committed[0].name).toBe("Incremented");
|
|
265
|
+
expect4(committed[2].data).toEqual({ amount: 3 });
|
|
226
266
|
for (let i = 1; i < committed.length; i++) {
|
|
227
|
-
|
|
267
|
+
expect4(committed[i].id).toBeGreaterThan(committed[i - 1].id);
|
|
228
268
|
}
|
|
229
269
|
});
|
|
230
|
-
|
|
270
|
+
it4("attaches correlation and stream metadata", async () => {
|
|
231
271
|
const s = `commit-meta-${uid()}`;
|
|
232
272
|
const correlation = `cor-${uid()}`;
|
|
233
273
|
const committed = await store.commit(
|
|
234
274
|
s,
|
|
235
275
|
[inc(1)],
|
|
236
|
-
|
|
276
|
+
make_meta({ stream: s, correlation })
|
|
237
277
|
);
|
|
238
|
-
|
|
239
|
-
|
|
278
|
+
expect4(committed[0].stream).toBe(s);
|
|
279
|
+
expect4(committed[0].meta.correlation).toBe(correlation);
|
|
240
280
|
});
|
|
241
|
-
|
|
281
|
+
it4("throws ConcurrencyError when expectedVersion is wrong", async () => {
|
|
242
282
|
const s = `commit-cc-${uid()}`;
|
|
243
|
-
await store.commit(s, [inc(1)], makeMeta({ stream: s }));
|
|
244
283
|
await store.commit(
|
|
245
284
|
s,
|
|
246
285
|
[inc(1)],
|
|
247
|
-
|
|
286
|
+
make_meta({ stream: s })
|
|
287
|
+
);
|
|
288
|
+
await store.commit(
|
|
289
|
+
s,
|
|
290
|
+
[inc(1)],
|
|
291
|
+
make_meta({ stream: s }),
|
|
248
292
|
0
|
|
249
293
|
);
|
|
250
|
-
await
|
|
251
|
-
store.commit(s, [inc(1)],
|
|
294
|
+
await expect4(
|
|
295
|
+
store.commit(s, [inc(1)], make_meta({ stream: s }), 0)
|
|
252
296
|
).rejects.toBeInstanceOf(ConcurrencyError);
|
|
253
297
|
});
|
|
254
|
-
|
|
298
|
+
it4("preserves prior events when a concurrent commit is rejected", async () => {
|
|
255
299
|
const s = `commit-cc-preserve-${uid()}`;
|
|
256
300
|
await store.commit(
|
|
257
301
|
s,
|
|
258
302
|
[inc(1), inc(2)],
|
|
259
|
-
|
|
303
|
+
make_meta({ stream: s })
|
|
260
304
|
);
|
|
261
|
-
await
|
|
262
|
-
store.commit(s, [inc(3)],
|
|
305
|
+
await expect4(
|
|
306
|
+
store.commit(s, [inc(3)], make_meta({ stream: s }), 0)
|
|
263
307
|
).rejects.toBeInstanceOf(ConcurrencyError);
|
|
264
308
|
const found = await collect(store, { stream: s, stream_exact: true });
|
|
265
|
-
|
|
309
|
+
expect4(found).toHaveLength(2);
|
|
266
310
|
});
|
|
267
311
|
});
|
|
268
|
-
|
|
269
|
-
|
|
312
|
+
describe4("query", () => {
|
|
313
|
+
it4("filters by stream, names, correlation, limit, with_snaps", async () => {
|
|
270
314
|
const s1 = `q-s1-${uid()}`;
|
|
271
315
|
const s2 = `q-s2-${uid()}`;
|
|
272
316
|
const cor = `q-cor-${uid()}`;
|
|
273
317
|
await store.commit(
|
|
274
318
|
s1,
|
|
275
319
|
[inc(1), dec(1)],
|
|
276
|
-
|
|
320
|
+
make_meta({ stream: s1, correlation: cor })
|
|
277
321
|
);
|
|
278
322
|
await store.commit(
|
|
279
323
|
s2,
|
|
280
324
|
[inc(2), dec(2), reset()],
|
|
281
|
-
|
|
325
|
+
make_meta({ stream: s2, correlation: cor })
|
|
282
326
|
);
|
|
283
|
-
const
|
|
327
|
+
const by_stream = await collect(store, {
|
|
284
328
|
stream: s1,
|
|
285
329
|
stream_exact: true
|
|
286
330
|
});
|
|
287
|
-
|
|
288
|
-
const
|
|
331
|
+
expect4(by_stream).toHaveLength(2);
|
|
332
|
+
const by_name = await collect(store, {
|
|
289
333
|
stream: s2,
|
|
290
334
|
stream_exact: true,
|
|
291
335
|
names: ["Reset"]
|
|
292
336
|
});
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
const
|
|
296
|
-
|
|
337
|
+
expect4(by_name).toHaveLength(1);
|
|
338
|
+
expect4(by_name[0].name).toBe("Reset");
|
|
339
|
+
const by_correlation = await collect(store, { correlation: cor });
|
|
340
|
+
expect4(by_correlation).toHaveLength(5);
|
|
297
341
|
const limited = await collect(store, {
|
|
298
342
|
correlation: cor,
|
|
299
343
|
limit: 2
|
|
300
344
|
});
|
|
301
|
-
|
|
345
|
+
expect4(limited).toHaveLength(2);
|
|
302
346
|
});
|
|
303
|
-
|
|
347
|
+
it4("supports backward traversal", async () => {
|
|
304
348
|
const s = `q-back-${uid()}`;
|
|
305
349
|
const committed = await store.commit(
|
|
306
350
|
s,
|
|
307
351
|
[inc(1), inc(2), inc(3)],
|
|
308
|
-
|
|
352
|
+
make_meta({ stream: s })
|
|
309
353
|
);
|
|
310
354
|
const forward = await collect(store, { stream: s, stream_exact: true });
|
|
311
355
|
const backward = await collect(store, {
|
|
@@ -313,8 +357,8 @@ var runStoreTck = (options) => {
|
|
|
313
357
|
stream_exact: true,
|
|
314
358
|
backward: true
|
|
315
359
|
});
|
|
316
|
-
|
|
317
|
-
|
|
360
|
+
expect4(forward.map((e) => e.id)).toEqual(committed.map((c) => c.id));
|
|
361
|
+
expect4(backward.map((e) => e.id)).toEqual(
|
|
318
362
|
[...committed].reverse().map((c) => c.id)
|
|
319
363
|
);
|
|
320
364
|
const latest = await collect(store, {
|
|
@@ -323,58 +367,62 @@ var runStoreTck = (options) => {
|
|
|
323
367
|
backward: true,
|
|
324
368
|
limit: 1
|
|
325
369
|
});
|
|
326
|
-
|
|
327
|
-
|
|
370
|
+
expect4(latest).toHaveLength(1);
|
|
371
|
+
expect4(latest[0].id).toBe(committed.at(-1).id);
|
|
328
372
|
});
|
|
329
|
-
|
|
373
|
+
it4("after/before bound the id range", async () => {
|
|
330
374
|
const s = `q-bounds-${uid()}`;
|
|
331
375
|
const committed = await store.commit(
|
|
332
376
|
s,
|
|
333
377
|
[inc(1), inc(2), inc(3), inc(4)],
|
|
334
|
-
|
|
378
|
+
make_meta({ stream: s })
|
|
335
379
|
);
|
|
336
|
-
const
|
|
380
|
+
const after_first = await collect(store, {
|
|
337
381
|
stream: s,
|
|
338
382
|
stream_exact: true,
|
|
339
383
|
after: committed[0].id
|
|
340
384
|
});
|
|
341
|
-
|
|
385
|
+
expect4(after_first.map((e) => e.id)).toEqual(
|
|
342
386
|
committed.slice(1).map((c) => c.id)
|
|
343
387
|
);
|
|
344
|
-
const
|
|
388
|
+
const before_last = await collect(store, {
|
|
345
389
|
stream: s,
|
|
346
390
|
stream_exact: true,
|
|
347
391
|
before: committed[committed.length - 1].id
|
|
348
392
|
});
|
|
349
|
-
|
|
393
|
+
expect4(before_last.map((e) => e.id)).toEqual(
|
|
350
394
|
committed.slice(0, -1).map((c) => c.id)
|
|
351
395
|
);
|
|
352
396
|
});
|
|
353
|
-
|
|
397
|
+
it4("created_after/created_before filter by timestamp", async () => {
|
|
354
398
|
const s = `q-ts-${uid()}`;
|
|
355
399
|
const before = new Date(Date.now() - 6e4);
|
|
356
400
|
const future = new Date(Date.now() + 6e4);
|
|
357
|
-
await store.commit(
|
|
358
|
-
|
|
401
|
+
await store.commit(
|
|
402
|
+
s,
|
|
403
|
+
[inc(1)],
|
|
404
|
+
make_meta({ stream: s })
|
|
405
|
+
);
|
|
406
|
+
const in_window = await collect(store, {
|
|
359
407
|
stream: s,
|
|
360
408
|
stream_exact: true,
|
|
361
409
|
created_after: before,
|
|
362
410
|
created_before: future
|
|
363
411
|
});
|
|
364
|
-
|
|
365
|
-
const
|
|
412
|
+
expect4(in_window.length).toBe(1);
|
|
413
|
+
const out_of_window = await collect(store, {
|
|
366
414
|
stream: s,
|
|
367
415
|
stream_exact: true,
|
|
368
416
|
created_after: future
|
|
369
417
|
});
|
|
370
|
-
|
|
418
|
+
expect4(out_of_window.length).toBe(0);
|
|
371
419
|
});
|
|
372
|
-
|
|
420
|
+
it4("backward traversal short-circuits at `after` id boundary", async () => {
|
|
373
421
|
const s = `q-back-after-${uid()}`;
|
|
374
422
|
const committed = await store.commit(
|
|
375
423
|
s,
|
|
376
424
|
[inc(1), inc(2), inc(3)],
|
|
377
|
-
|
|
425
|
+
make_meta({ stream: s })
|
|
378
426
|
);
|
|
379
427
|
const got = await collect(store, {
|
|
380
428
|
stream: s,
|
|
@@ -382,14 +430,18 @@ var runStoreTck = (options) => {
|
|
|
382
430
|
backward: true,
|
|
383
431
|
after: committed[0].id
|
|
384
432
|
});
|
|
385
|
-
|
|
433
|
+
expect4(got.map((e) => e.id)).toEqual([
|
|
386
434
|
committed[2].id,
|
|
387
435
|
committed[1].id
|
|
388
436
|
]);
|
|
389
437
|
});
|
|
390
|
-
|
|
438
|
+
it4("backward traversal short-circuits at `created_after` boundary", async () => {
|
|
391
439
|
const s = `q-back-cafter-${uid()}`;
|
|
392
|
-
await store.commit(
|
|
440
|
+
await store.commit(
|
|
441
|
+
s,
|
|
442
|
+
[inc(1)],
|
|
443
|
+
make_meta({ stream: s })
|
|
444
|
+
);
|
|
393
445
|
const future = new Date(Date.now() + 6e4);
|
|
394
446
|
const got = await collect(store, {
|
|
395
447
|
stream: s,
|
|
@@ -397,11 +449,11 @@ var runStoreTck = (options) => {
|
|
|
397
449
|
backward: true,
|
|
398
450
|
created_after: future
|
|
399
451
|
});
|
|
400
|
-
|
|
452
|
+
expect4(got).toHaveLength(0);
|
|
401
453
|
});
|
|
402
|
-
|
|
454
|
+
it4("backward traversal honors created_before by skipping newer events", async () => {
|
|
403
455
|
const s = `q-back-ts-${uid()}`;
|
|
404
|
-
await store.commit(s, [inc(1)],
|
|
456
|
+
await store.commit(s, [inc(1)], make_meta());
|
|
405
457
|
const past = new Date(Date.now() - 6e4);
|
|
406
458
|
const got = await collect(store, {
|
|
407
459
|
stream: s,
|
|
@@ -409,302 +461,362 @@ var runStoreTck = (options) => {
|
|
|
409
461
|
backward: true,
|
|
410
462
|
created_before: past
|
|
411
463
|
});
|
|
412
|
-
|
|
464
|
+
expect4(got).toHaveLength(0);
|
|
413
465
|
});
|
|
414
|
-
|
|
466
|
+
it4("stream_exact disables regex matching", async () => {
|
|
415
467
|
const tag = uid();
|
|
416
468
|
const a = `q-exact-${tag}`;
|
|
417
469
|
const b = `q-exact-${tag}-extra`;
|
|
418
|
-
await store.commit(
|
|
419
|
-
|
|
470
|
+
await store.commit(
|
|
471
|
+
a,
|
|
472
|
+
[inc(1)],
|
|
473
|
+
make_meta({ stream: a })
|
|
474
|
+
);
|
|
475
|
+
await store.commit(
|
|
476
|
+
b,
|
|
477
|
+
[inc(2)],
|
|
478
|
+
make_meta({ stream: b })
|
|
479
|
+
);
|
|
420
480
|
const exact = await collect(store, { stream: a, stream_exact: true });
|
|
421
|
-
|
|
422
|
-
|
|
481
|
+
expect4(exact).toHaveLength(1);
|
|
482
|
+
expect4(exact[0].data).toEqual({ amount: 1 });
|
|
423
483
|
});
|
|
424
|
-
|
|
484
|
+
it4("plain regex without anchors is a substring match", async () => {
|
|
425
485
|
const tag = uid();
|
|
426
486
|
const inner = `qr-${tag}-inner`;
|
|
427
487
|
const longer = `qr-${tag}-inner-extra`;
|
|
428
488
|
await store.commit(
|
|
429
489
|
inner,
|
|
430
490
|
[inc(1)],
|
|
431
|
-
|
|
491
|
+
make_meta({ stream: inner })
|
|
432
492
|
);
|
|
433
493
|
await store.commit(
|
|
434
494
|
longer,
|
|
435
495
|
[inc(2)],
|
|
436
|
-
|
|
496
|
+
make_meta({ stream: longer })
|
|
437
497
|
);
|
|
438
498
|
const got = await collect(store, { stream: `qr-${tag}-inner` });
|
|
439
|
-
|
|
499
|
+
expect4(got.map((e) => e.stream).sort()).toEqual([inner, longer].sort());
|
|
440
500
|
});
|
|
441
|
-
|
|
501
|
+
it4("caller-anchored `^name$` matches only the whole string", async () => {
|
|
442
502
|
const tag = uid();
|
|
443
503
|
const inner = `qr-${tag}-anchor`;
|
|
444
504
|
const longer = `qr-${tag}-anchor-extra`;
|
|
445
505
|
await store.commit(
|
|
446
506
|
inner,
|
|
447
507
|
[inc(1)],
|
|
448
|
-
|
|
508
|
+
make_meta({ stream: inner })
|
|
449
509
|
);
|
|
450
510
|
await store.commit(
|
|
451
511
|
longer,
|
|
452
512
|
[inc(2)],
|
|
453
|
-
|
|
513
|
+
make_meta({ stream: longer })
|
|
454
514
|
);
|
|
455
515
|
const got = await collect(store, { stream: `^qr-${tag}-anchor$` });
|
|
456
|
-
|
|
457
|
-
|
|
516
|
+
expect4(got).toHaveLength(1);
|
|
517
|
+
expect4(got[0].stream).toBe(inner);
|
|
458
518
|
});
|
|
459
|
-
|
|
519
|
+
it4("caller-anchored `^prefix` matches by prefix", async () => {
|
|
460
520
|
const tag = uid();
|
|
461
521
|
const a = `qr-${tag}-pfx-a`;
|
|
462
522
|
const b = `qr-${tag}-pfx-b`;
|
|
463
523
|
const other = `zz-${tag}-other`;
|
|
464
|
-
await store.commit(
|
|
465
|
-
|
|
524
|
+
await store.commit(
|
|
525
|
+
a,
|
|
526
|
+
[inc(1)],
|
|
527
|
+
make_meta({ stream: a })
|
|
528
|
+
);
|
|
529
|
+
await store.commit(
|
|
530
|
+
b,
|
|
531
|
+
[inc(2)],
|
|
532
|
+
make_meta({ stream: b })
|
|
533
|
+
);
|
|
466
534
|
await store.commit(
|
|
467
535
|
other,
|
|
468
536
|
[inc(3)],
|
|
469
|
-
|
|
537
|
+
make_meta({ stream: other })
|
|
470
538
|
);
|
|
471
539
|
const got = await collect(store, { stream: `^qr-${tag}-pfx-` });
|
|
472
|
-
|
|
540
|
+
expect4(got.map((e) => e.stream).sort()).toEqual([a, b].sort());
|
|
473
541
|
});
|
|
474
542
|
});
|
|
475
|
-
|
|
476
|
-
|
|
543
|
+
describe4("subscribe + claim + ack", () => {
|
|
544
|
+
it4("subscribes new streams and is idempotent on repeat", async () => {
|
|
477
545
|
const s = `sub-${uid()}`;
|
|
478
546
|
const first = await store.subscribe([{ stream: s }]);
|
|
479
|
-
|
|
547
|
+
expect4(first.subscribed).toBe(1);
|
|
480
548
|
const second = await store.subscribe([{ stream: s }]);
|
|
481
|
-
|
|
549
|
+
expect4(second.subscribed).toBe(0);
|
|
482
550
|
});
|
|
483
|
-
|
|
551
|
+
it4("claims a subscribed stream and ack releases the lease", async () => {
|
|
484
552
|
const s = `claim-${uid()}`;
|
|
485
553
|
await store.subscribe([{ stream: s }]);
|
|
486
|
-
await store.commit(
|
|
554
|
+
await store.commit(
|
|
555
|
+
s,
|
|
556
|
+
[inc(1)],
|
|
557
|
+
make_meta({ stream: s })
|
|
558
|
+
);
|
|
487
559
|
const by = `worker-${uid()}`;
|
|
488
560
|
const leased = await store.claim(100, 0, by, 1e4);
|
|
489
561
|
const mine = leased.find((l) => l.stream === s);
|
|
490
|
-
|
|
491
|
-
|
|
562
|
+
expect4(mine).toBeDefined();
|
|
563
|
+
expect4(mine.by).toBe(by);
|
|
492
564
|
await store.ack([{ ...mine, at: mine.at + 1 }]);
|
|
493
565
|
});
|
|
494
|
-
|
|
566
|
+
it4("does not double-claim a held lease", async () => {
|
|
495
567
|
const s = `claim-held-${uid()}`;
|
|
496
568
|
const other = `claim-other-${uid()}`;
|
|
497
569
|
await store.subscribe([{ stream: s }]);
|
|
498
|
-
await store.commit(
|
|
570
|
+
await store.commit(
|
|
571
|
+
s,
|
|
572
|
+
[inc(1)],
|
|
573
|
+
make_meta({ stream: s })
|
|
574
|
+
);
|
|
499
575
|
const leasedA = await store.claim(100, 0, `wA-${uid()}`, 1e5);
|
|
500
576
|
const targetA = leasedA.find((l) => l.stream === s);
|
|
501
|
-
|
|
577
|
+
expect4(targetA).toBeDefined();
|
|
502
578
|
await store.subscribe([{ stream: other }]);
|
|
503
579
|
await store.commit(
|
|
504
580
|
other,
|
|
505
581
|
[inc(2)],
|
|
506
|
-
|
|
582
|
+
make_meta({ stream: other })
|
|
507
583
|
);
|
|
508
584
|
const leasedB = await store.claim(100, 0, `wB-${uid()}`, 1e5);
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
585
|
+
expect4(leasedB.length).toBeGreaterThan(0);
|
|
586
|
+
expect4(leasedB.find((l) => l.stream === s)).toBeUndefined();
|
|
587
|
+
expect4(leasedB.find((l) => l.stream === other)).toBeDefined();
|
|
512
588
|
});
|
|
513
|
-
|
|
589
|
+
it4("supports dual frontiers (lagging + leading)", async () => {
|
|
514
590
|
const s = `claim-dual-${uid()}`;
|
|
515
591
|
await store.subscribe([{ stream: s }]);
|
|
516
592
|
await store.commit(
|
|
517
593
|
s,
|
|
518
594
|
[inc(1), inc(2)],
|
|
519
|
-
|
|
595
|
+
make_meta({ stream: s })
|
|
520
596
|
);
|
|
521
597
|
const first = await store.claim(100, 0, `w-${uid()}`, 1);
|
|
522
598
|
const mine = first.find((l) => l.stream === s);
|
|
523
|
-
|
|
599
|
+
expect4(mine).toBeDefined();
|
|
524
600
|
await store.ack([{ ...mine, at: mine.at + 1 }]);
|
|
525
601
|
const second = await store.claim(0, 100, `w-${uid()}`, 1);
|
|
526
|
-
|
|
602
|
+
expect4(second.find((l) => l.stream === s)).toBeDefined();
|
|
527
603
|
});
|
|
528
|
-
|
|
604
|
+
it4("dedupes when both frontiers would return the same stream", async () => {
|
|
529
605
|
const s = `claim-dedup-${uid()}`;
|
|
530
606
|
await store.subscribe([{ stream: s }]);
|
|
531
|
-
await store.commit(
|
|
607
|
+
await store.commit(
|
|
608
|
+
s,
|
|
609
|
+
[inc(1)],
|
|
610
|
+
make_meta({ stream: s })
|
|
611
|
+
);
|
|
532
612
|
const claimed = await store.claim(100, 100, `w-${uid()}`, 1e5);
|
|
533
613
|
const matches = claimed.filter((l) => l.stream === s);
|
|
534
|
-
|
|
614
|
+
expect4(matches).toHaveLength(1);
|
|
535
615
|
});
|
|
536
|
-
|
|
616
|
+
it4("silently ignores ack from the wrong holder", async () => {
|
|
537
617
|
const s = `ack-wrong-${uid()}`;
|
|
538
618
|
const sibling = `ack-sibling-${uid()}`;
|
|
539
619
|
await store.subscribe([{ stream: s }, { stream: sibling }]);
|
|
540
|
-
await store.commit(
|
|
620
|
+
await store.commit(
|
|
621
|
+
s,
|
|
622
|
+
[inc(1)],
|
|
623
|
+
make_meta({ stream: s })
|
|
624
|
+
);
|
|
541
625
|
await store.commit(
|
|
542
626
|
sibling,
|
|
543
627
|
[inc(2)],
|
|
544
|
-
|
|
628
|
+
make_meta({ stream: sibling })
|
|
545
629
|
);
|
|
546
630
|
const leased = await store.claim(100, 0, `right-${uid()}`, 1e5);
|
|
547
631
|
const mine = leased.find((l) => l.stream === s);
|
|
548
|
-
const
|
|
549
|
-
|
|
550
|
-
|
|
632
|
+
const sibling_lease = leased.find((l) => l.stream === sibling);
|
|
633
|
+
expect4(mine).toBeDefined();
|
|
634
|
+
expect4(sibling_lease).toBeDefined();
|
|
551
635
|
const acked = await store.ack([
|
|
552
636
|
{ ...mine, by: "imposter" },
|
|
553
|
-
|
|
637
|
+
sibling_lease
|
|
554
638
|
]);
|
|
555
|
-
|
|
556
|
-
|
|
639
|
+
expect4(acked.length).toBeGreaterThan(0);
|
|
640
|
+
expect4(acked.find((l) => l.stream === s)).toBeUndefined();
|
|
557
641
|
});
|
|
558
|
-
|
|
642
|
+
it4("ack with a stale (lower) watermark does not throw", async () => {
|
|
559
643
|
const s = `ack-stale-${uid()}`;
|
|
560
644
|
await store.subscribe([{ stream: s }]);
|
|
561
645
|
const by = `w-${uid()}`;
|
|
562
646
|
const leased = await store.claim(100, 0, by, 1e5);
|
|
563
647
|
const mine = leased.find((l) => l.stream === s);
|
|
564
|
-
|
|
565
|
-
await
|
|
648
|
+
expect4(mine).toBeDefined();
|
|
649
|
+
await expect4(
|
|
566
650
|
store.ack([{ ...mine, at: -5 }])
|
|
567
651
|
).resolves.toBeDefined();
|
|
568
652
|
});
|
|
569
|
-
|
|
653
|
+
it4("claim with no subscribed streams returns an empty array", async () => {
|
|
570
654
|
const fresh = await options.factory();
|
|
571
655
|
try {
|
|
572
656
|
await fresh.drop();
|
|
573
657
|
await fresh.seed();
|
|
574
658
|
const claimed = await fresh.claim(1, 1, `w-${uid()}`, 1e3);
|
|
575
|
-
|
|
659
|
+
expect4(claimed).toEqual([]);
|
|
576
660
|
} finally {
|
|
577
661
|
await fresh.dispose();
|
|
578
662
|
}
|
|
579
663
|
});
|
|
580
664
|
});
|
|
581
|
-
|
|
582
|
-
|
|
665
|
+
describe4("block", () => {
|
|
666
|
+
it4("hides blocked streams from claim", async () => {
|
|
583
667
|
const s = `block-${uid()}`;
|
|
584
668
|
await store.subscribe([{ stream: s }]);
|
|
585
|
-
await store.commit(
|
|
669
|
+
await store.commit(
|
|
670
|
+
s,
|
|
671
|
+
[inc(1)],
|
|
672
|
+
make_meta({ stream: s })
|
|
673
|
+
);
|
|
586
674
|
const leased = await store.claim(100, 0, `w-${uid()}`, 1e5);
|
|
587
675
|
const mine = leased.find((l) => l.stream === s);
|
|
588
|
-
|
|
676
|
+
expect4(mine).toBeDefined();
|
|
589
677
|
const others = leased.filter((l) => l.stream !== s);
|
|
590
678
|
await store.ack(others);
|
|
591
679
|
const blocked = await store.block([
|
|
592
680
|
{ ...mine, error: "boom" }
|
|
593
681
|
]);
|
|
594
|
-
|
|
595
|
-
|
|
682
|
+
expect4(blocked).toHaveLength(1);
|
|
683
|
+
expect4(blocked[0].error).toBe("boom");
|
|
596
684
|
const again = await store.claim(100, 100, `w2-${uid()}`, 1e5);
|
|
597
|
-
|
|
685
|
+
expect4(again.find((l) => l.stream === s)).toBeUndefined();
|
|
598
686
|
});
|
|
599
|
-
|
|
687
|
+
it4("rejects block calls from a different holder", async () => {
|
|
600
688
|
const s = `block-wrong-${uid()}`;
|
|
601
689
|
await store.subscribe([{ stream: s }]);
|
|
602
|
-
await store.commit(
|
|
690
|
+
await store.commit(
|
|
691
|
+
s,
|
|
692
|
+
[inc(1)],
|
|
693
|
+
make_meta({ stream: s })
|
|
694
|
+
);
|
|
603
695
|
const leased = await store.claim(100, 0, `right-${uid()}`, 1e5);
|
|
604
696
|
const mine = leased.find((l) => l.stream === s);
|
|
605
|
-
|
|
697
|
+
expect4(mine).toBeDefined();
|
|
606
698
|
const others = leased.filter((l) => l.stream !== s);
|
|
607
699
|
await store.ack(others);
|
|
608
700
|
const blocked = await store.block([
|
|
609
701
|
{ ...mine, by: "imposter", error: "no" }
|
|
610
702
|
]);
|
|
611
|
-
|
|
703
|
+
expect4(blocked).toHaveLength(0);
|
|
612
704
|
});
|
|
613
705
|
});
|
|
614
|
-
|
|
615
|
-
|
|
706
|
+
describe4("reset", () => {
|
|
707
|
+
it4("rewinds a stream watermark to -1", async () => {
|
|
616
708
|
const s = `reset-${uid()}`;
|
|
617
709
|
await store.subscribe([{ stream: s }]);
|
|
618
|
-
await store.commit(
|
|
710
|
+
await store.commit(
|
|
711
|
+
s,
|
|
712
|
+
[inc(1)],
|
|
713
|
+
make_meta({ stream: s })
|
|
714
|
+
);
|
|
619
715
|
const leased = await store.claim(100, 0, `w-${uid()}`, 1e5);
|
|
620
716
|
const mine = leased.find((l) => l.stream === s);
|
|
621
|
-
|
|
717
|
+
expect4(mine).toBeDefined();
|
|
622
718
|
await store.ack([{ ...mine, at: 99 }]);
|
|
623
|
-
|
|
719
|
+
expect4(await store.reset([s])).toBe(1);
|
|
624
720
|
const after = await store.claim(100, 0, `w2-${uid()}`, 1e5);
|
|
625
721
|
const back = after.find((l) => l.stream === s);
|
|
626
|
-
|
|
627
|
-
|
|
722
|
+
expect4(back).toBeDefined();
|
|
723
|
+
expect4(back.at).toBe(-1);
|
|
628
724
|
});
|
|
629
|
-
|
|
725
|
+
it4("clears blocked status when resetting", async () => {
|
|
630
726
|
const s = `reset-blk-${uid()}`;
|
|
631
727
|
await store.subscribe([{ stream: s }]);
|
|
632
|
-
await store.commit(
|
|
728
|
+
await store.commit(
|
|
729
|
+
s,
|
|
730
|
+
[inc(1)],
|
|
731
|
+
make_meta({ stream: s })
|
|
732
|
+
);
|
|
633
733
|
const leased = await store.claim(100, 0, `w-${uid()}`, 1e5);
|
|
634
734
|
const mine = leased.find((l) => l.stream === s);
|
|
635
735
|
const others = leased.filter((l) => l.stream !== s);
|
|
636
736
|
await store.ack(others);
|
|
637
737
|
await store.block([{ ...mine, error: "boom" }]);
|
|
638
|
-
|
|
738
|
+
expect4(await store.reset([s])).toBe(1);
|
|
639
739
|
const after = await store.claim(100, 0, `w2-${uid()}`, 1e5);
|
|
640
|
-
|
|
740
|
+
expect4(after.find((l) => l.stream === s)).toBeDefined();
|
|
641
741
|
});
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
742
|
+
it4("returns 0 for unknown streams and empty input", async () => {
|
|
743
|
+
expect4(await store.reset([`missing-${uid()}`])).toBe(0);
|
|
744
|
+
expect4(await store.reset([])).toBe(0);
|
|
645
745
|
});
|
|
646
746
|
});
|
|
647
|
-
|
|
648
|
-
|
|
747
|
+
describe4("unblock", () => {
|
|
748
|
+
it4("clears blocked flag and preserves the watermark", async () => {
|
|
649
749
|
const s = `unblock-${uid()}`;
|
|
650
750
|
await store.subscribe([{ stream: s }]);
|
|
651
|
-
await store.commit(
|
|
652
|
-
|
|
751
|
+
await store.commit(
|
|
752
|
+
s,
|
|
753
|
+
[inc(1)],
|
|
754
|
+
make_meta({ stream: s })
|
|
755
|
+
);
|
|
756
|
+
await store.commit(
|
|
757
|
+
s,
|
|
758
|
+
[inc(2)],
|
|
759
|
+
make_meta({ stream: s })
|
|
760
|
+
);
|
|
653
761
|
const first = await store.claim(100, 0, `w-${uid()}`, 1e5);
|
|
654
762
|
const m1 = first.find((l) => l.stream === s);
|
|
655
763
|
await store.ack([{ ...m1, at: m1.at }]);
|
|
656
|
-
const
|
|
657
|
-
const m2 =
|
|
658
|
-
|
|
659
|
-
const
|
|
764
|
+
const before_block = await store.claim(100, 0, `w-${uid()}`, 1e5);
|
|
765
|
+
const m2 = before_block.find((l) => l.stream === s);
|
|
766
|
+
expect4(m2).toBeDefined();
|
|
767
|
+
const watermark_before = m2.at;
|
|
660
768
|
await store.block([{ ...m2, error: "permanent" }]);
|
|
661
|
-
let
|
|
769
|
+
let blocked_flag;
|
|
662
770
|
await store.query_streams(
|
|
663
771
|
(p) => {
|
|
664
|
-
|
|
772
|
+
blocked_flag = p.blocked;
|
|
665
773
|
},
|
|
666
774
|
{ stream: s, stream_exact: true, limit: 1 }
|
|
667
775
|
);
|
|
668
|
-
|
|
669
|
-
|
|
776
|
+
expect4(blocked_flag).toBe(true);
|
|
777
|
+
expect4(await store.unblock([s])).toBe(1);
|
|
670
778
|
const after = await store.claim(100, 0, `w-${uid()}`, 1e5);
|
|
671
779
|
const back = after.find((l) => l.stream === s);
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
780
|
+
expect4(back).toBeDefined();
|
|
781
|
+
expect4(back.at).toBe(watermark_before);
|
|
782
|
+
expect4(back.retry).toBe(0);
|
|
675
783
|
});
|
|
676
|
-
|
|
784
|
+
it4("returns 0 when the stream is not blocked", async () => {
|
|
677
785
|
const s = `unblock-noop-${uid()}`;
|
|
678
786
|
await store.subscribe([{ stream: s }]);
|
|
679
|
-
await store.commit(
|
|
680
|
-
|
|
787
|
+
await store.commit(
|
|
788
|
+
s,
|
|
789
|
+
[inc(1)],
|
|
790
|
+
make_meta({ stream: s })
|
|
791
|
+
);
|
|
792
|
+
expect4(await store.unblock([s])).toBe(0);
|
|
681
793
|
});
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
794
|
+
it4("returns 0 for unknown streams and empty input", async () => {
|
|
795
|
+
expect4(await store.unblock([`missing-${uid()}`])).toBe(0);
|
|
796
|
+
expect4(await store.unblock([])).toBe(0);
|
|
685
797
|
});
|
|
686
|
-
|
|
798
|
+
it4("only counts streams that were actually blocked", async () => {
|
|
687
799
|
const s1 = `unblock-mix-a-${uid()}`;
|
|
688
800
|
const s2 = `unblock-mix-b-${uid()}`;
|
|
689
801
|
await store.subscribe([{ stream: s1 }, { stream: s2 }]);
|
|
690
802
|
await store.commit(
|
|
691
803
|
s1,
|
|
692
804
|
[inc(1)],
|
|
693
|
-
|
|
805
|
+
make_meta({ stream: s1 })
|
|
694
806
|
);
|
|
695
807
|
await store.commit(
|
|
696
808
|
s2,
|
|
697
809
|
[inc(1)],
|
|
698
|
-
|
|
810
|
+
make_meta({ stream: s2 })
|
|
699
811
|
);
|
|
700
812
|
const leased = await store.claim(100, 0, `w-${uid()}`, 1e5);
|
|
701
813
|
const m1 = leased.find((l) => l.stream === s1);
|
|
702
814
|
const others = leased.filter((l) => l.stream !== s1);
|
|
703
815
|
await store.ack(others);
|
|
704
816
|
await store.block([{ ...m1, error: "boom" }]);
|
|
705
|
-
|
|
817
|
+
expect4(await store.unblock([s1, s2])).toBe(1);
|
|
706
818
|
});
|
|
707
|
-
|
|
819
|
+
it4("filter form: unblocks by stream pattern", async () => {
|
|
708
820
|
const tag = uid();
|
|
709
821
|
const s1 = `unblock-filter-${tag}-a`;
|
|
710
822
|
const s2 = `unblock-filter-${tag}-b`;
|
|
@@ -713,17 +825,17 @@ var runStoreTck = (options) => {
|
|
|
713
825
|
await store.commit(
|
|
714
826
|
s1,
|
|
715
827
|
[inc(1)],
|
|
716
|
-
|
|
828
|
+
make_meta({ stream: s1 })
|
|
717
829
|
);
|
|
718
830
|
await store.commit(
|
|
719
831
|
s2,
|
|
720
832
|
[inc(1)],
|
|
721
|
-
|
|
833
|
+
make_meta({ stream: s2 })
|
|
722
834
|
);
|
|
723
835
|
await store.commit(
|
|
724
836
|
s3,
|
|
725
837
|
[inc(1)],
|
|
726
|
-
|
|
838
|
+
make_meta({ stream: s3 })
|
|
727
839
|
);
|
|
728
840
|
const leased = await store.claim(100, 0, `w-${uid()}`, 1e5);
|
|
729
841
|
const blockable = leased.filter((l) => l.stream === s1 || l.stream === s2 || l.stream === s3).map((l) => ({ ...l, error: "boom" }));
|
|
@@ -736,13 +848,13 @@ var runStoreTck = (options) => {
|
|
|
736
848
|
const count = await store.unblock({
|
|
737
849
|
stream: `^unblock-filter-${tag}-`
|
|
738
850
|
});
|
|
739
|
-
|
|
851
|
+
expect4(count).toBe(2);
|
|
740
852
|
const after = await store.claim(100, 0, `w-${uid()}`, 1e5);
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
853
|
+
expect4(after.find((l) => l.stream === s3)).toBeUndefined();
|
|
854
|
+
expect4(after.find((l) => l.stream === s1)).toBeDefined();
|
|
855
|
+
expect4(after.find((l) => l.stream === s2)).toBeDefined();
|
|
744
856
|
});
|
|
745
|
-
|
|
857
|
+
it4("filter form: empty filter unblocks every blocked stream", async () => {
|
|
746
858
|
const tag = uid();
|
|
747
859
|
const s1 = `unblock-empty-${tag}-a`;
|
|
748
860
|
const s2 = `unblock-empty-${tag}-b`;
|
|
@@ -750,12 +862,12 @@ var runStoreTck = (options) => {
|
|
|
750
862
|
await store.commit(
|
|
751
863
|
s1,
|
|
752
864
|
[inc(1)],
|
|
753
|
-
|
|
865
|
+
make_meta({ stream: s1 })
|
|
754
866
|
);
|
|
755
867
|
await store.commit(
|
|
756
868
|
s2,
|
|
757
869
|
[inc(1)],
|
|
758
|
-
|
|
870
|
+
make_meta({ stream: s2 })
|
|
759
871
|
);
|
|
760
872
|
const leased = await store.claim(100, 0, `w-${uid()}`, 1e5);
|
|
761
873
|
const mine = leased.filter((l) => l.stream === s1 || l.stream === s2);
|
|
@@ -766,14 +878,18 @@ var runStoreTck = (options) => {
|
|
|
766
878
|
const count = await store.unblock({
|
|
767
879
|
stream: `^unblock-empty-${tag}-`
|
|
768
880
|
});
|
|
769
|
-
|
|
881
|
+
expect4(count).toBe(2);
|
|
770
882
|
});
|
|
771
|
-
|
|
883
|
+
it4("filter form: explicit blocked:false matches nothing", async () => {
|
|
772
884
|
const tag = uid();
|
|
773
885
|
const s = `unblock-blocked-false-${tag}`;
|
|
774
886
|
await store.subscribe([{ stream: s }]);
|
|
775
|
-
await store.commit(
|
|
776
|
-
|
|
887
|
+
await store.commit(
|
|
888
|
+
s,
|
|
889
|
+
[inc(1)],
|
|
890
|
+
make_meta({ stream: s })
|
|
891
|
+
);
|
|
892
|
+
expect4(
|
|
777
893
|
await store.unblock({
|
|
778
894
|
stream: `^unblock-blocked-false-${tag}`,
|
|
779
895
|
blocked: false
|
|
@@ -781,8 +897,8 @@ var runStoreTck = (options) => {
|
|
|
781
897
|
).toBe(0);
|
|
782
898
|
});
|
|
783
899
|
});
|
|
784
|
-
|
|
785
|
-
|
|
900
|
+
describe4("reset filter form", () => {
|
|
901
|
+
it4("resets streams matching a stream pattern", async () => {
|
|
786
902
|
const tag = uid();
|
|
787
903
|
const s1 = `reset-filter-${tag}-a`;
|
|
788
904
|
const s2 = `reset-filter-${tag}-b`;
|
|
@@ -795,17 +911,17 @@ var runStoreTck = (options) => {
|
|
|
795
911
|
await store.commit(
|
|
796
912
|
s1,
|
|
797
913
|
[inc(1)],
|
|
798
|
-
|
|
914
|
+
make_meta({ stream: s1 })
|
|
799
915
|
);
|
|
800
916
|
await store.commit(
|
|
801
917
|
s2,
|
|
802
918
|
[inc(1)],
|
|
803
|
-
|
|
919
|
+
make_meta({ stream: s2 })
|
|
804
920
|
);
|
|
805
921
|
await store.commit(
|
|
806
922
|
other,
|
|
807
923
|
[inc(1)],
|
|
808
|
-
|
|
924
|
+
make_meta({ stream: other })
|
|
809
925
|
);
|
|
810
926
|
const leased = await store.claim(100, 0, `w-${uid()}`, 1e5);
|
|
811
927
|
const mine = leased.filter(
|
|
@@ -813,8 +929,8 @@ var runStoreTck = (options) => {
|
|
|
813
929
|
);
|
|
814
930
|
await store.ack(mine.map((l) => ({ ...l, at: l.at + 100 })));
|
|
815
931
|
const count = await store.reset({ stream: `^reset-filter-${tag}-` });
|
|
816
|
-
|
|
817
|
-
const
|
|
932
|
+
expect4(count).toBe(2);
|
|
933
|
+
const position_for = async (name) => {
|
|
818
934
|
let at = null;
|
|
819
935
|
await store.query_streams(
|
|
820
936
|
(p) => {
|
|
@@ -824,11 +940,11 @@ var runStoreTck = (options) => {
|
|
|
824
940
|
);
|
|
825
941
|
return at;
|
|
826
942
|
};
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
943
|
+
expect4(await position_for(s1)).toBe(-1);
|
|
944
|
+
expect4(await position_for(s2)).toBe(-1);
|
|
945
|
+
expect4(await position_for(other)).toBeGreaterThan(-1);
|
|
830
946
|
});
|
|
831
|
-
|
|
947
|
+
it4("filter form: resets only blocked streams when blocked:true", async () => {
|
|
832
948
|
const tag = uid();
|
|
833
949
|
const s1 = `reset-blocked-${tag}-blocked`;
|
|
834
950
|
const s2 = `reset-blocked-${tag}-fine`;
|
|
@@ -836,12 +952,12 @@ var runStoreTck = (options) => {
|
|
|
836
952
|
await store.commit(
|
|
837
953
|
s1,
|
|
838
954
|
[inc(1)],
|
|
839
|
-
|
|
955
|
+
make_meta({ stream: s1 })
|
|
840
956
|
);
|
|
841
957
|
await store.commit(
|
|
842
958
|
s2,
|
|
843
959
|
[inc(1)],
|
|
844
|
-
|
|
960
|
+
make_meta({ stream: s2 })
|
|
845
961
|
);
|
|
846
962
|
const leased = await store.claim(100, 0, `w-${uid()}`, 1e5);
|
|
847
963
|
const m1 = leased.find((l) => l.stream === s1);
|
|
@@ -851,11 +967,11 @@ var runStoreTck = (options) => {
|
|
|
851
967
|
stream: `^reset-blocked-${tag}-`,
|
|
852
968
|
blocked: true
|
|
853
969
|
});
|
|
854
|
-
|
|
970
|
+
expect4(count).toBe(1);
|
|
855
971
|
});
|
|
856
972
|
});
|
|
857
|
-
|
|
858
|
-
|
|
973
|
+
describe4("prioritize", () => {
|
|
974
|
+
it4("sets priority directly, overriding subscribe's max() rule", async () => {
|
|
859
975
|
const tag = uid();
|
|
860
976
|
const s1 = `pri-${tag}-a`;
|
|
861
977
|
const s2 = `pri-${tag}-b`;
|
|
@@ -867,7 +983,7 @@ var runStoreTck = (options) => {
|
|
|
867
983
|
{ stream: s1, stream_exact: true },
|
|
868
984
|
3
|
|
869
985
|
);
|
|
870
|
-
|
|
986
|
+
expect4(updated).toBe(1);
|
|
871
987
|
const got1 = {};
|
|
872
988
|
const got2 = {};
|
|
873
989
|
await store.query_streams(
|
|
@@ -877,12 +993,12 @@ var runStoreTck = (options) => {
|
|
|
877
993
|
},
|
|
878
994
|
{ stream: `pri-${tag}-.*`, limit: 100 }
|
|
879
995
|
);
|
|
880
|
-
|
|
881
|
-
|
|
996
|
+
expect4(got1.priority).toBe(3);
|
|
997
|
+
expect4(got2.priority).toBe(5);
|
|
882
998
|
});
|
|
883
999
|
});
|
|
884
|
-
|
|
885
|
-
|
|
1000
|
+
describe4("lanes", () => {
|
|
1001
|
+
it4("subscribe defaults lane to 'default' when omitted", async () => {
|
|
886
1002
|
const s = `lane-default-${uid()}`;
|
|
887
1003
|
await store.subscribe([{ stream: s }]);
|
|
888
1004
|
const seen = [];
|
|
@@ -890,9 +1006,9 @@ var runStoreTck = (options) => {
|
|
|
890
1006
|
stream: s,
|
|
891
1007
|
stream_exact: true
|
|
892
1008
|
});
|
|
893
|
-
|
|
1009
|
+
expect4(seen).toEqual(["default"]);
|
|
894
1010
|
});
|
|
895
|
-
|
|
1011
|
+
it4("subscribe records the lane passed in", async () => {
|
|
896
1012
|
const s = `lane-set-${uid()}`;
|
|
897
1013
|
await store.subscribe([{ stream: s, lane: "slow" }]);
|
|
898
1014
|
const seen = [];
|
|
@@ -900,9 +1016,9 @@ var runStoreTck = (options) => {
|
|
|
900
1016
|
stream: s,
|
|
901
1017
|
stream_exact: true
|
|
902
1018
|
});
|
|
903
|
-
|
|
1019
|
+
expect4(seen).toEqual(["slow"]);
|
|
904
1020
|
});
|
|
905
|
-
|
|
1021
|
+
it4("subscribe re-lanes existing streams on subsequent calls", async () => {
|
|
906
1022
|
const s = `lane-upsert-${uid()}`;
|
|
907
1023
|
await store.subscribe([{ stream: s, lane: "slow" }]);
|
|
908
1024
|
await store.subscribe([{ stream: s, lane: "fast" }]);
|
|
@@ -911,45 +1027,45 @@ var runStoreTck = (options) => {
|
|
|
911
1027
|
stream: s,
|
|
912
1028
|
stream_exact: true
|
|
913
1029
|
});
|
|
914
|
-
|
|
1030
|
+
expect4(seen).toEqual(["fast"]);
|
|
915
1031
|
});
|
|
916
|
-
|
|
1032
|
+
it4("claim() filters by lane when supplied and returns lane on the Lease", async () => {
|
|
917
1033
|
const tag = uid();
|
|
918
1034
|
const src1 = `lane-claim-src1-${tag}`;
|
|
919
1035
|
const src2 = `lane-claim-src2-${tag}`;
|
|
920
|
-
const
|
|
921
|
-
const
|
|
1036
|
+
const sub_default = `lane-claim-def-${tag}`;
|
|
1037
|
+
const sub_slow = `lane-claim-slow-${tag}`;
|
|
922
1038
|
await store.commit(
|
|
923
1039
|
src1,
|
|
924
1040
|
[inc(1)],
|
|
925
|
-
|
|
1041
|
+
make_meta({ stream: src1 })
|
|
926
1042
|
);
|
|
927
1043
|
await store.commit(
|
|
928
1044
|
src2,
|
|
929
1045
|
[inc(1)],
|
|
930
|
-
|
|
1046
|
+
make_meta({ stream: src2 })
|
|
931
1047
|
);
|
|
932
1048
|
await store.subscribe([
|
|
933
|
-
{ stream:
|
|
934
|
-
{ stream:
|
|
1049
|
+
{ stream: sub_default, source: src1 },
|
|
1050
|
+
{ stream: sub_slow, source: src2, lane: "slow" }
|
|
935
1051
|
]);
|
|
936
1052
|
const slow = await store.claim(50, 0, `w-slow-${tag}`, 1e3, "slow");
|
|
937
|
-
const
|
|
938
|
-
(l) => l.stream ===
|
|
1053
|
+
const slow_mine = slow.filter(
|
|
1054
|
+
(l) => l.stream === sub_default || l.stream === sub_slow
|
|
939
1055
|
);
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
await store.ack(
|
|
1056
|
+
expect4(slow_mine.map((l) => l.stream)).toEqual([sub_slow]);
|
|
1057
|
+
expect4(slow_mine[0]?.lane).toBe("slow");
|
|
1058
|
+
await store.ack(slow_mine.map((l) => ({ ...l, at: l.at + 1 })));
|
|
943
1059
|
const all = await store.claim(50, 0, `w-all-${tag}`, 1e3);
|
|
944
|
-
const
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
{ stream:
|
|
948
|
-
{ stream:
|
|
1060
|
+
const all_mine = all.filter((l) => l.stream === sub_default || l.stream === sub_slow).map((l) => ({ stream: l.stream, lane: l.lane }));
|
|
1061
|
+
expect4(all_mine).toEqual(
|
|
1062
|
+
expect4.arrayContaining([
|
|
1063
|
+
{ stream: sub_default, lane: "default" },
|
|
1064
|
+
{ stream: sub_slow, lane: "slow" }
|
|
949
1065
|
])
|
|
950
1066
|
);
|
|
951
1067
|
});
|
|
952
|
-
|
|
1068
|
+
it4("query_streams filters by lane", async () => {
|
|
953
1069
|
const tag = uid();
|
|
954
1070
|
const a = `lane-q-a-${tag}`;
|
|
955
1071
|
const b = `lane-q-b-${tag}`;
|
|
@@ -965,9 +1081,9 @@ var runStoreTck = (options) => {
|
|
|
965
1081
|
stream: `lane-q-.*-${tag}`,
|
|
966
1082
|
limit: 100
|
|
967
1083
|
});
|
|
968
|
-
|
|
1084
|
+
expect4(seen.sort()).toEqual([a, c]);
|
|
969
1085
|
});
|
|
970
|
-
|
|
1086
|
+
it4("prioritize filters by lane", async () => {
|
|
971
1087
|
const tag = uid();
|
|
972
1088
|
const a = `lane-pri-a-${tag}`;
|
|
973
1089
|
const b = `lane-pri-b-${tag}`;
|
|
@@ -976,16 +1092,16 @@ var runStoreTck = (options) => {
|
|
|
976
1092
|
{ stream: b, lane: `pfast-${tag}` }
|
|
977
1093
|
]);
|
|
978
1094
|
const updated = await store.prioritize({ lane: `pslow-${tag}` }, 7);
|
|
979
|
-
|
|
1095
|
+
expect4(updated).toBe(1);
|
|
980
1096
|
const seen = /* @__PURE__ */ new Map();
|
|
981
1097
|
await store.query_streams((p) => seen.set(p.stream, p.priority), {
|
|
982
1098
|
stream: `lane-pri-.*-${tag}`,
|
|
983
1099
|
limit: 100
|
|
984
1100
|
});
|
|
985
|
-
|
|
986
|
-
|
|
1101
|
+
expect4(seen.get(a)).toBe(7);
|
|
1102
|
+
expect4(seen.get(b)).toBe(0);
|
|
987
1103
|
});
|
|
988
|
-
|
|
1104
|
+
it4("reset filters by lane", async () => {
|
|
989
1105
|
const tag = uid();
|
|
990
1106
|
const src = `lane-reset-src-${tag}`;
|
|
991
1107
|
const a = `lane-reset-a-${tag}`;
|
|
@@ -993,7 +1109,7 @@ var runStoreTck = (options) => {
|
|
|
993
1109
|
await store.commit(
|
|
994
1110
|
src,
|
|
995
1111
|
[inc(1)],
|
|
996
|
-
|
|
1112
|
+
make_meta({ stream: src })
|
|
997
1113
|
);
|
|
998
1114
|
await store.subscribe([
|
|
999
1115
|
{ stream: a, source: src, lane: `rslow-${tag}` },
|
|
@@ -1003,7 +1119,7 @@ var runStoreTck = (options) => {
|
|
|
1003
1119
|
const mine = leases.filter((l) => l.stream === a || l.stream === b);
|
|
1004
1120
|
await store.ack(mine.map((l) => ({ ...l, at: l.at + 1 })));
|
|
1005
1121
|
const count = await store.reset({ lane: `rslow-${tag}` });
|
|
1006
|
-
|
|
1122
|
+
expect4(count).toBe(1);
|
|
1007
1123
|
const ats = /* @__PURE__ */ new Map();
|
|
1008
1124
|
for (const name of [a, b]) {
|
|
1009
1125
|
await store.query_streams((p) => ats.set(p.stream, p.at), {
|
|
@@ -1011,10 +1127,10 @@ var runStoreTck = (options) => {
|
|
|
1011
1127
|
stream_exact: true
|
|
1012
1128
|
});
|
|
1013
1129
|
}
|
|
1014
|
-
|
|
1015
|
-
|
|
1130
|
+
expect4(ats.get(a)).toBe(-1);
|
|
1131
|
+
expect4(ats.get(b)).toBeGreaterThanOrEqual(0);
|
|
1016
1132
|
});
|
|
1017
|
-
|
|
1133
|
+
it4("unblock filters by lane", async () => {
|
|
1018
1134
|
const tag = uid();
|
|
1019
1135
|
const src = `lane-ub-src-${tag}`;
|
|
1020
1136
|
const a = `lane-ub-a-${tag}`;
|
|
@@ -1022,7 +1138,7 @@ var runStoreTck = (options) => {
|
|
|
1022
1138
|
await store.commit(
|
|
1023
1139
|
src,
|
|
1024
1140
|
[inc(1)],
|
|
1025
|
-
|
|
1141
|
+
make_meta({ stream: src })
|
|
1026
1142
|
);
|
|
1027
1143
|
await store.subscribe([
|
|
1028
1144
|
{ stream: a, source: src, lane: `uslow-${tag}` },
|
|
@@ -1032,7 +1148,7 @@ var runStoreTck = (options) => {
|
|
|
1032
1148
|
const mine = leases.filter((l) => l.stream === a || l.stream === b);
|
|
1033
1149
|
await store.block(mine.map((l) => ({ ...l, error: "boom" })));
|
|
1034
1150
|
const count = await store.unblock({ lane: `uslow-${tag}` });
|
|
1035
|
-
|
|
1151
|
+
expect4(count).toBe(1);
|
|
1036
1152
|
const blocked = /* @__PURE__ */ new Map();
|
|
1037
1153
|
for (const name of [a, b]) {
|
|
1038
1154
|
await store.query_streams((p) => blocked.set(p.stream, p.blocked), {
|
|
@@ -1040,20 +1156,20 @@ var runStoreTck = (options) => {
|
|
|
1040
1156
|
stream_exact: true
|
|
1041
1157
|
});
|
|
1042
1158
|
}
|
|
1043
|
-
|
|
1044
|
-
|
|
1159
|
+
expect4(blocked.get(a)).toBe(false);
|
|
1160
|
+
expect4(blocked.get(b)).toBe(true);
|
|
1045
1161
|
});
|
|
1046
1162
|
});
|
|
1047
|
-
|
|
1048
|
-
|
|
1163
|
+
describe4("truncate", () => {
|
|
1164
|
+
it4("seeds a tombstone when no snapshot is provided", async () => {
|
|
1049
1165
|
const s = `trunc-tomb-${uid()}`;
|
|
1050
1166
|
await store.commit(
|
|
1051
1167
|
s,
|
|
1052
1168
|
[inc(1), inc(2)],
|
|
1053
|
-
|
|
1169
|
+
make_meta({ stream: s })
|
|
1054
1170
|
);
|
|
1055
1171
|
const result = await store.truncate([{ stream: s }]);
|
|
1056
|
-
|
|
1172
|
+
expect4(result.get(s)?.deleted).toBe(2);
|
|
1057
1173
|
const remaining = [];
|
|
1058
1174
|
await store.query(
|
|
1059
1175
|
(e) => {
|
|
@@ -1061,18 +1177,22 @@ var runStoreTck = (options) => {
|
|
|
1061
1177
|
},
|
|
1062
1178
|
{ stream: s, stream_exact: true }
|
|
1063
1179
|
);
|
|
1064
|
-
|
|
1065
|
-
|
|
1180
|
+
expect4(remaining).toHaveLength(1);
|
|
1181
|
+
expect4(remaining[0].name).toBe(
|
|
1066
1182
|
"__tombstone__"
|
|
1067
1183
|
);
|
|
1068
1184
|
});
|
|
1069
|
-
|
|
1185
|
+
it4("seeds a snapshot when one is provided", async () => {
|
|
1070
1186
|
const s = `trunc-snap-${uid()}`;
|
|
1071
|
-
await store.commit(
|
|
1187
|
+
await store.commit(
|
|
1188
|
+
s,
|
|
1189
|
+
[inc(1)],
|
|
1190
|
+
make_meta({ stream: s })
|
|
1191
|
+
);
|
|
1072
1192
|
const result = await store.truncate([
|
|
1073
1193
|
{ stream: s, snapshot: { count: 7 } }
|
|
1074
1194
|
]);
|
|
1075
|
-
|
|
1195
|
+
expect4(result.get(s)?.deleted).toBe(1);
|
|
1076
1196
|
const remaining = [];
|
|
1077
1197
|
await store.query(
|
|
1078
1198
|
(e) => {
|
|
@@ -1080,24 +1200,24 @@ var runStoreTck = (options) => {
|
|
|
1080
1200
|
},
|
|
1081
1201
|
{ stream: s, stream_exact: true, with_snaps: true }
|
|
1082
1202
|
);
|
|
1083
|
-
|
|
1084
|
-
|
|
1203
|
+
expect4(remaining).toHaveLength(1);
|
|
1204
|
+
expect4(remaining[0].name).toBe(
|
|
1085
1205
|
"__snapshot__"
|
|
1086
1206
|
);
|
|
1087
|
-
|
|
1207
|
+
expect4(remaining[0].data).toEqual({ count: 7 });
|
|
1088
1208
|
});
|
|
1089
|
-
|
|
1209
|
+
it4("returns an empty map for empty input", async () => {
|
|
1090
1210
|
const result = await store.truncate([]);
|
|
1091
|
-
|
|
1211
|
+
expect4(result.size).toBe(0);
|
|
1092
1212
|
});
|
|
1093
|
-
|
|
1213
|
+
it4("returns 0 deleted for streams that don't exist", async () => {
|
|
1094
1214
|
const s = `trunc-missing-${uid()}`;
|
|
1095
1215
|
const result = await store.truncate([{ stream: s }]);
|
|
1096
|
-
|
|
1216
|
+
expect4(result.get(s)?.deleted).toBe(0);
|
|
1097
1217
|
});
|
|
1098
1218
|
});
|
|
1099
|
-
|
|
1100
|
-
|
|
1219
|
+
describe4("query_streams", () => {
|
|
1220
|
+
it4("returns positions filtered by stream regex, exact, source, and source_exact", async () => {
|
|
1101
1221
|
const tag = uid();
|
|
1102
1222
|
const proj1 = `qs-${tag}-projection-tickets`;
|
|
1103
1223
|
const proj2 = `qs-${tag}-projection-users`;
|
|
@@ -1112,41 +1232,41 @@ var runStoreTck = (options) => {
|
|
|
1112
1232
|
{ stream: dyn2, source: src2 }
|
|
1113
1233
|
]);
|
|
1114
1234
|
const all = [];
|
|
1115
|
-
const
|
|
1235
|
+
const all_result = await store.query_streams(
|
|
1116
1236
|
(p) => all.push({ stream: p.stream, source: p.source }),
|
|
1117
1237
|
{ stream: `qs-${tag}-.*` }
|
|
1118
1238
|
);
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1239
|
+
expect4(all_result.count).toBe(4);
|
|
1240
|
+
expect4(all_result.maxEventId).toBeGreaterThanOrEqual(-1);
|
|
1241
|
+
expect4(all.map((p) => p.stream).sort()).toEqual(
|
|
1122
1242
|
[proj1, proj2, dyn1, dyn2].sort()
|
|
1123
1243
|
);
|
|
1124
1244
|
const projections = [];
|
|
1125
1245
|
await store.query_streams((p) => projections.push(p.stream), {
|
|
1126
1246
|
stream: `qs-${tag}-projection-.*`
|
|
1127
1247
|
});
|
|
1128
|
-
|
|
1248
|
+
expect4(projections.sort()).toEqual([proj1, proj2].sort());
|
|
1129
1249
|
const exact = [];
|
|
1130
1250
|
await store.query_streams((p) => exact.push(p.stream), {
|
|
1131
1251
|
stream: dyn1,
|
|
1132
1252
|
stream_exact: true
|
|
1133
1253
|
});
|
|
1134
|
-
|
|
1135
|
-
const
|
|
1136
|
-
await store.query_streams((p) =>
|
|
1254
|
+
expect4(exact).toEqual([dyn1]);
|
|
1255
|
+
const by_source = [];
|
|
1256
|
+
await store.query_streams((p) => by_source.push(p.stream), {
|
|
1137
1257
|
stream: `qs-${tag}-.*`,
|
|
1138
1258
|
source: `qs-${tag}-src-.*`
|
|
1139
1259
|
});
|
|
1140
|
-
|
|
1141
|
-
const
|
|
1142
|
-
await store.query_streams((p) =>
|
|
1260
|
+
expect4(by_source.sort()).toEqual([dyn1, dyn2].sort());
|
|
1261
|
+
const exact_source = [];
|
|
1262
|
+
await store.query_streams((p) => exact_source.push(p.stream), {
|
|
1143
1263
|
stream: `qs-${tag}-.*`,
|
|
1144
1264
|
source: src2,
|
|
1145
1265
|
source_exact: true
|
|
1146
1266
|
});
|
|
1147
|
-
|
|
1267
|
+
expect4(exact_source).toEqual([dyn2]);
|
|
1148
1268
|
});
|
|
1149
|
-
|
|
1269
|
+
it4("paginates with limit + after (keyset)", async () => {
|
|
1150
1270
|
const tag = uid();
|
|
1151
1271
|
const streams = [
|
|
1152
1272
|
`qp-${tag}-a`,
|
|
@@ -1160,22 +1280,26 @@ var runStoreTck = (options) => {
|
|
|
1160
1280
|
stream: `qp-${tag}-.*`,
|
|
1161
1281
|
limit: 2
|
|
1162
1282
|
});
|
|
1163
|
-
|
|
1283
|
+
expect4(page1).toHaveLength(2);
|
|
1164
1284
|
const page2 = [];
|
|
1165
1285
|
await store.query_streams((p) => page2.push(p.stream), {
|
|
1166
1286
|
stream: `qp-${tag}-.*`,
|
|
1167
1287
|
limit: 2,
|
|
1168
1288
|
after: page1.at(-1)
|
|
1169
1289
|
});
|
|
1170
|
-
|
|
1171
|
-
|
|
1290
|
+
expect4(page2).toHaveLength(2);
|
|
1291
|
+
expect4([...page1, ...page2].sort()).toEqual([...streams].sort());
|
|
1172
1292
|
});
|
|
1173
|
-
|
|
1293
|
+
it4("filters by blocked status", async () => {
|
|
1174
1294
|
const tag = uid();
|
|
1175
1295
|
const s = `qb-${tag}`;
|
|
1176
1296
|
const sibling = `qb-${tag}-other`;
|
|
1177
1297
|
await store.subscribe([{ stream: s }, { stream: sibling }]);
|
|
1178
|
-
await store.commit(
|
|
1298
|
+
await store.commit(
|
|
1299
|
+
s,
|
|
1300
|
+
[inc(1)],
|
|
1301
|
+
make_meta({ stream: s })
|
|
1302
|
+
);
|
|
1179
1303
|
const leased = await store.claim(100, 0, `w-${uid()}`, 1e5);
|
|
1180
1304
|
const mine = leased.find((l) => l.stream === s);
|
|
1181
1305
|
const others = leased.filter((l) => l.stream !== s);
|
|
@@ -1186,18 +1310,18 @@ var runStoreTck = (options) => {
|
|
|
1186
1310
|
(p) => blocked.push({ stream: p.stream, error: p.error }),
|
|
1187
1311
|
{ stream: `qb-${tag}.*`, blocked: true }
|
|
1188
1312
|
);
|
|
1189
|
-
|
|
1190
|
-
|
|
1313
|
+
expect4(blocked).toHaveLength(1);
|
|
1314
|
+
expect4(blocked[0].error).toBe("boom");
|
|
1191
1315
|
const unblocked = [];
|
|
1192
1316
|
await store.query_streams((p) => unblocked.push(p.stream), {
|
|
1193
1317
|
stream: `qb-${tag}.*`,
|
|
1194
1318
|
blocked: false
|
|
1195
1319
|
});
|
|
1196
|
-
|
|
1320
|
+
expect4(unblocked).toEqual([sibling]);
|
|
1197
1321
|
});
|
|
1198
1322
|
});
|
|
1199
|
-
|
|
1200
|
-
|
|
1323
|
+
describe4("query_stats", () => {
|
|
1324
|
+
it4("array input \u2014 returns head per stream, absent when not in input", async () => {
|
|
1201
1325
|
const tag = uid();
|
|
1202
1326
|
const sA = `qst-${tag}-a`;
|
|
1203
1327
|
const sB = `qst-${tag}-b`;
|
|
@@ -1205,111 +1329,131 @@ var runStoreTck = (options) => {
|
|
|
1205
1329
|
await store.commit(
|
|
1206
1330
|
sA,
|
|
1207
1331
|
[inc(1), inc(2)],
|
|
1208
|
-
|
|
1332
|
+
make_meta({ stream: sA })
|
|
1209
1333
|
);
|
|
1210
1334
|
await store.commit(
|
|
1211
1335
|
sB,
|
|
1212
1336
|
[dec(5)],
|
|
1213
|
-
|
|
1337
|
+
make_meta({ stream: sB })
|
|
1214
1338
|
);
|
|
1215
1339
|
await store.commit(
|
|
1216
1340
|
sUnasked,
|
|
1217
1341
|
[inc(99)],
|
|
1218
|
-
|
|
1342
|
+
make_meta({ stream: sUnasked })
|
|
1219
1343
|
);
|
|
1220
1344
|
const stats = await store.query_stats([sA, sB]);
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1345
|
+
expect4(stats.size).toBe(2);
|
|
1346
|
+
expect4(stats.get(sA)?.head.name).toBe("Incremented");
|
|
1347
|
+
expect4((stats.get(sA)?.head.data).amount).toBe(2);
|
|
1348
|
+
expect4(stats.get(sB)?.head.name).toBe("Decremented");
|
|
1349
|
+
expect4((stats.get(sB)?.head.data).amount).toBe(5);
|
|
1350
|
+
expect4(stats.has(sUnasked)).toBe(false);
|
|
1227
1351
|
const empty = await store.query_stats([]);
|
|
1228
|
-
|
|
1352
|
+
expect4(empty.size).toBe(0);
|
|
1229
1353
|
const unknown = await store.query_stats([`qst-${tag}-missing`]);
|
|
1230
|
-
|
|
1354
|
+
expect4(unknown.size).toBe(0);
|
|
1231
1355
|
});
|
|
1232
|
-
|
|
1356
|
+
it4("tail returns the earliest event per stream", async () => {
|
|
1233
1357
|
const tag = uid();
|
|
1234
1358
|
const s = `qst-tail-${tag}`;
|
|
1235
|
-
await store.commit(
|
|
1236
|
-
|
|
1237
|
-
|
|
1359
|
+
await store.commit(
|
|
1360
|
+
s,
|
|
1361
|
+
[inc(1)],
|
|
1362
|
+
make_meta({ stream: s })
|
|
1363
|
+
);
|
|
1364
|
+
await store.commit(
|
|
1365
|
+
s,
|
|
1366
|
+
[inc(2)],
|
|
1367
|
+
make_meta({ stream: s })
|
|
1368
|
+
);
|
|
1369
|
+
await store.commit(
|
|
1370
|
+
s,
|
|
1371
|
+
[inc(3)],
|
|
1372
|
+
make_meta({ stream: s })
|
|
1373
|
+
);
|
|
1238
1374
|
const stats = await store.query_stats([s], {
|
|
1239
1375
|
tail: true
|
|
1240
1376
|
});
|
|
1241
1377
|
const r = stats.get(s);
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1378
|
+
expect4(r?.head.name).toBe("Incremented");
|
|
1379
|
+
expect4((r?.head.data).amount).toBe(3);
|
|
1380
|
+
expect4(r?.tail?.name).toBe("Incremented");
|
|
1381
|
+
expect4((r?.tail?.data).amount).toBe(1);
|
|
1246
1382
|
});
|
|
1247
|
-
|
|
1383
|
+
it4("count + names \u2014 full aggregates including framework markers", async () => {
|
|
1248
1384
|
const tag = uid();
|
|
1249
1385
|
const s = `qst-cn-${tag}`;
|
|
1250
|
-
await store.commit(
|
|
1386
|
+
await store.commit(
|
|
1387
|
+
s,
|
|
1388
|
+
[inc(1)],
|
|
1389
|
+
make_meta({ stream: s })
|
|
1390
|
+
);
|
|
1251
1391
|
await store.truncate([{ stream: s, snapshot: { count: 99 } }]);
|
|
1252
1392
|
await store.commit(
|
|
1253
1393
|
s,
|
|
1254
1394
|
[inc(2), inc(3), dec(1)],
|
|
1255
|
-
|
|
1395
|
+
make_meta({ stream: s })
|
|
1256
1396
|
);
|
|
1257
1397
|
const stats = await store.query_stats([s], {
|
|
1258
1398
|
count: true,
|
|
1259
1399
|
names: true
|
|
1260
1400
|
});
|
|
1261
1401
|
const r = stats.get(s);
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1402
|
+
expect4(r?.count).toBe(4);
|
|
1403
|
+
expect4(r?.names?.[SNAP_EVENT]).toBe(1);
|
|
1404
|
+
expect4(r?.names?.Incremented).toBe(2);
|
|
1405
|
+
expect4(r?.names?.Decremented).toBe(1);
|
|
1406
|
+
expect4(r?.names?.[SNAP_EVENT]).toBe(1);
|
|
1267
1407
|
});
|
|
1268
|
-
|
|
1408
|
+
it4("exclude shifts head past filtered events; stream absent when all filtered", async () => {
|
|
1269
1409
|
const tag = uid();
|
|
1270
1410
|
const s = `qst-excl-${tag}`;
|
|
1271
1411
|
const sAllOut = `qst-allout-${tag}`;
|
|
1272
1412
|
await store.commit(
|
|
1273
1413
|
s,
|
|
1274
1414
|
[inc(1), dec(2), inc(3)],
|
|
1275
|
-
|
|
1415
|
+
make_meta({ stream: s })
|
|
1276
1416
|
);
|
|
1277
1417
|
await store.commit(
|
|
1278
1418
|
sAllOut,
|
|
1279
1419
|
[inc(7)],
|
|
1280
|
-
|
|
1420
|
+
make_meta({ stream: sAllOut })
|
|
1281
1421
|
);
|
|
1282
1422
|
const all = await store.query_stats([s]);
|
|
1283
|
-
|
|
1284
|
-
|
|
1423
|
+
expect4(all.get(s)?.head.name).toBe("Incremented");
|
|
1424
|
+
expect4((all.get(s)?.head.data).amount).toBe(3);
|
|
1285
1425
|
const excl = await store.query_stats([s], {
|
|
1286
1426
|
exclude: ["Incremented"]
|
|
1287
1427
|
});
|
|
1288
|
-
|
|
1289
|
-
|
|
1428
|
+
expect4(excl.get(s)?.head.name).toBe("Decremented");
|
|
1429
|
+
expect4((excl.get(s)?.head.data).amount).toBe(2);
|
|
1290
1430
|
const wipe = await store.query_stats([sAllOut], {
|
|
1291
1431
|
exclude: ["Incremented", "Decremented", "Reset"]
|
|
1292
1432
|
});
|
|
1293
|
-
|
|
1294
|
-
const
|
|
1433
|
+
expect4(wipe.has(sAllOut)).toBe(false);
|
|
1434
|
+
const no_tomb = await store.query_stats([s], {
|
|
1295
1435
|
exclude: [TOMBSTONE_EVENT]
|
|
1296
1436
|
});
|
|
1297
|
-
|
|
1437
|
+
expect4(no_tomb.get(s)?.head.name).toBe("Incremented");
|
|
1298
1438
|
});
|
|
1299
|
-
|
|
1439
|
+
it4("before \u2014 time travel narrows head/tail/count", async () => {
|
|
1300
1440
|
const tag = uid();
|
|
1301
1441
|
const s = `qst-tt-${tag}`;
|
|
1302
1442
|
const c1 = await store.commit(
|
|
1303
1443
|
s,
|
|
1304
1444
|
[inc(1)],
|
|
1305
|
-
|
|
1445
|
+
make_meta({ stream: s })
|
|
1306
1446
|
);
|
|
1307
1447
|
const c2 = await store.commit(
|
|
1308
1448
|
s,
|
|
1309
1449
|
[inc(2)],
|
|
1310
|
-
|
|
1450
|
+
make_meta({ stream: s })
|
|
1451
|
+
);
|
|
1452
|
+
await store.commit(
|
|
1453
|
+
s,
|
|
1454
|
+
[inc(3)],
|
|
1455
|
+
make_meta({ stream: s })
|
|
1311
1456
|
);
|
|
1312
|
-
await store.commit(s, [inc(3)], makeMeta({ stream: s }));
|
|
1313
1457
|
const before = c2[0].id;
|
|
1314
1458
|
const stats = await store.query_stats([s], {
|
|
1315
1459
|
tail: true,
|
|
@@ -1317,15 +1461,15 @@ var runStoreTck = (options) => {
|
|
|
1317
1461
|
before
|
|
1318
1462
|
});
|
|
1319
1463
|
const r = stats.get(s);
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1464
|
+
expect4(r?.count).toBe(1);
|
|
1465
|
+
expect4(r?.head.id).toBe(c1[0].id);
|
|
1466
|
+
expect4(r?.tail?.id).toBe(c1[0].id);
|
|
1323
1467
|
const empty = await store.query_stats([s], {
|
|
1324
1468
|
before: 0
|
|
1325
1469
|
});
|
|
1326
|
-
|
|
1470
|
+
expect4(empty.has(s)).toBe(false);
|
|
1327
1471
|
});
|
|
1328
|
-
|
|
1472
|
+
it4("filter form \u2014 stream regex, stream_exact, empty {} match", async () => {
|
|
1329
1473
|
const tag = uid();
|
|
1330
1474
|
const sA = `qsf-${tag}-orders-1`;
|
|
1331
1475
|
const sB = `qsf-${tag}-orders-2`;
|
|
@@ -1333,94 +1477,110 @@ var runStoreTck = (options) => {
|
|
|
1333
1477
|
await store.commit(
|
|
1334
1478
|
sA,
|
|
1335
1479
|
[inc(1)],
|
|
1336
|
-
|
|
1480
|
+
make_meta({ stream: sA })
|
|
1337
1481
|
);
|
|
1338
1482
|
await store.commit(
|
|
1339
1483
|
sB,
|
|
1340
1484
|
[inc(2)],
|
|
1341
|
-
|
|
1485
|
+
make_meta({ stream: sB })
|
|
1342
1486
|
);
|
|
1343
1487
|
await store.commit(
|
|
1344
1488
|
sOther,
|
|
1345
1489
|
[inc(3)],
|
|
1346
|
-
|
|
1490
|
+
make_meta({ stream: sOther })
|
|
1347
1491
|
);
|
|
1348
1492
|
const orders = await store.query_stats({
|
|
1349
1493
|
stream: `^qsf-${tag}-orders-`
|
|
1350
1494
|
});
|
|
1351
|
-
|
|
1495
|
+
expect4([...orders.keys()].sort()).toEqual([sA, sB].sort());
|
|
1352
1496
|
const exact = await store.query_stats({
|
|
1353
1497
|
stream: sA,
|
|
1354
1498
|
stream_exact: true
|
|
1355
1499
|
});
|
|
1356
|
-
|
|
1500
|
+
expect4([...exact.keys()]).toEqual([sA]);
|
|
1357
1501
|
const all = await store.query_stats({
|
|
1358
1502
|
stream: `^qsf-${tag}-`
|
|
1359
1503
|
});
|
|
1360
|
-
|
|
1504
|
+
expect4([...all.keys()].sort()).toEqual([sA, sB, sOther].sort());
|
|
1361
1505
|
});
|
|
1362
|
-
|
|
1506
|
+
it4("compose with query_streams for subscription-level filters", async () => {
|
|
1363
1507
|
const tag = uid();
|
|
1364
1508
|
const a = `qsc-${tag}-a`;
|
|
1365
1509
|
const b = `qsc-${tag}-b`;
|
|
1366
1510
|
await store.subscribe([{ stream: a }, { stream: b }]);
|
|
1367
|
-
await store.commit(
|
|
1368
|
-
|
|
1511
|
+
await store.commit(
|
|
1512
|
+
a,
|
|
1513
|
+
[inc(1)],
|
|
1514
|
+
make_meta({ stream: a })
|
|
1515
|
+
);
|
|
1516
|
+
await store.commit(
|
|
1517
|
+
b,
|
|
1518
|
+
[inc(2)],
|
|
1519
|
+
make_meta({ stream: b })
|
|
1520
|
+
);
|
|
1369
1521
|
const leased = await store.claim(100, 0, `w-${uid()}`, 1e5);
|
|
1370
1522
|
const mine = leased.find((l) => l.stream === a);
|
|
1371
|
-
|
|
1523
|
+
expect4(mine).toBeDefined();
|
|
1372
1524
|
const others = leased.filter((l) => l.stream !== a);
|
|
1373
1525
|
await store.ack(others);
|
|
1374
1526
|
await store.block([{ ...mine, error: "boom" }]);
|
|
1375
|
-
const
|
|
1376
|
-
await store.query_streams((p) =>
|
|
1527
|
+
const blocked_names = [];
|
|
1528
|
+
await store.query_streams((p) => blocked_names.push(p.stream), {
|
|
1377
1529
|
stream: `^qsc-${tag}-`,
|
|
1378
1530
|
blocked: true
|
|
1379
1531
|
});
|
|
1380
|
-
|
|
1381
|
-
const stats = await store.query_stats(
|
|
1382
|
-
|
|
1383
|
-
|
|
1532
|
+
expect4(blocked_names).toEqual([a]);
|
|
1533
|
+
const stats = await store.query_stats(blocked_names);
|
|
1534
|
+
expect4(stats.get(a)?.head.name).toBe("Incremented");
|
|
1535
|
+
expect4(stats.has(b)).toBe(false);
|
|
1384
1536
|
});
|
|
1385
|
-
|
|
1537
|
+
it4("empty filter {} \u2014 matches every event-bearing stream", async () => {
|
|
1386
1538
|
const tag = uid();
|
|
1387
1539
|
const a = `qse-${tag}-a`;
|
|
1388
1540
|
const b = `qse-${tag}-b`;
|
|
1389
|
-
await store.commit(
|
|
1390
|
-
|
|
1541
|
+
await store.commit(
|
|
1542
|
+
a,
|
|
1543
|
+
[inc(1)],
|
|
1544
|
+
make_meta({ stream: a })
|
|
1545
|
+
);
|
|
1546
|
+
await store.commit(
|
|
1547
|
+
b,
|
|
1548
|
+
[dec(2)],
|
|
1549
|
+
make_meta({ stream: b })
|
|
1550
|
+
);
|
|
1391
1551
|
const all = await store.query_stats({});
|
|
1392
|
-
|
|
1393
|
-
|
|
1552
|
+
expect4(all.has(a)).toBe(true);
|
|
1553
|
+
expect4(all.has(b)).toBe(true);
|
|
1394
1554
|
});
|
|
1395
|
-
|
|
1555
|
+
it4("stat-flag combinations \u2014 count-only, names-only, tail-only", async () => {
|
|
1396
1556
|
const tag = uid();
|
|
1397
1557
|
const s = `qsfl-${tag}`;
|
|
1398
1558
|
await store.commit(
|
|
1399
1559
|
s,
|
|
1400
1560
|
[inc(1), inc(2), dec(3)],
|
|
1401
|
-
|
|
1561
|
+
make_meta({ stream: s })
|
|
1402
1562
|
);
|
|
1403
1563
|
const c = await store.query_stats([s], {
|
|
1404
1564
|
count: true
|
|
1405
1565
|
});
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1566
|
+
expect4(c.get(s)?.count).toBe(3);
|
|
1567
|
+
expect4(c.get(s)?.names).toBeUndefined();
|
|
1568
|
+
expect4(c.get(s)?.tail).toBeUndefined();
|
|
1409
1569
|
const n = await store.query_stats([s], {
|
|
1410
1570
|
names: true
|
|
1411
1571
|
});
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1572
|
+
expect4(n.get(s)?.names).toEqual({ Incremented: 2, Decremented: 1 });
|
|
1573
|
+
expect4(n.get(s)?.count).toBeUndefined();
|
|
1574
|
+
expect4(n.get(s)?.tail).toBeUndefined();
|
|
1415
1575
|
const t = await store.query_stats([s], { tail: true });
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1576
|
+
expect4(t.get(s)?.tail?.name).toBe("Incremented");
|
|
1577
|
+
expect4((t.get(s)?.tail?.data).amount).toBe(1);
|
|
1578
|
+
expect4(t.get(s)?.count).toBeUndefined();
|
|
1579
|
+
expect4(t.get(s)?.names).toBeUndefined();
|
|
1420
1580
|
});
|
|
1421
1581
|
});
|
|
1422
|
-
|
|
1423
|
-
|
|
1582
|
+
describe4("query_streams anchor contract", () => {
|
|
1583
|
+
it4("plain regex without anchors is a substring match", async () => {
|
|
1424
1584
|
const tag = uid();
|
|
1425
1585
|
const inner = `qsr-${tag}-inner`;
|
|
1426
1586
|
const longer = `qsr-${tag}-inner-extra`;
|
|
@@ -1434,9 +1594,9 @@ var runStoreTck = (options) => {
|
|
|
1434
1594
|
await store.query_streams((p) => seen.push(p.stream), {
|
|
1435
1595
|
stream: `qsr-${tag}-inner`
|
|
1436
1596
|
});
|
|
1437
|
-
|
|
1597
|
+
expect4(seen.sort()).toEqual([inner, longer].sort());
|
|
1438
1598
|
});
|
|
1439
|
-
|
|
1599
|
+
it4("caller-anchored `^name$` matches only the whole string", async () => {
|
|
1440
1600
|
const tag = uid();
|
|
1441
1601
|
const inner = `qsr-${tag}-anchor`;
|
|
1442
1602
|
const longer = `qsr-${tag}-anchor-extra`;
|
|
@@ -1445,9 +1605,9 @@ var runStoreTck = (options) => {
|
|
|
1445
1605
|
await store.query_streams((p) => seen.push(p.stream), {
|
|
1446
1606
|
stream: `^qsr-${tag}-anchor$`
|
|
1447
1607
|
});
|
|
1448
|
-
|
|
1608
|
+
expect4(seen).toEqual([inner]);
|
|
1449
1609
|
});
|
|
1450
|
-
|
|
1610
|
+
it4("caller-anchored `^prefix` matches by prefix", async () => {
|
|
1451
1611
|
const tag = uid();
|
|
1452
1612
|
const a = `qsr-${tag}-pfx-a`;
|
|
1453
1613
|
const b = `qsr-${tag}-pfx-b`;
|
|
@@ -1461,11 +1621,11 @@ var runStoreTck = (options) => {
|
|
|
1461
1621
|
await store.query_streams((p) => seen.push(p.stream), {
|
|
1462
1622
|
stream: `^qsr-${tag}-pfx-`
|
|
1463
1623
|
});
|
|
1464
|
-
|
|
1624
|
+
expect4(seen.sort()).toEqual([a, b].sort());
|
|
1465
1625
|
});
|
|
1466
1626
|
});
|
|
1467
|
-
|
|
1468
|
-
|
|
1627
|
+
describe4("prioritize anchor contract", () => {
|
|
1628
|
+
it4("caller-anchored `^name$` filter matches only the whole string", async () => {
|
|
1469
1629
|
const tag = uid();
|
|
1470
1630
|
const inner = `pr-${tag}-anchor`;
|
|
1471
1631
|
const longer = `pr-${tag}-anchor-extra`;
|
|
@@ -1477,45 +1637,49 @@ var runStoreTck = (options) => {
|
|
|
1477
1637
|
{ stream: `^pr-${tag}-anchor$` },
|
|
1478
1638
|
7
|
|
1479
1639
|
);
|
|
1480
|
-
|
|
1640
|
+
expect4(updated).toBe(1);
|
|
1481
1641
|
const seen = /* @__PURE__ */ new Map();
|
|
1482
1642
|
await store.query_streams((p) => seen.set(p.stream, p.priority), {
|
|
1483
1643
|
stream: `pr-${tag}-anchor`
|
|
1484
1644
|
});
|
|
1485
|
-
|
|
1486
|
-
|
|
1645
|
+
expect4(seen.get(inner)).toBe(7);
|
|
1646
|
+
expect4(seen.get(longer)).toBe(0);
|
|
1487
1647
|
});
|
|
1488
1648
|
});
|
|
1489
|
-
|
|
1490
|
-
|
|
1649
|
+
describe4("query_streams head", () => {
|
|
1650
|
+
it4("maxEventId tracks the highest committed id", async () => {
|
|
1491
1651
|
const s = `head-${uid()}`;
|
|
1492
1652
|
await store.subscribe([{ stream: s }]);
|
|
1493
|
-
await store.commit(
|
|
1653
|
+
await store.commit(
|
|
1654
|
+
s,
|
|
1655
|
+
[inc(1)],
|
|
1656
|
+
make_meta({ stream: s })
|
|
1657
|
+
);
|
|
1494
1658
|
const positions = [];
|
|
1495
1659
|
const { maxEventId } = await store.query_streams(
|
|
1496
1660
|
(p) => positions.push(p.stream),
|
|
1497
1661
|
{ stream: s, stream_exact: true, limit: 1 }
|
|
1498
1662
|
);
|
|
1499
|
-
|
|
1500
|
-
|
|
1663
|
+
expect4(maxEventId).toBeGreaterThanOrEqual(0);
|
|
1664
|
+
expect4(positions).toEqual([s]);
|
|
1501
1665
|
});
|
|
1502
1666
|
});
|
|
1503
|
-
|
|
1504
|
-
|
|
1667
|
+
describe4("seed_stream helper coverage", () => {
|
|
1668
|
+
it4("commits N events with monotonically increasing ids", async () => {
|
|
1505
1669
|
const s = `seed-${uid()}`;
|
|
1506
|
-
const committed = await
|
|
1507
|
-
|
|
1670
|
+
const committed = await seed_stream(store, s, 3);
|
|
1671
|
+
expect4(committed).toHaveLength(3);
|
|
1508
1672
|
for (let i = 1; i < committed.length; i++) {
|
|
1509
|
-
|
|
1673
|
+
expect4(committed[i].id).toBeGreaterThan(committed[i - 1].id);
|
|
1510
1674
|
}
|
|
1511
1675
|
});
|
|
1512
1676
|
});
|
|
1513
|
-
|
|
1677
|
+
describe4.skipIf(!caps.restore)("restore (capability)", () => {
|
|
1514
1678
|
beforeEach(async () => {
|
|
1515
1679
|
await store.drop();
|
|
1516
1680
|
await store.seed();
|
|
1517
1681
|
});
|
|
1518
|
-
const
|
|
1682
|
+
const as_source = (events) => ({
|
|
1519
1683
|
async query(callback) {
|
|
1520
1684
|
for (const e of events)
|
|
1521
1685
|
await Promise.resolve(
|
|
@@ -1528,8 +1692,8 @@ var runStoreTck = (options) => {
|
|
|
1528
1692
|
async dispose() {
|
|
1529
1693
|
}
|
|
1530
1694
|
});
|
|
1531
|
-
const event = (
|
|
1532
|
-
id:
|
|
1695
|
+
const event = (original_id, stream, version, name, created, data = {}) => ({
|
|
1696
|
+
id: original_id,
|
|
1533
1697
|
name,
|
|
1534
1698
|
data,
|
|
1535
1699
|
stream,
|
|
@@ -1547,18 +1711,18 @@ var runStoreTck = (options) => {
|
|
|
1547
1711
|
await cache.dispose();
|
|
1548
1712
|
}
|
|
1549
1713
|
};
|
|
1550
|
-
|
|
1551
|
-
const result = await restore(
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1714
|
+
it4("returns kept=0 on an empty source", async () => {
|
|
1715
|
+
const result = await restore(as_source([]));
|
|
1716
|
+
expect4(result.kept).toBe(0);
|
|
1717
|
+
expect4(result.duration_ms).toBeGreaterThanOrEqual(0);
|
|
1718
|
+
expect4(result.dropped).toEqual({
|
|
1555
1719
|
closed_streams: 0,
|
|
1556
1720
|
snapshots: 0
|
|
1557
1721
|
});
|
|
1558
1722
|
const events = await collect(store, { limit: 10 });
|
|
1559
|
-
|
|
1723
|
+
expect4(events).toHaveLength(0);
|
|
1560
1724
|
});
|
|
1561
|
-
|
|
1725
|
+
it4("rebuilds a single stream and preserves `created` verbatim", async () => {
|
|
1562
1726
|
const s = `restore-single-${uid()}`;
|
|
1563
1727
|
const t0 = /* @__PURE__ */ new Date("2020-01-01T00:00:00.000Z");
|
|
1564
1728
|
const t1 = /* @__PURE__ */ new Date("2020-01-02T00:00:00.000Z");
|
|
@@ -1568,8 +1732,8 @@ var runStoreTck = (options) => {
|
|
|
1568
1732
|
event(2, s, 1, "Incremented", t1, { amount: 2 }),
|
|
1569
1733
|
event(3, s, 2, "Decremented", t2, { amount: 1 })
|
|
1570
1734
|
];
|
|
1571
|
-
const result = await restore(
|
|
1572
|
-
|
|
1735
|
+
const result = await restore(as_source(events));
|
|
1736
|
+
expect4(result.kept).toBe(3);
|
|
1573
1737
|
const back = [];
|
|
1574
1738
|
await store.query(
|
|
1575
1739
|
(e) => {
|
|
@@ -1577,8 +1741,8 @@ var runStoreTck = (options) => {
|
|
|
1577
1741
|
},
|
|
1578
1742
|
{ stream: s, stream_exact: true }
|
|
1579
1743
|
);
|
|
1580
|
-
|
|
1581
|
-
|
|
1744
|
+
expect4(back).toHaveLength(3);
|
|
1745
|
+
expect4(
|
|
1582
1746
|
back.map((e) => ({
|
|
1583
1747
|
stream: e.stream,
|
|
1584
1748
|
version: e.version,
|
|
@@ -1610,7 +1774,7 @@ var runStoreTck = (options) => {
|
|
|
1610
1774
|
}
|
|
1611
1775
|
]);
|
|
1612
1776
|
});
|
|
1613
|
-
|
|
1777
|
+
it4("rebuilds multiple streams interleaved", async () => {
|
|
1614
1778
|
const a = `restore-multi-a-${uid()}`;
|
|
1615
1779
|
const b = `restore-multi-b-${uid()}`;
|
|
1616
1780
|
const t = /* @__PURE__ */ new Date("2020-06-01T00:00:00.000Z");
|
|
@@ -1620,8 +1784,8 @@ var runStoreTck = (options) => {
|
|
|
1620
1784
|
event(3, a, 1, "Decremented", t, { amount: 5 }),
|
|
1621
1785
|
event(4, b, 1, "Incremented", t, { amount: 30 })
|
|
1622
1786
|
];
|
|
1623
|
-
const result = await restore(
|
|
1624
|
-
|
|
1787
|
+
const result = await restore(as_source(events));
|
|
1788
|
+
expect4(result.kept).toBe(4);
|
|
1625
1789
|
const aBack = [];
|
|
1626
1790
|
const bBack = [];
|
|
1627
1791
|
await store.query(
|
|
@@ -1636,14 +1800,14 @@ var runStoreTck = (options) => {
|
|
|
1636
1800
|
},
|
|
1637
1801
|
{ stream: b, stream_exact: true }
|
|
1638
1802
|
);
|
|
1639
|
-
|
|
1640
|
-
|
|
1803
|
+
expect4(aBack.map((e) => e.version)).toEqual([0, 1]);
|
|
1804
|
+
expect4(bBack.map((e) => e.version)).toEqual([0, 1]);
|
|
1641
1805
|
});
|
|
1642
|
-
|
|
1806
|
+
it4("preserves Date `created` verbatim", async () => {
|
|
1643
1807
|
const s = `restore-isoc-${uid()}`;
|
|
1644
1808
|
const iso = "2021-07-15T12:34:56.789Z";
|
|
1645
1809
|
await restore(
|
|
1646
|
-
|
|
1810
|
+
as_source([
|
|
1647
1811
|
{
|
|
1648
1812
|
id: 1,
|
|
1649
1813
|
stream: s,
|
|
@@ -1662,53 +1826,53 @@ var runStoreTck = (options) => {
|
|
|
1662
1826
|
},
|
|
1663
1827
|
{ stream: s, stream_exact: true }
|
|
1664
1828
|
);
|
|
1665
|
-
|
|
1666
|
-
|
|
1829
|
+
expect4(back).toHaveLength(1);
|
|
1830
|
+
expect4(back[0].created.toISOString()).toBe(iso);
|
|
1667
1831
|
});
|
|
1668
|
-
|
|
1832
|
+
it4("wipes pre-existing events before inserting", async () => {
|
|
1669
1833
|
const old = `restore-old-${uid()}`;
|
|
1670
1834
|
await store.commit(
|
|
1671
1835
|
old,
|
|
1672
1836
|
[inc(1), inc(2)],
|
|
1673
|
-
|
|
1837
|
+
make_meta({ stream: old })
|
|
1674
1838
|
);
|
|
1675
1839
|
const fresh = `restore-fresh-${uid()}`;
|
|
1676
1840
|
const t = /* @__PURE__ */ new Date("2020-01-01T00:00:00.000Z");
|
|
1677
1841
|
await restore(
|
|
1678
|
-
|
|
1842
|
+
as_source([event(1, fresh, 0, "Incremented", t, { amount: 99 })])
|
|
1679
1843
|
);
|
|
1680
|
-
const
|
|
1844
|
+
const old_back = await collect(store, {
|
|
1681
1845
|
stream: old,
|
|
1682
1846
|
stream_exact: true
|
|
1683
1847
|
});
|
|
1684
|
-
|
|
1685
|
-
const
|
|
1848
|
+
expect4(old_back).toHaveLength(0);
|
|
1849
|
+
const fresh_back = await collect(store, {
|
|
1686
1850
|
stream: fresh,
|
|
1687
1851
|
stream_exact: true
|
|
1688
1852
|
});
|
|
1689
|
-
|
|
1853
|
+
expect4(fresh_back).toHaveLength(1);
|
|
1690
1854
|
});
|
|
1691
|
-
|
|
1855
|
+
it4("clears subscription/stream-position metadata", async () => {
|
|
1692
1856
|
const sub = `restore-sub-${uid()}`;
|
|
1693
1857
|
await store.subscribe([{ stream: sub, source: "anything" }]);
|
|
1694
|
-
const
|
|
1858
|
+
const collect_streams = async () => {
|
|
1695
1859
|
const out = [];
|
|
1696
1860
|
await store.query_streams((p) => {
|
|
1697
1861
|
out.push(p.stream);
|
|
1698
1862
|
});
|
|
1699
1863
|
return out;
|
|
1700
1864
|
};
|
|
1701
|
-
const before = await
|
|
1702
|
-
|
|
1703
|
-
await restore(
|
|
1704
|
-
const after = await
|
|
1705
|
-
|
|
1865
|
+
const before = await collect_streams();
|
|
1866
|
+
expect4(before.includes(sub)).toBe(true);
|
|
1867
|
+
await restore(as_source([]));
|
|
1868
|
+
const after = await collect_streams();
|
|
1869
|
+
expect4(after.includes(sub)).toBe(false);
|
|
1706
1870
|
});
|
|
1707
|
-
|
|
1871
|
+
it4("preserves snapshot events through restore", async () => {
|
|
1708
1872
|
const s = `restore-snap-${uid()}`;
|
|
1709
1873
|
const t = /* @__PURE__ */ new Date("2020-04-01T00:00:00.000Z");
|
|
1710
1874
|
await restore(
|
|
1711
|
-
|
|
1875
|
+
as_source([
|
|
1712
1876
|
{
|
|
1713
1877
|
id: 1,
|
|
1714
1878
|
stream: s,
|
|
@@ -1725,10 +1889,10 @@ var runStoreTck = (options) => {
|
|
|
1725
1889
|
stream_exact: true,
|
|
1726
1890
|
with_snaps: true
|
|
1727
1891
|
});
|
|
1728
|
-
|
|
1729
|
-
|
|
1892
|
+
expect4(back).toHaveLength(1);
|
|
1893
|
+
expect4(back[0].name).toBe(SNAP_EVENT);
|
|
1730
1894
|
});
|
|
1731
|
-
|
|
1895
|
+
it4("rewrites causation refs through the old\u2192new id map", async () => {
|
|
1732
1896
|
const s = `restore-caus-${uid()}`;
|
|
1733
1897
|
const t = /* @__PURE__ */ new Date("2020-08-01T00:00:00.000Z");
|
|
1734
1898
|
const events = [
|
|
@@ -1770,7 +1934,7 @@ var runStoreTck = (options) => {
|
|
|
1770
1934
|
}
|
|
1771
1935
|
}
|
|
1772
1936
|
];
|
|
1773
|
-
await restore(
|
|
1937
|
+
await restore(as_source(events));
|
|
1774
1938
|
const back = [];
|
|
1775
1939
|
await store.query(
|
|
1776
1940
|
(e) => {
|
|
@@ -1778,16 +1942,16 @@ var runStoreTck = (options) => {
|
|
|
1778
1942
|
},
|
|
1779
1943
|
{ stream: s, stream_exact: true }
|
|
1780
1944
|
);
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1945
|
+
expect4(back).toHaveLength(3);
|
|
1946
|
+
expect4(back[0].meta.causation.event).toBeUndefined();
|
|
1947
|
+
expect4(back[1].meta.causation.event?.id).toBe(back[0].id);
|
|
1948
|
+
expect4(back[2].meta.causation.event?.id).toBe(back[1].id);
|
|
1785
1949
|
});
|
|
1786
|
-
|
|
1950
|
+
it4("leaves causation refs unmapped when the target isn't in the source", async () => {
|
|
1787
1951
|
const s = `restore-orphan-${uid()}`;
|
|
1788
1952
|
const t = /* @__PURE__ */ new Date("2020-09-01T00:00:00.000Z");
|
|
1789
1953
|
await restore(
|
|
1790
|
-
|
|
1954
|
+
as_source([
|
|
1791
1955
|
{
|
|
1792
1956
|
id: 1,
|
|
1793
1957
|
stream: s,
|
|
@@ -1811,14 +1975,14 @@ var runStoreTck = (options) => {
|
|
|
1811
1975
|
},
|
|
1812
1976
|
{ stream: s, stream_exact: true }
|
|
1813
1977
|
);
|
|
1814
|
-
|
|
1978
|
+
expect4(back[0].meta.causation.event?.id).toBe(999);
|
|
1815
1979
|
});
|
|
1816
|
-
|
|
1980
|
+
it4("rolls back atomically when the source throws mid-iteration", async () => {
|
|
1817
1981
|
const original = `restore-pre-${uid()}`;
|
|
1818
1982
|
const committed = await store.commit(
|
|
1819
1983
|
original,
|
|
1820
1984
|
[inc(1), inc(2), inc(3)],
|
|
1821
|
-
|
|
1985
|
+
make_meta({ stream: original })
|
|
1822
1986
|
);
|
|
1823
1987
|
const explosive = {
|
|
1824
1988
|
async query(callback) {
|
|
@@ -1839,7 +2003,7 @@ var runStoreTck = (options) => {
|
|
|
1839
2003
|
async dispose() {
|
|
1840
2004
|
}
|
|
1841
2005
|
};
|
|
1842
|
-
await
|
|
2006
|
+
await expect4(restore(explosive)).rejects.toThrow("boom");
|
|
1843
2007
|
const back = [];
|
|
1844
2008
|
await store.query(
|
|
1845
2009
|
(e) => {
|
|
@@ -1847,14 +2011,14 @@ var runStoreTck = (options) => {
|
|
|
1847
2011
|
},
|
|
1848
2012
|
{ stream: original, stream_exact: true }
|
|
1849
2013
|
);
|
|
1850
|
-
|
|
1851
|
-
|
|
2014
|
+
expect4(back).toHaveLength(3);
|
|
2015
|
+
expect4(back.map((e) => e.id)).toEqual(committed.map((c) => c.id));
|
|
1852
2016
|
});
|
|
1853
|
-
|
|
2017
|
+
it4("drop_snapshots: skips SNAP_EVENT rows and counts them", async () => {
|
|
1854
2018
|
const s = `restore-drop-snap-${uid()}`;
|
|
1855
2019
|
const t = /* @__PURE__ */ new Date("2020-10-01T00:00:00.000Z");
|
|
1856
2020
|
const result = await restore(
|
|
1857
|
-
|
|
2021
|
+
as_source([
|
|
1858
2022
|
event(1, s, 0, "Incremented", t, { amount: 1 }),
|
|
1859
2023
|
{
|
|
1860
2024
|
id: 2,
|
|
@@ -1869,34 +2033,34 @@ var runStoreTck = (options) => {
|
|
|
1869
2033
|
]),
|
|
1870
2034
|
{ drop_snapshots: true }
|
|
1871
2035
|
);
|
|
1872
|
-
|
|
1873
|
-
|
|
2036
|
+
expect4(result.kept).toBe(2);
|
|
2037
|
+
expect4(result.dropped.snapshots).toBe(1);
|
|
1874
2038
|
const back = await collect(store, {
|
|
1875
2039
|
stream: s,
|
|
1876
2040
|
stream_exact: true,
|
|
1877
2041
|
with_snaps: true
|
|
1878
2042
|
});
|
|
1879
|
-
|
|
1880
|
-
|
|
2043
|
+
expect4(back).toHaveLength(2);
|
|
2044
|
+
expect4(
|
|
1881
2045
|
back.every((e) => e.name !== SNAP_EVENT)
|
|
1882
2046
|
).toBe(true);
|
|
1883
2047
|
});
|
|
1884
|
-
|
|
2048
|
+
it4("on_progress fires once per event (caller throttles)", async () => {
|
|
1885
2049
|
const calls = [];
|
|
1886
2050
|
const s = `restore-progress-${uid()}`;
|
|
1887
2051
|
const t = /* @__PURE__ */ new Date("2021-02-01T00:00:00.000Z");
|
|
1888
2052
|
await restore(
|
|
1889
|
-
|
|
2053
|
+
as_source([
|
|
1890
2054
|
event(1, s, 0, "Incremented", t, { amount: 1 }),
|
|
1891
2055
|
event(2, s, 1, "Incremented", t, { amount: 2 })
|
|
1892
2056
|
]),
|
|
1893
2057
|
{ on_progress: (p) => calls.push(p.processed) }
|
|
1894
2058
|
);
|
|
1895
|
-
|
|
2059
|
+
expect4(calls).toEqual([1, 2]);
|
|
1896
2060
|
});
|
|
1897
2061
|
});
|
|
1898
|
-
|
|
1899
|
-
|
|
2062
|
+
describe4.skipIf(!caps.pii_isolation)("pii_isolation (capability)", () => {
|
|
2063
|
+
it4("commits and loads pii alongside data", async () => {
|
|
1900
2064
|
const s = `pii-roundtrip-${uid()}`;
|
|
1901
2065
|
const committed = await store.commit(
|
|
1902
2066
|
s,
|
|
@@ -1907,10 +2071,10 @@ var runStoreTck = (options) => {
|
|
|
1907
2071
|
pii: { email: "u@example.com", name: "Ursula" }
|
|
1908
2072
|
}
|
|
1909
2073
|
],
|
|
1910
|
-
|
|
2074
|
+
make_meta({ stream: s })
|
|
1911
2075
|
);
|
|
1912
|
-
|
|
1913
|
-
|
|
2076
|
+
expect4(committed).toHaveLength(1);
|
|
2077
|
+
expect4(committed[0].pii).toEqual({
|
|
1914
2078
|
email: "u@example.com",
|
|
1915
2079
|
name: "Ursula"
|
|
1916
2080
|
});
|
|
@@ -1921,13 +2085,17 @@ var runStoreTck = (options) => {
|
|
|
1921
2085
|
},
|
|
1922
2086
|
{ stream: s, stream_exact: true }
|
|
1923
2087
|
);
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
2088
|
+
expect4(seen).toHaveLength(1);
|
|
2089
|
+
expect4(seen[0].pii).toEqual({ email: "u@example.com", name: "Ursula" });
|
|
2090
|
+
expect4(seen[0].data).toEqual({ amount: 1 });
|
|
1927
2091
|
});
|
|
1928
|
-
|
|
2092
|
+
it4("passes through events without pii (pii is null or undefined on load)", async () => {
|
|
1929
2093
|
const s = `pii-none-${uid()}`;
|
|
1930
|
-
await store.commit(
|
|
2094
|
+
await store.commit(
|
|
2095
|
+
s,
|
|
2096
|
+
[inc(1)],
|
|
2097
|
+
make_meta({ stream: s })
|
|
2098
|
+
);
|
|
1931
2099
|
const seen = [];
|
|
1932
2100
|
await store.query(
|
|
1933
2101
|
(e) => {
|
|
@@ -1935,10 +2103,10 @@ var runStoreTck = (options) => {
|
|
|
1935
2103
|
},
|
|
1936
2104
|
{ stream: s, stream_exact: true }
|
|
1937
2105
|
);
|
|
1938
|
-
|
|
1939
|
-
|
|
2106
|
+
expect4(seen).toHaveLength(1);
|
|
2107
|
+
expect4(seen[0].pii == null).toBe(true);
|
|
1940
2108
|
});
|
|
1941
|
-
|
|
2109
|
+
it4("wipes pii for every event on the stream via forget_pii", async () => {
|
|
1942
2110
|
const s = `pii-forget-${uid()}`;
|
|
1943
2111
|
await store.commit(
|
|
1944
2112
|
s,
|
|
@@ -1954,12 +2122,12 @@ var runStoreTck = (options) => {
|
|
|
1954
2122
|
pii: { email: "b@example.com" }
|
|
1955
2123
|
}
|
|
1956
2124
|
],
|
|
1957
|
-
|
|
2125
|
+
make_meta({ stream: s })
|
|
1958
2126
|
);
|
|
1959
2127
|
const forget = store.forget_pii;
|
|
1960
|
-
|
|
2128
|
+
expect4(forget).toBeDefined();
|
|
1961
2129
|
const wiped = await forget.call(store, s);
|
|
1962
|
-
|
|
2130
|
+
expect4(wiped).toBe(2);
|
|
1963
2131
|
const seen = [];
|
|
1964
2132
|
await store.query(
|
|
1965
2133
|
(e) => {
|
|
@@ -1967,13 +2135,13 @@ var runStoreTck = (options) => {
|
|
|
1967
2135
|
},
|
|
1968
2136
|
{ stream: s, stream_exact: true }
|
|
1969
2137
|
);
|
|
1970
|
-
|
|
2138
|
+
expect4(seen).toHaveLength(2);
|
|
1971
2139
|
for (const e of seen) {
|
|
1972
|
-
|
|
1973
|
-
|
|
2140
|
+
expect4(e.pii == null).toBe(true);
|
|
2141
|
+
expect4(e.data).toBeDefined();
|
|
1974
2142
|
}
|
|
1975
2143
|
});
|
|
1976
|
-
|
|
2144
|
+
it4("is idempotent \u2014 second forget_pii returns 0, no error", async () => {
|
|
1977
2145
|
const s = `pii-forget-idem-${uid()}`;
|
|
1978
2146
|
await store.commit(
|
|
1979
2147
|
s,
|
|
@@ -1984,15 +2152,15 @@ var runStoreTck = (options) => {
|
|
|
1984
2152
|
pii: { email: "u@example.com" }
|
|
1985
2153
|
}
|
|
1986
2154
|
],
|
|
1987
|
-
|
|
2155
|
+
make_meta({ stream: s })
|
|
1988
2156
|
);
|
|
1989
2157
|
const forget = store.forget_pii;
|
|
1990
2158
|
const first = await forget.call(store, s);
|
|
1991
|
-
|
|
2159
|
+
expect4(first).toBe(1);
|
|
1992
2160
|
const second = await forget.call(store, s);
|
|
1993
|
-
|
|
2161
|
+
expect4(second).toBe(0);
|
|
1994
2162
|
});
|
|
1995
|
-
|
|
2163
|
+
it4("only wipes the targeted stream \u2014 siblings untouched", async () => {
|
|
1996
2164
|
const sA = `pii-iso-a-${uid()}`;
|
|
1997
2165
|
const sB = `pii-iso-b-${uid()}`;
|
|
1998
2166
|
await store.commit(
|
|
@@ -2004,7 +2172,7 @@ var runStoreTck = (options) => {
|
|
|
2004
2172
|
pii: { email: "alice@example.com" }
|
|
2005
2173
|
}
|
|
2006
2174
|
],
|
|
2007
|
-
|
|
2175
|
+
make_meta({ stream: sA })
|
|
2008
2176
|
);
|
|
2009
2177
|
await store.commit(
|
|
2010
2178
|
sB,
|
|
@@ -2015,7 +2183,7 @@ var runStoreTck = (options) => {
|
|
|
2015
2183
|
pii: { email: "bob@example.com" }
|
|
2016
2184
|
}
|
|
2017
2185
|
],
|
|
2018
|
-
|
|
2186
|
+
make_meta({ stream: sB })
|
|
2019
2187
|
);
|
|
2020
2188
|
await store.forget_pii.call(store, sA);
|
|
2021
2189
|
const a = [];
|
|
@@ -2025,7 +2193,7 @@ var runStoreTck = (options) => {
|
|
|
2025
2193
|
},
|
|
2026
2194
|
{ stream: sA, stream_exact: true }
|
|
2027
2195
|
);
|
|
2028
|
-
|
|
2196
|
+
expect4(a[0].pii == null).toBe(true);
|
|
2029
2197
|
const b = [];
|
|
2030
2198
|
await store.query(
|
|
2031
2199
|
(e) => {
|
|
@@ -2033,28 +2201,32 @@ var runStoreTck = (options) => {
|
|
|
2033
2201
|
},
|
|
2034
2202
|
{ stream: sB, stream_exact: true }
|
|
2035
2203
|
);
|
|
2036
|
-
|
|
2204
|
+
expect4(b[0].pii).toEqual({ email: "bob@example.com" });
|
|
2037
2205
|
});
|
|
2038
|
-
|
|
2206
|
+
it4("forget_pii on a stream with no pii events returns 0", async () => {
|
|
2039
2207
|
const s = `pii-forget-empty-${uid()}`;
|
|
2040
|
-
await store.commit(
|
|
2208
|
+
await store.commit(
|
|
2209
|
+
s,
|
|
2210
|
+
[inc(1)],
|
|
2211
|
+
make_meta({ stream: s })
|
|
2212
|
+
);
|
|
2041
2213
|
const wiped = await store.forget_pii.call(store, s);
|
|
2042
|
-
|
|
2214
|
+
expect4(wiped).toBe(0);
|
|
2043
2215
|
});
|
|
2044
2216
|
});
|
|
2045
2217
|
if (caps.notify) {
|
|
2046
|
-
|
|
2047
|
-
|
|
2218
|
+
describe4("notify (capability)", () => {
|
|
2219
|
+
it4("delivers a notification when a different instance commits", async () => {
|
|
2048
2220
|
const notify = store.notify;
|
|
2049
|
-
|
|
2221
|
+
expect4(notify).toBeDefined();
|
|
2050
2222
|
const received = [];
|
|
2051
|
-
let
|
|
2223
|
+
let resolve_arrived;
|
|
2052
2224
|
const arrived = new Promise((res) => {
|
|
2053
|
-
|
|
2225
|
+
resolve_arrived = res;
|
|
2054
2226
|
});
|
|
2055
2227
|
const disposer = await notify.call(store, (n) => {
|
|
2056
2228
|
received.push(n);
|
|
2057
|
-
|
|
2229
|
+
resolve_arrived();
|
|
2058
2230
|
});
|
|
2059
2231
|
const writer = await options.factory();
|
|
2060
2232
|
try {
|
|
@@ -2062,12 +2234,12 @@ var runStoreTck = (options) => {
|
|
|
2062
2234
|
await writer.commit(
|
|
2063
2235
|
stream,
|
|
2064
2236
|
[inc(1)],
|
|
2065
|
-
|
|
2237
|
+
make_meta({ stream })
|
|
2066
2238
|
);
|
|
2067
2239
|
await arrived;
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2240
|
+
expect4(received.length).toBeGreaterThanOrEqual(1);
|
|
2241
|
+
expect4(received[0].stream).toBe(stream);
|
|
2242
|
+
expect4(received[0].events.length).toBeGreaterThanOrEqual(1);
|
|
2071
2243
|
} finally {
|
|
2072
2244
|
await writer.dispose();
|
|
2073
2245
|
await Promise.resolve(disposer());
|
|
@@ -2087,12 +2259,11 @@ export {
|
|
|
2087
2259
|
collect,
|
|
2088
2260
|
dec,
|
|
2089
2261
|
inc,
|
|
2090
|
-
makeMeta,
|
|
2091
2262
|
reset,
|
|
2092
2263
|
runCacheTck,
|
|
2093
2264
|
runLoggerTck,
|
|
2265
|
+
runStabilityTck,
|
|
2094
2266
|
runStoreTck,
|
|
2095
|
-
seedStream,
|
|
2096
2267
|
uid
|
|
2097
2268
|
};
|
|
2098
2269
|
//# sourceMappingURL=index.js.map
|