Skip to content

Commit

Permalink
feat: custom handler config (#14)
Browse files Browse the repository at this point in the history
* refactor: configure TLS from config

* feat: custom handler config
  • Loading branch information
shawalli authored Aug 26, 2024
1 parent 0e39e1d commit 4c9d97f
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 20 deletions.
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -233,8 +233,10 @@ Mock.On(http.MethodGet, "/some/path", nil).RespondOK([]byte(`{"id": "1234"}`)).H

`httpmock.Server` is a glorified version of `httptest.Server` with a default handler. With both server types, the
server runs as a goroutine. The default behavior is to log the panic details and recover from it. However, an
implementation can set `NotRecoverable()` to indicate to the handler that an unmatched request should cause the server
to panic outside of the server goroutine and into the main process.
implementation can set `NotRecoverable()` to indicate to the default or custom handler that an unmatched request
should cause the server to panic outside of the server goroutine and into the main process.

If writing a custom handler, the handler should react to a panic based on the server's `IsRecoverable()` response.

## Installation

Expand Down
29 changes: 25 additions & 4 deletions server.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@ type Server struct {
ignorePanic bool
}

// ServerConfig contains settings for configuring a [Server]. It is used with
// [NewServerWithConfig]. For default behavior, use [NewServer].
type ServerConfig struct {
// Create TLS-configured server
TLS bool

// Custom server handler
Handler http.HandlerFunc
}

// makeHandler creates a standard [http.HandlerFunc] that may be used by a
// regular or TLS [Server] to log requests and write configured responses.
func makeHandler(s *Server) http.HandlerFunc {
Expand Down Expand Up @@ -54,10 +64,19 @@ func NewServer() *Server {
return s
}

// NewServer creates a new [Server], configured for TLS, and associated [Mock].
func NewTLSServer() *Server {
func NewServerWithConfig(cfg ServerConfig) *Server {
s := &Server{Mock: new(Mock)}
s.Server = httptest.NewTLSServer(http.HandlerFunc(makeHandler(s)))

handler := cfg.Handler
if handler == nil {
handler = http.HandlerFunc(makeHandler(s))
}

if cfg.TLS {
s.Server = httptest.NewTLSServer(handler)
} else {
s.Server = httptest.NewServer(handler)
}

return s
}
Expand All @@ -67,7 +86,9 @@ func NewTLSServer() *Server {
// and printed to stdout, with a final 404 returned to the client.
//
// 404 was chosen rather than 500 due to panics almost always occurring when a
// matching [Request] cannot be found.
// matching [Request] cannot be found. However, custom handlers can choose to
// implement their recovery mechanism however they would like, using the
// [Server.IsRecoverable] method to access this value.
func (s *Server) NotRecoverable() *Server {
s.ignorePanic = true
return s
Expand Down
60 changes: 46 additions & 14 deletions server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,28 +17,60 @@ func Test_NewServer(t *testing.T) {

// Assertions
assert.NotNil(t, s.Mock)
assert.NotNil(t, s.Server)
if s.Server != nil {
// Equivalent to s != TLSServer
assert.Nil(t, s.Server.TLS)
// Equivalent to the server having been Start()'ed already
assert.NotEmpty(t, s.Server.URL)
if s.Server == nil {
t.Fatalf("unexpected nil Server")
}
// Equivalent to s != TLSServer
assert.Nil(t, s.Server.TLS)
// Equivalent to the server having been Start()'ed already
assert.NotEmpty(t, s.Server.URL)
}

func Test_NewTLSServer(t *testing.T) {
func Test_NewServerWithConfig_TLS(t *testing.T) {
// Setup
cfg := ServerConfig{TLS: true}

// Test
s := NewServerWithConfig(cfg)

// Assertions
assert.NotNil(t, s.Mock)
if s.Server == nil {
t.Fatalf("unexpected nil Server")
}
// Equivalent to s == TLSServer
assert.NotNil(t, s.Server.TLS)
// Equivalent to the server having been Start()'ed already
assert.NotEmpty(t, s.Server.URL)
}

func Test_NewServerWithConfig_CustomHandler(t *testing.T) {
// Setup
handler := func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusForbidden)
}
cfg := ServerConfig{Handler: handler}

// Test
s := NewTLSServer()
s := NewServerWithConfig(cfg)
s.On(AnyMethod, "/", nil).RespondOK([]byte(``))

// Assertions
assert.NotNil(t, s.Mock)
assert.NotNil(t, s.Server)
if s.Server != nil {
// Equivalent to s == TLSServer
assert.NotNil(t, s.Server.TLS)
// Equivalent to the server having been Start()'ed already
assert.NotEmpty(t, s.Server.URL)
if s.Server == nil {
t.Fatalf("unexpected nil Server")
}
// Equivalent to s != TLSServer
assert.Nil(t, s.Server.TLS)
// Equivalent to the server having been Start()'ed already
assert.NotEmpty(t, s.Server.URL)

resp, err := s.Client().Get(s.URL)
if err != nil {
t.Fatalf("unexpecrted failure when reading response: %v", err)
}

assert.Equal(t, http.StatusForbidden, resp.StatusCode)
}

func TestServer_NotRecoverable(t *testing.T) {
Expand Down

0 comments on commit 4c9d97f

Please sign in to comment.