blob: 937ff8b92458e2d100ebb825cd59e82f095c2e0f [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
Joe Topjian12f19e52016-11-05 14:35:08 -060038 iFalse := false
Jon Perrittb5d13ad2014-10-06 16:39:27 -050039 opts := struct {
Jon Perritte43f3de2015-02-12 11:45:34 -070040 J int `q:"j"`
41 R string `q:"r,required"`
42 C bool `q:"c"`
43 S []string `q:"s"`
44 TS []testVar `q:"ts"`
45 TI []int `q:"ti"`
Joe Topjian12f19e52016-11-05 14:35:08 -060046 F *bool `q:"f"`
Jon Perrittb5d13ad2014-10-06 16:39:27 -050047 }{
Jon Perritte43f3de2015-02-12 11:45:34 -070048 J: 2,
49 R: "red",
50 C: true,
51 S: []string{"one", "two", "three"},
52 TS: []testVar{"a", "b"},
53 TI: []int{1, 2},
Joe Topjian12f19e52016-11-05 14:35:08 -060054 F: &iFalse,
Jon Perritt255b6f82014-09-30 16:07:50 -050055 }
Joe Topjian12f19e52016-11-05 14:35:08 -060056 expected := &url.URL{RawQuery: "c=true&f=false&j=2&r=red&s=one&s=two&s=three&ti=1&ti=2&ts=a&ts=b"}
jrperritt3d966162016-06-06 14:08:54 -050057 actual, err := gophercloud.BuildQueryString(&opts)
Jon Perritt255b6f82014-09-30 16:07:50 -050058 if err != nil {
59 t.Errorf("Error building query string: %v", err)
60 }
Jon Perrittc3e04b62014-10-06 16:17:49 -050061 th.CheckDeepEquals(t, expected, actual)
Jon Perrittdb00ad12014-09-30 16:29:50 -050062
Jon Perrittb5d13ad2014-10-06 16:39:27 -050063 opts = struct {
Jon Perritte43f3de2015-02-12 11:45:34 -070064 J int `q:"j"`
65 R string `q:"r,required"`
66 C bool `q:"c"`
67 S []string `q:"s"`
68 TS []testVar `q:"ts"`
69 TI []int `q:"ti"`
Joe Topjian12f19e52016-11-05 14:35:08 -060070 F *bool `q:"f"`
Jon Perrittb5d13ad2014-10-06 16:39:27 -050071 }{
72 J: 2,
73 C: true,
Jon Perrittdb00ad12014-09-30 16:29:50 -050074 }
jrperritt3d966162016-06-06 14:08:54 -050075 _, err = gophercloud.BuildQueryString(&opts)
Jon Perrittdb00ad12014-09-30 16:29:50 -050076 if err == nil {
Jon Perrittb5d13ad2014-10-06 16:39:27 -050077 t.Errorf("Expected error: 'Required field not set'")
Jon Perrittdb00ad12014-09-30 16:29:50 -050078 }
Jon Perrittb5d13ad2014-10-06 16:39:27 -050079 th.CheckDeepEquals(t, expected, actual)
Jon Perrittdb00ad12014-09-30 16:29:50 -050080
jrperritt3d966162016-06-06 14:08:54 -050081 _, err = gophercloud.BuildQueryString(map[string]interface{}{"Number": 4})
Jon Perrittb5d13ad2014-10-06 16:39:27 -050082 if err == nil {
83 t.Errorf("Expected error: 'Options type is not a struct'")
84 }
Jon Perrittdb00ad12014-09-30 16:29:50 -050085}
Jon Perrittc3e04b62014-10-06 16:17:49 -050086
87func TestBuildHeaders(t *testing.T) {
88 testStruct := struct {
89 Accept string `h:"Accept"`
Jon Perrittdfeb33b2014-10-06 16:28:58 -050090 Num int `h:"Number,required"`
Jon Perrittc3e04b62014-10-06 16:17:49 -050091 Style bool `h:"Style"`
92 }{
93 Accept: "application/json",
94 Num: 4,
95 Style: true,
96 }
97 expected := map[string]string{"Accept": "application/json", "Number": "4", "Style": "true"}
jrperritt3d966162016-06-06 14:08:54 -050098 actual, err := gophercloud.BuildHeaders(&testStruct)
Jon Perrittc3e04b62014-10-06 16:17:49 -050099 th.CheckNoErr(t, err)
100 th.CheckDeepEquals(t, expected, actual)
Jon Perrittdfeb33b2014-10-06 16:28:58 -0500101
102 testStruct.Num = 0
jrperritt3d966162016-06-06 14:08:54 -0500103 _, err = gophercloud.BuildHeaders(&testStruct)
Jon Perrittdfeb33b2014-10-06 16:28:58 -0500104 if err == nil {
105 t.Errorf("Expected error: 'Required header not set'")
106 }
107
jrperritt3d966162016-06-06 14:08:54 -0500108 _, err = gophercloud.BuildHeaders(map[string]interface{}{"Number": 4})
Jon Perrittdfeb33b2014-10-06 16:28:58 -0500109 if err == nil {
110 t.Errorf("Expected error: 'Options type is not a struct'")
111 }
Jon Perrittc3e04b62014-10-06 16:17:49 -0500112}
113
Jamie Hannafordf68c3e42014-11-18 13:02:09 +0100114func TestQueriesAreEscaped(t *testing.T) {
115 type foo struct {
116 Name string `q:"something"`
117 Shape string `q:"else"`
118 }
119
120 expected := &url.URL{RawQuery: "else=Triangl+e&something=blah%2B%3F%21%21foo"}
121
jrperritt3d966162016-06-06 14:08:54 -0500122 actual, err := gophercloud.BuildQueryString(foo{Name: "blah+?!!foo", Shape: "Triangl e"})
Jamie Hannafordf68c3e42014-11-18 13:02:09 +0100123 th.AssertNoErr(t, err)
124
125 th.AssertDeepEquals(t, expected, actual)
Jon Perrittc3e04b62014-10-06 16:17:49 -0500126}
Jon Perrittdb0ae142016-03-13 00:33:41 -0600127
128func TestBuildRequestBody(t *testing.T) {
129 type PasswordCredentials struct {
130 Username string `json:"username" required:"true"`
131 Password string `json:"password" required:"true"`
132 }
133
134 type TokenCredentials struct {
135 ID string `json:"id,omitempty" required:"true"`
136 }
137
138 type orFields struct {
139 Filler int `json:"filler,omitempty"`
140 F1 int `json:"f1,omitempty" or:"F2"`
141 F2 int `json:"f2,omitempty" or:"F1"`
142 }
143
144 // AuthOptions wraps a gophercloud AuthOptions in order to adhere to the AuthOptionsBuilder
145 // interface.
146 type AuthOptions struct {
147 PasswordCredentials `json:"passwordCredentials,omitempty" xor:"TokenCredentials"`
148
149 // The TenantID and TenantName fields are optional for the Identity V2 API.
150 // Some providers allow you to specify a TenantName instead of the TenantId.
151 // Some require both. Your provider's authentication policies will determine
152 // how these fields influence authentication.
153 TenantID string `json:"tenantId,omitempty"`
154 TenantName string `json:"tenantName,omitempty"`
155
156 // TokenCredentials allows users to authenticate (possibly as another user) with an
157 // authentication token ID.
158 TokenCredentials `json:"token,omitempty" xor:"PasswordCredentials"`
159
160 OrFields orFields `json:"or_fields,omitempty"`
161 }
162
163 var successCases = []struct {
164 opts AuthOptions
165 expected map[string]interface{}
166 }{
167 {
168 AuthOptions{
169 PasswordCredentials: PasswordCredentials{
170 Username: "me",
171 Password: "swordfish",
172 },
173 },
174 map[string]interface{}{
175 "auth": map[string]interface{}{
176 "passwordCredentials": map[string]interface{}{
177 "password": "swordfish",
178 "username": "me",
179 },
180 },
181 },
182 },
183 {
184 AuthOptions{
185 TokenCredentials: TokenCredentials{
186 ID: "1234567",
187 },
188 },
189 map[string]interface{}{
190 "auth": map[string]interface{}{
191 "token": map[string]interface{}{
192 "id": "1234567",
193 },
194 },
195 },
196 },
197 }
198
199 for _, successCase := range successCases {
jrperritt3d966162016-06-06 14:08:54 -0500200 actual, err := gophercloud.BuildRequestBody(successCase.opts, "auth")
Jon Perrittdb0ae142016-03-13 00:33:41 -0600201 th.AssertNoErr(t, err)
202 th.AssertDeepEquals(t, successCase.expected, actual)
203 }
204
205 var failCases = []struct {
206 opts AuthOptions
207 expected error
208 }{
209 {
210 AuthOptions{
211 TenantID: "987654321",
212 TenantName: "me",
213 },
jrperritt3d966162016-06-06 14:08:54 -0500214 gophercloud.ErrMissingInput{},
Jon Perrittdb0ae142016-03-13 00:33:41 -0600215 },
216 {
217 AuthOptions{
218 TokenCredentials: TokenCredentials{
219 ID: "1234567",
220 },
221 PasswordCredentials: PasswordCredentials{
222 Username: "me",
223 Password: "swordfish",
224 },
225 },
jrperritt3d966162016-06-06 14:08:54 -0500226 gophercloud.ErrMissingInput{},
Jon Perrittdb0ae142016-03-13 00:33:41 -0600227 },
228 {
229 AuthOptions{
230 PasswordCredentials: PasswordCredentials{
231 Password: "swordfish",
232 },
233 },
jrperritt3d966162016-06-06 14:08:54 -0500234 gophercloud.ErrMissingInput{},
Jon Perrittdb0ae142016-03-13 00:33:41 -0600235 },
236 {
237 AuthOptions{
238 PasswordCredentials: PasswordCredentials{
239 Username: "me",
240 Password: "swordfish",
241 },
242 OrFields: orFields{
243 Filler: 2,
244 },
245 },
jrperritt3d966162016-06-06 14:08:54 -0500246 gophercloud.ErrMissingInput{},
Jon Perrittdb0ae142016-03-13 00:33:41 -0600247 },
248 }
249
250 for _, failCase := range failCases {
jrperritt3d966162016-06-06 14:08:54 -0500251 _, err := gophercloud.BuildRequestBody(failCase.opts, "auth")
Jon Perrittdb0ae142016-03-13 00:33:41 -0600252 th.AssertDeepEquals(t, reflect.TypeOf(failCase.expected), reflect.TypeOf(err))
253 }
254}