More Testing

Why test manually when you can test automated...ly

Amazing work so far. We have a nice little API running and can store and fetch secrets. What if we want to do some refactoring though, how do we guarantee that the behavior that we want stays the same? Well, automated testing is the answer! We already went over test setup, so this part will be mostly review and maybe some copy/pasta, but if you haven't copy pasted code from the internet, are you even a real engineer?

Add this test code to a new file, tests/secrets.rs. Copy the code below and then make sure you run your tests as we did in the earlier testing section!

use carrier_pigeon::{
    app::app,
    controllers::secrets::{CreateSecretReq, CreateSecretRes, GetSecretRes},
};
use chrono::Utc;
use hyper::Body;
use rand::{distributions::Alphanumeric, thread_rng, Rng};
use sqlx::{postgres::PgPoolOptions, Executor};
use thruster::Testable;

async fn setup() -> impl Testable {
    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");

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

    app
}

#[tokio::test]
async fn it_should_respond_to_create_secret() {
    let app = setup().await;
    let secret: String = thread_rng()
        .sample_iter(&Alphanumeric)
        .take(24)
        .map(char::from)
        .collect();

    let response = Testable::post(
        &app,
        "/secrets",
        vec![],
        Body::from(
            serde_json::to_string(&CreateSecretReq {
                secret,
                expires_at: Utc::now(),
            })
            .expect("Could not serialize request"),
        ),
    )
    .await
    .expect("Could not run get")
    .expect_status(200, "It should return a 200 response code");

    // We're just testing the parsing succeeds here, no need to test the actual code yet.
    let _response_json = serde_json::from_str::<CreateSecretRes>(&response.body_string())
        .expect("Could not parse response");
}

#[tokio::test]
async fn it_should_correctly_return_a_secret() {
    let app = setup().await;
    let secret: String = thread_rng()
        .sample_iter(&Alphanumeric)
        .take(24)
        .map(char::from)
        .collect();

    let response = Testable::post(
        &app,
        "/secrets",
        vec![],
        Body::from(
            serde_json::to_string(&CreateSecretReq {
                secret: secret.clone(),
                expires_at: Utc::now(),
            })
            .expect("Could not serialize request"),
        ),
    )
    .await
    .expect("Could not run get")
    .expect_status(200, "It should return a 200 response code");

    let response_json = serde_json::from_str::<CreateSecretRes>(&response.body_string())
        .expect("Could not parse response");

    let response = Testable::get(
        &app,
        &format!("/secrets/{}?code={}", response_json.id, response_json.code),
        vec![],
    )
    .await
    .expect("Could not run get")
    .expect_status(200, "It should return a 200 response code");

    let response_json = serde_json::from_str::<GetSecretRes>(&response.body_string())
        .expect("Could not parse response");

    assert_eq!(
        response_json.secret, secret,
        "The resulting secret should be the same as the original"
    );
}

#[tokio::test]
async fn it_should_404_when_a_secret_is_requested_twice() {
    let app = setup().await;
    let secret: String = thread_rng()
        .sample_iter(&Alphanumeric)
        .take(24)
        .map(char::from)
        .collect();

    let response = Testable::post(
        &app,
        "/secrets",
        vec![],
        Body::from(
            serde_json::to_string(&CreateSecretReq {
                secret: secret.clone(),
                expires_at: Utc::now(),
            })
            .expect("Could not serialize request"),
        ),
    )
    .await
    .expect("Could not run get")
    .expect_status(200, "It should return a 200 response code");

    let response_json = serde_json::from_str::<CreateSecretRes>(&response.body_string())
        .expect("Could not parse response");

    let _response = Testable::get(
        &app,
        &format!("/secrets/{}?code={}", response_json.id, response_json.code),
        vec![],
    )
    .await
    .expect("Could not run get")
    .expect_status(200, "It should return a 200 response code");
    let _response = Testable::get(
        &app,
        &format!("/secrets/{}?code={}", response_json.id, response_json.code),
        vec![],
    )
    .await
    .expect("Could not run get")
    .expect_status(
        404,
        "It should return a 404 response when a secret has been decrypted more than once",
    );
}

That's it!! Now... how do we deploy...?

Last updated