package errors import ( "errors" "net/http" "testing" "github.com/stretchr/testify/assert" ) func TestNewAppError(t *testing.T) { err := NewAppError("test_code", "测试消息", http.StatusBadRequest) assert.Equal(t, "test_code", err.Code) assert.Equal(t, "测试消息", err.Message) assert.Equal(t, http.StatusBadRequest, err.HTTPStatus) assert.Nil(t, err.Cause) assert.Nil(t, err.Context) } func TestAppError_Error(t *testing.T) { tests := []struct { name string err *AppError expected string }{ { name: "无原因错误", err: NewAppError("code1", "消息1", 400), expected: "code1: 消息1", }, { name: "带原因错误", err: Wrap(NewAppError("code2", "消息2", 500), errors.New("原始错误")), expected: "code2: 消息2 (原始错误)", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { assert.Equal(t, tt.expected, tt.err.Error()) }) } } func TestAppError_Unwrap(t *testing.T) { cause := errors.New("原始错误") err := Wrap(ErrInternal, cause) assert.Equal(t, cause, err.Unwrap()) } func TestWrap(t *testing.T) { cause := errors.New("网络超时") wrapped := Wrap(ErrInternal, cause) assert.Equal(t, "internal_error", wrapped.Code) assert.Equal(t, "内部错误", wrapped.Message) assert.Equal(t, http.StatusInternalServerError, wrapped.HTTPStatus) assert.Equal(t, cause, wrapped.Cause) } func TestWithContext(t *testing.T) { err := WithContext(ErrModelNotFound, "model", "gpt-4") assert.Equal(t, "model_not_found", err.Code) assert.NotNil(t, err.Context) assert.Equal(t, "gpt-4", err.Context["model"]) // 测试链式添加上下文 err2 := WithContext(err, "provider", "openai") assert.Equal(t, "gpt-4", err2.Context["model"]) assert.Equal(t, "openai", err2.Context["provider"]) } func TestWithMessage(t *testing.T) { err := WithMessage(ErrInvalidRequest, "自定义错误消息") assert.Equal(t, "invalid_request", err.Code) assert.Equal(t, "自定义错误消息", err.Message) assert.Equal(t, http.StatusBadRequest, err.HTTPStatus) } func TestPredefinedErrors(t *testing.T) { tests := []struct { name string err *AppError code string httpStatus int }{ {"ErrModelNotFound", ErrModelNotFound, "model_not_found", http.StatusNotFound}, {"ErrModelDisabled", ErrModelDisabled, "model_disabled", http.StatusNotFound}, {"ErrProviderNotFound", ErrProviderNotFound, "provider_not_found", http.StatusNotFound}, {"ErrProviderDisabled", ErrProviderDisabled, "provider_disabled", http.StatusNotFound}, {"ErrInvalidRequest", ErrInvalidRequest, "invalid_request", http.StatusBadRequest}, {"ErrInternal", ErrInternal, "internal_error", http.StatusInternalServerError}, {"ErrDatabaseNotInit", ErrDatabaseNotInit, "database_not_initialized", http.StatusInternalServerError}, {"ErrConflict", ErrConflict, "conflict", http.StatusConflict}, {"ErrRequestCreate", ErrRequestCreate, "request_create_error", http.StatusInternalServerError}, {"ErrRequestSend", ErrRequestSend, "request_send_error", http.StatusBadGateway}, {"ErrResponseRead", ErrResponseRead, "response_read_error", http.StatusBadGateway}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { assert.Equal(t, tt.code, tt.err.Code) assert.Equal(t, tt.httpStatus, tt.err.HTTPStatus) }) } } func TestAsAppError(t *testing.T) { t.Run("nil输入", func(t *testing.T) { _, ok := AsAppError(nil) assert.False(t, ok) }) t.Run("AppError类型", func(t *testing.T) { appErr, ok := AsAppError(ErrModelNotFound) assert.True(t, ok) assert.Equal(t, ErrModelNotFound, appErr) }) t.Run("Wrapped AppError", func(t *testing.T) { wrapped := Wrap(ErrInternal, errors.New("cause")) appErr, ok := AsAppError(wrapped) assert.True(t, ok) assert.Equal(t, "internal_error", appErr.Code) }) t.Run("非AppError类型", func(t *testing.T) { _, ok := AsAppError(errors.New("普通错误")) assert.False(t, ok) }) } func TestWithCause(t *testing.T) { cause := errors.New("连接超时") err := ErrRequestSend.WithCause(cause) assert.Equal(t, "request_send_error", err.Code) assert.Equal(t, http.StatusBadGateway, err.HTTPStatus) assert.Equal(t, cause, err.Cause) assert.True(t, errors.Is(err, cause)) var appErr *AppError assert.True(t, errors.As(err, &appErr)) assert.Equal(t, "request_send_error", appErr.Code) }