Skip to content

Commit 14adcfd

Browse files
get_topics_from_page unit tests (#2978)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 3286acc commit 14adcfd

File tree

1 file changed

+236
-0
lines changed

1 file changed

+236
-0
lines changed

courses/serializers/utils_test.py

Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
"""Unit tests for courses.serializers.utils module"""
2+
3+
from unittest.mock import Mock
4+
5+
import pytest
6+
7+
from cms.factories import CoursePageFactory
8+
from courses.factories import CoursesTopicFactory
9+
from courses.serializers.utils import get_topics_from_page
10+
11+
pytestmark = [pytest.mark.django_db]
12+
13+
14+
class TestGetTopicsFromPage:
15+
"""Test cases for get_topics_from_page function"""
16+
17+
def test_get_topics_from_page_with_none_page_instance(self):
18+
"""Test that None page instance returns empty list"""
19+
result = get_topics_from_page(None)
20+
assert result == []
21+
22+
def test_get_topics_from_page_with_empty_topics(self):
23+
"""Test page instance with no topics returns empty list"""
24+
mock_page = Mock()
25+
mock_page.topics.all.return_value = []
26+
27+
result = get_topics_from_page(mock_page)
28+
assert result == []
29+
30+
def test_get_topics_from_page_with_direct_topics_only(self):
31+
"""Test page with direct topics but no parent topics"""
32+
# Create topics without parent topics
33+
topic1 = CoursesTopicFactory.create(name="Mathematics")
34+
topic2 = CoursesTopicFactory.create(name="Computer Science")
35+
topic3 = CoursesTopicFactory.create(name="Physics")
36+
37+
mock_page = Mock()
38+
mock_page.topics.all.return_value = [topic1, topic2, topic3]
39+
40+
result = get_topics_from_page(mock_page)
41+
42+
# Should be sorted alphabetically
43+
expected = [
44+
{"name": "Computer Science"},
45+
{"name": "Mathematics"},
46+
{"name": "Physics"},
47+
]
48+
assert result == expected
49+
50+
def test_get_topics_from_page_with_parent_topics(self):
51+
"""Test page with topics that have parent topics"""
52+
# Create parent topics
53+
parent_topic1 = CoursesTopicFactory.create(name="Science", parent=None)
54+
parent_topic2 = CoursesTopicFactory.create(name="Technology", parent=None)
55+
56+
# Create child topics
57+
child_topic1 = CoursesTopicFactory.create(name="Physics", parent=parent_topic1)
58+
child_topic2 = CoursesTopicFactory.create(
59+
name="Chemistry", parent=parent_topic1
60+
)
61+
child_topic3 = CoursesTopicFactory.create(
62+
name="Programming", parent=parent_topic2
63+
)
64+
65+
mock_page = Mock()
66+
mock_page.topics.all.return_value = [child_topic1, child_topic2, child_topic3]
67+
68+
result = get_topics_from_page(mock_page)
69+
70+
# Should include both direct topics (sorted) and parent topics
71+
expected_direct_topics = [
72+
{"name": "Chemistry"},
73+
{"name": "Physics"},
74+
{"name": "Programming"},
75+
]
76+
77+
# Parent topics should be appended after direct topics
78+
# Note: The order of parent topics may vary since they're added in a loop
79+
assert len(result) == 5 # 3 direct + 2 parent topics
80+
81+
# Check that all direct topics are present at the beginning (sorted)
82+
for i, expected_topic in enumerate(expected_direct_topics):
83+
assert result[i] == expected_topic
84+
85+
# Check that parent topics are included
86+
parent_topic_names = {result[i]["name"] for i in range(3, 5)}
87+
assert parent_topic_names == {"Science", "Technology"}
88+
89+
def test_get_topics_from_page_with_mixed_parent_and_orphan_topics(self):
90+
"""Test page with mix of topics that have parents and topics without parents"""
91+
# Create parent topic
92+
parent_topic = CoursesTopicFactory.create(name="Science", parent=None)
93+
94+
# Create child topic
95+
child_topic = CoursesTopicFactory.create(name="Physics", parent=parent_topic)
96+
97+
# Create orphan topics (no parent)
98+
orphan_topic1 = CoursesTopicFactory.create(name="Art", parent=None)
99+
orphan_topic2 = CoursesTopicFactory.create(name="Literature", parent=None)
100+
101+
mock_page = Mock()
102+
mock_page.topics.all.return_value = [child_topic, orphan_topic1, orphan_topic2]
103+
104+
result = get_topics_from_page(mock_page)
105+
106+
# Should have 3 direct topics + 1 parent topic
107+
assert len(result) == 4
108+
109+
# Check direct topics are sorted alphabetically
110+
expected_direct_topics = [
111+
{"name": "Art"},
112+
{"name": "Literature"},
113+
{"name": "Physics"},
114+
]
115+
116+
for i, expected_topic in enumerate(expected_direct_topics):
117+
assert result[i] == expected_topic
118+
119+
# Check parent topic is included
120+
assert result[3] == {"name": "Science"}
121+
122+
def test_get_topics_from_page_with_multiple_parent_topics(self):
123+
"""Test page with topics from different parent categories"""
124+
# Create multiple parent topics
125+
science_parent = CoursesTopicFactory.create(name="Science", parent=None)
126+
tech_parent = CoursesTopicFactory.create(name="Technology", parent=None)
127+
arts_parent = CoursesTopicFactory.create(name="Arts", parent=None)
128+
129+
# Create child topics under different parents
130+
physics_topic = CoursesTopicFactory.create(
131+
name="Physics", parent=science_parent
132+
)
133+
programming_topic = CoursesTopicFactory.create(
134+
name="Programming", parent=tech_parent
135+
)
136+
music_topic = CoursesTopicFactory.create(name="Music", parent=arts_parent)
137+
138+
mock_page = Mock()
139+
mock_page.topics.all.return_value = [
140+
physics_topic,
141+
programming_topic,
142+
music_topic,
143+
]
144+
145+
result = get_topics_from_page(mock_page)
146+
147+
# Should have 3 direct topics + 3 parent topics
148+
assert len(result) == 6
149+
150+
# Check direct topics are sorted alphabetically
151+
expected_direct_topics = [
152+
{"name": "Music"},
153+
{"name": "Physics"},
154+
{"name": "Programming"},
155+
]
156+
157+
for i, expected_topic in enumerate(expected_direct_topics):
158+
assert result[i] == expected_topic
159+
160+
# Check that all parent topics are included
161+
parent_topic_names = {result[i]["name"] for i in range(3, 6)}
162+
assert parent_topic_names == {"Arts", "Science", "Technology"}
163+
164+
def test_get_topics_from_page_single_topic_with_parent(self):
165+
"""Test edge case with single topic that has a parent"""
166+
# Create parent topic
167+
parent_topic = CoursesTopicFactory.create(name="Science", parent=None)
168+
169+
# Create single child topic
170+
child_topic = CoursesTopicFactory.create(name="Physics", parent=parent_topic)
171+
172+
mock_page = Mock()
173+
mock_page.topics.all.return_value = [child_topic]
174+
175+
result = get_topics_from_page(mock_page)
176+
177+
# Should have 1 direct topic + 1 parent topic
178+
assert len(result) == 2
179+
assert result[0] == {"name": "Physics"}
180+
assert result[1] == {"name": "Science"}
181+
182+
def test_get_topics_from_page_with_course_page_object(self):
183+
"""Test integration with actual CoursesTopic model and database queries"""
184+
# Create real topics with parent-child relationships
185+
parent_topic = CoursesTopicFactory.create(name="Computer Science")
186+
child_topic1 = CoursesTopicFactory.create(
187+
name="Machine Learning", parent=parent_topic
188+
)
189+
child_topic2 = CoursesTopicFactory.create(
190+
name="Data Science", parent=parent_topic
191+
)
192+
orphan_topic = CoursesTopicFactory.create(name="Mathematics")
193+
194+
# Create a real CoursePage and assign topics to it
195+
course_page = CoursePageFactory.create()
196+
course_page.topics.set([child_topic1, child_topic2, orphan_topic])
197+
198+
# This will actually test the CoursesTopic.objects.filter logic
199+
result = get_topics_from_page(course_page)
200+
201+
# Should have 3 direct topics + 1 parent topic
202+
assert len(result) == 4
203+
204+
# Verify direct topics are sorted
205+
expected_direct_topics = [
206+
{"name": "Data Science"},
207+
{"name": "Machine Learning"},
208+
{"name": "Mathematics"},
209+
]
210+
211+
for i, expected_topic in enumerate(expected_direct_topics):
212+
assert result[i] == expected_topic
213+
214+
# Verify parent topic is included (found via database query)
215+
assert result[3] == {"name": "Computer Science"}
216+
217+
def test_get_topics_from_page_preserves_sorting_with_unicode_characters(self):
218+
"""Test that sorting works correctly with unicode characters"""
219+
# Create topics with unicode characters
220+
topic1 = CoursesTopicFactory.create(name="Ångström Physics")
221+
topic2 = CoursesTopicFactory.create(name="Ácoustics")
222+
topic3 = CoursesTopicFactory.create(name="Basic Math")
223+
224+
mock_page = Mock()
225+
mock_page.topics.all.return_value = [topic1, topic2, topic3]
226+
227+
result = get_topics_from_page(mock_page)
228+
229+
# Python's default sorting treats accented characters after non-accented ones
230+
# This reflects the actual behavior of the sorted() function in Python
231+
expected = [
232+
{"name": "Basic Math"},
233+
{"name": "Ácoustics"},
234+
{"name": "Ångström Physics"},
235+
]
236+
assert result == expected

0 commit comments

Comments
 (0)