# 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, use the `architecturekittest` package:

*(Changed in 2.0)*

```go
import "github.com/thenativeweb/architecturekit-golang/v2/architecturekit/architecturekittest"
```

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

Call the `Given` function with a `*testing.T`, the decider, and the events that have happened so far. Then call the `When` function with the command, and check the decision:

```go
func TestBorrowBook(t *testing.T) {
  architecturekittest.Given(t, borrowBook,
    BookAcquired{
      Title:  "2001 – A Space Odyssey",
      Author: "Arthur C. Clarke",
      ISBN:   "978-0756906788",
    },
  ).
    When(BorrowBook{
      BookID:        "42",
      ReaderID:      "23",
      BorrowedUntil: "2026-10-24",
    }).
    ThenEvents(BookBorrowed{
      BorrowedBy:    "23",
      BorrowedUntil: "2026-10-24",
    })
}
```

`Given` returns a `*Fixture`, and `When` returns a `*Decision`, which is the decider's decision on the command: events, nothing, or a refusal. The functions that check the decision return the decision again, so they can be chained.

Like `Execute`, `When` refuses an event that is `nil`, a `nil` pointer included, an event that the state of the decider has no rule for, and an event whose data can not be encoded as JSON, for example because it holds a float `NaN` (see **[Executing Commands](/docs/architecturekit/v2.0/executing-commands)**). As `Execute` does, it checks all events for `nil` first, then all of them for a rule, and encodes them last. It also 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/v2.0/using-preconditions)**). The decision then holds the same error of the category `ErrPermanent` that `Execute` returns, so `ThenEvents` and the other functions that expect events, or nothing, fail and name the cause, and `ThenFailed(architecturekit.ErrPermanent)` matches. Only an event ID of `OnEventID` that is not a revision is refused with the error that wraps `ErrNotARevision` instead, as `Execute` does, which `ThenFailed(architecturekit.ErrNotARevision)` matches.

*Note that `Given` takes a `testing.TB`, as does every function of the package that can fail a test, so it works with the `*testing.B` of a benchmark as well.*

## Expecting Events

To expect exactly the given events, in the given order, call the `ThenEvents` function, as shown above. To expect neither events nor an error, call the `ThenNothing` function:

```go
architecturekittest.Given(t, returnBook, BookAcquired{}).
  When(ReturnBook{BookID: "42"}).
  ThenNothing()
```

To check the events with a function, call the `ThenSomeEvent` function to expect at least one matching event, the `ThenEveryEvent` function to expect only matching events, and at least one, or the `ThenNoEvent` function to expect no matching event:

```go
isBookBorrowed := func(event architecturekit.Event) bool {
  _, ok := event.(BookBorrowed)
  return ok
}

architecturekittest.Given(t, borrowBook, BookAcquired{}).
  When(BorrowBook{BookID: "42", ReaderID: "23"}).
  ThenEveryEvent(isBookBorrowed)
```

*Note that `ThenSomeEvent`, `ThenEveryEvent`, and `ThenNoEvent` fail the test for `nil` as the function, whatever the decision is, and say so. Otherwise, `ThenNoEvent` would pass for a decision without events, although it checks nothing.*

## Expecting Rejections

To expect that a command is rejected, call the `ThenFailed` function with the error you expect. It matches with `errors.Is`, so it takes the error the decider returns, as well as every error that this error wraps. For example, `acquireBook` wraps `ErrBookAlreadyAcquired` to add the ID of the book (see **[Making Decisions](/docs/architecturekit/v2.0/making-decisions)**), and `ThenFailed` still finds it:

```go
architecturekittest.Given(t, acquireBook, BookAcquired{}).
  When(AcquireBook{BookID: "42"}).
  ThenFailed(ErrBookAlreadyAcquired)
```

To expect any error of a category instead, hand over the category, such as `architecturekit.ErrDomain` or `architecturekit.ErrPermanent`. For example, `borrowBook` rejects a book that does not exist with an error that it creates in place with `NewDomainError`, which belongs to the category `ErrDomain`:

```go
architecturekittest.Given(t, borrowBook).
  When(BorrowBook{BookID: "42"}).
  ThenFailed(architecturekit.ErrDomain)
```

*Note that `ThenFailed` does not compare messages, so rewording a rejection breaks no test. To tell a rejection apart from the others of the same category, create it once, as a variable, as `ErrBookAlreadyAcquired` is.*

## Expecting Preconditions

To expect exactly the given preconditions, in the given order, call the `ThenPreconditions` function. Describe the preconditions with the functions of the same names, `OnStateRead`, `Unconditionally`, `OnPristineSubject`, `OnPopulatedSubject`, and `OnEventID`, and one made with `Require` from an EventQL query with the `OnQuery` function. 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/v2.0/using-preconditions#checking-the-revision-of-the-caller)**):

```go
architecturekittest.Given(t, borrowBook, BookAcquired{}).
  When(BorrowBook{BookID: "42", ReaderID: "23", ExpectedEventID: "0"}).
  ThenPreconditions(architecturekittest.OnEventID("/books/42", "0"))
```

`ThenPreconditions` describes a precondition by what the database checks, so `OnPristineSubject`, `OnPopulatedSubject`, and `OnEventID` also describe a precondition made with `Require` from the function of the client SDK that does the same, such as `NewIsSubjectPristinePrecondition`. 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/v2.0/using-preconditions#preventing-duplicates)**):

```go
architecturekittest.Given(t, acquireBook).
  When(AcquireBook{BookID: "42"}).
  ThenPreconditions(architecturekittest.OnPristineSubject("/books/42"))
```

For a pristine or a populated subject, the two are alike. For a revision, they are not: only `OnEventID` checks the event ID before anything is read, and refuses one that is not an event ID with an error that wraps `ErrNotARevision`, while `Require` with `NewIsSubjectOnEventIDPrecondition` leaves it to the database, which refuses it as a malformed request, so that `Execute` fails with an error of the category `ErrPermanent` (see **[Checking the Revision of the Caller](/docs/architecturekit/v2.0/using-preconditions#checking-the-revision-of-the-caller)**). `ThenPreconditions` matches both, so a test tells them apart by what they do. For example, the following test passes for the `BorrowBook` that checks the revision of the caller with `OnEventID`, and fails for one that uses `Require` instead, since the test fixture then leaves the ID to the database, as `Execute` does, and the decider decides on the command:

```go
architecturekittest.Given(t, borrowBook, BookAcquired{}).
  When(BorrowBook{BookID: "42", ReaderID: "23", ExpectedEventID: "abc"}).
  ThenFailed(architecturekit.ErrNotARevision)
```

*(Changed in 2.1)* To get the preconditions of a command directly, call the `PreconditionsOf` function. It returns a slice of `Precondition`, with the fields `Subject`, `Pristine`, `Populated`, `EventID`, `Query`, `OnStateRead`, and `Unconditional`:

```go
preconditions := architecturekittest.PreconditionsOf(ReturnBook{BookID: "42"})
```

## Inspecting State

To check the state the command has been decided on, call the `ThenState` function with a function that receives the state:

```go
architecturekittest.Given(t, returnBook, BookAcquired{}, BookBorrowed{}).
  When(ReturnBook{BookID: "42"}).
  ThenState(func(book Book) {
    if !book.IsBorrowed {
      t.Fatal("expected the book to be borrowed")
    }
  })
```

*Note that `ThenState` fails the test for `nil` as the function, and says so.*

## Testing Upcasters

To test an upcaster, call the `GivenStored` 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 `bookState.UpcastWith(libraryUpcasters)` (see **[Versioning Events](/docs/architecturekit/v2.0/versioning-events)**). Without it, the state has no rule for the older event type, and `GivenStored` fails the test. To turn a typed event into a stored one, call the `StoredEvent` function with the subject, the event ID, and the event:

```go
architecturekittest.GivenStored(t, borrowBook,
  architecturekittest.StoredEvent("/books/42", "0", BookAcquired{
    Title:  "2001 – A Space Odyssey",
    Author: "Arthur C. Clarke",
    ISBN:   "978-0756906788",
  }),
  eventsourcingdb.Event{
    Subject: "/books/42",
    Type:    "io.eventsourcingdb.library.book-lent",
    ID:      "1",
    Data:    json.RawMessage(`{"lentTo":"23","until":"2026-10-24"}`),
  },
).
  When(BorrowBook{BookID: "42", ReaderID: "17"}).
  ThenFailed(architecturekit.ErrDomain)
```

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

## Replaying Events Directly

To evolve a state from events without a decider, call the `Replay` function with the state and typed events, or the `ReplayStored` function with the state and 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/v2.0/stepping-through-states)**):

```go
book, err := architecturekit.Replay(bookState, BookAcquired{}, BookBorrowed{})
if err != nil {
  // ...
}
```
