ihsm 0.0.21 → 0.0.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +161 -8
- package/lib/cjs/index.d.ts +517 -56
- package/lib/cjs/index.js +279 -5
- package/lib/cjs/index.js.map +1 -1
- package/lib/cjs/internal/defs.private.d.ts +10 -0
- package/lib/cjs/internal/dispatch.debug.js +2 -1
- package/lib/cjs/internal/dispatch.debug.js.map +1 -1
- package/lib/cjs/internal/dispatch.production.js +2 -1
- package/lib/cjs/internal/dispatch.production.js.map +1 -1
- package/lib/cjs/internal/dispatch.trace.js +8 -1
- package/lib/cjs/internal/dispatch.trace.js.map +1 -1
- package/lib/cjs/internal/hsm.d.ts +7 -1
- package/lib/cjs/internal/hsm.js +36 -3
- package/lib/cjs/internal/hsm.js.map +1 -1
- package/lib/cjs/internal/lookup.d.ts +15 -0
- package/lib/cjs/internal/lookup.js +32 -0
- package/lib/cjs/internal/lookup.js.map +1 -0
- package/lib/cjs/testing.d.ts +279 -0
- package/lib/cjs/testing.js +392 -0
- package/lib/cjs/testing.js.map +1 -0
- package/lib/esm/index.d.ts +517 -56
- package/lib/esm/index.js +275 -5
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/internal/defs.private.d.ts +10 -0
- package/lib/esm/internal/dispatch.debug.js +2 -1
- package/lib/esm/internal/dispatch.debug.js.map +1 -1
- package/lib/esm/internal/dispatch.production.js +2 -1
- package/lib/esm/internal/dispatch.production.js.map +1 -1
- package/lib/esm/internal/dispatch.trace.js +8 -1
- package/lib/esm/internal/dispatch.trace.js.map +1 -1
- package/lib/esm/internal/hsm.d.ts +7 -1
- package/lib/esm/internal/hsm.js +36 -3
- package/lib/esm/internal/hsm.js.map +1 -1
- package/lib/esm/internal/lookup.d.ts +15 -0
- package/lib/esm/internal/lookup.js +29 -0
- package/lib/esm/internal/lookup.js.map +1 -0
- package/lib/esm/testing.d.ts +279 -0
- package/lib/esm/testing.js +370 -0
- package/lib/esm/testing.js.map +1 -0
- package/package.json +20 -4
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.PreloadError = exports.TestPort = void 0;
|
|
18
|
+
exports.mock = mock;
|
|
19
|
+
exports.makeTestPort = makeTestPort;
|
|
20
|
+
exports.makeTestActor = makeTestActor;
|
|
21
|
+
/**
|
|
22
|
+
* `ihsm/testing` — deterministic-simulation testing utilities for ihsm.
|
|
23
|
+
*
|
|
24
|
+
* This entry point is **separate from the core runtime** (`ihsm`): the mock-port machinery,
|
|
25
|
+
* the manual virtual clock, and the full-access test actor live here so they are never bundled
|
|
26
|
+
* into production code that only imports `ihsm`. Import test helpers from `ihsm/testing`:
|
|
27
|
+
*
|
|
28
|
+
* ```ts
|
|
29
|
+
* import { makeHsm, TopState } from 'ihsm'; // production code
|
|
30
|
+
* import { makeTestActor, mock, TestPort } from 'ihsm/testing'; // tests only
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* For convenience this module also re-exports the entire core API, so a test file may import
|
|
34
|
+
* everything it needs from `ihsm/testing` alone.
|
|
35
|
+
*
|
|
36
|
+
* @packageDocumentation
|
|
37
|
+
*/
|
|
38
|
+
const index_1 = require("./index");
|
|
39
|
+
__exportStar(require("./index"), exports);
|
|
40
|
+
class TestPort extends index_1.BasePort {
|
|
41
|
+
_messages = [];
|
|
42
|
+
_preloads = new Map();
|
|
43
|
+
_now = 0;
|
|
44
|
+
_timerSeq = 0;
|
|
45
|
+
_timers = [];
|
|
46
|
+
_cancelled = new Set();
|
|
47
|
+
_randomQueue = [];
|
|
48
|
+
_cryptoRandomQueue = [];
|
|
49
|
+
_uuidQueue = [];
|
|
50
|
+
_byteQueue = [];
|
|
51
|
+
/**
|
|
52
|
+
* Append an entry to the recorded message log (for assertions / golden traces).
|
|
53
|
+
*
|
|
54
|
+
* Callable both from inside the mock (e.g. a port method logging its outbound call) and from a
|
|
55
|
+
* test (e.g. a `Disposable.dispose` closure recording its own teardown into the trace).
|
|
56
|
+
*
|
|
57
|
+
* @param event - Event name or free-form label (e.g. `'connect'`, `'attempt 1: ok'`)
|
|
58
|
+
* @param payload - Optional values associated with the entry
|
|
59
|
+
*/
|
|
60
|
+
record(event, ...payload) {
|
|
61
|
+
this._messages.push({ event, payload: [...payload] });
|
|
62
|
+
}
|
|
63
|
+
_slot(name) {
|
|
64
|
+
let slot = this._preloads.get(name);
|
|
65
|
+
if (slot === undefined) {
|
|
66
|
+
slot = { queue: [], calls: [] };
|
|
67
|
+
this._preloads.set(name, slot);
|
|
68
|
+
}
|
|
69
|
+
return slot;
|
|
70
|
+
}
|
|
71
|
+
/** @internal Set the persistent implementation for a stubbed method (used by `method.default`). */
|
|
72
|
+
_stubDefault(name, impl) {
|
|
73
|
+
this._slot(name).fallback = impl;
|
|
74
|
+
}
|
|
75
|
+
/** @internal Queue a one-shot implementation for a stubbed method (used by `method.once`). */
|
|
76
|
+
_stubOnce(name, impl) {
|
|
77
|
+
this._slot(name).queue.push(impl);
|
|
78
|
+
}
|
|
79
|
+
/** @internal Clear a stubbed method's queued/persistent implementations and recorded calls (`method.reset`). */
|
|
80
|
+
_stubReset(name) {
|
|
81
|
+
const slot = this._slot(name);
|
|
82
|
+
slot.queue.length = 0;
|
|
83
|
+
slot.fallback = undefined;
|
|
84
|
+
slot.calls.length = 0;
|
|
85
|
+
}
|
|
86
|
+
/** @internal The live, typed list of argument tuples a stubbed method was called with (`method.calls`). */
|
|
87
|
+
_stubCalls(name) {
|
|
88
|
+
return this._slot(name).calls;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* @internal Consume the next implementation for a stubbed method. Installed as the body of every
|
|
92
|
+
* `@`{@link mock}-stubbed method by {@link makeTestPort}; not called directly.
|
|
93
|
+
*
|
|
94
|
+
* The call is recorded first (globally in {@link trace} and in the method's {@link Stubbed.calls}),
|
|
95
|
+
* then one-shot stubs (queued via `method.once`) are consumed in order; otherwise the persistent
|
|
96
|
+
* `method.default` implementation runs. With neither set, a {@link PreloadError} naming the method
|
|
97
|
+
* is thrown.
|
|
98
|
+
*
|
|
99
|
+
* @param name - Port method name
|
|
100
|
+
* @param args - Arguments the machine passed to the method
|
|
101
|
+
* @throws {@link PreloadError} if nothing was stubbed for `name`
|
|
102
|
+
*/
|
|
103
|
+
_consumePreload(name, args) {
|
|
104
|
+
const slot = this._slot(name);
|
|
105
|
+
slot.calls.push(args); // typed per-method record
|
|
106
|
+
this.record(name, ...args); // global golden-trace record, even if unstubbed
|
|
107
|
+
const impl = slot.queue.shift() ?? slot.fallback;
|
|
108
|
+
if (impl === undefined) {
|
|
109
|
+
throw new PreloadError(name);
|
|
110
|
+
}
|
|
111
|
+
return impl(...args);
|
|
112
|
+
}
|
|
113
|
+
/** Every recorded message, in order. */
|
|
114
|
+
get messages() {
|
|
115
|
+
return this._messages;
|
|
116
|
+
}
|
|
117
|
+
/** Just the recorded event names, in order. */
|
|
118
|
+
get events() {
|
|
119
|
+
return this._messages.map(m => m.event);
|
|
120
|
+
}
|
|
121
|
+
/** Rendered `event` / `event:arg,arg` strings — convenient for `deep.equal` / `include`. */
|
|
122
|
+
get trace() {
|
|
123
|
+
return this._messages.map(m => (m.payload.length > 0 ? `${m.event}:${m.payload.join(',')}` : m.event));
|
|
124
|
+
}
|
|
125
|
+
/** The most recently recorded message, or `undefined`. */
|
|
126
|
+
get last() {
|
|
127
|
+
return this._messages[this._messages.length - 1];
|
|
128
|
+
}
|
|
129
|
+
/** Number of recorded messages. */
|
|
130
|
+
get count() {
|
|
131
|
+
return this._messages.length;
|
|
132
|
+
}
|
|
133
|
+
/** Clear the recorded message log. */
|
|
134
|
+
clear() {
|
|
135
|
+
this._messages.length = 0;
|
|
136
|
+
}
|
|
137
|
+
_sortTimers() {
|
|
138
|
+
this._timers.sort((a, b) => a.at - b.at || a.id - b.id);
|
|
139
|
+
}
|
|
140
|
+
/** @inheritdoc Port.setTimeout — backed by the virtual clock; fire with {@link TestPort.advance | advance}. */
|
|
141
|
+
setTimeout(callback, millis) {
|
|
142
|
+
const id = ++this._timerSeq;
|
|
143
|
+
this._timers.push({ id, at: this._now + Math.max(0, millis ?? 0), callback });
|
|
144
|
+
this._sortTimers();
|
|
145
|
+
return id;
|
|
146
|
+
}
|
|
147
|
+
/** @inheritdoc Port.clearTimeout */
|
|
148
|
+
clearTimeout(id) {
|
|
149
|
+
if (id === undefined) {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
this._cancelled.add(id);
|
|
153
|
+
const index = this._timers.findIndex(timer => timer.id === id);
|
|
154
|
+
if (index >= 0) {
|
|
155
|
+
this._timers.splice(index, 1);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
/** @inheritdoc Port.setInterval — backed by the virtual clock; fire with {@link TestPort.advance | advance}. */
|
|
159
|
+
setInterval(callback, millis) {
|
|
160
|
+
const id = ++this._timerSeq;
|
|
161
|
+
const repeat = Math.max(0, millis ?? 0);
|
|
162
|
+
this._timers.push({ id, at: this._now + repeat, callback, repeat });
|
|
163
|
+
this._sortTimers();
|
|
164
|
+
return id;
|
|
165
|
+
}
|
|
166
|
+
/** @inheritdoc Port.clearInterval */
|
|
167
|
+
clearInterval(id) {
|
|
168
|
+
this.clearTimeout(id);
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Advance the virtual clock by `millis`, firing every timer whose deadline is reached, in
|
|
172
|
+
* deadline order. Timers scheduled by a fired callback within the same window are **not** run
|
|
173
|
+
* until a later `advance` — mirroring how a deferred re-schedule lands on the next run-to-completion turn.
|
|
174
|
+
*
|
|
175
|
+
* @param millis - Virtual milliseconds to advance (negative values are clamped to `0`)
|
|
176
|
+
*/
|
|
177
|
+
advance(millis) {
|
|
178
|
+
const target = this._now + Math.max(0, millis);
|
|
179
|
+
const due = this._timers.filter(timer => timer.at <= target && !this._cancelled.has(timer.id));
|
|
180
|
+
this._timers.splice(0, due.length);
|
|
181
|
+
for (const timer of due) {
|
|
182
|
+
this._now = timer.at;
|
|
183
|
+
timer.callback();
|
|
184
|
+
if (timer.repeat !== undefined && !this._cancelled.has(timer.id)) {
|
|
185
|
+
this._timers.push({ id: timer.id, at: this._now + timer.repeat, callback: timer.callback, repeat: timer.repeat });
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
this._sortTimers();
|
|
189
|
+
this._now = target;
|
|
190
|
+
}
|
|
191
|
+
/** Current virtual time, in milliseconds since construction. */
|
|
192
|
+
get now() {
|
|
193
|
+
return this._now;
|
|
194
|
+
}
|
|
195
|
+
/** Number of timers still pending (not yet fired or disposed). */
|
|
196
|
+
get pending() {
|
|
197
|
+
return this._timers.length;
|
|
198
|
+
}
|
|
199
|
+
/** Queue values returned by successive {@link TestPort.random | random} calls (FIFO). */
|
|
200
|
+
feedRandom(...values) {
|
|
201
|
+
this._randomQueue.push(...values);
|
|
202
|
+
}
|
|
203
|
+
/** Queue values returned by successive {@link TestPort.cryptoRandom | cryptoRandom} calls (FIFO). */
|
|
204
|
+
feedCryptoRandom(...values) {
|
|
205
|
+
this._cryptoRandomQueue.push(...values);
|
|
206
|
+
}
|
|
207
|
+
/** Queue values returned by successive {@link TestPort.randomUUID | randomUUID} calls (FIFO). */
|
|
208
|
+
feedUUID(...values) {
|
|
209
|
+
this._uuidQueue.push(...values);
|
|
210
|
+
}
|
|
211
|
+
/** Queue bytes used to fill arrays in successive {@link TestPort.getRandomValues | getRandomValues} calls (FIFO). */
|
|
212
|
+
feedRandomBytes(...bytes) {
|
|
213
|
+
this._byteQueue.push(...bytes);
|
|
214
|
+
}
|
|
215
|
+
/** Clear all scripted random queues ({@link TestPort.feedRandom | feedRandom} / {@link TestPort.feedCryptoRandom | feedCryptoRandom} / …). */
|
|
216
|
+
resetRandom() {
|
|
217
|
+
this._randomQueue.length = 0;
|
|
218
|
+
this._cryptoRandomQueue.length = 0;
|
|
219
|
+
this._uuidQueue.length = 0;
|
|
220
|
+
this._byteQueue.length = 0;
|
|
221
|
+
}
|
|
222
|
+
/** @inheritdoc RandomService.random — queued values only; returns `0` when empty (never calls `Math.random`). */
|
|
223
|
+
random() {
|
|
224
|
+
const next = this._randomQueue.shift();
|
|
225
|
+
return next ?? 0;
|
|
226
|
+
}
|
|
227
|
+
/** @inheritdoc RandomService.cryptoRandom — queued values only; returns `0` when empty (never calls `crypto.random`). */
|
|
228
|
+
cryptoRandom() {
|
|
229
|
+
const next = this._cryptoRandomQueue.shift();
|
|
230
|
+
return next ?? 0;
|
|
231
|
+
}
|
|
232
|
+
/** @inheritdoc RandomService.randomUUID — queued values only; never calls `crypto.randomUUID`. */
|
|
233
|
+
randomUUID() {
|
|
234
|
+
return this._uuidQueue.shift() ?? '00000000-0000-0000-0000-000000000000';
|
|
235
|
+
}
|
|
236
|
+
/** @inheritdoc RandomService.getRandomValues — queued bytes only; never calls `crypto.getRandomValues`. */
|
|
237
|
+
getRandomValues(array) {
|
|
238
|
+
const view = new Uint8Array(array.buffer, array.byteOffset, array.byteLength);
|
|
239
|
+
for (let index = 0; index < view.length; index++) {
|
|
240
|
+
view[index] = this._byteQueue.shift() ?? 0;
|
|
241
|
+
}
|
|
242
|
+
return array;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
exports.TestPort = TestPort;
|
|
246
|
+
/**
|
|
247
|
+
* Thrown when an abstract `@`{@link mock} port method is called but the test scripted no
|
|
248
|
+
* implementation for it. The message names the method so the fix is obvious.
|
|
249
|
+
*
|
|
250
|
+
* @category Testing
|
|
251
|
+
*/
|
|
252
|
+
class PreloadError extends Error {
|
|
253
|
+
constructor(method) {
|
|
254
|
+
super(`ihsm: '${method}()' was called but not stubbed — script it first with port.${method}.default(...) or port.${method}.once(...)`);
|
|
255
|
+
this.name = 'PreloadError';
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
exports.PreloadError = PreloadError;
|
|
259
|
+
/** Property names the auto-stub must never synthesize — JS/test-framework probes and built-in port services. */
|
|
260
|
+
const NON_STUB_PROPS = new Set(['then', 'catch', 'finally', 'toJSON', 'inspect', 'asymmetricMatch', '$$typeof', 'nodeType', 'setTimeout', 'setInterval', 'clearTimeout', 'clearInterval', 'random', 'cryptoRandom', 'randomUUID', 'getRandomValues', 'advance', 'now', 'pending', 'feedRandom', 'feedCryptoRandom', 'feedUUID', 'feedRandomBytes', 'resetRandom']);
|
|
261
|
+
function buildMethodStub(target, name) {
|
|
262
|
+
const stub = (...args) => target._consumePreload(name, args);
|
|
263
|
+
stub.default = (impl) => {
|
|
264
|
+
target._stubDefault(name, impl);
|
|
265
|
+
return stub;
|
|
266
|
+
};
|
|
267
|
+
stub.once = (impl) => {
|
|
268
|
+
target._stubOnce(name, impl);
|
|
269
|
+
return stub;
|
|
270
|
+
};
|
|
271
|
+
stub.reset = () => {
|
|
272
|
+
target._stubReset(name);
|
|
273
|
+
return stub;
|
|
274
|
+
};
|
|
275
|
+
Object.defineProperty(stub, 'calls', { get: () => target._stubCalls(name), enumerable: false });
|
|
276
|
+
return stub;
|
|
277
|
+
}
|
|
278
|
+
/** Marker set by {@link mock} so {@link makeTestPort} can verify the class was decorated. */
|
|
279
|
+
const MOCK_MARKER = Symbol('ihsm.mock');
|
|
280
|
+
/**
|
|
281
|
+
* Class decorator that turns an **abstract** {@link TestPort} subclass into a preloadable mock.
|
|
282
|
+
*
|
|
283
|
+
* Declare each port method as an `abstract` member whose **signature matches the real port**, and
|
|
284
|
+
* decorate the class with `@`{@link mock}. Instances (built with {@link makeTestPort}) then expose
|
|
285
|
+
* every abstract method as a scriptable {@link Stubbed} method: it records each call and returns
|
|
286
|
+
* whatever the test scripted with `method.default(...)` / `method.once(...)`. You never write a
|
|
287
|
+
* method body — the behavior lives in the test, so one mock serves many scenarios.
|
|
288
|
+
*
|
|
289
|
+
* Concrete members (public fields holding device state, helper methods) are left untouched; only the
|
|
290
|
+
* abstract port methods are auto-stubbed.
|
|
291
|
+
*
|
|
292
|
+
* @example
|
|
293
|
+
* ```ts
|
|
294
|
+
* @ihsmTest.mock
|
|
295
|
+
* abstract class WatcherMock extends ihsmTest.TestPort<WatcherTop> {
|
|
296
|
+
* abstract watch(path: string): ihsm.ResultWithSubscription<number>; // signature matches the port
|
|
297
|
+
* }
|
|
298
|
+
*
|
|
299
|
+
* const port = ihsmTest.makeTestPort(WatcherMock);
|
|
300
|
+
* port.watch.default(path => ({ value: 1, subscription: { dispose: () => port.record('dispose') } }));
|
|
301
|
+
* ```
|
|
302
|
+
*
|
|
303
|
+
* @category Testing
|
|
304
|
+
*/
|
|
305
|
+
function mock(Ctor) {
|
|
306
|
+
const Decorated = class extends Ctor {
|
|
307
|
+
constructor(...args) {
|
|
308
|
+
super(...args);
|
|
309
|
+
const stubs = new Map();
|
|
310
|
+
const proxy = new Proxy(this, {
|
|
311
|
+
get(t, prop, receiver) {
|
|
312
|
+
if (typeof prop === 'string' && !(prop in t) && !NON_STUB_PROPS.has(prop)) {
|
|
313
|
+
let stub = stubs.get(prop);
|
|
314
|
+
if (stub === undefined) {
|
|
315
|
+
stub = buildMethodStub(t, prop);
|
|
316
|
+
stubs.set(prop, stub);
|
|
317
|
+
}
|
|
318
|
+
return stub;
|
|
319
|
+
}
|
|
320
|
+
return Reflect.get(t, prop, receiver);
|
|
321
|
+
},
|
|
322
|
+
});
|
|
323
|
+
return proxy;
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
Decorated[MOCK_MARKER] = true;
|
|
327
|
+
return Decorated;
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Instantiate an `@`{@link mock}-decorated mock class — the canonical way to build a test port.
|
|
331
|
+
*
|
|
332
|
+
* Pass the mock **class**; you get back a typed instance whose abstract port methods (inferred from
|
|
333
|
+
* the machine's {@link TopState}) are scriptable {@link Stubbed} methods, ready for
|
|
334
|
+
* `port.method.default(...)`. The port's `actor` is still bound lazily when you hand the instance to
|
|
335
|
+
* {@link makeActor} / {@link makeTestActor}.
|
|
336
|
+
*
|
|
337
|
+
* @typeParam P - The mock class instance type (a {@link TestPort} subclass)
|
|
338
|
+
* @param PortClass - The `@mock`-decorated mock class
|
|
339
|
+
* @returns A scriptable {@link Mock} instance
|
|
340
|
+
* @throws If `PortClass` was not decorated with `@`{@link mock}
|
|
341
|
+
*
|
|
342
|
+
* @example
|
|
343
|
+
* ```ts
|
|
344
|
+
* const port = ihsmTest.makeTestPort(WatcherMock);
|
|
345
|
+
* port.watch.default(() => ({ value: 1, subscription: { dispose: () => port.send('onClosed') } }));
|
|
346
|
+
* const sm = ihsmTest.makeTestActor(WatcherTop, new WatcherCtx(), port);
|
|
347
|
+
* ```
|
|
348
|
+
*
|
|
349
|
+
* @category Testing
|
|
350
|
+
*/
|
|
351
|
+
function makeTestPort(PortClass) {
|
|
352
|
+
if (PortClass[MOCK_MARKER] !== true) {
|
|
353
|
+
throw new Error('ihsm: makeTestPort requires a class decorated with @ihsm.mock');
|
|
354
|
+
}
|
|
355
|
+
return new PortClass();
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Creates a **full-access** actor for deterministic tests: merged protocol + typed `port`.
|
|
359
|
+
*
|
|
360
|
+
* Identical construction to {@link makeActor} (same three mandatory arguments + {@link ActorOptions}),
|
|
361
|
+
* but the returned {@link TestActor} exposes the merged {@link Dispatch} protocol — so tests can
|
|
362
|
+
* drive internal events directly (no live port required) — and grants typed access to `port` for
|
|
363
|
+
* asserting outbound interactions or pushing observations.
|
|
364
|
+
*
|
|
365
|
+
* Unlike {@link makeActor}, `traceLevel` defaults to {@link TraceLevel.VERBOSE_DEBUG} so a failing
|
|
366
|
+
* test is fully readable; opt down explicitly via `options.traceLevel` only when you need a quiet run.
|
|
367
|
+
*
|
|
368
|
+
* @typeParam Context - Domain context type
|
|
369
|
+
* @typeParam Public - Public protocol
|
|
370
|
+
* @typeParam Internal - Internal protocol
|
|
371
|
+
* @typeParam P - Port type
|
|
372
|
+
* @param topState - Root state class; `Context` / `Public` / `Internal` are inferred from it (see {@link TopStateArg})
|
|
373
|
+
* @param ctx - Mutable domain object shared by all states
|
|
374
|
+
* @param port - Outbound {@link Port} instance (its `actor` is bound by the factory)
|
|
375
|
+
* @param options - Optional tuning: `initialize` / `traceLevel` / `traceWriter` / … (see {@link ActorOptions})
|
|
376
|
+
* @returns A {@link TestActor} handle with full event + port access
|
|
377
|
+
*
|
|
378
|
+
* @example
|
|
379
|
+
* ```ts
|
|
380
|
+
* const sm = makeTestActor(ConnTop, new ConnCtx(), ihsmTest.makeTestPort(ConnMock));
|
|
381
|
+
* ```
|
|
382
|
+
*
|
|
383
|
+
* @category Factory
|
|
384
|
+
*/
|
|
385
|
+
function makeTestActor(topState, ctx, port, options = {}, ..._disjointGuard) {
|
|
386
|
+
// Tests default to the most verbose trace (so a failing run is fully readable). Never silence to
|
|
387
|
+
// a production level here — the user opts down explicitly via `options.traceLevel`.
|
|
388
|
+
const { initialize = index_1.defaultInitialize, traceLevel = index_1.TraceLevel.VERBOSE_DEBUG, traceWriter = index_1.defaultTraceWriter, dispatchErrorCallback = index_1.defaultDispatchErrorCallback } = options;
|
|
389
|
+
const hsm = (0, index_1.makeHsm)(topState, ctx, initialize, traceLevel, traceWriter, dispatchErrorCallback, port);
|
|
390
|
+
return hsm;
|
|
391
|
+
}
|
|
392
|
+
//# sourceMappingURL=testing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testing.js","sourceRoot":"","sources":["../../src/testing.ts"],"names":[],"mappings":"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"}
|