Gateway: field is `file`, no Expect header, a computed Content-Length (not chunked, not buffered -- a 90 min WAV is ~170 MB), verbose_json normalised with the last segment clamped to the audio duration, errors independent of the body, an empty transcript is a failure. Chain: ANY primary failure falls back and the row records the fallback endpoint; a cancelled job never falls back to the cloud. Red: package transcribe does not exist. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
100 lines
3.3 KiB
Go
100 lines
3.3 KiB
Go
package transcribe_test
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"git.d-ma.be/mathias/tapir/internal/adapters/transcribe"
|
|
"git.d-ma.be/mathias/tapir/internal/domain"
|
|
)
|
|
|
|
type fakeBackend struct {
|
|
name string
|
|
text string
|
|
err error
|
|
calls int
|
|
}
|
|
|
|
func (f *fakeBackend) Name() string { return f.name }
|
|
func (f *fakeBackend) Transcribe(_ context.Context, _ string) (domain.Transcription, error) {
|
|
f.calls++
|
|
if f.err != nil {
|
|
return domain.Transcription{}, f.err
|
|
}
|
|
return domain.Transcription{Text: f.text, Endpoint: f.name}, nil
|
|
}
|
|
|
|
func TestChain_PrimarySuccessNeverTouchesTheFallback(t *testing.T) {
|
|
primary := &fakeBackend{name: "iguana/kb-whisper", text: "primary text"}
|
|
fallback := &fakeBackend{name: "berget/kb-whisper", text: "fallback text"}
|
|
|
|
got, err := transcribe.NewChain(primary, fallback).Transcribe(context.Background(), "/a.wav")
|
|
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "primary text", got.Text)
|
|
assert.Equal(t, "iguana/kb-whisper", got.Endpoint)
|
|
assert.Zero(t, fallback.calls, "audio must not leave the homelab when the local endpoint worked")
|
|
}
|
|
|
|
func TestChain_AnyPrimaryFailureFallsBackAndRecordsTheFallbackEndpoint(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
err error
|
|
}{
|
|
{"server error", &transcribe.StatusError{Code: 500}},
|
|
{"gateway timeout", &transcribe.StatusError{Code: 504}},
|
|
{"client error", &transcribe.StatusError{Code: 400}},
|
|
{"transport failure", errors.New("dial tcp: connection refused")},
|
|
{"empty transcript", transcribe.ErrEmptyTranscript},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
primary := &fakeBackend{name: "iguana/kb-whisper", err: tt.err}
|
|
fallback := &fakeBackend{name: "berget/kb-whisper", text: "fallback text"}
|
|
|
|
got, err := transcribe.NewChain(primary, fallback).Transcribe(context.Background(), "/a.wav")
|
|
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "fallback text", got.Text)
|
|
assert.Equal(t, "berget/kb-whisper", got.Endpoint,
|
|
"the row must say berget served it: that means the audio left the homelab")
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestChain_BothFailingReportsBothErrors(t *testing.T) {
|
|
primary := &fakeBackend{name: "iguana/kb-whisper", err: errors.New("iguana down")}
|
|
fallback := &fakeBackend{name: "berget/kb-whisper", err: errors.New("berget down")}
|
|
|
|
_, err := transcribe.NewChain(primary, fallback).Transcribe(context.Background(), "/a.wav")
|
|
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "iguana down")
|
|
assert.Contains(t, err.Error(), "berget down")
|
|
}
|
|
|
|
func TestChain_WithoutAFallbackAPrimaryFailureIsTheResult(t *testing.T) {
|
|
primary := &fakeBackend{name: "iguana/kb-whisper", err: errors.New("iguana down")}
|
|
|
|
_, err := transcribe.NewChain(primary, nil).Transcribe(context.Background(), "/a.wav")
|
|
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "iguana down")
|
|
}
|
|
|
|
func TestChain_ACancelledJobDoesNotFallBack(t *testing.T) {
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
cancel()
|
|
primary := &fakeBackend{name: "iguana/kb-whisper", err: context.Canceled}
|
|
fallback := &fakeBackend{name: "berget/kb-whisper", text: "x"}
|
|
|
|
_, err := transcribe.NewChain(primary, fallback).Transcribe(ctx, "/a.wav")
|
|
|
|
assert.ErrorIs(t, err, context.Canceled)
|
|
assert.Zero(t, fallback.calls, "a cancelled job must not ship the audio to a cloud endpoint")
|
|
}
|