11"""Unit Tests for openedx_authz migrations."""
22
3- from unittest .mock import patch
3+ from unittest .mock import MagicMock , patch
44
55from django .contrib .auth import get_user_model
66from django .contrib .auth .models import Group
@@ -278,11 +278,12 @@ def setUp(self):
278278 class MockPermission :
279279 """Mock class to simulate CourseAccessRole entries for testing the rollback migration."""
280280
281- def __init__ (self , user , role , course_id , id_in ):
281+ def __init__ (self , user , role , course_id , id_in , * , org = "" ):
282282 self .user = user
283283 self .role = role
284284 self .course_id = course_id
285285 self .id = id_in
286+ self .org = org
286287
287288 class MockUser :
288289 """Mock class to simulate User objects for testing the rollback migration."""
@@ -296,7 +297,7 @@ class MockQuerySet:
296297 def __init__ (self , permissions ):
297298 self .permissions = permissions
298299
299- def filter (self , ** kwargs ):
300+ def filter (self , * args , * *kwargs ):
300301 return self
301302
302303 def select_related (self , * args , ** kwargs ):
@@ -1169,6 +1170,41 @@ def test_migrate_legacy_course_roles_to_authz_user_not_added(
11691170 self .assertEqual (len (successes ), 0 )
11701171 self .assertEqual (errors [0 ].user .username , "testuser" )
11711172
1173+ @patch ("openedx_authz.engine.utils.LEGACY_COURSE_ROLE_EQUIVALENCES" , {"instructor" : "instructor-role" })
1174+ def test_migrate_legacy_course_roles_to_authz_instance_wide_role_is_error (self ):
1175+ """Instance-wide roles (no course_id and no org) are logged as errors and skipped.
1176+
1177+ Expected result:
1178+ A CourseAccessRole with both course_id and org blank is logged as an error and
1179+ returned in permissions_with_errors, but not migrated.
1180+ """
1181+ instance_wide_permission = MagicMock ()
1182+ instance_wide_permission .user .username = "instance_user"
1183+ instance_wide_permission .role = "instructor"
1184+ instance_wide_permission .course_id = ""
1185+ instance_wide_permission .org = ""
1186+
1187+ mock_qs = MagicMock ()
1188+ mock_qs .filter .return_value = mock_qs
1189+ mock_qs .select_related .return_value = mock_qs
1190+ mock_qs .all .return_value = [instance_wide_permission ]
1191+
1192+ mock_model = MagicMock ()
1193+ mock_model .objects .filter .return_value = mock_qs
1194+
1195+ with self .assertLogs ("openedx_authz.engine.utils" , level = "ERROR" ) as log :
1196+ errors , successes = migrate_legacy_course_roles_to_authz (
1197+ mock_model ,
1198+ course_id_list = ["course-v1:test" ],
1199+ org_id = None ,
1200+ delete_after_migration = False ,
1201+ )
1202+
1203+ self .assertEqual (len (errors ), 1 )
1204+ self .assertEqual (len (successes ), 0 )
1205+ self .assertEqual (errors [0 ].user .username , "instance_user" )
1206+ self .assertTrue (any ("instance_user" in msg for msg in log .output ))
1207+
11721208 @patch ("openedx_authz.api.data.CourseOverview" , CourseOverview )
11731209 def test_migrate_authz_to_legacy_course_roles_user_not_added (self ):
11741210 permissions_with_errors , permissions_with_no_errors = migrate_legacy_course_roles_to_authz (
@@ -1279,7 +1315,7 @@ def test_rollback_org_level_scope_creates_org_only_course_access_role(self):
12791315 user = User .objects .create_user (
12801316 username = f"org_user_{ OBJECT_PREFIX } " , email = f"org_user_{ OBJECT_PREFIX } @example.com"
12811317 )
1282- CourseAccessRole .objects .create (user = user , org = org_short_name_new , course_id = "" , role = "instructor" )
1318+ CourseAccessRole .objects .create (user = user , org = org_short_name_new , role = "instructor" )
12831319
12841320 migrate_legacy_course_roles_to_authz (
12851321 CourseAccessRole , course_id_list = None , org_id = org_short_name_new , delete_after_migration = True
0 commit comments