Skip to content

Commit

Permalink
feat(generate): Added UUIDv6 generation (#301)
Browse files Browse the repository at this point in the history
  • Loading branch information
ekkinox authored Nov 18, 2024
1 parent b155eeb commit d78a0ed
Show file tree
Hide file tree
Showing 10 changed files with 290 additions and 3 deletions.
2 changes: 1 addition & 1 deletion generate/.golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ linters:
- importas
- ineffassign
- interfacebloat
- logrlint
- loggercheck
- maintidx
- makezero
- misspell
Expand Down
52 changes: 52 additions & 0 deletions generate/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
* [Installation](#installation)
* [Documentation](#documentation)
* [UUID V4](#uuid-v4)
* [UUID V6](#uuid-v6)
* [UUID V7](#uuid-v7)
<!-- TOC -->

Expand Down Expand Up @@ -71,6 +72,57 @@ func main() {
}
```

### UUID V6

This module provides an [UuidV6Generator](uuidv6/generator.go) interface, allowing to generate UUIDs V6.

The `DefaultUuidV6Generator` implementing it is based on [Google UUID](https://github.com/google/uuid).

```go
package main

import (
"fmt"

"github.com/ankorstore/yokai/generate/uuidv6"
uuidv6test "github.com/ankorstore/yokai/generate/generatetest/uuidv6"
)

func main() {
// default UUID V6 generator
generator := uuidv6.NewDefaultUuidV6Generator()
uuid, _ := generator.Generate()
fmt.Printf("uuid: %s", uuid.String()) // uuid: 1efa5a1e-a679-67b7-ae79-f36f6749aa6b

// test UUID generator (with deterministic value for testing, requires valid UUID v6)
testGenerator, _ := uuidv6test.NewTestUuidV6Generator("1efa5a08-2883-6652-b357-5dd221ce0561")
uuid, _ = testGenerator.Generate()
fmt.Printf("uuid: %s", uuid.String()) // uuid: 1efa5a08-2883-6652-b357-5dd221ce0561
}
```

The module also provides a [UuidV6GeneratorFactory](uuidv6/factory.go) interface, to create
the [UuidV6Generator](uuidv6/generator.go) instances.

The `DefaultUuidV6GeneratorFactory` generates `DefaultUuidV6Generator` instances.

```go
package main

import (
"fmt"

"github.com/ankorstore/yokai/generate/uuidv6"
)

func main() {
// default UUID generator factory
generator := uuidv6.NewDefaultUuidV6GeneratorFactory().Create()
uuid, _ := generator.Generate()
fmt.Printf("uuid: %s", uuid.String()) // uuid: 1efa5a1e-a679-67b7-ae79-f36f6749aa6b
}
```

### UUID V7

This module provides an [UuidV7Generator](uuidv7/generator.go) interface, allowing to generate UUIDs V7.
Expand Down
39 changes: 39 additions & 0 deletions generate/generatetest/uuidv6/generator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package uuidv6

import googleuuid "github.com/google/uuid"

// TestUuidV6Generator is a [UuidV6Generator] implementation allowing deterministic generations (for testing).
type TestUuidV6Generator struct {
value string
}

// NewTestUuidV6Generator returns a [TestUuidGenerator], implementing [UuidGenerator].
//
// It accepts a value that will be used for deterministic generation results.
func NewTestUuidV6Generator(value string) (*TestUuidV6Generator, error) {
err := googleuuid.Validate(value)
if err != nil {
return nil, err
}

return &TestUuidV6Generator{
value: value,
}, nil
}

// SetValue sets the value to use for deterministic generations.
func (g *TestUuidV6Generator) SetValue(value string) error {
err := googleuuid.Validate(value)
if err != nil {
return err
}

g.value = value

return nil
}

// Generate returns the configured deterministic value.
func (g *TestUuidV6Generator) Generate() (googleuuid.UUID, error) {
return googleuuid.Parse(g.value)
}
68 changes: 68 additions & 0 deletions generate/generatetest/uuidv6/generator_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package uuidv6_test

import (
"testing"

uuidv6test "github.com/ankorstore/yokai/generate/generatetest/uuidv6"
"github.com/ankorstore/yokai/generate/uuidv6"
"github.com/stretchr/testify/assert"
)

const (
uuid1 = "1efa59f2-d438-6ec0-9d52-4da3ad16f2c6"
uuid2 = "1efa59f2-d438-6ec1-8b52-6844309a22de"
uuid3 = "1efa59f2-d438-6ec2-abd0-e36a967ab868"
)

func TestNewTestUuidV6Generator(t *testing.T) {
t.Parallel()

generator, err := uuidv6test.NewTestUuidV6Generator(uuid1)
assert.NoError(t, err)

assert.IsType(t, &uuidv6test.TestUuidV6Generator{}, generator)
assert.Implements(t, (*uuidv6.UuidV6Generator)(nil), generator)
}

func TestGenerateSuccess(t *testing.T) {
t.Parallel()

generator, err := uuidv6test.NewTestUuidV6Generator(uuid2)
assert.NoError(t, err)

value1, err := generator.Generate()
assert.NoError(t, err)

value2, err := generator.Generate()
assert.NoError(t, err)

assert.Equal(t, uuid2, value1.String())
assert.Equal(t, uuid2, value2.String())

err = generator.SetValue(uuid3)
assert.NoError(t, err)

value1, err = generator.Generate()
assert.NoError(t, err)

value2, err = generator.Generate()
assert.NoError(t, err)

assert.Equal(t, uuid3, value1.String())
assert.Equal(t, uuid3, value2.String())
}

func TestGenerateFailure(t *testing.T) {
t.Parallel()

_, err := uuidv6test.NewTestUuidV6Generator("invalid")
assert.Error(t, err)
assert.Contains(t, err.Error(), "invalid UUID length: 7")

generator, err := uuidv6test.NewTestUuidV6Generator(uuid1)
assert.NoError(t, err)

err = generator.SetValue("invalid")
assert.Error(t, err)
assert.Contains(t, err.Error(), "invalid UUID length: 7")
}
2 changes: 1 addition & 1 deletion generate/generatetest/uuidv7/generator.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package uuid
package uuidv7

import googleuuid "github.com/google/uuid"

Expand Down
2 changes: 1 addition & 1 deletion generate/generatetest/uuidv7/generator_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package uuid_test
package uuidv7_test

import (
"testing"
Expand Down
19 changes: 19 additions & 0 deletions generate/uuidv6/factory.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package uuidv6

// UuidV6GeneratorFactory is the interface for [UuidV6Generator] factories.
type UuidV6GeneratorFactory interface {
Create() UuidV6Generator
}

// DefaultUuidV6GeneratorFactory is the default [UuidV6GeneratorFactory] implementation.
type DefaultUuidV6GeneratorFactory struct{}

// NewDefaultUuidV6GeneratorFactory returns a [DefaultUuidV6GeneratorFactory], implementing [UuidV6GeneratorFactory].
func NewDefaultUuidV6GeneratorFactory() UuidV6GeneratorFactory {
return &DefaultUuidV6GeneratorFactory{}
}

// Create returns a new [UuidV6Generator].
func (g *DefaultUuidV6GeneratorFactory) Create() UuidV6Generator {
return NewDefaultUuidV6Generator()
}
43 changes: 43 additions & 0 deletions generate/uuidv6/factory_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package uuidv6_test

import (
"testing"

"github.com/ankorstore/yokai/generate/uuidv6"
googleuuid "github.com/google/uuid"
"github.com/stretchr/testify/assert"
)

func TestNewDefaultUuidV6GeneratorFactory(t *testing.T) {
t.Parallel()

factory := uuidv6.NewDefaultUuidV6GeneratorFactory()

assert.IsType(t, &uuidv6.DefaultUuidV6GeneratorFactory{}, factory)
assert.Implements(t, (*uuidv6.UuidV6GeneratorFactory)(nil), factory)
}

func TestCreate(t *testing.T) {
t.Parallel()

generator := uuidv6.NewDefaultUuidV6GeneratorFactory().Create()

uuid1, err := generator.Generate()
assert.NoError(t, err)

uuid2, err := generator.Generate()
assert.NoError(t, err)

assert.NotEqual(t, uuid1, uuid2)

parsedValue1, err := googleuuid.Parse(uuid1.String())
assert.NoError(t, err)

parsedValue2, err := googleuuid.Parse(uuid2.String())
assert.NoError(t, err)

assert.NotEqual(t, parsedValue1.String(), parsedValue2.String())

assert.Equal(t, uuid1.String(), parsedValue1.String())
assert.Equal(t, uuid2.String(), parsedValue2.String())
}
23 changes: 23 additions & 0 deletions generate/uuidv6/generator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package uuidv6

import googleuuid "github.com/google/uuid"

// UuidV6Generator is the interface for UUID v6 generators.
type UuidV6Generator interface {
Generate() (googleuuid.UUID, error)
}

// DefaultUuidV6Generator is the default [UuidGenerator] implementation.
type DefaultUuidV6Generator struct{}

// NewDefaultUuidV6Generator returns a [DefaultUuidGenerator], implementing [UuidGenerator].
func NewDefaultUuidV6Generator() *DefaultUuidV6Generator {
return &DefaultUuidV6Generator{}
}

// Generate returns a new UUID V6, using [Google UUID].
//
// [Google UUID]: https://github.com/google/uuid
func (g *DefaultUuidV6Generator) Generate() (googleuuid.UUID, error) {
return googleuuid.NewV6()
}
43 changes: 43 additions & 0 deletions generate/uuidv6/generator_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package uuidv6_test

import (
"testing"

"github.com/ankorstore/yokai/generate/uuidv6"
googleuuid "github.com/google/uuid"
"github.com/stretchr/testify/assert"
)

func TestNewDefaultUuidV6Generator(t *testing.T) {
t.Parallel()

generator := uuidv6.NewDefaultUuidV6Generator()

assert.IsType(t, &uuidv6.DefaultUuidV6Generator{}, generator)
assert.Implements(t, (*uuidv6.UuidV6Generator)(nil), generator)
}

func TestGenerate(t *testing.T) {
t.Parallel()

generator := uuidv6.NewDefaultUuidV6Generator()

uuid1, err := generator.Generate()
assert.NoError(t, err)

uuid2, err := generator.Generate()
assert.NoError(t, err)

assert.NotEqual(t, uuid1.String(), uuid2.String())

parsedUuid1, err := googleuuid.Parse(uuid1.String())
assert.NoError(t, err)

parsedUuid2, err := googleuuid.Parse(uuid2.String())
assert.NoError(t, err)

assert.NotEqual(t, parsedUuid1.String(), parsedUuid2.String())

assert.Equal(t, uuid1.String(), parsedUuid1.String())
assert.Equal(t, uuid2.String(), parsedUuid2.String())
}

0 comments on commit d78a0ed

Please sign in to comment.