Files
wangjia 40760aa884
ci / server (push) Failing after 14s
ci / design-tokens (push) Failing after 11s
dudu MVP:五端语音输入法初始提交
- server:Go 网关(WS 流式识别中继/计费配额/微信登录支付 mock/反馈/埋点),gummy provider 已真实联调
- desktop:Tauri 2(全局快捷键 push-to-talk/浮层/托盘/设置/登录购买/反馈/首启引导)
- android:Compose 主 App + IME(键盘内录音直传)
- ios:App + 键盘扩展(1A spike 实证键盘内不可录音,走 deep link 听写)
- design/design-pipeline:设计系统 + token 导出 iOS/Android 主题
- doc:前后端设计文档(HTML);web:官网宣传页;todo:任务看板

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 00:38:37 +08:00

96 lines
2.3 KiB
Go

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
}