新增测试代码规范、修复响应体断言缺失与魔法数字

- AGENTS.md 新增测试代码专项规范:常量、fixture、触发器、HTTP断言、层边界说明
- TestUpdateAllocationExpiryBaseHTTP 补全 PATCH 响应体字段断言
- 四个集成测试文件以 testIDMask 局部常量替换 0x7fffffff 魔法数字

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-07-23 11:35:38 +09:00
parent 4aaae07cee
commit d5a35f6360
5 changed files with 83 additions and 9 deletions

View File

@@ -1,5 +1,8 @@
package admin
// testIDMask 用于将纳秒时间戳截断为合法的 uint 主键范围,仅限测试用。
const testIDMask = 0x7fffffff
import (
"bytes"
"context"
@@ -94,7 +97,34 @@ func TestUpdateAllocationExpiryBaseHTTP(t *testing.T) {
app := fixture.newApp(constants.UserTypeSuperAdmin, 0)
path := "/api/admin/shop-package-allocations/" + strconv.FormatUint(uint64(allocation.ID), 10) + "/expiry-base"
for _, body := range []string{`{"expiry_base_override":"from_activation"}`, `{"expiry_base_override":"from_activation"}`, `{"expiry_base_override":null}`} {
// 首次 PATCH 验证响应体包含 spec 要求的所有生效条件字段
{
status, respBody := expiryBaseHTTPRequest(t, app, http.MethodPatch, path, `{"expiry_base_override":"from_activation"}`)
if status != http.StatusOK {
t.Fatalf("首次 PATCH 失败status=%d body=%s", status, respBody)
}
var resp struct {
Data struct {
DefaultExpiryBase string `json:"default_expiry_base"`
DefaultExpiryBaseName string `json:"default_expiry_base_name"`
ExpiryBaseOverride *string `json:"expiry_base_override"`
ExpiryBaseOverrideName string `json:"expiry_base_override_name"`
EffectiveExpiryBase string `json:"effective_expiry_base"`
EffectiveExpiryBaseName string `json:"effective_expiry_base_name"`
} `json:"data"`
}
if err := sonic.Unmarshal(respBody, &resp); err != nil {
t.Fatalf("解析 PATCH 响应失败:%v", err)
}
d := resp.Data
if d.DefaultExpiryBase == "" || d.DefaultExpiryBaseName == "" ||
d.ExpiryBaseOverride == nil || d.ExpiryBaseOverrideName == "" ||
d.EffectiveExpiryBase == "" || d.EffectiveExpiryBaseName == "" {
t.Fatalf("PATCH 响应缺少 spec 要求的生效条件字段:%+v", d)
}
}
// 幂等 + 恢复默认
for _, body := range []string{`{"expiry_base_override":"from_activation"}`, `{"expiry_base_override":null}`} {
status, responseBody := expiryBaseHTTPRequest(t, app, http.MethodPatch, path, body)
if status != http.StatusOK {
t.Fatalf("修改覆盖值失败status=%d body=%s", status, responseBody)
@@ -200,7 +230,7 @@ func expiryBaseHTTPRequest(t *testing.T, app *fiber.App, method, path, body stri
}
func completeExpiryBaseTestUsage(packageID uint) *model.PackageUsage {
unique := uint(time.Now().UnixNano() & 0x7fffffff)
unique := uint(time.Now().UnixNano() & testIDMask)
return &model.PackageUsage{OrderID: unique, OrderNo: "UR55-PATCH-USAGE", PackageID: packageID, UsageType: constants.AssetWalletResourceTypeIotCard, IotCardID: unique, DataLimitMB: 1, Status: constants.PackageUsageStatusPending, Priority: 1, PackageName: "UR55测试套餐", Generation: 1, ExpiryBaseSnapshot: constants.PackageExpiryBaseFromPurchase, CalendarTypeSnapshot: constants.PackageCalendarTypeByDay, DurationDaysSnapshot: 30}
}