Skip to content

Commit

Permalink
added test cases and refactored code for testing
Browse files Browse the repository at this point in the history
  • Loading branch information
s0s01qp committed Jul 25, 2021
1 parent 1592b8a commit a5c9506
Show file tree
Hide file tree
Showing 17 changed files with 770 additions and 63 deletions.
24 changes: 24 additions & 0 deletions commons/utils_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package commons_test

import (
"github.com/stretchr/testify/assert"
"testing"

"github.com/sinhashubham95/moxy/commons"
)

func TestEncodeJSON(t *testing.T) {
data := map[string]string{
"naruto": "rocks",
}
bytes, err := commons.EncodeJSON(data)
assert.NoError(t, err)
assert.Contains(t, string(bytes), "naruto")
}

func TestDecodeJSON(t *testing.T) {
var data map[string]interface{}
err := commons.DecodeJSON([]byte("{\"naruto\": \"rocks\"}"), &data)
assert.NoError(t, err)
assert.Equal(t, "rocks", data["naruto"])
}
21 changes: 21 additions & 0 deletions controllers/commons.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package controllers

import (
"github.com/sinhashubham95/moxy/commons"
"github.com/sinhashubham95/moxy/persistence"
)

// EncodeJSON is used to encode the given interface into json bytes
var EncodeJSON = commons.EncodeJSON

// DecodeJSON is used to decode the given bytes into interface
var DecodeJSON = commons.DecodeJSON

// PersistenceSave is used to save the entity
var PersistenceSave = persistence.Save

// PersistenceView is used to view the entity
var PersistenceView = persistence.View

// PersistenceDelete is used to delete the entity
var PersistenceDelete = persistence.Delete
117 changes: 117 additions & 0 deletions controllers/commons_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package controllers_test

import (
"bytes"
"errors"
"fmt"
"github.com/sinhashubham95/moxy/commons"
"github.com/sinhashubham95/moxy/controllers"
"github.com/sinhashubham95/moxy/persistence"
"github.com/stretchr/testify/assert"
"github.com/valyala/fasthttp"
"net/http"
"sync"
"testing"
)

var encodeJSON = commons.EncodeJSON
var decodeJSON = commons.DecodeJSON

var persistenceSave = persistence.Save
var persistenceView = persistence.View
var persistenceDelete = persistence.Delete

var portMu sync.Mutex
var port = 1001

func getRandomPortNumber() int {
portMu.Lock()
defer portMu.Unlock()
port += 10
return port
}

func setupFastHTTPHandlersAndGetResponse(t *testing.T, method, path string, body []byte,
handler fasthttp.RequestHandler) *http.Response {
port := getRandomPortNumber()

go func(method, path string, handler fasthttp.RequestHandler) {
assert.NoError(
t,
fasthttp.ListenAndServe(
fmt.Sprintf(":%d", port),
func(ctx *fasthttp.RequestCtx) {
if string(ctx.Method()) == method && string(ctx.Path()) == path {
handler(ctx)
return
}
ctx.SetStatusCode(http.StatusNotFound)
},
),
)
}(method, path, handler)

request, err := http.NewRequest(method, fmt.Sprintf("http://localhost:%d%s", port, path),
bytes.NewReader(body))
assert.NoError(t, err)

response, err := http.DefaultClient.Do(request)
assert.NoError(t, err)

return response
}

func mockEncodeJSONWithError() {
encodeJSON = controllers.EncodeJSON
controllers.EncodeJSON = func(interface{}) ([]byte, error) {
return nil, errors.New("error")
}
}

func unMockEncodeJSON() {
controllers.EncodeJSON = encodeJSON
}

func mockDecodeJSONWithError() {
decodeJSON = controllers.DecodeJSON
controllers.DecodeJSON = func([]byte, interface{}) error {
return errors.New("error")
}
}

func unMockDecodeJSON() {
controllers.DecodeJSON = decodeJSON
}

func mockPersistenceSaveWithError() {
persistenceSave = controllers.PersistenceSave
controllers.PersistenceSave = func(persistence.Entity) error {
return errors.New("error")
}
}

func unMockPersistenceSave() {
controllers.PersistenceSave = persistenceSave
}

func mockPersistenceViewWithError() {
persistenceView = controllers.PersistenceView
controllers.PersistenceView = func(persistence.Entity) error {
return errors.New("error")
}
}

func unMockPersistenceView() {
controllers.PersistenceView = persistenceView
}

func mockPersistenceDeleteWithError() {
persistenceDelete = controllers.PersistenceDelete
controllers.PersistenceDelete = func(persistence.Entity) error {
return errors.New("error")
}
}

func unMockPersistenceDelete() {
controllers.PersistenceDelete = persistenceDelete
}
5 changes: 2 additions & 3 deletions controllers/handle.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package controllers

import (
"fmt"
"github.com/sinhashubham95/moxy/persistence"
"github.com/valyala/fasthttp"
"net/http"

Expand All @@ -25,7 +24,7 @@ func Handle(ctx *fasthttp.RequestCtx) {
}

// try to fetch the mock
err := persistence.View(&mock)
err := PersistenceView(&mock)
if err == nil {
// this means that the mock exists
ctx.SetStatusCode(mock.Status)
Expand All @@ -36,7 +35,7 @@ func Handle(ctx *fasthttp.RequestCtx) {
ctx.SetBodyString(s)
} else {
// first parse to bytes
body, err := commons.EncodeJSON(mock.Body)
body, err := EncodeJSON(mock.Body)
if err == nil {
// now write
ctx.SetContentType(commons.ApplicationJSONContentType)
Expand Down
12 changes: 6 additions & 6 deletions controllers/mock.go
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
package controllers

import (
"github.com/sinhashubham95/moxy/commons"
"github.com/sinhashubham95/moxy/models"
"github.com/sinhashubham95/moxy/persistence"
"github.com/sinhashubham95/moxy/persistence/entities"
"github.com/valyala/fasthttp"
"net/http"

"github.com/sinhashubham95/moxy/models"
"github.com/sinhashubham95/moxy/persistence/entities"
)

// HandleMock is used to mock and endpoint
func HandleMock(ctx *fasthttp.RequestCtx) {
var request models.MockRequest
err := commons.DecodeJSON(ctx.PostBody(), &request)
err := DecodeJSON(ctx.PostBody(), &request)
if err != nil {
// invalid request body
ctx.Error("invalid json request body provided", http.StatusBadRequest)
return
}
(&request).Clean()
err = (&request).Validate()
if err != nil {
// invalid request
Expand All @@ -26,7 +26,7 @@ func HandleMock(ctx *fasthttp.RequestCtx) {
}
(&request).Default()
// now time to save it
err = persistence.Save(&entities.Mock{
err = PersistenceSave(&entities.Mock{
Tag: request.Tag,
Method: request.Method,
Path: request.Path,
Expand Down
167 changes: 167 additions & 0 deletions controllers/mock_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
package controllers_test

import (
"fmt"
"github.com/sinhashubham95/moxy/commons"
"github.com/sinhashubham95/moxy/controllers"
"github.com/sinhashubham95/moxy/models"
"github.com/sinhashubham95/moxy/persistence/entities"
"github.com/stretchr/testify/assert"
"io"
"io/ioutil"
"net/http"
"testing"
)

func testHandleMockAgainstError(t *testing.T, resBody io.ReadCloser, msg string) {
body, err := ioutil.ReadAll(resBody)
assert.NoError(t, err)
defer func(t *testing.T, body io.ReadCloser) {
assert.NoError(t, body.Close())
}(t, resBody)
assert.Equal(t, msg, string(body))
}

func getMockBytes(t *testing.T, tag, method, path string, statusCode int, body interface{}) []byte {
request := models.MockRequest{
Tag: tag,
Method: method,
Path: path,
ResponseStatus: statusCode,
ResponseBody: body,
}
bytes, err := commons.EncodeJSON(&request)
assert.NoError(t, err)
return bytes
}

func TestHandleMockRequestParseError(t *testing.T) {
mockDecodeJSONWithError()
defer unMockDecodeJSON()
res := setupFastHTTPHandlersAndGetResponse(
t,
http.MethodPost,
"/moxy/mock",
[]byte("sample"),
controllers.HandleMock,
)

assert.Equal(t, http.StatusBadRequest, res.StatusCode)
testHandleMockAgainstError(t, res.Body, "invalid json request body provided")
}

func TestHandleMockEmptyTag(t *testing.T) {
res := setupFastHTTPHandlersAndGetResponse(
t,
http.MethodPost,
"/moxy/mock",
getMockBytes(t, "", "GET", "/naruto", 200, "naruto"),
controllers.HandleMock,
)

assert.Equal(t, http.StatusBadRequest, res.StatusCode)
testHandleMockAgainstError(t, res.Body, "empty tag provided")
}

func TestHandleMockInvalidMethod(t *testing.T) {
res := setupFastHTTPHandlersAndGetResponse(
t,
http.MethodPost,
"/moxy/mock",
getMockBytes(t, "1234", "", "/naruto", 200, "naruto"),
controllers.HandleMock,
)

assert.Equal(t, http.StatusBadRequest, res.StatusCode)
testHandleMockAgainstError(t, res.Body, "http method should be one of GET, POST, PUT or DELETE")
}

func TestHandleMockEmptyPath(t *testing.T) {
res := setupFastHTTPHandlersAndGetResponse(
t,
http.MethodPost,
"/moxy/mock",
getMockBytes(t, "1234", "GET", "", 200, "naruto"),
controllers.HandleMock,
)

assert.Equal(t, http.StatusBadRequest, res.StatusCode)
testHandleMockAgainstError(t, res.Body, "empty path provided")
}

func TestHandleMockActuatorPath(t *testing.T) {
res := setupFastHTTPHandlersAndGetResponse(
t,
http.MethodPost,
"/moxy/mock",
getMockBytes(t, "1234", "GET", "/actuator/info", 200, "naruto"),
controllers.HandleMock,
)

assert.Equal(t, http.StatusBadRequest, res.StatusCode)
testHandleMockAgainstError(t, res.Body, fmt.Sprintf("path cannot start with %s", commons.ActuatorPrefix))
}

func TestHandleMockMoxyPrefix(t *testing.T) {
res := setupFastHTTPHandlersAndGetResponse(
t,
http.MethodPost,
"/moxy/mock",
getMockBytes(t, "1234", "GET", "/moxy/info", 200, "naruto"),
controllers.HandleMock,
)

assert.Equal(t, http.StatusBadRequest, res.StatusCode)
testHandleMockAgainstError(t, res.Body, fmt.Sprintf("path cannot start with %s", commons.MoxyPrefix))
}

func TestHandleMockInvalidStatus(t *testing.T) {
res := setupFastHTTPHandlersAndGetResponse(
t,
http.MethodPost,
"/moxy/mock",
getMockBytes(t, "1234", "GET", "/naruto", 8, "naruto"),
controllers.HandleMock,
)

assert.Equal(t, http.StatusBadRequest, res.StatusCode)
testHandleMockAgainstError(t, res.Body, "response status code should be in the range 100-599")
}

func TestHandleMockPersistenceSaveError(t *testing.T) {
mockPersistenceSaveWithError()
defer unMockPersistenceSave()

res := setupFastHTTPHandlersAndGetResponse(
t,
http.MethodPost,
"/moxy/mock",
getMockBytes(t, "1234", "GET", "/naruto", 0, "naruto"),
controllers.HandleMock,
)

assert.Equal(t, http.StatusInternalServerError, res.StatusCode)
testHandleMockAgainstError(t, res.Body, "error")
}

func TestHandleMock(t *testing.T) {
defer func(t *testing.T) {
assert.NoError(
t,
persistenceDelete(&entities.Mock{
Tag: "1234",
Method: "GET",
Path: "/naruto",
}),
)
}(t)
res := setupFastHTTPHandlersAndGetResponse(
t,
http.MethodPost,
"/moxy/mock",
getMockBytes(t, "1234", "GET", "/naruto", 0, "naruto"),
controllers.HandleMock,
)

assert.Equal(t, http.StatusOK, res.StatusCode)
}
Loading

0 comments on commit a5c9506

Please sign in to comment.