Files
JWeBot/tests/test_release_067.py
T
JWeBot Contributors 2bb2effc04
check / local-gate (push) Failing after 58s
Externalize autochess game data
2026-10-15 18:37:41 +08:00

197 lines
6.6 KiB
Python

"""Behavioral coverage for the CS Major autochess 0.6.7 pacing rewrite."""
from __future__ import annotations
import json
import re
import tempfile
import unittest
from importlib import resources
from pathlib import Path
from test_jwebot_edge import MODULE, config
from jwebot_official_extensions import cs_autochess as AUTOCHESS
class Release067AutochessTests(unittest.TestCase):
def setUp(self):
self.temp = tempfile.TemporaryDirectory()
self.engine = MODULE.EdgeApplication(config(Path(self.temp.name)))
self.counter = 0
def tearDown(self):
self.engine.close()
self.temp.cleanup()
def command(self, question, *, sender="Alice", event_id=None):
self.counter += 1
response = self.engine.accept(
{
"event_id": event_id or f"autochess-067-{self.counter}",
"group_fingerprint": "group-a",
"sender": sender,
"text": "@JWeBot " + question,
"mention_bot": True,
"confidence": 1.0,
"sender_confidence": 1.0,
"body_confidence": 1.0,
}
)
self.assertEqual(response["status"], "reply")
return list(response.get("texts") or [response["text"]])
def state(self):
row = self.engine.ledger.connection.execute(
"""
SELECT state_json FROM autochess_runs
WHERE group_fingerprint = 'group-a'
ORDER BY season_number DESC
LIMIT 1
"""
).fetchone()
self.assertIsNotNone(row)
return json.loads(row[0])
def ready_all(self):
state = self.state()
replies = []
for key in list(state.get("ready_keys") or []):
replies.extend(
self.command(
"/结束回合",
sender=state["players"][key]["display_name"],
)
)
return replies
def start_two_human_two_ai(self):
self.command("/自走棋开始")
self.command("/自走棋报名", sender="Bob")
self.command("/自走棋加AI战队")
self.command("/自走棋开赛")
def finish_draft(self):
self.command("/选1")
self.command("/选1", sender="Bob")
self.command("/选1", sender="Bob")
self.command("/选1")
self.command("/选1")
self.command("/选1", sender="Bob")
def test_narrative_inventory_and_training_round_robin(self):
self.assertGreaterEqual(AUTOCHESS._narrative_inventory_size(), 120)
self.start_two_human_two_ai()
self.finish_draft()
training = "\n".join(self.ready_all())
state = self.state()
self.assertEqual(len(state["training_rounds"]), 3)
self.assertEqual(state["training_round_cursor"], 1)
self.assertIn("训练赛第 1/3 轮对阵", training)
self.assertIn("Team", training)
self.assertIn("AI 战队", training)
self.assertNotIn("目标", training)
self.assertNotIn("发挥", training)
def test_round_simulation_produces_natural_score_variety(self):
self.start_two_human_two_ai()
self.finish_draft()
state = self.state()
key_a, key_b = state["seed_order"][:2]
scores = set()
for index in range(12):
result = AUTOCHESS._battle(
state,
key_a,
key_b,
f"score-variety-{index}",
best_of=1,
)
score = result["maps"][0]["score"]
self.assertRegex(score, r"^\d+-\d+$")
self.assertGreaterEqual(result["maps"][0]["rounds_played"], 16)
scores.add(score)
old_fixed = {"16-6", "16-9", "16-12", "16-14", "19-17"}
self.assertGreaterEqual(len(scores), 3)
self.assertTrue(scores - old_fixed)
def test_group_report_is_paced_and_hides_raw_calculation_terms(self):
self.start_two_human_two_ai()
self.finish_draft()
self.ready_all()
self.ready_all()
self.ready_all()
self.ready_all()
report = "\n".join(self.ready_all())
self.assertIn("图1", report)
self.assertIn("系列赛结束", report)
self.assertNotIn("目标", report)
self.assertNotIn("发挥", report)
self.assertNotIn("克制修正", report)
self.assertIsNotNone(re.search(r"\b\d{2}-\d{1,2}\b", report))
def test_legacy_active_run_does_not_block_new_schema_three_season(self):
self.command("/自走棋开始")
state = self.state()
state["schema_version"] = 2
state["status"] = "active"
self.engine.ledger.connection.execute(
"""
UPDATE autochess_runs
SET state = 'active', state_json = ?
WHERE group_fingerprint = 'group-a' AND run_id = ?
""",
(
json.dumps(state, ensure_ascii=False, sort_keys=True),
state["run_id"],
),
)
self.engine.ledger.connection.commit()
response = "\n".join(
self.command("/自走棋开始", event_id="legacy-active-create")
)
new_state = self.state()
self.assertIn("旧版未完成赛事", response)
self.assertEqual(new_state["schema_version"], 3)
self.assertEqual(new_state["season_number"], 2)
self.assertEqual(new_state["status"], "registration")
def test_autochess_data_files_are_externalized_and_loaded(self):
data_root = resources.files(AUTOCHESS.__package__).joinpath(
AUTOCHESS.DATA_ROOT
)
payloads = {}
for filename in AUTOCHESS.DATA_FILES:
path = data_root.joinpath(filename)
self.assertTrue(path.is_file(), filename)
payload = json.loads(path.read_text(encoding="utf-8"))
self.assertEqual(payload["schema_version"], 1)
payloads[filename] = payload
self.assertEqual(
len(payloads["players.json"]["players"]),
len(AUTOCHESS.PLAYER_POOL),
)
self.assertEqual(
len(payloads["coaches.json"]["coaches"]),
len(AUTOCHESS.COACH_POOL),
)
narrative_count = sum(
len(items)
for items in payloads["narratives.json"]["fragments"].values()
)
self.assertEqual(narrative_count, AUTOCHESS._narrative_inventory_size())
self.assertGreaterEqual(narrative_count, 120)
self.assertEqual(AUTOCHESS.MATCH_BEST_OF["group"], 3)
self.assertEqual(AUTOCHESS.MATCH_BEST_OF["final"], 5)
if __name__ == "__main__":
unittest.main()