1414 OrgContentLibraryGlobData ,
1515 OrgCourseOverviewGlobData ,
1616 PermissionData ,
17+ PlatformCourseOverviewGlobData ,
1718 RoleAssignmentData ,
1819 RoleData ,
1920 ScopeData ,
@@ -267,12 +268,17 @@ def test_scope_data_registration(self):
267268 self .assertIn ("course-v1" , ScopeMeta .org_glob_registry )
268269 self .assertIs (ScopeMeta .org_glob_registry ["course-v1" ], OrgCourseOverviewGlobData )
269270
271+ # Glob registries for platform-level scopes
272+ self .assertIn ("course-v1" , ScopeMeta .platform_glob_registry )
273+ self .assertIs (ScopeMeta .platform_glob_registry ["course-v1" ], PlatformCourseOverviewGlobData )
274+
270275 @data (
271276 ("ccx-v1^ccx-v1:OpenedX+DemoX+DemoCourse+ccx@1" , CCXCourseOverviewData ),
272277 ("course-v1^course-v1:WGU+CS002+2025_T1" , CourseOverviewData ),
273278 ("lib^lib:DemoX:CSPROB" , ContentLibraryData ),
274279 ("lib^lib:DemoX*" , OrgContentLibraryGlobData ),
275280 ("course-v1^course-v1:OpenedX*" , OrgCourseOverviewGlobData ),
281+ ("course-v1^course-v1:*" , PlatformCourseOverviewGlobData ),
276282 ("global^generic_scope" , ScopeData ),
277283 )
278284 @unpack
@@ -294,6 +300,7 @@ def test_dynamic_instantiation_via_namespaced_key(self, namespaced_key, expected
294300 ("lib^lib:DemoX:CSPROB" , ContentLibraryData ),
295301 ("lib^lib:DemoX:*" , OrgContentLibraryGlobData ),
296302 ("course-v1^course-v1:OpenedX+*" , OrgCourseOverviewGlobData ),
303+ ("course-v1^course-v1:*" , PlatformCourseOverviewGlobData ),
297304 ("global^generic" , ScopeData ),
298305 ("unknown^something" , ScopeData ),
299306 )
@@ -318,6 +325,7 @@ def test_get_subclass_by_namespaced_key(self, namespaced_key, expected_class):
318325 ("lib:DemoX:CSPROB" , ContentLibraryData ),
319326 ("lib:DemoX:*" , OrgContentLibraryGlobData ),
320327 ("course-v1:OpenedX+*" , OrgCourseOverviewGlobData ),
328+ ("course-v1:*" , PlatformCourseOverviewGlobData ),
321329 ("lib:edX:Demo" , ContentLibraryData ),
322330 ("global:generic_scope" , ScopeData ),
323331 )
@@ -906,3 +914,79 @@ def test_exists_false_when_org_cannot_be_parsed(self):
906914
907915 self .assertIsNone (scope .org )
908916 self .assertFalse (scope .exists ())
917+
918+
919+ @ddt
920+ class TestPlatformCourseOverviewGlobData (TestCase ):
921+ """Tests for the PlatformCourseOverviewGlobData scope."""
922+
923+ PLATFORM_GLOB_EXTERNAL_KEY = "course-v1:*"
924+ PLATFORM_GLOB_NAMESPACED_KEY = "course-v1^course-v1:*"
925+
926+ def test_build_external_key (self ):
927+ """build_external_key returns the platform-wide course glob pattern."""
928+ self .assertEqual (PlatformCourseOverviewGlobData .build_external_key (), self .PLATFORM_GLOB_EXTERNAL_KEY )
929+
930+ @data (
931+ ("course-v1:*" , True ),
932+ ("course-v1:OpenedX+*" , False ),
933+ ("course-v1:OpenedX*" , False ),
934+ ("course-v1:OpenedX" , False ),
935+ ("course-v1:*:*" , False ),
936+ ("other:*" , False ),
937+ ("lib:*" , False ),
938+ )
939+ @unpack
940+ def test_validate_external_key (self , external_key , expected_valid ):
941+ """Validate platform-level course glob external keys."""
942+ self .assertEqual (PlatformCourseOverviewGlobData .validate_external_key (external_key ), expected_valid )
943+
944+ def test_exists_always_true (self ):
945+ """exists() returns True without checking the database."""
946+ scope = PlatformCourseOverviewGlobData (external_key = self .PLATFORM_GLOB_EXTERNAL_KEY )
947+
948+ self .assertTrue (scope .exists ())
949+
950+ def test_get_object_returns_none (self ):
951+ """Platform glob scopes do not map to a concrete domain object."""
952+ scope = PlatformCourseOverviewGlobData (external_key = self .PLATFORM_GLOB_EXTERNAL_KEY )
953+
954+ self .assertIsNone (scope .get_object ())
955+
956+ def test_namespaced_key (self ):
957+ """namespaced_key includes namespace prefix and external key."""
958+ scope = PlatformCourseOverviewGlobData (external_key = self .PLATFORM_GLOB_EXTERNAL_KEY )
959+
960+ self .assertEqual (scope .namespaced_key , self .PLATFORM_GLOB_NAMESPACED_KEY )
961+
962+ def test_dynamic_instantiation_via_scope_data (self ):
963+ """ScopeData resolves course-v1:* to PlatformCourseOverviewGlobData."""
964+ scope = ScopeData (external_key = self .PLATFORM_GLOB_EXTERNAL_KEY )
965+
966+ self .assertIsInstance (scope , PlatformCourseOverviewGlobData )
967+ self .assertEqual (scope .external_key , self .PLATFORM_GLOB_EXTERNAL_KEY )
968+
969+ def test_get_admin_view_permission (self ):
970+ """View permission matches course team view permission."""
971+ self .assertEqual (
972+ PlatformCourseOverviewGlobData .get_admin_view_permission (), permissions .COURSES_VIEW_COURSE_TEAM
973+ )
974+
975+ def test_get_admin_manage_permission (self ):
976+ """Manage permission matches course team manage permission."""
977+ self .assertEqual (
978+ PlatformCourseOverviewGlobData .get_admin_manage_permission (),
979+ permissions .COURSES_MANAGE_COURSE_TEAM ,
980+ )
981+
982+ def test_is_platform_glob (self ):
983+ """Platform course glob is flagged as a platform-level glob scope."""
984+ self .assertTrue (PlatformCourseOverviewGlobData .IS_PLATFORM_GLOB )
985+ self .assertFalse (PlatformCourseOverviewGlobData .IS_ORG_GLOB )
986+
987+ def test_get_all_platform_glob_namespaces (self ):
988+ """Platform glob namespace is registered in ScopeMeta."""
989+ platform_globs = ScopeMeta .get_all_platform_glob_namespaces ()
990+
991+ self .assertIn ("course-v1" , platform_globs )
992+ self .assertIs (platform_globs ["course-v1" ], PlatformCourseOverviewGlobData )
0 commit comments