From 21d65af4703ed18b0484e7b1482d1a14a2618afa Mon Sep 17 00:00:00 2001 From: stefiosif Date: Sun, 7 Jun 2026 20:20:53 +0300 Subject: [PATCH] test(backend): cover stream_part range requests --- backend/src/model.rs | 49 +++++++++++++++++++++++-- backend/src/web/routes_file.rs | 67 +++++++++++++++++++++++++++++++++- 2 files changed, 111 insertions(+), 5 deletions(-) diff --git a/backend/src/model.rs b/backend/src/model.rs index 597f49c..69fd35e 100644 --- a/backend/src/model.rs +++ b/backend/src/model.rs @@ -431,12 +431,14 @@ mod tests { .await .unwrap(); - let _downloaded = file_repository + let chunks: Vec<_> = file_repository .download_file(file_record.id, user.id) .await - .unwrap(); - - //TODO: add assert + .unwrap() + .collect() + .await; + let downloaded: Vec = chunks.into_iter().flat_map(|c| c.unwrap()).collect(); + assert_eq!(downloaded, vec![0u8; 10]); truncate_file_records(&file_repository.pool).await; truncate_users(&user_repository.pool).await; @@ -454,6 +456,45 @@ mod tests { )); } + #[tokio::test] + #[serial_test::serial] + async fn test_stream_part() { + let user_repository = user_repository().await.unwrap(); + let user = user_repository + .create_user("username", "password_hash") + .await + .unwrap(); + let file_repository = file_repository().await.unwrap(); + truncate_file_records(&file_repository.pool).await; + + let file_name = "a.png"; + let file_storage_key = format!("{}-{}", file_name, uuid::Uuid::new_v4()); + let file_size = file_repository + .upload_file( + stream::iter(vec![Ok(Bytes::from((0u8..10).collect::>()))]), + &file_storage_key, + ) + .await + .unwrap(); + + let file_record = file_repository + .create_file_record(user.id, "a.jpg", file_size, &file_storage_key) + .await + .unwrap(); + + let chunks: Vec<_> = file_repository + .stream_part(file_record.id, user.id, 2, 6) + .await + .unwrap() + .collect() + .await; + let partial: Vec = chunks.into_iter().flat_map(|c| c.unwrap()).collect(); + assert_eq!(partial, vec![2u8, 3, 4, 5]); + + truncate_file_records(&file_repository.pool).await; + truncate_users(&user_repository.pool).await; + } + #[tokio::test] #[serial_test::serial] async fn test_delete() { diff --git a/backend/src/web/routes_file.rs b/backend/src/web/routes_file.rs index 31f2de4..3625dfe 100644 --- a/backend/src/web/routes_file.rs +++ b/backend/src/web/routes_file.rs @@ -146,7 +146,7 @@ async fn list_files( #[cfg(test)] mod tests { - use axum::{Router, middleware}; + use axum::{Router, http::header, middleware}; use axum_test::{ TestServer, multipart::{MultipartForm, Part}, @@ -334,6 +334,71 @@ mod tests { truncate(&file_repository.pool).await; } + #[tokio::test] + #[serial_test::serial] + async fn test_stream_part_range() { + 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, format!("auth-token={token}")) + .multipart(MultipartForm::new().add_part( + "file", + Part::bytes(b"0123456789".to_vec()).file_name("a.txt"), + )) + .await; + let id = post_res.json::()["id"].as_i64().unwrap(); + + let res = server + .get(&format!("/api/files/{id}/stream_part")) + .add_header(axum::http::header::COOKIE, format!("auth-token={token}")) + .add_header(axum::http::header::RANGE, "bytes=2-5") + .await; + + res.assert_status(axum::http::StatusCode::PARTIAL_CONTENT); + assert_eq!(res.as_bytes(), b"2345".as_ref()); + assert_eq!(res.header(header::CONTENT_RANGE), "bytes 2-5/10"); + assert_eq!(res.header(header::CONTENT_LENGTH), "4"); + assert_eq!(res.header(header::ACCEPT_RANGES), "bytes"); + truncate(&file_repository.pool).await; + } + + #[tokio::test] + #[serial_test::serial] + async fn test_stream_part_open_ended() { + 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, format!("auth-token={token}")) + .multipart(MultipartForm::new().add_part( + "file", + Part::bytes(b"0123456789".to_vec()).file_name("a.txt"), + )) + .await; + let id = post_res.json::()["id"].as_i64().unwrap(); + + let res = server + .get(&format!("/api/files/{id}/stream_part")) + .add_header(axum::http::header::COOKIE, format!("auth-token={token}")) + .add_header(axum::http::header::RANGE, "bytes=0-") + .await; + + res.assert_status(axum::http::StatusCode::PARTIAL_CONTENT); + assert_eq!(res.as_bytes(), b"0123456789".as_ref()); + assert_eq!(res.header(header::CONTENT_RANGE), "bytes 0-9/10"); + assert_eq!(res.header(header::CONTENT_LENGTH), "10"); + truncate(&file_repository.pool).await; + } + #[tokio::test] #[serial_test::serial] async fn test_download_file_not_found() {