2023-01-03 22:03:42 +00:00
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
2021-11-16 17:29:17 -08:00
package plugin
import (
2022-08-19 00:42:19 -04:00
"context"
2021-11-16 17:29:17 -08:00
"testing"
"time"
2022-08-19 00:42:19 -04:00
"github.com/google/go-cmp/cmp"
"github.com/hashicorp/boundary/sdk/pbs/plugin"
2021-11-16 17:29:17 -08:00
"github.com/stretchr/testify/require"
2022-03-10 11:00:57 -08:00
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
2022-08-19 00:42:19 -04:00
"google.golang.org/protobuf/testing/protocmp"
2021-11-16 17:29:17 -08:00
"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 ,
}),
2022-08-19 00:42:19 -04:00
expectedErrContains : "attributes.bar: unrecognized field, attributes.foo: unrecognized field" ,
2021-11-16 17:29:17 -08:00
},
{
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 )
2022-03-10 11:00:57 -08:00
require . Equal ( status . Code ( err ), codes . InvalidArgument )
2021-11-16 17:29:17 -08:00
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 ,
}),
2022-08-19 00:42:19 -04:00
expectedErrContains : "secrets.bar: unrecognized field, secrets.foo: unrecognized field" ,
2021-11-16 17:29:17 -08:00
},
{
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 )
2022-03-10 11:00:57 -08:00
require . Equal ( status . Code ( err ), codes . InvalidArgument )
2021-11-16 17:29:17 -08:00
return
}
require . NoError ( err )
require . Equal ( tc . expected , actual )
})
}
}
func TestGetSetAttributes ( t * testing . T ) {
cases := [] struct {
name string
in * structpb . Struct
2022-08-19 00:42:19 -04:00
normalized * structpb . Struct
2021-11-16 17:29:17 -08:00
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 ,
}),
2022-08-19 00:42:19 -04:00
expectedErrContains : "attributes.bar: unrecognized field, attributes.foo: unrecognized field" ,
2021-11-16 17:29:17 -08:00
},
{
name : "good" ,
in : mustStruct ( map [ string ] interface {}{
constDescribeInstancesFilters : [] interface {}{
"foo=bar" ,
"zip=zap" ,
},
}),
expected : & SetAttributes {
Filters : [] string { "foo=bar" , "zip=zap" },
},
},
2022-08-19 00:42:19 -04:00
{
name : "good with filter transform" ,
in : mustStruct ( map [ string ] interface {}{
constDescribeInstancesFilters : "foo=bar" ,
}),
normalized : mustStruct ( map [ string ] interface {}{
constDescribeInstancesFilters : [] interface {}{ "foo=bar" },
}),
expected : & SetAttributes {
Filters : [] string { "foo=bar" },
},
},
2021-11-16 17:29:17 -08:00
}
for _ , tc := range cases {
tc := tc
t . Run ( tc . name , func ( t * testing . T ) {
require := require . New ( t )
2022-08-19 00:42:19 -04:00
p := new ( AwsPlugin )
normalizedOut , err := p . NormalizeSetData ( context . Background (), & plugin . NormalizeSetDataRequest { Attributes : tc . in })
require . NoError ( err )
if tc . normalized != nil {
require . Empty ( cmp . Diff ( tc . normalized , normalizedOut . Attributes , protocmp . Transform ()))
}
tc . in = normalizedOut . Attributes
2021-11-16 17:29:17 -08:00
actual , err := getSetAttributes ( tc . in )
if tc . expectedErrContains != "" {
require . Error ( err )
require . Contains ( err . Error (), tc . expectedErrContains )
2022-03-10 11:00:57 -08:00
require . Equal ( status . Code ( err ), codes . InvalidArgument )
2021-11-16 17:29:17 -08:00
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 != "" {
2022-03-10 11:00:57 -08:00
require . Contains ( err . Error (), tc . expectedErr )
require . Equal ( status . Code ( err ), codes . InvalidArgument )
2021-11-16 17:29:17 -08:00
return
}
require . NoError ( err )
})
}
}