Add tests for login/logout endpoints.
Changes in the test DB setup are there to support persistent in-memory DBs for testing.
This commit is contained in:
parent
81adffe7f0
commit
659bc0d008
2 changed files with 113 additions and 12 deletions
25
src/db.rs
25
src/db.rs
|
@ -16,7 +16,8 @@ use uuid::Uuid;
|
||||||
use crate::User;
|
use crate::User;
|
||||||
|
|
||||||
const MAX_CONNS: u32 = 100;
|
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);
|
const SESSION_TTL: Duration = Duration::from_secs((365.2422 * 24. * 3600.0) as u64);
|
||||||
|
|
||||||
pub async fn get_pool() -> SqlitePool {
|
pub async fn get_pool() -> SqlitePool {
|
||||||
|
@ -30,11 +31,16 @@ pub async fn get_pool() -> SqlitePool {
|
||||||
}
|
}
|
||||||
#[cfg(test)]
|
#[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()
|
let conn_opts = SqliteConnectOptions::new()
|
||||||
.foreign_keys(true)
|
.foreign_keys(true)
|
||||||
.auto_vacuum(sqlx::sqlite::SqliteAutoVacuum::Incremental)
|
.auto_vacuum(sqlx::sqlite::SqliteAutoVacuum::Incremental)
|
||||||
|
@ -44,6 +50,9 @@ pub async fn get_pool() -> SqlitePool {
|
||||||
|
|
||||||
let pool = SqlitePoolOptions::new()
|
let pool = SqlitePoolOptions::new()
|
||||||
.max_connections(MAX_CONNS)
|
.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)
|
.connect_with(conn_opts)
|
||||||
.await
|
.await
|
||||||
.expect("can't connect to database");
|
.expect("can't connect to database");
|
||||||
|
@ -58,14 +67,10 @@ pub async fn get_pool() -> SqlitePool {
|
||||||
m.run(&pool)
|
m.run(&pool)
|
||||||
.await
|
.await
|
||||||
.expect("Should be able to run the migration.");
|
.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
|
pool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,10 +81,6 @@ pub async fn session_layer(pool: SqlitePool, secret: &[u8]) -> SessionLayer<Sqli
|
||||||
.await
|
.await
|
||||||
.expect("Calling `migrate()` should be reliable, is the DB gone?");
|
.expect("Calling `migrate()` should be reliable, is the DB gone?");
|
||||||
|
|
||||||
// hack to ensure that migration has actually run; surely there is a better
|
|
||||||
// way!!
|
|
||||||
tokio::time::sleep(Duration::from_secs(1)).await;
|
|
||||||
|
|
||||||
// since the secret is new every time the server starts, old sessions won't be
|
// since the secret is new every time the server starts, old sessions won't be
|
||||||
// valid anymore; if there were ever more than one service host or there were
|
// valid anymore; if there were ever more than one service host or there were
|
||||||
// managed secrets, this would need to go away.
|
// managed secrets, this would need to go away.
|
||||||
|
|
100
src/login.rs
100
src/login.rs
|
@ -99,3 +99,103 @@ pub async fn post_logout(mut auth: AuthContext) -> impl IntoResponse {
|
||||||
}
|
}
|
||||||
LogoutPost
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue