package weixin_ilink // 本文件是 M49 的**出口回归探针**:令牌守卫上移进 trace.UpstreamError 之后, // iLink 适配器一行没改也应当自动安全 —— 上游把散文塞进 error.code / error.type // 时,错误文本与日志里都搜不到它,而 status / body_bytes 照旧。 // // 断言形状是「摘掉 trace.UpstreamError 里的守卫就变红」:这里没有任何本地守卫。 // iLink 会在 errmsg 里回显请求内容(发出去的就是她说的话),所以这一条不能漏。 import ( "bytes" "context" "log/slog" "net/http" "net/http/httptest" "strings" "testing" ) // TestDoJSON_StatusErrorDropsFreeTextUpstreamCode 4xx 报文的 error.code / // error.type 里带散文 ⇒ 两个字段整体消失,状态码与体积摘要仍在。 func TestDoJSON_StatusErrorDropsFreeTextUpstreamCode(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(`{"error":{"code":"` + prose + `","type":"` + prose + `"}}`)) })) defer srv.Close() ad := newAdapter(t, Config{BaseURL: srv.URL, BotToken: "tok"}, srv) err := ad.doJSON(context.Background(), http.MethodPost, ad.apiURL("/ilink/bot/sendmessage"), nil, nil) if err == nil { t.Fatal("400 却报成功") } var logBuf bytes.Buffer slog.New(slog.NewJSONHandler(&logBuf, nil)).Error("ilink call failed", "err", err) for _, surface := range []string{err.Error(), logBuf.String()} { if strings.Contains(surface, "她刚才说的那句话") { t.Fatalf("上游 error.code/type 里的散文进了错误或日志:%s", surface) } } for _, want := range []string{"status=400", "body_bytes="} { if !strings.Contains(err.Error(), want) { t.Fatalf("错误里缺少 %q:%v", want, err) } } }