"""Tests for booking validation service.""" from datetime import datetime import pytest from sqlalchemy.orm import Session from app.models.booking import Booking from app.models.settings import Settings from app.models.space import Space from app.models.user import User from app.services.booking_service import validate_booking_rules @pytest.fixture def test_settings(db: Session) -> Settings: """Create test settings.""" settings = Settings( id=1, min_duration_minutes=30, max_duration_minutes=480, # 8 hours working_hours_start=8, working_hours_end=20, max_bookings_per_day_per_user=3, min_hours_before_cancel=2, ) db.add(settings) db.commit() db.refresh(settings) return settings def test_validate_duration_too_short( db: Session, test_user: User, test_space: Space, test_settings: Settings ): """Test validation fails for booking duration too short.""" start = datetime(2024, 3, 15, 10, 0, 0) end = datetime(2024, 3, 15, 10, 15, 0) # Only 15 minutes (min is 30) errors = validate_booking_rules( db=db, space_id=test_space.id, user_id=test_user.id, start_datetime=start, end_datetime=end, ) assert len(errors) == 1 assert "Durata rezervării trebuie să fie între 30 și 480 minute" in errors[0] def test_validate_duration_too_long( db: Session, test_user: User, test_space: Space, test_settings: Settings ): """Test validation fails for booking duration too long.""" start = datetime(2024, 3, 15, 8, 0, 0) end = datetime(2024, 3, 15, 20, 0, 0) # 12 hours = 720 minutes (max is 480) errors = validate_booking_rules( db=db, space_id=test_space.id, user_id=test_user.id, start_datetime=start, end_datetime=end, ) assert len(errors) == 1 assert "Durata rezervării trebuie să fie între 30 și 480 minute" in errors[0] def test_validate_outside_working_hours_start( db: Session, test_user: User, test_space: Space, test_settings: Settings ): """Test validation fails for booking starting before working hours.""" start = datetime(2024, 3, 15, 7, 0, 0) # Before 8 AM end = datetime(2024, 3, 15, 9, 0, 0) errors = validate_booking_rules( db=db, space_id=test_space.id, user_id=test_user.id, start_datetime=start, end_datetime=end, ) assert len(errors) == 1 assert "Rezervările sunt permise doar între 8:00 și 20:00" in errors[0] def test_validate_outside_working_hours_end( db: Session, test_user: User, test_space: Space, test_settings: Settings ): """Test validation fails for booking ending after working hours.""" start = datetime(2024, 3, 15, 19, 0, 0) end = datetime(2024, 3, 15, 21, 0, 0) # After 8 PM errors = validate_booking_rules( db=db, space_id=test_space.id, user_id=test_user.id, start_datetime=start, end_datetime=end, ) assert len(errors) == 1 assert "Rezervările sunt permise doar între 8:00 și 20:00" in errors[0] def test_validate_overlap_detected_pending( db: Session, test_user: User, test_space: Space, test_settings: Settings ): """Test validation fails when space is already booked (pending status).""" # Create existing booking existing = Booking( user_id=test_user.id, space_id=test_space.id, title="Existing Meeting", start_datetime=datetime(2024, 3, 15, 10, 0, 0), end_datetime=datetime(2024, 3, 15, 12, 0, 0), status="pending", ) db.add(existing) db.commit() # Try to create overlapping booking start = datetime(2024, 3, 15, 11, 0, 0) end = datetime(2024, 3, 15, 13, 0, 0) errors = validate_booking_rules( db=db, space_id=test_space.id, user_id=test_user.id, start_datetime=start, end_datetime=end, ) assert len(errors) == 1 assert "Spațiul este deja rezervat în acest interval" in errors[0] def test_validate_overlap_detected_approved( db: Session, test_user: User, test_space: Space, test_settings: Settings ): """Test validation fails when space is already booked (approved status).""" # Create existing booking existing = Booking( user_id=test_user.id, space_id=test_space.id, title="Existing Meeting", start_datetime=datetime(2024, 3, 15, 10, 0, 0), end_datetime=datetime(2024, 3, 15, 12, 0, 0), status="approved", ) db.add(existing) db.commit() # Try to create overlapping booking start = datetime(2024, 3, 15, 11, 0, 0) end = datetime(2024, 3, 15, 13, 0, 0) errors = validate_booking_rules( db=db, space_id=test_space.id, user_id=test_user.id, start_datetime=start, end_datetime=end, ) assert len(errors) == 1 assert "Spațiul este deja rezervat în acest interval" in errors[0] def test_validate_no_overlap_rejected( db: Session, test_user: User, test_space: Space, test_settings: Settings ): """Test validation passes when existing booking is rejected.""" # Create rejected booking existing = Booking( user_id=test_user.id, space_id=test_space.id, title="Rejected Meeting", start_datetime=datetime(2024, 3, 15, 10, 0, 0), end_datetime=datetime(2024, 3, 15, 12, 0, 0), status="rejected", ) db.add(existing) db.commit() # Try to create booking in same time slot start = datetime(2024, 3, 15, 11, 0, 0) end = datetime(2024, 3, 15, 12, 0, 0) errors = validate_booking_rules( db=db, space_id=test_space.id, user_id=test_user.id, start_datetime=start, end_datetime=end, ) # Should have no overlap error (rejected bookings don't count) assert "Spațiul este deja rezervat în acest interval" not in errors def test_validate_max_bookings_exceeded( db: Session, test_user: User, test_space: Space, test_settings: Settings ): """Test validation fails when user exceeds max bookings per day.""" # Create 3 bookings for the same day (max is 3) base_date = datetime(2024, 3, 15) for i in range(3): booking = Booking( user_id=test_user.id, space_id=test_space.id, title=f"Meeting {i+1}", start_datetime=base_date.replace(hour=9 + i * 2), end_datetime=base_date.replace(hour=10 + i * 2), status="approved", ) db.add(booking) db.commit() # Try to create 4th booking on same day start = datetime(2024, 3, 15, 16, 0, 0) end = datetime(2024, 3, 15, 17, 0, 0) errors = validate_booking_rules( db=db, space_id=test_space.id, user_id=test_user.id, start_datetime=start, end_datetime=end, ) assert len(errors) == 1 assert "Ai atins limita de 3 rezervări pe zi" in errors[0] def test_validate_max_bookings_different_day_ok( db: Session, test_user: User, test_space: Space, test_settings: Settings ): """Test validation passes when max bookings reached on different day.""" # Create 3 bookings for previous day previous_date = datetime(2024, 3, 14) for i in range(3): booking = Booking( user_id=test_user.id, space_id=test_space.id, title=f"Meeting {i+1}", start_datetime=previous_date.replace(hour=9 + i * 2), end_datetime=previous_date.replace(hour=10 + i * 2), status="approved", ) db.add(booking) db.commit() # Try to create booking on different day start = datetime(2024, 3, 15, 10, 0, 0) end = datetime(2024, 3, 15, 11, 0, 0) errors = validate_booking_rules( db=db, space_id=test_space.id, user_id=test_user.id, start_datetime=start, end_datetime=end, ) # Should have no max bookings error (different day) assert "Ai atins limita de 3 rezervări pe zi" not in errors def test_validate_all_rules_pass( db: Session, test_user: User, test_space: Space, test_settings: Settings ): """Test validation passes when all rules are satisfied (happy path).""" start = datetime(2024, 3, 15, 10, 0, 0) end = datetime(2024, 3, 15, 11, 0, 0) # 1 hour duration errors = validate_booking_rules( db=db, space_id=test_space.id, user_id=test_user.id, start_datetime=start, end_datetime=end, ) assert len(errors) == 0 def test_validate_multiple_errors( db: Session, test_user: User, test_space: Space, test_settings: Settings ): """Test validation returns multiple errors when multiple rules fail.""" # Duration too short AND outside working hours start = datetime(2024, 3, 15, 6, 0, 0) # Before 8 AM end = datetime(2024, 3, 15, 6, 10, 0) # Only 10 minutes errors = validate_booking_rules( db=db, space_id=test_space.id, user_id=test_user.id, start_datetime=start, end_datetime=end, ) assert len(errors) == 2 assert any("Durata rezervării" in error for error in errors) assert any("Rezervările sunt permise doar" in error for error in errors) def test_validate_creates_default_settings(db: Session, test_user: User, test_space: Space): """Test validation creates default settings if they don't exist.""" # Ensure no settings exist db.query(Settings).delete() db.commit() start = datetime(2024, 3, 15, 10, 0, 0) end = datetime(2024, 3, 15, 11, 0, 0) errors = validate_booking_rules( db=db, space_id=test_space.id, user_id=test_user.id, start_datetime=start, end_datetime=end, ) # Verify settings were created settings = db.query(Settings).filter(Settings.id == 1).first() assert settings is not None assert settings.min_duration_minutes == 30 assert settings.max_duration_minutes == 480 assert settings.working_hours_start == 8 assert settings.working_hours_end == 20 assert settings.max_bookings_per_day_per_user == 3 # Should pass validation with default settings assert len(errors) == 0