107 lines
3.7 KiB
Go
107 lines
3.7 KiB
Go
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("非零时间应视为已删")
|
||
}
|
||
}
|