From 56e15f16d497829e07a09af64de811b11b7f6ead Mon Sep 17 00:00:00 2001 From: Brian McGee Date: Tue, 23 Jul 2024 20:55:45 +0100 Subject: [PATCH] wip: add cache tests Signed-off-by: Brian McGee --- cache/cache.go | 4 ++ cache/cache_test.go | 132 ++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 132 insertions(+), 4 deletions(-) diff --git a/cache/cache.go b/cache/cache.go index a469caa5..b6703cba 100644 --- a/cache/cache.go +++ b/cache/cache.go @@ -27,6 +27,10 @@ type Bucket[V any] struct { bucket *bolt.Bucket } +func (b *Bucket[V]) Size() int { + return b.bucket.Stats().KeyN +} + func (b *Bucket[V]) Get(key string) (*V, error) { bytes := b.bucket.Get([]byte(key)) if bytes == nil { diff --git a/cache/cache_test.go b/cache/cache_test.go index db5b425f..ec10af76 100644 --- a/cache/cache_test.go +++ b/cache/cache_test.go @@ -1,15 +1,17 @@ package cache import ( - "github.com/adrg/xdg" - "github.com/stretchr/testify/assert" "os" "strings" "testing" + "time" + + "github.com/adrg/xdg" + "github.com/stretchr/testify/require" ) func TestCache_Open(t *testing.T) { - as := assert.New(t) + as := require.New(t) tempDir := t.TempDir() xdgPrefix, err := xdg.CacheFile("") @@ -30,12 +32,134 @@ func TestCache_Open(t *testing.T) { _, err = os.Stat(path) as.NoError(err, "db path %s should still exist after closing the cache", path) - // open a temporary cache e.g. --no-cache + // open a temp cache e.g. --no-cache tempDir = t.TempDir() cache, err = Open(tempDir, true) as.NoError(err) + // close temp cache as.NoError(cache.Close()) _, err = os.Stat(cache.db.Path()) as.ErrorIs(err, os.ErrNotExist, "temp db path %s should not exist after closing the cache") } + +func TestCache_Update(t *testing.T) { + as := require.New(t) + + cache, err := Open(t.TempDir(), false) + as.NoError(err) + + now := time.Now() + + testData := map[string]map[string]*Entry{ + "paths": { + "foo": {Size: 0, Modified: now}, + "bar": {Size: 1, Modified: now.Add(-1 * time.Second)}, + "fizz/buzz": {Size: 1 << 16, Modified: now.Add(-1 * time.Minute)}, + }, + "formatters": { + "bla": {Size: 1 << 32, Modified: now.Add(-1 * time.Hour)}, + "foo/bar/baz": {Size: 1 << 24, Modified: now.Add(-24 * time.Hour)}, + }, + } + + putEntries := func(bucket *Bucket[Entry], err error) func(string) { + return func(name string) { + as.NoError(err) + for k, v := range testData[name] { + as.NoError(bucket.Put(k, v), "failed to put value") + } + } + } + + getEntries := func(bucket *Bucket[Entry], err error) func(string) { + return func(name string) { + as.NoError(err) + as.Equal(len(testData[name]), bucket.Size()) + for k, v := range testData[name] { + actual, err := bucket.Get(k) + as.NoError(err) + as.EqualExportedValues(*v, *actual) + } + } + } + + modifyEntries := func(bucket *Bucket[Entry], err error) func(string) { + return func(name string) { + entries := testData[name] + idx := 0 + for k := range entries { + switch idx { + case 0: + // delete the first entry + as.NoError(bucket.Delete(k)) + delete(entries, k) + case 1: + // change the second + entries[k] = &Entry{Size: 123, Modified: now.Add(-2 * time.Hour)} + as.NoError(bucket.Put(k, entries[k])) + default: + break + } + } + } + } + + clearEntries := func(bucket *Bucket[Entry], err error) { + as.NoError(err) + as.NoError(bucket.DeleteAll()) + } + + checkEmpty := func(bucket *Bucket[Entry], err error) { + as.NoError(err) + as.Equal(0, bucket.Size()) + } + + // insert the test data into the cache + err = cache.Update(func(tx *Tx) error { + putEntries(tx.Paths())("paths") + putEntries(tx.Formatters())("formatters") + return nil + }) + as.NoError(err) + + // read it back and check it matches + err = cache.View(func(tx *Tx) error { + getEntries(tx.Paths())("paths") + getEntries(tx.Formatters())("formatters") + return nil + }) + as.NoError(err) + + // delete and update some entries + err = cache.Update(func(tx *Tx) error { + modifyEntries(tx.Paths()) + modifyEntries(tx.Formatters()) + return nil + }) + as.NoError(err) + + // read them back and make sure they match the updated test data + err = cache.View(func(tx *Tx) error { + getEntries(tx.Paths())("paths") + getEntries(tx.Formatters())("formatters") + return nil + }) + as.NoError(err) + + // delete all + err = cache.Update(func(tx *Tx) error { + clearEntries(tx.Paths()) + clearEntries(tx.Formatters()) + return nil + }) + as.NoError(err) + + // check the cache is empty + err = cache.Update(func(tx *Tx) error { + checkEmpty(tx.Paths()) + checkEmpty(tx.Formatters()) + return nil + }) + as.NoError(err) +}