Add backend unit tests

This commit is contained in:
Jamie Curnow
2023-07-25 11:59:02 +10:00
parent 72b071dbaa
commit b123ca4fd0
17 changed files with 399 additions and 283 deletions

View File

@ -1,46 +0,0 @@
package http
import (
"context"
"encoding/json"
"github.com/qri-io/jsonschema"
"github.com/rotisserie/eris"
)
var (
// ErrInvalidJSON is an error for invalid json
ErrInvalidJSON = eris.New("JSON is invalid")
// ErrInvalidPayload is an error for invalid incoming data
ErrInvalidPayload = eris.New("Payload is invalid")
)
// ValidateRequestSchema takes a Schema and the Content to validate against it
func ValidateRequestSchema(schema string, requestBody []byte) ([]jsonschema.KeyError, error) {
var jsonErrors []jsonschema.KeyError
var schemaBytes = []byte(schema)
// Make sure the body is valid JSON
if !isJSON(requestBody) {
return jsonErrors, ErrInvalidJSON
}
rs := &jsonschema.Schema{}
if err := json.Unmarshal(schemaBytes, rs); err != nil {
return jsonErrors, err
}
var validationErr error
ctx := context.TODO()
if jsonErrors, validationErr = rs.ValidateBytes(ctx, requestBody); len(jsonErrors) > 0 {
return jsonErrors, validationErr
}
// Valid
return nil, nil
}
func isJSON(bytes []byte) bool {
var js map[string]interface{}
return json.Unmarshal(bytes, &js) == nil
}

View File

@ -11,6 +11,12 @@ import (
"npm/internal/logger"
"github.com/qri-io/jsonschema"
"github.com/rotisserie/eris"
)
var (
// ErrInvalidPayload is an error for invalid incoming data
ErrInvalidPayload = eris.New("Payload is invalid")
)
// Response interface for standard API results

View File

@ -0,0 +1,180 @@
package http
import (
"io"
"net/http"
"net/http/httptest"
"npm/internal/entity/user"
"npm/internal/model"
"testing"
"github.com/qri-io/jsonschema"
"github.com/stretchr/testify/assert"
)
func TestResultResponseJSON(t *testing.T) {
tests := []struct {
name string
status int
given interface{}
want string
}{
{
name: "simple response",
status: http.StatusOK,
given: true,
want: "{\"result\":true}",
},
{
name: "detailed response",
status: http.StatusBadRequest,
given: user.Model{
ModelBase: model.ModelBase{ID: 10},
Email: "me@example.com",
Name: "John Doe",
Nickname: "Jonny",
},
want: "{\"result\":{\"id\":10,\"created_at\":0,\"updated_at\":0,\"name\":\"John Doe\",\"nickname\":\"Jonny\",\"email\":\"me@example.com\",\"is_disabled\":false,\"gravatar_url\":\"\"}}",
},
{
name: "error response",
status: http.StatusNotFound,
given: ErrorResponse{
Code: 404,
Message: "Not found",
Invalid: []string{"your", "page", "was", "not", "found"},
},
want: "{\"result\":null,\"error\":{\"code\":404,\"message\":\"Not found\",\"invalid\":[\"your\",\"page\",\"was\",\"not\",\"found\"]}}",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := httptest.NewRequest(http.MethodGet, "/anything", nil)
w := httptest.NewRecorder()
ResultResponseJSON(w, r, tt.status, tt.given)
res := w.Result()
defer res.Body.Close()
body, err := io.ReadAll(res.Body)
if err != nil {
t.Errorf("expected error to be nil got %v", err)
}
assert.Equal(t, tt.want, string(body))
assert.Equal(t, tt.status, res.StatusCode)
assert.Equal(t, "application/json; charset=utf-8", res.Header.Get("Content-Type"))
})
}
}
func TestResultSchemaErrorJSON(t *testing.T) {
tests := []struct {
name string
given []jsonschema.KeyError
want string
}{
{
name: "case a",
given: []jsonschema.KeyError{
{
PropertyPath: "/something",
InvalidValue: "name",
Message: "Name cannot be empty",
},
},
want: "{\"result\":null,\"error\":{\"code\":400,\"message\":{},\"invalid\":[{\"propertyPath\":\"/something\",\"invalidValue\":\"name\",\"message\":\"Name cannot be empty\"}]}}",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := httptest.NewRequest(http.MethodGet, "/anything", nil)
w := httptest.NewRecorder()
ResultSchemaErrorJSON(w, r, tt.given)
res := w.Result()
defer res.Body.Close()
body, err := io.ReadAll(res.Body)
if err != nil {
t.Errorf("expected error to be nil got %v", err)
}
assert.Equal(t, tt.want, string(body))
assert.Equal(t, 400, res.StatusCode)
assert.Equal(t, "application/json; charset=utf-8", res.Header.Get("Content-Type"))
})
}
}
func TestResultErrorJSON(t *testing.T) {
tests := []struct {
name string
status int
message string
extended interface{}
want string
}{
{
name: "case a",
status: http.StatusBadGateway,
message: "Oh not something is not acceptable",
extended: nil,
want: "{\"result\":null,\"error\":{\"code\":502,\"message\":\"Oh not something is not acceptable\"}}",
},
{
name: "case b",
status: http.StatusNotAcceptable,
message: "Oh not something is not acceptable again",
extended: []string{"name is not allowed", "dob is wrong or something"},
want: "{\"result\":null,\"error\":{\"code\":406,\"message\":\"Oh not something is not acceptable again\",\"invalid\":[\"name is not allowed\",\"dob is wrong or something\"]}}",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := httptest.NewRequest(http.MethodGet, "/anything", nil)
w := httptest.NewRecorder()
ResultErrorJSON(w, r, tt.status, tt.message, tt.extended)
res := w.Result()
defer res.Body.Close()
body, err := io.ReadAll(res.Body)
if err != nil {
t.Errorf("expected error to be nil got %v", err)
}
assert.Equal(t, tt.want, string(body))
assert.Equal(t, tt.status, res.StatusCode)
assert.Equal(t, "application/json; charset=utf-8", res.Header.Get("Content-Type"))
})
}
}
func TestNotFound(t *testing.T) {
t.Run("basic test", func(t *testing.T) {
r := httptest.NewRequest(http.MethodGet, "/anything", nil)
w := httptest.NewRecorder()
NotFound(w, r)
res := w.Result()
defer res.Body.Close()
body, err := io.ReadAll(res.Body)
if err != nil {
t.Errorf("expected error to be nil got %v", err)
}
assert.Equal(t, "{\"result\":null,\"error\":{\"code\":404,\"message\":\"Not found\"}}", string(body))
assert.Equal(t, http.StatusNotFound, res.StatusCode)
assert.Equal(t, "application/json; charset=utf-8", res.Header.Get("Content-Type"))
})
}
func TestResultResponseText(t *testing.T) {
t.Run("basic test", func(t *testing.T) {
r := httptest.NewRequest(http.MethodGet, "/anything", nil)
w := httptest.NewRecorder()
ResultResponseText(w, r, http.StatusOK, "omg this works")
res := w.Result()
defer res.Body.Close()
body, err := io.ReadAll(res.Body)
if err != nil {
t.Errorf("expected error to be nil got %v", err)
}
assert.Equal(t, "omg this works", string(body))
assert.Equal(t, http.StatusOK, res.StatusCode)
assert.Equal(t, "text/plain; charset=utf-8", res.Header.Get("Content-Type"))
})
}