Skip to content

Testing ​

What It Is ​

The testing surface provides deterministic replacements for real time: VirtualClockProvider and VirtualScheduler.

Use this page when retry, timeout, lifecycle timing, scheduled work, or deadline behavior must be asserted without waiting for wall-clock delays.

How It Works ​

Install the virtual providers before each test, drive time manually, drain scheduled tasks, and restore real providers after the test. Runtime code continues to call Clock and Scheduler; the provider swap makes the behavior deterministic.

The testing utilities are for runtime behavior, not graph validation. Pair them with Validator or dispatcher registration tests when document shape also matters.

Diagrams, Examples, and Outputs ​

Testing utilities are runtime providers. These references show the contracts they implement:

What It Lets You Do ​

The testing reference lets applications replace real time with deterministic clock and scheduler implementations.

@studnicky/dagonizer/testing

The testing subpath exports two deterministic replacements for the real-time clock and scheduler. Install them before each test; reset them after.

Code Samples ​

The code below covers virtual clocks, virtual schedulers, deterministic retry timing, scheduler draining, and provider reset patterns.

Import ​

ts
import { 
VirtualClockProvider
,
VirtualScheduler
} from '@studnicky/dagonizer/testing';

Class: VirtualClockProvider ​

In-memory monotonic clock. Time advances only when you advance it.

ts
import { 
VirtualClockProvider
} from '@studnicky/dagonizer/testing';
import {
Clock
} from '@studnicky/dagonizer/runtime';

Constructor ​

ts
new 
VirtualClockProvider
(0n);

initialNs is the starting nanosecond value. Defaults to 0n.

.tickMs(deltaMs) ​

ts
clock
.
tickMs
(100);

Advance the virtual clock by deltaMs milliseconds.

.tickNs(deltaNs) ​

ts
clock
.
tickNs
(100_000_000n);

Advance the virtual clock by deltaNs nanoseconds.

The clock only moves forward — tickNs/tickMs are relative advances, there is no method to set an absolute value after construction. To start the clock at a specific nanosecond value, pass it to the constructor:

ts
const 
clock
= new
VirtualClockProvider
(500_000_000n);

Usage ​

ts
<<< @/../examples/dags/virtual-clock.ts#virtual-time

Class: VirtualScheduler ​

In-memory min-heap scheduler. No platform timers. Advance time via advance(ms), runUntil(atMs), or runAll().

ts
import { 
VirtualScheduler
} from '@studnicky/dagonizer/testing';
import {
Scheduler
} from '@studnicky/dagonizer/runtime';

Constructor ​

ts
new 
VirtualScheduler
(0);

initialAtMs is the starting virtual-now value. Defaults to 0.

.advance(deltaMs) ​

ts
scheduler
.
advance
(500);

Advance virtual time by deltaMs, firing all tasks scheduled in that window in order.

.runUntil(atMs) ​

ts
scheduler
.
runUntil
(1000);

Advance virtual time to atMs, firing tasks in order.

.runAll() ​

ts
scheduler
.
runAll
();

Fire all pending one-shot tasks in monotonic order.

.virtualNow ​

ts
const 
now
: number =
scheduler
.
virtualNow
;

Current virtual time in ms.

.pendingCount ​

ts
const 
count
: number =
scheduler
.
pendingCount
;

Number of active (non-cancelled) pending tasks.

Usage with RetryPolicy ​

ts
<<< @/../examples/dags/virtual-clock.ts#virtual-time

SchedulerProviderInterface interface ​

Both VirtualScheduler and RealTimeScheduler implement SchedulerProviderInterface:

ts
// SchedulerProviderInterface (from @studnicky/dagonizer/runtime):
//   after(delayMs, options?: { signal? }): Promise<void>
//   at(atMs, options?: { signal? }): Promise<void>
//   every(intervalMs, options?: { signal? }): AsyncIterable<void>
//   cancelAll(): void
const 
_scheduler
: SchedulerProviderInterface = {} as SchedulerProviderInterface;

Implement this interface to create a custom test scheduler (e.g. one that records fired tasks for assertions).

Details for Nerds ​

Virtual providers are process-global while installed. Reset them after each test so one suite does not leak deterministic time into another.

Prefer advancing virtual time to sleeping. Tests that wait on wall-clock time are slower and less reliable than tests that control Clock and drain Scheduler.

Watched over by the Order of Dagon.