Skip to content

Prevent test courses from being overwritten #2262

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
May 28, 2025
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
23 changes: 18 additions & 5 deletions learning_resources/etl/loaders.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from django.contrib.auth import get_user_model
from django.db import transaction
from django.db.models import Q

from learning_resources.constants import (
LearningResourceDelivery,
Expand Down Expand Up @@ -70,7 +71,11 @@ def update_index(learning_resource, newly_created):
learning resource (LearningResource): a learning resource
newly_created (bool): whether the learning resource has just been created
"""
if not newly_created and not learning_resource.published:
if (
not newly_created
and not learning_resource.published
and not learning_resource.test_mode
):
resource_unpublished_actions(learning_resource)
elif learning_resource.published:
resource_upserted_actions(learning_resource, percolate=False)
Expand Down Expand Up @@ -293,6 +298,7 @@ def load_run(
LearningResourceRun: the created/updated resource run
"""
run_id = run_data.pop("run_id")

image_data = run_data.pop("image", None)
status = run_data.pop("status", None)
instructors_data = run_data.pop("instructors", [])
Expand All @@ -305,6 +311,9 @@ def load_run(
run_data["prices"] = []
resource_prices = []

if learning_resource.test_mode:
run_data["published"] = True

with transaction.atomic():
(
learning_resource_run,
Expand All @@ -314,7 +323,6 @@ def load_run(
run_id=run_id,
defaults={**run_data},
)

load_instructors(learning_resource_run, instructors_data)
load_prices(learning_resource_run, resource_prices)
load_image(learning_resource_run, image_data)
Expand Down Expand Up @@ -498,7 +506,8 @@ def load_course(
# The course ETL should be the ultimate source of truth for
# courses and their runs.
for run in learning_resource.runs.exclude(
run_id__in=run_ids_to_update_or_create
Q(run_id__in=run_ids_to_update_or_create)
| Q(learning_resource__test_mode=True)
).filter(published=True):
run.published = False
run.save()
Expand Down Expand Up @@ -548,7 +557,10 @@ def load_courses(
if courses and config.prune:
for learning_resource in LearningResource.objects.filter(
etl_source=etl_source, resource_type=LearningResourceType.course.name
).exclude(id__in=[learning_resource.id for learning_resource in courses]):
).exclude(
Q(id__in=[learning_resource.id for learning_resource in courses])
| Q(test_mode=True)
):
learning_resource.published = False
learning_resource.save()
resource_unpublished_actions(learning_resource)
Expand Down Expand Up @@ -613,7 +625,8 @@ def load_program(
if config.prune:
# mark runs no longer included here as unpublished
for run in learning_resource.runs.exclude(
run_id__in=run_ids_to_update_or_create
Q(run_id__in=run_ids_to_update_or_create)
| Q(learning_resource__test_mode=True)
).filter(published=True):
run.published = False
run.save()
Expand Down
60 changes: 60 additions & 0 deletions learning_resources/etl/loaders_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
PlatformType,
RunStatus,
)
from learning_resources.etl import loaders
from learning_resources.etl.constants import (
CourseLoaderConfig,
ETLSource,
Expand Down Expand Up @@ -143,6 +144,28 @@ def mock_upsert_tasks(mocker):
)


@pytest.mark.parametrize("published", [False, True])
@pytest.mark.parametrize("test_mode", [False, True])
@pytest.mark.parametrize("newly_created", [False, True])
def test_update_index_test_mode_behavior(
mocker,
published,
test_mode,
newly_created,
):
"""Test update_index does not remove test_mode content files from index"""
resource_unpublished_actions = mocker.patch(
"learning_resources.etl.loaders.resource_unpublished_actions"
)
lr = LearningResourceFactory.create(published=published, test_mode=test_mode)

loaders.update_index(lr, newly_created)
if test_mode:
resource_unpublished_actions.assert_not_called()
elif not published and not newly_created:
resource_unpublished_actions.assert_called_once()


@pytest.fixture
def learning_resource_offeror():
"""Return a LearningResourceOfferer"""
Expand Down Expand Up @@ -276,6 +299,43 @@ def test_load_program( # noqa: PLR0913
mock_upsert_tasks.upsert_learning_resource_immutable_signature.assert_not_called()


@pytest.mark.django_db
def test_load_run_sets_test_resource_run_to_published(mocker):
"""
Test that load_run sets the test_mode run to published
"""

mock_qs = mocker.patch.object(
loaders.LearningResourceRun.objects, "filter", autospec=True
)
mock_qs.return_value.exists.return_value = True

test_mode_learning_resource = LearningResourceFactory.create(test_mode=True)

test_mode_run_id = "test_run_id"
test_mode_run_data = {"run_id": test_mode_run_id, "published": False}

LearningResourceRunFactory.create(
learning_resource=test_mode_learning_resource,
run_id=test_mode_run_id,
published=False,
)

result = loaders.load_run(test_mode_learning_resource, test_mode_run_data)
assert result.published
regular_learning_resource = LearningResourceFactory.create(test_mode=False)
regular_run_id = "test_run_id"
regular_run_data = {"run_id": regular_run_id, "published": False}
LearningResourceRunFactory.create(
learning_resource=regular_learning_resource,
run_id=regular_run_id,
published=False,
)

result = loaders.load_run(regular_learning_resource, regular_run_data)
assert not result.published


def test_load_program_bad_platform(mocker):
"""A bad platform should log an exception and not create the program"""
mock_log = mocker.patch("learning_resources.etl.loaders.log.exception")
Expand Down
5 changes: 4 additions & 1 deletion learning_resources/management/commands/mixins.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from learning_resources.models import LearningResource
from learning_resources.models import LearningResource, LearningResourceRun


class TestResourceConfigurationMixin:
Expand All @@ -13,6 +13,9 @@ def configure_test_resources(self, options):
)
if options.get("test_ids"):
test_ids = options["test_ids"].split(",")
LearningResourceRun.objects.filter(
learning_resource__id__in=test_ids
).update(published=True)
LearningResource.objects.filter(id__in=test_ids).update(
test_mode=True, published=False
)
Expand Down
Loading