shared.go 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. // Package shared contains shared step definitions that are used across integration tests
  2. package shared
  3. import (
  4. "encoding/json"
  5. "fmt"
  6. "os"
  7. "reflect"
  8. "regexp"
  9. "strconv"
  10. "strings"
  11. . "github.com/lsegal/gucumber"
  12. "github.com/stretchr/testify/assert"
  13. "github.com/aws/aws-sdk-go/aws"
  14. "github.com/aws/aws-sdk-go/aws/awserr"
  15. "github.com/aws/aws-sdk-go/aws/awsutil"
  16. "github.com/aws/aws-sdk-go/aws/defaults"
  17. )
  18. // Imported is a marker to ensure that this package's init() function gets
  19. // executed.
  20. //
  21. // To use this package, import it and add:
  22. //
  23. // var _ = shared.Imported
  24. const Imported = true
  25. func init() {
  26. if os.Getenv("DEBUG") != "" {
  27. defaults.DefaultConfig.LogLevel = aws.LogLevel(aws.LogDebug)
  28. }
  29. if os.Getenv("DEBUG_SIGNING") != "" {
  30. defaults.DefaultConfig.LogLevel = aws.LogLevel(aws.LogDebugWithSigning)
  31. }
  32. if os.Getenv("DEBUG_BODY") != "" {
  33. defaults.DefaultConfig.LogLevel = aws.LogLevel(aws.LogDebugWithSigning | aws.LogDebugWithHTTPBody)
  34. }
  35. When(`^I call the "(.+?)" API$`, func(op string) {
  36. call(op, nil, false)
  37. })
  38. When(`^I call the "(.+?)" API with:$`, func(op string, args [][]string) {
  39. call(op, args, false)
  40. })
  41. Then(`^the value at "(.+?)" should be a list$`, func(member string) {
  42. vals := awsutil.ValuesAtAnyPath(World["response"], member)
  43. assert.NotNil(T, vals)
  44. })
  45. Then(`^the response should contain a "(.+?)"$`, func(member string) {
  46. vals := awsutil.ValuesAtAnyPath(World["response"], member)
  47. assert.NotEmpty(T, vals)
  48. })
  49. When(`^I attempt to call the "(.+?)" API with:$`, func(op string, args [][]string) {
  50. call(op, args, true)
  51. })
  52. Then(`^I expect the response error code to be "(.+?)"$`, func(code string) {
  53. err, ok := World["error"].(awserr.Error)
  54. assert.True(T, ok, "no error returned")
  55. if ok {
  56. assert.Equal(T, code, err.Code())
  57. }
  58. })
  59. And(`^I expect the response error message to include:$`, func(data string) {
  60. err, ok := World["error"].(awserr.Error)
  61. assert.True(T, ok, "no error returned")
  62. if ok {
  63. assert.Contains(T, err.Message(), data)
  64. }
  65. })
  66. And(`^I expect the response error message to include one of:$`, func(table [][]string) {
  67. err, ok := World["error"].(awserr.Error)
  68. assert.True(T, ok, "no error returned")
  69. if ok {
  70. found := false
  71. for _, row := range table {
  72. if strings.Contains(err.Message(), row[0]) {
  73. found = true
  74. break
  75. }
  76. }
  77. assert.True(T, found, fmt.Sprintf("no error messages matched: \"%s\"", err.Message()))
  78. }
  79. })
  80. When(`^I call the "(.+?)" API with JSON:$`, func(s1 string, data string) {
  81. callWithJSON(s1, data, false)
  82. })
  83. When(`^I attempt to call the "(.+?)" API with JSON:$`, func(s1 string, data string) {
  84. callWithJSON(s1, data, true)
  85. })
  86. Then(`^the error code should be "(.+?)"$`, func(s1 string) {
  87. err, ok := World["error"].(awserr.Error)
  88. assert.True(T, ok, "no error returned")
  89. assert.Equal(T, s1, err.Code())
  90. })
  91. And(`^the error message should contain:$`, func(data string) {
  92. err, ok := World["error"].(awserr.Error)
  93. assert.True(T, ok, "no error returned")
  94. assert.Contains(T, err.Error(), data)
  95. })
  96. Then(`^the request should fail$`, func() {
  97. err, ok := World["error"].(awserr.Error)
  98. assert.True(T, ok, "no error returned")
  99. assert.Error(T, err)
  100. })
  101. }
  102. // findMethod finds the op operation on the v structure using a case-insensitive
  103. // lookup. Returns nil if no method is found.
  104. func findMethod(v reflect.Value, op string) *reflect.Value {
  105. t := v.Type()
  106. op = strings.ToLower(op)
  107. for i := 0; i < t.NumMethod(); i++ {
  108. name := t.Method(i).Name
  109. if strings.ToLower(name) == op {
  110. m := v.MethodByName(name)
  111. return &m
  112. }
  113. }
  114. return nil
  115. }
  116. // call calls an operation on World["client"] by the name op using the args
  117. // table of arguments to set.
  118. func call(op string, args [][]string, allowError bool) {
  119. v := reflect.ValueOf(World["client"])
  120. if m := findMethod(v, op); m != nil {
  121. t := m.Type()
  122. in := reflect.New(t.In(0).Elem())
  123. fillArgs(in, args)
  124. resps := m.Call([]reflect.Value{in})
  125. World["response"] = resps[0].Interface()
  126. World["error"] = resps[1].Interface()
  127. if !allowError {
  128. err, _ := World["error"].(error)
  129. assert.NoError(T, err)
  130. }
  131. } else {
  132. assert.Fail(T, "failed to find operation "+op)
  133. }
  134. }
  135. // reIsNum is a regular expression matching a numeric input (integer)
  136. var reIsNum = regexp.MustCompile(`^\d+$`)
  137. // reIsArray is a regular expression matching a list
  138. var reIsArray = regexp.MustCompile(`^\['.*?'\]$`)
  139. var reArrayElem = regexp.MustCompile(`'(.+?)'`)
  140. // fillArgs fills arguments on the input structure using the args table of
  141. // arguments.
  142. func fillArgs(in reflect.Value, args [][]string) {
  143. if args == nil {
  144. return
  145. }
  146. for _, row := range args {
  147. path := row[0]
  148. var val interface{} = row[1]
  149. if reIsArray.MatchString(row[1]) {
  150. quotedStrs := reArrayElem.FindAllString(row[1], -1)
  151. strs := make([]*string, len(quotedStrs))
  152. for i, e := range quotedStrs {
  153. str := e[1 : len(e)-1]
  154. strs[i] = &str
  155. }
  156. val = strs
  157. } else if reIsNum.MatchString(row[1]) { // handle integer values
  158. num, err := strconv.ParseInt(row[1], 10, 64)
  159. if err == nil {
  160. val = num
  161. }
  162. }
  163. awsutil.SetValueAtAnyPath(in.Interface(), path, val)
  164. }
  165. }
  166. func callWithJSON(op, j string, allowError bool) {
  167. v := reflect.ValueOf(World["client"])
  168. if m := findMethod(v, op); m != nil {
  169. t := m.Type()
  170. in := reflect.New(t.In(0).Elem())
  171. fillJSON(in, j)
  172. resps := m.Call([]reflect.Value{in})
  173. World["response"] = resps[0].Interface()
  174. World["error"] = resps[1].Interface()
  175. if !allowError {
  176. err, _ := World["error"].(error)
  177. assert.NoError(T, err)
  178. }
  179. } else {
  180. assert.Fail(T, "failed to find operation "+op)
  181. }
  182. }
  183. func fillJSON(in reflect.Value, j string) {
  184. d := json.NewDecoder(strings.NewReader(j))
  185. if err := d.Decode(in.Interface()); err != nil {
  186. panic(err)
  187. }
  188. }