test(gemini): verify cancel goroutine exits when Download returns

This commit is contained in:
2026-06-30 20:21:00 +02:00
parent 3752b3d3eb
commit 185e6ec20a
2 changed files with 127 additions and 0 deletions
+48
View File
@@ -77,6 +77,34 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
output when combined with `--json`. Credentials embedded in the stored
URL are masked before display (the password placeholder is `xxxxx`).
### Fixed
- **Gopher: `ctx.Done()` cancellation goroutine leak** — the goroutine
spawned in `Download` to close the socket on `ctx.Done()` previously
waited indefinitely on the parent context, leaking every time the
context outlived the function. Now bounded by a `defer`-closed
`done` channel so the goroutine exits on every return path.
- **Gemini: `ctx.Done()` cancellation goroutine leak** — same bug,
same fix in `downloadWithRedirects`.
- **Gopher: partial state lost on cancel-then-close** — when the server
closed the connection after `ctx` was cancelled, `downloadText`
treated the resulting EOF as a successful end-of-stream and never
persisted `.goget.meta`. Mirrors the equivalent Gemini behavior
(already present from the Gemini resume feature): if `ctx.Err() != nil`,
save partial state and return the context error.
- **Gopher: `--resume` ignored when caller supplies `Writer`** — when
`--resume` was set together with a caller-supplied `Writer`, the
partial-state sidecar would track bytes that landed in the `Writer`
rather than `Output`. `--resume` is now silently disabled in that
case (with a `--verbose` warning), keeping metadata consistent with
the actual on-disk state.
- **Gemini: `--resume` ignored when caller supplies `Writer`** — same
fix applied to `downloadWithRedirects`.
### Testing
- `cmd/goget/man_page_test.go` — new test file. Asserts the embedded
@@ -85,6 +113,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
`defineFlags` (so adding a new flag without documenting it now breaks
the build). When `groff` is installed, also runs `groff -man -ww
-Kutf-8 -z` over the embedded source and fails on any warning.
- Resume tests deterministic across the protocol stack
(`TestGeminiResume{SaveMetadata,Download}` and
`TestGopherResume{SaveMetadata,Download}`) — the four tests previously
relied on a fixed `time.Sleep` before calling `cancel()`, making them
flaky on slow CI schedulers. They now wait for a `ProgressCallback`
signal, which fires synchronously after the first `writer.Write`
lands on disk.
- Goroutine leak regression suite
(`TestGeminiCancelGoroutineExits`, `TestGopherCancelGoroutineExits`)
— runs 50 downloads back-to-back, settles the runtime via
`runtime.GC()`, and asserts `runtime.NumGoroutine()` returns to
baseline + 1 within a 2-second window.
- `TestGopherResumeInformation` — new regression test covering the
`typeInformation` text-mode path through resume, which differs from
`typeHTML`/`typeTextFile` because lines are passed through verbatim
(no metadata strip).
- `TestGopherResumeStaleMetadata` now asserts that an orphan
`.goget.meta` carried over from a different URL is deleted once a
successful fresh download completes, preventing it from being
picked up by a future `--resume` run.
## [1.0.0] — 2026-06-26