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