package voice // 本文件钉住 DashScope 错误收口里的**令牌守卫**(trace.ErrorToken):code 与 // request_id 都是上游自由文本面 —— code 在 DashScope 是枚举(InvalidParameter / // Throttling.RateQuota / Arrearage …),但网关 / 兼容层可以往里塞任何东西。 // // 已有的用例(TestDashscopeTTS_Non200DropsSpokenText)只断言「令牌形状的 code 被 // 保留」,去掉守卫它照样通过 —— 那是个半张网。这里补上另一半:**不像令牌的 code // 必须变空串**,而 status / request_id 这些诊断面仍在。 // // 形状是「把守去掉就变红」:断言的是错误文本,也就是真正进日志的字符串。 import ( "context" "errors" "net/http" "net/http/httptest" "strings" "testing" "git.yeij.top/AskaEth/Cyrene_v2/internal/config" ) // TestDashscopeTTS_Non200DropsFreeTextCode 4xx 的 code 里放一段散文(带空格与标点) // ⇒ 该字段整体消失(连 `code=` 这个键都不出现),而 status 400 与 request_id 仍在。 func TestDashscopeTTS_Non200DropsFreeTextCode(t *testing.T) { t.Parallel() const prose = "Invalid value: '她刚才说的那句话'" srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusBadRequest) _, _ = w.Write([]byte(`{"code":"` + prose + `","request_id":"req-0123456789abcdef"}`)) })) defer srv.Close() tts := NewDashscopeTTS(config.VoiceConfig{DashscopeBaseURL: srv.URL, DashscopeAPIKey: "sk"}) _, err := tts.TTS(context.Background(), "随便说点什么") if err == nil { t.Fatal("400 却报成功") } if !errors.Is(err, ErrProviderFailed) { t.Fatalf("err = %v, want ErrProviderFailed(熔断口径不变)", err) } msg := err.Error() if strings.Contains(msg, prose) { t.Fatalf("code 里的散文进了错误:%v", msg) } if strings.Contains(msg, "code=") { t.Fatalf("非令牌形状的 code 没有被丢弃:%v", msg) } // 诊断面不能一起丢:状态码与 request_id(对厂商报障时要给的那个)。 for _, want := range []string{"status=400", "req-0123456789abcdef"} { if !strings.Contains(msg, want) { t.Fatalf("错误里缺少 %q(诊断面不能一起丢):%v", want, msg) } } }