1package httpmock
2
3import (
4	"encoding/json"
5	"encoding/xml"
6	"errors"
7	"io/ioutil"
8	"net/http"
9	"testing"
10)
11
12func TestResponderFromResponse(t *testing.T) {
13	responder := ResponderFromResponse(NewStringResponse(200, "hello world"))
14
15	req, err := http.NewRequest(http.MethodGet, testURL, nil)
16	if err != nil {
17		t.Fatal("Error creating request")
18	}
19	response1, err := responder(req)
20	if err != nil {
21		t.Error("Error should be nil")
22	}
23
24	testURLWithQuery := testURL + "?a=1"
25	req, err = http.NewRequest(http.MethodGet, testURLWithQuery, nil)
26	if err != nil {
27		t.Fatal("Error creating request")
28	}
29	response2, err := responder(req)
30	if err != nil {
31		t.Error("Error should be nil")
32	}
33
34	// Body should be the same for both responses
35	assertBody(t, response1, "hello world")
36	assertBody(t, response2, "hello world")
37
38	// Request should be non-nil and different for each response
39	if response1.Request != nil && response2.Request != nil {
40		if response1.Request.URL.String() != testURL {
41			t.Errorf("Expected request url %s, got: %s", testURL, response1.Request.URL.String())
42		}
43		if response2.Request.URL.String() != testURLWithQuery {
44			t.Errorf("Expected request url %s, got: %s", testURLWithQuery, response2.Request.URL.String())
45		}
46	} else {
47		t.Error("response.Request should not be nil")
48	}
49}
50
51func TestNewNotFoundResponder(t *testing.T) {
52	responder := NewNotFoundResponder(func(args ...interface{}) {})
53
54	req, err := http.NewRequest("GET", "http://foo.bar/path", nil)
55	if err != nil {
56		t.Fatal("Error creating request")
57	}
58
59	const title = "Responder not found for GET http://foo.bar/path"
60
61	resp, err := responder(req)
62	if resp != nil {
63		t.Error("resp should be nil")
64	}
65	if err == nil {
66		t.Error("err should be not nil")
67	} else if err.Error() != title {
68		t.Errorf(`err mismatch, got: "%s", expected: "%s"`,
69			err, "Responder not found for: GET http://foo.bar/path")
70	} else if ne, ok := err.(stackTracer); !ok {
71		t.Errorf(`err type mismatch, got %T, expected httpmock.notFound`, err)
72	} else if ne.customFn == nil {
73		t.Error(`err customFn mismatch, got: nil, expected: non-nil`)
74	}
75
76	// nil fn
77	responder = NewNotFoundResponder(nil)
78
79	resp, err = responder(req)
80	if resp != nil {
81		t.Error("resp should be nil")
82	}
83	if err == nil {
84		t.Error("err should be not nil")
85	} else if err.Error() != title {
86		t.Errorf(`err mismatch, got: "%s", expected: "%s"`,
87			err, "Responder not found for: GET http://foo.bar/path")
88	} else if ne, ok := err.(stackTracer); !ok {
89		t.Errorf(`err type mismatch, got %T, expected httpmock.notFound`, err)
90	} else if ne.customFn != nil {
91		t.Errorf(`err customFn mismatch, got: %p, expected: nil`, ne.customFn)
92	}
93}
94
95func TestNewStringResponse(t *testing.T) {
96	body := "hello world"
97	status := 200
98	response := NewStringResponse(status, body)
99
100	data, err := ioutil.ReadAll(response.Body)
101	if err != nil {
102		t.Fatal(err)
103	}
104
105	if string(data) != body {
106		t.FailNow()
107	}
108
109	if response.StatusCode != status {
110		t.FailNow()
111	}
112}
113
114func TestNewBytesResponse(t *testing.T) {
115	body := []byte("hello world")
116	status := 200
117	response := NewBytesResponse(status, body)
118
119	data, err := ioutil.ReadAll(response.Body)
120	if err != nil {
121		t.Fatal(err)
122	}
123
124	if string(data) != string(body) {
125		t.FailNow()
126	}
127
128	if response.StatusCode != status {
129		t.FailNow()
130	}
131}
132
133func TestNewJsonResponse(t *testing.T) {
134	type schema struct {
135		Hello string `json:"hello"`
136	}
137
138	body := &schema{"world"}
139	status := 200
140
141	response, err := NewJsonResponse(status, body)
142	if err != nil {
143		t.Fatal(err)
144	}
145
146	if response.StatusCode != status {
147		t.FailNow()
148	}
149
150	if response.Header.Get("Content-Type") != "application/json" {
151		t.FailNow()
152	}
153
154	checkBody := &schema{}
155	if err := json.NewDecoder(response.Body).Decode(checkBody); err != nil {
156		t.Fatal(err)
157	}
158
159	if checkBody.Hello != body.Hello {
160		t.FailNow()
161	}
162}
163
164func TestNewXmlResponse(t *testing.T) {
165	type schema struct {
166		Hello string `xml:"hello"`
167	}
168
169	body := &schema{"world"}
170	status := 200
171
172	response, err := NewXmlResponse(status, body)
173	if err != nil {
174		t.Fatal(err)
175	}
176
177	if response.StatusCode != status {
178		t.FailNow()
179	}
180
181	if response.Header.Get("Content-Type") != "application/xml" {
182		t.FailNow()
183	}
184
185	checkBody := &schema{}
186	if err := xml.NewDecoder(response.Body).Decode(checkBody); err != nil {
187		t.Fatal(err)
188	}
189
190	if checkBody.Hello != body.Hello {
191		t.FailNow()
192	}
193}
194
195func TestNewErrorResponder(t *testing.T) {
196	// From go1.13, a stack frame is stored into errors issued by errors.New()
197	origError := errors.New("oh no")
198	responder := NewErrorResponder(origError)
199	req, err := http.NewRequest(http.MethodGet, testURL, nil)
200	if err != nil {
201		t.Fatal("Error creating request")
202	}
203	response, err := responder(req)
204	if response != nil {
205		t.Error("Response should be nil")
206	}
207	if err != origError {
208		t.Errorf("Expected error %#v, got: %#v", origError, err)
209	}
210}
211
212func TestRewindResponse(t *testing.T) {
213	body := []byte("hello world")
214	status := 200
215	responses := []*http.Response{
216		NewBytesResponse(status, body),
217		NewStringResponse(status, string(body)),
218	}
219
220	for _, response := range responses {
221
222		data, err := ioutil.ReadAll(response.Body)
223		if err != nil {
224			t.Fatal(err)
225		}
226
227		if string(data) != string(body) {
228			t.FailNow()
229		}
230
231		if response.StatusCode != status {
232			t.FailNow()
233		}
234
235		data, err = ioutil.ReadAll(response.Body)
236		if err != nil {
237			t.Fatal(err)
238		}
239
240		if string(data) != string(body) {
241			t.FailNow()
242		}
243
244		if response.StatusCode != status {
245			t.FailNow()
246		}
247	}
248}
249
250func TestResponder(t *testing.T) {
251	req, err := http.NewRequest(http.MethodGet, "http://foo.bar", nil)
252	if err != nil {
253		t.Fatal("Error creating request")
254	}
255	resp := &http.Response{}
256
257	chk := func(r Responder, expectedResp *http.Response, expectedErr string) {
258		//t.Helper // Only available since 1.9
259		gotResp, gotErr := r(req)
260		if gotResp != expectedResp {
261			t.Errorf(`Response mismatch, expected: %v, got: %v`, expectedResp, gotResp)
262		}
263		var gotErrStr string
264		if gotErr != nil {
265			gotErrStr = gotErr.Error()
266		}
267		if gotErrStr != expectedErr {
268			t.Errorf(`Error mismatch, expected: %v, got: %v`, expectedErr, gotErrStr)
269		}
270	}
271	called := false
272	chkNotCalled := func() {
273		if called {
274			//t.Helper // Only available since 1.9
275			t.Errorf("Original responder should not be called")
276			called = false
277		}
278	}
279	chkCalled := func() {
280		if !called {
281			//t.Helper // Only available since 1.9
282			t.Errorf("Original responder should be called")
283		}
284		called = false
285	}
286
287	r := Responder(func(*http.Request) (*http.Response, error) {
288		called = true
289		return resp, nil
290	})
291	chk(r, resp, "")
292	chkCalled()
293
294	//
295	// Once
296	ro := r.Once()
297	chk(ro, resp, "")
298	chkCalled()
299
300	chk(ro, nil, "Responder not found for GET http://foo.bar (coz Once and already called 2 times)")
301	chkNotCalled()
302
303	chk(ro, nil, "Responder not found for GET http://foo.bar (coz Once and already called 3 times)")
304	chkNotCalled()
305
306	ro = r.Once(func(args ...interface{}) {})
307	chk(ro, resp, "")
308	chkCalled()
309
310	chk(ro, nil, "Responder not found for GET http://foo.bar (coz Once and already called 2 times)")
311	chkNotCalled()
312
313	//
314	// Times
315	rt := r.Times(2)
316	chk(rt, resp, "")
317	chkCalled()
318
319	chk(rt, resp, "")
320	chkCalled()
321
322	chk(rt, nil, "Responder not found for GET http://foo.bar (coz Times and already called 3 times)")
323	chkNotCalled()
324
325	chk(rt, nil, "Responder not found for GET http://foo.bar (coz Times and already called 4 times)")
326	chkNotCalled()
327
328	rt = r.Times(1, func(args ...interface{}) {})
329	chk(rt, resp, "")
330	chkCalled()
331
332	chk(rt, nil, "Responder not found for GET http://foo.bar (coz Times and already called 2 times)")
333	chkNotCalled()
334
335	//
336	// Trace
337	rt = r.Trace(func(args ...interface{}) {})
338	chk(rt, resp, "")
339	chkCalled()
340
341	chk(rt, resp, "")
342	chkCalled()
343}
344