Test the image proxy flow end to end (closes #80)
check / check (push) Failing after 2s

TestImageProxyFlow in internal/server starts the database, handlers and
middleware from the constructors pixad uses, with a fresh state
directory, and replaces only the upstream origin with an httptest
server. For a resize with a format change and for orig it checks a 200
MISS with the right type and size, then a HIT after one upstream request,
and the files and rows the cache keeps. Two optional test seams make
that possible: httpfetcher.Config.DialContext and handlers.Params.Fetcher.
pixad sets neither and the config file and environment cannot, and tests
show production still uses the checked dialer and builds its own fetcher.

Model: opus-5-5
This commit was merged in pull request #195.
This commit is contained in:
2026-10-04 23:24:46 +02:00
parent 708a9bec20
commit cca2e3f926
7 changed files with 473 additions and 9 deletions
@@ -0,0 +1,66 @@
package httpfetcher
import (
"errors"
"net"
"testing"
)
// TestNewUsesCheckedDialerWithoutDialContext checks that a fetcher built
// without DialContext, as pixa builds it, refuses to connect to a local
// server.
func TestNewUsesCheckedDialerWithoutDialContext(t *testing.T) {
t.Parallel()
srv := startUpstream(t)
transport := transportOf(t, New(DefaultConfig()))
addr := srv.Listener.Addr().String()
_, err := transport.DialContext(testContext(t), "tcp", addr)
if !errors.Is(err, ErrSSRFBlocked) {
t.Fatalf("DialContext(%s) error = %v, want ErrSSRFBlocked", addr, err)
}
}
// TestDialContextReplacesOnlyTheDialer checks that a fetcher built with
// DialContext connects through it, while the URL check still refuses a
// loopback URL and the redirect check a redirect to a link-local address.
func TestDialContextReplacesOnlyTheDialer(t *testing.T) {
t.Parallel()
srv := startUpstream(t)
dialer := &recordingDialer{target: srv.Listener.Addr().String()}
cfg := DefaultConfig()
cfg.AllowHTTP = true
cfg.DialContext = dialer.dialContext
f := New(cfg)
if body := fetchBody(t, f, "/image"); body != imagePayload {
t.Errorf("body = %q, want %q", body, imagePayload)
}
_, err := f.Fetch(testContext(t), "http://127.0.0.1/image")
if !errors.Is(err, ErrSSRFBlocked) {
t.Errorf("Fetch(loopback URL) error = %v, want ErrSSRFBlocked", err)
}
_, err = f.Fetch(testContext(t), upstreamURL("/redirect/private"))
if !errors.Is(err, ErrSSRFBlocked) {
t.Errorf("Fetch(/redirect/private) error = %v, want ErrSSRFBlocked", err)
}
// The upstream server is reached through DialContext, and nothing else
// is asked of it.
dialed := dialer.dialedAddrs()
if len(dialed) == 0 {
t.Error("DialContext was never called")
}
for _, addr := range dialed {
if addr != net.JoinHostPort(testPublicHost, "80") {
t.Errorf("DialContext was asked to connect to %s", addr)
}
}
}