"""SpaceDevs caches survive empty files and failed disk writes.""" import errno import json from datetime import datetime, timedelta from pathlib import Path from unittest.mock import Mock import pytest from agenda import thespacedevs @pytest.mark.parametrize("contents", ["", "{", "[]", '{"detail": "throttled"}']) def test_launch_cache_falls_back_to_usable_file(tmp_path: Path, contents: str) -> None: old = datetime.now() - timedelta(days=2) (tmp_path / old.strftime("%Y-%m-%d_%H:%M:%S.json")).write_text('{"results": []}') (tmp_path / datetime.now().strftime("%Y-%m-%d_%H:%M:%S.json")).write_text(contents) assert thespacedevs.load_cached_launches(str(tmp_path)) == {"results": []} assert thespacedevs.read_cached_launches(str(tmp_path)) == [] assert not thespacedevs.is_launches_cache_fresh(str(tmp_path)) def test_empty_file_is_not_parsed( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: filename = tmp_path / "empty.json" filename.touch() parser = Mock(side_effect=AssertionError("empty file must not be parsed")) monkeypatch.setattr("agenda.thespacedevs.json.load", parser) assert thespacedevs.read_json_cache(str(filename)) is None parser.assert_not_called() @pytest.mark.parametrize("existing", [False, True]) @pytest.mark.parametrize("failure", ["fsync", "replace"]) def test_disk_failure_preserves_cache( tmp_path: Path, monkeypatch: pytest.MonkeyPatch, existing: bool, failure: str ) -> None: filename = tmp_path / "cache.json" original = '{"results": ["old"]}' if existing: filename.write_text(original) fail = Mock(side_effect=OSError(errno.ENOSPC, "No space left on device")) monkeypatch.setattr(f"agenda.thespacedevs.os.{failure}", fail) with pytest.raises(OSError, match="No space left"): thespacedevs.write_json_cache(str(filename), {"results": ["new"]}) if existing: assert filename.read_text() == original else: assert not filename.exists() assert not list(tmp_path.glob(".cache-*")) def test_successful_write_replaces_empty_file(tmp_path: Path) -> None: filename = tmp_path / "cache.json" filename.touch() thespacedevs.write_json_cache(str(filename), {"results": []}) assert json.loads(filename.read_text()) == {"results": []} assert not list(tmp_path.glob(".cache-*")) def test_all_empty_launch_files_return_no_cache(tmp_path: Path) -> None: (tmp_path / "2026-09-30_12:00:00.json").touch() assert thespacedevs.load_cached_launches(str(tmp_path)) is None assert not thespacedevs.is_launches_cache_fresh(str(tmp_path))