Coverage for .tox / coverage / lib / python3.11 / site-packages / wuttaweb / testing.py: 100%
85 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-02-04 06:44 -0600
« prev ^ index » next coverage.py v7.13.1, created at 2026-02-04 06:44 -0600
1# -*- coding: utf-8; -*-
2################################################################################
3#
4# wuttaweb -- Web App for Wutta Framework
5# Copyright © 2024-2025 Lance Edgar
6#
7# This file is part of Wutta Framework.
8#
9# Wutta Framework is free software: you can redistribute it and/or modify it
10# under the terms of the GNU General Public License as published by the Free
11# Software Foundation, either version 3 of the License, or (at your option) any
12# later version.
13#
14# Wutta Framework is distributed in the hope that it will be useful, but
15# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
17# more details.
18#
19# You should have received a copy of the GNU General Public License along with
20# Wutta Framework. If not, see <http://www.gnu.org/licenses/>.
21#
22################################################################################
23"""
24WuttaWeb - test utilities
25"""
27import re
28import sys
29from unittest.mock import MagicMock
31import fanstatic
32import pytest
33from pyramid import testing
34from webtest import TestApp
36from wuttjamaican.testing import DataTestCase
37from wuttjamaican.db.model.base import metadata
39from wuttaweb import subscribers, app as appmod
40from wuttaweb.conf import WuttaWebConfigExtension
43class WebTestCase(DataTestCase):
44 """
45 Base class for test suites requiring a full (typical) web app.
46 """
48 mako_directories = ["wuttaweb:templates"]
50 def setUp(self): # pylint: disable=empty-docstring
51 """ """
52 self.setup_web()
54 def setup_web(self):
55 """
56 Perform setup for the testing web app.
57 """
58 self.setup_db()
59 self.request = self.make_request()
60 self.pyramid_config = testing.setUp(
61 request=self.request,
62 settings={
63 "wutta_config": self.config,
64 "mako.directories": self.mako_directories,
65 "pyramid_deform.template_search_path": "wuttaweb:templates/deform",
66 },
67 )
69 # init web
70 self.pyramid_config.include("pyramid_deform")
71 self.pyramid_config.include("pyramid_mako")
72 self.pyramid_config.add_directive(
73 "add_wutta_permission_group", "wuttaweb.auth.add_permission_group"
74 )
75 self.pyramid_config.add_directive(
76 "add_wutta_permission", "wuttaweb.auth.add_permission"
77 )
78 self.pyramid_config.add_directive(
79 "add_wutta_master_view", "wuttaweb.conf.add_master_view"
80 )
81 self.pyramid_config.add_subscriber(
82 "wuttaweb.subscribers.before_render", "pyramid.events.BeforeRender"
83 )
84 self.pyramid_config.include("wuttaweb.static")
86 # nb. mock out fanstatic env..good enough for now to avoid errors..
87 needed = fanstatic.init_needed()
88 self.request.environ[fanstatic.NEEDED] = needed
90 # setup new request w/ anonymous user
91 event = MagicMock(request=self.request)
92 subscribers.new_request(event)
94 def user_getter(request, **kwargs): # pylint: disable=unused-argument
95 pass
97 subscribers.new_request_set_user(
98 event, db_session=self.session, user_getter=user_getter
99 )
101 def tearDown(self):
102 self.teardown_web()
104 def teardown_web(self):
105 """
106 Perform teardown for the testing web app.
107 """
108 testing.tearDown()
109 self.teardown_db()
111 def make_request(self):
112 """
113 Make and return a new dummy request object.
114 """
115 return testing.DummyRequest(client_addr="127.0.0.1")
118@pytest.mark.versioned
119class VersionWebTestCase(WebTestCase):
120 """
121 Base class for test suites requiring a full (typical) web app,
122 with Continuum versioning support.
123 """
125 def setUp(self):
126 self.setup_versioning()
128 def setup_versioning(self):
129 """
130 Perform setup for the testing web app.
131 """
132 self.setup_web()
134 def tearDown(self):
135 self.teardown_versioning()
137 def teardown_versioning(self):
138 """
139 Perform teardown for the testing web app.
140 """
141 import sqlalchemy_continuum as continuum # pylint: disable=import-outside-toplevel
143 continuum.remove_versioning()
144 continuum.versioning_manager.transaction_cls = continuum.TransactionFactory()
145 self.teardown_web()
147 def make_config(self, files=None, **kwargs):
148 """
149 Make and customize the config object.
151 We override this to explicitly enable the versioning feature.
152 """
153 from wutta_continuum.conf import ( # pylint: disable=import-outside-toplevel
154 WuttaContinuumConfigExtension,
155 )
157 config = super().make_config(files, **kwargs)
158 config.setdefault("wutta_continuum.enable_versioning", "true")
160 # nb. must purge model classes from sys.modules, so they will
161 # be reloaded and sqlalchemy-continuum can reconfigure
162 if "wuttjamaican.db.model" in sys.modules:
163 del sys.modules["wuttjamaican.db.model.batch"]
164 del sys.modules["wuttjamaican.db.model.upgrades"]
165 del sys.modules["wuttjamaican.db.model.auth"]
166 del sys.modules["wuttjamaican.db.model.base"]
167 del sys.modules["wuttjamaican.db.model"]
169 self.assertNotIn("user_version", metadata.tables)
171 ext = WuttaWebConfigExtension()
172 ext.configure(config)
174 ext = WuttaContinuumConfigExtension()
175 ext.startup(config)
177 return config
180# TODO: this interface likely needs to change. it is only used
181# by a single test so far, in tests/views/test_users.py
182@pytest.mark.functional
183class FunctionalTestCase(DataTestCase):
184 """
185 Base class for test suites requiring a fully complete web app, for
186 sake of functional tests.
188 .. warning::
190 This class is very new and not yet mature; it will surely change.
191 """
193 wsgi_main_app = None
195 def make_config(self, **kwargs): # pylint: disable=arguments-differ
196 sqlite_path = self.write_file("test.sqlite", "")
197 self.sqlite_engine_url = f"sqlite:///{sqlite_path}"
199 config_path = self.write_file(
200 "test.ini",
201 f"""
202[wutta.db]
203default.url = {self.sqlite_engine_url}
205[alembic]
206script_location = wuttjamaican.db:alembic
207version_locations = wuttjamaican.db:alembic/versions
208""",
209 )
211 return super().make_config([config_path], **kwargs)
213 def make_webtest(self): # pylint: disable=missing-function-docstring
214 webapp = appmod.make_wsgi_app(
215 self.wsgi_main_app or appmod.main, config=self.config
216 )
218 return TestApp(webapp)
220 def get_csrf_token(self, testapp): # pylint: disable=missing-function-docstring
221 res = testapp.get("/login")
222 match = re.search(
223 r'<input name="_csrf" type="hidden" value="(\w+)" />', res.text
224 )
225 self.assertTrue(match)
226 return match.group(1)