test(download): add integration tests with HTTP test server #161

Merged
fuzzy merged 2 commits from feature/integration-tests into main 2026-07-14 09:27:48 +00:00
Owner

What

Add comprehensive integration tests (16 tests) using httptest.NewServer. Also fix two real bugs discovered during testing.

Why

No test coverage existed for the core download engine. Tests caught a data race and a concurrent write corruption bug.

Tests

  • SplitChunks, BuildRangeHeader, ProbeURL (range, no-range, Content-Disposition)
  • DownloadHTTP with range (3 workers), without range, resume, checksum (valid + mismatch), retry, retry exhausted, rate limiting, context cancellation
  • UniquePath, HumanBytes, ParseFTPURL, IsFTPURL, VerifyChecksum

Bugs Fixed

  1. writeAtSeeker embedded *os.Fileio.CopyN called os.File.ReadFrom (sendfile) which wrote at the shared file offset instead of via WriteAt, corrupting concurrent chunk writes. Fixed by storing *os.File as a named field instead of embedding.

  2. progress.Tracker data race — Multiple workers called bar.Set() concurrently while the render goroutine read bar.String(). Fixed by adding a sync.Mutex around bar access.

Testing

  • go test -race ./... — all pass, no races
  • go build ./..., go vet ./..., golangci-lint run — all clean

Breaking Changes

None — both bugs were latent and produced wrong output under concurrent downloads.

Closes #112, #108, #109, #110, #111

## What Add comprehensive integration tests (16 tests) using httptest.NewServer. Also fix two real bugs discovered during testing. ## Why No test coverage existed for the core download engine. Tests caught a data race and a concurrent write corruption bug. ## Tests - SplitChunks, BuildRangeHeader, ProbeURL (range, no-range, Content-Disposition) - DownloadHTTP with range (3 workers), without range, resume, checksum (valid + mismatch), retry, retry exhausted, rate limiting, context cancellation - UniquePath, HumanBytes, ParseFTPURL, IsFTPURL, VerifyChecksum ## Bugs Fixed 1. **writeAtSeeker embedded `*os.File`** — `io.CopyN` called `os.File.ReadFrom` (sendfile) which wrote at the shared file offset instead of via `WriteAt`, corrupting concurrent chunk writes. Fixed by storing `*os.File` as a named field instead of embedding. 2. **progress.Tracker data race** — Multiple workers called `bar.Set()` concurrently while the render goroutine read `bar.String()`. Fixed by adding a `sync.Mutex` around bar access. ## Testing - [x] `go test -race ./...` — all pass, no races - [x] `go build ./...`, `go vet ./...`, `golangci-lint run` — all clean ## Breaking Changes None — both bugs were latent and produced wrong output under concurrent downloads. Closes #112, #108, #109, #110, #111
Also fix two real bugs discovered during testing:
- writeAtSeeker embedded *os.File causing io.CopyN to bypass Write()
  via ReadFrom, corrupting concurrent chunk writes
- progress.Tracker data race on bar.Set from multiple workers

16 tests total: SplitChunks, BuildRangeHeader, ProbeURL (range,
no-range, content-disposition), DownloadHTTP (range, no-range, resume,
checksum, retry, retry exhausted, rate-limit, context-cancel),
UniquePath, HumanBytes, ParseFTPURL, IsFTPURL, VerifyChecksum.

Closes #112, #108, #109, #110, #111
docs(roadmap): mark integration tests complete
Some checks failed
Test and Release / lint (pull_request) Has been cancelled
Test and Release / test (pull_request) Has been cancelled
c2d65d6025
Closes #112, #108, #109, #110, #111
fuzzy force-pushed feature/integration-tests from c2d65d6025
Some checks failed
Test and Release / lint (pull_request) Has been cancelled
Test and Release / test (pull_request) Has been cancelled
to 56f57056e9
All checks were successful
Test and Release / test (pull_request) Successful in 2m12s
Test and Release / lint (pull_request) Successful in 9m55s
2026-07-14 09:17:50 +00:00
Compare
fuzzy scheduled this pull request to auto merge when all checks succeed 2026-07-14 09:17:57 +00:00
the.auditor left a comment

Review: APPROVED

Verification

  • go test -race ./internal/downloader/... 4.3s, all pass
  • go test -race ./internal/progress/... 1.0s, all pass
  • go vet ./... passes

Changes

  • internal/downloader/download_test.go (new, 534 lines) — 16 integration tests using httptest.NewServer
  • internal/downloader/download.gowriteAtSeeker no longer embeds *os.File (named field file + explicit WriteAt), fixing concurrent chunk corruption
  • internal/progress/progress.gosync.Mutex around bar.Set()/bar.String() calls, fixing data race
  • ROADMAP.md — Marks benchmarking items as remaining (not closed by this PR)

Bugs fixed

  1. Concurrent download corruptionio.CopyN called os.File.ReadFrom (sendfile) via promoted method, bypassing WriteAt. Fixed by storing *os.File as named field.
  2. Progress bar data race — Concurrent bar.Set() from workers + bar.String() from render goroutine. Fixed with sync.Mutex.

Test coverage (16 tests)

SplitChunks, BuildRangeHeader, ProbeURL (range + no-range + Content-Disposition), Download (parallel + single-stream + resume + checksum + retry + retry-exhausted + rate-limit + context-cancel), UniquePath, HumanBytes, ParseFTPURL, IsFTPURL, VerifyChecksum

Closes #108, #109, #110, #111, #112 on merge.

## Review: APPROVED ### Verification - `go test -race ./internal/downloader/...` — ✅ 4.3s, all pass - `go test -race ./internal/progress/...` — ✅ 1.0s, all pass - `go vet ./...` — ✅ passes ### Changes - `internal/downloader/download_test.go` (new, 534 lines) — 16 integration tests using `httptest.NewServer` - `internal/downloader/download.go` — `writeAtSeeker` no longer embeds `*os.File` (named field `file` + explicit `WriteAt`), fixing concurrent chunk corruption - `internal/progress/progress.go` — `sync.Mutex` around `bar.Set()`/`bar.String()` calls, fixing data race - ROADMAP.md — Marks benchmarking items as remaining (not closed by this PR) ### Bugs fixed 1. **Concurrent download corruption** — `io.CopyN` called `os.File.ReadFrom` (sendfile) via promoted method, bypassing `WriteAt`. Fixed by storing `*os.File` as named field. 2. **Progress bar data race** — Concurrent `bar.Set()` from workers + `bar.String()` from render goroutine. Fixed with `sync.Mutex`. ### Test coverage (16 tests) SplitChunks, BuildRangeHeader, ProbeURL (range + no-range + Content-Disposition), Download (parallel + single-stream + resume + checksum + retry + retry-exhausted + rate-limit + context-cancel), UniquePath, HumanBytes, ParseFTPURL, IsFTPURL, VerifyChecksum Closes #108, #109, #110, #111, #112 on merge.
fuzzy merged commit 56f57056e9 into main 2026-07-14 09:27:48 +00:00
fuzzy deleted branch feature/integration-tests 2026-07-14 09:27:48 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
3 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
thwap/pget!161
No description provided.