Test that a shared miss keeps its deadline and reports panics (closes #65)

A request with a deadline whose fetch waits on its context, as the
fetcher does while it waits for a free connection to the host, must
return by that deadline. A panic while a variant is being made must be
reported through the Sentry hub on the request's context, where the
Sentry middleware puts one when sentry_dsn is set. The panic test fails
on next, where the panic leaves Get.

Model: opus-5-5
This commit is contained in:
2026-10-03 15:53:34 +00:00
parent b36b1abbc8
commit 2777d3f784
@@ -13,6 +13,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/getsentry/sentry-go"
"sneak.berlin/go/pixa/internal/httpfetcher" "sneak.berlin/go/pixa/internal/httpfetcher"
"sneak.berlin/go/pixa/internal/magic" "sneak.berlin/go/pixa/internal/magic"
) )
@@ -408,6 +409,32 @@ func TestService_Get_EndedRequestFetchesNothing(t *testing.T) {
} }
} }
// TestService_Get_ReturnsByItsDeadline gives a request a deadline and holds
// its fetch until the fetch's context ends, as the fetcher does while it waits
// for a free connection to the host. The request must return by its deadline
// with the deadline's error.
func TestService_Get_ReturnsByItsDeadline(t *testing.T) {
t.Parallel()
svc, fixtures, _ := setupHeldFetchService(t)
const timeout = 200 * time.Millisecond
ctx, cancel := context.WithTimeout(t.Context(), timeout)
defer cancel()
results := startGet(ctx, svc, photoVariant(fixtures, 85, FitCover))
select {
case got := <-results:
if !errors.Is(got.err, context.DeadlineExceeded) {
t.Errorf("Get() error = %v, want %v", got.err, context.DeadlineExceeded)
}
case <-time.After(timeout + time.Second):
t.Fatal("request did not return by its deadline")
}
}
// panickingFetcher panics on every fetch. // panickingFetcher panics on every fetch.
type panickingFetcher struct{} type panickingFetcher struct{}
@@ -442,3 +469,41 @@ func TestService_Get_PanicBecomesAnError(t *testing.T) {
t.Errorf("Get() error = %v, want one naming the panic", err) t.Errorf("Get() error = %v, want one naming the panic", err)
} }
} }
// TestService_Get_PanicIsReportedToSentry checks that a panic while a variant
// is being made is reported through the Sentry hub on the request's context,
// where the Sentry middleware puts one when sentry_dsn is set.
func TestService_Get_PanicIsReportedToSentry(t *testing.T) {
t.Parallel()
svc, fixtures := SetupTestService(t)
svc.fetcher = panickingFetcher{}
transport := &sentry.MockTransport{}
client, err := sentry.NewClient(sentry.ClientOptions{
Dsn: "https://abc123@sentry.example.com/42",
Transport: transport,
})
if err != nil {
t.Fatal(err)
}
ctx := sentry.SetHubOnContext(t.Context(),
sentry.NewHub(client, sentry.NewScope()))
func() {
defer func() {
if recovered := recover(); recovered != nil {
t.Fatalf("Get() panicked: %v", recovered)
}
}()
_, _ = svc.Get(ctx, photoVariant(fixtures, 85, FitCover))
}()
events := transport.Events()
if len(events) != 1 || events[0].Message != "upstream fetcher panicked" {
t.Errorf("Sentry events = %+v, want one for the panic", events)
}
}