补充AI下次时间接口异常入参与验证记录
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
# AI 下次抽烟时间验证记录(2026-02-28)
|
||||
|
||||
对应 issue:`#4 [P0][T1] AI 下次抽烟时间端到端验证`
|
||||
|
||||
## 验证结论
|
||||
|
||||
- 关键场景:通过(默认模式、AI 模式、缓存命中/过期判定)。
|
||||
- 边界场景:通过(空节点、非法时间、睡眠区间调整、未来计划日期最小时间)。
|
||||
- 异常场景:通过(非法 `mode` 返回 `400`,错误文案明确)。
|
||||
|
||||
## 覆盖明细
|
||||
|
||||
1. 正常链路
|
||||
- `internal/smoke/service/smoke_ai_next_smoke_service_test.go::TestShouldRefreshCache`
|
||||
- 覆盖当天有效缓存命中。
|
||||
- `internal/smoke/service/smoke_ai_next_smoke_service_test.go::TestNormalizeNodesFiltersInvalidAndDuplicate`
|
||||
- 覆盖节点去重、非当天过滤、睡眠区间过滤。
|
||||
|
||||
2. 边界数据
|
||||
- `internal/smoke/service/smoke_ai_next_smoke_service_test.go::TestComputeMinNotBeforeForFuturePlanUsesWakeUp`
|
||||
- 覆盖明日计划默认 07:00 与按起床时间约束。
|
||||
|
||||
3. 异常输入/兜底
|
||||
- `internal/smoke/handler/smoke_next_handler_test.go::TestResolveNextSmokeMode`
|
||||
- 覆盖 `mode` 非法值校验,保证返回 `400` 而不是隐式降级。
|
||||
|
||||
## 执行命令
|
||||
|
||||
```bash
|
||||
go test ./...
|
||||
```
|
||||
|
||||
## 问题清单
|
||||
|
||||
- 当前未引入真实外部 AI 服务的在线回归(依赖环境密钥与网络),本次以可复现单元测试覆盖核心逻辑路径。
|
||||
@@ -75,7 +75,11 @@ func (h *SmokeHandler) GetNextSmokeTime(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
mode := strings.ToLower(strings.TrimSpace(c.DefaultQuery("mode", "auto")))
|
||||
mode, ok := resolveNextSmokeMode(c.Query("mode"))
|
||||
if !ok {
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "mode 参数错误,应为 auto|ai|default"))
|
||||
return
|
||||
}
|
||||
|
||||
formatPtr := func(t *time.Time) string {
|
||||
if t == nil {
|
||||
@@ -194,6 +198,20 @@ func dateOnlyLocal(t time.Time) time.Time {
|
||||
return time.Date(local.Year(), local.Month(), local.Day(), 0, 0, 0, 0, time.Local)
|
||||
}
|
||||
|
||||
func resolveNextSmokeMode(raw string) (string, bool) {
|
||||
mode := strings.ToLower(strings.TrimSpace(raw))
|
||||
if mode == "" {
|
||||
return "auto", true
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case "auto", "ai", "default":
|
||||
return mode, true
|
||||
default:
|
||||
return "", false
|
||||
}
|
||||
}
|
||||
|
||||
type nextSmokeDefaultResponse struct {
|
||||
LastSmokeAt string `json:"last_smoke_at,omitempty"`
|
||||
NextSmokeAt string `json:"next_smoke_at,omitempty"`
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package handler
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestResolveNextSmokeMode(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
mode string
|
||||
ok bool
|
||||
}{
|
||||
{name: "默认值", input: "", mode: "auto", ok: true},
|
||||
{name: "自动模式", input: "auto", mode: "auto", ok: true},
|
||||
{name: "AI模式", input: "ai", mode: "ai", ok: true},
|
||||
{name: "默认策略模式", input: "default", mode: "default", ok: true},
|
||||
{name: "大小写兼容", input: "AI", mode: "ai", ok: true},
|
||||
{name: "非法值", input: "fast", mode: "", ok: false},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
gotMode, gotOK := resolveNextSmokeMode(tc.input)
|
||||
if gotMode != tc.mode || gotOK != tc.ok {
|
||||
t.Fatalf("resolveNextSmokeMode(%q)=(%q,%v), want=(%q,%v)", tc.input, gotMode, gotOK, tc.mode, tc.ok)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user