package id import ( "strings" "testing" "time" ) // TestNewCanonicalUserID 验证 ULID 生成基本性质(26 位、Crockford、可解码)。 func TestNewCanonicalUserID(t *testing.T) { uid, err := NewCanonicalUserID() if err != nil { t.Fatalf("NewCanonicalUserID 失败: %v", err) } if len(uid) != Length { t.Fatalf("ULID 长度应为 %d,得 %d(%q)", Length, len(uid), uid) } if !Validate(uid) { t.Fatalf("生成的 ULID 应通过 Validate:%q", uid) } } // TestNewCanonicalUserIDAt 验证指定时间戳 + 时间有序性(前 48bit 毫秒时间戳)。 func TestNewCanonicalUserIDAt(t *testing.T) { base := time.Date(2026, 11, 7, 10, 0, 0, 0, time.UTC) uidA, err := NewCanonicalUserIDAt(base) if err != nil { t.Fatalf("NewCanonicalUserIDAt 失败: %v", err) } uidB, err := NewCanonicalUserIDAt(base.Add(2 * time.Second)) if err != nil { t.Fatalf("NewCanonicalUserIDAt(+2s) 失败: %v", err) } // 时间戳后移 → ULID 字典序应更大(前 48bit 时间戳递增) if !(uidA < uidB) { t.Errorf("时间有序的 ULID 应满足 uidA(%s) < uidB(%s)", uidA, uidB) } } // TestValidate 校验合法/非法 ULID。 func TestValidate(t *testing.T) { uid, _ := NewCanonicalUserID() cases := []struct { in string want bool }{ {uid, true}, // 合法(新生成) {strings.ToLower(uid), false}, // 小写非法(统一大写) {"01GAQF0Z6C1V9Y3E5H7K2M8B4T", true}, // 标准样例 {"abc", false}, // 长度不符 {"", false}, // 空 {"01GAQF0Z6C1V9Y3E5H7K2M8B4I", false}, // 含 I(Crockford 剔除) {"01GAQF0Z6C1V9Y3E5H7K2M8B4L", false}, // 含 L {"01GAQF0Z6C1V9Y3E5H7K2M8B4O", false}, // 含 O {"01GAQF0Z6C1V9Y3E5H7K2M8B4U", false}, // 含 U {"01GAQF0Z6C1V9Y3E5H7K2M8B4T0", false}, // 27 位(长度) } for i, c := range cases { if got := Validate(c.in); got != c.want { t.Errorf("case %d: Validate(%q)=%v, want %v", i, c.in, got, c.want) } } } // TestNewCanonicalUserIDUniqueness 验证并发/批量生成的唯一性(80bit 随机熵)。 func TestNewCanonicalUserIDUniqueness(t *testing.T) { seen := make(map[string]struct{}, 1000) // 同一毫秒内批量生成(随机部分保证唯一) fixed := time.Date(2026, 11, 7, 8, 0, 0, 0, time.UTC) for i := 0; i < 1000; i++ { uid, err := NewCanonicalUserIDAt(fixed) if err != nil { t.Fatalf("第 %d 次生成失败: %v", i, err) } if _, dup := seen[uid]; dup { t.Fatalf("第 %d 次生成重复 ULID:%s(随机熵不足)", i, uid) } seen[uid] = struct{}{} } }