From 33f6c2ae3f4384ef7c3f59bd747851593798042b Mon Sep 17 00:00:00 2001 From: Michael-u21546551 Date: Sat, 27 Jul 2024 18:05:11 +0200 Subject: [PATCH] =?UTF-8?q?test:=20wrote=20some=20more=20tests=20for=20cod?= =?UTF-8?q?ecov,=20now=20500=20passing=20tests=F0=9F=A7=AA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build-python.yml | 2 +- .github/workflows/deploy-docs.yml | 2 +- .github/workflows/deploy-golang-develop.yml | 2 +- .github/workflows/deploy-golang-prod.yml | 2 +- .github/workflows/deploy-landing-page.yml | 2 +- .github/workflows/deploy-python.yml | 2 +- .github/workflows/deploy-web-develop.yml | 2 +- .github/workflows/deploy-web-prod.yml | 2 +- .github/workflows/lint-test-build-golang.yml | 2 +- .github/workflows/lint-test-build-web.yml | 2 +- .github/workflows/lint-test-mobile.yml | 2 +- .github/workflows/test-and-cov.yml | 2 +- occupi-backend/pkg/database/database.go | 18 +- occupi-backend/pkg/handlers/api_handlers.go | 2 +- occupi-backend/pkg/handlers/auth_helpers.go | 7 + occupi-backend/tests/cache_test.go | 194 +++++++ occupi-backend/tests/database_test.go | 549 +++++++++++++++++++ 17 files changed, 767 insertions(+), 27 deletions(-) diff --git a/.github/workflows/build-python.yml b/.github/workflows/build-python.yml index 9df2cde0..88c1999a 100644 --- a/.github/workflows/build-python.yml +++ b/.github/workflows/build-python.yml @@ -1,4 +1,4 @@ -name: Build Python App +name: Build Python App 🏗️ on: pull_request: diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml index da21d9d6..fc9d68a7 100644 --- a/.github/workflows/deploy-docs.yml +++ b/.github/workflows/deploy-docs.yml @@ -1,4 +1,4 @@ -name: Deploy Docs site to Live site +name: Build 🏗️ and Deploy Documentation 🛳️ on: push: diff --git a/.github/workflows/deploy-golang-develop.yml b/.github/workflows/deploy-golang-develop.yml index 35049a57..6549a8dd 100644 --- a/.github/workflows/deploy-golang-develop.yml +++ b/.github/workflows/deploy-golang-develop.yml @@ -1,4 +1,4 @@ -name: Build and Deploy Dev golang +name: Build 🏗️ and Deploy Golang App 🛳️ to Develop 🚈 on: push: diff --git a/.github/workflows/deploy-golang-prod.yml b/.github/workflows/deploy-golang-prod.yml index 3ca978a5..b9a18663 100644 --- a/.github/workflows/deploy-golang-prod.yml +++ b/.github/workflows/deploy-golang-prod.yml @@ -1,4 +1,4 @@ -name: Build and Deploy Prod golang +name: Build🏗️ and Deploy Golang App 🛳️ to Prod 🚝 on: push: diff --git a/.github/workflows/deploy-landing-page.yml b/.github/workflows/deploy-landing-page.yml index 2de5b755..648b6502 100644 --- a/.github/workflows/deploy-landing-page.yml +++ b/.github/workflows/deploy-landing-page.yml @@ -1,4 +1,4 @@ -name: Deploy Landing page +name: Build 🏗️ and Deploy Landing Page 🛳️ on: push: diff --git a/.github/workflows/deploy-python.yml b/.github/workflows/deploy-python.yml index 0bbd490c..05940986 100644 --- a/.github/workflows/deploy-python.yml +++ b/.github/workflows/deploy-python.yml @@ -1,4 +1,4 @@ -name: Build and Deploy Python App +name: Build 🏗️ and Deploy Python App 🛳️ on: push: diff --git a/.github/workflows/deploy-web-develop.yml b/.github/workflows/deploy-web-develop.yml index c60ac9b5..e966bb20 100644 --- a/.github/workflows/deploy-web-develop.yml +++ b/.github/workflows/deploy-web-develop.yml @@ -1,4 +1,4 @@ -name: Deploy Develop Dashboard +name: Build 🏗️ and Deploy Web 🛳️ to Develop 🚈 on: push: diff --git a/.github/workflows/deploy-web-prod.yml b/.github/workflows/deploy-web-prod.yml index 3c0ec2f8..e6917e3f 100644 --- a/.github/workflows/deploy-web-prod.yml +++ b/.github/workflows/deploy-web-prod.yml @@ -1,4 +1,4 @@ -name: Deploy Prod Dashboard +name: Build 🏗️ and Deploy 🛳️ Web App to Production 🚝 on: push: diff --git a/.github/workflows/lint-test-build-golang.yml b/.github/workflows/lint-test-build-golang.yml index bb2df726..26cf8e3a 100644 --- a/.github/workflows/lint-test-build-golang.yml +++ b/.github/workflows/lint-test-build-golang.yml @@ -1,4 +1,4 @@ -name: Lint, Test and Build golang +name: Lint🌸, Test🧪 and Build🏗️ golang on: pull_request: diff --git a/.github/workflows/lint-test-build-web.yml b/.github/workflows/lint-test-build-web.yml index b74d0ad7..1d8e3c15 100644 --- a/.github/workflows/lint-test-build-web.yml +++ b/.github/workflows/lint-test-build-web.yml @@ -1,4 +1,4 @@ -name: Lint, Test, Build Web +name: Lint🌸, Test🧪, Build🏗️ Web on: pull_request: diff --git a/.github/workflows/lint-test-mobile.yml b/.github/workflows/lint-test-mobile.yml index 62c13f54..3298f2c1 100644 --- a/.github/workflows/lint-test-mobile.yml +++ b/.github/workflows/lint-test-mobile.yml @@ -1,4 +1,4 @@ -name: Lint and Test Mobile +name: Lint🌸 and Test🧪 Mobile on: pull_request: diff --git a/.github/workflows/test-and-cov.yml b/.github/workflows/test-and-cov.yml index ee25f31a..1ae8eba4 100644 --- a/.github/workflows/test-and-cov.yml +++ b/.github/workflows/test-and-cov.yml @@ -1,4 +1,4 @@ -name: Test Web, Mobile and API and generate coverage report +name: Test🧪 Web💻, Mobile📱 and API🔌 and generate coverage report📋 on: push: diff --git a/occupi-backend/pkg/database/database.go b/occupi-backend/pkg/database/database.go index 1a113ca4..8af745a8 100644 --- a/occupi-backend/pkg/database/database.go +++ b/occupi-backend/pkg/database/database.go @@ -87,10 +87,10 @@ func ConfirmCheckIn(ctx *gin.Context, appsession *models.AppSession, checkIn mod // Save the check-in to the database collection := appsession.DB.Database(configs.GetMongoDBName()).Collection("RoomBooking") - // Find the booking by bookingId, roomId, and creator + // Find the booking by bookingId, occupiId, and creator filter := bson.M{ - "_id": checkIn.BookingID, - "creator": checkIn.Creator, + "occupiId": checkIn.BookingID, + "creator": checkIn.Creator, } update := bson.M{"$set": bson.M{"checkedIn": true}} @@ -154,7 +154,7 @@ func BookingExists(ctx *gin.Context, appsession *models.AppSession, id string) b // Check if the booking exists in the database collection := appsession.DB.Database(configs.GetMongoDBName()).Collection("RoomBooking") - filter := bson.M{"_id": id} + filter := bson.M{"occupiId": id} var existingbooking models.Booking err := collection.FindOne(ctx, filter).Decode(&existingbooking) if err != nil { @@ -395,11 +395,6 @@ func CheckIfNextVerificationDateIsDue(ctx *gin.Context, appsession *models.AppSe if !time.Now().After(userData.NextVerificationDate) { return false, nil } - _, err := UpdateVerificationStatusTo(ctx, appsession, email, false) - if err != nil { - logrus.Error(err) - return false, err - } return true, nil } @@ -419,11 +414,6 @@ func CheckIfNextVerificationDateIsDue(ctx *gin.Context, appsession *models.AppSe if !time.Now().After(user.NextVerificationDate) { return false, nil } - _, err = UpdateVerificationStatusTo(ctx, appsession, email, false) - if err != nil { - logrus.Error(err) - return false, err - } return true, nil } diff --git a/occupi-backend/pkg/handlers/api_handlers.go b/occupi-backend/pkg/handlers/api_handlers.go index 088c1af9..01d9adf7 100644 --- a/occupi-backend/pkg/handlers/api_handlers.go +++ b/occupi-backend/pkg/handlers/api_handlers.go @@ -154,7 +154,7 @@ func BookRoom(ctx *gin.Context, appsession *models.AppSession) { return } - ctx.JSON(http.StatusOK, utils.SuccessResponse(http.StatusOK, "Successfully booked!", booking.RoomID)) + ctx.JSON(http.StatusOK, utils.SuccessResponse(http.StatusOK, "Successfully booked!", booking.OccupiID)) } func CancelBooking(ctx *gin.Context, appsession *models.AppSession) { diff --git a/occupi-backend/pkg/handlers/auth_helpers.go b/occupi-backend/pkg/handlers/auth_helpers.go index 8c9702ca..9d87a3aa 100644 --- a/occupi-backend/pkg/handlers/auth_helpers.go +++ b/occupi-backend/pkg/handlers/auth_helpers.go @@ -334,6 +334,13 @@ func PreLoginAccountChecks(ctx *gin.Context, appsession *models.AppSession, emai return false, err } + // update verification status in database to false + _, err = database.UpdateVerificationStatusTo(ctx, appsession, email, false) + if err != nil { + ctx.JSON(http.StatusInternalServerError, utils.InternalServerError()) + return false, err + } + // check if the users ip address is logging in from a known location isIPValid, unrecognizedLogger, err := database.CheckIfUserIsLoggingInFromKnownLocation(ctx, appsession, email, utils.GetClientIP(ctx)) diff --git a/occupi-backend/tests/cache_test.go b/occupi-backend/tests/cache_test.go index d1c67b86..d4c03145 100644 --- a/occupi-backend/tests/cache_test.go +++ b/occupi-backend/tests/cache_test.go @@ -16,6 +16,105 @@ import ( "github.com/COS301-SE-2024/occupi/occupi-backend/pkg/models" ) +func TestSaveBooking_WithCache(t *testing.T) { + // Create database connection and Cache + db := configs.ConnectToDatabase(constants.AdminDBAccessOption) + Cache := configs.CreateCache() + + // Create a new ResponseRecorder (which satisfies http.ResponseWriter) to record the response. + w := httptest.NewRecorder() + + // Create a response writer and context + ctx, _ := gin.CreateTestContext(w) + + // Create a new AppSession with the Cache + appSession := &models.AppSession{ + DB: db, + Cache: Cache, + } + + booking := models.Booking{ + OccupiID: "OCCUPI01", + } + + success, err := database.SaveBooking(ctx, appSession, booking) + assert.True(t, success) + assert.Nil(t, err) + + // Verify the booking is in the Cache + cachedBooking1, err := Cache.Get(cache.RoomBookingKey(booking.OccupiID)) + assert.Nil(t, err) + assert.NotNil(t, cachedBooking1) + + // sleep for 2 * Cache expiry time to ensure the Cache expires + time.Sleep(time.Duration(configs.GetCacheEviction()) * 2 * time.Second) + + // Verify the booking is not in the Cache + cachedBooking2, err := Cache.Get(cache.UserKey(booking.OccupiID)) + assert.NotNil(t, err) + assert.Nil(t, cachedBooking2) +} + +func TestConfirmCheckin_WithCache(t *testing.T) { + // Create database connection and Cache + db := configs.ConnectToDatabase(constants.AdminDBAccessOption) + Cache := configs.CreateCache() + + // Create a new ResponseRecorder (which satisfies http.ResponseWriter) to record the response. + w := httptest.NewRecorder() + + // Create a response writer and context + ctx, _ := gin.CreateTestContext(w) + + // Create a new AppSession with the Cache + appSession := &models.AppSession{ + DB: db, + Cache: Cache, + } + + checkin := models.CheckIn{ + BookingID: "ROOM01", + Creator: "TestConfirmCheckin_WithCache@example.com", + } + + booking := models.Booking{ + OccupiID: checkin.BookingID, + Creator: checkin.Creator, + CheckedIn: false, + } + + collection := db.Database(configs.GetMongoDBName()).Collection("RoomBooking") + _, err := collection.InsertOne(ctx, booking) + + assert.Nil(t, err) + + // marshall and add the booking to cache + bookingData, err := bson.Marshal(booking) + + assert.Nil(t, err) + + err = Cache.Set(cache.RoomBookingKey(booking.OccupiID), bookingData) + + assert.Nil(t, err) + + success, err := database.ConfirmCheckIn(ctx, appSession, checkin) + assert.True(t, success) + assert.Nil(t, err) + + // Verify the booking is in the Cache + cachedBooking1, err := Cache.Get(cache.RoomBookingKey(checkin.BookingID)) + assert.Nil(t, err) + assert.NotNil(t, cachedBooking1) + + // sleep for 2 * Cache expiry time to ensure the Cache expires + time.Sleep(time.Duration(configs.GetCacheEviction()) * 2 * time.Second) + + // Verify the booking is not in the Cache + cachedBooking2, err := Cache.Get(cache.UserKey(checkin.BookingID)) + assert.NotNil(t, err) + assert.Nil(t, cachedBooking2) +} + func TestEmailExistsPerformance(t *testing.T) { email := "TestEmailExistsPerformance@example.com" @@ -112,6 +211,101 @@ func TestEmailExists_WithCache(t *testing.T) { assert.NotNil(t, cachedUser) } +func TestBookingExistsPerformance(t *testing.T) { + id := "OCCUPI0101" + + // Create database connection and Cache + db := configs.ConnectToDatabase(constants.AdminDBAccessOption) + Cache := configs.CreateCache() + + // Create a new ResponseRecorder (which satisfies http.ResponseWriter) to record the response. + w := httptest.NewRecorder() + + // Create a response writer and context + ctx, _ := gin.CreateTestContext(w) + + // Create a new AppSession with the Cache + appsessionWithCache := &models.AppSession{ + DB: db, + Cache: Cache, + } + // Create a new AppSession without the Cache + appsessionWithoutCache := &models.AppSession{ + DB: db, + Cache: nil, + } + + // Mock the DB response + collection := db.Database(configs.GetMongoDBName()).Collection("RoomBooking") + bookingStruct := models.Booking{ + OccupiID: id, + } + _, err := collection.InsertOne(ctx, bookingStruct) + if err != nil { + t.Fatalf("Failed to insert test booking into database: %v", err) + } + + // Test performance with Cache + startTime := time.Now() + for i := 0; i < 1000; i++ { + database.BookingExists(ctx, appsessionWithCache, id) + } + durationWithCache := time.Since(startTime) + + // Test performance without Cache + startTime = time.Now() + for i := 0; i < 1000; i++ { + database.BookingExists(ctx, appsessionWithoutCache, id) + } + durationWithoutCache := time.Since(startTime) + + // Assert that the Cache improves the speed + if durationWithoutCache <= durationWithCache { + t.Errorf("Cache did not improve performance: duration with Cache %v, duration without Cache %v", durationWithCache, durationWithoutCache) + } +} + +func TestBookingExists_WithCache(t *testing.T) { + id := "OCCUPI0101" + // Create database connection and Cache + db := configs.ConnectToDatabase(constants.AdminDBAccessOption) + Cache := configs.CreateCache() + + // Create a new ResponseRecorder (which satisfies http.ResponseWriter) to record the response. + w := httptest.NewRecorder() + + // Create a response writer and context + ctx, _ := gin.CreateTestContext(w) + + // Create a new AppSession with the Cache + appSession := &models.AppSession{ + DB: db, + Cache: Cache, + } + + // Mock the DB response + collection := db.Database(configs.GetMongoDBName()).Collection("RoomBooking") + bookingStruct := models.Booking{ + OccupiID: id, + } + _, err := collection.InsertOne(ctx, bookingStruct) + if err != nil { + t.Fatalf("Failed to insert test booking into database: %v", err) + } + + // call the function to test + exists := database.BookingExists(ctx, appSession, id) + + // Verify the response + assert.True(t, exists) + + // Verify the booking is in the Cache + cachedBooking, err := Cache.Get(cache.RoomBookingKey(id)) + + assert.Nil(t, err) + assert.NotNil(t, cachedBooking) +} + func TestAddUser_WithCache(t *testing.T) { // Create database connection and Cache db := configs.ConnectToDatabase(constants.AdminDBAccessOption) diff --git a/occupi-backend/tests/database_test.go b/occupi-backend/tests/database_test.go index 88d1e55a..e4c68699 100644 --- a/occupi-backend/tests/database_test.go +++ b/occupi-backend/tests/database_test.go @@ -131,6 +131,212 @@ func TestGetAllData(t *testing.T) { }) } +func TestSaveBooking(t *testing.T) { + // Setup mock MongoDB instance + mt := mtest.New(t, mtest.NewOptions().ClientType(mtest.Mock)) + + gin.SetMode(configs.GetGinRunMode()) + + // Create a new HTTP request with the POST method. + req, _ := http.NewRequest("POST", "/", nil) + + // Create a new ResponseRecorder (which satisfies http.ResponseWriter) to record the response. + w := httptest.NewRecorder() + + // Create a new context with the Request and ResponseWriter. + ctx, _ := gin.CreateTestContext(w) + ctx.Request = req + + // Optionally, set any values in the context. + ctx.Set("test", "test") + + booking := models.Booking{ + OccupiID: "OCCUPI01", + } + + mt.Run("Nil database", func(mt *mtest.T) { + // Call the function under test + appsession := &models.AppSession{} + success, err := database.SaveBooking(ctx, appsession, booking) + + // Validate the result + assert.Error(t, err) + assert.False(t, success) + }) + + mt.Run("Add room successfully", func(mt *mtest.T) { + mt.AddMockResponses(mtest.CreateSuccessResponse()) + + // Call the function under test + appsession := &models.AppSession{ + DB: mt.Client, + } + success, err := database.SaveBooking(ctx, appsession, booking) + + // Validate the result + assert.NoError(t, err) + assert.True(t, success) + }) + + mt.Run("Add room successfully to Cache", func(mt *mtest.T) { + mt.AddMockResponses(mtest.CreateSuccessResponse()) + + Cache := configs.CreateCache() + + appsession := &models.AppSession{ + DB: mt.Client, + Cache: Cache, + } + + // Call the function under test + success, err := database.SaveBooking(ctx, appsession, booking) + + // Validate the result + assert.NoError(t, err) + assert.True(t, success) + + // Verify the room was added to the Cache + roomv, err := Cache.Get(cache.RoomBookingKey(booking.OccupiID)) + + assert.Nil(t, err) + assert.NotNil(t, roomv) + }) + + mt.Run("InsertOne error", func(mt *mtest.T) { + mt.AddMockResponses(mtest.CreateCommandErrorResponse(mtest.CommandError{ + Code: 11000, + Message: "duplicate key error", + })) + + // Call the function under test + appsession := &models.AppSession{ + DB: mt.Client, + } + success, err := database.SaveBooking(ctx, appsession, booking) + + // Validate the result + assert.Error(t, err) + assert.False(t, success) + }) +} + +func TestConfirmCheckIn(t *testing.T) { + // Setup mock MongoDB instance + mt := mtest.New(t, mtest.NewOptions().ClientType(mtest.Mock)) + + gin.SetMode(configs.GetGinRunMode()) + + // Create a new HTTP request with the POST method. + req, _ := http.NewRequest("POST", "/", nil) + + // Create a new ResponseRecorder (which satisfies http.ResponseWriter) to record the response. + w := httptest.NewRecorder() + + // Create a new context with the Request and ResponseWriter. + ctx, _ := gin.CreateTestContext(w) + ctx.Request = req + + // Optionally, set any values in the context. + ctx.Set("test", "test") + + checkin := models.CheckIn{ + BookingID: "ROOM01", + Creator: "test@example.com", + } + + mt.Run("Nil database", func(mt *mtest.T) { + // Call the function under test + appsession := &models.AppSession{} + success, err := database.ConfirmCheckIn(ctx, appsession, checkin) + + // Validate the result + assert.Error(t, err) + assert.False(t, success) + }) + + mt.Run("Check in successfully", func(mt *mtest.T) { + mt.AddMockResponses(mtest.CreateCursorResponse(1, configs.GetMongoDBName()+".RoomBooking", mtest.FirstBatch, bson.D{ + {Key: "email", Value: checkin.Creator}, + {Key: "roomId", Value: checkin.BookingID}, + {Key: "checkedIn", Value: false}, + })) + + // Call the function under test + appsession := &models.AppSession{ + DB: mt.Client, + } + success, err := database.ConfirmCheckIn(ctx, appsession, checkin) + + // Validate the result + assert.NoError(t, err) + assert.True(t, success) + }) + + mt.Run("Check in successfully in Cache", func(mt *mtest.T) { + mt.AddMockResponses(mtest.CreateSuccessResponse()) + + Cache := configs.CreateCache() + + appsession := &models.AppSession{ + DB: mt.Client, + Cache: Cache, + } + + booking := models.Booking{ + OccupiID: checkin.BookingID, + Creator: checkin.Creator, + CheckedIn: false, + } + + // marshall and add the booking to cache + bookingData, err := bson.Marshal(booking) + + assert.Nil(t, err) + + err = Cache.Set(cache.RoomBookingKey(booking.OccupiID), bookingData) + + assert.Nil(t, err) + + // Call the function under test + success, err := database.ConfirmCheckIn(ctx, appsession, checkin) + + // Validate the result + assert.NoError(t, err) + assert.True(t, success) + + // Verify the room was added to the Cache + bookingv, err := Cache.Get(cache.RoomBookingKey(booking.OccupiID)) + + assert.Nil(t, err) + assert.NotNil(t, bookingv) + + // unmarshall + var booking2 models.Booking + err = bson.Unmarshal(bookingv, &booking2) + + assert.Nil(t, err) + + assert.True(t, booking2.CheckedIn) + }) + + mt.Run("InsertOne error", func(mt *mtest.T) { + mt.AddMockResponses(mtest.CreateCommandErrorResponse(mtest.CommandError{ + Code: 11000, + Message: "duplicate key error", + })) + + // Call the function under test + appsession := &models.AppSession{ + DB: mt.Client, + } + success, err := database.ConfirmCheckIn(ctx, appsession, checkin) + + // Validate the result + assert.Error(t, err) + assert.False(t, success) + }) +} + func TestEmailExists(t *testing.T) { // Setup mock MongoDB instance mt := mtest.New(t, mtest.NewOptions().ClientType(mtest.Mock)) @@ -230,6 +436,105 @@ func TestEmailExists(t *testing.T) { }) } +func TestBookingExists(t *testing.T) { + // Setup mock MongoDB instance + mt := mtest.New(t, mtest.NewOptions().ClientType(mtest.Mock)) + + gin.SetMode(configs.GetGinRunMode()) + + // Create a new HTTP request with the POST method. + req, _ := http.NewRequest("POST", "/", nil) + + // Create a new ResponseRecorder (which satisfies http.ResponseWriter) to record the response. + w := httptest.NewRecorder() + + // Create a new context with the Request and ResponseWriter. + ctx, _ := gin.CreateTestContext(w) + ctx.Request = req + + // Optionally, set any values in the context. + ctx.Set("test", "test") + + id := "OCCUPI0101" + + mt.Run("Nil database", func(mt *mtest.T) { + // Call the function under test + appsession := &models.AppSession{} + exists := database.BookingExists(ctx, appsession, id) + + // Validate the result + assert.False(t, exists) + }) + + mt.Run("Booking exists", func(mt *mtest.T) { + mt.AddMockResponses(mtest.CreateCursorResponse(1, configs.GetMongoDBName()+".RoomBooking", mtest.FirstBatch, bson.D{ + {Key: "occupiId", Value: id}, + })) + + // Call the function under test + appsession := &models.AppSession{ + DB: mt.Client, + } + exists := database.BookingExists(ctx, appsession, id) + + // Validate the result + assert.True(t, exists) + }) + + mt.Run("Email exists adding to Cache", func(mt *mtest.T) { + mt.AddMockResponses(mtest.CreateCursorResponse(1, configs.GetMongoDBName()+".RoomBooking", mtest.FirstBatch, bson.D{ + {Key: "occupiId", Value: id}, + })) + + Cache := configs.CreateCache() + + appsession := &models.AppSession{ + DB: mt.Client, + Cache: Cache, + } + + // Call the function under test + exists := database.BookingExists(ctx, appsession, id) + + // Validate the result + assert.True(t, exists) + + // Check if the email exists in the Cache + booking, err := Cache.Get(cache.RoomBookingKey(id)) + assert.NoError(t, err) + assert.NotNil(t, booking) + }) + + mt.Run("Email does not exist", func(mt *mtest.T) { + mt.AddMockResponses(mtest.CreateCursorResponse(1, configs.GetMongoDBName()+".RoomBooking", mtest.FirstBatch)) + + // Call the function under test + appsession := &models.AppSession{ + DB: mt.Client, + } + exists := database.BookingExists(ctx, appsession, id) + + // Validate the result + assert.False(t, exists) + }) + + mt.Run("Handle find error", func(mt *mtest.T) { + mt.AddMockResponses(mtest.CreateCommandErrorResponse(mtest.CommandError{ + Code: 1, + Message: "find error", + })) + + // Call the function under test + appsession := &models.AppSession{ + DB: mt.Client, + } + exists := database.BookingExists(ctx, appsession, id) + + // Validate the result + assert.False(t, exists) + }) +} + func TestAddUser(t *testing.T) { // Setup mock MongoDB instance mt := mtest.New(t, mtest.NewOptions().ClientType(mtest.Mock)) @@ -917,6 +1222,171 @@ func TestGetPassword(t *testing.T) { }) } +func TestCheckIfNextVerificationDateIsDue(t *testing.T) { + // Setup mock MongoDB instance + mt := mtest.New(t, mtest.NewOptions().ClientType(mtest.Mock)) + + gin.SetMode(configs.GetGinRunMode()) + + // Create a new HTTP request with the POST method. + req, _ := http.NewRequest("POST", "/", nil) + + // Create a new ResponseRecorder (which satisfies http.ResponseWriter) to record the response. + w := httptest.NewRecorder() + + // Create a new context with the Request and ResponseWriter. + ctx, _ := gin.CreateTestContext(w) + ctx.Request = req + + // Optionally, set any values in the context. + ctx.Set("test", "test") + + email1 := "test1@example.com" + email2 := "test2@example.com" + + dueDate := time.Now().Add(-1 * time.Hour) + notDueDate := time.Now().Add(1 * time.Hour) + + mt.Run("Nil database", func(mt *mtest.T) { + // Call the function under test + appsession := &models.AppSession{} + isDue, err := database.CheckIfNextVerificationDateIsDue(ctx, appsession, email1) + + // Validate the result + assert.Error(t, err) + assert.False(t, isDue) + }) + + mt.Run("Verification date is not due", func(mt *mtest.T) { + mt.AddMockResponses(mtest.CreateCursorResponse(1, configs.GetMongoDBName()+".Users", mtest.FirstBatch, bson.D{ + {Key: "email", Value: email1}, + {Key: "isVerified", Value: true}, + {Key: "nextVerificationDate", Value: notDueDate}, + })) + + // Call the function under test + appsession := &models.AppSession{ + DB: mt.Client, + } + isDue, err := database.CheckIfNextVerificationDateIsDue(ctx, appsession, email1) + + // Validate the result + assert.NoError(t, err) + assert.False(t, isDue) + }) + + mt.Run("Verification date is due", func(mt *mtest.T) { + mt.AddMockResponses(mtest.CreateCursorResponse(2, configs.GetMongoDBName()+".Users", mtest.FirstBatch, bson.D{ + {Key: "email", Value: email2}, + {Key: "isVerified", Value: true}, + {Key: "nextVerificationDate", Value: dueDate}, + })) + + // Call the function under test + appsession := &models.AppSession{ + DB: mt.Client, + } + isDue, err := database.CheckIfNextVerificationDateIsDue(ctx, appsession, email2) + + // Validate the result + assert.NoError(t, err) + assert.True(t, isDue) + }) + + mt.Run("Verification date is not due in cache", func(mt *mtest.T) { + mt.AddMockResponses(mtest.CreateSuccessResponse()) + + Cache := configs.CreateCache() + + userStruct := models.User{ + Email: email1, + IsVerified: false, + NextVerificationDate: notDueDate, + } + + // add user to Cache + if userData, err := bson.Marshal(userStruct); err != nil { + t.Fatal(err) + } else { + if err := Cache.Set(cache.UserKey(email1), userData); err != nil { + t.Fatal(err) + } + } + + // Assert that the user is in the Cache + userA, err := Cache.Get(cache.UserKey(email1)) + + assert.Nil(t, err) + assert.NotNil(t, userA) + + // Call the function under test + appsession := &models.AppSession{ + DB: mt.Client, + Cache: Cache, + } + isDue, err := database.CheckIfNextVerificationDateIsDue(ctx, appsession, email1) + + // Validate the result + assert.NoError(t, err) + assert.False(t, isDue) + }) + + mt.Run("Verification date is due in cache", func(mt *mtest.T) { + mt.AddMockResponses(mtest.CreateSuccessResponse()) + + Cache := configs.CreateCache() + + userStruct := models.User{ + Email: email2, + IsVerified: false, + NextVerificationDate: dueDate, + } + + // add user to Cache + if userData, err := bson.Marshal(userStruct); err != nil { + t.Fatal(err) + } else { + if err := Cache.Set(cache.UserKey(email2), userData); err != nil { + t.Fatal(err) + } + } + + // Assert that the user is in the Cache + userA, err := Cache.Get(cache.UserKey(email2)) + + assert.Nil(t, err) + assert.NotNil(t, userA) + + // Call the function under test + appsession := &models.AppSession{ + DB: mt.Client, + Cache: Cache, + } + isDue, err := database.CheckIfNextVerificationDateIsDue(ctx, appsession, email2) + + // Validate the result + assert.NoError(t, err) + assert.True(t, isDue) + }) + + mt.Run("FindOne error", func(mt *mtest.T) { + mt.AddMockResponses(mtest.CreateCommandErrorResponse(mtest.CommandError{ + Code: 11000, + Message: "find error", + })) + + // Call the function under test + appsession := &models.AppSession{ + DB: mt.Client, + } + isDue, err := database.CheckIfNextVerificationDateIsDue(ctx, appsession, email1) + + // Validate the result + assert.Error(t, err) + assert.False(t, isDue) + }) +} + func TestCheckIfUserIsVerified(t *testing.T) { // Setup mock MongoDB instance mt := mtest.New(t, mtest.NewOptions().ClientType(mtest.Mock)) @@ -982,6 +1452,85 @@ func TestCheckIfUserIsVerified(t *testing.T) { assert.False(t, isVerified) }) + mt.Run("User is verified in cache", func(mt *mtest.T) { + mt.AddMockResponses(mtest.CreateCursorResponse(1, configs.GetMongoDBName()+".Users", mtest.FirstBatch, bson.D{ + {Key: "email", Value: email}, + {Key: "isVerified", Value: true}, + })) + + Cache := configs.CreateCache() + + userStruct := models.User{ + Email: email, + IsVerified: true, + } + + // add user to Cache + if userData, err := bson.Marshal(userStruct); err != nil { + t.Fatal(err) + } else { + if err := Cache.Set(cache.UserKey(email), userData); err != nil { + t.Fatal(err) + } + } + + // Assert that the user is in the Cache + userA, err := Cache.Get(cache.UserKey(email)) + + assert.Nil(t, err) + assert.NotNil(t, userA) + + // Call the function under test + appsession := &models.AppSession{ + DB: mt.Client, + Cache: Cache, + } + isVerified, err := database.CheckIfUserIsVerified(ctx, appsession, email) + + // Validate the result + assert.NoError(t, err) + assert.True(t, isVerified) + }) + + mt.Run("User is not verified in cache", func(mt *mtest.T) { + mt.AddMockResponses(mtest.CreateCursorResponse(1, configs.GetMongoDBName()+".Users", mtest.FirstBatch, bson.D{ + {Key: "email", Value: email}, + {Key: "isVerified", Value: false}, + })) + + Cache := configs.CreateCache() + + userStruct := models.User{ + Email: email, + IsVerified: false, + } + + // add user to Cache + if userData, err := bson.Marshal(userStruct); err != nil { + t.Fatal(err) + } else { + if err := Cache.Set(cache.UserKey(email), userData); err != nil { + t.Fatal(err) + } + } + + // Assert that the user is in the Cache + userA, err := Cache.Get(cache.UserKey(email)) + + assert.Nil(t, err) + assert.NotNil(t, userA) + + // Call the function under test + appsession := &models.AppSession{ + DB: mt.Client, + } + isVerified, err := database.CheckIfUserIsVerified(ctx, appsession, email) + + // Validate the result + assert.NoError(t, err) + assert.False(t, isVerified) + }) + mt.Run("FindOne error", func(mt *mtest.T) { mt.AddMockResponses(mtest.CreateCommandErrorResponse(mtest.CommandError{ Code: 11000,