9b6dba4c2b
覆盖 NormalizeCode 边界/幂等、GenerateRedeemCode 格式/无歧义字母表 (剔除 0/O/1/I/L)/无碰撞。.gitignore 忽略全局 todo skill 本地数据 与 backend/gencode 构建产物。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Y2Wdwo7SmgBJU37cBrkhPK
95 lines
2.6 KiB
Go
95 lines
2.6 KiB
Go
package util
|
||
|
||
import (
|
||
"strings"
|
||
"testing"
|
||
)
|
||
|
||
func TestNormalizeCode(t *testing.T) {
|
||
cases := []struct {
|
||
name string
|
||
in string
|
||
want string
|
||
}{
|
||
{"展示格式带连字符", "JIUKU-YD9S-7CAP", "JIUKUYD9S7CAP"},
|
||
{"小写转大写", "jiuku-yd9s-7cap", "JIUKUYD9S7CAP"},
|
||
{"去除空格", "JIUKU YD9S 7CAP", "JIUKUYD9S7CAP"},
|
||
{"去除制表/换行/回车", "JIUKU\tYD9S\n7CAP\r", "JIUKUYD9S7CAP"},
|
||
{"已归一化幂等", "JIUKUYD9S7CAP", "JIUKUYD9S7CAP"},
|
||
{"空串", "", ""},
|
||
{"混合大小写与分隔", " jiUKu--yd9s 7caP\n", "JIUKUYD9S7CAP"},
|
||
}
|
||
for _, c := range cases {
|
||
t.Run(c.name, func(t *testing.T) {
|
||
if got := NormalizeCode(c.in); got != c.want {
|
||
t.Fatalf("NormalizeCode(%q) = %q, want %q", c.in, got, c.want)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
func TestNormalizeCodeIdempotent(t *testing.T) {
|
||
// 二次归一化结果不变
|
||
once := NormalizeCode("jiuku-yd9s-7cap")
|
||
if twice := NormalizeCode(once); twice != once {
|
||
t.Fatalf("NormalizeCode 非幂等: %q -> %q", once, twice)
|
||
}
|
||
}
|
||
|
||
func TestGenerateRedeemCodeFormat(t *testing.T) {
|
||
for i := 0; i < 200; i++ {
|
||
code := GenerateRedeemCode()
|
||
|
||
// 展示格式:JIUKU-XXXX-XXXX
|
||
parts := strings.Split(code, "-")
|
||
if len(parts) != 3 {
|
||
t.Fatalf("码段数应为 3,得到 %d:%q", len(parts), code)
|
||
}
|
||
if parts[0] != "JIUKU" {
|
||
t.Fatalf("前缀应为 JIUKU,得到 %q:%q", parts[0], code)
|
||
}
|
||
if len(parts[1]) != 4 || len(parts[2]) != 4 {
|
||
t.Fatalf("随机段长度应各为 4,得到 %d/%d:%q", len(parts[1]), len(parts[2]), code)
|
||
}
|
||
|
||
// 归一化后 = 前缀 + 8 位随机
|
||
norm := NormalizeCode(code)
|
||
if len(norm) != len("JIUKU")+8 {
|
||
t.Fatalf("归一化长度应为 %d,得到 %d:%q", len("JIUKU")+8, len(norm), norm)
|
||
}
|
||
|
||
// 随机段只含无歧义字母表字符(不含 0/O/1/I/L)
|
||
random := parts[1] + parts[2]
|
||
for _, r := range random {
|
||
if !strings.ContainsRune(codeAlphabet, r) {
|
||
t.Fatalf("出现字母表外字符 %q:%q", r, code)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
func TestCodeAlphabetExcludesAmbiguous(t *testing.T) {
|
||
for _, bad := range []rune{'0', 'O', '1', 'I', 'L'} {
|
||
if strings.ContainsRune(codeAlphabet, bad) {
|
||
t.Fatalf("字母表不应包含易混字符 %q", bad)
|
||
}
|
||
}
|
||
}
|
||
|
||
func TestGenerateRedeemCodeUnique(t *testing.T) {
|
||
// 不要求绝对唯一,但大量生成应几乎无碰撞(31^8 空间)
|
||
const n = 1000
|
||
seen := make(map[string]struct{}, n)
|
||
dup := 0
|
||
for i := 0; i < n; i++ {
|
||
c := NormalizeCode(GenerateRedeemCode())
|
||
if _, ok := seen[c]; ok {
|
||
dup++
|
||
}
|
||
seen[c] = struct{}{}
|
||
}
|
||
if dup > 0 {
|
||
t.Fatalf("%d 次生成出现 %d 次重复,随机性可疑", n, dup)
|
||
}
|
||
}
|