feat(backend): replace auth stub with real session, create users and sessions tables

This commit is contained in:
2026-05-26 02:34:23 +03:00
parent a54ec4ed34
commit e7eaa42064
11 changed files with 636 additions and 135 deletions

View File

@@ -93,36 +93,71 @@ mod tests {
TestServer,
multipart::{MultipartForm, Part},
};
use rand::RngExt;
use serde_json::json;
use sqlx::PgPool;
use tower_cookies::CookieManagerLayer;
use crate::{
model::FileRepository,
model::{FileRepository, UserRepository},
web::{
mw_auth::{mw_ctx_resolver, mw_require_auth},
routes_file::routes_file,
},
};
// Cookie format: user-[user-id].[expiration].[signature]
const AUTH_COOKIE: &str = "auth-token=user-1.0123456789.a1b2c3d4e5f6";
const BAD_AUTH_COOKIE: &str = "auth-token=user-1.0123456789";
const BAD_AUTH_COOKIE: &str = "auth-token=user-0123456789";
async fn file_repository() -> FileRepository {
dotenvy::dotenv().ok();
let database_url = std::env::var("DATABASE_URL").expect("DATABASE_URL must be set");
let pool = PgPool::connect(&database_url).await.unwrap();
let file_repository = FileRepository::new(pool).unwrap();
file_repository
}
async fn user_repository() -> UserRepository {
dotenvy::dotenv().ok();
let database_url = std::env::var("DATABASE_URL").expect("DATABASE_URL must be set");
let pool = PgPool::connect(&database_url).await.unwrap();
let user_repository = UserRepository::new(pool).unwrap();
user_repository
}
async fn test_server() -> TestServer {
let file_repository = FileRepository::new().await.unwrap();
let user_repository = user_repository().await;
let file_repository = file_repository().await;
let routes_file =
routes_file(file_repository.clone()).route_layer(middleware::from_fn(mw_require_auth));
let app = Router::new()
.nest("/api", routes_file)
.layer(middleware::from_fn_with_state(
file_repository,
user_repository,
mw_ctx_resolver,
))
.layer(CookieManagerLayer::new());
TestServer::new(app)
}
async fn create_test_session(user_repository: &UserRepository) -> String {
let user = user_repository
.create_user("testuser".to_string(), "hash".to_string())
.await
.unwrap();
let token: String = rand::rng()
.sample_iter(&rand::distr::Alphanumeric)
.take(64)
.map(char::from)
.collect();
let expires_at = chrono::Utc::now() + chrono::Duration::days(1);
user_repository
.create_session(user.id, token.clone(), expires_at)
.await
.unwrap();
token
}
async fn truncate(pool: &PgPool) {
sqlx::query!("TRUNCATE TABLE file_records")
.execute(pool)
@@ -149,7 +184,7 @@ mod tests {
#[tokio::test]
#[serial_test::serial]
async fn test_requires_auth_post() {
let file_repository = FileRepository::new().await.unwrap();
let file_repository = file_repository().await;
truncate(&file_repository.pool).await;
let server = test_server().await;
@@ -167,13 +202,15 @@ mod tests {
#[tokio::test]
#[serial_test::serial]
async fn test_list_files_empty() {
let file_repository = FileRepository::new().await.unwrap();
let user_repository = user_repository().await;
let token = create_test_session(&user_repository).await;
let file_repository = file_repository().await;
truncate(&file_repository.pool).await;
let server = test_server().await;
server
.get("/api/files")
.add_header(axum::http::header::COOKIE, AUTH_COOKIE)
.add_header(axum::http::header::COOKIE, format!("auth-token={token}"))
.await
.assert_status_ok()
.assert_json(&json!([]));
@@ -182,13 +219,15 @@ mod tests {
#[tokio::test]
#[serial_test::serial]
async fn test_upload_and_list_files() {
let file_repository = FileRepository::new().await.unwrap();
let user_repository = user_repository().await;
let token = create_test_session(&user_repository).await;
let file_repository = file_repository().await;
truncate(&file_repository.pool).await;
let server = test_server().await;
let res = server
.post("/api/files")
.add_header(axum::http::header::COOKIE, AUTH_COOKIE)
.add_header(axum::http::header::COOKIE, format!("auth-token={token}"))
.multipart(MultipartForm::new().add_part(
"file",
Part::bytes(b"fake_bytes".to_vec()).file_name("a.jpg"),
@@ -201,7 +240,7 @@ mod tests {
let list = server
.get("/api/files")
.add_header(axum::http::header::COOKIE, AUTH_COOKIE)
.add_header(axum::http::header::COOKIE, format!("auth-token={token}"))
.await
.json::<serde_json::Value>();
@@ -212,13 +251,15 @@ mod tests {
#[tokio::test]
#[serial_test::serial]
async fn test_download_file() {
let file_repository = FileRepository::new().await.unwrap();
let user_repository = user_repository().await;
let token = create_test_session(&user_repository).await;
let file_repository = file_repository().await;
truncate(&file_repository.pool).await;
let server = test_server().await;
let post_res = server
.post("/api/files")
.add_header(axum::http::header::COOKIE, AUTH_COOKIE)
.add_header(axum::http::header::COOKIE, format!("auth-token={token}"))
.multipart(MultipartForm::new().add_part(
"file",
Part::bytes(b"fake_bytes".to_vec()).file_name("a.jpg"),
@@ -227,7 +268,7 @@ mod tests {
let id = post_res.json::<serde_json::Value>()["id"].as_i64().unwrap();
let res = server
.get(&format!("/api/files/{id}/download"))
.add_header(axum::http::header::COOKIE, AUTH_COOKIE)
.add_header(axum::http::header::COOKIE, format!("auth-token={token}"))
.await;
res.assert_status_ok();
@@ -238,10 +279,12 @@ mod tests {
#[tokio::test]
#[serial_test::serial]
async fn test_download_file_not_found() {
let user_repository = user_repository().await;
let token = create_test_session(&user_repository).await;
let server = test_server().await;
server
.get("/api/files/99/download")
.add_header(axum::http::header::COOKIE, AUTH_COOKIE)
.add_header(axum::http::header::COOKIE, format!("auth-token={token}"))
.await
.assert_status_not_found();
}
@@ -249,13 +292,15 @@ mod tests {
#[tokio::test]
#[serial_test::serial]
async fn test_delete_file() {
let file_repository = FileRepository::new().await.unwrap();
let user_repository = user_repository().await;
let token = create_test_session(&user_repository).await;
let file_repository = file_repository().await;
truncate(&file_repository.pool).await;
let server = test_server().await;
let post_res = server
.post("/api/files")
.add_header(axum::http::header::COOKIE, AUTH_COOKIE)
.add_header(axum::http::header::COOKIE, format!("auth-token={token}"))
.multipart(MultipartForm::new().add_part(
"file",
Part::bytes(b"fake_bytes".to_vec()).file_name("a.jpg"),
@@ -265,13 +310,13 @@ mod tests {
server
.delete(&format!("/api/files/{id}"))
.add_header(axum::http::header::COOKIE, AUTH_COOKIE)
.add_header(axum::http::header::COOKIE, format!("auth-token={token}"))
.await
.assert_status_ok();
server
.get(&format!("/api/files/{id}"))
.add_header(axum::http::header::COOKIE, AUTH_COOKIE)
.add_header(axum::http::header::COOKIE, format!("auth-token={token}"))
.await
.assert_status_not_found();
@@ -281,10 +326,12 @@ mod tests {
#[tokio::test]
#[serial_test::serial]
async fn test_delete_file_not_found() {
let user_repository = user_repository().await;
let token = create_test_session(&user_repository).await;
let server = test_server().await;
server
.delete("/api/files/99")
.add_header(axum::http::header::COOKIE, AUTH_COOKIE)
.add_header(axum::http::header::COOKIE, format!("auth-token={token}"))
.await
.assert_status_not_found();
}