154 lines
5.6 KiB
Python
154 lines
5.6 KiB
Python
from __future__ import annotations
|
|
|
|
import io
|
|
import os
|
|
import unittest
|
|
from contextlib import nullcontext, redirect_stdout
|
|
from datetime import datetime, timezone
|
|
from unittest.mock import patch
|
|
|
|
import app.historical_runner as historical_runner
|
|
from app.historical_runner import _maybe_run_database_maintenance, _run_refresh_with_retries
|
|
|
|
|
|
class HistoricalRunnerMaintenanceTests(unittest.TestCase):
|
|
def setUp(self) -> None:
|
|
historical_runner._LAST_DATABASE_MAINTENANCE_RUN_AT = None
|
|
|
|
def tearDown(self) -> None:
|
|
historical_runner._LAST_DATABASE_MAINTENANCE_RUN_AT = None
|
|
|
|
def test_scheduler_disabled_does_not_call_cleanup(self) -> None:
|
|
with (
|
|
patch.dict(os.environ, {"HLL_DB_MAINTENANCE_ENABLED": "false"}, clear=False),
|
|
patch("app.historical_runner.run_database_maintenance_cleanup") as cleanup,
|
|
):
|
|
result = _maybe_run_database_maintenance(
|
|
now=datetime(2026, 6, 20, 12, tzinfo=timezone.utc)
|
|
)
|
|
|
|
cleanup.assert_not_called()
|
|
self.assertEqual(result["status"], "skipped")
|
|
self.assertEqual(result["reason"], "disabled")
|
|
|
|
def test_scheduler_enabled_but_not_due_does_not_call_cleanup(self) -> None:
|
|
with (
|
|
patch.dict(
|
|
os.environ,
|
|
{
|
|
"HLL_DB_MAINTENANCE_ENABLED": "true",
|
|
"HLL_DB_MAINTENANCE_INTERVAL_SECONDS": "43200",
|
|
},
|
|
clear=False,
|
|
),
|
|
patch(
|
|
"app.historical_runner.run_database_maintenance_cleanup",
|
|
return_value={"status": "ok"},
|
|
) as cleanup,
|
|
):
|
|
first = _maybe_run_database_maintenance(
|
|
now=datetime(2026, 6, 20, 0, tzinfo=timezone.utc)
|
|
)
|
|
second = _maybe_run_database_maintenance(
|
|
now=datetime(2026, 6, 20, 1, tzinfo=timezone.utc)
|
|
)
|
|
|
|
self.assertEqual(first["status"], "ok")
|
|
self.assertEqual(second["status"], "skipped")
|
|
self.assertEqual(second["reason"], "not-due")
|
|
cleanup.assert_called_once()
|
|
|
|
def test_scheduler_enabled_and_due_calls_cleanup(self) -> None:
|
|
with (
|
|
patch.dict(os.environ, {"HLL_DB_MAINTENANCE_ENABLED": "true"}, clear=False),
|
|
patch(
|
|
"app.historical_runner.run_database_maintenance_cleanup",
|
|
return_value={"status": "ok"},
|
|
) as cleanup,
|
|
):
|
|
result = _maybe_run_database_maintenance(
|
|
now=datetime(2026, 6, 20, 12, tzinfo=timezone.utc)
|
|
)
|
|
|
|
cleanup.assert_called_once()
|
|
self.assertEqual(result["status"], "ok")
|
|
|
|
def test_cleanup_exception_is_logged_and_runner_continues(self) -> None:
|
|
stream = io.StringIO()
|
|
with (
|
|
patch.dict(os.environ, {"HLL_DB_MAINTENANCE_ENABLED": "true"}, clear=False),
|
|
patch("app.historical_runner.backend_writer_lock", return_value=nullcontext()),
|
|
patch(
|
|
"app.historical_runner._run_primary_rcon_capture",
|
|
return_value={"status": "ok", "targets": []},
|
|
),
|
|
patch(
|
|
"app.historical_runner.run_incremental_refresh",
|
|
return_value={"status": "ok"},
|
|
),
|
|
patch(
|
|
"app.historical_runner.generate_historical_snapshots",
|
|
return_value={"status": "ok"},
|
|
),
|
|
patch(
|
|
"app.historical_runner.rebuild_elo_mmr_models",
|
|
return_value={"status": "ok"},
|
|
),
|
|
patch(
|
|
"app.historical_runner.run_database_maintenance_cleanup",
|
|
side_effect=RuntimeError("maintenance failed"),
|
|
),
|
|
redirect_stdout(stream),
|
|
):
|
|
result = _run_refresh_with_retries(
|
|
max_retries=0,
|
|
retry_delay_seconds=0,
|
|
server_slug="comunidad-hispana-01",
|
|
max_pages=None,
|
|
page_size=None,
|
|
run_number=1,
|
|
)
|
|
|
|
self.assertEqual(result["status"], "ok")
|
|
self.assertEqual(result["database_maintenance_result"]["status"], "error")
|
|
self.assertIn("database-maintenance-scheduler-failed", stream.getvalue())
|
|
|
|
def test_interval_parsing_handles_invalid_values_safely(self) -> None:
|
|
with patch.dict(
|
|
os.environ,
|
|
{
|
|
"HLL_DB_MAINTENANCE_ENABLED": "true",
|
|
"HLL_DB_MAINTENANCE_INTERVAL_SECONDS": "bad",
|
|
},
|
|
clear=False,
|
|
):
|
|
interval_seconds, source = historical_runner._resolve_db_maintenance_interval_seconds()
|
|
|
|
self.assertEqual(interval_seconds, 43200)
|
|
self.assertEqual(source, "default-invalid-env-fallback")
|
|
|
|
def test_maintenance_state_is_tracked_in_process(self) -> None:
|
|
with (
|
|
patch.dict(
|
|
os.environ,
|
|
{
|
|
"HLL_DB_MAINTENANCE_ENABLED": "true",
|
|
"HLL_DB_MAINTENANCE_INTERVAL_SECONDS": "3600",
|
|
},
|
|
clear=False,
|
|
),
|
|
patch(
|
|
"app.historical_runner.run_database_maintenance_cleanup",
|
|
return_value={"status": "ok"},
|
|
),
|
|
):
|
|
_maybe_run_database_maintenance(now=datetime(2026, 6, 20, 12, tzinfo=timezone.utc))
|
|
self.assertEqual(
|
|
historical_runner._LAST_DATABASE_MAINTENANCE_RUN_AT,
|
|
datetime(2026, 6, 20, 12, tzinfo=timezone.utc),
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|