Skip to main content

Testing Retries, Timeouts, and Sagas in <5ms with Virtual Clocks (@siglo/testing)

Why do most backend teams skip testing their retry loops, webhook suspensions, and Saga rollback handlers? Because in traditional services, testing failure recovery is slow, brittle, and invasive:
  • Testing a 3-attempt exponential backoff means either waiting seconds of wall-clock time in CI or monkey-patching global setTimeout.
  • Testing what happens when step 3 of a 4-step pipeline fails requires mocking global HTTP clients or mutating dependency-injected singletons.
  • Reproducing a production bug locally requires manually reconstructing intermediate database states while hoping you don’t leak customer PII or API keys into test fixtures.
When we built Siglo, we designed @siglo/testing so that every workflow resilience feature—retries, Saga compensations, durable event suspensions, and production trace replays—can be tested deterministically in under 5 milliseconds. Here is how createWorkflowHarness, FakeClock, spyStep, and replayExecution work together.

1. Selective Step Mocking and Spying (createWorkflowHarness)

Suppose we have a 3-step checkout pipeline:
With createWorkflowHarness, you don’t need module-level mocks (vi.mock or sinon.stub) to simulate a flaky payment gateway or a downstream shipping outage. You can mock or spy on individual named steps while letting the rest of the pipeline run untouched:
Under the hood, WorkflowHarness clones the workflow steps, attaches an isolated InMemoryStorageFake and FakeClock, and records a structured StepTraceRecord (start, retry, complete, fail, skip) for every execution phase.

2. Deterministic Virtual Time (FakeClock)

When testing time-dependent workflows—such as audit timestamps, SLA expirations, or scheduled checkpoints—relying on Date.now() leads to flaky assertions. @siglo/testing provides FakeClock, which starts at a fixed epoch (or Date.now()) and advances only when you call clock.advanceTime(ms) or await clock.tick(ms):

3. Testing Webhook Suspensions (injectEvent)

If your workflow uses waitForEvent to pause for an external webhook or human approval, you don’t need to spin up an HTTP server in your unit tests. harness.injectEvent() resumes the suspended workflow directly in memory:

4. Exporting Redacted Production Traces & Replaying Locally (replayExecution)

When an edge-case failure happens in staging or production, how do you turn it into a regression test without copying sensitive user credentials? harness.exportExecutionRecord() (and sanitizeExecutionRecord()) automatically scrubs sensitive keys (password, token, secret, authorization, apiKey, creditCard, cvv, ssn, privateKey, plus any custom excludeKeys) into '[REDACTED]':
replayExecution strips ephemeral runtime metadata (stackId, $stepId, $history), substitutes deterministic fixtures for external I/O steps, runs the current workflow code against the recorded input, and reports any structural diffs in replay.discrepancies.

Try @siglo/testing