- Updated Selectel documentation to clarify error responses related to balance issues. - Introduced `peek_balance_rub` function for best-effort balance checks, handling API failures gracefully. - Improved error messages for insufficient funds and quota issues, specifying Selectel's 403 policy response. - Added balance checks in the doctor command to ensure users are informed about their balance status before attempting GPU creation. - Refactored exception handling in cloud operations to provide clearer feedback on balance-related errors. Co-authored-by: Cursor <cursoragent@cursor.com>
73 lines
2.3 KiB
Python
73 lines
2.3 KiB
Python
from gpu_rent.balance import (
|
|
BalanceWatchState,
|
|
balance_rub_from_payload,
|
|
peek_balance_rub,
|
|
plan_balance_notices,
|
|
spend_steps,
|
|
)
|
|
from gpu_rent.errors import CloudError
|
|
|
|
|
|
def test_balance_kopecks():
|
|
payload = {
|
|
"data": {
|
|
"billings": [
|
|
{"balances_values_sum": 123456}, # 1234.56 ₽
|
|
]
|
|
}
|
|
}
|
|
assert balance_rub_from_payload(payload) == 1234.56
|
|
|
|
|
|
def test_spend_steps_200():
|
|
assert spend_steps(1000, 850, 200) == 0
|
|
assert spend_steps(1000, 800, 200) == 1
|
|
assert spend_steps(1000, 600, 200) == 2
|
|
assert spend_steps(1000, 1100, 200) == 0 # top-up direction
|
|
|
|
|
|
def test_plan_notifies_each_step():
|
|
state = BalanceWatchState(baseline_rub=1000.0)
|
|
state, msgs = plan_balance_notices(state, 750.0, step_rub=200.0)
|
|
assert state.last_notified_step == 1
|
|
assert len(msgs) == 1
|
|
assert "200" in msgs[0]
|
|
state, msgs2 = plan_balance_notices(state, 550.0, step_rub=200.0)
|
|
assert state.last_notified_step == 2
|
|
assert len(msgs2) == 1
|
|
# no re-notify same step
|
|
state, msgs3 = plan_balance_notices(state, 520.0, step_rub=200.0)
|
|
assert msgs3 == []
|
|
assert state.last_notified_step == 2
|
|
|
|
|
|
def test_plan_topup_resets():
|
|
state = BalanceWatchState(baseline_rub=500.0, last_notified_step=2)
|
|
state, msgs = plan_balance_notices(state, 900.0, step_rub=200.0)
|
|
assert state.baseline_rub == 900.0
|
|
assert state.last_notified_step == 0
|
|
assert any("пополнен" in m for m in msgs)
|
|
|
|
|
|
def test_plan_low_once():
|
|
state = BalanceWatchState(baseline_rub=1000.0)
|
|
state, msgs = plan_balance_notices(state, 150.0, step_rub=200.0, low_rub=200.0)
|
|
assert state.low_notified
|
|
assert any("низкий" in m for m in msgs)
|
|
state, msgs2 = plan_balance_notices(state, 100.0, step_rub=200.0, low_rub=200.0)
|
|
assert not any("низкий" in m for m in msgs2)
|
|
|
|
|
|
def test_peek_balance_rub_empty_token():
|
|
assert peek_balance_rub("") is None
|
|
assert peek_balance_rub(None) is None
|
|
|
|
|
|
def test_peek_balance_rub_swallows_errors(monkeypatch):
|
|
def boom(*_a, **_k):
|
|
raise CloudError("сеть")
|
|
|
|
monkeypatch.setattr("gpu_rent.balance.fetch_balance_rub", boom)
|
|
assert peek_balance_rub("tok") is None
|
|
|