summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/onsi/gomega/ghttp
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/onsi/gomega/ghttp')
-rw-r--r--vendor/github.com/onsi/gomega/ghttp/handlers.go313
-rw-r--r--vendor/github.com/onsi/gomega/ghttp/protobuf/protobuf.go3
-rw-r--r--vendor/github.com/onsi/gomega/ghttp/protobuf/simple_message.pb.go55
-rw-r--r--vendor/github.com/onsi/gomega/ghttp/protobuf/simple_message.proto9
-rw-r--r--vendor/github.com/onsi/gomega/ghttp/test_server.go381
-rw-r--r--vendor/github.com/onsi/gomega/ghttp/test_server_suite_test.go13
-rw-r--r--vendor/github.com/onsi/gomega/ghttp/test_server_test.go1089
7 files changed, 1863 insertions, 0 deletions
diff --git a/vendor/github.com/onsi/gomega/ghttp/handlers.go b/vendor/github.com/onsi/gomega/ghttp/handlers.go
new file mode 100644
index 0000000..63ff691
--- /dev/null
+++ b/vendor/github.com/onsi/gomega/ghttp/handlers.go
@@ -0,0 +1,313 @@
+package ghttp
+
+import (
+ "encoding/base64"
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "net/url"
+ "reflect"
+
+ "github.com/golang/protobuf/proto"
+ . "github.com/onsi/gomega"
+ "github.com/onsi/gomega/types"
+)
+
+//CombineHandler takes variadic list of handlers and produces one handler
+//that calls each handler in order.
+func CombineHandlers(handlers ...http.HandlerFunc) http.HandlerFunc {
+ return func(w http.ResponseWriter, req *http.Request) {
+ for _, handler := range handlers {
+ handler(w, req)
+ }
+ }
+}
+
+//VerifyRequest returns a handler that verifies that a request uses the specified method to connect to the specified path
+//You may also pass in an optional rawQuery string which is tested against the request's `req.URL.RawQuery`
+//
+//For path, you may pass in a string, in which case strict equality will be applied
+//Alternatively you can pass in a matcher (ContainSubstring("/foo") and MatchRegexp("/foo/[a-f0-9]+") for example)
+func VerifyRequest(method string, path interface{}, rawQuery ...string) http.HandlerFunc {
+ return func(w http.ResponseWriter, req *http.Request) {
+ Ω(req.Method).Should(Equal(method), "Method mismatch")
+ switch p := path.(type) {
+ case types.GomegaMatcher:
+ Ω(req.URL.Path).Should(p, "Path mismatch")
+ default:
+ Ω(req.URL.Path).Should(Equal(path), "Path mismatch")
+ }
+ if len(rawQuery) > 0 {
+ values, err := url.ParseQuery(rawQuery[0])
+ Ω(err).ShouldNot(HaveOccurred(), "Expected RawQuery is malformed")
+
+ Ω(req.URL.Query()).Should(Equal(values), "RawQuery mismatch")
+ }
+ }
+}
+
+//VerifyContentType returns a handler that verifies that a request has a Content-Type header set to the
+//specified value
+func VerifyContentType(contentType string) http.HandlerFunc {
+ return func(w http.ResponseWriter, req *http.Request) {
+ Ω(req.Header.Get("Content-Type")).Should(Equal(contentType))
+ }
+}
+
+//VerifyBasicAuth returns a handler that verifies the request contains a BasicAuth Authorization header
+//matching the passed in username and password
+func VerifyBasicAuth(username string, password string) http.HandlerFunc {
+ return func(w http.ResponseWriter, req *http.Request) {
+ auth := req.Header.Get("Authorization")
+ Ω(auth).ShouldNot(Equal(""), "Authorization header must be specified")
+
+ decoded, err := base64.StdEncoding.DecodeString(auth[6:])
+ Ω(err).ShouldNot(HaveOccurred())
+
+ Ω(string(decoded)).Should(Equal(fmt.Sprintf("%s:%s", username, password)), "Authorization mismatch")
+ }
+}
+
+//VerifyHeader returns a handler that verifies the request contains the passed in headers.
+//The passed in header keys are first canonicalized via http.CanonicalHeaderKey.
+//
+//The request must contain *all* the passed in headers, but it is allowed to have additional headers
+//beyond the passed in set.
+func VerifyHeader(header http.Header) http.HandlerFunc {
+ return func(w http.ResponseWriter, req *http.Request) {
+ for key, values := range header {
+ key = http.CanonicalHeaderKey(key)
+ Ω(req.Header[key]).Should(Equal(values), "Header mismatch for key: %s", key)
+ }
+ }
+}
+
+//VerifyHeaderKV returns a handler that verifies the request contains a header matching the passed in key and values
+//(recall that a `http.Header` is a mapping from string (key) to []string (values))
+//It is a convenience wrapper around `VerifyHeader` that allows you to avoid having to create an `http.Header` object.
+func VerifyHeaderKV(key string, values ...string) http.HandlerFunc {
+ return VerifyHeader(http.Header{key: values})
+}
+
+//VerifyBody returns a handler that verifies that the body of the request matches the passed in byte array.
+//It does this using Equal().
+func VerifyBody(expectedBody []byte) http.HandlerFunc {
+ return CombineHandlers(
+ func(w http.ResponseWriter, req *http.Request) {
+ body, err := ioutil.ReadAll(req.Body)
+ req.Body.Close()
+ Ω(err).ShouldNot(HaveOccurred())
+ Ω(body).Should(Equal(expectedBody), "Body Mismatch")
+ },
+ )
+}
+
+//VerifyJSON returns a handler that verifies that the body of the request is a valid JSON representation
+//matching the passed in JSON string. It does this using Gomega's MatchJSON method
+//
+//VerifyJSON also verifies that the request's content type is application/json
+func VerifyJSON(expectedJSON string) http.HandlerFunc {
+ return CombineHandlers(
+ VerifyContentType("application/json"),
+ func(w http.ResponseWriter, req *http.Request) {
+ body, err := ioutil.ReadAll(req.Body)
+ req.Body.Close()
+ Ω(err).ShouldNot(HaveOccurred())
+ Ω(body).Should(MatchJSON(expectedJSON), "JSON Mismatch")
+ },
+ )
+}
+
+//VerifyJSONRepresenting is similar to VerifyJSON. Instead of taking a JSON string, however, it
+//takes an arbitrary JSON-encodable object and verifies that the requests's body is a JSON representation
+//that matches the object
+func VerifyJSONRepresenting(object interface{}) http.HandlerFunc {
+ data, err := json.Marshal(object)
+ Ω(err).ShouldNot(HaveOccurred())
+ return CombineHandlers(
+ VerifyContentType("application/json"),
+ VerifyJSON(string(data)),
+ )
+}
+
+//VerifyForm returns a handler that verifies a request contains the specified form values.
+//
+//The request must contain *all* of the specified values, but it is allowed to have additional
+//form values beyond the passed in set.
+func VerifyForm(values url.Values) http.HandlerFunc {
+ return func(w http.ResponseWriter, r *http.Request) {
+ err := r.ParseForm()
+ Ω(err).ShouldNot(HaveOccurred())
+ for key, vals := range values {
+ Ω(r.Form[key]).Should(Equal(vals), "Form mismatch for key: %s", key)
+ }
+ }
+}
+
+//VerifyFormKV returns a handler that verifies a request contains a form key with the specified values.
+//
+//It is a convenience wrapper around `VerifyForm` that lets you avoid having to create a `url.Values` object.
+func VerifyFormKV(key string, values ...string) http.HandlerFunc {
+ return VerifyForm(url.Values{key: values})
+}
+
+//VerifyProtoRepresenting returns a handler that verifies that the body of the request is a valid protobuf
+//representation of the passed message.
+//
+//VerifyProtoRepresenting also verifies that the request's content type is application/x-protobuf
+func VerifyProtoRepresenting(expected proto.Message) http.HandlerFunc {
+ return CombineHandlers(
+ VerifyContentType("application/x-protobuf"),
+ func(w http.ResponseWriter, req *http.Request) {
+ body, err := ioutil.ReadAll(req.Body)
+ Ω(err).ShouldNot(HaveOccurred())
+ req.Body.Close()
+
+ expectedType := reflect.TypeOf(expected)
+ actualValuePtr := reflect.New(expectedType.Elem())
+
+ actual, ok := actualValuePtr.Interface().(proto.Message)
+ Ω(ok).Should(BeTrue(), "Message value is not a proto.Message")
+
+ err = proto.Unmarshal(body, actual)
+ Ω(err).ShouldNot(HaveOccurred(), "Failed to unmarshal protobuf")
+
+ Ω(actual).Should(Equal(expected), "ProtoBuf Mismatch")
+ },
+ )
+}
+
+func copyHeader(src http.Header, dst http.Header) {
+ for key, value := range src {
+ dst[key] = value
+ }
+}
+
+/*
+RespondWith returns a handler that responds to a request with the specified status code and body
+
+Body may be a string or []byte
+
+Also, RespondWith can be given an optional http.Header. The headers defined therein will be added to the response headers.
+*/
+func RespondWith(statusCode int, body interface{}, optionalHeader ...http.Header) http.HandlerFunc {
+ return func(w http.ResponseWriter, req *http.Request) {
+ if len(optionalHeader) == 1 {
+ copyHeader(optionalHeader[0], w.Header())
+ }
+ w.WriteHeader(statusCode)
+ switch x := body.(type) {
+ case string:
+ w.Write([]byte(x))
+ case []byte:
+ w.Write(x)
+ default:
+ Ω(body).Should(BeNil(), "Invalid type for body. Should be string or []byte.")
+ }
+ }
+}
+
+/*
+RespondWithPtr returns a handler that responds to a request with the specified status code and body
+
+Unlike RespondWith, you pass RepondWithPtr a pointer to the status code and body allowing different tests
+to share the same setup but specify different status codes and bodies.
+
+Also, RespondWithPtr can be given an optional http.Header. The headers defined therein will be added to the response headers.
+Since the http.Header can be mutated after the fact you don't need to pass in a pointer.
+*/
+func RespondWithPtr(statusCode *int, body interface{}, optionalHeader ...http.Header) http.HandlerFunc {
+ return func(w http.ResponseWriter, req *http.Request) {
+ if len(optionalHeader) == 1 {
+ copyHeader(optionalHeader[0], w.Header())
+ }
+ w.WriteHeader(*statusCode)
+ if body != nil {
+ switch x := (body).(type) {
+ case *string:
+ w.Write([]byte(*x))
+ case *[]byte:
+ w.Write(*x)
+ default:
+ Ω(body).Should(BeNil(), "Invalid type for body. Should be string or []byte.")
+ }
+ }
+ }
+}
+
+/*
+RespondWithJSONEncoded returns a handler that responds to a request with the specified status code and a body
+containing the JSON-encoding of the passed in object
+
+Also, RespondWithJSONEncoded can be given an optional http.Header. The headers defined therein will be added to the response headers.
+*/
+func RespondWithJSONEncoded(statusCode int, object interface{}, optionalHeader ...http.Header) http.HandlerFunc {
+ data, err := json.Marshal(object)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ var headers http.Header
+ if len(optionalHeader) == 1 {
+ headers = optionalHeader[0]
+ } else {
+ headers = make(http.Header)
+ }
+ if _, found := headers["Content-Type"]; !found {
+ headers["Content-Type"] = []string{"application/json"}
+ }
+ return RespondWith(statusCode, string(data), headers)
+}
+
+/*
+RespondWithJSONEncodedPtr behaves like RespondWithJSONEncoded but takes a pointer
+to a status code and object.
+
+This allows different tests to share the same setup but specify different status codes and JSON-encoded
+objects.
+
+Also, RespondWithJSONEncodedPtr can be given an optional http.Header. The headers defined therein will be added to the response headers.
+Since the http.Header can be mutated after the fact you don't need to pass in a pointer.
+*/
+func RespondWithJSONEncodedPtr(statusCode *int, object interface{}, optionalHeader ...http.Header) http.HandlerFunc {
+ return func(w http.ResponseWriter, req *http.Request) {
+ data, err := json.Marshal(object)
+ Ω(err).ShouldNot(HaveOccurred())
+ var headers http.Header
+ if len(optionalHeader) == 1 {
+ headers = optionalHeader[0]
+ } else {
+ headers = make(http.Header)
+ }
+ if _, found := headers["Content-Type"]; !found {
+ headers["Content-Type"] = []string{"application/json"}
+ }
+ copyHeader(headers, w.Header())
+ w.WriteHeader(*statusCode)
+ w.Write(data)
+ }
+}
+
+//RespondWithProto returns a handler that responds to a request with the specified status code and a body
+//containing the protobuf serialization of the provided message.
+//
+//Also, RespondWithProto can be given an optional http.Header. The headers defined therein will be added to the response headers.
+func RespondWithProto(statusCode int, message proto.Message, optionalHeader ...http.Header) http.HandlerFunc {
+ return func(w http.ResponseWriter, req *http.Request) {
+ data, err := proto.Marshal(message)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ var headers http.Header
+ if len(optionalHeader) == 1 {
+ headers = optionalHeader[0]
+ } else {
+ headers = make(http.Header)
+ }
+ if _, found := headers["Content-Type"]; !found {
+ headers["Content-Type"] = []string{"application/x-protobuf"}
+ }
+ copyHeader(headers, w.Header())
+
+ w.WriteHeader(statusCode)
+ w.Write(data)
+ }
+}
diff --git a/vendor/github.com/onsi/gomega/ghttp/protobuf/protobuf.go b/vendor/github.com/onsi/gomega/ghttp/protobuf/protobuf.go
new file mode 100644
index 0000000..b2972bc
--- /dev/null
+++ b/vendor/github.com/onsi/gomega/ghttp/protobuf/protobuf.go
@@ -0,0 +1,3 @@
+package protobuf
+
+//go:generate protoc --go_out=. simple_message.proto
diff --git a/vendor/github.com/onsi/gomega/ghttp/protobuf/simple_message.pb.go b/vendor/github.com/onsi/gomega/ghttp/protobuf/simple_message.pb.go
new file mode 100644
index 0000000..c55a484
--- /dev/null
+++ b/vendor/github.com/onsi/gomega/ghttp/protobuf/simple_message.pb.go
@@ -0,0 +1,55 @@
+// Code generated by protoc-gen-go.
+// source: simple_message.proto
+// DO NOT EDIT!
+
+/*
+Package protobuf is a generated protocol buffer package.
+
+It is generated from these files:
+ simple_message.proto
+
+It has these top-level messages:
+ SimpleMessage
+*/
+package protobuf
+
+import proto "github.com/golang/protobuf/proto"
+import fmt "fmt"
+import math "math"
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+type SimpleMessage struct {
+ Description *string `protobuf:"bytes,1,req,name=description" json:"description,omitempty"`
+ Id *int32 `protobuf:"varint,2,req,name=id" json:"id,omitempty"`
+ Metadata *string `protobuf:"bytes,3,opt,name=metadata" json:"metadata,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *SimpleMessage) Reset() { *m = SimpleMessage{} }
+func (m *SimpleMessage) String() string { return proto.CompactTextString(m) }
+func (*SimpleMessage) ProtoMessage() {}
+
+func (m *SimpleMessage) GetDescription() string {
+ if m != nil && m.Description != nil {
+ return *m.Description
+ }
+ return ""
+}
+
+func (m *SimpleMessage) GetId() int32 {
+ if m != nil && m.Id != nil {
+ return *m.Id
+ }
+ return 0
+}
+
+func (m *SimpleMessage) GetMetadata() string {
+ if m != nil && m.Metadata != nil {
+ return *m.Metadata
+ }
+ return ""
+}
diff --git a/vendor/github.com/onsi/gomega/ghttp/protobuf/simple_message.proto b/vendor/github.com/onsi/gomega/ghttp/protobuf/simple_message.proto
new file mode 100644
index 0000000..35b7145
--- /dev/null
+++ b/vendor/github.com/onsi/gomega/ghttp/protobuf/simple_message.proto
@@ -0,0 +1,9 @@
+syntax = "proto2";
+
+package protobuf;
+
+message SimpleMessage {
+ required string description = 1;
+ required int32 id = 2;
+ optional string metadata = 3;
+}
diff --git a/vendor/github.com/onsi/gomega/ghttp/test_server.go b/vendor/github.com/onsi/gomega/ghttp/test_server.go
new file mode 100644
index 0000000..40d92de
--- /dev/null
+++ b/vendor/github.com/onsi/gomega/ghttp/test_server.go
@@ -0,0 +1,381 @@
+/*
+Package ghttp supports testing HTTP clients by providing a test server (simply a thin wrapper around httptest's server) that supports
+registering multiple handlers. Incoming requests are not routed between the different handlers
+- rather it is merely the order of the handlers that matters. The first request is handled by the first
+registered handler, the second request by the second handler, etc.
+
+The intent here is to have each handler *verify* that the incoming request is valid. To accomplish, ghttp
+also provides a collection of bite-size handlers that each perform one aspect of request verification. These can
+be composed together and registered with a ghttp server. The result is an expressive language for describing
+the requests generated by the client under test.
+
+Here's a simple example, note that the server handler is only defined in one BeforeEach and then modified, as required, by the nested BeforeEaches.
+A more comprehensive example is available at https://onsi.github.io/gomega/#_testing_http_clients
+
+ var _ = Describe("A Sprockets Client", func() {
+ var server *ghttp.Server
+ var client *SprocketClient
+ BeforeEach(func() {
+ server = ghttp.NewServer()
+ client = NewSprocketClient(server.URL(), "skywalker", "tk427")
+ })
+
+ AfterEach(func() {
+ server.Close()
+ })
+
+ Describe("fetching sprockets", func() {
+ var statusCode int
+ var sprockets []Sprocket
+ BeforeEach(func() {
+ statusCode = http.StatusOK
+ sprockets = []Sprocket{}
+ server.AppendHandlers(ghttp.CombineHandlers(
+ ghttp.VerifyRequest("GET", "/sprockets"),
+ ghttp.VerifyBasicAuth("skywalker", "tk427"),
+ ghttp.RespondWithJSONEncodedPtr(&statusCode, &sprockets),
+ ))
+ })
+
+ Context("when requesting all sprockets", func() {
+ Context("when the response is succesful", func() {
+ BeforeEach(func() {
+ sprockets = []Sprocket{
+ NewSprocket("Alfalfa"),
+ NewSprocket("Banana"),
+ }
+ })
+
+ It("should return the returned sprockets", func() {
+ Ω(client.Sprockets()).Should(Equal(sprockets))
+ })
+ })
+
+ Context("when the response is missing", func() {
+ BeforeEach(func() {
+ statusCode = http.StatusNotFound
+ })
+
+ It("should return an empty list of sprockets", func() {
+ Ω(client.Sprockets()).Should(BeEmpty())
+ })
+ })
+
+ Context("when the response fails to authenticate", func() {
+ BeforeEach(func() {
+ statusCode = http.StatusUnauthorized
+ })
+
+ It("should return an AuthenticationError error", func() {
+ sprockets, err := client.Sprockets()
+ Ω(sprockets).Should(BeEmpty())
+ Ω(err).Should(MatchError(AuthenticationError))
+ })
+ })
+
+ Context("when the response is a server failure", func() {
+ BeforeEach(func() {
+ statusCode = http.StatusInternalServerError
+ })
+
+ It("should return an InternalError error", func() {
+ sprockets, err := client.Sprockets()
+ Ω(sprockets).Should(BeEmpty())
+ Ω(err).Should(MatchError(InternalError))
+ })
+ })
+ })
+
+ Context("when requesting some sprockets", func() {
+ BeforeEach(func() {
+ sprockets = []Sprocket{
+ NewSprocket("Alfalfa"),
+ NewSprocket("Banana"),
+ }
+
+ server.WrapHandler(0, ghttp.VerifyRequest("GET", "/sprockets", "filter=FOOD"))
+ })
+
+ It("should make the request with a filter", func() {
+ Ω(client.Sprockets("food")).Should(Equal(sprockets))
+ })
+ })
+ })
+ })
+*/
+package ghttp
+
+import (
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "net/http/httptest"
+ "reflect"
+ "regexp"
+ "strings"
+ "sync"
+
+ . "github.com/onsi/gomega"
+)
+
+func new() *Server {
+ return &Server{
+ AllowUnhandledRequests: false,
+ UnhandledRequestStatusCode: http.StatusInternalServerError,
+ writeLock: &sync.Mutex{},
+ }
+}
+
+type routedHandler struct {
+ method string
+ pathRegexp *regexp.Regexp
+ path string
+ handler http.HandlerFunc
+}
+
+// NewServer returns a new `*ghttp.Server` that wraps an `httptest` server. The server is started automatically.
+func NewServer() *Server {
+ s := new()
+ s.HTTPTestServer = httptest.NewServer(s)
+ return s
+}
+
+// NewUnstartedServer return a new, unstarted, `*ghttp.Server`. Useful for specifying a custom listener on `server.HTTPTestServer`.
+func NewUnstartedServer() *Server {
+ s := new()
+ s.HTTPTestServer = httptest.NewUnstartedServer(s)
+ return s
+}
+
+// NewTLSServer returns a new `*ghttp.Server` that wraps an `httptest` TLS server. The server is started automatically.
+func NewTLSServer() *Server {
+ s := new()
+ s.HTTPTestServer = httptest.NewTLSServer(s)
+ return s
+}
+
+type Server struct {
+ //The underlying httptest server
+ HTTPTestServer *httptest.Server
+
+ //Defaults to false. If set to true, the Server will allow more requests than there are registered handlers.
+ AllowUnhandledRequests bool
+
+ //The status code returned when receiving an unhandled request.
+ //Defaults to http.StatusInternalServerError.
+ //Only applies if AllowUnhandledRequests is true
+ UnhandledRequestStatusCode int
+
+ //If provided, ghttp will log about each request received to the provided io.Writer
+ //Defaults to nil
+ //If you're using Ginkgo, set this to GinkgoWriter to get improved output during failures
+ Writer io.Writer
+
+ receivedRequests []*http.Request
+ requestHandlers []http.HandlerFunc
+ routedHandlers []routedHandler
+
+ writeLock *sync.Mutex
+ calls int
+}
+
+//Start() starts an unstarted ghttp server. It is a catastrophic error to call Start more than once (thanks, httptest).
+func (s *Server) Start() {
+ s.HTTPTestServer.Start()
+}
+
+//URL() returns a url that will hit the server
+func (s *Server) URL() string {
+ return s.HTTPTestServer.URL
+}
+
+//Addr() returns the address on which the server is listening.
+func (s *Server) Addr() string {
+ return s.HTTPTestServer.Listener.Addr().String()
+}
+
+//Close() should be called at the end of each test. It spins down and cleans up the test server.
+func (s *Server) Close() {
+ s.writeLock.Lock()
+ server := s.HTTPTestServer
+ s.HTTPTestServer = nil
+ s.writeLock.Unlock()
+
+ if server != nil {
+ server.Close()
+ }
+}
+
+//ServeHTTP() makes Server an http.Handler
+//When the server receives a request it handles the request in the following order:
+//
+//1. If the request matches a handler registered with RouteToHandler, that handler is called.
+//2. Otherwise, if there are handlers registered via AppendHandlers, those handlers are called in order.
+//3. If all registered handlers have been called then:
+// a) If AllowUnhandledRequests is true, the request will be handled with response code of UnhandledRequestStatusCode
+// b) If AllowUnhandledRequests is false, the request will not be handled and the current test will be marked as failed.
+func (s *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
+ s.writeLock.Lock()
+ defer func() {
+ e := recover()
+ if e != nil {
+ w.WriteHeader(http.StatusInternalServerError)
+ }
+
+ //If the handler panics GHTTP will silently succeed. This is bad™.
+ //To catch this case we need to fail the test if the handler has panicked.
+ //However, if the handler is panicking because Ginkgo's causing it to panic (i.e. an assertion failed)
+ //then we shouldn't double-report the error as this will confuse people.
+
+ //So: step 1, if this is a Ginkgo panic - do nothing, Ginkgo's aware of the failure
+ eAsString, ok := e.(string)
+ if ok && strings.Contains(eAsString, "defer GinkgoRecover()") {
+ return
+ }
+
+ //If we're here, we have to do step 2: assert that the error is nil. This assertion will
+ //allow us to fail the test suite (note: we can't call Fail since Gomega is not allowed to import Ginkgo).
+ //Since a failed assertion throws a panic, and we are likely in a goroutine, we need to defer within our defer!
+ defer func() {
+ recover()
+ }()
+ Ω(e).Should(BeNil(), "Handler Panicked")
+ }()
+
+ if s.Writer != nil {
+ s.Writer.Write([]byte(fmt.Sprintf("GHTTP Received Request: %s - %s\n", req.Method, req.URL)))
+ }
+
+ s.receivedRequests = append(s.receivedRequests, req)
+ if routedHandler, ok := s.handlerForRoute(req.Method, req.URL.Path); ok {
+ s.writeLock.Unlock()
+ routedHandler(w, req)
+ } else if s.calls < len(s.requestHandlers) {
+ h := s.requestHandlers[s.calls]
+ s.calls++
+ s.writeLock.Unlock()
+ h(w, req)
+ } else {
+ s.writeLock.Unlock()
+ if s.AllowUnhandledRequests {
+ ioutil.ReadAll(req.Body)
+ req.Body.Close()
+ w.WriteHeader(s.UnhandledRequestStatusCode)
+ } else {
+ Ω(req).Should(BeNil(), "Received Unhandled Request")
+ }
+ }
+}
+
+//ReceivedRequests is an array containing all requests received by the server (both handled and unhandled requests)
+func (s *Server) ReceivedRequests() []*http.Request {
+ s.writeLock.Lock()
+ defer s.writeLock.Unlock()
+
+ return s.receivedRequests
+}
+
+//RouteToHandler can be used to register handlers that will always handle requests that match
+//the passed in method and path.
+//
+//The path may be either a string object or a *regexp.Regexp.
+func (s *Server) RouteToHandler(method string, path interface{}, handler http.HandlerFunc) {
+ s.writeLock.Lock()
+ defer s.writeLock.Unlock()
+
+ rh := routedHandler{
+ method: method,
+ handler: handler,
+ }
+
+ switch p := path.(type) {
+ case *regexp.Regexp:
+ rh.pathRegexp = p
+ case string:
+ rh.path = p
+ default:
+ panic("path must be a string or a regular expression")
+ }
+
+ for i, existingRH := range s.routedHandlers {
+ if existingRH.method == method &&
+ reflect.DeepEqual(existingRH.pathRegexp, rh.pathRegexp) &&
+ existingRH.path == rh.path {
+ s.routedHandlers[i] = rh
+ return
+ }
+ }
+ s.routedHandlers = append(s.routedHandlers, rh)
+}
+
+func (s *Server) handlerForRoute(method string, path string) (http.HandlerFunc, bool) {
+ for _, rh := range s.routedHandlers {
+ if rh.method == method {
+ if rh.pathRegexp != nil {
+ if rh.pathRegexp.Match([]byte(path)) {
+ return rh.handler, true
+ }
+ } else if rh.path == path {
+ return rh.handler, true
+ }
+ }
+ }
+
+ return nil, false
+}
+
+//AppendHandlers will appends http.HandlerFuncs to the server's list of registered handlers. The first incoming request is handled by the first handler, the second by the second, etc...
+func (s *Server) AppendHandlers(handlers ...http.HandlerFunc) {
+ s.writeLock.Lock()
+ defer s.writeLock.Unlock()
+
+ s.requestHandlers = append(s.requestHandlers, handlers...)
+}
+
+//SetHandler overrides the registered handler at the passed in index with the passed in handler
+//This is useful, for example, when a server has been set up in a shared context, but must be tweaked
+//for a particular test.
+func (s *Server) SetHandler(index int, handler http.HandlerFunc) {
+ s.writeLock.Lock()
+ defer s.writeLock.Unlock()
+
+ s.requestHandlers[index] = handler
+}
+
+//GetHandler returns the handler registered at the passed in index.
+func (s *Server) GetHandler(index int) http.HandlerFunc {
+ s.writeLock.Lock()
+ defer s.writeLock.Unlock()
+
+ return s.requestHandlers[index]
+}
+
+func (s *Server) Reset() {
+ s.writeLock.Lock()
+ defer s.writeLock.Unlock()
+
+ s.HTTPTestServer.CloseClientConnections()
+ s.calls = 0
+ s.receivedRequests = nil
+ s.requestHandlers = nil
+ s.routedHandlers = nil
+}
+
+//WrapHandler combines the passed in handler with the handler registered at the passed in index.
+//This is useful, for example, when a server has been set up in a shared context but must be tweaked
+//for a particular test.
+//
+//If the currently registered handler is A, and the new passed in handler is B then
+//WrapHandler will generate a new handler that first calls A, then calls B, and assign it to index
+func (s *Server) WrapHandler(index int, handler http.HandlerFunc) {
+ existingHandler := s.GetHandler(index)
+ s.SetHandler(index, CombineHandlers(existingHandler, handler))
+}
+
+func (s *Server) CloseClientConnections() {
+ s.writeLock.Lock()
+ defer s.writeLock.Unlock()
+
+ s.HTTPTestServer.CloseClientConnections()
+}
diff --git a/vendor/github.com/onsi/gomega/ghttp/test_server_suite_test.go b/vendor/github.com/onsi/gomega/ghttp/test_server_suite_test.go
new file mode 100644
index 0000000..7c12360
--- /dev/null
+++ b/vendor/github.com/onsi/gomega/ghttp/test_server_suite_test.go
@@ -0,0 +1,13 @@
+package ghttp_test
+
+import (
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+
+ "testing"
+)
+
+func TestGHTTP(t *testing.T) {
+ RegisterFailHandler(Fail)
+ RunSpecs(t, "GHTTP Suite")
+}
diff --git a/vendor/github.com/onsi/gomega/ghttp/test_server_test.go b/vendor/github.com/onsi/gomega/ghttp/test_server_test.go
new file mode 100644
index 0000000..88b3246
--- /dev/null
+++ b/vendor/github.com/onsi/gomega/ghttp/test_server_test.go
@@ -0,0 +1,1089 @@
+package ghttp_test
+
+import (
+ "bytes"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "net/url"
+ "regexp"
+
+ "github.com/golang/protobuf/proto"
+ "github.com/onsi/gomega/gbytes"
+ "github.com/onsi/gomega/ghttp/protobuf"
+
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+ . "github.com/onsi/gomega/ghttp"
+)
+
+var _ = Describe("TestServer", func() {
+ var (
+ resp *http.Response
+ err error
+ s *Server
+ )
+
+ BeforeEach(func() {
+ s = NewServer()
+ })
+
+ AfterEach(func() {
+ s.Close()
+ })
+
+ Describe("Resetting the server", func() {
+ BeforeEach(func() {
+ s.RouteToHandler("GET", "/", func(w http.ResponseWriter, req *http.Request) {})
+ s.AppendHandlers(func(w http.ResponseWriter, req *http.Request) {})
+ http.Get(s.URL() + "/")
+
+ Ω(s.ReceivedRequests()).Should(HaveLen(1))
+ })
+
+ It("clears all handlers and call counts", func() {
+ s.Reset()
+ Ω(s.ReceivedRequests()).Should(HaveLen(0))
+ Ω(func() { s.GetHandler(0) }).Should(Panic())
+ })
+ })
+
+ Describe("closing client connections", func() {
+ It("closes", func() {
+ s.RouteToHandler("GET", "/",
+ func(w http.ResponseWriter, req *http.Request) {
+ io.WriteString(w, req.RemoteAddr)
+ },
+ )
+ client := http.Client{Transport: &http.Transport{DisableKeepAlives: true}}
+ resp, err := client.Get(s.URL())
+ Ω(err).ShouldNot(HaveOccurred())
+ Ω(resp.StatusCode).Should(Equal(200))
+
+ body, err := ioutil.ReadAll(resp.Body)
+ resp.Body.Close()
+ Ω(err).ShouldNot(HaveOccurred())
+
+ s.CloseClientConnections()
+
+ resp, err = client.Get(s.URL())
+ Ω(err).ShouldNot(HaveOccurred())
+ Ω(resp.StatusCode).Should(Equal(200))
+
+ body2, err := ioutil.ReadAll(resp.Body)
+ resp.Body.Close()
+ Ω(err).ShouldNot(HaveOccurred())
+
+ Ω(body2).ShouldNot(Equal(body))
+ })
+ })
+
+ Describe("closing server mulitple times", func() {
+ It("should not fail", func() {
+ s.Close()
+ Ω(s.Close).ShouldNot(Panic())
+ })
+ })
+
+ Describe("allowing unhandled requests", func() {
+ Context("when true", func() {
+ BeforeEach(func() {
+ s.AllowUnhandledRequests = true
+ s.UnhandledRequestStatusCode = http.StatusForbidden
+ resp, err = http.Get(s.URL() + "/foo")
+ Ω(err).ShouldNot(HaveOccurred())
+ })
+
+ It("should allow unhandled requests and respond with the passed in status code", func() {
+ Ω(err).ShouldNot(HaveOccurred())
+ Ω(resp.StatusCode).Should(Equal(http.StatusForbidden))
+
+ data, err := ioutil.ReadAll(resp.Body)
+ Ω(err).ShouldNot(HaveOccurred())
+ Ω(data).Should(BeEmpty())
+ })
+
+ It("should record the requests", func() {
+ Ω(s.ReceivedRequests()).Should(HaveLen(1))
+ Ω(s.ReceivedRequests()[0].URL.Path).Should(Equal("/foo"))
+ })
+ })
+
+ Context("when false", func() {
+ It("should fail when attempting a request", func() {
+ failures := InterceptGomegaFailures(func() {
+ http.Get(s.URL() + "/foo")
+ })
+
+ Ω(failures[0]).Should(ContainSubstring("Received Unhandled Request"))
+ })
+ })
+ })
+
+ Describe("Managing Handlers", func() {
+ var called []string
+ BeforeEach(func() {
+ called = []string{}
+ s.RouteToHandler("GET", "/routed", func(w http.ResponseWriter, req *http.Request) {
+ called = append(called, "r1")
+ })
+ s.RouteToHandler("POST", regexp.MustCompile(`/routed\d`), func(w http.ResponseWriter, req *http.Request) {
+ called = append(called, "r2")
+ })
+ s.AppendHandlers(func(w http.ResponseWriter, req *http.Request) {
+ called = append(called, "A")
+ }, func(w http.ResponseWriter, req *http.Request) {
+ called = append(called, "B")
+ })
+ })
+
+ It("should prefer routed handlers if there is a match", func() {
+ http.Get(s.URL() + "/routed")
+ http.Post(s.URL()+"/routed7", "application/json", nil)
+ http.Get(s.URL() + "/foo")
+ http.Get(s.URL() + "/routed")
+ http.Post(s.URL()+"/routed9", "application/json", nil)
+ http.Get(s.URL() + "/bar")
+
+ failures := InterceptGomegaFailures(func() {
+ http.Get(s.URL() + "/foo")
+ http.Get(s.URL() + "/routed/not/a/match")
+ http.Get(s.URL() + "/routed7")
+ http.Post(s.URL()+"/routed", "application/json", nil)
+ })
+
+ Ω(failures[0]).Should(ContainSubstring("Received Unhandled Request"))
+ Ω(failures).Should(HaveLen(4))
+
+ http.Post(s.URL()+"/routed3", "application/json", nil)
+
+ Ω(called).Should(Equal([]string{"r1", "r2", "A", "r1", "r2", "B", "r2"}))
+ })
+
+ It("should override routed handlers when reregistered", func() {
+ s.RouteToHandler("GET", "/routed", func(w http.ResponseWriter, req *http.Request) {
+ called = append(called, "r3")
+ })
+ s.RouteToHandler("POST", regexp.MustCompile(`/routed\d`), func(w http.ResponseWriter, req *http.Request) {
+ called = append(called, "r4")
+ })
+
+ http.Get(s.URL() + "/routed")
+ http.Post(s.URL()+"/routed7", "application/json", nil)
+
+ Ω(called).Should(Equal([]string{"r3", "r4"}))
+ })
+
+ It("should call the appended handlers, in order, as requests come in", func() {
+ http.Get(s.URL() + "/foo")
+ Ω(called).Should(Equal([]string{"A"}))
+
+ http.Get(s.URL() + "/foo")
+ Ω(called).Should(Equal([]string{"A", "B"}))
+
+ failures := InterceptGomegaFailures(func() {
+ http.Get(s.URL() + "/foo")
+ })
+
+ Ω(failures[0]).Should(ContainSubstring("Received Unhandled Request"))
+ })
+
+ Describe("Overwriting an existing handler", func() {
+ BeforeEach(func() {
+ s.SetHandler(0, func(w http.ResponseWriter, req *http.Request) {
+ called = append(called, "C")
+ })
+ })
+
+ It("should override the specified handler", func() {
+ http.Get(s.URL() + "/foo")
+ http.Get(s.URL() + "/foo")
+ Ω(called).Should(Equal([]string{"C", "B"}))
+ })
+ })
+
+ Describe("Getting an existing handler", func() {
+ It("should return the handler func", func() {
+ s.GetHandler(1)(nil, nil)
+ Ω(called).Should(Equal([]string{"B"}))
+ })
+ })
+
+ Describe("Wrapping an existing handler", func() {
+ BeforeEach(func() {
+ s.WrapHandler(0, func(w http.ResponseWriter, req *http.Request) {
+ called = append(called, "C")
+ })
+ })
+
+ It("should wrap the existing handler in a new handler", func() {
+ http.Get(s.URL() + "/foo")
+ http.Get(s.URL() + "/foo")
+ Ω(called).Should(Equal([]string{"A", "C", "B"}))
+ })
+ })
+ })
+
+ Describe("When a handler fails", func() {
+ BeforeEach(func() {
+ s.UnhandledRequestStatusCode = http.StatusForbidden //just to be clear that 500s aren't coming from unhandled requests
+ })
+
+ Context("because the handler has panicked", func() {
+ BeforeEach(func() {
+ s.AppendHandlers(func(w http.ResponseWriter, req *http.Request) {
+ panic("bam")
+ })
+ })
+
+ It("should respond with a 500 and make a failing assertion", func() {
+ var resp *http.Response
+ var err error
+
+ failures := InterceptGomegaFailures(func() {
+ resp, err = http.Get(s.URL())
+ })
+
+ Ω(err).ShouldNot(HaveOccurred())
+ Ω(resp.StatusCode).Should(Equal(http.StatusInternalServerError))
+ Ω(failures).Should(ConsistOf(ContainSubstring("Handler Panicked")))
+ })
+ })
+
+ Context("because an assertion has failed", func() {
+ BeforeEach(func() {
+ s.AppendHandlers(func(w http.ResponseWriter, req *http.Request) {
+ // Ω(true).Should(BeFalse()) <-- would be nice to do it this way, but the test just can't be written this way
+
+ By("We're cheating a bit here -- we're throwing a GINKGO_PANIC which simulates a failed assertion")
+ panic(GINKGO_PANIC)
+ })
+ })
+
+ It("should respond with a 500 and *not* make a failing assertion, instead relying on Ginkgo to have already been notified of the error", func() {
+ resp, err := http.Get(s.URL())
+
+ Ω(err).ShouldNot(HaveOccurred())
+ Ω(resp.StatusCode).Should(Equal(http.StatusInternalServerError))
+ })
+ })
+ })
+
+ Describe("Logging to the Writer", func() {
+ var buf *gbytes.Buffer
+ BeforeEach(func() {
+ buf = gbytes.NewBuffer()
+ s.Writer = buf
+ s.AppendHandlers(func(w http.ResponseWriter, req *http.Request) {})
+ s.AppendHandlers(func(w http.ResponseWriter, req *http.Request) {})
+ })
+
+ It("should write to the buffer when a request comes in", func() {
+ http.Get(s.URL() + "/foo")
+ Ω(buf).Should(gbytes.Say("GHTTP Received Request: GET - /foo\n"))
+
+ http.Post(s.URL()+"/bar", "", nil)
+ Ω(buf).Should(gbytes.Say("GHTTP Received Request: POST - /bar\n"))
+ })
+ })
+
+ Describe("Request Handlers", func() {
+ Describe("VerifyRequest", func() {
+ BeforeEach(func() {
+ s.AppendHandlers(VerifyRequest("GET", "/foo"))
+ })
+
+ It("should verify the method, path", func() {
+ resp, err = http.Get(s.URL() + "/foo?baz=bar")
+ Ω(err).ShouldNot(HaveOccurred())
+ })
+
+ It("should verify the method, path", func() {
+ failures := InterceptGomegaFailures(func() {
+ http.Get(s.URL() + "/foo2")
+ })
+ Ω(failures).Should(HaveLen(1))
+ })
+
+ It("should verify the method, path", func() {
+ failures := InterceptGomegaFailures(func() {
+ http.Post(s.URL()+"/foo", "application/json", nil)
+ })
+ Ω(failures).Should(HaveLen(1))
+ })
+
+ Context("when passed a rawQuery", func() {
+ It("should also be possible to verify the rawQuery", func() {
+ s.SetHandler(0, VerifyRequest("GET", "/foo", "baz=bar"))
+ resp, err = http.Get(s.URL() + "/foo?baz=bar")
+ Ω(err).ShouldNot(HaveOccurred())
+ })
+
+ It("should match irregardless of query parameter ordering", func() {
+ s.SetHandler(0, VerifyRequest("GET", "/foo", "type=get&name=money"))
+ u, _ := url.Parse(s.URL() + "/foo")
+ u.RawQuery = url.Values{
+ "type": []string{"get"},
+ "name": []string{"money"},
+ }.Encode()
+
+ resp, err = http.Get(u.String())
+ Ω(err).ShouldNot(HaveOccurred())
+ })
+ })
+
+ Context("when passed a matcher for path", func() {
+ It("should apply the matcher", func() {
+ s.SetHandler(0, VerifyRequest("GET", MatchRegexp(`/foo/[a-f]*/3`)))
+ resp, err = http.Get(s.URL() + "/foo/abcdefa/3")
+ Ω(err).ShouldNot(HaveOccurred())
+ })
+ })
+ })
+
+ Describe("VerifyContentType", func() {
+ BeforeEach(func() {
+ s.AppendHandlers(CombineHandlers(
+ VerifyRequest("GET", "/foo"),
+ VerifyContentType("application/octet-stream"),
+ ))
+ })
+
+ It("should verify the content type", func() {
+ req, err := http.NewRequest("GET", s.URL()+"/foo", nil)
+ Ω(err).ShouldNot(HaveOccurred())
+ req.Header.Set("Content-Type", "application/octet-stream")
+
+ resp, err = http.DefaultClient.Do(req)
+ Ω(err).ShouldNot(HaveOccurred())
+ })
+
+ It("should verify the content type", func() {
+ req, err := http.NewRequest("GET", s.URL()+"/foo", nil)
+ Ω(err).ShouldNot(HaveOccurred())
+ req.Header.Set("Content-Type", "application/json")
+
+ failures := InterceptGomegaFailures(func() {
+ http.DefaultClient.Do(req)
+ })
+ Ω(failures).Should(HaveLen(1))
+ })
+ })
+
+ Describe("Verify BasicAuth", func() {
+ BeforeEach(func() {
+ s.AppendHandlers(CombineHandlers(
+ VerifyRequest("GET", "/foo"),
+ VerifyBasicAuth("bob", "password"),
+ ))
+ })
+
+ It("should verify basic auth", func() {
+ req, err := http.NewRequest("GET", s.URL()+"/foo", nil)
+ Ω(err).ShouldNot(HaveOccurred())
+ req.SetBasicAuth("bob", "password")
+
+ resp, err = http.DefaultClient.Do(req)
+ Ω(err).ShouldNot(HaveOccurred())
+ })
+
+ It("should verify basic auth", func() {
+ req, err := http.NewRequest("GET", s.URL()+"/foo", nil)
+ Ω(err).ShouldNot(HaveOccurred())
+ req.SetBasicAuth("bob", "bassword")
+
+ failures := InterceptGomegaFailures(func() {
+ http.DefaultClient.Do(req)
+ })
+ Ω(failures).Should(HaveLen(1))
+ })
+
+ It("should require basic auth header", func() {
+ req, err := http.NewRequest("GET", s.URL()+"/foo", nil)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ failures := InterceptGomegaFailures(func() {
+ http.DefaultClient.Do(req)
+ })
+ Ω(failures).Should(ContainElement(ContainSubstring("Authorization header must be specified")))
+ })
+ })
+
+ Describe("VerifyHeader", func() {
+ BeforeEach(func() {
+ s.AppendHandlers(CombineHandlers(
+ VerifyRequest("GET", "/foo"),
+ VerifyHeader(http.Header{
+ "accept": []string{"jpeg", "png"},
+ "cache-control": []string{"omicron"},
+ "Return-Path": []string{"hobbiton"},
+ }),
+ ))
+ })
+
+ It("should verify the headers", func() {
+ req, err := http.NewRequest("GET", s.URL()+"/foo", nil)
+ Ω(err).ShouldNot(HaveOccurred())
+ req.Header.Add("Accept", "jpeg")
+ req.Header.Add("Accept", "png")
+ req.Header.Add("Cache-Control", "omicron")
+ req.Header.Add("return-path", "hobbiton")
+
+ resp, err = http.DefaultClient.Do(req)
+ Ω(err).ShouldNot(HaveOccurred())
+ })
+
+ It("should verify the headers", func() {
+ req, err := http.NewRequest("GET", s.URL()+"/foo", nil)
+ Ω(err).ShouldNot(HaveOccurred())
+ req.Header.Add("Schmaccept", "jpeg")
+ req.Header.Add("Schmaccept", "png")
+ req.Header.Add("Cache-Control", "omicron")
+ req.Header.Add("return-path", "hobbiton")
+
+ failures := InterceptGomegaFailures(func() {
+ http.DefaultClient.Do(req)
+ })
+ Ω(failures).Should(HaveLen(1))
+ })
+ })
+
+ Describe("VerifyHeaderKV", func() {
+ BeforeEach(func() {
+ s.AppendHandlers(CombineHandlers(
+ VerifyRequest("GET", "/foo"),
+ VerifyHeaderKV("accept", "jpeg", "png"),
+ VerifyHeaderKV("cache-control", "omicron"),
+ VerifyHeaderKV("Return-Path", "hobbiton"),
+ ))
+ })
+
+ It("should verify the headers", func() {
+ req, err := http.NewRequest("GET", s.URL()+"/foo", nil)
+ Ω(err).ShouldNot(HaveOccurred())
+ req.Header.Add("Accept", "jpeg")
+ req.Header.Add("Accept", "png")
+ req.Header.Add("Cache-Control", "omicron")
+ req.Header.Add("return-path", "hobbiton")
+
+ resp, err = http.DefaultClient.Do(req)
+ Ω(err).ShouldNot(HaveOccurred())
+ })
+
+ It("should verify the headers", func() {
+ req, err := http.NewRequest("GET", s.URL()+"/foo", nil)
+ Ω(err).ShouldNot(HaveOccurred())
+ req.Header.Add("Accept", "jpeg")
+ req.Header.Add("Cache-Control", "omicron")
+ req.Header.Add("return-path", "hobbiton")
+
+ failures := InterceptGomegaFailures(func() {
+ http.DefaultClient.Do(req)
+ })
+ Ω(failures).Should(HaveLen(1))
+ })
+ })
+
+ Describe("VerifyBody", func() {
+ BeforeEach(func() {
+ s.AppendHandlers(CombineHandlers(
+ VerifyRequest("POST", "/foo"),
+ VerifyBody([]byte("some body")),
+ ))
+ })
+
+ It("should verify the body", func() {
+ resp, err = http.Post(s.URL()+"/foo", "", bytes.NewReader([]byte("some body")))
+ Ω(err).ShouldNot(HaveOccurred())
+ })
+
+ It("should verify the body", func() {
+ failures := InterceptGomegaFailures(func() {
+ http.Post(s.URL()+"/foo", "", bytes.NewReader([]byte("wrong body")))
+ })
+ Ω(failures).Should(HaveLen(1))
+ })
+ })
+
+ Describe("VerifyJSON", func() {
+ BeforeEach(func() {
+ s.AppendHandlers(CombineHandlers(
+ VerifyRequest("POST", "/foo"),
+ VerifyJSON(`{"a":3, "b":2}`),
+ ))
+ })
+
+ It("should verify the json body and the content type", func() {
+ resp, err = http.Post(s.URL()+"/foo", "application/json", bytes.NewReader([]byte(`{"b":2, "a":3}`)))
+ Ω(err).ShouldNot(HaveOccurred())
+ })
+
+ It("should verify the json body and the content type", func() {
+ failures := InterceptGomegaFailures(func() {
+ http.Post(s.URL()+"/foo", "application/json", bytes.NewReader([]byte(`{"b":2, "a":4}`)))
+ })
+ Ω(failures).Should(HaveLen(1))
+ })
+
+ It("should verify the json body and the content type", func() {
+ failures := InterceptGomegaFailures(func() {
+ http.Post(s.URL()+"/foo", "application/not-json", bytes.NewReader([]byte(`{"b":2, "a":3}`)))
+ })
+ Ω(failures).Should(HaveLen(1))
+ })
+ })
+
+ Describe("VerifyJSONRepresenting", func() {
+ BeforeEach(func() {
+ s.AppendHandlers(CombineHandlers(
+ VerifyRequest("POST", "/foo"),
+ VerifyJSONRepresenting([]int{1, 3, 5}),
+ ))
+ })
+
+ It("should verify the json body and the content type", func() {
+ resp, err = http.Post(s.URL()+"/foo", "application/json", bytes.NewReader([]byte(`[1,3,5]`)))
+ Ω(err).ShouldNot(HaveOccurred())
+ })
+
+ It("should verify the json body and the content type", func() {
+ failures := InterceptGomegaFailures(func() {
+ http.Post(s.URL()+"/foo", "application/json", bytes.NewReader([]byte(`[1,3]`)))
+ })
+ Ω(failures).Should(HaveLen(1))
+ })
+ })
+
+ Describe("VerifyForm", func() {
+ var formValues url.Values
+
+ BeforeEach(func() {
+ formValues = make(url.Values)
+ formValues.Add("users", "user1")
+ formValues.Add("users", "user2")
+ formValues.Add("group", "users")
+ })
+
+ Context("when encoded in the URL", func() {
+ BeforeEach(func() {
+ s.AppendHandlers(CombineHandlers(
+ VerifyRequest("GET", "/foo"),
+ VerifyForm(url.Values{
+ "users": []string{"user1", "user2"},
+ "group": []string{"users"},
+ }),
+ ))
+ })
+
+ It("should verify form values", func() {
+ resp, err = http.Get(s.URL() + "/foo?" + formValues.Encode())
+ Ω(err).ShouldNot(HaveOccurred())
+ })
+
+ It("should ignore extra values", func() {
+ formValues.Add("extra", "value")
+ resp, err = http.Get(s.URL() + "/foo?" + formValues.Encode())
+ Ω(err).ShouldNot(HaveOccurred())
+ })
+
+ It("fail on missing values", func() {
+ formValues.Del("group")
+ failures := InterceptGomegaFailures(func() {
+ resp, err = http.Get(s.URL() + "/foo?" + formValues.Encode())
+ })
+ Ω(failures).Should(HaveLen(1))
+ })
+
+ It("fail on incorrect values", func() {
+ formValues.Set("group", "wheel")
+ failures := InterceptGomegaFailures(func() {
+ resp, err = http.Get(s.URL() + "/foo?" + formValues.Encode())
+ })
+ Ω(failures).Should(HaveLen(1))
+ })
+ })
+
+ Context("when present in the body", func() {
+ BeforeEach(func() {
+ s.AppendHandlers(CombineHandlers(
+ VerifyRequest("POST", "/foo"),
+ VerifyForm(url.Values{
+ "users": []string{"user1", "user2"},
+ "group": []string{"users"},
+ }),
+ ))
+ })
+
+ It("should verify form values", func() {
+ resp, err = http.PostForm(s.URL()+"/foo", formValues)
+ Ω(err).ShouldNot(HaveOccurred())
+ })
+
+ It("should ignore extra values", func() {
+ formValues.Add("extra", "value")
+ resp, err = http.PostForm(s.URL()+"/foo", formValues)
+ Ω(err).ShouldNot(HaveOccurred())
+ })
+
+ It("fail on missing values", func() {
+ formValues.Del("group")
+ failures := InterceptGomegaFailures(func() {
+ resp, err = http.PostForm(s.URL()+"/foo", formValues)
+ })
+ Ω(failures).Should(HaveLen(1))
+ })
+
+ It("fail on incorrect values", func() {
+ formValues.Set("group", "wheel")
+ failures := InterceptGomegaFailures(func() {
+ resp, err = http.PostForm(s.URL()+"/foo", formValues)
+ })
+ Ω(failures).Should(HaveLen(1))
+ })
+ })
+ })
+
+ Describe("VerifyFormKV", func() {
+ Context("when encoded in the URL", func() {
+ BeforeEach(func() {
+ s.AppendHandlers(CombineHandlers(
+ VerifyRequest("GET", "/foo"),
+ VerifyFormKV("users", "user1", "user2"),
+ ))
+ })
+
+ It("verifies the form value", func() {
+ resp, err = http.Get(s.URL() + "/foo?users=user1&users=user2")
+ Ω(err).ShouldNot(HaveOccurred())
+ })
+
+ It("verifies the form value", func() {
+ failures := InterceptGomegaFailures(func() {
+ resp, err = http.Get(s.URL() + "/foo?users=user1")
+ })
+ Ω(failures).Should(HaveLen(1))
+ })
+ })
+
+ Context("when present in the body", func() {
+ BeforeEach(func() {
+ s.AppendHandlers(CombineHandlers(
+ VerifyRequest("POST", "/foo"),
+ VerifyFormKV("users", "user1", "user2"),
+ ))
+ })
+
+ It("verifies the form value", func() {
+ resp, err = http.PostForm(s.URL()+"/foo", url.Values{"users": []string{"user1", "user2"}})
+ Ω(err).ShouldNot(HaveOccurred())
+ })
+
+ It("verifies the form value", func() {
+ failures := InterceptGomegaFailures(func() {
+ resp, err = http.PostForm(s.URL()+"/foo", url.Values{"users": []string{"user1"}})
+ })
+ Ω(failures).Should(HaveLen(1))
+ })
+ })
+ })
+
+ Describe("VerifyProtoRepresenting", func() {
+ var message *protobuf.SimpleMessage
+
+ BeforeEach(func() {
+ message = new(protobuf.SimpleMessage)
+ message.Description = proto.String("A description")
+ message.Id = proto.Int32(0)
+
+ s.AppendHandlers(CombineHandlers(
+ VerifyRequest("POST", "/proto"),
+ VerifyProtoRepresenting(message),
+ ))
+ })
+
+ It("verifies the proto body and the content type", func() {
+ serialized, err := proto.Marshal(message)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ resp, err = http.Post(s.URL()+"/proto", "application/x-protobuf", bytes.NewReader(serialized))
+ Ω(err).ShouldNot(HaveOccurred())
+ })
+
+ It("should verify the proto body and the content type", func() {
+ serialized, err := proto.Marshal(&protobuf.SimpleMessage{
+ Description: proto.String("A description"),
+ Id: proto.Int32(0),
+ Metadata: proto.String("some metadata"),
+ })
+ Ω(err).ShouldNot(HaveOccurred())
+
+ failures := InterceptGomegaFailures(func() {
+ http.Post(s.URL()+"/proto", "application/x-protobuf", bytes.NewReader(serialized))
+ })
+ Ω(failures).Should(HaveLen(1))
+ })
+
+ It("should verify the proto body and the content type", func() {
+ serialized, err := proto.Marshal(message)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ failures := InterceptGomegaFailures(func() {
+ http.Post(s.URL()+"/proto", "application/not-x-protobuf", bytes.NewReader(serialized))
+ })
+ Ω(failures).Should(HaveLen(1))
+ })
+ })
+
+ Describe("RespondWith", func() {
+ Context("without headers", func() {
+ BeforeEach(func() {
+ s.AppendHandlers(CombineHandlers(
+ VerifyRequest("POST", "/foo"),
+ RespondWith(http.StatusCreated, "sweet"),
+ ), CombineHandlers(
+ VerifyRequest("POST", "/foo"),
+ RespondWith(http.StatusOK, []byte("sour")),
+ ))
+ })
+
+ It("should return the response", func() {
+ resp, err = http.Post(s.URL()+"/foo", "application/json", nil)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ Ω(resp.StatusCode).Should(Equal(http.StatusCreated))
+
+ body, err := ioutil.ReadAll(resp.Body)
+ Ω(err).ShouldNot(HaveOccurred())
+ Ω(body).Should(Equal([]byte("sweet")))
+
+ resp, err = http.Post(s.URL()+"/foo", "application/json", nil)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ Ω(resp.StatusCode).Should(Equal(http.StatusOK))
+
+ body, err = ioutil.ReadAll(resp.Body)
+ Ω(err).ShouldNot(HaveOccurred())
+ Ω(body).Should(Equal([]byte("sour")))
+ })
+ })
+
+ Context("with headers", func() {
+ BeforeEach(func() {
+ s.AppendHandlers(CombineHandlers(
+ VerifyRequest("POST", "/foo"),
+ RespondWith(http.StatusCreated, "sweet", http.Header{"X-Custom-Header": []string{"my header"}}),
+ ))
+ })
+
+ It("should return the headers too", func() {
+ resp, err = http.Post(s.URL()+"/foo", "application/json", nil)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ Ω(resp.StatusCode).Should(Equal(http.StatusCreated))
+ Ω(ioutil.ReadAll(resp.Body)).Should(Equal([]byte("sweet")))
+ Ω(resp.Header.Get("X-Custom-Header")).Should(Equal("my header"))
+ })
+ })
+ })
+
+ Describe("RespondWithPtr", func() {
+ var code int
+ var byteBody []byte
+ var stringBody string
+ BeforeEach(func() {
+ code = http.StatusOK
+ byteBody = []byte("sweet")
+ stringBody = "sour"
+
+ s.AppendHandlers(CombineHandlers(
+ VerifyRequest("POST", "/foo"),
+ RespondWithPtr(&code, &byteBody),
+ ), CombineHandlers(
+ VerifyRequest("POST", "/foo"),
+ RespondWithPtr(&code, &stringBody),
+ ))
+ })
+
+ It("should return the response", func() {
+ code = http.StatusCreated
+ byteBody = []byte("tasty")
+ stringBody = "treat"
+
+ resp, err = http.Post(s.URL()+"/foo", "application/json", nil)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ Ω(resp.StatusCode).Should(Equal(http.StatusCreated))
+
+ body, err := ioutil.ReadAll(resp.Body)
+ Ω(err).ShouldNot(HaveOccurred())
+ Ω(body).Should(Equal([]byte("tasty")))
+
+ resp, err = http.Post(s.URL()+"/foo", "application/json", nil)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ Ω(resp.StatusCode).Should(Equal(http.StatusCreated))
+
+ body, err = ioutil.ReadAll(resp.Body)
+ Ω(err).ShouldNot(HaveOccurred())
+ Ω(body).Should(Equal([]byte("treat")))
+ })
+
+ Context("when passed a nil body", func() {
+ BeforeEach(func() {
+ s.SetHandler(0, CombineHandlers(
+ VerifyRequest("POST", "/foo"),
+ RespondWithPtr(&code, nil),
+ ))
+ })
+
+ It("should return an empty body and not explode", func() {
+ resp, err = http.Post(s.URL()+"/foo", "application/json", nil)
+
+ Ω(err).ShouldNot(HaveOccurred())
+ Ω(resp.StatusCode).Should(Equal(http.StatusOK))
+ body, err := ioutil.ReadAll(resp.Body)
+ Ω(err).ShouldNot(HaveOccurred())
+ Ω(body).Should(BeEmpty())
+
+ Ω(s.ReceivedRequests()).Should(HaveLen(1))
+ })
+ })
+ })
+
+ Describe("RespondWithJSON", func() {
+ Context("when no optional headers are set", func() {
+ BeforeEach(func() {
+ s.AppendHandlers(CombineHandlers(
+ VerifyRequest("POST", "/foo"),
+ RespondWithJSONEncoded(http.StatusCreated, []int{1, 2, 3}),
+ ))
+ })
+
+ It("should return the response", func() {
+ resp, err = http.Post(s.URL()+"/foo", "application/json", nil)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ Ω(resp.StatusCode).Should(Equal(http.StatusCreated))
+
+ body, err := ioutil.ReadAll(resp.Body)
+ Ω(err).ShouldNot(HaveOccurred())
+ Ω(body).Should(MatchJSON("[1,2,3]"))
+ })
+
+ It("should set the Content-Type header to application/json", func() {
+ resp, err = http.Post(s.URL()+"/foo", "application/json", nil)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ Ω(resp.Header["Content-Type"]).Should(Equal([]string{"application/json"}))
+ })
+ })
+
+ Context("when optional headers are set", func() {
+ var headers http.Header
+ BeforeEach(func() {
+ headers = http.Header{"Stuff": []string{"things"}}
+ })
+
+ JustBeforeEach(func() {
+ s.AppendHandlers(CombineHandlers(
+ VerifyRequest("POST", "/foo"),
+ RespondWithJSONEncoded(http.StatusCreated, []int{1, 2, 3}, headers),
+ ))
+ })
+
+ It("should preserve those headers", func() {
+ resp, err = http.Post(s.URL()+"/foo", "application/json", nil)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ Ω(resp.Header["Stuff"]).Should(Equal([]string{"things"}))
+ })
+
+ It("should set the Content-Type header to application/json", func() {
+ resp, err = http.Post(s.URL()+"/foo", "application/json", nil)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ Ω(resp.Header["Content-Type"]).Should(Equal([]string{"application/json"}))
+ })
+
+ Context("when setting the Content-Type explicitly", func() {
+ BeforeEach(func() {
+ headers["Content-Type"] = []string{"not-json"}
+ })
+
+ It("should use the Content-Type header that was explicitly set", func() {
+ resp, err = http.Post(s.URL()+"/foo", "application/json", nil)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ Ω(resp.Header["Content-Type"]).Should(Equal([]string{"not-json"}))
+ })
+ })
+ })
+ })
+
+ Describe("RespondWithJSONPtr", func() {
+ type testObject struct {
+ Key string
+ Value string
+ }
+
+ var code int
+ var object testObject
+
+ Context("when no optional headers are set", func() {
+ BeforeEach(func() {
+ code = http.StatusOK
+ object = testObject{}
+ s.AppendHandlers(CombineHandlers(
+ VerifyRequest("POST", "/foo"),
+ RespondWithJSONEncodedPtr(&code, &object),
+ ))
+ })
+
+ It("should return the response", func() {
+ code = http.StatusCreated
+ object = testObject{
+ Key: "Jim",
+ Value: "Codes",
+ }
+ resp, err = http.Post(s.URL()+"/foo", "application/json", nil)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ Ω(resp.StatusCode).Should(Equal(http.StatusCreated))
+
+ body, err := ioutil.ReadAll(resp.Body)
+ Ω(err).ShouldNot(HaveOccurred())
+ Ω(body).Should(MatchJSON(`{"Key": "Jim", "Value": "Codes"}`))
+ })
+
+ It("should set the Content-Type header to application/json", func() {
+ resp, err = http.Post(s.URL()+"/foo", "application/json", nil)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ Ω(resp.Header["Content-Type"]).Should(Equal([]string{"application/json"}))
+ })
+ })
+
+ Context("when optional headers are set", func() {
+ var headers http.Header
+ BeforeEach(func() {
+ headers = http.Header{"Stuff": []string{"things"}}
+ })
+
+ JustBeforeEach(func() {
+ code = http.StatusOK
+ object = testObject{}
+ s.AppendHandlers(CombineHandlers(
+ VerifyRequest("POST", "/foo"),
+ RespondWithJSONEncodedPtr(&code, &object, headers),
+ ))
+ })
+
+ It("should preserve those headers", func() {
+ resp, err = http.Post(s.URL()+"/foo", "application/json", nil)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ Ω(resp.Header["Stuff"]).Should(Equal([]string{"things"}))
+ })
+
+ It("should set the Content-Type header to application/json", func() {
+ resp, err = http.Post(s.URL()+"/foo", "application/json", nil)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ Ω(resp.Header["Content-Type"]).Should(Equal([]string{"application/json"}))
+ })
+
+ Context("when setting the Content-Type explicitly", func() {
+ BeforeEach(func() {
+ headers["Content-Type"] = []string{"not-json"}
+ })
+
+ It("should use the Content-Type header that was explicitly set", func() {
+ resp, err = http.Post(s.URL()+"/foo", "application/json", nil)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ Ω(resp.Header["Content-Type"]).Should(Equal([]string{"not-json"}))
+ })
+ })
+ })
+ })
+
+ Describe("RespondWithProto", func() {
+ var message *protobuf.SimpleMessage
+
+ BeforeEach(func() {
+ message = new(protobuf.SimpleMessage)
+ message.Description = proto.String("A description")
+ message.Id = proto.Int32(99)
+ })
+
+ Context("when no optional headers are set", func() {
+ BeforeEach(func() {
+ s.AppendHandlers(CombineHandlers(
+ VerifyRequest("POST", "/proto"),
+ RespondWithProto(http.StatusCreated, message),
+ ))
+ })
+
+ It("should return the response", func() {
+ resp, err = http.Post(s.URL()+"/proto", "application/x-protobuf", nil)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ Ω(resp.StatusCode).Should(Equal(http.StatusCreated))
+
+ var received protobuf.SimpleMessage
+ body, err := ioutil.ReadAll(resp.Body)
+ err = proto.Unmarshal(body, &received)
+ Ω(err).ShouldNot(HaveOccurred())
+ })
+
+ It("should set the Content-Type header to application/x-protobuf", func() {
+ resp, err = http.Post(s.URL()+"/proto", "application/x-protobuf", nil)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ Ω(resp.Header["Content-Type"]).Should(Equal([]string{"application/x-protobuf"}))
+ })
+ })
+
+ Context("when optional headers are set", func() {
+ var headers http.Header
+ BeforeEach(func() {
+ headers = http.Header{"Stuff": []string{"things"}}
+ })
+
+ JustBeforeEach(func() {
+ s.AppendHandlers(CombineHandlers(
+ VerifyRequest("POST", "/proto"),
+ RespondWithProto(http.StatusCreated, message, headers),
+ ))
+ })
+
+ It("should preserve those headers", func() {
+ resp, err = http.Post(s.URL()+"/proto", "application/x-protobuf", nil)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ Ω(resp.Header["Stuff"]).Should(Equal([]string{"things"}))
+ })
+
+ It("should set the Content-Type header to application/x-protobuf", func() {
+ resp, err = http.Post(s.URL()+"/proto", "application/x-protobuf", nil)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ Ω(resp.Header["Content-Type"]).Should(Equal([]string{"application/x-protobuf"}))
+ })
+
+ Context("when setting the Content-Type explicitly", func() {
+ BeforeEach(func() {
+ headers["Content-Type"] = []string{"not-x-protobuf"}
+ })
+
+ It("should use the Content-Type header that was explicitly set", func() {
+ resp, err = http.Post(s.URL()+"/proto", "application/x-protobuf", nil)
+ Ω(err).ShouldNot(HaveOccurred())
+
+ Ω(resp.Header["Content-Type"]).Should(Equal([]string{"not-x-protobuf"}))
+ })
+ })
+ })
+ })
+ })
+})