@rotorsoft/act-tck 1.38.20 → 1.38.22
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/dist/.tsbuildinfo +1 -1
- package/dist/@types/store-tck.d.ts.map +1 -1
- package/dist/index.cjs +187 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +188 -3
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -135,9 +135,9 @@ var runCacheDifferentialTck = (options) => {
|
|
|
135
135
|
|
|
136
136
|
// src/cache-tck.ts
|
|
137
137
|
import { afterEach, beforeEach as beforeEach2, describe as describe2, expect as expect2, it as it2 } from "vitest";
|
|
138
|
-
var entry = (event_id,
|
|
138
|
+
var entry = (event_id, state2 = {}, stream = `s-${event_id}`) => ({
|
|
139
139
|
stream,
|
|
140
|
-
state,
|
|
140
|
+
state: state2,
|
|
141
141
|
version: event_id,
|
|
142
142
|
event_id,
|
|
143
143
|
patches: 1,
|
|
@@ -363,7 +363,7 @@ async function load_surface(entry2) {
|
|
|
363
363
|
if (visited.has(abs)) return;
|
|
364
364
|
const content = await fs.readFile(abs, "utf8");
|
|
365
365
|
visited.set(abs, content);
|
|
366
|
-
const re_export = /^\s*
|
|
366
|
+
const re_export = /^\s*export\b[^"';]*\bfrom\s+["'](\.[^"']+)["']/gm;
|
|
367
367
|
for (const m of content.matchAll(re_export)) {
|
|
368
368
|
const rel = m[1].replace(/\.js$/, ".ts");
|
|
369
369
|
const target = path.resolve(path.dirname(abs), rel);
|
|
@@ -1113,10 +1113,12 @@ import {
|
|
|
1113
1113
|
InMemoryCache,
|
|
1114
1114
|
SNAP_EVENT as SNAP_EVENT2,
|
|
1115
1115
|
sleep,
|
|
1116
|
+
state,
|
|
1116
1117
|
TOMBSTONE_EVENT,
|
|
1117
1118
|
ValidationError
|
|
1118
1119
|
} from "@rotorsoft/act";
|
|
1119
1120
|
import { afterAll as afterAll4, beforeAll as beforeAll4, describe as describe8, expect as expect8, it as it7, vi } from "vitest";
|
|
1121
|
+
import { z as z2 } from "zod";
|
|
1120
1122
|
var runStoreTck = (options) => {
|
|
1121
1123
|
describe8(`TCK / Store / ${options.name}`, () => {
|
|
1122
1124
|
let store;
|
|
@@ -3218,6 +3220,106 @@ var runStoreTck = (options) => {
|
|
|
3218
3220
|
expect8(got1.priority).toBe(3);
|
|
3219
3221
|
expect8(got2.priority).toBe(5);
|
|
3220
3222
|
});
|
|
3223
|
+
it7("matches a stream pattern", async () => {
|
|
3224
|
+
const tag = uid();
|
|
3225
|
+
await store.subscribe([
|
|
3226
|
+
{ stream: `pri-${tag}-proj-a` },
|
|
3227
|
+
{ stream: `pri-${tag}-proj-b` },
|
|
3228
|
+
{ stream: `pri-${tag}-audit-x` }
|
|
3229
|
+
]);
|
|
3230
|
+
expect8(
|
|
3231
|
+
await store.prioritize({ stream: `^pri-${tag}-proj-.*$` }, 3)
|
|
3232
|
+
).toBe(2);
|
|
3233
|
+
});
|
|
3234
|
+
it7("matches by source, skipping rows that have none", async () => {
|
|
3235
|
+
const tag = uid();
|
|
3236
|
+
await store.subscribe([
|
|
3237
|
+
{ stream: `pri-${tag}-t1`, source: `pri-src-${tag}-users` },
|
|
3238
|
+
{ stream: `pri-${tag}-t2`, source: `pri-src-${tag}-audit` },
|
|
3239
|
+
{ stream: `pri-${tag}-t3` }
|
|
3240
|
+
]);
|
|
3241
|
+
expect8(
|
|
3242
|
+
await store.prioritize(
|
|
3243
|
+
{ source: `pri-src-${tag}-users`, source_exact: true },
|
|
3244
|
+
4
|
|
3245
|
+
)
|
|
3246
|
+
).toBe(1);
|
|
3247
|
+
expect8(
|
|
3248
|
+
await store.prioritize({ source: `^pri-src-${tag}-.*$` }, 6)
|
|
3249
|
+
).toBe(2);
|
|
3250
|
+
});
|
|
3251
|
+
it7("matches by blocked state, both ways", async () => {
|
|
3252
|
+
const fresh = await options.factory();
|
|
3253
|
+
try {
|
|
3254
|
+
await fresh.drop();
|
|
3255
|
+
await fresh.seed();
|
|
3256
|
+
const tag = uid();
|
|
3257
|
+
const src = `pri-src-${tag}`;
|
|
3258
|
+
const ok = `pri-${tag}-ok`;
|
|
3259
|
+
const bad = `pri-${tag}-bad`;
|
|
3260
|
+
await fresh.commit(
|
|
3261
|
+
src,
|
|
3262
|
+
[inc(1)],
|
|
3263
|
+
make_meta({ stream: src })
|
|
3264
|
+
);
|
|
3265
|
+
await fresh.subscribe([
|
|
3266
|
+
{ stream: ok, source: src },
|
|
3267
|
+
{ stream: bad, source: src }
|
|
3268
|
+
]);
|
|
3269
|
+
await correlate(fresh);
|
|
3270
|
+
const leases = await fresh.claim(2, 0, `w-${uid()}`, 5e3);
|
|
3271
|
+
const lease = leases.find((l) => l.stream === bad);
|
|
3272
|
+
await fresh.block([{ ...lease, error: "boom" }]);
|
|
3273
|
+
const scope = `^pri-${tag}-.*$`;
|
|
3274
|
+
expect8(
|
|
3275
|
+
await fresh.prioritize({ stream: scope, blocked: true }, 9)
|
|
3276
|
+
).toBe(1);
|
|
3277
|
+
expect8(
|
|
3278
|
+
await fresh.prioritize({ stream: scope, blocked: false }, 9)
|
|
3279
|
+
).toBe(1);
|
|
3280
|
+
} finally {
|
|
3281
|
+
await fresh.dispose();
|
|
3282
|
+
}
|
|
3283
|
+
});
|
|
3284
|
+
it7("an empty filter updates every row, and skips rows already at the value", async () => {
|
|
3285
|
+
const fresh = await options.factory();
|
|
3286
|
+
try {
|
|
3287
|
+
await fresh.drop();
|
|
3288
|
+
await fresh.seed();
|
|
3289
|
+
const tag = uid();
|
|
3290
|
+
await fresh.subscribe([
|
|
3291
|
+
{ stream: `pri-${tag}-a` },
|
|
3292
|
+
{ stream: `pri-${tag}-b`, priority: 5 }
|
|
3293
|
+
]);
|
|
3294
|
+
expect8(await fresh.prioritize({}, 5)).toBe(1);
|
|
3295
|
+
expect8(await fresh.prioritize({}, 5)).toBe(0);
|
|
3296
|
+
} finally {
|
|
3297
|
+
await fresh.dispose();
|
|
3298
|
+
}
|
|
3299
|
+
});
|
|
3300
|
+
it7("claim returns higher-priority streams first under tied watermarks", async () => {
|
|
3301
|
+
const fresh = await options.factory();
|
|
3302
|
+
try {
|
|
3303
|
+
await fresh.drop();
|
|
3304
|
+
await fresh.seed();
|
|
3305
|
+
const tag = uid();
|
|
3306
|
+
const src = `pri-src-${tag}`;
|
|
3307
|
+
await fresh.commit(
|
|
3308
|
+
src,
|
|
3309
|
+
[inc(1), inc(2)],
|
|
3310
|
+
make_meta({ stream: src })
|
|
3311
|
+
);
|
|
3312
|
+
await fresh.subscribe([
|
|
3313
|
+
{ stream: `pri-${tag}-low`, source: src, priority: 0 },
|
|
3314
|
+
{ stream: `pri-${tag}-high`, source: src, priority: 5 }
|
|
3315
|
+
]);
|
|
3316
|
+
await correlate(fresh);
|
|
3317
|
+
const leases = await fresh.claim(1, 0, `w-${uid()}`, 5e3);
|
|
3318
|
+
expect8(leases.map((l) => l.stream)).toEqual([`pri-${tag}-high`]);
|
|
3319
|
+
} finally {
|
|
3320
|
+
await fresh.dispose();
|
|
3321
|
+
}
|
|
3322
|
+
});
|
|
3221
3323
|
});
|
|
3222
3324
|
describe8("lanes", () => {
|
|
3223
3325
|
it7("subscribe defaults lane to 'default' when omitted", async () => {
|
|
@@ -4258,6 +4360,43 @@ var runStoreTck = (options) => {
|
|
|
4258
4360
|
});
|
|
4259
4361
|
expect8([...all.keys()].sort()).toEqual([...streams].sort());
|
|
4260
4362
|
});
|
|
4363
|
+
it7("pages the count and tail paths by limit + after too", async () => {
|
|
4364
|
+
const tag = uid();
|
|
4365
|
+
for (const s of [`qsc-${tag}-c`, `qsc-${tag}-a`, `qsc-${tag}-b`])
|
|
4366
|
+
await store.commit(
|
|
4367
|
+
s,
|
|
4368
|
+
[inc(1), inc(2)],
|
|
4369
|
+
make_meta({ stream: s })
|
|
4370
|
+
);
|
|
4371
|
+
const pattern = { stream: `qsc-${tag}-.*` };
|
|
4372
|
+
const counted = await store.query_stats(pattern, {
|
|
4373
|
+
count: true,
|
|
4374
|
+
names: true,
|
|
4375
|
+
limit: 2
|
|
4376
|
+
});
|
|
4377
|
+
expect8([...counted.keys()]).toEqual([`qsc-${tag}-a`, `qsc-${tag}-b`]);
|
|
4378
|
+
expect8(counted.get(`qsc-${tag}-a`)?.count).toBe(2);
|
|
4379
|
+
expect8(counted.get(`qsc-${tag}-a`)?.names).toEqual({ Incremented: 2 });
|
|
4380
|
+
const counted2 = await store.query_stats(pattern, {
|
|
4381
|
+
count: true,
|
|
4382
|
+
limit: 2,
|
|
4383
|
+
after: `qsc-${tag}-b`
|
|
4384
|
+
});
|
|
4385
|
+
expect8([...counted2.keys()]).toEqual([`qsc-${tag}-c`]);
|
|
4386
|
+
const tailed = await store.query_stats(pattern, {
|
|
4387
|
+
tail: true,
|
|
4388
|
+
limit: 2
|
|
4389
|
+
});
|
|
4390
|
+
expect8([...tailed.keys()]).toEqual([`qsc-${tag}-a`, `qsc-${tag}-b`]);
|
|
4391
|
+
expect8(tailed.get(`qsc-${tag}-a`)?.head.version).toBe(1);
|
|
4392
|
+
expect8(tailed.get(`qsc-${tag}-a`)?.tail?.version).toBe(0);
|
|
4393
|
+
const tailed2 = await store.query_stats(pattern, {
|
|
4394
|
+
tail: true,
|
|
4395
|
+
limit: 2,
|
|
4396
|
+
after: `qsc-${tag}-b`
|
|
4397
|
+
});
|
|
4398
|
+
expect8([...tailed2.keys()]).toEqual([`qsc-${tag}-c`]);
|
|
4399
|
+
});
|
|
4261
4400
|
});
|
|
4262
4401
|
describe8.skipIf(!caps.source_matches)(
|
|
4263
4402
|
"query_streams source_matches (capability)",
|
|
@@ -4388,6 +4527,52 @@ var runStoreTck = (options) => {
|
|
|
4388
4527
|
expect8(positions).toEqual([s]);
|
|
4389
4528
|
});
|
|
4390
4529
|
});
|
|
4530
|
+
describe8("autoclose end-to-end", () => {
|
|
4531
|
+
const Ticket = state({ TckTicket: z2.object({ open: z2.boolean() }) }).init(() => ({ open: false })).emits({
|
|
4532
|
+
TckTicketOpened: z2.object({ title: z2.string() }),
|
|
4533
|
+
TckTicketResolved: z2.object({})
|
|
4534
|
+
}).patch({
|
|
4535
|
+
TckTicketOpened: () => ({ open: true }),
|
|
4536
|
+
TckTicketResolved: () => ({ open: false })
|
|
4537
|
+
}).on({ OpenTckTicket: z2.object({ title: z2.string() }) }).emit((a) => ["TckTicketOpened", { title: a.title }]).on({ ResolveTckTicket: z2.object({}) }).emit(() => ["TckTicketResolved", {}]).autocloses({ is: "TckTicketResolved" }).build();
|
|
4538
|
+
const actor2 = { id: "tck", name: "tck" };
|
|
4539
|
+
const run = async (resolve) => {
|
|
4540
|
+
const fresh = await options.factory();
|
|
4541
|
+
await fresh.drop();
|
|
4542
|
+
await fresh.seed();
|
|
4543
|
+
const cache = new InMemoryCache();
|
|
4544
|
+
const app = act().withState(Ticket).build({ scoped: { store: fresh, cache } });
|
|
4545
|
+
const truncated = [];
|
|
4546
|
+
app.on("closed", (r) => truncated.push(...r.truncated.keys()));
|
|
4547
|
+
const stream = `ac-${uid()}`;
|
|
4548
|
+
try {
|
|
4549
|
+
await app.do("OpenTckTicket", { stream, actor: actor2 }, { title: "a" });
|
|
4550
|
+
if (resolve) await app.do("ResolveTckTicket", { stream, actor: actor2 }, {});
|
|
4551
|
+
await app.correlate();
|
|
4552
|
+
await app.drain();
|
|
4553
|
+
const surviving = [];
|
|
4554
|
+
await fresh.query((e) => surviving.push(String(e.name)), {
|
|
4555
|
+
stream,
|
|
4556
|
+
stream_exact: true
|
|
4557
|
+
});
|
|
4558
|
+
return { stream, truncated, surviving };
|
|
4559
|
+
} finally {
|
|
4560
|
+
await app.shutdown();
|
|
4561
|
+
await cache.dispose();
|
|
4562
|
+
await fresh.dispose();
|
|
4563
|
+
}
|
|
4564
|
+
};
|
|
4565
|
+
it7("closes a stream whose head is the terminal event, leaving a tombstone", async () => {
|
|
4566
|
+
const { stream, truncated, surviving } = await run(true);
|
|
4567
|
+
expect8(truncated).toContain(stream);
|
|
4568
|
+
expect8(surviving).toEqual([TOMBSTONE_EVENT]);
|
|
4569
|
+
});
|
|
4570
|
+
it7("leaves a stream whose head is not the terminal event", async () => {
|
|
4571
|
+
const { stream, truncated, surviving } = await run(false);
|
|
4572
|
+
expect8(truncated).not.toContain(stream);
|
|
4573
|
+
expect8(surviving).toEqual(["TckTicketOpened"]);
|
|
4574
|
+
});
|
|
4575
|
+
});
|
|
4391
4576
|
describe8("seed_stream helper coverage", () => {
|
|
4392
4577
|
it7("commits N events with monotonically increasing ids", async () => {
|
|
4393
4578
|
const s = `seed-${uid()}`;
|