Skip to content

Commit

Permalink
Fluent Testing API (#31)
Browse files Browse the repository at this point in the history
  • Loading branch information
garrettladley authored Jan 20, 2024
1 parent b087de4 commit 0930342
Show file tree
Hide file tree
Showing 4 changed files with 217 additions and 135 deletions.
180 changes: 89 additions & 91 deletions backend/tests/api/category_test.go
Original file line number Diff line number Diff line change
@@ -1,127 +1,125 @@
package tests

import (
"io"
"fmt"
"net/http"
"testing"

"github.com/GenerateNU/sac/backend/src/models"
"github.com/GenerateNU/sac/backend/src/transactions"
"github.com/huandu/go-assert"

"github.com/goccy/go-json"
)

func TestCreateCategoryWorks(t *testing.T) {
app, assert, resp := RequestTesterWithJSONBody(t, "POST", "/api/v1/categories/", &map[string]interface{}{
"category_name": "Science",
}, nil, nil, nil)
defer app.DropDB()
func CreateSampleCategory(t *testing.T, categoryName string) ExistingAppAssert {
return TestRequest{
Method: "POST",
Path: "/api/v1/categories/",
Body: &map[string]interface{}{
"category_name": categoryName,
},
}.TestOnStatusAndDBKeepDB(t, nil,
DBTesterWithStatus{
Status: 201,
DBTester: func(app TestApp, assert *assert.A, resp *http.Response) {

assert.Equal(201, resp.StatusCode)
var respCategory models.Category

var respCategory models.Category
err := json.NewDecoder(resp.Body).Decode(&respCategory)

err := json.NewDecoder(resp.Body).Decode(&respCategory)
assert.NilError(err)

assert.NilError(err)
dbCategory, err := transactions.GetCategory(app.Conn, respCategory.ID)

dbCategory, err := transactions.GetCategory(app.Conn, respCategory.ID)
assert.NilError(err)

assert.NilError(err)
assert.Equal(dbCategory, &respCategory)
},
},
)
}

assert.Equal(dbCategory, &respCategory)
func TestCreateCategoryWorks(t *testing.T) {
appAssert := CreateSampleCategory(t, "Science")
appAssert.App.DropDB()
}

func TestCreateCategoryIgnoresid(t *testing.T) {
app, assert, resp := RequestTesterWithJSONBody(t, "POST", "/api/v1/categories/", &map[string]interface{}{
"id": 12,
"category_name": "Science",
}, nil, nil, nil)
defer app.DropDB()

var respCategory models.Category

err := json.NewDecoder(resp.Body).Decode(&respCategory)

assert.NilError(err)

dbCategory, err := transactions.GetCategory(app.Conn, respCategory.ID)

assert.NilError(err)

assert.NotEqual(12, dbCategory.ID)
TestRequest{
Method: "POST",
Path: "/api/v1/categories/",
Body: &map[string]interface{}{
"id": 12,
"category_name": "Science",
},
}.TestOnStatusAndDB(t, nil,
DBTesterWithStatus{
Status: 201,
DBTester: func(app TestApp, assert *assert.A, resp *http.Response) {
var respCategory models.Category

err := json.NewDecoder(resp.Body).Decode(&respCategory)

assert.NilError(err)

dbCategory, err := transactions.GetCategory(app.Conn, respCategory.ID)

assert.NilError(err)

assert.NotEqual(12, dbCategory.ID)
},
},
)
}

func TestCreateCategoryFailsIfNameIsNotString(t *testing.T) {
app, assert, resp := RequestTesterWithJSONBody(t, "POST", "/api/v1/categories/", &map[string]interface{}{
"category_name": 1231,
}, nil, nil, nil)
defer app.DropDB()

defer resp.Body.Close()

bodyBytes, err := io.ReadAll(resp.Body)

assert.NilError(err)

msg := string(bodyBytes)

assert.Equal("failed to process the request", msg)

assert.Equal(400, resp.StatusCode)
TestRequest{
Method: "POST",
Path: "/api/v1/categories/",
Body: &map[string]interface{}{
"category_name": 1231,
},
}.TestOnStatusAndMessage(t, nil,
MessageWithStatus{
Status: 400,
Message: "failed to process the request",
})
}

func TestCreateCategoryFailsIfNameIsMissing(t *testing.T) {
app, assert, resp := RequestTesterWithJSONBody(t, "POST", "/api/v1/categories/", &map[string]interface{}{}, nil, nil, nil)
defer app.DropDB()

defer resp.Body.Close()

bodyBytes, err := io.ReadAll(resp.Body)

assert.NilError(err)

msg := string(bodyBytes)

assert.Equal("failed to validate the data", msg)

assert.Equal(400, resp.StatusCode)
TestRequest{
Method: "POST",
Path: "/api/v1/categories/",
Body: &map[string]interface{}{},
}.TestOnStatusAndMessage(t, nil,
MessageWithStatus{
Status: 400,
Message: "failed to validate the data",
},
)
}

func TestCreateCategoryFailsIfCategoryWithThatNameAlreadyExists(t *testing.T) {
categoryName := "Science"
app, assert, resp := RequestTesterWithJSONBody(t, "POST", "/api/v1/categories/", &map[string]interface{}{
"category_name": categoryName,
}, nil, nil, nil)

assert.Equal(201, resp.StatusCode)

var respCategory models.Category

err := json.NewDecoder(resp.Body).Decode(&respCategory)

assert.NilError(err)

dbCategory, err := transactions.GetCategory(app.Conn, respCategory.ID)

assert.NilError(err)

assert.Equal(dbCategory, &respCategory)
existingAppAssert := CreateSampleCategory(t, categoryName)

for _, permutation := range AllCasingPermutations(categoryName) {
_, _, resp = RequestTesterWithJSONBody(t, "POST", "/api/v1/categories/", &map[string]interface{}{
"category_name": permutation,
}, nil, &app, assert)

defer resp.Body.Close()

bodyBytes, err := io.ReadAll(resp.Body)

assert.NilError(err)

msg := string(bodyBytes)

assert.Equal("category with that name already exists", msg)

assert.Equal(400, resp.StatusCode)
fmt.Println(permutation)
TestRequest{
Method: "POST",
Path: "/api/v1/categories/",
Body: &map[string]interface{}{
"category_name": permutation,
},
}.TestOnStatusAndMessageKeepDB(t, &existingAppAssert,
MessageWithStatus{
Status: 400,
Message: "category with that name already exists",
},
)
}

existingAppAssert.App.DropDB()
}
11 changes: 6 additions & 5 deletions backend/tests/api/health_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import (
)

func TestHealthWorks(t *testing.T) {
app, assert, resp := RequestTester(t, "GET", "/health", nil, nil, nil, nil)

defer app.DropDB()

assert.Equal(200, resp.StatusCode)
TestRequest{
Method: "GET",
Path: "/health",
}.TestOnStatus(t, nil,
200,
)
}
117 changes: 97 additions & 20 deletions backend/tests/api/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
crand "crypto/rand"
"fmt"
"io"
"math/big"
"net"
"net/http"
Expand Down Expand Up @@ -133,37 +134,43 @@ func (app TestApp) DropDB() {
app.Conn.Exec(fmt.Sprintf("DROP DATABASE %s;", app.Settings.Database.DatabaseName))
}

type ExistingAppAssert struct {
App TestApp
Assert *assert.A
}

type TestRequest struct {
TestApp TestApp
Assert *assert.A
Resp *http.Response
Method string
Path string
Body *map[string]interface{}
Headers *map[string]string
}

func RequestTester(t *testing.T, method string, path string, body *map[string]interface{}, headers *map[string]string, exisitingApp *TestApp, exisitingAssert *assert.A) (TestApp, *assert.A, *http.Response) {
func (request TestRequest) Test(t *testing.T, existingAppAssert *ExistingAppAssert) (ExistingAppAssert, *http.Response) {
var app TestApp
var assert *assert.A

if exisitingApp == nil || exisitingAssert == nil {
if existingAppAssert == nil {
app, assert = InitTest(t)
} else {
app, assert = *exisitingApp, exisitingAssert
app, assert = existingAppAssert.App, existingAppAssert.Assert
}

address := fmt.Sprintf("%s%s", app.Address, path)
address := fmt.Sprintf("%s%s", app.Address, request.Path)

var req *http.Request

if body == nil {
req = httptest.NewRequest(method, address, nil)
if request.Body == nil {
req = httptest.NewRequest(request.Method, address, nil)
} else {
bodyBytes, err := json.Marshal(body)
bodyBytes, err := json.Marshal(request.Body)

assert.NilError(err)

req = httptest.NewRequest(method, address, bytes.NewBuffer(bodyBytes))
req = httptest.NewRequest(request.Method, address, bytes.NewBuffer(bodyBytes))

if headers != nil {
for key, value := range *headers {
if request.Headers != nil {
for key, value := range *request.Headers {
req.Header.Set(key, value)
}
}
Expand All @@ -173,17 +180,87 @@ func RequestTester(t *testing.T, method string, path string, body *map[string]in

assert.NilError(err)

return app, assert, resp
return ExistingAppAssert{
App: app,
Assert: assert,
}, resp
}

func (request TestRequest) TestOnStatus(t *testing.T, existingAppAssert *ExistingAppAssert, status int) ExistingAppAssert {
appAssert, resp := request.Test(t, existingAppAssert)
app, assert := appAssert.App, appAssert.Assert
if existingAppAssert != nil {
defer app.DropDB()
}

assert.Equal(status, resp.StatusCode)

return appAssert
}

func RequestTesterWithJSONBody(t *testing.T, method string, path string, body *map[string]interface{}, headers *map[string]string, exisitingApp *TestApp, exisitingAssert *assert.A) (TestApp, *assert.A, *http.Response) {
if headers == nil {
headers = &map[string]string{"Content-Type": "application/json"}
} else if _, ok := (*headers)["Content-Type"]; !ok {
(*headers)["Content-Type"] = "application/json"
func (request TestRequest) TestWithJSONBody(t *testing.T, existingAppAssert *ExistingAppAssert) (ExistingAppAssert, *http.Response) {
if request.Headers == nil {
request.Headers = &map[string]string{"Content-Type": "application/json"}
} else if _, ok := (*request.Headers)["Content-Type"]; !ok {
(*request.Headers)["Content-Type"] = "application/json"
}

return RequestTester(t, method, path, body, headers, exisitingApp, exisitingAssert)
return request.Test(t, existingAppAssert)
}

type MessageWithStatus struct {
Status int
Message string
}

func (request TestRequest) TestOnStatusAndMessage(t *testing.T, existingAppAssert *ExistingAppAssert, messagedStatus MessageWithStatus) ExistingAppAssert {
appAssert := request.TestOnStatusAndMessageKeepDB(t, existingAppAssert, messagedStatus)
appAssert.App.DropDB()
return appAssert
}

func (request TestRequest) TestOnStatusAndMessageKeepDB(t *testing.T, existingAppAssert *ExistingAppAssert, messagedStatus MessageWithStatus) ExistingAppAssert {
appAssert, resp := request.TestWithJSONBody(t, existingAppAssert)
assert := appAssert.Assert

defer resp.Body.Close()

bodyBytes, err := io.ReadAll(resp.Body)

appAssert.Assert.NilError(err)

msg := string(bodyBytes)

assert.Equal(messagedStatus.Message, msg)

assert.Equal(messagedStatus.Status, resp.StatusCode)

return appAssert
}

type DBTester func(app TestApp, assert *assert.A, resp *http.Response)

type DBTesterWithStatus struct {
Status int
DBTester
}

func (request TestRequest) TestOnStatusAndDB(t *testing.T, existingAppAssert *ExistingAppAssert, dbTesterStatus DBTesterWithStatus) ExistingAppAssert {
appAssert := request.TestOnStatusAndDBKeepDB(t, existingAppAssert, dbTesterStatus)
appAssert.App.DropDB()
return appAssert
}

func (request TestRequest) TestOnStatusAndDBKeepDB(t *testing.T, existingAppAssert *ExistingAppAssert, dbTesterStatus DBTesterWithStatus) ExistingAppAssert {
appAssert, resp := request.TestWithJSONBody(t, existingAppAssert)
app, assert := appAssert.App, appAssert.Assert
defer resp.Body.Close()

assert.Equal(dbTesterStatus.Status, resp.StatusCode)

dbTesterStatus.DBTester(app, assert, resp)

return appAssert
}

func generateCasingPermutations(word string, currentPermutation string, index int, results *[]string) {
Expand Down
Loading

0 comments on commit 0930342

Please sign in to comment.