Skip to content

Commit

Permalink
Added mocks content from file (#7)
Browse files Browse the repository at this point in the history
* Added file mock

* Added correct errors description

* Refactored mock middleware

* Added test for content type detection

* Added misspeling

* Added tests for mock content
  • Loading branch information
Evgeny Abramovich authored Nov 20, 2022
1 parent 1670ebd commit 3596139
Show file tree
Hide file tree
Showing 11 changed files with 217 additions and 73 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ require (
github.com/gorilla/mux v1.8.0
github.com/pseidemann/finish v1.2.0
github.com/pterm/pterm v0.12.49
github.com/spf13/afero v1.9.3
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.14.0
github.com/stretchr/testify v1.8.1
Expand All @@ -32,7 +33,6 @@ require (
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rivo/uniseg v0.4.3 // indirect
github.com/spf13/afero v1.9.3 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/subosito/gotenv v1.4.1 // indirect
Expand Down
19 changes: 11 additions & 8 deletions internal/middlewares/mock/handler.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
package mock

import (
"fmt"
"net/http"

"github.com/evg4b/uncors/internal/contracts"
"github.com/evg4b/uncors/internal/infrastructure"
"github.com/go-http-utils/headers"
"github.com/spf13/afero"
)

type internalHandler struct {
response Response
logger contracts.Logger
fs afero.Fs
}

func (handler *internalHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
Expand All @@ -21,14 +21,17 @@ func (handler *internalHandler) ServeHTTP(writer http.ResponseWriter, request *h
for key, value := range response.Headers {
header.Set(key, value)
}
if len(header.Get(headers.ContentType)) == 0 {
contentType := http.DetectContentType([]byte(response.RawContent))
header.Set(headers.ContentType, contentType)

var err error
if len(handler.response.File) > 0 {
err = handler.serveFileContent(writer, request)
} else {
err = handler.serveRawContent(writer)
}

writer.WriteHeader(normaliseCode(response.Code))
if _, err := fmt.Fprint(writer, response.RawContent); err != nil {
return // TODO: add error handler
if err != nil {
// TODO: add error handling
return
}

handler.logger.PrintResponse(&http.Response{
Expand Down
154 changes: 108 additions & 46 deletions internal/middlewares/mock/handler_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,54 +11,122 @@ import (
"github.com/stretchr/testify/assert"
)

const textPlain = "text/plain; charset=utf-8"
const (
textPlain = "text/plain; charset=utf-8"
imagePng = "image/png"
)

const testContent = "test content"
const (
textContent = "status: ok"
jsonContent = `{ "test": "ok" }`
htmlContent = "<html></html>"
pngContent = "\x89PNG\x0D\x0A\x1A\x0A"
)

const (
textFile = "test.txt"
jsonFile = "test.json"
htmlFile = "test.html"
pngFile = "test.png"
)

func TestHandler(t *testing.T) {
logger := mocks.NewNoopLogger(t)
fileSystem := testutils.FsFromMap(t, map[string]string{
textFile: textContent,
jsonFile: jsonContent,
htmlFile: htmlContent,
pngFile: pngContent,
})

var makeHandler = func(t *testing.T, response Response) *internalHandler {
return &internalHandler{
logger: mocks.NewNoopLogger(t),
response: response,
fs: fileSystem,
}
}

t.Run("mock content", func(t *testing.T) {
tests := []struct {
name string
response Response
expected string
}{
{
name: "raw content",
response: Response{RawContent: jsonContent},
expected: jsonContent,
},
{
name: "file content",
response: Response{File: jsonFile},
expected: jsonContent,
},
}
for _, testCase := range tests {
t.Run(testCase.name, func(t *testing.T) {
handler := makeHandler(t, testCase.response)

recorder := httptest.NewRecorder()
request := httptest.NewRequest(http.MethodGet, "/", nil)
handler.ServeHTTP(recorder, request)

t.Run("content type setting", func(t *testing.T) {
body := testutils.ReadBody(t, recorder)
assert.EqualValues(t, testCase.expected, body)
})
}
})

t.Run("content type detection", func(t *testing.T) {
tests := []struct {
name string
body string
response Response
expected string
}{
{
name: "plain text",
body: `status: ok`,
name: "raw content with plain text",
response: Response{RawContent: textContent},
expected: textPlain,
},
{
name: "json",
body: `{ "status": "ok" }`,
name: "raw content with json",
response: Response{RawContent: jsonContent},
expected: textPlain,
},
{
name: "html",
body: `<html></html>`,
name: "raw content with html",
response: Response{RawContent: htmlContent},
expected: "text/html; charset=utf-8",
},
{
name: "xml",
body: `<?xml />`,
expected: "text/xml; charset=utf-8",
name: "raw content with png",
response: Response{RawContent: pngContent},
expected: imagePng,
},
{
name: "png",
body: "\x89PNG\x0D\x0A\x1A\x0A",
expected: "image/png",
name: "file with plain text",
response: Response{File: textFile},
expected: textPlain,
},
{
name: "file with json",
response: Response{File: jsonFile},
expected: "application/json",
},
{
name: "file with html",
response: Response{File: htmlFile},
expected: "text/html; charset=utf-8",
},
{
name: "file with png",
response: Response{File: pngFile},
expected: imagePng,
},
}
for _, testCase := range tests {
t.Run(testCase.name, func(t *testing.T) {
handler := internalHandler{
logger: logger,
response: Response{
Code: 200,
RawContent: testCase.body,
},
}
handler := makeHandler(t, testCase.response)

recorder := httptest.NewRecorder()
request := httptest.NewRequest(http.MethodGet, "/", nil)
Expand All @@ -79,8 +147,8 @@ func TestHandler(t *testing.T) {
{
name: "should put default CORS headers",
response: Response{
Code: 200,
RawContent: testContent,
Code: http.StatusOK,
RawContent: textContent,
},
expected: map[string][]string{
headers.AccessControlAllowOrigin: {"*"},
Expand All @@ -92,8 +160,8 @@ func TestHandler(t *testing.T) {
{
name: "should set response code",
response: Response{
Code: 200,
RawContent: testContent,
Code: http.StatusOK,
RawContent: textContent,
},
expected: map[string][]string{
headers.AccessControlAllowOrigin: {"*"},
Expand All @@ -105,11 +173,11 @@ func TestHandler(t *testing.T) {
{
name: "should set custom headers",
response: Response{
Code: 200,
Code: http.StatusOK,
Headers: map[string]string{
"X-Key": "X-Key-Value",
},
RawContent: testContent,
RawContent: textContent,
},
expected: map[string][]string{
headers.AccessControlAllowOrigin: {"*"},
Expand All @@ -122,13 +190,13 @@ func TestHandler(t *testing.T) {
{
name: "should override default headers",
response: Response{
Code: 200,
Code: http.StatusOK,
Headers: map[string]string{
headers.AccessControlAllowOrigin: "localhost",
headers.AccessControlAllowCredentials: "false",
headers.ContentType: "none",
},
RawContent: testContent,
RawContent: textContent,
},
expected: map[string][]string{
headers.AccessControlAllowOrigin: {"localhost"},
Expand All @@ -140,18 +208,15 @@ func TestHandler(t *testing.T) {
}
for _, testCase := range tests {
t.Run(testCase.name, func(t *testing.T) {
handler := internalHandler{
logger: logger,
response: testCase.response,
}
handler := makeHandler(t, testCase.response)

request := httptest.NewRequest(http.MethodGet, "/", nil)
recorder := httptest.NewRecorder()

handler.ServeHTTP(recorder, request)

assert.EqualValues(t, testCase.expected, testutils.ReadHeader(t, recorder))
assert.Equal(t, 200, recorder.Code)
assert.Equal(t, http.StatusOK, recorder.Code)
})
}
})
Expand All @@ -165,29 +230,26 @@ func TestHandler(t *testing.T) {
{
name: "provide 201 code",
response: Response{
Code: 201,
Code: http.StatusCreated,
},
expected: 201,
expected: http.StatusCreated,
},
{
name: "provide 503 code",
response: Response{
Code: 503,
Code: http.StatusServiceUnavailable,
},
expected: 503,
expected: http.StatusServiceUnavailable,
},
{
name: "automatically provide 200 code",
response: Response{},
expected: 200,
expected: http.StatusOK,
},
}
for _, testCase := range tests {
t.Run(testCase.name, func(t *testing.T) {
handler := internalHandler{
logger: logger,
response: testCase.response,
}
handler := makeHandler(t, testCase.response)

request := httptest.NewRequest(http.MethodGet, "/", nil)
recorder := httptest.NewRecorder()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,43 +1,36 @@
package mock

import (
"github.com/evg4b/uncors/internal/contracts"
"github.com/gorilla/mux"
)

func makeMockedRoutes(router *mux.Router, logger contracts.Logger, mocks []Mock) {
func (m *Middleware) makeMockedRoutes() {
var defaultMocks []Mock

for _, mock := range mocks {
for _, mock := range m.mocks {
if len(mock.Queries) > 0 || len(mock.Headers) > 0 || len(mock.Method) > 0 {
route := router.NewRoute()

route := m.router.NewRoute()
setPath(route, mock.Path)
setMethod(route, mock.Method)
setQueries(route, mock.Queries)
setHeaders(route, mock.Headers)

route.Handler(&internalHandler{
response: mock.Response,
logger: logger,
})
route.Handler(m.makeHandler(mock.Response))
} else {
defaultMocks = append(defaultMocks, mock)
}
}

for _, mock := range defaultMocks {
route := router.NewRoute()

route := m.router.NewRoute()
setPath(route, mock.Path)

route.Handler(&internalHandler{
response: mock.Response,
logger: logger,
})
route.Handler(m.makeHandler(mock.Response))
}
}

func (m *Middleware) makeHandler(response Response) *internalHandler {
return &internalHandler{response, m.logger, m.fs}
}

func setPath(route *mux.Route, path string) {
if len(path) > 0 {
route.Path(path)
Expand Down
4 changes: 3 additions & 1 deletion internal/middlewares/mock/middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ import (

"github.com/evg4b/uncors/internal/contracts"
"github.com/gorilla/mux"
"github.com/spf13/afero"
)

type Middleware struct {
router *mux.Router
next http.Handler
logger contracts.Logger
mocks []Mock
fs afero.Fs
}

func NewMockMiddleware(options ...MiddlewareOption) *Middleware {
Expand All @@ -22,7 +24,7 @@ func NewMockMiddleware(options ...MiddlewareOption) *Middleware {
option(middleware)
}

makeMockedRoutes(middleware.router, middleware.logger, middleware.mocks)
middleware.makeMockedRoutes()
router.NotFoundHandler = middleware.next
router.MethodNotAllowedHandler = middleware.next

Expand Down
Loading

0 comments on commit 3596139

Please sign in to comment.