#!/usr/bin/env python3 """Regression tests for the empty-My-Packs FIFA 17 compatibility workaround (bug 6c). Pins the behavior store_catalog() now depends on: - unopenedPackIds == [] -> exactly one synthetic active `mypacks` placeholder id 65534 - unopenedPackIds == [70] -> no synthetic placeholder; the genuine owned pack is shown - synthetic id 65534 stays economy-safe (non-resolvable, non-openable, non-granting) - normal store packs (1/5/6/7) are untouched by the empty-state behavior See docs/evidence/STORE_TILE_6C.md and FIFA17_EMPTY_MYPACKS_CLIENT_CONTRACT.md. Standalone unit test in the project style: `python3 test_empty_mypacks.py`. """ import importlib 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) SENTINEL_ID = 65534 def _set_unopened(fut_store, ids): """Deterministically set the active profile's owned unopened packs.""" p = fut_store.STORE.load() p["unopenedPackIds"] = list(ids) fut_store.STORE._save() def _mypacks(catalog): return [p for p in catalog["purchase"] if (p.get("displayGroup") or {}).get("value") == "mypacks"] def _fake_request(command, body): class _H: pass h = _H() h.command = command h._body = body return h 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) fut_accounts.activate({"personaId": 111001, "personaName": "TEST_A"}) catalog_ids = [p["id"] for p in fut_store.PACK_CATALOG] # ---- A. Empty unopened packs -> one active synthetic 65534 placeholder ---- _set_unopened(fut_store, []) utas_server._OPENED_PACK_GRACE.clear() status, cat = utas_server.store_catalog(None) assert status == 200 myp = _mypacks(cat) assert len(myp) == 1, "expected exactly one mypacks entry, got %r" % myp s = myp[0] assert s["id"] == SENTINEL_ID, s assert s["state"] == "active", s # the P2 fix: active, not inactive assert (s.get("displayGroup") or {}).get("value") == "mypacks", s assert SENTINEL_ID not in catalog_ids, "65534 must not be in PACK_CATALOG" assert fut_store.pack_by_id(SENTINEL_ID) is None print("A empty-state active placeholder: PASS") # ---- D (empty half). Normal packs untouched in empty state ---- norm = {p["id"]: p for p in cat["purchase"] if p["id"] in (1, 5, 6, 7)} assert set(norm) == {1, 5, 6, 7}, sorted(norm) assert all(norm[i]["state"] == "active" for i in norm), norm assert norm[1]["packType"] == "BRONZE" and norm[1]["description"] == "Bronze Pack" # ---- B. Non-empty unopened packs -> NO synthetic; genuine owned pack shown ---- _set_unopened(fut_store, [70]) utas_server._OPENED_PACK_GRACE.clear() status, cat = utas_server.store_catalog(None) assert status == 200 ids = [p["id"] for p in cat["purchase"]] assert SENTINEL_ID not in ids, "synthetic placeholder must be suppressed when a pack exists" myp = _mypacks(cat) assert len(myp) == 1 and myp[0]["id"] == 70, myp assert myp[0]["state"] == "active" and myp[0]["unopened"] is True, myp[0] # normal packs still intact alongside the owned pack assert {1, 5, 6, 7}.issubset(set(ids)), sorted(ids) print("B non-empty-state genuine pack: PASS") # ---- C. Economy safety of the synthetic placeholder ---- _set_unopened(fut_store, []) utas_server._OPENED_PACK_GRACE.clear() coins0 = fut_store.STORE.coins() items0 = len(fut_store.STORE.items()) next0 = fut_store.STORE.load()["nextItemId"] assert fut_store.pack_by_id(SENTINEL_ID) is None # store_buy: a confirmed-buy transaction for 65534 must be a no-op {} status, body = utas_server.store_buy( _fake_request("PUT", b'{"packId":65534,"state":"TRANSACTIONCREATED"}')) assert status == 200 and body == {}, (status, body) # purchased_items: POST buy for 65534 must not open/grant anything status, body = utas_server.purchased_items( _fake_request("POST", b'{"packId":65534,"useCredits":1,"usePreOrder":0,"currency":"COINS"}')) assert status == 200, (status, body) assert "createPackResponse" not in body, body # 65534 cannot enter the owned-pack pile (not a catalog pack) assert fut_store.STORE.grant_unopened_pack(SENTINEL_ID) is False assert SENTINEL_ID not in fut_store.STORE.unopened_packs() # nothing mutated assert fut_store.STORE.coins() == coins0, (fut_store.STORE.coins(), coins0) assert len(fut_store.STORE.items()) == items0 assert fut_store.STORE.load()["nextItemId"] == next0 assert not any(i.get("id") == SENTINEL_ID or i.get("resourceId") == SENTINEL_ID for i in fut_store.STORE.items()) print("C economy safety (65534 non-openable / non-granting): PASS") print("empty My Packs compatibility: PASS") return 0 if __name__ == "__main__": raise SystemExit(main())