mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
* Initial refactoring work for plugins kvstore * Replace implementations for keystore and angularstore * Cleanup * add interface check * lint * fix storeKeyGetter not being called in namespacedstore set * Fix tests * Comments * Add tests * Fix invalid cap in ListKeys when store is empty * Update docstrings * Add setLastUpdatedOnDelete * Renamed DefaultStoreKeyGetterFunc, add TestDefaultStoreKeyGetter * Sort imports * PR review: removed last_updated key * PR review: Removed setLastUpdatedOnDelete * Re-added relevant tests * PR review: Removed SingleKeyStore * PR review: Removed custom marshaling support * Renamed marshaler.go to marshal.go * PR review: removed unused interfaces * PR review: Moved marshal into namespacedstore.go * PR review: removed storekeygetter * Removed unused file cachekvstore.go * Renamed NamespacedStore to CacheKvStore * removed todo
189 lines
5.2 KiB
Go
189 lines
5.2 KiB
Go
package cachekvstore
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"sort"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/grafana/grafana/pkg/infra/kvstore"
|
|
)
|
|
|
|
func TestNamespacedStore(t *testing.T) {
|
|
const namespace = "namespace"
|
|
|
|
t.Run("simple", func(t *testing.T) {
|
|
store := NewCacheKvStore(kvstore.NewFakeKVStore(), namespace)
|
|
|
|
t.Run("default last updated time is zero", func(t *testing.T) {
|
|
ts, err := store.GetLastUpdated(context.Background())
|
|
require.NoError(t, err)
|
|
require.Zero(t, ts)
|
|
})
|
|
|
|
t.Run("Get returns false if key does not exist", func(t *testing.T) {
|
|
_, ok, err := store.Get(context.Background(), "key")
|
|
require.NoError(t, err)
|
|
require.False(t, ok)
|
|
})
|
|
|
|
t.Run("Set sets the value and updates the last updated time", func(t *testing.T) {
|
|
ts, err := store.GetLastUpdated(context.Background())
|
|
require.NoError(t, err)
|
|
require.Zero(t, ts)
|
|
|
|
require.NoError(t, store.Set(context.Background(), "key", "value"))
|
|
ts, err = store.GetLastUpdated(context.Background())
|
|
require.NoError(t, err)
|
|
require.NotZero(t, ts)
|
|
require.WithinDuration(t, ts, time.Now(), time.Second*10)
|
|
|
|
v, ok, err := store.Get(context.Background(), "key")
|
|
require.NoError(t, err)
|
|
require.True(t, ok)
|
|
require.Equal(t, "value", v)
|
|
})
|
|
|
|
t.Run("Delete deletes the value", func(t *testing.T) {
|
|
// First store
|
|
require.NoError(t, store.Set(context.Background(), "key", "value"))
|
|
|
|
// Then read it
|
|
v, ok, err := store.Get(context.Background(), "key")
|
|
require.NoError(t, err)
|
|
require.True(t, ok)
|
|
require.Equal(t, "value", v)
|
|
|
|
// Delete it
|
|
require.NoError(t, store.Delete(context.Background(), "key"))
|
|
|
|
// Read it again
|
|
_, ok, err = store.Get(context.Background(), "key")
|
|
require.NoError(t, err)
|
|
require.False(t, ok)
|
|
})
|
|
|
|
t.Run("sets last updated on delete", func(t *testing.T) {
|
|
store := NewCacheKvStore(kvstore.NewFakeKVStore(), namespace)
|
|
ts, err := store.GetLastUpdated(context.Background())
|
|
require.NoError(t, err)
|
|
require.Zero(t, ts)
|
|
|
|
require.NoError(t, store.Delete(context.Background(), "key"))
|
|
|
|
ts, err = store.GetLastUpdated(context.Background())
|
|
require.NoError(t, err)
|
|
require.WithinDuration(t, time.Now(), ts, time.Second*10)
|
|
})
|
|
|
|
t.Run("last updated key is used in GetLastUpdated", func(t *testing.T) {
|
|
store := NewCacheKvStore(kvstore.NewFakeKVStore(), namespace)
|
|
|
|
// Set in underlying store
|
|
ts := time.Now()
|
|
require.NoError(t, store.kv.Set(context.Background(), keyLastUpdated, ts.Format(time.RFC3339)))
|
|
|
|
// Make sure we get the same value
|
|
storeTs, err := store.GetLastUpdated(context.Background())
|
|
require.NoError(t, err)
|
|
// Format to account for marshal/unmarshal differences
|
|
require.Equal(t, ts.Format(time.RFC3339), storeTs.Format(time.RFC3339))
|
|
})
|
|
|
|
t.Run("last updated key is used in SetLastUpdated", func(t *testing.T) {
|
|
store := NewCacheKvStore(kvstore.NewFakeKVStore(), namespace)
|
|
require.NoError(t, store.SetLastUpdated(context.Background()))
|
|
|
|
marshaledStoreTs, ok, err := store.kv.Get(context.Background(), keyLastUpdated)
|
|
require.NoError(t, err)
|
|
require.True(t, ok)
|
|
storeTs, err := time.Parse(time.RFC3339, marshaledStoreTs)
|
|
require.NoError(t, err)
|
|
require.WithinDuration(t, time.Now(), storeTs, time.Second*10)
|
|
})
|
|
|
|
t.Run("ListKeys", func(t *testing.T) {
|
|
t.Run("returns empty list if no keys", func(t *testing.T) {
|
|
keys, err := store.ListKeys(context.Background())
|
|
require.NoError(t, err)
|
|
require.Empty(t, keys)
|
|
})
|
|
|
|
t.Run("returns the keys", func(t *testing.T) {
|
|
expectedKeys := make([]string, 0, 10)
|
|
for i := 0; i < 10; i++ {
|
|
k := fmt.Sprintf("key-%d", i)
|
|
err := store.Set(context.Background(), k, fmt.Sprintf("value-%d", i))
|
|
expectedKeys = append(expectedKeys, k)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
keys, err := store.ListKeys(context.Background())
|
|
require.NoError(t, err)
|
|
|
|
sort.Strings(expectedKeys)
|
|
sort.Strings(keys)
|
|
|
|
require.Equal(t, expectedKeys, keys)
|
|
})
|
|
})
|
|
})
|
|
|
|
t.Run("prefix", func(t *testing.T) {
|
|
t.Run("no prefix", func(t *testing.T) {
|
|
store := NewCacheKvStore(kvstore.NewFakeKVStore(), namespace)
|
|
require.Equal(t, "k", store.storeKey("k"))
|
|
})
|
|
|
|
t.Run("prefix", func(t *testing.T) {
|
|
store := NewCacheKvStoreWithPrefix(kvstore.NewFakeKVStore(), namespace, "my-")
|
|
require.Equal(t, "my-k", store.storeKey("k"))
|
|
})
|
|
})
|
|
}
|
|
|
|
func TestMarshal(t *testing.T) {
|
|
t.Run("json", func(t *testing.T) {
|
|
// Other type (rather than string, []byte or fmt.Stringer) marshals to JSON.
|
|
var value struct {
|
|
A string `json:"a"`
|
|
B string `json:"b"`
|
|
}
|
|
expV, err := json.Marshal(value)
|
|
require.NoError(t, err)
|
|
|
|
v, err := marshal(value)
|
|
require.NoError(t, err)
|
|
require.Equal(t, string(expV), v)
|
|
})
|
|
|
|
t.Run("string", func(t *testing.T) {
|
|
v, err := marshal("value")
|
|
require.NoError(t, err)
|
|
require.Equal(t, "value", v)
|
|
})
|
|
|
|
t.Run("stringer", func(t *testing.T) {
|
|
var s stringer
|
|
v, err := marshal(s)
|
|
require.NoError(t, err)
|
|
require.Equal(t, s.String(), v)
|
|
})
|
|
|
|
t.Run("byte slice", func(t *testing.T) {
|
|
v, err := marshal([]byte("value"))
|
|
require.NoError(t, err)
|
|
require.Equal(t, "value", v)
|
|
})
|
|
}
|
|
|
|
type stringer struct{}
|
|
|
|
func (s stringer) String() string {
|
|
return "aaaa"
|
|
}
|