mirror of
https://github.com/grafana/grafana.git
synced 2025-07-30 00:34:27 +08:00

* Add datastore * too many slashes * lint * add metadata store * simplify meta * Add eventstore * golint * lint * Add datastore * too many slashes * lint * pr comments * extract ParseKey * readcloser * remove get prefix * use dedicated keys * parsekey * sameresource * unrelated * name * renmae tests * add key validation * fix tests * refactor a bit * lint * allow empty ns * get keys instead of list * rename the functions * refactor yield candidate * update test * unistore: add LastResourceVersion to datastore * lint * use map string * missing err check * fix
1459 lines
37 KiB
Go
1459 lines
37 KiB
Go
package resource
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"testing"
|
|
|
|
"github.com/bwmarrin/snowflake"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
var node, _ = snowflake.NewNode(1)
|
|
|
|
func setupTestDataStore(t *testing.T) *dataStore {
|
|
kv := setupTestKV(t)
|
|
return newDataStore(kv)
|
|
}
|
|
|
|
func TestNewDataStore(t *testing.T) {
|
|
ds := setupTestDataStore(t)
|
|
require.NotNil(t, ds)
|
|
}
|
|
|
|
func TestDataKey_String(t *testing.T) {
|
|
rv := int64(1934555792099250176)
|
|
tests := []struct {
|
|
name string
|
|
key DataKey
|
|
expected string
|
|
}{
|
|
{
|
|
name: "created key",
|
|
key: DataKey{
|
|
Namespace: "test-namespace",
|
|
Group: "test-group",
|
|
Resource: "test-resource",
|
|
Name: "test-name",
|
|
ResourceVersion: rv,
|
|
Action: DataActionCreated,
|
|
},
|
|
expected: "test-namespace/test-group/test-resource/test-name/1934555792099250176~created",
|
|
}, {
|
|
name: "updated key",
|
|
key: DataKey{
|
|
Namespace: "test-namespace",
|
|
Group: "test-group",
|
|
Resource: "test-resource",
|
|
Name: "test-name",
|
|
ResourceVersion: rv,
|
|
Action: DataActionUpdated,
|
|
},
|
|
expected: "test-namespace/test-group/test-resource/test-name/1934555792099250176~updated",
|
|
},
|
|
{
|
|
name: "deleted key",
|
|
key: DataKey{
|
|
Namespace: "test-namespace",
|
|
Group: "test-group",
|
|
Resource: "test-resource",
|
|
Name: "test-name",
|
|
ResourceVersion: rv,
|
|
Action: DataActionDeleted,
|
|
},
|
|
expected: "test-namespace/test-group/test-resource/test-name/1934555792099250176~deleted",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
actual := tt.key.String()
|
|
require.Equal(t, tt.expected, actual)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDataKey_Validate(t *testing.T) {
|
|
rv := int64(1234567890)
|
|
|
|
validKey := DataKey{
|
|
Namespace: "test-namespace",
|
|
Group: "test-group",
|
|
Resource: "test-resource",
|
|
Name: "test-name",
|
|
ResourceVersion: rv,
|
|
Action: DataActionCreated,
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
key DataKey
|
|
expectError bool
|
|
errorMsg string
|
|
}{
|
|
{
|
|
name: "valid key with created action",
|
|
key: validKey,
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "valid key with updated action",
|
|
key: DataKey{
|
|
Namespace: "test-namespace",
|
|
Group: "test-group",
|
|
Resource: "test-resource",
|
|
Name: "test-name",
|
|
ResourceVersion: rv,
|
|
Action: DataActionUpdated,
|
|
},
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "valid key with deleted action",
|
|
key: DataKey{
|
|
Namespace: "test-namespace",
|
|
Group: "test-group",
|
|
Resource: "test-resource",
|
|
Name: "test-name",
|
|
ResourceVersion: rv,
|
|
Action: DataActionDeleted,
|
|
},
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "valid key with dots and dashes",
|
|
key: DataKey{
|
|
Namespace: "test.namespace-with-dashes",
|
|
Group: "test.group-123",
|
|
Resource: "test-resource.v1",
|
|
Name: "test-name.with.dots",
|
|
ResourceVersion: rv,
|
|
Action: DataActionCreated,
|
|
},
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "valid key with single character names",
|
|
key: DataKey{
|
|
Namespace: "a",
|
|
Group: "b",
|
|
Resource: "c",
|
|
Name: "d",
|
|
ResourceVersion: rv,
|
|
Action: DataActionCreated,
|
|
},
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "valid key with numbers",
|
|
key: DataKey{
|
|
Namespace: "namespace123",
|
|
Group: "group456",
|
|
Resource: "resource789",
|
|
Name: "name000",
|
|
ResourceVersion: rv,
|
|
Action: DataActionCreated,
|
|
},
|
|
expectError: false,
|
|
},
|
|
// Invalid cases - empty fields
|
|
{
|
|
name: "invalid - empty namespace",
|
|
key: DataKey{
|
|
Namespace: "",
|
|
Group: "test-group",
|
|
Resource: "test-resource",
|
|
Name: "test-name",
|
|
ResourceVersion: rv,
|
|
Action: DataActionCreated,
|
|
},
|
|
expectError: true,
|
|
errorMsg: "namespace is required when group, resource, or name are provided",
|
|
},
|
|
{
|
|
name: "invalid - empty group",
|
|
key: DataKey{
|
|
Namespace: "test-namespace",
|
|
Group: "",
|
|
Resource: "test-resource",
|
|
Name: "test-name",
|
|
ResourceVersion: rv,
|
|
Action: DataActionCreated,
|
|
},
|
|
expectError: true,
|
|
errorMsg: "group is required when resource or name are provided",
|
|
},
|
|
{
|
|
name: "invalid - empty resource",
|
|
key: DataKey{
|
|
Namespace: "test-namespace",
|
|
Group: "test-group",
|
|
Resource: "",
|
|
Name: "test-name",
|
|
ResourceVersion: rv,
|
|
Action: DataActionCreated,
|
|
},
|
|
expectError: true,
|
|
errorMsg: "resource is required when name is provided",
|
|
},
|
|
{
|
|
name: "invalid - empty name",
|
|
key: DataKey{
|
|
Namespace: "test-namespace",
|
|
Group: "test-group",
|
|
Resource: "test-resource",
|
|
Name: "",
|
|
ResourceVersion: rv,
|
|
Action: DataActionCreated,
|
|
},
|
|
expectError: true,
|
|
errorMsg: "name cannot be empty",
|
|
},
|
|
{
|
|
name: "invalid - empty action",
|
|
key: DataKey{
|
|
Namespace: "test-namespace",
|
|
Group: "test-group",
|
|
Resource: "test-resource",
|
|
Name: "test-name",
|
|
ResourceVersion: rv,
|
|
Action: "",
|
|
},
|
|
expectError: true,
|
|
errorMsg: "action cannot be empty",
|
|
},
|
|
{
|
|
name: "invalid - all fields empty",
|
|
key: DataKey{
|
|
Namespace: "",
|
|
Group: "",
|
|
Resource: "",
|
|
Name: "",
|
|
ResourceVersion: rv,
|
|
Action: "",
|
|
},
|
|
expectError: true,
|
|
errorMsg: "namespace cannot be empty",
|
|
},
|
|
// Invalid cases - uppercase characters
|
|
{
|
|
name: "invalid - uppercase in namespace",
|
|
key: DataKey{
|
|
Namespace: "Test-Namespace",
|
|
Group: "test-group",
|
|
Resource: "test-resource",
|
|
Name: "test-name",
|
|
ResourceVersion: rv,
|
|
Action: DataActionCreated,
|
|
},
|
|
expectError: true,
|
|
errorMsg: "namespace 'Test-Namespace' is invalid",
|
|
},
|
|
{
|
|
name: "invalid - uppercase in group",
|
|
key: DataKey{
|
|
Namespace: "test-namespace",
|
|
Group: "Test-Group",
|
|
Resource: "test-resource",
|
|
Name: "test-name",
|
|
ResourceVersion: rv,
|
|
Action: DataActionCreated,
|
|
},
|
|
expectError: true,
|
|
errorMsg: "group 'Test-Group' is invalid",
|
|
},
|
|
{
|
|
name: "invalid - uppercase in resource",
|
|
key: DataKey{
|
|
Namespace: "test-namespace",
|
|
Group: "test-group",
|
|
Resource: "Test-Resource",
|
|
Name: "test-name",
|
|
ResourceVersion: rv,
|
|
Action: DataActionCreated,
|
|
},
|
|
expectError: true,
|
|
errorMsg: "resource 'Test-Resource' is invalid",
|
|
},
|
|
{
|
|
name: "invalid - uppercase in name",
|
|
key: DataKey{
|
|
Namespace: "test-namespace",
|
|
Group: "test-group",
|
|
Resource: "test-resource",
|
|
Name: "Test-Name",
|
|
ResourceVersion: rv,
|
|
Action: DataActionCreated,
|
|
},
|
|
expectError: true,
|
|
errorMsg: "name 'Test-Name' is invalid",
|
|
},
|
|
// Invalid cases - invalid characters
|
|
{
|
|
name: "invalid - underscore in namespace",
|
|
key: DataKey{
|
|
Namespace: "test_namespace",
|
|
Group: "test-group",
|
|
Resource: "test-resource",
|
|
Name: "test-name",
|
|
ResourceVersion: rv,
|
|
Action: DataActionCreated,
|
|
},
|
|
expectError: true,
|
|
errorMsg: "namespace 'test_namespace' is invalid",
|
|
},
|
|
{
|
|
name: "invalid - space in group",
|
|
key: DataKey{
|
|
Namespace: "test-namespace",
|
|
Group: "test group",
|
|
Resource: "test-resource",
|
|
Name: "test-name",
|
|
ResourceVersion: rv,
|
|
Action: DataActionCreated,
|
|
},
|
|
expectError: true,
|
|
errorMsg: "group 'test group' is invalid",
|
|
},
|
|
{
|
|
name: "invalid - special character in resource",
|
|
key: DataKey{
|
|
Namespace: "test-namespace",
|
|
Group: "test-group",
|
|
Resource: "test@resource",
|
|
Name: "test-name",
|
|
ResourceVersion: rv,
|
|
Action: DataActionCreated,
|
|
},
|
|
expectError: true,
|
|
errorMsg: "resource 'test@resource' is invalid",
|
|
},
|
|
{
|
|
name: "invalid - slash in name",
|
|
key: DataKey{
|
|
Namespace: "test-namespace",
|
|
Group: "test-group",
|
|
Resource: "test-resource",
|
|
Name: "test/name",
|
|
ResourceVersion: rv,
|
|
Action: DataActionCreated,
|
|
},
|
|
expectError: true,
|
|
errorMsg: "name 'test/name' is invalid",
|
|
},
|
|
// Invalid cases - start/end with invalid characters
|
|
{
|
|
name: "invalid - namespace starts with dash",
|
|
key: DataKey{
|
|
Namespace: "-test-namespace",
|
|
Group: "test-group",
|
|
Resource: "test-resource",
|
|
Name: "test-name",
|
|
ResourceVersion: rv,
|
|
Action: DataActionCreated,
|
|
},
|
|
expectError: true,
|
|
errorMsg: "namespace '-test-namespace' is invalid",
|
|
},
|
|
{
|
|
name: "invalid - group ends with dot",
|
|
key: DataKey{
|
|
Namespace: "test-namespace",
|
|
Group: "test-group.",
|
|
Resource: "test-resource",
|
|
Name: "test-name",
|
|
ResourceVersion: rv,
|
|
Action: DataActionCreated,
|
|
},
|
|
expectError: true,
|
|
errorMsg: "group 'test-group.' is invalid",
|
|
},
|
|
{
|
|
name: "invalid - resource starts with dot",
|
|
key: DataKey{
|
|
Namespace: "test-namespace",
|
|
Group: "test-group",
|
|
Resource: ".test-resource",
|
|
Name: "test-name",
|
|
ResourceVersion: rv,
|
|
Action: DataActionCreated,
|
|
},
|
|
expectError: true,
|
|
errorMsg: "resource '.test-resource' is invalid",
|
|
},
|
|
{
|
|
name: "invalid - name ends with dash",
|
|
key: DataKey{
|
|
Namespace: "test-namespace",
|
|
Group: "test-group",
|
|
Resource: "test-resource",
|
|
Name: "test-name-",
|
|
ResourceVersion: rv,
|
|
Action: DataActionCreated,
|
|
},
|
|
expectError: true,
|
|
errorMsg: "name 'test-name-' is invalid",
|
|
},
|
|
// Invalid cases - invalid action
|
|
{
|
|
name: "invalid - unknown action",
|
|
key: DataKey{
|
|
Namespace: "test-namespace",
|
|
Group: "test-group",
|
|
Resource: "test-resource",
|
|
Name: "test-name",
|
|
ResourceVersion: rv,
|
|
Action: DataAction("unknown"),
|
|
},
|
|
expectError: true,
|
|
errorMsg: "action 'unknown' is invalid",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
err := tt.key.Validate()
|
|
if tt.expectError {
|
|
require.Error(t, err)
|
|
if tt.errorMsg != "" {
|
|
require.Contains(t, err.Error(), tt.errorMsg)
|
|
}
|
|
} else {
|
|
require.NoError(t, err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestParseKey(t *testing.T) {
|
|
rv := node.Generate()
|
|
|
|
tests := []struct {
|
|
name string
|
|
key string
|
|
expected DataKey
|
|
expectError bool
|
|
}{
|
|
{
|
|
name: "valid normal key",
|
|
key: "test-namespace/test-group/test-resource/test-name/" + rv.String() + "~created",
|
|
expected: DataKey{
|
|
Namespace: "test-namespace",
|
|
Group: "test-group",
|
|
Resource: "test-resource",
|
|
Name: "test-name",
|
|
ResourceVersion: rv.Int64(),
|
|
Action: DataActionCreated,
|
|
},
|
|
},
|
|
{
|
|
name: "valid deleted key",
|
|
key: "test-namespace/test-group/test-resource/test-name/" + rv.String() + "~deleted",
|
|
expected: DataKey{
|
|
Namespace: "test-namespace",
|
|
Group: "test-group",
|
|
Resource: "test-resource",
|
|
Name: "test-name",
|
|
ResourceVersion: rv.Int64(),
|
|
Action: DataActionDeleted,
|
|
},
|
|
},
|
|
{
|
|
name: "invalid key - too short",
|
|
key: "test",
|
|
expectError: true,
|
|
},
|
|
{
|
|
name: "invalid key - too many slashes",
|
|
key: "test-namespace/test-group/test-resource/test-name/1934555792099250176~created/extra-slash",
|
|
expectError: true,
|
|
},
|
|
{
|
|
name: "invalid key - invalid uuid",
|
|
key: "test-namespace/test-group/test-resource/test-name/invalid-uuid",
|
|
expectError: true,
|
|
},
|
|
{
|
|
name: "invalid key - too many dashes in uuid part",
|
|
key: "test-namespace/test-group/test-resource/test-name/uuid-part-extra-dash",
|
|
expectError: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
actual, err := ParseKey(tt.key)
|
|
|
|
if tt.expectError {
|
|
require.Error(t, err)
|
|
} else {
|
|
require.NoError(t, err)
|
|
require.Equal(t, tt.expected, actual)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDataStore_Save_And_Get(t *testing.T) {
|
|
ds := setupTestDataStore(t)
|
|
ctx := context.Background()
|
|
|
|
rv := node.Generate()
|
|
|
|
testKey := DataKey{
|
|
Namespace: "test-namespace",
|
|
Group: "test-group",
|
|
Resource: "test-resource",
|
|
Name: "test-name",
|
|
ResourceVersion: rv.Int64(),
|
|
Action: DataActionCreated,
|
|
}
|
|
testValue := bytes.NewReader([]byte("test-value"))
|
|
|
|
t.Run("save and get normal key", func(t *testing.T) {
|
|
err := ds.Save(ctx, testKey, testValue)
|
|
require.NoError(t, err)
|
|
|
|
result, err := ds.Get(ctx, testKey)
|
|
require.NoError(t, err)
|
|
|
|
// Read the content and compare
|
|
resultBytes, err := io.ReadAll(result)
|
|
require.NoError(t, err)
|
|
require.Equal(t, []byte("test-value"), resultBytes)
|
|
})
|
|
|
|
t.Run("save and get deleted key", func(t *testing.T) {
|
|
deletedKey := testKey
|
|
deletedKey.Action = DataActionDeleted
|
|
deletedValue := bytes.NewReader([]byte("deleted-value"))
|
|
|
|
err := ds.Save(ctx, deletedKey, deletedValue)
|
|
require.NoError(t, err)
|
|
|
|
result, err := ds.Get(ctx, deletedKey)
|
|
require.NoError(t, err)
|
|
|
|
// Read the content and compare
|
|
resultBytes, err := io.ReadAll(result)
|
|
require.NoError(t, err)
|
|
require.Equal(t, []byte("deleted-value"), resultBytes)
|
|
})
|
|
|
|
t.Run("get non-existent key", func(t *testing.T) {
|
|
rv := node.Generate()
|
|
|
|
nonExistentKey := DataKey{
|
|
Namespace: "non-existent",
|
|
Group: "test-group",
|
|
Resource: "test-resource",
|
|
Name: "test-name",
|
|
ResourceVersion: rv.Int64(),
|
|
Action: DataActionCreated,
|
|
}
|
|
|
|
_, err := ds.Get(ctx, nonExistentKey)
|
|
require.Error(t, err)
|
|
require.Equal(t, ErrNotFound, err)
|
|
})
|
|
}
|
|
|
|
func TestDataStore_Delete(t *testing.T) {
|
|
ds := setupTestDataStore(t)
|
|
ctx := context.Background()
|
|
|
|
rv := node.Generate()
|
|
|
|
testKey := DataKey{
|
|
Namespace: "test-namespace",
|
|
Group: "test-group",
|
|
Resource: "test-resource",
|
|
Name: "test-name",
|
|
ResourceVersion: rv.Int64(),
|
|
Action: DataActionCreated,
|
|
}
|
|
testValue := bytes.NewReader([]byte("test-value"))
|
|
|
|
t.Run("delete existing key", func(t *testing.T) {
|
|
// First save the key
|
|
err := ds.Save(ctx, testKey, testValue)
|
|
require.NoError(t, err)
|
|
|
|
// Verify it exists
|
|
_, err = ds.Get(ctx, testKey)
|
|
require.NoError(t, err)
|
|
|
|
// Delete it
|
|
err = ds.Delete(ctx, testKey)
|
|
require.NoError(t, err)
|
|
|
|
// Verify it's gone
|
|
_, err = ds.Get(ctx, testKey)
|
|
require.Error(t, err)
|
|
require.Equal(t, ErrNotFound, err)
|
|
})
|
|
|
|
t.Run("delete non-existent key", func(t *testing.T) {
|
|
nonExistentKey := DataKey{
|
|
Namespace: "non-existent",
|
|
Group: "test-group",
|
|
Resource: "test-resource",
|
|
Name: "test-name",
|
|
ResourceVersion: rv.Int64(),
|
|
Action: DataActionCreated,
|
|
}
|
|
|
|
err := ds.Delete(ctx, nonExistentKey)
|
|
require.Error(t, err)
|
|
require.Equal(t, ErrNotFound, err)
|
|
})
|
|
}
|
|
|
|
func TestDataStore_List(t *testing.T) {
|
|
ds := setupTestDataStore(t)
|
|
ctx := context.Background()
|
|
|
|
resourceKey := ListRequestKey{
|
|
Namespace: "test-namespace",
|
|
Group: "test-group",
|
|
Resource: "test-resource",
|
|
Name: "test-name",
|
|
}
|
|
|
|
// Create test data
|
|
rv1 := node.Generate()
|
|
rv2 := node.Generate()
|
|
testValue1 := bytes.NewReader([]byte("test-value-1"))
|
|
testValue2 := bytes.NewReader([]byte("test-value-2"))
|
|
|
|
dataKey1 := DataKey{
|
|
Namespace: resourceKey.Namespace,
|
|
Group: resourceKey.Group,
|
|
Resource: resourceKey.Resource,
|
|
Name: resourceKey.Name,
|
|
ResourceVersion: rv1.Int64(),
|
|
Action: DataActionCreated,
|
|
}
|
|
|
|
dataKey2 := DataKey{
|
|
Namespace: resourceKey.Namespace,
|
|
Group: resourceKey.Group,
|
|
Resource: resourceKey.Resource,
|
|
Name: resourceKey.Name,
|
|
ResourceVersion: rv2.Int64(),
|
|
Action: DataActionCreated,
|
|
}
|
|
|
|
t.Run("list multiple keys", func(t *testing.T) {
|
|
// Save test data
|
|
err := ds.Save(ctx, dataKey1, testValue1)
|
|
require.NoError(t, err)
|
|
|
|
err = ds.Save(ctx, dataKey2, testValue2)
|
|
require.NoError(t, err)
|
|
|
|
// List the data
|
|
var results []DataKey
|
|
for key, err := range ds.Keys(ctx, resourceKey) {
|
|
require.NoError(t, err)
|
|
results = append(results, key)
|
|
}
|
|
|
|
// Verify results
|
|
require.Len(t, results, 2)
|
|
|
|
// Check first result
|
|
result1 := results[0]
|
|
require.Equal(t, rv1.Int64(), result1.ResourceVersion)
|
|
require.Equal(t, resourceKey.Namespace, result1.Namespace)
|
|
require.Equal(t, resourceKey.Group, result1.Group)
|
|
require.Equal(t, resourceKey.Resource, result1.Resource)
|
|
require.Equal(t, DataActionCreated, result1.Action)
|
|
|
|
// Check second result
|
|
result2 := results[1]
|
|
require.Equal(t, rv2.Int64(), result2.ResourceVersion)
|
|
require.Equal(t, resourceKey.Namespace, result2.Namespace)
|
|
require.Equal(t, resourceKey.Group, result2.Group)
|
|
require.Equal(t, resourceKey.Resource, result2.Resource)
|
|
require.Equal(t, DataActionCreated, result2.Action)
|
|
})
|
|
|
|
t.Run("list empty", func(t *testing.T) {
|
|
emptyResourceKey := ListRequestKey{
|
|
Namespace: "empty-namespace",
|
|
Group: "empty-group",
|
|
Resource: "empty-resource",
|
|
Name: "empty-name",
|
|
}
|
|
|
|
var results []DataKey
|
|
for key, err := range ds.Keys(ctx, emptyResourceKey) {
|
|
require.NoError(t, err)
|
|
results = append(results, key)
|
|
}
|
|
|
|
require.Len(t, results, 0)
|
|
})
|
|
|
|
t.Run("list with deleted keys", func(t *testing.T) {
|
|
deletedResourceKey := ListRequestKey{
|
|
Namespace: "deleted-namespace",
|
|
Group: "deleted-group",
|
|
Resource: "deleted-resource",
|
|
Name: "deleted-name",
|
|
}
|
|
|
|
rv3 := node.Generate()
|
|
testValue3 := bytes.NewReader([]byte("deleted-value"))
|
|
|
|
deletedKey := DataKey{
|
|
Namespace: deletedResourceKey.Namespace,
|
|
Group: deletedResourceKey.Group,
|
|
Resource: deletedResourceKey.Resource,
|
|
Name: deletedResourceKey.Name,
|
|
ResourceVersion: rv3.Int64(),
|
|
Action: DataActionDeleted,
|
|
}
|
|
|
|
// Save deleted key
|
|
err := ds.Save(ctx, deletedKey, testValue3)
|
|
require.NoError(t, err)
|
|
|
|
// List should include deleted keys
|
|
var results []DataKey
|
|
for key, err := range ds.Keys(ctx, deletedResourceKey) {
|
|
require.NoError(t, err)
|
|
results = append(results, key)
|
|
}
|
|
|
|
require.Len(t, results, 1)
|
|
require.Equal(t, rv3.Int64(), results[0].ResourceVersion)
|
|
require.Equal(t, DataActionDeleted, results[0].Action)
|
|
})
|
|
}
|
|
|
|
func TestDataStore_Integration(t *testing.T) {
|
|
ds := setupTestDataStore(t)
|
|
ctx := context.Background()
|
|
|
|
t.Run("full lifecycle test", func(t *testing.T) {
|
|
resourceKey := ListRequestKey{
|
|
Namespace: "integration-ns",
|
|
Group: "integration-group",
|
|
Resource: "integration-resource",
|
|
Name: "integration-name",
|
|
}
|
|
|
|
rv1 := node.Generate()
|
|
rv2 := node.Generate()
|
|
rv3 := node.Generate()
|
|
|
|
// Create multiple versions
|
|
versions := []struct {
|
|
rv int64
|
|
value io.Reader
|
|
}{
|
|
{rv1.Int64(), bytes.NewReader([]byte("version-1"))},
|
|
{rv2.Int64(), bytes.NewReader([]byte("version-2"))},
|
|
{rv3.Int64(), bytes.NewReader([]byte("version-3"))},
|
|
}
|
|
|
|
// Save all versions
|
|
for _, version := range versions {
|
|
dataKey := DataKey{
|
|
Namespace: resourceKey.Namespace,
|
|
Group: resourceKey.Group,
|
|
Resource: resourceKey.Resource,
|
|
Name: resourceKey.Name,
|
|
ResourceVersion: version.rv,
|
|
Action: DataActionUpdated,
|
|
}
|
|
|
|
err := ds.Save(ctx, dataKey, version.value)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
// List all versions
|
|
var results []DataKey
|
|
for key, err := range ds.Keys(ctx, resourceKey) {
|
|
require.NoError(t, err)
|
|
results = append(results, key)
|
|
}
|
|
|
|
require.Len(t, results, 3)
|
|
|
|
// Delete one version
|
|
deleteKey := DataKey{
|
|
Namespace: resourceKey.Namespace,
|
|
Group: resourceKey.Group,
|
|
Resource: resourceKey.Resource,
|
|
Name: resourceKey.Name,
|
|
ResourceVersion: versions[1].rv,
|
|
Action: DataActionUpdated,
|
|
}
|
|
|
|
err := ds.Delete(ctx, deleteKey)
|
|
require.NoError(t, err)
|
|
|
|
// Verify it's gone
|
|
_, err = ds.Get(ctx, deleteKey)
|
|
require.Equal(t, ErrNotFound, err)
|
|
|
|
// List should now have 2 items
|
|
results = nil
|
|
for key, err := range ds.Keys(ctx, resourceKey) {
|
|
require.NoError(t, err)
|
|
results = append(results, key)
|
|
}
|
|
|
|
require.Len(t, results, 2)
|
|
|
|
// Verify remaining items
|
|
remainingUUIDs := make(map[int64]bool)
|
|
for _, result := range results {
|
|
remainingUUIDs[result.ResourceVersion] = true
|
|
}
|
|
|
|
require.True(t, remainingUUIDs[versions[0].rv])
|
|
require.False(t, remainingUUIDs[versions[1].rv]) // deleted
|
|
require.True(t, remainingUUIDs[versions[2].rv])
|
|
})
|
|
}
|
|
|
|
func TestDataStore_Keys(t *testing.T) {
|
|
ds := setupTestDataStore(t)
|
|
ctx := context.Background()
|
|
|
|
resourceKey := ListRequestKey{
|
|
Namespace: "test-namespace",
|
|
Group: "test-group",
|
|
Resource: "test-resource",
|
|
Name: "test-name",
|
|
}
|
|
|
|
// Create test data
|
|
rv1 := node.Generate()
|
|
rv2 := node.Generate()
|
|
rv3 := node.Generate()
|
|
testValue1 := bytes.NewReader([]byte("test-value-1"))
|
|
testValue2 := bytes.NewReader([]byte("test-value-2"))
|
|
testValue3 := bytes.NewReader([]byte("test-value-3"))
|
|
|
|
dataKey1 := DataKey{
|
|
Namespace: resourceKey.Namespace,
|
|
Group: resourceKey.Group,
|
|
Resource: resourceKey.Resource,
|
|
Name: resourceKey.Name,
|
|
ResourceVersion: rv1.Int64(),
|
|
Action: DataActionCreated,
|
|
}
|
|
|
|
dataKey2 := DataKey{
|
|
Namespace: resourceKey.Namespace,
|
|
Group: resourceKey.Group,
|
|
Resource: resourceKey.Resource,
|
|
Name: resourceKey.Name,
|
|
ResourceVersion: rv2.Int64(),
|
|
Action: DataActionUpdated,
|
|
}
|
|
|
|
dataKey3 := DataKey{
|
|
Namespace: resourceKey.Namespace,
|
|
Group: resourceKey.Group,
|
|
Resource: resourceKey.Resource,
|
|
Name: resourceKey.Name,
|
|
ResourceVersion: rv3.Int64(),
|
|
Action: DataActionDeleted,
|
|
}
|
|
|
|
t.Run("keys with multiple entries", func(t *testing.T) {
|
|
// Save test data
|
|
err := ds.Save(ctx, dataKey1, testValue1)
|
|
require.NoError(t, err)
|
|
|
|
err = ds.Save(ctx, dataKey2, testValue2)
|
|
require.NoError(t, err)
|
|
|
|
err = ds.Save(ctx, dataKey3, testValue3)
|
|
require.NoError(t, err)
|
|
|
|
// Get keys
|
|
var keys []DataKey
|
|
for key, err := range ds.Keys(ctx, resourceKey) {
|
|
require.NoError(t, err)
|
|
keys = append(keys, key)
|
|
}
|
|
|
|
// Verify results
|
|
require.Len(t, keys, 3)
|
|
|
|
// Verify all keys are present
|
|
expectedKeys := []DataKey{
|
|
dataKey1,
|
|
dataKey2,
|
|
dataKey3,
|
|
}
|
|
|
|
for _, expectedKey := range expectedKeys {
|
|
require.Contains(t, keys, expectedKey)
|
|
}
|
|
})
|
|
|
|
t.Run("keys with empty result", func(t *testing.T) {
|
|
emptyResourceKey := ListRequestKey{
|
|
Namespace: "empty-namespace",
|
|
Group: "empty-group",
|
|
Resource: "empty-resource",
|
|
Name: "empty-name",
|
|
}
|
|
|
|
var keys []DataKey
|
|
for key, err := range ds.Keys(ctx, emptyResourceKey) {
|
|
require.NoError(t, err)
|
|
keys = append(keys, key)
|
|
}
|
|
|
|
require.Len(t, keys, 0)
|
|
})
|
|
|
|
t.Run("keys with partial prefix matching", func(t *testing.T) {
|
|
// Create keys with different names but same namespace/group/resource
|
|
partialKey := ListRequestKey{
|
|
Namespace: resourceKey.Namespace,
|
|
Group: resourceKey.Group,
|
|
Resource: resourceKey.Resource,
|
|
// Name is empty, so it should match all names
|
|
}
|
|
|
|
rv4 := node.Generate()
|
|
dataKey4 := DataKey{
|
|
Namespace: resourceKey.Namespace,
|
|
Group: resourceKey.Group,
|
|
Resource: resourceKey.Resource,
|
|
Name: "different-name",
|
|
ResourceVersion: rv4.Int64(),
|
|
Action: DataActionCreated,
|
|
}
|
|
|
|
err := ds.Save(ctx, dataKey4, io.NopCloser(bytes.NewReader([]byte("different-value"))))
|
|
require.NoError(t, err)
|
|
|
|
var keys []DataKey
|
|
for key, err := range ds.Keys(ctx, partialKey) {
|
|
require.NoError(t, err)
|
|
keys = append(keys, key)
|
|
}
|
|
|
|
// Should include all keys with matching namespace/group/resource
|
|
require.Len(t, keys, 4) // 3 from previous test + 1 new one
|
|
|
|
// Verify the new key is included
|
|
require.Contains(t, keys, dataKey4)
|
|
})
|
|
|
|
t.Run("keys with namespace only prefix", func(t *testing.T) {
|
|
// Create keys with different groups but same namespace
|
|
namespaceOnlyKey := ListRequestKey{
|
|
Namespace: resourceKey.Namespace,
|
|
// Group, Resource, Name are empty
|
|
}
|
|
|
|
rv5 := node.Generate()
|
|
dataKey5 := DataKey{
|
|
Namespace: resourceKey.Namespace,
|
|
Group: "different-group",
|
|
Resource: "different-resource",
|
|
Name: "different-name",
|
|
ResourceVersion: rv5.Int64(),
|
|
Action: DataActionCreated,
|
|
}
|
|
|
|
err := ds.Save(ctx, dataKey5, io.NopCloser(bytes.NewReader([]byte("namespace-only-value"))))
|
|
require.NoError(t, err)
|
|
|
|
var keys []DataKey
|
|
for key, err := range ds.Keys(ctx, namespaceOnlyKey) {
|
|
require.NoError(t, err)
|
|
keys = append(keys, key)
|
|
}
|
|
|
|
// Should include all keys with matching namespace
|
|
require.Len(t, keys, 5) // 4 from previous tests + 1 new one
|
|
|
|
// Verify the new key is included
|
|
require.Contains(t, keys, dataKey5)
|
|
})
|
|
|
|
t.Run("keys with empty namespace", func(t *testing.T) {
|
|
// Group, Resource, Name are provided but will be ignored due to validation
|
|
emptyNamespaceKey := ListRequestKey{
|
|
Namespace: "",
|
|
Group: "test-group",
|
|
Resource: "test-resource",
|
|
Name: "test-name",
|
|
}
|
|
|
|
var keys []DataKey
|
|
var hasError bool
|
|
for key, err := range ds.Keys(ctx, emptyNamespaceKey) {
|
|
if err != nil {
|
|
hasError = true
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "namespace is required")
|
|
break
|
|
}
|
|
keys = append(keys, key)
|
|
}
|
|
|
|
// Should get an error due to validation
|
|
require.True(t, hasError, "Expected an error due to empty namespace with other fields provided")
|
|
})
|
|
}
|
|
|
|
func TestDataStore_ValidationEnforced(t *testing.T) {
|
|
ds := setupTestDataStore(t)
|
|
ctx := context.Background()
|
|
|
|
// Create an invalid key
|
|
invalidKey := DataKey{
|
|
Namespace: "Invalid-Namespace", // uppercase is invalid
|
|
Group: "test-group",
|
|
Resource: "test-resource",
|
|
Name: "test-name",
|
|
ResourceVersion: 123456789,
|
|
Action: DataActionCreated,
|
|
}
|
|
|
|
testValue := io.NopCloser(bytes.NewReader([]byte("test-value")))
|
|
|
|
t.Run("Get with invalid key returns validation error", func(t *testing.T) {
|
|
_, err := ds.Get(ctx, invalidKey)
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "invalid data key")
|
|
require.Contains(t, err.Error(), "namespace 'Invalid-Namespace' is invalid")
|
|
})
|
|
|
|
t.Run("Save with invalid key returns validation error", func(t *testing.T) {
|
|
err := ds.Save(ctx, invalidKey, testValue)
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "invalid data key")
|
|
require.Contains(t, err.Error(), "namespace 'Invalid-Namespace' is invalid")
|
|
})
|
|
|
|
t.Run("Delete with invalid key returns validation error", func(t *testing.T) {
|
|
err := ds.Delete(ctx, invalidKey)
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "invalid data key")
|
|
require.Contains(t, err.Error(), "namespace 'Invalid-Namespace' is invalid")
|
|
})
|
|
|
|
// Test another type of invalid key
|
|
emptyFieldKey := DataKey{
|
|
Namespace: "",
|
|
Group: "test-group",
|
|
Resource: "test-resource",
|
|
Name: "test-name",
|
|
ResourceVersion: 123456789,
|
|
Action: DataActionCreated,
|
|
}
|
|
|
|
t.Run("Get with empty namespace returns validation error", func(t *testing.T) {
|
|
_, err := ds.Get(ctx, emptyFieldKey)
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "invalid data key")
|
|
require.Contains(t, err.Error(), "namespace is required")
|
|
})
|
|
|
|
t.Run("Save with empty namespace returns validation error", func(t *testing.T) {
|
|
err := ds.Save(ctx, emptyFieldKey, testValue)
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "invalid data key")
|
|
require.Contains(t, err.Error(), "namespace is required")
|
|
})
|
|
|
|
t.Run("Delete with empty namespace returns validation error", func(t *testing.T) {
|
|
err := ds.Delete(ctx, emptyFieldKey)
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "invalid data key")
|
|
require.Contains(t, err.Error(), "namespace is required")
|
|
})
|
|
}
|
|
|
|
func TestListRequestKey_Validate(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
key ListRequestKey
|
|
expectError bool
|
|
errorMsg string
|
|
}{
|
|
{
|
|
name: "valid - all fields provided",
|
|
key: ListRequestKey{
|
|
Namespace: "test-namespace",
|
|
Group: "test-group",
|
|
Resource: "test-resource",
|
|
Name: "test-name",
|
|
},
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "valid - only namespace",
|
|
key: ListRequestKey{
|
|
Namespace: "test-namespace",
|
|
},
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "valid - namespace and group",
|
|
key: ListRequestKey{
|
|
Namespace: "test-namespace",
|
|
Group: "test-group",
|
|
},
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "valid - namespace, group, and resource",
|
|
key: ListRequestKey{
|
|
Namespace: "test-namespace",
|
|
Group: "test-group",
|
|
Resource: "test-resource",
|
|
},
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "valid - all empty",
|
|
key: ListRequestKey{},
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "valid - with dots and dashes",
|
|
key: ListRequestKey{
|
|
Namespace: "test.namespace-123",
|
|
Group: "test-group.v1",
|
|
Resource: "test-resource",
|
|
Name: "test.name-456",
|
|
},
|
|
expectError: false,
|
|
},
|
|
// Invalid hierarchical cases
|
|
{
|
|
name: "invalid - group without namespace",
|
|
key: ListRequestKey{
|
|
Group: "test-group",
|
|
},
|
|
expectError: true,
|
|
errorMsg: "namespace is required when group, resource, or name are provided",
|
|
},
|
|
{
|
|
name: "invalid - resource without namespace",
|
|
key: ListRequestKey{
|
|
Resource: "test-resource",
|
|
},
|
|
expectError: true,
|
|
errorMsg: "namespace is required when group, resource, or name are provided",
|
|
},
|
|
{
|
|
name: "invalid - name without namespace",
|
|
key: ListRequestKey{
|
|
Name: "test-name",
|
|
},
|
|
expectError: true,
|
|
errorMsg: "namespace is required when group, resource, or name are provided",
|
|
},
|
|
{
|
|
name: "invalid - resource without group",
|
|
key: ListRequestKey{
|
|
Namespace: "test-namespace",
|
|
Resource: "test-resource",
|
|
},
|
|
expectError: true,
|
|
errorMsg: "group is required when resource or name are provided",
|
|
},
|
|
{
|
|
name: "invalid - name without group",
|
|
key: ListRequestKey{
|
|
Namespace: "test-namespace",
|
|
Name: "test-name",
|
|
},
|
|
expectError: true,
|
|
errorMsg: "group is required when resource or name are provided",
|
|
},
|
|
{
|
|
name: "invalid - name without resource",
|
|
key: ListRequestKey{
|
|
Namespace: "test-namespace",
|
|
Group: "test-group",
|
|
Name: "test-name",
|
|
},
|
|
expectError: true,
|
|
errorMsg: "resource is required when name is provided",
|
|
},
|
|
// Invalid naming cases
|
|
{
|
|
name: "invalid - uppercase in namespace",
|
|
key: ListRequestKey{
|
|
Namespace: "Test-Namespace",
|
|
},
|
|
expectError: true,
|
|
errorMsg: "namespace 'Test-Namespace' is invalid",
|
|
},
|
|
{
|
|
name: "invalid - uppercase in group",
|
|
key: ListRequestKey{
|
|
Namespace: "test-namespace",
|
|
Group: "Test-Group",
|
|
},
|
|
expectError: true,
|
|
errorMsg: "group 'Test-Group' is invalid",
|
|
},
|
|
{
|
|
name: "invalid - uppercase in resource",
|
|
key: ListRequestKey{
|
|
Namespace: "test-namespace",
|
|
Group: "test-group",
|
|
Resource: "Test-Resource",
|
|
},
|
|
expectError: true,
|
|
errorMsg: "resource 'Test-Resource' is invalid",
|
|
},
|
|
{
|
|
name: "invalid - uppercase in name",
|
|
key: ListRequestKey{
|
|
Namespace: "test-namespace",
|
|
Group: "test-group",
|
|
Resource: "test-resource",
|
|
Name: "Test-Name",
|
|
},
|
|
expectError: true,
|
|
errorMsg: "name 'Test-Name' is invalid",
|
|
},
|
|
{
|
|
name: "invalid - underscore in namespace",
|
|
key: ListRequestKey{
|
|
Namespace: "test_namespace",
|
|
},
|
|
expectError: true,
|
|
errorMsg: "namespace 'test_namespace' is invalid",
|
|
},
|
|
{
|
|
name: "invalid - starts with dash",
|
|
key: ListRequestKey{
|
|
Namespace: "-test-namespace",
|
|
},
|
|
expectError: true,
|
|
errorMsg: "namespace '-test-namespace' is invalid",
|
|
},
|
|
{
|
|
name: "invalid - ends with dot",
|
|
key: ListRequestKey{
|
|
Namespace: "test-namespace",
|
|
Group: "test-group.",
|
|
},
|
|
expectError: true,
|
|
errorMsg: "group 'test-group.' is invalid",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
err := tt.key.Validate()
|
|
if tt.expectError {
|
|
require.Error(t, err)
|
|
if tt.errorMsg != "" {
|
|
require.Contains(t, err.Error(), tt.errorMsg)
|
|
}
|
|
} else {
|
|
require.NoError(t, err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestListRequestKey_Prefix(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
key ListRequestKey
|
|
expected string
|
|
}{
|
|
{
|
|
name: "all fields provided",
|
|
key: ListRequestKey{
|
|
Namespace: "test-namespace",
|
|
Group: "test-group",
|
|
Resource: "test-resource",
|
|
Name: "test-name",
|
|
},
|
|
expected: "test-namespace/test-group/test-resource/test-name/",
|
|
},
|
|
{
|
|
name: "name is empty",
|
|
key: ListRequestKey{
|
|
Namespace: "test-namespace",
|
|
Group: "test-group",
|
|
Resource: "test-resource",
|
|
Name: "",
|
|
},
|
|
expected: "test-namespace/test-group/test-resource/",
|
|
},
|
|
{
|
|
name: "resource is empty",
|
|
key: ListRequestKey{
|
|
Namespace: "test-namespace",
|
|
Group: "test-group",
|
|
Resource: "",
|
|
Name: "",
|
|
},
|
|
expected: "test-namespace/test-group/",
|
|
},
|
|
{
|
|
name: "only namespace provided",
|
|
key: ListRequestKey{
|
|
Namespace: "test-namespace",
|
|
Group: "",
|
|
Resource: "",
|
|
Name: "",
|
|
},
|
|
expected: "test-namespace/",
|
|
},
|
|
{
|
|
name: "all fields empty",
|
|
key: ListRequestKey{
|
|
Namespace: "",
|
|
Group: "",
|
|
Resource: "",
|
|
Name: "",
|
|
},
|
|
expected: "",
|
|
},
|
|
{
|
|
name: "fields with special characters",
|
|
key: ListRequestKey{
|
|
Namespace: "test-namespace-with-dashes",
|
|
Group: "test.group.with.dots",
|
|
Resource: "test-resource",
|
|
Name: "test-name-with-multiple.special-chars",
|
|
},
|
|
expected: "test-namespace-with-dashes/test.group.with.dots/test-resource/test-name-with-multiple.special-chars/",
|
|
},
|
|
{
|
|
name: "invalid key still produces prefix",
|
|
key: ListRequestKey{
|
|
Namespace: "Test-Namespace", // invalid but we assume validity in Prefix
|
|
},
|
|
expected: "Test-Namespace/",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
actual := tt.key.Prefix()
|
|
require.Equal(t, tt.expected, actual)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDataStore_LastResourceVersion(t *testing.T) {
|
|
ds := setupTestDataStore(t)
|
|
ctx := context.Background()
|
|
|
|
t.Run("returns last resource version for existing data", func(t *testing.T) {
|
|
resourceKey := ListRequestKey{
|
|
Namespace: "test-namespace",
|
|
Group: "test-group",
|
|
Resource: "test-resource",
|
|
Name: "test-name",
|
|
}
|
|
|
|
// Create test data with multiple versions
|
|
rv1 := node.Generate()
|
|
rv2 := node.Generate()
|
|
rv3 := node.Generate()
|
|
|
|
versions := []int64{
|
|
rv1.Int64(),
|
|
rv2.Int64(),
|
|
rv3.Int64(),
|
|
}
|
|
|
|
// Save all versions
|
|
for _, version := range versions {
|
|
dataKey := DataKey{
|
|
Namespace: resourceKey.Namespace,
|
|
Group: resourceKey.Group,
|
|
Resource: resourceKey.Resource,
|
|
Name: resourceKey.Name,
|
|
ResourceVersion: version,
|
|
Action: DataActionCreated,
|
|
}
|
|
|
|
err := ds.Save(ctx, dataKey, bytes.NewReader([]byte(fmt.Sprintf("version-%d", version))))
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
// Get the last resource version
|
|
lastKey, err := ds.LastResourceVersion(ctx, resourceKey)
|
|
require.NoError(t, err)
|
|
|
|
// Verify the result
|
|
require.Equal(t, resourceKey.Namespace, lastKey.Namespace)
|
|
require.Equal(t, resourceKey.Group, lastKey.Group)
|
|
require.Equal(t, resourceKey.Resource, lastKey.Resource)
|
|
require.Equal(t, resourceKey.Name, lastKey.Name)
|
|
require.Equal(t, DataActionCreated, lastKey.Action)
|
|
|
|
require.Equal(t, rv3.Int64(), lastKey.ResourceVersion)
|
|
})
|
|
|
|
t.Run("returns error for non-existent resource", func(t *testing.T) {
|
|
nonExistentKey := ListRequestKey{
|
|
Namespace: "non-existent-namespace",
|
|
Group: "non-existent-group",
|
|
Resource: "non-existent-resource",
|
|
Name: "non-existent-name",
|
|
}
|
|
|
|
_, err := ds.LastResourceVersion(ctx, nonExistentKey)
|
|
require.Error(t, err)
|
|
require.Equal(t, ErrNotFound, err)
|
|
})
|
|
|
|
t.Run("returns error for empty required fields", func(t *testing.T) {
|
|
testCases := map[string]ListRequestKey{
|
|
"empty namespace": {
|
|
Namespace: "",
|
|
Group: "test-group",
|
|
Resource: "test-resource",
|
|
Name: "test-name",
|
|
},
|
|
"empty group": {
|
|
Namespace: "test-namespace",
|
|
Group: "",
|
|
Resource: "test-resource",
|
|
Name: "test-name",
|
|
},
|
|
"empty resource": {
|
|
Namespace: "test-namespace",
|
|
Group: "test-group",
|
|
Resource: "",
|
|
Name: "test-name",
|
|
},
|
|
"empty name": {
|
|
Namespace: "test-namespace",
|
|
Group: "test-group",
|
|
Resource: "test-resource",
|
|
Name: "",
|
|
},
|
|
}
|
|
|
|
for name, key := range testCases {
|
|
t.Run(name, func(t *testing.T) {
|
|
_, err := ds.LastResourceVersion(ctx, key)
|
|
require.Error(t, err)
|
|
})
|
|
}
|
|
})
|
|
}
|