blob: 90f3fad4ee8ff5254a88cbcd976603cdd14a2832 [file] [log] [blame]
jrperritt3d966162016-06-06 14:08:54 -05001package testing
Jon Perritt255b6f82014-09-30 16:07:50 -05002
3import (
4 "net/url"
Jon Perrittc3e04b62014-10-06 16:17:49 -05005 "reflect"
Jon Perritt255b6f82014-09-30 16:07:50 -05006 "testing"
7
jrperritt3d966162016-06-06 14:08:54 -05008 "github.com/gophercloud/gophercloud"
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
jrperritt3d966162016-06-06 14:08:54 -050015 actual := gophercloud.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
jrperritt3d966162016-06-06 14:08:54 -050020 actual = gophercloud.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
jrperritt3d966162016-06-06 14:08:54 -050027 actual := gophercloud.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
jrperritt3d966162016-06-06 14:08:54 -050032 actual = gophercloud.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"}
jrperritt3d966162016-06-06 14:08:54 -050054 actual, err := gophercloud.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 }
jrperritt3d966162016-06-06 14:08:54 -050071 _, err = gophercloud.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
jrperritt3d966162016-06-06 14:08:54 -050077 _, err = gophercloud.BuildQueryString(map[string]interface{}{"Number": 4})
Jon Perrittb5d13ad2014-10-06 16:39:27 -050078 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"}
jrperritt3d966162016-06-06 14:08:54 -050094 actual, err := gophercloud.BuildHeaders(&testStruct)
Jon Perrittc3e04b62014-10-06 16:17:49 -050095 th.CheckNoErr(t, err)
96 th.CheckDeepEquals(t, expected, actual)
Jon Perrittdfeb33b2014-10-06 16:28:58 -050097
98 testStruct.Num = 0
jrperritt3d966162016-06-06 14:08:54 -050099 _, err = gophercloud.BuildHeaders(&testStruct)
Jon Perrittdfeb33b2014-10-06 16:28:58 -0500100 if err == nil {
101 t.Errorf("Expected error: 'Required header not set'")
102 }
103
jrperritt3d966162016-06-06 14:08:54 -0500104 _, err = gophercloud.BuildHeaders(map[string]interface{}{"Number": 4})
Jon Perrittdfeb33b2014-10-06 16:28:58 -0500105 if err == nil {
106 t.Errorf("Expected error: 'Options type is not a struct'")
107 }
Jon Perrittc3e04b62014-10-06 16:17:49 -0500108}
109
Jamie Hannafordf68c3e42014-11-18 13:02:09 +0100110func TestQueriesAreEscaped(t *testing.T) {
111 type foo struct {
112 Name string `q:"something"`
113 Shape string `q:"else"`
114 }
115
116 expected := &url.URL{RawQuery: "else=Triangl+e&something=blah%2B%3F%21%21foo"}
117
jrperritt3d966162016-06-06 14:08:54 -0500118 actual, err := gophercloud.BuildQueryString(foo{Name: "blah+?!!foo", Shape: "Triangl e"})
Jamie Hannafordf68c3e42014-11-18 13:02:09 +0100119 th.AssertNoErr(t, err)
120
121 th.AssertDeepEquals(t, expected, actual)
Jon Perrittc3e04b62014-10-06 16:17:49 -0500122}
Jon Perrittdb0ae142016-03-13 00:33:41 -0600123
124func TestBuildRequestBody(t *testing.T) {
125 type PasswordCredentials struct {
126 Username string `json:"username" required:"true"`
127 Password string `json:"password" required:"true"`
128 }
129
130 type TokenCredentials struct {
131 ID string `json:"id,omitempty" required:"true"`
132 }
133
134 type orFields struct {
135 Filler int `json:"filler,omitempty"`
136 F1 int `json:"f1,omitempty" or:"F2"`
137 F2 int `json:"f2,omitempty" or:"F1"`
138 }
139
140 // AuthOptions wraps a gophercloud AuthOptions in order to adhere to the AuthOptionsBuilder
141 // interface.
142 type AuthOptions struct {
143 PasswordCredentials `json:"passwordCredentials,omitempty" xor:"TokenCredentials"`
144
145 // The TenantID and TenantName fields are optional for the Identity V2 API.
146 // Some providers allow you to specify a TenantName instead of the TenantId.
147 // Some require both. Your provider's authentication policies will determine
148 // how these fields influence authentication.
149 TenantID string `json:"tenantId,omitempty"`
150 TenantName string `json:"tenantName,omitempty"`
151
152 // TokenCredentials allows users to authenticate (possibly as another user) with an
153 // authentication token ID.
154 TokenCredentials `json:"token,omitempty" xor:"PasswordCredentials"`
155
156 OrFields orFields `json:"or_fields,omitempty"`
157 }
158
159 var successCases = []struct {
160 opts AuthOptions
161 expected map[string]interface{}
162 }{
163 {
164 AuthOptions{
165 PasswordCredentials: PasswordCredentials{
166 Username: "me",
167 Password: "swordfish",
168 },
169 },
170 map[string]interface{}{
171 "auth": map[string]interface{}{
172 "passwordCredentials": map[string]interface{}{
173 "password": "swordfish",
174 "username": "me",
175 },
176 },
177 },
178 },
179 {
180 AuthOptions{
181 TokenCredentials: TokenCredentials{
182 ID: "1234567",
183 },
184 },
185 map[string]interface{}{
186 "auth": map[string]interface{}{
187 "token": map[string]interface{}{
188 "id": "1234567",
189 },
190 },
191 },
192 },
193 }
194
195 for _, successCase := range successCases {
jrperritt3d966162016-06-06 14:08:54 -0500196 actual, err := gophercloud.BuildRequestBody(successCase.opts, "auth")
Jon Perrittdb0ae142016-03-13 00:33:41 -0600197 th.AssertNoErr(t, err)
198 th.AssertDeepEquals(t, successCase.expected, actual)
199 }
200
201 var failCases = []struct {
202 opts AuthOptions
203 expected error
204 }{
205 {
206 AuthOptions{
207 TenantID: "987654321",
208 TenantName: "me",
209 },
jrperritt3d966162016-06-06 14:08:54 -0500210 gophercloud.ErrMissingInput{},
Jon Perrittdb0ae142016-03-13 00:33:41 -0600211 },
212 {
213 AuthOptions{
214 TokenCredentials: TokenCredentials{
215 ID: "1234567",
216 },
217 PasswordCredentials: PasswordCredentials{
218 Username: "me",
219 Password: "swordfish",
220 },
221 },
jrperritt3d966162016-06-06 14:08:54 -0500222 gophercloud.ErrMissingInput{},
Jon Perrittdb0ae142016-03-13 00:33:41 -0600223 },
224 {
225 AuthOptions{
226 PasswordCredentials: PasswordCredentials{
227 Password: "swordfish",
228 },
229 },
jrperritt3d966162016-06-06 14:08:54 -0500230 gophercloud.ErrMissingInput{},
Jon Perrittdb0ae142016-03-13 00:33:41 -0600231 },
232 {
233 AuthOptions{
234 PasswordCredentials: PasswordCredentials{
235 Username: "me",
236 Password: "swordfish",
237 },
238 OrFields: orFields{
239 Filler: 2,
240 },
241 },
jrperritt3d966162016-06-06 14:08:54 -0500242 gophercloud.ErrMissingInput{},
Jon Perrittdb0ae142016-03-13 00:33:41 -0600243 },
244 }
245
246 for _, failCase := range failCases {
jrperritt3d966162016-06-06 14:08:54 -0500247 _, err := gophercloud.BuildRequestBody(failCase.opts, "auth")
Jon Perrittdb0ae142016-03-13 00:33:41 -0600248 th.AssertDeepEquals(t, reflect.TypeOf(failCase.expected), reflect.TypeOf(err))
249 }
250}