diff --git a/TESTS.md b/TESTS.md index 6bd34a3..258a62c 100644 --- a/TESTS.md +++ b/TESTS.md @@ -14,10 +14,12 @@ Keep this file as the reusable verification checklist while implementing - `POST /api/uploads` rejects an empty file name. - `PUT /api/uploads/:id/chunks/:index` stores validated chunk files. - `PUT /api/uploads/:id/chunks/:index` rejects wrong-size chunks. + - `PUT /api/uploads/:id/chunks/:index` rejects out-of-range indexes. - `PUT /api/uploads/:id/chunks/:index` accepts duplicate chunks. - `GET /api/uploads/:id` reports completed chunks from disk. - `POST /api/uploads/:id/complete` assembles verified chunks. - `POST /api/uploads/:id/complete` rejects incomplete uploads. + - `POST /api/uploads/:id/complete` rejects corrupt chunk files. - `static/app.js` passes `node --check`. - `just nginx-smoke` - Runs upl behind nginx in Docker. diff --git a/tests/chunk_upload.rs b/tests/chunk_upload.rs index c641386..594d134 100644 --- a/tests/chunk_upload.rs +++ b/tests/chunk_upload.rs @@ -68,6 +68,21 @@ async fn rejects_wrong_size_non_final_chunk() -> Result<(), Box Result<(), Box> { + let temp_dir = TempDir::new()?; + let app = test_app(temp_dir.path()); + let upload = create_upload(&app, temp_dir.path(), 4).await?; + + let response = app + .oneshot(chunk_request(&upload.upload_id, 1, b"data".to_vec())?) + .await?; + + assert_eq!(response.status(), StatusCode::BAD_REQUEST); + + Ok(()) +} + #[tokio::test] async fn accepts_duplicate_chunk_when_existing_length_matches() -> Result<(), Box> { diff --git a/tests/completion.rs b/tests/completion.rs index 0380bdb..e917071 100644 --- a/tests/completion.rs +++ b/tests/completion.rs @@ -97,6 +97,39 @@ async fn rejects_incomplete_upload() -> Result<(), Box> { Ok(()) } +#[tokio::test] +async fn rejects_corrupt_chunk_file() -> Result<(), Box> { + let temp_dir = TempDir::new()?; + let app = test_app(temp_dir.path()); + let upload = create_upload(&app, "corrupt.bin", 4).await?; + + let chunk_path = temp_dir + .path() + .join("staging") + .join(&upload.upload_id) + .join("chunks") + .join("000000.part"); + tokio::fs::write(chunk_path, b"bad").await?; + + let response = app + .oneshot(empty_request( + Method::POST, + &format!("/api/uploads/{}/complete", upload.upload_id), + )?) + .await?; + + assert_eq!(response.status(), StatusCode::CONFLICT); + assert!( + !temp_dir + .path() + .join("complete") + .join("corrupt.bin") + .exists() + ); + + Ok(()) +} + async fn create_upload( app: &axum::Router, name: &str,