Files
ttstd-infinite-canvas/backend/internal/app/cloud_agent_user_errors_test.go

81 lines
4.3 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package app
import (
"errors"
"fmt"
"strings"
"testing"
"yingce/backend/internal/model"
"yingce/backend/internal/repository"
)
// 线上真实报错逐条回放:用户只能看到中文、可操作的原因,看不到地址、IP 或 Go 原始报错;
// 同时不能一律变成"生成失败"。
func TestUserFacingTaskErrorMatchesCanvasNodeClassification(t *testing.T) {
for _, tc := range []struct{ name, raw, want string }{
{"connection reset", `Post "https://ai.cangyuansuanli.cn/v1/images/generations": read tcp 192.168.1.7:61229->154.12.46.253:443: read: connection reset by peer`, "网络异常。"},
{"dial", `dial tcp 10.0.0.1:443: connect: connection refused`, "网络异常。"},
{"upstream 500 with reason", "模型服务暂时不可用(HTTP 500);上游:Upstream gateway error", "模型服务暂时不可用(HTTP 500);上游:Upstream gateway error"},
{"bare 502", "HTTP 502 Bad Gateway", "网络异常。"},
{"rate limit", "HTTP 429", "服务当前繁忙,请稍后重试。"},
{"auth", "status 401", "生成服务鉴权失败,请检查渠道配置。"},
{"provider json", `接口请求失败:{"error":{"message":"图片尺寸不支持"}}`, "图片尺寸不支持"},
{"moderation", "sensitive_words_detected", taskErrorModerationMessage},
{"storage", "参考图片上传失败:bucket denied", "参考素材上传到对象存储失败,请检查对象存储配置后重试。"},
{"readable chinese", "模型服务拒绝了请求,请检查模型和参数", "模型服务拒绝了请求,请检查模型和参数"},
{"empty", "", taskErrorDefaultMessage},
} {
t.Run(tc.name, func(t *testing.T) {
if got := userFacingTaskError(tc.raw); got != tc.want {
t.Fatalf("got %q want %q", got, tc.want)
}
})
}
}
func TestUserFacingTaskFailureNamesTheStep(t *testing.T) {
task := &model.Task{Type: "canvas_image", Status: model.TaskStatusFailed, Error: `Post "https://x/v1/images/generations": read tcp 1.2.3.4:1->5.6.7.8:443: read: connection reset by peer`}
if got := userFacingTaskFailure(task); got != "图片生成失败:网络异常。" {
t.Fatalf("got %q", got)
}
step := &model.Task{Type: "canvas_text", Operation: cloudAgentStepOperation, Status: model.TaskStatusFailed, Error: "画布 Agent 接口没有返回内容"}
if got := userFacingTaskFailure(step); got != "模型调用失败:画布 Agent 接口没有返回内容" {
t.Fatalf("got %q", got)
}
}
func TestCloudAgentUserFailureMessageHidesInternalEnglish(t *testing.T) {
internal := fmt.Errorf("load session: %w", errors.New("invalid Agent checkpoint (runtime validation): Agent runtime has multiple active tasks"))
got := cloudAgentUserFailureMessage("run-1", internal)
if strings.ContainsAny(got, "abcdefghijklmnopqrstuvwxyz") && !strings.Contains(got, "run-1") {
t.Fatalf("internal English leaked: %q", got)
}
if strings.Contains(got, "checkpoint") || strings.Contains(got, "multiple active") {
t.Fatalf("internal English leaked: %q", got)
}
readable := fmt.Errorf("wrap: %w", BadAuthRequest("图片生成失败:网络异常。"))
if got := cloudAgentUserFailureMessage("run-1", readable); got != "图片生成失败:网络异常。" {
t.Fatalf("readable reason lost: %q", got)
}
if got := cloudAgentUserFailureMessage("run-1", fmt.Errorf("x: %w", repository.ErrInsufficientCredits)); got != "积分不足,请先充值" {
t.Fatalf("insufficient credits not localized: %q", got)
}
}
// 所有仓储层哨兵错误都必须是中文:它们会经 handler 直接返回给用户。
func TestRepositorySentinelErrorsAreChinese(t *testing.T) {
for _, err := range []error{
repository.ErrCreationConflict, repository.ErrInsufficientCredits, repository.ErrRedeemCodeInvalid,
repository.ErrActiveTaskLimit, repository.ErrTaskNotRetryable, repository.ErrBillingStateConflict,
repository.ErrBillingUsageUnavailable, repository.ErrBillingChargeLimit, repository.ErrChannelModelInUse,
repository.ErrLogicalModelInUse, repository.ErrLogicalModelUnavailable, repository.ErrTaskStateConflict,
repository.ErrCanvasRevisionConflict, repository.ErrDailyUploadLimitExceeded, repository.ErrProjectHasActiveTasks,
repository.ErrPaymentOrderStateConflict, repository.ErrPaymentEvidenceMismatch,
} {
if !cloudAgentReadableMessage(err.Error()) {
t.Errorf("sentinel error is not user-readable Chinese: %q", err.Error())
}
}
}