def test_add_to_blocklist__new_blocklist(mock_client): api = MockExportAPI() watcher = SubscriptionWatcher(api, mock_client) watcher.add_to_blocklist(18749, "test") assert len(watcher.blocklists[18749]) == 1 assert isinstance(watcher.blocklists[18749], set) assert watcher.blocklists[18749] == {"test"}
async def test_get_new_results__handles_sub_id_drop(mock_client): api = MockExportAPI() api.with_browse_results([MockSubmission("1220")]) watcher = SubscriptionWatcher(api, mock_client) watcher.latest_ids.append("1225") watcher.running = True results = await watcher._get_new_results() assert len(results) == 0
def test_update_latest_ids(mock_client): api = MockExportAPI() watcher = SubscriptionWatcher(api, mock_client) id_list = ["1234", "1233", "1230", "1229"] submissions = [MockSubmission(x) for x in id_list] mock_save_json = MockMethod() watcher.save_to_json = mock_save_json.call watcher._update_latest_ids(submissions) assert list(watcher.latest_ids) == id_list assert mock_save_json.called
def test_migrate_no_block_queries(mock_client): old_chat_id = 12345 new_chat_id = 54321 api = MockExportAPI() watcher = SubscriptionWatcher(api, mock_client) watcher.subscriptions.add(MockSubscription("ych", old_chat_id)) watcher.migrate_chat(old_chat_id, new_chat_id) assert len(watcher.subscriptions) == 1 sub = list(watcher.subscriptions)[0] assert sub.query_str == "ych" assert sub.destination == new_chat_id
async def test_run__is_stopped_by_running_false(mock_client): api = MockExportAPI() s = SubscriptionWatcher(api, mock_client) # Shorten the wait s.BACK_OFF = 1 task = asyncio.get_event_loop().create_task(watcher_killer(s)) # Run watcher await s.run() assert True await task
def test_list_blocklisted_tags(mock_client): api = MockExportAPI() watcher = SubscriptionWatcher(api, mock_client) watcher.blocklists[18749] = {"example", "deer"} watcher.blocklists[18747] = {"test"} func = BlocklistFunctionality(watcher) resp = func._list_blocklisted_tags(18749) assert "Current blocklist for this chat:" in resp assert "- deer" in resp assert "- example" in resp assert "- test" not in resp
def test_remove_from_blocklist__tag_not_in_blocklist(mock_client): api = MockExportAPI() watcher = SubscriptionWatcher(api, mock_client) watcher.blocklists[18749] = {"example"} watcher.blocklists[18747] = {"test"} func = BlocklistFunctionality(watcher) resp = func._remove_from_blocklist(18749, "test") assert resp == "The tag \"test\" is not on the blocklist for this chat." assert len(watcher.blocklists) == 2 assert len(watcher.blocklists[18749]) == 1 assert len(watcher.blocklists[18747]) == 1
async def test_run__calls_get_new_results(mock_client): api = MockExportAPI() watcher = SubscriptionWatcher(api, mock_client) method_called = MockMethod([]) watcher._get_new_results = method_called.async_call # Shorten the wait watcher.BACK_OFF = 1 task = asyncio.get_event_loop().create_task(watcher_killer(watcher)) # Run watcher await watcher.run() await task assert method_called.called
def test_to_json_and_back(mock_client): test_watcher_file = "./test_subscription_watcher.json" if os.path.exists(test_watcher_file): os.remove(test_watcher_file) old_filename = SubscriptionWatcher.FILENAME SubscriptionWatcher.FILENAME = test_watcher_file api = MockExportAPI() latest_submissions = [ SubmissionBuilder(submission_id="123243").build_short_submission(), SubmissionBuilder(submission_id="123242").build_short_submission(), SubmissionBuilder(submission_id="123240").build_short_submission() ] subscription1 = Subscription("query", 1234) subscription2 = Subscription("example", 5678) watcher = SubscriptionWatcher(api, mock_client) watcher._update_latest_ids(latest_submissions) watcher.subscriptions.add(subscription1) watcher.subscriptions.add(subscription2) watcher.blocklists[3452] = {"test", "example"} watcher.blocklists[1453] = {"ych"} try: watcher.save_to_json() new_watcher = SubscriptionWatcher.load_from_json(api, mock_client) assert len(new_watcher.latest_ids) == 3 assert "123243" in new_watcher.latest_ids assert "123242" in new_watcher.latest_ids assert "123240" in new_watcher.latest_ids assert list(watcher.latest_ids) == list(new_watcher.latest_ids) assert len(new_watcher.subscriptions) == 2 list_subs = list(new_watcher.subscriptions) if list_subs[0].query_str == "query": assert list_subs[0].destination == 1234 assert list_subs[1].query_str == "example" assert list_subs[1].destination == 5678 else: assert list_subs[0].query_str == "example" assert list_subs[0].destination == 5678 assert list_subs[1].query_str == "query" assert list_subs[1].destination == 1234 assert len(new_watcher.blocklists) == 2 assert 3452 in new_watcher.blocklists assert len(new_watcher.blocklists[3452]) == 2 assert isinstance(new_watcher.blocklists[3452], set) assert "test" in new_watcher.blocklists[3452] assert "example" in new_watcher.blocklists[3452] assert 1453 in new_watcher.blocklists assert len(new_watcher.blocklists[1453]) == 1 assert isinstance(new_watcher.blocklists[1453], set) assert "ych" in new_watcher.blocklists[1453] finally: SubscriptionWatcher.FILENAME = old_filename os.remove(test_watcher_file)
async def test_run__sleeps_backoff_time(mock_client): api = MockExportAPI() watcher = SubscriptionWatcher(api, mock_client) # Shorten the wait watcher.BACK_OFF = 3 api.call_after_x_browse = (lambda: watcher.stop(), 2) # Run watcher start_time = datetime.datetime.now() await watcher.run() end_time = datetime.datetime.now() time_waited = end_time - start_time assert 3 <= time_waited.seconds <= 5
def test_save_to_json(mock_client): test_watcher_file = "./test_subscription_watcher.json" if os.path.exists(test_watcher_file): os.remove(test_watcher_file) api = MockExportAPI() latest_submissions = [ SubmissionBuilder(submission_id="123243").build_short_submission(), SubmissionBuilder(submission_id="123242").build_short_submission(), SubmissionBuilder(submission_id="123240").build_short_submission() ] subscription1 = Subscription("query", 1234) subscription2 = Subscription("example", 5678) watcher = SubscriptionWatcher(api, mock_client) watcher._update_latest_ids(latest_submissions) watcher.subscriptions.add(subscription1) watcher.subscriptions.add(subscription2) watcher.blocklists[3452] = {"test", "example"} watcher.blocklists[1453] = {"ych"} watcher.FILENAME = test_watcher_file try: watcher.save_to_json() assert os.path.exists(test_watcher_file) with open(test_watcher_file, "r") as f: data = json.load(f) assert data is not None assert len(data['latest_ids']) == 3 assert "123240" in data['latest_ids'] assert "123242" in data['latest_ids'] assert "123243" in data['latest_ids'] assert len(data["destinations"]) == 4 assert len(data["destinations"]["1234"]["subscriptions"]) == 1 assert data["destinations"]["1234"]["subscriptions"][0][ "query"] == "query" assert len(data["destinations"]["1234"]["blocks"]) == 0 assert len(data["destinations"]["5678"]["subscriptions"]) == 1 assert data["destinations"]["5678"]["subscriptions"][0][ "query"] == "example" assert len(data["destinations"]["5678"]["blocks"]) == 0 assert len(data["destinations"]["3452"]["subscriptions"]) == 0 assert len(data["destinations"]["3452"]["blocks"]) == 2 assert set([ block["query"] for block in data["destinations"]["3452"]["blocks"] ]) == {"test", "example"} assert len(data["destinations"]["1453"]["subscriptions"]) == 0 assert len(data["destinations"]["1453"]["blocks"]) == 1 assert data["destinations"]["1453"]["blocks"][0]["query"] == "ych" finally: os.remove(test_watcher_file)
async def test_send_updates__gathers_subscriptions(mock_client): api = MockExportAPI() watcher = SubscriptionWatcher(api, mock_client) subscription1 = Subscription("test", 12345) subscription2 = Subscription("test2", 12345) subscription3 = Subscription("test", 54321) submission = SubmissionBuilder().build_mock_submission() await watcher._send_updates([subscription1, subscription2, subscription3], submission) assert submission._send_message.call_count == 2 call_list = submission._send_message.call_args_list # Indifferent to call order, so figure out the order here call1 = call_list[0] call2 = call_list[1] if call1[0][1] != 12345: call1 = call_list[1] call2 = call_list[0] args1, kwargs1 = call1 args2, kwargs2 = call2 # Check call matching two subscriptions assert args1[0] == mock_client assert args2[0] == mock_client assert args1[1] == 12345 assert args2[1] == 54321 assert "update" in kwargs1['prefix'].lower() assert "\"test\", \"test2\"" in kwargs1['prefix'] assert "subscriptions:" in kwargs1['prefix'].lower() # And check the one subscription call assert "update" in kwargs2['prefix'].lower() assert "\"test\"" in kwargs2['prefix'] assert "subscription:" in kwargs2['prefix'].lower()
async def test_get_new_results__requests_only_page_one(mock_client): api = MockExportAPI() api.with_browse_results([MockSubmission("1254")], page=1) api.call_after_x_browse = (lambda *args: (_ for _ in ()).throw(Exception), 2) watcher = SubscriptionWatcher(api, mock_client) watcher.latest_ids.append("1250") watcher.running = True results = await watcher._get_new_results() assert len(results) == 4 assert results[0].submission_id == "1251" assert results[1].submission_id == "1252" assert results[2].submission_id == "1253" assert results[3].submission_id == "1254"
async def test_get_new_results__returns_new_results(mock_client): api = MockExportAPI() api.with_browse_results([ MockSubmission("1222"), MockSubmission("1221"), MockSubmission("1220") ]) watcher = SubscriptionWatcher(api, mock_client) watcher.latest_ids.append("1220") watcher.running = True results = await watcher._get_new_results() assert len(results) == 2 assert results[0].submission_id == "1221" assert results[1].submission_id == "1222"
async def test_run__can_exit_fast(mock_client): api = MockExportAPI() watcher = SubscriptionWatcher(api, mock_client) # Shorten the wait watcher.BACK_OFF = 3 task = asyncio.get_event_loop().create_task(watcher_killer(watcher)) # Run watcher start_time = datetime.datetime.now() await watcher.run() end_time = datetime.datetime.now() await task time_waited = end_time - start_time assert time_waited.seconds <= 1
def test_pause_subscription__case_insensitive(mock_client): api = MockExportAPI() watcher = SubscriptionWatcher(api, mock_client) watcher.subscriptions.add(Subscription("EXAMPLE", 18749)) watcher.subscriptions.add(Subscription("TEST", 18749)) func = SubscriptionFunctionality(watcher) list_subs = MockMethod("Listing subscriptions") func._list_subs = list_subs.call resp = func._pause_subscription(18749, "test") assert f"Paused subscription: \"test\"." in resp assert list_subs.called assert list_subs.args[0] == 18749 assert "Listing subscriptions" in resp assert len(watcher.subscriptions) == 2 sub1, sub2 = watcher.subscriptions if sub1.query_str != "TEST": sub1, sub2 = sub2, sub1 assert sub1.query_str == "TEST" assert sub1.destination == 18749 assert sub1.paused is True assert sub2.query_str == "EXAMPLE" assert sub2.destination == 18749 assert sub2.paused is False
async def test_send_updates__blocked_pauses_other_subs(mock_client): api = MockExportAPI() watcher = SubscriptionWatcher(api, mock_client) subscription1 = Subscription("test", 12345) subscription2 = Subscription("other", 12345) subscription3 = Subscription("not me", 54321) watcher.subscriptions = {subscription1, subscription2, subscription3} submission = SubmissionBuilder().build_mock_submission() submission.send_message = lambda *args, **kwargs: (_ for _ in ()).throw( UserIsBlockedError(None)) await watcher._send_updates([subscription1], submission) assert subscription1.paused assert subscription2.paused assert not subscription3.paused
def test_remove_sub__removes_subscription_case_insensitive(mock_client): api = MockExportAPI() watcher = SubscriptionWatcher(api, mock_client) watcher.subscriptions.add(Subscription("example", 18749)) watcher.subscriptions.add(Subscription("test", 18747)) new_sub = Subscription("test", 18749) new_sub.latest_update = datetime.datetime.now() watcher.subscriptions.add(new_sub) func = SubscriptionFunctionality(watcher) list_subs = MockMethod("Listing subscriptions") func._list_subs = list_subs.call resp = func._remove_sub(18749, "TEST") assert "Removed subscription: \"TEST\"." in resp assert list_subs.called assert list_subs.args[0] == 18749 assert "Listing subscriptions" in resp assert len(watcher.subscriptions) == 2 subscriptions = list(watcher.subscriptions) if subscriptions[0].query_str == "test": assert subscriptions[0].destination == 18747 assert subscriptions[1].query_str == "example" assert subscriptions[1].destination == 18749 else: assert subscriptions[0].query_str == "example" assert subscriptions[0].destination == 18749 assert subscriptions[1].query_str == "test" assert subscriptions[1].destination == 18747
def test_pause_destination__not_in_other_destination(mock_client): api = MockExportAPI() watcher = SubscriptionWatcher(api, mock_client) watcher.subscriptions.add(Subscription("test", 18749)) watcher.subscriptions.add(Subscription("example", 12345)) func = SubscriptionFunctionality(watcher) list_subs = MockMethod("Listing subscriptions") func._list_subs = list_subs.call resp = func._pause_destination(18749) assert "Paused all subscriptions." in resp assert len(watcher.subscriptions) == 2 assert list_subs.called assert list_subs.args[0] == 18749 assert "Listing subscriptions" in resp sub1, sub2 = list(watcher.subscriptions)[:2] if sub1.destination != 18749: sub2, sub1 = sub1, sub2 assert sub1.destination == 18749 assert sub1.query_str == "test" assert sub1.paused is True assert sub2.destination == 12345 assert sub2.query_str == "example" assert sub2.paused is False
def test_resume_subscription__one_matching(mock_client): api = MockExportAPI() watcher = SubscriptionWatcher(api, mock_client) sub1 = Subscription("example", 18749) sub1.paused = True watcher.subscriptions.add(sub1) sub2 = Subscription("test", 18749) sub2.paused = True watcher.subscriptions.add(sub2) func = SubscriptionFunctionality(watcher) list_subs = MockMethod("Listing subscriptions") func._list_subs = list_subs.call resp = func._resume_subscription(18749, "test") assert "Resumed subscription: \"test\"." in resp assert list_subs.called assert list_subs.args[0] == 18749 assert "Listing subscriptions" in resp assert len(watcher.subscriptions) == 2 sub1, sub2 = watcher.subscriptions if sub1.query_str != "test": sub1, sub2 = sub2, sub1 assert sub1.query_str == "test" assert sub1.destination == 18749 assert sub1.paused is False assert sub2.query_str == "example" assert sub2.destination == 18749 assert sub2.paused is True
async def test_run__failed_to_send_doesnt_kill_watcher(mock_client): submission = MockSubmission("12322") api = MockExportAPI().with_browse_results([submission], 1) watcher = SubscriptionWatcher(api, mock_client) submission.send_message = lambda *args: (_ for _ in ()).throw(Exception) watcher.BACK_OFF = 3 sub1 = MockSubscription("deer", 0) watcher.subscriptions = [sub1] api.call_after_x_browse = (lambda: watcher.stop(), 2) # Run watcher start_time = datetime.datetime.now() await watcher.run() end_time = datetime.datetime.now() time_waited = end_time - start_time assert 3 <= time_waited.seconds <= 5
async def test_get_new_results__handles_empty_latest_ids(mock_client): api = MockExportAPI() api.with_browse_results([ MockSubmission("1223"), MockSubmission("1222"), MockSubmission("1220") ]) watcher = SubscriptionWatcher(api, mock_client) watcher.running = True results = await watcher._get_new_results() assert len(results) == 0 assert len(watcher.latest_ids) == 3 assert watcher.latest_ids[0] == "1220" assert watcher.latest_ids[1] == "1222" assert watcher.latest_ids[2] == "1223"
async def test_get_new_results__handles_cloudflare(mock_client): api = MockExportAPI() def raise_cloudflare(*_, **__): raise CloudflareError() api.get_browse_page = raise_cloudflare watcher = SubscriptionWatcher(api, mock_client) watcher.BROWSE_RETRY_BACKOFF = 0.1 watcher.latest_ids.append("1225") watcher.running = True task = asyncio.get_event_loop().create_task(watcher_killer(watcher)) results = await watcher._get_new_results() await task assert len(results) == 0
def test_init(mock_client): api = MockExportAPI() s = SubscriptionWatcher(api, mock_client) assert s.api == api assert len(s.latest_ids) == 0 assert s.running is False assert len(s.subscriptions) == 0
def test_pause_destination__no_subs(mock_client): api = MockExportAPI() watcher = SubscriptionWatcher(api, mock_client) func = SubscriptionFunctionality(watcher) resp = func._pause_destination(18749) assert resp == "There are no subscriptions posting here to pause." assert len(watcher.subscriptions) == 0
def test_add_sub__invalid_query(mock_client): api = MockExportAPI() watcher = SubscriptionWatcher(api, mock_client) func = SubscriptionFunctionality(watcher) resp = func._add_sub(18749, "(hello") assert resp.startswith("Failed to parse subscription query") assert len(watcher.subscriptions) == 0
def test_add_sub__no_add_blank(mock_client): api = MockExportAPI() watcher = SubscriptionWatcher(api, mock_client) func = SubscriptionFunctionality(watcher) resp = func._add_sub(18749, "") assert resp == "Please specify the subscription query you wish to add." assert len(watcher.subscriptions) == 0
def test_add_to_blocklist__no_add_blank(mock_client): api = MockExportAPI() watcher = SubscriptionWatcher(api, mock_client) func = BlocklistFunctionality(watcher) resp = func._add_to_blocklist(18749, "") assert resp == "Please specify the tag you wish to add to blocklist." assert len(watcher.blocklists) == 0
async def test_send_updates__updates_latest(mock_client): api = MockExportAPI() watcher = SubscriptionWatcher(api, mock_client) subscription = Subscription("test", 12345) submission = SubmissionBuilder().build_mock_submission() await watcher._send_updates([subscription], submission) assert subscription.latest_update is not None
def test_add_sub__no_add_duplicate_case_insensitive(mock_client): api = MockExportAPI() watcher = SubscriptionWatcher(api, mock_client) watcher.subscriptions.add(Subscription("test", 18749)) func = SubscriptionFunctionality(watcher) resp = func._add_sub(18749, "TEST") assert resp == "A subscription already exists for \"TEST\"." assert len(watcher.subscriptions) == 1