def should_only_cancel_old_thing_that_can_expire_bookings_before_start_date( self, app) -> None: now = datetime.utcnow() two_months_ago = now - timedelta(days=60) guitar = ProductFactory(type=str(offer_type.ThingType.INSTRUMENT)) old_guitar_booking = BookingFactory(stock__offer__product=guitar, dateCreated=two_months_ago) disc = ProductFactory(type=str(offer_type.ThingType.MUSIQUE)) old_disc_booking = BookingFactory(stock__offer__product=disc, dateCreated=two_months_ago) audio_book = ProductFactory(type=str(offer_type.ThingType.LIVRE_AUDIO)) old_audio_book_booking = BookingFactory( stock__offer__product=audio_book, dateCreated=two_months_ago) handle_expired_bookings.cancel_expired_bookings() assert old_guitar_booking.isCancelled assert old_guitar_booking.cancellationDate.timestamp( ) == pytest.approx(datetime.utcnow().timestamp(), rel=1) assert old_guitar_booking.cancellationReason == BookingCancellationReasons.EXPIRED assert old_disc_booking.isCancelled assert old_disc_booking.cancellationDate.timestamp() == pytest.approx( datetime.utcnow().timestamp(), rel=1) assert old_disc_booking.cancellationReason == BookingCancellationReasons.EXPIRED assert not old_audio_book_booking.isCancelled assert not old_audio_book_booking.cancellationDate assert not old_audio_book_booking.cancellationReason
def should_call_email_service_for_bookings_which_will_expire_in_7_days( self, mocked_email_recap, app) -> None: # Given now = date.today() booking_date_23_days_ago = now - timedelta(days=23) booking_date_22_days_ago = now - timedelta(days=22) dvd = ProductFactory(type=str(offer_type.ThingType.AUDIOVISUEL)) expire_in_7_days_dvd_booking = BookingFactory( stock__offer__product=dvd, dateCreated=booking_date_23_days_ago, isCancelled=False, ) non_expired_cd = ProductFactory(type=str(offer_type.ThingType.MUSIQUE)) dont_expire_in_7_days_cd_booking = BookingFactory( stock__offer__product=non_expired_cd, dateCreated=booking_date_22_days_ago, isCancelled=False, ) repository.save(dont_expire_in_7_days_cd_booking) # When notify_users_of_soon_to_be_expired_bookings() # Then mocked_email_recap.assert_called_once_with( expire_in_7_days_dvd_booking.user, [expire_in_7_days_dvd_booking])
def test_cancel_bookings_when_offerer_has_one_or_more(): # Given fraudulent_emails_providers = ["example.com"] admin_user = AdminFactory(email="*****@*****.**") beneficiary1 = BeneficiaryGrant18Factory(email="*****@*****.**") beneficiary2 = BeneficiaryGrant18Factory(email="*****@*****.**") fraudulent_user = ProFactory(email="*****@*****.**", ) offerer_with_bookings = OffererFactory() UserOffererFactory(user=fraudulent_user, offerer=offerer_with_bookings) offer1 = OfferFactory(venue__managingOfferer=offerer_with_bookings) offer2 = OfferFactory(venue__managingOfferer=offerer_with_bookings) stock1 = StockFactory(offer=offer1) stock2 = StockFactory(offer=offer2) booking1 = BookingFactory(user=beneficiary1, stock=stock1) booking2 = BookingFactory(user=beneficiary2, stock=stock2) # When suspend_fraudulent_pro_by_email_providers(fraudulent_emails_providers, admin_user, dry_run=False) # Then assert Offerer.query.count() == 1 assert Venue.query.count() == 2 assert Offer.query.count() == 2 assert Stock.query.count() == 2 assert Booking.query.count() == 2 assert booking1.isCancelled assert booking1.status is BookingStatus.CANCELLED assert booking1.cancellationReason is BookingCancellationReasons.FRAUD assert booking2.isCancelled assert booking2.status is BookingStatus.CANCELLED assert booking2.cancellationReason is BookingCancellationReasons.FRAUD
def test_should_send_email_to_beneficiary_when_expired_bookings_cancelled( self, app): amnesiac_user = users_factories.UserFactory(email="*****@*****.**") expired_today_dvd_booking = BookingFactory(user=amnesiac_user, ) expired_today_cd_booking = BookingFactory(user=amnesiac_user, ) send_expired_bookings_recap_email_to_beneficiary( amnesiac_user, [expired_today_cd_booking, expired_today_dvd_booking]) assert mails_testing.outbox[0].sent_data["Mj-TemplateID"] == 1951103
def test_should_send_email_to_offerer_when_expired_bookings_cancelled( self, app): offerer = OffererFactory() expired_today_dvd_booking = BookingFactory() expired_today_cd_booking = BookingFactory() send_expired_bookings_recap_email_to_offerer( offerer, [expired_today_cd_booking, expired_today_dvd_booking]) assert mails_testing.outbox[0].sent_data["Mj-TemplateID"] == 1952508
def test_should_send_email_to_offerer_when_expired_bookings_cancelled( self, app): now = datetime.utcnow() amnesiac_user = users_factories.UserFactory(email="*****@*****.**", firstName="Dory") long_ago = now - timedelta(days=31) dvd = ProductFactory(type=str(offer_type.ThingType.AUDIOVISUEL)) expired_today_dvd_booking = BookingFactory( stock__offer__product=dvd, stock__offer__name="Memento", stock__offer__venue__name="Mnémosyne", dateCreated=long_ago, isCancelled=True, cancellationReason=BookingCancellationReasons.EXPIRED, user=amnesiac_user, ) cd = ProductFactory(type=str(offer_type.ThingType.MUSIQUE)) expired_today_cd_booking = BookingFactory( stock__offer__product=cd, stock__offer__name="Random Access Memories", stock__offer__venue__name="Virgin Megastore", dateCreated=long_ago, isCancelled=True, cancellationReason=BookingCancellationReasons.EXPIRED, user=amnesiac_user, ) email_data = build_expired_bookings_recap_email_data_for_beneficiary( amnesiac_user, [expired_today_dvd_booking, expired_today_cd_booking], ) assert email_data == { "FromEmail": "*****@*****.**", "Mj-TemplateID": 1951103, "Mj-TemplateLanguage": True, "To": "*****@*****.**", "Vars": { "user_firstName": "Dory", "bookings": [ { "offer_name": "Memento", "venue_name": "Mnémosyne" }, { "offer_name": "Random Access Memories", "venue_name": "Virgin Megastore" }, ], "env": "-development", }, }
def test_should_send_email_to_beneficiary_when_expired_bookings_cancelled( self, app): amnesiac_user = users_factories.UserFactory(email="*****@*****.**") expired_today_dvd_booking = BookingFactory(user=amnesiac_user, ) expired_today_cd_booking = BookingFactory(user=amnesiac_user, ) mocked_send_email = Mock() send_expired_bookings_recap_email_to_beneficiary( amnesiac_user, [expired_today_cd_booking, expired_today_dvd_booking], mocked_send_email) mocked_send_email.assert_called_once() mocked_send_email.call_args_list[0][1]["MJ-TemplateID"] = 1951103
def should_notify_of_todays_expired_bookings(self, mocked_send_email_recap, mocked_send_raw_email, app, caplog) -> None: caplog.set_level(logging.INFO) now = datetime.utcnow() yesterday = now - timedelta(days=1) long_ago = now - timedelta(days=31) very_long_ago = now - timedelta(days=32) dvd = ProductFactory(type=str(offer_type.ThingType.AUDIOVISUEL)) expired_today_dvd_booking = BookingFactory( stock__offer__product=dvd, dateCreated=long_ago, isCancelled=True, cancellationReason=BookingCancellationReasons.EXPIRED, ) cd = ProductFactory(type=str(offer_type.ThingType.MUSIQUE)) expired_today_cd_booking = BookingFactory( stock__offer__product=cd, dateCreated=long_ago, isCancelled=True, cancellationReason=BookingCancellationReasons.EXPIRED, ) painting = ProductFactory(type=str(offer_type.ThingType.OEUVRE_ART)) expired_yesterday_painting_booking = BookingFactory( stock__offer__product=painting, dateCreated=very_long_ago, isCancelled=True, cancellationReason=BookingCancellationReasons.EXPIRED, ) expired_yesterday_painting_booking.cancellationDate = yesterday repository.save(expired_yesterday_painting_booking) handle_expired_bookings.notify_offerers_of_expired_bookings() assert ( caplog.records[1].message == f"[notify_users_of_expired_bookings] 2 Offerers have been notified: [{expired_today_dvd_booking.stock.offer.venue.managingOfferer}," f" {expired_today_cd_booking.stock.offer.venue.managingOfferer}]") assert str(expired_yesterday_painting_booking) not in caplog.text assert mocked_send_email_recap.call_args_list[0][0] == ( expired_today_dvd_booking.stock.offer.venue.managingOfferer, [expired_today_dvd_booking], mocked_send_raw_email, ) assert mocked_send_email_recap.call_args_list[1][0] == ( expired_today_cd_booking.stock.offer.venue.managingOfferer, [expired_today_cd_booking], mocked_send_raw_email, )
def test_should_send_email_to_offerer_when_expired_bookings_cancelled( self, app): offerer = OffererFactory() expired_today_dvd_booking = BookingFactory() expired_today_cd_booking = BookingFactory() mocked_send_email = Mock() send_expired_bookings_recap_email_to_offerer( offerer, [expired_today_cd_booking, expired_today_dvd_booking], mocked_send_email) mocked_send_email.assert_called_once() mocked_send_email.call_args_list[0][1]["MJ-TemplateID"] = 1952508 mocked_send_email.call_args_list[0][1][ "recipients"] = "*****@*****.**"
def test_should_sends_email_to_beneficiary_when_pro_cancels_booking(self): # Given booking = BookingFactory(user__email="*****@*****.**", user__firstName="Jeanne") # When send_warning_to_beneficiary_after_pro_booking_cancellation(booking) # Then assert mails_testing.outbox[0].sent_data == { "FromEmail": "*****@*****.**", "MJ-TemplateID": 1116690, "MJ-TemplateLanguage": True, "To": "*****@*****.**", "Vars": { "event_date": "", "event_hour": "", "is_event": 0, "is_free_offer": 0, "is_thing": 1, "is_online": 0, "offer_name": booking.stock.offer.name, "offer_price": "10.00", "offerer_name": booking.stock.offer.venue.managingOfferer.name, "user_first_name": "Jeanne", "can_book_again": True, "venue_name": booking.stock.offer.venue.name, "env": "-development", }, }
def test_should_return_booking_with_expected_information(self): # Given booking = BookingFactory( amount=1, quantity=1, token="GQTQR9", stock__price=10, ) # When serialized = serialize_booking_minimal(booking) # Then assert serialized == { "amount": 1.0, "isCancelled": booking.isCancelled, "id": humanize(booking.id), "stockId": humanize(booking.stockId), "quantity": 1, "stock": { "price": 10, }, "token": "GQTQR9", "completedUrl": None, }
def test_send_only_to_admin(self): booking = BookingFactory() send_booking_recap_emails(booking) assert mails_testing.outbox[0].sent_data[ "To"] == "*****@*****.**"
def when_user_has_cancelled_some_offers(self, app): # Given BookingFactory(isCancelled=True, user__email="*****@*****.**", user__postalCode="75130") # When response = TestClient(app.test_client()).with_auth( "*****@*****.**").get("/beneficiaries/current") # Then assert response.json["wallet_balance"] == 500.0 assert response.json["expenses"] == [ { "domain": "all", "current": 0.0, "max": 500.0 }, { "domain": "digital", "current": 0.0, "max": 200.0 }, { "domain": "physical", "current": 0.0, "max": 200.0 }, ]
def test_cancellation_limit_date_is_saved_to_db(self): booking = BookingFactory( stock__beginningDatetime=datetime.datetime.utcnow()) generated_cancellation_limit_date = booking.cancellationLimitDate booking_from_db = Booking.query.first() assert booking_from_db.cancellationLimitDate == generated_cancellation_limit_date
def test_send_to_offerer_and_admin(self): booking = BookingFactory( stock__offer__bookingEmail="*****@*****.**", ) send_booking_recap_emails(booking) assert mails_testing.outbox[0].sent_data[ "To"] == "[email protected], [email protected]"
def should_not_cancel_new_thing_that_can_expire_booking(self, app) -> None: book = ProductFactory(type=str(offer_type.ThingType.LIVRE_EDITION)) book_booking = BookingFactory(stock__offer__product=book) handle_expired_bookings.cancel_expired_bookings() assert not book_booking.isCancelled assert not book_booking.cancellationDate assert not book_booking.cancellationReason
def test_send_only_to_admin(self, feature_send_mail_to_users_enabled): booking = BookingFactory() mocked_send_email = Mock() send_booking_recap_emails(booking, mocked_send_email) mocked_send_email.assert_called_once() data = mocked_send_email.call_args_list[0][1]["data"] assert data["To"] == "*****@*****.**"
def test_queries_performance(self, app) -> None: now = datetime.utcnow() two_months_ago = now - timedelta(days=60) book = ProductFactory(type=str(offer_type.ThingType.LIVRE_EDITION)) user = UserFactory() BookingFactory.create_batch(size=10, stock__offer__product=book, dateCreated=two_months_ago, user=user) n_queries = ( 1 # select count + 1 # select initial ids + 1 # release savepoint/COMMIT + 4 * 3 # update, release savepoint/COMMIT, select next ids ) with assert_num_queries(n_queries): handle_expired_bookings.cancel_expired_bookings(batch_size=3)
def test_build_soon_to_be_expired_bookings_data(self, app): # Given beneficiary = UserFactory(email="*****@*****.**", firstName="ASIMOV", isBeneficiary=True, isAdmin=False) bookings = [ BookingFactory( stock__offer__name="offre 1", stock__offer__venue__name="venue 1", ), BookingFactory( stock__offer__name="offre 2", stock__offer__venue__name="venue 2", ), ] # When data = build_soon_to_be_expired_bookings_recap_email_data_for_beneficiary( beneficiary, bookings) # Then assert data == { "FromEmail": "*****@*****.**", "Mj-TemplateID": 1927224, "Mj-TemplateLanguage": True, "To": "*****@*****.**", "Vars": { "user_firstName": "ASIMOV", "bookings": [ { "offer_name": "offre 1", "venue_name": "venue 1" }, { "offer_name": "offre 2", "venue_name": "venue 2" }, ], "env": "-development", }, }
def test_returns_max_500_and_actual_210(self): # Given beneficiary = UserFactory() BookingFactory( user=beneficiary, amount=90, ) BookingFactory(user=beneficiary, amount=60, quantity=2) BookingFactory(user=beneficiary, amount=20, isCancelled=True) # when expenses = beneficiary.expenses # Then assert expenses == [ {"domain": "all", "current": 210.0, "max": 500}, {"domain": "digital", "current": 0.0, "max": 200}, {"domain": "physical", "current": 210.0, "max": 200}, ]
def test_send_to_developers(self, mock_feature_send_mail_to_users_enabled): booking = BookingFactory( stock__offer__bookingEmail="*****@*****.**", ) mocked_send_email = Mock() send_booking_recap_emails(booking, mocked_send_email) mocked_send_email.assert_called_once() data = mocked_send_email.call_args_list[0][1]["data"] assert data["To"] == "*****@*****.**"
def should_not_update_cancelled_old_thing_that_can_expire_booking( self, app) -> None: fifty_days_ago = datetime.utcnow() - timedelta(days=50) forty_days_ago = datetime.utcnow() - timedelta(days=40) book = ProductFactory(type=str(offer_type.ThingType.LIVRE_EDITION)) old_book_booking = BookingFactory( stock__offer__product=book, dateCreated=fifty_days_ago, isCancelled=True, cancellationReason=BookingCancellationReasons.BENEFICIARY, ) old_book_booking.cancellationDate = forty_days_ago repository.save() handle_expired_bookings.cancel_expired_bookings() assert old_book_booking.isCancelled assert old_book_booking.cancellationDate == forty_days_ago assert old_book_booking.cancellationReason == BookingCancellationReasons.BENEFICIARY
def test_should_return_0_offer_when_all_available_stock_is_booked(self, app): # Given beneficiary = users_factories.BeneficiaryGrant18Factory() offer = ThingOfferFactory() stock = ThingStockFactory(offer=offer, price=0, quantity=3) BookingFactory(user=beneficiary, stock=stock, quantity=2) BookingFactory(user=beneficiary, stock=stock, quantity=1) # When bookings_quantity = _build_bookings_quantity_subquery() offers_count = ( Offer.query.join(Stock) .outerjoin(bookings_quantity, Stock.id == bookings_quantity.c.stockId) .filter((Stock.quantity == None) | ((Stock.quantity - func.coalesce(bookings_quantity.c.quantity, 0)) > 0)) .count() ) # Then assert offers_count == 0
def test_should_send_email_to_beneficiary_when_they_have_soon_to_be_expired_bookings( self, build_soon_to_be_expired_bookings_recap_email_data_for_beneficiary ): # given now = datetime.utcnow() user = users_factories.UserFactory(email="*****@*****.**", isBeneficiary=True, isAdmin=False) created_23_days_ago = now - timedelta(days=23) dvd = ProductFactory(type=str(offer_type.ThingType.AUDIOVISUEL)) soon_to_be_expired_dvd_booking = BookingFactory( stock__offer__product=dvd, stock__offer__name="Fondation", stock__offer__venue__name="Première Fondation", dateCreated=created_23_days_ago, user=user, ) cd = ProductFactory(type=str(offer_type.ThingType.MUSIQUE)) soon_to_be_expired_cd_booking = BookingFactory( stock__offer__product=cd, stock__offer__name="Fondation et Empire", stock__offer__venue__name="Seconde Fondation", dateCreated=created_23_days_ago, user=user, ) mocked_send_email = Mock() # when send_soon_to_be_expired_bookings_recap_email_to_beneficiary( user, [soon_to_be_expired_cd_booking, soon_to_be_expired_dvd_booking], mocked_send_email) # then build_soon_to_be_expired_bookings_recap_email_data_for_beneficiary.assert_called_once_with( user, [soon_to_be_expired_cd_booking, soon_to_be_expired_dvd_booking]) mocked_send_email.assert_called_once_with( data={"MJ-TemplateID": 12345})
def test_build_soon_to_be_expired_bookings_data(self, app): # Given beneficiary = BeneficiaryGrant18Factory(email="*****@*****.**", firstName="ASIMOV") bookings = [ BookingFactory( stock__offer__name="offre 1", stock__offer__venue__name="venue 1", ), BookingFactory( stock__offer__name="offre 2", stock__offer__venue__name="venue 2", ), ] # When data = build_soon_to_be_expired_bookings_recap_email_data_for_beneficiary( beneficiary, bookings, days_before_cancel=7, days_from_booking=23) # Then assert data == { "Mj-TemplateID": 3095065, "Mj-TemplateLanguage": True, "Vars": { "user_firstName": "ASIMOV", "bookings": [ { "offer_name": "offre 1", "venue_name": "venue 1" }, { "offer_name": "offre 2", "venue_name": "venue 2" }, ], "days_before_cancel": 7, "days_from_booking": 23, }, }
def test_should_send_booking_cancellation_email_only_to_administration_when_no_booking_email_provided( self): # Given booking = BookingFactory(stock__offer__bookingEmail="") # When send_user_driven_cancellation_email_to_offerer(booking) # Then assert mails_testing.outbox[0].sent_data[ "To"] == "*****@*****.**"
def test_pc_send_tomorrow_events_notifications_only_to_individual_bookings_users( ): """ Test that each stock that is linked to an offer that occurs tomorrow and creates a job that will send a notification to all of the stock's users with a valid (not cancelled) booking, for individual bookings only. """ tomorrow = datetime.now() + timedelta(days=1) stock_tomorrow = EventStockFactory(beginningDatetime=tomorrow, offer__name="my_offer") begin = datetime.now() + timedelta(days=7) stock_next_week = EventStockFactory(beginningDatetime=begin) bookings_tomorrow = IndividualBookingFactory.create_batch( 2, stock=stock_tomorrow, isCancelled=False) BookingFactory.create_batch(2, stock=stock_tomorrow, isCancelled=True, status=BookingStatus.CANCELLED) BookingFactory.create_batch(2, stock=stock_next_week, isCancelled=False) EducationalBookingFactory.create_batch(2, stock=stock_tomorrow, isCancelled=False) pc_send_tomorrow_events_notifications() assert len(testing.requests) == 1 assert all(data["message"]["title"] == "my_offer, c'est demain !" for data in testing.requests) user_ids = set() for data in testing.requests: for user_id in data["user_ids"]: user_ids.add(user_id) expected_user_ids = { booking.individualBooking.userId for booking in bookings_tomorrow } assert user_ids == expected_user_ids
def when_user_is_logged_in_expect_booking_with_token_in_lower_case_to_be_used(self, app): booking = BookingFactory(token="ABCDEF") pro_user = UserFactory(email="*****@*****.**") offerer = booking.stock.offer.venue.managingOfferer offers_factories.UserOffererFactory(user=pro_user, offerer=offerer) url = f"/v2/bookings/use/token/{booking.token.lower()}" response = TestClient(app.test_client()).with_auth("*****@*****.**").patch(url) assert response.status_code == 204 booking = Booking.query.one() assert booking.isUsed
def expect_booking_to_be_used(self, app): booking = BookingFactory(token="ABCDEF") url = ( f"/bookings/token/{booking.token}?" f"email={booking.user.email}&offer_id={humanize(booking.stock.offerId)}" ) response = TestClient(app.test_client()).patch(url) assert response.status_code == 204 booking = Booking.query.one() assert booking.isUsed
def test_already_booked(self, app): user = users_factories.UserFactory(email=self.identifier) booking = BookingFactory(user=user) access_token = create_access_token(identity=self.identifier) test_client = TestClient(app.test_client()) test_client.auth_header = {"Authorization": f"Bearer {access_token}"} response = test_client.post("/native/v1/book_offer", json={"stockId": booking.stock.id, "quantity": 1}) assert response.status_code == 400 assert response.json["code"] == "ALREADY_BOOKED"