feat: tests and big refactor

This commit is contained in:
Ninjdai 2025-08-09 17:43:31 +02:00
parent 05e8366611
commit 82f2bb4a61
8 changed files with 402 additions and 238 deletions

61
tests/auth.rs Normal file
View file

@ -0,0 +1,61 @@
use std::collections::HashMap;
use alexandria::routes::auth::AuthBody;
use reqwest::StatusCode;
mod common;
#[tokio::test]
async fn auth_wrong_password() {
let data = common::setup().await;
let client = reqwest::Client::new();
let mut wrong_pwd_auth_map = HashMap::new();
wrong_pwd_auth_map.insert("username", "test_username");
wrong_pwd_auth_map.insert("password", "pwd");
let wrong_pwd_auth_res = client.execute(client.post(format!("{}/auth", data.api_path)).json(&wrong_pwd_auth_map).build().unwrap()).await.unwrap();
assert_eq!(wrong_pwd_auth_res.status(), StatusCode::UNAUTHORIZED);
}
#[tokio::test]
async fn auth_wrong_username() {
let data = common::setup().await;
let client = reqwest::Client::new();
let mut wrong_username_auth_map = HashMap::new();
wrong_username_auth_map.insert("username", "wrong_username");
wrong_username_auth_map.insert("password", "test_password");
let wrong_username_auth_res = client.execute(client.post(format!("{}/auth", data.api_path)).json(&wrong_username_auth_map).build().unwrap()).await.unwrap();
assert_eq!(wrong_username_auth_res.status(), StatusCode::UNAUTHORIZED);
}
#[tokio::test]
async fn auth_correct_credentials() {
let data = common::setup().await;
let client = reqwest::Client::new();
let mut auth_map = HashMap::new();
auth_map.insert("username", "test_username");
auth_map.insert("password", "test_password");
let auth_res = client.execute(client.post(format!("{}/auth", data.api_path)).json(&auth_map).build().unwrap()).await.unwrap();
assert_eq!(auth_res.status(), StatusCode::OK);
let auth_body = auth_res.json::<AuthBody>().await.unwrap();
let mut check_token_map = HashMap::new();
check_token_map.insert("token", auth_body.access_token);
let check_token_res = client.execute(client.post(format!("{}/token-check", data.api_path)).json(&check_token_map).build().unwrap()).await.unwrap();
let valid_token = check_token_res.json::<bool>().await.unwrap();
assert_eq!(valid_token, true);
}
#[tokio::test]
async fn auth_wrong_token_check() {
let data = common::setup().await;
let client = reqwest::Client::new();
let mut check_wrong_token_map = HashMap::new();
check_wrong_token_map.insert("token", "this-is-definitely-not-a-wrong-token");
let check_wrong_token_res = client.execute(client.post(format!("{}/token-check", data.api_path)).json(&check_wrong_token_map).build().unwrap()).await.unwrap();
let invalid_token = check_wrong_token_res.json::<bool>().await.unwrap();
assert_eq!(invalid_token, false);
}

86
tests/common/mod.rs Normal file
View file

@ -0,0 +1,86 @@
use std::{net::{Ipv4Addr, SocketAddrV4, TcpListener}, sync::Arc, time::{SystemTime, UNIX_EPOCH}};
use sea_orm::{ActiveModelTrait, ActiveValue::{NotSet, Set}, ColumnTrait, ConnectionTrait, Database, DatabaseConnection, EntityTrait, QueryFilter};
use alexandria::{create_tables, entities::{owner, prelude::*, user}, routes::auth::{generate_token_from_claims, Claims, DEFAULT_TOKEN_EXPIRY_TIME}, run_server, utils::auth::hash_password};
pub struct SetupData {
/// A valid JWT for testing features that need authentication
pub jwt: String,
pub api_path: String
}
/// Common setup function for tests that require a database and server setup
pub async fn setup() -> SetupData {
let _ = pretty_env_logger::try_init();
let db: Arc<DatabaseConnection> = Arc::new(
match Database::connect(format!("sqlite::memory:?mode=rwc")).await {
Ok(c) => c,
Err(e) => {
panic!("Error while opening fatabase: {}", e.to_string())
}
});
create_tables(db.as_ref()).await.expect("Create tables should not fail");
let port = free_local_ipv4_port().expect("Could not get a free port");
let db_c = db.clone();
tokio::spawn(async move {
run_server(db_c, port, false).await;
});
create_user(db.as_ref(), "test_username", "test_password").await;
let unix_timestamp = SystemTime::now().duration_since(UNIX_EPOCH).expect("Time should go forward").as_secs();
let claims = Claims {
sub: "test_username".to_string(),
exp: unix_timestamp + DEFAULT_TOKEN_EXPIRY_TIME,
user_id: 1
};
SetupData {
jwt: generate_token_from_claims(claims),
api_path: format!("http://0.0.0.0:{port}/api")
}
}
fn free_local_ipv4_port() -> Option<u16> {
let socket = SocketAddrV4::new(Ipv4Addr::LOCALHOST, 0);
TcpListener::bind(socket)
.and_then(|listener| listener.local_addr())
.map(|addr| addr.port())
.ok()
}
async fn create_user<C>(db_conn: &C, username_t: impl ToString, password_t: impl ToString)
where C: ConnectionTrait {
let username = username_t.to_string();
if User::find().filter(user::Column::Username.eq(username.clone())).one(db_conn).await.is_ok_and(|r| r.is_some()) {
panic!("Username {username} already in use");
} else {
let password = password_t.to_string();
let mut new_user = user::ActiveModel {
id: NotSet,
username: Set(username.clone()),
hashed_password: Set(hash_password(password)),
current_bal_id: Set(None),
owner_id: Set(None)
};
let res = new_user.clone().insert(db_conn).await.unwrap();
let new_owner = owner::ActiveModel {
id: NotSet,
user_id: Set(res.id),
first_name: Set(format!("{username} first name")),
last_name: Set(format!("{username} last name")),
contact: Set(format!("{username}@mail.com"))
};
let owner_res = new_owner.insert(db_conn).await.unwrap();
new_user.owner_id = Set(Some(owner_res.id));
let _ = new_user.update(db_conn);
}
}