id_test.go
1package id
2
3import (
4 "encoding/json"
5 "testing"
6 "time"
7
8 "github.com/stretchr/testify/assert"
9)
10
11func TestGeneratorConfig(t *testing.T) {
12 g, err := NewGenerator()
13 assert.NoError(t, err)
14 assert.Equal(t, uint8(10), g.nodeBits)
15 assert.Equal(t, uint8(0), g.randBits)
16 assert.NotNil(t, g.epoch)
17
18 // small number of nodes yields some random bits
19 g, err = NewGenerator(WithNodeNumber(0, 4))
20 assert.NoError(t, err)
21 assert.Equal(t, uint8(2), g.nodeBits)
22 assert.Equal(t, uint8(8), g.randBits)
23 assert.NotNil(t, g.epoch)
24
25 // total nodes exceeds 10-bit range
26 g, err = NewGenerator(WithNodeNumber(0, 4096))
27 assert.Error(t, err)
28
29 // future start time
30 g, err = NewGenerator(WithStartTime(time.Now().Add(1 * time.Minute)))
31 assert.Error(t, err)
32
33 g, err = NewGenerator(WithNodeNumber(4, 4))
34 assert.Error(t, err)
35 assert.Equal(t, "error configuring ID generator: nodes must be numbered from 0...3 for 4 total: got 4", err.Error())
36}
37
38func TestGen(t *testing.T) {
39 g, _ := NewGenerator()
40
41 now := time.Date(2025, time.December, 12, 0, 0, 0, 1, time.UTC)
42 id := g.new(now.Sub(g.epoch).Milliseconds())
43 assert.Equal(t, Key(131184407347200000), id)
44 assert.Equal(t, "131184407347200000", id.String())
45 assert.Equal(t, "zvozorhhxc0", id.Compact())
46}
47
48func TestUnique(t *testing.T) {
49 var x, y Key
50 g, _ := NewGenerator()
51 for i := 0; i < 1000000; i++ {
52 y = g.New()
53 if x == y {
54 t.Errorf("x(%d) & y(%d) are the same", x, y)
55 }
56 x = y
57 }
58}
59
60func TestRandBits(t *testing.T) {
61 a := getRandBits(8, 2)
62 assert.GreaterOrEqual(t, a, int64(0))
63}
64
65func TestRandomGen(t *testing.T) {
66 g, err := NewGenerator(WithNodeNumber(0, 4))
67 assert.NoError(t, err)
68 assert.Equal(t, uint8(8), g.randBits)
69
70 now := time.Date(2025, time.December, 12, 0, 0, 0, 0, time.UTC)
71 id := g.new(now.Sub(g.epoch).Milliseconds())
72 id2 := g.new(now.Sub(g.epoch).Milliseconds())
73 assert.NotEqual(t, id, id2)
74 assert.Equal(t, now, id.Time(g))
75 assert.Equal(t, now, id2.Time(g))
76}
77
78func TestUniqueRandom(t *testing.T) {
79 var x, y Key
80 g, _ := NewGenerator(WithNodeNumber(0, 4))
81 for i := 0; i < 1000000; i++ {
82 y = g.New()
83 if x == y {
84 t.Errorf("x(%d) & y(%d) are the same", x, y)
85 }
86 x = y
87 }
88}
89
90func TestCompactDefault(t *testing.T) {
91 for i := 0; i < 1000; i++ {
92 // set a new default generator
93 g, _ := NewGenerator(WithNodeNumber(0, 2))
94 SetDefault(g)
95 assert.Equal(t, g, Default())
96
97 id := New()
98 id2, err := FromCompact(id.Compact())
99 assert.NoError(t, err)
100 assert.Equal(t, id, id2)
101 }
102}
103
104func TestMarshalJSON(t *testing.T) {
105 g, _ := NewGenerator()
106 id := g.new(0)
107
108 b, err := json.Marshal(id)
109 assert.NoError(t, err)
110 assert.Equal(t, `"`+id.Compact()+`"`, string(b))
111}
112
113func TestUnmarshalJSON(t *testing.T) {
114 g, _ := NewGenerator()
115 id := g.new(0)
116
117 var id2 Key
118 err := json.Unmarshal([]byte(`"`+id.Compact()+`"`), &id2)
119 assert.NoError(t, err)
120 assert.Equal(t, id, id2)
121}
122
123func TestUnmarshalJSON_Invalid(t *testing.T) {
124 var id Key
125 err := json.Unmarshal([]byte(`"not-base36!"`), &id)
126 assert.Error(t, err)
127}
128
129func TestMarshalJSON_StructField(t *testing.T) {
130 g, _ := NewGenerator()
131 id := g.new(0)
132
133 type S struct {
134 ID Key `json:"id"`
135 }
136 s := S{ID: id}
137 b, err := json.Marshal(s)
138 assert.NoError(t, err)
139 assert.Equal(t, `{"id":"`+id.Compact()+`"}`, string(b))
140
141 var s2 S
142 err = json.Unmarshal(b, &s2)
143 assert.NoError(t, err)
144 assert.Equal(t, id, s2.ID)
145}