check / check (push) Canceled after 0s
The new package internal/imagerequest holds the request type, its two parts Source and Transform, the Format and Fit types, the dimension and fit checks, the /v1/image/ path parsing and the variant key; it imports no other package of pixa. imgcache.ParsedURL, imgcache.ImageRequest, signature.Request, imageprocessor.Request, the second Size, format and fit types and the conversions between them are gone. The image processor takes a Transform only. encurl keeps a private struct with the token's CBOR field names, so tokens made before still decode. VariantKey moves with the function that makes it. Tests follow the moved and renamed code; the tests of the removed conversions go with them. Model: opus-5-5
91 lines
2.9 KiB
Go
91 lines
2.9 KiB
Go
package handlers
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log/slog"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
|
|
"sneak.berlin/go/pixa/internal/httpfetcher"
|
|
"sneak.berlin/go/pixa/internal/imageprocessor"
|
|
"sneak.berlin/go/pixa/internal/imagerequest"
|
|
"sneak.berlin/go/pixa/internal/imgcache"
|
|
)
|
|
|
|
// TestServerBusyAnswers503 checks that both image routes answer 503 with
|
|
// a clear error when the image service gives up waiting for a free upstream
|
|
// connection or processing slot, wrapped as the service wraps them.
|
|
func TestServerBusyAnswers503(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
h := &Handlers{log: slog.New(slog.DiscardHandler)}
|
|
req := &imagerequest.Request{
|
|
Source: imagerequest.Source{Host: "img.example.com", Path: "/a.jpg"},
|
|
}
|
|
|
|
for _, err := range []error{
|
|
fmt.Errorf("upstream fetch failed: %w", httpfetcher.ErrTooManyConnections),
|
|
fmt.Errorf("image processing failed: %w", imageprocessor.ErrTooManyImages),
|
|
} {
|
|
plain := httptest.NewRecorder()
|
|
h.respondImageError(plain, req, err)
|
|
|
|
encrypted := httptest.NewRecorder()
|
|
h.handleImageError(encrypted, err)
|
|
|
|
for route, rec := range map[string]*httptest.ResponseRecorder{
|
|
"/v1/image/": plain, "/v1/e/": encrypted,
|
|
} {
|
|
if rec.Code != http.StatusServiceUnavailable {
|
|
t.Errorf("%s for %v: status = %d, want %d",
|
|
route, err, rec.Code, http.StatusServiceUnavailable)
|
|
}
|
|
|
|
if !strings.Contains(rec.Body.String(), "server busy, try again later") {
|
|
t.Errorf("%s for %v: body = %q, want the server busy error",
|
|
route, err, rec.Body.String())
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestProcessingTimedOutAnswers503 checks that respondImageError and
|
|
// handleImageError, which answer the errors of the two image routes, answer
|
|
// 503 with the server busy error when the image service reports that
|
|
// processing stopped at the deadline of the request doing it, wrapped as the
|
|
// service wraps it. A request that waited for that processing gets this error
|
|
// while its own deadline may have time left. The request's source only goes
|
|
// into a log line, so it is left empty.
|
|
func TestProcessingTimedOutAnswers503(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
h := &Handlers{log: slog.New(slog.DiscardHandler)}
|
|
err := fmt.Errorf("%w: failed to encode: %w",
|
|
imgcache.ErrProcessingTimedOut, context.DeadlineExceeded)
|
|
|
|
plain := httptest.NewRecorder()
|
|
h.respondImageError(plain, &imagerequest.Request{}, err)
|
|
|
|
encrypted := httptest.NewRecorder()
|
|
h.handleImageError(encrypted, err)
|
|
|
|
for name, rec := range map[string]*httptest.ResponseRecorder{
|
|
"respondImageError": plain, "handleImageError": encrypted,
|
|
} {
|
|
t.Logf("%s: status = %d, body = %q", name, rec.Code, rec.Body.String())
|
|
|
|
if rec.Code != http.StatusServiceUnavailable {
|
|
t.Errorf("%s: status = %d, want %d",
|
|
name, rec.Code, http.StatusServiceUnavailable)
|
|
}
|
|
|
|
if !strings.Contains(rec.Body.String(), "server busy, try again later") {
|
|
t.Errorf("%s: body = %q, want the server busy error",
|
|
name, rec.Body.String())
|
|
}
|
|
}
|
|
}
|