def test_handles_wrongly_formatted_response( mocker, esv_provider: ESVApiTranslationProvider): result = query_result_for_json([{'references': ['John.1.1'], 'words': []}]) mock_response(mocker, lambda: {'wrong': 'keys'}) with pytest.raises(ProbableBugError) as excinfo: esv_provider.add_translations(result) assert excinfo.value.message == 'Could not understand response from ESV API'
def test_handles_wrongly_formatted_query_result( mocker, esv_provider: ESVApiTranslationProvider): result = query_result_for_json([{'references': [], 'words': []}]) mock_response(mocker, lambda: None) with pytest.raises(ProbableBugError) as excinfo: esv_provider.add_translations(result) assert excinfo.value.message == 'Result has no references'
def test_chunks_translation_api_requests_by_verse_number( mocker, esv_provider: ESVApiTranslationProvider): result = query_result_for_json([{ 'references': ['John.1.1'], 'words': [] } for _ in range(900)]) mock_get = mock_response( mocker, lambda: {'passages': ['text of John.1.1' for _ in range(300)]}) esv_provider.add_translations(result) assert mock_get.call_count == 3
def test_includes_proper_request_params( mocker, esv_provider: ESVApiTranslationProvider): result = query_result_for_json([{'references': ['John.1.1'], 'words': []}]) mock_get = mock_response(mocker, lambda: {'passages': ['text']}) esv_provider.add_translations(result) get_params = mock_get.call_args.kwargs['params'] assert get_params['include-passage-references'] == 'false' assert get_params['include-verse-numbers'] == 'false' assert get_params['include-first-verse-numbers'] == 'false' assert get_params['include-footnotes'] == 'false' assert get_params['include-headings'] == 'false' assert get_params['include-short-copyright'] == 'true' assert get_params['indent-paragraphs'] == '0'
def test_handles_content_type_error(mocker, esv_provider: ESVApiTranslationProvider): mock_exception = make_mock_exception(mocker, 'aiohttp.ContentTypeError') def raise_mock_exception(): raise mock_exception mock_response(mocker, raise_mock_exception) result = query_result_for_json([{'references': ['John.1.1'], 'words': []}]) with pytest.raises(ServerOverwhelmedError) as excinfo: esv_provider.add_translations(result) assert excinfo.value.message == 'ESV API did not return JSON'
def test_handles_quota_exceeded(mocker, esv_provider: ESVApiTranslationProvider): mock_exception = make_mock_exception(mocker, 'aiohttp.ClientResponseError') mock_exception.status = 502 def raise_mock_exception(): raise mock_exception mock_response(mocker, raise_mock_exception) result = query_result_for_json([{'references': ['John.1.1'], 'words': []}]) with pytest.raises(ServerOverwhelmedError) as excinfo: esv_provider.add_translations(result) assert excinfo.value.message == 'ESV API quota exceeded'
def test_chunks_translation_api_request_by_query_string_length( mocker, esv_provider: ESVApiTranslationProvider): long_reference = ''.join(['a' for _ in range(3861)]) + '.1.1' result = query_result_for_json([ { 'references': [long_reference], 'words': [], }, { 'references': [long_reference], 'words': [], }, ]) mock_get = mock_response(mocker, lambda: {'passages': ['text']}) esv_provider.add_translations(result) assert mock_get.call_count == 2
def test_properly_formats_verse_query_string( mocker, esv_provider: ESVApiTranslationProvider): result = query_result_for_json([{ 'references': ['Mark.1.1'], 'words': [] }, { 'references': ['Matt.1.1', 'Matt.1.2'], 'words': [] }, { 'references': ['John.1.1', 'John.1.2', 'John.1.3'], 'words': [] }]) mock_get = mock_response(mocker, lambda: {'passages': ['text' for _ in range(3)]}) esv_provider.add_translations(result) get_params = mock_get.call_args.kwargs['params'] assert get_params['q'] == 'Mark.1.1;Matt.1.1-Matt.1.2;John.1.1-John.1.3'
def test_gets_translations_for_few_results( mocker, esv_provider: ESVApiTranslationProvider): result = query_result_for_json([{ 'references': ['Mark.1.1'], 'words': [] }, { 'references': ['Matt.1.1', 'Matt.1.2'], 'words': [] }]) mock_response( mocker, lambda: {'passages': [ 'text of Mark.1.1', 'text of Matt.1.1-Matt.1.2', ]}) esv_provider.add_translations(result) assert result.passages[0].translation == 'text of Mark.1.1' assert result.passages[1].translation == 'text of Matt.1.1-Matt.1.2'
from flask_limiter import Limiter from typing import Any, Dict, Union from translation_providers.ESVApiTranslationProvider import ESVApiTranslationProvider from text_providers.Nestle1904LowfatProvider import Nestle1904LowfatProvider from text_providers.TextProvider import TextProvider from werkzeug.http import HTTP_STATUS_CODES from AnoixoError import AnoixoError, ProbableBugError, TooManyRequestsError from TextQuery import TextQuery from translation_providers.TranslationProvider import TranslationProvider app = Flask(__name__) CORS(app) text_providers: Dict[str, TextProvider] = {'nlf': Nestle1904LowfatProvider()} translation_providers: Dict[str, TranslationProvider] = { 'esv': ESVApiTranslationProvider() } def _get_address_for_request(): """ In the default Nginx configuration for Anoixo, this Flask server is behind a proxy, and Nginx sets a 'X-Real-Ip' header with the original remote address whenever it forwards requests to Flask. If that header is not present (e.g. in a testing/development situation where a proxy server isn't set up), we are presumably not behind a proxy and can just use the original remote address. Please note this system means that IP addresses can be easily spoofed if the app is deployed NOT behind a proxy! All the spoofer would need to do is set a bogus X-Real-Ip header. So please make sure to deploy Anoixo behind a proxy in production environments.
def test_requests_proper_endpoint(mocker, esv_provider: ESVApiTranslationProvider): result = query_result_for_json([{'references': ['John.1.1'], 'words': []}]) mock_get = mock_response(mocker, lambda: {'passages': ['text']}) esv_provider.add_translations(result) assert mock_get.call_args.args[0] == 'https://api.esv.org/v3/passage/text'
def esv_provider(): return ESVApiTranslationProvider()