Skip to content

Commit 06eeff1

Browse files
Unit testing for slack student info GET
1 parent 39dd00d commit 06eeff1

File tree

1 file changed

+255
-0
lines changed

1 file changed

+255
-0
lines changed
Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
import pytest
2+
from unittest.mock import Mock
3+
from datetime import datetime, date
4+
from fastapi import HTTPException
5+
6+
from src.slack.student_info.service import fetch_student_info
7+
8+
9+
class TestFetchStudentInfo:
10+
"""Test suite for fetch_student_info service function."""
11+
12+
@pytest.fixture
13+
def student_row_data(self):
14+
"""Fixture containing typical student row data."""
15+
return {
16+
'cti_id': 1,
17+
'fname': 'John',
18+
'lname': 'Doe',
19+
'pname': 'Johnny',
20+
'institution': 'UC Berkeley',
21+
'target_year': 2025,
22+
'join_date': datetime(2023, 9, 1),
23+
'gender': 'Male',
24+
'ethnicities_agg': ['Hispanic', 'Asian'],
25+
'birthday': date(2004, 5, 15),
26+
'first_gen': True,
27+
'email': 'john.doe@example.com'
28+
}
29+
30+
def test_fetch_student_info_success(self, mock_postgresql_db, student_row_data):
31+
"""Test successful fetch of student info with valid email."""
32+
# Setup
33+
mock_result = Mock()
34+
mock_result._asdict.return_value = student_row_data
35+
mock_postgresql_db.execute.return_value.first.return_value = mock_result
36+
37+
# Execute
38+
result = fetch_student_info(mock_postgresql_db, 'john.doe@example.com')
39+
40+
# Assert
41+
assert result == student_row_data
42+
assert result['cti_id'] == 1
43+
assert result['fname'] == 'John'
44+
assert result['lname'] == 'Doe'
45+
mock_postgresql_db.execute.assert_called_once()
46+
47+
def test_fetch_student_info_with_preferred_name(self, mock_postgresql_db, student_row_data):
48+
"""Test that preferred name is included in result when present."""
49+
mock_result = Mock()
50+
mock_result._asdict.return_value = student_row_data
51+
mock_postgresql_db.execute.return_value.first.return_value = mock_result
52+
53+
result = fetch_student_info(mock_postgresql_db, 'john.doe@example.com')
54+
55+
assert result['pname'] == 'Johnny'
56+
57+
def test_fetch_student_info_without_preferred_name(self, mock_postgresql_db, student_row_data):
58+
"""Test result when student has no preferred name."""
59+
student_row_data['pname'] = None
60+
mock_result = Mock()
61+
mock_result._asdict.return_value = student_row_data
62+
mock_postgresql_db.execute.return_value.first.return_value = mock_result
63+
64+
result = fetch_student_info(mock_postgresql_db, 'john.doe@example.com')
65+
66+
assert result['pname'] is None
67+
68+
def test_fetch_student_info_no_records_found(self, mock_postgresql_db):
69+
"""Test HTTPException raised when no student records found."""
70+
mock_postgresql_db.execute.return_value.first.return_value = None
71+
72+
with pytest.raises(HTTPException) as exc_info:
73+
fetch_student_info(mock_postgresql_db, 'nonexistent@example.com')
74+
75+
assert exc_info.value.status_code == 404
76+
assert 'No student records found' in exc_info.value.detail
77+
assert 'nonexistent@example.com' in exc_info.value.detail
78+
79+
def test_fetch_student_info_with_null_optional_fields(self, mock_postgresql_db, student_row_data):
80+
"""Test that null optional fields are handled correctly."""
81+
student_row_data['pname'] = None
82+
student_row_data['gender'] = None
83+
student_row_data['birthday'] = None
84+
student_row_data['first_gen'] = None
85+
student_row_data['institution'] = None
86+
87+
mock_result = Mock()
88+
mock_result._asdict.return_value = student_row_data
89+
mock_postgresql_db.execute.return_value.first.return_value = mock_result
90+
91+
result = fetch_student_info(mock_postgresql_db, 'john.doe@example.com')
92+
93+
assert result['pname'] is None
94+
assert result['gender'] is None
95+
assert result['birthday'] is None
96+
assert result['first_gen'] is None
97+
assert result['institution'] is None
98+
99+
def test_fetch_student_info_with_ethnicities_agg(self, mock_postgresql_db, student_row_data):
100+
"""Test that ethnicities_agg array is returned correctly."""
101+
ethnicities = ['Hispanic', 'Asian', 'Pacific Islander']
102+
student_row_data['ethnicities_agg'] = ethnicities
103+
104+
mock_result = Mock()
105+
mock_result._asdict.return_value = student_row_data
106+
mock_postgresql_db.execute.return_value.first.return_value = mock_result
107+
108+
result = fetch_student_info(mock_postgresql_db, 'john.doe@example.com')
109+
110+
assert result['ethnicities_agg'] == ethnicities
111+
assert len(result['ethnicities_agg']) == 3
112+
113+
def test_fetch_student_info_with_empty_ethnicities_agg(self, mock_postgresql_db, student_row_data):
114+
"""Test handling of empty ethnicities array."""
115+
student_row_data['ethnicities_agg'] = []
116+
117+
mock_result = Mock()
118+
mock_result._asdict.return_value = student_row_data
119+
mock_postgresql_db.execute.return_value.first.return_value = mock_result
120+
121+
result = fetch_student_info(mock_postgresql_db, 'john.doe@example.com')
122+
123+
assert result['ethnicities_agg'] == []
124+
125+
def test_fetch_student_info_with_null_ethnicities_agg(self, mock_postgresql_db, student_row_data):
126+
"""Test handling of null ethnicities array."""
127+
student_row_data['ethnicities_agg'] = None
128+
129+
mock_result = Mock()
130+
mock_result._asdict.return_value = student_row_data
131+
mock_postgresql_db.execute.return_value.first.return_value = mock_result
132+
133+
result = fetch_student_info(mock_postgresql_db, 'john.doe@example.com')
134+
135+
assert result['ethnicities_agg'] is None
136+
137+
def test_fetch_student_info_different_email_formats(self, mock_postgresql_db, student_row_data):
138+
"""Test that function works with various email formats."""
139+
emails = [
140+
'user+tag@example.com',
141+
'user.name@example.co.uk',
142+
'user_name@example.org',
143+
'user123@example.com'
144+
]
145+
146+
for email in emails:
147+
mock_result = Mock()
148+
student_row_data['email'] = email
149+
mock_result._asdict.return_value = student_row_data
150+
mock_postgresql_db.execute.return_value.first.return_value = mock_result
151+
152+
result = fetch_student_info(mock_postgresql_db, email)
153+
154+
assert result['email'] == email
155+
156+
def test_fetch_student_info_query_construction(self, mock_postgresql_db, student_row_data):
157+
"""Test that the SQL query is constructed correctly."""
158+
mock_result = Mock()
159+
mock_result._asdict.return_value = student_row_data
160+
mock_postgresql_db.execute.return_value.first.return_value = mock_result
161+
162+
fetch_student_info(mock_postgresql_db, 'john.doe@example.com')
163+
164+
# Verify execute was called exactly once
165+
assert mock_postgresql_db.execute.call_count == 1
166+
167+
# Verify first() was called on the result
168+
mock_postgresql_db.execute.return_value.first.assert_called_once()
169+
170+
def test_fetch_student_info_returns_dict(self, mock_postgresql_db, student_row_data):
171+
"""Test that result is properly converted to dictionary."""
172+
mock_result = Mock()
173+
mock_result._asdict.return_value = student_row_data
174+
mock_postgresql_db.execute.return_value.first.return_value = mock_result
175+
176+
result = fetch_student_info(mock_postgresql_db, 'john.doe@example.com')
177+
178+
assert isinstance(result, dict)
179+
mock_result._asdict.assert_called_once()
180+
181+
def test_fetch_student_info_includes_all_required_fields(self, mock_postgresql_db, student_row_data):
182+
"""Test that all expected fields are present in the result."""
183+
required_fields = [
184+
'cti_id', 'fname', 'lname', 'pname', 'institution',
185+
'target_year', 'join_date', 'gender', 'ethnicities_agg',
186+
'birthday', 'first_gen', 'email'
187+
]
188+
189+
mock_result = Mock()
190+
mock_result._asdict.return_value = student_row_data
191+
mock_postgresql_db.execute.return_value.first.return_value = mock_result
192+
193+
result = fetch_student_info(mock_postgresql_db, 'john.doe@example.com')
194+
195+
for field in required_fields:
196+
assert field in result, f"Missing field: {field}"
197+
198+
def test_fetch_student_info_with_first_generation_true(self, mock_postgresql_db, student_row_data):
199+
"""Test first generation flag when True."""
200+
student_row_data['first_gen'] = True
201+
mock_result = Mock()
202+
mock_result._asdict.return_value = student_row_data
203+
mock_postgresql_db.execute.return_value.first.return_value = mock_result
204+
205+
result = fetch_student_info(mock_postgresql_db, 'john.doe@example.com')
206+
207+
assert result['first_gen'] is True
208+
209+
def test_fetch_student_info_with_first_generation_false(self, mock_postgresql_db, student_row_data):
210+
"""Test first generation flag when False."""
211+
student_row_data['first_gen'] = False
212+
mock_result = Mock()
213+
mock_result._asdict.return_value = student_row_data
214+
mock_postgresql_db.execute.return_value.first.return_value = mock_result
215+
216+
result = fetch_student_info(mock_postgresql_db, 'john.doe@example.com')
217+
218+
assert result['first_gen'] is False
219+
220+
def test_fetch_student_info_with_different_target_years(self, mock_postgresql_db, student_row_data):
221+
"""Test with various target year values."""
222+
target_years = [2024, 2025, 2026, 2027, 2028]
223+
224+
for year in target_years:
225+
student_row_data['target_year'] = year
226+
mock_result = Mock()
227+
mock_result._asdict.return_value = student_row_data
228+
mock_postgresql_db.execute.return_value.first.return_value = mock_result
229+
230+
result = fetch_student_info(mock_postgresql_db, 'john.doe@example.com')
231+
232+
assert result['target_year'] == year
233+
234+
def test_fetch_student_info_case_sensitivity(self, mock_postgresql_db, student_row_data):
235+
"""Test that email lookup is case-sensitive (or handles as expected)."""
236+
# Note: This test documents current behavior. Adjust if email matching should be case-insensitive.
237+
mock_result = Mock()
238+
mock_result._asdict.return_value = student_row_data
239+
mock_postgresql_db.execute.return_value.first.return_value = mock_result
240+
241+
# Both calls use the exact email passed to the function
242+
result1 = fetch_student_info(mock_postgresql_db, 'john.doe@example.com')
243+
result2 = fetch_student_info(mock_postgresql_db, 'JOHN.DOE@EXAMPLE.COM')
244+
245+
assert mock_postgresql_db.execute.call_count == 2
246+
247+
def test_fetch_student_info_error_message_contains_email(self, mock_postgresql_db):
248+
"""Test that error message includes the email that was searched."""
249+
test_email = 'test@example.com'
250+
mock_postgresql_db.execute.return_value.first.return_value = None
251+
252+
with pytest.raises(HTTPException) as exc_info:
253+
fetch_student_info(mock_postgresql_db, test_email)
254+
255+
assert test_email in exc_info.value.detail

0 commit comments

Comments
 (0)