Files
threshold-plugin-aws/plugin/attributes_test.go
T
Chris Marchesi 07270a7457 plugin: add unknown attr checks, fix E2E test
This adds checks for unknown attributes through the entire plugin
lifecycle for all attributes, secrets, and persisted state.

It also fixes the E2E test which seems to have drifted.
2021-11-16 17:29:17 -08:00

424 lines
9.4 KiB
Go

package plugin
import (
"testing"
"time"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/types/known/structpb"
)
func TestGetCatalogAttributes(t *testing.T) {
cases := []struct {
name string
in *structpb.Struct
expected *CatalogAttributes
expectedErrContains string
}{
{
name: "missing region",
in: mustStruct(map[string]interface{}{}),
expectedErrContains: "missing required value \"region\"",
},
{
name: "bad value for disable_credential_rotation",
in: mustStruct(map[string]interface{}{
constRegion: "us-west-2",
constDisableCredentialRotation: "sure",
}),
expectedErrContains: "unexpected type for value \"disable_credential_rotation\": want bool, got string",
},
{
name: "unknown fields",
in: mustStruct(map[string]interface{}{
constRegion: "us-west-2",
"foo": true,
"bar": true,
}),
expectedErrContains: "unknown catalog attribute fields provided: bar, foo",
},
{
name: "default",
in: mustStruct(map[string]interface{}{
constRegion: "us-west-2",
}),
expected: &CatalogAttributes{
Region: "us-west-2",
DisableCredentialRotation: false,
},
},
{
name: "with disable_credential_rotation",
in: mustStruct(map[string]interface{}{
constRegion: "us-west-2",
constDisableCredentialRotation: true,
}),
expected: &CatalogAttributes{
Region: "us-west-2",
DisableCredentialRotation: true,
},
},
}
for _, tc := range cases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
require := require.New(t)
actual, err := getCatalogAttributes(tc.in)
if tc.expectedErrContains != "" {
require.Error(err)
require.Contains(err.Error(), tc.expectedErrContains)
return
}
require.NoError(err)
require.Equal(tc.expected, actual)
})
}
}
func TestGetCatalogSecrets(t *testing.T) {
cases := []struct {
name string
in *structpb.Struct
expected *CatalogSecrets
expectedErrContains string
}{
{
name: "missing access_key_id",
in: mustStruct(map[string]interface{}{}),
expectedErrContains: "missing required value \"access_key_id\"",
},
{
name: "missing secret_access_key",
in: mustStruct(map[string]interface{}{
constAccessKeyId: "foobar",
}),
expectedErrContains: "missing required value \"secret_access_key\"",
},
{
name: "unknown fields",
in: mustStruct(map[string]interface{}{
constAccessKeyId: "foobar",
constSecretAccessKey: "bazqux",
"foo": true,
"bar": true,
}),
expectedErrContains: "unknown catalog secret fields provided: bar, foo",
},
{
name: "good",
in: mustStruct(map[string]interface{}{
constAccessKeyId: "foobar",
constSecretAccessKey: "bazqux",
}),
expected: &CatalogSecrets{
AccessKeyId: "foobar",
SecretAccessKey: "bazqux",
},
},
}
for _, tc := range cases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
require := require.New(t)
actual, err := getCatalogSecrets(tc.in)
if tc.expectedErrContains != "" {
require.Error(err)
require.Contains(err.Error(), tc.expectedErrContains)
return
}
require.NoError(err)
require.Equal(tc.expected, actual)
})
}
}
func TestGetSetAttributes(t *testing.T) {
cases := []struct {
name string
in *structpb.Struct
expected *SetAttributes
expectedErrContains string
}{
{
name: "missing",
in: mustStruct(map[string]interface{}{}),
expected: &SetAttributes{},
},
{
name: "non-string-slice value",
in: mustStruct(map[string]interface{}{
constDescribeInstancesFilters: "zip=foo,bar",
}),
expected: &SetAttributes{
Filters: []string{"zip=foo,bar"},
},
},
{
name: "bad filter element value",
in: mustStruct(map[string]interface{}{
constDescribeInstancesFilters: []interface{}{1},
}),
expectedErrContains: "expected type 'string', got unconvertible type 'float64'",
},
{
name: "unknown fields",
in: mustStruct(map[string]interface{}{
"foo": true,
"bar": true,
}),
expectedErrContains: "unknown set attribute fields provided: bar, foo",
},
{
name: "good",
in: mustStruct(map[string]interface{}{
constDescribeInstancesFilters: []interface{}{
"foo=bar",
"zip=zap",
},
}),
expected: &SetAttributes{
Filters: []string{"foo=bar", "zip=zap"},
},
},
}
for _, tc := range cases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
require := require.New(t)
actual, err := getSetAttributes(tc.in)
if tc.expectedErrContains != "" {
require.Error(err)
require.Contains(err.Error(), tc.expectedErrContains)
return
}
require.NoError(err)
require.Equal(tc.expected, actual)
})
}
}
func TestGetStringValue(t *testing.T) {
cases := []struct {
name string
in *structpb.Struct
key string
required bool
expected string
expectedErr string
}{
{
name: "required missing",
in: mustStruct(map[string]interface{}{}),
key: "foo",
required: true,
expectedErr: "missing required value \"foo\"",
},
{
name: "optional missing",
in: mustStruct(map[string]interface{}{}),
key: "foo",
expected: "",
},
{
name: "non-string value",
in: mustStruct(map[string]interface{}{
"foo": 1,
}),
key: "foo",
expectedErr: "unexpected type for value \"foo\": want string, got float64",
},
{
name: "required empty",
in: mustStruct(map[string]interface{}{
"foo": "",
}),
key: "foo",
required: true,
expectedErr: "value \"foo\" cannot be empty",
},
{
name: "good",
in: mustStruct(map[string]interface{}{
"foo": "bar",
}),
key: "foo",
expected: "bar",
},
}
for _, tc := range cases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
require := require.New(t)
actual, err := getStringValue(tc.in, tc.key, tc.required)
if tc.expectedErr != "" {
require.EqualError(err, tc.expectedErr)
return
}
require.NoError(err)
require.Equal(tc.expected, actual)
})
}
}
func TestGetBoolValue(t *testing.T) {
cases := []struct {
name string
in *structpb.Struct
key string
required bool
expected bool
expectedErr string
}{
{
name: "required missing",
in: mustStruct(map[string]interface{}{}),
key: "foo",
required: true,
expectedErr: "missing required value \"foo\"",
},
{
name: "optional missing",
in: mustStruct(map[string]interface{}{}),
key: "foo",
expected: false,
},
{
name: "non-bool value",
in: mustStruct(map[string]interface{}{
"foo": "bar",
}),
key: "foo",
expectedErr: "unexpected type for value \"foo\": want bool, got string",
},
{
name: "good",
in: mustStruct(map[string]interface{}{
"foo": true,
}),
key: "foo",
expected: true,
},
}
for _, tc := range cases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
require := require.New(t)
actual, err := getBoolValue(tc.in, tc.key, tc.required)
if tc.expectedErr != "" {
require.EqualError(err, tc.expectedErr)
return
}
require.NoError(err)
require.Equal(tc.expected, actual)
})
}
}
func TestGetTimeValue(t *testing.T) {
staticTime := time.Now()
cases := []struct {
name string
in *structpb.Struct
key string
expected time.Time
expectedErr string
}{
{
name: "missing",
in: mustStruct(map[string]interface{}{}),
key: "foo",
expected: time.Time{},
},
{
name: "non-time value",
in: mustStruct(map[string]interface{}{
"foo": 1,
}),
key: "foo",
expectedErr: "unexpected type for value \"foo\": want string, got float64",
},
{
name: "bad parse",
in: mustStruct(map[string]interface{}{
"foo": "bar",
}),
key: "foo",
expectedErr: "could not parse time in value \"foo\": parsing time \"bar\" as \"2006-01-02T15:04:05.999999999Z07:00\": cannot parse \"bar\" as \"2006\"",
},
{
name: "good",
in: mustStruct(map[string]interface{}{
"foo": staticTime.Format(time.RFC3339Nano),
}),
key: "foo",
expected: func() time.Time {
u, err := time.Parse(time.RFC3339Nano, staticTime.Format(time.RFC3339Nano))
if err != nil {
panic(err)
}
return u
}(),
},
}
for _, tc := range cases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
require := require.New(t)
actual, err := getTimeValue(tc.in, tc.key)
if tc.expectedErr != "" {
require.EqualError(err, tc.expectedErr)
return
}
require.NoError(err)
require.Equal(tc.expected, actual)
})
}
}
func TestValidateRegion(t *testing.T) {
cases := []struct {
name string
in string
expectedErr string
}{
{
name: "invalid region",
in: "foobar",
expectedErr: "not a valid region: foobar",
},
{
name: "good",
in: "us-west-2",
expectedErr: "",
},
}
for _, tc := range cases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
require := require.New(t)
err := validateRegion(tc.in)
if tc.expectedErr != "" {
require.EqualError(err, tc.expectedErr)
return
}
require.NoError(err)
})
}
}