Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions channels/views_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
User = get_user_model()


@pytest.mark.skip_nplusone_check
def test_list_channels(user_client):
"""Test that all channels are returned"""
ChannelFactory.create_batch(2, published=False) # should be filtered out
Expand Down Expand Up @@ -119,6 +120,7 @@ def test_partial_update_channel_featured_list_only_learning_path(
assert response.status_code == status


@pytest.mark.skip_nplusone_check
@pytest.mark.parametrize("resource_type", LearningResourceType)
def test_create_channel_lists_only_learning_path(admin_client, resource_type):
"""Only learning_paths may be used as one of lists"""
Expand Down Expand Up @@ -449,6 +451,7 @@ def test_channel_configuration_is_not_editable(client, channel):
assert channel.configuration == initial_config


@pytest.mark.skip_nplusone_check
def test_channel_counts_view(client):
"""Test the channel counts view returns counts for resources"""
url = reverse(
Expand Down Expand Up @@ -486,6 +489,7 @@ def test_channel_counts_view(client):
)


@pytest.mark.skip_nplusone_check
def test_channel_counts_view_is_cached_for_anonymous_users(client, settings):
"""Test the channel counts view is cached for anonymous users"""
settings.CACHES["redis"] = {
Expand All @@ -508,6 +512,7 @@ def test_channel_counts_view_is_cached_for_anonymous_users(client, settings):
assert len(response) == channel_count


@pytest.mark.skip_nplusone_check
def test_channel_counts_view_is_cached_for_authenticated_users(client, settings):
"""Test the channel counts view is cached for authenticated users"""
settings.CACHES["redis"] = {
Expand Down
11 changes: 11 additions & 0 deletions fixtures/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import responses
from pytest_mock import PytestMockWarning
from urllib3.exceptions import InsecureRequestWarning
from zeal import zeal_ignore

from channels.factories import ChannelUnitDetailFactory
from learning_resources.constants import LearningResourceRelationTypes, OfferedBy
Expand Down Expand Up @@ -122,3 +123,13 @@ def offeror_featured_lists():
channel = ChannelUnitDetailFactory.create(unit=offeror).channel
channel.featured_list = featured_path
channel.save()


@pytest.fixture(autouse=True)
def check_nplusone(request):
"""Raise nplusone errors"""
if request.node.get_closest_marker("skip_nplusone_check"):
with zeal_ignore():
yield
else:
yield
6 changes: 6 additions & 0 deletions learning_resources/filters_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,7 @@ def test_learning_resource_filter_delivery(mock_courses, client):
)


@pytest.mark.skip_nplusone_check
def test_content_file_filter_run_id(mock_content_files, client):
"""Test that the run_id filter works for contentfiles"""

Expand All @@ -553,6 +554,7 @@ def test_content_file_filter_run_id(mock_content_files, client):
)


@pytest.mark.skip_nplusone_check
def test_content_file_filter_resource_id(mock_content_files, client):
"""Test that the resource_id filter works for contentfiles"""

Expand All @@ -575,6 +577,7 @@ def test_content_file_filter_resource_id(mock_content_files, client):
)


@pytest.mark.skip_nplusone_check
def test_content_file_filter_edx_module_id(mock_content_files, client):
"""Test that the resource_id filter works for contentfiles"""
assert mock_content_files[0].edx_module_id is None
Expand All @@ -597,6 +600,7 @@ def test_content_file_filter_edx_module_id(mock_content_files, client):
]


@pytest.mark.skip_nplusone_check
def test_content_file_filter_platform(mock_content_files, client):
"""Test that the platform filter works"""

Expand All @@ -614,6 +618,7 @@ def test_content_file_filter_platform(mock_content_files, client):
)


@pytest.mark.skip_nplusone_check
def test_content_file_filter_offered_by(mock_content_files, client):
"""Test that the offered_by filter works for contentfiles"""

Expand All @@ -631,6 +636,7 @@ def test_content_file_filter_offered_by(mock_content_files, client):
)


@pytest.mark.skip_nplusone_check
def test_learning_resource_filter_content_feature_type(client):
"""Test that the resource_content_tag filter works"""

Expand Down
8 changes: 8 additions & 0 deletions learning_resources/views_learningpath_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ def mock_opensearch(mocker):
)


@pytest.mark.skip_nplusone_check
@pytest.mark.parametrize("is_public", [True, False])
@pytest.mark.parametrize("is_editor", [True, False])
@pytest.mark.parametrize("has_image", [True, False])
Expand Down Expand Up @@ -135,6 +136,7 @@ def test_learning_path_endpoint_create( # pylint: disable=too-many-arguments #
assert resp.data.get("description") == resp.data.get("description")


@pytest.mark.skip_nplusone_check
@pytest.mark.parametrize("is_public", [True, False])
@pytest.mark.parametrize("is_editor", [True, False])
@pytest.mark.parametrize("update_topics", [True, False])
Expand Down Expand Up @@ -175,6 +177,7 @@ def test_learning_path_endpoint_patch(client, update_topics, is_public, is_edito
)


@pytest.mark.skip_nplusone_check
@pytest.mark.parametrize("is_editor", [True, False])
def test_learning_path_items_endpoint_create_item(client, user, is_editor):
"""Test lr_learningpathitems_api endpoint for creating a LearningPath item"""
Expand Down Expand Up @@ -232,6 +235,7 @@ def test_learning_path_items_endpoint_create_item_bad_data(client, user):
}


@pytest.mark.skip_nplusone_check
@pytest.mark.parametrize(
("is_editor", "position"),
[[True, 0], [True, 2], [False, 1]], # noqa: PT007
Expand Down Expand Up @@ -301,6 +305,7 @@ def test_learning_path_items_endpoint_update_items_wrong_list(client, user):
assert resp.status_code == 404


@pytest.mark.skip_nplusone_check
@pytest.mark.parametrize("num_items", [2, 3])
@pytest.mark.parametrize("is_editor", [True, False])
def test_learning_path_items_endpoint_delete_items(client, user, is_editor, num_items):
Expand Down Expand Up @@ -334,6 +339,7 @@ def test_learning_path_items_endpoint_delete_items(client, user, is_editor, num_
assert item.position == (old_position - 1 if is_editor else old_position)


@pytest.mark.skip_nplusone_check
@pytest.mark.parametrize("is_editor", [True, False])
def test_learning_path_endpoint_delete(client, user, is_editor):
"""Test learningpath endpoint for deleting a LearningPath"""
Expand Down Expand Up @@ -414,6 +420,7 @@ def test_get_resource_learning_paths(user_client, user, is_editor):
assert response_data == expected


@pytest.mark.skip_nplusone_check
def test_set_learning_path_relationships(client, staff_user):
"""Test the learning_paths endpoint for setting multiple userlist relationships"""
course = factories.CourseFactory.create()
Expand All @@ -440,6 +447,7 @@ def test_set_learning_path_relationships(client, staff_user):
).exists()


@pytest.mark.skip_nplusone_check
def test_adding_to_learning_path_not_effect_existing_membership(client, staff_user):
"""
Given L1 (existing parent), L2 (new parent), and R (resource),
Expand Down
12 changes: 12 additions & 0 deletions learning_resources/views_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ def test_get_course_detail_endpoint(client, url):
)


@pytest.mark.skip_nplusone_check
@pytest.mark.parametrize(
"url",
[
Expand All @@ -139,6 +140,7 @@ def test_get_course_content_files_endpoint(client, url):
assert resp.data.get("results")[idx]["id"] == content_file.id


@pytest.mark.skip_nplusone_check
@pytest.mark.parametrize(
("reverse_url", "expected_url"),
[
Expand Down Expand Up @@ -264,6 +266,7 @@ def test_no_excess_offeror_queries(client, django_assert_num_queries, offeror_co
assert result["channel_url"] is not None


@pytest.mark.skip_nplusone_check
@pytest.mark.parametrize(
"user_role",
[
Expand Down Expand Up @@ -311,6 +314,7 @@ def test_list_content_files_list_endpoint(client, user_role, django_user_model):
assert result.get("content") is None


@pytest.mark.skip_nplusone_check
def test_list_content_files_list_endpoint_with_no_runs(client):
"""Test ContentFile list endpoint returns results even without associated runs"""
course = CourseFactory.create()
Expand All @@ -330,6 +334,7 @@ def test_list_content_files_list_endpoint_with_no_runs(client):
assert result["id"] in content_file_ids


@pytest.mark.skip_nplusone_check
def test_list_content_files_list_filtered(client):
"""Test ContentFile list endpoint"""
course_1 = CourseFactory.create()
Expand Down Expand Up @@ -505,6 +510,7 @@ def test_get_podcast_episode_detail_endpoint(client, url):
)


@pytest.mark.skip_nplusone_check
@pytest.mark.parametrize(
"url", ["lr:v1:learning_resource_items_api-list", "lr:v1:podcast_items_api-list"]
)
Expand Down Expand Up @@ -836,6 +842,7 @@ def test_offerors_detail_endpoint(client):
assert resp.data == LearningResourceOfferorDetailSerializer(instance=offeror).data


@pytest.mark.skip_nplusone_check
@pytest.mark.parametrize(
("url", "params"),
[
Expand Down Expand Up @@ -879,6 +886,7 @@ def test_get_video_playlist_detail_endpoint(client, url):
)


@pytest.mark.skip_nplusone_check
@pytest.mark.parametrize(
"url",
["lr:v1:learning_resource_items_api-list", "lr:v1:video_playlist_items_api-list"],
Expand Down Expand Up @@ -923,6 +931,7 @@ def test_get_video_playlist_items_endpoint(client, url):
)


@pytest.mark.skip_nplusone_check
@pytest.mark.parametrize(
("url", "params"),
[
Expand All @@ -948,6 +957,7 @@ def test_list_video_endpoint(client, url, params):
)


@pytest.mark.skip_nplusone_check
@pytest.mark.parametrize(
("url", "params"),
[
Expand Down Expand Up @@ -1228,6 +1238,7 @@ def test_similar_resources_endpoint_ignores_opensearch_published(mocker, client)
assert len(response_ids) == 4


@pytest.mark.skip_nplusone_check
def test_vector_similar_resources_endpoint_does_not_return_self(mocker, client):
"""Test vector based similar resources endpoint does not return initial id"""
from learning_resources.models import LearningResource
Expand Down Expand Up @@ -1298,6 +1309,7 @@ def test_vector_similar_resources_endpoint_only_returns_published(mocker, client
assert len(response_ids) == 1


@pytest.mark.skip_nplusone_check
def test_learning_resources_display_info_list_view(mocker, client):
"""Test learning_resources_display_info_list_view returns expected results"""
from learning_resources.models import LearningResource
Expand Down
4 changes: 4 additions & 0 deletions learning_resources/views_userlist_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ def test_user_list_endpoint_membership_get(client, user, is_authenticated):
assert resp.status_code == 403


@pytest.mark.skip_nplusone_check
@pytest.mark.parametrize("is_author", [True, False])
def test_user_list_items_endpoint_create_item(client, user, is_author):
"""Test userlistitems endpoint for creating a UserListItem"""
Expand Down Expand Up @@ -177,6 +178,7 @@ def test_user_list_items_endpoint_create_item_bad_data(client, user):
}


@pytest.mark.skip_nplusone_check
@pytest.mark.parametrize(
("is_author", "position"),
[[True, 0], [True, 2], [False, 1]], # noqa: PT007
Expand Down Expand Up @@ -309,6 +311,7 @@ def test_get_resource_user_lists(client, user, is_author, is_unlisted):
assert items_json == []


@pytest.mark.skip_nplusone_check
def test_set_userlist_relationships(client, user):
"""Test the userlists endpoint for setting multiple userlist relationships"""
course = factories.CourseFactory.create()
Expand Down Expand Up @@ -376,6 +379,7 @@ def assign_userlists(course, userlists):
)


@pytest.mark.skip_nplusone_check
def test_adding_to_userlist_not_effect_existing_membership(client, user):
"""
Given L1 (existing parent), L2 (new parent), and R (resource),
Expand Down
12 changes: 12 additions & 0 deletions main/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,17 @@
"django_scim.middleware.SCIMAuthCheckMiddleware",
)

ZEAL_ENABLE = get_bool("ZEAL_ENABLE", False) # noqa: FBT003

# enable the zeal nplusone profiler only in debug mode or under pytest
if ZEAL_ENABLE or ENVIRONMENT == "pytest":
INSTALLED_APPS += ("zeal",)
# this should be the first middleware so we catch any issues in our own middleware
MIDDLEWARE += ("zeal.middleware.zeal_middleware",)

ZEAL_RAISE = False
ZEAL_ALLOWLIST = []

# CORS
CORS_ALLOWED_ORIGINS = get_list_of_str("CORS_ALLOWED_ORIGINS", [])
CORS_ALLOWED_ORIGIN_REGEXES = get_list_of_str("CORS_ALLOWED_ORIGIN_REGEXES", [])
Expand Down Expand Up @@ -483,6 +494,7 @@
"opensearch": {"level": OS_LOG_LEVEL},
"nplusone": {"handlers": ["console"], "level": "ERROR"},
"boto3": {"handlers": ["console"], "level": "ERROR"},
"zeal": {"handlers": ["console"], "level": "ERROR"},
},
"root": {"handlers": ["console"], "level": LOG_LEVEL},
}
Expand Down
1 change: 1 addition & 0 deletions news_events/filters_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
ITEM_API_URL = "/api/v0/news_events/"


@pytest.mark.skip_nplusone_check
@pytest.mark.parametrize(
"multifilter", ["feed_type={}&feed_type={}", "feed_type={},{}"]
)
Expand Down
2 changes: 2 additions & 0 deletions news_events/views_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from news_events.factories import FeedEventDetailFactory


@pytest.mark.skip_nplusone_check
def test_feed_source_viewset_list(client):
"""Est that the feed sources list viewset returns data in expected format"""
sources = sorted(factories.FeedSourceFactory.create_batch(5), key=lambda x: x.id)
Expand All @@ -25,6 +26,7 @@ def test_feed_source_viewset_list(client):
)


@pytest.mark.skip_nplusone_check
@pytest.mark.parametrize("feed_type", FeedType.names())
def test_feed_source_viewset_list_filtered(client, feed_type):
"""Test that the sources list viewset returns data filtered by feed type"""
Expand Down
14 changes: 13 additions & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions profiles/views_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ def test_patch_profile_by_user(client, logged_in_profile):
assert logged_in_profile.location == location_json


@pytest.mark.skip_nplusone_check
def test_patch_topic_interests(client, logged_in_profile):
"""Test that patching Profile.topic_interests works correctly"""
topics = LearningResourceTopicFactory.create_batch(3)
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ wrapt = "^1.14.1"
youtube-transcript-api = "^1.0.0"
pypdfium2 = "^4.30.0"
pyarrow = "^21.0.0"
django-zeal = "^2.0.4"



Expand Down
Loading
Loading