From 2ab675b85bfada76d6d663e1e0d9f5d49097fb72 Mon Sep 17 00:00:00 2001 From: garrettladley Date: Sat, 3 Feb 2024 22:33:12 -0500 Subject: [PATCH] fixed all the tests i could --- backend/src/controllers/club.go | 20 ++-- backend/src/database/db.go | 26 ++++- backend/src/database/super.go | 3 + backend/src/models/club.go | 3 +- backend/src/models/user.go | 1 + backend/src/transactions/category_tag.go | 9 ++ backend/tests/api/category_tag_test.go | 15 +-- backend/tests/api/category_test.go | 23 +++- backend/tests/api/club_test.go | 47 +++++--- backend/tests/api/helpers/auth.go | 21 +++- backend/tests/api/helpers/database.go | 2 +- backend/tests/api/helpers/requests.go | 31 ++++-- backend/tests/api/helpers/utilities.go | 4 + backend/tests/api/tag_test.go | 14 +++ backend/tests/api/user_tag_test.go | 132 +++++++++++++---------- backend/tests/api/user_test.go | 75 ++++++++----- 16 files changed, 288 insertions(+), 138 deletions(-) diff --git a/backend/src/controllers/club.go b/backend/src/controllers/club.go index 4eab57c55..567b8a83f 100644 --- a/backend/src/controllers/club.go +++ b/backend/src/controllers/club.go @@ -17,11 +17,11 @@ func NewClubController(clubService services.ClubServiceInterface) *ClubControlle return &ClubController{clubService: clubService} } -func (l *ClubController) GetAllClubs(c *fiber.Ctx) error { +func (cl *ClubController) GetAllClubs(c *fiber.Ctx) error { defaultLimit := 10 defaultPage := 1 - clubs, err := l.clubService.GetClubs(c.Query("limit", strconv.Itoa(defaultLimit)), c.Query("page", strconv.Itoa(defaultPage))) + clubs, err := cl.clubService.GetClubs(c.Query("limit", strconv.Itoa(defaultLimit)), c.Query("page", strconv.Itoa(defaultPage))) if err != nil { return err.FiberError(c) } @@ -29,13 +29,13 @@ func (l *ClubController) GetAllClubs(c *fiber.Ctx) error { return c.Status(fiber.StatusOK).JSON(clubs) } -func (l *ClubController) CreateClub(c *fiber.Ctx) error { +func (cl *ClubController) CreateClub(c *fiber.Ctx) error { var clubBody models.CreateClubRequestBody if err := c.BodyParser(&clubBody); err != nil { return errors.FailedToParseRequestBody.FiberError(c) } - club, err := l.clubService.CreateClub(clubBody) + club, err := cl.clubService.CreateClub(clubBody) if err != nil { return err.FiberError(c) } @@ -43,8 +43,8 @@ func (l *ClubController) CreateClub(c *fiber.Ctx) error { return c.Status(fiber.StatusCreated).JSON(club) } -func (l *ClubController) GetClub(c *fiber.Ctx) error { - club, err := l.clubService.GetClub(c.Params("id")) +func (cl *ClubController) GetClub(c *fiber.Ctx) error { + club, err := cl.clubService.GetClub(c.Params("id")) if err != nil { return err.FiberError(c) } @@ -52,14 +52,14 @@ func (l *ClubController) GetClub(c *fiber.Ctx) error { return c.Status(fiber.StatusOK).JSON(club) } -func (l *ClubController) UpdateClub(c *fiber.Ctx) error { +func (cl *ClubController) UpdateClub(c *fiber.Ctx) error { var clubBody models.UpdateClubRequestBody if err := c.BodyParser(&clubBody); err != nil { return errors.FailedToParseRequestBody.FiberError(c) } - updatedClub, err := l.clubService.UpdateClub(c.Params("id"), clubBody) + updatedClub, err := cl.clubService.UpdateClub(c.Params("id"), clubBody) if err != nil { return err.FiberError(c) } @@ -67,8 +67,8 @@ func (l *ClubController) UpdateClub(c *fiber.Ctx) error { return c.Status(fiber.StatusOK).JSON(updatedClub) } -func (l *ClubController) DeleteClub(c *fiber.Ctx) error { - err := l.clubService.DeleteClub(c.Params("id")) +func (cl *ClubController) DeleteClub(c *fiber.Ctx) error { + err := cl.clubService.DeleteClub(c.Params("id")) if err != nil { return err.FiberError(c) } diff --git a/backend/src/database/db.go b/backend/src/database/db.go index f0d7d5717..35ad1d868 100644 --- a/backend/src/database/db.go +++ b/backend/src/database/db.go @@ -10,7 +10,7 @@ import ( ) func ConfigureDB(settings config.Settings) (*gorm.DB, error) { - db, err := EstablishConn(settings.Database.WithDb()) + db, err := EstablishConn(settings.Database.WithDb(), WithLoggerInfo()) if err != nil { return nil, err } @@ -22,12 +22,26 @@ func ConfigureDB(settings config.Settings) (*gorm.DB, error) { return db, nil } -func EstablishConn(dsn string) (*gorm.DB, error) { - db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{ - Logger: logger.Default.LogMode(logger.Info), +type OptionalFunc func(gorm.Config) gorm.Config + +func WithLoggerInfo() OptionalFunc { + return func(gormConfig gorm.Config) gorm.Config { + gormConfig.Logger = logger.Default.LogMode(logger.Info) + return gormConfig + } +} + +func EstablishConn(dsn string, opts ...OptionalFunc) (*gorm.DB, error) { + rootConfig := gorm.Config{ SkipDefaultTransaction: true, TranslateError: true, - }) + } + + for _, opt := range opts { + rootConfig = opt(rootConfig) + } + + db, err := gorm.Open(postgres.Open(dsn), &rootConfig) if err != nil { return nil, err } @@ -106,6 +120,8 @@ func createSuperUser(settings config.Settings, db *gorm.DB) error { return err } + SuperUserUUID = superUser.ID + superClub := SuperClub() if err := tx.Create(&superClub).Error; err != nil { diff --git a/backend/src/database/super.go b/backend/src/database/super.go index 24e2d0aac..6ad3f930f 100644 --- a/backend/src/database/super.go +++ b/backend/src/database/super.go @@ -5,8 +5,11 @@ import ( "github.com/GenerateNU/sac/backend/src/config" "github.com/GenerateNU/sac/backend/src/errors" "github.com/GenerateNU/sac/backend/src/models" + "github.com/google/uuid" ) +var SuperUserUUID uuid.UUID + func SuperUser(superUserSettings config.SuperUserSettings) (*models.User, *errors.Error) { passwordHash, err := auth.ComputePasswordHash(superUserSettings.Password) if err != nil { diff --git a/backend/src/models/club.go b/backend/src/models/club.go index 919d30fb6..3c56614d6 100644 --- a/backend/src/models/club.go +++ b/backend/src/models/club.go @@ -40,6 +40,7 @@ type Club struct { Parent *uuid.UUID `gorm:"foreignKey:Parent" json:"-" validate:"uuid4"` Tag []Tag `gorm:"many2many:club_tags;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"-" validate:"-"` // User + Admin []User `gorm:"many2many:user_club_admins;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"-" validate:"required"` Member []User `gorm:"many2many:user_club_members;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"-" validate:"required"` Follower []User `gorm:"many2many:user_club_followers;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"-" validate:"-"` IntendedApplicant []User `gorm:"many2many:user_club_intended_applicants;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"-" validate:"-"` @@ -56,7 +57,6 @@ type CreateClubRequestBody struct { Name string `json:"name" validate:"required,max=255"` Preview string `json:"preview" validate:"required,max=255"` Description string `json:"description" validate:"required,http_url,mongo_url,max=255"` // MongoDB URL - NumMembers int `json:"num_members" validate:"required,min=1"` IsRecruiting bool `json:"is_recruiting" validate:"required"` RecruitmentCycle RecruitmentCycle `gorm:"type:varchar(255);default:always" json:"recruitment_cycle" validate:"required,max=255,oneof=fall spring fallSpring always"` RecruitmentType RecruitmentType `gorm:"type:varchar(255);default:unrestricted" json:"recruitment_type" validate:"required,max=255,oneof=unrestricted tryout application"` @@ -68,7 +68,6 @@ type UpdateClubRequestBody struct { Name string `json:"name" validate:"omitempty,max=255"` Preview string `json:"preview" validate:"omitempty,max=255"` Description string `json:"description" validate:"omitempty,http_url,mongo_url,max=255"` // MongoDB URL - NumMembers int `json:"num_members" validate:"omitempty,min=1"` IsRecruiting bool `json:"is_recruiting" validate:"omitempty"` RecruitmentCycle RecruitmentCycle `gorm:"type:varchar(255);default:always" json:"recruitment_cycle" validate:"required,max=255,oneof=fall spring fallSpring always"` RecruitmentType RecruitmentType `gorm:"type:varchar(255);default:unrestricted" json:"recruitment_type" validate:"required,max=255,oneof=unrestricted tryout application"` diff --git a/backend/src/models/user.go b/backend/src/models/user.go index 9ddad75cb..ad27132c2 100644 --- a/backend/src/models/user.go +++ b/backend/src/models/user.go @@ -47,6 +47,7 @@ type User struct { Year Year `gorm:"type:smallint" json:"year" validate:"required,min=1,max=6"` Tag []Tag `gorm:"many2many:user_tags;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"-" validate:"-"` + Admin []Club `gorm:"many2many:user_club_admins;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"-" validate:"-"` Member []Club `gorm:"many2many:user_club_members;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"-" validate:"-"` Follower []Club `gorm:"many2many:user_club_followers;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"-" validate:"-"` IntendedApplicant []Club `gorm:"many2many:user_club_intended_applicants;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"-" validate:"-"` diff --git a/backend/src/transactions/category_tag.go b/backend/src/transactions/category_tag.go index 7ef3129a7..f827fb557 100644 --- a/backend/src/transactions/category_tag.go +++ b/backend/src/transactions/category_tag.go @@ -12,6 +12,15 @@ import ( ) func GetTagsByCategory(db *gorm.DB, categoryID uuid.UUID, limit int, offset int) ([]models.Tag, *errors.Error) { + var category models.Category + + if err := db.Where("id = ?", categoryID).First(&category).Error; err != nil { + if stdliberrors.Is(err, gorm.ErrRecordNotFound) { + return nil, &errors.CategoryNotFound + } + return nil, &errors.FailedToGetCategory + } + var tags []models.Tag if err := db.Where("category_id = ?", categoryID).Limit(limit).Offset(offset).Find(&tags).Error; err != nil { return nil, &errors.FailedToGetTags diff --git a/backend/tests/api/category_tag_test.go b/backend/tests/api/category_tag_test.go index 2d3cf62a7..6f466b62a 100644 --- a/backend/tests/api/category_tag_test.go +++ b/backend/tests/api/category_tag_test.go @@ -53,6 +53,7 @@ func TestGetCategoryTagsWorks(t *testing.T) { h.TestRequest{ Method: fiber.MethodGet, Path: fmt.Sprintf("/api/v1/categories/%s/tags", categoryUUID), + Role: &models.Super, }.TestOnStatusAndDB(t, &appAssert, h.TesterWithStatus{ Status: fiber.StatusOK, @@ -78,6 +79,7 @@ func TestGetCategoryTagsFailsCategoryBadRequest(t *testing.T) { h.TestRequest{ Method: fiber.MethodGet, Path: fmt.Sprintf("/api/v1/categories/%s/tags", badRequest), + Role: &models.Super, }.TestOnError(t, &appAssert, errors.FailedToValidateID) } @@ -92,17 +94,13 @@ func TestGetCategoryTagsFailsCategoryNotFound(t *testing.T) { h.TestRequest{ Method: fiber.MethodGet, Path: fmt.Sprintf("/api/v1/categories/%s/tags", uuid), + Role: &models.Super, }.TestOnErrorAndDB(t, &appAssert, h.ErrorWithTester{ Error: errors.CategoryNotFound, Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) { var category models.Category err := app.Conn.Where("id = ?", uuid).First(&category).Error - assert.Error(err) - - var respBody []map[string]interface{} - err = json.NewDecoder(resp.Body).Decode(&respBody) - assert.NilError(err) - assert.Equal(0, len(respBody)) + assert.Assert(err != nil) }, }).Close() } @@ -113,6 +111,7 @@ func TestGetCategoryTagWorks(t *testing.T) { h.TestRequest{ Method: fiber.MethodGet, Path: fmt.Sprintf("/api/v1/categories/%s/tags/%s", categoryUUID, tagUUID), + Role: &models.Super, }.TestOnStatusAndDB(t, &existingAppAssert, h.TesterWithStatus{ Status: fiber.StatusOK, @@ -138,6 +137,7 @@ func TestGetCategoryTagFailsCategoryBadRequest(t *testing.T) { h.TestRequest{ Method: fiber.MethodGet, Path: fmt.Sprintf("/api/v1/categories/%s/tags/%s", badRequest, tagUUID), + Role: &models.Super, }.TestOnError(t, &appAssert, errors.FailedToValidateID) } @@ -159,6 +159,7 @@ func TestGetCategoryTagFailsTagBadRequest(t *testing.T) { h.TestRequest{ Method: fiber.MethodGet, Path: fmt.Sprintf("/api/v1/categories/%s/tags/%s", categoryUUID, badRequest), + Role: &models.Super, }.TestOnError(t, &appAssert, errors.FailedToValidateID) } @@ -171,6 +172,7 @@ func TestGetCategoryTagFailsCategoryNotFound(t *testing.T) { h.TestRequest{ Method: fiber.MethodGet, Path: fmt.Sprintf("/api/v1/categories/%s/tags/%s", uuid.New(), tagUUID), + Role: &models.Super, }.TestOnError(t, &appAssert, errors.TagNotFound).Close() } @@ -180,5 +182,6 @@ func TestGetCategoryTagFailsTagNotFound(t *testing.T) { h.TestRequest{ Method: fiber.MethodGet, Path: fmt.Sprintf("/api/v1/categories/%s/tags/%s", categoryUUID, uuid.New()), + Role: &models.Super, }.TestOnError(t, &appAssert, errors.TagNotFound).Close() } diff --git a/backend/tests/api/category_test.go b/backend/tests/api/category_test.go index 9ffa710bc..5db838466 100644 --- a/backend/tests/api/category_test.go +++ b/backend/tests/api/category_test.go @@ -78,6 +78,7 @@ func CreateSampleCategory(t *testing.T, existingAppAssert *h.ExistingAppAssert) Method: fiber.MethodPost, Path: "/api/v1/categories/", Body: SampleCategoryFactory(), + Role: &models.Super, }.TestOnStatusAndDB(t, existingAppAssert, h.TesterWithStatus{ Status: fiber.StatusCreated, @@ -107,6 +108,7 @@ func TestCreateCategoryIgnoresid(t *testing.T) { "id": 12, "name": "Foo", }, + Role: &models.Super, }.TestOnStatusAndDB(t, nil, h.TesterWithStatus{ Status: fiber.StatusCreated, @@ -142,6 +144,7 @@ func TestCreateCategoryFailsIfNameIsNotString(t *testing.T) { Body: &map[string]interface{}{ "name": 1231, }, + Role: &models.Super, }.TestOnErrorAndDB(t, nil, h.ErrorWithTester{ Error: errors.FailedToParseRequestBody, @@ -155,6 +158,7 @@ func TestCreateCategoryFailsIfNameIsMissing(t *testing.T) { Method: fiber.MethodPost, Path: "/api/v1/categories/", Body: &map[string]interface{}{}, + Role: &models.Super, }.TestOnErrorAndDB(t, nil, h.ErrorWithTester{ Error: errors.FailedToValidateCategory, @@ -178,6 +182,7 @@ func TestCreateCategoryFailsIfCategoryWithThatNameAlreadyExists(t *testing.T) { Method: fiber.MethodPost, Path: "/api/v1/categories/", Body: &modifiedSampleCategoryBody, + Role: &models.Super, }.TestOnErrorAndDB(t, &existingAppAssert, h.ErrorWithTester{ Error: errors.CategoryAlreadyExists, @@ -193,8 +198,9 @@ func TestGetCategoryWorks(t *testing.T) { existingAppAssert, uuid := CreateSampleCategory(t, nil) h.TestRequest{ - Method: "GET", + Method: fiber.MethodGet, Path: fmt.Sprintf("/api/v1/categories/%s", uuid), + Role: &models.Super, }.TestOnStatusAndDB(t, &existingAppAssert, h.TesterWithStatus{ Status: fiber.StatusOK, @@ -216,16 +222,18 @@ func TestGetCategoryFailsBadRequest(t *testing.T) { for _, badRequest := range badRequests { h.TestRequest{ - Method: "GET", + Method: fiber.MethodGet, Path: fmt.Sprintf("/api/v1/categories/%s", badRequest), + Role: &models.Super, }.TestOnError(t, nil, errors.FailedToValidateID).Close() } } func TestGetCategoryFailsNotFound(t *testing.T) { h.TestRequest{ - Method: "GET", + Method: fiber.MethodGet, Path: fmt.Sprintf("/api/v1/categories/%s", uuid.New()), + Role: &models.Super, }.TestOnError(t, nil, errors.CategoryNotFound).Close() } @@ -233,8 +241,9 @@ func TestGetCategoriesWorks(t *testing.T) { existingAppAssert, _ := CreateSampleCategory(t, nil) h.TestRequest{ - Method: "GET", + Method: fiber.MethodGet, Path: "/api/v1/categories/", + Role: &models.Super, }.TestOnStatusAndDB(t, &existingAppAssert, h.TesterWithStatus{ Status: fiber.StatusOK, @@ -304,6 +313,7 @@ func TestUpdateCategoryWorks(t *testing.T) { Method: fiber.MethodPatch, Path: fmt.Sprintf("/api/v1/categories/%s", uuid), Body: &category, + Role: &models.Super, }.TestOnStatusAndDB(t, &existingAppAssert, h.TesterWithStatus{ Status: fiber.StatusOK, @@ -326,6 +336,7 @@ func TestUpdateCategoryWorksWithSameDetails(t *testing.T) { Method: fiber.MethodPatch, Path: fmt.Sprintf("/api/v1/categories/%s", uuid), Body: &category, + Role: &models.Super, }.TestOnStatusAndDB(t, &existingAppAssert, h.TesterWithStatus{ Status: fiber.StatusOK, @@ -348,6 +359,7 @@ func TestUpdateCategoryFailsBadRequest(t *testing.T) { Method: fiber.MethodPatch, Path: fmt.Sprintf("/api/v1/categories/%s", badRequest), Body: SampleCategoryFactory(), + Role: &models.Super, }.TestOnError(t, nil, errors.FailedToValidateID).Close() } } @@ -358,6 +370,7 @@ func TestDeleteCategoryWorks(t *testing.T) { h.TestRequest{ Method: fiber.MethodDelete, Path: fmt.Sprintf("/api/v1/categories/%s", uuid), + Role: &models.Super, }.TestOnStatusAndDB(t, &existingAppAssert, h.TesterWithStatus{ Status: fiber.StatusNoContent, @@ -381,6 +394,7 @@ func TestDeleteCategoryFailsBadRequest(t *testing.T) { h.TestRequest{ Method: fiber.MethodDelete, Path: fmt.Sprintf("/api/v1/categories/%s", badRequest), + Role: &models.Super, }.TestOnErrorAndDB(t, &existingAppAssert, h.ErrorWithTester{ Error: errors.FailedToValidateID, @@ -398,6 +412,7 @@ func TestDeleteCategoryFailsNotFound(t *testing.T) { h.TestRequest{ Method: fiber.MethodDelete, Path: fmt.Sprintf("/api/v1/categories/%s", uuid.New()), + Role: &models.Super, }.TestOnErrorAndDB(t, &existingAppAssert, h.ErrorWithTester{ Error: errors.CategoryNotFound, diff --git a/backend/tests/api/club_test.go b/backend/tests/api/club_test.go index 4e87a4880..cb93dea86 100644 --- a/backend/tests/api/club_test.go +++ b/backend/tests/api/club_test.go @@ -16,13 +16,12 @@ import ( "gorm.io/gorm" ) -func SampleClubFactory(userID uuid.UUID) *map[string]interface{} { +func SampleClubFactory(userID *uuid.UUID) *map[string]interface{} { return &map[string]interface{}{ "user_id": userID, "name": "Generate", "preview": "Generate is Northeastern's premier student-led product development studio.", "description": "https://mongodb.com", - "num_members": 1, "is_recruiting": true, "recruitment_cycle": "always", "recruitment_type": "application", @@ -130,7 +129,9 @@ func AssertClubWithBodyRespDBMostRecent(app h.TestApp, assert *assert.A, resp *h } func AssertSampleClubBodyRespDB(app h.TestApp, assert *assert.A, resp *http.Response, userID uuid.UUID) uuid.UUID { - return AssertClubBodyRespDB(app, assert, resp, SampleClubFactory(userID)) + sampleClub := SampleClubFactory(&userID) + (*sampleClub)["num_members"] = 1 + return AssertClubBodyRespDB(app, assert, resp, sampleClub) } func CreateSampleClub(t *testing.T, existingAppAssert *h.ExistingAppAssert) (eaa h.ExistingAppAssert, studentUUID uuid.UUID, clubUUID uuid.UUID) { @@ -141,7 +142,8 @@ func CreateSampleClub(t *testing.T, existingAppAssert *h.ExistingAppAssert) (eaa newAppAssert := h.TestRequest{ Method: fiber.MethodPost, Path: "/api/v1/clubs/", - Body: SampleClubFactory(userID), + Body: SampleClubFactory(&userID), + Role: &models.Super, }.TestOnStatusAndDB(t, &appAssert, h.TesterWithStatus{ Status: fiber.StatusCreated, @@ -167,6 +169,7 @@ func TestGetClubsWorks(t *testing.T) { h.TestRequest{ Method: fiber.MethodGet, Path: "/api/v1/clubs/", + Role: &models.Super, }.TestOnStatusAndDB(t, nil, h.TesterWithStatus{ Status: fiber.StatusOK, @@ -234,13 +237,14 @@ func AssertCreateBadClubDataFails(t *testing.T, jsonKey string, badValues []inte appAssert, uuid, _ := CreateSampleStudent(t, nil) for _, badValue := range badValues { - sampleClubPermutation := *SampleClubFactory(uuid) + sampleClubPermutation := *SampleClubFactory(&uuid) sampleClubPermutation[jsonKey] = badValue h.TestRequest{ Method: fiber.MethodPost, Path: "/api/v1/clubs/", Body: &sampleClubPermutation, + Role: &models.Super, }.TestOnErrorAndDB(t, &appAssert, h.ErrorWithTester{ Error: errors.FailedToValidateClub, @@ -307,10 +311,11 @@ func TestCreateClubFailsOnInvalidLogo(t *testing.T) { ) } +// TODO: need to be able to join the club func TestUpdateClubWorks(t *testing.T) { appAssert, studentUUID, clubUUID := CreateSampleClub(t, nil) - updatedClub := SampleClubFactory(studentUUID) + updatedClub := SampleClubFactory(&studentUUID) (*updatedClub)["name"] = "Updated Name" (*updatedClub)["preview"] = "Updated Preview" @@ -318,6 +323,7 @@ func TestUpdateClubWorks(t *testing.T) { Method: fiber.MethodPatch, Path: fmt.Sprintf("/api/v1/clubs/%s", clubUUID), Body: updatedClub, + Role: &models.Super, }.TestOnStatusAndDB(t, &appAssert, h.TesterWithStatus{ Status: fiber.StatusOK, @@ -328,10 +334,11 @@ func TestUpdateClubWorks(t *testing.T) { ).Close() } +// TODO: need to be able to join the club to try to update func TestUpdateClubFailsOnInvalidBody(t *testing.T) { appAssert, studentUUID, clubUUID := CreateSampleClub(t, nil) - body := SampleClubFactory(studentUUID) + body := SampleClubFactory(&studentUUID) for _, invalidData := range []map[string]interface{}{ {"description": "Not a URL"}, @@ -344,6 +351,7 @@ func TestUpdateClubFailsOnInvalidBody(t *testing.T) { Method: fiber.MethodPatch, Path: fmt.Sprintf("/api/v1/clubs/%s", clubUUID), Body: &invalidData, + Role: &models.Super, }.TestOnErrorAndDB(t, &appAssert, h.ErrorWithTester{ Error: errors.FailedToValidateClub, @@ -399,20 +407,22 @@ func TestUpdateClubFailsBadRequest(t *testing.T) { Method: fiber.MethodPatch, Path: fmt.Sprintf("/api/v1/clubs/%s", badRequest), Body: h.SampleStudentJSONFactory(sampleStudent, rawPassword), - }.TestOnError(t, nil, errors.FailedToValidateID).Close() + Role: &models.Super, + }.TestOnError(t, nil, errors.FailedToParseUUID).Close() } } +// TODO: should this be unauthorized or not found? func TestUpdateClubFailsOnClubIdNotExist(t *testing.T) { - appAssert, studentUUID, _ := CreateSampleStudent(t, nil) - uuid := uuid.New() h.TestRequest{ - Method: fiber.MethodPatch, - Path: fmt.Sprintf("/api/v1/clubs/%s", uuid), - Body: SampleClubFactory(studentUUID), - }.TestOnErrorAndDB(t, &appAssert, + Method: fiber.MethodPatch, + Path: fmt.Sprintf("/api/v1/clubs/%s", uuid), + Body: SampleClubFactory(nil), + Role: &models.Student, + TestUserIDRequired: h.BoolToPointer(true), + }.TestOnErrorAndDB(t, nil, h.ErrorWithTester{ Error: errors.ClubNotFound, Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) { @@ -426,12 +436,14 @@ func TestUpdateClubFailsOnClubIdNotExist(t *testing.T) { ).Close() } +// TODO: need to be able to join the club func TestDeleteClubWorks(t *testing.T) { appAssert, _, clubUUID := CreateSampleClub(t, nil) h.TestRequest{ Method: fiber.MethodDelete, Path: fmt.Sprintf("/api/v1/clubs/%s", clubUUID), + Role: &models.Super, }.TestOnStatusAndDB(t, &appAssert, h.TesterWithStatus{ Status: fiber.StatusNoContent, @@ -440,14 +452,16 @@ func TestDeleteClubWorks(t *testing.T) { ).Close() } +// TODO: should this be unauthorized or not found? func TestDeleteClubNotExist(t *testing.T) { uuid := uuid.New() h.TestRequest{ Method: fiber.MethodDelete, Path: fmt.Sprintf("/api/v1/clubs/%s", uuid), + Role: &models.Super, }.TestOnErrorAndDB(t, nil, h.ErrorWithTester{ - Error: errors.ClubNotFound, + Error: errors.Unauthorized, Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) { var club models.Club @@ -474,6 +488,7 @@ func TestDeleteClubBadRequest(t *testing.T) { h.TestRequest{ Method: fiber.MethodDelete, Path: fmt.Sprintf("/api/v1/clubs/%s", badRequest), - }.TestOnError(t, nil, errors.FailedToValidateID).Close() + Role: &models.Super, + }.TestOnError(t, nil, errors.FailedToParseUUID).Close() } } diff --git a/backend/tests/api/helpers/auth.go b/backend/tests/api/helpers/auth.go index 1cf50a17f..76fb3033a 100644 --- a/backend/tests/api/helpers/auth.go +++ b/backend/tests/api/helpers/auth.go @@ -4,10 +4,13 @@ import ( "github.com/GenerateNU/sac/backend/src/auth" "github.com/GenerateNU/sac/backend/src/database" "github.com/GenerateNU/sac/backend/src/models" + "github.com/goccy/go-json" "github.com/gofiber/fiber/v2" + "github.com/google/uuid" ) type TestUser struct { + UUID uuid.UUID Email string Password string AccessToken string @@ -60,6 +63,7 @@ func (app *TestApp) authSuper() { } app.TestUser = &TestUser{ + UUID: database.SuperUserUUID, Email: email, Password: password, AccessToken: accessToken, @@ -70,7 +74,7 @@ func (app *TestApp) authSuper() { func (app *TestApp) authStudent() { studentUser, rawPassword := SampleStudentFactory() - _, err := app.Send(TestRequest{ + resp, err := app.Send(TestRequest{ Method: fiber.MethodPost, Path: "/api/v1/users/", Body: SampleStudentJSONFactory(studentUser, rawPassword), @@ -78,8 +82,20 @@ func (app *TestApp) authStudent() { if err != nil { panic(err) } + var respBody map[string]interface{} - resp, err := app.Send(TestRequest{ + err = json.NewDecoder(resp.Body).Decode(&respBody) + if err != nil { + panic(err) + } + + rawStudentUserUUID := respBody["id"].(string) + studentUserUUID, err := uuid.Parse(rawStudentUserUUID) + if err != nil { + panic(err) + } + + resp, err = app.Send(TestRequest{ Method: fiber.MethodPost, Path: "/api/v1/auth/login", Body: &map[string]interface{}{ @@ -107,6 +123,7 @@ func (app *TestApp) authStudent() { } app.TestUser = &TestUser{ + UUID: studentUserUUID, Email: studentUser.Email, Password: rawPassword, AccessToken: accessToken, diff --git a/backend/tests/api/helpers/database.go b/backend/tests/api/helpers/database.go index 0b8a1ca0c..2f03ecaaa 100644 --- a/backend/tests/api/helpers/database.go +++ b/backend/tests/api/helpers/database.go @@ -17,7 +17,7 @@ var ( func RootConn(dbConfig config.DatabaseSettings) { once.Do(func() { var err error - rootConn, err = database.EstablishConn(dbConfig.WithDb()) + rootConn, err = database.EstablishConn(dbConfig.WithoutDb()) if err != nil { panic(err) } diff --git a/backend/tests/api/helpers/requests.go b/backend/tests/api/helpers/requests.go index 7d8fa7c6e..e740cff49 100644 --- a/backend/tests/api/helpers/requests.go +++ b/backend/tests/api/helpers/requests.go @@ -5,6 +5,7 @@ import ( "fmt" "net/http" "net/http/httptest" + "strings" "testing" "github.com/GenerateNU/sac/backend/src/errors" @@ -16,11 +17,12 @@ import ( ) type TestRequest struct { - Method string - Path string - Body *map[string]interface{} - Headers *map[string]string - Role *models.UserRole + Method string + Path string + Body *map[string]interface{} + Headers *map[string]string + Role *models.UserRole + TestUserIDRequired *bool } func (app TestApp) Send(request TestRequest) (*http.Response, error) { @@ -28,9 +30,17 @@ func (app TestApp) Send(request TestRequest) (*http.Response, error) { var req *http.Request + if request.TestUserIDRequired != nil && *request.TestUserIDRequired { + request.Path = strings.Replace(request.Path, ":userID", app.TestUser.UUID.String(), 1) + address = fmt.Sprintf("%s%s", app.Address, request.Path) + } if request.Body == nil { req = httptest.NewRequest(request.Method, address, nil) } else { + if app.TestUser != nil && request.TestUserIDRequired != nil && *request.TestUserIDRequired { + (*request.Body)["id"] = app.TestUser.UUID + } + bodyBytes, err := json.Marshal(request.Body) if err != nil { return nil, err @@ -75,10 +85,15 @@ func (request TestRequest) Test(t *testing.T, existingAppAssert *ExistingAppAsse if request.Role != nil { app.Auth(*request.Role) } + existingAppAssert = &ExistingAppAssert{ App: app, Assert: assert, } + } else { + if existingAppAssert.App.TestUser == nil && request.Role != nil { + existingAppAssert.App.Auth(*request.Role) + } } resp, err := existingAppAssert.App.Send(request) @@ -147,13 +162,13 @@ type TesterWithStatus struct { Tester } -func (request TestRequest) TestOnStatusAndDB(t *testing.T, existingAppAssert *ExistingAppAssert, dbTesterStatus TesterWithStatus) ExistingAppAssert { +func (request TestRequest) TestOnStatusAndDB(t *testing.T, existingAppAssert *ExistingAppAssert, testerStatus TesterWithStatus) ExistingAppAssert { appAssert, resp := request.Test(t, existingAppAssert) app, assert := appAssert.App, appAssert.Assert - assert.Equal(dbTesterStatus.Status, resp.StatusCode) + assert.Equal(testerStatus.Status, resp.StatusCode) - dbTesterStatus.Tester(app, assert, resp) + testerStatus.Tester(app, assert, resp) return appAssert } diff --git a/backend/tests/api/helpers/utilities.go b/backend/tests/api/helpers/utilities.go index ccf2f7ae1..f0c0ebea8 100644 --- a/backend/tests/api/helpers/utilities.go +++ b/backend/tests/api/helpers/utilities.go @@ -39,3 +39,7 @@ func AllCasingPermutations(word string) []string { generateCasingPermutations(word, "", 0, &results) return results } + +func BoolToPointer(b bool) *bool { + return &b +} diff --git a/backend/tests/api/tag_test.go b/backend/tests/api/tag_test.go index 588badf04..dec75da99 100644 --- a/backend/tests/api/tag_test.go +++ b/backend/tests/api/tag_test.go @@ -64,6 +64,7 @@ func CreateSampleTag(t *testing.T) (appAssert h.ExistingAppAssert, categoryUUID Method: fiber.MethodPost, Path: "/api/v1/tags/", Body: SampleTagFactory(categoryUUID), + Role: &models.Super, }.TestOnStatusAndDB(t, &appAssert, h.TesterWithStatus{ Status: fiber.StatusCreated, @@ -114,6 +115,7 @@ func TestCreateTagFailsBadRequest(t *testing.T) { Method: fiber.MethodPost, Path: "/api/v1/tags/", Body: &badBody, + Role: &models.Super, }.TestOnErrorAndDB(t, nil, h.ErrorWithTester{ Error: errors.FailedToParseRequestBody, @@ -139,6 +141,7 @@ func TestCreateTagFailsValidation(t *testing.T) { Method: fiber.MethodPost, Path: "/api/v1/tags/", Body: &badBody, + Role: &models.Super, }.TestOnErrorAndDB(t, nil, h.ErrorWithTester{ Error: errors.FailedToValidateTag, @@ -154,6 +157,7 @@ func TestGetTagWorks(t *testing.T) { h.TestRequest{ Method: fiber.MethodGet, Path: fmt.Sprintf("/api/v1/tags/%s", tagUUID), + Role: &models.Super, }.TestOnStatusAndDB(t, &existingAppAssert, h.TesterWithStatus{ Status: fiber.StatusOK, @@ -177,6 +181,7 @@ func TestGetTagFailsBadRequest(t *testing.T) { h.TestRequest{ Method: fiber.MethodGet, Path: fmt.Sprintf("/api/v1/tags/%s", badRequest), + Role: &models.Super, }.TestOnError(t, nil, errors.FailedToValidateID).Close() } } @@ -185,6 +190,7 @@ func TestGetTagFailsNotFound(t *testing.T) { h.TestRequest{ Method: fiber.MethodGet, Path: fmt.Sprintf("/api/v1/tags/%s", uuid.New()), + Role: &models.Super, }.TestOnError(t, nil, errors.TagNotFound).Close() } @@ -202,6 +208,7 @@ func TestUpdateTagWorksUpdateName(t *testing.T) { Method: fiber.MethodPatch, Path: fmt.Sprintf("/api/v1/tags/%s", tagUUID), Body: &generateNUTag, + Role: &models.Super, }.TestOnStatusAndDB(t, &existingAppAssert, h.TesterWithStatus{ Status: fiber.StatusOK, @@ -226,6 +233,7 @@ func TestUpdateTagWorksUpdateCategory(t *testing.T) { Method: fiber.MethodPost, Path: "/api/v1/categories/", Body: &technologyCategory, + Role: &models.Super, }.TestOnStatusAndDB(t, &existingAppAssert, h.TesterWithStatus{ Status: fiber.StatusCreated, @@ -243,6 +251,7 @@ func TestUpdateTagWorksUpdateCategory(t *testing.T) { Method: fiber.MethodPatch, Path: fmt.Sprintf("/api/v1/tags/%s", tagUUID), Body: &technologyTag, + Role: &models.Super, }.TestOnStatusAndDB(t, &existingAppAssert, h.TesterWithStatus{ Status: fiber.StatusOK, @@ -258,6 +267,7 @@ func TestUpdateTagWorksWithSameDetails(t *testing.T) { Method: fiber.MethodPatch, Path: fmt.Sprintf("/api/v1/tags/%s", tagUUID), Body: SampleTagFactory(categoryUUID), + Role: &models.Super, }.TestOnStatusAndDB(t, &existingAppAssert, h.TesterWithStatus{ Status: fiber.StatusOK, @@ -284,6 +294,7 @@ func TestUpdateTagFailsBadRequest(t *testing.T) { Method: fiber.MethodPatch, Path: fmt.Sprintf("/api/v1/tags/%s", badRequest), Body: SampleTagFactory(uuid), + Role: &models.Super, }.TestOnError(t, &appAssert, errors.FailedToValidateID).Close() } } @@ -294,6 +305,7 @@ func TestDeleteTagWorks(t *testing.T) { h.TestRequest{ Method: fiber.MethodDelete, Path: fmt.Sprintf("/api/v1/tags/%s", tagUUID), + Role: &models.Super, }.TestOnStatusAndDB(t, &existingAppAssert, h.TesterWithStatus{ Status: fiber.StatusNoContent, @@ -317,6 +329,7 @@ func TestDeleteTagFailsBadRequest(t *testing.T) { h.TestRequest{ Method: fiber.MethodDelete, Path: fmt.Sprintf("/api/v1/tags/%s", badRequest), + Role: &models.Super, }.TestOnErrorAndDB(t, &appAssert, h.ErrorWithTester{ Error: errors.FailedToValidateID, @@ -334,6 +347,7 @@ func TestDeleteTagFailsNotFound(t *testing.T) { h.TestRequest{ Method: fiber.MethodDelete, Path: fmt.Sprintf("/api/v1/tags/%s", uuid.New()), + Role: &models.Super, }.TestOnErrorAndDB(t, &appAssert, h.ErrorWithTester{ Error: errors.TagNotFound, diff --git a/backend/tests/api/user_tag_test.go b/backend/tests/api/user_tag_test.go index 339cc7d99..0c568abaf 100644 --- a/backend/tests/api/user_tag_test.go +++ b/backend/tests/api/user_tag_test.go @@ -56,7 +56,12 @@ func SampleTagIDsFactory(tagIDs *[]uuid.UUID) *map[string]interface{} { } } -func CreateSetOfTags(t *testing.T, appAssert h.ExistingAppAssert) []uuid.UUID { +func CreateSetOfTags(t *testing.T, appAssert *h.ExistingAppAssert) ([]uuid.UUID, *h.ExistingAppAssert) { + if appAssert == nil { + app, assert := h.InitTest(t) + appAssert = &h.ExistingAppAssert{App: app, Assert: assert} + } + categories := SampleCategoriesFactory() categoryIDs := []uuid.UUID{} @@ -65,7 +70,8 @@ func CreateSetOfTags(t *testing.T, appAssert h.ExistingAppAssert) []uuid.UUID { Method: fiber.MethodPost, Path: "/api/v1/categories/", Body: &category, - }.TestOnStatusAndDB(t, &appAssert, + Role: &models.Super, + }.TestOnStatusAndDB(t, appAssert, h.TesterWithStatus{ Status: fiber.StatusCreated, Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) { @@ -89,7 +95,8 @@ func CreateSetOfTags(t *testing.T, appAssert h.ExistingAppAssert) []uuid.UUID { Method: fiber.MethodPost, Path: "/api/v1/tags/", Body: &tag, - }.TestOnStatusAndDB(t, &appAssert, + Role: &models.Super, + }.TestOnStatusAndDB(t, appAssert, h.TesterWithStatus{ Status: fiber.StatusCreated, Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) { @@ -105,7 +112,7 @@ func CreateSetOfTags(t *testing.T, appAssert h.ExistingAppAssert) []uuid.UUID { ) } - return tagIDs + return tagIDs, appAssert } func AssertUserTagsRespDB(app h.TestApp, assert *assert.A, resp *http.Response, id uuid.UUID) { @@ -154,9 +161,11 @@ func TestCreateUserTagsFailsOnInvalidDataType(t *testing.T) { malformedTag["tags"] = tag h.TestRequest{ - Method: fiber.MethodPost, - Path: "/api/v1/users/1/tags/", - Body: &malformedTag, + Method: fiber.MethodPost, + Path: "/api/v1/users/:userID/tags/", + Body: &malformedTag, + Role: &models.Student, + TestUserIDRequired: h.BoolToPointer(true), }.TestOnError(t, nil, errors.FailedToParseRequestBody).Close() } } @@ -175,7 +184,8 @@ func TestCreateUserTagsFailsOnInvalidUserID(t *testing.T) { Method: fiber.MethodPost, Path: fmt.Sprintf("/api/v1/users/%s/tags", badRequest), Body: SampleTagIDsFactory(nil), - }.TestOnError(t, nil, errors.FailedToValidateID).Close() + Role: &models.Student, + }.TestOnError(t, nil, errors.FailedToParseUUID).Close() } } @@ -184,8 +194,6 @@ type UUIDSlice []uuid.UUID var testUUID = uuid.New() func TestCreateUserTagsFailsOnInvalidKey(t *testing.T) { - appAssert, uuid, _ := CreateSampleStudent(t, nil) - invalidBody := []map[string]interface{}{ { "tag": UUIDSlice{testUUID, testUUID}, @@ -197,39 +205,53 @@ func TestCreateUserTagsFailsOnInvalidKey(t *testing.T) { for _, body := range invalidBody { h.TestRequest{ - Method: fiber.MethodPost, - Path: fmt.Sprintf("/api/v1/users/%s/tags/", uuid), - Body: &body, - }.TestOnError(t, &appAssert, errors.FailedToValidateUserTags) + Method: fiber.MethodPost, + Path: "/api/v1/users/:userID/tags/", + Body: &body, + Role: &models.Student, + TestUserIDRequired: h.BoolToPointer(true), + }.TestOnError(t, nil, errors.FailedToValidateUserTags).Close() } - - appAssert.Close() } +// TODO: should this be unauthorized or not found? func TestCreateUserTagsFailsOnNonExistentUser(t *testing.T) { + uuid := uuid.New() + h.TestRequest{ Method: fiber.MethodPost, - Path: fmt.Sprintf("/api/v1/users/%s/tags", uuid.New()), + Path: fmt.Sprintf("/api/v1/users/%s/tags/", uuid), Body: SampleTagIDsFactory(nil), - }.TestOnError(t, nil, errors.UserNotFound).Close() + Role: &models.Super, + }.TestOnErrorAndDB(t, nil, + h.ErrorWithTester{ + Error: errors.UserNotFound, + Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) { + var dbUser models.User + err := app.Conn.First(&dbUser, uuid).Error + + assert.Error(err) + }, + }, + ) } func TestCreateUserTagsWorks(t *testing.T) { - appAssert, uuid, _ := CreateSampleStudent(t, nil) - // Create a set of tags: - tagUUIDs := CreateSetOfTags(t, appAssert) + tagUUIDs, appAssert := CreateSetOfTags(t, nil) // Confirm adding real tags adds them to the user: h.TestRequest{ - Method: fiber.MethodPost, - Path: fmt.Sprintf("/api/v1/users/%s/tags/", uuid), - Body: SampleTagIDsFactory(&tagUUIDs), - }.TestOnStatusAndDB(t, &appAssert, + Method: fiber.MethodPost, + Path: "/api/v1/users/:userID/tags/", + Body: SampleTagIDsFactory(&tagUUIDs), + Role: &models.Super, + TestUserIDRequired: h.BoolToPointer(true), + }.TestOnStatusAndDB(t, appAssert, h.TesterWithStatus{ Status: fiber.StatusCreated, Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) { - AssertSampleUserTagsRespDB(app, assert, resp, uuid) + AssertSampleUserTagsRespDB(app, assert, resp, app.TestUser.UUID) }, }, ) @@ -238,13 +260,13 @@ func TestCreateUserTagsWorks(t *testing.T) { } func TestCreateUserTagsNoneAddedIfInvalid(t *testing.T) { - appAssert, uuid, _ := CreateSampleStudent(t, nil) - h.TestRequest{ - Method: fiber.MethodPost, - Path: fmt.Sprintf("/api/v1/users/%s/tags/", uuid), - Body: SampleTagIDsFactory(nil), - }.TestOnStatusAndDB(t, &appAssert, + Method: fiber.MethodPost, + Path: "/api/v1/users/:userID/tags/", + Body: SampleTagIDsFactory(nil), + Role: &models.Super, + TestUserIDRequired: h.BoolToPointer(true), + }.TestOnStatusAndDB(t, nil, h.TesterWithStatus{ Status: fiber.StatusCreated, Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) { @@ -257,27 +279,27 @@ func TestCreateUserTagsNoneAddedIfInvalid(t *testing.T) { assert.Equal(len(respTags), 0) }, }, - ) - - appAssert.Close() + ).Close() } +// TODO: should this be unauthorized or not found? func TestGetUserTagsFailsOnNonExistentUser(t *testing.T) { h.TestRequest{ Method: fiber.MethodGet, Path: fmt.Sprintf("/api/v1/users/%s/tags/", uuid.New()), + Role: &models.Super, }.TestOnError(t, nil, errors.UserNotFound).Close() } func TestGetUserTagsReturnsEmptyListWhenNoneAdded(t *testing.T) { - appAssert, uuid, _ := CreateSampleStudent(t, nil) - h.TestRequest{ - Method: fiber.MethodGet, - Path: fmt.Sprintf("/api/v1/users/%s/tags/", uuid), - }.TestOnStatusAndDB(t, &appAssert, + Method: fiber.MethodGet, + Path: "/api/v1/users/:userID/tags/", + Role: &models.Student, + TestUserIDRequired: h.BoolToPointer(true), + }.TestOnStatusAndDB(t, nil, h.TesterWithStatus{ - Status: 200, + Status: fiber.StatusOK, Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) { var respTags []models.Tag @@ -288,33 +310,33 @@ func TestGetUserTagsReturnsEmptyListWhenNoneAdded(t *testing.T) { assert.Equal(len(respTags), 0) }, }, - ) - - appAssert.Close() + ).Close() } func TestGetUserTagsReturnsCorrectList(t *testing.T) { - appAssert, uuid, _ := CreateSampleStudent(t, nil) - // Create a set of tags: - tagUUIDs := CreateSetOfTags(t, appAssert) + tagUUIDs, appAssert := CreateSetOfTags(t, nil) // Add the tags: h.TestRequest{ - Method: fiber.MethodPost, - Path: fmt.Sprintf("/api/v1/users/%s/tags/", uuid), - Body: SampleTagIDsFactory(&tagUUIDs), - }.TestOnStatus(t, &appAssert, fiber.StatusCreated) + Method: fiber.MethodPost, + Path: "/api/v1/users/:userID/tags/", + Body: SampleTagIDsFactory(&tagUUIDs), + Role: &models.Student, + TestUserIDRequired: h.BoolToPointer(true), + }.TestOnStatus(t, appAssert, fiber.StatusCreated) // Get the tags: h.TestRequest{ - Method: fiber.MethodGet, - Path: fmt.Sprintf("/api/v1/users/%s/tags/", uuid), - }.TestOnStatusAndDB(t, &appAssert, + Method: fiber.MethodGet, + Path: "/api/v1/users/:userID/tags/", + Role: &models.Student, + TestUserIDRequired: h.BoolToPointer(true), + }.TestOnStatusAndDB(t, appAssert, h.TesterWithStatus{ Status: fiber.StatusOK, Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) { - AssertSampleUserTagsRespDB(app, assert, resp, uuid) + AssertSampleUserTagsRespDB(app, assert, resp, app.TestUser.UUID) }, }, ) diff --git a/backend/tests/api/user_test.go b/backend/tests/api/user_test.go index a9df2fd85..ebd8f8eea 100644 --- a/backend/tests/api/user_test.go +++ b/backend/tests/api/user_test.go @@ -69,12 +69,12 @@ func TestGetUsersFailsForStudent(t *testing.T) { } func TestGetUserWorks(t *testing.T) { - appAssert, uuid, _ := CreateSampleStudent(t, nil) - h.TestRequest{ - Method: fiber.MethodGet, - Path: fmt.Sprintf("/api/v1/users/%s", uuid), - }.TestOnStatusAndDB(t, &appAssert, + Method: fiber.MethodGet, + Path: "/api/v1/users/:userID", + Role: &models.Student, + TestUserIDRequired: h.BoolToPointer(true), + }.TestOnStatusAndDB(t, nil, h.TesterWithStatus{ Status: fiber.StatusOK, Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) { @@ -95,7 +95,7 @@ func TestGetUserWorks(t *testing.T) { assert.Equal(models.College(sampleUser["college"].(string)), respUser.College) assert.Equal(models.Year(sampleUser["year"].(int)), respUser.Year) - dbUser, err := transactions.GetUser(app.Conn, uuid) + dbUser, err := transactions.GetUser(app.Conn, app.TestUser.UUID) assert.NilError(&err) @@ -118,16 +118,19 @@ func TestGetUserFailsBadRequest(t *testing.T) { h.TestRequest{ Method: fiber.MethodGet, Path: fmt.Sprintf("/api/v1/users/%s", badRequest), - }.TestOnError(t, nil, errors.FailedToValidateID).Close() + Role: &models.Super, + }.TestOnError(t, nil, errors.FailedToParseUUID).Close() } } +// TODO: should this be not found or unauthorized? func TestGetUserFailsNotExist(t *testing.T) { uuid := uuid.New() h.TestRequest{ Method: fiber.MethodGet, Path: fmt.Sprintf("/api/v1/users/%s", uuid), + Role: &models.Super, }.TestOnErrorAndDB(t, nil, h.ErrorWithTester{ Error: errors.UserNotFound, @@ -142,20 +145,21 @@ func TestGetUserFailsNotExist(t *testing.T) { ).Close() } +// TODO: should this be unathorized or be allowed? func TestUpdateUserWorks(t *testing.T) { - appAssert, uuid, _ := CreateSampleStudent(t, nil) - newFirstName := "Michael" newLastName := "Brennan" h.TestRequest{ Method: fiber.MethodPatch, - Path: fmt.Sprintf("/api/v1/users/%s", uuid), + Path: "/api/v1/users/:userID", Body: &map[string]interface{}{ "first_name": newFirstName, "last_name": newLastName, }, - }.TestOnStatusAndDB(t, &appAssert, + Role: &models.Student, + TestUserIDRequired: h.BoolToPointer(true), + }.TestOnStatusAndDB(t, nil, h.TesterWithStatus{ Status: fiber.StatusOK, Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) { @@ -178,7 +182,7 @@ func TestUpdateUserWorks(t *testing.T) { var dbUser models.User - err = app.Conn.First(&dbUser, uuid).Error + err = app.Conn.First(&dbUser, app.TestUser.UUID).Error assert.NilError(err) @@ -193,9 +197,8 @@ func TestUpdateUserWorks(t *testing.T) { ).Close() } +// TODO: should this be unauthorized or fail on processing request func TestUpdateUserFailsOnInvalidBody(t *testing.T) { - appAssert, uuid, _ := CreateSampleStudent(t, nil) - for _, invalidData := range []map[string]interface{}{ {"email": "not.northeastern@gmail.com"}, {"nuid": "1800-123-4567"}, @@ -204,17 +207,18 @@ func TestUpdateUserFailsOnInvalidBody(t *testing.T) { {"college": "UT-Austin"}, } { h.TestRequest{ - Method: fiber.MethodPatch, - Path: fmt.Sprintf("/api/v1/users/%s", uuid), - Body: &invalidData, - }.TestOnErrorAndDB(t, &appAssert, + Method: fiber.MethodPatch, + Path: "/api/v1/users/:userID", + Body: &invalidData, + Role: &models.Student, + TestUserIDRequired: h.BoolToPointer(true), + }.TestOnErrorAndDB(t, nil, h.ErrorWithTester{ Error: errors.FailedToValidateUser, Tester: TestNumUsersRemainsAt2, }, - ) + ).Close() } - appAssert.Close() } func TestUpdateUserFailsBadRequest(t *testing.T) { @@ -233,10 +237,12 @@ func TestUpdateUserFailsBadRequest(t *testing.T) { Method: fiber.MethodPatch, Path: fmt.Sprintf("/api/v1/users/%s", badRequest), Body: h.SampleStudentJSONFactory(sampleStudent, rawPassword), - }.TestOnError(t, nil, errors.FailedToValidateID).Close() + Role: &models.Student, + }.TestOnError(t, nil, errors.FailedToParseUUID).Close() } } +// TODO: should this be unauthorized or not found? func TestUpdateUserFailsOnIdNotExist(t *testing.T) { uuid := uuid.New() @@ -246,6 +252,7 @@ func TestUpdateUserFailsOnIdNotExist(t *testing.T) { Method: fiber.MethodPatch, Path: fmt.Sprintf("/api/v1/users/%s", uuid), Body: h.SampleStudentJSONFactory(sampleStudent, rawPassword), + Role: &models.Super, }.TestOnErrorAndDB(t, nil, h.ErrorWithTester{ Error: errors.UserNotFound, @@ -260,13 +267,14 @@ func TestUpdateUserFailsOnIdNotExist(t *testing.T) { ).Close() } +// TODO: should this be unauthorized? func TestDeleteUserWorks(t *testing.T) { - appAssert, uuid, _ := CreateSampleStudent(t, nil) - h.TestRequest{ - Method: fiber.MethodDelete, - Path: fmt.Sprintf("/api/v1/users/%s", uuid), - }.TestOnStatusAndDB(t, &appAssert, + Method: fiber.MethodDelete, + Path: "/api/v1/users/:userID", + Role: &models.Student, + TestUserIDRequired: h.BoolToPointer(true), + }.TestOnStatusAndDB(t, nil, h.TesterWithStatus{ Status: fiber.StatusNoContent, Tester: TestNumUsersRemainsAt1, @@ -274,11 +282,14 @@ func TestDeleteUserWorks(t *testing.T) { ).Close() } +// TODO: how should this work now? func TestDeleteUserNotExist(t *testing.T) { uuid := uuid.New() h.TestRequest{ - Method: fiber.MethodDelete, - Path: fmt.Sprintf("/api/v1/users/%s", uuid), + Method: fiber.MethodDelete, + Path: fmt.Sprintf("/api/v1/users/%s", uuid), + Role: &models.Super, + TestUserIDRequired: h.BoolToPointer(true), }.TestOnErrorAndDB(t, nil, h.ErrorWithTester{ Error: errors.UserNotFound, @@ -308,9 +319,10 @@ func TestDeleteUserBadRequest(t *testing.T) { h.TestRequest{ Method: fiber.MethodDelete, Path: fmt.Sprintf("/api/v1/users/%s", badRequest), + Role: &models.Super, }.TestOnErrorAndDB(t, nil, h.ErrorWithTester{ - Error: errors.FailedToValidateID, + Error: errors.FailedToParseUUID, Tester: TestNumUsersRemainsAt1, }, ) @@ -372,6 +384,7 @@ func CreateSampleStudent(t *testing.T, existingAppAssert *h.ExistingAppAssert) ( Method: fiber.MethodPost, Path: "/api/v1/users/", Body: h.SampleStudentJSONFactory(sampleStudent, rawPassword), + Role: &models.Super, }.TestOnStatusAndDB(t, existingAppAssert, h.TesterWithStatus{ Status: fiber.StatusCreated, @@ -420,6 +433,7 @@ func TestCreateUserFailsIfUserWithEmailAlreadyExists(t *testing.T) { Method: fiber.MethodPost, Path: "/api/v1/users/", Body: body, + Role: &models.Super, }.TestOnErrorAndDB(t, &appAssert, h.ErrorWithTester{ Error: errors.UserAlreadyExists, @@ -445,6 +459,7 @@ func TestCreateUserFailsIfUserWithNUIDAlreadyExists(t *testing.T) { Method: fiber.MethodPost, Path: "/api/v1/users/", Body: slightlyDifferentSampleStudentJSON, + Role: &models.Super, }.TestOnErrorAndDB(t, &appAssert, h.ErrorWithTester{ Error: errors.UserAlreadyExists, @@ -466,6 +481,7 @@ func AssertCreateBadDataFails(t *testing.T, jsonKey string, badValues []interfac Method: fiber.MethodPost, Path: "/api/v1/users/", Body: &sampleUserPermutation, + Role: &models.Super, }.TestOnErrorAndDB(t, &appAssert, h.ErrorWithTester{ Error: errors.FailedToValidateUser, @@ -557,6 +573,7 @@ func TestCreateUserFailsOnMissingFields(t *testing.T) { Method: fiber.MethodPost, Path: "/api/v1/users/", Body: &sampleUserPermutation, + Role: &models.Super, }.TestOnErrorAndDB(t, &appAssert, h.ErrorWithTester{ Error: errors.FailedToValidateUser,