Skip to content

Commit 926793b

Browse files
committed
more testing
1 parent 17eef63 commit 926793b

File tree

1 file changed

+299
-0
lines changed

1 file changed

+299
-0
lines changed

uuid_test.go

Lines changed: 299 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ package uuid
2323
import (
2424
"bytes"
2525
"fmt"
26+
"sync"
2627
"testing"
28+
"time"
2729
)
2830

2931
func TestUUID(t *testing.T) {
@@ -224,3 +226,300 @@ func BenchmarkNewV7(b *testing.B) {
224226
_ = Must(NewV7())
225227
}
226228
}
229+
230+
func TestConcurrentGeneration(t *testing.T) {
231+
const numGoroutines = 100
232+
const numUUIDs = 100
233+
234+
var wg sync.WaitGroup
235+
results := make([][]UUID, numGoroutines)
236+
237+
for i := 0; i < numGoroutines; i++ {
238+
wg.Add(1)
239+
go func(idx int) {
240+
defer wg.Done()
241+
uuids := make([]UUID, numUUIDs)
242+
for j := 0; j < numUUIDs; j++ {
243+
u, err := NewV4()
244+
if err != nil {
245+
t.Errorf("NewV4() failed in goroutine %d: %v", idx, err)
246+
return
247+
}
248+
uuids[j] = u
249+
}
250+
results[idx] = uuids
251+
}(i)
252+
}
253+
254+
wg.Wait()
255+
256+
// Check all UUIDs are unique across all goroutines
257+
seen := make(map[UUID]bool)
258+
for i, uuids := range results {
259+
if uuids == nil {
260+
continue // goroutine failed
261+
}
262+
for j, u := range uuids {
263+
if seen[u] {
264+
t.Errorf("Duplicate UUID found: %s (goroutine %d, index %d)", u, i, j)
265+
}
266+
seen[u] = true
267+
}
268+
}
269+
}
270+
271+
func TestV7ConcurrentGeneration(t *testing.T) {
272+
const numGoroutines = 10
273+
const numUUIDs = 100
274+
275+
var wg sync.WaitGroup
276+
results := make([][]UUID, numGoroutines)
277+
278+
for i := 0; i < numGoroutines; i++ {
279+
wg.Add(1)
280+
go func(idx int) {
281+
defer wg.Done()
282+
uuids := make([]UUID, numUUIDs)
283+
for j := 0; j < numUUIDs; j++ {
284+
u, err := NewV7()
285+
if err != nil {
286+
t.Errorf("NewV7() failed in goroutine %d: %v", idx, err)
287+
return
288+
}
289+
uuids[j] = u
290+
}
291+
results[idx] = uuids
292+
}(i)
293+
}
294+
295+
wg.Wait()
296+
297+
// Check all UUIDs are unique and timestamps are reasonable
298+
seen := make(map[UUID]bool)
299+
for i, uuids := range results {
300+
if uuids == nil {
301+
continue
302+
}
303+
for j, u := range uuids {
304+
if seen[u] {
305+
t.Errorf("Duplicate V7 UUID found: %s (goroutine %d, index %d)", u, i, j)
306+
}
307+
seen[u] = true
308+
309+
// Verify timestamp extraction works
310+
if _, err := TimestampFromV7(u); err != nil {
311+
t.Errorf("Failed to extract timestamp from V7 UUID %s: %v", u, err)
312+
}
313+
}
314+
}
315+
}
316+
317+
func TestCustomEpochFunc(t *testing.T) {
318+
fixedTime := time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC)
319+
320+
gen := &Gen{
321+
epochFunc: func() time.Time { return fixedTime },
322+
}
323+
324+
u1, err := gen.NewV7()
325+
if err != nil {
326+
t.Fatalf("NewV7() failed: %v", err)
327+
}
328+
329+
u2, err := gen.NewV7()
330+
if err != nil {
331+
t.Fatalf("NewV7() failed: %v", err)
332+
}
333+
334+
ts1, _ := TimestampFromV7(u1)
335+
ts2, _ := TimestampFromV7(u2)
336+
337+
expectedTS := uint64(fixedTime.UnixMilli())
338+
if uint64(ts1) != expectedTS {
339+
t.Errorf("First V7 UUID has timestamp %d, want %d", ts1, expectedTS)
340+
}
341+
if uint64(ts2) != expectedTS {
342+
t.Errorf("Second V7 UUID has timestamp %d, want %d", ts2, expectedTS)
343+
}
344+
345+
// With same timestamp, clock sequence should increment
346+
if u1 == u2 {
347+
t.Errorf("Two V7 UUIDs with same timestamp should be different due to clock sequence")
348+
}
349+
}
350+
351+
func TestV7RapidGeneration(t *testing.T) {
352+
// Generate many UUIDs rapidly in same millisecond to test clock sequence behavior
353+
uuids := make([]UUID, 1000)
354+
for i := 0; i < 1000; i++ {
355+
u, err := NewV7()
356+
if err != nil {
357+
t.Fatalf("NewV7() failed at iteration %d: %v", i, err)
358+
}
359+
uuids[i] = u
360+
}
361+
362+
// All should be unique
363+
seen := make(map[UUID]bool)
364+
for i, u := range uuids {
365+
if seen[u] {
366+
t.Errorf("Duplicate V7 UUID at index %d: %s", i, u)
367+
}
368+
seen[u] = true
369+
}
370+
371+
// Timestamps should be monotonic (non-decreasing)
372+
var lastTS int64
373+
for i, u := range uuids {
374+
ts, err := TimestampFromV7(u)
375+
if err != nil {
376+
t.Errorf("Failed to extract timestamp from UUID %d: %v", i, err)
377+
continue
378+
}
379+
if ts < lastTS {
380+
t.Errorf("V7 timestamp went backwards at index %d: %d < %d", i, ts, lastTS)
381+
}
382+
lastTS = ts
383+
}
384+
}
385+
386+
func TestV4Correctness(t *testing.T) {
387+
for i := 0; i < 1000; i++ {
388+
u, err := NewV4()
389+
if err != nil {
390+
t.Fatalf("NewV4() failed: %v", err)
391+
}
392+
393+
// Check version
394+
if u.Version() != V4 {
395+
t.Errorf("UUID %s has version %d, want %d", u, u.Version(), V4)
396+
}
397+
398+
// Check variant
399+
if u.Variant() != VariantRFC4122 {
400+
t.Errorf("UUID %s has variant %d, want %d", u, u.Variant(), VariantRFC4122)
401+
}
402+
403+
// Check not nil
404+
if u.IsNil() {
405+
t.Errorf("UUID should not be nil: %s", u)
406+
}
407+
408+
// Check string format
409+
s := u.String()
410+
if len(s) != 36 {
411+
t.Errorf("UUID string %s has length %d, want 36", s, len(s))
412+
}
413+
414+
// Check parsing back
415+
parsed, err := FromString(s)
416+
if err != nil {
417+
t.Errorf("Failed to parse UUID string %s: %v", s, err)
418+
}
419+
if parsed != u {
420+
t.Errorf("Parsed UUID %s != original %s", parsed, u)
421+
}
422+
}
423+
}
424+
425+
func TestV7Correctness(t *testing.T) {
426+
var lastTime int64
427+
428+
for i := 0; i < 100; i++ {
429+
u, err := NewV7()
430+
if err != nil {
431+
t.Fatalf("NewV7() failed: %v", err)
432+
}
433+
434+
// Check version
435+
if u.Version() != V7 {
436+
t.Errorf("UUID %s has version %d, want %d", u, u.Version(), V7)
437+
}
438+
439+
// Check variant
440+
if u.Variant() != VariantRFC4122 {
441+
t.Errorf("UUID %s has variant %d, want %d", u, u.Variant(), VariantRFC4122)
442+
}
443+
444+
// Check timestamp extraction
445+
ts, err := TimestampFromV7(u)
446+
if err != nil {
447+
t.Errorf("Failed to extract timestamp from V7 UUID %s: %v", u, err)
448+
}
449+
450+
// Check timestamp is reasonable (within last hour and future)
451+
now := time.Now().UnixMilli()
452+
if ts < now-3600000 || ts > now+1000 {
453+
t.Errorf("V7 UUID %s has unreasonable timestamp %d (now=%d)", u, ts, now)
454+
}
455+
456+
// Check timestamps are non-decreasing (monotonic)
457+
if ts < lastTime {
458+
t.Errorf("V7 UUID timestamp went backwards: %d < %d", ts, lastTime)
459+
}
460+
lastTime = ts
461+
462+
time.Sleep(1 * time.Millisecond) // Ensure different timestamps
463+
}
464+
}
465+
466+
func TestUUIDUniqueness(t *testing.T) {
467+
seen := make(map[UUID]bool)
468+
469+
// Test V4 uniqueness
470+
for i := 0; i < 10000; i++ {
471+
u, err := NewV4()
472+
if err != nil {
473+
t.Fatalf("NewV4() failed: %v", err)
474+
}
475+
if seen[u] {
476+
t.Errorf("Duplicate V4 UUID generated: %s", u)
477+
}
478+
seen[u] = true
479+
}
480+
481+
// Test V7 uniqueness
482+
for i := 0; i < 1000; i++ {
483+
u, err := NewV7()
484+
if err != nil {
485+
t.Fatalf("NewV7() failed: %v", err)
486+
}
487+
if seen[u] {
488+
t.Errorf("Duplicate V7 UUID generated: %s", u)
489+
}
490+
seen[u] = true
491+
}
492+
}
493+
494+
func TestStringFormats(t *testing.T) {
495+
u, err := NewV4()
496+
if err != nil {
497+
t.Fatalf("NewV4() failed: %v", err)
498+
}
499+
500+
// Test canonical format
501+
canonical := u.Format(FormatCanonical)
502+
if len(canonical) != 36 {
503+
t.Errorf("Canonical format has wrong length: %d", len(canonical))
504+
}
505+
if canonical[8] != '-' || canonical[13] != '-' || canonical[18] != '-' || canonical[23] != '-' {
506+
t.Errorf("Canonical format missing dashes: %s", canonical)
507+
}
508+
509+
// Test hash format
510+
hash := u.Format(FormatHash)
511+
if len(hash) != 32 {
512+
t.Errorf("Hash format has wrong length: %d", len(hash))
513+
}
514+
515+
// Test base58 format
516+
base58 := u.Format(FormatBase58)
517+
if len(base58) == 0 {
518+
t.Errorf("Base58 format is empty")
519+
}
520+
521+
// Test that String() matches canonical
522+
if u.String() != canonical {
523+
t.Errorf("String() != Format(FormatCanonical): %s vs %s", u.String(), canonical)
524+
}
525+
}

0 commit comments

Comments
 (0)