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
Пример #16
0
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
Пример #19
0
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
Пример #20
0
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
Пример #25
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