refactor(*): 稳定期治理 - 收敛核心架构、错误语义并修复跨项目数据竞态
This commit is contained in:
@@ -14,32 +14,42 @@ import (
|
||||
)
|
||||
|
||||
type failureEnvelope struct {
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Reason string `json:"reason"`
|
||||
}
|
||||
|
||||
func TestFailServiceRegistrationCooldown(t *testing.T) {
|
||||
recorder, context := responseTestContext()
|
||||
failService(context, &service.EmailCodeCooldownError{Seconds: 47})
|
||||
response := decodeFailureEnvelope(t, recorder)
|
||||
if recorder.Code != http.StatusTooManyRequests || response.Code != 429 || recorder.Header().Get("Retry-After") != "47" || !strings.Contains(response.Msg, "47") {
|
||||
if recorder.Code != http.StatusTooManyRequests || response.Code != service.CodeRateLimited || response.Reason != string(service.ReasonRateLimited) || recorder.Header().Get("Retry-After") != "47" || !strings.Contains(response.Msg, "47") {
|
||||
t.Fatalf("cooldown response: status=%d header=%s body=%#v", recorder.Code, recorder.Header().Get("Retry-After"), response)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFailServiceProjectsAppError(t *testing.T) {
|
||||
recorder, context := responseTestContext()
|
||||
err := service.NewAppError(http.StatusTooManyRequests, "请求过于频繁,请稍后重试")
|
||||
err.Code = 42901
|
||||
err := service.RateLimited("请求过于频繁,请稍后重试")
|
||||
|
||||
failService(context, err)
|
||||
|
||||
response := decodeFailureEnvelope(t, recorder)
|
||||
if recorder.Code != http.StatusTooManyRequests || response.Code != 42901 || response.Msg != err.Message {
|
||||
if recorder.Code != http.StatusTooManyRequests || response.Code != service.CodeRateLimited || response.Reason != string(service.ReasonRateLimited) || response.Msg != err.Message {
|
||||
t.Fatalf("response = status %d, body %#v", recorder.Code, response)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFailServiceQuotaExceeded(t *testing.T) {
|
||||
recorder, context := responseTestContext()
|
||||
failService(context, service.QuotaExceeded("账号素材数量已达到 100 个上限"))
|
||||
|
||||
response := decodeFailureEnvelope(t, recorder)
|
||||
if recorder.Code != http.StatusForbidden || response.Code != service.CodeQuotaExceeded || response.Reason != string(service.ReasonQuotaExceeded) {
|
||||
t.Fatalf("quota response = status %d, body %#v", recorder.Code, response)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFailServiceHidesUnclassifiedInternalError(t *testing.T) {
|
||||
recorder, context := responseTestContext()
|
||||
failService(context, errors.New("database password=secret"))
|
||||
@@ -66,6 +76,40 @@ func TestFailInternalKeepsStatusWithoutLeakingCause(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestParsePaginationQueryUsesDefaultsAndRejectsInvalidValues(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
tests := []struct {
|
||||
name string
|
||||
query string
|
||||
page int
|
||||
pageSize int
|
||||
wantError bool
|
||||
}{
|
||||
{name: "defaults", query: "", page: 1, pageSize: 40},
|
||||
{name: "explicit values", query: "page=3&pageSize=25", page: 3, pageSize: 25},
|
||||
{name: "invalid page", query: "page=abc", wantError: true},
|
||||
{name: "zero page", query: "page=0", wantError: true},
|
||||
{name: "negative page size", query: "pageSize=-1", wantError: true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
request := httptest.NewRequest(http.MethodGet, "/api/test?"+tt.query, nil)
|
||||
context, _ := gin.CreateTestContext(httptest.NewRecorder())
|
||||
context.Request = request
|
||||
page, pageSize, err := parsePaginationQuery(context, 40)
|
||||
if (err != nil) != tt.wantError {
|
||||
t.Fatalf("error = %v, wantError = %t", err, tt.wantError)
|
||||
}
|
||||
if tt.wantError {
|
||||
return
|
||||
}
|
||||
if page != tt.page || pageSize != tt.pageSize {
|
||||
t.Fatalf("pagination = (%d, %d), want (%d, %d)", page, pageSize, tt.page, tt.pageSize)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func responseTestContext() (*httptest.ResponseRecorder, *gin.Context) {
|
||||
recorder := httptest.NewRecorder()
|
||||
context, _ := gin.CreateTestContext(recorder)
|
||||
|
||||
Reference in New Issue
Block a user