8cb2a0f9c6
Unit tests cover classification and body parsing; they do not prove the running
host behaves. These three scripts exercise the real service, and they found
nothing broken but make the two load-bearing claims checkable:
fifa17-apply-snapshot.py Core truth around an apply: coins, owned rows, kind
histogram, the source stack's copy count, and the
target's mutable fields (contract/fitness/playStyle/
training/injury). Coins and ownership come from the
staging DB, not the wire, so the check cannot be
satisfied by a projection bug.
fifa17-apply-probe-smoke.py Replays the EXACT captured request plus the edges,
against the live host, no client needed:
1. {"apply":[{"id":100000003}]} -> 200 {"itemData":[]}
source=Consumable subtype=201 copies=1,
target=fifa17_200389 rating=87
2. two targets -> 400 apply_batch_unsupported
3. unknown target wire id -> 200 UNRESOLVED_WIRE_ID
4. unowned source -> 200 NOT_OWNED
then re-snapshots: Core identical after all four.
fifa17-apply-gate-off.py The production-safety claim. With APPLY_PROBE unset
the same request must produce the pre-probe
behaviour, and does: no apply-probe line, three
passthrough lines, 502 into the dead upstream, Core
unchanged. Verified by restarting staging without the
flag -- an assertion about failing closed is worth
nothing unless the closed path is executed.
Nothing here writes to production; snapshot reads the staging DB read-only.
52 lines
1.9 KiB
Python
Executable File
52 lines
1.9 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Core snapshot around the consumable-apply probe: coins, ownership, the source
|
|
consumable's copies, and the target's mutable state. Run before and after; the
|
|
probe must change NOTHING."""
|
|
import json
|
|
import sys
|
|
import urllib.request
|
|
|
|
H = {"X-OpenFUT-Game": "fifa17"}
|
|
SOURCE_RES = 5001004
|
|
TARGET_WIRE = 100000003
|
|
|
|
|
|
def get(p):
|
|
req = urllib.request.Request("http://127.0.0.1:8299" + p, headers=H)
|
|
with urllib.request.urlopen(req, timeout=30) as r:
|
|
return json.loads(r.read())
|
|
|
|
|
|
snap = {}
|
|
import sqlite3
|
|
con = sqlite3.connect("file:/home/alex/openfut-sold-staging/staging-core.db?mode=ro", uri=True)
|
|
snap["coins"] = con.execute("SELECT coins FROM clubs LIMIT 1").fetchone()[0]
|
|
snap["owned_rows"] = con.execute("SELECT COUNT(*) FROM owned_cards").fetchone()[0]
|
|
snap["by_kind"] = dict(con.execute("SELECT content_kind, COUNT(*) FROM owned_cards GROUP BY 1"))
|
|
con.close()
|
|
|
|
cons = get("/ut/game/fifa17/club/consumables/development")
|
|
stacks = cons.get("itemData") or []
|
|
snap["development_stacks"] = len(stacks)
|
|
snap["development_copies"] = sum(s.get("count", 0) for s in stacks)
|
|
snap["source_stack"] = next(
|
|
({"count": s.get("count"), "resourceId": s.get("resourceId")}
|
|
for s in stacks if s.get("resourceId") == SOURCE_RES), None)
|
|
|
|
sq = get("/ut/game/fifa17/squad/0")
|
|
players = (sq.get("squad") or sq).get("players") or []
|
|
for p in players:
|
|
it = p.get("itemData") or {}
|
|
if it.get("id") == TARGET_WIRE:
|
|
snap["target"] = {
|
|
"resourceId": it.get("resourceId"), "rating": it.get("rating"),
|
|
"contract": it.get("contract"), "fitness": it.get("fitness"),
|
|
"injuryType": it.get("injuryType"), "training": it.get("training"),
|
|
"playStyle": it.get("playStyle"), "preferredPosition": it.get("preferredPosition"),
|
|
}
|
|
break
|
|
|
|
print(json.dumps(snap, indent=2, sort_keys=True))
|
|
if len(sys.argv) > 1:
|
|
open(sys.argv[1], "w").write(json.dumps(snap, sort_keys=True))
|