Add tests for the request builder
This commit is contained in:
		
							parent
							
								
									0bc0feb4bb
								
							
						
					
					
						commit
						21ef86b594
					
				|  | @ -0,0 +1,384 @@ | ||||||
|  | package requests | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"context" | ||||||
|  | 	"encoding/base64" | ||||||
|  | 	"encoding/json" | ||||||
|  | 	"fmt" | ||||||
|  | 	"io/ioutil" | ||||||
|  | 	"net/http" | ||||||
|  | 
 | ||||||
|  | 	"github.com/bitly/go-simplejson" | ||||||
|  | 	. "github.com/onsi/ginkgo" | ||||||
|  | 	. "github.com/onsi/gomega" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | var _ = Describe("Builder suite", func() { | ||||||
|  | 	var b Builder | ||||||
|  | 	getBuilder := func() Builder { return b } | ||||||
|  | 
 | ||||||
|  | 	baseHeaders := http.Header{ | ||||||
|  | 		"Accept-Encoding": []string{"gzip"}, | ||||||
|  | 		"User-Agent":      []string{"Go-http-client/1.1"}, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	BeforeEach(func() { | ||||||
|  | 		// Most tests will request the server address
 | ||||||
|  | 		b = New(serverAddr + "/json/path") | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	Context("with a basic request", func() { | ||||||
|  | 		assertSuccessfulRequest(getBuilder, testHTTPRequest{ | ||||||
|  | 			Method:     "GET", | ||||||
|  | 			Header:     baseHeaders, | ||||||
|  | 			Body:       []byte{}, | ||||||
|  | 			RequestURI: "/json/path", | ||||||
|  | 		}) | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	Context("with a context", func() { | ||||||
|  | 		var ctx context.Context | ||||||
|  | 		var cancel context.CancelFunc | ||||||
|  | 
 | ||||||
|  | 		BeforeEach(func() { | ||||||
|  | 			ctx, cancel = context.WithCancel(context.Background()) | ||||||
|  | 			b = b.WithContext(ctx) | ||||||
|  | 		}) | ||||||
|  | 
 | ||||||
|  | 		AfterEach(func() { | ||||||
|  | 			cancel() | ||||||
|  | 		}) | ||||||
|  | 
 | ||||||
|  | 		assertSuccessfulRequest(getBuilder, testHTTPRequest{ | ||||||
|  | 			Method:     "GET", | ||||||
|  | 			Header:     baseHeaders, | ||||||
|  | 			Body:       []byte{}, | ||||||
|  | 			RequestURI: "/json/path", | ||||||
|  | 		}) | ||||||
|  | 
 | ||||||
|  | 		Context("if the context is cancelled", func() { | ||||||
|  | 			BeforeEach(func() { | ||||||
|  | 				cancel() | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			assertRequestError(getBuilder, "context canceled") | ||||||
|  | 		}) | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	Context("with a body", func() { | ||||||
|  | 		const body = "{\"some\": \"body\"}" | ||||||
|  | 		header := baseHeaders.Clone() | ||||||
|  | 		header.Set("Content-Length", fmt.Sprintf("%d", len(body))) | ||||||
|  | 
 | ||||||
|  | 		BeforeEach(func() { | ||||||
|  | 			buf := bytes.NewBuffer([]byte(body)) | ||||||
|  | 			b = b.WithBody(buf) | ||||||
|  | 		}) | ||||||
|  | 
 | ||||||
|  | 		assertSuccessfulRequest(getBuilder, testHTTPRequest{ | ||||||
|  | 			Method:     "GET", | ||||||
|  | 			Header:     header, | ||||||
|  | 			Body:       []byte(body), | ||||||
|  | 			RequestURI: "/json/path", | ||||||
|  | 		}) | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	Context("with a method", func() { | ||||||
|  | 		Context("POST with a body", func() { | ||||||
|  | 			const body = "{\"some\": \"body\"}" | ||||||
|  | 			header := baseHeaders.Clone() | ||||||
|  | 			header.Set("Content-Length", fmt.Sprintf("%d", len(body))) | ||||||
|  | 
 | ||||||
|  | 			BeforeEach(func() { | ||||||
|  | 				buf := bytes.NewBuffer([]byte(body)) | ||||||
|  | 				b = b.WithMethod("POST").WithBody(buf) | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			assertSuccessfulRequest(getBuilder, testHTTPRequest{ | ||||||
|  | 				Method:     "POST", | ||||||
|  | 				Header:     header, | ||||||
|  | 				Body:       []byte(body), | ||||||
|  | 				RequestURI: "/json/path", | ||||||
|  | 			}) | ||||||
|  | 		}) | ||||||
|  | 
 | ||||||
|  | 		Context("POST without a body", func() { | ||||||
|  | 			header := baseHeaders.Clone() | ||||||
|  | 			header.Set("Content-Length", "0") | ||||||
|  | 
 | ||||||
|  | 			BeforeEach(func() { | ||||||
|  | 				b = b.WithMethod("POST") | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			assertSuccessfulRequest(getBuilder, testHTTPRequest{ | ||||||
|  | 				Method:     "POST", | ||||||
|  | 				Header:     header, | ||||||
|  | 				Body:       []byte{}, | ||||||
|  | 				RequestURI: "/json/path", | ||||||
|  | 			}) | ||||||
|  | 		}) | ||||||
|  | 
 | ||||||
|  | 		Context("OPTIONS", func() { | ||||||
|  | 			BeforeEach(func() { | ||||||
|  | 				b = b.WithMethod("OPTIONS") | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			assertSuccessfulRequest(getBuilder, testHTTPRequest{ | ||||||
|  | 				Method:     "OPTIONS", | ||||||
|  | 				Header:     baseHeaders, | ||||||
|  | 				Body:       []byte{}, | ||||||
|  | 				RequestURI: "/json/path", | ||||||
|  | 			}) | ||||||
|  | 		}) | ||||||
|  | 
 | ||||||
|  | 		Context("INVALID-\\t-METHOD", func() { | ||||||
|  | 			BeforeEach(func() { | ||||||
|  | 				b = b.WithMethod("INVALID-\t-METHOD") | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			assertRequestError(getBuilder, "error creating request: net/http: invalid method \"INVALID-\\t-METHOD\"") | ||||||
|  | 		}) | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	Context("with headers", func() { | ||||||
|  | 		Context("setting a header", func() { | ||||||
|  | 			header := baseHeaders.Clone() | ||||||
|  | 			header.Set("header", "value") | ||||||
|  | 
 | ||||||
|  | 			BeforeEach(func() { | ||||||
|  | 				b = b.SetHeader("header", "value") | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			assertSuccessfulRequest(getBuilder, testHTTPRequest{ | ||||||
|  | 				Method:     "GET", | ||||||
|  | 				Header:     header, | ||||||
|  | 				Body:       []byte{}, | ||||||
|  | 				RequestURI: "/json/path", | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			Context("then replacing the headers", func() { | ||||||
|  | 				replacementHeaders := http.Header{ | ||||||
|  | 					"Accept-Encoding": []string{"*"}, | ||||||
|  | 					"User-Agent":      []string{"test-agent"}, | ||||||
|  | 					"Foo":             []string{"bar, baz"}, | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				BeforeEach(func() { | ||||||
|  | 					b = b.WithHeaders(replacementHeaders) | ||||||
|  | 				}) | ||||||
|  | 
 | ||||||
|  | 				assertSuccessfulRequest(getBuilder, testHTTPRequest{ | ||||||
|  | 					Method:     "GET", | ||||||
|  | 					Header:     replacementHeaders, | ||||||
|  | 					Body:       []byte{}, | ||||||
|  | 					RequestURI: "/json/path", | ||||||
|  | 				}) | ||||||
|  | 			}) | ||||||
|  | 		}) | ||||||
|  | 
 | ||||||
|  | 		Context("replacing the header", func() { | ||||||
|  | 			replacementHeaders := http.Header{ | ||||||
|  | 				"Accept-Encoding": []string{"*"}, | ||||||
|  | 				"User-Agent":      []string{"test-agent"}, | ||||||
|  | 				"Foo":             []string{"bar, baz"}, | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			BeforeEach(func() { | ||||||
|  | 				b = b.WithHeaders(replacementHeaders) | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			assertSuccessfulRequest(getBuilder, testHTTPRequest{ | ||||||
|  | 				Method:     "GET", | ||||||
|  | 				Header:     replacementHeaders, | ||||||
|  | 				Body:       []byte{}, | ||||||
|  | 				RequestURI: "/json/path", | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			Context("then setting a header", func() { | ||||||
|  | 				header := replacementHeaders.Clone() | ||||||
|  | 				header.Set("User-Agent", "different-agent") | ||||||
|  | 
 | ||||||
|  | 				BeforeEach(func() { | ||||||
|  | 					b = b.SetHeader("User-Agent", "different-agent") | ||||||
|  | 				}) | ||||||
|  | 
 | ||||||
|  | 				assertSuccessfulRequest(getBuilder, testHTTPRequest{ | ||||||
|  | 					Method:     "GET", | ||||||
|  | 					Header:     header, | ||||||
|  | 					Body:       []byte{}, | ||||||
|  | 					RequestURI: "/json/path", | ||||||
|  | 				}) | ||||||
|  | 			}) | ||||||
|  | 		}) | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	Context("if the request has been completed and then modified", func() { | ||||||
|  | 		BeforeEach(func() { | ||||||
|  | 			_, err := b.Do() | ||||||
|  | 			Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 
 | ||||||
|  | 			b.WithMethod("POST") | ||||||
|  | 		}) | ||||||
|  | 
 | ||||||
|  | 		Context("should not redo the request", func() { | ||||||
|  | 			assertSuccessfulRequest(getBuilder, testHTTPRequest{ | ||||||
|  | 				Method:     "GET", | ||||||
|  | 				Header:     baseHeaders, | ||||||
|  | 				Body:       []byte{}, | ||||||
|  | 				RequestURI: "/json/path", | ||||||
|  | 			}) | ||||||
|  | 		}) | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	Context("when the requested page is not found", func() { | ||||||
|  | 		BeforeEach(func() { | ||||||
|  | 			b = New(serverAddr + "/not-found") | ||||||
|  | 		}) | ||||||
|  | 
 | ||||||
|  | 		assertJSONError(getBuilder, "404 page not found") | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	Context("when the requested page is not valid JSON", func() { | ||||||
|  | 		BeforeEach(func() { | ||||||
|  | 			b = New(serverAddr + "/string/path") | ||||||
|  | 		}) | ||||||
|  | 
 | ||||||
|  | 		assertJSONError(getBuilder, "invalid character 'O' looking for beginning of value") | ||||||
|  | 	}) | ||||||
|  | }) | ||||||
|  | 
 | ||||||
|  | func assertSuccessfulRequest(builder func() Builder, expectedRequest testHTTPRequest) { | ||||||
|  | 	Context("Do", func() { | ||||||
|  | 		var resp *http.Response | ||||||
|  | 
 | ||||||
|  | 		BeforeEach(func() { | ||||||
|  | 			var err error | ||||||
|  | 			resp, err = builder().Do() | ||||||
|  | 			Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 		}) | ||||||
|  | 
 | ||||||
|  | 		It("returns a successful status", func() { | ||||||
|  | 			Expect(resp.StatusCode).To(Equal(http.StatusOK)) | ||||||
|  | 		}) | ||||||
|  | 
 | ||||||
|  | 		It("made the expected request", func() { | ||||||
|  | 			body, err := ioutil.ReadAll(resp.Body) | ||||||
|  | 			Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 			resp.Body.Close() | ||||||
|  | 
 | ||||||
|  | 			actualRequest := testHTTPRequest{} | ||||||
|  | 			Expect(json.Unmarshal(body, &actualRequest)).To(Succeed()) | ||||||
|  | 
 | ||||||
|  | 			Expect(actualRequest).To(Equal(expectedRequest)) | ||||||
|  | 		}) | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	Context("UnmarshalInto", func() { | ||||||
|  | 		var actualRequest testHTTPRequest | ||||||
|  | 
 | ||||||
|  | 		BeforeEach(func() { | ||||||
|  | 			Expect(builder().UnmarshalInto(&actualRequest)).To(Succeed()) | ||||||
|  | 		}) | ||||||
|  | 
 | ||||||
|  | 		It("made the expected request", func() { | ||||||
|  | 			Expect(actualRequest).To(Equal(expectedRequest)) | ||||||
|  | 		}) | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	Context("UnmarshalJSON", func() { | ||||||
|  | 		var response *simplejson.Json | ||||||
|  | 
 | ||||||
|  | 		BeforeEach(func() { | ||||||
|  | 			var err error | ||||||
|  | 			response, err = builder().UnmarshalJSON() | ||||||
|  | 			Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 		}) | ||||||
|  | 
 | ||||||
|  | 		It("made the expected reqest", func() { | ||||||
|  | 			header := http.Header{} | ||||||
|  | 			for key, value := range response.Get("Header").MustMap() { | ||||||
|  | 				vs, ok := value.([]interface{}) | ||||||
|  | 				Expect(ok).To(BeTrue()) | ||||||
|  | 				svs := []string{} | ||||||
|  | 				for _, v := range vs { | ||||||
|  | 					sv, ok := v.(string) | ||||||
|  | 					Expect(ok).To(BeTrue()) | ||||||
|  | 					svs = append(svs, sv) | ||||||
|  | 				} | ||||||
|  | 				header[key] = svs | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			// Other json unmarhsallers base64 decode byte slices automatically
 | ||||||
|  | 			body, err := base64.StdEncoding.DecodeString(response.Get("Body").MustString()) | ||||||
|  | 			Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 
 | ||||||
|  | 			actualRequest := testHTTPRequest{ | ||||||
|  | 				Method:     response.Get("Method").MustString(), | ||||||
|  | 				Header:     header, | ||||||
|  | 				Body:       body, | ||||||
|  | 				RequestURI: response.Get("RequestURI").MustString(), | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			Expect(actualRequest).To(Equal(expectedRequest)) | ||||||
|  | 		}) | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func assertRequestError(builder func() Builder, errorMessage string) { | ||||||
|  | 	Context("Do", func() { | ||||||
|  | 		It("returns an error", func() { | ||||||
|  | 			resp, err := builder().Do() | ||||||
|  | 			Expect(err).To(MatchError(ContainSubstring(errorMessage))) | ||||||
|  | 			Expect(resp).To(BeNil()) | ||||||
|  | 		}) | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	Context("UnmarshalInto", func() { | ||||||
|  | 		It("returns an error", func() { | ||||||
|  | 			var actualRequest testHTTPRequest | ||||||
|  | 			err := builder().UnmarshalInto(&actualRequest) | ||||||
|  | 			Expect(err).To(MatchError(ContainSubstring(errorMessage))) | ||||||
|  | 
 | ||||||
|  | 			// Should be empty
 | ||||||
|  | 			Expect(actualRequest).To(Equal(testHTTPRequest{})) | ||||||
|  | 		}) | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	Context("UnmarshalJSON", func() { | ||||||
|  | 		It("returns an error", func() { | ||||||
|  | 			resp, err := builder().UnmarshalJSON() | ||||||
|  | 			Expect(err).To(MatchError(ContainSubstring(errorMessage))) | ||||||
|  | 			Expect(resp).To(BeNil()) | ||||||
|  | 		}) | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func assertJSONError(builder func() Builder, errorMessage string) { | ||||||
|  | 	Context("Do", func() { | ||||||
|  | 		It("does not return an error", func() { | ||||||
|  | 			resp, err := builder().Do() | ||||||
|  | 			Expect(err).To(BeNil()) | ||||||
|  | 			Expect(resp).ToNot(BeNil()) | ||||||
|  | 		}) | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	Context("UnmarshalInto", func() { | ||||||
|  | 		It("returns an error", func() { | ||||||
|  | 			var actualRequest testHTTPRequest | ||||||
|  | 			err := builder().UnmarshalInto(&actualRequest) | ||||||
|  | 			Expect(err).To(MatchError(ContainSubstring(errorMessage))) | ||||||
|  | 
 | ||||||
|  | 			// Should be empty
 | ||||||
|  | 			Expect(actualRequest).To(Equal(testHTTPRequest{})) | ||||||
|  | 		}) | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	Context("UnmarshalJSON", func() { | ||||||
|  | 		It("returns an error", func() { | ||||||
|  | 			resp, err := builder().UnmarshalJSON() | ||||||
|  | 			Expect(err).To(MatchError(ContainSubstring(errorMessage))) | ||||||
|  | 			Expect(resp).To(BeNil()) | ||||||
|  | 		}) | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  | @ -0,0 +1,96 @@ | ||||||
|  | package requests | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"encoding/json" | ||||||
|  | 	"fmt" | ||||||
|  | 	"io/ioutil" | ||||||
|  | 	"log" | ||||||
|  | 	"net/http" | ||||||
|  | 	"net/http/httptest" | ||||||
|  | 	"testing" | ||||||
|  | 
 | ||||||
|  | 	"github.com/oauth2-proxy/oauth2-proxy/pkg/logger" | ||||||
|  | 	. "github.com/onsi/ginkgo" | ||||||
|  | 	. "github.com/onsi/gomega" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | var ( | ||||||
|  | 	server     *httptest.Server | ||||||
|  | 	serverAddr string | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func TestRequetsSuite(t *testing.T) { | ||||||
|  | 	logger.SetOutput(GinkgoWriter) | ||||||
|  | 	log.SetOutput(GinkgoWriter) | ||||||
|  | 
 | ||||||
|  | 	RegisterFailHandler(Fail) | ||||||
|  | 	RunSpecs(t, "Requests Suite") | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | var _ = BeforeSuite(func() { | ||||||
|  | 	// Set up a webserver that reflects requests
 | ||||||
|  | 	mux := http.NewServeMux() | ||||||
|  | 	mux.Handle("/json/", &testHTTPUpstream{}) | ||||||
|  | 	mux.HandleFunc("/string/", func(rw http.ResponseWriter, _ *http.Request) { | ||||||
|  | 		rw.Write([]byte("OK")) | ||||||
|  | 	}) | ||||||
|  | 	server = httptest.NewServer(mux) | ||||||
|  | 	serverAddr = fmt.Sprintf("http://%s", server.Listener.Addr().String()) | ||||||
|  | }) | ||||||
|  | 
 | ||||||
|  | var _ = AfterSuite(func() { | ||||||
|  | 	server.Close() | ||||||
|  | }) | ||||||
|  | 
 | ||||||
|  | // testHTTPRequest is a struct used to capture the state of a request made to
 | ||||||
|  | // the test server
 | ||||||
|  | type testHTTPRequest struct { | ||||||
|  | 	Method     string | ||||||
|  | 	Header     http.Header | ||||||
|  | 	Body       []byte | ||||||
|  | 	RequestURI string | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type testHTTPUpstream struct{} | ||||||
|  | 
 | ||||||
|  | func (t *testHTTPUpstream) ServeHTTP(rw http.ResponseWriter, req *http.Request) { | ||||||
|  | 	request, err := toTestHTTPRequest(req) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.writeError(rw, err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	data, err := json.Marshal(request) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.writeError(rw, err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	rw.Header().Set("Content-Type", "application/json") | ||||||
|  | 	rw.Write(data) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (t *testHTTPUpstream) writeError(rw http.ResponseWriter, err error) { | ||||||
|  | 	rw.WriteHeader(500) | ||||||
|  | 	if err != nil { | ||||||
|  | 		rw.Write([]byte(err.Error())) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func toTestHTTPRequest(req *http.Request) (testHTTPRequest, error) { | ||||||
|  | 	requestBody := []byte{} | ||||||
|  | 	if req.Body != http.NoBody { | ||||||
|  | 		var err error | ||||||
|  | 		requestBody, err = ioutil.ReadAll(req.Body) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return testHTTPRequest{}, err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return testHTTPRequest{ | ||||||
|  | 		Method:     req.Method, | ||||||
|  | 		Header:     req.Header, | ||||||
|  | 		Body:       requestBody, | ||||||
|  | 		RequestURI: req.RequestURI, | ||||||
|  | 	}, nil | ||||||
|  | } | ||||||
		Loading…
	
		Reference in New Issue