Skip to content

Commit

Permalink
add cache tests
Browse files Browse the repository at this point in the history
Signed-off-by: Frank Jogeleit <frank.jogeleit@lovoo.com>
  • Loading branch information
Frank Jogeleit committed Apr 25, 2024
1 parent 0f2f1c9 commit 1edff60
Show file tree
Hide file tree
Showing 7 changed files with 214 additions and 18 deletions.
14 changes: 9 additions & 5 deletions pkg/cache/memory.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import (
)

type inMemoryCache struct {
caches *gocache.Cache
caches *gocache.Cache
keepDuration time.Duration
keepReport time.Duration
}

func (c *inMemoryCache) AddReport(report v1alpha2.ReportInterface) {
Expand All @@ -28,7 +30,7 @@ func (c *inMemoryCache) AddReport(report v1alpha2.ReportInterface) {

for id, item := range cache.Items() {
if !next[id] && item.Expiration == 0 {
cache.Set(id, nil, 6*time.Hour)
cache.Set(id, nil, c.keepDuration)
}
}

Expand All @@ -41,7 +43,7 @@ func (c *inMemoryCache) RemoveReport(id string) {
return
}

c.caches.Set(id, cache, 10*time.Minute)
c.caches.Set(id, cache, c.keepReport)
}

func (c *inMemoryCache) getCache(id string) (*gocache.Cache, bool) {
Expand Down Expand Up @@ -80,7 +82,7 @@ func (c *inMemoryCache) Shared() bool {
return false
}

func NewInMermoryCache() Cache {
func NewInMermoryCache(keepDuration, keepReport time.Duration) Cache {
cache := gocache.New(gocache.NoExpiration, 5*time.Minute)
cache.OnEvicted(func(s string, i interface{}) {
if c, ok := i.(*gocache.Cache); ok {
Expand All @@ -89,6 +91,8 @@ func NewInMermoryCache() Cache {
})

return &inMemoryCache{
caches: gocache.New(gocache.NoExpiration, 5*time.Minute),
caches: gocache.New(gocache.NoExpiration, 5*time.Minute),
keepDuration: keepDuration,
keepReport: keepReport,
}
}
69 changes: 69 additions & 0 deletions pkg/cache/memory_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package cache_test

import (
"testing"
"time"

"github.com/kyverno/policy-reporter/pkg/cache"
"github.com/kyverno/policy-reporter/pkg/fixtures"
)

func TestInMemory(t *testing.T) {
t.Run("add report", func(t *testing.T) {
id := fixtures.DefaultPolicyReport.GetID()

c := cache.NewInMermoryCache(time.Millisecond, time.Millisecond)

c.AddReport(fixtures.DefaultPolicyReport)

results := c.GetResults(id)
if len(results) != len(fixtures.DefaultPolicyReport.Results) {
t.Error("expected all results were cached")
}

c.AddReport(fixtures.MinPolicyReport)

time.Sleep(3 * time.Millisecond)

changed := c.GetResults(id)
if len(changed) != len(fixtures.MinPolicyReport.Results) {
t.Error("expected all old results were removed")
}
})
t.Run("remove report", func(t *testing.T) {
id := fixtures.DefaultPolicyReport.GetID()

c := cache.NewInMermoryCache(time.Millisecond, time.Millisecond)

c.AddReport(fixtures.DefaultPolicyReport)

c.RemoveReport(id)

time.Sleep(3 * time.Millisecond)

results := c.GetResults(id)
if len(results) != 0 {
t.Error("expected all results were removed")
}
})
t.Run("ceanup report", func(t *testing.T) {
id := fixtures.DefaultPolicyReport.GetID()

c := cache.NewInMermoryCache(time.Millisecond, time.Millisecond)

c.AddReport(fixtures.DefaultPolicyReport)

c.Clear()

results := c.GetResults(id)
if len(results) != 0 {
t.Error("expected all results were cleaned up")
}
})
t.Run("shared cache", func(t *testing.T) {
c := cache.NewInMermoryCache(time.Millisecond, time.Millisecond)
if c.Shared() {
t.Error("expected in memory cache is not shared")
}
})
}
13 changes: 10 additions & 3 deletions pkg/cache/redis.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,15 @@ import (
"github.com/kyverno/policy-reporter/pkg/crd/api/policyreport/v1alpha2"
)

type rdb interface {
Set(ctx context.Context, key string, value interface{}, expiration time.Duration) *goredis.StatusCmd
Keys(ctx context.Context, pattern string) *goredis.StringSliceCmd
Expire(ctx context.Context, key string, expiration time.Duration) *goredis.BoolCmd
Del(ctx context.Context, keys ...string) *goredis.IntCmd
}

type redisCache struct {
rdb *goredis.Client
rdb rdb
prefix string
ttl time.Duration
}
Expand All @@ -28,7 +35,7 @@ func (r *redisCache) AddReport(report v1alpha2.ReportInterface) {

for _, id := range r.GetResults(report.GetID()) {
if !next[id] {
r.rdb.Set(context.Background(), r.generateKey(report.GetID(), id), nil, 6*time.Hour)
r.rdb.Set(context.Background(), r.generateKey(report.GetID(), id), nil, r.ttl)
}
}
}
Expand Down Expand Up @@ -88,6 +95,6 @@ func (r *redisCache) generateKeyPattern(report string) string {
return fmt.Sprintf("%s:%s:*", r.prefix, report)
}

func NewRedisCache(prefix string, rdb *goredis.Client, ttl time.Duration) Cache {
func NewRedisCache(prefix string, rdb rdb, ttl time.Duration) Cache {
return &redisCache{rdb: rdb, prefix: prefix, ttl: ttl}
}
116 changes: 116 additions & 0 deletions pkg/cache/redis_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package cache_test

import (
"context"
"strings"
"testing"
"time"

goredis "github.com/go-redis/redis/v8"
"github.com/kyverno/policy-reporter/pkg/cache"
"github.com/kyverno/policy-reporter/pkg/fixtures"
)

type redis struct {
items map[string]any
}

func (r *redis) Set(ctx context.Context, key string, value interface{}, expiration time.Duration) *goredis.StatusCmd {
if expiration == -1 {
delete(r.items, key)
return nil
}

r.items[key] = value
return nil
}

func (r *redis) Keys(ctx context.Context, pattern string) *goredis.StringSliceCmd {
p := strings.TrimRight(pattern, "*")

keys := make([]string, 0, len(r.items))
for k := range r.items {
if strings.HasPrefix(k, p) {
keys = append(keys, k)
}
}

s := &goredis.StringSliceCmd{}
s.SetVal(keys)

return s
}

func (r *redis) Expire(ctx context.Context, key string, expiration time.Duration) *goredis.BoolCmd {
delete(r.items, key)

return nil
}

func (r *redis) Del(ctx context.Context, keys ...string) *goredis.IntCmd {
for _, k := range keys {
delete(r.items, k)
}

return nil
}

func newRedis() *redis {
return &redis{items: make(map[string]any)}
}

func TestRedisCache(t *testing.T) {
t.Run("add report", func(t *testing.T) {
id := fixtures.DefaultPolicyReport.GetID()

c := cache.NewRedisCache("cache", newRedis(), -1)

c.AddReport(fixtures.DefaultPolicyReport)

results := c.GetResults(id)
if len(results) != len(fixtures.DefaultPolicyReport.Results) {
t.Error("expected all results were cached")
}

c.AddReport(fixtures.MinPolicyReport)

changed := c.GetResults(id)
if len(changed) != len(fixtures.MinPolicyReport.Results) {
t.Error("expected all old results were removed")
}
})
t.Run("remove report", func(t *testing.T) {
id := fixtures.DefaultPolicyReport.GetID()

c := cache.NewRedisCache("cache", newRedis(), -1)

c.AddReport(fixtures.DefaultPolicyReport)

c.RemoveReport(id)

results := c.GetResults(id)
if len(results) != 0 {
t.Error("expected all results were removed")
}
})
t.Run("ceanup report", func(t *testing.T) {
id := fixtures.DefaultPolicyReport.GetID()

c := cache.NewRedisCache("cache", newRedis(), -1)

c.AddReport(fixtures.DefaultPolicyReport)

c.Clear()

results := c.GetResults(id)
if len(results) != 0 {
t.Error("expected all results were cleaned up")
}
})
t.Run("shared cache", func(t *testing.T) {
c := cache.NewRedisCache("cache", newRedis(), -1)
if !c.Shared() {
t.Error("expected redis cache is shared")
}
})
}
4 changes: 2 additions & 2 deletions pkg/config/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -470,10 +470,10 @@ func (r *Resolver) ResultCache() cache.Cache {
Password: r.config.Redis.Password,
DB: r.config.Redis.Database,
}),
2*time.Hour,
6*time.Hour,
)
} else {
r.resultCache = cache.NewInMermoryCache()
r.resultCache = cache.NewInMermoryCache(6*time.Hour, 10*time.Minute)
}

return r.resultCache
Expand Down
File renamed without changes.
16 changes: 8 additions & 8 deletions pkg/listener/new_result_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func Test_ResultListener(t *testing.T) {
t.Run("Publish Result", func(t *testing.T) {
var called v1alpha2.PolicyReportResult

slistener := listener.NewResultListener(true, cache.NewInMermoryCache(), time.Now())
slistener := listener.NewResultListener(true, cache.NewInMermoryCache(time.Minute, time.Minute), time.Now())
slistener.RegisterListener(func(_ v1alpha2.ReportInterface, r v1alpha2.PolicyReportResult, b bool) {
called = r
})
Expand All @@ -33,7 +33,7 @@ func Test_ResultListener(t *testing.T) {
t.Run("Ignore Delete Event", func(t *testing.T) {
var called bool

slistener := listener.NewResultListener(true, cache.NewInMermoryCache(), time.Now())
slistener := listener.NewResultListener(true, cache.NewInMermoryCache(time.Minute, time.Minute), time.Now())
slistener.RegisterListener(func(_ v1alpha2.ReportInterface, r v1alpha2.PolicyReportResult, b bool) {
called = true
})
Expand All @@ -48,7 +48,7 @@ func Test_ResultListener(t *testing.T) {
t.Run("Ignore Added Results created before startup", func(t *testing.T) {
var called bool

slistener := listener.NewResultListener(true, cache.NewInMermoryCache(), time.Now())
slistener := listener.NewResultListener(true, cache.NewInMermoryCache(time.Minute, time.Minute), time.Now())
slistener.RegisterListener(func(_ v1alpha2.ReportInterface, r v1alpha2.PolicyReportResult, b bool) {
called = true
})
Expand All @@ -63,7 +63,7 @@ func Test_ResultListener(t *testing.T) {
t.Run("Ignore CacheResults", func(t *testing.T) {
var called bool

slistener := listener.NewResultListener(true, cache.NewInMermoryCache(), time.Now())
slistener := listener.NewResultListener(true, cache.NewInMermoryCache(time.Minute, time.Minute), time.Now())
slistener.RegisterListener(func(_ v1alpha2.ReportInterface, r v1alpha2.PolicyReportResult, b bool) {
called = true
})
Expand All @@ -79,7 +79,7 @@ func Test_ResultListener(t *testing.T) {
t.Run("Early Return if Results are empty", func(t *testing.T) {
var called bool

slistener := listener.NewResultListener(true, cache.NewInMermoryCache(), time.Now())
slistener := listener.NewResultListener(true, cache.NewInMermoryCache(time.Minute, time.Minute), time.Now())
slistener.RegisterListener(func(_ v1alpha2.ReportInterface, r v1alpha2.PolicyReportResult, b bool) {
called = true
})
Expand All @@ -92,7 +92,7 @@ func Test_ResultListener(t *testing.T) {
})

t.Run("Skip process events when no listeners registered", func(t *testing.T) {
c := cache.NewInMermoryCache()
c := cache.NewInMermoryCache(time.Minute, time.Minute)

slistener := listener.NewResultListener(true, c, time.Now())
slistener.Listen(report.LifecycleEvent{Type: report.Added, PolicyReport: preport2})
Expand All @@ -105,7 +105,7 @@ func Test_ResultListener(t *testing.T) {
t.Run("UnregisterListener removes all listeners", func(t *testing.T) {
var called bool

slistener := listener.NewResultListener(true, cache.NewInMermoryCache(), time.Now())
slistener := listener.NewResultListener(true, cache.NewInMermoryCache(time.Minute, time.Minute), time.Now())
slistener.RegisterListener(func(_ v1alpha2.ReportInterface, r v1alpha2.PolicyReportResult, b bool) {
called = true
})
Expand All @@ -121,7 +121,7 @@ func Test_ResultListener(t *testing.T) {
t.Run("ignore results with past timestamps", func(t *testing.T) {
var called bool

slistener := listener.NewResultListener(true, cache.NewInMermoryCache(), time.Now())
slistener := listener.NewResultListener(true, cache.NewInMermoryCache(time.Minute, time.Minute), time.Now())
slistener.RegisterListener(func(_ v1alpha2.ReportInterface, r v1alpha2.PolicyReportResult, b bool) {
called = true
})
Expand Down

0 comments on commit 1edff60

Please sign in to comment.