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