package handlers import ( "net/http" "net/http/httptest" "testing" "veza-backend-api/internal/common" "veza-backend-api/internal/services" "github.com/gin-gonic/gin" "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) // MockNotificationService implements NotificationService interface for testing type MockNotificationService struct { mock.Mock } func (m *MockNotificationService) GetNotifications(userID uuid.UUID, unreadOnly bool) ([]services.Notification, error) { args := m.Called(userID, unreadOnly) if args.Get(0) == nil { return nil, args.Error(1) } return args.Get(0).([]services.Notification), args.Error(1) } func (m *MockNotificationService) MarkAsRead(userID uuid.UUID, notificationID uuid.UUID) error { args := m.Called(userID, notificationID) return args.Error(0) } func (m *MockNotificationService) MarkAllAsRead(userID uuid.UUID) error { args := m.Called(userID) return args.Error(0) } func (m *MockNotificationService) GetUnreadCount(userID uuid.UUID) (int, error) { args := m.Called(userID) return args.Int(0), args.Error(1) } func (m *MockNotificationService) DeleteAllNotifications(userID uuid.UUID) error { args := m.Called(userID) return args.Error(0) } func (m *MockNotificationService) DeleteNotification(userID uuid.UUID, notificationID uuid.UUID) error { args := m.Called(userID, notificationID) return args.Error(0) } func setupTestNotificationRouter(mockService *MockNotificationService) *gin.Engine { gin.SetMode(gin.TestMode) router := gin.New() handler := NewNotificationHandlersWithInterface(mockService) api := router.Group("/api/v1") api.Use(func(c *gin.Context) { // Mock auth middleware - set user_id from header if present userIDStr := c.GetHeader("X-User-ID") if userIDStr != "" { uid, err := uuid.Parse(userIDStr) if err == nil { common.SetUserIDInContext(c, uid) } } c.Next() }) { api.GET("/notifications", handler.GetNotifications) api.POST("/notifications/:id/read", handler.MarkAsRead) api.POST("/notifications/read-all", handler.MarkAllAsRead) api.GET("/notifications/unread-count", handler.GetUnreadCount) } return router } func TestNotificationHandlers_GetNotifications_Success(t *testing.T) { // Setup mockService := new(MockNotificationService) router := setupTestNotificationRouter(mockService) userID := uuid.New() expectedNotifications := []services.Notification{ { ID: uuid.New(), UserID: userID, Type: "follow", Title: "New follower", Content: "User followed you", Read: false, }, { ID: uuid.New(), UserID: userID, Type: "like", Title: "Track liked", Content: "User liked your track", Read: true, }, } mockService.On("GetNotifications", userID, false).Return(expectedNotifications, nil) // Execute req, _ := http.NewRequest("GET", "/api/v1/notifications", nil) req.Header.Set("X-User-ID", userID.String()) w := httptest.NewRecorder() router.ServeHTTP(w, req) // Assert assert.Equal(t, http.StatusOK, w.Code) mockService.AssertExpectations(t) } func TestNotificationHandlers_GetNotifications_UnreadOnly(t *testing.T) { // Setup mockService := new(MockNotificationService) router := setupTestNotificationRouter(mockService) userID := uuid.New() expectedNotifications := []services.Notification{ { ID: uuid.New(), UserID: userID, Type: "follow", Title: "New follower", Content: "User followed you", Read: false, }, } mockService.On("GetNotifications", userID, true).Return(expectedNotifications, nil) // Execute req, _ := http.NewRequest("GET", "/api/v1/notifications?read=false", nil) req.Header.Set("X-User-ID", userID.String()) w := httptest.NewRecorder() router.ServeHTTP(w, req) // Assert assert.Equal(t, http.StatusOK, w.Code) mockService.AssertExpectations(t) } func TestNotificationHandlers_GetNotifications_Unauthorized(t *testing.T) { // Setup mockService := new(MockNotificationService) router := setupTestNotificationRouter(mockService) // Execute req, _ := http.NewRequest("GET", "/api/v1/notifications", nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) // Assert assert.True(t, w.Code == http.StatusUnauthorized || w.Code == http.StatusForbidden) mockService.AssertNotCalled(t, "GetNotifications") } func TestNotificationHandlers_GetNotifications_ServiceError(t *testing.T) { // Setup mockService := new(MockNotificationService) router := setupTestNotificationRouter(mockService) userID := uuid.New() mockService.On("GetNotifications", userID, false).Return(nil, assert.AnError) // Execute req, _ := http.NewRequest("GET", "/api/v1/notifications", nil) req.Header.Set("X-User-ID", userID.String()) w := httptest.NewRecorder() router.ServeHTTP(w, req) // Assert assert.Equal(t, http.StatusInternalServerError, w.Code) mockService.AssertExpectations(t) } func TestNotificationHandlers_MarkAsRead_Success(t *testing.T) { // Setup mockService := new(MockNotificationService) router := setupTestNotificationRouter(mockService) userID := uuid.New() notificationID := uuid.New() mockService.On("MarkAsRead", userID, notificationID).Return(nil) // Execute req, _ := http.NewRequest("POST", "/api/v1/notifications/"+notificationID.String()+"/read", nil) req.Header.Set("X-User-ID", userID.String()) w := httptest.NewRecorder() router.ServeHTTP(w, req) // Assert assert.Equal(t, http.StatusOK, w.Code) mockService.AssertExpectations(t) } func TestNotificationHandlers_MarkAsRead_Unauthorized(t *testing.T) { // Setup mockService := new(MockNotificationService) router := setupTestNotificationRouter(mockService) notificationID := uuid.New() // Execute req, _ := http.NewRequest("POST", "/api/v1/notifications/"+notificationID.String()+"/read", nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) // Assert assert.True(t, w.Code == http.StatusUnauthorized || w.Code == http.StatusForbidden) mockService.AssertNotCalled(t, "MarkAsRead") } func TestNotificationHandlers_MarkAsRead_InvalidNotificationID(t *testing.T) { // Setup mockService := new(MockNotificationService) router := setupTestNotificationRouter(mockService) userID := uuid.New() // Execute req, _ := http.NewRequest("POST", "/api/v1/notifications/invalid/read", nil) req.Header.Set("X-User-ID", userID.String()) w := httptest.NewRecorder() router.ServeHTTP(w, req) // Assert assert.Equal(t, http.StatusBadRequest, w.Code) mockService.AssertNotCalled(t, "MarkAsRead") } func TestNotificationHandlers_MarkAsRead_ServiceError(t *testing.T) { // Setup mockService := new(MockNotificationService) router := setupTestNotificationRouter(mockService) userID := uuid.New() notificationID := uuid.New() mockService.On("MarkAsRead", userID, notificationID).Return(assert.AnError) // Execute req, _ := http.NewRequest("POST", "/api/v1/notifications/"+notificationID.String()+"/read", nil) req.Header.Set("X-User-ID", userID.String()) w := httptest.NewRecorder() router.ServeHTTP(w, req) // Assert assert.Equal(t, http.StatusInternalServerError, w.Code) mockService.AssertExpectations(t) } func TestNotificationHandlers_MarkAllAsRead_Success(t *testing.T) { // Setup mockService := new(MockNotificationService) router := setupTestNotificationRouter(mockService) userID := uuid.New() mockService.On("MarkAllAsRead", userID).Return(nil) // Execute req, _ := http.NewRequest("POST", "/api/v1/notifications/read-all", nil) req.Header.Set("X-User-ID", userID.String()) w := httptest.NewRecorder() router.ServeHTTP(w, req) // Assert assert.Equal(t, http.StatusOK, w.Code) mockService.AssertExpectations(t) } func TestNotificationHandlers_MarkAllAsRead_Unauthorized(t *testing.T) { // Setup mockService := new(MockNotificationService) router := setupTestNotificationRouter(mockService) // Execute req, _ := http.NewRequest("POST", "/api/v1/notifications/read-all", nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) // Assert assert.True(t, w.Code == http.StatusUnauthorized || w.Code == http.StatusForbidden) mockService.AssertNotCalled(t, "MarkAllAsRead") } func TestNotificationHandlers_MarkAllAsRead_ServiceError(t *testing.T) { // Setup mockService := new(MockNotificationService) router := setupTestNotificationRouter(mockService) userID := uuid.New() mockService.On("MarkAllAsRead", userID).Return(assert.AnError) // Execute req, _ := http.NewRequest("POST", "/api/v1/notifications/read-all", nil) req.Header.Set("X-User-ID", userID.String()) w := httptest.NewRecorder() router.ServeHTTP(w, req) // Assert assert.Equal(t, http.StatusInternalServerError, w.Code) mockService.AssertExpectations(t) } func TestNotificationHandlers_GetUnreadCount_Success(t *testing.T) { // Setup mockService := new(MockNotificationService) router := setupTestNotificationRouter(mockService) userID := uuid.New() mockService.On("GetUnreadCount", userID).Return(5, nil) // Execute req, _ := http.NewRequest("GET", "/api/v1/notifications/unread-count", nil) req.Header.Set("X-User-ID", userID.String()) w := httptest.NewRecorder() router.ServeHTTP(w, req) // Assert assert.Equal(t, http.StatusOK, w.Code) mockService.AssertExpectations(t) } func TestNotificationHandlers_GetUnreadCount_Unauthorized(t *testing.T) { // Setup mockService := new(MockNotificationService) router := setupTestNotificationRouter(mockService) // Execute req, _ := http.NewRequest("GET", "/api/v1/notifications/unread-count", nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) // Assert assert.Equal(t, http.StatusUnauthorized, w.Code) mockService.AssertNotCalled(t, "GetUnreadCount") } func TestNotificationHandlers_GetUnreadCount_ServiceError(t *testing.T) { // Setup mockService := new(MockNotificationService) router := setupTestNotificationRouter(mockService) userID := uuid.New() mockService.On("GetUnreadCount", userID).Return(0, assert.AnError) // Execute req, _ := http.NewRequest("GET", "/api/v1/notifications/unread-count", nil) req.Header.Set("X-User-ID", userID.String()) w := httptest.NewRecorder() router.ServeHTTP(w, req) // Assert assert.Equal(t, http.StatusInternalServerError, w.Code) mockService.AssertExpectations(t) } func TestNewNotificationHandlers(t *testing.T) { // Setup mockService := &services.NotificationService{} // Execute NewNotificationHandlers(mockService) // Assert assert.NotNil(t, NotificationHandlersInstance) assert.NotNil(t, NotificationHandlersInstance.notificationService) }