def test_returns_404_if_api_key_cant_access_venue(mock_synchronize_stocks, app): offerer = offers_factories.OffererFactory(siren=123456789) offers_factories.VenueFactory(managingOfferer=offerer, id=3) offerer2 = offers_factories.OffererFactory(siren=123456780) ApiKeyFactory(offerer=offerer2) mock_synchronize_stocks.return_value = {} test_client = TestClient(app.test_client()) test_client.auth_header = { "Authorization": f"Bearer {DEFAULT_CLEAR_API_KEY}" } response1 = test_client.post( "/v2/venue/3/stocks", json={"stocks": [{ "ref": "123456789", "available": 4 }]}) response2 = test_client.post( "/v2/venue/123/stocks", json={"stocks": [{ "ref": "123456789", "available": 4 }]}) assert response1.status_code == 404 assert response2.status_code == 404 mock_synchronize_stocks.assert_not_called()
def test_should_returns_204_with_cancellation_allowed(self, client): # Given stock = offers_factories.EventStockFactory( offer__name="Chouette concert") booking = bookings_factories.IndividualBookingFactory(stock=stock) ApiKeyFactory(offerer=booking.offerer) # When response = client.patch( f"/v2/bookings/cancel/token/{booking.token}", headers={"Authorization": "Bearer " + DEFAULT_CLEAR_API_KEY}, ) # Then # cancellation can trigger more than one request to Batch assert len(push_testing.requests) >= 1 assert response.status_code == 204 updated_booking = Booking.query.one() assert updated_booking.isCancelled assert updated_booking.status is BookingStatus.CANCELLED assert push_testing.requests[-1] == { "group_id": "Cancel_booking", "message": { "body": """Ta réservation "Chouette concert" a été annulée par l'offreur.""", "title": "Réservation annulée", }, "user_ids": [booking.individualBooking.userId], }
def test_accepts_request_with_price(price, expected_price, app): ProviderFactory(name="Pass Culture API Stocks", localClass="PCAPIStocks") offerer = offers_factories.OffererFactory(siren=123456789) venue = offers_factories.VenueFactory(managingOfferer=offerer) offer_to_update = offers_factories.OfferFactory( product__idAtProviders="123456789", product__subcategoryId="LIVRE_PAPIER", idAtProviders=f"123456789@{venue.id}", product__extraData={"prix_livre": expected_price}, venue=venue, ) ApiKeyFactory(offerer=offerer) test_client = TestClient(app.test_client()) test_client.auth_header = { "Authorization": f"Bearer {DEFAULT_CLEAR_API_KEY}" } response = test_client.post(f"/v2/venue/{venue.id}/stocks", json={ "stocks": [{ "ref": "123456789", "available": 4, "price": price }] }) assert response.status_code == 204 assert offer_to_update.stocks[0].price == expected_price
def test_delete_api_key_not_allowed(client): user_offerer = UserOffererFactory() api_key = ApiKeyFactory() client.with_session_auth(user_offerer.user.email) response = client.delete(f"/offerers/api_keys/{api_key.prefix}") assert response.status_code == 403
def create_industrial_pro_users_api_keys(offerers_by_name): """Create api_keys with shape : {env}_{offererId}_clearSecret{offererId} ex: 'testing_12_clearSecret12'""" logger.info("create_industrial_pro_users_api_keys") for offerer in offerers_by_name.items(): ApiKeyFactory( offerer=offerer[1], prefix=f"{settings.ENV}_{offerer[1].id}", hash_secret=f"clearSecret{offerer[1].id}" ) logger.info("created %d offerers with api key", len(offerers_by_name))
def test_when_api_key_is_provided_and_rights_and_regular_offer(self, client): # Given booking = bookings_factories.IndividualBookingFactory() ApiKeyFactory(offerer=booking.offerer, prefix="test_prefix") # When url = f"/v2/bookings/token/{booking.token}" response = client.get(url, headers={"Authorization": "Bearer test_prefix_clearSecret"}) # Then assert response.status_code == 200
def test_maximal_api_key_reached(client): user_offerer = UserOffererFactory() for i in range(5): ApiKeyFactory(prefix=f"prefix_{i}", offerer=user_offerer.offerer) client.with_session_auth(user_offerer.user.email) response = client.post(f"/offerers/{humanize(user_offerer.offerer.id)}/api_keys") assert response.status_code == 400 assert response.json["api_key_count_max"] == "Le nombre de clés maximal a été atteint" assert ApiKey.query.count() == 5
def test_should_returns_204_with_lowercase_token(self, client): # Given booking = bookings_factories.IndividualBookingFactory() ApiKeyFactory(offerer=booking.offerer) # When response = client.patch( f"/v2/bookings/cancel/token/{booking.token.lower()}", headers={"Authorization": "Bearer " + DEFAULT_CLEAR_API_KEY}, ) assert response.status_code == 204 booking = Booking.query.one() assert booking.isCancelled assert booking.status is BookingStatus.CANCELLED
def test_when_given_api_key_not_related_to_booking_offerer(self, client): # Given booking = bookings_factories.IndividualBookingFactory() ApiKeyFactory() # another offerer's API key # When auth = "Bearer development_prefix_clearSecret" url = f"/v2/bookings/token/{booking.token}" response = client.get(url, headers={"Authorization": auth}) # Then assert response.status_code == 403 assert response.json["user"] == [ "Vous n’avez pas les droits suffisants pour valider cette contremarque car cette réservation n'a pas été faite sur une de vos offres, ou que votre rattachement à la structure est encore en cours de validation" ]
def when_the_api_key_is_not_linked_to_the_right_offerer(self, client): # Given booking = bookings_factories.BookingFactory() ApiKeyFactory() # another offerer's API key # When response = client.patch( f"/v2/bookings/cancel/token/{booking.token}", headers={"Authorization": "Bearer " + DEFAULT_CLEAR_API_KEY}, ) # Then assert response.status_code == 403 assert response.json["user"] == [ "Vous n'avez pas les droits suffisants pour annuler cette réservation." ]
def test_when_the_api_key_is_not_linked_to_the_right_offerer( self, client): # Given booking = bookings_factories.BookingFactory() ApiKeyFactory() # another offerer's API key # When wrong_auth = f"Bearer {DEFAULT_CLEAR_API_KEY}" url = f"/v2/bookings/keep/token/{booking.token}" response = client.patch(url, headers={"Authorization": wrong_auth}) # Then assert response.status_code == 403 assert response.json["user"] == [ "Vous n’avez pas les droits suffisants pour valider cette contremarque car cette réservation n'a pas été faite sur une de vos offres, ou que votre rattachement à la structure est encore en cours de validation" ]
def test_returns_comprehensive_errors(mock_synchronize_stocks, app): ApiKeyFactory() mock_synchronize_stocks.return_value = {} test_client = TestClient(app.test_client()) test_client.auth_header = { "Authorization": f"Bearer {DEFAULT_CLEAR_API_KEY}" } response1 = test_client.post("/v2/venue/3/stocks", json={}) response2 = test_client.post( "/v2/venue/3/stocks", json={ "stocks": [ { "ref": "123456789" }, { "wrong_key": "123456789" }, { "ref": "1234567890", "available": "abc" }, { "ref": "12345678901", "available": -3 }, ] }, ) assert response1.status_code == 400 assert response1.json["stocks"] == ["Ce champ est obligatoire"] assert response2.status_code == 400 assert response2.json["stocks.0.available"] == ["Ce champ est obligatoire"] assert response2.json["stocks.1.available"] == ["Ce champ est obligatoire"] assert response2.json["stocks.1.ref"] == ["Ce champ est obligatoire"] assert response2.json["stocks.2.available"] == [ "Saisissez un nombre valide" ] assert response2.json["stocks.3.available"] == [ "Saisissez un nombre supérieur ou égal à 0" ] mock_synchronize_stocks.assert_not_called()
def test_when_api_key_is_provided_and_rights_and_regular_offer( self, client): booking = bookings_factories.BookingFactory(token="ABCDEF") ApiKeyFactory(offerer=booking.offerer) url = f"/v2/bookings/use/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 booking.isUsed assert booking.status == BookingStatus.USED
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_generate_and_save_api_key_for_offerer(): # Given siren_unknown = "291893841948" offerer_1_having_api_key = offers_factories.OffererFactory() ApiKeyFactory(offerer=offerer_1_having_api_key) offerer_2_needing_api_key = offers_factories.OffererFactory() # When generate_and_save_api_key_for_offerer([ siren_unknown, offerer_1_having_api_key.siren, offerer_2_needing_api_key.siren ]) # Then assert ApiKey.query.count() == 2 offerers = {k.offerer for k in ApiKey.query.all()} assert offerers == {offerer_1_having_api_key, offerer_2_needing_api_key}
def test_when_api_key_is_provided_and_booking_has_been_cancelled_already( self, client): # Given booking = bookings_factories.CancelledBookingFactory() ApiKeyFactory(offerer=booking.offerer) # When url = f"/v2/bookings/use/token/{booking.token}" auth = "Bearer development_prefix_clearSecret" response = client.patch(url, headers={"Authorization": auth}) # Then assert response.status_code == 403 assert response.json["booking"] == [ "Cette réservation a été annulée" ] booking = Booking.query.get(booking.id) assert not booking.isUsed assert booking.status is not BookingStatus.USED
def test_when_api_key_is_provided_and_booking_has_been_cancelled_already( self, client): # Given booking = bookings_factories.CancelledBookingFactory() ApiKeyFactory(offerer=booking.offerer) # When url = f"/v2/bookings/keep/token/{booking.token}" auth = f"Bearer {DEFAULT_CLEAR_API_KEY}" response = client.patch(url, headers={"Authorization": auth}) # Then booking = Booking.query.get(booking.id) assert response.status_code == 403 assert response.json["booking"] == [ "Cette réservation a été annulée" ] assert not booking.isUsed assert booking.status is BookingStatus.CANCELLED
def test_accepts_request(app): ProviderFactory(name="Pass Culture API Stocks", localClass="PCAPIStocks") offerer = offers_factories.OffererFactory(siren=123456789) venue = offers_factories.VenueFactory(managingOfferer=offerer, id=3) offer_to_update = offers_factories.OfferFactory( product__idAtProviders="123456789", product__subcategoryId="LIVRE_PAPIER", idAtProviders=f"123456789@{venue.id}", venue=venue, ) ApiKeyFactory(offerer=offerer) test_client = TestClient(app.test_client()) test_client.auth_header = { "Authorization": f"Bearer {DEFAULT_CLEAR_API_KEY}" } response = test_client.post( f"/v2/venue/{venue.id}/stocks", json={ "stocks": [ { "ref": "123456789", "available": 4, "price": 30 }, { "ref": "1234567890", "available": 0, "price": 10 }, ] }, ) assert response.status_code == 204 assert len(offer_to_update.stocks) == 1 assert offer_to_update.stocks[0].quantity == 4 assert offer_to_update.stocks[0].price == 30
def test_when_user_has_rights_on_offerer(self, app): pro = users_factories.ProFactory() offerer = offers_factories.OffererFactory() offers_factories.UserOffererFactory(user=pro, offerer=offerer) venue_1 = offers_factories.VenueFactory( managingOfferer=offerer, withdrawalDetails="Venue withdrawal details") offers_factories.OfferFactory(venue=venue_1) venue_2 = offers_factories.VenueFactory( managingOfferer=offerer, withdrawalDetails="Other venue withdrawal details") offers_factories.VenueFactory( managingOfferer=offerer, withdrawalDetails="More venue withdrawal details") ApiKeyFactory(offerer=offerer, prefix="testenv_prefix") ApiKeyFactory(offerer=offerer, prefix="testenv_prefix2") offers_factories.BankInformationFactory(venue=venue_1, applicationId=2, status="REJECTED") offers_factories.BankInformationFactory(venue=venue_1, applicationId=3) offers_factories.BankInformationFactory(venue=venue_2, applicationId=4) client = TestClient(app.test_client()).with_session_auth(pro.email) n_queries = ( testing.AUTHENTICATION_QUERIES + 1 # check_user_has_access_to_offerer + 1 # Offerer api_key prefix + 1 # Offerer hasDigitalVenueAtLeastOneOffer + 1 # Offerer BankInformation + 1 # Offerer hasMissingBankInformation ) with testing.assert_num_queries(n_queries): response = client.get(f"/offerers/{humanize(offerer.id)}") expected_serialized_offerer = { "address": offerer.address, "apiKey": { "maxAllowed": 5, "prefixes": ["testenv_prefix", "testenv_prefix2"] }, "bic": None, "iban": None, "city": offerer.city, "dateCreated": format_into_utc_date(offerer.dateCreated), "dateModifiedAtLastProvider": format_into_utc_date(offerer.dateModifiedAtLastProvider), "demarchesSimplifieesApplicationId": None, "hasDigitalVenueAtLeastOneOffer": False, "fieldsUpdated": offerer.fieldsUpdated, "hasMissingBankInformation": True, "id": humanize(offerer.id), "idAtProviders": offerer.idAtProviders, "isValidated": offerer.isValidated, "lastProviderId": offerer.lastProviderId, "managedVenues": [{ "audioDisabilityCompliant": False, "address": offererVenue.address, "bookingEmail": offererVenue.bookingEmail, "city": offererVenue.city, "comment": offererVenue.comment, "departementCode": offererVenue.departementCode, "id": humanize(offererVenue.id), "isValidated": offererVenue.isValidated, "isVirtual": offererVenue.isVirtual, "managingOffererId": humanize(offererVenue.managingOffererId), "mentalDisabilityCompliant": False, "motorDisabilityCompliant": False, "name": offererVenue.name, "postalCode": offererVenue.postalCode, "publicName": offererVenue.publicName, "venueLabelId": humanize(offererVenue.venueLabelId), "venueTypeId": humanize(offererVenue.venueTypeId), "visualDisabilityCompliant": False, "withdrawalDetails": offererVenue.withdrawalDetails, } for offererVenue in offerer.managedVenues], "name": offerer.name, "postalCode": offerer.postalCode, "siren": offerer.siren, } assert response.status_code == 200 assert response.json == expected_serialized_offerer
def test_authenticated_with_api_key_but_token_not_found(self, client): ApiKeyFactory(prefix="test_prefix") response = client.get("/v2/bookings/token/12345", headers={"Authorization": "Bearer test_prefix_clearSecret"}) assert response.status_code == 404 assert response.json["global"] == ["Cette contremarque n'a pas été trouvée"]