package services import ( "testing" "time" "github.com/google/uuid" ) // TestNotificationService_CreateNotification tests notification creation func TestNotificationService_CreateNotification(t *testing.T) { tests := []struct { name string userID uuid.UUID notifType NotificationType title string body string data map[string]interface{} expectError bool }{ { name: "valid notification", userID: uuid.New(), notifType: NotificationTypeConsentRequired, title: "Consent Required", body: "Please review and accept the new terms", data: map[string]interface{}{"document_id": "123"}, expectError: false, }, { name: "notification without data", userID: uuid.New(), notifType: NotificationTypeGeneral, title: "General Notification", body: "This is a test", data: nil, expectError: false, }, { name: "empty user ID", userID: uuid.Nil, notifType: NotificationTypeGeneral, title: "Test", body: "Test", expectError: true, }, { name: "empty title", userID: uuid.New(), notifType: NotificationTypeGeneral, title: "", body: "Test body", expectError: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { var err error if tt.userID == uuid.Nil { err = &ValidationError{Field: "user ID", Message: "required"} } else if tt.title == "" { err = &ValidationError{Field: "title", Message: "required"} } if tt.expectError && err == nil { t.Error("Expected error, got nil") } if !tt.expectError && err != nil { t.Errorf("Expected no error, got %v", err) } }) } } // TestNotificationService_NotificationTypes tests notification type validation func TestNotificationService_NotificationTypes(t *testing.T) { tests := []struct { notifType NotificationType isValid bool }{ {NotificationTypeConsentRequired, true}, {NotificationTypeConsentReminder, true}, {NotificationTypeVersionPublished, true}, {NotificationTypeVersionApproved, true}, {NotificationTypeVersionRejected, true}, {NotificationTypeAccountSuspended, true}, {NotificationTypeAccountRestored, true}, {NotificationTypeGeneral, true}, {NotificationType("invalid_type"), false}, {NotificationType(""), false}, } validTypes := map[NotificationType]bool{ NotificationTypeConsentRequired: true, NotificationTypeConsentReminder: true, NotificationTypeVersionPublished: true, NotificationTypeVersionApproved: true, NotificationTypeVersionRejected: true, NotificationTypeAccountSuspended: true, NotificationTypeAccountRestored: true, NotificationTypeGeneral: true, } for _, tt := range tests { t.Run(string(tt.notifType), func(t *testing.T) { isValid := validTypes[tt.notifType] if isValid != tt.isValid { t.Errorf("Type %s: expected valid=%v, got %v", tt.notifType, tt.isValid, isValid) } }) } } // TestNotificationService_NotificationChannels tests channel validation func TestNotificationService_NotificationChannels(t *testing.T) { tests := []struct { channel NotificationChannel isValid bool }{ {ChannelInApp, true}, {ChannelEmail, true}, {ChannelPush, true}, {NotificationChannel("sms"), false}, {NotificationChannel(""), false}, } validChannels := map[NotificationChannel]bool{ ChannelInApp: true, ChannelEmail: true, ChannelPush: true, } for _, tt := range tests { t.Run(string(tt.channel), func(t *testing.T) { isValid := validChannels[tt.channel] if isValid != tt.isValid { t.Errorf("Channel %s: expected valid=%v, got %v", tt.channel, tt.isValid, isValid) } }) } } // TestNotificationService_GetUserNotifications tests retrieving notifications func TestNotificationService_GetUserNotifications(t *testing.T) { tests := []struct { name string userID uuid.UUID limit int offset int unreadOnly bool expectError bool }{ { name: "get all notifications", userID: uuid.New(), limit: 50, offset: 0, unreadOnly: false, expectError: false, }, { name: "get unread only", userID: uuid.New(), limit: 50, offset: 0, unreadOnly: true, expectError: false, }, { name: "with pagination", userID: uuid.New(), limit: 10, offset: 20, unreadOnly: false, expectError: false, }, { name: "invalid user ID", userID: uuid.Nil, limit: 50, offset: 0, unreadOnly: false, expectError: true, }, { name: "negative limit", userID: uuid.New(), limit: -1, offset: 0, unreadOnly: false, expectError: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { var err error if tt.userID == uuid.Nil { err = &ValidationError{Field: "user ID", Message: "required"} } else if tt.limit < 0 { err = &ValidationError{Field: "limit", Message: "must be >= 0"} } if tt.expectError && err == nil { t.Error("Expected error, got nil") } if !tt.expectError && err != nil { t.Errorf("Expected no error, got %v", err) } }) } } // TestNotificationService_MarkAsRead tests marking notifications as read func TestNotificationService_MarkAsRead(t *testing.T) { tests := []struct { name string notificationID uuid.UUID userID uuid.UUID expectError bool }{ { name: "mark valid notification as read", notificationID: uuid.New(), userID: uuid.New(), expectError: false, }, { name: "invalid notification ID", notificationID: uuid.Nil, userID: uuid.New(), expectError: true, }, { name: "invalid user ID", notificationID: uuid.New(), userID: uuid.Nil, expectError: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { var err error if tt.notificationID == uuid.Nil { err = &ValidationError{Field: "notification ID", Message: "required"} } else if tt.userID == uuid.Nil { err = &ValidationError{Field: "user ID", Message: "required"} } if tt.expectError && err == nil { t.Error("Expected error, got nil") } if !tt.expectError && err != nil { t.Errorf("Expected no error, got %v", err) } }) } } // TestNotificationService_GetPreferences tests retrieving user preferences func TestNotificationService_GetPreferences(t *testing.T) { tests := []struct { name string userID uuid.UUID expectError bool }{ { name: "get valid user preferences", userID: uuid.New(), expectError: false, }, { name: "invalid user ID", userID: uuid.Nil, expectError: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { var err error if tt.userID == uuid.Nil { err = &ValidationError{Field: "user ID", Message: "required"} } if tt.expectError && err == nil { t.Error("Expected error, got nil") } if !tt.expectError && err != nil { t.Errorf("Expected no error, got %v", err) } }) } } // TestNotificationService_UpdatePreferences tests updating notification preferences func TestNotificationService_UpdatePreferences(t *testing.T) { tests := []struct { name string userID uuid.UUID emailEnabled bool pushEnabled bool inAppEnabled bool reminderFrequency string expectError bool }{ { name: "enable all notifications", userID: uuid.New(), emailEnabled: true, pushEnabled: true, inAppEnabled: true, reminderFrequency: "daily", expectError: false, }, { name: "disable email notifications", userID: uuid.New(), emailEnabled: false, pushEnabled: true, inAppEnabled: true, reminderFrequency: "weekly", expectError: false, }, { name: "set reminder frequency to never", userID: uuid.New(), emailEnabled: true, pushEnabled: false, inAppEnabled: true, reminderFrequency: "never", expectError: false, }, { name: "invalid reminder frequency", userID: uuid.New(), emailEnabled: true, pushEnabled: true, inAppEnabled: true, reminderFrequency: "hourly", expectError: true, }, } validFrequencies := map[string]bool{ "daily": true, "weekly": true, "never": true, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { var err error if !validFrequencies[tt.reminderFrequency] { err = &ValidationError{Field: "reminder_frequency", Message: "must be daily, weekly, or never"} } if tt.expectError && err == nil { t.Error("Expected error, got nil") } if !tt.expectError && err != nil { t.Errorf("Expected no error, got %v", err) } }) } } // TestNotificationService_NotifyConsentRequired tests consent required notification func TestNotificationService_NotifyConsentRequired(t *testing.T) { tests := []struct { name string documentName string versionID string expectError bool }{ { name: "valid consent notification", documentName: "Terms of Service", versionID: uuid.New().String(), expectError: false, }, { name: "empty document name", documentName: "", versionID: uuid.New().String(), expectError: true, }, { name: "empty version ID", documentName: "Privacy Policy", versionID: "", expectError: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { var err error if tt.documentName == "" { err = &ValidationError{Field: "document name", Message: "required"} } else if tt.versionID == "" { err = &ValidationError{Field: "version ID", Message: "required"} } if tt.expectError && err == nil { t.Error("Expected error, got nil") } if !tt.expectError && err != nil { t.Errorf("Expected no error, got %v", err) } }) } } // TestNotificationService_DeleteNotification tests deleting notifications func TestNotificationService_DeleteNotification(t *testing.T) { tests := []struct { name string notificationID uuid.UUID userID uuid.UUID expectError bool }{ { name: "delete valid notification", notificationID: uuid.New(), userID: uuid.New(), expectError: false, }, { name: "invalid notification ID", notificationID: uuid.Nil, userID: uuid.New(), expectError: true, }, { name: "invalid user ID", notificationID: uuid.New(), userID: uuid.Nil, expectError: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { var err error if tt.notificationID == uuid.Nil { err = &ValidationError{Field: "notification ID", Message: "required"} } else if tt.userID == uuid.Nil { err = &ValidationError{Field: "user ID", Message: "required"} } if tt.expectError && err == nil { t.Error("Expected error, got nil") } if !tt.expectError && err != nil { t.Errorf("Expected no error, got %v", err) } }) } } // TestNotificationService_BatchMarkAsRead tests batch marking as read func TestNotificationService_BatchMarkAsRead(t *testing.T) { tests := []struct { name string notificationIDs []uuid.UUID userID uuid.UUID expectError bool }{ { name: "mark multiple notifications", notificationIDs: []uuid.UUID{uuid.New(), uuid.New(), uuid.New()}, userID: uuid.New(), expectError: false, }, { name: "empty list", notificationIDs: []uuid.UUID{}, userID: uuid.New(), expectError: false, }, { name: "invalid user ID", notificationIDs: []uuid.UUID{uuid.New()}, userID: uuid.Nil, expectError: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { var err error if tt.userID == uuid.Nil { err = &ValidationError{Field: "user ID", Message: "required"} } if tt.expectError && err == nil { t.Error("Expected error, got nil") } if !tt.expectError && err != nil { t.Errorf("Expected no error, got %v", err) } }) } } // TestNotificationService_GetUnreadCount tests getting unread count func TestNotificationService_GetUnreadCount(t *testing.T) { tests := []struct { name string userID uuid.UUID expectError bool }{ { name: "get count for valid user", userID: uuid.New(), expectError: false, }, { name: "invalid user ID", userID: uuid.Nil, expectError: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { var err error if tt.userID == uuid.Nil { err = &ValidationError{Field: "user ID", Message: "required"} } if tt.expectError && err == nil { t.Error("Expected error, got nil") } if !tt.expectError && err != nil { t.Errorf("Expected no error, got %v", err) } }) } } // TestNotificationService_NotificationPriority tests notification priority func TestNotificationService_NotificationPriority(t *testing.T) { tests := []struct { name string notifType NotificationType expectedPrio string }{ { name: "consent required - high priority", notifType: NotificationTypeConsentRequired, expectedPrio: "high", }, { name: "account suspended - critical", notifType: NotificationTypeAccountSuspended, expectedPrio: "critical", }, { name: "version published - normal", notifType: NotificationTypeVersionPublished, expectedPrio: "normal", }, { name: "general - low", notifType: NotificationTypeGeneral, expectedPrio: "low", }, } priorityMap := map[NotificationType]string{ NotificationTypeConsentRequired: "high", NotificationTypeConsentReminder: "high", NotificationTypeAccountSuspended: "critical", NotificationTypeAccountRestored: "normal", NotificationTypeVersionPublished: "normal", NotificationTypeVersionApproved: "normal", NotificationTypeVersionRejected: "normal", NotificationTypeGeneral: "low", } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { priority := priorityMap[tt.notifType] if priority != tt.expectedPrio { t.Errorf("Expected priority %s, got %s", tt.expectedPrio, priority) } }) } } // TestNotificationService_ReminderFrequency tests reminder frequency logic func TestNotificationService_ReminderFrequency(t *testing.T) { now := time.Now() tests := []struct { name string frequency string lastReminder time.Time shouldSend bool }{ { name: "daily - last sent yesterday", frequency: "daily", lastReminder: now.AddDate(0, 0, -1), shouldSend: true, }, { name: "daily - last sent today", frequency: "daily", lastReminder: now.Add(-1 * time.Hour), shouldSend: false, }, { name: "weekly - last sent 8 days ago", frequency: "weekly", lastReminder: now.AddDate(0, 0, -8), shouldSend: true, }, { name: "weekly - last sent 5 days ago", frequency: "weekly", lastReminder: now.AddDate(0, 0, -5), shouldSend: false, }, { name: "never - should not send", frequency: "never", lastReminder: now.AddDate(0, 0, -30), shouldSend: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { var shouldSend bool switch tt.frequency { case "daily": daysSince := int(now.Sub(tt.lastReminder).Hours() / 24) shouldSend = daysSince >= 1 case "weekly": daysSince := int(now.Sub(tt.lastReminder).Hours() / 24) shouldSend = daysSince >= 7 case "never": shouldSend = false } if shouldSend != tt.shouldSend { t.Errorf("Expected shouldSend=%v, got %v", tt.shouldSend, shouldSend) } }) } }