From 0ae56cb1ea7e1426a1d4dd58a48028271667029c Mon Sep 17 00:00:00 2001 From: dagmaros27 Date: Fri, 23 Aug 2024 17:56:21 +0300 Subject: [PATCH 1/2] ai controller tested --- .../delivery/controller/ai_controller.go | 35 ++- .../delivery/controller/auth_controller.go | 152 ++++++++--- .../controller/tests/ai_controller_test.go | 113 ++++++++ .../controller/tests/auth_controller_test.go | 255 ++++++++++++++++++ .../controller/tests/blog_controller_test.go | 1 + .../tests/comment_controller_test.go | 1 + backend/AAiT-backend-group-11/go.mod | 4 + backend/AAiT-backend-group-11/go.sum | 1 + 8 files changed, 513 insertions(+), 49 deletions(-) create mode 100644 backend/AAiT-backend-group-11/delivery/controller/tests/ai_controller_test.go create mode 100644 backend/AAiT-backend-group-11/delivery/controller/tests/auth_controller_test.go create mode 100644 backend/AAiT-backend-group-11/delivery/controller/tests/blog_controller_test.go create mode 100644 backend/AAiT-backend-group-11/delivery/controller/tests/comment_controller_test.go diff --git a/backend/AAiT-backend-group-11/delivery/controller/ai_controller.go b/backend/AAiT-backend-group-11/delivery/controller/ai_controller.go index 387e757f5..7a4e2d15a 100644 --- a/backend/AAiT-backend-group-11/delivery/controller/ai_controller.go +++ b/backend/AAiT-backend-group-11/delivery/controller/ai_controller.go @@ -1,6 +1,7 @@ package controller import ( + "backend-starter-project/domain/dto" "backend-starter-project/service" "net/http" @@ -22,20 +23,29 @@ func (acc *AIContentController) GenerateContentSuggestions(c *gin.Context) { Keywords []string `json:"keywords"` } + var response dto.Response + + if err := c.ShouldBindJSON(&req); err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + response.Success = false + response.Error = "Invalid request payload" + c.JSON(http.StatusBadRequest, response) return } contentSuggestion, err := acc.aiContentService.GenerateContentSuggestions( req.Keywords) if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + response.Success = false + response.Error = "Error generating content suggestions" + c.JSON(http.StatusInternalServerError, response) return } - c.JSON(http.StatusOK, gin.H{ + response.Success = true + response.Data = gin.H{ "content": contentSuggestion, - }) + } + c.JSON(http.StatusOK, response) } func (acc *AIContentController) SuggestContentImprovements(c *gin.Context) { @@ -44,16 +54,27 @@ func (acc *AIContentController) SuggestContentImprovements(c *gin.Context) { Instruction string `json:"instruction"` } + var response dto.Response + if err := c.ShouldBindJSON(&req); err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + response.Success = false + response.Error = "Invalid request payload" + c.JSON(http.StatusBadRequest, response) return } contentSuggestion, err := acc.aiContentService.SuggestContentImprovements(req.BlogPostID, req.Instruction) if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + response.Success = false + response.Error = "Error suggesting content improvements" + c.JSON(http.StatusBadRequest, response) return } - c.JSON(http.StatusOK, contentSuggestion) + + response.Success = true + response.Data = gin.H{ + "suggestion result": contentSuggestion, + } + c.JSON(http.StatusOK, response) } diff --git a/backend/AAiT-backend-group-11/delivery/controller/auth_controller.go b/backend/AAiT-backend-group-11/delivery/controller/auth_controller.go index e2ef59efc..767e2bf1c 100644 --- a/backend/AAiT-backend-group-11/delivery/controller/auth_controller.go +++ b/backend/AAiT-backend-group-11/delivery/controller/auth_controller.go @@ -4,6 +4,7 @@ import ( "backend-starter-project/domain/dto" "backend-starter-project/domain/entities" "backend-starter-project/domain/interfaces" + "log" "net/http" "time" @@ -25,9 +26,11 @@ func NewAuthController(authService interfaces.AuthenticationService, passwordRes func (controller *AuthController) RegisterUser(c *gin.Context) { var userRequest dto.UserCreateRequestDTO - + var response dto.Response if err := c.ShouldBindJSON(&userRequest); err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + response.Success = false + response.Error = "Invalid request payload" + c.JSON(http.StatusBadRequest, response) return } @@ -45,7 +48,9 @@ func (controller *AuthController) RegisterUser(c *gin.Context) { createdUser, err := controller.authService.RegisterUser(&user) if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + response.Success = false + response.Error = err.Error() + c.JSON(http.StatusInternalServerError,response) return } @@ -55,8 +60,8 @@ func (controller *AuthController) RegisterUser(c *gin.Context) { Role: createdUser.Role, } - - c.JSON(http.StatusCreated, gin.H{"data": userResponse}) + response.Data = userResponse + c.JSON(http.StatusCreated, response) } func (controller *AuthController) Login(c *gin.Context) { @@ -65,107 +70,170 @@ func (controller *AuthController) Login(c *gin.Context) { c.JSON(400, gin.H{"error": err.Error()}) return } - + var response dto.Response refreshToken, accessToken, err := controller.authService.Login(user.Email, user.Password) if err != nil { - c.JSON(400, gin.H{"error": err.Error()}) + response.Success = false + response.Error = err.Error() + c.JSON(400, response) return } - c.Header("Authorization", "Bearer "+accessToken) - c.JSON(200, gin.H{"refresh_token": refreshToken.Token}) - c.JSON(200, gin.H{"message": "login successful"}) + response.Data = gin.H{ "access_token": accessToken} + response.Message = "Logged in successfully" c.Set("userId", refreshToken.UserID) c.SetCookie("refresh_token", refreshToken.Token, int(refreshToken.ExpiresAt.Unix()), "/", "localhost", false, true) + c.JSON(http.StatusOK, response) } func (controller *AuthController) Logout(c *gin.Context) { userId := c.GetString("userId") - - controller.authService.Logout(userId) - c.JSON(200, gin.H{"message": "succesfully logged out"}) + var response dto.Response + err := controller.authService.Logout(userId) + log.Println("logout" + userId) + if err != nil { + response.Success = false + response.Error = err.Error() + c.JSON(400, response) + return + } + response.Success = true + response.Message = "succesfully logged out" + c.JSON(200, response) } func (controller *AuthController) RefreshAccessToken(c *gin.Context) { var token entities.RefreshToken + var response dto.Response err := c.ShouldBindJSON(&token) if err != nil { - c.JSON(400, gin.H{"error": err.Error()}) + response.Success = true + response.Error = err.Error() + c.JSON(400, response) + return + } + + newToken,err := controller.authService.RefreshAccessToken(&token) + if err != nil { + response.Success = false + response.Error = err.Error() + c.JSON(400, response) return } - controller.authService.RefreshAccessToken(&token) + response.Success = true + response.Data = gin.H{"access_token": newToken} + c.JSON(200, response) } func (controller *AuthController) VerifyEmail(c *gin.Context) { - var emailVerification entities.EmailVerificationRequest if err := c.ShouldBindJSON(&emailVerification); err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + c.JSON(http.StatusBadRequest, dto.Response{ + Success: false, + Message: "Invalid request", + Error: err.Error(), + }) return } err := controller.authService.VerifyEmail(emailVerification.Email, emailVerification.Code) if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + c.JSON(http.StatusInternalServerError, dto.Response{ + Success: false, + Message: "Email verification failed", + Error: err.Error(), + }) return } - c.JSON(http.StatusOK, gin.H{"message": "Email verified successfully"}) + c.JSON(http.StatusOK, dto.Response{ + Success: true, + Message: "Email verified successfully", + }) } - func (controller *AuthController) RequestPasswordReset(c *gin.Context) { - var forgetPasswordRequest entities.ForgetPasswordRequest if err := c.ShouldBindJSON(&forgetPasswordRequest); err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + c.JSON(http.StatusBadRequest, dto.Response{ + Success: false, + Message: "Invalid request", + Error: err.Error(), + }) return } err := controller.passwordResetService.RequestPasswordReset(forgetPasswordRequest.Email) if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + c.JSON(http.StatusInternalServerError, dto.Response{ + Success: false, + Message: "Failed to send password reset link", + Error: err.Error(), + }) return } - c.JSON(http.StatusOK, gin.H{"message": "Password reset link sent to your email"}) + c.JSON(http.StatusOK, dto.Response{ + Success: true, + Message: "Password reset link sent to your email", + }) } func (controller *AuthController) ResetPassword(c *gin.Context) { - var passwordReset entities.PasswordReset if err := c.ShouldBindJSON(&passwordReset); err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + c.JSON(http.StatusBadRequest, dto.Response{ + Success: false, + Message: "Invalid request", + Error: err.Error(), + }) return } err := controller.passwordResetService.ResetPassword(passwordReset.Token, passwordReset.NewPassword) if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + c.JSON(http.StatusInternalServerError, dto.Response{ + Success: false, + Message: "Password reset failed", + Error: err.Error(), + }) return } - c.JSON(http.StatusOK, gin.H{"message": "Password reset successfully"}) + c.JSON(http.StatusOK, dto.Response{ + Success: true, + Message: "Password reset successfully", + }) } func (controller *AuthController) ResendOtp(c *gin.Context) { + var request entities.ResendOTPRequest - var request entities.ResendOTPRequest - - err := c.ShouldBind(&request) - if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) - return - } + err := c.ShouldBind(&request) + if err != nil { + c.JSON(http.StatusBadRequest, dto.Response{ + Success: false, + Message: "Invalid request", + Error: err.Error(), + }) + return + } - err = controller.authService.ResendOtp(request) - if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) - return - } + err = controller.authService.ResendOtp(request) + if err != nil { + c.JSON(http.StatusInternalServerError, dto.Response{ + Success: false, + Message: "Failed to resend OTP", + Error: err.Error(), + }) + return + } - c.JSON(http.StatusOK, gin.H{"message": "OTP sent successfully"}) -} \ No newline at end of file + c.JSON(http.StatusOK, dto.Response{ + Success: true, + Message: "OTP sent successfully", + }) +} diff --git a/backend/AAiT-backend-group-11/delivery/controller/tests/ai_controller_test.go b/backend/AAiT-backend-group-11/delivery/controller/tests/ai_controller_test.go new file mode 100644 index 000000000..520ed30fe --- /dev/null +++ b/backend/AAiT-backend-group-11/delivery/controller/tests/ai_controller_test.go @@ -0,0 +1,113 @@ +package controller_test + +import ( + "backend-starter-project/delivery/controller" + "bytes" + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/mock" + + "github.com/gin-gonic/gin" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" +) + +type MockAIContentService struct { + mock.Mock +} + +func (m *MockAIContentService) GenerateContentSuggestions(keywords []string) (string, error) { + args := m.Called(keywords) + return args.String(0), args.Error(1) +} + +func (m *MockAIContentService) SuggestContentImprovements(blogPostId, instruction string) (string, error) { + args := m.Called(blogPostId, instruction) + return args.String(0), args.Error(1) +} + + +type AIContentControllerTestSuite struct { + suite.Suite + mockAIContentService *MockAIContentService + controller *controller.AIContentController + router *gin.Engine +} + +func (suite *AIContentControllerTestSuite) SetupTest() { + suite.mockAIContentService = new(MockAIContentService) + suite.controller = controller.NewAIContentController(suite.mockAIContentService) + + suite.router = gin.Default() + suite.router.POST("/generate-suggestions", suite.controller.GenerateContentSuggestions) + suite.router.POST("/suggest-improvements", suite.controller.SuggestContentImprovements) +} + +func (suite *AIContentControllerTestSuite) TestGenerateContentSuggestions_Success() { + keywords := []string{"AI", "content", "generation"} + suggestion := "Here are some content suggestions." + + suite.mockAIContentService.On("GenerateContentSuggestions", keywords).Return(suggestion, nil) + + reqBody, _ := json.Marshal(map[string]interface{}{ + "keywords": keywords, + }) + req, _ := http.NewRequest(http.MethodPost, "/generate-suggestions", bytes.NewBuffer(reqBody)) + req.Header.Set("Content-Type", "application/json") + + w := httptest.NewRecorder() + suite.router.ServeHTTP(w, req) + + assert.Equal(suite.T(), http.StatusOK, w.Code) + assert.Contains(suite.T(), w.Body.String(), suggestion) +} + +func (suite *AIContentControllerTestSuite) TestGenerateContentSuggestions_BadRequest() { + req, _ := http.NewRequest(http.MethodPost, "/generate-suggestions", bytes.NewBuffer([]byte{})) + req.Header.Set("Content-Type", "application/json") + + w := httptest.NewRecorder() + suite.router.ServeHTTP(w, req) + + assert.Equal(suite.T(), http.StatusBadRequest, w.Code) + assert.Contains(suite.T(), w.Body.String(), "Invalid request payload") +} + +func (suite *AIContentControllerTestSuite) TestSuggestContentImprovements_Success() { + blogPostId := "12345" + instruction := "Improve readability" + suggestion := "Here are some improvements." + + suite.mockAIContentService.On("SuggestContentImprovements", blogPostId, instruction).Return(suggestion, nil) + + reqBody, _ := json.Marshal(map[string]interface{}{ + "blogPostId": blogPostId, + "instruction": instruction, + }) + req, _ := http.NewRequest(http.MethodPost, "/suggest-improvements", bytes.NewBuffer(reqBody)) + req.Header.Set("Content-Type", "application/json") + + w := httptest.NewRecorder() + suite.router.ServeHTTP(w, req) + + assert.Equal(suite.T(), http.StatusOK, w.Code) + assert.Contains(suite.T(), w.Body.String(), suggestion) +} + +func (suite *AIContentControllerTestSuite) TestSuggestContentImprovements_BadRequest() { + req, _ := http.NewRequest(http.MethodPost, "/suggest-improvements", bytes.NewBuffer([]byte{})) + req.Header.Set("Content-Type", "application/json") + + w := httptest.NewRecorder() + suite.router.ServeHTTP(w, req) + + assert.Equal(suite.T(), http.StatusBadRequest, w.Code) + assert.Contains(suite.T(), w.Body.String(), "Invalid request payload") +} + +func TestAIContentControllerTestSuite(t *testing.T) { + suite.Run(t, new(AIContentControllerTestSuite)) +} diff --git a/backend/AAiT-backend-group-11/delivery/controller/tests/auth_controller_test.go b/backend/AAiT-backend-group-11/delivery/controller/tests/auth_controller_test.go new file mode 100644 index 000000000..e2d1e1649 --- /dev/null +++ b/backend/AAiT-backend-group-11/delivery/controller/tests/auth_controller_test.go @@ -0,0 +1,255 @@ +package controller_test + +// import ( +// "backend-starter-project/delivery/controller" +// "backend-starter-project/domain/dto" +// "backend-starter-project/domain/entities" +// "bytes" +// "encoding/json" +// "net/http" +// "net/http/httptest" +// "testing" + +// "github.com/gin-gonic/gin" +// "github.com/stretchr/testify/mock" +// "github.com/stretchr/testify/suite" +// ) + +// type AuthControllerTestSuite struct { +// suite.Suite +// authController *controller.AuthController +// authServiceMock *MockAuthService +// passwordResetServiceMock *MockPasswordResetService +// router *gin.Engine +// } + +// // MockAuthService is a mock implementation of the AuthenticationService interface +// type MockAuthService struct { +// mock.Mock +// } + +// func (m *MockAuthService) RegisterUser(user *entities.User) (*entities.User, error) { +// args := m.Called(user) +// return args.Get(0).(*entities.User), args.Error(1) +// } + +// func (m *MockAuthService) Login(emailOrUsername, password string) (*entities.RefreshToken, string, error) { +// args := m.Called(emailOrUsername, password) +// return args.Get(0).(*entities.RefreshToken), args.String(1), args.Error(2) +// } + +// func (m *MockAuthService) Logout(userId string) error { +// args := m.Called(userId) +// return args.Error(0) +// } + +// func (m *MockAuthService) RefreshAccessToken(token *entities.RefreshToken) (string, error) { +// args := m.Called(token) +// return args.String(0), args.Error(1) +// } + +// func (m *MockAuthService) VerifyEmail(email string, code string) error { +// args := m.Called(email, code) +// return args.Error(0) +// } + +// func (m *MockAuthService) ResendOtp(request entities.ResendOTPRequest) error { +// args := m.Called(request) +// return args.Error(0) +// } + +// // MockPasswordResetService is a mock implementation of the PasswordResetService interface +// type MockPasswordResetService struct { +// mock.Mock +// } + +// func (m *MockPasswordResetService) RequestPasswordReset(email string) error { +// args := m.Called(email) +// return args.Error(0) +// } + +// func (m *MockPasswordResetService) ResetPassword(token, newPassword string) error { +// args := m.Called(token, newPassword) +// return args.Error(0) +// } + +// func (m *MockPasswordResetService) GeneratePasswordResetToken(user *entities.User) (string, error) { +// args := m.Called(user) +// return args.String(0), args.Error(1) +// } + +// // SetupTest initializes the test suite +// func (suite *AuthControllerTestSuite) SetupTest() { +// suite.authServiceMock = new(MockAuthService) +// suite.passwordResetServiceMock = new(MockPasswordResetService) +// suite.authController = controller.NewAuthController(suite.authServiceMock, suite.passwordResetServiceMock) + +// gin.SetMode(gin.TestMode) +// suite.router = gin.Default() +// } + +// // Test RegisterUser +// func (suite *AuthControllerTestSuite) TestRegisterUser() { +// suite.router.POST("/register", suite.authController.RegisterUser) + +// user := &entities.User{Username: "testuser", Email: "test@example.com", Password: "password123"} +// suite.authServiceMock.On("RegisterUser", mock.AnythingOfType("*entities.User")).Return(user, nil) + +// userRequest := dto.UserCreateRequestDTO{ +// Username: "testuser", +// Email: "test@example.com", +// Password: "password123", +// } + +// jsonValue, _ := json.Marshal(userRequest) +// req, _ := http.NewRequest("POST", "/register", bytes.NewBuffer(jsonValue)) +// req.Header.Set("Content-Type", "application/json") + +// w := httptest.NewRecorder() +// suite.router.ServeHTTP(w, req) + +// suite.Equal(http.StatusCreated, w.Code) +// suite.authServiceMock.AssertExpectations(suite.T()) +// } + +// // Test Login +// func (suite *AuthControllerTestSuite) TestLogin() { +// suite.router.POST("/login", suite.authController.Login) + +// refreshToken := &entities.RefreshToken{ +// UserID: "userID", +// Token: "refreshToken", +// } + +// suite.authServiceMock.On("Login", "test@example.com", "password123").Return(refreshToken, "accessToken", nil) + +// loginRequest := dto.LoginDto{ +// Email: "test@example.com", +// Password: "password123", +// } + +// jsonValue, _ := json.Marshal(loginRequest) +// req, _ := http.NewRequest("POST", "/login", bytes.NewBuffer(jsonValue)) +// req.Header.Set("Content-Type", "application/json") + +// w := httptest.NewRecorder() +// suite.router.ServeHTTP(w, req) + +// suite.Equal(http.StatusOK, w.Code) +// suite.authServiceMock.AssertExpectations(suite.T()) +// } + +// func (suite *AuthControllerTestSuite) TestLogout() { +// suite.router.POST("/logout", suite.authController.Logout) + +// suite.authServiceMock.On("Logout", "userID").Return(nil) + +// w := httptest.NewRecorder() +// req, _ := http.NewRequest("POST", "/logout", nil) +// req.Header.Set("userId", "userID") + +// suite.router.ServeHTTP(w, req) + +// // Check if the Logout method was called +// suite.authServiceMock.AssertCalled(suite.T(), "Logout", "userID") + +// // Check the response code +// suite.Equal(http.StatusOK, w.Code) +// suite.authServiceMock.AssertExpectations(suite.T()) +// } + + +// // Test RefreshAccessToken +// func (suite *AuthControllerTestSuite) TestRefreshAccessToken() { +// suite.router.POST("/refresh-token", suite.authController.RefreshAccessToken) + +// refreshToken := &entities.RefreshToken{Token: "oldRefreshToken"} +// suite.authServiceMock.On("RefreshAccessToken", refreshToken).Return("newAccessToken", nil) + +// jsonValue, _ := json.Marshal(refreshToken) +// req, _ := http.NewRequest("POST", "/refresh-token", bytes.NewBuffer(jsonValue)) +// req.Header.Set("Content-Type", "application/json") + +// w := httptest.NewRecorder() +// suite.router.ServeHTTP(w, req) + +// suite.Equal(http.StatusOK, w.Code) +// suite.authServiceMock.AssertExpectations(suite.T()) +// } + +// // Test VerifyEmail +// func (suite *AuthControllerTestSuite) TestVerifyEmail() { +// suite.router.POST("/verify-email", suite.authController.VerifyEmail) + +// emailVerification := entities.EmailVerificationRequest{Email: "test@example.com", Code: "123456"} +// suite.authServiceMock.On("VerifyEmail", "test@example.com", "123456").Return(nil) + +// jsonValue, _ := json.Marshal(emailVerification) +// req, _ := http.NewRequest("POST", "/verify-email", bytes.NewBuffer(jsonValue)) +// req.Header.Set("Content-Type", "application/json") + +// w := httptest.NewRecorder() +// suite.router.ServeHTTP(w, req) + +// suite.Equal(http.StatusOK, w.Code) +// suite.authServiceMock.AssertExpectations(suite.T()) +// } + +// // Test RequestPasswordReset +// func (suite *AuthControllerTestSuite) TestRequestPasswordReset() { +// suite.router.POST("/request-password-reset", suite.authController.RequestPasswordReset) + +// suite.passwordResetServiceMock.On("RequestPasswordReset", "test@example.com").Return(nil) + +// forgetPasswordRequest := entities.ForgetPasswordRequest{Email: "test@example.com"} +// jsonValue, _ := json.Marshal(forgetPasswordRequest) +// req, _ := http.NewRequest("POST", "/request-password-reset", bytes.NewBuffer(jsonValue)) +// req.Header.Set("Content-Type", "application/json") + +// w := httptest.NewRecorder() +// suite.router.ServeHTTP(w, req) + +// suite.Equal(http.StatusOK, w.Code) +// suite.passwordResetServiceMock.AssertExpectations(suite.T()) +// } + +// // Test ResetPassword +// func (suite *AuthControllerTestSuite) TestResetPassword() { +// suite.router.POST("/reset-password", suite.authController.ResetPassword) + +// passwordReset := entities.PasswordReset{Token: "resetToken", NewPassword: "newPassword"} +// suite.passwordResetServiceMock.On("ResetPassword", "resetToken", "newPassword").Return(nil) + +// jsonValue, _ := json.Marshal(passwordReset) +// req, _ := http.NewRequest("POST", "/reset-password", bytes.NewBuffer(jsonValue)) +// req.Header.Set("Content-Type", "application/json") + +// w := httptest.NewRecorder() +// suite.router.ServeHTTP(w, req) + +// suite.Equal(http.StatusOK, w.Code) +// suite.passwordResetServiceMock.AssertExpectations(suite.T()) +// } + +// // Test ResendOtp +// func (suite *AuthControllerTestSuite) TestResendOtp() { +// suite.router.POST("/resend-otp", suite.authController.ResendOtp) + +// otpRequest := entities.ResendOTPRequest{Email: "test@example.com"} +// suite.authServiceMock.On("ResendOtp", otpRequest).Return(nil) + +// jsonValue, _ := json.Marshal(otpRequest) +// req, _ := http.NewRequest("POST", "/resend-otp", bytes.NewBuffer(jsonValue)) +// req.Header.Set("Content-Type", "application/json") + +// w := httptest.NewRecorder() +// suite.router.ServeHTTP(w, req) + +// suite.Equal(http.StatusOK, w.Code) +// suite.authServiceMock.AssertExpectations(suite.T()) +// } + +// // Test suite runner +// func TestAuthControllerTestSuite(t *testing.T) { +// suite.Run(t, new(AuthControllerTestSuite)) +// } diff --git a/backend/AAiT-backend-group-11/delivery/controller/tests/blog_controller_test.go b/backend/AAiT-backend-group-11/delivery/controller/tests/blog_controller_test.go new file mode 100644 index 000000000..55c64d6ae --- /dev/null +++ b/backend/AAiT-backend-group-11/delivery/controller/tests/blog_controller_test.go @@ -0,0 +1 @@ +package controller_test \ No newline at end of file diff --git a/backend/AAiT-backend-group-11/delivery/controller/tests/comment_controller_test.go b/backend/AAiT-backend-group-11/delivery/controller/tests/comment_controller_test.go new file mode 100644 index 000000000..55c64d6ae --- /dev/null +++ b/backend/AAiT-backend-group-11/delivery/controller/tests/comment_controller_test.go @@ -0,0 +1 @@ +package controller_test \ No newline at end of file diff --git a/backend/AAiT-backend-group-11/go.mod b/backend/AAiT-backend-group-11/go.mod index f15630c1b..c8c96e452 100644 --- a/backend/AAiT-backend-group-11/go.mod +++ b/backend/AAiT-backend-group-11/go.mod @@ -8,6 +8,7 @@ require ( github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1 github.com/gin-gonic/gin v1.10.0 github.com/go-redis/redis/v8 v8.11.5 + github.com/stretchr/testify v1.9.0 go.mongodb.org/mongo-driver v1.16.1 google.golang.org/api v0.186.0 ) @@ -24,6 +25,7 @@ require ( github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/cloudwego/base64x v0.1.4 // indirect github.com/cloudwego/iasm v0.2.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/gabriel-vasile/mimetype v1.4.3 // indirect @@ -47,6 +49,8 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/stretchr/objx v0.5.2 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.12 // indirect go.opencensus.io v0.24.0 // indirect diff --git a/backend/AAiT-backend-group-11/go.sum b/backend/AAiT-backend-group-11/go.sum index 52f03c3e8..6e2c0f8ef 100644 --- a/backend/AAiT-backend-group-11/go.sum +++ b/backend/AAiT-backend-group-11/go.sum @@ -138,6 +138,7 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1: github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= From 41dd5108704900da57f441d3c3bde8a7b63f4688 Mon Sep 17 00:00:00 2001 From: dagmaros27 Date: Thu, 29 Aug 2024 11:23:06 +0300 Subject: [PATCH 2/2] blog controller test done --- .../delivery/controller/comment_controller.go | 60 ++- .../popularity_tracking_controller.go | 29 +- .../controller/tests/auth_controller_test.go | 439 +++++++++--------- .../controller/tests/blog_controller_test.go | 239 +++++++++- .../tests/comment_controller_test.go | 178 ++++++- .../tests/popularity_controller_test.go | 178 +++++++ .../tests/profile_controller_test.go | 251 ++++++++++ .../controller/tests/user_controller_test.go | 132 ++++++ .../delivery/controller/user_controller.go | 22 +- .../domain/entities/email.go | 4 +- 10 files changed, 1293 insertions(+), 239 deletions(-) create mode 100644 backend/AAiT-backend-group-11/delivery/controller/tests/popularity_controller_test.go create mode 100644 backend/AAiT-backend-group-11/delivery/controller/tests/profile_controller_test.go create mode 100644 backend/AAiT-backend-group-11/delivery/controller/tests/user_controller_test.go diff --git a/backend/AAiT-backend-group-11/delivery/controller/comment_controller.go b/backend/AAiT-backend-group-11/delivery/controller/comment_controller.go index 09e2b7e50..903876ddd 100644 --- a/backend/AAiT-backend-group-11/delivery/controller/comment_controller.go +++ b/backend/AAiT-backend-group-11/delivery/controller/comment_controller.go @@ -5,6 +5,7 @@ import ( "backend-starter-project/domain/entities" "backend-starter-project/domain/interfaces" + "backend-starter-project/domain/dto" "github.com/gin-gonic/gin" "go.mongodb.org/mongo-driver/bson/primitive" @@ -23,7 +24,11 @@ func NewCommentController(cs interfaces.CommentService) *CommentController { func (cc *CommentController) AddComment(c *gin.Context) { var comment entities.Comment if err := c.ShouldBindJSON(&comment); err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + c.JSON(http.StatusBadRequest, dto.Response{ + Success: false, + Message: "Failed to bind comment data", + Error: err.Error(), + }) return } @@ -32,22 +37,37 @@ func (cc *CommentController) AddComment(c *gin.Context) { createdComment, err := cc.commentService.AddComment(&comment) if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + c.JSON(http.StatusInternalServerError, dto.Response{ + Success: false, + Message: "Failed to add comment", + Error: err.Error(), + }) return } - c.JSON(http.StatusOK, createdComment) + c.JSON(http.StatusOK, dto.Response{ + Success: true, + Message: "Comment added successfully", + Data: createdComment, + }) } func (cc *CommentController) DeleteComment(c *gin.Context) { commentId := c.Param("id") err := cc.commentService.DeleteComment(commentId) if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + c.JSON(http.StatusInternalServerError, dto.Response{ + Success: false, + Message: "Failed to delete comment", + Error: err.Error(), + }) return } - c.JSON(http.StatusOK, gin.H{"message": "Comment deleted successfully"}) + c.JSON(http.StatusOK, dto.Response{ + Success: true, + Message: "Comment deleted successfully", + }) } func (cc *CommentController) GetCommentsByBlogPostId(c *gin.Context) { @@ -55,17 +75,29 @@ func (cc *CommentController) GetCommentsByBlogPostId(c *gin.Context) { comments, err := cc.commentService.GetCommentsByBlogPostId(blogPostId) if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + c.JSON(http.StatusInternalServerError, dto.Response{ + Success: false, + Message: "Failed to retrieve comments", + Error: err.Error(), + }) return } - c.JSON(http.StatusOK, comments) + c.JSON(http.StatusOK, dto.Response{ + Success: true, + Message: "Comments retrieved successfully", + Data: comments, + }) } func (cc *CommentController) UpdateComment(c *gin.Context) { var comment entities.Comment if err := c.ShouldBindJSON(&comment); err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + c.JSON(http.StatusBadRequest, dto.Response{ + Success: false, + Message: "Failed to bind comment data", + Error: err.Error(), + }) return } @@ -73,9 +105,17 @@ func (cc *CommentController) UpdateComment(c *gin.Context) { updatedComment, err := cc.commentService.UpdateComment(&comment) if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + c.JSON(http.StatusInternalServerError, dto.Response{ + Success: false, + Message: "Failed to update comment", + Error: err.Error(), + }) return } - c.JSON(http.StatusOK, updatedComment) + c.JSON(http.StatusOK, dto.Response{ + Success: true, + Message: "Comment updated successfully", + Data: updatedComment, + }) } diff --git a/backend/AAiT-backend-group-11/delivery/controller/popularity_tracking_controller.go b/backend/AAiT-backend-group-11/delivery/controller/popularity_tracking_controller.go index 26b12f161..5d81439e1 100644 --- a/backend/AAiT-backend-group-11/delivery/controller/popularity_tracking_controller.go +++ b/backend/AAiT-backend-group-11/delivery/controller/popularity_tracking_controller.go @@ -1,6 +1,7 @@ package controller import ( + "backend-starter-project/domain/dto" "backend-starter-project/domain/interfaces" "net/http" @@ -23,17 +24,25 @@ func (ptc *PopularityTrackingController) LikeBlogPost(c *gin.Context) { userId, ok := c.Get("userId") if !ok { - c.JSON(http.StatusInternalServerError, gin.H{"error": "User not found"}) + c.JSON(http.StatusInternalServerError, dto.Response{ + Success: false, + Error: "User not found", + }) return } err := ptc.popularityTrackingService.LikeBlogPost(id, userId.(string)) if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + c.JSON(http.StatusInternalServerError, dto.Response{ + Success: false, + Error: err.Error(), + }) return } - + c.JSON(http.StatusOK, dto.Response{ + Success: true, + }) } func (ptc *PopularityTrackingController) DislikeBlogPost(c *gin.Context) { @@ -41,15 +50,23 @@ func (ptc *PopularityTrackingController) DislikeBlogPost(c *gin.Context) { userId, ok := c.Get("userId") if !ok { - c.JSON(http.StatusInternalServerError, gin.H{"error": "User not found"}) + c.JSON(http.StatusInternalServerError, dto.Response{ + Success: false, + Error: "User not found", + }) return } err := ptc.popularityTrackingService.DislikeBlogPost(id, userId.(string)) if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + c.JSON(http.StatusInternalServerError, dto.Response{ + Success: false, + Error: err.Error(), + }) return } - + c.JSON(http.StatusOK, dto.Response{ + Success: true, + }) } \ No newline at end of file diff --git a/backend/AAiT-backend-group-11/delivery/controller/tests/auth_controller_test.go b/backend/AAiT-backend-group-11/delivery/controller/tests/auth_controller_test.go index e2d1e1649..b7596e489 100644 --- a/backend/AAiT-backend-group-11/delivery/controller/tests/auth_controller_test.go +++ b/backend/AAiT-backend-group-11/delivery/controller/tests/auth_controller_test.go @@ -1,255 +1,264 @@ package controller_test -// import ( -// "backend-starter-project/delivery/controller" -// "backend-starter-project/domain/dto" -// "backend-starter-project/domain/entities" -// "bytes" -// "encoding/json" -// "net/http" -// "net/http/httptest" -// "testing" - -// "github.com/gin-gonic/gin" -// "github.com/stretchr/testify/mock" -// "github.com/stretchr/testify/suite" -// ) - -// type AuthControllerTestSuite struct { -// suite.Suite -// authController *controller.AuthController -// authServiceMock *MockAuthService -// passwordResetServiceMock *MockPasswordResetService -// router *gin.Engine -// } - -// // MockAuthService is a mock implementation of the AuthenticationService interface -// type MockAuthService struct { -// mock.Mock -// } - -// func (m *MockAuthService) RegisterUser(user *entities.User) (*entities.User, error) { -// args := m.Called(user) -// return args.Get(0).(*entities.User), args.Error(1) -// } - -// func (m *MockAuthService) Login(emailOrUsername, password string) (*entities.RefreshToken, string, error) { -// args := m.Called(emailOrUsername, password) -// return args.Get(0).(*entities.RefreshToken), args.String(1), args.Error(2) -// } - -// func (m *MockAuthService) Logout(userId string) error { -// args := m.Called(userId) -// return args.Error(0) -// } - -// func (m *MockAuthService) RefreshAccessToken(token *entities.RefreshToken) (string, error) { -// args := m.Called(token) -// return args.String(0), args.Error(1) -// } - -// func (m *MockAuthService) VerifyEmail(email string, code string) error { -// args := m.Called(email, code) -// return args.Error(0) -// } - -// func (m *MockAuthService) ResendOtp(request entities.ResendOTPRequest) error { -// args := m.Called(request) -// return args.Error(0) -// } - -// // MockPasswordResetService is a mock implementation of the PasswordResetService interface -// type MockPasswordResetService struct { -// mock.Mock -// } - -// func (m *MockPasswordResetService) RequestPasswordReset(email string) error { -// args := m.Called(email) -// return args.Error(0) -// } - -// func (m *MockPasswordResetService) ResetPassword(token, newPassword string) error { -// args := m.Called(token, newPassword) -// return args.Error(0) -// } - -// func (m *MockPasswordResetService) GeneratePasswordResetToken(user *entities.User) (string, error) { -// args := m.Called(user) -// return args.String(0), args.Error(1) -// } - -// // SetupTest initializes the test suite -// func (suite *AuthControllerTestSuite) SetupTest() { -// suite.authServiceMock = new(MockAuthService) -// suite.passwordResetServiceMock = new(MockPasswordResetService) -// suite.authController = controller.NewAuthController(suite.authServiceMock, suite.passwordResetServiceMock) - -// gin.SetMode(gin.TestMode) -// suite.router = gin.Default() -// } - -// // Test RegisterUser -// func (suite *AuthControllerTestSuite) TestRegisterUser() { -// suite.router.POST("/register", suite.authController.RegisterUser) - -// user := &entities.User{Username: "testuser", Email: "test@example.com", Password: "password123"} -// suite.authServiceMock.On("RegisterUser", mock.AnythingOfType("*entities.User")).Return(user, nil) - -// userRequest := dto.UserCreateRequestDTO{ -// Username: "testuser", -// Email: "test@example.com", -// Password: "password123", -// } - -// jsonValue, _ := json.Marshal(userRequest) -// req, _ := http.NewRequest("POST", "/register", bytes.NewBuffer(jsonValue)) -// req.Header.Set("Content-Type", "application/json") - -// w := httptest.NewRecorder() -// suite.router.ServeHTTP(w, req) - -// suite.Equal(http.StatusCreated, w.Code) -// suite.authServiceMock.AssertExpectations(suite.T()) -// } - -// // Test Login -// func (suite *AuthControllerTestSuite) TestLogin() { -// suite.router.POST("/login", suite.authController.Login) - -// refreshToken := &entities.RefreshToken{ -// UserID: "userID", -// Token: "refreshToken", -// } +import ( + "backend-starter-project/delivery/controller" + "backend-starter-project/domain/dto" + "backend-starter-project/domain/entities" + "bytes" + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + + "github.com/gin-gonic/gin" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/suite" +) + +type AuthControllerTestSuite struct { + suite.Suite + authController *controller.AuthController + authServiceMock *MockAuthService + passwordResetServiceMock *MockPasswordResetService + router *gin.Engine +} + +// MockAuthService is a mock implementation of the AuthenticationService interface +type MockAuthService struct { + mock.Mock +} + +func (m *MockAuthService) RegisterUser(user *entities.User) (*entities.User, error) { + args := m.Called(user) + return args.Get(0).(*entities.User), args.Error(1) +} + +func (m *MockAuthService) Login(emailOrUsername, password string) (*entities.RefreshToken, string, error) { + args := m.Called(emailOrUsername, password) + return args.Get(0).(*entities.RefreshToken), args.String(1), args.Error(2) +} + +func (m *MockAuthService) Logout(userId string) error { + args := m.Called(userId) + return args.Error(0) +} + +func (m *MockAuthService) RefreshAccessToken(token *entities.RefreshToken) (string, error) { + args := m.Called(token) + return args.String(0), args.Error(1) +} + +func (m *MockAuthService) VerifyEmail(email string, code string) error { + args := m.Called(email, code) + return args.Error(0) +} + +func (m *MockAuthService) ResendOtp(request entities.ResendOTPRequest) error { + args := m.Called(request) + return args.Error(0) +} + +// MockPasswordResetService is a mock implementation of the PasswordResetService interface +type MockPasswordResetService struct { + mock.Mock +} + +func (m *MockPasswordResetService) RequestPasswordReset(email string) error { + args := m.Called(email) + return args.Error(0) +} + +func (m *MockPasswordResetService) ResetPassword(token, newPassword string) error { + args := m.Called(token, newPassword) + return args.Error(0) +} + +func (m *MockPasswordResetService) GeneratePasswordResetToken(user *entities.User) (string, error) { + args := m.Called(user) + return args.String(0), args.Error(1) +} + +// SetupTest initializes the test suite +func (suite *AuthControllerTestSuite) SetupTest() { + suite.authServiceMock = new(MockAuthService) + suite.passwordResetServiceMock = new(MockPasswordResetService) + suite.authController = controller.NewAuthController(suite.authServiceMock, suite.passwordResetServiceMock) + + gin.SetMode(gin.TestMode) + suite.router = gin.Default() +} + +// Test RegisterUser +func (suite *AuthControllerTestSuite) TestRegisterUser() { + suite.router.POST("/register", suite.authController.RegisterUser) + + user := &entities.User{Username: "testuser", Email: "test@example.com", Password: "password123"} + suite.authServiceMock.On("RegisterUser", mock.AnythingOfType("*entities.User")).Return(user, nil) + + userRequest := dto.UserCreateRequestDTO{ + Username: "testuser", + Email: "test@example.com", + Password: "password123", + } + + jsonValue, _ := json.Marshal(userRequest) + req, _ := http.NewRequest("POST", "/register", bytes.NewBuffer(jsonValue)) + req.Header.Set("Content-Type", "application/json") + + w := httptest.NewRecorder() + suite.router.ServeHTTP(w, req) + + suite.Equal(http.StatusCreated, w.Code) + suite.authServiceMock.AssertExpectations(suite.T()) +} + +// Test Login +func (suite *AuthControllerTestSuite) TestLogin() { + suite.router.POST("/login", suite.authController.Login) + + refreshToken := &entities.RefreshToken{ + UserID: "userID", + Token: "refreshToken", + } + + suite.authServiceMock.On("Login", "test@example.com", "password123").Return(refreshToken, "accessToken", nil) -// suite.authServiceMock.On("Login", "test@example.com", "password123").Return(refreshToken, "accessToken", nil) + loginRequest := dto.LoginDto{ + Email: "test@example.com", + Password: "password123", + } -// loginRequest := dto.LoginDto{ -// Email: "test@example.com", -// Password: "password123", -// } + jsonValue, _ := json.Marshal(loginRequest) + req, _ := http.NewRequest("POST", "/login", bytes.NewBuffer(jsonValue)) + req.Header.Set("Content-Type", "application/json") -// jsonValue, _ := json.Marshal(loginRequest) -// req, _ := http.NewRequest("POST", "/login", bytes.NewBuffer(jsonValue)) -// req.Header.Set("Content-Type", "application/json") + w := httptest.NewRecorder() + suite.router.ServeHTTP(w, req) -// w := httptest.NewRecorder() -// suite.router.ServeHTTP(w, req) + suite.Equal(http.StatusOK, w.Code) + suite.authServiceMock.AssertExpectations(suite.T()) +} -// suite.Equal(http.StatusOK, w.Code) -// suite.authServiceMock.AssertExpectations(suite.T()) -// } +func (suite *AuthControllerTestSuite) TestLogout() { + suite.router.POST("/logout", func(c *gin.Context) { + // Setting the userId in the Gin context + c.Set("userId", "userID") + suite.authController.Logout(c) + }) -// func (suite *AuthControllerTestSuite) TestLogout() { -// suite.router.POST("/logout", suite.authController.Logout) + // Mock the Logout method to return nil + suite.authServiceMock.On("Logout", "userID").Return(nil) -// suite.authServiceMock.On("Logout", "userID").Return(nil) + w := httptest.NewRecorder() + req, _ := http.NewRequest("POST", "/logout", nil) -// w := httptest.NewRecorder() -// req, _ := http.NewRequest("POST", "/logout", nil) -// req.Header.Set("userId", "userID") + suite.router.ServeHTTP(w, req) -// suite.router.ServeHTTP(w, req) + // Check if the Logout method was called with the correct arguments + suite.authServiceMock.AssertCalled(suite.T(), "Logout", "userID") -// // Check if the Logout method was called -// suite.authServiceMock.AssertCalled(suite.T(), "Logout", "userID") + // Check the response code + suite.Equal(http.StatusOK, w.Code) + suite.authServiceMock.AssertExpectations(suite.T()) +} -// // Check the response code -// suite.Equal(http.StatusOK, w.Code) -// suite.authServiceMock.AssertExpectations(suite.T()) -// } +// Test RefreshAccessToken +func (suite *AuthControllerTestSuite) TestRefreshAccessToken() { + suite.router.POST("/refresh-token", suite.authController.RefreshAccessToken) -// // Test RefreshAccessToken -// func (suite *AuthControllerTestSuite) TestRefreshAccessToken() { -// suite.router.POST("/refresh-token", suite.authController.RefreshAccessToken) + refreshToken := &entities.RefreshToken{Token: "oldRefreshToken"} + suite.authServiceMock.On("RefreshAccessToken", refreshToken).Return("newAccessToken", nil) -// refreshToken := &entities.RefreshToken{Token: "oldRefreshToken"} -// suite.authServiceMock.On("RefreshAccessToken", refreshToken).Return("newAccessToken", nil) + jsonValue, _ := json.Marshal(refreshToken) + req, _ := http.NewRequest("POST", "/refresh-token", bytes.NewBuffer(jsonValue)) + req.Header.Set("Content-Type", "application/json") -// jsonValue, _ := json.Marshal(refreshToken) -// req, _ := http.NewRequest("POST", "/refresh-token", bytes.NewBuffer(jsonValue)) -// req.Header.Set("Content-Type", "application/json") + w := httptest.NewRecorder() + suite.router.ServeHTTP(w, req) -// w := httptest.NewRecorder() -// suite.router.ServeHTTP(w, req) + suite.Equal(http.StatusOK, w.Code) + suite.authServiceMock.AssertExpectations(suite.T()) +} -// suite.Equal(http.StatusOK, w.Code) -// suite.authServiceMock.AssertExpectations(suite.T()) -// } +// Test VerifyEmail +func (suite *AuthControllerTestSuite) TestVerifyEmail() { + suite.router.POST("/verify-email", suite.authController.VerifyEmail) -// // Test VerifyEmail -// func (suite *AuthControllerTestSuite) TestVerifyEmail() { -// suite.router.POST("/verify-email", suite.authController.VerifyEmail) + emailVerification := entities.EmailVerificationRequest{Email: "test@example.com", Code: "123456"} + suite.authServiceMock.On("VerifyEmail", "test@example.com", "123456").Return(nil) -// emailVerification := entities.EmailVerificationRequest{Email: "test@example.com", Code: "123456"} -// suite.authServiceMock.On("VerifyEmail", "test@example.com", "123456").Return(nil) + jsonValue, _ := json.Marshal(emailVerification) + req, _ := http.NewRequest("POST", "/verify-email", bytes.NewBuffer(jsonValue)) + req.Header.Set("Content-Type", "application/json") -// jsonValue, _ := json.Marshal(emailVerification) -// req, _ := http.NewRequest("POST", "/verify-email", bytes.NewBuffer(jsonValue)) -// req.Header.Set("Content-Type", "application/json") + w := httptest.NewRecorder() + suite.router.ServeHTTP(w, req) -// w := httptest.NewRecorder() -// suite.router.ServeHTTP(w, req) + // Check that the status code is 200 OK + suite.Equal(http.StatusOK, w.Code) + + // Check that the VerifyEmail method was called with the correct arguments + suite.authServiceMock.AssertCalled(suite.T(), "VerifyEmail", "test@example.com", "123456") + suite.authServiceMock.AssertExpectations(suite.T()) +} -// suite.Equal(http.StatusOK, w.Code) -// suite.authServiceMock.AssertExpectations(suite.T()) -// } -// // Test RequestPasswordReset -// func (suite *AuthControllerTestSuite) TestRequestPasswordReset() { -// suite.router.POST("/request-password-reset", suite.authController.RequestPasswordReset) +// Test RequestPasswordReset +func (suite *AuthControllerTestSuite) TestRequestPasswordReset() { + suite.router.POST("/request-password-reset", suite.authController.RequestPasswordReset) -// suite.passwordResetServiceMock.On("RequestPasswordReset", "test@example.com").Return(nil) + suite.passwordResetServiceMock.On("RequestPasswordReset", "test@example.com").Return(nil) -// forgetPasswordRequest := entities.ForgetPasswordRequest{Email: "test@example.com"} -// jsonValue, _ := json.Marshal(forgetPasswordRequest) -// req, _ := http.NewRequest("POST", "/request-password-reset", bytes.NewBuffer(jsonValue)) -// req.Header.Set("Content-Type", "application/json") + forgetPasswordRequest := entities.ForgetPasswordRequest{Email: "test@example.com"} + jsonValue, _ := json.Marshal(forgetPasswordRequest) + req, _ := http.NewRequest("POST", "/request-password-reset", bytes.NewBuffer(jsonValue)) + req.Header.Set("Content-Type", "application/json") -// w := httptest.NewRecorder() -// suite.router.ServeHTTP(w, req) + w := httptest.NewRecorder() + suite.router.ServeHTTP(w, req) -// suite.Equal(http.StatusOK, w.Code) -// suite.passwordResetServiceMock.AssertExpectations(suite.T()) -// } + suite.Equal(http.StatusOK, w.Code) + suite.passwordResetServiceMock.AssertExpectations(suite.T()) +} -// // Test ResetPassword -// func (suite *AuthControllerTestSuite) TestResetPassword() { -// suite.router.POST("/reset-password", suite.authController.ResetPassword) +// Test ResetPassword +func (suite *AuthControllerTestSuite) TestResetPassword() { + suite.router.POST("/reset-password", suite.authController.ResetPassword) -// passwordReset := entities.PasswordReset{Token: "resetToken", NewPassword: "newPassword"} -// suite.passwordResetServiceMock.On("ResetPassword", "resetToken", "newPassword").Return(nil) + passwordReset := entities.PasswordReset{Token: "resetToken", NewPassword: "newPassword"} + suite.passwordResetServiceMock.On("ResetPassword", "resetToken", "newPassword").Return(nil) -// jsonValue, _ := json.Marshal(passwordReset) -// req, _ := http.NewRequest("POST", "/reset-password", bytes.NewBuffer(jsonValue)) -// req.Header.Set("Content-Type", "application/json") + jsonValue, _ := json.Marshal(passwordReset) + req, _ := http.NewRequest("POST", "/reset-password", bytes.NewBuffer(jsonValue)) + req.Header.Set("Content-Type", "application/json") -// w := httptest.NewRecorder() -// suite.router.ServeHTTP(w, req) + w := httptest.NewRecorder() + suite.router.ServeHTTP(w, req) -// suite.Equal(http.StatusOK, w.Code) -// suite.passwordResetServiceMock.AssertExpectations(suite.T()) -// } + suite.Equal(http.StatusOK, w.Code) + suite.passwordResetServiceMock.AssertExpectations(suite.T()) +} -// // Test ResendOtp -// func (suite *AuthControllerTestSuite) TestResendOtp() { -// suite.router.POST("/resend-otp", suite.authController.ResendOtp) +// Test ResendOtp +func (suite *AuthControllerTestSuite) TestResendOtp() { + suite.router.POST("/resend-otp", suite.authController.ResendOtp) -// otpRequest := entities.ResendOTPRequest{Email: "test@example.com"} -// suite.authServiceMock.On("ResendOtp", otpRequest).Return(nil) + otpRequest := entities.ResendOTPRequest{Email: "test@example.com"} + suite.authServiceMock.On("ResendOtp", otpRequest).Return(nil) -// jsonValue, _ := json.Marshal(otpRequest) -// req, _ := http.NewRequest("POST", "/resend-otp", bytes.NewBuffer(jsonValue)) -// req.Header.Set("Content-Type", "application/json") + jsonValue, _ := json.Marshal(otpRequest) + req, _ := http.NewRequest("POST", "/resend-otp", bytes.NewBuffer(jsonValue)) + req.Header.Set("Content-Type", "application/json") -// w := httptest.NewRecorder() -// suite.router.ServeHTTP(w, req) + w := httptest.NewRecorder() + suite.router.ServeHTTP(w, req) -// suite.Equal(http.StatusOK, w.Code) -// suite.authServiceMock.AssertExpectations(suite.T()) -// } + suite.Equal(http.StatusOK, w.Code) + suite.authServiceMock.AssertExpectations(suite.T()) +} -// // Test suite runner -// func TestAuthControllerTestSuite(t *testing.T) { -// suite.Run(t, new(AuthControllerTestSuite)) -// } +// Test suite runner +func TestAuthControllerTestSuite(t *testing.T) { + suite.Run(t, new(AuthControllerTestSuite)) +} diff --git a/backend/AAiT-backend-group-11/delivery/controller/tests/blog_controller_test.go b/backend/AAiT-backend-group-11/delivery/controller/tests/blog_controller_test.go index 55c64d6ae..3d55e40cc 100644 --- a/backend/AAiT-backend-group-11/delivery/controller/tests/blog_controller_test.go +++ b/backend/AAiT-backend-group-11/delivery/controller/tests/blog_controller_test.go @@ -1 +1,238 @@ -package controller_test \ No newline at end of file +package controller_test + +import ( + "bytes" + "encoding/json" + "backend-starter-project/delivery/controller" + "backend-starter-project/domain/dto" + "errors" + "net/http" + "net/http/httptest" + "testing" + + "github.com/gin-gonic/gin" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/suite" +) + +type BlogControllerTestSuite struct { + suite.Suite + mockBlogService *MockBlogService + blogController *controller.BlogController +} + +type MockBlogService struct { + mock.Mock +} + +func (m *MockBlogService) CreateBlogPost(blogPost *dto.AddBlogRequest, userId string) (*dto.AddBlogResponse, error) { + args := m.Called(blogPost, userId) + return args.Get(0).(*dto.AddBlogResponse), args.Error(1) +} + +func (m *MockBlogService) GetBlogPostById(blogPostId string, userId string) (*dto.GetBlogByIDResponse, error) { + args := m.Called(blogPostId, userId) + return args.Get(0).(*dto.GetBlogByIDResponse), args.Error(1) +} + +func (m *MockBlogService) GetBlogPosts(page int, pageSize int, sortBy string) (*dto.GetBlogPostsResponse, int, error) { + args := m.Called(page, pageSize, sortBy) + return args.Get(0).(*dto.GetBlogPostsResponse), args.Int(1), args.Error(2) +} + +func (m *MockBlogService) UpdateBlogPost(blogPost *dto.UpdateBlogRequest, userId string) (*dto.UpdateBlogResponse, error) { + args := m.Called(blogPost, userId) + return args.Get(0).(*dto.UpdateBlogResponse), args.Error(1) +} + +func (m *MockBlogService) DeleteBlogPost(blogPostId string, userId string, role string) error { + args := m.Called(blogPostId, userId, role) + return args.Error(0) +} + +func (m *MockBlogService) SearchBlogPosts(searchText string) (*dto.GetBlogPostsResponse, error) { + args := m.Called(searchText) + return args.Get(0).(*dto.GetBlogPostsResponse), args.Error(1) +} + +func (m *MockBlogService) FilterBlogPosts(filterReq dto.FilterBlogPostsRequest) (*dto.GetBlogPostsResponse, error) { + args := m.Called(filterReq) + return args.Get(0).(*dto.GetBlogPostsResponse), args.Error(1) +} + +func (suite *BlogControllerTestSuite) SetupTest() { + suite.mockBlogService = new(MockBlogService) + suite.blogController = controller.NewBlogController(suite.mockBlogService) +} + +func (suite *BlogControllerTestSuite) TestCreateBlogPost_Success() { + blogPost := dto.AddBlogRequest{ + Title: "Test Title", + Content: "Test Content", + Tags: []string{"test", "golang"}, + Username: "TestUser", + } + userId := "123" + createdBlogPost := &dto.AddBlogResponse{ + ID: "1", + AutherID: "123", + AutherUserName: "TestUser", + Title: "Test Title", + Content: "Test Content", + Tags: []string{"test", "golang"}, + } + + suite.mockBlogService.On("CreateBlogPost", &blogPost, userId).Return(createdBlogPost, nil) + + // Create a request with the JSON body + body, _ := json.Marshal(blogPost) + w := httptest.NewRecorder() + c, _ := gin.CreateTestContext(w) + c.Set("userId", userId) + c.Set("username", "TestUser") + c.Request = httptest.NewRequest("POST", "/blog", bytes.NewBuffer(body)) + c.Request.Header.Set("Content-Type", "application/json") + + suite.blogController.CreateBlogPost(c) + + suite.Equal(http.StatusOK, w.Code) + suite.Contains(w.Body.String(), "Blog post created successfully") + suite.mockBlogService.AssertCalled(suite.T(), "CreateBlogPost", &blogPost, userId) +} + +func (suite *BlogControllerTestSuite) TestCreateBlogPost_Failure() { + blogPost := dto.AddBlogRequest{ + Title: "Test Title", + Content: "Test Content", + Tags: []string{"test", "golang"}, + Username: "TestUser", + } + userId := "123" + + // Mock the service to return nil as the first argument and an error as the second + suite.mockBlogService.On("CreateBlogPost", &blogPost, userId).Return((*dto.AddBlogResponse)(nil), errors.New("error creating blog post")) + + body, _ := json.Marshal(blogPost) + w := httptest.NewRecorder() + c, _ := gin.CreateTestContext(w) + c.Set("userId", userId) + c.Set("username", "TestUser") + c.Request = httptest.NewRequest("POST", "/blog", bytes.NewBuffer(body)) + c.Request.Header.Set("Content-Type", "application/json") + + suite.blogController.CreateBlogPost(c) + + suite.Equal(http.StatusInternalServerError, w.Code) + suite.Contains(w.Body.String(), "Error while creating user") + suite.mockBlogService.AssertCalled(suite.T(), "CreateBlogPost", &blogPost, userId) +} + + +func (suite *BlogControllerTestSuite) TestGetBlogPost_Success() { + blogPostId := "1" + userId := "123" + blogPost := &dto.GetBlogByIDResponse{ + ID: "1", + AutherID: "123", + AutherUserName: "TestUser", + Title: "Test Title", + Content: "Test Content", + } + + suite.mockBlogService.On("GetBlogPostById", blogPostId, userId).Return(blogPost, nil) + + w := httptest.NewRecorder() + c, _ := gin.CreateTestContext(w) + c.Set("userId", userId) + c.Params = gin.Params{{Key: "id", Value: blogPostId}} + + suite.blogController.GetBlogPost(c) + + suite.Equal(http.StatusOK, w.Code) + suite.Contains(w.Body.String(), "Test Title") + suite.mockBlogService.AssertCalled(suite.T(), "GetBlogPostById", blogPostId, userId) +} + +func (suite *BlogControllerTestSuite) TestGetBlogPost_Failure() { + blogPostId := "1" + userId := "123" + + suite.mockBlogService.On("GetBlogPostById", blogPostId, userId).Return(&dto.GetBlogByIDResponse{}, errors.New("error getting blog post")) + + w := httptest.NewRecorder() + c, _ := gin.CreateTestContext(w) + c.Set("userId", userId) + c.Params = gin.Params{{Key: "id", Value: blogPostId}} + + suite.blogController.GetBlogPost(c) + + suite.Equal(http.StatusInternalServerError, w.Code) + suite.Contains(w.Body.String(), "Error getting blog post") + suite.mockBlogService.AssertCalled(suite.T(), "GetBlogPostById", blogPostId, userId) +} + +func (suite *BlogControllerTestSuite) TestGetBlogPosts_Success() { + page := 1 + pageSize := 10 + sortBy := "createdAt" + totalPosts := 50 + blogPosts := &dto.GetBlogPostsResponse{ + BlogPosts: []interface{}{ + dto.GetBlogByIDResponse{ + ID: "1", + AutherID: "123", + AutherUserName: "TestUser", + Title: "Test Title 1", + Content: "Test Content 1", + }, + dto.GetBlogByIDResponse{ + ID: "2", + AutherID: "124", + AutherUserName: "TestUser2", + Title: "Test Title 2", + Content: "Test Content 2", + }, + }, + Pagination: dto.Pagination{ + CurrentPage: page, + PageSize: pageSize, + TotalPages: (totalPosts + pageSize - 1) / pageSize, + TotalPosts: totalPosts, + }, + } + + suite.mockBlogService.On("GetBlogPosts", page, pageSize, sortBy).Return(blogPosts, totalPosts, nil) + + w := httptest.NewRecorder() + c, _ := gin.CreateTestContext(w) + c.Request = httptest.NewRequest("GET", "/blogs?page=1&pageSize=10&sortBy=createdAt", nil) + + suite.blogController.GetBlogPosts(c) + + suite.Equal(http.StatusOK, w.Code) + suite.Contains(w.Body.String(), "Test Title 1") + suite.Contains(w.Body.String(), "Test Title 2") + suite.mockBlogService.AssertCalled(suite.T(), "GetBlogPosts", page, pageSize, sortBy) +} + +func (suite *BlogControllerTestSuite) TestGetBlogPosts_Failure() { + page := 1 + pageSize := 10 + sortBy := "createdAt" + + suite.mockBlogService.On("GetBlogPosts", page, pageSize, sortBy).Return(&dto.GetBlogPostsResponse{}, 0, errors.New("error getting blog posts")) + + w := httptest.NewRecorder() + c, _ := gin.CreateTestContext(w) + c.Request = httptest.NewRequest("GET", "/blogs?page=1&pageSize=10&sortBy=createdAt", nil) + + suite.blogController.GetBlogPosts(c) + + suite.Equal(http.StatusInternalServerError, w.Code) + suite.Contains(w.Body.String(), "Error while getting blog posts") + suite.mockBlogService.AssertCalled(suite.T(), "GetBlogPosts", page, pageSize, sortBy) +} + +func TestBlogControllerTestSuite(t *testing.T) { + suite.Run(t, new(BlogControllerTestSuite)) +} diff --git a/backend/AAiT-backend-group-11/delivery/controller/tests/comment_controller_test.go b/backend/AAiT-backend-group-11/delivery/controller/tests/comment_controller_test.go index 55c64d6ae..5a8b8cc5a 100644 --- a/backend/AAiT-backend-group-11/delivery/controller/tests/comment_controller_test.go +++ b/backend/AAiT-backend-group-11/delivery/controller/tests/comment_controller_test.go @@ -1 +1,177 @@ -package controller_test \ No newline at end of file +package controller_test + +import ( + "bytes" + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + + "backend-starter-project/delivery/controller" + "backend-starter-project/domain/dto" + "backend-starter-project/domain/entities" + + "github.com/gin-gonic/gin" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/suite" + "go.mongodb.org/mongo-driver/bson/primitive" +) + +// MockCommentService is a mock implementation of the CommentService interface +type MockCommentService struct { + mock.Mock +} + +func (m *MockCommentService) AddComment(comment *entities.Comment) (*entities.Comment, error) { + args := m.Called(comment) + return args.Get(0).(*entities.Comment), args.Error(1) +} + +func (m *MockCommentService) DeleteComment(commentId string) error { + args := m.Called(commentId) + return args.Error(0) +} + +func (m *MockCommentService) GetCommentsByBlogPostId(blogPostId string) ([]entities.Comment, error) { + args := m.Called(blogPostId) + return args.Get(0).([]entities.Comment), args.Error(1) +} + +func (m *MockCommentService) UpdateComment(comment *entities.Comment) (*entities.Comment, error) { + args := m.Called(comment) + return args.Get(0).(*entities.Comment), args.Error(1) +} + +// CommentControllerSuite is the test suite for the CommentController +type CommentControllerSuite struct { + suite.Suite + mockService *MockCommentService + cc *controller.CommentController + router *gin.Engine +} + +func (suite *CommentControllerSuite) SetupSuite() { + gin.SetMode(gin.TestMode) + suite.mockService = new(MockCommentService) + suite.cc = controller.NewCommentController(suite.mockService) + suite.router = gin.Default() +} + +func (suite *CommentControllerSuite) SetupTest() { + // Common setup tasks before each test can go here +} + +func (suite *CommentControllerSuite) TestAddComment() { + suite.router.POST("/comments/:blogId", suite.cc.AddComment) + + comment := entities.Comment{ + ID: primitive.NewObjectID(), + Content: "Test comment", + BlogPostID: primitive.NewObjectID(), + AuthorID: primitive.NewObjectID(), + } + + suite.mockService.On("AddComment", mock.AnythingOfType("*entities.Comment")).Return(&comment, nil) + + body, _ := json.Marshal(comment) + req, _ := http.NewRequest(http.MethodPost, "/comments/"+comment.BlogPostID.Hex(), bytes.NewBuffer(body)) + req.Header.Set("Content-Type", "application/json") + req.Header.Set("userId", comment.AuthorID.Hex()) + + w := httptest.NewRecorder() + suite.router.ServeHTTP(w, req) + + suite.Equal(http.StatusOK, w.Code) + + var response dto.Response + err := json.Unmarshal(w.Body.Bytes(), &response) + suite.NoError(err) + suite.True(response.Success) + suite.Equal("Comment added successfully", response.Message) + suite.NotNil(response.Data) +} + +func (suite *CommentControllerSuite) TestDeleteComment() { + suite.router.DELETE("/comments/:id", suite.cc.DeleteComment) + + commentID := primitive.NewObjectID().Hex() + + suite.mockService.On("DeleteComment", commentID).Return(nil) + + req, _ := http.NewRequest(http.MethodDelete, "/comments/"+commentID, nil) + + w := httptest.NewRecorder() + suite.router.ServeHTTP(w, req) + + suite.Equal(http.StatusOK, w.Code) + + var response dto.Response + err := json.Unmarshal(w.Body.Bytes(), &response) + suite.NoError(err) + suite.True(response.Success) + suite.Equal("Comment deleted successfully", response.Message) +} + +func (suite *CommentControllerSuite) TestGetCommentsByBlogPostId() { + suite.router.GET("/comments/:blogId", suite.cc.GetCommentsByBlogPostId) + + blogPostID := primitive.NewObjectID().Hex() + comments := []entities.Comment{ + { + ID: primitive.NewObjectID(), + Content: "Test comment", + BlogPostID: primitive.NewObjectID(), + AuthorID: primitive.NewObjectID(), + }, + } + + suite.mockService.On("GetCommentsByBlogPostId", blogPostID).Return(comments, nil) + + req, _ := http.NewRequest(http.MethodGet, "/comments/"+blogPostID, nil) + + w := httptest.NewRecorder() + suite.router.ServeHTTP(w, req) + + suite.Equal(http.StatusOK, w.Code) + + var response dto.Response + err := json.Unmarshal(w.Body.Bytes(), &response) + suite.NoError(err) + suite.True(response.Success) + suite.Equal("Comments retrieved successfully", response.Message) + suite.NotNil(response.Data) +} + +func (suite *CommentControllerSuite) TestUpdateComment() { + suite.router.PUT("/comments", suite.cc.UpdateComment) + + comment := entities.Comment{ + ID: primitive.NewObjectID(), + Content: "Test comment", + BlogPostID: primitive.NewObjectID(), + AuthorID: primitive.NewObjectID(), + } + + suite.mockService.On("UpdateComment", mock.AnythingOfType("*entities.Comment")).Return(&comment, nil) + + body, _ := json.Marshal(comment) + req, _ := http.NewRequest(http.MethodPut, "/comments", bytes.NewBuffer(body)) + req.Header.Set("Content-Type", "application/json") + req.Header.Set("userId", comment.AuthorID.Hex()) + + w := httptest.NewRecorder() + suite.router.ServeHTTP(w, req) + + suite.Equal(http.StatusOK, w.Code) + + var response dto.Response + err := json.Unmarshal(w.Body.Bytes(), &response) + suite.NoError(err) + suite.True(response.Success) + suite.Equal("Comment updated successfully", response.Message) + suite.NotNil(response.Data) +} + +func TestCommentControllerSuite(t *testing.T) { + suite.Run(t, new(CommentControllerSuite)) +} diff --git a/backend/AAiT-backend-group-11/delivery/controller/tests/popularity_controller_test.go b/backend/AAiT-backend-group-11/delivery/controller/tests/popularity_controller_test.go new file mode 100644 index 000000000..0cf0e282a --- /dev/null +++ b/backend/AAiT-backend-group-11/delivery/controller/tests/popularity_controller_test.go @@ -0,0 +1,178 @@ +package controller + +import ( + "backend-starter-project/delivery/controller" + "backend-starter-project/domain/dto" + "encoding/json" + "errors" + "net/http" + "net/http/httptest" + "testing" + + "github.com/gin-gonic/gin" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/suite" +) + +type PopularityTrackingControllerTestSuite struct { + suite.Suite + controller *controller.PopularityTrackingController + mockService *MockPopularityTrackingService +} + +type MockPopularityTrackingService struct { + mock.Mock +} + +func (m *MockPopularityTrackingService) IncrementViewCount(blogPostId string) error { + args := m.Called(blogPostId) + return args.Error(0) +} + +func (m *MockPopularityTrackingService) LikeBlogPost(blogPostId string, userId string) error { + args := m.Called(blogPostId, userId) + return args.Error(0) +} + +func (m *MockPopularityTrackingService) DislikeBlogPost(blogPostId string, userId string) error { + args := m.Called(blogPostId, userId) + return args.Error(0) +} + +func (m *MockPopularityTrackingService) GetPopularityMetrics(blogPostId string) (map[string]int, error) { + args := m.Called(blogPostId) + return args.Get(0).(map[string]int), args.Error(1) +} + +func (suite *PopularityTrackingControllerTestSuite) SetupTest() { + suite.mockService = new(MockPopularityTrackingService) + suite.controller = controller.NewPopularityTrackingController(suite.mockService) +} + +func (suite *PopularityTrackingControllerTestSuite) TestLikeBlogPostSuccess() { + // Arrange + w := httptest.NewRecorder() + c, _ := gin.CreateTestContext(w) + c.Params = gin.Params{{Key: "id", Value: "1"}} + c.Set("userId", "user123") + + suite.mockService.On("LikeBlogPost", "1", "user123").Return(nil) + + // Act + suite.controller.LikeBlogPost(c) + + // Assert + suite.Equal(http.StatusOK, w.Code) + var response dto.Response + err := json.Unmarshal(w.Body.Bytes(), &response) + suite.NoError(err) + suite.True(response.Success) + suite.mockService.AssertExpectations(suite.T()) +} + +func (suite *PopularityTrackingControllerTestSuite) TestLikeBlogPostUserNotFound() { + // Arrange + w := httptest.NewRecorder() + c, _ := gin.CreateTestContext(w) + c.Params = gin.Params{{Key: "id", Value: "1"}} + // No userId is set in the context + + // Act + suite.controller.LikeBlogPost(c) + + // Assert + suite.Equal(http.StatusInternalServerError, w.Code) + var response dto.Response + err := json.Unmarshal(w.Body.Bytes(), &response) + suite.NoError(err) + suite.False(response.Success) + suite.Equal("User not found", response.Error) +} + +func (suite *PopularityTrackingControllerTestSuite) TestLikeBlogPostError() { + // Arrange + w := httptest.NewRecorder() + c, _ := gin.CreateTestContext(w) + c.Params = gin.Params{{Key: "id", Value: "1"}} + c.Set("userId", "user123") + + suite.mockService.On("LikeBlogPost", "1", "user123").Return(errors.New("some error")) + + // Act + suite.controller.LikeBlogPost(c) + + // Assert + suite.Equal(http.StatusInternalServerError, w.Code) + var response dto.Response + err := json.Unmarshal(w.Body.Bytes(), &response) + suite.NoError(err) + suite.False(response.Success) + suite.Equal("some error", response.Error) + suite.mockService.AssertExpectations(suite.T()) +} + +func (suite *PopularityTrackingControllerTestSuite) TestDislikeBlogPostSuccess() { + // Arrange + w := httptest.NewRecorder() + c, _ := gin.CreateTestContext(w) + c.Params = gin.Params{{Key: "id", Value: "1"}} + c.Set("userId", "user123") + + suite.mockService.On("DislikeBlogPost", "1", "user123").Return(nil) + + // Act + suite.controller.DislikeBlogPost(c) + + // Assert + suite.Equal(http.StatusOK, w.Code) + var response dto.Response + err := json.Unmarshal(w.Body.Bytes(), &response) + suite.NoError(err) + suite.True(response.Success) + suite.mockService.AssertExpectations(suite.T()) +} + +func (suite *PopularityTrackingControllerTestSuite) TestDislikeBlogPostUserNotFound() { + // Arrange + w := httptest.NewRecorder() + c, _ := gin.CreateTestContext(w) + c.Params = gin.Params{{Key: "id", Value: "1"}} + // No userId is set in the context + + // Act + suite.controller.DislikeBlogPost(c) + + // Assert + suite.Equal(http.StatusInternalServerError, w.Code) + var response dto.Response + err := json.Unmarshal(w.Body.Bytes(), &response) + suite.NoError(err) + suite.False(response.Success) + suite.Equal("User not found", response.Error) +} + +func (suite *PopularityTrackingControllerTestSuite) TestDislikeBlogPostError() { + // Arrange + w := httptest.NewRecorder() + c, _ := gin.CreateTestContext(w) + c.Params = gin.Params{{Key: "id", Value: "1"}} + c.Set("userId", "user123") + + suite.mockService.On("DislikeBlogPost", "1", "user123").Return(errors.New("some error")) + + // Act + suite.controller.DislikeBlogPost(c) + + // Assert + suite.Equal(http.StatusInternalServerError, w.Code) + var response dto.Response + err := json.Unmarshal(w.Body.Bytes(), &response) + suite.NoError(err) + suite.False(response.Success) + suite.Equal("some error", response.Error) + suite.mockService.AssertExpectations(suite.T()) +} + +func TestPopularityTrackingControllerTestSuite(t *testing.T) { + suite.Run(t, new(PopularityTrackingControllerTestSuite)) +} diff --git a/backend/AAiT-backend-group-11/delivery/controller/tests/profile_controller_test.go b/backend/AAiT-backend-group-11/delivery/controller/tests/profile_controller_test.go new file mode 100644 index 000000000..2d7eb65ba --- /dev/null +++ b/backend/AAiT-backend-group-11/delivery/controller/tests/profile_controller_test.go @@ -0,0 +1,251 @@ +package controller_test + +import ( + "backend-starter-project/delivery/controller" + "backend-starter-project/domain/dto" + "backend-starter-project/domain/entities" + "errors" + "net/http" + "net/http/httptest" + "strings" + "testing" + "time" + + "github.com/gin-gonic/gin" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/suite" + "go.mongodb.org/mongo-driver/bson/primitive" +) + + +type MockProfileService struct { + mock.Mock +} + +func (m *MockProfileService) GetUserProfile(userId string) (*entities.Profile, error) { + args := m.Called(userId) + profile, ok := args.Get(0).(*entities.Profile) + if !ok { + return nil, args.Error(1) + } + return profile, args.Error(1) +} + +func (m *MockProfileService) UpdateUserProfile(profile *dto.UpdateProfileDto) (*entities.Profile, error) { + args := m.Called(profile) + profile_, ok := args.Get(0).(*entities.Profile) + if !ok { + return nil, args.Error(1) + } + return profile_, args.Error(1) +} + +func (m *MockProfileService) CreateUserProfile(profile *dto.CreateProfileDto) (*entities.Profile, error) { + args := m.Called(profile) + profile_, ok := args.Get(0).(*entities.Profile) + if !ok { + return nil, args.Error(1) + } + return profile_, args.Error(1) +} + +func (m *MockProfileService) DeleteUserProfile(user_id string) error { + args := m.Called(user_id) + return args.Error(0) +} + +type ProfileControllerTestSuite struct { + suite.Suite + controller controller.ProfileController + mockService *MockProfileService +} + +func (suite *ProfileControllerTestSuite) SetupTest() { + suite.mockService = new(MockProfileService) + suite.controller = controller.NewProfileController(suite.mockService) +} + +func (suite *ProfileControllerTestSuite) TestCreateUserProfile_Success() { + w := httptest.NewRecorder() + c, _ := gin.CreateTestContext(w) + c.Set("userId", "user123") + + jsonBody := `{"profilePicture": "pic.com", "bio": "new bio", "phoneNumber": "0912131415", "email": "example@example.com", "address": "Some Address"}` + c.Request = httptest.NewRequest("POST", "/profile", strings.NewReader(jsonBody)) + c.Request.Header.Set("Content-Type", "application/json") + + profileDto := &dto.CreateProfileDto{ + UserID: "user123", + ProfilePicture: "pic.com", + Bio: "new bio", + PhoneNumber: "0912131415", + Email: "example@example.com", + Address: "Some Address", + } + + objId := primitive.NewObjectID() + expectedProfile := &entities.Profile{ + UserID: objId, + ProfilePicture: "pic.com", + Bio: "new bio", + ContactInfo: entities.ContactInfo{PhoneNumber: "0912131415", Email: "example@example.com", Address: "Some Address"}, + UpdatedAt: time.Now(), + } + suite.mockService.On("CreateUserProfile", profileDto).Return(expectedProfile, nil) + + suite.controller.CreateUserProfile(c) + + assert.Equal(suite.T(), http.StatusOK, w.Code) + suite.mockService.AssertExpectations(suite.T()) +} + +func (suite *ProfileControllerTestSuite) TestCreateUserProfile_Fail() { + w := httptest.NewRecorder() + c, _ := gin.CreateTestContext(w) + c.Set("userId", "user123") + + jsonBody := `{"profilePicture": "pic.com", "bio": "new bio", "phoneNumber": "0912131415", "email": "example@example.com", "address": "Some Address"}` + c.Request = httptest.NewRequest("POST", "/profile", strings.NewReader(jsonBody)) + c.Request.Header.Set("Content-Type", "application/json") + + profileDto := &dto.CreateProfileDto{ + UserID: "user123", + ProfilePicture: "pic.com", + Bio: "new bio", + PhoneNumber: "0912131415", + Email: "example@example.com", + Address: "Some Address", + } + + suite.mockService.On("CreateUserProfile", profileDto).Return(nil, errors.New("some error")) + + suite.controller.CreateUserProfile(c) + + assert.Equal(suite.T(), http.StatusBadRequest, w.Code) + suite.mockService.AssertExpectations(suite.T()) +} + +func (suite *ProfileControllerTestSuite) TestGetUserProfile_Success() { + w := httptest.NewRecorder() + c, _ := gin.CreateTestContext(w) + c.Params = gin.Params{{Key: "userId", Value: "user123"}} + objId := primitive.NewObjectID() + expectedProfile := &entities.Profile{ + UserID: objId, + ProfilePicture: "pic.com", + Bio: "new bio", + ContactInfo: entities.ContactInfo{PhoneNumber: "0912131415", Email: "example@example.com", Address: "Some Address"}, + UpdatedAt: time.Now(), + } + suite.mockService.On("GetUserProfile", "user123").Return(expectedProfile, nil) + + suite.controller.GetUserProfile(c) + + assert.Equal(suite.T(), http.StatusOK, w.Code) + suite.mockService.AssertExpectations(suite.T()) +} + +func (suite *ProfileControllerTestSuite) TestGetUserProfile_Fail() { + w := httptest.NewRecorder() + c, _ := gin.CreateTestContext(w) + c.Params = gin.Params{{Key: "userId", Value: "user123"}} + + suite.mockService.On("GetUserProfile", "user123").Return(nil, errors.New("some error")) + + suite.controller.GetUserProfile(c) + + assert.Equal(suite.T(), http.StatusBadRequest, w.Code) + suite.mockService.AssertExpectations(suite.T()) +} + +func (suite *ProfileControllerTestSuite) TestUpdateUserProfile_Success() { + w := httptest.NewRecorder() + c, _ := gin.CreateTestContext(w) + c.Params = gin.Params{{Key: "userId", Value: "user123"}} + c.Set("userId", "user123") + + jsonBody := `{"profilePicture": "pic.com", "bio": "new bio", "phoneNumber": "0912131415", "address": "Some Address"}` + c.Request = httptest.NewRequest("PUT", "/profile/user123", strings.NewReader(jsonBody)) + c.Request.Header.Set("Content-Type", "application/json") + + profileDto := &dto.UpdateProfileDto{ + UserID: "user123", + ProfilePicture: "pic.com", + Bio: "new bio", + Address: "Some Address", + } + + objId := primitive.NewObjectID() + updatedProfile := &entities.Profile{ + UserID: objId, + ProfilePicture: "pic.com", + Bio: "new bio", + ContactInfo: entities.ContactInfo{PhoneNumber: "0912131415", Address: "Some Address"}, + UpdatedAt: time.Now(), + } + + suite.mockService.On("UpdateUserProfile", profileDto).Return(updatedProfile, nil) + + suite.controller.UpdateUserProfile(c) + + assert.Equal(suite.T(), http.StatusOK, w.Code) + suite.mockService.AssertExpectations(suite.T()) +} + +func (suite *ProfileControllerTestSuite) TestUpdateUserProfile_Fail() { + w := httptest.NewRecorder() + c, _ := gin.CreateTestContext(w) + c.Params = gin.Params{{Key: "userId", Value: "user123"}} + c.Set("userId", "user123") + + jsonBody := `{"profilePicture": "pic.com", "bio": "new bio", "phoneNumber": "0912131415", "address": "Some Address"}` + c.Request = httptest.NewRequest("PUT", "/profile/user123", strings.NewReader(jsonBody)) + c.Request.Header.Set("Content-Type", "application/json") + + profileDto := &dto.UpdateProfileDto{ + UserID: "user123", + ProfilePicture: "pic.com", + Bio: "new bio", + Address: "Some Address", + } + + suite.mockService.On("UpdateUserProfile", profileDto).Return(nil, errors.New("some error")) + + suite.controller.UpdateUserProfile(c) + + assert.Equal(suite.T(), http.StatusBadRequest, w.Code) + suite.mockService.AssertExpectations(suite.T()) +} + +func (suite *ProfileControllerTestSuite) TestDeleteUserProfile_Success() { + w := httptest.NewRecorder() + c, _ := gin.CreateTestContext(w) + c.Params = gin.Params{{Key: "userId", Value: "user123"}} + c.Set("userId", "user123") + + suite.mockService.On("DeleteUserProfile", "user123").Return(nil) + + suite.controller.DeleteUserProfile(c) + + assert.Equal(suite.T(), http.StatusOK, w.Code) + suite.mockService.AssertExpectations(suite.T()) +} + +func (suite *ProfileControllerTestSuite) TestDeleteUserProfile_Fail() { + w := httptest.NewRecorder() + c, _ := gin.CreateTestContext(w) + c.Params = gin.Params{{Key: "userId", Value: "user123"}} + c.Set("userId", "user123") + + suite.mockService.On("DeleteUserProfile", "user123").Return(errors.New("some error")) + + suite.controller.DeleteUserProfile(c) + + assert.Equal(suite.T(), http.StatusBadRequest, w.Code) + suite.mockService.AssertExpectations(suite.T()) +} + +func TestProfileControllerTestSuite(t *testing.T) { + suite.Run(t, new(ProfileControllerTestSuite)) + } \ No newline at end of file diff --git a/backend/AAiT-backend-group-11/delivery/controller/tests/user_controller_test.go b/backend/AAiT-backend-group-11/delivery/controller/tests/user_controller_test.go new file mode 100644 index 000000000..887f5cdd8 --- /dev/null +++ b/backend/AAiT-backend-group-11/delivery/controller/tests/user_controller_test.go @@ -0,0 +1,132 @@ +package controller_test + +import ( + "backend-starter-project/delivery/controller" + "backend-starter-project/domain/entities" + "errors" + "net/http" + "net/http/httptest" + "testing" + + "github.com/gin-gonic/gin" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/suite" +) + +type UserControllerTestSuite struct { + suite.Suite + userController *controller.UserController + mockUserService *MockUserService +} + + +type MockUserService struct { + mock.Mock +} + +func (m *MockUserService) CreateUser(user *entities.User) (*entities.User, error) { + args := m.Called(user) + return args.Get(0).(*entities.User), args.Error(1) +} + +func (m *MockUserService) FindUserByEmail(email string) (*entities.User, error) { + args := m.Called(email) + return args.Get(0).(*entities.User), args.Error(1) +} + +func (m *MockUserService) FindUserById(userId string) (*entities.User, error) { + args := m.Called(userId) + return args.Get(0).(*entities.User), args.Error(1) +} + +func (m *MockUserService) UpdateUser(user *entities.User) (*entities.User, error) { + args := m.Called(user) + return args.Get(0).(*entities.User), args.Error(1) +} + +func (m *MockUserService) DeleteUser(userId string) error { + args := m.Called(userId) + return args.Error(0) +} + +func (m *MockUserService) PromoteUserToAdmin(userId string) error { + args := m.Called(userId) + return args.Error(0) +} + +func (m *MockUserService) DemoteUserToRegular(userId string) error { + args := m.Called(userId) + return args.Error(0) +} + +func (m *MockUserService) MarkUserAsVerified(email string) error { + args := m.Called(email) + return args.Error(0) +} + +func (suite *UserControllerTestSuite) SetupTest() { + suite.mockUserService = new(MockUserService) + suite.userController = controller.NewUserController(suite.mockUserService) +} + +func (suite *UserControllerTestSuite) TestPromoteUser_Success() { + suite.mockUserService.On("PromoteUserToAdmin", "123").Return(nil) + + w := httptest.NewRecorder() + c, _ := gin.CreateTestContext(w) + c.Params = gin.Params{{Key: "id", Value: "123"}} + + suite.userController.PromoteUser(c) + + suite.Equal(http.StatusOK, w.Code) + suite.Contains(w.Body.String(), "User promoted successfully") + suite.mockUserService.AssertCalled(suite.T(), "PromoteUserToAdmin", "123") +} + +func (suite *UserControllerTestSuite) TestPromoteUser_Failure() { + // Simulate an error in the service layer + suite.mockUserService.On("PromoteUserToAdmin", "123").Return(errors.New("mocked error")) + + w := httptest.NewRecorder() + c, _ := gin.CreateTestContext(w) + c.Params = gin.Params{{Key: "id", Value: "123"}} + + suite.userController.PromoteUser(c) + + suite.Equal(http.StatusInternalServerError, w.Code) + suite.Contains(w.Body.String(), "mocked error") + suite.mockUserService.AssertCalled(suite.T(), "PromoteUserToAdmin", "123") +} + +func (suite *UserControllerTestSuite) TestDemoteUser_Success() { + suite.mockUserService.On("DemoteUserToRegular", "123").Return(nil) + + w := httptest.NewRecorder() + c, _ := gin.CreateTestContext(w) + c.Params = gin.Params{{Key: "id", Value: "123"}} + + suite.userController.DemoteUser(c) + + suite.Equal(http.StatusOK, w.Code) + suite.Contains(w.Body.String(), "User demoted successfully") + suite.mockUserService.AssertCalled(suite.T(), "DemoteUserToRegular", "123") +} + +func (suite *UserControllerTestSuite) TestDemoteUser_Failure() { + // Simulate an error in the service layer + suite.mockUserService.On("DemoteUserToRegular", "123").Return(errors.New("mocked error")) + + w := httptest.NewRecorder() + c, _ := gin.CreateTestContext(w) + c.Params = gin.Params{{Key: "id", Value: "123"}} + + suite.userController.DemoteUser(c) + + suite.Equal(http.StatusInternalServerError, w.Code) + suite.Contains(w.Body.String(), "mocked error") + suite.mockUserService.AssertCalled(suite.T(), "DemoteUserToRegular", "123") +} + +func TestUserControllerTestSuite(t *testing.T) { + suite.Run(t, new(UserControllerTestSuite)) +} diff --git a/backend/AAiT-backend-group-11/delivery/controller/user_controller.go b/backend/AAiT-backend-group-11/delivery/controller/user_controller.go index 7bec90276..5befef3d1 100644 --- a/backend/AAiT-backend-group-11/delivery/controller/user_controller.go +++ b/backend/AAiT-backend-group-11/delivery/controller/user_controller.go @@ -1,6 +1,7 @@ package controller import ( + "backend-starter-project/domain/dto" "backend-starter-project/domain/interfaces" "github.com/gin-gonic/gin" @@ -20,21 +21,34 @@ func NewUserController(userService interfaces.UserService) *UserController { func (uc *UserController) PromoteUser(c *gin.Context) { userId := c.Param("id") err := uc.userService.PromoteUserToAdmin(userId) + var response dto.Response if err != nil { - c.JSON(500, gin.H{"error": err.Error()}) + response.Success = false + response.Error = err.Error() + c.JSON(500,response) return } - c.JSON(200, gin.H{"message": "User promoted successfully"}) + response.Success = true + response.Message = "User promoted successfully" + + c.JSON(200, response) } func (uc *UserController) DemoteUser(c *gin.Context) { userId := c.Param("id") err := uc.userService.DemoteUserToRegular(userId) + if err != nil { - c.JSON(500, gin.H{"error": err.Error()}) + c.JSON(500, dto.Response{ + Success: false, + Error: err.Error(), + }) return } - c.JSON(200, gin.H{"message": "User demoted successfully"}) + c.JSON(200, dto.Response{ + Success: true, + Message: "User demoted successfully", + }) } \ No newline at end of file diff --git a/backend/AAiT-backend-group-11/domain/entities/email.go b/backend/AAiT-backend-group-11/domain/entities/email.go index 784de0f14..289050edd 100644 --- a/backend/AAiT-backend-group-11/domain/entities/email.go +++ b/backend/AAiT-backend-group-11/domain/entities/email.go @@ -12,6 +12,6 @@ type SMTPConfig struct { } type EmailVerificationRequest struct { - Email string `json:"email" binding:"required, email"` - Code string `json:"code" binding:"required, min=5"` + Email string `json:"email" binding:"required,email"` + Code string `json:"code" binding:"required,min=5"` }