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}