@voltro/testing 0.33.0 → 0.34.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 +1801 -0
- package/README.md +1 -1
- package/dist/client.d.ts +49 -2
- package/dist/client.js +55 -45
- package/dist/dialect.js +192 -154
- package/dist/index.d.ts +378 -21
- package/dist/index.js +308 -112
- package/package.json +9 -9
package/dist/index.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
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
|
-
import { SubjectService as m, anonymousSubject as h, checkGuardsEffect as g,
|
|
4
|
-
import { EventBus as
|
|
5
|
-
import { installEnvSnapshot as
|
|
6
|
-
import
|
|
7
|
-
import { describe as
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
3
|
+
import { SubjectService as m, anonymousSubject as h, checkGuardsEffect as g, composeAuthStrategies as _, composeRpcInterceptors as v, eventRoute as ee, systemSubject as y, tenantScopedSubject as b } from "@voltro/protocol";
|
|
4
|
+
import { EventBus as te, InMemoryDataStore as ne, applyRowFilterToDescriptor as re, clearSystemStoreHandle as ie, getRowFilter as ae, getSystemStoreHandle as oe, makeAppAccess as se, makeDataLoader as x, makeEffectStoreLayer as ce, makeOutboxFacade as le, makeSchemaRegistry as ue, publishEvent as de, resolveRowFilterScopeFor as fe, runProvidedEffect as S, runWithDeadlockRetry as pe, setSystemStoreHandle as C, wrapStoreWithMixinBehaviour as w } from "@voltro/runtime";
|
|
5
|
+
import { installEnvSnapshot as me } from "@voltro/env";
|
|
6
|
+
import he from "node:net";
|
|
7
|
+
import { describe as ge } from "vitest";
|
|
8
|
+
import { collectPublicApiRoutes as _e, dispatchSharedPath as ve, restRoutesToHttpRoutes as ye } from "@voltro/protocol/rest";
|
|
9
|
+
import { createLogger as be } from "@voltro/logger";
|
|
10
|
+
import { CurrentWorkflowRunId as xe, inMemoryWorkflowEngineLayer as Se, makeInMemoryRecorder as Ce } from "@voltro/workflow";
|
|
10
11
|
//#region src/fixtureRow.ts
|
|
11
|
-
var
|
|
12
|
+
var we = new Set(e), T = 0, E = () => ++T, Te = (e, t) => {
|
|
12
13
|
if (t.oneOf && t.oneOf.length > 0) return t.oneOf[0];
|
|
13
14
|
if (t.enumValues && t.enumValues.length > 0) return t.enumValues[0];
|
|
14
15
|
let n = t.unique === !0;
|
|
@@ -16,27 +17,95 @@ var j = new Set(e), M = 0, N = (e, t) => {
|
|
|
16
17
|
case "text":
|
|
17
18
|
case "reference":
|
|
18
19
|
case "id":
|
|
19
|
-
case "enum": return n ? `${e}-${++
|
|
20
|
+
case "enum": return n ? `${e}-${++T}` : e;
|
|
20
21
|
case "integer":
|
|
21
|
-
case "real": return n ? ++
|
|
22
|
+
case "real": return n ? ++T : 0;
|
|
22
23
|
case "decimal":
|
|
23
|
-
case "bigint": return n ? String(++
|
|
24
|
+
case "bigint": return n ? String(++T) : "0";
|
|
24
25
|
case "boolean": return !1;
|
|
25
26
|
case "timestamp":
|
|
26
27
|
case "date": return /* @__PURE__ */ new Date(0);
|
|
27
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}: … }).`);
|
|
28
29
|
}
|
|
29
|
-
},
|
|
30
|
+
}, D = (e, t = {}) => {
|
|
30
31
|
let n = {};
|
|
31
|
-
for (let r of s(e, t))
|
|
32
|
+
for (let r of s(e, t)) we.has(r) || (n[r] = Te(r, e.fields[r]));
|
|
32
33
|
return {
|
|
33
34
|
...n,
|
|
34
35
|
...t
|
|
35
36
|
};
|
|
36
|
-
},
|
|
37
|
+
}, O = (e) => {
|
|
38
|
+
let t = {};
|
|
39
|
+
for (let [n, r] of Object.entries(e)) t[n] = typeof r == "function" ? r(E()) : r;
|
|
40
|
+
return t;
|
|
41
|
+
}, Ee = (e) => {
|
|
42
|
+
for (let [t, n] of Object.entries(e.fields)) if (n.type === "id") return t;
|
|
43
|
+
return "id";
|
|
44
|
+
}, De = async (e, t, n, r, i) => {
|
|
45
|
+
let a = {};
|
|
46
|
+
for (let o of s(t, n)) {
|
|
47
|
+
let n = t.fields[o];
|
|
48
|
+
if (n === void 0 || n.type !== "reference" || n.references === void 0) continue;
|
|
49
|
+
let s = n.references();
|
|
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
|
+
let c = r[o];
|
|
52
|
+
a[o] = (c === void 0 ? await k(e, s, {}, {}, [...i, s.tableName]) : await c.create(e, {}))[Ee(s)];
|
|
53
|
+
}
|
|
54
|
+
return a;
|
|
55
|
+
}, k = async (e, t, n, r, i) => {
|
|
56
|
+
let a = {
|
|
57
|
+
...await De(e, t, n, r, i),
|
|
58
|
+
...n
|
|
59
|
+
};
|
|
60
|
+
return e.insert(t.tableName, D(t, a));
|
|
61
|
+
}, A = (e, t) => typeof e == "function" ? e(t) : e ?? {}, Oe = (e, t = {}) => {
|
|
62
|
+
let n = t.traits ?? {}, r = t.associations ?? {}, i = (t) => {
|
|
63
|
+
let a = (e = {}) => ({
|
|
64
|
+
...O(t),
|
|
65
|
+
...e
|
|
66
|
+
}), o = (t = {}) => D(e, a(t));
|
|
67
|
+
return {
|
|
68
|
+
table: e,
|
|
69
|
+
build: o,
|
|
70
|
+
buildList: (e, t) => Array.from({ length: e }, (e, n) => o(A(t, n))),
|
|
71
|
+
create: (t, n = {}) => k(t, e, a(n), r, [e.tableName]),
|
|
72
|
+
createList: async (t, n, i) => {
|
|
73
|
+
let o = [];
|
|
74
|
+
for (let s = 0; s < n; s++) o.push(await k(t, e, a(A(i, s)), r, [e.tableName]));
|
|
75
|
+
return o;
|
|
76
|
+
},
|
|
77
|
+
with: (...r) => {
|
|
78
|
+
let a = t;
|
|
79
|
+
for (let t of r) {
|
|
80
|
+
let r = n[t];
|
|
81
|
+
if (r === void 0) throw Error(`defineFactory: '${e.tableName}' has no trait '${t}'. Declared: ${Object.keys(n).join(", ") || "(none)"}.`);
|
|
82
|
+
a = {
|
|
83
|
+
...a,
|
|
84
|
+
...r
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
return i(a);
|
|
88
|
+
},
|
|
89
|
+
extend: (e) => i({
|
|
90
|
+
...t,
|
|
91
|
+
...e
|
|
92
|
+
})
|
|
93
|
+
};
|
|
94
|
+
};
|
|
95
|
+
return i(t.defaults ?? {});
|
|
96
|
+
}, j = Symbol.for("voltro/testing/mockClock.installed"), M = () => globalThis, ke = (e, t) => new Proxy(e, {
|
|
97
|
+
construct: (e, n, r) => Reflect.construct(e, n.length === 0 ? [t()] : n, r),
|
|
98
|
+
apply: () => new e(t()).toString(),
|
|
99
|
+
get: (e, n, r) => n === "now" ? () => t() : Reflect.get(e, n, r)
|
|
100
|
+
}), N = (e) => {
|
|
101
|
+
if (typeof e == "number") return e;
|
|
102
|
+
let t = e instanceof Date ? e.getTime() : Date.parse(e);
|
|
103
|
+
if (Number.isNaN(t)) throw Error(`mockClock: cannot read an instant from '${String(e)}'`);
|
|
104
|
+
return t;
|
|
105
|
+
}, P = class {
|
|
37
106
|
currentMs;
|
|
38
|
-
constructor(e =
|
|
39
|
-
this.currentMs =
|
|
107
|
+
constructor(e = "2026-01-01T00:00:00Z") {
|
|
108
|
+
this.currentMs = N(e);
|
|
40
109
|
}
|
|
41
110
|
now() {
|
|
42
111
|
return this.currentMs;
|
|
@@ -45,9 +114,47 @@ var j = new Set(e), M = 0, N = (e, t) => {
|
|
|
45
114
|
return new Date(this.currentMs);
|
|
46
115
|
}
|
|
47
116
|
advance(e) {
|
|
48
|
-
this.currentMs += typeof e == "number" ? e :
|
|
117
|
+
this.currentMs += typeof e == "number" ? e : Ne(e);
|
|
118
|
+
}
|
|
119
|
+
set(e) {
|
|
120
|
+
this.currentMs = N(e);
|
|
121
|
+
}
|
|
122
|
+
get installed() {
|
|
123
|
+
return M()[j]?.clock === this;
|
|
49
124
|
}
|
|
50
|
-
|
|
125
|
+
install() {
|
|
126
|
+
let e = M(), t = e[j];
|
|
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
|
+
let n = globalThis.Date;
|
|
129
|
+
return e[j] = {
|
|
130
|
+
realDate: n,
|
|
131
|
+
clock: this
|
|
132
|
+
}, globalThis.Date = ke(n, () => this.currentMs), () => {
|
|
133
|
+
this.uninstall();
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
uninstall() {
|
|
137
|
+
let e = M(), t = e[j];
|
|
138
|
+
t === void 0 || t.clock !== this || (globalThis.Date = t.realDate, delete e[j]);
|
|
139
|
+
}
|
|
140
|
+
}, Ae = (e) => typeof e == "object" && !!e && typeof e.then == "function", je = (e, t) => {
|
|
141
|
+
let n = e instanceof P ? e : new P(e);
|
|
142
|
+
n.install();
|
|
143
|
+
let r = !1;
|
|
144
|
+
try {
|
|
145
|
+
let e = t(n);
|
|
146
|
+
return Ae(e) ? (r = !0, e.then((e) => (n.uninstall(), e), (e) => {
|
|
147
|
+
throw n.uninstall(), e;
|
|
148
|
+
})) : e;
|
|
149
|
+
} finally {
|
|
150
|
+
r || n.uninstall();
|
|
151
|
+
}
|
|
152
|
+
}, Me = (e) => u.acquireRelease(u.sync(() => {
|
|
153
|
+
let t = e instanceof P ? e : new P(e);
|
|
154
|
+
return t.install(), t;
|
|
155
|
+
}), (e) => u.sync(() => {
|
|
156
|
+
e.uninstall();
|
|
157
|
+
})), Ne = (e) => {
|
|
51
158
|
let t = /^\s*(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)\s*$/.exec(e);
|
|
52
159
|
if (!t) throw Error(`mockClock: cannot parse duration '${e}'`);
|
|
53
160
|
let n = Number(t[1]), r = t[2];
|
|
@@ -59,21 +166,7 @@ var j = new Set(e), M = 0, N = (e, t) => {
|
|
|
59
166
|
case "d": return n * 864e5;
|
|
60
167
|
default: throw Error(`mockClock: unknown unit '${r}'`);
|
|
61
168
|
}
|
|
62
|
-
},
|
|
63
|
-
sent = [];
|
|
64
|
-
send(e) {
|
|
65
|
-
this.sent.push({
|
|
66
|
-
...e,
|
|
67
|
-
sentAt: /* @__PURE__ */ new Date()
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
lastTo(e) {
|
|
71
|
-
for (let t = this.sent.length - 1; t >= 0; t--) if (this.sent[t].to === e) return this.sent[t];
|
|
72
|
-
}
|
|
73
|
-
clear() {
|
|
74
|
-
this.sent.length = 0;
|
|
75
|
-
}
|
|
76
|
-
}, R = class {
|
|
169
|
+
}, F = class {
|
|
77
170
|
emitted = [];
|
|
78
171
|
async emit(e, t) {
|
|
79
172
|
let n = typeof e == "string" ? e : e.id ?? e.event ?? "(unnamed event)";
|
|
@@ -92,7 +185,7 @@ var j = new Set(e), M = 0, N = (e, t) => {
|
|
|
92
185
|
clear() {
|
|
93
186
|
this.emitted.length = 0;
|
|
94
187
|
}
|
|
95
|
-
},
|
|
188
|
+
}, I = class {
|
|
96
189
|
queue;
|
|
97
190
|
calls = [];
|
|
98
191
|
constructor(e) {
|
|
@@ -107,29 +200,32 @@ var j = new Set(e), M = 0, N = (e, t) => {
|
|
|
107
200
|
remaining() {
|
|
108
201
|
return this.queue.length;
|
|
109
202
|
}
|
|
110
|
-
},
|
|
111
|
-
let n =
|
|
203
|
+
}, Pe = (e) => e, L = "00000000000000000000000000000000", R = /* @__PURE__ */ new WeakMap(), z = /* @__PURE__ */ new WeakMap(), B = /* @__PURE__ */ new WeakMap(), V = /* @__PURE__ */ new WeakMap(), H = /* @__PURE__ */ new WeakMap(), U = (e, t) => {
|
|
204
|
+
let n = V.get(e) ?? [], r = t === "mutation" ? "interceptMutation" : t === "query" ? "interceptQuery" : "interceptAction", i = [];
|
|
112
205
|
for (let e of n) {
|
|
113
206
|
let t = e[r];
|
|
114
207
|
typeof t == "function" && i.push(t);
|
|
115
208
|
}
|
|
116
|
-
return
|
|
117
|
-
},
|
|
118
|
-
let t =
|
|
209
|
+
return v(i);
|
|
210
|
+
}, W = (e) => {
|
|
211
|
+
let t = (V.get(e) ?? []).map((e) => e.services).filter((e) => e !== void 0), n = H.get(e) ?? [], r = [...t, ...n];
|
|
212
|
+
if (r.length !== 0) return f.mergeAll(...r);
|
|
213
|
+
}, G = (e) => {
|
|
214
|
+
let t = z.get(e);
|
|
119
215
|
t !== void 0 && (t.length = 0);
|
|
120
|
-
},
|
|
121
|
-
let t =
|
|
216
|
+
}, K = async (e) => {
|
|
217
|
+
let t = z.get(e);
|
|
122
218
|
if (t === void 0 || t.length === 0) return;
|
|
123
219
|
let n = [...t];
|
|
124
220
|
t.length = 0;
|
|
125
221
|
for (let e of n) await e();
|
|
126
|
-
},
|
|
127
|
-
let n =
|
|
222
|
+
}, Fe = (e) => B.get(e) ?? [], q = async (e, t) => {
|
|
223
|
+
let n = R.get(e);
|
|
128
224
|
return n === void 0 ? e.store.transactional(async (n) => t({
|
|
129
225
|
...e,
|
|
130
226
|
store: n
|
|
131
227
|
})) : n(t);
|
|
132
|
-
},
|
|
228
|
+
}, Ie = (e) => {
|
|
133
229
|
let t = /* @__PURE__ */ new Map(), n = /* @__PURE__ */ new Map(), r = (n) => {
|
|
134
230
|
let r = t.get(n);
|
|
135
231
|
return r === void 0 ? !1 : r.expiresAt !== null && r.expiresAt <= e() ? (t.delete(n), !1) : !0;
|
|
@@ -168,7 +264,7 @@ var j = new Set(e), M = 0, N = (e, t) => {
|
|
|
168
264
|
return i(e, o, n.ttlMs, n.tags), o;
|
|
169
265
|
}
|
|
170
266
|
};
|
|
171
|
-
},
|
|
267
|
+
}, Le = (e) => {
|
|
172
268
|
let t = /* @__PURE__ */ new Map(), n = (n) => {
|
|
173
269
|
let r = t.get(n);
|
|
174
270
|
return r === void 0 ? !1 : r.expiresAt !== null && r.expiresAt <= e() ? (t.delete(n), !1) : !0;
|
|
@@ -196,96 +292,98 @@ var j = new Set(e), M = 0, N = (e, t) => {
|
|
|
196
292
|
t.clear();
|
|
197
293
|
}
|
|
198
294
|
};
|
|
199
|
-
},
|
|
200
|
-
let n =
|
|
201
|
-
|
|
295
|
+
}, Re = async (e, t) => {
|
|
296
|
+
let n = oe();
|
|
297
|
+
C(e);
|
|
202
298
|
try {
|
|
203
299
|
return await t();
|
|
204
300
|
} finally {
|
|
205
|
-
n === void 0 ?
|
|
301
|
+
n === void 0 ? ie() : C(n);
|
|
206
302
|
}
|
|
207
|
-
},
|
|
208
|
-
let r =
|
|
303
|
+
}, ze = (e, t, n) => {
|
|
304
|
+
let r = Be, i;
|
|
209
305
|
return () => {
|
|
210
|
-
let a = t ??
|
|
211
|
-
return (i === void 0 || a !== r) && (r = a, i =
|
|
306
|
+
let a = t ?? ae();
|
|
307
|
+
return (i === void 0 || a !== r) && (r = a, i = Re(n, () => S(fe(a, e, (e) => {
|
|
212
308
|
console.error("[voltro:testing] row filter failed to load — reads refused for this subject", e);
|
|
213
309
|
})))), i;
|
|
214
310
|
};
|
|
215
|
-
},
|
|
216
|
-
if (n === "query") return async (n) => e.query(
|
|
311
|
+
}, Be = Symbol("unresolved"), J = (e, t) => new Proxy(e, { get: (e, n) => {
|
|
312
|
+
if (n === "query") return async (n) => e.query(re(await t(), n));
|
|
217
313
|
let r = Reflect.get(e, n, e);
|
|
218
314
|
return typeof r == "function" ? r.bind(e) : r;
|
|
219
|
-
} }),
|
|
220
|
-
if (
|
|
315
|
+
} }), Ve = (e = {}) => {
|
|
316
|
+
if (me({
|
|
221
317
|
...process.env,
|
|
222
318
|
...e.env ?? {}
|
|
223
319
|
}), e.relations !== void 0) {
|
|
224
320
|
o();
|
|
225
321
|
for (let t of e.relations) c(t);
|
|
226
322
|
}
|
|
227
|
-
let n =
|
|
323
|
+
let n = ue(e.tables ?? t()), r = new ne(e.store ?? {}), i = {
|
|
228
324
|
dataStore: r,
|
|
229
325
|
schemaRegistry: n
|
|
230
326
|
};
|
|
231
|
-
|
|
232
|
-
let a = new
|
|
233
|
-
let
|
|
327
|
+
C(i);
|
|
328
|
+
let a = new P(e.clockStart), s = new F(), l = new I(e.llmResponses ?? []), u = Ie(() => a.now()), d = Le(() => a.now()), f = (t, o = r, c) => {
|
|
329
|
+
let p = {
|
|
234
330
|
subject: t,
|
|
235
|
-
traceId:
|
|
236
|
-
},
|
|
331
|
+
traceId: L
|
|
332
|
+
}, m = c?.queued ?? [], h = c?.nudges ?? [], g = x({ store: o }), _ = ze(t, e.rowFilter, i), v = {
|
|
237
333
|
clock: a,
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
llm: u,
|
|
334
|
+
webhooks: s,
|
|
335
|
+
llm: l,
|
|
241
336
|
...e.ai === void 0 ? {} : { ai: e.ai },
|
|
242
|
-
request:
|
|
243
|
-
access:
|
|
244
|
-
cache:
|
|
245
|
-
kv:
|
|
246
|
-
store:
|
|
337
|
+
request: p,
|
|
338
|
+
access: se(t),
|
|
339
|
+
cache: u,
|
|
340
|
+
kv: d,
|
|
341
|
+
store: w(J(o, _), {
|
|
247
342
|
subject: t,
|
|
248
343
|
schemaRegistry: n
|
|
249
344
|
}),
|
|
250
|
-
storeForTenant: (e) =>
|
|
251
|
-
subject:
|
|
345
|
+
storeForTenant: (e) => w(J(o, _), {
|
|
346
|
+
subject: b(t, e),
|
|
252
347
|
schemaRegistry: n
|
|
253
348
|
}),
|
|
254
|
-
outbox:
|
|
349
|
+
outbox: le({
|
|
255
350
|
store: o,
|
|
256
351
|
subject: t,
|
|
257
|
-
traceId:
|
|
352
|
+
traceId: L,
|
|
258
353
|
nudge: () => {
|
|
259
|
-
|
|
354
|
+
h.push(L);
|
|
260
355
|
},
|
|
261
356
|
afterCommit: (e) => {
|
|
262
|
-
|
|
357
|
+
m.push(e);
|
|
263
358
|
}
|
|
264
359
|
}),
|
|
265
|
-
load:
|
|
266
|
-
loadMany:
|
|
267
|
-
withSubject: (e, t) => Promise.resolve(t(
|
|
268
|
-
queued:
|
|
269
|
-
nudges:
|
|
360
|
+
load: g.load,
|
|
361
|
+
loadMany: g.loadMany,
|
|
362
|
+
withSubject: (e, t) => Promise.resolve(t(f(e, o, {
|
|
363
|
+
queued: m,
|
|
364
|
+
nudges: h
|
|
270
365
|
}))),
|
|
271
|
-
withTenant: (e, n) => Promise.resolve(n(
|
|
366
|
+
withTenant: (e, n) => Promise.resolve(n(f({
|
|
272
367
|
...t,
|
|
273
368
|
tenantId: e
|
|
274
369
|
}, o, {
|
|
275
|
-
queued:
|
|
276
|
-
nudges:
|
|
370
|
+
queued: m,
|
|
371
|
+
nudges: h
|
|
277
372
|
})))
|
|
278
373
|
};
|
|
279
|
-
return
|
|
280
|
-
queued:
|
|
281
|
-
nudges:
|
|
282
|
-
})))),
|
|
374
|
+
return z.set(v, m), B.set(v, h), V.set(v, e.plugins ?? []), H.set(v, e.layers ?? []), R.set(v, (e) => o.transactional((n) => e(f(t, n, {
|
|
375
|
+
queued: m,
|
|
376
|
+
nudges: h
|
|
377
|
+
})))), v;
|
|
283
378
|
};
|
|
284
|
-
return
|
|
285
|
-
},
|
|
379
|
+
return f(e.subject ?? h(null));
|
|
380
|
+
}, He = (e, t) => {
|
|
381
|
+
let n = W(t);
|
|
382
|
+
return (n === void 0 ? e : u.provide(e, n)).pipe(u.provide(ce(t.store)), u.provideService(m, t.request.subject));
|
|
383
|
+
}, Y = async (e, t, n, r) => {
|
|
286
384
|
let i = e.input, a = await p.decodeUnknownPromise(i)(n), o = e.kind, s = async (e) => {
|
|
287
385
|
let n = t(a, e);
|
|
288
|
-
return u.isEffect(n) ?
|
|
386
|
+
return u.isEffect(n) ? S(He(n, e)) : n;
|
|
289
387
|
}, c = async () => {
|
|
290
388
|
let t = e.guards;
|
|
291
389
|
if (t !== void 0 && t.length > 0) {
|
|
@@ -293,9 +391,9 @@ var j = new Set(e), M = 0, N = (e, t) => {
|
|
|
293
391
|
if (e !== null) throw e;
|
|
294
392
|
}
|
|
295
393
|
if (o !== "mutation") return s(r);
|
|
296
|
-
let n = await
|
|
297
|
-
return await
|
|
298
|
-
}, f = o === "mutation" || o === "query" || o === "action" ? o : void 0, m = f === void 0 ? void 0 :
|
|
394
|
+
let n = await pe(async () => (G(r), q(r, async (e) => s(e))), { delay: () => Promise.resolve() });
|
|
395
|
+
return await K(r), n;
|
|
396
|
+
}, f = o === "mutation" || o === "query" || o === "action" ? o : void 0, m = f === void 0 ? void 0 : U(r, f);
|
|
299
397
|
if (m === void 0 || f === void 0) return await c();
|
|
300
398
|
let h = m(u.tryPromise({
|
|
301
399
|
try: () => c(),
|
|
@@ -310,12 +408,12 @@ var j = new Set(e), M = 0, N = (e, t) => {
|
|
|
310
408
|
}), _ = await u.runPromiseExit(h);
|
|
311
409
|
if (d.isSuccess(_)) return _.value;
|
|
312
410
|
throw l.squash(_.cause);
|
|
313
|
-
},
|
|
314
|
-
let n = new
|
|
411
|
+
}, X = (e) => new Promise((t) => {
|
|
412
|
+
let n = new he.Socket(), r = (e) => {
|
|
315
413
|
clearTimeout(i), n.destroy(), t(e);
|
|
316
414
|
}, i = setTimeout(() => r(!1), e.timeoutMs ?? 750);
|
|
317
415
|
n.once("connect", () => r(!0)), n.once("error", () => r(!1)), n.connect(e.port, e.host);
|
|
318
|
-
}),
|
|
416
|
+
}), Z = async (e, t, n, r) => {
|
|
319
417
|
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.`);
|
|
320
418
|
let i = !1;
|
|
321
419
|
try {
|
|
@@ -323,17 +421,115 @@ var j = new Set(e), M = 0, N = (e, t) => {
|
|
|
323
421
|
} catch {
|
|
324
422
|
i = !1;
|
|
325
423
|
}
|
|
326
|
-
|
|
327
|
-
},
|
|
328
|
-
await
|
|
329
|
-
},
|
|
330
|
-
|
|
424
|
+
ge.skipIf(!i)(i ? e : `${e} [SKIPPED: needs ${t}]`, r);
|
|
425
|
+
}, Ue = async (e, t, n) => {
|
|
426
|
+
await Z(e, `${t.name} at ${t.host}:${t.port}`, () => X(t), n);
|
|
427
|
+
}, We = "test-tenant", Q = (e, t, n) => ({
|
|
428
|
+
type: e,
|
|
429
|
+
id: t,
|
|
430
|
+
tenantId: n.tenantId ?? "test-tenant",
|
|
431
|
+
scopes: n.scopes ?? [],
|
|
432
|
+
...n.metadata === void 0 ? {} : { metadata: n.metadata }
|
|
433
|
+
}), Ge = (e, t = {}) => Q("user", e, t), Ke = (e, t = {}) => Q("apiKey", e, t), qe = (e, t = {}) => Q("serviceAccount", e, t), Je = (e = null) => h(e), Ye = (e = "job:test", t) => t === void 0 ? y(e) : y(e, t), Xe = (e) => {
|
|
434
|
+
let t = e.indexOf("?");
|
|
435
|
+
return t === -1 ? {
|
|
436
|
+
path: e,
|
|
437
|
+
query: ""
|
|
438
|
+
} : {
|
|
439
|
+
path: e.slice(0, t),
|
|
440
|
+
query: e.slice(t + 1)
|
|
441
|
+
};
|
|
442
|
+
}, Ze = (e, t) => {
|
|
443
|
+
let n = e.split("/").filter((e) => e.length > 0), r = t.split("/").filter((e) => e.length > 0);
|
|
444
|
+
return n.length === r.length && n.every((e, t) => e.startsWith(":") || e === r[t]);
|
|
445
|
+
}, $ = (e) => {
|
|
446
|
+
let t = {};
|
|
447
|
+
for (let [n, r] of Object.entries(e)) t[n.toLowerCase()] = r;
|
|
448
|
+
return t;
|
|
449
|
+
}, Qe = (e) => e === void 0 ? /* @__PURE__ */ new Uint8Array() : new TextEncoder().encode(JSON.stringify(e)), $e = (e) => e === void 0 ? "" : typeof e == "string" ? e : new TextDecoder().decode(e), et = (e) => {
|
|
450
|
+
let { ctx: t } = e, n = _e((e.publicApi ?? []).map((e) => ({
|
|
451
|
+
descriptor: e.descriptor,
|
|
452
|
+
invoke: (n, r) => t.withSubject(r.subject, (t) => Y(e.descriptor, e.handler, n, t))
|
|
453
|
+
}))), r = [...e.restRoutes ?? [], ...n], i = _(e.strategies ?? [], {
|
|
454
|
+
...e.anonymousTenantRequired === void 0 ? {} : { anonymousTenantRequired: e.anonymousTenantRequired },
|
|
455
|
+
getStore: () => t.store
|
|
456
|
+
}), a = (n, o) => {
|
|
457
|
+
let s = {
|
|
458
|
+
store: t.store,
|
|
459
|
+
resolveSubject: n === void 0 ? async (e) => (await i({
|
|
460
|
+
headers: e,
|
|
461
|
+
clientId: -1
|
|
462
|
+
})).subject : () => n,
|
|
463
|
+
...e.idempotency === void 0 ? {} : { idempotency: e.idempotency }
|
|
464
|
+
}, c = ye(r, s).map((e, t) => ({
|
|
465
|
+
pattern: r[t].path,
|
|
466
|
+
route: e
|
|
467
|
+
})), l = async (e, n, i = {}) => {
|
|
468
|
+
let { path: a, query: s } = Xe(n), l = c.filter((e) => Ze(e.pattern, a)).map((e) => e.route);
|
|
469
|
+
if (l.length === 0) return {
|
|
470
|
+
status: 404,
|
|
471
|
+
headers: {},
|
|
472
|
+
text: "",
|
|
473
|
+
body: {
|
|
474
|
+
error: "No route mounted for this path",
|
|
475
|
+
mounted: r.map((e) => e.path)
|
|
476
|
+
},
|
|
477
|
+
stream: !1
|
|
478
|
+
};
|
|
479
|
+
let u = await ve(l, {
|
|
480
|
+
method: e.toUpperCase(),
|
|
481
|
+
path: a,
|
|
482
|
+
query: s,
|
|
483
|
+
headers: {
|
|
484
|
+
...$(o),
|
|
485
|
+
...$(i.headers ?? {})
|
|
486
|
+
},
|
|
487
|
+
rawBody: Qe(i.body),
|
|
488
|
+
store: t.store
|
|
489
|
+
}), d = $(u.headers ?? {});
|
|
490
|
+
u.contentType !== void 0 && (d["content-type"] = u.contentType);
|
|
491
|
+
let f = $e(u.body), p = (u.contentType ?? "").includes("json");
|
|
492
|
+
return {
|
|
493
|
+
status: u.status,
|
|
494
|
+
headers: d,
|
|
495
|
+
text: f,
|
|
496
|
+
body: p && f !== "" ? JSON.parse(f) : void 0,
|
|
497
|
+
stream: u.stream !== void 0
|
|
498
|
+
};
|
|
499
|
+
};
|
|
500
|
+
return {
|
|
501
|
+
actingAs: (e) => a(e, o),
|
|
502
|
+
withHeaders: (e) => a(n, {
|
|
503
|
+
...o,
|
|
504
|
+
...e
|
|
505
|
+
}),
|
|
506
|
+
get: (e, t) => l("GET", e, t ?? {}),
|
|
507
|
+
delete: (e, t) => l("DELETE", e, t ?? {}),
|
|
508
|
+
post: (e, t, n) => l("POST", e, {
|
|
509
|
+
...n ?? {},
|
|
510
|
+
body: t
|
|
511
|
+
}),
|
|
512
|
+
put: (e, t, n) => l("PUT", e, {
|
|
513
|
+
...n ?? {},
|
|
514
|
+
body: t
|
|
515
|
+
}),
|
|
516
|
+
patch: (e, t, n) => l("PATCH", e, {
|
|
517
|
+
...n ?? {},
|
|
518
|
+
body: t
|
|
519
|
+
}),
|
|
520
|
+
request: (e, t, n) => l(e, t, n ?? {}),
|
|
521
|
+
paths: r.map((e) => e.path)
|
|
522
|
+
};
|
|
523
|
+
};
|
|
524
|
+
return a(void 0, {});
|
|
525
|
+
}, tt = (e = {}) => {
|
|
526
|
+
let t = e.tenantId ?? null, n = new te({
|
|
331
527
|
origin: e.origin ?? "test",
|
|
332
528
|
...e.ringSize === void 0 ? {} : { ringSize: e.ringSize }
|
|
333
529
|
});
|
|
334
530
|
return {
|
|
335
531
|
publish: async (e, r, i) => {
|
|
336
|
-
let a = await u.runPromise(u.either(
|
|
532
|
+
let a = await u.runPromise(u.either(de({
|
|
337
533
|
bus: n,
|
|
338
534
|
tenantId: t
|
|
339
535
|
}, e, r, i)));
|
|
@@ -382,7 +578,7 @@ var j = new Set(e), M = 0, N = (e, t) => {
|
|
|
382
578
|
},
|
|
383
579
|
skipSerials: (e, r, i, a) => {
|
|
384
580
|
let o = {
|
|
385
|
-
route:
|
|
581
|
+
route: ee(a?.tenantId ?? t, e.name, r),
|
|
386
582
|
event: e.name,
|
|
387
583
|
origin: "test-gap",
|
|
388
584
|
n: i + 1,
|
|
@@ -396,17 +592,17 @@ var j = new Set(e), M = 0, N = (e, t) => {
|
|
|
396
592
|
n.clear();
|
|
397
593
|
}
|
|
398
594
|
};
|
|
399
|
-
},
|
|
595
|
+
}, nt = (e = {}) => {
|
|
400
596
|
let t = e.id ?? "test", n = e.store ?? new Proxy({}, { get: (e, t) => {
|
|
401
597
|
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.`);
|
|
402
598
|
} });
|
|
403
599
|
return {
|
|
404
600
|
id: t,
|
|
405
|
-
log: e.log ??
|
|
601
|
+
log: e.log ?? be({ scope: `subscribe:${t}` }),
|
|
406
602
|
store: n,
|
|
407
603
|
...e.publish === void 0 ? {} : { publish: e.publish }
|
|
408
604
|
};
|
|
409
|
-
},
|
|
605
|
+
}, rt = (e) => {
|
|
410
606
|
let t = /* @__PURE__ */ new Map();
|
|
411
607
|
for (let n of e) {
|
|
412
608
|
let e = t.get(n.stepName);
|
|
@@ -432,13 +628,13 @@ var j = new Set(e), M = 0, N = (e, t) => {
|
|
|
432
628
|
});
|
|
433
629
|
}
|
|
434
630
|
return n.sort((e, t) => e._startedAt - t._startedAt), n.map(({ _startedAt: e, ...t }) => t);
|
|
435
|
-
},
|
|
631
|
+
}, it = (e) => {
|
|
436
632
|
let t = e.workflows ?? [], n = /* @__PURE__ */ new Map(), r = 0;
|
|
437
633
|
return {
|
|
438
634
|
start: async (i, a) => {
|
|
439
635
|
let o = t.find((e) => e.workflow.name === i);
|
|
440
636
|
if (o === void 0) throw Error(`makeWorkflowRunner: no workflow named '${i}' — pass it in makeWorkflowRunner({ ctx, workflows: [{ workflow, execute }] }).`);
|
|
441
|
-
let s = `wfrun_test_${++r}`, c =
|
|
637
|
+
let s = `wfrun_test_${++r}`, c = Ce(), l = o.workflow.toLayer(o.execute).pipe(f.provideMerge(Se)), d = /* @__PURE__ */ new Date(), p = o.workflow.execute(a).pipe(u.locally(xe, s), u.provide(c.layer), u.provide(l), u.either), m = await u.runPromise(p), h = rt(c.readSteps()), g = /* @__PURE__ */ new Date(), _;
|
|
442
638
|
if (m._tag === "Right") _ = {
|
|
443
639
|
status: "succeeded",
|
|
444
640
|
output: m.right,
|
|
@@ -478,6 +674,6 @@ var j = new Set(e), M = 0, N = (e, t) => {
|
|
|
478
674
|
},
|
|
479
675
|
inspect: async (e) => n.get(e) ?? null
|
|
480
676
|
};
|
|
481
|
-
},
|
|
677
|
+
}, at = 1;
|
|
482
678
|
//#endregion
|
|
483
|
-
export {
|
|
679
|
+
export { P as MockClock, I as MockLLM, F as MockWebhooks, at as TESTING_PRESET_VERSION, We as TEST_TENANT_ID, Je as anonymous, Ke as apiKey, n as changeDelete, r as changeInsert, i as changeSoftDelete, a as changeUpdate, Oe as defineFactory, Z as describeIfAvailable, Ue as describeIfReachable, D as fixtureRow, Me as frozenTime, Y as invoke, X as isTcpReachable, nt as makeSubscribeContext, et as makeTestApp, Ve as makeTestContext, it as makeWorkflowRunner, Pe as mockStore, E as nextSequence, Fe as outboxNudgesOf, G as resetAfterCommit, U as rpcInterceptorFor, K as runAfterCommit, q as runInStoreTransaction, qe as serviceAccount, W as serviceLayerFor, Ye as system, tt as testEventBus, Ge as user, je as withFrozenTime };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voltro/testing",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Test utilities for Voltro apps — deterministic clock,
|
|
3
|
+
"version": "0.34.0",
|
|
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",
|
|
7
7
|
"typescript",
|
|
@@ -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.34.0",
|
|
47
|
+
"@voltro/env": "0.34.0",
|
|
48
|
+
"@voltro/logger": "0.34.0",
|
|
49
|
+
"@voltro/protocol": "0.34.0",
|
|
50
|
+
"@voltro/runtime": "0.34.0",
|
|
51
|
+
"@voltro/workflow": "0.34.0"
|
|
52
52
|
},
|
|
53
53
|
"peerDependencies": {
|
|
54
|
-
"@voltro/client": "0.
|
|
54
|
+
"@voltro/client": "0.34.0",
|
|
55
55
|
"effect": "^3.22.0",
|
|
56
56
|
"react": "^19.0.0",
|
|
57
57
|
"@effect/sql": "^0.52.0"
|