Source code for kotti.testing

"""
Inheritance Diagram
-------------------

.. inheritance-diagram:: kotti.testing
"""
import os
from os.path import dirname
from os.path import join
from unittest import TestCase
from warnings import catch_warnings

import transaction
from pyramid import testing
from pyramid.events import NewResponse
from pyramid.interfaces import ILocation
from pyramid.security import ALL_PERMISSIONS
from pytest import mark
from zope.interface import implementer

# re-enable deprecation warnings during test runs
# however, let the `ImportWarning` produced by Babel's
# `localedata.py` vs `localedata/` show up once...
with catch_warnings():
    pass
    # import compiler
    # localedata, compiler    # make pyflakes happy... :p


# py.test markers (see http://pytest.org/latest/example/markers.html)
user = mark.user


BASE_URL = "http://localhost:6543"


[docs]class Dummy(dict): # noinspection PyMissingConstructor def __init__(self, **kwargs): self.__dict__.update(kwargs)
[docs]class DummyRequest(testing.DummyRequest): is_xhr = False POST = dict() user = None referrer = None @staticmethod def is_response(ob): return ( hasattr(ob, "app_iter") and hasattr(ob, "headerlist") and hasattr(ob, "status") ) # noinspection PyPep8Naming
[docs] @classmethod def blank(cls, path, environ=None, base_url=None, headers=None, POST=None, **kw): """ ``request.blank`` is used in Kotti only when assigning slots, where the POST parameters are faked as a querystring. """ from urllib.parse import parse_qsl def _decode(body): if not body: return {} return {x: y for x, y in parse_qsl(body)} if POST and isinstance(POST, bytes): POST = POST.decode() if POST and isinstance(POST, str): POST = _decode(POST) req = testing.DummyRequest( path=path, environ=environ, headers=headers, cookies=None, post=POST, **kw ) return req
def asset(name): import kotti return open(join(dirname(kotti.__file__), "tests", name), "rb")
[docs]def includeme_login(config): """ Pyramid includeme hook. :param config: app config :type config: :class:`pyramid.config.Configurator` """ config.add_view(login_view, name="login", renderer="kotti:templates/login.pt")
[docs]def includeme_layout(config): """ Pyramid includeme hook. :param config: app config :type config: :class:`pyramid.config.Configurator` """ # override edit master layout with view master layout config.override_asset( to_override="kotti:templates/edit/master.pt", override_with="kotti:templates/view/master.pt", )
def login_view(request): return {} def dummy_search(search_term, request): return "Not found. Sorry!" def testing_db_url(): return os.environ.get("KOTTI_TEST_DB_STRING", "sqlite://") def _init_testing_db(): from sqlalchemy import create_engine from kotti import get_settings from kotti.resources import initialize_sql database_url = testing_db_url() get_settings()["sqlalchemy.url"] = database_url session = initialize_sql(create_engine(database_url), drop_all=True) return session def _populator(): from kotti import DBSession from kotti.resources import Document from kotti.populate import populate populate() for doc in DBSession.query(Document)[1:]: DBSession.delete(doc) transaction.commit() def _turn_warnings_into_errors(): # pragma: no cover # turn all warnings into errors, but let the `ImportWarning` # produced by Babel's `localedata.py` vs `localedata/` show up once... from babel import localedata localedata # make pyflakes happy... :p from warnings import filterwarnings filterwarnings("error") # noinspection PyPep8Naming def setUp(init_db=True, **kwargs): # _turn_warnings_into_errors() from kotti import _resolve_dotted from kotti import conf_defaults tearDown() settings = conf_defaults.copy() settings["kotti.secret"] = "secret" settings["kotti.secret2"] = "secret2" settings["kotti.populators"] = "kotti.testing._populator" settings["pyramid_deform.tempdir"] = "/tmp" settings.update(kwargs.get("settings", {})) settings = _resolve_dotted(settings) kwargs["settings"] = settings config = testing.setUp(**kwargs) config.add_default_renderers() if init_db: _init_testing_db() transaction.begin() return config # noinspection PyPep8Naming def tearDown(): from depot.manager import DepotManager from kotti import events from kotti import security from kotti.message import _inject_mailer # These should arguable use the configurator, so they don't need # to be torn down separately: events.clear() security.reset() _inject_mailer[:] = [] transaction.abort() DepotManager._clear() testing.tearDown()
[docs]class UnitTestBase(TestCase):
[docs] def setUp(self, **kwargs): self.config = setUp(**kwargs)
[docs] def tearDown(self): tearDown()
[docs]class EventTestBase(TestCase):
[docs] def setUp(self, **kwargs): super().setUp(**kwargs) self.config.include("kotti.events")
# Functional ---- def _functional_includeme(config): from kotti import DBSession def expire(event): DBSession.flush() DBSession.expire_all() config.add_subscriber(expire, NewResponse) def _zope_testbrowser_pyquery(self): from pyquery import PyQuery return PyQuery(self.contents.replace('xmlns="http://www.w3.org/1999/xhtml', "")) # noinspection PyPep8Naming def setUpFunctional(global_config=None, **settings): from kotti import main from zope.testbrowser.wsgi import Browser from webtest import TestApp tearDown() _settings = { "sqlalchemy.url": testing_db_url(), "kotti.secret": "secret", "kotti.site_title": "Website des Kottbusser Tors", # for mailing "kotti.populators": "kotti.testing._populator", "mail.default_sender": "kotti@localhost", "pyramid.includes": "kotti.testing._functional_includeme", } _settings.update(settings) host, port = BASE_URL.split(":")[-2:] app = main({}, **_settings) Browser.pyquery = property(_zope_testbrowser_pyquery) return dict( Browser=lambda: Browser( "http://{}:{}/".format(host[2:], int(port)), wsgi_app=app ), browser=Browser("http://{}:{}/".format(host[2:], int(port)), wsgi_app=app), test_app=TestApp(app), )
[docs]class FunctionalTestBase(TestCase): BASE_URL = BASE_URL
[docs] def setUp(self, **kwargs): self.__dict__.update(setUpFunctional(**kwargs))
[docs] def tearDown(self): tearDown()
def login(self, login="admin", password="secret"): return self.test_app.post( "/@@login", {"login": login, "password": password, "submit": "submit"}, status=302, )
[docs]@implementer(ILocation) class RootFactory(dict): __name__ = "" # root is required to have an empty name! __parent__ = None __acl__ = [("Allow", "role:admin", ALL_PERMISSIONS)] def __init__(self, request): super().__init__()
def dummy_view(context, request): return {}
[docs]def include_testing_view(config): """ Pyramid includeme hook. :param config: app config :type config: :class:`pyramid.config.Configurator` """ config.add_view( dummy_view, context=RootFactory, renderer="kotti:tests/testing_view.pt" ) config.add_view( dummy_view, name="secured", permission="view", context=RootFactory, renderer="kotti:tests/testing_view.pt", )
# noinspection PyPep8Naming def setUpFunctionalStrippedDownApp(global_config=None, **settings): # An app that doesn't use Nodes at all _settings = { "kotti.base_includes": ( "kotti kotti.views kotti.views.login kotti.views.users " "kotti.views.view" ), "kotti.use_tables": "principals", "kotti.populators": "kotti.populate.populate_users", "pyramid.includes": "kotti.testing.include_testing_view", "kotti.root_factory": "kotti.testing.RootFactory", "kotti.site_title": "My Stripped Down Kotti", } _settings.update(settings) return setUpFunctional(global_config, **_settings) # noinspection PyPep8Naming def registerDummyMailer(): from pyramid_mailer.mailer import DummyMailer from kotti.message import _inject_mailer mailer = DummyMailer() _inject_mailer.append(mailer) return mailer # set up deprecation warnings from zope.deprecation.deprecation import deprecated # noqa for item in UnitTestBase, EventTestBase, FunctionalTestBase, _init_testing_db: name = getattr(item, "__name__", item) deprecated( name, "Unittest-style tests are deprecated as of Kotti 0.7. " "Please use pytest function arguments instead.", )