Simulations (Test Clocks)
Simulations (Test Clocks)
Overview
Simulations provide a deterministic testing environment for subscription lifecycle flows. Unlike production subscriptions that run asynchronously, simulations execute all evaluations synchronously within a single database transaction, allowing you to fast-forward time and observe exact state changes.
Key features:
- Virtual time control: Advance time to specific moments without waiting
- Deterministic execution: Same input state always produces the same effects
- Full transaction isolation: All changes happen atomically — simulations never affect production data
- Event timeline: Complete audit trail of every state change and evaluation
- Synchronous evaluation: No background workflows — results are immediate and predictable
Use cases
Create a simulation
Create a test clock with a virtual timeline for deterministic subscription testing.
Request parameters:
Important: All entities created within a simulation (subscriptions, invoices, payments) are automatically linked to the simulation and isolated from production data via the simulation_id foreign key.
Create test subscriptions
Once you have a simulation, create subscriptions that will be evaluated on the virtual timeline.
Key points:
- Pass the
simulation_idto link the subscription to the simulation - The subscription’s
start_dateshould align with or follow the simulation’sstart_time - All subscription evaluations will use the simulation’s
virtual_clockinstead of real time
Advance simulation time
Move the virtual clock forward to trigger subscription evaluations and state changes.
How time advancement works
When you advance time, PaymentKit:
- Validates the target:
target_timemust be in the future relative to the currentvirtual_clock - Updates the virtual clock: Sets
virtual_clocktotarget_time - Evaluates subscriptions: Finds all subscriptions linked to this simulation and evaluates each one using the new
virtual_clock - Executes effects synchronously: All effects (invoice creation, payment attempts, state transitions) run immediately in a single transaction
- Records events: Logs each evaluation and effect to the simulation’s event timeline
- Returns results: Reports how many subscriptions were evaluated and events recorded
Important invariants:
- Time causality: Virtual clock only moves forward (never backward)
- Determinism: Same subscription state + same target time → same effects
- Atomicity: All evaluations complete or the entire operation rolls back
- Synchronous execution: All subscription evaluations and effects execute immediately within a single transaction
Retrieve simulation details
Fetch the current state of a simulation.
View simulation timeline
Retrieve a chronological log of all events that occurred during time advancement.
Event types:
Testing patterns
Test case: Trial expiration with successful payment
Test case: Dunning retry schedule
Test case: Fixed-term contract completion
Limitations and differences from production
Important: Simulations achieve 100% behavior parity with production subscription logic, including event publishing, webhook delivery, and email notifications. The key difference is execution mode: production uses async Restate workflows while simulations execute synchronously within a single transaction.
Best practices
Use simulations for integration tests
Create a simulation before each test, advance time to trigger the scenario, then assert on the resulting subscription/invoice state. Simulations are isolated and can be safely run in parallel.
Always verify via API, not just events
The event timeline shows what happened, but always fetch the subscription/invoice state via the API to confirm the final result matches expectations.
Test edge cases with multiple subscriptions
Create multiple subscriptions in the same simulation with different billing dates, trial periods, and dunning settings. Advance time once and verify all subscriptions evaluated correctly.
Clean up after testing
Simulations and their linked entities are isolated from production, but they consume database space. Delete test simulations after validation is complete (future API will support simulation deletion).
Use descriptive descriptions
The description field helps track what each simulation is testing. Use names like “Dunning exhaustion test” or “Trial → Active transition with proration” to make timelines easier to debug.
Simulations are for testing only
Simulations trigger domain events, webhooks, and email notifications just like production, but execute synchronously in controlled virtual time. Never use simulations for production subscriptions — they exist solely for deterministic testing environments where you can fast-forward time and verify exact state transitions.