# Testing

It's important for any API to have a set of automated tests, that way you can change things with at least a little bit of confidence that the behavior will stay the same at the output. Thruster comes will a full test suite to help developer test their code. In order to use it, we'll have to do a little refactoring.

First, we're going to make a method specifically to create an `app`. Right now we're building the app and the server in the `main` function, so we'll split that out. First create a new file, `app.rs`

```rust
use sqlx::{Pool, Postgres};
use thruster::{
    context::typed_hyper_context::TypedHyperContext, m, middleware_fn, App, HyperRequest,
    MiddlewareNext, MiddlewareResult,
};

pub type Ctx = TypedHyperContext<RequestConfig>;

#[derive(Default)]
pub struct ServerConfig {}

#[derive(Default)]
pub struct RequestConfig {}

fn generate_context(request: HyperRequest, _state: &ServerConfig, _path: &str) -> Ctx {
    Ctx::new(request, RequestConfig {})
}

#[middleware_fn]
async fn hello(mut context: Ctx, _next: MiddlewareNext<Ctx>) -> MiddlewareResult<Ctx> {
    context.body("Hello, world!");

    Ok(context)
}

pub async fn app(
    _pool: Pool<Postgres>,
) -> Result<App<HyperRequest, Ctx, ServerConfig>, Box<dyn std::error::Error>> {
    Ok(
        App::<HyperRequest, Ctx, ServerConfig>::create(generate_context, ServerConfig {})
            .get("/hello", m![hello]),
    )
}

```

We *basically* copied over everything from `main.rs` and made `Ctx`, `ServerConfig`, and `RequestConfig` public. With that, we need to update our `main.rs` file as well, it'll get a lot simpler without all the app creation and middleware functions:

```rust
use log::info;
use sqlx::{postgres::PgPoolOptions, Executor};
use thruster::{hyper_server::HyperServer, ThrusterServer};

pub mod app;

#[tokio::main]
async fn main() {
    env_logger::init();
    info!("Starting server...");

    let pool = PgPoolOptions::new()
        .max_connections(5)
        .connect("postgres://postgres:password@localhost:5432/pigeon")
        .await
        .expect("Could not create postgres connection pool");

    pool.execute(include_str!("../schema.sql"))
        .await
        .expect("Could not create schema in database");

    let app = app::app(pool).await.expect("Could not create app");

    let server = HyperServer::new(app);
    server.build("0.0.0.0", 4321).await;
}
```

In order to use our library in tests we also need, well, a library! So create a `lib.rs` file. In that file, add a single line (for now.)

```rust
pub mod app;
```

Now we're in a great place to write a simple test, starting with our `/hello` route. Make a new file in a new folder named `tests/` called `hello.rs`. I like to name tests based on routes, but you can really organize them whoever you please.

In `hello.rs`, we're going to make a very simple test as follows:

```rust
use carrier_pigeon::app::app;
use sqlx::{postgres::PgPoolOptions, Executor};
use thruster::Testable;

#[tokio::test]
async fn it_should_respond_to_create_user() {
    let pool = PgPoolOptions::new()
        .max_connections(5)
        .connect("postgres://postgres:password@localhost:5433/pigeon-test")
        .await
        .expect("Could not create postgres connection pool");

    pool.execute(include_str!("../schema.sql"))
        .await
        .expect("Could not create schema in database");
        
    // 1
    let app = app(pool).await.expect("Could not create app").commit();

    // 2
    let response = Testable::get(&app, "/hello", vec![])
        .await
        .expect("Could not run get")
        .expect_status(200, "It should return a 200 response code");

    assert_eq!(
        response.body_string(),
        "Hello, world!",
        "It should return the correct body"
    );
}

```

Some of this code should look *very* familiar! We're making a new database pool, and then running our quick schema to make sure it's in the correct state. One difference to note is that we're explicitly calling out port 5433 instead of the typical 5432. This is to make sure that we're not running tests on our local development database! We should start another container for testing before we actually run this,

```bash
docker run -d \
    -e POSTGRES_PASSWORD=password \
    -e POSTGRES_DB=pigeon-test \
    -p 5433:5432
    --name=pigeon-test \
    postgres
```

In the above rust code, I'll note a few important pieces here:

1. Note that we're creating the app the same way we would in the `main` function, *however* we're also running this little `commit` method at the end. It's not terribly important for this tutorial as to why that needs to be run, but loosely it's a method that gets run when the server starts so that it can efficiently serve routes. If you *didn't* run it you would get 404 status codes. If you feel like it, go ahead and comment it out and see what part of the test fails.
2. Thruster provides the `Testable` trait, which basically is an extension of `App` specifically for testing. It uses the same names as setting a new route for the app, so we use the "[fully qualified syntax](https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#fully-qualified-syntax-for-disambiguation-calling-methods-with-the-same-name)" so that the compiler knows we want to call the test version. This test version basically fakes a request using the same router and middleware, but ignoring the "Server" backend piece (the piece that thruster can easily swap out between hyper, actix, or the homegrown version for example.)

Now you should be ready to run the test! Run

```
cargo test
```

and bask in the glory of your newly tested code.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://mertz.gitbook.io/thruster/building-a-cli-secret-sharer/testing.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
