You've already forked golang-saas-starter-kit
mirror of
https://github.com/raseels-repos/golang-saas-starter-kit.git
synced 2025-06-15 00:15:15 +02:00
Basic example cleanup
Rename sales-api to web-api and remove sales-admin
This commit is contained in:
447
example-project/cmd/web-api/tests/project_test.go
Normal file
447
example-project/cmd/web-api/tests/project_test.go
Normal file
@ -0,0 +1,447 @@
|
||||
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/project"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
)
|
||||
|
||||
// TestProjects is the entry point for the projects
|
||||
func TestProjects(t *testing.T) {
|
||||
defer tests.Recover(t)
|
||||
|
||||
t.Run("getProjects200Empty", getProjects200Empty)
|
||||
t.Run("postProject400", postProject400)
|
||||
t.Run("postProject401", postProject401)
|
||||
t.Run("getProject404", getProject404)
|
||||
t.Run("getProject400", getProject400)
|
||||
t.Run("deleteProject404", deleteProject404)
|
||||
t.Run("putProject404", putProject404)
|
||||
t.Run("crudProjects", crudProject)
|
||||
}
|
||||
|
||||
// getProjects200Empty validates an empty projects list can be retrieved with the endpoint.
|
||||
func getProjects200Empty(t *testing.T) {
|
||||
r := httptest.NewRequest("GET", "/v1/projects", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
r.Header.Set("Authorization", userAuthorization)
|
||||
|
||||
a.ServeHTTP(w, r)
|
||||
|
||||
t.Log("Given the need to fetch an empty list of projects with the projects endpoint.")
|
||||
{
|
||||
t.Log("\tTest 0:\tWhen fetching an empty project 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// postProject400 validates a project can't be created with the endpoint
|
||||
// unless a valid project document is submitted.
|
||||
func postProject400(t *testing.T) {
|
||||
r := httptest.NewRequest("POST", "/v1/projects", strings.NewReader(`{}`))
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
r.Header.Set("Authorization", userAuthorization)
|
||||
|
||||
a.ServeHTTP(w, r)
|
||||
|
||||
t.Log("Given the need to validate a new project can't be created with an invalid document.")
|
||||
{
|
||||
t.Log("\tTest 0:\tWhen using an incomplete project 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// postProject401 validates a project can't be created with the endpoint
|
||||
// unless the user is authenticated
|
||||
func postProject401(t *testing.T) {
|
||||
np := project.NewProject{
|
||||
Name: "Comic Books",
|
||||
Cost: 25,
|
||||
Quantity: 60,
|
||||
}
|
||||
|
||||
body, err := json.Marshal(&np)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
r := httptest.NewRequest("POST", "/v1/projects", bytes.NewBuffer(body))
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
// Not setting an authorization header
|
||||
|
||||
a.ServeHTTP(w, r)
|
||||
|
||||
t.Log("Given the need to validate a new project can't be created with an invalid document.")
|
||||
{
|
||||
t.Log("\tTest 0:\tWhen using an incomplete project 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// getProject400 validates a project request for a malformed id.
|
||||
func getProject400(t *testing.T) {
|
||||
id := "12345"
|
||||
|
||||
r := httptest.NewRequest("GET", "/v1/projects/"+id, nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
r.Header.Set("Authorization", userAuthorization)
|
||||
|
||||
a.ServeHTTP(w, r)
|
||||
|
||||
t.Log("Given the need to validate getting a project with a malformed id.")
|
||||
{
|
||||
t.Logf("\tTest 0:\tWhen using the new project %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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// getProject404 validates a project request for a project that does not exist with the endpoint.
|
||||
func getProject404(t *testing.T) {
|
||||
id := bson.NewObjectId().Hex()
|
||||
|
||||
r := httptest.NewRequest("GET", "/v1/projects/"+id, nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
r.Header.Set("Authorization", userAuthorization)
|
||||
|
||||
a.ServeHTTP(w, r)
|
||||
|
||||
t.Log("Given the need to validate getting a project with an unknown id.")
|
||||
{
|
||||
t.Logf("\tTest 0:\tWhen using the new project %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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// deleteProject404 validates deleting a project that does not exist.
|
||||
func deleteProject404(t *testing.T) {
|
||||
id := bson.NewObjectId().Hex()
|
||||
|
||||
r := httptest.NewRequest("DELETE", "/v1/projects/"+id, nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
r.Header.Set("Authorization", userAuthorization)
|
||||
|
||||
a.ServeHTTP(w, r)
|
||||
|
||||
t.Log("Given the need to validate deleting a project that does not exist.")
|
||||
{
|
||||
t.Logf("\tTest 0:\tWhen using the new project %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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// putProject404 validates updating a project that does not exist.
|
||||
func putProject404(t *testing.T) {
|
||||
up := project.UpdateProject{
|
||||
Name: tests.StringPointer("Nonexistent"),
|
||||
}
|
||||
|
||||
id := bson.NewObjectId().Hex()
|
||||
|
||||
body, err := json.Marshal(&up)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
r := httptest.NewRequest("PUT", "/v1/projects/"+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 project that does not exist.")
|
||||
{
|
||||
t.Logf("\tTest 0:\tWhen using the new project %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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// crudProject performs a complete test of CRUD against the api.
|
||||
func crudProject(t *testing.T) {
|
||||
p := postProject201(t)
|
||||
defer deleteProject204(t, p.ID.Hex())
|
||||
|
||||
getProject200(t, p.ID.Hex())
|
||||
putProject204(t, p.ID.Hex())
|
||||
}
|
||||
|
||||
// postProject201 validates a project can be created with the endpoint.
|
||||
func postProject201(t *testing.T) project.Project {
|
||||
np := project.NewProject{
|
||||
Name: "Comic Books",
|
||||
Cost: 25,
|
||||
Quantity: 60,
|
||||
}
|
||||
|
||||
body, err := json.Marshal(&np)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
r := httptest.NewRequest("POST", "/v1/projects", bytes.NewBuffer(body))
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
r.Header.Set("Authorization", userAuthorization)
|
||||
|
||||
a.ServeHTTP(w, r)
|
||||
|
||||
// p is the value we will return.
|
||||
var p project.Project
|
||||
|
||||
t.Log("Given the need to create a new project with the projects endpoint.")
|
||||
{
|
||||
t.Log("\tTest 0:\tWhen using the declared project 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
|
||||
}
|
||||
|
||||
// deleteProject200 validates deleting a project that does exist.
|
||||
func deleteProject204(t *testing.T, id string) {
|
||||
r := httptest.NewRequest("DELETE", "/v1/projects/"+id, nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
r.Header.Set("Authorization", userAuthorization)
|
||||
|
||||
a.ServeHTTP(w, r)
|
||||
|
||||
t.Log("Given the need to validate deleting a project that does exist.")
|
||||
{
|
||||
t.Logf("\tTest 0:\tWhen using the new project %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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// getProject200 validates a project request for an existing id.
|
||||
func getProject200(t *testing.T, id string) {
|
||||
r := httptest.NewRequest("GET", "/v1/projects/"+id, nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
r.Header.Set("Authorization", userAuthorization)
|
||||
|
||||
a.ServeHTTP(w, r)
|
||||
|
||||
t.Log("Given the need to validate getting a project that exists.")
|
||||
{
|
||||
t.Logf("\tTest 0:\tWhen using the new project %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 project.Project
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// putProject204 validates updating a project that does exist.
|
||||
func putProject204(t *testing.T, id string) {
|
||||
body := `{"name": "Graphic Novels", "cost": 100}`
|
||||
r := httptest.NewRequest("PUT", "/v1/projects/"+id, strings.NewReader(body))
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
r.Header.Set("Authorization", userAuthorization)
|
||||
|
||||
a.ServeHTTP(w, r)
|
||||
|
||||
t.Log("Given the need to update a project with the projects endpoint.")
|
||||
{
|
||||
t.Log("\tTest 0:\tWhen using the modified project 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/projects/"+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 project.Project
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user