From 659bc0d0088b6c2626bbf50fc0219fa322c2ee1b Mon Sep 17 00:00:00 2001 From: Joe Ardent Date: Fri, 2 Jun 2023 14:12:29 -0700 Subject: [PATCH] Add tests for login/logout endpoints. Changes in the test DB setup are there to support persistent in-memory DBs for testing. --- src/db.rs | 25 ++++++------- src/login.rs | 100 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+), 12 deletions(-) diff --git a/src/db.rs b/src/db.rs index 593e091..7358ff1 100644 --- a/src/db.rs +++ b/src/db.rs @@ -16,7 +16,8 @@ use uuid::Uuid; use crate::User; const MAX_CONNS: u32 = 100; -const TIMEOUT: u64 = 5; +const MIN_CONNS: u32 = 10; +const TIMEOUT: u64 = 11; const SESSION_TTL: Duration = Duration::from_secs((365.2422 * 24. * 3600.0) as u64); pub async fn get_pool() -> SqlitePool { @@ -30,11 +31,16 @@ pub async fn get_pool() -> SqlitePool { } #[cfg(test)] { - ":memory:".to_string() + use rand_core::RngCore; + let mut rng = rand_core::OsRng; + let id = rng.next_u64(); + format!("file:testdb-{id}?mode=memory&cache=shared") } }) }; + tracing::info!("Connecting to DB at {db_filename}"); + let conn_opts = SqliteConnectOptions::new() .foreign_keys(true) .auto_vacuum(sqlx::sqlite::SqliteAutoVacuum::Incremental) @@ -44,6 +50,9 @@ pub async fn get_pool() -> SqlitePool { let pool = SqlitePoolOptions::new() .max_connections(MAX_CONNS) + .min_connections(MIN_CONNS) + .idle_timeout(Some(Duration::from_secs(10))) + .max_lifetime(Some(Duration::from_secs(3600))) .connect_with(conn_opts) .await .expect("can't connect to database"); @@ -58,14 +67,10 @@ pub async fn get_pool() -> SqlitePool { m.run(&pool) .await .expect("Should be able to run the migration."); + + tracing::info!("Ran migrations"); } - // hack to ensure that migration has actually run; surely there is a better - // way!! - tokio::time::sleep(Duration::from_secs(1)).await; - - tracing::info!("Ran migrations"); - pool } @@ -76,10 +81,6 @@ pub async fn session_layer(pool: SqlitePool, secret: &[u8]) -> SessionLayer impl IntoResponse { } LogoutPost } + +#[cfg(test)] +mod test { + use std::time::Duration; + + use axum::body::Bytes; + use axum_test::TestServer; + + use crate::{ + db, + signup::create_user, + templates::{LoginGet, LogoutGet, LogoutPost}, + }; + + async fn tserver() -> TestServer { + let pool = db::get_pool().await; + let secret = [0u8; 64]; + + tokio::time::sleep(Duration::from_secs(2)).await; + + let _user = create_user( + "test_user", + &Some("Test User".to_string()), + &Some("mail@email".to_string()), + "aaaa".as_bytes(), + &pool, + ) + .await + .unwrap(); + + let r = sqlx::query("select count(*) from witches") + .fetch_one(&pool) + .await; + assert!(r.is_ok()); + + let app = crate::app(pool, &secret).await.into_make_service(); + + TestServer::new(app).unwrap() + } + + #[tokio::test] + async fn get_login() { + let s = tserver().await; + let resp = s.get("/login").await; + let body = std::str::from_utf8(resp.bytes()).unwrap().to_string(); + assert_eq!(body, LoginGet::default().to_string()); + } + + #[tokio::test] + async fn post_login_success() { + let s = tserver().await; + + let form = "username=test_user&password=aaaa".to_string(); + let bytes = form.as_bytes(); + let body = Bytes::copy_from_slice(bytes); + + let resp = s + .post("/login") + .expect_failure() + .content_type("application/x-www-form-urlencoded") + .bytes(body) + .await; + assert_eq!(resp.status_code(), 303); + } + + #[tokio::test] + async fn post_login_bad_user() { + let s = tserver().await; + + let form = "username=test_LOSER&password=aaaa".to_string(); + let bytes = form.as_bytes(); + let body = Bytes::copy_from_slice(bytes); + + let resp = s + .post("/login") + .expect_success() + .content_type("application/x-www-form-urlencoded") + .bytes(body) + .await; + assert_eq!(resp.status_code(), 200); + } + + #[tokio::test] + async fn get_logout() { + let s = tserver().await; + let resp = s.get("/logout").await; + let body = std::str::from_utf8(resp.bytes()).unwrap().to_string(); + assert_eq!(body, LogoutGet.to_string()); + } + + #[tokio::test] + async fn post_logout() { + let s = tserver().await; + let resp = s.post("/logout").await; + resp.assert_status_ok(); + let body = std::str::from_utf8(resp.bytes()).unwrap(); + let default = LogoutPost.to_string(); + assert_eq!(body, &default); + } +}