231 lines
6.5 KiB
Go
231 lines
6.5 KiB
Go
|
// +build integration
|
||
|
|
||
|
// Package smoke contains shared step definitions that are used across integration tests
|
||
|
package smoke
|
||
|
|
||
|
import (
|
||
|
"encoding/json"
|
||
|
"fmt"
|
||
|
"os"
|
||
|
"reflect"
|
||
|
"regexp"
|
||
|
"strconv"
|
||
|
"strings"
|
||
|
|
||
|
"github.com/gucumber/gucumber"
|
||
|
"github.com/stretchr/testify/assert"
|
||
|
|
||
|
"github.com/aws/aws-sdk-go/aws"
|
||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||
|
"github.com/aws/aws-sdk-go/aws/awsutil"
|
||
|
"github.com/aws/aws-sdk-go/aws/session"
|
||
|
)
|
||
|
|
||
|
// Session is a shared session for all integration smoke tests to use.
|
||
|
var Session = session.Must(session.NewSession())
|
||
|
|
||
|
func init() {
|
||
|
logLevel := Session.Config.LogLevel
|
||
|
if os.Getenv("DEBUG") != "" {
|
||
|
logLevel = aws.LogLevel(aws.LogDebug)
|
||
|
}
|
||
|
if os.Getenv("DEBUG_SIGNING") != "" {
|
||
|
logLevel = aws.LogLevel(aws.LogDebugWithSigning)
|
||
|
}
|
||
|
if os.Getenv("DEBUG_BODY") != "" {
|
||
|
logLevel = aws.LogLevel(aws.LogDebugWithHTTPBody)
|
||
|
}
|
||
|
Session.Config.LogLevel = logLevel
|
||
|
|
||
|
gucumber.When(`^I call the "(.+?)" API$`, func(op string) {
|
||
|
call(op, nil, false)
|
||
|
})
|
||
|
|
||
|
gucumber.When(`^I call the "(.+?)" API with:$`, func(op string, args [][]string) {
|
||
|
call(op, args, false)
|
||
|
})
|
||
|
|
||
|
gucumber.Then(`^the value at "(.+?)" should be a list$`, func(member string) {
|
||
|
vals, _ := awsutil.ValuesAtPath(gucumber.World["response"], member)
|
||
|
assert.NotNil(gucumber.T, vals)
|
||
|
})
|
||
|
|
||
|
gucumber.Then(`^the response should contain a "(.+?)"$`, func(member string) {
|
||
|
vals, _ := awsutil.ValuesAtPath(gucumber.World["response"], member)
|
||
|
assert.NotEmpty(gucumber.T, vals)
|
||
|
})
|
||
|
|
||
|
gucumber.When(`^I attempt to call the "(.+?)" API with:$`, func(op string, args [][]string) {
|
||
|
call(op, args, true)
|
||
|
})
|
||
|
|
||
|
gucumber.Then(`^I expect the response error code to be "(.+?)"$`, func(code string) {
|
||
|
err, ok := gucumber.World["error"].(awserr.Error)
|
||
|
assert.True(gucumber.T, ok, "no error returned")
|
||
|
if ok {
|
||
|
assert.Equal(gucumber.T, code, err.Code(), "Error: %v", err)
|
||
|
}
|
||
|
})
|
||
|
|
||
|
gucumber.And(`^I expect the response error message to include:$`, func(data string) {
|
||
|
err, ok := gucumber.World["error"].(awserr.Error)
|
||
|
assert.True(gucumber.T, ok, "no error returned")
|
||
|
if ok {
|
||
|
assert.Contains(gucumber.T, err.Error(), data)
|
||
|
}
|
||
|
})
|
||
|
|
||
|
gucumber.And(`^I expect the response error message to include one of:$`, func(table [][]string) {
|
||
|
err, ok := gucumber.World["error"].(awserr.Error)
|
||
|
assert.True(gucumber.T, ok, "no error returned")
|
||
|
if ok {
|
||
|
found := false
|
||
|
for _, row := range table {
|
||
|
if strings.Contains(err.Error(), row[0]) {
|
||
|
found = true
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
|
||
|
assert.True(gucumber.T, found, fmt.Sprintf("no error messages matched: \"%s\"", err.Error()))
|
||
|
}
|
||
|
})
|
||
|
|
||
|
gucumber.And(`^I expect the response error message not be empty$`, func() {
|
||
|
err, ok := gucumber.World["error"].(awserr.Error)
|
||
|
assert.True(gucumber.T, ok, "no error returned")
|
||
|
assert.NotEmpty(gucumber.T, err.Message())
|
||
|
})
|
||
|
|
||
|
gucumber.When(`^I call the "(.+?)" API with JSON:$`, func(s1 string, data string) {
|
||
|
callWithJSON(s1, data, false)
|
||
|
})
|
||
|
|
||
|
gucumber.When(`^I attempt to call the "(.+?)" API with JSON:$`, func(s1 string, data string) {
|
||
|
callWithJSON(s1, data, true)
|
||
|
})
|
||
|
|
||
|
gucumber.Then(`^the error code should be "(.+?)"$`, func(s1 string) {
|
||
|
err, ok := gucumber.World["error"].(awserr.Error)
|
||
|
assert.True(gucumber.T, ok, "no error returned")
|
||
|
assert.Equal(gucumber.T, s1, err.Code())
|
||
|
})
|
||
|
|
||
|
gucumber.And(`^the error message should contain:$`, func(data string) {
|
||
|
err, ok := gucumber.World["error"].(awserr.Error)
|
||
|
assert.True(gucumber.T, ok, "no error returned")
|
||
|
assert.Contains(gucumber.T, err.Error(), data)
|
||
|
})
|
||
|
|
||
|
gucumber.Then(`^the request should fail$`, func() {
|
||
|
err, ok := gucumber.World["error"].(awserr.Error)
|
||
|
assert.True(gucumber.T, ok, "no error returned")
|
||
|
assert.Error(gucumber.T, err)
|
||
|
})
|
||
|
|
||
|
gucumber.Then(`^the request should be successful$`, func() {
|
||
|
err, ok := gucumber.World["error"].(awserr.Error)
|
||
|
assert.False(gucumber.T, ok, "error returned")
|
||
|
assert.NoError(gucumber.T, err)
|
||
|
})
|
||
|
}
|
||
|
|
||
|
// findMethod finds the op operation on the v structure using a case-insensitive
|
||
|
// lookup. Returns nil if no method is found.
|
||
|
func findMethod(v reflect.Value, op string) *reflect.Value {
|
||
|
t := v.Type()
|
||
|
op = strings.ToLower(op)
|
||
|
for i := 0; i < t.NumMethod(); i++ {
|
||
|
name := t.Method(i).Name
|
||
|
if strings.ToLower(name) == op {
|
||
|
m := v.MethodByName(name)
|
||
|
return &m
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// call calls an operation on gucumber.World["client"] by the name op using the args
|
||
|
// table of arguments to set.
|
||
|
func call(op string, args [][]string, allowError bool) {
|
||
|
v := reflect.ValueOf(gucumber.World["client"])
|
||
|
if m := findMethod(v, op); m != nil {
|
||
|
t := m.Type()
|
||
|
in := reflect.New(t.In(0).Elem())
|
||
|
fillArgs(in, args)
|
||
|
|
||
|
resps := m.Call([]reflect.Value{in})
|
||
|
gucumber.World["response"] = resps[0].Interface()
|
||
|
gucumber.World["error"] = resps[1].Interface()
|
||
|
|
||
|
if !allowError {
|
||
|
err, _ := gucumber.World["error"].(error)
|
||
|
assert.NoError(gucumber.T, err)
|
||
|
}
|
||
|
} else {
|
||
|
assert.Fail(gucumber.T, "failed to find operation "+op)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// reIsNum is a regular expression matching a numeric input (integer)
|
||
|
var reIsNum = regexp.MustCompile(`^\d+$`)
|
||
|
|
||
|
// reIsArray is a regular expression matching a list
|
||
|
var reIsArray = regexp.MustCompile(`^\['.*?'\]$`)
|
||
|
var reArrayElem = regexp.MustCompile(`'(.+?)'`)
|
||
|
|
||
|
// fillArgs fills arguments on the input structure using the args table of
|
||
|
// arguments.
|
||
|
func fillArgs(in reflect.Value, args [][]string) {
|
||
|
if args == nil {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
for _, row := range args {
|
||
|
path := row[0]
|
||
|
var val interface{} = row[1]
|
||
|
if reIsArray.MatchString(row[1]) {
|
||
|
quotedStrs := reArrayElem.FindAllString(row[1], -1)
|
||
|
strs := make([]*string, len(quotedStrs))
|
||
|
for i, e := range quotedStrs {
|
||
|
str := e[1 : len(e)-1]
|
||
|
strs[i] = &str
|
||
|
}
|
||
|
val = strs
|
||
|
} else if reIsNum.MatchString(row[1]) { // handle integer values
|
||
|
num, err := strconv.ParseInt(row[1], 10, 64)
|
||
|
if err == nil {
|
||
|
val = num
|
||
|
}
|
||
|
}
|
||
|
awsutil.SetValueAtPath(in.Interface(), path, val)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func callWithJSON(op, j string, allowError bool) {
|
||
|
v := reflect.ValueOf(gucumber.World["client"])
|
||
|
if m := findMethod(v, op); m != nil {
|
||
|
t := m.Type()
|
||
|
in := reflect.New(t.In(0).Elem())
|
||
|
fillJSON(in, j)
|
||
|
|
||
|
resps := m.Call([]reflect.Value{in})
|
||
|
gucumber.World["response"] = resps[0].Interface()
|
||
|
gucumber.World["error"] = resps[1].Interface()
|
||
|
|
||
|
if !allowError {
|
||
|
err, _ := gucumber.World["error"].(error)
|
||
|
assert.NoError(gucumber.T, err)
|
||
|
}
|
||
|
} else {
|
||
|
assert.Fail(gucumber.T, "failed to find operation "+op)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func fillJSON(in reflect.Value, j string) {
|
||
|
d := json.NewDecoder(strings.NewReader(j))
|
||
|
if err := d.Decode(in.Interface()); err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
}
|