33"""
44
55from datetime import datetime , timezone
6+ import logging
67
78import pytest
89from django .db import connection
@@ -227,7 +228,9 @@ def test_create_course_run_for_modulestore_course_with_existing_org():
227228
228229# FIXME: this test passes on MySQL but not SQLite. We need to update the Organizations code to behave consistently.
229230@pytest .mark .skipif (connection .vendor == "sqlite" , reason = "Only passes on MySQL" )
230- def test_create_course_run_for_modulestore_course_with_existing_org_different_capitalization ():
231+ def test_create_course_run_for_modulestore_course_with_existing_org_different_capitalization (
232+ caplog : pytest .LogCaptureFixture ,
233+ ):
231234 """
232235 Test create_course_run_for_modulestore_course_with() when the org already
233236 exists but with different capitalization
@@ -239,6 +242,21 @@ def test_create_course_run_for_modulestore_course_with_existing_org_different_ca
239242 run = api .create_course_run_for_modulestore_course_with (
240243 course_id , display_name = "Introducción a las pruebas" , language_short = "es"
241244 )
245+
246+ # Verify that a warning was logged. We actually get two warnings - one from the API and one from model.clean():
247+ assert caplog .record_tuples == [
248+ (
249+ "openedx_catalog.api_impl" ,
250+ logging .WARN ,
251+ 'The course with ID "course-v1:NewOrg+Test+2026" does not match its Organization.short_name "nEWoRG"' ,
252+ ),
253+ (
254+ "openedx_catalog.models.course_run" ,
255+ logging .WARN ,
256+ 'Course run "course-v1:NewOrg+Test+2026" does not match case of its org short_name "nEWoRG"' ,
257+ ),
258+ ]
259+
242260 assert run .catalog_course .org_id == existing_org_id
243261 assert run .catalog_course .org_code == "nEWoRG" # Uses canonical capitalization
244262 assert run .catalog_course .course_code == "Test"
@@ -290,7 +308,35 @@ def test_create_course_run_for_modulestore_course_with_existing_run():
290308 assert old_run .run == "2025"
291309 # When there was only one run, the catalog course would be given the name of that run:
292310 assert new_run .catalog_course .display_name == "Previous Run (2025)"
293- assert new_run .display_name
294311 assert new_run .run == "2026"
295312 assert new_run .display_name == "New Run (2026)"
296313 assert new_run .course_id == course_id
314+
315+
316+ def test_create_course_run_for_modulestore_course_run_that_exists (caplog : pytest .LogCaptureFixture ) -> None :
317+ """
318+ Test create_course_run_for_modulestore_course_with() when that exact
319+ CourseRun already exists, e.g. due to a race condition.
320+ """
321+ org_code , course_code , run_code = "NewOrg" , "Test" , "2026"
322+ course_id = CourseKey .from_string (f"course-v1:{ org_code } +{ course_code } +{ run_code } " )
323+
324+ existing_run = api .create_course_run_for_modulestore_course_with (course_id , display_name = "Original Name" )
325+ # Call the API again to create the exact same run that we just created:
326+ new_run = api .create_course_run_for_modulestore_course_with (course_id , display_name = "New Name (ignore)" )
327+
328+ # Verify that a warning was logged:
329+ assert caplog .record_tuples == [
330+ (
331+ "openedx_catalog.api_impl" ,
332+ logging .WARN ,
333+ 'Expected to create CourseRun for "course-v1:NewOrg+Test+2026" but it already existed.' ,
334+ ),
335+ ]
336+
337+ existing_run .refresh_from_db () # Let's make sure it hasn't changed
338+ assert existing_run == new_run
339+ assert existing_run .display_name == "Original Name"
340+ assert new_run .display_name == "Original Name"
341+ assert new_run .catalog_course .display_name == "Original Name"
342+ assert new_run .run == run_code
0 commit comments