jarcoal/httpmock

test httpstatus 302 on resty OnAfterResponse not expected

daviyang35 opened this issue · 3 comments

I want use resty OnAfterResponse interface disable 302 auto redirect and replace with my own error.
when i tested this goal, then return "no responder found"

import (
	"errors"
	"fmt"
	"github.com/go-resty/resty/v2"
	"github.com/jarcoal/httpmock"
	"github.com/stretchr/testify/assert"
	"net/http"
	"strings"
	"testing"
	"time"
)

var ErrNoAuth = errors.New("no auth")

func createClient() *resty.Client {
	client := resty.New()
	client.EnableTrace()
	client.SetTimeout(time.Second * 60)
	client.SetRetryCount(0)
	client.SetBaseURL("http://mock.server")

	client.OnAfterResponse(func(client *resty.Client, response *resty.Response) error {
		switch response.StatusCode() {
		case http.StatusFound:
			location := response.Header().Get("Location")
			if strings.Contains(location, "/account/login") {
				fmt.Printf("matched 302 location. return ErrNoAuth\n")
				return ErrNoAuth
			}
		}
		return nil
	})

	return client
}

func TestForResty302(t *testing.T) {
	client := createClient()

	httpmock.Activate()
	httpmock.ActivateNonDefault(client.GetClient())
	defer httpmock.DeactivateAndReset()

	httpmock.RegisterResponder("POST", "/api/v1/some_action", func(request *http.Request) (*http.Response, error) {
		response := httpmock.NewStringResponse(http.StatusFound, "")
		response.Header.Set("Location", "https://new.server/account/login")
		return response, nil
	})

	post, err := client.R().Post("/api/v1/some_action")
	_ = post
	_ = err
	// Output: Post "https://new.server/account/login": no responder found
	fmt.Printf("%+v\n", err)

	// Expect
	assert.ErrorIs(t, err, ErrNoAuth)
}

Hello,
You set a "Location" header in your mocked response of POST /api/v1/some_action, so you have to provide a responder for this new URL...

I found a way to skip this problem.

import (
	"errors"
	"fmt"
	"github.com/go-resty/resty/v2"
	"github.com/jarcoal/httpmock"
	"github.com/stretchr/testify/assert"
	"net/http"
	"strings"
	"testing"
	"time"
)

var ErrNoAuth = errors.New("no auth")

func createClient() *resty.Client {
	client := resty.New()
	client.EnableTrace()
	client.SetTimeout(time.Second * 60)
	client.SetRetryCount(0)
	client.SetBaseURL("http://mock.server")

	client.SetRedirectPolicy(resty.RedirectPolicyFunc(func(request *http.Request, requests []*http.Request) error {
		if strings.Contains(request.URL.Path, "/account/login") {
			fmt.Printf("RedirectPolicy matched 302 location. return ErrNoAuth\n")
			return ErrNoAuth
		}
		return nil
	}))

	//client.OnAfterResponse(func(client *resty.Client, response *resty.Response) error {
	//	switch response.StatusCode() {
	//	case http.StatusFound:
	//		location := response.Header().Get("Location")
	//		if strings.Contains(location, "/account/login") {
	//			fmt.Printf("OnAfterResponse matched 302 location. return ErrNoAuth\n")
	//			return ErrNoAuth
	//		}
	//	}
	//	return nil
	//})

	return client
}

func TestForResty302(t *testing.T) {
	client := createClient()

	httpmock.Activate()
	httpmock.ActivateNonDefault(client.GetClient())
	defer httpmock.DeactivateAndReset()

	httpmock.RegisterResponder("POST", "/api/v1/some_action", func(request *http.Request) (*http.Response, error) {
		response := httpmock.NewStringResponse(http.StatusFound, "")
		response.Header.Set("Location", "https://new.server/account/login")
		return response, nil
	})

	_, err := client.R().Post("/api/v1/some_action")

	// Expect
	assert.ErrorIs(t, err, ErrNoAuth)
}

Hello, You set a "Location" header in your mocked response of POST /api/v1/some_action, so you have to provide a responder for this new URL...

Using a new Responder is a bit counterintuitive. This is because I originally intended to test whether OnAfterResponse was in effect.

Only 302 redirects are special and can be intercepted by the SetRedirectPolicy interface provided by resty.
In this case, use httpmock to set the server response message to get the correct result.

Thanks for your work.