first init
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
from pathlib import Path
|
||||
|
||||
from vpn_bot.db import Database
|
||||
|
||||
|
||||
def test_invite_is_stored_before_activation_and_can_be_loaded(tmp_path: Path):
|
||||
db = Database(tmp_path / "bot.sqlite3")
|
||||
db.init()
|
||||
|
||||
db.create_invite(token="abc", admin_id=42, created_at=1000)
|
||||
user = db.get_by_token("abc")
|
||||
|
||||
assert user is not None
|
||||
assert user["invite_token"] == "abc"
|
||||
assert user["invite_created_by_admin_id"] == 42
|
||||
assert user["tg_user_id"] is None
|
||||
|
||||
|
||||
def test_activation_stores_telegram_identity_and_marzban_username(tmp_path: Path):
|
||||
db = Database(tmp_path / "bot.sqlite3")
|
||||
db.init()
|
||||
db.create_invite(token="abc", admin_id=42, created_at=1000)
|
||||
|
||||
db.activate_invite(
|
||||
token="abc",
|
||||
tg_user_id=856850518,
|
||||
tg_username="krosh",
|
||||
tg_first_name="Dmitry",
|
||||
tg_last_name="Gudov",
|
||||
tg_phone=None,
|
||||
marzban_username="tg856850518",
|
||||
activated_at=1100,
|
||||
)
|
||||
|
||||
user = db.get_by_tg_user_id(856850518)
|
||||
assert user is not None
|
||||
assert user["invite_token"] == "abc"
|
||||
assert user["marzban_username"] == "tg856850518"
|
||||
assert user["tg_first_name"] == "Dmitry"
|
||||
|
||||
|
||||
def test_used_invite_cannot_be_activated_twice(tmp_path: Path):
|
||||
db = Database(tmp_path / "bot.sqlite3")
|
||||
db.init()
|
||||
db.create_invite(token="abc", admin_id=42, created_at=1000)
|
||||
db.activate_invite(
|
||||
token="abc",
|
||||
tg_user_id=1,
|
||||
tg_username=None,
|
||||
tg_first_name="One",
|
||||
tg_last_name=None,
|
||||
tg_phone=None,
|
||||
marzban_username="tg1",
|
||||
activated_at=1100,
|
||||
)
|
||||
|
||||
try:
|
||||
db.activate_invite(
|
||||
token="abc",
|
||||
tg_user_id=2,
|
||||
tg_username=None,
|
||||
tg_first_name="Two",
|
||||
tg_last_name=None,
|
||||
tg_phone=None,
|
||||
marzban_username="tg2",
|
||||
activated_at=1200,
|
||||
)
|
||||
except ValueError as exc:
|
||||
assert "already used" in str(exc)
|
||||
else:
|
||||
raise AssertionError("second activation should fail")
|
||||
@@ -0,0 +1,23 @@
|
||||
from vpn_bot.marzban import MarzbanClient
|
||||
from vpn_bot.utils import GIB
|
||||
|
||||
|
||||
def test_build_trial_user_payload_matches_required_proxy_inbound_and_limits():
|
||||
payload = MarzbanClient.build_trial_user_payload(
|
||||
tg_user_id=856850518,
|
||||
first_name="Dmitry",
|
||||
last_name="Gudov",
|
||||
tg_username="krosh",
|
||||
phone="+79990000000",
|
||||
now=1_700_000_000,
|
||||
)
|
||||
|
||||
assert payload["username"] == "tg856850518"
|
||||
assert payload["proxies"] == {"vless": {"flow": "xtls-rprx-vision"}}
|
||||
assert payload["inbounds"] == {"vless": ["VLESS TCP REALITY"]}
|
||||
assert payload["expire"] == 1_700_000_000 + 3 * 24 * 60 * 60
|
||||
assert payload["data_limit"] == 10 * GIB
|
||||
assert payload["data_limit_reset_strategy"] == "no_reset"
|
||||
assert payload["status"] == "active"
|
||||
assert "Phone: +79990000000" in payload["note"]
|
||||
assert "856850518" not in payload["note"]
|
||||
@@ -0,0 +1,67 @@
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from vpn_bot.utils import (
|
||||
GIB,
|
||||
add_days_from_base,
|
||||
bytes_to_human,
|
||||
build_marzban_note,
|
||||
calculate_reset_expire,
|
||||
format_marzban_username,
|
||||
)
|
||||
|
||||
|
||||
def test_format_marzban_username_uses_tg_prefix():
|
||||
assert format_marzban_username(856850518) == "tg856850518"
|
||||
|
||||
|
||||
def test_build_marzban_note_omits_telegram_id_and_missing_phone():
|
||||
note = build_marzban_note(
|
||||
first_name="Dmitry",
|
||||
last_name="Gudov",
|
||||
username="dima",
|
||||
phone=None,
|
||||
)
|
||||
|
||||
assert "First name: Dmitry" in note
|
||||
assert "Last name: Gudov" in note
|
||||
assert "Username: @dima" in note
|
||||
assert "Phone:" not in note
|
||||
assert "856" not in note
|
||||
|
||||
|
||||
def test_add_days_from_future_expiration_extends_from_future_timestamp():
|
||||
now = 1_700_000_000
|
||||
future = now + 2 * 24 * 60 * 60
|
||||
|
||||
assert add_days_from_base(current_expire=future, days=30, now=now) == future + 30 * 24 * 60 * 60
|
||||
|
||||
|
||||
def test_add_days_from_expired_account_extends_from_now():
|
||||
now = 1_700_000_000
|
||||
expired = now - 10
|
||||
|
||||
assert add_days_from_base(current_expire=expired, days=30, now=now) == now + 30 * 24 * 60 * 60
|
||||
|
||||
|
||||
def test_calculate_reset_expire_floors_remaining_time_to_whole_30_day_months():
|
||||
now = 1_700_000_000
|
||||
expire = now + (1 * 30 + 12) * 24 * 60 * 60
|
||||
|
||||
new_expire, full_months = calculate_reset_expire(expire=expire, now=now)
|
||||
|
||||
assert full_months == 1
|
||||
assert new_expire == now + 30 * 24 * 60 * 60
|
||||
|
||||
|
||||
def test_calculate_reset_expire_discards_partial_month_when_less_than_one_month_left():
|
||||
now = 1_700_000_000
|
||||
expire = now + 12 * 24 * 60 * 60
|
||||
|
||||
new_expire, full_months = calculate_reset_expire(expire=expire, now=now)
|
||||
|
||||
assert full_months == 0
|
||||
assert new_expire == now
|
||||
|
||||
|
||||
def test_bytes_to_human_formats_gib():
|
||||
assert bytes_to_human(50 * GIB) == "50.00 GB"
|
||||
Reference in New Issue
Block a user