@voltro/testing 0.84.0 → 0.86.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/dist/index.js CHANGED
@@ -1,934 +1,20 @@
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
- import { Cause as l, Effect as u, Either as d, Exit as f, Layer as p, Schema as m } from "effect";
3
- import { ApiUnavailable as h, SubjectService as g, anonymousSubject as _, checkGuardsEffect as v, composeAuthStrategies as y, composeRpcInterceptors as b, eventRoute as x, systemSubject as S, tenantScopedSubject as ee } from "@voltro/protocol";
4
- import { EventBus as te, InMemoryDataStore as ne, NO_ROW_FILTER as C, apiBindingTokenEnv as re, apiBindingUrlEnv as ie, appValidation as ae, applyRowFilterToDescriptor as oe, clearSystemStoreHandle as se, getFieldCipher as ce, getRowFilter as w, getSystemStoreHandle as le, makeAppAccess as ue, makeConnectionState as de, makeDataLoader as fe, makeEffectStoreLayer as T, makeEventPublisher as pe, makeFieldCipher as me, makeOutboxFacade as he, makeSchemaRegistry as ge, mintServiceToken as _e, publishEvent as ve, resolveRequestClientAddress as ye, resolveRowFilterScopeFor as be, resolveTransactionSubject as xe, resolveTrustedProxies as Se, runProvidedEffect as E, runWithDeadlockRetry as Ce, serviceCallerTokenEnv as we, setFieldCipher as Te, setSystemStoreHandle as D, wrapStoreWithMixinBehaviour as O } from "@voltro/runtime";
5
- import { installEnvSnapshot as Ee } from "@voltro/env";
6
- import k from "node:net";
7
- import { describe as De } from "vitest";
8
- import { collectPublicApiRoutes as Oe, dispatchSharedPath as ke, restRoutesToHttpRoutes as Ae } from "@voltro/protocol/rest";
9
- import { createLogger as je } from "@voltro/logger";
10
- import { randomBytes as Me } from "node:crypto";
11
- import { CurrentWorkflowRunId as Ne, getWorkflowJournalCipher as Pe, inMemoryWorkflowEngineLayer as Fe, makeInMemoryRecorder as Ie, setWorkflowJournalCipher as Le } from "@voltro/workflow";
12
- import { spawn as Re } from "node:child_process";
13
- import { createRequire as ze } from "node:module";
14
- import { dirname as Be, join as A } from "node:path";
15
- //#region src/fixtureRow.ts
16
- var Ve = new Set(e), j = 0, M = () => ++j, He = (e, t) => {
17
- if (t.oneOf && t.oneOf.length > 0) return t.oneOf[0];
18
- if (t.enumValues && t.enumValues.length > 0) return t.enumValues[0];
19
- let n = t.unique === !0;
20
- switch (t.type) {
21
- case "text":
22
- case "id":
23
- case "enum": return n ? `${e}-${++j}` : e;
24
- case "reference": return t.referenceStorage === "numeric" ? n ? ++j : 0 : n ? `${e}-${++j}` : e;
25
- case "integer":
26
- case "real": return n ? ++j : 0;
27
- case "decimal":
28
- case "bigint": return n ? String(++j) : "0";
29
- case "boolean": return !1;
30
- case "timestamp":
31
- case "date": return /* @__PURE__ */ new Date(0);
32
- 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}: … }).`);
33
- }
34
- }, N = (e, t = {}) => {
35
- let n = {};
36
- for (let r of s(e, t)) Ve.has(r) || (n[r] = He(r, e.fields[r]));
37
- return {
38
- ...n,
39
- ...t
40
- };
41
- }, P = m.decodeUnknownEither(m.DateFromString), Ue = /^\d{4}-\d{2}-\d{2}$/, We = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/, Ge = (e, t) => {
42
- if (typeof e != "string" || !We.test(e) && !(t === "date" && Ue.test(e))) return !1;
43
- let n = e.slice(0, 10), r = P(n);
44
- return d.isRight(r) && Number.isFinite(r.right.getTime()) && r.right.toISOString().slice(0, 10) === n;
45
- }, Ke = (e, t) => {
46
- let n = Object.entries(e.fields).filter(([, e]) => e.type === "timestamp" || e.type === "date");
47
- return t.map((t, r) => {
48
- let i = { ...t };
49
- for (let [a, o] of n) {
50
- let n = t[a];
51
- if (n == null || n instanceof Date && Number.isFinite(n.getTime())) continue;
52
- let s = P(n);
53
- if (!Ge(n, o.type) || d.isLeft(s) || !Number.isFinite(s.right.getTime())) throw TypeError(`hydrateFixtureRows: table '${e.tableName}', row ${r}, column '${a}' requires a valid Date or ISO date-time with Z or an offset${o.type === "date" ? " (YYYY-MM-DD is also accepted as UTC midnight)" : ""}.`);
54
- i[a] = s.right;
55
- }
56
- return i;
57
- });
58
- }, qe = (e) => {
59
- let t = {};
60
- for (let [n, r] of Object.entries(e)) t[n] = typeof r == "function" ? r(M()) : r;
61
- return t;
62
- }, Je = (e) => {
63
- for (let [t, n] of Object.entries(e.fields)) if (n.type === "id") return t;
64
- return "id";
65
- }, Ye = async (e, t, n, r, i) => {
66
- let a = {};
67
- for (let o of s(t, n)) {
68
- let n = t.fields[o];
69
- if (n === void 0 || n.type !== "reference" || n.references === void 0) continue;
70
- let s = n.references();
71
- 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 }).`);
72
- let c = r[o];
73
- a[o] = (c === void 0 ? await F(e, s, {}, {}, [...i, s.tableName]) : await c.create(e, {}))[n.referenceColumn ?? Je(s)];
74
- }
75
- return a;
76
- }, F = async (e, t, n, r, i) => {
77
- let a = {
78
- ...await Ye(e, t, n, r, i),
79
- ...n
80
- };
81
- return e.insert(t.tableName, N(t, a));
82
- }, Xe = (e, t) => typeof e == "function" ? e(t) : e ?? {}, Ze = (e, t = {}) => {
83
- let n = t.traits ?? {}, r = t.associations ?? {}, i = (t) => {
84
- let a = (e = {}) => ({
85
- ...qe(t),
86
- ...e
87
- }), o = (t = {}) => N(e, a(t));
88
- return {
89
- table: e,
90
- build: o,
91
- buildList: (e, t) => Array.from({ length: e }, (e, n) => o(Xe(t, n))),
92
- create: (t, n = {}) => F(t, e, a(n), r, [e.tableName]),
93
- createList: async (t, n, i) => {
94
- let o = [];
95
- for (let s = 0; s < n; s++) o.push(await F(t, e, a(Xe(i, s)), r, [e.tableName]));
96
- return o;
97
- },
98
- with: (...r) => {
99
- let a = t;
100
- for (let t of r) {
101
- let r = n[t];
102
- if (r === void 0) throw Error(`defineFactory: '${e.tableName}' has no trait '${t}'. Declared: ${Object.keys(n).join(", ") || "(none)"}.`);
103
- a = {
104
- ...a,
105
- ...r
106
- };
107
- }
108
- return i(a);
109
- },
110
- extend: (e) => i({
111
- ...t,
112
- ...e
113
- })
114
- };
115
- };
116
- return i(t.defaults ?? {});
117
- }, I = Symbol.for("voltro/testing/mockClock.installed"), L = () => globalThis, Qe = (e, t) => new Proxy(e, {
118
- construct: (e, n, r) => Reflect.construct(e, n.length === 0 ? [t()] : n, r),
119
- apply: () => new e(t()).toString(),
120
- get: (e, n, r) => n === "now" ? () => t() : Reflect.get(e, n, r)
121
- }), R = (e) => {
122
- if (typeof e == "number") return e;
123
- let t = e instanceof Date ? e.getTime() : Date.parse(e);
124
- if (Number.isNaN(t)) throw Error(`mockClock: cannot read an instant from '${String(e)}'`);
125
- return t;
126
- }, z = class {
127
- currentMs;
128
- constructor(e = "2026-01-01T00:00:00Z") {
129
- this.currentMs = R(e);
130
- }
131
- now() {
132
- return this.currentMs;
133
- }
134
- date() {
135
- return new Date(this.currentMs);
136
- }
137
- advance(e) {
138
- this.currentMs += typeof e == "number" ? e : nt(e);
139
- }
140
- set(e) {
141
- this.currentMs = R(e);
142
- }
143
- get installed() {
144
- return L()[I]?.clock === this;
145
- }
146
- install() {
147
- let e = L(), t = e[I];
148
- 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.");
149
- let n = globalThis.Date;
150
- return e[I] = {
151
- realDate: n,
152
- clock: this
153
- }, globalThis.Date = Qe(n, () => this.currentMs), () => {
154
- this.uninstall();
155
- };
156
- }
157
- uninstall() {
158
- let e = L(), t = e[I];
159
- t !== void 0 && t.clock === this && (globalThis.Date = t.realDate, delete e[I]);
160
- }
161
- }, $e = (e) => typeof e == "object" && !!e && typeof e.then == "function", et = (e, t) => {
162
- let n = e instanceof z ? e : new z(e);
163
- n.install();
164
- let r = !1;
165
- try {
166
- let e = t(n);
167
- return $e(e) ? (r = !0, e.then((e) => (n.uninstall(), e), (e) => {
168
- throw n.uninstall(), e;
169
- })) : e;
170
- } finally {
171
- r || n.uninstall();
172
- }
173
- }, tt = (e) => u.acquireRelease(u.sync(() => {
174
- let t = e instanceof z ? e : new z(e);
175
- return t.install(), t;
176
- }), (e) => u.sync(() => {
177
- e.uninstall();
178
- })), nt = (e) => {
179
- let t = /^\s*(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)\s*$/.exec(e);
180
- if (!t) throw Error(`mockClock: cannot parse duration '${e}'`);
181
- let n = Number(t[1]), r = t[2];
182
- switch (r) {
183
- case "ms": return n;
184
- case "s": return n * 1e3;
185
- case "m": return n * 6e4;
186
- case "h": return n * 36e5;
187
- case "d": return n * 864e5;
188
- default: throw Error(`mockClock: unknown unit '${r}'`);
189
- }
190
- }, B = class {
191
- emitted = [];
192
- async emit(e, t) {
193
- let n = typeof e == "string" ? e : e.name ?? e.id ?? e.event ?? "(unnamed event)";
194
- return this.emitted.push({
195
- event: n,
196
- payload: t,
197
- emittedAt: /* @__PURE__ */ new Date()
198
- }), { delivered: 0 };
199
- }
200
- async listTargets(e, t) {
201
- return [];
202
- }
203
- payloadsFor(e) {
204
- return this.emitted.filter((t) => t.event === e).map((e) => e.payload);
205
- }
206
- last(e) {
207
- for (let t = this.emitted.length - 1; t >= 0; t--) if (this.emitted[t].event === e) return this.emitted[t];
208
- }
209
- clear() {
210
- this.emitted.length = 0;
211
- }
212
- }, V = (e = {}) => {
213
- let t = e.tenantId ?? null, n = new te({
214
- origin: e.origin ?? "test",
215
- ...e.ringSize === void 0 ? {} : { ringSize: e.ringSize }
216
- });
217
- return {
218
- publish: async (e, r, i) => {
219
- let a = await u.runPromise(u.either(ve({
220
- bus: n,
221
- tenantId: t
222
- }, e, r, i)));
223
- if (a._tag === "Left") {
224
- let t = a.left;
225
- throw Error(`testEventBus.publish(${e.name}) was rejected: ${t._tag}`, { cause: t });
226
- }
227
- return { n: a.right.n };
228
- },
229
- subscribe: (e, r, i) => {
230
- let a = [], o = [];
231
- return {
232
- get received() {
233
- return a.map((e) => e.payload);
234
- },
235
- get deliveries() {
236
- return a;
237
- },
238
- get missed() {
239
- return o;
240
- },
241
- get missedCount() {
242
- return o.reduce((e, t) => e + t.count, 0);
243
- },
244
- stop: n.subscribe({
245
- tenantId: i?.tenantId ?? t,
246
- event: e.name,
247
- key: r,
248
- listener: (e) => {
249
- if (e.kind === "gap") {
250
- o.push({
251
- count: e.missed,
252
- reason: e.reason
253
- });
254
- return;
255
- }
256
- a.push({
257
- payload: e.envelope.payload,
258
- origin: e.envelope.origin,
259
- n: e.envelope.n
260
- });
261
- },
262
- ...i?.resume === void 0 ? {} : { options: { resume: i.resume } }
263
- })
264
- };
265
- },
266
- skipSerials: (e, r, i, a) => {
267
- let o = a?.tenantId ?? t, s = {
268
- route: x(o, e.name, r),
269
- event: e.name,
270
- origin: "test-gap",
271
- n: i + 1,
272
- emittedAt: Date.now(),
273
- payload: void 0
274
- };
275
- n.injectRemote(s);
276
- },
277
- bus: n,
278
- reset: () => {
279
- n.clear();
280
- }
281
- };
282
- }, H = class {
283
- queue;
284
- calls = [];
285
- constructor(e) {
286
- this.queue = [...e];
287
- }
288
- next(e) {
289
- this.calls.push(e);
290
- let t = this.queue.shift();
291
- if (!t) throw Error("MockLLM: no more responses queued");
292
- return t;
293
- }
294
- remaining() {
295
- return this.queue.length;
296
- }
297
- }, rt = (e) => e, U = "00000000000000000000000000000000", W = /* @__PURE__ */ new WeakMap(), G = /* @__PURE__ */ new WeakMap(), it = (e, t) => {
298
- let n = G.get(e);
299
- if (n === void 0) throw Error("withRpcConnection requires a context created by makeTestContext");
300
- return n(t);
301
- }, K = /* @__PURE__ */ new WeakMap(), q = /* @__PURE__ */ new WeakMap(), J = /* @__PURE__ */ new WeakMap(), Y = (e) => J.get(e) ?? [], X = /* @__PURE__ */ new WeakMap(), Z = (e, t, n) => {
302
- X.set(e, {
303
- publicApi: t,
304
- ...n === void 0 ? {} : { remoteAddr: n }
305
- });
306
- }, at = (e) => X.get(e), ot = /* @__PURE__ */ new WeakMap(), st = (e, t) => {
307
- let n = J.get(e) ?? [], r = t === "mutation" ? "interceptMutation" : t === "query" ? "interceptQuery" : "interceptAction", i = [];
308
- for (let e of n) {
309
- let t = e[r];
310
- typeof t == "function" && i.push(t);
311
- }
312
- return b(i);
313
- }, ct = (e) => {
314
- let t = (J.get(e) ?? []).map((e) => e.services).filter((e) => e !== void 0), n = ot.get(e) ?? [], r = [...t, ...n];
315
- if (r.length !== 0) return p.mergeAll(...r);
316
- }, lt = (e) => {
317
- let t = K.get(e);
318
- t !== void 0 && (t.length = 0);
319
- }, Q = async (e) => {
320
- let t = K.get(e);
321
- if (t === void 0 || t.length === 0) return;
322
- let n = [...t];
323
- t.length = 0;
324
- for (let e of n) await e();
325
- }, ut = (e) => q.get(e) ?? [], dt = async (e, t) => {
326
- let n = W.get(e);
327
- return n === void 0 ? e.store.transactional(async (n) => t({
328
- ...e,
329
- store: n
330
- })) : n(t);
331
- }, ft = (e) => {
332
- let t = /* @__PURE__ */ new Map(), n = /* @__PURE__ */ new Map(), r = (n) => {
333
- let r = t.get(n);
334
- return r === void 0 ? !1 : r.expiresAt !== null && r.expiresAt <= e() ? (t.delete(n), !1) : !0;
335
- }, i = (r, i, a, o) => {
336
- if (t.set(r, {
337
- value: i,
338
- expiresAt: a === void 0 ? null : e() + a
339
- }), o) for (let e of o) {
340
- let t = n.get(e) ?? /* @__PURE__ */ new Set();
341
- t.add(r), n.set(e, t);
342
- }
343
- };
344
- return {
345
- get: async (e) => r(e) ? t.get(e).value : void 0,
346
- set: async (e, t, n) => {
347
- i(e, t, n?.ttlMs);
348
- },
349
- setWithTags: async (e, t, n) => {
350
- i(e, t, n.ttlMs, n.tags);
351
- },
352
- remove: async (e) => t.delete(e),
353
- has: async (e) => r(e),
354
- clear: async () => {
355
- t.clear(), n.clear();
356
- },
357
- invalidateTag: async (e) => {
358
- let r = n.get(e);
359
- if (r === void 0) return 0;
360
- let i = 0;
361
- for (let e of r) t.delete(e) && i++;
362
- return n.delete(e), i;
363
- },
364
- wrap: async (e, n, a) => {
365
- if (r(e)) return t.get(e).value;
366
- let o = await a();
367
- return i(e, o, n.ttlMs, n.tags), o;
368
- }
369
- };
370
- }, pt = (e) => {
371
- let t = /* @__PURE__ */ new Map(), n = (n) => {
372
- let r = t.get(n);
373
- return r === void 0 ? !1 : r.expiresAt !== null && r.expiresAt <= e() ? (t.delete(n), !1) : !0;
374
- };
375
- return {
376
- get: async (e) => n(e) ? t.get(e).value : void 0,
377
- getOrElse: async (e, r) => n(e) ? t.get(e).value : r(),
378
- set: async (n, r, i) => {
379
- t.set(n, {
380
- value: r,
381
- expiresAt: i?.ttlMs === void 0 ? null : e() + i.ttlMs
382
- });
383
- },
384
- delete: async (e) => {
385
- let r = n(e);
386
- return t.delete(e), r;
387
- },
388
- has: async (e) => n(e),
389
- list: async (e) => {
390
- let r = [];
391
- for (let i of t.keys()) n(i) && (e === "" || i.startsWith(e)) && r.push(i);
392
- return r;
393
- },
394
- clear: async () => {
395
- t.clear();
396
- }
397
- };
398
- }, mt = async (e, t) => {
399
- let n = le();
400
- D(e);
401
- try {
402
- return await t();
403
- } finally {
404
- n === void 0 ? se() : D(n);
405
- }
406
- }, ht = /* @__PURE__ */ new Set(), gt = (e) => {
407
- ht.has(e) || (ht.add(e), console.warn(e));
408
- }, _t = (e, t, n) => {
409
- let r = vt, i;
410
- return () => {
411
- let a = t ?? w();
412
- return t !== void 0 && w() !== void 0 && t !== w() && gt("[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 = mt(n, () => E(be(a, e, (e) => {
413
- console.error("[voltro:testing] row filter failed to load — reads refused for this subject", e);
414
- })))), i;
415
- };
416
- }, vt = Symbol("unresolved"), yt = (e, t) => new Proxy(e, { get: (e, n) => {
417
- if (n === "query") return async (n) => e.query(oe(await t(), n));
418
- let r = Reflect.get(e, n, e);
419
- return typeof r == "function" ? r.bind(e) : r;
420
- } }), bt = (e = {}) => {
421
- if (Ee({
422
- ...process.env,
423
- ...e.env ?? {}
424
- }), e.fieldEncryptionKey !== void 0 && Te(me(e.fieldEncryptionKey)), e.relations !== void 0) {
425
- o();
426
- for (let t of e.relations) c(t);
427
- }
428
- let n = e.tables ?? t(), r = ge(n), i = new ne(e.store ?? {});
429
- for (let t of e.plugins ?? []) t.bindDataStore?.(i);
430
- let a = {
431
- dataStore: i,
432
- schemaRegistry: r
433
- };
434
- D(a);
435
- let s = new z(e.clockStart), l = new B(), u = V(), d = new H(e.llmResponses ?? []), f = ft(() => s.now()), p = pt(() => s.now()), m = (t, n, o = i, c, h = !1) => {
436
- let g = {
437
- subject: t,
438
- traceId: U,
439
- ...n === void 0 ? {} : { connection: n }
440
- }, _ = c?.queued ?? [], v = c?.nudges ?? [], y = pe({
441
- bus: u.bus,
442
- tenantId: t.tenantId ?? null,
443
- ...o === i ? {} : { deferToCommit: (e) => {
444
- _.push(async () => {
445
- e();
446
- });
447
- } }
448
- }), b = fe({ store: o }), x = h ? async () => C : _t(t, e.rowFilter, a), S = {
449
- apis: e.apis ?? Object.freeze({}),
450
- transaction: async (e, r) => {
451
- let a = xe(t, r?.tenantId, o !== i);
452
- if (a !== t) return m(a, n, i, void 0, !0).transaction(e);
453
- if (o !== i) return e(S);
454
- let s, c = await o.transactional(async (r) => {
455
- let i = m(t, n, r, {
456
- queued: [],
457
- nudges: []
458
- }, h), a = await e(i);
459
- return s = i, a;
460
- });
461
- return s !== void 0 && (await Q(s), v.push(...ut(s))), c;
462
- },
463
- clock: s,
464
- webhooks: l,
465
- events: y,
466
- eventBus: u,
467
- llm: d,
468
- ...e.ai === void 0 ? {} : { ai: e.ai },
469
- request: g,
470
- ...n === void 0 ? {} : { connectionState: de(n, o === i ? void 0 : (e) => {
471
- _.push(e);
472
- }) },
473
- validation: ae,
474
- access: ue(t),
475
- cache: f,
476
- kv: p,
477
- store: O(yt(o, x), {
478
- subject: t,
479
- schemaRegistry: r,
480
- rowFilter: C
481
- }),
482
- storeForTenant: (e) => O(yt(o, x), {
483
- subject: ee(t, e),
484
- schemaRegistry: r,
485
- rowFilter: C
486
- }),
487
- outbox: he({
488
- store: o,
489
- subject: t,
490
- traceId: U,
491
- nudge: () => {
492
- v.push(U);
493
- },
494
- afterCommit: (e) => {
495
- _.push(e);
496
- }
497
- }),
498
- load: b.load,
499
- loadMany: b.loadMany,
500
- withSubject: (e, t) => Promise.resolve(t(m(e, n, o, {
501
- queued: _,
502
- nudges: v
503
- }))),
504
- withTenant: (e, r) => Promise.resolve(r(m({
505
- ...t,
506
- tenantId: e
507
- }, n, o, {
508
- queued: _,
509
- nudges: v
510
- }))),
511
- asSubject: (e) => m(e, n, o, {
512
- queued: _,
513
- nudges: v
514
- }),
515
- asTenant: (e) => m({
516
- ...t,
517
- tenantId: e
518
- }, n, o, {
519
- queued: _,
520
- nudges: v
521
- })
522
- };
523
- return K.set(S, _), G.set(S, (e) => m(t, e, o, {
524
- queued: _,
525
- nudges: v
526
- })), q.set(S, v), J.set(S, e.plugins ?? []), ot.set(S, e.layers ?? []), W.set(S, (e) => o.transactional((r) => e(m(t, n, r, {
527
- queued: _,
528
- nudges: v
529
- })))), S;
530
- };
531
- return m(e.subject ?? _(null), e.connection);
532
- }, xt = (e, t) => {
533
- let n = ct(t);
534
- return (n === void 0 ? e : u.provide(e, n)).pipe(u.provide(T(t.store)), u.provideService(g, t.request.subject));
535
- }, St = async (e, t, n, r) => {
536
- let i = e.input, a = await m.decodeUnknownPromise(i)(n), o = e.kind, s = async (e) => {
537
- let n = t(a, e);
538
- return u.isEffect(n) ? E(xt(n, e)) : n;
539
- }, c = async () => {
540
- let t = e.guards;
541
- if (t !== void 0 && t.length > 0) {
542
- let e = await u.runPromise(v(r.request.subject, t, a));
543
- if (e !== null) throw e;
544
- }
545
- if (o !== "mutation") return s(r);
546
- let n = await Ce(async () => dt(r, async (e) => (lt(r), s(e))), { delay: () => Promise.resolve() });
547
- return await Q(r), n;
548
- }, d = o === "mutation" || o === "query" || o === "action" ? o : void 0, p = d === void 0 ? void 0 : st(r, d);
549
- if (p === void 0 || d === void 0) return await c();
550
- let h = u.tryPromise({
551
- try: () => c(),
552
- catch: (e) => e
553
- }), g = at(r), _ = p(h, {
554
- tag: e.name ?? "",
555
- kind: d,
556
- input: a,
557
- subject: r.request.subject,
558
- traceId: r.request.traceId,
559
- ...r.request.spanId === void 0 ? {} : { spanId: r.request.spanId },
560
- ...g === void 0 ? {} : {
561
- surface: "rest",
562
- publicApi: g.publicApi
563
- },
564
- ...g?.remoteAddr === void 0 ? {} : { remoteAddr: g.remoteAddr }
565
- }), y = await u.runPromiseExit(_);
566
- if (f.isSuccess(y)) return y.value;
567
- throw l.squash(y.cause);
568
- }, Ct = (e) => new Promise((t) => {
569
- let n = new k.Socket(), r = (e) => {
570
- clearTimeout(i), n.destroy(), t(e);
571
- }, i = setTimeout(() => r(!1), e.timeoutMs ?? 750);
572
- n.once("connect", () => r(!0)), n.once("error", () => r(!1)), n.connect(e.port, e.host);
573
- }), wt = async (e, t, n, r) => {
574
- 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.`);
575
- let i = !1;
576
- try {
577
- i = await n();
578
- } catch {
579
- i = !1;
580
- }
581
- De.skipIf(!i)(i ? e : `${e} [SKIPPED: needs ${t}]`, r);
582
- }, Tt = async (e, t, n) => {
583
- await wt(e, `${t.name} at ${t.host}:${t.port}`, () => Ct(t), n);
584
- }, Et = async (e, t) => {
585
- try {
586
- let n = await t();
587
- n !== void 0 && n !== "" && console.log(`[engine] ${e}: ${n}`);
588
- } catch {}
589
- }, Dt = "test-tenant", $ = (e, t, n) => ({
590
- type: e,
591
- id: t,
592
- tenantId: n.tenantId ?? "test-tenant",
593
- scopes: n.scopes ?? [],
594
- ...n.metadata === void 0 ? {} : { metadata: n.metadata }
595
- }), Ot = (e, t = {}) => $("user", e, t), kt = (e, t = {}) => $("apiKey", e, t), At = (e, t = {}) => $("serviceAccount", e, t), jt = (e = null) => _(e), Mt = (e = "job:test", t) => t === void 0 ? S(e) : S(e, t), Nt = "127.0.0.1", Pt = (e) => {
596
- let t = e.indexOf("?");
597
- return t === -1 ? {
598
- path: e,
599
- query: ""
600
- } : {
601
- path: e.slice(0, t),
602
- query: e.slice(t + 1)
603
- };
604
- }, Ft = (e, t) => {
605
- let n = e.split("/").filter((e) => e.length > 0), r = t.split("/").filter((e) => e.length > 0);
606
- return n.length === r.length && n.every((e, t) => e.startsWith(":") || e === r[t]);
607
- }, It = (e) => {
608
- let t = {};
609
- for (let [n, r] of Object.entries(e)) t[n.toLowerCase()] = r;
610
- return t;
611
- }, Lt = (e) => e === void 0 ? /* @__PURE__ */ new Uint8Array() : new TextEncoder().encode(JSON.stringify(e)), Rt = (e) => e === void 0 ? "" : typeof e == "string" ? e : new TextDecoder().decode(e), zt = (e) => {
612
- let t = Se(e.security?.trustedProxies, process.env, e.security?.clientAddressHeader), { ctx: n } = e, r = Oe((e.publicApi ?? []).map((e) => ({
613
- descriptor: e.descriptor,
614
- invoke: (t, r) => n.withSubject(r.subject, (n) => (e.descriptor.publicApi !== void 0 && Z(n, e.descriptor.publicApi, r.remoteAddr), St(e.descriptor, e.handler, t, n)))
615
- })), {
616
- pluginErrors: Y(n).flatMap((e) => e.errorSchemas ?? []),
617
- ...e.publicApiProfile === void 0 ? {} : { profile: e.publicApiProfile }
618
- }), i = [...e.restRoutes ?? [], ...r], a = y(e.strategies ?? [], {
619
- anonymous: e.anonymous ?? { headerOrTenant: "acme" },
620
- getStore: () => n.store
621
- }), o = (r, s) => {
622
- let c = {
623
- store: n.store,
624
- buildContext: () => n,
625
- resolveSubject: r === void 0 ? async (e) => (await a({
626
- headers: e,
627
- clientId: -1
628
- })).subject : () => r,
629
- ...e.idempotency === void 0 ? {} : { idempotency: e.idempotency }
630
- }, l = Ae(i, c).map((e, t) => ({
631
- pattern: i[t].path,
632
- route: e
633
- })), u = async (e, r, a = {}) => {
634
- let { path: o, query: c } = Pt(r), u = l.filter((e) => Ft(e.pattern, o)).map((e) => e.route);
635
- if (u.length === 0) return {
636
- status: 404,
637
- headers: {},
638
- text: "",
639
- body: {
640
- error: "No route mounted for this path",
641
- mounted: i.map((e) => e.path)
642
- },
643
- stream: !1
644
- };
645
- let d = {
646
- ...It(s),
647
- ...It(a.headers ?? {})
648
- }, f = ye({
649
- socketAddress: a.remoteAddr ?? Nt,
650
- headers: d,
651
- config: t
652
- }), p = {
653
- method: e.toUpperCase(),
654
- path: o,
655
- query: c,
656
- headers: d,
657
- rawBody: Lt(a.body),
658
- store: n.store,
659
- ...f === void 0 ? {} : { remoteAddr: f }
660
- }, m = await ke(u, p), h = {};
661
- for (let [e, t] of Object.entries(m.headers ?? {})) h[e.toLowerCase()] = t;
662
- m.contentType !== void 0 && (h["content-type"] = m.contentType);
663
- let g = Rt(m.body), _ = (m.contentType ?? "").includes("json");
664
- return {
665
- status: m.status,
666
- headers: h,
667
- text: g,
668
- body: _ && g !== "" ? JSON.parse(g) : void 0,
669
- stream: m.stream !== void 0
670
- };
671
- };
672
- return {
673
- actingAs: (e) => o(e, s),
674
- withHeaders: (e) => o(r, {
675
- ...s,
676
- ...e
677
- }),
678
- get: (e, t) => u("GET", e, t ?? {}),
679
- delete: (e, t) => u("DELETE", e, t ?? {}),
680
- post: (e, t, n) => u("POST", e, {
681
- ...n ?? {},
682
- body: t
683
- }),
684
- put: (e, t, n) => u("PUT", e, {
685
- ...n ?? {},
686
- body: t
687
- }),
688
- patch: (e, t, n) => u("PATCH", e, {
689
- ...n ?? {},
690
- body: t
691
- }),
692
- request: (e, t, n) => u(e, t, n ?? {}),
693
- paths: i.map((e) => e.path)
694
- };
695
- };
696
- return o(void 0, {});
697
- }, Bt = (e = {}) => {
698
- let t = e.id ?? "test", n = e.store ?? new Proxy({}, { get: (e, t) => {
699
- 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.`);
700
- } });
701
- return {
702
- id: t,
703
- log: e.log ?? je({ scope: `subscribe:${t}` }),
704
- store: n,
705
- ...e.publish === void 0 ? {} : { publish: e.publish }
706
- };
707
- }, Vt = (e) => {
708
- let t = /* @__PURE__ */ new Map();
709
- for (let n of e) {
710
- let e = t.get(n.stepName);
711
- e === void 0 ? t.set(n.stepName, [n]) : e.push(n);
712
- }
713
- let n = [];
714
- for (let [e, r] of t) {
715
- r.sort((e, t) => e.attempt - t.attempt);
716
- let t = r[r.length - 1], i = t.errorTag !== null || t.errorMessage !== null ? {
717
- tag: t.errorTag,
718
- message: t.errorMessage ?? ""
719
- } : null;
720
- n.push({
721
- name: e,
722
- attempt: t.attempt,
723
- attempts: r.length,
724
- status: t.status,
725
- input: t.input,
726
- output: t.output,
727
- error: i,
728
- durationMs: t.durationMs,
729
- _startedAt: r[0].startedAt.getTime()
730
- });
731
- }
732
- return n.sort((e, t) => e._startedAt - t._startedAt), n.map(({ _startedAt: e, ...t }) => t);
733
- }, Ht = () => {
734
- if (Pe() !== void 0) return;
735
- let e = me(Me(32).toString("hex"));
736
- Le(() => ce() ?? e);
737
- }, Ut = (e) => {
738
- Ht();
739
- let t = e.workflows ?? [], n = /* @__PURE__ */ new Map(), r = 0;
740
- return {
741
- start: async (i, a) => {
742
- let o = t.find((e) => e.workflow.name === i);
743
- if (o === void 0) throw Error(`makeWorkflowRunner: no workflow named '${i}' — pass it in makeWorkflowRunner({ ctx, workflows: [{ workflow, execute }] }).`);
744
- let s = `wfrun_test_${++r}`, c = Ie(), l = o.workflow.toLayer(o.execute).pipe(p.provideMerge(Fe)), d = /* @__PURE__ */ new Date(), f = o.workflow.execute(a).pipe(u.locally(Ne, s), u.provide(c.layer), u.provide(l)), m = (e.ctx.store === void 0 ? f : u.provide(f, T(e.ctx.store))).pipe(u.either), h = await u.runPromise(m), g = Vt(c.readSteps()), _ = /* @__PURE__ */ new Date(), v;
745
- if (h._tag === "Right") v = {
746
- status: "succeeded",
747
- output: h.right,
748
- error: null,
749
- steps: g,
750
- runId: s
751
- };
752
- else {
753
- let e = h.left;
754
- v = {
755
- status: "failed",
756
- output: null,
757
- error: {
758
- tag: e && typeof e == "object" && "_tag" in e ? String(e._tag) : null,
759
- message: e && typeof e == "object" && "message" in e && typeof e.message == "string" ? e.message : String(h.left)
760
- },
761
- steps: g,
762
- runId: s
763
- };
764
- }
765
- return n.set(s, {
766
- id: s,
767
- executionId: s,
768
- name: i,
769
- status: v.status,
770
- input: a,
771
- output: v.output,
772
- subject: e.ctx.request.subject,
773
- source: "test-runner",
774
- steps: g,
775
- startedAt: d,
776
- finishedAt: _,
777
- traceId: null,
778
- parentExecutionId: null,
779
- parentClosePolicy: null
780
- }), v;
781
- },
782
- inspect: async (e) => n.get(e) ?? null
783
- };
784
- }, Wt = (e, t, n = {}) => {
785
- let r = n.name ?? "mock", i = [], a = /* @__PURE__ */ new Map(), o = (e, t) => `${e}\u0000${JSON.stringify(t)}`, s = async (n, a, o) => {
786
- let s = e[n];
787
- if (s === void 0) throw Error(`makeServiceApiMock: '${n}' is not a service procedure of this api`);
788
- let c = s.input, l = m.encodeUnknownSync(c)(a), u = m.decodeUnknownSync(c)(l);
789
- i.push({
790
- tag: n,
791
- input: u,
792
- idempotencyKey: o?.idempotencyKey
793
- });
794
- let d = t[n];
795
- if (d === void 0) throw new h({
796
- api: r,
797
- tag: n,
798
- reason: "transport",
799
- message: "no handler in makeServiceApiMock"
800
- });
801
- let f = await d(u), p = s.output;
802
- return m.decodeUnknownSync(p)(m.encodeUnknownSync(p)(f));
803
- }, c = (e) => {
804
- let t = e.join("."), n = Object.assign((e, n) => s(t, e, n), {
805
- effect: (e, n) => u.tryPromise({
806
- try: () => s(t, e, n),
807
- catch: (e) => e
808
- }),
809
- subscribe: (e, n) => {
810
- let r = o(t, e), i = a.get(r) ?? /* @__PURE__ */ new Set();
811
- return a.set(r, i), i.add(n.onValue), s(t, e).then(n.onValue, (e) => n.onError?.(e)), { unsubscribe: () => {
812
- i.delete(n.onValue);
813
- } };
814
- }
815
- });
816
- return new Proxy(n, { get: (t, n) => {
817
- if (typeof n == "string") {
818
- if (n === "effect" || n === "subscribe") return t[n];
819
- if (n !== "then") return c([...e, n]);
820
- }
821
- } });
822
- };
823
- return {
824
- api: new Proxy({}, { get: (e, t) => typeof t == "string" && t !== "then" ? c([t]) : void 0 }),
825
- calls: (e) => e === void 0 ? [...i] : i.filter((t) => t.tag === e),
826
- emit: (t, n, r) => {
827
- let i = e[t], s = i === void 0 ? r : m.decodeUnknownSync(i.output)(m.encodeUnknownSync(i.output)(r));
828
- for (let e of a.get(o(t, n)) ?? []) e(s);
829
- }
830
- };
831
- }, Gt = () => new Promise((e, t) => {
832
- let n = k.createServer();
833
- n.once("error", t), n.listen(0, "127.0.0.1", () => {
834
- let t = n.address(), r = typeof t == "object" && t ? t.port : 0;
835
- n.close(() => e(r));
836
- });
837
- }), Kt = async (e) => {
838
- try {
839
- return (await fetch(`http://127.0.0.1:${e}/internal/readiness`)).status === 200;
840
- } catch {
841
- return !1;
842
- }
843
- }, qt = (e) => A(Be(ze(A(e, "package.json")).resolve("@voltro/cli/package.json")), "bin", "voltro.mjs"), Jt = 1e4, Yt = (e) => new Promise((t) => {
844
- if (e.exitCode !== null || e.signalCode !== null) return t();
845
- let n = setTimeout(() => {
846
- e.kill("SIGKILL");
847
- }, Jt);
848
- e.once("exit", () => {
849
- clearTimeout(n), t();
850
- }), e.kill("SIGTERM");
851
- }), Xt = async (e) => {
852
- let t = Object.keys(e.apps);
853
- if (t.length === 0) throw Error("startTestApps: no apps given");
854
- let n = e.mode ?? "dev", r = e.bootTimeoutMs ?? 3e5, i = e.cli ?? qt(e.apps[t[0]].root), a = /* @__PURE__ */ new Map();
855
- for (let e of t) a.set(e, await Gt());
856
- let o = /* @__PURE__ */ new Map(), s = new Map(t.map((e) => [e, {}]));
857
- for (let n of e.edges ?? []) {
858
- for (let e of [n.caller, n.callee]) if (!t.includes(e)) throw Error(`startTestApps: edge names '${e}', which is not among the apps (${t.join(", ")})`);
859
- let e = _e();
860
- o.set(`${n.caller}\u0000${n.callee}`, e), Object.assign(s.get(n.caller), {
861
- [re(n.binding)]: e,
862
- [ie(n.binding)]: `ws://127.0.0.1:${a.get(n.callee)}/ws`
863
- }), Object.assign(s.get(n.callee), { [we(n.caller)]: e });
864
- }
865
- let c = [], l = /* @__PURE__ */ new Set(), u = (t) => {
866
- if (!c.includes(t)) {
867
- if (l.has(t)) throw Error(`startTestApps: the edges form a cycle through '${t}' — no app can boot first`);
868
- l.add(t);
869
- for (let n of e.edges ?? []) n.caller === t && u(n.callee);
870
- l.delete(t), c.push(t);
871
- }
872
- };
873
- for (let e of t) u(e);
874
- let d = [], f = /* @__PURE__ */ new Map(), p = async () => {
875
- await Promise.all(d.map(Yt));
876
- };
877
- for (let t of c) {
878
- let o = e.apps[t], c = a.get(t), l = "", u = Re(process.execPath, [
879
- i,
880
- n,
881
- ...n === "serve" ? ["."] : []
882
- ], {
883
- cwd: o.root,
884
- env: {
885
- ...process.env,
886
- ...n === "serve" ? { VOLTRO_SESSION_SECRET: Me(32).toString("base64url") } : {},
887
- ...o.env,
888
- ...s.get(t),
889
- PORT: String(c)
890
- },
891
- stdio: [
892
- "ignore",
893
- "pipe",
894
- "pipe"
895
- ]
896
- });
897
- d.push(u), u.stdout?.on("data", (e) => {
898
- l += e.toString();
899
- }), u.stderr?.on("data", (e) => {
900
- l += e.toString();
901
- }), u.on("exit", (e, t) => {
902
- l += `\n[exited] code=${String(e)} signal=${String(t)}`;
903
- });
904
- let m = {
905
- name: t,
906
- port: c,
907
- url: `http://127.0.0.1:${c}`,
908
- wsUrl: `ws://127.0.0.1:${c}/ws`,
909
- log: () => l,
910
- stop: () => Yt(u)
911
- };
912
- f.set(t, m);
913
- let h = Date.now() + r;
914
- for (; !await Kt(c);) {
915
- if (u.exitCode !== null || u.signalCode !== null || Date.now() > h) {
916
- await p();
917
- let e = u.exitCode !== null || u.signalCode !== null ? "exited before it was ready" : `was not ready within ${r} ms`;
918
- throw Error(`startTestApps: '${t}' ${e}\n${m.log().slice(-4e3)}`);
919
- }
920
- await new Promise((e) => setTimeout(e, 250));
921
- }
922
- }
923
- return {
924
- apps: Object.fromEntries(f),
925
- token: (e, t) => {
926
- let n = o.get(`${e}\u0000${t}`);
927
- if (n === void 0) throw Error(`startTestApps: no edge ${e} → ${t}`);
928
- return n;
929
- },
930
- stop: p
931
- };
932
- }, Zt = 1;
1
+ import { fixtureRow as e, nextSequence as t } from "./fixtureRow.js";
2
+ import { hydrateFixtureRows as n } from "./hydrateFixtureRows.js";
3
+ import { defineFactory as r } from "./factory.js";
4
+ import { MockClock as i, frozenTime as a, withFrozenTime as o } from "./mockClock.js";
5
+ import { MockWebhooks as s } from "./mockWebhooks.js";
6
+ import { testEventBus as c } from "./testEventBus.js";
7
+ import { MockLLM as l } from "./mockLLM.js";
8
+ import { makeTestContext as u, markRestInvocation as d, mockStore as f, outboxNudgesOf as p, pluginsOf as m, resetAfterCommit as h, restInvocationOf as g, rpcInterceptorFor as _, runAfterCommit as v, runInStoreTransaction as y, serviceLayerFor as b, withRpcConnection as x } from "./testContext.js";
9
+ import { invoke as S } from "./invoke.js";
10
+ import { describeIfAvailable as C, describeIfReachable as w, isTcpReachable as T, reportEngineVersion as E } from "./reachable.js";
11
+ import { TEST_TENANT_ID as D, anonymous as O, apiKey as k, serviceAccount as A, system as j, user as M } from "./subject.js";
12
+ import { makeTestApp as N } from "./testRequest.js";
13
+ import { changeDelete as P, changeInsert as F, changeSoftDelete as I, changeUpdate as L, makeSubscribeContext as R } from "./subscribeContext.js";
14
+ import { makeWorkflowRunner as z } from "./workflowRunner.js";
15
+ import { makeServiceApiMock as B } from "./serviceApiMock.js";
16
+ import { startTestApps as V } from "./testApps.js";
17
+ //#region src/index.ts
18
+ var H = 1;
933
19
  //#endregion
934
- export { z as MockClock, H as MockLLM, B as MockWebhooks, Zt as TESTING_PRESET_VERSION, Dt as TEST_TENANT_ID, jt as anonymous, kt as apiKey, n as changeDelete, r as changeInsert, i as changeSoftDelete, a as changeUpdate, Ze as defineFactory, wt as describeIfAvailable, Tt as describeIfReachable, N as fixtureRow, tt as frozenTime, Ke as hydrateFixtureRows, St as invoke, Ct as isTcpReachable, Wt as makeServiceApiMock, Bt as makeSubscribeContext, zt as makeTestApp, bt as makeTestContext, Ut as makeWorkflowRunner, Z as markRestInvocation, rt as mockStore, M as nextSequence, ut as outboxNudgesOf, Y as pluginsOf, Et as reportEngineVersion, lt as resetAfterCommit, at as restInvocationOf, st as rpcInterceptorFor, Q as runAfterCommit, dt as runInStoreTransaction, At as serviceAccount, ct as serviceLayerFor, Xt as startTestApps, Mt as system, V as testEventBus, Ot as user, et as withFrozenTime, it as withRpcConnection };
20
+ export { i as MockClock, l as MockLLM, s as MockWebhooks, H as TESTING_PRESET_VERSION, D as TEST_TENANT_ID, O as anonymous, k as apiKey, P as changeDelete, F as changeInsert, I as changeSoftDelete, L as changeUpdate, r as defineFactory, C as describeIfAvailable, w as describeIfReachable, e as fixtureRow, a as frozenTime, n as hydrateFixtureRows, S as invoke, T as isTcpReachable, B as makeServiceApiMock, R as makeSubscribeContext, N as makeTestApp, u as makeTestContext, z as makeWorkflowRunner, d as markRestInvocation, f as mockStore, t as nextSequence, p as outboxNudgesOf, m as pluginsOf, E as reportEngineVersion, h as resetAfterCommit, g as restInvocationOf, _ as rpcInterceptorFor, v as runAfterCommit, y as runInStoreTransaction, A as serviceAccount, b as serviceLayerFor, V as startTestApps, j as system, c as testEventBus, M as user, o as withFrozenTime, x as withRpcConnection };