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
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DispatchErrorCallback, PostedEvent, EventPayload, ServiceResponse, ServiceName, ServiceRequest, StateClass, TraceLevel, TraceWriter } from '../';
|
|
1
|
+
import { Disposable, DispatchErrorCallback, EventObserver, PostedEvent, EventPayload, ServiceResponse, ServiceName, ServiceRequest, StateClass, TraceLevel, TraceWriter } from '../';
|
|
2
2
|
import { HsmWithTracing, Instance, Task, Transition } from './defs.private';
|
|
3
3
|
/** @internal */
|
|
4
4
|
export declare class HsmObject<Context, Protocol extends {} | undefined> implements HsmWithTracing<Context, Protocol> {
|
|
@@ -14,6 +14,7 @@ export declare class HsmObject<Context, Protocol extends {} | undefined> impleme
|
|
|
14
14
|
_transitionState?: StateClass<Context, Protocol>;
|
|
15
15
|
_currentEventName?: string;
|
|
16
16
|
_currentEventPayload?: any[];
|
|
17
|
+
private _observers?;
|
|
17
18
|
dispatchErrorCallback: DispatchErrorCallback<Context, Protocol>;
|
|
18
19
|
private _traceLevel;
|
|
19
20
|
private _traceDomainStack;
|
|
@@ -22,6 +23,8 @@ export declare class HsmObject<Context, Protocol extends {} | undefined> impleme
|
|
|
22
23
|
constructor(TopState: StateClass<Context, Protocol>, instance: Instance<Context, Protocol>, traceWriter: TraceWriter, traceLevel: TraceLevel, dispatchErrorCallback: DispatchErrorCallback<Context, Protocol>, initialize: boolean);
|
|
23
24
|
get ctx(): Context;
|
|
24
25
|
set ctx(ctx: Context);
|
|
26
|
+
/** Outbound boundary supplied to (or defaulted by) the factory (see {@link makeHsm}); never `undefined` at runtime. */
|
|
27
|
+
get port(): unknown;
|
|
25
28
|
get eventName(): string;
|
|
26
29
|
get eventPayload(): any[];
|
|
27
30
|
get currentStateName(): string;
|
|
@@ -29,6 +32,9 @@ export declare class HsmObject<Context, Protocol extends {} | undefined> impleme
|
|
|
29
32
|
set currentState(newState: StateClass<Context, Protocol>);
|
|
30
33
|
post<EventName extends keyof Protocol>(eventName: PostedEvent<Protocol, EventName>, ...eventPayload: EventPayload<Protocol, EventName>): void;
|
|
31
34
|
postNow<EventName extends keyof Protocol>(eventName: PostedEvent<Protocol, EventName>, ...eventPayload: EventPayload<Protocol, EventName>): void;
|
|
35
|
+
/** Register a test-only observer; returns a Disposable that removes it. See {@link TestActor.subscribe}. */
|
|
36
|
+
subscribe(observer: EventObserver): Disposable;
|
|
37
|
+
private _notifyObservers;
|
|
32
38
|
deferredPost<EventName extends keyof Protocol>(millis: number, eventName: PostedEvent<Protocol, EventName>, ...eventPayload: EventPayload<Protocol, EventName>): void;
|
|
33
39
|
transition(nextState: StateClass<Context, Protocol>): void;
|
|
34
40
|
unhandled(): never;
|
package/lib/cjs/internal/hsm.js
CHANGED
|
@@ -41,6 +41,7 @@ class HsmObject {
|
|
|
41
41
|
_transitionState;
|
|
42
42
|
_currentEventName;
|
|
43
43
|
_currentEventPayload;
|
|
44
|
+
_observers;
|
|
44
45
|
dispatchErrorCallback;
|
|
45
46
|
_traceLevel;
|
|
46
47
|
_traceDomainStack;
|
|
@@ -75,15 +76,46 @@ class HsmObject {
|
|
|
75
76
|
set ctx(ctx) {
|
|
76
77
|
this._instance.ctx = ctx;
|
|
77
78
|
}
|
|
79
|
+
/** Outbound boundary supplied to (or defaulted by) the factory (see {@link makeHsm}); never `undefined` at runtime. */
|
|
80
|
+
get port() {
|
|
81
|
+
return this._instance.portRef;
|
|
82
|
+
}
|
|
78
83
|
get eventName() { return this._currentEventName; }
|
|
79
84
|
get eventPayload() { return this._currentEventPayload; }
|
|
80
85
|
get currentStateName() { return (0, utils_1.getStateName)(Object.getPrototypeOf(this._instance).constructor); }
|
|
81
86
|
get currentState() { return Object.getPrototypeOf(this._instance).constructor; }
|
|
82
87
|
set currentState(newState) { Object.setPrototypeOf(this._instance, newState.prototype); }
|
|
83
|
-
post(eventName, ...eventPayload) { this.pushTask(this._createEventDispatchTask(this, eventName, ...eventPayload)); }
|
|
84
|
-
postNow(eventName, ...eventPayload) { this.pushHiPriorityTask(this._createEventDispatchTask(this, eventName, ...eventPayload)); }
|
|
88
|
+
post(eventName, ...eventPayload) { this._notifyObservers(eventName, eventPayload); this.pushTask(this._createEventDispatchTask(this, eventName, ...eventPayload)); }
|
|
89
|
+
postNow(eventName, ...eventPayload) { this._notifyObservers(eventName, eventPayload); this.pushHiPriorityTask(this._createEventDispatchTask(this, eventName, ...eventPayload)); }
|
|
90
|
+
/** Register a test-only observer; returns a Disposable that removes it. See {@link TestActor.subscribe}. */
|
|
91
|
+
subscribe(observer) {
|
|
92
|
+
if (this._observers === undefined)
|
|
93
|
+
this._observers = new Set();
|
|
94
|
+
this._observers.add(observer);
|
|
95
|
+
return {
|
|
96
|
+
dispose: () => {
|
|
97
|
+
this._observers?.delete(observer);
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
_notifyObservers(eventName, eventPayload) {
|
|
102
|
+
if (this._observers === undefined || this._observers.size === 0)
|
|
103
|
+
return;
|
|
104
|
+
const message = { event: String(eventName), payload: [...eventPayload] };
|
|
105
|
+
for (const observer of this._observers)
|
|
106
|
+
observer(message);
|
|
107
|
+
}
|
|
85
108
|
deferredPost(millis, eventName, ...eventPayload) {
|
|
86
|
-
|
|
109
|
+
const enqueue = () => { this.pushTask(this._createEventDispatchTask(this, eventName, ...eventPayload)); };
|
|
110
|
+
// Use the port's timer service when available (Port / TestPort always provide setTimeout);
|
|
111
|
+
// fall back to global setTimeout for bare BasePort subclasses that omit it.
|
|
112
|
+
const port = this._instance.portRef;
|
|
113
|
+
if (port !== undefined && typeof port.setTimeout === 'function') {
|
|
114
|
+
port.setTimeout(enqueue, millis);
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
setTimeout(enqueue, Math.max(0, millis));
|
|
118
|
+
}
|
|
87
119
|
}
|
|
88
120
|
transition(nextState) { this._transitionState = nextState; }
|
|
89
121
|
unhandled() { throw new __1.UnhandledEventError(this); }
|
|
@@ -172,6 +204,7 @@ class HsmObject {
|
|
|
172
204
|
return `${this._traceDomainStack.length === 0 ? '' : this._traceDomainStack.join('|') + '|'}`;
|
|
173
205
|
}
|
|
174
206
|
call(eventName, ...eventPayload) {
|
|
207
|
+
this._notifyObservers(eventName, eventPayload);
|
|
175
208
|
return new Promise((resolve, reject) => {
|
|
176
209
|
const taskFactory = this._createEventDispatchTask;
|
|
177
210
|
this.pushTask(taskFactory(this, eventName, ...[resolve, reject, ...eventPayload]));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hsm.js","sourceRoot":"","sources":["../../../src/internal/hsm.ts"],"names":[],"mappings":";;;AAAA,
|
|
1
|
+
{"version":3,"file":"hsm.js","sourceRoot":"","sources":["../../../src/internal/hsm.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { HsmWithTracing } from './defs.private';
|
|
2
|
+
/**
|
|
3
|
+
* Canonical event-handler lookup shared by every dispatch mode (production, debug, verbose).
|
|
4
|
+
*
|
|
5
|
+
* Walks the **state-class constructor chain** starting at `hsm.currentState` and going up to and
|
|
6
|
+
* **including** {@link TopState}, returning the first state that defines `eventName` as an **own**
|
|
7
|
+
* prototype property. Stopping at `TopState` keeps resolution inside the user's state hierarchy: it
|
|
8
|
+
* never falls through to `State.prototype` / `Object.prototype`, so a posted event can never
|
|
9
|
+
* accidentally resolve to a runtime method (`post`, `transition`, `toString`, …). Returns
|
|
10
|
+
* `undefined` when no state owns the handler — the caller then routes the event to `onUnhandled`.
|
|
11
|
+
*
|
|
12
|
+
* The verbose-trace dispatcher narrates this exact algorithm step by step; sharing it here
|
|
13
|
+
* guarantees production / debug / verbose dispatch all resolve handlers identically (proposal T6).
|
|
14
|
+
*/
|
|
15
|
+
export declare function lookupEventHandler<Context, Protocol extends {} | undefined>(hsm: HsmWithTracing<Context, Protocol>, eventName: PropertyKey): ((...args: any[]) => unknown) | undefined;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.lookupEventHandler = lookupEventHandler;
|
|
4
|
+
/** @internal */
|
|
5
|
+
const __1 = require("../");
|
|
6
|
+
/**
|
|
7
|
+
* Canonical event-handler lookup shared by every dispatch mode (production, debug, verbose).
|
|
8
|
+
*
|
|
9
|
+
* Walks the **state-class constructor chain** starting at `hsm.currentState` and going up to and
|
|
10
|
+
* **including** {@link TopState}, returning the first state that defines `eventName` as an **own**
|
|
11
|
+
* prototype property. Stopping at `TopState` keeps resolution inside the user's state hierarchy: it
|
|
12
|
+
* never falls through to `State.prototype` / `Object.prototype`, so a posted event can never
|
|
13
|
+
* accidentally resolve to a runtime method (`post`, `transition`, `toString`, …). Returns
|
|
14
|
+
* `undefined` when no state owns the handler — the caller then routes the event to `onUnhandled`.
|
|
15
|
+
*
|
|
16
|
+
* The verbose-trace dispatcher narrates this exact algorithm step by step; sharing it here
|
|
17
|
+
* guarantees production / debug / verbose dispatch all resolve handlers identically (proposal T6).
|
|
18
|
+
*/
|
|
19
|
+
function lookupEventHandler(hsm, eventName) {
|
|
20
|
+
let state = hsm.currentState;
|
|
21
|
+
while (true) {
|
|
22
|
+
const prototype = state.prototype;
|
|
23
|
+
if (Object.prototype.hasOwnProperty.call(prototype, eventName)) {
|
|
24
|
+
return prototype[eventName];
|
|
25
|
+
}
|
|
26
|
+
if (state === __1.TopState) {
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
state = Object.getPrototypeOf(state);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=lookup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lookup.js","sourceRoot":"","sources":["../../../src/internal/lookup.ts"],"names":[],"mappings":";;AAkBA,gDAYC;AA9BD,gBAAgB;AAChB,2BAA2C;AAI3C;;;;;;;;;;;;GAYG;AACH,SAAgB,kBAAkB,CAA2C,GAAsC,EAAE,SAAsB;IAC1I,IAAI,KAAK,GAAkC,GAAG,CAAC,YAAY,CAAC;IAC5D,OAAO,IAAI,EAAE,CAAC;QACb,MAAM,SAAS,GAAG,KAAK,CAAC,SAAoD,CAAC;QAC7E,IAAI,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,EAAE,CAAC;YAChE,OAAO,SAAS,CAAC,SAAS,CAAgC,CAAC;QAC5D,CAAC;QACD,IAAK,KAAiB,KAAK,YAAQ,EAAE,CAAC;YACrC,OAAO,SAAS,CAAC;QAClB,CAAC;QACD,KAAK,GAAG,MAAM,CAAC,cAAc,CAAC,KAAK,CAAkC,CAAC;IACvE,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ihsm/testing` — deterministic-simulation testing utilities for ihsm.
|
|
3
|
+
*
|
|
4
|
+
* This entry point is **separate from the core runtime** (`ihsm`): the mock-port machinery,
|
|
5
|
+
* the manual virtual clock, and the full-access test actor live here so they are never bundled
|
|
6
|
+
* into production code that only imports `ihsm`. Import test helpers from `ihsm/testing`:
|
|
7
|
+
*
|
|
8
|
+
* ```ts
|
|
9
|
+
* import { makeHsm, TopState } from 'ihsm'; // production code
|
|
10
|
+
* import { makeTestActor, mock, TestPort } from 'ihsm/testing'; // tests only
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
* For convenience this module also re-exports the entire core API, so a test file may import
|
|
14
|
+
* everything it needs from `ihsm/testing` alone.
|
|
15
|
+
*
|
|
16
|
+
* @packageDocumentation
|
|
17
|
+
*/
|
|
18
|
+
import { Any, ActorOptions, BasePort, Disjoint, Dispatch, Disposable, EventObserver, Hsm, MachinePort, PortHandle, RandomService, TimerHandle, TopStateArg, TracedMessage } from './index';
|
|
19
|
+
export * from './index';
|
|
20
|
+
/**
|
|
21
|
+
* Full-access view returned by {@link makeTestActor} for **deterministic testing**.
|
|
22
|
+
*
|
|
23
|
+
* Exposes the merged {@link Dispatch} protocol — so a test can drive internal events
|
|
24
|
+
* (`onSpawn`, `onExit`, …) directly without a live port — **plus** typed access to the
|
|
25
|
+
* machine's `port` for asserting outbound calls or pushing observations.
|
|
26
|
+
*
|
|
27
|
+
* @typeParam Context - Domain context
|
|
28
|
+
* @typeParam Protocol - Public protocol
|
|
29
|
+
* @typeParam InternalProtocol - Internal (port-driven) protocol
|
|
30
|
+
* @typeParam P - Port type
|
|
31
|
+
*
|
|
32
|
+
* @category State machine
|
|
33
|
+
*/
|
|
34
|
+
export type TestActor<Context = Any, Protocol extends {} | undefined = undefined, InternalProtocol extends {} = {}, P = undefined> = Hsm<Context, Dispatch<Protocol, InternalProtocol>> & {
|
|
35
|
+
readonly port: P;
|
|
36
|
+
/**
|
|
37
|
+
* Observe **every** event as it is posted through this machine — a capability unique to the
|
|
38
|
+
* test surface (it is intentionally absent from {@link Actor} / {@link Hsm}).
|
|
39
|
+
*
|
|
40
|
+
* The observer fires synchronously at post time with the event name and payload, capturing
|
|
41
|
+
* client posts, handler self-posts, and port-driven internal events alike. Wire it to
|
|
42
|
+
* {@link TestPort.record} when you want a golden trace on the port under test.
|
|
43
|
+
*
|
|
44
|
+
* @param observer - Callback receiving each {@link TracedMessage}
|
|
45
|
+
* @returns A {@link Disposable} that stops the observation
|
|
46
|
+
*/
|
|
47
|
+
subscribe(observer: EventObserver): Disposable;
|
|
48
|
+
};
|
|
49
|
+
export declare class TestPort<T = Any> extends BasePort<T> implements RandomService {
|
|
50
|
+
private readonly _messages;
|
|
51
|
+
private readonly _preloads;
|
|
52
|
+
private _now;
|
|
53
|
+
private _timerSeq;
|
|
54
|
+
private readonly _timers;
|
|
55
|
+
private readonly _cancelled;
|
|
56
|
+
private readonly _randomQueue;
|
|
57
|
+
private readonly _cryptoRandomQueue;
|
|
58
|
+
private readonly _uuidQueue;
|
|
59
|
+
private readonly _byteQueue;
|
|
60
|
+
/**
|
|
61
|
+
* Append an entry to the recorded message log (for assertions / golden traces).
|
|
62
|
+
*
|
|
63
|
+
* Callable both from inside the mock (e.g. a port method logging its outbound call) and from a
|
|
64
|
+
* test (e.g. a `Disposable.dispose` closure recording its own teardown into the trace).
|
|
65
|
+
*
|
|
66
|
+
* @param event - Event name or free-form label (e.g. `'connect'`, `'attempt 1: ok'`)
|
|
67
|
+
* @param payload - Optional values associated with the entry
|
|
68
|
+
*/
|
|
69
|
+
record(event: string, ...payload: unknown[]): void;
|
|
70
|
+
private _slot;
|
|
71
|
+
/** @internal Set the persistent implementation for a stubbed method (used by `method.default`). */
|
|
72
|
+
_stubDefault(name: string, impl: (...args: unknown[]) => unknown): void;
|
|
73
|
+
/** @internal Queue a one-shot implementation for a stubbed method (used by `method.once`). */
|
|
74
|
+
_stubOnce(name: string, impl: (...args: unknown[]) => unknown): void;
|
|
75
|
+
/** @internal Clear a stubbed method's queued/persistent implementations and recorded calls (`method.reset`). */
|
|
76
|
+
_stubReset(name: string): void;
|
|
77
|
+
/** @internal The live, typed list of argument tuples a stubbed method was called with (`method.calls`). */
|
|
78
|
+
_stubCalls(name: string): unknown[][];
|
|
79
|
+
/**
|
|
80
|
+
* @internal Consume the next implementation for a stubbed method. Installed as the body of every
|
|
81
|
+
* `@`{@link mock}-stubbed method by {@link makeTestPort}; not called directly.
|
|
82
|
+
*
|
|
83
|
+
* The call is recorded first (globally in {@link trace} and in the method's {@link Stubbed.calls}),
|
|
84
|
+
* then one-shot stubs (queued via `method.once`) are consumed in order; otherwise the persistent
|
|
85
|
+
* `method.default` implementation runs. With neither set, a {@link PreloadError} naming the method
|
|
86
|
+
* is thrown.
|
|
87
|
+
*
|
|
88
|
+
* @param name - Port method name
|
|
89
|
+
* @param args - Arguments the machine passed to the method
|
|
90
|
+
* @throws {@link PreloadError} if nothing was stubbed for `name`
|
|
91
|
+
*/
|
|
92
|
+
_consumePreload(name: string, args: unknown[]): unknown;
|
|
93
|
+
/** Every recorded message, in order. */
|
|
94
|
+
get messages(): readonly TracedMessage[];
|
|
95
|
+
/** Just the recorded event names, in order. */
|
|
96
|
+
get events(): readonly string[];
|
|
97
|
+
/** Rendered `event` / `event:arg,arg` strings — convenient for `deep.equal` / `include`. */
|
|
98
|
+
get trace(): readonly string[];
|
|
99
|
+
/** The most recently recorded message, or `undefined`. */
|
|
100
|
+
get last(): TracedMessage | undefined;
|
|
101
|
+
/** Number of recorded messages. */
|
|
102
|
+
get count(): number;
|
|
103
|
+
/** Clear the recorded message log. */
|
|
104
|
+
clear(): void;
|
|
105
|
+
private _sortTimers;
|
|
106
|
+
/** @inheritdoc Port.setTimeout — backed by the virtual clock; fire with {@link TestPort.advance | advance}. */
|
|
107
|
+
setTimeout(callback: () => void, millis?: number): TimerHandle;
|
|
108
|
+
/** @inheritdoc Port.clearTimeout */
|
|
109
|
+
clearTimeout(id: TimerHandle | undefined): void;
|
|
110
|
+
/** @inheritdoc Port.setInterval — backed by the virtual clock; fire with {@link TestPort.advance | advance}. */
|
|
111
|
+
setInterval(callback: () => void, millis?: number): TimerHandle;
|
|
112
|
+
/** @inheritdoc Port.clearInterval */
|
|
113
|
+
clearInterval(id: TimerHandle | undefined): void;
|
|
114
|
+
/**
|
|
115
|
+
* Advance the virtual clock by `millis`, firing every timer whose deadline is reached, in
|
|
116
|
+
* deadline order. Timers scheduled by a fired callback within the same window are **not** run
|
|
117
|
+
* until a later `advance` — mirroring how a deferred re-schedule lands on the next run-to-completion turn.
|
|
118
|
+
*
|
|
119
|
+
* @param millis - Virtual milliseconds to advance (negative values are clamped to `0`)
|
|
120
|
+
*/
|
|
121
|
+
advance(millis: number): void;
|
|
122
|
+
/** Current virtual time, in milliseconds since construction. */
|
|
123
|
+
get now(): number;
|
|
124
|
+
/** Number of timers still pending (not yet fired or disposed). */
|
|
125
|
+
get pending(): number;
|
|
126
|
+
/** Queue values returned by successive {@link TestPort.random | random} calls (FIFO). */
|
|
127
|
+
feedRandom(...values: number[]): void;
|
|
128
|
+
/** Queue values returned by successive {@link TestPort.cryptoRandom | cryptoRandom} calls (FIFO). */
|
|
129
|
+
feedCryptoRandom(...values: number[]): void;
|
|
130
|
+
/** Queue values returned by successive {@link TestPort.randomUUID | randomUUID} calls (FIFO). */
|
|
131
|
+
feedUUID(...values: string[]): void;
|
|
132
|
+
/** Queue bytes used to fill arrays in successive {@link TestPort.getRandomValues | getRandomValues} calls (FIFO). */
|
|
133
|
+
feedRandomBytes(...bytes: number[]): void;
|
|
134
|
+
/** Clear all scripted random queues ({@link TestPort.feedRandom | feedRandom} / {@link TestPort.feedCryptoRandom | feedCryptoRandom} / …). */
|
|
135
|
+
resetRandom(): void;
|
|
136
|
+
/** @inheritdoc RandomService.random — queued values only; returns `0` when empty (never calls `Math.random`). */
|
|
137
|
+
random(): number;
|
|
138
|
+
/** @inheritdoc RandomService.cryptoRandom — queued values only; returns `0` when empty (never calls `crypto.random`). */
|
|
139
|
+
cryptoRandom(): number;
|
|
140
|
+
/** @inheritdoc RandomService.randomUUID — queued values only; never calls `crypto.randomUUID`. */
|
|
141
|
+
randomUUID(): string;
|
|
142
|
+
/** @inheritdoc RandomService.getRandomValues — queued bytes only; never calls `crypto.getRandomValues`. */
|
|
143
|
+
getRandomValues<T extends ArrayBufferView>(array: T): T;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Thrown when an abstract `@`{@link mock} port method is called but the test scripted no
|
|
147
|
+
* implementation for it. The message names the method so the fix is obvious.
|
|
148
|
+
*
|
|
149
|
+
* @category Testing
|
|
150
|
+
*/
|
|
151
|
+
export declare class PreloadError extends Error {
|
|
152
|
+
constructor(method: string);
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* A scriptable abstract port method on a {@link makeTestPort} mock — the per-method analogue of
|
|
156
|
+
* `jest.fn()` / Sinon stubs, fully typed from the machine's {@link TopState}.
|
|
157
|
+
*
|
|
158
|
+
* It stays **callable with the port method's exact signature** (so the machine invokes it normally),
|
|
159
|
+
* and carries the scripting + introspection surface a test drives:
|
|
160
|
+
*
|
|
161
|
+
* - `default(impl)` — set the **persistent** implementation (every call runs it until replaced).
|
|
162
|
+
* - `once(impl)` — queue a **one-shot** implementation, consumed by the next call; queue several to
|
|
163
|
+
* script a sequence. One-shots are consumed before the persistent `default`.
|
|
164
|
+
* - `reset()` — clear queued/persistent implementations **and** the recorded {@link calls}.
|
|
165
|
+
* - `calls` — the live, typed list of argument tuples this method was called with (`Parameters<F>[]`).
|
|
166
|
+
*
|
|
167
|
+
* Both `default` and `once` take a closure with the **same parameters and return type** as the port
|
|
168
|
+
* method, so scripts stay type-safe. Pushing internal events *inward* remains the separate
|
|
169
|
+
* {@link BasePort.send | send} channel.
|
|
170
|
+
*
|
|
171
|
+
* @typeParam A - The method's argument tuple (`Parameters<F>`)
|
|
172
|
+
* @typeParam R - The method's return type (`ReturnType<F>`)
|
|
173
|
+
*
|
|
174
|
+
* @category Testing
|
|
175
|
+
*/
|
|
176
|
+
export interface Stubbed<A extends unknown[], R> {
|
|
177
|
+
(...args: A): R;
|
|
178
|
+
/** Set the persistent implementation run for every call (until replaced). Returns `this` for chaining. */
|
|
179
|
+
default(impl: (...args: A) => R): this;
|
|
180
|
+
/** Queue a one-shot implementation, consumed by the next call (FIFO). Returns `this` for chaining. */
|
|
181
|
+
once(impl: (...args: A) => R): this;
|
|
182
|
+
/** Clear all queued/persistent implementations and the recorded {@link calls}. Returns `this` for chaining. */
|
|
183
|
+
reset(): this;
|
|
184
|
+
/** Argument tuples this method was called with, in order — typed exactly as the port method's parameters. */
|
|
185
|
+
readonly calls: readonly A[];
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* The fully-wired mock type returned by {@link makeTestPort}: the mock class `P` itself, with each
|
|
189
|
+
* **port method** (inferred from the machine's {@link TopState} via {@link MachinePort}) upgraded to
|
|
190
|
+
* a scriptable, introspectable {@link Stubbed} method.
|
|
191
|
+
*
|
|
192
|
+
* @typeParam P - The mock port class instance type
|
|
193
|
+
* @typeParam T - The machine's root {@link TopState} subclass
|
|
194
|
+
*
|
|
195
|
+
* @category Testing
|
|
196
|
+
*/
|
|
197
|
+
export type Mock<P, T> = P & {
|
|
198
|
+
[K in keyof MachinePort<T>]: MachinePort<T>[K] extends (...args: infer A) => infer R ? Stubbed<A, R> : MachinePort<T>[K];
|
|
199
|
+
};
|
|
200
|
+
/**
|
|
201
|
+
* Class decorator that turns an **abstract** {@link TestPort} subclass into a preloadable mock.
|
|
202
|
+
*
|
|
203
|
+
* Declare each port method as an `abstract` member whose **signature matches the real port**, and
|
|
204
|
+
* decorate the class with `@`{@link mock}. Instances (built with {@link makeTestPort}) then expose
|
|
205
|
+
* every abstract method as a scriptable {@link Stubbed} method: it records each call and returns
|
|
206
|
+
* whatever the test scripted with `method.default(...)` / `method.once(...)`. You never write a
|
|
207
|
+
* method body — the behavior lives in the test, so one mock serves many scenarios.
|
|
208
|
+
*
|
|
209
|
+
* Concrete members (public fields holding device state, helper methods) are left untouched; only the
|
|
210
|
+
* abstract port methods are auto-stubbed.
|
|
211
|
+
*
|
|
212
|
+
* @example
|
|
213
|
+
* ```ts
|
|
214
|
+
* @ihsmTest.mock
|
|
215
|
+
* abstract class WatcherMock extends ihsmTest.TestPort<WatcherTop> {
|
|
216
|
+
* abstract watch(path: string): ihsm.ResultWithSubscription<number>; // signature matches the port
|
|
217
|
+
* }
|
|
218
|
+
*
|
|
219
|
+
* const port = ihsmTest.makeTestPort(WatcherMock);
|
|
220
|
+
* port.watch.default(path => ({ value: 1, subscription: { dispose: () => port.record('dispose') } }));
|
|
221
|
+
* ```
|
|
222
|
+
*
|
|
223
|
+
* @category Testing
|
|
224
|
+
*/
|
|
225
|
+
export declare function mock<C extends abstract new (...args: Any[]) => TestPort<Any>>(Ctor: C): C;
|
|
226
|
+
/**
|
|
227
|
+
* Instantiate an `@`{@link mock}-decorated mock class — the canonical way to build a test port.
|
|
228
|
+
*
|
|
229
|
+
* Pass the mock **class**; you get back a typed instance whose abstract port methods (inferred from
|
|
230
|
+
* the machine's {@link TopState}) are scriptable {@link Stubbed} methods, ready for
|
|
231
|
+
* `port.method.default(...)`. The port's `actor` is still bound lazily when you hand the instance to
|
|
232
|
+
* {@link makeActor} / {@link makeTestActor}.
|
|
233
|
+
*
|
|
234
|
+
* @typeParam P - The mock class instance type (a {@link TestPort} subclass)
|
|
235
|
+
* @param PortClass - The `@mock`-decorated mock class
|
|
236
|
+
* @returns A scriptable {@link Mock} instance
|
|
237
|
+
* @throws If `PortClass` was not decorated with `@`{@link mock}
|
|
238
|
+
*
|
|
239
|
+
* @example
|
|
240
|
+
* ```ts
|
|
241
|
+
* const port = ihsmTest.makeTestPort(WatcherMock);
|
|
242
|
+
* port.watch.default(() => ({ value: 1, subscription: { dispose: () => port.send('onClosed') } }));
|
|
243
|
+
* const sm = ihsmTest.makeTestActor(WatcherTop, new WatcherCtx(), port);
|
|
244
|
+
* ```
|
|
245
|
+
*
|
|
246
|
+
* @category Testing
|
|
247
|
+
*/
|
|
248
|
+
export declare function makeTestPort<P extends TestPort<Any>>(PortClass: abstract new () => P): Mock<P, P extends {
|
|
249
|
+
readonly __topState: infer T;
|
|
250
|
+
} ? T : never>;
|
|
251
|
+
/**
|
|
252
|
+
* Creates a **full-access** actor for deterministic tests: merged protocol + typed `port`.
|
|
253
|
+
*
|
|
254
|
+
* Identical construction to {@link makeActor} (same three mandatory arguments + {@link ActorOptions}),
|
|
255
|
+
* but the returned {@link TestActor} exposes the merged {@link Dispatch} protocol — so tests can
|
|
256
|
+
* drive internal events directly (no live port required) — and grants typed access to `port` for
|
|
257
|
+
* asserting outbound interactions or pushing observations.
|
|
258
|
+
*
|
|
259
|
+
* Unlike {@link makeActor}, `traceLevel` defaults to {@link TraceLevel.VERBOSE_DEBUG} so a failing
|
|
260
|
+
* test is fully readable; opt down explicitly via `options.traceLevel` only when you need a quiet run.
|
|
261
|
+
*
|
|
262
|
+
* @typeParam Context - Domain context type
|
|
263
|
+
* @typeParam Public - Public protocol
|
|
264
|
+
* @typeParam Internal - Internal protocol
|
|
265
|
+
* @typeParam P - Port type
|
|
266
|
+
* @param topState - Root state class; `Context` / `Public` / `Internal` are inferred from it (see {@link TopStateArg})
|
|
267
|
+
* @param ctx - Mutable domain object shared by all states
|
|
268
|
+
* @param port - Outbound {@link Port} instance (its `actor` is bound by the factory)
|
|
269
|
+
* @param options - Optional tuning: `initialize` / `traceLevel` / `traceWriter` / … (see {@link ActorOptions})
|
|
270
|
+
* @returns A {@link TestActor} handle with full event + port access
|
|
271
|
+
*
|
|
272
|
+
* @example
|
|
273
|
+
* ```ts
|
|
274
|
+
* const sm = makeTestActor(ConnTop, new ConnCtx(), ihsmTest.makeTestPort(ConnMock));
|
|
275
|
+
* ```
|
|
276
|
+
*
|
|
277
|
+
* @category Factory
|
|
278
|
+
*/
|
|
279
|
+
export declare function makeTestActor<Context, Public extends undefined | {}, Internal extends {} = {}, P extends PortHandle<Context, Internal> = TestPort>(topState: TopStateArg<Context, Public, Internal>, ctx: Context, port: P, options?: ActorOptions<Context, Public, Internal>, ..._disjointGuard: Disjoint<Public, Internal> extends true ? [] : [error: Disjoint<Public, Internal>]): TestActor<Context, Public, Internal, P>;
|