params_test.go 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. package testing
  2. import (
  3. "net/url"
  4. "reflect"
  5. "testing"
  6. "time"
  7. "devel.mephi.ru/iacherepanov/openstack-gophercloud"
  8. th "devel.mephi.ru/iacherepanov/openstack-gophercloud/testhelper"
  9. )
  10. func TestMaybeString(t *testing.T) {
  11. testString := ""
  12. var expected *string
  13. actual := gophercloud.MaybeString(testString)
  14. th.CheckDeepEquals(t, expected, actual)
  15. testString = "carol"
  16. expected = &testString
  17. actual = gophercloud.MaybeString(testString)
  18. th.CheckDeepEquals(t, expected, actual)
  19. }
  20. func TestMaybeInt(t *testing.T) {
  21. testInt := 0
  22. var expected *int
  23. actual := gophercloud.MaybeInt(testInt)
  24. th.CheckDeepEquals(t, expected, actual)
  25. testInt = 4
  26. expected = &testInt
  27. actual = gophercloud.MaybeInt(testInt)
  28. th.CheckDeepEquals(t, expected, actual)
  29. }
  30. func TestBuildQueryString(t *testing.T) {
  31. type testVar string
  32. iFalse := false
  33. opts := struct {
  34. J int `q:"j"`
  35. R string `q:"r" required:"true"`
  36. C bool `q:"c"`
  37. S []string `q:"s"`
  38. TS []testVar `q:"ts"`
  39. TI []int `q:"ti"`
  40. F *bool `q:"f"`
  41. M map[string]string `q:"m"`
  42. }{
  43. J: 2,
  44. R: "red",
  45. C: true,
  46. S: []string{"one", "two", "three"},
  47. TS: []testVar{"a", "b"},
  48. TI: []int{1, 2},
  49. F: &iFalse,
  50. M: map[string]string{"k1": "success1"},
  51. }
  52. expected := &url.URL{RawQuery: "c=true&f=false&j=2&m=%7B%27k1%27%3A%27success1%27%7D&r=red&s=one&s=two&s=three&ti=1&ti=2&ts=a&ts=b"}
  53. actual, err := gophercloud.BuildQueryString(&opts)
  54. if err != nil {
  55. t.Errorf("Error building query string: %v", err)
  56. }
  57. th.CheckDeepEquals(t, expected, actual)
  58. opts = struct {
  59. J int `q:"j"`
  60. R string `q:"r" required:"true"`
  61. C bool `q:"c"`
  62. S []string `q:"s"`
  63. TS []testVar `q:"ts"`
  64. TI []int `q:"ti"`
  65. F *bool `q:"f"`
  66. M map[string]string `q:"m"`
  67. }{
  68. J: 2,
  69. C: true,
  70. }
  71. _, err = gophercloud.BuildQueryString(&opts)
  72. if err == nil {
  73. t.Errorf("Expected error: 'Required field not set'")
  74. }
  75. th.CheckDeepEquals(t, expected, actual)
  76. _, err = gophercloud.BuildQueryString(map[string]interface{}{"Number": 4})
  77. if err == nil {
  78. t.Errorf("Expected error: 'Options type is not a struct'")
  79. }
  80. }
  81. func TestBuildHeaders(t *testing.T) {
  82. testStruct := struct {
  83. Accept string `h:"Accept"`
  84. Num int `h:"Number" required:"true"`
  85. Style bool `h:"Style"`
  86. }{
  87. Accept: "application/json",
  88. Num: 4,
  89. Style: true,
  90. }
  91. expected := map[string]string{"Accept": "application/json", "Number": "4", "Style": "true"}
  92. actual, err := gophercloud.BuildHeaders(&testStruct)
  93. th.CheckNoErr(t, err)
  94. th.CheckDeepEquals(t, expected, actual)
  95. testStruct.Num = 0
  96. _, err = gophercloud.BuildHeaders(&testStruct)
  97. if err == nil {
  98. t.Errorf("Expected error: 'Required header not set'")
  99. }
  100. _, err = gophercloud.BuildHeaders(map[string]interface{}{"Number": 4})
  101. if err == nil {
  102. t.Errorf("Expected error: 'Options type is not a struct'")
  103. }
  104. }
  105. func TestQueriesAreEscaped(t *testing.T) {
  106. type foo struct {
  107. Name string `q:"something"`
  108. Shape string `q:"else"`
  109. }
  110. expected := &url.URL{RawQuery: "else=Triangl+e&something=blah%2B%3F%21%21foo"}
  111. actual, err := gophercloud.BuildQueryString(foo{Name: "blah+?!!foo", Shape: "Triangl e"})
  112. th.AssertNoErr(t, err)
  113. th.AssertDeepEquals(t, expected, actual)
  114. }
  115. func TestBuildRequestBody(t *testing.T) {
  116. type PasswordCredentials struct {
  117. Username string `json:"username" required:"true"`
  118. Password string `json:"password" required:"true"`
  119. }
  120. type TokenCredentials struct {
  121. ID string `json:"id,omitempty" required:"true"`
  122. }
  123. type orFields struct {
  124. Filler int `json:"filler,omitempty"`
  125. F1 int `json:"f1,omitempty" or:"F2"`
  126. F2 int `json:"f2,omitempty" or:"F1"`
  127. }
  128. // AuthOptions wraps a gophercloud AuthOptions in order to adhere to the AuthOptionsBuilder
  129. // interface.
  130. type AuthOptions struct {
  131. PasswordCredentials *PasswordCredentials `json:"passwordCredentials,omitempty" xor:"TokenCredentials"`
  132. // The TenantID and TenantName fields are optional for the Identity V2 API.
  133. // Some providers allow you to specify a TenantName instead of the TenantId.
  134. // Some require both. Your provider's authentication policies will determine
  135. // how these fields influence authentication.
  136. TenantID string `json:"tenantId,omitempty"`
  137. TenantName string `json:"tenantName,omitempty"`
  138. // TokenCredentials allows users to authenticate (possibly as another user) with an
  139. // authentication token ID.
  140. TokenCredentials *TokenCredentials `json:"token,omitempty" xor:"PasswordCredentials"`
  141. OrFields *orFields `json:"or_fields,omitempty"`
  142. }
  143. var successCases = []struct {
  144. opts AuthOptions
  145. expected map[string]interface{}
  146. }{
  147. {
  148. AuthOptions{
  149. PasswordCredentials: &PasswordCredentials{
  150. Username: "me",
  151. Password: "swordfish",
  152. },
  153. },
  154. map[string]interface{}{
  155. "auth": map[string]interface{}{
  156. "passwordCredentials": map[string]interface{}{
  157. "password": "swordfish",
  158. "username": "me",
  159. },
  160. },
  161. },
  162. },
  163. {
  164. AuthOptions{
  165. TokenCredentials: &TokenCredentials{
  166. ID: "1234567",
  167. },
  168. },
  169. map[string]interface{}{
  170. "auth": map[string]interface{}{
  171. "token": map[string]interface{}{
  172. "id": "1234567",
  173. },
  174. },
  175. },
  176. },
  177. }
  178. for _, successCase := range successCases {
  179. actual, err := gophercloud.BuildRequestBody(successCase.opts, "auth")
  180. th.AssertNoErr(t, err)
  181. th.AssertDeepEquals(t, successCase.expected, actual)
  182. }
  183. var failCases = []struct {
  184. opts AuthOptions
  185. expected error
  186. }{
  187. {
  188. AuthOptions{
  189. TenantID: "987654321",
  190. TenantName: "me",
  191. },
  192. gophercloud.ErrMissingInput{},
  193. },
  194. {
  195. AuthOptions{
  196. TokenCredentials: &TokenCredentials{
  197. ID: "1234567",
  198. },
  199. PasswordCredentials: &PasswordCredentials{
  200. Username: "me",
  201. Password: "swordfish",
  202. },
  203. },
  204. gophercloud.ErrMissingInput{},
  205. },
  206. {
  207. AuthOptions{
  208. PasswordCredentials: &PasswordCredentials{
  209. Password: "swordfish",
  210. },
  211. },
  212. gophercloud.ErrMissingInput{},
  213. },
  214. {
  215. AuthOptions{
  216. PasswordCredentials: &PasswordCredentials{
  217. Username: "me",
  218. Password: "swordfish",
  219. },
  220. OrFields: &orFields{
  221. Filler: 2,
  222. },
  223. },
  224. gophercloud.ErrMissingInput{},
  225. },
  226. }
  227. for _, failCase := range failCases {
  228. _, err := gophercloud.BuildRequestBody(failCase.opts, "auth")
  229. th.AssertDeepEquals(t, reflect.TypeOf(failCase.expected), reflect.TypeOf(err))
  230. }
  231. createdAt := time.Date(2018, 1, 4, 10, 00, 12, 0, time.UTC)
  232. var complexFields = struct {
  233. Username string `json:"username" required:"true"`
  234. CreatedAt *time.Time `json:"-"`
  235. }{
  236. Username: "jdoe",
  237. CreatedAt: &createdAt,
  238. }
  239. expectedComplexFields := map[string]interface{}{
  240. "username": "jdoe",
  241. }
  242. actual, err := gophercloud.BuildRequestBody(complexFields, "")
  243. th.AssertNoErr(t, err)
  244. th.AssertDeepEquals(t, expectedComplexFields, actual)
  245. }