Release beta 1.0.5 primogem economy
This commit is contained in:
@@ -179,6 +179,10 @@ class RoutingTests(unittest.TestCase):
|
||||
MODULE.classify_question("/version"),
|
||||
("local", "local_jwebot_version"),
|
||||
)
|
||||
self.assertEqual(
|
||||
MODULE.classify_question("/版本"),
|
||||
("local", "local_jwebot_version"),
|
||||
)
|
||||
|
||||
def test_unknown_slash_command_stays_local(self):
|
||||
self.assertEqual(
|
||||
@@ -884,9 +888,9 @@ class LedgerAndApplicationTests(unittest.TestCase):
|
||||
self.assertEqual(version["route"], "local")
|
||||
self.assertIn("JWeBot 版本", version["text"])
|
||||
self.assertIn("kernel", version["text"])
|
||||
self.assertIn("最新版本:beta 1.0.4.1016", version["text"])
|
||||
self.assertIn("更新时间:2026-10-16", version["text"])
|
||||
self.assertIn("更新摘要:新增版本更新补偿流程", version["text"])
|
||||
self.assertIn("最新版本:beta 1.0.5.1016", version["text"])
|
||||
self.assertIn("更新时间:2026-10-16 16:30:44", version["text"])
|
||||
self.assertIn("更新摘要:新增原石系统", version["text"])
|
||||
self.assertEqual(debug["route"], "local")
|
||||
self.assertIn("调试埋点", debug["text"])
|
||||
self.assertEqual(balance["route"], "local")
|
||||
|
||||
@@ -83,6 +83,7 @@ class LocalExtensionInventoryTests(unittest.TestCase):
|
||||
"context-summary": OFFICIAL_AUTHOR,
|
||||
"person-directory": OFFICIAL_AUTHOR,
|
||||
"single-player-games": JWEBOT_AUTHOR,
|
||||
"game-history": JWEBOT_AUTHOR,
|
||||
"cs-major-autochess": JWEBOT_AUTHOR,
|
||||
"person-memory": OFFICIAL_AUTHOR,
|
||||
"server-status": OFFICIAL_AUTHOR,
|
||||
@@ -93,6 +94,7 @@ class LocalExtensionInventoryTests(unittest.TestCase):
|
||||
"coin-transfer": JWEBOT_AUTHOR,
|
||||
"coin-ranking": JWEBOT_AUTHOR,
|
||||
"coin-taffy": JWEBOT_AUTHOR,
|
||||
"primogem-economy": JWEBOT_AUTHOR,
|
||||
"coin-loan": JWEBOT_AUTHOR,
|
||||
"random-number": JWEBOT_AUTHOR,
|
||||
"draw-lot": JWEBOT_AUTHOR,
|
||||
@@ -120,9 +122,14 @@ class LocalExtensionInventoryTests(unittest.TestCase):
|
||||
"prize_draw",
|
||||
"lottery",
|
||||
"blackjack",
|
||||
"game_history",
|
||||
"game_rules",
|
||||
),
|
||||
"primogem-economy": (
|
||||
"primogem_wish",
|
||||
"primogem_ranking",
|
||||
"scan_boost",
|
||||
"taffy_say",
|
||||
),
|
||||
"person-directory": ("person_directory", "person_info"),
|
||||
"person-memory": (
|
||||
"remember",
|
||||
|
||||
@@ -270,11 +270,11 @@ class Release063GameTests(unittest.TestCase):
|
||||
sender="Bob",
|
||||
event_id="wallet-after-transfer-b",
|
||||
)
|
||||
self.assertIn("alice:当前摩拉 750;无贷款;", alice_wallet)
|
||||
self.assertIn("alice:当前摩拉 750;原石 0 个;无贷款;", alice_wallet)
|
||||
self.assertIn("最近10次账户变动:", alice_wallet)
|
||||
self.assertIn("-250:转账给 bob", alice_wallet)
|
||||
self.assertIn("+1000:首次初始化", alice_wallet)
|
||||
self.assertIn("bob:当前摩拉 1250;无贷款;", bob_wallet)
|
||||
self.assertIn("bob:当前摩拉 1250;原石 0 个;无贷款;", bob_wallet)
|
||||
self.assertIn("+250:转账 来自 alice", bob_wallet)
|
||||
|
||||
def test_transfer_rejects_invalid_self_and_insufficient_cases(self):
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
"""Behavioral coverage for beta 1.0.5 primogem economy."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import tempfile
|
||||
import unittest
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from unittest import mock
|
||||
|
||||
from test_jwebot_edge import MODULE, config
|
||||
|
||||
|
||||
class Release105PrimogemTests(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
|
||||
row = {
|
||||
"question": question,
|
||||
"sender": sender,
|
||||
"group_fingerprint": "group-a",
|
||||
"event_id": event_id or f"primogem-{self.counter}",
|
||||
}
|
||||
return self.engine.local.handle(row, {})
|
||||
|
||||
def state(self, sender="Alice"):
|
||||
key = self.engine.ledger.resolve_person("group-a", sender)["person_key"]
|
||||
row = self.engine.ledger.connection.execute(
|
||||
"""
|
||||
SELECT state_json FROM player_accounts
|
||||
WHERE group_fingerprint = ? AND person_key = ?
|
||||
""",
|
||||
("group-a", key),
|
||||
).fetchone()
|
||||
self.assertIsNotNone(row)
|
||||
return json.loads(row[0])
|
||||
|
||||
@staticmethod
|
||||
def blackjack_deck(*prefix):
|
||||
used = set(prefix)
|
||||
return list(prefix) + [card for card in range(52) if card not in used]
|
||||
|
||||
def test_natural_blackjack_grants_primogem_and_wish_can_award_scan_ticket(self):
|
||||
deck = self.blackjack_deck(0, 12, 1, 2)
|
||||
|
||||
def shuffle(cards):
|
||||
cards[:] = deck
|
||||
|
||||
with mock.patch.object(self.engine.local.players.rng, "shuffle", shuffle):
|
||||
result = self.command("/21点 开始 10")
|
||||
|
||||
self.assertIn("+1 原石", result)
|
||||
self.assertEqual(self.state()["primogems"], 1)
|
||||
|
||||
with mock.patch.object(
|
||||
self.engine.local.players.rng,
|
||||
"randrange",
|
||||
side_effect=[0, 0],
|
||||
):
|
||||
wish = self.command("/原石祈愿")
|
||||
|
||||
self.assertIn("抽到 20 摩拉", wish)
|
||||
self.assertIn("扫码概率券 1 张", wish)
|
||||
state = self.state()
|
||||
self.assertEqual(state["primogems"], 0)
|
||||
self.assertEqual(state["scan_boost_tickets"], 1)
|
||||
|
||||
def test_extreme_blackjack_hit_from_20_to_21_grants_two_primogems(self):
|
||||
prefix = [9, 12, 1, 2]
|
||||
deck = self.blackjack_deck(*prefix, 0)
|
||||
deck = deck[:4] + [card for card in deck[4:] if card != 0] + [0]
|
||||
|
||||
def shuffle(cards):
|
||||
cards[:] = deck
|
||||
|
||||
with mock.patch.object(self.engine.local.players.rng, "shuffle", shuffle):
|
||||
self.command("/21点 开始 10")
|
||||
self.command("/21点 要牌")
|
||||
result = self.command("/21点 停牌")
|
||||
|
||||
self.assertIn("21点极限挑战", result)
|
||||
self.assertIn("+2 原石", result)
|
||||
self.assertEqual(self.state()["primogems"], 2)
|
||||
|
||||
def test_lottery_two_consecutive_personal_draws_with_five_total_hits(self):
|
||||
self.command("/彩票开始")
|
||||
self.command("/彩票34710")
|
||||
with mock.patch.object(
|
||||
self.engine.local.players.rng,
|
||||
"sample",
|
||||
return_value=[3, 7, 9],
|
||||
):
|
||||
self.command("/彩票开奖")
|
||||
|
||||
self.command("/彩票开始", event_id="lottery-start-two")
|
||||
self.command("/彩票34710", event_id="lottery-entry-two")
|
||||
with mock.patch.object(
|
||||
self.engine.local.players.rng,
|
||||
"sample",
|
||||
return_value=[3, 4, 7],
|
||||
):
|
||||
result = self.command("/彩票开奖", event_id="lottery-draw-two")
|
||||
|
||||
self.assertIn("彩票三中挑战", result)
|
||||
self.assertEqual(self.state()["primogems"], 2)
|
||||
|
||||
def test_scan_boost_and_taffy_say_spend_primogem_resources(self):
|
||||
self.command("/原石")
|
||||
account = self.state()
|
||||
account["primogems"] = 2
|
||||
self.engine.ledger._save_player_account(
|
||||
"group-a",
|
||||
self.engine.ledger.resolve_person("group-a", "Alice")["person_key"],
|
||||
account,
|
||||
MODULE.utc_now(),
|
||||
)
|
||||
self.engine.ledger.connection.commit()
|
||||
|
||||
boost = self.command("/扫码加权 Bob")
|
||||
self.assertIn("已给 bob 增加整点扫码权重", boost)
|
||||
boost_targets = self.engine.ledger.active_scan_boost_targets("group-a")
|
||||
bob_key = self.engine.ledger.resolve_person("group-a", "Bob")["person_key"]
|
||||
self.assertIn(bob_key, boost_targets)
|
||||
|
||||
speech = self.command("/Taffy发话 今天也要加班吗")
|
||||
self.assertEqual(speech, "Taffy:今天也要加班吗喵")
|
||||
state = self.state()
|
||||
self.assertEqual(state["primogems"], 0)
|
||||
self.assertEqual(state["taffy_say_credits"], 1)
|
||||
|
||||
def test_scheduled_taffy_speech_uses_clock_delivery_queue(self):
|
||||
self.command("/原石")
|
||||
account = self.state()
|
||||
account["primogems"] = 1
|
||||
person_key = self.engine.ledger.resolve_person("group-a", "Alice")["person_key"]
|
||||
self.engine.ledger._save_player_account(
|
||||
"group-a",
|
||||
person_key,
|
||||
account,
|
||||
MODULE.utc_now(),
|
||||
)
|
||||
self.engine.ledger.connection.commit()
|
||||
scheduled_at = datetime(
|
||||
2026,
|
||||
10,
|
||||
16,
|
||||
8,
|
||||
0,
|
||||
tzinfo=MODULE.BEIJING_TIMEZONE,
|
||||
)
|
||||
|
||||
response = self.engine.ledger.player_taffy_say(
|
||||
"group-a",
|
||||
"Alice",
|
||||
"scheduled-taffy",
|
||||
"taffy_say:test",
|
||||
"准点提醒",
|
||||
scheduled_at.timestamp(),
|
||||
lambda data: self.engine.local.players.render_taffy_say(data, "alice"),
|
||||
)
|
||||
self.assertIn("已安排Taffy", response)
|
||||
|
||||
delivered = []
|
||||
self.engine.deliver = lambda row: delivered.append(row["result_text"])
|
||||
self.assertTrue(
|
||||
self.engine.process_clock_once(
|
||||
now=scheduled_at,
|
||||
group_fingerprint="group-a",
|
||||
)
|
||||
)
|
||||
self.assertEqual(delivered, ["Taffy:准点提醒"])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -20,7 +20,7 @@ SPEC.loader.exec_module(MODULE)
|
||||
|
||||
|
||||
class ReleaseUpdateTests(unittest.TestCase):
|
||||
release = "beta 1.0.4.1016"
|
||||
release = "beta 1.0.5.1016"
|
||||
|
||||
def setUp(self):
|
||||
self.temporary = tempfile.TemporaryDirectory()
|
||||
@@ -111,9 +111,16 @@ class ReleaseUpdateTests(unittest.TestCase):
|
||||
self.assertEqual(summary, "第一行更新摘要, 同一条摘要可以换行。")
|
||||
announcement = MODULE.build_announcement(self.release, summary)
|
||||
self.assertIn(f"{self.release} 更新现已完成", announcement)
|
||||
self.assertIn("更新补偿:摩拉 x200", announcement)
|
||||
self.assertIn("更新补偿:摩拉 x200,原石 x1", announcement)
|
||||
self.assertNotIn("第二条", announcement)
|
||||
|
||||
def test_patch_release_without_policy_does_not_announce_primogem(self):
|
||||
release = "beta 1.0.6.1016"
|
||||
self.assertEqual(MODULE.primogem_compensation_amount(release), 0)
|
||||
announcement = MODULE.build_announcement(release, "普通小版本更新。")
|
||||
self.assertIn("更新补偿:摩拉 x200", announcement)
|
||||
self.assertNotIn("原石 x1", announcement)
|
||||
|
||||
def test_release_update_grants_once_and_announces_once(self):
|
||||
delivered = []
|
||||
|
||||
@@ -148,6 +155,7 @@ class ReleaseUpdateTests(unittest.TestCase):
|
||||
self.assertIn("更新摩拉账户并修复重复回复问题", delivered[0][2])
|
||||
self.assertEqual([state["balance"] for state in self.account_states()], [1200, 250])
|
||||
for state in self.account_states():
|
||||
self.assertEqual(state["primogems"], 1)
|
||||
matches = [
|
||||
event
|
||||
for event in state["coin_events"]
|
||||
@@ -156,6 +164,17 @@ class ReleaseUpdateTests(unittest.TestCase):
|
||||
]
|
||||
self.assertEqual(len(matches), 1)
|
||||
self.assertEqual(matches[0]["created_at"], applied_at.timestamp())
|
||||
primogem_matches = [
|
||||
event
|
||||
for event in state["primogem_events"]
|
||||
if event["delta"] == 1
|
||||
and event["reason"] == f"{self.release} 版本更新补偿"
|
||||
]
|
||||
self.assertEqual(len(primogem_matches), 1)
|
||||
self.assertEqual(
|
||||
primogem_matches[0]["created_at"],
|
||||
applied_at.timestamp(),
|
||||
)
|
||||
self.assertEqual(self.announcement_marker()["state"], "delivered")
|
||||
self.assertEqual(len(list(self.backups.glob("*.sqlite3"))), 1)
|
||||
|
||||
@@ -173,6 +192,7 @@ class ReleaseUpdateTests(unittest.TestCase):
|
||||
deliverer=uncertain,
|
||||
)
|
||||
self.assertEqual([state["balance"] for state in self.account_states()], [1200, 250])
|
||||
self.assertEqual([state["primogems"] for state in self.account_states()], [1, 1])
|
||||
self.assertEqual(self.announcement_marker()["state"], "uncertain")
|
||||
|
||||
called = []
|
||||
@@ -203,6 +223,7 @@ class ReleaseUpdateTests(unittest.TestCase):
|
||||
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([state["primogems"] for state in self.account_states()], [1, 1])
|
||||
self.assertEqual(self.announcement_marker()["state"], "delivered")
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user