Release beta 1.0.3 Mora wallet updates
This commit is contained in:
@@ -677,6 +677,23 @@ class LedgerAndApplicationTests(unittest.TestCase):
|
||||
self.application.deepseek.complete.assert_called_once()
|
||||
self.application.deliver.assert_called_once()
|
||||
|
||||
def test_semantic_duplicate_with_new_event_id_is_ignored(self):
|
||||
first = self.application.accept(
|
||||
{**event("event-1", "@JWeBot /金币"), "source": "rapidocr-local"}
|
||||
)
|
||||
duplicate = self.application.accept(
|
||||
{**event("event-2", "@JWeBot /金币"), "source": "rapidocr-local"}
|
||||
)
|
||||
|
||||
self.assertEqual(first["status"], "reply")
|
||||
self.assertEqual(duplicate["status"], "ignored")
|
||||
self.assertTrue(duplicate["duplicate"])
|
||||
with self.application.ledger.lock:
|
||||
count = self.application.ledger.connection.execute(
|
||||
"SELECT COUNT(*) FROM events"
|
||||
).fetchone()[0]
|
||||
self.assertEqual(count, 1)
|
||||
|
||||
def test_failed_fast_ack_never_delivers_the_generated_reply(self):
|
||||
self.application.deepseek.complete = mock.Mock(return_value="最终回复喵")
|
||||
self.application.deliver = mock.Mock()
|
||||
@@ -867,9 +884,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.2.1016", version["text"])
|
||||
self.assertIn("最新版本:beta 1.0.3.1016", version["text"])
|
||||
self.assertIn("更新时间:2026-10-16", version["text"])
|
||||
self.assertIn("更新摘要:Cache OCR-negative", version["text"])
|
||||
self.assertIn("更新摘要:Rename the virtual currency", version["text"])
|
||||
self.assertEqual(debug["route"], "local")
|
||||
self.assertIn("调试埋点", debug["text"])
|
||||
self.assertEqual(balance["route"], "local")
|
||||
|
||||
@@ -39,7 +39,7 @@ class Release061Tests(unittest.TestCase):
|
||||
return json.loads(row[0])
|
||||
|
||||
def test_aliases_are_corresponding_and_case_insensitive(self):
|
||||
pairs = [("上下文", "Context"), ("金币", "wallet"), ("抽奖", "draw"),
|
||||
pairs = [("上下文", "Context"), ("摩拉", "wallet"), ("抽奖", "draw"),
|
||||
("买彩票", "lottery"), ("21点", "blackjack"),
|
||||
("人物列表", "people"), ("人物", "person"), ("游戏帮助", "games")]
|
||||
for chinese, english in pairs:
|
||||
@@ -82,18 +82,18 @@ class Release061Tests(unittest.TestCase):
|
||||
self.assertEqual(ledger.search_repeat_history("group-a", "好"), [])
|
||||
|
||||
def test_initial_funds_aliases_groups_and_replay(self):
|
||||
self.assertIn("金币 1000", self.command("/wallet", event_id="wallet-1"))
|
||||
self.assertIn("摩拉 1000", self.command("/wallet", event_id="wallet-1"))
|
||||
with mock.patch.object(self.local.players.rng, "randrange", return_value=99):
|
||||
first = self.command("/draw 10", event_id="draw-1")
|
||||
replay = self.command("/draw 10", sender="a1", event_id="draw-1")
|
||||
self.assertEqual(first, replay)
|
||||
self.assertEqual(self.state("a1")["balance"], 990)
|
||||
self.assertIn("金币 1000", self.command("/wallet", sender="Bob"))
|
||||
self.assertIn("金币 1000", self.command("/wallet", group="group-b"))
|
||||
self.assertIn("摩拉 1000", self.command("/wallet", sender="Bob"))
|
||||
self.assertIn("摩拉 1000", self.command("/wallet", group="group-b"))
|
||||
self.engine.ledger.connection.close()
|
||||
self.engine = MODULE.EdgeApplication(config(self.root))
|
||||
self.local = self.engine.local
|
||||
self.assertIn("金币 990", self.command("/wallet", event_id="restart"))
|
||||
self.assertIn("摩拉 990", self.command("/wallet", event_id="restart"))
|
||||
|
||||
def test_concurrent_replay_debits_once_and_rolls_back_failure(self):
|
||||
replies = []
|
||||
@@ -123,7 +123,7 @@ class Release061Tests(unittest.TestCase):
|
||||
with mock.patch.object(self.local.players.rng, "randrange", return_value=99):
|
||||
for _ in range(10):
|
||||
self.command("/draw 100")
|
||||
self.assertIn("金币不足", self.command("/draw 2"))
|
||||
self.assertIn("摩拉不足", self.command("/draw 2"))
|
||||
self.assertEqual(self.state()["balance"], 0)
|
||||
|
||||
def test_blackjack_is_private_persistent_and_settles_exactly_once(self):
|
||||
|
||||
@@ -119,8 +119,8 @@ class Release062BlackjackTests(unittest.TestCase):
|
||||
self.assertIn("分两步", reply)
|
||||
self.assertIn("100 是本局下注", reply)
|
||||
self.assertIn("再单独发送", reply)
|
||||
wallet = self.command("/金币")
|
||||
self.assertIn("金币 1000", wallet)
|
||||
wallet = self.command("/摩拉")
|
||||
self.assertIn("摩拉 1000", wallet)
|
||||
self.assertIn("无进行中的牌局", wallet)
|
||||
|
||||
|
||||
|
||||
+44
-23
@@ -215,9 +215,9 @@ class Release063GameTests(unittest.TestCase):
|
||||
settled = self.command("/21点 要牌", sender="Bob")
|
||||
self.assertIn("这一桌每个人的变化", settled)
|
||||
self.assertIn("alice #1:你赢了", settled)
|
||||
self.assertIn("下注 200,返还 400,净变化 +200;金币 1200", settled)
|
||||
self.assertIn("下注 200,返还 400,净变化 +200;摩拉 1200", settled)
|
||||
self.assertIn("bob #1:你输了", settled)
|
||||
self.assertIn("下注 100,返还 0,净变化 -100;金币 900", settled)
|
||||
self.assertIn("下注 100,返还 0,净变化 -100;摩拉 900", settled)
|
||||
self.assertEqual(self.state()["balance"], 1200)
|
||||
self.assertEqual(self.state("Bob")["balance"], 900)
|
||||
self.assertIn("alice #1:21点 你赢了", self.command("/游戏记录"))
|
||||
@@ -225,6 +225,11 @@ class Release063GameTests(unittest.TestCase):
|
||||
"bob #1:21点 你输了",
|
||||
self.command("/游戏记录", sender="Bob"),
|
||||
)
|
||||
self.assertIn("+200:21点 获胜", self.command("/金币"))
|
||||
self.assertIn(
|
||||
"-100:21点 失败",
|
||||
self.command("/金币", sender="Bob"),
|
||||
)
|
||||
|
||||
def test_prize_draw_has_indexed_history(self):
|
||||
with mock.patch.object(
|
||||
@@ -242,12 +247,12 @@ class Release063GameTests(unittest.TestCase):
|
||||
self.assertIn("alice #1:抽奖 未中奖", history)
|
||||
|
||||
def test_transfer_requires_initialized_target_and_replays_once(self):
|
||||
self.assertIn("金币 1000", self.command("/金币"))
|
||||
self.assertIn("摩拉 1000", self.command("/摩拉"))
|
||||
missing = self.command("/转账 Bob 100", event_id="missing-target")
|
||||
self.assertIn("bob 还没有金币账户", missing)
|
||||
self.assertIn("bob 还没有摩拉账户", missing)
|
||||
self.assertEqual(self.state()["balance"], 1000)
|
||||
|
||||
self.assertIn("金币 1000", self.command("/金币", sender="Bob"))
|
||||
self.assertIn("摩拉 1000", self.command("/金币", sender="Bob"))
|
||||
first = self.command("/转账 Bob 250", event_id="transfer-1")
|
||||
replay = self.command(
|
||||
"/转账 Bob 250",
|
||||
@@ -256,40 +261,56 @@ class Release063GameTests(unittest.TestCase):
|
||||
)
|
||||
|
||||
self.assertEqual(replay, first)
|
||||
self.assertIn("alice:已向 bob 转账 250 金币", first)
|
||||
self.assertIn("alice:已向 bob 转账 250 摩拉", first)
|
||||
self.assertEqual(self.state()["balance"], 750)
|
||||
self.assertEqual(self.state("Bob")["balance"], 1250)
|
||||
alice_wallet = self.command("/金币", event_id="wallet-after-transfer-a")
|
||||
bob_wallet = self.command(
|
||||
"/金币",
|
||||
sender="Bob",
|
||||
event_id="wallet-after-transfer-b",
|
||||
)
|
||||
self.assertIn("alice:当前摩拉 750;无贷款;", 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("+250:转账 来自 alice", bob_wallet)
|
||||
|
||||
def test_transfer_rejects_invalid_self_and_insufficient_cases(self):
|
||||
self.assertIn("金币 1000", self.command("/金币"))
|
||||
self.assertIn("摩拉 1000", self.command("/金币"))
|
||||
self.assertIn("转账金额必须为 1~100000", self.command("/转账 Bob 0"))
|
||||
self.assertIn("不能给自己转账", self.command("/转账 a1 100"))
|
||||
self.assertIn("金币 1000", self.command("/金币", sender="Bob"))
|
||||
self.assertIn("金币不足", self.command("/transfer 1001 Bob"))
|
||||
self.assertIn("摩拉 1000", self.command("/金币", sender="Bob"))
|
||||
self.assertIn("摩拉不足", self.command("/transfer 1001 Bob"))
|
||||
self.assertEqual(self.state()["balance"], 1000)
|
||||
self.assertEqual(self.state("Bob")["balance"], 1000)
|
||||
|
||||
def test_loan_repay_and_replay_once(self):
|
||||
self.assertIn("金币 1000", self.command("/金币"))
|
||||
self.assertIn("摩拉 1000", self.command("/金币"))
|
||||
first = self.command("/贷款 300", event_id="loan-1")
|
||||
replay = self.command("/贷款 300", event_id="loan-1")
|
||||
|
||||
self.assertEqual(replay, first)
|
||||
self.assertIn("贷款 300 金币已到账", first)
|
||||
self.assertIn("贷款 300 摩拉已到账", first)
|
||||
self.assertEqual(self.state()["balance"], 1300)
|
||||
self.assertEqual(self.state()["loan"]["principal"], 300)
|
||||
self.assertIn("未还贷款 300", self.command("/金币"))
|
||||
self.assertIn("已有未还贷款 300", self.command("/贷款 100"))
|
||||
|
||||
partial = self.command("/还款 120")
|
||||
self.assertIn("已还款 120 金币", partial)
|
||||
self.assertIn("已还款 120 摩拉", partial)
|
||||
self.assertEqual(self.state()["balance"], 1180)
|
||||
self.assertEqual(self.state()["loan"]["principal"], 180)
|
||||
|
||||
done = self.command("/还款 全部")
|
||||
self.assertIn("已还清贷款 180 金币", done)
|
||||
self.assertIn("已还清贷款 180 摩拉", done)
|
||||
self.assertEqual(self.state()["balance"], 1000)
|
||||
self.assertIsNone(self.state()["loan"])
|
||||
wallet = self.command("/金币", event_id="wallet-after-repay")
|
||||
self.assertIn("+300:贷款到账", wallet)
|
||||
self.assertIn("-120:还款", wallet)
|
||||
self.assertIn("-180:还款", wallet)
|
||||
|
||||
def test_loan_rejects_invalid_amounts_and_empty_repay(self):
|
||||
self.assertIn("贷款金额必须为 1~500", self.command("/贷款 0"))
|
||||
@@ -297,20 +318,20 @@ class Release063GameTests(unittest.TestCase):
|
||||
self.assertIn("当前没有未还贷款", self.command("/还款 全部"))
|
||||
|
||||
def test_coin_ranking_lists_accounts_by_balance(self):
|
||||
self.assertIn("金币 1000", self.command("/金币"))
|
||||
self.assertIn("金币 1000", self.command("/金币", sender="Bob"))
|
||||
self.assertIn("金币 1000", self.command("/金币", sender="Carol"))
|
||||
self.assertIn("摩拉 1000", self.command("/金币"))
|
||||
self.assertIn("摩拉 1000", self.command("/金币", sender="Bob"))
|
||||
self.assertIn("摩拉 1000", self.command("/金币", sender="Carol"))
|
||||
self.command("/转账 Bob 250")
|
||||
self.command("/贷款 100", sender="Carol")
|
||||
|
||||
ranking = self.command("/大富翁榜")
|
||||
|
||||
self.assertIn("大富翁榜:", ranking)
|
||||
self.assertIn("1. bob 1250 金币", ranking)
|
||||
self.assertIn("2. carol 1100 金币(未还贷款 100)", ranking)
|
||||
self.assertIn("3. alice 750 金币", ranking)
|
||||
self.assertIn("1. bob 1250 摩拉", ranking)
|
||||
self.assertIn("2. carol 1100 摩拉(未还贷款 100)", ranking)
|
||||
self.assertIn("3. alice 750 摩拉", ranking)
|
||||
self.assertNotIn("Dave", ranking)
|
||||
self.assertIn("金币排行人数必须为 1~20", self.command("/金币排行 0"))
|
||||
self.assertIn("摩拉排行人数必须为 1~20", self.command("/摩拉排行 0"))
|
||||
|
||||
def test_taffy_coin_stats_sums_all_game_history(self):
|
||||
with mock.patch.object(
|
||||
@@ -326,14 +347,14 @@ class Release063GameTests(unittest.TestCase):
|
||||
):
|
||||
self.command("/抽奖10", sender="Bob")
|
||||
|
||||
stats = self.command("/Taffy金币")
|
||||
stats = self.command("/Taffy摩拉")
|
||||
|
||||
self.assertIn("玩家总下注 20,总返还 50", stats)
|
||||
self.assertIn("Taffy累计倒贴 30 金币", stats)
|
||||
self.assertIn("Taffy累计倒贴 30 摩拉", stats)
|
||||
self.assertIn("最大上贡者排行(含负数):", stats)
|
||||
self.assertIn("alice:上贡 10", stats)
|
||||
self.assertIn("bob:从Taffy赢走 40", stats)
|
||||
self.assertIn("Taffy金币人数必须为 1~20", self.command("/Taffy金币 0"))
|
||||
self.assertIn("Taffy摩拉人数必须为 1~20", self.command("/Taffy摩拉 0"))
|
||||
|
||||
def test_open_round_and_history_survive_restart(self):
|
||||
root = Path(self.temp.name)
|
||||
|
||||
@@ -423,6 +423,31 @@ class LayoutScalingTests(unittest.TestCase):
|
||||
fast_ack_sender.assert_called_once()
|
||||
legacy.assert_not_called()
|
||||
|
||||
def test_ignored_duplicate_does_not_fall_back(self):
|
||||
legacy = mock.Mock(return_value="legacy")
|
||||
response = {
|
||||
"status": "ignored",
|
||||
"route": "local",
|
||||
"execution_path": "local_command",
|
||||
"duplicate": True,
|
||||
}
|
||||
with mock.patch.object(
|
||||
MODULE.jwebot_edge,
|
||||
"request_edge",
|
||||
return_value=response,
|
||||
):
|
||||
reply = MODULE.edge_router_reply(
|
||||
{
|
||||
"event_id": "event-2",
|
||||
"group_fingerprint": "group-a",
|
||||
"text": "@JWeBot /金币",
|
||||
},
|
||||
legacy_router=legacy,
|
||||
)
|
||||
|
||||
self.assertEqual(reply, "NO_REPLY")
|
||||
legacy.assert_not_called()
|
||||
|
||||
def test_hash_command_uses_legacy_router_when_edge_is_unavailable(self):
|
||||
legacy = mock.Mock(return_value="legacy")
|
||||
with mock.patch.object(
|
||||
|
||||
Reference in New Issue
Block a user