From 6101dd4e2f498b4fb31b663fd85650e91d7a4127 Mon Sep 17 00:00:00 2001 From: Mathias Date: Sat, 3 Oct 2026 20:56:47 +0200 Subject: [PATCH] test(audio): a hung ffprobe times out on its own deadline as unreadable (#49), red Co-Authored-By: Claude Sonnet 5.5 --- internal/adapters/audio/ffprobe_test.go | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/internal/adapters/audio/ffprobe_test.go b/internal/adapters/audio/ffprobe_test.go index cc3fa68..d0e68f7 100644 --- a/internal/adapters/audio/ffprobe_test.go +++ b/internal/adapters/audio/ffprobe_test.go @@ -234,3 +234,28 @@ func TestFFprobeProbe_OutputWithinTheCapIsParsed(t *testing.T) { require.NoError(t, err) assert.True(t, got.HasAudio) } + +func TestFFprobeProbe_AHungProbeTimesOutOnItsOwnDeadlineAsUnreadableNotAsOurFault(t *testing.T) { + // Accept runs the probe on the request context, which has no deadline of its own; + // a probe that never returns would pin a request (#49). `exec` so the sleep is the + // process itself and not a child holding the pipe open. + bin := stub(t, `exec sleep 30`) + + start := time.Now() + _, err := (&audio.FFprobe{Bin: bin, Timeout: 200 * time.Millisecond}).Probe(context.Background(), "/x") + + assert.ErrorIs(t, err, uploads.ErrUnreadable) + assert.Less(t, time.Since(start), 3*time.Second) +} + +func TestFFprobeProbe_ParentCancellationIsStillNotUnreadableWhenATimeoutIsSet(t *testing.T) { + bin := stub(t, `exec sleep 30`) + ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond) + defer cancel() + + _, err := (&audio.FFprobe{Bin: bin, Timeout: time.Minute}).Probe(ctx, "/x") + + require.Error(t, err) + assert.False(t, errors.Is(err, uploads.ErrUnreadable), "the caller giving up is not a verdict on the file") + assert.ErrorIs(t, err, context.DeadlineExceeded) +}