def test_balance(self): # given user = users_factories.BeneficiaryGrant18Factory(deposit__version=1) bookings_factories.UsedIndividualBookingFactory( individualBooking__user=user, quantity=1, amount=10) bookings_factories.UsedIndividualBookingFactory( individualBooking__user=user, quantity=2, amount=20) bookings_factories.IndividualBookingFactory( individualBooking__user=user, quantity=3, amount=30) bookings_factories.CancelledIndividualBookingFactory( individualBooking__user=user, quantity=4, amount=40) # then assert user.wallet_balance == 500 - (10 + 2 * 20 + 3 * 30) assert user.real_wallet_balance == 500 - (10 + 2 * 20)
def test_raise_if_booking_is_already_used(self): booking = booking_factories.UsedIndividualBookingFactory() with pytest.raises(exceptions.BookingIsAlreadyUsed): api.cancel_booking_by_beneficiary(booking.individualBooking.user, booking) assert not booking.isCancelled assert booking.status is not BookingStatus.CANCELLED
def test_raise_if_already_used(self): booking = factories.UsedIndividualBookingFactory() with pytest.raises(api_errors.ForbiddenError) as exc: validation.check_booking_can_be_cancelled(booking) assert exc.value.errors["global"] == [ "Impossible d'annuler une réservation consommée" ]
def should_raise_if_used(self): booking = factories.UsedIndividualBookingFactory() with pytest.raises(api_errors.ResourceGoneError) as exc: validation.check_is_usable(booking) assert exc.value.errors["booking"] == [ "Cette réservation a déjà été validée" ]
def test_cancel_all_bookings_from_stock(self, app): stock = offers_factories.StockFactory(dnBookedQuantity=1) booking_1 = booking_factories.IndividualBookingFactory(stock=stock) booking_2 = booking_factories.IndividualBookingFactory(stock=stock) used_booking = booking_factories.UsedIndividualBookingFactory(stock=stock) cancelled_booking = booking_factories.CancelledIndividualBookingFactory(stock=stock) api.cancel_bookings_when_offerer_deletes_stock(stock) # cancellation can trigger more than one request to Batch assert len(push_testing.requests) >= 1 assert models.Booking.query.filter().count() == 4 assert models.Booking.query.filter(models.Booking.isCancelled == True).count() == 3 assert models.Booking.query.filter(models.Booking.isUsed == True).count() == 1 assert booking_1.isCancelled assert booking_1.status is BookingStatus.CANCELLED assert booking_1.cancellationReason == BookingCancellationReasons.OFFERER assert booking_2.isCancelled assert booking_2.status is BookingStatus.CANCELLED assert booking_2.cancellationReason == BookingCancellationReasons.OFFERER assert not used_booking.isCancelled assert used_booking.status is not BookingStatus.CANCELLED assert not used_booking.cancellationReason assert cancelled_booking.isCancelled assert cancelled_booking.status is BookingStatus.CANCELLED assert cancelled_booking.cancellationReason == BookingCancellationReasons.BENEFICIARY
def test_raise_if_has_payment(self): booking = booking_factories.UsedIndividualBookingFactory() payments_factories.PaymentFactory(booking=booking) with pytest.raises(api_errors.ResourceGoneError): api.mark_as_unused(booking) assert booking.isUsed # unchanged assert booking.status is BookingStatus.USED
def test_no_need_when_booking_is_autovalidated(self): # Given offer = offers_factories.OfferFactory( venue__name="Lieu de l'offreur", venue__managingOfferer__name="Théâtre du coin", product=offers_factories.DigitalProductFactory( name="Super événement", url="http://example.com"), ) digital_stock = offers_factories.StockWithActivationCodesFactory() first_activation_code = digital_stock.activationCodes[0] booking = bookings_factories.UsedIndividualBookingFactory( individualBooking__user__email="*****@*****.**", individualBooking__user__firstName="John", individualBooking__user__lastName="Doe", stock__offer=offer, activationCode=first_activation_code, dateCreated=datetime(2018, 1, 1), ) # When email_data = retrieve_data_for_offerer_booking_recap_email( booking.individualBooking) # Then expected = get_expected_base_email_data( booking, date="", heure="", is_event=0, is_booking_autovalidated=1, must_use_token_for_payment=0, offer_type="VOD", contremarque=booking.token, ) assert email_data == expected
def should_raises_forbidden_error_if_payement_exists(self, app): booking = factories.UsedIndividualBookingFactory() payments_factories.PaymentFactory(booking=booking) with pytest.raises(api_errors.ForbiddenError) as exc: validation.check_is_usable(booking) assert exc.value.errors["payment"] == [ "Cette réservation a été remboursée" ]
def test_raise_if_already_used(self): booking = booking_factories.UsedIndividualBookingFactory() with pytest.raises(api_errors.ForbiddenError): api.cancel_booking_by_offerer(booking) assert booking.isUsed assert not booking.isCancelled assert booking.status is BookingStatus.USED assert not booking.cancellationReason assert push_testing.requests == []
def test_does_not_update_booking_if_already_used(self): event_date = datetime.now() - timedelta(days=3) booking = booking_factories.UsedIndividualBookingFactory(stock__beginningDatetime=event_date) initial_date_used = booking.dateUsed api.auto_mark_as_used_after_event() booking = Booking.query.first() assert booking.isUsed assert booking.dateUsed == initial_date_used
def test_raise_if_booking_was_automatically_used_for_digital_offer_with_activation_code(self): offer = offers_factories.OfferFactory(product=offers_factories.DigitalProductFactory()) digital_stock = offers_factories.StockWithActivationCodesFactory() first_activation_code = digital_stock.activationCodes[0] booking = booking_factories.UsedIndividualBookingFactory( stock__offer=offer, activationCode=first_activation_code ) with pytest.raises(api_errors.ForbiddenError): api.mark_as_unused(booking) assert booking.isUsed assert booking.status is BookingStatus.USED
def test_balance_should_not_be_negative(self): # given user = users_factories.BeneficiaryGrant18Factory(deposit__version=1) bookings_factories.UsedIndividualBookingFactory( individualBooking__user=user, quantity=1, amount=10) deposit = user.deposit deposit.expirationDate = datetime(2000, 1, 1) # then assert user.wallet_balance == 0 assert user.real_wallet_balance == 0
def test_when_user_is_logged_in_and_regular_offer(self, client): booking = bookings_factories.UsedIndividualBookingFactory() pro_user = offers_factories.UserOffererFactory( offerer=booking.offerer).user url = f"/v2/bookings/keep/token/{booking.token}" response = client.with_session_auth(pro_user.email).patch(url) assert response.status_code == 204 booking = Booking.query.one() assert not booking.isUsed assert booking.status is not BookingStatus.USED assert booking.dateUsed is None
def test_ignore_activation_that_is_already_used_for_booking(self): # Given beneficiary = users_factories.BeneficiaryGrant18Factory() booking = booking_factories.UsedIndividualBookingFactory(token="ABCDEF") stock = offers_factories.StockWithActivationCodesFactory( activationCodes=["code-vgya451afvyux", "code-bha45k15fuz"] ) stock.activationCodes[0].booking = booking # When booking = api.book_offer(beneficiary=beneficiary, stock_id=stock.id, quantity=1) # Then assert booking.activationCode.code == "code-bha45k15fuz"
def test_integration_toggle_visibility(self, app): user = users_factories.BeneficiaryGrant18Factory(email=self.identifier) access_token = create_access_token(identity=self.identifier) stock = StockWithActivationCodesFactory() activation_code = stock.activationCodes[0] booking = booking_factories.UsedIndividualBookingFactory( individualBooking__user=user, displayAsEnded=None, dateUsed=datetime.now(), stock=stock, activationCode=activation_code, ) test_client = TestClient(app.test_client()) test_client.auth_header = {"Authorization": f"Bearer {access_token}"} response = test_client.get("/native/v1/bookings") assert response.status_code == 200 assert [b["id"] for b in response.json["ongoing_bookings"]] == [booking.id] assert [b["id"] for b in response.json["ended_bookings"]] == [] response = test_client.post( f"/native/v1/bookings/{booking.id}/toggle_display", json={"ended": True}) assert response.status_code == 204 response = test_client.get("/native/v1/bookings") assert response.status_code == 200 assert [b["id"] for b in response.json["ongoing_bookings"]] == [] assert [b["id"] for b in response.json["ended_bookings"]] == [booking.id] response = test_client.post( f"/native/v1/bookings/{booking.id}/toggle_display", json={"ended": False}) assert response.status_code == 204 response = test_client.get("/native/v1/bookings") assert response.status_code == 200 assert [b["id"] for b in response.json["ongoing_bookings"]] == [booking.id] assert [b["id"] for b in response.json["ended_bookings"]] == []
def test_suspend_users_by_ids_does_not_cancel_bookings_when_not_cancellable( self): fraudulent_user_ids = [15] admin_user = AdminFactory() fraudulent_user = BeneficiaryGrant18Factory(id=15) uncancellable_booking = booking_factories.UsedIndividualBookingFactory( individualBooking__user=fraudulent_user, stock__price=1) suspend_fraudulent_beneficiary_users_by_ids(fraudulent_user_ids, admin_user, dry_run=False) assert not fraudulent_user.isActive assert not uncancellable_booking.isCancelled assert uncancellable_booking.status is not BookingStatus.CANCELLED
def test_when_there_is_no_remaining_quantity_after_validating( self, client): booking = bookings_factories.UsedIndividualBookingFactory( stock__quantity=1) pro_user = offers_factories.UserOffererFactory( offerer=booking.offerer).user url = f"/v2/bookings/keep/token/{booking.token.lower()}" response = client.with_session_auth(pro_user.email).patch(url) assert response.status_code == 204 booking = Booking.query.one() assert not booking.isUsed assert booking.status is not BookingStatus.USED assert booking.dateUsed is None
def test_should_not_return_payments_to_retry_if_no_bank_information(self, app): # Given product = offers_factories.ThingProductFactory(name="Lire un livre", isNational=True) venue = offers_factories.VenueFactory(postalCode="34000", departementCode="34") offer = offers_factories.ThingOfferFactory(product=product, venue=venue) stock = offers_factories.ThingStockFactory(offer=offer, price=0, quantity=2) booking = bookings_factories.UsedIndividualBookingFactory(stock=stock, quantity=2) payment = factories.PaymentFactory(booking=booking, amount=10) factories.PaymentStatusFactory(payment=payment, status=TransactionStatus.NOT_PROCESSABLE) # When payments_to_retry = payment_queries.find_not_processable_with_bank_information() # Then assert payments_to_retry == []
def test_raise_when_no_activation_code_available(self): # Given beneficiary = users_factories.BeneficiaryGrant18Factory() booking = booking_factories.UsedIndividualBookingFactory(token="ABCDEF") stock = offers_factories.StockWithActivationCodesFactory(activationCodes=["code-vgya451afvyux"]) stock.activationCodes[0].booking = booking # When with pytest.raises(exceptions.NoActivationCodeAvailable) as error: api.book_offer(beneficiary=beneficiary, stock_id=stock.id, quantity=1) # Then assert Booking.query.count() == 1 assert error.value.errors == { "noActivationCodeAvailable": ["Ce stock ne contient plus de code d'activation disponible."] }
def when_the_booking_cannot_be_cancelled(self, app): # Given booking = bookings_factories.UsedIndividualBookingFactory() # When client = TestClient(app.test_client()).with_session_auth(booking.email) response = client.put(f"/bookings/{humanize(booking.id)}/cancel") booking = Booking.query.get(booking.id) # Then assert response.status_code == 400 assert response.json["booking"] == [ "Impossible d'annuler une réservation consommée" ] assert not booking.isCancelled assert booking.status is BookingStatus.USED
def test_cancel_all_bookings_from_stock(self, app): stock = offers_factories.StockFactory(dnBookedQuantity=1) booking_factories.IndividualBookingFactory(stock=stock) booking_factories.IndividualBookingFactory(stock=stock) booking_factories.UsedIndividualBookingFactory(stock=stock) booking_factories.CancelledIndividualBookingFactory(stock=stock) # open a second connection on purpose and lock the stock engine = create_engine(app.config["SQLALCHEMY_DATABASE_URI"]) with engine.connect() as connection: connection.execute(text("""SELECT * FROM stock WHERE stock.id = :stock_id FOR UPDATE"""), stock_id=stock.id) with pytest.raises(sqlalchemy.exc.OperationalError): api.cancel_bookings_when_offerer_deletes_stock(stock) assert models.Booking.query.filter().count() == 4 assert models.Booking.query.filter(models.Booking.isCancelled == True).count() == 1
def test_does_not_cancel_booking_when_not_cancellable(self): # Given fraudulent_emails_providers = ["example.com"] admin_user = AdminFactory() fraudulent_user = BeneficiaryGrant18Factory( email="*****@*****.**") uncancellable_booking = booking_factories.UsedIndividualBookingFactory( individualBooking__user=fraudulent_user, stock__price=1) # When suspend_fraudulent_beneficiary_users_by_email_providers( fraudulent_emails_providers, admin_user, dry_run=False) # Then assert not fraudulent_user.isActive assert not uncancellable_booking.isCancelled assert uncancellable_booking.status is not BookingStatus.CANCELLED
def test_when_api_key_provided_is_related_to_regular_offer_with_rights( self, client): booking = bookings_factories.UsedIndividualBookingFactory() ApiKeyFactory(offerer=booking.offerer) url = f"/v2/bookings/keep/token/{booking.token}" response = client.patch( url, headers={ "Authorization": f"Bearer {DEFAULT_CLEAR_API_KEY}", }, ) assert response.status_code == 204 booking = Booking.query.one() assert not booking.isUsed assert booking.status is not BookingStatus.USED assert booking.dateUsed is None
def test_hide_cancellation_policy_on_bookings_with_activation_code(self): offer = offers_factories.OfferFactory( venue__name="Lieu de l'offreur", venue__managingOfferer__name="Théâtre du coin", product=offers_factories.DigitalProductFactory(name="Super offre numérique", url="http://example.com"), ) digital_stock = offers_factories.StockWithActivationCodesFactory() first_activation_code = digital_stock.activationCodes[0] booking = bookings_factories.UsedIndividualBookingFactory( stock__offer=offer, activationCode=first_activation_code, dateCreated=datetime(2018, 1, 1), ) mediation = offers_factories.MediationFactory(offer=booking.stock.offer) email_data = retrieve_data_for_beneficiary_booking_confirmation_email(booking.individualBooking) expected = get_expected_base_email_data( booking, mediation, all_but_not_virtual_thing=0, all_things_not_virtual_thing=0, event_date="", event_hour="", is_event=0, is_single_event=0, offer_name="Super offre numérique", offer_price="10.00 €", offer_token=booking.activationCode.code, can_expire=0, is_digital_booking_with_activation_code_and_no_expiration_date=1, has_offer_url=1, digital_offer_url="http://example.com", user_first_name="Jeanne", venue_address="1 boulevard Poissonnière", venue_city="Paris", venue_postal_code="75000", booking_date="1 janvier 2018", booking_hour="01h00", expiration_delay="", ) assert email_data == expected
def test_can_not_cancel_refunded_booking(self, app): users_factories.UserFactory(email="*****@*****.**", isAdmin=True) booking = bookings_factories.UsedIndividualBookingFactory() payments_factories.PaymentFactory(booking=booking) client = TestClient( app.test_client()).with_session_auth("*****@*****.**") route = f"/pc/back-office/bookings/cancel/{booking.id}" response = client.post(route, form={}) assert response.status_code == 302 assert response.location == f"http://localhost/pc/back-office/bookings/?id={booking.id}" response = client.get(response.location) content = response.data.decode(response.charset) assert "L'opération a échoué : la réservation a déjà été remboursée" in content booking = Booking.query.get(booking.id) assert not booking.isCancelled
def test_should_return_beneficiary_bookings_with_expected_information(self, app): # Given beneficiary = BeneficiaryGrant18Factory() offer = ThingOfferFactory(isActive=True, url="http://url.com", product__thumbCount=1) stock = ThingStockFactory(offer=offer, price=0, quantity=10) booking = booking_factories.UsedIndividualBookingFactory( individualBooking__user=beneficiary, stock=stock, token="ABCDEF", dateCreated=datetime(2020, 4, 22, 0, 0), dateUsed=datetime(2020, 5, 5, 0, 0), quantity=2, ) # When result = BeneficiaryBookingsSQLRepository().get_beneficiary_bookings(beneficiary_id=beneficiary.id) # Then assert isinstance(result, BeneficiaryBookingsWithStocks) assert len(result.bookings) == 1 expected_booking = result.bookings[0] assert expected_booking.amount == 0.0 assert expected_booking.cancellationDate is None assert expected_booking.dateCreated == datetime(2020, 4, 22, 0, 0) assert expected_booking.dateUsed == datetime(2020, 5, 5, 0, 0) assert expected_booking.id == booking.id assert expected_booking.isCancelled is False assert expected_booking.isUsed is True assert expected_booking.quantity == 2 assert expected_booking.stockId == stock.id assert expected_booking.token == booking.token assert expected_booking.userId == beneficiary.id assert expected_booking.offerId == stock.offer.id assert expected_booking.name == stock.offer.name assert expected_booking.url == stock.offer.url assert expected_booking.email == beneficiary.email assert expected_booking.beginningDatetime == stock.beginningDatetime assert expected_booking.venueId == stock.offer.venue.id assert expected_booking.departementCode == stock.offer.venue.departementCode assert ( expected_booking.thumb_url == f"http://localhost/storage/thumbs/products/{humanize(stock.offer.productId)}" )
def test_should_return_one_payment_info_with_error_status(self, app): # Given stock = offers_factories.ThingStockFactory(price=10) now = datetime.utcnow() booking = bookings_factories.UsedIndividualBookingFactory( stock=stock, dateUsed=now, token="ABCDEF") payment = payments_factories.PaymentFactory( booking=booking, transactionLabel= "pass Culture Pro - remboursement 1ère quinzaine 07-2019") payments_factories.PaymentStatusFactory(payment=payment, status=TransactionStatus.ERROR, detail="Iban non fourni") # When payments = legacy_find_all_offerer_payments( stock.offer.venue.managingOfferer.id) # Then assert len(payments) == 1 assert payments[0] == ( "Doux", "Jeanne", None, None, "ABCDEF", now, 1, Decimal("10.00"), stock.offer.name, "1 boulevard Poissonnière", stock.offer.venue.name, stock.offer.venue.siret, "1 boulevard Poissonnière", Decimal("10.00"), Decimal("1.00"), "CF13QSDFGH456789", "pass Culture Pro - remboursement 1ère quinzaine 07-2019", TransactionStatus.ERROR, "Iban non fourni", )
def test_should_return_payment_to_retry_if_bank_information_linked_to_offerer_and_current_status_is_not_processable( self, app ): # Given user_offerer = offers_factories.UserOffererFactory() product = offers_factories.ThingProductFactory(name="Lire un livre", isNational=True) venue = offers_factories.VenueFactory( managingOfferer=user_offerer.offerer, postalCode="34000", departementCode="34" ) offer = offers_factories.ThingOfferFactory(product=product, venue=venue) stock = offers_factories.ThingStockFactory(offer=offer, price=0) booking = bookings_factories.UsedIndividualBookingFactory(stock=stock) not_processable_payment = factories.PaymentFactory(booking=booking, amount=10) factories.PaymentStatusFactory(payment=not_processable_payment, status=TransactionStatus.NOT_PROCESSABLE) offers_factories.BankInformationFactory(offerer=user_offerer.offerer) # When payments_to_retry = payment_queries.find_not_processable_with_bank_information() # Then assert not_processable_payment in payments_to_retry
def test_returns_both_current_and_real_balances(self): # given offer = offers_factories.OfferFactory() stock1 = offers_factories.StockFactory(offer=offer, price=20) stock2 = offers_factories.StockFactory(offer=offer, price=30) stock3 = offers_factories.StockFactory(offer=offer, price=40) user = users_factories.BeneficiaryGrant18Factory(deposit__version=1) bookings_factories.IndividualBookingFactory( individualBooking__user=user, stock=stock1) bookings_factories.CancelledIndividualBookingFactory( individualBooking__user=user, stock=stock2) bookings_factories.UsedIndividualBookingFactory( individualBooking__user=user, stock=stock3, quantity=2) # when balances = get_all_users_wallet_balances() # then balance = balances[0] assert balance.current_balance == 500 - (20 + 40 * 2) assert balance.real_balance == 500 - (40 * 2)
def test_should_not_return_payment_to_retry_if_bank_information_status_is_not_accepted(self, app): # Given user_offerer = offers_factories.UserOffererFactory() product = offers_factories.ThingProductFactory(name="Lire un livre", isNational=True) venue = offers_factories.VenueFactory( managingOfferer=user_offerer.offerer, postalCode="34000", departementCode="34" ) offer = offers_factories.ThingOfferFactory(product=product, venue=venue) stock = offers_factories.ThingStockFactory(offer=offer, price=0) booking = bookings_factories.UsedIndividualBookingFactory(stock=stock) not_processable_payment = factories.PaymentFactory( booking=booking, amount=10, iban="CF13QSDFGH456789", bic="QSDFGH8Z555" ) factories.PaymentStatusFactory(payment=not_processable_payment, status=TransactionStatus.NOT_PROCESSABLE) offers_factories.BankInformationFactory( offerer=user_offerer.offerer, iban=None, bic=None, status=BankInformationStatus.DRAFT ) # When payments_to_retry = payment_queries.find_not_processable_with_bank_information() # Then assert payments_to_retry == []