1
0
mirror of https://github.com/raseels-repos/golang-saas-starter-kit.git synced 2025-06-06 23:46:29 +02:00

448 lines
13 KiB
Go
Raw Normal View History

package tests
import (
"bytes"
"encoding/json"
"net/http"
"net/http/httptest"
"strings"
"testing"
"geeks-accelerator/oss/saas-starter-kit/example-project/internal/platform/tests"
"geeks-accelerator/oss/saas-starter-kit/example-project/internal/platform/web"
"geeks-accelerator/oss/saas-starter-kit/example-project/internal/product"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"gopkg.in/mgo.v2/bson"
)
// TestProducts is the entry point for the products
func TestProducts(t *testing.T) {
defer tests.Recover(t)
t.Run("getProducts200Empty", getProducts200Empty)
t.Run("postProduct400", postProduct400)
t.Run("postProduct401", postProduct401)
t.Run("getProduct404", getProduct404)
t.Run("getProduct400", getProduct400)
t.Run("deleteProduct404", deleteProduct404)
t.Run("putProduct404", putProduct404)
t.Run("crudProducts", crudProduct)
}
// getProducts200Empty validates an empty products list can be retrieved with the endpoint.
func getProducts200Empty(t *testing.T) {
r := httptest.NewRequest("GET", "/v1/products", nil)
w := httptest.NewRecorder()
r.Header.Set("Authorization", userAuthorization)
a.ServeHTTP(w, r)
t.Log("Given the need to fetch an empty list of products with the products endpoint.")
{
t.Log("\tTest 0:\tWhen fetching an empty product list.")
{
if w.Code != http.StatusOK {
t.Fatalf("\t%s\tShould receive a status code of 200 for the response : %v", tests.Failed, w.Code)
}
t.Logf("\t%s\tShould receive a status code of 200 for the response.", tests.Success)
recv := w.Body.String()
resp := `[]`
if resp != recv {
t.Log("Got :", recv)
t.Log("Want:", resp)
t.Fatalf("\t%s\tShould get the expected result.", tests.Failed)
}
t.Logf("\t%s\tShould get the expected result.", tests.Success)
}
}
}
// postProduct400 validates a product can't be created with the endpoint
// unless a valid product document is submitted.
func postProduct400(t *testing.T) {
r := httptest.NewRequest("POST", "/v1/products", strings.NewReader(`{}`))
w := httptest.NewRecorder()
r.Header.Set("Authorization", userAuthorization)
a.ServeHTTP(w, r)
t.Log("Given the need to validate a new product can't be created with an invalid document.")
{
t.Log("\tTest 0:\tWhen using an incomplete product value.")
{
if w.Code != http.StatusBadRequest {
t.Fatalf("\t%s\tShould receive a status code of 400 for the response : %v", tests.Failed, w.Code)
}
t.Logf("\t%s\tShould receive a status code of 400 for the response.", tests.Success)
// Inspect the response.
var got web.ErrorResponse
if err := json.NewDecoder(w.Body).Decode(&got); err != nil {
t.Fatalf("\t%s\tShould be able to unmarshal the response to an error type : %v", tests.Failed, err)
}
t.Logf("\t%s\tShould be able to unmarshal the response to an error type.", tests.Success)
// Define what we want to see.
want := web.ErrorResponse{
Error: "field validation error",
Fields: []web.FieldError{
{Field: "name", Error: "name is a required field"},
{Field: "cost", Error: "cost is a required field"},
{Field: "quantity", Error: "quantity is a required field"},
},
}
// We can't rely on the order of the field errors so they have to be
// sorted. Tell the cmp package how to sort them.
sorter := cmpopts.SortSlices(func(a, b web.FieldError) bool {
return a.Field < b.Field
})
if diff := cmp.Diff(want, got, sorter); diff != "" {
t.Fatalf("\t%s\tShould get the expected result. Diff:\n%s", tests.Failed, diff)
}
t.Logf("\t%s\tShould get the expected result.", tests.Success)
}
}
}
// postProduct401 validates a product can't be created with the endpoint
// unless the user is authenticated
func postProduct401(t *testing.T) {
np := product.NewProduct{
Name: "Comic Books",
Cost: 25,
Quantity: 60,
}
body, err := json.Marshal(&np)
if err != nil {
t.Fatal(err)
}
r := httptest.NewRequest("POST", "/v1/products", bytes.NewBuffer(body))
w := httptest.NewRecorder()
// Not setting an authorization header
a.ServeHTTP(w, r)
t.Log("Given the need to validate a new product can't be created with an invalid document.")
{
t.Log("\tTest 0:\tWhen using an incomplete product value.")
{
if w.Code != http.StatusUnauthorized {
t.Fatalf("\t%s\tShould receive a status code of 401 for the response : %v", tests.Failed, w.Code)
}
t.Logf("\t%s\tShould receive a status code of 401 for the response.", tests.Success)
}
}
}
// getProduct400 validates a product request for a malformed id.
func getProduct400(t *testing.T) {
id := "12345"
r := httptest.NewRequest("GET", "/v1/products/"+id, nil)
w := httptest.NewRecorder()
r.Header.Set("Authorization", userAuthorization)
a.ServeHTTP(w, r)
t.Log("Given the need to validate getting a product with a malformed id.")
{
t.Logf("\tTest 0:\tWhen using the new product %s.", id)
{
if w.Code != http.StatusBadRequest {
t.Fatalf("\t%s\tShould receive a status code of 400 for the response : %v", tests.Failed, w.Code)
}
t.Logf("\t%s\tShould receive a status code of 400 for the response.", tests.Success)
recv := w.Body.String()
resp := `{"error":"ID is not in its proper form"}`
if resp != recv {
t.Log("Got :", recv)
t.Log("Want:", resp)
t.Fatalf("\t%s\tShould get the expected result.", tests.Failed)
}
t.Logf("\t%s\tShould get the expected result.", tests.Success)
}
}
}
// getProduct404 validates a product request for a product that does not exist with the endpoint.
func getProduct404(t *testing.T) {
id := bson.NewObjectId().Hex()
r := httptest.NewRequest("GET", "/v1/products/"+id, nil)
w := httptest.NewRecorder()
r.Header.Set("Authorization", userAuthorization)
a.ServeHTTP(w, r)
t.Log("Given the need to validate getting a product with an unknown id.")
{
t.Logf("\tTest 0:\tWhen using the new product %s.", id)
{
if w.Code != http.StatusNotFound {
t.Fatalf("\t%s\tShould receive a status code of 404 for the response : %v", tests.Failed, w.Code)
}
t.Logf("\t%s\tShould receive a status code of 404 for the response.", tests.Success)
recv := w.Body.String()
resp := "Entity not found"
if !strings.Contains(recv, resp) {
t.Log("Got :", recv)
t.Log("Want:", resp)
t.Fatalf("\t%s\tShould get the expected result.", tests.Failed)
}
t.Logf("\t%s\tShould get the expected result.", tests.Success)
}
}
}
// deleteProduct404 validates deleting a product that does not exist.
func deleteProduct404(t *testing.T) {
id := bson.NewObjectId().Hex()
r := httptest.NewRequest("DELETE", "/v1/products/"+id, nil)
w := httptest.NewRecorder()
r.Header.Set("Authorization", userAuthorization)
a.ServeHTTP(w, r)
t.Log("Given the need to validate deleting a product that does not exist.")
{
t.Logf("\tTest 0:\tWhen using the new product %s.", id)
{
if w.Code != http.StatusNotFound {
t.Fatalf("\t%s\tShould receive a status code of 404 for the response : %v", tests.Failed, w.Code)
}
t.Logf("\t%s\tShould receive a status code of 404 for the response.", tests.Success)
recv := w.Body.String()
resp := "Entity not found"
if !strings.Contains(recv, resp) {
t.Log("Got :", recv)
t.Log("Want:", resp)
t.Fatalf("\t%s\tShould get the expected result.", tests.Failed)
}
t.Logf("\t%s\tShould get the expected result.", tests.Success)
}
}
}
// putProduct404 validates updating a product that does not exist.
func putProduct404(t *testing.T) {
up := product.UpdateProduct{
Name: tests.StringPointer("Nonexistent"),
}
id := bson.NewObjectId().Hex()
body, err := json.Marshal(&up)
if err != nil {
t.Fatal(err)
}
r := httptest.NewRequest("PUT", "/v1/products/"+id, bytes.NewBuffer(body))
w := httptest.NewRecorder()
r.Header.Set("Authorization", userAuthorization)
a.ServeHTTP(w, r)
t.Log("Given the need to validate updating a product that does not exist.")
{
t.Logf("\tTest 0:\tWhen using the new product %s.", id)
{
if w.Code != http.StatusNotFound {
t.Fatalf("\t%s\tShould receive a status code of 404 for the response : %v", tests.Failed, w.Code)
}
t.Logf("\t%s\tShould receive a status code of 404 for the response.", tests.Success)
recv := w.Body.String()
resp := "Entity not found"
if !strings.Contains(recv, resp) {
t.Log("Got :", recv)
t.Log("Want:", resp)
t.Fatalf("\t%s\tShould get the expected result.", tests.Failed)
}
t.Logf("\t%s\tShould get the expected result.", tests.Success)
}
}
}
// crudProduct performs a complete test of CRUD against the api.
func crudProduct(t *testing.T) {
p := postProduct201(t)
defer deleteProduct204(t, p.ID.Hex())
getProduct200(t, p.ID.Hex())
putProduct204(t, p.ID.Hex())
}
// postProduct201 validates a product can be created with the endpoint.
func postProduct201(t *testing.T) product.Product {
np := product.NewProduct{
Name: "Comic Books",
Cost: 25,
Quantity: 60,
}
body, err := json.Marshal(&np)
if err != nil {
t.Fatal(err)
}
r := httptest.NewRequest("POST", "/v1/products", bytes.NewBuffer(body))
w := httptest.NewRecorder()
r.Header.Set("Authorization", userAuthorization)
a.ServeHTTP(w, r)
// p is the value we will return.
var p product.Product
t.Log("Given the need to create a new product with the products endpoint.")
{
t.Log("\tTest 0:\tWhen using the declared product value.")
{
if w.Code != http.StatusCreated {
t.Fatalf("\t%s\tShould receive a status code of 201 for the response : %v", tests.Failed, w.Code)
}
t.Logf("\t%s\tShould receive a status code of 201 for the response.", tests.Success)
if err := json.NewDecoder(w.Body).Decode(&p); err != nil {
t.Fatalf("\t%s\tShould be able to unmarshal the response : %v", tests.Failed, err)
}
// Define what we wanted to receive. We will just trust the generated
// fields like ID and Dates so we copy p.
want := p
want.Name = "Comic Books"
want.Cost = 25
want.Quantity = 60
if diff := cmp.Diff(want, p); diff != "" {
t.Fatalf("\t%s\tShould get the expected result. Diff:\n%s", tests.Failed, diff)
}
t.Logf("\t%s\tShould get the expected result.", tests.Success)
}
}
return p
}
// deleteProduct200 validates deleting a product that does exist.
func deleteProduct204(t *testing.T, id string) {
r := httptest.NewRequest("DELETE", "/v1/products/"+id, nil)
w := httptest.NewRecorder()
r.Header.Set("Authorization", userAuthorization)
a.ServeHTTP(w, r)
t.Log("Given the need to validate deleting a product that does exist.")
{
t.Logf("\tTest 0:\tWhen using the new product %s.", id)
{
if w.Code != http.StatusNoContent {
t.Fatalf("\t%s\tShould receive a status code of 204 for the response : %v", tests.Failed, w.Code)
}
t.Logf("\t%s\tShould receive a status code of 204 for the response.", tests.Success)
}
}
}
// getProduct200 validates a product request for an existing id.
func getProduct200(t *testing.T, id string) {
r := httptest.NewRequest("GET", "/v1/products/"+id, nil)
w := httptest.NewRecorder()
r.Header.Set("Authorization", userAuthorization)
a.ServeHTTP(w, r)
t.Log("Given the need to validate getting a product that exists.")
{
t.Logf("\tTest 0:\tWhen using the new product %s.", id)
{
if w.Code != http.StatusOK {
t.Fatalf("\t%s\tShould receive a status code of 200 for the response : %v", tests.Failed, w.Code)
}
t.Logf("\t%s\tShould receive a status code of 200 for the response.", tests.Success)
var p product.Product
if err := json.NewDecoder(w.Body).Decode(&p); err != nil {
t.Fatalf("\t%s\tShould be able to unmarshal the response : %v", tests.Failed, err)
}
// Define what we wanted to receive. We will just trust the generated
// fields like Dates so we copy p.
want := p
want.ID = bson.ObjectIdHex(id)
want.Name = "Comic Books"
want.Cost = 25
want.Quantity = 60
if diff := cmp.Diff(want, p); diff != "" {
t.Fatalf("\t%s\tShould get the expected result. Diff:\n%s", tests.Failed, diff)
}
t.Logf("\t%s\tShould get the expected result.", tests.Success)
}
}
}
// putProduct204 validates updating a product that does exist.
func putProduct204(t *testing.T, id string) {
body := `{"name": "Graphic Novels", "cost": 100}`
r := httptest.NewRequest("PUT", "/v1/products/"+id, strings.NewReader(body))
w := httptest.NewRecorder()
r.Header.Set("Authorization", userAuthorization)
a.ServeHTTP(w, r)
t.Log("Given the need to update a product with the products endpoint.")
{
t.Log("\tTest 0:\tWhen using the modified product value.")
{
if w.Code != http.StatusNoContent {
t.Fatalf("\t%s\tShould receive a status code of 204 for the response : %v", tests.Failed, w.Code)
}
t.Logf("\t%s\tShould receive a status code of 204 for the response.", tests.Success)
r = httptest.NewRequest("GET", "/v1/products/"+id, nil)
w = httptest.NewRecorder()
r.Header.Set("Authorization", userAuthorization)
a.ServeHTTP(w, r)
if w.Code != http.StatusOK {
t.Fatalf("\t%s\tShould receive a status code of 200 for the retrieve : %v", tests.Failed, w.Code)
}
t.Logf("\t%s\tShould receive a status code of 200 for the retrieve.", tests.Success)
var ru product.Product
if err := json.NewDecoder(w.Body).Decode(&ru); err != nil {
t.Fatalf("\t%s\tShould be able to unmarshal the response : %v", tests.Failed, err)
}
if ru.Name != "Graphic Novels" {
t.Fatalf("\t%s\tShould see an updated Name : got %q want %q", tests.Failed, ru.Name, "Graphic Novels")
}
t.Logf("\t%s\tShould see an updated Name.", tests.Success)
}
}
}