test: cover chunk validation edge cases

Add focused regression coverage for validation rules called out in PLAN.md.
Chunk upload tests now prove that an otherwise valid upload rejects an
out-of-range chunk index through the HTTP API. Completion tests now prove that a
manually corrupted chunk file is not assembled into a final file.

Update TESTS.md so the reusable checklist reflects these automated proofs.

Test Plan:
- just check
- just nginx-smoke

Refs: PLAN.md validation checklist
This commit is contained in:
2026-05-30 17:20:19 +02:00
parent 858f4d949c
commit aeec7a0345
3 changed files with 50 additions and 0 deletions
+2
View File
@@ -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.
+15
View File
@@ -68,6 +68,21 @@ async fn rejects_wrong_size_non_final_chunk() -> Result<(), Box<dyn std::error::
Ok(())
}
#[tokio::test]
async fn rejects_out_of_range_chunk_index() -> Result<(), Box<dyn std::error::Error>> {
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<dyn std::error::Error>> {
+33
View File
@@ -97,6 +97,39 @@ async fn rejects_incomplete_upload() -> Result<(), Box<dyn std::error::Error>> {
Ok(())
}
#[tokio::test]
async fn rejects_corrupt_chunk_file() -> Result<(), Box<dyn std::error::Error>> {
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,