Test ETag, 304 and HEAD on /v1/e/
An image served through an encrypted URL must carry an ETag, a request whose If-None-Match is that ETag must get 304 with no body, HEAD must get 200 with the headers and no body, and the server must route HEAD on /v1/e/ to its handler instead of answering 405. Model: opus-5-5
This commit is contained in:
@@ -96,3 +96,70 @@ func TestHandleImageEnc_InvalidFitMode_Returns400(t *testing.T) {
|
||||
t.Fatalf("status = %d, want %d", rec.Code, http.StatusBadRequest)
|
||||
}
|
||||
}
|
||||
|
||||
// TestHandleImageEnc_IfNoneMatch_Returns304 verifies that an image served
|
||||
// through an encrypted URL carries an ETag, and that a request whose
|
||||
// If-None-Match is that ETag is answered 304 Not Modified with no body.
|
||||
func TestHandleImageEnc_IfNoneMatch_Returns304(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
h, srv := newSignedHostServer(t, slog.New(slog.DiscardHandler))
|
||||
target := encPhotoURL(t, h)
|
||||
|
||||
rec := httptest.NewRecorder()
|
||||
srv.ServeHTTP(rec, httptest.NewRequestWithContext(
|
||||
t.Context(), http.MethodGet, target, nil))
|
||||
|
||||
etag := rec.Header().Get("ETag")
|
||||
t.Logf("GET: %d, ETag %q", rec.Code, etag)
|
||||
|
||||
if rec.Code != http.StatusOK || etag == "" {
|
||||
t.Fatalf("GET: status = %d, ETag = %q, want %d and an ETag",
|
||||
rec.Code, etag, http.StatusOK)
|
||||
}
|
||||
|
||||
req := httptest.NewRequestWithContext(t.Context(), http.MethodGet, target, nil)
|
||||
req.Header.Set("If-None-Match", etag)
|
||||
|
||||
rec = httptest.NewRecorder()
|
||||
srv.ServeHTTP(rec, req)
|
||||
t.Logf("GET with If-None-Match: %d, %d body bytes", rec.Code, rec.Body.Len())
|
||||
|
||||
if rec.Code != http.StatusNotModified || rec.Body.Len() != 0 {
|
||||
t.Errorf("status = %d with %d body bytes, want %d with none",
|
||||
rec.Code, rec.Body.Len(), http.StatusNotModified)
|
||||
}
|
||||
}
|
||||
|
||||
// TestHandleImageEnc_HEAD_ReturnsHeadersOnly verifies that HEAD on an
|
||||
// encrypted URL is answered 200 with the headers GET sends and no body.
|
||||
func TestHandleImageEnc_HEAD_ReturnsHeadersOnly(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
h, _ := newSignedHostServer(t, slog.New(slog.DiscardHandler))
|
||||
|
||||
r := chi.NewRouter()
|
||||
r.Head("/v1/e/{token}/*", h.HandleImageEnc())
|
||||
|
||||
rec := httptest.NewRecorder()
|
||||
r.ServeHTTP(rec, httptest.NewRequestWithContext(
|
||||
t.Context(), http.MethodHead, encPhotoURL(t, h), nil))
|
||||
t.Logf("HEAD: %d, headers %v, %d body bytes",
|
||||
rec.Code, rec.Header(), rec.Body.Len())
|
||||
|
||||
if rec.Code != http.StatusOK {
|
||||
t.Fatalf("status = %d, want %d", rec.Code, http.StatusOK)
|
||||
}
|
||||
|
||||
for _, name := range []string{
|
||||
"Content-Type", "Content-Length", "Cache-Control", "ETag",
|
||||
} {
|
||||
if rec.Header().Get(name) == "" {
|
||||
t.Errorf("HEAD response has no %s", name)
|
||||
}
|
||||
}
|
||||
|
||||
if rec.Body.Len() != 0 {
|
||||
t.Errorf("HEAD response body has %d bytes, want none", rec.Body.Len())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestEncryptedImageRouteAnswersHEAD verifies that HEAD on the encrypted image
|
||||
// route reaches its handler, as GET does, instead of being answered 405 Method
|
||||
// Not Allowed. The handler refuses a token it cannot decrypt with 400, so that
|
||||
// status shows the request got through.
|
||||
func TestEncryptedImageRouteAnswersHEAD(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
s := newTestServer(t)
|
||||
|
||||
rec := httptest.NewRecorder()
|
||||
s.ServeHTTP(rec, httptest.NewRequestWithContext(
|
||||
t.Context(), http.MethodHead, encryptedImagePath, nil))
|
||||
t.Logf("status %d", rec.Code)
|
||||
|
||||
if rec.Code != http.StatusBadRequest {
|
||||
t.Errorf("status = %d, want %d from the encrypted image handler",
|
||||
rec.Code, http.StatusBadRequest)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user