Files
NotSoToothless 24335a2677 Auto merge dev → main (#1353)
* feat(gateway): hashed key store with grant + hot reload

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(gateway): channel registry + aiohttp app (keyed auth, whoami, per-channel ws/proxy)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(gateway): manage_keys CLI (add/list/revoke)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(gateway): retire srebot_external, run relay-gateway under PM2

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(gateway): point ecosystem + README at relay-gateway

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(tss): replay outbox producer for relay gateway

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(tss): forward processed games to relay outbox

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(tss-api): db helpers, app skeleton, info endpoint, fixtures

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(tss-api): player, games, history, search endpoints

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(tss-api): live, match, scoreboard, matches-search, maps

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(tss-api): filter-required leaderboards (players/vehicles/stats)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(tss-api): tournament list/detail/standings/matches

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat: wire tss upstream through gateway + tssbot-api PM2 app

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 03:38:20 -07:00

97 lines
5.6 KiB
Python

import sqlite3
import sys
from pathlib import Path
import pytest
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "SHARED"))
BATTLES_DDL = """
CREATE TABLE match_summary (
session_id TEXT PRIMARY KEY, mission_mode TEXT, mission_name TEXT, level_path TEXT,
mission_path TEXT, difficulty TEXT, starttime_unix INTEGER, endtime_unix INTEGER,
duration REAL, draw INTEGER NOT NULL DEFAULT 0, winning_slot TEXT, losing_slot TEXT,
received_unix INTEGER, tournament_id INTEGER, tournament_name TEXT, match_id TEXT, bracket TEXT);
CREATE TABLE player_games_hist (
UID TEXT NOT NULL, nick TEXT NOT NULL, team_name TEXT, team_slot TEXT, session_id TEXT NOT NULL,
vehicle TEXT, vehicle_internal TEXT, ground_kills INTEGER NOT NULL DEFAULT 0,
air_kills INTEGER NOT NULL DEFAULT 0, assists INTEGER NOT NULL DEFAULT 0,
captures INTEGER NOT NULL DEFAULT 0, deaths INTEGER NOT NULL DEFAULT 0, score INTEGER NOT NULL DEFAULT 0,
missile_evades INTEGER NOT NULL DEFAULT 0, shell_interceptions INTEGER NOT NULL DEFAULT 0,
team_kills_stat INTEGER NOT NULL DEFAULT 0, country_id INTEGER, victor_bool TEXT NOT NULL DEFAULT 'Loss',
endtime_unix INTEGER NOT NULL DEFAULT 0, team_id INTEGER, tss_role TEXT, pvp_ratio REAL,
UNIQUE (UID, session_id, vehicle_internal));
CREATE TABLE match_logs (
session_id TEXT PRIMARY KEY, chat_log_json TEXT, battle_log_json TEXT, built_unix INTEGER, event_log_json TEXT);
"""
TOURN_DDL = """
CREATE TABLE tournaments (
tournament_id INTEGER PRIMARY KEY, name TEXT, format TEXT, game_mode TEXT, cluster TEXT,
date_start INTEGER, date_end INTEGER, team_count INTEGER NOT NULL DEFAULT 0,
match_count INTEGER NOT NULL DEFAULT 0, status TEXT NOT NULL DEFAULT 'pending',
first_seen_unix INTEGER, scanned_unix INTEGER);
CREATE TABLE tournament_matches (
tournament_id INTEGER NOT NULL, match_id TEXT NOT NULL, type_bracket TEXT NOT NULL, side TEXT,
round INTEGER, position INTEGER, team_a_uuid TEXT, team_a_name TEXT, team_b_uuid TEXT, team_b_name TEXT,
winner_name TEXT, score_a INTEGER NOT NULL DEFAULT 0, score_b INTEGER NOT NULL DEFAULT 0,
status TEXT NOT NULL DEFAULT 'pending', time_start INTEGER,
PRIMARY KEY (tournament_id, match_id, type_bracket));
CREATE TABLE tournament_battles (
session_hex TEXT PRIMARY KEY, session_decimal TEXT, tournament_id INTEGER NOT NULL, match_id TEXT NOT NULL,
type_bracket TEXT NOT NULL, position INTEGER, winner_name TEXT, status_replay TEXT);
CREATE TABLE tournament_standings (
tournament_id INTEGER NOT NULL, group_index INTEGER NOT NULL DEFAULT 0, team_uuid TEXT NOT NULL,
team_name TEXT, points INTEGER NOT NULL DEFAULT 0, wins INTEGER NOT NULL DEFAULT 0,
draws INTEGER NOT NULL DEFAULT 0, losses INTEGER NOT NULL DEFAULT 0, buchholz REAL NOT NULL DEFAULT 0,
rank INTEGER, PRIMARY KEY (tournament_id, group_index, team_uuid));
"""
def _seed(battles: Path, tourn: Path) -> None:
b = sqlite3.connect(battles); b.executescript(BATTLES_DDL)
b.execute("INSERT INTO match_summary VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",
("sess1", "Tournament", "Gladiators 1x1 - Kursk", "levels/kursk.bin",
"gamedata/missions/x.blk", "realistic", 1780937467, 1780937680, 156.84, 0,
"1", "2", 1780937817, 24839, "Cadet 1x1 RB Air", "884571", "single-elim"))
b.execute("INSERT INTO player_games_hist (UID,nick,team_name,team_slot,session_id,vehicle,vehicle_internal,ground_kills,air_kills,assists,captures,deaths,score,country_id,victor_bool,endtime_unix,team_id,tss_role,pvp_ratio) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",
("148919027", "Joe", "SunThunder", "1", "sess1", "I-153 M-62", "i-153_m62",
0, 1, 0, 0, 1, 270, 3, "Win", 1780937680, 1211052, "captain", 1.0))
b.execute("INSERT INTO player_games_hist (UID,nick,team_name,team_slot,session_id,vehicle,vehicle_internal,deaths,score,victor_bool,endtime_unix,team_id,tss_role) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)",
("80080809", "Foe", "RedTeam", "2", "sess1", "I-153 M-62", "i-153_m62",
2, 90, "Loss", 1780937680, 1211099, "player"))
b.execute("INSERT INTO match_logs VALUES (?,?,?,?,?)",
("sess1", "[]", '["[01:18] kill"]', 1781776039, '{"kills": []}'))
b.commit(); b.close()
t = sqlite3.connect(tourn); t.executescript(TOURN_DDL)
t.execute("INSERT INTO tournaments VALUES (?,?,?,?,?,?,?,?,?,?,?,?)",
(24839, "Cadet 1x1 RB Air", "single-elim", "RB", "EU", 1780937400, 1780944668,
63, 63, "finished", 1782016814, 1782016814))
t.execute("INSERT INTO tournament_matches VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",
(24839, "336136", "Swiss", "swiss", "", "", "uuid-a", "BenisPutt", "uuid-b", "roflan",
"BenisPutt", 1, 0, "played", 1782014460))
t.execute("INSERT INTO tournament_battles VALUES (?,?,?,?,?,?,?,?)",
("6cbc20b001de1ee", "489698337002217966", 24839, "336136", "Swiss", 0, "BenisPutt", "view replay"))
t.execute("INSERT INTO tournament_standings VALUES (?,?,?,?,?,?,?,?,?,?)",
(24839, 0, "uuid-a", "BenisPutt", 4, 2, 0, 5, 41.0, 1))
t.commit(); t.close()
@pytest.fixture
def client(tmp_path, monkeypatch):
battles = tmp_path / "tss_battles.db"
tourn = tmp_path / "tss_tournaments.db"
_seed(battles, tourn)
monkeypatch.setenv("TSS_API_BATTLES_DB", str(battles))
monkeypatch.setenv("TSS_API_TOURNAMENTS_DB", str(tourn))
monkeypatch.setenv("STORAGE_VOL_PATH", str(tmp_path))
from fastapi.testclient import TestClient
import importlib
import web.api.db as dbmod
importlib.reload(dbmod)
import web.api.app as appmod
importlib.reload(appmod)
return TestClient(appmod.app)