package asr import ( "context" "encoding/binary" "os" "strings" "testing" "time" ) // TestGummyLive 9A gummy 实测(需真实凭证,无则跳过): // // rbw get dashscope-api-key | { read -r K; DASHSCOPE_API_KEY="$K" GUMMY_TEST_WAV=/path/16k.wav go test ./internal/asr -run TestGummyLive -v; } func TestGummyLive(t *testing.T) { key := os.Getenv("DASHSCOPE_API_KEY") wav := os.Getenv("GUMMY_TEST_WAV") if key == "" || wav == "" { t.Skip("DASHSCOPE_API_KEY / GUMMY_TEST_WAV 未设置,跳过 gummy 实测") } pcm, err := readWavDataT(t, wav) if err != nil { t.Fatalf("读 wav: %v", err) } ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) defer cancel() sess, err := NewGummy(key).StartSession(ctx, SessionConfig{SampleRate: 16000, SessionID: "live-test"}) if err != nil { t.Fatalf("StartSession: %v", err) } var partials, finals int var lastFinal string var maxEndMs int64 done := make(chan struct{}) go func() { defer close(done) for r := range sess.Results() { if r.Err != nil { t.Errorf("result err: %v", r.Err) return } if r.IsFinal { finals++ lastFinal = r.Text } else { partials++ } if r.EndTimeMs > maxEndMs { maxEndMs = r.EndTimeMs } } }() for off := 0; off < len(pcm); off += 3200 { end := min(off+3200, len(pcm)) if err := sess.SendAudio(pcm[off:end]); err != nil { t.Fatalf("SendAudio: %v", err) } time.Sleep(50 * time.Millisecond) } _ = sess.Close() <-done if partials == 0 || finals == 0 { t.Fatalf("期望 partial>0 且 final>0,实得 partial=%d final=%d", partials, finals) } if strings.TrimSpace(lastFinal) == "" { t.Fatal("final 文本为空") } audioMs := int64(len(pcm)) * 1000 / 32000 if maxEndMs <= 0 || maxEndMs > audioMs+3000 { t.Fatalf("provider 时间戳异常: end=%dms audio=%dms", maxEndMs, audioMs) } t.Logf("partials=%d finals=%d end=%dms final=%q", partials, finals, maxEndMs, lastFinal) } func readWavDataT(t *testing.T, path string) ([]byte, error) { t.Helper() b, err := os.ReadFile(path) if err != nil { return nil, err } off := 12 for off+8 <= len(b) { id := string(b[off : off+4]) size := int(binary.LittleEndian.Uint32(b[off+4 : off+8])) if id == "data" { return b[off+8 : min(off+8+size, len(b))], nil } off += 8 + size } return nil, os.ErrInvalid }