Files
AskaEth b0fbb8bc0d
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
docs(comments): 注释中文化 · internal/runtime/multimodal(21 文件,纯替换)
- 只翻注释/文档字符串,代码零变化:go/scanner 词法流比对 21 文件 token+字面量全等,增删行数 1:1(467/467)
- 刻意保留非散文英文:JSON Schema description、工具 Description() 返回值、adapter prompt、API 路径与命令示例、环境变量名
- 逐行确认代码前缀未变,未触碰其他模块
2026-10-04 12:28:16 +08:00

109 lines
2.4 KiB
Go

package voice
import (
"context"
"errors"
"sync"
"testing"
"time"
)
func TestService_STTAndTTS(t *testing.T) {
t.Parallel()
chain := NewChain(
NewProvider("cloud", &stubSTT{text: "transcribed"}, &stubTTS{audio: Audio{Format: "mp3", Data: []byte("a")}}),
)
svc := NewService(chain)
defer svc.Close()
text, err := svc.STT(context.Background(), Audio{Format: "wav", Data: []byte("x")})
if err != nil {
t.Fatalf("STT error: %v", err)
}
if text != "transcribed" {
t.Errorf("text = %q, want transcribed", text)
}
audio, err := svc.TTS(context.Background(), "hello")
if err != nil {
t.Fatalf("TTS error: %v", err)
}
if string(audio.Data) != "a" || audio.Format != "mp3" {
t.Errorf("audio = %+v, want mp3/a", audio)
}
}
func TestService_CloseStopsNewCalls(t *testing.T) {
t.Parallel()
svc := NewService(NewChain(SilentProvider()))
svc.Close()
_, err := svc.STT(context.Background(), Audio{})
if !errors.Is(err, ErrClosed) {
t.Fatalf("err = %v, want ErrClosed", err)
}
}
// blockingSTT 会阻塞到它的 context 被取消,同时标记它已启动。
type blockingSTT struct {
started chan struct{}
once sync.Once
}
func (b *blockingSTT) STT(ctx context.Context, _ Audio) (string, error) {
b.once.Do(func() { close(b.started) })
<-ctx.Done()
return "", ctx.Err()
}
func TestService_CloseCancelsInFlightWorker(t *testing.T) {
t.Parallel()
blk := &blockingSTT{started: make(chan struct{})}
chain := NewChain(NewProvider("cloud", blk, nil))
svc := NewService(chain)
done := make(chan struct{})
go func() {
defer close(done)
_, _ = svc.STT(context.Background(), Audio{Format: "wav", Data: []byte("x")})
}()
select {
case <-blk.started:
case <-time.After(2 * time.Second):
t.Fatal("provider never started")
}
svc.Close()
select {
case <-done:
case <-time.After(2 * time.Second):
t.Fatal("STT call did not return after Close; worker likely leaked")
}
}
func TestService_CallerCancelUnblocks(t *testing.T) {
t.Parallel()
blk := &blockingSTT{started: make(chan struct{})}
svc := NewService(NewChain(NewProvider("cloud", blk, nil)))
defer svc.Close()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
done := make(chan struct{})
go func() {
defer close(done)
_, _ = svc.STT(ctx, Audio{Format: "wav", Data: []byte("x")})
}()
<-blk.started
cancel()
select {
case <-done:
case <-time.After(2 * time.Second):
t.Fatal("caller cancellation did not unblock STT")
}
}