Skip to content

Commit

Permalink
Responce mocks (#3)
Browse files Browse the repository at this point in the history
* Added mock draft

* Updated handler

* Added methods tests

* Added tests for mock routes generator

* Added mocks parameter

* Cleanup packages

* Added documentation
  • Loading branch information
Evgeny Abramovich committed Oct 16, 2022
1 parent bfe40b0 commit 9c2bfa1
Show file tree
Hide file tree
Showing 9 changed files with 615 additions and 4 deletions.
38 changes: 38 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,45 @@ If you are a Windows user, substitute the $HOME environment variable above with
* `--cert-file` - Path to HTTPS certificate file.
* `--key-file` - Path to matching for certificate private key.
* `--proxy` - HTTP/HTTPS proxy to provide requests to real server (used system by default).
* `--mocks` - File with defined [mocks](#mocks)

## Mocks

Uncors has endpoint mocks mechanism.
All mocks should be defined in yaml file and passed as parameter `--mocks`.
Currently available path, method, queries and headers filters
(for more information see [gorilla/mux](https://github.com/gorilla/mux#matching-routes) route matching) .

**Mocks file example:**

```yaml
- path: /hello
response:
code: 200
raw-content: '
Hello word
'
- path: /word
method: POST
queries:
param1: param 1 value
param2: param 1 value
headers:
header1: header 1 value
header2: header 2 value
response:
code: 200
headers:
header1: header 1 value
header2: header 2 value
raw-content: '
{ "status": "ok" }
'
```

At the moment supported only raw response content.
Content should be defined as multiline string (see more [here](https://yaml-multiline.info/)).
`Content-Type` will be set automatically, but you can specify custom content type via headers section.

## How it works

Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ go 1.19
require (
github.com/PuerkitoBio/purell v1.2.0
github.com/gojuno/minimock/v3 v3.0.10
github.com/gorilla/mux v1.8.0
github.com/pseidemann/finish v1.2.0
github.com/pterm/pterm v0.12.49
github.com/stretchr/testify v1.8.0
golang.org/x/net v0.0.0-20221004154528-8021a29435af
gopkg.in/yaml.v3 v3.0.1
)

require (
Expand All @@ -17,7 +19,6 @@ require (
github.com/containerd/console v1.0.3 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/gookit/color v1.5.2 // indirect
github.com/gorilla/mux v1.8.0 // indirect
github.com/lithammer/fuzzysearch v1.1.5 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
Expand All @@ -26,5 +27,4 @@ require (
golang.org/x/sys v0.0.0-20221006211917-84dc82d7e875 // indirect
golang.org/x/term v0.0.0-20220919170432-7a66f970e087 // indirect
golang.org/x/text v0.3.7 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
52 changes: 52 additions & 0 deletions internal/mock/handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package mock

import (
"fmt"
"net/http"

"github.com/evg4b/uncors/internal/responseprinter"
"github.com/pterm/pterm"
)

type Handler struct {
mock Mock
mockWriter pterm.PrefixPrinter
}

func NewMockHandler(options ...HandlerOption) *Handler {
handler := &Handler{
mockWriter: pterm.PrefixPrinter{
MessageStyle: &pterm.ThemeDefault.InfoMessageStyle,
Prefix: pterm.Prefix{
Style: &pterm.Style{pterm.FgBlack, pterm.BgLightMagenta},
Text: " MOCK ",
},
},
}

for _, option := range options {
option(handler)
}

return handler
}

func (handler *Handler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
updateRequest(request)
writer.WriteHeader(handler.mock.Response.Code)
fmt.Fprint(writer, handler.mock.Response.RawContent)
handler.mockWriter.Println(responseprinter.PrintResponse(&http.Response{
Request: request,
StatusCode: handler.mock.Response.Code,
}))
}

func updateRequest(request *http.Request) {
request.URL.Host = request.Host

if request.TLS != nil {
request.URL.Scheme = "https"
} else {
request.URL.Scheme = "http"
}
}
15 changes: 15 additions & 0 deletions internal/mock/model.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package mock

type Response struct {
Code int `yaml:"code"`
Headers map[string]string `yaml:"headers"`
RawContent string `yaml:"raw-content"` //nolint:tagliatelle
}

type Mock struct {
Path string `yaml:"path"`
Method string `yaml:"method"`
Queries map[string]string `yaml:"queries"`
Headers map[string]string `yaml:"headers"`
Response Response `yaml:"response"`
}
9 changes: 9 additions & 0 deletions internal/mock/options.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package mock

type HandlerOption = func(*Handler)

func WithMock(mock Mock) HandlerOption {
return func(handler *Handler) {
handler.mock = mock
}
}
60 changes: 60 additions & 0 deletions internal/mock/routes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package mock

import "github.com/gorilla/mux"

func MakeMockedRoutes(router *mux.Router, mocks []Mock) {
var defaultMocks []Mock

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

setPath(route, mock.Path)
setMethod(route, mock.Method)
setQueries(route, mock.Queries)
setHeaders(route, mock.Headers)

handler := NewMockHandler(WithMock(mock))
route.Handler(handler)
} else {
defaultMocks = append(defaultMocks, mock)
}
}

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

setPath(route, mock.Path)

handler := NewMockHandler(WithMock(mock))
route.Handler(handler)
}
}

func setPath(route *mux.Route, path string) {
if len(path) > 0 {
route.Path(path)
}
}

func setMethod(route *mux.Route, methods string) {
if len(methods) > 0 {
route.Methods(methods)
}
}

func setQueries(route *mux.Route, queries map[string]string) {
if len(queries) > 0 {
for key, value := range queries {
route.Queries(key, value)
}
}
}

func setHeaders(route *mux.Route, headers map[string]string) {
if len(headers) > 0 {
for key, value := range headers {
route.Headers(key, value)
}
}
}
Loading

0 comments on commit 9c2bfa1

Please sign in to comment.