Fix public bank endpoint rollout (Refs josh/tws-zombies-scripts#13)
This commit is contained in:
@@ -1652,16 +1652,17 @@ class AgentTests(unittest.TestCase):
|
||||
operation = self.operation("instance.create", payload, "create")
|
||||
self.assertEqual(self.agent.execute(operation)["state"], "completed")
|
||||
path = self.agent.instance_dir(INSTANCE, 1) / "app/configs/electric_cherry_bank.cfg"
|
||||
self.assertEqual(path.read_text(), f'set tws_bank_token "{secret}"\nset tws_bank_url "https://controller.example/api/bank"\n')
|
||||
self.assertEqual(path.read_text(), f'set tws_bank_token "{secret}"\n')
|
||||
self.assertEqual(self.agent.config["controller"], "https://controller.example")
|
||||
self.assertIn("bank.settlement", self.agent.inventory()["capabilities"])
|
||||
payload["bank"]["token"] = secret + '\nset bad "1"'
|
||||
with self.assertRaises(AgentError):
|
||||
self.agent.initial_files(payload)
|
||||
|
||||
def bank_journal(self, decisions=("commit", "cancel"), game="t5"):
|
||||
path = self.agent.instance_dir(INSTANCE, 1) / f"app/runtime/tws-bank/{game}/scope.json"
|
||||
def bank_journal(self, decisions=("commit", "cancel"), game="t5", endpoint=module.BANK_GAME_ENDPOINT):
|
||||
path = self.agent.instance_dir(INSTANCE, 1) / f"app/runtime/tws-bank/{game}/{module.digest(endpoint.encode())}.json"
|
||||
reports = [json.dumps({"server": "t5_zombie_test_1", "guid": "ace", "match": "match-one", "request_id": f"{game}-{i}", "operation": "deposit", "amount": 2000, "capacity": 1000000, "decision": decision}) for i, decision in enumerate(decisions)]
|
||||
module.atomic_json(path, {"version": 1, "server": "t5_zombie_test_1", "endpoint": "https://controller.example/api/bank", "reports": reports})
|
||||
module.atomic_json(path, {"version": 1, "server": "t5_zombie_test_1", "endpoint": endpoint, "reports": reports})
|
||||
return path
|
||||
|
||||
def bank_acknowledgment(self, method, path, payload):
|
||||
@@ -1698,6 +1699,79 @@ class AgentTests(unittest.TestCase):
|
||||
self.assertFalse(path.exists())
|
||||
self.assertEqual(self.docker.calls, calls)
|
||||
|
||||
def test_endpoint_upgrade_rebuild_retries_original_decisions_before_starting_public_bank(self):
|
||||
secret = "ec_bank_" + "a" * 43
|
||||
payload = self.create()
|
||||
payload["bank"] = {"token": secret, "configPath": "configs/electric_cherry_bank.cfg"}
|
||||
paths = [self.bank_journal(game=game, endpoint=endpoint)
|
||||
for game in ("t5", "t6")
|
||||
for endpoint in (module.BANK_GAME_ENDPOINT, "https://controller.example/api/bank")]
|
||||
originals = {path: path.read_bytes() for path in paths}
|
||||
operation = self.operation("instance.rebuild", {"spec": payload, "nextGeneration": 2, "safetyOverride": True}, "rebuild")
|
||||
applied, calls = {}, []
|
||||
|
||||
def acknowledge(method, path, data):
|
||||
self.assertFalse(self.agent.inspect(INSTANCE, 2)["exists"])
|
||||
self.assertEqual(self.agent.config["controller"], "https://controller.example")
|
||||
for journal, raw in originals.items():
|
||||
self.assertEqual(journal.read_bytes(), raw)
|
||||
transfer = data["transfer"]
|
||||
request_id = transfer["request_id"]
|
||||
calls.append(request_id)
|
||||
if request_id in applied:
|
||||
self.assertEqual(applied[request_id], transfer)
|
||||
applied[request_id] = transfer
|
||||
response = self.bank_acknowledgment(method, path, data)
|
||||
if len(calls) == 1:
|
||||
raise AgentError("Lost reply after settlement")
|
||||
return response
|
||||
|
||||
with patch.object(self.agent, "request", side_effect=acknowledge):
|
||||
self.assertEqual(self.agent.execute(operation)["state"], "unknown")
|
||||
self.agent.journal.close()
|
||||
self.agent = Agent(self.config, self.docker)
|
||||
with patch.object(self.agent, "request", side_effect=acknowledge):
|
||||
self.assertEqual(self.agent.execute(operation)["state"], "failed")
|
||||
retry = self.operation("instance.rebuild", operation["payload"], "replacement")
|
||||
result = self.agent.execute(retry)
|
||||
self.assertEqual(result["state"], "completed", result)
|
||||
# Reconciliation drains once, then the replacement operation drains
|
||||
# again before deleting the unchanged journals. All retries keep IDs.
|
||||
self.assertEqual(len(calls), 17)
|
||||
self.assertEqual(len(applied), 4)
|
||||
self.assertEqual({item["decision"] for item in applied.values()}, {"commit", "cancel"})
|
||||
self.assertTrue(all(not path.exists() for path in paths))
|
||||
self.assertTrue(self.agent.inspect(INSTANCE, 2)["running"])
|
||||
config = self.agent.instance_dir(INSTANCE, 2) / "app/configs/electric_cherry_bank.cfg"
|
||||
self.assertEqual(config.read_text(), f'set tws_bank_token "{secret}"\n')
|
||||
|
||||
def test_remove_drains_legacy_and_public_endpoints_without_following_journal_urls(self):
|
||||
self.create()
|
||||
paths = [self.bank_journal(game=game, endpoint=endpoint)
|
||||
for game in ("t5", "t6")
|
||||
for endpoint in (module.BANK_GAME_ENDPOINT, "https://controller.example/api/bank")]
|
||||
with patch.object(self.agent, "request", side_effect=self.bank_acknowledgment) as request:
|
||||
result = self.agent.execute(self.operation("instance.remove", {"safetyOverride": True}, "remove"))
|
||||
self.assertEqual(result["state"], "completed", result)
|
||||
self.assertEqual(request.call_count, 8)
|
||||
self.assertTrue(all(not path.exists() for path in paths))
|
||||
|
||||
def test_unrecognized_bank_endpoints_retain_storage_without_settlement(self):
|
||||
self.create()
|
||||
path = self.bank_journal()
|
||||
operation = self.operation("instance.remove", {"safetyOverride": True}, "remove")
|
||||
for endpoint in ("https://untrusted.example/api/bank", "http://ec.thewrightserver.net/api/bank",
|
||||
"https://ec.thewrightserver.net.evil/api/bank", module.BANK_GAME_ENDPOINT + "?redirect=evil",
|
||||
"https://controller.example/other/api/bank", "", None):
|
||||
with self.subTest(endpoint=endpoint):
|
||||
journal = json.loads(path.read_text())
|
||||
journal["endpoint"] = endpoint
|
||||
module.atomic_json(path, journal)
|
||||
with patch.object(self.agent, "request") as request:
|
||||
self.assertEqual(self.agent.execute(operation)["state"], "unknown")
|
||||
request.assert_not_called()
|
||||
self.assertTrue(path.exists())
|
||||
|
||||
def test_rebuild_settles_before_starting_replacement_and_retains_unknown_decisions(self):
|
||||
payload = self.create()
|
||||
path = self.bank_journal(("commit",))
|
||||
|
||||
Reference in New Issue
Block a user