# Testing with a Database

Some tests need a real database, for example to run commands from end to end.

To get one, turn on the feature `testcontainers` for the tests, which starts EventSourcingDB in a container, and therefore needs Docker. It turns on the feature `testing` as well:

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

Call the `testing::store` function with the source and the schemas to register. **It returns a store on a database that all tests of the program share**, which the first test that asks for it starts in a container, while the others wait for that. The example gives the book a random ID of the `uuid` crate, which you add with `cargo add uuid --dev --features v4`:

```rust
use architecturekit::{Events, testing};
use uuid::Uuid;

#[tokio::test]
async fn acquires_a_book() {
  let store = testing::store("https://library.eventsourcingdb.io", BookEvent::schemas()).await;

  store
    .execute(
      &acquire_book(),
      AcquireBook {
        book: BOOKS.build(&[Uuid::new_v4().to_string()]),
        title: "2001 – A Space Odyssey".to_owned(),
        author: "Arthur C. Clarke".to_owned(),
        isbn: "978-0756906788".to_owned(),
      },
    )
    .await
    .expect("the book is acquired");
}
```

To build the store with options, such as `state_cache`, call the `testing::store_with` function, with a function that sets them on the builder, so that a test runs with the same store as the application:

```rust
let store =
  testing::store_with("https://library.eventsourcingdb.io", BookEvent::schemas(), |builder| {
    builder.state_cache(1_000)
  })
  .await;
```

**The shared database keeps running beyond the test that started it, until the program ends**, so there is nothing to stop once the tests have run. Then the reaper of Testcontainers, Ryuk, which runs in a container of its own, removes it a few seconds later, however the program ends, even if it is killed. The database is registered with Ryuk before it starts, so that holds even for a program that ends while the database is still starting. Ryuk is configured with environment variables: `TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED` set to `true` starts it privileged, `TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE` names the socket of Docker where it does not live at `/var/run/docker.sock`, and `TESTCONTAINERS_RYUK_DISABLED` set to `true` turns it off, which leaves the database behind.

*Note that the schemas come as one argument. To register the schemas of several enums, chain them, for example `BookEvent::schemas().into_iter().chain(ReaderEvent::schemas())`.*

The tests share the events as well, so a test writes to subjects of its own, for example with a random ID in them, and reads only from those. A test that reads more than that, such as a projection from `/`, needs a database of its own. Call `Database::isolated` to start one for the test alone, and ask it for a store. Its container stops once the database is dropped, so keep it in a local variable for as long as the test runs. It takes about a second to start, so use it only where the shared one would not do:

```rust
use architecturekit::testing::Database;

let database = Database::isolated().await;
let store = database.store("https://library.eventsourcingdb.io", BookEvent::schemas()).await;
```

For a test that connects by itself, such as one that starts a whole server, call `Database::shared` for the shared database, or `Database::isolated`. The `url` and `api_token` methods of a `Database` return what a client needs. Its `client` method returns a client, for example to write an event that no command would, and its `store` and `store_with` methods return a store, as above. Here, `Config` stands for the configuration of your application:

```rust
let database = Database::shared().await;

let config =
  Config { database_url: database.url().to_string(), api_token: database.api_token().to_owned() };
```

*Note that every test that asks for a database fails if the database can not be started, for example since Docker is not running, with the message of the failure. A failed start of the shared database is not tried again: every test that asks for it later fails with the same message. To run the other tests without Docker, mark the tests that need a database with `#[ignore]`, and run them where Docker is available, with `cargo test -- --include-ignored`.*

*Note that the database runs the image `thenativeweb/eventsourcingdb` with the tag that the environment variable `EVENTSOURCINGDB_IMAGE_TAG` names, or `latest` without it, as for the tests of the client SDK, for example `EVENTSOURCINGDB_IMAGE_TAG=preview cargo test`. Docker pulls the image only if it is missing, so which release a tag stands for depends on what the machine has pulled before. The database runs without a signing key, so its events carry no signature, and a store with `signature_verification` fails to read them with `Permanent::Unverified`. Signature verification can therefore not be tested with it (see **[Verifying Events](/docs/architecturekit/rust/verifying-events)**).*

*Note that `testcontainers` is a feature of its own because it brings along Testcontainers and a client of Docker. Tests that only use the feature `testing`, for example to test deciders and projections, build without either.*
