package server import ( "io" "net/http" "net/http/httptest" "strings" "sync/atomic" "testing" "sneak.berlin/go/pixa/internal/imageproxy" "sneak.berlin/go/pixa/internal/imagerequest" "sneak.berlin/go/pixa/internal/magic" ) // testSVG is the SVG the upstream serves in the SVG tests below. const testSVG = `` + `` // startSVGUpstream starts an upstream that answers every request with // testSVG as image/svg+xml, and returns it with its count of requests. func startSVGUpstream(t *testing.T) (*httptest.Server, *atomic.Int64) { t.Helper() var requests atomic.Int64 upstream := httptest.NewServer(http.HandlerFunc( func(w http.ResponseWriter, _ *http.Request) { requests.Add(1) w.Header().Set("Content-Type", "image/svg+xml") _, _ = io.WriteString(w, testSVG) })) t.Cleanup(upstream.Close) return upstream, &requests } // TestImageProxyServesSVGAsItIs requests an SVG source in its own format // (orig) at the orig size through pixa's router, upstream fetcher, image // processor and cache, twice. Both answers are the SVG itself, never another // format, the second from the cache; the upstream is asked once. Scripts in // the SVG cannot run, as the Content-Security-Policy allows no inline // script. orig at another size is refused with 400. func TestImageProxyServesSVGAsItIs(t *testing.T) { t.Parallel() upstream, requests := startSVGUpstream(t) s, _, _ := startImageProxy(t, upstream) target := "/v1/image/" + upstreamHost + "/drawing.svg/orig.orig" for _, wantCache := range []imageproxy.CacheStatus{ imageproxy.CacheMiss, imageproxy.CacheHit, } { rec := getImage(t, s, target) if rec.Header().Get("Content-Type") != "image/svg+xml" || rec.Body.String() != testSVG { t.Errorf("GET %s: %s %q, want image/svg+xml %q", target, rec.Header().Get("Content-Type"), rec.Body.String(), testSVG) } if got := rec.Header().Get("X-Pixa-Cache"); got != string(wantCache) { t.Errorf("GET %s: X-Pixa-Cache = %q, want %q", target, got, wantCache) } policy := rec.Header().Get("Content-Security-Policy") if !strings.Contains(policy, "script-src 'self'") || strings.Contains(policy, "unsafe-inline") { t.Errorf("GET %s: Content-Security-Policy = %q, want script-src "+ "'self' and no unsafe-inline", target, policy) } } if requests.Load() != 1 { t.Errorf("upstream requests = %d, want 1", requests.Load()) } resized := "/v1/image/" + upstreamHost + "/drawing.svg/32x24.orig" rec := httptest.NewRecorder() s.ServeHTTP(rec, httptest.NewRequestWithContext( t.Context(), http.MethodGet, resized, nil)) t.Logf("GET %s: %d %s", resized, rec.Code, rec.Body.String()) if rec.Code != http.StatusBadRequest { t.Errorf("GET %s: %d %s, want 400", resized, rec.Code, rec.Body.String()) } } // TestImageProxyRendersSVG requests an SVG source as each raster format // through pixa's router, upstream fetcher and image processor. Each answer // is an image in that format. func TestImageProxyRendersSVG(t *testing.T) { t.Parallel() upstream, _ := startSVGUpstream(t) s, _, _ := startImageProxy(t, upstream) for _, format := range []imagerequest.Format{ imagerequest.FormatJPEG, imagerequest.FormatPNG, imagerequest.FormatGIF, imagerequest.FormatWebP, imagerequest.FormatAVIF, imagerequest.FormatJXL, } { target := "/v1/image/" + upstreamHost + "/drawing.svg/32x24." + string(format) rec := getImage(t, s, target) detected, err := magic.DetectFormat(rec.Body.Bytes()) t.Logf("GET %s: %s, body in %q (%v)", target, rec.Header().Get("Content-Type"), detected, err) if rec.Header().Get("Content-Type") != format.MIMEType() || err != nil || detected != format { t.Errorf("GET %s: %s, body in %q (%v); want %s", target, rec.Header().Get("Content-Type"), detected, err, format.MIMEType()) } } }