Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,7 @@ GEM
PLATFORMS
arm64-darwin-21
arm64-darwin-23
arm64-darwin-24
x64-mingw-ucrt
x86_64-darwin-21
x86_64-darwin-22
Expand Down
254 changes: 254 additions & 0 deletions tools/tests/automation_prepare_adhoc_availability_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
import tempfile
import os
import sys
import pytest
import pandas as pd
from ruamel.yaml import YAML
from automation_prepare_adhoc_availability import (
get_available_mentor_sort,
get_unavailable_mentor_sort,
get_availability_update_dict,
update_mentor_availability,
MONTHS_MAP,
TYPE_LONG_TERM,
TYPE_AD_HOC,
TYPE_BOTH
)

yaml = YAML()


class TestGetAvailableMentorSort:
def test_new_mentor_with_full_availability_returns_500(self):
mentor = {'name': 'Test Mentor', 'hours': 2}
current_availability = [4, 5, 6]

result = get_available_mentor_sort(mentor, current_availability)

assert result == 500

def test_mentor_with_more_than_3_hours_returns_500(self):
mentor = {'name': 'Test Mentor', 'hours': 4}
current_availability = [4]

result = get_available_mentor_sort(mentor, current_availability)

assert result == 500

def test_mentor_with_3_or_less_hours_returns_200(self):
mentor = {'name': 'Test Mentor', 'hours': 3}
current_availability = [4]

result = get_available_mentor_sort(mentor, current_availability)

assert result == 200

def test_mentor_with_1_hour_returns_200(self):
mentor = {'name': 'Test Mentor', 'hours': 1}
current_availability = [4]

result = get_available_mentor_sort(mentor, current_availability)

assert result == 200


class TestGetUnavailableMentorSort:
def test_disabled_mentor_returns_1(self):
mentor = {'name': 'Test Mentor', 'disabled': True, 'type': TYPE_BOTH}

result = get_unavailable_mentor_sort(mentor)

assert result == 1

def test_long_term_mentor_returns_10(self):
mentor = {'name': 'Test Mentor', 'disabled': False, 'type': TYPE_LONG_TERM}

result = get_unavailable_mentor_sort(mentor)

assert result == 10

def test_ad_hoc_mentor_returns_100(self):
mentor = {'name': 'Test Mentor', 'disabled': False, 'type': TYPE_AD_HOC}

result = get_unavailable_mentor_sort(mentor)

assert result == 100

def test_both_type_mentor_returns_100(self):
mentor = {'name': 'Test Mentor', 'disabled': False, 'type': TYPE_BOTH}

result = get_unavailable_mentor_sort(mentor)

assert result == 100


class TestGetAvailabilityUpdateDict:
def test_returns_dict_with_mentor_hours(self):
data = {
'Mentor Name': ['Alice Smith', 'Bob Jones'],
'Availability (Hours)': [5, 3]
}
df = pd.DataFrame(data)

result = get_availability_update_dict(df)

assert result['Alice Smith'] == 5
assert result['Bob Jones'] == 3

def test_empty_hours_returns_none(self):
data = {
'Mentor Name': ['Alice Smith'],
'Availability (Hours)': ['']
}
df = pd.DataFrame(data)

result = get_availability_update_dict(df)

assert result['Alice Smith'] is None

def test_nan_hours_returns_none(self):
data = {
'Mentor Name': ['Alice Smith'],
'Availability (Hours)': [pd.NA]
}
df = pd.DataFrame(data)

result = get_availability_update_dict(df)

assert result['Alice Smith'] is None

def test_strips_whitespace_from_names(self):
data = {
'Mentor Name': [' Alice Smith '],
'Availability (Hours)': [4]
}
df = pd.DataFrame(data)

result = get_availability_update_dict(df)

assert 'Alice Smith' in result


class TestUpdateMentorAvailability:
def test_updates_mentor_availability_from_xlsx(self, monkeypatch):

with tempfile.NamedTemporaryFile(suffix='.xlsx', delete=False) as xlsx_file:
xlsx_path = xlsx_file.name

with tempfile.NamedTemporaryFile(suffix='.yml', mode='w', delete=False) as yml_file:
yml_path = yml_file.name

try:
df = pd.DataFrame({
'Mentor Name': ['Alice Smith'],
'Availability (Hours)': [5]
})
df.to_excel(xlsx_path, index=False)

mentors = [
{
'name': 'Alice Smith',
'hours': 2,
'availability': [4, 5],
'sort': 200,
'type': TYPE_AD_HOC,
'disabled': False
},
{
'name': 'Bob Jones',
'hours': 3,
'availability': [4],
'sort': 100,
'type': TYPE_LONG_TERM,
'disabled': False
}
]

with open(yml_path, 'w') as f:
yaml.dump(mentors, f)

update_mentor_availability(4, xlsx_path, yml_path)

with open(yml_path, 'r') as f:
result = yaml.load(f)

alice = next(m for m in result if m['name'] == 'Alice Smith')
assert alice['hours'] == 5
assert alice['availability'] == [4]
assert alice['sort'] == 500

bob = next(m for m in result if m['name'] == 'Bob Jones')
assert bob['availability'] == []
assert bob['sort'] == 10

finally:
os.remove(xlsx_path)
os.remove(yml_path)

def test_mentor_not_in_xlsx_becomes_unavailable(self, monkeypatch):
with tempfile.NamedTemporaryFile(suffix='.xlsx', delete=False) as xlsx_file:
xlsx_path = xlsx_file.name

with tempfile.NamedTemporaryFile(suffix='.yml', mode='w', delete=False) as yml_file:
yml_path = yml_file.name

try:
df = pd.DataFrame({
'Mentor Name': ['Alice Smith'],
'Availability (Hours)': [5]
})
df.to_excel(xlsx_path, index=False)

mentors = [
{'name': 'Alice Smith', 'hours': 2, 'availability': [4], 'sort': 200, 'type': TYPE_AD_HOC, 'disabled': False},
{'name': 'Bob Jones', 'hours': 3, 'availability': [4], 'sort': 200, 'type': TYPE_BOTH, 'disabled': False}
]

with open(yml_path, 'w') as f:
yaml.dump(mentors, f)

update_mentor_availability(4, xlsx_path, yml_path)

with open(yml_path, 'r') as f:
result = yaml.load(f)

bob = next(m for m in result if m['name'] == 'Bob Jones')
assert bob['availability'] == []
assert bob['sort'] == 100

finally:
os.remove(xlsx_path)
os.remove(yml_path)

def test_keeps_existing_hours_when_xlsx_hours_empty(self):
with tempfile.NamedTemporaryFile(suffix='.xlsx', delete=False) as xlsx_file:
xlsx_path = xlsx_file.name

with tempfile.NamedTemporaryFile(suffix='.yml', mode='w', delete=False) as yml_file:
yml_path = yml_file.name

try:
df = pd.DataFrame({
'Mentor Name': ['Alice Smith'],
'Availability (Hours)': ['']
})
df.to_excel(xlsx_path, index=False)

mentors = [
{'name': 'Alice Smith', 'hours': 3, 'availability': [4], 'sort': 200, 'type': TYPE_AD_HOC, 'disabled': False}
]

with open(yml_path, 'w') as f:
yaml.dump(mentors, f)

update_mentor_availability(4, xlsx_path, yml_path)

with open(yml_path, 'r') as f:
result = yaml.load(f)

alice = result[0]
assert alice['hours'] == 3

finally:
os.remove(xlsx_path)
os.remove(yml_path)
93 changes: 93 additions & 0 deletions tools/tests/download_image_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import os
import sys
import io
import tempfile
import pytest
import builtins
import requests
from unittest import mock

import download_image


class TestDownloadImage:
def test_successful_download_creates_file(self, tmp_path, monkeypatch):
monkeypatch.setattr(download_image, "IMAGE_FILE_PATH", str(tmp_path))

mock_response = mock.Mock()
mock_response.status_code = 200
mock_response.content = b"fake image data"
mock_response.raise_for_status = mock.Mock()
monkeypatch.setattr(requests, "get", mock.Mock(return_value=mock_response))

url = "https://example.com/test.jpeg"
mentor_name = "Alice Smith"
image_path = download_image.download_image(url, mentor_name)

assert image_path.endswith("alice_smith.jpeg")
assert os.path.exists(image_path)
with open(image_path, "rb") as f:
assert f.read() == b"fake image data"

def test_download_failure_returns_none(self, tmp_path, monkeypatch):
monkeypatch.setattr(download_image, "IMAGE_FILE_PATH", str(tmp_path))

monkeypatch.setattr(requests, "get", mock.Mock(side_effect=requests.exceptions.RequestException("network error")))

result = download_image.download_image("https://badurl.com/image.jpeg", "Bob")
assert result is None

def test_directory_created_if_not_exists(self, tmp_path, monkeypatch):
image_dir = tmp_path / "new_images"
monkeypatch.setattr(download_image, "IMAGE_FILE_PATH", str(image_dir))

mock_response = mock.Mock()
mock_response.status_code = 200
mock_response.content = b"123"
mock_response.raise_for_status = mock.Mock()
monkeypatch.setattr(requests, "get", mock.Mock(return_value=mock_response))

result = download_image.download_image("https://example.com/img.jpg", "John Doe")
assert os.path.exists(image_dir)
assert result.endswith("john_doe.jpeg")

def test_filename_sanitization(self, tmp_path, monkeypatch):
monkeypatch.setattr(download_image, "IMAGE_FILE_PATH", str(tmp_path))

mock_response = mock.Mock()
mock_response.content = b"data"
mock_response.raise_for_status = mock.Mock()
monkeypatch.setattr(requests, "get", mock.Mock(return_value=mock_response))

result = download_image.download_image("https://example.com/img.jpg", "Alice Smith-Jones")
assert "alice_smith-jones.jpeg" in result


class TestRunAutomation:
def test_run_automation_success(self, tmp_path, monkeypatch, capsys):
monkeypatch.setattr(sys, "argv", ["download_image.py", "https://example.com/test.jpg", "Charlie"])

fake_path = str(tmp_path / "charlie.jpeg")
monkeypatch.setattr(download_image, "download_image", mock.Mock(return_value=fake_path))

download_image.run_automation()

captured = capsys.readouterr()
assert "Image saved to" in captured.out
download_image.download_image.assert_called_once()

def test_run_automation_failure(self, monkeypatch, capsys):
monkeypatch.setattr(sys, "argv", ["download_image.py", "https://example.com/fail.jpg", "David"])
monkeypatch.setattr(download_image, "download_image", mock.Mock(return_value=None))

download_image.run_automation()
captured = capsys.readouterr()
assert "Failed to download the image." in captured.out

def test_run_automation_no_args(self, monkeypatch, caplog):
monkeypatch.setattr(sys, "argv", ["download_image.py"])
caplog.set_level("INFO")

download_image.run_automation()

assert "Add parameters for download" in caplog.text
Loading