From ba564eacc75bb6d5be24a30396bb717ab175758a Mon Sep 17 00:00:00 2001 From: garrettladley Date: Mon, 15 Jan 2024 20:36:59 -0500 Subject: [PATCH 01/11] init --- backend/src/controllers/tag.go | 49 +++++++++++++++++++++++++++++++++ backend/src/server/server.go | 9 ++++++ backend/src/services/tag.go | 20 ++++++++++++++ backend/src/transactions/tag.go | 25 +++++++++++++++++ backend/tests/api/tag_test.go | 46 +++++++++++++++++++++++++++++++ 5 files changed, 149 insertions(+) create mode 100644 backend/src/controllers/tag.go create mode 100644 backend/src/services/tag.go create mode 100644 backend/src/transactions/tag.go create mode 100644 backend/tests/api/tag_test.go diff --git a/backend/src/controllers/tag.go b/backend/src/controllers/tag.go new file mode 100644 index 000000000..0cefe25e8 --- /dev/null +++ b/backend/src/controllers/tag.go @@ -0,0 +1,49 @@ +package controllers + +import ( + "backend/src/models" + "backend/src/services" + "backend/src/utilities" + + "github.com/gofiber/fiber/v2" +) + +type TagController struct { + tagService services.TagServiceInterface +} + +func NewTagController(tagService services.TagServiceInterface) *TagController { + return &TagController{tagService: tagService} +} + +// CreateTag godoc +// +// @Summary Creates a tag +// @Description Creates a tag +// @ID create-tag +// @Tags tag +// @Accept json +// @Produce json +// @Success 200 {object} string +// @Failure 400 {string} string "Failed to process the request" +// @Failure 400 {string} string "Failed to validate the data" +// @Failure 400 {string} string "Failed to create tag" +// @Router /api/v1/tags/create [post] +func (t *TagController) CreateTag(c *fiber.Ctx) error { + var tag models.Tag + + if err := c.BodyParser(&tag); err != nil { + return fiber.NewError(fiber.StatusBadRequest, "Failed to process the request") + } + + if err := utilities.ValidateData(c, tag); err != nil { + return fiber.NewError(fiber.StatusBadRequest, "Failed to validate the data") + } + + tag, err := t.tagService.CreateTag(tag) + if err != nil { + return fiber.NewError(fiber.StatusBadRequest, "Failed to create tag") + } + + return c.Status(fiber.StatusOK).JSON(tag) +} diff --git a/backend/src/server/server.go b/backend/src/server/server.go index 7785d1e98..ff6add9ef 100644 --- a/backend/src/server/server.go +++ b/backend/src/server/server.go @@ -30,6 +30,7 @@ func Init(db *gorm.DB) *fiber.App { apiv1 := app.Group("/api/v1") userRoutes(apiv1, &services.UserService{DB: db}) + tagRoutes(apiv1, &services.TagService{DB: db}) return app } @@ -63,3 +64,11 @@ func userRoutes(router fiber.Router, userService services.UserServiceInterface) users.Get("/", userController.GetAllUsers) } + +func tagRoutes(router fiber.Router, tagService services.TagServiceInterface) { + tagController := controllers.NewTagController(tagService) + + tags := router.Group("/tags") + + tags.Post("/create", tagController.CreateTag) +} diff --git a/backend/src/services/tag.go b/backend/src/services/tag.go new file mode 100644 index 000000000..9529f6355 --- /dev/null +++ b/backend/src/services/tag.go @@ -0,0 +1,20 @@ +package services + +import ( + "backend/src/models" + "backend/src/transactions" + + "gorm.io/gorm" +) + +type TagServiceInterface interface { + CreateTag(tag models.Tag) (models.Tag, error) +} + +type TagService struct { + DB *gorm.DB +} + +func (t *TagService) CreateTag(tag models.Tag) (models.Tag, error) { + return transactions.CreateTag(t.DB, tag) +} diff --git a/backend/src/transactions/tag.go b/backend/src/transactions/tag.go new file mode 100644 index 000000000..ac2175a58 --- /dev/null +++ b/backend/src/transactions/tag.go @@ -0,0 +1,25 @@ +package transactions + +import ( + "backend/src/models" + + "gorm.io/gorm" +) + +func CreateTag(db *gorm.DB, tag models.Tag) (models.Tag, error) { + if err := db.Create(&tag).Error; err != nil { + return models.Tag{}, err + } + + return tag, nil +} + +func GetTag(db *gorm.DB, id uint) (models.Tag, error) { + var tag models.Tag + + if err := db.First(&tag, id).Error; err != nil { + return models.Tag{}, err + } + + return tag, nil +} diff --git a/backend/tests/api/tag_test.go b/backend/tests/api/tag_test.go new file mode 100644 index 000000000..8c6cac745 --- /dev/null +++ b/backend/tests/api/tag_test.go @@ -0,0 +1,46 @@ +package tests + +import ( + "backend/src/models" + "backend/src/transactions" + "bytes" + "fmt" + "net/http/httptest" + "testing" + + "github.com/goccy/go-json" +) + +func TestCreateTagWorks(t *testing.T) { + app, assert := InitTest(t) + + data := map[string]interface{}{ + "name": "Generate", + "category_id": 1, + } + + body, err := json.Marshal(data) + + assert.NilError(err) + + req := httptest.NewRequest("POST", fmt.Sprintf("%s/api/v1/tags/create", app.Address), bytes.NewBuffer(body)) + req.Header.Set("Content-Type", "application/json") + + resp, err := app.App.Test(req) + + assert.NilError(err) + + assert.Equal(200, resp.StatusCode) + + var respTag models.Tag + + err = json.NewDecoder(resp.Body).Decode(&respTag) + + assert.NilError(err) + + dbTag, err := transactions.GetTag(app.Conn, respTag.ID) + + assert.NilError(err) + + assert.Equal(dbTag, respTag) +} From 6127a86dd124d25c865dbbbc282fad975ee10ca7 Mon Sep 17 00:00:00 2001 From: garrettladley Date: Mon, 15 Jan 2024 20:51:40 -0500 Subject: [PATCH 02/11] failure tests | misc fixes --- backend/src/controllers/tag.go | 4 +- backend/src/models/tag.go | 2 +- backend/tests/api/tag_test.go | 72 ++++++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+), 3 deletions(-) diff --git a/backend/src/controllers/tag.go b/backend/src/controllers/tag.go index 0cefe25e8..7c41a7081 100644 --- a/backend/src/controllers/tag.go +++ b/backend/src/controllers/tag.go @@ -27,7 +27,7 @@ func NewTagController(tagService services.TagServiceInterface) *TagController { // @Success 200 {object} string // @Failure 400 {string} string "Failed to process the request" // @Failure 400 {string} string "Failed to validate the data" -// @Failure 400 {string} string "Failed to create tag" +// @Failure 500 {string} string "Failed to create tag" // @Router /api/v1/tags/create [post] func (t *TagController) CreateTag(c *fiber.Ctx) error { var tag models.Tag @@ -42,7 +42,7 @@ func (t *TagController) CreateTag(c *fiber.Ctx) error { tag, err := t.tagService.CreateTag(tag) if err != nil { - return fiber.NewError(fiber.StatusBadRequest, "Failed to create tag") + return fiber.NewError(fiber.StatusInternalServerError, "Failed to create tag") } return c.Status(fiber.StatusOK).JSON(tag) diff --git a/backend/src/models/tag.go b/backend/src/models/tag.go index 142b04727..54b01b792 100644 --- a/backend/src/models/tag.go +++ b/backend/src/models/tag.go @@ -9,7 +9,7 @@ type Tag struct { Name string `gorm:"type:varchar(255)" json:"name" validate:"required"` - CategoryID uint `gorm:"foreignKey:CategoryID" json:"category_id" validate:"-"` + CategoryID uint `gorm:"foreignKey:CategoryID" json:"category_id" validate:"required"` User []User `gorm:"many2many:user_tags;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"users" validate:"-"` Club []Club `gorm:"many2many:club_tags;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"clubs" validate:"-"` diff --git a/backend/tests/api/tag_test.go b/backend/tests/api/tag_test.go index 8c6cac745..9ab3405d9 100644 --- a/backend/tests/api/tag_test.go +++ b/backend/tests/api/tag_test.go @@ -5,6 +5,7 @@ import ( "backend/src/transactions" "bytes" "fmt" + "io" "net/http/httptest" "testing" @@ -44,3 +45,74 @@ func TestCreateTagWorks(t *testing.T) { assert.Equal(dbTag, respTag) } + +func TestCreateTagFailsBadRequest(t *testing.T) { + app, assert := InitTest(t) + + badReqs := []map[string]interface{}{ + { + "name": "Generate", + "category_id": "1", + }, + { + "name": 1, + "category_id": 1, + }, + } + + for _, badReq := range badReqs { + body, err := json.Marshal(badReq) + + assert.NilError(err) + + req := httptest.NewRequest("POST", fmt.Sprintf("%s/api/v1/tags/create", app.Address), bytes.NewBuffer(body)) + req.Header.Set("Content-Type", "application/json") + + resp, err := app.App.Test(req) + + assert.NilError(err) + + assert.Equal(400, resp.StatusCode) + + body, err = io.ReadAll(resp.Body) + + assert.NilError(err) + + assert.Equal("Failed to process the request", string(body)) + } +} + +func TestCreateTagFailsValidation(t *testing.T) { + app, assert := InitTest(t) + + badReqs := []map[string]interface{}{ + { + "name": "Generate", + }, + { + "category_id": 1, + }, + {}, + } + + for _, badReq := range badReqs { + body, err := json.Marshal(badReq) + + assert.NilError(err) + + req := httptest.NewRequest("POST", fmt.Sprintf("%s/api/v1/tags/create", app.Address), bytes.NewBuffer(body)) + req.Header.Set("Content-Type", "application/json") + + resp, err := app.App.Test(req) + + assert.NilError(err) + + assert.Equal(400, resp.StatusCode) + + body, err = io.ReadAll(resp.Body) + + assert.NilError(err) + + assert.Equal("Failed to validate the data", string(body)) + } +} From 1f7eedfa849ac090548499a05ae1ef7830455278 Mon Sep 17 00:00:00 2001 From: garrettladley Date: Tue, 16 Jan 2024 22:43:50 -0500 Subject: [PATCH 03/11] addressed review --- backend/src/controllers/tag.go | 8 ++++---- backend/src/server/server.go | 2 +- backend/src/services/tag.go | 4 ++-- backend/src/transactions/tag.go | 6 +++--- backend/tests/api/tag_test.go | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/backend/src/controllers/tag.go b/backend/src/controllers/tag.go index 7c41a7081..234c92e0f 100644 --- a/backend/src/controllers/tag.go +++ b/backend/src/controllers/tag.go @@ -24,11 +24,11 @@ func NewTagController(tagService services.TagServiceInterface) *TagController { // @Tags tag // @Accept json // @Produce json -// @Success 200 {object} string +// @Success 201 {object} models.Tag // @Failure 400 {string} string "Failed to process the request" // @Failure 400 {string} string "Failed to validate the data" // @Failure 500 {string} string "Failed to create tag" -// @Router /api/v1/tags/create [post] +// @Router /api/v1/tags/ [post] func (t *TagController) CreateTag(c *fiber.Ctx) error { var tag models.Tag @@ -40,10 +40,10 @@ func (t *TagController) CreateTag(c *fiber.Ctx) error { return fiber.NewError(fiber.StatusBadRequest, "Failed to validate the data") } - tag, err := t.tagService.CreateTag(tag) + dbTag, err := t.tagService.CreateTag(tag) if err != nil { return fiber.NewError(fiber.StatusInternalServerError, "Failed to create tag") } - return c.Status(fiber.StatusOK).JSON(tag) + return c.Status(fiber.StatusCreated).JSON(&dbTag) } diff --git a/backend/src/server/server.go b/backend/src/server/server.go index ff6add9ef..b0d3b7214 100644 --- a/backend/src/server/server.go +++ b/backend/src/server/server.go @@ -70,5 +70,5 @@ func tagRoutes(router fiber.Router, tagService services.TagServiceInterface) { tags := router.Group("/tags") - tags.Post("/create", tagController.CreateTag) + tags.Post("/", tagController.CreateTag) } diff --git a/backend/src/services/tag.go b/backend/src/services/tag.go index 9529f6355..9aba5e773 100644 --- a/backend/src/services/tag.go +++ b/backend/src/services/tag.go @@ -8,13 +8,13 @@ import ( ) type TagServiceInterface interface { - CreateTag(tag models.Tag) (models.Tag, error) + CreateTag(tag models.Tag) (*models.Tag, error) } type TagService struct { DB *gorm.DB } -func (t *TagService) CreateTag(tag models.Tag) (models.Tag, error) { +func (t *TagService) CreateTag(tag models.Tag) (*models.Tag, error) { return transactions.CreateTag(t.DB, tag) } diff --git a/backend/src/transactions/tag.go b/backend/src/transactions/tag.go index ac2175a58..d7597fe39 100644 --- a/backend/src/transactions/tag.go +++ b/backend/src/transactions/tag.go @@ -6,12 +6,12 @@ import ( "gorm.io/gorm" ) -func CreateTag(db *gorm.DB, tag models.Tag) (models.Tag, error) { +func CreateTag(db *gorm.DB, tag models.Tag) (*models.Tag, error) { if err := db.Create(&tag).Error; err != nil { - return models.Tag{}, err + return nil, err } - return tag, nil + return &tag, nil } func GetTag(db *gorm.DB, id uint) (models.Tag, error) { diff --git a/backend/tests/api/tag_test.go b/backend/tests/api/tag_test.go index 9ab3405d9..27bdb5609 100644 --- a/backend/tests/api/tag_test.go +++ b/backend/tests/api/tag_test.go @@ -31,7 +31,7 @@ func TestCreateTagWorks(t *testing.T) { assert.NilError(err) - assert.Equal(200, resp.StatusCode) + assert.Equal(201, resp.StatusCode) var respTag models.Tag From 4659deb0d5526abfb9489d26f86d782da8b22bef Mon Sep 17 00:00:00 2001 From: garrettladley Date: Tue, 16 Jan 2024 22:49:45 -0500 Subject: [PATCH 04/11] update tests --- backend/tests/api/tag_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/tests/api/tag_test.go b/backend/tests/api/tag_test.go index 27bdb5609..ac57c7cbe 100644 --- a/backend/tests/api/tag_test.go +++ b/backend/tests/api/tag_test.go @@ -24,7 +24,7 @@ func TestCreateTagWorks(t *testing.T) { assert.NilError(err) - req := httptest.NewRequest("POST", fmt.Sprintf("%s/api/v1/tags/create", app.Address), bytes.NewBuffer(body)) + req := httptest.NewRequest("POST", fmt.Sprintf("%s/api/v1/tags/", app.Address), bytes.NewBuffer(body)) req.Header.Set("Content-Type", "application/json") resp, err := app.App.Test(req) @@ -65,7 +65,7 @@ func TestCreateTagFailsBadRequest(t *testing.T) { assert.NilError(err) - req := httptest.NewRequest("POST", fmt.Sprintf("%s/api/v1/tags/create", app.Address), bytes.NewBuffer(body)) + req := httptest.NewRequest("POST", fmt.Sprintf("%s/api/v1/tags/", app.Address), bytes.NewBuffer(body)) req.Header.Set("Content-Type", "application/json") resp, err := app.App.Test(req) @@ -100,7 +100,7 @@ func TestCreateTagFailsValidation(t *testing.T) { assert.NilError(err) - req := httptest.NewRequest("POST", fmt.Sprintf("%s/api/v1/tags/create", app.Address), bytes.NewBuffer(body)) + req := httptest.NewRequest("POST", fmt.Sprintf("%s/api/v1/tags/", app.Address), bytes.NewBuffer(body)) req.Header.Set("Content-Type", "application/json") resp, err := app.App.Test(req) From ae5e6bdda2efb94eb24fc1d85f0e7c1a81ca1aad Mon Sep 17 00:00:00 2001 From: garrettladley Date: Thu, 18 Jan 2024 15:01:19 -0500 Subject: [PATCH 05/11] defer expensive transaction creation until necessary --- backend/src/database/db.go | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/backend/src/database/db.go b/backend/src/database/db.go index 1f91265a7..48954e10d 100644 --- a/backend/src/database/db.go +++ b/backend/src/database/db.go @@ -55,12 +55,6 @@ func MigrateDB(settings config.Settings, db *gorm.DB) error { return err } - tx := db.Begin() - - if err := tx.Error; err != nil { - return err - } - superUser := models.User{ Role: models.Super, NUID: "000000000", @@ -74,7 +68,13 @@ func MigrateDB(settings config.Settings, db *gorm.DB) error { var user models.User - if err := tx.Where("nuid = ?", superUser.NUID).First(&user).Error; err != nil { + if err := db.Where("nuid = ?", superUser.NUID).First(&user).Error; err != nil { + tx := db.Begin() + + if err := tx.Error; err != nil { + return err + } + if err := tx.Create(&superUser).Error; err != nil { tx.Rollback() return err @@ -99,7 +99,9 @@ func MigrateDB(settings config.Settings, db *gorm.DB) error { tx.Rollback() return err } - } - return tx.Commit().Error + return tx.Commit().Error + + } + return nil } From 5e70ee7e682d133c016777f4722132cc1389ef34 Mon Sep 17 00:00:00 2001 From: garrettladley Date: Thu, 18 Jan 2024 19:56:27 -0500 Subject: [PATCH 06/11] ALDER + GML CHANGES --- backend/src/controllers/tag.go | 21 +++++++++++---------- backend/src/models/tag.go | 5 +++++ backend/src/services/tag.go | 6 ++++++ backend/src/transactions/tag.go | 15 ++++++++++----- backend/src/utilities/validator.go | 3 +-- 5 files changed, 33 insertions(+), 17 deletions(-) diff --git a/backend/src/controllers/tag.go b/backend/src/controllers/tag.go index 234c92e0f..6047883bf 100644 --- a/backend/src/controllers/tag.go +++ b/backend/src/controllers/tag.go @@ -3,7 +3,6 @@ package controllers import ( "backend/src/models" "backend/src/services" - "backend/src/utilities" "github.com/gofiber/fiber/v2" ) @@ -25,24 +24,26 @@ func NewTagController(tagService services.TagServiceInterface) *TagController { // @Accept json // @Produce json // @Success 201 {object} models.Tag -// @Failure 400 {string} string "Failed to process the request" -// @Failure 400 {string} string "Failed to validate the data" -// @Failure 500 {string} string "Failed to create tag" +// @Failure 400 {string} string "failed to process the request" +// @Failure 400 {string} string "failed to validate the data" +// @Failure 500 {string} string "failed to create tag" // @Router /api/v1/tags/ [post] func (t *TagController) CreateTag(c *fiber.Ctx) error { - var tag models.Tag + var tag models.TagCreateRequestBody if err := c.BodyParser(&tag); err != nil { - return fiber.NewError(fiber.StatusBadRequest, "Failed to process the request") + return fiber.NewError(fiber.StatusBadRequest, "failed to process the request") } - if err := utilities.ValidateData(c, tag); err != nil { - return fiber.NewError(fiber.StatusBadRequest, "Failed to validate the data") + partialTag := models.Tag{ + Name: tag.Name, + CategoryID: tag.CategoryID, } - dbTag, err := t.tagService.CreateTag(tag) + dbTag, err := t.tagService.CreateTag(partialTag) + if err != nil { - return fiber.NewError(fiber.StatusInternalServerError, "Failed to create tag") + return err } return c.Status(fiber.StatusCreated).JSON(&dbTag) diff --git a/backend/src/models/tag.go b/backend/src/models/tag.go index 54b01b792..4aa48fbcf 100644 --- a/backend/src/models/tag.go +++ b/backend/src/models/tag.go @@ -15,3 +15,8 @@ type Tag struct { Club []Club `gorm:"many2many:club_tags;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"clubs" validate:"-"` Event []Event `gorm:"many2many:event_tags;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"events" validate:"-"` } + +type TagCreateRequestBody struct { + Name string `json:"name" validate:"required"` + CategoryID uint `json:"category_id" validate:"required"` +} diff --git a/backend/src/services/tag.go b/backend/src/services/tag.go index 9aba5e773..99fd7eed0 100644 --- a/backend/src/services/tag.go +++ b/backend/src/services/tag.go @@ -3,7 +3,9 @@ package services import ( "backend/src/models" "backend/src/transactions" + "backend/src/utilities" + "github.com/gofiber/fiber/v2" "gorm.io/gorm" ) @@ -16,5 +18,9 @@ type TagService struct { } func (t *TagService) CreateTag(tag models.Tag) (*models.Tag, error) { + if err := utilities.ValidateData(tag); err != nil { + return nil, fiber.NewError(fiber.StatusBadRequest, "failed to validate the data") + } + return transactions.CreateTag(t.DB, tag) } diff --git a/backend/src/transactions/tag.go b/backend/src/transactions/tag.go index d7597fe39..9b90b21fa 100644 --- a/backend/src/transactions/tag.go +++ b/backend/src/transactions/tag.go @@ -2,24 +2,29 @@ package transactions import ( "backend/src/models" + "errors" + "github.com/gofiber/fiber/v2" "gorm.io/gorm" ) func CreateTag(db *gorm.DB, tag models.Tag) (*models.Tag, error) { if err := db.Create(&tag).Error; err != nil { - return nil, err + return nil, fiber.NewError(fiber.StatusInternalServerError, "failed to create tag") } - return &tag, nil } -func GetTag(db *gorm.DB, id uint) (models.Tag, error) { +func GetTag(db *gorm.DB, id uint) (*models.Tag, error) { var tag models.Tag if err := db.First(&tag, id).Error; err != nil { - return models.Tag{}, err + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, fiber.NewError(fiber.StatusBadRequest, "invalid tag id") + } else { + return nil, fiber.NewError(fiber.StatusInternalServerError, "unable to retrieve tag") + } } - return tag, nil + return &tag, nil } diff --git a/backend/src/utilities/validator.go b/backend/src/utilities/validator.go index a6dd65bbe..770097268 100644 --- a/backend/src/utilities/validator.go +++ b/backend/src/utilities/validator.go @@ -2,11 +2,10 @@ package utilities import ( "github.com/go-playground/validator/v10" - "github.com/gofiber/fiber/v2" ) // Validate the data sent to the server if the data is invalid, return an error otherwise, return nil -func ValidateData(c *fiber.Ctx, model interface{}) error { +func ValidateData(model interface{}) error { validate := validator.New(validator.WithRequiredStructEnabled()) if err := validate.Struct(model); err != nil { return err From f7622e825f40adf3c838537b8970af12efaebd09 Mon Sep 17 00:00:00 2001 From: garrettladley Date: Thu, 18 Jan 2024 20:05:45 -0500 Subject: [PATCH 07/11] defer resp.Body.Close() --- backend/tests/api/tag_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/backend/tests/api/tag_test.go b/backend/tests/api/tag_test.go index ac57c7cbe..21bd1ba8b 100644 --- a/backend/tests/api/tag_test.go +++ b/backend/tests/api/tag_test.go @@ -74,6 +74,8 @@ func TestCreateTagFailsBadRequest(t *testing.T) { assert.Equal(400, resp.StatusCode) + defer resp.Body.Close() + body, err = io.ReadAll(resp.Body) assert.NilError(err) @@ -109,6 +111,8 @@ func TestCreateTagFailsValidation(t *testing.T) { assert.Equal(400, resp.StatusCode) + defer resp.Body.Close() + body, err = io.ReadAll(resp.Body) assert.NilError(err) From 3e9945a1234f21e87071715c55ea67397908b48d Mon Sep 17 00:00:00 2001 From: garrettladley Date: Sat, 20 Jan 2024 15:12:00 -0500 Subject: [PATCH 08/11] clean up | testing revolution --- backend/src/controllers/tag.go | 15 ++-- backend/src/models/tag.go | 2 +- backend/src/services/tag.go | 16 ++-- backend/src/transactions/tag.go | 3 +- backend/tests/api/category_test.go | 54 +++++++++--- backend/tests/api/helpers.go | 22 ++++- backend/tests/api/tag_test.go | 137 +++++++++++++---------------- 7 files changed, 139 insertions(+), 110 deletions(-) diff --git a/backend/src/controllers/tag.go b/backend/src/controllers/tag.go index 6047883bf..3d47bdac0 100644 --- a/backend/src/controllers/tag.go +++ b/backend/src/controllers/tag.go @@ -1,8 +1,8 @@ package controllers import ( - "backend/src/models" - "backend/src/services" + "github.com/GenerateNU/sac/backend/src/models" + "github.com/GenerateNU/sac/backend/src/services" "github.com/gofiber/fiber/v2" ) @@ -29,18 +29,13 @@ func NewTagController(tagService services.TagServiceInterface) *TagController { // @Failure 500 {string} string "failed to create tag" // @Router /api/v1/tags/ [post] func (t *TagController) CreateTag(c *fiber.Ctx) error { - var tag models.TagCreateRequestBody + var tagBody models.CreateTagRequestBody - if err := c.BodyParser(&tag); err != nil { + if err := c.BodyParser(&tagBody); err != nil { return fiber.NewError(fiber.StatusBadRequest, "failed to process the request") } - partialTag := models.Tag{ - Name: tag.Name, - CategoryID: tag.CategoryID, - } - - dbTag, err := t.tagService.CreateTag(partialTag) + dbTag, err := t.tagService.CreateTag(tagBody) if err != nil { return err diff --git a/backend/src/models/tag.go b/backend/src/models/tag.go index d38246bdd..bcb48a96d 100644 --- a/backend/src/models/tag.go +++ b/backend/src/models/tag.go @@ -16,7 +16,7 @@ type Tag struct { Event []Event `gorm:"many2many:event_tags;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"-" validate:"-"` } -type TagCreateRequestBody struct { +type CreateTagRequestBody struct { Name string `json:"name" validate:"required"` CategoryID uint `json:"category_id" validate:"required"` } diff --git a/backend/src/services/tag.go b/backend/src/services/tag.go index 99fd7eed0..a6093bdb5 100644 --- a/backend/src/services/tag.go +++ b/backend/src/services/tag.go @@ -1,23 +1,27 @@ package services import ( - "backend/src/models" - "backend/src/transactions" - "backend/src/utilities" - + "github.com/GenerateNU/sac/backend/src/models" + "github.com/GenerateNU/sac/backend/src/transactions" + "github.com/GenerateNU/sac/backend/src/utilities" "github.com/gofiber/fiber/v2" "gorm.io/gorm" ) type TagServiceInterface interface { - CreateTag(tag models.Tag) (*models.Tag, error) + CreateTag(partialTag models.CreateTagRequestBody) (*models.Tag, error) } type TagService struct { DB *gorm.DB } -func (t *TagService) CreateTag(tag models.Tag) (*models.Tag, error) { +func (t *TagService) CreateTag(partialTag models.CreateTagRequestBody) (*models.Tag, error) { + tag := models.Tag{ + Name: partialTag.Name, + CategoryID: partialTag.CategoryID, + } + if err := utilities.ValidateData(tag); err != nil { return nil, fiber.NewError(fiber.StatusBadRequest, "failed to validate the data") } diff --git a/backend/src/transactions/tag.go b/backend/src/transactions/tag.go index 9b90b21fa..ec261aa7e 100644 --- a/backend/src/transactions/tag.go +++ b/backend/src/transactions/tag.go @@ -1,9 +1,10 @@ package transactions import ( - "backend/src/models" "errors" + "github.com/GenerateNU/sac/backend/src/models" + "github.com/gofiber/fiber/v2" "gorm.io/gorm" ) diff --git a/backend/tests/api/category_test.go b/backend/tests/api/category_test.go index 4119dc3cb..b552fa244 100644 --- a/backend/tests/api/category_test.go +++ b/backend/tests/api/category_test.go @@ -73,6 +73,20 @@ func TestCreateCategoryIgnoresid(t *testing.T) { ) } +func AssertNoCategoryCreation(app TestApp, assert *assert.A, resp *http.Response) { + AssertNumCategoriesRemainsAtN(app, assert, resp, 0) +} + +func AssertNumCategoriesRemainsAtN(app TestApp, assert *assert.A, resp *http.Response, n int) { + var categories []models.Category + + err := app.Conn.Find(&categories).Error + + assert.NilError(err) + + assert.Equal(n, len(categories)) +} + func TestCreateCategoryFailsIfNameIsNotString(t *testing.T) { TestRequest{ Method: "POST", @@ -80,11 +94,15 @@ func TestCreateCategoryFailsIfNameIsNotString(t *testing.T) { Body: &map[string]interface{}{ "category_name": 1231, }, - }.TestOnStatusAndMessage(t, nil, - MessageWithStatus{ - Status: 400, - Message: "failed to process the request", - }) + }.TestOnStatusMessageAndDB(t, nil, + StatusMessageDBTester{ + MessageWithStatus: MessageWithStatus{ + Status: 400, + Message: "failed to process the request", + }, + DBTester: AssertNoCategoryCreation, + }, + ) } func TestCreateCategoryFailsIfNameIsMissing(t *testing.T) { @@ -92,10 +110,13 @@ func TestCreateCategoryFailsIfNameIsMissing(t *testing.T) { Method: "POST", Path: "/api/v1/categories/", Body: &map[string]interface{}{}, - }.TestOnStatusAndMessage(t, nil, - MessageWithStatus{ - Status: 400, - Message: "failed to validate the data", + }.TestOnStatusMessageAndDB(t, nil, + StatusMessageDBTester{ + MessageWithStatus: MessageWithStatus{ + Status: 400, + Message: "failed to validate the data", + }, + DBTester: AssertNoCategoryCreation, }, ) } @@ -105,6 +126,10 @@ func TestCreateCategoryFailsIfCategoryWithThatNameAlreadyExists(t *testing.T) { existingAppAssert := CreateSampleCategory(t, categoryName) + var TestNumCategoriesRemainsAt1 = func(app TestApp, assert *assert.A, resp *http.Response) { + AssertNumCategoriesRemainsAtN(app, assert, resp, 1) + } + for _, permutation := range AllCasingPermutations(categoryName) { fmt.Println(permutation) TestRequest{ @@ -113,10 +138,13 @@ func TestCreateCategoryFailsIfCategoryWithThatNameAlreadyExists(t *testing.T) { Body: &map[string]interface{}{ "category_name": permutation, }, - }.TestOnStatusAndMessageKeepDB(t, &existingAppAssert, - MessageWithStatus{ - Status: 400, - Message: "category with that name already exists", + }.TestOnStatusMessageAndDBKeepDB(t, &existingAppAssert, + StatusMessageDBTester{ + MessageWithStatus: MessageWithStatus{ + Status: 400, + Message: "category with that name already exists", + }, + DBTester: TestNumCategoriesRemainsAt1, }, ) } diff --git a/backend/tests/api/helpers.go b/backend/tests/api/helpers.go index 534f00f4e..144904269 100644 --- a/backend/tests/api/helpers.go +++ b/backend/tests/api/helpers.go @@ -213,10 +213,9 @@ type MessageWithStatus struct { Message string } -func (request TestRequest) TestOnStatusAndMessage(t *testing.T, existingAppAssert *ExistingAppAssert, messagedStatus MessageWithStatus) ExistingAppAssert { +func (request TestRequest) TestOnStatusAndMessage(t *testing.T, existingAppAssert *ExistingAppAssert, messagedStatus MessageWithStatus) { appAssert := request.TestOnStatusAndMessageKeepDB(t, existingAppAssert, messagedStatus) appAssert.App.DropDB() - return appAssert } func (request TestRequest) TestOnStatusAndMessageKeepDB(t *testing.T, existingAppAssert *ExistingAppAssert, messagedStatus MessageWithStatus) ExistingAppAssert { @@ -238,6 +237,22 @@ func (request TestRequest) TestOnStatusAndMessageKeepDB(t *testing.T, existingAp return appAssert } +type StatusMessageDBTester struct { + MessageWithStatus MessageWithStatus + DBTester DBTester +} + +func (request TestRequest) TestOnStatusMessageAndDB(t *testing.T, existingAppAssert *ExistingAppAssert, statusMessageDBTester StatusMessageDBTester) { + appAssert := request.TestOnStatusMessageAndDBKeepDB(t, existingAppAssert, statusMessageDBTester) + appAssert.App.DropDB() +} + +func (request TestRequest) TestOnStatusMessageAndDBKeepDB(t *testing.T, existingAppAssert *ExistingAppAssert, statusMessageDBTester StatusMessageDBTester) ExistingAppAssert { + appAssert := request.TestOnStatusAndMessageKeepDB(t, existingAppAssert, statusMessageDBTester.MessageWithStatus) + statusMessageDBTester.DBTester(appAssert.App, appAssert.Assert, nil) + return appAssert +} + type DBTester func(app TestApp, assert *assert.A, resp *http.Response) type DBTesterWithStatus struct { @@ -245,10 +260,9 @@ type DBTesterWithStatus struct { DBTester } -func (request TestRequest) TestOnStatusAndDB(t *testing.T, existingAppAssert *ExistingAppAssert, dbTesterStatus DBTesterWithStatus) ExistingAppAssert { +func (request TestRequest) TestOnStatusAndDB(t *testing.T, existingAppAssert *ExistingAppAssert, dbTesterStatus DBTesterWithStatus) { appAssert := request.TestOnStatusAndDBKeepDB(t, existingAppAssert, dbTesterStatus) appAssert.App.DropDB() - return appAssert } func (request TestRequest) TestOnStatusAndDBKeepDB(t *testing.T, existingAppAssert *ExistingAppAssert, dbTesterStatus DBTesterWithStatus) ExistingAppAssert { diff --git a/backend/tests/api/tag_test.go b/backend/tests/api/tag_test.go index 21bd1ba8b..5a0ab27c1 100644 --- a/backend/tests/api/tag_test.go +++ b/backend/tests/api/tag_test.go @@ -1,55 +1,57 @@ package tests import ( - "backend/src/models" - "backend/src/transactions" - "bytes" - "fmt" - "io" - "net/http/httptest" + "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 TestCreateTagWorks(t *testing.T) { - app, assert := InitTest(t) - - data := map[string]interface{}{ - "name": "Generate", - "category_id": 1, - } - - body, err := json.Marshal(data) - - assert.NilError(err) - - req := httptest.NewRequest("POST", fmt.Sprintf("%s/api/v1/tags/", app.Address), bytes.NewBuffer(body)) - req.Header.Set("Content-Type", "application/json") + existingAppAssert := CreateSampleCategory(t, "Science") + TestRequest{ + Method: "POST", + Path: "/api/v1/tags/", + Body: &map[string]interface{}{ + "name": "Generate", + "category_id": 1, + }, + }.TestOnStatusAndDB(t, &existingAppAssert, + DBTesterWithStatus{ + Status: 201, + DBTester: func(app TestApp, assert *assert.A, resp *http.Response) { + var respTag models.Tag - resp, err := app.App.Test(req) + err := json.NewDecoder(resp.Body).Decode(&respTag) - assert.NilError(err) + assert.NilError(err) - assert.Equal(201, resp.StatusCode) + dbTag, err := transactions.GetTag(app.Conn, respTag.ID) - var respTag models.Tag + assert.NilError(err) - err = json.NewDecoder(resp.Body).Decode(&respTag) + assert.Equal(dbTag, &respTag) + }, + }, + ) +} - assert.NilError(err) +var AssertNoTagCreation = func(app TestApp, assert *assert.A, resp *http.Response) { + var tags []models.Tag - dbTag, err := transactions.GetTag(app.Conn, respTag.ID) + err := app.Conn.Find(&tags).Error assert.NilError(err) - assert.Equal(dbTag, respTag) + assert.Equal(0, len(tags)) } func TestCreateTagFailsBadRequest(t *testing.T) { - app, assert := InitTest(t) - - badReqs := []map[string]interface{}{ + badBodys := []map[string]interface{}{ { "name": "Generate", "category_id": "1", @@ -60,34 +62,25 @@ func TestCreateTagFailsBadRequest(t *testing.T) { }, } - for _, badReq := range badReqs { - body, err := json.Marshal(badReq) - - assert.NilError(err) - - req := httptest.NewRequest("POST", fmt.Sprintf("%s/api/v1/tags/", app.Address), bytes.NewBuffer(body)) - req.Header.Set("Content-Type", "application/json") - - resp, err := app.App.Test(req) - - assert.NilError(err) - - assert.Equal(400, resp.StatusCode) - - defer resp.Body.Close() - - body, err = io.ReadAll(resp.Body) - - assert.NilError(err) - - assert.Equal("Failed to process the request", string(body)) + for _, badBody := range badBodys { + TestRequest{ + Method: "POST", + Path: "/api/v1/tags/", + Body: &badBody, + }.TestOnStatusMessageAndDB(t, nil, + StatusMessageDBTester{ + MessageWithStatus: MessageWithStatus{ + Status: 400, + Message: "failed to process the request", + }, + DBTester: AssertNoTagCreation, + }, + ) } } func TestCreateTagFailsValidation(t *testing.T) { - app, assert := InitTest(t) - - badReqs := []map[string]interface{}{ + badBodys := []map[string]interface{}{ { "name": "Generate", }, @@ -97,26 +90,20 @@ func TestCreateTagFailsValidation(t *testing.T) { {}, } - for _, badReq := range badReqs { - body, err := json.Marshal(badReq) - - assert.NilError(err) - - req := httptest.NewRequest("POST", fmt.Sprintf("%s/api/v1/tags/", app.Address), bytes.NewBuffer(body)) - req.Header.Set("Content-Type", "application/json") - - resp, err := app.App.Test(req) - - assert.NilError(err) - - assert.Equal(400, resp.StatusCode) - - defer resp.Body.Close() - - body, err = io.ReadAll(resp.Body) - - assert.NilError(err) - - assert.Equal("Failed to validate the data", string(body)) + for _, badBody := range badBodys { + TestRequest{ + Method: "POST", + Path: "/api/v1/tags/", + Body: &badBody, + }.TestOnStatusMessageAndDB(t, nil, + StatusMessageDBTester{ + MessageWithStatus: MessageWithStatus{ + Status: 400, + Message: "failed to validate the data", + }, + DBTester: AssertNoTagCreation, + }, + ) } + } From fa44d3bca2afe28ffe06b73f59f66723d26a63d9 Mon Sep 17 00:00:00 2001 From: garrettladley Date: Sat, 20 Jan 2024 15:54:29 -0500 Subject: [PATCH 09/11] get route | utility scripts --- backend/src/controllers/tag.go | 23 +++++++++ backend/src/server/server.go | 1 + backend/src/services/tag.go | 11 +++++ backend/src/transactions/tag.go | 4 +- backend/src/utilities/validator.go | 21 +++++++++ backend/tests/api/category_test.go | 2 +- backend/tests/api/helpers.go | 3 +- backend/tests/api/tag_test.go | 75 ++++++++++++++++++++++++++++-- scripts/clean_old_test_dbs.sh | 17 +++++++ scripts/clean_prefixed_test_dbs.sh | 17 +++++++ 10 files changed, 165 insertions(+), 9 deletions(-) create mode 100755 scripts/clean_old_test_dbs.sh create mode 100755 scripts/clean_prefixed_test_dbs.sh diff --git a/backend/src/controllers/tag.go b/backend/src/controllers/tag.go index 3d47bdac0..15ef076d6 100644 --- a/backend/src/controllers/tag.go +++ b/backend/src/controllers/tag.go @@ -43,3 +43,26 @@ func (t *TagController) CreateTag(c *fiber.Ctx) error { return c.Status(fiber.StatusCreated).JSON(&dbTag) } + +// GetTag godoc +// +// @Summary Gets a tag +// @Description Returns a tag +// @ID get-tag +// @Tags tag +// @Produce json +// @Param id path int true "Tag ID" +// @Success 200 {object} models.Tag +// @Failure 400 {string} string "failed to validate id" +// @Failure 404 {string} string "faied to find tag" +// @Failure 500 {string} string "failed to retrieve tag" +// @Router /api/v1/tags/{id} [get] +func (t *TagController) GetTag(c *fiber.Ctx) error { + tag, err := t.tagService.GetTag(c.Params("id")) + + if err != nil { + return err + } + + return c.Status(fiber.StatusOK).JSON(&tag) +} diff --git a/backend/src/server/server.go b/backend/src/server/server.go index 7711bf788..3f8682d8d 100644 --- a/backend/src/server/server.go +++ b/backend/src/server/server.go @@ -81,4 +81,5 @@ func tagRoutes(router fiber.Router, tagService services.TagServiceInterface) { tags := router.Group("/tags") tags.Post("/", tagController.CreateTag) + tags.Get("/:id", tagController.GetTag) } diff --git a/backend/src/services/tag.go b/backend/src/services/tag.go index a6093bdb5..12457ed14 100644 --- a/backend/src/services/tag.go +++ b/backend/src/services/tag.go @@ -10,6 +10,7 @@ import ( type TagServiceInterface interface { CreateTag(partialTag models.CreateTagRequestBody) (*models.Tag, error) + GetTag(id string) (*models.Tag, error) } type TagService struct { @@ -28,3 +29,13 @@ func (t *TagService) CreateTag(partialTag models.CreateTagRequestBody) (*models. return transactions.CreateTag(t.DB, tag) } + +func (t *TagService) GetTag(id string) (*models.Tag, error) { + idAsUint, err := utilities.ValidateID(id) + + if err != nil { + return nil, err + } + + return transactions.GetTag(t.DB, *idAsUint) +} diff --git a/backend/src/transactions/tag.go b/backend/src/transactions/tag.go index ec261aa7e..ea844ecb5 100644 --- a/backend/src/transactions/tag.go +++ b/backend/src/transactions/tag.go @@ -21,9 +21,9 @@ func GetTag(db *gorm.DB, id uint) (*models.Tag, error) { if err := db.First(&tag, id).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { - return nil, fiber.NewError(fiber.StatusBadRequest, "invalid tag id") + return nil, fiber.NewError(fiber.StatusNotFound, "failed to find tag") } else { - return nil, fiber.NewError(fiber.StatusInternalServerError, "unable to retrieve tag") + return nil, fiber.NewError(fiber.StatusInternalServerError, "failed to retrieve tag") } } diff --git a/backend/src/utilities/validator.go b/backend/src/utilities/validator.go index 770097268..a161587c6 100644 --- a/backend/src/utilities/validator.go +++ b/backend/src/utilities/validator.go @@ -1,7 +1,10 @@ package utilities import ( + "strconv" + "github.com/go-playground/validator/v10" + "github.com/gofiber/fiber/v2" ) // Validate the data sent to the server if the data is invalid, return an error otherwise, return nil @@ -13,3 +16,21 @@ func ValidateData(model interface{}) error { return nil } + +func ValidateID(id string) (*uint, error) { + idAsInt, err := strconv.Atoi(id) + + errMsg := "failed to validate id" + + if err != nil { + return nil, fiber.NewError(fiber.StatusBadRequest, errMsg) + } + + if idAsInt < 1 { // postgres ids start at 1 + return nil, fiber.NewError(fiber.StatusBadRequest, errMsg) + } + + idAsUint := uint(idAsInt) + + return &idAsUint, nil +} diff --git a/backend/tests/api/category_test.go b/backend/tests/api/category_test.go index b552fa244..27e74d0fa 100644 --- a/backend/tests/api/category_test.go +++ b/backend/tests/api/category_test.go @@ -122,7 +122,7 @@ func TestCreateCategoryFailsIfNameIsMissing(t *testing.T) { } func TestCreateCategoryFailsIfCategoryWithThatNameAlreadyExists(t *testing.T) { - categoryName := "Science" + categoryName := "foo" existingAppAssert := CreateSampleCategory(t, categoryName) diff --git a/backend/tests/api/helpers.go b/backend/tests/api/helpers.go index 144904269..9a4e838d7 100644 --- a/backend/tests/api/helpers.go +++ b/backend/tests/api/helpers.go @@ -78,8 +78,9 @@ func generateRandomInt(max int64) int64 { } func generateRandomDBName() string { + prefix := "sac_test_" letterBytes := "abcdefghijklmnopqrstuvwxyz" - length := 36 + length := len(prefix) + 36 result := make([]byte, length) for i := 0; i < length; i++ { result[i] = letterBytes[generateRandomInt(int64(len(letterBytes)))] diff --git a/backend/tests/api/tag_test.go b/backend/tests/api/tag_test.go index 5a0ab27c1..fd6206d20 100644 --- a/backend/tests/api/tag_test.go +++ b/backend/tests/api/tag_test.go @@ -1,6 +1,7 @@ package tests import ( + "fmt" "net/http" "testing" @@ -11,16 +12,16 @@ import ( "github.com/goccy/go-json" ) -func TestCreateTagWorks(t *testing.T) { - existingAppAssert := CreateSampleCategory(t, "Science") - TestRequest{ +func CreateSampleTag(t *testing.T, tagName string, categoryName string) ExistingAppAssert { + existingAppAssert := CreateSampleCategory(t, categoryName) + return TestRequest{ Method: "POST", Path: "/api/v1/tags/", Body: &map[string]interface{}{ - "name": "Generate", + "name": tagName, "category_id": 1, }, - }.TestOnStatusAndDB(t, &existingAppAssert, + }.TestOnStatusAndDBKeepDB(t, &existingAppAssert, DBTesterWithStatus{ Status: 201, DBTester: func(app TestApp, assert *assert.A, resp *http.Response) { @@ -40,6 +41,11 @@ func TestCreateTagWorks(t *testing.T) { ) } +func TestCreateTagWorks(t *testing.T) { + appAssert := CreateSampleTag(t, "Generate", "Science") + appAssert.App.DropDB() +} + var AssertNoTagCreation = func(app TestApp, assert *assert.A, resp *http.Response) { var tags []models.Tag @@ -105,5 +111,64 @@ func TestCreateTagFailsValidation(t *testing.T) { }, ) } +} + +func TestGetTagWorks(t *testing.T) { + existingAppAssert := CreateSampleTag(t, "Generate", "Science") + + TestRequest{ + Method: "GET", + Path: "/api/v1/tags/1", + }.TestOnStatusAndDB(t, &existingAppAssert, + DBTesterWithStatus{ + Status: 200, + DBTester: func(app TestApp, assert *assert.A, resp *http.Response) { + var respTag models.Tag + + err := json.NewDecoder(resp.Body).Decode(&respTag) + + assert.NilError(err) + + dbTag, err := transactions.GetTag(app.Conn, respTag.ID) + + assert.NilError(err) + + assert.Equal(dbTag, &respTag) + }, + }, + ) +} + +func TestGetTagFailsBadRequest(t *testing.T) { + badRequests := []string{ + "0", + "-1", + "1.1", + "foo", + "null", + } + + for _, badRequest := range badRequests { + TestRequest{ + Method: "GET", + Path: fmt.Sprintf("/api/v1/tags/%s", badRequest), + }.TestOnStatusAndMessage(t, nil, + MessageWithStatus{ + Status: 400, + Message: "failed to validate id", + }, + ) + } +} +func TestGetTagFailsNotFound(t *testing.T) { + TestRequest{ + Method: "GET", + Path: "/api/v1/tags/1", + }.TestOnStatusAndMessage(t, nil, + MessageWithStatus{ + Status: 404, + Message: "failed to find tag", + }, + ) } diff --git a/scripts/clean_old_test_dbs.sh b/scripts/clean_old_test_dbs.sh new file mode 100755 index 000000000..04c97262b --- /dev/null +++ b/scripts/clean_old_test_dbs.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" + +PGHOST="127.0.0.1" +PGPORT="5432" +PGUSER="postgres" +PGPASSWORD="postgres" +PGDATABASE="sac" + +DATABASES=$(psql -h "$PGHOST" -p "$PGPORT" -U "$PGUSER" -d "$PGDATABASE" -t -c "SELECT datname FROM pg_database WHERE datistemplate = false AND datname != 'postgres' AND datname != 'template0' AND datname != '$PGDATABASE' AND datname !='$(whoami)';") + + +for db in $DATABASES; do + echo "Dropping database $db" + psql -h "$PGHOST" -p "$PGPORT" -U "$PGUSER" -d "$PGDATABASE" -c "DROP DATABASE $db;" +done diff --git a/scripts/clean_prefixed_test_dbs.sh b/scripts/clean_prefixed_test_dbs.sh new file mode 100755 index 000000000..61797e999 --- /dev/null +++ b/scripts/clean_prefixed_test_dbs.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" + +PGHOST="127.0.0.1" +PGPORT="5432" +PGUSER="postgres" +PGPASSWORD="postgres" +PGDATABASE="sac" +PREFIX="sac_test_" + +DATABASES=$(psql -h "$PGHOST" -p "$PGPORT" -U "$PGUSER" -d "$PGDATABASE" -t -c "SELECT datname FROM pg_database WHERE datistemplate = false AND datname like '$PREFIX%';") + +for db in $DATABASES; do + echo "Dropping database $db" + psql -h "$PGHOST" -p "$PGPORT" -U "$PGUSER" -d "$PGDATABASE" -c "DROP DATABASE $db;" +done From ab1fdcbb23d5490567c2f7467b1ff7ef4c6abb45 Mon Sep 17 00:00:00 2001 From: garrettladley Date: Sat, 20 Jan 2024 23:17:08 -0500 Subject: [PATCH 10/11] addressed review --- backend/src/models/tag.go | 4 ++-- backend/src/services/tag.go | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/backend/src/models/tag.go b/backend/src/models/tag.go index bcb48a96d..d515fb692 100644 --- a/backend/src/models/tag.go +++ b/backend/src/models/tag.go @@ -17,6 +17,6 @@ type Tag struct { } type CreateTagRequestBody struct { - Name string `json:"name" validate:"required"` - CategoryID uint `json:"category_id" validate:"required"` + Name string `json:"name" validate:"required,max=255"` + CategoryID uint `json:"category_id" validate:"required,min=1"` } diff --git a/backend/src/services/tag.go b/backend/src/services/tag.go index 12457ed14..7a741b994 100644 --- a/backend/src/services/tag.go +++ b/backend/src/services/tag.go @@ -9,7 +9,7 @@ import ( ) type TagServiceInterface interface { - CreateTag(partialTag models.CreateTagRequestBody) (*models.Tag, error) + CreateTag(tagBody models.CreateTagRequestBody) (*models.Tag, error) GetTag(id string) (*models.Tag, error) } @@ -17,10 +17,10 @@ type TagService struct { DB *gorm.DB } -func (t *TagService) CreateTag(partialTag models.CreateTagRequestBody) (*models.Tag, error) { +func (t *TagService) CreateTag(tagBody models.CreateTagRequestBody) (*models.Tag, error) { tag := models.Tag{ - Name: partialTag.Name, - CategoryID: partialTag.CategoryID, + Name: tagBody.Name, + CategoryID: tagBody.CategoryID, } if err := utilities.ValidateData(tag); err != nil { From f73d592a6dd58b8c346eed48685b075de032f343 Mon Sep 17 00:00:00 2001 From: garrettladley Date: Sun, 21 Jan 2024 00:03:48 -0500 Subject: [PATCH 11/11] fixes --- backend/src/services/tag.go | 1 + backend/tests/api/category_test.go | 8 ++++---- backend/tests/api/tag_test.go | 20 +++++++++++++------- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/backend/src/services/tag.go b/backend/src/services/tag.go index eb4b08528..7a741b994 100644 --- a/backend/src/services/tag.go +++ b/backend/src/services/tag.go @@ -21,6 +21,7 @@ func (t *TagService) CreateTag(tagBody models.CreateTagRequestBody) (*models.Tag tag := models.Tag{ Name: tagBody.Name, CategoryID: tagBody.CategoryID, + } if err := utilities.ValidateData(tag); err != nil { return nil, fiber.NewError(fiber.StatusBadRequest, "failed to validate the data") diff --git a/backend/tests/api/category_test.go b/backend/tests/api/category_test.go index dd1ae904f..a43039a8c 100644 --- a/backend/tests/api/category_test.go +++ b/backend/tests/api/category_test.go @@ -12,14 +12,14 @@ import ( "github.com/goccy/go-json" ) -func CreateSampleCategory(t *testing.T, categoryName string) ExistingAppAssert { +func CreateSampleCategory(t *testing.T, categoryName string, existingAppAssert *ExistingAppAssert) ExistingAppAssert { return TestRequest{ Method: "POST", Path: "/api/v1/categories/", Body: &map[string]interface{}{ "category_name": categoryName, }, - }.TestOnStatusAndDB(t, nil, + }.TestOnStatusAndDB(t, existingAppAssert, DBTesterWithStatus{ Status: 201, DBTester: func(app TestApp, assert *assert.A, resp *http.Response) { @@ -41,7 +41,7 @@ func CreateSampleCategory(t *testing.T, categoryName string) ExistingAppAssert { } func TestCreateCategoryWorks(t *testing.T) { - CreateSampleCategory(t, "Science") + CreateSampleCategory(t, "Science", nil) } func TestCreateCategoryIgnoresid(t *testing.T) { @@ -123,7 +123,7 @@ func TestCreateCategoryFailsIfNameIsMissing(t *testing.T) { func TestCreateCategoryFailsIfCategoryWithThatNameAlreadyExists(t *testing.T) { categoryName := "foo" - existingAppAssert := CreateSampleCategory(t, categoryName) + existingAppAssert := CreateSampleCategory(t, categoryName, nil) var TestNumCategoriesRemainsAt1 = func(app TestApp, assert *assert.A, resp *http.Response) { AssertNumCategoriesRemainsAtN(app, assert, resp, 1) diff --git a/backend/tests/api/tag_test.go b/backend/tests/api/tag_test.go index d73b1b702..d119f32e1 100644 --- a/backend/tests/api/tag_test.go +++ b/backend/tests/api/tag_test.go @@ -12,17 +12,22 @@ import ( "github.com/goccy/go-json" ) +func CreateSampleTag(t *testing.T, tagName string, categoryName string, existingAppAssert *ExistingAppAssert) ExistingAppAssert { + var appAssert ExistingAppAssert -func TestCreateTagWorks(t *testing.T) { - existingAppAssert := CreateSampleCategory(t, "Science") - TestRequest{ + if existingAppAssert == nil { + appAssert = CreateSampleCategory(t, categoryName, existingAppAssert) + } else { + appAssert = CreateSampleCategory(t, categoryName, existingAppAssert) + } + return TestRequest{ Method: "POST", Path: "/api/v1/tags/", Body: &map[string]interface{}{ - "name": "Generate", + "name": tagName, "category_id": 1, }, - }.TestOnStatusAndDB(t, &existingAppAssert, + }.TestOnStatusAndDB(t, &appAssert, DBTesterWithStatus{ Status: 201, DBTester: func(app TestApp, assert *assert.A, resp *http.Response) { @@ -43,7 +48,7 @@ func TestCreateTagWorks(t *testing.T) { } func TestCreateTagWorks(t *testing.T) { - CreateSampleTag(t, "Generate", "Science") + CreateSampleTag(t, "Generate", "Science", nil) } var AssertNoTagCreation = func(app TestApp, assert *assert.A, resp *http.Response) { @@ -114,7 +119,7 @@ func TestCreateTagFailsValidation(t *testing.T) { } func TestGetTagWorks(t *testing.T) { - existingAppAssert := CreateSampleTag(t, "Generate", "Science") + existingAppAssert := CreateSampleTag(t, "Generate", "Science", nil) TestRequest{ Method: "GET", @@ -171,3 +176,4 @@ func TestGetTagFailsNotFound(t *testing.T) { Message: "failed to find tag", }, ) +}