Sith/com/tests/test_api.py

123 lines
4.0 KiB
Python
Raw Normal View History

2025-01-05 00:04:11 +00:00
from dataclasses import dataclass
2025-01-05 00:32:54 +00:00
from datetime import datetime, timedelta
2025-01-05 00:04:11 +00:00
from pathlib import Path
2025-01-05 00:32:54 +00:00
from typing import Callable
2025-01-05 00:04:11 +00:00
from unittest.mock import MagicMock, patch
import pytest
from django.conf import settings
2025-01-05 00:32:54 +00:00
from django.http import HttpResponse
2025-01-05 00:04:11 +00:00
from django.test.client import Client
from django.urls import reverse
2025-01-05 00:32:54 +00:00
from django.utils import timezone
2025-01-05 00:04:11 +00:00
from com.calendar import IcsCalendar
@dataclass
class MockResponse:
status: int
value: str
@property
def data(self):
return self.value.encode("utf8")
2025-01-05 00:32:54 +00:00
def accel_redirect_to_file(response: HttpResponse) -> Path | None:
redirect = Path(response.headers.get("X-Accel-Redirect", ""))
if not redirect.is_relative_to(Path("/") / settings.MEDIA_ROOT.stem):
return None
return settings.MEDIA_ROOT / redirect.relative_to(
Path("/") / settings.MEDIA_ROOT.stem
)
2025-01-05 00:04:11 +00:00
@pytest.mark.django_db
class TestExternalCalendar:
@pytest.fixture
def mock_request(self):
2025-01-05 00:32:54 +00:00
mock = MagicMock()
with patch("urllib3.request", mock):
yield mock
@pytest.fixture
def mock_current_time(self):
mock = MagicMock()
original = timezone.now
with patch("django.utils.timezone.now", mock):
yield mock, original
2025-01-05 00:04:11 +00:00
@pytest.fixture(autouse=True)
def clear_cache(self):
IcsCalendar._EXTERNAL_CALENDAR.unlink(missing_ok=True)
@pytest.mark.parametrize("error_code", [403, 404, 500])
def test_fetch_error(
self, client: Client, mock_request: MagicMock, error_code: int
):
mock_request.return_value = MockResponse(error_code, "not allowed")
assert client.get(reverse("api:calendar_external")).status_code == 404
def test_fetch_success(self, client: Client, mock_request: MagicMock):
external_response = MockResponse(200, "Definitely an ICS")
mock_request.return_value = external_response
response = client.get(reverse("api:calendar_external"))
assert response.status_code == 200
2025-01-05 00:32:54 +00:00
out_file = accel_redirect_to_file(response)
assert out_file is not None
2025-01-05 00:04:11 +00:00
assert out_file.exists()
with open(out_file, "r") as f:
assert f.read() == external_response.value
2025-01-05 00:32:54 +00:00
def test_fetch_caching(
self,
client: Client,
mock_request: MagicMock,
mock_current_time: tuple[MagicMock, Callable[[], datetime]],
):
fake_current_time, original_timezone = mock_current_time
start_time = original_timezone()
fake_current_time.return_value = start_time
external_response = MockResponse(200, "Definitely an ICS")
mock_request.return_value = external_response
with open(
accel_redirect_to_file(client.get(reverse("api:calendar_external"))), "r"
) as f:
assert f.read() == external_response.value
mock_request.return_value = MockResponse(200, "This should be ignored")
with open(
accel_redirect_to_file(client.get(reverse("api:calendar_external"))), "r"
) as f:
assert f.read() == external_response.value
mock_request.assert_called_once()
fake_current_time.return_value = start_time + timedelta(hours=1, seconds=1)
external_response = MockResponse(200, "This won't be ignored")
mock_request.return_value = external_response
with open(
accel_redirect_to_file(client.get(reverse("api:calendar_external"))), "r"
) as f:
assert f.read() == external_response.value
assert mock_request.call_count == 2
2025-01-05 00:36:41 +00:00
@pytest.mark.django_db
class TestInternalCalendar:
@pytest.fixture(autouse=True)
def clear_cache(self):
IcsCalendar._INTERNAL_CALENDAR.unlink(missing_ok=True)
def test_fetch_success(self, client: Client):
response = client.get(reverse("api:calendar_internal"))
assert response.status_code == 200
out_file = accel_redirect_to_file(response)
assert out_file is not None
assert out_file.exists()