Files
qitongxue-api/internal/logic/note_test.go

108 lines
4.0 KiB
Go
Raw Normal View History

2026-09-29 10:57:01 +08:00
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)
}
}