package handlers import ( "fmt" "log/slog" "maps" "net/http" "net/http/httptest" "net/url" "strings" "testing" "time" "github.com/davidbyttow/govips/v2/vips" "sneak.berlin/go/pixa/internal/encurl" "sneak.berlin/go/pixa/internal/imgcache" "sneak.berlin/go/pixa/internal/signature" ) // requireJPEGXL requires that rec answers 200 with a JPEG XL image. func requireJPEGXL(t *testing.T, rec *httptest.ResponseRecorder) { t.Helper() if rec.Code != http.StatusOK { t.Fatalf("status = %d, want %d; body %q", rec.Code, http.StatusOK, rec.Body.String()) } if got := rec.Header().Get("Content-Type"); got != jxlType { t.Errorf("Content-Type = %q, want %s", got, jxlType) } if got := vips.DetermineImageType(rec.Body.Bytes()); got != vips.ImageTypeJXL { t.Errorf("body is %s, want jxl", vips.ImageTypes[got]) } } // TestImageWithoutFormat_ServesJPEGXL verifies that a /v1/image/ URL whose // last segment is a size with no format, 50x50 or orig, answers JPEG XL. func TestImageWithoutFormat_ServesJPEGXL(t *testing.T) { t.Parallel() route := newImageRoute(t, newPhotoFetcher(t, allowlistedHost)) for _, size := range []string{"50x50", "orig"} { target := "/v1/image/" + allowlistedHost + photoPath + "/" + size requireJPEGXL(t, sendGet(t, route, target)) } } // TestImageWithoutFormat_SignedAsJXL verifies that a /v1/image/ URL with no // format is signed as jxl: the signature made for the URL ending in .jxl is // accepted for the same URL without .jxl. func TestImageWithoutFormat_SignedAsJXL(t *testing.T) { t.Parallel() route := newImageRoute(t, newPhotoFetcher(t, signedHost)) expires := time.Now().Add(time.Hour) sig := signature.New(testSigningKey).Sign(&signature.Request{ SourceHost: signedHost, SourcePath: photoPath, Width: 50, Height: 50, Format: string(imgcache.FormatJXL), Quality: encurl.DefaultQuality, FitMode: string(imgcache.FitCover), Expires: expires, }) query := fmt.Sprintf("?sig=%s&exp=%d", sig, expires.Unix()) for _, size := range []string{"50x50.jxl", "50x50"} { target := "/v1/image/" + signedHost + photoPath + "/" + size + query requireJPEGXL(t, sendGet(t, route, target)) } } // TestImageEncWithoutFormat_ServesJPEGXL verifies that an encrypted URL whose // token holds no format answers JPEG XL. func TestImageEncWithoutFormat_ServesJPEGXL(t *testing.T) { t.Parallel() h, srv := newSignedHostServer(t, slog.New(slog.DiscardHandler)) token, err := h.encGen.Generate(&encurl.Payload{ SourceHost: signedHost, SourcePath: photoPath, Width: 50, Height: 50, }) if err != nil { t.Fatalf("Generate() error = %v", err) } requireJPEGXL(t, getEncToken(srv, token)) } // TestGeneratorPage_SelectsJPEGXL verifies that the generator page's format // choice is JPEG XL until another is chosen. func TestGeneratorPage_SelectsJPEGXL(t *testing.T) { t.Parallel() h, srv := newCSRFTestRouter(t) req := httptest.NewRequestWithContext(t.Context(), http.MethodGet, "/", nil) req.AddCookie(newSessionCookie(t, h)) rec := httptest.NewRecorder() srv.ServeHTTP(rec, req) if !strings.Contains(rec.Body.String(), `