fix: project current match live data safely
This commit is contained in:
118
backend/tests/test_current_match_payload.py
Normal file
118
backend/tests/test_current_match_payload.py
Normal file
@@ -0,0 +1,118 @@
|
||||
from http import HTTPStatus
|
||||
from unittest.mock import patch
|
||||
|
||||
from app.payloads import build_current_match_payload
|
||||
from app.rcon_client import RconServerTarget
|
||||
from app.routes import resolve_get_payload
|
||||
|
||||
|
||||
TARGET = RconServerTarget(
|
||||
name="Comunidad Hispana #01",
|
||||
host="127.0.0.1",
|
||||
port=7779,
|
||||
password="test-password",
|
||||
source_name="test-rcon",
|
||||
external_server_id="comunidad-hispana-01",
|
||||
)
|
||||
|
||||
|
||||
def test_current_match_payload_projects_rich_live_rcon_session_fields():
|
||||
data = _build_with_rcon_sample(
|
||||
{
|
||||
"normalized": {
|
||||
"server_name": "Comunidad Hispana #01",
|
||||
"status": "online",
|
||||
"current_map": "carentan_warfare",
|
||||
"game_mode": "Warfare",
|
||||
"allied_score": 2,
|
||||
"axis_score": 2,
|
||||
"allied_players": 0,
|
||||
"axis_players": 0,
|
||||
"players": 0,
|
||||
"max_players": 100,
|
||||
"match_time_seconds": 5400,
|
||||
"remaining_match_time_seconds": 0,
|
||||
},
|
||||
"raw_session": {"mapId": "carentan_warfare", "mapName": "CARENTAN"},
|
||||
}
|
||||
)
|
||||
|
||||
assert data["map"] == "Carentan"
|
||||
assert data["map_id"] == "carentan_warfare"
|
||||
assert data["map_pretty_name"] == "Carentan"
|
||||
assert data["game_mode"] == "Warfare"
|
||||
assert data["allied_score"] == 2
|
||||
assert data["axis_score"] == 2
|
||||
assert data["players"] == 0
|
||||
assert data["player_count_quality"] == "rcon-session-unverified"
|
||||
assert data["player_count_source"] == "rcon-session"
|
||||
assert data["score_source"] == "rcon-session"
|
||||
assert data["map_source"] == "rcon-session"
|
||||
assert data["public_scoreboard_url"] == "https://scoreboard.comunidadhll.es"
|
||||
assert "/games" not in data["public_scoreboard_url"]
|
||||
|
||||
|
||||
def test_current_match_payload_preserves_missing_values_as_null():
|
||||
data = _build_with_rcon_sample(
|
||||
{
|
||||
"normalized": {
|
||||
"server_name": "Comunidad Hispana #01",
|
||||
"status": "online",
|
||||
"current_map": None,
|
||||
"game_mode": None,
|
||||
"players": None,
|
||||
"max_players": None,
|
||||
},
|
||||
"raw_session": {},
|
||||
}
|
||||
)
|
||||
|
||||
assert data["map"] is None
|
||||
assert data["map_id"] is None
|
||||
assert data["game_mode"] is None
|
||||
assert data["allied_score"] is None
|
||||
assert data["axis_score"] is None
|
||||
assert data["players"] is None
|
||||
assert data["player_count_quality"] is None
|
||||
assert data["player_count_source"] is None
|
||||
assert data["score_source"] is None
|
||||
assert data["map_source"] is None
|
||||
|
||||
|
||||
def test_current_match_payload_keeps_explicit_zero_score():
|
||||
data = _build_with_rcon_sample(
|
||||
{
|
||||
"normalized": {
|
||||
"server_name": "Comunidad Hispana #01",
|
||||
"status": "online",
|
||||
"current_map": "stmariedumont_warfare",
|
||||
"allied_score": 0,
|
||||
"axis_score": 0,
|
||||
},
|
||||
"raw_session": {
|
||||
"mapId": "stmariedumont_warfare",
|
||||
"mapName": "ST MARIE DU MONT",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
assert data["map"] == "St. Marie Du Mont"
|
||||
assert data["allied_score"] == 0
|
||||
assert data["axis_score"] == 0
|
||||
assert data["score_source"] == "rcon-session"
|
||||
|
||||
|
||||
def test_current_match_route_rejects_unsupported_server():
|
||||
status, payload = resolve_get_payload("/api/current-match?server=not-trusted")
|
||||
|
||||
assert status == HTTPStatus.NOT_FOUND
|
||||
assert payload["status"] == "error"
|
||||
|
||||
|
||||
def _build_with_rcon_sample(sample: dict[str, object]) -> dict[str, object]:
|
||||
with (
|
||||
patch("app.payloads.load_rcon_targets", return_value=(TARGET,)),
|
||||
patch("app.payloads.query_live_server_sample", return_value=sample),
|
||||
):
|
||||
payload = build_current_match_payload(server_slug="comunidad-hispana-01")
|
||||
return payload["data"]
|
||||
@@ -1,6 +1,7 @@
|
||||
import gc
|
||||
import json
|
||||
import sqlite3
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from app.rcon_admin_log_storage import (
|
||||
initialize_rcon_admin_log_storage,
|
||||
@@ -309,3 +310,61 @@ def test_current_match_kill_feed_prefers_open_match_window_and_normalizes_rows(t
|
||||
"is_teamkill": True,
|
||||
}
|
||||
gc.collect()
|
||||
|
||||
|
||||
def test_current_match_kill_feed_filters_stale_recent_fallback_rows(tmp_path):
|
||||
db_path = tmp_path / "admin_log.sqlite3"
|
||||
persist_rcon_admin_log_entries(
|
||||
target=TARGET,
|
||||
entries=[
|
||||
{
|
||||
"timestamp": "2026-05-21T09:30:00Z",
|
||||
"message": (
|
||||
"[1:00 min (1779355800)] KILL: Old Killer(Allies/steam-old) -> "
|
||||
"Old Victim(Axis/steam-victim-old) with M1 GARAND"
|
||||
),
|
||||
}
|
||||
],
|
||||
db_path=db_path,
|
||||
)
|
||||
|
||||
feed = list_current_match_kill_feed(
|
||||
server_key="test-rcon-target",
|
||||
db_path=db_path,
|
||||
now=datetime(2026, 5, 21, 10, 0, tzinfo=timezone.utc),
|
||||
)
|
||||
|
||||
assert feed["scope"] == "no-current-match-events"
|
||||
assert feed["confidence"] == "stale-filtered"
|
||||
assert feed["stale_events_filtered"] == 1
|
||||
assert feed["items"] == []
|
||||
gc.collect()
|
||||
|
||||
|
||||
def test_current_match_kill_feed_marks_fresh_recent_fallback_rows_partial(tmp_path):
|
||||
db_path = tmp_path / "admin_log.sqlite3"
|
||||
persist_rcon_admin_log_entries(
|
||||
target=TARGET,
|
||||
entries=[
|
||||
{
|
||||
"timestamp": "2026-05-21T09:50:00Z",
|
||||
"message": (
|
||||
"[1:00 min (1779357000)] KILL: Fresh Killer(Allies/steam-fresh) -> "
|
||||
"Fresh Victim(Axis/steam-victim-fresh) with M1 GARAND"
|
||||
),
|
||||
}
|
||||
],
|
||||
db_path=db_path,
|
||||
)
|
||||
|
||||
feed = list_current_match_kill_feed(
|
||||
server_key="test-rcon-target",
|
||||
db_path=db_path,
|
||||
now=datetime(2026, 5, 21, 10, 0, tzinfo=timezone.utc),
|
||||
)
|
||||
|
||||
assert feed["scope"] == "recent-admin-log-window"
|
||||
assert feed["confidence"] == "partial"
|
||||
assert feed["stale_events_filtered"] == 0
|
||||
assert [item["killer_name"] for item in feed["items"]] == ["Fresh Killer"]
|
||||
gc.collect()
|
||||
|
||||
Reference in New Issue
Block a user