Skip to content

Commit

Permalink
Added examples & tests
Browse files Browse the repository at this point in the history
  • Loading branch information
sonnes committed Sep 1, 2023
1 parent e49c813 commit 78e3365
Show file tree
Hide file tree
Showing 9 changed files with 278 additions and 1 deletion.
2 changes: 2 additions & 0 deletions xload/providers/cached/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
)

// Cacher is the interface for custom cache implementations.
//
//go:generate mockery --name Cacher --structname MockCache --filename mock_test.go --outpkg cached --output .
type Cacher interface {
Get(key string) (string, error)
Set(key, value string, ttl time.Duration) error
Expand Down
6 changes: 6 additions & 0 deletions xload/providers/cached/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Package cached provides a cached provider for xload.
// This loader can be used to cache the results of any loader.
//
// Useful for loaders that have a high latency, or loaders that are
// called frequently.
package cached
82 changes: 82 additions & 0 deletions xload/providers/cached/example_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package cached_test

import (
"context"
"time"

"github.com/gojekfarm/xtools/xload"
"github.com/gojekfarm/xtools/xload/providers/cached"
)

func Example() {
// This example shows how to use the cached loader
// with a remote loader.

ctx := context.Background()
cfg := struct {
Title string `env:"TITLE"`
Link string `env:"LINK"`
ButtonLabel string `env:"BUTTON_LABEL"`
}{}

remoteLoader := xload.LoaderFunc(func(ctx context.Context, key string) (string, error) {
// Load the value from a remote source.

return "", nil
})

err := xload.Load(
ctx, &cfg,
cached.NewLoader(
remoteLoader,
cached.TTL(5*60*time.Minute),
),
)
if err != nil {
panic(err)
}
}

type CustomCache struct{}

func NewCustomCache() *CustomCache {
return &CustomCache{}
}

func (c *CustomCache) Get(key string) (string, error) {
return "", nil
}

func (c *CustomCache) Set(key, value string, ttl time.Duration) error {
return nil
}

func Example_customCache() {
// This example shows how to use a custom cache
// with the cached loader.

ctx := context.Background()
cfg := struct {
Title string `env:"TITLE"`
Link string `env:"LINK"`
ButtonLabel string `env:"BUTTON_LABEL"`
}{}

remoteLoader := xload.LoaderFunc(func(ctx context.Context, key string) (string, error) {
// Load the value from a remote source.

return "", nil
})

err := xload.Load(
ctx, &cfg,
cached.NewLoader(
remoteLoader,
cached.TTL(5*60*time.Minute),
cached.Cache(NewCustomCache()),
),
)
if err != nil {
panic(err)
}
}
1 change: 1 addition & 0 deletions xload/providers/cached/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ require (
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/cast v1.5.1 // indirect
github.com/stretchr/objx v0.5.0 // indirect
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.9.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
Expand Down
6 changes: 6 additions & 0 deletions xload/providers/cached/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,12 @@ github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIK
github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA=
github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
Expand All @@ -23,5 +28,6 @@ go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
3 changes: 3 additions & 0 deletions xload/providers/cached/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ func NewLoader(l xload.Loader, opts ...Option) xload.LoaderFunc {
return "", err
}

// DESIGN: If the loader returns an empty value, we
// consider it a cache HIT and cache the empty value.

err = o.cache.Set(key, loaded, o.ttl)

return loaded, err
Expand Down
110 changes: 110 additions & 0 deletions xload/providers/cached/loader_test.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,119 @@
package cached

import (
"context"
"testing"
"time"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"

"github.com/gojekfarm/xtools/xload"
)

type config struct {
Key1 string `env:"KEY_1"`
Key2 string `env:"KEY_2"`
Key3 string `env:"KEY_3"`
}

func TestNewLoader(t *testing.T) {
loader := xload.MapLoader(map[string]string{
"KEY_1": "value-1",
"KEY_2": "value-2",
})

cachedLoader := NewLoader(loader)

t.Run("Cache MISS", func(t *testing.T) {
cfg := config{}

err := xload.Load(context.TODO(), &cfg, cachedLoader)
assert.NoError(t, err)
assert.Equal(t, "value-1", cfg.Key1)
assert.Equal(t, "value-2", cfg.Key2)
})

t.Run("Cache HIT", func(t *testing.T) {
cfg := config{}

err := xload.Load(context.TODO(), &cfg, cachedLoader)
assert.NoError(t, err)
assert.Equal(t, "value-1", cfg.Key1)
assert.Equal(t, "value-2", cfg.Key2)
})
}

func TestNewLoader_WithTTL(t *testing.T) {
loader := xload.MapLoader(map[string]string{
"KEY_1": "value-1",
"KEY_2": "value-2",
})

ttl := 123 * time.Second

mc := NewMockCache(t)
mc.On("Get", mock.Anything).Return("", nil).Times(3)
mc.On("Set", mock.Anything, mock.Anything, ttl).Return(nil).Times(3)

cachedLoader := NewLoader(loader, TTL(ttl), Cache(mc))

cfg := config{}

err := xload.Load(context.Background(), &cfg, cachedLoader)
assert.NoError(t, err)

mc.AssertExpectations(t)
}

func TestNewLoader_ForwardError(t *testing.T) {
failingLoader := xload.LoaderFunc(func(ctx context.Context, key string) (string, error) {
return "", assert.AnError
})

cachedLoader := NewLoader(failingLoader)

cfg := config{}

err := xload.Load(context.Background(), &cfg, cachedLoader)
assert.Error(t, err)
assert.Equal(t, assert.AnError, err)
}

func TestNewLoader_CacheError(t *testing.T) {
loader := xload.MapLoader(map[string]string{
"KEY_1": "value-1",
"KEY_2": "value-2",
})

cfg := config{}

t.Run("Cache SET error", func(t *testing.T) {
mc := NewMockCache(t)

mc.On("Get", "KEY_1").Return("", nil)
mc.On("Set", "KEY_1", "value-1", mock.Anything).Return(assert.AnError)

cachedLoader := NewLoader(loader, Cache(mc))

err := xload.Load(context.Background(), &cfg, cachedLoader)
assert.Error(t, err)
assert.Equal(t, assert.AnError, err)

mc.AssertExpectations(t)
})

t.Run("Cache GET error", func(t *testing.T) {
mc := NewMockCache(t)

mc.On("Get", "KEY_1").Return("", assert.AnError)

cachedLoader := NewLoader(loader, Cache(mc))

err := xload.Load(context.Background(), &cfg, cachedLoader)
assert.Error(t, err)
assert.Equal(t, assert.AnError, err)

mc.AssertExpectations(t)
})
}
67 changes: 67 additions & 0 deletions xload/providers/cached/mock_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion xproto/pb_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 78e3365

Please sign in to comment.