Spaces:
Paused
Paused
Download tests/test_channel_data.py from Mega-Evolutions/ButtonBot: direct link, hf CLI and curl.
- Browser
- Download file 9.11 kB
-
https://huggingface.co/spaces/Mega-Evolutions/ButtonBot/resolve/main/tests/test_channel_data.py
- Command line
-
hf download hf://spaces/Mega-Evolutions/ButtonBot/tests/test_channel_data.py
-
curl -L -o test_channel_data.py https://huggingface.co/spaces/Mega-Evolutions/ButtonBot/resolve/main/tests/test_channel_data.py
9.11 kB
| """Taking a channel out of the bot: the bot asks whether the channel's saved posts (its data in My posts) are deleted as | |
| well - Yes removes the channel and the data, No removes only the channel and keeps the data.""" | |
| import asyncio | |
| from app.db import utcnow | |
| from app.sync_engine import sync_channel | |
| from .harness import App, norm | |
| from .test_sync import old_msg, opts, world | |
| def run(coro): | |
| return asyncio.run(coro) | |
| async def make_app(tmp_path, **cfg): | |
| """Two channels: "Anime Channel" (id 1) has three published posts and a draft, "Other Channel" (id 2) has two posts; | |
| channel 1 also has what the sync keeps (where it stopped, a post it must forget) and a timed deletion.""" | |
| app = App(tmp_path, edit_delay=0, **cfg) | |
| await app.start() | |
| await app.db.save_channel(1, 5, "Anime Channel", "animech", 1) | |
| await app.db.save_channel(2, 6, "Other Channel", "otherch", 1) | |
| for cid, n in ((1, 3), (2, 2)): | |
| for i in range(1, n + 1): | |
| await app.db.create_post( | |
| channel_id=cid, message_id=i, status="sent", source="adopted", text=f"post {cid}/{i}", entities=[], buttons=[] | |
| ) | |
| await app.db.create_post(channel_id=1, status="draft", source="bot", text="a draft", entities=[], buttons=[]) | |
| for cid in (1, 2): | |
| await app.db.save_sync(cid, last_top=3, last_pts=3) | |
| await app.db.start_history(cid, 3) | |
| await app.db.ignore_post(cid, 9) | |
| app.ctx.sync_ignore.add((cid, 9)) | |
| await app.db.schedule_deletes( | |
| [dict(channel_id=1, message_id=2, delete_at=utcnow(), group_id="g", created_by=1)] | |
| ) | |
| return app | |
| async def tap_channel(app, label_part="1"): | |
| await app.text("/channels") | |
| await app.press(app.out.callback_exact(label_part)) | |
| # ============================================================================================ the question | |
| def test_the_owner_is_asked_whether_the_saved_posts_go_too(tmp_path): | |
| async def go(): | |
| app = await make_app(tmp_path) | |
| await tap_channel(app) | |
| text = norm(app.out.last_text) | |
| assert "Take Anime Channel out of the bot" in text | |
| assert "Do you also want to delete its saved posts" in text and "4 post(s)" in text # three published + a draft | |
| assert "Nothing is deleted in the channel itself" in text | |
| assert app.out.last_buttons() == [["π Yes, delete them too", "π¦ No, keep them"], ["π Back"]] | |
| # the question alone changes nothing | |
| assert [c.id for c in await app.db.list_channels()] == [1, 2] and await app.db.count_channel_posts(1) == 4 | |
| await app.press(app.out.callback_exact("π Back")) | |
| assert "Your channels" in norm(app.out.last_text) | |
| await app.db.close() | |
| run(go()) | |
| def test_a_channel_without_saved_posts_has_nothing_to_ask(tmp_path): | |
| async def go(): | |
| app = await make_app(tmp_path) | |
| await app.db.save_channel(3, 7, "Empty Channel", "emptych", 1) | |
| await app.text("/channels") | |
| await app.press(app.out.callback_exact("2")) # Anime, Empty, Other: the second one | |
| text = norm(app.out.last_text) | |
| assert "Take Empty Channel out of the bot?" in text and "no saved posts" in text | |
| assert app.out.last_buttons() == [["β Yes, remove it", "π Back"]] | |
| await app.press(app.out.callback_exact("β Yes, remove it")) | |
| assert [c.id for c in await app.db.list_channels()] == [1, 2] | |
| await app.db.close() | |
| run(go()) | |
| # ================================================================================================ yes | |
| def test_yes_removes_the_channel_and_deletes_its_saved_posts(tmp_path): | |
| async def go(): | |
| app = await make_app(tmp_path) | |
| await tap_channel(app) | |
| await app.press(app.out.callback_data("Yes, delete them too")) | |
| text = norm(app.out.last_text) | |
| assert "Anime Channel was removed from the bot" in text and "4 saved post(s) were deleted from My posts" in text | |
| assert "Nothing was deleted in the channel" in text | |
| assert [c.id for c in await app.db.list_channels()] == [2] | |
| row = await app.db.get_channel(1) | |
| assert row is not None and not row.active # switched off, not erased: a repost or shift that names it still finds it | |
| assert await app.db.count_channel_posts(1) == 0 and await app.db.sent_posts(1) == [] | |
| _, total = await app.db.list_posts(1, "draft", 0, 10) | |
| assert total == 0 | |
| # what the sync kept about the channel goes too, so it starts from scratch when it is added again | |
| assert await app.db.get_sync(1) is None and await app.db.get_history(1) is None | |
| assert await app.db.ignored_ids(1) == set() and not any(k[0] == 1 for k in app.ctx.sync_ignore) | |
| # ... and nothing of the other channel is touched | |
| assert await app.db.count_channel_posts(2) == 2 | |
| assert await app.db.get_sync(2) is not None and await app.db.get_history(2) is not None | |
| assert await app.db.ignored_ids(2) == {9} and (2, 9) in app.ctx.sync_ignore | |
| # a timed deletion is a promise about a message that is still in the channel: it stays | |
| assert [r.message_id for r in await app.db.pending_deletes()] == [2] | |
| await app.db.close() | |
| run(go()) | |
| def test_nothing_is_deleted_in_telegram_when_the_data_goes(tmp_path): | |
| async def go(): | |
| app = await make_app(tmp_path) | |
| before = len(app.tg.requests) | |
| await tap_channel(app) | |
| await app.press(app.out.callback_data("Yes, delete them too")) | |
| assert app.tg.deleted == [] and len(app.tg.requests) == before | |
| await app.db.close() | |
| run(go()) | |
| def test_the_posts_are_not_deleted_while_a_look_at_the_channels_is_running(tmp_path): | |
| async def go(): | |
| app = await make_app(tmp_path) | |
| lock = app.ctx.syncer.exclusive() | |
| await lock.acquire() # a look at the channels is in progress: it could save posts again | |
| task = asyncio.create_task(app.press("chy:1:1")) | |
| await asyncio.sleep(0.2) | |
| assert not task.done() and await app.db.count_channel_posts(1) == 4 | |
| lock.release() | |
| await task | |
| assert await app.db.count_channel_posts(1) == 0 | |
| await app.db.close() | |
| run(go()) | |
| # =================================================================================================== no | |
| def test_no_removes_only_the_channel_and_keeps_its_saved_posts(tmp_path): | |
| async def go(): | |
| app = await make_app(tmp_path) | |
| await tap_channel(app) | |
| await app.press(app.out.callback_data("No, keep them")) | |
| text = norm(app.out.last_text) | |
| assert "Anime Channel was removed from the bot" in text and "saved posts are kept" in text | |
| assert [c.id for c in await app.db.list_channels()] == [2] | |
| assert await app.db.count_channel_posts(1) == 4 and await app.db.get_sync(1) is not None | |
| assert await app.db.ignored_ids(1) == {9} and (1, 9) in app.ctx.sync_ignore | |
| # added again: everything is there again | |
| await app.db.save_channel(1, 5, "Anime Channel", "animech", 1) | |
| assert [c.id for c in await app.db.list_channels()] == [1, 2] and await app.db.count_channel_posts(1) == 4 | |
| await app.db.close() | |
| run(go()) | |
| def test_a_button_from_the_old_question_still_removes_only_the_channel(tmp_path): | |
| async def go(): | |
| app = await make_app(tmp_path) | |
| await app.press("chy:1") # what the earlier version of the bot put on its button | |
| assert [c.id for c in await app.db.list_channels()] == [2] | |
| assert await app.db.count_channel_posts(1) == 4 | |
| assert "saved posts are kept" in norm(app.out.last_text) | |
| await app.db.close() | |
| run(go()) | |
| # ============================================================================================ who may do it | |
| def test_only_an_owner_can_remove_a_channel_or_delete_its_data(tmp_path): | |
| async def go(): | |
| app = await make_app(tmp_path, owner=7, admins=frozenset({1})) | |
| app.uid = 1 # an admin, not an owner | |
| for data in ("chd:1", "chy:1:1", "chy:1:0", "chy:1"): | |
| await app.press(data) | |
| assert [c.id for c in await app.db.list_channels()] == [1, 2] and await app.db.count_channel_posts(1) == 4 | |
| await app.db.close() | |
| run(go()) | |
| # ========================================================================================= coming back | |
| def test_a_channel_that_comes_back_after_its_data_was_deleted_is_read_in_again(tmp_path): | |
| async def go(): | |
| chan, db, ch = await world(tmp_path, {i: old_msg(i, f"older post {i}") for i in range(1, 6)}, save=False) | |
| rep = await sync_channel(chan, db, ch, opts()) | |
| assert rep.first_look and rep.imported == 5 | |
| assert await db.delete_channel_data(1) == 5 | |
| assert await db.sent_posts(1) == [] and await db.get_sync(1) is None and await db.get_history(1) is None | |
| rep = await sync_channel(chan, db, ch, opts()) # as if it had never been looked at | |
| assert rep.first_look and rep.imported == 5 and rep.history_done | |
| assert len(await db.sent_posts(1)) == 5 | |
| await db.close() | |
| run(go()) | |