screen-locker/screen_locker/tests/test_temperature.py
Krzysztof kuhy Rudnicki 399f9815c4
Some checks are pending
pre-commit / pre-commit (push) Waiting to run
Tests / test (3.10) (push) Waiting to run
Tests / test (3.11) (push) Waiting to run
Tests / test (3.12) (push) Waiting to run
Add phone-workout GitHub sync via shared crdt_sync (Workstream C)
Sync the workout log across devices using the shared crdt-sync /
crdt_sync_dart CRDT transport (same scheme as diet_guard).

- Python: new _workout_sync.py + _temperature_status_mixin.py; wire sync
  into screen_lock.py / status_view.py; add the crdt-sync git dependency
  and test_sync_fixtures / test_workout_sync suites.
- Dart (workout_app): new workout_sync_service.dart, github_device_auth.dart,
  sync_settings.dart with tests + fakes; settings/workout screens updated.
- docs: workstream-d-shared-crdt-transport.md.

Verification: pytest 747 passed at 100% coverage; flutter analyze clean;
flutter test passed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RRCdR4D8SaVrN2xraMQxM
2026-07-11 00:06:59 +02:00

100 lines
3.9 KiB
Python

"""Tests for wttr.in temperature fetching used by the heat-skip feature."""
from __future__ import annotations
import time
from unittest.mock import MagicMock, patch
import urllib.error
from screen_locker._temperature import TemperatureCheck, fetch_current_temp_with_status
def _mock_urlopen_returning(body: bytes) -> MagicMock:
mock_response = MagicMock()
mock_response.read.return_value = body
mock_urlopen = MagicMock()
mock_urlopen.return_value.__enter__.return_value = mock_response
return mock_urlopen
class TestFetchCurrentTempWithStatus:
def test_returns_parsed_temperature_on_success(self) -> None:
body = b'{"current_condition": [{"temp_C": "27"}]}'
with patch(
"screen_locker._temperature.urllib.request.urlopen",
_mock_urlopen_returning(body),
):
result = fetch_current_temp_with_status("Warsaw")
assert result == TemperatureCheck(temp_celsius=27.0, timed_out=False)
def test_returns_none_on_url_error(self) -> None:
with patch(
"screen_locker._temperature.urllib.request.urlopen",
side_effect=urllib.error.URLError("unreachable"),
):
result = fetch_current_temp_with_status("Warsaw")
assert result == TemperatureCheck(temp_celsius=None, timed_out=False)
def test_returns_none_on_timeout_error(self) -> None:
with patch(
"screen_locker._temperature.urllib.request.urlopen",
side_effect=TimeoutError("timed out"),
):
result = fetch_current_temp_with_status("Warsaw")
assert result == TemperatureCheck(temp_celsius=None, timed_out=False)
def test_returns_none_on_os_error(self) -> None:
with patch(
"screen_locker._temperature.urllib.request.urlopen",
side_effect=OSError("network unreachable"),
):
result = fetch_current_temp_with_status("Warsaw")
assert result == TemperatureCheck(temp_celsius=None, timed_out=False)
def test_returns_none_on_missing_key(self) -> None:
body = b'{"unexpected": "shape"}'
with patch(
"screen_locker._temperature.urllib.request.urlopen",
_mock_urlopen_returning(body),
):
result = fetch_current_temp_with_status("Warsaw")
assert result == TemperatureCheck(temp_celsius=None, timed_out=False)
def test_returns_none_on_empty_current_condition(self) -> None:
body = b'{"current_condition": []}'
with patch(
"screen_locker._temperature.urllib.request.urlopen",
_mock_urlopen_returning(body),
):
result = fetch_current_temp_with_status("Warsaw")
assert result == TemperatureCheck(temp_celsius=None, timed_out=False)
def test_returns_none_on_non_numeric_temp(self) -> None:
body = b'{"current_condition": [{"temp_C": "not-a-number"}]}'
with patch(
"screen_locker._temperature.urllib.request.urlopen",
_mock_urlopen_returning(body),
):
result = fetch_current_temp_with_status("Warsaw")
assert result == TemperatureCheck(temp_celsius=None, timed_out=False)
def test_returns_none_on_invalid_json(self) -> None:
with patch(
"screen_locker._temperature.urllib.request.urlopen",
_mock_urlopen_returning(b"not json{{{"),
):
result = fetch_current_temp_with_status("Warsaw")
assert result == TemperatureCheck(temp_celsius=None, timed_out=False)
def test_reports_timed_out_when_fetch_exceeds_hard_timeout(self) -> None:
def _slow_fetch(_city: str) -> float | None:
time.sleep(0.3)
return 27.0
with patch(
"screen_locker._temperature._fetch_current_temp_celsius_unbounded",
side_effect=_slow_fetch,
):
result = fetch_current_temp_with_status("Warsaw", hard_timeout=0.05)
assert result == TemperatureCheck(temp_celsius=None, timed_out=True)