Files
viettranx 8c8849365f feat(audio): Manager STT dispatch with channel override
Add Transcribe() method to Manager with STT provider dispatch and channel-specific overrides. Implements channelSTTOverrides map, WithChannel context propagation, RegisterChannelSTT registration, and resolveSTTChain fallback logic. Includes manager-level unit tests.
2026-04-15 11:24:57 +07:00

147 lines
4.2 KiB
Go

package audio
import (
"context"
"errors"
"testing"
)
// mockSTT is a test STTProvider that either returns a fixed result or error.
type mockSTT struct {
name string
result *TranscriptResult
err error
}
func (m *mockSTT) Name() string { return m.name }
func (m *mockSTT) Transcribe(_ context.Context, _ STTInput, _ STTOptions) (*TranscriptResult, error) {
return m.result, m.err
}
func newTestManager() *Manager {
return NewManager(ManagerConfig{})
}
// Case 1: chain ["elevenlabs","proxy"] — Scribe succeeds, result.Provider="elevenlabs".
func TestManager_Transcribe_ChainSuccessFirst(t *testing.T) {
m := newTestManager()
m.RegisterSTT(&mockSTT{
name: "elevenlabs",
result: &TranscriptResult{Text: "hello", Provider: "elevenlabs"},
})
m.RegisterSTT(&mockSTT{
name: "proxy",
result: &TranscriptResult{Text: "world", Provider: "proxy"},
})
m.SetSTTChain([]string{"elevenlabs", "proxy"})
res, err := m.Transcribe(context.Background(), STTInput{}, STTOptions{})
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if res.Provider != "elevenlabs" {
t.Errorf("expected provider 'elevenlabs', got %q", res.Provider)
}
if res.Text != "hello" {
t.Errorf("expected text 'hello', got %q", res.Text)
}
}
// Case 2: Scribe fails → proxy succeeds → result.Provider="proxy".
func TestManager_Transcribe_FallsToProxy(t *testing.T) {
m := newTestManager()
m.RegisterSTT(&mockSTT{
name: "elevenlabs",
err: errors.New("scribe 500"),
})
m.RegisterSTT(&mockSTT{
name: "proxy",
result: &TranscriptResult{Text: "fallback", Provider: "proxy"},
})
m.SetSTTChain([]string{"elevenlabs", "proxy"})
res, err := m.Transcribe(context.Background(), STTInput{}, STTOptions{})
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if res.Provider != "proxy" {
t.Errorf("expected provider 'proxy', got %q", res.Provider)
}
}
// Case 3: both fail → ErrAllSTTProvidersFailed.
func TestManager_Transcribe_AllFail(t *testing.T) {
m := newTestManager()
m.RegisterSTT(&mockSTT{name: "elevenlabs", err: errors.New("scribe error")})
m.RegisterSTT(&mockSTT{name: "proxy", err: errors.New("proxy error")})
m.SetSTTChain([]string{"elevenlabs", "proxy"})
_, err := m.Transcribe(context.Background(), STTInput{}, STTOptions{})
if err == nil {
t.Fatal("expected error, got nil")
}
if !errors.Is(err, ErrAllSTTProvidersFailed) {
t.Errorf("expected ErrAllSTTProvidersFailed, got: %v", err)
}
}
// Case 4: empty chain → error.
func TestManager_Transcribe_EmptyChain(t *testing.T) {
m := newTestManager()
m.SetSTTChain([]string{})
_, err := m.Transcribe(context.Background(), STTInput{}, STTOptions{})
if err == nil {
t.Fatal("expected error for empty chain, got nil")
}
if !errors.Is(err, ErrAllSTTProvidersFailed) {
t.Errorf("expected ErrAllSTTProvidersFailed, got: %v", err)
}
}
// Case 5: unknown provider in chain is skipped with warn; known one succeeds.
func TestManager_Transcribe_UnknownSkipped(t *testing.T) {
m := newTestManager()
m.RegisterSTT(&mockSTT{
name: "proxy",
result: &TranscriptResult{Text: "ok", Provider: "proxy"},
})
m.SetSTTChain([]string{"unknown_provider", "proxy"})
res, err := m.Transcribe(context.Background(), STTInput{}, STTOptions{})
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if res.Provider != "proxy" {
t.Errorf("expected provider 'proxy', got %q", res.Provider)
}
}
// Case 6: channel override wins over tenant chain.
func TestManager_Transcribe_ChannelOverrideWins(t *testing.T) {
m := newTestManager()
// Register tenant-level provider.
m.RegisterSTT(&mockSTT{
name: "elevenlabs",
result: &TranscriptResult{Text: "tenant", Provider: "elevenlabs"},
})
m.SetSTTChain([]string{"elevenlabs"})
// Register channel-scoped proxy that wins for "telegram".
channelProxy := &mockSTT{
name: "proxy",
result: &TranscriptResult{Text: "channel-override", Provider: "proxy"},
}
m.RegisterChannelSTT("telegram", channelProxy)
ctx := WithChannel(context.Background(), "telegram")
res, err := m.Transcribe(ctx, STTInput{}, STTOptions{})
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if res.Text != "channel-override" {
t.Errorf("expected channel override result, got %q", res.Text)
}
}