Files
backend_v2/internal/pkg/hashid/hashid_test.go
toom1996 7dd3fda73c update
2026-09-16 00:56:15 +08:00

112 lines
3.6 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 hashid
import "testing"
func TestRoundtrip(t *testing.T) {
// 覆盖边界与大量随机值,验证 Decode(Encode(n)) == n 严格成立
cases := []uint32{0, 1, 2, 3, 7, 15, 16, 255, 256, 4095, 4096, 65535, 65536, 1 << 31, (1 << 32) - 1}
for _, n := range cases {
got, err := Decode(Encode(n))
if err != nil {
t.Fatalf("Decode(Encode(%d)) 返回错误: %v", n, err)
}
if got != n {
t.Fatalf("往返不一致: 输入 %d, 得到 %d", n, got)
}
}
// 随机大批量
for n := uint32(1); n < 5000; n++ {
got, err := Decode(Encode(n))
if err != nil || got != n {
t.Fatalf("往返不一致: 输入 %d, 得到 %d (err=%v)", n, got, err)
}
}
}
func TestNonSequential(t *testing.T) {
Init("test-salt")
a, b, c := Encode(100), Encode(101), Encode(102)
if a == b || b == c || a == c {
t.Fatalf("相邻 id 编码结果出现了相等: %s %s %s", a, b, c)
}
}
func TestInvalid(t *testing.T) {
if _, err := Decode(""); err == nil {
t.Fatal("空串应返回错误")
}
if _, err := Decode("!!!"); err == nil {
t.Fatal("含非法字符应返回错误")
}
}
// TestTyped 验证带类型前缀的编解码:同数字不同前缀得到不同串,且能正确还原类型与数字主键。
func TestTyped(t *testing.T) {
Init("test-salt")
r := EncodeTyped(TypeRunway, 5)
s := EncodeTyped(TypeSnap, 5)
if r == s {
t.Fatalf("同数字不同前缀应得到不同串: runway=%q snap=%q", r, s)
}
if r[0] != TypeRunway || s[0] != TypeSnap {
t.Fatalf("前缀字符不正确: runway=%q snap=%q", r, s)
}
tr, nr, err := DecodeTyped(r)
if err != nil || tr != TypeRunway || nr != 5 {
t.Fatalf("DecodeTyped(runway) 失败: type=%c num=%d err=%v", tr, nr, err)
}
ts, ns, err := DecodeTyped(s)
if err != nil || ts != TypeSnap || ns != 5 {
t.Fatalf("DecodeTyped(snap) 失败: type=%c num=%d err=%v", ts, ns, err)
}
// 无前缀旧串仍可用 Decode 还原(兼容历史链接)
old := Encode(5)
if no, err := Decode(old); err != nil || no != 5 {
t.Fatalf("旧无前缀串解码失败: num=%d err=%v", no, err)
}
// 长度不足 2 视为非法
if _, _, err := DecodeTyped("r"); err == nil {
t.Fatal("长度不足 2 的 typed 串应返回错误")
}
}
// TestTypedWithType 验证新编码(类型进密码、无前缀):
// - 往返严格成立 DecodeWithType(EncodeWithType(n, typ), typ) == n;
// - 同数字不同类别得到完全不相关的串(runway#5 ≠ snap#5),且串本身不含类型字母;
// - 用错误 typ 解码得到错误主键(≠ n),从而查库自然 404。
func TestTypedWithType(t *testing.T) {
Init("test-salt")
// 往返
for _, typ := range []byte{TypeRunway, TypeSnap, TypeRunwayImage, TypeSnapImage} {
for _, n := range []uint32{0, 1, 5, 999, 1 << 20, (1 << 32) - 1} {
got, err := DecodeWithType(EncodeWithType(n, typ), typ)
if err != nil || got != n {
t.Fatalf("往返不一致: typ=%c n=%d got=%d err=%v", typ, n, got, err)
}
}
}
// 同数字不同类别:串不同且不相关,且都不含类型字母
r := EncodeWithType(5, TypeRunway)
s := EncodeWithType(5, TypeSnap)
if r == s {
t.Fatalf("同数字不同类别应得到不同串: runway=%q snap=%q", r, s)
}
if r[0] == TypeRunway || r[0] == TypeSnap || s[0] == TypeRunway || s[0] == TypeSnap {
t.Fatalf("新编码不应含类型字母前缀: runway=%q snap=%q", r, s)
}
// 用错误类别解码得不到原主键
if wrong, err := DecodeWithType(r, TypeSnap); err != nil || wrong == 5 {
t.Fatalf("用错误类别解码应得不到原主键: got=%d err=%v", wrong, err)
}
// 空串报错
if _, err := DecodeWithType("", TypeRunway); err == nil {
t.Fatal("空串应返回错误")
}
}