def test_should_mark_products_as_compatible_via_isbn(self): product = ProductFactory(id=1, extraData={"isbn": "ABCDEFG"}, isGcuCompatible=False) product_1 = ProductFactory(id=2, extraData={"isbn": "HIJKLMN"}, isGcuCompatible=False) product_2 = ProductFactory(id=3, extraData={"isbn": "VWXYZ"}, isGcuCompatible=False) product_3 = ProductFactory(id=4, extraData={"isbn": "HFGDS"}, isGcuCompatible=False) isbns_list = ["ABCDEFG", "HIJKLMN", "OPQRSTU", "HFGDS"] queries = 2 # update; commit with assert_num_queries(queries): bulk_update_is_gcu_compatible_via_isbns(isbns_list, 4, is_compatible=True) assert product.isGcuCompatible assert product_1.isGcuCompatible assert not product_2.isGcuCompatible assert product_3.isGcuCompatible
def test_get_subcategories(self, app): with assert_num_queries(0): response = TestClient( app.test_client()).get("/native/v1/subcategories") assert response.status_code == 200 assert list(response.json.keys()) == [ "subcategories", "searchGroups", "homepageLabels" ] assert len(response.json["subcategories"]) == len( subcategories.ALL_SUBCATEGORIES) assert len(response.json["searchGroups"]) == len( subcategories.SearchGroups) assert len(response.json["homepageLabels"]) == len( subcategories.HomepageLabels) assert all( list(subcategory_dict.keys()) == [ "id", "categoryId", "appLabel", "searchGroupName", "homepageLabelName", "isEvent", "onlineOfflinePlatform", ] for subcategory_dict in response.json["subcategories"]) assert all( list(search_group_dict.keys()) == [ "name", "value", ] for search_group_dict in response.json["searchGroups"]) assert all( list(homepage_label_dict.keys()) == [ "name", "value", ] for homepage_label_dict in response.json["homepageLabels"])
def test_should_mark_offers_and_products_as_incompatible_via_isbn( self, mocked_unindex_offer_ids): # Given product = ProductFactory(id=1, extraData={"isbn": "ABCDEFG"}) product_1 = ProductFactory(id=2, extraData={"isbn": "HIJKLMN"}) product_2 = ProductFactory(id=3, extraData={"isbn": "VWXYZ"}) product_3 = ProductFactory(id=4, extraData={"isbn": "HFGDS"}) offer = OfferFactory(id=1, product=product) offer_1 = OfferFactory(id=2, product=product_1) offer_2 = OfferFactory(id=3, product=product_2) offer_3 = OfferFactory(id=4, product=product_3) isbns_list = ["ABCDEFG", "HIJKLMN", "OPQRSTU", "HFGDS"] queries = 1 # update product queries += 1 # select offer queries += 2 # update offer; commit queries *= 2 # two batches # When with assert_num_queries(queries): bulk_update_is_gcu_compatible_via_isbns(isbns_list, 2, is_compatible=False) # Then assert not product.isGcuCompatible assert not product_1.isGcuCompatible assert product_2.isGcuCompatible assert not product_3.isGcuCompatible assert not offer.isActive assert not offer_1.isActive assert offer_2.isActive assert not offer_3.isActive mocked_unindex_offer_ids.assert_has_calls([call([1, 2]), call([4])])
def test_report_offer_with_custom_reason(self, app): user, test_client = create_user_and_test_client(app) offer = OfferFactory() # expected queries: # * select offer # * get user # * insert report # * release savepoint # # * reload user # * select offer # * insert email into db # * release savepoint with assert_num_queries(8): data = {"reason": "OTHER", "customReason": "saynul"} response = test_client.post(f"/native/v1/offer/{offer.id}/report", json=data) assert response.status_code == 204 assert OfferReport.query.count() == 1 report = OfferReport.query.first() assert report.user == user assert report.offer == offer assert len(mails_testing.outbox) == 1 email = mails_testing.outbox[0] assert email.sent_data["To"] == "*****@*****.**" assert email.sent_data["Vars"]["user_id"] == user.id assert email.sent_data["Vars"]["offer_id"] == offer.id assert "saynul" in email.sent_data["Vars"]["reason"] assert "offer_url" in email.sent_data["Vars"]
def test_send_offer_link_notification(self, app): """ Test that a push notification to the user is send with a link to the offer. """ # offer.id must be used before the assert_num_queries context manager # because it triggers a SQL query. offer = OfferFactory() offer_id = offer.id user, test_client = create_user_and_test_client(app) # expected queries: # * get user # * get offer # * get FeatureToggle WEBAPP_V2_ENABLED (to build URL) with assert_num_queries(3): response = test_client.post( f"/native/v1/send_offer_link_by_push/{offer_id}") assert response.status_code == 204 assert len(notifications_testing.requests) == 1 notification = notifications_testing.requests[0] assert notification["user_ids"] == [user.id] assert offer.name in notification["message"]["title"] assert "deeplink" in notification
def when_requested_booking_period_dates_are_iso_format(self, app): booking_date = datetime(2020, 8, 12, 20, 00, tzinfo=timezone.utc) booking_period_beginning_date_iso = "2020-08-10" booking_period_ending_date_iso = "2020-08-12" booking = bookings_factories.BookingFactory(dateCreated=booking_date, token="AAAAAA") bookings_factories.BookingFactory(token="BBBBBB") pro_user = users_factories.ProFactory(email="*****@*****.**") offers_factories.UserOffererFactory(user=pro_user, offerer=booking.offerer) client = TestClient(app.test_client()).with_session_auth(pro_user.email) with assert_num_queries( testing.AUTHENTICATION_QUERIES + 2 + 1 # TODO: query for feature flag, to be removed when IMPROVE_BOOKINGS_PERF is definitely adopted ): response = client.get( "/bookings/pro?bookingPeriodBeginningDate=%s&bookingPeriodEndingDate=%s" % (booking_period_beginning_date_iso, booking_period_ending_date_iso) ) assert response.status_code == 200 assert len(response.json["bookings_recap"]) == 1 assert response.json["bookings_recap"][0]["booking_date"] == datetime.isoformat( utc_datetime_to_department_timezone(booking.dateCreated, booking.venue.departementCode) ) assert response.json["page"] == 1 assert response.json["pages"] == 1 assert response.json["total"] == 1
def test_cancel_booking(self): stock = offers_factories.StockFactory(offer__bookingEmail="*****@*****.**") booking = booking_factories.IndividualBookingFactory.create_batch(20, stock=stock)[0] queries = 1 # select booking queries += 1 # select stock for update queries += 1 # refresh booking queries += 3 # update stock ; update booking ; release savepoint queries += 8 # (update batch attributes): select booking ; individualBooking ; user ; user.bookings ; deposit ; user_offerer ; favorites ; stock queries += 1 # select offer queries += 2 # insert email ; release savepoint queries += 4 # (TODO: optimize) select booking ; stock ; offer ; user queries += 1 # select bookings of same stock with users joinedloaded to avoid N+1 requests queries += 2 # select venue ; offerer queries += 2 # insert email ; release savepoint with assert_num_queries(queries): api.cancel_booking_by_beneficiary(booking.individualBooking.user, booking) # cancellation can trigger more than one request to Batch assert len(push_testing.requests) >= 1 assert booking.isCancelled assert booking.status is BookingStatus.CANCELLED assert booking.cancellationReason == BookingCancellationReasons.BENEFICIARY assert len(mails_testing.outbox) == 2 email_data1 = mails_testing.outbox[0].sent_data assert email_data1["Mj-TemplateID"] == 1091464 # to beneficiary email_data2 = mails_testing.outbox[1].sent_data assert email_data2["MJ-TemplateID"] == 780015 # to offerer
def test_get_user_attributes(): user = BeneficiaryGrant18Factory(deposit__version=1) offer = OfferFactory(product__id=list(TRACKED_PRODUCT_IDS.keys())[0]) b1 = IndividualBookingFactory(individualBooking__user=user, amount=10, stock__offer=offer) b2 = IndividualBookingFactory(individualBooking__user=user, amount=10, dateUsed=datetime(2021, 5, 6), stock__offer=offer) IndividualBookingFactory( individualBooking__user=user, amount=100, status=BookingStatus.CANCELLED) # should be ignored last_date_created = max(booking.dateCreated for booking in [b1, b2]) n_query_get_user = 1 n_query_get_bookings = 1 n_query_get_deposit = 1 n_query_is_pro = 1 n_query_get_last_favorite = 1 with assert_num_queries(n_query_get_user + n_query_get_bookings + n_query_get_deposit + n_query_is_pro + n_query_get_last_favorite): attributes = get_user_attributes(user) assert attributes == UserAttributes( domains_credit=DomainsCredit( all=Credit(initial=Decimal("500"), remaining=Decimal("480.00")), digital=Credit(initial=Decimal("200"), remaining=Decimal("200")), physical=Credit(initial=200, remaining=Decimal("180.00")), ), booking_categories=["FILM"], date_created=user.dateCreated, date_of_birth=user.dateOfBirth, departement_code="75", deposit_expiration_date=user.deposit_expiration_date, eligibility=EligibilityType.AGE18, first_name="Jeanne", is_beneficiary=True, is_pro=False, last_booking_date=last_date_created, last_name="Doux", marketing_push_subscription=True, postal_code=None, products_use_date={"product_brut_x_use": datetime(2021, 5, 6, 0, 0)}, booking_count=2, booking_subcategories=["SUPPORT_PHYSIQUE_FILM"], deposit_activation_date=user.deposit_activation_date, has_completed_id_check=None, user_id=user.id, is_eligible=True, is_email_validated=True, last_favorite_creation_date=None, last_visit_date=None, marketing_email_subscription=True, )
def test_is_active_query_count_inside_request_context(self): feature = Feature.query.filter_by( name=FeatureToggle.WEBAPP_SIGNUP.name).first() feature.isActive = True repository.save(feature) with assert_num_queries(1): FeatureToggle.WEBAPP_SIGNUP.is_active() FeatureToggle.WEBAPP_SIGNUP.is_active() FeatureToggle.WEBAPP_SIGNUP.is_active()
def test_get_expired_offer(self, app): stock = EventStockFactory(beginningDatetime=datetime.utcnow() - timedelta(days=1)) offer_id = stock.offer.id with assert_num_queries(1): response = TestClient( app.test_client()).get(f"/native/v1/offer/{offer_id}") assert response.json["isExpired"]
def test_update_external_pro_user(): user = ProFactory() n_query_get_user = 1 n_query_is_pro = 1 with assert_num_queries(n_query_get_user + n_query_is_pro): update_external_user(user) assert len(batch_testing.requests) == 0 assert len(sendinblue_testing.sendinblue_requests) == 1
def test_send_offer_webapp_link_by_email_not_found(self, app): _, test_client = create_user_and_test_client(app) # expected queries: # * get User # * try to find Offer with assert_num_queries(2): response = test_client.post( "/native/v1/send_offer_webapp_link_by_email/98765432123456789") assert response.status_code == 404 assert not mails_testing.outbox
def test_report_offer_unknown_reason(self, app, client): offer = OfferFactory() offer_id = offer.id with assert_num_queries(0): data = {"reason": "UNKNOWN"} response = client.post(f"/native/v1/offer/{offer_id}/report", json=data) assert response.status_code == 400 assert response.json["reason"] == ["unknown reason"] assert OfferReport.query.count() == 0 # no new report assert not mails_testing.outbox
def test_send_offer_link_notification_not_found(self, app): """Test that no push notification is sent when offer is not found""" _, test_client = create_user_and_test_client(app) # expected queries: # * get user # * search for offer with assert_num_queries(2): response = test_client.post( "/native/v1/send_offer_link_by_push/9999999999") assert response.status_code == 404 assert len(notifications_testing.requests) == 0
def test_check_user_has_access_to_offerer(self, app): pro = users_factories.UserFactory() offerer = offers_factories.OffererFactory() offers_factories.UserOffererFactory(user=pro, offerer=offerer) # fmt: off n_queries = ( 1 # select Offerer + 1 # select User + 1 # select UserOfferer ) # fmt: on with assert_num_queries(n_queries): check_user_has_access_to_offerer(pro, offerer.id)
def test_list_beneficiaries(self, mocked_validate_csrf_token, app): users_factories.AdminFactory(email="*****@*****.**") users_factories.BeneficiaryGrant18Factory.create_batch(3) client = TestClient( app.test_client()).with_session_auth("*****@*****.**") n_queries = testing.AUTHENTICATION_QUERIES n_queries += 1 # select COUNT n_queries += 1 # select users with testing.assert_num_queries(n_queries): response = client.get("/pc/back-office/beneficiary_users") assert response.status_code == 200
def test_no_modifications(self, app) -> None: # given venue = offers_factories.VenueFactory() # when venue_data = { "departementCode": venue.departementCode, "city": venue.city, "motorDisabilityCompliant": venue.motorDisabilityCompliant, } # nothing has changed => nothing to save nor update with assert_num_queries(0): offerers_api.update_venue(venue, **venue_data)
def test_records_new_payment_lines_in_database(self): # Given cutoff = datetime.datetime.now() before_cutoff = cutoff - datetime.timedelta(days=1) beneficiary = users_factories.BeneficiaryGrant18Factory( email="*****@*****.**") offerer = offers_factories.OffererFactory() offer = offers_factories.ThingOfferFactory( venue__managingOfferer=offerer) paying_stock = offers_factories.ThingStockFactory(offer=offer) free_stock = offers_factories.ThingStockFactory(offer=offer, price=0) bookings_factories.UsedBookingFactory(user=beneficiary, stock=paying_stock, dateUsed=before_cutoff) bookings_factories.UsedBookingFactory(user=beneficiary, stock=paying_stock, dateUsed=before_cutoff) bookings_factories.UsedBookingFactory(user=beneficiary, stock=free_stock, dateUsed=before_cutoff) bookings_factories.UsedBookingFactory(user=beneficiary, stock=free_stock, dateUsed=before_cutoff) bookings_factories.UsedBookingFactory(user=beneficiary, stock=paying_stock, dateUsed=cutoff) payment_message = payments_factories.PaymentMessageFactory( name="ABCD123") payments_factories.PaymentFactory(paymentMessage=payment_message) initial_payment_count = Payment.query.count() # When n_queries = 1 # get_venue_ids_to_reimburse() n_queries += 1 # fetch custom reimbursement rules n_queries += 1 # find_bookings_eligible_for_payment_for_venue() n_queries += 1 # insert payments n_queries += 1 # release savepoint (commit) n_queries += 1 # insert PENDING payment statuses n_queries += 1 # release savepoint (commit) n_queries += 1 # insert NOT_PROCESSABLE payment statuses n_queries += 1 # release savepoint (commit) with assert_num_queries(n_queries): generate_new_payments(cutoff, batch_date=datetime.datetime.now()) # Then assert Payment.query.count() - initial_payment_count == 2
def test_report_offer_custom_reason_too_long(self, app, client): offer = OfferFactory() offer_id = offer.id with assert_num_queries(0): data = {"reason": "OTHER", "customReason": "a" * 513} response = client.post(f"/native/v1/offer/{offer_id}/report", json=data) assert response.status_code == 400 assert response.json["customReason"] == [ "custom reason is too long" ] assert OfferReport.query.count() == 0 # no new report assert not mails_testing.outbox
def test_is_active_query_count_outside_request_context(self, app): feature = Feature.query.filter_by( name=FeatureToggle.WEBAPP_SIGNUP.name).first() feature.isActive = True repository.save(feature) context = flask._request_ctx_stack.pop() # we don't cache yet outside the scope of a request so it'll be 3 DB queries try: with assert_num_queries(3): FeatureToggle.WEBAPP_SIGNUP.is_active() FeatureToggle.WEBAPP_SIGNUP.is_active() FeatureToggle.WEBAPP_SIGNUP.is_active() finally: flask._request_ctx_stack.push(context)
def test_update_external_user(): user = BeneficiaryGrant18Factory() IndividualBookingFactory(individualBooking__user=user) n_query_get_user = 1 n_query_get_bookings = 1 n_query_get_deposit = 1 n_query_is_pro = 1 n_query_get_last_favorite = 1 with assert_num_queries(n_query_get_user + n_query_get_bookings + n_query_get_deposit + n_query_is_pro + n_query_get_last_favorite): update_external_user(user) assert len(batch_testing.requests) == 1 assert len(sendinblue_testing.sendinblue_requests) == 1
def test_check_number_of_sql_queries(): offer = offers_factories.OfferFactory() # FIXME (dbaty, 2021-07-05): we should put these `joinedload` in a # function, and call that function from `_reindex_offer_ids()` # where we fetch offers. offer = (offers_models.Offer.query.options( joinedload(offers_models.Offer.venue).joinedload( offerers_models.Venue.managingOfferer)).options( joinedload(offers_models.Offer.criteria)).options( joinedload(offers_models.Offer.mediations)).options( joinedload(offers_models.Offer.product)).options( joinedload(offers_models.Offer.stocks)).one()) # Make sure that the JOINs above are enough to avoid any extra SQL # query below where serializing an offer. with assert_num_queries(0): appsearch.AppSearchBackend().serialize_offer(offer)
def test_get_digital_offer_without_available_activation_code(self, app): # given stock = StockWithActivationCodesFactory( activationCodes__expirationDate=datetime(2000, 1, 1)) offer_id = stock.offer.id queries = 1 # select offer queries += 1 # get available_activation_code for each offer.stocks # when with assert_num_queries(2): response = TestClient( app.test_client()).get(f"/native/v1/offer/{offer_id}") # then assert response.status_code == 200 assert response.json["stocks"][0]["activationCode"] is None
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_report_offer_twice(self, app): user, test_client = create_user_and_test_client(app) offer = OfferFactory() OfferReportFactory(user=user, offer=offer) # expected queries: # * get user # * get offer # * rollback with assert_num_queries(3): response = test_client.post(f"/native/v1/offer/{offer.id}/report", json={"reason": "PRICE_TOO_HIGH"}) assert response.status_code == 400 assert response.json["code"] == "OFFER_ALREADY_REPORTED" assert OfferReport.query.count() == 1 # no new report assert not mails_testing.outbox
def test_send_offer_webapp_link_by_email(self, app): offer_id = OfferFactory().id user, test_client = create_user_and_test_client(app) # expected queries: # * get User # * find Offer # * find FeatureToggle WEBAPP_V2_ENABLED (to build URL) # * save email to DB (testing backend) # * release savepoint after saving email with assert_num_queries(5): response = test_client.post( f"/native/v1/send_offer_webapp_link_by_email/{offer_id}") assert response.status_code == 204 assert len(mails_testing.outbox) == 1 mail = mails_testing.outbox[0] assert mail.sent_data["To"] == user.email
def test_get_thing_offer(self, app): product = ProductFactory(thumbCount=1, subcategoryId=subcategories.ABO_MUSEE.id) offer = OfferFactory(product=product, isEducational=True, venue__isPermanent=True) ThingStockFactory(offer=offer, price=12.34) offer_id = offer.id with assert_num_queries(1): response = TestClient( app.test_client()).get(f"/native/v1/offer/{offer_id}") assert response.status_code == 200 assert not response.json["stocks"][0]["beginningDatetime"] assert response.json["stocks"][0]["price"] == 1234 assert response.json["subcategoryId"] == "ABO_MUSEE" assert response.json["isEducational"] assert not response.json["isExpired"] assert response.json["venue"]["isPermanent"]
def test_queries_performance_educational_bookings(self, app) -> None: now = datetime.utcnow() yesterday = now - timedelta(days=1) booking_factories.PendingEducationalBookingFactory.create_batch( size=10, educationalBooking__confirmationLimitDate=yesterday) n_queries = ( +1 # select count + 1 # select initial booking ids + 1 # release savepoint/COMMIT + 4 * ( 1 # update + 1 # release savepoint/COMMIT + 1 # select stock + 1 # recompute dnBookedQuantity + 1 # select next ids )) with assert_num_queries(n_queries): handle_expired_bookings.cancel_expired_educational_bookings( batch_size=3)
def test_queries_performance_individual_bookings(self, app) -> None: now = datetime.utcnow() two_months_ago = now - timedelta(days=60) book = ProductFactory(subcategoryId=subcategories.LIVRE_PAPIER.id) booking_factories.IndividualBookingFactory.create_batch( size=10, stock__offer__product=book, dateCreated=two_months_ago) n_queries = ( +1 # select count + 1 # select initial booking ids + 1 # release savepoint/COMMIT + 4 * ( 1 # update + 1 # release savepoint/COMMIT + 1 # select stock + 1 # recompute dnBookedQuantity + 1 # select next ids )) with assert_num_queries(n_queries): handle_expired_bookings.cancel_expired_individual_bookings( batch_size=3)
def test_admin_call_num_queries(app): admin_user = users_factories.AdminFactory(email="*****@*****.**") user_offerers = offers_factories.UserOffererFactory.create_batch(3) offers_factories.VenueFactory(managingOfferer=user_offerers[0].offerer) offers_factories.VenueFactory(managingOfferer=user_offerers[1].offerer) offers_factories.VenueFactory(managingOfferer=user_offerers[2].offerer) client = TestClient(app.test_client()).with_session_auth(admin_user.email) # when n_queries = testing.AUTHENTICATION_QUERIES n_queries += 1 # get venues + offerers with assert_num_queries(n_queries): response = client.get("/venues") # then assert response.status_code == 200 assert len(response.json["venues"]) == 3