package vision // 本文件是 M49 的**出口回归探针**:令牌守卫上移进 trace.UpstreamError 之后, // 本适配器一行没改也应当自动安全 —— 视觉上游把散文塞进 error.code / error.type // 时,错误文本与日志里都搜不到它,而 status / body_bytes 这些诊断面照旧。 // // 断言形状是「摘掉 trace.UpstreamError 里的守卫就变红」:这里没有任何本地守卫。 // 视觉这一处尤其不能漏 —— 送上游的图像可能是她的照片,回显它的散文同样不能进日志。 import ( "bytes" "context" "errors" "io" "log/slog" "net/http" "strings" "testing" ) // TestOpenAICompatibleAdapter_DropsFreeTextUpstreamCode 4xx 报文的 error.code / // error.type 里带散文 ⇒ 两个字段整体消失,status 与体积摘要仍在。 func TestOpenAICompatibleAdapter_DropsFreeTextUpstreamCode(t *testing.T) { t.Parallel() const prose = "Invalid value: '她刚才说的那句话'" body := `{"error":{"code":"` + prose + `","type":"` + prose + `"}}` ad := newAdapter("https://vision.example.com") ad.client = &http.Client{Transport: stubTransport(func(*http.Request) (*http.Response, error) { return &http.Response{ StatusCode: http.StatusBadRequest, Header: http.Header{"Content-Type": []string{"application/json"}}, Body: io.NopCloser(strings.NewReader(body)), }, nil })} _, err := ad.Describe(context.Background(), ImageRef{Source: SourceBase64, Data: "aGVsbG8="}, "") if err == nil { t.Fatal("400 却报成功") } if !errors.Is(err, ErrUpstream) { t.Fatalf("err = %v, want ErrUpstream", err) } var logBuf bytes.Buffer slog.New(slog.NewJSONHandler(&logBuf, nil)).Error("vision 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) } } }