Files
qitongxue-api/internal/logic/softdelete_test.go
2026-09-29 10:57:01 +08:00

107 lines
3.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package logic
import (
"strings"
"testing"
"github.com/gogf/gf/v2/os/gtime"
)
// TestTombstoneValue 墓碑值由主键派生(前缀 + id):不同 id 必然不同、且以墓碑前缀开头,
// 保证与真实值空间不相交(真实 openid/手机号不会以 "#" 开头)。
func TestTombstoneValue(t *testing.T) {
if got := tombstoneShort(7); got != "#del7" {
t.Fatalf("tombstoneShort(7)=%q want %q", got, "#del7")
}
if tombstoneShort(1) == tombstoneShort(2) {
t.Fatal("不同 id 的墓碑值必须不同")
}
if !strings.HasPrefix(tombstoneShort(123), tombstonePrefix) {
t.Fatal("墓碑值必须以墓碑前缀开头")
}
if !strings.HasPrefix(tombstoneLong(123), tombstonePrefix) {
t.Fatal("长墓碑值必须以墓碑前缀开头")
}
if tombstoneLong(1) == tombstoneLong(2) {
t.Fatal("不同 id 的长墓碑值必须不同")
}
}
// TestTombstoneLong_Length 长墓碑长度必须 > 32(用户名空间外)且 <= 64(放得进 varchar(64))。
func TestTombstoneLong_Length(t *testing.T) {
for _, id := range []int64{1, 5, 999, 9223372036854775} {
n := len(tombstoneLong(id))
if n <= 32 {
t.Fatalf("tombstoneLong(%d) 长度=%d,必须 >32 否则可被抢注", id, n)
}
if n > 64 {
t.Fatalf("tombstoneLong(%d) 长度=%d,必须 <=64 否则写不进 varchar(64)", id, n)
}
}
}
// TestTombstoneForColumn username 走长墓碑(可被抢注列),phone / openid 走短墓碑。
func TestTombstoneForColumn(t *testing.T) {
if got := tombstoneForColumn("username", 5); got != tombstoneLong(5) {
t.Fatalf("username 应使用长墓碑,got %q", got)
}
if got := tombstoneForColumn("phone", 5); got != tombstoneShort(5) {
t.Fatalf("phone 应使用短墓碑,got %q", got)
}
if got := tombstoneForColumn("openid", 5); got != tombstoneShort(5) {
t.Fatalf("openid 应使用短墓碑,got %q", got)
}
}
// TestTombstoneLong_NotSquattable 抢注通道关闭证明:任何**合法**用户名(长度 3-32,
// 即 username 校验 required|length:3,32 允许的范围)都不可能等于任何长墓碑值。
func TestTombstoneLong_NotSquattable(t *testing.T) {
longs := map[string]bool{}
for _, id := range []int64{1, 5, 42, 999, 9223372036854775} {
longs[tombstoneLong(id)] = true
}
// 枚举长度 3-32 的一组候选名,其中尤其包含「短墓碑名」——短墓碑名长度=len("#del5")=5,在合法区间内,
// 但长墓碑长度 >32,绝不可能命中。
candidates := []string{"abc", "abcde", "#del5", "#del42", "#del999", strings.Repeat("a", 32)}
for _, name := range candidates {
if len(name) < 3 || len(name) > 32 {
t.Fatalf("候选名 %q 不在合法区间 3-32", name)
}
if longs[name] {
t.Fatalf("合法用户名 %q 命中长墓碑,抢注通道未关闭", name)
}
}
}
// TestDeletedFromJSON 原值快照:空 map / nil → nil(写 NULL);非空 → JSON 字符串且含原值。
func TestDeletedFromJSON(t *testing.T) {
if v := deletedFromJSON(map[string]string{}); v != nil {
t.Fatalf("空 map 应返回 nil,got %v", v)
}
if v := deletedFromJSON(nil); v != nil {
t.Fatalf("nil 应返回 nil,got %v", v)
}
v := deletedFromJSON(map[string]string{"phone": "13800000000"})
s, ok := v.(string)
if !ok {
t.Fatalf("非空应返回 string,got %T", v)
}
if !strings.Contains(s, "13800000000") || !strings.Contains(s, "phone") {
t.Fatalf("JSON 应包含原值,got %s", s)
}
}
// TestAlreadySoftDeleted 幂等跳过判定:NULL / 零值时间视为未删;非零时间视为已删。
func TestAlreadySoftDeleted(t *testing.T) {
if alreadySoftDeleted(nil) {
t.Fatal("nil 应视为未删")
}
var zero gtime.Time
if alreadySoftDeleted(&zero) {
t.Fatal("零值时间应视为未删")
}
if !alreadySoftDeleted(gtime.Now()) {
t.Fatal("非零时间应视为已删")
}
}