108 lines
4.0 KiB
Go
108 lines
4.0 KiB
Go
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)
|
||
}
|
||
}
|