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_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_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_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 wrapper(*args, **kwargs): mandatory_authorization_type = "Bearer " authorization_header = request.headers.get("Authorization") if authorization_header and mandatory_authorization_type in authorization_header: adage_jwt = authorization_header.replace( mandatory_authorization_type, "") try: adage_jwt_decoded = user_utils.decode_jwt_token_rs256( adage_jwt) except InvalidSignatureError as invalid_signature_error: logger.error("Signature of adage jwt cannot be verified", extra={"error": invalid_signature_error}) raise ForbiddenError({"Authorization": "Unrecognized token"}) except ExpiredSignatureError as expired_signature_error: logger.warning("Token has expired", extra={"error": expired_signature_error}) raise InvalidTokenError("Token expired") if not adage_jwt_decoded.get("exp"): logger.warning("Token does not contain an expiration date") raise InvalidTokenError("No expiration date provided") authenticated_information = AuthenticatedInformation( civility=adage_jwt_decoded.get("civilite"), lastname=adage_jwt_decoded.get("nom"), firstname=adage_jwt_decoded.get("prenom"), email=adage_jwt_decoded.get("mail"), uai=adage_jwt_decoded.get("uai"), ) kwargs["authenticated_information"] = authenticated_information return route_function(*args, **kwargs) raise ForbiddenError({"Authorization": "Unrecognized token"})
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_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_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_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 book_educational_offer( body: BookEducationalOfferRequest, authenticated_information: AuthenticatedInformation, ) -> BookEducationalOfferResponse: try: booking = educational_api.book_educational_offer( redactor_informations= get_redactor_information_from_adage_authentication( authenticated_information), stock_id=body.stockId, ) except AdageException as exception: logger.info( "Could not book offer: adage api call failed", extra={ "redactor_email": authenticated_information.email, "status_code": str(exception.status_code), "response_text": exception.response_text, }, ) raise ApiErrors( { "global": "La récupération des informations du rédacteur de projet via Adage a échoué" }, status_code=500, ) except MissingRequiredRedactorInformation: logger.info( "Could not book offer: missing information in adage jwt", extra={ "authenticated_information": authenticated_information.dict() }, ) raise ApiErrors( { "authorization": "Des informations sur le rédacteur de projet, et nécessaires à la préréservation, sont manquantes" }, status_code=403, ) except InstitutionalProjectRedactorNotFoundException: logger.info( "Could not book offer: redactor does not exist in Adage", extra={ "redactor_email": authenticated_information.email, }, ) raise ApiErrors( {"global": "Le rédacteur de projet n'existe pas dans Adage"}, status_code=404) except offers_exceptions.StockDoesNotExist: logger.info("Could not book offer: stock does not exist", extra={"stock_id": body.stockId}) raise ApiErrors({"stock": "Stock introuvable"}, status_code=400) except exceptions.StockNotBookable: logger.info("Could not book offer: stock is not bookable", extra={"stock_id": body.stockId}) raise ApiErrors( {"stock": "Cette offre n'est pas disponible à la réservation"}) except exceptions.OfferIsNotEvent: logger.info("Could not book offer: offer is not an event", extra={"stock_id": body.stockId}) raise ApiErrors({"offer": "L'offre n'est pas un évènement"}) except exceptions.OfferIsNotEducational: logger.info("Could not book offer: offer is not educational", extra={"stock_id": body.stockId}) raise ApiErrors( {"offer": "L'offre n'est pas une offre éducationnelle"}) except exceptions.EducationalInstitutionUnknown: logger.info("Could not book offer: educational institution not found", extra={"uai_code": authenticated_information.uai}) raise ApiErrors({ "educationalInstitution": "Cet établissement n'est pas éligible au pass Culture." }) except exceptions.EducationalYearNotFound: logger.info( "Could not book offer: associated educational year not found", extra={"stock_id": body.stockId}) raise ApiErrors({ "educationalYear": "Aucune année scolaire ne correspond à cet évènement" }) return BookEducationalOfferResponse(bookingId=booking.id)