Files
doctate/server/tests/web_test.rs
T
Brummel af3377a6bc refactor(tests): migrate remaining batches to tests/common/
Finishes the lift of shared helpers into `tests/common/`. Covered:
- health, web, oneliners_api, upload (31 tests; upload exercises the
  new `multipart_upload_body` helper driven by doctate_common field
  constants)
- sse_integration, sse_cleanup, transcribe, oneliner_heal_decoupled,
  silent_case_empty, failed_only_case_empty_oneliner,
  transient_failure_retries (24 tests)

Also applied cargo fmt across the test tree and fixed one clippy
needless_borrows_for_generic_args warning in analyze_test.

All 387 tests pass; 3 ignored (as before).

Side effect: health_test previously used a hardcoded `/tmp/doctate-test`
data path, which parallel `cargo test` runs could collide on. The
migration replaces it with the common unique-tmpdir pattern, removing
a latent flake.
2026-04-22 10:51:33 +02:00

194 lines
5.7 KiB
Rust

mod common;
use axum::body::Body;
use axum::http::{Request, StatusCode};
use tower::util::ServiceExt;
use common::{TestConfig, header_opt, test_user};
fn test_config() -> std::sync::Arc<doctate_server::config::Config> {
TestConfig::new()
.with_label("web")
.with_user(test_user("dr_test"))
.build()
}
#[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 = common::seed_case(&data_path, "dr_test", case_id);
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!(header_opt(&response, "content-type"), Some("audio/mp4"));
let bytes = common::body_bytes(response).await;
assert_eq!(&bytes[..], audio_bytes);
}
#[tokio::test]
async fn web_audio_path_traversal_rejected() {
let app = doctate_server::create_router(test_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 app = doctate_server::create_router(test_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_serves_range_as_206() {
let config = test_config();
let data_path = config.data_path.clone();
let case_id = "770e8400-e29b-41d4-a716-446655440001";
let case_dir = common::seed_case(&data_path, "dr_test", case_id);
let audio_bytes: Vec<u8> = (0u8..=200u8).collect();
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}"))
.header("Range", "bytes=10-19")
.body(Body::empty())
.unwrap(),
)
.await
.unwrap();
assert_eq!(response.status(), StatusCode::PARTIAL_CONTENT);
assert_eq!(
header_opt(&response, "content-range"),
Some(format!("bytes 10-19/{}", audio_bytes.len()).as_str())
);
assert_eq!(header_opt(&response, "content-length"), Some("10"));
assert_eq!(header_opt(&response, "accept-ranges"), Some("bytes"));
let bytes = common::body_bytes(response).await;
assert_eq!(&bytes[..], &audio_bytes[10..20]);
}
#[tokio::test]
async fn web_audio_invalid_range_returns_416() {
let config = test_config();
let data_path = config.data_path.clone();
let case_id = "770e8400-e29b-41d4-a716-446655440002";
let case_dir = common::seed_case(&data_path, "dr_test", case_id);
let audio_bytes = b"short";
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}"))
.header("Range", "bytes=1000-2000")
.body(Body::empty())
.unwrap(),
)
.await
.unwrap();
assert_eq!(response.status(), StatusCode::RANGE_NOT_SATISFIABLE);
assert_eq!(
header_opt(&response, "content-range"),
Some(format!("bytes */{}", audio_bytes.len()).as_str())
);
}
#[tokio::test]
async fn web_audio_no_range_header_advertises_accept_ranges() {
let config = test_config();
let data_path = config.data_path.clone();
let case_id = "770e8400-e29b-41d4-a716-446655440003";
let case_dir = common::seed_case(&data_path, "dr_test", case_id);
let audio_bytes = b"full file content";
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!(header_opt(&response, "accept-ranges"), Some("bytes"));
assert_eq!(
header_opt(&response, "content-length"),
Some(audio_bytes.len().to_string().as_str())
);
}
#[tokio::test]
async fn web_audio_nonexistent_file_returns_404() {
let app = doctate_server::create_router(test_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);
}