@voltro/testing 0.65.0 → 0.66.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +94 -0
- package/dist/dialect.js +941 -235
- package/dist/index.d.ts +10 -0
- package/dist/index.js +136 -119
- package/package.json +8 -8
package/dist/index.d.ts
CHANGED
|
@@ -20,6 +20,7 @@ import { RestRouteDescriptor } from '@voltro/protocol/rest';
|
|
|
20
20
|
import { RestServeBindings } from '@voltro/protocol/rest';
|
|
21
21
|
import { Row } from '@voltro/database';
|
|
22
22
|
import { RowFilter } from '@voltro/runtime';
|
|
23
|
+
import { RpcConnection } from '@voltro/runtime';
|
|
23
24
|
import { RpcInterceptor } from '@voltro/protocol';
|
|
24
25
|
import { RpcKind } from '@voltro/protocol';
|
|
25
26
|
import { Schema } from 'effect';
|
|
@@ -351,6 +352,10 @@ export declare interface MakeTestContextOptions<RowFilterCtx = unknown> {
|
|
|
351
352
|
/** The acting subject. Default: anonymous, no tenant. Read it back at
|
|
352
353
|
* `ctx.request.subject` (the same place a live handler reads it). */
|
|
353
354
|
readonly subject?: Subject;
|
|
355
|
+
/** Optional connection fixture, or the actual RuntimeContext.connection from
|
|
356
|
+
* a listener test. Shared by transaction/subject re-scopes, never invented by
|
|
357
|
+
* the harness; omitting it models an internal invocation. */
|
|
358
|
+
readonly connection?: RpcConnection;
|
|
354
359
|
/** Tables to wire into the store's schema. Default: all globally
|
|
355
360
|
* registered tables (whatever the test imported). */
|
|
356
361
|
readonly tables?: ReadonlyArray<TableLike>;
|
|
@@ -930,6 +935,11 @@ export declare const user: (id: string, options?: TestSubjectOptions) => Subject
|
|
|
930
935
|
*/
|
|
931
936
|
export declare const withFrozenTime: <A>(at: Date | number | string | MockClock, body: (clock: MockClock) => A) => A;
|
|
932
937
|
|
|
938
|
+
/** Model another native RPC connection against the SAME test store/services.
|
|
939
|
+
* The connection may come from a real listener. Passing undefined models an
|
|
940
|
+
* internal call; no identity is fabricated or copied from another caller. */
|
|
941
|
+
export declare const withRpcConnection: (ctx: TestContext, connection: RpcConnection | undefined) => TestContext;
|
|
942
|
+
|
|
933
943
|
/** A workflow + its execute function, as the framework pairs them via
|
|
934
944
|
* `workflow.toLayer(execute)`. Structural (no `@effect/workflow` generic
|
|
935
945
|
* signature) so callers can pass the value from `workflow({ name, ... })`
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { AUTO_FILLED_COLUMNS as e, allRegisteredTables as t, changeDelete as n, changeInsert as r, changeSoftDelete as i, changeUpdate as a, clearRelationsRegistry as o, missingRequiredColumns as s, registerRelations as c } from "@voltro/database";
|
|
2
2
|
import { Cause as l, Effect as u, Exit as d, Layer as f, Schema as p } from "effect";
|
|
3
3
|
import { SubjectService as m, anonymousSubject as h, checkGuardsEffect as g, composeAuthStrategies as _, composeRpcInterceptors as v, eventRoute as y, systemSubject as b, tenantScopedSubject as ee } from "@voltro/protocol";
|
|
4
|
-
import { EventBus as
|
|
4
|
+
import { EventBus as x, InMemoryDataStore as te, NO_ROW_FILTER as S, appValidation as ne, applyRowFilterToDescriptor as re, clearSystemStoreHandle as ie, getRowFilter as C, getSystemStoreHandle as ae, makeAppAccess as oe, makeConnectionState as se, makeDataLoader as ce, makeEffectStoreLayer as le, makeEventPublisher as ue, makeOutboxFacade as de, makeSchemaRegistry as fe, publishEvent as pe, resolveRowFilterScopeFor as me, runProvidedEffect as w, runWithDeadlockRetry as he, setSystemStoreHandle as T, wrapStoreWithMixinBehaviour as E } from "@voltro/runtime";
|
|
5
5
|
import { installEnvSnapshot as ge } from "@voltro/env";
|
|
6
6
|
import _e from "node:net";
|
|
7
7
|
import { describe as ve } from "vitest";
|
|
@@ -9,39 +9,39 @@ import { collectPublicApiRoutes as ye, dispatchSharedPath as be, restRoutesToHtt
|
|
|
9
9
|
import { createLogger as Se } from "@voltro/logger";
|
|
10
10
|
import { CurrentWorkflowRunId as Ce, inMemoryWorkflowEngineLayer as we, makeInMemoryRecorder as Te } from "@voltro/workflow";
|
|
11
11
|
//#region src/fixtureRow.ts
|
|
12
|
-
var Ee = new Set(e),
|
|
12
|
+
var Ee = new Set(e), D = 0, O = () => ++D, De = (e, t) => {
|
|
13
13
|
if (t.oneOf && t.oneOf.length > 0) return t.oneOf[0];
|
|
14
14
|
if (t.enumValues && t.enumValues.length > 0) return t.enumValues[0];
|
|
15
15
|
let n = t.unique === !0;
|
|
16
16
|
switch (t.type) {
|
|
17
17
|
case "text":
|
|
18
|
-
case "reference":
|
|
19
18
|
case "id":
|
|
20
|
-
case "enum": return n ? `${e}-${++
|
|
19
|
+
case "enum": return n ? `${e}-${++D}` : e;
|
|
20
|
+
case "reference": return t.referenceStorage === "numeric" ? n ? ++D : 0 : n ? `${e}-${++D}` : e;
|
|
21
21
|
case "integer":
|
|
22
|
-
case "real": return n ? ++
|
|
22
|
+
case "real": return n ? ++D : 0;
|
|
23
23
|
case "decimal":
|
|
24
|
-
case "bigint": return n ? String(++
|
|
24
|
+
case "bigint": return n ? String(++D) : "0";
|
|
25
25
|
case "boolean": return !1;
|
|
26
26
|
case "timestamp":
|
|
27
27
|
case "date": return /* @__PURE__ */ new Date(0);
|
|
28
28
|
default: throw Error(`fixtureRow: column '${e}' is a required '${t.type}' with no default, and fixtureRow can't synthesize a safe placeholder for that type. Pass it explicitly: fixtureRow(table, { ${e}: … }).`);
|
|
29
29
|
}
|
|
30
|
-
},
|
|
30
|
+
}, k = (e, t = {}) => {
|
|
31
31
|
let n = {};
|
|
32
|
-
for (let r of s(e, t)) Ee.has(r) || (n[r] =
|
|
32
|
+
for (let r of s(e, t)) Ee.has(r) || (n[r] = De(r, e.fields[r]));
|
|
33
33
|
return {
|
|
34
34
|
...n,
|
|
35
35
|
...t
|
|
36
36
|
};
|
|
37
|
-
},
|
|
37
|
+
}, Oe = (e) => {
|
|
38
38
|
let t = {};
|
|
39
|
-
for (let [n, r] of Object.entries(e)) t[n] = typeof r == "function" ? r(
|
|
39
|
+
for (let [n, r] of Object.entries(e)) t[n] = typeof r == "function" ? r(O()) : r;
|
|
40
40
|
return t;
|
|
41
|
-
},
|
|
41
|
+
}, ke = (e) => {
|
|
42
42
|
for (let [t, n] of Object.entries(e.fields)) if (n.type === "id") return t;
|
|
43
43
|
return "id";
|
|
44
|
-
},
|
|
44
|
+
}, Ae = async (e, t, n, r, i) => {
|
|
45
45
|
let a = {};
|
|
46
46
|
for (let o of s(t, n)) {
|
|
47
47
|
let n = t.fields[o];
|
|
@@ -49,29 +49,29 @@ var Ee = new Set(e), E = 0, De = () => ++E, Oe = (e, t) => {
|
|
|
49
49
|
let s = n.references();
|
|
50
50
|
if (i.includes(s.tableName)) throw Error(`defineFactory: creating '${t.tableName}' requires '${s.tableName}' via '${o}', and that reference is cyclic (${[...i, s.tableName].join(" → ")}). Insert one side first and pass the id: create(store, { ${o}: existing.id }).`);
|
|
51
51
|
let c = r[o];
|
|
52
|
-
a[o] = (c === void 0 ? await
|
|
52
|
+
a[o] = (c === void 0 ? await A(e, s, {}, {}, [...i, s.tableName]) : await c.create(e, {}))[n.referenceColumn ?? ke(s)];
|
|
53
53
|
}
|
|
54
54
|
return a;
|
|
55
|
-
},
|
|
55
|
+
}, A = async (e, t, n, r, i) => {
|
|
56
56
|
let a = {
|
|
57
|
-
...await
|
|
57
|
+
...await Ae(e, t, n, r, i),
|
|
58
58
|
...n
|
|
59
59
|
};
|
|
60
|
-
return e.insert(t.tableName,
|
|
61
|
-
},
|
|
60
|
+
return e.insert(t.tableName, k(t, a));
|
|
61
|
+
}, j = (e, t) => typeof e == "function" ? e(t) : e ?? {}, je = (e, t = {}) => {
|
|
62
62
|
let n = t.traits ?? {}, r = t.associations ?? {}, i = (t) => {
|
|
63
63
|
let a = (e = {}) => ({
|
|
64
|
-
...
|
|
64
|
+
...Oe(t),
|
|
65
65
|
...e
|
|
66
|
-
}), o = (t = {}) =>
|
|
66
|
+
}), o = (t = {}) => k(e, a(t));
|
|
67
67
|
return {
|
|
68
68
|
table: e,
|
|
69
69
|
build: o,
|
|
70
|
-
buildList: (e, t) => Array.from({ length: e }, (e, n) => o(
|
|
71
|
-
create: (t, n = {}) =>
|
|
70
|
+
buildList: (e, t) => Array.from({ length: e }, (e, n) => o(j(t, n))),
|
|
71
|
+
create: (t, n = {}) => A(t, e, a(n), r, [e.tableName]),
|
|
72
72
|
createList: async (t, n, i) => {
|
|
73
73
|
let o = [];
|
|
74
|
-
for (let s = 0; s < n; s++) o.push(await
|
|
74
|
+
for (let s = 0; s < n; s++) o.push(await A(t, e, a(j(i, s)), r, [e.tableName]));
|
|
75
75
|
return o;
|
|
76
76
|
},
|
|
77
77
|
with: (...r) => {
|
|
@@ -93,19 +93,19 @@ var Ee = new Set(e), E = 0, De = () => ++E, Oe = (e, t) => {
|
|
|
93
93
|
};
|
|
94
94
|
};
|
|
95
95
|
return i(t.defaults ?? {});
|
|
96
|
-
},
|
|
96
|
+
}, M = Symbol.for("voltro/testing/mockClock.installed"), N = () => globalThis, Me = (e, t) => new Proxy(e, {
|
|
97
97
|
construct: (e, n, r) => Reflect.construct(e, n.length === 0 ? [t()] : n, r),
|
|
98
98
|
apply: () => new e(t()).toString(),
|
|
99
99
|
get: (e, n, r) => n === "now" ? () => t() : Reflect.get(e, n, r)
|
|
100
|
-
}),
|
|
100
|
+
}), P = (e) => {
|
|
101
101
|
if (typeof e == "number") return e;
|
|
102
102
|
let t = e instanceof Date ? e.getTime() : Date.parse(e);
|
|
103
103
|
if (Number.isNaN(t)) throw Error(`mockClock: cannot read an instant from '${String(e)}'`);
|
|
104
104
|
return t;
|
|
105
|
-
},
|
|
105
|
+
}, F = class {
|
|
106
106
|
currentMs;
|
|
107
107
|
constructor(e = "2026-01-01T00:00:00Z") {
|
|
108
|
-
this.currentMs =
|
|
108
|
+
this.currentMs = P(e);
|
|
109
109
|
}
|
|
110
110
|
now() {
|
|
111
111
|
return this.currentMs;
|
|
@@ -114,47 +114,47 @@ var Ee = new Set(e), E = 0, De = () => ++E, Oe = (e, t) => {
|
|
|
114
114
|
return new Date(this.currentMs);
|
|
115
115
|
}
|
|
116
116
|
advance(e) {
|
|
117
|
-
this.currentMs += typeof e == "number" ? e :
|
|
117
|
+
this.currentMs += typeof e == "number" ? e : Ie(e);
|
|
118
118
|
}
|
|
119
119
|
set(e) {
|
|
120
|
-
this.currentMs =
|
|
120
|
+
this.currentMs = P(e);
|
|
121
121
|
}
|
|
122
122
|
get installed() {
|
|
123
|
-
return
|
|
123
|
+
return N()[M]?.clock === this;
|
|
124
124
|
}
|
|
125
125
|
install() {
|
|
126
|
-
let e =
|
|
126
|
+
let e = N(), t = e[M];
|
|
127
127
|
if (t !== void 0) throw Error("mockClock: global time is already faked" + (t.clock === this ? " by this same clock" : " by another MockClock") + ". Uninstall it before installing again — a nested install restores the outer fake on the way out, which leaves the realm frozen with nothing pointing at why. `withFrozenTime` always restores, including on a throw.");
|
|
128
128
|
let n = globalThis.Date;
|
|
129
|
-
return e[
|
|
129
|
+
return e[M] = {
|
|
130
130
|
realDate: n,
|
|
131
131
|
clock: this
|
|
132
|
-
}, globalThis.Date =
|
|
132
|
+
}, globalThis.Date = Me(n, () => this.currentMs), () => {
|
|
133
133
|
this.uninstall();
|
|
134
134
|
};
|
|
135
135
|
}
|
|
136
136
|
uninstall() {
|
|
137
|
-
let e =
|
|
138
|
-
t !== void 0 && t.clock === this && (globalThis.Date = t.realDate, delete e[
|
|
137
|
+
let e = N(), t = e[M];
|
|
138
|
+
t !== void 0 && t.clock === this && (globalThis.Date = t.realDate, delete e[M]);
|
|
139
139
|
}
|
|
140
|
-
},
|
|
141
|
-
let n = e instanceof
|
|
140
|
+
}, Ne = (e) => typeof e == "object" && !!e && typeof e.then == "function", Pe = (e, t) => {
|
|
141
|
+
let n = e instanceof F ? e : new F(e);
|
|
142
142
|
n.install();
|
|
143
143
|
let r = !1;
|
|
144
144
|
try {
|
|
145
145
|
let e = t(n);
|
|
146
|
-
return
|
|
146
|
+
return Ne(e) ? (r = !0, e.then((e) => (n.uninstall(), e), (e) => {
|
|
147
147
|
throw n.uninstall(), e;
|
|
148
148
|
})) : e;
|
|
149
149
|
} finally {
|
|
150
150
|
r || n.uninstall();
|
|
151
151
|
}
|
|
152
|
-
},
|
|
153
|
-
let t = e instanceof
|
|
152
|
+
}, Fe = (e) => u.acquireRelease(u.sync(() => {
|
|
153
|
+
let t = e instanceof F ? e : new F(e);
|
|
154
154
|
return t.install(), t;
|
|
155
155
|
}), (e) => u.sync(() => {
|
|
156
156
|
e.uninstall();
|
|
157
|
-
})),
|
|
157
|
+
})), Ie = (e) => {
|
|
158
158
|
let t = /^\s*(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)\s*$/.exec(e);
|
|
159
159
|
if (!t) throw Error(`mockClock: cannot parse duration '${e}'`);
|
|
160
160
|
let n = Number(t[1]), r = t[2];
|
|
@@ -166,7 +166,7 @@ var Ee = new Set(e), E = 0, De = () => ++E, Oe = (e, t) => {
|
|
|
166
166
|
case "d": return n * 864e5;
|
|
167
167
|
default: throw Error(`mockClock: unknown unit '${r}'`);
|
|
168
168
|
}
|
|
169
|
-
},
|
|
169
|
+
}, I = class {
|
|
170
170
|
emitted = [];
|
|
171
171
|
async emit(e, t) {
|
|
172
172
|
let n = typeof e == "string" ? e : e.id ?? e.event ?? "(unnamed event)";
|
|
@@ -185,8 +185,8 @@ var Ee = new Set(e), E = 0, De = () => ++E, Oe = (e, t) => {
|
|
|
185
185
|
clear() {
|
|
186
186
|
this.emitted.length = 0;
|
|
187
187
|
}
|
|
188
|
-
},
|
|
189
|
-
let t = e.tenantId ?? null, n = new
|
|
188
|
+
}, L = (e = {}) => {
|
|
189
|
+
let t = e.tenantId ?? null, n = new x({
|
|
190
190
|
origin: e.origin ?? "test",
|
|
191
191
|
...e.ringSize === void 0 ? {} : { ringSize: e.ringSize }
|
|
192
192
|
});
|
|
@@ -255,7 +255,7 @@ var Ee = new Set(e), E = 0, De = () => ++E, Oe = (e, t) => {
|
|
|
255
255
|
n.clear();
|
|
256
256
|
}
|
|
257
257
|
};
|
|
258
|
-
},
|
|
258
|
+
}, R = class {
|
|
259
259
|
queue;
|
|
260
260
|
calls = [];
|
|
261
261
|
constructor(e) {
|
|
@@ -270,27 +270,31 @@ var Ee = new Set(e), E = 0, De = () => ++E, Oe = (e, t) => {
|
|
|
270
270
|
remaining() {
|
|
271
271
|
return this.queue.length;
|
|
272
272
|
}
|
|
273
|
-
},
|
|
274
|
-
let n = V.get(e)
|
|
273
|
+
}, Le = (e) => e, z = "00000000000000000000000000000000", B = /* @__PURE__ */ new WeakMap(), V = /* @__PURE__ */ new WeakMap(), Re = (e, t) => {
|
|
274
|
+
let n = V.get(e);
|
|
275
|
+
if (n === void 0) throw Error("withRpcConnection requires a context created by makeTestContext");
|
|
276
|
+
return n(t);
|
|
277
|
+
}, H = /* @__PURE__ */ new WeakMap(), U = /* @__PURE__ */ new WeakMap(), W = /* @__PURE__ */ new WeakMap(), G = /* @__PURE__ */ new WeakMap(), K = (e, t) => {
|
|
278
|
+
let n = W.get(e) ?? [], r = t === "mutation" ? "interceptMutation" : t === "query" ? "interceptQuery" : "interceptAction", i = [];
|
|
275
279
|
for (let e of n) {
|
|
276
280
|
let t = e[r];
|
|
277
281
|
typeof t == "function" && i.push(t);
|
|
278
282
|
}
|
|
279
283
|
return v(i);
|
|
280
|
-
},
|
|
281
|
-
let t = (
|
|
284
|
+
}, q = (e) => {
|
|
285
|
+
let t = (W.get(e) ?? []).map((e) => e.services).filter((e) => e !== void 0), n = G.get(e) ?? [], r = [...t, ...n];
|
|
282
286
|
if (r.length !== 0) return f.mergeAll(...r);
|
|
283
|
-
},
|
|
284
|
-
let t =
|
|
287
|
+
}, J = (e) => {
|
|
288
|
+
let t = H.get(e);
|
|
285
289
|
t !== void 0 && (t.length = 0);
|
|
286
|
-
},
|
|
287
|
-
let t =
|
|
290
|
+
}, Y = async (e) => {
|
|
291
|
+
let t = H.get(e);
|
|
288
292
|
if (t === void 0 || t.length === 0) return;
|
|
289
293
|
let n = [...t];
|
|
290
294
|
t.length = 0;
|
|
291
295
|
for (let e of n) await e();
|
|
292
|
-
}, ze = (e) =>
|
|
293
|
-
let n =
|
|
296
|
+
}, ze = (e) => U.get(e) ?? [], X = async (e, t) => {
|
|
297
|
+
let n = B.get(e);
|
|
294
298
|
return n === void 0 ? e.store.transactional(async (n) => t({
|
|
295
299
|
...e,
|
|
296
300
|
store: n
|
|
@@ -363,28 +367,28 @@ var Ee = new Set(e), E = 0, De = () => ++E, Oe = (e, t) => {
|
|
|
363
367
|
}
|
|
364
368
|
};
|
|
365
369
|
}, He = async (e, t) => {
|
|
366
|
-
let n =
|
|
367
|
-
|
|
370
|
+
let n = ae();
|
|
371
|
+
T(e);
|
|
368
372
|
try {
|
|
369
373
|
return await t();
|
|
370
374
|
} finally {
|
|
371
|
-
n === void 0 ?
|
|
375
|
+
n === void 0 ? ie() : T(n);
|
|
372
376
|
}
|
|
373
|
-
},
|
|
374
|
-
|
|
377
|
+
}, Z = /* @__PURE__ */ new Set(), Ue = (e) => {
|
|
378
|
+
Z.has(e) || (Z.add(e), console.warn(e));
|
|
375
379
|
}, We = (e, t, n) => {
|
|
376
380
|
let r = Ge, i;
|
|
377
381
|
return () => {
|
|
378
|
-
let a = t ??
|
|
379
|
-
return t !== void 0 &&
|
|
382
|
+
let a = t ?? C();
|
|
383
|
+
return t !== void 0 && C() !== void 0 && t !== C() && Ue("[voltro:testing] this test passes `rowFilter:` while a DIFFERENT filter is registered via `setRowFilter`. The option wins, so this test does not exercise your app's registration. Drop `rowFilter:` to run the real process-global one."), (i === void 0 || a !== r) && (r = a, i = He(n, () => w(me(a, e, (e) => {
|
|
380
384
|
console.error("[voltro:testing] row filter failed to load — reads refused for this subject", e);
|
|
381
385
|
})))), i;
|
|
382
386
|
};
|
|
383
|
-
}, Ge = Symbol("unresolved"),
|
|
384
|
-
if (n === "query") return async (n) => e.query(
|
|
387
|
+
}, Ge = Symbol("unresolved"), Ke = (e, t) => new Proxy(e, { get: (e, n) => {
|
|
388
|
+
if (n === "query") return async (n) => e.query(re(await t(), n));
|
|
385
389
|
let r = Reflect.get(e, n, e);
|
|
386
390
|
return typeof r == "function" ? r.bind(e) : r;
|
|
387
|
-
} }),
|
|
391
|
+
} }), qe = (e = {}) => {
|
|
388
392
|
if (ge({
|
|
389
393
|
...process.env,
|
|
390
394
|
...e.env ?? {}
|
|
@@ -392,78 +396,90 @@ var Ee = new Set(e), E = 0, De = () => ++E, Oe = (e, t) => {
|
|
|
392
396
|
o();
|
|
393
397
|
for (let t of e.relations) c(t);
|
|
394
398
|
}
|
|
395
|
-
let n = e.tables ?? t(), r = fe(n), i = new
|
|
399
|
+
let n = e.tables ?? t(), r = fe(n), i = new te(e.store ?? {}), a = {
|
|
396
400
|
dataStore: i,
|
|
397
401
|
schemaRegistry: r
|
|
398
402
|
};
|
|
399
|
-
|
|
400
|
-
let s = new
|
|
401
|
-
let
|
|
403
|
+
T(a);
|
|
404
|
+
let s = new F(e.clockStart), l = new I(), u = L(), d = new R(e.llmResponses ?? []), f = Be(() => s.now()), p = Ve(() => s.now()), m = (t, n, o = i, c) => {
|
|
405
|
+
let h = {
|
|
402
406
|
subject: t,
|
|
403
|
-
traceId:
|
|
404
|
-
|
|
407
|
+
traceId: z,
|
|
408
|
+
...n === void 0 ? {} : { connection: n }
|
|
409
|
+
}, g = c?.queued ?? [], _ = c?.nudges ?? [], v = ue({
|
|
405
410
|
bus: u.bus,
|
|
406
|
-
tenantId: t.tenantId ?? null
|
|
407
|
-
|
|
411
|
+
tenantId: t.tenantId ?? null,
|
|
412
|
+
...o === i ? {} : { deferToCommit: (e) => {
|
|
413
|
+
g.push(async () => {
|
|
414
|
+
e();
|
|
415
|
+
});
|
|
416
|
+
} }
|
|
417
|
+
}), y = ce({ store: o }), b = We(t, e.rowFilter, a), x = {
|
|
408
418
|
clock: s,
|
|
409
419
|
webhooks: l,
|
|
410
|
-
events:
|
|
420
|
+
events: v,
|
|
411
421
|
eventBus: u,
|
|
412
422
|
llm: d,
|
|
413
423
|
...e.ai === void 0 ? {} : { ai: e.ai },
|
|
414
|
-
request:
|
|
415
|
-
|
|
416
|
-
|
|
424
|
+
request: h,
|
|
425
|
+
...n === void 0 ? {} : { connectionState: se(n, o === i ? void 0 : (e) => {
|
|
426
|
+
g.push(e);
|
|
427
|
+
}) },
|
|
428
|
+
validation: ne,
|
|
429
|
+
access: oe(t),
|
|
417
430
|
cache: f,
|
|
418
431
|
kv: p,
|
|
419
|
-
store:
|
|
432
|
+
store: E(Ke(o, b), {
|
|
420
433
|
subject: t,
|
|
421
434
|
schemaRegistry: r,
|
|
422
|
-
rowFilter:
|
|
435
|
+
rowFilter: S
|
|
423
436
|
}),
|
|
424
|
-
storeForTenant: (e) =>
|
|
437
|
+
storeForTenant: (e) => E(Ke(o, b), {
|
|
425
438
|
subject: ee(t, e),
|
|
426
439
|
schemaRegistry: r,
|
|
427
|
-
rowFilter:
|
|
440
|
+
rowFilter: S
|
|
428
441
|
}),
|
|
429
442
|
outbox: de({
|
|
430
|
-
store:
|
|
443
|
+
store: o,
|
|
431
444
|
subject: t,
|
|
432
|
-
traceId:
|
|
445
|
+
traceId: z,
|
|
433
446
|
nudge: () => {
|
|
434
|
-
_.push(
|
|
447
|
+
_.push(z);
|
|
435
448
|
},
|
|
436
449
|
afterCommit: (e) => {
|
|
437
450
|
g.push(e);
|
|
438
451
|
}
|
|
439
452
|
}),
|
|
440
|
-
load:
|
|
441
|
-
loadMany:
|
|
442
|
-
withSubject: (e, t) => Promise.resolve(t(m(e, n, {
|
|
453
|
+
load: y.load,
|
|
454
|
+
loadMany: y.loadMany,
|
|
455
|
+
withSubject: (e, t) => Promise.resolve(t(m(e, n, o, {
|
|
443
456
|
queued: g,
|
|
444
457
|
nudges: _
|
|
445
458
|
}))),
|
|
446
459
|
withTenant: (e, r) => Promise.resolve(r(m({
|
|
447
460
|
...t,
|
|
448
461
|
tenantId: e
|
|
449
|
-
}, n, {
|
|
462
|
+
}, n, o, {
|
|
450
463
|
queued: g,
|
|
451
464
|
nudges: _
|
|
452
465
|
})))
|
|
453
466
|
};
|
|
454
|
-
return
|
|
467
|
+
return H.set(x, g), V.set(x, (e) => m(t, e, o, {
|
|
468
|
+
queued: g,
|
|
469
|
+
nudges: _
|
|
470
|
+
})), U.set(x, _), W.set(x, e.plugins ?? []), G.set(x, e.layers ?? []), B.set(x, (e) => o.transactional((r) => e(m(t, n, r, {
|
|
455
471
|
queued: g,
|
|
456
472
|
nudges: _
|
|
457
|
-
})))),
|
|
473
|
+
})))), x;
|
|
458
474
|
};
|
|
459
|
-
return m(e.subject ?? h(null));
|
|
460
|
-
},
|
|
461
|
-
let n =
|
|
475
|
+
return m(e.subject ?? h(null), e.connection);
|
|
476
|
+
}, Je = (e, t) => {
|
|
477
|
+
let n = q(t);
|
|
462
478
|
return (n === void 0 ? e : u.provide(e, n)).pipe(u.provide(le(t.store)), u.provideService(m, t.request.subject));
|
|
463
|
-
},
|
|
479
|
+
}, Ye = async (e, t, n, r) => {
|
|
464
480
|
let i = e.input, a = await p.decodeUnknownPromise(i)(n), o = e.kind, s = async (e) => {
|
|
465
481
|
let n = t(a, e);
|
|
466
|
-
return u.isEffect(n) ?
|
|
482
|
+
return u.isEffect(n) ? w(Je(n, e)) : n;
|
|
467
483
|
}, c = async () => {
|
|
468
484
|
let t = e.guards;
|
|
469
485
|
if (t !== void 0 && t.length > 0) {
|
|
@@ -471,9 +487,9 @@ var Ee = new Set(e), E = 0, De = () => ++E, Oe = (e, t) => {
|
|
|
471
487
|
if (e !== null) throw e;
|
|
472
488
|
}
|
|
473
489
|
if (o !== "mutation") return s(r);
|
|
474
|
-
let n = await he(async () =>
|
|
475
|
-
return await
|
|
476
|
-
}, f = o === "mutation" || o === "query" || o === "action" ? o : void 0, m = f === void 0 ? void 0 :
|
|
490
|
+
let n = await he(async () => X(r, async (e) => (J(r), s(e))), { delay: () => Promise.resolve() });
|
|
491
|
+
return await Y(r), n;
|
|
492
|
+
}, f = o === "mutation" || o === "query" || o === "action" ? o : void 0, m = f === void 0 ? void 0 : K(r, f);
|
|
477
493
|
if (m === void 0 || f === void 0) return await c();
|
|
478
494
|
let h = m(u.tryPromise({
|
|
479
495
|
try: () => c(),
|
|
@@ -488,12 +504,12 @@ var Ee = new Set(e), E = 0, De = () => ++E, Oe = (e, t) => {
|
|
|
488
504
|
}), _ = await u.runPromiseExit(h);
|
|
489
505
|
if (d.isSuccess(_)) return _.value;
|
|
490
506
|
throw l.squash(_.cause);
|
|
491
|
-
},
|
|
507
|
+
}, Xe = (e) => new Promise((t) => {
|
|
492
508
|
let n = new _e.Socket(), r = (e) => {
|
|
493
509
|
clearTimeout(i), n.destroy(), t(e);
|
|
494
510
|
}, i = setTimeout(() => r(!1), e.timeoutMs ?? 750);
|
|
495
511
|
n.once("connect", () => r(!0)), n.once("error", () => r(!1)), n.connect(e.port, e.host);
|
|
496
|
-
}),
|
|
512
|
+
}), Ze = async (e, t, n, r) => {
|
|
497
513
|
if (typeof t != "string" || typeof n != "function" || typeof r != "function") throw TypeError(`describeIfAvailable('${e}', ...): expected (label, dependency, probe, suite) — got dependency: ${typeof t}, probe: ${typeof n}, suite: ${typeof r}. Three arguments means the suite body is being used as the probe: it would run at file scope, unconditionally, and the skip would silently do nothing.`);
|
|
498
514
|
let i = !1;
|
|
499
515
|
try {
|
|
@@ -502,20 +518,20 @@ var Ee = new Set(e), E = 0, De = () => ++E, Oe = (e, t) => {
|
|
|
502
518
|
i = !1;
|
|
503
519
|
}
|
|
504
520
|
ve.skipIf(!i)(i ? e : `${e} [SKIPPED: needs ${t}]`, r);
|
|
505
|
-
},
|
|
506
|
-
await
|
|
507
|
-
},
|
|
521
|
+
}, Qe = async (e, t, n) => {
|
|
522
|
+
await Ze(e, `${t.name} at ${t.host}:${t.port}`, () => Xe(t), n);
|
|
523
|
+
}, $e = async (e, t) => {
|
|
508
524
|
try {
|
|
509
525
|
let n = await t();
|
|
510
526
|
n !== void 0 && n !== "" && console.log(`[engine] ${e}: ${n}`);
|
|
511
527
|
} catch {}
|
|
512
|
-
},
|
|
528
|
+
}, et = "test-tenant", Q = (e, t, n) => ({
|
|
513
529
|
type: e,
|
|
514
530
|
id: t,
|
|
515
531
|
tenantId: n.tenantId ?? "test-tenant",
|
|
516
532
|
scopes: n.scopes ?? [],
|
|
517
533
|
...n.metadata === void 0 ? {} : { metadata: n.metadata }
|
|
518
|
-
}),
|
|
534
|
+
}), tt = (e, t = {}) => Q("user", e, t), nt = (e, t = {}) => Q("apiKey", e, t), rt = (e, t = {}) => Q("serviceAccount", e, t), it = (e = null) => h(e), at = (e = "job:test", t) => t === void 0 ? b(e) : b(e, t), ot = (e) => {
|
|
519
535
|
let t = e.indexOf("?");
|
|
520
536
|
return t === -1 ? {
|
|
521
537
|
path: e,
|
|
@@ -524,23 +540,24 @@ var Ee = new Set(e), E = 0, De = () => ++E, Oe = (e, t) => {
|
|
|
524
540
|
path: e.slice(0, t),
|
|
525
541
|
query: e.slice(t + 1)
|
|
526
542
|
};
|
|
527
|
-
},
|
|
543
|
+
}, st = (e, t) => {
|
|
528
544
|
let n = e.split("/").filter((e) => e.length > 0), r = t.split("/").filter((e) => e.length > 0);
|
|
529
545
|
return n.length === r.length && n.every((e, t) => e.startsWith(":") || e === r[t]);
|
|
530
|
-
},
|
|
546
|
+
}, $ = (e) => {
|
|
531
547
|
let t = {};
|
|
532
548
|
for (let [n, r] of Object.entries(e)) t[n.toLowerCase()] = r;
|
|
533
549
|
return t;
|
|
534
|
-
},
|
|
550
|
+
}, ct = (e) => e === void 0 ? /* @__PURE__ */ new Uint8Array() : new TextEncoder().encode(JSON.stringify(e)), lt = (e) => e === void 0 ? "" : typeof e == "string" ? e : new TextDecoder().decode(e), ut = (e) => {
|
|
535
551
|
let { ctx: t } = e, n = ye((e.publicApi ?? []).map((e) => ({
|
|
536
552
|
descriptor: e.descriptor,
|
|
537
|
-
invoke: (n, r) => t.withSubject(r.subject, (t) =>
|
|
553
|
+
invoke: (n, r) => t.withSubject(r.subject, (t) => Ye(e.descriptor, e.handler, n, t))
|
|
538
554
|
}))), r = [...e.restRoutes ?? [], ...n], i = _(e.strategies ?? [], {
|
|
539
555
|
...e.anonymousTenantRequired === void 0 ? {} : { anonymousTenantRequired: e.anonymousTenantRequired },
|
|
540
556
|
getStore: () => t.store
|
|
541
557
|
}), a = (n, o) => {
|
|
542
558
|
let s = {
|
|
543
559
|
store: t.store,
|
|
560
|
+
buildContext: () => t,
|
|
544
561
|
resolveSubject: n === void 0 ? async (e) => (await i({
|
|
545
562
|
headers: e,
|
|
546
563
|
clientId: -1
|
|
@@ -550,7 +567,7 @@ var Ee = new Set(e), E = 0, De = () => ++E, Oe = (e, t) => {
|
|
|
550
567
|
pattern: r[t].path,
|
|
551
568
|
route: e
|
|
552
569
|
})), l = async (e, n, i = {}) => {
|
|
553
|
-
let { path: a, query: s } =
|
|
570
|
+
let { path: a, query: s } = ot(n), l = c.filter((e) => st(e.pattern, a)).map((e) => e.route);
|
|
554
571
|
if (l.length === 0) return {
|
|
555
572
|
status: 404,
|
|
556
573
|
headers: {},
|
|
@@ -566,15 +583,15 @@ var Ee = new Set(e), E = 0, De = () => ++E, Oe = (e, t) => {
|
|
|
566
583
|
path: a,
|
|
567
584
|
query: s,
|
|
568
585
|
headers: {
|
|
569
|
-
|
|
570
|
-
|
|
586
|
+
...$(o),
|
|
587
|
+
...$(i.headers ?? {})
|
|
571
588
|
},
|
|
572
|
-
rawBody:
|
|
589
|
+
rawBody: ct(i.body),
|
|
573
590
|
store: t.store
|
|
574
591
|
}, d = await be(l, u), f = {};
|
|
575
592
|
for (let [e, t] of Object.entries(d.headers ?? {})) f[e.toLowerCase()] = t;
|
|
576
593
|
d.contentType !== void 0 && (f["content-type"] = d.contentType);
|
|
577
|
-
let p =
|
|
594
|
+
let p = lt(d.body), m = (d.contentType ?? "").includes("json");
|
|
578
595
|
return {
|
|
579
596
|
status: d.status,
|
|
580
597
|
headers: f,
|
|
@@ -608,7 +625,7 @@ var Ee = new Set(e), E = 0, De = () => ++E, Oe = (e, t) => {
|
|
|
608
625
|
};
|
|
609
626
|
};
|
|
610
627
|
return a(void 0, {});
|
|
611
|
-
},
|
|
628
|
+
}, dt = (e = {}) => {
|
|
612
629
|
let t = e.id ?? "test", n = e.store ?? new Proxy({}, { get: (e, t) => {
|
|
613
630
|
throw Error(`makeSubscribeContext: this subscriber reached ctx.store.${String(t)} and the test passed no store. Pass \`{ store: makeTestContext().store }\` so the subscriber and the code under test share one — an empty stand-in would let a read of the wrong table pass.`);
|
|
614
631
|
} });
|
|
@@ -618,7 +635,7 @@ var Ee = new Set(e), E = 0, De = () => ++E, Oe = (e, t) => {
|
|
|
618
635
|
store: n,
|
|
619
636
|
...e.publish === void 0 ? {} : { publish: e.publish }
|
|
620
637
|
};
|
|
621
|
-
},
|
|
638
|
+
}, ft = (e) => {
|
|
622
639
|
let t = /* @__PURE__ */ new Map();
|
|
623
640
|
for (let n of e) {
|
|
624
641
|
let e = t.get(n.stepName);
|
|
@@ -644,13 +661,13 @@ var Ee = new Set(e), E = 0, De = () => ++E, Oe = (e, t) => {
|
|
|
644
661
|
});
|
|
645
662
|
}
|
|
646
663
|
return n.sort((e, t) => e._startedAt - t._startedAt), n.map(({ _startedAt: e, ...t }) => t);
|
|
647
|
-
},
|
|
664
|
+
}, pt = (e) => {
|
|
648
665
|
let t = e.workflows ?? [], n = /* @__PURE__ */ new Map(), r = 0;
|
|
649
666
|
return {
|
|
650
667
|
start: async (i, a) => {
|
|
651
668
|
let o = t.find((e) => e.workflow.name === i);
|
|
652
669
|
if (o === void 0) throw Error(`makeWorkflowRunner: no workflow named '${i}' — pass it in makeWorkflowRunner({ ctx, workflows: [{ workflow, execute }] }).`);
|
|
653
|
-
let s = `wfrun_test_${++r}`, c = Te(), l = o.workflow.toLayer(o.execute).pipe(f.provideMerge(we)), d = /* @__PURE__ */ new Date(), p = o.workflow.execute(a).pipe(u.locally(Ce, s), u.provide(c.layer), u.provide(l), u.either), m = await u.runPromise(p), h =
|
|
670
|
+
let s = `wfrun_test_${++r}`, c = Te(), l = o.workflow.toLayer(o.execute).pipe(f.provideMerge(we)), d = /* @__PURE__ */ new Date(), p = o.workflow.execute(a).pipe(u.locally(Ce, s), u.provide(c.layer), u.provide(l), u.either), m = await u.runPromise(p), h = ft(c.readSteps()), g = /* @__PURE__ */ new Date(), _;
|
|
654
671
|
if (m._tag === "Right") _ = {
|
|
655
672
|
status: "succeeded",
|
|
656
673
|
output: m.right,
|
|
@@ -690,6 +707,6 @@ var Ee = new Set(e), E = 0, De = () => ++E, Oe = (e, t) => {
|
|
|
690
707
|
},
|
|
691
708
|
inspect: async (e) => n.get(e) ?? null
|
|
692
709
|
};
|
|
693
|
-
},
|
|
710
|
+
}, mt = 1;
|
|
694
711
|
//#endregion
|
|
695
|
-
export {
|
|
712
|
+
export { F as MockClock, R as MockLLM, I as MockWebhooks, mt as TESTING_PRESET_VERSION, et as TEST_TENANT_ID, it as anonymous, nt as apiKey, n as changeDelete, r as changeInsert, i as changeSoftDelete, a as changeUpdate, je as defineFactory, Ze as describeIfAvailable, Qe as describeIfReachable, k as fixtureRow, Fe as frozenTime, Ye as invoke, Xe as isTcpReachable, dt as makeSubscribeContext, ut as makeTestApp, qe as makeTestContext, pt as makeWorkflowRunner, Le as mockStore, O as nextSequence, ze as outboxNudgesOf, $e as reportEngineVersion, J as resetAfterCommit, K as rpcInterceptorFor, Y as runAfterCommit, X as runInStoreTransaction, rt as serviceAccount, q as serviceLayerFor, at as system, L as testEventBus, tt as user, Pe as withFrozenTime, Re as withRpcConnection };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voltro/testing",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.66.0",
|
|
4
4
|
"description": "Test utilities for Voltro apps — deterministic clock, subject and row factories, a handler-level invoke and a request-level app harness, queued LLM responses, scoped subject/tenant runners, and a cross-dialect parity harness.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"voltro",
|
|
@@ -43,15 +43,15 @@
|
|
|
43
43
|
"node": ">=24.0.0"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@voltro/database": "0.
|
|
47
|
-
"@voltro/env": "0.
|
|
48
|
-
"@voltro/logger": "0.
|
|
49
|
-
"@voltro/protocol": "0.
|
|
50
|
-
"@voltro/runtime": "0.
|
|
51
|
-
"@voltro/workflow": "0.
|
|
46
|
+
"@voltro/database": "0.66.0",
|
|
47
|
+
"@voltro/env": "0.66.0",
|
|
48
|
+
"@voltro/logger": "0.66.0",
|
|
49
|
+
"@voltro/protocol": "0.66.0",
|
|
50
|
+
"@voltro/runtime": "0.66.0",
|
|
51
|
+
"@voltro/workflow": "0.66.0"
|
|
52
52
|
},
|
|
53
53
|
"peerDependencies": {
|
|
54
|
-
"@voltro/client": "0.
|
|
54
|
+
"@voltro/client": "0.66.0",
|
|
55
55
|
"effect": "^3.22.0",
|
|
56
56
|
"react": "^19.0.0",
|
|
57
57
|
"@effect/sql": "^0.52.0"
|