import asyncio from telethon import errors from telethon.tl import types from conftest import RecordingNotifier from errors import PermanentError, TransientError from progress import ProgressReporter, TransferProgress, format_bytes, format_duration from telegram_client import classify_media, map_telegram_error, parse_channel_reference, to_source_message # -- progress ------------------------------------------------------------------------------ def test_formatting(): assert format_bytes(512) == "512 B" assert format_bytes(1536) == "1.5 KB" assert format_bytes(5 * 1024**3) == "5.0 GB" assert format_duration(42) == "42s" assert format_duration(125) == "2m 5s" assert format_duration(3 * 3600 + 120) == "3h 2m" def test_speed_and_eta(): now = [0.0] p = TransferProgress(total=100 * 1024 * 1024, clock=lambda: now[0]) p.update(0) now[0] = 10.0 p.update(50 * 1024 * 1024) assert round(p.speed) == 5 * 1024 * 1024 assert round(p.eta) == 10 assert p.percent == 50 text = p.describe() assert "50%" in text and "MB/s" in text and "ETA 10s" in text def test_reporter_edits_single_message(): notifier = RecordingNotifier() reporter = ProgressReporter(notifier, [1, 2], interval=60) async def go(): reporter.board.begin_item(12, 150, "Misc• Grammar Practice", "Class 01 Aman sir.mp4") reporter.board.start_download(1000) reporter.board.download.update(640) await reporter.refresh() await reporter.refresh() # unchanged -> nothing sent reporter.board.download.update(900) await reporter.refresh() asyncio.run(go()) assert len(notifier.sent) == 2 # one per chat assert len(notifier.edits) == 2 # then edited first = notifier.sent[0][1] assert first.splitlines()[0] == "Processing video 12 of 150" assert "Topic: Misc• Grammar Practice" in first and "Download: 64%" in first and "Upload: Waiting" in first # -- telegram media classification ---------------------------------------------------------- def _doc(attributes, mime="video/mp4"): return types.Document(id=77, access_hash=1, file_reference=b"", date=None, mime_type=mime, size=1234, dc_id=1, attributes=attributes) def _msg(media, text="Title: A.mp4"): return types.Message(id=5, peer_id=types.PeerChannel(1), date=None, message=text, media=media) def test_classify_video_message(): video = types.DocumentAttributeVideo(duration=60, w=1280, h=720) info = classify_media(_msg(types.MessageMediaDocument(document=_doc([video]))), include_documents=False) assert info.kind == "video" and info.size == 1234 and info.media_id == 77 sm = to_source_message(_msg(types.MessageMediaDocument(document=_doc([video]))), -100, False) assert sm.caption == "Title: A.mp4" and sm.message_id == 5 def test_classify_document_video_only_when_enabled(): doc = _doc([types.DocumentAttributeFilename("lecture.mkv")], mime="application/octet-stream") message = _msg(types.MessageMediaDocument(document=doc)) assert classify_media(message, include_documents=False) is None assert classify_media(message, include_documents=True).file_name == "lecture.mkv" def test_unsupported_media_is_ignored(): gif = _doc([types.DocumentAttributeVideo(duration=1, w=1, h=1), types.DocumentAttributeAnimated()]) note = _doc([types.DocumentAttributeVideo(duration=1, w=1, h=1, round_message=True)]) pdf = _doc([types.DocumentAttributeFilename("notes.pdf")], mime="application/pdf") for doc in (gif, note, pdf): assert classify_media(_msg(types.MessageMediaDocument(document=doc)), include_documents=True) is None photo = types.MessageMediaPhoto(photo=types.PhotoEmpty(id=1)) assert classify_media(_msg(photo), include_documents=True) is None assert classify_media(_msg(None, text="text only"), include_documents=True) is None def test_channel_reference_parsing(): assert parse_channel_reference("-1001234567890") == -1001234567890 assert parse_channel_reference("@my_channel") == "my_channel" assert parse_channel_reference("https://t.me/my_channel") == "my_channel" assert parse_channel_reference("https://t.me/c/1234567890/55") == -1001234567890 def test_telegram_error_mapping(): flood = map_telegram_error(errors.FloodWaitError(request=None, capture=42)) assert isinstance(flood, TransientError) and flood.retry_after == 42 and flood.rate_limited assert isinstance(map_telegram_error(errors.ChannelPrivateError(request=None)), PermanentError) assert isinstance(map_telegram_error(ConnectionError("x")), TransientError)