1package autorest 2 3// Copyright 2017 Microsoft Corporation 4// 5// Licensed under the Apache License, Version 2.0 (the "License"); 6// you may not use this file except in compliance with the License. 7// You may obtain a copy of the License at 8// 9// http://www.apache.org/licenses/LICENSE-2.0 10// 11// Unless required by applicable law or agreed to in writing, software 12// distributed under the License is distributed on an "AS IS" BASIS, 13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14// See the License for the specific language governing permissions and 15// limitations under the License. 16 17import ( 18 "bytes" 19 "encoding/json" 20 "encoding/xml" 21 "errors" 22 "fmt" 23 "io" 24 "net/http" 25 "net/url" 26 "reflect" 27 "sort" 28 "strings" 29 "testing" 30 31 "github.com/Azure/go-autorest/autorest/mocks" 32) 33 34const ( 35 jsonT = ` 36 { 37 "name":"Rob Pike", 38 "age":42 39 }` 40 xmlT = `<?xml version="1.0" encoding="UTF-8"?> 41 <Person> 42 <Name>Rob Pike</Name> 43 <Age>42</Age> 44 </Person>` 45) 46 47func TestNewDecoderCreatesJSONDecoder(t *testing.T) { 48 d := NewDecoder(EncodedAsJSON, strings.NewReader(jsonT)) 49 _, ok := d.(*json.Decoder) 50 if d == nil || !ok { 51 t.Fatal("autorest: NewDecoder failed to create a JSON decoder when requested") 52 } 53} 54 55func TestNewDecoderCreatesXMLDecoder(t *testing.T) { 56 d := NewDecoder(EncodedAsXML, strings.NewReader(xmlT)) 57 _, ok := d.(*xml.Decoder) 58 if d == nil || !ok { 59 t.Fatal("autorest: NewDecoder failed to create an XML decoder when requested") 60 } 61} 62 63func TestNewDecoderReturnsNilForUnknownEncoding(t *testing.T) { 64 d := NewDecoder("unknown", strings.NewReader(xmlT)) 65 if d != nil { 66 t.Fatal("autorest: NewDecoder created a decoder for an unknown encoding") 67 } 68} 69 70func TestCopyAndDecodeDecodesJSON(t *testing.T) { 71 _, err := CopyAndDecode(EncodedAsJSON, strings.NewReader(jsonT), &mocks.T{}) 72 if err != nil { 73 t.Fatalf("autorest: CopyAndDecode returned an error with valid JSON - %v", err) 74 } 75} 76 77func TestCopyAndDecodeDecodesXML(t *testing.T) { 78 _, err := CopyAndDecode(EncodedAsXML, strings.NewReader(xmlT), &mocks.T{}) 79 if err != nil { 80 t.Fatalf("autorest: CopyAndDecode returned an error with valid XML - %v", err) 81 } 82} 83 84func TestCopyAndDecodeReturnsJSONDecodingErrors(t *testing.T) { 85 _, err := CopyAndDecode(EncodedAsJSON, strings.NewReader(jsonT[0:len(jsonT)-2]), &mocks.T{}) 86 if err == nil { 87 t.Fatalf("autorest: CopyAndDecode failed to return an error with invalid JSON") 88 } 89} 90 91func TestCopyAndDecodeReturnsXMLDecodingErrors(t *testing.T) { 92 _, err := CopyAndDecode(EncodedAsXML, strings.NewReader(xmlT[0:len(xmlT)-2]), &mocks.T{}) 93 if err == nil { 94 t.Fatalf("autorest: CopyAndDecode failed to return an error with invalid XML") 95 } 96} 97 98func TestCopyAndDecodeAlwaysReturnsACopy(t *testing.T) { 99 b, _ := CopyAndDecode(EncodedAsJSON, strings.NewReader(jsonT), &mocks.T{}) 100 if b.String() != jsonT { 101 t.Fatalf("autorest: CopyAndDecode failed to return a valid copy of the data - %v", b.String()) 102 } 103} 104 105func TestTeeReadCloser_Copies(t *testing.T) { 106 v := &mocks.T{} 107 r := mocks.NewResponseWithContent(jsonT) 108 b := &bytes.Buffer{} 109 110 r.Body = TeeReadCloser(r.Body, b) 111 112 err := Respond(r, 113 ByUnmarshallingJSON(v), 114 ByClosing()) 115 if err != nil { 116 t.Fatalf("autorest: TeeReadCloser returned an unexpected error -- %v", err) 117 } 118 if b.String() != jsonT { 119 t.Fatalf("autorest: TeeReadCloser failed to copy the bytes read") 120 } 121} 122 123func TestTeeReadCloser_PassesReadErrors(t *testing.T) { 124 v := &mocks.T{} 125 r := mocks.NewResponseWithContent(jsonT) 126 127 r.Body.(*mocks.Body).Close() 128 r.Body = TeeReadCloser(r.Body, &bytes.Buffer{}) 129 130 err := Respond(r, 131 ByUnmarshallingJSON(v), 132 ByClosing()) 133 if err == nil { 134 t.Fatalf("autorest: TeeReadCloser failed to return the expected error") 135 } 136} 137 138func TestTeeReadCloser_ClosesWrappedReader(t *testing.T) { 139 v := &mocks.T{} 140 r := mocks.NewResponseWithContent(jsonT) 141 142 b := r.Body.(*mocks.Body) 143 r.Body = TeeReadCloser(r.Body, &bytes.Buffer{}) 144 err := Respond(r, 145 ByUnmarshallingJSON(v), 146 ByClosing()) 147 if err != nil { 148 t.Fatalf("autorest: TeeReadCloser returned an unexpected error -- %v", err) 149 } 150 if b.IsOpen() { 151 t.Fatalf("autorest: TeeReadCloser failed to close the nested io.ReadCloser") 152 } 153} 154 155func TestContainsIntFindsValue(t *testing.T) { 156 ints := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} 157 v := 5 158 if !containsInt(ints, v) { 159 t.Fatalf("autorest: containsInt failed to find %v in %v", v, ints) 160 } 161} 162 163func TestContainsIntDoesNotFindValue(t *testing.T) { 164 ints := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} 165 v := 42 166 if containsInt(ints, v) { 167 t.Fatalf("autorest: containsInt unexpectedly found %v in %v", v, ints) 168 } 169} 170 171func TestContainsIntAcceptsEmptyList(t *testing.T) { 172 ints := make([]int, 10) 173 if containsInt(ints, 42) { 174 t.Fatalf("autorest: containsInt failed to handle an empty list") 175 } 176} 177 178func TestContainsIntAcceptsNilList(t *testing.T) { 179 var ints []int 180 if containsInt(ints, 42) { 181 t.Fatalf("autorest: containsInt failed to handle an nil list") 182 } 183} 184 185func TestEscapeStrings(t *testing.T) { 186 m := map[string]string{ 187 "string": "a long string with = odd characters", 188 "int": "42", 189 "nil": "", 190 } 191 r := map[string]string{ 192 "string": "a+long+string+with+%3D+odd+characters", 193 "int": "42", 194 "nil": "", 195 } 196 v := escapeValueStrings(m) 197 if !reflect.DeepEqual(v, r) { 198 t.Fatalf("autorest: ensureValueStrings returned %v\n", v) 199 } 200} 201 202func TestEnsureStrings(t *testing.T) { 203 m := map[string]interface{}{ 204 "string": "string", 205 "int": 42, 206 "nil": nil, 207 "bytes": []byte{255, 254, 253}, 208 } 209 r := map[string]string{ 210 "string": "string", 211 "int": "42", 212 "nil": "", 213 "bytes": string([]byte{255, 254, 253}), 214 } 215 v := ensureValueStrings(m) 216 if !reflect.DeepEqual(v, r) { 217 t.Fatalf("autorest: ensureValueStrings returned %v\n", v) 218 } 219} 220 221func ExampleString() { 222 m := []string{ 223 "string1", 224 "string2", 225 "string3", 226 } 227 228 fmt.Println(String(m, ",")) 229 // Output: string1,string2,string3 230} 231 232func TestStringWithValidString(t *testing.T) { 233 i := 123 234 if got, want := String(i), "123"; got != want { 235 t.Logf("got: %q\nwant: %q", got, want) 236 t.Fail() 237 } 238} 239 240func TestStringWithStringSlice(t *testing.T) { 241 s := []string{"string1", "string2"} 242 if got, want := String(s, ","), "string1,string2"; got != want { 243 t.Logf("got: %q\nwant: %q", got, want) 244 t.Fail() 245 } 246} 247 248func TestStringWithEnum(t *testing.T) { 249 type TestEnumType string 250 s := TestEnumType("string1") 251 if got, want := String(s), "string1"; got != want { 252 t.Logf("got: %q\nwant: %q", got, want) 253 t.Fail() 254 } 255} 256 257func TestStringWithEnumSlice(t *testing.T) { 258 type TestEnumType string 259 s := []TestEnumType{"string1", "string2"} 260 if got, want := String(s, ","), "string1,string2"; got != want { 261 t.Logf("got: %q\nwant: %q", got, want) 262 t.Fail() 263 } 264} 265 266func TestStringWithIntegerSlice(t *testing.T) { 267 type TestEnumType int32 268 s := []TestEnumType{1, 2} 269 if got, want := String(s, ","), "1,2"; got != want { 270 t.Logf("got: %q\nwant: %q", got, want) 271 t.Fail() 272 } 273} 274 275func ExampleAsStringSlice() { 276 type TestEnumType string 277 278 a := []TestEnumType{"value1", "value2"} 279 b, _ := AsStringSlice(a) 280 for _, c := range b { 281 fmt.Println(c) 282 } 283 // Output: 284 // value1 285 // value2 286} 287 288func TestEncodeWithValidPath(t *testing.T) { 289 s := Encode("Path", "Hello Gopher") 290 if s != "Hello%20Gopher" { 291 t.Fatalf("autorest: Encode method failed for valid path encoding. Got: %v; Want: %v", s, "Hello%20Gopher") 292 } 293} 294 295func TestEncodeWithValidQuery(t *testing.T) { 296 s := Encode("Query", "Hello Gopher") 297 if s != "Hello+Gopher" { 298 t.Fatalf("autorest: Encode method failed for valid query encoding. Got: '%v'; Want: 'Hello+Gopher'", s) 299 } 300} 301 302func TestEncodeWithValidNotPathQuery(t *testing.T) { 303 s := Encode("Host", "Hello Gopher") 304 if s != "Hello Gopher" { 305 t.Fatalf("autorest: Encode method failed for parameter not query or path. Got: '%v'; Want: 'Hello Gopher'", s) 306 } 307} 308 309func TestMapToValues(t *testing.T) { 310 m := map[string]interface{}{ 311 "a": "a", 312 "b": 2, 313 } 314 v := url.Values{} 315 v.Add("a", "a") 316 v.Add("b", "2") 317 if !isEqual(v, MapToValues(m)) { 318 t.Fatalf("autorest: MapToValues method failed to return correct values - expected(%v) got(%v)", v, MapToValues(m)) 319 } 320} 321 322func TestMapToValuesWithArrayValues(t *testing.T) { 323 m := map[string]interface{}{ 324 "a": []string{"a", "b"}, 325 "b": 2, 326 "c": []int{3, 4}, 327 } 328 v := url.Values{} 329 v.Add("a", "a") 330 v.Add("a", "b") 331 v.Add("b", "2") 332 v.Add("c", "3") 333 v.Add("c", "4") 334 335 if !isEqual(v, MapToValues(m)) { 336 t.Fatalf("autorest: MapToValues method failed to return correct values - expected(%v) got(%v)", v, MapToValues(m)) 337 } 338} 339 340type someTempError struct{} 341 342func (s someTempError) Error() string { 343 return "temporary error" 344} 345func (s someTempError) Timeout() bool { 346 return true 347} 348func (s someTempError) Temporary() bool { 349 return true 350} 351 352func TestIsTemporaryNetworkErrorTrue(t *testing.T) { 353 if !IsTemporaryNetworkError(someTempError{}) { 354 t.Fatal("expected someTempError to be a temporary network error") 355 } 356 if !IsTemporaryNetworkError(errors.New("non-temporary network error")) { 357 t.Fatal("expected random error to be a temporary network error") 358 } 359} 360 361type someFatalError struct{} 362 363func (s someFatalError) Error() string { 364 return "fatal error" 365} 366func (s someFatalError) Timeout() bool { 367 return false 368} 369func (s someFatalError) Temporary() bool { 370 return false 371} 372 373func TestIsTemporaryNetworkErrorFalse(t *testing.T) { 374 if IsTemporaryNetworkError(someFatalError{}) { 375 t.Fatal("expected someFatalError to be a fatal network error") 376 } 377} 378 379func isEqual(v, u url.Values) bool { 380 for key, value := range v { 381 if len(u[key]) == 0 { 382 return false 383 } 384 sort.Strings(value) 385 sort.Strings(u[key]) 386 for i := range value { 387 if value[i] != u[key][i] { 388 return false 389 } 390 } 391 u.Del(key) 392 } 393 if len(u) > 0 { 394 return false 395 } 396 return true 397} 398 399func doEnsureBodyClosed(t *testing.T) SendDecorator { 400 return func(s Sender) Sender { 401 return SenderFunc(func(r *http.Request) (*http.Response, error) { 402 resp, err := s.Do(r) 403 if resp != nil && resp.Body != nil && resp.Body.(*mocks.Body).IsOpen() { 404 t.Fatal("autorest: Expected Body to be closed -- it was left open") 405 } 406 return resp, err 407 }) 408 } 409} 410 411type mockAuthorizer struct{} 412 413func (ma mockAuthorizer) WithAuthorization() PrepareDecorator { 414 return WithHeader(headerAuthorization, mocks.TestAuthorizationHeader) 415} 416 417type mockFailingAuthorizer struct{} 418 419func (mfa mockFailingAuthorizer) WithAuthorization() PrepareDecorator { 420 return func(p Preparer) Preparer { 421 return PreparerFunc(func(r *http.Request) (*http.Request, error) { 422 return r, fmt.Errorf("ERROR: mockFailingAuthorizer returned expected error") 423 }) 424 } 425} 426 427type mockInspector struct { 428 wasInvoked bool 429} 430 431func (mi *mockInspector) WithInspection() PrepareDecorator { 432 return func(p Preparer) Preparer { 433 return PreparerFunc(func(r *http.Request) (*http.Request, error) { 434 mi.wasInvoked = true 435 return p.Prepare(r) 436 }) 437 } 438} 439 440func (mi *mockInspector) ByInspecting() RespondDecorator { 441 return func(r Responder) Responder { 442 return ResponderFunc(func(resp *http.Response) error { 443 mi.wasInvoked = true 444 return r.Respond(resp) 445 }) 446 } 447} 448 449func withMessage(output *string, msg string) SendDecorator { 450 return func(s Sender) Sender { 451 return SenderFunc(func(r *http.Request) (*http.Response, error) { 452 resp, err := s.Do(r) 453 if err == nil { 454 *output += msg 455 } 456 return resp, err 457 }) 458 } 459} 460 461func withErrorRespondDecorator(e *error) RespondDecorator { 462 return func(r Responder) Responder { 463 return ResponderFunc(func(resp *http.Response) error { 464 err := r.Respond(resp) 465 if err != nil { 466 return err 467 } 468 *e = fmt.Errorf("autorest: Faux Respond Error") 469 return *e 470 }) 471 } 472} 473 474type mockDrain struct { 475 read bool 476 closed bool 477} 478 479func (md *mockDrain) Read(b []byte) (int, error) { 480 md.read = true 481 b = append(b, 0xff) 482 return 1, io.EOF 483} 484 485func (md *mockDrain) Close() error { 486 md.closed = true 487 return nil 488} 489 490func TestDrainResponseBody(t *testing.T) { 491 err := DrainResponseBody(nil) 492 if err != nil { 493 t.Fatalf("expected nil error, got %v", err) 494 } 495 err = DrainResponseBody(&http.Response{}) 496 if err != nil { 497 t.Fatalf("expected nil error, got %v", err) 498 } 499 md := &mockDrain{} 500 err = DrainResponseBody(&http.Response{Body: md}) 501 if err != nil { 502 t.Fatalf("expected nil error, got %v", err) 503 } 504 if !md.closed { 505 t.Fatal("mockDrain wasn't closed") 506 } 507 if !md.read { 508 t.Fatal("mockDrain wasn't read") 509 } 510} 511