From 883947b75f162b477155f1bb69d8b767cbf9b5c5 Mon Sep 17 00:00:00 2001 From: dandds Date: Thu, 29 Aug 2019 13:07:24 -0400 Subject: [PATCH] Enable debug mode in tests. Debug mode allows route integration tests to raise explicit exceptions on errors, instead of returning error pages. Some portions of the test suite need to be able to ignore exceptions (the response is not under test) so they use a separate pytest fixture version of the app and client that are configured with debug disabled, as it would be in production. --- .secrets.baseline | 5 ++--- config/test.ini | 3 ++- tests/conftest.py | 24 ++++++++++++++------- tests/routes/portfolios/test_admin.py | 4 ++-- tests/routes/portfolios/test_index.py | 7 ++++--- tests/routes/test_errors.py | 28 +++++++++++++++++++------ tests/test_access.py | 29 ++++++++++++++++++++++---- tests/utils/test_context_processors.py | 14 +++++++------ 8 files changed, 82 insertions(+), 32 deletions(-) diff --git a/.secrets.baseline b/.secrets.baseline index 814fd704..36866487 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -3,7 +3,7 @@ "files": "^.secrets.baseline$", "lines": null }, - "generated_at": "2019-08-28T19:50:39Z", + "generated_at": "2019-08-29T17:03:11Z", "plugins_used": [ { "base64_limit": 4.5, @@ -194,11 +194,10 @@ "hashed_secret": "e4f14805dfd1e6af030359090c535e149e6b4207", "is_secret": false, "is_verified": false, - "line_number": 523, + "line_number": 544, "type": "Hex High Entropy String" } ] }, "version": "0.12.5" } - diff --git a/config/test.ini b/config/test.ini index c39d4de4..c323f599 100644 --- a/config/test.ini +++ b/config/test.ini @@ -1,7 +1,8 @@ [default] -DEBUG = false +DEBUG = true ENVIRONMENT = test PGDATABASE = atat_test CRL_STORAGE_CONTAINER = tests/fixtures/crl WTF_CSRF_ENABLED = false STORAGE_PROVIDER=LOCAL +PRESERVE_CONTEXT_ON_EXCEPTION = false diff --git a/tests/conftest.py b/tests/conftest.py index cede1f3c..9a667936 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -28,13 +28,7 @@ dictConfig({"version": 1, "handlers": {"wsgi": {"class": "logging.NullHandler"}} @pytest.fixture(scope="session") def app(request): - upload_dir = TemporaryDirectory() - config = make_config() - config.update( - {"STORAGE_CONTAINER": upload_dir.name, "CRL_STORAGE_PROVIDER": "LOCAL"} - ) - _app = make_app(config) ctx = _app.app_context() @@ -42,11 +36,27 @@ def app(request): yield _app - upload_dir.cleanup() + ctx.pop() + + +@pytest.fixture(scope="function") +def no_debug_app(request): + config = make_config(direct_config={"DEBUG": False}) + _app = make_app(config) + + ctx = _app.app_context() + ctx.push() + + yield _app ctx.pop() +@pytest.fixture(scope="function") +def no_debug_client(no_debug_app): + yield no_debug_app.test_client() + + def apply_migrations(): """Applies all alembic migrations.""" alembic_config = os.path.join(os.path.dirname(__file__), "../", "alembic.ini") diff --git a/tests/routes/portfolios/test_admin.py b/tests/routes/portfolios/test_admin.py index 52ae1965..312ab2ee 100644 --- a/tests/routes/portfolios/test_admin.py +++ b/tests/routes/portfolios/test_admin.py @@ -1,5 +1,5 @@ from flask import url_for -from unittest.mock import Mock +from unittest.mock import MagicMock from atst.domain.permission_sets import PermissionSets from atst.domain.portfolio_roles import PortfolioRoles @@ -129,7 +129,7 @@ def test_rerender_admin_page_if_member_perms_form_does_not_validate( "members_permissions-0-perms_portfolio_mgmt": "view_portfolio_admin", } - mock_route = Mock() + mock_route = MagicMock(return_value=("", 200, {})) monkeypatch.setattr("atst.routes.portfolios.admin.render_admin_page", mock_route) client.post( url_for("portfolios.edit_members", portfolio_id=portfolio.id), data=form_data diff --git a/tests/routes/portfolios/test_index.py b/tests/routes/portfolios/test_index.py index e54e2a23..153ea406 100644 --- a/tests/routes/portfolios/test_index.py +++ b/tests/routes/portfolios/test_index.py @@ -1,4 +1,5 @@ from flask import url_for +import pytest from tests.factories import ( random_future_date, @@ -9,7 +10,7 @@ from tests.factories import ( UserFactory, ) from atst.utils.localization import translate -from atst.domain.portfolios import Portfolios +from atst.domain.portfolios import Portfolios, PortfolioDeletionApplicationsExistError from atst.domain.portfolios.query import PortfoliosQuery @@ -128,7 +129,7 @@ def test_delete_portfolio_success(client, user_session): assert len(Portfolios.for_user(user=owner)) == 0 -def test_delete_portfolio_failure(client, user_session): +def test_delete_portfolio_failure(no_debug_client, user_session): portfolio = PortfolioFactory.create() application = ApplicationFactory.create(portfolio=portfolio) owner = portfolio.owner @@ -136,7 +137,7 @@ def test_delete_portfolio_failure(client, user_session): assert len(Portfolios.for_user(user=owner)) == 1 - response = client.post( + response = no_debug_client.post( url_for("portfolios.delete_portfolio", portfolio_id=portfolio.id) ) diff --git a/tests/routes/test_errors.py b/tests/routes/test_errors.py index f5b0289e..caf7fc0e 100644 --- a/tests/routes/test_errors.py +++ b/tests/routes/test_errors.py @@ -1,6 +1,8 @@ import pytest from flask import url_for -from copy import copy + +from atst.app import make_config, make_app + from tests.factories import UserFactory @@ -24,14 +26,28 @@ def test_csrf_error(csrf_enabled_app, client): assert "Log in required" in body -def test_errors_generate_notifications(app, client, user_session, notification_sender): - user_session(UserFactory.create()) - new_app = copy(app) +@pytest.fixture +def blowup_app(notification_sender): + _blowup_app = make_app(make_config(direct_config={"DEBUG": False})) + _blowup_app.notification_sender = notification_sender - @new_app.route("/throw") + @_blowup_app.route("/throw") def throw(): raise ValueError() - new_app.test_client().get("/throw") + yield _blowup_app + + +@pytest.fixture +def blowup_client(blowup_app): + yield blowup_app.test_client() + + +def test_errors_generate_notifications( + blowup_client, client, user_session, notification_sender +): + user_session(UserFactory.create()) + + blowup_client.get("/throw") notification_sender.send.assert_called_once() diff --git a/tests/test_access.py b/tests/test_access.py index 7da4868e..787742a2 100644 --- a/tests/test_access.py +++ b/tests/test_access.py @@ -12,6 +12,9 @@ from atst.models import CSPRole, PortfolioRoleStatus, ApplicationRoleStatus from tests.factories import * + +from atst.app import make_config, make_app + _NO_ACCESS_CHECK_REQUIRED = _NO_LOGIN_REQUIRED + [ "applications.accept_invitation", # available to all users; access control is built into invitation logic "atst.catch_all", # available to all users @@ -90,21 +93,39 @@ def user_with(*perm_sets_names): return UserFactory.create(permission_sets=PermissionSets.get_many(perm_sets_names)) +@pytest.fixture(scope="session") +def app(request): + config = make_config(direct_config={"DEBUG": False}) + _app = make_app(config) + + ctx = _app.app_context() + ctx.push() + + yield _app + + ctx.pop() + + +@pytest.fixture(scope="session") +def client(app): + yield app.test_client() + + @pytest.fixture -def get_url_assert_status(client, user_session): +def get_url_assert_status(no_debug_client, user_session): def _get_url_assert_status(user, url, status): user_session(user) - resp = client.get(url) + resp = no_debug_client.get(url) assert resp.status_code == status return _get_url_assert_status @pytest.fixture -def post_url_assert_status(client, user_session): +def post_url_assert_status(no_debug_client, user_session): def _get_url_assert_status(user, url, status, data=None): user_session(user) - resp = client.post(url, data=data) + resp = no_debug_client.post(url, data=data) assert resp.status_code == status return _get_url_assert_status diff --git a/tests/utils/test_context_processors.py b/tests/utils/test_context_processors.py index e2802b4e..73a26cbc 100644 --- a/tests/utils/test_context_processors.py +++ b/tests/utils/test_context_processors.py @@ -1,3 +1,5 @@ +from unittest.mock import Mock + import pytest from atst.domain.permission_sets import PermissionSets @@ -33,16 +35,15 @@ def test_get_resources_from_context(): @pytest.fixture -def set_g(request_ctx): +def set_g(monkeypatch): + _g = Mock() + monkeypatch.setattr("atst.utils.context_processors.g", _g) + def _set_g(attr, val): - setattr(request_ctx.g, attr, val) + setattr(_g, attr, val) yield _set_g - setattr(request_ctx.g, "application", None) - setattr(request_ctx.g, "portfolio", None) - setattr(request_ctx.g, "current_user", None) - def test_user_can_view(set_g): owner = UserFactory.create() @@ -77,4 +78,5 @@ def test_portfolio_no_user(set_g): def test_portfolio_with_user(set_g): user = UserFactory.create() set_g("current_user", user) + set_g("portfolio", None) assert portfolio_context() != {}