# Testing Deciders

Deciders can be tested without a database, from the events that have happened so far, the command, and the expected decision.

To test deciders without a database, turn on the feature `testing` for the tests, which adds the module `architecturekit::testing`:

```shell
cargo add architecturekit --dev --features testing
```

The examples use the commands as **[Defining Commands](/docs/architecturekit/rust/defining-commands)** declares them, with `on_state_read`, apart from those under **[Expecting Preconditions](/docs/architecturekit/rust/testing-deciders#expecting-preconditions)**, which use the `BorrowBook` that checks the revision of the caller with `on_event_id` (see **[Checking the Revision of the Caller](/docs/architecturekit/rust/using-preconditions#checking-the-revision-of-the-caller)**) and the `AcquireBook` that prevents duplicates with `on_pristine_subject` (see **[Preventing Duplicates](/docs/architecturekit/rust/using-preconditions#preventing-duplicates)**).

Call the `given` function with the decider and the events that have happened so far. Then call the `when` method with the command, wait for the decision, and check it. **A decider decides asynchronously, so a test runs on Tokio**, as `#[tokio::test]` does:

```rust
use architecturekit::testing::given;

#[tokio::test]
async fn borrows_a_book() {
  given(
    &borrow_book(),
    [BookEvent::Acquired(BookAcquired {
      title: "2001 – A Space Odyssey".to_owned(),
      author: "Arthur C. Clarke".to_owned(),
      isbn: "978-0756906788".to_owned(),
    })],
  )
  .when(BorrowBook {
    book: Subject::new("/books/42"),
    reader_id: "23".to_owned(),
    borrowed_until: "2026-10-24".to_owned(),
  })
  .await
  .then_events([BookEvent::Borrowed(BookBorrowed {
    borrowed_by: "23".to_owned(),
    borrowed_until: "2026-10-24".to_owned(),
  })]);
}
```

`given` returns a `Fixture`, and `when` returns a `Decision`, which is the decider's decision on the command: events, nothing, or a refusal. The methods that check the decision return the decision again, so they can be chained. **A check that does not hold panics with a message that says what was expected and what was decided**, as `assert_eq!` does, which fails the test.

The events are variants of the enum of the state, so the compiler checks that the state has a rule for each of them. `given` builds the state by writing every event as JSON and reading it back before the state evolves by it, exactly as executing a command and reading the state again would, so a field that does not survive that, such as one that serde skips, shows up in the test. It fails the test if an event can not be encoded. Like `execute`, `when` checks the preconditions of the command before the decider decides, so that a command `execute` refuses, such as one that combines `unconditionally` with others, is refused here as well (see **[Using Preconditions](/docs/architecturekit/rust/using-preconditions)**), and it refuses the events if the data of one of them can not be encoded as JSON. The decision then holds the same error that `execute` returns, `ExecuteError::Permanent`, so `then_events` and the other methods that expect events, or nothing, fail and name the cause, and `then_failed_in(Category::Permanent)` matches.

The examples below start from the event of the first example, which a function of the test returns:

```rust
fn book_acquired() -> BookEvent {
  BookEvent::Acquired(BookAcquired {
    title: "2001 – A Space Odyssey".to_owned(),
    author: "Arthur C. Clarke".to_owned(),
    isbn: "978-0756906788".to_owned(),
  })
}
```

## Expecting Events

To expect exactly the given events, in the given order, call the `then_events` method, as shown above. It compares the events by their type and the JSON of their data, which is what executing the command would write, so the events need no `PartialEq`. To expect neither events nor an error, call the `then_nothing` method:

```rust
given(&return_book(), [book_acquired()])
  .when(ReturnBook { book: Subject::new("/books/42") })
  .await
  .then_nothing();
```

To check the events with a function, call the `then_some_event` method to expect at least one matching event, the `then_every_event` method to expect only matching events, and at least one, or the `then_no_event` method to expect no matching event:

```rust
given(&borrow_book(), [book_acquired()])
  .when(BorrowBook {
    book: Subject::new("/books/42"),
    reader_id: "23".to_owned(),
    borrowed_until: "2026-10-24".to_owned(),
  })
  .await
  .then_every_event(|event| matches!(event, BookEvent::Borrowed(_)));
```

*Note that `then_every_event` fails the test for a decision without events, while `then_no_event` passes for one, since no event matches.*

## Expecting Rejections

To expect that a command is rejected, call the `then_failed` method with the error you expect. It compares the error of the decider with `==`, so the error type implements `PartialEq` and `Debug`, as `DomainError` does. For example, `acquire_book` refuses a book that has been acquired already (see **[Making Decisions](/docs/architecturekit/rust/making-decisions)**):

```rust
given(&acquire_book(), [book_acquired()])
  .when(AcquireBook {
    book: Subject::new("/books/42"),
    title: "2001 – A Space Odyssey".to_owned(),
    author: "Arthur C. Clarke".to_owned(),
    isbn: "978-0756906788".to_owned(),
  })
  .await
  .then_failed(DomainError::new("/books/42 has already been acquired"));
```

To expect any error of a category instead, call the `then_failed_in` method with the category, such as `Category::Domain` or `Category::Permanent`. It matches an error of the decider as well as one of the kit. For example, `borrow_book` rejects a book that does not exist with a `DomainError`, which belongs to the category `Category::Domain`:

```rust
given(&borrow_book(), [])
  .when(BorrowBook {
    book: Subject::new("/books/42"),
    reader_id: "23".to_owned(),
    borrowed_until: "2026-10-24".to_owned(),
  })
  .await
  .then_failed_in(Category::Domain);
```

For a failure that neither its value nor its category tells apart, such as a refusal of the kit, which may have the same category as an error of the decider, call the `then_failed_matching` method with a function that receives the `ExecuteError`, as in `then_failed_matching(|failure| matches!(failure, ExecuteError::Permanent(_)))`.

*Note that `then_failed` compares the whole error, which for a `DomainError` is its message, so rewording a rejection breaks a test that expects it. `then_failed_in` does not compare messages. To tell a rejection apart from the others of the same category without its message, give the decider an error type of its own, such as an enum with a variant per rejection, which implements `Categorized`, and expect the variant.*

## Expecting Preconditions

To expect exactly the given preconditions, in the given order, call the `then_preconditions` method. Describe the preconditions as the command does, with `Precondition::on_state_read`, `unconditionally`, `on_pristine_subject`, `on_populated_subject`, and `on_event_id`, and one made with `require` from a precondition of the client SDK, such as an EventQL query. For example, the `BorrowBook` that checks the revision of the caller requires its subject to be on the event ID the command carries (see **[Checking the Revision of the Caller](/docs/architecturekit/rust/using-preconditions#checking-the-revision-of-the-caller)**):

```rust
given(&borrow_book(), [book_acquired()])
  .when(BorrowBook {
    book: Subject::new("/books/42"),
    reader_id: "23".to_owned(),
    borrowed_until: "2026-10-24".to_owned(),
    expected_event_id: Revision::from(0),
  })
  .await
  .then_preconditions([Precondition::on_event_id(Subject::new("/books/42"), Revision::from(0))]);
```

`then_preconditions` compares preconditions by their kind and by what the database checks for them. `on_pristine_subject` and `on_populated_subject` are the same as `require` with `is_subject_pristine` and `is_subject_populated` of the client SDK, so each matches the other. A test that expects a pristine subject fails for a command that declares a populated one, and the failure names both. For example, the `AcquireBook` that prevents duplicates requires a pristine subject (see **[Preventing Duplicates](/docs/architecturekit/rust/using-preconditions#preventing-duplicates)**):

```rust
given(&acquire_book(), [])
  .when(AcquireBook {
    book: Subject::new("/books/42"),
    title: "2001 – A Space Odyssey".to_owned(),
    author: "Arthur C. Clarke".to_owned(),
    isbn: "978-0756906788".to_owned(),
  })
  .await
  .then_preconditions([Precondition::on_pristine_subject(Subject::new("/books/42"))]);
```

For a pristine or a populated subject, the two are alike. **For a revision, they are not**: `on_event_id` takes a `Revision`, which the caller reads from its text first, so that a value that is not one is refused before anything is read, while `require` with `is_subject_on_event_id` takes any text, and leaves it to the database, which refuses one that is not an event ID as a malformed request, so that `execute` fails with an error of the category `Category::Permanent` (see **[Checking the Revision of the Caller](/docs/architecturekit/rust/using-preconditions#checking-the-revision-of-the-caller)**). So `then_preconditions` tells them apart: a test that expects `on_event_id` fails for a command that uses `require` instead, and the failure shows which function made each of them.

A precondition that checks the subject together with every subject below it, made with `require` from `is_subject_pristine`, `is_subject_populated`, or `is_subject_on_event_id` with `PreconditionOptions` that set `recursive` to `true` (see **[Using Preconditions](/docs/architecturekit/rust/using-preconditions)**), has no function of its own in the kit. Describe it the same way in the test. It checks something else than the precondition of the subject alone, so a test that expects the one fails for a command that declares the other, and the failure names both, the recursive one with its options. `PreconditionOptions` that set `recursive` to `false` mean the same as none, so `on_pristine_subject` and `on_populated_subject` describe such a precondition. For example, an `AcquireBook` that requires neither its subject nor any subject below it to have events yet passes the following test, while the one that prevents duplicates with `on_pristine_subject` fails it:

```rust
given(&acquire_book(), [])
  .when(AcquireBook {
    book: Subject::new("/books/42"),
    title: "2001 – A Space Odyssey".to_owned(),
    author: "Arthur C. Clarke".to_owned(),
    isbn: "978-0756906788".to_owned(),
  })
  .await
  .then_preconditions([Precondition::require(eventsourcingdb::Precondition::is_subject_pristine(
    "/books/42",
    Some(eventsourcingdb::PreconditionOptions { recursive: true }),
  ))]);
```

To get the preconditions of a command directly, call its `preconditions` method, which the `Command` trait declares. It returns a `Vec<Precondition>`, which a test compares with `assert_eq!`, since `Precondition` implements `PartialEq` and `Debug`:

```rust
use architecturekit::Command;

let preconditions = ReturnBook { book: Subject::new("/books/42") }.preconditions();
```

## Inspecting State

To check the state the command has been decided on, call the `then_state` method with a function that receives the state, and panics, as `assert!` does, if the state is not what you expect:

```rust
given(
  &return_book(),
  [
    book_acquired(),
    BookEvent::Borrowed(BookBorrowed {
      borrowed_by: "23".to_owned(),
      borrowed_until: "2026-10-24".to_owned(),
    }),
  ],
)
.when(ReturnBook { book: Subject::new("/books/42") })
.await
.then_state(|book| assert!(book.is_borrowed, "expected the book to be borrowed"));
```

## Testing Upcasters

To test an upcaster, call the `given_stored` function instead of `given`, and hand over the events as they are stored. They run through the upcasters of the decider's state, as they do when reading from the database, so the example below needs a `Book` whose `upcasters` returns the upcasters of the library (see **[Versioning Events](/docs/architecturekit/rust/versioning-events)**). Without them, the state has no rule for the older event type, and `given_stored` fails the test. To turn a typed event into a stored one, create a `StoredEvent` with the subject, the event ID, and the event, and for an event that no type describes, such as one of an older type, with `StoredEvent::raw` and its type and data, as a `Value` of the `serde_json` crate, which you add with `cargo add serde_json --dev`:

```rust
use architecturekit::testing::{StoredEvent, given_stored};
use serde_json::json;

given_stored(
  &borrow_book(),
  [
    StoredEvent::new(Subject::new("/books/42"), 0, &book_acquired()),
    StoredEvent::raw(
      Subject::new("/books/42"),
      1,
      "io.eventsourcingdb.library.book-lent",
      json!({ "lentTo": "23", "until": "2026-10-24" }),
    ),
  ],
)
.when(BorrowBook {
  book: Subject::new("/books/42"),
  reader_id: "17".to_owned(),
  borrowed_until: "2026-10-31".to_owned(),
})
.await
.then_failed_in(Category::Domain);
```

The upcaster turns the older event into a `BookBorrowed` event, so the book is borrowed already, and `borrow_book` rejects the command.

## Replaying Events Directly

To evolve a state from events without a decider, call the `replay` function with typed events, or the `replay_stored` function with stored events. To check the state after each event, advance it one event at a time with the `step` function (see **[Stepping Through States](/docs/architecturekit/rust/stepping-through-states)**):

```rust
use architecturekit::replay;

let book: Book = replay([
  book_acquired(),
  BookEvent::Borrowed(BookBorrowed {
    borrowed_by: "23".to_owned(),
    borrowed_until: "2026-10-24".to_owned(),
  }),
])?;
```
