fix: separate HWID device renewal flows
Keep one-off device top-ups scoped to the active subscription term and move device renewal into subscription checkout. Carry HWID renewal metadata through provider callbacks and webhooks, including YooKassa saved-card flows. Add admin extension controls, docs, demo data, and regression coverage.
This commit is contained in:
@@ -185,6 +185,97 @@ class HwidTariffSwitchConversionTests(unittest.IsolatedAsyncioTestCase):
|
||||
self.assertEqual(change_payload["converted_hwid_value_rub"], 50)
|
||||
self.assertEqual(change_payload["converted_hwid_days"], 7)
|
||||
|
||||
async def test_paid_switch_records_payment_id_in_single_tariff_change(self):
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
settings = _settings(tmpdir)
|
||||
service = _service(settings)
|
||||
user = SimpleNamespace(
|
||||
user_id=42,
|
||||
telegram_id=42,
|
||||
panel_user_uuid="panel-user",
|
||||
email=None,
|
||||
username="u",
|
||||
first_name="U",
|
||||
last_name="L",
|
||||
)
|
||||
sub = SimpleNamespace(
|
||||
subscription_id=11,
|
||||
user_id=42,
|
||||
panel_user_uuid="panel-user",
|
||||
panel_subscription_uuid="panel-sub",
|
||||
tariff_key="basic",
|
||||
start_date=datetime(2099, 1, 1, tzinfo=timezone.utc),
|
||||
end_date=datetime(2099, 2, 1, tzinfo=timezone.utc),
|
||||
effective_monthly_price_rub=100,
|
||||
premium_topup_balance_bytes=0,
|
||||
premium_topup_used_bytes=0,
|
||||
premium_used_bytes=0,
|
||||
topup_balance_bytes=0,
|
||||
regular_bonus_bytes=0,
|
||||
regular_unlimited_override=False,
|
||||
traffic_used_bytes=0,
|
||||
extra_hwid_devices=0,
|
||||
hwid_device_limit=3,
|
||||
)
|
||||
updated = SimpleNamespace(**{**sub.__dict__, "tariff_key": "pro"})
|
||||
updated.hwid_device_limit = 5
|
||||
updated.extra_hwid_devices = 0
|
||||
updated.traffic_limit_bytes = 200 * (1024**3)
|
||||
updated.premium_is_limited = False
|
||||
updated.effective_monthly_price_rub = 200
|
||||
|
||||
with (
|
||||
patch(
|
||||
"bot.services.subscription_service_impl.lifecycle.user_dal.get_user_by_id",
|
||||
AsyncMock(return_value=user),
|
||||
),
|
||||
patch(
|
||||
"bot.services.subscription_service_impl.lifecycle.subscription_dal.get_active_subscription_by_user_id",
|
||||
AsyncMock(return_value=sub),
|
||||
),
|
||||
patch.object(
|
||||
service,
|
||||
"calculate_tariff_switch_options_with_hwid",
|
||||
AsyncMock(
|
||||
return_value={
|
||||
"mode": "period_to_period",
|
||||
"remaining_days": 20,
|
||||
"recalc_days": 20,
|
||||
"paid_diff_rub": 50,
|
||||
"target_monthly_rub": 200,
|
||||
"converted_hwid_value_rub": 0,
|
||||
"converted_hwid_days": 0,
|
||||
"convertible_hwid_purchase_ids": [],
|
||||
}
|
||||
),
|
||||
),
|
||||
patch(
|
||||
"bot.services.subscription_service_impl.lifecycle.tariff_dal.sum_active_hwid_devices",
|
||||
AsyncMock(return_value=0),
|
||||
),
|
||||
patch(
|
||||
"bot.services.subscription_service_impl.lifecycle.subscription_dal.update_subscription",
|
||||
AsyncMock(return_value=updated),
|
||||
),
|
||||
patch(
|
||||
"bot.services.subscription_service_impl.lifecycle.tariff_dal.create_tariff_change",
|
||||
AsyncMock(),
|
||||
) as create_change,
|
||||
):
|
||||
result = await service.switch_tariff_without_payment(
|
||||
AsyncMock(),
|
||||
user_id=42,
|
||||
target_tariff_key="pro",
|
||||
mode="paid_diff",
|
||||
payment_id=99,
|
||||
)
|
||||
|
||||
self.assertEqual(result["tariff_key"], "pro")
|
||||
create_change.assert_awaited_once()
|
||||
change_payload = create_change.await_args.args[1]
|
||||
self.assertEqual(change_payload["payment_id"], 99)
|
||||
self.assertEqual(change_payload["mode"], "paid_diff")
|
||||
|
||||
|
||||
if __name__ == "__main__": # pragma: no cover
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user