import pytest
from bot import sysinfo
def test_parse_cpu_max():
assert sysinfo.parse_cpu_max("200000 100000") == 2.0
assert sysinfo.parse_cpu_max("max 100000") is None
assert sysinfo.parse_cpu_max("garbage") is None
assert sysinfo.parse_cpu_max(None) is None
def test_parse_mem_limit():
assert sysinfo.parse_mem_limit("17179869184") == 17179869184
assert sysinfo.parse_mem_limit("max") is None
assert sysinfo.parse_mem_limit("9223372036854771712") is None # cgroup v1 "unlimited"
assert sysinfo.parse_mem_limit(None) is None
def test_parse_stat_value():
text = "usage_usec 12345\nuser_usec 1\ninactive_file 99"
assert sysinfo.parse_stat_value(text, "usage_usec") == 12345
assert sysinfo.parse_stat_value(text, "inactive_file") == 99
assert sysinfo.parse_stat_value(text, "missing") is None
@pytest.fixture
def fake_cgroup(tmp_path, monkeypatch):
(tmp_path / "cpu.max").write_text("200000 100000\n")
(tmp_path / "memory.max").write_text("17179869184\n")
(tmp_path / "memory.current").write_text("3000000000\n")
(tmp_path / "memory.stat").write_text("anon 1\ninactive_file 1000000000\n")
(tmp_path / "cpu.stat").write_text("usage_usec 1000000\n")
monkeypatch.setattr(sysinfo, "CGROUP_ROOT", str(tmp_path))
return tmp_path
def test_container_memory_uses_cgroup_and_ignores_page_cache(fake_cgroup):
used, total = sysinfo.memory_usage()
assert used == 2_000_000_000
assert total <= 17179869184
assert sysinfo.cpu_cores() == 2.0
async def test_container_cpu_percent_from_cgroup(fake_cgroup, monkeypatch):
calls = {"n": 0}
real = sysinfo._cgroup_cpu_usec
def fake_usec():
calls["n"] += 1
return 1_000_000 if calls["n"] == 1 else 1_500_000 # 0.5 cpu-seconds over ~0.1s window
monkeypatch.setattr(sysinfo, "_cgroup_cpu_usec", fake_usec)
pct = await sysinfo.cpu_percent(interval=0.1)
assert 0 < pct <= 100
monkeypatch.setattr(sysinfo, "_cgroup_cpu_usec", real)
def test_falls_back_to_psutil_without_cgroup(tmp_path, monkeypatch):
monkeypatch.setattr(sysinfo, "CGROUP_ROOT", str(tmp_path / "does-not-exist"))
used, total = sysinfo.memory_usage()
assert total > 0 and used >= 0
assert sysinfo.cpu_cores() >= 1
async def test_format_status_and_stats(tmp_path):
text = sysinfo.format_status(123.4, 1, "1.45.0")
assert "123 ms" in text and "DC 1" in text and "v1.0.0" in text and "Uptime" in text
assert "n/a" in sysinfo.format_status(None, None, "x")
stats = await sysinfo.format_stats(3, 12, 4_500_000_000, 1, 2, str(tmp_path))
for needle in ("Users: 3", "Files renamed", "1 running · 2 waiting", "CPU", "RAM", "Disk", "Network"):
assert needle in stats
# ---------------------------------------------------------------------------
# what makes transfers fast: cryptg, uvloop, parallel transfer
# ---------------------------------------------------------------------------
def test_telethon_encrypts_with_cryptg_here():
# also a guard for the deployment: requirements.txt must keep installing cryptg
assert sysinfo.crypto_backend() == "cryptg"
@pytest.mark.parametrize("cryptg, libssl_works, expected", [
(object(), True, "cryptg"), (object(), False, "cryptg"), (None, True, "libssl"), (None, False, "python"),
])
def test_crypto_backend_follows_the_order_telethon_picks(monkeypatch, cryptg, libssl_works, expected):
from telethon.crypto import aes
monkeypatch.setattr(aes, "cryptg", cryptg)
monkeypatch.setattr(aes.libssl, "encrypt_ige", (lambda *a: b"") if libssl_works else None)
monkeypatch.setattr(aes.libssl, "decrypt_ige", (lambda *a: b"") if libssl_works else None)
assert sysinfo.crypto_backend() == expected
async def test_loop_backend_sees_the_standard_loop():
assert sysinfo.loop_backend() == "asyncio"
def test_loop_backend_outside_a_loop_and_on_uvloop():
uvloop = pytest.importorskip("uvloop")
assert sysinfo.loop_backend() == "none"
loop = uvloop.new_event_loop()
try:
assert loop.run_until_complete(_loop_name()) == "uvloop"
finally:
loop.close()
async def _loop_name():
return sysinfo.loop_backend()
def test_the_bot_really_starts_on_uvloop():
from bot import main as bot_main
pytest.importorskip("uvloop")
seen = []
async def probe():
seen.append(sysinfo.loop_backend())
return 0
assert bot_main._run(probe) == 0 and seen == ["uvloop"]
def test_speed_summary_and_checks_mark_what_is_off(monkeypatch):
monkeypatch.setattr(sysinfo, "crypto_backend", lambda: "cryptg")
monkeypatch.setattr(sysinfo, "loop_backend", lambda: "uvloop")
assert sysinfo.speed_summary(True, 8) == "cryptg ✅ · uvloop ✅ · parallel ×8 ✅"
assert all(fine for _short, _detail, fine in sysinfo.speed_checks(True, 8))
monkeypatch.setattr(sysinfo, "crypto_backend", lambda: "python")
monkeypatch.setattr(sysinfo, "loop_backend", lambda: "asyncio")
assert sysinfo.speed_summary(False, 8) == "slow Python crypto ⚠️ · no uvloop ⚠️ · parallel off ⚠️"
details = [detail for _short, detail, _fine in sysinfo.speed_checks(False, 8)]
assert "cryptg is missing" in details[0] and "uvloop is not running" in details[1] and "FAST_TRANSFER=0" in details[2]
monkeypatch.setattr(sysinfo, "crypto_backend", lambda: "libssl")
assert sysinfo.speed_summary(True, 16).startswith("libssl ✅") and "parallel ×16 ✅" in sysinfo.speed_summary(True, 16)
def test_the_speed_section_shows_the_stack_the_counts_and_the_last_failure(monkeypatch):
from bot.fasttransfer import TransferStats
monkeypatch.setattr(sysinfo, "crypto_backend", lambda: "cryptg")
monkeypatch.setattr(sysinfo, "loop_backend", lambda: "uvloop")
stats = TransferStats(fast_down=12, fast_up=9, small_up=3)
text = sysinfo.format_speed(True, 8, stats)
assert "⚡ Speed" in text and "Encryption: cryptg ✅" in text and "Event loop: uvloop ✅" in text
assert "Parallel transfer: on, 8 connections ✅" in text
assert "Downloads: 12 parallel · 0 standard" in text
assert "Uploads: 9 parallel · 3 up to 10 MB · 0 standard" in text
assert "failed" not in text and "⚠️" not in text
stats.standard_down, stats.standard_up = 2, 1
stats.fell_back("download", ValueError(" & worse"))
text = sysinfo.format_speed(False, 8, stats)
assert "off (FAST_TRANSFER=0) ⚠️" in text and "2 standard" in text
assert "failed 1×" in text and "download: ValueError: <bad> & worse" in text # HTML-safe
def test_format_status_adds_the_speed_section_only_when_given_one():
plain = sysinfo.format_status(10, 1, "1.45.0")
assert "Speed" not in plain and plain.endswith("Telethon 1.45.0")
with_speed = sysinfo.format_status(10, 1, "1.45.0", "⚡ Speed\nline")
assert with_speed.startswith(plain) and with_speed.endswith("\n\n⚡ Speed\nline")
def test_the_speed_summary_and_section_say_whether_streaming_is_on(monkeypatch):
from bot.fasttransfer import TransferStats
monkeypatch.setattr(sysinfo, "crypto_backend", lambda: "cryptg")
monkeypatch.setattr(sysinfo, "loop_backend", lambda: "uvloop")
assert sysinfo.speed_summary(True, 8, True) == "cryptg ✅ · uvloop ✅ · parallel ×8 ✅ · streaming ✅"
assert sysinfo.speed_summary(True, 8, False).endswith("parallel ×8 ✅ · no streaming ⚠️")
assert sysinfo.speed_summary(False, 8, True).endswith("parallel off ⚠️ · no streaming ⚠️") # streaming needs the parallel transfer
assert sysinfo.speed_summary(True, 8) == "cryptg ✅ · uvloop ✅ · parallel ×8 ✅" # nothing added when it is not asked for
stats = TransferStats(streamed=7)
text = sysinfo.format_speed(True, 8, stats, True)
assert "⬇⬆ Streaming rename: on: big files are uploaded while they download ✅" in text and "Streamed renames: 7" in text
assert "Streaming failed" not in text and "⚠️" not in text
assert "Streaming rename" not in sysinfo.format_speed(True, 8, stats) and "Streamed renames" not in sysinfo.format_speed(True, 8, stats)
stats.stream_failed(ValueError(""))
text = sysinfo.format_speed(True, 8, stats, False)
assert "off (STREAM_RENAME=0) ⚠️" in text
assert "⚠️ Streaming failed 1× and those files were saved first and then uploaded. Last: ValueError: <x>" in text
assert "The parallel transfer failed" not in text # a streaming failure is not a parallel-transfer failure