package services import ( "testing" "time" "attune-heart-therapy/internal/config" "attune-heart-therapy/internal/models" ) // MockBookingRepository for testing type MockBookingRepository struct { bookings map[uint]*models.Booking nextID uint } func NewMockBookingRepository() *MockBookingRepository { return &MockBookingRepository{ bookings: make(map[uint]*models.Booking), nextID: 1, } } func (m *MockBookingRepository) Create(booking *models.Booking) error { booking.ID = m.nextID m.nextID++ m.bookings[booking.ID] = booking return nil } func (m *MockBookingRepository) GetByID(id uint) (*models.Booking, error) { if booking, exists := m.bookings[id]; exists { return booking, nil } return nil, nil } func (m *MockBookingRepository) GetByUserID(userID uint) ([]models.Booking, error) { var result []models.Booking for _, booking := range m.bookings { if booking.UserID == userID { result = append(result, *booking) } } return result, nil } func (m *MockBookingRepository) Update(booking *models.Booking) error { m.bookings[booking.ID] = booking return nil } func (m *MockBookingRepository) Delete(id uint) error { delete(m.bookings, id) return nil } func (m *MockBookingRepository) GetUpcomingBookings() ([]models.Booking, error) { var result []models.Booking now := time.Now() for _, booking := range m.bookings { if booking.Status == models.BookingStatusScheduled && booking.ScheduledAt.After(now) { result = append(result, *booking) } } return result, nil } // MockScheduleRepository for testing type MockScheduleRepository struct { schedules map[uint]*models.Schedule } func NewMockScheduleRepository() *MockScheduleRepository { return &MockScheduleRepository{ schedules: make(map[uint]*models.Schedule), } } func (m *MockScheduleRepository) Create(schedule *models.Schedule) error { m.schedules[schedule.ID] = schedule return nil } func (m *MockScheduleRepository) GetAvailable(date time.Time) ([]models.Schedule, error) { var result []models.Schedule for _, schedule := range m.schedules { if schedule.IsAvailable && schedule.BookedCount < schedule.MaxBookings { result = append(result, *schedule) } } return result, nil } func (m *MockScheduleRepository) Update(schedule *models.Schedule) error { m.schedules[schedule.ID] = schedule return nil } func (m *MockScheduleRepository) GetByID(id uint) (*models.Schedule, error) { if schedule, exists := m.schedules[id]; exists { return schedule, nil } return nil, nil } func (m *MockScheduleRepository) IncrementBookedCount(scheduleID uint) error { if schedule, exists := m.schedules[scheduleID]; exists { schedule.BookedCount++ } return nil } func (m *MockScheduleRepository) DecrementBookedCount(scheduleID uint) error { if schedule, exists := m.schedules[scheduleID]; exists { schedule.BookedCount-- } return nil } func (m *MockBookingRepository) GetByPaymentID(paymentID string) (*models.Booking, error) { for _, booking := range m.bookings { if booking.PaymentID == paymentID { return booking, nil } } return nil, nil } // MockUserRepository for testing type MockUserRepository struct { users map[uint]*models.User } func NewMockUserRepository() *MockUserRepository { return &MockUserRepository{ users: make(map[uint]*models.User), } } func (m *MockUserRepository) Create(user *models.User) error { m.users[user.ID] = user return nil } func (m *MockUserRepository) GetByID(id uint) (*models.User, error) { if user, exists := m.users[id]; exists { return user, nil } user := &models.User{ Email: "test@example.com", FirstName: "Test", } user.ID = id return user, nil } func (m *MockUserRepository) GetByEmail(email string) (*models.User, error) { for _, user := range m.users { if user.Email == email { return user, nil } } return nil, nil } func (m *MockUserRepository) Update(user *models.User) error { m.users[user.ID] = user return nil } func (m *MockUserRepository) GetActiveUsersCount() (int64, error) { return int64(len(m.users)), nil } // MockNotificationService for testing type MockNotificationService struct{} func (m *MockNotificationService) SendWelcomeEmail(user *models.User) error { return nil } func (m *MockNotificationService) SendPaymentNotification(user *models.User, booking *models.Booking, success bool) error { return nil } func (m *MockNotificationService) SendMeetingInfo(user *models.User, booking *models.Booking) error { return nil } func (m *MockNotificationService) SendReminder(user *models.User, booking *models.Booking) error { return nil } func (m *MockNotificationService) ScheduleReminder(bookingID uint, reminderTime time.Time) error { return nil } func (m *MockNotificationService) ProcessPendingNotifications() error { return nil } func TestBookingService_CreateBookingWithJitsiIntegration(t *testing.T) { // Setup mock repositories bookingRepo := NewMockBookingRepository() scheduleRepo := NewMockScheduleRepository() userRepo := NewMockUserRepository() // Setup Jitsi service jitsiConfig := &config.JitsiConfig{ BaseURL: "https://meet.jit.si", } jitsiService := NewJitsiService(jitsiConfig) // Setup mock services var paymentService PaymentService notificationService := &MockNotificationService{} // Create booking service bookingService := NewBookingService(bookingRepo, scheduleRepo, userRepo, jitsiService, paymentService, notificationService) // Create a test schedule schedule := &models.Schedule{ StartTime: time.Now().Add(24 * time.Hour), EndTime: time.Now().Add(25 * time.Hour), IsAvailable: true, MaxBookings: 1, BookedCount: 0, } schedule.ID = 1 scheduleRepo.schedules[1] = schedule // Create booking request req := BookingRequest{ ScheduleID: 1, Amount: 100.0, Notes: "Test booking with Jitsi integration", } // Create booking booking, err := bookingService.CreateBooking(123, req) if err != nil { t.Fatalf("Expected no error, got %v", err) } if booking == nil { t.Fatal("Expected booking to be created, got nil") } // Verify booking details if booking.UserID != 123 { t.Errorf("Expected user ID 123, got %d", booking.UserID) } if booking.Amount != 100.0 { t.Errorf("Expected amount 100.0, got %f", booking.Amount) } // Verify Jitsi integration if booking.JitsiRoomID == "" { t.Error("Expected Jitsi room ID to be set") } if booking.JitsiRoomURL == "" { t.Error("Expected Jitsi room URL to be set") } // Verify URL format expectedPrefix := "https://meet.jit.si/" if len(booking.JitsiRoomURL) <= len(expectedPrefix) { t.Error("Expected room URL to contain room ID") } if booking.JitsiRoomURL[:len(expectedPrefix)] != expectedPrefix { t.Errorf("Expected room URL to start with %s, got %s", expectedPrefix, booking.JitsiRoomURL) } // Verify schedule booking count was incremented updatedSchedule, _ := scheduleRepo.GetByID(1) if updatedSchedule.BookedCount != 1 { t.Errorf("Expected booked count to be 1, got %d", updatedSchedule.BookedCount) } }