179 lines
4.9 KiB
Python
179 lines
4.9 KiB
Python
import gc
|
|
import sqlite3
|
|
|
|
from app.rcon_admin_log_storage import (
|
|
initialize_rcon_admin_log_storage,
|
|
list_rcon_admin_log_event_counts,
|
|
persist_rcon_admin_log_entries,
|
|
)
|
|
|
|
|
|
TARGET = {
|
|
"target_key": "test-rcon-target",
|
|
"external_server_id": "test-rcon-target",
|
|
}
|
|
|
|
|
|
def test_initialize_rcon_admin_log_storage_creates_event_table(tmp_path):
|
|
db_path = tmp_path / "admin_log.sqlite3"
|
|
|
|
resolved_path = initialize_rcon_admin_log_storage(db_path=db_path)
|
|
|
|
assert resolved_path == db_path
|
|
connection = sqlite3.connect(db_path)
|
|
try:
|
|
table_names = {
|
|
row[0]
|
|
for row in connection.execute(
|
|
"SELECT name FROM sqlite_master WHERE type = 'table'"
|
|
).fetchall()
|
|
}
|
|
columns = {
|
|
row[1]
|
|
for row in connection.execute("PRAGMA table_info(rcon_admin_log_events)")
|
|
}
|
|
finally:
|
|
connection.close()
|
|
gc.collect()
|
|
|
|
assert "rcon_admin_log_events" in table_names
|
|
assert {
|
|
"target_key",
|
|
"event_type",
|
|
"raw_message",
|
|
"canonical_message",
|
|
"parsed_payload_json",
|
|
"raw_entry_json",
|
|
}.issubset(columns)
|
|
|
|
|
|
def test_persist_rcon_admin_log_entries_inserts_then_reports_duplicates(tmp_path):
|
|
db_path = tmp_path / "admin_log.sqlite3"
|
|
entries = [
|
|
{
|
|
"timestamp": "2026-05-19T10:00:00Z",
|
|
"message": "[1:00 min (100)] CONNECTED Player One (steam-1)",
|
|
},
|
|
{
|
|
"timestamp": "2026-05-19T10:01:00Z",
|
|
"message": "[2:00 min (120)] DISCONNECTED Player One (steam-1)",
|
|
},
|
|
]
|
|
|
|
first_delta = persist_rcon_admin_log_entries(
|
|
target=TARGET,
|
|
entries=entries,
|
|
db_path=db_path,
|
|
)
|
|
second_delta = persist_rcon_admin_log_entries(
|
|
target=TARGET,
|
|
entries=entries,
|
|
db_path=db_path,
|
|
)
|
|
|
|
assert first_delta == {
|
|
"events_seen": 2,
|
|
"events_inserted": 2,
|
|
"duplicate_events": 0,
|
|
}
|
|
assert second_delta == {
|
|
"events_seen": 2,
|
|
"events_inserted": 0,
|
|
"duplicate_events": 2,
|
|
}
|
|
gc.collect()
|
|
|
|
|
|
def test_canonical_message_dedupes_changing_relative_prefixes(tmp_path):
|
|
db_path = tmp_path / "admin_log.sqlite3"
|
|
original_entry = {
|
|
"timestamp": "2026-05-19T10:00:00Z",
|
|
"message": "[1:00 min (100)] MESSAGE: player [Player One(steam-1)], content [hello]",
|
|
}
|
|
repeated_read_entry = {
|
|
"timestamp": "2026-05-19T10:05:00Z",
|
|
"message": "[6:00 min (100)] MESSAGE: player [Player One(steam-1)], content [hello]",
|
|
}
|
|
|
|
first_delta = persist_rcon_admin_log_entries(
|
|
target=TARGET,
|
|
entries=[original_entry],
|
|
db_path=db_path,
|
|
)
|
|
second_delta = persist_rcon_admin_log_entries(
|
|
target=TARGET,
|
|
entries=[repeated_read_entry],
|
|
db_path=db_path,
|
|
)
|
|
|
|
assert first_delta["events_inserted"] == 1
|
|
assert second_delta == {
|
|
"events_seen": 1,
|
|
"events_inserted": 0,
|
|
"duplicate_events": 1,
|
|
}
|
|
gc.collect()
|
|
|
|
|
|
def test_list_rcon_admin_log_event_counts_groups_by_target_and_event_type(tmp_path):
|
|
db_path = tmp_path / "admin_log.sqlite3"
|
|
other_target = {
|
|
"target_key": "other-rcon-target",
|
|
"external_server_id": "other-rcon-target",
|
|
}
|
|
|
|
persist_rcon_admin_log_entries(
|
|
target=TARGET,
|
|
entries=[
|
|
{
|
|
"timestamp": "2026-05-19T10:00:00Z",
|
|
"message": "[1:00 min (100)] CONNECTED Player One (steam-1)",
|
|
},
|
|
{
|
|
"timestamp": "2026-05-19T10:01:00Z",
|
|
"message": "[2:00 min (120)] DISCONNECTED Player One (steam-1)",
|
|
},
|
|
],
|
|
db_path=db_path,
|
|
)
|
|
persist_rcon_admin_log_entries(
|
|
target=other_target,
|
|
entries=[
|
|
{
|
|
"timestamp": "2026-05-19T10:02:00Z",
|
|
"message": "[3:00 min (140)] CONNECTED Player Two (steam-2)",
|
|
}
|
|
],
|
|
db_path=db_path,
|
|
)
|
|
|
|
counts = {
|
|
(row["target_key"], row["event_type"]): row
|
|
for row in list_rcon_admin_log_event_counts(db_path=db_path)
|
|
}
|
|
|
|
assert counts == {
|
|
("other-rcon-target", "connected"): {
|
|
"target_key": "other-rcon-target",
|
|
"event_type": "connected",
|
|
"event_count": 1,
|
|
"first_server_time": 140,
|
|
"last_server_time": 140,
|
|
},
|
|
("test-rcon-target", "connected"): {
|
|
"target_key": "test-rcon-target",
|
|
"event_type": "connected",
|
|
"event_count": 1,
|
|
"first_server_time": 100,
|
|
"last_server_time": 100,
|
|
},
|
|
("test-rcon-target", "disconnected"): {
|
|
"target_key": "test-rcon-target",
|
|
"event_type": "disconnected",
|
|
"event_count": 1,
|
|
"first_server_time": 120,
|
|
"last_server_time": 120,
|
|
},
|
|
}
|
|
gc.collect()
|