107 lines
4.5 KiB
Go
107 lines
4.5 KiB
Go
package logic
|
||
|
||
import (
|
||
"testing"
|
||
|
||
"github.com/gogf/gf/v2/os/gtime"
|
||
|
||
"tool-api/internal/consts"
|
||
"tool-api/internal/model/entity"
|
||
)
|
||
|
||
// ============================================================================
|
||
// 优惠券单测(T04.8 / T06)
|
||
//
|
||
// 覆盖纯函数:券型隔离(展示券不可用于抵扣)、读取态过期判定、已发券快照隔离。
|
||
// 全部不依赖 DB。
|
||
//
|
||
// 注意(T06):expire_at 的「是否过期」已改为 **SQL 侧**判定后以布尔量传入
|
||
// (effectiveCouponStatus / couponUsableErr),故这里直接注入该布尔量,不再依赖 Go 侧时间比较。
|
||
// ============================================================================
|
||
|
||
// TestCouponUsableErr 券型隔离(约定 S7):仅免单券可支付;展示券一律不行(fail-closed)。
|
||
func TestCouponUsableErr(t *testing.T) {
|
||
free := func() *entity.UserCoupons {
|
||
return &entity.UserCoupons{Id: 1, CouponType: consts.CouponTypeFreebie, Status: consts.UserCouponStatusUnused}
|
||
}
|
||
cases := []struct {
|
||
name string
|
||
c *entity.UserCoupons
|
||
expired bool
|
||
wantCode int // 0 = 通过
|
||
}{
|
||
{"免单券未用未过期-可用", free(), false, 0},
|
||
{"nil-不可用", nil, false, consts.CodeCouponUnavailable},
|
||
{"展示券-不可抵扣", &entity.UserCoupons{CouponType: consts.CouponTypeDisplay, Status: consts.UserCouponStatusUnused}, false, consts.CodeCouponUnavailable},
|
||
{"免单券已用", &entity.UserCoupons{CouponType: consts.CouponTypeFreebie, Status: consts.UserCouponStatusUsed}, false, consts.CodeCouponUnavailable},
|
||
{"免单券已作废", &entity.UserCoupons{CouponType: consts.CouponTypeFreebie, Status: consts.UserCouponStatusVoid}, false, consts.CodeCouponUnavailable},
|
||
{"免单券已过期-状态位", &entity.UserCoupons{CouponType: consts.CouponTypeFreebie, Status: consts.UserCouponStatusExpired}, false, consts.CodeCouponUnavailable},
|
||
{"免单券已过期-SQL 侧判定", &entity.UserCoupons{
|
||
CouponType: consts.CouponTypeFreebie, Status: consts.UserCouponStatusUnused,
|
||
ExpireAt: gtime.New("2000-01-01 00:00:00"),
|
||
}, true, consts.CodeCouponUnavailable},
|
||
}
|
||
for _, c := range cases {
|
||
if got := codeOf(couponUsableErr(c.c, c.expired)); got != c.wantCode {
|
||
t.Errorf("%s: code=%d, want %d", c.name, got, c.wantCode)
|
||
}
|
||
}
|
||
}
|
||
|
||
// TestEffectiveCouponStatus 读取态过期判定:未使用且(SQL 侧判定)已过期 → 视为已过期(不落库)。
|
||
func TestEffectiveCouponStatus(t *testing.T) {
|
||
unused := &entity.UserCoupons{Status: consts.UserCouponStatusUnused}
|
||
// 未使用 + 已过期 → 2
|
||
if got := effectiveCouponStatus(unused, true); got != consts.UserCouponStatusExpired {
|
||
t.Errorf("已过期未使用券应判为 2,got %d", got)
|
||
}
|
||
// 未使用 + 未过期 → 0
|
||
if got := effectiveCouponStatus(unused, false); got != consts.UserCouponStatusUnused {
|
||
t.Errorf("未过期未使用券应判为 0,got %d", got)
|
||
}
|
||
// 已使用 → 保持 1(不因为过期改判)
|
||
used := &entity.UserCoupons{Status: consts.UserCouponStatusUsed}
|
||
if got := effectiveCouponStatus(used, true); got != consts.UserCouponStatusUsed {
|
||
t.Errorf("已使用券应保持 1,got %d", got)
|
||
}
|
||
}
|
||
|
||
// TestBuildUserCouponRowSnapshot 快照隔离(约定 S8):
|
||
// 发放时把模板字段快照进 user_coupons;之后模板改名/改型/改面额**不影响已发券**。
|
||
func TestBuildUserCouponRowSnapshot(t *testing.T) {
|
||
now := gtime.Now()
|
||
template := &entity.Coupons{
|
||
Id: 10, Title: "免单券A", CouponType: consts.CouponTypeFreebie,
|
||
Kind: consts.CouponKindFreebie, Value: 500, ThresholdCents: 100,
|
||
ValidTo: gtime.New("2999-01-01 00:00:00"),
|
||
}
|
||
row := buildUserCouponRow(template, 7, "admin", "新客礼", now)
|
||
|
||
// 发放后立刻改动模板
|
||
template.Title = "改名后"
|
||
template.CouponType = consts.CouponTypeDisplay
|
||
template.Value = 9999
|
||
|
||
if row["coupon_title"] != "免单券A" {
|
||
t.Errorf("标题快照应保持不变,got %v", row["coupon_title"])
|
||
}
|
||
if row["coupon_type"] != consts.CouponTypeFreebie {
|
||
t.Errorf("券型快照应保持不变,got %v", row["coupon_type"])
|
||
}
|
||
if row["value"] != 500 {
|
||
t.Errorf("面额快照应保持不变,got %v", row["value"])
|
||
}
|
||
if row["user_id"] != int64(7) {
|
||
t.Errorf("user_id 错误,got %v", row["user_id"])
|
||
}
|
||
if row["status"] != consts.UserCouponStatusUnused {
|
||
t.Errorf("新发券状态应为未使用 0,got %v", row["status"])
|
||
}
|
||
if row["source"] != consts.UserCouponSourceAdmin {
|
||
t.Errorf("来源应为后台发放,got %v", row["source"])
|
||
}
|
||
if row["granted_by"] != "admin" {
|
||
t.Errorf("发放人错误,got %v", row["granted_by"])
|
||
}
|
||
}
|