chore: scaffold axum server with in-memory CRUD, auth stub and tracing

This commit is contained in:
2026-04-19 16:03:04 +03:00
parent 132de93600
commit 90c9b9d6ff
10 changed files with 1756 additions and 13 deletions

View File

@@ -0,0 +1,132 @@
use axum::{
Json, Router,
extract::{Path, State},
routing::get,
};
use tracing::info;
use crate::{
error::LoftError,
model::{File, FileController, FileToCreate},
};
pub fn routes_file(file_controller: FileController) -> Router {
Router::new()
.route("/files", get(list_files).post(upload_file))
.route("/files/{id}", get(download_file).delete(delete_file))
.with_state(file_controller)
}
async fn upload_file(
State(file_controller): State<FileController>,
Json(file_to_create): Json<FileToCreate>,
) -> Result<Json<File>, LoftError> {
info!("handler: upload_file");
let file = file_controller.upload_file(file_to_create).await?;
Ok(Json(file))
}
async fn download_file(
State(file_controller): State<FileController>,
Path(file_id): Path<u64>,
) -> Result<Json<File>, LoftError> {
info!("handler: download_file");
let file = file_controller.download_file(file_id).await?;
Ok(Json(file))
}
async fn delete_file(
State(file_controller): State<FileController>,
Path(file_id): Path<u64>,
) -> Result<Json<File>, LoftError> {
info!("handler: delete_file");
let file = file_controller.delete_file(file_id).await?;
Ok(Json(file))
}
async fn list_files(
State(file_controller): State<FileController>,
// can add a filters param here
) -> Result<Json<Vec<File>>, LoftError> {
info!("handler: list_files");
let files = file_controller.list_files().await?;
Ok(Json(files))
}
#[cfg(test)]
mod tests {
use axum_test::TestServer;
use serde_json::json;
use crate::{model::FileController, web::routes_file::routes_file};
async fn test_server() -> TestServer {
let fc = FileController::new().await.unwrap();
TestServer::new(routes_file(fc))
}
#[tokio::test]
async fn test_list_files_empty() {
let server = test_server().await;
server
.get("/files")
.await
.assert_status_ok()
.assert_json(&json!([]));
}
#[tokio::test]
async fn test_upload_and_list_files() {
let server = test_server().await;
let res = server
.post("/files")
.json(&json!({"name": "a.txt", "file_type": "text"}))
.await;
res.assert_status_ok();
let file = res.json::<serde_json::Value>();
assert_eq!(file["name"], "a.txt");
assert_eq!(file["id"], 0);
let list = server.get("/files").await.json::<serde_json::Value>();
assert_eq!(list.as_array().unwrap().len(), 1);
}
#[tokio::test]
async fn test_download_file() {
let server = test_server().await;
server
.post("/files")
.json(&json!({"name": "b.txt", "file_type": "text"}))
.await;
let res = server.get("/files/0").await;
res.assert_status_ok();
assert_eq!(res.json::<serde_json::Value>()["name"], "b.txt");
}
#[tokio::test]
async fn test_download_file_not_found() {
let server = test_server().await;
server.get("/files/99").await.assert_status_not_found();
}
#[tokio::test]
async fn test_delete_file() {
let server = test_server().await;
server
.post("/files")
.json(&json!({"name": "c.txt", "file_type": "text"}))
.await;
server.delete("/files/0").await.assert_status_ok();
server.get("/files/0").await.assert_status_not_found();
}
#[tokio::test]
async fn test_delete_file_not_found() {
let server = test_server().await;
server.delete("/files/99").await.assert_status_not_found();
}
}