Adding tests

Adds tests for:
- Cache clearing functionality
- Very basic schema generation
This commit is contained in:
Michael Bolot 2022-08-26 09:28:21 -05:00
parent 115eb31f57
commit b449b93643
2 changed files with 243 additions and 0 deletions

168
pkg/schema/factory_test.go Normal file
View File

@ -0,0 +1,168 @@
package schema
import (
"context"
"github.com/stretchr/testify/assert"
"testing"
"github.com/rancher/apiserver/pkg/types"
"github.com/rancher/wrangler/pkg/schemas"
k8sSchema "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apiserver/pkg/authentication/user"
)
const (
testGroup = "test.k8s.io"
testVersion = "v1"
)
type schemaTestConfig struct {
permissionVerbs []string
desiredResourceVerbs []string
desiredCollectionVerbs []string
errDesired bool
}
func TestSchemas(t *testing.T) {
tests := []struct {
name string
config schemaTestConfig
}{
{
name: "basic get schema test",
config: schemaTestConfig{
permissionVerbs: []string{"get"},
desiredResourceVerbs: []string{"GET"},
desiredCollectionVerbs: []string{"GET"},
errDesired: false,
},
},
}
for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
t.Parallel()
// test caching functionality
mockLookup := newMockAccessSetLookup()
userName := "testUser"
testUser := user.DefaultInfo{
Name: userName,
UID: userName,
Groups: []string{},
Extra: map[string][]string{},
}
collection := NewCollection(context.TODO(), types.EmptyAPISchemas(), mockLookup)
collection.schemas = map[string]*types.APISchema{"testCRD": makeSchema("testCRD")}
runSchemaTest(t, test.config, mockLookup, collection, &testUser)
})
}
}
func TestSchemaCache(t *testing.T) {
// Schemas are a frequently used resource. It's important that the cache doesn't have a leak given size/frequency of resource
tests := []struct {
name string
before schemaTestConfig
after schemaTestConfig
}{
{
name: "permissions increase, cache size same",
before: schemaTestConfig{
permissionVerbs: []string{"get"},
desiredResourceVerbs: []string{"GET"},
desiredCollectionVerbs: []string{"GET"},
errDesired: false,
},
after: schemaTestConfig{
permissionVerbs: []string{"get", "create", "delete"},
desiredResourceVerbs: []string{"GET", "DELETE"},
desiredCollectionVerbs: []string{"GET", "POST"},
errDesired: false,
},
},
{
name: "permissions decrease, cache size same",
before: schemaTestConfig{
permissionVerbs: []string{"get", "create", "delete"},
desiredResourceVerbs: []string{"GET", "DELETE"},
desiredCollectionVerbs: []string{"GET", "POST"},
errDesired: false,
},
after: schemaTestConfig{
permissionVerbs: []string{"get"},
desiredResourceVerbs: []string{"GET"},
desiredCollectionVerbs: []string{"GET"},
errDesired: false,
},
},
}
for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
t.Parallel()
// test caching functionality
mockLookup := newMockAccessSetLookup()
userName := "testUser"
testUser := user.DefaultInfo{
Name: userName,
UID: userName,
Groups: []string{},
Extra: map[string][]string{},
}
collection := NewCollection(context.TODO(), types.EmptyAPISchemas(), mockLookup)
collection.schemas = map[string]*types.APISchema{"testCRD": makeSchema("testCRD")}
runSchemaTest(t, test.before, mockLookup, collection, &testUser)
assert.Len(t, collection.cache.Keys(), 1, "expected cache to be size 1")
mockLookup.Clear()
runSchemaTest(t, test.after, mockLookup, collection, &testUser)
assert.Len(t, collection.cache.Keys(), 1, "expected cache to be size 1")
})
}
}
func runSchemaTest(t *testing.T, config schemaTestConfig, lookup *mockAccessSetLookup, collection *Collection, testUser user.Info) {
for _, verb := range config.permissionVerbs {
lookup.AddAccessForUser(testUser, verb, k8sSchema.GroupResource{Group: testGroup, Resource: "testCRD"}, "*", "*")
}
collection.schemas = map[string]*types.APISchema{"testCRD": makeSchema("testCRD")}
userSchemas, err := collection.Schemas(testUser)
if config.errDesired {
assert.Error(t, err, "expected error but none was found")
}
var testSchema *types.APISchema
for schemaName, userSchema := range userSchemas.Schemas {
if schemaName == "testCRD" {
testSchema = userSchema
}
}
assert.NotNil(t, testSchema, "expected a test schema, but was nil")
assert.Len(t, testSchema.ResourceMethods, len(config.desiredResourceVerbs), "did not get as many verbs as expected for resource methods")
assert.Len(t, testSchema.CollectionMethods, len(config.desiredCollectionVerbs), "did not get as many verbs as expected for resource methods")
for _, verb := range config.desiredResourceVerbs {
assert.Contains(t, testSchema.ResourceMethods, verb, "did not find %s in resource methods %v", verb, testSchema.ResourceMethods)
}
for _, verb := range config.desiredCollectionVerbs {
assert.Contains(t, testSchema.CollectionMethods, verb, "did not find %s in resource methods %v", verb, testSchema.CollectionMethods)
}
}
func makeSchema(resourceType string) *types.APISchema {
return &types.APISchema{
Schema: &schemas.Schema{
ID: resourceType,
CollectionMethods: []string{},
ResourceMethods: []string{},
ResourceFields: map[string]schemas.Field{
"name": {Type: "string"},
"value": {Type: "string"},
},
Attributes: map[string]interface{}{
"group": testGroup,
"version": testVersion,
"resource": resourceType,
"verbs": []string{"get", "list", "watch", "delete", "update", "create"},
},
},
}
}

75
pkg/schema/mock_test.go Normal file
View File

@ -0,0 +1,75 @@
package schema
import (
"crypto/sha256"
"encoding/hex"
"hash"
"github.com/rancher/steve/pkg/accesscontrol"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apiserver/pkg/authentication/user"
)
const (
insideSeparator = "&"
outsideSeparator = "%"
)
type mockAccessSetLookup struct {
accessSets map[string]*accesscontrol.AccessSet
currentHash map[string]hash.Hash
}
func newMockAccessSetLookup() *mockAccessSetLookup {
return &mockAccessSetLookup{
accessSets: map[string]*accesscontrol.AccessSet{},
currentHash: map[string]hash.Hash{},
}
}
func (m *mockAccessSetLookup) AccessFor(user user.Info) *accesscontrol.AccessSet {
if set, ok := m.accessSets[user.GetName()]; ok {
return set
}
return nil
}
func (m *mockAccessSetLookup) PurgeUserData(id string) {
var foundKey string
for key, value := range m.accessSets {
if value.ID == id {
foundKey = key
}
}
if foundKey != "" {
delete(m.accessSets, foundKey)
}
}
func (m *mockAccessSetLookup) AddAccessForUser(user user.Info, verb string, gr schema.GroupResource, namespace string, name string) {
currentAccessSet, ok := m.accessSets[user.GetName()]
var currentHash hash.Hash
if !ok {
currentAccessSet = &accesscontrol.AccessSet{}
currentHash = sha256.New()
} else {
currentHash = m.currentHash[currentAccessSet.ID]
}
currentAccessSet.Add(verb, gr, accesscontrol.Access{Namespace: namespace, ResourceName: name})
calculateAccessSetID(currentHash, verb, gr, namespace, name)
currentAccessSet.ID = hex.EncodeToString(currentHash.Sum(nil))
m.accessSets[user.GetName()] = currentAccessSet
m.currentHash[currentAccessSet.ID] = currentHash
}
func (m *mockAccessSetLookup) Clear() {
m.accessSets = map[string]*accesscontrol.AccessSet{}
m.currentHash = map[string]hash.Hash{}
}
func calculateAccessSetID(digest hash.Hash, verb string, gr schema.GroupResource, namespace string, name string) {
digest.Write([]byte(verb + insideSeparator))
digest.Write([]byte(gr.String() + insideSeparator))
digest.Write([]byte(namespace + insideSeparator))
digest.Write([]byte(name + outsideSeparator))
}