use std::collections::HashMap; use std::sync::Arc; use axum::body::Body; use axum::http::{Request, StatusCode}; use tower::util::ServiceExt; use doctate_server::config::{Config, User}; fn test_config() -> Arc { let data_path = std::env::temp_dir().join(format!( "doctate-web-test-{}-{}", std::process::id(), uuid::Uuid::new_v4() )); Arc::new(Config { data_path, users: vec![User { slug: "dr_test".into(), api_key: "test-key".into(), web_password: "unused".into(), role: "doctor".into(), whisper: Default::default(), }], api_keys: HashMap::from([("test-key".into(), "dr_test".into())]), ..Config::test_default() }) } #[tokio::test] async fn web_audio_serves_file() { let config = test_config(); let data_path = config.data_path.clone(); let case_id = "770e8400-e29b-41d4-a716-446655440000"; let case_dir = data_path.join("dr_test").join(case_id); std::fs::create_dir_all(&case_dir).unwrap(); let audio_bytes = b"fake audio content for test"; let filename = "2026-04-13T10-30-00Z.m4a"; std::fs::write(case_dir.join(filename), audio_bytes).unwrap(); let app = doctate_server::create_router(config); let response = app .oneshot( Request::builder() .uri(format!("/web/audio/dr_test/{case_id}/{filename}")) .body(Body::empty()) .unwrap(), ) .await .unwrap(); assert_eq!(response.status(), StatusCode::OK); assert_eq!( response .headers() .get("content-type") .unwrap() .to_str() .unwrap(), "audio/mp4" ); let bytes = axum::body::to_bytes(response.into_body(), usize::MAX) .await .unwrap(); assert_eq!(&bytes[..], audio_bytes); let _ = std::fs::remove_dir_all(&data_path); } #[tokio::test] async fn web_audio_path_traversal_rejected() { let config = test_config(); let app = doctate_server::create_router(config); let response = app .oneshot( Request::builder() .uri("/web/audio/..%2Fetc/550e8400-e29b-41d4-a716-446655440000/foo.m4a") .body(Body::empty()) .unwrap(), ) .await .unwrap(); assert_eq!(response.status(), StatusCode::BAD_REQUEST); } #[tokio::test] async fn web_audio_invalid_case_id_rejected() { let config = test_config(); let app = doctate_server::create_router(config); let response = app .oneshot( Request::builder() .uri("/web/audio/dr_test/not-a-uuid/foo.m4a") .body(Body::empty()) .unwrap(), ) .await .unwrap(); assert_eq!(response.status(), StatusCode::BAD_REQUEST); } #[tokio::test] async fn web_audio_nonexistent_file_returns_404() { let config = test_config(); let app = doctate_server::create_router(config); let response = app .oneshot( Request::builder() .uri("/web/audio/dr_test/550e8400-e29b-41d4-a716-446655440000/missing.m4a") .body(Body::empty()) .unwrap(), ) .await .unwrap(); assert_eq!(response.status(), StatusCode::NOT_FOUND); }