async def test_username_with_colon(requests_mock): # FA doesn't allow usernames to have : in them username = "******" event = MockTelegramEvent.with_inline_query(query=f"gallery:{username}") inline = InlineFunctionality(MockExportAPI()) # mock export api doesn't do non-existent users, so mocking with requests inline.api = FAExportAPI("http://example.com", ignore_status=True) requests_mock.get(f"http://example.com/user/{username}/gallery.json", status_code=404) with pytest.raises(StopPropagation): await inline.call(event) event.answer.assert_called_once() args = event.answer.call_args[0] assert event.answer.call_args[1]['next_offset'] is None assert event.answer.call_args[1]['gallery'] is False assert isinstance(args[0], list) assert len(args[0]) == 1 assert isinstance(args[0][0], _MockInlineBuilder._MockInlineArticle) assert args[0][0].kwargs == { "title": "User does not exist.", "description": f"FurAffinity user does not exist by the name: \"{username}\".", "text": f"FurAffinity user does not exist by the name: \"{username}\".", }
async def test_no_username_set(requests_mock): username = "" event = MockTelegramEvent.with_inline_query(query=f"gallery:{username}") inline = InlineFunctionality(MockExportAPI()) # mock export api doesn't do non-existent users, so mocking with requests inline.api = FAExportAPI("http://example.com", ignore_status=True) requests_mock.get( f"http://example.com/user/{username}/gallery.json?page=1&full=1", json={ "id": None, "name": "gallery", "profile": "https://www.furaffinity.net/user/gallery/" }) with pytest.raises(StopPropagation): await inline.call(event) event.answer.assert_called_once() args = event.answer.call_args[0] assert event.answer.call_args[1]['next_offset'] is None assert event.answer.call_args[1]['gallery'] is False assert isinstance(args[0], list) assert len(args[0]) == 1 assert isinstance(args[0][0], _MockInlineBuilder._MockInlineArticle) assert args[0][0].kwargs == { "title": "User does not exist.", "description": f"FurAffinity user does not exist by the name: \"{username}\".", "text": f"FurAffinity user does not exist by the name: \"{username}\".", }
async def test_second_page(mock_client): post_id = 234563 username = "******" event = MockTelegramEvent.with_inline_query(query=f"scraps:{username}", offset="2") submission = MockSubmission(post_id) inline = InlineFunctionality(MockExportAPI()) inline.api.with_user_folder(username, "scraps", [submission], page=2) with pytest.raises(StopPropagation): await inline.call(event) event.answer.assert_called_once() args = event.answer.call_args[0] assert event.answer.call_args[1]['next_offset'] == "3" assert event.answer.call_args[1]['gallery'] is True assert isinstance(args[0], list) assert len(args[0]) == 1 assert isinstance(args[0][0], _MockInlineBuilder._MockInlinePhoto) assert args[0][0].kwargs['file'] == submission.thumbnail_url assert args[0][0].kwargs['id'] == str(post_id) assert args[0][0].kwargs['text'] == submission.link assert len(args[0][0].kwargs['buttons']) == 1 assert args[0][0].kwargs['buttons'][ 0].data == f"neaten_me:{submission.submission_id}".encode()
async def test_over_max_submissions_continue_end(mock_client): username = "******" posts = 72 post_ids = list(range(123456, 123456 + posts)) submissions = [MockSubmission(x) for x in post_ids] mock_api = MockExportAPI() mock_api.with_user_folder(username, "gallery", submissions) inline = InlineFunctionality(mock_api) skip = posts - inline.INLINE_MAX + 3 event = MockTelegramEvent.with_inline_query(query=f"gallery:{username}", offset=f"1:{skip}") with pytest.raises(StopPropagation): await inline.call(event) event.answer.assert_called_once() args = event.answer.call_args[0] assert event.answer.call_args[1]['next_offset'] == "2" assert event.answer.call_args[1]['gallery'] is True assert isinstance(args[0], list) assert len(args[0]) == inline.INLINE_MAX - 3 assert isinstance(args[0][0], _MockInlineBuilder._MockInlinePhoto) assert isinstance(args[0][1], _MockInlineBuilder._MockInlinePhoto) for x in range(inline.INLINE_MAX - 3): assert args[0][x].kwargs['file'] == submissions[x + skip].thumbnail_url assert args[0][x].kwargs['id'] == str(post_ids[x + skip]) assert args[0][x].kwargs['text'] == submissions[x + skip].link assert len(args[0][x].kwargs['buttons']) == 1 assert args[0][x].kwargs['buttons'][ 0].data == f"neaten_me:{submissions[x + skip].submission_id}".encode( )
async def test_search_with_offset(mock_client): post_id = 234563 search_term = "YCH" offset = 2 event = MockTelegramEvent.with_inline_query(query=search_term, offset=offset) submission = MockSubmission(post_id) inline = InlineFunctionality(MockExportAPI()) inline.api.with_search_results(search_term, [submission], page=offset) with pytest.raises(StopPropagation): await inline.call(event) event.answer.assert_called_once() args = event.answer.call_args[0] assert isinstance(args[0], list) assert len(args[0]) > 0 for result in args[0]: assert isinstance(result, _MockInlineBuilder._MockInlinePhoto) assert result.kwargs['file'] == submission.thumbnail_url assert result.kwargs['id'] == str(post_id) assert result.kwargs['text'] == submission.link assert len(result.kwargs['buttons']) == 1 assert result.kwargs['buttons'][ 0].data == f"neaten_me:{post_id}".encode()
async def test_get_user_gallery(mock_client): post_id1 = 234563 post_id2 = 393282 username = "******" event = MockTelegramEvent.with_inline_query(query=f"gallery:{username}") submission1 = MockSubmission(post_id1) submission2 = MockSubmission(post_id2) inline = InlineFunctionality(MockExportAPI()) inline.api.with_user_folder(username, "gallery", [submission1, submission2]) with pytest.raises(StopPropagation): await inline.call(event) event.answer.assert_called_once() args = event.answer.call_args[0] assert event.answer.call_args[1]['next_offset'] == "2" assert event.answer.call_args[1]['gallery'] is True assert isinstance(args[0], list) assert len(args[0]) == 2 assert isinstance(args[0][0], _MockInlineBuilder._MockInlinePhoto) assert isinstance(args[0][1], _MockInlineBuilder._MockInlinePhoto) assert args[0][0].kwargs['file'] == submission1.thumbnail_url assert args[0][0].kwargs['id'] == str(post_id1) assert args[0][0].kwargs['text'] == submission1.link assert len(args[0][0].kwargs['buttons']) == 1 assert args[0][0].kwargs['buttons'][ 0].data == f"neaten_me:{submission1.submission_id}".encode() assert args[0][1].kwargs['file'] == submission2.thumbnail_url assert args[0][1].kwargs['id'] == str(post_id2) assert args[0][1].kwargs['text'] == submission2.link assert len(args[0][1].kwargs['buttons']) == 1 assert args[0][1].kwargs['buttons'][ 0].data == f"neaten_me:{submission2.submission_id}".encode()
async def test_search_with_spaces(mock_client): search_term = "deer YCH" event = MockTelegramEvent.with_inline_query(query=search_term) post_id1 = 213231 post_id2 = 84331 submission1 = MockSubmission(post_id1) submission2 = MockSubmission(post_id2) inline = InlineFunctionality(MockExportAPI()) inline.api.with_search_results(search_term, [submission1, submission2]) with pytest.raises(StopPropagation): await inline.call(event) event.answer.assert_called_once() args = event.answer.call_args[0] assert event.answer.call_args[1]['next_offset'] == "2" assert event.answer.call_args[1]['gallery'] is True assert isinstance(args[0], list) assert len(args[0]) == 2 for result in args[0]: assert isinstance(result, _MockInlineBuilder._MockInlinePhoto) assert args[0][0].kwargs['file'] == submission1.thumbnail_url assert args[0][0].kwargs['id'] == str(post_id1) assert args[0][0].kwargs['text'] == submission1.link assert len(args[0][0].kwargs['buttons']) == 1 assert args[0][0].kwargs['buttons'][ 0].data == f"neaten_me:{submission1.submission_id}".encode() assert args[0][1].kwargs['file'] == submission2.thumbnail_url assert args[0][1].kwargs['id'] == str(post_id2) assert args[0][1].kwargs['text'] == submission2.link assert len(args[0][1].kwargs['buttons']) == 1 assert args[0][1].kwargs['buttons'][ 0].data == f"neaten_me:{submission2.submission_id}".encode()
async def test_over_max_favs(mock_client): username = "******" post_ids = list(range(123456, 123456 + 72)) submissions = [MockSubmission(x) for x in post_ids] inline = InlineFunctionality(MockExportAPI()) inline.api.with_user_favs(username, submissions) event = MockTelegramEvent.with_inline_query(query=f"favs:{username}") with pytest.raises(StopPropagation): await inline.call(event) event.answer.assert_called_once() args = event.answer.call_args[0] assert event.answer.call_args[1]['next_offset'] == submissions[ inline.INLINE_MAX - 1].fav_id assert event.answer.call_args[1]['gallery'] is True assert isinstance(args[0], list) assert len(args[0]) == inline.INLINE_MAX assert isinstance(args[0][0], _MockInlineBuilder._MockInlinePhoto) assert isinstance(args[0][1], _MockInlineBuilder._MockInlinePhoto) for x in range(inline.INLINE_MAX): assert args[0][x].kwargs['file'] == submissions[x].thumbnail_url assert args[0][x].kwargs['id'] == str(post_ids[x]) assert args[0][x].kwargs['text'] == submissions[x].link assert len(args[0][x].kwargs['buttons']) == 1 assert args[0][x].kwargs['buttons'][ 0].data == f"neaten_me:{submissions[x].submission_id}".encode()
async def test_empty_query_no_results(mock_client): event = MockTelegramEvent.with_inline_query(query="") inline = InlineFunctionality(MockExportAPI()) with pytest.raises(StopPropagation): await inline.call(event) event.answer.assert_called_with([])
def initialise_functionalities(self): return [ BeepFunctionality(), WelcomeFunctionality(), ImageHashRecommendFunctionality(), NeatenFunctionality(self.api), InlineNeatenFunctionality(self.api), InlineFunctionality(self.api), InlineEditFunctionality(self.api, self.client), InlineEditButtonPress(self.api), SubscriptionFunctionality(self.subscription_watcher), BlocklistFunctionality(self.subscription_watcher), SupergroupUpgradeFunctionality(self.subscription_watcher), UnhandledMessageFunctionality(), ]
async def test_search_with_offset_no_more_results(mock_client): search_term = "YCH" offset = 2 event = MockTelegramEvent.with_inline_query(query=search_term, offset=offset) inline = InlineFunctionality(MockExportAPI()) inline.api.with_search_results(search_term, [], page=offset) with pytest.raises(StopPropagation): await inline.call(event) event.answer.assert_called_once() args = event.answer.call_args[0] assert event.answer.call_args[1]['next_offset'] is None assert event.answer.call_args[1]['gallery'] is True assert isinstance(args[0], list) assert len(args[0]) == 0
async def test_over_max_submissions_continue_over_page(mock_client): username = "******" posts = 72 post_ids = list(range(123456, 123456 + posts)) skip = posts + 5 submissions = [MockSubmission(x) for x in post_ids] inline = InlineFunctionality(MockExportAPI()) inline.api.with_user_folder(username, "gallery", submissions) event = MockTelegramEvent.with_inline_query(query=f"gallery:{username}", offset=f"1:{skip}") with pytest.raises(StopPropagation): await inline.call(event) event.answer.assert_called_once() args = event.answer.call_args[0] assert event.answer.call_args[1]['next_offset'] == "2" assert event.answer.call_args[1]['gallery'] is True assert isinstance(args[0], list) assert len(args[0]) == 0
async def test_no_search_results(mock_client): search_term = "RareKeyword" event = MockTelegramEvent.with_inline_query(query=search_term) inline = InlineFunctionality(MockExportAPI()) inline.api.with_search_results(search_term, []) with pytest.raises(StopPropagation): await inline.call(event) event.answer.assert_called_once() args = event.answer.call_args[0] assert event.answer.call_args[1]['next_offset'] is None assert event.answer.call_args[1]['gallery'] is False assert isinstance(args[0], list) assert len(args[0]) == 1 assert isinstance(args[0][0], _MockInlineBuilder._MockInlineArticle) assert args[0][0].kwargs == { "title": "No results found.", "description": f"No results for search \"{search_term}\".", "text": f"No results for search \"{search_term}\".", }
async def test_empty_favs(mock_client): username = "******" event = MockTelegramEvent.with_inline_query(query=f"favs:{username}") inline = InlineFunctionality(MockExportAPI()) inline.api.with_user_favs(username, []) with pytest.raises(StopPropagation): await inline.call(event) event.answer.assert_called_once() args = event.answer.call_args[0] assert event.answer.call_args[1]['next_offset'] is None assert event.answer.call_args[1]['gallery'] is False assert isinstance(args[0], list) assert len(args[0]) == 1 assert isinstance(args[0][0], _MockInlineBuilder._MockInlineArticle) assert args[0][0].kwargs == { "title": "Nothing in favourites.", "description": f"There are no favourites for user \"{username}\".", "text": f"There are no favourites for user \"{username}\".", }
async def test_user_favourites_last_page(mock_client): # On the last page of favourites, if you specify "next", it repeats the same page, this simulates that. post_id1 = 234563 post_id2 = 393282 submission1 = MockSubmission(post_id1) submission2 = MockSubmission(post_id2) username = "******" event = MockTelegramEvent.with_inline_query(query=f"favourites:{username}", offset=submission2.fav_id) inline = InlineFunctionality(MockExportAPI()) inline.api.with_user_favs(username, [submission1, submission2], next_id=submission2.fav_id) with pytest.raises(StopPropagation): await inline.call(event) event.answer.assert_called_once() args = event.answer.call_args[0] assert event.answer.call_args[1]['next_offset'] is None assert event.answer.call_args[1]['gallery'] is True assert isinstance(args[0], list) assert len(args[0]) == 0
async def test_search_with_combo_characters(mock_client): search_term = "(deer & !ych) | (dragon & !ych)" event = MockTelegramEvent.with_inline_query(query=search_term) post_id = 213231 submission = MockSubmission(post_id) inline = InlineFunctionality(MockExportAPI()) inline.api.with_search_results(search_term, [submission]) with pytest.raises(StopPropagation): await inline.call(event) event.answer.assert_called_once() args = event.answer.call_args[0] assert event.answer.call_args[1]['next_offset'] == "2" assert event.answer.call_args[1]['gallery'] is True assert isinstance(args[0], list) assert len(args[0]) == 1 assert isinstance(args[0][0], _MockInlineBuilder._MockInlinePhoto) assert args[0][0].kwargs['file'] == submission.thumbnail_url assert args[0][0].kwargs['id'] == str(post_id) assert args[0][0].kwargs['text'] == submission.link assert len(args[0][0].kwargs['buttons']) == 1 assert args[0][0].kwargs['buttons'][ 0].data == f"neaten_me:{submission.submission_id}".encode()