diff --git a/CHANGELOG.md b/CHANGELOG.md index 897aef89a5..5994be09c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +### 35.3.6 [#984](https://github.com/openfisca/openfisca-core/pull/984) + +#### Technical changes + +- In web_api tests, extract `test_client` to a fixture reusable by all the tests in the test suite. + - To mitigate possible performance issues, by default the fixture is initialised once per test module. + - This follows the same approach as [#997](https://github.com/openfisca/openfisca-core/pull/997) + + ### 35.3.5 [#997](https://github.com/openfisca/openfisca-core/pull/997) #### Technical changes diff --git a/conftest.py b/conftest.py index ae14b71c8a..3adc794111 100644 --- a/conftest.py +++ b/conftest.py @@ -1,4 +1,5 @@ pytest_plugins = [ + "tests.fixtures.appclient", "tests.fixtures.entities", "tests.fixtures.simulations", "tests.fixtures.taxbenefitsystems", diff --git a/setup.py b/setup.py index 601c0e7493..3411528df7 100644 --- a/setup.py +++ b/setup.py @@ -35,7 +35,7 @@ setup( name = 'OpenFisca-Core', - version = '35.3.5', + version = '35.3.6', author = 'OpenFisca Team', author_email = 'contact@openfisca.org', classifiers = [ diff --git a/tests/fixtures/appclient.py b/tests/fixtures/appclient.py new file mode 100644 index 0000000000..32d9e71aee --- /dev/null +++ b/tests/fixtures/appclient.py @@ -0,0 +1,31 @@ +import pytest + +from openfisca_web_api import app + + +@pytest.fixture(scope="module") +def test_client(tax_benefit_system): + """ This module-scoped fixture creates an API client for the TBS defined in the `tax_benefit_system` + fixture. This `tax_benefit_system` is mutable, so you can add/update variables. Example: + + ``` + from openfisca_country_template import entities + from openfisca_core import periods + from openfisca_core.variables import Variable + ... + + class new_variable(Variable): + value_type = float + entity = entities.Person + definition_period = periods.MONTH + label = "New variable" + reference = "https://law.gov.example/new_variable" # Always use the most official source + + tax_benefit_system.add_variable(new_variable) + flask_app = app.create_app(tax_benefit_system) + ``` + """ + + # Create the test API client + flask_app = app.create_app(tax_benefit_system) + return flask_app.test_client() diff --git a/tests/web_api/__init__.py b/tests/web_api/__init__.py index c98d824b4a..8098c2a5a2 100644 --- a/tests/web_api/__init__.py +++ b/tests/web_api/__init__.py @@ -1,10 +1,4 @@ -# -*- coding: utf-8 -*- - import pkg_resources -from openfisca_web_api.app import create_app -from openfisca_core.scripts import build_tax_benefit_system TEST_COUNTRY_PACKAGE_NAME = 'openfisca_country_template' distribution = pkg_resources.get_distribution(TEST_COUNTRY_PACKAGE_NAME) -tax_benefit_system = build_tax_benefit_system(TEST_COUNTRY_PACKAGE_NAME, extensions = None, reforms = None) -subject = create_app(tax_benefit_system).test_client() diff --git a/tests/web_api/test_calculate.py b/tests/web_api/test_calculate.py index 06d8b84af8..061c9e3e5d 100644 --- a/tests/web_api/test_calculate.py +++ b/tests/web_api/test_calculate.py @@ -1,28 +1,23 @@ -# -*- coding: utf-8 -*- - -import os +import copy +import dpath import json -from http.client import OK, BAD_REQUEST, NOT_FOUND, INTERNAL_SERVER_ERROR -from copy import deepcopy - +from http import client +import os import pytest -import dpath from openfisca_country_template.situation_examples import couple -from . import subject - -def post_json(data = None, file = None): +def post_json(client, data = None, file = None): if file: file_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'assets', file) with open(file_path, 'r') as file: data = file.read() - return subject.post('/calculate', data = data, content_type = 'application/json') + return client.post('/calculate', data = data, content_type = 'application/json') -def check_response(data, expected_error_code, path_to_check, content_to_check): - response = post_json(data) +def check_response(client, data, expected_error_code, path_to_check, content_to_check): + response = post_json(client, data) assert response.status_code == expected_error_code json_response = json.loads(response.data.decode('utf-8')) if path_to_check: @@ -31,32 +26,32 @@ def check_response(data, expected_error_code, path_to_check, content_to_check): @pytest.mark.parametrize("test", [ - ('{"a" : "x", "b"}', BAD_REQUEST, 'error', 'Invalid JSON'), - ('["An", "array"]', BAD_REQUEST, 'error', 'Invalid type'), - ('{"persons": {}}', BAD_REQUEST, 'persons', 'At least one person'), - ('{"persons": {"bob": {}}, "unknown_entity": {}}', BAD_REQUEST, 'unknown_entity', 'entities are not found',), - ('{"persons": {"bob": {}}, "households": {"dupont": {"parents": {}}}}', BAD_REQUEST, 'households/dupont/parents', 'type',), - ('{"persons": {"bob": {"unknown_variable": {}}}}', NOT_FOUND, 'persons/bob/unknown_variable', 'You tried to calculate or to set',), - ('{"persons": {"bob": {"housing_allowance": {}}}}', BAD_REQUEST, 'persons/bob/housing_allowance', "You tried to compute the variable 'housing_allowance' for the entity 'persons'",), - ('{"persons": {"bob": {"salary": 4000 }}}', BAD_REQUEST, 'persons/bob/salary', 'period',), - ('{"persons": {"bob": {"salary": {"2017-01": "toto"} }}}', BAD_REQUEST, 'persons/bob/salary/2017-01', 'expected type number',), - ('{"persons": {"bob": {"salary": {"2017-01": {}} }}}', BAD_REQUEST, 'persons/bob/salary/2017-01', 'expected type number',), - ('{"persons": {"bob": {"age": {"2017-01": "toto"} }}}', BAD_REQUEST, 'persons/bob/age/2017-01', 'expected type integer',), - ('{"persons": {"bob": {"birth": {"2017-01": "toto"} }}}', BAD_REQUEST, 'persons/bob/birth/2017-01', 'Can\'t deal with date',), - ('{"persons": {"bob": {}}, "households": {"household": {"parents": ["unexpected_person_id"]}}}', BAD_REQUEST, 'households/household/parents', 'has not been declared in persons',), - ('{"persons": {"bob": {}}, "households": {"household": {"parents": ["bob", "bob"]}}}', BAD_REQUEST, 'households/household/parents', 'has been declared more than once',), - ('{"persons": {"bob": {}}, "households": {"household": {"parents": ["bob", {}]}}}', BAD_REQUEST, 'households/household/parents/1', 'Invalid type',), - ('{"persons": {"bob": {"salary": {"invalid period": 2000 }}}}', BAD_REQUEST, 'persons/bob/salary', 'Expected a period',), - ('{"persons": {"bob": {"salary": {"invalid period": null }}}}', BAD_REQUEST, 'persons/bob/salary', 'Expected a period',), - ('{"persons": {"bob": {"basic_income": {"2017": 2000 }}}, "households": {"household": {"parents": ["bob"]}}}', BAD_REQUEST, 'persons/bob/basic_income/2017', '"basic_income" can only be set for one month',), - ('{"persons": {"bob": {"salary": {"ETERNITY": 2000 }}}, "households": {"household": {"parents": ["bob"]}}}', BAD_REQUEST, 'persons/bob/salary/ETERNITY', 'salary is only defined for months',), - ('{"persons": {"alice": {}, "bob": {}, "charlie": {}}, "households": {"_": {"parents": ["alice", "bob", "charlie"]}}}', BAD_REQUEST, 'households/_/parents', 'at most 2 parents in a household',), + ('{"a" : "x", "b"}', client.BAD_REQUEST, 'error', 'Invalid JSON'), + ('["An", "array"]', client.BAD_REQUEST, 'error', 'Invalid type'), + ('{"persons": {}}', client.BAD_REQUEST, 'persons', 'At least one person'), + ('{"persons": {"bob": {}}, "unknown_entity": {}}', client.BAD_REQUEST, 'unknown_entity', 'entities are not found',), + ('{"persons": {"bob": {}}, "households": {"dupont": {"parents": {}}}}', client.BAD_REQUEST, 'households/dupont/parents', 'type',), + ('{"persons": {"bob": {"unknown_variable": {}}}}', client.NOT_FOUND, 'persons/bob/unknown_variable', 'You tried to calculate or to set',), + ('{"persons": {"bob": {"housing_allowance": {}}}}', client.BAD_REQUEST, 'persons/bob/housing_allowance', "You tried to compute the variable 'housing_allowance' for the entity 'persons'",), + ('{"persons": {"bob": {"salary": 4000 }}}', client.BAD_REQUEST, 'persons/bob/salary', 'period',), + ('{"persons": {"bob": {"salary": {"2017-01": "toto"} }}}', client.BAD_REQUEST, 'persons/bob/salary/2017-01', 'expected type number',), + ('{"persons": {"bob": {"salary": {"2017-01": {}} }}}', client.BAD_REQUEST, 'persons/bob/salary/2017-01', 'expected type number',), + ('{"persons": {"bob": {"age": {"2017-01": "toto"} }}}', client.BAD_REQUEST, 'persons/bob/age/2017-01', 'expected type integer',), + ('{"persons": {"bob": {"birth": {"2017-01": "toto"} }}}', client.BAD_REQUEST, 'persons/bob/birth/2017-01', 'Can\'t deal with date',), + ('{"persons": {"bob": {}}, "households": {"household": {"parents": ["unexpected_person_id"]}}}', client.BAD_REQUEST, 'households/household/parents', 'has not been declared in persons',), + ('{"persons": {"bob": {}}, "households": {"household": {"parents": ["bob", "bob"]}}}', client.BAD_REQUEST, 'households/household/parents', 'has been declared more than once',), + ('{"persons": {"bob": {}}, "households": {"household": {"parents": ["bob", {}]}}}', client.BAD_REQUEST, 'households/household/parents/1', 'Invalid type',), + ('{"persons": {"bob": {"salary": {"invalid period": 2000 }}}}', client.BAD_REQUEST, 'persons/bob/salary', 'Expected a period',), + ('{"persons": {"bob": {"salary": {"invalid period": null }}}}', client.BAD_REQUEST, 'persons/bob/salary', 'Expected a period',), + ('{"persons": {"bob": {"basic_income": {"2017": 2000 }}}, "households": {"household": {"parents": ["bob"]}}}', client.BAD_REQUEST, 'persons/bob/basic_income/2017', '"basic_income" can only be set for one month',), + ('{"persons": {"bob": {"salary": {"ETERNITY": 2000 }}}, "households": {"household": {"parents": ["bob"]}}}', client.BAD_REQUEST, 'persons/bob/salary/ETERNITY', 'salary is only defined for months',), + ('{"persons": {"alice": {}, "bob": {}, "charlie": {}}, "households": {"_": {"parents": ["alice", "bob", "charlie"]}}}', client.BAD_REQUEST, 'households/_/parents', 'at most 2 parents in a household',), ]) -def test_responses(test): - check_response(*test) +def test_responses(test_client, test): + check_response(test_client, *test) -def test_basic_calculation(): +def test_basic_calculation(test_client): simulation_json = json.dumps({ "persons": { "bill": { @@ -101,8 +96,8 @@ def test_basic_calculation(): } }) - response = post_json(simulation_json) - assert response.status_code == OK + response = post_json(test_client, simulation_json) + assert response.status_code == client.OK response_json = json.loads(response.data.decode('utf-8')) assert dpath.get(response_json, 'persons/bill/basic_income/2017-12') == 600 # Universal basic income assert dpath.get(response_json, 'persons/bill/income_tax/2017-12') == 300 # 15% of the salary @@ -112,7 +107,7 @@ def test_basic_calculation(): assert dpath.get(response_json, 'households/first_household/housing_tax/2017') == 3000 -def test_enums_sending_identifier(): +def test_enums_sending_identifier(test_client): simulation_json = json.dumps({ "persons": { "bill": {} @@ -133,13 +128,13 @@ def test_enums_sending_identifier(): } }) - response = post_json(simulation_json) - assert response.status_code == OK + response = post_json(test_client, simulation_json) + assert response.status_code == client.OK response_json = json.loads(response.data.decode('utf-8')) assert dpath.get(response_json, 'households/_/housing_tax/2017') == 0 -def test_enum_output(): +def test_enum_output(test_client): simulation_json = json.dumps({ "persons": { "bill": {}, @@ -154,13 +149,13 @@ def test_enum_output(): } }) - response = post_json(simulation_json) - assert response.status_code == OK + response = post_json(test_client, simulation_json) + assert response.status_code == client.OK response_json = json.loads(response.data.decode('utf-8')) assert dpath.get(response_json, "households/_/housing_occupancy_status/2017-01") == "tenant" -def test_enum_wrong_value(): +def test_enum_wrong_value(test_client): simulation_json = json.dumps({ "persons": { "bill": {}, @@ -175,15 +170,15 @@ def test_enum_wrong_value(): } }) - response = post_json(simulation_json) - assert response.status_code == BAD_REQUEST + response = post_json(test_client, simulation_json) + assert response.status_code == client.BAD_REQUEST response_json = json.loads(response.data.decode('utf-8')) message = "Possible values are ['owner', 'tenant', 'free_lodger', 'homeless']" text = dpath.get(response_json, "households/_/housing_occupancy_status/2017-01") assert message in text -def test_encoding_variable_value(): +def test_encoding_variable_value(test_client): simulation_json = json.dumps({ "persons": { "toto": {} @@ -202,15 +197,15 @@ def test_encoding_variable_value(): }) # No UnicodeDecodeError - response = post_json(simulation_json) - assert response.status_code == BAD_REQUEST, response.data.decode('utf-8') + response = post_json(test_client, simulation_json) + assert response.status_code == client.BAD_REQUEST, response.data.decode('utf-8') response_json = json.loads(response.data.decode('utf-8')) message = "'Locataire ou sous-locataire d‘un logement loué vide non-HLM' is not a known value for 'housing_occupancy_status'. Possible values are " text = dpath.get(response_json, 'households/_/housing_occupancy_status/2017-07') assert message in text -def test_encoding_entity_name(): +def test_encoding_entity_name(test_client): simulation_json = json.dumps({ "persons": { "O‘Ryan": {}, @@ -227,17 +222,17 @@ def test_encoding_entity_name(): }) # No UnicodeDecodeError - response = post_json(simulation_json) + response = post_json(test_client, simulation_json) response_json = json.loads(response.data.decode('utf-8')) # In Python 3, there is no encoding issue. - if response.status_code != OK: + if response.status_code != client.OK: message = "'O‘Ryan' is not a valid ASCII value." text = response_json['error'] assert message in text -def test_encoding_period_id(): +def test_encoding_period_id(test_client): simulation_json = json.dumps({ "persons": { "bill": { @@ -268,8 +263,8 @@ def test_encoding_period_id(): }) # No UnicodeDecodeError - response = post_json(simulation_json) - assert response.status_code == BAD_REQUEST + response = post_json(test_client, simulation_json) + assert response.status_code == client.BAD_REQUEST response_json = json.loads(response.data.decode('utf-8')) # In Python 3, there is no encoding issue. @@ -279,17 +274,17 @@ def test_encoding_period_id(): assert message in text -def test_str_variable(): - new_couple = deepcopy(couple) +def test_str_variable(test_client): + new_couple = copy.deepcopy(couple) new_couple['households']['_']['postal_code'] = {'2017-01': None} simulation_json = json.dumps(new_couple) - response = subject.post('/calculate', data = simulation_json, content_type = 'application/json') + response = test_client.post('/calculate', data = simulation_json, content_type = 'application/json') - assert response.status_code == OK + assert response.status_code == client.OK -def test_periods(): +def test_periods(test_client): simulation_json = json.dumps({ "persons": { "bill": {} @@ -307,8 +302,8 @@ def test_periods(): } }) - response = post_json(simulation_json) - assert response.status_code == OK + response = post_json(test_client, simulation_json) + assert response.status_code == client.OK response_json = json.loads(response.data.decode('utf-8')) @@ -319,7 +314,7 @@ def test_periods(): assert monthly_variable == {'2017-01': 'tenant'} -def test_gracefully_handle_unexpected_errors(): +def test_gracefully_handle_unexpected_errors(test_client): """ Context ======== @@ -358,8 +353,8 @@ def test_gracefully_handle_unexpected_errors(): } }) - response = post_json(simulation_json) - assert response.status_code == INTERNAL_SERVER_ERROR + response = post_json(test_client, simulation_json) + assert response.status_code == client.INTERNAL_SERVER_ERROR error = json.loads(response.data)["error"] assert f"Unable to compute variable '{variable}' for period {period}" in error diff --git a/tests/web_api/test_entities.py b/tests/web_api/test_entities.py index de5600599f..6f8153ed37 100644 --- a/tests/web_api/test_entities.py +++ b/tests/web_api/test_entities.py @@ -1,24 +1,25 @@ # -*- coding: utf-8 -*- -from http.client import OK +from http import client import json -import openfisca_country_template -from . import subject -entities_response = subject.get('/entities') +from openfisca_country_template import entities + # /entities -def test_return_code(): - assert entities_response.status_code == OK +def test_return_code(test_client): + entities_response = test_client.get('/entities') + assert entities_response.status_code == client.OK -def test_response_data(): - entities = json.loads(entities_response.data.decode('utf-8')) - test_documentation = openfisca_country_template.entities.Household.doc.strip() +def test_response_data(test_client): + entities_response = test_client.get('/entities') + entities_dict = json.loads(entities_response.data.decode('utf-8')) + test_documentation = entities.Household.doc.strip() - assert entities['household'] == { + assert entities_dict['household'] == { 'description': 'All the people in a family or group who live together in the same place.', 'documentation': test_documentation, 'plural': 'households', diff --git a/tests/web_api/test_headers.py b/tests/web_api/test_headers.py index e1239cf8a9..54bbfd0df8 100644 --- a/tests/web_api/test_headers.py +++ b/tests/web_api/test_headers.py @@ -1,13 +1,13 @@ # -*- coding: utf-8 -*- -from . import distribution, subject +from . import distribution -parameters_response = subject.get('/parameters') - -def test_package_name_header(): +def test_package_name_header(test_client): + parameters_response = test_client.get('/parameters') assert parameters_response.headers.get('Country-Package') == distribution.key -def test_package_version_header(): +def test_package_version_header(test_client): + parameters_response = test_client.get('/parameters') assert parameters_response.headers.get('Country-Package-Version') == distribution.version diff --git a/tests/web_api/test_helpers.py b/tests/web_api/test_helpers.py index d36d399e25..cb049a0822 100644 --- a/tests/web_api/test_helpers.py +++ b/tests/web_api/test_helpers.py @@ -1,7 +1,8 @@ import os +from openfisca_web_api.loader import parameters + from openfisca_core.parameters import load_parameter_file -from openfisca_web_api.loader.parameters import build_api_values_history, get_value dir_path = os.path.join(os.path.dirname(__file__), 'assets') @@ -16,7 +17,7 @@ def test_build_api_values_history(): '2015-01-01': 0.04, '2013-01-01': 0.03, } - assert build_api_values_history(parameter) == values + assert parameters.build_api_values_history(parameter) == values def test_build_api_values_history_with_stop_date(): @@ -30,23 +31,23 @@ def test_build_api_values_history_with_stop_date(): '2013-01-01': 0.03, } - assert build_api_values_history(parameter) == values + assert parameters.build_api_values_history(parameter) == values def test_get_value(): values = {'2013-01-01': 0.03, '2017-01-01': 0.02, '2015-01-01': 0.04} - assert get_value('2013-01-01', values) == 0.03 - assert get_value('2014-01-01', values) == 0.03 - assert get_value('2015-02-01', values) == 0.04 - assert get_value('2016-12-31', values) == 0.04 - assert get_value('2017-01-01', values) == 0.02 - assert get_value('2018-01-01', values) == 0.02 + assert parameters.get_value('2013-01-01', values) == 0.03 + assert parameters.get_value('2014-01-01', values) == 0.03 + assert parameters.get_value('2015-02-01', values) == 0.04 + assert parameters.get_value('2016-12-31', values) == 0.04 + assert parameters.get_value('2017-01-01', values) == 0.02 + assert parameters.get_value('2018-01-01', values) == 0.02 def test_get_value_with_none(): values = {'2015-01-01': 0.04, '2017-01-01': None} - assert get_value('2016-12-31', values) == 0.04 - assert get_value('2017-01-01', values) is None - assert get_value('2011-01-01', values) is None + assert parameters.get_value('2016-12-31', values) == 0.04 + assert parameters.get_value('2017-01-01', values) is None + assert parameters.get_value('2011-01-01', values) is None diff --git a/tests/web_api/test_parameters.py b/tests/web_api/test_parameters.py index a3b58df7c6..efef6ffcd2 100644 --- a/tests/web_api/test_parameters.py +++ b/tests/web_api/test_parameters.py @@ -1,23 +1,22 @@ -# -*- coding: utf-8 -*- - -from http.client import OK, NOT_FOUND +from http import client import json +import pytest import re -import pytest -from . import tax_benefit_system, subject # /parameters -parameters_response = subject.get('/parameters') + GITHUB_URL_REGEX = r'^https://github\.com/openfisca/country-template/blob/\d+\.\d+\.\d+((.dev|rc)\d+)?/openfisca_country_template/parameters/(.)+\.yaml$' -def test_return_code(): - assert parameters_response.status_code == OK +def test_return_code(test_client): + parameters_response = test_client.get('/parameters') + assert parameters_response.status_code == client.OK -def test_response_data(): +def test_response_data(test_client): + parameters_response = test_client.get('/parameters') parameters = json.loads(parameters_response.data.decode('utf-8')) assert parameters['taxes.income_tax_rate'] == { @@ -29,23 +28,23 @@ def test_response_data(): # /parameter/ -def test_error_code_non_existing_parameter(): - response = subject.get('/parameter/non/existing.parameter') - assert response.status_code == NOT_FOUND +def test_error_code_non_existing_parameter(test_client): + response = test_client.get('/parameter/non/existing.parameter') + assert response.status_code == client.NOT_FOUND -def test_return_code_existing_parameter(): - response = subject.get('/parameter/taxes/income_tax_rate') - assert response.status_code == OK +def test_return_code_existing_parameter(test_client): + response = test_client.get('/parameter/taxes/income_tax_rate') + assert response.status_code == client.OK -def test_legacy_parameter_route(): - response = subject.get('/parameter/taxes.income_tax_rate') - assert response.status_code == OK +def test_legacy_parameter_route(test_client): + response = test_client.get('/parameter/taxes.income_tax_rate') + assert response.status_code == client.OK -def test_parameter_values(): - response = subject.get('/parameter/taxes/income_tax_rate') +def test_parameter_values(test_client): + response = test_client.get('/parameter/taxes/income_tax_rate') parameter = json.loads(response.data) assert sorted(list(parameter.keys())), ['description', 'id', 'metadata', 'source', 'values'] assert parameter['id'] == 'taxes.income_tax_rate' @@ -56,7 +55,7 @@ def test_parameter_values(): assert 'taxes/income_tax_rate.yaml' in parameter['source'] # 'documentation' attribute exists only when a value is defined - response = subject.get('/parameter/benefits/housing_allowance') + response = test_client.get('/parameter/benefits/housing_allowance') parameter = json.loads(response.data) assert sorted(list(parameter.keys())), ['description', 'documentation', 'id', 'metadata', 'source' == 'values'] assert ( @@ -65,9 +64,9 @@ def test_parameter_values(): ) -def test_parameter_node(): - response = subject.get('/parameter/benefits') - assert response.status_code == OK +def test_parameter_node(tax_benefit_system, test_client): + response = test_client.get('/parameter/benefits') + assert response.status_code == client.OK parameter = json.loads(response.data) assert sorted(list(parameter.keys())), ['description', 'documentation', 'id', 'metadata', 'source' == 'subparams'] assert parameter['documentation'] == ( @@ -86,14 +85,14 @@ def test_parameter_node(): ), parameter['subparams']['basic_income']['description'] -def test_stopped_parameter_values(): - response = subject.get('/parameter/benefits/housing_allowance') +def test_stopped_parameter_values(test_client): + response = test_client.get('/parameter/benefits/housing_allowance') parameter = json.loads(response.data) assert parameter['values'] == {'2016-12-01': None, '2010-01-01': 0.25} -def test_scale(): - response = subject.get('/parameter/taxes/social_security_contribution') +def test_scale(test_client): + response = test_client.get('/parameter/taxes/social_security_contribution') parameter = json.loads(response.data) assert sorted(list(parameter.keys())), ['brackets', 'description', 'id', 'metadata' == 'source'] assert parameter['brackets'] == { @@ -105,25 +104,25 @@ def test_scale(): } -def check_code(route, code): - response = subject.get(route) +def check_code(client, route, code): + response = client.get(route) assert response.status_code == code @pytest.mark.parametrize("expected_code", [ - ('/parameters/', OK), - ('/parameter', NOT_FOUND), - ('/parameter/', NOT_FOUND), - ('/parameter/with-ÜNı©ød€', NOT_FOUND), - ('/parameter/with%20url%20encoding', NOT_FOUND), - ('/parameter/taxes/income_tax_rate/', OK), - ('/parameter/taxes/income_tax_rate/too-much-nesting', NOT_FOUND), - ('/parameter//taxes/income_tax_rate/', NOT_FOUND), + ('/parameters/', client.OK), + ('/parameter', client.NOT_FOUND), + ('/parameter/', client.NOT_FOUND), + ('/parameter/with-ÜNı©ød€', client.NOT_FOUND), + ('/parameter/with%20url%20encoding', client.NOT_FOUND), + ('/parameter/taxes/income_tax_rate/', client.OK), + ('/parameter/taxes/income_tax_rate/too-much-nesting', client.NOT_FOUND), + ('/parameter//taxes/income_tax_rate/', client.NOT_FOUND), ]) -def test_routes_robustness(expected_code): - check_code(*expected_code) +def test_routes_robustness(test_client, expected_code): + check_code(test_client, *expected_code) -def test_parameter_encoding(): - parameter_response = subject.get('/parameter/general/age_of_retirement') - assert parameter_response.status_code == OK +def test_parameter_encoding(test_client): + parameter_response = test_client.get('/parameter/general/age_of_retirement') + assert parameter_response.status_code == client.OK diff --git a/tests/web_api/test_spec.py b/tests/web_api/test_spec.py index dd59b588bd..5e19752119 100644 --- a/tests/web_api/test_spec.py +++ b/tests/web_api/test_spec.py @@ -1,27 +1,25 @@ -# -*- coding: utf-8 -*- - -import json -from http.client import OK - import dpath -from . import subject +import json +import pytest +from http import client def assert_items_equal(x, y): assert sorted(x) == sorted(y) -openAPI_response = subject.get('/spec') - - -def test_return_code(): - assert openAPI_response.status_code == OK +def test_return_code(test_client): + openAPI_response = test_client.get('/spec') + assert openAPI_response.status_code == client.OK -body = json.loads(openAPI_response.data.decode('utf-8')) +@pytest.fixture(scope="module") +def body(test_client): + openAPI_response = test_client.get('/spec') + return json.loads(openAPI_response.data.decode('utf-8')) -def test_paths(): +def test_paths(body): assert_items_equal( body['paths'], ["/parameter/{parameterID}", @@ -35,7 +33,7 @@ def test_paths(): ) -def test_entity_definition(): +def test_entity_definition(body): assert 'parents' in dpath.get(body, 'definitions/Household/properties') assert 'children' in dpath.get(body, 'definitions/Household/properties') assert 'salary' in dpath.get(body, 'definitions/Person/properties') @@ -43,7 +41,7 @@ def test_entity_definition(): assert 'number' == dpath.get(body, 'definitions/Person/properties/salary/additionalProperties/type') -def test_situation_definition(): +def test_situation_definition(body): situation_input = body['definitions']['SituationInput'] situation_output = body['definitions']['SituationOutput'] for situation in situation_input, situation_output: @@ -53,5 +51,5 @@ def test_situation_definition(): assert "#/definitions/Person" == dpath.get(situation, '/properties/persons/additionalProperties/$ref') -def test_host(): +def test_host(body): assert 'http' not in body['host'] diff --git a/tests/web_api/test_trace.py b/tests/web_api/test_trace.py index 143e33e549..b59fbdb5f0 100644 --- a/tests/web_api/test_trace.py +++ b/tests/web_api/test_trace.py @@ -1,23 +1,19 @@ -# -*- coding: utf-8 -*- - +import copy +import dpath +from http import client import json -from copy import deepcopy -from http.client import OK -import dpath from openfisca_country_template.situation_examples import single, couple -from . import subject - def assert_items_equal(x, y): assert set(x) == set(y) -def test_trace_basic(): +def test_trace_basic(test_client): simulation_json = json.dumps(single) - response = subject.post('/trace', data = simulation_json, content_type = 'application/json') - assert response.status_code == OK + response = test_client.post('/trace', data = simulation_json, content_type = 'application/json') + assert response.status_code == client.OK response_json = json.loads(response.data.decode('utf-8')) disposable_income_value = dpath.util.get(response_json, 'trace/disposable_income<2017-01>/value') assert isinstance(disposable_income_value, list) @@ -31,19 +27,19 @@ def test_trace_basic(): assert_items_equal(basic_income_dep, ['age<2017-01>']) -def test_trace_enums(): - new_single = deepcopy(single) +def test_trace_enums(test_client): + new_single = copy.deepcopy(single) new_single['households']['_']['housing_occupancy_status'] = {"2017-01": None} simulation_json = json.dumps(new_single) - response = subject.post('/trace', data = simulation_json, content_type = 'application/json') + response = test_client.post('/trace', data = simulation_json, content_type = 'application/json') response_json = json.loads(response.data) housing_status = dpath.util.get(response_json, 'trace/housing_occupancy_status<2017-01>/value') assert housing_status[0] == 'tenant' # The default value -def test_entities_description(): +def test_entities_description(test_client): simulation_json = json.dumps(couple) - response = subject.post('/trace', data = simulation_json, content_type = 'application/json') + response = test_client.post('/trace', data = simulation_json, content_type = 'application/json') response_json = json.loads(response.data.decode('utf-8')) assert_items_equal( dpath.util.get(response_json, 'entitiesDescription/persons'), @@ -51,9 +47,9 @@ def test_entities_description(): ) -def test_root_nodes(): +def test_root_nodes(test_client): simulation_json = json.dumps(couple) - response = subject.post('/trace', data = simulation_json, content_type = 'application/json') + response = test_client.post('/trace', data = simulation_json, content_type = 'application/json') response_json = json.loads(response.data.decode('utf-8')) assert_items_equal( dpath.util.get(response_json, 'requestedCalculations'), @@ -61,22 +57,22 @@ def test_root_nodes(): ) -def test_str_variable(): - new_couple = deepcopy(couple) +def test_str_variable(test_client): + new_couple = copy.deepcopy(couple) new_couple['households']['_']['postal_code'] = {'2017-01': None} simulation_json = json.dumps(new_couple) - response = subject.post('/trace', data = simulation_json, content_type = 'application/json') + response = test_client.post('/trace', data = simulation_json, content_type = 'application/json') - assert response.status_code == OK + assert response.status_code == client.OK -def test_trace_parameters(): - new_couple = deepcopy(couple) +def test_trace_parameters(test_client): + new_couple = copy.deepcopy(couple) new_couple['households']['_']['housing_tax'] = {'2017': None} simulation_json = json.dumps(new_couple) - response = subject.post('/trace', data = simulation_json, content_type = 'application/json') + response = test_client.post('/trace', data = simulation_json, content_type = 'application/json') response_json = json.loads(response.data.decode('utf-8')) assert len(dpath.util.get(response_json, 'trace/housing_tax<2017>/parameters')) > 0 diff --git a/tests/web_api/test_variables.py b/tests/web_api/test_variables.py index 53d4a8948f..4581608aa8 100644 --- a/tests/web_api/test_variables.py +++ b/tests/web_api/test_variables.py @@ -1,28 +1,30 @@ -# -*- coding: utf-8 -*- - -from http.client import OK, NOT_FOUND +from http import client import json -import re - import pytest -from . import subject +import re def assert_items_equal(x, y): assert set(x) == set(y) +GITHUB_URL_REGEX = r'^https://github\.com/openfisca/country-template/blob/\d+\.\d+\.\d+((.dev|rc)\d+)?/openfisca_country_template/variables/(.)+\.py#L\d+-L\d+$' + + # /variables -variables_response = subject.get('/variables') -GITHUB_URL_REGEX = r'^https://github\.com/openfisca/country-template/blob/\d+\.\d+\.\d+((.dev|rc)\d+)?/openfisca_country_template/variables/(.)+\.py#L\d+-L\d+$' + +@pytest.fixture(scope="module") +def variables_response(test_client): + variables_response = test_client.get("/variables") + return variables_response -def test_return_code(): - assert variables_response.status_code == OK +def test_return_code(variables_response): + assert variables_response.status_code == client.OK -def test_response_data(): +def test_response_data(variables_response): variables = json.loads(variables_response.data.decode('utf-8')) assert variables['birth'] == { 'description': 'Birth date', @@ -33,20 +35,22 @@ def test_response_data(): # /variable/ -def test_error_code_non_existing_variable(): - response = subject.get('/variable/non_existing_variable') - assert response.status_code == NOT_FOUND +def test_error_code_non_existing_variable(test_client): + response = test_client.get('/variable/non_existing_variable') + assert response.status_code == client.NOT_FOUND -input_variable_response = subject.get('/variable/birth') -input_variable = json.loads(input_variable_response.data.decode('utf-8')) +@pytest.fixture(scope="module") +def input_variable_response(test_client): + input_variable_response = test_client.get('/variable/birth') + return input_variable_response -def test_return_code_existing_input_variable(): - assert input_variable_response.status_code == OK +def test_return_code_existing_input_variable(input_variable_response): + assert input_variable_response.status_code == client.OK -def check_input_variable_value(key, expected_value): +def check_input_variable_value(key, expected_value, input_variable=None): assert input_variable[key] == expected_value @@ -58,23 +62,25 @@ def check_input_variable_value(key, expected_value): ('entity', 'person'), ('references', ['https://en.wiktionary.org/wiki/birthdate']), ]) -def test_input_variable_value(expected_values): - check_input_variable_value(*expected_values) +def test_input_variable_value(expected_values, input_variable_response): + input_variable = json.loads(input_variable_response.data.decode('utf-8')) + check_input_variable_value(*expected_values, input_variable=input_variable) -def test_input_variable_github_url(): - assert re.match(GITHUB_URL_REGEX, input_variable['source']) +def test_input_variable_github_url(test_client): + input_variable_response = test_client.get('/variable/income_tax') + input_variable = json.loads(input_variable_response.data.decode('utf-8')) -variable_response = subject.get('/variable/income_tax') -variable = json.loads(variable_response.data.decode('utf-8')) + assert re.match(GITHUB_URL_REGEX, input_variable['source']) -def test_return_code_existing_variable(): - assert variable_response.status_code == OK +def test_return_code_existing_variable(test_client): + variable_response = test_client.get('/variable/income_tax') + assert variable_response.status_code == client.OK -def check_variable_value(key, expected_value): +def check_variable_value(key, expected_value, variable=None): assert variable[key] == expected_value @@ -85,36 +91,42 @@ def check_variable_value(key, expected_value): ('definitionPeriod', 'MONTH'), ('entity', 'person'), ]) -def test_variable_value(expected_values): - check_variable_value(*expected_values) +def test_variable_value(expected_values, test_client): + variable_response = test_client.get('/variable/income_tax') + variable = json.loads(variable_response.data.decode('utf-8')) + check_variable_value(*expected_values, variable=variable) -def test_variable_formula_github_link(): +def test_variable_formula_github_link(test_client): + variable_response = test_client.get('/variable/income_tax') + variable = json.loads(variable_response.data.decode('utf-8')) assert re.match(GITHUB_URL_REGEX, variable['formulas']['0001-01-01']['source']) -def test_variable_formula_content(): +def test_variable_formula_content(test_client): + variable_response = test_client.get('/variable/income_tax') + variable = json.loads(variable_response.data.decode('utf-8')) content = variable['formulas']['0001-01-01']['content'] assert "def formula(person, period, parameters):" in content assert "return person(\"salary\", period) * parameters(period).taxes.income_tax_rate" in content -def test_null_values_are_dropped(): - variable_response = subject.get('/variable/age') +def test_null_values_are_dropped(test_client): + variable_response = test_client.get('/variable/age') variable = json.loads(variable_response.data.decode('utf-8')) assert 'references' not in variable.keys() -def test_variable_with_start_and_stop_date(): - response = subject.get('/variable/housing_allowance') +def test_variable_with_start_and_stop_date(test_client): + response = test_client.get('/variable/housing_allowance') variable = json.loads(response.data.decode('utf-8')) assert_items_equal(variable['formulas'], ['1980-01-01', '2016-12-01']) assert variable['formulas']['2016-12-01'] is None assert 'formula' in variable['formulas']['1980-01-01']['content'] -def test_variable_with_enum(): - response = subject.get('/variable/housing_occupancy_status') +def test_variable_with_enum(test_client): + response = test_client.get('/variable/housing_occupancy_status') variable = json.loads(response.data.decode('utf-8')) assert variable['valueType'] == 'String' assert variable['defaultValue'] == 'tenant' @@ -126,19 +138,23 @@ def test_variable_with_enum(): 'tenant': 'Tenant'} -dated_variable_response = subject.get('/variable/basic_income') -dated_variable = json.loads(dated_variable_response.data.decode('utf-8')) +@pytest.fixture(scope="module") +def dated_variable_response(test_client): + dated_variable_response = test_client.get('/variable/basic_income') + return dated_variable_response -def test_return_code_existing_dated_variable(): - assert dated_variable_response.status_code == OK +def test_return_code_existing_dated_variable(dated_variable_response): + assert dated_variable_response.status_code == client.OK -def test_dated_variable_formulas_dates(): +def test_dated_variable_formulas_dates(dated_variable_response): + dated_variable = json.loads(dated_variable_response.data.decode('utf-8')) assert_items_equal(dated_variable['formulas'], ['2016-12-01', '2015-12-01']) -def test_dated_variable_formulas_content(): +def test_dated_variable_formulas_content(dated_variable_response): + dated_variable = json.loads(dated_variable_response.data.decode('utf-8')) formula_code_2016 = dated_variable['formulas']['2016-12-01']['content'] formula_code_2015 = dated_variable['formulas']['2015-12-01']['content'] @@ -148,13 +164,13 @@ def test_dated_variable_formulas_content(): assert "return" in formula_code_2015 -def test_variable_encoding(): - variable_response = subject.get('/variable/pension') - assert variable_response.status_code == OK +def test_variable_encoding(test_client): + variable_response = test_client.get('/variable/pension') + assert variable_response.status_code == client.OK -def test_variable_documentation(): - response = subject.get('/variable/housing_allowance') +def test_variable_documentation(test_client): + response = test_client.get('/variable/housing_allowance') variable = json.loads(response.data.decode('utf-8')) assert variable['documentation'] == "This allowance was introduced on the 1st of Jan 1980.\nIt disappeared in Dec 2016."