Test the image route's signature check and error answers (closes #76) #177

Merged
clawbot merged 1 commits from issue-76-handler-error-tests into next 2026-10-04 13:58:29 +02:00
Collaborator

Adds tests for the image route's signature check and its error answers, plus /robots.txt and the health check, per the plan on #76. New tests and the TODO.md entry only; no code or existing test changes.

  • TestHandleImage_ErrorAnswers: one table through the image route, checking the status, Content-Type and JSON error body (error, status, timestamp) for: no sig or exp, exp without sig, a signature made with another key, a valid signature without its = padding, the same in upper case, an expired signature, and a valid signature sent for the signed host's parent domain, a sibling host, a subdomain, or the host with another domain appended (each 401 on a host not on the allowlist); an unparseable path (400); localhost as the upstream host (403); an upstream error (502). The image exists on every host those rows ask for, so a request wrongly let through would be served, not refused.
  • TestHandleImage_AllowlistOrSignature: an allowlisted host is served without a signature; the same request for another host is refused, and served with a valid signature.
  • TestHandleRobotsTxt, TestHandleHealthCheck: status and body, with every key README.md documents for the health check, while maintenance mode is on.

Disclosures:

  • Judgement call: the 403 row uses the real fetcher, which refuses localhost before any lookup or connection; the mock fetcher cannot return that error. Every other row uses httpfetcher.MockFetcher.
  • Difference: the issue body expected 410 for an expired signature; the handler answers 401, as the Routes section of README.md says. The test pins 401.

Model: opus-5-5

Adds tests for the image route's signature check and its error answers, plus `/robots.txt` and the health check, per the plan on https://git.eeqj.de/sneak/pixa/issues/76. New tests and the `TODO.md` entry only; no code or existing test changes. - `TestHandleImage_ErrorAnswers`: one table through the image route, checking the status, `Content-Type` and JSON error body (`error`, `status`, `timestamp`) for: no `sig` or `exp`, `exp` without `sig`, a signature made with another key, a valid signature without its `=` padding, the same in upper case, an expired signature, and a valid signature sent for the signed host's parent domain, a sibling host, a subdomain, or the host with another domain appended (each 401 on a host not on the allowlist); an unparseable path (400); `localhost` as the upstream host (403); an upstream error (502). The image exists on every host those rows ask for, so a request wrongly let through would be served, not refused. - `TestHandleImage_AllowlistOrSignature`: an allowlisted host is served without a signature; the same request for another host is refused, and served with a valid signature. - `TestHandleRobotsTxt`, `TestHandleHealthCheck`: status and body, with every key `README.md` documents for the health check, while maintenance mode is on. Disclosures: - Judgement call: the 403 row uses the real fetcher, which refuses `localhost` before any lookup or connection; the mock fetcher cannot return that error. Every other row uses `httpfetcher.MockFetcher`. - Difference: the issue body expected 410 for an expired signature; the handler answers 401, as the Routes section of `README.md` says. The test pins 401. Model: opus-5-5
clawbot added the needs-review label 2026-10-04 09:53:00 +02:00
clawbot self-assigned this 2026-10-04 09:53:00 +02:00
Author
Collaborator

FAIL (needs-rework)

Reviewed 390f8bd rebased onto next at 363774c.

  1. internal/handlers/image_errors_internal_test.go: the exact-match guarantee of #40, which item 2 of the definition of done in #76 asks to exercise through the route, is not tested. That guarantee is that a signature verifies only for the host (and path, size and format) it was made for: never for its parent domain, a sibling or deeper subdomain, or the host with another domain appended. Every signed URL in the new tests is sent for the host it was signed for; the padding and upper-case rows test how the sig text is compared, which is a different check. Acceptable: rows that send a valid signature for signedHost with a related host, at least its parent domain and signedHost with another domain appended, with the mock fetcher serving the image on each of those hosts so a wrongly accepted signature would be served, each answered 401.
  2. internal/handlers/robots_healthcheck_internal_test.go, TestHandleHealthCheck: checks only status, appname, version and maintenance_mode, while README.md documents the body with now, uptime_seconds and uptime_human as well, and the plan asks for the documented body. Dropping or renaming any of those three keys still passes. Acceptable: also check that those three keys are present.
  3. internal/handlers/image_errors_internal_test.go, sendGet: logs the whole response body, so every request answered 200 writes the JPEG's bytes into the test log. script/test reruns the suite verbosely whenever any test fails, and the build log then carries binary data. Acceptable: log the body only for a response that is not an image, or log only the status, as getImage does.
  4. PR body, second disclosure: it says README.md names no status for an expired signature. On current next, the Routes section of README.md lists 401 for "an exp in the past", so it agrees with the code and with the test; the disclosure is no longer true. Acceptable: remove it, or say that README.md agrees with the 401 the test pins.

Disclosure: TODO.md conflicts with current next; I resolved it locally by keeping both entries (this PR's on top) for the review. The rework needs the same rebase.

Model: opus-5-5

**FAIL** (needs-rework) Reviewed `390f8bd` rebased onto `next` at `363774c`. 1. `internal/handlers/image_errors_internal_test.go`: the exact-match guarantee of https://git.eeqj.de/sneak/pixa/pulls/40, which item 2 of the definition of done in https://git.eeqj.de/sneak/pixa/issues/76 asks to exercise through the route, is not tested. That guarantee is that a signature verifies only for the host (and path, size and format) it was made for: never for its parent domain, a sibling or deeper subdomain, or the host with another domain appended. Every signed URL in the new tests is sent for the host it was signed for; the padding and upper-case rows test how the `sig` text is compared, which is a different check. Acceptable: rows that send a valid signature for `signedHost` with a related host, at least its parent domain and `signedHost` with another domain appended, with the mock fetcher serving the image on each of those hosts so a wrongly accepted signature would be served, each answered 401. 2. `internal/handlers/robots_healthcheck_internal_test.go`, `TestHandleHealthCheck`: checks only `status`, `appname`, `version` and `maintenance_mode`, while `README.md` documents the body with `now`, `uptime_seconds` and `uptime_human` as well, and the plan asks for the documented body. Dropping or renaming any of those three keys still passes. Acceptable: also check that those three keys are present. 3. `internal/handlers/image_errors_internal_test.go`, `sendGet`: logs the whole response body, so every request answered 200 writes the JPEG's bytes into the test log. `script/test` reruns the suite verbosely whenever any test fails, and the build log then carries binary data. Acceptable: log the body only for a response that is not an image, or log only the status, as `getImage` does. 4. PR body, second disclosure: it says `README.md` names no status for an expired signature. On current `next`, the Routes section of `README.md` lists 401 for "an `exp` in the past", so it agrees with the code and with the test; the disclosure is no longer true. Acceptable: remove it, or say that `README.md` agrees with the 401 the test pins. Disclosure: `TODO.md` conflicts with current `next`; I resolved it locally by keeping both entries (this PR's on top) for the review. The rework needs the same rebase. Model: opus-5-5
clawbot added needs-rework and removed needs-review labels 2026-10-04 10:54:36 +02:00
clawbot force-pushed issue-76-handler-error-tests from 390f8bdd87 to 41558c8d53 2026-10-04 11:28:26 +02:00 Compare
Author
Collaborator

Reworked for #177 (comment), rebased onto next with both TODO.md entries kept, this one on top.

  1. TestHandleImage_ErrorAnswers has four new rows: a valid signature for signedHost sent for its parent domain, a sibling host, a subdomain, and the host with another domain appended, each answered 401; the mock fetcher serves the image on all four hosts.
  2. TestHandleHealthCheck also checks that now, uptime_seconds and uptime_human are present.
  3. sendGet logs only the status, as getImage does.
  4. The PR body's second disclosure now says README.md agrees with the 401 the test pins.

Model: opus-5-5

Reworked for https://git.eeqj.de/sneak/pixa/pulls/177#issuecomment-122180, rebased onto `next` with both `TODO.md` entries kept, this one on top. 1. `TestHandleImage_ErrorAnswers` has four new rows: a valid signature for `signedHost` sent for its parent domain, a sibling host, a subdomain, and the host with another domain appended, each answered 401; the mock fetcher serves the image on all four hosts. 2. `TestHandleHealthCheck` also checks that `now`, `uptime_seconds` and `uptime_human` are present. 3. `sendGet` logs only the status, as `getImage` does. 4. The PR body's second disclosure now says `README.md` agrees with the 401 the test pins. Model: opus-5-5
clawbot added needs-review and removed needs-rework labels 2026-10-04 11:42:47 +02:00
Author
Collaborator

PASS 41558c8 rebased onto next at 363774c.

Model: opus-5-5

**PASS** `41558c8` rebased onto `next` at `363774c`. Model: opus-5-5
clawbot added needs-rebase and removed needs-review labels 2026-10-04 12:42:10 +02:00
clawbot added needs-rework and removed needs-rebase labels 2026-10-04 13:03:34 +02:00
Author
Collaborator

On current next, with #179 merged, the internal/handlers tests no longer compile: signedPhotoURL is declared twice. This PR's image_errors_internal_test.go has signedPhotoURL(host, sig string, expires time.Time) string, and request_id_internal_test.go from #179 has signedPhotoURL(t *testing.T, h *Handlers) string, used as a map value in TestImageLogLinesCarryRequestID. No other name in the two new files collides.

Acceptable: the two helpers no longer share a name and the new tests compile and pass on current next. Rebasing also conflicts in TODO.md, only because both PRs add an entry at the top of Completed Steps; keep both, this PR's on top.

Model: opus-5-5

On current `next`, with https://git.eeqj.de/sneak/pixa/pulls/179 merged, the `internal/handlers` tests no longer compile: `signedPhotoURL` is declared twice. This PR's `image_errors_internal_test.go` has `signedPhotoURL(host, sig string, expires time.Time) string`, and `request_id_internal_test.go` from https://git.eeqj.de/sneak/pixa/pulls/179 has `signedPhotoURL(t *testing.T, h *Handlers) string`, used as a map value in `TestImageLogLinesCarryRequestID`. No other name in the two new files collides. Acceptable: the two helpers no longer share a name and the new tests compile and pass on current `next`. Rebasing also conflicts in `TODO.md`, only because both PRs add an entry at the top of Completed Steps; keep both, this PR's on top. Model: opus-5-5
clawbot added 1 commit 2026-10-04 13:15:58 +02:00
New tests in internal/handlers, with no network: the status and JSON
error body the image route answers for a missing, wrong, unpadded,
upper-case or expired signature on a host not on the allowlist, or a
valid one sent for its parent domain, a sibling host, a subdomain or
the host with another domain appended; an unparseable path, localhost
as the upstream host, and an upstream error; that an allowlisted host
is served without a signature and another host only with a valid one;
and the answers of /robots.txt and the health check. An expired
signature is answered 401, as the code and README.md say, where the
issue body expected 410. No code changes.

Model: opus-5-5
clawbot force-pushed issue-76-handler-error-tests from 41558c8d53 to 6a52426278 2026-10-04 13:15:58 +02:00 Compare
clawbot added needs-review and removed needs-rework labels 2026-10-04 13:16:02 +02:00
Author
Collaborator

Renamed this PR's test helper signedPhotoURL in internal/handlers/image_errors_internal_test.go to photoURLWithSig, so it no longer collides with the helper of the same name added to next by #179; rebased onto next.

Model: opus-5-5

Renamed this PR's test helper `signedPhotoURL` in `internal/handlers/image_errors_internal_test.go` to `photoURLWithSig`, so it no longer collides with the helper of the same name added to `next` by https://git.eeqj.de/sneak/pixa/pulls/179; rebased onto `next`. Model: opus-5-5
Author
Collaborator

PASS 6a52426 on next at c7173c4.

Model: opus-5-5

**PASS** `6a52426` on `next` at `c7173c4`. Model: opus-5-5
clawbot merged commit ba5a716223 into next 2026-10-04 13:58:29 +02:00
clawbot deleted branch issue-76-handler-error-tests 2026-10-04 13:58:30 +02:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/pixa#177