Files
sub2api/backend/internal/repository/plugin_kv_store_test.go
T

149 lines
4.8 KiB
Go

package repository
import (
"context"
"testing"
"time"
"github.com/alicebob/miniredis/v2"
"github.com/redis/go-redis/v9"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func newTestPluginKVStore(t *testing.T) (*pluginKVStore, *miniredis.Miniredis) {
t.Helper()
mr := miniredis.RunT(t)
rdb := redis.NewClient(&redis.Options{Addr: mr.Addr()})
store, ok := NewPluginKVStore(rdb).(*pluginKVStore)
require.True(t, ok)
return store, mr
}
func TestPluginKVStore_SetGetDelete(t *testing.T) {
store, _ := newTestPluginKVStore(t)
ctx := context.Background()
value, found, err := store.Get(ctx, "p1", "state", "k")
require.NoError(t, err)
assert.False(t, found)
assert.Nil(t, value)
require.NoError(t, store.Set(ctx, "p1", "state", "k", []byte("hello"), 0))
value, found, err = store.Get(ctx, "p1", "state", "k")
require.NoError(t, err)
assert.True(t, found)
assert.Equal(t, []byte("hello"), value)
require.NoError(t, store.Delete(ctx, "p1", "state", "k"))
_, found, err = store.Get(ctx, "p1", "state", "k")
require.NoError(t, err)
assert.False(t, found)
}
func TestPluginKVStore_TTLExpires(t *testing.T) {
store, mr := newTestPluginKVStore(t)
ctx := context.Background()
require.NoError(t, store.Set(ctx, "p1", "state", "k", []byte("v"), 30*time.Second))
full := store.fullKey("p1", "state", "k")
ttl := mr.TTL(full)
assert.Equal(t, 30*time.Second, ttl)
mr.FastForward(31 * time.Second)
_, found, err := store.Get(ctx, "p1", "state", "k")
require.NoError(t, err)
assert.False(t, found)
}
func TestPluginKVStore_PluginIsolation(t *testing.T) {
store, _ := newTestPluginKVStore(t)
ctx := context.Background()
require.NoError(t, store.Set(ctx, "p1", "state", "shared", []byte("one"), 0))
require.NoError(t, store.Set(ctx, "p2", "state", "shared", []byte("two"), 0))
v1, found, err := store.Get(ctx, "p1", "state", "shared")
require.NoError(t, err)
require.True(t, found)
assert.Equal(t, []byte("one"), v1)
v2, found, err := store.Get(ctx, "p2", "state", "shared")
require.NoError(t, err)
require.True(t, found)
assert.Equal(t, []byte("two"), v2)
}
func TestPluginKVStore_ListStripsPrefixAndHonorsLimit(t *testing.T) {
store, _ := newTestPluginKVStore(t)
ctx := context.Background()
for _, key := range []string{"account-1", "account-2", "account-3", "other"} {
require.NoError(t, store.Set(ctx, "p1", "state", key, []byte("x"), 0))
}
// 另一命名空间/插件的键不应混入结果。
require.NoError(t, store.Set(ctx, "p1", "other-ns", "account-9", []byte("x"), 0))
require.NoError(t, store.Set(ctx, "p2", "state", "account-9", []byte("x"), 0))
keys, err := store.List(ctx, "p1", "state", "account-", 100)
require.NoError(t, err)
assert.ElementsMatch(t, []string{"account-1", "account-2", "account-3"}, keys)
keys, err = store.List(ctx, "p1", "state", "", 100)
require.NoError(t, err)
assert.ElementsMatch(t, []string{"account-1", "account-2", "account-3", "other"}, keys)
keys, err = store.List(ctx, "p1", "state", "account-", 2)
require.NoError(t, err)
assert.Len(t, keys, 2)
keys, err = store.List(ctx, "p1", "state", "account-", 0)
require.NoError(t, err)
assert.Empty(t, keys)
}
// namespace 是另一 namespace 的前缀时(state vs state2),List 不能串味——内部键的 ':'
// 分隔符 + namespace 字符集排除 ':' 共同保证消歧。
func TestPluginKVStore_ListNamespacePrefixDisambiguation(t *testing.T) {
store, _ := newTestPluginKVStore(t)
ctx := context.Background()
require.NoError(t, store.Set(ctx, "p1", "state", "k1", []byte("a"), 0))
require.NoError(t, store.Set(ctx, "p1", "state2", "k2", []byte("b"), 0))
keys, err := store.List(ctx, "p1", "state", "", 100)
require.NoError(t, err)
assert.Equal(t, []string{"k1"}, keys)
keys, err = store.List(ctx, "p1", "state2", "", 100)
require.NoError(t, err)
assert.Equal(t, []string{"k2"}, keys)
}
func TestPluginKVStore_OverwriteResetsValueAndTTL(t *testing.T) {
store, mr := newTestPluginKVStore(t)
ctx := context.Background()
require.NoError(t, store.Set(ctx, "p1", "state", "k", []byte("v1"), 10*time.Second))
require.NoError(t, store.Set(ctx, "p1", "state", "k", []byte("v2"), 0))
value, found, err := store.Get(ctx, "p1", "state", "k")
require.NoError(t, err)
require.True(t, found)
assert.Equal(t, []byte("v2"), value)
// ttl=0 覆盖后应变为不过期。
assert.Equal(t, time.Duration(0), mr.TTL(store.fullKey("p1", "state", "k")))
}
func TestPluginKVStore_EmptyValueIsFound(t *testing.T) {
store, _ := newTestPluginKVStore(t)
ctx := context.Background()
require.NoError(t, store.Set(ctx, "p1", "state", "k", []byte{}, 0))
value, found, err := store.Get(ctx, "p1", "state", "k")
require.NoError(t, err)
assert.True(t, found)
assert.Empty(t, value)
}
func TestPluginKVStore_DeleteMissingIsNoError(t *testing.T) {
store, _ := newTestPluginKVStore(t)
require.NoError(t, store.Delete(context.Background(), "p1", "state", "missing"))
}