# Caches and events

An event is a fact an action records, such as "an order was placed",
and a subscriber is code that runs when one is delivered
([Subscribers](subscribers.md)). Events run after the
action that emits them commits. In a test, what a step emits is delivered
before the next line runs, so a test checks an event's effects on the
line after the call:

- **Events** are delivered after each step of the test commits and before
  the next statement runs, at the test's clock, until no events are left.
  A test checks their effects, such as a sent reminder in `requests`. A
  subscriber that fails fails the test, as below.
- **Caches** are copies used only by display reads from routes
  ([Caches](caches.md)). In a test they are on and start empty,
  in an in-memory cache that behaves like Redis, so a route test sees the
  same invalidation as production. Tests can't read, fill or clear a
  cache directly. Actions never read caches, so only route tests meet
  them.

## Steps and delivery

A step is a call of an action or fixture, a route test's request, a
schedule's `.run()` or `clock.advance(...)`. Once a step comes back, what
is due in the test's outbox, the list of events waiting to be delivered,
is delivered as the runner delivers it anywhere: as the system, limited
to its event's tenant. Then what those subscribers emit is delivered in
turn, until nothing is due.

## The order subscribers run in

Subscribers run in an order shuffled by the run's seed, the one the
report prints and `--seed` gives again, mixed with the test's own
identity. Each delivery is drawn from all those due at that moment. So a
test that relies on the order its subscribers run in fails under some
seed, and rerunning it with that seed fails the same way.

## Retries

A delivery whose subscriber fails in a way worth trying again waits its
full backoff: 30 seconds, and then twice as long each time. There is no
random jitter, since a test has no randomness to draw it from
([Retries and giving up](retries-and-giving-up.md)).
The test's clock moves only when the test moves it, so the retry runs
once `clock.advance` reaches it.

A fake failing a subscriber's call to a provider is retried and given up
the same way, and a test scripts it with `fakes:`. A subscriber's own
failure worth retrying tests its retries too.

## A delivery that fails for good

A delivery fails for good when its failure isn't worth retrying, when it
fails on its last attempt, or when it hits a fault. It ends the test there
as [`test.subscriber_failed`](../errors/test.subscriber_failed.md), naming
the event, the subscriber and the attempt.

What a route's `settle:` delivers while its request waits belongs to the
request, and the response says how it ended
([Waiting for follow-on work](waiting-for-follow-on-work.md)).

## An example

Here placing an order emits `OrderPlaced`, and `cook_placed` cooks it
once a chef is on shift:

```jadpo
use kitchen/entities { Order, OrderNotFound }
use kitchen/failures { NoChef }
use kitchen/queries { chefs }

/// Cooks an order, once a chef is on shift.
action cook(order_id: Order.id) fails OrderNotFound, NoChef -> Unit {
    if (list.count(chefs()) == 0) {
        reject NoChef {}
    }
    attempt Order.update(order_id) {
        set: {
            cooked_at: clock.now
        }
    }
}
```

```jadpo
use kitchen/actions { cook }
use kitchen/events { OrderPlaced }

/// Cooks each order placed.
subscriber cook_placed {
    on: OrderPlaced
    run: cook(event.order_id)
}
```

```jadpo
use kitchen/entities { Chef, Order }
use kitchen/actions { start_shift }

test "an order is cooked before the next line" {
    clock: "2026-10-20T18:00:00Z"
    var chef: Chef = start_shift()
    assert chef.on_shift
    var order: Order = place_order()
    var cooked: Order = attempt Order.get(order.id)
    assert cooked.cooked_at == "2026-10-20T18:00:00Z"
}

test "an order placed before the shift is cooked when its retry comes round" {
    clock: "2026-10-20T18:00:00Z"
    var order: Order = place_order()
    var chef: Chef = start_shift()
    assert chef.on_shift
    var waiting: Order = attempt Order.get(order.id)
    assert waiting.cooked_at == none
    clock.advance(30s)
    var cooked: Order = attempt Order.get(order.id)
    assert cooked.cooked_at == "2026-10-20T18:00:30Z"
}
```

The first test reads the order cooked on the line after placing it.
In the second, the delivery fails with `NoChef`, which is worth
retrying, so it waits 30 seconds. Starting the shift doesn't move the
clock, and the retry runs once `clock.advance(30s)` reaches it.

Not built yet: caches, which need the Redis driver, and running a batch
to the end in a test. A batch run a step starts is left as it was started
([Provider calls and capabilities in tests](fakes-for-integrations.md#provider-calls-and-capabilities-in-tests)).
