add completeness test for types with CacheInfo method (#9480)

include all fields when fuzzing in tests
split tests by struct type

Ensure the new value for the field is different

fuzzer.Fuzz could produce the same value again in some cases.

Use a custom fuzz function for QueryOptions. That type is an embedded struct in the request types
but only one of the fields is important to include in the cache key.

Move enterpriseMetaField to an oss file so that we can change it in enterprise.
This commit is contained in:
kevinkengne 2021-01-13 00:45:46 +00:00 committed by GitHub
parent 1b55fcef33
commit 18a12d6517
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 80 additions and 0 deletions

View File

@ -7,6 +7,8 @@ import (
"github.com/stretchr/testify/require"
)
var enterpriseMetaField = "EnterpriseMeta"
func TestServiceID_String(t *testing.T) {
t.Run("value", func(t *testing.T) {
sid := NewServiceID("the-id", &EnterpriseMeta{})

View File

@ -11,6 +11,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
fuzz "github.com/google/gofuzz"
"github.com/hashicorp/consul/acl"
"github.com/hashicorp/consul/agent/cache"
"github.com/hashicorp/consul/api"
@ -1528,6 +1530,82 @@ func TestStructs_validateMetaPair(t *testing.T) {
}
}
func TestDCSpecificRequestCacheInfoKey(t *testing.T) {
assertCacheInfoKeyIsComplete(t, &DCSpecificRequest{}, nil)
}
func TestNodeSpecificRequestCacheInfoKey(t *testing.T) {
assertCacheInfoKeyIsComplete(t, &NodeSpecificRequest{}, nil)
}
func TestServiceSpecificRequestCacheInfoKey(t *testing.T) {
ignoredFields := map[string]bool{
// TODO: should this filed be included?
"ServiceKind": true,
// TODO: this filed should be included: github.com/hashicorp/consul/pull/9436
"Ingress": true,
}
assertCacheInfoKeyIsComplete(t, &ServiceSpecificRequest{}, ignoredFields)
}
func TestServiceDumpRequestCacheInfoKey(t *testing.T) {
ignoredFields := map[string]bool{
// ServiceKind is only included when UseServiceKind=true
"ServiceKind": true,
}
assertCacheInfoKeyIsComplete(t, &ServiceDumpRequest{}, ignoredFields)
}
// cacheInfoIgnoredFields are fields that can be ignored in all cache.Request types
// because the cache itself includes these values in the cache key, or because
// they are options used to specify the cache operation, and are not part of the
// cache entry value.
var cacheInfoIgnoredFields = map[string]bool{
// Datacenter is part of the cache key added by the cache itself.
"Datacenter": true,
// QuerySource is always the same for every request a single agent, so it
// is excluded from the key.
"Source": true,
// EnterpriseMeta is an empty struct, so can not be included.
enterpriseMetaField: true,
}
func assertCacheInfoKeyIsComplete(t *testing.T, request cache.Request, ignoredFields map[string]bool) {
fuzzer := fuzz.NewWithSeed(time.Now().UnixNano())
fuzzer.Funcs(randQueryOptions)
fuzzer.Fuzz(request)
requestValue := reflect.ValueOf(request).Elem()
for i := 0; i < requestValue.NumField(); i++ {
originalKey := request.CacheInfo().Key
field := requestValue.Field(i)
fieldName := requestValue.Type().Field(i).Name
originalValue := field.Interface()
if cacheInfoIgnoredFields[fieldName] || ignoredFields[fieldName] {
continue
}
for i := 0; reflect.DeepEqual(originalValue, field.Interface()) && i < 20; i++ {
fuzzer.Fuzz(field.Addr().Interface())
}
key := request.CacheInfo().Key
if originalKey == key {
t.Fatalf("expected field %v to be represented in the CacheInfo.Key, %v change to %v",
fieldName,
originalValue,
field.Interface())
}
}
}
func randQueryOptions(o *QueryOptions, c fuzz.Continue) {
c.Fuzz(&o.Filter)
}
func TestSpecificServiceRequest_CacheInfo(t *testing.T) {
tests := []struct {
name string