A request that waited for another request's transcode, stopped at that request's deadline, must get ErrProcessingTimedOut, which both image routes answer 503. Declares that error so the tests build. Also tests that Process, past its deadline before it starts, never reads its input. Model: opus-5-5
88 lines
2.8 KiB
Go
88 lines
2.8 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/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 := &imgcache.ImageRequest{SourceHost: "img.example.com", SourcePath: "/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, &imgcache.ImageRequest{}, 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())
|
|
}
|
|
}
|
|
}
|