Skip to content

Commit 0185c3a

Browse files
Add automation scripts tests (#586)
1 parent 5da8cfd commit 0185c3a

4 files changed

Lines changed: 474 additions & 0 deletions

File tree

Gemfile.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,7 @@ GEM
279279
PLATFORMS
280280
arm64-darwin-21
281281
arm64-darwin-23
282+
arm64-darwin-24
282283
x64-mingw-ucrt
283284
x86_64-darwin-21
284285
x86_64-darwin-22
Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
import tempfile
2+
import os
3+
import sys
4+
import pytest
5+
import pandas as pd
6+
from ruamel.yaml import YAML
7+
from automation_prepare_adhoc_availability import (
8+
get_available_mentor_sort,
9+
get_unavailable_mentor_sort,
10+
get_availability_update_dict,
11+
update_mentor_availability,
12+
MONTHS_MAP,
13+
TYPE_LONG_TERM,
14+
TYPE_AD_HOC,
15+
TYPE_BOTH
16+
)
17+
18+
yaml = YAML()
19+
20+
21+
class TestGetAvailableMentorSort:
22+
def test_new_mentor_with_full_availability_returns_500(self):
23+
mentor = {'name': 'Test Mentor', 'hours': 2}
24+
current_availability = [4, 5, 6]
25+
26+
result = get_available_mentor_sort(mentor, current_availability)
27+
28+
assert result == 500
29+
30+
def test_mentor_with_more_than_3_hours_returns_500(self):
31+
mentor = {'name': 'Test Mentor', 'hours': 4}
32+
current_availability = [4]
33+
34+
result = get_available_mentor_sort(mentor, current_availability)
35+
36+
assert result == 500
37+
38+
def test_mentor_with_3_or_less_hours_returns_200(self):
39+
mentor = {'name': 'Test Mentor', 'hours': 3}
40+
current_availability = [4]
41+
42+
result = get_available_mentor_sort(mentor, current_availability)
43+
44+
assert result == 200
45+
46+
def test_mentor_with_1_hour_returns_200(self):
47+
mentor = {'name': 'Test Mentor', 'hours': 1}
48+
current_availability = [4]
49+
50+
result = get_available_mentor_sort(mentor, current_availability)
51+
52+
assert result == 200
53+
54+
55+
class TestGetUnavailableMentorSort:
56+
def test_disabled_mentor_returns_1(self):
57+
mentor = {'name': 'Test Mentor', 'disabled': True, 'type': TYPE_BOTH}
58+
59+
result = get_unavailable_mentor_sort(mentor)
60+
61+
assert result == 1
62+
63+
def test_long_term_mentor_returns_10(self):
64+
mentor = {'name': 'Test Mentor', 'disabled': False, 'type': TYPE_LONG_TERM}
65+
66+
result = get_unavailable_mentor_sort(mentor)
67+
68+
assert result == 10
69+
70+
def test_ad_hoc_mentor_returns_100(self):
71+
mentor = {'name': 'Test Mentor', 'disabled': False, 'type': TYPE_AD_HOC}
72+
73+
result = get_unavailable_mentor_sort(mentor)
74+
75+
assert result == 100
76+
77+
def test_both_type_mentor_returns_100(self):
78+
mentor = {'name': 'Test Mentor', 'disabled': False, 'type': TYPE_BOTH}
79+
80+
result = get_unavailable_mentor_sort(mentor)
81+
82+
assert result == 100
83+
84+
85+
class TestGetAvailabilityUpdateDict:
86+
def test_returns_dict_with_mentor_hours(self):
87+
data = {
88+
'Mentor Name': ['Alice Smith', 'Bob Jones'],
89+
'Availability (Hours)': [5, 3]
90+
}
91+
df = pd.DataFrame(data)
92+
93+
result = get_availability_update_dict(df)
94+
95+
assert result['Alice Smith'] == 5
96+
assert result['Bob Jones'] == 3
97+
98+
def test_empty_hours_returns_none(self):
99+
data = {
100+
'Mentor Name': ['Alice Smith'],
101+
'Availability (Hours)': ['']
102+
}
103+
df = pd.DataFrame(data)
104+
105+
result = get_availability_update_dict(df)
106+
107+
assert result['Alice Smith'] is None
108+
109+
def test_nan_hours_returns_none(self):
110+
data = {
111+
'Mentor Name': ['Alice Smith'],
112+
'Availability (Hours)': [pd.NA]
113+
}
114+
df = pd.DataFrame(data)
115+
116+
result = get_availability_update_dict(df)
117+
118+
assert result['Alice Smith'] is None
119+
120+
def test_strips_whitespace_from_names(self):
121+
data = {
122+
'Mentor Name': [' Alice Smith '],
123+
'Availability (Hours)': [4]
124+
}
125+
df = pd.DataFrame(data)
126+
127+
result = get_availability_update_dict(df)
128+
129+
assert 'Alice Smith' in result
130+
131+
132+
class TestUpdateMentorAvailability:
133+
def test_updates_mentor_availability_from_xlsx(self, monkeypatch):
134+
135+
with tempfile.NamedTemporaryFile(suffix='.xlsx', delete=False) as xlsx_file:
136+
xlsx_path = xlsx_file.name
137+
138+
with tempfile.NamedTemporaryFile(suffix='.yml', mode='w', delete=False) as yml_file:
139+
yml_path = yml_file.name
140+
141+
try:
142+
df = pd.DataFrame({
143+
'Mentor Name': ['Alice Smith'],
144+
'Availability (Hours)': [5]
145+
})
146+
df.to_excel(xlsx_path, index=False)
147+
148+
mentors = [
149+
{
150+
'name': 'Alice Smith',
151+
'hours': 2,
152+
'availability': [4, 5],
153+
'sort': 200,
154+
'type': TYPE_AD_HOC,
155+
'disabled': False
156+
},
157+
{
158+
'name': 'Bob Jones',
159+
'hours': 3,
160+
'availability': [4],
161+
'sort': 100,
162+
'type': TYPE_LONG_TERM,
163+
'disabled': False
164+
}
165+
]
166+
167+
with open(yml_path, 'w') as f:
168+
yaml.dump(mentors, f)
169+
170+
update_mentor_availability(4, xlsx_path, yml_path)
171+
172+
with open(yml_path, 'r') as f:
173+
result = yaml.load(f)
174+
175+
alice = next(m for m in result if m['name'] == 'Alice Smith')
176+
assert alice['hours'] == 5
177+
assert alice['availability'] == [4]
178+
assert alice['sort'] == 500
179+
180+
bob = next(m for m in result if m['name'] == 'Bob Jones')
181+
assert bob['availability'] == []
182+
assert bob['sort'] == 10
183+
184+
finally:
185+
os.remove(xlsx_path)
186+
os.remove(yml_path)
187+
188+
def test_mentor_not_in_xlsx_becomes_unavailable(self, monkeypatch):
189+
with tempfile.NamedTemporaryFile(suffix='.xlsx', delete=False) as xlsx_file:
190+
xlsx_path = xlsx_file.name
191+
192+
with tempfile.NamedTemporaryFile(suffix='.yml', mode='w', delete=False) as yml_file:
193+
yml_path = yml_file.name
194+
195+
try:
196+
df = pd.DataFrame({
197+
'Mentor Name': ['Alice Smith'],
198+
'Availability (Hours)': [5]
199+
})
200+
df.to_excel(xlsx_path, index=False)
201+
202+
mentors = [
203+
{'name': 'Alice Smith', 'hours': 2, 'availability': [4], 'sort': 200, 'type': TYPE_AD_HOC, 'disabled': False},
204+
{'name': 'Bob Jones', 'hours': 3, 'availability': [4], 'sort': 200, 'type': TYPE_BOTH, 'disabled': False}
205+
]
206+
207+
with open(yml_path, 'w') as f:
208+
yaml.dump(mentors, f)
209+
210+
update_mentor_availability(4, xlsx_path, yml_path)
211+
212+
with open(yml_path, 'r') as f:
213+
result = yaml.load(f)
214+
215+
bob = next(m for m in result if m['name'] == 'Bob Jones')
216+
assert bob['availability'] == []
217+
assert bob['sort'] == 100
218+
219+
finally:
220+
os.remove(xlsx_path)
221+
os.remove(yml_path)
222+
223+
def test_keeps_existing_hours_when_xlsx_hours_empty(self):
224+
with tempfile.NamedTemporaryFile(suffix='.xlsx', delete=False) as xlsx_file:
225+
xlsx_path = xlsx_file.name
226+
227+
with tempfile.NamedTemporaryFile(suffix='.yml', mode='w', delete=False) as yml_file:
228+
yml_path = yml_file.name
229+
230+
try:
231+
df = pd.DataFrame({
232+
'Mentor Name': ['Alice Smith'],
233+
'Availability (Hours)': ['']
234+
})
235+
df.to_excel(xlsx_path, index=False)
236+
237+
mentors = [
238+
{'name': 'Alice Smith', 'hours': 3, 'availability': [4], 'sort': 200, 'type': TYPE_AD_HOC, 'disabled': False}
239+
]
240+
241+
with open(yml_path, 'w') as f:
242+
yaml.dump(mentors, f)
243+
244+
update_mentor_availability(4, xlsx_path, yml_path)
245+
246+
with open(yml_path, 'r') as f:
247+
result = yaml.load(f)
248+
249+
alice = result[0]
250+
assert alice['hours'] == 3
251+
252+
finally:
253+
os.remove(xlsx_path)
254+
os.remove(yml_path)

tools/tests/download_image_test.py

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import os
2+
import sys
3+
import io
4+
import tempfile
5+
import pytest
6+
import builtins
7+
import requests
8+
from unittest import mock
9+
10+
import download_image
11+
12+
13+
class TestDownloadImage:
14+
def test_successful_download_creates_file(self, tmp_path, monkeypatch):
15+
monkeypatch.setattr(download_image, "IMAGE_FILE_PATH", str(tmp_path))
16+
17+
mock_response = mock.Mock()
18+
mock_response.status_code = 200
19+
mock_response.content = b"fake image data"
20+
mock_response.raise_for_status = mock.Mock()
21+
monkeypatch.setattr(requests, "get", mock.Mock(return_value=mock_response))
22+
23+
url = "https://example.com/test.jpeg"
24+
mentor_name = "Alice Smith"
25+
image_path = download_image.download_image(url, mentor_name)
26+
27+
assert image_path.endswith("alice_smith.jpeg")
28+
assert os.path.exists(image_path)
29+
with open(image_path, "rb") as f:
30+
assert f.read() == b"fake image data"
31+
32+
def test_download_failure_returns_none(self, tmp_path, monkeypatch):
33+
monkeypatch.setattr(download_image, "IMAGE_FILE_PATH", str(tmp_path))
34+
35+
monkeypatch.setattr(requests, "get", mock.Mock(side_effect=requests.exceptions.RequestException("network error")))
36+
37+
result = download_image.download_image("https://badurl.com/image.jpeg", "Bob")
38+
assert result is None
39+
40+
def test_directory_created_if_not_exists(self, tmp_path, monkeypatch):
41+
image_dir = tmp_path / "new_images"
42+
monkeypatch.setattr(download_image, "IMAGE_FILE_PATH", str(image_dir))
43+
44+
mock_response = mock.Mock()
45+
mock_response.status_code = 200
46+
mock_response.content = b"123"
47+
mock_response.raise_for_status = mock.Mock()
48+
monkeypatch.setattr(requests, "get", mock.Mock(return_value=mock_response))
49+
50+
result = download_image.download_image("https://example.com/img.jpg", "John Doe")
51+
assert os.path.exists(image_dir)
52+
assert result.endswith("john_doe.jpeg")
53+
54+
def test_filename_sanitization(self, tmp_path, monkeypatch):
55+
monkeypatch.setattr(download_image, "IMAGE_FILE_PATH", str(tmp_path))
56+
57+
mock_response = mock.Mock()
58+
mock_response.content = b"data"
59+
mock_response.raise_for_status = mock.Mock()
60+
monkeypatch.setattr(requests, "get", mock.Mock(return_value=mock_response))
61+
62+
result = download_image.download_image("https://example.com/img.jpg", "Alice Smith-Jones")
63+
assert "alice_smith-jones.jpeg" in result
64+
65+
66+
class TestRunAutomation:
67+
def test_run_automation_success(self, tmp_path, monkeypatch, capsys):
68+
monkeypatch.setattr(sys, "argv", ["download_image.py", "https://example.com/test.jpg", "Charlie"])
69+
70+
fake_path = str(tmp_path / "charlie.jpeg")
71+
monkeypatch.setattr(download_image, "download_image", mock.Mock(return_value=fake_path))
72+
73+
download_image.run_automation()
74+
75+
captured = capsys.readouterr()
76+
assert "Image saved to" in captured.out
77+
download_image.download_image.assert_called_once()
78+
79+
def test_run_automation_failure(self, monkeypatch, capsys):
80+
monkeypatch.setattr(sys, "argv", ["download_image.py", "https://example.com/fail.jpg", "David"])
81+
monkeypatch.setattr(download_image, "download_image", mock.Mock(return_value=None))
82+
83+
download_image.run_automation()
84+
captured = capsys.readouterr()
85+
assert "Failed to download the image." in captured.out
86+
87+
def test_run_automation_no_args(self, monkeypatch, caplog):
88+
monkeypatch.setattr(sys, "argv", ["download_image.py"])
89+
caplog.set_level("INFO")
90+
91+
download_image.run_automation()
92+
93+
assert "Add parameters for download" in caplog.text

0 commit comments

Comments
 (0)