Fix
This commit is contained in:
171
backend/tests/test_rcon_historical_worker.py
Normal file
171
backend/tests/test_rcon_historical_worker.py
Normal file
@@ -0,0 +1,171 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import unittest
|
||||
from contextlib import contextmanager
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import patch
|
||||
|
||||
from app.rcon_historical_worker import (
|
||||
CAPTURE_MODE_CURRENT_LIVE,
|
||||
CAPTURE_MODE_HISTORICAL,
|
||||
build_arg_parser,
|
||||
main,
|
||||
run_rcon_historical_capture,
|
||||
run_rcon_historical_capture_unlocked,
|
||||
)
|
||||
|
||||
|
||||
TARGET = SimpleNamespace(
|
||||
external_server_id="comunidad-hispana-01",
|
||||
name="Comunidad Hispana #01",
|
||||
host="203.0.113.10",
|
||||
port=7779,
|
||||
region="ES",
|
||||
game_port=7777,
|
||||
query_port=7778,
|
||||
source_name="community-hispana-rcon",
|
||||
)
|
||||
|
||||
|
||||
class RconHistoricalWorkerTests(unittest.TestCase):
|
||||
def test_current_live_capture_skips_materialization(self) -> None:
|
||||
with (
|
||||
patch("app.rcon_historical_worker.initialize_rcon_historical_storage"),
|
||||
patch("app.rcon_historical_worker._select_targets", return_value=[TARGET]),
|
||||
patch(
|
||||
"app.rcon_historical_worker.query_live_server_sample",
|
||||
return_value={"normalized": {"players": 10}, "raw_session": {"raw": True}},
|
||||
),
|
||||
patch(
|
||||
"app.rcon_historical_worker.persist_rcon_historical_sample",
|
||||
return_value={"samples_inserted": 1, "duplicate_samples": 0},
|
||||
),
|
||||
patch(
|
||||
"app.rcon_historical_worker._ingest_target_admin_log",
|
||||
return_value={
|
||||
"status": "ok",
|
||||
"errors": [],
|
||||
"totals": {
|
||||
"events_seen": 4,
|
||||
"events_inserted": 2,
|
||||
"duplicate_events": 2,
|
||||
"failed_targets": 0,
|
||||
},
|
||||
},
|
||||
),
|
||||
patch("app.rcon_historical_worker.start_rcon_historical_capture_run", return_value=101),
|
||||
patch("app.rcon_historical_worker.finalize_rcon_historical_capture_run"),
|
||||
patch(
|
||||
"app.rcon_historical_worker.list_rcon_historical_target_statuses",
|
||||
return_value=[{"target_key": "comunidad-hispana-01"}],
|
||||
),
|
||||
patch("app.rcon_historical_worker.materialize_rcon_admin_log") as materialize,
|
||||
):
|
||||
payload = run_rcon_historical_capture_unlocked(capture_mode=CAPTURE_MODE_CURRENT_LIVE)
|
||||
|
||||
materialize.assert_not_called()
|
||||
self.assertEqual(payload["capture_mode"], CAPTURE_MODE_CURRENT_LIVE)
|
||||
self.assertIs(payload["materialization_skipped"], True)
|
||||
self.assertEqual(payload["admin_log_events_seen"], 4)
|
||||
self.assertEqual(payload["admin_log_events_inserted"], 2)
|
||||
self.assertEqual(payload["duplicate_events"], 2)
|
||||
self.assertEqual(payload["samples_inserted"], 1)
|
||||
self.assertEqual(payload["materialization_result"]["status"], "skipped")
|
||||
|
||||
def test_historical_capture_keeps_materialization(self) -> None:
|
||||
with (
|
||||
patch("app.rcon_historical_worker.initialize_rcon_historical_storage"),
|
||||
patch("app.rcon_historical_worker._select_targets", return_value=[TARGET]),
|
||||
patch(
|
||||
"app.rcon_historical_worker.query_live_server_sample",
|
||||
return_value={"normalized": {"players": 10}, "raw_session": {"raw": True}},
|
||||
),
|
||||
patch(
|
||||
"app.rcon_historical_worker.persist_rcon_historical_sample",
|
||||
return_value={"samples_inserted": 1, "duplicate_samples": 0},
|
||||
),
|
||||
patch(
|
||||
"app.rcon_historical_worker._ingest_target_admin_log",
|
||||
return_value={
|
||||
"status": "ok",
|
||||
"errors": [],
|
||||
"totals": {
|
||||
"events_seen": 1,
|
||||
"events_inserted": 1,
|
||||
"duplicate_events": 0,
|
||||
"failed_targets": 0,
|
||||
},
|
||||
},
|
||||
),
|
||||
patch("app.rcon_historical_worker.start_rcon_historical_capture_run", return_value=102),
|
||||
patch("app.rcon_historical_worker.finalize_rcon_historical_capture_run"),
|
||||
patch(
|
||||
"app.rcon_historical_worker.list_rcon_historical_target_statuses",
|
||||
return_value=[{"target_key": "comunidad-hispana-01"}],
|
||||
),
|
||||
patch(
|
||||
"app.rcon_historical_worker.materialize_rcon_admin_log",
|
||||
return_value={"matches_materialized": 3, "matches_updated": 2},
|
||||
) as materialize,
|
||||
):
|
||||
payload = run_rcon_historical_capture_unlocked(capture_mode=CAPTURE_MODE_HISTORICAL)
|
||||
|
||||
materialize.assert_called_once_with()
|
||||
self.assertEqual(payload["capture_mode"], CAPTURE_MODE_HISTORICAL)
|
||||
self.assertIs(payload["materialization_skipped"], False)
|
||||
self.assertEqual(payload["totals"]["materialized_matches_inserted"], 3)
|
||||
self.assertEqual(payload["totals"]["materialized_matches_updated"], 2)
|
||||
|
||||
def test_cli_and_env_can_activate_current_live_mode(self) -> None:
|
||||
with _temporary_env(
|
||||
HLL_RCON_CURRENT_MATCH_MODE="true",
|
||||
HLL_RCON_CURRENT_MATCH_CAPTURE_INTERVAL_SECONDS="5",
|
||||
):
|
||||
args = build_arg_parser().parse_args(["loop"])
|
||||
with patch("app.rcon_historical_worker.run_periodic_rcon_historical_capture") as runner:
|
||||
exit_code = main(["loop"])
|
||||
|
||||
self.assertEqual(args.capture_mode, CAPTURE_MODE_CURRENT_LIVE)
|
||||
self.assertEqual(exit_code, 0)
|
||||
runner.assert_called_once()
|
||||
self.assertEqual(runner.call_args.kwargs["capture_mode"], CAPTURE_MODE_CURRENT_LIVE)
|
||||
self.assertEqual(runner.call_args.kwargs["interval_seconds"], 5)
|
||||
|
||||
explicit = build_arg_parser().parse_args(["capture", "--skip-materialization"])
|
||||
self.assertIs(explicit.skip_materialization, True)
|
||||
|
||||
def test_current_live_capture_uses_short_lock_timeout(self) -> None:
|
||||
seen: dict[str, object] = {}
|
||||
|
||||
@contextmanager
|
||||
def fake_lock(**kwargs):
|
||||
seen.update(kwargs)
|
||||
yield {"holder": kwargs["holder"]}
|
||||
|
||||
with (
|
||||
_temporary_env(HLL_RCON_CURRENT_MATCH_WRITER_LOCK_TIMEOUT_SECONDS="3.5"),
|
||||
patch("app.rcon_historical_worker.backend_writer_lock", side_effect=fake_lock),
|
||||
patch(
|
||||
"app.rcon_historical_worker.run_rcon_historical_capture_unlocked",
|
||||
return_value={"status": "ok"},
|
||||
),
|
||||
):
|
||||
run_rcon_historical_capture(capture_mode=CAPTURE_MODE_CURRENT_LIVE)
|
||||
|
||||
self.assertEqual(seen["timeout_seconds"], 3.5)
|
||||
|
||||
|
||||
@contextmanager
|
||||
def _temporary_env(**values: str):
|
||||
previous = {name: os.environ.get(name) for name in values}
|
||||
try:
|
||||
for name, value in values.items():
|
||||
os.environ[name] = value
|
||||
yield
|
||||
finally:
|
||||
for name, value in previous.items():
|
||||
if value is None:
|
||||
os.environ.pop(name, None)
|
||||
else:
|
||||
os.environ[name] = value
|
||||
Reference in New Issue
Block a user