From dd2d256bc2b82bead50e782da6f21b869cd2bb44 Mon Sep 17 00:00:00 2001 From: sneak Date: Mon, 28 Sep 2026 17:16:53 +0000 Subject: [PATCH] Test that an unparseable exp on /v1/image/ is refused with 400 (closes #72) An exp that is not a whole number, or an empty exp=, is ignored today, so a URL for a host that needs a signature gets 401 as if it had no exp. This test asks for a 400 naming exp and the value instead; it fails until the route refuses it. A URL without exp still gets 401. Model: opus-5-5 --- .../handlers/image_signature_internal_test.go | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/internal/handlers/image_signature_internal_test.go b/internal/handlers/image_signature_internal_test.go index a045f0e..c435668 100644 --- a/internal/handlers/image_signature_internal_test.go +++ b/internal/handlers/image_signature_internal_test.go @@ -1,6 +1,7 @@ package handlers import ( + "encoding/json" "fmt" "net/http" "net/http/httptest" @@ -118,3 +119,53 @@ func TestHandleImage_GeneratedSignedURLVerifies(t *testing.T) { }) } } + +// TestHandleImage_InvalidExp_Returns400 sends a signed-host URL whose exp is +// not a whole number, and one whose exp is empty. Each is refused with 400 +// naming exp and the value, not with the 401 a URL without exp still gets. +func TestHandleImage_InvalidExp_Returns400(t *testing.T) { + t.Parallel() + + tests := []struct { + query string + wantStatus int + wantError string + }{ + {"sig=x&exp=banana", http.StatusBadRequest, + `invalid exp: not a number, got "banana"`}, + {"sig=x&exp=", http.StatusBadRequest, `invalid exp: not a number, got ""`}, + {"sig=x", http.StatusUnauthorized, "unauthorized"}, + } + + for _, tt := range tests { + t.Run(tt.query, func(t *testing.T) { + t.Parallel() + + fix := setupTestHandler(t) + + r := chi.NewRouter() + r.Get("/v1/image/*", fix.handler.HandleImage()) + + req := httptest.NewRequestWithContext(t.Context(), http.MethodGet, + "/v1/image/"+signedHost+"/images/photo.jpg/50x50.jpeg?"+tt.query, nil) + rec := httptest.NewRecorder() + + r.ServeHTTP(rec, req) + t.Logf("GET %s: %d %s", req.URL, rec.Code, rec.Body) + + var body struct { + Error string `json:"error"` + } + + err := json.NewDecoder(rec.Body).Decode(&body) + if err != nil { + t.Fatalf("decoding response body: %v", err) + } + + if rec.Code != tt.wantStatus || body.Error != tt.wantError { + t.Errorf("got %d %q, want %d %q", + rec.Code, body.Error, tt.wantStatus, tt.wantError) + } + }) + } +}