def test_should_not_create_educational_booking_when_stock_is_not_bookable( self, mocked_get_and_lock_stock): # Given stock = mock.MagicMock() stock.isBookable = False stock.id = 1 mocked_get_and_lock_stock.return_value = stock educational_redactor = educational_factories.EducationalRedactorFactory( email="*****@*****.**") educational_institution = educational_factories.EducationalInstitutionFactory( ) redactor_informations = AuthenticatedInformation( email=educational_redactor.email, civility=educational_redactor.civility, firstname=educational_redactor.firstName, lastname=educational_redactor.lastName, uai=educational_institution.institutionId, ) # When with pytest.raises(exceptions.StockNotBookable) as error: educational_api.book_educational_offer( redactor_informations=redactor_informations, stock_id=stock.id, ) # Then assert error.value.errors == { "stock": [f"Le stock {stock.id} n'est pas réservable"] } saved_bookings = EducationalBooking.query.join(Booking).filter( Booking.stockId == stock.id).all() assert len(saved_bookings) == 0
def test_should_not_create_educational_booking_when_stock_does_not_exist( self): # Given offers_factories.EducationalEventStockFactory( beginningDatetime=datetime.datetime(2021, 5, 15)) educational_institution = educational_factories.EducationalInstitutionFactory( ) educational_factories.EducationalYearFactory() educational_redactor = educational_factories.EducationalRedactorFactory( email="*****@*****.**") requested_stock_id = 4875 redactor_informations = AuthenticatedInformation( email=educational_redactor.email, civility=educational_redactor.civility, firstname=educational_redactor.firstName, lastname=educational_redactor.lastName, uai=educational_institution.institutionId, ) # When with pytest.raises(offers_exceptions.StockDoesNotExist): educational_api.book_educational_offer( redactor_informations=redactor_informations, stock_id=requested_stock_id, ) # Then saved_bookings = EducationalBooking.query.all() assert len(saved_bookings) == 0
def test_should_not_create_educational_booking_when_offer_is_not_an_event( self): # Given educational_institution = educational_factories.EducationalInstitutionFactory( ) educational_factories.EducationalYearFactory() educational_redactor = educational_factories.EducationalRedactorFactory( email="*****@*****.**") stock = offers_factories.ThingStockFactory( beginningDatetime=datetime.datetime(2021, 5, 15), offer__isEducational=True) redactor_informations = AuthenticatedInformation( email=educational_redactor.email, civility=educational_redactor.civility, firstname=educational_redactor.firstName, lastname=educational_redactor.lastName, uai=educational_institution.institutionId, ) # When with pytest.raises(exceptions.OfferIsNotEvent) as error: educational_api.book_educational_offer( redactor_informations=redactor_informations, stock_id=stock.id, ) # Then assert error.value.errors == { "offer": [f"L'offre {stock.offer.id} n'est pas une offre évènementielle"] } saved_bookings = EducationalBooking.query.join(Booking).filter( Booking.stockId == stock.id).all() assert len(saved_bookings) == 0
def test_import_educational_institutions_and_deposits_with_existing_data_on_future_year( self, db_session): # Given educational_year = factories.EducationalYearFactory( beginningDate=datetime(2020, 9, 1), expirationDate=datetime(2021, 8, 30), ) future_educational_year = factories.EducationalYearFactory( beginningDate=datetime(2021, 9, 1), expirationDate=datetime(2022, 8, 30), ) educational_institution2 = factories.EducationalInstitutionFactory( institutionId="4790032L") factories.EducationalDepositFactory( educationalInstitution=educational_institution2, educationalYear=future_educational_year, ) # When _process_educational_csv([{ "UAICode": "4790032L", "depositAmount": 5000 }]) # Then it creates educational deposit for current year even if it exists for another year educational_deposit = models.EducationalDeposit.query.filter( models.EducationalDeposit.educationalInstitutionId == educational_institution2.id, models.EducationalDeposit.educationalYearId == educational_year.adageId, ).one() assert educational_deposit.amount == Decimal(5000)
def test_should_not_create_educational_booking_when_educational_institution_unknown( self): # Given stock = offers_factories.EducationalEventStockFactory( beginningDatetime=datetime.datetime(2021, 5, 15)) educational_factories.EducationalInstitutionFactory() educational_factories.EducationalYearFactory() educational_redactor = educational_factories.EducationalRedactorFactory( email="*****@*****.**") provided_institution_id = "AU3568Unknown" redactor_informations = AuthenticatedInformation( email=educational_redactor.email, civility=educational_redactor.civility, firstname=educational_redactor.firstName, lastname=educational_redactor.lastName, uai=provided_institution_id, ) # When with pytest.raises(exceptions.EducationalInstitutionUnknown) as error: educational_api.book_educational_offer( redactor_informations=redactor_informations, stock_id=stock.id, ) # Then assert error.value.errors == { "educationalInstitution": ["Cette institution est inconnue"] } saved_bookings = EducationalBooking.query.join(Booking).filter( Booking.stockId == stock.id).all() assert len(saved_bookings) == 0
def test_should_send_email_on_educational_booking_creation(self): # Given stock = offers_factories.EducationalEventStockFactory( beginningDatetime=datetime.datetime(2021, 5, 15), offer__bookingEmail="*****@*****.**", ) educational_institution = educational_factories.EducationalInstitutionFactory( ) educational_factories.EducationalYearFactory( beginningDate=datetime.datetime(2020, 9, 1), expirationDate=datetime.datetime(2021, 8, 31)) educational_factories.EducationalYearFactory( beginningDate=datetime.datetime(2021, 9, 1), expirationDate=datetime.datetime(2022, 8, 31)) educational_redactor = educational_factories.EducationalRedactorFactory( email="*****@*****.**", firstName="Georges", lastName="Moustaki", ) redactor_informations = AuthenticatedInformation( email=educational_redactor.email, civility=educational_redactor.civility, firstname=educational_redactor.firstName, lastname=educational_redactor.lastName, uai=educational_institution.institutionId, ) # When educational_api.book_educational_offer( redactor_informations=redactor_informations, stock_id=stock.id, ) # Then assert len(mails_testing.outbox) == 1 sent_data = mails_testing.outbox[0].sent_data offer = stock.offer assert sent_data == { "FromEmail": "*****@*****.**", "MJ-TemplateID": 3174424, "MJ-TemplateLanguage": True, "To": "*****@*****.**", "Vars": { "departement": "75", "lien_offre_pcpro": f"http://*****:*****@example.com", "is_event": 1, }, }
def test_confirm_educational_booking_sends_email(self, db_session): # Given educational_redactor = educational_factories.EducationalRedactorFactory( email="*****@*****.**", firstName="Georges", lastName="Moustaki") educational_institution = educational_factories.EducationalInstitutionFactory( ) educational_year = educational_factories.EducationalYearFactory( adageId="1") educational_factories.EducationalDepositFactory( educationalInstitution=educational_institution, educationalYear=educational_year, amount=Decimal(1400.00), isFinal=True, ) booking = bookings_factories.EducationalBookingFactory( amount=Decimal(20.00), quantity=20, educationalBooking__educationalInstitution=educational_institution, educationalBooking__educationalYear=educational_year, educationalBooking__educationalRedactor=educational_redactor, status=BookingStatus.PENDING, stock__offer__bookingEmail="*****@*****.**", stock__beginningDatetime=datetime.datetime(2021, 5, 15), ) # When educational_api.confirm_educational_booking( booking.educationalBookingId) # Then assert len(mails_testing.outbox) == 1 sent_data = mails_testing.outbox[0].sent_data offer = booking.stock.offer assert sent_data == { "FromEmail": "*****@*****.**", "MJ-TemplateID": 3174413, "MJ-TemplateLanguage": True, "To": "*****@*****.**", "Vars": { "lien_offre_pcpro": f"http://*****:*****@example.com", "is_event": 1, }, }
def test_update_educational_institutions_deposits(self): # Given educational_year = factories.EducationalYearFactory( beginningDate=datetime(2020, 9, 1), expirationDate=datetime(2021, 8, 30), ) educational_institution1 = factories.EducationalInstitutionFactory( institutionId="0790032L") deposit1 = factories.EducationalDepositFactory( educationalInstitution=educational_institution1, educationalYear=educational_year, amount=500, isFinal=False, ) educational_institution2 = factories.EducationalInstitutionFactory( institutionId="1790032L") deposit2 = factories.EducationalDepositFactory( educationalInstitution=educational_institution2, educationalYear=educational_year, amount=600, isFinal=False, ) # When update_educational_institutions_deposits( "update_deposits.csv", "tests/scripts/educational/fixtures") # Then educational_institutions = models.EducationalInstitution.query.all() assert len(educational_institutions) == 2 educational_deposits = models.EducationalDeposit.query.all() assert len(educational_deposits) == 2 educational_deposit1 = models.EducationalDeposit.query.get(deposit1.id) assert educational_deposit1.isFinal is True assert educational_deposit1.amount == 6000 educational_deposit2 = models.EducationalDeposit.query.get(deposit2.id) assert educational_deposit2.isFinal is True assert educational_deposit2.amount == 9000
def test_data(self): stock = offer_factories.EducationalEventStockFactory( beginningDatetime=stock_date) educational_institution = educational_factories.EducationalInstitutionFactory( ) educational_factories.EducationalYearFactory( beginningDate=educational_year_dates["start"], expirationDate=educational_year_dates["end"]) educational_redactor = educational_factories.EducationalRedactorFactory( email="*****@*****.**") return (stock, educational_institution, educational_redactor)
def test_update_institutions_deposits_with_institution_deposit_missing_ceases_execution( self): # Given educational_year = factories.EducationalYearFactory( beginningDate=datetime(2020, 9, 1), expirationDate=datetime(2021, 8, 30), ) factories.EducationalInstitutionFactory(institutionId="4790032L") educational_institution2 = factories.EducationalInstitutionFactory( institutionId="1790032L") educational_deposit = factories.EducationalDepositFactory( educationalInstitution=educational_institution2, educationalYear=educational_year, amount=500, isFinal=False, ) # When _process_educational_csv([ { "UAICode": "4790032L", "depositAmount": 5000 }, { "UAICode": "1790032L", "depositAmount": 3000 }, ]) # Then educational_deposit = models.EducationalDeposit.query.filter( models.EducationalDeposit.educationalInstitutionId == educational_institution2.id, models.EducationalDeposit.educationalYearId == educational_year.adageId, ).one() assert educational_deposit.amount == Decimal(500)
def test_post_educational_booking_with_less_redactor_information( self, app): # Given stock = offer_factories.EducationalEventStockFactory( beginningDatetime=stock_date) educational_institution = educational_factories.EducationalInstitutionFactory( ) educational_year = educational_factories.EducationalYearFactory( beginningDate=educational_year_dates["start"], expirationDate=educational_year_dates["end"]) adage_jwt_fake_valid_token = _create_adage_valid_token_with_email( email="*****@*****.**", uai=educational_institution.institutionId, civility=None, firstname=None, lastname=None, ) test_client = TestClient(app.test_client()) test_client.auth_header = { "Authorization": f"Bearer {adage_jwt_fake_valid_token}" } # When response = test_client.post( "/adage-iframe/bookings", json={ "stockId": stock.id, }, ) # Then assert response.status_code == 200 booking = Booking.query.filter(Booking.stockId == stock.id).first() assert booking.educationalBookingId is not None assert booking.individualBookingId is None assert booking.stock.id == stock.id educational_booking = booking.educationalBooking assert educational_booking.educationalInstitution.institutionId == educational_institution.institutionId assert educational_booking.educationalYear.adageId == educational_year.adageId educational_redactor = educational_booking.educationalRedactor assert educational_redactor.civility == None assert educational_redactor.firstName == None assert educational_redactor.lastName == None assert educational_redactor.email == "*****@*****.**" assert response.json["bookingId"] == booking.id
def test_confirm_educational_booking_lock(self, app): educational_institution = educational_factories.EducationalInstitutionFactory( ) educational_year = educational_factories.EducationalYearFactory( adageId="1") deposit = educational_factories.EducationalDepositFactory( educationalInstitution=educational_institution, educationalYear=educational_year, amount=Decimal(1400.00), isFinal=True, ) bookings_factories.EducationalBookingFactory( amount=Decimal(20.00), quantity=20, educationalBooking__educationalInstitution=educational_institution, educationalBooking__educationalYear=educational_year, status=BookingStatus.CONFIRMED, ) booking = bookings_factories.EducationalBookingFactory( amount=Decimal(20.00), quantity=20, educationalBooking__educationalInstitution=educational_institution, educationalBooking__educationalYear=educational_year, status=BookingStatus.PENDING, ) # open a second connection on purpose and lock the deposit engine = create_engine(app.config["SQLALCHEMY_DATABASE_URI"]) with engine.connect() as connection: connection.execute( text( """SELECT * FROM educational_deposit WHERE id = :educational_deposit_id FOR UPDATE""" ), educational_deposit_id=deposit.id, ) with pytest.raises(sqlalchemy.exc.OperationalError): educational_api.confirm_educational_booking( booking.educationalBookingId) refreshed_booking = Booking.query.filter( Booking.id == booking.id).one() assert refreshed_booking.status == BookingStatus.PENDING
def test_should_create_educational_booking_on_requested_educational_offer( self): # Given stock = offers_factories.EducationalEventStockFactory( beginningDatetime=datetime.datetime(2021, 5, 15)) educational_institution = educational_factories.EducationalInstitutionFactory( ) educational_year = educational_factories.EducationalYearFactory( beginningDate=datetime.datetime(2020, 9, 1), expirationDate=datetime.datetime(2021, 8, 31)) educational_factories.EducationalYearFactory( beginningDate=datetime.datetime(2021, 9, 1), expirationDate=datetime.datetime(2022, 8, 31)) educational_redactor = educational_factories.EducationalRedactorFactory( email="*****@*****.**") redactor_informations = AuthenticatedInformation( email=educational_redactor.email, civility=educational_redactor.civility, firstname=educational_redactor.firstName, lastname=educational_redactor.lastName, uai=educational_institution.institutionId, ) # When returned_booking = educational_api.book_educational_offer( redactor_informations=redactor_informations, stock_id=stock.id, ) # Then saved_educational_booking = EducationalBooking.query.join( Booking).filter(Booking.stockId == stock.id).first() assert saved_educational_booking.booking.id == returned_booking.id assert saved_educational_booking.booking.stock.id == stock.id assert saved_educational_booking.booking.stock.dnBookedQuantity == 1 assert saved_educational_booking.confirmationLimitDate == stock.bookingLimitDatetime assert saved_educational_booking.educationalInstitution.institutionId == educational_institution.institutionId assert saved_educational_booking.educationalYear.adageId == educational_year.adageId assert saved_educational_booking.booking.status == BookingStatus.PENDING # Assert we do not create an extra educational redactor when exist assert EducationalRedactor.query.count() == 1
def test_stop_execution_when_csv_is_invalid(self): # Given educational_year = factories.EducationalYearFactory( beginningDate=datetime(2020, 9, 1), expirationDate=datetime(2021, 8, 30), ) educational_institution = factories.EducationalInstitutionFactory( institutionId="0790032L") educational_deposit = factories.EducationalDepositFactory( educationalInstitution=educational_institution, educationalYear=educational_year, amount=500, isFinal=False, ) # When update_educational_institutions_deposits( "invalid.csv", "tests/scripts/educational/fixtures") # Then deposit = models.EducationalDeposit.query.get(educational_deposit.id) assert deposit.amount == 500
def test_confirm_educational_booking(self, db_session): educational_institution = educational_factories.EducationalInstitutionFactory( ) educational_year = educational_factories.EducationalYearFactory( adageId="1") educational_factories.EducationalDepositFactory( educationalInstitution=educational_institution, educationalYear=educational_year, amount=Decimal(1400.00), isFinal=True, ) booking = bookings_factories.EducationalBookingFactory( amount=Decimal(20.00), quantity=20, educationalBooking__educationalInstitution=educational_institution, educationalBooking__educationalYear=educational_year, status=BookingStatus.PENDING, ) educational_api.confirm_educational_booking( booking.educationalBookingId) assert booking.status == BookingStatus.CONFIRMED
def test_update_institutions_deposits_with_educational_year_missing_ceases_execution( self): # Given educational_year = factories.EducationalYearFactory( beginningDate=datetime(2018, 9, 1), expirationDate=datetime(2019, 8, 30), ) educational_institution = factories.EducationalInstitutionFactory( institutionId="0790032L") educational_deposit = factories.EducationalDepositFactory( educationalInstitution=educational_institution, educationalYear=educational_year, amount=500, isFinal=False, ) # When update_educational_institutions_deposits( "update_deposits.csv", "tests/scripts/educational/fixtures") # Then deposit1 = models.EducationalDeposit.query.get(educational_deposit.id) assert deposit1.amount == 500 assert deposit1.isFinal == False
def test_should_not_create_educational_booking_when_educational_year_not_found( self): # Given date_before_education_year_beginning = datetime.datetime(2018, 9, 20) stock = offers_factories.EducationalEventStockFactory( beginningDatetime=date_before_education_year_beginning) educational_institution = educational_factories.EducationalInstitutionFactory( ) educational_factories.EducationalYearFactory() educational_redactor = educational_factories.EducationalRedactorFactory( email="*****@*****.**") redactor_informations = AuthenticatedInformation( email=educational_redactor.email, civility=educational_redactor.civility, firstname=educational_redactor.firstName, lastname=educational_redactor.lastName, uai=educational_institution.institutionId, ) # When with pytest.raises(exceptions.EducationalYearNotFound) as error: educational_api.book_educational_offer( redactor_informations=redactor_informations, stock_id=stock.id, ) # Then assert error.value.errors == { "educationalYear": [ "Aucune année scolaire correspondant à la réservation demandée n'a été trouvée" ] } saved_bookings = EducationalBooking.query.join(Booking).filter( Booking.stockId == stock.id).all() assert len(saved_bookings) == 0
def test_raises_insufficient_temporary_fund(self, db_session) -> None: # When educational_year = educational_factories.EducationalYearFactory( adageId="1") educational_institution = educational_factories.EducationalInstitutionFactory( ) educational_factories.EducationalDepositFactory( educationalYear=educational_year, educationalInstitution=educational_institution, amount=Decimal(1000.00), isFinal=False, ) booking = bookings_factories.EducationalBookingFactory( educationalBooking__educationalYear=educational_year, educationalBooking__educationalInstitution=educational_institution, amount=Decimal(900.00), quantity=1, status=BookingStatus.PENDING, ) # Then with pytest.raises(exceptions.InsufficientTemporaryFund): educational_api.confirm_educational_booking( booking.educationalBookingId)
def test_should_create_educational_redactor_when_it_does_not_exist(self): # Given stock = offers_factories.EducationalEventStockFactory( beginningDatetime=datetime.datetime(2021, 5, 15)) educational_institution = educational_factories.EducationalInstitutionFactory( ) educational_factories.EducationalYearFactory( beginningDate=datetime.datetime(2020, 9, 1), expirationDate=datetime.datetime(2021, 8, 31)) educational_factories.EducationalYearFactory( beginningDate=datetime.datetime(2021, 9, 1), expirationDate=datetime.datetime(2022, 8, 31)) redactor_informations = AuthenticatedInformation( email="*****@*****.**", civility="Mme", firstname="Project", lastname="Redactor", uai=educational_institution.institutionId, ) # When educational_api.book_educational_offer( redactor_informations=redactor_informations, stock_id=stock.id, ) # Then saved_educational_booking = EducationalBooking.query.join( Booking).filter(Booking.stockId == stock.id).first() assert saved_educational_booking.educationalRedactor.email == redactor_informations.email educational_redactor: EducationalRedactor = EducationalRedactor.query.one( ) assert educational_redactor.email == redactor_informations.email assert educational_redactor.firstName == "Project" assert educational_redactor.lastName == "Redactor" assert educational_redactor.civility == "Mme"
def create_industrial_educational_bookings() -> None: educational_current_year = educational_factories.EducationalYearFactory() educational_next_year = educational_factories.EducationalYearFactory() educational_institutions = [ educational_factories.EducationalInstitutionFactory( institutionId="0780032L"), educational_factories.EducationalInstitutionFactory( institutionId="0781839A"), educational_factories.EducationalInstitutionFactory( institutionId="0290047U"), educational_factories.EducationalInstitutionFactory( institutionId="0290198H"), educational_factories.EducationalInstitutionFactory( institutionId="0910620E"), educational_factories.EducationalInstitutionFactory( institutionId="0560071Y"), ] now = datetime.datetime.now(datetime.timezone.utc) stocks = [] venue = VenueFactory(name="Opéra Royal de Versailles", isPermanent=True) UserOffererFactory(validationToken=None, offerer=venue.managingOfferer) educational_redactor = educational_factories.EducationalRedactorFactory( email="*****@*****.**") user_offerer_reimbursements = UserOffererFactory( validationToken=None, user__email="*****@*****.**") venue_reimbursements = VenueFactory( name="Théâtre des potirons", isPermanent=True, managingOfferer=user_offerer_reimbursements.offerer) for stock_data in FAKE_STOCK_DATA: stocks.append( EducationalEventStockFactory( quantity=100, price=stock_data["price"], beginningDatetime=now + datetime.timedelta(days=stock_data["timedelta"]), offer__durationMinutes=60, offer__withdrawalDetails= "Récupération du ticket à l'adresse du lieu", offer__description= "Une description multi-lignes.\nOù il est notamment question du nombre d'élèves.\nNbr d'élèves max: 50", offer__name=stock_data["name"], offer__venue=venue, )) for stock in stocks: mediation = MediationFactory(offer=stock.offer, credit="Crédit photo") store_public_object_from_sandbox_assets("thumbs", mediation, mediation.offer.subcategoryId) next_year_stocks = [ EducationalEventStockFactory( quantity=100, price=1200, beginningDatetime=educational_next_year.beginningDate + datetime.timedelta(days=10), offer__durationMinutes=60, offer__withdrawalDetails= "Récupération du ticket à l'adresse du lieu", offer__description= "Une description multi-lignes.\nOù il est notamment question du nombre d'élèves.\nNbr d'élèves max: 50", offer__name= "Stage d'initiation à la photographie : prise en main de l'appareil-photo", offer__venue=venue, ), EducationalEventStockFactory( quantity=60, price=1400, beginningDatetime=educational_next_year.beginningDate + datetime.timedelta(days=15), offer__durationMinutes=60, offer__withdrawalDetails= "Récupération du ticket à l'adresse du lieu", offer__description= "Une description multi-lignes.\nOù il est notamment question du nombre d'élèves.\nNbr d'élèves max: 50", offer__name= "Explorer la nature au Parc Zoologique et Botanique de Mulhouse", offer__venue=venue, ), ] deposits = [] for educational_institution in educational_institutions: deposits.append( educational_factories.EducationalDepositFactory( educationalInstitution=educational_institution, educationalYear=educational_current_year, amount=20000, )) deposits.append( educational_factories.EducationalDepositFactory( educationalInstitution=educational_institution, educationalYear=educational_next_year, amount=25000, isFinal=False, )) for stock in stocks: for educational_institution in educational_institutions: EducationalBookingFactory( educationalBooking__educationalRedactor=educational_redactor, educationalBooking__educationalInstitution= educational_institution, educationalBooking__educationalYear=educational_current_year, educationalBooking__confirmationLimitDate=now + datetime.timedelta(days=10), cancellation_limit_date=now + datetime.timedelta(days=4), status=BookingStatus.PENDING, stock=stock, ) UsedEducationalBookingFactory( educationalBooking__educationalRedactor=educational_redactor, educationalBooking__educationalInstitution= educational_institution, educationalBooking__educationalYear=educational_current_year, educationalBooking__confirmationLimitDate=now - datetime.timedelta(days=20), cancellation_limit_date=now - datetime.timedelta(days=15), dateUsed=now - datetime.timedelta(8), status=BookingStatus.USED, stock=EducationalEventStockFactory( quantity=100, price=1200, beginningDatetime=now - datetime.timedelta(days=10), bookingLimitDatetime=now - datetime.timedelta(days=10), offer__venue=venue_reimbursements, ), ) for next_year_stock in next_year_stocks: for educational_institution in educational_institutions: EducationalBookingFactory( educationalBooking__educationalRedactor=educational_redactor, educationalBooking__educationalInstitution= educational_institution, educationalBooking__educationalYear=educational_next_year, educationalBooking__confirmationLimitDate=now + datetime.timedelta(days=30), status=BookingStatus.PENDING, stock=next_year_stock, )