...

Source file src/github.com/jarcoal/httpmock/response_test.go

Documentation: github.com/jarcoal/httpmock

     1  package httpmock_test
     2  
     3  import (
     4  	"encoding/xml"
     5  	"errors"
     6  	"fmt"
     7  	"io"
     8  	"io/ioutil"
     9  	"net/http"
    10  	"path/filepath"
    11  	"strings"
    12  	"sync"
    13  	"testing"
    14  	"time"
    15  
    16  	"github.com/maxatome/go-testdeep/td"
    17  
    18  	. "github.com/jarcoal/httpmock"
    19  	"github.com/jarcoal/httpmock/internal"
    20  )
    21  
    22  func TestResponderFromResponse(t *testing.T) {
    23  	assert, require := td.AssertRequire(t)
    24  
    25  	responder := ResponderFromResponse(NewStringResponse(200, "hello world"))
    26  
    27  	req, err := http.NewRequest(http.MethodGet, testURL, nil)
    28  	require.CmpNoError(err)
    29  
    30  	response1, err := responder(req)
    31  	require.CmpNoError(err)
    32  
    33  	testURLWithQuery := testURL + "?a=1"
    34  	req, err = http.NewRequest(http.MethodGet, testURLWithQuery, nil)
    35  	require.CmpNoError(err)
    36  
    37  	response2, err := responder(req)
    38  	require.CmpNoError(err)
    39  
    40  	// Body should be the same for both responses
    41  	assertBody(assert, response1, "hello world")
    42  	assertBody(assert, response2, "hello world")
    43  
    44  	// Request should be non-nil and different for each response
    45  	require.NotNil(response1.Request)
    46  	assert.String(response1.Request.URL, testURL)
    47  
    48  	require.NotNil(response2.Request)
    49  	assert.String(response2.Request.URL, testURLWithQuery)
    50  }
    51  
    52  func TestResponderFromResponses(t *testing.T) {
    53  	assert, require := td.AssertRequire(t)
    54  
    55  	jsonResponse, err := NewJsonResponse(200, map[string]string{"test": "toto"})
    56  	require.CmpNoError(err)
    57  
    58  	responder := ResponderFromMultipleResponses(
    59  		[]*http.Response{
    60  			jsonResponse,
    61  			NewStringResponse(200, "hello world"),
    62  		},
    63  	)
    64  
    65  	req, err := http.NewRequest(http.MethodGet, testURL, nil)
    66  	require.CmpNoError(err)
    67  
    68  	response1, err := responder(req)
    69  	require.CmpNoError(err)
    70  
    71  	testURLWithQuery := testURL + "?a=1"
    72  	req, err = http.NewRequest(http.MethodGet, testURLWithQuery, nil)
    73  	require.CmpNoError(err)
    74  
    75  	response2, err := responder(req)
    76  	require.CmpNoError(err)
    77  
    78  	// Body should be the same for both responses
    79  	assertBody(assert, response1, `{"test":"toto"}`)
    80  	assertBody(assert, response2, "hello world")
    81  
    82  	// Request should be non-nil and different for each response
    83  	require.NotNil(response1.Request)
    84  	assert.String(response1.Request.URL, testURL)
    85  
    86  	require.NotNil(response2.Request)
    87  	assert.String(response2.Request.URL, testURLWithQuery)
    88  
    89  	// ensure we can't call the responder more than the number of responses it embeds
    90  	_, err = responder(req)
    91  	assert.String(err, "not enough responses provided: responder called 3 time(s) but 2 response(s) provided")
    92  
    93  	// fn usage
    94  	responder = ResponderFromMultipleResponses([]*http.Response{}, func(args ...interface{}) {})
    95  	_, err = responder(req)
    96  	assert.String(err, "not enough responses provided: responder called 1 time(s) but 0 response(s) provided")
    97  	if assert.Isa(err, internal.StackTracer{}) {
    98  		assert.NotNil(err.(internal.StackTracer).CustomFn)
    99  	}
   100  }
   101  
   102  func TestNewNotFoundResponder(t *testing.T) {
   103  	assert, require := td.AssertRequire(t)
   104  
   105  	responder := NewNotFoundResponder(func(args ...interface{}) {})
   106  
   107  	req, err := http.NewRequest("GET", "http://foo.bar/path", nil)
   108  	require.CmpNoError(err)
   109  
   110  	const title = "Responder not found for GET http://foo.bar/path"
   111  
   112  	resp, err := responder(req)
   113  	assert.Nil(resp)
   114  	assert.String(err, title)
   115  	if assert.Isa(err, internal.StackTracer{}) {
   116  		assert.NotNil(err.(internal.StackTracer).CustomFn)
   117  	}
   118  
   119  	// nil fn
   120  	responder = NewNotFoundResponder(nil)
   121  
   122  	resp, err = responder(req)
   123  	assert.Nil(resp)
   124  	assert.String(err, title)
   125  	if assert.Isa(err, internal.StackTracer{}) {
   126  		assert.Nil(err.(internal.StackTracer).CustomFn)
   127  	}
   128  }
   129  
   130  func TestNewStringResponse(t *testing.T) {
   131  	assert, require := td.AssertRequire(t)
   132  
   133  	const (
   134  		body   = "hello world"
   135  		status = 200
   136  	)
   137  	response := NewStringResponse(status, body)
   138  
   139  	data, err := ioutil.ReadAll(response.Body)
   140  	require.CmpNoError(err)
   141  
   142  	assert.String(data, body)
   143  	assert.Cmp(response.StatusCode, status)
   144  }
   145  
   146  func TestNewBytesResponse(t *testing.T) {
   147  	assert, require := td.AssertRequire(t)
   148  
   149  	const (
   150  		body   = "hello world"
   151  		status = 200
   152  	)
   153  	response := NewBytesResponse(status, []byte(body))
   154  
   155  	data, err := ioutil.ReadAll(response.Body)
   156  	require.CmpNoError(err)
   157  
   158  	assert.String(data, body)
   159  	assert.Cmp(response.StatusCode, status)
   160  }
   161  
   162  func TestNewJsonResponse(t *testing.T) {
   163  	assert := td.Assert(t)
   164  
   165  	type schema struct {
   166  		Hello string `json:"hello"`
   167  	}
   168  
   169  	dir, cleanup := tmpDir(assert)
   170  	defer cleanup()
   171  	fileName := filepath.Join(dir, "ok.json")
   172  	writeFile(assert, fileName, []byte(`{ "test": true }`))
   173  
   174  	for i, test := range []struct {
   175  		body     interface{}
   176  		expected string
   177  	}{
   178  		{body: &schema{"world"}, expected: `{"hello":"world"}`},
   179  		{body: File(fileName), expected: `{"test":true}`},
   180  	} {
   181  		assert.Run(fmt.Sprintf("#%d", i), func(assert *td.T) {
   182  			response, err := NewJsonResponse(200, test.body)
   183  			if !assert.CmpNoError(err) {
   184  				return
   185  			}
   186  			assert.Cmp(response.StatusCode, 200)
   187  			assert.Cmp(response.Header.Get("Content-Type"), "application/json")
   188  			assertBody(assert, response, test.expected)
   189  		})
   190  	}
   191  
   192  	// Error case
   193  	response, err := NewJsonResponse(200, func() {})
   194  	assert.CmpError(err)
   195  	assert.Nil(response)
   196  }
   197  
   198  func checkResponder(assert *td.T, r Responder, expectedStatus int, expectedBody string) {
   199  	assert.Helper()
   200  
   201  	req, err := http.NewRequest(http.MethodGet, "/foo", nil)
   202  	assert.FailureIsFatal().CmpNoError(err)
   203  
   204  	resp, err := r(req)
   205  	if !assert.CmpNoError(err, "Responder returned no error") {
   206  		return
   207  	}
   208  
   209  	if !assert.NotNil(resp, "Responder returned a non-nil response") {
   210  		return
   211  	}
   212  
   213  	assert.Cmp(resp.StatusCode, expectedStatus, "Status code is OK")
   214  	assertBody(assert, resp, expectedBody)
   215  }
   216  
   217  func TestNewJsonResponder(t *testing.T) {
   218  	assert := td.Assert(t)
   219  
   220  	assert.Run("OK", func(assert *td.T) {
   221  		r, err := NewJsonResponder(200, map[string]int{"foo": 42})
   222  		if assert.CmpNoError(err) {
   223  			checkResponder(assert, r, 200, `{"foo":42}`)
   224  		}
   225  	})
   226  
   227  	assert.Run("OK file", func(assert *td.T) {
   228  		dir, cleanup := tmpDir(assert)
   229  		defer cleanup()
   230  		fileName := filepath.Join(dir, "ok.json")
   231  		writeFile(assert, fileName, []byte(`{  "foo"  :  42  }`))
   232  
   233  		r, err := NewJsonResponder(200, File(fileName))
   234  		if assert.CmpNoError(err) {
   235  			checkResponder(assert, r, 200, `{"foo":42}`)
   236  		}
   237  	})
   238  
   239  	assert.Run("Error", func(assert *td.T) {
   240  		r, err := NewJsonResponder(200, func() {})
   241  		assert.CmpError(err)
   242  		assert.Nil(r)
   243  	})
   244  
   245  	assert.Run("OK don't panic", func(assert *td.T) {
   246  		assert.CmpNotPanic(
   247  			func() {
   248  				r := NewJsonResponderOrPanic(200, map[string]int{"foo": 42})
   249  				checkResponder(assert, r, 200, `{"foo":42}`)
   250  			})
   251  	})
   252  
   253  	assert.Run("Panic", func(assert *td.T) {
   254  		assert.CmpPanic(
   255  			func() { NewJsonResponderOrPanic(200, func() {}) },
   256  			td.Ignore())
   257  	})
   258  }
   259  
   260  type schemaXML struct {
   261  	Hello string `xml:"hello"`
   262  }
   263  
   264  func TestNewXmlResponse(t *testing.T) {
   265  	assert := td.Assert(t)
   266  
   267  	body := &schemaXML{"world"}
   268  
   269  	b, err := xml.Marshal(body)
   270  	if err != nil {
   271  		t.Fatalf("Cannot xml.Marshal expected body: %s", err)
   272  	}
   273  	expectedBody := string(b)
   274  
   275  	dir, cleanup := tmpDir(assert)
   276  	defer cleanup()
   277  	fileName := filepath.Join(dir, "ok.xml")
   278  	writeFile(assert, fileName, b)
   279  
   280  	for i, test := range []struct {
   281  		body     interface{}
   282  		expected string
   283  	}{
   284  		{body: body, expected: expectedBody},
   285  		{body: File(fileName), expected: expectedBody},
   286  	} {
   287  		assert.Run(fmt.Sprintf("#%d", i), func(assert *td.T) {
   288  			response, err := NewXmlResponse(200, test.body)
   289  			if !assert.CmpNoError(err) {
   290  				return
   291  			}
   292  			assert.Cmp(response.StatusCode, 200)
   293  			assert.Cmp(response.Header.Get("Content-Type"), "application/xml")
   294  			assertBody(assert, response, test.expected)
   295  		})
   296  	}
   297  
   298  	// Error case
   299  	response, err := NewXmlResponse(200, func() {})
   300  	assert.CmpError(err)
   301  	assert.Nil(response)
   302  }
   303  
   304  func TestNewXmlResponder(t *testing.T) {
   305  	assert, require := td.AssertRequire(t)
   306  
   307  	body := &schemaXML{"world"}
   308  
   309  	b, err := xml.Marshal(body)
   310  	require.CmpNoError(err)
   311  	expectedBody := string(b)
   312  
   313  	assert.Run("OK", func(assert *td.T) {
   314  		r, err := NewXmlResponder(200, body)
   315  		if assert.CmpNoError(err) {
   316  			checkResponder(assert, r, 200, expectedBody)
   317  		}
   318  	})
   319  
   320  	assert.Run("OK file", func(assert *td.T) {
   321  		dir, cleanup := tmpDir(assert)
   322  		defer cleanup()
   323  		fileName := filepath.Join(dir, "ok.xml")
   324  		writeFile(assert, fileName, b)
   325  
   326  		r, err := NewXmlResponder(200, File(fileName))
   327  		if assert.CmpNoError(err) {
   328  			checkResponder(assert, r, 200, expectedBody)
   329  		}
   330  	})
   331  
   332  	assert.Run("Error", func(assert *td.T) {
   333  		r, err := NewXmlResponder(200, func() {})
   334  		assert.CmpError(err)
   335  		assert.Nil(r)
   336  	})
   337  
   338  	assert.Run("OK don't panic", func(assert *td.T) {
   339  		assert.CmpNotPanic(
   340  			func() {
   341  				r := NewXmlResponderOrPanic(200, body)
   342  				checkResponder(assert, r, 200, expectedBody)
   343  			})
   344  	})
   345  
   346  	assert.Run("Panic", func(assert *td.T) {
   347  		assert.CmpPanic(
   348  			func() { NewXmlResponderOrPanic(200, func() {}) },
   349  			td.Ignore())
   350  	})
   351  }
   352  
   353  func TestNewErrorResponder(t *testing.T) {
   354  	assert, require := td.AssertRequire(t)
   355  
   356  	origError := errors.New("oh no")
   357  	responder := NewErrorResponder(origError)
   358  
   359  	req, err := http.NewRequest(http.MethodGet, testURL, nil)
   360  	require.CmpNoError(err)
   361  
   362  	response, err := responder(req)
   363  	assert.Cmp(err, origError)
   364  	assert.Nil(response)
   365  }
   366  
   367  func TestResponseBody(t *testing.T) {
   368  	assert := td.Assert(t)
   369  
   370  	const (
   371  		body   = "hello world"
   372  		status = 200
   373  	)
   374  
   375  	assert.Run("http.Response", func(assert *td.T) {
   376  		for i, response := range []*http.Response{
   377  			NewBytesResponse(status, []byte(body)),
   378  			NewStringResponse(status, body),
   379  		} {
   380  			assert.Run(fmt.Sprintf("resp #%d", i), func(assert *td.T) {
   381  				assertBody(assert, response, body)
   382  
   383  				assert.Cmp(response.StatusCode, status)
   384  
   385  				var buf [1]byte
   386  				_, err := response.Body.Read(buf[:])
   387  				assert.Cmp(err, io.EOF)
   388  			})
   389  		}
   390  	})
   391  
   392  	assert.Run("Responder", func(assert *td.T) {
   393  		for i, responder := range []Responder{
   394  			NewBytesResponder(200, []byte(body)),
   395  			NewStringResponder(200, body),
   396  		} {
   397  			assert.Run(fmt.Sprintf("resp #%d", i), func(assert *td.T) {
   398  				req, _ := http.NewRequest("GET", "http://foo.bar", nil)
   399  				response, err := responder(req)
   400  				if !assert.CmpNoError(err) {
   401  					return
   402  				}
   403  
   404  				assertBody(assert, response, body)
   405  
   406  				var buf [1]byte
   407  				_, err = response.Body.Read(buf[:])
   408  				assert.Cmp(err, io.EOF)
   409  			})
   410  		}
   411  	})
   412  }
   413  
   414  func TestResponder(t *testing.T) {
   415  	req, err := http.NewRequest(http.MethodGet, "http://foo.bar", nil)
   416  	td.Require(t).CmpNoError(err)
   417  
   418  	resp := &http.Response{}
   419  
   420  	chk := func(r Responder, expectedResp *http.Response, expectedErr string) {
   421  		t.Helper()
   422  		gotResp, gotErr := r(req)
   423  		td.CmpShallow(t, gotResp, expectedResp)
   424  		var gotErrStr string
   425  		if gotErr != nil {
   426  			gotErrStr = gotErr.Error()
   427  		}
   428  		td.Cmp(t, gotErrStr, expectedErr)
   429  	}
   430  	called := false
   431  	chkNotCalled := func() {
   432  		if called {
   433  			t.Helper()
   434  			t.Errorf("Original responder should not be called")
   435  			called = false
   436  		}
   437  	}
   438  	chkCalled := func() {
   439  		if !called {
   440  			t.Helper()
   441  			t.Errorf("Original responder should be called")
   442  		}
   443  		called = false
   444  	}
   445  
   446  	r := Responder(func(*http.Request) (*http.Response, error) {
   447  		called = true
   448  		return resp, nil
   449  	})
   450  	chk(r, resp, "")
   451  	chkCalled()
   452  
   453  	//
   454  	// Once
   455  	ro := r.Once()
   456  	chk(ro, resp, "")
   457  	chkCalled()
   458  
   459  	chk(ro, nil, "Responder not found for GET http://foo.bar (coz Once and already called 2 times)")
   460  	chkNotCalled()
   461  
   462  	chk(ro, nil, "Responder not found for GET http://foo.bar (coz Once and already called 3 times)")
   463  	chkNotCalled()
   464  
   465  	ro = r.Once(func(args ...interface{}) {})
   466  	chk(ro, resp, "")
   467  	chkCalled()
   468  
   469  	chk(ro, nil, "Responder not found for GET http://foo.bar (coz Once and already called 2 times)")
   470  	chkNotCalled()
   471  
   472  	//
   473  	// Times
   474  	rt := r.Times(2)
   475  	chk(rt, resp, "")
   476  	chkCalled()
   477  
   478  	chk(rt, resp, "")
   479  	chkCalled()
   480  
   481  	chk(rt, nil, "Responder not found for GET http://foo.bar (coz Times and already called 3 times)")
   482  	chkNotCalled()
   483  
   484  	chk(rt, nil, "Responder not found for GET http://foo.bar (coz Times and already called 4 times)")
   485  	chkNotCalled()
   486  
   487  	rt = r.Times(1, func(args ...interface{}) {})
   488  	chk(rt, resp, "")
   489  	chkCalled()
   490  
   491  	chk(rt, nil, "Responder not found for GET http://foo.bar (coz Times and already called 2 times)")
   492  	chkNotCalled()
   493  
   494  	//
   495  	// Trace
   496  	rt = r.Trace(func(args ...interface{}) {})
   497  	chk(rt, resp, "")
   498  	chkCalled()
   499  
   500  	chk(rt, resp, "")
   501  	chkCalled()
   502  
   503  	//
   504  	// Delay
   505  	rt = r.Delay(100 * time.Millisecond)
   506  	before := time.Now()
   507  	chk(rt, resp, "")
   508  	duration := time.Since(before)
   509  	chkCalled()
   510  	td.Cmp(t, duration, td.Gte(100*time.Millisecond), "Responder is delayed")
   511  }
   512  
   513  func TestResponder_Then(t *testing.T) {
   514  	assert, require := td.AssertRequire(t)
   515  
   516  	req, err := http.NewRequest(http.MethodGet, "http://foo.bar", nil)
   517  	require.CmpNoError(err)
   518  
   519  	//
   520  	// Then
   521  	var stack string
   522  	newResponder := func(level string) Responder {
   523  		return func(*http.Request) (*http.Response, error) {
   524  			stack += level
   525  			return NewStringResponse(200, level), nil
   526  		}
   527  	}
   528  	var rt Responder
   529  	chk := func(assert *td.T, expectedLevel, expectedStack string) {
   530  		assert.Helper()
   531  		resp, err := rt(req)
   532  		if !assert.CmpNoError(err, "Responder call") {
   533  			return
   534  		}
   535  		b, err := ioutil.ReadAll(resp.Body)
   536  		if !assert.CmpNoError(err, "Read response") {
   537  			return
   538  		}
   539  		assert.String(b, expectedLevel)
   540  		assert.Cmp(stack, expectedStack)
   541  	}
   542  
   543  	A, B, C := newResponder("A"), newResponder("B"), newResponder("C")
   544  	D, E, F := newResponder("D"), newResponder("E"), newResponder("F")
   545  
   546  	assert.Run("simple", func(assert *td.T) {
   547  		// (r=A,then=B)
   548  		rt = A.Then(B)
   549  
   550  		chk(assert, "A", "A")
   551  		chk(assert, "B", "AB")
   552  		chk(assert, "B", "ABB")
   553  		chk(assert, "B", "ABBB")
   554  	})
   555  
   556  	stack = ""
   557  
   558  	assert.Run("simple chained", func(assert *td.T) {
   559  		//             (r=A,then=B)
   560  		//          (r=↑,then=C)
   561  		//       (r=↑,then=D)
   562  		//    (r=↑,then=E)
   563  		// (r=↑,then=F)
   564  		rt = A.Then(B).
   565  			Then(C).
   566  			Then(D).
   567  			Then(E).
   568  			Then(F)
   569  
   570  		chk(assert, "A", "A")
   571  		chk(assert, "B", "AB")
   572  		chk(assert, "C", "ABC")
   573  		chk(assert, "D", "ABCD")
   574  		chk(assert, "E", "ABCDE")
   575  		chk(assert, "F", "ABCDEF")
   576  		chk(assert, "F", "ABCDEFF")
   577  		chk(assert, "F", "ABCDEFFF")
   578  	})
   579  
   580  	stack = ""
   581  
   582  	assert.Run("Then Responder as Then param", func(assert *td.T) {
   583  		assert.CmpPanic(
   584  			func() { A.Then(B.Then(C)) },
   585  			"Then() does not accept another Then() Responder as parameter")
   586  	})
   587  }
   588  
   589  func TestParallelResponder(t *testing.T) {
   590  	req, err := http.NewRequest(http.MethodGet, "http://foo.bar", nil)
   591  	td.Require(t).CmpNoError(err)
   592  
   593  	body := strings.Repeat("ABC-", 1000)
   594  
   595  	for ir, r := range []Responder{
   596  		NewStringResponder(200, body),
   597  		NewBytesResponder(200, []byte(body)),
   598  	} {
   599  		var wg sync.WaitGroup
   600  		for n := 0; n < 100; n++ {
   601  			wg.Add(1)
   602  			go func() {
   603  				defer wg.Done()
   604  				resp, _ := r(req)
   605  				b, err := ioutil.ReadAll(resp.Body)
   606  				td.CmpNoError(t, err, "resp #%d", ir)
   607  				td.CmpLen(t, b, 4000, "resp #%d", ir)
   608  				td.CmpHasPrefix(t, b, "ABC-", "resp #%d", ir)
   609  			}()
   610  		}
   611  		wg.Wait()
   612  	}
   613  }
   614  

View as plain text