package logic import ( "testing" "time" "github.com/gogf/gf/v2/os/gctx" "github.com/gogf/gf/v2/os/gtime" "tool-api/internal/consts" "tool-api/internal/model/entity" ) // TestEvaluateNoteQuota 覆盖额度边界: // - 免费未满(49) → 允许新建 // - 免费刚好满(50) → 禁止新建(对应「第 51 条返回 4003」的前置条件) // - 会员过期回落且活跃数超限(51) → 仅禁止新建,不删数据 // - 会员(不限量) → 允许新建 // // 该函数是 SaveNote/RestoreNote 真实调用的额度判定,非复述逻辑。 func TestEvaluateNoteQuota(t *testing.T) { cases := []struct { name string activeCount int isMember bool wantLimit int wantUnlimit bool wantCreate bool }{ {"免费_未满", 49, false, consts.NoteFreeLimit, false, true}, {"免费_刚好满", 50, false, consts.NoteFreeLimit, false, false}, {"会员过期回落_超限仅禁新建", 51, false, consts.NoteFreeLimit, false, false}, {"会员_超限仍不限", 51, true, 0, true, true}, {"会员_零条", 0, true, 0, true, true}, } for _, c := range cases { limit, unlimited, canCreate := evaluateNoteQuota(c.activeCount, c.isMember) if limit != c.wantLimit || unlimited != c.wantUnlimit || canCreate != c.wantCreate { t.Fatalf("%s: got(limit=%d, unlimited=%v, canCreate=%v) want(%d, %v, %v)", c.name, limit, unlimited, canCreate, c.wantLimit, c.wantUnlimit, c.wantCreate) } } } // TestEffectiveLevelKeyMemberFallback 证明会员判定确实走 EffectiveLevelKey, // 且会员到期后回落默认等级(保证「会员过期即回落免费额度」)。 func TestEffectiveLevelKeyMemberFallback(t *testing.T) { if got := EffectiveLevelKey(nil); got != consts.DefaultLevelKey { t.Fatalf("nil user: got %s want %s", got, consts.DefaultLevelKey) } // 永久会员(level_expire_at 为空) if got := EffectiveLevelKey(&entity.Users{LevelKey: "v2"}); got != "v2" { t.Fatalf("永久会员: got %s want v2", got) } // 未过期会员 if got := EffectiveLevelKey(&entity.Users{LevelKey: "v2", LevelExpireAt: gtime.Now().Add(time.Hour)}); got != "v2" { t.Fatalf("未过期会员: got %s want v2", got) } // 已过期会员 → 回落默认(免费)等级 if got := EffectiveLevelKey(&entity.Users{LevelKey: "v2", LevelExpireAt: gtime.Now().Add(-time.Hour)}); got != consts.DefaultLevelKey { t.Fatalf("已过期会员: got %s want %s", got, consts.DefaultLevelKey) } } // TestSendNoteRemindWithEmptyTemplateDegrades 覆盖「模板未配置时接口正常、无 panic」: // 直接调用真实发送函数并传入空模板 ID,断言不 panic 且返回 nil(降级)。 func TestSendNoteRemindWithEmptyTemplateDegrades(t *testing.T) { defer func() { if r := recover(); r != nil { t.Fatalf("模板未配置时不应 panic,但发生了 panic: %v", r) } }() ctx := gctx.New() err := sendNoteRemindWithTemplate(ctx, NoteRemindSendReq{ NoteId: 1, Openid: "o_test_openid", Title: "会议要点", Content: "待办清单:a、b、c", Time: "2026-09-20 09:00:00", }, "") if err != nil { t.Fatalf("模板未配置时应降级返回 nil,但返回 err=%v", err) } } // TestShouldClaimRemind 证明「同一提醒只发一次」的条件更新语义: // 只有当抢占了 remind_status=1→2 的那一次(rowsAffected==1)才继续发送。 func TestShouldClaimRemind(t *testing.T) { if !shouldClaimRemind(1) { t.Fatalf("rowsAffected=1 时应继续发送") } if shouldClaimRemind(0) { t.Fatalf("rowsAffected=0 时应跳过(已被其它实例/轮次抢占)") } } // TestJoinAndSplitTags 覆盖标签规整(去空白/去空项/去重)与回拆分。 func TestJoinAndSplitTags(t *testing.T) { csv := joinNoteTags([]string{" 工作 ", "", "待办", "工作"}) if csv != "工作,待办" { t.Fatalf("joinNoteTags got %q want %q", csv, "工作,待办") } tags := splitNoteTags(csv) if len(tags) != 2 || tags[0] != "工作" || tags[1] != "待办" { t.Fatalf("splitNoteTags got %v want [工作 待办]", tags) } }