@siglo/testing 0.0.0-stage → 1.0.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2017 Jerico de guzman
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,190 @@
1
- # Temporary Holding Version
1
+ # 09. Testing and Replay
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ `@siglo/testing` provides a comprehensive, zero-flake test harness for deterministic execution, step mocking, spy inspection, virtual timers, execution trace snapshots, and production failure replay.
4
+
5
+ ---
6
+
7
+ ## 1. The Workflow Harness
8
+
9
+ Create a test harness wrapping any workflow:
10
+
11
+ ```typescript
12
+ import { createWorkflowHarness } from '@siglo/testing';
13
+ import { orderWorkflow } from './order.workflow';
14
+
15
+ const harness = createWorkflowHarness(orderWorkflow, {
16
+ services: {
17
+ paymentGateway: mockPaymentGateway
18
+ }
19
+ });
20
+
21
+ const result = await harness.run({ orderId: 'ord-100' });
22
+ ```
23
+
24
+ ---
25
+
26
+ ## 2. Spies and Assertions
27
+
28
+ Inspect step calls, attempt counts, inputs, and outputs:
29
+
30
+ ```typescript
31
+ const spy = harness.spyStep('validateOrder');
32
+
33
+ await harness.run({ orderId: 'ord-100' });
34
+
35
+ console.log(spy.called); // true
36
+ console.log(spy.callCount); // 1
37
+ console.log(spy.firstCall()?.input); // { orderId: 'ord-100' }
38
+
39
+ // Built-in assertions
40
+ harness.assertStepExecuted('validateOrder', 1);
41
+ harness.assertStepRetried('chargeCard', 2);
42
+ harness.assertStepFailed('riskyStep', /timeout/);
43
+ ```
44
+
45
+ ---
46
+
47
+ ## 3. Step Mocking
48
+
49
+ Replace third-party dependencies or expensive external APIs without modifying workflow code:
50
+
51
+ ```typescript
52
+ harness.mockStep('callStripe', async (param) => {
53
+ return { chargeId: 'ch_mock_123', status: 'SUCCESS' };
54
+ });
55
+ ```
56
+
57
+ ---
58
+
59
+ ## 4. Fake Clock & Virtual Timers
60
+
61
+ Control virtual timestamps for harness trace records without `setTimeout` delays in tests. Note that `FakeClock` advances its own timestamp counter and `tick()` flushes pending microtasks via `setImmediate`; it does not mock Node's global `Date.now` or `setTimeout`.
62
+
63
+ ```typescript
64
+ import { FakeClock, createWorkflowHarness } from '@siglo/testing';
65
+
66
+ const clock = new FakeClock(1000);
67
+ const harness = createWorkflowHarness(workflow, { clock });
68
+
69
+ // Advance virtual clock timestamp
70
+ clock.advanceTime(5000);
71
+
72
+ // Advance virtual clock and flush microtask loop
73
+ await clock.tick(1000);
74
+ ```
75
+
76
+ ---
77
+
78
+ ## 5. Execution Records and Failure Replay
79
+
80
+ Export sanitized execution records from real executions or staging runs and replay them locally:
81
+
82
+ ```typescript
83
+ // 1. Export sanitized record from execution
84
+ const record = harness.exportExecutionRecord({
85
+ excludeKeys: ['internalSecret']
86
+ });
87
+
88
+ // 2. Replay execution against workflow definition
89
+ import { replayExecution } from '@siglo/testing';
90
+
91
+ const replayResult = await replayExecution(record, orderWorkflow, {
92
+ substituteFixtures: {
93
+ // Replace flaky third-party API with recorded response
94
+ chargeCard: { success: true, txnId: 'txn_999' }
95
+ }
96
+ });
97
+
98
+ console.log(replayResult.matched); // true or false
99
+ console.log(replayResult.discrepancies); // List of mismatches if any
100
+ ```
101
+
102
+ ---
103
+
104
+ ## Minimal Example
105
+
106
+ ```typescript
107
+ import { expect } from 'chai';
108
+ import { createWorkflowHarness } from '@siglo/testing';
109
+ import { Step, flow } from '@siglo/core';
110
+
111
+ const s1 = Step.create('add', (p: { x: number }) => ({ res: p.x + 5 }));
112
+ const wf = flow(s1);
113
+
114
+ const harness = createWorkflowHarness(wf);
115
+ const res = await harness.run({ x: 10 });
116
+ expect(res.res).to.equal(15);
117
+ harness.assertStepExecuted('add', 1);
118
+ ```
119
+
120
+ ---
121
+
122
+ ## Failure Example: Catching Regressions with Replay
123
+
124
+ ```typescript
125
+ import { replayExecution } from '@siglo/testing';
126
+ import { Step, flow } from '@siglo/core';
127
+
128
+ // Modified workflow with a bug: calculates 10% tax instead of 5%
129
+ const buggyWf = flow(
130
+ Step.create('calcTax', (p: { amount: number }) => ({ tax: p.amount * 0.10 }))
131
+ );
132
+
133
+ const recordedBaseline = {
134
+ input: { amount: 100 },
135
+ output: { tax: 5 }, // Expected 5 from baseline
136
+ trace: [],
137
+ recordedAt: new Date().toISOString()
138
+ };
139
+
140
+ const result = await replayExecution(recordedBaseline, buggyWf);
141
+
142
+ if (!result.matched) {
143
+ console.error('Regression Detected:');
144
+ for (const diff of result.discrepancies) {
145
+ console.error(' -', diff);
146
+ }
147
+ }
148
+ ```
149
+
150
+ ---
151
+
152
+ ## Testing Example: Full Mocking & Spying
153
+
154
+ ```typescript
155
+ import { expect } from 'chai';
156
+ import { createWorkflowHarness } from '@siglo/testing';
157
+ import { Step, flow } from '@siglo/core';
158
+
159
+ const s1 = Step.create('fetchUser', () => ({ user: 'Alice' }));
160
+ const s2 = Step.create('emailUser', (p: any) => ({ sent: true }));
161
+ const wf = flow(s1, s2);
162
+
163
+ describe('Full Harness Suite', () => {
164
+ it('mocks step and asserts spy calls', async () => {
165
+ const harness = createWorkflowHarness(wf);
166
+ const emailSpy = harness.spyStep('emailUser');
167
+
168
+ harness.mockStep('fetchUser', () => ({ user: 'MockedUser' }));
169
+
170
+ const result = await harness.run();
171
+ expect(result.user).to.equal('MockedUser');
172
+ expect(emailSpy.called).to.be.true;
173
+ expect(emailSpy.firstCall()?.input.user).to.equal('MockedUser');
174
+ });
175
+ });
176
+ ```
177
+
178
+ ---
179
+
180
+ ## Operational Caveats
181
+
182
+ 1. **Secret Redaction**: Always run `sanitizeExecutionRecord()` before writing execution records to bug reports or test fixtures. Defaults automatically redact passwords, API keys, and credit cards.
183
+ 2. **Deterministic Randomness**: If steps generate UUIDs or timestamps, inject a deterministic clock or generator via `param.$services` so replays produce matching records.
184
+
185
+ ---
186
+
187
+ ## API Reference Links
188
+
189
+ - [TypeScript Reference](../../docs/10-typescript-reference.md)
190
+ - [CLI Reference](../../docs/adapters/cli.md)
@@ -0,0 +1,115 @@
1
+ import { StorageAdapter } from '@siglo/core';
2
+ export interface StepTraceRecord {
3
+ stepName: string;
4
+ phase: 'start' | 'retry' | 'complete' | 'fail' | 'skip';
5
+ timestamp: number;
6
+ input: any;
7
+ output?: any;
8
+ error?: any;
9
+ attempt?: number;
10
+ }
11
+ export interface ExecutionRecord {
12
+ workflowVersion?: string | number;
13
+ input: any;
14
+ output?: any;
15
+ error?: any;
16
+ trace: StepTraceRecord[];
17
+ recordedAt: string;
18
+ }
19
+ export interface ReplayOptions {
20
+ substituteFixtures?: Record<string, any>;
21
+ excludeKeys?: string[];
22
+ }
23
+ export interface ReplayResult {
24
+ matched: boolean;
25
+ expectedOutput: any;
26
+ actualOutput: any;
27
+ discrepancies: string[];
28
+ }
29
+ export declare class FakeClock {
30
+ private currentTime;
31
+ constructor(initialTime?: number);
32
+ now(): number;
33
+ advanceTime(ms: number): number;
34
+ tick(ms?: number): Promise<void>;
35
+ reset(toTime?: number): void;
36
+ }
37
+ export declare class InMemoryStorageFake implements StorageAdapter {
38
+ readonly name = "in-memory-fake";
39
+ private checkpoints;
40
+ private cache;
41
+ contextChanges: any[];
42
+ onContextChange(event: any): Promise<void>;
43
+ saveCheckpoint(checkpoint: any): Promise<void>;
44
+ getCheckpoint(key: string): Promise<any>;
45
+ getCachedStep(key: string): Promise<any>;
46
+ setCachedStep(key: string, val: any): Promise<void>;
47
+ clear(): void;
48
+ getCheckpoints(): Map<string, any>;
49
+ }
50
+ export declare class StepSpy {
51
+ readonly name: string;
52
+ readonly calls: Array<{
53
+ input: any;
54
+ output?: any;
55
+ error?: any;
56
+ timestamp: number;
57
+ }>;
58
+ constructor(name: string);
59
+ get called(): boolean;
60
+ get callCount(): number;
61
+ firstCall(): {
62
+ input: any;
63
+ output?: any;
64
+ error?: any;
65
+ timestamp: number;
66
+ } | undefined;
67
+ lastCall(): {
68
+ input: any;
69
+ output?: any;
70
+ error?: any;
71
+ timestamp: number;
72
+ } | undefined;
73
+ record(call: {
74
+ input: any;
75
+ output?: any;
76
+ error?: any;
77
+ timestamp: number;
78
+ }): void;
79
+ }
80
+ export declare function sanitizeExecutionRecord(record: ExecutionRecord, options?: {
81
+ excludeKeys?: string[];
82
+ }): ExecutionRecord;
83
+ export interface HarnessOptions {
84
+ services?: Record<string, any>;
85
+ clock?: FakeClock;
86
+ storage?: InMemoryStorageFake;
87
+ recordTrace?: boolean;
88
+ }
89
+ export declare class WorkflowHarness {
90
+ private workflow;
91
+ private options;
92
+ private mocks;
93
+ private spies;
94
+ private trace;
95
+ private lastRunResult;
96
+ clock: FakeClock;
97
+ storage: InMemoryStorageFake;
98
+ constructor(workflow: any, options?: HarnessOptions);
99
+ mockStep(stepName: string, mockImpl: (param: any) => any): this;
100
+ spyStep(stepName: string): StepSpy;
101
+ getTrace(): StepTraceRecord[];
102
+ injectEvent(eventName: string, correlationKey: string, payload?: any): Promise<any>;
103
+ private wrapWorkflowSteps;
104
+ run(input?: any, execOptions?: any): Promise<any>;
105
+ execute(input?: any, execOptions?: any): Promise<any>;
106
+ assertStepExecuted(stepName: string, times?: number): void;
107
+ assertStepRetried(stepName: string, minAttempts?: number): void;
108
+ assertStepFailed(stepName: string, errorPattern?: string | RegExp): void;
109
+ exportExecutionRecord(options?: {
110
+ excludeKeys?: string[];
111
+ }): ExecutionRecord;
112
+ }
113
+ export declare function createWorkflowHarness(workflow: any, options?: HarnessOptions): WorkflowHarness;
114
+ export declare function replayExecution(record: ExecutionRecord, workflow: any, options?: ReplayOptions): Promise<ReplayResult>;
115
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAGH,cAAc,EAIjB,MAAM,aAAa,CAAC;AAErB,MAAM,WAAW,eAAe;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,OAAO,GAAG,OAAO,GAAG,UAAU,GAAG,MAAM,GAAG,MAAM,CAAC;IACxD,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,GAAG,CAAC;IACX,MAAM,CAAC,EAAE,GAAG,CAAC;IACb,KAAK,CAAC,EAAE,GAAG,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC5B,eAAe,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAClC,KAAK,EAAE,GAAG,CAAC;IACX,MAAM,CAAC,EAAE,GAAG,CAAC;IACb,KAAK,CAAC,EAAE,GAAG,CAAC;IACZ,KAAK,EAAE,eAAe,EAAE,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC1B,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACzC,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,YAAY;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,cAAc,EAAE,GAAG,CAAC;IACpB,YAAY,EAAE,GAAG,CAAC;IAClB,aAAa,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,qBAAa,SAAS;IAClB,OAAO,CAAC,WAAW,CAAS;gBAEhB,WAAW,GAAE,MAAmB;IAI5C,GAAG,IAAI,MAAM;IAIb,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM;IAK/B,IAAI,CAAC,EAAE,GAAE,MAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAKnC,KAAK,CAAC,MAAM,GAAE,MAAmB,GAAG,IAAI;CAG3C;AAED,qBAAa,mBAAoB,YAAW,cAAc;IACtD,QAAQ,CAAC,IAAI,oBAAoB;IACjC,OAAO,CAAC,WAAW,CAA0B;IAC7C,OAAO,CAAC,KAAK,CAA0B;IAChC,cAAc,EAAE,GAAG,EAAE,CAAM;IAE5B,eAAe,CAAC,KAAK,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;IAI1C,cAAc,CAAC,UAAU,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;IAK9C,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC;IAKxC,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC;IAIxC,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;IAIzD,KAAK,IAAI,IAAI;IAMb,cAAc,IAAI,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC;CAGrC;AAED,qBAAa,OAAO;IAChB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,GAAG,CAAC;QAAC,MAAM,CAAC,EAAE,GAAG,CAAC;QAAC,KAAK,CAAC,EAAE,GAAG,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAM;gBAE7E,IAAI,EAAE,MAAM;IAIxB,IAAI,MAAM,IAAI,OAAO,CAEpB;IAED,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED,SAAS,IAAI;QAAE,KAAK,EAAE,GAAG,CAAC;QAAC,MAAM,CAAC,EAAE,GAAG,CAAC;QAAC,KAAK,CAAC,EAAE,GAAG,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS;IAIrF,QAAQ,IAAI;QAAE,KAAK,EAAE,GAAG,CAAC;QAAC,MAAM,CAAC,EAAE,GAAG,CAAC;QAAC,KAAK,CAAC,EAAE,GAAG,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS;IAIpF,MAAM,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,GAAG,CAAC;QAAC,MAAM,CAAC,EAAE,GAAG,CAAC;QAAC,KAAK,CAAC,EAAE,GAAG,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;CAGnF;AAiBD,wBAAgB,uBAAuB,CACnC,MAAM,EAAE,eAAe,EACvB,OAAO,GAAE;IAAE,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;CAAO,GACzC,eAAe,CAkCjB;AAED,MAAM,WAAW,cAAc;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC/B,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAC9B,WAAW,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,qBAAa,eAAe;IACxB,OAAO,CAAC,QAAQ,CAAM;IACtB,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,KAAK,CAA0C;IACvD,OAAO,CAAC,KAAK,CAA8B;IAC3C,OAAO,CAAC,KAAK,CAAyB;IACtC,OAAO,CAAC,aAAa,CAAM;IACpB,KAAK,EAAE,SAAS,CAAC;IACjB,OAAO,EAAE,mBAAmB,CAAC;gBAExB,QAAQ,EAAE,GAAG,EAAE,OAAO,GAAE,cAAmB;IAOvD,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,GAAG,GAAG,IAAI;IAK/D,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IASlC,QAAQ,IAAI,eAAe,EAAE;IAIvB,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC;IAKzF,OAAO,CAAC,iBAAiB;IA4DnB,GAAG,CAAC,KAAK,GAAE,GAAQ,EAAE,WAAW,GAAE,GAAQ,GAAG,OAAO,CAAC,GAAG,CAAC;IAiBzD,OAAO,CAAC,KAAK,GAAE,GAAQ,EAAE,WAAW,GAAE,GAAQ,GAAG,OAAO,CAAC,GAAG,CAAC;IAInE,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI;IAa1D,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,GAAE,MAAU,GAAG,IAAI;IAOlE,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAgBxE,qBAAqB,CAAC,OAAO,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,GAAG,eAAe;CAgB/E;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,GAAG,EAAE,OAAO,GAAE,cAAmB,GAAG,eAAe,CAElG;AAeD,wBAAsB,eAAe,CACjC,MAAM,EAAE,eAAe,EACvB,QAAQ,EAAE,GAAG,EACb,OAAO,GAAE,aAAkB,GAC5B,OAAO,CAAC,YAAY,CAAC,CAuCvB"}
package/dist/index.js ADDED
@@ -0,0 +1,342 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.WorkflowHarness = exports.StepSpy = exports.InMemoryStorageFake = exports.FakeClock = void 0;
4
+ exports.sanitizeExecutionRecord = sanitizeExecutionRecord;
5
+ exports.createWorkflowHarness = createWorkflowHarness;
6
+ exports.replayExecution = replayExecution;
7
+ const core_1 = require("@siglo/core");
8
+ class FakeClock {
9
+ currentTime;
10
+ constructor(initialTime = Date.now()) {
11
+ this.currentTime = initialTime;
12
+ }
13
+ now() {
14
+ return this.currentTime;
15
+ }
16
+ advanceTime(ms) {
17
+ this.currentTime += ms;
18
+ return this.currentTime;
19
+ }
20
+ tick(ms = 0) {
21
+ this.advanceTime(ms);
22
+ return new Promise(resolve => setImmediate(resolve));
23
+ }
24
+ reset(toTime = Date.now()) {
25
+ this.currentTime = toTime;
26
+ }
27
+ }
28
+ exports.FakeClock = FakeClock;
29
+ class InMemoryStorageFake {
30
+ name = 'in-memory-fake';
31
+ checkpoints = new Map();
32
+ cache = new Map();
33
+ contextChanges = [];
34
+ async onContextChange(event) {
35
+ this.contextChanges.push(event);
36
+ }
37
+ async saveCheckpoint(checkpoint) {
38
+ const key = checkpoint.stackId || checkpoint.id || 'default';
39
+ this.checkpoints.set(key, JSON.parse(JSON.stringify(checkpoint)));
40
+ }
41
+ async getCheckpoint(key) {
42
+ const item = this.checkpoints.get(key);
43
+ return item ? JSON.parse(JSON.stringify(item)) : undefined;
44
+ }
45
+ async getCachedStep(key) {
46
+ return this.cache.get(key);
47
+ }
48
+ async setCachedStep(key, val) {
49
+ this.cache.set(key, val);
50
+ }
51
+ clear() {
52
+ this.checkpoints.clear();
53
+ this.cache.clear();
54
+ this.contextChanges = [];
55
+ }
56
+ getCheckpoints() {
57
+ return new Map(this.checkpoints);
58
+ }
59
+ }
60
+ exports.InMemoryStorageFake = InMemoryStorageFake;
61
+ class StepSpy {
62
+ name;
63
+ calls = [];
64
+ constructor(name) {
65
+ this.name = name;
66
+ }
67
+ get called() {
68
+ return this.calls.length > 0;
69
+ }
70
+ get callCount() {
71
+ return this.calls.length;
72
+ }
73
+ firstCall() {
74
+ return this.calls[0];
75
+ }
76
+ lastCall() {
77
+ return this.calls[this.calls.length - 1];
78
+ }
79
+ record(call) {
80
+ this.calls.push(call);
81
+ }
82
+ }
83
+ exports.StepSpy = StepSpy;
84
+ const DEFAULT_SECRET_KEYS = new Set([
85
+ 'password',
86
+ 'token',
87
+ 'secret',
88
+ 'authorization',
89
+ 'apikey',
90
+ 'api_key',
91
+ 'creditcard',
92
+ 'cardnumber',
93
+ 'cvv',
94
+ 'ssn',
95
+ 'privatekey',
96
+ 'private_key'
97
+ ]);
98
+ function sanitizeExecutionRecord(record, options = {}) {
99
+ const customExcludes = new Set((options.excludeKeys || []).map(k => k.toLowerCase()));
100
+ const sanitizeVal = (val) => {
101
+ if (!val || typeof val !== 'object') {
102
+ return val;
103
+ }
104
+ if (Array.isArray(val)) {
105
+ return val.map(sanitizeVal);
106
+ }
107
+ const clean = {};
108
+ for (const [k, v] of Object.entries(val)) {
109
+ const lowerKey = k.toLowerCase();
110
+ if (DEFAULT_SECRET_KEYS.has(lowerKey) || customExcludes.has(lowerKey)) {
111
+ clean[k] = '[REDACTED]';
112
+ }
113
+ else {
114
+ clean[k] = sanitizeVal(v);
115
+ }
116
+ }
117
+ return clean;
118
+ };
119
+ return {
120
+ workflowVersion: record.workflowVersion,
121
+ recordedAt: record.recordedAt,
122
+ input: sanitizeVal(record.input),
123
+ output: sanitizeVal(record.output),
124
+ error: record.error ? (record.error.message || String(record.error)) : undefined,
125
+ trace: record.trace.map(t => ({
126
+ ...t,
127
+ input: sanitizeVal(t.input),
128
+ output: sanitizeVal(t.output)
129
+ }))
130
+ };
131
+ }
132
+ class WorkflowHarness {
133
+ workflow;
134
+ options;
135
+ mocks = new Map();
136
+ spies = new Map();
137
+ trace = [];
138
+ lastRunResult;
139
+ clock;
140
+ storage;
141
+ constructor(workflow, options = {}) {
142
+ this.workflow = workflow;
143
+ this.options = options;
144
+ this.clock = options.clock || new FakeClock();
145
+ this.storage = options.storage || new InMemoryStorageFake();
146
+ }
147
+ mockStep(stepName, mockImpl) {
148
+ this.mocks.set(stepName, mockImpl);
149
+ return this;
150
+ }
151
+ spyStep(stepName) {
152
+ let spy = this.spies.get(stepName);
153
+ if (!spy) {
154
+ spy = new StepSpy(stepName);
155
+ this.spies.set(stepName, spy);
156
+ }
157
+ return spy;
158
+ }
159
+ getTrace() {
160
+ return [...this.trace];
161
+ }
162
+ async injectEvent(eventName, correlationKey, payload) {
163
+ const res = await (0, core_1.resumeWorkflow)(this.workflow, correlationKey, payload, { pojo: true });
164
+ return (0, core_1.toPOJO)(res);
165
+ }
166
+ wrapWorkflowSteps() {
167
+ const rawSteps = (0, core_1.isFlow)(this.workflow) ? this.workflow.steps : [this.workflow];
168
+ const wrappedSteps = rawSteps.map((origStep) => {
169
+ const name = origStep.name || origStep.func || 'anonymous';
170
+ const mockFn = this.mocks.get(name);
171
+ const spy = this.spies.get(name);
172
+ const instrumentedAction = async (param) => {
173
+ const ts = this.clock.now();
174
+ this.trace.push({
175
+ stepName: name,
176
+ phase: 'start',
177
+ timestamp: ts,
178
+ input: (0, core_1.toPOJO)(param),
179
+ attempt: param?.$attempt || 1
180
+ });
181
+ try {
182
+ let out;
183
+ if (mockFn) {
184
+ out = await mockFn(param);
185
+ }
186
+ else {
187
+ out = await origStep.action(param);
188
+ }
189
+ if (spy) {
190
+ spy.record({ input: (0, core_1.toPOJO)(param), output: out, timestamp: ts });
191
+ }
192
+ this.trace.push({
193
+ stepName: name,
194
+ phase: 'complete',
195
+ timestamp: this.clock.now(),
196
+ input: (0, core_1.toPOJO)(param),
197
+ output: out
198
+ });
199
+ return out;
200
+ }
201
+ catch (err) {
202
+ if (spy) {
203
+ spy.record({ input: (0, core_1.toPOJO)(param), error: err, timestamp: ts });
204
+ }
205
+ this.trace.push({
206
+ stepName: name,
207
+ phase: 'fail',
208
+ timestamp: this.clock.now(),
209
+ input: (0, core_1.toPOJO)(param),
210
+ error: err?.message || String(err)
211
+ });
212
+ throw err;
213
+ }
214
+ };
215
+ const clone = Object.assign(Object.create(Object.getPrototypeOf(origStep)), origStep);
216
+ clone.action = instrumentedAction;
217
+ return clone;
218
+ });
219
+ return (0, core_1.flow)(...wrappedSteps);
220
+ }
221
+ async run(input = {}, execOptions = {}) {
222
+ (0, core_1.useStorage)(this.storage);
223
+ const instrumented = this.wrapWorkflowSteps();
224
+ const mergedServices = {
225
+ ...(this.options.services || {}),
226
+ ...(execOptions.services || {})
227
+ };
228
+ const result = await instrumented.run(input, {
229
+ ...execOptions,
230
+ services: mergedServices,
231
+ pojo: execOptions.pojo !== false
232
+ });
233
+ this.lastRunResult = result;
234
+ return result;
235
+ }
236
+ async execute(input = {}, execOptions = {}) {
237
+ return this.run(input, execOptions);
238
+ }
239
+ assertStepExecuted(stepName, times) {
240
+ const executions = this.trace.filter(t => t.stepName === stepName && t.phase === 'complete');
241
+ if (times !== undefined) {
242
+ if (executions.length !== times) {
243
+ throw new Error(`Expected step '${stepName}' to be executed ${times} time(s), but was executed ${executions.length} time(s).`);
244
+ }
245
+ }
246
+ else {
247
+ if (executions.length === 0) {
248
+ throw new Error(`Expected step '${stepName}' to be executed, but was not found in completed trace.`);
249
+ }
250
+ }
251
+ }
252
+ assertStepRetried(stepName, minAttempts = 2) {
253
+ const attempts = this.trace.filter(t => t.stepName === stepName && (t.phase === 'start' || t.phase === 'retry'));
254
+ if (attempts.length < minAttempts) {
255
+ throw new Error(`Expected step '${stepName}' to have at least ${minAttempts} attempts, but found ${attempts.length}.`);
256
+ }
257
+ }
258
+ assertStepFailed(stepName, errorPattern) {
259
+ const failures = this.trace.filter(t => t.stepName === stepName && t.phase === 'fail');
260
+ if (failures.length === 0) {
261
+ throw new Error(`Expected step '${stepName}' to fail, but no failure was recorded in trace.`);
262
+ }
263
+ if (errorPattern) {
264
+ const matched = failures.some(f => {
265
+ const msg = f.error || '';
266
+ return errorPattern instanceof RegExp ? errorPattern.test(msg) : msg.includes(errorPattern);
267
+ });
268
+ if (!matched) {
269
+ throw new Error(`Step '${stepName}' failed, but error did not match expected pattern '${errorPattern}'.`);
270
+ }
271
+ }
272
+ }
273
+ exportExecutionRecord(options) {
274
+ const lastComplete = [...this.trace].reverse().find(t => t.phase === 'complete');
275
+ const lastFail = [...this.trace].reverse().find(t => t.phase === 'fail');
276
+ const firstStart = this.trace.find(t => t.phase === 'start');
277
+ const rawRecord = {
278
+ workflowVersion: this.workflow?.workflowVersion,
279
+ input: firstStart?.input || {},
280
+ output: this.lastRunResult !== undefined ? this.lastRunResult : lastComplete?.output,
281
+ error: lastFail?.error,
282
+ trace: this.trace,
283
+ recordedAt: new Date(this.clock.now()).toISOString()
284
+ };
285
+ return sanitizeExecutionRecord(rawRecord, options);
286
+ }
287
+ }
288
+ exports.WorkflowHarness = WorkflowHarness;
289
+ function createWorkflowHarness(workflow, options = {}) {
290
+ return new WorkflowHarness(workflow, options);
291
+ }
292
+ function normalizeForComparison(val) {
293
+ if (!val || typeof val !== 'object')
294
+ return val;
295
+ if (Array.isArray(val))
296
+ return val.map(normalizeForComparison);
297
+ const clean = {};
298
+ for (const [k, v] of Object.entries(val)) {
299
+ if (k === 'stackId' || k === '$stackId' || k === '$stepId' || k === '$steps' || k === '$history') {
300
+ continue;
301
+ }
302
+ clean[k] = normalizeForComparison(v);
303
+ }
304
+ return clean;
305
+ }
306
+ async function replayExecution(record, workflow, options = {}) {
307
+ const harness = createWorkflowHarness(workflow);
308
+ if (options.substituteFixtures) {
309
+ for (const [stepName, fixtureOutput] of Object.entries(options.substituteFixtures)) {
310
+ harness.mockStep(stepName, () => fixtureOutput);
311
+ }
312
+ }
313
+ let actualOutput;
314
+ let actualError;
315
+ try {
316
+ actualOutput = await harness.run(record.input);
317
+ }
318
+ catch (err) {
319
+ actualError = err.message || String(err);
320
+ }
321
+ const discrepancies = [];
322
+ const cleanExpected = normalizeForComparison(record.output ?? null);
323
+ const cleanActual = normalizeForComparison(actualOutput ?? null);
324
+ const expectedJson = JSON.stringify(cleanExpected);
325
+ const actualJson = JSON.stringify(cleanActual);
326
+ if (expectedJson !== actualJson) {
327
+ discrepancies.push(`Output mismatch: expected ${expectedJson}, received ${actualJson}`);
328
+ }
329
+ if (record.error && !actualError) {
330
+ discrepancies.push(`Expected execution to fail with error '${record.error}', but succeeded.`);
331
+ }
332
+ else if (!record.error && actualError) {
333
+ discrepancies.push(`Expected execution to succeed, but failed with '${actualError}'.`);
334
+ }
335
+ return {
336
+ matched: discrepancies.length === 0,
337
+ expectedOutput: record.output,
338
+ actualOutput,
339
+ discrepancies
340
+ };
341
+ }
342
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,53 @@
1
1
  {
2
2
  "name": "@siglo/testing",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.0.1",
4
+ "description": "Deterministic test utilities, workflow harness, mocks, fake clock, and replay tooling for Siglo",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "import": "./dist/index.js",
11
+ "require": "./dist/index.js",
12
+ "default": "./dist/index.js"
13
+ },
14
+ "./package.json": "./package.json"
15
+ },
16
+ "files": [
17
+ "dist",
18
+ "README.md",
19
+ "LICENSE"
20
+ ],
21
+ "publishConfig": {
22
+ "access": "public",
23
+ "registry": "https://registry.npmjs.org/"
24
+ },
25
+ "scripts": {
26
+ "build": "tsc -p tsconfig.json",
27
+ "prepublishOnly": "npm run build",
28
+ "test": "npm run build && mocha -r tsx 'src/**/*.test.ts'"
29
+ },
30
+ "repository": {
31
+ "type": "git",
32
+ "url": "git+https://github.com/jsoftgem/siglo.git",
33
+ "directory": "packages/testing"
34
+ },
35
+ "author": "Jerico de Guzman",
36
+ "license": "MIT",
37
+ "bugs": {
38
+ "url": "https://github.com/jsoftgem/siglo/issues"
39
+ },
40
+ "homepage": "https://github.com/jsoftgem/siglo#readme",
41
+ "dependencies": {
42
+ "@siglo/core": "^1.0.0"
43
+ },
44
+ "devDependencies": {
45
+ "@types/chai": "^5.2.3",
46
+ "@types/mocha": "^10.0.10",
47
+ "@types/node": "^26.6.5",
48
+ "chai": "^3.5.0",
49
+ "mocha": "^12.0.3",
50
+ "tsx": "^4.23.15",
51
+ "typescript": "~5.7.0"
52
+ }
53
+ }