Release beta 1.0.4 update settlement
check / local-gate (push) Failing after 1m7s

This commit is contained in:
JWeBot Contributors
2026-10-16 16:10:19 +08:00
parent bf6b738baa
commit 127fea8cd0
24 changed files with 954 additions and 28 deletions
+210
View File
@@ -0,0 +1,210 @@
#!/usr/bin/env python3
import importlib.util
import json
import sqlite3
import sys
import tempfile
import unittest
from datetime import datetime, timezone
from pathlib import Path
PROJECT_ROOT = Path(__file__).resolve().parents[1]
MODULE_PATH = PROJECT_ROOT / "jwebot_release_update.py"
SPEC = importlib.util.spec_from_file_location("jwebot_release_update", MODULE_PATH)
MODULE = importlib.util.module_from_spec(SPEC)
assert SPEC.loader is not None
sys.modules[SPEC.name] = MODULE
SPEC.loader.exec_module(MODULE)
class ReleaseUpdateTests(unittest.TestCase):
release = "beta 1.0.4.1016"
def setUp(self):
self.temporary = tempfile.TemporaryDirectory()
self.root = Path(self.temporary.name)
self.database = self.root / "edge.sqlite3"
self.backups = self.root / "backups"
self.version = self.root / "VERSION"
self.changelog = self.root / "CHANGELOG.md"
self.delivery_helper = self.root / "jwebot_edge_delivery.py"
self.version.write_text(self.release + "\n", encoding="utf-8")
self.changelog.write_text(
(
"# Changelog\n\n"
f"## {self.release} - 2026-10-16\n\n"
"- 更新摩拉账户并修复重复回复问题。\n"
"- Internal implementation detail.\n"
),
encoding="utf-8",
)
self.delivery_helper.write_text("# test helper\n", encoding="utf-8")
connection = sqlite3.connect(self.database)
connection.executescript(
"""
CREATE TABLE metadata (
key TEXT PRIMARY KEY,
value TEXT NOT NULL,
updated_at TEXT NOT NULL
);
CREATE TABLE player_accounts (
group_fingerprint TEXT NOT NULL,
person_key TEXT NOT NULL,
state_json TEXT NOT NULL,
updated_at TEXT NOT NULL,
PRIMARY KEY (group_fingerprint, person_key)
);
"""
)
for person_key, balance in (("person-a", 1000), ("person-b", 50)):
connection.execute(
"INSERT INTO player_accounts VALUES (?, ?, ?, ?)",
(
"group-a",
person_key,
json.dumps({"balance": balance}),
"2026-10-16T00:00:00Z",
),
)
connection.commit()
connection.close()
def tearDown(self):
self.temporary.cleanup()
def account_states(self):
connection = sqlite3.connect(self.database)
try:
rows = connection.execute(
"SELECT state_json FROM player_accounts ORDER BY person_key"
).fetchall()
finally:
connection.close()
return [json.loads(row[0]) for row in rows]
def announcement_marker(self):
key = MODULE.announcement_key(self.release, "group-a")
connection = sqlite3.connect(self.database)
try:
row = connection.execute(
"SELECT value FROM metadata WHERE key = ?",
(key,),
).fetchone()
finally:
connection.close()
return json.loads(row[0]) if row else None
def test_changelog_first_bullet_is_the_announcement_summary(self):
self.changelog.write_text(
(
"# Changelog\n\n"
f"## {self.release} - 2026-10-16\n\n"
"- 第一行更新摘要,\n"
" 同一条摘要可以换行。\n"
"- 第二条不进入群公告。\n"
),
encoding="utf-8",
)
summary = MODULE.read_changelog_summary(self.changelog, self.release)
self.assertEqual(summary, "第一行更新摘要, 同一条摘要可以换行。")
announcement = MODULE.build_announcement(self.release, summary)
self.assertIn(f"{self.release} 更新现已完成", announcement)
self.assertIn("更新补偿:摩拉 x200", announcement)
self.assertNotIn("第二条", announcement)
def test_release_update_grants_once_and_announces_once(self):
delivered = []
def deliverer(path, group, announcement, event_id):
delivered.append((path, group, announcement, event_id))
applied_at = datetime(2026, 10, 16, 8, 0, tzinfo=timezone.utc)
first = MODULE.run_release_update(
release_file=self.version,
changelog=self.changelog,
database_path=self.database,
backup_dir=self.backups,
delivery_helper=self.delivery_helper,
deliverer=deliverer,
applied_at=applied_at,
)
second = MODULE.run_release_update(
release_file=self.version,
changelog=self.changelog,
database_path=self.database,
backup_dir=self.backups,
delivery_helper=self.delivery_helper,
deliverer=deliverer,
applied_at=applied_at,
)
self.assertEqual(first["compensation"], "applied")
self.assertEqual(second["compensation"], "already_applied")
self.assertEqual(first["accounts"], 2)
self.assertEqual(len(delivered), 1)
self.assertEqual(delivered[0][1], "group-a")
self.assertIn("更新摩拉账户并修复重复回复问题", delivered[0][2])
self.assertEqual([state["balance"] for state in self.account_states()], [1200, 250])
for state in self.account_states():
matches = [
event
for event in state["coin_events"]
if event["delta"] == 200
and event["reason"] == f"{self.release} 版本更新补偿"
]
self.assertEqual(len(matches), 1)
self.assertEqual(matches[0]["created_at"], applied_at.timestamp())
self.assertEqual(self.announcement_marker()["state"], "delivered")
self.assertEqual(len(list(self.backups.glob("*.sqlite3"))), 1)
def test_uncertain_announcement_never_duplicates_compensation(self):
def uncertain(*_args):
raise MODULE.AnnouncementUncertainError("uncertain")
with self.assertRaises(MODULE.AnnouncementUncertainError):
MODULE.run_release_update(
release_file=self.version,
changelog=self.changelog,
database_path=self.database,
backup_dir=self.backups,
delivery_helper=self.delivery_helper,
deliverer=uncertain,
)
self.assertEqual([state["balance"] for state in self.account_states()], [1200, 250])
self.assertEqual(self.announcement_marker()["state"], "uncertain")
called = []
def delivered(*args):
called.append(args)
with self.assertRaises(MODULE.AnnouncementUncertainError):
MODULE.run_release_update(
release_file=self.version,
changelog=self.changelog,
database_path=self.database,
backup_dir=self.backups,
delivery_helper=self.delivery_helper,
deliverer=delivered,
)
self.assertFalse(called)
result = MODULE.run_release_update(
release_file=self.version,
changelog=self.changelog,
database_path=self.database,
backup_dir=self.backups,
delivery_helper=self.delivery_helper,
deliverer=delivered,
retry_uncertain=True,
)
self.assertEqual(result["compensation"], "already_applied")
self.assertEqual(len(called), 1)
self.assertEqual([state["balance"] for state in self.account_states()], [1200, 250])
self.assertEqual(self.announcement_marker()["state"], "delivered")
if __name__ == "__main__":
unittest.main()