package domain import ( "bytes" "errors" "sort" "sync" "testing" "time" ) func TestUUIDv7CanonicalValidation_HP_IDEM_01(t *testing.T) { t.Parallel() valid := "01890a5d-ac96-7a11-b234-123456789abc" parsed, err := ParseUUIDv7(valid) if err != nil { t.Fatalf("ParseUUIDv7(%q): %v", valid, err) } if parsed.String() != valid { t.Fatalf("round trip = %q, want %q", parsed.String(), valid) } for _, value := range []string{ "", "01890A5D-AC96-7A11-B234-123456789ABC", "01890a5dac967a11b234123456789abc", "{01890a5d-ac96-7a11-b234-123456789abc}", "01890a5d-ac96-4a11-b234-123456789abc", "01890a5d-ac96-7a11-7234-123456789abc", } { if _, err := ParseUUIDv7(value); !errors.Is(err, ErrInvalidUUIDv7) { t.Errorf("ParseUUIDv7(%q) error = %v, want ErrInvalidUUIDv7", value, err) } } } func TestUUIDv7GeneratorMonotonicAcrossClockRollback_HP_IDEM_10(t *testing.T) { t.Parallel() times := []time.Time{ time.UnixMilli(1_700_000_000_000), time.UnixMilli(1_700_000_000_000), time.UnixMilli(1_699_999_999_000), time.UnixMilli(1_700_000_000_001), } next := 0 generator := NewUUIDv7Generator(func() time.Time { value := times[next] next++ return value }, bytes.NewReader(bytes.Repeat([]byte{0x11}, 40))) var previous string for range times { id, err := generator.New() if err != nil { t.Fatalf("New(): %v", err) } if _, err := ParseUUIDv7(id.String()); err != nil { t.Fatalf("generated invalid UUID %q: %v", id, err) } if previous != "" && id.String() <= previous { t.Fatalf("UUID %q did not sort after %q", id, previous) } previous = id.String() } } func TestUUIDv7GeneratorConcurrentOrderingAndUniqueness_HP_IDEM_10(t *testing.T) { t.Parallel() const count = 512 generator := NewUUIDv7Generator( func() time.Time { return time.UnixMilli(1_700_000_000_000) }, bytes.NewReader(bytes.Repeat([]byte{0x42}, count*10)), ) ids := make(chan string, count) errCh := make(chan error, count) var group sync.WaitGroup for range count { group.Add(1) go func() { defer group.Done() id, err := generator.New() if err != nil { errCh <- err return } ids <- id.String() }() } group.Wait() close(ids) close(errCh) for err := range errCh { t.Errorf("New(): %v", err) } ordered := make([]string, 0, count) seen := make(map[string]struct{}, count) for id := range ids { if _, duplicate := seen[id]; duplicate { t.Fatalf("duplicate UUID %q", id) } seen[id] = struct{}{} ordered = append(ordered, id) } if len(ordered) != count { t.Fatalf("generated %d UUIDs, want %d", len(ordered), count) } sort.Strings(ordered) for index := 1; index < len(ordered); index++ { if ordered[index] <= ordered[index-1] { t.Fatalf("UUID ordering failed at %d", index) } } } func TestUUIDv7GeneratorErrors_HP_IDEM_01(t *testing.T) { t.Parallel() t.Run("entropy", func(t *testing.T) { generator := NewUUIDv7Generator(time.Now, bytes.NewReader(nil)) if _, err := generator.New(); err == nil { t.Fatal("New() succeeded with empty entropy") } }) t.Run("before epoch", func(t *testing.T) { generator := NewUUIDv7Generator(func() time.Time { return time.UnixMilli(-1) }, bytes.NewReader(make([]byte, 10))) if _, err := generator.New(); !errors.Is(err, ErrUUIDTimeRange) { t.Fatalf("New() error = %v, want ErrUUIDTimeRange", err) } }) }