1923ff2a6f
The browser upload flow was built around one selected file and one global upload state. That made the existing chunk pool useful for a single file, but users could not start several selected files at the same time. Refactor the browser state into per-file upload items. Each selected file now has its own upload record, completed-chunk set, abort controller, retry state, progress row, and saved IndexedDB resume record. The picker accepts multiple files, `Start all` and `Resume all` use a bounded file-level pool, and each file keeps the existing bounded chunk pool. This keeps parallel uploads useful without letting one large selection create unbounded request fan-out. Keep the server API unchanged. Each file still receives a separate server upload id, and server-side progress remains authoritative before any missing chunks are scheduled. Terminal conflicts still stop the affected file without overwriting completed data. Update the user-facing markup, styles, project docs, and test checklist for the multi-file scheduler. Add a server regression test that interleaves two uploads and verifies the completed files contain exactly their own bytes. Test Plan: - just check - git diff --check
2.7 KiB
2.7 KiB
Test Scenarios
Keep this file as the reusable verification checklist while implementing
PLAN.md.
Automated
just check- Runs formatting, all Rust tests, and clippy.
- Current coverage:
GET /serves the static browser page.GET /healthzreportsok.POST /api/uploadscreatesmeta.json, a temp upload file, and a completion-marker directory.POST /api/uploadsrejects an empty file name.PUT /api/uploads/:id/chunks/:indexwrites validated chunks into the temp upload file and records completion markers.PUT /api/uploads/:id/chunks/:indexrejects wrong-size chunks.PUT /api/uploads/:id/chunks/:indexrejects out-of-range indexes.PUT /api/uploads/:id/chunks/:indexaccepts duplicate chunks.GET /api/uploads/:idreports completed chunks from disk markers.POST /api/uploads/:id/completerenames the verified temp upload file and removes staging data.- Parallel upload requests for separate files complete without crossing bytes between temp upload files.
POST /api/uploads/:id/completerejects incomplete uploads.POST /api/uploads/:id/completerejects tampered temp upload files.static/app.jspassesnode --check.
just nginx-smoke- Runs upl behind nginx in Docker.
- Uploads a 17 MiB file through nginx.
- Restarts the Rust backend mid-upload, resumes through nginx, completes, and compares SHA-256 hashes.
- Serves the completed file through nginx's final-upload alias.
Manual
These scenarios come from PLAN.md and remain useful for real browser and
deployment retests.
- Upload a small file in one pass.
- Upload a file larger than one chunk.
- Select multiple files and confirm several upload rows advance at the same time.
- Kill the browser tab mid-upload and resume.
- Restart the Rust server mid-upload and resume.
- Interrupt the network and resume.
- Pause from the browser controls and resume.
- Reload the page and resume from the pending upload list.
- In a browser with the File System Access API, resume without reselecting the file after granting read permission.
- In a browser without the File System Access API, resume after reselecting the same file.
- Retry a duplicate chunk and confirm it is accepted idempotently.
- Attempt an invalid chunk index and confirm it is rejected.
- Attempt a wrong-size non-final chunk and confirm it is rejected.
- Install
deploy/nginx/upl.conf.exampleon the deployment host, add the real TLS certificate and access-control settings, and repeat the resume scenarios through the public nginx URL. - Complete an upload and compare the final file with the source file:
sha256sum source-file data/complete/uploaded-file