package idgen import ( "testing" "testing/synctest" "time" ) func TestNewToken_length(t *testing.T) { tok := NewToken() if len(tok) != 32 { t.Fatalf("expected 32 chars, got %d: %q", len(tok), tok) } } func TestNewToken_validBase32(t *testing.T) { tok := NewToken() if _, err := encoding.DecodeString(tok); err != nil { t.Fatalf("invalid base32: %v", err) } } func TestNewToken_unique(t *testing.T) { seen := make(map[string]bool, 100) for range 100 { tok := NewToken() if seen[tok] { t.Fatal("duplicate token generated") } seen[tok] = true } } func TestNextID_roundtrip(t *testing.T) { const nodeID = 42 before := time.Now().Unix() id := NextID(nodeID) after := time.Now().Unix() gotTime, gotNode, gotSeq := SplitID(id) if gotNode != nodeID { t.Errorf("nodeID: got %d, want %d", gotNode, nodeID) } if gotTime < before || gotTime > after { t.Errorf("timestamp %d out of range [%d, %d]", gotTime, before, after) } if gotSeq < 1 { t.Errorf("seq should be >= 1, got %d", gotSeq) } } func TestNextID_allNodeIDs(t *testing.T) { for nodeID := int64(0); nodeID < 64; nodeID++ { id := NextID(nodeID) _, got, _ := SplitID(id) if got != nodeID { t.Errorf("nodeID %d: SplitID returned %d", nodeID, got) } } } func TestNextID_monotonic(t *testing.T) { prev := NextID(0) for range 1000 { id := NextID(0) if id <= prev { t.Fatalf("not monotonically increasing: %d <= %d", id, prev) } prev = id } } func TestNextID_unique(t *testing.T) { seen := make(map[int64]bool, 1000) for i := range 1000 { id := NextID(0) if seen[id] { t.Fatalf("duplicate ID at i=%d", i) } seen[id] = true } } func TestNextID_invalidNodeID(t *testing.T) { for _, bad := range []int64{-1, 64, 100} { func() { defer func() { if recover() == nil { t.Errorf("NextID(%d) should have panicked", bad) } }() NextID(bad) }() } } func TestNextID_timestampAdvances(t *testing.T) { synctest.Test(t, func(t *testing.T) { // synctest's synthetic clock starts at 2000-01-01 00:00:00 UTC. // Prime ts to one second before that so the first NextID call // enters the new-second branch and records the synthetic start time. lock.Lock() ts = time.Now().Unix() - 1 seq = 0 lock.Unlock() id1 := NextID(0) t1, _, _ := SplitID(id1) time.Sleep(2 * time.Second) id2 := NextID(0) t2, _, _ := SplitID(id2) if t2 <= t1 { t.Errorf("timestamp did not advance: %d -> %d", t1, t2) } }) } func BenchmarkNextID(b *testing.B) { for b.Loop() { NextID(0) } }