def test_api_request(requests_mock): api_url = "http://example.com/" path = "/resources/123" api = FAExportAPI(api_url, ignore_status=True) test_obj = {"key": "value"} requests_mock.get("http://example.com/resources/123", json=test_obj) resp = api._api_request(path) assert resp.json() == test_obj
async def test_get_status_before_submission(requests_mock): builder = SubmissionBuilder(thumb_size=300) api = FAExportAPI("http://example.com/") requests_mock.get("http://example.com/status.json", json={ "online": { "guests": 17, "registered": api.STATUS_LIMIT_REGISTERED - 1, "other": 12, "total": api.STATUS_LIMIT_REGISTERED + 28 }, "fa_server_time_at": "2020-09-08T00:13:14Z" }) requests_mock.get( f"http://example.com/submission/{builder.submission_id}.json", json=builder.build_submission_json()) submission = await api.get_full_submission(builder.submission_id) assert api.last_status_check is not None assert api.slow_down_status is False assert isinstance(submission, FASubmissionFull) assert submission.submission_id == builder.submission_id assert submission.link == builder.link assert submission.thumbnail_url == builder.thumbnail_url.replace( "@300-", "@1600-") assert submission.download_url == builder.download_url assert submission.full_image_url == builder.download_url
async def test_get_status_api_retry(requests_mock): api_url = "http://example.com/" path = "/resources/200" api = FAExportAPI(api_url) test_obj = {"key": "value"} requests_mock.get("http://example.com/status.json", json={ "online": { "guests": 17, "registered": api.STATUS_LIMIT_REGISTERED - 1, "other": 12, "total": api.STATUS_LIMIT_REGISTERED + 28 }, "fa_server_time_at": "2020-09-08T00:13:14Z" }) requests_mock.get("http://example.com/resources/200", [ { "json": test_obj, "status_code": 200 }, ]) resp = await api._api_request_with_retry(path) assert api.last_status_check is not None assert api.slow_down_status is False assert resp.status_code == 200 assert resp.json() == test_obj
async def test_get_status_turns_on_slowdown(requests_mock): api_url = "http://example.com/" path = "/resources/200" api = FAExportAPI(api_url) test_obj = {"key": "value"} requests_mock.get("http://example.com/status.json", json={ "online": { "guests": 17, "registered": api.STATUS_LIMIT_REGISTERED + 1, "other": 12, "total": api.STATUS_LIMIT_REGISTERED + 30 }, "fa_server_time_at": "2020-09-08T00:13:14Z" }) requests_mock.get("http://example.com/resources/200", [ { "json": test_obj, "status_code": 200 }, ]) start_time = datetime.datetime.now() resp = await api._api_request_with_retry(path) end_time = datetime.datetime.now() assert api.last_status_check is not None assert api.slow_down_status is True time_waited = end_time - start_time assert time_waited.seconds >= 1 assert resp.status_code == 200 assert resp.json() == test_obj
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_custom_icon_not_recognised(): default_icon_sub = "42085964" api = FAExportAPI("https://faexport.spangle.org.uk") full = await api.get_full_submission(default_icon_sub) sub = FASubmissionFullWithDefaultStoryIconDetection.from_sub_full(full) assert not sub._has_default_story_pic()
async def test_get_browse_page_no_results(requests_mock): post_id = "32342" api = FAExportAPI("http://example.com/", ignore_status=True) requests_mock.get(f"http://example.com/browse.json?page=5", json=[]) results = await api.get_browse_page(5) assert len(results) == 0
def test_api_request__detects_cloudflare(requests_mock): api_url = "http://example.com/" path = "/resources/123" api = FAExportAPI(api_url, ignore_status=True) requests_mock.get( "http://example.com/resources/123", status_code=503, json={ "error": "Cannot access FA, https://www.furaffinity.net/ as cloudflare protection is up", "url": "https://www.furaffinity.net/" }) try: api._api_request(path) assert False, "Should have thrown cloudflare error" except CloudflareError as e: pass
async def test_get_user_folder_empty(requests_mock): username = "******" api = FAExportAPI("http://example.com/", ignore_status=True) requests_mock.get( f"http://example.com/user/{username}/gallery.json?page=1&full=1", json=[]) results = await api.get_user_folder(username, "gallery") assert len(results) == 0
async def test_get_search_results_no_results(requests_mock): search = "chital_deer" api = FAExportAPI("http://example.com/", ignore_status=True) requests_mock.get( f"http://example.com/search.json?full=1&perpage=48&q={search}&page=1", json=[]) results = await api.get_search_results(search) assert len(results) == 0
def __init__(self, conf_file): with open(conf_file, 'r') as f: self.config = Config.from_dict(json.load(f)) self.api = FAExportAPI(self.config.fa_api_url) self.client = None self.alive = False self.functionalities = [] self.subscription_watcher = None self.subscription_watcher_thread = None self.log_task = None self.watcher_task = None
async def test_get_browse_page_specify_page(requests_mock): builder = SubmissionBuilder() api = FAExportAPI("http://example.com/", ignore_status=True) requests_mock.get(f"http://example.com/browse.json?page=5", json=[builder.build_search_json()]) results = await api.get_browse_page(5) assert len(results) == 1 assert isinstance(results[0], FASubmissionShort) assert results[0].submission_id == builder.submission_id
async def test_get_full_submission_fails(requests_mock): post_id = "45282" api = FAExportAPI("http://example.com/", ignore_status=True) requests_mock.get(f"http://example.com/submission/{post_id}.json", status_code=404) try: await api.get_full_submission(post_id) assert False, "Should have thrown exception" except PageNotFound as e: assert str(e) == f"Submission not found with ID: {post_id}"
async def test_get_user_folder_does_not_exist(requests_mock): username = "******" api = FAExportAPI("http://example.com/", ignore_status=True) requests_mock.get( f"http://example.com/user/{username}/gallery.json?page=1&full=1", status_code=404) try: await api.get_user_folder(username, "gallery") assert False, "Should have thrown exception" except PageNotFound as e: assert str(e) == f"User not found by name: {username}"
async def test_get_search_results_specified_page(requests_mock): builder = SubmissionBuilder() search = "deer" api = FAExportAPI("http://example.com/", ignore_status=True) requests_mock.get( f"http://example.com/search.json?full=1&perpage=48&q={search}&page=2", json=[builder.build_search_json()]) results = await api.get_search_results(search, 2) assert len(results) == 1 assert isinstance(results[0], FASubmissionShort) assert results[0].submission_id == builder.submission_id
async def test_get_search_results_with_extended_modifiers(requests_mock): builder = SubmissionBuilder() search = "(deer & !lion) | (goat & !tiger)" search_safe = "(deer%20&%20!lion)%20%7C%20(goat%20&%20!tiger)" api = FAExportAPI("http://example.com/", ignore_status=True) requests_mock.get( f"http://example.com/search.json?full=1&perpage=48&q={search_safe}&page=1", json=[builder.build_search_json()]) results = await api.get_search_results(search) assert len(results) == 1 assert isinstance(results[0], FASubmissionShort) assert results[0].submission_id == builder.submission_id
async def test_get_user_folder_specified_page(requests_mock): builder = SubmissionBuilder() username = "******" api = FAExportAPI("http://example.com/", ignore_status=True) requests_mock.get( f"http://example.com/user/{username}/gallery.json?page=2&full=1", json=[builder.build_search_json()]) results = await api.get_user_folder(username, "gallery", 2) assert len(results) == 1 assert isinstance(results[0], FASubmissionShort) assert results[0].submission_id == builder.submission_id assert results[0].link == builder.link assert results[0].thumbnail_url == builder.thumbnail_url
async def test_get_full_submission(requests_mock): builder = SubmissionBuilder(thumb_size=300) api = FAExportAPI("http://example.com/", ignore_status=True) requests_mock.get( f"http://example.com/submission/{builder.submission_id}.json", json=builder.build_submission_json()) submission = await api.get_full_submission(builder.submission_id) assert isinstance(submission, FASubmissionFull) assert submission.submission_id == builder.submission_id assert submission.link == builder.link assert submission.thumbnail_url == builder.thumbnail_url.replace( "@300-", "@1600-") assert submission.download_url == builder.download_url assert submission.full_image_url == builder.download_url
async def test_get_user_folder_awkward_characters(requests_mock): builder = SubmissionBuilder() username = "******" safe_username = "******" api = FAExportAPI("http://example.com/", ignore_status=True) requests_mock.get( f"http://example.com/user/{safe_username}/gallery.json?page=1&full=1", json=[builder.build_search_json()]) results = await api.get_user_folder(username, "gallery") assert len(results) == 1 assert isinstance(results[0], FASubmissionShort) assert results[0].submission_id == builder.submission_id assert results[0].link == builder.link assert results[0].thumbnail_url == builder.thumbnail_url
async def test_get_user_folder_blank_username(requests_mock): username = "" 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/" }) try: await api.get_user_folder(username, "gallery") assert False, "Should have thrown exception" except PageNotFound as e: assert str(e) == f"User not found by name: {username}"
async def test_get_user_folder(requests_mock): post_id1 = "32342" post_id2 = "32337" builder1 = SubmissionBuilder(submission_id=post_id1) builder2 = SubmissionBuilder(submission_id=post_id2) username = "******" api = FAExportAPI("http://example.com/", ignore_status=True) requests_mock.get( f"http://example.com/user/{username}/gallery.json?page=1&full=1", json=[builder1.build_search_json(), builder2.build_search_json()]) results = await api.get_user_folder(username, "gallery") assert len(results) == 2 for result in results: assert isinstance(result, FASubmissionShort) assert results[0].submission_id == post_id1 assert results[1].submission_id == post_id2 assert results[0].link == f"https://furaffinity.net/view/{post_id1}/" assert results[1].link == f"https://furaffinity.net/view/{post_id2}/" assert results[0].thumbnail_url == builder1.thumbnail_url assert results[1].thumbnail_url == builder2.thumbnail_url
async def test_api_request_with_retry__does_not_retry_400_error(requests_mock): api_url = "http://example.com/" path = "/resources/400" api = FAExportAPI(api_url, ignore_status=True) requests_mock.get("http://example.com/resources/400", [ { "text": "400 error, you messed up.", "status_code": 400 }, { "text": "500 Errorequests_mock. Something broke.", "status_code": 500 }, ]) start_time = datetime.datetime.now() resp = await api._api_request_with_retry(path) end_time = datetime.datetime.now() time_waited = end_time - start_time assert time_waited.seconds <= 1 assert resp.status_code == 400 assert resp.text == "400 error, you messed up."
async def test_get_search_results(requests_mock): post_id1 = "32342" post_id2 = "32337" builder1 = SubmissionBuilder(submission_id=post_id1) builder2 = SubmissionBuilder(submission_id=post_id2) search = "deer" api = FAExportAPI("http://example.com/", ignore_status=True) requests_mock.get( f"http://example.com/search.json?full=1&perpage=48&q={search}&page=1", json=[builder1.build_search_json(), builder2.build_search_json()]) results = await api.get_search_results(search) assert len(results) == 2 assert isinstance(results[0], FASubmissionShort) assert isinstance(results[1], FASubmissionShort) assert results[0].submission_id == post_id1 assert results[1].submission_id == post_id2 assert results[0].link == f"https://furaffinity.net/view/{post_id1}/" assert results[1].link == f"https://furaffinity.net/view/{post_id2}/" assert results[0].thumbnail_url == builder1.thumbnail_url assert results[1].thumbnail_url == builder2.thumbnail_url
async def test_api_request_with_retry__does_not_retry_200(requests_mock): api_url = "http://example.com/" path = "/resources/200" api = FAExportAPI(api_url, ignore_status=True) test_obj = {"key": "value"} requests_mock.get("http://example.com/resources/200", [ { "json": test_obj, "status_code": 200 }, { "text": "500 Errorequests_mock. Something broke.", "status_code": 500 }, ]) start_time = datetime.datetime.now() resp = await api._api_request_with_retry(path) end_time = datetime.datetime.now() time_waited = end_time - start_time assert time_waited.seconds <= 1 assert resp.status_code == 200 assert resp.json() == test_obj
def test_constructor(): api_url = "http://example.com/" api = FAExportAPI(api_url, ignore_status=True) assert api.base_url == "http://example.com"