Skip to content

Commit 07d69c4

Browse files
committed
🧭 feat(admin): add crypto v2 migration status command
Add /crypto_migration_status for admins to track wallet_crypto_v2 rollout progress with pending breakdown, include the command in /help, and cover behavior with router tests.
1 parent c7fefa9 commit 07d69c4

File tree

3 files changed

+126
-0
lines changed

3 files changed

+126
-0
lines changed

β€Žbot/routers/admin.pyβ€Ž

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,51 @@ async def cmd_set_usdt(message: types.Message, session: AsyncSession):
434434
)
435435

436436

437+
@router.message(Command(commands=["crypto_migration_status"]))
438+
async def cmd_crypto_migration_status(message: types.Message, session: AsyncSession):
439+
base_filter = MyMtlWalletBot.need_delete == 0
440+
441+
total_wallets = (
442+
await session.execute(
443+
select(func.count()).select_from(MyMtlWalletBot).filter(base_filter)
444+
)
445+
).scalar() or 0
446+
447+
migrated_wallets = (
448+
await session.execute(
449+
select(func.count())
450+
.select_from(MyMtlWalletBot)
451+
.filter(base_filter, MyMtlWalletBot.wallet_crypto_v2.is_not(None))
452+
)
453+
).scalar() or 0
454+
455+
pending_wallets = max(total_wallets - migrated_wallets, 0)
456+
457+
pending_requires_user_pin = (
458+
await session.execute(
459+
select(func.count())
460+
.select_from(MyMtlWalletBot)
461+
.filter(
462+
base_filter,
463+
MyMtlWalletBot.wallet_crypto_v2.is_(None),
464+
MyMtlWalletBot.use_pin.in_([1, 2]),
465+
)
466+
)
467+
).scalar() or 0
468+
469+
pending_other = max(pending_wallets - pending_requires_user_pin, 0)
470+
progress_pct = (migrated_wallets / total_wallets * 100) if total_wallets else 0.0
471+
472+
await message.answer(
473+
"πŸ” Crypto v2 migration status\n"
474+
f"Total wallets: {total_wallets}\n"
475+
f"Migrated: {migrated_wallets} ({progress_pct:.1f}%)\n"
476+
f"Pending: {pending_wallets}\n"
477+
f"- Pending (requires user PIN/password): {pending_requires_user_pin}\n"
478+
f"- Pending (other): {pending_other}"
479+
)
480+
481+
437482
@router.message(Command(commands=["help"]))
438483
async def cmd_help(message: types.Message):
439484
await message.answer(
@@ -448,6 +493,7 @@ async def cmd_help(message: types.Message):
448493
"/usdt1 β€” Π°Π²Ρ‚ΠΎΠ²Ρ‹Π²ΠΎΠ΄ ΠΏΠ΅Ρ€Π²ΠΎΠ³ΠΎ Π² ΠΎΡ‡Π΅Ρ€Π΅Π΄ΠΈ\n"
449494
"/check_usdt @user β€” свСрка баланса Π‘Π” ΠΈ Π±Π»ΠΎΠΊΡ‡Π΅ΠΉΠ½Π°\n"
450495
"/set_usdt @user amount β€” установка баланса Π‘Π”\n"
496+
"/crypto_migration_status β€” прогрСсс ΠΌΠΈΠ³Ρ€Π°Ρ†ΠΈΠΈ wallet_crypto_v2\n"
451497
"/balance β€” ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΈΡ‚ΡŒ баланс"
452498
)
453499

β€Žbot/tests/routers/test_admin.pyβ€Ž

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,48 @@ async def test_cmd_stats(mock_telegram, router_app_context, setup_admin_mocks):
119119
assert "op1: 5" in req["data"]["text"]
120120

121121

122+
@pytest.mark.asyncio
123+
async def test_cmd_crypto_migration_status(
124+
mock_telegram, router_app_context, setup_admin_mocks
125+
):
126+
"""Test /crypto_migration_status: should show migration counters."""
127+
dp = router_app_context.dispatcher
128+
dp.include_router(admin_router)
129+
130+
def _result_with_scalar(value):
131+
result = MagicMock()
132+
result.scalar.return_value = value
133+
return result
134+
135+
setup_admin_mocks.mock_session.execute = AsyncMock(
136+
side_effect=[
137+
_result_with_scalar(100), # total
138+
_result_with_scalar(80), # migrated
139+
_result_with_scalar(15), # pending requires user pin/password
140+
]
141+
)
142+
143+
update = create_message_update(
144+
user_id=123,
145+
text="/crypto_migration_status",
146+
username="itolstov",
147+
)
148+
await dp.feed_update(
149+
bot=router_app_context.bot,
150+
update=update,
151+
app_context=router_app_context,
152+
)
153+
154+
req = get_telegram_request(mock_telegram, "sendMessage")
155+
text = req["data"]["text"]
156+
assert "Crypto v2 migration status" in text
157+
assert "Total wallets: 100" in text
158+
assert "Migrated: 80 (80.0%)" in text
159+
assert "Pending: 20" in text
160+
assert "Pending (requires user PIN/password): 15" in text
161+
assert "Pending (other): 5" in text
162+
163+
122164
@pytest.mark.asyncio
123165
async def test_cmd_exit_restart_flow(
124166
mock_telegram, router_app_context, setup_admin_mocks
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# admin-crypto-migration-status: Admin command for crypto v2 migration progress
2+
3+
## Context
4+
5+
Need an admin-visible command to show how many wallets are not yet migrated to
6+
`wallet_crypto_v2`, with a compact breakdown useful during rollout.
7+
8+
## Files/Directories To Change
9+
10+
- `bot/routers/admin.py`
11+
- `bot/tests/routers/test_admin.py`
12+
- `docs/exec-plans/active/2026-03-01-admin-crypto-migration-status.md`
13+
14+
## Edit Permission
15+
16+
- [x] Allowed paths confirmed by user.
17+
- [x] No edits outside listed paths.
18+
19+
Permission evidence (copy user wording or exact confirmation):
20+
21+
> "новая Π·Π°Π΄Π°Ρ‡Π° пиши Π½ΠΎΠ²Ρ‹ΠΉ ΠΏΠ»Π°Π½. Π² Π°Π΄ΠΌΠΈΠ½ Ρ€Π΅ΠΆΠΈΠΌ Ρ…ΠΎΡ‡Ρƒ ΠΊΠΎΠΌΠ΅Π½Π΄Π° которая ΠΏΠΎΠΊΠ°ΠΆΠ΅Ρ‚ сколкьо Π΅Ρ‰Π΅ Π½Π΅ ΠΏΠ΅Ρ€Π΅ΡˆΠ»ΠΎ Π½Π° Π½ΠΎΠ²Ρ‹ΠΉ сСкрСт" and "ΠΎΠΊ ΠΈ Π΅Ρ‰Π΅ Π² Ρ…Π΅Π»ΠΏ Π΄ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ Π΅Π΅ Ρ‡Ρ‚ΠΎΠ± я Π½Π΅ искал"
22+
23+
## Change Plan
24+
25+
1. [x] Add `/crypto_migration_status` command in admin router.
26+
2. [x] Include new command in `/help` output.
27+
3. [x] Add router test that validates command output structure.
28+
4. [x] Run targeted admin tests and `just check-fast`.
29+
30+
## Risks / Open Questions
31+
32+
- Keep query logic simple and deterministic for testability.
33+
- Exclude `need_delete=1` wallets from totals to match migration script behavior.
34+
35+
## Verification
36+
37+
- `cd bot && uv run --package mmwb-bot pytest tests/routers/test_admin.py`
38+
- `just check-fast`

0 commit comments

Comments
Β (0)