Files
OpenFUT/fifa17-recon/docker/fifa17-python/tools/test_match_lifecycle.py
T
root 70a64e3709 fifa17-python: commit working FUT backend deployment (client/server split)
Freeze the running offline FUT backend into version control as
fifa17-recon/docker/fifa17-python/ - declarative and rebuildable from a
fresh checkout:

* OPENFUT_BIND / OPENFUT_ADVERTISE client/server split in the responders
  (lsx, blaze, roster, utas, pow) + entrypoint.sh; OPENFUT_ADVERTISE is
  required for remote mode (compose and entrypoint fail without it)
* docker-compose.yml reproducing the frozen baseline container exactly
  (env, ports incl. the 8085->8080 POW-content remap, /state bind, restart)
* .env.example / .env for site config - the LAN IP is never hardcoded in source
* tools/ + data/ staged from openfut-fut-backend:python-baseline-2026-08-10,
  verified byte-identical to the running container at freeze time
* client_arm.sh (the 105 client-side arming counterpart)
* Dockerfile bakes /app/SHA256SUMS.txt so any image is self-identifying
* docs/BASELINE-python-2026-08-10.md: frozen image/container/hash record,
  restore instructions and rebuild-equivalence procedure

Secrets (redir key/cert, .env) and runtime state (docker/state) stay gitignored.
The live container is untouched pending the .105 launcher audit.
2026-08-10 23:54:04 +00:00

89 lines
3.1 KiB
Python

#!/usr/bin/env python3
"""Isolated account-scoped regression for the FUT match HTTP lifecycle.
Drives CREATE -> READY -> PLAY -> END through match_route using a temporary
profile root. No live profile or server is touched.
"""
import importlib
import json
import os
import sys
import tempfile
TOOLS = os.path.dirname(os.path.abspath(__file__))
if TOOLS not in sys.path:
sys.path.insert(0, TOOLS)
class Request:
def __init__(self, path, body, command="POST"):
self.path = path
self.command = command
self._body = json.dumps(body).encode("utf-8")
def main():
with tempfile.TemporaryDirectory() as state:
os.environ["FUT_ACCOUNT_PATH"] = os.path.join(state, "active_account.json")
os.environ["FUT_PROFILE_ROOT"] = os.path.join(state, "accounts")
os.environ.pop("FUT_PROFILE", None)
import fut_account
import fut_store
import fut_accounts
import utas_server
importlib.reload(fut_account)
importlib.reload(fut_store)
importlib.reload(fut_accounts)
importlib.reload(utas_server)
persona_id = 909001
fut_accounts.activate({"personaId": persona_id, "personaName": "MATCH_TEST"})
initial = fut_store.STORE.load()
initial_coins = initial["coins"]
initial_next_id = initial["nextItemId"]
status, created = utas_server.match_route(
Request("/ut/game/fifa17/match", {}))
assert status == 200
match_id = created["id"]
assert created["reportIdEnabled"] is False
assert fut_store.STORE.load()["nextItemId"] == initial_next_id + 1
status, ready = utas_server.match_route(
Request("/ut/game/fifa17/match/ready", {"matchId": match_id}))
assert status == 200
assert ready == {"matchId": match_id, "opponentPersonaId": 0}
next_id_before_play = fut_store.STORE.load()["nextItemId"]
status, played = utas_server.match_route(
Request("/ut/game/fifa17/match", {"matchId": match_id}))
assert status == 200
assert played == {}
assert fut_store.STORE.load()["nextItemId"] == next_id_before_play
status, ended = utas_server.match_route(Request(
"/ut/game/fifa17/match/end",
{"matchId": match_id, "endReason": "WIN",
"myMatchStats": {"goals": 2},
"opponentMatchStats": {"goals": 1}},
))
assert status == 200
expected_reward = (utas_server.MATCH_COINS["won"]
+ utas_server.MATCH_PARTICIPATION)
assert ended["allCoins"] == initial_coins + expected_reward
profile_path = os.path.join(state, "accounts", str(persona_id),
"fifa17_profile.json")
persisted = json.load(open(profile_path, encoding="utf-8"))
assert persisted["coins"] == initial_coins + expected_reward
assert persisted["record"] == {"won": 1, "draw": 0, "loss": 0}
assert persisted["matchesPlayed"] == 1
print("match lifecycle persistence: PASS")
return 0
if __name__ == "__main__":
raise SystemExit(main())