blob: 6789a5a7d87f592a6ef17d8a9bc6feabb80bd822 [file] [log] [blame]
Jon Perritt255b6f82014-09-30 16:07:50 -05001package gophercloud
2
3import (
4 "net/url"
Jon Perrittc3e04b62014-10-06 16:17:49 -05005 "reflect"
Jon Perritt255b6f82014-09-30 16:07:50 -05006 "testing"
Jon Perrittc3e04b62014-10-06 16:17:49 -05007 "time"
Jon Perritt255b6f82014-09-30 16:07:50 -05008
Jon Perritt27249f42016-02-18 10:35:59 -06009 th "github.com/gophercloud/gophercloud/testhelper"
Jon Perritt255b6f82014-09-30 16:07:50 -050010)
11
Jon Perritt1e17aec2014-10-06 14:33:34 -050012func TestMaybeString(t *testing.T) {
13 testString := ""
14 var expected *string
15 actual := MaybeString(testString)
Jon Perrittc3e04b62014-10-06 16:17:49 -050016 th.CheckDeepEquals(t, expected, actual)
Jon Perritt1e17aec2014-10-06 14:33:34 -050017
18 testString = "carol"
19 expected = &testString
20 actual = MaybeString(testString)
Jon Perrittc3e04b62014-10-06 16:17:49 -050021 th.CheckDeepEquals(t, expected, actual)
Jon Perritt255b6f82014-09-30 16:07:50 -050022}
23
Jon Perritt1e17aec2014-10-06 14:33:34 -050024func TestMaybeInt(t *testing.T) {
25 testInt := 0
26 var expected *int
27 actual := MaybeInt(testInt)
Jon Perrittc3e04b62014-10-06 16:17:49 -050028 th.CheckDeepEquals(t, expected, actual)
Jon Perritt1e17aec2014-10-06 14:33:34 -050029
30 testInt = 4
31 expected = &testInt
32 actual = MaybeInt(testInt)
Jon Perrittc3e04b62014-10-06 16:17:49 -050033 th.CheckDeepEquals(t, expected, actual)
Jon Perritt255b6f82014-09-30 16:07:50 -050034}
35
Jon Perrittb5d13ad2014-10-06 16:39:27 -050036func TestBuildQueryString(t *testing.T) {
Jon Perritte43f3de2015-02-12 11:45:34 -070037 type testVar string
Jon Perrittb5d13ad2014-10-06 16:39:27 -050038 opts := struct {
Jon Perritte43f3de2015-02-12 11:45:34 -070039 J int `q:"j"`
40 R string `q:"r,required"`
41 C bool `q:"c"`
42 S []string `q:"s"`
43 TS []testVar `q:"ts"`
44 TI []int `q:"ti"`
Jon Perrittb5d13ad2014-10-06 16:39:27 -050045 }{
Jon Perritte43f3de2015-02-12 11:45:34 -070046 J: 2,
47 R: "red",
48 C: true,
49 S: []string{"one", "two", "three"},
50 TS: []testVar{"a", "b"},
51 TI: []int{1, 2},
Jon Perritt255b6f82014-09-30 16:07:50 -050052 }
Jon Perritte43f3de2015-02-12 11:45:34 -070053 expected := &url.URL{RawQuery: "c=true&j=2&r=red&s=one&s=two&s=three&ti=1&ti=2&ts=a&ts=b"}
Jon Perrittdb00ad12014-09-30 16:29:50 -050054 actual, err := BuildQueryString(&opts)
Jon Perritt255b6f82014-09-30 16:07:50 -050055 if err != nil {
56 t.Errorf("Error building query string: %v", err)
57 }
Jon Perrittc3e04b62014-10-06 16:17:49 -050058 th.CheckDeepEquals(t, expected, actual)
Jon Perrittdb00ad12014-09-30 16:29:50 -050059
Jon Perrittb5d13ad2014-10-06 16:39:27 -050060 opts = struct {
Jon Perritte43f3de2015-02-12 11:45:34 -070061 J int `q:"j"`
62 R string `q:"r,required"`
63 C bool `q:"c"`
64 S []string `q:"s"`
65 TS []testVar `q:"ts"`
66 TI []int `q:"ti"`
Jon Perrittb5d13ad2014-10-06 16:39:27 -050067 }{
68 J: 2,
69 C: true,
Jon Perrittdb00ad12014-09-30 16:29:50 -050070 }
Jon Perrittb5d13ad2014-10-06 16:39:27 -050071 _, err = BuildQueryString(&opts)
Jon Perrittdb00ad12014-09-30 16:29:50 -050072 if err == nil {
Jon Perrittb5d13ad2014-10-06 16:39:27 -050073 t.Errorf("Expected error: 'Required field not set'")
Jon Perrittdb00ad12014-09-30 16:29:50 -050074 }
Jon Perrittb5d13ad2014-10-06 16:39:27 -050075 th.CheckDeepEquals(t, expected, actual)
Jon Perrittdb00ad12014-09-30 16:29:50 -050076
Jon Perrittb5d13ad2014-10-06 16:39:27 -050077 _, err = BuildQueryString(map[string]interface{}{"Number": 4})
78 if err == nil {
79 t.Errorf("Expected error: 'Options type is not a struct'")
80 }
Jon Perrittdb00ad12014-09-30 16:29:50 -050081}
Jon Perrittc3e04b62014-10-06 16:17:49 -050082
83func TestBuildHeaders(t *testing.T) {
84 testStruct := struct {
85 Accept string `h:"Accept"`
Jon Perrittdfeb33b2014-10-06 16:28:58 -050086 Num int `h:"Number,required"`
Jon Perrittc3e04b62014-10-06 16:17:49 -050087 Style bool `h:"Style"`
88 }{
89 Accept: "application/json",
90 Num: 4,
91 Style: true,
92 }
93 expected := map[string]string{"Accept": "application/json", "Number": "4", "Style": "true"}
94 actual, err := BuildHeaders(&testStruct)
95 th.CheckNoErr(t, err)
96 th.CheckDeepEquals(t, expected, actual)
Jon Perrittdfeb33b2014-10-06 16:28:58 -050097
98 testStruct.Num = 0
99 _, err = BuildHeaders(&testStruct)
100 if err == nil {
101 t.Errorf("Expected error: 'Required header not set'")
102 }
103
104 _, err = BuildHeaders(map[string]interface{}{"Number": 4})
105 if err == nil {
106 t.Errorf("Expected error: 'Options type is not a struct'")
107 }
Jon Perrittc3e04b62014-10-06 16:17:49 -0500108}
109
110func TestIsZero(t *testing.T) {
111 var testMap map[string]interface{}
112 testMapValue := reflect.ValueOf(testMap)
113 expected := true
114 actual := isZero(testMapValue)
115 th.CheckEquals(t, expected, actual)
116 testMap = map[string]interface{}{"empty": false}
117 testMapValue = reflect.ValueOf(testMap)
118 expected = false
119 actual = isZero(testMapValue)
120 th.CheckEquals(t, expected, actual)
121
122 var testArray [2]string
123 testArrayValue := reflect.ValueOf(testArray)
124 expected = true
125 actual = isZero(testArrayValue)
126 th.CheckEquals(t, expected, actual)
127 testArray = [2]string{"one", "two"}
128 testArrayValue = reflect.ValueOf(testArray)
129 expected = false
130 actual = isZero(testArrayValue)
131 th.CheckEquals(t, expected, actual)
132
133 var testStruct struct {
134 A string
135 B time.Time
136 }
137 testStructValue := reflect.ValueOf(testStruct)
138 expected = true
139 actual = isZero(testStructValue)
140 th.CheckEquals(t, expected, actual)
141 testStruct = struct {
142 A string
143 B time.Time
144 }{
145 B: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC),
146 }
147 testStructValue = reflect.ValueOf(testStruct)
148 expected = false
149 actual = isZero(testStructValue)
150 th.CheckEquals(t, expected, actual)
Jamie Hannafordf68c3e42014-11-18 13:02:09 +0100151}
Jon Perrittc3e04b62014-10-06 16:17:49 -0500152
Jamie Hannafordf68c3e42014-11-18 13:02:09 +0100153func TestQueriesAreEscaped(t *testing.T) {
154 type foo struct {
155 Name string `q:"something"`
156 Shape string `q:"else"`
157 }
158
159 expected := &url.URL{RawQuery: "else=Triangl+e&something=blah%2B%3F%21%21foo"}
160
161 actual, err := BuildQueryString(foo{Name: "blah+?!!foo", Shape: "Triangl e"})
162 th.AssertNoErr(t, err)
163
164 th.AssertDeepEquals(t, expected, actual)
Jon Perrittc3e04b62014-10-06 16:17:49 -0500165}
Jon Perrittdb0ae142016-03-13 00:33:41 -0600166
167func TestBuildRequestBody(t *testing.T) {
168 type PasswordCredentials struct {
169 Username string `json:"username" required:"true"`
170 Password string `json:"password" required:"true"`
171 }
172
173 type TokenCredentials struct {
174 ID string `json:"id,omitempty" required:"true"`
175 }
176
177 type orFields struct {
178 Filler int `json:"filler,omitempty"`
179 F1 int `json:"f1,omitempty" or:"F2"`
180 F2 int `json:"f2,omitempty" or:"F1"`
181 }
182
183 // AuthOptions wraps a gophercloud AuthOptions in order to adhere to the AuthOptionsBuilder
184 // interface.
185 type AuthOptions struct {
186 PasswordCredentials `json:"passwordCredentials,omitempty" xor:"TokenCredentials"`
187
188 // The TenantID and TenantName fields are optional for the Identity V2 API.
189 // Some providers allow you to specify a TenantName instead of the TenantId.
190 // Some require both. Your provider's authentication policies will determine
191 // how these fields influence authentication.
192 TenantID string `json:"tenantId,omitempty"`
193 TenantName string `json:"tenantName,omitempty"`
194
195 // TokenCredentials allows users to authenticate (possibly as another user) with an
196 // authentication token ID.
197 TokenCredentials `json:"token,omitempty" xor:"PasswordCredentials"`
198
199 OrFields orFields `json:"or_fields,omitempty"`
200 }
201
202 var successCases = []struct {
203 opts AuthOptions
204 expected map[string]interface{}
205 }{
206 {
207 AuthOptions{
208 PasswordCredentials: PasswordCredentials{
209 Username: "me",
210 Password: "swordfish",
211 },
212 },
213 map[string]interface{}{
214 "auth": map[string]interface{}{
215 "passwordCredentials": map[string]interface{}{
216 "password": "swordfish",
217 "username": "me",
218 },
219 },
220 },
221 },
222 {
223 AuthOptions{
224 TokenCredentials: TokenCredentials{
225 ID: "1234567",
226 },
227 },
228 map[string]interface{}{
229 "auth": map[string]interface{}{
230 "token": map[string]interface{}{
231 "id": "1234567",
232 },
233 },
234 },
235 },
236 }
237
238 for _, successCase := range successCases {
239 actual, err := BuildRequestBody(successCase.opts, "auth")
240 th.AssertNoErr(t, err)
241 th.AssertDeepEquals(t, successCase.expected, actual)
242 }
243
244 var failCases = []struct {
245 opts AuthOptions
246 expected error
247 }{
248 {
249 AuthOptions{
250 TenantID: "987654321",
251 TenantName: "me",
252 },
253 ErrMissingInput{},
254 },
255 {
256 AuthOptions{
257 TokenCredentials: TokenCredentials{
258 ID: "1234567",
259 },
260 PasswordCredentials: PasswordCredentials{
261 Username: "me",
262 Password: "swordfish",
263 },
264 },
265 ErrMissingInput{},
266 },
267 {
268 AuthOptions{
269 PasswordCredentials: PasswordCredentials{
270 Password: "swordfish",
271 },
272 },
273 ErrMissingInput{},
274 },
275 {
276 AuthOptions{
277 PasswordCredentials: PasswordCredentials{
278 Username: "me",
279 Password: "swordfish",
280 },
281 OrFields: orFields{
282 Filler: 2,
283 },
284 },
285 ErrMissingInput{},
286 },
287 }
288
289 for _, failCase := range failCases {
290 _, err := BuildRequestBody(failCase.opts, "auth")
291 th.AssertDeepEquals(t, reflect.TypeOf(failCase.expected), reflect.TypeOf(err))
292 }
293}