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 = 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 { 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(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); } }