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