From e3d98b3b048166dbb5844e6c11bddd19929c881f Mon Sep 17 00:00:00 2001 From: Asad Ali Date: Thu, 23 Jul 2026 13:28:36 +0500 Subject: [PATCH 01/10] fix: allow 0 as minimum # of value (#3779) --- courses/forms.py | 8 ++++---- courses/models.py | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/courses/forms.py b/courses/forms.py index 5c82538f2d..b3214a80f7 100644 --- a/courses/forms.py +++ b/courses/forms.py @@ -141,7 +141,7 @@ def program_requirements_schema(): "format": "number", "title": "Value", "default": 1, - "minimum": 1, + "minimum": 0, "options": { "dependencies": { "node_type": ProgramRequirementNodeType.OPERATOR.value, @@ -305,13 +305,13 @@ def _validate_elective_value_presence(operator): # Ensure the value is not negative try: value = int(operator["data"]["operator_value"]) - if value < 1: + if value < 0: raise ValidationError( - '"Minimum # of" operator must have Value equal to 1 or more.' # noqa: EM101 + '"Minimum # of" operator must have Value equal to 0 or more.' # noqa: EM101 ) except (ValueError, TypeError): raise ValidationError( - '"Minimum # of" operator must have a valid numeric Value equal to 1 or more.' # noqa: EM101 + '"Minimum # of" operator must have a valid numeric Value equal to 0 or more.' # noqa: EM101 ) from None def _validate_operator_title(operator): diff --git a/courses/models.py b/courses/models.py index 600f5a7d5c..50cbeadce7 100644 --- a/courses/models.py +++ b/courses/models.py @@ -2598,9 +2598,9 @@ def clean(self): ): try: value = int(self.operator_value) - if value < 1: + if value < 0: raise ValidationError( - {"operator_value": "Minimum # of value must be 1 or greater."} + {"operator_value": "Minimum # of value must be 0 or greater."} ) except (ValueError, TypeError): raise ValidationError( From fc6ec4e960adbb4d157663a83b0960c23af63ee9 Mon Sep 17 00:00:00 2001 From: Nathan Levesque Date: Thu, 23 Jul 2026 14:47:31 -0400 Subject: [PATCH 02/10] Speed up pytest: fast password hasher, drop unused coverage html report (#3782) Co-authored-by: Claude Sonnet 5 --- main/settings.py | 4 ++++ pyproject.toml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/main/settings.py b/main/settings.py index 5ba9f80cbb..a562fc0222 100644 --- a/main/settings.py +++ b/main/settings.py @@ -327,6 +327,10 @@ ZEAL_RAISE = False ZEAL_ALLOWLIST = [] +# use a fast, insecure password hasher under pytest - tests don't need real hashing strength +if ENVIRONMENT == "pytest": + PASSWORD_HASHERS = ["django.contrib.auth.hashers.MD5PasswordHasher"] + SESSION_ENGINE = "django.contrib.sessions.backends.signed_cookies" MITXONLINE_NEW_USER_LOGIN_URL = get_string( diff --git a/pyproject.toml b/pyproject.toml index adf140f0a7..442b8a741b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -252,7 +252,7 @@ fixture-parentheses = false [tool.pytest.ini_options] -addopts = "--cov . --cov-report xml --cov-report term --cov-report html --ds=main.settings --reuse-db --color=yes" +addopts = "--cov . --cov-report xml --cov-report term --ds=main.settings --reuse-db --color=yes" norecursedirs = [ "node_modules", ".git", From 63ded115b0babf1a5e2bfcb07ca0a84cd5ca8fe6 Mon Sep 17 00:00:00 2001 From: Nathan Levesque Date: Thu, 23 Jul 2026 14:47:32 -0400 Subject: [PATCH 03/10] Shard CI pytest run across a 4-way matrix, drop unused coverage instrumentation (#3783) Co-authored-by: Claude Sonnet 5 --- .github/workflows/ci.yml | 90 +- .test_durations | 2708 +++++++++++++++++++++++++++++++++ cms/api.py | 6 +- cms/api_test.py | 40 +- cms/conftest.py | 18 + cms/models.py | 5 +- cms/models_test.py | 11 +- cms/utils.py | 7 + pyproject.toml | 1 + scripts/test/python_checks.sh | 26 + scripts/test/python_tests.sh | 5 +- uv.lock | 14 + 12 files changed, 2892 insertions(+), 39 deletions(-) create mode 100644 .test_durations create mode 100755 scripts/test/python_checks.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 73a05cb08e..6be086a2ba 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,9 +1,89 @@ name: CI on: [push] jobs: + python-checks: + runs-on: ubuntu-24.04 + + services: + db: + image: postgres:15.16 + # Health checks to wait until postgres has started + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + env: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres # pragma: allowlist secret + POSTGRES_DB: postgres + ports: + - 5432:5432 + + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Apt update + run: sudo apt-get update -y + + - name: Apt install + run: cat Aptfile | sudo xargs apt-get install + + - name: Install uv + uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 + with: + enable-cache: true + + - name: Install Python + run: uv python install 3.11 + + - name: Install dependencies + run: uv sync --locked + + - name: Create test local state + run: ./scripts/test/stub-data.sh + + - name: Django system checks + run: uv run ./manage.py check --fail-level WARNING + env: &django-env-vars + DEBUG: False + NODE_ENV: 'production' + CELERY_TASK_ALWAYS_EAGER: 'True' + CELERY_BROKER_URL: redis://localhost:6379/4 + CELERY_RESULT_BACKEND: redis://localhost:6379/4 + DATABASE_URL: postgres://postgres:postgres@localhost:5432/postgres # pragma: allowlist secret + MAILGUN_KEY: fake_mailgun_key + MAILGUN_SENDER_DOMAIN: other.fake.site + MITX_ONLINE_ADMIN_EMAIL: example@localhost + MITX_ONLINE_BASE_URL: http://localhost:8013 + MITX_ONLINE_DB_DISABLE_SSL: 'True' + MITX_ONLINE_EMAIL_BACKEND: django.core.mail.backends.locmem.EmailBackend + MITX_ONLINE_NOTIFICATION_EMAIL_BACKEND: django.core.mail.backends.locmem.EmailBackend + MITX_ONLINE_SECURE_SSL_REDIRECT: 'False' + MITX_ONLINE_USE_S3: 'False' + OPENEDX_API_BASE_URL: http://localhost:18000 + OPENEDX_API_CLIENT_ID: fake_client_id + OPENEDX_API_CLIENT_SECRET: fake_client_secret # pragma: allowlist secret + SECRET_KEY: local_unsafe_key # pragma: allowlist secret + + - name: Migration and OpenAPI spec checks + run: ./scripts/test/python_checks.sh + env: *django-env-vars + python-tests: runs-on: ubuntu-24.04 + strategy: + fail-fast: false + matrix: + group: [1, 2, 3, 4] + + env: + # coverage instrumentation roughly doubles suite runtime and nothing in CI + # consumes the coverage output (no upload step, all codecov.yml thresholds + # disabled) - skip it here, it's still available locally via a plain `pytest --cov`. + PYTEST_ADDOPTS: "--no-cov" + services: db: image: postgres:15.16 @@ -52,7 +132,7 @@ jobs: run: | celery -A main worker -B -l INFO & sleep 10 - env: &django-env-vars + env: &django-shard-env-vars DEBUG: False NODE_ENV: 'production' CELERY_TASK_ALWAYS_EAGER: 'True' @@ -73,16 +153,12 @@ jobs: OPENEDX_API_CLIENT_SECRET: fake_client_secret # pragma: allowlist secret SECRET_KEY: local_unsafe_key # pragma: allowlist secret - - name: Django system checks - run: uv run ./manage.py check --fail-level WARNING - env: *django-env-vars - - name: Tests run: | export MEDIA_ROOT="$(mktemp -d)" cp scripts/test/data/webpack-stats/* webpack-stats/ - ./scripts/test/python_tests.sh - env: *django-env-vars + ./scripts/test/python_tests.sh -n logical --splits 4 --group ${{ matrix.group }} + env: *django-shard-env-vars javascript-tests: runs-on: ubuntu-24.04 diff --git a/.test_durations b/.test_durations new file mode 100644 index 0000000000..b44bb648f8 --- /dev/null +++ b/.test_durations @@ -0,0 +1,2708 @@ +{ + "authentication/api_gateway/serializers_test.py::test_register_details_serializer_allows_numeric_only_name[ 12345 ]": 0.04125746699719457, + "authentication/api_gateway/serializers_test.py::test_register_details_serializer_allows_numeric_only_name[12345]": 0.03554568300023675, + "authentication/api_gateway/serializers_test.py::test_register_details_serializer_allows_numeric_only_name[123]": 0.040776241999992635, + "authentication/api_gateway/serializers_test.py::test_register_details_serializer_allows_numeric_only_name[12]": 0.04033824699581601, + "authentication/api_gateway/serializers_test.py::test_register_details_serializer_allows_numeric_only_name[999999]": 0.04567168500943808, + "authentication/api_gateway/serializers_test.py::test_register_details_serializer_create": 56.49728787800268, + "authentication/api_gateway/serializers_test.py::test_register_details_serializer_invalid_data": 0.04215330499573611, + "authentication/api_gateway/serializers_test.py::test_register_extra_details_serializer_valid_data": 0.034265410009538755, + "authentication/api_gateway/serializers_test.py::test_register_no_edx_user": 0.09999290799896698, + "authentication/api_gateway/views_test.py::test_account_action_callback": 0.03223050499946112, + "authentication/api_gateway/views_test.py::test_account_action_start[/account/action/start/update-email]": 0.03341613999509718, + "authentication/api_gateway/views_test.py::test_account_action_start[/account/action/start/update-password]": 0.03087588299968047, + "authentication/api_gateway/views_test.py::test_custom_login_view_authenticated_user_with_completed_onboarding": 0.06620956500410102, + "authentication/api_gateway/views_test.py::test_custom_login_view_authenticated_user_with_enrollments[/dashboard]": 0.2798967709968565, + "authentication/api_gateway/views_test.py::test_custom_login_view_authenticated_user_with_enrollments[http://openedx.odl.local/courses/abc]": 0.20210941901314072, + "authentication/api_gateway/views_test.py::test_custom_login_view_authenticated_user_with_onboarding": 0.0752274420010508, + "authentication/api_gateway/views_test.py::test_gateway_logout[None-/logout-https://openedx.odl.local/logout?redirect_url=http%3A%2F%2Fmitxonline.odl.local]": 0.03204834599455353, + "authentication/api_gateway/views_test.py::test_gateway_logout[None-/logout?no_redirect=1-http://mitxonline.odl.local]": 0.030373618996236473, + "authentication/api_gateway/views_test.py::test_gateway_logout[user-/logout-https://openedx.odl.local/logout?redirect_url=http%3A%2F%2Fmitxonline.odl.local]": 0.056865259997721296, + "authentication/api_gateway/views_test.py::test_gateway_logout[user-/logout?no_redirect=1-http://mitxonline.odl.local/logout/oidc]": 0.04039814400312025, + "authentication/api_gateway/views_test.py::test_logout_complete": 0.036187552992487326, + "authentication/api_gateway/views_test.py::test_post_user_extra_detail": 0.0873961440011044, + "authentication/api_gateway/views_test.py::test_post_user_profile_detail": 0.6101658990010037, + "authentication/api_test.py::test_create_user_session": 0.03404853800020646, + "authentication/views_test.py::test_well_known_openid_configuration": 0.12008664700260852, + "b2b/api_test.py::test_apply_available_discount_seat_limit": 0.46987814100430114, + "b2b/api_test.py::test_apply_available_discount_unlimited_seats[False]": 0.37753355199674843, + "b2b/api_test.py::test_apply_available_discount_unlimited_seats[True]": 0.39049168100609677, + "b2b/api_test.py::test_b2b_basket_validation[False-False]": 0.1821526769999764, + "b2b/api_test.py::test_b2b_basket_validation[False-True]": 0.16027790299267508, + "b2b/api_test.py::test_b2b_basket_validation[True-False]": 0.3030466899945168, + "b2b/api_test.py::test_b2b_basket_validation[True-True]": 0.28748638299293816, + "b2b/api_test.py::test_b2b_contract_removal_keeps_enrollments": 0.2158300120063359, + "b2b/api_test.py::test_b2b_org_attach_calls_keycloak[None]": 0.08402710199152352, + "b2b/api_test.py::test_b2b_org_attach_calls_keycloak[has_id1]": 0.09441202200105181, + "b2b/api_test.py::test_b2b_reconcile_keycloak_orgs[False]": 0.20258784599718638, + "b2b/api_test.py::test_b2b_reconcile_keycloak_orgs[True]": 0.2533141300009447, + "b2b/api_test.py::test_b2b_reconcile_user_orgs": 0.2928166070050793, + "b2b/api_test.py::test_create_b2b_enrollment[False-False-False-False-False-False-False]": 0.3155888080073055, + "b2b/api_test.py::test_create_b2b_enrollment[False-False-False-False-False-False-True]": 0.3873546620088746, + "b2b/api_test.py::test_create_b2b_enrollment[False-False-False-False-False-True-False]": 0.3465153570068651, + "b2b/api_test.py::test_create_b2b_enrollment[False-False-False-False-False-True-True]": 0.36708419699425576, + "b2b/api_test.py::test_create_b2b_enrollment[False-False-False-False-True-False-False]": 0.3424826819973532, + "b2b/api_test.py::test_create_b2b_enrollment[False-False-False-False-True-False-True]": 0.3323733010038268, + "b2b/api_test.py::test_create_b2b_enrollment[False-False-False-False-True-True-False]": 0.44831800799875055, + "b2b/api_test.py::test_create_b2b_enrollment[False-False-False-False-True-True-True]": 0.38889992899930803, + "b2b/api_test.py::test_create_b2b_enrollment[False-False-False-True-False-False-False]": 0.6909354849994997, + "b2b/api_test.py::test_create_b2b_enrollment[False-False-False-True-False-False-True]": 0.359249486013141, + "b2b/api_test.py::test_create_b2b_enrollment[False-False-False-True-False-True-False]": 0.2902462539932458, + "b2b/api_test.py::test_create_b2b_enrollment[False-False-False-True-False-True-True]": 0.34649398399778875, + "b2b/api_test.py::test_create_b2b_enrollment[False-False-False-True-True-False-False]": 0.3314277389945346, + "b2b/api_test.py::test_create_b2b_enrollment[False-False-False-True-True-False-True]": 0.28711224300786853, + "b2b/api_test.py::test_create_b2b_enrollment[False-False-False-True-True-True-False]": 0.26711721200990723, + "b2b/api_test.py::test_create_b2b_enrollment[False-False-False-True-True-True-True]": 0.33582890999969095, + "b2b/api_test.py::test_create_b2b_enrollment[False-False-True-False-False-False-False]": 57.30616290600301, + "b2b/api_test.py::test_create_b2b_enrollment[False-False-True-False-False-False-True]": 0.40707244399527553, + "b2b/api_test.py::test_create_b2b_enrollment[False-False-True-False-False-True-False]": 0.3740167699943413, + "b2b/api_test.py::test_create_b2b_enrollment[False-False-True-False-False-True-True]": 0.3552387420058949, + "b2b/api_test.py::test_create_b2b_enrollment[False-False-True-False-True-False-False]": 0.38952624599915, + "b2b/api_test.py::test_create_b2b_enrollment[False-False-True-False-True-False-True]": 0.4153273830015678, + "b2b/api_test.py::test_create_b2b_enrollment[False-False-True-False-True-True-False]": 0.38386380700103473, + "b2b/api_test.py::test_create_b2b_enrollment[False-False-True-False-True-True-True]": 0.4332081290122005, + "b2b/api_test.py::test_create_b2b_enrollment[False-False-True-True-False-False-False]": 0.2949667130014859, + "b2b/api_test.py::test_create_b2b_enrollment[False-False-True-True-False-False-True]": 0.32837755599757656, + "b2b/api_test.py::test_create_b2b_enrollment[False-False-True-True-False-True-False]": 0.32471336900198366, + "b2b/api_test.py::test_create_b2b_enrollment[False-False-True-True-False-True-True]": 0.5660155100049451, + "b2b/api_test.py::test_create_b2b_enrollment[False-False-True-True-True-False-False]": 0.2830582390015479, + "b2b/api_test.py::test_create_b2b_enrollment[False-False-True-True-True-False-True]": 0.32570769199810456, + "b2b/api_test.py::test_create_b2b_enrollment[False-False-True-True-True-True-False]": 0.2802969209951698, + "b2b/api_test.py::test_create_b2b_enrollment[False-False-True-True-True-True-True]": 0.560578009011806, + "b2b/api_test.py::test_create_b2b_enrollment[False-True-False-False-False-False-False]": 0.34699092200025916, + "b2b/api_test.py::test_create_b2b_enrollment[False-True-False-False-False-False-True]": 0.7636945870035561, + "b2b/api_test.py::test_create_b2b_enrollment[False-True-False-False-False-True-False]": 0.3865937730006408, + "b2b/api_test.py::test_create_b2b_enrollment[False-True-False-False-False-True-True]": 0.5149161409935914, + "b2b/api_test.py::test_create_b2b_enrollment[False-True-False-False-True-False-False]": 0.5223119430011138, + "b2b/api_test.py::test_create_b2b_enrollment[False-True-False-False-True-False-True]": 0.5949782140014577, + "b2b/api_test.py::test_create_b2b_enrollment[False-True-False-False-True-True-False]": 0.44389081800181884, + "b2b/api_test.py::test_create_b2b_enrollment[False-True-False-False-True-True-True]": 0.5073085619951598, + "b2b/api_test.py::test_create_b2b_enrollment[False-True-False-True-False-False-False]": 0.27471209399664076, + "b2b/api_test.py::test_create_b2b_enrollment[False-True-False-True-False-False-True]": 0.4294711689944961, + "b2b/api_test.py::test_create_b2b_enrollment[False-True-False-True-False-True-False]": 0.3248836159982602, + "b2b/api_test.py::test_create_b2b_enrollment[False-True-False-True-False-True-True]": 0.5104398360053892, + "b2b/api_test.py::test_create_b2b_enrollment[False-True-False-True-True-False-False]": 0.3625521980065969, + "b2b/api_test.py::test_create_b2b_enrollment[False-True-False-True-True-False-True]": 0.43884874301147647, + "b2b/api_test.py::test_create_b2b_enrollment[False-True-False-True-True-True-False]": 0.2959836360096233, + "b2b/api_test.py::test_create_b2b_enrollment[False-True-False-True-True-True-True]": 0.40426580300118076, + "b2b/api_test.py::test_create_b2b_enrollment[False-True-True-False-False-False-False]": 0.33420211799966637, + "b2b/api_test.py::test_create_b2b_enrollment[False-True-True-False-False-False-True]": 0.5122186789958505, + "b2b/api_test.py::test_create_b2b_enrollment[False-True-True-False-False-True-False]": 0.39312071899621515, + "b2b/api_test.py::test_create_b2b_enrollment[False-True-True-False-False-True-True]": 0.4902300999892759, + "b2b/api_test.py::test_create_b2b_enrollment[False-True-True-False-True-False-False]": 0.39227281299827155, + "b2b/api_test.py::test_create_b2b_enrollment[False-True-True-False-True-False-True]": 0.4323254399932921, + "b2b/api_test.py::test_create_b2b_enrollment[False-True-True-False-True-True-False]": 0.39552150500094285, + "b2b/api_test.py::test_create_b2b_enrollment[False-True-True-False-True-True-True]": 0.5072522389964433, + "b2b/api_test.py::test_create_b2b_enrollment[False-True-True-True-False-False-False]": 0.2600087349928799, + "b2b/api_test.py::test_create_b2b_enrollment[False-True-True-True-False-False-True]": 0.44424115900619654, + "b2b/api_test.py::test_create_b2b_enrollment[False-True-True-True-False-True-False]": 0.27254502200958086, + "b2b/api_test.py::test_create_b2b_enrollment[False-True-True-True-False-True-True]": 0.664245223000762, + "b2b/api_test.py::test_create_b2b_enrollment[False-True-True-True-True-False-False]": 0.24166065400640946, + "b2b/api_test.py::test_create_b2b_enrollment[False-True-True-True-True-False-True]": 0.38271902200358454, + "b2b/api_test.py::test_create_b2b_enrollment[False-True-True-True-True-True-False]": 0.24051159699592972, + "b2b/api_test.py::test_create_b2b_enrollment[False-True-True-True-True-True-True]": 0.5885818370006746, + "b2b/api_test.py::test_create_b2b_enrollment[True-False-False-False-False-False-False]": 0.3625525799943716, + "b2b/api_test.py::test_create_b2b_enrollment[True-False-False-False-False-False-True]": 0.42995176700060256, + "b2b/api_test.py::test_create_b2b_enrollment[True-False-False-False-False-True-False]": 0.36977840001054574, + "b2b/api_test.py::test_create_b2b_enrollment[True-False-False-False-False-True-True]": 0.38306423400354106, + "b2b/api_test.py::test_create_b2b_enrollment[True-False-False-False-True-False-False]": 0.3354999920047703, + "b2b/api_test.py::test_create_b2b_enrollment[True-False-False-False-True-False-True]": 0.39320131699787453, + "b2b/api_test.py::test_create_b2b_enrollment[True-False-False-False-True-True-False]": 0.39647034800145775, + "b2b/api_test.py::test_create_b2b_enrollment[True-False-False-False-True-True-True]": 0.370982300992182, + "b2b/api_test.py::test_create_b2b_enrollment[True-False-False-True-False-False-False]": 0.3011195960061741, + "b2b/api_test.py::test_create_b2b_enrollment[True-False-False-True-False-False-True]": 0.30950402699090773, + "b2b/api_test.py::test_create_b2b_enrollment[True-False-False-True-False-True-False]": 0.30999568200059, + "b2b/api_test.py::test_create_b2b_enrollment[True-False-False-True-False-True-True]": 0.31360783298441675, + "b2b/api_test.py::test_create_b2b_enrollment[True-False-False-True-True-False-False]": 0.27244017500197515, + "b2b/api_test.py::test_create_b2b_enrollment[True-False-False-True-True-False-True]": 0.28566832000069553, + "b2b/api_test.py::test_create_b2b_enrollment[True-False-False-True-True-True-False]": 0.28823876699607354, + "b2b/api_test.py::test_create_b2b_enrollment[True-False-False-True-True-True-True]": 0.3412384269904578, + "b2b/api_test.py::test_create_b2b_enrollment[True-False-True-False-False-False-False]": 0.31757137599925045, + "b2b/api_test.py::test_create_b2b_enrollment[True-False-True-False-False-False-True]": 0.34637061299872585, + "b2b/api_test.py::test_create_b2b_enrollment[True-False-True-False-False-True-False]": 0.3331398810041719, + "b2b/api_test.py::test_create_b2b_enrollment[True-False-True-False-False-True-True]": 0.3517173680011183, + "b2b/api_test.py::test_create_b2b_enrollment[True-False-True-False-True-False-False]": 0.3734225940061151, + "b2b/api_test.py::test_create_b2b_enrollment[True-False-True-False-True-False-True]": 0.3599367459974019, + "b2b/api_test.py::test_create_b2b_enrollment[True-False-True-False-True-True-False]": 0.35346854499948677, + "b2b/api_test.py::test_create_b2b_enrollment[True-False-True-False-True-True-True]": 0.40520257699972717, + "b2b/api_test.py::test_create_b2b_enrollment[True-False-True-True-False-False-False]": 0.2876661070040427, + "b2b/api_test.py::test_create_b2b_enrollment[True-False-True-True-False-False-True]": 0.32460375101072714, + "b2b/api_test.py::test_create_b2b_enrollment[True-False-True-True-False-True-False]": 0.293630441003188, + "b2b/api_test.py::test_create_b2b_enrollment[True-False-True-True-False-True-True]": 0.5571977859945036, + "b2b/api_test.py::test_create_b2b_enrollment[True-False-True-True-True-False-False]": 0.26809572700585704, + "b2b/api_test.py::test_create_b2b_enrollment[True-False-True-True-True-False-True]": 0.29898841000249377, + "b2b/api_test.py::test_create_b2b_enrollment[True-False-True-True-True-True-False]": 0.25035936698986916, + "b2b/api_test.py::test_create_b2b_enrollment[True-False-True-True-True-True-True]": 0.5196614219967159, + "b2b/api_test.py::test_create_b2b_enrollment[True-True-False-False-False-False-False]": 0.45484660899819573, + "b2b/api_test.py::test_create_b2b_enrollment[True-True-False-False-False-False-True]": 0.47806981900066603, + "b2b/api_test.py::test_create_b2b_enrollment[True-True-False-False-False-True-False]": 0.363273772993125, + "b2b/api_test.py::test_create_b2b_enrollment[True-True-False-False-False-True-True]": 0.48455097200348973, + "b2b/api_test.py::test_create_b2b_enrollment[True-True-False-False-True-False-False]": 0.34485553600097774, + "b2b/api_test.py::test_create_b2b_enrollment[True-True-False-False-True-False-True]": 0.5757163610032876, + "b2b/api_test.py::test_create_b2b_enrollment[True-True-False-False-True-True-False]": 0.37126681500376435, + "b2b/api_test.py::test_create_b2b_enrollment[True-True-False-False-True-True-True]": 0.5591695239927503, + "b2b/api_test.py::test_create_b2b_enrollment[True-True-False-True-False-False-False]": 0.28072017599333776, + "b2b/api_test.py::test_create_b2b_enrollment[True-True-False-True-False-False-True]": 0.38078612200479256, + "b2b/api_test.py::test_create_b2b_enrollment[True-True-False-True-False-True-False]": 0.300201231999381, + "b2b/api_test.py::test_create_b2b_enrollment[True-True-False-True-False-True-True]": 0.4342828750013723, + "b2b/api_test.py::test_create_b2b_enrollment[True-True-False-True-True-False-False]": 0.311307026007853, + "b2b/api_test.py::test_create_b2b_enrollment[True-True-False-True-True-False-True]": 0.4016600019967882, + "b2b/api_test.py::test_create_b2b_enrollment[True-True-False-True-True-True-False]": 0.29930740700365277, + "b2b/api_test.py::test_create_b2b_enrollment[True-True-False-True-True-True-True]": 0.3978828930048621, + "b2b/api_test.py::test_create_b2b_enrollment[True-True-True-False-False-False-False]": 0.38103923700691666, + "b2b/api_test.py::test_create_b2b_enrollment[True-True-True-False-False-False-True]": 0.4476138509999146, + "b2b/api_test.py::test_create_b2b_enrollment[True-True-True-False-False-True-False]": 0.35923909099801676, + "b2b/api_test.py::test_create_b2b_enrollment[True-True-True-False-False-True-True]": 0.5353259219918982, + "b2b/api_test.py::test_create_b2b_enrollment[True-True-True-False-True-False-False]": 0.38456033900001785, + "b2b/api_test.py::test_create_b2b_enrollment[True-True-True-False-True-False-True]": 0.5346249020076357, + "b2b/api_test.py::test_create_b2b_enrollment[True-True-True-False-True-True-False]": 0.315119461993163, + "b2b/api_test.py::test_create_b2b_enrollment[True-True-True-False-True-True-True]": 0.4244961250005872, + "b2b/api_test.py::test_create_b2b_enrollment[True-True-True-True-False-False-False]": 0.2878913790045772, + "b2b/api_test.py::test_create_b2b_enrollment[True-True-True-True-False-False-True]": 0.38519665500643896, + "b2b/api_test.py::test_create_b2b_enrollment[True-True-True-True-False-True-False]": 0.27592967799137114, + "b2b/api_test.py::test_create_b2b_enrollment[True-True-True-True-False-True-True]": 0.6097986629974912, + "b2b/api_test.py::test_create_b2b_enrollment[True-True-True-True-True-False-False]": 0.2624239340002532, + "b2b/api_test.py::test_create_b2b_enrollment[True-True-True-True-True-False-True]": 0.40538066999579314, + "b2b/api_test.py::test_create_b2b_enrollment[True-True-True-True-True-True-False]": 0.305710189997626, + "b2b/api_test.py::test_create_b2b_enrollment[True-True-True-True-True-True-True]": 1.3831555069991737, + "b2b/api_test.py::test_create_contract_run[False-False]": 0.23843158899398986, + "b2b/api_test.py::test_create_contract_run[False-True]": 0.21061146399733843, + "b2b/api_test.py::test_create_contract_run[True-False]": 0.23509141000249656, + "b2b/api_test.py::test_create_contract_run[True-True]": 0.22339310200186446, + "b2b/api_test.py::test_create_contract_run_duplicate_language_source_raises[both]": 0.21009615100047085, + "b2b/api_test.py::test_create_contract_run_duplicate_language_source_raises[first]": 0.25831187100266106, + "b2b/api_test.py::test_create_contract_run_duplicate_language_source_raises[no]": 0.14515937798569212, + "b2b/api_test.py::test_create_contract_run_duplicate_language_source_raises[second]": 0.2076724299986381, + "b2b/api_test.py::test_create_contract_run_key": 0.4046652280012495, + "b2b/api_test.py::test_create_contract_run_multi_language": 0.2502811269951053, + "b2b/api_test.py::test_create_contract_run_multi_language_propagates_language": 0.2138368320011068, + "b2b/api_test.py::test_create_contract_run_single_language_legacy": 0.2538562789923162, + "b2b/api_test.py::test_create_contract_run_variants": 0.4597374259974458, + "b2b/api_test.py::test_create_single_course_run[False-False]": 0.2639266029946157, + "b2b/api_test.py::test_create_single_course_run[False-True]": 0.24178288400435122, + "b2b/api_test.py::test_create_single_course_run[True-False]": 0.24329581599886296, + "b2b/api_test.py::test_create_single_course_run[True-True]": 0.28470168601052137, + "b2b/api_test.py::test_enroll_in_program_for_b2b[False-True]": 0.292519786999037, + "b2b/api_test.py::test_enroll_in_program_for_b2b[True-False]": 0.19038108400127385, + "b2b/api_test.py::test_enroll_in_program_for_b2b[True-True]": 0.3883400260019698, + "b2b/api_test.py::test_enroll_prereqs_existing_enrollment[None]": 0.21892377799667884, + "b2b/api_test.py::test_enroll_prereqs_existing_enrollment[]": 0.211003937991336, + "b2b/api_test.py::test_enroll_prereqs_existing_enrollment[unenrolled]": 0.21117343000514666, + "b2b/api_test.py::test_ensure_contract_run_pricing": 0.22717103900504299, + "b2b/api_test.py::test_ensure_contract_run_products": 0.22637430399481673, + "b2b/api_test.py::test_ensure_enrollment_codes[False-False-False-False-False-False]": 0.3256287439944572, + "b2b/api_test.py::test_ensure_enrollment_codes[False-False-False-False-False-True]": 0.33588417699502315, + "b2b/api_test.py::test_ensure_enrollment_codes[False-False-False-False-True-False]": 0.3129451670029084, + "b2b/api_test.py::test_ensure_enrollment_codes[False-False-False-True-False-False]": 0.32464028800313827, + "b2b/api_test.py::test_ensure_enrollment_codes[False-False-True-False-False-False]": 0.29835485999501543, + "b2b/api_test.py::test_ensure_enrollment_codes[False-False-True-False-False-True]": 0.3608248340096907, + "b2b/api_test.py::test_ensure_enrollment_codes[False-False-True-False-True-False]": 0.38723876800213475, + "b2b/api_test.py::test_ensure_enrollment_codes[False-False-True-True-False-False]": 0.28643479299353203, + "b2b/api_test.py::test_ensure_enrollment_codes[False-True-False-False-False-False]": 0.2528135450047557, + "b2b/api_test.py::test_ensure_enrollment_codes[False-True-False-False-False-True]": 0.3004452210079762, + "b2b/api_test.py::test_ensure_enrollment_codes[False-True-False-True-False-False]": 0.3240673049876932, + "b2b/api_test.py::test_ensure_enrollment_codes[False-True-True-False-False-False]": 0.2715115090031759, + "b2b/api_test.py::test_ensure_enrollment_codes[False-True-True-False-False-True]": 0.3728622980052023, + "b2b/api_test.py::test_ensure_enrollment_codes[False-True-True-True-False-False]": 0.33005328301078407, + "b2b/api_test.py::test_ensure_enrollment_codes[True-False-False-False-False-False]": 0.2718701829944621, + "b2b/api_test.py::test_ensure_enrollment_codes[True-False-False-True-False-False]": 0.3057440830016276, + "b2b/api_test.py::test_ensure_enrollment_codes[True-False-True-False-False-False]": 0.28883551700710086, + "b2b/api_test.py::test_ensure_enrollment_codes[True-False-True-True-False-False]": 0.3211461990067619, + "b2b/api_test.py::test_ensure_enrollment_codes[True-True-False-False-False-False]": 0.24896312600321835, + "b2b/api_test.py::test_ensure_enrollment_codes[True-True-False-False-False-True]": 0.30153703899122775, + "b2b/api_test.py::test_ensure_enrollment_codes[True-True-False-True-False-False]": 0.2827690180056379, + "b2b/api_test.py::test_ensure_enrollment_codes[True-True-True-False-False-False]": 0.32351920600194717, + "b2b/api_test.py::test_ensure_enrollment_codes[True-True-True-False-False-True]": 0.38368730001093354, + "b2b/api_test.py::test_ensure_enrollment_codes[True-True-True-True-False-False]": 0.36858462201053044, + "b2b/api_test.py::test_ensure_enrollment_codes_clears_extras": 0.2887434590011253, + "b2b/api_test.py::test_get_contract_products_with_bad_pricing": 0.2198320440002135, + "b2b/api_test.py::test_get_runs_without_products": 0.20468093499948736, + "b2b/api_test.py::test_get_source_course_runs_no_variants": 0.1738063719967613, + "b2b/api_test.py::test_get_source_course_runs_with_variants[False-False]": 0.1650813059968641, + "b2b/api_test.py::test_get_source_course_runs_with_variants[False-True]": 0.18008591999387136, + "b2b/api_test.py::test_get_source_course_runs_with_variants[True-False]": 0.24280515599821229, + "b2b/api_test.py::test_get_source_course_runs_with_variants[True-True]": 0.24393642200448085, + "b2b/api_test.py::test_get_source_course_runs_with_variants_lang_filter[ignore_langs]": 0.21152394900127547, + "b2b/api_test.py::test_get_source_course_runs_with_variants_lang_filter[only_lang]": 0.14963487999193603, + "b2b/api_test.py::test_get_user_b2b_organizations_excludes_contracts_user_not_in": 0.14195651300542522, + "b2b/api_test.py::test_get_user_b2b_organizations_excludes_inactive_contracts": 0.13940991300478345, + "b2b/api_test.py::test_get_user_b2b_organizations_excludes_other_users_orgs": 0.08986502500192728, + "b2b/api_test.py::test_get_user_b2b_organizations_mixed_contracts": 0.15992888800246874, + "b2b/api_test.py::test_get_user_b2b_organizations_multiple_orgs": 0.09984071899816627, + "b2b/api_test.py::test_get_user_b2b_organizations_no_orgs": 0.027708195004379377, + "b2b/api_test.py::test_get_user_b2b_organizations_prefetches_active_contracts": 0.11488525800086791, + "b2b/api_test.py::test_get_user_b2b_organizations_prefetches_contract_program_items": 0.36860278800304513, + "b2b/api_test.py::test_get_user_b2b_organizations_returns_user_org": 0.09746619601355633, + "b2b/api_test.py::test_import_and_create_contract_run[False-False]": 0.1147207760004676, + "b2b/api_test.py::test_import_and_create_contract_run[False-True]": 0.12549652100278763, + "b2b/api_test.py::test_import_and_create_contract_run[True-True]": 0.21798768600274343, + "b2b/api_test.py::test_import_and_create_contract_run_with_all_kwargs": 0.12887417500314768, + "b2b/api_test.py::test_import_and_create_contract_run_with_string_departments": 0.21144170701154508, + "b2b/api_test.py::test_reconcile_bad_keycloak_org": 0.08275713200418977, + "b2b/api_test.py::test_reconcile_keycloak_org_without_description": 0.10683242600498488, + "b2b/api_test.py::test_remove_extra_codes": 0.2949511520055239, + "b2b/api_test.py::test_user_add_b2b_org": 0.2227190769990557, + "b2b/api_test.py::test_user_remove_b2b_org": 0.0003662639937829226, + "b2b/commands_test.py::test_b2b_courseware_remove_run_does_not_delete_used_discount_contract_attachment": 0.23831008098932216, + "b2b/commands_test.py::test_b2b_courseware_remove_run_does_not_delete_used_discount_order_redemption": 0.266979372005153, + "b2b/commands_test.py::test_b2b_courseware_remove_run_with_enrollments_keeps_contract_and_deactivates": 0.24526797699945746, + "b2b/commands_test.py::test_b2b_courseware_remove_run_without_enrollments_unlinks_and_deactivates": 0.2682699969955138, + "b2b/keycloak_admin_api_test.py::test_admin_model": 0.03643754099175567, + "b2b/keycloak_admin_api_test.py::test_bootstrap_client[False]": 0.036475747001532, + "b2b/keycloak_admin_api_test.py::test_bootstrap_client[True]": 0.03595459799544187, + "b2b/keycloak_admin_api_test.py::test_client_associate": 0.05406704900087789, + "b2b/keycloak_admin_api_test.py::test_client_create": 0.04250816699641291, + "b2b/keycloak_admin_api_test.py::test_client_disassociate": 0.03576877600426087, + "b2b/keycloak_admin_api_test.py::test_client_get_one_realm": 0.044480459997430444, + "b2b/keycloak_admin_api_test.py::test_client_get_realms": 0.03640683200501371, + "b2b/keycloak_admin_api_test.py::test_client_init": 0.038179809009307064, + "b2b/keycloak_admin_api_test.py::test_client_init_base_url_processing": 0.031128845999774057, + "b2b/keycloak_admin_api_test.py::test_client_init_empty_base_url": 0.018633986001077574, + "b2b/keycloak_admin_api_test.py::test_client_init_empty_client_id": 0.02752767899801256, + "b2b/keycloak_admin_api_test.py::test_client_init_empty_client_secret": 0.028566629996930715, + "b2b/keycloak_admin_api_test.py::test_client_init_empty_discovery_url": 0.02269230299134506, + "b2b/keycloak_admin_api_test.py::test_client_init_empty_realm_name": 0.019798802000877913, + "b2b/keycloak_admin_api_test.py::test_client_init_missing_base_url": 0.018778725003357977, + "b2b/keycloak_admin_api_test.py::test_client_init_missing_client_id": 0.025181935001455713, + "b2b/keycloak_admin_api_test.py::test_client_init_missing_client_secret": 0.02833192300022347, + "b2b/keycloak_admin_api_test.py::test_client_init_missing_discovery_url": 0.023222343996167183, + "b2b/keycloak_admin_api_test.py::test_client_init_missing_realm_name": 0.020809754001675174, + "b2b/keycloak_admin_api_test.py::test_client_init_oauth_session_configuration": 0.0377706869985559, + "b2b/keycloak_admin_api_test.py::test_client_init_openid_configuration_request_failure": 0.028397048998158425, + "b2b/keycloak_admin_api_test.py::test_client_init_ssl_verification_disabled": 0.03320987299957778, + "b2b/keycloak_admin_api_test.py::test_client_init_ssl_verification_handling": 0.034045782005705405, + "b2b/keycloak_admin_api_test.py::test_client_list": 0.04370021400245605, + "b2b/keycloak_admin_api_test.py::test_client_realmify": 0.03164758501225151, + "b2b/keycloak_admin_api_test.py::test_client_retrieve": 0.048656226994353347, + "b2b/keycloak_admin_api_test.py::test_client_save": 0.04957383500004653, + "b2b/mail_test.py::test_send_email_helper_generates_unique_uuids_for_non_mailgun_backend": 0.021465343997988384, + "b2b/mail_test.py::test_send_email_helper_ignores_mailgun_status_on_non_mailgun_backend": 0.023686651009484194, + "b2b/mail_test.py::test_send_email_helper_logs_and_returns_none_on_send_error": 0.020322103002399672, + "b2b/mail_test.py::test_send_email_helper_returns_mailgun_message_id": 0.030359741998836398, + "b2b/mail_test.py::test_send_email_helper_returns_none_when_mailgun_status_missing": 0.021225238997431006, + "b2b/mail_test.py::test_send_email_helper_returns_uuid_for_non_mailgun_backend": 0.02464621300168801, + "b2b/management/tests/b2b_courseware_test.py::test_add_course[False-False-False]": 0.2659808120006346, + "b2b/management/tests/b2b_courseware_test.py::test_add_course[False-False-True]": 0.3603109539981233, + "b2b/management/tests/b2b_courseware_test.py::test_add_course[False-True-False]": 0.29022277399053564, + "b2b/management/tests/b2b_courseware_test.py::test_add_course[False-True-True]": 0.3038419720105594, + "b2b/management/tests/b2b_courseware_test.py::test_add_course[True-False-False]": 0.26802791600493947, + "b2b/management/tests/b2b_courseware_test.py::test_add_course[True-False-True]": 0.35370984899782343, + "b2b/management/tests/b2b_courseware_test.py::test_add_course[True-True-False]": 0.3003083920048084, + "b2b/management/tests/b2b_courseware_test.py::test_add_course[True-True-True]": 0.32823489700240316, + "b2b/management/tests/b2b_courseware_test.py::test_add_course_ignore_langs[False]": 0.26851675599755254, + "b2b/management/tests/b2b_courseware_test.py::test_add_course_ignore_langs[True]": 0.28851563000353053, + "b2b/management/tests/b2b_courseware_test.py::test_add_course_specific_lang[False]": 0.24680914800410392, + "b2b/management/tests/b2b_courseware_test.py::test_add_course_specific_lang[True]": 0.27947865900205215, + "b2b/management/tests/b2b_courseware_test.py::test_add_course_variants[False]": 0.34642734399676556, + "b2b/management/tests/b2b_courseware_test.py::test_add_course_variants[True]": 0.3494822270004079, + "b2b/management/tests/b2b_courseware_test.py::test_add_courserun": 0.18837266000628006, + "b2b/management/tests/b2b_courseware_test.py::test_add_courserun_existing_contract[False]": 0.24602016800054116, + "b2b/management/tests/b2b_courseware_test.py::test_add_courserun_existing_contract[True]": 0.2592498599915416, + "b2b/management/tests/b2b_courseware_test.py::test_add_program[False-False-False]": 0.9513012959941989, + "b2b/management/tests/b2b_courseware_test.py::test_add_program[False-False-True]": 1.0178459200033103, + "b2b/management/tests/b2b_courseware_test.py::test_add_program[False-True-False]": 0.947281225999177, + "b2b/management/tests/b2b_courseware_test.py::test_add_program[False-True-True]": 0.9947026270019705, + "b2b/management/tests/b2b_courseware_test.py::test_add_program[True-False-False]": 1.1161189250051393, + "b2b/management/tests/b2b_courseware_test.py::test_add_program[True-False-True]": 1.2772016859962605, + "b2b/management/tests/b2b_courseware_test.py::test_add_program[True-True-False]": 1.0955288079931051, + "b2b/management/tests/b2b_courseware_test.py::test_add_program[True-True-True]": 1.2943169159989338, + "b2b/management/tests/b2b_courseware_test.py::test_add_program_ignore_langs[False]": 0.5583380149982986, + "b2b/management/tests/b2b_courseware_test.py::test_add_program_ignore_langs[True]": 0.625336993005476, + "b2b/management/tests/b2b_courseware_test.py::test_add_program_specific_lang[False]": 0.548029368997959, + "b2b/management/tests/b2b_courseware_test.py::test_add_program_specific_lang[True]": 0.5439822660046048, + "b2b/management/tests/backfill_b2b_program_enrollments_test.py::test_backfill_creates_missing_program_enrollment": 0.3654011210091994, + "b2b/management/tests/backfill_b2b_program_enrollments_test.py::test_backfill_dry_run_creates_nothing": 0.3437997730070492, + "b2b/management/tests/backfill_b2b_program_enrollments_test.py::test_backfill_ignores_non_b2b_enrollments": 0.26322951900510816, + "b2b/management/tests/backfill_b2b_program_enrollments_test.py::test_backfill_is_idempotent": 0.3158057269974961, + "b2b/management/tests/backfill_b2b_program_enrollments_test.py::test_backfill_skips_ambiguous_course": 0.45468227899982594, + "b2b/management/tests/backfill_b2b_program_enrollments_test.py::test_backfill_skips_course_not_in_any_program": 0.3453835559921572, + "b2b/models_test.py::test_add_program_courses_to_contract": 0.9022166590002598, + "b2b/models_test.py::test_attach_user_no_sso_id[False]": 0.10630244599451544, + "b2b/models_test.py::test_attach_user_no_sso_id[True]": 0.08603723500709748, + "b2b/models_test.py::test_get_unused_discounts": 0.3077696790060145, + "b2b/models_test.py::test_organization_page_slug_generated_on_create": 0.08045341999968514, + "b2b/models_test.py::test_organization_page_slug_not_overwritten_if_set": 0.09790459900250426, + "b2b/models_test.py::test_organization_page_slug_preserved_on_name_change": 0.08899089600163279, + "b2b/models_test.py::test_remove_user_contracts_only_affects_specified_user": 0.16540147799969418, + "b2b/models_test.py::test_remove_user_contracts_only_removes_managed_contracts": 0.23027440500300145, + "b2b/permissions_test.py::TestIsOrganizationManagerHasObjectPermission::test_manager_with_b2b_contract_attr_matching": 0.08435156400082633, + "b2b/permissions_test.py::TestIsOrganizationManagerHasObjectPermission::test_manager_with_b2b_contract_attr_mismatched": 56.32322940899758, + "b2b/permissions_test.py::TestIsOrganizationManagerHasObjectPermission::test_manager_with_organization_attr_matching": 0.08479940800316399, + "b2b/permissions_test.py::TestIsOrganizationManagerHasObjectPermission::test_manager_with_organization_attr_mismatched": 0.1047336330084363, + "b2b/permissions_test.py::TestIsOrganizationManagerHasObjectPermission::test_manager_with_organization_id_attr_matching": 0.09959605200128863, + "b2b/permissions_test.py::TestIsOrganizationManagerHasObjectPermission::test_manager_with_organization_id_attr_mismatched": 0.10035424700618023, + "b2b/permissions_test.py::TestIsOrganizationManagerHasObjectPermission::test_manager_with_run_b2b_contract_attr_matching": 0.09201389000372728, + "b2b/permissions_test.py::TestIsOrganizationManagerHasObjectPermission::test_manager_with_run_b2b_contract_attr_mismatched": 0.10258096099278191, + "b2b/permissions_test.py::TestIsOrganizationManagerHasObjectPermission::test_superuser_always_allowed": 0.14244911100104218, + "b2b/permissions_test.py::TestIsOrganizationManagerHasPermission::test_manager_of_different_org_denied": 0.12865889100066852, + "b2b/permissions_test.py::TestIsOrganizationManagerHasPermission::test_manager_of_org_allowed": 0.08635039901128039, + "b2b/permissions_test.py::TestIsOrganizationManagerHasPermission::test_missing_org_id_in_kwargs_denied": 0.027633519006485585, + "b2b/permissions_test.py::TestIsOrganizationManagerHasPermission::test_non_manager_member_denied": 0.09083902800193755, + "b2b/permissions_test.py::TestIsOrganizationManagerHasPermission::test_superuser_always_allowed": 0.08910386599745834, + "b2b/permissions_test.py::TestIsOrganizationManagerHasPermission::test_unauthenticated_user_denied": 0.09586932599631837, + "b2b/serializers/v0/manager_test.py::TestManagerEnrollmentCodeSerializerAssigned::test_assigned_name": 0.11656394599413034, + "b2b/serializers/v0/manager_test.py::TestManagerEnrollmentCodeSerializerAssigned::test_assigned_on_is_populated": 0.11509424899122678, + "b2b/serializers/v0/manager_test.py::TestManagerEnrollmentCodeSerializerAssigned::test_assigned_to_email": 0.10251425499882316, + "b2b/serializers/v0/manager_test.py::TestManagerEnrollmentCodeSerializerAssigned::test_blank_assigned_name_serialized_as_empty_string": 0.11186850199737819, + "b2b/serializers/v0/manager_test.py::TestManagerEnrollmentCodeSerializerAssigned::test_last_sent_is_none_when_not_set": 0.12397058499482227, + "b2b/serializers/v0/manager_test.py::TestManagerEnrollmentCodeSerializerAssigned::test_last_sent_populated_when_set": 0.12873471100465395, + "b2b/serializers/v0/manager_test.py::TestManagerEnrollmentCodeSerializerAssigned::test_redeemed_by_is_none_when_not_redeemed": 0.10938094599259784, + "b2b/serializers/v0/manager_test.py::TestManagerEnrollmentCodeSerializerAssigned::test_redeemed_on_is_none_when_not_redeemed": 0.12594963200535858, + "b2b/serializers/v0/manager_test.py::TestManagerEnrollmentCodeSerializerAssigned::test_redemption_status_is_assigned": 0.160304860008182, + "b2b/serializers/v0/manager_test.py::TestManagerEnrollmentCodeSerializerPrefetchBehavior::test_empty_prefetched_list_treated_as_unassigned": 0.026623577999998815, + "b2b/serializers/v0/manager_test.py::TestManagerEnrollmentCodeSerializerPrefetchBehavior::test_fields_present_in_output": 0.02264236399787478, + "b2b/serializers/v0/manager_test.py::TestManagerEnrollmentCodeSerializerPrefetchBehavior::test_uses_first_redemption_in_list": 0.11471007100044517, + "b2b/serializers/v0/manager_test.py::TestManagerEnrollmentCodeSerializerRedeemed::test_assigned_to_still_populated_after_redemption": 0.14888780000183033, + "b2b/serializers/v0/manager_test.py::TestManagerEnrollmentCodeSerializerRedeemed::test_redeemed_by_none_when_no_user": 0.11672756399639184, + "b2b/serializers/v0/manager_test.py::TestManagerEnrollmentCodeSerializerRedeemed::test_redeemed_by_returns_user_email": 0.1301294350050739, + "b2b/serializers/v0/manager_test.py::TestManagerEnrollmentCodeSerializerRedeemed::test_redeemed_on_is_populated": 0.15673978999984683, + "b2b/serializers/v0/manager_test.py::TestManagerEnrollmentCodeSerializerRedeemed::test_redemption_status_redeemed_when_redeemed_on_set": 0.12175888299680082, + "b2b/serializers/v0/manager_test.py::TestManagerEnrollmentCodeSerializerRedeemed::test_redemption_status_redeemed_when_user_set": 0.12852213700534776, + "b2b/serializers/v0/manager_test.py::TestManagerEnrollmentCodeSerializerUnassigned::test_assigned_name_is_none": 0.022765788999095093, + "b2b/serializers/v0/manager_test.py::TestManagerEnrollmentCodeSerializerUnassigned::test_assigned_on_is_none": 0.022937112997169606, + "b2b/serializers/v0/manager_test.py::TestManagerEnrollmentCodeSerializerUnassigned::test_assigned_to_is_none": 0.02351450399874011, + "b2b/serializers/v0/manager_test.py::TestManagerEnrollmentCodeSerializerUnassigned::test_code_field_maps_discount_code": 0.026082653996127192, + "b2b/serializers/v0/manager_test.py::TestManagerEnrollmentCodeSerializerUnassigned::test_id_field": 0.0233597659971565, + "b2b/serializers/v0/manager_test.py::TestManagerEnrollmentCodeSerializerUnassigned::test_last_sent_is_none": 0.02123532199766487, + "b2b/serializers/v0/manager_test.py::TestManagerEnrollmentCodeSerializerUnassigned::test_no_prefetched_redemptions_attr": 0.022469055002147797, + "b2b/serializers/v0/manager_test.py::TestManagerEnrollmentCodeSerializerUnassigned::test_redeemed_by_is_none": 0.022398439003154635, + "b2b/serializers/v0/manager_test.py::TestManagerEnrollmentCodeSerializerUnassigned::test_redeemed_on_is_none": 0.022163494992128108, + "b2b/serializers/v0/manager_test.py::TestManagerEnrollmentCodeSerializerUnassigned::test_redemption_status_is_unassigned": 0.022522431005199905, + "b2b/serializers/v0/serializers_test.py::test_contract_page_serializer_expands_embeds": 0.5234379580069799, + "b2b/sheets_test.py::test_check_codes_expected_loc": 0.5586052209982881, + "b2b/sheets_test.py::test_ensure_header[0]": 0.14129921099811327, + "b2b/sheets_test.py::test_ensure_header[3]": 0.12214534499798901, + "b2b/sheets_test.py::test_formatted_codes": 0.5427891639992595, + "b2b/sheets_test.py::test_handler_init[False-False]": 0.1070904160005739, + "b2b/sheets_test.py::test_handler_init[False-True]": 0.1329209279938368, + "b2b/sheets_test.py::test_handler_init[True-False]": 0.12234974799503107, + "b2b/sheets_test.py::test_handler_init[True-True]": 0.15275079099956201, + "b2b/sheets_test.py::test_row_helpers": 0.12344181300431956, + "b2b/sheets_test.py::test_sorted_codes": 0.6254767159989569, + "b2b/sheets_test.py::test_update_sheet": 0.5261853719930514, + "b2b/sheets_test.py::test_write_codes": 0.5326063679967774, + "b2b/sheets_test.py::test_write_row[False-False]": 0.5307301919965539, + "b2b/sheets_test.py::test_write_row[False-True]": 0.5016408550000051, + "b2b/sheets_test.py::test_write_row[True-False]": 0.5021383950006566, + "b2b/sheets_test.py::test_write_row[True-True]": 0.46157335099997, + "b2b/tasks_test.py::test_create_program_contract_runs_clears_cached_requirements_data": 0.36675093800295144, + "b2b/tasks_test.py::test_create_program_contract_runs_courses_without_source_runs": 0.43615452999802073, + "b2b/tasks_test.py::test_create_program_contract_runs_exception_releases_lock": 0.5249296589972801, + "b2b/tasks_test.py::test_create_program_contract_runs_lock_not_acquired": 0.22215474800032098, + "b2b/tasks_test.py::test_create_program_contract_runs_logging_output": 0.34247752799274167, + "b2b/tasks_test.py::test_create_program_contract_runs_mixed_source_run_types": 0.4257148719916586, + "b2b/tasks_test.py::test_create_program_contract_runs_no_source_runs_for_course": 0.3249474670010386, + "b2b/tasks_test.py::test_create_program_contract_runs_skips_existing_runs": 0.369241931985016, + "b2b/tasks_test.py::test_create_program_contract_runs_source_run_by_tag": 0.3285638710076455, + "b2b/tasks_test.py::test_create_program_contract_runs_success": 0.4597952160038403, + "b2b/views/v0/manager_test.py::test_assign_code": 1.036933064002369, + "b2b/views/v0/manager_test.py::test_assign_code_already_assigned": 0.9428443989963853, + "b2b/views/v0/manager_test.py::test_assign_code_forbidden": 0.9274466950082569, + "b2b/views/v0/manager_test.py::test_assign_code_internal_error": 0.9855402260000119, + "b2b/views/v0/manager_test.py::test_assign_code_invalid_request": 1.078827156998159, + "b2b/views/v0/manager_test.py::test_assign_code_name_defaults_to_empty": 1.0347941709987936, + "b2b/views/v0/manager_test.py::test_assign_code_not_found": 0.9534183329960797, + "b2b/views/v0/manager_test.py::test_assign_codes_and_send_emails_bulk_create_failure": 0.9494532829994569, + "b2b/views/v0/manager_test.py::test_assign_codes_and_send_emails_creates_records": 1.0931139379899832, + "b2b/views/v0/manager_test.py::test_assign_codes_and_send_emails_empty_list": 0.9474269749989617, + "b2b/views/v0/manager_test.py::test_assign_codes_and_send_emails_queues_email_with_record_ids": 1.0436980090016732, + "b2b/views/v0/manager_test.py::test_assign_codes_and_send_emails_sets_prefetched_redemptions": 0.9474548010039143, + "b2b/views/v0/manager_test.py::test_assign_codes_and_send_emails_timestamps_are_set": 0.9146206770019489, + "b2b/views/v0/manager_test.py::test_bulk_assign": 0.9926502050002455, + "b2b/views/v0/manager_test.py::test_bulk_assign_deduplicates_emails": 1.0553006299960543, + "b2b/views/v0/manager_test.py::test_bulk_assign_forbidden": 1.0440190619920031, + "b2b/views/v0/manager_test.py::test_bulk_assign_insufficient_codes": 1.1825349200080382, + "b2b/views/v0/manager_test.py::test_bulk_assign_internal_error": 1.0028383160024532, + "b2b/views/v0/manager_test.py::test_bulk_assign_invalid_request": 1.0067232609944767, + "b2b/views/v0/manager_test.py::test_bulk_assign_no_product_for_uncapped_contract": 1.2180331100025796, + "b2b/views/v0/manager_test.py::test_bulk_assign_provisions_codes_for_uncapped_contract": 1.2354652750000241, + "b2b/views/v0/manager_test.py::test_bulk_assign_skips_already_assigned_or_redeemed": 1.2543530859984457, + "b2b/views/v0/manager_test.py::test_manager_org_list_member_not_manager_sees_empty": 1.057943127998442, + "b2b/views/v0/manager_test.py::test_manager_org_list_multiple_managed_orgs": 1.2543070759929833, + "b2b/views/v0/manager_test.py::test_manager_org_list_non_manager_sees_empty": 0.9995377249942976, + "b2b/views/v0/manager_test.py::test_manager_org_list_only_active_contracts": 0.9919676330027869, + "b2b/views/v0/manager_test.py::test_manager_org_list_unauthenticated": 1.0189531790092587, + "b2b/views/v0/manager_test.py::test_manager_org_retrieve_nonexistent_returns_404": 0.9217661750008119, + "b2b/views/v0/manager_test.py::test_manager_org_retrieve_not_managed_returns_404": 0.9111909379935241, + "b2b/views/v0/manager_test.py::test_manager_org_retrieve_only_active_contracts": 1.0514838109957054, + "b2b/views/v0/manager_test.py::test_manager_org_retrieve_success": 1.0112340000050608, + "b2b/views/v0/manager_test.py::test_manager_org_retrieve_unauthenticated": 1.0442783050020807, + "b2b/views/v0/manager_test.py::test_org_contract_codes": 1.1490492050070316, + "b2b/views/v0/manager_test.py::test_org_contract_codes_redeemed_by_differs_from_assigned_to": 1.1468787549965782, + "b2b/views/v0/manager_test.py::test_org_contract_codes_search_term": 1.1803073800110724, + "b2b/views/v0/manager_test.py::test_org_contract_codes_status_and_search_combined": 1.1138176300009945, + "b2b/views/v0/manager_test.py::test_org_contract_codes_status_filter": 0.9803369590008515, + "b2b/views/v0/manager_test.py::test_org_contract_detail_no_max_learners": 1.6203772329972708, + "b2b/views/v0/manager_test.py::test_org_contract_lists": 1.032869312010007, + "b2b/views/v0/manager_test.py::test_org_contract_run_enrollments": 1.1646059550039354, + "b2b/views/v0/manager_test.py::test_org_contract_run_list": 1.0670812950047548, + "b2b/views/v0/manager_test.py::test_org_setup": 0.941587145003723, + "b2b/views/v0/manager_test.py::test_reassign_code": 1.1182336649944773, + "b2b/views/v0/manager_test.py::test_reassign_code_already_redeemed": 1.067664160997083, + "b2b/views/v0/manager_test.py::test_reassign_code_forbidden": 1.059886404000281, + "b2b/views/v0/manager_test.py::test_reassign_code_invalid_request": 1.0357134680089075, + "b2b/views/v0/manager_test.py::test_reassign_code_name_defaults_to_empty": 1.0886797890052549, + "b2b/views/v0/manager_test.py::test_reassign_code_no_assignment": 1.0834248990067863, + "b2b/views/v0/manager_test.py::test_reassign_code_not_found": 1.0942839369963622, + "b2b/views/v0/manager_test.py::test_reassign_code_updates_assigned_by": 1.3914040539966663, + "b2b/views/v0/manager_test.py::test_revoke_code": 0.9561981509978068, + "b2b/views/v0/manager_test.py::test_revoke_code_already_redeemed": 1.1544252810053877, + "b2b/views/v0/manager_test.py::test_revoke_code_assignment_not_found": 1.001337750996754, + "b2b/views/v0/manager_test.py::test_revoke_code_forbidden": 0.949689806002425, + "b2b/views/v0/manager_test.py::test_revoke_code_not_found": 0.9351917760068318, + "b2b/views/v0/manager_test.py::test_send_reminder_already_redeemed": 1.1059198350048973, + "b2b/views/v0/manager_test.py::test_send_reminder_assignment_not_found": 1.0358892399890465, + "b2b/views/v0/manager_test.py::test_send_reminder_code_not_found": 0.9487688430090202, + "b2b/views/v0/manager_test.py::test_send_reminder_for_code_assignment": 1.0770773039985215, + "b2b/views/v0/manager_test.py::test_send_reminder_forbidden": 1.0222941350002657, + "b2b/views/v0/manager_test.py::test_send_reminder_no_assigned_email": 1.0416383719930309, + "b2b/views/v0/manager_test.py::test_send_reminder_redeemed_on_set": 1.0665077369994833, + "b2b/views/v0/views_test.py::test_b2b_contract_attachment[active-False-10]": 0.3755362649972085, + "b2b/views/v0/views_test.py::test_b2b_contract_attachment[active-False-None]": 0.4192390269963653, + "b2b/views/v0/views_test.py::test_b2b_contract_attachment[active-True-10]": 0.2805409470020095, + "b2b/views/v0/views_test.py::test_b2b_contract_attachment[active-True-None]": 0.35711147100664675, + "b2b/views/v0/views_test.py::test_b2b_contract_attachment[date-False-10]": 0.3046271200000774, + "b2b/views/v0/views_test.py::test_b2b_contract_attachment[date-False-None]": 0.3394358529985766, + "b2b/views/v0/views_test.py::test_b2b_contract_attachment[date-True-10]": 0.3411900560022332, + "b2b/views/v0/views_test.py::test_b2b_contract_attachment[date-True-None]": 0.2877722130069742, + "b2b/views/v0/views_test.py::test_b2b_contract_attachment[flag-False-10]": 0.3469981330053997, + "b2b/views/v0/views_test.py::test_b2b_contract_attachment[flag-False-None]": 0.2849817520036595, + "b2b/views/v0/views_test.py::test_b2b_contract_attachment[flag-True-10]": 0.34507131000282243, + "b2b/views/v0/views_test.py::test_b2b_contract_attachment[flag-True-None]": 0.29962984900339507, + "b2b/views/v0/views_test.py::test_b2b_contract_attachment_bad_code": 0.055911846000526566, + "b2b/views/v0/views_test.py::test_b2b_contract_attachment_code_with_no_contracts": 0.060967088997131214, + "b2b/views/v0/views_test.py::test_b2b_contract_attachment_creates_org_membership_with_keep_until_seen": 0.37506145399675006, + "b2b/views/v0/views_test.py::test_b2b_contract_attachment_full_contract": 0.3426257240062114, + "b2b/views/v0/views_test.py::test_b2b_contract_attachment_full_contract_with_used_code": 0.2914228989975527, + "b2b/views/v0/views_test.py::test_b2b_contract_attachment_invalid_code_dates[False]": 0.3292790959967533, + "b2b/views/v0/views_test.py::test_b2b_contract_attachment_invalid_code_dates[True]": 0.4693833160054055, + "b2b/views/v0/views_test.py::test_b2b_contract_attachment_invalid_contract_dates[False]": 0.33602041700214613, + "b2b/views/v0/views_test.py::test_b2b_contract_attachment_invalid_contract_dates[True]": 0.3505817830009619, + "b2b/views/v0/views_test.py::test_b2b_contract_attachment_requires_authentication": 0.024765256996033713, + "b2b/views/v0/views_test.py::test_b2b_contract_attachment_response_excludes_unrelated_contracts": 0.4240427999975509, + "b2b/views/v0/views_test.py::test_b2b_contract_attachment_returns_matching_contract_when_already_attached": 0.4017545849928865, + "b2b/views/v0/views_test.py::test_b2b_contract_attachment_sets_redeemed_on": 0.37742039400473004, + "b2b/views/v0/views_test.py::test_b2b_enroll[active-False-False-False]": 0.319640399000491, + "b2b/views/v0/views_test.py::test_b2b_enroll[active-False-False-True]": 0.3398066169975209, + "b2b/views/v0/views_test.py::test_b2b_enroll[active-False-True-False]": 0.2997935039966251, + "b2b/views/v0/views_test.py::test_b2b_enroll[active-False-True-True]": 0.3206987189987558, + "b2b/views/v0/views_test.py::test_b2b_enroll[active-True-False-False]": 1.588041978997353, + "b2b/views/v0/views_test.py::test_b2b_enroll[active-True-False-True]": 1.5457288370016613, + "b2b/views/v0/views_test.py::test_b2b_enroll[active-True-True-False]": 0.8293014260052587, + "b2b/views/v0/views_test.py::test_b2b_enroll[active-True-True-True]": 0.4525817050089245, + "b2b/views/v0/views_test.py::test_b2b_enroll[date-False-False-False]": 0.35440525299782166, + "b2b/views/v0/views_test.py::test_b2b_enroll[date-False-False-True]": 0.32051102699915646, + "b2b/views/v0/views_test.py::test_b2b_enroll[date-False-True-False]": 0.33537730300304247, + "b2b/views/v0/views_test.py::test_b2b_enroll[date-False-True-True]": 0.30589881799096474, + "b2b/views/v0/views_test.py::test_b2b_enroll[date-True-False-False]": 0.30518684099661186, + "b2b/views/v0/views_test.py::test_b2b_enroll[date-True-False-True]": 0.3182673019982758, + "b2b/views/v0/views_test.py::test_b2b_enroll[date-True-True-False]": 0.3691523369998322, + "b2b/views/v0/views_test.py::test_b2b_enroll[date-True-True-True]": 0.4191305170097621, + "b2b/views/v0/views_test.py::test_b2b_enroll[flag-False-False-False]": 0.34870972399949096, + "b2b/views/v0/views_test.py::test_b2b_enroll[flag-False-False-True]": 0.3075595810005325, + "b2b/views/v0/views_test.py::test_b2b_enroll[flag-False-True-False]": 0.35968958900048165, + "b2b/views/v0/views_test.py::test_b2b_enroll[flag-False-True-True]": 0.352965366007993, + "b2b/views/v0/views_test.py::test_b2b_enroll[flag-True-False-False]": 0.3674057750031352, + "b2b/views/v0/views_test.py::test_b2b_enroll[flag-True-False-True]": 0.3707629570053541, + "b2b/views/v0/views_test.py::test_b2b_enroll[flag-True-True-False]": 0.38220222200470744, + "b2b/views/v0/views_test.py::test_b2b_enroll[flag-True-True-True]": 0.3521432910056319, + "b2b/views/v0/views_test.py::test_enroll_courserun_not_found": 0.44175892700150143, + "b2b/views/v0/views_test.py::test_enroll_courserun_without_b2b_contract_not_found": 0.19167986899992684, + "b2b/views/v0/views_test.py::test_enroll_failure_returns_400[b2b-error-already-enrolled]": 0.24047638499177992, + "b2b/views/v0/views_test.py::test_enroll_failure_returns_400[b2b-error-no-contract]": 0.27204687900666613, + "b2b/views/v0/views_test.py::test_enroll_failure_returns_400[b2b-error-not-enrollable]": 0.29082798299350543, + "b2b/views/v0/views_test.py::test_enroll_failure_returns_400[b2b-error-requires-checkout]": 0.2241272910105181, + "b2b/views/v0/views_test.py::test_enroll_omits_program_id_when_not_provided": 0.2473539059938048, + "b2b/views/v0/views_test.py::test_enroll_passes_program_id_to_api": 0.24818081999546848, + "b2b/views/v0/views_test.py::test_enroll_product_not_found": 0.2393447610011208, + "b2b/views/v0/views_test.py::test_enroll_requires_authentication": 0.03982511200592853, + "b2b/views/v0/views_test.py::test_enroll_success": 0.24744581000413746, + "b2b/views/v0/views_test.py::test_preassigned_code_can_be_redeemed": 56.89341177600727, + "cms/api_test.py::test_create_courseware_page": 0.41716963900398696, + "cms/api_test.py::test_create_featured_items": 0.49589864499284886, + "cms/api_test.py::test_create_featured_items_cache_no_expiry": 0.16144987300504, + "cms/api_test.py::test_create_featured_items_course_run_at_exact_time": 0.1612159140058793, + "cms/api_test.py::test_create_featured_items_many_regular_courses": 2.14743700499821, + "cms/api_test.py::test_create_featured_items_many_self_paced_courses": 0.4049389060091926, + "cms/api_test.py::test_create_featured_items_mixed_course_types": 0.29525832099170657, + "cms/api_test.py::test_create_featured_items_no_courses": 0.03241047100163996, + "cms/api_test.py::test_create_featured_items_no_courses_no_prior_cache": 0.025585670002328698, + "cms/api_test.py::test_create_featured_items_no_enrollable_courses": 0.16350491799676092, + "cms/api_test.py::test_ensure_home_page_and_site": 0.1586560650030151, + "cms/api_test.py::test_ensure_product_index": 0.25589388299704297, + "cms/api_test.py::test_ensure_program_product_index": 0.25697748999664327, + "cms/api_test.py::test_ensure_resource_pages": 0.3317092270081048, + "cms/api_test.py::test_get_home_page": 0.062224652006989345, + "cms/api_test.py::test_get_home_page_specific": 0.05607909899845254, + "cms/api_test.py::test_get_wagtail_img_src": 0.03995025499898475, + "cms/api_test.py::test_home_page_featured_products": 0.14659669599495828, + "cms/api_test.py::test_home_page_featured_products_published_only": 0.4970918839899241, + "cms/api_test.py::test_home_page_featured_products_sorting": 0.23633725200488698, + "cms/commands_test.py::test_configure_wagtail": 0.03459533699788153, + "cms/models_test.py::TestFlexiblePricingFormBuilder::test_create_country_field_ordering": 0.022038755007088184, + "cms/models_test.py::TestFlexiblePricingFormBuilder::test_create_country_field_with_currency_exchange_rates": 0.019222725000872742, + "cms/models_test.py::TestFlexiblePricingFormBuilder::test_create_country_field_with_no_currency_exchange_rates": 0.016691579003236257, + "cms/models_test.py::TestFlexiblePricingFormBuilder::test_create_country_field_with_null_description": 0.017566201997397002, + "cms/models_test.py::test_certificate_for_course_page": 0.16566554900055053, + "cms/models_test.py::test_certificate_for_program_page": 0.13897274199553067, + "cms/models_test.py::test_certificate_page_product_name_field_labels": 0.015924196006380953, + "cms/models_test.py::test_course_index_page_get_child_by_readable_id": 0.16561702300532488, + "cms/models_test.py::test_course_index_page_get_child_by_readable_id_raises_does_not_exist": 0.036060421996808145, + "cms/models_test.py::test_course_object_index_page_route_converts_does_not_exist_to_http404": 0.09591799199552042, + "cms/models_test.py::test_course_object_index_page_route_multiple_path_components": 0.09594563499558717, + "cms/models_test.py::test_course_object_index_page_route_with_empty_path_components": 0.031163198997091968, + "cms/models_test.py::test_course_object_index_page_route_with_invalid_readable_id": 0.03264001299976371, + "cms/models_test.py::test_course_object_index_page_route_with_remaining_components": 0.09981143299955875, + "cms/models_test.py::test_course_object_index_page_route_with_valid_course_readable_id": 0.11472548399615334, + "cms/models_test.py::test_course_page_context[False-False-False-True-False-False-True]": 0.34516028399957577, + "cms/models_test.py::test_course_page_context[False-True-True-True-False-False-False]": 0.26393945799645735, + "cms/models_test.py::test_course_page_context[True-True-True-False-True-False-False]": 0.27116911000484833, + "cms/models_test.py::test_course_page_context[True-True-True-False-True-True-True]": 0.4197786889926647, + "cms/models_test.py::test_course_page_context_edx_access[False-False-True-True-False]": 0.30412156399688683, + "cms/models_test.py::test_course_page_context_edx_access[True-False-True-False-False]": 0.3013115780049702, + "cms/models_test.py::test_course_page_context_edx_access[True-True-False-True-False]": 0.27061174200935056, + "cms/models_test.py::test_course_page_context_edx_access[True-True-True-False-True]": 0.3076188360064407, + "cms/models_test.py::test_course_page_context_edx_access[True-True-True-True-True]": 0.296372381002584, + "cms/models_test.py::test_courseware_title_synced_with_product_page_title[False]": 0.13599434000934707, + "cms/models_test.py::test_courseware_title_synced_with_product_page_title[True]": 0.12946793999435613, + "cms/models_test.py::test_custom_detail_page_urls": 0.32462002598913386, + "cms/models_test.py::test_custom_detail_page_urls_handled": 0.26179724199755583, + "cms/models_test.py::test_flex_pricing_form_courseware_object": 0.4519166169993696, + "cms/models_test.py::test_flex_pricing_form_display[False-False]": 0.21772609500476392, + "cms/models_test.py::test_flex_pricing_form_display[True-False]": 0.2447574120014906, + "cms/models_test.py::test_flex_pricing_form_display[True-True]": 0.22580151200236287, + "cms/models_test.py::test_flex_pricing_form_state_display[approved]": 0.21697495300031733, + "cms/models_test.py::test_flex_pricing_form_state_display[created]": 0.2373722709962749, + "cms/models_test.py::test_flex_pricing_form_state_display[denied]": 0.5717194390017539, + "cms/models_test.py::test_flex_pricing_form_state_display[reset]": 0.21466989100008504, + "cms/models_test.py::test_flex_pricing_form_state_display_no_discount_tier": 0.3959875069995178, + "cms/models_test.py::test_flex_pricing_parent_resources[False]": 0.15014416399935726, + "cms/models_test.py::test_flex_pricing_parent_resources[True]": 0.13789703801012365, + "cms/models_test.py::test_flex_pricing_single_submission[course]": 0.44099192299472634, + "cms/models_test.py::test_flex_pricing_single_submission[one_program]": 0.4532116819973453, + "cms/models_test.py::test_flex_pricing_single_submission[two_programs]": 0.47899717400287045, + "cms/models_test.py::test_flexible_pricing_form_get_context_absolute_last_submission_fallback": 0.3429227489978075, + "cms/models_test.py::test_flexible_pricing_form_get_context_basic_structure": 0.2214680630058865, + "cms/models_test.py::test_flexible_pricing_form_get_context_country_precedence[False-False]": 0.18088299599912716, + "cms/models_test.py::test_flexible_pricing_form_get_context_country_precedence[False-True]": 0.23456563499348704, + "cms/models_test.py::test_flexible_pricing_form_get_context_country_precedence[True-False]": 0.27782484700583154, + "cms/models_test.py::test_flexible_pricing_form_get_context_country_precedence[True-True]": 0.26612126200052444, + "cms/models_test.py::test_flexible_pricing_form_get_context_course_no_active_products": 0.20176171499770135, + "cms/models_test.py::test_flexible_pricing_form_get_context_courseware_specific": 0.47408224399987375, + "cms/models_test.py::test_flexible_pricing_form_get_context_no_previous_submission": 0.21269311999640195, + "cms/models_test.py::test_flexible_pricing_form_get_context_previous_submission_overrides": 0.2510375650017522, + "cms/models_test.py::test_flexible_pricing_form_get_context_product_for_course": 0.23191704299824778, + "cms/models_test.py::test_flexible_pricing_form_get_context_product_for_program": 0.21696409399737604, + "cms/models_test.py::test_flexible_pricing_form_get_context_with_anonymous_user": 0.1688848300036625, + "cms/models_test.py::test_flexible_pricing_form_get_context_with_specific_submission": 0.2883035889972234, + "cms/models_test.py::test_flexible_pricing_form_get_context_with_user_legal_address": 0.19716326100751758, + "cms/models_test.py::test_flexible_pricing_request_form_context[False]": 0.3079324110003654, + "cms/models_test.py::test_flexible_pricing_request_form_context[True]": 0.25571922999370145, + "cms/models_test.py::test_fp_request_form_get_context_absolute_last_submission": 0.30965779500547796, + "cms/models_test.py::test_fp_request_form_get_context_no_legal_address": 0.20879344499553554, + "cms/models_test.py::test_fp_request_form_get_context_no_previous_submission": 0.18887128798814956, + "cms/models_test.py::test_fp_request_form_get_context_program_page": 0.2120398879997083, + "cms/models_test.py::test_fp_request_form_get_context_with_previous_submission": 0.20446383499802323, + "cms/models_test.py::test_homepage_featured_products": 0.3095325170070282, + "cms/models_test.py::test_program_index_page_get_child_by_readable_id": 0.17442849800136173, + "cms/models_test.py::test_program_object_index_page_route_with_valid_program_readable_id": 0.11193193100189092, + "cms/models_test.py::test_resource_page_site_name": 0.04970352199597983, + "cms/models_test.py::test_signatory_page_requires_signature_image": 0.12890122800308745, + "cms/serializers_test.py::test_get_course_specific_form_returns_only_live_forms": 0.26960552400851157, + "cms/serializers_test.py::test_get_current_price_mixed_zero_and_positive_prices": 0.27422848899732344, + "cms/serializers_test.py::test_get_current_price_no_active_products": 0.2099487390005379, + "cms/serializers_test.py::test_get_current_price_product_without_price_attribute": 0.2960906439984683, + "cms/serializers_test.py::test_get_current_price_with_attribute_error": 0.27675032000115607, + "cms/serializers_test.py::test_get_current_price_with_empty_list": 0.2712196450011106, + "cms/serializers_test.py::test_get_current_price_with_empty_queryset": 0.2839328820045921, + "cms/serializers_test.py::test_get_current_price_with_prefetched_list_multiple_products": 0.28443860299739754, + "cms/serializers_test.py::test_get_current_price_with_prefetched_list_single_product": 0.26432745300553506, + "cms/serializers_test.py::test_get_current_price_with_queryset_multiple_products": 0.30247945900191553, + "cms/serializers_test.py::test_get_current_price_with_queryset_single_product": 0.25865581999823917, + "cms/serializers_test.py::test_get_current_price_with_type_error": 0.23436170500644948, + "cms/serializers_test.py::test_get_current_price_zero_price_product": 0.2649890879911254, + "cms/serializers_test.py::test_serialize_course_page": 0.3722105560009368, + "cms/serializers_test.py::test_serialize_course_page_with_flex_price_form_as_child_no_program": 0.33361372100625886, + "cms/serializers_test.py::test_serialize_course_page_with_flex_price_form_as_program_child": 0.46787148900330067, + "cms/serializers_test.py::test_serialize_course_page_with_flex_price_with_program_fk_and_parent": 0.49113943699921947, + "cms/serializers_test.py::test_serialize_course_page_with_flex_price_with_program_fk_no_parent": 0.5758202069991967, + "cms/serializers_test.py::test_serialize_program_page": 0.329581390004023, + "cms/serializers_test.py::test_serialize_program_page__form_child_of_course_with_program_fk": 0.42570041599537944, + "cms/serializers_test.py::test_serialize_program_page__no_financial_form": 0.299630128996796, + "cms/serializers_test.py::test_serialize_program_page__with_related_financial_form": 0.4065950820004218, + "cms/serializers_test.py::test_serialize_program_page__with_related_program_no_financial_form": 0.35365013299451675, + "cms/serializers_test.py::test_serialized_course_finaid_form_url[False-True-False]": 0.4487459320007474, + "cms/serializers_test.py::test_serialized_course_finaid_form_url[False-True-True]": 0.5035255629991298, + "cms/serializers_test.py::test_serialized_course_finaid_form_url[True-False-False]": 0.4489680179976858, + "cms/serializers_test.py::test_serialized_course_finaid_form_url[True-True-False]": 0.46892586600006325, + "cms/serializers_test.py::test_serialized_course_finaid_form_url[True-True-True]": 0.46975224299239926, + "cms/serializers_test.py::test_serialized_course_finaid_form_url_publishing_states[False-False]": 0.44896161999349715, + "cms/serializers_test.py::test_serialized_course_finaid_form_url_publishing_states[False-True]": 0.451363712993043, + "cms/serializers_test.py::test_serialized_course_finaid_form_url_publishing_states[True-False]": 0.42212599500635406, + "cms/serializers_test.py::test_serialized_course_finaid_form_url_publishing_states[True-True]": 0.4954514860000927, + "cms/templatetags_test.py::test_featured_img_src": 0.14103694599907612, + "cms/templatetags_test.py::test_wagtail_img_src": 0.017673531008767895, + "cms/wagtail_api/views_test.py::test_anonymous_can_access_certificate_pages": 0.32360155500646215, + "cms/wagtail_api/views_test.py::test_anonymous_can_access_course_pages": 0.29791129600198474, + "cms/wagtail_api/views_test.py::test_anonymous_can_access_program_pages": 0.3152242939977441, + "cms/wagtail_api/views_test.py::test_anonymous_course_pages_filtered_by_include_in_learn_catalog": 0.3689356980030425, + "cms/wagtail_api/views_test.py::test_anonymous_denied_for_non_public_page_types": 0.19773095400887541, + "cms/wagtail_api/views_test.py::test_anonymous_denied_without_type": 0.20394485699216602, + "cms/wagtail_api/views_test.py::test_anonymous_program_pages_filtered_by_b2b_only": 0.4167575359897455, + "cms/wagtail_api/views_test.py::test_anonymous_program_pages_filtered_by_include_in_learn_catalog": 0.4073961670001154, + "cms/wagtail_api/views_test.py::test_authenticated_can_list_pages_without_type": 0.22091569899203023, + "cms/wagtail_api/views_test.py::test_authenticated_sees_all_course_pages_regardless_of_catalog_flag": 0.373632462004025, + "cms/wagtail_api/views_test.py::test_authenticated_sees_b2b_program_pages": 0.32420979099697433, + "cms/wagtail_api/views_test.py::test_detail_view_certificate_page_with_invalid_revision_returns_404": 0.3617532869975548, + "cms/wagtail_api/views_test.py::test_detail_view_certificate_page_with_valid_revision": 0.38987885099777486, + "cms/wagtail_api/views_test.py::test_detail_view_non_certificate_page_ignores_revision_id": 0.68208305099688, + "cms/wagtail_api/views_test.py::test_detail_view_returns_correct_page": 0.5972254059961415, + "cms/wagtail_api/views_test.py::test_listing_response_includes_live_in_meta": 0.3258580170077039, + "cms/wagtail_api/views_test.py::test_readable_id_filter_replaces_spaces_with_plus": 0.33592944499105215, + "cms/wagtail_api/views_test.py::test_readable_id_filter_returns_matching_course_page": 0.37660853299166774, + "courses/admin_test.py::test_repair_exhausted_display[False-4-False]": 0.07866902100795414, + "courses/admin_test.py::test_repair_exhausted_display[False-5-True]": 0.06979310499446001, + "courses/admin_test.py::test_repair_exhausted_display[True-5-False]": 0.0722543849988142, + "courses/admin_test.py::test_reset_edx_enrollment_retry_count_action": 0.45717447399511, + "courses/api_test.py::TestDeactivateEnrollments::test_deactivate_run_enrollment": 0.19127843599562766, + "courses/api_test.py::TestDeactivateEnrollments::test_deactivate_run_enrollment_api_fail[False]": 0.18246531999466242, + "courses/api_test.py::TestDeactivateEnrollments::test_deactivate_run_enrollment_api_fail[True]": 0.17954852699040202, + "courses/api_test.py::TestDeactivateEnrollments::test_deactivate_run_enrollment_line_does_not_exist": 0.19713891400169814, + "courses/api_test.py::TestUpgradeAuditRunEnrollmentsForProgramPurchase::test_mixed_enrollments_upgrades_only_audit": 0.3682526139964466, + "courses/api_test.py::TestUpgradeAuditRunEnrollmentsForProgramPurchase::test_returns_empty_when_no_enrollments": 0.22997151700110408, + "courses/api_test.py::TestUpgradeAuditRunEnrollmentsForProgramPurchase::test_skips_audit_only_run": 0.2349556500048493, + "courses/api_test.py::TestUpgradeAuditRunEnrollmentsForProgramPurchase::test_skips_inactive_enrollment": 0.2994324600076652, + "courses/api_test.py::TestUpgradeAuditRunEnrollmentsForProgramPurchase::test_skips_non_live_run": 0.28554322000127286, + "courses/api_test.py::TestUpgradeAuditRunEnrollmentsForProgramPurchase::test_skips_past_upgrade_deadline": 0.2620804180041887, + "courses/api_test.py::TestUpgradeAuditRunEnrollmentsForProgramPurchase::test_skips_verified_enrollment": 0.284709022998868, + "courses/api_test.py::TestUpgradeAuditRunEnrollmentsForProgramPurchase::test_upgrades_audit_enrollment": 0.390315931996156, + "courses/api_test.py::TestUpgradeAuditRunEnrollmentsForProgramPurchase::test_upgrades_only_program_courses": 0.3692366400064202, + "courses/api_test.py::TestUpgradeAuditRunEnrollmentsForProgramPurchase::test_upgrades_when_no_upgrade_deadline": 0.3179779599959147, + "courses/api_test.py::test_b2b_re_enrollment_after_multiple_unenrollments": 1.13993688600749, + "courses/api_test.py::test_check_course_modes[False-False]": 0.1419713929935824, + "courses/api_test.py::test_check_course_modes[False-True]": 0.16719945801014546, + "courses/api_test.py::test_check_course_modes[True-False]": 0.15930780400231015, + "courses/api_test.py::test_check_course_modes[True-True]": 0.15554730499570724, + "courses/api_test.py::test_course_certificates_with_course_end_date_self_paced_combination[False-None]": 0.19703475500136847, + "courses/api_test.py::test_course_certificates_with_course_end_date_self_paced_combination[False-end_date1]": 0.16963600300368853, + "courses/api_test.py::test_course_certificates_with_course_end_date_self_paced_combination[True-end_date0]": 0.17483392300346168, + "courses/api_test.py::test_course_run_certificate[0.0-True-True-False-False-False]": 0.15271748300438048, + "courses/api_test.py::test_course_run_certificate[0.25-True-False-False-False-False]": 0.19465837200550595, + "courses/api_test.py::test_course_run_certificate[0.25-True-True-True-True-False]": 0.17593434999434976, + "courses/api_test.py::test_course_run_certificate[1.0-False-True-False-False-False]": 0.14295162999769673, + "courses/api_test.py::test_course_run_certificate_idempotent": 0.1515947590014548, + "courses/api_test.py::test_course_run_certificate_not_passing": 0.16336070498800837, + "courses/api_test.py::test_course_run_certificate_verifiable_credentials": 0.18837816300219856, + "courses/api_test.py::test_course_run_certificate_verifiable_credentials_feature_flag_disabled": 0.18627016600657953, + "courses/api_test.py::test_course_run_certificate_verifiable_credentials_signing_payload": 0.22496721900097327, + "courses/api_test.py::test_course_run_certificates_access": 0.18242384900077013, + "courses/api_test.py::test_create_local_enrollment_existing_enrollment[False-False]": 0.19891389300028095, + "courses/api_test.py::test_create_local_enrollment_existing_enrollment[True-True]": 0.18426511899451725, + "courses/api_test.py::test_create_local_enrollment_new[audit]": 0.15528165899740998, + "courses/api_test.py::test_create_local_enrollment_new[verified]": 0.17726532799133565, + "courses/api_test.py::test_create_local_enrollment_writes_audit_on_creation": 0.2575319879979361, + "courses/api_test.py::test_create_program_enrollments_creation[audit]": 0.2848574189993087, + "courses/api_test.py::test_create_program_enrollments_creation[verified]": 0.2664747409944539, + "courses/api_test.py::test_create_program_enrollments_creation_fail": 0.3245390200027032, + "courses/api_test.py::test_create_program_enrollments_reactivation[None-False]": 0.1468354550015647, + "courses/api_test.py::test_create_program_enrollments_reactivation[None-True]": 0.1614799419985502, + "courses/api_test.py::test_create_program_enrollments_reactivation[deferred-False]": 0.1587943330014241, + "courses/api_test.py::test_create_program_enrollments_reactivation[deferred-True]": 0.15707442499115132, + "courses/api_test.py::test_create_program_enrollments_reactivation[refunded-False]": 0.2533246289967792, + "courses/api_test.py::test_create_program_enrollments_reactivation[refunded-True]": 0.1630346019883291, + "courses/api_test.py::test_create_program_enrollments_reactivation[transferred-False]": 0.16061082299711416, + "courses/api_test.py::test_create_program_enrollments_reactivation[transferred-True]": 0.16738981399976183, + "courses/api_test.py::test_create_program_enrollments_reactivation[unenrolled-False]": 0.19753719499567524, + "courses/api_test.py::test_create_program_enrollments_reactivation[unenrolled-True]": 0.2140033129908261, + "courses/api_test.py::test_create_program_enrollments_writes_audit_on_creation": 0.30000465599732706, + "courses/api_test.py::test_create_run_enrollments[audit]": 0.4223641639982816, + "courses/api_test.py::test_create_run_enrollments[verified]": 0.39717320800264133, + "courses/api_test.py::test_create_run_enrollments_api_fail[ConnectionError]": 0.15830189600819722, + "courses/api_test.py::test_create_run_enrollments_api_fail[HTTPError]": 0.14971272500406485, + "courses/api_test.py::test_create_run_enrollments_api_fail[NoEdxApiAuthError]": 0.17939359999581939, + "courses/api_test.py::test_create_run_enrollments_creation_fail": 0.3438949159972253, + "courses/api_test.py::test_create_run_enrollments_enroll_api_fail[EdxApiEnrollErrorException-inner_exception0-False]": 0.4036287299968535, + "courses/api_test.py::test_create_run_enrollments_enroll_api_fail[EdxApiEnrollErrorException-inner_exception0-True]": 0.5086255739952321, + "courses/api_test.py::test_create_run_enrollments_enroll_api_fail[UnknownEdxApiEnrollException-inner_exception1-False]": 0.7620768359993235, + "courses/api_test.py::test_create_run_enrollments_enroll_api_fail[UnknownEdxApiEnrollException-inner_exception1-True]": 0.5133839079935569, + "courses/api_test.py::test_create_run_enrollments_feature_flag[False-True-False]": 0.24809078000544105, + "courses/api_test.py::test_create_run_enrollments_feature_flag[None-False-False]": 0.2894655369891552, + "courses/api_test.py::test_create_run_enrollments_feature_flag[None-True-True]": 0.2699404809900443, + "courses/api_test.py::test_create_run_enrollments_feature_flag[True-False-True]": 0.2522411960089812, + "courses/api_test.py::test_create_run_enrollments_multiple_programs": 0.3744026480053435, + "courses/api_test.py::test_create_run_enrollments_upgrade[False]": 0.30788719699921785, + "courses/api_test.py::test_create_run_enrollments_upgrade[True]": 0.28809531600563787, + "courses/api_test.py::test_create_run_enrollments_upgrade_edx_request_failure": 0.22938027200871147, + "courses/api_test.py::test_create_run_enrollments_writes_audit_on_creation": 0.25918658100272296, + "courses/api_test.py::test_deactivate_run_enrollment_feature_flag[False-True-False]": 0.23982211200200254, + "courses/api_test.py::test_deactivate_run_enrollment_feature_flag[None-False-False]": 0.13975086999562336, + "courses/api_test.py::test_deactivate_run_enrollment_feature_flag[None-True-True]": 0.18699267001647968, + "courses/api_test.py::test_deactivate_run_enrollment_feature_flag[True-False-True]": 0.17619949700019788, + "courses/api_test.py::test_deactivate_run_enrollment_removes_paid_course_run": 0.17080621600325685, + "courses/api_test.py::test_defer_enrollment[False-False-False-False]": 0.2244180910056457, + "courses/api_test.py::test_defer_enrollment[False-False-False-True]": 0.20030538399441866, + "courses/api_test.py::test_defer_enrollment[False-False-True-False]": 0.17313502099568723, + "courses/api_test.py::test_defer_enrollment[False-False-True-True]": 0.16867460899811704, + "courses/api_test.py::test_defer_enrollment[False-True-False-False]": 0.16973987399978796, + "courses/api_test.py::test_defer_enrollment[False-True-False-True]": 0.1843438759970013, + "courses/api_test.py::test_defer_enrollment[False-True-True-False]": 0.18429592199390754, + "courses/api_test.py::test_defer_enrollment[False-True-True-True]": 0.18045442798756994, + "courses/api_test.py::test_defer_enrollment[True-False-False-False]": 0.18488387900288217, + "courses/api_test.py::test_defer_enrollment[True-False-False-True]": 0.17709965600079158, + "courses/api_test.py::test_defer_enrollment[True-False-True-False]": 0.18753255699994043, + "courses/api_test.py::test_defer_enrollment[True-False-True-True]": 0.248570858995663, + "courses/api_test.py::test_defer_enrollment[True-True-False-False]": 0.20167365800443804, + "courses/api_test.py::test_defer_enrollment[True-True-False-True]": 0.26297951400920283, + "courses/api_test.py::test_defer_enrollment[True-True-True-False]": 0.21361670700571267, + "courses/api_test.py::test_defer_enrollment[True-True-True-True]": 0.21449903000029735, + "courses/api_test.py::test_defer_enrollment_validation": 0.4029687369984458, + "courses/api_test.py::test_force_generate_program_certificate_success[False]": 1.2041008700034581, + "courses/api_test.py::test_force_generate_program_certificate_success[True]": 1.140544741996564, + "courses/api_test.py::test_generate_course_certificates_failure_isolation": 0.1855579309994937, + "courses/api_test.py::test_generate_course_certificates_no_valid_course_run": 0.6442821149903466, + "courses/api_test.py::test_generate_course_certificates_self_paced_course": 0.17693658400094137, + "courses/api_test.py::test_generate_course_certificates_with_course_end_date[False]": 0.1819131039956119, + "courses/api_test.py::test_generate_course_certificates_with_course_end_date[True]": 0.20694403199740918, + "courses/api_test.py::test_generate_missing_program_certificates_creates_cert": 0.2628831469992292, + "courses/api_test.py::test_generate_missing_program_certificates_failure_isolation": 0.22923391399672255, + "courses/api_test.py::test_generate_missing_program_certificates_idempotent": 0.2886381310017896, + "courses/api_test.py::test_generate_openedx_course_url[False-False]": 0.02730579000490252, + "courses/api_test.py::test_generate_openedx_course_url[False-True]": 0.028175238003314007, + "courses/api_test.py::test_generate_openedx_course_url[True-False]": 0.027449093999166507, + "courses/api_test.py::test_generate_openedx_course_url[True-True]": 0.027399853002862073, + "courses/api_test.py::test_generate_program_certificate_already_exist": 0.19507480599713745, + "courses/api_test.py::test_generate_program_certificate_audit_courses": 0.461033507999673, + "courses/api_test.py::test_generate_program_certificate_existing_revoked_returns_existing": 0.1667791460058652, + "courses/api_test.py::test_generate_program_certificate_failure_missing_certificates": 1.0149347750048037, + "courses/api_test.py::test_generate_program_certificate_failure_not_all_passed": 1.2883948480011895, + "courses/api_test.py::test_generate_program_certificate_failure_not_all_passed_nested_elective_stipulation": 0.5028887009975733, + "courses/api_test.py::test_generate_program_certificate_not_verified[False]": 0.8997538819967303, + "courses/api_test.py::test_generate_program_certificate_not_verified[True]": 0.9870227279971004, + "courses/api_test.py::test_generate_program_certificate_success_minimum_electives_not_met": 0.5319802920057555, + "courses/api_test.py::test_generate_program_certificate_success_multiple_required_courses": 0.5639701390027767, + "courses/api_test.py::test_generate_program_certificate_success_single_requirement_course": 0.3050489179950091, + "courses/api_test.py::test_generate_program_certificate_with_revoked_subprogram_certificate": 0.4664502560044639, + "courses/api_test.py::test_generate_program_certificate_with_subprogram_requirement[False-False]": 0.3679673799997545, + "courses/api_test.py::test_generate_program_certificate_with_subprogram_requirement[False-True]": 0.4298644710070221, + "courses/api_test.py::test_generate_program_certificate_with_subprogram_requirement[True-False]": 0.4385594050036161, + "courses/api_test.py::test_generate_program_certificate_with_subprogram_requirement[True-True]": 0.5149415949854301, + "courses/api_test.py::test_get_certificate_grade_eligible_runs[False-False-False]": 0.12113800599763636, + "courses/api_test.py::test_get_certificate_grade_eligible_runs[False-False-True]": 0.1264704949935549, + "courses/api_test.py::test_get_certificate_grade_eligible_runs[True-False-False]": 0.28133018199150683, + "courses/api_test.py::test_get_certificate_grade_eligible_runs[True-False-True]": 0.27495239899872104, + "courses/api_test.py::test_get_certificate_grade_eligible_runs[True-True-False]": 0.2669537290057633, + "courses/api_test.py::test_get_certificate_grade_eligible_runs[True-True-True]": 0.3060290499997791, + "courses/api_test.py::test_get_eligible_program_certificate_candidates[audit_mode-False]": 0.13423394899291452, + "courses/api_test.py::test_get_eligible_program_certificate_candidates[has_certificate-False]": 0.13399999200191814, + "courses/api_test.py::test_get_eligible_program_certificate_candidates[non_live_program-False]": 0.12424349800130585, + "courses/api_test.py::test_get_eligible_program_certificate_candidates[verified_only-True]": 0.1350033929993515, + "courses/api_test.py::test_import_courserun_from_edx_basic[False-False-False-False-False]": 0.030054595998080913, + "courses/api_test.py::test_import_courserun_from_edx_basic[True-True-True-True-True]": 0.05226786999992328, + "courses/api_test.py::test_import_courserun_from_edx_calls_pull_course_modes": 0.03551283000706462, + "courses/api_test.py::test_import_courserun_from_edx_pulls_enrollment_modes": 0.0334175289972336, + "courses/api_test.py::test_import_courserun_from_edx_specific_course_pages[False-True-False-False-False]": 0.09098283599450951, + "courses/api_test.py::test_import_courserun_from_edx_specific_course_pages[False-True-True-True-True]": 0.10865716000262182, + "courses/api_test.py::test_import_courserun_from_edx_specific_course_pages[True-False-False-False-False]": 0.12006412500340957, + "courses/api_test.py::test_override_user_grade[0.0-F-True-False]": 0.16966226200020174, + "courses/api_test.py::test_override_user_grade[0.1-F-True-True]": 0.16520040499744937, + "courses/api_test.py::test_override_user_grade[0.5-C-False-False]": 0.1958106459933333, + "courses/api_test.py::test_override_user_grade[0.5-C-True-True]": 0.1708014309988357, + "courses/api_test.py::test_program_certificate_verifiable_credentials": 1.2145869599989965, + "courses/api_test.py::test_program_certificate_verifiable_credentials_signing_payload": 0.41257231299823616, + "courses/api_test.py::test_program_certificates_access": 0.17688684299355373, + "courses/api_test.py::test_pull_course_modes[False]": 0.21556406400486594, + "courses/api_test.py::test_pull_course_modes[True]": 0.22152041499793995, + "courses/api_test.py::test_sync_course_mode[mocked_api_response0-True]": 0.1742740390036488, + "courses/api_test.py::test_sync_course_mode[mocked_api_response1-True]": 0.1415676300021005, + "courses/api_test.py::test_sync_course_mode[mocked_api_response2-True]": 0.15525290699588368, + "courses/api_test.py::test_sync_course_mode[mocked_api_response3-False]": 0.14870126500318293, + "courses/api_test.py::test_sync_course_mode[mocked_api_response4-False]": 0.14672296599746915, + "courses/api_test.py::test_sync_course_mode[mocked_api_response5-False]": 0.17936473699955968, + "courses/api_test.py::test_sync_course_runs[mocked_api_response0-True]": 0.26673016399581684, + "courses/api_test.py::test_sync_course_runs[mocked_api_response1-False]": 0.2563378010017914, + "courses/api_test.py::test_sync_course_runs[mocked_api_response2-False]": 0.23982243099453626, + "courses/api_test.py::test_sync_course_runs[mocked_api_response3-False]": 0.24666548000095645, + "courses/api_test.py::test_sync_course_runs[mocked_api_response4-False]": 0.2168187229981413, + "courses/api_test.py::test_sync_course_runs_bulk[mocked_api_response0-True]": 0.22416469699965091, + "courses/api_test.py::test_sync_course_runs_bulk[mocked_api_response1-False]": 0.221505804991466, + "courses/api_test.py::test_sync_course_runs_bulk[mocked_api_response2-False]": 0.22045199599961052, + "courses/api_test.py::test_sync_course_runs_bulk[mocked_api_response3-False]": 0.24263949500891613, + "courses/api_test.py::test_sync_course_runs_bulk[mocked_api_response4-False]": 0.23458199499873444, + "courses/api_test.py::test_upgrade_program_enrollment_if_eligible_query_count": 0.9775330009942991, + "courses/api_test.py::test_upgrade_program_enrollment_if_eligible_returns_false_when_electives_missing": 0.8938598789973184, + "courses/api_test.py::test_upgrade_program_enrollment_if_eligible_upgrades_when_requirements_met": 1.0525279700013925, + "courses/api_test.py::test_webhook_certificate_already_exists": 0.2334440819977317, + "courses/api_test.py::test_webhook_certificate_bypasses_certificate_available_date": 0.19291086500015808, + "courses/api_test.py::test_webhook_certificate_status[audit-0.8-True-False]": 0.19911802100250497, + "courses/api_test.py::test_webhook_certificate_status[verified-0.3-False-False]": 0.16459604999545263, + "courses/api_test.py::test_webhook_certificate_status[verified-0.8-True-True]": 0.18306304300494958, + "courses/api_test.py::test_webhook_no_grade_from_edx": 0.14656772700254805, + "courses/constants_test.py::test_content_type_names": 0.015502356000069994, + "courses/factories_test.py::test_courserun_completed_trait": 8.381900522006617, + "courses/mail_api_test.py::test_send_course_run_enrollment_email": 0.13419865299511002, + "courses/mail_api_test.py::test_send_course_run_enrollment_email_error": 0.15705984200758394, + "courses/mail_api_test.py::test_send_enrollment_failure_message[False]": 0.16487167299783323, + "courses/mail_api_test.py::test_send_enrollment_failure_message[True]": 0.156434956996236, + "courses/mail_api_test.py::test_send_partner_school_sharing_message": 0.18759540100290906, + "courses/management/commands/test_backfill_program_certificate_titles.py::test_all_programs_processes_clean_programs_and_skips_placeholder": 0.4946668059928925, + "courses/management/commands/test_backfill_program_certificate_titles.py::test_all_programs_skips_programs_without_cms_page": 0.19888112200715113, + "courses/management/commands/test_backfill_program_certificate_titles.py::test_both_mode_flags_raise_command_error": 0.016395158992963843, + "courses/management/commands/test_backfill_program_certificate_titles.py::test_commit_is_idempotent": 0.2577868140069768, + "courses/management/commands/test_backfill_program_certificate_titles.py::test_commit_rollback_emits_no_misleading_success_output": 0.23455197899602354, + "courses/management/commands/test_backfill_program_certificate_titles.py::test_commit_updates_frozen_title_to_live_page_value": 0.22829731300589629, + "courses/management/commands/test_backfill_program_certificate_titles.py::test_dry_run_by_default_makes_no_db_changes": 0.2292590110009769, + "courses/management/commands/test_backfill_program_certificate_titles.py::test_dry_run_output_reports_planned_change": 0.19461060599132907, + "courses/management/commands/test_backfill_program_certificate_titles.py::test_empty_live_title_skips_program_without_writing": 0.18852871398848947, + "courses/management/commands/test_backfill_program_certificate_titles.py::test_no_mode_flag_raises_command_error": 0.015299019003577996, + "courses/management/commands/test_backfill_program_certificate_titles.py::test_null_revision_cert_is_skipped_without_error": 0.23077813599957153, + "courses/management/commands/test_backfill_program_certificate_titles.py::test_placeholder_live_title_skips_program_without_writing": 0.18774441000277875, + "courses/management/commands/test_backfill_program_certificate_titles.py::test_revoked_certificate_is_also_backfilled": 0.198874859008356, + "courses/management/commands/test_backfill_program_certificate_titles.py::test_shared_revision_updated_once_for_all_certs": 0.2595232739986386, + "courses/management/commands/test_backfill_program_certificate_titles.py::test_summary_line_reports_totals": 0.1796286640019389, + "courses/management/commands/test_backfill_program_certificate_titles.py::test_unknown_program_is_reported_and_other_programs_still_processed": 0.178624846994353, + "courses/management/commands/test_manage_certificate.py::test_certificate_management_create[False]": 0.16751520799152786, + "courses/management/commands/test_manage_certificate.py::test_certificate_management_create[True]": 0.17616315399936866, + "courses/management/commands/test_manage_certificate.py::test_certificate_management_create_no_user": 0.27926396799011854, + "courses/management/commands/test_manage_certificate.py::test_certificate_management_invalid_run": 0.03474630400160095, + "courses/management/commands/test_manage_certificate.py::test_certificate_management_no_argument": 0.019499105990689714, + "courses/management/commands/test_manage_certificate.py::test_certificate_management_revoke_unrevoke_invalid_args[None-None-True]": 0.150913074008713, + "courses/management/commands/test_manage_certificate.py::test_certificate_management_revoke_unrevoke_invalid_args[None-True-None]": 0.1321630800011917, + "courses/management/commands/test_manage_certificate.py::test_certificate_management_revoke_unrevoke_invalid_args[test-None-True]": 0.16467089299840154, + "courses/management/commands/test_manage_certificate.py::test_certificate_management_revoke_unrevoke_invalid_args[test-True-None]": 0.14388502400106518, + "courses/management/commands/test_manage_certificate.py::test_certificate_management_revoke_unrevoke_success[None-True]": 0.1597663310021744, + "courses/management/commands/test_manage_certificate.py::test_certificate_management_revoke_unrevoke_success[True-None]": 0.15811699599726126, + "courses/management/commands/test_manage_certificate.py::test_certificate_override_grade_no_letter_grade": 0.13425461500446545, + "courses/management/commands/test_manage_certificate.py::test_certificate_override_grade_no_user": 0.14370888499979628, + "courses/management/tests/fix_eligible_course_flags_test.py::test_b2b_course[False-False]": 0.28890904400032014, + "courses/management/tests/fix_eligible_course_flags_test.py::test_b2b_course[False-True]": 0.28043151699966984, + "courses/management/tests/fix_eligible_course_flags_test.py::test_b2b_course[True-False]": 0.2696118449966889, + "courses/management/tests/fix_eligible_course_flags_test.py::test_b2b_course[True-True]": 0.27759940499527147, + "courses/management/tests/fix_eligible_course_flags_test.py::test_basic_eligible_course[False]": 0.1601343949951115, + "courses/management/tests/fix_eligible_course_flags_test.py::test_basic_eligible_course[True]": 0.1726764129998628, + "courses/management/tests/fix_eligible_course_flags_test.py::test_filter_by_course[1]": 0.46630577499308856, + "courses/management/tests/fix_eligible_course_flags_test.py::test_filter_by_course[3]": 0.7110928639958729, + "courses/management/tests/fix_eligible_course_flags_test.py::test_filter_by_program": 1.0228567000085604, + "courses/management/tests/import_courserun_test.py::TestImportCourserunCommand::test_bad_language": 0.07033529200271005, + "courses/management/tests/import_courserun_test.py::TestImportCourserunCommand::test_cms_page_flag_validation[False-False-False]": 0.01555143099540146, + "courses/management/tests/import_courserun_test.py::TestImportCourserunCommand::test_cms_page_flag_validation[False-True-False]": 0.015811353994649835, + "courses/management/tests/import_courserun_test.py::TestImportCourserunCommand::test_cms_page_flag_validation[True-False-False]": 0.015807920004590414, + "courses/management/tests/import_courserun_test.py::TestImportCourserunCommand::test_cms_page_flag_validation[True-True-True]": 0.0172556430043187, + "courses/management/tests/import_courserun_test.py::TestImportCourserunCommand::test_contract_validation_failure": 0.018410666001727805, + "courses/management/tests/import_courserun_test.py::TestImportCourserunCommand::test_program_without_run_tag": 0.29223772199475206, + "courses/management/tests/import_courserun_test.py::TestImportCourserunCommand::test_resolve_contract_by_id": 0.09835799600114115, + "courses/management/tests/import_courserun_test.py::TestImportCourserunCommand::test_resolve_contract_by_slug": 0.10266405799484346, + "courses/management/tests/import_courserun_test.py::TestImportCourserunCommand::test_resolve_contract_none_identifier": 0.01566359100252157, + "courses/management/tests/import_courserun_test.py::TestImportCourserunCommand::test_resolve_contract_not_found": 0.022609387000557035, + "courses/management/tests/import_courserun_test.py::TestImportCourserunCommand::test_run_tag_without_program": 0.017766040997230448, + "courses/management/tests/import_courserun_test.py::TestImportCourserunCommand::test_use_specific_course_flag_passed_to_api": 0.01706671400461346, + "courses/management/tests/import_courserun_test.py::TestImportCourserunCommand::test_variant_options[False]": 0.029712057003052905, + "courses/management/tests/import_courserun_test.py::TestImportCourserunCommand::test_variant_options[True]": 0.12885684500361094, + "courses/management/tests/manage_program_certificate_test.py::test_program_certificate_management_create[False]": 0.4320625460095471, + "courses/management/tests/manage_program_certificate_test.py::test_program_certificate_management_create[True]": 0.4343314349971479, + "courses/management/tests/manage_program_certificate_test.py::test_program_certificate_management_force_create": 1.1074896749996697, + "courses/management/tests/manage_program_certificate_test.py::test_program_certificate_management_invalid_program": 0.03142297199519817, + "courses/management/tests/manage_program_certificate_test.py::test_program_certificate_management_no_argument": 0.017371686997648794, + "courses/management/tests/manage_program_certificate_test.py::test_program_certificate_management_revoke_unrevoke_invalid_args[test-None-True]": 0.1388311359914951, + "courses/management/tests/manage_program_certificate_test.py::test_program_certificate_management_revoke_unrevoke_invalid_args[test-True-None]": 0.12425606700708158, + "courses/management/tests/manage_program_certificate_test.py::test_program_certificate_management_revoke_unrevoke_success[None-True]": 0.17090170100709656, + "courses/management/tests/manage_program_certificate_test.py::test_program_certificate_management_revoke_unrevoke_success[True-None]": 0.1835374150032294, + "courses/management/tests/migrate_certificate_revisions_test.py::test_migrate_certificate_revisions_all_confirmation[-course]": 0.2300049429977662, + "courses/management/tests/migrate_certificate_revisions_test.py::test_migrate_certificate_revisions_all_confirmation[-courserun]": 0.2010317739986931, + "courses/management/tests/migrate_certificate_revisions_test.py::test_migrate_certificate_revisions_all_confirmation[-program]": 0.1902392060001148, + "courses/management/tests/migrate_certificate_revisions_test.py::test_migrate_certificate_revisions_all_confirmation[Y-course]": 0.19976775199756958, + "courses/management/tests/migrate_certificate_revisions_test.py::test_migrate_certificate_revisions_all_confirmation[Y-courserun]": 0.2112345070054289, + "courses/management/tests/migrate_certificate_revisions_test.py::test_migrate_certificate_revisions_all_confirmation[Y-program]": 0.2341563450099784, + "courses/management/tests/migrate_certificate_revisions_test.py::test_migrate_certificate_revisions_all_confirmation[n-course]": 0.23440253600711003, + "courses/management/tests/migrate_certificate_revisions_test.py::test_migrate_certificate_revisions_all_confirmation[n-courserun]": 0.2341393939932459, + "courses/management/tests/migrate_certificate_revisions_test.py::test_migrate_certificate_revisions_all_confirmation[n-program]": 0.21509405299002538, + "courses/management/tests/migrate_certificate_revisions_test.py::test_migrate_certificate_revisions_all_confirmation[y-course]": 0.20272448899777373, + "courses/management/tests/migrate_certificate_revisions_test.py::test_migrate_certificate_revisions_all_confirmation[y-courserun]": 0.18948290399566758, + "courses/management/tests/migrate_certificate_revisions_test.py::test_migrate_certificate_revisions_all_confirmation[y-program]": 0.18977001599705545, + "courses/management/tests/migrate_certificate_revisions_test.py::test_migrate_certificate_revisions_certificate_page_has_no_revisions": 0.15178409199870657, + "courses/management/tests/migrate_certificate_revisions_test.py::test_migrate_certificate_revisions_course_no_certificate_page": 0.02846220299397828, + "courses/management/tests/migrate_certificate_revisions_test.py::test_migrate_certificate_revisions_missing_only[course]": 0.22483609400660498, + "courses/management/tests/migrate_certificate_revisions_test.py::test_migrate_certificate_revisions_missing_only[courserun]": 0.23157927099964581, + "courses/management/tests/migrate_certificate_revisions_test.py::test_migrate_certificate_revisions_missing_only[program]": 0.20551251799770398, + "courses/management/tests/migrate_certificate_revisions_test.py::test_migrate_certificate_revisions_validation_errors[handle_kwargs0-The command needs one of --course, --courserun, or --program.]": 0.022005007005645894, + "courses/management/tests/migrate_certificate_revisions_test.py::test_migrate_certificate_revisions_validation_errors[handle_kwargs1-Provide only one of --course, --courserun, or --program.]": 0.01929658699373249, + "courses/management/tests/migrate_certificate_revisions_test.py::test_migrate_certificate_revisions_validation_errors[handle_kwargs2-Provide only one of --course, --courserun, or --program.]": 0.015910169000562746, + "courses/management/tests/migrate_certificate_revisions_test.py::test_migrate_certificate_revisions_validation_errors[handle_kwargs3-Could not find course with readable_id=does-not-exist.]": 0.01817233199835755, + "courses/management/tests/migrate_certificate_revisions_test.py::test_migrate_certificate_revisions_validation_errors[handle_kwargs4-Could not find course run with courseware_id=does-not-exist.]": 0.021325631998479366, + "courses/management/tests/migrate_certificate_revisions_test.py::test_migrate_certificate_revisions_validation_errors[handle_kwargs5-Could not find program with readable_id=does-not-exist.]": 0.0204512119962601, + "courses/management/tests/report_missing_cms_pages_test.py::test_report_missing_cms_pages_all_buckets": 0.6004411969988723, + "courses/management/tests/report_missing_cms_pages_test.py::test_report_missing_cms_pages_eligible_users_only_filter": 0.10196239699871512, + "courses/management/tests/report_missing_cms_pages_test.py::test_report_missing_cms_pages_live_filter": 0.051532973993744235, + "courses/management/tests/unenroll_learners_test.py::TestBulkUnenrollCSV::test_csv_file_not_found": 0.01628412700665649, + "courses/management/tests/unenroll_learners_test.py::TestBulkUnenrollCSV::test_csv_missing_columns": 0.016601553004875313, + "courses/management/tests/unenroll_learners_test.py::TestBulkUnenrollCSV::test_csv_skips_empty_rows": 0.017023332991811913, + "courses/management/tests/unenroll_learners_test.py::TestBulkUnenrollCSV::test_csv_success": 0.017713371002173517, + "courses/management/tests/unenroll_learners_test.py::TestBulkUnenrollDryRun::test_default_is_dry_run": 0.13121891799528385, + "courses/management/tests/unenroll_learners_test.py::TestBulkUnenrollDryRun::test_dry_run_skips_inactive": 0.1481365090003237, + "courses/management/tests/unenroll_learners_test.py::TestBulkUnenrollDryRun::test_dry_run_user_not_found": 0.15408135899633635, + "courses/management/tests/unenroll_learners_test.py::TestBulkUnenrollInlineUsers::test_inline_users_keep_failed_enrollments": 0.018271427004947327, + "courses/management/tests/unenroll_learners_test.py::TestBulkUnenrollInlineUsers::test_inline_users_missing_run": 0.01645576199371135, + "courses/management/tests/unenroll_learners_test.py::TestBulkUnenrollInlineUsers::test_inline_users_mixed_results": 0.016988016999675892, + "courses/management/tests/unenroll_learners_test.py::TestBulkUnenrollInlineUsers::test_inline_users_success": 0.019381639009225182, + "courses/management/tests/unenroll_learners_test.py::TestBulkUnenrollInlineUsers::test_no_email_flag": 0.016361123001843225, + "courses/management/tests/unenroll_learners_test.py::TestBulkUnenrollLearnersUtil::test_course_run_not_found": 0.026461253000888973, + "courses/management/tests/unenroll_learners_test.py::TestBulkUnenrollLearnersUtil::test_deactivation_failure": 0.15221716000087326, + "courses/management/tests/unenroll_learners_test.py::TestBulkUnenrollLearnersUtil::test_keep_failed_enrollments_passed": 0.18761921200348297, + "courses/management/tests/unenroll_learners_test.py::TestBulkUnenrollLearnersUtil::test_no_active_enrollment": 0.1435232509902562, + "courses/management/tests/unenroll_learners_test.py::TestBulkUnenrollLearnersUtil::test_send_notification_passed": 0.1955428010041942, + "courses/management/tests/unenroll_learners_test.py::TestBulkUnenrollLearnersUtil::test_successful_unenrollment": 0.1417484720077482, + "courses/management/tests/unenroll_learners_test.py::TestBulkUnenrollLearnersUtil::test_user_not_found": 0.18132549499568995, + "courses/management/tests/unenroll_learners_test.py::TestBulkUnenrollNoArgs::test_csv_with_run_raises_error": 0.01551005600776989, + "courses/management/tests/unenroll_learners_test.py::TestBulkUnenrollNoArgs::test_csv_with_users_raises_error": 0.01609172399912495, + "courses/management/tests/unenroll_learners_test.py::TestBulkUnenrollNoArgs::test_no_args_raises_error": 0.017186042008688673, + "courses/management/tests/unenroll_learners_test.py::TestBulkUnenrollRunAlone::test_run_alone_dry_run_by_default": 0.14046643799520098, + "courses/management/tests/unenroll_learners_test.py::TestBulkUnenrollRunAlone::test_run_alone_no_active_enrollments": 0.13016006900579669, + "courses/management/tests/unenroll_learners_test.py::TestBulkUnenrollRunAlone::test_run_alone_nonexistent_run": 0.01740470400545746, + "courses/management/tests/unenroll_learners_test.py::TestBulkUnenrollRunAlone::test_run_alone_unenrolls_all_active": 0.14903804100322304, + "courses/management/utils_test.py::test_create_run_enrollment_edx_failure[EdxApiEnrollErrorException-inner_exception0-False]": 0.2496038410026813, + "courses/management/utils_test.py::test_create_run_enrollment_edx_failure[EdxApiEnrollErrorException-inner_exception0-True]": 0.23439762900670758, + "courses/management/utils_test.py::test_create_run_enrollment_edx_failure[UnknownEdxApiEnrollException-inner_exception1-False]": 0.26239539500238607, + "courses/management/utils_test.py::test_create_run_enrollment_edx_failure[UnknownEdxApiEnrollException-inner_exception1-True]": 0.27872585500153946, + "courses/management/utils_test.py::test_fetch_enrollment": 0.2524358490045415, + "courses/models_test.py::test_active_products_for_expired_course_run": 0.1591309020004701, + "courses/models_test.py::test_audit[False]": 0.22336200399149675, + "courses/models_test.py::test_audit[True]": 0.17346092299703741, + "courses/models_test.py::test_certificate_choice_limits": 0.295713578008872, + "courses/models_test.py::test_change_payment_to_run": 0.2523558500033687, + "courses/models_test.py::test_course_course_number": 0.0697788710021996, + "courses/models_test.py::test_course_creation_without_department": 0.02400138099619653, + "courses/models_test.py::test_course_first_unexpired_run": 0.1449905530025717, + "courses/models_test.py::test_course_next_run_multiple_languages[False-False-False]": 0.13336990900279488, + "courses/models_test.py::test_course_next_run_multiple_languages[False-False-True]": 0.1558411209989572, + "courses/models_test.py::test_course_next_run_multiple_languages[False-True-False]": 0.16100298701348947, + "courses/models_test.py::test_course_next_run_multiple_languages[False-True-True]": 0.14803086000028998, + "courses/models_test.py::test_course_next_run_multiple_languages[True-False-False]": 0.2766795150018879, + "courses/models_test.py::test_course_next_run_multiple_languages[True-False-True]": 0.23056171400094172, + "courses/models_test.py::test_course_next_run_multiple_languages[True-True-False]": 0.24922746500669746, + "courses/models_test.py::test_course_next_run_multiple_languages[True-True-True]": 0.2651251530041918, + "courses/models_test.py::test_course_run_certificate_clean_allows_missing_page_revision": 0.1607076110012713, + "courses/models_test.py::test_course_run_certificate_save_without_certificate_child_page": 0.30068121499789413, + "courses/models_test.py::test_course_run_certificate_save_without_course_page": 0.060372214000381064, + "courses/models_test.py::test_course_run_certificate_start_end_dates_and_page_revision": 0.19409533699217718, + "courses/models_test.py::test_course_run_expiration_date[-1-2-3]": 0.17020616499939933, + "courses/models_test.py::test_course_run_expiration_date[1-3-4]": 0.1861179070037906, + "courses/models_test.py::test_course_run_expiration_date[10-20-30]": 0.1358863639907213, + "courses/models_test.py::test_course_run_in_progress[-1-2-True]": 0.1397940379974898, + "courses/models_test.py::test_course_run_in_progress[-1-None-True]": 0.13097589999233605, + "courses/models_test.py::test_course_run_in_progress[-2--1-False]": 0.15849256699584657, + "courses/models_test.py::test_course_run_in_progress[None-2-False]": 0.1601549880069797, + "courses/models_test.py::test_course_run_invalid_expiration_date[1-2--1]": 0.13602703901415225, + "courses/models_test.py::test_course_run_invalid_expiration_date[1-2-1]": 0.12028394599474268, + "courses/models_test.py::test_course_run_past[-1-True]": 0.13904623800772242, + "courses/models_test.py::test_course_run_past[1-False]": 0.14081389200146077, + "courses/models_test.py::test_course_run_past[None-False]": 0.1586105260066688, + "courses/models_test.py::test_course_run_upgradeable[-1-True-True-False]": 0.14079865299572702, + "courses/models_test.py::test_course_run_upgradeable[1-False-True-False]": 0.12778298601187998, + "courses/models_test.py::test_course_run_upgradeable[1-True-False-False]": 0.14633779100404354, + "courses/models_test.py::test_course_run_upgradeable[1-True-True-True]": 0.14535521100333426, + "courses/models_test.py::test_course_run_upgradeable[None-False-False-False]": 0.15448355799162528, + "courses/models_test.py::test_course_run_upgradeable[None-False-True-False]": 0.1628481070001726, + "courses/models_test.py::test_course_run_upgradeable[None-True-False-False]": 0.17845007599680685, + "courses/models_test.py::test_course_run_upgradeable[None-True-True-True]": 0.18310557400400285, + "courses/models_test.py::test_courserun_different_tags_same_language_allowed": 0.15816939099750016, + "courses/models_test.py::test_courserun_language_null_not_constrained": 0.15814302500075428, + "courses/models_test.py::test_courserun_language_unique_constraint": 0.14980220200231997, + "courses/models_test.py::test_courserun_qs_b2b_flags": 0.7065630400029477, + "courses/models_test.py::test_courses_in_program": 1.158164068001497, + "courses/models_test.py::test_courseware_url": 0.07288679699558998, + "courses/models_test.py::test_courseware_url_default_has_courseware_url_true": 0.0831440520050819, + "courses/models_test.py::test_courseware_url_none_when_has_courseware_url_false": 0.07537198900536168, + "courses/models_test.py::test_courseware_url_with_trailing_slash_base_url": 0.08946180500061018, + "courses/models_test.py::test_create_program_without_department": 0.025868897995678708, + "courses/models_test.py::test_deactivate_and_save": 0.3528330420012935, + "courses/models_test.py::test_enrollment_is_ended": 0.27324270300596254, + "courses/models_test.py::test_get_courseware_url_utility_function": 0.020806214000913315, + "courses/models_test.py::test_get_courseware_url_with_none": 0.021228174002317246, + "courses/models_test.py::test_get_program_run_enrollments": 0.5030689570048708, + "courses/models_test.py::test_nonvariant_filter": 0.19035793399234535, + "courses/models_test.py::test_program_add_elective": 0.30931113000406185, + "courses/models_test.py::test_program_add_program_elective": 0.27960344000166515, + "courses/models_test.py::test_program_add_program_requirement": 0.2549046769927372, + "courses/models_test.py::test_program_add_requirement": 0.2593174880021252, + "courses/models_test.py::test_program_certificate_start_end_dates_and_page_revision": 0.37820594599907054, + "courses/models_test.py::test_program_elective_programs_property": 0.4659286789901671, + "courses/models_test.py::test_program_first_unexpired_run": 0.3603841929943883, + "courses/models_test.py::test_program_minimum_elective_courses_requirement": 0.13729645199055085, + "courses/models_test.py::test_program_minimum_elective_courses_requirement_no_elective_node": 0.14551750299870037, + "courses/models_test.py::test_program_num_courses": 0.3735623470056453, + "courses/models_test.py::test_program_required_programs_property": 0.41020249799476005, + "courses/models_test.py::test_program_requirement_node_types": 0.22160030800296227, + "courses/models_test.py::test_program_requirement_properties_use_prefetched_all_requirements": 0.8970300599976326, + "courses/models_test.py::test_program_requirements": 0.8861320619980688, + "courses/models_test.py::test_program_requirements_is_all_of_operator[all_of-True]": 0.0187827150002704, + "courses/models_test.py::test_program_requirements_is_all_of_operator[min_number_of-False]": 0.01712917600525543, + "courses/models_test.py::test_program_requirements_is_course": 0.018076639003993478, + "courses/models_test.py::test_program_requirements_is_min_number_of_operator[all_of-False]": 0.015320455997425597, + "courses/models_test.py::test_program_requirements_is_min_number_of_operator[min_number_of-True]": 0.015720513998530805, + "courses/models_test.py::test_program_requirements_is_operator": 0.01574925000750227, + "courses/models_test.py::test_program_requirements_is_root": 0.02101980799488956, + "courses/models_test.py::test_program_requirements_root_node_collation": 8.050764942003298, + "courses/models_test.py::test_reactivate_and_save": 0.31827438499749405, + "courses/models_test.py::test_readable_id_invalid[ somevalue]": 0.0954500939915306, + "courses/models_test.py::test_readable_id_invalid[/somevalue]": 0.09065314999315888, + "courses/models_test.py::test_readable_id_invalid[]": 0.10815971700503724, + "courses/models_test.py::test_readable_id_invalid[some value]": 0.09406820299773244, + "courses/models_test.py::test_readable_id_invalid[some/value]": 0.08539460899919504, + "courses/models_test.py::test_readable_id_invalid[somevalue ]": 0.08467083699360956, + "courses/models_test.py::test_readable_id_invalid[somevalue/]": 0.11135706700588344, + "courses/models_test.py::test_readable_id_valid[some+value]": 0.1001986449919059, + "courses/models_test.py::test_readable_id_valid[some-value]": 0.10945632400398608, + "courses/models_test.py::test_readable_id_valid[some:value]": 0.10396576599305263, + "courses/models_test.py::test_readable_id_valid[some_value]": 0.11922040500212461, + "courses/models_test.py::test_readable_id_valid[somevalue]": 0.10906300599890528, + "courses/models_test.py::test_related_programs": 0.445317164005246, + "courses/models_test.py::test_run_language_constraints_regular_duplication": 0.11703999499877682, + "courses/models_test.py::test_run_language_constraints_regular_languages[False-False]": 0.29215126499912003, + "courses/models_test.py::test_run_language_constraints_regular_languages[False-True]": 0.2945109080101247, + "courses/models_test.py::test_run_language_constraints_regular_languages[True-False]": 0.312471926008584, + "courses/models_test.py::test_run_language_constraints_regular_languages[True-True]": 0.31347751100111054, + "courses/serializers/utils_test.py::TestGetTopicsFromPage::test_get_topics_from_page_preserves_sorting_with_unicode_characters": 0.020798965997528285, + "courses/serializers/utils_test.py::TestGetTopicsFromPage::test_get_topics_from_page_single_topic_with_parent": 0.020404663991939742, + "courses/serializers/utils_test.py::TestGetTopicsFromPage::test_get_topics_from_page_with_course_page_object": 0.13641290600207867, + "courses/serializers/utils_test.py::TestGetTopicsFromPage::test_get_topics_from_page_with_direct_topics_only": 0.02432297700579511, + "courses/serializers/utils_test.py::TestGetTopicsFromPage::test_get_topics_from_page_with_empty_topics": 0.017554889993334655, + "courses/serializers/utils_test.py::TestGetTopicsFromPage::test_get_topics_from_page_with_mixed_parent_and_orphan_topics": 0.020532918002572842, + "courses/serializers/utils_test.py::TestGetTopicsFromPage::test_get_topics_from_page_with_multiple_parent_topics": 0.02493366900307592, + "courses/serializers/utils_test.py::TestGetTopicsFromPage::test_get_topics_from_page_with_none_page_instance": 0.019222978997277096, + "courses/serializers/utils_test.py::TestGetTopicsFromPage::test_get_topics_from_page_with_parent_topics": 0.02484256000025198, + "courses/serializers/v1/base_test.py::test_base_course_serializer": 0.12203876300191041, + "courses/serializers/v1/base_test.py::test_base_program_serializer": 0.14244198700180277, + "courses/serializers/v1/courses_test.py::test_serialize_course[False-False]": 0.3989469259904581, + "courses/serializers/v1/courses_test.py::test_serialize_course[False-True]": 0.39166249999107094, + "courses/serializers/v1/courses_test.py::test_serialize_course[True-False]": 0.3951959129990428, + "courses/serializers/v1/courses_test.py::test_serialize_course[True-True]": 0.48159004098852165, + "courses/serializers/v1/courses_test.py::test_serialize_course_run": 0.04319409000163432, + "courses/serializers/v1/courses_test.py::test_serialize_course_run_enrollments[False]": 0.21866767600295134, + "courses/serializers/v1/courses_test.py::test_serialize_course_run_enrollments[True]": 0.18619215899525443, + "courses/serializers/v1/courses_test.py::test_serialize_course_run_enrollments_with_flexible_pricing[False]": 0.2571293080036412, + "courses/serializers/v1/courses_test.py::test_serialize_course_run_enrollments_with_flexible_pricing[True]": 0.27345349899405846, + "courses/serializers/v1/courses_test.py::test_serialize_course_run_enrollments_with_grades": 0.17948178300866857, + "courses/serializers/v1/courses_test.py::test_serialize_course_run_with_course": 0.03955174699513009, + "courses/serializers/v1/courses_test.py::test_serialize_course_with_page_fields[False]": 0.14350991600076668, + "courses/serializers/v1/courses_test.py::test_serialize_course_with_page_fields[True]": 0.1704413389961701, + "courses/serializers/v1/programs_test.py::test_learner_record_serializer[audit]": 0.5097499240000616, + "courses/serializers/v1/programs_test.py::test_learner_record_serializer[verified]": 0.5616840239963494, + "courses/serializers/v1/programs_test.py::test_program_requirement_deletion": 0.5440423199979705, + "courses/serializers/v1/programs_test.py::test_program_requirement_serializer_valid[data0]": 0.016654833998472895, + "courses/serializers/v1/programs_test.py::test_program_requirement_serializer_valid[data1]": 0.01728730899776565, + "courses/serializers/v1/programs_test.py::test_program_requirement_serializer_valid[data2]": 0.015484532996197231, + "courses/serializers/v1/programs_test.py::test_program_requirement_serializer_valid[data3]": 0.016858953997143544, + "courses/serializers/v1/programs_test.py::test_program_requirement_tree_serializer_save": 0.4276180589949945, + "courses/serializers/v1/programs_test.py::test_program_serializer_returns_null_image_when_no_page": 0.034652946997084655, + "courses/serializers/v1/programs_test.py::test_serialize_program[False]": 0.4299639059972833, + "courses/serializers/v1/programs_test.py::test_serialize_program[True]": 0.2855499180004699, + "courses/serializers/v2/certificates_test.py::test_serialize_certificate[False]": 0.3133592369922553, + "courses/serializers/v2/certificates_test.py::test_serialize_certificate[True]": 0.35518729299656115, + "courses/serializers/v2/courses_test.py::TestCourseRunEnrollmentSerializerV2::test_serializer_fields": 0.18140510100056417, + "courses/serializers/v2/courses_test.py::TestCourseRunEnrollmentSerializerV2::test_serializer_with_b2b_contract": 0.2517063840132323, + "courses/serializers/v2/courses_test.py::TestCourseRunEnrollmentSerializerV2::test_serializer_without_b2b_contract": 0.21005636900372338, + "courses/serializers/v2/courses_test.py::TestUserEnrollmentFiltering::test_exclude_b2b_filter_logic": 0.31174203700356884, + "courses/serializers/v2/courses_test.py::TestUserEnrollmentFiltering::test_org_id_filter_logic": 0.34639177699864376, + "courses/serializers/v2/courses_test.py::test_course_serializer_canonical_run_fallback_to_oldest": 0.19875731199863367, + "courses/serializers/v2/courses_test.py::test_course_serializer_canonical_run_per_tag": 0.1668288470027619, + "courses/serializers/v2/courses_test.py::test_serialize_course[False-Certificate of Completion-False]": 0.4107432449964108, + "courses/serializers/v2/courses_test.py::test_serialize_course[False-Certificate of Completion-True]": 0.38079798600665526, + "courses/serializers/v2/courses_test.py::test_serialize_course[False-MicroMasters Credential-False]": 0.3545452450052835, + "courses/serializers/v2/courses_test.py::test_serialize_course[False-MicroMasters Credential-True]": 0.3508296549989609, + "courses/serializers/v2/courses_test.py::test_serialize_course[True-Certificate of Completion-False]": 0.34801889800291974, + "courses/serializers/v2/courses_test.py::test_serialize_course[True-Certificate of Completion-True]": 0.40779416800796753, + "courses/serializers/v2/courses_test.py::test_serialize_course[True-MicroMasters Credential-False]": 0.426549673997215, + "courses/serializers/v2/courses_test.py::test_serialize_course[True-MicroMasters Credential-True]": 0.7195934200062766, + "courses/serializers/v2/courses_test.py::test_serialize_course_required_prerequisites[None]": 0.2398332459924859, + "courses/serializers/v2/courses_test.py::test_serialize_course_required_prerequisites[]": 0.17158033600571798, + "courses/serializers/v2/courses_test.py::test_serialize_course_required_prerequisites[mock value]": 0.19236597399867605, + "courses/serializers/v2/courses_test.py::test_serialize_course_with_no_page": 0.0663354310017894, + "courses/serializers/v2/departments_test.py::test_serialize_department": 0.03222876499785343, + "courses/serializers/v2/departments_test.py::test_serialize_department_with_courses_and_programs__no_related": 0.25529660000029253, + "courses/serializers/v2/departments_test.py::test_serialize_department_with_courses_and_programs__with_multiples[0-0-0-0]": 0.03324514399719192, + "courses/serializers/v2/departments_test.py::test_serialize_department_with_courses_and_programs__with_multiples[0-0-1-1]": 0.041577083997253794, + "courses/serializers/v2/departments_test.py::test_serialize_department_with_courses_and_programs__with_multiples[1-1-0-0]": 0.2600029040040681, + "courses/serializers/v2/departments_test.py::test_serialize_department_with_courses_and_programs__with_multiples[2-2-0-0]": 0.4282837390055647, + "courses/serializers/v2/departments_test.py::test_serialize_department_with_courses_and_programs__with_multiples[2-2-1-1]": 0.42962466499739094, + "courses/serializers/v2/programs_test.py::test_program_requirement_tree_serializer_save": 0.4271919929960859, + "courses/serializers/v2/programs_test.py::test_serialize_program[-Certificate of Completion-False]": 0.5080502159980824, + "courses/serializers/v2/programs_test.py::test_serialize_program[-Certificate of Completion-True]": 0.39439994099666364, + "courses/serializers/v2/programs_test.py::test_serialize_program[-MicroMasters Credential-False]": 0.5054610119987046, + "courses/serializers/v2/programs_test.py::test_serialize_program[-MicroMasters Credential-True]": 0.36705514899949776, + "courses/serializers/v2/programs_test.py::test_serialize_program[None-Certificate of Completion-False]": 0.5044686679975712, + "courses/serializers/v2/programs_test.py::test_serialize_program[None-Certificate of Completion-True]": 0.35291684900585096, + "courses/serializers/v2/programs_test.py::test_serialize_program[None-MicroMasters Credential-False]": 0.47786004299268825, + "courses/serializers/v2/programs_test.py::test_serialize_program[None-MicroMasters Credential-True]": 0.41435634599474724, + "courses/serializers/v2/programs_test.py::test_serialize_program[program prerequisites-Certificate of Completion-False]": 0.5682624030087027, + "courses/serializers/v2/programs_test.py::test_serialize_program[program prerequisites-Certificate of Completion-True]": 0.415627159003634, + "courses/serializers/v2/programs_test.py::test_serialize_program[program prerequisites-MicroMasters Credential-False]": 0.5328166399995098, + "courses/serializers/v2/programs_test.py::test_serialize_program[program prerequisites-MicroMasters Credential-True]": 0.39859514100680826, + "courses/serializers/v2/programs_test.py::test_serialize_program_certificate_available[False-False]": 0.20269809100136627, + "courses/serializers/v2/programs_test.py::test_serialize_program_certificate_available[True-True]": 0.21228426399466116, + "courses/serializers/v2/programs_test.py::test_serialize_program_programs_excludes_non_live[False-False-False]": 0.30775155900482787, + "courses/serializers/v2/programs_test.py::test_serialize_program_programs_excludes_non_live[False-True-False]": 0.32097071300813695, + "courses/serializers/v2/programs_test.py::test_serialize_program_programs_excludes_non_live[True-False-False]": 0.27973636900424026, + "courses/serializers/v2/programs_test.py::test_serialize_program_programs_excludes_non_live[True-True-True]": 0.31580251899868017, + "courses/serializers/v2/programs_test.py::test_serialize_program_requirements_use_all_requirements_prefetch_cache": 0.938068617011595, + "courses/serializers/v2/programs_test.py::test_serialize_program_topics_use_all_requirements_prefetch_cache": 1.0114266960008536, + "courses/serializers/v2/programs_test.py::test_serialize_program_uses_collection_membership_prefetch_cache": 0.2576748490027967, + "courses/serializers/v2/programs_test.py::test_serialize_program_uses_prefetched_collection_memberships": 0.24859008199564414, + "courses/serializers/v2/programs_test.py::test_serialize_program_with_no_page": 0.05431431699253153, + "courses/serializers/v2/programs_test.py::test_serialize_program_with_products": 0.21412298399809515, + "courses/serializers/v3/courses_test.py::TestCourseRunEnrollmentSerializerV3::test_serializer_fields": 0.17831165900133783, + "courses/serializers/v3/courses_test.py::TestCourseRunEnrollmentSerializerV3::test_serializer_includes_upgrade_fields_for_upgradable_run": 0.18644657100230688, + "courses/serializers/v3/courses_test.py::TestCourseRunEnrollmentSerializerV3::test_serializer_upgrade_fields_null_when_not_eligible": 0.14734333399974275, + "courses/serializers/v3/courses_test.py::TestCourseRunEnrollmentSerializerV3::test_serializer_with_b2b_contract": 0.2615480770036811, + "courses/serializers/v3/courses_test.py::TestCourseRunEnrollmentSerializerV3::test_serializer_without_b2b_contract": 0.18543851299909875, + "courses/serializers/v3/programs_test.py::test_serialize_program_enrollment[False]": 0.1699198110072757, + "courses/serializers/v3/programs_test.py::test_serialize_program_enrollment[True]": 0.13507640399620868, + "courses/signals_test.py::test_create_course_certificate": 0.5553417050032294, + "courses/signals_test.py::test_generate_program_certificate_if_not_live": 0.5416618539966294, + "courses/signals_test.py::test_generate_program_certificate_not_called": 0.3539490230032243, + "courses/signals_test.py::test_purge_fastly_cache_on_course_run_save": 0.31016780299978564, + "courses/signals_test.py::test_purge_fastly_cache_on_course_save_update": 0.2739011890007532, + "courses/signals_test.py::test_purge_fastly_cache_on_program_save": 0.2833406680074404, + "courses/signals_test.py::test_sync_course_certificate_with_hubspot_on_save": 0.32162074399820995, + "courses/signals_test.py::test_sync_program_certificate_with_hubspot_on_save": 0.32116314800077816, + "courses/tasks_test.py::test_generate_course_certificates_task": 0.01901078700029757, + "courses/tasks_test.py::test_generate_program_certificates_task": 0.020639867994759697, + "courses/tasks_test.py::test_send_partner_school_email": 0.17020742599561345, + "courses/tasks_test.py::test_subscribe_edx_course_emails": 0.1916224050000892, + "courses/utils_test.py::test_get_dated_courseruns": 0.3652899000007892, + "courses/utils_test.py::test_get_enrollable_courseruns_qs": 0.1563521399948513, + "courses/utils_test.py::test_get_enrollable_courses": 0.2538473869935842, + "courses/utils_test.py::test_get_program_certificate_by_enrollment": 0.9186727289998089, + "courses/utils_test.py::test_get_program_certificate_by_enrollment_program_certificate_page_does_not_exist": 1.1171412879994023, + "courses/utils_test.py::test_get_program_certificate_by_enrollment_program_does_not_exist": 0.1579216549944249, + "courses/utils_test.py::test_get_program_certificate_by_enrollment_program_page_does_not_exist": 1.046268964993942, + "courses/utils_test.py::test_get_unenrollable_courses": 0.19921810198866297, + "courses/utils_test.py::test_is_contract_order_false_for_course_run_without_contract": 0.014724968001246452, + "courses/utils_test.py::test_is_contract_order_false_for_non_course_run_product": 0.012916371000756044, + "courses/utils_test.py::test_is_contract_order_true_for_course_run_with_contract": 0.015798868000274524, + "courses/utils_test.py::test_is_uai_course_run[UAI_MIT+1.001x+2025_C12-True]": 0.13302611000835896, + "courses/utils_test.py::test_is_uai_course_run[course-v1:MITx+6.00.1x+1T2026-False]": 0.1489805680103018, + "courses/utils_test.py::test_is_uai_course_run[course-v1:UAI_MIT+1.001x+2025_C12-True]": 0.14169794500776334, + "courses/utils_test.py::test_is_uai_order_detects_uai_course_runs_and_programs[purchasable_object0-True]": 0.1374209370042081, + "courses/utils_test.py::test_is_uai_order_detects_uai_course_runs_and_programs[purchasable_object1-True]": 0.15330330500000855, + "courses/utils_test.py::test_is_uai_order_detects_uai_course_runs_and_programs[purchasable_object2-False]": 0.13276646699523553, + "courses/utils_test.py::test_is_uai_order_detects_uai_course_runs_and_programs[purchasable_object3-False]": 0.11755019900010666, + "courses/utils_test.py::test_is_uai_order_uses_purchased_object_when_available": 0.012311589998716954, + "courses/views/internal/views_test.py::test_get_ingestible_courses[False-100-20]": 31.101650127995526, + "courses/views/internal/views_test.py::test_get_ingestible_courses[True-100-20]": 31.496146693985793, + "courses/views/internal/views_test.py::test_is_etl_permission[False-False-100-20]": 12.824486808000074, + "courses/views/internal/views_test.py::test_is_etl_permission[False-True-100-20]": 12.903860681995866, + "courses/views/internal/views_test.py::test_is_etl_permission[True-False-100-20]": 13.799842331995023, + "courses/views/internal/views_test.py::test_is_etl_permission[True-True-100-20]": 12.833173653998529, + "courses/views/utils_test.py::test_is_etl_permission[False-False]": 0.025651408002886456, + "courses/views/utils_test.py::test_is_etl_permission[False-True]": 0.02701928799797315, + "courses/views/utils_test.py::test_is_etl_permission[True-False]": 0.028729427001962904, + "courses/views/utils_test.py::test_is_etl_permission[True-True]": 0.03893846600112738, + "courses/views/v1/views_test.py::TestCourseFilterSet::test_filter_courserun_is_enrollable_false": 0.1869492719997652, + "courses/views/v1/views_test.py::TestCourseFilterSet::test_filter_courserun_is_enrollable_true": 0.17445399099960923, + "courses/views/v1/views_test.py::TestProgramViewSetPagination::test_paginate_queryset_no_page_param": 0.021108547000039835, + "courses/views/v1/views_test.py::TestProgramViewSetPagination::test_paginate_queryset_with_page_param": 0.02230734300246695, + "courses/views/v1/views_test.py::TestUserEnrollmentsApiViewSetSync::test_list_with_sync_disabled": 0.032932093999988865, + "courses/views/v1/views_test.py::TestUserEnrollmentsApiViewSetSync::test_list_with_sync_enabled_exception": 0.0327644680000958, + "courses/views/v1/views_test.py::TestUserEnrollmentsApiViewSetSync::test_list_with_sync_enabled_success": 0.0288530690013431, + "courses/views/v1/views_test.py::test_create_course[1-1]": 0.3166511629970046, + "courses/views/v1/views_test.py::test_create_course_run": 0.3385376010046457, + "courses/views/v1/views_test.py::test_create_enrollments[False-False-False]": 0.2264087860021391, + "courses/views/v1/views_test.py::test_create_enrollments[False-False-True]": 0.22700619699753588, + "courses/views/v1/views_test.py::test_create_enrollments[False-True-False]": 0.25315798899828224, + "courses/views/v1/views_test.py::test_create_enrollments[False-True-True]": 0.26146276800864143, + "courses/views/v1/views_test.py::test_create_enrollments[True-False-False]": 0.24100481999630574, + "courses/views/v1/views_test.py::test_create_enrollments[True-False-True]": 0.26065252300031716, + "courses/views/v1/views_test.py::test_create_enrollments[True-True-False]": 0.2742374320005183, + "courses/views/v1/views_test.py::test_create_enrollments[True-True-True]": 0.28313639300176874, + "courses/views/v1/views_test.py::test_create_enrollments_blocked_country": 0.1918311889967299, + "courses/views/v1/views_test.py::test_create_enrollments_failed": 0.17958982599520823, + "courses/views/v1/views_test.py::test_create_enrollments_no_run": 0.03841489199839998, + "courses/views/v1/views_test.py::test_create_enrollments_with_existing_fulfilled_order[False]": 0.25356510300480295, + "courses/views/v1/views_test.py::test_create_enrollments_with_existing_fulfilled_order[True]": 0.24803792500460986, + "courses/views/v1/views_test.py::test_create_program[1-1]": 0.33347081299871206, + "courses/views/v1/views_test.py::test_delete_course[1-1]": 0.26572332799696596, + "courses/views/v1/views_test.py::test_delete_course_run": 0.32479298100224696, + "courses/views/v1/views_test.py::test_delete_program[1-1]": 0.23709039400273468, + "courses/views/v1/views_test.py::test_destroy_program_enrollment_paid_fails": 0.1682018049978069, + "courses/views/v1/views_test.py::test_get_course[1-1]": 0.5290322139990167, + "courses/views/v1/views_test.py::test_get_course_by_readable_id[False-False-1-1]": 0.48797205599112203, + "courses/views/v1/views_test.py::test_get_course_by_readable_id[False-True-1-1]": 0.4811975569900824, + "courses/views/v1/views_test.py::test_get_course_by_readable_id[True-False-1-1]": 1.0327214139979333, + "courses/views/v1/views_test.py::test_get_course_by_readable_id[True-True-1-1]": 0.48877840499335434, + "courses/views/v1/views_test.py::test_get_course_run": 0.4926159739916329, + "courses/views/v1/views_test.py::test_get_course_runs": 0.6977117860005819, + "courses/views/v1/views_test.py::test_get_course_runs_relevant[False]": 0.4602297190067475, + "courses/views/v1/views_test.py::test_get_course_runs_relevant[True]": 0.48225249200186227, + "courses/views/v1/views_test.py::test_get_course_runs_relevant_missing": 0.03825868899730267, + "courses/views/v1/views_test.py::test_get_courses[15-100]": 25.901992918996257, + "courses/views/v1/views_test.py::test_get_learner_record": 0.1524909759973525, + "courses/views/v1/views_test.py::test_get_learner_record_requires_program_enrollment": 0.12998085200524656, + "courses/views/v1/views_test.py::test_get_program[1-1]": 0.6174875049982802, + "courses/views/v1/views_test.py::test_get_programs[1-1]": 0.6255758369952673, + "courses/views/v1/views_test.py::test_get_shared_learner_record": 0.19674095100344857, + "courses/views/v1/views_test.py::test_get_shared_learner_record_inactive_share_returns_404": 0.16623887100286083, + "courses/views/v1/views_test.py::test_patch_course[1-1]": 0.2883200930082239, + "courses/views/v1/views_test.py::test_patch_course_run": 0.37862432800466195, + "courses/views/v1/views_test.py::test_patch_program[1-1]": 0.27615512100601336, + "courses/views/v1/views_test.py::test_program_enrollment_destroy": 0.25801060600497294, + "courses/views/v1/views_test.py::test_program_enrollment_destroy_no_enrollment_is_idempotent": 0.13259404800191987, + "courses/views/v1/views_test.py::test_program_enrollment_destroy_program_not_found": 0.02973434600426117, + "courses/views/v1/views_test.py::test_program_enrollments": 9.48765241799265, + "courses/views/v1/views_test.py::test_program_enrollments_future_program_cert": 0.2127760930088698, + "courses/views/v1/views_test.py::test_revoke_learner_record_share": 0.13695323700085282, + "courses/views/v1/views_test.py::test_revoke_learner_record_share_requires_program_enrollment": 0.11997378199157538, + "courses/views/v1/views_test.py::test_share_learner_record": 0.15633491100015817, + "courses/views/v1/views_test.py::test_share_learner_record_partner_school": 0.1440660320004099, + "courses/views/v1/views_test.py::test_share_learner_record_requires_program_enrollment": 0.12558686999545898, + "courses/views/v1/views_test.py::test_update_user_enrollment[False]": 0.17364543700387003, + "courses/views/v1/views_test.py::test_update_user_enrollment[True]": 0.17743716201221105, + "courses/views/v1/views_test.py::test_update_user_enrollment_failure[ConnectionError-False]": 0.16216223000810714, + "courses/views/v1/views_test.py::test_update_user_enrollment_failure[ConnectionError-True]": 0.14480437699967297, + "courses/views/v1/views_test.py::test_update_user_enrollment_failure[HTTPError-False]": 0.1523819579888368, + "courses/views/v1/views_test.py::test_update_user_enrollment_failure[HTTPError-True]": 0.14958174600906204, + "courses/views/v1/views_test.py::test_update_user_enrollment_failure[NoEdxApiAuthError-False]": 0.16957604700291995, + "courses/views/v1/views_test.py::test_update_user_enrollment_failure[NoEdxApiAuthError-True]": 0.15118422899831785, + "courses/views/v1/views_test.py::test_user_enrollment_delete[False-True-204]": 0.21589954699447844, + "courses/views/v1/views_test.py::test_user_enrollment_delete[True-False-400]": 0.24112921501364326, + "courses/views/v1/views_test.py::test_user_enrollment_delete_other_fail": 0.17020494099415373, + "courses/views/v1/views_test.py::test_user_enrollments_create[False]": 0.3257191000011517, + "courses/views/v1/views_test.py::test_user_enrollments_create[True]": 0.29461078500025906, + "courses/views/v1/views_test.py::test_user_enrollments_create_b2b_run_invalid": 0.2151088320024428, + "courses/views/v1/views_test.py::test_user_enrollments_create_invalid": 0.03152605199284153, + "courses/views/v1/views_test.py::test_user_enrollments_future_course_cert": 0.2647592980065383, + "courses/views/v1/views_test.py::test_user_enrollments_list": 0.36847656399186235, + "courses/views/v1/views_test.py::test_user_enrollments_list_sync[False]": 0.03915886700269766, + "courses/views/v1/views_test.py::test_user_enrollments_list_sync[True]": 0.04049367800325854, + "courses/views/v1/views_test.py::test_user_enrollments_list_sync_fail[HTTPError]": 0.03295516700745793, + "courses/views/v1/views_test.py::test_user_enrollments_list_sync_fail[NoEdxApiAuthError]": 0.03666517999226926, + "courses/views/v1/views_test.py::test_user_enrollments_list_sync_fail[ValueError]": 0.03336469499481609, + "courses/views/v2/views_test.py::test_add_nested_verified_program_course_enrollment[None-None-False]": 0.29144805599207757, + "courses/views/v2/views_test.py::test_add_nested_verified_program_course_enrollment[None-None-True]": 0.27614772899687523, + "courses/views/v2/views_test.py::test_add_nested_verified_program_course_enrollment[None-audit-False]": 0.7962020329941879, + "courses/views/v2/views_test.py::test_add_nested_verified_program_course_enrollment[None-audit-True]": 0.32844296200346434, + "courses/views/v2/views_test.py::test_add_nested_verified_program_course_enrollment[None-verified-False]": 1.5366505140118534, + "courses/views/v2/views_test.py::test_add_nested_verified_program_course_enrollment[None-verified-True]": 0.34524012600741116, + "courses/views/v2/views_test.py::test_add_nested_verified_program_course_enrollment[audit-None-False]": 0.827688825003861, + "courses/views/v2/views_test.py::test_add_nested_verified_program_course_enrollment[audit-None-True]": 0.40317631099605933, + "courses/views/v2/views_test.py::test_add_nested_verified_program_course_enrollment[audit-audit-False]": 0.7692536970062065, + "courses/views/v2/views_test.py::test_add_nested_verified_program_course_enrollment[audit-audit-True]": 0.3519778940026299, + "courses/views/v2/views_test.py::test_add_nested_verified_program_course_enrollment[audit-verified-False]": 1.459265885001514, + "courses/views/v2/views_test.py::test_add_nested_verified_program_course_enrollment[audit-verified-True]": 0.37143153400393203, + "courses/views/v2/views_test.py::test_add_nested_verified_program_course_enrollment[verified-None-False]": 0.3492251329953433, + "courses/views/v2/views_test.py::test_add_nested_verified_program_course_enrollment[verified-None-True]": 0.38060308499552775, + "courses/views/v2/views_test.py::test_add_nested_verified_program_course_enrollment[verified-audit-False]": 0.33150969199778046, + "courses/views/v2/views_test.py::test_add_nested_verified_program_course_enrollment[verified-audit-True]": 0.3605873810010962, + "courses/views/v2/views_test.py::test_add_nested_verified_program_course_enrollment[verified-verified-False]": 1.2477894990079221, + "courses/views/v2/views_test.py::test_add_nested_verified_program_course_enrollment[verified-verified-True]": 0.38731410200853134, + "courses/views/v2/views_test.py::test_add_verified_program_course_enrollment[None-requirement]": 0.22638923799968325, + "courses/views/v2/views_test.py::test_add_verified_program_course_enrollment[audit-elective-extra]": 0.6437126329983585, + "courses/views/v2/views_test.py::test_add_verified_program_course_enrollment[audit-elective]": 0.6284579469938762, + "courses/views/v2/views_test.py::test_add_verified_program_course_enrollment[audit-requirement]": 0.6135853939995286, + "courses/views/v2/views_test.py::test_add_verified_program_course_enrollment[verified-elective-extra]": 1.2264520009994158, + "courses/views/v2/views_test.py::test_add_verified_program_course_enrollment[verified-elective]": 1.698999811997055, + "courses/views/v2/views_test.py::test_add_verified_program_course_enrollment[verified-requirement]": 1.3461575740002445, + "courses/views/v2/views_test.py::test_add_verified_program_course_enrollment_audit_only_run_falls_back_to_audit": 0.5753889400075423, + "courses/views/v2/views_test.py::test_correct_courserun_languages[none]": 0.22976181400736095, + "courses/views/v2/views_test.py::test_correct_courserun_languages[reg]": 0.21498583799984772, + "courses/views/v2/views_test.py::test_correct_courserun_languages[source]": 0.216908513008093, + "courses/views/v2/views_test.py::test_correct_courserun_languages[transreg]": 0.20017199700669153, + "courses/views/v2/views_test.py::test_course_run_and_product_prefetch_optimized": 0.26255163000314496, + "courses/views/v2/views_test.py::test_create_program[1-1]": 0.3182001620007213, + "courses/views/v2/views_test.py::test_delete_program[1-1]": 0.2609651660095551, + "courses/views/v2/views_test.py::test_destroy_program_enrollment_paid_fails": 0.16223119500500616, + "courses/views/v2/views_test.py::test_filter_by_contract_id_unauthenticated_user": 0.3489265179960057, + "courses/views/v2/views_test.py::test_filter_by_contract_id_with_contracted_user": 0.4024372529966058, + "courses/views/v2/views_test.py::test_filter_by_contract_id_without_contract_access": 0.35858075899159303, + "courses/views/v2/views_test.py::test_filter_by_org_id_unauthenticated_user": 0.3412081589922309, + "courses/views/v2/views_test.py::test_filter_by_org_id_with_contracted_user": 0.4467335900044418, + "courses/views/v2/views_test.py::test_filter_by_org_id_without_contract_access": 0.35758174000511644, + "courses/views/v2/views_test.py::test_filter_courses_with_contract_id_anonymous": 0.09878215700155124, + "courses/views/v2/views_test.py::test_filter_courses_with_contract_id_authenticated_user": 1.8151772130004247, + "courses/views/v2/views_test.py::test_filter_courses_with_contract_id_no_access": 0.2628361919996678, + "courses/views/v2/views_test.py::test_filter_programs_by_org_and_contract_no_duplicates": 0.36782401399250375, + "courses/views/v2/views_test.py::test_filter_with_org_id_anonymous": 0.08411825600342127, + "courses/views/v2/views_test.py::test_filter_with_org_id_combined_with_other_filters": 0.5422428949968889, + "courses/views/v2/views_test.py::test_filter_with_org_id_detail_view_unauthorized_user": 0.22390057399024954, + "courses/views/v2/views_test.py::test_filter_with_org_id_inactive_contract_excluded": 0.24958978300128365, + "courses/views/v2/views_test.py::test_filter_with_org_id_multiple_courses_same_org": 0.7955926490030834, + "courses/views/v2/views_test.py::test_filter_with_org_id_multiple_orgs": 0.5702054870052962, + "courses/views/v2/views_test.py::test_filter_with_org_id_nonexistent_org_id": 0.11563653399934992, + "courses/views/v2/views_test.py::test_filter_with_org_id_pagination": 2.043441765003081, + "courses/views/v2/views_test.py::test_filter_with_org_id_respects_course_live_status": 0.33454875699680997, + "courses/views/v2/views_test.py::test_filter_with_org_id_returns_contracted_course": 0.34950946900062263, + "courses/views/v2/views_test.py::test_filter_with_org_id_returns_detail_view": 0.3259138030043687, + "courses/views/v2/views_test.py::test_filter_with_org_id_user_in_org_but_no_contract": 0.32178895899414783, + "courses/views/v2/views_test.py::test_filter_with_org_id_user_not_associated_with_org_returns_no_courses": 0.22029163099068683, + "courses/views/v2/views_test.py::test_filter_without_org_id_authenticated_user": 0.3845476769929519, + "courses/views/v2/views_test.py::test_get_course[False-1-1]": 0.39205260200833436, + "courses/views/v2/views_test.py::test_get_course[True-1-1]": 0.40917951900337357, + "courses/views/v2/views_test.py::test_get_course_certificate": 0.5473690939979861, + "courses/views/v2/views_test.py::test_get_course_certificate_future_issue_date": 0.2090869439925882, + "courses/views/v2/views_test.py::test_get_courses[False-False-2-100]": 8.009537921992887, + "courses/views/v2/views_test.py::test_get_courses[False-None-2-100]": 7.662710941003752, + "courses/views/v2/views_test.py::test_get_courses[False-True-2-100]": 7.565616373001831, + "courses/views/v2/views_test.py::test_get_courses[None-False-2-100]": 26.386907767002413, + "courses/views/v2/views_test.py::test_get_courses[None-None-2-100]": 26.456229111005086, + "courses/views/v2/views_test.py::test_get_courses[None-True-2-100]": 27.415488929997082, + "courses/views/v2/views_test.py::test_get_courses[True-False-2-100]": 20.454409587000555, + "courses/views/v2/views_test.py::test_get_courses[True-None-2-100]": 25.33468013699894, + "courses/views/v2/views_test.py::test_get_courses[True-True-2-100]": 24.711880306997045, + "courses/views/v2/views_test.py::test_get_courses_b2b_programs[False]": 0.3385500539880013, + "courses/views/v2/views_test.py::test_get_courses_b2b_programs[True]": 0.2862785599936615, + "courses/views/v2/views_test.py::test_get_courses_b2b_runs[False-False]": 0.2160923659976106, + "courses/views/v2/views_test.py::test_get_courses_b2b_runs[False-True]": 0.27451957599259913, + "courses/views/v2/views_test.py::test_get_courses_b2b_runs[True-False]": 0.21585892199800583, + "courses/views/v2/views_test.py::test_get_courses_b2b_runs[True-True]": 0.28409140199801186, + "courses/views/v2/views_test.py::test_get_courses_with_specified_contract_programs": 0.519272388999525, + "courses/views/v2/views_test.py::test_get_departments[15-100]": 12.033896251996339, + "courses/views/v2/views_test.py::test_get_nonexistent_course_by_readable_id": 0.043433293991256505, + "courses/views/v2/views_test.py::test_get_nonexistent_program_by_readable_id": 0.04092503699212102, + "courses/views/v2/views_test.py::test_get_program[1-1]": 0.3514415050012758, + "courses/views/v2/views_test.py::test_get_program_by_readable_id_or_pk": 0.18842378800036386, + "courses/views/v2/views_test.py::test_get_program_certificate": 0.4973122619994683, + "courses/views/v2/views_test.py::test_get_program_certificate_future_issue_date": 0.21888485399540514, + "courses/views/v2/views_test.py::test_get_program_includes_topics_from_required_courses": 0.43939540200517513, + "courses/views/v2/views_test.py::test_get_programs[12-100]": 12.33625313599623, + "courses/views/v2/views_test.py::test_next_run_id_with_org_filter": 1.5223681190036586, + "courses/views/v2/views_test.py::test_patch_program[1-1]": 0.28603175999887753, + "courses/views/v2/views_test.py::test_program_enrollment_destroy": 0.23113955699955113, + "courses/views/v2/views_test.py::test_program_enrollment_destroy_no_enrollment_is_idempotent": 0.12445956499868771, + "courses/views/v2/views_test.py::test_program_enrollment_destroy_program_not_found": 0.03218142699915916, + "courses/views/v2/views_test.py::test_program_enrollments": 6.270221810002113, + "courses/views/v2/views_test.py::test_program_enrollments_future_course_cert": 0.4058159199921647, + "courses/views/v2/views_test.py::test_program_enrollments_future_program_cert": 0.19535441800690023, + "courses/views/v2/views_test.py::test_program_filter_for_b2b_org": 0.5759364379919134, + "courses/views/v2/views_test.py::test_program_filter_multiple_ids": 0.40627415199560346, + "courses/views/v2/views_test.py::test_program_products_prefetch_query_count": 0.4487973939976655, + "courses/views/v2/views_test.py::test_retrieving_single_program_by_pk_or_readable_id_includes_parent_programs": 0.31096268400142435, + "courses/views/v2/views_test.py::test_retrievinng_single_course_by_pk_or_readable_id_includes_programs": 0.5736511130016879, + "courses/views/v2/views_test.py::test_user_enrollments_b2b_organization_filter": 0.6794266440047068, + "courses/views/v2/views_test.py::test_user_enrollments_create_b2b_run_invalid_v2": 0.1764403719935217, + "courses/views/v2/views_test.py::test_user_enrollments_list_sync_with_flag[False-False-False]": 0.25418388000980485, + "courses/views/v2/views_test.py::test_user_enrollments_list_sync_with_flag[True-False-False]": 0.2566344489969197, + "courses/views/v2/views_test.py::test_user_enrollments_list_sync_with_flag[True-True-False]": 0.24762868500693003, + "courses/views/v2/views_test.py::test_user_enrollments_list_sync_with_flag[True-True-True]": 0.24137294700631173, + "courses/views/v3/views_test.py::test_course_outline_v3_authenticated_success[100-20]": 9.630834399002197, + "courses/views/v3/views_test.py::test_course_outline_v3_invalid_course_id[100-20]": 9.47141638299945, + "courses/views/v3/views_test.py::test_course_outline_v3_public_success[100-20]": 9.64203152799746, + "courses/views/v3/views_test.py::test_course_outline_v3_upstream_failures[exception_instance0-Unable to fetch course outline from Open edX.-100-20]": 9.539654676998907, + "courses/views/v3/views_test.py::test_course_outline_v3_upstream_failures[exception_instance1-Unable to fetch course outline from Open edX.-100-20]": 10.121873387994128, + "courses/views/v3/views_test.py::test_create_program_enrollment[100-20]": 10.037745370005723, + "courses/views/v3/views_test.py::test_create_program_enrollment_already_active[100-20]": 10.01768099200126, + "courses/views/v3/views_test.py::test_create_program_enrollment_nonlive_program[100-20]": 10.299443793002865, + "courses/views/v3/views_test.py::test_create_program_enrollment_not_found[100-20]": 10.39123393099726, + "courses/views/v3/views_test.py::test_create_program_enrollment_reactivate[100-20]": 10.83495622099872, + "courses/views/v3/views_test.py::test_create_program_enrollment_unauthenticated[100-20]": 10.50498489200254, + "courses/views/v3/views_test.py::test_destroy_program_enrollment[100-20]": 9.94437498699699, + "courses/views/v3/views_test.py::test_destroy_program_enrollment_no_enrollment_is_idempotent[100-20]": 10.399846397995134, + "courses/views/v3/views_test.py::test_destroy_program_enrollment_paid_fails[100-20]": 9.92994660500699, + "courses/views/v3/views_test.py::test_destroy_program_enrollment_program_not_found[100-20]": 9.91748773901054, + "courses/views/v3/views_test.py::test_program_enrollments[100-20]": 10.21997451500647, + "courses/views/v3/views_test.py::test_program_enrollments_future_program_cert[100-20]": 9.979245408991119, + "courses/views/v3/views_test.py::test_user_enrollments_detail[100-20]": 10.890932283997245, + "courses/views/v3/views_test.py::test_user_enrollments_future_course_cert[100-20]": 10.142024471002514, + "courses/views/v3/views_test.py::test_user_enrollments_list[100-20]": 10.562190521995944, + "courses/views/v3/views_test.py::test_user_enrollments_list_filter_exclude_b2b[100-20]": 10.870096966005804, + "courses/views/v3/views_test.py::test_user_enrollments_list_filter_org_id[100-20]": 10.363723046000814, + "courses/views/v3/views_test.py::test_user_enrollments_list_query_count_guard[100-20]": 10.013941078992502, + "ecommerce/admin_test.py::test_admin_product_create_generates_reversion": 0.15679441999236587, + "ecommerce/admin_test.py::test_admin_refund_order_get_not_found": 0.0484600190029596, + "ecommerce/admin_test.py::test_admin_refund_order_get_not_fulfilled": 0.501365354997688, + "ecommerce/admin_test.py::test_admin_refund_order_get_success": 0.14106271900527645, + "ecommerce/admin_test.py::test_admin_refund_order_post_invalid_form": 0.06827976899512578, + "ecommerce/admin_test.py::test_admin_refund_order_post_order_not_found": 0.0370933279991732, + "ecommerce/admin_test.py::test_admin_refund_order_post_refund_failed": 0.04565652200108161, + "ecommerce/admin_test.py::test_admin_refund_order_post_success[False-refunded.]": 0.04991064599744277, + "ecommerce/admin_test.py::test_admin_refund_order_post_success[True-refunded and unenrollment is in progress.]": 0.050078313004632946, + "ecommerce/api_test.py::test_apply_discount_to_basket[0-False-False]": 0.1765698999952292, + "ecommerce/api_test.py::test_apply_discount_to_basket[0-False-True]": 0.18111389699333813, + "ecommerce/api_test.py::test_apply_discount_to_basket[0-True-False]": 0.19653897000534926, + "ecommerce/api_test.py::test_apply_discount_to_basket[0-True-True]": 0.15378146299917717, + "ecommerce/api_test.py::test_apply_discount_to_basket[1-False-False]": 0.17646737399627455, + "ecommerce/api_test.py::test_apply_discount_to_basket[1-False-True]": 0.15123707000748254, + "ecommerce/api_test.py::test_apply_discount_to_basket[1-True-False]": 0.16239914800098632, + "ecommerce/api_test.py::test_apply_discount_to_basket[1-True-True]": 0.18180665200634394, + "ecommerce/api_test.py::test_apply_discount_to_basket[10-False-False]": 0.134639320000133, + "ecommerce/api_test.py::test_apply_discount_to_basket[10-False-True]": 0.14444070599711267, + "ecommerce/api_test.py::test_apply_discount_to_basket[10-True-False]": 0.156642957997974, + "ecommerce/api_test.py::test_apply_discount_to_basket[10-True-True]": 0.1674506830095197, + "ecommerce/api_test.py::test_apply_discount_to_basket[11-False-False]": 0.14397492000716738, + "ecommerce/api_test.py::test_apply_discount_to_basket[11-False-True]": 0.14527071900374722, + "ecommerce/api_test.py::test_apply_discount_to_basket[11-True-False]": 0.1641277840026305, + "ecommerce/api_test.py::test_apply_discount_to_basket[11-True-True]": 0.15525754999544006, + "ecommerce/api_test.py::test_apply_discount_to_basket[12-False-False]": 0.13701875301194377, + "ecommerce/api_test.py::test_apply_discount_to_basket[12-False-True]": 0.13829188000090653, + "ecommerce/api_test.py::test_apply_discount_to_basket[12-True-False]": 0.13963320100447163, + "ecommerce/api_test.py::test_apply_discount_to_basket[12-True-True]": 0.1540580310029327, + "ecommerce/api_test.py::test_apply_discount_to_basket[13-False-False]": 0.15074955200543627, + "ecommerce/api_test.py::test_apply_discount_to_basket[13-False-True]": 0.15805812300095567, + "ecommerce/api_test.py::test_apply_discount_to_basket[13-True-False]": 0.1683698810011265, + "ecommerce/api_test.py::test_apply_discount_to_basket[13-True-True]": 0.19003250800597016, + "ecommerce/api_test.py::test_apply_discount_to_basket[14-False-False]": 0.15999874399858527, + "ecommerce/api_test.py::test_apply_discount_to_basket[14-False-True]": 0.1649018719981541, + "ecommerce/api_test.py::test_apply_discount_to_basket[14-True-False]": 0.157680171992979, + "ecommerce/api_test.py::test_apply_discount_to_basket[14-True-True]": 0.14784658099233638, + "ecommerce/api_test.py::test_apply_discount_to_basket[15-False-False]": 0.1553962869948009, + "ecommerce/api_test.py::test_apply_discount_to_basket[15-False-True]": 0.1612533449952025, + "ecommerce/api_test.py::test_apply_discount_to_basket[15-True-False]": 0.161380146004376, + "ecommerce/api_test.py::test_apply_discount_to_basket[15-True-True]": 0.16447417500603478, + "ecommerce/api_test.py::test_apply_discount_to_basket[16-False-False]": 0.17323435000434984, + "ecommerce/api_test.py::test_apply_discount_to_basket[16-False-True]": 0.15977508599462453, + "ecommerce/api_test.py::test_apply_discount_to_basket[16-True-False]": 0.1798316509957658, + "ecommerce/api_test.py::test_apply_discount_to_basket[16-True-True]": 0.17389663199719507, + "ecommerce/api_test.py::test_apply_discount_to_basket[17-False-False]": 0.1630833069939399, + "ecommerce/api_test.py::test_apply_discount_to_basket[17-False-True]": 0.1695970440050587, + "ecommerce/api_test.py::test_apply_discount_to_basket[17-True-False]": 0.17132978099834872, + "ecommerce/api_test.py::test_apply_discount_to_basket[17-True-True]": 0.17404228199302452, + "ecommerce/api_test.py::test_apply_discount_to_basket[18-False-False]": 0.1593271240053582, + "ecommerce/api_test.py::test_apply_discount_to_basket[18-False-True]": 0.1511378779905499, + "ecommerce/api_test.py::test_apply_discount_to_basket[18-True-False]": 0.16833776800194755, + "ecommerce/api_test.py::test_apply_discount_to_basket[18-True-True]": 0.19743721000122605, + "ecommerce/api_test.py::test_apply_discount_to_basket[19-False-False]": 0.14803606900386512, + "ecommerce/api_test.py::test_apply_discount_to_basket[19-False-True]": 0.17676759099413175, + "ecommerce/api_test.py::test_apply_discount_to_basket[19-True-False]": 0.19341529100347543, + "ecommerce/api_test.py::test_apply_discount_to_basket[19-True-True]": 0.19763417899957858, + "ecommerce/api_test.py::test_apply_discount_to_basket[2-False-False]": 0.16342068601079518, + "ecommerce/api_test.py::test_apply_discount_to_basket[2-False-True]": 0.17117118299211143, + "ecommerce/api_test.py::test_apply_discount_to_basket[2-True-False]": 0.19088911500148242, + "ecommerce/api_test.py::test_apply_discount_to_basket[2-True-True]": 0.16738025700033177, + "ecommerce/api_test.py::test_apply_discount_to_basket[20-False-False]": 0.1548916090032435, + "ecommerce/api_test.py::test_apply_discount_to_basket[20-False-True]": 0.168897144991206, + "ecommerce/api_test.py::test_apply_discount_to_basket[20-True-False]": 0.15988268800720107, + "ecommerce/api_test.py::test_apply_discount_to_basket[20-True-True]": 0.17766317800123943, + "ecommerce/api_test.py::test_apply_discount_to_basket[21-False-False]": 0.1527852519939188, + "ecommerce/api_test.py::test_apply_discount_to_basket[21-False-True]": 0.14587715200468665, + "ecommerce/api_test.py::test_apply_discount_to_basket[21-True-False]": 0.15858466400095494, + "ecommerce/api_test.py::test_apply_discount_to_basket[21-True-True]": 0.18300061299669323, + "ecommerce/api_test.py::test_apply_discount_to_basket[22-False-False]": 0.16431098500470398, + "ecommerce/api_test.py::test_apply_discount_to_basket[22-False-True]": 0.20723711900791386, + "ecommerce/api_test.py::test_apply_discount_to_basket[22-True-False]": 0.17055947899643797, + "ecommerce/api_test.py::test_apply_discount_to_basket[22-True-True]": 0.16196164099528687, + "ecommerce/api_test.py::test_apply_discount_to_basket[23-False-False]": 0.14719737799168797, + "ecommerce/api_test.py::test_apply_discount_to_basket[23-False-True]": 0.15070921200094745, + "ecommerce/api_test.py::test_apply_discount_to_basket[23-True-False]": 0.16587814900412923, + "ecommerce/api_test.py::test_apply_discount_to_basket[23-True-True]": 0.149989639998239, + "ecommerce/api_test.py::test_apply_discount_to_basket[24-False-False]": 0.1520936359956977, + "ecommerce/api_test.py::test_apply_discount_to_basket[24-False-True]": 0.15025283299473813, + "ecommerce/api_test.py::test_apply_discount_to_basket[24-True-False]": 0.1882186000002548, + "ecommerce/api_test.py::test_apply_discount_to_basket[24-True-True]": 0.17575865399703616, + "ecommerce/api_test.py::test_apply_discount_to_basket[25-False-False]": 0.13322168699960457, + "ecommerce/api_test.py::test_apply_discount_to_basket[25-False-True]": 0.1735203630014439, + "ecommerce/api_test.py::test_apply_discount_to_basket[25-True-False]": 0.17600135000247974, + "ecommerce/api_test.py::test_apply_discount_to_basket[25-True-True]": 0.14984619199094595, + "ecommerce/api_test.py::test_apply_discount_to_basket[26-False-False]": 0.1399421230016742, + "ecommerce/api_test.py::test_apply_discount_to_basket[26-False-True]": 0.14460746599797858, + "ecommerce/api_test.py::test_apply_discount_to_basket[26-True-False]": 0.15133630700438516, + "ecommerce/api_test.py::test_apply_discount_to_basket[26-True-True]": 0.14903304399194894, + "ecommerce/api_test.py::test_apply_discount_to_basket[27-False-False]": 0.16731765499571338, + "ecommerce/api_test.py::test_apply_discount_to_basket[27-False-True]": 0.18418672998814145, + "ecommerce/api_test.py::test_apply_discount_to_basket[27-True-False]": 0.18107607700221706, + "ecommerce/api_test.py::test_apply_discount_to_basket[27-True-True]": 0.16595224200136727, + "ecommerce/api_test.py::test_apply_discount_to_basket[28-False-False]": 0.18342302900418872, + "ecommerce/api_test.py::test_apply_discount_to_basket[28-False-True]": 0.15970658700098284, + "ecommerce/api_test.py::test_apply_discount_to_basket[28-True-False]": 0.16318434198910836, + "ecommerce/api_test.py::test_apply_discount_to_basket[28-True-True]": 0.17473888098902535, + "ecommerce/api_test.py::test_apply_discount_to_basket[29-False-False]": 0.1447724490062683, + "ecommerce/api_test.py::test_apply_discount_to_basket[29-False-True]": 0.18036026699701324, + "ecommerce/api_test.py::test_apply_discount_to_basket[29-True-False]": 0.1748371319990838, + "ecommerce/api_test.py::test_apply_discount_to_basket[29-True-True]": 0.18765194700245047, + "ecommerce/api_test.py::test_apply_discount_to_basket[3-False-False]": 0.19184779799979879, + "ecommerce/api_test.py::test_apply_discount_to_basket[3-False-True]": 0.18305553799291374, + "ecommerce/api_test.py::test_apply_discount_to_basket[3-True-False]": 0.17523947200243128, + "ecommerce/api_test.py::test_apply_discount_to_basket[3-True-True]": 0.146554784005275, + "ecommerce/api_test.py::test_apply_discount_to_basket[30-False-False]": 0.13879522299976088, + "ecommerce/api_test.py::test_apply_discount_to_basket[30-False-True]": 0.14029016000131378, + "ecommerce/api_test.py::test_apply_discount_to_basket[30-True-False]": 0.16191554899705807, + "ecommerce/api_test.py::test_apply_discount_to_basket[30-True-True]": 0.15321419900283217, + "ecommerce/api_test.py::test_apply_discount_to_basket[31-False-False]": 0.14013115900888806, + "ecommerce/api_test.py::test_apply_discount_to_basket[31-False-True]": 0.1411596720063244, + "ecommerce/api_test.py::test_apply_discount_to_basket[31-True-False]": 0.14199290199758252, + "ecommerce/api_test.py::test_apply_discount_to_basket[31-True-True]": 0.14432124199083773, + "ecommerce/api_test.py::test_apply_discount_to_basket[32-False-False]": 0.20799578100559302, + "ecommerce/api_test.py::test_apply_discount_to_basket[32-False-True]": 0.16142744699754985, + "ecommerce/api_test.py::test_apply_discount_to_basket[32-True-False]": 0.16057427999476204, + "ecommerce/api_test.py::test_apply_discount_to_basket[32-True-True]": 0.15759906399762258, + "ecommerce/api_test.py::test_apply_discount_to_basket[33-False-False]": 0.15510195899696555, + "ecommerce/api_test.py::test_apply_discount_to_basket[33-False-True]": 0.16517390300577972, + "ecommerce/api_test.py::test_apply_discount_to_basket[33-True-False]": 0.18975116499495925, + "ecommerce/api_test.py::test_apply_discount_to_basket[33-True-True]": 0.18204562399478164, + "ecommerce/api_test.py::test_apply_discount_to_basket[34-False-False]": 0.1758754139955272, + "ecommerce/api_test.py::test_apply_discount_to_basket[34-False-True]": 0.14995900000212714, + "ecommerce/api_test.py::test_apply_discount_to_basket[34-True-False]": 0.19081987599929562, + "ecommerce/api_test.py::test_apply_discount_to_basket[34-True-True]": 0.1821999500025413, + "ecommerce/api_test.py::test_apply_discount_to_basket[35-False-False]": 0.16974128700530855, + "ecommerce/api_test.py::test_apply_discount_to_basket[35-False-True]": 0.14154197100288002, + "ecommerce/api_test.py::test_apply_discount_to_basket[35-True-False]": 0.16507948199432576, + "ecommerce/api_test.py::test_apply_discount_to_basket[35-True-True]": 0.20724320899898885, + "ecommerce/api_test.py::test_apply_discount_to_basket[36-False-False]": 0.15764126300200587, + "ecommerce/api_test.py::test_apply_discount_to_basket[36-False-True]": 0.1612312409997685, + "ecommerce/api_test.py::test_apply_discount_to_basket[36-True-False]": 0.18761875000200234, + "ecommerce/api_test.py::test_apply_discount_to_basket[36-True-True]": 0.23519992299407022, + "ecommerce/api_test.py::test_apply_discount_to_basket[37-False-False]": 0.15041972800099757, + "ecommerce/api_test.py::test_apply_discount_to_basket[37-False-True]": 0.14437104099488351, + "ecommerce/api_test.py::test_apply_discount_to_basket[37-True-False]": 0.17751690300065093, + "ecommerce/api_test.py::test_apply_discount_to_basket[37-True-True]": 0.1589060519982013, + "ecommerce/api_test.py::test_apply_discount_to_basket[38-False-False]": 0.1658748870031559, + "ecommerce/api_test.py::test_apply_discount_to_basket[38-False-True]": 0.16058857399912085, + "ecommerce/api_test.py::test_apply_discount_to_basket[38-True-False]": 0.18093811499420553, + "ecommerce/api_test.py::test_apply_discount_to_basket[38-True-True]": 0.18466883299697656, + "ecommerce/api_test.py::test_apply_discount_to_basket[39-False-False]": 0.1722209430008661, + "ecommerce/api_test.py::test_apply_discount_to_basket[39-False-True]": 0.16700344000128098, + "ecommerce/api_test.py::test_apply_discount_to_basket[39-True-False]": 0.15299061499536037, + "ecommerce/api_test.py::test_apply_discount_to_basket[39-True-True]": 0.15505028000916354, + "ecommerce/api_test.py::test_apply_discount_to_basket[4-False-False]": 0.1342050159946666, + "ecommerce/api_test.py::test_apply_discount_to_basket[4-False-True]": 0.14166945400211262, + "ecommerce/api_test.py::test_apply_discount_to_basket[4-True-False]": 0.1510473280068254, + "ecommerce/api_test.py::test_apply_discount_to_basket[4-True-True]": 0.19409954100410687, + "ecommerce/api_test.py::test_apply_discount_to_basket[40-False-False]": 0.1465596039997763, + "ecommerce/api_test.py::test_apply_discount_to_basket[40-False-True]": 0.13176227600342827, + "ecommerce/api_test.py::test_apply_discount_to_basket[40-True-False]": 0.1923694059951231, + "ecommerce/api_test.py::test_apply_discount_to_basket[40-True-True]": 0.16764277099719038, + "ecommerce/api_test.py::test_apply_discount_to_basket[41-False-False]": 0.184452172004967, + "ecommerce/api_test.py::test_apply_discount_to_basket[41-False-True]": 0.15886524799861945, + "ecommerce/api_test.py::test_apply_discount_to_basket[41-True-False]": 0.16416135400504572, + "ecommerce/api_test.py::test_apply_discount_to_basket[41-True-True]": 0.15509002399630845, + "ecommerce/api_test.py::test_apply_discount_to_basket[42-False-False]": 0.1700492930103792, + "ecommerce/api_test.py::test_apply_discount_to_basket[42-False-True]": 0.15505600999313174, + "ecommerce/api_test.py::test_apply_discount_to_basket[42-True-False]": 0.1460548270115396, + "ecommerce/api_test.py::test_apply_discount_to_basket[42-True-True]": 0.1788311610071105, + "ecommerce/api_test.py::test_apply_discount_to_basket[43-False-False]": 0.1440014169929782, + "ecommerce/api_test.py::test_apply_discount_to_basket[43-False-True]": 0.14712718799273716, + "ecommerce/api_test.py::test_apply_discount_to_basket[43-True-False]": 0.18840229099441785, + "ecommerce/api_test.py::test_apply_discount_to_basket[43-True-True]": 0.18058537301112665, + "ecommerce/api_test.py::test_apply_discount_to_basket[44-False-False]": 0.1674537830040208, + "ecommerce/api_test.py::test_apply_discount_to_basket[44-False-True]": 0.16285406400129432, + "ecommerce/api_test.py::test_apply_discount_to_basket[44-True-False]": 0.18121769900608342, + "ecommerce/api_test.py::test_apply_discount_to_basket[44-True-True]": 0.14977054599876283, + "ecommerce/api_test.py::test_apply_discount_to_basket[45-False-False]": 0.17478613200364634, + "ecommerce/api_test.py::test_apply_discount_to_basket[45-False-True]": 0.1587297519945423, + "ecommerce/api_test.py::test_apply_discount_to_basket[45-True-False]": 0.18826197800808586, + "ecommerce/api_test.py::test_apply_discount_to_basket[45-True-True]": 0.1819218560049194, + "ecommerce/api_test.py::test_apply_discount_to_basket[46-False-False]": 0.1878480080049485, + "ecommerce/api_test.py::test_apply_discount_to_basket[46-False-True]": 0.17888430900347885, + "ecommerce/api_test.py::test_apply_discount_to_basket[46-True-False]": 0.18035571798827732, + "ecommerce/api_test.py::test_apply_discount_to_basket[46-True-True]": 0.16060456799459644, + "ecommerce/api_test.py::test_apply_discount_to_basket[47-False-False]": 0.1625572200064198, + "ecommerce/api_test.py::test_apply_discount_to_basket[47-False-True]": 0.17548993899981724, + "ecommerce/api_test.py::test_apply_discount_to_basket[47-True-False]": 0.17336166500899708, + "ecommerce/api_test.py::test_apply_discount_to_basket[47-True-True]": 0.18799107200175058, + "ecommerce/api_test.py::test_apply_discount_to_basket[48-False-False]": 0.1730105660026311, + "ecommerce/api_test.py::test_apply_discount_to_basket[48-False-True]": 0.15372075199411483, + "ecommerce/api_test.py::test_apply_discount_to_basket[48-True-False]": 0.18423441200866364, + "ecommerce/api_test.py::test_apply_discount_to_basket[48-True-True]": 0.18977956799790263, + "ecommerce/api_test.py::test_apply_discount_to_basket[49-False-False]": 0.17019665100087877, + "ecommerce/api_test.py::test_apply_discount_to_basket[49-False-True]": 0.1973493149926071, + "ecommerce/api_test.py::test_apply_discount_to_basket[49-True-False]": 0.19032453899853863, + "ecommerce/api_test.py::test_apply_discount_to_basket[49-True-True]": 0.16894867300288752, + "ecommerce/api_test.py::test_apply_discount_to_basket[5-False-False]": 0.14924399799929233, + "ecommerce/api_test.py::test_apply_discount_to_basket[5-False-True]": 0.14986649100319482, + "ecommerce/api_test.py::test_apply_discount_to_basket[5-True-False]": 0.17055555900151376, + "ecommerce/api_test.py::test_apply_discount_to_basket[5-True-True]": 0.20644911700219382, + "ecommerce/api_test.py::test_apply_discount_to_basket[50-False-False]": 0.16024621300311992, + "ecommerce/api_test.py::test_apply_discount_to_basket[50-False-True]": 0.1615345689933747, + "ecommerce/api_test.py::test_apply_discount_to_basket[50-True-False]": 0.15859661199647235, + "ecommerce/api_test.py::test_apply_discount_to_basket[50-True-True]": 0.17773637600475922, + "ecommerce/api_test.py::test_apply_discount_to_basket[51-False-False]": 0.1410006349979085, + "ecommerce/api_test.py::test_apply_discount_to_basket[51-False-True]": 0.16263116600021021, + "ecommerce/api_test.py::test_apply_discount_to_basket[51-True-False]": 0.18258998600504128, + "ecommerce/api_test.py::test_apply_discount_to_basket[51-True-True]": 0.19414523999148514, + "ecommerce/api_test.py::test_apply_discount_to_basket[52-False-False]": 0.18063470799825154, + "ecommerce/api_test.py::test_apply_discount_to_basket[52-False-True]": 0.17575795999437105, + "ecommerce/api_test.py::test_apply_discount_to_basket[52-True-False]": 0.1753164900073898, + "ecommerce/api_test.py::test_apply_discount_to_basket[52-True-True]": 0.15355729200382484, + "ecommerce/api_test.py::test_apply_discount_to_basket[53-False-False]": 0.14476846899924567, + "ecommerce/api_test.py::test_apply_discount_to_basket[53-False-True]": 0.17765711699757958, + "ecommerce/api_test.py::test_apply_discount_to_basket[53-True-False]": 0.19391994400211843, + "ecommerce/api_test.py::test_apply_discount_to_basket[53-True-True]": 0.19358451499283547, + "ecommerce/api_test.py::test_apply_discount_to_basket[54-False-False]": 0.17014045600080863, + "ecommerce/api_test.py::test_apply_discount_to_basket[54-False-True]": 0.16322761700575938, + "ecommerce/api_test.py::test_apply_discount_to_basket[54-True-False]": 0.1834518960022251, + "ecommerce/api_test.py::test_apply_discount_to_basket[54-True-True]": 0.17306058899703203, + "ecommerce/api_test.py::test_apply_discount_to_basket[55-False-False]": 0.16319843800738454, + "ecommerce/api_test.py::test_apply_discount_to_basket[55-False-True]": 0.1435548619920155, + "ecommerce/api_test.py::test_apply_discount_to_basket[55-True-False]": 0.15065344300091965, + "ecommerce/api_test.py::test_apply_discount_to_basket[55-True-True]": 0.15943608800444053, + "ecommerce/api_test.py::test_apply_discount_to_basket[56-False-False]": 0.15860458400129573, + "ecommerce/api_test.py::test_apply_discount_to_basket[56-False-True]": 0.16210928400687408, + "ecommerce/api_test.py::test_apply_discount_to_basket[56-True-False]": 0.18107061100454303, + "ecommerce/api_test.py::test_apply_discount_to_basket[56-True-True]": 0.18706019000819651, + "ecommerce/api_test.py::test_apply_discount_to_basket[57-False-False]": 0.1689008039975306, + "ecommerce/api_test.py::test_apply_discount_to_basket[57-False-True]": 0.21933277800417272, + "ecommerce/api_test.py::test_apply_discount_to_basket[57-True-False]": 0.1806470570008969, + "ecommerce/api_test.py::test_apply_discount_to_basket[57-True-True]": 0.16471612799796276, + "ecommerce/api_test.py::test_apply_discount_to_basket[58-False-False]": 0.17419659400184173, + "ecommerce/api_test.py::test_apply_discount_to_basket[58-False-True]": 0.18355889499071054, + "ecommerce/api_test.py::test_apply_discount_to_basket[58-True-False]": 0.16247556100279326, + "ecommerce/api_test.py::test_apply_discount_to_basket[58-True-True]": 0.1555390100038494, + "ecommerce/api_test.py::test_apply_discount_to_basket[59-False-False]": 0.14195568500144873, + "ecommerce/api_test.py::test_apply_discount_to_basket[59-False-True]": 0.1653685520068393, + "ecommerce/api_test.py::test_apply_discount_to_basket[59-True-False]": 0.17565009399550036, + "ecommerce/api_test.py::test_apply_discount_to_basket[59-True-True]": 0.18453177300398238, + "ecommerce/api_test.py::test_apply_discount_to_basket[6-False-False]": 0.16297441499045817, + "ecommerce/api_test.py::test_apply_discount_to_basket[6-False-True]": 0.1884518289953121, + "ecommerce/api_test.py::test_apply_discount_to_basket[6-True-False]": 0.19816932999674464, + "ecommerce/api_test.py::test_apply_discount_to_basket[6-True-True]": 0.17021101799764438, + "ecommerce/api_test.py::test_apply_discount_to_basket[60-False-False]": 0.14136322400008794, + "ecommerce/api_test.py::test_apply_discount_to_basket[60-False-True]": 0.14946172601048602, + "ecommerce/api_test.py::test_apply_discount_to_basket[60-True-False]": 0.15263465200405335, + "ecommerce/api_test.py::test_apply_discount_to_basket[60-True-True]": 0.15516338399902452, + "ecommerce/api_test.py::test_apply_discount_to_basket[61-False-False]": 0.16943133899621898, + "ecommerce/api_test.py::test_apply_discount_to_basket[61-False-True]": 0.14320974799920805, + "ecommerce/api_test.py::test_apply_discount_to_basket[61-True-False]": 0.14166619299794547, + "ecommerce/api_test.py::test_apply_discount_to_basket[61-True-True]": 0.15040383800078416, + "ecommerce/api_test.py::test_apply_discount_to_basket[62-False-False]": 0.19820913799776463, + "ecommerce/api_test.py::test_apply_discount_to_basket[62-False-True]": 0.1870373730052961, + "ecommerce/api_test.py::test_apply_discount_to_basket[62-True-False]": 0.15530699899682077, + "ecommerce/api_test.py::test_apply_discount_to_basket[62-True-True]": 0.18282822400215082, + "ecommerce/api_test.py::test_apply_discount_to_basket[63-False-False]": 0.1336330770063796, + "ecommerce/api_test.py::test_apply_discount_to_basket[63-False-True]": 0.15701649399852613, + "ecommerce/api_test.py::test_apply_discount_to_basket[63-True-False]": 0.2183528839959763, + "ecommerce/api_test.py::test_apply_discount_to_basket[63-True-True]": 0.1685691839957144, + "ecommerce/api_test.py::test_apply_discount_to_basket[64-False-False]": 0.15938243200071156, + "ecommerce/api_test.py::test_apply_discount_to_basket[64-False-True]": 0.15033232200221391, + "ecommerce/api_test.py::test_apply_discount_to_basket[64-True-False]": 0.14986442199733574, + "ecommerce/api_test.py::test_apply_discount_to_basket[64-True-True]": 0.16062188699288527, + "ecommerce/api_test.py::test_apply_discount_to_basket[65-False-False]": 0.14109807699423982, + "ecommerce/api_test.py::test_apply_discount_to_basket[65-False-True]": 0.16102081999997608, + "ecommerce/api_test.py::test_apply_discount_to_basket[65-True-False]": 0.17703575899940915, + "ecommerce/api_test.py::test_apply_discount_to_basket[65-True-True]": 0.21220557201013435, + "ecommerce/api_test.py::test_apply_discount_to_basket[66-False-False]": 0.153828770002292, + "ecommerce/api_test.py::test_apply_discount_to_basket[66-False-True]": 0.16364659399550874, + "ecommerce/api_test.py::test_apply_discount_to_basket[66-True-False]": 0.1737275269915699, + "ecommerce/api_test.py::test_apply_discount_to_basket[66-True-True]": 0.15785311899526278, + "ecommerce/api_test.py::test_apply_discount_to_basket[67-False-False]": 0.15673076298844535, + "ecommerce/api_test.py::test_apply_discount_to_basket[67-False-True]": 0.16752363199339015, + "ecommerce/api_test.py::test_apply_discount_to_basket[67-True-False]": 0.16931758799910313, + "ecommerce/api_test.py::test_apply_discount_to_basket[67-True-True]": 0.16823290799948154, + "ecommerce/api_test.py::test_apply_discount_to_basket[68-False-False]": 0.14734290300111752, + "ecommerce/api_test.py::test_apply_discount_to_basket[68-False-True]": 0.15023044899862725, + "ecommerce/api_test.py::test_apply_discount_to_basket[68-True-False]": 0.151518615995883, + "ecommerce/api_test.py::test_apply_discount_to_basket[68-True-True]": 0.15580391199910082, + "ecommerce/api_test.py::test_apply_discount_to_basket[69-False-False]": 0.170318602999032, + "ecommerce/api_test.py::test_apply_discount_to_basket[69-False-True]": 0.15477043300052173, + "ecommerce/api_test.py::test_apply_discount_to_basket[69-True-False]": 0.14544070799456676, + "ecommerce/api_test.py::test_apply_discount_to_basket[69-True-True]": 0.16444133499317104, + "ecommerce/api_test.py::test_apply_discount_to_basket[7-False-False]": 0.20615927199833095, + "ecommerce/api_test.py::test_apply_discount_to_basket[7-False-True]": 0.16272965900134295, + "ecommerce/api_test.py::test_apply_discount_to_basket[7-True-False]": 0.17240482699708082, + "ecommerce/api_test.py::test_apply_discount_to_basket[7-True-True]": 0.16805434399429942, + "ecommerce/api_test.py::test_apply_discount_to_basket[70-False-False]": 0.1619227779956418, + "ecommerce/api_test.py::test_apply_discount_to_basket[70-False-True]": 0.1832208760024514, + "ecommerce/api_test.py::test_apply_discount_to_basket[70-True-False]": 0.1672347199928481, + "ecommerce/api_test.py::test_apply_discount_to_basket[70-True-True]": 0.18847112100047525, + "ecommerce/api_test.py::test_apply_discount_to_basket[71-False-False]": 0.15750949299399508, + "ecommerce/api_test.py::test_apply_discount_to_basket[71-False-True]": 0.1953032559977146, + "ecommerce/api_test.py::test_apply_discount_to_basket[71-True-False]": 0.1733988920095726, + "ecommerce/api_test.py::test_apply_discount_to_basket[71-True-True]": 0.19553183199604973, + "ecommerce/api_test.py::test_apply_discount_to_basket[72-False-False]": 0.1556366689983406, + "ecommerce/api_test.py::test_apply_discount_to_basket[72-False-True]": 0.14926611800910905, + "ecommerce/api_test.py::test_apply_discount_to_basket[72-True-False]": 0.1696297230082564, + "ecommerce/api_test.py::test_apply_discount_to_basket[72-True-True]": 0.172078595001949, + "ecommerce/api_test.py::test_apply_discount_to_basket[73-False-False]": 0.13828409100096906, + "ecommerce/api_test.py::test_apply_discount_to_basket[73-False-True]": 0.13456958999449853, + "ecommerce/api_test.py::test_apply_discount_to_basket[73-True-False]": 0.15186722899670713, + "ecommerce/api_test.py::test_apply_discount_to_basket[73-True-True]": 0.16180212100152858, + "ecommerce/api_test.py::test_apply_discount_to_basket[74-False-False]": 0.15332507101265946, + "ecommerce/api_test.py::test_apply_discount_to_basket[74-False-True]": 0.15677259700169088, + "ecommerce/api_test.py::test_apply_discount_to_basket[74-True-False]": 0.1617238550024922, + "ecommerce/api_test.py::test_apply_discount_to_basket[74-True-True]": 0.17561964499327587, + "ecommerce/api_test.py::test_apply_discount_to_basket[75-False-False]": 0.1669916389946593, + "ecommerce/api_test.py::test_apply_discount_to_basket[75-False-True]": 0.15230984299705597, + "ecommerce/api_test.py::test_apply_discount_to_basket[75-True-False]": 0.15539442600129405, + "ecommerce/api_test.py::test_apply_discount_to_basket[75-True-True]": 0.1912922970004729, + "ecommerce/api_test.py::test_apply_discount_to_basket[76-False-False]": 0.18171587200049544, + "ecommerce/api_test.py::test_apply_discount_to_basket[76-False-True]": 0.18249450299481396, + "ecommerce/api_test.py::test_apply_discount_to_basket[76-True-False]": 0.17420495099941036, + "ecommerce/api_test.py::test_apply_discount_to_basket[76-True-True]": 0.174790811004641, + "ecommerce/api_test.py::test_apply_discount_to_basket[77-False-False]": 0.17951596400234848, + "ecommerce/api_test.py::test_apply_discount_to_basket[77-False-True]": 0.14531249699939508, + "ecommerce/api_test.py::test_apply_discount_to_basket[77-True-False]": 0.18397938599810004, + "ecommerce/api_test.py::test_apply_discount_to_basket[77-True-True]": 0.1584243730030721, + "ecommerce/api_test.py::test_apply_discount_to_basket[78-False-False]": 0.1439744399904157, + "ecommerce/api_test.py::test_apply_discount_to_basket[78-False-True]": 0.15179041100054746, + "ecommerce/api_test.py::test_apply_discount_to_basket[78-True-False]": 0.18800978799845325, + "ecommerce/api_test.py::test_apply_discount_to_basket[78-True-True]": 0.22552665200055344, + "ecommerce/api_test.py::test_apply_discount_to_basket[79-False-False]": 0.15413639599864837, + "ecommerce/api_test.py::test_apply_discount_to_basket[79-False-True]": 0.13949271499586757, + "ecommerce/api_test.py::test_apply_discount_to_basket[79-True-False]": 0.17372512300789822, + "ecommerce/api_test.py::test_apply_discount_to_basket[79-True-True]": 0.541154168000503, + "ecommerce/api_test.py::test_apply_discount_to_basket[8-False-False]": 0.1421886079988326, + "ecommerce/api_test.py::test_apply_discount_to_basket[8-False-True]": 0.18473960900882958, + "ecommerce/api_test.py::test_apply_discount_to_basket[8-True-False]": 0.2277042409987189, + "ecommerce/api_test.py::test_apply_discount_to_basket[8-True-True]": 0.1956997829984175, + "ecommerce/api_test.py::test_apply_discount_to_basket[80-False-False]": 0.1863250869937474, + "ecommerce/api_test.py::test_apply_discount_to_basket[80-False-True]": 0.1626220930047566, + "ecommerce/api_test.py::test_apply_discount_to_basket[80-True-False]": 0.18628652299958048, + "ecommerce/api_test.py::test_apply_discount_to_basket[80-True-True]": 0.16791297999589005, + "ecommerce/api_test.py::test_apply_discount_to_basket[81-False-False]": 0.15313286899618106, + "ecommerce/api_test.py::test_apply_discount_to_basket[81-False-True]": 0.13591053600248415, + "ecommerce/api_test.py::test_apply_discount_to_basket[81-True-False]": 0.1369409210019512, + "ecommerce/api_test.py::test_apply_discount_to_basket[81-True-True]": 0.17977726200479083, + "ecommerce/api_test.py::test_apply_discount_to_basket[82-False-False]": 0.1324556039980962, + "ecommerce/api_test.py::test_apply_discount_to_basket[82-False-True]": 0.1397641069997917, + "ecommerce/api_test.py::test_apply_discount_to_basket[82-True-False]": 0.14697230199817568, + "ecommerce/api_test.py::test_apply_discount_to_basket[82-True-True]": 0.1497371510049561, + "ecommerce/api_test.py::test_apply_discount_to_basket[83-False-False]": 0.13709797900810372, + "ecommerce/api_test.py::test_apply_discount_to_basket[83-False-True]": 0.14261945000180276, + "ecommerce/api_test.py::test_apply_discount_to_basket[83-True-False]": 0.1353978439947241, + "ecommerce/api_test.py::test_apply_discount_to_basket[83-True-True]": 0.13924739800131647, + "ecommerce/api_test.py::test_apply_discount_to_basket[84-False-False]": 0.1663854930011439, + "ecommerce/api_test.py::test_apply_discount_to_basket[84-False-True]": 0.16654425799788442, + "ecommerce/api_test.py::test_apply_discount_to_basket[84-True-False]": 0.15679363499657484, + "ecommerce/api_test.py::test_apply_discount_to_basket[84-True-True]": 0.14660340199770872, + "ecommerce/api_test.py::test_apply_discount_to_basket[85-False-False]": 0.15625992400600808, + "ecommerce/api_test.py::test_apply_discount_to_basket[85-False-True]": 0.16967403200396802, + "ecommerce/api_test.py::test_apply_discount_to_basket[85-True-False]": 0.16344228200614452, + "ecommerce/api_test.py::test_apply_discount_to_basket[85-True-True]": 0.1634777250001207, + "ecommerce/api_test.py::test_apply_discount_to_basket[86-False-False]": 0.17758653899363708, + "ecommerce/api_test.py::test_apply_discount_to_basket[86-False-True]": 0.1526302369966288, + "ecommerce/api_test.py::test_apply_discount_to_basket[86-True-False]": 0.1730910300029791, + "ecommerce/api_test.py::test_apply_discount_to_basket[86-True-True]": 0.18843362399638863, + "ecommerce/api_test.py::test_apply_discount_to_basket[87-False-False]": 0.15540456100279698, + "ecommerce/api_test.py::test_apply_discount_to_basket[87-False-True]": 0.1806896459966083, + "ecommerce/api_test.py::test_apply_discount_to_basket[87-True-False]": 0.17633125199063215, + "ecommerce/api_test.py::test_apply_discount_to_basket[87-True-True]": 0.17937764799717115, + "ecommerce/api_test.py::test_apply_discount_to_basket[88-False-False]": 0.1614143289916683, + "ecommerce/api_test.py::test_apply_discount_to_basket[88-False-True]": 0.17679828999825986, + "ecommerce/api_test.py::test_apply_discount_to_basket[88-True-False]": 0.16526096000598045, + "ecommerce/api_test.py::test_apply_discount_to_basket[88-True-True]": 0.16307604600297054, + "ecommerce/api_test.py::test_apply_discount_to_basket[89-False-False]": 0.15123787101038033, + "ecommerce/api_test.py::test_apply_discount_to_basket[89-False-True]": 0.15925435200188076, + "ecommerce/api_test.py::test_apply_discount_to_basket[89-True-False]": 0.19560826099768747, + "ecommerce/api_test.py::test_apply_discount_to_basket[89-True-True]": 0.17536183500487823, + "ecommerce/api_test.py::test_apply_discount_to_basket[9-False-False]": 0.1535863539975253, + "ecommerce/api_test.py::test_apply_discount_to_basket[9-False-True]": 0.1745015849955962, + "ecommerce/api_test.py::test_apply_discount_to_basket[9-True-False]": 0.17326159999356605, + "ecommerce/api_test.py::test_apply_discount_to_basket[9-True-True]": 0.178146687001572, + "ecommerce/api_test.py::test_apply_discount_to_basket[90-False-False]": 0.19368905399460346, + "ecommerce/api_test.py::test_apply_discount_to_basket[90-False-True]": 0.16745261200412642, + "ecommerce/api_test.py::test_apply_discount_to_basket[90-True-False]": 0.1785175889963284, + "ecommerce/api_test.py::test_apply_discount_to_basket[90-True-True]": 0.17264022599556483, + "ecommerce/api_test.py::test_apply_discount_to_basket[91-False-False]": 0.17122703800851014, + "ecommerce/api_test.py::test_apply_discount_to_basket[91-False-True]": 0.17988059998606332, + "ecommerce/api_test.py::test_apply_discount_to_basket[91-True-False]": 0.20198264800274046, + "ecommerce/api_test.py::test_apply_discount_to_basket[91-True-True]": 0.2062151790014468, + "ecommerce/api_test.py::test_apply_discount_to_basket[92-False-False]": 0.18417553700419376, + "ecommerce/api_test.py::test_apply_discount_to_basket[92-False-True]": 0.15694368499680422, + "ecommerce/api_test.py::test_apply_discount_to_basket[92-True-False]": 0.16549121199932415, + "ecommerce/api_test.py::test_apply_discount_to_basket[92-True-True]": 0.17430604599940125, + "ecommerce/api_test.py::test_apply_discount_to_basket[93-False-False]": 0.19138523100264138, + "ecommerce/api_test.py::test_apply_discount_to_basket[93-False-True]": 0.1623932359943865, + "ecommerce/api_test.py::test_apply_discount_to_basket[93-True-False]": 0.15592640100658173, + "ecommerce/api_test.py::test_apply_discount_to_basket[93-True-True]": 0.16378031200292753, + "ecommerce/api_test.py::test_apply_discount_to_basket[94-False-False]": 0.15665110800182447, + "ecommerce/api_test.py::test_apply_discount_to_basket[94-False-True]": 0.17010737000236986, + "ecommerce/api_test.py::test_apply_discount_to_basket[94-True-False]": 0.19034579499566462, + "ecommerce/api_test.py::test_apply_discount_to_basket[94-True-True]": 0.1753208890077076, + "ecommerce/api_test.py::test_apply_discount_to_basket[95-False-False]": 0.1925060330031556, + "ecommerce/api_test.py::test_apply_discount_to_basket[95-False-True]": 0.15912895200017374, + "ecommerce/api_test.py::test_apply_discount_to_basket[95-True-False]": 0.16930104400671553, + "ecommerce/api_test.py::test_apply_discount_to_basket[95-True-True]": 0.15310187799332198, + "ecommerce/api_test.py::test_apply_discount_to_basket[96-False-False]": 0.1692997119971551, + "ecommerce/api_test.py::test_apply_discount_to_basket[96-False-True]": 0.1956683019961929, + "ecommerce/api_test.py::test_apply_discount_to_basket[96-True-False]": 0.17916234899894334, + "ecommerce/api_test.py::test_apply_discount_to_basket[96-True-True]": 0.22389646900410298, + "ecommerce/api_test.py::test_apply_discount_to_basket[97-False-False]": 0.17782028000510763, + "ecommerce/api_test.py::test_apply_discount_to_basket[97-False-True]": 0.17560773100558436, + "ecommerce/api_test.py::test_apply_discount_to_basket[97-True-False]": 0.20559075399069116, + "ecommerce/api_test.py::test_apply_discount_to_basket[97-True-True]": 0.1915870519951568, + "ecommerce/api_test.py::test_apply_discount_to_basket[98-False-False]": 0.168931333006185, + "ecommerce/api_test.py::test_apply_discount_to_basket[98-False-True]": 0.19090623799274908, + "ecommerce/api_test.py::test_apply_discount_to_basket[98-True-False]": 0.20378628100297647, + "ecommerce/api_test.py::test_apply_discount_to_basket[98-True-True]": 0.16798987999936799, + "ecommerce/api_test.py::test_apply_discount_to_basket[99-False-False]": 0.20458149300975492, + "ecommerce/api_test.py::test_apply_discount_to_basket[99-False-True]": 0.17614124000101583, + "ecommerce/api_test.py::test_apply_discount_to_basket[99-True-False]": 0.1699262269976316, + "ecommerce/api_test.py::test_apply_discount_to_basket[99-True-True]": 0.15718825799558545, + "ecommerce/api_test.py::test_apply_discount_to_basket_with_user_discount[False]": 0.1714630349961226, + "ecommerce/api_test.py::test_apply_discount_to_basket_with_user_discount[True]": 0.179281919998175, + "ecommerce/api_test.py::test_apply_discount_to_basket_with_user_discount_and_finaid[False]": 0.22939000100450357, + "ecommerce/api_test.py::test_apply_discount_to_basket_with_user_discount_and_finaid[True]": 0.18549873799202032, + "ecommerce/api_test.py::test_check_and_process_pending_orders_for_resolution[None]": 0.04085563399712555, + "ecommerce/api_test.py::test_check_and_process_pending_orders_for_resolution[empty]": 0.03127404699625913, + "ecommerce/api_test.py::test_check_and_process_pending_orders_for_resolution[fail]": 0.038331312003720086, + "ecommerce/api_test.py::test_create_verified_program_course_run_enrollment": 0.33471714700135635, + "ecommerce/api_test.py::test_create_verified_program_discount": 0.128953362996981, + "ecommerce/api_test.py::test_create_vpcre_bad_basket": 0.45309373798954766, + "ecommerce/api_test.py::test_create_vpcre_no_program": 0.2482026250072522, + "ecommerce/api_test.py::test_cybersource_order_no_transaction": 0.04505209998751525, + "ecommerce/api_test.py::test_cybersource_refund_no_fulfilled_order[canceled]": 0.049157016997924075, + "ecommerce/api_test.py::test_cybersource_refund_no_fulfilled_order[declined]": 0.03807080499973381, + "ecommerce/api_test.py::test_cybersource_refund_no_fulfilled_order[errored]": 0.04481544100417523, + "ecommerce/api_test.py::test_cybersource_refund_no_fulfilled_order[pending]": 0.04082862400537124, + "ecommerce/api_test.py::test_cybersource_refund_no_fulfilled_order[refunded]": 0.044701661994622555, + "ecommerce/api_test.py::test_cybersource_refund_no_fulfilled_order[review]": 0.03784805400209734, + "ecommerce/api_test.py::test_cybersource_refund_no_order": 0.02245272800064413, + "ecommerce/api_test.py::test_cybersource_refund_no_order_id": 0.027575356005399954, + "ecommerce/api_test.py::test_duplicate_redemption_check[False]": 0.05876081700262148, + "ecommerce/api_test.py::test_duplicate_redemption_check[True]": 0.059970398993755225, + "ecommerce/api_test.py::test_establish_basket_calls_create_user[False]": 0.040783614000247326, + "ecommerce/api_test.py::test_establish_basket_calls_create_user[True]": 0.048218869997072034, + "ecommerce/api_test.py::test_get_auto_apply_discounts": 0.3964534460028517, + "ecommerce/api_test.py::test_get_auto_apply_discounts_respects_dates": 0.25965426299808314, + "ecommerce/api_test.py::test_order_refund_failure": 0.04099438700359315, + "ecommerce/api_test.py::test_order_refund_failure_no_exception": 0.0509223480039509, + "ecommerce/api_test.py::test_order_refund_success[DUPLICATE_REQUEST-False]": 0.0443116909955279, + "ecommerce/api_test.py::test_order_refund_success[DUPLICATE_REQUEST-True]": 0.04890307699679397, + "ecommerce/api_test.py::test_order_refund_success[PENDING-False]": 0.0711514180075028, + "ecommerce/api_test.py::test_order_refund_success[PENDING-True]": 0.0741574749990832, + "ecommerce/api_test.py::test_order_refund_success_with_ref_num[False]": 0.07082286400691373, + "ecommerce/api_test.py::test_order_refund_success_with_ref_num[True]": 0.06599239399656653, + "ecommerce/api_test.py::test_paypal_refunds": 0.03238843401049962, + "ecommerce/api_test.py::test_process_cybersource_payment_decline_response[False]": 0.6230227240012027, + "ecommerce/api_test.py::test_process_cybersource_payment_decline_response[True]": 0.5738258580022375, + "ecommerce/api_test.py::test_process_cybersource_payment_response": 0.6686902150031528, + "ecommerce/api_test.py::test_unenrollment_unenrolls_learner": 0.2910322670068126, + "ecommerce/discounts_test.py::test_discount_factory_adjustment": 0.4634424269970623, + "ecommerce/discounts_test.py::test_discount_factory_generation": 0.04104063099657651, + "ecommerce/discounts_test.py::test_discounted_price[dollars-off-product_price0-10-expected_price0]": 0.16166615999827627, + "ecommerce/discounts_test.py::test_discounted_price[dollars-off-product_price1-10-expected_price1]": 0.19294891200843267, + "ecommerce/discounts_test.py::test_discounted_price[fixed-price-product_price2-42-expected_price2]": 0.1959562150077545, + "ecommerce/discounts_test.py::test_discounted_price[fixed-price-product_price3-42-expected_price3]": 0.14664139999513282, + "ecommerce/discounts_test.py::test_discounted_price[fixed-price-product_price4-42-expected_price4]": 0.20051735100423684, + "ecommerce/discounts_test.py::test_discounted_price[percent-off-product_price5-25-expected_price5]": 0.14835537999897497, + "ecommerce/discounts_test.py::test_discounted_price[percent-off-product_price6-33-expected_price6]": 0.15120551500149304, + "ecommerce/discounts_test.py::test_discounted_price_uses_best_price_across_multiple_discounts": 0.15671000400470803, + "ecommerce/mail_api_test.py::test_mail_api_receipt_generation": 1.7209914780105464, + "ecommerce/mail_api_test.py::test_mail_api_refund_email_generation": 1.4091196930166916, + "ecommerce/mail_api_test.py::test_mail_api_task_called": 1.3825050719970022, + "ecommerce/models_test.py::test_basket_discount_conversion": 0.03874445599649334, + "ecommerce/models_test.py::test_basket_order_equivalency": 0.08120260699797655, + "ecommerce/models_test.py::test_create_transaction_with_no_transaction_id": 0.03837501400266774, + "ecommerce/models_test.py::test_discount_expires_before_activation": 0.023467441998946015, + "ecommerce/models_test.py::test_discount_expires_in_past": 0.022933587999432348, + "ecommerce/models_test.py::test_discount_product_calculation[dollars-off-False]": 0.16513104800105793, + "ecommerce/models_test.py::test_discount_product_calculation[dollars-off-True]": 0.15821415800019167, + "ecommerce/models_test.py::test_discount_product_calculation[fixed-price-False]": 0.18331314800161636, + "ecommerce/models_test.py::test_discount_product_calculation[fixed-price-True]": 0.15223934499226743, + "ecommerce/models_test.py::test_discount_product_calculation[percent-off-False]": 0.15772628199192695, + "ecommerce/models_test.py::test_discount_with_activation_date_in_future_is_not_valid_for_basket": 0.1777180280041648, + "ecommerce/models_test.py::test_discount_with_activation_date_in_past_is_valid_for_basket[False]": 0.1772341750038322, + "ecommerce/models_test.py::test_discount_with_activation_date_in_past_is_valid_for_basket[True]": 0.1431872450048104, + "ecommerce/models_test.py::test_discount_with_expiration_date_in_future_is_valid_for_basket[False]": 0.15467432800505776, + "ecommerce/models_test.py::test_discount_with_expiration_date_in_future_is_valid_for_basket[True]": 0.16870754599949578, + "ecommerce/models_test.py::test_discount_with_expiration_date_in_past_is_not_valid_for_basket": 0.696609291000641, + "ecommerce/models_test.py::test_discount_with_max_redemptions_is_not_valid_for_basket": 0.16611566699430114, + "ecommerce/models_test.py::test_discount_with_max_redemptions_is_valid_for_basket[False]": 0.161477184999967, + "ecommerce/models_test.py::test_discount_with_max_redemptions_is_valid_for_basket[True]": 0.14281932599988068, + "ecommerce/models_test.py::test_discount_with_product_value_is_valid_for_basket[False]": 0.15741248399717733, + "ecommerce/models_test.py::test_discount_with_product_value_is_valid_for_basket[True]": 0.17631747900304617, + "ecommerce/models_test.py::test_discount_with_unmatched_product_value_is_not_valid_for_basket": 0.27265757800341817, + "ecommerce/models_test.py::test_discount_with_unmatched_user_value_is_not_valid_for_basket": 0.22537652699975297, + "ecommerce/models_test.py::test_discount_with_user_value_is_valid_for_basket[False]": 0.16912554799637292, + "ecommerce/models_test.py::test_discount_with_user_value_is_valid_for_basket[True]": 0.1596662920055678, + "ecommerce/models_test.py::test_duplicated_product_lines_validation": 0.34142820100532845, + "ecommerce/models_test.py::test_fulfill_skip_receipt[False-True]": 0.04539711400138913, + "ecommerce/models_test.py::test_fulfill_skip_receipt[True-False]": 0.04933802400046261, + "ecommerce/models_test.py::test_new_pending_order_is_created_if_product_is_different": 0.27605035699525615, + "ecommerce/models_test.py::test_one_time_discount": 0.03933271400455851, + "ecommerce/models_test.py::test_one_time_per_user_discount": 0.046276086002762895, + "ecommerce/models_test.py::test_order_refund": 0.2786601850093575, + "ecommerce/models_test.py::test_order_update_reference_number": 0.028159985995444003, + "ecommerce/models_test.py::test_pending_order_is_reused": 0.19440248099999735, + "ecommerce/models_test.py::test_pending_order_is_reused_but_discounts_cleared[to_basket]": 0.21914775300683687, + "ecommerce/models_test.py::test_pending_order_is_reused_but_discounts_cleared[to_order]": 0.21867747899523238, + "ecommerce/models_test.py::test_pending_order_is_reused_but_discounts_cleared[to_second_basket]": 0.24782889999914914, + "ecommerce/models_test.py::test_pending_order_is_reused_if_multiple_exist": 0.18839012600074057, + "ecommerce/models_test.py::test_pending_order_with_bad_product_versions": 0.17894079299730947, + "ecommerce/models_test.py::test_pending_order_with_multiple_product_versions[False]": 0.1889698520026286, + "ecommerce/models_test.py::test_pending_order_with_multiple_product_versions[True]": 0.17752300400024978, + "ecommerce/models_test.py::test_process_transaction_line_hooks": 1.0341582339970046, + "ecommerce/models_test.py::test_product_delete_protection_inactive": 0.6058125799972913, + "ecommerce/models_test.py::test_product_managers": 0.5108258249965729, + "ecommerce/models_test.py::test_product_multiple_active_for_single_purchasable_object": 0.14859060299932025, + "ecommerce/models_test.py::test_set_limit_discount_multiple_users": 0.04591040199738927, + "ecommerce/models_test.py::test_set_limit_discount_single_user": 0.03934705100255087, + "ecommerce/models_test.py::test_unlimited_discounts": 0.06056840199744329, + "ecommerce/serializers/serializers_test.py::test_basket_item_serializer": 0.17863894000038272, + "ecommerce/serializers/serializers_test.py::test_basket_product_serializer_with_zero_value_discount[0-dollars-off]": 0.20902459201170132, + "ecommerce/serializers/serializers_test.py::test_basket_product_serializer_with_zero_value_discount[0-fixed-price]": 0.22020926200639224, + "ecommerce/serializers/serializers_test.py::test_basket_product_serializer_with_zero_value_discount[0-percent-off]": 0.24048355200648075, + "ecommerce/serializers/serializers_test.py::test_basket_product_serializer_with_zero_value_discount[50-dollars-off]": 0.21282381400669692, + "ecommerce/serializers/serializers_test.py::test_basket_product_serializer_with_zero_value_discount[50-fixed-price]": 0.21090500200079987, + "ecommerce/serializers/serializers_test.py::test_basket_product_serializer_with_zero_value_discount[50-percent-off]": 0.22296706599445315, + "ecommerce/serializers/serializers_test.py::test_basket_serializer": 0.15128155000275, + "ecommerce/serializers/serializers_test.py::test_basket_with_product_serializer": 0.20796789399901172, + "ecommerce/serializers/serializers_test.py::test_basket_with_program_product_serializer": 0.22000858799583511, + "ecommerce/serializers/serializers_test.py::test_order_receipt_lines_serializer": 1.3229562360065756, + "ecommerce/serializers/serializers_test.py::test_order_receipt_order_serializer": 1.4073996959923534, + "ecommerce/serializers/serializers_test.py::test_order_receipt_purchase_serializer": 1.3180012820084812, + "ecommerce/serializers/serializers_test.py::test_order_receipt_purchaser_serializer": 1.2968342969979858, + "ecommerce/serializers/serializers_test.py::test_product_course_serializer": 0.35303837000537897, + "ecommerce/serializers/serializers_test.py::test_product_flexible_price_serializer": 0.3591570079952362, + "ecommerce/serializers/serializers_test.py::test_product_program_object_serializer": 0.1556168239985709, + "ecommerce/serializers/serializers_test.py::test_product_program_serializer": 0.14482628400583053, + "ecommerce/serializers/serializers_test.py::test_program_order_receipt_lines_serializer": 1.1987583990121493, + "ecommerce/serializers/v0/serializers_test.py::test_courserun_order_lines_serializer": 1.5809489880048204, + "ecommerce/serializers/v0/serializers_test.py::test_program_order_lines_serializer": 1.239980544996797, + "ecommerce/tasks_test.py::test_delayed_downgrade_user": 0.02601470600347966, + "ecommerce/tasks_test.py::test_delayed_order_receipt_sends_email": 1.3337154119944898, + "ecommerce/tasks_test.py::test_delayed_order_refund_sends_email": 1.2341974669907358, + "ecommerce/tasks_test.py::test_delayed_unenrollment_unenrolls_user": 0.026873102004174143, + "ecommerce/tests/test_multiple_cart_items.py::TestMultipleCartItems::test_add_to_cart_multiple_items_mode_new_product": 0.2590547229920048, + "ecommerce/tests/test_multiple_cart_items.py::TestMultipleCartItems::test_add_to_cart_nonexistent_product": 0.040011459997913335, + "ecommerce/tests/test_multiple_cart_items.py::TestMultipleCartItems::test_add_to_cart_single_item_mode_default": 0.3201586360009969, + "ecommerce/tests/test_multiple_cart_items.py::TestMultipleCartItems::test_basket_items_count_endpoint": 0.2177937749947887, + "ecommerce/tests/test_multiple_cart_items.py::TestMultipleCartItems::test_cart_with_multiple_items_pricing": 0.6640925190004054, + "ecommerce/tests/test_multiple_cart_items.py::TestMultipleCartItems::test_existing_basket_item_viewset_still_works": 0.222479578013008, + "ecommerce/views/legacy/views_test.py::test_add_basket_item": 0.24347379600658314, + "ecommerce/views/legacy/views_test.py::test_add_to_cart_api_with_feature_flag[False]": 0.32986768599948846, + "ecommerce/views/legacy/views_test.py::test_add_to_cart_api_with_feature_flag[True]": 0.3181026250022114, + "ecommerce/views/legacy/views_test.py::test_add_to_cart_does_not_trigger_hubspot_for_duplicate_product": 0.1745476529977168, + "ecommerce/views/legacy/views_test.py::test_add_to_cart_triggers_hubspot_cart_add_for_uai_course": 0.17117428899655351, + "ecommerce/views/legacy/views_test.py::test_backoffice_callback_bad_order": 0.030606664993683808, + "ecommerce/views/legacy/views_test.py::test_backoffice_callback_good_order": 0.29062775199417956, + "ecommerce/views/legacy/views_test.py::test_backoffice_callback_invalid_payload": 0.03344510500028264, + "ecommerce/views/legacy/views_test.py::test_bulk_discount_create[False]": 0.050179199999547563, + "ecommerce/views/legacy/views_test.py::test_bulk_discount_create[True]": 0.062399828995694406, + "ecommerce/views/legacy/views_test.py::test_checkout_api_result[ACCEPT-fulfilled-False]": 0.9207034559949534, + "ecommerce/views/legacy/views_test.py::test_checkout_api_result[CANCEL-canceled-True]": 0.5699454659988987, + "ecommerce/views/legacy/views_test.py::test_checkout_api_result[DECLINE-declined-True]": 0.5967990209974232, + "ecommerce/views/legacy/views_test.py::test_checkout_api_result[ERROR-errored-True]": 0.5816614509967621, + "ecommerce/views/legacy/views_test.py::test_checkout_api_result[REVIEW-canceled-True]": 0.6297319319928647, + "ecommerce/views/legacy/views_test.py::test_checkout_api_result_program_accept": 0.7606481080001686, + "ecommerce/views/legacy/views_test.py::test_checkout_api_result_verification_failure": 0.6043358059978345, + "ecommerce/views/legacy/views_test.py::test_checkout_cart_with_program_product": 0.3789499380000052, + "ecommerce/views/legacy/views_test.py::test_checkout_interstitial_google_analytics_object": 1.1568275739991805, + "ecommerce/views/legacy/views_test.py::test_checkout_product[False-False-True]": 0.18715421199885895, + "ecommerce/views/legacy/views_test.py::test_checkout_product[False-True-False]": 0.2865918390089064, + "ecommerce/views/legacy/views_test.py::test_checkout_product[False-True-True]": 0.19680802500079153, + "ecommerce/views/legacy/views_test.py::test_checkout_product[True-False-True]": 0.2509847599940258, + "ecommerce/views/legacy/views_test.py::test_checkout_product[True-True-False]": 0.34227983000164386, + "ecommerce/views/legacy/views_test.py::test_checkout_product[True-True-True]": 0.28338446699490305, + "ecommerce/views/legacy/views_test.py::test_checkout_product_cart[False-True-406-No basket]": 0.03465794899238972, + "ecommerce/views/legacy/views_test.py::test_checkout_product_cart[True-False-200-]": 0.5684351040035835, + "ecommerce/views/legacy/views_test.py::test_checkout_product_cart[True-True-406-No product in basket]": 0.06181506498978706, + "ecommerce/views/legacy/views_test.py::test_checkout_product_with_program_id": 0.29880627999227727, + "ecommerce/views/legacy/views_test.py::test_checkout_result[ACCEPT-/dashboard/-fulfilled-False]": 1.401193883997621, + "ecommerce/views/legacy/views_test.py::test_checkout_result[CANCEL-/cart/-canceled-True]": 0.6690240259995335, + "ecommerce/views/legacy/views_test.py::test_checkout_result[DECLINE-/cart/-declined-True]": 0.7361989249984617, + "ecommerce/views/legacy/views_test.py::test_checkout_result[ERROR-/cart/-errored-True]": 0.8501230559922988, + "ecommerce/views/legacy/views_test.py::test_checkout_result[REVIEW-/cart/-canceled-True]": 0.698813673996483, + "ecommerce/views/legacy/views_test.py::test_checkout_result_no_learn_redirect_without_global_id": 1.3248331879949546, + "ecommerce/views/legacy/views_test.py::test_checkout_result_redirects_to_learn_when_flag_enabled": 1.2327517719968455, + "ecommerce/views/legacy/views_test.py::test_checkout_result_redirects_uai_b2c_courserun_to_learn_dashboard": 0.7349978410056792, + "ecommerce/views/legacy/views_test.py::test_checkout_result_redirects_uai_b2c_program_to_learn_dashboard": 0.5235153250105213, + "ecommerce/views/legacy/views_test.py::test_delete_basket_item": 0.1622115939971991, + "ecommerce/views/legacy/views_test.py::test_discount_redemptions_api[False]": 0.8193509470002027, + "ecommerce/views/legacy/views_test.py::test_discount_redemptions_api[True]": 0.8939255720033543, + "ecommerce/views/legacy/views_test.py::test_discount_rest_api": 0.0782181410031626, + "ecommerce/views/legacy/views_test.py::test_get_basket": 0.17311949400027515, + "ecommerce/views/legacy/views_test.py::test_get_basket_items": 0.33555274700484006, + "ecommerce/views/legacy/views_test.py::test_get_products": 0.6642375070005073, + "ecommerce/views/legacy/views_test.py::test_get_products_inactive": 0.5263451850041747, + "ecommerce/views/legacy/views_test.py::test_list_products": 1.231751767998503, + "ecommerce/views/legacy/views_test.py::test_non_upgradable_courserun_checkout[None-200]": 1.080288199998904, + "ecommerce/views/legacy/views_test.py::test_non_upgradable_courserun_checkout[upgrade_deadline0-302]": 0.856325561995618, + "ecommerce/views/legacy/views_test.py::test_non_upgradable_courserun_checkout[upgrade_deadline1-200]": 0.950187286012806, + "ecommerce/views/legacy/views_test.py::test_paid_and_unpaid_courserun_checkout": 1.0646790490136482, + "ecommerce/views/legacy/views_test.py::test_program_product_purchasing": 0.272540359008417, + "ecommerce/views/legacy/views_test.py::test_redeem_discount[False-True]": 0.5636741629932658, + "ecommerce/views/legacy/views_test.py::test_redeem_discount[True-False]": 0.5467584610014455, + "ecommerce/views/legacy/views_test.py::test_redeem_discount[True-True]": 0.5858313019998604, + "ecommerce/views/legacy/views_test.py::test_redeem_discount_with_higher_discount": 1.3662083330054884, + "ecommerce/views/legacy/views_test.py::test_redeem_product_discount[False]": 0.584905975003494, + "ecommerce/views/legacy/views_test.py::test_redeem_product_discount[True]": 0.5981387210049434, + "ecommerce/views/legacy/views_test.py::test_redeem_time_limited_discount[future-False]": 0.6283407439987059, + "ecommerce/views/legacy/views_test.py::test_redeem_time_limited_discount[past-False]": 0.7197316529927775, + "ecommerce/views/legacy/views_test.py::test_redeem_time_limited_discount[valid-True]": 0.6830528079954092, + "ecommerce/views/legacy/views_test.py::test_start_checkout_and_ensure_edx_username_created": 1.7333980929979589, + "ecommerce/views/legacy/views_test.py::test_start_checkout_with_discounts_and_b2b[False]": 0.527780720993178, + "ecommerce/views/legacy/views_test.py::test_start_checkout_with_discounts_and_b2b[True]": 0.8698020129959332, + "ecommerce/views/legacy/views_test.py::test_start_checkout_with_invalid_discounts": 1.552934870007448, + "ecommerce/views/legacy/views_test.py::test_start_checkout_with_zero_value": 2.0622880899973097, + "ecommerce/views/legacy/views_test.py::test_start_checkout_with_zero_value_redirects_to_learn": 1.5126035449939081, + "ecommerce/views/legacy/views_test.py::test_user_discounts_api": 0.3046539459974156, + "ecommerce/views/v0/views_test.py::test_add_basket_item": 0.24364504199911607, + "ecommerce/views/v0/views_test.py::test_bulk_discount_create[False]": 0.03759288400760852, + "ecommerce/views/v0/views_test.py::test_bulk_discount_create[True]": 0.0391109860065626, + "ecommerce/views/v0/views_test.py::test_create_basket_with_product[False-False-False-no]": 0.47145679599634605, + "ecommerce/views/v0/views_test.py::test_create_basket_with_product[False-True-False-no]": 0.6174846259964397, + "ecommerce/views/v0/views_test.py::test_create_basket_with_product[False-True-True-no]": 0.5274396859967965, + "ecommerce/views/v0/views_test.py::test_create_basket_with_product[True-False-False-no]": 0.48996319199795835, + "ecommerce/views/v0/views_test.py::test_create_basket_with_product[True-True-False-better]": 0.6749739619917818, + "ecommerce/views/v0/views_test.py::test_create_basket_with_product[True-True-False-worse]": 0.7483633440060657, + "ecommerce/views/v0/views_test.py::test_create_basket_with_product[True-True-True-better]": 0.6347539950002101, + "ecommerce/views/v0/views_test.py::test_create_basket_with_product[True-True-True-worse]": 0.6274544019906898, + "ecommerce/views/v0/views_test.py::test_create_basket_with_products[False-False-False]": 0.7378490580085781, + "ecommerce/views/v0/views_test.py::test_create_basket_with_products[False-False-True]": 0.7457959489984205, + "ecommerce/views/v0/views_test.py::test_create_basket_with_products[False-True-False]": 0.9290330549993087, + "ecommerce/views/v0/views_test.py::test_create_basket_with_products[False-True-True]": 0.9699896500023897, + "ecommerce/views/v0/views_test.py::test_create_basket_with_products[True-False-False]": 0.27895049499784363, + "ecommerce/views/v0/views_test.py::test_create_basket_with_products[True-False-True]": 0.24388566800189437, + "ecommerce/views/v0/views_test.py::test_create_basket_with_products[True-True-False]": 0.32005805199878523, + "ecommerce/views/v0/views_test.py::test_create_basket_with_products[True-True-True]": 0.2920065520083881, + "ecommerce/views/v0/views_test.py::test_delete_basket_item": 0.16269166800339008, + "ecommerce/views/v0/views_test.py::test_discount_redemptions_api[False]": 1.490240373997949, + "ecommerce/views/v0/views_test.py::test_discount_redemptions_api[True]": 2.1195125140002347, + "ecommerce/views/v0/views_test.py::test_discount_rest_api": 0.09245561998977792, + "ecommerce/views/v0/views_test.py::test_get_basket": 0.3713323760021012, + "ecommerce/views/v0/views_test.py::test_get_basket_item": 0.3407887159992242, + "ecommerce/views/v0/views_test.py::test_get_basket_items": 0.3585412910033483, + "ecommerce/views/v0/views_test.py::test_get_products": 0.7177322860006825, + "ecommerce/views/v0/views_test.py::test_get_products_inactive": 0.539189894006995, + "ecommerce/views/v0/views_test.py::test_list_products": 1.271775097004138, + "ecommerce/views/v0/views_test.py::test_order_history_list": 0.9805134260022896, + "ecommerce/views/v0/views_test.py::test_order_history_retrieve": 0.47422512999764876, + "ecommerce/views/v0/views_test.py::test_order_receipt_retrieve": 0.46799963600642513, + "ecommerce/views/v0/views_test.py::test_product_user_flexible_price": 0.5955473279973376, + "ecommerce/views/v0/views_test.py::test_product_user_flexible_price_no_flexible_price": 0.5634298880031565, + "ecommerce/views/v0/views_test.py::test_product_user_flexible_price_unauthenticated": 0.5155495390063152, + "ecommerce/views/v0/views_test.py::test_program_product_purchasing": 0.9368997080018744, + "ecommerce/views/v0/views_test.py::test_receipt_by_program_not_found": 0.1526724959985586, + "ecommerce/views/v0/views_test.py::test_receipt_by_program_redirects": 0.14946031400177162, + "ecommerce/views/v0/views_test.py::test_receipt_by_program_redirects_on_non_api_route": 0.17339135600923328, + "ecommerce/views/v0/views_test.py::test_receipt_by_program_unauthenticated": 0.02718271900084801, + "ecommerce/views/v0/views_test.py::test_receipt_by_program_uses_latest_paid_record": 0.130273834001855, + "ecommerce/views/v0/views_test.py::test_receipt_by_run_not_found": 0.15900379099912243, + "ecommerce/views/v0/views_test.py::test_receipt_by_run_redirects": 0.15748742699361173, + "ecommerce/views/v0/views_test.py::test_receipt_by_run_redirects_on_non_api_route": 0.1481275339901913, + "ecommerce/views/v0/views_test.py::test_receipt_by_run_unauthenticated": 0.018964315990160685, + "ecommerce/views/v0/views_test.py::test_receipt_by_run_uses_latest_paid_record": 0.1647031030006474, + "ecommerce/views/v0/views_test.py::test_redeem_discount[False-True]": 0.6691368730025715, + "ecommerce/views/v0/views_test.py::test_redeem_discount[True-False]": 0.5776656199959689, + "ecommerce/views/v0/views_test.py::test_redeem_discount[True-True]": 0.5111014899957809, + "ecommerce/views/v0/views_test.py::test_redeem_discount_with_higher_discount": 1.453610437005409, + "ecommerce/views/v0/views_test.py::test_redeem_product_discount[False]": 0.619583336992946, + "ecommerce/views/v0/views_test.py::test_redeem_product_discount[True]": 0.5162489749927772, + "ecommerce/views/v0/views_test.py::test_redeem_time_limited_discount[future-False]": 0.6037344319993281, + "ecommerce/views/v0/views_test.py::test_redeem_time_limited_discount[past-False]": 0.6494141130024218, + "ecommerce/views/v0/views_test.py::test_redeem_time_limited_discount[valid-True]": 0.6632152340025641, + "ecommerce/views/v0/views_test.py::test_start_checkout": 0.8543309109954862, + "ecommerce/views/v0/views_test.py::test_start_checkout_with_b2b_products[False-code]": 0.4608364910018281, + "ecommerce/views/v0/views_test.py::test_start_checkout_with_b2b_products[False-managed]": 0.4374897510060691, + "ecommerce/views/v0/views_test.py::test_start_checkout_with_b2b_products[True-code]": 0.5913862850065925, + "ecommerce/views/v0/views_test.py::test_start_checkout_with_bad_discount": 0.5450797479934408, + "ecommerce/views/v0/views_test.py::test_start_checkout_with_blocked_country": 0.2589428119972581, + "ecommerce/views/v0/views_test.py::test_start_checkout_with_discounts": 1.0840033779968508, + "ecommerce/views/v0/views_test.py::test_start_checkout_with_no_lines": 0.5579813639924396, + "ecommerce/views/v0/views_test.py::test_start_checkout_with_unupgradable_course": 0.3270938260029652, + "ecommerce/views/v0/views_test.py::test_start_checkout_with_upgraded_course": 0.7475490910001099, + "ecommerce/views/v0/views_test.py::test_user_discounts_api": 0.553352585004177, + "flexiblepricing/api_test.py::ExchangeRateAPITests::test_update_currency_exchange_rate": 0.027076574006059673, + "flexiblepricing/api_test.py::FlexiblePriceAPITests::test_determine_auto_approval_01__0___0___True_": 0.585231664001185, + "flexiblepricing/api_test.py::FlexiblePriceAPITests::test_determine_auto_approval_02__1___0___True_": 0.1766218770062551, + "flexiblepricing/api_test.py::FlexiblePriceAPITests::test_determine_auto_approval_03__0___50___False_": 0.17058116200496443, + "flexiblepricing/api_test.py::FlexiblePriceAPITests::test_determine_auto_approval_04__49999___50___False_": 0.18871912000031443, + "flexiblepricing/api_test.py::FlexiblePriceAPITests::test_determine_auto_approval_05__50000___50___False_": 0.18085547399095958, + "flexiblepricing/api_test.py::FlexiblePriceAPITests::test_determine_auto_approval_06__50001___50___True_": 0.18390531800105236, + "flexiblepricing/api_test.py::FlexiblePriceAPITests::test_determine_auto_approval_07__0___0___True__True_": 0.7234670149991871, + "flexiblepricing/api_test.py::FlexiblePriceAPITests::test_determine_auto_approval_08__1___0___True__True_": 0.2219006159939454, + "flexiblepricing/api_test.py::FlexiblePriceAPITests::test_determine_auto_approval_09__0___50___False__True_": 0.18261682800221024, + "flexiblepricing/api_test.py::FlexiblePriceAPITests::test_determine_auto_approval_10__49999___50___False__True_": 0.21542002700152807, + "flexiblepricing/api_test.py::FlexiblePriceAPITests::test_determine_auto_approval_11__50000___50___False__True_": 0.2186040939996019, + "flexiblepricing/api_test.py::FlexiblePriceAPITests::test_determine_auto_approval_12__50001___50___True__True_": 0.21352787198702572, + "flexiblepricing/api_test.py::FlexiblePriceAPITests::test_determine_courseware_flexible_price_discount_01__0___0___True_": 0.07317945099930512, + "flexiblepricing/api_test.py::FlexiblePriceAPITests::test_determine_courseware_flexible_price_discount_02__1___0___True_": 0.06150388699461473, + "flexiblepricing/api_test.py::FlexiblePriceAPITests::test_determine_courseware_flexible_price_discount_03__0___50___False_": 0.0654456430056598, + "flexiblepricing/api_test.py::FlexiblePriceAPITests::test_determine_courseware_flexible_price_discount_04__49999___50___False_": 0.07284297300793696, + "flexiblepricing/api_test.py::FlexiblePriceAPITests::test_determine_courseware_flexible_price_discount_05__50000___50___False_": 0.06644910199975129, + "flexiblepricing/api_test.py::FlexiblePriceAPITests::test_determine_courseware_flexible_price_discount_06__50001___50___True_": 0.06911558800493367, + "flexiblepricing/api_test.py::FlexiblePriceAPITests::test_determine_courseware_flexible_price_discount_07__0___0___True__True_": 0.055745774996466935, + "flexiblepricing/api_test.py::FlexiblePriceAPITests::test_determine_courseware_flexible_price_discount_08__1___0___True__True_": 0.059219214010227006, + "flexiblepricing/api_test.py::FlexiblePriceAPITests::test_determine_courseware_flexible_price_discount_09__0___50___False__True_": 0.04575536699849181, + "flexiblepricing/api_test.py::FlexiblePriceAPITests::test_determine_courseware_flexible_price_discount_10__49999___50___False__True_": 0.05249949300196022, + "flexiblepricing/api_test.py::FlexiblePriceAPITests::test_determine_courseware_flexible_price_discount_11__50000___50___False__True_": 0.0488902899887762, + "flexiblepricing/api_test.py::FlexiblePriceAPITests::test_determine_courseware_flexible_price_discount_12__50001___50___True__True_": 0.046449501991446596, + "flexiblepricing/api_test.py::FlexiblePriceAPITests::test_determine_courseware_flexible_price_discount_anonymous_user": 0.04277252699830569, + "flexiblepricing/api_test.py::FlexiblePriceAPITests::test_determine_courseware_flexible_price_discount_expired": 0.27059924000059254, + "flexiblepricing/api_test.py::FlexiblePriceAPITests::test_determine_courseware_flexible_pricing_hierarchy_1__0___0___True_": 0.3527208769955905, + "flexiblepricing/api_test.py::FlexiblePriceAPITests::test_determine_courseware_flexible_pricing_hierarchy_2__1___0___True_": 0.3496668910011067, + "flexiblepricing/api_test.py::FlexiblePriceAPITests::test_determine_courseware_flexible_pricing_hierarchy_3__0___50___False_": 0.3019366810040083, + "flexiblepricing/api_test.py::FlexiblePriceAPITests::test_determine_courseware_flexible_pricing_hierarchy_4__49999___50___False_": 0.33857640098722186, + "flexiblepricing/api_test.py::FlexiblePriceAPITests::test_determine_courseware_flexible_pricing_hierarchy_5__50000___50___False_": 0.32463202800863655, + "flexiblepricing/api_test.py::FlexiblePriceAPITests::test_determine_courseware_flexible_pricing_hierarchy_6__50001___50___True_": 0.3222270749974996, + "flexiblepricing/api_test.py::FlexiblePriceAPITests::test_determine_income_usd_from_not_usd": 0.02442860800510971, + "flexiblepricing/api_test.py::FlexiblePriceAPITests::test_determine_income_usd_from_usd": 0.02100168699689675, + "flexiblepricing/api_test.py::FlexiblePriceAPITests::test_determine_tier_courseware_1__0___0k__": 0.03389616100321291, + "flexiblepricing/api_test.py::FlexiblePriceAPITests::test_determine_tier_courseware_2__1000___0k__": 0.037199922000581864, + "flexiblepricing/api_test.py::FlexiblePriceAPITests::test_determine_tier_courseware_3__25000___25k__": 0.042567016986140516, + "flexiblepricing/api_test.py::FlexiblePriceAPITests::test_determine_tier_courseware_4__27500___25k__": 0.03712948199972743, + "flexiblepricing/api_test.py::FlexiblePriceAPITests::test_determine_tier_courseware_5__50000___50k__": 0.04449397999997018, + "flexiblepricing/api_test.py::FlexiblePriceAPITests::test_determine_tier_courseware_6__72800___50k__": 0.04087456599518191, + "flexiblepricing/api_test.py::FlexiblePriceAPITests::test_determine_tier_courseware_7__75000___75k__": 0.04089898100210121, + "flexiblepricing/api_test.py::FlexiblePriceAPITests::test_determine_tier_courseware_8__34938234___75k__": 0.04046369500429137, + "flexiblepricing/api_test.py::FlexiblePriceAPITests::test_determine_tier_courseware_improper_setup": 0.21816851199400844, + "flexiblepricing/api_test.py::FlexiblePriceAPITests::test_determine_tier_courseware_not_current_1_True": 0.030948469997383654, + "flexiblepricing/api_test.py::FlexiblePriceAPITests::test_determine_tier_courseware_not_current_2_False": 0.027741329002310522, + "flexiblepricing/api_test.py::FlexiblePriceAPITests::test_is_courseware_flexible_price_approved_1__True_": 0.2085305549990153, + "flexiblepricing/api_test.py::FlexiblePriceAPITests::test_is_courseware_flexible_price_approved_2__False_": 0.19003016699571162, + "flexiblepricing/api_test.py::test_create_finaid_form[course-False-False-False]": 0.18508635299804155, + "flexiblepricing/api_test.py::test_create_finaid_form[course-False-True-True]": 0.19415024800400715, + "flexiblepricing/api_test.py::test_create_finaid_form[coursemultipleprogram-False-False-False]": 0.292858217995672, + "flexiblepricing/api_test.py::test_create_finaid_form[coursemultipleprogram-True-False-False]": 0.3982988280040445, + "flexiblepricing/api_test.py::test_create_finaid_form[courseprogram-False-False-False]": 0.28771104000043124, + "flexiblepricing/api_test.py::test_create_finaid_form[courseprogram-False-True-True]": 0.28089733199885814, + "flexiblepricing/api_test.py::test_create_finaid_form[courseprogram-True-False-False]": 0.23747274700144771, + "flexiblepricing/api_test.py::test_create_finaid_form[program-False-False-False]": 0.1690409369984991, + "flexiblepricing/api_test.py::test_create_finaid_form[program-False-True-True]": 0.16155127000092762, + "flexiblepricing/api_test.py::test_import_country_income_thresholds": 0.028514597994217183, + "flexiblepricing/api_test.py::test_parse_country_income_thresholds": 0.015345053994678892, + "flexiblepricing/api_test.py::test_parse_country_income_thresholds_missing_header_fields": 0.015161861003434751, + "flexiblepricing/api_test.py::test_parse_country_income_thresholds_no_header": 0.016033807005442213, + "flexiblepricing/management/commands/configure_tiers_test.py::test_course_tier_setup[False]": 0.20427904900134308, + "flexiblepricing/management/commands/configure_tiers_test.py::test_course_tier_setup[True]": 0.20029444400279317, + "flexiblepricing/management/commands/configure_tiers_test.py::test_program_tier_setup[False]": 0.0642164280070574, + "flexiblepricing/management/commands/configure_tiers_test.py::test_program_tier_setup[True]": 0.06276109500322491, + "flexiblepricing/management/commands/create_product_for_program_courses_test.py::test_create_product_for_program_courses_command[program-v1:MITx+DEDP-1000-False]": 0.29104244399786694, + "flexiblepricing/management/commands/create_product_for_program_courses_test.py::test_create_product_for_program_courses_command[program-v1:MITx+DEDP-1000-True]": 0.2741160959994886, + "flexiblepricing/management/commands/create_product_for_program_courses_test.py::test_create_product_for_program_courses_command[program-v1:MITx+DEDP-900-True]": 0.28226769100729143, + "flexiblepricing/management/commands/create_product_for_program_courses_test.py::test_create_product_for_program_courses_command[program-v2:MITx+TEST-1000-False]": 0.27452130799065344, + "flexiblepricing/management/commands/create_product_for_program_courses_test.py::test_create_product_for_program_courses_command[program-v2:MITx+TEST-1000-True]": 0.24892005899891956, + "flexiblepricing/management/commands/create_product_for_program_courses_test.py::test_create_product_for_program_courses_command[program-v2:MITx+TEST-900-True]": 0.2501928830097313, + "flexiblepricing/models_test.py::test_submission_status": 0.5552019510068931, + "flexiblepricing/signals_test.py::TestFlexiblePriceSignals::test_should_process_flexible_price_invalid_statuses": 0.21179515799303772, + "flexiblepricing/signals_test.py::TestFlexiblePriceSignals::test_should_process_flexible_price_valid_statuses": 0.42807967800763436, + "flexiblepricing/tasks_test.py::TaskConfigurationTest::test_unset_currency_exchange_api_url": 0.03454627401515609, + "flexiblepricing/tasks_test.py::TasksTest::test_delete_currency_exchange_rate": 0.042629887007933576, + "flexiblepricing/tasks_test.py::TasksTest::test_exchange_rate_exceeded_api_calls": 0.02816604600957362, + "flexiblepricing/tasks_test.py::TasksTest::test_exchange_rate_unexpected_api_error": 0.029345349998038728, + "flexiblepricing/tasks_test.py::TasksTest::test_update_and_add_currency_exchange_rates": 0.030559283994080033, + "flexiblepricing/tasks_test.py::TestFlexiblePriceDiscountProcessing::test_calculate_discount_amount_no_discount": 0.6331486360068084, + "flexiblepricing/tasks_test.py::TestFlexiblePriceDiscountProcessing::test_calculate_discount_amount_success": 0.6129468660001294, + "flexiblepricing/tasks_test.py::TestFlexiblePriceDiscountProcessing::test_create_discount_api_call_failure": 0.5512490199980675, + "flexiblepricing/tasks_test.py::TestFlexiblePriceDiscountProcessing::test_create_discount_api_call_success": 0.47378014500282006, + "flexiblepricing/tasks_test.py::TestFlexiblePriceDiscountProcessing::test_get_valid_product_id_no_products": 0.42553745199984405, + "flexiblepricing/tasks_test.py::TestFlexiblePriceDiscountProcessing::test_get_valid_product_id_request_exception": 0.4061583569928189, + "flexiblepricing/tasks_test.py::TestFlexiblePriceDiscountProcessing::test_get_valid_product_id_success": 0.447532559010142, + "flexiblepricing/tasks_test.py::TestFlexiblePriceDiscountProcessing::test_process_course_discounts_no_runs": 0.5787502330058487, + "flexiblepricing/tasks_test.py::TestFlexiblePriceDiscountProcessing::test_process_course_discounts_success": 0.39331747200048994, + "flexiblepricing/tasks_test.py::TestFlexiblePriceDiscountProcessing::test_process_flexible_price_discount_course": 0.5528415310036507, + "flexiblepricing/tasks_test.py::TestFlexiblePriceDiscountProcessing::test_process_flexible_price_discount_program": 0.5023855250037741, + "flexiblepricing/tasks_test.py::TestFlexiblePriceDiscountProcessing::test_process_flexible_price_discount_task_error": 0.41803537200758, + "flexiblepricing/tasks_test.py::TestFlexiblePriceDiscountProcessing::test_process_flexible_price_discount_task_success": 0.5362550290010404, + "flexiblepricing/tasks_test.py::TestFlexiblePriceDiscountProcessing::test_validate_courseware_object_with_valid_object": 0.5082886169984704, + "flexiblepricing/tasks_test.py::test_financial_assistance_denied_email[approved]": 0.6532234559999779, + "flexiblepricing/tasks_test.py::test_financial_assistance_denied_email[denied]": 0.6294489889987744, + "flexiblepricing/tasks_test.py::test_financial_assistance_denied_email[pending-manual-approval]": 0.6348861860024044, + "flexiblepricing/tasks_test.py::test_financial_assistance_denied_email[reset]": 0.6583221889959532, + "flexiblepricing/tasks_test.py::test_flexible_price_status_change_email[approved]": 0.5891486990003614, + "flexiblepricing/tasks_test.py::test_flexible_price_status_change_email[pending-manual-approval]": 0.6509549249894917, + "flexiblepricing/tasks_test.py::test_flexible_price_status_change_email[reset]": 0.5620782139958465, + "flexiblepricing/tasks_test.py::test_process_flexible_price_discount_skips": 0.028292063005210366, + "flexiblepricing/utils_test.py::test_form_with_misnamed_fields[country_of_income]": 0.14343857399944682, + "flexiblepricing/utils_test.py::test_form_with_misnamed_fields[country_of_residence]": 0.15986244699161034, + "flexiblepricing/utils_test.py::test_form_with_misnamed_fields[income_currency]": 0.14248110500193434, + "flexiblepricing/utils_test.py::test_form_with_misnamed_fields[your_income]": 0.14074456800881308, + "flexiblepricing/utils_test.py::test_form_with_no_fields": 0.1860578450068715, + "flexiblepricing/utils_test.py::test_form_with_proper_fields": 0.1871053610011586, + "flexiblepricing/views_test.py::test_basic_country_income_thresholds": 0.039300492993788794, + "flexiblepricing/views_test.py::test_basic_exchange_rates": 0.03624417600076413, + "flexiblepricing/views_test.py::test_basic_flex_payments": 1.1900610310040065, + "hubspot_sync/api_test.py::test_associate_objects_with_retry_raises_after_exhausting_retries": 0.02072535001207143, + "hubspot_sync/api_test.py::test_associate_objects_with_retry_retries_on_api_exception": 0.017051759001333266, + "hubspot_sync/api_test.py::test_associate_objects_with_retry_succeeds_on_first_attempt": 0.016558010000153445, + "hubspot_sync/api_test.py::test_build_target_line_item_message_uses_target_product_id_from_search": 0.15353594200860243, + "hubspot_sync/api_test.py::test_ensure_target_hubspot_custom_properties_skips_unique_app_id_update": 1.8438819040165981, + "hubspot_sync/api_test.py::test_ensure_target_hubspot_product_for_line_creates_when_missing": 0.16114534199732589, + "hubspot_sync/api_test.py::test_existing_user_sync_contact_with_hubspot_error": 0.057257287997344974, + "hubspot_sync/api_test.py::test_get_course_run_certificate_hubspot_property_removes_semicolons": 0.12206445199262816, + "hubspot_sync/api_test.py::test_get_hubspot_id_for_user[False]": 0.06298408700240543, + "hubspot_sync/api_test.py::test_get_hubspot_id_for_user[True]": 0.028483761998359114, + "hubspot_sync/api_test.py::test_get_hubspot_id_raises": 0.0534642310012714, + "hubspot_sync/api_test.py::test_get_program_certificate_hubspot_property_removes_semicolons": 0.15471056398382643, + "hubspot_sync/api_test.py::test_make_contact_sync_message": 0.4687797020014841, + "hubspot_sync/api_test.py::test_make_contact_sync_message_empty_name": 0.037106474992469884, + "hubspot_sync/api_test.py::test_make_contact_sync_message_minimal_user": 0.03415924299770268, + "hubspot_sync/api_test.py::test_make_deal_sync_message": 0.19921123499807436, + "hubspot_sync/api_test.py::test_make_line_item_sync_message[deferred-audit]": 0.17297056799725397, + "hubspot_sync/api_test.py::test_make_line_item_sync_message[deferred-verified]": 0.18601376500009792, + "hubspot_sync/api_test.py::test_make_line_item_sync_message[refunded-audit]": 0.2028653419984039, + "hubspot_sync/api_test.py::test_make_line_item_sync_message[refunded-verified]": 0.20070630299596814, + "hubspot_sync/api_test.py::test_make_line_item_sync_message[transferred-audit]": 0.18122690699237864, + "hubspot_sync/api_test.py::test_make_line_item_sync_message[transferred-verified]": 0.18264140400424367, + "hubspot_sync/api_test.py::test_make_line_item_sync_message[unenrolled-audit]": 0.1910859400013578, + "hubspot_sync/api_test.py::test_make_line_item_sync_message[unenrolled-verified]": 0.16567139900143957, + "hubspot_sync/api_test.py::test_make_product_sync_message": 0.1222523569886107, + "hubspot_sync/api_test.py::test_normalize_deal_properties_for_target_account_falls_back_to_default_pipeline": 0.020933367988618556, + "hubspot_sync/api_test.py::test_normalize_deal_properties_for_target_account_falls_back_to_hubspot_pipeline_id": 0.021854147998965345, + "hubspot_sync/api_test.py::test_normalize_deal_properties_for_target_account_pipeline_stage_mismatch": 0.01799288400070509, + "hubspot_sync/api_test.py::test_normalize_deal_properties_for_target_account_prefers_ecommerce_pipeline": 0.019831780999084003, + "hubspot_sync/api_test.py::test_normalize_deal_properties_preserves_numeric_stage_already_valid": 0.01928986200800864, + "hubspot_sync/api_test.py::test_sync_cart_add_deal_with_hubspot_normalizes_stage_after_override": 0.15963913000450702, + "hubspot_sync/api_test.py::test_sync_cart_add_deal_with_hubspot_sets_checkout_abandoned_stage": 0.18885320699337171, + "hubspot_sync/api_test.py::test_sync_contact_hubspot_ids_alternate[False]": 0.03815886100346688, + "hubspot_sync/api_test.py::test_sync_contact_hubspot_ids_alternate[True]": 0.034639037992747035, + "hubspot_sync/api_test.py::test_sync_contact_hubspot_ids_to_hubspot[False]": 0.05023538100067526, + "hubspot_sync/api_test.py::test_sync_contact_hubspot_ids_to_hubspot[True]": 0.05719175199919846, + "hubspot_sync/api_test.py::test_sync_contact_with_hubspot": 0.05250741799682146, + "hubspot_sync/api_test.py::test_sync_contact_with_hubspot_error": 0.0465912720101187, + "hubspot_sync/api_test.py::test_sync_contact_with_hubspot_skips_b2b_users": 0.1183933549982612, + "hubspot_sync/api_test.py::test_sync_contact_with_hubspot_syncs_regular_users": 0.06237637699814513, + "hubspot_sync/api_test.py::test_sync_course_run_certificate_with_hubspot": 0.022642043986706994, + "hubspot_sync/api_test.py::test_sync_deal_hubspot_ids_to_hubspot[False-False]": 0.6171432430055575, + "hubspot_sync/api_test.py::test_sync_deal_hubspot_ids_to_hubspot[False-True]": 0.6395799889942282, + "hubspot_sync/api_test.py::test_sync_deal_hubspot_ids_to_hubspot[True-False]": 0.6729597470039153, + "hubspot_sync/api_test.py::test_sync_deal_hubspot_ids_to_hubspot[True-True]": 0.7411486899945885, + "hubspot_sync/api_test.py::test_sync_deal_line_hubspot_ids_to_hubspot_two_lines[False-3]": 0.3421794459936791, + "hubspot_sync/api_test.py::test_sync_deal_line_hubspot_ids_to_hubspot_two_lines[True-2]": 0.3716015779937152, + "hubspot_sync/api_test.py::test_sync_deal_with_hubspot": 0.3355494940042263, + "hubspot_sync/api_test.py::test_sync_deal_with_hubspot_skips_b2b_users": 0.10983083499741042, + "hubspot_sync/api_test.py::test_sync_deal_with_hubspot_syncs_contact_when_missing": 0.2507846289954614, + "hubspot_sync/api_test.py::test_sync_deal_with_hubspot_targeted_skips_b2b_users": 0.11751959999674, + "hubspot_sync/api_test.py::test_sync_deal_with_hubspot_targeted_updates_when_found_by_unique_app_id": 0.19014478199824225, + "hubspot_sync/api_test.py::test_sync_line_item_with_hubspot": 0.38958707400161074, + "hubspot_sync/api_test.py::test_sync_line_item_with_hubspot_syncs_deal_when_missing": 0.2631512689986266, + "hubspot_sync/api_test.py::test_sync_product_hubspot_ids_dupe_names": 0.2325633140062564, + "hubspot_sync/api_test.py::test_sync_product_hubspot_ids_to_hubspot[False]": 0.33768747100111796, + "hubspot_sync/api_test.py::test_sync_product_hubspot_ids_to_hubspot[True]": 0.3353711020099581, + "hubspot_sync/api_test.py::test_sync_product_with_hubspot": 0.15917595099745085, + "hubspot_sync/api_test.py::test_sync_program_certificate_with_hubspot": 0.06320869398768991, + "hubspot_sync/api_test.py::test_track_cart_add_with_hubspot_returns_false_when_contact_sync_fails": 0.16702739399624988, + "hubspot_sync/api_test.py::test_track_cart_add_with_hubspot_returns_false_when_unconfigured": 0.15273429799708538, + "hubspot_sync/api_test.py::test_track_cart_add_with_hubspot_syncs_missing_contact": 0.18399839199264534, + "hubspot_sync/api_test.py::test_track_cart_add_with_hubspot_uai_fallback_to_primary_account": 0.1959464379979181, + "hubspot_sync/api_test.py::test_track_cart_add_with_hubspot_uses_uai_account": 0.180291083001066, + "hubspot_sync/rate_limiter_test.py::TestHubSpotRateLimiter::test_init_sets_correct_defaults": 0.01516114600963192, + "hubspot_sync/rate_limiter_test.py::TestHubSpotRateLimiter::test_init_with_custom_delay": 0.01624952500424115, + "hubspot_sync/rate_limiter_test.py::TestHubSpotRateLimiter::test_wait_for_rate_limit_cleanup_counter": 76.2940897689914, + "hubspot_sync/rate_limiter_test.py::TestHubSpotRateLimiter::test_wait_for_rate_limit_cleanup_old_timestamps": 0.013381675002165139, + "hubspot_sync/rate_limiter_test.py::TestHubSpotRateLimiter::test_wait_for_rate_limit_concurrent_requests_in_window": 0.013550212992413435, + "hubspot_sync/rate_limiter_test.py::TestHubSpotRateLimiter::test_wait_for_rate_limit_first_request": 0.013986007994390093, + "hubspot_sync/rate_limiter_test.py::TestHubSpotRateLimiter::test_wait_for_rate_limit_jitter": 0.015631933994882274, + "hubspot_sync/rate_limiter_test.py::TestHubSpotRateLimiter::test_wait_for_rate_limit_logs_sleep_time": 0.06606254699727288, + "hubspot_sync/rate_limiter_test.py::TestHubSpotRateLimiter::test_wait_for_rate_limit_negative_jitter_protection": 0.014534453002852388, + "hubspot_sync/rate_limiter_test.py::TestHubSpotRateLimiter::test_wait_for_rate_limit_no_sleep_when_min_delay_satisfied": 0.01618578899797285, + "hubspot_sync/rate_limiter_test.py::TestHubSpotRateLimiter::test_wait_for_rate_limit_respects_min_delay": 0.015178159010247327, + "hubspot_sync/rate_limiter_test.py::TestHubSpotRateLimiter::test_wait_for_rate_limit_sliding_window_limit": 0.014045100004295819, + "hubspot_sync/rate_limiter_test.py::TestHubSpotRateLimiter::test_wait_for_rate_limit_thread_safety": 0.6722084849970997, + "hubspot_sync/rate_limiter_test.py::TestHubSpotRateLimiter::test_wait_for_rate_limit_window_size_boundary": 0.013839953004207928, + "hubspot_sync/rate_limiter_test.py::TestHubSpotRateLimiter::test_wait_for_rate_limit_zero_min_delay": 0.014619542002037633, + "hubspot_sync/serializers_test.py::test_serialize_line": 0.21443820500280708, + "hubspot_sync/serializers_test.py::test_serialize_line_no_corresponding_enrollment": 0.18856974200025434, + "hubspot_sync/serializers_test.py::test_serialize_order[fulfilled]": 0.18988626200007275, + "hubspot_sync/serializers_test.py::test_serialize_order[pending]": 0.21941747199889505, + "hubspot_sync/serializers_test.py::test_serialize_order_with_coupon[dollars-off-amount1-37.50-75.00-False]": 0.28640812799858395, + "hubspot_sync/serializers_test.py::test_serialize_order_with_coupon[fixed-price-amount2-62.50-125.00-False]": 0.3084619009969174, + "hubspot_sync/serializers_test.py::test_serialize_order_with_coupon[fixed-price-amount3-100.00-200.00-False]": 0.3078347629925702, + "hubspot_sync/serializers_test.py::test_serialize_order_with_coupon[fixed-price-amount4-100.00-0.00-True]": 0.3017907600005856, + "hubspot_sync/serializers_test.py::test_serialize_order_with_coupon[percent-off-amount0-75.00-150.00-False]": 0.29018187699693954, + "hubspot_sync/serializers_test.py::test_serialize_product[course-v1:MITxOnline+SysEngxNAV+R1-Run 1]": 0.15110950800590217, + "hubspot_sync/serializers_test.py::test_serialize_product[course-v1:MITxOnline+SysEngxNAV+R10-Run 10]": 0.1255245249994914, + "hubspot_sync/serializers_test.py::test_serialize_product[course-v1:MITxOnline+SysEngxNAV-course-v1:MITxOnline+SysEngxNAV]": 0.12687481900502462, + "hubspot_sync/task_helpers_test.py::test_sync_hubspot_cart_add[False]": 0.0731057490047533, + "hubspot_sync/task_helpers_test.py::test_sync_hubspot_cart_add[True]": 0.07190819500101497, + "hubspot_sync/task_helpers_test.py::test_sync_hubspot_deal_non_uai_order[False]": 0.19988933900458505, + "hubspot_sync/task_helpers_test.py::test_sync_hubspot_deal_non_uai_order[True]": 0.19343550100165885, + "hubspot_sync/task_helpers_test.py::test_sync_hubspot_deal_uai_order_with_uai_token[False]": 0.17617871399852447, + "hubspot_sync/task_helpers_test.py::test_sync_hubspot_deal_uai_order_with_uai_token[True]": 0.15803229799348628, + "hubspot_sync/task_helpers_test.py::test_sync_hubspot_product[False]": 0.06516271299187792, + "hubspot_sync/task_helpers_test.py::test_sync_hubspot_product[True]": 0.06524008300038986, + "hubspot_sync/task_helpers_test.py::test_sync_hubspot_user[False]": 0.028815461999329273, + "hubspot_sync/task_helpers_test.py::test_sync_hubspot_user[True]": 0.028870309004560113, + "hubspot_sync/task_helpers_test.py::test_sync_hubspot_user_skips_b2b_users": 0.12756685700151138, + "hubspot_sync/task_helpers_test.py::test_sync_hubspot_user_syncs_regular_users": 0.027168446002178825, + "hubspot_sync/tasks_test.py::TestRateLimitingInBatchFunctions::test_batch_create_hubspot_objects_chunked_calls_rate_limit[15]": 0.5187557870012824, + "hubspot_sync/tasks_test.py::TestRateLimitingInBatchFunctions::test_batch_create_hubspot_objects_chunked_calls_rate_limit[5]": 0.17420057600247674, + "hubspot_sync/tasks_test.py::TestRateLimitingInBatchFunctions::test_batch_create_hubspot_objects_chunked_rate_limit_call_order": 0.19942933199490653, + "hubspot_sync/tasks_test.py::TestRateLimitingInBatchFunctions::test_batch_create_hubspot_objects_chunked_rate_limit_called_after_exception": 0.14985374898969894, + "hubspot_sync/tasks_test.py::TestRateLimitingInBatchFunctions::test_batch_create_hubspot_objects_chunked_rate_limit_called_on_exception_no_reraise": 0.13131129499379313, + "hubspot_sync/tasks_test.py::TestRateLimitingInBatchFunctions::test_batch_update_hubspot_objects_chunked_calls_rate_limit[15]": 0.4137215750015457, + "hubspot_sync/tasks_test.py::TestRateLimitingInBatchFunctions::test_batch_update_hubspot_objects_chunked_calls_rate_limit[5]": 0.1642868830022053, + "hubspot_sync/tasks_test.py::TestRateLimitingInBatchFunctions::test_batch_update_hubspot_objects_chunked_rate_limit_call_order": 0.1329083150048973, + "hubspot_sync/tasks_test.py::TestRateLimitingInBatchFunctions::test_batch_update_hubspot_objects_chunked_rate_limit_called_after_exception": 0.1472360380066675, + "hubspot_sync/tasks_test.py::TestRateLimitingInBatchFunctions::test_batch_update_hubspot_objects_chunked_rate_limit_called_on_exception_no_reraise": 0.11680649699701462, + "hubspot_sync/tasks_test.py::test_batch_create_hubspot_objects_chunked[15]": 0.6548812420005561, + "hubspot_sync/tasks_test.py::test_batch_create_hubspot_objects_chunked[5]": 0.2875553000048967, + "hubspot_sync/tasks_test.py::test_batch_create_hubspot_objects_chunked_error[429-TooManyRequestsException]": 0.2957756629984942, + "hubspot_sync/tasks_test.py::test_batch_create_hubspot_objects_chunked_error[500-ApiException]": 0.29982733299402753, + "hubspot_sync/tasks_test.py::test_batch_create_hubspot_objects_with_ids": 0.09984012100176187, + "hubspot_sync/tasks_test.py::test_batch_update_hubspot_objects_chunked[15]": 0.6258195029950002, + "hubspot_sync/tasks_test.py::test_batch_update_hubspot_objects_chunked[5]": 0.19826686000305926, + "hubspot_sync/tasks_test.py::test_batch_update_hubspot_objects_chunked_error[429-TooManyRequestsException]": 0.2666421679969062, + "hubspot_sync/tasks_test.py::test_batch_update_hubspot_objects_chunked_error[500-ApiException]": 0.28524440799083095, + "hubspot_sync/tasks_test.py::test_batch_update_hubspot_objects_with_ids": 0.8775095110104303, + "hubspot_sync/tasks_test.py::test_batch_upsert_associations": 0.21837141500873258, + "hubspot_sync/tasks_test.py::test_batch_upsert_associations_chunked": 0.720264796000265, + "hubspot_sync/tasks_test.py::test_batch_upsert_hubspot_objects[False]": 0.24862496700370684, + "hubspot_sync/tasks_test.py::test_batch_upsert_hubspot_objects[True]": 0.23026666200894397, + "hubspot_sync/tasks_test.py::test_handle_failed_batch_chunk[False-False]": 0.019286110007669777, + "hubspot_sync/tasks_test.py::test_handle_failed_batch_chunk[False-True]": 0.018376810003246646, + "hubspot_sync/tasks_test.py::test_handle_failed_batch_chunk[True-False]": 0.020818165990931448, + "hubspot_sync/tasks_test.py::test_handle_failed_batch_chunk[True-True]": 0.023099253005057108, + "hubspot_sync/tasks_test.py::test_sync_failed_contacts": 0.22580278900568374, + "hubspot_sync/tasks_test.py::test_task_functions_error[429-TooManyRequestsException-sync_contact_with_hubspot]": 0.028032121001160704, + "hubspot_sync/tasks_test.py::test_task_functions_error[429-TooManyRequestsException-sync_deal_with_hubspot]": 0.030406106008740608, + "hubspot_sync/tasks_test.py::test_task_functions_error[429-TooManyRequestsException-sync_product_with_hubspot]": 0.14532814700214658, + "hubspot_sync/tasks_test.py::test_task_functions_error[500-ApiException-sync_contact_with_hubspot]": 0.026240940002026036, + "hubspot_sync/tasks_test.py::test_task_functions_error[500-ApiException-sync_deal_with_hubspot]": 0.040625740999530535, + "hubspot_sync/tasks_test.py::test_task_functions_error[500-ApiException-sync_product_with_hubspot]": 0.1837880670063896, + "hubspot_sync/tasks_test.py::test_task_sync_cart_add_event_with_hubspot": 0.15048342200316256, + "hubspot_sync/tasks_test.py::test_task_sync_contact_with_hubspot": 0.03372194700205, + "hubspot_sync/tasks_test.py::test_task_sync_course_run_certificate_with_hubspot": 0.1643598160080728, + "hubspot_sync/tasks_test.py::test_task_sync_deal_with_hubspot": 0.02899582000100054, + "hubspot_sync/tasks_test.py::test_task_sync_deal_with_hubspot_skips_b2b_users": 0.13684048599679954, + "hubspot_sync/tasks_test.py::test_task_sync_deal_with_hubspot_targeted": 0.0330251949999365, + "hubspot_sync/tasks_test.py::test_task_sync_deal_with_hubspot_targeted_api_error[429-TooManyRequestsException]": 0.03209476699703373, + "hubspot_sync/tasks_test.py::test_task_sync_deal_with_hubspot_targeted_api_error[500-ApiException]": 0.03152813300403068, + "hubspot_sync/tasks_test.py::test_task_sync_deal_with_hubspot_targeted_no_token_error": 0.029181708006944973, + "hubspot_sync/tasks_test.py::test_task_sync_deal_with_hubspot_targeted_order_not_found": 0.021161749995371792, + "hubspot_sync/tasks_test.py::test_task_sync_deal_with_hubspot_targeted_skips_b2b_users": 0.1253431530058151, + "hubspot_sync/tasks_test.py::test_task_sync_deal_with_hubspot_targeted_uai_order": 0.03606862800370436, + "hubspot_sync/tasks_test.py::test_task_sync_product_with_hubspot": 0.15154916101164417, + "hubspot_sync/tasks_test.py::test_task_sync_program_certificate_with_hubspot": 0.13928034699347336, + "hubspot_sync/test_rate_limiting_integration.py::TestAPIRateLimitingIntegration::test_sync_contact_with_hubspot_applies_rate_limiting": 0.04087095999420853, + "hubspot_sync/test_rate_limiting_integration.py::TestAPIRateLimitingIntegration::test_sync_contact_with_hubspot_rate_limit_called_before_api": 0.045583173996419646, + "hubspot_sync/test_rate_limiting_integration.py::TestRateLimitingErrorScenarios::test_rate_limiting_with_api_exception": 0.05501281800388824, + "hubspot_sync/test_rate_limiting_integration.py::TestRateLimitingErrorScenarios::test_rate_limiting_with_too_many_requests_exception": 0.041729838005267084, + "hubspot_sync/test_rate_limiting_integration.py::TestTaskRateLimitingIntegration::test_sync_contact_with_hubspot_task": 0.026051670996821485, + "hubspot_sync/test_rate_limiting_integration.py::TestTaskRateLimitingIntegration::test_sync_failed_contacts_applies_rate_limiting": 0.037624630000209436, + "hubspot_sync/test_rate_limiting_integration.py::TestTaskRateLimitingIntegration::test_sync_failed_contacts_handles_exceptions": 0.04431376600405201, + "hubspot_sync/test_rate_limiting_integration.py::TestTaskRateLimitingIntegration::test_sync_failed_contacts_rate_limiting_called_before_sync": 0.023204577999422327, + "mail/api_test.py::test_build_message": 0.030525891997967847, + "mail/api_test.py::test_build_message_optional_params": 0.027813938999315724, + "mail/api_test.py::test_build_messages": 0.021034024997788947, + "mail/api_test.py::test_build_user_specific_messages": 0.02413710100518074, + "mail/api_test.py::test_context_for_user[None-None]": 0.017832530997111462, + "mail/api_test.py::test_context_for_user[None-user]": 0.02616691900038859, + "mail/api_test.py::test_context_for_user[extra_context1-None]": 0.01944530899345409, + "mail/api_test.py::test_context_for_user[extra_context1-user]": 0.029602827999042347, + "mail/api_test.py::test_context_for_user[extra_context2-None]": 0.020789354006410576, + "mail/api_test.py::test_context_for_user[extra_context2-user]": 0.028321079997112975, + "mail/api_test.py::test_messages_for_recipients": 0.06666007800231455, + "mail/api_test.py::test_render_email_templates": 0.029206939987489022, + "mail/api_test.py::test_safe_format_recipients": 0.05047508700954495, + "mail/api_test.py::test_safe_format_recipients_override": 0.03361071299150353, + "mail/api_test.py::test_send_message": 0.07788212200102862, + "mail/api_test.py::test_send_message_failure": 0.05211048899946036, + "mail/api_test.py::test_send_message_failure_raise_error": 0.055940355006896425, + "mail/verification_api_test.py::test_send_verify_email_change_email": 0.04487888599396683, + "main/env_test.py::test_get_float": 0.0190885980046005, + "main/middleware_test.py::test_host_based_csrf_middleware[-]": 0.01955906999501167, + "main/middleware_test.py::test_host_based_csrf_middleware[http://-]": 0.019681643003423233, + "main/middleware_test.py::test_host_based_csrf_middleware[http://api.learn.mit.edu-api.learn.mit.edu]": 0.02083281700470252, + "main/middleware_test.py::test_host_based_csrf_middleware[http://example.com-]": 0.021024202986154705, + "main/middleware_test.py::test_host_based_csrf_middleware[http://learn.mit.edu-learn.mit.edu]": 0.019253894992289133, + "main/middleware_test.py::test_host_based_csrf_middleware[http://localhost-]": 0.02101132499956293, + "main/middleware_test.py::test_host_based_csrf_middleware[http://mitxonline.mit.edu-mitxonline.mit.edu]": 0.02861059499264229, + "main/middleware_test.py::test_host_based_csrf_middleware[http://mitxonline.mit.edu:8080-]": 0.0193254769910709, + "main/middleware_test.py::test_host_based_csrf_middleware[http://mitxonline.odl.local:8013-mitxonline.odl.local]": 0.01996929300366901, + "main/middleware_test.py::test_host_based_csrf_middleware[http://sub.sub.sub.learn.mit.edu-sub.sub.sub.learn.mit.edu]": 0.019814512008451857, + "main/middleware_test.py::test_host_based_csrf_middleware[not-a-url-]": 0.01995728100882843, + "main/middleware_test.py::test_host_based_csrf_middleware_no_referer": 0.019796133005002048, + "main/permissions_test.py::test_user_is_owner_permission[False-False]": 0.014220534998457879, + "main/permissions_test.py::test_user_is_owner_permission[False-True]": 0.01432078600191744, + "main/permissions_test.py::test_user_is_owner_permission[True-False]": 0.014319661000627093, + "main/permissions_test.py::test_user_is_owner_permission[True-True]": 0.015501426998525858, + "main/settings_test.py::test_admin_settings": 0.2147675380037981, + "main/settings_test.py::test_bump_my_version_format": 0.0002631089955684729, + "main/settings_test.py::test_csrf_cookie_domain": 0.14573861999087967, + "main/settings_test.py::test_csrf_trusted_origins": 0.14830607501062332, + "main/settings_test.py::test_db_ssl_enable": 0.2729025919979904, + "main/settings_test.py::test_mitol_apigateway_allowed_redirect_hosts": 0.14839060999656795, + "main/settings_test.py::test_s3_settings": 0.21516732099553337, + "main/test_utils_test.py::test_assert_drf_json_equall": 0.012937615996634122, + "main/test_utils_test.py::test_assert_not_raises_exception": 0.013832552991516422, + "main/test_utils_test.py::test_assert_not_raises_failure": 0.012849414997617714, + "main/test_utils_test.py::test_assert_not_raises_none": 0.014115850994130597, + "main/test_utils_test.py::test_mock_response[123-123-123]": 0.012291089005884714, + "main/test_utils_test.py::test_mock_response[content1-{\"test\": \"content\"}-expected_json1]": 0.014047636999748647, + "main/test_utils_test.py::test_mock_response[content2-[\"test\", \"content\"]-expected_json2]": 0.012426320994563866, + "main/test_utils_test.py::test_mock_response[{\"test\": \"content\"}-{\"test\": \"content\"}-expected_json0]": 0.013090726999507751, + "main/tests/js_interop_test.py::test_js_settings": 0.014425999994273297, + "main/urls_test.py::URLTests::test_urls": 0.01513324700499652, + "main/utils_test.py::test_date_to_datetime": 0.01386232200457016, + "main/utils_test.py::test_get_field_names": 0.016268310995656066, + "main/utils_test.py::test_get_js_settings": 0.02564524200715823, + "main/utils_test.py::test_get_partitioned_set_difference": 0.014236254006391391, + "main/utils_test.py::test_parse_supplied_data": 0.013488782999047544, + "main/views_test.py::test_cms_signin_redirect_to_site_signin": 0.02355589000217151, + "main/views_test.py::test_dashboard_no_redirect_for_anonymous": 0.03491855900210794, + "main/views_test.py::test_dashboard_no_redirect_without_global_id": 0.046224015997722745, + "main/views_test.py::test_dashboard_redirect[False]": 0.04181359599897405, + "main/views_test.py::test_dashboard_redirect[True]": 0.04796755399729591, + "main/views_test.py::test_dashboard_redirect_preserves_query_params": 0.039972854996449314, + "main/views_test.py::test_never_cache_react_views[staff-dashboard]": 0.03689811799995368, + "main/views_test.py::test_never_cache_react_views[user-dashboard]": 0.040173287998186424, + "main/views_test.py::test_noindex_meta_on_learner_records[/dashboard/-False]": 0.023945534994709305, + "main/views_test.py::test_noindex_meta_on_learner_records[/records/-True]": 0.026202982000540942, + "main/views_test.py::test_noindex_meta_on_learner_records[/records/programs/some-uuid/-True]": 0.025866943993605673, + "openedx/api_test.py::test_bulk_retire_edx_users": 0.016055411004344933, + "openedx/api_test.py::test_create_edx_auth_token": 0.08119912399706664, + "openedx/api_test.py::test_create_edx_user[missing_username:false-None-0-synced:false]": 0.05359616100031417, + "openedx/api_test.py::test_create_edx_user[missing_username:false-None-0-synced:true]": 0.05022557899792446, + "openedx/api_test.py::test_create_edx_user[missing_username:false-None-1-synced:false]": 0.06483483899501152, + "openedx/api_test.py::test_create_edx_user[missing_username:false-None-1-synced:true]": 0.07685777999722632, + "openedx/api_test.py::test_create_edx_user[missing_username:false-None-3-synced:false]": 0.06772110399469966, + "openedx/api_test.py::test_create_edx_user[missing_username:false-None-3-synced:true]": 0.06117171100049745, + "openedx/api_test.py::test_create_edx_user[missing_username:false-test_username-0-synced:false]": 0.06946601399977226, + "openedx/api_test.py::test_create_edx_user[missing_username:false-test_username-0-synced:true]": 0.061436128002242185, + "openedx/api_test.py::test_create_edx_user[missing_username:false-test_username-1-synced:false]": 0.06415190699772211, + "openedx/api_test.py::test_create_edx_user[missing_username:false-test_username-1-synced:true]": 0.06017440799041651, + "openedx/api_test.py::test_create_edx_user[missing_username:false-test_username-3-synced:false]": 0.06879750699590659, + "openedx/api_test.py::test_create_edx_user[missing_username:false-test_username-3-synced:true]": 0.062473738005792256, + "openedx/api_test.py::test_create_edx_user[missing_username:true-None-0-synced:false]": 0.07067495299270377, + "openedx/api_test.py::test_create_edx_user[missing_username:true-None-0-synced:true]": 0.031871138002316, + "openedx/api_test.py::test_create_edx_user[missing_username:true-None-1-synced:false]": 0.06322376199386781, + "openedx/api_test.py::test_create_edx_user[missing_username:true-None-1-synced:true]": 0.02786330499657197, + "openedx/api_test.py::test_create_edx_user[missing_username:true-None-3-synced:false]": 0.06499316499684937, + "openedx/api_test.py::test_create_edx_user[missing_username:true-None-3-synced:true]": 0.030661713004519697, + "openedx/api_test.py::test_create_edx_user[missing_username:true-test_username-0-synced:false]": 0.057376741991902236, + "openedx/api_test.py::test_create_edx_user[missing_username:true-test_username-0-synced:true]": 0.026159547000133898, + "openedx/api_test.py::test_create_edx_user[missing_username:true-test_username-1-synced:false]": 0.07196756600023946, + "openedx/api_test.py::test_create_edx_user[missing_username:true-test_username-1-synced:true]": 0.026020568002422806, + "openedx/api_test.py::test_create_edx_user[missing_username:true-test_username-3-synced:false]": 0.0707598339940887, + "openedx/api_test.py::test_create_edx_user[missing_username:true-test_username-3-synced:true]": 0.030933176007238217, + "openedx/api_test.py::test_create_edx_user_409_errors[200-error_data0]": 0.057834412007650826, + "openedx/api_test.py::test_create_edx_user_409_errors[200-error_data1]": 0.06476841100811725, + "openedx/api_test.py::test_create_edx_user_409_errors[400-error_data0]": 0.05920464699738659, + "openedx/api_test.py::test_create_edx_user_409_errors[400-error_data1]": 0.052158022997900844, + "openedx/api_test.py::test_create_edx_user_409_errors[500-error_data0]": 0.04894517900538631, + "openedx/api_test.py::test_create_edx_user_409_errors[500-error_data1]": 0.051004475993977394, + "openedx/api_test.py::test_create_edx_user_conflict[username_suggestions0-testuser--with OpenEdX suggestions-False]": 0.051741075003519654, + "openedx/api_test.py::test_create_edx_user_conflict[username_suggestions1-Jos\\xe9--with empty suggestions (non-ASCII fallback)-False]": 0.05660003698721994, + "openedx/api_test.py::test_create_edx_user_conflict[username_suggestions2-Jos\\xe9--with empty suggestions (non-ASCII fallback)-True]": 0.06139570099912817, + "openedx/api_test.py::test_create_edx_user_conflict_suggested_username_taken_locally": 0.06011098599265097, + "openedx/api_test.py::test_create_edx_user_for_user_not_synced_with_edx[False-False]": 0.04330418199242558, + "openedx/api_test.py::test_create_edx_user_for_user_not_synced_with_edx[False-True]": 0.04139057499560295, + "openedx/api_test.py::test_create_edx_user_for_user_not_synced_with_edx[True-False]": 0.03633438700489933, + "openedx/api_test.py::test_create_edx_user_for_user_not_synced_with_edx[True-True]": 0.0321781699967687, + "openedx/api_test.py::test_create_user": 0.028655720998358447, + "openedx/api_test.py::test_create_user_ignore_edx_failures_flag[False-False-False]": 0.02858929699868895, + "openedx/api_test.py::test_create_user_ignore_edx_failures_flag[True-False-False]": 0.0318597010045778, + "openedx/api_test.py::test_create_user_ignore_edx_failures_flag[True-False-True]": 0.0317036510023172, + "openedx/api_test.py::test_create_user_ignore_edx_failures_flag[True-True-False]": 0.024907798004278447, + "openedx/api_test.py::test_create_user_ignore_edx_failures_flag[True-True-True]": 0.02977570299844956, + "openedx/api_test.py::test_create_user_ignore_edx_failures_flag_disabled_raises": 0.03876776900142431, + "openedx/api_test.py::test_enroll_api_fail": 0.09010300100635504, + "openedx/api_test.py::test_enroll_in_edx_course_runs[False]": 0.11292780200892594, + "openedx/api_test.py::test_enroll_in_edx_course_runs[True]": 0.1096409039964783, + "openedx/api_test.py::test_enroll_pro_unknown_fail": 0.07379591900098603, + "openedx/api_test.py::test_existing_edx_enrollment[False-audit-audit-True-False]": 0.04184236599394353, + "openedx/api_test.py::test_existing_edx_enrollment[False-verified-verified-True-False]": 0.027726443993742578, + "openedx/api_test.py::test_existing_edx_enrollment[True-audit-audit-False-False]": 0.02880580400233157, + "openedx/api_test.py::test_existing_edx_enrollment[True-audit-audit-True-True]": 0.03171545200893888, + "openedx/api_test.py::test_existing_edx_enrollment[True-audit-verified-False-False]": 0.02504343800683273, + "openedx/api_test.py::test_existing_edx_enrollment[True-audit-verified-True-False]": 0.027536338995560072, + "openedx/api_test.py::test_existing_edx_enrollment[True-verified-audit-True-False]": 0.03138165400014259, + "openedx/api_test.py::test_existing_edx_enrollment[True-verified-verified-True-True]": 0.026095690001966432, + "openedx/api_test.py::test_generate_unique_username[Jos\\xe9-Jos\\xe9_]": 0.018106731004081666, + "openedx/api_test.py::test_generate_unique_username[testuser-testuser_]": 0.01855763899948215, + "openedx/api_test.py::test_generate_unique_username[user123-user123_]": 0.016123579007398803, + "openedx/api_test.py::test_get_edx_api_client": 0.030579714999475982, + "openedx/api_test.py::test_get_edx_api_client_not_synced_ignore_failures": 0.028158891000202857, + "openedx/api_test.py::test_get_edx_api_client_not_synced_raises": 0.024152478996256832, + "openedx/api_test.py::test_get_edx_course_outline": 0.030953389999922365, + "openedx/api_test.py::test_get_edx_course_outline_http_error": 0.04432310100673931, + "openedx/api_test.py::test_get_edx_course_outline_invalid_json": 0.0447435910027707, + "openedx/api_test.py::test_get_edx_course_outline_missing_service_token": 0.019033242999284994, + "openedx/api_test.py::test_get_edx_retirement_service_client": 0.025858198998321313, + "openedx/api_test.py::test_get_valid_edx_api_auth_expired": 0.06763306399807334, + "openedx/api_test.py::test_get_valid_edx_api_auth_unexpired": 0.06137364900496323, + "openedx/api_test.py::test_process_course_run_clone": 0.1438039939966984, + "openedx/api_test.py::test_push_edx_modes_from_run": 0.16421716699551325, + "openedx/api_test.py::test_reconcile_edx_username[False]": 0.03214268499868922, + "openedx/api_test.py::test_reconcile_edx_username[True]": 0.04973447999509517, + "openedx/api_test.py::test_reconcile_edx_username_conflict": 0.04865212699951371, + "openedx/api_test.py::test_repair_all_faulty_openedx_users": 0.10912172999815084, + "openedx/api_test.py::test_repair_faulty_edx_user[False-False]": 0.025033324003743473, + "openedx/api_test.py::test_repair_faulty_edx_user[False-True]": 0.026619235002726782, + "openedx/api_test.py::test_repair_faulty_edx_user[True-False]": 0.028493224002886564, + "openedx/api_test.py::test_repair_faulty_edx_user[True-True]": 0.040855029998056125, + "openedx/api_test.py::test_repair_faulty_openedx_users[Exception]": 0.1237354159966344, + "openedx/api_test.py::test_repair_faulty_openedx_users[MockHttpError]": 0.13322825700015528, + "openedx/api_test.py::test_repair_faulty_openedx_users[None]": 0.1089124999998603, + "openedx/api_test.py::test_retry_failed_edx_enrollments[None]": 0.6333555769961094, + "openedx/api_test.py::test_retry_failed_edx_enrollments[exception_raised0]": 0.5759597520009265, + "openedx/api_test.py::test_retry_failed_edx_enrollments_counts_missing_openedx_user": 0.20252083399827825, + "openedx/api_test.py::test_retry_failed_edx_enrollments_dead_letters_at_max_retries": 0.2248257899991586, + "openedx/api_test.py::test_retry_failed_edx_enrollments_does_not_count_transient_failures[0]": 0.5522195049925358, + "openedx/api_test.py::test_retry_failed_edx_enrollments_does_not_count_transient_failures[1]": 0.16721759199572261, + "openedx/api_test.py::test_retry_failed_edx_enrollments_does_not_count_transient_failures[exception0]": 0.19750195200322196, + "openedx/api_test.py::test_retry_failed_edx_enrollments_excludes_after_max_retries": 0.19351124900276773, + "openedx/api_test.py::test_retry_failed_edx_enrollments_increments_retry_count": 0.19459906599513488, + "openedx/api_test.py::test_retry_failed_enroll_grace_period": 0.36225642699719174, + "openedx/api_test.py::test_retry_users_grace_period": 0.2632426829877659, + "openedx/api_test.py::test_subscribe_to_edx_course_emails": 0.14478043199778767, + "openedx/api_test.py::test_subscribe_to_edx_course_emails_failure[Exception-UnknownEdxApiEmailSettingsException]": 0.17068781999114435, + "openedx/api_test.py::test_subscribe_to_edx_course_emails_failure[MockHttpError-EdxApiEmailSettingsErrorException]": 0.19131409299006918, + "openedx/api_test.py::test_subscribe_to_edx_course_emails_failure[ValueError-UnknownEdxApiEmailSettingsException]": 0.1976188939952408, + "openedx/api_test.py::test_subscribe_to_edx_course_emails_not_synced": 0.17118970600131433, + "openedx/api_test.py::test_sync_enrollments_with_edx_active": 0.3990753580001183, + "openedx/api_test.py::test_sync_enrollments_with_edx_missing": 0.16920693300198764, + "openedx/api_test.py::test_sync_enrollments_with_edx_new": 0.2632346639948082, + "openedx/api_test.py::test_sync_enrollments_with_edx_not_synced": 0.15421765299106482, + "openedx/api_test.py::test_unenroll_edx_course_run": 0.1833076700058882, + "openedx/api_test.py::test_unsubscribe_from_edx_course_emails": 0.1580239739996614, + "openedx/api_test.py::test_unsubscribe_from_edx_course_emails_failure[Exception-UnknownEdxApiEmailSettingsException]": 0.20045513401419157, + "openedx/api_test.py::test_unsubscribe_from_edx_course_emails_failure[MockHttpError-EdxApiEmailSettingsErrorException]": 0.1753493070063996, + "openedx/api_test.py::test_unsubscribe_from_edx_course_emails_failure[ValueError-UnknownEdxApiEmailSettingsException]": 0.18711227900348604, + "openedx/api_test.py::test_unsubscribe_from_edx_course_emails_not_synced": 0.15783414800534956, + "openedx/api_test.py::test_update_edx_user_email": 0.044599244007258676, + "openedx/api_test.py::test_update_edx_user_name_creates_missing_auth": 0.03464834200713085, + "openedx/api_test.py::test_update_edx_user_name_failure[Exception-UserNameUpdateFailedException]": 0.032233343001280446, + "openedx/api_test.py::test_update_edx_user_name_failure[MockHttpError-UserNameUpdateFailedException]": 0.031133267002587672, + "openedx/api_test.py::test_update_edx_user_name_failure[ValueError-UserNameUpdateFailedException]": 0.037803809995239135, + "openedx/api_test.py::test_update_edx_user_name_not_synced": 0.026080682000610977, + "openedx/api_test.py::test_update_edx_user_name_not_synced_raises": 0.027674469994963147, + "openedx/api_test.py::test_update_edx_user_profile_error": 0.026868649991229177, + "openedx/api_test.py::test_update_edx_user_profile_no_openedx_user": 0.02595444299367955, + "openedx/api_test.py::test_update_edx_user_profile_success": 0.027633542005787604, + "openedx/api_test.py::test_update_user_edx_name": 0.041269560999353416, + "openedx/api_test.py::test_validate_edx_username_conflict": 0.02772021500277333, + "openedx/management/tests/reset_edx_enrollment_retries_test.py::test_reset_edx_enrollment_retries": 0.2771308540031896, + "openedx/management/tests/reset_edx_enrollment_retries_test.py::test_reset_edx_enrollment_retries_all_flag": 0.19749892899562838, + "openedx/management/tests/reset_edx_enrollment_retries_test.py::test_reset_edx_enrollment_retries_duplicate_uservalues": 0.21777777799434261, + "openedx/management/tests/reset_edx_enrollment_retries_test.py::test_reset_edx_enrollment_retries_no_matches": 0.019573890007450245, + "openedx/management/tests/reset_edx_enrollment_retries_test.py::test_reset_edx_enrollment_retries_only_exhausted": 0.15899520298989955, + "openedx/management/tests/reset_edx_enrollment_retries_test.py::test_reset_edx_enrollment_retries_requires_scope": 0.15886140200018417, + "openedx/tasks_test.py::test_clone_courserun_does_not_retry_non_transient_errors": 0.1449083299958147, + "openedx/tasks_test.py::test_clone_courserun_logs_exception_after_retry_exhaustion": 0.15298137000354473, + "openedx/tasks_test.py::test_clone_courserun_retries_transient_errors[exc0]": 0.1847213639994152, + "openedx/tasks_test.py::test_clone_courserun_retries_transient_errors[exc1]": 0.16514988600101788, + "openedx/tasks_test.py::test_create_edx_user_from_id": 0.04351769200729905, + "openedx/tasks_test.py::test_get_clone_courserun_retry_countdown": 0.023617005994310603, + "openedx/tasks_test.py::test_repair_faulty_openedx_users[False]": 0.022573543996259104, + "openedx/tasks_test.py::test_repair_faulty_openedx_users[True]": 0.021594183999695815, + "openedx/tasks_test.py::test_update_edx_user_email_async": 0.032694765002815984, + "openedx/tasks_test.py::test_update_edx_user_name_async": 0.03863568800443318, + "openedx/views_test.py::TestEdxCertificateWebhook::test_certificate_status[audit_passed]": 0.20851471700007096, + "openedx/views_test.py::TestEdxCertificateWebhook::test_certificate_status[verified_not_passed]": 0.17862205699202605, + "openedx/views_test.py::TestEdxCertificateWebhook::test_certificate_status[verified_passed_instructor_paced]": 0.23193414800334722, + "openedx/views_test.py::TestEdxCertificateWebhook::test_certificate_status[verified_passed_self_paced]": 0.18292647599446354, + "openedx/views_test.py::TestEdxCertificateWebhook::test_idempotent_certificate_already_exists": 0.19129664600040996, + "openedx/views_test.py::TestEdxCertificateWebhook::test_missing_fields_returns_400[payload0-email]": 0.029177169999456964, + "openedx/views_test.py::TestEdxCertificateWebhook::test_missing_fields_returns_400[payload1-course_id]": 0.028128939993621316, + "openedx/views_test.py::TestEdxCertificateWebhook::test_missing_fields_returns_400[payload2-None]": 0.030007346998900175, + "openedx/views_test.py::TestEdxCertificateWebhook::test_non_admin_returns_403": 0.029832497995812446, + "openedx/views_test.py::TestEdxCertificateWebhook::test_not_found_returns_404[course_run_not_found]": 0.04520808800589293, + "openedx/views_test.py::TestEdxCertificateWebhook::test_not_found_returns_404[user_not_found]": 0.1621094449874363, + "openedx/views_test.py::TestEdxCertificateWebhook::test_unauthenticated_returns_401": 0.02260274199943524, + "openedx/views_test.py::TestEdxEnrollmentWebhook::test_already_enrolled_user": 0.16408978099934757, + "openedx/views_test.py::TestEdxEnrollmentWebhook::test_authentication_and_permission_failures[expired-401]": 0.06320539100852329, + "openedx/views_test.py::TestEdxEnrollmentWebhook::test_authentication_and_permission_failures[invalid-401]": 0.06143416299164528, + "openedx/views_test.py::TestEdxEnrollmentWebhook::test_authentication_and_permission_failures[non_staff-403]": 0.06404278999980306, + "openedx/views_test.py::TestEdxEnrollmentWebhook::test_authentication_and_permission_failures[none-401]": 0.05403085798752727, + "openedx/views_test.py::TestEdxEnrollmentWebhook::test_enrollment_creation_exception": 0.1925605349970283, + "openedx/views_test.py::TestEdxEnrollmentWebhook::test_get_method_not_allowed": 0.040039255007286556, + "openedx/views_test.py::TestEdxEnrollmentWebhook::test_missing_required_field[course_id]": 0.04832321300636977, + "openedx/views_test.py::TestEdxEnrollmentWebhook::test_missing_required_field[email]": 0.036448984988965094, + "openedx/views_test.py::TestEdxEnrollmentWebhook::test_no_edx_api_call": 0.7201643480002531, + "openedx/views_test.py::TestEdxEnrollmentWebhook::test_resource_not_found[course_run_not_found]": 0.06115173600119306, + "openedx/views_test.py::TestEdxEnrollmentWebhook::test_resource_not_found[user_not_found]": 0.1811691030015936, + "openedx/views_test.py::TestEdxEnrollmentWebhook::test_successful_enrollment[instructor]": 0.36892706400249153, + "openedx/views_test.py::TestEdxEnrollmentWebhook::test_successful_enrollment[staff]": 0.3444062530033989, + "openedx/views_test.py::test_openedx_private_auth_complete_view[openedx-private-oauth-complete-no-apisix]": 0.023861634996137582, + "openedx/views_test.py::test_openedx_private_auth_complete_view[openedx-private-oauth-complete]": 0.024624579004012048, + "sheets/tasks_test.py::test_process_google_sheets_requests[False-False]": 0.037199393002083525, + "sheets/tasks_test.py::test_process_google_sheets_requests[True-True]": 0.03517080800520489, + "users/adapters_test.py::test_user_adapter_blank_fields": 0.043244153006526176, + "users/api_test.py::test_fetch_user[email-abc@example.com-None]": 0.030687498008774128, + "users/api_test.py::test_fetch_user[id-100-100]": 0.02525111800059676, + "users/api_test.py::test_fetch_user[id-100-None]": 0.022276179006439634, + "users/api_test.py::test_fetch_user[openedx_user__edx_username-abcdefgh-None]": 0.026963681004417595, + "users/api_test.py::test_fetch_user_case_sens": 0.03757363200566033, + "users/api_test.py::test_fetch_user_fail": 0.016839277996041346, + "users/api_test.py::test_fetch_users[email-values3-None]": 0.04722922899964033, + "users/api_test.py::test_fetch_users[id-values1-None]": 0.04184718200122006, + "users/api_test.py::test_fetch_users[id-values2-db_values2]": 0.04369044699706137, + "users/api_test.py::test_fetch_users[openedx_user__edx_username-values0-None]": 0.052889508006046526, + "users/api_test.py::test_fetch_users_case_sens": 0.04917856499378104, + "users/api_test.py::test_fetch_users_fail[email-existing_values2-missing_values2]": 0.03360052300558891, + "users/api_test.py::test_fetch_users_fail[id-existing_values1-missing_values1]": 0.025758796000445727, + "users/api_test.py::test_fetch_users_fail[openedx_user__edx_username-existing_values0-missing_values0]": 0.02948113900492899, + "users/api_test.py::test_get_user_by_id": 0.031793041998753324, + "users/management/tests/block_users_test.py::TestblockUsers::test_block_user_blocking_with_email": 0.034638469995115884, + "users/management/tests/block_users_test.py::TestblockUsers::test_multiple_success_blocking_user": 0.05185479499778012, + "users/management/tests/block_users_test.py::TestblockUsers::test_user_blocking_if_not_requested": 0.02168330198765034, + "users/management/tests/block_users_test.py::TestblockUsers::test_user_blocking_with_invalid_email": 0.02104542899178341, + "users/management/tests/migrate_usernames_test.py::test_migrate_usernames[False]": 0.1079274509975221, + "users/management/tests/migrate_usernames_test.py::test_migrate_usernames[True]": 0.12905499099724693, + "users/management/tests/promote_user_test.py::test_demote_user[False]": 0.027218557996093296, + "users/management/tests/promote_user_test.py::test_demote_user[True]": 0.030794817001151387, + "users/management/tests/promote_user_test.py::test_promote_user[False]": 0.03346044399950188, + "users/management/tests/promote_user_test.py::test_promote_user[True]": 0.028664652003499214, + "users/management/tests/promote_user_test.py::test_promote_user_bad_args": 0.03228553099324927, + "users/management/tests/retire_users_test.py::test_multiple_success": 0.061537935995147564, + "users/management/tests/retire_users_test.py::test_multiple_success_blocking_user": 0.09218444600264775, + "users/management/tests/retire_users_test.py::test_retire_user_blocking_with_email": 0.046064461006608326, + "users/management/tests/retire_users_test.py::test_retire_user_with_email": 0.032208454998908564, + "users/management/tests/retire_users_test.py::test_single_success": 0.030908987013390288, + "users/management/tests/retire_users_test.py::test_user_blocking_if_not_requested": 0.036611579998862, + "users/management/tests/unblock_users_test.py::TestUnblockUsers::test_multiple_success_unblocking_user": 0.0633074080105871, + "users/management/tests/unblock_users_test.py::TestUnblockUsers::test_user_unblocking_with_email": 0.03896787999110529, + "users/management/tests/unblock_users_test.py::TestUnblockUsers::test_user_unblocking_with_invalid_email": 0.019898037011444103, + "users/management/tests/unblock_users_test.py::TestUnblockUsers::test_user_unblocking_with_no_users": 0.016785480998805724, + "users/models_test.py::test_create_superuser_error[kwargs0]": 0.01509573101066053, + "users/models_test.py::test_create_superuser_error[kwargs1]": 0.014532112989400048, + "users/models_test.py::test_create_superuser_error[kwargs2]": 0.014501329002087004, + "users/models_test.py::test_create_user[None-create_superuser-True-True-True]": 0.028915153998241294, + "users/models_test.py::test_create_user[None-create_user-False-False-True]": 0.040672319002624135, + "users/models_test.py::test_create_user[pass-create_superuser-True-True-True]": 0.030610166002588812, + "users/models_test.py::test_create_user[pass-create_user-False-False-True]": 0.02643886298756115, + "users/models_test.py::test_faulty_user_qset": 0.05939791199489264, + "users/models_test.py::test_legal_address_us_state": 0.029529370993259363, + "users/models_test.py::test_legal_address_validation[country-US-True]": 0.015442144002008718, + "users/models_test.py::test_legal_address_validation[country-United States-False]": 0.015474784006073605, + "users/models_test.py::test_user_is_editor[False-False-False-False]": 0.03018841899756808, + "users/models_test.py::test_user_is_editor[False-False-True-True]": 0.03152511099324329, + "users/models_test.py::test_user_is_editor[False-True-False-True]": 0.029169783992983866, + "users/models_test.py::test_user_is_editor[True-False-False-True]": 0.029875371990783606, + "users/models_test.py::test_user_is_editor[True-True-True-True]": 0.0423363890076871, + "users/models_test.py::test_user_profile_edx_education": 0.035706054004549515, + "users/models_test.py::test_user_profile_edx_properties[False]": 0.039649050995649304, + "users/models_test.py::test_user_profile_edx_properties[True]": 0.03752771200379357, + "users/serializers_test.py::test_create_email_change_request_same_email": 0.030191841004125308, + "users/serializers_test.py::test_create_user_serializer[False]": 0.02897624899924267, + "users/serializers_test.py::test_create_user_serializer[True]": 0.032288212001731154, + "users/serializers_test.py::test_legal_address_validate_state_field[address_no_state_dict-None]": 0.014291444007540122, + "users/serializers_test.py::test_legal_address_validate_state_field[intl_address_dict-None]": 0.015189041005214676, + "users/serializers_test.py::test_legal_address_validate_state_field[invalid_address_dict-Invalid state specified]": 0.014347259006171953, + "users/serializers_test.py::test_legal_address_validate_state_field[valid_address_dict-None]": 0.014667609997559339, + "users/serializers_test.py::test_update_email_change_request_existing_email": 0.03555035899626091, + "users/serializers_test.py::test_update_user_email[False]": 0.054960151996056084, + "users/serializers_test.py::test_update_user_email[True]": 0.039515475000371225, + "users/serializers_test.py::test_update_user_serializer": 0.053610650000337046, + "users/serializers_test.py::test_update_user_serializer_sets_addl_field_flag[0]": 0.03816604799794732, + "users/serializers_test.py::test_update_user_serializer_sets_addl_field_flag[1]": 0.036319564998848364, + "users/serializers_test.py::test_update_user_serializer_sets_addl_field_flag[2]": 0.03867301799618872, + "users/serializers_test.py::test_update_user_serializer_with_profile[False]": 0.030770887999096885, + "users/serializers_test.py::test_update_user_serializer_with_profile[True]": 0.03032801201334223, + "users/serializers_test.py::test_user_create_required_fields_not_post": 0.034367078995273914, + "users/serializers_test.py::test_user_create_required_fields_post": 0.028233665994775947, + "users/serializers_test.py::test_username_validation[ MY-USERNAME -False-None]": 0.03371798100124579, + "users/serializers_test.py::test_username_validation[ My-\\xdcs\\xe9rname 1 -True-My-\\xdcs\\xe9rname 1]": 0.03969075099303154, + "users/serializers_test.py::test_username_validation[my-username-1-True-my-username-1]": 0.08337271799973678, + "users/serializers_test.py::test_username_validation[my>username>1-False-None]": 0.03403133800748037, + "users/serializers_test.py::test_username_validation[my@username-False-None]": 0.028405832999851555, + "users/serializers_test.py::test_username_validation_connection_error": 0.02022414199745981, + "users/serializers_test.py::test_username_validation_exception": 0.03878516799886711, + "users/serializers_test.py::test_username_validation_server_error": 0.022971048994804733, + "users/serializers_test.py::test_validate_legal_address": 0.015375783994386438, + "users/serializers_test.py::test_validate_required_fields[country--This field may not be blank.]": 0.014165365995722823, + "users/serializers_test.py::test_validate_required_fields[country-None-This field may not be null.]": 0.01542809900274733, + "users/signals_test.py::test_user_creation_triggers_hubspot_sync": 1.2703043869914836, + "users/signals_test.py::test_user_update_does_not_trigger_hubspot_sync": 0.028991912004130427, + "users/utils_test.py::test_determine_approx_age": 0.0782239309992292, + "users/utils_test.py::test_format_recipient": 0.027075405996583868, + "users/utils_test.py::test_is_duplicate_username_error[DETAILS: (email)=(ABCDEFG) already exists-False]": 0.012811221997253597, + "users/utils_test.py::test_is_duplicate_username_error[DETAILS: (username)=(ABCDEFG) already exists-True]": 0.015320915008487646, + "users/views_test.py::test_cannot_create_user": 0.033530114000313915, + "users/views_test.py::test_cannot_update_user": 0.031005880002339836, + "users/views_test.py::test_countries_states_view": 0.0253547019965481, + "users/views_test.py::test_create_email_change_request_existing_email": 0.03870546299731359, + "users/views_test.py::test_create_email_change_request_expired_code": 0.049000639002770185, + "users/views_test.py::test_create_email_change_request_invalid_password": 0.035326187004102394, + "users/views_test.py::test_create_email_change_request_same_email": 0.028539320002892055, + "users/views_test.py::test_create_email_change_request_valid_email": 0.0769919410086004, + "users/views_test.py::test_get_user_by_id": 0.0626055149914464, + "users/views_test.py::test_get_user_by_me[False-False]": 0.1245546869977261, + "users/views_test.py::test_get_user_by_me[False-True]": 0.03577567399042891, + "users/views_test.py::test_get_user_by_me[True-False]": 0.2364877170039108, + "users/views_test.py::test_get_user_by_me[True-True]": 0.037929910999082495, + "users/views_test.py::test_get_user_by_me_excludes_inactive_contracts": 0.3040349939983571, + "users/views_test.py::test_get_user_by_me_excludes_unenrolled_contracts": 0.32204520399682224, + "users/views_test.py::test_get_userinfo[False-False-False]": 0.05386073200497776, + "users/views_test.py::test_get_userinfo[False-True-False]": 0.058196625010168646, + "users/views_test.py::test_get_userinfo[False-True-True]": 0.12537572300061584, + "users/views_test.py::test_get_userinfo[True-False-False]": 0.028997921988775488, + "users/views_test.py::test_get_userinfo[True-True-False]": 0.040011885001149494, + "users/views_test.py::test_get_userinfo[True-True-True]": 0.030934785994759295, + "users/views_test.py::test_update_email_change_request_invalid_token": 0.037593629996990785, + "users/views_test.py::test_update_user_edx_failures_feature_flag[False-False]": 0.14360085500084097, + "users/views_test.py::test_update_user_edx_failures_feature_flag[True-False]": 0.125294623998343, + "users/views_test.py::test_update_user_edx_failures_feature_flag[True-True]": 0.15303475900145713, + "users/views_test.py::test_update_user_name_change": 0.11695423100172775, + "users/views_test.py::test_update_user_name_change_edx": 0.1459489940025378, + "users/views_test.py::test_update_user_no_name_change_edx": 0.15775602900248487, + "variants/models_test.py::test_clean_language": 0.1385403349995613 +} diff --git a/cms/api.py b/cms/api.py index 293aedeaf6..332fd3b24b 100644 --- a/cms/api.py +++ b/cms/api.py @@ -23,9 +23,9 @@ from wagtail.rich_text import RichText from cms import models as cms_models +from cms import utils as cms_utils from cms.constants import ( CERTIFICATE_INDEX_SLUG, - FEATURED_ITEMS_CACHE_KEY, INSTRUCTOR_INDEX_SLUG, ) from cms.exceptions import WagtailSpecificPageError @@ -549,7 +549,9 @@ def create_featured_items(): # Store only course IDs to avoid pickling issues and ensure fresh data on retrieval # Use timeout=None so the cache never auto-expires; stale data is better than no data. - redis_cache.set(FEATURED_ITEMS_CACHE_KEY, all_course_ids, timeout=None) + redis_cache.set( + cms_utils.get_featured_items_cache_key(), all_course_ids, timeout=None + ) return list( Course.objects.filter(id__in=all_course_ids) diff --git a/cms/api_test.py b/cms/api_test.py index 8035e2f46c..4668c3895f 100644 --- a/cms/api_test.py +++ b/cms/api_test.py @@ -10,6 +10,7 @@ from wagtail.models import Page from wagtail_factories import PageFactory +from cms import utils as cms_utils from cms.api import ( RESOURCE_PAGE_TITLES, create_default_courseware_page, @@ -21,7 +22,6 @@ get_home_page, get_wagtail_img_src, ) -from cms.constants import FEATURED_ITEMS_CACHE_KEY from cms.exceptions import WagtailSpecificPageError from cms.factories import CoursePageFactory, HomePageFactory, ProgramPageFactory from cms.models import ( @@ -295,9 +295,9 @@ def test_create_featured_items(): # pytest does not clear cache thus if we have a cache value set, it will persist between tests and test runs # thus we need to clear the cache before running the test redis_cache = caches["redis"] - featured_courses = redis_cache.get("CMS_homepage_featured_courses") + featured_courses = redis_cache.get(cms_utils.get_featured_items_cache_key()) if featured_courses is not None: - redis_cache.delete("CMS_homepage_featured_courses") + redis_cache.delete(cms_utils.get_featured_items_cache_key()) now = now_in_utc() future_date = now + timedelta(days=1) @@ -358,7 +358,7 @@ def test_create_featured_items(): ) create_featured_items() - cache_value = redis_cache.get("CMS_homepage_featured_courses") + cache_value = redis_cache.get(cms_utils.get_featured_items_cache_key()) assert len(cache_value) == 4 assert set(cache_value) == { @@ -381,10 +381,10 @@ def test_create_featured_items_no_courses(): """Test create_featured_items with no courses leaves stale cache intact""" redis_cache = caches["redis"] stale_value = [999] - redis_cache.set("CMS_homepage_featured_courses", stale_value) + redis_cache.set(cms_utils.get_featured_items_cache_key(), stale_value) result = create_featured_items() - cache_value = redis_cache.get("CMS_homepage_featured_courses") + cache_value = redis_cache.get(cms_utils.get_featured_items_cache_key()) # Returns empty list but does NOT wipe stale cache data assert result == [] @@ -395,10 +395,10 @@ def test_create_featured_items_no_courses(): def test_create_featured_items_no_courses_no_prior_cache(): """Test create_featured_items with no courses and no prior cache entry""" redis_cache = caches["redis"] - redis_cache.delete("CMS_homepage_featured_courses") + redis_cache.delete(cms_utils.get_featured_items_cache_key()) result = create_featured_items() - cache_value = redis_cache.get("CMS_homepage_featured_courses") + cache_value = redis_cache.get(cms_utils.get_featured_items_cache_key()) assert result == [] assert cache_value is None @@ -409,7 +409,7 @@ def test_create_featured_items_no_enrollable_courses(): """Test create_featured_items with courses but no enrollable runs leaves stale cache intact""" redis_cache = caches["redis"] stale_value = [888] - redis_cache.set("CMS_homepage_featured_courses", stale_value) + redis_cache.set(cms_utils.get_featured_items_cache_key(), stale_value) # Create course with no enrollable runs course = CourseFactory.create(page=None, live=True) @@ -417,7 +417,7 @@ def test_create_featured_items_no_enrollable_courses(): CourseRunFactory.create(course=course, live=False, past_enrollment_end=True) result = create_featured_items() - cache_value = redis_cache.get("CMS_homepage_featured_courses") + cache_value = redis_cache.get(cms_utils.get_featured_items_cache_key()) # Returns empty list but does NOT wipe stale cache data assert result == [] @@ -428,7 +428,7 @@ def test_create_featured_items_no_enrollable_courses(): def test_create_featured_items_many_self_paced_courses(): """Test create_featured_items limits self-paced courses to 2""" redis_cache = caches["redis"] - redis_cache.delete("CMS_homepage_featured_courses") + redis_cache.delete(cms_utils.get_featured_items_cache_key()) now = now_in_utc() further_future_date = now + timedelta(days=2) @@ -459,7 +459,7 @@ def test_create_featured_items_many_self_paced_courses(): def test_create_featured_items_many_regular_courses(): """Test create_featured_items limits regular courses to 20""" redis_cache = caches["redis"] - redis_cache.delete("CMS_homepage_featured_courses") + redis_cache.delete(cms_utils.get_featured_items_cache_key()) now = now_in_utc() future_date = now + timedelta(days=1) @@ -492,7 +492,7 @@ def test_create_featured_items_many_regular_courses(): def test_create_featured_items_course_run_at_exact_time(): """Test create_featured_items with course run starting exactly at now""" redis_cache = caches["redis"] - redis_cache.delete("CMS_homepage_featured_courses") + redis_cache.delete(cms_utils.get_featured_items_cache_key()) now = now_in_utc() further_future_date = now + timedelta(days=2) @@ -511,7 +511,7 @@ def test_create_featured_items_course_run_at_exact_time(): ) result = create_featured_items() - cache_value = redis_cache.get("CMS_homepage_featured_courses") + cache_value = redis_cache.get(cms_utils.get_featured_items_cache_key()) assert len(result) == 1 assert result[0] == exact_time_course @@ -522,7 +522,7 @@ def test_create_featured_items_course_run_at_exact_time(): def test_create_featured_items_mixed_course_types(): """Test create_featured_items with mix of self-paced, future, and started courses""" redis_cache = caches["redis"] - redis_cache.delete("CMS_homepage_featured_courses") + redis_cache.delete(cms_utils.get_featured_items_cache_key()) now = now_in_utc() future_date = now + timedelta(days=1) @@ -579,7 +579,7 @@ def test_create_featured_items_mixed_course_types(): def test_create_featured_items_cache_no_expiry(): """Test that cache is set with timeout=None so it never auto-expires""" redis_cache = caches["redis"] - redis_cache.delete(FEATURED_ITEMS_CACHE_KEY) + redis_cache.delete(cms_utils.get_featured_items_cache_key()) # Create a simple course to have something to cache course = CourseFactory.create(page=None, live=True) @@ -596,7 +596,7 @@ def test_create_featured_items_cache_no_expiry(): create_featured_items() # Verify cache is still set and correct - cache_value = redis_cache.get(FEATURED_ITEMS_CACHE_KEY) + cache_value = redis_cache.get(cms_utils.get_featured_items_cache_key()) assert cache_value is not None assert len(cache_value) == 1 @@ -606,9 +606,9 @@ def test_create_featured_items_cache_no_expiry(): # note that the actual key in redis is prefixed with ":1:" # we use the raw client, not the one provided by redis_django, # because the latter rewrites the return values - ttl = redis_client.ttl(f":1:{FEATURED_ITEMS_CACHE_KEY}") + ttl = redis_client.ttl(f":1:{cms_utils.get_featured_items_cache_key()}") assert ttl == -1, ( - f"Key `{FEATURED_ITEMS_CACHE_KEY}` is missing" + f"Key `{cms_utils.get_featured_items_cache_key()}` is missing" if ttl == -2 - else f"Unexpected ttl value `{ttl}` for `{FEATURED_ITEMS_CACHE_KEY}`" + else f"Unexpected ttl value `{ttl}` for `{cms_utils.get_featured_items_cache_key()}`" ) diff --git a/cms/conftest.py b/cms/conftest.py index 92de27b01e..9a0121bb7e 100644 --- a/cms/conftest.py +++ b/cms/conftest.py @@ -5,6 +5,24 @@ import pytest from cms.api import ensure_home_page_and_site, ensure_product_index +from cms.constants import FEATURED_ITEMS_CACHE_KEY + + +@pytest.fixture(autouse=True) +def isolate_featured_items_cache_key(mocker, worker_id): + """ + Give each xdist worker its own featured items cache key. + + Tests exercise the real CMS_homepage_featured_courses Redis key, and + several of them delete/set/get it directly. Since all workers in a run + share one Redis instance, two such tests landing on different workers + at the same time race on that key. Suffixing by worker_id (e.g. "gw0", + "master" outside xdist) gives each worker its own key. + """ + mocker.patch( + "cms.utils.get_featured_items_cache_key", + return_value=f"{FEATURED_ITEMS_CACHE_KEY}_{worker_id}", + ) @pytest.fixture diff --git a/cms/models.py b/cms/models.py index 699f8144dd..7c3661d98c 100644 --- a/cms/models.py +++ b/cms/models.py @@ -49,6 +49,7 @@ from wagtail.snippets.models import register_snippet from wagtailmetadata.models import MetadataPageMixin +from cms import utils as cms_utils from cms.blocks import ( CourseRunCertificateOverrides, PriceBlock, @@ -811,7 +812,9 @@ def get_cached_featured_products(self): """ now = now_in_utc() redis_cache = caches["redis"] - cached_featured_course_ids = redis_cache.get("CMS_homepage_featured_courses") + cached_featured_course_ids = redis_cache.get( + cms_utils.get_featured_items_cache_key() + ) if cached_featured_course_ids and len(cached_featured_course_ids) > 0: # Load fresh course data from database using cached IDs relevant_run_course_ids = ( diff --git a/cms/models_test.py b/cms/models_test.py index f3c922f012..3114ca890c 100644 --- a/cms/models_test.py +++ b/cms/models_test.py @@ -20,6 +20,7 @@ from mitol.common.utils.datetime import now_in_utc from wagtail.models import Page +from cms import utils as cms_utils from cms.api import create_featured_items from cms.constants import CMS_EDITORS_GROUP_NAME from cms.factories import ( @@ -704,10 +705,10 @@ def test_homepage_featured_products(settings, mocker): furthest_future_date = further_future_date + timedelta(days=1) redis_cache = caches["redis"] # Ensure the key is empty since pytest doesn't clear the cache between tests - featured_courses = redis_cache.get("CMS_homepage_featured_courses") + featured_courses = redis_cache.get(cms_utils.get_featured_items_cache_key()) if featured_courses is not None: - redis_cache.delete("CMS_homepage_featured_courses") - assert redis_cache.get("CMS_homepage_featured_courses") is None + redis_cache.delete(cms_utils.get_featured_items_cache_key()) + assert redis_cache.get(cms_utils.get_featured_items_cache_key()) is None enrollable_future_course = CourseFactory.create(page=None, live=True) enrollable_future_course_page = CoursePageFactory.create( @@ -722,7 +723,7 @@ def test_homepage_featured_products(settings, mocker): end_date=furthest_future_date, ) create_featured_items() - assert len(redis_cache.get("CMS_homepage_featured_courses")) == 1 + assert len(redis_cache.get(cms_utils.get_featured_items_cache_key())) == 1 hf = HomePageFactory.create() assert hf.get_cached_featured_products == [ { @@ -755,7 +756,7 @@ def test_homepage_featured_products(settings, mocker): end_date=furthest_future_date, ) create_featured_items() - assert len(redis_cache.get("CMS_homepage_featured_courses")) == 1 + assert len(redis_cache.get(cms_utils.get_featured_items_cache_key())) == 1 hf = HomePageFactory.create() assert hf.get_cached_featured_products == [ { diff --git a/cms/utils.py b/cms/utils.py index 27d638517c..c4c9251d9e 100644 --- a/cms/utils.py +++ b/cms/utils.py @@ -1,6 +1,8 @@ from django.conf import settings from django.urls import reverse +from cms.constants import FEATURED_ITEMS_CACHE_KEY + def get_page_editing_url(page_id: int) -> str: """ @@ -10,3 +12,8 @@ def get_page_editing_url(page_id: int) -> str: f"{settings.SITE_BASE_URL.rstrip('/')}" f"{reverse('wagtailadmin_pages:edit', args=[page_id])}" ) + + +def get_featured_items_cache_key(): + """Return the cache key used to store the CMS homepage's featured items""" + return FEATURED_ITEMS_CACHE_KEY diff --git a/pyproject.toml b/pyproject.toml index 442b8a741b..6e909ffbfa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -111,6 +111,7 @@ dev = [ "mitol-drf-lint>=2025.3.27", "pytest-urllib3>=0.2.0", "granian[reload]>=2.5.4,<3", + "pytest-split>=0.9,<1", ] [tool.uv] diff --git a/scripts/test/python_checks.sh b/scripts/test/python_checks.sh new file mode 100755 index 0000000000..ae20f89b5d --- /dev/null +++ b/scripts/test/python_checks.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash +set -e +set -o pipefail +status=0 + +echohighlight() { + # shellcheck disable=SC2145 + echo -e "\x1b[32;1m$@\x1b[0m" +} + +function run_test { + # shellcheck disable=SC2145 + echohighlight "[TEST SUITE] $@" + uv run "$@" + local test_status=$? + if [ $test_status -ne 0 ]; then + status=$test_status + fi + return $status +} + +run_test ./scripts/test/detect_missing_migrations.sh +run_test ./scripts/test/no_auto_migrations.sh +run_test ./scripts/test/openapi_spec_check.sh + +exit $status diff --git a/scripts/test/python_tests.sh b/scripts/test/python_tests.sh index 5dd7a9d3dd..05220fcf89 100755 --- a/scripts/test/python_tests.sh +++ b/scripts/test/python_tests.sh @@ -19,9 +19,6 @@ function run_test { return $status } -run_test ./scripts/test/detect_missing_migrations.sh -run_test ./scripts/test/no_auto_migrations.sh -run_test ./scripts/test/openapi_spec_check.sh -run_test pytest -n logical +run_test pytest "$@" exit $status diff --git a/uv.lock b/uv.lock index b8e2bac246..3e606a76ce 100644 --- a/uv.lock +++ b/uv.lock @@ -2120,6 +2120,7 @@ dev = [ { name = "pytest-django" }, { name = "pytest-env" }, { name = "pytest-mock" }, + { name = "pytest-split" }, { name = "pytest-urllib3" }, { name = "pytest-xdist", extra = ["psutil"] }, { name = "responses" }, @@ -2228,6 +2229,7 @@ dev = [ { name = "pytest-django", specifier = ">=4.5.2,<5" }, { name = "pytest-env", specifier = ">=1.0.0,<2" }, { name = "pytest-mock", specifier = ">=3.11.1,<4" }, + { name = "pytest-split", specifier = ">=0.9,<1" }, { name = "pytest-urllib3", specifier = ">=0.2.0" }, { name = "pytest-xdist", extras = ["psutil"], specifier = ">=3.6.1,<4" }, { name = "responses", specifier = ">=0.26.0,<0.27" }, @@ -3094,6 +3096,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5a/cc/06253936f4a7fa2e0f48dfe6d851d9c56df896a9ab09ac019d70b760619c/pytest_mock-3.15.1-py3-none-any.whl", hash = "sha256:0a25e2eb88fe5168d535041d09a4529a188176ae608a6d249ee65abc0949630d", size = 10095, upload-time = "2025-09-16T16:37:25.734Z" }, ] +[[package]] +name = "pytest-split" +version = "0.11.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2f/16/8af4c5f2ceb3640bb1f78dfdf5c184556b10dfe9369feaaad7ff1c13f329/pytest_split-0.11.0.tar.gz", hash = "sha256:8ebdb29cc72cc962e8eb1ec07db1eeb98ab25e215ed8e3216f6b9fc7ce0ec2b5", size = 13421, upload-time = "2026-02-03T09:14:31.469Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ae/a1/d4423657caaa8be9b31e491592b49cebdcfd434d3e74512ce71f6ec39905/pytest_split-0.11.0-py3-none-any.whl", hash = "sha256:899d7c0f5730da91e2daf283860eb73b503259cb416851a65599368849c7f382", size = 11911, upload-time = "2026-02-03T09:14:33.708Z" }, +] + [[package]] name = "pytest-urllib3" version = "0.2.0" From 80c2a512f1e3d2a93c06bb4270ba87976cf369ef Mon Sep 17 00:00:00 2001 From: Nathan Levesque Date: Fri, 24 Jul 2026 13:32:04 -0400 Subject: [PATCH 04/10] Fix OAS comment generation (#3788) --- .github/workflows/openapi-diff.yml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/.github/workflows/openapi-diff.yml b/.github/workflows/openapi-diff.yml index 9390037f61..9c3832f943 100644 --- a/.github/workflows/openapi-diff.yml +++ b/.github/workflows/openapi-diff.yml @@ -38,11 +38,6 @@ jobs: fi done echo "EOF" >> $GITHUB_OUTPUT - - name: Get summary - id: oasdif_summary - run: | - echo summary=$(echo "${{ steps.oasdif_changelog.outputs.changelog }}" | head -1 ) \ - >> $GITHUB_OUTPUT - name: Find existing comment id: find_comment uses: peter-evans/find-comment@b30e6a3c0ed37e7c023ccd3f1db5c6c0b0c23aad # v4 @@ -54,7 +49,6 @@ jobs: - name: Post changes as comment uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 # v5 # Even if no changes, make sure to update old comment if it was found. - if: steps.oasdif_summary.outputs.summary || steps.find_comment.outputs.comment-id with: token: ${{ secrets.GITHUB_TOKEN }} edit-mode: "replace" @@ -65,7 +59,7 @@ jobs: ## OpenAPI Changes
- Show/hide ${{ steps.oasdif_summary.outputs.summary || 'No detectable change.' }} + Show/hide changes ``` ${{ steps.oasdif_changelog.outputs.changelog }} From 7111b602b0298a31379d1bc689b17d8066ed570d Mon Sep 17 00:00:00 2001 From: Muhammad Anas <88967643+Anas12091101@users.noreply.github.com> Date: Tue, 28 Jul 2026 15:38:52 +0500 Subject: [PATCH 05/10] fix: count each B2B program enrollment once in backfill (#3787) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .../backfill_b2b_program_enrollments.py | 14 +++++- .../backfill_b2b_program_enrollments_test.py | 43 +++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/b2b/management/commands/backfill_b2b_program_enrollments.py b/b2b/management/commands/backfill_b2b_program_enrollments.py index b71ac0a03f..4309ec3c32 100644 --- a/b2b/management/commands/backfill_b2b_program_enrollments.py +++ b/b2b/management/commands/backfill_b2b_program_enrollments.py @@ -76,6 +76,13 @@ def handle(self, *args, **kwargs): # noqa: ARG002 already_enrolled = 0 no_program = 0 ambiguous = [] + # A user can be enrolled in several course runs that all map to the same + # program. ProgramEnrollment is unique on (user, program), so those + # collapse to a single enrollment regardless of contract. Track the + # (user, program) pairs already accounted for this run to avoid + # double-counting - this keeps the dry-run count consistent with what a + # --commit run would actually create. + planned = set() self.stdout.write( f"Scanning {enrollments.count()} active B2B course-run enrollment(s)..." @@ -106,11 +113,16 @@ def handle(self, *args, **kwargs): # noqa: ARG002 program = candidate_programs[0] - if ProgramEnrollment.all_objects.filter( + # Skip if the user is already enrolled in this program, or if another + # course run for the same user+program was already handled this run. + # The latter keeps the dry-run count consistent with what a --commit + # run would create (each program enrollment counted exactly once). + if (user.id, program.id) in planned or ProgramEnrollment.all_objects.filter( user=user, program=program ).exists(): already_enrolled += 1 continue + planned.add((user.id, program.id)) self.stdout.write( f"\t{user.email}: enrolling in program {program.readable_id} " diff --git a/b2b/management/tests/backfill_b2b_program_enrollments_test.py b/b2b/management/tests/backfill_b2b_program_enrollments_test.py index 121e14593b..ca230e3a1d 100644 --- a/b2b/management/tests/backfill_b2b_program_enrollments_test.py +++ b/b2b/management/tests/backfill_b2b_program_enrollments_test.py @@ -1,5 +1,7 @@ """Tests for the backfill_b2b_program_enrollments command.""" +import io + import pytest from django.core.management import call_command @@ -131,3 +133,44 @@ def test_backfill_ignores_non_b2b_enrollments(): call_command(COMMAND, "--commit") assert not ProgramEnrollment.objects.filter(user=enrollment.user).exists() + + +def test_backfill_counts_each_program_enrollment_once(): + """Multiple runs mapping to one program collapse to a single enrollment. + + Regression: a user enrolled in several B2B course runs that all belong to + the same program used to inflate the dry-run "Would create" count (one per + run) even though a --commit run only ever creates a single ProgramEnrollment. + The dry-run count must match what --commit actually creates. + """ + + contract = ContractPageFactory.create( + membership_type=CONTRACT_MEMBERSHIP_MANAGED, + ) + program = ProgramFactory.create() + user = None + + for _ in range(3): + course = CourseFactory.create() + program.add_requirement(course) + ContractProgramItem.objects.get_or_create( + contract=contract, program=program, defaults={"sort_order": 0} + ) + run = CourseRunFactory.create(course=course, b2b_contract=contract) + # Reuse the same user across all three runs. + enrollment = CourseRunEnrollmentFactory.create( + run=run, **({"user": user} if user else {}) + ) + user = enrollment.user + + # Dry run: reports a single planned enrollment, not one per course run. + out = io.StringIO() + call_command(COMMAND, stdout=out) + assert "Would create 1 program enrollment(s)." in out.getvalue() + assert not ProgramEnrollment.objects.filter(user=user, program=program).exists() + + # Commit: creates exactly one ProgramEnrollment, matching the dry-run count. + out = io.StringIO() + call_command(COMMAND, "--commit", stdout=out) + assert "Created 1 program enrollment(s)." in out.getvalue() + assert ProgramEnrollment.all_objects.filter(user=user, program=program).count() == 1 From 39798d2cc180e686d6d623957ceadac6840ead59 Mon Sep 17 00:00:00 2001 From: Alex H Date: Tue, 28 Jul 2026 09:37:15 -0400 Subject: [PATCH 06/10] Add redirect for terms, privacy, and honor code (#3776) Signed-off-by: Alex H --- .env.example | 5 +++++ cms/templates/certificate_page.html | 4 ++-- .../templates/mail/partials/_uai_footer.html | 2 +- .../mail/product_order_receipt/body.html | 2 +- frontend/public/src/flow/declarations.js | 3 +++ frontend/public/src/lib/urls.js | 10 +++++++--- mail/api.py | 8 +++++++- mail/api_test.py | 3 +++ mail/templates/email_base.html | 2 +- mail/views.py | 3 +-- main/constants.py | 7 +++++++ main/context_processors.py | 3 +++ main/settings.py | 19 +++++++++++++++++++ main/templates/footer.html | 6 +++--- main/utils.py | 3 +++ main/utils_test.py | 3 +++ 16 files changed, 69 insertions(+), 14 deletions(-) diff --git a/.env.example b/.env.example index 9efae1eff5..9821182802 100644 --- a/.env.example +++ b/.env.example @@ -39,3 +39,8 @@ MITOL_APIGATEWAY_USERINFO_UPDATE=True # Host for Digital Credentials issuer-coordinator service VERIFIABLE_CREDENTIAL_SIGNER_URL=http://dcc.odl.local:4005/instance/test/credentials/issue VERIFIABLE_CREDENTIAL_BEARER_TOKEN='test' + +# MIT Learn URLs (defaults point to production Learn; override to point at a local Learn instance) +MIT_LEARN_TERMS_URL=https://learn.mit.edu/terms +MIT_LEARN_PRIVACY_URL=https://learn.mit.edu/privacy +MIT_LEARN_HONOR_CODE_URL=https://learn.mit.edu/honor_code diff --git a/cms/templates/certificate_page.html b/cms/templates/certificate_page.html index 4f5283bc8a..1d240a7688 100755 --- a/cms/templates/certificate_page.html +++ b/cms/templates/certificate_page.html @@ -119,8 +119,8 @@

Congratulations, {{ learner_name }}!

{% endblock %} diff --git a/frontend/public/src/flow/declarations.js b/frontend/public/src/flow/declarations.js index c27460b90b..2687b8c93a 100644 --- a/frontend/public/src/flow/declarations.js +++ b/frontend/public/src/flow/declarations.js @@ -26,6 +26,9 @@ declare type Settings = { unified_ecommerce_url: ?string, oidc_login_url: ?string, mit_learn_dashboard_url: ?string, + mit_learn_terms_url: ?string, + mit_learn_privacy_url: ?string, + mit_learn_honor_code_url: ?string, api_gateway_enabled: boolean, } declare var SETTINGS: Settings diff --git a/frontend/public/src/lib/urls.js b/frontend/public/src/lib/urls.js index be23c81492..69fb1d62f5 100644 --- a/frontend/public/src/lib/urls.js +++ b/frontend/public/src/lib/urls.js @@ -1,4 +1,5 @@ // @flow +/* global SETTINGS:false */ import { include } from "named-urls" import qs from "query-string" @@ -54,9 +55,12 @@ export const routes = { orderReceipt: "/orders/receipt/:orderId", informationLinks: include("", { - termsOfService: "terms-of-service", - privacyPolicy: "privacy-policy", - honorCode: "honor-code" + termsOfService: + SETTINGS.mit_learn_terms_url || "https://learn.mit.edu/terms", + privacyPolicy: + SETTINGS.mit_learn_privacy_url || "https://learn.mit.edu/privacy", + honorCode: + SETTINGS.mit_learn_honor_code_url || "https://learn.mit.edu/honor_code" }), learnerRecords: "/records/:program/", diff --git a/mail/api.py b/mail/api.py index bfdcd76ff2..96bc942f06 100644 --- a/mail/api.py +++ b/mail/api.py @@ -95,7 +95,13 @@ def can_email_user(user): def get_base_context(): """Returns a dict of context variables that are needed in all emails""" - return {"base_url": settings.SITE_BASE_URL, "site_name": settings.SITE_NAME} + return { + "base_url": settings.SITE_BASE_URL, + "site_name": settings.SITE_NAME, + "mit_learn_terms_url": settings.MIT_LEARN_TERMS_URL, + "mit_learn_privacy_url": settings.MIT_LEARN_PRIVACY_URL, + "mit_learn_honor_code_url": settings.MIT_LEARN_HONOR_CODE_URL, + } def context_for_user(*, user=None, extra_context=None): diff --git a/mail/api_test.py b/mail/api_test.py index bc320f7447..1c0854106f 100644 --- a/mail/api_test.py +++ b/mail/api_test.py @@ -56,6 +56,9 @@ def test_context_for_user(settings, test_user, extra_context): assert context_for_user(user=test_user, extra_context=extra_context) == { "base_url": settings.SITE_BASE_URL, "site_name": settings.SITE_NAME, + "mit_learn_terms_url": settings.MIT_LEARN_TERMS_URL, + "mit_learn_privacy_url": settings.MIT_LEARN_PRIVACY_URL, + "mit_learn_honor_code_url": settings.MIT_LEARN_HONOR_CODE_URL, **(extra_context or {}), **user_ctx, } diff --git a/mail/templates/email_base.html b/mail/templates/email_base.html index d531e6a94a..511f95b46a 100644 --- a/mail/templates/email_base.html +++ b/mail/templates/email_base.html @@ -295,7 +295,7 @@ {% block footer-address %} MIT Open Learning, 600 Technology Square, NE49-2000, Cambridge, MA 02139
- © {% now "Y" %} {{ site_name }}, All Rights Reserved. Terms of Service and Privacy Policy + © {% now "Y" %} {{ site_name }}, All Rights Reserved. Terms of Service and Privacy Policy {% endblock %} diff --git a/mail/views.py b/mail/views.py index 861d82ac2c..9038ca4d9b 100644 --- a/mail/views.py +++ b/mail/views.py @@ -1,6 +1,5 @@ """Mail views""" -from django.conf import settings from django.http import JsonResponse from django.shortcuts import render from django.utils.decorators import method_decorator @@ -42,7 +41,7 @@ def post(self, request): return JsonResponse({"error": "invalid input"}) email_type = form.cleaned_data["email_type"] - context = {"base_url": settings.SITE_BASE_URL, "site_name": settings.SITE_NAME} + context = api.get_base_context() email_extra_context = EMAIL_DEBUG_EXTRA_CONTEXT.get(email_type, {}) context.update(email_extra_context) diff --git a/main/constants.py b/main/constants.py index 9121e5ae95..5d1030f3dd 100644 --- a/main/constants.py +++ b/main/constants.py @@ -59,3 +59,10 @@ ] USER_REGISTRATION_FAILED_MSG = "Unable to register at this time, please try again later" + +# Maps the value of settings.ENVIRONMENT to the hostname for that environment's Learn instance +ENV_TO_LEARN_HOSTNAME_MAP = { + "production": "learn.mit.edu", + "rc": "rc.learn.mit.edu", + "ci": "ci.learn.mit.edu", +} diff --git a/main/context_processors.py b/main/context_processors.py index 075768f205..2dfd724c8b 100644 --- a/main/context_processors.py +++ b/main/context_processors.py @@ -26,4 +26,7 @@ def configuration_context(request): # noqa: ARG001 """ return { "site_name": settings.SITE_NAME, + "mit_learn_terms_url": settings.MIT_LEARN_TERMS_URL, + "mit_learn_privacy_url": settings.MIT_LEARN_PRIVACY_URL, + "mit_learn_honor_code_url": settings.MIT_LEARN_HONOR_CODE_URL, } diff --git a/main/settings.py b/main/settings.py index a562fc0222..cc8c7c3071 100644 --- a/main/settings.py +++ b/main/settings.py @@ -33,6 +33,7 @@ from redbeat import RedBeatScheduler from main.celery_utils import OffsettingSchedule +from main.constants import ENV_TO_LEARN_HOSTNAME_MAP from main.env import get_float from main.sentry import init_sentry from openapi.settings_spectacular import open_spectacular_settings @@ -656,6 +657,24 @@ description="Dashboard URL for UAI enrollment emails", ) +MIT_LEARN_TERMS_URL = get_string( + name="MIT_LEARN_TERMS_URL", + default=f"https://{ENV_TO_LEARN_HOSTNAME_MAP.get(ENVIRONMENT, 'learn.mit.edu')}/terms", + description="Terms of service URL", +) + +MIT_LEARN_PRIVACY_URL = get_string( + name="MIT_LEARN_PRIVACY_URL", + default=f"https://{ENV_TO_LEARN_HOSTNAME_MAP.get(ENVIRONMENT, 'learn.mit.edu')}/privacy", + description="Privacy policy URL", +) + +MIT_LEARN_HONOR_CODE_URL = get_string( + name="MIT_LEARN_HONOR_CODE_URL", + default=f"https://{ENV_TO_LEARN_HOSTNAME_MAP.get(ENVIRONMENT, 'learn.mit.edu')}/honor_code", + description="Honor code URL", +) + # Logging configuration LOG_LEVEL = get_string( name="MITX_ONLINE_LOG_LEVEL", default="INFO", description="The log level default" diff --git a/main/templates/footer.html b/main/templates/footer.html index e45523f5fb..c9819c0acb 100644 --- a/main/templates/footer.html +++ b/main/templates/footer.html @@ -37,7 +37,7 @@ diff --git a/main/utils.py b/main/utils.py index ae15826d56..05ccd91155 100644 --- a/main/utils.py +++ b/main/utils.py @@ -52,6 +52,9 @@ def get_js_settings(request: HttpRequest): # noqa: ARG001 "oidc_login_url": None, "api_gateway_enabled": not settings.MITOL_APIGATEWAY_DISABLE_MIDDLEWARE, "mit_learn_dashboard_url": settings.MIT_LEARN_DASHBOARD_URL, + "mit_learn_terms_url": settings.MIT_LEARN_TERMS_URL, + "mit_learn_privacy_url": settings.MIT_LEARN_PRIVACY_URL, + "mit_learn_honor_code_url": settings.MIT_LEARN_HONOR_CODE_URL, } diff --git a/main/utils_test.py b/main/utils_test.py index e29c50b270..ac498e5cb2 100644 --- a/main/utils_test.py +++ b/main/utils_test.py @@ -56,6 +56,9 @@ def test_get_js_settings(settings, rf): "oidc_login_url": None, "api_gateway_enabled": not settings.MITOL_APIGATEWAY_DISABLE_MIDDLEWARE, "mit_learn_dashboard_url": settings.MIT_LEARN_DASHBOARD_URL, + "mit_learn_terms_url": settings.MIT_LEARN_TERMS_URL, + "mit_learn_privacy_url": settings.MIT_LEARN_PRIVACY_URL, + "mit_learn_honor_code_url": settings.MIT_LEARN_HONOR_CODE_URL, } From dd216066f54a8fd32778de9baf93a6725759b741 Mon Sep 17 00:00:00 2001 From: cp-at-mit Date: Tue, 28 Jul 2026 14:33:01 -0400 Subject: [PATCH 07/10] Use Redis-backed HubSpot rate limiter (#3790) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- hubspot_sync/rate_limiter.py | 129 ++++++++++--------- hubspot_sync/rate_limiter_test.py | 199 ++++++------------------------ 2 files changed, 109 insertions(+), 219 deletions(-) diff --git a/hubspot_sync/rate_limiter.py b/hubspot_sync/rate_limiter.py index 3de95d0e64..13321d87ac 100644 --- a/hubspot_sync/rate_limiter.py +++ b/hubspot_sync/rate_limiter.py @@ -4,86 +4,101 @@ import logging import random -import threading import time -from collections import deque +import uuid from django.conf import settings log = logging.getLogger(__name__) +# Atomic sliding-window rate limiter implemented as a Redis Lua script. +# +# The sorted set stores claimed slots: member = unique ID, score = scheduled +# execution time. Cleanup removes slots whose window has expired. When all +# slots in the current window are claimed, the next slot is pushed past the +# oldest slot's window boundary, distributing load across workers automatically. +_RATE_LIMIT_LUA = """ +local key = KEYS[1] +local now = tonumber(ARGV[1]) +local window = tonumber(ARGV[2]) +local max_req = tonumber(ARGV[3]) +local min_delay = tonumber(ARGV[4]) +local member = ARGV[5] + +redis.call('ZREMRANGEBYSCORE', key, '-inf', now - window) + +local count = redis.call('ZCARD', key) +local target + +if count < max_req then + if count > 0 then + local last = redis.call('ZRANGE', key, -1, -1, 'WITHSCORES') + local last_t = tonumber(last[2]) + target = math.max(now, last_t + min_delay) + else + target = now + end +else + local oldest = redis.call('ZRANGE', key, 0, 0, 'WITHSCORES') + local last = redis.call('ZRANGE', key, -1, -1, 'WITHSCORES') + local oldest_t = tonumber(oldest[2]) + local last_t = tonumber(last[2]) + target = math.max(oldest_t + window, last_t + min_delay, now) +end + +redis.call('ZADD', key, target, member) +redis.call('EXPIRE', key, math.ceil(window) + 1) + +local wait_ms = (target - now) * 1000 +if wait_ms < 0 then wait_ms = 0 end +return tostring(wait_ms) +""" + class HubSpotRateLimiter: """ - A scalable rate limiter that uses a sliding window approach. + Distributed rate limiter using a Redis sorted-set sliding window. + + All Celery workers share the same Redis key, so rate limiting is enforced + globally across processes rather than per-process. """ def __init__(self): self.min_delay_ms = getattr(settings, "HUBSPOT_TASK_DELAY", 60) - - self._lock = threading.Lock() - self._request_times = deque() - self._window_size_seconds = 1.0 self._max_requests_per_second = 19 + self._redis_key = "hubspot:rate_limit" - self._cleanup_counter = 0 - self._cleanup_interval = 50 - - def wait_for_rate_limit(self) -> None: - """ - Wait for an amount of time based on sliding window rate limiting. - """ - current_time = time.time() + def _get_redis(self): + from django_redis import get_redis_connection # noqa: PLC0415 - with self._lock: - self._cleanup_counter += 1 - if self._cleanup_counter >= self._cleanup_interval: - self._cleanup_old_timestamps(current_time) - self._cleanup_counter = 0 + return get_redis_connection("redis") - target_time = self._calculate_next_available_time(current_time) - - self._request_times.append(target_time) - - sleep_time = max(0, target_time - current_time) - - if sleep_time > 0: + def wait_for_rate_limit(self) -> None: + redis_client = self._get_redis() + now = time.time() + member = str(uuid.uuid4()) + + script = redis_client.register_script(_RATE_LIMIT_LUA) + result = script( + keys=[self._redis_key], + args=[ + str(now), + str(self._window_size_seconds), + str(self._max_requests_per_second), + str(self.min_delay_ms / 1000), + member, + ], + ) + wait_ms = float(result) + + if wait_ms > 0: + sleep_time = wait_ms / 1000 jitter = random.uniform(-0.05, 0.05) * sleep_time # noqa: S311 sleep_time = max(0, sleep_time + jitter) - log.debug("Rate limiting: sleeping for %.3f seconds", sleep_time) time.sleep(sleep_time) - def _cleanup_old_timestamps(self, current_time: float) -> None: - """Remove timestamps outside the sliding window.""" - cutoff_time = current_time - self._window_size_seconds - while self._request_times and self._request_times[0] < cutoff_time: - self._request_times.popleft() - - def _calculate_next_available_time(self, current_time: float) -> float: - """ - Calculate when the next request can be made based on the sliding window. - """ - self._cleanup_old_timestamps(current_time) - - if len(self._request_times) < self._max_requests_per_second: - if self._request_times: - last_request_time = self._request_times[-1] - min_next_time = last_request_time + (self.min_delay_ms / 1000) - return max(current_time, min_next_time) - return current_time - - oldest_in_window = self._request_times[0] - next_available = oldest_in_window + self._window_size_seconds - - if self._request_times: - last_request_time = self._request_times[-1] - min_next_time = last_request_time + (self.min_delay_ms / 1000) - next_available = max(next_available, min_next_time) - - return max(next_available, current_time) - rate_limiter = HubSpotRateLimiter() diff --git a/hubspot_sync/rate_limiter_test.py b/hubspot_sync/rate_limiter_test.py index 5191fdef4a..1fcc031421 100644 --- a/hubspot_sync/rate_limiter_test.py +++ b/hubspot_sync/rate_limiter_test.py @@ -2,201 +2,76 @@ from unittest.mock import patch +import pytest from django.test import override_settings from hubspot_sync.rate_limiter import ( + _RATE_LIMIT_LUA, HubSpotRateLimiter, ) class TestHubSpotRateLimiter: - """Test cases for the HubSpotRateLimiter class.""" - def setup_method(self): - """Set up test fixtures.""" self.rate_limiter = HubSpotRateLimiter() + def _mock_redis(self, mocker, wait_ms_bytes=b"0.0"): + mock_redis = mocker.Mock() + mock_script = mocker.Mock(return_value=wait_ms_bytes) + mock_redis.register_script.return_value = mock_script + mocker.patch.object(self.rate_limiter, "_get_redis", return_value=mock_redis) + return mock_redis, mock_script + @override_settings(HUBSPOT_TASK_DELAY=100) def test_init_with_custom_delay(self): - """Test that HubSpotRateLimiter initializes with custom delay setting.""" limiter = HubSpotRateLimiter() assert limiter.min_delay_ms == 100 def test_init_sets_correct_defaults(self): - """Test that HubSpotRateLimiter initializes with correct default values.""" - limiter = HubSpotRateLimiter() - assert limiter.min_delay_ms == 60 + assert self.rate_limiter.min_delay_ms == 60 @patch("hubspot_sync.rate_limiter.time.sleep") - @patch("hubspot_sync.rate_limiter.time.time") - def test_wait_for_rate_limit_first_request(self, mock_time, mock_sleep): - """Test first request with no previous requests.""" - mock_time.return_value = 1000.0 - + def test_no_sleep_when_redis_returns_zero(self, mock_sleep, mocker): + self._mock_redis(mocker, b"0.0") self.rate_limiter.wait_for_rate_limit() - mock_sleep.assert_not_called() @patch("hubspot_sync.rate_limiter.time.sleep") - @patch("hubspot_sync.rate_limiter.time.time") - def test_wait_for_rate_limit_respects_min_delay(self, mock_time, mock_sleep): - """Test that minimum delay is respected between requests.""" - - mock_time.side_effect = [1000.0, 1000.05, 1000.05, 1000.05] - self.rate_limiter.min_delay_ms = 100 - - self.rate_limiter.wait_for_rate_limit() + def test_sleeps_when_redis_returns_wait_time(self, mock_sleep, mocker): + self._mock_redis(mocker, b"500.0") self.rate_limiter.wait_for_rate_limit() - - expected_sleep = 0.05 mock_sleep.assert_called_once() - sleep_time = mock_sleep.call_args[0][0] - assert abs(sleep_time - expected_sleep) < 0.01 - - @patch("hubspot_sync.rate_limiter.time.sleep") - @patch("hubspot_sync.rate_limiter.time.time") - def test_wait_for_rate_limit_no_sleep_when_min_delay_satisfied( - self, mock_time, mock_sleep - ): - """Test no sleep when minimum delay is already satisfied.""" - mock_time.side_effect = [1000.0, 1000.2, 1000.2, 1000.2] - self.rate_limiter.min_delay_ms = 100 + assert abs(mock_sleep.call_args[0][0] - 0.5) < 0.1 + def test_uses_correct_lua_script(self, mocker): + mock_redis, _ = self._mock_redis(mocker) self.rate_limiter.wait_for_rate_limit() - self.rate_limiter.wait_for_rate_limit() - - mock_sleep.assert_not_called() - - @patch("hubspot_sync.rate_limiter.time.sleep") - @patch("hubspot_sync.rate_limiter.time.time") - def test_wait_for_rate_limit_sliding_window_limit(self, mock_time, mock_sleep): - """Test rate limiting when hitting max requests per second.""" - base_time = 1000.0 - mock_time.side_effect = [base_time + i * 0.01 for i in range(25)] - self.rate_limiter.min_delay_ms = 0 - - for _ in range(19): - self.rate_limiter.wait_for_rate_limit() + mock_redis.register_script.assert_called_once_with(_RATE_LIMIT_LUA) + def test_script_receives_correct_keys_and_args(self, mocker): + _, mock_script = self._mock_redis(mocker) self.rate_limiter.wait_for_rate_limit() + call_kwargs = mock_script.call_args[1] + assert call_kwargs["keys"] == ["hubspot:rate_limit"] + args = call_kwargs["args"] + assert args[1] == "1.0" # window_size_seconds + assert args[2] == "19" # max_requests_per_second - mock_sleep.assert_called() - + @pytest.mark.parametrize("wait_ms_bytes", [b"0.0", b"100.0", b"999.9"]) @patch("hubspot_sync.rate_limiter.time.sleep") - @patch("hubspot_sync.rate_limiter.time.time") - def test_wait_for_rate_limit_cleanup_old_timestamps(self, mock_time, mock_sleep): - """Test that old timestamps are cleaned up properly.""" - base_time = 1000.0 - for i in range(10): - self.rate_limiter._request_times.append(base_time + i * 0.1) # noqa: SLF001 - - mock_time.return_value = base_time + 2.0 - + def test_parses_bytes_return_value(self, mock_sleep, wait_ms_bytes, mocker): + self._mock_redis(mocker, wait_ms_bytes) self.rate_limiter.wait_for_rate_limit() - - mock_sleep.assert_not_called() - - @patch("hubspot_sync.rate_limiter.time.time") - def test_wait_for_rate_limit_cleanup_counter(self, mock_time): - """Test that cleanup counter triggers periodic cleanup.""" - mock_time.return_value = 1000.0 - - for _ in range(51): - self.rate_limiter.wait_for_rate_limit() - - @patch("hubspot_sync.rate_limiter.time.sleep") - @patch("hubspot_sync.rate_limiter.time.time") - @patch("hubspot_sync.rate_limiter.random.uniform") - def test_wait_for_rate_limit_jitter(self, mock_random, mock_time, mock_sleep): - """Test that jitter is applied to sleep time.""" - mock_time.side_effect = [1000.0, 1000.05, 1000.05, 1000.05] - mock_random.return_value = 0.01 - self.rate_limiter.min_delay_ms = 100 - - self.rate_limiter.wait_for_rate_limit() - self.rate_limiter.wait_for_rate_limit() - - mock_random.assert_called_once() - mock_sleep.assert_called_once() - sleep_time = mock_sleep.call_args[0][0] - assert sleep_time > 0.05 - - @patch("hubspot_sync.rate_limiter.time.sleep") - @patch("hubspot_sync.rate_limiter.time.time") - @patch("hubspot_sync.rate_limiter.random.uniform") - def test_wait_for_rate_limit_negative_jitter_protection( - self, mock_random, mock_time, mock_sleep - ): - """Test that negative jitter doesn't result in negative sleep time.""" - mock_time.side_effect = [1000.0, 1000.05, 1000.05, 1000.05] - mock_random.return_value = -0.1 - self.rate_limiter.min_delay_ms = 100 - - self.rate_limiter.wait_for_rate_limit() - self.rate_limiter.wait_for_rate_limit() - - mock_sleep.assert_called_once() - sleep_time = mock_sleep.call_args[0][0] - assert sleep_time >= 0 - - @patch("hubspot_sync.rate_limiter.time.time") - def test_wait_for_rate_limit_thread_safety(self, mock_time): - """Test that the rate limiter handles concurrent access safely.""" - mock_time.return_value = 1000.0 - - for _ in range(5): - self.rate_limiter.wait_for_rate_limit() + expected_wait_s = float(wait_ms_bytes) / 1000 + if expected_wait_s > 0: + assert mock_sleep.called + assert abs(mock_sleep.call_args[0][0] - expected_wait_s) < 0.1 + else: + mock_sleep.assert_not_called() @patch("hubspot_sync.rate_limiter.log.debug") - @patch("hubspot_sync.rate_limiter.time.time") - def test_wait_for_rate_limit_logs_sleep_time(self, mock_time, mock_log): - """Test that sleep time is logged when rate limiting occurs.""" - mock_time.side_effect = [1000.0, 1000.05, 1000.05, 1000.05] - self.rate_limiter.min_delay_ms = 100 - + def test_logs_sleep_time_when_rate_limited(self, mock_log, mocker): + self._mock_redis(mocker, b"200.0") self.rate_limiter.wait_for_rate_limit() - self.rate_limiter.wait_for_rate_limit() - mock_log.assert_called_once() - log_message = mock_log.call_args[0][0] - assert "Rate limiting: sleeping for" in log_message - - @patch("hubspot_sync.rate_limiter.time.sleep") - @patch("hubspot_sync.rate_limiter.time.time") - def test_wait_for_rate_limit_window_size_boundary(self, mock_time, mock_sleep): - """Test behavior at window size boundary.""" - base_time = 1000.0 - for i in range(19): - self.rate_limiter._request_times.append(base_time + i * 0.01) # noqa: SLF001 - - mock_time.return_value = base_time + 1.0 - - self.rate_limiter.wait_for_rate_limit() - - mock_sleep.assert_not_called() - - @patch("hubspot_sync.rate_limiter.time.sleep") - @patch("hubspot_sync.rate_limiter.time.time") - def test_wait_for_rate_limit_concurrent_requests_in_window( - self, mock_time, mock_sleep - ): - """Test multiple requests within the same time window.""" - mock_time.return_value = 1000.0 - self.rate_limiter.min_delay_ms = 10 - - for _ in range(3): - self.rate_limiter.wait_for_rate_limit() - - assert mock_sleep.call_count >= 2 - - @patch("hubspot_sync.rate_limiter.time.sleep") - @patch("hubspot_sync.rate_limiter.time.time") - def test_wait_for_rate_limit_zero_min_delay(self, mock_time, mock_sleep): - """Test behavior with zero minimum delay.""" - mock_time.side_effect = [1000.0, 1000.0, 1000.0, 1000.0] - self.rate_limiter.min_delay_ms = 0 - - self.rate_limiter.wait_for_rate_limit() - self.rate_limiter.wait_for_rate_limit() - - mock_sleep.assert_not_called() + assert "Rate limiting: sleeping for" in mock_log.call_args[0][0] From 09cd7332bae57549cf5b6999deb2781c391ee3d9 Mon Sep 17 00:00:00 2001 From: Tobias Macey Date: Tue, 28 Jul 2026 16:18:09 -0400 Subject: [PATCH 08/10] feat(b2b): look up managed orgs by sso_organization_id; expose the org UUID (#3789) Co-authored-by: Claude Opus 4.8 --- b2b/serializers/v0/__init__.py | 2 + b2b/views/v0/manager.py | 36 ++++++++++++++++- b2b/views/v0/manager_test.py | 70 ++++++++++++++++++++++++++++++++++ openapi/specs/v0.yaml | 16 ++++++++ openapi/specs/v1.yaml | 16 ++++++++ openapi/specs/v2.yaml | 16 ++++++++ users/models.py | 6 +-- users/views_test.py | 3 ++ 8 files changed, 160 insertions(+), 5 deletions(-) diff --git a/b2b/serializers/v0/__init__.py b/b2b/serializers/v0/__init__.py index 8627ced777..91772062af 100644 --- a/b2b/serializers/v0/__init__.py +++ b/b2b/serializers/v0/__init__.py @@ -97,6 +97,7 @@ class Meta: "description", "logo", "slug", + "sso_organization_id", "contracts", ] read_only_fields = [ @@ -105,6 +106,7 @@ class Meta: "description", "logo", "slug", + "sso_organization_id", "contracts", ] diff --git a/b2b/views/v0/manager.py b/b2b/views/v0/manager.py index 2077d48d2e..f89dc54952 100644 --- a/b2b/views/v0/manager.py +++ b/b2b/views/v0/manager.py @@ -7,6 +7,7 @@ from django.db.models import Count, Prefetch, Q from django.shortcuts import get_object_or_404 +from drf_spectacular.types import OpenApiTypes from drf_spectacular.utils import ( OpenApiParameter, extend_schema, @@ -147,9 +148,17 @@ class ManagerOrganizationViewSet(viewsets.ReadOnlyModelViewSet): pagination_class = ManagerContractOrgPagination def get_queryset(self): - """Filter to organizations where the user is a manager.""" + """Filter to organizations where the user is a manager. + + An optional ``sso_organization_id`` query param narrows the result to a + single org by its Keycloak organization UUID. This is what + ol-analytics-api uses to ask "does this user manage the org with this + UUID?" (a non-empty response means yes) without dereferencing the + internal PK. An unparseable UUID yields an empty queryset (fail-safe: + reads as "not a manager"), never a 500. + """ - return ( + queryset = ( OrganizationPage.objects.prefetch_related( Prefetch( "contracts", @@ -170,9 +179,32 @@ def get_queryset(self): .distinct() ) + sso_organization_id = self.request.query_params.get("sso_organization_id") + if sso_organization_id: + try: + org_uuid = uuid.UUID(sso_organization_id) + except (ValueError, TypeError): + return queryset.none() + queryset = queryset.filter(sso_organization_id=org_uuid) + + return queryset + @extend_schema( operation_id="b2b_manager_organizations_list", description="List managed organizations", + parameters=[ + OpenApiParameter( + name="sso_organization_id", + type=OpenApiTypes.UUID, + location=OpenApiParameter.QUERY, + required=False, + description=( + "Narrow the result to the single org with this Keycloak " + "organization UUID (sso_organization_id). A non-empty " + "response means the caller manages that org." + ), + ), + ], ) def list(self, request, *args, **kwargs): """List the orgs.""" diff --git a/b2b/views/v0/manager_test.py b/b2b/views/v0/manager_test.py index e9c221907a..9bab5cf4f6 100644 --- a/b2b/views/v0/manager_test.py +++ b/b2b/views/v0/manager_test.py @@ -1,5 +1,7 @@ """Tests for the B2B Manager views""" +import uuid + import pytest import reversion from django.urls import reverse @@ -2011,6 +2013,74 @@ def test_manager_org_list_multiple_managed_orgs(): assert org_2.id in result_ids +def test_manager_org_list_filters_by_sso_organization_id(): + """?sso_organization_id= narrows the managed-org list to that one org -- + the lookup ol-analytics-api uses to ask "does this user manage org UUID X?". + The org's UUID is also serialized in the response. + """ + manager = UserFactory.create() + org_1 = ContractPageFactory.create( + membership_type=CONTRACT_MEMBERSHIP_CODE + ).organization + org_2 = ContractPageFactory.create( + membership_type=CONTRACT_MEMBERSHIP_CODE + ).organization + org_1.sso_organization_id = uuid.uuid4() + org_1.save() + org_2.sso_organization_id = uuid.uuid4() + org_2.save() + UserOrganization.objects.create(user=manager, organization=org_1, is_manager=True) + UserOrganization.objects.create(user=manager, organization=org_2, is_manager=True) + client = APIClient() + client.force_login(manager) + url = reverse("b2b:b2b-manager-organization-list") + + resp = client.get(url, {"sso_organization_id": str(org_1.sso_organization_id)}) + assert resp.status_code == status.HTTP_200_OK + results = resp.json()["results"] + assert {r["id"] for r in results} == {org_1.id} + assert results[0]["sso_organization_id"] == str(org_1.sso_organization_id) + + +def test_manager_org_list_sso_filter_excludes_orgs_the_user_does_not_manage(): + """The filter only narrows the manager-scoped queryset -- filtering by an org + the user does NOT manage returns empty, never that org. + """ + manager = UserFactory.create() + managed = ContractPageFactory.create( + membership_type=CONTRACT_MEMBERSHIP_CODE + ).organization + other = ContractPageFactory.create( + membership_type=CONTRACT_MEMBERSHIP_CODE + ).organization + other.sso_organization_id = uuid.uuid4() + other.save() + UserOrganization.objects.create(user=manager, organization=managed, is_manager=True) + client = APIClient() + client.force_login(manager) + url = reverse("b2b:b2b-manager-organization-list") + + resp = client.get(url, {"sso_organization_id": str(other.sso_organization_id)}) + assert resp.status_code == status.HTTP_200_OK + assert resp.json()["results"] == [] + + +def test_manager_org_list_invalid_sso_uuid_returns_empty_not_500(): + """An unparseable sso_organization_id yields an empty result, not a 500.""" + manager = UserFactory.create() + org = ContractPageFactory.create( + membership_type=CONTRACT_MEMBERSHIP_CODE + ).organization + UserOrganization.objects.create(user=manager, organization=org, is_manager=True) + client = APIClient() + client.force_login(manager) + url = reverse("b2b:b2b-manager-organization-list") + + resp = client.get(url, {"sso_organization_id": "not-a-uuid"}) + assert resp.status_code == status.HTTP_200_OK + assert resp.json()["results"] == [] + + # --------------------------------------------------------------------------- # assign_codes_and_send_emails unit tests # --------------------------------------------------------------------------- diff --git a/openapi/specs/v0.yaml b/openapi/specs/v0.yaml index cb1efb9a4a..930ff7039d 100644 --- a/openapi/specs/v0.yaml +++ b/openapi/specs/v0.yaml @@ -262,6 +262,14 @@ paths: description: Number of results to return per page. schema: type: integer + - in: query + name: sso_organization_id + schema: + type: string + format: uuid + description: Narrow the result to the single org with this Keycloak organization + UUID (sso_organization_id). A non-empty response means the caller manages + that org. tags: - b2b responses: @@ -7465,6 +7473,13 @@ components: readOnly: true description: The name of the page as it will appear in URLs e.g http://domain.com/blog/[my-slug]/ pattern: ^[-\w]+$ + sso_organization_id: + type: string + format: uuid + readOnly: true + nullable: true + title: Organization SSO ID + description: The UUID for the organization in the SSO provider. contracts: type: array items: @@ -7477,6 +7492,7 @@ components: - logo - name - slug + - sso_organization_id Override: type: object description: Serializer for overrides used in certificate pages. diff --git a/openapi/specs/v1.yaml b/openapi/specs/v1.yaml index ea37999834..1c8dc24f80 100644 --- a/openapi/specs/v1.yaml +++ b/openapi/specs/v1.yaml @@ -262,6 +262,14 @@ paths: description: Number of results to return per page. schema: type: integer + - in: query + name: sso_organization_id + schema: + type: string + format: uuid + description: Narrow the result to the single org with this Keycloak organization + UUID (sso_organization_id). A non-empty response means the caller manages + that org. tags: - b2b responses: @@ -7465,6 +7473,13 @@ components: readOnly: true description: The name of the page as it will appear in URLs e.g http://domain.com/blog/[my-slug]/ pattern: ^[-\w]+$ + sso_organization_id: + type: string + format: uuid + readOnly: true + nullable: true + title: Organization SSO ID + description: The UUID for the organization in the SSO provider. contracts: type: array items: @@ -7477,6 +7492,7 @@ components: - logo - name - slug + - sso_organization_id Override: type: object description: Serializer for overrides used in certificate pages. diff --git a/openapi/specs/v2.yaml b/openapi/specs/v2.yaml index 9f7511d1f3..c9f2a2a7ee 100644 --- a/openapi/specs/v2.yaml +++ b/openapi/specs/v2.yaml @@ -262,6 +262,14 @@ paths: description: Number of results to return per page. schema: type: integer + - in: query + name: sso_organization_id + schema: + type: string + format: uuid + description: Narrow the result to the single org with this Keycloak organization + UUID (sso_organization_id). A non-empty response means the caller manages + that org. tags: - b2b responses: @@ -7465,6 +7473,13 @@ components: readOnly: true description: The name of the page as it will appear in URLs e.g http://domain.com/blog/[my-slug]/ pattern: ^[-\w]+$ + sso_organization_id: + type: string + format: uuid + readOnly: true + nullable: true + title: Organization SSO ID + description: The UUID for the organization in the SSO provider. contracts: type: array items: @@ -7477,6 +7492,7 @@ components: - logo - name - slug + - sso_organization_id Override: type: object description: Serializer for overrides used in certificate pages. diff --git a/users/models.py b/users/models.py index 120dbb35b8..e06e61c925 100644 --- a/users/models.py +++ b/users/models.py @@ -361,9 +361,9 @@ def is_editor(self) -> bool: def b2b_organization_sso_ids(self): """Just the UUIDs for the organizations the user is in.""" return list( - self.organizations.filter(sso_organization_id__isnull=False).values_list( - "sso_organization_id", flat=True - ) + self.b2b_organizations.filter( + sso_organization_id__isnull=False + ).values_list("sso_organization_id", flat=True) ) @property diff --git a/users/views_test.py b/users/views_test.py index ec758e6ee5..cf6b96d538 100644 --- a/users/views_test.py +++ b/users/views_test.py @@ -69,6 +69,9 @@ def test_get_user_by_me(mocker, client, user, is_anonymous, has_orgs): "description": contract.organization.description, "logo": None, "slug": contract.organization.slug, + "sso_organization_id": str( + contract.organization.sso_organization_id + ), "contracts": [ { "id": contract.id, From 04a73a5535fa8bee3322d6a03e6fb64430c82310 Mon Sep 17 00:00:00 2001 From: Nathan Levesque Date: Wed, 29 Jul 2026 09:52:05 -0400 Subject: [PATCH 09/10] Fix oasdiff for large diffs (#3796) --- .github/workflows/openapi-diff.yml | 65 +++++++++++++++--------------- 1 file changed, 33 insertions(+), 32 deletions(-) diff --git a/.github/workflows/openapi-diff.yml b/.github/workflows/openapi-diff.yml index 9c3832f943..ec5982c546 100644 --- a/.github/workflows/openapi-diff.yml +++ b/.github/workflows/openapi-diff.yml @@ -18,26 +18,38 @@ jobs: ref: ${{ github.base_ref }} path: base - name: Generate oasdiff changelog - id: oasdif_changelog - run: | # Capture changelog as a multiline output - echo "changelog< $GITHUB_OUTPUT - for spec in base/openapi/specs/*.yaml; do - base_spec="$spec" - head_spec="head/openapi/specs/$(basename "$spec")" - if [ -f "$head_spec" ]; then - echo "## Changes for $(basename "$spec"):" >> $GITHUB_OUTPUT - docker run --rm \ - --workdir ${{ github.workspace }} \ - --volume ${{ github.workspace }}:${{ github.workspace }}:rw \ - -e GITHUB_WORKSPACE=${{ github.workspace }} \ - tufin/oasdiff changelog \ - "$base_spec" \ - "$head_spec" \ - >> $GITHUB_OUTPUT - echo "" >> $GITHUB_OUTPUT - fi - done - echo "EOF" >> $GITHUB_OUTPUT + run: | # Write the full comment body to a file rather than a step output. + # A large changelog interpolated into a JS action's `body:` input becomes a + # huge INPUT_BODY env var, which can blow past the OS argv+envp size limit + # and crash the action with "Argument list too long". Writing straight to a + # file and using `body-path` avoids that entirely. + { + echo "## OpenAPI Changes" + echo "" + echo "
" + echo "Show/hide changes" + echo "" + echo '```' + for spec in base/openapi/specs/*.yaml; do + base_spec="$spec" + head_spec="head/openapi/specs/$(basename "$spec")" + if [ -f "$head_spec" ]; then + echo "## Changes for $(basename "$spec"):" + docker run --rm \ + --workdir ${{ github.workspace }} \ + --volume ${{ github.workspace }}:${{ github.workspace }}:rw \ + -e GITHUB_WORKSPACE=${{ github.workspace }} \ + tufin/oasdiff changelog \ + "$base_spec" \ + "$head_spec" + echo "" + fi + done + echo '```' + echo "" + echo "Unexpected changes? Ensure your branch is up-to-date with \`main\` (consider rebasing)." + echo "
" + } > comment_body.md - name: Find existing comment id: find_comment uses: peter-evans/find-comment@b30e6a3c0ed37e7c023ccd3f1db5c6c0b0c23aad # v4 @@ -55,18 +67,7 @@ jobs: repository: ${{ github.repository }} issue-number: ${{ github.event.pull_request.number }} comment-id: ${{ steps.find_comment.outputs.comment-id }} - body: | - ## OpenAPI Changes - -
- Show/hide changes - - ``` - ${{ steps.oasdif_changelog.outputs.changelog }} - ``` - - Unexpected changes? Ensure your branch is up-to-date with `main` (consider rebasing). -
+ body-path: comment_body.md - name: Check for breaking changes id: oasdif_breaking run: | From e008c960dff72a3218bbb0857a9003de79ac7d52 Mon Sep 17 00:00:00 2001 From: Doof Date: Wed, 29 Jul 2026 15:48:05 +0000 Subject: [PATCH 10/10] Release 1.161.0 --- RELEASE.rst | 13 +++++++++++++ main/settings.py | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/RELEASE.rst b/RELEASE.rst index 2421d1f410..6a46e23417 100644 --- a/RELEASE.rst +++ b/RELEASE.rst @@ -1,6 +1,19 @@ Release Notes ============= +Version 1.161.0 +--------------- + +- Fix oasdiff for large diffs (#3796) +- feat(b2b): look up managed orgs by sso_organization_id; expose the org UUID (#3789) +- Use Redis-backed HubSpot rate limiter (#3790) +- Add redirect for terms, privacy, and honor code (#3776) +- fix: count each B2B program enrollment once in backfill (#3787) +- Fix OAS comment generation (#3788) +- Shard CI pytest run across a 4-way matrix, drop unused coverage instrumentation (#3783) +- Speed up pytest: fast password hasher, drop unused coverage html report (#3782) +- fix: allow 0 as minimum # of value (#3779) + Version 1.160.5 (Released July 22, 2026) --------------- diff --git a/main/settings.py b/main/settings.py index cc8c7c3071..1b11f1e042 100644 --- a/main/settings.py +++ b/main/settings.py @@ -38,7 +38,7 @@ from main.sentry import init_sentry from openapi.settings_spectacular import open_spectacular_settings -VERSION = "1.160.5" +VERSION = "1.161.0" log = logging.getLogger()