mirror of https://github.com/hashicorp/consul
114 lines
3.1 KiB
Go
114 lines
3.1 KiB
Go
|
package pbservice
|
||
|
|
||
|
import (
|
||
|
"os"
|
||
|
"reflect"
|
||
|
"strconv"
|
||
|
"testing"
|
||
|
"time"
|
||
|
|
||
|
"github.com/google/go-cmp/cmp"
|
||
|
"github.com/google/go-cmp/cmp/cmpopts"
|
||
|
fuzz "github.com/google/gofuzz"
|
||
|
|
||
|
"github.com/hashicorp/consul/agent/structs"
|
||
|
)
|
||
|
|
||
|
func TestNewCheckServiceNodeFromStructs_RoundTrip(t *testing.T) {
|
||
|
repeat(t, func(t *testing.T, fuzzer *fuzz.Fuzzer) {
|
||
|
fuzzer.Funcs(randInt32, randUint32, randInterface, randStructsUpstream, randEnterpriseMeta)
|
||
|
var target structs.CheckServiceNode
|
||
|
fuzzer.Fuzz(&target)
|
||
|
|
||
|
result, err := CheckServiceNodeToStructs(NewCheckServiceNodeFromStructs(&target))
|
||
|
if err != nil {
|
||
|
t.Fatalf("unexpected error: %s", err.Error())
|
||
|
}
|
||
|
assertEqual(t, &target, result)
|
||
|
})
|
||
|
}
|
||
|
|
||
|
func repeat(t *testing.T, fn func(t *testing.T, fuzzer *fuzz.Fuzzer)) {
|
||
|
reps := getEnvIntWithDefault(t, "TEST_REPEAT_COUNT", 5)
|
||
|
seed := getEnvIntWithDefault(t, "TEST_RANDOM_SEED", time.Now().UnixNano())
|
||
|
t.Logf("using seed %d for %d repetitions", seed, reps)
|
||
|
|
||
|
fuzzer := fuzz.NewWithSeed(seed)
|
||
|
for i := 0; i < int(reps); i++ {
|
||
|
t.Run(strconv.Itoa(i), func(t *testing.T) {
|
||
|
fn(t, fuzzer)
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func getEnvIntWithDefault(t *testing.T, key string, d int64) int64 {
|
||
|
t.Helper()
|
||
|
raw, ok := os.LookupEnv(key)
|
||
|
if !ok {
|
||
|
return d
|
||
|
}
|
||
|
v, err := strconv.Atoi(raw)
|
||
|
if err != nil {
|
||
|
t.Fatalf("invald value for %v: %v", key, err.Error())
|
||
|
}
|
||
|
return int64(v)
|
||
|
}
|
||
|
|
||
|
func assertEqual(t *testing.T, x, y interface{}) {
|
||
|
t.Helper()
|
||
|
if diff := cmp.Diff(x, y, cmpopts.EquateEmpty()); diff != "" {
|
||
|
t.Fatalf("assertion failed: values are not equal\n--- original\n+++ result\n\n%v", diff)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// randUint32 is a custom fuzzer function which limits all uints to 32 bits.
|
||
|
// This is necessary because the structs types use un-sized uints, however in
|
||
|
// practice they are constrained to 32 bits, and the protobuf types use (u)int32.
|
||
|
// The structs types use (u)int64 for any fields that require 64 bits.
|
||
|
func randUint32(i *uint, c fuzz.Continue) {
|
||
|
*i = uint(c.Rand.Uint32())
|
||
|
}
|
||
|
|
||
|
// see randUint32
|
||
|
func randInt32(i *int, c fuzz.Continue) {
|
||
|
*i = int(c.Rand.Int31())
|
||
|
}
|
||
|
|
||
|
// randStructsUpstream is a custom fuzzer function which skips generating values
|
||
|
// for fields enumerated in the ignore-fields annotation.
|
||
|
func randStructsUpstream(u *structs.Upstream, c fuzz.Continue) {
|
||
|
v := reflect.ValueOf(u).Elem()
|
||
|
for i := 0; i < v.NumField(); i++ {
|
||
|
switch v.Type().Field(i).Name {
|
||
|
case "IngressHosts":
|
||
|
continue
|
||
|
}
|
||
|
c.Fuzz(v.Field(i).Addr().Interface())
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// randInterface is a custom fuzzer function which generates random data for
|
||
|
// interface{} (most likely used in a map[string]interface{}).
|
||
|
// The random data does not contain any ints (or float32) because protobuf
|
||
|
// converts them to float64, which will cause the test to fail.
|
||
|
func randInterface(m *interface{}, c fuzz.Continue) {
|
||
|
switch c.Rand.Intn(6) {
|
||
|
case 0:
|
||
|
*m = nil
|
||
|
case 1:
|
||
|
*m = c.RandBool()
|
||
|
case 2:
|
||
|
*m = c.Rand.Float64()
|
||
|
case 3:
|
||
|
*m = c.RandString()
|
||
|
case 4:
|
||
|
*m = []interface{}{c.RandString(), c.RandBool(), nil, c.Rand.Float64()}
|
||
|
case 5:
|
||
|
*m = map[string]interface{}{
|
||
|
c.RandString(): c.RandString(),
|
||
|
c.RandString(): c.Rand.Float64(),
|
||
|
c.RandString(): nil,
|
||
|
}
|
||
|
}
|
||
|
}
|