Skip to content

Commit 3e7b441

Browse files
committed
test: add more test cases
1 parent 03cfa1e commit 3e7b441

3 files changed

Lines changed: 286 additions & 9 deletions

File tree

openedx_authz/settings/common.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ def plugin_settings(settings):
5050
if not hasattr(settings, "OPENEDX_AUTHZ_COURSE_OVERVIEW_MODEL"):
5151
settings.OPENEDX_AUTHZ_COURSE_OVERVIEW_MODEL = "course_overviews.CourseOverview"
5252

53+
# Set default Organization model for swappable dependency
54+
if not hasattr(settings, "OPENEDX_AUTHZ_ORGANIZATION_MODEL"):
55+
settings.OPENEDX_AUTHZ_ORGANIZATION_MODEL = "organizations.Organization"
56+
5357
# Set default CASBIN_LOG_LEVEL if not already set.
5458
# This setting defines the logging level for the Casbin enforcer.
5559
if not hasattr(settings, "CASBIN_LOG_LEVEL"):

openedx_authz/tests/api/test_data.py

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from unittest.mock import Mock, patch
44

55
from ddt import data, ddt, unpack
6-
from django.test import TestCase, override_settings
6+
from django.test import TestCase
77
from opaque_keys.edx.locator import LibraryLocatorV2
88

99
from openedx_authz.api.data import (
@@ -717,7 +717,6 @@ def test_exists_returns_false_when_library_does_not_exist(self, mock_content_lib
717717

718718

719719
@ddt
720-
@override_settings(OPENEDX_AUTHZ_CONTENT_LIBRARY_MODEL="content_libraries.ContentLibrary")
721720
class TestOrgContentLibraryGlobData(TestCase):
722721
"""Tests for the OrgContentLibraryGlobData scope."""
723722

@@ -737,6 +736,7 @@ class TestOrgContentLibraryGlobData(TestCase):
737736
("lib:Org+WithPlus:*", False),
738737
("lib:(Org):*", False),
739738
("lib:Org", False),
739+
("lib:Org*", False),
740740
("other:DemoX:*", False),
741741
("lib:DemoX:*:*", False),
742742
)
@@ -752,14 +752,17 @@ def test_validate_external_key(self, external_key, expected_valid):
752752
("lib:Org:With:Colon:*", "Org:With:Colon"),
753753
("lib:DemoX", None),
754754
("lib:DemoX:+*", None),
755+
("lib:DemoX*", None),
756+
("lib:DemoX:**", None),
757+
("lib:DemoX:suffix", None),
755758
)
756759
@unpack
757760
def test_get_org(self, external_key, expected_org):
758761
"""Test organization extraction from library glob pattern."""
759762
self.assertEqual(OrgContentLibraryGlobData.get_org(external_key), expected_org)
760763

761-
def test_exists_true_when_org_has_libraries_in_db(self):
762-
"""exists() returns True when at least one library with the org exists in the DB."""
764+
def test_exists_true_when_org_exists(self):
765+
"""exists() returns True when the org exists."""
763766
org_name = "DemoX"
764767
organization = Organization.objects.create(short_name=org_name)
765768
ContentLibrary.objects.create(org=organization, slug="testlib", title="Test Library")
@@ -768,8 +771,8 @@ def test_exists_true_when_org_has_libraries_in_db(self):
768771

769772
self.assertTrue(result)
770773

771-
def test_exists_false_when_org_does_not_exist_in_db(self):
772-
"""exists() returns False when the org does not exist in the DB."""
774+
def test_exists_false_when_org_does_not_exist(self):
775+
"""exists() returns False when the org does not exist."""
773776
org_name = "DemoX"
774777

775778
result = OrgContentLibraryGlobData(external_key=f"lib:{org_name}:*").exists()
@@ -785,7 +788,6 @@ def test_exists_false_when_org_cannot_be_parsed(self):
785788

786789

787790
@ddt
788-
@override_settings(OPENEDX_AUTHZ_COURSE_OVERVIEW_MODEL="course_overviews.CourseOverview")
789791
class TestOrgCourseOverviewGlobData(TestCase):
790792
"""Tests for the OrgCourseOverviewGlobData scope."""
791793

@@ -806,6 +808,7 @@ class TestOrgCourseOverviewGlobData(TestCase):
806808
("course-v1:(Org)+*", False),
807809
("course-v1:Org:With:Plus+*", False),
808810
("course-v1:OpenedX", False),
811+
("course-v1:OpenedX*", False),
809812
("other:OpenedX+*", False),
810813
("course-v1:OpenedX**", False),
811814
)
@@ -819,14 +822,18 @@ def test_validate_external_key(self, external_key, expected_valid):
819822
("course-v1:My-Org_1+*", "My-Org_1"),
820823
("course-v1:Org.with.dots+*", "Org.with.dots"),
821824
("course-v1:Org:With:Plus+*", "Org:With:Plus"),
825+
("course-v1:OpenedX", None),
826+
("course-v1:OpenedX*", None),
827+
("course-v1:OpenedX+**", None),
828+
("course-v1:OpenedX+suffix", None),
822829
)
823830
@unpack
824831
def test_get_org(self, external_key, expected_org):
825832
"""Test organization extraction from course glob pattern."""
826833
self.assertEqual(OrgCourseOverviewGlobData.get_org(external_key), expected_org)
827834

828-
def test_exists_true_when_org_has_courses(self):
829-
"""exists() returns True when at least one course with the org exists."""
835+
def test_exists_true_when_org_exists(self):
836+
"""exists() returns True when the org exists."""
830837
org_name = "OpenedX"
831838
Organization.objects.create(short_name=org_name)
832839
CourseOverview.objects.create(org=org_name, display_name="Test Course")
Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
.annotation_safe_list.yml:
2+
- annotation_data: This model has no PII
3+
annotation_token: '.. no_pii:'
4+
extra:
5+
full_comment: '{''.. no_pii:'': ''This model has no PII''}'
6+
object_id: sessions.Session
7+
filename: .annotation_safe_list.yml
8+
found_by: safelist
9+
line_number: 0
10+
report_group_id: 1
11+
- annotation_data: This model has no PII
12+
annotation_token: '.. no_pii:'
13+
extra:
14+
full_comment: '{''.. no_pii:'': ''This model has no PII''}'
15+
object_id: contenttypes.ContentType
16+
filename: .annotation_safe_list.yml
17+
found_by: safelist
18+
line_number: 0
19+
report_group_id: 2
20+
- annotation_data: This model has no PII
21+
annotation_token: '.. no_pii:'
22+
extra:
23+
full_comment: '{''.. no_pii:'': ''This model has no PII''}'
24+
object_id: auth.Permission
25+
filename: .annotation_safe_list.yml
26+
found_by: safelist
27+
line_number: 0
28+
report_group_id: 3
29+
- annotation_data: This model stores authorization policy rules and contains no PII
30+
annotation_token: '.. no_pii:'
31+
extra:
32+
full_comment: '{''.. no_pii:'': ''This model stores authorization policy rules
33+
and contains no PII''}'
34+
object_id: casbin_adapter.CasbinRule
35+
filename: .annotation_safe_list.yml
36+
found_by: safelist
37+
line_number: 0
38+
report_group_id: 4
39+
- annotation_data: This model has no PII
40+
annotation_token: '.. no_pii:'
41+
extra:
42+
full_comment: '{''.. no_pii:'': ''This model has no PII''}'
43+
object_id: admin.LogEntry
44+
filename: .annotation_safe_list.yml
45+
found_by: safelist
46+
line_number: 0
47+
report_group_id: 5
48+
- annotation_data: This model minimally contains a username, password, and email
49+
annotation_token: .. pii
50+
extra:
51+
full_comment: '{''.. pii'': ''This model minimally contains a username, password,
52+
and email'', ''.. pii_types'': ''username, email_address, password'', ''.. pii_retirement'':
53+
''consumer_api''}'
54+
object_id: auth.User
55+
filename: .annotation_safe_list.yml
56+
found_by: safelist
57+
line_number: 0
58+
report_group_id: 6
59+
- annotation_data: username, email_address, password
60+
annotation_token: .. pii_types
61+
extra:
62+
full_comment: '{''.. pii'': ''This model minimally contains a username, password,
63+
and email'', ''.. pii_types'': ''username, email_address, password'', ''.. pii_retirement'':
64+
''consumer_api''}'
65+
object_id: auth.User
66+
filename: .annotation_safe_list.yml
67+
found_by: safelist
68+
line_number: 0
69+
report_group_id: 6
70+
- annotation_data: consumer_api
71+
annotation_token: .. pii_retirement
72+
extra:
73+
full_comment: '{''.. pii'': ''This model minimally contains a username, password,
74+
and email'', ''.. pii_types'': ''username, email_address, password'', ''.. pii_retirement'':
75+
''consumer_api''}'
76+
object_id: auth.User
77+
filename: .annotation_safe_list.yml
78+
found_by: safelist
79+
line_number: 0
80+
report_group_id: 6
81+
- annotation_data: This model has no PII
82+
annotation_token: '.. no_pii:'
83+
extra:
84+
full_comment: '{''.. no_pii:'': ''This model has no PII''}'
85+
object_id: auth.Group
86+
filename: .annotation_safe_list.yml
87+
found_by: safelist
88+
line_number: 0
89+
report_group_id: 7
90+
/home/bryanttv/edunext/tutor/main-rbac/extra-deps/openedx-authz/openedx_authz/models/core.py:
91+
- annotation_data: ''
92+
annotation_token: '.. no_pii:'
93+
extra:
94+
full_comment: "Model representing a scope in the authorization system.\n\n \
95+
\ .. no_pii:\n\n This model can be extended to represent different types\
96+
\ of scopes,\n such as courses or content libraries.\n\n Subclasses should\
97+
\ define a NAMESPACE class attribute (e.g., 'lib' for content libraries)\n \
98+
\ and implement get_or_create_for_external_key() classmethod."
99+
object_id: openedx_authz.Scope
100+
filename: /home/bryanttv/edunext/tutor/main-rbac/extra-deps/openedx-authz/openedx_authz/models/core.py
101+
found_by: django
102+
line_number: 105
103+
report_group_id: 12
104+
- annotation_data: ''
105+
annotation_token: '.. no_pii:'
106+
extra:
107+
full_comment: "Extended model for Casbin rules to store additional metadata.\n\
108+
\n .. no_pii:\n\n This model extends the CasbinRule model provided by\
109+
\ the casbin_adapter\n package to include additional fields for storing metadata\
110+
\ about each rule."
111+
object_id: openedx_authz.ExtendedCasbinRule
112+
filename: /home/bryanttv/edunext/tutor/main-rbac/extra-deps/openedx-authz/openedx_authz/models/core.py
113+
found_by: django
114+
line_number: 141
115+
report_group_id: 13
116+
- annotation_data: ''
117+
annotation_token: '.. no_pii:'
118+
extra:
119+
full_comment: "Model representing a subject in the authorization system.\n\n \
120+
\ .. no_pii:\n\n This model can be extended to represent different types\
121+
\ of subjects,\n such as users or groups.\n\n Subclasses should define\
122+
\ a NAMESPACE class attribute (e.g., 'user' for users)\n and implement get_or_create_for_external_key()\
123+
\ classmethod."
124+
object_id: openedx_authz.Subject
125+
filename: /home/bryanttv/edunext/tutor/main-rbac/extra-deps/openedx-authz/openedx_authz/models/core.py
126+
found_by: django
127+
line_number: 123
128+
report_group_id: 14
129+
/home/bryanttv/edunext/tutor/main-rbac/extra-deps/openedx-authz/openedx_authz/models/engine.py:
130+
- annotation_data: ''
131+
annotation_token: '.. no_pii:'
132+
extra:
133+
full_comment: "Model to control policy cache invalidation.\n\n This model can\
134+
\ be used to trigger cache invalidation for authorization policies\n by changing\
135+
\ the version. Whenever this model is updated, the authorization\n engine\
136+
\ should invalidate its cached policies.\n\n .. no_pii:"
137+
object_id: openedx_authz.PolicyCacheControl
138+
filename: /home/bryanttv/edunext/tutor/main-rbac/extra-deps/openedx-authz/openedx_authz/models/engine.py
139+
found_by: django
140+
line_number: 8
141+
report_group_id: 20
142+
/home/bryanttv/edunext/tutor/main-rbac/extra-deps/openedx-authz/openedx_authz/models/scopes.py:
143+
- annotation_data: ''
144+
annotation_token: '.. no_pii:'
145+
extra:
146+
full_comment: "Scope representing a content library in the authorization system.\n\
147+
\n .. no_pii:"
148+
object_id: openedx_authz.ContentLibraryScope
149+
filename: /home/bryanttv/edunext/tutor/main-rbac/extra-deps/openedx-authz/openedx_authz/models/scopes.py
150+
found_by: django
151+
line_number: 76
152+
report_group_id: 8
153+
- annotation_data: ''
154+
annotation_token: '.. no_pii:'
155+
extra:
156+
full_comment: "Model representing a scope in the authorization system.\n\n \
157+
\ .. no_pii:\n\n This model can be extended to represent different types\
158+
\ of scopes,\n such as courses or content libraries.\n\n Subclasses should\
159+
\ define a NAMESPACE class attribute (e.g., 'lib' for content libraries)\n \
160+
\ and implement get_or_create_for_external_key() classmethod."
161+
object_id: openedx_authz.ContentLibraryScope
162+
filename: /home/bryanttv/edunext/tutor/main-rbac/extra-deps/openedx-authz/openedx_authz/models/core.py
163+
found_by: django
164+
line_number: 105
165+
report_group_id: 9
166+
- annotation_data: ''
167+
annotation_token: '.. no_pii:'
168+
extra:
169+
full_comment: "Scope representing a course in the authorization system.\n\n \
170+
\ .. no_pii:"
171+
object_id: openedx_authz.CourseScope
172+
filename: /home/bryanttv/edunext/tutor/main-rbac/extra-deps/openedx-authz/openedx_authz/models/scopes.py
173+
found_by: django
174+
line_number: 120
175+
report_group_id: 10
176+
- annotation_data: ''
177+
annotation_token: '.. no_pii:'
178+
extra:
179+
full_comment: "Model representing a scope in the authorization system.\n\n \
180+
\ .. no_pii:\n\n This model can be extended to represent different types\
181+
\ of scopes,\n such as courses or content libraries.\n\n Subclasses should\
182+
\ define a NAMESPACE class attribute (e.g., 'lib' for content libraries)\n \
183+
\ and implement get_or_create_for_external_key() classmethod."
184+
object_id: openedx_authz.CourseScope
185+
filename: /home/bryanttv/edunext/tutor/main-rbac/extra-deps/openedx-authz/openedx_authz/models/core.py
186+
found_by: django
187+
line_number: 105
188+
report_group_id: 11
189+
/home/bryanttv/edunext/tutor/main-rbac/extra-deps/openedx-authz/openedx_authz/models/subjects.py:
190+
- annotation_data: ''
191+
annotation_token: '.. no_pii:'
192+
extra:
193+
full_comment: "Subject representing a user in the authorization system.\n\n \
194+
\ .. no_pii:"
195+
object_id: openedx_authz.UserSubject
196+
filename: /home/bryanttv/edunext/tutor/main-rbac/extra-deps/openedx-authz/openedx_authz/models/subjects.py
197+
found_by: django
198+
line_number: 16
199+
report_group_id: 21
200+
- annotation_data: ''
201+
annotation_token: '.. no_pii:'
202+
extra:
203+
full_comment: "Model representing a subject in the authorization system.\n\n \
204+
\ .. no_pii:\n\n This model can be extended to represent different types\
205+
\ of subjects,\n such as users or groups.\n\n Subclasses should define\
206+
\ a NAMESPACE class attribute (e.g., 'user' for users)\n and implement get_or_create_for_external_key()\
207+
\ classmethod."
208+
object_id: openedx_authz.UserSubject
209+
filename: /home/bryanttv/edunext/tutor/main-rbac/extra-deps/openedx-authz/openedx_authz/models/core.py
210+
found_by: django
211+
line_number: 123
212+
report_group_id: 22
213+
/home/bryanttv/edunext/tutor/main-rbac/extra-deps/openedx-authz/openedx_authz/tests/stubs/models.py:
214+
- annotation_data: ''
215+
annotation_token: '.. no_pii:'
216+
extra:
217+
full_comment: "Stub model representing an organization for testing purposes.\n\
218+
\n .. no_pii:"
219+
object_id: stubs.Organization
220+
filename: /home/bryanttv/edunext/tutor/main-rbac/extra-deps/openedx-authz/openedx_authz/tests/stubs/models.py
221+
found_by: django
222+
line_number: 15
223+
report_group_id: 15
224+
- annotation_data: ''
225+
annotation_token: '.. no_pii:'
226+
extra:
227+
full_comment: "Stub model representing legacy content library permissions for\
228+
\ testing purposes.\n\n .. no_pii:"
229+
object_id: stubs.ContentLibraryPermission
230+
filename: /home/bryanttv/edunext/tutor/main-rbac/extra-deps/openedx-authz/openedx_authz/tests/stubs/models.py
231+
found_by: django
232+
line_number: 69
233+
report_group_id: 16
234+
- annotation_data: ''
235+
annotation_token: '.. no_pii:'
236+
extra:
237+
full_comment: "Maps users to org, courses, and roles. Used by student.roles.CourseRole\
238+
\ and OrgRole.\n To establish a user as having a specific role over all courses\
239+
\ in the org, create an entry\n without a course_id.\n\n .. no_pii:"
240+
object_id: stubs.CourseAccessRole
241+
filename: /home/bryanttv/edunext/tutor/main-rbac/extra-deps/openedx-authz/openedx_authz/tests/stubs/models.py
242+
found_by: django
243+
line_number: 165
244+
report_group_id: 17
245+
- annotation_data: ''
246+
annotation_token: '.. no_pii:'
247+
extra:
248+
full_comment: "Stub model representing a course overview for testing purposes.\n\
249+
\n This model contains basic course metadata such as an ID, display name,\
250+
\ and organization.\n It is used to link CourseScope instances to actual\
251+
\ courses in the system.\n\n .. no_pii:"
252+
object_id: stubs.CourseOverview
253+
filename: /home/bryanttv/edunext/tutor/main-rbac/extra-deps/openedx-authz/openedx_authz/tests/stubs/models.py
254+
found_by: django
255+
line_number: 94
256+
report_group_id: 18
257+
- annotation_data: ''
258+
annotation_token: '.. no_pii:'
259+
extra:
260+
full_comment: "Stub model representing a content library for testing purposes.\n\
261+
\n .. no_pii:"
262+
object_id: stubs.ContentLibrary
263+
filename: /home/bryanttv/edunext/tutor/main-rbac/extra-deps/openedx-authz/openedx_authz/tests/stubs/models.py
264+
found_by: django
265+
line_number: 50
266+
report_group_id: 19

0 commit comments

Comments
 (0)