|
| 1 | +import pytest |
| 2 | + |
| 3 | +from telegram_mcp import runtime |
| 4 | +from telegram_mcp.tools import media |
| 5 | + |
| 6 | + |
| 7 | +class _DummyClient: |
| 8 | + def __init__(self): |
| 9 | + self.sent = None |
| 10 | + |
| 11 | + async def send_file(self, entity, file_paths, caption=None): |
| 12 | + self.sent = { |
| 13 | + "entity": entity, |
| 14 | + "file_paths": file_paths, |
| 15 | + "caption": caption, |
| 16 | + } |
| 17 | + |
| 18 | + |
| 19 | +@pytest.mark.asyncio |
| 20 | +@pytest.mark.parametrize("tool_name", ["send_album", "send_file"]) |
| 21 | +async def test_album_mode_sends_multiple_files_as_one_media_group( |
| 22 | + tmp_path, monkeypatch, tool_name |
| 23 | +): |
| 24 | + root = (tmp_path / "root").resolve() |
| 25 | + root.mkdir() |
| 26 | + first = root / "one.png" |
| 27 | + second = root / "two.png" |
| 28 | + first.write_bytes(b"png-one") |
| 29 | + second.write_bytes(b"png-two") |
| 30 | + |
| 31 | + client = _DummyClient() |
| 32 | + monkeypatch.setattr(runtime, "SERVER_ALLOWED_ROOTS", [root]) |
| 33 | + monkeypatch.setattr(media, "clients", {"default": client}) |
| 34 | + monkeypatch.setattr(media, "get_client", lambda account=None: client) |
| 35 | + |
| 36 | + async def _resolve_entity(chat_id, cl): |
| 37 | + assert chat_id == "AgenticAIChat" |
| 38 | + assert cl is client |
| 39 | + return "entity:AgenticAIChat" |
| 40 | + |
| 41 | + monkeypatch.setattr(media, "resolve_entity", _resolve_entity) |
| 42 | + |
| 43 | + tool = getattr(media, tool_name) |
| 44 | + result = await tool( |
| 45 | + "AgenticAIChat", |
| 46 | + ["one.png", str(second)], |
| 47 | + caption="pick one", |
| 48 | + ) |
| 49 | + |
| 50 | + assert result == "Album sent to chat AgenticAIChat with 2 files." |
| 51 | + assert client.sent == { |
| 52 | + "entity": "entity:AgenticAIChat", |
| 53 | + "file_paths": [str(first), str(second)], |
| 54 | + "caption": "pick one", |
| 55 | + } |
| 56 | + |
| 57 | + |
| 58 | +@pytest.mark.asyncio |
| 59 | +@pytest.mark.parametrize( |
| 60 | + ("file_paths", "expected"), |
| 61 | + [ |
| 62 | + ("not-a-list", "file_paths must be a list of file paths."), |
| 63 | + (["one.png"], "Albums must contain between 2 and 10 files."), |
| 64 | + ([f"{index}.png" for index in range(11)], "Albums must contain between 2 and 10 files."), |
| 65 | + ], |
| 66 | +) |
| 67 | +async def test_send_album_validates_album_file_count(file_paths, expected, monkeypatch): |
| 68 | + monkeypatch.setattr(media, "clients", {"default": _DummyClient()}) |
| 69 | + |
| 70 | + result = await media.send_album("AgenticAIChat", file_paths) |
| 71 | + |
| 72 | + assert result == expected |
| 73 | + |
| 74 | + |
| 75 | +@pytest.mark.asyncio |
| 76 | +async def test_send_album_reuses_readable_path_security(tmp_path, monkeypatch): |
| 77 | + root = (tmp_path / "root").resolve() |
| 78 | + outside = (tmp_path / "outside").resolve() |
| 79 | + root.mkdir() |
| 80 | + outside.mkdir() |
| 81 | + (root / "one.png").write_bytes(b"png-one") |
| 82 | + outside_file = outside / "two.png" |
| 83 | + outside_file.write_bytes(b"png-two") |
| 84 | + |
| 85 | + monkeypatch.setattr(runtime, "SERVER_ALLOWED_ROOTS", [root]) |
| 86 | + monkeypatch.setattr(media, "clients", {"default": _DummyClient()}) |
| 87 | + |
| 88 | + result = await media.send_album("AgenticAIChat", ["one.png", str(outside_file)]) |
| 89 | + |
| 90 | + assert result == "Path is outside allowed roots." |
0 commit comments