Spaces:
Paused
Paused
Download tests/test_buttons_sync.py from Mega-Evolutions/ButtonBot: direct link, hf CLI and curl.
- Browser
- Download file 8.12 kB
-
https://huggingface.co/spaces/Mega-Evolutions/ButtonBot/resolve/main/tests/test_buttons_sync.py
- Command line
-
hf download hf://spaces/Mega-Evolutions/ButtonBot/tests/test_buttons_sync.py
-
curl -L -o test_buttons_sync.py https://huggingface.co/spaces/Mega-Evolutions/ButtonBot/resolve/main/tests/test_buttons_sync.py
8.12 kB
| """Checking and repairing the buttons a channel really shows.""" | |
| import asyncio | |
| import pytest | |
| from app.buttons_sync import ButtonsNotShown, ensure_markup, fix_buttons, link_urls, scan_buttons | |
| from app.tgutil import build_markup, button_url, peer_of | |
| from .dbutil import db_url_for, fresh_db | |
| from .fakes import FakeChannelClient, make_msg, markup, rpc, url_btn | |
| from .harness import App, norm | |
| A = "https://t.me/goku?start=A" | |
| B = "https://t.me/goku?start=B" | |
| ROWS = [[{"t": "Get", "u": A}], [{"t": "More", "u": B}]] | |
| def shown(chan, i): | |
| m = chan.msgs[i] | |
| return [button_url(b) for r in (m.reply_markup.rows if m.reply_markup else []) for b in r.buttons] | |
| class Peer: | |
| id, access_hash = 1, 5 | |
| def run(coro): | |
| return asyncio.run(coro) | |
| def test_link_urls(): | |
| assert link_urls(None) == [] | |
| assert link_urls(markup([url_btn("x", A), url_btn("y", B)])) == [A, B] | |
| def test_ensure_markup_in_every_situation(): | |
| async def go(): | |
| want = build_markup(ROWS) | |
| peer = peer_of(Peer) | |
| chan = FakeChannelClient({1: make_msg(1, "ok", markup=want)}) | |
| assert await ensure_markup(chan, peer, 1, want, pause=0) == "fine" | |
| assert chan.edits == [] | |
| chan = FakeChannelClient({1: make_msg(1, "no keyboard")}) # simply missing: one edit is enough | |
| assert await ensure_markup(chan, peer, 1, want, pause=0) == "set" | |
| assert shown(chan, 1) == [A, B] and len(chan.edits) == 1 and chan.msgs[1].message == "no keyboard" | |
| chan = FakeChannelClient({1: make_msg(1, "hidden")}) # Telegram believes the keyboard is there already | |
| chan.msgs[1]._hidden = want | |
| assert await ensure_markup(chan, peer, 1, want, pause=0) == "nudged" | |
| assert shown(chan, 1) == [A, B] and len(chan.edits) == 2 | |
| chan = FakeChannelClient({}) | |
| with pytest.raises(LookupError): | |
| await ensure_markup(chan, peer, 1, want, pause=0) | |
| chan = FakeChannelClient({1: make_msg(1, "x")}) | |
| chan.drop_all_markup = True # edits are accepted but the keyboard never appears | |
| with pytest.raises(ButtonsNotShown): | |
| await ensure_markup(chan, peer, 1, want, pause=0) | |
| chan = FakeChannelClient({1: make_msg(1, "x")}) # nothing to show -> nothing to do | |
| assert await ensure_markup(chan, peer, 1, None, pause=0) == "fine" and chan.requests == [] | |
| run(go()) | |
| async def seeded(tmp_path): | |
| """Channel + database as /repost used to leave them: the database knows the buttons, the channel may not show them.""" | |
| db = await fresh_db(db_url_for(tmp_path)) | |
| await db.save_channel(1, 5, "Test", "testch", 99) | |
| ch = await db.get_channel(1) | |
| want = build_markup(ROWS) | |
| msgs = { | |
| 10: make_msg(10, "fine", markup=want), | |
| 11: make_msg(11, "missing"), | |
| 12: make_msg(12, "hidden"), | |
| 13: make_msg(13, "other link", markup=markup([url_btn("Get", "https://t.me/other?start=Z")])), | |
| 15: make_msg(15, "no buttons saved"), | |
| } | |
| chan = FakeChannelClient(msgs) | |
| chan.msgs[12]._hidden = want | |
| for i in (10, 11, 12, 13, 14): # 14 was deleted by hand | |
| await db.create_post(channel_id=1, message_id=i, status="sent", source="bot", text="t", entities=[], buttons=ROWS) | |
| await db.create_post(channel_id=1, message_id=15, status="sent", source="bot", text="t", entities=[], buttons=[]) | |
| await db.create_post(channel_id=1, message_id=None, status="draft", source="bot", text="draft", entities=[], buttons=ROWS) | |
| return db, ch, chan | |
| def test_scan_finds_exactly_the_posts_with_wrong_buttons(tmp_path): | |
| async def go(): | |
| db, ch, chan = await seeded(tmp_path) | |
| scan = await scan_buttons(chan, ch, await db.sent_posts(1)) | |
| assert scan.total == 5 # 10..14 have saved link buttons; 15 and the draft do not count | |
| assert scan.fine == 1 | |
| assert sorted(p.message_id for p, _, _ in scan.bad) == [11, 12, 13] | |
| assert [p.message_id for p in scan.gone] == [14] | |
| live13 = next(live for p, live, _ in scan.bad if p.message_id == 13) | |
| assert live13 == ["https://t.me/other?start=Z"] | |
| await db.close() | |
| run(go()) | |
| def test_fix_puts_the_saved_buttons_back(tmp_path): | |
| async def go(): | |
| db, ch, chan = await seeded(tmp_path) | |
| scan = await scan_buttons(chan, ch, await db.sent_posts(1)) | |
| res = await fix_buttons(chan, ch, scan.bad, delay=0, pause=0) | |
| assert (res.fixed, res.nudged, res.already, res.gone, res.failed, res.aborted) == (3, 1, 0, 0, [], None) | |
| for i in (10, 11, 12, 13): | |
| assert shown(chan, i) == [A, B] | |
| assert chan.msgs[11].message == "missing" # texts untouched | |
| again = await scan_buttons(chan, ch, await db.sent_posts(1)) | |
| assert again.bad == [] and again.fine == 4 and len(again.gone) == 1 | |
| await db.close() | |
| run(go()) | |
| def test_fix_gives_up_after_several_identical_refusals(tmp_path): | |
| async def go(): | |
| db = await fresh_db(db_url_for(tmp_path)) | |
| await db.save_channel(1, 5, "Test", "testch", 99) | |
| ch = await db.get_channel(1) | |
| chan = FakeChannelClient({i: make_msg(i, "x") for i in range(1, 9)}) | |
| chan.fail["EditMessageRequest"] = rpc("MessageIdInvalidError") | |
| for i in range(1, 9): | |
| await db.create_post(channel_id=1, message_id=i, status="sent", source="bot", text="x", entities=[], buttons=ROWS) | |
| scan = await scan_buttons(chan, ch, await db.sent_posts(1)) | |
| res = await fix_buttons(chan, ch, scan.bad, delay=0, pause=0) | |
| assert res.fixed == 0 and len(res.failed) == 5 and res.aborted == "MessageIdInvalidError" | |
| await db.close() | |
| run(go()) | |
| # ------------------------------------------------------------------------------------------ the button in My posts | |
| def test_check_buttons_in_my_posts(tmp_path): | |
| async def go(): | |
| app = App(tmp_path) | |
| await app.start() | |
| db, ch, chan = await seeded_app(app) | |
| await app.press("pc:1:0") | |
| assert "Check buttons" in str(app.out.last_buttons()) | |
| await app.press("pk:1") | |
| t = norm(app.out.last_text) | |
| assert "checked 5 post(s)" in t and "1 show their buttons correctly" in t and "3 are saved with buttons" in t | |
| assert "#11" in t and "#12" in t and "#13" in t and "1 saved post(s) no longer exist" in t | |
| assert "Fix 3 post(s)" in str(app.out.last_buttons()) | |
| await app.press(app.out.callback_data("Fix 3")) | |
| t = norm(app.out.last_text) | |
| assert "put the saved buttons back on 3 post(s)" in t and "1 of them needed a second edit" in t | |
| for i in (10, 11, 12, 13): | |
| assert shown(app.tg, i) == [A, B] | |
| await app.press("pk:1") | |
| assert "Everything matches" in norm(app.out.last_text) and "Fix" not in str(app.out.last_buttons()) | |
| await app.db.close() | |
| run(go()) | |
| async def seeded_app(app): | |
| await app.db.save_channel(1, 5, "Anime Channel", "animech", 1) | |
| want = build_markup(ROWS) | |
| app.tg.msgs.update( | |
| { | |
| 10: make_msg(10, "fine", markup=want), | |
| 11: make_msg(11, "missing"), | |
| 12: make_msg(12, "hidden"), | |
| 13: make_msg(13, "other link", markup=markup([url_btn("Get", "https://t.me/other?start=Z")])), | |
| 15: make_msg(15, "no buttons saved"), | |
| } | |
| ) | |
| app.tg.msgs[12]._hidden = want | |
| for i in (10, 11, 12, 13, 14): | |
| await app.db.create_post(channel_id=1, message_id=i, status="sent", source="bot", text="t", entities=[], buttons=ROWS) | |
| return app.db, None, app.tg | |
| def test_check_buttons_is_for_the_owner_only(tmp_path): | |
| async def go(): | |
| app = App(tmp_path, owner=1, admins=frozenset({2})) | |
| await app.start() | |
| await seeded_app(app) | |
| app.uid = 2 # an admin who may make posts but is not the owner | |
| await app.press("pc:1:0") | |
| assert "Check buttons" not in str(app.out.last_buttons()) | |
| await app.press("pk:1") | |
| assert "Only the bot owner" in app.out.last_text | |
| await app.press("pkf:1") | |
| assert "Only the bot owner" in app.out.last_text | |
| await app.db.close() | |
| run(go()) | |