"""Media handling tests. Run with: python test_bot.py Covers the dispatch table that decides how an incoming message is stored and how it comes back through inline search. """ import datetime import os import sys os.environ.setdefault("BOT_TOKEN", "123456789:AAHtestTokenPlaceholder_ABCDEFGHIJKLMNOP") os.environ.setdefault("DB_PATH", ":memory:") sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) from aiogram.types import (Animation, Audio, Chat, Document, Message, PhotoSize, Sticker, Video, VideoNote, Voice) import bot as B CHAT = Chat(id=1, type="private") NOW = datetime.datetime.now() def msg(**kw) -> Message: return Message(message_id=1, date=NOW, chat=CHAT, **kw) def sample_messages(): photo = PhotoSize(file_id="F_photo", file_unique_id="U_photo", width=1, height=1) anim = Animation(file_id="F_gif", file_unique_id="U_gif", width=1, height=1, duration=1) sticker = Sticker(file_id="F_st", file_unique_id="U_st", width=1, height=1, type="regular", is_animated=False, is_video=False) return [ ("photo", msg(photo=[photo])), # Telegram sets document alongside animation for backward compatibility. ("gif", msg(animation=anim, document=Document(file_id="F_gif", file_unique_id="U_gif"))), ("sticker", msg(sticker=sticker)), ("video", msg(video=Video(file_id="F_v", file_unique_id="U_v", width=1, height=1, duration=1))), ("video", msg(video_note=VideoNote(file_id="F_vn", file_unique_id="U_vn", length=1, duration=1))), ("voice", msg(voice=Voice(file_id="F_vo", file_unique_id="U_vo", duration=1))), ("audio", msg(audio=Audio(file_id="F_a", file_unique_id="U_a", duration=1))), ("document", msg(document=Document(file_id="F_doc", file_unique_id="U_doc"))), ] def test_every_type_extracts(): for expected, message in sample_messages(): found = B.extract_media(message) assert found is not None, f"{expected}: nothing extracted" assert found[0] == expected, f"expected {expected}, got {found[0]}" print(" every supported media type extracts to the right media_type") def test_animation_beats_document(): gif = sample_messages()[1][1] assert gif.document is not None, "test fixture should mimic Telegram's dual field" assert B.extract_media(gif)[0] == "gif", "a GIF must not be filed as a document" print(" animation wins over the backward-compat document field") def test_video_note_stored_as_video(): note = sample_messages()[4][1] assert B.extract_media(note) == ("video", "F_vn", "U_vn") print(" video notes are stored as video so inline search can return them") def test_non_media_ignored(): assert B.extract_media(msg(text="hello")) is None print(" plain text messages are ignored") def test_every_stored_type_renders_inline(): stored = {t for t, _ in B.MEDIA_EXTRACTORS} missing = stored - set(B.INLINE_BUILDERS) assert not missing, f"stored but not renderable inline: {sorted(missing)}" print(" every stored media_type has a matching inline builder") def test_builders_construct(): for media_type, build in B.INLINE_BUILDERS.items(): build("rid", f"FID_{media_type}", "cat, funny") # Documents and videos require a non-empty title; keywords can be blank. build("rid", f"FID_{media_type}", "") print(" inline builders construct, including with empty keywords") def main(): for test in (test_every_type_extracts, test_animation_beats_document, test_video_note_stored_as_video, test_non_media_ignored, test_every_stored_type_renders_inline, test_builders_construct): test() print("\nAll media tests passed.") if __name__ == "__main__": main()