use std::{ net::{Ipv4Addr, SocketAddr}, path::Path, }; use axum::{ body::Body, http::{Request, StatusCode, header}, }; use http_body_util::BodyExt; use serde_json::json; use tempfile::TempDir; use tower::ServiceExt; use upl::{ app::{AppConfig, build_router}, model::{CHUNK_SIZE, CreateUploadResponse, UploadMeta}, }; #[tokio::test] async fn creates_upload_metadata_on_disk() -> Result<(), Box> { let temp_dir = TempDir::new()?; let app = test_app(temp_dir.path()); let response = app .oneshot(json_request( "/api/uploads", &json!({ "name": "movie:mkv", "size": CHUNK_SIZE + 1, "last_modified": 1_760_000_000_000_i64 }), )?) .await?; assert_eq!(response.status(), StatusCode::OK); let response_body = response.into_body().collect().await?.to_bytes(); let response: CreateUploadResponse = serde_json::from_slice(&response_body)?; assert_eq!(response.chunk_size, CHUNK_SIZE); assert_eq!(response.total_chunks, 2); assert!(response.completed_chunks.is_empty()); let upload_dir = temp_dir.path().join("staging").join(&response.upload_id); let meta_path = upload_dir.join("meta.json"); assert!(upload_dir.join(".upload.tmp").is_file()); assert!(upload_dir.join("completed").is_dir()); assert!(temp_dir.path().join("complete").is_dir()); let meta: UploadMeta = serde_json::from_slice(&tokio::fs::read(meta_path).await?)?; assert_eq!(meta.id, response.upload_id); assert_eq!(meta.original_name, "movie:mkv"); assert_eq!(meta.safe_name, "movie_mkv"); assert_eq!(meta.total_chunks, 2); Ok(()) } #[tokio::test] async fn rejects_empty_upload_name() -> Result<(), Box> { let temp_dir = TempDir::new()?; let app = test_app(temp_dir.path()); let response = app .oneshot(json_request( "/api/uploads", &json!({ "name": " ", "size": 10, "last_modified": 1_760_000_000_000_i64 }), )?) .await?; assert_eq!(response.status(), StatusCode::BAD_REQUEST); Ok(()) } fn test_app(data_dir: &Path) -> axum::Router { build_router(&AppConfig::new( SocketAddr::from((Ipv4Addr::LOCALHOST, 0)), concat!(env!("CARGO_MANIFEST_DIR"), "/static"), data_dir, )) } fn json_request( uri: &str, body: &serde_json::Value, ) -> Result, Box> { Ok(Request::builder() .method("POST") .uri(uri) .header(header::CONTENT_TYPE, "application/json") .body(Body::from(serde_json::to_vec(&body)?))?) }