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:
@@ -14,10 +14,12 @@ Keep this file as the reusable verification checklist while implementing
|
|||||||
- `POST /api/uploads` rejects an empty file name.
|
- `POST /api/uploads` rejects an empty file name.
|
||||||
- `PUT /api/uploads/:id/chunks/:index` stores validated chunk files.
|
- `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 wrong-size chunks.
|
||||||
|
- `PUT /api/uploads/:id/chunks/:index` rejects out-of-range indexes.
|
||||||
- `PUT /api/uploads/:id/chunks/:index` accepts duplicate chunks.
|
- `PUT /api/uploads/:id/chunks/:index` accepts duplicate chunks.
|
||||||
- `GET /api/uploads/:id` reports completed chunks from disk.
|
- `GET /api/uploads/:id` reports completed chunks from disk.
|
||||||
- `POST /api/uploads/:id/complete` assembles verified chunks.
|
- `POST /api/uploads/:id/complete` assembles verified chunks.
|
||||||
- `POST /api/uploads/:id/complete` rejects incomplete uploads.
|
- `POST /api/uploads/:id/complete` rejects incomplete uploads.
|
||||||
|
- `POST /api/uploads/:id/complete` rejects corrupt chunk files.
|
||||||
- `static/app.js` passes `node --check`.
|
- `static/app.js` passes `node --check`.
|
||||||
- `just nginx-smoke`
|
- `just nginx-smoke`
|
||||||
- Runs upl behind nginx in Docker.
|
- Runs upl behind nginx in Docker.
|
||||||
|
|||||||
@@ -68,6 +68,21 @@ async fn rejects_wrong_size_non_final_chunk() -> Result<(), Box<dyn std::error::
|
|||||||
Ok(())
|
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]
|
#[tokio::test]
|
||||||
async fn accepts_duplicate_chunk_when_existing_length_matches()
|
async fn accepts_duplicate_chunk_when_existing_length_matches()
|
||||||
-> Result<(), Box<dyn std::error::Error>> {
|
-> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
|||||||
@@ -97,6 +97,39 @@ async fn rejects_incomplete_upload() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
Ok(())
|
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(
|
async fn create_upload(
|
||||||
app: &axum::Router,
|
app: &axum::Router,
|
||||||
name: &str,
|
name: &str,
|
||||||
|
|||||||
Reference in New Issue
Block a user