diff --git a/.circleci/config.yml b/.circleci/config.yml index 38edecf2a83..55c52eace04 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -14,7 +14,7 @@ jobs: CKAN_POSTGRES_PWD: pass PGPASSWORD: ckan NODE_TESTS_CONTAINER: 2 - NOSETEST_COMMON_OPTIONS: -v --ckan --reset-db --with-pylons=test-core-circle-ci.ini --nologcapture --with-coverage --cover-package=ckan --cover-package=ckanext --with-xunit --xunit-file=/root/junit/junit.xml ckan ckanext + PYTEST_COMMON_OPTIONS: -v --ckan-ini=test-core-circle-ci.ini --cov=ckan --cov=ckanext --junitxml=/root/junit/junit.xml --test-group-count 4 --test-group-random-seed 1 - image: postgres:10 environment: POSTGRES_USER: ckan @@ -65,26 +65,18 @@ jobs: - run: | mkdir -p ~/junit case $CIRCLE_NODE_INDEX in - 0) nosetests $NOSETEST_COMMON_OPTIONS --segments 0123 + 0) python -m pytest $PYTEST_COMMON_OPTIONS --test-group 1 ;; - 1) nosetests $NOSETEST_COMMON_OPTIONS --segments 4567 + 1) python -m pytest $PYTEST_COMMON_OPTIONS --test-group 2 ;; - 2) nosetests $NOSETEST_COMMON_OPTIONS --segments 89ab + 2) python -m pytest $PYTEST_COMMON_OPTIONS --test-group 3 ;; - 3) nosetests $NOSETEST_COMMON_OPTIONS --segments cdef + 3) python -m pytest $PYTEST_COMMON_OPTIONS --test-group 4 ;; esac - store_test_results: path: ~/junit - # test_revision_legacy_code is run separately because it mucks up the model for some other tests when nose starts up - - run: - command: | - case $CIRCLE_NODE_INDEX in - 3) nosetests -v --ckan --with-pylons=test-core-circle-ci.ini --nologcapture test_revision_legacy_code.py - ;; - esac - # Tests Frontend, only in one container - run: command: | diff --git a/ckan/cli/dataset.py b/ckan/cli/dataset.py index f62056a47e0..3a8eeca5e97 100644 --- a/ckan/cli/dataset.py +++ b/ckan/cli/dataset.py @@ -53,7 +53,6 @@ def delete(package): dataset = _get_dataset(package) old_state = dataset.state - model.repo.new_revision() dataset.delete() model.repo.commit_and_remove() dataset = _get_dataset(package) diff --git a/ckan/logic/action/update.py b/ckan/logic/action/update.py index 5dc1520310c..4ee07af7106 100644 --- a/ckan/logic/action/update.py +++ b/ckan/logic/action/update.py @@ -747,7 +747,7 @@ def task_status_update(context, data_dict): ''' model = context['model'] - session = model.meta.create_local_session() + session = model.Session context['session'] = session user = context['user'] diff --git a/ckan/model/__init__.py b/ckan/model/__init__.py index 027e33920af..c4c42ec4e49 100644 --- a/ckan/model/__init__.py +++ b/ckan/model/__init__.py @@ -187,7 +187,8 @@ def init_db(self): self.session.remove() # sqlite database needs to be recreated each time as the # memory database is lost. - if self.metadata.bind.name == 'sqlite': + + if self.metadata.bind.engine.url.drivername == 'sqlite': # this creates the tables, which isn't required inbetween tests # that have simply called rebuild_db. self.create_db() diff --git a/ckan/model/meta.py b/ckan/model/meta.py index aded9871c80..6c0ce282925 100644 --- a/ckan/model/meta.py +++ b/ckan/model/meta.py @@ -100,11 +100,12 @@ def after_rollback(self, session): def engine_is_sqlite(sa_engine=None): # Returns true iff the engine is connected to a sqlite database. - return (sa_engine or engine).url.drivername == 'sqlite' + + return (sa_engine or engine).engine.url.drivername == 'sqlite' def engine_is_pg(sa_engine=None): # Returns true iff the engine is connected to a postgresql database. # According to http://docs.sqlalchemy.org/en/latest/core/engines.html#postgresql # all Postgres driver names start with `postgres` - return (sa_engine or engine).url.drivername.startswith('postgres') + return (sa_engine or engine).engine.url.drivername.startswith('postgres') diff --git a/ckan/pastertemplates/template/README.rst_tmpl b/ckan/pastertemplates/template/README.rst_tmpl index a6783ba15dd..f0528fa7e11 100644 --- a/ckan/pastertemplates/template/README.rst_tmpl +++ b/ckan/pastertemplates/template/README.rst_tmpl @@ -100,12 +100,12 @@ Tests To run the tests, do:: - nosetests --nologcapture --with-pylons=test.ini + pytest --ckan-ini=test.ini To run the tests and produce a coverage report, first make sure you have coverage installed in your virtualenv (``pip install coverage``) then run:: - nosetests --nologcapture --with-pylons=test.ini --with-coverage --cover-package=ckanext.{{ project_shortname }} --cover-inclusive --cover-erase --cover-tests + pytest --ckan-ini=test.ini --cov=ckanext.{{ project_shortname }} ---------------------------------------- diff --git a/ckan/pastertemplates/template/bin/travis-run.sh_tmpl b/ckan/pastertemplates/template/bin/travis-run.sh_tmpl index 56e9548c07d..6a899cd8842 100755 --- a/ckan/pastertemplates/template/bin/travis-run.sh_tmpl +++ b/ckan/pastertemplates/template/bin/travis-run.sh_tmpl @@ -5,14 +5,8 @@ flake8 --version # stop the build if there are Python syntax errors or undefined names flake8 . --count --select=E901,E999,F821,F822,F823 --show-source --statistics --exclude ckan,{{ project }} -nosetests --ckan \ - --nologcapture \ - --with-pylons=subdir/test.ini \ - --with-coverage \ - --cover-package=ckanext.{{ project_shortname }} \ - --cover-inclusive \ - --cover-erase \ - --cover-tests +pytest --ckan-ini=subdir/test.ini \ + --cov=ckanext.{{ project_shortname }} # strict linting flake8 . --count --max-complexity=10 --max-line-length=127 --statistics --exclude ckan,{{ project }} diff --git a/ckan/tests/config/test_environment.py b/ckan/tests/config/test_environment.py index 49501898e98..4b256f584e0 100644 --- a/ckan/tests/config/test_environment.py +++ b/ckan/tests/config/test_environment.py @@ -1,113 +1,102 @@ # encoding: utf-8 import os -from nose import tools as nosetools -from ckan.common import config +import pytest -import ckan.tests.helpers as h import ckan.plugins as p from ckan.config import environment from ckan.exceptions import CkanConfigurationException -from ckan.tests import helpers - - -class TestUpdateConfig(h.FunctionalTestBase): - - ''' - Tests for config settings from env vars, set in - config.environment.update_config(). - ''' - - ENV_VAR_LIST = [ - ('CKAN_SQLALCHEMY_URL', 'postgresql://mynewsqlurl/'), - ('CKAN_DATASTORE_WRITE_URL', 'http://mynewdbwriteurl/'), - ('CKAN_DATASTORE_READ_URL', 'http://mynewdbreadurl/'), - ('CKAN_SOLR_URL', 'http://mynewsolrurl/solr'), - ('CKAN_SITE_ID', 'my-site'), - ('CKAN_DB', 'postgresql://mydeprectatesqlurl/'), - ('CKAN_SMTP_SERVER', 'mail.example.com'), - ('CKAN_SMTP_STARTTLS', 'True'), - ('CKAN_SMTP_USER', 'my_user'), - ('CKAN_SMTP_PASSWORD', 'password'), - ('CKAN_SMTP_MAIL_FROM', 'server@example.com'), - ('CKAN_MAX_UPLOAD_SIZE_MB', '50') - ] - - def _setup_env_vars(self): - for env_var, value in self.ENV_VAR_LIST: - os.environ.setdefault(env_var, value) - # plugin.load() will force the config to update - p.load() - - def setup(self): - self._old_config = dict(config) - - def teardown(self): - for env_var, _ in self.ENV_VAR_LIST: - if os.environ.get(env_var, None): - del os.environ[env_var] - config.update(self._old_config) - # plugin.load() will force the config to update - p.load() - - def test_update_config_env_vars(self): - ''' - Setting an env var from the whitelist will set the appropriate option - in config object. - ''' - self._setup_env_vars() - - nosetools.assert_equal(config['solr_url'], 'http://mynewsolrurl/solr') - nosetools.assert_equal(config['sqlalchemy.url'], - 'postgresql://mynewsqlurl/') - nosetools.assert_equal(config['ckan.datastore.write_url'], - 'http://mynewdbwriteurl/') - nosetools.assert_equal(config['ckan.datastore.read_url'], - 'http://mynewdbreadurl/') - nosetools.assert_equal(config['ckan.site_id'], 'my-site') - nosetools.assert_equal(config['smtp.server'], 'mail.example.com') - nosetools.assert_equal(config['smtp.starttls'], 'True') - nosetools.assert_equal(config['smtp.user'], 'my_user') - nosetools.assert_equal(config['smtp.password'], 'password') - nosetools.assert_equal(config['smtp.mail_from'], 'server@example.com') - nosetools.assert_equal(config['ckan.max_resource_size'], '50') - - def test_update_config_db_url_precedence(self): - '''CKAN_SQLALCHEMY_URL in the env takes precedence over CKAN_DB''' - os.environ.setdefault('CKAN_DB', 'postgresql://mydeprectatesqlurl/') - os.environ.setdefault('CKAN_SQLALCHEMY_URL', - 'postgresql://mynewsqlurl/') - p.load() - - nosetools.assert_equal(config['sqlalchemy.url'], - 'postgresql://mynewsqlurl/') - - -class TestSiteUrlMandatory(object): - - @helpers.change_config('ckan.site_url', '') - def test_missing_siteurl(self): - nosetools.assert_raises(RuntimeError, environment.update_config) - - @helpers.change_config('ckan.site_url', 'demo.ckan.org') - def test_siteurl_missing_schema(self): - nosetools.assert_raises(RuntimeError, environment.update_config) - - @helpers.change_config('ckan.site_url', 'ftp://demo.ckan.org') - def test_siteurl_wrong_schema(self): - nosetools.assert_raises(RuntimeError, environment.update_config) - - @helpers.change_config('ckan.site_url', 'http://demo.ckan.org/') - def test_siteurl_removes_backslash(self): +ENV_VAR_LIST = [ + (u"CKAN_SQLALCHEMY_URL", u"postgresql://mynewsqlurl/"), + (u"CKAN_DATASTORE_WRITE_URL", u"http://mynewdbwriteurl/"), + (u"CKAN_DATASTORE_READ_URL", u"http://mynewdbreadurl/"), + (u"CKAN_SOLR_URL", u"http://mynewsolrurl/solr"), + (u"CKAN_SITE_ID", u"my-site"), + (u"CKAN_DB", u"postgresql://mydeprectatesqlurl/"), + (u"CKAN_SMTP_SERVER", u"mail.example.com"), + (u"CKAN_SMTP_STARTTLS", u"True"), + (u"CKAN_SMTP_USER", u"my_user"), + (u"CKAN_SMTP_PASSWORD", u"password"), + (u"CKAN_SMTP_MAIL_FROM", u"server@example.com"), + (u"CKAN_MAX_UPLOAD_SIZE_MB", u"50"), +] + + +@pytest.fixture +def reset_env(): + """Reset all environment variables that were patched during tests. + + """ + yield + for env_var, _ in ENV_VAR_LIST: + if os.environ.get(env_var, None): + del os.environ[env_var] + p.load() + + +@pytest.mark.usefixtures(u"reset_env") +def test_update_config_env_vars(ckan_config): + """ + Setting an env var from the whitelist will set the appropriate option + in config object. + """ + for env_var, value in ENV_VAR_LIST: + os.environ.setdefault(env_var, value) + # plugin.load() will force the config to update + p.load() + + assert ckan_config[u"solr_url"] == u"http://mynewsolrurl/solr" + assert ckan_config[u"sqlalchemy.url"] == u"postgresql://mynewsqlurl/" + assert ( + ckan_config[u"ckan.datastore.write_url"] == u"http://mynewdbwriteurl/" + ) + assert ckan_config[u"ckan.datastore.read_url"] == u"http://mynewdbreadurl/" + assert ckan_config[u"ckan.site_id"] == u"my-site" + assert ckan_config[u"smtp.server"] == u"mail.example.com" + assert ckan_config[u"smtp.starttls"] == u"True" + assert ckan_config[u"smtp.user"] == u"my_user" + assert ckan_config[u"smtp.password"] == u"password" + assert ckan_config[u"smtp.mail_from"] == u"server@example.com" + assert ckan_config[u"ckan.max_resource_size"] == u"50" + + +@pytest.mark.usefixtures("reset_env") +def test_update_config_db_url_precedence(ckan_config): + """CKAN_SQLALCHEMY_URL in the env takes precedence over CKAN_DB""" + os.environ.setdefault("CKAN_DB", "postgresql://mydeprectatesqlurl/") + os.environ.setdefault("CKAN_SQLALCHEMY_URL", "postgresql://mynewsqlurl/") + p.load() + + assert ckan_config["sqlalchemy.url"] == "postgresql://mynewsqlurl/" + + +@pytest.mark.ckan_config("ckan.site_url", "") +def test_missing_siteurl(): + with pytest.raises(RuntimeError): environment.update_config() - nosetools.assert_equals(config['ckan.site_url'], - 'http://demo.ckan.org') -class TestDisplayTimezone(object): +@pytest.mark.ckan_config("ckan.site_url", "demo.ckan.org") +def test_siteurl_missing_schema(): + with pytest.raises(RuntimeError): + environment.update_config() + + +@pytest.mark.ckan_config("ckan.site_url", "ftp://demo.ckan.org") +def test_siteurl_wrong_schema(): + with pytest.raises(RuntimeError): + environment.update_config() - @helpers.change_config('ckan.display_timezone', 'Krypton/Argo City') - def test_missing_timezone(self): - nosetools.assert_raises(CkanConfigurationException, environment.update_config) + +@pytest.mark.ckan_config("ckan.site_url", "http://demo.ckan.org/") +def test_siteurl_removes_backslash(ckan_config): + environment.update_config() + assert ckan_config["ckan.site_url"] == "http://demo.ckan.org" + + +@pytest.mark.ckan_config("ckan.display_timezone", "Krypton/Argo City") +def test_missing_timezone(): + with pytest.raises(CkanConfigurationException): + environment.update_config() diff --git a/ckan/tests/config/test_middleware.py b/ckan/tests/config/test_middleware.py index cdfe0ddf54e..41c80558f69 100644 --- a/ckan/tests/config/test_middleware.py +++ b/ckan/tests/config/test_middleware.py @@ -1,653 +1,498 @@ # encoding: utf-8 +import flask import mock +import pytest import wsgiref -from nose.tools import assert_equals, assert_not_equals, eq_, assert_raises -from ckan.lib.helpers import url_for from flask import Blueprint -import flask +import ckan.lib.helpers as h import ckan.model as model import ckan.plugins as p -import ckan.lib.helpers as h -import ckan.tests.helpers as helpers import ckan.tests.factories as factories from ckan.common import config, _ - from ckan.config.middleware import AskAppDispatcherMiddleware from ckan.config.middleware.flask_app import CKANFlask from ckan.config.middleware.pylons_app import CKANPylonsApp -class TestPylonsResponseCleanupMiddleware(helpers.FunctionalTestBase): - @classmethod - def _apply_config_changes(cls, config): - config['ckan.use_pylons_response_cleanup_middleware'] = True - - def test_homepage_with_middleware_activated(self): - '''Test the home page renders with the middleware activated - - We are just testing the home page renders without any troubles and that - the middleware has not done anything strange to the response string''' - app = self._get_test_app() - response = app.get(url=url_for(controller='home', action='index')) - - assert_equals(200, response.status_int) - # make sure we haven't overwritten the response too early. - assert_not_equals( - 'response cleared by pylons response cleanup middleware', - response.body - ) - - -class TestAppDispatcherPlain(object): - ''' - These tests need the test app to be created at specific times to not affect - the mocks, so they don't extend FunctionalTestBase - ''' - - def test_flask_can_handle_request_is_called_with_environ(self): - - with mock.patch.object(CKANFlask, 'can_handle_request') as \ - mock_can_handle_request: - # We need set this otherwise the mock object is returned - mock_can_handle_request.return_value = (False, 'flask_app') - - app = helpers._get_test_app() - # We want our CKAN app, not the WebTest one - ckan_app = app.app - - environ = { - 'PATH_INFO': '/', - } - wsgiref.util.setup_testing_defaults(environ) - start_response = mock.MagicMock() +_test_controller = u"ckan.tests.config.test_middleware:MockPylonsController" - ckan_app(environ, start_response) - assert mock_can_handle_request.called_with(environ) - - def test_pylons_can_handle_request_is_called_with_environ(self): - - with mock.patch.object(CKANPylonsApp, 'can_handle_request') as \ - mock_can_handle_request: - - # We need set this otherwise the mock object is returned - mock_can_handle_request.return_value = (True, 'pylons_app', 'core') - - app = helpers._get_test_app() - # We want our CKAN app, not the WebTest one - ckan_app = app.app - - environ = { - 'PATH_INFO': '/', - } - wsgiref.util.setup_testing_defaults(environ) - start_response = mock.MagicMock() - - ckan_app(environ, start_response) - - assert mock_can_handle_request.called_with(environ) - - -class TestAppDispatcher(helpers.FunctionalTestBase): - - @classmethod - def setup_class(cls): - - super(TestAppDispatcher, cls).setup_class() - - # Add a custom route to the Flask app - app = cls._get_test_app() +class MockRoutingPlugin(p.SingletonPlugin): - flask_app = app.flask_app + p.implements(p.IRoutes) + p.implements(p.IBlueprint) - def test_view(): - return 'This was served from Flask' + controller = _test_controller - # This endpoint is defined both in Flask and in Pylons core - flask_app.add_url_rule( - '/flask_core', - view_func=test_view, - endpoint='flask_core.index') + def before_map(self, _map): - # This endpoint is defined both in Flask and a Pylons extension - flask_app.add_url_rule( - '/pylons_and_flask', - view_func=test_view, - endpoint='pylons_and_flask.index' + _map.connect( + u"/from_pylons_extension_before_map", + controller=self.controller, + action=u"view", ) - def test_ask_around_is_called(self): - - app = self._get_test_app() - with mock.patch.object(AskAppDispatcherMiddleware, 'ask_around') as \ - mock_ask_around: - app.get('/', status=404) - - assert mock_ask_around.called - - def test_ask_around_is_called_with_args(self): - - app = self._get_test_app() - ckan_app = app.app - - environ = {} - start_response = mock.MagicMock() - wsgiref.util.setup_testing_defaults(environ) - - with mock.patch.object(AskAppDispatcherMiddleware, 'ask_around') as \ - mock_ask_around: - - ckan_app(environ, start_response) - assert mock_ask_around.called - mock_ask_around.assert_called_with(environ) - - def test_ask_around_flask_core_route_get(self): - - app = self._get_test_app() - - # We want our CKAN app, not the WebTest one - app = app.app - - environ = { - 'PATH_INFO': '/', - 'REQUEST_METHOD': 'GET', - } - wsgiref.util.setup_testing_defaults(environ) - - answers = app.ask_around(environ) - - eq_(answers, [(True, 'flask_app', 'core'), - (False, 'pylons_app')]) - - def test_ask_around_flask_core_route_post(self): - - app = self._get_test_app() - - # We want our CKAN app, not the WebTest one - app = app.app - - environ = { - 'PATH_INFO': '/group/new', - 'REQUEST_METHOD': 'POST', - } - wsgiref.util.setup_testing_defaults(environ) - - answers = app.ask_around(environ) - - # Even though this route is defined in Flask, there is catch all route - # in Pylons for all requests to point arbitrary urls to templates with - # the same name, so we get two positive answers - eq_(answers, [(True, 'flask_app', 'core'), - (True, 'pylons_app', 'core')]) - - def test_ask_around_pylons_core_route_get(self): - - app = self._get_test_app() - - # We want our CKAN app, not the WebTest one - app = app.app - - environ = { - 'PATH_INFO': '/tag', - 'REQUEST_METHOD': 'GET', - } - wsgiref.util.setup_testing_defaults(environ) - - answers = app.ask_around(environ) - - eq_(answers, [(False, 'flask_app'), (True, 'pylons_app', 'core')]) - - def test_ask_around_pylons_core_route_post(self): - - app = self._get_test_app() - - # We want our CKAN app, not the WebTest one - app = app.app - - environ = { - 'PATH_INFO': '/tag', - 'REQUEST_METHOD': 'POST', - } - wsgiref.util.setup_testing_defaults(environ) - - answers = app.ask_around(environ) - - eq_(answers, [(False, 'flask_app'), (True, 'pylons_app', 'core')]) - - def test_ask_around_pylons_extension_route_get_before_map(self): - - if not p.plugin_loaded('test_routing_plugin'): - p.load('test_routing_plugin') - - app = self._get_test_app() - - # We want our CKAN app, not the WebTest one - app = app.app - - environ = { - 'PATH_INFO': '/from_pylons_extension_before_map', - 'REQUEST_METHOD': 'GET', - } - wsgiref.util.setup_testing_defaults(environ) + _map.connect( + u"/from_pylons_extension_before_map_post_only", + controller=self.controller, + action=u"view", + conditions={u"method": u"POST"}, + ) + # This one conflicts with an extension Flask route + _map.connect( + u"/pylons_and_flask", controller=self.controller, action=u"view" + ) - answers = app.ask_around(environ) + # This one conflicts with a core Flask route + _map.connect(u"/about", controller=self.controller, action=u"view") - eq_(answers, [(False, 'flask_app'), (True, 'pylons_app', 'extension')]) + _map.connect( + u"/pylons_route_flask_url_for", + controller=self.controller, + action=u"test_flask_url_for", + ) + _map.connect( + u"/pylons_translated", + controller=self.controller, + action=u"test_translation", + ) - p.unload('test_routing_plugin') + return _map - def test_ask_around_pylons_extension_route_post(self): + def after_map(self, _map): - if not p.plugin_loaded('test_routing_plugin'): - p.load('test_routing_plugin') + _map.connect( + u"/from_pylons_extension_after_map", + controller=self.controller, + action=u"view", + ) - app = self._get_test_app() + return _map - # We want our CKAN app, not the WebTest one - app = app.app + def get_blueprint(self): + # Create Blueprint for plugin + blueprint = Blueprint(self.name, self.__module__) - environ = { - 'PATH_INFO': '/from_pylons_extension_before_map_post_only', - 'REQUEST_METHOD': 'POST', - } - wsgiref.util.setup_testing_defaults(environ) + blueprint.add_url_rule( + u"/simple_flask", u"flask_plugin_view", flask_plugin_view + ) - answers = app.ask_around(environ) + blueprint.add_url_rule( + u"/flask_route_pylons_url_for", + u"flask_route_pylons_url_for", + flask_plugin_view_url_for, + ) + blueprint.add_url_rule( + u"/flask_translated", u"flask_translated", flask_translated_view + ) - eq_(answers, [(False, 'flask_app'), (True, 'pylons_app', 'extension')]) + return blueprint - p.unload('test_routing_plugin') - def test_ask_around_pylons_extension_route_post_using_get(self): +def flask_plugin_view(): + return u"Hello World, this is served from a Flask extension" - if not p.plugin_loaded('test_routing_plugin'): - p.load('test_routing_plugin') - app = self._get_test_app() +def flask_plugin_view_url_for(): + url = h.url_for(controller=_test_controller, action=u"view") + return u"This URL was generated by Pylons: {0}".format(url) - # We want our CKAN app, not the WebTest one - app = app.app - environ = { - 'PATH_INFO': '/from_pylons_extension_before_map_post_only', - 'REQUEST_METHOD': 'GET', - } - wsgiref.util.setup_testing_defaults(environ) +def flask_translated_view(): + return _(u"Dataset") - answers = app.ask_around(environ) - # We are going to get an answer from Pylons, but just because it will - # match the catch-all template route, hence the `core` origin. - eq_(answers, [(False, 'flask_app'), (True, 'pylons_app', 'core')]) +class MockPylonsController(p.toolkit.BaseController): + def view(self): + return u"Hello World, this is served from a Pylons extension" - p.unload('test_routing_plugin') + def test_flask_url_for(self): + url = h.url_for(u"api.get_api", ver=3) + return u"This URL was generated by Flask: {0}".format(url) - def test_ask_around_pylons_extension_route_get_after_map(self): + def test_translation(self): + return _(u"Groups") - if not p.plugin_loaded('test_routing_plugin'): - p.load('test_routing_plugin') - app = self._get_test_app() +@pytest.fixture +def patched_app(app): + flask_app = app.flask_app - # We want our CKAN app, not the WebTest one - app = app.app + def test_view(): + return u"This was served from Flask" - environ = { - 'PATH_INFO': '/from_pylons_extension_after_map', - 'REQUEST_METHOD': 'GET', - } - wsgiref.util.setup_testing_defaults(environ) + # This endpoint is defined both in Flask and in Pylons core + flask_app.add_url_rule( + u"/flask_core", view_func=test_view, endpoint=u"flask_core.index" + ) - answers = app.ask_around(environ) + # This endpoint is defined both in Flask and a Pylons extension + flask_app.add_url_rule( + u"/pylons_and_flask", + view_func=test_view, + endpoint=u"pylons_and_flask.index", + ) + return app - eq_(answers, [(False, 'flask_app'), (True, 'pylons_app', 'extension')]) - p.unload('test_routing_plugin') +@pytest.mark.ckan_config(u"ckan.plugins", u"test_routing_plugin") +def test_ask_around_flask_core_and_pylons_extension_route(patched_app): - def test_ask_around_flask_core_and_pylons_extension_route(self): + environ = {u"PATH_INFO": u"/pylons_and_flask", u"REQUEST_METHOD": u"GET"} + wsgiref.util.setup_testing_defaults(environ) - if not p.plugin_loaded('test_routing_plugin'): - p.load('test_routing_plugin') + answers = patched_app.app.ask_around(environ) + answers = sorted(answers, key=lambda a: a[1]) - app = self._get_test_app() + assert answers == [ + (True, u"flask_app", u"core"), + (True, u"pylons_app", u"extension"), + ] - # We want our CKAN app, not the WebTest one - app = app.app - environ = { - 'PATH_INFO': '/pylons_and_flask', - 'REQUEST_METHOD': 'GET', - } - wsgiref.util.setup_testing_defaults(environ) +@pytest.mark.ckan_config(u"ckan.plugins", u"test_routing_plugin") +def test_flask_core_and_pylons_extension_route_is_served_by_pylons( + patched_app +): + res = patched_app.get(u"/pylons_and_flask") - answers = app.ask_around(environ) - answers = sorted(answers, key=lambda a: a[1]) + assert res.environ[u"ckan.app"] == u"pylons_app" + assert res.body == u"Hello World, this is served from a Pylons extension" - eq_(answers, [(True, 'flask_app', 'core'), - (True, 'pylons_app', 'extension')]) - p.unload('test_routing_plugin') +def test_ask_around_pylons_core_route_get(patched_app): + environ = {u"PATH_INFO": u"/tag", u"REQUEST_METHOD": u"GET"} + wsgiref.util.setup_testing_defaults(environ) - def test_flask_core_route_is_served_by_flask(self): + answers = patched_app.app.ask_around(environ) - app = self._get_test_app() + assert answers == [(False, u"flask_app"), (True, u"pylons_app", u"core")] - res = app.get('/') - eq_(res.environ['ckan.app'], 'flask_app') +def test_ask_around_pylons_core_route_post(patched_app): + environ = {u"PATH_INFO": u"/tag", u"REQUEST_METHOD": u"POST"} + wsgiref.util.setup_testing_defaults(environ) - def test_flask_extension_route_is_served_by_flask(self): + answers = patched_app.app.ask_around(environ) - app = self._get_test_app() + assert answers == [(False, u"flask_app"), (True, u"pylons_app", u"core")] - # Install plugin and register its blueprint - if not p.plugin_loaded('test_routing_plugin'): - p.load('test_routing_plugin') - plugin = p.get_plugin('test_routing_plugin') - app.flask_app.register_extension_blueprint(plugin.get_blueprint()) - res = app.get('/simple_flask') +@pytest.mark.ckan_config(u"ckan.plugins", u"test_routing_plugin") +def test_ask_around_pylons_extension_route_get_before_map(patched_app): + environ = { + u"PATH_INFO": u"/from_pylons_extension_before_map", + u"REQUEST_METHOD": u"GET", + } + wsgiref.util.setup_testing_defaults(environ) - eq_(res.environ['ckan.app'], 'flask_app') + answers = patched_app.app.ask_around(environ) - p.unload('test_routing_plugin') + assert answers == [ + (False, u"flask_app"), + (True, u"pylons_app", u"extension"), + ] - def test_pylons_extension_route_is_served_by_pylons(self): - if not p.plugin_loaded('test_routing_plugin'): - p.load('test_routing_plugin') +@pytest.mark.ckan_config(u"ckan.plugins", u"test_routing_plugin") +def test_ask_around_pylons_extension_route_post(patched_app): + environ = { + u"PATH_INFO": u"/from_pylons_extension_before_map_post_only", + u"REQUEST_METHOD": u"POST", + } + wsgiref.util.setup_testing_defaults(environ) - app = self._get_test_app() + answers = patched_app.app.ask_around(environ) - res = app.get('/from_pylons_extension_before_map') + assert answers == [ + (False, u"flask_app"), + (True, u"pylons_app", u"extension"), + ] - eq_(res.environ['ckan.app'], 'pylons_app') - eq_(res.body, 'Hello World, this is served from a Pylons extension') - p.unload('test_routing_plugin') +@pytest.mark.ckan_config(u"ckan.plugins", u"test_routing_plugin") +def test_ask_around_pylons_extension_route_post_using_get(patched_app): + environ = { + u"PATH_INFO": u"/from_pylons_extension_before_map_post_only", + u"REQUEST_METHOD": u"GET", + } + wsgiref.util.setup_testing_defaults(environ) - def test_flask_core_and_pylons_extension_route_is_served_by_pylons(self): + answers = patched_app.app.ask_around(environ) - if not p.plugin_loaded('test_routing_plugin'): - p.load('test_routing_plugin') + # We are going to get an answer from Pylons, but just because it will + # match the catch-all template route, hence the `core` origin. + assert answers == [(False, u"flask_app"), (True, u"pylons_app", u"core")] - app = self._get_test_app() - res = app.get('/pylons_and_flask') +@pytest.mark.ckan_config(u"ckan.plugins", u"test_routing_plugin") +def test_ask_around_pylons_extension_route_get_after_map(patched_app): + environ = { + u"PATH_INFO": u"/from_pylons_extension_after_map", + u"REQUEST_METHOD": u"GET", + } + wsgiref.util.setup_testing_defaults(environ) - eq_(res.environ['ckan.app'], 'pylons_app') - eq_(res.body, 'Hello World, this is served from a Pylons extension') + answers = patched_app.app.ask_around(environ) - p.unload('test_routing_plugin') + assert answers == [ + (False, u"flask_app"), + (True, u"pylons_app", u"extension"), + ] - def test_flask_core_and_pylons_core_route_is_served_by_flask(self): - ''' - This should never happen in core, but just in case - ''' - app = self._get_test_app() - res = app.get('/flask_core') - eq_(res.environ['ckan.app'], 'flask_app') - eq_(res.body, 'This was served from Flask') +def test_flask_core_route_is_served_by_flask(patched_app): + res = patched_app.get(u"/") + assert res.environ[u"ckan.app"] == u"flask_app" -class TestFlaskUserIdentifiedInRequest(helpers.FunctionalTestBase): - '''Flask identifies user during each request. +@pytest.mark.ckan_config(u"ckan.plugins", u"test_routing_plugin") +def test_flask_extension_route_is_served_by_flask(patched_app): + res = patched_app.get(u"/simple_flask") + assert res.environ[u"ckan.app"] == u"flask_app" - Flask route provided by test.helpers.SimpleFlaskPlugin. - ''' - @classmethod - def setup_class(cls): - super(TestFlaskUserIdentifiedInRequest, cls).setup_class() - cls.app = cls._get_test_app() - cls.flask_app = cls.app.flask_app +@pytest.mark.ckan_config(u"ckan.plugins", u"test_routing_plugin") +def test_pylons_extension_route_is_served_by_pylons(patched_app): - if not p.plugin_loaded('test_routing_plugin'): - p.load('test_routing_plugin') - plugin = p.get_plugin('test_routing_plugin') - cls.flask_app.register_extension_blueprint( - plugin.get_blueprint()) + res = patched_app.get(u"/from_pylons_extension_before_map") - @classmethod - def teardown_class(cls): - super(TestFlaskUserIdentifiedInRequest, cls).teardown_class() - p.unload('test_routing_plugin') + assert res.environ[u"ckan.app"] == u"pylons_app" + assert res.body == u"Hello World, this is served from a Pylons extension" - def test_user_objects_in_g_normal_user(self): - ''' - A normal logged in user request will have expected user objects added - to request. - ''' - user = factories.User() - test_user_obj = model.User.by_name(user['name']) - with self.flask_app.app_context(): - self.app.get( - '/simple_flask', - extra_environ={'REMOTE_USER': user['name'].encode('ascii')},) - eq_(flask.g.user, user['name']) - eq_(flask.g.userobj, test_user_obj) - eq_(flask.g.author, user['name']) - eq_(flask.g.remote_addr, 'Unknown IP Address') +def test_flask_core_and_pylons_core_route_is_served_by_flask(patched_app): + """ + This should never happen in core, but just in case + """ + res = patched_app.get(u"/flask_core") - def test_user_objects_in_g_anon_user(self): - ''' - An anon user request will have expected user objects added to request. - ''' - with self.flask_app.app_context(): - self.app.get( - '/simple_flask', - extra_environ={'REMOTE_USER': ''},) - eq_(flask.g.user, '') - eq_(flask.g.userobj, None) - eq_(flask.g.author, 'Unknown IP Address') - eq_(flask.g.remote_addr, 'Unknown IP Address') + assert res.environ[u"ckan.app"] == u"flask_app" + assert res.body == u"This was served from Flask" - def test_user_objects_in_g_sysadmin(self): - ''' - A sysadmin user request will have expected user objects added to - request. - ''' - user = factories.Sysadmin() - test_user_obj = model.User.by_name(user['name']) - with self.flask_app.app_context(): - self.app.get( - '/simple_flask', - extra_environ={'REMOTE_USER': user['name'].encode('ascii')},) - eq_(flask.g.user, user['name']) - eq_(flask.g.userobj, test_user_obj) - eq_(flask.g.author, user['name']) - eq_(flask.g.remote_addr, 'Unknown IP Address') - - -class TestPylonsUserIdentifiedInRequest(helpers.FunctionalTestBase): - - '''Pylons identifies user during each request. - - Using a route setup via an extension to ensure we're always testing a - Pylons-flavoured request. - ''' - - def test_user_objects_in_c_normal_user(self): - ''' - A normal logged in user request will have expected user objects added - to request. - ''' - if not p.plugin_loaded('test_routing_plugin'): - p.load('test_routing_plugin') - - app = self._get_test_app() - user = factories.User() - test_user_obj = model.User.by_name(user['name']) - - resp = app.get( - '/from_pylons_extension_before_map', - extra_environ={'REMOTE_USER': user['name'].encode('ascii')}) - - # tmpl_context available on response - eq_(resp.tmpl_context.user, user['name']) - eq_(resp.tmpl_context.userobj, test_user_obj) - eq_(resp.tmpl_context.author, user['name']) - eq_(resp.tmpl_context.remote_addr, 'Unknown IP Address') - - p.unload('test_routing_plugin') - - def test_user_objects_in_c_anon_user(self): - ''' - An anon user request will have expected user objects added to request. - ''' - if not p.plugin_loaded('test_routing_plugin'): - p.load('test_routing_plugin') - - app = self._get_test_app() - - resp = app.get( - '/from_pylons_extension_before_map', - extra_environ={'REMOTE_USER': ''}) - - # tmpl_context available on response - eq_(resp.tmpl_context.user, '') - eq_(resp.tmpl_context.userobj, None) - eq_(resp.tmpl_context.author, 'Unknown IP Address') - eq_(resp.tmpl_context.remote_addr, 'Unknown IP Address') - - p.unload('test_routing_plugin') - - def test_user_objects_in_c_sysadmin(self): - ''' - A sysadmin user request will have expected user objects added to - request. - ''' - if not p.plugin_loaded('test_routing_plugin'): - p.load('test_routing_plugin') - - app = self._get_test_app() - user = factories.Sysadmin() - test_user_obj = model.User.by_name(user['name']) - - resp = app.get( - '/from_pylons_extension_before_map', - extra_environ={'REMOTE_USER': user['name'].encode('ascii')}) - - # tmpl_context available on response - eq_(resp.tmpl_context.user, user['name']) - eq_(resp.tmpl_context.userobj, test_user_obj) - eq_(resp.tmpl_context.author, user['name']) - eq_(resp.tmpl_context.remote_addr, 'Unknown IP Address') - - p.unload('test_routing_plugin') - - -_test_controller = 'ckan.tests.config.test_middleware:MockPylonsController' +@pytest.mark.usefixtures(u"clean_db") +@pytest.mark.ckan_config(u"ckan.plugins", u"test_routing_plugin") +def test_user_objects_in_g_normal_user(app): + """ + A normal logged in user request will have expected user objects added + to request. + """ + username = factories.User()[u"name"] + test_user_obj = model.User.by_name(username) + with app.flask_app.app_context(): + app.get( + u"/simple_flask", + extra_environ={u"REMOTE_USER": username.encode(u"ascii")}, + ) + assert flask.g.user == username + assert flask.g.userobj == test_user_obj + assert flask.g.author == username + assert flask.g.remote_addr == u"Unknown IP Address" + + +@pytest.mark.usefixtures(u"clean_db") +@pytest.mark.ckan_config(u"ckan.plugins", u"test_routing_plugin") +def test_user_objects_in_g_anon_user(app): + """ + An anon user request will have expected user objects added to request. + """ + with app.flask_app.app_context(): + app.get(u"/simple_flask", extra_environ={u"REMOTE_USER": str(u"")}) + assert flask.g.user == u"" + assert flask.g.userobj is None + assert flask.g.author == u"Unknown IP Address" + assert flask.g.remote_addr == u"Unknown IP Address" + + +@pytest.mark.usefixtures(u"clean_db") +@pytest.mark.ckan_config(u"ckan.plugins", u"test_routing_plugin") +def test_user_objects_in_g_sysadmin(app): + """ + A sysadmin user request will have expected user objects added to + request. + """ + user = factories.Sysadmin() + test_user_obj = model.User.by_name(user[u"name"]) + + with app.flask_app.app_context(): + app.get( + u"/simple_flask", + extra_environ={u"REMOTE_USER": user[u"name"].encode(u"ascii")}, + ) + assert flask.g.user == user[u"name"] + assert flask.g.userobj == test_user_obj + assert flask.g.author == user[u"name"] + assert flask.g.remote_addr == u"Unknown IP Address" + + +@pytest.mark.ckan_config(u"ckan.plugins", u"test_routing_plugin") +def test_user_objects_in_c_normal_user(app): + """ + A normal logged in user request will have expected user objects added + to request. + """ + username = factories.User()[u"name"] + test_user_obj = model.User.by_name(username) + + resp = app.get( + u"/from_pylons_extension_before_map", + extra_environ={u"REMOTE_USER": username.encode(u"ascii")}, + ) + + # tmpl_context available on response + assert resp.tmpl_context.user == username + assert resp.tmpl_context.userobj == test_user_obj + assert resp.tmpl_context.author == username + assert resp.tmpl_context.remote_addr == u"Unknown IP Address" + + +@pytest.mark.ckan_config(u"ckan.plugins", u"test_routing_plugin") +def test_user_objects_in_c_anon_user(app): + """An anon user request will have expected user objects added to + request. + """ + + resp = app.get( + u"/from_pylons_extension_before_map", + extra_environ={u"REMOTE_USER": str(u"")}, + ) + + # tmpl_context available on response + assert resp.tmpl_context.user == u"" + assert resp.tmpl_context.userobj is None + assert resp.tmpl_context.author == u"Unknown IP Address" + assert resp.tmpl_context.remote_addr == u"Unknown IP Address" -class MockRoutingPlugin(p.SingletonPlugin): - p.implements(p.IRoutes) - p.implements(p.IBlueprint) +@pytest.mark.ckan_config(u"ckan.plugins", u"test_routing_plugin") +def test_user_objects_in_c_sysadmin(app): + """A sysadmin user request will have expected user objects added to + request. + """ + username = factories.Sysadmin()[u"name"] + test_user_obj = model.User.by_name(username) - controller = _test_controller + resp = app.get( + u"/from_pylons_extension_before_map", + extra_environ={u"REMOTE_USER": username.encode(u"ascii")}, + ) - def before_map(self, _map): + # tmpl_context available on response + assert resp.tmpl_context.user == username + assert resp.tmpl_context.userobj == test_user_obj + assert resp.tmpl_context.author == username + assert resp.tmpl_context.remote_addr == u"Unknown IP Address" - _map.connect('/from_pylons_extension_before_map', - controller=self.controller, action='view') - _map.connect('/from_pylons_extension_before_map_post_only', - controller=self.controller, action='view', - conditions={'method': 'POST'}) - # This one conflicts with an extension Flask route - _map.connect('/pylons_and_flask', - controller=self.controller, action='view') +@pytest.mark.ckan_config(u"SECRET_KEY", u"super_secret_stuff") +def test_secret_key_is_used_if_present(app): + assert app.flask_app.config[u"SECRET_KEY"] == u"super_secret_stuff" - # This one conflicts with a core Flask route - _map.connect('/about', - controller=self.controller, action='view') - _map.connect('/pylons_route_flask_url_for', - controller=self.controller, action='test_flask_url_for') - _map.connect('/pylons_translated', - controller=self.controller, action='test_translation') +@pytest.mark.ckan_config(u"SECRET_KEY", None) +def test_beaker_secret_is_used_by_default(app): + assert ( + app.flask_app.config[u"SECRET_KEY"] == config[u"beaker.session.secret"] + ) - return _map - def after_map(self, _map): +@pytest.mark.ckan_config(u"SECRET_KEY", None) +@pytest.mark.ckan_config(u"beaker.session.secret", None) +def test_no_beaker_secret_crashes(make_app): + # TODO: When Pylons is finally removed, we should test for + # RuntimeError instead (thrown on `make_flask_stack`) + with pytest.raises( + RuntimeError + ): + make_app() - _map.connect('/from_pylons_extension_after_map', - controller=self.controller, action='view') - return _map +@pytest.mark.ckan_config(u"ckan.plugins", u"test_routing_plugin") +@pytest.mark.ckan_config(u"ckan.use_pylons_response_cleanup_middleware", True) +def test_pylons_route_with_cleanup_middleware_activated(app): + u"""Test the home page renders with the middleware activated - def get_blueprint(self): - # Create Blueprint for plugin - blueprint = Blueprint(self.name, self.__module__) - # Add plugin url rule to Blueprint object - blueprint.add_url_rule('/pylons_and_flask', 'flask_plugin_view', - flask_plugin_view) + We are just testing the home page renders without any troubles and that + the middleware has not done anything strange to the response string""" - blueprint.add_url_rule('/simple_flask', 'flask_plugin_view', - flask_plugin_view) + response = app.get(url=u"/pylons_translated") - blueprint.add_url_rule('/flask_route_pylons_url_for', - 'flask_route_pylons_url_for', - flask_plugin_view_url_for) - blueprint.add_url_rule('/flask_translated', 'flask_translated', - flask_translated_view) + assert response.status_int == 200 + # make sure we haven't overwritten the response too early. + assert u"cleanup middleware" not in response.body - return blueprint +@pytest.mark.parametrize( + u"rv,app_base", + [ + ((False, u"flask_app"), CKANFlask), + ((True, u"pylons_app", u"core"), CKANPylonsApp), + ], +) +def test_can_handle_request_with_environ(monkeypatch, app, rv, app_base): + ckan_app = app.app -def flask_plugin_view(): - return 'Hello World, this is served from a Flask extension' + handler = mock.Mock(return_value=rv) + monkeypatch.setattr(app_base, u"can_handle_request", handler) + environ = {u"PATH_INFO": str(u"/")} + wsgiref.util.setup_testing_defaults(environ) + start_response = mock.MagicMock() + ckan_app(environ, start_response) -def flask_plugin_view_url_for(): - url = h.url_for(controller=_test_controller, action='view') - return 'This URL was generated by Pylons: {0}'.format(url) + assert handler.called_with(environ) -def flask_translated_view(): - return _('Dataset') +def test_ask_around_is_called(monkeypatch, app): + ask = mock.MagicMock() + monkeypatch.setattr(AskAppDispatcherMiddleware, u"ask_around", ask) + app.get(u"/", status=404) + assert ask.called -class MockPylonsController(p.toolkit.BaseController): +def test_ask_around_is_called_with_args(monkeypatch, app): + ckan_app = app.app - def view(self): - return 'Hello World, this is served from a Pylons extension' + environ = {} + start_response = mock.MagicMock() + wsgiref.util.setup_testing_defaults(environ) - def test_flask_url_for(self): - url = h.url_for('api.get_api', ver=3) - return 'This URL was generated by Flask: {0}'.format(url) + ask = mock.MagicMock() + monkeypatch.setattr(AskAppDispatcherMiddleware, u"ask_around", ask) - def test_translation(self): - return _('Groups') + ckan_app(environ, start_response) + assert ask.called + ask.assert_called_with(environ) -class TestSecretKey(object): +def test_ask_around_flask_core_route_get(app): + ckan_app = app.app - @helpers.change_config('SECRET_KEY', 'super_secret_stuff') - def test_secret_key_is_used_if_present(self): + environ = {u"PATH_INFO": u"/", u"REQUEST_METHOD": u"GET"} + wsgiref.util.setup_testing_defaults(environ) - app = helpers._get_test_app() + answers = ckan_app.ask_around(environ) - eq_(app.flask_app.config['SECRET_KEY'], - u'super_secret_stuff') + assert answers == [(True, u"flask_app", u"core"), (False, u"pylons_app")] - @helpers.change_config('SECRET_KEY', None) - def test_beaker_secret_is_used_by_default(self): - app = helpers._get_test_app() +def test_ask_around_flask_core_route_post(app): + ckan_app = app.app - eq_(app.flask_app.config['SECRET_KEY'], - config['beaker.session.secret']) + environ = {u"PATH_INFO": u"/group/new", u"REQUEST_METHOD": u"POST"} + wsgiref.util.setup_testing_defaults(environ) - @helpers.change_config('SECRET_KEY', None) - @helpers.change_config('beaker.session.secret', None) - def test_no_beaker_secret_crashes(self): + answers = ckan_app.ask_around(environ) - assert_raises(RuntimeError, helpers._get_test_app) + # Even though this route is defined in Flask, there is catch all route + # in Pylons for all requests to point arbitrary urls to templates with + # the same name, so we get two positive answers + assert answers == [ + (True, u"flask_app", u"core"), + (True, u"pylons_app", u"core"), + ] diff --git a/ckan/tests/config/test_sessions.py b/ckan/tests/config/test_sessions.py index 45fc05d1e22..a4c6ac840e0 100644 --- a/ckan/tests/config/test_sessions.py +++ b/ckan/tests/config/test_sessions.py @@ -1,127 +1,123 @@ # encoding: utf-8 -from nose.tools import ok_ +import pytest +from flask import Blueprint, render_template -from flask import Blueprint -from flask import render_template -from ckan.lib.base import render as pylons_render - -import ckan.plugins as p -import ckan.tests.helpers as helpers import ckan.lib.helpers as h +import ckan.plugins as p +from ckan.lib.base import render as pylons_render -class TestCrossFlaskPylonsFlashMessages(helpers.FunctionalTestBase): - u''' - Test that flash message set in the Pylons controller can be accessed by - Flask views, and visa versa. - ''' - - def setup(self): - self.app = helpers._get_test_app() +@pytest.mark.ckan_config(u"ckan.plugins", u"test_flash_plugin") +def test_flash_populated_by_flask_redirect_to_flask(app): + u""" + Flash store is populated by Flask view is accessible by another Flask + view. + """ + res = app.get(u"/flask_add_flash_message_redirect_to_flask").follow() - # Install plugin and register its blueprint - if not p.plugin_loaded(u'test_flash_plugin'): - p.load(u'test_flash_plugin') - plugin = p.get_plugin(u'test_flash_plugin') - self.app.flask_app.register_extension_blueprint( - plugin.get_blueprint()) + assert u"This is a success message populated by Flask" in res.body - def test_flash_populated_by_flask_redirect_to_flask(self): - u''' - Flash store is populated by Flask view is accessible by another Flask - view. - ''' - res = self.app.get( - u'/flask_add_flash_message_redirect_to_flask').follow() - ok_(u'This is a success message populated by Flask' in res.body) +@pytest.mark.ckan_config(u"ckan.plugins", u"test_flash_plugin") +def test_flash_populated_in_pylons_action_redirect_to_flask(app): + u""" + Flash store is populated by pylons action is accessible by Flask view. + """ + res = app.get(u"/pylons_add_flash_message_redirect_view").follow() - def test_flash_populated_in_pylons_action_redirect_to_flask(self): - u''' - Flash store is populated by pylons action is accessible by Flask view. - ''' - res = self.app.get(u'/pylons_add_flash_message_redirect_view').follow() + assert u"This is a success message populated by Pylons" in res.body - ok_(u'This is a success message populated by Pylons' in res.body) - def test_flash_populated_in_flask_view_redirect_to_pylons(self): - u''' - Flash store is populated by flask view is accessible by pylons action. - ''' - res = self.app.get( - u'/flask_add_flash_message_redirect_pylons').follow() +@pytest.mark.ckan_config(u"ckan.plugins", u"test_flash_plugin") +def test_flash_populated_in_flask_view_redirect_to_pylons(app): + u""" + Flash store is populated by flask view is accessible by pylons action. + """ + res = app.get(u"/flask_add_flash_message_redirect_pylons").follow() - ok_(u'This is a success message populated by Flask' in res.body) + assert u"This is a success message populated by Flask" in res.body class FlashMessagePlugin(p.SingletonPlugin): - u''' + u""" A Flask and Pylons compatible IRoutes/IBlueprint plugin to add Flask views and Pylons actions to display flash messages. - ''' + """ p.implements(p.IRoutes, inherit=True) p.implements(p.IBlueprint) def flash_message_view(self): - u'''Flask view that renders the flash message html template.''' - return render_template(u'tests/flash_messages.html') + u"""Flask view that renders the flash message html template.""" + return render_template(u"tests/flash_messages.html") def add_flash_message_view_redirect_to_flask(self): - u'''Add flash message, then redirect to Flask view to render it.''' - h.flash_success(u'This is a success message populated by Flask') + u"""Add flash message, then redirect to Flask view to render it.""" + h.flash_success(u"This is a success message populated by Flask") return h.redirect_to( - h.url_for(u'test_flash_plugin.flash_message_view')) + h.url_for(u"test_flash_plugin.flash_message_view") + ) def add_flash_message_view_redirect_to_pylons(self): - u'''Add flash message, then redirect to view that renders it''' - h.flash_success(u'This is a success message populated by Flask') - return h.redirect_to(u'/pylons_view_flash_message') + u"""Add flash message, then redirect to view that renders it""" + h.flash_success(u"This is a success message populated by Flask") + return h.redirect_to(u"/pylons_view_flash_message") def get_blueprint(self): - u'''Return Flask Blueprint object to be registered by the Flask app.''' + u"""Return Flask Blueprint object to be registered by the Flask app.""" # Create Blueprint for plugin blueprint = Blueprint(self.name, self.__module__) # Add plugin url rules to Blueprint object rules = [ - (u'/flask_add_flash_message_redirect_to_flask', - u'add_flash_message', - self.add_flash_message_view_redirect_to_flask), - (u'/flask_add_flash_message_redirect_pylons', - u'add_flash_message_view_redirect_to_pylons', - self.add_flash_message_view_redirect_to_pylons), - (u'/flask_view_flash_message', u'flash_message_view', - self.flash_message_view), + ( + u"/flask_add_flash_message_redirect_to_flask", + u"add_flash_message", + self.add_flash_message_view_redirect_to_flask, + ), + ( + u"/flask_add_flash_message_redirect_pylons", + u"add_flash_message_view_redirect_to_pylons", + self.add_flash_message_view_redirect_to_pylons, + ), + ( + u"/flask_view_flash_message", + u"flash_message_view", + self.flash_message_view, + ), ] for rule in rules: blueprint.add_url_rule(*rule) return blueprint - controller = \ - u'ckan.tests.config.test_sessions:PylonsAddFlashMessageController' + controller = ( + u"ckan.tests.config.test_sessions:PylonsAddFlashMessageController" + ) def before_map(self, _map): - u'''Update the pylons route map to be used by the Pylons app.''' - _map.connect(u'/pylons_add_flash_message_redirect_view', - controller=self.controller, - action=u'add_flash_message_redirect') - - _map.connect(u'/pylons_view_flash_message', - controller=self.controller, - action=u'flash_message_action') + u"""Update the pylons route map to be used by the Pylons app.""" + _map.connect( + u"/pylons_add_flash_message_redirect_view", + controller=self.controller, + action=u"add_flash_message_redirect", + ) + + _map.connect( + u"/pylons_view_flash_message", + controller=self.controller, + action=u"flash_message_action", + ) return _map class PylonsAddFlashMessageController(p.toolkit.BaseController): - def flash_message_action(self): - u'''Pylons view to render flash messages in a template.''' - return pylons_render(u'tests/flash_messages.html') + u"""Pylons view to render flash messages in a template.""" + return pylons_render(u"tests/flash_messages.html") def add_flash_message_redirect(self): # Adds a flash message and redirects to flask view - h.flash_success(u'This is a success message populated by Pylons') - return h.redirect_to(u'/flask_view_flash_message') + h.flash_success(u"This is a success message populated by Pylons") + return h.redirect_to(u"/flask_view_flash_message") diff --git a/ckan/tests/controllers/__init__.py b/ckan/tests/controllers/__init__.py index 4f48ec7afd4..4eb3d6d08ce 100644 --- a/ckan/tests/controllers/__init__.py +++ b/ckan/tests/controllers/__init__.py @@ -1,6 +1,5 @@ # encoding: utf-8 - -''' +""" Controller tests probably shouldn't use mocking. .. todo:: @@ -52,4 +51,4 @@ this it may be necessary to write a lot of controller tests to get this code's behavior into a test harness before it can be safely refactored. -''' +""" diff --git a/ckan/tests/controllers/test_admin.py b/ckan/tests/controllers/test_admin.py index 99aeba12b84..c67020d91c1 100644 --- a/ckan/tests/controllers/test_admin.py +++ b/ckan/tests/controllers/test_admin.py @@ -1,418 +1,404 @@ # encoding: utf-8 -from nose.tools import assert_true, assert_equal - +import pytest from bs4 import BeautifulSoup -from ckan.lib.helpers import url_for -from ckan.common import config import ckan.model as model -import ckan.tests.helpers as helpers import ckan.tests.factories as factories +import ckan.tests.helpers as helpers +from ckan.common import config +from ckan.lib.helpers import url_for from ckan.model.system_info import get_system_info - submit_and_follow = helpers.submit_and_follow webtest_submit = helpers.webtest_submit def _get_admin_config_page(app): user = factories.Sysadmin() - env = {'REMOTE_USER': user['name'].encode('ascii')} + env = {"REMOTE_USER": user["name"].encode("ascii")} response = app.get( - url=url_for(controller='admin', action='config'), - extra_environ=env, + url=url_for(controller="admin", action="config"), extra_environ=env ) return env, response def _reset_config(app): - '''Reset config via action''' + """Reset config via action""" user = factories.Sysadmin() - env = {'REMOTE_USER': user['name'].encode('ascii')} - app.post( - url=url_for('admin.reset_config'), - extra_environ=env, - ) + env = {"REMOTE_USER": user["name"].encode("ascii")} + app.post(url=url_for("admin.reset_config"), extra_environ=env) -class TestConfig(helpers.FunctionalTestBase): - '''View tests to go along with 'Customizing look and feel' docs.''' +class TestConfig(object): + """View tests to go along with 'Customizing look and feel' docs.""" - def teardown(self): - helpers.reset_db() + @pytest.mark.usefixtures("clean_db") + def test_form_renders(self, app): + """admin-config-form in the response""" - def test_form_renders(self): - '''admin-config-form in the response''' - app = self._get_test_app() env, response = _get_admin_config_page(app) - assert_true('admin-config-form' in response.forms) + assert "admin-config-form" in response.forms - def test_site_title(self): - '''Configure the site title''' + @pytest.mark.usefixtures("clean_db") + def test_site_title(self, app): + """Configure the site title""" # current site title - app = self._get_test_app() - - index_response = app.get('/') - assert_true('Welcome - CKAN' in index_response) + index_response = app.get("/") + assert "Welcome - CKAN" in index_response # change site title env, config_response = _get_admin_config_page(app) - config_form = config_response.forms['admin-config-form'] - config_form['ckan.site_title'] = 'Test Site Title' - webtest_submit(config_form, 'save', status=302, extra_environ=env) + config_form = config_response.forms["admin-config-form"] + config_form["ckan.site_title"] = "Test Site Title" + webtest_submit(config_form, "save", status=302, extra_environ=env) # new site title - new_index_response = app.get('/') - assert_true('Welcome - Test Site Title' in new_index_response) + new_index_response = app.get("/") + assert "Welcome - Test Site Title" in new_index_response # reset config value _reset_config(app) - reset_index_response = app.get('/') - assert_true('Welcome - CKAN' in reset_index_response) + reset_index_response = app.get("/") + assert "Welcome - CKAN" in reset_index_response - def test_main_css_list(self): - '''Style list contains pre-configured styles''' + @pytest.mark.usefixtures("clean_db") + def test_main_css_list(self, app): + """Style list contains pre-configured styles""" - STYLE_NAMES = [ - 'Default', - 'Red', - 'Green', - 'Maroon', - 'Fuchsia' - ] - - app = self._get_test_app() + STYLE_NAMES = ["Default", "Red", "Green", "Maroon", "Fuchsia"] env, config_response = _get_admin_config_page(app) config_response_html = BeautifulSoup(config_response.body) - style_select_options = \ - config_response_html.select('#field-ckan-main-css option') + style_select_options = config_response_html.select( + "#field-ckan-main-css option" + ) for option in style_select_options: - assert_true(option.string in STYLE_NAMES) + assert option.string in STYLE_NAMES - def test_main_css(self): - '''Select a colour style''' - app = self._get_test_app() + @pytest.mark.usefixtures("clean_db") + def test_main_css(self, app): + """Select a colour style""" # current style - index_response = app.get('/') - assert_true('main.css' in index_response or - 'main.min.css' in index_response) + index_response = app.get("/") + assert "main.css" in index_response or "main.min.css" in index_response # set new style css env, config_response = _get_admin_config_page(app) - config_form = config_response.forms['admin-config-form'] - config_form['ckan.main_css'] = '/base/css/red.css' - webtest_submit(config_form, 'save', status=302, extra_environ=env) + config_form = config_response.forms["admin-config-form"] + config_form["ckan.main_css"] = "/base/css/red.css" + webtest_submit(config_form, "save", status=302, extra_environ=env) # new style - new_index_response = app.get('/') - assert_true('red.css' in new_index_response or - 'red.min.css' in new_index_response) - assert_true('main.css' not in new_index_response) - assert_true('main.min.css' not in new_index_response) + new_index_response = app.get("/") + assert ( + "red.css" in new_index_response + or "red.min.css" in new_index_response + ) + assert "main.css" not in new_index_response + assert "main.min.css" not in new_index_response # reset config value _reset_config(app) - reset_index_response = app.get('/') - assert_true('main.css' in reset_index_response or - 'main.min.css' in reset_index_response) - - def test_tag_line(self): - '''Add a tag line (only when no logo)''' - app = self._get_test_app() - + reset_index_response = app.get("/") + assert ( + "main.css" in reset_index_response + or "main.min.css" in reset_index_response + ) + + @pytest.mark.usefixtures("clean_db") + def test_tag_line(self, app): + """Add a tag line (only when no logo)""" # current tagline - index_response = app.get('/') - assert_true('Special Tagline' not in index_response) + index_response = app.get("/") + assert "Special Tagline" not in index_response # set new tagline css env, config_response = _get_admin_config_page(app) - config_form = config_response.forms['admin-config-form'] - config_form['ckan.site_description'] = 'Special Tagline' - webtest_submit(config_form, 'save', status=302, extra_environ=env) + config_form = config_response.forms["admin-config-form"] + config_form["ckan.site_description"] = "Special Tagline" + webtest_submit(config_form, "save", status=302, extra_environ=env) # new tagline not visible yet - new_index_response = app.get('/') - assert_true('Special Tagline' not in new_index_response) + new_index_response = app.get("/") + assert "Special Tagline" not in new_index_response # remove logo env, config_response = _get_admin_config_page(app) - config_form = config_response.forms['admin-config-form'] - config_form['ckan.site_logo'] = '' - webtest_submit(config_form, 'save', status=302, extra_environ=env) + config_form = config_response.forms["admin-config-form"] + config_form["ckan.site_logo"] = "" + webtest_submit(config_form, "save", status=302, extra_environ=env) # new tagline - new_index_response = app.get('/') - assert_true('Special Tagline' in new_index_response) + new_index_response = app.get("/") + assert "Special Tagline" in new_index_response # reset config value _reset_config(app) - reset_index_response = app.get('/') - assert_true('Special Tagline' not in reset_index_response) + reset_index_response = app.get("/") + assert "Special Tagline" not in reset_index_response - def test_about(self): - '''Add some About tag text''' - app = self._get_test_app() + @pytest.mark.usefixtures("clean_db") + def test_about(self, app): + """Add some About tag text""" # current about - about_response = app.get('/about') - assert_true('My special about text' not in about_response) + about_response = app.get("/about") + assert "My special about text" not in about_response # set new about env, config_response = _get_admin_config_page(app) - config_form = config_response.forms['admin-config-form'] - config_form['ckan.site_about'] = 'My special about text' - webtest_submit(config_form, 'save', status=302, extra_environ=env) + config_form = config_response.forms["admin-config-form"] + config_form["ckan.site_about"] = "My special about text" + webtest_submit(config_form, "save", status=302, extra_environ=env) # new about - new_about_response = app.get('/about') - assert_true('My special about text' in new_about_response) + new_about_response = app.get("/about") + assert "My special about text" in new_about_response # reset config value _reset_config(app) - reset_about_response = app.get('/about') - assert_true('My special about text' not in reset_about_response) + reset_about_response = app.get("/about") + assert "My special about text" not in reset_about_response - def test_intro(self): - '''Add some Intro tag text''' - app = self._get_test_app() + @pytest.mark.usefixtures("clean_db") + def test_intro(self, app): + """Add some Intro tag text""" # current intro - intro_response = app.get('/') - assert_true('My special intro text' not in intro_response) + intro_response = app.get("/") + assert "My special intro text" not in intro_response # set new intro env, config_response = _get_admin_config_page(app) - config_form = config_response.forms['admin-config-form'] - config_form['ckan.site_intro_text'] = 'My special intro text' - webtest_submit(config_form, 'save', status=302, extra_environ=env) + config_form = config_response.forms["admin-config-form"] + config_form["ckan.site_intro_text"] = "My special intro text" + webtest_submit(config_form, "save", status=302, extra_environ=env) # new intro - new_intro_response = app.get('/') - assert_true('My special intro text' in new_intro_response) + new_intro_response = app.get("/") + assert "My special intro text" in new_intro_response # reset config value _reset_config(app) - reset_intro_response = app.get('/') - assert_true('My special intro text' not in reset_intro_response) - - def test_custom_css(self): - '''Add some custom css to the head element''' - app = self._get_test_app() + reset_intro_response = app.get("/") + assert "My special intro text" not in reset_intro_response + @pytest.mark.usefixtures("clean_db") + def test_custom_css(self, app): + """Add some custom css to the head element""" # current tagline - intro_response_html = BeautifulSoup(app.get('/').body) - style_tag = intro_response_html.select('head style') - assert_equal(len(style_tag), 0) + intro_response_html = BeautifulSoup(app.get("/").body) + style_tag = intro_response_html.select("head style") + assert len(style_tag) == 0 # set new tagline css env, config_response = _get_admin_config_page(app) - config_form = config_response.forms['admin-config-form'] - config_form['ckan.site_custom_css'] = 'body {background-color:red}' - webtest_submit(config_form, 'save', status=302, extra_environ=env) + config_form = config_response.forms["admin-config-form"] + config_form["ckan.site_custom_css"] = "body {background-color:red}" + webtest_submit(config_form, "save", status=302, extra_environ=env) # new tagline not visible yet - new_intro_response_html = BeautifulSoup(app.get('/').body) - style_tag = new_intro_response_html.select('head style') - assert_equal(len(style_tag), 1) - assert_equal(style_tag[0].text.strip(), 'body {background-color:red}') + new_intro_response_html = BeautifulSoup(app.get("/").body) + style_tag = new_intro_response_html.select("head style") + assert len(style_tag) == 1 + assert style_tag[0].text.strip() == "body {background-color:red}" # reset config value _reset_config(app) - reset_intro_response_html = BeautifulSoup(app.get('/').body) - style_tag = reset_intro_response_html.select('head style') - assert_equal(len(style_tag), 0) - - @helpers.change_config('debug', True) - def test_homepage_style(self): - '''Select a homepage style''' - app = self._get_test_app() - + reset_intro_response_html = BeautifulSoup(app.get("/").body) + style_tag = reset_intro_response_html.select("head style") + assert len(style_tag) == 0 + + @pytest.mark.ckan_config("debug", True) + @pytest.mark.usefixtures("clean_db") + def test_homepage_style(self, app): + """Select a homepage style""" # current style - index_response = app.get('/') - assert_true('' - in index_response) + index_response = app.get("/") + assert "" in index_response # set new style css env, config_response = _get_admin_config_page(app) - config_form = config_response.forms['admin-config-form'] - config_form['ckan.homepage_style'] = '2' - webtest_submit(config_form, 'save', status=302, extra_environ=env) + config_form = config_response.forms["admin-config-form"] + config_form["ckan.homepage_style"] = "2" + webtest_submit(config_form, "save", status=302, extra_environ=env) # new style - new_index_response = app.get('/') - assert_true('' - not in new_index_response) - assert_true('' - in new_index_response) + new_index_response = app.get("/") + assert ( + "" + not in new_index_response + ) + assert "" in new_index_response # reset config value _reset_config(app) - reset_index_response = app.get('/') - assert_true('' - in reset_index_response) - + reset_index_response = app.get("/") + assert ( + "" in reset_index_response + ) -class TestTrashView(helpers.FunctionalTestBase): - '''View tests for permanently deleting datasets with Admin Trash.''' - @helpers.change_config('debug', True) - def test_trash_view_anon_user(self): - '''An anon user shouldn't be able to access trash view.''' - app = self._get_test_app() +class TestTrashView(object): + """View tests for permanently deleting datasets with Admin Trash.""" - trash_url = url_for(controller='admin', action='trash') + @pytest.mark.ckan_config("debug", True) + def test_trash_view_anon_user(self, app): + """An anon user shouldn't be able to access trash view.""" + trash_url = url_for(controller="admin", action="trash") trash_response = app.get(trash_url, status=403) - def test_trash_view_normal_user(self): - '''A normal logged in user shouldn't be able to access trash view.''' + @pytest.mark.usefixtures("clean_db") + def test_trash_view_normal_user(self, app): + """A normal logged in user shouldn't be able to access trash view.""" user = factories.User() - app = self._get_test_app() - env = {'REMOTE_USER': user['name'].encode('ascii')} - trash_url = url_for(controller='admin', action='trash') + env = {"REMOTE_USER": user["name"].encode("ascii")} + trash_url = url_for(controller="admin", action="trash") trash_response = app.get(trash_url, extra_environ=env, status=403) - assert_true('Need to be system administrator to administer' - in trash_response) + assert ( + "Need to be system administrator to administer" in trash_response + ) - def test_trash_view_sysadmin(self): - '''A sysadmin should be able to access trash view.''' + @pytest.mark.usefixtures("clean_db") + def test_trash_view_sysadmin(self, app): + """A sysadmin should be able to access trash view.""" user = factories.Sysadmin() - app = self._get_test_app() - env = {'REMOTE_USER': user['name'].encode('ascii')} - trash_url = url_for(controller='admin', action='trash') + env = {"REMOTE_USER": user["name"].encode("ascii")} + trash_url = url_for(controller="admin", action="trash") trash_response = app.get(trash_url, extra_environ=env, status=200) # On the purge page - assert_true('form-purge-packages' in trash_response) + assert "form-purge-packages" in trash_response - def test_trash_no_datasets(self): - '''Getting the trash view with no 'deleted' datasets should list no - datasets.''' + @pytest.mark.usefixtures("clean_db") + def test_trash_no_datasets(self, app): + """Getting the trash view with no 'deleted' datasets should list no + datasets.""" factories.Dataset() user = factories.Sysadmin() - app = self._get_test_app() - env = {'REMOTE_USER': user['name'].encode('ascii')} - trash_url = url_for(controller='admin', action='trash') + env = {"REMOTE_USER": user["name"].encode("ascii")} + trash_url = url_for(controller="admin", action="trash") trash_response = app.get(trash_url, extra_environ=env, status=200) trash_response_html = BeautifulSoup(trash_response.body) # it's called a 'user list' for some reason - trash_pkg_list = trash_response_html.select('ul.user-list li') + trash_pkg_list = trash_response_html.select("ul.user-list li") # no packages available to purge - assert_equal(len(trash_pkg_list), 0) + assert len(trash_pkg_list) == 0 - def test_trash_with_deleted_datasets(self): - '''Getting the trash view with 'deleted' datasets should list the - datasets.''' + @pytest.mark.usefixtures("clean_db") + def test_trash_with_deleted_datasets(self, app): + """Getting the trash view with 'deleted' datasets should list the + datasets.""" user = factories.Sysadmin() - factories.Dataset(state='deleted') - factories.Dataset(state='deleted') + factories.Dataset(state="deleted") + factories.Dataset(state="deleted") factories.Dataset() - app = self._get_test_app() - env = {'REMOTE_USER': user['name'].encode('ascii')} - trash_url = url_for(controller='admin', action='trash') + env = {"REMOTE_USER": user["name"].encode("ascii")} + trash_url = url_for(controller="admin", action="trash") trash_response = app.get(trash_url, extra_environ=env, status=200) trash_response_html = BeautifulSoup(trash_response.body) # it's called a 'user list' for some reason - trash_pkg_list = trash_response_html.select('ul.user-list li') + trash_pkg_list = trash_response_html.select("ul.user-list li") # Two packages in the list to purge - assert_equal(len(trash_pkg_list), 2) + assert len(trash_pkg_list) == 2 - def test_trash_purge_deleted_datasets(self): - '''Posting the trash view with 'deleted' datasets, purges the - datasets.''' + @pytest.mark.usefixtures("clean_db") + def test_trash_purge_deleted_datasets(self, app): + """Posting the trash view with 'deleted' datasets, purges the + datasets.""" user = factories.Sysadmin() - factories.Dataset(state='deleted') - factories.Dataset(state='deleted') + factories.Dataset(state="deleted") + factories.Dataset(state="deleted") factories.Dataset() - app = self._get_test_app() # how many datasets before purge pkgs_before_purge = model.Session.query(model.Package).count() - assert_equal(pkgs_before_purge, 3) + assert pkgs_before_purge == 3 - env = {'REMOTE_USER': user['name'].encode('ascii')} - trash_url = url_for(controller='admin', action='trash') + env = {"REMOTE_USER": user["name"].encode("ascii")} + trash_url = url_for(controller="admin", action="trash") trash_response = app.get(trash_url, extra_environ=env, status=200) # submit the purge form - purge_form = trash_response.forms['form-purge-packages'] - purge_response = webtest_submit(purge_form, 'purge-packages', - status=302, extra_environ=env) + purge_form = trash_response.forms["form-purge-packages"] + purge_response = webtest_submit( + purge_form, "purge-packages", status=302, extra_environ=env + ) purge_response = purge_response.follow(extra_environ=env) # redirected back to trash page - assert_true('Purge complete' in purge_response) + assert "Purge complete" in purge_response # how many datasets after purge pkgs_after_purge = model.Session.query(model.Package).count() - assert_equal(pkgs_after_purge, 1) - + assert pkgs_after_purge == 1 -class TestAdminConfigUpdate(helpers.FunctionalTestBase): - def teardown(self): - '''Reset the database and clear the search indexes.''' - helpers.reset_db() - - def _update_config_option(self): +class TestAdminConfigUpdate(object): + def _update_config_option(self, app): sysadmin = factories.Sysadmin() - env = {'REMOTE_USER': sysadmin['name'].encode('ascii')} - app = self._get_test_app() - url = url_for(controller='admin', action='config') + env = {"REMOTE_USER": sysadmin["name"].encode("ascii")} + + url = url_for(controller="admin", action="config") response = app.get(url=url, extra_environ=env) form = response.forms[1] - form['ckan.site_title'] = 'My Updated Site Title' + form["ckan.site_title"] = "My Updated Site Title" - webtest_submit(form, 'save', status=302, extra_environ=env) + webtest_submit(form, "save", status=302, extra_environ=env) - def test_admin_config_update(self): - '''Changing a config option using the admin interface appropriately + @pytest.mark.usefixtures("clean_db") + def test_admin_config_update(self, app): + """Changing a config option using the admin interface appropriately updates value returned by config_option_show, - system_info.get_system_info and in the title tag in templates.''' + system_info.get_system_info and in the title tag in templates.""" # test value before update # config_option_show returns default value - before_update = helpers.call_action('config_option_show', - key='ckan.site_title') - assert_equal(before_update, 'CKAN') + before_update = helpers.call_action( + "config_option_show", key="ckan.site_title" + ) + assert before_update == "CKAN" # system_info.get_system_info returns None, or default # test value before update - before_update = get_system_info('ckan.site_title') - assert_equal(before_update, None) + before_update = get_system_info("ckan.site_title") + assert before_update is None # test value before update with default - before_update_default = get_system_info('ckan.site_title', - config['ckan.site_title']) - assert_equal(before_update_default, 'CKAN') + before_update_default = get_system_info( + "ckan.site_title", config["ckan.site_title"] + ) + assert before_update_default == "CKAN" # title tag contains default value - app = self._get_test_app() - home_page_before = app.get('/', status=200) - assert_true('Welcome - CKAN' in home_page_before) + # app = make_app() + home_page_before = app.get("/", status=200) + assert "Welcome - CKAN" in home_page_before # update the option - self._update_config_option() + self._update_config_option(app) # test config_option_show returns new value after update - after_update = helpers.call_action('config_option_show', - key='ckan.site_title') - assert_equal(after_update, 'My Updated Site Title') + after_update = helpers.call_action( + "config_option_show", key="ckan.site_title" + ) + assert after_update == "My Updated Site Title" # system_info.get_system_info returns new value - after_update = get_system_info('ckan.site_title') - assert_equal(after_update, 'My Updated Site Title') + after_update = get_system_info("ckan.site_title") + assert after_update == "My Updated Site Title" # test value after update with default - after_update_default = get_system_info('ckan.site_title', - config['ckan.site_title']) - assert_equal(after_update_default, 'My Updated Site Title') + after_update_default = get_system_info( + "ckan.site_title", config["ckan.site_title"] + ) + assert after_update_default == "My Updated Site Title" # title tag contains new value - home_page_after = app.get('/', status=200) - assert_true('Welcome - My Updated Site Title' in home_page_after) + home_page_after = app.get("/", status=200) + assert "Welcome - My Updated Site Title" in home_page_after diff --git a/ckan/tests/controllers/test_api.py b/ckan/tests/controllers/test_api.py index af73e40a653..761f1dfc405 100644 --- a/ckan/tests/controllers/test_api.py +++ b/ckan/tests/controllers/test_api.py @@ -1,15 +1,16 @@ # encoding: utf-8 - -''' +""" NB Don't test logic functions here. This is just for the mechanics of the API controller itself. -''' +""" import json import re -import mock + import __builtin__ as builtins -from nose.tools import assert_equal, assert_in, eq_ +import mock +import pytest + from pyfakefs import fake_filesystem from ckan.lib.helpers import url_for @@ -30,279 +31,270 @@ def mock_open_if_open_fails(*args, **kwargs): return fake_open(*args, **kwargs) -class TestApiController(helpers.FunctionalTestBase): - - @helpers.change_config('ckan.storage_path', '/doesnt_exist') - @mock.patch.object(builtins, 'open', side_effect=mock_open_if_open_fails) - @mock.patch.object(ckan_uploader, 'os', fake_os) - @mock.patch.object(ckan_uploader, '_storage_path', new='/doesnt_exist') - def test_resource_create_upload_file(self, _): +class TestApiController(object): + @pytest.mark.usefixtures("clean_db") + @pytest.mark.ckan_config("ckan.storage_path", "/doesnt_exist") + @mock.patch.object(builtins, "open", side_effect=mock_open_if_open_fails) + @mock.patch.object(ckan_uploader, "os", fake_os) + @mock.patch.object(ckan_uploader, "_storage_path", new="/doesnt_exist") + def test_resource_create_upload_file(self, _, app): user = factories.User() - pkg = factories.Dataset(creator_user_id=user['id']) + pkg = factories.Dataset(creator_user_id=user["id"]) url = url_for( - controller='api', - action='action', - logic_function='resource_create', ver='/3') - env = {'REMOTE_USER': user['name'].encode('ascii')} - postparams = { - 'name': 'test-flask-upload', - 'package_id': pkg['id'] - } - upload_content = 'test-content' - upload_info = ('upload', 'test-upload.txt', upload_content) - app = self._get_test_app() + controller="api", + action="action", + logic_function="resource_create", + ver="/3", + ) + env = {"REMOTE_USER": user["name"].encode("ascii")} + postparams = {"name": "test-flask-upload", "package_id": pkg["id"]} + upload_content = "test-content" + upload_info = ("upload", "test-upload.txt", upload_content) + resp = app.post( - url, params=postparams, + url, + params=postparams, upload_files=[upload_info], extra_environ=env # content_type= 'application/json' ) - result = resp.json['result'] - eq_('upload', result['url_type']) - eq_(len(upload_content), result['size']) + result = resp.json["result"] + assert "upload" == result["url_type"] + assert len(upload_content) == result["size"] - def test_unicode_in_error_message_works_ok(self): + @pytest.mark.usefixtures("clean_db") + def test_unicode_in_error_message_works_ok(self, app): # Use tag_delete to echo back some unicode - app = self._get_test_app() - org_url = '/api/action/tag_delete' - data_dict = {'id': u'Delta symbol: \u0394'} # unicode gets rec'd ok - postparams = '%s=1' % json.dumps(data_dict) + + org_url = "/api/action/tag_delete" + data_dict = {"id": u"Delta symbol: \u0394"} # unicode gets rec'd ok + postparams = "%s=1" % json.dumps(data_dict) response = app.post(url=org_url, params=postparams, status=404) # The unicode is backslash encoded (because that is the default when # you do str(exception) ) - assert 'Delta symbol: \\u0394' in response.body + assert "Delta symbol: \\u0394" in response.body - def test_dataset_autocomplete_name(self): - dataset = factories.Dataset(name='rivers') - url = url_for(controller='api', action='dataset_autocomplete', ver='/2') - assert_equal(url, '/api/2/util/dataset/autocomplete') - app = self._get_test_app() - - response = app.get( - url=url, - params={ - 'incomplete': u'rive', - }, - status=200, + @pytest.mark.usefixtures("clean_db", "clean_index") + def test_dataset_autocomplete_name(self, app): + dataset = factories.Dataset(name="rivers") + url = url_for( + controller="api", action="dataset_autocomplete", ver="/2" ) + assert url == "/api/2/util/dataset/autocomplete" + + response = app.get(url=url, params={"incomplete": u"rive"}, status=200) results = json.loads(response.body) - assert_equal(results, {"ResultSet": {"Result": [{ - 'match_field': 'name', - "name": "rivers", - 'match_displayed': 'rivers', - 'title': dataset['title'], - }]}}) - assert_equal(response.headers['Content-Type'], - 'application/json;charset=utf-8') - - def test_dataset_autocomplete_title(self): - dataset = factories.Dataset(name='test_ri', title='Rivers') - url = url_for(controller='api', action='dataset_autocomplete', ver='/2') - assert_equal(url, '/api/2/util/dataset/autocomplete') - app = self._get_test_app() - - response = app.get( - url=url, - params={ - 'incomplete': u'riv', - }, - status=200, + assert results == { + u"ResultSet": { + u"Result": [ + { + u"match_field": u"name", + u"name": u"rivers", + u"match_displayed": u"rivers", + u"title": dataset["title"], + } + ] + } + } + assert ( + response.headers["Content-Type"] + == "application/json;charset=utf-8" ) - results = json.loads(response.body) - assert_equal(results, {"ResultSet": {"Result": [{ - 'match_field': 'title', - "name": dataset['name'], - 'match_displayed': 'Rivers (test_ri)', - 'title': 'Rivers', - }]}}) - assert_equal(response.headers['Content-Type'], - 'application/json;charset=utf-8') - - def test_tag_autocomplete(self): - factories.Dataset(tags=[{'name': 'rivers'}]) - url = url_for(controller='api', action='tag_autocomplete', ver='/2') - assert_equal(url, '/api/2/util/tag/autocomplete') - app = self._get_test_app() - - response = app.get( - url=url, - params={ - 'incomplete': u'rive', - }, - status=200, + @pytest.mark.usefixtures("clean_db", "clean_index") + def test_dataset_autocomplete_title(self, app): + dataset = factories.Dataset(name="test_ri", title="Rivers") + url = url_for( + controller="api", action="dataset_autocomplete", ver="/2" ) + assert url == "/api/2/util/dataset/autocomplete" + + response = app.get(url=url, params={"incomplete": u"riv"}, status=200) results = json.loads(response.body) - assert_equal(results, {"ResultSet": {"Result": [{"Name": "rivers"}]}}) - assert_equal(response.headers['Content-Type'], - 'application/json;charset=utf-8') + assert results == { + u"ResultSet": { + u"Result": [ + { + u"match_field": u"title", + u"name": dataset["name"], + u"match_displayed": u"Rivers (test_ri)", + u"title": u"Rivers", + } + ] + } + } + assert ( + response.headers["Content-Type"] + == "application/json;charset=utf-8" + ) - def test_group_autocomplete_by_name(self): - org = factories.Group(name='rivers', title='Bridges') - url = url_for(controller='api', action='group_autocomplete', ver='/2') - assert_equal(url, '/api/2/util/group/autocomplete') - app = self._get_test_app() + @pytest.mark.usefixtures("clean_db") + def test_tag_autocomplete(self, app): + factories.Dataset(tags=[{"name": "rivers"}]) + url = url_for(controller="api", action="tag_autocomplete", ver="/2") + assert url == "/api/2/util/tag/autocomplete" - response = app.get( - url=url, - params={ - 'q': u'rive', - }, - status=200, - ) + response = app.get(url=url, params={"incomplete": u"rive"}, status=200) results = json.loads(response.body) - assert_equal(len(results), 1) - assert_equal(results[0]['name'], 'rivers') - assert_equal(results[0]['title'], 'Bridges') - assert_equal(response.headers['Content-Type'], - 'application/json;charset=utf-8') - - def test_group_autocomplete_by_title(self): - org = factories.Group(name='frogs', title='Bugs') - url = url_for(controller='api', action='group_autocomplete', ver='/2') - app = self._get_test_app() - - response = app.get( - url=url, - params={ - 'q': u'bug', - }, - status=200, + assert results == {"ResultSet": {"Result": [{"Name": "rivers"}]}} + assert ( + response.headers["Content-Type"] + == "application/json;charset=utf-8" ) + @pytest.mark.usefixtures("clean_db") + def test_group_autocomplete_by_name(self, app): + org = factories.Group(name="rivers", title="Bridges") + url = url_for(controller="api", action="group_autocomplete", ver="/2") + assert url == "/api/2/util/group/autocomplete" + + response = app.get(url=url, params={"q": u"rive"}, status=200) + results = json.loads(response.body) - assert_equal(len(results), 1) - assert_equal(results[0]['name'], 'frogs') + assert len(results) == 1 + assert results[0]["name"] == "rivers" + assert results[0]["title"] == "Bridges" + assert ( + response.headers["Content-Type"] + == "application/json;charset=utf-8" + ) - def test_organization_autocomplete_by_name(self): - org = factories.Organization(name='simple-dummy-org') - url = url_for(controller='api', action='organization_autocomplete', ver='/2') - assert_equal(url, '/api/2/util/organization/autocomplete') - app = self._get_test_app() + @pytest.mark.usefixtures("clean_db") + def test_group_autocomplete_by_title(self, app): + org = factories.Group(name="frogs", title="Bugs") + url = url_for(controller="api", action="group_autocomplete", ver="/2") - response = app.get( - url=url, - params={ - 'q': u'simple', - }, - status=200, + response = app.get(url=url, params={"q": u"bug"}, status=200) + + results = json.loads(response.body) + assert len(results) == 1 + assert results[0]["name"] == "frogs" + + @pytest.mark.usefixtures("clean_db") + def test_organization_autocomplete_by_name(self, app): + org = factories.Organization(name="simple-dummy-org") + url = url_for( + controller="api", action="organization_autocomplete", ver="/2" ) + assert url == "/api/2/util/organization/autocomplete" + + response = app.get(url=url, params={"q": u"simple"}, status=200) results = json.loads(response.body) - assert_equal(len(results), 1) - assert_equal(results[0]['name'], 'simple-dummy-org') - assert_equal(results[0]['title'], org['title']) - assert_equal(response.headers['Content-Type'], - 'application/json;charset=utf-8') - - def test_organization_autocomplete_by_title(self): - org = factories.Organization(title='Simple dummy org') - url = url_for(controller='api', action='organization_autocomplete', ver='/2') - app = self._get_test_app() - - response = app.get( - url=url, - params={ - 'q': u'simple dum', - }, - status=200, + assert len(results) == 1 + assert results[0]["name"] == "simple-dummy-org" + assert results[0]["title"] == org["title"] + assert ( + response.headers["Content-Type"] + == "application/json;charset=utf-8" + ) + + @pytest.mark.usefixtures("clean_db") + def test_organization_autocomplete_by_title(self, app): + org = factories.Organization(title="Simple dummy org") + url = url_for( + controller="api", action="organization_autocomplete", ver="/2" ) + response = app.get(url=url, params={"q": u"simple dum"}, status=200) + results = json.loads(response.body) - assert_equal(len(results), 1) - assert_equal(results[0]['title'], 'Simple dummy org') + assert len(results) == 1 + assert results[0]["title"] == "Simple dummy org" - def test_config_option_list_access_sysadmin(self): + @pytest.mark.usefixtures("clean_db") + def test_config_option_list_access_sysadmin(self, app): user = factories.Sysadmin() url = url_for( - controller='api', - action='action', - logic_function='config_option_list', - ver='/3') - app = self._get_test_app() + controller="api", + action="action", + logic_function="config_option_list", + ver="/3", + ) app.get( url=url, params={}, - extra_environ={'REMOTE_USER': user['name'].encode('ascii')}, + extra_environ={"REMOTE_USER": user["name"].encode("ascii")}, status=200, ) - def test_config_option_list_access_sysadmin_jsonp(self): + @pytest.mark.usefixtures("clean_db") + def test_config_option_list_access_sysadmin_jsonp(self, app): user = factories.Sysadmin() url = url_for( - controller='api', - action='action', - logic_function='config_option_list', - ver='/3') - app = self._get_test_app() + controller="api", + action="action", + logic_function="config_option_list", + ver="/3", + ) app.get( url=url, - params={'callback': 'myfn'}, - extra_environ={'REMOTE_USER': user['name'].encode('ascii')}, + params={"callback": "myfn"}, + extra_environ={"REMOTE_USER": user["name"].encode("ascii")}, status=403, ) - def test_jsonp_works_on_get_requests(self): + @pytest.mark.usefixtures("clean_db") + def test_jsonp_works_on_get_requests(self, app): dataset1 = factories.Dataset() dataset2 = factories.Dataset() url = url_for( - controller='api', - action='action', - logic_function='package_list', - ver='/3') - app = self._get_test_app() - res = app.get( - url=url, - params={'callback': 'my_callback'}, + controller="api", + action="action", + logic_function="package_list", + ver="/3", ) - assert re.match(r'my_callback\(.*\);', res.body), res + + res = app.get(url=url, params={"callback": "my_callback"}) + assert re.match(r"my_callback\(.*\);", res.body), res # Unwrap JSONP callback (we want to look at the data). - msg = res.body[len('my_callback') + 1:-2] + start = len("my_callback") + 1 + msg = res.body[start: -2] res_dict = json.loads(msg) - eq_(res_dict['success'], True) - eq_(sorted(res_dict['result']), - sorted([dataset1['name'], dataset2['name']])) + assert res_dict["success"] + assert sorted(res_dict["result"]) == sorted( + [dataset1["name"], dataset2["name"]] + ) - def test_jsonp_returns_javascript_content_type(self): + @pytest.mark.usefixtures("clean_db") + def test_jsonp_returns_javascript_content_type(self, app): url = url_for( - controller='api', - action='action', - logic_function='status_show', - ver='/3') - app = self._get_test_app() - res = app.get( - url=url, - params={'callback': 'my_callback'}, + controller="api", + action="action", + logic_function="status_show", + ver="/3", ) - assert_in('application/javascript', res.headers.get('Content-Type')) - def test_jsonp_does_not_work_on_post_requests(self): + res = app.get(url=url, params={"callback": "my_callback"}) + assert "application/javascript" in res.headers.get("Content-Type") + + @pytest.mark.usefixtures("clean_db") + def test_jsonp_does_not_work_on_post_requests(self, app): dataset1 = factories.Dataset() dataset2 = factories.Dataset() url = url_for( - controller='api', - action='action', - logic_function='package_list', - ver='/3', - callback='my_callback', - ) - app = self._get_test_app() - res = app.post( - url=url, + controller="api", + action="action", + logic_function="package_list", + ver="/3", + callback="my_callback", ) + + res = app.post(url=url) # The callback param is ignored and the normal response is returned - assert not res.body.startswith('my_callback') + assert not res.body.startswith("my_callback") res_dict = json.loads(res.body) - eq_(res_dict['success'], True) - eq_(sorted(res_dict['result']), - sorted([dataset1['name'], dataset2['name']])) + assert res_dict["success"] + assert sorted(res_dict["result"]) == sorted( + [dataset1["name"], dataset2["name"]] + ) diff --git a/ckan/tests/controllers/test_feed.py b/ckan/tests/controllers/test_feed.py index f34fa273ec2..13161fc1031 100644 --- a/ckan/tests/controllers/test_feed.py +++ b/ckan/tests/controllers/test_feed.py @@ -1,5 +1,7 @@ # encoding: utf-8 +import pytest + from ckan.lib.helpers import url_for import ckan.tests.helpers as helpers @@ -8,134 +10,94 @@ from webhelpers.feedgenerator import GeoAtom1Feed -class TestFeedNew(helpers.FunctionalTestBase): - @classmethod - def teardown_class(cls): - helpers.reset_db() - - def test_atom_feed_page_zero_gives_error(self): - group = factories.Group() - offset = url_for(u'feeds.group', id=group['name']) + '?page=0' - app = self._get_test_app() - offset = url_for(u'feeds.group', id=group['name']) + u'?page=0' - - res = app.get(offset, status=400) - assert '"page" parameter must be a positive integer' in res, res - - def test_atom_feed_page_negative_gives_error(self): - group = factories.Group() - offset = url_for(u'feeds.group', id=group['name']) + '?page=-2' - app = self._get_test_app() - offset = url_for(u'feeds.group', id=group['name']) + '?page=-2' - res = app.get(offset, status=400) - assert '"page" parameter must be a positive integer' in res, res - - def test_atom_feed_page_not_int_gives_error(self): - group = factories.Group() - offset = url_for(u'feeds.group', id=group['name']) + '?page=abc' - app = self._get_test_app() - offset = url_for(u'feeds.group', id=group['name']) + '?page=abc' - res = app.get(offset, status=400) - assert '"page" parameter must be a positive integer' in res, res - - def test_general_atom_feed_works(self): - dataset = factories.Dataset() - offset = url_for(u'feeds.general') - app = self._get_test_app() - offset = url_for(u'feeds.general') - res = app.get(offset) - - assert u'{0}'.format( - dataset['title']) in res.body - - def test_group_atom_feed_works(self): - group = factories.Group() - dataset = factories.Dataset(groups=[{'id': group['id']}]) - offset = url_for(u'feeds.group', id=group['name']) - app = self._get_test_app() - offset = url_for(u'feeds.group', id=group['name']) - res = app.get(offset) - - assert u'{0}'.format( - dataset['title']) in res.body - - def test_organization_atom_feed_works(self): - group = factories.Organization() - dataset = factories.Dataset(owner_org=group['id']) - offset = url_for(u'feeds.organization', id=group['name']) - app = self._get_test_app() - offset = url_for(u'feeds.organization', id=group['name']) - res = app.get(offset) - - assert u'{0}'.format( - dataset['title']) in res.body - - def test_custom_atom_feed_works(self): - dataset1 = factories.Dataset( - title=u'Test weekly', - extras=[{ - 'key': 'frequency', - 'value': 'weekly' - }]) - dataset2 = factories.Dataset( - title=u'Test daily', - extras=[{ - 'key': 'frequency', - 'value': 'daily' - }]) - - offset = url_for(u'feeds.custom') - params = {'q': 'frequency:weekly'} - app = self._get_test_app() - res = app.get(offset, params=params) - - assert u'{0}'.format( - dataset1['title']) in res.body - - assert u'{0}'.format( - dataset2['title']) not in res.body - - -class TestFeedInterface(helpers.FunctionalTestBase): - @classmethod - def setup_class(cls): - super(TestFeedInterface, cls).setup_class() - - if not plugins.plugin_loaded('test_feed_plugin'): - plugins.load('test_feed_plugin') - - @classmethod - def teardown_class(cls): - helpers.reset_db() - plugins.unload('test_feed_plugin') - - def test_custom_class_used(self): - - app = self._get_test_app() - offset = url_for(u'feeds.general') - app = self._get_test_app() - res = app.get(offset) - - assert 'xmlns:georss="http://www.georss.org/georss"' in res.body, res.body - - def test_additional_fields_added(self): - metadata = { - 'ymin': '-2373790', - 'xmin': '2937940', - 'ymax': '-1681290', - 'xmax': '3567770', - } - - extras = [{'key': k, 'value': v} for (k, v) in metadata.items()] - - factories.Dataset(extras=extras) - - app = self._get_test_app() - offset = url_for(u'feeds.general') - app = self._get_test_app() - res = app.get(offset) - - assert '-2373790.000000 2937940.000000 -1681290.000000 3567770.000000' in res.body, res.body +@pytest.mark.usefixtures("clean_db") +@pytest.mark.parametrize("page", [0, -2, "abc"]) +def test_atom_feed_incorrect_page_gives_error(page, app): + group = factories.Group() + offset = url_for(u"feeds.group", id=group["name"]) + u"?page={}".format( + page + ) + res = app.get(offset, status=400) + assert ""page" parameter must be a positive integer" in res, res + + +@pytest.mark.usefixtures("clean_db") +def test_general_atom_feed_works(app): + dataset = factories.Dataset() + offset = url_for(u"feeds.general") + res = app.get(offset) + + assert u"{0}".format(dataset["title"]) in res.body + + +@pytest.mark.usefixtures("clean_db") +def test_group_atom_feed_works(app): + group = factories.Group() + dataset = factories.Dataset(groups=[{"id": group["id"]}]) + offset = url_for(u"feeds.group", id=group["name"]) + res = app.get(offset) + + assert u"{0}".format(dataset["title"]) in res.body + + +@pytest.mark.usefixtures("clean_db") +def test_organization_atom_feed_works(app): + group = factories.Organization() + dataset = factories.Dataset(owner_org=group["id"]) + offset = url_for(u"feeds.organization", id=group["name"]) + res = app.get(offset) + + assert u"{0}".format(dataset["title"]) in res.body + + +@pytest.mark.usefixtures("clean_db") +def test_custom_atom_feed_works(app): + dataset1 = factories.Dataset( + title=u"Test weekly", extras=[{"key": "frequency", "value": "weekly"}] + ) + dataset2 = factories.Dataset( + title=u"Test daily", extras=[{"key": "frequency", "value": "daily"}] + ) + + offset = url_for(u"feeds.custom") + params = {"q": "frequency:weekly"} + + res = app.get(offset, params=params) + + assert u"{0}".format(dataset1["title"]) in res.body + + assert u'{0}'.format(dataset2["title"]) not in res.body + + +@pytest.mark.ckan_config("ckan.plugins", "test_feed_plugin") +@pytest.mark.usefixtures("clean_db", "clean_index", "with_plugins") +def test_custom_class_used(app): + offset = url_for(u"feeds.general") + res = app.get(offset) + + assert 'xmlns:georss="http://www.georss.org/georss"' in res.body, res.body + + +@pytest.mark.ckan_config("ckan.plugins", "test_feed_plugin") +@pytest.mark.usefixtures("clean_db", "clean_index", "with_plugins") +def test_additional_fields_added(app): + metadata = { + "ymin": "-2373790", + "xmin": "2937940", + "ymax": "-1681290", + "xmax": "3567770", + } + + extras = [{"key": k, "value": v} for (k, v) in metadata.items()] + + factories.Dataset(extras=extras) + offset = url_for(u"feeds.general") + res = app.get(offset) + + assert ( + "-2373790.000000 2937940.000000 -1681290.000000 3567770.000000" + in res.body + ), res.body class MockFeedPlugin(plugins.SingletonPlugin): @@ -145,8 +107,9 @@ def get_feed_class(self): return GeoAtom1Feed def get_item_additional_fields(self, dataset_dict): - extras = {e['key']: e['value'] for e in dataset_dict['extras']} + extras = {e["key"]: e["value"] for e in dataset_dict["extras"]} box = tuple( - float(extras.get(n)) for n in ('ymin', 'xmin', 'ymax', 'xmax')) - return {'geometry': box} + float(extras.get(n)) for n in ("ymin", "xmin", "ymax", "xmax") + ) + return {"geometry": box} diff --git a/ckan/tests/controllers/test_group.py b/ckan/tests/controllers/test_group.py index c71a896d6bb..dbec64577ef 100644 --- a/ckan/tests/controllers/test_group.py +++ b/ckan/tests/controllers/test_group.py @@ -1,10 +1,7 @@ # encoding: utf-8 from bs4 import BeautifulSoup -from nose.tools import assert_equal, assert_true, assert_in - -from six.moves import xrange - +import pytest from ckan.lib.helpers import url_for import ckan.tests.helpers as helpers @@ -15,812 +12,824 @@ submit_and_follow = helpers.submit_and_follow -class TestGroupController(helpers.FunctionalTestBase): - - def setup(self): - model.repo.rebuild_db() - - def test_bulk_process_throws_404_for_nonexistent_org(self): - app = self._get_test_app() - bulk_process_url = url_for('organization.bulk_process', id='does-not-exist') +class TestGroupController(object): + @pytest.mark.usefixtures("clean_db") + def test_bulk_process_throws_404_for_nonexistent_org(self, app): + bulk_process_url = url_for( + "organization.bulk_process", id="does-not-exist" + ) app.get(url=bulk_process_url, status=404) - def test_page_thru_list_of_orgs_preserves_sort_order(self): + @pytest.mark.usefixtures("clean_db") + def test_page_thru_list_of_orgs_preserves_sort_order(self, app): orgs = [factories.Organization() for _ in range(35)] - app = self._get_test_app() - org_url = url_for('organization.index', sort='name desc') + org_url = url_for("organization.index", sort="name desc") response = app.get(url=org_url) - assert orgs[-1]['name'] in response - assert orgs[0]['name'] not in response + assert orgs[-1]["name"] in response + assert orgs[0]["name"] not in response - response2 = response.click('2') - assert orgs[-1]['name'] not in response2 - assert orgs[0]['name'] in response2 + response2 = response.click("2") + assert orgs[-1]["name"] not in response2 + assert orgs[0]["name"] in response2 - def test_page_thru_list_of_groups_preserves_sort_order(self): + @pytest.mark.usefixtures("clean_db") + def test_page_thru_list_of_groups_preserves_sort_order(self, app): groups = [factories.Group() for _ in range(35)] - app = self._get_test_app() - group_url = url_for('group.index', sort='title desc') + group_url = url_for("group.index", sort="title desc") response = app.get(url=group_url) - assert groups[-1]['title'] in response - assert groups[0]['title'] not in response + assert groups[-1]["title"] in response + assert groups[0]["title"] not in response - response2 = response.click(r'^2$') - assert groups[-1]['title'] not in response2 - assert groups[0]['title'] in response2 + response2 = response.click(r"^2$") + assert groups[-1]["title"] not in response2 + assert groups[0]["title"] in response2 - def test_invalid_sort_param_does_not_crash(self): - app = self._get_test_app() + @pytest.mark.usefixtures("clean_db") + def test_invalid_sort_param_does_not_crash(self, app): with app.flask_app.test_request_context(): - group_url = url_for('group.index', - sort='title desc nope') + group_url = url_for("group.index", sort="title desc nope") app.get(url=group_url) - group_url = url_for('group.index', - sort='title nope desc nope') + group_url = url_for("group.index", sort="title nope desc nope") app.get(url=group_url) def _get_group_new_page(app): user = factories.User() - env = {'REMOTE_USER': user['name'].encode('ascii')} - response = app.get( - url=url_for('group.new'), - extra_environ=env, - ) + env = {"REMOTE_USER": user["name"].encode("ascii")} + response = app.get(url=url_for("group.new"), extra_environ=env) return env, response -class TestGroupControllerNew(helpers.FunctionalTestBase): - def test_not_logged_in(self): - app = self._get_test_app() - app.get(url=url_for('group.new'), - status=403) +class TestGroupControllerNew(object): + def test_not_logged_in(self, app): + app.get(url=url_for("group.new"), status=403) - def test_form_renders(self): - app = self._get_test_app() + def test_form_renders(self, app): env, response = _get_group_new_page(app) - assert_in('group-edit', response.forms) + assert "group-edit" in response.forms - def test_name_required(self): - app = self._get_test_app() + @pytest.mark.usefixtures("clean_db") + def test_name_required(self, app): env, response = _get_group_new_page(app) - form = response.forms['group-edit'] + form = response.forms["group-edit"] - response = webtest_submit(form, 'save', status=200, extra_environ=env) - assert_true('group-edit' in response.forms) - assert_true('Name: Missing value' in response) + response = webtest_submit(form, "save", status=200, extra_environ=env) + assert "group-edit" in response.forms + assert "Name: Missing value" in response - def test_saved(self): - app = self._get_test_app() + @pytest.mark.usefixtures("clean_db") + def test_saved(self, app): env, response = _get_group_new_page(app) - form = response.forms['group-edit'] - form['name'] = u'saved' + form = response.forms["group-edit"] + form["name"] = u"saved" - response = submit_and_follow(app, form, env, 'save') - group = model.Group.by_name(u'saved') - assert_equal(group.title, u'') - assert_equal(group.type, 'group') - assert_equal(group.state, 'active') + response = submit_and_follow(app, form, env, "save") + group = model.Group.by_name(u"saved") + assert group.title == u"" + assert group.type == "group" + assert group.state == "active" - def test_all_fields_saved(self): - app = self._get_test_app() + @pytest.mark.usefixtures("clean_db") + def test_all_fields_saved(self, app): env, response = _get_group_new_page(app) - form = response.forms['group-edit'] - form['name'] = u'all-fields-saved' - form['title'] = 'Science' - form['description'] = 'Sciencey datasets' - form['image_url'] = 'http://example.com/image.png' + form = response.forms["group-edit"] + form["name"] = u"all-fields-saved" + form["title"] = "Science" + form["description"] = "Sciencey datasets" + form["image_url"] = "http://example.com/image.png" - response = submit_and_follow(app, form, env, 'save') - group = model.Group.by_name(u'all-fields-saved') - assert_equal(group.title, u'Science') - assert_equal(group.description, 'Sciencey datasets') + response = submit_and_follow(app, form, env, "save") + group = model.Group.by_name(u"all-fields-saved") + assert group.title == u"Science" + assert group.description == "Sciencey datasets" def _get_group_edit_page(app, group_name=None): user = factories.User() if group_name is None: group = factories.Group(user=user) - group_name = group['name'] - env = {'REMOTE_USER': user['name'].encode('ascii')} - url = url_for('group.edit', id=group_name) + group_name = group["name"] + env = {"REMOTE_USER": user["name"].encode("ascii")} + url = url_for("group.edit", id=group_name) response = app.get(url=url, extra_environ=env) return env, response, group_name -class TestGroupControllerEdit(helpers.FunctionalTestBase): - def test_not_logged_in(self): - app = self._get_test_app() - app.get(url=url_for('group.new'), - status=403) +class TestGroupControllerEdit(object): + def test_not_logged_in(self, app): + app.get(url=url_for("group.new"), status=403) - def test_group_doesnt_exist(self): - app = self._get_test_app() + def test_group_doesnt_exist(self, app): user = factories.User() - env = {'REMOTE_USER': user['name'].encode('ascii')} - url = url_for('group.edit', - id='doesnt_exist') - app.get(url=url, extra_environ=env, - status=404) - - def test_form_renders(self): - app = self._get_test_app() + env = {"REMOTE_USER": user["name"].encode("ascii")} + url = url_for("group.edit", id="doesnt_exist") + app.get(url=url, extra_environ=env, status=404) + + def test_form_renders(self, app): env, response, group_name = _get_group_edit_page(app) - assert_in('group-edit', response.forms) + assert "group-edit" in response.forms - def test_saved(self): - app = self._get_test_app() + @pytest.mark.usefixtures("clean_db") + def test_saved(self, app): env, response, group_name = _get_group_edit_page(app) - form = response.forms['group-edit'] + form = response.forms["group-edit"] - response = submit_and_follow(app, form, env, 'save') + response = submit_and_follow(app, form, env, "save") group = model.Group.by_name(group_name) - assert_equal(group.state, 'active') + assert group.state == "active" - def test_all_fields_saved(self): - app = self._get_test_app() + @pytest.mark.usefixtures("clean_db") + def test_all_fields_saved(self, app): env, response, group_name = _get_group_edit_page(app) - form = response.forms['group-edit'] - form['name'] = u'all-fields-edited' - form['title'] = 'Science' - form['description'] = 'Sciencey datasets' - form['image_url'] = 'http://example.com/image.png' - - response = submit_and_follow(app, form, env, 'save') - group = model.Group.by_name(u'all-fields-edited') - assert_equal(group.title, u'Science') - assert_equal(group.description, 'Sciencey datasets') - assert_equal(group.image_url, 'http://example.com/image.png') - - -class TestGroupRead(helpers.FunctionalTestBase): - - def test_group_read(self): + form = response.forms["group-edit"] + form["name"] = u"all-fields-edited" + form["title"] = "Science" + form["description"] = "Sciencey datasets" + form["image_url"] = "http://example.com/image.png" + + response = submit_and_follow(app, form, env, "save") + group = model.Group.by_name(u"all-fields-edited") + assert group.title == u"Science" + assert group.description == "Sciencey datasets" + assert group.image_url == "http://example.com/image.png" + + +class TestGroupRead(object): + @pytest.mark.usefixtures("clean_db") + def test_group_read(self, app): group = factories.Group() - app = helpers._get_test_app() - response = app.get(url=url_for('group.read', id=group['name'])) - assert_in(group['title'], response) - assert_in(group['description'], response) + response = app.get(url=url_for("group.read", id=group["name"])) + assert group["title"] in response + assert group["description"] in response - def test_redirect_when_given_id(self): + @pytest.mark.usefixtures("clean_db") + def test_redirect_when_given_id(self, app): group = factories.Group() - app = helpers._get_test_app() - response = app.get(url_for('group.read', id=group['id']), status=302) + + response = app.get(url_for("group.read", id=group["id"]), status=302) # redirect replaces the ID with the name in the URL redirected_response = response.follow() - expected_url = url_for('group.read', id=group['name']) - assert_equal(redirected_response.request.path, expected_url) + expected_url = url_for("group.read", id=group["name"]) + assert redirected_response.request.path == expected_url + + @pytest.mark.usefixtures("clean_db") + def test_no_redirect_loop_when_name_is_the_same_as_the_id(self, app): + group = factories.Group(id="abc", name="abc") - def test_no_redirect_loop_when_name_is_the_same_as_the_id(self): - group = factories.Group(id='abc', name='abc') - app = helpers._get_test_app() # 200 == no redirect - app.get(url_for('group.read', id=group['id']), status=200) - - -class TestGroupDelete(helpers.FunctionalTestBase): - def setup(self): - super(TestGroupDelete, self).setup() - self.app = helpers._get_test_app() - self.user = factories.User() - self.user_env = {'REMOTE_USER': self.user['name'].encode('ascii')} - self.group = factories.Group(user=self.user) - - def test_owner_delete(self): - response = self.app.get(url=url_for('group.delete', - id=self.group['id']), - status=200, - extra_environ=self.user_env) - - form = response.forms['group-confirm-delete-form'] - response = submit_and_follow(self.app, form, name='delete', - extra_environ=self.user_env) - group = helpers.call_action('group_show', - id=self.group['id']) - assert_equal(group['state'], 'deleted') - - def test_sysadmin_delete(self): - sysadmin = factories.Sysadmin() - extra_environ = {'REMOTE_USER': sysadmin['name'].encode('ascii')} - response = self.app.get(url=url_for('group.delete', - id=self.group['id']), - status=200, - extra_environ=extra_environ) - - form = response.forms['group-confirm-delete-form'] - response = submit_and_follow(self.app, form, name='delete', - extra_environ=self.user_env) - group = helpers.call_action('group_show', - id=self.group['id']) - assert_equal(group['state'], 'deleted') - - def test_non_authorized_user_trying_to_delete_fails(self): + app.get(url_for("group.read", id=group["id"]), status=200) + + +class TestGroupDelete(object): + @pytest.fixture + def initial_data(self): user = factories.User() - extra_environ = {'REMOTE_USER': user['name'].encode('ascii')} - self.app.get(url=url_for('group.delete', - id=self.group['id']), - status=403, - extra_environ=extra_environ) + return { + "user": user, + "user_env": {"REMOTE_USER": user["name"].encode("ascii")}, + "group": factories.Group(user=user), + } + + def test_owner_delete(self, app, initial_data): + response = app.get( + url=url_for("group.delete", id=initial_data["group"]["id"]), + status=200, + extra_environ=initial_data["user_env"], + ) - group = helpers.call_action('group_show', - id=self.group['id']) - assert_equal(group['state'], 'active') + form = response.forms["group-confirm-delete-form"] + response = submit_and_follow( + app, form, name="delete", extra_environ=initial_data["user_env"] + ) + group = helpers.call_action( + "group_show", id=initial_data["group"]["id"] + ) + assert group["state"] == "deleted" + + def test_sysadmin_delete(self, app, initial_data): + sysadmin = factories.Sysadmin() + extra_environ = {"REMOTE_USER": sysadmin["name"].encode("ascii")} + response = app.get( + url=url_for("group.delete", id=initial_data["group"]["id"]), + status=200, + extra_environ=extra_environ, + ) - def test_anon_user_trying_to_delete_fails(self): - self.app.get(url=url_for('group.delete', - id=self.group['id']), - status=403) + form = response.forms["group-confirm-delete-form"] + response = submit_and_follow( + app, form, name="delete", extra_environ=initial_data["user_env"] + ) + group = helpers.call_action( + "group_show", id=initial_data["group"]["id"] + ) + assert group["state"] == "deleted" - group = helpers.call_action('group_show', - id=self.group['id']) - assert_equal(group['state'], 'active') + def test_non_authorized_user_trying_to_delete_fails( + self, app, initial_data + ): + user = factories.User() + extra_environ = {"REMOTE_USER": user["name"].encode("ascii")} + app.get( + url=url_for("group.delete", id=initial_data["group"]["id"]), + status=403, + extra_environ=extra_environ, + ) + group = helpers.call_action( + "group_show", id=initial_data["group"]["id"] + ) + assert group["state"] == "active" -class TestGroupMembership(helpers.FunctionalTestBase): + def test_anon_user_trying_to_delete_fails(self, app, initial_data): + app.get( + url=url_for("group.delete", id=initial_data["group"]["id"]), + status=403, + ) + group = helpers.call_action( + "group_show", id=initial_data["group"]["id"] + ) + assert group["state"] == "active" + + +class TestGroupMembership(object): def _create_group(self, owner_username, users=None): - '''Create a group with the owner defined by owner_username and - optionally with a list of other users.''' + """Create a group with the owner defined by owner_username and + optionally with a list of other users.""" if users is None: users = [] - context = {'user': owner_username, 'ignore_auth': True, } - group = helpers.call_action('group_create', context=context, - name='test-group', users=users) + context = {"user": owner_username, "ignore_auth": True} + group = helpers.call_action( + "group_create", context=context, name="test-group", users=users + ) return group def _get_group_add_member_page(self, app, user, group_name): - env = {'REMOTE_USER': user['name'].encode('ascii')} - url = url_for('group.member_new', - id=group_name) + env = {"REMOTE_USER": user["name"].encode("ascii")} + url = url_for("group.member_new", id=group_name) response = app.get(url=url, extra_environ=env) return env, response - def test_membership_list(self): - '''List group admins and members''' - app = self._get_test_app() - user_one = factories.User(fullname='User One', name='user-one') - user_two = factories.User(fullname='User Two') + @pytest.mark.usefixtures("clean_db") + def test_membership_list(self, app): + """List group admins and members""" + user_one = factories.User(fullname="User One", name="user-one") + user_two = factories.User(fullname="User Two") - other_users = [ - {'name': user_two['id'], 'capacity': 'member'} - ] + other_users = [{"name": user_two["id"], "capacity": "member"}] - group = self._create_group(user_one['name'], other_users) + group = self._create_group(user_one["name"], other_users) - member_list_url = url_for('group.members', - id=group['id']) - env = {'REMOTE_USER': user_one['name'].encode('ascii')} + member_list_url = url_for("group.members", id=group["id"]) + env = {"REMOTE_USER": user_one["name"].encode("ascii")} - member_list_response = app.get( - member_list_url, extra_environ=env) + member_list_response = app.get(member_list_url, extra_environ=env) - assert_true('2 members' in member_list_response) + assert "2 members" in member_list_response member_response_html = BeautifulSoup(member_list_response.body) - user_names = [u.string for u in - member_response_html.select('#member-table td.media a')] - roles = [r.next_sibling.next_sibling.string - for r - in member_response_html.select('#member-table td.media')] + user_names = [ + u.string + for u in member_response_html.select("#member-table td.media a") + ] + roles = [ + r.next_sibling.next_sibling.string + for r in member_response_html.select("#member-table td.media") + ] user_roles = dict(zip(user_names, roles)) - assert_equal(user_roles['User One'], 'Admin') - assert_equal(user_roles['User Two'], 'Member') + assert user_roles["User One"] == "Admin" + assert user_roles["User Two"] == "Member" - def test_membership_add(self): - '''Member can be added via add member page''' - app = self._get_test_app() - owner = factories.User(fullname='My Owner') - factories.User(fullname="My Fullname", name='my-user') - group = self._create_group(owner['name']) + @pytest.mark.usefixtures("clean_db") + def test_membership_add(self, app): + """Member can be added via add member page""" + owner = factories.User(fullname="My Owner") + factories.User(fullname="My Fullname", name="my-user") + group = self._create_group(owner["name"]) - env, response = self._get_group_add_member_page(app, - owner, - group['name']) + env, response = self._get_group_add_member_page( + app, owner, group["name"] + ) - add_form = response.forms['add-member-form'] - add_form['username'] = 'my-user' - add_response = submit_and_follow(app, add_form, env, 'save') + add_form = response.forms["add-member-form"] + add_form["username"] = "my-user" + add_response = submit_and_follow(app, add_form, env, "save") - assert_true('2 members' in add_response) + assert "2 members" in add_response add_response_html = BeautifulSoup(add_response.body) - user_names = [u.string for u in - add_response_html.select('#member-table td.media a')] - roles = [r.next_sibling.next_sibling.string - for r in add_response_html.select('#member-table td.media')] + user_names = [ + u.string + for u in add_response_html.select("#member-table td.media a") + ] + roles = [ + r.next_sibling.next_sibling.string + for r in add_response_html.select("#member-table td.media") + ] user_roles = dict(zip(user_names, roles)) - assert_equal(user_roles['My Owner'], 'Admin') - assert_equal(user_roles['My Fullname'], 'Member') + assert user_roles["My Owner"] == "Admin" + assert user_roles["My Fullname"] == "Member" - def test_admin_add(self): - '''Admin can be added via add member page''' - app = self._get_test_app() - owner = factories.User(fullname='My Owner') - factories.User(fullname="My Fullname", name='my-user') - group = self._create_group(owner['name']) + @pytest.mark.usefixtures("clean_db") + def test_admin_add(self, app): + """Admin can be added via add member page""" + owner = factories.User(fullname="My Owner") + factories.User(fullname="My Fullname", name="my-user") + group = self._create_group(owner["name"]) - env, response = self._get_group_add_member_page(app, - owner, - group['name']) + env, response = self._get_group_add_member_page( + app, owner, group["name"] + ) - add_form = response.forms['add-member-form'] - add_form['username'] = 'my-user' - add_form['role'] = 'admin' - add_response = submit_and_follow(app, add_form, env, 'save') + add_form = response.forms["add-member-form"] + add_form["username"] = "my-user" + add_form["role"] = "admin" + add_response = submit_and_follow(app, add_form, env, "save") - assert_true('2 members' in add_response) + assert "2 members" in add_response add_response_html = BeautifulSoup(add_response.body) - user_names = [u.string for u in - add_response_html.select('#member-table td.media a')] - roles = [r.next_sibling.next_sibling.string - for r in add_response_html.select('#member-table td.media')] + user_names = [ + u.string + for u in add_response_html.select("#member-table td.media a") + ] + roles = [ + r.next_sibling.next_sibling.string + for r in add_response_html.select("#member-table td.media") + ] user_roles = dict(zip(user_names, roles)) - assert_equal(user_roles['My Owner'], 'Admin') - assert_equal(user_roles['My Fullname'], 'Admin') + assert user_roles["My Owner"] == "Admin" + assert user_roles["My Fullname"] == "Admin" - def test_remove_member(self): - '''Member can be removed from group''' - app = self._get_test_app() - user_one = factories.User(fullname='User One', name='user-one') - user_two = factories.User(fullname='User Two') + @pytest.mark.usefixtures("clean_db") + def test_remove_member(self, app): + """Member can be removed from group""" + user_one = factories.User(fullname="User One", name="user-one") + user_two = factories.User(fullname="User Two") - other_users = [ - {'name': user_two['id'], 'capacity': 'member'} - ] + other_users = [{"name": user_two["id"], "capacity": "member"}] - group = self._create_group(user_one['name'], other_users) + group = self._create_group(user_one["name"], other_users) - remove_url = url_for('group.member_delete', - user=user_two['id'], id=group['id']) + remove_url = url_for( + "group.member_delete", user=user_two["id"], id=group["id"] + ) - env = {'REMOTE_USER': user_one['name'].encode('ascii')} + env = {"REMOTE_USER": user_one["name"].encode("ascii")} remove_response = app.post(remove_url, extra_environ=env, status=302) # redirected to member list after removal remove_response = remove_response.follow(extra_environ=env) - assert_true('Group member has been deleted.' in remove_response) - assert_true('1 members' in remove_response) + assert "Group member has been deleted." in remove_response + assert "1 members" in remove_response remove_response_html = BeautifulSoup(remove_response.body) - user_names = [u.string for u in - remove_response_html.select('#member-table td.media a')] - roles = [r.next_sibling.next_sibling.string - for r in - remove_response_html.select('#member-table td.media')] + user_names = [ + u.string + for u in remove_response_html.select("#member-table td.media a") + ] + roles = [ + r.next_sibling.next_sibling.string + for r in remove_response_html.select("#member-table td.media") + ] user_roles = dict(zip(user_names, roles)) - assert_equal(len(user_roles.keys()), 1) - assert_equal(user_roles['User One'], 'Admin') + assert len(user_roles.keys()) == 1 + assert user_roles["User One"] == "Admin" - def test_member_users_cannot_add_members(self): + @pytest.mark.usefixtures("clean_db") + def test_member_users_cannot_add_members(self, app): user = factories.User() group = factories.Group( - users=[{'name': user['name'], 'capacity': 'member'}] + users=[{"name": user["name"], "capacity": "member"}] ) - app = helpers._get_test_app() - - env = {'REMOTE_USER': user['name'].encode('ascii')} + env = {"REMOTE_USER": user["name"].encode("ascii")} with app.flask_app.test_request_context(): app.get( - url_for( - 'group.member_new', - id=group['id'], - ), + url_for("group.member_new", id=group["id"]), extra_environ=env, status=403, ) app.post( - url_for( - 'group.member_new', - id=group['id'], - ), - {'id': 'test', 'username': 'test', 'save': 'save', 'role': 'test'}, + url_for("group.member_new", id=group["id"]), + { + "id": "test", + "username": "test", + "save": "save", + "role": "test", + }, extra_environ=env, status=403, ) - def test_anonymous_users_cannot_add_members(self): + @pytest.mark.usefixtures("clean_db") + def test_anonymous_users_cannot_add_members(self, app): group = factories.Group() - app = helpers._get_test_app() - with app.flask_app.test_request_context(): - app.get( - url_for( - 'group.member_new', - id=group['id'], - ), - status=403, - ) + app.get(url_for("group.member_new", id=group["id"]), status=403) app.post( - url_for( - 'group.member_new', - id=group['id'], - ), - {'id': 'test', 'username': 'test', 'save': 'save', 'role': 'test'}, + url_for("group.member_new", id=group["id"]), + { + "id": "test", + "username": "test", + "save": "save", + "role": "test", + }, status=403, ) -class TestGroupFollow(helpers.FunctionalTestBase): - - def test_group_follow(self): - app = self._get_test_app() +class TestGroupFollow: + @pytest.mark.usefixtures("clean_db") + def test_group_follow(self, app): user = factories.User() group = factories.Group() - env = {'REMOTE_USER': user['name'].encode('ascii')} - follow_url = url_for('group.follow', - id=group['id']) + env = {"REMOTE_USER": user["name"].encode("ascii")} + follow_url = url_for("group.follow", id=group["id"]) response = app.post(follow_url, extra_environ=env, status=302) response = response.follow() - assert_true('You are now following {0}' - .format(group['display_name']) - in response) - - def test_group_follow_not_exist(self): - '''Pass an id for a group that doesn't exist''' - app = self._get_test_app() + assert ( + "You are now following {0}".format(group["display_name"]) + in response + ) + @pytest.mark.usefixtures("clean_db") + def test_group_follow_not_exist(self, app): + """Pass an id for a group that doesn't exist""" user_one = factories.User() - env = {'REMOTE_USER': user_one['name'].encode('ascii')} - follow_url = url_for('group.follow', - id='not-here') + env = {"REMOTE_USER": user_one["name"].encode("ascii")} + follow_url = url_for("group.follow", id="not-here") response = app.post(follow_url, extra_environ=env, status=302) response = response.follow(status=404) - assert_true('Group not found' in response) + assert "Group not found" in response - def test_group_unfollow(self): - app = self._get_test_app() + @pytest.mark.usefixtures("clean_db") + def test_group_unfollow(self, app): user_one = factories.User() group = factories.Group() - env = {'REMOTE_USER': user_one['name'].encode('ascii')} - follow_url = url_for('group.follow', - id=group['id']) + env = {"REMOTE_USER": user_one["name"].encode("ascii")} + follow_url = url_for("group.follow", id=group["id"]) app.post(follow_url, extra_environ=env, status=302) - unfollow_url = url_for('group.unfollow', - id=group['id']) - unfollow_response = app.post(unfollow_url, extra_environ=env, - status=302) + unfollow_url = url_for("group.unfollow", id=group["id"]) + unfollow_response = app.post( + unfollow_url, extra_environ=env, status=302 + ) unfollow_response = unfollow_response.follow() - assert_true('You are no longer following {0}' - .format(group['display_name']) - in unfollow_response) + assert ( + "You are no longer following {0}".format(group["display_name"]) + in unfollow_response + ) - def test_group_unfollow_not_following(self): - '''Unfollow a group not currently following''' - app = self._get_test_app() + @pytest.mark.usefixtures("clean_db") + def test_group_unfollow_not_following(self, app): + """Unfollow a group not currently following""" user_one = factories.User() group = factories.Group() - env = {'REMOTE_USER': user_one['name'].encode('ascii')} - unfollow_url = url_for('group.unfollow', - id=group['id']) - unfollow_response = app.post(unfollow_url, extra_environ=env, - status=302) + env = {"REMOTE_USER": user_one["name"].encode("ascii")} + unfollow_url = url_for("group.unfollow", id=group["id"]) + unfollow_response = app.post( + unfollow_url, extra_environ=env, status=302 + ) unfollow_response = unfollow_response.follow() # /group/[id] 302s to: unfollow_response = unfollow_response.follow() # /group/[name] - assert_true('You are not following {0}'.format(group['id']) - in unfollow_response) + assert ( + "You are not following {0}".format(group["id"]) + in unfollow_response + ) - def test_group_unfollow_not_exist(self): - '''Unfollow a group that doesn't exist.''' - app = self._get_test_app() + @pytest.mark.usefixtures("clean_db") + def test_group_unfollow_not_exist(self, app): + """Unfollow a group that doesn't exist.""" user_one = factories.User() - env = {'REMOTE_USER': user_one['name'].encode('ascii')} - unfollow_url = url_for('group.unfollow', - id='not-here') - unfollow_response = app.post(unfollow_url, extra_environ=env, - status=302) - assert_in('group/not-here', unfollow_response.headers['location']) + env = {"REMOTE_USER": user_one["name"].encode("ascii")} + unfollow_url = url_for("group.unfollow", id="not-here") + unfollow_response = app.post( + unfollow_url, extra_environ=env, status=302 + ) + assert "group/not-here" in unfollow_response.headers["location"] - def test_group_follower_list(self): - '''Following users appear on followers list page.''' - app = self._get_test_app() + @pytest.mark.usefixtures("clean_db") + def test_group_follower_list(self, app): + """Following users appear on followers list page.""" user_one = factories.Sysadmin() group = factories.Group() - env = {'REMOTE_USER': user_one['name'].encode('ascii')} - follow_url = url_for('group.follow', - id=group['id']) + env = {"REMOTE_USER": user_one["name"].encode("ascii")} + follow_url = url_for("group.follow", id=group["id"]) app.post(follow_url, extra_environ=env, status=302) - followers_url = url_for('group.followers', - id=group['id']) + followers_url = url_for("group.followers", id=group["id"]) # Only sysadmins can view the followers list pages - followers_response = app.get(followers_url, extra_environ=env, - status=200) - assert_true(user_one['display_name'] in followers_response) - - -class TestGroupSearch(helpers.FunctionalTestBase): + followers_response = app.get( + followers_url, extra_environ=env, status=200 + ) + assert user_one["display_name"] in followers_response - '''Test searching for groups.''' - def setup(self): - super(TestGroupSearch, self).setup() - self.app = self._get_test_app() - factories.Group(name='grp-one', title='AGrp One') - factories.Group(name='grp-two', title='AGrp Two') - factories.Group(name='grp-three', title='Grp Three') - self.search_url = url_for('group.index') +class TestGroupSearch(object): + """Test searching for groups.""" - def test_group_search(self): - '''Requesting group search (index) returns list of groups and search - form.''' + @pytest.mark.usefixtures("clean_db", "clean_index") + def test_group_search(self, app): + """Requesting group search (index) returns list of groups and search + form.""" - index_response = self.app.get(self.search_url) + factories.Group(name="grp-one", title="AGrp One") + factories.Group(name="grp-two", title="AGrp Two") + factories.Group(name="grp-three", title="Grp Three") + index_response = app.get(url_for("group.index")) index_response_html = BeautifulSoup(index_response.body) - grp_names = index_response_html.select('ul.media-grid ' - 'li.media-item ' - 'h3.media-heading') + grp_names = index_response_html.select( + "ul.media-grid " "li.media-item " "h3.media-heading" + ) grp_names = [n.string for n in grp_names] - assert_equal(len(grp_names), 3) - assert_true('AGrp One' in grp_names) - assert_true('AGrp Two' in grp_names) - assert_true('Grp Three' in grp_names) - - def test_group_search_results(self): - '''Searching via group search form returns list of expected groups.''' - - index_response = self.app.get(self.search_url) - search_form = index_response.forms['group-search-form'] - search_form['q'] = 'AGrp' + assert len(grp_names) == 3 + assert "AGrp One" in grp_names + assert "AGrp Two" in grp_names + assert "Grp Three" in grp_names + + @pytest.mark.usefixtures("clean_db", "clean_index") + def test_group_search_results(self, app): + """Searching via group search form returns list of expected groups.""" + factories.Group(name="grp-one", title="AGrp One") + factories.Group(name="grp-two", title="AGrp Two") + factories.Group(name="grp-three", title="Grp Three") + + index_response = app.get(url_for("group.index")) + search_form = index_response.forms["group-search-form"] + search_form["q"] = "AGrp" search_response = webtest_submit(search_form) search_response_html = BeautifulSoup(search_response.body) - grp_names = search_response_html.select('ul.media-grid ' - 'li.media-item ' - 'h3.media-heading') + grp_names = search_response_html.select( + "ul.media-grid " "li.media-item " "h3.media-heading" + ) grp_names = [n.string for n in grp_names] - assert_equal(len(grp_names), 2) - assert_true('AGrp One' in grp_names) - assert_true('AGrp Two' in grp_names) - assert_true('Grp Three' not in grp_names) - - def test_group_search_no_results(self): - '''Searching with a term that doesn't apply returns no results.''' - - index_response = self.app.get(self.search_url) - search_form = index_response.forms['group-search-form'] - search_form['q'] = 'No Results Here' + assert len(grp_names) == 2 + assert "AGrp One" in grp_names + assert "AGrp Two" in grp_names + assert "Grp Three" not in grp_names + + @pytest.mark.usefixtures("clean_db", "clean_index") + def test_group_search_no_results(self, app): + """Searching with a term that doesn't apply returns no results.""" + + factories.Group(name="grp-one", title="AGrp One") + factories.Group(name="grp-two", title="AGrp Two") + factories.Group(name="grp-three", title="Grp Three") + index_response = app.get(url_for("group.index")) + search_form = index_response.forms["group-search-form"] + search_form["q"] = "No Results Here" search_response = webtest_submit(search_form) search_response_html = BeautifulSoup(search_response.body) - grp_names = search_response_html.select('ul.media-grid ' - 'li.media-item ' - 'h3.media-heading') + grp_names = search_response_html.select( + "ul.media-grid " "li.media-item " "h3.media-heading" + ) grp_names = [n.string for n in grp_names] - assert_equal(len(grp_names), 0) - assert_true('No groups found for "No Results Here"' - in search_response) - - -class TestGroupInnerSearch(helpers.FunctionalTestBase): + assert len(grp_names) == 0 + assert 'No groups found for "No Results Here"' in search_response - '''Test searching within an group.''' - def test_group_search_within_org(self): - '''Group read page request returns list of datasets owned by group.''' - app = self._get_test_app() +class TestGroupInnerSearch(object): + """Test searching within an group.""" + @pytest.mark.usefixtures("clean_db", "clean_index") + def test_group_search_within_org(self, app): + """Group read page request returns list of datasets owned by group.""" grp = factories.Group() - factories.Dataset(name="ds-one", title="Dataset One", - groups=[{'id': grp['id']}]) - factories.Dataset(name="ds-two", title="Dataset Two", - groups=[{'id': grp['id']}]) - factories.Dataset(name="ds-three", title="Dataset Three", - groups=[{'id': grp['id']}]) - - grp_url = url_for('group.read', id=grp['name']) + factories.Dataset( + name="ds-one", title="Dataset One", groups=[{"id": grp["id"]}] + ) + factories.Dataset( + name="ds-two", title="Dataset Two", groups=[{"id": grp["id"]}] + ) + factories.Dataset( + name="ds-three", title="Dataset Three", groups=[{"id": grp["id"]}] + ) + + grp_url = url_for("group.read", id=grp["name"]) grp_response = app.get(grp_url) grp_response_html = BeautifulSoup(grp_response.body) - ds_titles = grp_response_html.select('.dataset-list ' - '.dataset-item ' - '.dataset-heading a') + ds_titles = grp_response_html.select( + ".dataset-list " ".dataset-item " ".dataset-heading a" + ) ds_titles = [t.string for t in ds_titles] - assert_true('3 datasets found' in grp_response) - assert_equal(len(ds_titles), 3) - assert_true('Dataset One' in ds_titles) - assert_true('Dataset Two' in ds_titles) - assert_true('Dataset Three' in ds_titles) + assert "3 datasets found" in grp_response + assert len(ds_titles) == 3 + assert "Dataset One" in ds_titles + assert "Dataset Two" in ds_titles + assert "Dataset Three" in ds_titles - def test_group_search_within_org_results(self): - '''Searching within an group returns expected dataset results.''' - app = self._get_test_app() + @pytest.mark.usefixtures("clean_db", "clean_index") + def test_group_search_within_org_results(self, app): + """Searching within an group returns expected dataset results.""" grp = factories.Group() - factories.Dataset(name="ds-one", title="Dataset One", - groups=[{'id': grp['id']}]) - factories.Dataset(name="ds-two", title="Dataset Two", - groups=[{'id': grp['id']}]) - factories.Dataset(name="ds-three", title="Dataset Three", - groups=[{'id': grp['id']}]) - - grp_url = url_for('group.read', id=grp['name']) + factories.Dataset( + name="ds-one", title="Dataset One", groups=[{"id": grp["id"]}] + ) + factories.Dataset( + name="ds-two", title="Dataset Two", groups=[{"id": grp["id"]}] + ) + factories.Dataset( + name="ds-three", title="Dataset Three", groups=[{"id": grp["id"]}] + ) + + grp_url = url_for("group.read", id=grp["name"]) grp_response = app.get(grp_url) - search_form = grp_response.forms['group-datasets-search-form'] - search_form['q'] = 'One' + search_form = grp_response.forms["group-datasets-search-form"] + search_form["q"] = "One" search_response = webtest_submit(search_form) - assert_true('1 dataset found for "One"' in search_response) + assert "1 dataset found for "One"" in search_response search_response_html = BeautifulSoup(search_response.body) - ds_titles = search_response_html.select('.dataset-list ' - '.dataset-item ' - '.dataset-heading a') + ds_titles = search_response_html.select( + ".dataset-list " ".dataset-item " ".dataset-heading a" + ) ds_titles = [t.string for t in ds_titles] - assert_equal(len(ds_titles), 1) - assert_true('Dataset One' in ds_titles) - assert_true('Dataset Two' not in ds_titles) - assert_true('Dataset Three' not in ds_titles) + assert len(ds_titles) == 1 + assert "Dataset One" in ds_titles + assert "Dataset Two" not in ds_titles + assert "Dataset Three" not in ds_titles - def test_group_search_within_org_no_results(self): - '''Searching for non-returning phrase within an group returns no - results.''' - app = self._get_test_app() + @pytest.mark.usefixtures("clean_db", "clean_index") + def test_group_search_within_org_no_results(self, app): + """Searching for non-returning phrase within an group returns no + results.""" grp = factories.Group() - factories.Dataset(name="ds-one", title="Dataset One", - groups=[{'id': grp['id']}]) - factories.Dataset(name="ds-two", title="Dataset Two", - groups=[{'id': grp['id']}]) - factories.Dataset(name="ds-three", title="Dataset Three", - groups=[{'id': grp['id']}]) - - grp_url = url_for('group.read', id=grp['name']) + factories.Dataset( + name="ds-one", title="Dataset One", groups=[{"id": grp["id"]}] + ) + factories.Dataset( + name="ds-two", title="Dataset Two", groups=[{"id": grp["id"]}] + ) + factories.Dataset( + name="ds-three", title="Dataset Three", groups=[{"id": grp["id"]}] + ) + + grp_url = url_for("group.read", id=grp["name"]) grp_response = app.get(grp_url) - search_form = grp_response.forms['group-datasets-search-form'] - search_form['q'] = 'Nout' + search_form = grp_response.forms["group-datasets-search-form"] + search_form["q"] = "Nout" search_response = webtest_submit(search_form) - assert_true('No datasets found for "Nout"' in search_response.body) + assert 'No datasets found for "Nout"' in search_response.body search_response_html = BeautifulSoup(search_response.body) - ds_titles = search_response_html.select('.dataset-list ' - '.dataset-item ' - '.dataset-heading a') + ds_titles = search_response_html.select( + ".dataset-list " ".dataset-item " ".dataset-heading a" + ) ds_titles = [t.string for t in ds_titles] - assert_equal(len(ds_titles), 0) - + assert len(ds_titles) == 0 -class TestGroupIndex(helpers.FunctionalTestBase): - def test_group_index(self): - app = self._get_test_app() +class TestGroupIndex(object): + @pytest.mark.usefixtures("clean_db") + def test_group_index(self, app): - for i in xrange(1, 26): - _i = '0' + str(i) if i < 10 else i + for i in range(1, 26): + _i = "0" + str(i) if i < 10 else i factories.Group( - name='test-group-{0}'.format(_i), - title='Test Group {0}'.format(_i)) + name="test-group-{0}".format(_i), + title="Test Group {0}".format(_i), + ) - url = url_for('group.index') + url = url_for("group.index") response = app.get(url) - for i in xrange(1, 21): - _i = '0' + str(i) if i < 10 else i - assert_in('Test Group {0}'.format(_i), response) + for i in range(1, 21): + _i = "0" + str(i) if i < 10 else i + assert "Test Group {0}".format(_i) in response - assert 'Test Group 21' not in response + assert "Test Group 21" not in response - url = url_for('group.index', page=1) + url = url_for("group.index", page=1) response = app.get(url) - for i in xrange(1, 21): - _i = '0' + str(i) if i < 10 else i - assert_in('Test Group {0}'.format(_i), response) + for i in range(1, 21): + _i = "0" + str(i) if i < 10 else i + assert "Test Group {0}".format(_i) in response - assert 'Test Group 21' not in response + assert "Test Group 21" not in response - url = url_for('group.index', page=2) + url = url_for("group.index", page=2) response = app.get(url) - for i in xrange(21, 26): - assert_in('Test Group {0}'.format(i), response) + for i in range(21, 26): + assert "Test Group {0}".format(i) in response - assert 'Test Group 20' not in response + assert "Test Group 20" not in response -class TestActivity(helpers.FunctionalTestBase): - def test_simple(self): - '''Checking the template shows the activity stream.''' - app = self._get_test_app() +class TestActivity: + @pytest.mark.usefixtures("clean_db") + def test_simple(self, app): + """Checking the template shows the activity stream.""" user = factories.User() group = factories.Group(user=user) - url = url_for('group.activity', - id=group['id']) + url = url_for("group.activity", id=group["id"]) response = app.get(url) - assert_in('Mr. Test User', response) - assert_in('created the group', response) + assert "Mr. Test User" in response + assert "created the group" in response - def test_create_group(self): - app = self._get_test_app() + @pytest.mark.usefixtures("clean_db") + def test_create_group(self, app): user = factories.User() group = factories.Group(user=user) - url = url_for('group.activity', - id=group['id']) + url = url_for("group.activity", id=group["id"]) response = app.get(url) - assert_in('Mr. Test User'.format(user['name']), - response) - assert_in('created the group', response) - assert_in('Test Group'.format( - group['name']), response) + assert ( + 'Mr. Test User'.format(user["name"]) in response + ) + assert "created the group" in response + assert ( + 'Test Group'.format(group["name"]) in response + ) def _clear_activities(self): model.Session.query(model.ActivityDetail).delete() model.Session.query(model.Activity).delete() model.Session.flush() - def test_change_group(self): - app = self._get_test_app() + @pytest.mark.usefixtures("clean_db") + def test_change_group(self, app): user = factories.User() group = factories.Group(user=user) self._clear_activities() - group['title'] = 'Group with changed title' + group["title"] = "Group with changed title" helpers.call_action( - 'group_update', context={'user': user['name']}, **group) + "group_update", context={"user": user["name"]}, **group + ) - url = url_for('group.activity', - id=group['id']) + url = url_for("group.activity", id=group["id"]) response = app.get(url) - assert_in('Mr. Test User'.format(user['name']), - response) - assert_in('updated the group', response) - assert_in('Group with changed title' - .format(group['name']), response) - - def test_delete_group_using_group_delete(self): - app = self._get_test_app() + assert ( + 'Mr. Test User'.format(user["name"]) in response + ) + assert "updated the group" in response + assert ( + 'Group with changed title'.format( + group["name"] + ) + in response + ) + + @pytest.mark.usefixtures("clean_db") + def test_delete_group_using_group_delete(self, app): user = factories.User() group = factories.Group(user=user) self._clear_activities() helpers.call_action( - 'group_delete', context={'user': user['name']}, **group) + "group_delete", context={"user": user["name"]}, **group + ) - url = url_for('group.activity', - id=group['id']) - env = {'REMOTE_USER': user['name'].encode('ascii')} + url = url_for("group.activity", id=group["id"]) + env = {"REMOTE_USER": user["name"].encode("ascii")} response = app.get(url, extra_environ=env, status=404) # group_delete causes the Member to state=deleted and then the user # doesn't have permission to see their own deleted Group. Therefore you @@ -828,76 +837,87 @@ def test_delete_group_using_group_delete(self): # group_delete was the same as group_update state=deleted but they are # not... - def test_delete_group_by_updating_state(self): - app = self._get_test_app() + @pytest.mark.usefixtures("clean_db") + def test_delete_group_by_updating_state(self, app): user = factories.User() group = factories.Group(user=user) self._clear_activities() - group['state'] = 'deleted' + group["state"] = "deleted" helpers.call_action( - 'group_update', context={'user': user['name']}, **group) + "group_update", context={"user": user["name"]}, **group + ) - url = url_for('group.activity', - id=group['id']) - env = {'REMOTE_USER': user['name'].encode('ascii')} + url = url_for("group.activity", id=group["id"]) + env = {"REMOTE_USER": user["name"].encode("ascii")} response = app.get(url, extra_environ=env) - assert_in('Mr. Test User'.format(user['name']), - response) - assert_in('deleted the group', response) - assert_in('Test Group' - .format(group['name']), response) - - def test_create_dataset(self): - app = self._get_test_app() + assert ( + 'Mr. Test User'.format(user["name"]) in response + ) + assert "deleted the group" in response + assert ( + 'Test Group'.format(group["name"]) in response + ) + + @pytest.mark.usefixtures("clean_db") + def test_create_dataset(self, app): user = factories.User() group = factories.Group(user=user) self._clear_activities() - dataset = factories.Dataset(groups=[{'id': group['id']}], user=user) + dataset = factories.Dataset(groups=[{"id": group["id"]}], user=user) - url = url_for('group.activity', - id=group['id']) + url = url_for("group.activity", id=group["id"]) response = app.get(url) - assert_in('Mr. Test User'.format(user['name']), - response) - assert_in('created the dataset', response) - assert_in('Test Dataset'.format(dataset['id']), - response) - - def test_change_dataset(self): - app = self._get_test_app() + assert ( + 'Mr. Test User'.format(user["name"]) in response + ) + assert "created the dataset" in response + assert ( + 'Test Dataset'.format(dataset["id"]) + in response + ) + + @pytest.mark.usefixtures("clean_db") + def test_change_dataset(self, app): + user = factories.User() group = factories.Group(user=user) - dataset = factories.Dataset(groups=[{'id': group['id']}], user=user) + dataset = factories.Dataset(groups=[{"id": group["id"]}], user=user) self._clear_activities() - dataset['title'] = 'Dataset with changed title' + dataset["title"] = "Dataset with changed title" helpers.call_action( - 'package_update', context={'user': user['name']}, **dataset) + "package_update", context={"user": user["name"]}, **dataset + ) - url = url_for('group.activity', - id=group['id']) + url = url_for("group.activity", id=group["id"]) response = app.get(url) - assert_in('Mr. Test User'.format(user['name']), - response) - assert_in('updated the dataset', response) - assert_in('Dataset with changed title' - .format(dataset['id']), - response) - - def test_delete_dataset(self): - app = self._get_test_app() + assert ( + 'Mr. Test User'.format(user["name"]) in response + ) + assert "updated the dataset" in response + assert ( + 'Dataset with changed title'.format( + dataset["id"] + ) + in response + ) + + @pytest.mark.usefixtures("clean_db") + def test_delete_dataset(self, app): user = factories.User() group = factories.Group(user=user) - dataset = factories.Dataset(groups=[{'id': group['id']}], user=user) + dataset = factories.Dataset(groups=[{"id": group["id"]}], user=user) self._clear_activities() helpers.call_action( - 'package_delete', context={'user': user['name']}, **dataset) + "package_delete", context={"user": user["name"]}, **dataset + ) - url = url_for('group.activity', - id=group['id']) + url = url_for("group.activity", id=group["id"]) response = app.get(url) - assert_in('Mr. Test User'.format(user['name']), - response) - assert_in('deleted the dataset', response) - assert_in('Test Dataset' - .format(dataset['id']), - response) + assert ( + 'Mr. Test User'.format(user["name"]) in response + ) + assert "deleted the dataset" in response + assert ( + 'Test Dataset'.format(dataset["id"]) + in response + ) diff --git a/ckan/tests/controllers/test_home.py b/ckan/tests/controllers/test_home.py index 1b800610588..eb8effde93c 100644 --- a/ckan/tests/controllers/test_home.py +++ b/ckan/tests/controllers/test_home.py @@ -1,119 +1,115 @@ # encoding: utf-8 -from nose.tools import eq_ +import pytest from ckan.lib.helpers import url_for from bs4 import BeautifulSoup from ckan.tests import factories -import ckan.tests.helpers as helpers -class TestHome(helpers.FunctionalTestBase): +class TestHome(object): + def test_home_renders(self, app): + response = app.get(url_for("home.index")) + assert "Welcome to CKAN" in response.body - def test_home_renders(self): - app = self._get_test_app() - response = app.get(url_for('home.index')) - assert 'Welcome to CKAN' in response.body - - def test_template_head_end(self): - app = self._get_test_app() + def test_template_head_end(self, app): # test-core.ini sets ckan.template_head_end to this: - test_link = '' - response = app.get(url_for('home.index')) + ) + response = app.get(url_for("home.index")) assert test_link in response.body - def test_template_footer_end(self): - app = self._get_test_app() + def test_template_footer_end(self, app): # test-core.ini sets ckan.template_footer_end to this: - test_html = 'TEST TEMPLATE_FOOTER_END TEST' - response = app.get(url_for('home.index')) + test_html = "TEST TEMPLATE_FOOTER_END TEST" + response = app.get(url_for("home.index")) assert test_html in response.body - def test_email_address_nag(self): + @pytest.mark.usefixtures("clean_db") + def test_email_address_nag(self, app): # before CKAN 1.6, users were allowed to have no email addresses - app = self._get_test_app() # can't use factory to create user as without email it fails validation from ckan import model - user = model.user.User(name='has-no-email') + user = model.user.User(name="has-no-email") model.Session.add(user) model.Session.commit() - env = {'REMOTE_USER': user.name.encode('ascii')} + env = {"REMOTE_USER": user.name.encode("ascii")} - response = app.get(url=url_for('home.index'), extra_environ=env) + response = app.get(url=url_for("home.index"), extra_environ=env) - assert 'update your profile' in response.body - assert str(url_for('user.edit')) in response.body - assert ' and add your email address.' in response.body + assert "update your profile" in response.body + assert str(url_for("user.edit")) in response.body + assert " and add your email address." in response.body - def test_email_address_no_nag(self): - app = self._get_test_app() - user = factories.User(email='filled_in@nicely.com') - env = {'REMOTE_USER': user['name'].encode('ascii')} + @pytest.mark.usefixtures("clean_db") + def test_email_address_no_nag(self, app): + user = factories.User(email="filled_in@nicely.com") + env = {"REMOTE_USER": user["name"].encode("ascii")} - response = app.get(url=url_for('home.index'), extra_environ=env) + response = app.get(url=url_for("home.index"), extra_environ=env) - assert 'add your email address' not in response + assert "add your email address" not in response - @helpers.change_config('ckan.legacy_route_mappings', - '{"my_home_route": "home.index"}') - def test_map_pylons_to_flask_route(self): - app = self._get_test_app() - response = app.get(url_for('my_home_route')) - assert 'Welcome to CKAN' in response.body + @pytest.mark.ckan_config( + "ckan.legacy_route_mappings", '{"my_home_route": "home.index"}' + ) + def test_map_pylons_to_flask_route(self, app): + response = app.get(url_for("my_home_route")) + assert "Welcome to CKAN" in response.body - response = app.get(url_for('home')) - assert 'Welcome to CKAN' in response.body + response = app.get(url_for("home")) + assert "Welcome to CKAN" in response.body - @helpers.change_config('ckan.legacy_route_mappings', - {'my_home_route': 'home.index'}) - def test_map_pylons_to_flask_route_using_dict(self): - app = self._get_test_app() - response = app.get(url_for('my_home_route')) - assert 'Welcome to CKAN' in response.body + @pytest.mark.ckan_config( + "ckan.legacy_route_mappings", {"my_home_route": "home.index"} + ) + def test_map_pylons_to_flask_route_using_dict(self, app): + response = app.get(url_for("my_home_route")) + assert "Welcome to CKAN" in response.body - response = app.get(url_for('home')) - assert 'Welcome to CKAN' in response.body + response = app.get(url_for("home")) + assert "Welcome to CKAN" in response.body -class TestI18nURLs(helpers.FunctionalTestBase): +class TestI18nURLs(object): + def test_right_urls_are_rendered_on_language_selector(self, app): - def test_right_urls_are_rendered_on_language_selector(self): - app = self._get_test_app() - response = app.get(url_for('home.index')) + response = app.get(url_for("home.index")) html = BeautifulSoup(response.body) - select = html.find(id='field-lang-select') - for option in select.find_all('option'): - if option.text.strip() == u'English': - eq_(option['value'], '/en/') - elif option.text.strip() == u'čeština (Česká republika)': - eq_(option['value'], '/cs_CZ/') - elif option.text.strip() == u'português (Brasil)': - eq_(option['value'], '/pt_BR/') - elif option.text.strip() == u'srpski (latinica)': - eq_(option['value'], '/sr_Latn/') - - def test_default_english_option_is_selected_on_language_selector(self): - app = self._get_test_app() - response = app.get(url_for('home.index')) + select = html.find(id="field-lang-select") + for option in select.find_all("option"): + if option.text.strip() == u"English": + assert option["value"] == "/en/" + elif option.text.strip() == u"čeština (Česká republika)": + assert option["value"] == "/cs_CZ/" + elif option.text.strip() == u"português (Brasil)": + assert option["value"] == "/pt_BR/" + elif option.text.strip() == u"srpski (latinica)": + assert option["value"] == "/sr_Latn/" + + def test_default_english_option_is_selected_on_language_selector( + self, app + ): + response = app.get(url_for("home.index")) html = BeautifulSoup(response.body) - select = html.find(id='field-lang-select') - for option in select.find_all('option'): - if option['value'] == '/en/': - eq_(option['selected'], 'selected') + select = html.find(id="field-lang-select") + for option in select.find_all("option"): + if option["value"] == "/en/": + assert option["selected"] == "selected" else: - assert not option.has_attr('selected') + assert not option.has_attr("selected") - def test_right_option_is_selected_on_language_selector(self): - app = self._get_test_app() - response = app.get(url_for('home.index', locale='ca')) + def test_right_option_is_selected_on_language_selector(self, app): + response = app.get(url_for("home.index", locale="ca")) html = BeautifulSoup(response.body) - select = html.find(id='field-lang-select') - for option in select.find_all('option'): - if option['value'] == '/ca/': - eq_(option['selected'], 'selected') + select = html.find(id="field-lang-select") + for option in select.find_all("option"): + if option["value"] == "/ca/": + assert option["selected"] == "selected" else: - assert not option.has_attr('selected') + assert not option.has_attr("selected") diff --git a/ckan/tests/controllers/test_organization.py b/ckan/tests/controllers/test_organization.py index 8604b6bd233..b3c181fb8fd 100644 --- a/ckan/tests/controllers/test_organization.py +++ b/ckan/tests/controllers/test_organization.py @@ -1,264 +1,322 @@ # encoding: utf-8 +import pytest from bs4 import BeautifulSoup -from nose.tools import assert_equal, assert_true, assert_in from mock import patch -from ckan.lib.helpers import url_for from ckan import model - +from ckan.lib.helpers import url_for from ckan.tests import factories, helpers from ckan.tests.helpers import webtest_submit, submit_and_follow -class TestOrganizationNew(helpers.FunctionalTestBase): - def setup(self): - super(TestOrganizationNew, self).setup() - self.app = helpers._get_test_app() - self.user = factories.User() - self.user_env = {'REMOTE_USER': self.user['name'].encode('ascii')} - self.organization_new_url = url_for('organization.new') +class TestOrganizationNew(object): + @pytest.fixture + def user_env(self): + user = factories.User() + return {"REMOTE_USER": user["name"].encode("ascii")} - def test_not_logged_in(self): - self.app.get(url=url_for('group.new'), status=403) + def test_not_logged_in(self, app): + app.get(url=url_for("group.new"), status=403) - def test_name_required(self): - response = self.app.get( - url=self.organization_new_url, extra_environ=self.user_env) - form = response.forms['organization-edit-form'] - response = webtest_submit( - form, name='save', extra_environ=self.user_env) + @pytest.mark.usefixtures("clean_db") + def test_name_required(self, app, user_env): + response = app.get( + url=url_for("organization.new"), extra_environ=user_env + ) + form = response.forms["organization-edit-form"] + response = webtest_submit(form, name="save", extra_environ=user_env) - assert_true('organization-edit-form' in response.forms) - assert_true('Name: Missing value' in response) + assert "organization-edit-form" in response.forms + assert "Name: Missing value" in response - def test_saved(self): - response = self.app.get( - url=self.organization_new_url, extra_environ=self.user_env) + @pytest.mark.usefixtures("clean_db") + def test_saved(self, app, user_env): + response = app.get( + url=url_for("organization.new"), extra_environ=user_env + ) - form = response.forms['organization-edit-form'] - form['name'] = u'saved' + form = response.forms["organization-edit-form"] + form["name"] = u"saved" response = submit_and_follow( - self.app, form, name='save', extra_environ=self.user_env) - group = helpers.call_action('organization_show', id='saved') - assert_equal(group['title'], u'') - assert_equal(group['type'], 'organization') - assert_equal(group['state'], 'active') - - def test_all_fields_saved(self): - app = helpers._get_test_app() + app, form, name="save", extra_environ=user_env + ) + group = helpers.call_action("organization_show", id="saved") + assert group["title"] == u"" + assert group["type"] == "organization" + assert group["state"] == "active" + + @pytest.mark.usefixtures("clean_db") + def test_all_fields_saved(self, app, user_env): response = app.get( - url=self.organization_new_url, extra_environ=self.user_env) + url=url_for("organization.new"), extra_environ=user_env + ) - form = response.forms['organization-edit-form'] - form['name'] = u'all-fields-saved' - form['title'] = 'Science' - form['description'] = 'Sciencey datasets' - form['image_url'] = 'http://example.com/image.png' + form = response.forms["organization-edit-form"] + form["name"] = u"all-fields-saved" + form["title"] = "Science" + form["description"] = "Sciencey datasets" + form["image_url"] = "http://example.com/image.png" response = submit_and_follow( - self.app, form, name='save', extra_environ=self.user_env) - group = helpers.call_action('organization_show', id='all-fields-saved') - assert_equal(group['title'], u'Science') - assert_equal(group['description'], 'Sciencey datasets') + app, form, name="save", extra_environ=user_env + ) + group = helpers.call_action("organization_show", id="all-fields-saved") + assert group["title"] == u"Science" + assert group["description"] == "Sciencey datasets" -class TestOrganizationList(helpers.FunctionalTestBase): - def setup(self): - super(TestOrganizationList, self).setup() - self.app = helpers._get_test_app() - self.user = factories.User() - self.user_env = {'REMOTE_USER': self.user['name'].encode('ascii')} - self.organization_list_url = url_for('organization.index') - +class TestOrganizationList(object): @patch( - 'ckan.logic.auth.get.organization_list', - return_value={'success': False}) + "ckan.logic.auth.get.organization_list", + return_value={"success": False}, + ) + @pytest.mark.usefixtures("clean_db") def test_error_message_shown_when_no_organization_list_permission( - self, mock_check_access): - response = self.app.get( + self, mock_check_access, app + ): + self.user = factories.User() + self.user_env = {"REMOTE_USER": self.user["name"].encode("ascii")} + self.organization_list_url = url_for("organization.index") + + response = app.get( url=self.organization_list_url, extra_environ=self.user_env, - status=403) + status=403, + ) -class TestOrganizationRead(helpers.FunctionalTestBase): - def test_group_read(self): +class TestOrganizationRead(object): + @pytest.mark.usefixtures("clean_db") + def test_group_read(self, app): org = factories.Organization() - app = helpers._get_test_app() - response = app.get(url=url_for('organization.read', id=org['name'])) - assert_in(org['title'], response) - assert_in(org['description'], response) + response = app.get(url=url_for("organization.read", id=org["name"])) + assert org["title"] in response + assert org["description"] in response - def test_read_redirect_when_given_id(self): + @pytest.mark.usefixtures("clean_db") + def test_read_redirect_when_given_id(self, app): org = factories.Organization() - app = helpers._get_test_app() - response = app.get(url_for('organization.read', - id=org['id']), status=302) + response = app.get( + url_for("organization.read", id=org["id"]), status=302 + ) # redirect replaces the ID with the name in the URL redirected_response = response.follow() - expected_url = url_for('organization.read', id=org['name']) - assert_equal(redirected_response.request.path, expected_url) + expected_url = url_for("organization.read", id=org["name"]) + assert redirected_response.request.path == expected_url - def test_no_redirect_loop_when_name_is_the_same_as_the_id(self): - org = factories.Organization(id='abc', name='abc') - app = helpers._get_test_app() - app.get(url_for('organization.read', - id=org['id']), status=200) # ie no redirect + @pytest.mark.usefixtures("clean_db") + def test_no_redirect_loop_when_name_is_the_same_as_the_id(self, app): + org = factories.Organization(id="abc", name="abc") + app.get( + url_for("organization.read", id=org["id"]), status=200 + ) # ie no redirect -class TestOrganizationEdit(helpers.FunctionalTestBase): - def setup(self): - super(TestOrganizationEdit, self).setup() - self.app = helpers._get_test_app() - self.user = factories.User() - self.user_env = {'REMOTE_USER': self.user['name'].encode('ascii')} - self.organization = factories.Organization(user=self.user) - self.organization_edit_url = url_for( - 'organization.edit', id=self.organization['id']) - - def test_group_doesnt_exist(self): - url = url_for('organization.edit', id='doesnt_exist') - self.app.get(url=url, extra_environ=self.user_env, status=404) - - def test_saved(self): - response = self.app.get( - url=self.organization_edit_url, extra_environ=self.user_env) +class TestOrganizationEdit(object): + @pytest.fixture + def initial_data(self): + user = factories.User() + return { + "user": user, + "user_env": {"REMOTE_USER": user["name"].encode("ascii")}, + "organization": factories.Organization(user=user), + } + + @pytest.mark.usefixtures("clean_db") + def test_group_doesnt_exist(self, app, initial_data): + url = url_for("organization.edit", id="doesnt_exist") + app.get(url=url, extra_environ=initial_data["user_env"], status=404) + + @pytest.mark.usefixtures("clean_db") + def test_saved(self, app, initial_data): + response = app.get( + url=url_for( + "organization.edit", id=initial_data["organization"]["id"] + ), + extra_environ=initial_data["user_env"], + ) - form = response.forms['organization-edit-form'] + form = response.forms["organization-edit-form"] response = webtest_submit( - form, name='save', extra_environ=self.user_env) + form, name="save", extra_environ=initial_data["user_env"] + ) group = helpers.call_action( - 'organization_show', id=self.organization['id']) - assert_equal(group['title'], u'Test Organization') - assert_equal(group['type'], 'organization') - assert_equal(group['state'], 'active') - - def test_all_fields_saved(self): - response = self.app.get( - url=self.organization_edit_url, extra_environ=self.user_env) - - form = response.forms['organization-edit-form'] - form['name'] = u'all-fields-edited' - form['title'] = 'Science' - form['description'] = 'Sciencey datasets' - form['image_url'] = 'http://example.com/image.png' + "organization_show", id=initial_data["organization"]["id"] + ) + assert group["title"] == u"Test Organization" + assert group["type"] == "organization" + assert group["state"] == "active" + + @pytest.mark.usefixtures("clean_db") + def test_all_fields_saved(self, app, initial_data): + response = app.get( + url=url_for( + "organization.edit", id=initial_data["organization"]["id"] + ), + extra_environ=initial_data["user_env"], + ) + + form = response.forms["organization-edit-form"] + form["name"] = u"all-fields-edited" + form["title"] = "Science" + form["description"] = "Sciencey datasets" + form["image_url"] = "http://example.com/image.png" response = webtest_submit( - form, name='save', extra_environ=self.user_env) + form, name="save", extra_environ=initial_data["user_env"] + ) group = helpers.call_action( - 'organization_show', id=self.organization['id']) - assert_equal(group['title'], u'Science') - assert_equal(group['description'], 'Sciencey datasets') - assert_equal(group['image_url'], 'http://example.com/image.png') + "organization_show", id=initial_data["organization"]["id"] + ) + assert group["title"] == u"Science" + assert group["description"] == "Sciencey datasets" + assert group["image_url"] == "http://example.com/image.png" -class TestOrganizationDelete(helpers.FunctionalTestBase): - def setup(self): - super(TestOrganizationDelete, self).setup() - self.app = helpers._get_test_app() - self.user = factories.User() - self.user_env = {'REMOTE_USER': self.user['name'].encode('ascii')} - self.organization = factories.Organization(user=self.user) - - def test_owner_delete(self): - response = self.app.get( - url=url_for('organization.delete', id=self.organization['id']), +class TestOrganizationDelete(object): + @pytest.fixture + def initial_data(self): + user = factories.User() + return { + "user": user, + "user_env": {"REMOTE_USER": user["name"].encode("ascii")}, + "organization": factories.Organization(user=user), + } + + @pytest.mark.usefixtures("clean_db") + def test_owner_delete(self, app, initial_data): + response = app.get( + url=url_for( + "organization.delete", id=initial_data["organization"]["id"] + ), status=200, - extra_environ=self.user_env) + extra_environ=initial_data["user_env"], + ) - form = response.forms['organization-confirm-delete-form'] + form = response.forms["organization-confirm-delete-form"] response = submit_and_follow( - self.app, form, name='delete', extra_environ=self.user_env) + app, form, name="delete", extra_environ=initial_data["user_env"] + ) organization = helpers.call_action( - 'organization_show', id=self.organization['id']) - assert_equal(organization['state'], 'deleted') + "organization_show", id=initial_data["organization"]["id"] + ) + assert organization["state"] == "deleted" - def test_sysadmin_delete(self): + @pytest.mark.usefixtures("clean_db") + def test_sysadmin_delete(self, app, initial_data): sysadmin = factories.Sysadmin() - extra_environ = {'REMOTE_USER': sysadmin['name'].encode('ascii')} - response = self.app.get( - url=url_for('organization.delete', id=self.organization['id']), + extra_environ = {"REMOTE_USER": sysadmin["name"].encode("ascii")} + response = app.get( + url=url_for( + "organization.delete", id=initial_data["organization"]["id"] + ), status=200, - extra_environ=extra_environ) + extra_environ=extra_environ, + ) - form = response.forms['organization-confirm-delete-form'] + form = response.forms["organization-confirm-delete-form"] response = submit_and_follow( - self.app, form, name='delete', extra_environ=self.user_env) + app, form, name="delete", extra_environ=initial_data["user_env"] + ) organization = helpers.call_action( - 'organization_show', id=self.organization['id']) - assert_equal(organization['state'], 'deleted') - - def test_non_authorized_user_trying_to_delete_fails(self): + "organization_show", id=initial_data["organization"]["id"] + ) + assert organization["state"] == "deleted" + + @pytest.mark.usefixtures("clean_db") + def test_non_authorized_user_trying_to_delete_fails( + self, app, initial_data + ): user = factories.User() - extra_environ = {'REMOTE_USER': user['name'].encode('ascii')} - self.app.get( - url=url_for('organization.delete', id=self.organization['id']), + extra_environ = {"REMOTE_USER": user["name"].encode("ascii")} + app.get( + url=url_for( + "organization.delete", id=initial_data["organization"]["id"] + ), status=403, - extra_environ=extra_environ) + extra_environ=extra_environ, + ) organization = helpers.call_action( - 'organization_show', id=self.organization['id']) - assert_equal(organization['state'], 'active') - - def test_anon_user_trying_to_delete_fails(self): - self.app.get( - url=url_for('organization.delete', id=self.organization['id']), - status=403) + "organization_show", id=initial_data["organization"]["id"] + ) + assert organization["state"] == "active" + + @pytest.mark.usefixtures("clean_db") + def test_anon_user_trying_to_delete_fails(self, app, initial_data): + app.get( + url=url_for( + "organization.delete", id=initial_data["organization"]["id"] + ), + status=403, + ) organization = helpers.call_action( - 'organization_show', id=self.organization['id']) - assert_equal(organization['state'], 'active') - - @helpers.change_config('ckan.auth.create_unowned_dataset', False) - def test_delete_organization_with_datasets(self): - ''' Test deletion of organization that has datasets''' - text = 'Organization cannot be deleted while it still has datasets' + "organization_show", id=initial_data["organization"]["id"] + ) + assert organization["state"] == "active" + + @pytest.mark.ckan_config("ckan.auth.create_unowned_dataset", False) + @pytest.mark.usefixtures("clean_db") + def test_delete_organization_with_datasets(self, app, initial_data): + """ Test deletion of organization that has datasets""" + text = "Organization cannot be deleted while it still has datasets" datasets = [ - factories.Dataset(owner_org=self.organization['id']) + factories.Dataset(owner_org=initial_data["organization"]["id"]) for i in range(0, 5) ] - response = self.app.get( - url=url_for('organization.delete', id=self.organization['id']), + response = app.get( + url=url_for( + "organization.delete", id=initial_data["organization"]["id"] + ), status=200, - extra_environ=self.user_env) + extra_environ=initial_data["user_env"], + ) - form = response.forms['organization-confirm-delete-form'] + form = response.forms["organization-confirm-delete-form"] response = submit_and_follow( - self.app, form, name='delete', extra_environ=self.user_env) + app, form, name="delete", extra_environ=initial_data["user_env"] + ) assert text in response.body - def test_delete_organization_with_unknown_dataset_true(self): - ''' Test deletion of organization that has datasets and unknown - datasets are set to true''' - dataset = factories.Dataset(owner_org=self.organization['id']) - assert_equal(dataset['owner_org'], self.organization['id']) + @pytest.mark.usefixtures("clean_db") + def test_delete_organization_with_unknown_dataset_true(self, initial_data): + """ Test deletion of organization that has datasets and unknown + datasets are set to true""" + dataset = factories.Dataset( + owner_org=initial_data["organization"]["id"] + ) + assert dataset["owner_org"] == initial_data["organization"]["id"] user = factories.User() - helpers.call_action('organization_delete', id=self.organization['id'], - context={'user': user['name']}) + helpers.call_action( + "organization_delete", + id=initial_data["organization"]["id"], + context={"user": user["name"]}, + ) - dataset = helpers.call_action('package_show', id=dataset['id']) - assert_equal(dataset['owner_org'], None) + dataset = helpers.call_action("package_show", id=dataset["id"]) + assert dataset["owner_org"] is None -class TestOrganizationBulkProcess(helpers.FunctionalTestBase): - def setup(self): - super(TestOrganizationBulkProcess, self).setup() - self.app = helpers._get_test_app() +class TestOrganizationBulkProcess(object): + @pytest.mark.usefixtures("clean_db") + def test_make_private(self, app): self.user = factories.User() - self.user_env = {'REMOTE_USER': self.user['name'].encode('ascii')} + self.user_env = {"REMOTE_USER": self.user["name"].encode("ascii")} self.organization = factories.Organization(user=self.user) - self.organization_bulk_url = url_for( - 'organization.bulk_process', id=self.organization['id']) - def test_make_private(self): datasets = [ - factories.Dataset(owner_org=self.organization['id']) + factories.Dataset(owner_org=self.organization["id"]) for i in range(0, 5) ] - response = self.app.get( - url=self.organization_bulk_url, extra_environ=self.user_env) + response = app.get( + url=url_for( + "organization.bulk_process", id=self.organization["id"] + ), + extra_environ=self.user_env, + ) form = response.forms[1] for v in form.fields.values(): try: @@ -267,21 +325,31 @@ def test_make_private(self): pass response = webtest_submit( form, - name='bulk_action.private', - value='private', - extra_environ=self.user_env) + name="bulk_action.private", + value="private", + extra_environ=self.user_env, + ) for dataset in datasets: - d = helpers.call_action('package_show', id=dataset['id']) - assert_equal(d['private'], True) + d = helpers.call_action("package_show", id=dataset["id"]) + assert d["private"] + + @pytest.mark.usefixtures("clean_db") + def test_make_public(self, app): + self.user = factories.User() + self.user_env = {"REMOTE_USER": self.user["name"].encode("ascii")} + self.organization = factories.Organization(user=self.user) - def test_make_public(self): datasets = [ - factories.Dataset(owner_org=self.organization['id'], private=True) + factories.Dataset(owner_org=self.organization["id"], private=True) for i in range(0, 5) ] - response = self.app.get( - url=self.organization_bulk_url, extra_environ=self.user_env) + response = app.get( + url=url_for( + "organization.bulk_process", id=self.organization["id"] + ), + extra_environ=self.user_env, + ) form = response.forms[1] for v in form.fields.values(): try: @@ -290,21 +358,30 @@ def test_make_public(self): pass response = webtest_submit( form, - name='bulk_action.public', - value='public', - extra_environ=self.user_env) + name="bulk_action.public", + value="public", + extra_environ=self.user_env, + ) for dataset in datasets: - d = helpers.call_action('package_show', id=dataset['id']) - assert_equal(d['private'], False) + d = helpers.call_action("package_show", id=dataset["id"]) + assert not (d["private"]) - def test_delete(self): + @pytest.mark.usefixtures("clean_db") + def test_delete(self, app): + self.user = factories.User() + self.user_env = {"REMOTE_USER": self.user["name"].encode("ascii")} + self.organization = factories.Organization(user=self.user) datasets = [ - factories.Dataset(owner_org=self.organization['id'], private=True) + factories.Dataset(owner_org=self.organization["id"], private=True) for i in range(0, 5) ] - response = self.app.get( - url=self.organization_bulk_url, extra_environ=self.user_env) + response = app.get( + url=url_for( + "organization.bulk_process", id=self.organization["id"] + ), + extra_environ=self.user_env, + ) form = response.forms[1] for v in form.fields.values(): try: @@ -313,328 +390,337 @@ def test_delete(self): pass response = webtest_submit( form, - name='bulk_action.delete', - value='delete', - extra_environ=self.user_env) + name="bulk_action.delete", + value="delete", + extra_environ=self.user_env, + ) for dataset in datasets: - d = helpers.call_action('package_show', id=dataset['id']) - assert_equal(d['state'], 'deleted') + d = helpers.call_action("package_show", id=dataset["id"]) + assert d["state"] == "deleted" -class TestOrganizationSearch(helpers.FunctionalTestBase): - '''Test searching for organizations.''' +class TestOrganizationSearch(object): + """Test searching for organizations.""" - def setup(self): - super(TestOrganizationSearch, self).setup() - self.app = self._get_test_app() - factories.Organization(name='org-one', title='AOrg One') - factories.Organization(name='org-two', title='AOrg Two') - factories.Organization(name='org-three', title='Org Three') - self.search_url = url_for('organization.index') + @pytest.mark.usefixtures("clean_db") + def test_organization_search(self, app): + """Requesting organization search (index) returns list of + organizations and search form.""" - def test_organization_search(self): - '''Requesting organization search (index) returns list of - organizations and search form.''' + factories.Organization(name="org-one", title="AOrg One") + factories.Organization(name="org-two", title="AOrg Two") + factories.Organization(name="org-three", title="Org Three") - index_response = self.app.get(self.search_url) + index_response = app.get(url_for("organization.index")) index_response_html = BeautifulSoup(index_response.body) - org_names = index_response_html.select('ul.media-grid ' - 'li.media-item ' - 'h3.media-heading') + org_names = index_response_html.select( + "ul.media-grid " "li.media-item " "h3.media-heading" + ) org_names = [n.string for n in org_names] - assert_equal(len(org_names), 3) - assert_true('AOrg One' in org_names) - assert_true('AOrg Two' in org_names) - assert_true('Org Three' in org_names) - - def test_organization_search_results(self): - '''Searching via organization search form returns list of expected - organizations.''' - - index_response = self.app.get(self.search_url) - search_form = index_response.forms['organization-search-form'] - search_form['q'] = 'AOrg' + assert len(org_names) == 3 + assert "AOrg One" in org_names + assert "AOrg Two" in org_names + assert "Org Three" in org_names + + @pytest.mark.usefixtures("clean_db") + def test_organization_search_results(self, app): + """Searching via organization search form returns list of expected + organizations.""" + factories.Organization(name="org-one", title="AOrg One") + factories.Organization(name="org-two", title="AOrg Two") + factories.Organization(name="org-three", title="Org Three") + + index_response = app.get(url_for("organization.index")) + search_form = index_response.forms["organization-search-form"] + search_form["q"] = "AOrg" search_response = webtest_submit(search_form) search_response_html = BeautifulSoup(search_response.body) - org_names = search_response_html.select('ul.media-grid ' - 'li.media-item ' - 'h3.media-heading') + org_names = search_response_html.select( + "ul.media-grid " "li.media-item " "h3.media-heading" + ) org_names = [n.string for n in org_names] - assert_equal(len(org_names), 2) - assert_true('AOrg One' in org_names) - assert_true('AOrg Two' in org_names) - assert_true('Org Three' not in org_names) - - def test_organization_search_no_results(self): - '''Searching with a term that doesn't apply returns no results.''' - - index_response = self.app.get(self.search_url) - search_form = index_response.forms['organization-search-form'] - search_form['q'] = 'No Results Here' + assert len(org_names) == 2 + assert "AOrg One" in org_names + assert "AOrg Two" in org_names + assert "Org Three" not in org_names + + @pytest.mark.usefixtures("clean_db") + def test_organization_search_no_results(self, app): + """Searching with a term that doesn't apply returns no results.""" + factories.Organization(name="org-one", title="AOrg One") + factories.Organization(name="org-two", title="AOrg Two") + factories.Organization(name="org-three", title="Org Three") + + index_response = app.get(url_for("organization.index")) + search_form = index_response.forms["organization-search-form"] + search_form["q"] = "No Results Here" search_response = webtest_submit(search_form) search_response_html = BeautifulSoup(search_response.body) - org_names = search_response_html.select('ul.media-grid ' - 'li.media-item ' - 'h3.media-heading') + org_names = search_response_html.select( + "ul.media-grid " "li.media-item " "h3.media-heading" + ) org_names = [n.string for n in org_names] - assert_equal(len(org_names), 0) - assert_true('No organizations found for "No Results Here"' in - search_response.body) - + assert len(org_names) == 0 + assert ( + 'No organizations found for "No Results Here"' + in search_response.body + ) -class TestOrganizationInnerSearch(helpers.FunctionalTestBase): - '''Test searching within an organization.''' - def test_organization_search_within_org(self): - '''Organization read page request returns list of datasets owned by - organization.''' - app = self._get_test_app() +class TestOrganizationInnerSearch(object): + """Test searching within an organization.""" + @pytest.mark.usefixtures("clean_db", "clean_index") + def test_organization_search_within_org(self, app): + """Organization read page request returns list of datasets owned by + organization.""" org = factories.Organization() - factories.Dataset(name="ds-one", title="Dataset One", - owner_org=org['id']) - factories.Dataset(name="ds-two", title="Dataset Two", - owner_org=org['id']) - factories.Dataset(name="ds-three", title="Dataset Three", - owner_org=org['id']) - - org_url = url_for('organization.read', id=org['name']) + factories.Dataset( + name="ds-one", title="Dataset One", owner_org=org["id"] + ) + factories.Dataset( + name="ds-two", title="Dataset Two", owner_org=org["id"] + ) + factories.Dataset( + name="ds-three", title="Dataset Three", owner_org=org["id"] + ) + + org_url = url_for("organization.read", id=org["name"]) org_response = app.get(org_url) org_response_html = BeautifulSoup(org_response.body) - ds_titles = org_response_html.select('.dataset-list ' - '.dataset-item ' - '.dataset-heading a') + ds_titles = org_response_html.select( + ".dataset-list " ".dataset-item " ".dataset-heading a" + ) ds_titles = [t.string for t in ds_titles] - assert_true('3 datasets found' in org_response) - assert_equal(len(ds_titles), 3) - assert_true('Dataset One' in ds_titles) - assert_true('Dataset Two' in ds_titles) - assert_true('Dataset Three' in ds_titles) - - def test_organization_search_within_org_results(self): - '''Searching within an organization returns expected dataset - results.''' - app = self._get_test_app() + assert "3 datasets found" in org_response + assert len(ds_titles) == 3 + assert "Dataset One" in ds_titles + assert "Dataset Two" in ds_titles + assert "Dataset Three" in ds_titles + @pytest.mark.usefixtures("clean_db", "clean_index") + def test_organization_search_within_org_results(self, app): + """Searching within an organization returns expected dataset + results.""" org = factories.Organization() - factories.Dataset(name="ds-one", title="Dataset One", - owner_org=org['id']) - factories.Dataset(name="ds-two", title="Dataset Two", - owner_org=org['id']) - factories.Dataset(name="ds-three", title="Dataset Three", - owner_org=org['id']) - - org_url = url_for('organization.read', id=org['name']) + factories.Dataset( + name="ds-one", title="Dataset One", owner_org=org["id"] + ) + factories.Dataset( + name="ds-two", title="Dataset Two", owner_org=org["id"] + ) + factories.Dataset( + name="ds-three", title="Dataset Three", owner_org=org["id"] + ) + + org_url = url_for("organization.read", id=org["name"]) org_response = app.get(org_url) - search_form = org_response.forms['organization-datasets-search-form'] - search_form['q'] = 'One' + search_form = org_response.forms["organization-datasets-search-form"] + search_form["q"] = "One" search_response = webtest_submit(search_form) - assert_true('1 dataset found for "One"' in search_response) + assert "1 dataset found for "One"" in search_response search_response_html = BeautifulSoup(search_response.body) - ds_titles = search_response_html.select('.dataset-list ' - '.dataset-item ' - '.dataset-heading a') + ds_titles = search_response_html.select( + ".dataset-list " ".dataset-item " ".dataset-heading a" + ) ds_titles = [t.string for t in ds_titles] - assert_equal(len(ds_titles), 1) - assert_true('Dataset One' in ds_titles) - assert_true('Dataset Two' not in ds_titles) - assert_true('Dataset Three' not in ds_titles) + assert len(ds_titles) == 1 + assert "Dataset One" in ds_titles + assert "Dataset Two" not in ds_titles + assert "Dataset Three" not in ds_titles - def test_organization_search_within_org_no_results(self): - '''Searching for non-returning phrase within an organization returns - no results.''' - app = self._get_test_app() + @pytest.mark.usefixtures("clean_db", "clean_index") + def test_organization_search_within_org_no_results(self, app): + """Searching for non-returning phrase within an organization returns + no results.""" org = factories.Organization() - factories.Dataset(name="ds-one", title="Dataset One", - owner_org=org['id']) - factories.Dataset(name="ds-two", title="Dataset Two", - owner_org=org['id']) - factories.Dataset(name="ds-three", title="Dataset Three", - owner_org=org['id']) - - org_url = url_for('organization.read', id=org['name']) + factories.Dataset( + name="ds-one", title="Dataset One", owner_org=org["id"] + ) + factories.Dataset( + name="ds-two", title="Dataset Two", owner_org=org["id"] + ) + factories.Dataset( + name="ds-three", title="Dataset Three", owner_org=org["id"] + ) + + org_url = url_for("organization.read", id=org["name"]) org_response = app.get(org_url) - search_form = org_response.forms['organization-datasets-search-form'] - search_form['q'] = 'Nout' + search_form = org_response.forms["organization-datasets-search-form"] + search_form["q"] = "Nout" search_response = webtest_submit(search_form) - assert_true('No datasets found for "Nout"' in search_response.body) + assert 'No datasets found for "Nout"' in search_response.body search_response_html = BeautifulSoup(search_response.body) - ds_titles = search_response_html.select('.dataset-list ' - '.dataset-item ' - '.dataset-heading a') + ds_titles = search_response_html.select( + ".dataset-list " ".dataset-item " ".dataset-heading a" + ) ds_titles = [t.string for t in ds_titles] - assert_equal(len(ds_titles), 0) + assert len(ds_titles) == 0 -class TestOrganizationMembership(helpers.FunctionalTestBase): - def test_editor_users_cannot_add_members(self): +class TestOrganizationMembership(object): + @pytest.mark.usefixtures("clean_db") + def test_editor_users_cannot_add_members(self, app): user = factories.User() - organization = factories.Organization(users=[{ - 'name': user['name'], - 'capacity': 'editor' - }]) - - app = helpers._get_test_app() + organization = factories.Organization( + users=[{"name": user["name"], "capacity": "editor"}] + ) - env = {'REMOTE_USER': user['name'].encode('ascii')} + env = {"REMOTE_USER": user["name"].encode("ascii")} with app.flask_app.test_request_context(): app.get( - url_for( - 'organization.member_new', - id=organization['id'], ), + url_for("organization.member_new", id=organization["id"]), extra_environ=env, - status=403, ) + status=403, + ) app.post( - url_for( - 'organization.member_new', - id=organization['id'], ), + url_for("organization.member_new", id=organization["id"]), { - 'id': 'test', - 'username': 'test', - 'save': 'save', - 'role': 'test' + "id": "test", + "username": "test", + "save": "save", + "role": "test", }, extra_environ=env, - status=403, ) - - def test_member_users_cannot_add_members(self): + status=403, + ) + @pytest.mark.usefixtures("clean_db") + def test_member_users_cannot_add_members(self, app): user = factories.User() - organization = factories.Organization(users=[{ - 'name': user['name'], - 'capacity': 'member' - }]) - - app = helpers._get_test_app() + organization = factories.Organization( + users=[{"name": user["name"], "capacity": "member"}] + ) - env = {'REMOTE_USER': user['name'].encode('ascii')} + env = {"REMOTE_USER": user["name"].encode("ascii")} with app.flask_app.test_request_context(): app.get( - url_for( - 'organization.member_new', - id=organization['id'], ), + url_for("organization.member_new", id=organization["id"]), extra_environ=env, - status=403, ) + status=403, + ) app.post( - url_for( - 'organization.member_new', - id=organization['id'], ), + url_for("organization.member_new", id=organization["id"]), { - 'id': 'test', - 'username': 'test', - 'save': 'save', - 'role': 'test' + "id": "test", + "username": "test", + "save": "save", + "role": "test", }, extra_environ=env, - status=403, ) + status=403, + ) - def test_anonymous_users_cannot_add_members(self): + @pytest.mark.usefixtures("clean_db") + def test_anonymous_users_cannot_add_members(self, app): organization = factories.Organization() - app = helpers._get_test_app() - with app.flask_app.test_request_context(): app.get( - url_for( - 'organization.member_new', - id=organization['id'], ), - status=403, ) + url_for("organization.member_new", id=organization["id"]), + status=403, + ) app.post( - url_for( - 'organization.member_new', - id=organization['id'], ), + url_for("organization.member_new", id=organization["id"]), { - 'id': 'test', - 'username': 'test', - 'save': 'save', - 'role': 'test' + "id": "test", + "username": "test", + "save": "save", + "role": "test", }, - status=403, ) + status=403, + ) -class TestActivity(helpers.FunctionalTestBase): - def test_simple(self): - '''Checking the template shows the activity stream.''' - app = self._get_test_app() +class TestActivity(object): + @pytest.mark.usefixtures("clean_db") + def test_simple(self, app): + """Checking the template shows the activity stream.""" user = factories.User() org = factories.Organization(user=user) - url = url_for('organization.activity', - id=org['id']) + url = url_for("organization.activity", id=org["id"]) response = app.get(url) - assert_in('Mr. Test User', response) - assert_in('created the organization', response) + assert "Mr. Test User" in response + assert "created the organization" in response - def test_create_organization(self): - app = self._get_test_app() + @pytest.mark.usefixtures("clean_db") + def test_create_organization(self, app): user = factories.User() org = factories.Organization(user=user) - url = url_for('organization.activity', - id=org['id']) + url = url_for("organization.activity", id=org["id"]) response = app.get(url) - assert_in('Mr. Test User'.format(user['name']), - response) - assert_in('created the organization', response) - assert_in('Test Organization'.format( - org['name']), response) + assert ( + 'Mr. Test User'.format(user["name"]) in response + ) + assert "created the organization" in response + assert ( + 'Test Organization'.format(org["name"]) + in response + ) def _clear_activities(self): model.Session.query(model.ActivityDetail).delete() model.Session.query(model.Activity).delete() model.Session.flush() - def test_change_organization(self): - app = self._get_test_app() + @pytest.mark.usefixtures("clean_db") + def test_change_organization(self, app): user = factories.User() org = factories.Organization(user=user) self._clear_activities() - org['title'] = 'Organization with changed title' + org["title"] = "Organization with changed title" helpers.call_action( - 'organization_update', context={'user': user['name']}, **org) + "organization_update", context={"user": user["name"]}, **org + ) - url = url_for('organization.activity', - id=org['id']) + url = url_for("organization.activity", id=org["id"]) response = app.get(url) - assert_in('Mr. Test User'.format(user['name']), - response) - assert_in('updated the organization', response) - assert_in('Organization with changed title' - .format(org['name']), response) - - def test_delete_org_using_organization_delete(self): - app = self._get_test_app() + assert ( + 'Mr. Test User'.format(user["name"]) in response + ) + assert "updated the organization" in response + assert ( + 'Organization with changed title'.format( + org["name"] + ) + in response + ) + + @pytest.mark.usefixtures("clean_db") + def test_delete_org_using_organization_delete(self, app): user = factories.User() org = factories.Organization(user=user) self._clear_activities() helpers.call_action( - 'organization_delete', context={'user': user['name']}, **org) + "organization_delete", context={"user": user["name"]}, **org + ) - url = url_for('organization.activity', - id=org['id']) - env = {'REMOTE_USER': user['name'].encode('ascii')} + url = url_for("organization.activity", id=org["id"]) + env = {"REMOTE_USER": user["name"].encode("ascii")} response = app.get(url, extra_environ=env, status=404) # organization_delete causes the Member to state=deleted and then the # user doesn't have permission to see their own deleted Organization. @@ -642,76 +728,87 @@ def test_delete_org_using_organization_delete(self): # hope that organization_delete was the same as organization_update # state=deleted but they are not... - def test_delete_org_by_updating_state(self): - app = self._get_test_app() + @pytest.mark.usefixtures("clean_db") + def test_delete_org_by_updating_state(self, app): user = factories.User() org = factories.Organization(user=user) self._clear_activities() - org['state'] = 'deleted' + org["state"] = "deleted" helpers.call_action( - 'organization_update', context={'user': user['name']}, **org) + "organization_update", context={"user": user["name"]}, **org + ) - url = url_for('organization.activity', - id=org['id']) - env = {'REMOTE_USER': user['name'].encode('ascii')} + url = url_for("organization.activity", id=org["id"]) + env = {"REMOTE_USER": user["name"].encode("ascii")} response = app.get(url, extra_environ=env) - assert_in('Mr. Test User'.format(user['name']), - response) - assert_in('deleted the organization', response) - assert_in('Test Organization'.format( - org['name']), response) - - def test_create_dataset(self): - app = self._get_test_app() + assert ( + 'Mr. Test User'.format(user["name"]) in response + ) + assert "deleted the organization" in response + assert ( + 'Test Organization'.format(org["name"]) + in response + ) + + @pytest.mark.usefixtures("clean_db") + def test_create_dataset(self, app): user = factories.User() org = factories.Organization() self._clear_activities() - dataset = factories.Dataset(owner_org=org['id'], user=user) + dataset = factories.Dataset(owner_org=org["id"], user=user) - url = url_for('organization.activity', - id=org['id']) + url = url_for("organization.activity", id=org["id"]) response = app.get(url) - assert_in('Mr. Test User'.format(user['name']), - response) - assert_in('created the dataset', response) - assert_in('Test Dataset'.format(dataset['id']), - response) - - def test_change_dataset(self): - app = self._get_test_app() + assert ( + 'Mr. Test User'.format(user["name"]) in response + ) + assert "created the dataset" in response + assert ( + 'Test Dataset'.format(dataset["id"]) + in response + ) + + @pytest.mark.usefixtures("clean_db") + def test_change_dataset(self, app): user = factories.User() org = factories.Organization() - dataset = factories.Dataset(owner_org=org['id'], user=user) + dataset = factories.Dataset(owner_org=org["id"], user=user) self._clear_activities() - dataset['title'] = 'Dataset with changed title' + dataset["title"] = "Dataset with changed title" helpers.call_action( - 'package_update', context={'user': user['name']}, **dataset) + "package_update", context={"user": user["name"]}, **dataset + ) - url = url_for('organization.activity', - id=org['id']) + url = url_for("organization.activity", id=org["id"]) response = app.get(url) - assert_in('Mr. Test User'.format(user['name']), - response) - assert_in('updated the dataset', response) - assert_in('Dataset with changed title' - .format(dataset['id']), - response) - - def test_delete_dataset(self): - app = self._get_test_app() + assert ( + 'Mr. Test User'.format(user["name"]) in response + ) + assert "updated the dataset" in response + assert ( + 'Dataset with changed title'.format( + dataset["id"] + ) + in response + ) + + @pytest.mark.usefixtures("clean_db") + def test_delete_dataset(self, app): user = factories.User() org = factories.Organization() - dataset = factories.Dataset(owner_org=org['id'], user=user) + dataset = factories.Dataset(owner_org=org["id"], user=user) self._clear_activities() helpers.call_action( - 'package_delete', context={'user': user['name']}, **dataset) + "package_delete", context={"user": user["name"]}, **dataset + ) - url = url_for('organization.activity', - id=org['id']) + url = url_for("organization.activity", id=org["id"]) response = app.get(url) - assert_in('Mr. Test User'.format(user['name']), - response) - assert_in('deleted the dataset', response) - assert_in('Test Dataset' - .format(dataset['id']), - response) + assert ( + 'Mr. Test User'.format(user["name"]) in response + ) + assert "deleted the dataset" in response + assert ( + 'Test Dataset'.format(dataset["id"]) + in response + ) diff --git a/ckan/tests/controllers/test_package.py b/ckan/tests/controllers/test_package.py index f847142b16d..7296e6a1da9 100644 --- a/ckan/tests/controllers/test_package.py +++ b/ckan/tests/controllers/test_package.py @@ -1,18 +1,10 @@ # encoding: utf-8 from bs4 import BeautifulSoup -from nose.tools import ( - assert_equal, - assert_not_equal, - assert_raises, - assert_true, - assert_not_in, - assert_in, -) import mock from ckan.lib.helpers import url_for - +import pytest import ckan.model as model import ckan.model.activity as activity_model import ckan.plugins as p @@ -22,666 +14,608 @@ import ckan.tests.helpers as helpers import ckan.tests.factories as factories - webtest_submit = helpers.webtest_submit submit_and_follow = helpers.submit_and_follow def _get_package_new_page(app): user = factories.User() - env = {'REMOTE_USER': user['name'].encode('ascii')} - response = app.get( - url=url_for('dataset.new'), - extra_environ=env, - ) + env = {"REMOTE_USER": user["name"].encode("ascii")} + response = app.get(url=url_for("dataset.new"), extra_environ=env) return env, response -class TestPackageNew(helpers.FunctionalTestBase): - def test_form_renders(self): - app = self._get_test_app() +class TestPackageNew(object): + @pytest.mark.usefixtures("clean_db") + def test_form_renders(self, app): env, response = _get_package_new_page(app) - assert_true('dataset-edit' in response.forms) + assert "dataset-edit" in response.forms - @helpers.change_config('ckan.auth.create_unowned_dataset', 'false') - def test_needs_organization_but_no_organizations_has_button(self): - ''' Scenario: The settings say every dataset needs an organization + @pytest.mark.ckan_config("ckan.auth.create_unowned_dataset", "false") + @pytest.mark.usefixtures("clean_db") + def test_needs_organization_but_no_organizations_has_button(self, app): + """ Scenario: The settings say every dataset needs an organization but there are no organizations. If the user is allowed to create an organization they should be prompted to do so when they try to create - a new dataset''' - app = self._get_test_app() + a new dataset""" sysadmin = factories.Sysadmin() - env = {'REMOTE_USER': sysadmin['name'].encode('ascii')} - response = app.get( - url=url_for('dataset.new'), - extra_environ=env - ) - assert 'dataset-edit' not in response.forms - assert url_for(controller='organization', action='new') in response - - @helpers.mock_auth('ckan.logic.auth.create.package_create') - @helpers.change_config('ckan.auth.create_unowned_dataset', 'false') - @helpers.change_config('ckan.auth.user_create_organizations', 'false') - def test_needs_organization_but_no_organizations_no_button(self, - mock_p_create): - ''' Scenario: The settings say every dataset needs an organization + env = {"REMOTE_USER": sysadmin["name"].encode("ascii")} + response = app.get(url=url_for("dataset.new"), extra_environ=env) + assert "dataset-edit" not in response.forms + assert url_for(controller="organization", action="new") in response + + @pytest.mark.ckan_config("ckan.auth.create_unowned_dataset", "false") + @pytest.mark.ckan_config("ckan.auth.user_create_organizations", "false") + @mock.patch("ckan.logic.auth.create.package_create") + @pytest.mark.usefixtures("clean_db") + def test_needs_organization_but_no_organizations_no_button( + self, mock_p_create, app + ): + """ Scenario: The settings say every dataset needs an organization but there are no organizations. If the user is not allowed to create an organization they should be told to ask the admin but no link should be presented. Note: This cannot happen with the default ckan and requires - a plugin to overwrite the package_create behavior''' - mock_p_create.return_value = {'success': True} + a plugin to overwrite the package_create behavior""" + mock_p_create.return_value = {"success": True} - app = self._get_test_app() user = factories.User() - env = {'REMOTE_USER': user['name'].encode('ascii')} - response = app.get( - url=url_for('dataset.new'), - extra_environ=env - ) + env = {"REMOTE_USER": user["name"].encode("ascii")} + response = app.get(url=url_for("dataset.new"), extra_environ=env) - assert 'dataset-edit' not in response.forms - assert url_for(controller='organization', action='new') not in response - assert 'Ask a system administrator' in response + assert "dataset-edit" not in response.forms + assert url_for(controller="organization", action="new") not in response + assert "Ask a system administrator" in response - def test_name_required(self): - app = self._get_test_app() + @pytest.mark.usefixtures("clean_db") + def test_name_required(self, app): env, response = _get_package_new_page(app) - form = response.forms['dataset-edit'] + form = response.forms["dataset-edit"] - response = webtest_submit(form, 'save', status=200, extra_environ=env) - assert_true('dataset-edit' in response.forms) - assert_true('Name: Missing value' in response) + response = webtest_submit(form, "save", status=200, extra_environ=env) + assert "dataset-edit" in response.forms + assert "Name: Missing value" in response - def test_resource_form_renders(self): - app = self._get_test_app() + @pytest.mark.usefixtures("clean_db") + def test_resource_form_renders(self, app): env, response = _get_package_new_page(app) - form = response.forms['dataset-edit'] - form['name'] = u'resource-form-renders' + form = response.forms["dataset-edit"] + form["name"] = u"resource-form-renders" - response = submit_and_follow(app, form, env, 'save') - assert_true('resource-edit' in response.forms) + response = submit_and_follow(app, form, env, "save") + assert "resource-edit" in response.forms - def test_first_page_creates_draft_package(self): - app = self._get_test_app() + @pytest.mark.usefixtures("clean_db") + def test_first_page_creates_draft_package(self, app): env, response = _get_package_new_page(app) - form = response.forms['dataset-edit'] - form['name'] = u'first-page-creates-draft' + form = response.forms["dataset-edit"] + form["name"] = u"first-page-creates-draft" - webtest_submit(form, 'save', status=302, extra_environ=env) - pkg = model.Package.by_name(u'first-page-creates-draft') - assert_equal(pkg.state, 'draft') + webtest_submit(form, "save", status=302, extra_environ=env) + pkg = model.Package.by_name(u"first-page-creates-draft") + assert pkg.state == "draft" - def test_resource_required(self): - app = self._get_test_app() + @pytest.mark.usefixtures("clean_db") + def test_resource_required(self, app): env, response = _get_package_new_page(app) - form = response.forms['dataset-edit'] - form['name'] = u'one-resource-required' + form = response.forms["dataset-edit"] + form["name"] = u"one-resource-required" - response = submit_and_follow(app, form, env, 'save') - form = response.forms['resource-edit'] + response = submit_and_follow(app, form, env, "save") + form = response.forms["resource-edit"] - response = webtest_submit(form, 'save', value='go-metadata', - status=200, extra_environ=env) - assert_true('resource-edit' in response.forms) - assert_true('You must add at least one data resource' in response) + response = webtest_submit( + form, "save", value="go-metadata", status=200, extra_environ=env + ) + assert "resource-edit" in response.forms + assert "You must add at least one data resource" in response - def test_complete_package_with_one_resource(self): - app = self._get_test_app() + @pytest.mark.usefixtures("clean_db") + def test_complete_package_with_one_resource(self, app): env, response = _get_package_new_page(app) - form = response.forms['dataset-edit'] - form['name'] = u'complete-package-with-one-resource' + form = response.forms["dataset-edit"] + form["name"] = u"complete-package-with-one-resource" - response = submit_and_follow(app, form, env, 'save') - form = response.forms['resource-edit'] - form['url'] = u'http://example.com/resource' + response = submit_and_follow(app, form, env, "save") + form = response.forms["resource-edit"] + form["url"] = u"http://example.com/resource" - submit_and_follow(app, form, env, 'save', 'go-metadata') - pkg = model.Package.by_name(u'complete-package-with-one-resource') - assert_equal(pkg.resources[0].url, u'http://example.com/resource') - assert_equal(pkg.state, 'active') + submit_and_follow(app, form, env, "save", "go-metadata") + pkg = model.Package.by_name(u"complete-package-with-one-resource") + assert pkg.resources[0].url == u"http://example.com/resource" + assert pkg.state == "active" - def test_complete_package_with_two_resources(self): - app = self._get_test_app() + @pytest.mark.usefixtures("clean_db") + def test_complete_package_with_two_resources(self, app): env, response = _get_package_new_page(app) - form = response.forms['dataset-edit'] - form['name'] = u'complete-package-with-two-resources' - - response = submit_and_follow(app, form, env, 'save') - form = response.forms['resource-edit'] - form['url'] = u'http://example.com/resource0' - - response = submit_and_follow(app, form, env, 'save', 'again') - form = response.forms['resource-edit'] - form['url'] = u'http://example.com/resource1' - - submit_and_follow(app, form, env, 'save', 'go-metadata') - pkg = model.Package.by_name(u'complete-package-with-two-resources') - assert_equal(pkg.resources[0].url, u'http://example.com/resource0') - assert_equal(pkg.resources[1].url, u'http://example.com/resource1') - assert_equal(pkg.state, 'active') - - # def test_resource_uploads(self): - # app = self._get_test_app() - # env, response = _get_package_new_page(app) - # form = response.forms['dataset-edit'] - # form['name'] = u'complete-package-with-two-resources' - - # response = submit_and_follow(app, form, env, 'save') - # form = response.forms['resource-edit'] - # form['upload'] = ('README.rst', b'data') - - # response = submit_and_follow(app, form, env, 'save', 'go-metadata') - # pkg = model.Package.by_name(u'complete-package-with-two-resources') - # assert_equal(pkg.resources[0].url_type, u'upload') - # assert_equal(pkg.state, 'active') - # response = app.get( - # url_for( - # controller='package', - # action='resource_download', - # id=pkg.id, - # resource_id=pkg.resources[0].id - # ), - # ) - # assert_equal('data', response.body) - - def test_previous_button_works(self): - app = self._get_test_app() + form = response.forms["dataset-edit"] + form["name"] = u"complete-package-with-two-resources" + + response = submit_and_follow(app, form, env, "save") + form = response.forms["resource-edit"] + form["url"] = u"http://example.com/resource0" + + response = submit_and_follow(app, form, env, "save", "again") + form = response.forms["resource-edit"] + form["url"] = u"http://example.com/resource1" + + submit_and_follow(app, form, env, "save", "go-metadata") + pkg = model.Package.by_name(u"complete-package-with-two-resources") + assert pkg.resources[0].url == u"http://example.com/resource0" + assert pkg.resources[1].url == u"http://example.com/resource1" + assert pkg.state == "active" + + @pytest.mark.usefixtures("clean_db") + def test_previous_button_works(self, app): env, response = _get_package_new_page(app) - form = response.forms['dataset-edit'] - form['name'] = u'previous-button-works' + form = response.forms["dataset-edit"] + form["name"] = u"previous-button-works" - response = submit_and_follow(app, form, env, 'save') - form = response.forms['resource-edit'] + response = submit_and_follow(app, form, env, "save") + form = response.forms["resource-edit"] - response = submit_and_follow(app, form, env, 'save', 'go-dataset') - assert_true('dataset-edit' in response.forms) + response = submit_and_follow(app, form, env, "save", "go-dataset") + assert "dataset-edit" in response.forms - def test_previous_button_populates_form(self): - app = self._get_test_app() + @pytest.mark.usefixtures("clean_db") + def test_previous_button_populates_form(self, app): env, response = _get_package_new_page(app) - form = response.forms['dataset-edit'] - form['name'] = u'previous-button-populates-form' + form = response.forms["dataset-edit"] + form["name"] = u"previous-button-populates-form" - response = submit_and_follow(app, form, env, 'save') - form = response.forms['resource-edit'] + response = submit_and_follow(app, form, env, "save") + form = response.forms["resource-edit"] - response = submit_and_follow(app, form, env, 'save', 'go-dataset') - form = response.forms['dataset-edit'] - assert_true('title' in form.fields) - assert_equal(form['name'].value, u'previous-button-populates-form') + response = submit_and_follow(app, form, env, "save", "go-dataset") + form = response.forms["dataset-edit"] + assert "title" in form.fields + assert form["name"].value == u"previous-button-populates-form" - def test_previous_next_maintains_draft_state(self): - app = self._get_test_app() + @pytest.mark.usefixtures("clean_db") + def test_previous_next_maintains_draft_state(self, app): env, response = _get_package_new_page(app) - form = response.forms['dataset-edit'] - form['name'] = u'previous-next-maintains-draft' + form = response.forms["dataset-edit"] + form["name"] = u"previous-next-maintains-draft" - response = submit_and_follow(app, form, env, 'save') - form = response.forms['resource-edit'] + response = submit_and_follow(app, form, env, "save") + form = response.forms["resource-edit"] - response = submit_and_follow(app, form, env, 'save', 'go-dataset') - form = response.forms['dataset-edit'] + response = submit_and_follow(app, form, env, "save", "go-dataset") + form = response.forms["dataset-edit"] - webtest_submit(form, 'save', status=302, extra_environ=env) - pkg = model.Package.by_name(u'previous-next-maintains-draft') - assert_equal(pkg.state, 'draft') + webtest_submit(form, "save", status=302, extra_environ=env) + pkg = model.Package.by_name(u"previous-next-maintains-draft") + assert pkg.state == "draft" - def test_dataset_edit_org_dropdown_visible_to_normal_user_with_orgs_available(self): - ''' + @pytest.mark.usefixtures("clean_db") + def test_dataset_edit_org_dropdown_visible_to_normal_user_with_orgs_available( + self, app + ): + """ The 'Organization' dropdown is available on the dataset create/edit page to normal (non-sysadmin) users who have organizations available to them. - ''' + """ user = factories.User() # user is admin of org. org = factories.Organization( - name="my-org", - users=[{'name': user['id'], 'capacity': 'admin'}] + name="my-org", users=[{"name": user["id"], "capacity": "admin"}] ) - app = self._get_test_app() - env = {'REMOTE_USER': user['name'].encode('ascii')} - response = app.get( - url=url_for('dataset.new'), - extra_environ=env, - ) + env = {"REMOTE_USER": user["name"].encode("ascii")} + response = app.get(url=url_for("dataset.new"), extra_environ=env) # organization dropdown available in create page. - form = response.forms['dataset-edit'] - assert 'owner_org' in form.fields + form = response.forms["dataset-edit"] + assert "owner_org" in form.fields # create dataset - form['name'] = u'my-dataset' - form['owner_org'] = org['id'] - response = submit_and_follow(app, form, env, 'save') + form["name"] = u"my-dataset" + form["owner_org"] = org["id"] + response = submit_and_follow(app, form, env, "save") # add a resource to make the pkg active - resource_form = response.forms['resource-edit'] - resource_form['url'] = u'http://example.com/resource' - submit_and_follow(app, resource_form, env, 'save', 'go-metadata') - pkg = model.Package.by_name(u'my-dataset') - assert_equal(pkg.state, 'active') + resource_form = response.forms["resource-edit"] + resource_form["url"] = u"http://example.com/resource" + submit_and_follow(app, resource_form, env, "save", "go-metadata") + pkg = model.Package.by_name(u"my-dataset") + assert pkg.state == "active" # edit package page response - url = url_for('dataset.edit', - id=pkg.id) + url = url_for("dataset.edit", id=pkg.id) pkg_edit_response = app.get(url=url, extra_environ=env) # A field with the correct id is in the response - form = pkg_edit_response.forms['dataset-edit'] - assert 'owner_org' in form.fields + form = pkg_edit_response.forms["dataset-edit"] + assert "owner_org" in form.fields # The organization id is in the response in a value attribute - owner_org_options = [value for (value, _) in form['owner_org'].options] - assert org['id'] in owner_org_options + owner_org_options = [value for (value, _) in form["owner_org"].options] + assert org["id"] in owner_org_options - def test_dataset_edit_org_dropdown_normal_user_can_remove_org(self): - ''' + @pytest.mark.usefixtures("clean_db") + def test_dataset_edit_org_dropdown_normal_user_can_remove_org(self, app): + """ A normal user (non-sysadmin) can remove an organization from a dataset have permissions on. - ''' + """ user = factories.User() # user is admin of org. - org = factories.Organization(name="my-org", - users=[{'name': user['id'], 'capacity': 'admin'}]) - - app = self._get_test_app() - env = {'REMOTE_USER': user['name'].encode('ascii')} - response = app.get( - url=url_for('dataset.new'), - extra_environ=env, + org = factories.Organization( + name="my-org", users=[{"name": user["id"], "capacity": "admin"}] ) + env = {"REMOTE_USER": user["name"].encode("ascii")} + response = app.get(url=url_for("dataset.new"), extra_environ=env) + # create dataset with owner_org - form = response.forms['dataset-edit'] - form['name'] = u'my-dataset' - form['owner_org'] = org['id'] - response = submit_and_follow(app, form, env, 'save') + form = response.forms["dataset-edit"] + form["name"] = u"my-dataset" + form["owner_org"] = org["id"] + response = submit_and_follow(app, form, env, "save") # add a resource to make the pkg active - resource_form = response.forms['resource-edit'] - resource_form['url'] = u'http://example.com/resource' - submit_and_follow(app, resource_form, env, 'save', 'go-metadata') - pkg = model.Package.by_name(u'my-dataset') - assert_equal(pkg.state, 'active') - assert_equal(pkg.owner_org, org['id']) - assert_not_equal(pkg.owner_org, None) + resource_form = response.forms["resource-edit"] + resource_form["url"] = u"http://example.com/resource" + submit_and_follow(app, resource_form, env, "save", "go-metadata") + pkg = model.Package.by_name(u"my-dataset") + assert pkg.state == "active" + assert pkg.owner_org == org["id"] + assert pkg.owner_org is not None # edit package page response - url = url_for('dataset.edit', - id=pkg.id) + url = url_for("dataset.edit", id=pkg.id) pkg_edit_response = app.get(url=url, extra_environ=env) # edit dataset - edit_form = pkg_edit_response.forms['dataset-edit'] - edit_form['owner_org'] = '' - submit_and_follow(app, edit_form, env, 'save') - post_edit_pkg = model.Package.by_name(u'my-dataset') - assert_equal(post_edit_pkg.owner_org, None) - assert_not_equal(post_edit_pkg.owner_org, org['id']) - - def test_dataset_edit_org_dropdown_not_visible_to_normal_user_with_no_orgs_available(self): - ''' + edit_form = pkg_edit_response.forms["dataset-edit"] + edit_form["owner_org"] = "" + submit_and_follow(app, edit_form, env, "save") + post_edit_pkg = model.Package.by_name(u"my-dataset") + assert post_edit_pkg.owner_org is None + assert post_edit_pkg.owner_org != org["id"] + + @pytest.mark.usefixtures("clean_db") + def test_dataset_edit_org_dropdown_not_visible_to_normal_user_with_no_orgs_available( + self, app + ): + """ The 'Organization' dropdown is not available on the dataset create/edit page to normal (non-sysadmin) users who have no organizations available to them. - ''' + """ user = factories.User() # user isn't admin of org. org = factories.Organization(name="my-org") - app = self._get_test_app() - env = {'REMOTE_USER': user['name'].encode('ascii')} - response = app.get( - url=url_for('dataset.new'), - extra_environ=env, - ) + env = {"REMOTE_USER": user["name"].encode("ascii")} + response = app.get(url=url_for("dataset.new"), extra_environ=env) # organization dropdown not in create page. - form = response.forms['dataset-edit'] - assert 'owner_org' not in form.fields + form = response.forms["dataset-edit"] + assert "owner_org" not in form.fields # create dataset - form['name'] = u'my-dataset' - response = submit_and_follow(app, form, env, 'save') + form["name"] = u"my-dataset" + response = submit_and_follow(app, form, env, "save") # add a resource to make the pkg active - resource_form = response.forms['resource-edit'] - resource_form['url'] = u'http://example.com/resource' - submit_and_follow(app, resource_form, env, 'save', 'go-metadata') - pkg = model.Package.by_name(u'my-dataset') - assert_equal(pkg.state, 'active') + resource_form = response.forms["resource-edit"] + resource_form["url"] = u"http://example.com/resource" + submit_and_follow(app, resource_form, env, "save", "go-metadata") + pkg = model.Package.by_name(u"my-dataset") + assert pkg.state == "active" # edit package response - url = url_for('dataset.edit', - id=model.Package.by_name(u'my-dataset').id) + url = url_for( + "dataset.edit", id=model.Package.by_name(u"my-dataset").id + ) pkg_edit_response = app.get(url=url, extra_environ=env) # A field with the correct id is in the response - form = pkg_edit_response.forms['dataset-edit'] - assert 'owner_org' not in form.fields + form = pkg_edit_response.forms["dataset-edit"] + assert "owner_org" not in form.fields # The organization id is in the response in a value attribute - assert 'value="{0}"'.format(org['id']) not in pkg_edit_response + assert 'value="{0}"'.format(org["id"]) not in pkg_edit_response - def test_dataset_edit_org_dropdown_visible_to_sysadmin_with_no_orgs_available(self): - ''' + @pytest.mark.usefixtures("clean_db") + def test_dataset_edit_org_dropdown_visible_to_sysadmin_with_no_orgs_available( + self, app + ): + """ The 'Organization' dropdown is available to sysadmin users regardless of whether they personally have an organization they administrate. - ''' + """ user = factories.User() sysadmin = factories.Sysadmin() # user is admin of org. - org = factories.Organization(name="my-org", - users=[{'name': user['id'], 'capacity': 'admin'}]) + org = factories.Organization( + name="my-org", users=[{"name": user["id"], "capacity": "admin"}] + ) - app = self._get_test_app() # user in env is sysadmin - env = {'REMOTE_USER': sysadmin['name'].encode('ascii')} - response = app.get( - url=url_for('dataset.new'), - extra_environ=env, - ) + env = {"REMOTE_USER": sysadmin["name"].encode("ascii")} + response = app.get(url=url_for("dataset.new"), extra_environ=env) # organization dropdown available in create page. assert 'id="field-organizations"' in response # create dataset - form = response.forms['dataset-edit'] - form['name'] = u'my-dataset' - form['owner_org'] = org['id'] - response = submit_and_follow(app, form, env, 'save') + form = response.forms["dataset-edit"] + form["name"] = u"my-dataset" + form["owner_org"] = org["id"] + response = submit_and_follow(app, form, env, "save") # add a resource to make the pkg active - resource_form = response.forms['resource-edit'] - resource_form['url'] = u'http://example.com/resource' - submit_and_follow(app, resource_form, env, 'save', 'go-metadata') - pkg = model.Package.by_name(u'my-dataset') - assert_equal(pkg.state, 'active') + resource_form = response.forms["resource-edit"] + resource_form["url"] = u"http://example.com/resource" + submit_and_follow(app, resource_form, env, "save", "go-metadata") + pkg = model.Package.by_name(u"my-dataset") + assert pkg.state == "active" # edit package page response - url = url_for('dataset.edit', - id=pkg.id) + url = url_for("dataset.edit", id=pkg.id) pkg_edit_response = app.get(url=url, extra_environ=env) # A field with the correct id is in the response assert 'id="field-organizations"' in pkg_edit_response # The organization id is in the response in a value attribute - assert 'value="{0}"'.format(org['id']) in pkg_edit_response + assert 'value="{0}"'.format(org["id"]) in pkg_edit_response - def test_unauthed_user_creating_dataset(self): - app = self._get_test_app() + @pytest.mark.usefixtures("clean_db") + def test_unauthed_user_creating_dataset(self, app): # provide REMOTE_ADDR to idenfity as remote user, see # ckan.views.identify_user() for details - response = app.post(url=url_for('dataset.new'), - extra_environ={'REMOTE_ADDR': '127.0.0.1'}, - status=403) + response = app.post( + url=url_for("dataset.new"), + extra_environ={"REMOTE_ADDR": "127.0.0.1"}, + status=403, + ) -class TestPackageEdit(helpers.FunctionalTestBase): - def test_organization_admin_can_edit(self): +class TestPackageEdit(object): + @pytest.mark.usefixtures("clean_db") + def test_organization_admin_can_edit(self, app): user = factories.User() organization = factories.Organization( - users=[{'name': user['id'], 'capacity': 'admin'}] + users=[{"name": user["id"], "capacity": "admin"}] ) - dataset = factories.Dataset(owner_org=organization['id']) - app = helpers._get_test_app() - env = {'REMOTE_USER': user['name'].encode('ascii')} + dataset = factories.Dataset(owner_org=organization["id"]) + env = {"REMOTE_USER": user["name"].encode("ascii")} response = app.get( - url_for('dataset.edit', - id=dataset['name']), - extra_environ=env, + url_for("dataset.edit", id=dataset["name"]), extra_environ=env ) - form = response.forms['dataset-edit'] - form['notes'] = u'edited description' - submit_and_follow(app, form, env, 'save') + form = response.forms["dataset-edit"] + form["notes"] = u"edited description" + submit_and_follow(app, form, env, "save") - result = helpers.call_action('package_show', id=dataset['id']) - assert_equal(u'edited description', result['notes']) + result = helpers.call_action("package_show", id=dataset["id"]) + assert u"edited description" == result["notes"] - def test_organization_editor_can_edit(self): + @pytest.mark.usefixtures("clean_db") + def test_organization_editor_can_edit(self, app): user = factories.User() organization = factories.Organization( - users=[{'name': user['id'], 'capacity': 'editor'}] + users=[{"name": user["id"], "capacity": "editor"}] ) - dataset = factories.Dataset(owner_org=organization['id']) - app = helpers._get_test_app() - env = {'REMOTE_USER': user['name'].encode('ascii')} + dataset = factories.Dataset(owner_org=organization["id"]) + env = {"REMOTE_USER": user["name"].encode("ascii")} response = app.get( - url_for('dataset.edit', - id=dataset['name']), - extra_environ=env, + url_for("dataset.edit", id=dataset["name"]), extra_environ=env ) - form = response.forms['dataset-edit'] - form['notes'] = u'edited description' - submit_and_follow(app, form, env, 'save') + form = response.forms["dataset-edit"] + form["notes"] = u"edited description" + submit_and_follow(app, form, env, "save") - result = helpers.call_action('package_show', id=dataset['id']) - assert_equal(u'edited description', result['notes']) + result = helpers.call_action("package_show", id=dataset["id"]) + assert u"edited description" == result["notes"] - def test_organization_member_cannot_edit(self): + @pytest.mark.usefixtures("clean_db") + def test_organization_member_cannot_edit(self, app): user = factories.User() organization = factories.Organization( - users=[{'name': user['id'], 'capacity': 'member'}] + users=[{"name": user["id"], "capacity": "member"}] ) - dataset = factories.Dataset(owner_org=organization['id']) - app = helpers._get_test_app() - env = {'REMOTE_USER': user['name'].encode('ascii')} + dataset = factories.Dataset(owner_org=organization["id"]) + env = {"REMOTE_USER": user["name"].encode("ascii")} response = app.get( - url_for('dataset.edit', - id=dataset['name']), + url_for("dataset.edit", id=dataset["name"]), extra_environ=env, status=403, ) - def test_user_not_in_organization_cannot_edit(self): + @pytest.mark.usefixtures("clean_db") + def test_user_not_in_organization_cannot_edit(self, app): user = factories.User() organization = factories.Organization() - dataset = factories.Dataset(owner_org=organization['id']) - app = helpers._get_test_app() - env = {'REMOTE_USER': user['name'].encode('ascii')} + dataset = factories.Dataset(owner_org=organization["id"]) + env = {"REMOTE_USER": user["name"].encode("ascii")} response = app.get( - url_for('dataset.edit', - id=dataset['name']), + url_for("dataset.edit", id=dataset["name"]), extra_environ=env, status=403, ) - env = {'REMOTE_USER': user['name'].encode('ascii')} + env = {"REMOTE_USER": user["name"].encode("ascii")} response = app.post( - url_for('dataset.edit', - id=dataset['name']), - {'notes': 'edited description'}, + url_for("dataset.edit", id=dataset["name"]), + {"notes": "edited description"}, extra_environ=env, status=403, ) - def test_anonymous_user_cannot_edit(self): + @pytest.mark.usefixtures("clean_db") + def test_anonymous_user_cannot_edit(self, app): organization = factories.Organization() - dataset = factories.Dataset(owner_org=organization['id']) - app = helpers._get_test_app() + dataset = factories.Dataset(owner_org=organization["id"]) response = app.get( - url_for('dataset.edit', - id=dataset['name']), - status=403, + url_for("dataset.edit", id=dataset["name"]), status=403 ) response = app.post( - url_for('dataset.edit', - id=dataset['name']), - {'notes': 'edited description'}, + url_for("dataset.edit", id=dataset["name"]), + {"notes": "edited description"}, status=403, ) - def test_validation_errors_for_dataset_name_appear(self): - '''fill out a bad dataset set name and make sure errors appear''' + @pytest.mark.usefixtures("clean_db") + def test_validation_errors_for_dataset_name_appear(self, app): + """fill out a bad dataset set name and make sure errors appear""" user = factories.User() organization = factories.Organization( - users=[{'name': user['id'], 'capacity': 'admin'}] + users=[{"name": user["id"], "capacity": "admin"}] ) - dataset = factories.Dataset(owner_org=organization['id']) - app = helpers._get_test_app() - env = {'REMOTE_USER': user['name'].encode('ascii')} + dataset = factories.Dataset(owner_org=organization["id"]) + env = {"REMOTE_USER": user["name"].encode("ascii")} response = app.get( - url_for('dataset.edit', - id=dataset['name']), - extra_environ=env, + url_for("dataset.edit", id=dataset["name"]), extra_environ=env ) - form = response.forms['dataset-edit'] - form['name'] = u'this is not a valid name' - response = webtest_submit(form, 'save', status=200, extra_environ=env) - assert_in('The form contains invalid entries', response.body) + form = response.forms["dataset-edit"] + form["name"] = u"this is not a valid name" + response = webtest_submit(form, "save", status=200, extra_environ=env) + assert "The form contains invalid entries" in response.body - assert_in('Name: Must be purely lowercase alphanumeric (ascii) ' - 'characters and these symbols: -_', response.body) + assert ( + "Name: Must be purely lowercase alphanumeric (ascii) " + "characters and these symbols: -_" in response.body + ) - def test_edit_a_dataset_that_does_not_exist_404s(self): + @pytest.mark.usefixtures("clean_db") + def test_edit_a_dataset_that_does_not_exist_404s(self, app): user = factories.User() - app = helpers._get_test_app() - env = {'REMOTE_USER': user['name'].encode('ascii')} + env = {"REMOTE_USER": user["name"].encode("ascii")} response = app.get( - url_for('dataset.edit', - id='does-not-exist'), + url_for("dataset.edit", id="does-not-exist"), extra_environ=env, - expect_errors=True + expect_errors=True, ) - assert_equal(404, response.status_int) + assert 404 == response.status_int -class TestPackageRead(helpers.FunctionalTestBase): - def test_read(self): +class TestPackageRead(object): + @pytest.mark.usefixtures("clean_db") + def test_read(self, app): dataset = factories.Dataset() - app = helpers._get_test_app() - response = app.get(url_for('dataset.read', - id=dataset['name'])) - response.mustcontain('Test Dataset') - response.mustcontain('Just another test dataset') + response = app.get(url_for("dataset.read", id=dataset["name"])) + response.mustcontain("Test Dataset") + response.mustcontain("Just another test dataset") - def test_organization_members_can_read_private_datasets(self): + @pytest.mark.usefixtures("clean_db") + def test_organization_members_can_read_private_datasets(self, app): members = { - 'member': factories.User(), - 'editor': factories.User(), - 'admin': factories.User(), - 'sysadmin': factories.Sysadmin() + "member": factories.User(), + "editor": factories.User(), + "admin": factories.User(), + "sysadmin": factories.Sysadmin(), } organization = factories.Organization( users=[ - {'name': members['member']['id'], 'capacity': 'member'}, - {'name': members['editor']['id'], 'capacity': 'editor'}, - {'name': members['admin']['id'], 'capacity': 'admin'}, + {"name": members["member"]["id"], "capacity": "member"}, + {"name": members["editor"]["id"], "capacity": "editor"}, + {"name": members["admin"]["id"], "capacity": "admin"}, ] ) - dataset = factories.Dataset( - owner_org=organization['id'], - private=True, - ) - app = helpers._get_test_app() - + dataset = factories.Dataset(owner_org=organization["id"], private=True) for user, user_dict in members.items(): response = app.get( - url_for( - 'dataset.read', - id=dataset['name'] - ), + url_for("dataset.read", id=dataset["name"]), extra_environ={ - 'REMOTE_USER': user_dict['name'].encode('ascii'), + "REMOTE_USER": user_dict["name"].encode("ascii") }, ) - assert_in('Test Dataset', response.body) - assert_in('Just another test dataset', response.body) + assert "Test Dataset" in response.body + assert "Just another test dataset" in response.body - def test_anonymous_users_cannot_read_private_datasets(self): + @pytest.mark.usefixtures("clean_db") + def test_anonymous_users_cannot_read_private_datasets(self, app): organization = factories.Organization() - dataset = factories.Dataset( - owner_org=organization['id'], - private=True, - ) - app = helpers._get_test_app() + dataset = factories.Dataset(owner_org=organization["id"], private=True) response = app.get( - url_for('dataset.read', id=dataset['name']), - status=404 + url_for("dataset.read", id=dataset["name"]), status=404 ) - assert_equal(404, response.status_int) + assert 404 == response.status_int - def test_user_not_in_organization_cannot_read_private_datasets(self): + @pytest.mark.usefixtures("clean_db") + def test_user_not_in_organization_cannot_read_private_datasets(self, app): user = factories.User() organization = factories.Organization() - dataset = factories.Dataset( - owner_org=organization['id'], - private=True, - ) - app = helpers._get_test_app() + dataset = factories.Dataset(owner_org=organization["id"], private=True) response = app.get( - url_for('dataset.read', id=dataset['name']), - extra_environ={'REMOTE_USER': user['name'].encode('ascii')}, - status=404 + url_for("dataset.read", id=dataset["name"]), + extra_environ={"REMOTE_USER": user["name"].encode("ascii")}, + status=404, ) - assert_equal(404, response.status_int) + assert 404 == response.status_int - def test_read_rdf(self): - ''' The RDF outputs now live in ckanext-dcat''' + @pytest.mark.usefixtures("clean_db") + def test_read_rdf(self, app): + """ The RDF outputs now live in ckanext-dcat""" dataset1 = factories.Dataset() - offset = url_for('dataset.read', - id=dataset1['name']) + ".rdf" - app = self._get_test_app() + offset = url_for("dataset.read", id=dataset1["name"]) + ".rdf" app.get(offset, status=404) - def test_read_n3(self): - ''' The RDF outputs now live in ckanext-dcat''' + @pytest.mark.usefixtures("clean_db") + def test_read_n3(self, app): + """ The RDF outputs now live in ckanext-dcat""" dataset1 = factories.Dataset() - offset = url_for('dataset.read', - id=dataset1['name']) + ".n3" - app = self._get_test_app() + offset = url_for("dataset.read", id=dataset1["name"]) + ".n3" app.get(offset, status=404) - def test_read_dataset_as_it_used_to_be(self): - dataset = factories.Dataset(title='Original title') - activity = model.Session.query(model.Activity) \ - .filter_by(object_id=dataset['id']) \ + @pytest.mark.usefixtures("clean_db") + def test_read_dataset_as_it_used_to_be(self, app): + dataset = factories.Dataset(title="Original title") + activity = ( + model.Session.query(model.Activity) + .filter_by(object_id=dataset["id"]) .one() - dataset['title'] = 'Changed title' - helpers.call_action('package_update', **dataset) + ) + dataset["title"] = "Changed title" + helpers.call_action("package_update", **dataset) - app = helpers._get_test_app() sysadmin = factories.Sysadmin() - env = {'REMOTE_USER': sysadmin['name'].encode('ascii')} - response = app.get(url_for('dataset.read', - id=dataset['name'], - activity_id=activity.id), - extra_environ=env) - response.mustcontain('Original title') - - def test_read_dataset_as_it_used_to_be_but_is_unmigrated(self): + env = {"REMOTE_USER": sysadmin["name"].encode("ascii")} + response = app.get( + url_for( + "dataset.read", id=dataset["name"], activity_id=activity.id + ), + extra_environ=env, + ) + response.mustcontain("Original title") + + @pytest.mark.usefixtures("clean_db") + def test_read_dataset_as_it_used_to_be_but_is_unmigrated(self, app): # Renders the dataset using the activity detail, when that Activity was # created with an earlier version of CKAN, and it has not been migrated # (with migrate_package_activity.py), which should give a 404 - app = self._get_test_app() user = factories.User() dataset = factories.Dataset(user=user) # delete the modern Activity object that's been automatically created - modern_activity = model.Session.query(model.Activity) \ - .filter_by(object_id=dataset['id']) \ + modern_activity = ( + model.Session.query(model.Activity) + .filter_by(object_id=dataset["id"]) .one() + ) modern_activity.delete() # Create an Activity object as it was in earlier versions of CKAN. # This code is based on: # https://github.com/ckan/ckan/blob/b348bf2fe68db6704ea0a3e22d533ded3d8d4344/ckan/model/package.py#L508 - activity_type = 'changed' + activity_type = "changed" dataset_table_dict = dictization.table_dictize( - model.Package.get(dataset['id']), context={'model': model}) + model.Package.get(dataset["id"]), context={"model": model} + ) activity = model.Activity( - user_id=user['id'], - object_id=dataset['id'], + user_id=user["id"], + object_id=dataset["id"], activity_type="%s package" % activity_type, data={ # "actor": a legacy activity had no "actor" # "package": a legacy activity had just the package table, # rather than the result of package_show - 'package': dataset_table_dict, - } + "package": dataset_table_dict + }, ) model.Session.add(activity) # a legacy activity had a ActivityDetail associated with the Activity @@ -689,1491 +623,1493 @@ def test_read_dataset_as_it_used_to_be_but_is_unmigrated(self): # https://github.com/ckan/ckan/blob/b348bf2fe68db6704ea0a3e22d533ded3d8d4344/ckan/model/package.py#L542 activity_detail = model.ActivityDetail( activity_id=activity.id, - object_id=dataset['id'], + object_id=dataset["id"], object_type=u"Package", activity_type=activity_type, - data={u'package': dataset_table_dict}) + data={u"package": dataset_table_dict}, + ) model.Session.add(activity_detail) model.Session.flush() sysadmin = factories.Sysadmin() - env = {'REMOTE_USER': sysadmin['name'].encode('ascii')} - response = app.get(url_for('dataset.read', - id=dataset['name'], - activity_id=activity.id), - extra_environ=env, - status=404) + env = {"REMOTE_USER": sysadmin["name"].encode("ascii")} + response = app.get( + url_for( + "dataset.read", id=dataset["name"], activity_id=activity.id + ), + extra_environ=env, + status=404, + ) -class TestPackageDelete(helpers.FunctionalTestBase): - def test_owner_delete(self): +class TestPackageDelete(object): + @pytest.mark.usefixtures("clean_db") + def test_owner_delete(self, app): user = factories.User() owner_org = factories.Organization( - users=[{'name': user['id'], 'capacity': 'admin'}] + users=[{"name": user["id"], "capacity": "admin"}] ) - dataset = factories.Dataset(owner_org=owner_org['id']) + dataset = factories.Dataset(owner_org=owner_org["id"]) - app = helpers._get_test_app() - env = {'REMOTE_USER': user['name'].encode('ascii')} + env = {"REMOTE_USER": user["name"].encode("ascii")} response = app.post( - url_for('dataset.delete', id=dataset['name']), - extra_environ=env, + url_for("dataset.delete", id=dataset["name"]), extra_environ=env ) response = response.follow() - assert_equal(200, response.status_int) + assert 200 == response.status_int - deleted = helpers.call_action('package_show', id=dataset['id']) - assert_equal('deleted', deleted['state']) + deleted = helpers.call_action("package_show", id=dataset["id"]) + assert "deleted" == deleted["state"] - def test_delete_on_non_existing_dataset(self): - app = helpers._get_test_app() + @pytest.mark.usefixtures("clean_db") + def test_delete_on_non_existing_dataset(self, app): response = app.post( - url_for('dataset.delete', - id='schrodingersdatset'), + url_for("dataset.delete", id="schrodingersdatset"), expect_errors=True, ) - assert_equal(404, response.status_int) + assert 404 == response.status_int - def test_sysadmin_can_delete_any_dataset(self): + @pytest.mark.usefixtures("clean_db") + def test_sysadmin_can_delete_any_dataset(self, app): owner_org = factories.Organization() - dataset = factories.Dataset(owner_org=owner_org['id']) - app = helpers._get_test_app() + dataset = factories.Dataset(owner_org=owner_org["id"]) user = factories.Sysadmin() - env = {'REMOTE_USER': user['name'].encode('ascii')} + env = {"REMOTE_USER": user["name"].encode("ascii")} response = app.post( - url_for('dataset.delete', id=dataset['name']), - extra_environ=env, + url_for("dataset.delete", id=dataset["name"]), extra_environ=env ) response = response.follow() - assert_equal(200, response.status_int) + assert 200 == response.status_int - deleted = helpers.call_action('package_show', id=dataset['id']) - assert_equal('deleted', deleted['state']) + deleted = helpers.call_action("package_show", id=dataset["id"]) + assert "deleted" == deleted["state"] - def test_anon_user_cannot_delete_owned_dataset(self): + @pytest.mark.usefixtures("clean_db") + def test_anon_user_cannot_delete_owned_dataset(self, app): user = factories.User() owner_org = factories.Organization( - users=[{'name': user['id'], 'capacity': 'admin'}] + users=[{"name": user["id"], "capacity": "admin"}] ) - dataset = factories.Dataset(owner_org=owner_org['id']) + dataset = factories.Dataset(owner_org=owner_org["id"]) - app = helpers._get_test_app() response = app.post( - url_for('dataset.delete', id=dataset['name']), - status=403, + url_for("dataset.delete", id=dataset["name"]), status=403 ) - response.mustcontain('Unauthorized to delete package') + response.mustcontain("Unauthorized to delete package") - deleted = helpers.call_action('package_show', id=dataset['id']) - assert_equal('active', deleted['state']) + deleted = helpers.call_action("package_show", id=dataset["id"]) + assert "active" == deleted["state"] - def test_logged_in_user_cannot_delete_owned_dataset(self): + @pytest.mark.usefixtures("clean_db") + def test_logged_in_user_cannot_delete_owned_dataset(self, app): owner = factories.User() owner_org = factories.Organization( - users=[{'name': owner['id'], 'capacity': 'admin'}] + users=[{"name": owner["id"], "capacity": "admin"}] ) - dataset = factories.Dataset(owner_org=owner_org['id']) + dataset = factories.Dataset(owner_org=owner_org["id"]) - app = helpers._get_test_app() user = factories.User() - env = {'REMOTE_USER': user['name'].encode('ascii')} + env = {"REMOTE_USER": user["name"].encode("ascii")} response = app.post( - url_for('dataset.delete', id=dataset['name']), + url_for("dataset.delete", id=dataset["name"]), extra_environ=env, - expect_errors=True + expect_errors=True, ) - assert_equal(403, response.status_int) - response.mustcontain('Unauthorized to delete package') + assert 403 == response.status_int + response.mustcontain("Unauthorized to delete package") - def test_confirm_cancel_delete(self): - '''Test confirmation of deleting datasets + @pytest.mark.usefixtures("clean_db") + def test_confirm_cancel_delete(self, app): + """Test confirmation of deleting datasets When package_delete is made as a get request, it should return a - 'do you want to delete this dataset? confirmation page''' + 'do you want to delete this dataset? confirmation page""" user = factories.User() owner_org = factories.Organization( - users=[{'name': user['id'], 'capacity': 'admin'}] + users=[{"name": user["id"], "capacity": "admin"}] ) - dataset = factories.Dataset(owner_org=owner_org['id']) + dataset = factories.Dataset(owner_org=owner_org["id"]) - app = helpers._get_test_app() - env = {'REMOTE_USER': user['name'].encode('ascii')} + env = {"REMOTE_USER": user["name"].encode("ascii")} response = app.get( - url_for('dataset.delete', id=dataset['name']), - extra_environ=env, + url_for("dataset.delete", id=dataset["name"]), extra_environ=env ) - assert_equal(200, response.status_int) - message = 'Are you sure you want to delete dataset - {name}?' - response.mustcontain(message.format(name=dataset['title'])) + assert 200 == response.status_int + message = "Are you sure you want to delete dataset - {name}?" + response.mustcontain(message.format(name=dataset["title"])) - form = response.forms['confirm-dataset-delete-form'] - response = form.submit('cancel') - response = helpers.webtest_maybe_follow( - response, - extra_environ=env, - ) - assert_equal(200, response.status_int) + form = response.forms["confirm-dataset-delete-form"] + response = form.submit("cancel") + response = helpers.webtest_maybe_follow(response, extra_environ=env) + assert 200 == response.status_int -class TestResourceNew(helpers.FunctionalTestBase): - def test_manage_dataset_resource_listing_page(self): +class TestResourceNew(object): + @pytest.mark.usefixtures("clean_db") + def test_manage_dataset_resource_listing_page(self, app): user = factories.User() organization = factories.Organization(user=user) - dataset = factories.Dataset(owner_org=organization['id']) - resource = factories.Resource(package_id=dataset['id']) - app = helpers._get_test_app() - env = {'REMOTE_USER': user['name'].encode('ascii')} + dataset = factories.Dataset(owner_org=organization["id"]) + resource = factories.Resource(package_id=dataset["id"]) + env = {"REMOTE_USER": user["name"].encode("ascii")} response = app.get( - url_for( - 'dataset.resources', - id=dataset['name'], - ), - extra_environ=env + url_for("dataset.resources", id=dataset["name"]), extra_environ=env ) - assert_in(resource['name'], response) - assert_in(resource['description'], response) - assert_in(resource['format'], response) + assert resource["name"] in response + assert resource["description"] in response + assert resource["format"] in response - def test_unauth_user_cannot_view_manage_dataset_resource_listing_page(self): + @pytest.mark.usefixtures("clean_db") + def test_unauth_user_cannot_view_manage_dataset_resource_listing_page( + self, app + ): user = factories.User() organization = factories.Organization(user=user) - dataset = factories.Dataset(owner_org=organization['id']) - resource = factories.Resource(package_id=dataset['id']) - app = helpers._get_test_app() - env = {'REMOTE_USER': user['name'].encode('ascii')} + dataset = factories.Dataset(owner_org=organization["id"]) + resource = factories.Resource(package_id=dataset["id"]) + env = {"REMOTE_USER": user["name"].encode("ascii")} response = app.get( - url_for( - 'dataset.resources', - id=dataset['name'], - ), - extra_environ=env + url_for("dataset.resources", id=dataset["name"]), extra_environ=env ) - assert_in(resource['name'], response) - assert_in(resource['description'], response) - assert_in(resource['format'], response) + assert resource["name"] in response + assert resource["description"] in response + assert resource["format"] in response - def test_404_on_manage_dataset_resource_listing_page_that_does_not_exist(self): + @pytest.mark.usefixtures("clean_db") + def test_404_on_manage_dataset_resource_listing_page_that_does_not_exist( + self, app + ): user = factories.User() - app = helpers._get_test_app() - env = {'REMOTE_USER': user['name'].encode('ascii')} + env = {"REMOTE_USER": user["name"].encode("ascii")} response = app.get( - url_for( - 'dataset.resources', - id='does-not-exist' - ), + url_for("dataset.resources", id="does-not-exist"), extra_environ=env, - expect_errors=True + expect_errors=True, ) - assert_equal(404, response.status_int) + assert 404 == response.status_int - def test_add_new_resource_with_link_and_download(self): + @pytest.mark.usefixtures("clean_db") + def test_add_new_resource_with_link_and_download(self, app): user = factories.User() dataset = factories.Dataset() - env = {'REMOTE_USER': user['name'].encode('ascii')} - app = helpers._get_test_app() + env = {"REMOTE_USER": user["name"].encode("ascii")} response = app.get( - url_for( - 'resource.new', - id=dataset['id'], - ), - extra_environ=env + url_for("resource.new", id=dataset["id"]), extra_environ=env ) - form = response.forms['resource-edit'] - form['url'] = u'http://test.com/' - response = submit_and_follow(app, form, env, 'save', - 'go-dataset-complete') + form = response.forms["resource-edit"] + form["url"] = u"http://test.com/" + response = submit_and_follow( + app, form, env, "save", "go-dataset-complete" + ) - result = helpers.call_action('package_show', id=dataset['id']) + result = helpers.call_action("package_show", id=dataset["id"]) response = app.get( url_for( - 'resource.download', - id=dataset['id'], - resource_id=result['resources'][0]['id'] + "resource.download", + id=dataset["id"], + resource_id=result["resources"][0]["id"], ), extra_environ=env, ) - assert_equal(302, response.status_int) + assert 302 == response.status_int - def test_editor_can_add_new_resource(self): + @pytest.mark.usefixtures("clean_db") + def test_editor_can_add_new_resource(self, app): user = factories.User() organization = factories.Organization( - users=[{'name': user['id'], 'capacity': 'editor'}] - ) - dataset = factories.Dataset( - owner_org=organization['id'], + users=[{"name": user["id"], "capacity": "editor"}] ) - env = {'REMOTE_USER': user['name'].encode('ascii')} - app = helpers._get_test_app() + dataset = factories.Dataset(owner_org=organization["id"]) + env = {"REMOTE_USER": user["name"].encode("ascii")} response = app.get( - url_for( - 'resource.new', - id=dataset['id'], - ), - extra_environ=env + url_for("resource.new", id=dataset["id"]), extra_environ=env ) - form = response.forms['resource-edit'] - form['name'] = u'test resource' - form['url'] = u'http://test.com/' - response = submit_and_follow(app, form, env, 'save', - 'go-dataset-complete') + form = response.forms["resource-edit"] + form["name"] = u"test resource" + form["url"] = u"http://test.com/" + response = submit_and_follow( + app, form, env, "save", "go-dataset-complete" + ) - result = helpers.call_action('package_show', id=dataset['id']) - assert_equal(1, len(result['resources'])) - assert_equal(u'test resource', result['resources'][0]['name']) + result = helpers.call_action("package_show", id=dataset["id"]) + assert 1 == len(result["resources"]) + assert u"test resource" == result["resources"][0]["name"] - def test_admin_can_add_new_resource(self): + @pytest.mark.usefixtures("clean_db") + def test_admin_can_add_new_resource(self, app): user = factories.User() organization = factories.Organization( - users=[{'name': user['id'], 'capacity': 'admin'}] + users=[{"name": user["id"], "capacity": "admin"}] ) - dataset = factories.Dataset( - owner_org=organization['id'], - ) - env = {'REMOTE_USER': user['name'].encode('ascii')} - app = helpers._get_test_app() + dataset = factories.Dataset(owner_org=organization["id"]) + env = {"REMOTE_USER": user["name"].encode("ascii")} response = app.get( - url_for( - 'resource.new', - id=dataset['id'], - ), - extra_environ=env + url_for("resource.new", id=dataset["id"]), extra_environ=env ) - form = response.forms['resource-edit'] - form['name'] = u'test resource' - form['url'] = u'http://test.com/' - response = submit_and_follow(app, form, env, 'save', - 'go-dataset-complete') + form = response.forms["resource-edit"] + form["name"] = u"test resource" + form["url"] = u"http://test.com/" + response = submit_and_follow( + app, form, env, "save", "go-dataset-complete" + ) - result = helpers.call_action('package_show', id=dataset['id']) - assert_equal(1, len(result['resources'])) - assert_equal(u'test resource', result['resources'][0]['name']) + result = helpers.call_action("package_show", id=dataset["id"]) + assert 1 == len(result["resources"]) + assert u"test resource" == result["resources"][0]["name"] - def test_member_cannot_add_new_resource(self): + @pytest.mark.usefixtures("clean_db") + def test_member_cannot_add_new_resource(self, app): user = factories.User() organization = factories.Organization( - users=[{'name': user['id'], 'capacity': 'member'}] - ) - dataset = factories.Dataset( - owner_org=organization['id'], + users=[{"name": user["id"], "capacity": "member"}] ) - env = {'REMOTE_USER': user['name'].encode('ascii')} - app = helpers._get_test_app() + dataset = factories.Dataset(owner_org=organization["id"]) + env = {"REMOTE_USER": user["name"].encode("ascii")} response = app.get( - url_for( - 'resource.new', - id=dataset['id'], - ), + url_for("resource.new", id=dataset["id"]), extra_environ=env, status=403, ) response = app.post( - url_for( - 'resource.new', - id=dataset['id'], - ), - {'name': 'test', 'url': 'test', 'save': 'save', 'id': ''}, + url_for("resource.new", id=dataset["id"]), + {"name": "test", "url": "test", "save": "save", "id": ""}, extra_environ=env, status=403, ) - def test_non_organization_users_cannot_add_new_resource(self): - '''on an owned dataset''' + @pytest.mark.usefixtures("clean_db") + def test_non_organization_users_cannot_add_new_resource(self, app): + """on an owned dataset""" user = factories.User() organization = factories.Organization() - dataset = factories.Dataset( - owner_org=organization['id'], - ) - env = {'REMOTE_USER': user['name'].encode('ascii')} - app = helpers._get_test_app() + dataset = factories.Dataset(owner_org=organization["id"]) + env = {"REMOTE_USER": user["name"].encode("ascii")} response = app.get( - url_for( - 'resource.new', - id=dataset['id'], - ), + url_for("resource.new", id=dataset["id"]), extra_environ=env, status=403, ) response = app.post( - url_for( - 'resource.new', - id=dataset['id'], - ), - {'name': 'test', 'url': 'test', 'save': 'save', 'id': ''}, + url_for("resource.new", id=dataset["id"]), + {"name": "test", "url": "test", "save": "save", "id": ""}, extra_environ=env, status=403, ) - def test_anonymous_users_cannot_add_new_resource(self): + @pytest.mark.usefixtures("clean_db") + def test_anonymous_users_cannot_add_new_resource(self, app): organization = factories.Organization() - dataset = factories.Dataset( - owner_org=organization['id'], - ) - app = helpers._get_test_app() + dataset = factories.Dataset(owner_org=organization["id"]) response = app.get( - url_for( - 'resource.new', - id=dataset['id'], - ), - status=403, + url_for("resource.new", id=dataset["id"]), status=403 ) response = app.post( - url_for( - 'resource.new', - id=dataset['id'], - ), - {'name': 'test', 'url': 'test', 'save': 'save', 'id': ''}, + url_for("resource.new", id=dataset["id"]), + {"name": "test", "url": "test", "save": "save", "id": ""}, status=403, ) - def test_anonymous_users_cannot_edit_resource(self): + @pytest.mark.usefixtures("clean_db") + def test_anonymous_users_cannot_edit_resource(self, app): organization = factories.Organization() - dataset = factories.Dataset( - owner_org=organization['id'], - ) - resource = factories.Resource(package_id=dataset['id']) - app = helpers._get_test_app() + dataset = factories.Dataset(owner_org=organization["id"]) + resource = factories.Resource(package_id=dataset["id"]) with app.flask_app.test_request_context(): response = app.get( url_for( - 'resource.edit', - id=dataset['id'], - resource_id=resource['id'], + "resource.edit", + id=dataset["id"], + resource_id=resource["id"], ), status=403, ) response = app.post( url_for( - 'resource.edit', - id=dataset['id'], - resource_id=resource['id'], + "resource.edit", + id=dataset["id"], + resource_id=resource["id"], ), - {'name': 'test', 'url': 'test', 'save': 'save', 'id': ''}, + {"name": "test", "url": "test", "save": "save", "id": ""}, status=403, ) -class TestResourceView(helpers.FunctionalTestBase): - @classmethod - def _apply_config_changes(cls, cfg): - cfg['ckan.plugins'] = 'image_view' - - @classmethod - def setup_class(cls): - super(cls, cls).setup_class() - - if not p.plugin_loaded('image_view'): - p.load('image_view') - helpers.reset_db() - - @classmethod - def teardown_class(cls): - p.unload('image_view') - - def test_resource_view_create(self): +class TestResourceView(object): + @pytest.mark.ckan_config("ckan.plugins", "image_view") + @pytest.mark.usefixtures("clean_db", "with_plugins") + def test_resource_view_create(self, app): user = factories.User() - env = {'REMOTE_USER': user['name'].encode('ascii')} + env = {"REMOTE_USER": user["name"].encode("ascii")} owner_org = factories.Organization( - users=[{'name': user['id'], 'capacity': 'admin'}] + users=[{"name": user["id"], "capacity": "admin"}] ) - dataset = factories.Dataset(owner_org=owner_org['id']) - resource = factories.Resource(package_id=dataset['id']) + dataset = factories.Dataset(owner_org=owner_org["id"]) + resource = factories.Resource(package_id=dataset["id"]) - url = url_for('resource.edit_view', - id=resource['package_id'], - resource_id=resource['id'], - view_type='image_view') + url = url_for( + "resource.edit_view", + id=resource["package_id"], + resource_id=resource["id"], + view_type="image_view", + ) - app = self._get_test_app() response = app.post( - url, {'title': 'Test Image View'}, extra_environ=env + url, {"title": "Test Image View"}, extra_environ=env ).follow(extra_environ=env) - response.mustcontain('Test Image View') + response.mustcontain("Test Image View") - def test_resource_view_edit(self): + @pytest.mark.ckan_config("ckan.plugins", "image_view") + @pytest.mark.usefixtures("clean_db", "with_plugins") + def test_resource_view_edit(self, app): user = factories.User() - env = {'REMOTE_USER': user['name'].encode('ascii')} + env = {"REMOTE_USER": user["name"].encode("ascii")} owner_org = factories.Organization( - users=[{'name': user['id'], 'capacity': 'admin'}] + users=[{"name": user["id"], "capacity": "admin"}] ) - dataset = factories.Dataset(owner_org=owner_org['id']) - resource = factories.Resource(package_id=dataset['id']) + dataset = factories.Dataset(owner_org=owner_org["id"]) + resource = factories.Resource(package_id=dataset["id"]) - resource_view = factories.ResourceView(resource_id=resource['id']) - url = url_for('resource.edit_view', - id=resource_view['package_id'], - resource_id=resource_view['resource_id'], - view_id=resource_view['id']) + resource_view = factories.ResourceView(resource_id=resource["id"]) + url = url_for( + "resource.edit_view", + id=resource_view["package_id"], + resource_id=resource_view["resource_id"], + view_id=resource_view["id"], + ) - app = self._get_test_app() response = app.post( - url, {'title': 'Updated RV Title'}, extra_environ=env + url, {"title": "Updated RV Title"}, extra_environ=env ).follow(extra_environ=env) - response.mustcontain('Updated RV Title') + response.mustcontain("Updated RV Title") - def test_resource_view_delete(self): + @pytest.mark.ckan_config("ckan.plugins", "image_view") + @pytest.mark.usefixtures("clean_db", "with_plugins") + def test_resource_view_delete(self, app): user = factories.User() - env = {'REMOTE_USER': user['name'].encode('ascii')} + env = {"REMOTE_USER": user["name"].encode("ascii")} owner_org = factories.Organization( - users=[{'name': user['id'], 'capacity': 'admin'}] + users=[{"name": user["id"], "capacity": "admin"}] ) - dataset = factories.Dataset(owner_org=owner_org['id']) - resource = factories.Resource(package_id=dataset['id']) + dataset = factories.Dataset(owner_org=owner_org["id"]) + resource = factories.Resource(package_id=dataset["id"]) - resource_view = factories.ResourceView(resource_id=resource['id']) - url = url_for('resource.edit_view', - id=resource_view['package_id'], - resource_id=resource_view['resource_id'], - view_id=resource_view['id']) + resource_view = factories.ResourceView(resource_id=resource["id"]) + url = url_for( + "resource.edit_view", + id=resource_view["package_id"], + resource_id=resource_view["resource_id"], + view_id=resource_view["id"], + ) - app = self._get_test_app() response = app.post( - url, {'delete': 'Delete'}, extra_environ=env + url, {"delete": "Delete"}, extra_environ=env ).follow(extra_environ=env) - response.mustcontain('This resource has no views') + response.mustcontain("This resource has no views") - def test_existent_resource_view_page_returns_ok_code(self): + @pytest.mark.ckan_config("ckan.plugins", "image_view") + @pytest.mark.usefixtures("clean_db", "with_plugins") + def test_existent_resource_view_page_returns_ok_code(self, app): resource_view = factories.ResourceView() - url = url_for('resource.read', - id=resource_view['package_id'], - resource_id=resource_view['resource_id'], - view_id=resource_view['id']) + url = url_for( + "resource.read", + id=resource_view["package_id"], + resource_id=resource_view["resource_id"], + view_id=resource_view["id"], + ) - app = self._get_test_app() app.get(url, status=200) - def test_inexistent_resource_view_page_returns_not_found_code(self): + @pytest.mark.ckan_config("ckan.plugins", "image_view") + @pytest.mark.usefixtures("clean_db", "with_plugins") + def test_inexistent_resource_view_page_returns_not_found_code(self, app): resource_view = factories.ResourceView() - url = url_for('resource.read', - id=resource_view['package_id'], - resource_id=resource_view['resource_id'], - view_id='inexistent-view-id') + url = url_for( + "resource.read", + id=resource_view["package_id"], + resource_id=resource_view["resource_id"], + view_id="inexistent-view-id", + ) - app = self._get_test_app() app.get(url, status=404) - def test_resource_view_description_is_rendered_as_markdown(self): + @pytest.mark.ckan_config("ckan.plugins", "image_view") + @pytest.mark.usefixtures("clean_db", "with_plugins") + def test_resource_view_description_is_rendered_as_markdown(self, app): resource_view = factories.ResourceView(description="Some **Markdown**") - url = url_for('resource.read', - id=resource_view['package_id'], - resource_id=resource_view['resource_id'], - view_id=resource_view['id']) - app = self._get_test_app() + url = url_for( + "resource.read", + id=resource_view["package_id"], + resource_id=resource_view["resource_id"], + view_id=resource_view["id"], + ) response = app.get(url) - response.mustcontain('Some Markdown') + response.mustcontain("Some Markdown") -class TestResourceRead(helpers.FunctionalTestBase): - def test_existing_resource_with_not_associated_dataset(self): +class TestResourceRead(object): + @pytest.mark.usefixtures("clean_db") + def test_existing_resource_with_not_associated_dataset(self, app): dataset = factories.Dataset() resource = factories.Resource() - url = url_for('resource.read', - id=dataset['id'], - resource_id=resource['id']) + url = url_for( + "resource.read", id=dataset["id"], resource_id=resource["id"] + ) - app = self._get_test_app() app.get(url, status=404) - def test_resource_read_logged_in_user(self): - ''' + @pytest.mark.usefixtures("clean_db") + def test_resource_read_logged_in_user(self, app): + """ A logged-in user can view resource page. - ''' + """ user = factories.User() - env = {'REMOTE_USER': user['name'].encode('ascii')} + env = {"REMOTE_USER": user["name"].encode("ascii")} dataset = factories.Dataset() - resource = factories.Resource(package_id=dataset['id']) + resource = factories.Resource(package_id=dataset["id"]) - url = url_for('resource.read', - id=dataset['id'], - resource_id=resource['id']) + url = url_for( + "resource.read", id=dataset["id"], resource_id=resource["id"] + ) - app = self._get_test_app() app.get(url, status=200, extra_environ=env) - def test_resource_read_anon_user(self): - ''' + @pytest.mark.usefixtures("clean_db") + def test_resource_read_anon_user(self, app): + """ An anon user can view resource page. - ''' + """ dataset = factories.Dataset() - resource = factories.Resource(package_id=dataset['id']) + resource = factories.Resource(package_id=dataset["id"]) - url = url_for('resource.read', - id=dataset['id'], - resource_id=resource['id']) + url = url_for( + "resource.read", id=dataset["id"], resource_id=resource["id"] + ) - app = self._get_test_app() app.get(url, status=200) - def test_resource_read_sysadmin(self): - ''' + @pytest.mark.usefixtures("clean_db") + def test_resource_read_sysadmin(self, app): + """ A sysadmin can view resource page. - ''' + """ sysadmin = factories.Sysadmin() - env = {'REMOTE_USER': sysadmin['name'].encode('ascii')} + env = {"REMOTE_USER": sysadmin["name"].encode("ascii")} dataset = factories.Dataset() - resource = factories.Resource(package_id=dataset['id']) + resource = factories.Resource(package_id=dataset["id"]) - url = url_for('resource.read', - id=dataset['id'], - resource_id=resource['id']) + url = url_for( + "resource.read", id=dataset["id"], resource_id=resource["id"] + ) - app = self._get_test_app() app.get(url, status=200, extra_environ=env) - def test_user_not_in_organization_cannot_read_private_dataset(self): + @pytest.mark.usefixtures("clean_db") + def test_user_not_in_organization_cannot_read_private_dataset(self, app): user = factories.User() - env = {'REMOTE_USER': user['name'].encode('ascii')} + env = {"REMOTE_USER": user["name"].encode("ascii")} organization = factories.Organization() - dataset = factories.Dataset( - owner_org=organization['id'], - private=True, - ) - resource = factories.Resource(package_id=dataset['id']) + dataset = factories.Dataset(owner_org=organization["id"], private=True) + resource = factories.Resource(package_id=dataset["id"]) - url = url_for('resource.read', - id=dataset['id'], - resource_id=resource['id']) + url = url_for( + "resource.read", id=dataset["id"], resource_id=resource["id"] + ) - app = self._get_test_app() - response = app.get(url, - status=404, - extra_environ=env) + response = app.get(url, status=404, extra_environ=env) - def test_organization_members_can_read_resources_in_private_datasets(self): + @pytest.mark.usefixtures("clean_db") + def test_organization_members_can_read_resources_in_private_datasets( + self, app + ): members = { - 'member': factories.User(), - 'editor': factories.User(), - 'admin': factories.User(), - 'sysadmin': factories.Sysadmin() + "member": factories.User(), + "editor": factories.User(), + "admin": factories.User(), + "sysadmin": factories.Sysadmin(), } organization = factories.Organization( users=[ - {'name': members['member']['id'], 'capacity': 'member'}, - {'name': members['editor']['id'], 'capacity': 'editor'}, - {'name': members['admin']['id'], 'capacity': 'admin'}, + {"name": members["member"]["id"], "capacity": "member"}, + {"name": members["editor"]["id"], "capacity": "editor"}, + {"name": members["admin"]["id"], "capacity": "admin"}, ] ) - dataset = factories.Dataset( - owner_org=organization['id'], - private=True, - ) - resource = factories.Resource(package_id=dataset['id']) - - app = helpers._get_test_app() + dataset = factories.Dataset(owner_org=organization["id"], private=True) + resource = factories.Resource(package_id=dataset["id"]) for user, user_dict in members.items(): response = app.get( url_for( - 'resource.read', - id=dataset['name'], - resource_id=resource['id'], + "resource.read", + id=dataset["name"], + resource_id=resource["id"], ), extra_environ={ - 'REMOTE_USER': user_dict['name'].encode('ascii'), + "REMOTE_USER": user_dict["name"].encode("ascii") }, ) - assert_in('Just another test resource', response.body) + assert "Just another test resource" in response.body - def test_anonymous_users_cannot_read_private_datasets(self): + @pytest.mark.usefixtures("clean_db") + def test_anonymous_users_cannot_read_private_datasets(self, app): organization = factories.Organization() - dataset = factories.Dataset( - owner_org=organization['id'], - private=True, - ) - app = helpers._get_test_app() + dataset = factories.Dataset(owner_org=organization["id"], private=True) response = app.get( - url_for('dataset.read', id=dataset['name']), - status=404 + url_for("dataset.read", id=dataset["name"]), status=404 ) - assert_equal(404, response.status_int) + assert 404 == response.status_int -class TestResourceDelete(helpers.FunctionalTestBase): - def test_dataset_owners_can_delete_resources(self): +class TestResourceDelete(object): + @pytest.mark.usefixtures("clean_db") + def test_dataset_owners_can_delete_resources(self, app): user = factories.User() owner_org = factories.Organization( - users=[{'name': user['id'], 'capacity': 'admin'}] + users=[{"name": user["id"], "capacity": "admin"}] ) - dataset = factories.Dataset(owner_org=owner_org['id']) - resource = factories.Resource(package_id=dataset['id']) - app = helpers._get_test_app() - env = {'REMOTE_USER': user['name'].encode('ascii')} + dataset = factories.Dataset(owner_org=owner_org["id"]) + resource = factories.Resource(package_id=dataset["id"]) + env = {"REMOTE_USER": user["name"].encode("ascii")} response = app.post( - url_for('resource.delete', - id=dataset['name'], resource_id=resource['id']), + url_for( + "resource.delete", + id=dataset["name"], + resource_id=resource["id"], + ), extra_environ=env, ) response = response.follow() - assert_equal(200, response.status_int) - response.mustcontain('This dataset has no data') + assert 200 == response.status_int + response.mustcontain("This dataset has no data") - assert_raises(p.toolkit.ObjectNotFound, helpers.call_action, - 'resource_show', id=resource['id']) + with pytest.raises(p.toolkit.ObjectNotFound): + helpers.call_action("resource_show", id=resource["id"]) - def test_deleting_non_existing_resource_404s(self): + @pytest.mark.usefixtures("clean_db") + def test_deleting_non_existing_resource_404s(self, app): user = factories.User() owner_org = factories.Organization( - users=[{'name': user['id'], 'capacity': 'admin'}] + users=[{"name": user["id"], "capacity": "admin"}] ) - dataset = factories.Dataset(owner_org=owner_org['id']) - env = {'REMOTE_USER': user['name'].encode('ascii')} - app = helpers._get_test_app() + dataset = factories.Dataset(owner_org=owner_org["id"]) + env = {"REMOTE_USER": user["name"].encode("ascii")} response = app.post( - url_for('resource.delete', - id=dataset['name'], resource_id='doesnotexist'), + url_for( + "resource.delete", + id=dataset["name"], + resource_id="doesnotexist", + ), extra_environ=env, - expect_errors=True + expect_errors=True, ) - assert_equal(404, response.status_int) + assert 404 == response.status_int - def test_anon_users_cannot_delete_owned_resources(self): + @pytest.mark.usefixtures("clean_db") + def test_anon_users_cannot_delete_owned_resources(self, app): user = factories.User() owner_org = factories.Organization( - users=[{'name': user['id'], 'capacity': 'admin'}] + users=[{"name": user["id"], "capacity": "admin"}] ) - dataset = factories.Dataset(owner_org=owner_org['id']) - resource = factories.Resource(package_id=dataset['id']) + dataset = factories.Dataset(owner_org=owner_org["id"]) + resource = factories.Resource(package_id=dataset["id"]) - app = helpers._get_test_app() response = app.post( - url_for('resource.delete', - id=dataset['name'], resource_id=resource['id']), + url_for( + "resource.delete", + id=dataset["name"], + resource_id=resource["id"], + ), status=403, ) - response.mustcontain('Unauthorized to delete package') + response.mustcontain("Unauthorized to delete package") - def test_logged_in_users_cannot_delete_resources_they_do_not_own(self): + @pytest.mark.usefixtures("clean_db") + def test_logged_in_users_cannot_delete_resources_they_do_not_own( + self, app + ): # setup our dataset owner = factories.User() owner_org = factories.Organization( - users=[{'name': owner['id'], 'capacity': 'admin'}] + users=[{"name": owner["id"], "capacity": "admin"}] ) - dataset = factories.Dataset(owner_org=owner_org['id']) - resource = factories.Resource(package_id=dataset['id']) + dataset = factories.Dataset(owner_org=owner_org["id"]) + resource = factories.Resource(package_id=dataset["id"]) # access as another user user = factories.User() - env = {'REMOTE_USER': user['name'].encode('ascii')} - app = helpers._get_test_app() + env = {"REMOTE_USER": user["name"].encode("ascii")} response = app.post( - url_for('resource.delete', - id=dataset['name'], resource_id=resource['id']), + url_for( + "resource.delete", + id=dataset["name"], + resource_id=resource["id"], + ), extra_environ=env, - expect_errors=True + expect_errors=True, ) - assert_equal(403, response.status_int) - response.mustcontain('Unauthorized to delete package') + assert 403 == response.status_int + response.mustcontain("Unauthorized to delete package") - def test_sysadmins_can_delete_any_resource(self): + @pytest.mark.usefixtures("clean_db") + def test_sysadmins_can_delete_any_resource(self, app): owner_org = factories.Organization() - dataset = factories.Dataset(owner_org=owner_org['id']) - resource = factories.Resource(package_id=dataset['id']) + dataset = factories.Dataset(owner_org=owner_org["id"]) + resource = factories.Resource(package_id=dataset["id"]) sysadmin = factories.Sysadmin() - app = helpers._get_test_app() - env = {'REMOTE_USER': sysadmin['name'].encode('ascii')} + env = {"REMOTE_USER": sysadmin["name"].encode("ascii")} response = app.post( - url_for('resource.delete', - id=dataset['name'], resource_id=resource['id']), + url_for( + "resource.delete", + id=dataset["name"], + resource_id=resource["id"], + ), extra_environ=env, ) response = response.follow() - assert_equal(200, response.status_int) - response.mustcontain('This dataset has no data') + assert 200 == response.status_int + response.mustcontain("This dataset has no data") - assert_raises(p.toolkit.ObjectNotFound, helpers.call_action, - 'resource_show', id=resource['id']) + with pytest.raises(p.toolkit.ObjectNotFound): + helpers.call_action("resource_show", id=resource["id"]) - def test_confirm_and_cancel_deleting_a_resource(self): - '''Test confirmation of deleting resources + @pytest.mark.usefixtures("clean_db") + def test_confirm_and_cancel_deleting_a_resource(self, app): + """Test confirmation of deleting resources When resource_delete is made as a get request, it should return a - 'do you want to delete this reource? confirmation page''' + 'do you want to delete this reource? confirmation page""" user = factories.User() owner_org = factories.Organization( - users=[{'name': user['id'], 'capacity': 'admin'}] + users=[{"name": user["id"], "capacity": "admin"}] ) - dataset = factories.Dataset(owner_org=owner_org['id']) - resource = factories.Resource(package_id=dataset['id']) - app = helpers._get_test_app() - env = {'REMOTE_USER': user['name'].encode('ascii')} + dataset = factories.Dataset(owner_org=owner_org["id"]) + resource = factories.Resource(package_id=dataset["id"]) + env = {"REMOTE_USER": user["name"].encode("ascii")} response = app.get( - url_for('resource.delete', - id=dataset['name'], resource_id=resource['id']), + url_for( + "resource.delete", + id=dataset["name"], + resource_id=resource["id"], + ), extra_environ=env, ) - assert_equal(200, response.status_int) - message = 'Are you sure you want to delete resource - {name}?' - response.mustcontain(message.format(name=resource['name'])) + assert 200 == response.status_int + message = "Are you sure you want to delete resource - {name}?" + response.mustcontain(message.format(name=resource["name"])) # cancelling sends us back to the resource edit page - form = response.forms['confirm-resource-delete-form'] - response = form.submit('cancel') + form = response.forms["confirm-resource-delete-form"] + response = form.submit("cancel") response = response.follow(extra_environ=env) - assert_equal(200, response.status_int) + assert 200 == response.status_int -class TestSearch(helpers.FunctionalTestBase): - def test_search_basic(self): +class TestSearch(object): + @pytest.mark.usefixtures("clean_db", "clean_index") + def test_search_basic(self, app): dataset1 = factories.Dataset() - offset = url_for('dataset.search') - app = self._get_test_app() + offset = url_for("dataset.search") page = app.get(offset) - assert dataset1['name'] in page.body.decode('utf8') + assert dataset1["name"] in page.body.decode("utf8") - def test_search_language_toggle(self): + @pytest.mark.usefixtures("clean_db", "clean_index") + def test_search_language_toggle(self, app): dataset1 = factories.Dataset() - app = self._get_test_app() with app.flask_app.test_request_context(): - offset = url_for( - 'dataset.search', q=dataset1['name']) + offset = url_for("dataset.search", q=dataset1["name"]) page = app.get(offset) - assert dataset1['name'] in page.body.decode('utf8') - assert ('q=' + dataset1['name']) in page.body.decode('utf8') + assert dataset1["name"] in page.body.decode("utf8") + assert ("q=" + dataset1["name"]) in page.body.decode("utf8") - def test_search_sort_by_blank(self): + @pytest.mark.usefixtures("clean_db", "clean_index") + def test_search_sort_by_blank(self, app): factories.Dataset() # ?sort has caused an exception in the past - offset = url_for('dataset.search') + '?sort' - app = self._get_test_app() + offset = url_for("dataset.search") + "?sort" app.get(offset) - def test_search_sort_by_bad(self): + @pytest.mark.usefixtures("clean_db", "clean_index") + def test_search_sort_by_bad(self, app): factories.Dataset() # bad spiders try all sorts of invalid values for sort. They should get # a 400 error with specific error message. No need to alert the # administrator. - offset = url_for('dataset.search') + \ - '?sort=gvgyr_fgevat+nfp' - app = self._get_test_app() + offset = url_for("dataset.search") + "?sort=gvgyr_fgevat+nfp" response = app.get(offset, status=[200, 400]) if response.status == 200: import sys + sys.stdout.write(response.body) - raise Exception("Solr returned an unknown error message. " - "Please check the error handling " - "in ckan/lib/search/query.py:run") + raise Exception( + "Solr returned an unknown error message. " + "Please check the error handling " + "in ckan/lib/search/query.py:run" + ) - def test_search_solr_syntax_error(self): + @pytest.mark.usefixtures("clean_db", "clean_index") + def test_search_solr_syntax_error(self, app): factories.Dataset() # SOLR raises SyntaxError when it can't parse q (or other fields?). # Whilst this could be due to a bad user input, it could also be # because CKAN mangled things somehow and therefore we flag it up to # the administrator and give a meaningless error, just in case - offset = url_for('dataset.search') + \ - '?q=--included' - app = self._get_test_app() + offset = url_for("dataset.search") + "?q=--included" search_response = app.get(offset) search_response_html = BeautifulSoup(search_response.body) - err_msg = search_response_html.select('#search-error') - err_msg = ''.join([n.text for n in err_msg]) - assert_in('error while searching', err_msg) + err_msg = search_response_html.select("#search-error") + err_msg = "".join([n.text for n in err_msg]) + assert "error while searching" in err_msg - def test_search_plugin_hooks(self): - with p.use_plugin('test_package_controller_plugin') as plugin: + @pytest.mark.usefixtures("clean_db", "clean_index") + def test_search_plugin_hooks(self, app): + with p.use_plugin("test_package_controller_plugin") as plugin: - offset = url_for('dataset.search') - app = self._get_test_app() + offset = url_for("dataset.search") app.get(offset) # get redirected ... - assert plugin.calls['before_search'] == 1, plugin.calls - assert plugin.calls['after_search'] == 1, plugin.calls + assert plugin.calls["before_search"] == 1, plugin.calls + assert plugin.calls["after_search"] == 1, plugin.calls + + @pytest.mark.usefixtures("clean_db", "clean_index") + def test_search_page_request(self, app): + """Requesting package search page returns list of datasets.""" - def test_search_page_request(self): - '''Requesting package search page returns list of datasets.''' - app = self._get_test_app() - factories.Dataset(name="dataset-one", title='Dataset One') - factories.Dataset(name="dataset-two", title='Dataset Two') - factories.Dataset(name="dataset-three", title='Dataset Three') + factories.Dataset(name="dataset-one", title="Dataset One") + factories.Dataset(name="dataset-two", title="Dataset Two") + factories.Dataset(name="dataset-three", title="Dataset Three") - search_url = url_for('dataset.search') + search_url = url_for("dataset.search") search_response = app.get(search_url) - assert_true('3 datasets found' in search_response) + assert "3 datasets found" in search_response search_response_html = BeautifulSoup(search_response.body) - ds_titles = search_response_html.select('.dataset-list ' - '.dataset-item ' - '.dataset-heading a') + ds_titles = search_response_html.select( + ".dataset-list " ".dataset-item " ".dataset-heading a" + ) ds_titles = [n.string for n in ds_titles] - assert_equal(len(ds_titles), 3) - assert_true('Dataset One' in ds_titles) - assert_true('Dataset Two' in ds_titles) - assert_true('Dataset Three' in ds_titles) + assert len(ds_titles) == 3 + assert "Dataset One" in ds_titles + assert "Dataset Two" in ds_titles + assert "Dataset Three" in ds_titles + + @pytest.mark.usefixtures("clean_db", "clean_index") + def test_search_page_results(self, app): + """Searching for datasets returns expected results.""" - def test_search_page_results(self): - '''Searching for datasets returns expected results.''' - app = self._get_test_app() - factories.Dataset(name="dataset-one", title='Dataset One') - factories.Dataset(name="dataset-two", title='Dataset Two') - factories.Dataset(name="dataset-three", title='Dataset Three') + factories.Dataset(name="dataset-one", title="Dataset One") + factories.Dataset(name="dataset-two", title="Dataset Two") + factories.Dataset(name="dataset-three", title="Dataset Three") - search_url = url_for('dataset.search') + search_url = url_for("dataset.search") search_response = app.get(search_url) - search_form = search_response.forms['dataset-search-form'] - search_form['q'] = 'One' + search_form = search_response.forms["dataset-search-form"] + search_form["q"] = "One" search_results = webtest_submit(search_form) - assert_true('1 dataset found' in search_results) + assert "1 dataset found" in search_results search_response_html = BeautifulSoup(search_results.body) - ds_titles = search_response_html.select('.dataset-list ' - '.dataset-item ' - '.dataset-heading a') + ds_titles = search_response_html.select( + ".dataset-list " ".dataset-item " ".dataset-heading a" + ) ds_titles = [n.string for n in ds_titles] - assert_equal(len(ds_titles), 1) - assert_true('Dataset One' in ds_titles) + assert len(ds_titles) == 1 + assert "Dataset One" in ds_titles + + @pytest.mark.usefixtures("clean_db", "clean_index") + def test_search_page_no_results(self, app): + """Search with non-returning phrase returns no results.""" - def test_search_page_no_results(self): - '''Search with non-returning phrase returns no results.''' - app = self._get_test_app() - factories.Dataset(name="dataset-one", title='Dataset One') - factories.Dataset(name="dataset-two", title='Dataset Two') - factories.Dataset(name="dataset-three", title='Dataset Three') + factories.Dataset(name="dataset-one", title="Dataset One") + factories.Dataset(name="dataset-two", title="Dataset Two") + factories.Dataset(name="dataset-three", title="Dataset Three") - search_url = url_for('dataset.search') + search_url = url_for("dataset.search") search_response = app.get(search_url) - search_form = search_response.forms['dataset-search-form'] - search_form['q'] = 'Nout' + search_form = search_response.forms["dataset-search-form"] + search_form["q"] = "Nout" search_results = webtest_submit(search_form) - assert_true('No datasets found for "Nout"' in search_results) + assert 'No datasets found for "Nout"' in search_results search_response_html = BeautifulSoup(search_results.body) - ds_titles = search_response_html.select('.dataset-list ' - '.dataset-item ' - '.dataset-heading a') + ds_titles = search_response_html.select( + ".dataset-list " ".dataset-item " ".dataset-heading a" + ) ds_titles = [n.string for n in ds_titles] - assert_equal(len(ds_titles), 0) + assert len(ds_titles) == 0 + + @pytest.mark.usefixtures("clean_db", "clean_index") + def test_search_page_results_tag(self, app): + """Searching with a tag returns expected results.""" - def test_search_page_results_tag(self): - '''Searching with a tag returns expected results.''' - app = self._get_test_app() - factories.Dataset(name="dataset-one", title='Dataset One', - tags=[{'name': 'my-tag'}]) - factories.Dataset(name="dataset-two", title='Dataset Two') - factories.Dataset(name="dataset-three", title='Dataset Three') + factories.Dataset( + name="dataset-one", title="Dataset One", tags=[{"name": "my-tag"}] + ) + factories.Dataset(name="dataset-two", title="Dataset Two") + factories.Dataset(name="dataset-three", title="Dataset Three") - search_url = url_for('dataset.search') + search_url = url_for("dataset.search") search_response = app.get(search_url) - assert_true('/dataset/?tags=my-tag' in search_response) + assert "/dataset/?tags=my-tag" in search_response - tag_search_response = app.get('/dataset?tags=my-tag') + tag_search_response = app.get("/dataset?tags=my-tag") - assert_true('1 dataset found' in tag_search_response) + assert "1 dataset found" in tag_search_response search_response_html = BeautifulSoup(tag_search_response.body) - ds_titles = search_response_html.select('.dataset-list ' - '.dataset-item ' - '.dataset-heading a') + ds_titles = search_response_html.select( + ".dataset-list " ".dataset-item " ".dataset-heading a" + ) ds_titles = [n.string for n in ds_titles] - assert_equal(len(ds_titles), 1) - assert_true('Dataset One' in ds_titles) + assert len(ds_titles) == 1 + assert "Dataset One" in ds_titles - def test_search_page_results_tags(self): - '''Searching with a tag returns expected results with multiple tags''' + @pytest.mark.usefixtures("clean_db", "clean_index") + def test_search_page_results_tags(self, app): + """Searching with a tag returns expected results with multiple tags""" - app = self._get_test_app() - factories.Dataset(name="dataset-one", title='Dataset One', - tags=[ - {'name': 'my-tag-1'}, - {'name': 'my-tag-2'}, - {'name': 'my-tag-3'}, - ]) - factories.Dataset(name="dataset-two", title='Dataset Two') - factories.Dataset(name="dataset-three", title='Dataset Three') + factories.Dataset( + name="dataset-one", + title="Dataset One", + tags=[ + {"name": "my-tag-1"}, + {"name": "my-tag-2"}, + {"name": "my-tag-3"}, + ], + ) + factories.Dataset(name="dataset-two", title="Dataset Two") + factories.Dataset(name="dataset-three", title="Dataset Three") - params = '/dataset/?tags=my-tag-1&tags=my-tag-2&tags=my-tag-3' + params = "/dataset/?tags=my-tag-1&tags=my-tag-2&tags=my-tag-3" tag_search_response = app.get(params) - assert_true('1 dataset found' in tag_search_response) + assert "1 dataset found" in tag_search_response search_response_html = BeautifulSoup(tag_search_response.body) - ds_titles = search_response_html.select('.filtered') - assert_equal(len(ds_titles), 3) + ds_titles = search_response_html.select(".filtered") + assert len(ds_titles) == 3 - def test_search_page_results_private(self): - '''Private datasets don't show up in dataset search results.''' - app = self._get_test_app() + @pytest.mark.usefixtures("clean_db", "clean_index") + def test_search_page_results_private(self, app): + """Private datasets don't show up in dataset search results.""" org = factories.Organization() - factories.Dataset(name="dataset-one", title='Dataset One', - owner_org=org['id'], private=True) - factories.Dataset(name="dataset-two", title='Dataset Two') - factories.Dataset(name="dataset-three", title='Dataset Three') + factories.Dataset( + name="dataset-one", + title="Dataset One", + owner_org=org["id"], + private=True, + ) + factories.Dataset(name="dataset-two", title="Dataset Two") + factories.Dataset(name="dataset-three", title="Dataset Three") - search_url = url_for('dataset.search') + search_url = url_for("dataset.search") search_response = app.get(search_url) search_response_html = BeautifulSoup(search_response.body) - ds_titles = search_response_html.select('.dataset-list ' - '.dataset-item ' - '.dataset-heading a') + ds_titles = search_response_html.select( + ".dataset-list " ".dataset-item " ".dataset-heading a" + ) ds_titles = [n.string for n in ds_titles] - assert_equal(len(ds_titles), 2) - assert_true('Dataset One' not in ds_titles) - assert_true('Dataset Two' in ds_titles) - assert_true('Dataset Three' in ds_titles) + assert len(ds_titles) == 2 + assert "Dataset One" not in ds_titles + assert "Dataset Two" in ds_titles + assert "Dataset Three" in ds_titles + + @pytest.mark.usefixtures("clean_db", "clean_index") + def test_user_not_in_organization_cannot_search_private_datasets( + self, app + ): - def test_user_not_in_organization_cannot_search_private_datasets(self): - app = helpers._get_test_app() user = factories.User() organization = factories.Organization() - dataset = factories.Dataset( - owner_org=organization['id'], - private=True, - ) - env = {'REMOTE_USER': user['name'].encode('ascii')} - search_url = url_for('dataset.search') + dataset = factories.Dataset(owner_org=organization["id"], private=True) + env = {"REMOTE_USER": user["name"].encode("ascii")} + search_url = url_for("dataset.search") search_response = app.get(search_url, extra_environ=env) search_response_html = BeautifulSoup(search_response.body) - ds_titles = search_response_html.select('.dataset-list ' - '.dataset-item ' - '.dataset-heading a') - assert_equal([n.string for n in ds_titles], []) + ds_titles = search_response_html.select( + ".dataset-list " ".dataset-item " ".dataset-heading a" + ) + assert [n.string for n in ds_titles] == [] + + @pytest.mark.usefixtures("clean_db", "clean_index") + def test_user_in_organization_can_search_private_datasets(self, app): - def test_user_in_organization_can_search_private_datasets(self): - app = helpers._get_test_app() user = factories.User() organization = factories.Organization( - users=[{'name': user['id'], 'capacity': 'member'}]) + users=[{"name": user["id"], "capacity": "member"}] + ) dataset = factories.Dataset( - title='A private dataset', - owner_org=organization['id'], + title="A private dataset", + owner_org=organization["id"], private=True, ) - env = {'REMOTE_USER': user['name'].encode('ascii')} - search_url = url_for('dataset.search') + env = {"REMOTE_USER": user["name"].encode("ascii")} + search_url = url_for("dataset.search") search_response = app.get(search_url, extra_environ=env) search_response_html = BeautifulSoup(search_response.body) - ds_titles = search_response_html.select('.dataset-list ' - '.dataset-item ' - '.dataset-heading a') - assert_equal([n.string for n in ds_titles], ['A private dataset']) + ds_titles = search_response_html.select( + ".dataset-list " ".dataset-item " ".dataset-heading a" + ) + assert [n.string for n in ds_titles] == ["A private dataset"] - def test_user_in_different_organization_cannot_search_private_datasets(self): - app = helpers._get_test_app() + @pytest.mark.usefixtures("clean_db", "clean_index") + def test_user_in_different_organization_cannot_search_private_datasets( + self, app + ): user = factories.User() org1 = factories.Organization( - users=[{'name': user['id'], 'capacity': 'member'}]) + users=[{"name": user["id"], "capacity": "member"}] + ) org2 = factories.Organization() dataset = factories.Dataset( - title='A private dataset', - owner_org=org2['id'], - private=True, + title="A private dataset", owner_org=org2["id"], private=True ) - env = {'REMOTE_USER': user['name'].encode('ascii')} - search_url = url_for('dataset.search') + env = {"REMOTE_USER": user["name"].encode("ascii")} + search_url = url_for("dataset.search") search_response = app.get(search_url, extra_environ=env) search_response_html = BeautifulSoup(search_response.body) - ds_titles = search_response_html.select('.dataset-list ' - '.dataset-item ' - '.dataset-heading a') - assert_equal([n.string for n in ds_titles], []) - - @helpers.change_config('ckan.search.default_include_private', 'false') - def test_search_default_include_private_false(self): - app = helpers._get_test_app() + ds_titles = search_response_html.select( + ".dataset-list " ".dataset-item " ".dataset-heading a" + ) + assert [n.string for n in ds_titles] == [] + + @pytest.mark.usefixtures("clean_db", "clean_index") + @pytest.mark.ckan_config("ckan.search.default_include_private", "false") + def test_search_default_include_private_false(self, app): user = factories.User() organization = factories.Organization( - users=[{'name': user['id'], 'capacity': 'member'}]) - dataset = factories.Dataset( - owner_org=organization['id'], - private=True, + users=[{"name": user["id"], "capacity": "member"}] ) - env = {'REMOTE_USER': user['name'].encode('ascii')} - search_url = url_for('dataset.search') + dataset = factories.Dataset(owner_org=organization["id"], private=True) + env = {"REMOTE_USER": user["name"].encode("ascii")} + search_url = url_for("dataset.search") search_response = app.get(search_url, extra_environ=env) search_response_html = BeautifulSoup(search_response.body) - ds_titles = search_response_html.select('.dataset-list ' - '.dataset-item ' - '.dataset-heading a') - assert_equal([n.string for n in ds_titles], []) + ds_titles = search_response_html.select( + ".dataset-list " ".dataset-item " ".dataset-heading a" + ) + assert [n.string for n in ds_titles] == [] - def test_sysadmin_can_search_private_datasets(self): - app = helpers._get_test_app() + @pytest.mark.usefixtures("clean_db", "clean_index") + def test_sysadmin_can_search_private_datasets(self, app): user = factories.Sysadmin() organization = factories.Organization() dataset = factories.Dataset( - title='A private dataset', - owner_org=organization['id'], + title="A private dataset", + owner_org=organization["id"], private=True, ) - env = {'REMOTE_USER': user['name'].encode('ascii')} - search_url = url_for('dataset.search') + env = {"REMOTE_USER": user["name"].encode("ascii")} + search_url = url_for("dataset.search") search_response = app.get(search_url, extra_environ=env) search_response_html = BeautifulSoup(search_response.body) - ds_titles = search_response_html.select('.dataset-list ' - '.dataset-item ' - '.dataset-heading a') - assert_equal([n.string for n in ds_titles], ['A private dataset']) - + ds_titles = search_response_html.select( + ".dataset-list " ".dataset-item " ".dataset-heading a" + ) + assert [n.string for n in ds_titles] == ["A private dataset"] -class TestPackageFollow(helpers.FunctionalTestBase): - def test_package_follow(self): - app = self._get_test_app() +class TestPackageFollow(object): + @pytest.mark.usefixtures("clean_db") + def test_package_follow(self, app): user = factories.User() package = factories.Dataset() - env = {'REMOTE_USER': user['name'].encode('ascii')} - follow_url = url_for('dataset.follow', - id=package['id']) + env = {"REMOTE_USER": user["name"].encode("ascii")} + follow_url = url_for("dataset.follow", id=package["id"]) response = app.post(follow_url, extra_environ=env, status=302) response = response.follow() - assert_true('You are now following {0}' - .format(package['title']) - in response) + assert "You are now following {0}".format(package["title"]) in response - def test_package_follow_not_exist(self): - '''Pass an id for a package that doesn't exist''' - app = self._get_test_app() + @pytest.mark.usefixtures("clean_db") + def test_package_follow_not_exist(self, app): + """Pass an id for a package that doesn't exist""" user_one = factories.User() - env = {'REMOTE_USER': user_one['name'].encode('ascii')} - follow_url = url_for('dataset.follow', - id='not-here') + env = {"REMOTE_USER": user_one["name"].encode("ascii")} + follow_url = url_for("dataset.follow", id="not-here") response = app.post(follow_url, extra_environ=env, status=302) response = response.follow(status=404) - assert_true('Dataset not found' in response) + assert "Dataset not found" in response - def test_package_unfollow(self): - app = self._get_test_app() + @pytest.mark.usefixtures("clean_db") + def test_package_unfollow(self, app): user_one = factories.User() package = factories.Dataset() - env = {'REMOTE_USER': user_one['name'].encode('ascii')} - follow_url = url_for('dataset.follow', - id=package['id']) + env = {"REMOTE_USER": user_one["name"].encode("ascii")} + follow_url = url_for("dataset.follow", id=package["id"]) app.post(follow_url, extra_environ=env, status=302) - unfollow_url = url_for('dataset.unfollow', - id=package['id']) - unfollow_response = app.post(unfollow_url, extra_environ=env, - status=302) + unfollow_url = url_for("dataset.unfollow", id=package["id"]) + unfollow_response = app.post( + unfollow_url, extra_environ=env, status=302 + ) unfollow_response = unfollow_response.follow() - assert_true('You are no longer following {0}' - .format(package['title']) - in unfollow_response) + assert ( + "You are no longer following {0}".format(package["title"]) + in unfollow_response + ) - def test_package_unfollow_not_following(self): - '''Unfollow a package not currently following''' - app = self._get_test_app() + @pytest.mark.usefixtures("clean_db") + def test_package_unfollow_not_following(self, app): + """Unfollow a package not currently following""" user_one = factories.User() package = factories.Dataset() - env = {'REMOTE_USER': user_one['name'].encode('ascii')} - unfollow_url = url_for('dataset.unfollow', - id=package['id']) - unfollow_response = app.post(unfollow_url, extra_environ=env, - status=302) + env = {"REMOTE_USER": user_one["name"].encode("ascii")} + unfollow_url = url_for("dataset.unfollow", id=package["id"]) + unfollow_response = app.post( + unfollow_url, extra_environ=env, status=302 + ) unfollow_response = unfollow_response.follow() # /package/[id] 302s to unfollow_response = unfollow_response.follow() # /package/[name] - assert_true('You are not following {0}'.format(package['id']) - in unfollow_response) + assert ( + "You are not following {0}".format(package["id"]) + in unfollow_response + ) - def test_package_unfollow_not_exist(self): - '''Unfollow a package that doesn't exist.''' - app = self._get_test_app() + @pytest.mark.usefixtures("clean_db") + def test_package_unfollow_not_exist(self, app): + """Unfollow a package that doesn't exist.""" user_one = factories.User() - env = {'REMOTE_USER': user_one['name'].encode('ascii')} - unfollow_url = url_for('dataset.unfollow', - id='not-here') - unfollow_response = app.post(unfollow_url, extra_environ=env, - status=302) + env = {"REMOTE_USER": user_one["name"].encode("ascii")} + unfollow_url = url_for("dataset.unfollow", id="not-here") + unfollow_response = app.post( + unfollow_url, extra_environ=env, status=302 + ) unfollow_response = unfollow_response.follow(status=404) - assert_true('Dataset not found' in unfollow_response) + assert "Dataset not found" in unfollow_response - def test_package_follower_list(self): - '''Following users appear on followers list page.''' - app = self._get_test_app() + @pytest.mark.usefixtures("clean_db") + def test_package_follower_list(self, app): + """Following users appear on followers list page.""" user_one = factories.Sysadmin() package = factories.Dataset() - env = {'REMOTE_USER': user_one['name'].encode('ascii')} - follow_url = url_for('dataset.follow', - id=package['id']) + env = {"REMOTE_USER": user_one["name"].encode("ascii")} + follow_url = url_for("dataset.follow", id=package["id"]) app.post(follow_url, extra_environ=env, status=302) - followers_url = url_for('dataset.followers', - id=package['id']) + followers_url = url_for("dataset.followers", id=package["id"]) # Only sysadmins can view the followers list pages - followers_response = app.get(followers_url, extra_environ=env, - status=200) - assert_true(user_one['display_name'] in followers_response) - + followers_response = app.get( + followers_url, extra_environ=env, status=200 + ) + assert user_one["display_name"] in followers_response -class TestDatasetRead(helpers.FunctionalTestBase): - def test_dataset_read(self): - app = self._get_test_app() +class TestDatasetRead(object): + @pytest.mark.usefixtures("clean_db") + def test_dataset_read(self, app): dataset = factories.Dataset() - url = url_for('dataset.read', - id=dataset['name']) + url = url_for("dataset.read", id=dataset["name"]) response = app.get(url) - assert_in(dataset['title'], response) + assert dataset["title"] in response - def test_redirect_when_given_id(self): + @pytest.mark.usefixtures("clean_db") + def test_redirect_when_given_id(self, app): dataset = factories.Dataset() - app = helpers._get_test_app() - response = app.get(url_for('dataset.read', id=dataset['id']), - status=302) + response = app.get( + url_for("dataset.read", id=dataset["id"]), status=302 + ) # redirect replaces the ID with the name in the URL redirected_response = response.follow() - expected_url = url_for('dataset.read', id=dataset['name']) - assert_equal(redirected_response.request.path, expected_url) - assert_equal(redirected_response.request.query_string, '') + expected_url = url_for("dataset.read", id=dataset["name"]) + assert redirected_response.request.path == expected_url + assert redirected_response.request.query_string == "" - def test_redirect_also_with_activity_parameter(self): + @pytest.mark.usefixtures("clean_db") + def test_redirect_also_with_activity_parameter(self, app): dataset = factories.Dataset() - activity = activity_model.package_activity_list(dataset['id'], limit=1, - offset=0)[0] + activity = activity_model.package_activity_list( + dataset["id"], limit=1, offset=0 + )[0] # view as an admin because viewing the old versions of a dataset sysadmin = factories.Sysadmin() - env = {'REMOTE_USER': sysadmin['name'].encode('ascii')} - app = helpers._get_test_app() - response = app.get(url_for('dataset.read', id=dataset['id'], - activity_id=activity.id), - status=302, extra_environ=env) + env = {"REMOTE_USER": sysadmin["name"].encode("ascii")} + response = app.get( + url_for("dataset.read", id=dataset["id"], activity_id=activity.id), + status=302, + extra_environ=env, + ) redirected_response = response.follow(extra_environ=env) - expected_path = url_for('dataset.read', id=dataset['name']) - assert_equal(redirected_response.request.path, expected_path) - assert_equal(redirected_response.request.query_string, - 'activity_id={}'.format(activity.id)) - - def test_no_redirect_loop_when_name_is_the_same_as_the_id(self): - dataset = factories.Dataset(id='abc', name='abc') - app = helpers._get_test_app() - app.get(url_for('dataset.read', id=dataset['id']), - status=200) # ie no redirect - - -class TestActivity(helpers.FunctionalTestBase): - def test_simple(self): - '''Checking the template shows the activity stream.''' - app = self._get_test_app() + expected_path = url_for("dataset.read", id=dataset["name"]) + assert redirected_response.request.path == expected_path + assert ( + redirected_response.request.query_string + == "activity_id={}".format(activity.id) + ) + + @pytest.mark.usefixtures("clean_db") + def test_no_redirect_loop_when_name_is_the_same_as_the_id(self, app): + dataset = factories.Dataset(id="abc", name="abc") + app.get( + url_for("dataset.read", id=dataset["id"]), status=200 + ) # ie no redirect + + +class TestActivity(object): + @pytest.mark.usefixtures("clean_db") + def test_simple(self, app): + """Checking the template shows the activity stream.""" user = factories.User() dataset = factories.Dataset(user=user) - url = url_for('dataset.activity', - id=dataset['id']) + url = url_for("dataset.activity", id=dataset["id"]) response = app.get(url) - assert_in('Mr. Test User', response) - assert_in('created the dataset', response) + assert "Mr. Test User" in response + assert "created the dataset" in response + + @pytest.mark.usefixtures("clean_db") + def test_create_dataset(self, app): - def test_create_dataset(self): - app = self._get_test_app() user = factories.User() dataset = factories.Dataset(user=user) - url = url_for('dataset.activity', - id=dataset['id']) + url = url_for("dataset.activity", id=dataset["id"]) response = app.get(url) - assert_in('Mr. Test User'.format(user['name']), - response) - assert_in('created the dataset', response) - assert_in('Test Dataset'.format(dataset['id']), - response) + assert ( + 'Mr. Test User'.format(user["name"]) in response + ) + assert "created the dataset" in response + assert ( + 'Test Dataset'.format(dataset["id"]) + in response + ) def _clear_activities(self): model.Session.query(model.Activity).delete() model.Session.flush() - def test_change_dataset(self): - app = self._get_test_app() + @pytest.mark.usefixtures("clean_db") + def test_change_dataset(self, app): + user = factories.User() dataset = factories.Dataset(user=user) self._clear_activities() - dataset['title'] = 'Dataset with changed title' + dataset["title"] = "Dataset with changed title" helpers.call_action( - 'package_update', context={'user': user['name']}, **dataset) + "package_update", context={"user": user["name"]}, **dataset + ) - url = url_for('dataset.activity', - id=dataset['id']) + url = url_for("dataset.activity", id=dataset["id"]) response = app.get(url) - assert_in('Mr. Test User'.format(user['name']), - response) - assert_in('updated the dataset', response) - assert_in('Dataset with changed title' - .format(dataset['id']), - response) - - def test_create_tag_directly(self): - app = self._get_test_app() + assert ( + 'Mr. Test User'.format(user["name"]) in response + ) + assert "updated the dataset" in response + assert ( + 'Dataset with changed title'.format( + dataset["id"] + ) + in response + ) + + @pytest.mark.usefixtures("clean_db") + def test_create_tag_directly(self, app): + user = factories.User() dataset = factories.Dataset(user=user) self._clear_activities() - dataset['tags'] = [{'name': 'some_tag'}] + dataset["tags"] = [{"name": "some_tag"}] helpers.call_action( - 'package_update', context={'user': user['name']}, **dataset) + "package_update", context={"user": user["name"]}, **dataset + ) - url = url_for('dataset.activity', - id=dataset['id']) + url = url_for("dataset.activity", id=dataset["id"]) response = app.get(url) - assert_in('Mr. Test User'.format(user['name']), - response) - assert_in('updated the dataset', response) - assert_in('{}' - .format(dataset['id'], dataset['title']), - response) + assert ( + 'Mr. Test User'.format(user["name"]) in response + ) + assert "updated the dataset" in response + assert ( + '{}'.format(dataset["id"], dataset["title"]) + in response + ) activities = helpers.call_action( - 'package_activity_list', id=dataset['id']) + "package_activity_list", id=dataset["id"] + ) - assert_equal(len(activities), 1) + assert len(activities) == 1 - def test_create_tag(self): - app = self._get_test_app() + @pytest.mark.usefixtures("clean_db") + def test_create_tag(self, app): user = factories.User() dataset = factories.Dataset(user=user) self._clear_activities() - dataset['tags'] = [{'name': 'some_tag'}] + dataset["tags"] = [{"name": "some_tag"}] helpers.call_action( - 'package_update', context={'user': user['name']}, **dataset) + "package_update", context={"user": user["name"]}, **dataset + ) - url = url_for('dataset.activity', - id=dataset['id']) + url = url_for("dataset.activity", id=dataset["id"]) response = app.get(url) - assert_in('Mr. Test User'.format(user['name']), - response) - assert_in('updated the dataset', response) - assert_in('{}' - .format(dataset['id'], dataset['title']), - response) + assert ( + 'Mr. Test User'.format(user["name"]) in response + ) + assert "updated the dataset" in response + assert ( + '{}'.format(dataset["id"], dataset["title"]) + in response + ) activities = helpers.call_action( - 'package_activity_list', id=dataset['id']) + "package_activity_list", id=dataset["id"] + ) - assert_equal(len(activities), 1) + assert len(activities) == 1 - def test_create_extra(self): - app = self._get_test_app() + @pytest.mark.usefixtures("clean_db") + def test_create_extra(self, app): user = factories.User() dataset = factories.Dataset(user=user) self._clear_activities() - dataset['extras'] = [{'key': 'some', 'value': 'extra'}] + dataset["extras"] = [{"key": "some", "value": "extra"}] helpers.call_action( - 'package_update', context={'user': user['name']}, **dataset) + "package_update", context={"user": user["name"]}, **dataset + ) - url = url_for('dataset.activity', - id=dataset['id']) + url = url_for("dataset.activity", id=dataset["id"]) response = app.get(url) - assert_in('Mr. Test User'.format(user['name']), - response) - assert_in('updated the dataset', response) - assert_in('{}' - .format(dataset['id'], dataset['title']), - response) + assert ( + 'Mr. Test User'.format(user["name"]) in response + ) + assert "updated the dataset" in response + assert ( + '{}'.format(dataset["id"], dataset["title"]) + in response + ) activities = helpers.call_action( - 'package_activity_list', id=dataset['id']) + "package_activity_list", id=dataset["id"] + ) - assert_equal(len(activities), 1) + assert len(activities) == 1 - def test_create_resource(self): - app = self._get_test_app() + @pytest.mark.usefixtures("clean_db") + def test_create_resource(self, app): user = factories.User() dataset = factories.Dataset(user=user) self._clear_activities() helpers.call_action( - 'resource_create', context={'user': user['name']}, - name='Test resource', - package_id=dataset['id']) + "resource_create", + context={"user": user["name"]}, + name="Test resource", + package_id=dataset["id"], + ) - url = url_for('dataset.activity', - id=dataset['id']) + url = url_for("dataset.activity", id=dataset["id"]) response = app.get(url) - assert_in('Mr. Test User'.format(user['name']), - response) - assert_in('updated the dataset', response) - assert_in('{}' - .format(dataset['id'], dataset['title']), - response) + assert ( + 'Mr. Test User'.format(user["name"]) in response + ) + assert "updated the dataset" in response + assert ( + '{}'.format(dataset["id"], dataset["title"]) + in response + ) activities = helpers.call_action( - 'package_activity_list', id=dataset['id']) + "package_activity_list", id=dataset["id"] + ) - assert_equal(len(activities), 1) + assert len(activities) == 1 - def test_update_resource(self): - app = self._get_test_app() + @pytest.mark.usefixtures("clean_db") + def test_update_resource(self, app): user = factories.User() dataset = factories.Dataset(user=user) - resource = factories.Resource(package_id=dataset['id']) + resource = factories.Resource(package_id=dataset["id"]) self._clear_activities() helpers.call_action( - 'resource_update', context={'user': user['name']}, - id=resource['id'], - name='Test resource updated', - package_id=dataset['id']) + "resource_update", + context={"user": user["name"]}, + id=resource["id"], + name="Test resource updated", + package_id=dataset["id"], + ) - url = url_for('dataset.activity', - id=dataset['id']) + url = url_for("dataset.activity", id=dataset["id"]) response = app.get(url) - assert_in('Mr. Test User'.format(user['name']), - response) - assert_in('updated the dataset', response) - assert_in('{}' - .format(dataset['id'], dataset['title']), - response) + assert ( + 'Mr. Test User'.format(user["name"]) in response + ) + assert "updated the dataset" in response + assert ( + '{}'.format(dataset["id"], dataset["title"]) + in response + ) activities = helpers.call_action( - 'package_activity_list', id=dataset['id']) + "package_activity_list", id=dataset["id"] + ) - assert_equal(len(activities), 1) + assert len(activities) == 1 - def test_delete_dataset(self): - app = self._get_test_app() + @pytest.mark.usefixtures("clean_db") + def test_delete_dataset(self, app): user = factories.User() org = factories.Organization() - dataset = factories.Dataset(owner_org=org['id'], user=user) + dataset = factories.Dataset(owner_org=org["id"], user=user) self._clear_activities() helpers.call_action( - 'package_delete', context={'user': user['name']}, **dataset) + "package_delete", context={"user": user["name"]}, **dataset + ) - url = url_for('organization.activity', - id=org['id']) + url = url_for("organization.activity", id=org["id"]) response = app.get(url) - assert_in('Mr. Test User'.format(user['name']), - response) - assert_in('deleted the dataset', response) - assert_in('Test Dataset' - .format(dataset['id']), - response) - - def test_admin_can_see_old_versions(self): - app = self._get_test_app() + assert ( + 'Mr. Test User'.format(user["name"]) in response + ) + assert "deleted the dataset" in response + assert ( + 'Test Dataset'.format(dataset["id"]) + in response + ) + + @pytest.mark.usefixtures("clean_db") + def test_admin_can_see_old_versions(self, app): + user = factories.User() - env = {'REMOTE_USER': user['name'].encode('ascii')} + env = {"REMOTE_USER": user["name"].encode("ascii")} dataset = factories.Dataset(user=user) - url = url_for('dataset.activity', - id=dataset['id']) + url = url_for("dataset.activity", id=dataset["id"]) response = app.get(url, extra_environ=env) - assert_in('View this version', response) + assert "View this version" in response + + @pytest.mark.usefixtures("clean_db") + def test_public_cant_see_old_versions(self, app): - def test_public_cant_see_old_versions(self): - app = self._get_test_app() user = factories.User() dataset = factories.Dataset(user=user) - url = url_for('dataset.activity', - id=dataset['id']) + url = url_for("dataset.activity", id=dataset["id"]) response = app.get(url) - assert_not_in('View this version', response) + assert "View this version" not in response + + @pytest.mark.usefixtures("clean_db") + def test_admin_can_see_changes(self, app): - def test_admin_can_see_changes(self): - app = self._get_test_app() user = factories.User() - env = {'REMOTE_USER': user['name'].encode('ascii')} + env = {"REMOTE_USER": user["name"].encode("ascii")} dataset = factories.Dataset() # activities by system user aren't shown - dataset['title'] = 'Changed' - helpers.call_action('package_update', **dataset) + dataset["title"] = "Changed" + helpers.call_action("package_update", **dataset) - url = url_for('dataset.activity', - id=dataset['id']) + url = url_for("dataset.activity", id=dataset["id"]) response = app.get(url, extra_environ=env) - assert_in('Changes', response) - changes_page = response.click('Changes', extra_environ=env) + assert "Changes" in response + changes_page = response.click("Changes", extra_environ=env) - def test_public_cant_see_changes(self): - app = self._get_test_app() + @pytest.mark.usefixtures("clean_db") + def test_public_cant_see_changes(self, app): dataset = factories.Dataset() # activities by system user aren't shown - dataset['title'] = 'Changed' - helpers.call_action('package_update', **dataset) + dataset["title"] = "Changed" + helpers.call_action("package_update", **dataset) - url = url_for('dataset.activity', - id=dataset['id']) + url = url_for("dataset.activity", id=dataset["id"]) response = app.get(url) - assert_not_in('Changes', response) + assert "Changes" not in response - def test_legacy_changed_package_activity(self): - '''Render an activity that was created with an earlier version of CKAN, + @pytest.mark.usefixtures("clean_db") + def test_legacy_changed_package_activity(self, app): + """Render an activity that was created with an earlier version of CKAN, and it has not been migrated (with migrate_package_activity.py) - ''' - app = self._get_test_app() + """ user = factories.User() dataset = factories.Dataset(user=user) # delete the modern Activity object that's been automatically created - modern_activity = model.Session.query(model.Activity) \ - .filter_by(object_id=dataset['id']) \ + modern_activity = ( + model.Session.query(model.Activity) + .filter_by(object_id=dataset["id"]) .one() + ) modern_activity.delete() # Create an Activity object as it was in earlier versions of CKAN. # This code is based on: # https://github.com/ckan/ckan/blob/b348bf2fe68db6704ea0a3e22d533ded3d8d4344/ckan/model/package.py#L508 - activity_type = 'changed' + activity_type = "changed" dataset_table_dict = dictization.table_dictize( - model.Package.get(dataset['id']), context={'model': model}) + model.Package.get(dataset["id"]), context={"model": model} + ) activity = model.Activity( - user_id=user['id'], - object_id=dataset['id'], + user_id=user["id"], + object_id=dataset["id"], activity_type="%s package" % activity_type, data={ # "actor": a legacy activity had no "actor" # "package": a legacy activity had just the package table, # rather than the result of package_show - 'package': dataset_table_dict, - } + "package": dataset_table_dict + }, ) model.Session.add(activity) # a legacy activity had a ActivityDetail associated with the Activity @@ -2181,86 +2117,88 @@ def test_legacy_changed_package_activity(self): # https://github.com/ckan/ckan/blob/b348bf2fe68db6704ea0a3e22d533ded3d8d4344/ckan/model/package.py#L542 activity_detail = model.ActivityDetail( activity_id=activity.id, - object_id=dataset['id'], + object_id=dataset["id"], object_type=u"Package", activity_type=activity_type, - data={u'package': dataset_table_dict}) + data={u"package": dataset_table_dict}, + ) model.Session.add(activity_detail) model.Session.flush() - url = url_for('dataset.activity', - id=dataset['id']) + url = url_for("dataset.activity", id=dataset["id"]) response = app.get(url) - assert_in('Mr. Test User'.format(user['name']), - response) - assert_in('updated the dataset', response) - assert_in('Test Dataset'.format(dataset['id']), - response) + assert ( + 'Mr. Test User'.format(user["name"]) in response + ) + assert "updated the dataset" in response + assert ( + 'Test Dataset'.format(dataset["id"]) + in response + ) # ckanext-canada uses their IActivity to add their custom activity to the # list of validators: https://github.com/open-data/ckanext-canada/blob/6870e5bc38a04aa8cef191b5e9eb361f9560872b/ckanext/canada/plugins.py#L596 # but it's easier here to just hack patch it in @mock.patch( - 'ckan.logic.validators.object_id_validators', dict( - object_id_validators.items() + - [('changed datastore', package_id_exists)]) + "ckan.logic.validators.object_id_validators", + dict( + object_id_validators.items() + + [("changed datastore", package_id_exists)] + ), ) - def test_custom_activity(self): - '''Render a custom activity - ''' - app = self._get_test_app() + @pytest.mark.usefixtures("clean_db") + def test_custom_activity(self, app): + """Render a custom activity + """ user = factories.User() organization = factories.Organization( - users=[{'name': user['id'], 'capacity': 'admin'}] + users=[{"name": user["id"], "capacity": "admin"}] ) - dataset = factories.Dataset(owner_org=organization['id'], user=user) - resource = factories.Resource(package_id=dataset['id']) + dataset = factories.Dataset(owner_org=organization["id"], user=user) + resource = factories.Resource(package_id=dataset["id"]) self._clear_activities() # Create a custom Activity object. This one is inspired by: # https://github.com/open-data/ckanext-canada/blob/master/ckanext/canada/activity.py activity_dict = { - 'user_id': user['id'], - 'object_id': dataset['id'], - 'activity_type': 'changed datastore', - 'data': { - 'resource_id': resource['id'], - 'pkg_type': dataset['type'], - 'resource_name': 'june-2018', - 'owner_org': organization['name'], - 'count': 5, - } + "user_id": user["id"], + "object_id": dataset["id"], + "activity_type": "changed datastore", + "data": { + "resource_id": resource["id"], + "pkg_type": dataset["type"], + "resource_name": "june-2018", + "owner_org": organization["name"], + "count": 5, + }, } - helpers.call_action('activity_create', **activity_dict) + helpers.call_action("activity_create", **activity_dict) - url = url_for('dataset.activity', - id=dataset['id']) + url = url_for("dataset.activity", id=dataset["id"]) response = app.get(url) - assert_in('Mr. Test User'.format(user['name']), - response) + assert ( + 'Mr. Test User'.format(user["name"]) in response + ) # it renders the activity with fallback.html, since we've not defined # changed_datastore.html in this case - assert_in('changed datastore', response) + assert "changed datastore" in response class TestChanges(object): # i.e. the diff - @classmethod - def setup_class(cls): - helpers.reset_db() - - def test_simple(self): + @pytest.mark.usefixtures("clean_db") + def test_simple(self, app): user = factories.User() - dataset = factories.Dataset(title='First title', user=user) - dataset['title'] = 'Second title' - helpers.call_action('package_update', **dataset) + dataset = factories.Dataset(title="First title", user=user) + dataset["title"] = "Second title" + helpers.call_action("package_update", **dataset) - app = helpers._get_test_app() activity = activity_model.package_activity_list( - dataset['id'], limit=1, offset=0)[0] - env = {'REMOTE_USER': user['name'].encode('ascii')} - response = app.get(url_for('dataset.changes', - id=activity.id), - extra_environ=env) - response.mustcontain('First') - response.mustcontain('Second') + dataset["id"], limit=1, offset=0 + )[0] + env = {"REMOTE_USER": user["name"].encode("ascii")} + response = app.get( + url_for("dataset.changes", id=activity.id), extra_environ=env + ) + response.mustcontain("First") + response.mustcontain("Second") diff --git a/ckan/tests/controllers/test_template.py b/ckan/tests/controllers/test_template.py index 380aac1a8fd..eb17c607b14 100644 --- a/ckan/tests/controllers/test_template.py +++ b/ckan/tests/controllers/test_template.py @@ -1,20 +1,16 @@ # encoding: utf-8 -from nose.tools import assert_equal -import six +import pytest -import ckan.tests.helpers as helpers - -class TestTemplateController(helpers.FunctionalTestBase): - - def test_content_type(self): - cases = { - u'/robots.txt': u'text/plain; charset=utf-8', - u'/page': u'text/html; charset=utf-8', - u'/page.html': u'text/html; charset=utf-8', - } - app = self._get_test_app() - for url, expected in six.iteritems(cases): - response = app.get(url, status=200) - assert_equal(response.headers.get(u'Content-Type'), expected) +@pytest.mark.parametrize( + u"url,expected", + [ + (u"/robots.txt", u"text/plain; charset=utf-8"), + (u"/page", u"text/html; charset=utf-8"), + (u"/page.html", u"text/html; charset=utf-8"), + ], +) +def test_content_type(url, expected, app): + response = app.get(url, status=200) + assert response.headers.get(u"Content-Type") == expected diff --git a/ckan/tests/controllers/test_user.py b/ckan/tests/controllers/test_user.py index ddaba15aae4..7457c6e81b2 100644 --- a/ckan/tests/controllers/test_user.py +++ b/ckan/tests/controllers/test_user.py @@ -1,938 +1,953 @@ # encoding: utf-8 -from bs4 import BeautifulSoup -from nose.tools import assert_true, assert_false, assert_equal, assert_in import mock +import pytest +from bs4 import BeautifulSoup -import ckan.tests.helpers as helpers import ckan.tests.factories as factories import ckan.tests.helpers as helpers - -from ckan.lib.helpers import url_for from ckan import model +from ckan.lib.helpers import url_for from ckan.lib.mailer import create_reset_key, MailerException + webtest_submit = helpers.webtest_submit submit_and_follow = helpers.submit_and_follow +def _clear_activities(): + model.Session.query(model.ActivityDetail).delete() + model.Session.query(model.Activity).delete() + model.Session.flush() + + def _get_user_edit_page(app): user = factories.User() - env = {'REMOTE_USER': user['name'].encode('ascii')} - response = app.get( - url=url_for('user.edit'), - extra_environ=env, - ) + env = {"REMOTE_USER": user["name"].encode("ascii")} + response = app.get(url=url_for("user.edit"), extra_environ=env) return env, response, user -class TestRegisterUser(helpers.FunctionalTestBase): - def test_register_a_user(self): - app = helpers._get_test_app() - response = app.get(url=url_for('user.register')) - - form = response.forms['user-register-form'] - form['name'] = 'newuser' - form['fullname'] = 'New User' - form['email'] = 'test@test.com' - form['password1'] = 'TestPassword1' - form['password2'] = 'TestPassword1' - response = submit_and_follow(app, form, name='save') - response = response.follow() - assert_equal(200, response.status_int) - - user = helpers.call_action('user_show', id='newuser') - assert_equal(user['name'], 'newuser') - assert_equal(user['fullname'], 'New User') - assert_false(user['sysadmin']) - - def test_register_user_bad_password(self): - app = helpers._get_test_app() - response = app.get(url=url_for('user.register')) - - form = response.forms['user-register-form'] - form['name'] = 'newuser' - form['fullname'] = 'New User' - form['email'] = 'test@test.com' - form['password1'] = 'TestPassword1' - form['password2'] = '' - - response = form.submit('save') - assert_true('The passwords you entered do not match' in response) - - def test_create_user_as_sysadmin(self): - admin_pass = 'RandomPassword123' - sysadmin = factories.Sysadmin(password=admin_pass) - app = self._get_test_app() - - # Have to do an actual login as this test relies on repoze - # cookie handling. - - # get the form - response = app.get('/user/login') - # ...it's the second one - login_form = response.forms[1] - # fill it in - login_form['login'] = sysadmin['name'] - login_form['password'] = admin_pass - # submit it - login_form.submit('save') - - response = app.get( - url=url_for('user.register'), - ) - assert "user-register-form" in response.forms - form = response.forms['user-register-form'] - form['name'] = 'newestuser' - form['fullname'] = 'Newest User' - form['email'] = 'test@test.com' - form['password1'] = 'NewPassword1' - form['password2'] = 'NewPassword1' - response2 = form.submit('save') - assert '/user/activity' in response2.location - - -class TestLoginView(helpers.FunctionalTestBase): - def test_registered_user_login(self): - ''' - Registered user can submit valid login details at /user/login and - be returned to appropriate place. - ''' - app = helpers._get_test_app() - - # make a user - user = factories.User() - - # get the form - response = app.get('/user/login') - # ...it's the second one - login_form = response.forms[1] - - # fill it in - login_form['login'] = user['name'] - login_form['password'] = 'RandomPassword123' - - # submit it - submit_response = login_form.submit() - # let's go to the last redirect in the chain - final_response = helpers.webtest_maybe_follow(submit_response) - - # the response is the user dashboard, right? - final_response.mustcontain('Dashboard', - '{0}' - .format(user['fullname'])) - # and we're definitely not back on the login page. - final_response.mustcontain(no='

Login

') - - def test_registered_user_login_bad_password(self): - ''' - Registered user is redirected to appropriate place if they submit - invalid login details at /user/login. - ''' - app = helpers._get_test_app() - - # make a user - user = factories.User() - - # get the form - response = app.get('/user/login') - # ...it's the second one - login_form = response.forms[1] - - # fill it in - login_form['login'] = user['name'] - login_form['password'] = 'BadPass1' - - # submit it - submit_response = login_form.submit() - # let's go to the last redirect in the chain - final_response = helpers.webtest_maybe_follow(submit_response) - - # the response is the login page again - final_response.mustcontain('

Login

', - 'Login failed. Bad username or password.') - # and we're definitely not on the dashboard. - final_response.mustcontain(no='Dashboard'), - final_response.mustcontain(no='{0}' - .format(user['fullname'])) - - -class TestLogout(helpers.FunctionalTestBase): - - def test_user_logout_url_redirect(self): - '''_logout url redirects to logged out page. - - Note: this doesn't test the actual logout of a logged in user, just - the associated redirect. - ''' - app = self._get_test_app() - - logout_url = url_for('user.logout') - logout_response = app.get(logout_url, status=302) - final_response = helpers.webtest_maybe_follow(logout_response) - - assert_true('You are now logged out.' in final_response) - - @helpers.change_config('ckan.root_path', '/my/prefix') - def test_non_root_user_logout_url_redirect(self): - ''' - _logout url redirects to logged out page with `ckan.root_path` - prefixed. - - Note: this doesn't test the actual logout of a logged in user, just - the associated redirect. - ''' - app = self._get_test_app() - - logout_url = url_for('user.logout') - # Remove the prefix otherwise the test app won't find the correct route - logout_url = logout_url.replace('/my/prefix', '') - logout_response = app.get(logout_url, status=302) - assert_equal(logout_response.status_int, 302) - assert_true('/my/prefix/user/logout' in logout_response.location) - - -class TestUser(helpers.FunctionalTestBase): - - def test_not_logged_in_dashboard(self): - app = self._get_test_app() - - for route in ['index', 'organizations', 'datasets', 'groups']: - app.get( - url=url_for(u'dashboard.{}'.format(route)), - status=403 - ) - - def test_own_datasets_show_up_on_user_dashboard(self): - user = factories.User() - dataset_title = 'My very own dataset' - factories.Dataset(user=user, - name='my-own-dataset', - title=dataset_title) - - app = self._get_test_app() - env = {'REMOTE_USER': user['name'].encode('ascii')} - response = app.get( - url=url_for('dashboard.datasets'), - extra_environ=env, - ) +@pytest.mark.usefixtures("clean_db") +def test_register_a_user(app): + response = app.get(url=url_for("user.register")) + + form = response.forms["user-register-form"] + form["name"] = "newuser" + form["fullname"] = "New User" + form["email"] = "test@test.com" + form["password1"] = "TestPassword1" + form["password2"] = "TestPassword1" + response = submit_and_follow(app, form, name="save") + response = response.follow() + assert 200 == response.status_int + + user = helpers.call_action("user_show", id="newuser") + assert user["name"] == "newuser" + assert user["fullname"] == "New User" + assert not (user["sysadmin"]) + + +@pytest.mark.usefixtures("clean_db") +def test_register_user_bad_password(app): + response = app.get(url=url_for("user.register")) + + form = response.forms["user-register-form"] + form["name"] = "newuser" + form["fullname"] = "New User" + form["email"] = "test@test.com" + form["password1"] = "TestPassword1" + form["password2"] = "" + + response = form.submit("save") + assert "The passwords you entered do not match" in response + + +@pytest.mark.usefixtures("clean_db") +def test_create_user_as_sysadmin(app): + admin_pass = "RandomPassword123" + sysadmin = factories.Sysadmin(password=admin_pass) + + # Have to do an actual login as this test relies on repoze + # cookie handling. + + # get the form + response = app.get("/user/login") + # ...it's the second one + login_form = response.forms[1] + # fill it in + login_form["login"] = sysadmin["name"] + login_form["password"] = admin_pass + # submit it + login_form.submit("save") + + response = app.get(url=url_for("user.register")) + assert "user-register-form" in response.forms + form = response.forms["user-register-form"] + form["name"] = "newestuser" + form["fullname"] = "Newest User" + form["email"] = "test@test.com" + form["password1"] = "NewPassword1" + form["password2"] = "NewPassword1" + response2 = form.submit("save") + assert "/user/activity" in response2.location + + +@pytest.mark.usefixtures("clean_db") +def test_registered_user_login(app): + """ + Registered user can submit valid login details at /user/login and + be returned to appropriate place. + """ + + # make a user + user = factories.User() - assert_true(dataset_title in response) - - def test_other_datasets_dont_show_up_on_user_dashboard(self): - user1 = factories.User() - user2 = factories.User() - dataset_title = 'Someone else\'s dataset' - factories.Dataset(user=user1, - name='someone-elses-dataset', - title=dataset_title) - - app = self._get_test_app() - env = {'REMOTE_USER': user2['name'].encode('ascii')} - response = app.get( - url=url_for('dashboard.datasets'), - extra_environ=env, - ) + # get the form + response = app.get("/user/login") + # ...it's the second one + login_form = response.forms[1] + + # fill it in + login_form["login"] = user["name"] + login_form["password"] = "RandomPassword123" - assert_false(dataset_title in response) + # submit it + submit_response = login_form.submit() + # let's go to the last redirect in the chain + final_response = helpers.webtest_maybe_follow(submit_response) + # the response is the user dashboard, right? + final_response.mustcontain( + 'Dashboard', + '{0}'.format(user["fullname"]), + ) + # and we're definitely not back on the login page. + final_response.mustcontain(no='

Login

') -class TestUserEdit(helpers.FunctionalTestBase): - def test_user_edit_no_user(self): - app = self._get_test_app() - response = app.get( - url_for('user.edit', id=None), - status=400 - ) - assert_true('No user specified' in response) - - def test_user_edit_unknown_user(self): - '''Attempt to read edit user for an unknown user redirects to login - page.''' - app = self._get_test_app() - response = app.get( - url_for('user.edit', id='unknown_person'), - status=403) - - def test_user_edit_not_logged_in(self): - '''Attempt to read edit user for an existing, not-logged in user - redirects to login page.''' - app = self._get_test_app() - user = factories.User() - username = user['name'] - response = app.get( - url_for('user.edit', id=username), - status=403 - ) +@pytest.mark.usefixtures("clean_db") +def test_registered_user_login_bad_password(app): + """ + Registered user is redirected to appropriate place if they submit + invalid login details at /user/login. + """ - def test_edit_user(self): - user = factories.User(password='TestPassword1') - app = self._get_test_app() - env = {'REMOTE_USER': user['name'].encode('ascii')} - response = app.get( - url=url_for('user.edit'), - extra_environ=env, - ) - # existing values in the form - form = response.forms['user-edit-form'] - assert_equal(form['name'].value, user['name']) - assert_equal(form['fullname'].value, user['fullname']) - assert_equal(form['email'].value, user['email']) - assert_equal(form['about'].value, user['about']) - assert_equal(form['activity_streams_email_notifications'].value, None) - assert_equal(form['password1'].value, '') - assert_equal(form['password2'].value, '') - - # new values - # form['name'] = 'new-name' - form['fullname'] = 'new full name' - form['email'] = 'new@example.com' - form['about'] = 'new about' - form['activity_streams_email_notifications'] = True - form['old_password'] = 'TestPassword1' - form['password1'] = 'NewPass1' - form['password2'] = 'NewPass1' - response = submit_and_follow(app, form, env, 'save') - - user = model.Session.query(model.User).get(user['id']) - # assert_equal(user.name, 'new-name') - assert_equal(user.fullname, 'new full name') - assert_equal(user.email, 'new@example.com') - assert_equal(user.about, 'new about') - assert_equal(user.activity_streams_email_notifications, True) - - def test_email_change_without_password(self): - - app = self._get_test_app() - env, response, user = _get_user_edit_page(app) - - form = response.forms['user-edit-form'] - - # new values - form['email'] = 'new@example.com' - - # factory returns user with password 'pass' - form.fields['old_password'][0].value = 'Wrong-pass1' - - response = webtest_submit(form, 'save', status=200, extra_environ=env) - assert_true('Old Password: incorrect password' in response) - - def test_email_change_with_password(self): - app = self._get_test_app() - env, response, user = _get_user_edit_page(app) - - form = response.forms['user-edit-form'] - - # new values - form['email'] = 'new@example.com' - - # factory returns user with password 'pass' - form.fields['old_password'][0].value = 'RandomPassword123' - - response = submit_and_follow(app, form, env, 'save') - assert_true('Profile updated' in response) - - def test_edit_user_logged_in_username_change(self): - - user_pass = 'TestPassword1' - user = factories.User(password=user_pass) - app = self._get_test_app() - - # Have to do an actual login as this test relys on repoze cookie handling. - # get the form - response = app.get('/user/login') - # ...it's the second one - login_form = response.forms[1] - # fill it in - login_form['login'] = user['name'] - login_form['password'] = user_pass - # submit it - login_form.submit() - - # Now the cookie is set, run the test - response = app.get( - url=url_for('user.edit'), - ) - # existing values in the form - form = response.forms['user-edit-form'] - - # new values - form['name'] = 'new-name' - env = {'REMOTE_USER': user['name'].encode('ascii')} - response = webtest_submit(form, 'save', status=200, extra_environ=env) - assert_true('That login name can not be modified' in response) - - def test_edit_user_logged_in_username_change_by_name(self): - user_pass = 'TestPassword1' - user = factories.User(password=user_pass) - app = self._get_test_app() - - # Have to do an actual login as this test relys on repoze cookie handling. - # get the form - response = app.get('/user/login') - # ...it's the second one - login_form = response.forms[1] - # fill it in - login_form['login'] = user['name'] - login_form['password'] = user_pass - # submit it - login_form.submit() - - # Now the cookie is set, run the test - response = app.get( - url=url_for('user.edit', id=user['name']), - ) - # existing values in the form - form = response.forms['user-edit-form'] - - # new values - form['name'] = 'new-name' - env = {'REMOTE_USER': user['name'].encode('ascii')} - response = webtest_submit(form, 'save', status=200, extra_environ=env) - assert_true('That login name can not be modified' in response) - - def test_edit_user_logged_in_username_change_by_id(self): - user_pass = 'TestPassword1' - user = factories.User(password=user_pass) - app = self._get_test_app() - - # Have to do an actual login as this test relys on repoze cookie handling. - # get the form - response = app.get('/user/login') - # ...it's the second one - login_form = response.forms[1] - # fill it in - login_form['login'] = user['name'] - login_form['password'] = user_pass - # submit it - login_form.submit() - - # Now the cookie is set, run the test - response = app.get( - url=url_for('user.edit', id=user['id']), + # make a user + user = factories.User() + + # get the form + response = app.get("/user/login") + # ...it's the second one + login_form = response.forms[1] + + # fill it in + login_form["login"] = user["name"] + login_form["password"] = "BadPass1" + + # submit it + submit_response = login_form.submit() + # let's go to the last redirect in the chain + final_response = helpers.webtest_maybe_follow(submit_response) + + # the response is the login page again + final_response.mustcontain( + '

Login

', + "Login failed. Bad username or password.", + ) + # and we're definitely not on the dashboard. + final_response.mustcontain(no='Dashboard'), + final_response.mustcontain( + no='{0}'.format(user["fullname"]) + ) + + +@pytest.mark.usefixtures("clean_db") +def test_user_logout_url_redirect(app): + """_logout url redirects to logged out page. + + Note: this doesn't test the actual logout of a logged in user, just + the associated redirect. + """ + + logout_url = url_for("user.logout") + logout_response = app.get(logout_url, status=302) + final_response = helpers.webtest_maybe_follow(logout_response) + + assert "You are now logged out." in final_response + + +@pytest.mark.ckan_config("ckan.root_path", "/my/prefix") +@pytest.mark.usefixtures("clean_db") +def test_non_root_user_logout_url_redirect(app): + """ + _logout url redirects to logged out page with `ckan.root_path` + prefixed. + + Note: this doesn't test the actual logout of a logged in user, just + the associated redirect. + """ + + logout_url = url_for("user.logout") + # Remove the prefix otherwise the test app won't find the correct route + logout_url = logout_url.replace("/my/prefix", "") + logout_response = app.get(logout_url, status=302) + assert logout_response.status_int == 302 + assert "/my/prefix/user/logout" in logout_response.location + + +@pytest.mark.usefixtures("clean_db") +def test_not_logged_in_dashboard(app): + + for route in ["index", "organizations", "datasets", "groups"]: + app.get(url=url_for(u"dashboard.{}".format(route)), status=403) + + +@pytest.mark.usefixtures("clean_db") +def test_own_datasets_show_up_on_user_dashboard(app): + user = factories.User() + dataset_title = "My very own dataset" + factories.Dataset(user=user, name="my-own-dataset", title=dataset_title) + + env = {"REMOTE_USER": user["name"].encode("ascii")} + response = app.get(url=url_for("dashboard.datasets"), extra_environ=env) + + assert dataset_title in response + + +@pytest.mark.usefixtures("clean_db") +def test_other_datasets_dont_show_up_on_user_dashboard(app): + user1 = factories.User() + user2 = factories.User() + dataset_title = "Someone else's dataset" + factories.Dataset( + user=user1, name="someone-elses-dataset", title=dataset_title + ) + + env = {"REMOTE_USER": user2["name"].encode("ascii")} + response = app.get(url=url_for("dashboard.datasets"), extra_environ=env) + + assert not (dataset_title in response) + + +@pytest.mark.usefixtures("clean_db") +def test_user_edit_no_user(app): + + response = app.get(url_for("user.edit", id=None), status=400) + assert "No user specified" in response + + +@pytest.mark.usefixtures("clean_db") +def test_user_edit_unknown_user(app): + """Attempt to read edit user for an unknown user redirects to login + page.""" + + response = app.get(url_for("user.edit", id="unknown_person"), status=403) + + +@pytest.mark.usefixtures("clean_db") +def test_user_edit_not_logged_in(app): + """Attempt to read edit user for an existing, not-logged in user + redirects to login page.""" + + user = factories.User() + username = user["name"] + response = app.get(url_for("user.edit", id=username), status=403) + + +@pytest.mark.usefixtures("clean_db") +def test_edit_user(app): + user = factories.User(password="TestPassword1") + + env = {"REMOTE_USER": user["name"].encode("ascii")} + response = app.get(url=url_for("user.edit"), extra_environ=env) + # existing values in the form + form = response.forms["user-edit-form"] + assert form["name"].value == user["name"] + assert form["fullname"].value == user["fullname"] + assert form["email"].value == user["email"] + assert form["about"].value == user["about"] + assert form["activity_streams_email_notifications"].value is None + assert form["password1"].value == "" + assert form["password2"].value == "" + + # new values + # form['name'] = 'new-name' + form["fullname"] = "new full name" + form["email"] = "new@example.com" + form["about"] = "new about" + form["activity_streams_email_notifications"] = True + form["old_password"] = "TestPassword1" + form["password1"] = "NewPass1" + form["password2"] = "NewPass1" + response = submit_and_follow(app, form, env, "save") + + user = model.Session.query(model.User).get(user["id"]) + # assert(user.name== 'new-name') + assert user.fullname == "new full name" + assert user.email == "new@example.com" + assert user.about == "new about" + assert user.activity_streams_email_notifications + + +@pytest.mark.usefixtures("clean_db") +def test_email_change_without_password(app): + + env, response, user = _get_user_edit_page(app) + + form = response.forms["user-edit-form"] + + # new values + form["email"] = "new@example.com" + + # factory returns user with password 'pass' + form.fields["old_password"][0].value = "Wrong-pass1" + + response = webtest_submit(form, "save", status=200, extra_environ=env) + assert "Old Password: incorrect password" in response + + +@pytest.mark.usefixtures("clean_db") +def test_email_change_with_password(app): + + env, response, user = _get_user_edit_page(app) + + form = response.forms["user-edit-form"] + + # new values + form["email"] = "new@example.com" + + # factory returns user with password 'pass' + form.fields["old_password"][0].value = "RandomPassword123" + + response = submit_and_follow(app, form, env, "save") + assert "Profile updated" in response + + +@pytest.mark.usefixtures("clean_db") +def test_edit_user_logged_in_username_change(app): + + user_pass = "TestPassword1" + user = factories.User(password=user_pass) + + # Have to do an actual login as this test relys on repoze cookie handling. + # get the form + response = app.get("/user/login") + # ...it's the second one + login_form = response.forms[1] + # fill it in + login_form["login"] = user["name"] + login_form["password"] = user_pass + # submit it + login_form.submit() + + # Now the cookie is set, run the test + response = app.get(url=url_for("user.edit")) + # existing values in the form + form = response.forms["user-edit-form"] + + # new values + form["name"] = "new-name" + env = {"REMOTE_USER": user["name"].encode("ascii")} + response = webtest_submit(form, "save", status=200, extra_environ=env) + assert "That login name can not be modified" in response + + +@pytest.mark.usefixtures("clean_db") +def test_edit_user_logged_in_username_change_by_name(app): + user_pass = "TestPassword1" + user = factories.User(password=user_pass) + + # Have to do an actual login as this test relys on repoze cookie handling. + # get the form + response = app.get("/user/login") + # ...it's the second one + login_form = response.forms[1] + # fill it in + login_form["login"] = user["name"] + login_form["password"] = user_pass + # submit it + login_form.submit() + + # Now the cookie is set, run the test + response = app.get(url=url_for("user.edit", id=user["name"])) + # existing values in the form + form = response.forms["user-edit-form"] + + # new values + form["name"] = "new-name" + env = {"REMOTE_USER": user["name"].encode("ascii")} + response = webtest_submit(form, "save", status=200, extra_environ=env) + assert "That login name can not be modified" in response + + +@pytest.mark.usefixtures("clean_db") +def test_edit_user_logged_in_username_change_by_id(app): + user_pass = "TestPassword1" + user = factories.User(password=user_pass) + + # Have to do an actual login as this test relys on repoze cookie handling. + # get the form + response = app.get("/user/login") + # ...it's the second one + login_form = response.forms[1] + # fill it in + login_form["login"] = user["name"] + login_form["password"] = user_pass + # submit it + login_form.submit() + + # Now the cookie is set, run the test + response = app.get(url=url_for("user.edit", id=user["id"])) + # existing values in the form + form = response.forms["user-edit-form"] + + # new values + form["name"] = "new-name" + env = {"REMOTE_USER": user["name"].encode("ascii")} + response = webtest_submit(form, "save", status=200, extra_environ=env) + assert "That login name can not be modified" in response + + +@pytest.mark.usefixtures("clean_db") +def test_perform_reset_for_key_change(app): + password = "TestPassword1" + params = {"password1": password, "password2": password} + user = factories.User() + user_obj = helpers.model.User.by_name(user["name"]) + create_reset_key(user_obj) + key = user_obj.reset_key + + offset = url_for( + controller="user", + action="perform_reset", + id=user_obj.id, + key=user_obj.reset_key, + ) + response = app.post(offset, params=params, status=302) + user_obj = helpers.model.User.by_name(user["name"]) # Update user_obj + + assert key != user_obj.reset_key + + +@pytest.mark.usefixtures("clean_db") +def test_password_reset_correct_password(app): + """ + user password reset attempted with correct old password + """ + + env, response, user = _get_user_edit_page(app) + + form = response.forms["user-edit-form"] + + # factory returns user with password 'RandomPassword123' + form.fields["old_password"][0].value = "RandomPassword123" + form.fields["password1"][0].value = "NewPassword1" + form.fields["password2"][0].value = "NewPassword1" + + response = submit_and_follow(app, form, env, "save") + assert "Profile updated" in response + + +@pytest.mark.usefixtures("clean_db") +def test_password_reset_incorrect_password(app): + """ + user password reset attempted with invalid old password + """ + + env, response, user = _get_user_edit_page(app) + + form = response.forms["user-edit-form"] + + # factory returns user with password 'RandomPassword123' + form.fields["old_password"][0].value = "Wrong-Pass1" + form.fields["password1"][0].value = "NewPassword1" + form.fields["password2"][0].value = "NewPassword1" + + response = webtest_submit(form, "save", status=200, extra_environ=env) + assert "Old Password: incorrect password" in response + + +@pytest.mark.usefixtures("clean_db") +def test_user_follow(app): + + user_one = factories.User() + user_two = factories.User() + + env = {"REMOTE_USER": user_one["name"].encode("ascii")} + follow_url = url_for(controller="user", action="follow", id=user_two["id"]) + response = app.post(follow_url, extra_environ=env, status=302) + response = response.follow() + assert ( + "You are now following {0}".format(user_two["display_name"]) + in response + ) + + +@pytest.mark.usefixtures("clean_db") +def test_user_follow_not_exist(app): + """Pass an id for a user that doesn't exist""" + + user_one = factories.User() + + env = {"REMOTE_USER": user_one["name"].encode("ascii")} + follow_url = url_for(controller="user", action="follow", id="not-here") + response = app.post(follow_url, extra_environ=env, status=302) + response = response.follow(status=302) + assert "user/login" in response.headers["location"] + + +@pytest.mark.usefixtures("clean_db") +def test_user_unfollow(app): + + user_one = factories.User() + user_two = factories.User() + + env = {"REMOTE_USER": user_one["name"].encode("ascii")} + follow_url = url_for(controller="user", action="follow", id=user_two["id"]) + app.post(follow_url, extra_environ=env, status=302) + + unfollow_url = url_for("user.unfollow", id=user_two["id"]) + unfollow_response = app.post(unfollow_url, extra_environ=env, status=302) + unfollow_response = unfollow_response.follow() + + assert ( + "You are no longer following {0}".format(user_two["display_name"]) + in unfollow_response + ) + + +@pytest.mark.usefixtures("clean_db") +def test_user_unfollow_not_following(app): + """Unfollow a user not currently following""" + + user_one = factories.User() + user_two = factories.User() + + env = {"REMOTE_USER": user_one["name"].encode("ascii")} + unfollow_url = url_for("user.unfollow", id=user_two["id"]) + unfollow_response = app.post(unfollow_url, extra_environ=env, status=302) + unfollow_response = unfollow_response.follow() + + assert ( + "You are not following {0}".format(user_two["id"]) in unfollow_response + ) + + +@pytest.mark.usefixtures("clean_db") +def test_user_unfollow_not_exist(app): + """Unfollow a user that doesn't exist.""" + + user_one = factories.User() + + env = {"REMOTE_USER": user_one["name"].encode("ascii")} + unfollow_url = url_for("user.unfollow", id="not-here") + unfollow_response = app.post(unfollow_url, extra_environ=env, status=302) + unfollow_response = unfollow_response.follow(status=302) + assert "user/login" in unfollow_response.headers["location"] + + +@pytest.mark.usefixtures("clean_db") +def test_user_follower_list(app): + """Following users appear on followers list page.""" + + user_one = factories.Sysadmin() + user_two = factories.User() + + env = {"REMOTE_USER": user_one["name"].encode("ascii")} + follow_url = url_for(controller="user", action="follow", id=user_two["id"]) + app.post(follow_url, extra_environ=env, status=302) + + followers_url = url_for("user.followers", id=user_two["id"]) + + # Only sysadmins can view the followers list pages + followers_response = app.get(followers_url, extra_environ=env, status=200) + assert user_one["display_name"] in followers_response + + +@pytest.mark.usefixtures("clean_db") +def test_user_page_anon_access(app): + """Anon users can access the user list page""" + + user_url = url_for("user.index") + user_response = app.get(user_url, status=200) + assert "All Users - CKAN" in user_response + + +@pytest.mark.usefixtures("clean_db") +def test_user_page_lists_users(app): + """/users/ lists registered users""" + initial_user_count = model.User.count() + factories.User(fullname="User One") + factories.User(fullname="User Two") + factories.User(fullname="User Three") + + user_url = url_for("user.index") + user_response = app.get(user_url, status=200) + + user_response_html = BeautifulSoup(user_response.body) + user_list = user_response_html.select("ul.user-list li") + assert len(user_list) == 3 + initial_user_count + + user_names = [u.text.strip() for u in user_list] + assert "User One" in user_names + assert "User Two" in user_names + assert "User Three" in user_names + + +@pytest.mark.usefixtures("clean_db") +def test_user_page_doesnot_list_deleted_users(app): + """/users/ doesn't list deleted users""" + initial_user_count = model.User.count() + + factories.User(fullname="User One", state="deleted") + factories.User(fullname="User Two") + factories.User(fullname="User Three") + + user_url = url_for("user.index") + user_response = app.get(user_url, status=200) + + user_response_html = BeautifulSoup(user_response.body) + user_list = user_response_html.select("ul.user-list li") + assert len(user_list) == 2 + initial_user_count + + user_names = [u.text.strip() for u in user_list] + assert "User One" not in user_names + assert "User Two" in user_names + assert "User Three" in user_names + + +@pytest.mark.usefixtures("clean_db") +def test_user_page_anon_search(app): + """Anon users can search for users by username.""" + + factories.User(fullname="User One", email="useroneemail@example.com") + factories.User(fullname="Person Two") + factories.User(fullname="Person Three") + + user_url = url_for("user.index") + user_response = app.get(user_url, status=200) + search_form = user_response.forms["user-search-form"] + search_form["q"] = "Person" + search_response = webtest_submit(search_form, status=200) + + search_response_html = BeautifulSoup(search_response.body) + user_list = search_response_html.select("ul.user-list li") + assert len(user_list) == 2 + + user_names = [u.text.strip() for u in user_list] + assert "Person Two" in user_names + assert "Person Three" in user_names + assert "User One" not in user_names + + +@pytest.mark.usefixtures("clean_db") +def test_user_page_anon_search_not_by_email(app): + """Anon users can not search for users by email.""" + + factories.User(fullname="User One", email="useroneemail@example.com") + factories.User(fullname="Person Two") + factories.User(fullname="Person Three") + + user_url = url_for("user.index") + user_response = app.get(user_url, status=200) + search_form = user_response.forms["user-search-form"] + search_form["q"] = "useroneemail@example.com" + search_response = webtest_submit(search_form, status=200) + + search_response_html = BeautifulSoup(search_response.body) + user_list = search_response_html.select("ul.user-list li") + assert len(user_list) == 0 + + +@pytest.mark.usefixtures("clean_db") +def test_user_page_sysadmin_user(app): + """Sysadmin can search for users by email.""" + + sysadmin = factories.Sysadmin() + + factories.User(fullname="User One", email="useroneemail@example.com") + factories.User(fullname="Person Two") + factories.User(fullname="Person Three") + + env = {"REMOTE_USER": sysadmin["name"].encode("ascii")} + user_url = url_for("user.index") + user_response = app.get(user_url, status=200, extra_environ=env) + search_form = user_response.forms["user-search-form"] + search_form["q"] = "useroneemail@example.com" + search_response = webtest_submit( + search_form, status=200, extra_environ=env + ) + + search_response_html = BeautifulSoup(search_response.body) + user_list = search_response_html.select("ul.user-list li") + assert len(user_list) == 1 + assert user_list[0].text.strip() == "User One" + + +@pytest.mark.usefixtures("clean_db") +def test_simple(app): + """Checking the template shows the activity stream.""" + + user = factories.User() + + url = url_for("user.activity", id=user["id"]) + response = app.get(url) + assert "Mr. Test User" in response + assert "signed up" in response + + +@pytest.mark.usefixtures("clean_db") +def test_create_user(app): + + user = factories.User() + + url = url_for("user.activity", id=user["id"]) + response = app.get(url) + assert 'Mr. Test User'.format(user["name"]) in response + assert "signed up" in response + + +@pytest.mark.usefixtures("clean_db") +def test_change_user(app): + + user = factories.User() + _clear_activities() + user["fullname"] = "Mr. Changed Name" + helpers.call_action("user_update", context={"user": user["name"]}, **user) + + url = url_for("user.activity", id=user["id"]) + response = app.get(url) + assert ( + 'Mr. Changed Name'.format(user["name"]) in response + ) + assert "updated their profile" in response + + +@pytest.mark.usefixtures("clean_db") +def test_create_dataset(app): + + user = factories.User() + _clear_activities() + dataset = factories.Dataset(user=user) + + url = url_for("user.activity", id=user["id"]) + response = app.get(url) + assert 'Mr. Test User'.format(user["name"]) in response + assert "created the dataset" in response + assert ( + 'Test Dataset'.format(dataset["id"]) in response + ) + + +@pytest.mark.usefixtures("clean_db") +def test_change_dataset(app): + + user = factories.User() + dataset = factories.Dataset(user=user) + _clear_activities() + dataset["title"] = "Dataset with changed title" + helpers.call_action( + "package_update", context={"user": user["name"]}, **dataset + ) + + url = url_for("user.activity", id=user["id"]) + response = app.get(url) + assert 'Mr. Test User'.format(user["name"]) in response + assert "updated the dataset" in response + assert ( + 'Dataset with changed title'.format( + dataset["id"] ) - # existing values in the form - form = response.forms['user-edit-form'] - - # new values - form['name'] = 'new-name' - env = {'REMOTE_USER': user['name'].encode('ascii')} - response = webtest_submit(form, 'save', status=200, extra_environ=env) - assert_true('That login name can not be modified' in response) - - def test_perform_reset_for_key_change(self): - password = 'TestPassword1' - params = {'password1': password, 'password2': password} - user = factories.User() - user_obj = helpers.model.User.by_name(user['name']) - create_reset_key(user_obj) - key = user_obj.reset_key - - app = self._get_test_app() - offset = url_for(controller='user', - action='perform_reset', - id=user_obj.id, - key=user_obj.reset_key) - response = app.post(offset, params=params, status=302) - user_obj = helpers.model.User.by_name(user['name']) # Update user_obj - - assert_true(key != user_obj.reset_key) - - def test_password_reset_correct_password(self): - """ - user password reset attempted with correct old password - """ - app = self._get_test_app() - env, response, user = _get_user_edit_page(app) - - form = response.forms['user-edit-form'] - - # factory returns user with password 'RandomPassword123' - form.fields['old_password'][0].value = 'RandomPassword123' - form.fields['password1'][0].value = 'NewPassword1' - form.fields['password2'][0].value = 'NewPassword1' - - response = submit_and_follow(app, form, env, 'save') - assert_true('Profile updated' in response) - - def test_password_reset_incorrect_password(self): - """ - user password reset attempted with invalid old password - """ - - app = self._get_test_app() - env, response, user = _get_user_edit_page(app) - - form = response.forms['user-edit-form'] - - # factory returns user with password 'RandomPassword123' - form.fields['old_password'][0].value = 'Wrong-Pass1' - form.fields['password1'][0].value = 'NewPassword1' - form.fields['password2'][0].value = 'NewPassword1' - - response = webtest_submit(form, 'save', status=200, extra_environ=env) - assert_true('Old Password: incorrect password' in response) - - -class TestUserFollow(helpers.FunctionalTestBase): - - def test_user_follow(self): - app = self._get_test_app() - - user_one = factories.User() - user_two = factories.User() - - env = {'REMOTE_USER': user_one['name'].encode('ascii')} - follow_url = url_for(controller='user', - action='follow', - id=user_two['id']) - response = app.post(follow_url, extra_environ=env, status=302) - response = response.follow() - assert_true('You are now following {0}' - .format(user_two['display_name']) - in response) - - def test_user_follow_not_exist(self): - '''Pass an id for a user that doesn't exist''' - app = self._get_test_app() - - user_one = factories.User() - - env = {'REMOTE_USER': user_one['name'].encode('ascii')} - follow_url = url_for(controller='user', - action='follow', - id='not-here') - response = app.post(follow_url, extra_environ=env, status=302) - response = response.follow(status=302) - assert_in('user/login', response.headers['location']) - - def test_user_unfollow(self): - app = self._get_test_app() - - user_one = factories.User() - user_two = factories.User() - - env = {'REMOTE_USER': user_one['name'].encode('ascii')} - follow_url = url_for(controller='user', - action='follow', - id=user_two['id']) - app.post(follow_url, extra_environ=env, status=302) - - unfollow_url = url_for('user.unfollow', - id=user_two['id']) - unfollow_response = app.post(unfollow_url, extra_environ=env, - status=302) - unfollow_response = unfollow_response.follow() - - assert_true('You are no longer following {0}' - .format(user_two['display_name']) - in unfollow_response) - - def test_user_unfollow_not_following(self): - '''Unfollow a user not currently following''' - app = self._get_test_app() - - user_one = factories.User() - user_two = factories.User() - - env = {'REMOTE_USER': user_one['name'].encode('ascii')} - unfollow_url = url_for('user.unfollow', - id=user_two['id']) - unfollow_response = app.post(unfollow_url, extra_environ=env, - status=302) - unfollow_response = unfollow_response.follow() - - assert_true('You are not following {0}'.format(user_two['id']) - in unfollow_response) - - def test_user_unfollow_not_exist(self): - '''Unfollow a user that doesn't exist.''' - app = self._get_test_app() - - user_one = factories.User() - - env = {'REMOTE_USER': user_one['name'].encode('ascii')} - unfollow_url = url_for('user.unfollow', - id='not-here') - unfollow_response = app.post(unfollow_url, extra_environ=env, - status=302) - unfollow_response = unfollow_response.follow(status=302) - assert_in('user/login', unfollow_response.headers['location']) - - def test_user_follower_list(self): - '''Following users appear on followers list page.''' - app = self._get_test_app() - - user_one = factories.Sysadmin() - user_two = factories.User() - - env = {'REMOTE_USER': user_one['name'].encode('ascii')} - follow_url = url_for(controller='user', - action='follow', - id=user_two['id']) - app.post(follow_url, extra_environ=env, status=302) - - followers_url = url_for('user.followers', - id=user_two['id']) - - # Only sysadmins can view the followers list pages - followers_response = app.get(followers_url, extra_environ=env, - status=200) - assert_true(user_one['display_name'] in followers_response) - - -class TestUserSearch(helpers.FunctionalTestBase): - - def test_user_page_anon_access(self): - '''Anon users can access the user list page''' - app = self._get_test_app() - - user_url = url_for('user.index') - user_response = app.get(user_url, status=200) - assert_true('All Users - CKAN' - in user_response) - - def test_user_page_lists_users(self): - '''/users/ lists registered users''' - app = self._get_test_app() - factories.User(fullname='User One') - factories.User(fullname='User Two') - factories.User(fullname='User Three') - - user_url = url_for('user.index') - user_response = app.get(user_url, status=200) - - user_response_html = BeautifulSoup(user_response.body) - user_list = user_response_html.select('ul.user-list li') - assert_equal(len(user_list), 3) - - user_names = [u.text.strip() for u in user_list] - assert_true('User One' in user_names) - assert_true('User Two' in user_names) - assert_true('User Three' in user_names) - - def test_user_page_doesnot_list_deleted_users(self): - '''/users/ doesn't list deleted users''' - app = self._get_test_app() - factories.User(fullname='User One', state='deleted') - factories.User(fullname='User Two') - factories.User(fullname='User Three') - - user_url = url_for('user.index') - user_response = app.get(user_url, status=200) - - user_response_html = BeautifulSoup(user_response.body) - user_list = user_response_html.select('ul.user-list li') - assert_equal(len(user_list), 2) - - user_names = [u.text.strip() for u in user_list] - assert_true('User One' not in user_names) - assert_true('User Two' in user_names) - assert_true('User Three' in user_names) - - def test_user_page_anon_search(self): - '''Anon users can search for users by username.''' - app = self._get_test_app() - factories.User(fullname='User One', email='useroneemail@example.com') - factories.User(fullname='Person Two') - factories.User(fullname='Person Three') - - user_url = url_for('user.index') - user_response = app.get(user_url, status=200) - search_form = user_response.forms['user-search-form'] - search_form['q'] = 'Person' - search_response = webtest_submit(search_form, status=200) - - search_response_html = BeautifulSoup(search_response.body) - user_list = search_response_html.select('ul.user-list li') - assert_equal(len(user_list), 2) - - user_names = [u.text.strip() for u in user_list] - assert_true('Person Two' in user_names) - assert_true('Person Three' in user_names) - assert_true('User One' not in user_names) - - def test_user_page_anon_search_not_by_email(self): - '''Anon users can not search for users by email.''' - app = self._get_test_app() - factories.User(fullname='User One', email='useroneemail@example.com') - factories.User(fullname='Person Two') - factories.User(fullname='Person Three') - - user_url = url_for('user.index') - user_response = app.get(user_url, status=200) - search_form = user_response.forms['user-search-form'] - search_form['q'] = 'useroneemail@example.com' - search_response = webtest_submit(search_form, status=200) - - search_response_html = BeautifulSoup(search_response.body) - user_list = search_response_html.select('ul.user-list li') - assert_equal(len(user_list), 0) - - def test_user_page_sysadmin_user(self): - '''Sysadmin can search for users by email.''' - app = self._get_test_app() - sysadmin = factories.Sysadmin() - - factories.User(fullname='User One', email='useroneemail@example.com') - factories.User(fullname='Person Two') - factories.User(fullname='Person Three') - - env = {'REMOTE_USER': sysadmin['name'].encode('ascii')} - user_url = url_for('user.index') - user_response = app.get(user_url, status=200, extra_environ=env) - search_form = user_response.forms['user-search-form'] - search_form['q'] = 'useroneemail@example.com' - search_response = webtest_submit(search_form, status=200, - extra_environ=env) - - search_response_html = BeautifulSoup(search_response.body) - user_list = search_response_html.select('ul.user-list li') - assert_equal(len(user_list), 1) - assert_equal(user_list[0].text.strip(), 'User One') - - -class TestActivity(helpers.FunctionalTestBase): - def test_simple(self): - '''Checking the template shows the activity stream.''' - app = self._get_test_app() - user = factories.User() - - url = url_for('user.activity', - id=user['id']) - response = app.get(url) - assert_in('Mr. Test User', response) - assert_in('signed up', response) - - def test_create_user(self): - app = self._get_test_app() - user = factories.User() - - url = url_for('user.activity', - id=user['id']) - response = app.get(url) - assert_in('Mr. Test User'.format(user['name']), - response) - assert_in('signed up', response) - - def _clear_activities(self): - model.Session.query(model.ActivityDetail).delete() - model.Session.query(model.Activity).delete() - model.Session.flush() - - def test_change_user(self): - app = self._get_test_app() - user = factories.User() - self._clear_activities() - user['fullname'] = 'Mr. Changed Name' - helpers.call_action( - 'user_update', context={'user': user['name']}, **user) - - url = url_for('user.activity', - id=user['id']) - response = app.get(url) - assert_in('Mr. Changed Name'.format(user['name']), - response) - assert_in('updated their profile', response) - - def test_create_dataset(self): - app = self._get_test_app() - user = factories.User() - self._clear_activities() - dataset = factories.Dataset(user=user) - - url = url_for('user.activity', - id=user['id']) - response = app.get(url) - assert_in('Mr. Test User'.format(user['name']), - response) - assert_in('created the dataset', response) - assert_in('Test Dataset'.format(dataset['id']), - response) - - def test_change_dataset(self): - app = self._get_test_app() - user = factories.User() - dataset = factories.Dataset(user=user) - self._clear_activities() - dataset['title'] = 'Dataset with changed title' - helpers.call_action( - 'package_update', context={'user': user['name']}, **dataset) - - url = url_for('user.activity', - id=user['id']) - response = app.get(url) - assert_in('Mr. Test User'.format(user['name']), - response) - assert_in('updated the dataset', response) - assert_in('Dataset with changed title' - .format(dataset['id']), - response) - - def test_delete_dataset(self): - app = self._get_test_app() - user = factories.User() - dataset = factories.Dataset(user=user) - self._clear_activities() - helpers.call_action( - 'package_delete', context={'user': user['name']}, **dataset) - - url = url_for('user.activity', - id=user['id']) - env = {'REMOTE_USER': user['name'].encode('ascii')} - response = app.get(url, extra_environ=env) - assert_in('Mr. Test User'.format(user['name']), - response) - assert_in('deleted the dataset', response) - assert_in('Test Dataset' - .format(dataset['id']), - response) - - def test_create_group(self): - app = self._get_test_app() - user = factories.User() - group = factories.Group(user=user) - - url = url_for('user.activity', - id=user['id']) - response = app.get(url) - assert_in('Mr. Test User'.format(user['name']), - response) - assert_in('created the group', response) - assert_in('Test Group'.format( - group['id']), response) - - def test_change_group(self): - app = self._get_test_app() - user = factories.User() - group = factories.Group(user=user) - self._clear_activities() - group['title'] = 'Group with changed title' - helpers.call_action( - 'group_update', context={'user': user['name']}, **group) - - url = url_for('user.activity', - id=user['id']) - response = app.get(url) - assert_in('Mr. Test User'.format(user['name']), - response) - assert_in('updated the group', response) - assert_in('Group with changed title' - .format(group['id']), response) - - def test_delete_group_using_group_delete(self): - app = self._get_test_app() - user = factories.User() - group = factories.Group(user=user) - self._clear_activities() - helpers.call_action( - 'group_delete', context={'user': user['name']}, **group) - - url = url_for('user.activity', - id=user['id']) - response = app.get(url) - assert_in('Mr. Test User'.format(user['name']), - response) - assert_in('deleted the group', response) - assert_in('Test Group' - .format(group['id']), response) - - def test_delete_group_by_updating_state(self): - app = self._get_test_app() - user = factories.User() - group = factories.Group(user=user) - self._clear_activities() - group['state'] = 'deleted' - helpers.call_action( - 'group_update', context={'user': user['name']}, **group) - - url = url_for('group.activity', - id=group['id']) - env = {'REMOTE_USER': user['name'].encode('ascii')} - response = app.get(url, extra_environ=env) - assert_in('Mr. Test User'.format(user['name']), - response) - assert_in('deleted the group', response) - assert_in('Test Group' - .format(group['name']), response) - - -class TestUserResetRequest(helpers.FunctionalTestBase): - @mock.patch('ckan.lib.mailer.send_reset_link') - def test_request_reset_by_email(self, send_reset_link): - user = factories.User() - app = self._get_test_app() - offset = url_for('user.request_reset') - response = app.post(offset, params=dict(user=user['email']), - status=302).follow() - - assert_in('A reset link has been emailed to you', response) - assert_equal(send_reset_link.call_args[0][0].id, user['id']) - - @mock.patch('ckan.lib.mailer.send_reset_link') - def test_request_reset_by_name(self, send_reset_link): - user = factories.User() - app = self._get_test_app() - offset = url_for('user.request_reset') - response = app.post(offset, params=dict(user=user['name']), - status=302).follow() - - assert_in('A reset link has been emailed to you', response) - assert_equal(send_reset_link.call_args[0][0].id, user['id']) - - @mock.patch('ckan.lib.mailer.send_reset_link') - def test_request_reset_when_duplicate_emails(self, send_reset_link): - user_a = factories.User(email='me@example.com') - user_b = factories.User(email='me@example.com') - app = self._get_test_app() - offset = url_for('user.request_reset') - response = app.post(offset, params=dict(user='me@example.com'), - status=302).follow() - - assert_in('A reset link has been emailed to you', response) - emailed_users = [call[0][0].name - for call in send_reset_link.call_args_list] - assert_equal(emailed_users, [user_a['name'], user_b['name']]) - - def test_request_reset_without_param(self): - app = self._get_test_app() - offset = url_for('user.request_reset') - response = app.post(offset).follow() - - assert_in('Email is required', response) - - @mock.patch('ckan.lib.mailer.send_reset_link') - def test_request_reset_for_unknown_username(self, send_reset_link): - app = self._get_test_app() - offset = url_for('user.request_reset') - response = app.post(offset, params=dict(user='unknown'), - status=302).follow() - - # doesn't reveal account does or doesn't exist - assert_in('A reset link has been emailed to you', response) - send_reset_link.assert_not_called() - - @mock.patch('ckan.lib.mailer.send_reset_link') - def test_request_reset_for_unknown_email(self, send_reset_link): - app = self._get_test_app() - offset = url_for('user.request_reset') - response = app.post(offset, params=dict(user='unknown@example.com'), - status=302).follow() - - # doesn't reveal account does or doesn't exist - assert_in('A reset link has been emailed to you', response) - send_reset_link.assert_not_called() - - @mock.patch('ckan.lib.mailer.send_reset_link') - def test_request_reset_but_mailer_not_configured(self, send_reset_link): - user = factories.User() - app = self._get_test_app() - offset = url_for('user.request_reset') - # This is the exception when the mailer is not configured: - send_reset_link.side_effect = MailerException( - 'SMTP server could not be connected to: "localhost" ' - '[Errno 111] Connection refused') - response = app.post(offset, params=dict(user=user['name']), - status=302).follow() - - assert_in('Error sending the email', response) + in response + ) + + +@pytest.mark.usefixtures("clean_db") +def test_delete_dataset(app): + + user = factories.User() + dataset = factories.Dataset(user=user) + _clear_activities() + helpers.call_action( + "package_delete", context={"user": user["name"]}, **dataset + ) + + url = url_for("user.activity", id=user["id"]) + env = {"REMOTE_USER": user["name"].encode("ascii")} + response = app.get(url, extra_environ=env) + assert 'Mr. Test User'.format(user["name"]) in response + assert "deleted the dataset" in response + assert ( + 'Test Dataset'.format(dataset["id"]) in response + ) + + +@pytest.mark.usefixtures("clean_db") +def test_create_group(app): + + user = factories.User() + group = factories.Group(user=user) + + url = url_for("user.activity", id=user["id"]) + response = app.get(url) + assert 'Mr. Test User'.format(user["name"]) in response + assert "created the group" in response + assert 'Test Group'.format(group["id"]) in response + + +@pytest.mark.usefixtures("clean_db") +def test_change_group(app): + + user = factories.User() + group = factories.Group(user=user) + _clear_activities() + group["title"] = "Group with changed title" + helpers.call_action( + "group_update", context={"user": user["name"]}, **group + ) + + url = url_for("user.activity", id=user["id"]) + response = app.get(url) + assert 'Mr. Test User'.format(user["name"]) in response + assert "updated the group" in response + assert ( + 'Group with changed title'.format(group["id"]) + in response + ) + + +@pytest.mark.usefixtures("clean_db") +def test_delete_group_using_group_delete(app): + + user = factories.User() + group = factories.Group(user=user) + _clear_activities() + helpers.call_action( + "group_delete", context={"user": user["name"]}, **group + ) + + url = url_for("user.activity", id=user["id"]) + response = app.get(url) + assert 'Mr. Test User'.format(user["name"]) in response + assert "deleted the group" in response + assert 'Test Group'.format(group["id"]) in response + + +@pytest.mark.usefixtures("clean_db") +def test_delete_group_by_updating_state(app): + + user = factories.User() + group = factories.Group(user=user) + _clear_activities() + group["state"] = "deleted" + helpers.call_action( + "group_update", context={"user": user["name"]}, **group + ) + + url = url_for("group.activity", id=group["id"]) + env = {"REMOTE_USER": user["name"].encode("ascii")} + response = app.get(url, extra_environ=env) + assert 'Mr. Test User'.format(user["name"]) in response + assert "deleted the group" in response + assert 'Test Group'.format(group["name"]) in response + + +@mock.patch("ckan.lib.mailer.send_reset_link") +@pytest.mark.usefixtures("clean_db") +def test_request_reset_by_email(send_reset_link, app): + user = factories.User() + + offset = url_for("user.request_reset") + response = app.post( + offset, params=dict(user=user["email"]), status=302 + ).follow() + + assert "A reset link has been emailed to you" in response + assert send_reset_link.call_args[0][0].id == user["id"] + + +@mock.patch("ckan.lib.mailer.send_reset_link") +@pytest.mark.usefixtures("clean_db") +def test_request_reset_by_name(send_reset_link, app): + user = factories.User() + + offset = url_for("user.request_reset") + response = app.post( + offset, params=dict(user=user["name"]), status=302 + ).follow() + + assert "A reset link has been emailed to you" in response + assert send_reset_link.call_args[0][0].id == user["id"] + + +@mock.patch("ckan.lib.mailer.send_reset_link") +@pytest.mark.usefixtures("clean_db") +def test_request_reset_when_duplicate_emails(send_reset_link, app): + user_a = factories.User(email="me@example.com") + user_b = factories.User(email="me@example.com") + + offset = url_for("user.request_reset") + response = app.post( + offset, params=dict(user="me@example.com"), status=302 + ).follow() + + assert "A reset link has been emailed to you" in response + emailed_users = [ + call[0][0].name for call in send_reset_link.call_args_list + ] + assert emailed_users == [user_a["name"], user_b["name"]] + + +@pytest.mark.usefixtures("clean_db") +def test_request_reset_without_param(app): + + offset = url_for("user.request_reset") + response = app.post(offset).follow() + + assert "Email is required" in response + + +@mock.patch("ckan.lib.mailer.send_reset_link") +@pytest.mark.usefixtures("clean_db") +def test_request_reset_for_unknown_username(send_reset_link, app): + + offset = url_for("user.request_reset") + response = app.post( + offset, params=dict(user="unknown"), status=302 + ).follow() + + # doesn't reveal account does or doesn't exist + assert "A reset link has been emailed to you" in response + send_reset_link.assert_not_called() + + +@mock.patch("ckan.lib.mailer.send_reset_link") +@pytest.mark.usefixtures("clean_db") +def test_request_reset_for_unknown_email(send_reset_link, app): + + offset = url_for("user.request_reset") + response = app.post( + offset, params=dict(user="unknown@example.com"), status=302 + ).follow() + + # doesn't reveal account does or doesn't exist + assert "A reset link has been emailed to you" in response + send_reset_link.assert_not_called() + + +@mock.patch("ckan.lib.mailer.send_reset_link") +@pytest.mark.usefixtures("clean_db") +def test_request_reset_but_mailer_not_configured(send_reset_link, app): + user = factories.User() + + offset = url_for("user.request_reset") + # This is the exception when the mailer is not configured: + send_reset_link.side_effect = MailerException( + 'SMTP server could not be connected to: "localhost" ' + "[Errno 111] Connection refused" + ) + response = app.post( + offset, params=dict(user=user["name"]), status=302 + ).follow() + + assert "Error sending the email" in response diff --git a/ckan/tests/controllers/test_util.py b/ckan/tests/controllers/test_util.py index 9e59ba9f2b2..44d5a0d7583 100644 --- a/ckan/tests/controllers/test_util.py +++ b/ckan/tests/controllers/test_util.py @@ -1,43 +1,27 @@ # encoding: utf-8 -from nose.tools import assert_equal +import pytest from ckan.lib.helpers import url_for as url_for -import ckan.tests.helpers as helpers - - -class TestUtil(helpers.FunctionalTestBase): - def test_redirect_ok(self): - app = self._get_test_app() - response = app.get( - url=url_for('util.internal_redirect'), - params={'url': '/dataset'}, - status=302, - ) - assert_equal(response.headers.get('Location'), - 'http://test.ckan.net/dataset') - - def test_redirect_external(self): - app = self._get_test_app() - response = app.get( - url=url_for('util.internal_redirect'), - params={'url': 'http://nastysite.com'}, - status=403, - ) - - def test_redirect_no_params(self): - app = self._get_test_app() - response = app.get( - url=url_for('util.internal_redirect'), - params={}, - status=400, - ) - - def test_redirect_no_params_2(self): - app = self._get_test_app() - response = app.get( - url=url_for('util.internal_redirect'), - params={'url': ''}, - status=400, - ) + +def test_redirect_ok(app): + response = app.get( + url=url_for("util.internal_redirect"), + params={"url": "/dataset"}, + status=302, + ) + assert response.headers.get("Location") == "http://test.ckan.net/dataset" + + +def test_redirect_external(app): + app.get( + url=url_for("util.internal_redirect"), + params={"url": "http://nastysite.com"}, + status=403, + ) + + +@pytest.mark.parametrize("params", [{}, {"url": ""}]) +def test_redirect_no_params(params, app): + app.get(url=url_for("util.internal_redirect"), params=params, status=400) diff --git a/ckan/tests/factories.py b/ckan/tests/factories.py index ab47cc37e2e..40aa4b5d408 100644 --- a/ckan/tests/factories.py +++ b/ckan/tests/factories.py @@ -1,6 +1,6 @@ # encoding: utf-8 -'''This is a collection of factory classes for building CKAN users, datasets, +"""This is a collection of factory classes for building CKAN users, datasets, etc. These are meant to be used by tests to create any objects that are needed for @@ -37,7 +37,7 @@ # to the factory: user = factories.User(**user_attributes_dict) -''' +""" import random import string import factory @@ -49,69 +49,69 @@ def _get_action_user_name(kwargs): - '''Return the name of the user in kwargs, defaulting to the site user + """Return the name of the user in kwargs, defaulting to the site user It can be overriden by explictly setting {'user': None} in the keyword arguments. In that case, this method will return None. - ''' + """ - if 'user' in kwargs: - user = kwargs['user'] + if "user" in kwargs: + user = kwargs["user"] else: - user = helpers.call_action('get_site_user') + user = helpers.call_action("get_site_user") if user is None: user_name = None else: - user_name = user['name'] + user_name = user["name"] return user_name def _generate_email(user): - '''Return an email address for the given User factory stub object.''' + """Return an email address for the given User factory stub object.""" - return '{0}@ckan.org'.format(user.name).lower() + return "{0}@ckan.org".format(user.name).lower() def _generate_reset_key(user): - '''Return a reset key for the given User factory stub object.''' + """Return a reset key for the given User factory stub object.""" - return '{0}_reset_key'.format(user.name).lower() + return "{0}_reset_key".format(user.name).lower() def _generate_user_id(user): - '''Return a user id for the given User factory stub object.''' + """Return a user id for the given User factory stub object.""" - return '{0}_user_id'.format(user.name).lower() + return "{0}_user_id".format(user.name).lower() def _generate_group_title(group): - '''Return a title for the given Group factory stub object.''' + """Return a title for the given Group factory stub object.""" - return group.name.replace('_', ' ').title() + return group.name.replace("_", " ").title() def _generate_random_string(length=6): - '''Return a random string of the defined length.''' + """Return a random string of the defined length.""" - return ''.join(random.sample(string.ascii_lowercase, length)) + return "".join(random.sample(string.ascii_lowercase, length)) class User(factory.Factory): - '''A factory class for creating CKAN users.''' + """A factory class for creating CKAN users.""" # This is the class that UserFactory will create and return instances # of. FACTORY_FOR = ckan.model.User # These are the default params that will be used to create new users. - fullname = 'Mr. Test User' - password = 'RandomPassword123' - about = 'Just another test user.' + fullname = "Mr. Test User" + password = "RandomPassword123" + about = "Just another test user." # Generate a different user name param for each user that gets created. - name = factory.Sequence(lambda n: 'test_user_{0:02d}'.format(n)) + name = factory.Sequence(lambda n: "test_user_{0:02d}".format(n)) # Compute the email param for each user based on the values of the other # params above. @@ -131,20 +131,20 @@ def _build(cls, target_class, *args, **kwargs): def _create(cls, target_class, *args, **kwargs): if args: assert False, "Positional args aren't supported, use keyword args." - user_dict = helpers.call_action('user_create', **kwargs) + user_dict = helpers.call_action("user_create", **kwargs) return user_dict class Resource(factory.Factory): - '''A factory class for creating CKAN resources.''' + """A factory class for creating CKAN resources.""" FACTORY_FOR = ckan.model.Resource - name = factory.Sequence(lambda n: 'test_resource_{0:02d}'.format(n)) - description = 'Just another test resource.' - format = 'res_format' - url = 'http://link.to.some.data' - package_id = factory.LazyAttribute(lambda _: Dataset()['id']) + name = factory.Sequence(lambda n: "test_resource_{0:02d}".format(n)) + description = "Just another test resource." + format = "res_format" + url = "http://link.to.some.data" + package_id = factory.LazyAttribute(lambda _: Dataset()["id"]) @classmethod def _build(cls, target_class, *args, **kwargs): @@ -155,39 +155,35 @@ def _create(cls, target_class, *args, **kwargs): if args: assert False, "Positional args aren't supported, use keyword args." - context = {'user': _get_action_user_name(kwargs)} + context = {"user": _get_action_user_name(kwargs)} - resource_dict = helpers.call_action('resource_create', context=context, - **kwargs) + resource_dict = helpers.call_action( + "resource_create", context=context, **kwargs + ) return resource_dict class ResourceView(factory.Factory): - '''A factory class for creating CKAN resource views. + """A factory class for creating CKAN resource views. Note: if you use this factory, you need to load the `image_view` plugin on your test class (and unload it later), otherwise you will get an error. Example:: - class TestSomethingWithResourceViews(object): - @classmethod - def setup_class(cls): - if not p.plugin_loaded('image_view'): - p.load('image_view') + @pytest.mark.ckan_config("ckan.plugins", "image_view") + @pytest.mark.usefixtures("with_plugins") + def test_resource_view_factory(): + ... - @classmethod - def teardown_class(cls): - p.unload('image_view') - - ''' + """ FACTORY_FOR = ckan.model.ResourceView - title = factory.Sequence(lambda n: 'test_resource_view_{0:02d}'.format(n)) - description = 'Just another test resource view.' - view_type = 'image_view' - resource_id = factory.LazyAttribute(lambda _: Resource()['id']) + title = factory.Sequence(lambda n: "test_resource_view_{0:02d}".format(n)) + description = "Just another test resource view." + view_type = "image_view" + resource_id = factory.LazyAttribute(lambda _: Resource()["id"]) @classmethod def _build(cls, target_class, *args, **kwargs): @@ -198,23 +194,24 @@ def _create(cls, target_class, *args, **kwargs): if args: assert False, "Positional args aren't supported, use keyword args." - context = {'user': _get_action_user_name(kwargs)} + context = {"user": _get_action_user_name(kwargs)} - resource_dict = helpers.call_action('resource_view_create', - context=context, **kwargs) + resource_dict = helpers.call_action( + "resource_view_create", context=context, **kwargs + ) return resource_dict class Sysadmin(factory.Factory): - '''A factory class for creating sysadmin users.''' + """A factory class for creating sysadmin users.""" FACTORY_FOR = ckan.model.User - fullname = 'Mr. Test Sysadmin' - password = 'RandomPassword123' - about = 'Just another test sysadmin.' + fullname = "Mr. Test Sysadmin" + password = "RandomPassword123" + about = "Just another test sysadmin." - name = factory.Sequence(lambda n: 'test_sysadmin_{0:02d}'.format(n)) + name = factory.Sequence(lambda n: "test_sysadmin_{0:02d}".format(n)) email = factory.LazyAttribute(_generate_email) sysadmin = True @@ -237,22 +234,24 @@ def _create(cls, target_class, *args, **kwargs): # to get one. We pass the user's name in the context because we want # the API key and other sensitive data to be returned in the user # dict. - user_dict = helpers.call_action('user_show', id=user.id, - context={'user': user.name}) + user_dict = helpers.call_action( + "user_show", id=user.id, context={"user": user.name} + ) return user_dict class Group(factory.Factory): - '''A factory class for creating CKAN groups.''' + """A factory class for creating CKAN groups.""" FACTORY_FOR = ckan.model.Group - name = factory.Sequence(lambda n: 'test_group_{0:02d}'.format(n)) + name = factory.Sequence(lambda n: "test_group_{0:02d}".format(n)) title = factory.LazyAttribute(_generate_group_title) - description = 'A test description for this test group.' + description = "A test description for this test group." - user = factory.LazyAttribute(lambda _: - helpers.call_action('get_site_user')) + user = factory.LazyAttribute( + lambda _: helpers.call_action("get_site_user") + ) @classmethod def _build(cls, target_class, *args, **kwargs): @@ -263,16 +262,16 @@ def _create(cls, target_class, *args, **kwargs): if args: assert False, "Positional args aren't supported, use keyword args." - context = {'user': _get_action_user_name(kwargs)} + context = {"user": _get_action_user_name(kwargs)} - group_dict = helpers.call_action('group_create', - context=context, - **kwargs) + group_dict = helpers.call_action( + "group_create", context=context, **kwargs + ) return group_dict class Organization(factory.Factory): - '''A factory class for creating CKAN organizations.''' + """A factory class for creating CKAN organizations.""" # This is the class that OrganizationFactory will create and return # instances of. @@ -282,12 +281,12 @@ class Organization(factory.Factory): # organizations. is_organization = True - title = 'Test Organization' - description = 'Just another test organization.' - image_url = 'http://placekitten.com/g/200/100' + title = "Test Organization" + description = "Just another test organization." + image_url = "http://placekitten.com/g/200/100" # Generate a different group name param for each user that gets created. - name = factory.Sequence(lambda n: 'test_org_{0:02d}'.format(n)) + name = factory.Sequence(lambda n: "test_org_{0:02d}".format(n)) @classmethod def _build(cls, target_class, *args, **kwargs): @@ -298,27 +297,27 @@ def _create(cls, target_class, *args, **kwargs): if args: assert False, "Positional args aren't supported, use keyword args." - context = {'user': _get_action_user_name(kwargs)} + context = {"user": _get_action_user_name(kwargs)} - kwargs.setdefault('type', 'organization') + kwargs.setdefault("type", "organization") - group_dict = helpers.call_action('organization_create', - context=context, - **kwargs) + group_dict = helpers.call_action( + "organization_create", context=context, **kwargs + ) return group_dict class Dataset(factory.Factory): - '''A factory class for creating CKAN datasets.''' + """A factory class for creating CKAN datasets.""" FACTORY_FOR = ckan.model.Package # These are the default params that will be used to create new groups. - title = 'Test Dataset' - notes = 'Just another test dataset.' + title = "Test Dataset" + notes = "Just another test dataset." # Generate a different group name param for each user that gets created. - name = factory.Sequence(lambda n: 'test_dataset_{0:02d}'.format(n)) + name = factory.Sequence(lambda n: "test_dataset_{0:02d}".format(n)) @classmethod def _build(cls, target_class, *args, **kwargs): @@ -329,23 +328,23 @@ def _create(cls, target_class, *args, **kwargs): if args: assert False, "Positional args aren't supported, use keyword args." - context = {'user': _get_action_user_name(kwargs)} + context = {"user": _get_action_user_name(kwargs)} - dataset_dict = helpers.call_action('package_create', - context=context, - **kwargs) + dataset_dict = helpers.call_action( + "package_create", context=context, **kwargs + ) return dataset_dict class MockUser(factory.Factory): - '''A factory class for creating mock CKAN users using the mock library.''' + """A factory class for creating mock CKAN users using the mock library.""" FACTORY_FOR = mock.MagicMock - fullname = 'Mr. Mock User' - password = 'pass' - about = 'Just another mock user.' - name = factory.Sequence(lambda n: 'mock_user_{0:02d}'.format(n)) + fullname = "Mr. Mock User" + password = "pass" + about = "Just another mock user." + name = factory.Sequence(lambda n: "mock_user_{0:02d}".format(n)) email = factory.LazyAttribute(_generate_email) reset_key = factory.LazyAttribute(_generate_reset_key) id = factory.LazyAttribute(_generate_user_id) @@ -365,12 +364,12 @@ def _create(cls, target_class, *args, **kwargs): class SystemInfo(factory.Factory): - '''A factory class for creating SystemInfo objects (config objects - stored in the DB).''' + """A factory class for creating SystemInfo objects (config objects + stored in the DB).""" FACTORY_FOR = ckan.model.SystemInfo - key = factory.Sequence(lambda n: 'test_config_{0:02d}'.format(n)) + key = factory.Sequence(lambda n: "test_config_{0:02d}".format(n)) value = _generate_random_string() @classmethod @@ -382,35 +381,37 @@ def _create(cls, target_class, *args, **kwargs): if args: assert False, "Positional args aren't supported, use keyword args." - ckan.model.system_info.set_system_info(kwargs['key'], - kwargs['value']) - obj = ckan.model.Session.query(ckan.model.system_info.SystemInfo) \ - .filter_by(key=kwargs['key']).first() + ckan.model.system_info.set_system_info(kwargs["key"], kwargs["value"]) + obj = ( + ckan.model.Session.query(ckan.model.system_info.SystemInfo) + .filter_by(key=kwargs["key"]) + .first() + ) return obj def validator_data_dict(): - '''Return a data dict with some arbitrary data in it, suitable to be passed + """Return a data dict with some arbitrary data in it, suitable to be passed to validator functions for testing. - ''' - return {('other key',): 'other value'} + """ + return {("other key",): "other value"} def validator_errors_dict(): - '''Return an errors dict with some arbitrary errors in it, suitable to be + """Return an errors dict with some arbitrary errors in it, suitable to be passed to validator functions for testing. - ''' - return {('other key',): ['other error']} + """ + return {("other key",): ["other error"]} class Vocabulary(factory.Factory): - '''A factory class for creating tag vocabularies.''' + """A factory class for creating tag vocabularies.""" FACTORY_FOR = ckan.model.Vocabulary - name = factory.Sequence(lambda n: 'test_vocabulary_{0:02d}'.format(n)) + name = factory.Sequence(lambda n: "test_vocabulary_{0:02d}".format(n)) @classmethod def _build(cls, target_class, *args, **kwargs): @@ -420,11 +421,11 @@ def _build(cls, target_class, *args, **kwargs): def _create(cls, target_class, *args, **kwargs): if args: assert False, "Positional args aren't supported, use keyword args." - return helpers.call_action('vocabulary_create', **kwargs) + return helpers.call_action("vocabulary_create", **kwargs) class Activity(factory.Factory): - '''A factory class for creating CKAN activity objects.''' + """A factory class for creating CKAN activity objects.""" FACTORY_FOR = ckan.model.Activity @@ -436,4 +437,4 @@ def _build(cls, target_class, *args, **kwargs): def _create(cls, target_class, *args, **kwargs): if args: assert False, "Positional args aren't supported, use keyword args." - return helpers.call_action('activity_create', **kwargs) + return helpers.call_action("activity_create", **kwargs) diff --git a/ckan/tests/helpers.py b/ckan/tests/helpers.py index 2302674a9c1..3d316ca46c2 100644 --- a/ckan/tests/helpers.py +++ b/ckan/tests/helpers.py @@ -1,11 +1,10 @@ # encoding: utf-8 -'''This is a collection of helper functions for use in tests. +"""This is a collection of helper functions for use in tests. We want to avoid sharing test helper functions between test modules as -much as possible, and we definitely don't want to share test fixtures between -test modules, or to introduce a complex hierarchy of test class subclasses, -etc. +much as possible, and we definitely don't want to introduce a complex +hierarchy of test class subclasses, etc. We want to reduce the amount of "travel" that a reader needs to undertake to understand a test method -- reducing the number of other files they need to go @@ -16,9 +15,12 @@ and make writing tests so much easier, that it's worth having them despite the potential drawbacks. +Consider using :ref:`fixtures` whenever is possible for setting up initial +state of a test or creating related objects. + This module is reserved for these very useful functions. -''' +""" import collections import contextlib @@ -41,9 +43,9 @@ def reset_db(): - '''Reset CKAN's database. + """Reset CKAN's database. - If a test class uses the database, then it should call this function in its + If a test class uses the database, then it may call this function in its ``setup()`` method to make sure that it has a clean database to start with (nothing left over from other test classes or from previous test runs). @@ -52,7 +54,7 @@ def reset_db(): :returns: ``None`` - ''' + """ # Close any database connections that have been left open. # This prevents CKAN from hanging waiting for some unclosed connection. model.Session.close_all() @@ -61,7 +63,7 @@ def reset_db(): def call_action(action_name, context=None, **kwargs): - '''Call the named ``ckan.logic.action`` function and return the result. + """Call the named ``ckan.logic.action`` function and return the result. This is just a nicer way for user code to call action functions, nicer than either calling the action function directly or via @@ -98,16 +100,16 @@ def call_action(action_name, context=None, **kwargs): :type context: dict :returns: the dict or other value that the action function returns - ''' + """ if context is None: context = {} - context.setdefault('user', '127.0.0.1') - context.setdefault('ignore_auth', True) + context.setdefault("user", "127.0.0.1") + context.setdefault("ignore_auth", True) return logic.get_action(action_name)(context=context, data_dict=kwargs) def call_auth(auth_name, context, **kwargs): - '''Call the named ``ckan.logic.auth`` function and return the result. + """Call the named ``ckan.logic.auth`` function and return the result. This is just a convenience function for tests in :py:mod:`ckan.tests.logic.auth` to use. @@ -132,50 +134,52 @@ def call_auth(auth_name, context, **kwargs): or just ``{'success': False}`` :rtype: dict - ''' - assert 'user' in context, ('Test methods must put a user name in the ' - 'context dict') - assert 'model' in context, ('Test methods must put a model in the ' - 'context dict') + """ + assert "user" in context, ( + "Test methods must put a user name in the " "context dict" + ) + assert "model" in context, ( + "Test methods must put a model in the " "context dict" + ) return logic.check_access(auth_name, context, data_dict=kwargs) class CKANTestApp(webtest.TestApp): - '''A wrapper around webtest.TestApp + """A wrapper around webtest.TestApp It adds some convenience methods for CKAN - ''' + """ _flask_app = None @property def flask_app(self): if not self._flask_app: - self._flask_app = self.app.apps['flask_app']._wsgi_app + self._flask_app = self.app.apps["flask_app"]._wsgi_app return self._flask_app def post(self, url, *args, **kwargs): - url = url.encode('utf8') # or maybe it should be url_encoded? + url = url.encode("utf8") # or maybe it should be url_encoded? return super(CKANTestApp, self).post(url, *args, **kwargs) def _get_test_app(): - '''Return a webtest.TestApp for CKAN, with legacy templates disabled. + """Return a webtest.TestApp for CKAN, with legacy templates disabled. For functional tests that need to request CKAN pages or post to the API. Unit tests shouldn't need this. - ''' - config['ckan.legacy_templates'] = False - config['testing'] = True - app = ckan.config.middleware.make_app(config['global_conf'], **config) + """ + config["ckan.legacy_templates"] = False + config["testing"] = True + app = ckan.config.middleware.make_app(config["global_conf"], **config) app = CKANTestApp(app) return app class FunctionalTestBase(object): - '''A base class for functional test classes to inherit from. + """A base class for functional test classes to inherit from. Allows configuration changes by overriding _apply_config_changes and resetting the CKAN config after your test class has run. It creates a @@ -187,23 +191,25 @@ class FunctionalTestBase(object): and teardown_class(), make sure to use super() to call this class's methods at the top of yours! - ''' + """ + @classmethod def _get_test_app(cls): # leading _ because nose is terrible # FIXME: remove this method and switch to using helpers.get_test_app # in each test once the old functional tests are fixed or removed - if not hasattr(cls, '_test_app'): + if not hasattr(cls, "_test_app"): cls._test_app = _get_test_app() return cls._test_app @classmethod def setup_class(cls): import ckan.plugins as p + # Make a copy of the Pylons config, so we can restore it in teardown. cls._original_config = dict(config) cls._apply_config_changes(config) try: - config['ckan.plugins'] = ' '.join(cls._load_plugins) + config["ckan.plugins"] = " ".join(cls._load_plugins) del cls._test_app # reload with the new plugins except AttributeError: pass @@ -214,16 +220,17 @@ def _apply_config_changes(cls, cfg): pass def setup(self): - '''Reset the database and clear the search indexes.''' + """Reset the database and clear the search indexes.""" reset_db() - if hasattr(self, '_test_app'): + if hasattr(self, "_test_app"): self._test_app.reset() search.clear_all() @classmethod def teardown_class(cls): import ckan.plugins as p - for plugin in reversed(getattr(cls, '_load_plugins', [])): + + for plugin in reversed(getattr(cls, "_load_plugins", [])): p.unload(plugin) # Restore the Pylons config to its original values, in case any tests # changed any config settings. @@ -232,13 +239,14 @@ def teardown_class(cls): class RQTestBase(object): - ''' + """ Base class for tests of RQ functionality. - ''' + """ + def setup(self): - u''' + u""" Delete all RQ queues and jobs. - ''' + """ # See https://github.com/nvie/rq/issues/731 redis_conn = connect_to_redis() for queue in rq.Queue.all(connection=redis_conn): @@ -247,9 +255,9 @@ def setup(self): redis_conn.delete(queue._key) def all_jobs(self): - u''' + u""" Get a list of all RQ jobs. - ''' + """ jobs = [] redis_conn = connect_to_redis() for queue in rq.Queue.all(connection=redis_conn): @@ -257,39 +265,49 @@ def all_jobs(self): return jobs def enqueue(self, job=None, *args, **kwargs): - u''' + u""" Enqueue a test job. - ''' + """ if job is None: job = jobs.test_job return jobs.enqueue(job, *args, **kwargs) class FunctionalRQTestBase(FunctionalTestBase, RQTestBase): - ''' + """ Base class for functional tests of RQ functionality. - ''' + """ + def setup(self): FunctionalTestBase.setup(self) RQTestBase.setup(self) -def submit_and_follow(app, form, extra_environ=None, name=None, - value=None, **args): - ''' +def submit_and_follow( + app, form, extra_environ=None, name=None, value=None, **args +): + """ Call webtest_submit with name/value passed expecting a redirect and return the response from following that redirect. - ''' - response = webtest_submit(form, name, value=value, status=302, - extra_environ=extra_environ, **args) - return app.get(url=response.headers['Location'], - extra_environ=extra_environ) + """ + response = webtest_submit( + form, + name, + value=value, + status=302, + extra_environ=extra_environ, + **args + ) + return app.get( + url=response.headers["Location"], extra_environ=extra_environ + ) # FIXME: remove webtest_* functions below when we upgrade webtest + def webtest_submit(form, name=None, index=None, value=None, **args): - ''' + """ backported version of webtest.Form.submit that actually works for submitting with different submit buttons. @@ -297,20 +315,22 @@ def webtest_submit(form, name=None, index=None, value=None, **args): on an old version of webob because we're stuck on an old version of Pylons. This prolongs our suffering, but on the bright side it lets us have functional tests that work. - ''' + """ fields = webtest_submit_fields(form, name, index=index, submit_value=value) if form.method.upper() != "GET": args.setdefault("content_type", form.enctype) - return form.response.goto(form.action, method=form.method, - params=fields, **args) + return form.response.goto( + form.action, method=form.method, params=fields, **args + ) def webtest_submit_fields(form, name=None, index=None, submit_value=None): - ''' + """ backported version of webtest.Form.submit_fields that actually works for submitting with different submit buttons. - ''' + """ from webtest.app import File + submit = [] # Use another name here so we can keep function param the same for BWC. submit_name = name @@ -329,8 +349,10 @@ def webtest_submit_fields(form, name=None, index=None, submit_value=None): if submit_name is not None and name == submit_name: if index is not None and current_index == index: submit.append((name, field.value_if_submitted())) - if submit_value is not None and \ - field.value_if_submitted() == submit_value: + if ( + submit_value is not None + and field.value_if_submitted() == submit_value + ): submit.append((name, field.value_if_submitted())) current_index += 1 else: @@ -366,7 +388,7 @@ def webtest_maybe_follow(response, **kw): def change_config(key, value): - '''Decorator to temporarily change CKAN's config to a new value + """Decorator to temporarily change CKAN's config to a new value This allows you to easily create tests that need specific config values to be set, making sure it'll be reverted to what it was originally, after your @@ -385,19 +407,22 @@ def test_ckan_site_title(self): :type value: string .. seealso:: The context manager :py:func:`changed_config` - ''' + """ + def decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): with changed_config(key, value): return func(*args, **kwargs) + return wrapper + return decorator @contextlib.contextmanager def changed_config(key, value): - ''' + """ Context manager for temporarily changing a config value. Allows you to temporarily change the value of a CKAN configuration @@ -410,7 +435,7 @@ def changed_config(key, value): assert config[u'ckan.site_title'] == u'My Test CKAN' .. seealso:: The decorator :py:func:`change_config` - ''' + """ _original_config = config.copy() config[key] = value try: @@ -421,7 +446,7 @@ def changed_config(key, value): def mock_auth(auth_function_path): - ''' + """ Decorator to easily mock a CKAN auth method in the context of a test function @@ -448,7 +473,7 @@ def test_mock_package_create(self, mock_package_create): e.g. ``ckan.logic.auth.create.package_create`` :type action_name: string - ''' + """ from ckan.authz import clear_auth_functions_cache def decorator(func): @@ -464,11 +489,12 @@ def wrapper(*args, **kwargs): return return_value return nose.tools.make_decorator(func)(wrapper) + return decorator def mock_action(action_name): - ''' + """ Decorator to easily mock a CKAN action in the context of a test function It adds a mock object for the provided action as a parameter to the test @@ -494,7 +520,8 @@ def test_mock_user_list(self, mock_user_list): e.g. ``package_create`` :type action_name: string - ''' + """ + def decorator(func): def wrapper(*args, **kwargs): mock_action = mock.MagicMock() @@ -506,10 +533,13 @@ def side_effect(called_action_name): return mock_action else: return original_get_action(called_action_name) + try: - with mock.patch('ckan.logic.get_action') as mock_get_action, \ - mock.patch('ckan.plugins.toolkit.get_action') \ - as mock_get_action_toolkit: + with mock.patch( + "ckan.logic.get_action" + ) as mock_get_action, mock.patch( + "ckan.plugins.toolkit.get_action" + ) as mock_get_action_toolkit: mock_get_action.side_effect = side_effect mock_get_action_toolkit.side_effect = side_effect @@ -521,11 +551,12 @@ def side_effect(called_action_name): return return_value return nose.tools.make_decorator(func)(wrapper) + return decorator def set_extra_environ(key, value): - '''Decorator to temporarily changes a single request environemnt value + """Decorator to temporarily changes a single request environemnt value Create a new test app and use the a side effect of making a request to set an extra_environ value. Reset the value to '' after the test. @@ -541,26 +572,33 @@ def test_ckan_thing_affected_by_script_name(self): :param value: the new extra_environ key's value, e.g. ``'/myscript'`` :type value: string - ''' + """ + def decorator(func): def wrapper(*args, **kwargs): app = _get_test_app() - app.get('/', extra_environ={key: value}) + app.get("/", extra_environ={key: value}) try: return_value = func(*args, **kwargs) finally: - app.get('/', extra_environ={key: ''}) + app.get("/", extra_environ={key: ""}) return return_value + return nose.tools.make_decorator(func)(wrapper) + return decorator @contextlib.contextmanager -def recorded_logs(logger=None, level=logging.DEBUG, - override_disabled=True, override_global_level=True): - u''' +def recorded_logs( + logger=None, + level=logging.DEBUG, + override_disabled=True, + override_global_level=True, +): + u""" Context manager for recording log messages. :param logger: The logger to record messages from. Can either be a @@ -600,7 +638,7 @@ def recorded_logs(logger=None, level=logging.DEBUG, logger.info(u'Hello, world!') logs.assert_log(u'info', u'world') - ''' + """ if logger is None: logger = logging.getLogger() elif not isinstance(logger, logging.Logger): @@ -629,7 +667,7 @@ def recorded_logs(logger=None, level=logging.DEBUG, class RecordingLogHandler(logging.Handler): - u''' + u""" Log handler that records log messages for later inspection. You can inspect the recorded messages via the ``messages`` attribute @@ -638,7 +676,8 @@ class RecordingLogHandler(logging.Handler): This class is rarely useful on its own, instead use :py:func:`recorded_logs` to temporarily record log messages. - ''' + """ + def __init__(self, *args, **kwargs): super(RecordingLogHandler, self).__init__(*args, **kwargs) self.clear() @@ -647,7 +686,7 @@ def emit(self, record): self.messages[record.levelname.lower()].append(record.getMessage()) def assert_log(self, level, pattern, msg=None): - u''' + u""" Assert that a certain message has been logged. :param string pattern: A regex which the message has to match. @@ -659,24 +698,28 @@ def assert_log(self, level, pattern, msg=None): log message was not logged. :raises AssertionError: If the expected message was not logged. - ''' + """ compiled_pattern = re.compile(pattern) for log_msg in self.messages[level]: if compiled_pattern.search(log_msg): return if not msg: if self.messages[level]: - lines = u'\n '.join(self.messages[level]) - msg = (u'Pattern "{}" was not found in the log messages for ' - + u'level "{}":\n {}').format(pattern, level, lines) + lines = u"\n ".join(self.messages[level]) + msg = ( + u'Pattern "{}" was not found in the log messages for ' + + u'level "{}":\n {}' + ).format(pattern, level, lines) else: - msg = (u'Pattern "{}" was not found in the log messages for ' - + u'level "{}" (no messages were recorded for that ' - + u'level).').format(pattern, level) + msg = ( + u'Pattern "{}" was not found in the log messages for ' + + u'level "{}" (no messages were recorded for that ' + + u"level)." + ).format(pattern, level) raise AssertionError(msg) def clear(self): - u''' + u""" Clear all captured log messages. - ''' + """ self.messages = collections.defaultdict(list) diff --git a/ckan/tests/i18n/test_check_po_files.py b/ckan/tests/i18n/test_check_po_files.py index 5243aeeaecd..82d38046d5c 100644 --- a/ckan/tests/i18n/test_check_po_files.py +++ b/ckan/tests/i18n/test_check_po_files.py @@ -1,16 +1,13 @@ # encoding: utf-8 -import nose - -from ckan.i18n.check_po_files import (check_po_file, - simple_conv_specs, - mapping_keys, - replacement_fields) - -eq_ = nose.tools.eq_ - - -PO_OK = ''' +from ckan.i18n.check_po_files import ( + check_po_file, + simple_conv_specs, + mapping_keys, + replacement_fields, +) + +PO_OK = """ #: ckan/lib/formatters.py:57 msgid "November" msgstr "Noiembrie" @@ -18,108 +15,107 @@ #: ckan/lib/formatters.py:61 msgid "December" msgstr "Decembrie" -''' +""" -PO_WRONG = ''' +PO_WRONG = """ #: ckan/templates/snippets/search_result_text.html:15 msgid "{number} dataset found for {query}" msgstr "צביר נתונים אחד נמצא עבור {query}" -''' +""" -PO_PLURALS_OK = ''' +PO_PLURALS_OK = """ #: ckan/lib/formatters.py:114 msgid "{hours} hour ago" msgid_plural "{hours} hours ago" msgstr[0] "Fa {hours} hora" msgstr[1] "Fa {hours} hores" -''' +""" -PO_WRONG_PLURALS = ''' +PO_WRONG_PLURALS = """ #: ckan/lib/formatters.py:114 msgid "{hours} hour ago" msgid_plural "{hours} hours ago" msgstr[0] "o oră în urmă" msgstr[1] "cîteva ore în urmă" msgstr[2] "{hours} ore în urmă" -''' - - -class TestCheckPoFiles(object): - - def test_basic(self): - - errors = check_po_file(PO_OK) - - eq_(errors, []) - - def test_wrong(self): - - errors = check_po_file(PO_WRONG) - - eq_(len(errors), 1) - - eq_(errors[0][0], '{number} dataset found for {query}') - - def test_plurals_ok(self): - - errors = check_po_file(PO_PLURALS_OK) - - eq_(errors, []) - - def test_wrong_plurals(self): - - errors = check_po_file(PO_WRONG_PLURALS) - - eq_(len(errors), 2) - - for error in errors: - assert error[0] in ('{hours} hour ago', '{hours} hours ago') - - -class TestValidators(object): - - def test_simple_conv_specs(self): - eq_(simple_conv_specs("Authorization function not found: %s"), - (['%s'])) - eq_(simple_conv_specs("Problem purging revision %s: %s"), - (['%s', '%s'])) - eq_(simple_conv_specs("Cannot create new entity of this type: %s %s"), - ['%s', '%s']) - eq_(simple_conv_specs("Could not read parameters: %r"), ['%r']) - eq_(simple_conv_specs("User %r not authorized to edit %r"), - (['%r', '%r'])) - eq_(simple_conv_specs( - "Please update your profile and add your email " - "address and your full name. " - "%s uses your email address if you need to reset your password."), - (['%s', '%s'])) - eq_(simple_conv_specs("You can use %sMarkdown formatting%s here."), - ['%s', '%s']) - eq_(simple_conv_specs("Name must be a maximum of %i characters long"), - ['%i']) - eq_(simple_conv_specs("Blah blah %s blah %(key)s blah %i"), - (['%s', '%i'])) - - def test_replacement_fields(self): - eq_(replacement_fields( - "{actor} added the tag {object} to the dataset {target}"), - (['{actor}', '{object}', '{target}'])) - eq_(replacement_fields("{actor} updated their profile"), ['{actor}']) - - def test_mapping_keys(self): - eq_(mapping_keys( - "You have requested your password on %(site_title)s to be reset.\n" - "\n" - "Please click the following link to confirm this request:\n" - "\n" - " %(reset_link)s\n"), - ['%(reset_link)s', '%(site_title)s']) - eq_(mapping_keys( - "The input field %(name)s was not expected."), - ['%(name)s']) - eq_(mapping_keys( - "[1:You searched for \"%(query)s\". ]%(number_of_results)s " - "datasets found."), - ['%(number_of_results)s', '%(query)s']) - eq_(mapping_keys("Blah blah %s blah %(key)s blah %i"), - (['%(key)s']), mapping_keys("Blah blah %s blah %(key)s blah %i")) +""" + + +def test_basic(): + errors = check_po_file(PO_OK) + assert errors == [] + + +def test_wrong(): + errors = check_po_file(PO_WRONG) + assert len(errors) == 1 + assert errors[0][0] == "{number} dataset found for {query}" + + +def test_plurals_ok(): + errors = check_po_file(PO_PLURALS_OK) + assert errors == [] + + +def test_wrong_plurals(): + errors = check_po_file(PO_WRONG_PLURALS) + assert len(errors) == 2 + + for error in errors: + assert error[0] in ("{hours} hour ago", "{hours} hours ago") + + +def test_simple_conv_specs(): + assert simple_conv_specs("Authorization function not found: %s") == ( + ["%s"] + ) + assert simple_conv_specs("Problem purging revision %s: %s") == ( + ["%s", "%s"] + ) + assert simple_conv_specs( + "Cannot create new entity of this type: %s %s" + ) == ["%s", "%s"] + assert simple_conv_specs("Could not read parameters: %r") == ["%r"] + assert simple_conv_specs("User %r not authorized to edit %r") == ( + ["%r", "%r"] + ) + assert simple_conv_specs( + 'Please update your profile and add your email ' + "address and your full name. " + "%s uses your email address if you need to reset your password." + ) == (["%s", "%s"]) + assert simple_conv_specs("You can use %sMarkdown formatting%s here.") == [ + "%s", + "%s", + ] + assert simple_conv_specs( + "Name must be a maximum of %i characters long" + ) == ["%i"] + assert simple_conv_specs("Blah blah %s blah %(key)s blah %i") == ( + ["%s", "%i"] + ) + + +def test_replacement_fields(): + assert replacement_fields( + "{actor} added the tag {object} to the dataset {target}" + ) == (["{actor}", "{object}", "{target}"]) + assert replacement_fields("{actor} updated their profile") == ["{actor}"] + + +def test_mapping_keys(): + assert mapping_keys( + "You have requested your password on %(site_title)s to be reset.\n" + "\n" + "Please click the following link to confirm this request:\n" + "\n" + " %(reset_link)s\n" + ) == ["%(reset_link)s", "%(site_title)s"] + assert mapping_keys("The input field %(name)s was not expected.") == [ + "%(name)s" + ] + assert mapping_keys( + '[1:You searched for "%(query)s". ]%(number_of_results)s ' + "datasets found." + ) == ["%(number_of_results)s", "%(query)s"] + assert mapping_keys("Blah blah %s blah %(key)s blah %i") == (["%(key)s"]) diff --git a/ckan/tests/legacy/__init__.py b/ckan/tests/legacy/__init__.py index 4e8340db074..0da06b7d2af 100644 --- a/ckan/tests/legacy/__init__.py +++ b/ckan/tests/legacy/__init__.py @@ -35,42 +35,46 @@ # evil hack as url_for is passed out url_for = h.url_for -__all__ = ['url_for', - 'TestController', - 'CreateTestData', - 'TestSearchIndexer', - 'CheckMethods', - 'CommonFixtureMethods', - 'TestCase', - 'SkipTest', - 'CkanServerCase', - 'call_action_api', - 'BaseCase', - 'here_dir', - 'conf_dir', - 'is_datastore_supported', - ] +__all__ = [ + "url_for", + "TestController", + "CreateTestData", + "TestSearchIndexer", + "CheckMethods", + "CommonFixtureMethods", + "TestCase", + "SkipTest", + "CkanServerCase", + "call_action_api", + "BaseCase", + "here_dir", + "conf_dir", + "is_datastore_supported", +] here_dir = os.path.dirname(os.path.abspath(__file__)) conf_dir = os.path.dirname(os.path.dirname(here_dir)) # Invoke websetup with the current config file -SetupCommand('setup-app').run([config['__file__']]) +# SetupCommand('setup-app').run([config['__file__']]) # monkey patch paste.fixtures.TestRespose # webtest (successor library) already has this # http://pythonpaste.org/webtest/#parsing-the-body def _getjson(self): return json.loads(self.body) + + paste.fixture.TestResponse.json = property(_getjson) # Check config is correct for sqlite if model.engine_is_sqlite(): - assert ckan_nose_plugin.CkanNose.settings.is_ckan, \ - 'You forgot the "--ckan" nosetest setting - see doc/test.rst' + assert ( + ckan_nose_plugin.CkanNose.settings.is_ckan + ), 'You forgot the "--ckan" nosetest setting - see doc/test.rst' -class BaseCase(object): +class BaseCase(object): def setup(self): pass @@ -80,18 +84,18 @@ def teardown(self): @staticmethod def _system(cmd): import commands + (status, output) = commands.getstatusoutput(cmd) if status: raise Exception("Couldn't execute cmd: %s: %s" % (cmd, output)) @classmethod def _paster(cls, cmd, config_path_rel): - config_path = os.path.join(config['here'], config_path_rel) - cls._system('paster --plugin ckan %s --config=%s' % (cmd, config_path)) + config_path = os.path.join(config["here"], config_path_rel) + cls._system("paster --plugin ckan %s --config=%s" % (cmd, config_path)) class CommonFixtureMethods(BaseCase): - @classmethod def create_package(self, data={}, **kwds): # Todo: A simpler method for just creating a package. @@ -99,7 +103,7 @@ def create_package(self, data={}, **kwds): @classmethod def create_user(cls, **kwds): - user = model.User(name=kwds['name']) + user = model.User(name=kwds["name"]) model.Session.add(user) model.Session.commit() model.Session.remove() @@ -137,7 +141,9 @@ def purge_packages(cls, pkg_names): @classmethod def purge_all_packages(self): - all_pkg_names = [pkg.name for pkg in model.Session.query(model.Package)] + all_pkg_names = [ + pkg.name for pkg in model.Session.query(model.Package) + ] self.purge_packages(all_pkg_names) def purge_group_by_name(self, group_name): @@ -148,42 +154,50 @@ def purge_group_by_name(self, group_name): @classmethod def clear_all_tst_ratings(self): - ratings = model.Session.query(model.Rating).filter_by(package=model.Package.by_name(u'annakarenina')).all() - ratings += model.Session.query(model.Rating).filter_by(package=model.Package.by_name(u'warandpeace')).all() + ratings = ( + model.Session.query(model.Rating) + .filter_by(package=model.Package.by_name(u"annakarenina")) + .all() + ) + ratings += ( + model.Session.query(model.Rating) + .filter_by(package=model.Package.by_name(u"warandpeace")) + .all() + ) for rating in ratings[:]: model.Session.delete(rating) model.repo.commit_and_remove() @property def war(self): - return self.get_package_by_name(u'warandpeace') + return self.get_package_by_name(u"warandpeace") @property def anna(self): - return self.get_package_by_name(u'annakarenina') + return self.get_package_by_name(u"annakarenina") @property def roger(self): - return self.get_group_by_name(u'roger') + return self.get_group_by_name(u"roger") @property def david(self): - return self.get_group_by_name(u'david') + return self.get_group_by_name(u"david") @property def russian(self): - return self.get_tag_by_name(u'russian') + return self.get_tag_by_name(u"russian") @property def tolstoy(self): - return self.get_tag_by_name(u'tolstoy') + return self.get_tag_by_name(u"tolstoy") @property def flexible_tag(self): - return self.get_tag_by_name(u'Flexible \u30a1') + return self.get_tag_by_name(u"Flexible \u30a1") -class CheckMethods(BaseCase): +class CheckMethods(BaseCase): def assert_true(self, value): assert value, "Not true: '%s'" % value @@ -191,10 +205,12 @@ def assert_false(self, value): assert not value, "Not false: '%s'" % value def assert_equal(self, value1, value2): - assert value1 == value2, 'Not equal: %s' % ((value1, value2),) + assert value1 == value2, "Not equal: %s" % ((value1, value2),) def assert_isinstance(self, value, check): - assert isinstance(value, check), 'Not an instance: %s' % ((value, check),) + assert isinstance(value, check), "Not an instance: %s" % ( + (value, check), + ) def assert_raises(self, exception_class, callable, *args, **kwds): try: @@ -202,19 +218,34 @@ def assert_raises(self, exception_class, callable, *args, **kwds): except exception_class: pass else: - assert False, "Didn't raise '%s' when calling: %s with %s" % (exception_class, callable, (args, kwds)) + assert False, "Didn't raise '%s' when calling: %s with %s" % ( + exception_class, + callable, + (args, kwds), + ) def assert_contains(self, sequence, item): - assert item in sequence, "Sequence %s does not contain item: %s" % (sequence, item) + assert item in sequence, "Sequence %s does not contain item: %s" % ( + sequence, + item, + ) def assert_missing(self, sequence, item): - assert item not in sequence, "Sequence %s does contain item: %s" % (sequence, item) + assert item not in sequence, "Sequence %s does contain item: %s" % ( + sequence, + item, + ) def assert_len(self, sequence, count): - assert len(sequence) == count, "Length of sequence %s was not %s." % (sequence, count) + assert len(sequence) == count, "Length of sequence %s was not %s." % ( + sequence, + count, + ) def assert_isinstance(self, object, kind): - assert isinstance(object, kind), "Object %s is not an instance of %s." % (object, kind) + assert isinstance( + object, kind + ), "Object %s is not an instance of %s." % (object, kind) class TestCase(CommonFixtureMethods, CheckMethods, BaseCase): @@ -228,8 +259,8 @@ def teardown(self): class WsgiAppCase(BaseCase): - wsgiapp = pylonsapp - assert wsgiapp, 'You need to run nose with --with-pylons' + # wsgiapp = pylonsapp + # assert wsgiapp, 'You need to run nose with --with-pylons' # Either that, or this file got imported somehow before the tests started # running, meaning the pylonsapp wasn't setup yet (which is done in # pylons.test.py:begin()) @@ -238,25 +269,27 @@ class WsgiAppCase(BaseCase): def config_abspath(file_path): - if os.path.isabs(file_path): - return file_path - return os.path.join(conf_dir, file_path) + if os.path.isabs(file_path): + return file_path + return os.path.join(conf_dir, file_path) + class CkanServerCase(BaseCase): @classmethod def _recreate_ckan_server_testdata(cls, config_path): - cls._paster('db clean', config_path) - cls._paster('db init', config_path) - cls._paster('create-test-data', config_path) - cls._paster('search-index rebuild', config_path) + cls._paster("db clean", config_path) + cls._paster("db init", config_path) + cls._paster("create-test-data", config_path) + cls._paster("search-index rebuild", config_path) @staticmethod def _start_ckan_server(config_file=None): if not config_file: - config_file = config['__file__'] + config_file = config["__file__"] config_path = config_abspath(config_file) import subprocess - process = subprocess.Popen(['paster', 'serve', config_path]) + + process = subprocess.Popen(["paster", "serve", config_path]) return process @staticmethod @@ -264,11 +297,14 @@ def _stop_ckan_server(process): pid = process.pid pid = int(pid) if os.system("kill -9 %d" % pid): - raise Exception("Can't kill foreign CKAN instance (pid: %d)." % pid) - + raise Exception( + "Can't kill foreign CKAN instance (pid: %d)." % pid + ) -class TestController(CommonFixtureMethods, CkanServerCase, WsgiAppCase, BaseCase): +class TestController( + CommonFixtureMethods, CkanServerCase, WsgiAppCase, BaseCase +): def assert_equal(self, *args, **kwds): assert_equal(*args, **kwds) @@ -280,20 +316,21 @@ def clear_language_setting(self): class TestSearchIndexer: - ''' + """ Tests which use search can use this object to provide indexing Usage: self.tsi = TestSearchIndexer() (create packages) self.tsi.index() (do searching) - ''' + """ def __init__(self): from ckan import plugins + if not is_search_supported(): raise SkipTest("Search not supported") - plugins.load('synchronous_search') + plugins.load("synchronous_search") @classmethod def index(cls): @@ -301,45 +338,58 @@ def index(cls): @classmethod def list(cls): - return [model.Package.get(pkg_index.package_id).name for pkg_index in model.Session.query(model.PackageSearch)] + return [ + model.Package.get(pkg_index.package_id).name + for pkg_index in model.Session.query(model.PackageSearch) + ] + def setup_test_search_index(): - #from ckan import plugins + # from ckan import plugins if not is_search_supported(): raise SkipTest("Search not supported") search.clear_all() - #plugins.load('synchronous_search') + # plugins.load('synchronous_search') + def is_search_supported(): is_supported_db = not model.engine_is_sqlite() return is_supported_db + def are_foreign_keys_supported(): return not model.engine_is_sqlite() + def is_regex_supported(): is_supported_db = not model.engine_is_sqlite() return is_supported_db + def is_migration_supported(): is_supported_db = not model.engine_is_sqlite() return is_supported_db + def is_datastore_supported(): # we assume that the datastore uses the same db engine that ckan uses is_supported_db = model.engine_is_pg() return is_supported_db + def regex_related(test): def skip_test(*args): raise SkipTest("Regex not supported") + if not is_regex_supported(): return make_decorator(test)(skip_test) return test + def clear_flash(res=None): messages = h._flash.pop_messages() + class StatusCodes: STATUS_200_OK = 200 STATUS_201_CREATED = 201 @@ -350,7 +400,7 @@ class StatusCodes: def call_action_api(app, action, apikey=None, status=200, **kwargs): - '''POST an HTTP request to the CKAN API and return the result. + """POST an HTTP request to the CKAN API and return the result. Any additional keyword arguments that you pass to this function as **kwargs are posted as params to the API. @@ -396,16 +446,22 @@ def call_action_api(app, action, apikey=None, status=200, **kwargs): :returns: the 'result' or 'error' dictionary from the CKAN API response :rtype: dictionary - ''' + """ params = json.dumps(kwargs) - response = app.post('/api/action/{0}'.format(action), params=params, - extra_environ={'Authorization': str(apikey)}, status=status) - assert '/api/3/action/help_show?name={0}'.format(action) \ - in response.json['help'] + response = app.post( + "/api/action/{0}".format(action), + params=params, + extra_environ={"Authorization": str(apikey)}, + status=status, + ) + assert ( + "/api/3/action/help_show?name={0}".format(action) + in response.json["help"] + ) if status in (200,): - assert response.json['success'] is True - return response.json['result'] + assert response.json["success"] is True + return response.json["result"] else: - assert response.json['success'] is False - return response.json['error'] + assert response.json["success"] is False + return response.json["error"] diff --git a/ckan/tests/legacy/ckantestplugins.py b/ckan/tests/legacy/ckantestplugins.py index a2cc8fa2472..7380e649a63 100644 --- a/ckan/tests/legacy/ckantestplugins.py +++ b/ckan/tests/legacy/ckantestplugins.py @@ -12,17 +12,20 @@ class MapperPlugin(p.SingletonPlugin): def __init__(self, *args, **kw): self.calls = [] + def _get_instance_name(self, instance): + return getattr(instance, "name", None) + def before_insert(self, mapper, conn, instance): - self.calls.append(('before_insert', instance.name)) + self.calls.append(("before_insert", self._get_instance_name(instance))) def after_insert(self, mapper, conn, instance): - self.calls.append(('after_insert', instance.name)) + self.calls.append(("after_insert", self._get_instance_name(instance))) def before_delete(self, mapper, conn, instance): - self.calls.append(('before_delete', instance.name)) + self.calls.append(("before_delete", self._get_instance_name(instance))) def after_delete(self, mapper, conn, instance): - self.calls.append(('after_delete', instance.name)) + self.calls.append(("after_delete", self._get_instance_name(instance))) class MapperPlugin2(MapperPlugin): @@ -42,6 +45,7 @@ def before_insert(self, mapper, conn, instance): def before_delete(self, mapper, conn, instance): self.deleted.append(instance) + class RoutesPlugin(p.SingletonPlugin): p.implements(p.IRoutes, inherit=True) @@ -49,28 +53,31 @@ def __init__(self, *args, **kw): self.calls_made = [] def before_map(self, map): - self.calls_made.append('before_map') + self.calls_made.append("before_map") return map def after_map(self, map): - self.calls_made.append('after_map') + self.calls_made.append("after_map") return map class PluginObserverPlugin(mock_plugin.MockSingletonPlugin): p.implements(p.IPluginObserver) + class ActionPlugin(p.SingletonPlugin): p.implements(p.IActions) def get_actions(self): - return {'status_show': lambda context, data_dict: {}} + return {"status_show": lambda context, data_dict: {}} + class AuthPlugin(p.SingletonPlugin): p.implements(p.IAuthFunctions) def get_auth_functions(self): - return {'package_list': lambda context, data_dict: {}} + return {"package_list": lambda context, data_dict: {}} + class MockGroupControllerPlugin(p.SingletonPlugin): p.implements(p.IGroupController) @@ -79,19 +86,19 @@ def __init__(self, *args, **kw): self.calls = defaultdict(int) def read(self, entity): - self.calls['read'] += 1 + self.calls["read"] += 1 def create(self, entity): - self.calls['create'] += 1 + self.calls["create"] += 1 def edit(self, entity): - self.calls['edit'] += 1 + self.calls["edit"] += 1 def delete(self, entity): - self.calls['delete'] += 1 + self.calls["delete"] += 1 def before_view(self, data_dict): - self.calls['before_view'] += 1 + self.calls["before_view"] += 1 return data_dict @@ -102,56 +109,55 @@ def __init__(self, *args, **kw): self.calls = defaultdict(int) def read(self, entity): - self.calls['read'] += 1 + self.calls["read"] += 1 def create(self, entity): - self.calls['create'] += 1 + self.calls["create"] += 1 def edit(self, entity): - self.calls['edit'] += 1 + self.calls["edit"] += 1 def delete(self, entity): - self.calls['delete'] += 1 + self.calls["delete"] += 1 def before_search(self, search_params): - self.calls['before_search'] += 1 + self.calls["before_search"] += 1 return search_params def after_search(self, search_results, search_params): - self.calls['after_search'] += 1 + self.calls["after_search"] += 1 return search_results def before_index(self, data_dict): - self.calls['before_index'] += 1 + self.calls["before_index"] += 1 return data_dict def before_view(self, data_dict): - self.calls['before_view'] += 1 + self.calls["before_view"] += 1 return data_dict def after_create(self, context, data_dict): - self.calls['after_create'] += 1 - self.id_in_dict = 'id' in data_dict + self.calls["after_create"] += 1 + self.id_in_dict = "id" in data_dict return data_dict def after_update(self, context, data_dict): - self.calls['after_update'] += 1 + self.calls["after_update"] += 1 return data_dict def after_delete(self, context, data_dict): - self.calls['after_delete'] += 1 + self.calls["after_delete"] += 1 return data_dict def after_show(self, context, data_dict): - self.calls['after_show'] += 1 + self.calls["after_show"] += 1 return data_dict def update_facet_titles(self, facet_titles): return facet_titles - class MockResourcePreviewExtension(mock_plugin.MockSingletonPlugin): p.implements(p.IResourcePreview) @@ -159,22 +165,22 @@ def __init__(self, *args, **kw): self.calls = defaultdict(int) def setup_template_variables(self, context, data_dict): - self.calls['setup_template_variables'] += 1 + self.calls["setup_template_variables"] += 1 def can_preview(self, data_dict): - assert(isinstance(data_dict['resource'], dict)) - assert(isinstance(data_dict['package'], dict)) - assert('on_same_domain' in data_dict['resource']) + assert isinstance(data_dict["resource"], dict) + assert isinstance(data_dict["package"], dict) + assert "on_same_domain" in data_dict["resource"] - self.calls['can_preview'] += 1 - return data_dict['resource']['format'].lower() == 'mock' + self.calls["can_preview"] += 1 + return data_dict["resource"]["format"].lower() == "mock" def preview_template(self, context, data_dict): - assert(isinstance(data_dict['resource'], dict)) - assert(isinstance(data_dict['package'], dict)) + assert isinstance(data_dict["resource"], dict) + assert isinstance(data_dict["package"], dict) - self.calls['preview_templates'] += 1 - return 'tests/mock_resource_preview_template.html' + self.calls["preview_templates"] += 1 + return "tests/mock_resource_preview_template.html" class JsonMockResourcePreviewExtension(mock_plugin.MockSingletonPlugin): @@ -184,17 +190,17 @@ def __init__(self, *args, **kw): self.calls = defaultdict(int) def setup_template_variables(self, context, data_dict): - self.calls['setup_template_variables'] += 1 + self.calls["setup_template_variables"] += 1 def can_preview(self, data_dict): - self.calls['can_preview'] += 1 - return data_dict['resource']['format'].lower() == 'json' + self.calls["can_preview"] += 1 + return data_dict["resource"]["format"].lower() == "json" def preview_template(self, context, data_dict): - self.calls['preview_templates'] += 1 - return 'tests/mock_json_resource_preview_template.html' + self.calls["preview_templates"] += 1 + return "tests/mock_json_resource_preview_template.html" # importing this file loads all these extensions by default # so clean up the extensions -p.plugins_update() +# p.plugins_update() diff --git a/ckan/tests/legacy/functional/api/__init__.py b/ckan/tests/legacy/functional/api/__init__.py index d0a6e636d32..0589c3ffce0 100644 --- a/ckan/tests/legacy/functional/api/__init__.py +++ b/ckan/tests/legacy/functional/api/__init__.py @@ -1,18 +1,17 @@ # encoding: utf-8 -from nose.tools import assert_equal import copy def change_lists_to_sets(iterable): - '''Convert any lists or tuples in `iterable` into sets. + """Convert any lists or tuples in `iterable` into sets. Recursively drill down into iterable and convert any list or tuples to sets. Does not work for dictionaries in lists. - ''' + """ if isinstance(iterable, dict): for key in iterable: if isinstance(iterable[key], (list, tuple)): @@ -21,26 +20,26 @@ def change_lists_to_sets(iterable): except TypeError: # e.g. unhashable pass - elif getattr(iterable[key], '__iter__', False): + elif getattr(iterable[key], "__iter__", False): change_lists_to_sets(iterable[key]) elif isinstance(iterable, (list, tuple)): for item in iterable: if isinstance(item, (list, tuple)): iterable.pop(item) iterable.append(set(item)) - elif getattr(item, '__iter__', False): + elif getattr(item, "__iter__", False): change_lists_to_sets(item) else: raise NotImplementedError def assert_dicts_equal_ignoring_ordering(dict1, dict2): - '''Assert that dict1 and dict2 are equal. + """Assert that dict1 and dict2 are equal. Assumes that the ordering of any lists in the dicts is unimportant. - ''' + """ dicts = [copy.deepcopy(dict1), copy.deepcopy(dict2)] for d in dicts: d = change_lists_to_sets(d) - assert_equal(dicts[0], dicts[1]) + assert dicts[0] == dicts[1] diff --git a/ckan/tests/legacy/functional/api/base.py b/ckan/tests/legacy/functional/api/base.py index 287750cad78..e853d36beb2 100644 --- a/ckan/tests/legacy/functional/api/base.py +++ b/ckan/tests/legacy/functional/api/base.py @@ -1,6 +1,5 @@ # encoding: utf-8 -from nose.tools import assert_equal from paste.fixture import TestRequest from six.moves.urllib.parse import quote @@ -13,6 +12,7 @@ ACCESS_DENIED = [403] + class ApiTestCase(object): STATUS_200_OK = 200 @@ -27,12 +27,13 @@ class ApiTestCase(object): api_version = None - ref_package_by = '' - ref_group_by = '' + ref_package_by = "" + ref_group_by = "" def get(self, offset, status=[200]): - response = self.app.get(offset, status=status, - extra_environ=self.get_extra_environ()) + response = self.app.get( + offset, status=status, extra_environ=self.get_extra_environ() + ) return response def post(self, offset, data, status=[200,201], *args, **kwds): @@ -43,15 +44,16 @@ def post(self, offset, data, status=[200,201], *args, **kwds): extra_environ=self.get_extra_environ()) return response - def app_delete(self, offset, status=[200,201], *args, **kwds): - response = self.app.delete(offset, status=status, - extra_environ=self.get_extra_environ()) + def app_delete(self, offset, status=[200, 201], *args, **kwds): + response = self.app.delete( + offset, status=status, extra_environ=self.get_extra_environ() + ) return response def get_extra_environ(self): extra_environ = {} - for (key,value) in self.extra_environ.items(): - if key == 'Authorization': + for (key, value) in self.extra_environ.items(): + if key == "Authorization": if self.send_authorization_header == True: extra_environ[key] = value else: @@ -74,65 +76,80 @@ def offset(self, path): [1] http://www.w3.org/International/articles/idn-and-iri/ """ assert self.api_version != None, "API version is missing." - base = '/api' + base = "/api" if self.api_version: - base += '/%s' % self.api_version - utf8_encoded = (u'%s%s' % (base, path)).encode('utf8') + base += "/%s" % self.api_version + utf8_encoded = (u"%s%s" % (base, path)).encode("utf8") url_encoded = quote(utf8_encoded) return url_encoded def assert_msg_represents_anna(self, msg): - assert 'annakarenina' in msg, msg + assert "annakarenina" in msg, msg data = self.loads(msg) - self.assert_equal(data['name'], 'annakarenina') - self.assert_equal(data['license_id'], 'other-open') + assert data["name"] == "annakarenina" + assert data["license_id"] == "other-open" assert '"license_id": "other-open"' in msg, str(msg) - assert 'russian' in msg, msg - assert 'tolstoy' in msg, msg + assert "russian" in msg, msg + assert "tolstoy" in msg, msg assert '"extras": {' in msg, msg assert '"genre": "romantic novel"' in msg, msg assert '"original media": "book"' in msg, msg - assert 'datahub.io/download' in msg, msg + assert "datahub.io/download" in msg, msg assert '"plain text"' in msg, msg assert '"Index of the novel"' in msg, msg assert '"id": "%s"' % self.anna.id in msg, msg expected = '"groups": [' assert expected in msg, (expected, msg) - expected = self.group_ref_from_name('roger') + expected = self.group_ref_from_name("roger") assert expected in msg, (expected, msg) - expected = self.group_ref_from_name('david') + expected = self.group_ref_from_name("david") assert expected in msg, (expected, msg) # Todo: What is the deal with ckan_url? And should this use IDs rather than names? - assert 'ckan_url' in msg - assert '"ckan_url": "http://test.ckan.net/dataset/annakarenina"' in msg, msg + assert "ckan_url" in msg + assert ( + '"ckan_url": "http://test.ckan.net/dataset/annakarenina"' in msg + ), msg - assert 'tags' in data, "Expected a tags list in json payload" - assert self.russian.name in data['tags'], data['tags'] - assert self.tolstoy.name in data['tags'], data['tags'] - assert self.flexible_tag.name in data['tags'], data['tags'] + assert "tags" in data, "Expected a tags list in json payload" + assert self.russian.name in data["tags"], data["tags"] + assert self.tolstoy.name in data["tags"], data["tags"] + assert self.flexible_tag.name in data["tags"], data["tags"] def assert_msg_represents_roger(self, msg): - assert 'roger' in msg, msg + assert "roger" in msg, msg data = self.loads(msg) keys = set(data.keys()) - expected_keys = set(['id', 'name', 'title', 'description', 'created', - 'state', 'packages']) + expected_keys = set( + [ + "id", + "name", + "title", + "description", + "created", + "state", + "packages", + ] + ) missing_keys = expected_keys - keys assert not missing_keys, missing_keys - assert_equal(data['name'], 'roger') - assert_equal(data['title'], 'Roger\'s books') - assert_equal(data['description'], 'Roger likes these books.') - assert_equal(data['state'], 'active') - assert_equal(data['packages'], [self._ref_package(self.anna)]) + assert data["name"] == "roger" + assert data["title"] == "Roger's books" + assert data["description"] == "Roger likes these books." + assert data["state"] == "active" + assert data["packages"] == [self._ref_package(self.anna)] def assert_msg_represents_russian(self, msg): data = self.loads(msg) pkgs = set(data) - expected_pkgs = set([self.package_ref_from_name('annakarenina'), - self.package_ref_from_name('warandpeace')]) + expected_pkgs = set( + [ + self.package_ref_from_name("annakarenina"), + self.package_ref_from_name("warandpeace"), + ] + ) differences = expected_pkgs ^ pkgs - assert not differences, '%r != %r' % (pkgs, expected_pkgs) + assert not differences, "%r != %r" % (pkgs, expected_pkgs) def assert_msg_represents_flexible_tag(self, msg): """ @@ -142,10 +159,14 @@ def assert_msg_represents_flexible_tag(self, msg): """ data = self.loads(msg) pkgs = set(data) - expected_pkgs = set([self.package_ref_from_name('annakarenina'), - self.package_ref_from_name('warandpeace')]) + expected_pkgs = set( + [ + self.package_ref_from_name("annakarenina"), + self.package_ref_from_name("warandpeace"), + ] + ) differences = expected_pkgs ^ pkgs - assert not differences, '%r != %r' % (pkgs, expected_pkgs) + assert not differences, "%r != %r" % (pkgs, expected_pkgs) def data_from_res(self, res): return self.loads(res.body) @@ -165,7 +186,7 @@ def package_id_from_ref(self, package_name): return self.ref_package(package) def ref_package(self, package): - assert self.ref_package_by in ['id', 'name'] + assert self.ref_package_by in ["id", "name"] return getattr(package, self.ref_package_by) def get_expected_api_version(self): @@ -181,73 +202,72 @@ def loads(self, chars): raise Exception("Couldn't loads string '%s': %s" % (chars, inst)) def assert_json_response(self, res, expected_in_body=None): - content_type = res.headers.get('Content-Type') - assert 'application/json' in content_type, content_type + content_type = res.headers.get("Content-Type") + assert "application/json" in content_type, content_type res_json = self.loads(res.body) if expected_in_body: - assert expected_in_body in res_json or \ - expected_in_body in str(res_json), \ - 'Expected to find %r in JSON response %r' % \ - (expected_in_body, res_json) + assert expected_in_body in res_json or expected_in_body in str( + res_json + ), ( + "Expected to find %r in JSON response %r" + % (expected_in_body, res_json) + ) class Api3TestCase(ApiTestCase): api_version = 3 - ref_package_by = 'name' - ref_group_by = 'name' - ref_tag_by = 'name' + ref_package_by = "name" + ref_group_by = "name" + ref_tag_by = "name" def assert_msg_represents_anna(self, msg): super(ApiTestCase, self).assert_msg_represents_anna(msg) - assert 'download_url' not in msg, msg + assert "download_url" not in msg, msg class BaseModelApiTestCase(ApiTestCase, ControllerTestCase): - testpackage_license_id = u'gpl-3.0' + testpackage_license_id = u"gpl-3.0" package_fixture_data = { - 'name': - u'testpkg', - 'title': - u'Some Title', - 'url': - u'http://blahblahblah.mydomain', - 'resources': [{ - u'url': u'http://blah.com/file.xml', - u'format': u'XML', - u'description': u'Main file', - u'hash': u'abc123', - u'alt_url': u'alt_url', - u'size_extra': u'200', - }, { - u'url': u'http://blah.com/file2.xml', - u'format': u'XML', - u'description': u'Second file', - u'hash': u'def123', - u'alt_url': u'alt_url', - u'size_extra': u'200', - }], - 'tags': [u'russion', u'novel'], - 'license_id': - testpackage_license_id, - 'extras': { - 'genre': u'horror', - 'media': u'dvd', - }, + "name": u"testpkg", + "title": u"Some Title", + "url": u"http://blahblahblah.mydomain", + "resources": [ + { + u"url": u"http://blah.com/file.xml", + u"format": u"XML", + u"description": u"Main file", + u"hash": u"abc123", + u"alt_url": u"alt_url", + u"size_extra": u"200", + }, + { + u"url": u"http://blah.com/file2.xml", + u"format": u"XML", + u"description": u"Second file", + u"hash": u"def123", + u"alt_url": u"alt_url", + u"size_extra": u"200", + }, + ], + "tags": [u"russion", u"novel"], + "license_id": testpackage_license_id, + "extras": {"genre": u"horror", "media": u"dvd"}, } testgroupvalues = { - 'name' : u'testgroup', - 'title' : u'Some Group Title', - 'description' : u'Great group!', - 'packages' : [u'annakarenina', u'warandpeace'], + "name": u"testgroup", + "title": u"Some Group Title", + "description": u"Great group!", + "packages": [u"annakarenina", u"warandpeace"], } - user_name = u'myrandom' + user_name = u"myrandom" def setup(self): super(BaseModelApiTestCase, self).setup() -# self.conditional_create_common_fixtures() -# self.init_extra_environ() + + # self.conditional_create_common_fixtures() + # self.init_extra_environ() def teardown(self): model.Session.remove() @@ -260,51 +280,64 @@ def init_extra_environ(cls, user_name): # called elsewhere in this class are run with the specified # user logged in. cls.user = model.User.by_name(user_name) - cls.extra_environ={'Authorization' : str(cls.user.apikey)} - cls.adminuser = model.User.by_name('testsysadmin') - cls.admin_extra_environ={'Authorization' : str(cls.adminuser.apikey)} + cls.extra_environ = {"Authorization": str(cls.user.apikey)} + cls.adminuser = model.User.by_name("testsysadmin") + cls.admin_extra_environ = {"Authorization": str(cls.adminuser.apikey)} def post_json(self, offset, data, status=None, extra_environ=None): - ''' Posts data in the body in application/json format, used by + """ Posts data in the body in application/json format, used by javascript libraries. (rather than Paste Fixture\'s default format of application/x-www-form-urlencoded) - ''' - return self.http_request(offset, data, content_type='application/json', - request_method='POST', - content_length=len(data), - status=status, extra_environ=extra_environ) + """ + return self.http_request( + offset, + data, + content_type="application/json", + request_method="POST", + content_length=len(data), + status=status, + extra_environ=extra_environ, + ) def delete_request(self, offset, status=None, extra_environ=None): - ''' Sends a delete request. Similar to the paste.delete but it + """ Sends a delete request. Similar to the paste.delete but it does not send the content type or content length. - ''' - return self.http_request(offset, data='', content_type=None, - request_method='DELETE', - content_length=None, - status=status, - extra_environ=extra_environ) - - def http_request(self, offset, data, - content_type='application/json', - request_method='POST', - content_length=None, - status=None, - extra_environ=None): - ''' Posts data in the body in a user-specified format. + """ + return self.http_request( + offset, + data="", + content_type=None, + request_method="DELETE", + content_length=None, + status=status, + extra_environ=extra_environ, + ) + + def http_request( + self, + offset, + data, + content_type="application/json", + request_method="POST", + content_length=None, + status=None, + extra_environ=None, + ): + """ Posts data in the body in a user-specified format. (rather than Paste Fixture\'s default Content-Type of application/x-www-form-urlencoded) - ''' + """ environ = self.app._make_environ() if content_type: - environ['CONTENT_TYPE'] = content_type + environ["CONTENT_TYPE"] = content_type if content_length is not None: - environ['CONTENT_LENGTH'] = str(content_length) - environ['REQUEST_METHOD'] = request_method - environ['QUERY_STRING'] = '' # avoids a warning - environ['wsgi.input'] = StringIO(data) + environ["CONTENT_LENGTH"] = str(content_length) + environ["REQUEST_METHOD"] = request_method + environ["QUERY_STRING"] = "" # avoids a warning + environ["wsgi.input"] = StringIO(data) if extra_environ: environ.update(extra_environ) self.app._set_headers({}, environ) @@ -312,6 +345,6 @@ def http_request(self, offset, data, return self.app.do_request(req, status=status) def set_env(self, extra_environ): - ''' used to reset env when admin has been forced etc ''' + """ used to reset env when admin has been forced etc """ environ = self.app._make_environ() environ.update(extra_environ) diff --git a/ckan/tests/legacy/functional/api/model/test_group.py b/ckan/tests/legacy/functional/api/model/test_group.py index 689b58dabfd..0bc6dd9900f 100644 --- a/ckan/tests/legacy/functional/api/model/test_group.py +++ b/ckan/tests/legacy/functional/api/model/test_group.py @@ -6,18 +6,16 @@ from ckan.lib.create_test_data import CreateTestData from ckan.lib import search -from nose.tools import assert_equal from ckan.tests.legacy.functional.api.base import BaseModelApiTestCase class GroupsTestCase(BaseModelApiTestCase): - @classmethod def setup_class(cls): search.clear_all() CreateTestData.create() - cls.user_name = u'russianfan' # created in CreateTestData + cls.user_name = u"russianfan" # created in CreateTestData cls.init_extra_environ(cls.user_name) @classmethod @@ -25,7 +23,7 @@ def teardown_class(cls): model.repo.rebuild_db() def teardown(self): - self.purge_group_by_name(self.testgroupvalues['name']) + self.purge_group_by_name(self.testgroupvalues["name"]) def test_register_get_ok(self): offset = self.group_offset() @@ -35,21 +33,30 @@ def test_register_get_ok(self): def test_register_post_ok(self): data = self.testgroupvalues - postparams = '%s=1' % self.dumps(data) + postparams = "%s=1" % self.dumps(data) offset = self.group_offset() - res = self.app.post(offset, params=postparams, - status=self.STATUS_201_CREATED, - extra_environ=self.extra_environ) + res = self.app.post( + offset, + params=postparams, + status=self.STATUS_201_CREATED, + extra_environ=self.extra_environ, + ) # check group object - group = self.get_group_by_name(self.testgroupvalues['name']) + group = self.get_group_by_name(self.testgroupvalues["name"]) assert group - assert group.title == self.testgroupvalues['title'], group - assert group.description == self.testgroupvalues['description'], group + assert group.title == self.testgroupvalues["title"], group + assert group.description == self.testgroupvalues["description"], group pkg_ids = [member.table_id for member in group.member_all] - pkgs = model.Session.query(model.Package).filter(model.Package.id.in_(pkg_ids)).all() + pkgs = ( + model.Session.query(model.Package) + .filter(model.Package.id.in_(pkg_ids)) + .all() + ) pkg_names = [pkg.name for pkg in pkgs] - assert set(pkg_names) == set(('annakarenina', 'warandpeace')), pkg_names + assert set(pkg_names) == set( + ("annakarenina", "warandpeace") + ), pkg_names # check register updated res = self.app.get(offset, status=self.STATUS_200_OK) @@ -58,32 +65,36 @@ def test_register_post_ok(self): assert self._ref_group(group) in data, data # check entity - offset = self.group_offset(self.testgroupvalues['name']) + offset = self.group_offset(self.testgroupvalues["name"]) res = self.app.get(offset, status=self.STATUS_200_OK) group = self.loads(res.body) expected_group = copy.deepcopy(self.testgroupvalues) - expected_group['packages'] = \ - sorted(self.ref_package(self.get_package_by_name(pkg_name)) - for pkg_name in expected_group['packages']) + expected_group["packages"] = sorted( + self.ref_package(self.get_package_by_name(pkg_name)) + for pkg_name in expected_group["packages"] + ) for expected_key, expected_value in expected_group.items(): - assert_equal(group.get(expected_key), expected_value) + assert group.get(expected_key) == expected_value # Test Group Register Post 409 (conflict - create duplicate group). offset = self.group_offset() - postparams = '%s=1' % self.dumps(self.testgroupvalues) - res = self.app.post(offset, params=postparams, - status=self.STATUS_409_CONFLICT, - extra_environ=self.extra_environ) - self.assert_json_response(res, 'Group name already exists') + postparams = "%s=1" % self.dumps(self.testgroupvalues) + res = self.app.post( + offset, + params=postparams, + status=self.STATUS_409_CONFLICT, + extra_environ=self.extra_environ, + ) + self.assert_json_response(res, "Group name already exists") def test_entity_get_ok(self): offset = self.group_offset(self.roger.name) res = self.app.get(offset, status=self.STATUS_200_OK) self.assert_msg_represents_roger(msg=res.body) - assert self.package_ref_from_name('annakarenina') in res, res - assert self.group_ref_from_name('roger') in res, res - assert not self.package_ref_from_name('warandpeace') in res, res + assert self.package_ref_from_name("annakarenina") in res, res + assert self.group_ref_from_name("roger") in res, res + assert not self.package_ref_from_name("warandpeace") in res, res def test_entity_get_then_post(self): # (ticket 662) Ensure an entity you 'get' from a register can be @@ -91,26 +102,29 @@ def test_entity_get_then_post(self): offset = self.group_offset(self.david.name) res = self.app.get(offset, status=self.STATUS_200_OK) data = self.loads(res.body) - postparams = '%s=1' % self.dumps(data) - res = self.app.post(offset, params=postparams, - status=self.STATUS_200_OK, - extra_environ=self.admin_extra_environ) + postparams = "%s=1" % self.dumps(data) + res = self.app.post( + offset, + params=postparams, + status=self.STATUS_200_OK, + extra_environ=self.admin_extra_environ, + ) res = self.set_env(self.extra_environ) def test_10_edit_group_name_duplicate(self): # create a group with testgroupvalues - if not model.Group.by_name(self.testgroupvalues['name']): + if not model.Group.by_name(self.testgroupvalues["name"]): group = model.Group() model.Session.add(group) - group.name = self.testgroupvalues['name'] + group.name = self.testgroupvalues["name"] model.Session.commit() - group = model.Group.by_name(self.testgroupvalues['name']) + group = model.Group.by_name(self.testgroupvalues["name"]) model.repo.commit_and_remove() - assert model.Group.by_name(self.testgroupvalues['name']) + assert model.Group.by_name(self.testgroupvalues["name"]) # create a group with name 'dupname' - dupname = u'dupname' + dupname = u"dupname" if not model.Group.by_name(dupname): group = model.Group() model.Session.add(group) @@ -119,61 +133,76 @@ def test_10_edit_group_name_duplicate(self): assert model.Group.by_name(dupname) # edit first group to have dupname - group_vals = {'name':dupname} - offset = self.group_offset(self.testgroupvalues['name']) - postparams = '%s=1' % self.dumps(group_vals) - res = self.app.post(offset, params=postparams, status=[409], - extra_environ=self.admin_extra_environ) - self.assert_json_response(res, 'Group name already exists') + group_vals = {"name": dupname} + offset = self.group_offset(self.testgroupvalues["name"]) + postparams = "%s=1" % self.dumps(group_vals) + res = self.app.post( + offset, + params=postparams, + status=[409], + extra_environ=self.admin_extra_environ, + ) + self.assert_json_response(res, "Group name already exists") res = self.set_env(self.extra_environ) def test_11_delete_group(self): # Test Groups Entity Delete 200. # create a group with testgroupvalues - group = model.Group.by_name(self.testgroupvalues['name']) + group = model.Group.by_name(self.testgroupvalues["name"]) if not group: group = model.Group() model.Session.add(group) - group.name = self.testgroupvalues['name'] + group.name = self.testgroupvalues["name"] model.repo.commit_and_remove() - group = model.Group.by_name(self.testgroupvalues['name']) + group = model.Group.by_name(self.testgroupvalues["name"]) model.repo.commit_and_remove() assert group user = model.User.by_name(self.user_name) # delete it - offset = self.group_offset(self.testgroupvalues['name']) - res = self.app.delete(offset, status=[200], - extra_environ=self.admin_extra_environ) + offset = self.group_offset(self.testgroupvalues["name"]) + res = self.app.delete( + offset, status=[200], extra_environ=self.admin_extra_environ + ) res = self.set_env(self.extra_environ) - group = model.Group.by_name(self.testgroupvalues['name']) + group = model.Group.by_name(self.testgroupvalues["name"]) assert group - assert group.state == 'deleted', group.state + assert group.state == "deleted", group.state # Anyone can see groups especially sysadmins # maybe we want to do something different with # deleted groups but that would be a new requirement - #res = self.app.get(offset, status=[403]) - #self.assert_json_response(res, 'Access denied') - res = self.app.get(offset, status=[200], - extra_environ=self.admin_extra_environ) + # res = self.app.get(offset, status=[403]) + # self.assert_json_response(res, 'Access denied') + res = self.app.get( + offset, status=[200], extra_environ=self.admin_extra_environ + ) res = self.set_env(self.extra_environ) def test_12_get_group_404(self): # Test Package Entity Get 404. - assert not model.Session.query(model.Group).filter_by(name=self.testgroupvalues['name']).count() - offset = self.group_offset(self.testgroupvalues['name']) + assert ( + not model.Session.query(model.Group) + .filter_by(name=self.testgroupvalues["name"]) + .count() + ) + offset = self.group_offset(self.testgroupvalues["name"]) res = self.app.get(offset, status=404) - self.assert_json_response(res, 'Not found') + self.assert_json_response(res, "Not found") def test_13_delete_group_404(self): # Test Packages Entity Delete 404. - assert not model.Session.query(model.Group).filter_by(name=self.testgroupvalues['name']).count() - offset = self.group_offset(self.testgroupvalues['name']) - res = self.app.delete(offset, status=[404], - extra_environ=self.extra_environ) - self.assert_json_response(res, 'not found') + assert ( + not model.Session.query(model.Group) + .filter_by(name=self.testgroupvalues["name"]) + .count() + ) + offset = self.group_offset(self.testgroupvalues["name"]) + res = self.app.delete( + offset, status=[404], extra_environ=self.extra_environ + ) + self.assert_json_response(res, "not found") diff --git a/ckan/tests/legacy/functional/api/model/test_package.py b/ckan/tests/legacy/functional/api/model/test_package.py deleted file mode 100644 index 27e3064a757..00000000000 --- a/ckan/tests/legacy/functional/api/model/test_package.py +++ /dev/null @@ -1,564 +0,0 @@ -# encoding: utf-8 - -from __future__ import print_function - -import copy - -from nose.tools import assert_equal, assert_raises - -from ckan.lib.create_test_data import CreateTestData -import ckan.lib.search as search -from ckan.lib.search.common import SolrSettings - -from ckan.tests.legacy.functional.api.base import BaseModelApiTestCase - -import ckan.tests.legacy as tests - -# Todo: Remove this ckan.model stuff. -import ckan.model as model - -class PackagesTestCase(BaseModelApiTestCase): - - @classmethod - def setup_class(cls): - CreateTestData.create() - cls.user_name = u'annafan' # created in CreateTestData - cls.init_extra_environ(cls.user_name) - - @classmethod - def teardown_class(cls): - model.repo.rebuild_db() - - def teardown(self): - self.purge_package_by_name(self.package_fixture_data['name']) - - def get_groups_identifiers(self, test_groups, users=[]): - groups = [] - for grp in test_groups: - group = model.Group.get(grp) - if self.get_expected_api_version() == 1: - groups.append(group.name) - else: - groups.append(group.id) - return groups - - def test_register_get_ok(self): - offset = self.package_offset() - res = self.app.get(offset, status=self.STATUS_200_OK) - assert self.ref_package(self.anna) in res, res - assert self.ref_package(self.war) in res, res - - def test_register_post_ok(self): - assert not self.get_package_by_name(self.package_fixture_data['name']) - offset = self.package_offset() - postparams = '%s=1' % self.dumps(self.package_fixture_data) - res = self.app.post(offset, params=postparams, - status=self.STATUS_201_CREATED, - extra_environ=self.admin_extra_environ) - - # Check the returned package is as expected - pkg = self.loads(res.body) - assert_equal(pkg['name'], self.package_fixture_data['name']) - assert_equal(pkg['title'], self.package_fixture_data['title']) - assert_equal(set(pkg['tags']), set(self.package_fixture_data['tags'])) - assert_equal(len(pkg['resources']), len(self.package_fixture_data['resources'])) - assert_equal(pkg['extras'], self.package_fixture_data['extras']) - - # Check the value of the Location header. - location = res.headers.get('Location') - - assert offset in location - res = self.app.get(location, status=self.STATUS_200_OK) - # Check the database record. - model.Session.remove() - package = self.get_package_by_name(self.package_fixture_data['name']) - assert package - self.assert_equal(package.title, self.package_fixture_data['title']) - self.assert_equal(package.url, self.package_fixture_data['url']) - self.assert_equal(package.license_id, self.testpackage_license_id) - self.assert_equal(len(package.get_tags()), 2) - self.assert_equal(len(package.extras), 2) - for key, value in self.package_fixture_data['extras'].items(): - self.assert_equal(package.extras[key], value) - self.assert_equal(len(package.resources), len(self.package_fixture_data['resources'])) - for (i, expected_resource) in enumerate(self.package_fixture_data['resources']): - package_resource = package.resources[i] - for key in expected_resource.keys(): - if key == 'extras': - package_resource_extras = getattr(package_resource, key) - expected_resource_extras = expected_resource[key].items() - for expected_extras_key, expected_extras_value in expected_resource_extras: - package_resource_value = package_resource_extras[expected_extras_key],\ - 'Package:%r Extras:%r Expected_extras:%r' % \ - (self.package_fixture_data['name'], - package_resource_extras, expected_resource) - else: - package_resource_value = getattr(package_resource, key, None) - if not package_resource_value: - package_resource_value = package_resource.extras[key] - - expected_resource_value = expected_resource[key] - self.assert_equal(package_resource_value, expected_resource_value) - - # Test Package Entity Get 200. - offset = self.package_offset(self.package_fixture_data['name']) - res = self.app.get(offset, status=self.STATUS_200_OK) - # Todo: Instead loads() the data and then check actual values. - assert self.package_fixture_data['name'] in res, res - assert '"license_id": "%s"' % self.package_fixture_data['license_id'] in res, res - assert self.package_fixture_data['tags'][0] in res, res - assert self.package_fixture_data['tags'][1] in res, res - assert '"extras": {' in res, res - for key, value in self.package_fixture_data['extras'].items(): - assert '"%s": "%s"' % (key, value) in res, res - - model.Session.remove() - - # Test Packages Register Post 409 (conflict - create duplicate package). - offset = self.package_offset() - postparams = '%s=1' % self.dumps(self.package_fixture_data) - res = self.app.post(offset, params=postparams, status=self.STATUS_409_CONFLICT, - extra_environ=self.admin_extra_environ) - model.Session.remove() - - def test_register_post_with_group(self): - assert not self.get_package_by_name(self.package_fixture_data['name']) - offset = self.package_offset() - - test_groups = [u'david'] - user = model.User.by_name(u'testsysadmin') - - groups = self.get_groups_identifiers(test_groups,[user]) - - package_fixture_data = self.package_fixture_data - package_fixture_data['groups'] = groups - data = self.dumps(package_fixture_data) - res = self.post_json(offset, data, status=self.STATUS_201_CREATED, - extra_environ={'Authorization':str(user.apikey)}) - - # Check the database record. - model.Session.remove() - package = self.get_package_by_name(self.package_fixture_data['name']) - assert package - pkg_groups = model.Session.query(model.Group).\ - join(model.Member, model.Member.group_id == model.Group.id).\ - filter(model.Member.table_id == package.id).all() - if self.get_expected_api_version() == 1: - self.assert_equal([g.name for g in pkg_groups], groups) - else: - self.assert_equal([g.id for g in pkg_groups], groups) - del package_fixture_data['groups'] - - def test_register_post_with_group_not_authorized(self): - assert not self.get_package_by_name(self.package_fixture_data['name']) - offset = self.package_offset() - - test_groups = [u'david'] - groups = self.get_groups_identifiers(test_groups) - - package_fixture_data = self.package_fixture_data - package_fixture_data['groups'] = groups - data = self.dumps(package_fixture_data) - res = self.post_json(offset, data, status=self.STATUS_403_ACCESS_DENIED, - extra_environ=self.extra_environ) - del package_fixture_data['groups'] - - def test_register_post_with_group_not_found(self): - assert not self.get_package_by_name(self.package_fixture_data['name']) - offset = self.package_offset() - - test_groups = [u'this-group-does-not-exist'] - groups = test_groups - - package_fixture_data = self.package_fixture_data - package_fixture_data['groups'] = groups - data = self.dumps(package_fixture_data) - res = self.post_json(offset, data, status=self.STATUS_404_NOT_FOUND, - extra_environ=self.extra_environ) - del package_fixture_data['groups'] - - def test_register_post_with_group_sysadmin(self): - assert not self.get_package_by_name(self.package_fixture_data['name']) - offset = self.package_offset() - user = model.User.by_name(u'testsysadmin') - test_groups = [u'david'] - groups = self.get_groups_identifiers(test_groups) - - package_fixture_data = self.package_fixture_data - package_fixture_data['groups'] = groups - data = self.dumps(package_fixture_data) - res = self.post_json(offset, data, status=self.STATUS_201_CREATED, - extra_environ={'Authorization':str(user.apikey)}) - # Check the database record. - model.Session.remove() - package = self.get_package_by_name(self.package_fixture_data['name']) - assert package - pkg_groups = model.Session.query(model.Group).\ - join(model.Member, model.Member.group_id == model.Group.id).\ - filter(model.Member.table_id == package.id).all() - if self.get_expected_api_version() == 1: - self.assert_equal([g.name for g in pkg_groups], groups) - else: - self.assert_equal([g.id for g in pkg_groups], groups) - - del package_fixture_data['groups'] - - def test_register_post_json(self): - assert not self.get_package_by_name(self.package_fixture_data['name']) - offset = self.package_offset() - data = self.dumps(self.package_fixture_data) - res = self.post_json(offset, data, status=self.STATUS_201_CREATED, - extra_environ=self.admin_extra_environ) - # Check the database record. - model.Session.remove() - package = self.get_package_by_name(self.package_fixture_data['name']) - assert package - self.assert_equal(package.title, self.package_fixture_data['title']) - - def test_register_post_indexerror(self): - """ - Test that we can't add a package if Solr is down. - """ - bad_solr_url = 'http://example.com/badsolrurl' - original_settings = SolrSettings.get()[0] - try: - SolrSettings.init(bad_solr_url) - - assert not self.get_package_by_name(self.package_fixture_data['name']) - offset = self.package_offset() - data = self.dumps(self.package_fixture_data) - - self.post_json(offset, data, status=500, extra_environ=self.admin_extra_environ) - model.Session.remove() - finally: - SolrSettings.init(original_settings) - - def test_register_post_tag_too_long(self): - pkg = {'name': 'test_tag_too_long', - 'tags': ['tagok', 't'*101]} - assert not self.get_package_by_name(pkg['name']) - offset = self.package_offset() - data = self.dumps(pkg) - res = self.post_json(offset, data, status=self.STATUS_409_CONFLICT, - extra_environ=self.admin_extra_environ) - assert 'length is more than maximum 100' in res.body, res.body - assert 'tagok' not in res.body - - def test_entity_get_ok_jsonp(self): - offset = self.anna_offset(postfix='?callback=jsoncallback') - res = self.app.get(offset, status=self.STATUS_200_OK) - import re - assert re.match('jsoncallback\(.*\);', res.body), res - # Unwrap JSONP callback (we want to look at the data). - msg = res.body[len('jsoncallback')+1:-2] - self.assert_msg_represents_anna(msg=msg) - - def test_entity_get_then_post(self): - # (ticket 662) Ensure an entity you 'get' from a register can be - # returned by posting it back - offset = self.package_offset(self.war.name) - res = self.app.get(offset, status=self.STATUS_200_OK) - data = self.loads(res.body) - - postparams = '%s=1' % self.dumps(data) - res = self.app.post(offset, params=postparams, - status=self.STATUS_200_OK, - extra_environ=self.admin_extra_environ) - data_returned = self.loads(res.body) - assert_equal(data['name'], data_returned['name']) - assert_equal(data['license_id'], data_returned['license_id']) - - def test_entity_get_then_post_new(self): - offset = self.package_offset(self.war.name) - res = self.app.get(offset, status=self.STATUS_200_OK) - data = self.loads(res.body) - - # change name and create a new package - data['name'] = u'newpkg' - data['id'] = None # ensure this doesn't clash or you get 409 error - postparams = '%s=1' % self.dumps(data) - # use russianfan now because he has rights to add this package to - # the 'david' group. - extra_environ = {'REMOTE_USER': 'testsysadmin'} - res = self.app.post(self.package_offset(), params=postparams, - status=self.STATUS_201_CREATED, - extra_environ=extra_environ) - try: - data_returned = self.loads(res.body) - assert_equal(data['name'], data_returned['name']) - assert_equal(data['license_id'], data_returned['license_id']) - finally: - self.purge_package_by_name(data['name']) - - def test_entity_post_changed_readonly(self): - # (ticket 662) Edit a readonly field gives error - offset = self.package_offset(self.war.name) - res = self.app.get(offset, status=self.STATUS_200_OK) - data = self.loads(res.body) - data['id'] = 'illegally changed value' - postparams = '%s=1' % self.dumps(data) - res = self.app.post(offset, params=postparams, - status=self.STATUS_409_CONFLICT, - extra_environ=self.admin_extra_environ) - assert "Cannot change value of key from" in res.body, res.body - assert "to illegally changed value. This key is read-only" in res.body, res.body - - def test_entity_update_denied(self): - offset = self.anna_offset() - postparams = '%s=1' % self.dumps(self.package_fixture_data) - res = self.app.post(offset, params=postparams, status=self.STATUS_403_ACCESS_DENIED) - - def test_entity_delete_denied(self): - offset = self.anna_offset() - res = self.app.delete(offset, status=self.STATUS_403_ACCESS_DENIED) - - def create_package_with_admin_user(self, package_data): - '''Creates a package with self.user as admin and provided package_data. - ''' - self.create_package(data=package_data) - - def test_package_update_ok_by_id(self): - self.assert_package_update_ok('id', 'post') - - def test_entity_update_ok_by_name(self): - self.assert_package_update_ok('name', 'post') - - def test_package_update_ok_by_id_by_put(self): - self.assert_package_update_ok('id', 'put') - - def test_entity_update_ok_by_name_by_put(self): - self.assert_package_update_ok('name', 'put') - - def test_package_update_delete_last_extra(self): - old_fixture_data = { - 'name': self.package_fixture_data['name'], - 'extras': { - u'key1': u'val1', - }, - } - new_fixture_data = { - 'name':u'somethingnew', - 'extras': { - u'key1': None, - }, - } - self.create_package_with_admin_user(old_fixture_data) - offset = self.package_offset(old_fixture_data['name']) - params = '%s=1' % self.dumps(new_fixture_data) - res = self.app.post(offset, params=params, status=self.STATUS_200_OK, - extra_environ=self.admin_extra_environ) - - try: - # Check the returned package is as expected - pkg = self.loads(res.body) - assert_equal(pkg['name'], new_fixture_data['name']) - expected_extras = copy.deepcopy(new_fixture_data['extras']) - del expected_extras['key1'] - assert_equal(pkg['extras'], expected_extras) - - # Check extra was deleted - model.Session.remove() - package = self.get_package_by_name(new_fixture_data['name']) - # - title - self.assert_equal(package.extras, {}) - finally: - self.purge_package_by_name(new_fixture_data['name']) - - def test_package_update_do_not_delete_last_extra(self): - old_fixture_data = { - 'name': self.package_fixture_data['name'], - 'extras': { - u'key1': u'val1', - }, - } - new_fixture_data = { - 'name':u'somethingnew', - 'extras': {}, # no extras specified, but existing - # ones should be left alone - } - self.create_package_with_admin_user(old_fixture_data) - offset = self.package_offset(old_fixture_data['name']) - params = '%s=1' % self.dumps(new_fixture_data) - res = self.app.post(offset, params=params, status=self.STATUS_200_OK, - extra_environ=self.admin_extra_environ) - - try: - # Check the returned package is as expected - pkg = self.loads(res.body) - assert_equal(pkg['name'], new_fixture_data['name']) - expected_extras = {u'key1': u'val1'} # should not be deleted - assert_equal(pkg['extras'], expected_extras) - - # Check extra was not deleted - model.Session.remove() - package = self.get_package_by_name(new_fixture_data['name']) - # - title - assert len(package.extras) == 1, package.extras - finally: - self.purge_package_by_name(new_fixture_data['name']) - - def test_entity_update_readd_tag(self): - name = self.package_fixture_data['name'] - old_fixture_data = { - 'name': name, - 'tags': ['tag 1.', 'tag2'] - } - new_fixture_data = { - 'name': name, - 'tags': ['tag 1.'] - } - self.create_package_with_admin_user(old_fixture_data) - offset = self.package_offset(name) - params = '%s=1' % self.dumps(new_fixture_data) - res = self.app.post(offset, params=params, status=self.STATUS_200_OK, - extra_environ=self.admin_extra_environ) - - # Check the returned package is as expected - pkg = self.loads(res.body) - assert_equal(pkg['name'], new_fixture_data['name']) - assert_equal(pkg['tags'], ['tag 1.']) - - package = self.get_package_by_name(new_fixture_data['name']) - assert len(package.get_tags()) == 1, package.get_tags() - - # now reinstate the tag - params = '%s=1' % self.dumps(old_fixture_data) - res = self.app.post(offset, params=params, status=self.STATUS_200_OK, - extra_environ=self.admin_extra_environ) - pkg = self.loads(res.body) - assert_equal(pkg['tags'], ['tag 1.', 'tag2']) - - def test_entity_update_conflict(self): - package1_name = self.package_fixture_data['name'] - package1_data = {'name': package1_name} - package1 = self.create_package_with_admin_user(package1_data) - package2_name = u'somethingnew' - package2_data = {'name': package2_name} - package2 = self.create_package_with_admin_user(package2_data) - try: - package1_offset = self.package_offset(package1_name) - # trying to rename package 1 to package 2's name - print(package1_offset, package2_data) - self.post(package1_offset, package2_data, self.STATUS_409_CONFLICT, extra_environ=self.admin_extra_environ) - finally: - self.purge_package_by_name(package2_name) - - def test_entity_update_empty(self): - package1_name = self.package_fixture_data['name'] - package1_data = {'name': package1_name} - package1 = self.create_package_with_admin_user(package1_data) - package2_data = '' # this is the error - package1_offset = self.package_offset(package1_name) - self.app.put(package1_offset, package2_data, - status=self.STATUS_400_BAD_REQUEST) - - def test_entity_update_indexerror(self): - """ - Test that we can't update a package if Solr is down. - """ - bad_solr_url = 'http://example.com/badsolrurl' - original_settings = SolrSettings.get()[0] - try: - SolrSettings.init(bad_solr_url) - - assert_raises( - search.SearchIndexError, self.assert_package_update_ok, 'name', 'post' - ) - finally: - SolrSettings.init(original_settings) - - def test_package_update_delete_resource(self): - old_fixture_data = { - 'name': self.package_fixture_data['name'], - 'resources': [{ - u'url':u'http://blah.com/file2.xml', - u'format':u'XML', - u'description':u'Appendix 1', - u'hash':u'def123', - u'alt_url':u'alt123', - },{ - u'url':u'http://blah.com/file3.xml', - u'format':u'XML', - u'description':u'Appenddic 2', - u'hash':u'ghi123', - u'alt_url':u'alt123', - }], - } - new_fixture_data = { - 'name':u'somethingnew', - 'resources': [], - } - self.create_package_with_admin_user(old_fixture_data) - offset = self.package_offset(old_fixture_data['name']) - params = '%s=1' % self.dumps(new_fixture_data) - res = self.app.post(offset, params=params, status=self.STATUS_200_OK, - extra_environ=self.admin_extra_environ) - - try: - # Check the returned package is as expected - pkg = self.loads(res.body) - assert_equal(pkg['name'], new_fixture_data['name']) - assert_equal(pkg['resources'], []) - - # Check resources were deleted - model.Session.remove() - package = self.get_package_by_name(new_fixture_data['name']) - self.assert_equal(len(package.resources), 0) - finally: - self.purge_package_by_name(new_fixture_data['name']) - - def test_entity_delete_ok(self): - # create a package with package_fixture_data - if not self.get_package_by_name(self.package_fixture_data['name']): - self.create_package(name=self.package_fixture_data['name']) - assert self.get_package_by_name(self.package_fixture_data['name']) - # delete it - offset = self.package_offset(self.package_fixture_data['name']) - res = self.app.delete(offset, status=self.STATUS_200_OK, - extra_environ=self.admin_extra_environ) - package = self.get_package_by_name(self.package_fixture_data['name']) - self.assert_equal(package.state, 'deleted') - model.Session.remove() - - def test_entity_delete_ok_without_request_headers(self): - # create a package with package_fixture_data - if not self.get_package_by_name(self.package_fixture_data['name']): - self.create_package(name=self.package_fixture_data['name']) - assert self.get_package_by_name(self.package_fixture_data['name']) - # delete it - offset = self.package_offset(self.package_fixture_data['name']) - res = self.delete_request(offset, status=self.STATUS_200_OK, - extra_environ=self.admin_extra_environ) - package = self.get_package_by_name(self.package_fixture_data['name']) - self.assert_equal(package.state, 'deleted') - model.Session.remove() - - def test_create_private_package_with_no_organization(self): - '''Test that private packages with no organization cannot be created. - - ''' - testsysadmin = model.User.by_name('testsysadmin') - result = tests.call_action_api(self.app, 'package_create', name='test', - private=True, apikey=testsysadmin.apikey, status=409) - assert result == {'__type': 'Validation Error', - 'private': ["Datasets with no organization can't be private."]} - - def test_create_public_package_with_no_organization(self): - '''Test that public packages with no organization can be created.''' - testsysadmin = model.User.by_name('testsysadmin') - tests.call_action_api(self.app, 'package_create', name='test', - private=False, apikey=testsysadmin.apikey) - - def test_make_package_with_no_organization_private(self): - '''Test that private packages with no organization cannot be created - by package_update. - - ''' - testsysadmin = model.User.by_name('testsysadmin') - package = tests.call_action_api(self.app, 'package_create', - name='test_2', private=False, apikey=testsysadmin.apikey) - package['private'] = True - result = tests.call_action_api(self.app, 'package_update', - apikey=testsysadmin.apikey, status=409, **package) - assert result == {'__type': 'Validation Error', - 'private': ["Datasets with no organization can't be private."]} diff --git a/ckan/tests/legacy/functional/api/model/test_ratings.py b/ckan/tests/legacy/functional/api/model/test_ratings.py deleted file mode 100644 index 2edca6a45a2..00000000000 --- a/ckan/tests/legacy/functional/api/model/test_ratings.py +++ /dev/null @@ -1,71 +0,0 @@ -# encoding: utf-8 - -from ckan import model -from ckan.lib.create_test_data import CreateTestData - -from ckan.tests.legacy.functional.api.base import BaseModelApiTestCase - - -class RatingsTestCase(BaseModelApiTestCase): - - @classmethod - def setup_class(cls): - CreateTestData.create() - cls.testsysadmin = model.User.by_name(u'testsysadmin') - cls.comment = u'Comment umlaut: \xfc.' - cls.user_name = u'annafan' # created in CreateTestData - cls.init_extra_environ(cls.user_name) - - @classmethod - def teardown_class(cls): - model.repo.rebuild_db() - - def test_register_post(self): - # Test Rating Register Post 200. - self.clear_all_tst_ratings() - offset = self.rating_offset() - rating_opts = {'package':u'warandpeace', - 'rating':5} - pkg_name = rating_opts['package'] - postparams = '%s=1' % self.dumps(rating_opts) - res = self.app.post(offset, params=postparams, status=[201], - extra_environ=self.extra_environ) - model.Session.remove() - pkg = self.get_package_by_name(pkg_name) - assert pkg - assert len(pkg.ratings) == 1 - assert pkg.ratings[0].rating == rating_opts['rating'], pkg.ratings - - # Get package to see rating - offset = self.package_offset(pkg_name) - res = self.app.get(offset, status=[200]) - assert pkg_name in res, res - assert '"ratings_average": %s.0' % rating_opts['rating'] in res, res - assert '"ratings_count": 1' in res, res - - model.Session.remove() - - # Rerate package - offset = self.rating_offset() - postparams = '%s=1' % self.dumps(rating_opts) - res = self.app.post(offset, params=postparams, status=[201], - extra_environ=self.extra_environ) - model.Session.remove() - pkg = self.get_package_by_name(pkg_name) - assert pkg - assert len(pkg.ratings) == 1 - assert pkg.ratings[0].rating == rating_opts['rating'], pkg.ratings - - def test_entity_post_invalid(self): - self.clear_all_tst_ratings() - offset = self.rating_offset() - rating_opts = {'package':u'warandpeace', - 'rating':0} - postparams = '%s=1' % self.dumps(rating_opts) - res = self.app.post(offset, params=postparams, status=[409], - extra_environ=self.extra_environ) - self.assert_json_response(res, 'rating') - model.Session.remove() - pkg = self.get_package_by_name(rating_opts['package']) - assert pkg - assert len(pkg.ratings) == 0 diff --git a/ckan/tests/legacy/functional/api/model/test_tag.py b/ckan/tests/legacy/functional/api/model/test_tag.py deleted file mode 100644 index a46a5f172fb..00000000000 --- a/ckan/tests/legacy/functional/api/model/test_tag.py +++ /dev/null @@ -1,53 +0,0 @@ -# encoding: utf-8 - -from ckan import model -from ckan.lib.create_test_data import CreateTestData -import ckan.lib.search as search - -from ckan.tests.legacy.functional.api.base import BaseModelApiTestCase - - -class TagsTestCase(BaseModelApiTestCase): - - @classmethod - def setup_class(cls): - search.clear_all() - CreateTestData.create() - cls.testsysadmin = model.User.by_name(u'testsysadmin') - cls.comment = u'Comment umlaut: \xfc.' - cls.user_name = u'annafan' # created in CreateTestData - cls.init_extra_environ(cls.user_name) - - @classmethod - def teardown_class(cls): - search.clear_all() - model.repo.rebuild_db() - - def test_register_get_ok(self): - offset = self.tag_offset() - res = self.app.get(offset, status=self.STATUS_200_OK) - results = self.loads(res.body) - assert self.russian.name in results, results - assert self.tolstoy.name in results, results - assert self.flexible_tag.name in results, results - - def test_entity_get_ok(self): - offset = self.tag_offset(self.russian.name) - res = self.app.get(offset, status=self.STATUS_200_OK) - self.assert_msg_represents_russian(msg=res.body) - - def test_entity_get_ok_flexible_tag(self): - """ - Asserts that searching for a tag name with spaces and punctuation works. - - The tag name is u'Flexible \u30a1', and both the 'warandpeace' - and 'annakarenina' packages should be returned. - """ - offset = self.tag_offset(self.flexible_tag.name) - res = self.app.get(offset, status=self.STATUS_200_OK) - self.assert_msg_represents_flexible_tag(msg=res.body) - - def test_entity_get_not_found(self): - offset = self.tag_offset('doesntexist') - res = self.app.get(offset, status=404) - self.assert_json_response(res, 'Not found') diff --git a/ckan/tests/legacy/functional/api/model/test_vocabulary.py b/ckan/tests/legacy/functional/api/model/test_vocabulary.py index 373f8b6d8b6..924ed0e8d5f 100644 --- a/ckan/tests/legacy/functional/api/model/test_vocabulary.py +++ b/ckan/tests/legacy/functional/api/model/test_vocabulary.py @@ -4,22 +4,15 @@ import ckan.lib.helpers as helpers import ckan.tests.helpers as h import ckan.lib.dictization.model_dictize as model_dictize +import pytest class TestVocabulary(object): - - @classmethod - def setup_class(self): - self.app = h._get_test_app() - - @classmethod - def teardown_class(self): - ckan.model.repo.rebuild_db() - - def setup(self): - self.clean_vocab() + @pytest.fixture(autouse=True) + def initial_data(self, clean_db, app): + self.app = app model = ckan.model - context = {'model': model} + context = {"model": model} genre = model.Vocabulary("Genre") time_period = ckan.model.Vocabulary("Time Period") @@ -27,1099 +20,1218 @@ def setup(self): model.Session.add_all([genre, time_period, composers]) self.genre_vocab = model_dictize.vocabulary_dictize(genre, context) - self.timeperiod_vocab = model_dictize.vocabulary_dictize(time_period, - context) - self.composers_vocab = model_dictize.vocabulary_dictize(composers, - context) + self.timeperiod_vocab = model_dictize.vocabulary_dictize( + time_period, context + ) + self.composers_vocab = model_dictize.vocabulary_dictize( + composers, context + ) ckan.model.Session.commit() - self.sysadmin_user = ckan.model.User.get('admin') - self.normal_user = ckan.model.User.get('normal') + self.sysadmin_user = ckan.model.User.get("admin") + self.normal_user = ckan.model.User.get("normal") if not self.sysadmin_user: - normal_user = ckan.model.User(name=u'normal', password=u'annafan') - sysadmin_user = ckan.model.User(name=u'admin', - password=u'testsysadmin') + normal_user = ckan.model.User(name=u"normal", password=u"annafan") + sysadmin_user = ckan.model.User( + name=u"admin", password=u"testsysadmin" + ) sysadmin_user.sysadmin = True ckan.model.Session.add(normal_user) ckan.model.Session.add(sysadmin_user) ckan.model.Session.commit() - self.sysadmin_user = ckan.model.User.get('admin') - self.normal_user = ckan.model.User.get('normal') + self.sysadmin_user = ckan.model.User.get("admin") + self.normal_user = ckan.model.User.get("normal") self.sysadmin_apikey = self.sysadmin_user.apikey - def clean_vocab(self): - ckan.model.Session.execute('delete from package_tag_revision') - ckan.model.Session.execute('delete from package_tag') - ckan.model.Session.execute('delete from tag') - ckan.model.Session.execute('delete from vocabulary') - ckan.model.Session.commit() - - @classmethod def _post(self, url, params=None, extra_environ=None): if params is None: params = {} param_string = helpers.json.dumps(params) - response = self.app.post(url, params=param_string, - extra_environ=extra_environ) + response = self.app.post( + url, params=param_string, extra_environ=extra_environ + ) assert not response.errors return response.json - @classmethod def _create_vocabulary(self, vocab_name=None, user=None): # Create a new vocabulary. - params = {'name': vocab_name} + params = {"name": vocab_name} if user: - extra_environ = {'Authorization': str(user.apikey)} + extra_environ = {"Authorization": str(user.apikey)} else: extra_environ = None - response = self._post('/api/action/vocabulary_create', params=params, - extra_environ=extra_environ) + response = self._post( + "/api/action/vocabulary_create", + params=params, + extra_environ=extra_environ, + ) # Check the values of the response. - assert response['success'] is True - assert response['result'] - created_vocab = response['result'] - assert created_vocab['name'] == vocab_name - assert created_vocab['id'] + assert response["success"] is True + assert response["result"] + created_vocab = response["result"] + assert created_vocab["name"] == vocab_name + assert created_vocab["id"] # Get the list of vocabularies. - response = self._post('/api/action/vocabulary_list') + response = self._post("/api/action/vocabulary_list") # Check that the vocabulary we created is in the list. - assert response['success'] is True - assert response['result'] - assert response['result'].count(created_vocab) == 1 + assert response["success"] is True + assert response["result"] + assert response["result"].count(created_vocab) == 1 # Get the created vocabulary. - params = {'id': created_vocab['id']} - response = self._post('/api/action/vocabulary_show', params) + params = {"id": created_vocab["id"]} + response = self._post("/api/action/vocabulary_show", params) # Check that retrieving the vocab by name gives the same result. - by_name_params = {'id': created_vocab['name']} - assert response == self._post('/api/action/vocabulary_show', - by_name_params) + by_name_params = {"id": created_vocab["name"]} + assert response == self._post( + "/api/action/vocabulary_show", by_name_params + ) # Check that it matches what we created. - assert response['success'] is True - assert response['result'] == created_vocab + assert response["success"] is True + assert response["result"] == created_vocab return created_vocab def _update_vocabulary(self, params, user=None): if user: - extra_environ = {'Authorization': str(user.apikey)} + extra_environ = {"Authorization": str(user.apikey)} else: extra_environ = None - original_vocab = self._post('/api/action/vocabulary_show', - {'id': params.get('id') or params.get('name')})['result'] + original_vocab = self._post( + "/api/action/vocabulary_show", + {"id": params.get("id") or params.get("name")}, + )["result"] - response = self._post('/api/action/vocabulary_update', params=params, - extra_environ=extra_environ) + response = self._post( + "/api/action/vocabulary_update", + params=params, + extra_environ=extra_environ, + ) # Check the values of the response. - assert response['success'] is True - assert response['result'] - updated_vocab = response['result'] + assert response["success"] is True + assert response["result"] + updated_vocab = response["result"] # id should never change. - assert updated_vocab['id'] == original_vocab['id'] - if 'id' in params: - assert updated_vocab['id'] == params['id'] + assert updated_vocab["id"] == original_vocab["id"] + if "id" in params: + assert updated_vocab["id"] == params["id"] # name should change only if given in params. - if 'name' in params: - assert updated_vocab['name'] == params['name'] + if "name" in params: + assert updated_vocab["name"] == params["name"] else: - assert updated_vocab['name'] == original_vocab['name'] + assert updated_vocab["name"] == original_vocab["name"] # tags should change only if given in params. - if 'tags' in params: - assert sorted(tag['name'] for tag in params['tags']) \ - == sorted(tag['name'] for tag in updated_vocab['tags']) + + if "tags" in params: + assert sorted([tag["name"] for tag in params["tags"]]) == sorted( + [tag["name"] for tag in updated_vocab["tags"]] + ) else: - assert updated_vocab['tags'] == original_vocab['tags'] + assert updated_vocab["tags"] == original_vocab["tags"] # Get the list of vocabularies. - response = self._post('/api/action/vocabulary_list') + response = self._post("/api/action/vocabulary_list") # Check that the vocabulary we created is in the list. - assert response['success'] is True - assert response['result'] - assert response['result'].count(updated_vocab) == 1 + assert response["success"] is True + assert response["result"] + assert response["result"].count(updated_vocab) == 1 # Get the created vocabulary. - params = {'id': updated_vocab['id']} - response = self._post('/api/action/vocabulary_show', params) + params = {"id": updated_vocab["id"]} + response = self._post("/api/action/vocabulary_show", params) # Check that retrieving the vocab by name gives the same result. - by_name_params = {'id': updated_vocab['name']} - assert response == self._post('/api/action/vocabulary_show', - by_name_params) + by_name_params = {"id": updated_vocab["name"]} + assert response == self._post( + "/api/action/vocabulary_show", by_name_params + ) # Check that it matches what we created. - assert response['success'] is True - assert response['result'] == updated_vocab + assert response["success"] is True + assert response["result"] == updated_vocab return updated_vocab - def _delete_vocabulary(self, vocab_id, user=None): - if user: - extra_environ = {'Authorization': str(user.apikey)} - else: - extra_environ = None - params = {'id': vocab_id} - response = self._post('/api/action/vocabulary_delete', params=params, - extra_environ=extra_environ) - - # Check the values of the response. - assert response['success'] is True - assert response['result'] is None - response['result'] - - # Get the list of vocabularies. - response = self._post('/api/action/vocabulary_list') - assert response['success'] is True - assert response['result'] - # Check that the vocabulary we deleted is not in the list. - assert vocab_id not in [vocab['id'] for vocab in response['result']] - - # Check that the deleted vocabulary can no longer be retrieved. - response = self.app.post('/api/action/vocabulary_show', - params=helpers.json.dumps(params), - extra_environ={'Authorization': - str(self.sysadmin_user.apikey)}, - status=404) - assert response.json['success'] is False - def _list_tags(self, vocabulary=None, user=None): params = {} if vocabulary: - params['vocabulary_id'] = vocabulary['id'] + params["vocabulary_id"] = vocabulary["id"] if user: - extra_environ = {'Authorization': str(user.apikey)} + extra_environ = {"Authorization": str(user.apikey)} else: extra_environ = None - response = self._post('/api/action/tag_list', params=params, - extra_environ=extra_environ) - assert response['success'] is True - return response['result'] + response = self._post( + "/api/action/tag_list", params=params, extra_environ=extra_environ + ) + assert response["success"] is True + return response["result"] def _create_tag(self, user, tag_name, vocabulary=None): - tag_dict = {'name': tag_name} + tag_dict = {"name": tag_name} if vocabulary: - tag_dict['vocabulary_id'] = vocabulary['id'] + tag_dict["vocabulary_id"] = vocabulary["id"] if user: - extra_environ = {'Authorization': str(user.apikey)} + extra_environ = {"Authorization": str(user.apikey)} else: extra_environ = None - response = self._post('/api/action/tag_create', params=tag_dict, - extra_environ=extra_environ) - assert response['success'] is True - return response['result'] + response = self._post( + "/api/action/tag_create", + params=tag_dict, + extra_environ=extra_environ, + ) + assert response["success"] is True + return response["result"] def _delete_tag(self, user, tag_id_or_name, vocab_id_or_name=None): - params = {'id': tag_id_or_name} + params = {"id": tag_id_or_name} if vocab_id_or_name: - params['vocabulary_id'] = vocab_id_or_name + params["vocabulary_id"] = vocab_id_or_name if user: - extra_environ = {'Authorization': str(user.apikey)} + extra_environ = {"Authorization": str(user.apikey)} else: extra_environ = None - response = self._post('/api/action/tag_delete', params=params, - extra_environ=extra_environ) - assert response['success'] is True - return response['result'] + response = self._post( + "/api/action/tag_delete", + params=params, + extra_environ=extra_environ, + ) + assert response["success"] is True + return response["result"] def test_vocabulary_create(self): - '''Test adding a new vocabulary to a CKAN instance via the action + """Test adding a new vocabulary to a CKAN instance via the action API. - ''' - self._create_vocabulary(vocab_name="My cool vocab", - user=self.sysadmin_user) + """ + self._create_vocabulary( + vocab_name="My cool vocab", user=self.sysadmin_user + ) def test_vocabulary_create_with_tags(self): - '''Test adding a new vocabulary with some tags. - - ''' - params = {'name': 'foobar'} - tag1 = {'name': 'foo'} - tag2 = {'name': 'bar'} - params['tags'] = [tag1, tag2] - response = self._post('/api/action/vocabulary_create', - params=params, - extra_environ={'Authorization': str(self.sysadmin_apikey)}) - assert response['success'] is True - assert response['result'] - created_vocab = response['result'] - assert created_vocab['name'] == 'foobar' - assert created_vocab['id'] + """Test adding a new vocabulary with some tags. + + """ + params = {"name": "foobar"} + tag1 = {"name": "foo"} + tag2 = {"name": "bar"} + params["tags"] = [tag1, tag2] + response = self._post( + "/api/action/vocabulary_create", + params=params, + extra_environ={"Authorization": str(self.sysadmin_apikey)}, + ) + assert response["success"] is True + assert response["result"] + created_vocab = response["result"] + assert created_vocab["name"] == "foobar" + assert created_vocab["id"] # Get the list of vocabularies. - response = self._post('/api/action/vocabulary_list') + response = self._post("/api/action/vocabulary_list") # Check that the vocabulary we created is in the list. - assert response['success'] is True - assert response['result'] - assert response['result'].count(created_vocab) == 1 + assert response["success"] is True + assert response["result"] + assert response["result"].count(created_vocab) == 1 # Get the created vocabulary. - params = {'id': created_vocab['id']} - response = self._post('/api/action/vocabulary_show', params) + params = {"id": created_vocab["id"]} + response = self._post("/api/action/vocabulary_show", params) # Check that retrieving the vocab by name gives the same result. - by_name_params = {'id': created_vocab['name']} - assert response == self._post('/api/action/vocabulary_show', - by_name_params) + by_name_params = {"id": created_vocab["name"]} + assert response == self._post( + "/api/action/vocabulary_show", by_name_params + ) # Check that it matches what we created. - assert response['success'] is True - assert response['result'] == created_vocab + assert response["success"] is True + assert response["result"] == created_vocab # Get the list of tags for the vocabulary. tags = self._list_tags(created_vocab) assert len(tags) == 2 - assert tags.count('foo') == 1 - assert tags.count('bar') == 1 + assert tags.count("foo") == 1 + assert tags.count("bar") == 1 - def test_vocabulary_create_bad_tags(self): - '''Test creating new vocabularies with invalid tags. + def test_vocabulary_create_bad_tags(self, app): + """Test creating new vocabularies with invalid tags. - ''' + """ for tags in ( - [{'id': 'xxx'}, {'name': 'foo'}], - [{'name': 'foo'}, {'name': None}], - [{'name': 'foo'}, {'name': ''}], - [{'name': 'foo'}, {'name': 'f'}], - [{'name': 'f' * 200}, {'name': 'foo'}], - [{'name': 'Invalid!'}, {'name': 'foo'}], - ): - params = {'name': 'foobar', 'tags': tags} - response = self.app.post('/api/action/vocabulary_create', - params=helpers.json.dumps(params), - extra_environ={'Authorization': str(self.sysadmin_apikey)}, - status=409) - assert response.json['success'] is False - assert 'tags' in response.json['error'] - assert len(response.json['error']) == 2 - - def test_vocabulary_create_none_tags(self): - '''Test creating new vocabularies with None for 'tags'. - - ''' - params = {'name': 'foobar', 'tags': None} - response = self.app.post('/api/action/vocabulary_create', + [{"id": "xxx"}, {"name": "foo"}], + [{"name": "foo"}, {"name": None}], + [{"name": "foo"}, {"name": ""}], + [{"name": "foo"}, {"name": "f"}], + [{"name": "f" * 200}, {"name": "foo"}], + [{"name": "Invalid!"}, {"name": "foo"}], + ): + params = {"name": "foobar", "tags": tags} + response = app.post( + "/api/action/vocabulary_create", params=helpers.json.dumps(params), - extra_environ={'Authorization': - str(self.sysadmin_user.apikey)}, - status=400) + extra_environ={"Authorization": str(self.sysadmin_apikey)}, + status=409, + ) + assert response.json["success"] is False + assert "tags" in response.json["error"] + assert len(response.json["error"]) == 2 + + def test_vocabulary_create_none_tags(self, app): + """Test creating new vocabularies with None for 'tags'. + + """ + params = {"name": "foobar", "tags": None} + response = app.post( + "/api/action/vocabulary_create", + params=helpers.json.dumps(params), + extra_environ={"Authorization": str(self.sysadmin_user.apikey)}, + status=400, + ) assert "Integrity Error" in response.body - def test_vocabulary_create_empty_tags(self): - '''Test creating new vocabularies with [] for 'tags'. - - ''' - params = {'name': 'foobar', 'tags': []} - response = self.app.post('/api/action/vocabulary_create', - params=helpers.json.dumps(params), - extra_environ={'Authorization': - str(self.sysadmin_user.apikey)}, - status=200) - assert response.json['success'] is True - assert response.json['result'] - created_vocab = response.json['result'] - assert created_vocab['name'] == 'foobar' - assert created_vocab['id'] - assert created_vocab['tags'] == [] - params = {'id': created_vocab['id']} - response = self._post('/api/action/vocabulary_show', params) - assert response['success'] is True - assert response['result'] == created_vocab + def test_vocabulary_create_empty_tags(self, app): + """Test creating new vocabularies with [] for 'tags'. + + """ + params = {"name": "foobar", "tags": []} + response = app.post( + "/api/action/vocabulary_create", + params=helpers.json.dumps(params), + extra_environ={"Authorization": str(self.sysadmin_user.apikey)}, + status=200, + ) + assert response.json["success"] is True + assert response.json["result"] + created_vocab = response.json["result"] + assert created_vocab["name"] == "foobar" + assert created_vocab["id"] + assert created_vocab["tags"] == [] + params = {"id": created_vocab["id"]} + response = self._post("/api/action/vocabulary_show", params) + assert response["success"] is True + assert response["result"] == created_vocab tags = self._list_tags(created_vocab) assert tags == [] - def test_vocabulary_create_id(self): - '''Test error response when user tries to supply their own ID when + def test_vocabulary_create_id(self, app): + """Test error response when user tries to supply their own ID when creating a vocabulary. - ''' - params = {'id': 'xxx', 'name': 'foobar'} + """ + params = {"id": "xxx", "name": "foobar"} param_string = helpers.json.dumps(params) - response = self.app.post('/api/action/vocabulary_create', - params=param_string, - extra_environ={'Authorization': - str(self.sysadmin_user.apikey)}, - status=409) - assert response.json['success'] is False - assert response.json['error']['id'] == [u'The input field id was ' - 'not expected.'] - - def test_vocabulary_create_no_name(self): - '''Test error response when user tries to create a vocab without a + response = app.post( + "/api/action/vocabulary_create", + params=param_string, + extra_environ={"Authorization": str(self.sysadmin_user.apikey)}, + status=409, + ) + assert response.json["success"] is False + assert response.json["error"]["id"] == [ + u"The input field id was " "not expected." + ] + + def test_vocabulary_create_no_name(self, app): + """Test error response when user tries to create a vocab without a name. - ''' + """ params = {} param_string = helpers.json.dumps(params) - response = self.app.post('/api/action/vocabulary_create', - params=param_string, - extra_environ={'Authorization': - str(self.sysadmin_user.apikey)}, - status=409) - assert response.json['success'] is False - assert response.json['error']['name'] == [u'Missing value'] - - def test_vocabulary_create_invalid_name(self): - '''Test error response when user tries to create a vocab with an + response = app.post( + "/api/action/vocabulary_create", + params=param_string, + extra_environ={"Authorization": str(self.sysadmin_user.apikey)}, + status=409, + ) + assert response.json["success"] is False + assert response.json["error"]["name"] == [u"Missing value"] + + def test_vocabulary_create_invalid_name(self, app): + """Test error response when user tries to create a vocab with an invalid name. - ''' - for name in (None, '', 'a', 'foobar' * 100): - params = {'name': name} + """ + for name in (None, "", "a", "foobar" * 100): + params = {"name": name} param_string = helpers.json.dumps(params) - response = self.app.post('/api/action/vocabulary_create', - params=param_string, - extra_environ={'Authorization': - str(self.sysadmin_apikey)}, - status=409) - assert response.json['success'] is False - assert response.json['error']['name'] - - def test_vocabulary_create_exists(self): - '''Test error response when user tries to create a vocab that already + response = app.post( + "/api/action/vocabulary_create", + params=param_string, + extra_environ={"Authorization": str(self.sysadmin_apikey)}, + status=409, + ) + assert response.json["success"] is False + assert response.json["error"]["name"] + + def test_vocabulary_create_exists(self, app): + """Test error response when user tries to create a vocab that already exists. - ''' - params = {'name': self.genre_vocab['name']} + """ + params = {"name": self.genre_vocab["name"]} param_string = helpers.json.dumps(params) - response = self.app.post('/api/action/vocabulary_create', - params=param_string, - extra_environ={'Authorization': - str(self.sysadmin_user.apikey)}, - status=409) - assert response.json['success'] is False - assert response.json['error']['name'] == [u'That vocabulary name is ' - 'already in use.'] - - def test_vocabulary_create_not_logged_in(self): - '''Test that users who are not logged in cannot create vocabularies.''' - - params = {'name': - "Spam Vocabulary: SpamCo Duck Rental: Rent Your Ducks From Us!"} + response = app.post( + "/api/action/vocabulary_create", + params=param_string, + extra_environ={"Authorization": str(self.sysadmin_user.apikey)}, + status=409, + ) + assert response.json["success"] is False + assert response.json["error"]["name"] == [ + u"That vocabulary name is " "already in use." + ] + + def test_vocabulary_create_not_logged_in(self, app): + """Test that users who are not logged in cannot create vocabularies.""" + + params = { + "name": "Spam Vocabulary: SpamCo Duck Rental: Rent Your Ducks From Us!" + } param_string = helpers.json.dumps(params) - response = self.app.post('/api/action/vocabulary_create', - params=param_string, - status=403) - assert response.json['success'] is False - assert response.json['error']['__type'] == 'Authorization Error' + response = app.post( + "/api/action/vocabulary_create", params=param_string, status=403 + ) + assert response.json["success"] is False + assert response.json["error"]["__type"] == "Authorization Error" - def test_vocabulary_create_not_authorized(self): - '''Test that users who are not authorized cannot create vocabs.''' + def test_vocabulary_create_not_authorized(self, app): + """Test that users who are not authorized cannot create vocabs.""" - params = {'name': 'My Unauthorised Vocabulary'} + params = {"name": "My Unauthorised Vocabulary"} param_string = helpers.json.dumps(params) - response = self.app.post('/api/action/vocabulary_create', - params=param_string, - extra_environ={'Authorization': - str(self.normal_user.apikey)}, - status=403) - assert response.json['success'] is False - assert response.json['error']['__type'] == 'Authorization Error' + response = app.post( + "/api/action/vocabulary_create", + params=param_string, + extra_environ={"Authorization": str(self.normal_user.apikey)}, + status=403, + ) + assert response.json["success"] is False + assert response.json["error"]["__type"] == "Authorization Error" def test_vocabulary_update_id_only(self): - self._update_vocabulary({'id': self.genre_vocab['id']}, - self.sysadmin_user) + self._update_vocabulary( + {"id": self.genre_vocab["id"]}, self.sysadmin_user + ) def test_vocabulary_update_id_and_same_name(self): - self._update_vocabulary({'id': self.genre_vocab['id'], - 'name': self.genre_vocab['name']}, self.sysadmin_user) + self._update_vocabulary( + {"id": self.genre_vocab["id"], "name": self.genre_vocab["name"]}, + self.sysadmin_user, + ) def test_vocabulary_update_id_and_new_name(self): - self._update_vocabulary({'id': self.genre_vocab['id'], - 'name': 'new name'}, self.sysadmin_user) + self._update_vocabulary( + {"id": self.genre_vocab["id"], "name": "new name"}, + self.sysadmin_user, + ) def test_vocabulary_update_id_and_same_tags(self): - self._update_vocabulary({'id': self.genre_vocab['id'], - 'tags': self.genre_vocab['tags']}, self.sysadmin_user) + self._update_vocabulary( + {"id": self.genre_vocab["id"], "tags": self.genre_vocab["tags"]}, + self.sysadmin_user, + ) def test_vocabulary_update_id_and_new_tags(self): tags = [ - {'name': 'new test tag one'}, - {'name': 'new test tag two'}, - {'name': 'new test tag three'}, - ] - self._update_vocabulary({'id': self.genre_vocab['id'], 'tags': tags}, - self.sysadmin_user) + {"name": "new test tag one"}, + {"name": "new test tag two"}, + {"name": "new test tag three"}, + ] + self._update_vocabulary( + {"id": self.genre_vocab["id"], "tags": tags}, self.sysadmin_user + ) def test_vocabulary_update_id_same_name_and_same_tags(self): - self._update_vocabulary({'id': self.genre_vocab['id'], - 'name': self.genre_vocab['name'], - 'tags': self.genre_vocab['tags']}, self.sysadmin_user) + self._update_vocabulary( + { + "id": self.genre_vocab["id"], + "name": self.genre_vocab["name"], + "tags": self.genre_vocab["tags"], + }, + self.sysadmin_user, + ) def test_vocabulary_update_id_same_name_and_new_tags(self): tags = [ - {'name': 'new test tag one'}, - {'name': 'new test tag two'}, - {'name': 'new test tag three'}, - ] - self._update_vocabulary({'id': self.genre_vocab['id'], - 'name': self.genre_vocab['name'], - 'tags': tags}, self.sysadmin_user) + {"name": "new test tag one"}, + {"name": "new test tag two"}, + {"name": "new test tag three"}, + ] + self._update_vocabulary( + { + "id": self.genre_vocab["id"], + "name": self.genre_vocab["name"], + "tags": tags, + }, + self.sysadmin_user, + ) def test_vocabulary_update_id_new_name_and_same_tags(self): - self._update_vocabulary({'id': self.genre_vocab['id'], - 'name': 'new name', - 'tags': self.genre_vocab['tags']}, self.sysadmin_user) - - def test_vocabulary_update_not_exists(self): - '''Test the error response given when a user tries to update a + self._update_vocabulary( + { + "id": self.genre_vocab["id"], + "name": "new name", + "tags": self.genre_vocab["tags"], + }, + self.sysadmin_user, + ) + + def test_vocabulary_update_not_exists(self, app): + """Test the error response given when a user tries to update a vocabulary that doesn't exist. - ''' - params = {'id': 'xxxxxxx', 'name': 'updated_name'} + """ + params = {"id": "xxxxxxx", "name": "updated_name"} param_string = helpers.json.dumps(params) - response = self.app.post('/api/action/vocabulary_update', - params=param_string, - extra_environ={'Authorization': - str(self.sysadmin_user.apikey)}, - status=404) - assert response.json['success'] is False - assert response.json['error']['message'].startswith('Not found: ') - - def test_vocabulary_update_no_id(self): - params = {'name': 'bagel radio'} + response = app.post( + "/api/action/vocabulary_update", + params=param_string, + extra_environ={"Authorization": str(self.sysadmin_user.apikey)}, + status=404, + ) + assert response.json["success"] is False + assert response.json["error"]["message"].startswith("Not found: ") + + def test_vocabulary_update_no_id(self, app): + params = {"name": "bagel radio"} param_string = helpers.json.dumps(params) - response = self.app.post('/api/action/vocabulary_update', - params=param_string, - extra_environ={'Authorization': - str(self.sysadmin_user.apikey)}, - status=409) - assert response.json['success'] is False - assert 'id' in response.json['error'] - assert response.json['error']['id'] == 'id not in data' - - def test_vocabulary_update_not_logged_in(self): - '''Test that users who are not logged in cannot update vocabularies.''' - params = {'id': self.genre_vocab['id']} + response = app.post( + "/api/action/vocabulary_update", + params=param_string, + extra_environ={"Authorization": str(self.sysadmin_user.apikey)}, + status=409, + ) + assert response.json["success"] is False + assert "id" in response.json["error"] + assert response.json["error"]["id"] == "id not in data" + + def test_vocabulary_update_not_logged_in(self, app): + """Test that users who are not logged in cannot update vocabularies.""" + params = {"id": self.genre_vocab["id"]} param_string = helpers.json.dumps(params) - response = self.app.post('/api/action/vocabulary_update', - params=param_string, - status=403) - assert response.json['success'] is False - assert response.json['error']['__type'] == 'Authorization Error' + response = app.post( + "/api/action/vocabulary_update", params=param_string, status=403 + ) + assert response.json["success"] is False + assert response.json["error"]["__type"] == "Authorization Error" def test_vocabulary_update_with_tags(self): tags = [ - {'name': 'drone'}, - {'name': 'noise'}, - {'name': 'fuzz'}, - {'name': 'field recordings'}, - {'name': 'hypnagogia'}, - {'name': 'textures without rhythm'}, - ] + {"name": "drone"}, + {"name": "noise"}, + {"name": "fuzz"}, + {"name": "field recordings"}, + {"name": "hypnagogia"}, + {"name": "textures without rhythm"}, + ] self._update_vocabulary( - { - 'id': self.genre_vocab['id'], - 'name': self.genre_vocab['name'], - 'tags': tags - }, - self.sysadmin_user) - - params = {'id': self.genre_vocab['id']} - response = self._post('/api/action/vocabulary_show', params) + { + "id": self.genre_vocab["id"], + "name": self.genre_vocab["name"], + "tags": tags, + }, + self.sysadmin_user, + ) + + params = {"id": self.genre_vocab["id"]} + response = self._post("/api/action/vocabulary_show", params) # Check that retrieving the vocab by name gives the same result. - assert len(response['result']['tags']) == len(tags) + assert len(response["result"]["tags"]) == len(tags) - def test_vocabulary_update_not_authorized(self): - '''Test that users who are not authorized cannot update vocabs.''' - params = {'id': self.genre_vocab['id']} + def test_vocabulary_update_not_authorized(self, app): + """Test that users who are not authorized cannot update vocabs.""" + params = {"id": self.genre_vocab["id"]} param_string = helpers.json.dumps(params) - response = self.app.post('/api/action/vocabulary_update', - params=param_string, - extra_environ={'Authorization': - str(self.normal_user.apikey)}, - status=403) - assert response.json['success'] is False - assert response.json['error']['message'] == 'Access denied' - - def test_vocabulary_update_bad_tags(self): - '''Test updating vocabularies with invalid tags. - - ''' + response = app.post( + "/api/action/vocabulary_update", + params=param_string, + extra_environ={"Authorization": str(self.normal_user.apikey)}, + status=403, + ) + assert response.json["success"] is False + assert response.json["error"]["message"] == "Access denied" + + def test_vocabulary_update_bad_tags(self, app): + """Test updating vocabularies with invalid tags. + + """ apikey = str(self.sysadmin_user.apikey) for tags in ( - [{'id': 'xxx'}, {'name': 'foo'}], - [{'name': 'foo'}, {'name': None}], - [{'name': 'foo'}, {'name': ''}], - [{'name': 'foo'}, {'name': 'f'}], - [{'name': 'f' * 200}, {'name': 'foo'}], - [{'name': 'Invalid!'}, {'name': 'foo'}], - ): - params = {'id': self.genre_vocab['name'], 'tags': tags} - response = self.app.post('/api/action/vocabulary_update', - params=helpers.json.dumps(params), - extra_environ={'Authorization': apikey}, - status=409) - assert response.json['success'] is False - assert response.json['error']['tags'] - - def test_vocabulary_update_none_tags(self): - '''Test updating vocabularies with None for 'tags'. - - ''' - params = {'id': self.genre_vocab['id'], 'tags': None} - response = self.app.post('/api/action/vocabulary_update', + [{"id": "xxx"}, {"name": "foo"}], + [{"name": "foo"}, {"name": None}], + [{"name": "foo"}, {"name": ""}], + [{"name": "foo"}, {"name": "f"}], + [{"name": "f" * 200}, {"name": "foo"}], + [{"name": "Invalid!"}, {"name": "foo"}], + ): + params = {"id": self.genre_vocab["name"], "tags": tags} + response = app.post( + "/api/action/vocabulary_update", params=helpers.json.dumps(params), - extra_environ={'Authorization': - str(self.sysadmin_user.apikey)}, - status=400) + extra_environ={"Authorization": apikey}, + status=409, + ) + assert response.json["success"] is False + assert response.json["error"]["tags"] + + def test_vocabulary_update_none_tags(self, app): + """Test updating vocabularies with None for 'tags'. + + """ + params = {"id": self.genre_vocab["id"], "tags": None} + response = app.post( + "/api/action/vocabulary_update", + params=helpers.json.dumps(params), + extra_environ={"Authorization": str(self.sysadmin_user.apikey)}, + status=400, + ) assert "Integrity Error" in response.body, response.body - def test_vocabulary_update_empty_tags(self): - '''Test updating vocabularies with [] for 'tags'. - - ''' - params = {'id': self.genre_vocab['id'], 'tags': []} - response = self.app.post('/api/action/vocabulary_update', - params=helpers.json.dumps(params), - extra_environ={'Authorization': - str(self.sysadmin_user.apikey)}, - status=200) - assert response.json['success'] is True - assert response.json['result'] - updated_vocab = response.json['result'] - assert updated_vocab['name'] == self.genre_vocab['name'] - assert updated_vocab['id'] == self.genre_vocab['id'] - assert updated_vocab['tags'] == [] - params = {'id': updated_vocab['id']} - response = self._post('/api/action/vocabulary_show', params) - assert response['success'] is True - assert response['result'] == updated_vocab + def test_vocabulary_update_empty_tags(self, app): + """Test updating vocabularies with [] for 'tags'. + + """ + params = {"id": self.genre_vocab["id"], "tags": []} + response = app.post( + "/api/action/vocabulary_update", + params=helpers.json.dumps(params), + extra_environ={"Authorization": str(self.sysadmin_user.apikey)}, + status=200, + ) + assert response.json["success"] is True + assert response.json["result"] + updated_vocab = response.json["result"] + assert updated_vocab["name"] == self.genre_vocab["name"] + assert updated_vocab["id"] == self.genre_vocab["id"] + assert updated_vocab["tags"] == [] + params = {"id": updated_vocab["id"]} + response = self._post("/api/action/vocabulary_show", params) + assert response["success"] is True + assert response["result"] == updated_vocab tags = self._list_tags(updated_vocab) assert tags == [] - def test_vocabulary_delete(self): - self._delete_vocabulary(self.genre_vocab['id'], self.sysadmin_user) + def test_vocabulary_delete(self, app): + vocab_id = self.genre_vocab["id"] + user = self.sysadmin_user + if user: + extra_environ = {"Authorization": str(user.apikey)} + else: + extra_environ = None + params = {"id": vocab_id} + response = self._post( + "/api/action/vocabulary_delete", + params=params, + extra_environ=extra_environ, + ) + + # Check the values of the response. + assert response["success"] is True + assert response["result"] is None + response["result"] + + # Get the list of vocabularies. + response = self._post("/api/action/vocabulary_list") + assert response["success"] is True + assert response["result"] + # Check that the vocabulary we deleted is not in the list. + assert vocab_id not in [vocab["id"] for vocab in response["result"]] - def test_vocabulary_delete_not_exists(self): - '''Test the error response given when a user tries to delete a + # Check that the deleted vocabulary can no longer be retrieved. + response = app.post( + "/api/action/vocabulary_show", + params=helpers.json.dumps(params), + extra_environ={"Authorization": str(self.sysadmin_user.apikey)}, + status=404, + ) + assert response.json["success"] is False + + def test_vocabulary_delete_not_exists(self, app): + """Test the error response given when a user tries to delete a vocabulary that doesn't exist. - ''' - params = {'id': 'xxxxxxx'} + """ + params = {"id": "xxxxxxx"} param_string = helpers.json.dumps(params) - response = self.app.post('/api/action/vocabulary_delete', - params=param_string, - extra_environ={'Authorization': - str(self.sysadmin_user.apikey)}, - status=404) - assert response.json['success'] is False - assert response.json['error']['message'].startswith('Not found: ' - 'Could not find vocabulary') - - def test_vocabulary_delete_no_id(self): - '''Test the error response given when a user tries to delete a + response = app.post( + "/api/action/vocabulary_delete", + params=param_string, + extra_environ={"Authorization": str(self.sysadmin_user.apikey)}, + status=404, + ) + assert response.json["success"] is False + assert response.json["error"]["message"].startswith( + "Not found: " "Could not find vocabulary" + ) + + def test_vocabulary_delete_no_id(self, app): + """Test the error response given when a user tries to delete a vocabulary without giving the vocabulary id. - ''' + """ params = {} param_string = helpers.json.dumps(params) - response = self.app.post('/api/action/vocabulary_delete', - params=param_string, - extra_environ={'Authorization': - str(self.sysadmin_user.apikey)}, - status=409) - assert response.json['success'] is False - assert 'id' in response.json['error'] - assert response.json['error']['id'] == 'id not in data' - - def test_vocabulary_delete_not_logged_in(self): - '''Test that users who are not logged in cannot delete vocabularies.''' - params = {'id': self.genre_vocab['id']} + response = app.post( + "/api/action/vocabulary_delete", + params=param_string, + extra_environ={"Authorization": str(self.sysadmin_user.apikey)}, + status=409, + ) + assert response.json["success"] is False + assert "id" in response.json["error"] + assert response.json["error"]["id"] == "id not in data" + + def test_vocabulary_delete_not_logged_in(self, app): + """Test that users who are not logged in cannot delete vocabularies.""" + params = {"id": self.genre_vocab["id"]} param_string = helpers.json.dumps(params) - response = self.app.post('/api/action/vocabulary_delete', - params=param_string, - status=403) - assert response.json['success'] is False - assert response.json['error']['__type'] == 'Authorization Error' - - def test_vocabulary_delete_not_authorized(self): - '''Test that users who are not authorized cannot delete vocabs.''' - params = {'id': self.genre_vocab['id']} + response = app.post( + "/api/action/vocabulary_delete", params=param_string, status=403 + ) + assert response.json["success"] is False + assert response.json["error"]["__type"] == "Authorization Error" + + def test_vocabulary_delete_not_authorized(self, app): + """Test that users who are not authorized cannot delete vocabs.""" + params = {"id": self.genre_vocab["id"]} param_string = helpers.json.dumps(params) - response = self.app.post('/api/action/vocabulary_delete', - params=param_string, - extra_environ={'Authorization': - str(self.normal_user.apikey)}, - status=403) - assert response.json['success'] is False - assert response.json['error']['__type'] == 'Authorization Error' + response = app.post( + "/api/action/vocabulary_delete", + params=param_string, + extra_environ={"Authorization": str(self.normal_user.apikey)}, + status=403, + ) + assert response.json["success"] is False + assert response.json["error"]["__type"] == "Authorization Error" def test_add_tag_to_vocab(self): - '''Test that a tag can be added to and then retrieved from a vocab.''' + """Test that a tag can be added to and then retrieved from a vocab.""" vocab = self.genre_vocab tags_before = self._list_tags(vocab) - tag_created = self._create_tag(self.sysadmin_user, 'noise', vocab) + tag_created = self._create_tag(self.sysadmin_user, "noise", vocab) tags_after = self._list_tags(vocab) - new_tag_names = [tag_name for tag_name in tags_after if tag_name not in - tags_before] + new_tag_names = [ + tag_name for tag_name in tags_after if tag_name not in tags_before + ] assert len(new_tag_names) == 1 - assert tag_created['name'] in new_tag_names + assert tag_created["name"] in new_tag_names - def test_add_tag_no_vocab(self): - '''Test the error response when a user tries to create a tag without + def test_add_tag_no_vocab(self, app): + """Test the error response when a user tries to create a tag without specifying a vocab. - ''' - tag_dict = {'name': 'noise'} + """ + tag_dict = {"name": "noise"} tag_string = helpers.json.dumps(tag_dict) - response = self.app.post('/api/action/tag_create', - params=tag_string, - extra_environ={'Authorization': - str(self.sysadmin_user.apikey)}, - status=409) - assert response.json['success'] is False - assert response.json['error']['vocabulary_id'] == ['Missing value'] - - def test_add_tag_vocab_not_exists(self): - '''Test the error response when a user tries to add a tag to a vocab + response = app.post( + "/api/action/tag_create", + params=tag_string, + extra_environ={"Authorization": str(self.sysadmin_user.apikey)}, + status=409, + ) + assert response.json["success"] is False + assert response.json["error"]["vocabulary_id"] == ["Missing value"] + + def test_add_tag_vocab_not_exists(self, app): + """Test the error response when a user tries to add a tag to a vocab that doesn't exist. - ''' - tag_dict = {'name': 'noise', 'vocabulary_id': 'does not exist'} + """ + tag_dict = {"name": "noise", "vocabulary_id": "does not exist"} tag_string = helpers.json.dumps(tag_dict) - response = self.app.post('/api/action/tag_create', - params=tag_string, - extra_environ={'Authorization': - str(self.sysadmin_user.apikey)}, - status=409) - assert response.json['success'] is False - assert response.json['error']['vocabulary_id'] == [ - 'Tag vocabulary was not found.'] - - def test_add_tag_already_added(self): - '''Test the error response when a user tries to add a tag to a vocab + response = app.post( + "/api/action/tag_create", + params=tag_string, + extra_environ={"Authorization": str(self.sysadmin_user.apikey)}, + status=409, + ) + assert response.json["success"] is False + assert response.json["error"]["vocabulary_id"] == [ + "Tag vocabulary was not found." + ] + + def test_add_tag_already_added(self, app): + """Test the error response when a user tries to add a tag to a vocab that already has a tag with the same name. - ''' + """ self.test_add_tag_to_vocab() vocab = self.genre_vocab - tag_dict = {'name': 'noise', 'vocabulary_id': vocab['id']} + tag_dict = {"name": "noise", "vocabulary_id": vocab["id"]} tag_string = helpers.json.dumps(tag_dict) - response = self.app.post('/api/action/tag_create', - params=tag_string, - extra_environ={'Authorization': - str(self.sysadmin_user.apikey)}, - status=409) - assert response.json['success'] is False - assert response.json['error']['vocabulary_id'][0].startswith( - 'Tag noise already belongs to vocabulary') - - def test_add_tag_with_id(self): - '''Test the error response when a user tries to specify the tag ID when + response = app.post( + "/api/action/tag_create", + params=tag_string, + extra_environ={"Authorization": str(self.sysadmin_user.apikey)}, + status=409, + ) + assert response.json["success"] is False + assert response.json["error"]["vocabulary_id"][0].startswith( + "Tag noise already belongs to vocabulary" + ) + + def test_add_tag_with_id(self, app): + """Test the error response when a user tries to specify the tag ID when adding a tag to a vocab. - ''' + """ tag_dict = { - 'id': 'dsagdsgsgsd', - 'name': 'noise', - 'vocabulary_id': self.genre_vocab['id'] - } + "id": "dsagdsgsgsd", + "name": "noise", + "vocabulary_id": self.genre_vocab["id"], + } tag_string = helpers.json.dumps(tag_dict) - response = self.app.post('/api/action/tag_create', - params=tag_string, - extra_environ={'Authorization': - str(self.sysadmin_user.apikey)}, - status=409) - assert response.json['success'] is False - assert response.json['error']['id'] == [u'The input field id was not ' - 'expected.'] - - def test_add_tag_without_name(self): - '''Test the error response when a user tries to create a tag without a + response = app.post( + "/api/action/tag_create", + params=tag_string, + extra_environ={"Authorization": str(self.sysadmin_user.apikey)}, + status=409, + ) + assert response.json["success"] is False + assert response.json["error"]["id"] == [ + u"The input field id was not " "expected." + ] + + def test_add_tag_without_name(self, app): + """Test the error response when a user tries to create a tag without a name. - ''' - tag_dict = { - 'vocabulary_id': self.genre_vocab['id'] - } + """ + tag_dict = {"vocabulary_id": self.genre_vocab["id"]} tag_string = helpers.json.dumps(tag_dict) - response = self.app.post('/api/action/tag_create', - params=tag_string, - extra_environ={'Authorization': - str(self.sysadmin_user.apikey)}, - status=409) - assert response.json['success'] is False - assert response.json['error']['name'] == [u'Missing value'] - - def test_add_tag_invalid_name(self): - for name in ('Not a valid tag name!', '', None): - tag_dict = { - 'name': name, - 'vocabulary_id': self.genre_vocab['id'] - } + response = app.post( + "/api/action/tag_create", + params=tag_string, + extra_environ={"Authorization": str(self.sysadmin_user.apikey)}, + status=409, + ) + assert response.json["success"] is False + assert response.json["error"]["name"] == [u"Missing value"] + + def test_add_tag_invalid_name(self, app): + for name in ("Not a valid tag name!", "", None): + tag_dict = {"name": name, "vocabulary_id": self.genre_vocab["id"]} tag_string = helpers.json.dumps(tag_dict) - response = self.app.post('/api/action/tag_create', - params=tag_string, - extra_environ={'Authorization': - str(self.sysadmin_apikey)}, - status=409) - assert response.json['success'] is False - assert response.json['error']['name'] - - def test_add_tag_invalid_vocab_id(self): - tag_dict = { - 'name': 'noise', - 'vocabulary_id': 'xxcxzczxczxc', - } - tag_string = helpers.json.dumps(tag_dict) - response = self.app.post('/api/action/tag_create', + response = app.post( + "/api/action/tag_create", params=tag_string, - extra_environ={'Authorization': - str(self.sysadmin_user.apikey)}, - status=409) - assert response.json['success'] is False - assert response.json['error']['vocabulary_id'] == [ - u'Tag vocabulary was not found.'] - - def test_add_tag_not_logged_in(self): - tag_dict = { - 'name': 'noise', - 'vocabulary_id': self.genre_vocab['id'] - } + extra_environ={"Authorization": str(self.sysadmin_apikey)}, + status=409, + ) + assert response.json["success"] is False + assert response.json["error"]["name"] + + def test_add_tag_invalid_vocab_id(self, app): + tag_dict = {"name": "noise", "vocabulary_id": "xxcxzczxczxc"} tag_string = helpers.json.dumps(tag_dict) - response = self.app.post('/api/action/tag_create', - params=tag_string, - status=403) - assert response.json['success'] is False - assert response.json['error']['__type'] == 'Authorization Error' - - def test_add_tag_not_authorized(self): - tag_dict = { - 'name': 'noise', - 'vocabulary_id': self.genre_vocab['id'] - } + response = app.post( + "/api/action/tag_create", + params=tag_string, + extra_environ={"Authorization": str(self.sysadmin_user.apikey)}, + status=409, + ) + assert response.json["success"] is False + assert response.json["error"]["vocabulary_id"] == [ + u"Tag vocabulary was not found." + ] + + def test_add_tag_not_logged_in(self, app): + tag_dict = {"name": "noise", "vocabulary_id": self.genre_vocab["id"]} tag_string = helpers.json.dumps(tag_dict) - response = self.app.post('/api/action/tag_create', - params=tag_string, - extra_environ={'Authorization': - str(self.normal_user.apikey)}, - status=403) - assert response.json['success'] is False - assert response.json['error']['__type'] == 'Authorization Error' + response = app.post( + "/api/action/tag_create", params=tag_string, status=403 + ) + assert response.json["success"] is False + assert response.json["error"]["__type"] == "Authorization Error" + + def test_add_tag_not_authorized(self, app): + tag_dict = {"name": "noise", "vocabulary_id": self.genre_vocab["id"]} + tag_string = helpers.json.dumps(tag_dict) + response = app.post( + "/api/action/tag_create", + params=tag_string, + extra_environ={"Authorization": str(self.normal_user.apikey)}, + status=403, + ) + assert response.json["success"] is False + assert response.json["error"]["__type"] == "Authorization Error" def test_add_vocab_tag_to_dataset(self): - '''Test that a tag belonging to a vocab can be added to a dataset, - retrieved from the dataset, and then removed from the dataset.''' + """Test that a tag belonging to a vocab can be added to a dataset, + retrieved from the dataset, and then removed from the dataset.""" - ckan.model.repo.rebuild_db() - self.setup() ckan.tests.legacy.CreateTestData.create() # First add a tag to the vocab. vocab = self.genre_vocab - tag = self._create_tag(self.sysadmin_user, 'noise', vocab) + tag = self._create_tag(self.sysadmin_user, "noise", vocab) # Get a package from the API. - package = (self._post('/api/action/package_show', - {'id': self._post('/api/action/package_list')['result'][0]}) - ['result']) + package = self._post( + "/api/action/package_show", + {"id": self._post("/api/action/package_list")["result"][0]}, + )["result"] # Add the new vocab tag to the package. - package['tags'].append(tag) + package["tags"].append(tag) - updated_package = self._post('/api/action/package_update', - params={'id': package['id'], 'tags': package['tags']}, - extra_environ={'Authorization': - str(self.sysadmin_user.apikey)})['result'] + updated_package = self._post( + "/api/action/package_update", + params={"id": package["id"], "tags": package["tags"]}, + extra_environ={"Authorization": str(self.sysadmin_user.apikey)}, + )["result"] # Test that the new vocab tag was added to the package. - tags_in_pkg = [tag_in_pkg for tag_in_pkg in updated_package['tags'] if - tag_in_pkg['name'] == tag['name'] and - tag_in_pkg['vocabulary_id'] == tag['vocabulary_id']] + tags_in_pkg = [ + tag_in_pkg + for tag_in_pkg in updated_package["tags"] + if tag_in_pkg["name"] == tag["name"] + and tag_in_pkg["vocabulary_id"] == tag["vocabulary_id"] + ] assert len(tags_in_pkg) == 1 # Test that the package appears in tag_show. - noise_tag = self._post('/api/action/tag_show', - params={'id': 'noise', - 'vocabulary_id': vocab['id'], - 'include_datasets': True} - )['result'] - assert len([p for p in noise_tag['packages'] if - p['id'] == updated_package['id']]) == 1 + noise_tag = self._post( + "/api/action/tag_show", + params={ + "id": "noise", + "vocabulary_id": vocab["id"], + "include_datasets": True, + }, + )["result"] + assert ( + len( + [ + p + for p in noise_tag["packages"] + if p["id"] == updated_package["id"] + ] + ) + == 1 + ) # Remove the new vocab tag from the package. - package['tags'].remove(tag) - updated_package = self._post('/api/action/package_update', - params={'id': package['id'], 'tags': package['tags']}, - extra_environ={'Authorization': - str(self.sysadmin_user.apikey)})['result'] + package["tags"].remove(tag) + updated_package = self._post( + "/api/action/package_update", + params={"id": package["id"], "tags": package["tags"]}, + extra_environ={"Authorization": str(self.sysadmin_user.apikey)}, + )["result"] # Test that the tag no longer appears in the list of tags for the # package. - package = (self._post('/api/action/package_show', - {'id': self._post('/api/action/package_list')['result'][0]}) - ['result']) - tags_in_pkg = [tag_in_pkg for tag_in_pkg in package['tags'] if - tag_in_pkg['name'] == tag['name'] and - tag_in_pkg['vocabulary_id'] == tag['vocabulary_id']] + package = self._post( + "/api/action/package_show", + {"id": self._post("/api/action/package_list")["result"][0]}, + )["result"] + tags_in_pkg = [ + tag_in_pkg + for tag_in_pkg in package["tags"] + if tag_in_pkg["name"] == tag["name"] + and tag_in_pkg["vocabulary_id"] == tag["vocabulary_id"] + ] assert len(tags_in_pkg) == 0 def test_delete_tag_from_vocab(self): - '''Test that a tag can be deleted from a vocab.''' + """Test that a tag can be deleted from a vocab.""" - ckan.model.repo.rebuild_db() - self.setup() ckan.tests.legacy.CreateTestData.create() vocab = self.genre_vocab # First add some tags to the vocab. - noise_tag = self._create_tag(self.sysadmin_user, 'noise', vocab) - ragga_tag = self._create_tag(self.sysadmin_user, 'ragga', vocab) - grunge_tag = self._create_tag(self.sysadmin_user, 'grunge', vocab) - funk_tag = self._create_tag(self.sysadmin_user, 'funk', vocab) + noise_tag = self._create_tag(self.sysadmin_user, "noise", vocab) + ragga_tag = self._create_tag(self.sysadmin_user, "ragga", vocab) + grunge_tag = self._create_tag(self.sysadmin_user, "grunge", vocab) + funk_tag = self._create_tag(self.sysadmin_user, "funk", vocab) tags = (noise_tag, ragga_tag, grunge_tag, funk_tag) # Get a package from the API. - package = (self._post('/api/action/package_show', - {'id': self._post('/api/action/package_list')['result'][0]}) - ['result']) + package = self._post( + "/api/action/package_show", + {"id": self._post("/api/action/package_list")["result"][0]}, + )["result"] # Add the new vocab tags to the package. for tag in tags: - package['tags'].append(tag) + package["tags"].append(tag) - updated_package = self._post('/api/action/package_update', - params={'id': package['id'], 'tags': package['tags']}, - extra_environ={'Authorization': - str(self.sysadmin_user.apikey)})['result'] + updated_package = self._post( + "/api/action/package_update", + params={"id": package["id"], "tags": package["tags"]}, + extra_environ={"Authorization": str(self.sysadmin_user.apikey)}, + )["result"] # Test that the new vocab tags were added to the package. for tag in tags: - tags_in_pkg = [tag_in_pkg for tag_in_pkg in - updated_package['tags'] if tag_in_pkg['name'] == - tag['name'] and tag_in_pkg['vocabulary_id'] == - tag['vocabulary_id']] + tags_in_pkg = [ + tag_in_pkg + for tag_in_pkg in updated_package["tags"] + if tag_in_pkg["name"] == tag["name"] + and tag_in_pkg["vocabulary_id"] == tag["vocabulary_id"] + ] assert len(tags_in_pkg) == 1 # Now delete the tags from the vocab. tags_before = self._list_tags(vocab) - self._delete_tag(self.sysadmin_user, noise_tag['name'], vocab['name']) - self._delete_tag(self.sysadmin_user, ragga_tag['id'], vocab['name']) - self._delete_tag(self.sysadmin_user, grunge_tag['id'], vocab['id']) - self._delete_tag(self.sysadmin_user, funk_tag['name'], vocab['id']) + self._delete_tag(self.sysadmin_user, noise_tag["name"], vocab["name"]) + self._delete_tag(self.sysadmin_user, ragga_tag["id"], vocab["name"]) + self._delete_tag(self.sysadmin_user, grunge_tag["id"], vocab["id"]) + self._delete_tag(self.sysadmin_user, funk_tag["name"], vocab["id"]) # Test that the tags no longer appear in the list of tags for the # vocab. tags_after = self._list_tags(vocab) assert len(tags_after) == len(tags_before) - 4 - assert tag['name'] not in tags_after - difference = [tag_name for tag_name in tags_before if tag_name not in - tags_after] - assert sorted(difference) == sorted(tag['name'] for tag in tags) + assert tag["name"] not in tags_after + difference = [ + tag_name for tag_name in tags_before if tag_name not in tags_after + ] + assert sorted(difference) == sorted([tag["name"] for tag in tags]) # Test that the tags no longer appear in the list of tags for the # package. - package = (self._post('/api/action/package_show', - {'id': self._post('/api/action/package_list')['result'][0]}) - ['result']) + package = self._post( + "/api/action/package_show", + {"id": self._post("/api/action/package_list")["result"][0]}, + )["result"] for tag in tags: - tags_in_pkg = [tag_in_pkg for tag_in_pkg in package['tags'] if - tag_in_pkg['name'] == tag['name'] and - tag_in_pkg['vocabulary_id'] == tag['vocabulary_id']] + tags_in_pkg = [ + tag_in_pkg + for tag_in_pkg in package["tags"] + if tag_in_pkg["name"] == tag["name"] + and tag_in_pkg["vocabulary_id"] == tag["vocabulary_id"] + ] assert len(tags_in_pkg) == 0 def test_delete_free_tag(self): - '''Test that a free tag can be deleted via the API, and is + """Test that a free tag can be deleted via the API, and is automatically removed from datasets. - ''' - ckan.model.repo.rebuild_db() - self.setup() + """ ckan.tests.legacy.CreateTestData.create() # Get a package from the API. - package = (self._post('/api/action/package_show', - {'id': self._post('/api/action/package_list')['result'][0]}) - ['result']) - package_id = package['id'] + package = self._post( + "/api/action/package_show", + {"id": self._post("/api/action/package_list")["result"][0]}, + )["result"] + package_id = package["id"] # Add some new free tags to the package. - tags = package['tags'] - tags.append({'name': 'ducks'}) - tags.append({'name': 'birds'}) - self._post('/api/action/package_update', - params={'id': package['id'], 'tags': tags}, - extra_environ={'Authorization': - str(self.sysadmin_user.apikey)}) + tags = package["tags"] + tags.append({"name": "ducks"}) + tags.append({"name": "birds"}) + self._post( + "/api/action/package_update", + params={"id": package["id"], "tags": tags}, + extra_environ={"Authorization": str(self.sysadmin_user.apikey)}, + ) # Test that the new tags appear in the list of tags. tags = self._list_tags() - assert [tag for tag in tags].count('ducks') == 1 - assert [tag for tag in tags].count('birds') == 1 + assert [tag for tag in tags].count("ducks") == 1 + assert [tag for tag in tags].count("birds") == 1 # Test that the new tags appear in the package's list of tags. - package = (self._post('/api/action/package_show', - {'id': package_id})['result']) - packages_tags = [tag['name'] for tag in package['tags']] - assert [tag for tag in packages_tags].count('ducks') == 1 - assert [tag for tag in packages_tags].count('birds') == 1 + package = self._post("/api/action/package_show", {"id": package_id})[ + "result" + ] + packages_tags = [tag["name"] for tag in package["tags"]] + assert [tag for tag in packages_tags].count("ducks") == 1 + assert [tag for tag in packages_tags].count("birds") == 1 # Now delete the tags. - self._delete_tag(self.sysadmin_user, 'ducks') - birds_tag_id = self._post('/api/action/tag_show', - {'id': 'birds'})['result']['id'] + self._delete_tag(self.sysadmin_user, "ducks") + birds_tag_id = self._post("/api/action/tag_show", {"id": "birds"})[ + "result" + ]["id"] self._delete_tag(self.sysadmin_user, birds_tag_id) # Test that the tags no longer appear in the list of tags. tags = self._list_tags() - assert [tag for tag in tags].count('ducks') == 0 - assert [tag for tag in tags].count('birds') == 0 + assert [tag for tag in tags].count("ducks") == 0 + assert [tag for tag in tags].count("birds") == 0 # Test that the tags no longer appear in the package's list of tags. - package = (self._post('/api/action/package_show', - {'id': package_id})['result']) - packages_tags = [tag['name'] for tag in package['tags']] - assert [tag for tag in packages_tags].count('ducks') == 0 - assert [tag for tag in packages_tags].count('birds') == 0 - - def test_delete_tag_no_id(self): - '''Test the error response when a user tries to delete a tag without + package = self._post("/api/action/package_show", {"id": package_id})[ + "result" + ] + packages_tags = [tag["name"] for tag in package["tags"]] + assert [tag for tag in packages_tags].count("ducks") == 0 + assert [tag for tag in packages_tags].count("birds") == 0 + + def test_delete_tag_no_id(self, app): + """Test the error response when a user tries to delete a tag without giving the tag id. - ''' + """ vocab = self.genre_vocab - self._create_tag(self.sysadmin_user, 'noise', vocab) + self._create_tag(self.sysadmin_user, "noise", vocab) - for tag_id in ('missing', '', None): + for tag_id in ("missing", "", None): # Now try to delete the tag from the vocab. - params = {'vocabulary_id': vocab['name']} - if tag_id != 'missing': - params['id'] = tag_id - response = self.app.post('/api/action/tag_delete', - params=helpers.json.dumps(params), - extra_environ={'Authorization': - str(self.sysadmin_user.apikey)}, - status=409) - assert response.json['success'] is False - assert 'id' in response.json['error'] - assert response.json['error']['id'] == 'id not in data' - - def test_delete_tag_no_vocab(self): - '''Test the error response when a user tries to delete a vocab tag + params = {"vocabulary_id": vocab["name"]} + if tag_id != "missing": + params["id"] = tag_id + response = app.post( + "/api/action/tag_delete", + params=helpers.json.dumps(params), + extra_environ={ + "Authorization": str(self.sysadmin_user.apikey) + }, + status=409, + ) + assert response.json["success"] is False + assert "id" in response.json["error"] + assert response.json["error"]["id"] == "id not in data" + + def test_delete_tag_no_vocab(self, app): + """Test the error response when a user tries to delete a vocab tag without giving the vocab name. - ''' + """ vocab = self.genre_vocab - tag = self._create_tag(self.sysadmin_user, 'noise', vocab) + tag = self._create_tag(self.sysadmin_user, "noise", vocab) # Now try to delete the tag from the vocab. - for vocab_name in ('', None, 'missing'): - params = {'id': tag['name']} - if vocab_name != 'missing': - params['vocabulary_id'] = vocab_name - response = self.app.post('/api/action/tag_delete', - params=helpers.json.dumps(params), - extra_environ={'Authorization': - str(self.sysadmin_user.apikey)}, - status=404) - assert response.json['success'] is False - msg = response.json['error']['message'] + for vocab_name in ("", None, "missing"): + params = {"id": tag["name"]} + if vocab_name != "missing": + params["vocabulary_id"] = vocab_name + response = app.post( + "/api/action/tag_delete", + params=helpers.json.dumps(params), + extra_environ={ + "Authorization": str(self.sysadmin_user.apikey) + }, + status=404, + ) + assert response.json["success"] is False + msg = response.json["error"]["message"] assert msg == u'Not found: Could not find tag "{0}"'.format( - tag['name']), msg + tag["name"] + ), msg - def test_delete_tag_not_exists(self): - '''Test the error response when a user tries to delete a from a vocab + def test_delete_tag_not_exists(self, app): + """Test the error response when a user tries to delete a from a vocab but there is no tag with that name in the vocab. - ''' + """ vocab = self.genre_vocab - self._create_tag(self.sysadmin_user, 'noise', vocab) - - params = {'id': 'nonexistent', - 'vocabulary_id': self.genre_vocab['name']} - response = self.app.post('/api/action/tag_delete', - params=helpers.json.dumps(params), - extra_environ={'Authorization': - str(self.sysadmin_user.apikey)}, - status=404) - assert response.json['success'] is False - msg = response.json['error']['message'] - assert msg == u'Not found: Could not find tag "%s"' % 'nonexistent', \ - msg - - def test_delete_tag_vocab_not_exists(self): - '''Test the error response when a user tries to delete a from a vocab + self._create_tag(self.sysadmin_user, "noise", vocab) + + params = { + "id": "nonexistent", + "vocabulary_id": self.genre_vocab["name"], + } + response = app.post( + "/api/action/tag_delete", + params=helpers.json.dumps(params), + extra_environ={"Authorization": str(self.sysadmin_user.apikey)}, + status=404, + ) + assert response.json["success"] is False + msg = response.json["error"]["message"] + assert ( + msg == u'Not found: Could not find tag "%s"' % "nonexistent" + ), msg + + def test_delete_tag_vocab_not_exists(self, app): + """Test the error response when a user tries to delete a from a vocab but there is no vocab with that name. - ''' + """ vocab = self.genre_vocab - tag = self._create_tag(self.sysadmin_user, 'noise', vocab) - - params = {'id': tag['name'], - 'vocabulary_id': 'nonexistent'} - response = self.app.post('/api/action/tag_delete', - params=helpers.json.dumps(params), - extra_environ={'Authorization': - str(self.sysadmin_user.apikey)}, - status=404) - assert response.json['success'] is False - msg = response.json['error']['message'] - assert msg == u"Not found: could not find vocabulary 'nonexistent'", \ - msg - - def test_delete_tag_invalid_tag(self): - '''Test the error response when a user tries to delete a tag but gives + tag = self._create_tag(self.sysadmin_user, "noise", vocab) + + params = {"id": tag["name"], "vocabulary_id": "nonexistent"} + response = app.post( + "/api/action/tag_delete", + params=helpers.json.dumps(params), + extra_environ={"Authorization": str(self.sysadmin_user.apikey)}, + status=404, + ) + assert response.json["success"] is False + msg = response.json["error"]["message"] + assert ( + msg == u"Not found: could not find vocabulary 'nonexistent'" + ), msg + + def test_delete_tag_invalid_tag(self, app): + """Test the error response when a user tries to delete a tag but gives an invalid tag name. - ''' + """ vocab = self.genre_vocab - self._create_tag(self.sysadmin_user, 'noise', vocab) - - for tag_name in ('Invalid!', ' '): - params = {'id': tag_name, - 'vocabulary_id': self.genre_vocab['name']} - response = self.app.post('/api/action/tag_delete', - params=helpers.json.dumps(params), - extra_environ={'Authorization': - str(self.sysadmin_user.apikey)}, - status=404) - assert response.json['success'] is False - msg = response.json['error']['message'] + self._create_tag(self.sysadmin_user, "noise", vocab) + + for tag_name in ("Invalid!", " "): + params = { + "id": tag_name, + "vocabulary_id": self.genre_vocab["name"], + } + response = app.post( + "/api/action/tag_delete", + params=helpers.json.dumps(params), + extra_environ={ + "Authorization": str(self.sysadmin_user.apikey) + }, + status=404, + ) + assert response.json["success"] is False + msg = response.json["error"]["message"] assert msg == u'Not found: Could not find tag "%s"' % tag_name, msg - def test_delete_tag_invalid_vocab(self): - '''Test the error response when a user tries to delete a tag but gives + def test_delete_tag_invalid_vocab(self, app): + """Test the error response when a user tries to delete a tag but gives an invalid vocab name. - ''' - vocab = self.genre_vocab - tag = self._create_tag(self.sysadmin_user, 'noise', vocab) - - for vocab_name in ('Invalid!', ' '): - params = {'id': tag['name'], 'vocabulary_id': vocab_name} - response = self.app.post('/api/action/tag_delete', - params=helpers.json.dumps(params), - extra_environ={'Authorization': - str(self.sysadmin_user.apikey)}, - status=404) - assert response.json['success'] is False - msg = response.json['error']['message'] - assert msg == u"Not found: could not find vocabulary '%s'" \ - % vocab_name, msg - - def test_delete_tag_not_logged_in(self): + """ vocab = self.genre_vocab - tag = self._create_tag(self.sysadmin_user, 'noise', vocab) + tag = self._create_tag(self.sysadmin_user, "noise", vocab) - params = {'id': tag['name'], - 'vocabulary_id': self.genre_vocab['name']} - response = self.app.post('/api/action/tag_delete', + for vocab_name in ("Invalid!", " "): + params = {"id": tag["name"], "vocabulary_id": vocab_name} + response = app.post( + "/api/action/tag_delete", params=helpers.json.dumps(params), - status=403) - assert response.json['success'] is False - error = response.json['error']['__type'] - assert error == u"Authorization Error", error + extra_environ={ + "Authorization": str(self.sysadmin_user.apikey) + }, + status=404, + ) + assert response.json["success"] is False + msg = response.json["error"]["message"] + assert ( + msg + == u"Not found: could not find vocabulary '%s'" % vocab_name + ), msg - def test_delete_tag_not_authorized(self): + def test_delete_tag_not_logged_in(self, app): vocab = self.genre_vocab - tag = self._create_tag(self.sysadmin_user, 'noise', vocab) + tag = self._create_tag(self.sysadmin_user, "noise", vocab) + + params = {"id": tag["name"], "vocabulary_id": self.genre_vocab["name"]} + response = app.post( + "/api/action/tag_delete", + params=helpers.json.dumps(params), + status=403, + ) + assert response.json["success"] is False + error = response.json["error"]["__type"] + assert error == u"Authorization Error", error - params = {'id': tag['name'], - 'vocabulary_id': self.genre_vocab['name']} - response = self.app.post('/api/action/tag_delete', - params=helpers.json.dumps(params), - extra_environ={'Authorization': - str(self.normal_user.apikey)}, - status=403) - assert response.json['success'] is False - msg = response.json['error']['__type'] + def test_delete_tag_not_authorized(self, app): + vocab = self.genre_vocab + tag = self._create_tag(self.sysadmin_user, "noise", vocab) + + params = {"id": tag["name"], "vocabulary_id": self.genre_vocab["name"]} + response = app.post( + "/api/action/tag_delete", + params=helpers.json.dumps(params), + extra_environ={"Authorization": str(self.normal_user.apikey)}, + status=403, + ) + assert response.json["success"] is False + msg = response.json["error"]["__type"] assert msg == u"Authorization Error" diff --git a/ckan/tests/legacy/functional/api/test_api.py b/ckan/tests/legacy/functional/api/test_api.py index 04bbdef63f3..d0e9c310bb8 100644 --- a/ckan/tests/legacy/functional/api/test_api.py +++ b/ckan/tests/legacy/functional/api/test_api.py @@ -1,51 +1,50 @@ # encoding: utf-8 -from nose.tools import assert_in -from ckan.tests.legacy.functional.api.base import * +from ckan.tests.legacy.functional.api.base import ( + ApiTestCase, + ControllerTestCase, + Api3TestCase, +) class ApiTestCase(ApiTestCase, ControllerTestCase): - - def test_get_api(self): - offset = self.offset('') - res = self.app.get(offset, status=[200]) + def test_get_api(self, app): + offset = self.offset("") + res = app.get(offset, status=[200]) self.assert_version_data(res) def assert_version_data(self, res): data = self.data_from_res(res) - assert 'version' in data, data + assert "version" in data, data expected_version = self.get_expected_api_version() - self.assert_equal(data['version'], expected_version) + assert data["version"] == expected_version -class TestApi3(Api3TestCase, ApiTestCase): - def test_readonly_is_get_able_with_normal_url_params(self): - '''Test that a read-only action is GET-able +class TestApi3(Api3TestCase, ApiTestCase): + def test_readonly_is_get_able_with_normal_url_params(self, app): + """Test that a read-only action is GET-able Picks an action within `get.py` and checks that it works if it's invoked with a http GET request. The action's data_dict is populated from the url parameters. - ''' - offset = self.offset('/action/package_search') - params = {'q': 'russian'} - res = self.app.get(offset, params=params, status=[200]) + """ + offset = self.offset("/action/package_search") + params = {"q": "russian"} + res = app.get(offset, params=params, status=[200]) - def test_sideeffect_action_is_not_get_able(self): - '''Test that a non-readonly action is not GET-able. + def test_sideeffect_action_is_not_get_able(self, app): + """Test that a non-readonly action is not GET-able. Picks an action outside of `get.py`, and checks that it 400s if an attempt to invoke with a http GET request is made. - ''' - offset = self.offset('/action/package_create') - data_dict = { - 'type': 'dataset', - 'name': 'a-name' - } - res = self.app.get(offset, - params=data_dict, - status=[400], - expect_errors=True) - assert_in('Bad request - JSON Error: Invalid request.'\ - ' Please use POST method for your request', - res.body) + """ + offset = self.offset("/action/package_create") + data_dict = {"type": "dataset", "name": "a-name"} + res = app.get( + offset, params=data_dict, status=[400], expect_errors=True + ) + assert ( + "Bad request - JSON Error: Invalid request." + " Please use POST method for your request" in res.body + ) diff --git a/ckan/tests/legacy/functional/api/test_email_notifications.py b/ckan/tests/legacy/functional/api/test_email_notifications.py index 26b9147472c..9f46b59a3fd 100644 --- a/ckan/tests/legacy/functional/api/test_email_notifications.py +++ b/ckan/tests/legacy/functional/api/test_email_notifications.py @@ -9,453 +9,609 @@ from ckan.tests.legacy import TestController as ControllerTestCase -class TestEmailNotifications(mock_mail_server.SmtpServerHarness, ControllerTestCase): - +class TestEmailNotifications( + mock_mail_server.SmtpServerHarness, ControllerTestCase +): @classmethod def setup_class(cls): + model.repo.rebuild_db() + mock_mail_server.SmtpServerHarness.setup_class() tests.CreateTestData.create() cls.app = helpers._get_test_app() - joeadmin = model.User.get('joeadmin') - cls.joeadmin = {'id': joeadmin.id, - 'apikey': joeadmin.apikey, - } - testsysadmin = model.User.get('testsysadmin') - cls.testsysadmin = {'id': testsysadmin.id, - 'apikey': testsysadmin.apikey, - } - annafan = model.User.get('annafan') - cls.annafan = {'id': annafan.id, - 'apikey': annafan.apikey, - } + joeadmin = model.User.get("joeadmin") + cls.joeadmin = {"id": joeadmin.id, "apikey": joeadmin.apikey} + testsysadmin = model.User.get("testsysadmin") + cls.testsysadmin = { + "id": testsysadmin.id, + "apikey": testsysadmin.apikey, + } + annafan = model.User.get("annafan") + cls.annafan = {"id": annafan.id, "apikey": annafan.apikey} - @classmethod - def teardown_class(cls): - mock_mail_server.SmtpServerHarness.teardown_class() - model.repo.rebuild_db() + # Register a new user. + cls.sara = tests.call_action_api( + cls.app, + "user_create", + apikey=cls.testsysadmin["apikey"], + name="sara", + email="sara@sararollins.com", + password="TestPassword1", + fullname="Sara Rollins", + activity_streams_email_notifications=True, + ) def check_email(self, email, address, name, subject): - assert email[1] == 'info@test.ckan.net' + assert email[1] == "info@test.ckan.net" assert email[2] == [address] - encoded_subject = 'Subject: =?utf-8?q?{subject}'.format( - subject=subject.replace(' ', '_')) + encoded_subject = "Subject: =?utf-8?q?{subject}".format( + subject=subject.replace(" ", "_") + ) assert encoded_subject in email[3] # TODO: Check that body contains link to dashboard and email prefs. def test_00_send_email_notifications_not_logged_in(self): - '''Not-logged-in users shouldn't be able to send email notifications. + """Not-logged-in users shouldn't be able to send email notifications. - ''' - tests.call_action_api(self.app, 'send_email_notifications', - status=403) + """ + tests.call_action_api(self.app, "send_email_notifications", status=403) - def test_00_send_email_notifications_not_authorized(self): - '''Unauthorized users shouldn't be able to send email notifications. + # def test_00_send_email_notifications_not_authorized(self): + """Unauthorized users shouldn't be able to send email notifications. - ''' - tests.call_action_api(self.app, 'send_email_notifications', - apikey=self.annafan['apikey'], status=403) + """ + tests.call_action_api( + self.app, + "send_email_notifications", + apikey=self.annafan["apikey"], + status=403, + ) - def test_01_no_email_notifications_after_registration(self): - '''A new user who isn't following anything shouldn't get any emails.''' + # def test_01_no_email_notifications_after_registration(self): + """A new user who isn't following anything shouldn't get any emails.""" # Clear any emails already sent due to CreateTestData.create(). - tests.call_action_api(self.app, 'send_email_notifications', - apikey=self.testsysadmin['apikey']) + tests.call_action_api( + self.app, + "send_email_notifications", + apikey=self.testsysadmin["apikey"], + ) self.clear_smtp_messages() - # Register a new user. - sara = tests.call_action_api(self.app, 'user_create', - apikey=self.testsysadmin['apikey'], name='sara', - email='sara@sararollins.com', password='TestPassword1', - fullname='Sara Rollins', - activity_streams_email_notifications=True) - - # Save the user for later tests to use. - TestEmailNotifications.sara = sara - # No notification emails should be sent to anyone at this point. - tests.call_action_api(self.app, 'send_email_notifications', - apikey=self.testsysadmin['apikey']) + tests.call_action_api( + self.app, + "send_email_notifications", + apikey=self.testsysadmin["apikey"], + ) assert len(self.get_smtp_messages()) == 0 - def test_02_one_new_activity(self): - '''A user with one new activity should get one email.''' + # def test_02_one_new_activity(self): + """A user with one new activity should get one email.""" # Make Sara follow something, have to do this to get new activity. - tests.call_action_api(self.app, 'follow_dataset', - apikey=self.sara['apikey'], id='warandpeace') + tests.call_action_api( + self.app, + "follow_dataset", + apikey=self.sara["apikey"], + id="warandpeace", + ) # Make someone else update the dataset Sara's following, this should # create a new activity on Sara's dashboard. - tests.call_action_api(self.app, 'package_update', - apikey=self.joeadmin['apikey'], name='warandpeace', - notes='updated') + tests.call_action_api( + self.app, + "package_update", + apikey=self.joeadmin["apikey"], + name="warandpeace", + notes="updated", + ) # Run the email notifier job, it should send one notification email # to Sara. - tests.call_action_api(self.app, 'send_email_notifications', - apikey=self.testsysadmin['apikey']) + tests.call_action_api( + self.app, + "send_email_notifications", + apikey=self.testsysadmin["apikey"], + ) assert len(self.get_smtp_messages()) == 1 email = self.get_smtp_messages()[0] - self.check_email(email, 'sara@sararollins.com', 'Sara Rollins', - '1 new activity from CKAN') + self.check_email( + email, + "sara@sararollins.com", + "Sara Rollins", + "1 new activity from CKAN", + ) self.clear_smtp_messages() - def test_03_multiple_new_activities(self): - '''Test that a user with multiple new activities gets just one email. + # def test_03_multiple_new_activities(self): + """Test that a user with multiple new activities gets just one email. - ''' + """ # Make someone else update the dataset Sara's following three times, # this should create three new activities on Sara's dashboard. for i in range(1, 4): - tests.call_action_api(self.app, 'package_update', - apikey=self.joeadmin['apikey'], name='warandpeace', - notes='updated {0} times'.format(i)) + tests.call_action_api( + self.app, + "package_update", + apikey=self.joeadmin["apikey"], + name="warandpeace", + notes="updated {0} times".format(i), + ) # Run the email notifier job, it should send one notification email # to Sara. - tests.call_action_api(self.app, 'send_email_notifications', - apikey=self.testsysadmin['apikey']) + tests.call_action_api( + self.app, + "send_email_notifications", + apikey=self.testsysadmin["apikey"], + ) assert len(self.get_smtp_messages()) == 1 email = self.get_smtp_messages()[0] - self.check_email(email, 'sara@sararollins.com', 'Sara Rollins', - '3 new activities from CKAN') + self.check_email( + email, + "sara@sararollins.com", + "Sara Rollins", + "3 new activities from CKAN", + ) self.clear_smtp_messages() - def test_04_no_repeat_email_notifications(self): - '''Test that a user does not get a second email notification for the + # def test_04_no_repeat_email_notifications(self): + """Test that a user does not get a second email notification for the same new activity. - ''' + """ # TODO: Assert that Sara has some new activities and has already had # an email about them. - tests.call_action_api(self.app, 'send_email_notifications', - apikey=self.testsysadmin['apikey']) + tests.call_action_api( + self.app, + "send_email_notifications", + apikey=self.testsysadmin["apikey"], + ) assert len(self.get_smtp_messages()) == 0 - def test_05_no_email_if_seen_on_dashboard(self): - '''Test that emails are not sent for activities already seen on dash. + # def test_05_no_email_if_seen_on_dashboard(self): + """Test that emails are not sent for activities already seen on dash. If a user gets some new activities in her dashboard activity stream, then views her dashboard activity stream, then she should not got any email notifications about these new activities. - ''' + """ # Make someone else update the dataset Sara's following, this should # create a new activity on Sara's dashboard. - tests.call_action_api(self.app, 'package_update', - apikey=self.joeadmin['apikey'], name='warandpeace', - notes='updated by test_05_no_email_if_seen_on_dashboard') + tests.call_action_api( + self.app, + "package_update", + apikey=self.joeadmin["apikey"], + name="warandpeace", + notes="updated by test_05_no_email_if_seen_on_dashboard", + ) # At this point Sara should have a new activity on her dashboard. - num_new_activities = tests.call_action_api(self.app, - 'dashboard_new_activities_count', apikey=self.sara['apikey']) + num_new_activities = tests.call_action_api( + self.app, + "dashboard_new_activities_count", + apikey=self.sara["apikey"], + ) assert num_new_activities > 0, num_new_activities # View Sara's dashboard. - tests.call_action_api(self.app, 'dashboard_mark_activities_old', - apikey=self.sara['apikey']) + tests.call_action_api( + self.app, + "dashboard_mark_activities_old", + apikey=self.sara["apikey"], + ) # No email should be sent. - tests.call_action_api(self.app, 'send_email_notifications', - apikey=self.testsysadmin['apikey']) + tests.call_action_api( + self.app, + "send_email_notifications", + apikey=self.testsysadmin["apikey"], + ) assert len(self.get_smtp_messages()) == 0 - def test_05_no_email_notifications_when_disabled_site_wide(self): - '''Users should not get email notifications when the feature is - disabled site-wide by a sysadmin.''' + # def test_05_no_email_notifications_when_disabled_site_wide(self): + """Users should not get email notifications when the feature is + disabled site-wide by a sysadmin.""" - def test_06_enable_email_notifications_sitewide(self): - '''When a sysadamin enables email notifications site wide, users + # def test_06_enable_email_notifications_sitewide(self): + """When a sysadamin enables email notifications site wide, users should not get emails for new activities from before email notifications were enabled. - ''' + """ # It's just easier to separate these tests into their own test class. class TestEmailNotificationsUserPreference( - mock_mail_server.SmtpServerHarness, - ControllerTestCase): - '''Tests for the email notifications (on/off) user preference.''' + mock_mail_server.SmtpServerHarness, ControllerTestCase +): + """Tests for the email notifications (on/off) user preference.""" @classmethod def setup_class(cls): + model.repo.rebuild_db() mock_mail_server.SmtpServerHarness.setup_class() tests.CreateTestData.create() cls.app = helpers._get_test_app() - joeadmin = model.User.get('joeadmin') - cls.joeadmin = {'id': joeadmin.id, - 'apikey': joeadmin.apikey, - } - testsysadmin = model.User.get('testsysadmin') - cls.testsysadmin = {'id': testsysadmin.id, - 'apikey': testsysadmin.apikey, - } - - @classmethod - def teardown_class(self): - mock_mail_server.SmtpServerHarness.teardown_class() - model.repo.rebuild_db() + joeadmin = model.User.get("joeadmin") + cls.joeadmin = {"id": joeadmin.id, "apikey": joeadmin.apikey} + + testsysadmin = model.User.get("testsysadmin") + cls.testsysadmin = { + "id": testsysadmin.id, + "apikey": testsysadmin.apikey, + } + cls.sara = tests.call_action_api( + cls.app, + "user_create", + apikey=cls.testsysadmin["apikey"], + name="sara", + email="sara@sararollins.com", + password="TestPassword1", + fullname="Sara Rollins", + ) def test_00_email_notifications_disabled_by_default(self): - '''Email notifications should be disabled for new users.''' - - # Register a new user. - sara = tests.call_action_api(self.app, 'user_create', - apikey=self.testsysadmin['apikey'], name='sara', - email='sara@sararollins.com', password='TestPassword1', - fullname='Sara Rollins') - - # Save the user for later tests to use. - TestEmailNotificationsUserPreference.sara = sara - - # Email notifications should be disabled for the new user. - assert sara['activity_streams_email_notifications'] is False - assert (tests.call_action_api(self.app, 'user_show', - apikey=self.sara['apikey'], id='sara')[ - 'activity_streams_email_notifications'] is False) - - def test_01_no_email_notifications_when_disabled(self): - '''Users with email notifications turned off should not get emails.''' + """Email notifications should be disabled for new users.""" + assert self.sara["activity_streams_email_notifications"] is False + assert ( + tests.call_action_api( + self.app, "user_show", apikey=self.sara["apikey"], id="sara" + )["activity_streams_email_notifications"] + is False + ) + + # def test_01_no_email_notifications_when_disabled(self): + """Users with email notifications turned off should not get emails.""" # First make Sara follow something so she gets some new activity in # her dashboard activity stream. - tests.call_action_api(self.app, 'follow_dataset', - apikey=self.sara['apikey'], id='warandpeace') + tests.call_action_api( + self.app, + "follow_dataset", + apikey=self.sara["apikey"], + id="warandpeace", + ) # Now make someone else update the dataset so Sara gets a new activity. - tests.call_action_api(self.app, 'package_update', - apikey=self.joeadmin['apikey'], id='warandpeace', - notes='updated') + tests.call_action_api( + self.app, + "package_update", + apikey=self.joeadmin["apikey"], + id="warandpeace", + notes="updated", + ) # Test that Sara has a new activity, just to make sure. - assert tests.call_action_api(self.app, - 'dashboard_new_activities_count', apikey=self.sara['apikey']) > 0 + assert ( + tests.call_action_api( + self.app, + "dashboard_new_activities_count", + apikey=self.sara["apikey"], + ) + > 0 + ) # No email notifications should be sent to Sara. - tests.call_action_api(self.app, 'send_email_notifications', - apikey=self.testsysadmin['apikey']) + tests.call_action_api( + self.app, + "send_email_notifications", + apikey=self.testsysadmin["apikey"], + ) assert len(self.get_smtp_messages()) == 0 - def test_02_enable_email_notifications(self): - '''Users should be able to turn email notifications on.''' + # def test_02_enable_email_notifications(self): + """Users should be able to turn email notifications on.""" # Mark all Sara's new activities as old, just to get a fresh start. - tests.call_action_api(self.app, 'dashboard_mark_activities_old', - apikey=self.sara['apikey']) - assert tests.call_action_api(self.app, - 'dashboard_new_activities_count', apikey=self.sara['apikey']) == 0 + tests.call_action_api( + self.app, + "dashboard_mark_activities_old", + apikey=self.sara["apikey"], + ) + assert ( + tests.call_action_api( + self.app, + "dashboard_new_activities_count", + apikey=self.sara["apikey"], + ) + == 0 + ) # Update the followed dataset a few times so Sara gets a few new # activities. for i in range(1, 4): - tests.call_action_api(self.app, 'package_update', - apikey=self.joeadmin['apikey'], id='warandpeace', - notes='updated {0} times'.format(i)) + tests.call_action_api( + self.app, + "package_update", + apikey=self.joeadmin["apikey"], + id="warandpeace", + notes="updated {0} times".format(i), + ) # Now Sara should have new activities. - assert tests.call_action_api(self.app, - 'dashboard_new_activities_count', apikey=self.sara['apikey']) == 3 + assert ( + tests.call_action_api( + self.app, + "dashboard_new_activities_count", + apikey=self.sara["apikey"], + ) + == 3 + ) # Run the email notifier job. - tests.call_action_api(self.app, 'send_email_notifications', - apikey=self.testsysadmin['apikey']) + tests.call_action_api( + self.app, + "send_email_notifications", + apikey=self.testsysadmin["apikey"], + ) assert len(self.get_smtp_messages()) == 0 # Enable email notifications for Sara. - self.sara['activity_streams_email_notifications'] = True - tests.call_action_api(self.app, 'user_update', **self.sara) - - tests.call_action_api(self.app, 'send_email_notifications', - apikey=self.testsysadmin['apikey']) - assert len(self.get_smtp_messages()) == 0, ("After a user enables " + self.sara["activity_streams_email_notifications"] = True + tests.call_action_api(self.app, "user_update", **self.sara) + + tests.call_action_api( + self.app, + "send_email_notifications", + apikey=self.testsysadmin["apikey"], + ) + assert len(self.get_smtp_messages()) == 0, ( + "After a user enables " "email notifications she should _not_ get emails about activities " "that happened before she enabled them, even if those activities " - "are still marked as 'new' on her dashboard.") + "are still marked as 'new' on her dashboard." + ) # Update the package to generate another new activity. - tests.call_action_api(self.app, 'package_update', - apikey=self.joeadmin['apikey'], id='warandpeace', - notes='updated yet again') + tests.call_action_api( + self.app, + "package_update", + apikey=self.joeadmin["apikey"], + id="warandpeace", + notes="updated yet again", + ) # Check that Sara has a new activity. - assert tests.call_action_api(self.app, - 'dashboard_new_activities_count', apikey=self.sara['apikey']) == 4 + assert ( + tests.call_action_api( + self.app, + "dashboard_new_activities_count", + apikey=self.sara["apikey"], + ) + == 4 + ) # Run the email notifier job, this time Sara should get one email. - tests.call_action_api(self.app, 'send_email_notifications', - apikey=self.testsysadmin['apikey']) + tests.call_action_api( + self.app, + "send_email_notifications", + apikey=self.testsysadmin["apikey"], + ) assert len(self.get_smtp_messages()) == 1 self.clear_smtp_messages() - def test_03_disable_email_notifications(self): - '''Users should be able to turn email notifications off.''' - - self.sara['activity_streams_email_notifications'] = False - tests.call_action_api(self.app, 'user_update', **self.sara) - - tests.call_action_api(self.app, 'package_update', - apikey=self.joeadmin['apikey'], id='warandpeace', - notes='updated yet again') - - assert tests.call_action_api(self.app, - 'dashboard_new_activities_count', apikey=self.sara['apikey']) > 0 - - tests.call_action_api(self.app, 'send_email_notifications', - apikey=self.testsysadmin['apikey']) + # def test_03_disable_email_notifications(self): + """Users should be able to turn email notifications off.""" + + self.sara["activity_streams_email_notifications"] = False + tests.call_action_api(self.app, "user_update", **self.sara) + + tests.call_action_api( + self.app, + "package_update", + apikey=self.joeadmin["apikey"], + id="warandpeace", + notes="updated yet again", + ) + + assert ( + tests.call_action_api( + self.app, + "dashboard_new_activities_count", + apikey=self.sara["apikey"], + ) + > 0 + ) + + tests.call_action_api( + self.app, + "send_email_notifications", + apikey=self.testsysadmin["apikey"], + ) assert len(self.get_smtp_messages()) == 0 class TestEmailNotificationsIniSetting( - mock_mail_server.SmtpServerHarness, ControllerTestCase): - '''Tests for the ckan.activity_streams_email_notifications config setting. + mock_mail_server.SmtpServerHarness, ControllerTestCase +): + """Tests for the ckan.activity_streams_email_notifications config setting. + + """ - ''' @classmethod def setup_class(cls): # Disable the email notifications feature. cls.app = helpers._get_test_app() - + model.repo.rebuild_db() mock_mail_server.SmtpServerHarness.setup_class() tests.CreateTestData.create() - joeadmin = model.User.get('joeadmin') - cls.joeadmin = {'id': joeadmin.id, - 'apikey': joeadmin.apikey, - } - testsysadmin = model.User.get('testsysadmin') - cls.testsysadmin = {'id': testsysadmin.id, - 'apikey': testsysadmin.apikey, - } + joeadmin = model.User.get("joeadmin") + cls.joeadmin = {"id": joeadmin.id, "apikey": joeadmin.apikey} + testsysadmin = model.User.get("testsysadmin") + cls.testsysadmin = { + "id": testsysadmin.id, + "apikey": testsysadmin.apikey, + } - @classmethod - def teardown_class(cls): - mock_mail_server.SmtpServerHarness.teardown_class() - model.repo.rebuild_db() - - @helpers.change_config('ckan.activity_streams_email_notifications', False) + @helpers.change_config("ckan.activity_streams_email_notifications", False) def test_00_send_email_notifications_feature_disabled(self): - '''Send_email_notifications API should error when feature disabled.''' + """Send_email_notifications API should error when feature disabled.""" # Register a new user. - sara = tests.call_action_api(self.app, 'user_create', - apikey=self.testsysadmin['apikey'], name='sara', - email='sara@sararollins.com', password='TestPassword1', - fullname='Sara Rollins') + sara = tests.call_action_api( + self.app, + "user_create", + apikey=self.testsysadmin["apikey"], + name="sara", + email="sara@sararollins.com", + password="TestPassword1", + fullname="Sara Rollins", + ) # Save the user for later tests to use. TestEmailNotificationsIniSetting.sara = sara # Enable the new user's email notifications preference. - sara['activity_streams_email_notifications'] = True - tests.call_action_api(self.app, 'user_update', **sara) - assert (tests.call_action_api(self.app, 'user_show', - apikey=self.sara['apikey'], id='sara')[ - 'activity_streams_email_notifications'] - is True) + sara["activity_streams_email_notifications"] = True + tests.call_action_api(self.app, "user_update", **sara) + assert ( + tests.call_action_api( + self.app, "user_show", apikey=self.sara["apikey"], id="sara" + )["activity_streams_email_notifications"] + is True + ) # Make Sara follow something so she gets some new activity in her # dashboard activity stream. - tests.call_action_api(self.app, 'follow_dataset', - apikey=self.sara['apikey'], id='warandpeace') + tests.call_action_api( + self.app, + "follow_dataset", + apikey=self.sara["apikey"], + id="warandpeace", + ) # Now make someone else update the dataset so Sara gets a new activity. - tests.call_action_api(self.app, 'package_update', - apikey=self.joeadmin['apikey'], id='warandpeace', - notes='updated') + tests.call_action_api( + self.app, + "package_update", + apikey=self.joeadmin["apikey"], + id="warandpeace", + notes="updated", + ) # Test that Sara has a new activity, just to make sure. - assert tests.call_action_api(self.app, - 'dashboard_new_activities_count', apikey=self.sara['apikey']) > 0 + assert ( + tests.call_action_api( + self.app, + "dashboard_new_activities_count", + apikey=self.sara["apikey"], + ) + > 0 + ) # We expect an error when trying to call the send_email_notifications # API, because the feature is disabled by the ini file setting. - tests.call_action_api(self.app, 'send_email_notifications', - apikey=self.testsysadmin['apikey'], status=409) - - @helpers.change_config('ckan.activity_streams_email_notifications', False) + tests.call_action_api( + self.app, + "send_email_notifications", + apikey=self.testsysadmin["apikey"], + status=409, + ) + + @helpers.change_config("ckan.activity_streams_email_notifications", False) def test_01_no_emails_sent_if_turned_off(self): - '''No emails should be sent if the feature is disabled site-wide.''' + """No emails should be sent if the feature is disabled site-wide.""" # No emails should have been sent by the last test. assert len(self.get_smtp_messages()) == 0 class TestEmailNotificationsSinceIniSetting( - mock_mail_server.SmtpServerHarness, - ControllerTestCase): - '''Tests for the ckan.email_notifications_since config setting.''' + mock_mail_server.SmtpServerHarness, ControllerTestCase +): + """Tests for the ckan.email_notifications_since config setting.""" @classmethod def setup_class(cls): cls.app = helpers._get_test_app() + model.repo.rebuild_db() mock_mail_server.SmtpServerHarness.setup_class() tests.CreateTestData.create() - joeadmin = model.User.get('joeadmin') - cls.joeadmin = {'id': joeadmin.id, - 'apikey': joeadmin.apikey, - } - testsysadmin = model.User.get('testsysadmin') - cls.testsysadmin = {'id': testsysadmin.id, - 'apikey': testsysadmin.apikey, - } - - @classmethod - def teardown_class(self): - mock_mail_server.SmtpServerHarness.teardown_class() - model.repo.rebuild_db() + joeadmin = model.User.get("joeadmin") + cls.joeadmin = {"id": joeadmin.id, "apikey": joeadmin.apikey} + testsysadmin = model.User.get("testsysadmin") + cls.testsysadmin = { + "id": testsysadmin.id, + "apikey": testsysadmin.apikey, + } # Don't send email notifications for activities older than 1 # microsecond - @helpers.change_config('ckan.email_notifications_since', '.000001') + @helpers.change_config("ckan.email_notifications_since", ".000001") def test_00_email_notifications_since(self): - '''No emails should be sent for activities older than + """No emails should be sent for activities older than email_notifications_since. - ''' + """ # Register a new user. - sara = tests.call_action_api(self.app, 'user_create', - apikey=self.testsysadmin['apikey'], name='sara', - email='sara@sararollins.com', password='TestPassword1', - fullname='Sara Rollins') + sara = tests.call_action_api( + self.app, + "user_create", + apikey=self.testsysadmin["apikey"], + name="sara", + email="sara@sararollins.com", + password="TestPassword1", + fullname="Sara Rollins", + ) # Save the user for later tests to use. TestEmailNotificationsSinceIniSetting.sara = sara # Enable the new user's email notifications preference. - sara['activity_streams_email_notifications'] = True - tests.call_action_api(self.app, 'user_update', **sara) - assert (tests.call_action_api(self.app, 'user_show', - apikey=self.sara['apikey'], id='sara')[ - 'activity_streams_email_notifications'] - is True) + sara["activity_streams_email_notifications"] = True + tests.call_action_api(self.app, "user_update", **sara) + assert ( + tests.call_action_api( + self.app, "user_show", apikey=self.sara["apikey"], id="sara" + )["activity_streams_email_notifications"] + is True + ) # Make Sara follow something so she gets some new activity in her # dashboard activity stream. - tests.call_action_api(self.app, 'follow_dataset', - apikey=self.sara['apikey'], id='warandpeace') + tests.call_action_api( + self.app, + "follow_dataset", + apikey=self.sara["apikey"], + id="warandpeace", + ) # Now make someone else update the dataset so Sara gets a new activity. - tests.call_action_api(self.app, 'package_update', - apikey=self.joeadmin['apikey'], id='warandpeace', - notes='updated') + tests.call_action_api( + self.app, + "package_update", + apikey=self.joeadmin["apikey"], + id="warandpeace", + notes="updated", + ) # Test that Sara has a new activity, just to make sure. - assert tests.call_action_api(self.app, - 'dashboard_new_activities_count', apikey=self.sara['apikey']) > 0 + assert ( + tests.call_action_api( + self.app, + "dashboard_new_activities_count", + apikey=self.sara["apikey"], + ) + > 0 + ) # Wait 1 microsecond, just to make sure we're passed the 'since' time. time.sleep(0.000001) # No emails should be sent. - tests.call_action_api(self.app, 'send_email_notifications', - apikey=self.testsysadmin['apikey']) + tests.call_action_api( + self.app, + "send_email_notifications", + apikey=self.testsysadmin["apikey"], + ) assert len(self.get_smtp_messages()) == 0 diff --git a/ckan/tests/legacy/functional/api/test_follow.py b/ckan/tests/legacy/functional/api/test_follow.py index 3db56545773..b7ff86f7b70 100644 --- a/ckan/tests/legacy/functional/api/test_follow.py +++ b/ckan/tests/legacy/functional/api/test_follow.py @@ -1,6 +1,6 @@ # encoding: utf-8 -'''Test for the follower API. +"""Test for the follower API. This module tests following, unfollowing, getting a list of what you're following or the number of things you're following, getting a list of who's @@ -11,60 +11,85 @@ activities from everything you're following), that is tested in test_dashboard.py. -''' +""" import datetime import ckan -from ckan.tests.legacy import are_foreign_keys_supported, SkipTest, CreateTestData, call_action_api -import ckan.tests.helpers as helpers +import pytest +from ckan.tests.legacy import ( + are_foreign_keys_supported, + SkipTest, + CreateTestData, + call_action_api, +) + def datetime_from_string(s): - '''Return a standard datetime.datetime object initialised from a string in + """Return a standard datetime.datetime object initialised from a string in the same format used for timestamps in dictized activities (the format produced by datetime.datetime.isoformat()) - ''' - return datetime.datetime.strptime(s, '%Y-%m-%dT%H:%M:%S.%f') + """ + return datetime.datetime.strptime(s, "%Y-%m-%dT%H:%M:%S.%f") + def follow(func): - '''Return a wrapper function for a follow_* function. + """Return a wrapper function for a follow_* function. The wrapper functions test the `followee_list` and `followee_count` API calls, in addition to any tests carried out by the wrapped function. - ''' - def wrapped_func(app, follower_id, apikey, object_id, object_arg, - sysadmin_apikey): - followee_count_before = call_action_api(app, - 'followee_count', id=follower_id) - followees_before = call_action_api(app, 'followee_list', - id=follower_id, apikey=sysadmin_apikey) + """ + + def wrapped_func( + app, follower_id, apikey, object_id, object_arg, sysadmin_apikey + ): + followee_count_before = call_action_api( + app, "followee_count", id=follower_id + ) + followees_before = call_action_api( + app, "followee_list", id=follower_id, apikey=sysadmin_apikey + ) func(app, follower_id, apikey, object_id, object_arg, sysadmin_apikey) - followee_count_after = call_action_api(app, - 'followee_count', id=follower_id) - followees_after = call_action_api(app, 'followee_list', - id=follower_id, apikey=sysadmin_apikey) + followee_count_after = call_action_api( + app, "followee_count", id=follower_id + ) + followees_after = call_action_api( + app, "followee_list", id=follower_id, apikey=sysadmin_apikey + ) assert followee_count_after == followee_count_before + 1, ( - "After a user follows an object, the user's `followee_count` " - "should increase by 1") + "After a user follows an object, the user's `followee_count` " + "should increase by 1" + ) assert len(followees_after) == len(followees_before) + 1, ( - "After a user follows an object, the object should appear in " - "the user's `followee_list`") - assert len([followee for followee in followees_after - if followee['dict']['id'] == object_id]) == 1, ( - "After a user follows an object, the object should appear in " - "the user's `followee_list`") + "After a user follows an object, the object should appear in " + "the user's `followee_list`" + ) + assert ( + len( + [ + followee + for followee in followees_after + if followee["dict"]["id"] == object_id + ] + ) + == 1 + ), ( + "After a user follows an object, the object should appear in " + "the user's `followee_list`" + ) return wrapped_func @follow -def follow_user(app, follower_id, apikey, object_id, object_arg, - sysadmin_apikey): - '''Test a user starting to follow another user via the API. +def follow_user( + app, follower_id, apikey, object_id, object_arg, sysadmin_apikey +): + """Test a user starting to follow another user via the API. :param follower_id: id of the user that will be following something. :param apikey: API key of the user that will be following something. @@ -72,62 +97,86 @@ def follow_user(app, follower_id, apikey, object_id, object_arg, :param object_arg: the argument to pass to follow_user as the id of the object that will be followed, could be the object's id or name. - ''' + """ # Record the object's followers count before. - follower_count_before = call_action_api(app, - 'user_follower_count', id=object_id) + follower_count_before = call_action_api( + app, "user_follower_count", id=object_id + ) # Record the follower's followees count before. - followee_count_before = call_action_api(app, - 'user_followee_count', id=follower_id) + followee_count_before = call_action_api( + app, "user_followee_count", id=follower_id + ) # Check that the user is not already following the object. - result = call_action_api(app, 'am_following_user', - id=object_id, apikey=apikey) + result = call_action_api( + app, "am_following_user", id=object_id, apikey=apikey + ) assert result is False # Make the user start following the object. before = datetime.datetime.now() - follower = call_action_api(app, 'follow_user', id=object_arg, - apikey=apikey) + follower = call_action_api( + app, "follow_user", id=object_arg, apikey=apikey + ) after = datetime.datetime.now() - assert follower['follower_id'] == follower_id - assert follower['object_id'] == object_id - timestamp = datetime_from_string(follower['datetime']) - assert (timestamp >= before and timestamp <= after), str(timestamp) + assert follower["follower_id"] == follower_id + assert follower["object_id"] == object_id + timestamp = datetime_from_string(follower["datetime"]) + assert timestamp >= before and timestamp <= after, str(timestamp) # Check that am_following_user now returns True. - result = call_action_api(app, 'am_following_user', - id=object_id, apikey=apikey) + result = call_action_api( + app, "am_following_user", id=object_id, apikey=apikey + ) assert result is True # Check that the follower appears in the object's list of followers. - followers = call_action_api(app, 'user_follower_list', - id=object_id, apikey=sysadmin_apikey) + followers = call_action_api( + app, "user_follower_list", id=object_id, apikey=sysadmin_apikey + ) assert len(followers) == follower_count_before + 1 - assert len([follower for follower in followers if follower['id'] == follower_id]) == 1 + assert ( + len( + [ + follower + for follower in followers + if follower["id"] == follower_id + ] + ) + == 1 + ) # Check that the object appears in the follower's list of followees. - followees = call_action_api(app, 'user_followee_list', - apikey=sysadmin_apikey, id=follower_id) + followees = call_action_api( + app, "user_followee_list", apikey=sysadmin_apikey, id=follower_id + ) assert len(followees) == followee_count_before + 1 - assert len([followee for followee in followees if followee['id'] == object_id]) == 1 + assert ( + len( + [followee for followee in followees if followee["id"] == object_id] + ) + == 1 + ) # Check that the object's follower count has increased by 1. - follower_count_after = call_action_api(app, - 'user_follower_count', id=object_id) + follower_count_after = call_action_api( + app, "user_follower_count", id=object_id + ) assert follower_count_after == follower_count_before + 1 # Check that the follower's followee count has increased by 1. - followee_count_after = call_action_api(app, - 'user_followee_count', id=follower_id) + followee_count_after = call_action_api( + app, "user_followee_count", id=follower_id + ) assert followee_count_after == followee_count_before + 1 @follow -def follow_dataset(app, follower_id, apikey, dataset_id, dataset_arg, - sysadmin_apikey): - '''Test a user starting to follow a dataset via the API. +def follow_dataset( + app, follower_id, apikey, dataset_id, dataset_arg, sysadmin_apikey +): + """Test a user starting to follow a dataset via the API. :param follower_id: id of the user. :param apikey: API key of the user. @@ -135,61 +184,88 @@ def follow_dataset(app, follower_id, apikey, dataset_id, dataset_arg, :param dataset_arg: the argument to pass to follow_dataset as the id of the dataset that will be followed, could be the dataset's id or name. - ''' + """ # Record the dataset's followers count before. - follower_count_before = call_action_api(app, - 'dataset_follower_count', id=dataset_id) + follower_count_before = call_action_api( + app, "dataset_follower_count", id=dataset_id + ) # Record the follower's followees count before. - followee_count_before = call_action_api(app, - 'dataset_followee_count', id=follower_id) + followee_count_before = call_action_api( + app, "dataset_followee_count", id=follower_id + ) # Check that the user is not already following the dataset. - result = call_action_api(app, 'am_following_dataset', - id=dataset_id, apikey=apikey) + result = call_action_api( + app, "am_following_dataset", id=dataset_id, apikey=apikey + ) assert result is False # Make the user start following the dataset. before = datetime.datetime.now() - follower = call_action_api(app, 'follow_dataset', - id=dataset_arg, apikey=apikey) + follower = call_action_api( + app, "follow_dataset", id=dataset_arg, apikey=apikey + ) after = datetime.datetime.now() - assert follower['follower_id'] == follower_id - assert follower['object_id'] == dataset_id - timestamp = datetime_from_string(follower['datetime']) - assert (timestamp >= before and timestamp <= after), str(timestamp) + assert follower["follower_id"] == follower_id + assert follower["object_id"] == dataset_id + timestamp = datetime_from_string(follower["datetime"]) + assert timestamp >= before and timestamp <= after, str(timestamp) # Check that am_following_dataset now returns True. - result = call_action_api(app, 'am_following_dataset', - id=dataset_id, apikey=apikey) + result = call_action_api( + app, "am_following_dataset", id=dataset_id, apikey=apikey + ) assert result is True # Check that the follower appears in the dataset's list of followers. - followers = call_action_api(app, 'dataset_follower_list', - id=dataset_id, apikey=sysadmin_apikey) + followers = call_action_api( + app, "dataset_follower_list", id=dataset_id, apikey=sysadmin_apikey + ) assert len(followers) == follower_count_before + 1 - assert len([follower for follower in followers if follower['id'] == follower_id]) == 1 + assert ( + len( + [ + follower + for follower in followers + if follower["id"] == follower_id + ] + ) + == 1 + ) # Check that the dataset appears in the follower's list of followees. - followees = call_action_api(app, 'dataset_followee_list', - apikey=sysadmin_apikey, id=follower_id) + followees = call_action_api( + app, "dataset_followee_list", apikey=sysadmin_apikey, id=follower_id + ) assert len(followees) == followee_count_before + 1 - assert len([followee for followee in followees if followee['id'] == dataset_id]) == 1 + assert ( + len( + [ + followee + for followee in followees + if followee["id"] == dataset_id + ] + ) + == 1 + ) # Check that the dataset's follower count has increased by 1. - follower_count_after = call_action_api(app, - 'dataset_follower_count', id=dataset_id) + follower_count_after = call_action_api( + app, "dataset_follower_count", id=dataset_id + ) assert follower_count_after == follower_count_before + 1 # Check that the follower's followee count has increased by 1. - followee_count_after = call_action_api(app, - 'dataset_followee_count', id=follower_id) + followee_count_after = call_action_api( + app, "dataset_followee_count", id=follower_id + ) assert followee_count_after == followee_count_before + 1 @follow def follow_group(app, user_id, apikey, group_id, group_arg, sysadmin_apikey): - '''Test a user starting to follow a group via the API. + """Test a user starting to follow a group via the API. :param user_id: id of the user :param apikey: API key of the user @@ -197,718 +273,1030 @@ def follow_group(app, user_id, apikey, group_id, group_arg, sysadmin_apikey): :param group_arg: the argument to pass to follow_group as the id of the group that will be followed, could be the group's id or name - ''' + """ # Record the group's followers count before. - follower_count_before = call_action_api(app, - 'group_follower_count', id=group_id) + follower_count_before = call_action_api( + app, "group_follower_count", id=group_id + ) # Record the user's followees count before. - followee_count_before = call_action_api(app, - 'group_followee_count', id=user_id) + followee_count_before = call_action_api( + app, "group_followee_count", id=user_id + ) # Check that the user is not already following the group. - result = call_action_api(app, 'am_following_group', - id=group_id, apikey=apikey) + result = call_action_api( + app, "am_following_group", id=group_id, apikey=apikey + ) assert result is False # Make the user start following the group. before = datetime.datetime.now() - follower = call_action_api(app, 'follow_group', id=group_id, - apikey=apikey) + follower = call_action_api(app, "follow_group", id=group_id, apikey=apikey) after = datetime.datetime.now() - assert follower['follower_id'] == user_id - assert follower['object_id'] == group_id - timestamp = datetime_from_string(follower['datetime']) - assert (timestamp >= before and timestamp <= after), str(timestamp) + assert follower["follower_id"] == user_id + assert follower["object_id"] == group_id + timestamp = datetime_from_string(follower["datetime"]) + assert timestamp >= before and timestamp <= after, str(timestamp) # Check that am_following_group now returns True. - result = call_action_api(app, 'am_following_group', - id=group_id, apikey=apikey) + result = call_action_api( + app, "am_following_group", id=group_id, apikey=apikey + ) assert result is True # Check that the user appears in the group's list of followers. - followers = call_action_api(app, 'group_follower_list', - id=group_id, apikey=sysadmin_apikey) + followers = call_action_api( + app, "group_follower_list", id=group_id, apikey=sysadmin_apikey + ) assert len(followers) == follower_count_before + 1 - assert len([follower for follower in followers - if follower['id'] == user_id]) == 1 + assert ( + len([follower for follower in followers if follower["id"] == user_id]) + == 1 + ) # Check that the group appears in the user's list of followees. - followees = call_action_api(app, 'group_followee_list', - apikey=sysadmin_apikey, id=user_id) + followees = call_action_api( + app, "group_followee_list", apikey=sysadmin_apikey, id=user_id + ) assert len(followees) == followee_count_before + 1 - assert len([followee for followee in followees - if followee['id'] == group_id]) == 1 + assert ( + len([followee for followee in followees if followee["id"] == group_id]) + == 1 + ) # Check that the group's follower count has increased by 1. - follower_count_after = call_action_api(app, - 'group_follower_count', id=group_id) + follower_count_after = call_action_api( + app, "group_follower_count", id=group_id + ) assert follower_count_after == follower_count_before + 1 # Check that the user's followee count has increased by 1. - followee_count_after = call_action_api(app, - 'group_followee_count', id=user_id) + followee_count_after = call_action_api( + app, "group_followee_count", id=user_id + ) assert followee_count_after == followee_count_before + 1 class TestFollow(object): - '''Tests for the follower API.''' + """Tests for the follower API.""" - @classmethod - def setup_class(self): + @pytest.fixture(autouse=True) + def initial_data(self, clean_db, app): CreateTestData.create() + self.app = app self.testsysadmin = { - 'id': ckan.model.User.get('testsysadmin').id, - 'apikey': ckan.model.User.get('testsysadmin').apikey, - 'name': ckan.model.User.get('testsysadmin').name, - } + "id": ckan.model.User.get("testsysadmin").id, + "apikey": ckan.model.User.get("testsysadmin").apikey, + "name": ckan.model.User.get("testsysadmin").name, + } self.annafan = { - 'id': ckan.model.User.get('annafan').id, - 'apikey': ckan.model.User.get('annafan').apikey, - 'name': ckan.model.User.get('annafan').name, - } + "id": ckan.model.User.get("annafan").id, + "apikey": ckan.model.User.get("annafan").apikey, + "name": ckan.model.User.get("annafan").name, + } self.russianfan = { - 'id': ckan.model.User.get('russianfan').id, - 'apikey': ckan.model.User.get('russianfan').apikey, - 'name': ckan.model.User.get('russianfan').name, - } + "id": ckan.model.User.get("russianfan").id, + "apikey": ckan.model.User.get("russianfan").apikey, + "name": ckan.model.User.get("russianfan").name, + } self.joeadmin = { - 'id': ckan.model.User.get('joeadmin').id, - 'apikey': ckan.model.User.get('joeadmin').apikey, - 'name': ckan.model.User.get('joeadmin').name, - } + "id": ckan.model.User.get("joeadmin").id, + "apikey": ckan.model.User.get("joeadmin").apikey, + "name": ckan.model.User.get("joeadmin").name, + } self.warandpeace = { - 'id': ckan.model.Package.get('warandpeace').id, - 'name': ckan.model.Package.get('warandpeace').name, - } + "id": ckan.model.Package.get("warandpeace").id, + "name": ckan.model.Package.get("warandpeace").name, + } self.annakarenina = { - 'id': ckan.model.Package.get('annakarenina').id, - 'name': ckan.model.Package.get('annakarenina').name, - } + "id": ckan.model.Package.get("annakarenina").id, + "name": ckan.model.Package.get("annakarenina").name, + } self.rogers_group = { - 'id': ckan.model.Group.get('roger').id, - 'name': ckan.model.Group.get('roger').name, - } + "id": ckan.model.Group.get("roger").id, + "name": ckan.model.Group.get("roger").name, + } self.davids_group = { - 'id': ckan.model.Group.get('david').id, - 'name': ckan.model.Group.get('david').name, - } - self.app = helpers._get_test_app() - - @classmethod - def teardown_class(self): - ckan.model.repo.rebuild_db() - - def test_00_visitor_cannot_get_user_follower_list(self): - call_action_api(self.app, 'user_follower_list', - id=self.russianfan['id'], status=403) - - def test_00_user_cannot_get_user_follower_list(self): - call_action_api(self.app, 'user_follower_list', - id=self.russianfan['id'], status=403, - apikey=self.annafan['apikey']) - - def test_00_sysadmin_can_get_user_follower_list(self): - call_action_api(self.app, 'user_follower_list', - id=self.russianfan['id'], status=200, - apikey=self.testsysadmin['apikey']) - - def test_00_visitor_cannot_get_dataset_follower_list(self): - call_action_api(self.app, 'dataset_follower_list', - id='warandpeace', status=403) - - def test_00_user_cannot_get_dataset_follower_list(self): - call_action_api(self.app, 'dataset_follower_list', - id='warandpeace', status=403, apikey=self.annafan['apikey']) - - def test_00_sysadmin_can_get_dataset_follower_list(self): - call_action_api(self.app, 'dataset_follower_list', - id='warandpeace', status=200, - apikey=self.testsysadmin['apikey']) - - def test_00_visitor_cannot_get_group_follower_list(self): - call_action_api(self.app, 'group_follower_list', - id='roger', status=403) - - def test_00_user_cannot_get_group_follower_list(self): - call_action_api(self.app, 'group_follower_list', - id='roger', status=403, apikey=self.annafan['apikey']) - - def test_00_sysadmin_can_get_group_follower_list(self): - call_action_api(self.app, 'group_follower_list', - id='roger', status=200, apikey=self.testsysadmin['apikey']) - - def test_00_visitor_cannot_get_followee_list(self): - call_action_api(self.app, 'followee_list', - id=self.russianfan['id'], status=403) - - def test_00_user_cannot_get_followee_list(self): - call_action_api(self.app, 'followee_list', - id=self.russianfan['id'], status=403, - apikey=self.annafan['apikey']) - - def test_00_sysadmin_can_get_followee_list(self): - call_action_api(self.app, 'followee_list', - id=self.russianfan['id'], status=200, - apikey=self.testsysadmin['apikey']) - - def test_00_visitor_cannot_get_user_followee_list(self): - '''A visitor cannot see what users a user is following.''' - call_action_api(self.app, 'user_followee_list', - id=self.russianfan['id'], status=403) - - def test_00_user_cannot_get_user_followee_list(self): - '''A user cannot see what users another user is following.''' - call_action_api(self.app, 'user_followee_list', - id=self.russianfan['id'], status=403, - apikey=self.annafan['apikey']) - - def test_00_sysadmin_can_get_user_followee_list(self): - '''A sysadmin can see what users another user is following.''' - call_action_api(self.app, 'user_followee_list', - id=self.russianfan['id'], status=200, - apikey=self.testsysadmin['apikey']) - - def test_00_user_can_get_own_user_followee_list(self): - '''A user can see what users she herself is following.''' - call_action_api(self.app, 'user_followee_list', - id=self.russianfan['id'], status=200, - apikey=self.russianfan['apikey']) - - def test_00_visitor_cannot_get_dataset_followee_list(self): - '''A visitor cannot see what datasets a user is following.''' - call_action_api(self.app, 'dataset_followee_list', - id=self.russianfan['id'], status=403) - - def test_00_user_cannot_get_dataset_followee_list(self): - '''A user cannot see what datasets another user is following.''' - call_action_api(self.app, 'dataset_followee_list', - id='russianfan', status=403, apikey=self.annafan['apikey']) - - def test_00_sysadmin_can_get_dataset_followee_list(self): - '''A sysadmin can see what datasets another user is following.''' - call_action_api(self.app, 'dataset_followee_list', - id='russianfan', status=200, - apikey=self.testsysadmin['apikey']) - - def test_00_user_can_get_own_dataset_followee_list(self): - '''A user can see what datasets she herself is following.''' - call_action_api(self.app, 'dataset_followee_list', - id=self.russianfan['id'], status=200, - apikey=self.russianfan['apikey']) - - def test_00_visitor_cannot_get_group_followee_list(self): - '''A visitor cannot see what groups a user is following.''' - call_action_api(self.app, 'group_followee_list', - id='roger', status=403) - - def test_00_user_cannot_get_group_followee_list(self): - '''A user cannot see what groups another user is following.''' - call_action_api(self.app, 'group_followee_list', - id='roger', status=403, apikey=self.annafan['apikey']) - - def test_00_sysadmin_can_get_group_followee_list(self): - '''A sysadmin can see what groups another user is following.''' - call_action_api(self.app, 'group_followee_list', - id=self.annafan['id'], status=200, - apikey=self.testsysadmin['apikey']) - - def test_00_user_can_get_own_group_followee_list(self): - '''A user can see what groups she herself is following.''' - call_action_api(self.app, 'group_followee_list', - id=self.russianfan['id'], status=200, - apikey=self.russianfan['apikey']) - - def test_01_user_follow_user_bad_apikey(self): - for apikey in ('bad api key', '', ' ', 'None', '3', '35.7', 'xxx'): - error = call_action_api(self.app, 'follow_user', - id=self.russianfan['id'], apikey=apikey, - status=403) - assert error['__type'] == 'Authorization Error' - - def test_01_user_follow_dataset_bad_apikey(self): - for apikey in ('bad api key', '', ' ', 'None', '3', '35.7', 'xxx'): - error = call_action_api(self.app, 'follow_dataset', - id=self.warandpeace['id'], apikey=apikey, - status=403) - assert error['__type'] == 'Authorization Error' - - def test_01_user_follow_group_bad_apikey(self): - for apikey in ('bad api key', '', ' ', 'None', '3', '35.7', 'xxx'): - error = call_action_api(self.app, 'follow_group', - id=self.rogers_group['id'], apikey=apikey, - status=403) - assert error['__type'] == 'Authorization Error' - - def test_01_user_follow_user_missing_apikey(self): - error = call_action_api(self.app, 'follow_user', - id=self.russianfan['id'], status=403) - assert error['__type'] == 'Authorization Error' - - def test_01_user_follow_dataset_missing_apikey(self): - error = call_action_api(self.app, 'follow_dataset', - id=self.warandpeace['id'], status=403) - assert error['__type'] == 'Authorization Error' - - def test_01_user_follow_group_missing_apikey(self): - error = call_action_api(self.app, 'follow_group', - id=self.rogers_group['id'], status=403) - assert error['__type'] == 'Authorization Error' - - def test_01_follow_bad_object_id(self): - for action in ('follow_user', 'follow_dataset', 'follow_group'): - for object_id in ('bad id', ' ', 3, 35.7, 'xxx'): - error = call_action_api(self.app, action, - id=object_id, - apikey=self.annafan['apikey'], status=409) - assert error['id'][0].startswith('Not found') - - def test_01_follow_empty_object_id(self): - for action in ('follow_user', 'follow_dataset', 'follow_group'): - for object_id in ('', None): - error = call_action_api(self.app, action, - id=object_id, - apikey=self.annafan['apikey'], status=409) - assert error['id'] == ['Missing value'] - - def test_01_follow_missing_object_id(self): - for action in ('follow_user', 'follow_dataset', 'follow_group'): - error = call_action_api(self.app, action, - apikey=self.annafan['apikey'], status=409) - assert error['id'] == ['Missing value'] - - def test_02_user_follow_user_by_id(self): - follow_user(self.app, self.annafan['id'], self.annafan['apikey'], - self.russianfan['id'], self.russianfan['id'], - self.testsysadmin['apikey']) - - def test_02_user_follow_dataset_by_id(self): - follow_dataset(self.app, self.annafan['id'], self.annafan['apikey'], - self.warandpeace['id'], self.warandpeace['id'], - self.testsysadmin['apikey']) - - def test_02_user_follow_group_by_id(self): - follow_group(self.app, self.annafan['id'], self.annafan['apikey'], - self.rogers_group['id'], self.rogers_group['id'], - self.testsysadmin['apikey']) - - def test_02_user_follow_user_by_name(self): - follow_user(self.app, self.annafan['id'], self.annafan['apikey'], - self.testsysadmin['id'], self.testsysadmin['name'], - self.testsysadmin['apikey']) - - def test_02_user_follow_dataset_by_name(self): - follow_dataset(self.app, self.joeadmin['id'], self.joeadmin['apikey'], - self.warandpeace['id'], self.warandpeace['name'], - self.testsysadmin['apikey']) - - def test_02_user_follow_group_by_name(self): - follow_group(self.app, self.joeadmin['id'], self.joeadmin['apikey'], - self.rogers_group['id'], self.rogers_group['name'], - self.testsysadmin['apikey']) - - def test_03_user_follow_user_already_following(self): - for object_id in (self.russianfan['id'], self.russianfan['name'], - self.testsysadmin['id'], self.testsysadmin['name']): - error = call_action_api(self.app, 'follow_user', - id=object_id, apikey=self.annafan['apikey'], - status=409) - assert error['message'].startswith('You are already following ') - - def test_03_user_follow_dataset_already_following(self): - for object_id in (self.warandpeace['id'], self.warandpeace['name']): - error = call_action_api(self.app, 'follow_dataset', - id=object_id, apikey=self.annafan['apikey'], - status=409) - assert error['message'].startswith('You are already following ') - - def test_03_user_follow_group_already_following(self): - for group_id in (self.rogers_group['id'], self.rogers_group['name']): - error = call_action_api(self.app, 'follow_group', - id=group_id, apikey=self.annafan['apikey'], - status=409) - assert error['message'].startswith('You are already following ') - - def test_03_user_cannot_follow_herself(self): - error = call_action_api(self.app, 'follow_user', - apikey=self.annafan['apikey'], status=409, - id=self.annafan['id']) - assert error['message'] == 'You cannot follow yourself' - - def test_04_follower_count_bad_id(self): - for action in ('user_follower_count', 'dataset_follower_count', - 'group_follower_count'): - for object_id in ('bad id', ' ', 3, 35.7, 'xxx', ''): - error = call_action_api(self.app, action, - status=409, id=object_id) - assert 'id' in error - - def test_04_follower_count_missing_id(self): - for action in ('user_follower_count', 'dataset_follower_count', - 'group_follower_count'): - error = call_action_api(self.app, action, status=409) - assert error['id'] == ['Missing value'] - - def test_04_user_follower_count_no_followers(self): - follower_count = call_action_api(self.app, - 'user_follower_count', id=self.annafan['id']) - assert follower_count == 0 - - def test_04_dataset_follower_count_no_followers(self): - follower_count = call_action_api(self.app, - 'dataset_follower_count', id=self.annakarenina['id']) - assert follower_count == 0 - - def test_04_group_follower_count_no_followers(self): - follower_count = call_action_api(self.app, - 'group_follower_count', id=self.davids_group['id']) - assert follower_count == 0 + "id": ckan.model.Group.get("david").id, + "name": ckan.model.Group.get("david").name, + } + + def test_00_visitor_cannot_get_user_follower_list(self, app): + call_action_api( + app, "user_follower_list", id=self.russianfan["id"], status=403 + ) + + # def test_00_user_cannot_get_user_follower_list(self): + call_action_api( + app, + "user_follower_list", + id=self.russianfan["id"], + status=403, + apikey=self.annafan["apikey"], + ) + + # def test_00_sysadmin_can_get_user_follower_list(self): + call_action_api( + app, + "user_follower_list", + id=self.russianfan["id"], + status=200, + apikey=self.testsysadmin["apikey"], + ) + + # def test_00_visitor_cannot_get_dataset_follower_list(self): + call_action_api( + app, "dataset_follower_list", id="warandpeace", status=403 + ) + + # def test_00_user_cannot_get_dataset_follower_list(self): + call_action_api( + app, + "dataset_follower_list", + id="warandpeace", + status=403, + apikey=self.annafan["apikey"], + ) + + # def test_00_sysadmin_can_get_dataset_follower_list(self): + call_action_api( + app, + "dataset_follower_list", + id="warandpeace", + status=200, + apikey=self.testsysadmin["apikey"], + ) + + # def test_00_visitor_cannot_get_group_follower_list(self): + call_action_api(app, "group_follower_list", id="roger", status=403) + + # def test_00_user_cannot_get_group_follower_list(self): + call_action_api( + app, + "group_follower_list", + id="roger", + status=403, + apikey=self.annafan["apikey"], + ) + + # def test_00_sysadmin_can_get_group_follower_list(self): + call_action_api( + app, + "group_follower_list", + id="roger", + status=200, + apikey=self.testsysadmin["apikey"], + ) + + # def test_00_visitor_cannot_get_followee_list(self): + call_action_api( + app, "followee_list", id=self.russianfan["id"], status=403 + ) + + # def test_00_user_cannot_get_followee_list(self): + call_action_api( + app, + "followee_list", + id=self.russianfan["id"], + status=403, + apikey=self.annafan["apikey"], + ) + + # def test_00_sysadmin_can_get_followee_list(self): + call_action_api( + app, + "followee_list", + id=self.russianfan["id"], + status=200, + apikey=self.testsysadmin["apikey"], + ) + + # def test_00_visitor_cannot_get_user_followee_list(self): + """A visitor cannot see what users a user is following.""" + call_action_api( + app, "user_followee_list", id=self.russianfan["id"], status=403 + ) + + # def test_00_user_cannot_get_user_followee_list(self): + """A user cannot see what users another user is following.""" + call_action_api( + app, + "user_followee_list", + id=self.russianfan["id"], + status=403, + apikey=self.annafan["apikey"], + ) + + # def test_00_sysadmin_can_get_user_followee_list(self): + """A sysadmin can see what users another user is following.""" + call_action_api( + app, + "user_followee_list", + id=self.russianfan["id"], + status=200, + apikey=self.testsysadmin["apikey"], + ) + + # def test_00_user_can_get_own_user_followee_list(self): + """A user can see what users she herself is following.""" + call_action_api( + app, + "user_followee_list", + id=self.russianfan["id"], + status=200, + apikey=self.russianfan["apikey"], + ) + + # def test_00_visitor_cannot_get_dataset_followee_list(self): + """A visitor cannot see what datasets a user is following.""" + call_action_api( + app, "dataset_followee_list", id=self.russianfan["id"], status=403 + ) + + # def test_00_user_cannot_get_dataset_followee_list(self): + """A user cannot see what datasets another user is following.""" + call_action_api( + app, + "dataset_followee_list", + id="russianfan", + status=403, + apikey=self.annafan["apikey"], + ) + + # def test_00_sysadmin_can_get_dataset_followee_list(self): + """A sysadmin can see what datasets another user is following.""" + call_action_api( + app, + "dataset_followee_list", + id="russianfan", + status=200, + apikey=self.testsysadmin["apikey"], + ) + + # def test_00_user_can_get_own_dataset_followee_list(self): + """A user can see what datasets she herself is following.""" + call_action_api( + app, + "dataset_followee_list", + id=self.russianfan["id"], + status=200, + apikey=self.russianfan["apikey"], + ) + + # def test_00_visitor_cannot_get_group_followee_list(self): + """A visitor cannot see what groups a user is following.""" + call_action_api(app, "group_followee_list", id="roger", status=403) + + # def test_00_user_cannot_get_group_followee_list(self): + """A user cannot see what groups another user is following.""" + call_action_api( + app, + "group_followee_list", + id="roger", + status=403, + apikey=self.annafan["apikey"], + ) + + # def test_00_sysadmin_can_get_group_followee_list(self): + """A sysadmin can see what groups another user is following.""" + call_action_api( + app, + "group_followee_list", + id=self.annafan["id"], + status=200, + apikey=self.testsysadmin["apikey"], + ) + + # def test_00_user_can_get_own_group_followee_list(self): + """A user can see what groups she herself is following.""" + call_action_api( + app, + "group_followee_list", + id=self.russianfan["id"], + status=200, + apikey=self.russianfan["apikey"], + ) + + def test_01_user_follow_user_bad_apikey(self, app): + for apikey in ("bad api key", "", " ", "None", "3", "35.7", "xxx"): + error = call_action_api( + app, + "follow_user", + id=self.russianfan["id"], + apikey=apikey, + status=403, + ) + assert error["__type"] == "Authorization Error" + + # def test_01_user_follow_dataset_bad_apikey(self): + for apikey in ("bad api key", "", " ", "None", "3", "35.7", "xxx"): + error = call_action_api( + app, + "follow_dataset", + id=self.warandpeace["id"], + apikey=apikey, + status=403, + ) + assert error["__type"] == "Authorization Error" + + # def test_01_user_follow_group_bad_apikey(self): + for apikey in ("bad api key", "", " ", "None", "3", "35.7", "xxx"): + error = call_action_api( + app, + "follow_group", + id=self.rogers_group["id"], + apikey=apikey, + status=403, + ) + assert error["__type"] == "Authorization Error" + + # def test_01_user_follow_user_missing_apikey(self): + error = call_action_api( + app, "follow_user", id=self.russianfan["id"], status=403 + ) + assert error["__type"] == "Authorization Error" + + # def test_01_user_follow_dataset_missing_apikey(self): + error = call_action_api( + app, "follow_dataset", id=self.warandpeace["id"], status=403 + ) + assert error["__type"] == "Authorization Error" + + # def test_01_user_follow_group_missing_apikey(self): + error = call_action_api( + app, "follow_group", id=self.rogers_group["id"], status=403 + ) + assert error["__type"] == "Authorization Error" + + # def test_01_follow_bad_object_id(self): + for action in ("follow_user", "follow_dataset", "follow_group"): + for object_id in ("bad id", " ", 3, 35.7, "xxx"): + error = call_action_api( + app, + action, + id=object_id, + apikey=self.annafan["apikey"], + status=409, + ) + assert error["id"][0].startswith("Not found") + + # def test_01_follow_empty_object_id(self): + for action in ("follow_user", "follow_dataset", "follow_group"): + for object_id in ("", None): + error = call_action_api( + app, + action, + id=object_id, + apikey=self.annafan["apikey"], + status=409, + ) + assert error["id"] == ["Missing value"] + + # def test_01_follow_missing_object_id(self): + for action in ("follow_user", "follow_dataset", "follow_group"): + error = call_action_api( + app, action, apikey=self.annafan["apikey"], status=409 + ) + assert error["id"] == ["Missing value"] + + def test_02_user_follow_user_by_id(self, app): + follow_user( + app, + self.annafan["id"], + self.annafan["apikey"], + self.russianfan["id"], + self.russianfan["id"], + self.testsysadmin["apikey"], + ) + + # def test_02_user_follow_dataset_by_id(self): + follow_dataset( + app, + self.annafan["id"], + self.annafan["apikey"], + self.warandpeace["id"], + self.warandpeace["id"], + self.testsysadmin["apikey"], + ) + + # def test_02_user_follow_group_by_id(self): + follow_group( + app, + self.annafan["id"], + self.annafan["apikey"], + self.rogers_group["id"], + self.rogers_group["id"], + self.testsysadmin["apikey"], + ) + + # def test_02_user_follow_user_by_name(self): + follow_user( + app, + self.annafan["id"], + self.annafan["apikey"], + self.testsysadmin["id"], + self.testsysadmin["name"], + self.testsysadmin["apikey"], + ) + + # def test_02_user_follow_dataset_by_name(self): + follow_dataset( + app, + self.joeadmin["id"], + self.joeadmin["apikey"], + self.warandpeace["id"], + self.warandpeace["name"], + self.testsysadmin["apikey"], + ) + + # def test_02_user_follow_group_by_name(self): + follow_group( + app, + self.joeadmin["id"], + self.joeadmin["apikey"], + self.rogers_group["id"], + self.rogers_group["name"], + self.testsysadmin["apikey"], + ) + + # def test_03_user_follow_user_already_following(self): + for object_id in ( + self.russianfan["id"], + self.russianfan["name"], + self.testsysadmin["id"], + self.testsysadmin["name"], + ): + error = call_action_api( + app, + "follow_user", + id=object_id, + apikey=self.annafan["apikey"], + status=409, + ) + assert error["message"].startswith("You are already following ") + + # def test_03_user_follow_dataset_already_following(self): + for object_id in (self.warandpeace["id"], self.warandpeace["name"]): + error = call_action_api( + app, + "follow_dataset", + id=object_id, + apikey=self.annafan["apikey"], + status=409, + ) + assert error["message"].startswith("You are already following ") + + # def test_03_user_follow_group_already_following(self): + for group_id in (self.rogers_group["id"], self.rogers_group["name"]): + error = call_action_api( + app, + "follow_group", + id=group_id, + apikey=self.annafan["apikey"], + status=409, + ) + assert error["message"].startswith("You are already following ") + + # def test_03_user_cannot_follow_herself(self): + error = call_action_api( + app, + "follow_user", + apikey=self.annafan["apikey"], + status=409, + id=self.annafan["id"], + ) + assert error["message"] == "You cannot follow yourself" def _followee_count_bad_id(self, action): - for object_id in ('bad id', ' ', 3, 35.7, 'xxx', ''): - error = call_action_api(self.app, action, - status=409, id=object_id) - assert 'id' in error - - def test_04_followee_count_bad_id(self): - self._followee_count_bad_id('followee_count') - - def test_04_user_followee_count_bad_id(self): - self._followee_count_bad_id('user_followee_count') - - def test_04_dataset_followee_count_bad_id(self): - self._followee_count_bad_id('dataset_followee_count') - - def test_04_group_followee_count_bad_id(self): - self._followee_count_bad_id('group_followee_count') + for object_id in ("bad id", " ", 3, 35.7, "xxx", ""): + error = call_action_api(self.app, action, status=409, id=object_id) + assert "id" in error def _followee_count_missing_id(self, action): error = call_action_api(self.app, action, status=409) - assert error['id'] == ['Missing value'] - - def test_04_followee_count_missing_id(self): - self._followee_count_missing_id('followee_count') - - def test_04_user_followee_count_missing_id(self): - self._followee_count_missing_id('user_followee_count') - - def test_04_dataset_followee_count_missing_id(self): - self._followee_count_missing_id('dataset_followee_count') - - def test_04_group_followee_count_missing_id(self): - self._followee_count_missing_id('group_followee_count') + assert error["id"] == ["Missing value"] def _followee_count_not_following_anything(self, action): - followee_count = call_action_api(self.app, action, - id=self.russianfan['id']) + followee_count = call_action_api( + self.app, action, id=self.russianfan["id"] + ) assert followee_count == 0 - def test_04_followee_count_not_following_anything(self): - self._followee_count_not_following_anything('followee_count') - - def test_04_user_followee_count_not_following_anything(self): - self._followee_count_not_following_anything('user_followee_count') - - def test_04_dataset_followee_count_not_following_anything(self): - self._followee_count_not_following_anything('dataset_followee_count') - - def test_04_group_followee_count_not_following_anything(self): - self._followee_count_not_following_anything('group_followee_count') - - def test_04_follower_list_bad_id(self): - for action in ('user_follower_list', 'dataset_follower_list', - 'group_follower_list'): - for object_id in ('bad id', ' ', 3, 35.7, 'xxx', ''): - error = call_action_api(self.app, action, - status=409, id=object_id, - apikey=self.testsysadmin['apikey']) - assert error['id'] - - def test_04_follower_list_missing_id(self): - for action in ('user_follower_list', 'dataset_follower_list', - 'group_follower_list'): - error = call_action_api(self.app, action, status=409, - apikey=self.testsysadmin['apikey']) - assert error['id'] == ['Missing value'] - - def test_04_user_follower_list_no_followers(self): - followers = call_action_api(self.app, 'user_follower_list', - id=self.annafan['id'], apikey=self.testsysadmin['apikey']) - assert followers == [] - - def test_04_dataset_follower_list_no_followers(self): - followers = call_action_api(self.app, - 'dataset_follower_list', id=self.annakarenina['id'], - apikey=self.testsysadmin['apikey']) - assert followers == [] - - def test_04_group_follower_list_no_followers(self): - followers = call_action_api(self.app, 'group_follower_list', - id=self.davids_group['id'], apikey=self.testsysadmin['apikey']) - assert followers == [] - def _followee_list_bad_id(self, action): - for object_id in ('bad id', ' ', 3, 35.7, 'xxx', ''): - error = call_action_api(self.app, action, - status=409, id=object_id, - apikey=self.testsysadmin['apikey']) - assert error['id'] - - def test_04_followee_list_bad_id(self): - self._followee_list_bad_id('followee_list') + for object_id in ("bad id", " ", 3, 35.7, "xxx", ""): + error = call_action_api( + self.app, + action, + status=409, + id=object_id, + apikey=self.testsysadmin["apikey"], + ) + assert error["id"] - def test_04_user_followee_list_bad_id(self): - self._followee_list_bad_id('user_followee_list') - - def test_04_dataset_followee_list_bad_id(self): - self._followee_list_bad_id('dataset_followee_list') + def _followee_list_missing_id(self, action): + error = call_action_api( + self.app, action, status=409, apikey=self.testsysadmin["apikey"] + ) + assert error["id"] == ["Missing value"] - def test_04_group_followee_list_bad_id(self): - self._followee_list_bad_id('group_followee_list') + def _followee_list_not_following_anything(self, action): + followees = call_action_api( + self.app, + action, + id=self.russianfan["id"], + apikey=self.russianfan["apikey"], + ) + assert followees == [] - def _followee_list_missing_id(self, action): - error = call_action_api(self.app, action, status=409, - apikey=self.testsysadmin['apikey']) - assert error['id'] == ['Missing value'] + def test_04_follower_count_bad_id(self, app): + for action in ( + "user_follower_count", + "dataset_follower_count", + "group_follower_count", + ): + for object_id in ("bad id", " ", 3, 35.7, "xxx", ""): + error = call_action_api(app, action, status=409, id=object_id) + assert "id" in error + + # def test_04_follower_count_missing_id(self): + for action in ( + "user_follower_count", + "dataset_follower_count", + "group_follower_count", + ): + error = call_action_api(app, action, status=409) + assert error["id"] == ["Missing value"] + + # def test_04_user_follower_count_no_followers(self): + follower_count = call_action_api( + app, "user_follower_count", id=self.annafan["id"] + ) + assert follower_count == 0 - def test_04_followee_list_missing_id(self): - self._followee_list_missing_id('followee_list') + # def test_04_dataset_follower_count_no_followers(self): + follower_count = call_action_api( + app, "dataset_follower_count", id=self.annakarenina["id"] + ) + assert follower_count == 0 - def test_04_user_followee_list_missing_id(self): - self._followee_list_missing_id('user_followee_list') + # def test_04_group_follower_count_no_followers(self): + follower_count = call_action_api( + app, "group_follower_count", id=self.davids_group["id"] + ) + assert follower_count == 0 - def test_04_dataset_followee_missing_bad_id(self): - self._followee_list_missing_id('dataset_followee_list') + # def test_04_followee_count_bad_id(self): + self._followee_count_bad_id("followee_count") + + # def test_04_user_followee_count_bad_id(self): + self._followee_count_bad_id("user_followee_count") + + # def test_04_dataset_followee_count_bad_id(self): + self._followee_count_bad_id("dataset_followee_count") + + # def test_04_group_followee_count_bad_id(self): + self._followee_count_bad_id("group_followee_count") + + # def test_04_followee_count_missing_id(self): + self._followee_count_missing_id("followee_count") + + # def test_04_user_followee_count_missing_id(self): + self._followee_count_missing_id("user_followee_count") + + # def test_04_dataset_followee_count_missing_id(self): + self._followee_count_missing_id("dataset_followee_count") + + # def test_04_group_followee_count_missing_id(self): + self._followee_count_missing_id("group_followee_count") + + # def test_04_followee_count_not_following_anything(self): + self._followee_count_not_following_anything("followee_count") + + # def test_04_user_followee_count_not_following_anything(self): + self._followee_count_not_following_anything("user_followee_count") + + # def test_04_dataset_followee_count_not_following_anything(self): + self._followee_count_not_following_anything("dataset_followee_count") + + # def test_04_group_followee_count_not_following_anything(self): + self._followee_count_not_following_anything("group_followee_count") + + # def test_04_follower_list_bad_id(self): + for action in ( + "user_follower_list", + "dataset_follower_list", + "group_follower_list", + ): + for object_id in ("bad id", " ", 3, 35.7, "xxx", ""): + error = call_action_api( + app, + action, + status=409, + id=object_id, + apikey=self.testsysadmin["apikey"], + ) + assert error["id"] + + # def test_04_follower_list_missing_id(self): + for action in ( + "user_follower_list", + "dataset_follower_list", + "group_follower_list", + ): + error = call_action_api( + app, action, status=409, apikey=self.testsysadmin["apikey"] + ) + assert error["id"] == ["Missing value"] + + # def test_04_user_follower_list_no_followers(self): + followers = call_action_api( + app, + "user_follower_list", + id=self.annafan["id"], + apikey=self.testsysadmin["apikey"], + ) + assert followers == [] - def test_04_group_followee_missing_bad_id(self): - self._followee_list_missing_id('group_followee_list') + # def test_04_dataset_follower_list_no_followers(self): + followers = call_action_api( + app, + "dataset_follower_list", + id=self.annakarenina["id"], + apikey=self.testsysadmin["apikey"], + ) + assert followers == [] - def _followee_list_not_following_anything(self, action): - followees = call_action_api(self.app, action, - id=self.russianfan['id'], apikey=self.russianfan['apikey']) - assert followees == [] + # def test_04_group_follower_list_no_followers(self): + followers = call_action_api( + app, + "group_follower_list", + id=self.davids_group["id"], + apikey=self.testsysadmin["apikey"], + ) + assert followers == [] - def test_04_followee_list_not_following_anything(self): - self._followee_list_not_following_anything('followee_list') - - def test_04_user_followee_list_not_following_anything(self): - self._followee_list_not_following_anything('user_followee_list') - - def test_04_dataset_followee_not_following_anything(self): - self._followee_list_not_following_anything('dataset_followee_list') - - def test_04_group_followee_not_following_anything(self): - self._followee_list_not_following_anything('group_followee_list') - - def test_04_am_following_bad_id(self): - for action in ('am_following_dataset', 'am_following_user', - 'am_following_group'): - for object_id in ('bad id', ' ', 3, 35.7, 'xxx'): - error = call_action_api(self.app, action, - apikey=self.annafan['apikey'], status=409, id=object_id) - assert error['id'][0].startswith('Not found: ') - - def test_04_am_following_missing_id(self): - for action in ('am_following_dataset', 'am_following_user', - 'am_following_group'): - for id in ('missing', None, ''): - if id == 'missing': - error = call_action_api(self.app, action, - apikey=self.annafan['apikey'], status=409) + # def test_04_followee_list_bad_id(self): + self._followee_list_bad_id("followee_list") + + # def test_04_user_followee_list_bad_id(self): + self._followee_list_bad_id("user_followee_list") + + # def test_04_dataset_followee_list_bad_id(self): + self._followee_list_bad_id("dataset_followee_list") + + # def test_04_group_followee_list_bad_id(self): + self._followee_list_bad_id("group_followee_list") + + # def test_04_followee_list_missing_id(self): + self._followee_list_missing_id("followee_list") + + # def test_04_user_followee_list_missing_id(self): + self._followee_list_missing_id("user_followee_list") + + # def test_04_dataset_followee_missing_bad_id(self): + self._followee_list_missing_id("dataset_followee_list") + + # def test_04_group_followee_missing_bad_id(self): + self._followee_list_missing_id("group_followee_list") + + # def test_04_followee_list_not_following_anything(self): + self._followee_list_not_following_anything("followee_list") + + # def test_04_user_followee_list_not_following_anything(self): + self._followee_list_not_following_anything("user_followee_list") + + # def test_04_dataset_followee_not_following_anything(self): + self._followee_list_not_following_anything("dataset_followee_list") + + # def test_04_group_followee_not_following_anything(self): + self._followee_list_not_following_anything("group_followee_list") + + # def test_04_am_following_bad_id(self): + for action in ( + "am_following_dataset", + "am_following_user", + "am_following_group", + ): + for object_id in ("bad id", " ", 3, 35.7, "xxx"): + error = call_action_api( + app, + action, + apikey=self.annafan["apikey"], + status=409, + id=object_id, + ) + assert error["id"][0].startswith("Not found: ") + + # def test_04_am_following_missing_id(self): + for action in ( + "am_following_dataset", + "am_following_user", + "am_following_group", + ): + for id in ("missing", None, ""): + if id == "missing": + error = call_action_api( + app, action, apikey=self.annafan["apikey"], status=409 + ) else: - error = call_action_api(self.app, action, - apikey=self.annafan['apikey'], status=409, id=id) - assert error['id'] == [u'Missing value'] - - def test_04_am_following_dataset_bad_apikey(self): - for apikey in ('bad api key', '', ' ', 'None', '3', '35.7', 'xxx'): - error = call_action_api(self.app, - 'am_following_dataset', apikey=apikey, status=403, - id=self.warandpeace['id']) - assert error['message'] == 'Access denied' - - def test_04_am_following_dataset_missing_apikey(self): - error = call_action_api(self.app, 'am_following_dataset', - status=403, id=self.warandpeace['id']) - assert error['message'] == 'Access denied' - - def test_04_am_following_user_bad_apikey(self): - for apikey in ('bad api key', '', ' ', 'None', '3', '35.7', 'xxx'): - error = call_action_api(self.app, 'am_following_user', - apikey=apikey, status=403, id=self.annafan['id']) - assert error['message'] == 'Access denied' - - def test_04_am_following_user_missing_apikey(self): - error = call_action_api(self.app, 'am_following_user', - status=403, id=self.annafan['id']) - assert error['message'] == 'Access denied' - - def test_04_am_following_group_bad_apikey(self): - for apikey in ('bad api key', '', ' ', 'None', '3', '35.7', 'xxx'): - error = call_action_api(self.app, 'am_following_group', - apikey=apikey, status=403, id=self.rogers_group['id']) - assert error['message'] == 'Access denied' - - def test_04_am_following_group_missing_apikey(self): - error = call_action_api(self.app, 'am_following_group', - status=403, id=self.rogers_group['id']) - assert error['message'] == 'Access denied' + error = call_action_api( + app, + action, + apikey=self.annafan["apikey"], + status=409, + id=id, + ) + assert error["id"] == [u"Missing value"] + + # def test_04_am_following_dataset_bad_apikey(self): + for apikey in ("bad api key", "", " ", "None", "3", "35.7", "xxx"): + error = call_action_api( + app, + "am_following_dataset", + apikey=apikey, + status=403, + id=self.warandpeace["id"], + ) + assert error["message"] == "Access denied" + + # def test_04_am_following_dataset_missing_apikey(self): + error = call_action_api( + app, "am_following_dataset", status=403, id=self.warandpeace["id"] + ) + assert error["message"] == "Access denied" + + # def test_04_am_following_user_bad_apikey(self): + for apikey in ("bad api key", "", " ", "None", "3", "35.7", "xxx"): + error = call_action_api( + app, + "am_following_user", + apikey=apikey, + status=403, + id=self.annafan["id"], + ) + assert error["message"] == "Access denied" + + # def test_04_am_following_user_missing_apikey(self): + error = call_action_api( + app, "am_following_user", status=403, id=self.annafan["id"] + ) + assert error["message"] == "Access denied" + + # def test_04_am_following_group_bad_apikey(self): + for apikey in ("bad api key", "", " ", "None", "3", "35.7", "xxx"): + error = call_action_api( + app, + "am_following_group", + apikey=apikey, + status=403, + id=self.rogers_group["id"], + ) + assert error["message"] == "Access denied" + + # def test_04_am_following_group_missing_apikey(self): + error = call_action_api( + app, "am_following_group", status=403, id=self.rogers_group["id"] + ) + assert error["message"] == "Access denied" class TestFollowerDelete(object): - '''Tests for the unfollow_* APIs.''' + """Tests for the unfollow_* APIs.""" - @classmethod - def setup_class(self): + @pytest.fixture(autouse=True) + def initial_data(self, app, clean_db): CreateTestData.create() + self.app = app self.tester = { - 'id': ckan.model.User.get('tester').id, - 'apikey': ckan.model.User.get('tester').apikey, - 'name': ckan.model.User.get('tester').name, - } + "id": ckan.model.User.get("tester").id, + "apikey": ckan.model.User.get("tester").apikey, + "name": ckan.model.User.get("tester").name, + } self.testsysadmin = { - 'id': ckan.model.User.get('testsysadmin').id, - 'apikey': ckan.model.User.get('testsysadmin').apikey, - 'name': ckan.model.User.get('testsysadmin').name, - } + "id": ckan.model.User.get("testsysadmin").id, + "apikey": ckan.model.User.get("testsysadmin").apikey, + "name": ckan.model.User.get("testsysadmin").name, + } self.annafan = { - 'id': ckan.model.User.get('annafan').id, - 'apikey': ckan.model.User.get('annafan').apikey, - 'name': ckan.model.User.get('annafan').name, - } + "id": ckan.model.User.get("annafan").id, + "apikey": ckan.model.User.get("annafan").apikey, + "name": ckan.model.User.get("annafan").name, + } self.russianfan = { - 'id': ckan.model.User.get('russianfan').id, - 'apikey': ckan.model.User.get('russianfan').apikey, - 'name': ckan.model.User.get('russianfan').name, - } + "id": ckan.model.User.get("russianfan").id, + "apikey": ckan.model.User.get("russianfan").apikey, + "name": ckan.model.User.get("russianfan").name, + } self.joeadmin = { - 'id': ckan.model.User.get('joeadmin').id, - 'apikey': ckan.model.User.get('joeadmin').apikey, - 'name': ckan.model.User.get('joeadmin').name, - } + "id": ckan.model.User.get("joeadmin").id, + "apikey": ckan.model.User.get("joeadmin").apikey, + "name": ckan.model.User.get("joeadmin").name, + } self.warandpeace = { - 'id': ckan.model.Package.get('warandpeace').id, - 'name': ckan.model.Package.get('warandpeace').name, - } + "id": ckan.model.Package.get("warandpeace").id, + "name": ckan.model.Package.get("warandpeace").name, + } self.annakarenina = { - 'id': ckan.model.Package.get('annakarenina').id, - 'name': ckan.model.Package.get('annakarenina').name, - } + "id": ckan.model.Package.get("annakarenina").id, + "name": ckan.model.Package.get("annakarenina").name, + } self.rogers_group = { - 'id': ckan.model.Group.get('roger').id, - 'name': ckan.model.Group.get('roger').name, - } + "id": ckan.model.Group.get("roger").id, + "name": ckan.model.Group.get("roger").name, + } self.davids_group = { - 'id': ckan.model.Group.get('david').id, - 'name': ckan.model.Group.get('david').name, - } - self.app = helpers._get_test_app() - follow_user(self.app, self.testsysadmin['id'], - self.testsysadmin['apikey'], self.joeadmin['id'], - self.joeadmin['id'], self.testsysadmin['apikey']) - follow_user(self.app, self.tester['id'], self.tester['apikey'], - self.joeadmin['id'], self.joeadmin['id'], - self.testsysadmin['apikey']) - follow_user(self.app, self.russianfan['id'], self.russianfan['apikey'], - self.joeadmin['id'], self.joeadmin['id'], - self.testsysadmin['apikey']) - follow_user(self.app, self.annafan['id'], self.annafan['apikey'], - self.joeadmin['id'], self.joeadmin['id'], - self.testsysadmin['apikey']) - follow_user(self.app, self.annafan['id'], self.annafan['apikey'], - self.tester['id'], self.tester['id'], - self.testsysadmin['apikey']) - follow_dataset(self.app, self.testsysadmin['id'], - self.testsysadmin['apikey'], self.warandpeace['id'], - self.warandpeace['id'], self.testsysadmin['apikey']) - follow_dataset(self.app, self.tester['id'], self.tester['apikey'], - self.warandpeace['id'], self.warandpeace['id'], - self.testsysadmin['apikey']) - follow_dataset(self.app, self.russianfan['id'], self.russianfan['apikey'], - self.warandpeace['id'], self.warandpeace['id'], - self.testsysadmin['apikey']) - follow_dataset(self.app, self.annafan['id'], self.annafan['apikey'], - self.warandpeace['id'], self.warandpeace['id'], - self.testsysadmin['apikey']) - follow_group(self.app, self.annafan['id'], self.annafan['apikey'], - self.davids_group['id'], self.davids_group['id'], - self.testsysadmin['apikey']) - - @classmethod - def teardown_class(self): - ckan.model.repo.rebuild_db() - - def test_01_unfollow_user_not_exists(self): - '''Test the error response when a user tries to unfollow a user that + "id": ckan.model.Group.get("david").id, + "name": ckan.model.Group.get("david").name, + } + + follow_user( + app, + self.testsysadmin["id"], + self.testsysadmin["apikey"], + self.joeadmin["id"], + self.joeadmin["id"], + self.testsysadmin["apikey"], + ) + follow_user( + app, + self.tester["id"], + self.tester["apikey"], + self.joeadmin["id"], + self.joeadmin["id"], + self.testsysadmin["apikey"], + ) + follow_user( + app, + self.russianfan["id"], + self.russianfan["apikey"], + self.joeadmin["id"], + self.joeadmin["id"], + self.testsysadmin["apikey"], + ) + follow_user( + app, + self.annafan["id"], + self.annafan["apikey"], + self.joeadmin["id"], + self.joeadmin["id"], + self.testsysadmin["apikey"], + ) + follow_user( + app, + self.annafan["id"], + self.annafan["apikey"], + self.tester["id"], + self.tester["id"], + self.testsysadmin["apikey"], + ) + follow_dataset( + app, + self.testsysadmin["id"], + self.testsysadmin["apikey"], + self.warandpeace["id"], + self.warandpeace["id"], + self.testsysadmin["apikey"], + ) + follow_dataset( + app, + self.tester["id"], + self.tester["apikey"], + self.warandpeace["id"], + self.warandpeace["id"], + self.testsysadmin["apikey"], + ) + follow_dataset( + app, + self.russianfan["id"], + self.russianfan["apikey"], + self.warandpeace["id"], + self.warandpeace["id"], + self.testsysadmin["apikey"], + ) + follow_dataset( + app, + self.annafan["id"], + self.annafan["apikey"], + self.warandpeace["id"], + self.warandpeace["id"], + self.testsysadmin["apikey"], + ) + follow_group( + app, + self.annafan["id"], + self.annafan["apikey"], + self.davids_group["id"], + self.davids_group["id"], + self.testsysadmin["apikey"], + ) + + def test_01_unfollow_user_not_exists(self, app): + """Test the error response when a user tries to unfollow a user that she is not following. - ''' - error = call_action_api(self.app, 'unfollow_user', - apikey=self.annafan['apikey'], status=404, - id=self.russianfan['id']) - assert error['message'].startswith('Not found: You are not following ') - - def test_01_unfollow_dataset_not_exists(self): - '''Test the error response when a user tries to unfollow a dataset that + """ + error = call_action_api( + app, + "unfollow_user", + apikey=self.annafan["apikey"], + status=404, + id=self.russianfan["id"], + ) + assert error["message"].startswith("Not found: You are not following ") + + def test_01_unfollow_dataset_not_exists(self, app): + """Test the error response when a user tries to unfollow a dataset that she is not following. - ''' - error = call_action_api(self.app, 'unfollow_dataset', - apikey=self.annafan['apikey'], status=404, - id=self.annakarenina['id']) - assert error['message'].startswith('Not found: You are not following') - - def test_01_unfollow_group_not_exists(self): - '''Test the error response when a user tries to unfollow a group that + """ + error = call_action_api( + app, + "unfollow_dataset", + apikey=self.annafan["apikey"], + status=404, + id=self.annakarenina["id"], + ) + assert error["message"].startswith("Not found: You are not following") + + def test_01_unfollow_group_not_exists(self, app): + """Test the error response when a user tries to unfollow a group that she is not following. - ''' - error = call_action_api(self.app, 'unfollow_group', - apikey=self.annafan['apikey'], status=404, - id=self.rogers_group['id']) - assert error['message'].startswith('Not found: You are not following') - - def test_01_unfollow_bad_apikey(self): - '''Test the error response when a user tries to unfollow something + """ + error = call_action_api( + app, + "unfollow_group", + apikey=self.annafan["apikey"], + status=404, + id=self.rogers_group["id"], + ) + assert error["message"].startswith("Not found: You are not following") + + def test_01_unfollow_bad_apikey(self, app): + """Test the error response when a user tries to unfollow something but provides a bad API key. - ''' - for action in ('unfollow_user', 'unfollow_dataset', 'unfollow_group'): - for apikey in ('bad api key', '', ' ', 'None', '3', '35.7', - 'xxx'): - error = call_action_api(self.app, action, - apikey=apikey, status=403, id=self.joeadmin['id']) - assert error['__type'] == 'Authorization Error' - - def test_01_unfollow_missing_apikey(self): - '''Test error response when calling unfollow_* without api key.''' - for action in ('unfollow_user', 'unfollow_dataset', 'unfollow_group'): - error = call_action_api(self.app, action, status=403, - id=self.joeadmin['id']) - assert error['__type'] == 'Authorization Error' - - def test_01_unfollow_bad_object_id(self): - '''Test error response when calling unfollow_* with bad object id.''' - for action in ('unfollow_user', 'unfollow_dataset', 'unfollow_group'): - for object_id in ('bad id', ' ', 3, 35.7, 'xxx'): - error = call_action_api(self.app, action, - apikey=self.annafan['apikey'], status=409, - id=object_id) - assert error['id'][0].startswith('Not found') - - def test_01_unfollow_missing_object_id(self): - for action in ('unfollow_user', 'unfollow_dataset', 'unfollow_group'): - for id in ('missing', None, ''): - if id == 'missing': - error = call_action_api(self.app, action, - apikey=self.annafan['apikey'], status=409) + """ + for action in ("unfollow_user", "unfollow_dataset", "unfollow_group"): + for apikey in ( + "bad api key", + "", + " ", + "None", + "3", + "35.7", + "xxx", + ): + error = call_action_api( + app, + action, + apikey=apikey, + status=403, + id=self.joeadmin["id"], + ) + assert error["__type"] == "Authorization Error" + + def test_01_unfollow_missing_apikey(self, app): + """Test error response when calling unfollow_* without api key.""" + for action in ("unfollow_user", "unfollow_dataset", "unfollow_group"): + error = call_action_api( + app, action, status=403, id=self.joeadmin["id"] + ) + assert error["__type"] == "Authorization Error" + + def test_01_unfollow_bad_object_id(self, app): + """Test error response when calling unfollow_* with bad object id.""" + for action in ("unfollow_user", "unfollow_dataset", "unfollow_group"): + for object_id in ("bad id", " ", 3, 35.7, "xxx"): + error = call_action_api( + app, + action, + apikey=self.annafan["apikey"], + status=409, + id=object_id, + ) + assert error["id"][0].startswith("Not found") + + def test_01_unfollow_missing_object_id(self, app): + for action in ("unfollow_user", "unfollow_dataset", "unfollow_group"): + for id in ("missing", None, ""): + if id == "missing": + error = call_action_api( + app, action, apikey=self.annafan["apikey"], status=409 + ) else: - error = call_action_api(self.app, action, - apikey=self.annafan['apikey'], status=409, id=id) - assert error['id'] == [u'Missing value'] + error = call_action_api( + app, + action, + apikey=self.annafan["apikey"], + status=409, + id=id, + ) + assert error["id"] == [u"Missing value"] def _unfollow_user(self, follower_id, apikey, object_id, object_arg): - '''Test a user unfollowing a user via the API. + """Test a user unfollowing a user via the API. :param follower_id: id of the follower. :param apikey: API key of the follower. @@ -916,61 +1304,100 @@ def _unfollow_user(self, follower_id, apikey, object_id, object_arg): :param object_arg: the argument to pass to unfollow_user as the id of the object to unfollow, could be the object's id or name. - ''' + """ # Record the user's number of followers before. - count_before = call_action_api(self.app, - 'user_follower_count', id=object_id) - followee_count_before = call_action_api(self.app, - 'followee_count', id=follower_id) - user_followee_count_before = call_action_api(self.app, - 'user_followee_count', id=follower_id) + count_before = call_action_api( + self.app, "user_follower_count", id=object_id + ) + followee_count_before = call_action_api( + self.app, "followee_count", id=follower_id + ) + user_followee_count_before = call_action_api( + self.app, "user_followee_count", id=follower_id + ) # Check that the user is following the object. - am_following = call_action_api(self.app, - 'am_following_user', apikey=apikey, id=object_id) + am_following = call_action_api( + self.app, "am_following_user", apikey=apikey, id=object_id + ) assert am_following is True # Make the user unfollow the object. - call_action_api(self.app, 'unfollow_user', apikey=apikey, - id=object_arg) + call_action_api( + self.app, "unfollow_user", apikey=apikey, id=object_arg + ) # Check that am_following_user now returns False. - am_following = call_action_api(self.app, - 'am_following_user', apikey=apikey, id=object_id) + am_following = call_action_api( + self.app, "am_following_user", apikey=apikey, id=object_id + ) assert am_following is False # Check that the user doesn't appear in the object's list of followers. - followers = call_action_api(self.app, 'user_follower_list', - id=object_id, apikey=self.testsysadmin['apikey']) - assert len([follower for follower in followers if follower['id'] == - follower_id]) == 0 + followers = call_action_api( + self.app, + "user_follower_list", + id=object_id, + apikey=self.testsysadmin["apikey"], + ) + assert ( + len( + [ + follower + for follower in followers + if follower["id"] == follower_id + ] + ) + == 0 + ) # Check that the object's follower count has decreased by 1. - count_after = call_action_api(self.app, - 'user_follower_count', id=object_id) + count_after = call_action_api( + self.app, "user_follower_count", id=object_id + ) assert count_after == count_before - 1 # Check that the user doesn't appear in the subject's list of # followees. - followees = call_action_api(self.app, 'followee_list', - id=follower_id, apikey=apikey) - assert len([followee for followee in followees - if followee['dict']['id'] == object_id]) == 0 - followees = call_action_api(self.app, 'user_followee_list', - id=follower_id, apikey=apikey) - assert len([followee for followee in followees - if followee['id'] == object_id]) == 0 + followees = call_action_api( + self.app, "followee_list", id=follower_id, apikey=apikey + ) + assert ( + len( + [ + followee + for followee in followees + if followee["dict"]["id"] == object_id + ] + ) + == 0 + ) + followees = call_action_api( + self.app, "user_followee_list", id=follower_id, apikey=apikey + ) + assert ( + len( + [ + followee + for followee in followees + if followee["id"] == object_id + ] + ) + == 0 + ) # Check the the subject's followee cont has decreased by 1. - count_after = call_action_api(self.app, 'followee_count', - id=follower_id) + count_after = call_action_api( + self.app, "followee_count", id=follower_id + ) assert count_after == followee_count_before - 1 - count_after = call_action_api(self.app, - 'user_followee_count', id=follower_id) + count_after = call_action_api( + self.app, "user_followee_count", id=follower_id + ) assert count_after == user_followee_count_before - 1 def _unfollow_dataset(self, user_id, apikey, dataset_id, dataset_arg): - '''Test a user unfollowing a dataset via the API. + """Test a user unfollowing a dataset via the API. :param user_id: id of the follower. :param apikey: API key of the follower. @@ -978,63 +1405,99 @@ def _unfollow_dataset(self, user_id, apikey, dataset_id, dataset_arg): :param dataset_arg: the argument to pass to unfollow_dataset as the id of the object to unfollow, could be the object's id or name. - ''' + """ # Record the dataset's number of followers before. - count_before = call_action_api(self.app, - 'dataset_follower_count', id=dataset_id) - followee_count_before = call_action_api(self.app, - 'followee_count', id=user_id) - dataset_followee_count_before = call_action_api(self.app, - 'dataset_followee_count', id=user_id) + count_before = call_action_api( + self.app, "dataset_follower_count", id=dataset_id + ) + followee_count_before = call_action_api( + self.app, "followee_count", id=user_id + ) + dataset_followee_count_before = call_action_api( + self.app, "dataset_followee_count", id=user_id + ) # Check that the user is following the dataset. - am_following = call_action_api(self.app, - 'am_following_dataset', apikey=apikey, id=dataset_id) + am_following = call_action_api( + self.app, "am_following_dataset", apikey=apikey, id=dataset_id + ) assert am_following is True # Make the user unfollow the dataset. - call_action_api(self.app, 'unfollow_dataset', apikey=apikey, - id=dataset_arg) + call_action_api( + self.app, "unfollow_dataset", apikey=apikey, id=dataset_arg + ) # Check that am_following_dataset now returns False. - am_following = call_action_api(self.app, - 'am_following_dataset', apikey=apikey, id=dataset_id) + am_following = call_action_api( + self.app, "am_following_dataset", apikey=apikey, id=dataset_id + ) assert am_following is False # Check that the user doesn't appear in the dataset's list of # followers. - followers = call_action_api(self.app, - 'dataset_follower_list', id=dataset_id, - apikey=self.testsysadmin['apikey']) - assert len([follower for follower in followers if follower['id'] == - user_id]) == 0 + followers = call_action_api( + self.app, + "dataset_follower_list", + id=dataset_id, + apikey=self.testsysadmin["apikey"], + ) + assert ( + len( + [ + follower + for follower in followers + if follower["id"] == user_id + ] + ) + == 0 + ) # Check that the dataset's follower count has decreased by 1. - count_after = call_action_api(self.app, - 'dataset_follower_count', id=dataset_id) + count_after = call_action_api( + self.app, "dataset_follower_count", id=dataset_id + ) assert count_after == count_before - 1 # Check that the dataset doesn't appear in the user's list of # followees. - followees = call_action_api(self.app, 'followee_list', - id=user_id, apikey=apikey) - assert len([followee for followee in followees - if followee['dict']['id'] == dataset_id]) == 0 - followees = call_action_api(self.app, - 'dataset_followee_list', id=user_id, apikey=apikey) - assert len([followee for followee in followees - if followee['id'] == dataset_id]) == 0 + followees = call_action_api( + self.app, "followee_list", id=user_id, apikey=apikey + ) + assert ( + len( + [ + followee + for followee in followees + if followee["dict"]["id"] == dataset_id + ] + ) + == 0 + ) + followees = call_action_api( + self.app, "dataset_followee_list", id=user_id, apikey=apikey + ) + assert ( + len( + [ + followee + for followee in followees + if followee["id"] == dataset_id + ] + ) + == 0 + ) # Check the the user's followee count has decreased by 1. - count_after = call_action_api(self.app, 'followee_count', - id=user_id) + count_after = call_action_api(self.app, "followee_count", id=user_id) assert count_after == followee_count_before - 1 - count_after = call_action_api(self.app, - 'dataset_followee_count', id=user_id) + count_after = call_action_api( + self.app, "dataset_followee_count", id=user_id + ) assert count_after == dataset_followee_count_before - 1 def _unfollow_group(self, user_id, apikey, group_id, group_arg): - '''Test a user unfollowing a group via the API. + """Test a user unfollowing a group via the API. :param user_id: id of the user :param apikey: API key of the user @@ -1042,275 +1505,448 @@ def _unfollow_group(self, user_id, apikey, group_id, group_arg): :param group_arg: the argument to pass to unfollow_group as the id of the group, could be the group's id or name. - ''' + """ # Record the group's number of followers before. - count_before = call_action_api(self.app, - 'group_follower_count', id=group_id) - followee_count_before = call_action_api(self.app, - 'followee_count', id=user_id) - group_followee_count_before = call_action_api(self.app, - 'group_followee_count', id=user_id) + count_before = call_action_api( + self.app, "group_follower_count", id=group_id + ) + followee_count_before = call_action_api( + self.app, "followee_count", id=user_id + ) + group_followee_count_before = call_action_api( + self.app, "group_followee_count", id=user_id + ) # Check that the user is following the group. - am_following = call_action_api(self.app, - 'am_following_group', apikey=apikey, id=group_id) + am_following = call_action_api( + self.app, "am_following_group", apikey=apikey, id=group_id + ) assert am_following is True # Make the user unfollow the group. - call_action_api(self.app, 'unfollow_group', apikey=apikey, - id=group_arg) + call_action_api( + self.app, "unfollow_group", apikey=apikey, id=group_arg + ) # Check that am_following_group now returns False. - am_following = call_action_api(self.app, - 'am_following_group', apikey=apikey, id=group_id) + am_following = call_action_api( + self.app, "am_following_group", apikey=apikey, id=group_id + ) assert am_following is False # Check that the user doesn't appear in the group's list of # followers. - followers = call_action_api(self.app, 'group_follower_list', - id=group_id, apikey=self.testsysadmin['apikey']) - assert len([follower for follower in followers if follower['id'] == - user_id]) == 0 + followers = call_action_api( + self.app, + "group_follower_list", + id=group_id, + apikey=self.testsysadmin["apikey"], + ) + assert ( + len( + [ + follower + for follower in followers + if follower["id"] == user_id + ] + ) + == 0 + ) # Check that the group's follower count has decreased by 1. - count_after = call_action_api(self.app, - 'group_follower_count', id=group_id) + count_after = call_action_api( + self.app, "group_follower_count", id=group_id + ) assert count_after == count_before - 1 # Check that the group doesn't appear in the user's list of # followees. - followees = call_action_api(self.app, 'followee_list', - id=user_id, apikey=apikey) - assert len([followee for followee in followees - if followee['dict']['id'] == group_id]) == 0 - followees = call_action_api(self.app, - 'group_followee_list', id=user_id, - apikey=self.testsysadmin['apikey']) - assert len([followee for followee in followees - if followee['id'] == group_id]) == 0 + followees = call_action_api( + self.app, "followee_list", id=user_id, apikey=apikey + ) + assert ( + len( + [ + followee + for followee in followees + if followee["dict"]["id"] == group_id + ] + ) + == 0 + ) + followees = call_action_api( + self.app, + "group_followee_list", + id=user_id, + apikey=self.testsysadmin["apikey"], + ) + assert ( + len( + [ + followee + for followee in followees + if followee["id"] == group_id + ] + ) + == 0 + ) # Check the the user's followee count has decreased by 1. - count_after = call_action_api(self.app, 'followee_count', - id=user_id) + count_after = call_action_api(self.app, "followee_count", id=user_id) assert count_after == followee_count_before - 1 - count_after = call_action_api(self.app, - 'group_followee_count', id=user_id) + count_after = call_action_api( + self.app, "group_followee_count", id=user_id + ) assert count_after == group_followee_count_before - 1 def test_02_follower_delete_by_id(self): - self._unfollow_user(self.annafan['id'], self.annafan['apikey'], - self.joeadmin['id'], self.joeadmin['id']) - self._unfollow_dataset(self.annafan['id'], self.annafan['apikey'], - self.warandpeace['id'], self.warandpeace['id']) - self._unfollow_group(self.annafan['id'], self.annafan['apikey'], - self.davids_group['id'], self.davids_group['id']) + self._unfollow_user( + self.annafan["id"], + self.annafan["apikey"], + self.joeadmin["id"], + self.joeadmin["id"], + ) + self._unfollow_dataset( + self.annafan["id"], + self.annafan["apikey"], + self.warandpeace["id"], + self.warandpeace["id"], + ) + self._unfollow_group( + self.annafan["id"], + self.annafan["apikey"], + self.davids_group["id"], + self.davids_group["id"], + ) + class TestFollowerCascade(object): - '''Tests for on delete cascade of follower table rows.''' + """Tests for on delete cascade of follower table rows.""" - @classmethod - def setup_class(self): + @pytest.fixture(autouse=True) + def initial_data(self, clean_db, app): CreateTestData.create() + self.app = app self.tester = { - 'id': ckan.model.User.get('tester').id, - 'apikey': ckan.model.User.get('tester').apikey, - 'name': ckan.model.User.get('tester').name, - } + "id": ckan.model.User.get("tester").id, + "apikey": ckan.model.User.get("tester").apikey, + "name": ckan.model.User.get("tester").name, + } self.testsysadmin = { - 'id': ckan.model.User.get('testsysadmin').id, - 'apikey': ckan.model.User.get('testsysadmin').apikey, - 'name': ckan.model.User.get('testsysadmin').name, - } + "id": ckan.model.User.get("testsysadmin").id, + "apikey": ckan.model.User.get("testsysadmin").apikey, + "name": ckan.model.User.get("testsysadmin").name, + } self.annafan = { - 'id': ckan.model.User.get('annafan').id, - 'apikey': ckan.model.User.get('annafan').apikey, - 'name': ckan.model.User.get('annafan').name, - } + "id": ckan.model.User.get("annafan").id, + "apikey": ckan.model.User.get("annafan").apikey, + "name": ckan.model.User.get("annafan").name, + } self.russianfan = { - 'id': ckan.model.User.get('russianfan').id, - 'apikey': ckan.model.User.get('russianfan').apikey, - 'name': ckan.model.User.get('russianfan').name, - } + "id": ckan.model.User.get("russianfan").id, + "apikey": ckan.model.User.get("russianfan").apikey, + "name": ckan.model.User.get("russianfan").name, + } self.joeadmin = { - 'id': ckan.model.User.get('joeadmin').id, - 'apikey': ckan.model.User.get('joeadmin').apikey, - 'name': ckan.model.User.get('joeadmin').name, - } + "id": ckan.model.User.get("joeadmin").id, + "apikey": ckan.model.User.get("joeadmin").apikey, + "name": ckan.model.User.get("joeadmin").name, + } self.warandpeace = { - 'id': ckan.model.Package.get('warandpeace').id, - 'name': ckan.model.Package.get('warandpeace').name, - } + "id": ckan.model.Package.get("warandpeace").id, + "name": ckan.model.Package.get("warandpeace").name, + } self.annakarenina = { - 'id': ckan.model.Package.get('annakarenina').id, - 'name': ckan.model.Package.get('annakarenina').name, - } + "id": ckan.model.Package.get("annakarenina").id, + "name": ckan.model.Package.get("annakarenina").name, + } self.rogers_group = { - 'id': ckan.model.Group.get('roger').id, - 'name': ckan.model.Group.get('roger').name, - } + "id": ckan.model.Group.get("roger").id, + "name": ckan.model.Group.get("roger").name, + } self.davids_group = { - 'id': ckan.model.Group.get('david').id, - 'name': ckan.model.Group.get('david').name, - } - self.app = helpers._get_test_app() - - follow_user(self.app, self.joeadmin['id'], self.joeadmin['apikey'], - self.testsysadmin['id'], self.testsysadmin['id'], - self.testsysadmin['apikey']) - - follow_user(self.app, self.annafan['id'], self.annafan['apikey'], - self.testsysadmin['id'], self.testsysadmin['id'], - self.testsysadmin['apikey']) - follow_user(self.app, self.russianfan['id'], self.russianfan['apikey'], - self.testsysadmin['id'], self.testsysadmin['id'], - self.testsysadmin['apikey']) - - follow_dataset(self.app, self.joeadmin['id'], self.joeadmin['apikey'], - self.annakarenina['id'], self.annakarenina['id'], - self.testsysadmin['apikey']) - - follow_dataset(self.app, self.annafan['id'], self.annafan['apikey'], - self.annakarenina['id'], self.annakarenina['id'], - self.testsysadmin['apikey']) - follow_dataset(self.app, self.russianfan['id'], self.russianfan['apikey'], - self.annakarenina['id'], self.annakarenina['id'], - self.testsysadmin['apikey']) - - follow_user(self.app, self.tester['id'], self.tester['apikey'], - self.joeadmin['id'], self.joeadmin['id'], - self.testsysadmin['apikey']) - - follow_dataset(self.app, self.testsysadmin['id'], - self.testsysadmin['apikey'], self.warandpeace['id'], - self.warandpeace['id'], self.testsysadmin['apikey']) - - follow_group(self.app, self.testsysadmin['id'], - self.testsysadmin['apikey'], self.davids_group['id'], - self.davids_group['id'], self.testsysadmin['apikey']) + "id": ckan.model.Group.get("david").id, + "name": ckan.model.Group.get("david").name, + } + + follow_user( + app, + self.joeadmin["id"], + self.joeadmin["apikey"], + self.testsysadmin["id"], + self.testsysadmin["id"], + self.testsysadmin["apikey"], + ) + + follow_user( + app, + self.annafan["id"], + self.annafan["apikey"], + self.testsysadmin["id"], + self.testsysadmin["id"], + self.testsysadmin["apikey"], + ) + follow_user( + app, + self.russianfan["id"], + self.russianfan["apikey"], + self.testsysadmin["id"], + self.testsysadmin["id"], + self.testsysadmin["apikey"], + ) + + follow_dataset( + app, + self.joeadmin["id"], + self.joeadmin["apikey"], + self.annakarenina["id"], + self.annakarenina["id"], + self.testsysadmin["apikey"], + ) + + follow_dataset( + app, + self.annafan["id"], + self.annafan["apikey"], + self.annakarenina["id"], + self.annakarenina["id"], + self.testsysadmin["apikey"], + ) + follow_dataset( + app, + self.russianfan["id"], + self.russianfan["apikey"], + self.annakarenina["id"], + self.annakarenina["id"], + self.testsysadmin["apikey"], + ) + + follow_user( + app, + self.tester["id"], + self.tester["apikey"], + self.joeadmin["id"], + self.joeadmin["id"], + self.testsysadmin["apikey"], + ) + + follow_dataset( + app, + self.testsysadmin["id"], + self.testsysadmin["apikey"], + self.warandpeace["id"], + self.warandpeace["id"], + self.testsysadmin["apikey"], + ) + + follow_group( + app, + self.testsysadmin["id"], + self.testsysadmin["apikey"], + self.davids_group["id"], + self.davids_group["id"], + self.testsysadmin["apikey"], + ) session = ckan.model.Session() - session.delete(ckan.model.User.get('joeadmin')) + session.delete(ckan.model.User.get("joeadmin")) session.commit() - session.delete(ckan.model.Package.get('warandpeace')) + session.delete(ckan.model.Package.get("warandpeace")) session.commit() - session.delete(ckan.model.Group.get('david')) + session.delete(ckan.model.Group.get("david")) session.commit() - @classmethod - def teardown_class(self): - ckan.model.repo.rebuild_db() - - def test_01_on_delete_cascade_api(self): - ''' + def test_01_on_delete_cascade_api(self, app): + """ Test that UserFollowingUser and UserFollowingDataset rows cascade. - ''' + """ # It should no longer be possible to get joeadmin's follower list. - error = call_action_api(self.app, 'user_follower_list', - status=409, id='joeadmin', apikey=self.testsysadmin['apikey']) - assert 'id' in error + error = call_action_api( + app, + "user_follower_list", + status=409, + id="joeadmin", + apikey=self.testsysadmin["apikey"], + ) + assert "id" in error # It should no longer be possible to get joeadmin's followee lists. - for action in ('followee_list', 'user_followee_list', - 'dataset_followee_list', 'group_followee_list'): - error = call_action_api(self.app, action, status=409, - id='joeadmin', apikey=self.testsysadmin['apikey']) - assert 'id' in error + for action in ( + "followee_list", + "user_followee_list", + "dataset_followee_list", + "group_followee_list", + ): + error = call_action_api( + app, + action, + status=409, + id="joeadmin", + apikey=self.testsysadmin["apikey"], + ) + assert "id" in error # It should no longer be possible to get warandpeace's follower list. - error = call_action_api(self.app, 'dataset_follower_list', - status=409, id='warandpeace', apikey=self.testsysadmin['apikey']) - assert 'id' in error + error = call_action_api( + app, + "dataset_follower_list", + status=409, + id="warandpeace", + apikey=self.testsysadmin["apikey"], + ) + assert "id" in error # It should no longer be possible to get david's follower list. - error = call_action_api(self.app, 'group_follower_list', - status=409, id='david', apikey=self.testsysadmin['apikey']) - assert 'id' in error + error = call_action_api( + app, + "group_follower_list", + status=409, + id="david", + apikey=self.testsysadmin["apikey"], + ) + assert "id" in error # It should no longer be possible to get joeadmin's follower count. - error = call_action_api(self.app, 'user_follower_count', - status=409, id='joeadmin') - assert 'id' in error + error = call_action_api( + app, "user_follower_count", status=409, id="joeadmin" + ) + assert "id" in error # It should no longer be possible to get joeadmin's followee counts. - for action in ('followee_count', 'user_followee_count', - 'dataset_followee_count', 'group_followee_count'): - error = call_action_api(self.app, action, status=409, - id='joeadmin') - assert 'id' in error + for action in ( + "followee_count", + "user_followee_count", + "dataset_followee_count", + "group_followee_count", + ): + error = call_action_api(app, action, status=409, id="joeadmin") + assert "id" in error # It should no longer be possible to get warandpeace's follower count. - error = call_action_api(self.app, 'dataset_follower_count', - status=409, id='warandpeace') - assert 'id' in error + error = call_action_api( + app, "dataset_follower_count", status=409, id="warandpeace" + ) + assert "id" in error # It should no longer be possible to get david's follower count. - error = call_action_api(self.app, 'group_follower_count', - status=409, id='david') - assert 'id' in error + error = call_action_api( + app, "group_follower_count", status=409, id="david" + ) + assert "id" in error # It should no longer be possible to get am_following for joeadmin. - error = call_action_api(self.app, 'am_following_user', - apikey=self.testsysadmin['apikey'], status=409, id='joeadmin') - assert 'id' in error + error = call_action_api( + app, + "am_following_user", + apikey=self.testsysadmin["apikey"], + status=409, + id="joeadmin", + ) + assert "id" in error # It should no longer be possible to get am_following for warandpeace. - error = call_action_api(self.app, 'am_following_dataset', - apikey=self.testsysadmin['apikey'], status=409, - id='warandpeace') - assert 'id' in error + error = call_action_api( + app, + "am_following_dataset", + apikey=self.testsysadmin["apikey"], + status=409, + id="warandpeace", + ) + assert "id" in error # It should no longer be possible to get am_following for david. - error = call_action_api(self.app, 'am_following_group', - apikey=self.testsysadmin['apikey'], status=409, id='david') - assert 'id' in error + error = call_action_api( + app, + "am_following_group", + apikey=self.testsysadmin["apikey"], + status=409, + id="david", + ) + assert "id" in error # It should no longer be possible to unfollow joeadmin. - error = call_action_api(self.app, 'unfollow_user', - apikey=self.tester['apikey'], status=409, id='joeadmin') - assert error['id'] == ['Not found: User'] + error = call_action_api( + app, + "unfollow_user", + apikey=self.tester["apikey"], + status=409, + id="joeadmin", + ) + assert error["id"] == ["Not found: User"] # It should no longer be possible to unfollow warandpeace. - error = call_action_api(self.app, 'unfollow_dataset', - apikey=self.testsysadmin['apikey'], status=409, - id='warandpeace') - assert error['id'] == ['Not found: Dataset'] + error = call_action_api( + app, + "unfollow_dataset", + apikey=self.testsysadmin["apikey"], + status=409, + id="warandpeace", + ) + assert error["id"] == ["Not found: Dataset"] # It should no longer be possible to unfollow david. - error = call_action_api(self.app, 'unfollow_group', - apikey=self.testsysadmin['apikey'], status=409, id='david') - assert error['id'] == ['Not found: Group'] + error = call_action_api( + app, + "unfollow_group", + apikey=self.testsysadmin["apikey"], + status=409, + id="david", + ) + assert error["id"] == ["Not found: Group"] # It should no longer be possible to follow joeadmin. - error = call_action_api(self.app, 'follow_user', - apikey=self.annafan['apikey'], status=409, id='joeadmin') - assert 'id' in error + error = call_action_api( + app, + "follow_user", + apikey=self.annafan["apikey"], + status=409, + id="joeadmin", + ) + assert "id" in error # It should no longer be possible to follow warandpeace. - error = call_action_api(self.app, 'follow_dataset', - apikey=self.annafan['apikey'], status=409, id='warandpeace') - assert 'id' in error + error = call_action_api( + app, + "follow_dataset", + apikey=self.annafan["apikey"], + status=409, + id="warandpeace", + ) + assert "id" in error # It should no longer be possible to follow david. - error = call_action_api(self.app, 'follow_group', - apikey=self.annafan['apikey'], status=409, id='david') - assert 'id' in error + error = call_action_api( + app, + "follow_group", + apikey=self.annafan["apikey"], + status=409, + id="david", + ) + assert "id" in error # Users who joeadmin was following should no longer have him in their # follower list. - followers = call_action_api(self.app, 'user_follower_list', - id=self.testsysadmin['id'], apikey=self.testsysadmin['apikey']) - assert 'joeadmin' not in [follower['name'] for follower in followers] + followers = call_action_api( + app, + "user_follower_list", + id=self.testsysadmin["id"], + apikey=self.testsysadmin["apikey"], + ) + assert "joeadmin" not in [follower["name"] for follower in followers] # Datasets who joeadmin was following should no longer have him in # their follower list. - followers = call_action_api(self.app, - 'dataset_follower_list', id=self.annakarenina['id'], - apikey=self.testsysadmin['apikey']) - assert 'joeadmin' not in [follower['name'] for follower in followers] + followers = call_action_api( + app, + "dataset_follower_list", + id=self.annakarenina["id"], + apikey=self.testsysadmin["apikey"], + ) + assert "joeadmin" not in [follower["name"] for follower in followers] def test_02_on_delete_cascade_db(self): if not are_foreign_keys_supported(): @@ -1318,31 +1954,44 @@ def test_02_on_delete_cascade_db(self): # After the previous test above there should be no rows with joeadmin's # id in the UserFollowingUser or UserFollowingDataset tables. - from ckan.model import UserFollowingUser, UserFollowingDataset, UserFollowingGroup + from ckan.model import ( + UserFollowingUser, + UserFollowingDataset, + UserFollowingGroup, + ) + session = ckan.model.Session() query = session.query(UserFollowingUser) - query = query.filter(UserFollowingUser.follower_id==self.joeadmin['id']) + query = query.filter( + UserFollowingUser.follower_id == self.joeadmin["id"] + ) assert query.count() == 0 query = session.query(UserFollowingUser) - query = query.filter(UserFollowingUser.object_id==self.joeadmin['id']) + query = query.filter( + UserFollowingUser.object_id == self.joeadmin["id"] + ) assert query.count() == 0 query = session.query(UserFollowingDataset) - query = query.filter(UserFollowingUser.follower_id==self.joeadmin['id']) + query = query.filter( + UserFollowingUser.follower_id == self.joeadmin["id"] + ) assert query.count() == 0 # There should be no rows with warandpeace's id in the # UserFollowingDataset table. query = session.query(UserFollowingDataset) query = query.filter( - UserFollowingDataset.object_id==self.warandpeace['id']) + UserFollowingDataset.object_id == self.warandpeace["id"] + ) assert query.count() == 0 # There should be no rows with david's id in the # UserFollowingGroup table. query = session.query(UserFollowingGroup) query = query.filter( - UserFollowingGroup.object_id==self.davids_group['id']) + UserFollowingGroup.object_id == self.davids_group["id"] + ) assert query.count() == 0 diff --git a/ckan/tests/legacy/functional/api/test_package_search.py b/ckan/tests/legacy/functional/api/test_package_search.py index 55b73765925..02ca129c224 100644 --- a/ckan/tests/legacy/functional/api/test_package_search.py +++ b/ckan/tests/legacy/functional/api/test_package_search.py @@ -2,224 +2,237 @@ from six.moves.urllib.parse import quote +import pytest from dominate.util import escape import ckan.lib.search as search -from ckan.tests.legacy import setup_test_search_index -from ckan.tests.legacy.functional.api.base import * +from ckan.tests.legacy.functional.api.base import ApiTestCase, Api3TestCase +from ckan.tests.legacy import setup_test_search_index, CreateTestData from ckan.tests.legacy import TestController as ControllerTestCase class PackageSearchApiTestCase(ApiTestCase, ControllerTestCase): - - @classmethod - def setup_class(self): - setup_test_search_index() + @pytest.fixture(autouse=True) + def initial_data(self, clean_db, clean_index): CreateTestData.create() self.package_fixture_data = { - 'name' : u'testpkg', - 'title': 'Some Title', - 'url': u'http://blahblahblah.mydomain', - 'resources': [{u'url':u'http://blahblahblah.mydomain', - u'format':u'', u'description':''}], - 'tags': ['russion', 'novel'], - 'license_id': u'gpl-3.0', - 'extras': {'national_statistic':'yes', - 'geographic_coverage':'England, Wales'}, + "name": u"testpkg", + "title": "Some Title", + "url": u"http://blahblahblah.mydomain", + "resources": [ + { + u"url": u"http://blahblahblah.mydomain", + u"format": u"", + u"description": "", + } + ], + "tags": ["russion", "novel"], + "license_id": u"gpl-3.0", + "extras": { + "national_statistic": "yes", + "geographic_coverage": "England, Wales", + }, } CreateTestData.create_arbitrary(self.package_fixture_data) - self.base_url = self.offset('/action/package_search') - - @classmethod - def teardown_class(cls): - model.repo.rebuild_db() - search.clear_all() + self.base_url = self.offset("/action/package_search") def assert_results(self, res_dict, expected_package_names): - result = res_dict['result']['results'][0] - assert_equal(result['name'], expected_package_names) + result = res_dict["result"]["results"][0] + assert result["name"] == expected_package_names def test_01_uri_q(self): - offset = self.base_url + '?q=%s' % self.package_fixture_data['name'] + offset = self.base_url + "?q=%s" % self.package_fixture_data["name"] res = self.app.get(offset, status=200) res_dict = self.data_from_res(res) - self.assert_results(res_dict, 'testpkg') - assert res_dict['result']['count'] == 1, res_dict['result']['count'] + self.assert_results(res_dict, "testpkg") + assert res_dict["result"]["count"] == 1, res_dict["result"]["count"] def test_02_post_q(self): offset = self.base_url - query = {'q':'testpkg'} + query = {"q": "testpkg"} res = self.app.post(offset, params=query, status=200) res_dict = self.data_from_res(res) - self.assert_results(res_dict, 'testpkg') - assert res_dict['result']['count'] == 1, res_dict['result']['count'] + self.assert_results(res_dict, "testpkg") + assert res_dict["result"]["count"] == 1, res_dict["result"]["count"] def test_04_post_json(self): - query = {'q': self.package_fixture_data['name']} + query = {"q": self.package_fixture_data["name"]} json_query = self.dumps(query) offset = self.base_url res = self.app.post(offset, params=json_query, status=200) res_dict = self.data_from_res(res) - self.assert_results(res_dict, 'testpkg') - assert res_dict['result']['count'] == 1, res_dict['result']['count'] + self.assert_results(res_dict, "testpkg") + assert res_dict["result"]["count"] == 1, res_dict["result"]["count"] def test_06_uri_q_tags(self): query = escape('annakarenina tags:russian tags:tolstoy') offset = self.base_url + '?q=%s' % query res = self.app.get(offset, status=200) res_dict = self.data_from_res(res) - self.assert_results(res_dict, 'annakarenina') - assert res_dict['result']['count'] == 1, res_dict['count'] + self.assert_results(res_dict, "annakarenina") + assert res_dict["result"]["count"] == 1, res_dict["count"] def test_09_just_tags(self): - offset = self.base_url + '?q=tags:russian' + offset = self.base_url + "?q=tags:russian" res = self.app.get(offset, status=200) res_dict = self.data_from_res(res) - assert res_dict['result']['count'] == 2, res_dict + assert res_dict["result"]["count"] == 2, res_dict def test_10_multiple_tags(self): - offset = self.base_url + '?q=tags:tolstoy tags:russian' + offset = self.base_url + "?q=tags:tolstoy tags:russian" res = self.app.get(offset, status=200) res_dict = self.data_from_res(res) - assert res_dict['result']['count'] == 1, res_dict + assert res_dict["result"]["count"] == 1, res_dict def test_12_all_packages_q(self): offset = self.base_url + '?q=""' res = self.app.get(offset, status=200) res_dict = self.data_from_res(res) - assert_equal(res_dict['result']['count'], 3) + assert res_dict["result"]["count"] == 3 def test_12_all_packages_no_q(self): offset = self.base_url res = self.app.get(offset, status=200) res_dict = self.data_from_res(res) - assert_equal(res_dict['result']['count'], 3) + assert res_dict["result"]["count"] == 3 def test_12_filter_by_openness(self): - offset = self.base_url + '?filter_by_openness=1' - res = self.app.get(offset, status=400) # feature dropped in #1360 + offset = self.base_url + "?filter_by_openness=1" + res = self.app.get(offset, status=400) # feature dropped in #1360 assert "'filter_by_openness'" in res.body, res.body def test_12_filter_by_downloadable(self): - offset = self.base_url + '?filter_by_downloadable=1' - res = self.app.get(offset, status=400) # feature dropped in #1360 + offset = self.base_url + "?filter_by_downloadable=1" + res = self.app.get(offset, status=400) # feature dropped in #1360 assert "'filter_by_downloadable'" in res.body, res.body class LegacyOptionsTestCase(ApiTestCase, ControllerTestCase): - '''Here are tests with URIs in the syntax they were in - for API v1 and v2.''' - @classmethod - def setup_class(self): - setup_test_search_index() + """Here are tests with URIs in the syntax they were in + for API v1 and v2.""" + + @pytest.fixture(autouse=True) + def initial_data(self, clean_db, clean_index): CreateTestData.create() self.package_fixture_data = { - 'name' : u'testpkg', - 'title': 'Some Title', - 'url': u'http://blahblahblah.mydomain', - 'resources': [{u'url':u'http://blahblahblah.mydomain', - u'format':u'', u'description':''}], - 'tags': ['russion', 'novel'], - 'license_id': u'gpl-3.0', - 'extras': {'national_statistic':'yes', - 'geographic_coverage':'England, Wales'}, + "name": u"testpkg", + "title": "Some Title", + "url": u"http://blahblahblah.mydomain", + "resources": [ + { + u"url": u"http://blahblahblah.mydomain", + u"format": u"", + u"description": "", + } + ], + "tags": ["russion", "novel"], + "license_id": u"gpl-3.0", + "extras": { + "national_statistic": "yes", + "geographic_coverage": "England, Wales", + }, } CreateTestData.create_arbitrary(self.package_fixture_data) - self.base_url = self.offset('/search/dataset') - - @classmethod - def teardown_class(cls): - model.repo.rebuild_db() - search.clear_all() + self.base_url = self.offset("/search/dataset") def test_08_all_fields_syntax_error(self): - offset = self.base_url + '?all_fields=should_be_boolean' # invalid all_fields value + offset = ( + self.base_url + "?all_fields=should_be_boolean" + ) # invalid all_fields value res = self.app.get(offset, status=400) - assert('boolean' in res.body) - assert('all_fields' in res.body) - self.assert_json_response(res, 'boolean') + assert "boolean" in res.body + assert "all_fields" in res.body + self.assert_json_response(res, "boolean") def test_09_just_tags(self): - offset = self.base_url + '?tags=tolstoy' + offset = self.base_url + "?tags=tolstoy" res = self.app.get(offset, status=200) res_dict = self.data_from_res(res) - assert res_dict['count'] == 1, res_dict + assert res_dict["count"] == 1, res_dict def test_10_single_tag_with_plus(self): - tagname = "Flexible+" + quote(u'\u30a1'.encode('utf8')) - offset = self.base_url + "?tags=%s&all_fields=1"%tagname + tagname = "Flexible+" + quote(u"\u30a1".encode("utf8")) + offset = self.base_url + "?tags=%s&all_fields=1" % tagname res = self.app.get(offset, status=200) res_dict = self.data_from_res(res) - assert res_dict['count'] == 2, res_dict + assert res_dict["count"] == 2, res_dict def test_10_multi_tags_with_ampersand_including_a_multiword_tagame(self): - tagname = "Flexible+" + quote(u'\u30a1'.encode('utf8')) - offset = self.base_url + '?tags=tolstoy&tags=%s&all_fields=1' % tagname + tagname = "Flexible+" + quote(u"\u30a1".encode("utf8")) + offset = self.base_url + "?tags=tolstoy&tags=%s&all_fields=1" % tagname res = self.app.get(offset, status=200) res_dict = self.data_from_res(res) - assert res_dict['count'] == 1, res_dict + assert res_dict["count"] == 1, res_dict def test_10_multiple_tags_with_ampersand(self): - offset = self.base_url + '?tags=tolstoy&tags=russian&all_fields=1' + offset = self.base_url + "?tags=tolstoy&tags=russian&all_fields=1" res = self.app.get(offset, status=200) res_dict = self.data_from_res(res) - assert res_dict['count'] == 1, res_dict + assert res_dict["count"] == 1, res_dict def test_10_many_tags_with_ampersand(self): - offset = self.base_url + '?tags=tolstoy&tags=russian&tags=tolstoy' + offset = self.base_url + "?tags=tolstoy&tags=russian&tags=tolstoy" res = self.app.get(offset, status=200) res_dict = self.data_from_res(res) - assert res_dict['count'] == 1, res_dict + assert res_dict["count"] == 1, res_dict def test_13_just_groups(self): - offset = self.base_url + '?groups=roger' + offset = self.base_url + "?groups=roger" res = self.app.get(offset, status=200) res_dict = self.data_from_res(res) - assert res_dict['result']['count'] == 1, res_dict + assert res_dict["result"]["count"] == 1, res_dict def test_14_empty_parameter_ignored(self): - offset = self.base_url + '?groups=roger&title=' + offset = self.base_url + "?groups=roger&title=" res = self.app.get(offset, status=200) res_dict = self.data_from_res(res) - assert res_dict['result']['count'] == 1, res_dict + assert res_dict["result"]["count"] == 1, res_dict class TestPackageSearchApi3(Api3TestCase, PackageSearchApiTestCase): - '''Here are tests with URIs in specifically SOLR syntax.''' + """Here are tests with URIs in specifically SOLR syntax.""" + def test_09_just_tags(self): - offset = self.base_url + '?q=tags:russian&fl=*' + offset = self.base_url + "?q=tags:russian&fl=*" res = self.app.get(offset, status=200) res_dict = self.data_from_res(res) - assert res_dict['result']['count'] == 2, res_dict + assert res_dict["result"]["count"] == 2, res_dict def test_11_pagination_limit(self): - offset = self.base_url + '?fl=*&q=tags:russian&rows=1&sort=name asc' + offset = self.base_url + "?fl=*&q=tags:russian&rows=1&sort=name asc" res = self.app.get(offset, status=200) res_dict = self.data_from_res(res) - assert res_dict['result']['count'] == 2, res_dict - assert len(res_dict['result']['results']) == 1, res_dict - self.assert_results(res_dict, 'annakarenina') + assert res_dict["result"]["count"] == 2, res_dict + assert len(res_dict["result"]["results"]) == 1, res_dict + self.assert_results(res_dict, "annakarenina") def test_11_pagination_offset_limit(self): - offset = self.base_url + '?fl=*&q=tags:russian&start=1&rows=1&sort=name asc' + offset = ( + self.base_url + "?fl=*&q=tags:russian&start=1&rows=1&sort=name asc" + ) res = self.app.get(offset, status=200) res_dict = self.data_from_res(res) - assert res_dict['result']['count'] == 2, res_dict - assert len(res_dict['result']['results']) == 1, res_dict - self.assert_results(res_dict, 'warandpeace') + assert res_dict["result"]["count"] == 2, res_dict + assert len(res_dict["result"]["results"]) == 1, res_dict + self.assert_results(res_dict, "warandpeace") def test_11_pagination_validation_error(self): - offset = self.base_url + '?fl=*&q=tags:russian&start=should_be_integer&rows=1&sort=name asc' # invalid offset value + offset = ( + self.base_url + + "?fl=*&q=tags:russian&start=should_be_integer&rows=1&sort=name asc" + ) # invalid offset value res = self.app.get(offset, status=409) - assert('Validation Error' in res.body) + assert "Validation Error" in res.body def test_12_v1_or_v2_syntax(self): - offset = self.base_url + '?all_fields=1' + offset = self.base_url + "?all_fields=1" res = self.app.get(offset, status=400) - assert("Invalid search parameters: ['all_fields']" in res.body), res.body + assert ( + "Invalid search parameters: ['all_fields']" in res.body + ), res.body def test_13_just_groups(self): - offset = self.base_url + '?q=groups:roger' + offset = self.base_url + "?q=groups:roger" res = self.app.get(offset, status=200) res_dict = self.data_from_res(res) - assert res_dict['result']['count'] == 1, res_dict + assert res_dict["result"]["count"] == 1, res_dict diff --git a/ckan/tests/legacy/functional/api/test_resource.py b/ckan/tests/legacy/functional/api/test_resource.py index 5614a01cd3b..05c60246117 100644 --- a/ckan/tests/legacy/functional/api/test_resource.py +++ b/ckan/tests/legacy/functional/api/test_resource.py @@ -3,65 +3,71 @@ from ckan.tests.legacy.functional.api.base import ApiTestCase, CreateTestData from ckan.tests.legacy import TestController as ControllerTestCase from ckan import model +import pytest + class TestResourceApi(ApiTestCase, ControllerTestCase): - api_version = '2' + api_version = "2" - @classmethod - def setup_class(self): + @pytest.fixture(autouse=True) + def initial_data(self, clean_db): CreateTestData.create() - self.ab = 'http://site.com/a/b.txt' - self.cd = 'http://site.com/c/d.txt' + self.ab = "http://site.com/a/b.txt" + self.cd = "http://site.com/c/d.txt" self.package_fixture_data = { - 'name' : u'testpkg', - 'title': 'Some Title', - 'url': u'http://blahblahblah.mydomain', - 'resources':[ - {'url':self.ab, - 'description':'This is site ab.', - 'format':'Excel spreadsheet', - 'alt_url':'alt', - 'extras':{'size':'100'}, - 'hash':'abc-123'}, - {'url':self.cd, - 'description':'This is site cd.', - 'format':'CSV', - 'alt_url':'alt', - 'extras':{'size':'100'}, - 'hash':'qwe-456'}, - ], - 'tags': ['russian', 'novel'], - 'license_id': u'gpl-3.0', - 'extras': {'national_statistic':'yes', - 'geographic_coverage':'England, Wales'}, + "name": u"testpkg", + "title": "Some Title", + "url": u"http://blahblahblah.mydomain", + "resources": [ + { + "url": self.ab, + "description": "This is site ab.", + "format": "Excel spreadsheet", + "alt_url": "alt", + "extras": {"size": "100"}, + "hash": "abc-123", + }, + { + "url": self.cd, + "description": "This is site cd.", + "format": "CSV", + "alt_url": "alt", + "extras": {"size": "100"}, + "hash": "qwe-456", + }, + ], + "tags": ["russian", "novel"], + "license_id": u"gpl-3.0", + "extras": { + "national_statistic": "yes", + "geographic_coverage": "England, Wales", + }, } CreateTestData.create_arbitrary(self.package_fixture_data) - self.base_url = self.offset('/util/resource') - - @classmethod - def teardown_class(self): - model.repo.rebuild_db() + self.base_url = self.offset("/util/resource") - def test_good_input(self): - offset = self.base_url + '/format_autocomplete?incomplete=cs' - result = self.app.get(offset, status=200) - content_type = result.headers.get('Content-Type') - assert 'application/json' in content_type, content_type + def test_good_input(self, app): + offset = self.base_url + "/format_autocomplete?incomplete=cs" + result = app.get(offset, status=200) + content_type = result.headers.get("Content-Type") + assert "application/json" in content_type, content_type res_json = self.loads(result.body) - assert 'ResultSet' in res_json, res_json - assert 'Result' in res_json.get('ResultSet'), res_json - result_json = res_json.get('ResultSet').get('Result') + assert "ResultSet" in res_json, res_json + assert "Result" in res_json.get("ResultSet"), res_json + result_json = res_json.get("ResultSet").get("Result") assert len(result_json) == 1, result_json - assert 'Format' in result_json[0], result_json - assert result_json[0].get('Format') == 'csv' + assert "Format" in result_json[0], result_json + assert result_json[0].get("Format") == "csv" - def test_missing_format(self): - offset = self.base_url + '/format_autocomplete?incomplete=incorrectformat' - result = self.app.get(offset, status=200) - content_type = result.headers.get('Content-Type') - assert 'application/json' in content_type, content_type + def test_missing_format(self, app): + offset = ( + self.base_url + "/format_autocomplete?incomplete=incorrectformat" + ) + result = app.get(offset, status=200) + content_type = result.headers.get("Content-Type") + assert "application/json" in content_type, content_type res_json = self.loads(result.body) - assert 'ResultSet' in res_json, res_json - assert 'Result' in res_json.get('ResultSet'), res_json - result_json = res_json.get('ResultSet').get('Result') + assert "ResultSet" in res_json, res_json + assert "Result" in res_json.get("ResultSet"), res_json + result_json = res_json.get("ResultSet").get("Result") assert not result_json, result_json diff --git a/ckan/tests/legacy/functional/api/test_resource_search.py b/ckan/tests/legacy/functional/api/test_resource_search.py deleted file mode 100644 index 57d7ab58768..00000000000 --- a/ckan/tests/legacy/functional/api/test_resource_search.py +++ /dev/null @@ -1,80 +0,0 @@ -# encoding: utf-8 - -from ckan.tests.legacy.functional.api.base import * -from ckan.tests.legacy import TestController as ControllerTestCase - -class ResourceSearchApiTestCase(ApiTestCase, ControllerTestCase): - - @classmethod - def setup_class(self): - CreateTestData.create() - self.ab = 'http://site.com/a/b.txt' - self.cd = 'http://site.com/c/d.txt' - self.package_fixture_data = { - 'name' : u'testpkg', - 'title': 'Some Title', - 'url': u'http://blahblahblah.mydomain', - 'resources':[ - {'url':self.ab, - 'description':'This is site ab.', - 'format':'Excel spreadsheet', - 'alt_url':'alt', - 'extras':{'size':'100'}, - 'hash':'abc-123'}, - {'url':self.cd, - 'description':'This is site cd.', - 'format':'Office spreadsheet', - 'alt_url':'alt', - 'extras':{'size':'100'}, - 'hash':'qwe-456'}, - ], - 'tags': ['russion', 'novel', 'Leo Tolstoy'], - 'license_id': u'gpl-3.0', - 'extras': {'national_statistic':'yes', - 'geographic_coverage':'England, Wales'}, - } - CreateTestData.create_arbitrary(self.package_fixture_data) - self.base_url = self.offset('/search/resource') - - @classmethod - def teardown_class(self): - model.repo.rebuild_db() - - def assert_urls_in_search_results(self, offset, expected_urls): - result = self.app.get(offset, status=200) - result_dict = self.loads(result.body) - resources = [model.Session.query(model.Resource).get(resource_id) for resource_id in result_dict['results']] - urls = set([resource.url for resource in resources]) - assert urls == set(expected_urls), urls - - def test_01_url(self): - offset = self.base_url + '?url=site' - self.assert_urls_in_search_results(offset, [self.ab, self.cd]) - - def test_02_url_qjson(self): - query = {'url':'site'} - json_query = self.dumps(query) - offset = self.base_url + '?qjson=%s' % json_query - self.assert_urls_in_search_results(offset, [self.ab, self.cd]) - - def test_03_post_qjson(self): - query = {'url':'site'} - json_query = self.dumps(query) - offset = self.base_url - result = self.app.post(offset, params=json_query, status=200) - expected_urls = [self.ab, self.cd] - result_dict = self.loads(result.body) - resources = [model.Session.query(model.Resource).get(resource_id) for resource_id in result_dict['results']] - urls = set([resource.url for resource in resources]) - assert urls == set(expected_urls), urls - - def test_04_bad_option(self): - offset = self.base_url + '?random=option' - result = self.app.get(offset, status=400) - self.assert_json_response(result, 'Bad request - Bad search option') - - def test_05_options(self): - offset = self.base_url + '?url=site&all_fields=1&callback=mycallback' - result = self.app.get(offset, status=200) - assert re.match('^mycallback\(.*\);$', result.body), result.body - assert 'package_id' in result.body, result.body diff --git a/ckan/tests/legacy/functional/api/test_user.py b/ckan/tests/legacy/functional/api/test_user.py index a7f5826da9a..98968c3063e 100644 --- a/ckan/tests/legacy/functional/api/test_user.py +++ b/ckan/tests/legacy/functional/api/test_user.py @@ -1,6 +1,5 @@ # encoding: utf-8 -from nose.tools import assert_equal import ckan.logic as logic from ckan import model @@ -8,219 +7,185 @@ from ckan.tests.legacy import TestController as ControllerTestCase from ckan.tests.legacy import url_for from ckan.tests import helpers - +import pytest from ckan.common import json class TestUserApi(ControllerTestCase): - @classmethod - def setup_class(cls): + @pytest.fixture(autouse=True) + def initial_data(self, clean_db): CreateTestData.create() - @classmethod - def teardown_class(cls): - model.repo.rebuild_db() - def test_autocomplete(self): response = self.app.get( - url=url_for(controller='api', action='user_autocomplete', ver=2), - params={ - 'q': u'sysadmin', - }, + url=url_for(controller="api", action="user_autocomplete", ver=2), + params={"q": u"sysadmin"}, status=200, ) - print(response.json) - assert set(response.json[0].keys()) == set(['id', 'name', 'fullname']) - assert_equal(response.json[0]['name'], u'testsysadmin') - assert_equal(response.headers.get('Content-Type'), 'application/json;charset=utf-8') + assert set(response.json[0].keys()) == set(["id", "name", "fullname"]) + assert response.json[0]["name"] == u"testsysadmin" + assert ( + response.headers.get("Content-Type") + == "application/json;charset=utf-8" + ) def test_autocomplete_multiple(self): response = self.app.get( - url=url_for(controller='api', action='user_autocomplete', ver=2), - params={ - 'q': u'tes', - }, + url=url_for(controller="api", action="user_autocomplete", ver=2), + params={"q": u"tes"}, status=200, ) - print(response.json) - assert_equal(len(response.json), 2) + assert len(response.json) == 2 def test_autocomplete_limit(self): response = self.app.get( - url=url_for(controller='api', action='user_autocomplete', ver=2), - params={ - 'q': u'tes', - 'limit': 1 - }, + url=url_for(controller="api", action="user_autocomplete", ver=2), + params={"q": u"tes", "limit": 1}, status=200, ) print(response.json) - assert_equal(len(response.json), 1) + assert len(response.json) == 1 -class TestCreateUserApiDisabled(ControllerTestCase): - ''' +class TestCreateUserApiDisabled(object): + """ Tests for the creating user when create_user_via_api is disabled. - ''' + """ - @classmethod - def setup_class(cls): + @pytest.fixture(autouse=True) + def initial_data(self, clean_db): CreateTestData.create() - cls.sysadmin_user = model.User.get('testsysadmin') - cls.app = helpers._get_test_app() + self.sysadmin_user = model.User.get("testsysadmin") - @classmethod - def teardown_class(cls): - model.repo.rebuild_db() - - def test_user_create_api_enabled_sysadmin(self): + def test_user_create_api_enabled_sysadmin(self, app): params = { - 'name': 'testinganewusersysadmin', - 'email': 'testinganewuser@ckan.org', - 'password': 'TestPassword1', + "name": "testinganewusersysadmin", + "email": "testinganewuser@ckan.org", + "password": "TestPassword1", } - res = self.app.post( - '/api/3/action/user_create', + res = app.post( + "/api/3/action/user_create", json.dumps(params), - extra_environ={'Authorization': str(self.sysadmin_user.apikey)}, - expect_errors=True) + extra_environ={"Authorization": str(self.sysadmin_user.apikey)}, + expect_errors=True, + ) res_dict = res.json - assert res_dict['success'] is True + assert res_dict["success"] is True - def test_user_create_api_disabled_anon(self): + def test_user_create_api_disabled_anon(self, app): params = { - 'name': 'testinganewuseranon', - 'email': 'testinganewuser@ckan.org', - 'password': 'TestPassword1', + "name": "testinganewuseranon", + "email": "testinganewuser@ckan.org", + "password": "TestPassword1", } - res = self.app.post('/api/3/action/user_create', json.dumps(params), - expect_errors=True) + res = app.post( + "/api/3/action/user_create", json.dumps(params), expect_errors=True + ) res_dict = res.json - assert res_dict['success'] is False + assert res_dict["success"] is False -class TestCreateUserApiEnabled(ControllerTestCase): - ''' +class TestCreateUserApiEnabled(object): + """ Tests for the creating user when create_user_via_api is enabled. - ''' + """ - @classmethod - def setup_class(cls): + @pytest.fixture(autouse=True) + def initial_data(self, clean_db): CreateTestData.create() - cls.sysadmin_user = model.User.get('testsysadmin') - cls.app = helpers._get_test_app() - - @classmethod - def teardown_class(cls): - model.repo.rebuild_db() + self.sysadmin_user = model.User.get("testsysadmin") - def test_user_create_api_enabled_sysadmin(self): + def test_user_create_api_enabled_sysadmin(self, app): params = { - 'name': 'testinganewusersysadmin', - 'email': 'testinganewuser@ckan.org', - 'password': 'TestPassword1', + "name": "testinganewusersysadmin", + "email": "testinganewuser@ckan.org", + "password": "TestPassword1", } - res = self.app.post( - '/api/3/action/user_create', + res = app.post( + "/api/3/action/user_create", json.dumps(params), - extra_environ={'Authorization': str(self.sysadmin_user.apikey)}) + extra_environ={"Authorization": str(self.sysadmin_user.apikey)}, + ) res_dict = res.json - assert res_dict['success'] is True + assert res_dict["success"] is True - @helpers.change_config('ckan.auth.create_user_via_api', True) - def test_user_create_api_enabled_anon(self): + @pytest.mark.ckan_config("ckan.auth.create_user_via_api", True) + def test_user_create_api_enabled_anon(self, app): params = { - 'name': 'testinganewuseranon', - 'email': 'testinganewuser@ckan.org', - 'password': 'TestPassword1', + "name": "testinganewuseranon", + "email": "testinganewuser@ckan.org", + "password": "TestPassword1", } - res = self.app.post('/api/3/action/user_create', json.dumps(params)) + res = app.post("/api/3/action/user_create", json.dumps(params)) res_dict = res.json - assert res_dict['success'] is True + assert res_dict["success"] is True -class TestCreateUserWebDisabled(ControllerTestCase): - ''' +class TestCreateUserWebDisabled(object): + """ Tests for the creating user by create_user_via_web is disabled. - ''' + """ - @classmethod - def setup_class(cls): + @pytest.fixture(autouse=True) + def initial_data(self, clean_db): CreateTestData.create() - cls.app = helpers._get_test_app() - cls.sysadmin_user = model.User.get('testsysadmin') - - @classmethod - def teardown_class(cls): - model.repo.rebuild_db() + self.sysadmin_user = model.User.get("testsysadmin") - @helpers.change_config('ckan.auth.create_user_via_web', False) - def test_user_create_api_disabled(self): + @pytest.mark.ckan_config("ckan.auth.create_user_via_web", False) + def test_user_create_api_disabled(self, app): params = { - 'name': 'testinganewuser', - 'email': 'testinganewuser@ckan.org', - 'password': 'TestPassword1', + "name": "testinganewuser", + "email": "testinganewuser@ckan.org", + "password": "TestPassword1", } - res = self.app.post('/api/3/action/user_create', json.dumps(params), - expect_errors=True) + res = app.post( + "/api/3/action/user_create", json.dumps(params), expect_errors=True + ) res_dict = res.json - assert res_dict['success'] is False + assert res_dict["success"] is False -class TestCreateUserWebEnabled(ControllerTestCase): - ''' +class TestCreateUserWebEnabled(object): + """ Tests for the creating user by create_user_via_web is enabled. - ''' + """ - @classmethod - def setup_class(cls): + @pytest.fixture(autouse=True) + def initial_data(self, clean_db): CreateTestData.create() - cls.app = helpers._get_test_app() - cls.sysadmin_user = model.User.get('testsysadmin') - - @classmethod - def teardown_class(cls): - model.repo.rebuild_db() + self.sysadmin_user = model.User.get("testsysadmin") - @helpers.change_config('ckan.auth.create_user_via_web', True) - def test_user_create_api_disabled(self): + @pytest.mark.ckan_config("ckan.auth.create_user_via_web", True) + def test_user_create_api_disabled(self, app): params = { - 'name': 'testinganewuser', - 'email': 'testinganewuser@ckan.org', - 'password': 'TestPassword1', + "name": "testinganewuser", + "email": "testinganewuser@ckan.org", + "password": "TestPassword1", } - res = self.app.post('/api/3/action/user_create', json.dumps(params), - expect_errors=True) + res = app.post( + "/api/3/action/user_create", json.dumps(params), expect_errors=True + ) res_dict = res.json - assert res_dict['success'] is False + assert res_dict["success"] is False class TestUserActions(object): - - @classmethod - def setup_class(cls): + @pytest.fixture(autouse=True) + def initial_data(self, clean_db): CreateTestData.create() - @classmethod - def teardown_class(cls): - model.repo.rebuild_db() - def test_user_create_simple(self): - '''Simple creation of a new user by a non-sysadmin user.''' - context = { - 'model': model, - 'session': model.Session, - 'user': 'tester' - } + """Simple creation of a new user by a non-sysadmin user.""" + context = {"model": model, "session": model.Session, "user": "tester"} data_dict = { - 'name': 'a-new-user', - 'email': 'a.person@example.com', - 'password': 'TestPassword1', + "name": "a-new-user", + "email": "a.person@example.com", + "password": "TestPassword1", } - user_dict = logic.get_action('user_create')(context, data_dict) + user_dict = logic.get_action("user_create")(context, data_dict) - assert_equal(user_dict['name'], 'a-new-user') - assert 'email' in user_dict - assert 'apikey' in user_dict - assert 'password' not in user_dict + assert user_dict["name"] == "a-new-user" + assert "email" in user_dict + assert "apikey" in user_dict + assert "password" not in user_dict diff --git a/ckan/tests/legacy/functional/api/test_util.py b/ckan/tests/legacy/functional/api/test_util.py index dd7f7fc54d1..eb99c38335a 100644 --- a/ckan/tests/legacy/functional/api/test_util.py +++ b/ckan/tests/legacy/functional/api/test_util.py @@ -1,41 +1,41 @@ # encoding: utf-8 -from nose.tools import assert_equal - +import pytest from ckan import model from ckan.lib.create_test_data import CreateTestData from ckan.tests.legacy import TestController as ControllerTestCase from ckan.tests.legacy import url_for -class TestUtil(ControllerTestCase): - @classmethod - def setup_class(cls): - CreateTestData.create() - - @classmethod - def teardown_class(cls): - model.repo.rebuild_db() - - def test_munge_package_name(self): - response = self.app.get( - url=url_for(controller='api', action='munge_package_name', ver=2), - params={'name': 'test name'}, - status=200, - ) - assert_equal(response.body, '"test-name"') - - def test_munge_title_to_package_name(self): - response = self.app.get( - url=url_for(controller='api', action='munge_title_to_package_name', ver=2), - params={'name': 'Test title'}, - status=200, - ) - assert_equal(response.body, '"test-title"') - - def test_munge_tag(self): - response = self.app.get( - url=url_for(controller='api', action='munge_tag', ver=2), - params={'name': 'Test subject'}, - status=200, - ) - assert_equal(response.body, '"test-subject"') + +@pytest.fixture(autouse=True) +def initial_data(clean_db): + CreateTestData.create() + + +def test_munge_package_name(app): + response = app.get( + url=url_for(controller="api", action="munge_package_name", ver=2), + params={"name": "test name"}, + status=200, + ) + assert response.body == '"test-name"' + + +def test_munge_title_to_package_name(app): + response = app.get( + url=url_for( + controller="api", action="munge_title_to_package_name", ver=2 + ), + params={"name": "Test title"}, + status=200, + ) + assert response.body == '"test-title"' + + +def test_munge_tag(app): + response = app.get( + url=url_for(controller="api", action="munge_tag", ver=2), + params={"name": "Test subject"}, + status=200, + ) + assert response.body == '"test-subject"' diff --git a/ckan/tests/legacy/functional/base.py b/ckan/tests/legacy/functional/base.py index 671a0d1d5e3..8c18d868550 100644 --- a/ckan/tests/legacy/functional/base.py +++ b/ckan/tests/legacy/functional/base.py @@ -3,5 +3,6 @@ from ckan.tests.legacy.html_check import HtmlCheckMethods from ckan.tests.legacy import TestController as ControllerTestCase + class FunctionalTestCase(ControllerTestCase, HtmlCheckMethods): pass diff --git a/ckan/tests/legacy/functional/test_admin.py b/ckan/tests/legacy/functional/test_admin.py index 74c79477653..32cf2d02341 100644 --- a/ckan/tests/legacy/functional/test_admin.py +++ b/ckan/tests/legacy/functional/test_admin.py @@ -1,27 +1,20 @@ # encoding: utf-8 +import pytest import ckan.model as model -from ckan.tests.legacy import url_for, CreateTestData, WsgiAppCase +from ckan.tests.legacy import url_for, CreateTestData -class TestAdminController(WsgiAppCase): - @classmethod - def setup_class(cls): - # setup test data including testsysadmin user - CreateTestData.create() - @classmethod - def teardown_class(self): - model.repo.rebuild_db() - - #test that only sysadmins can access the /ckan-admin page - def test_index(self): - url = url_for('admin.index') - response = self.app.get(url, status=[403]) - # random username - response = self.app.get(url, status=[403], - extra_environ={'REMOTE_USER': 'my-random-user-name'}) - # now test real access - username = u'testsysadmin'.encode('utf8') - response = self.app.get(url, - extra_environ={'REMOTE_USER': username}) - assert 'Administration' in response, response +@pytest.mark.usefixtures("clean_db") +def test_index(app): + CreateTestData.create() + url = url_for("admin.index") + response = app.get(url, status=[403]) + # random username + response = app.get( + url, status=[403], extra_environ={"REMOTE_USER": "my-random-user-name"} + ) + # now test real access + username = u"testsysadmin".encode("utf8") + response = app.get(url, extra_environ={"REMOTE_USER": username}) + assert "Administration" in response, response diff --git a/ckan/tests/legacy/functional/test_error.py b/ckan/tests/legacy/functional/test_error.py index a67a42da2fc..5fc3b38b61b 100644 --- a/ckan/tests/legacy/functional/test_error.py +++ b/ckan/tests/legacy/functional/test_error.py @@ -1,9 +1,7 @@ # encoding: utf-8 -from base import FunctionalTestCase -class TestError(FunctionalTestCase): - def test_without_redirect(self): - # this is what a web bot might do - res = self.app.get('/error/document') - assert 'There is no error.' in str(res), str(res) +def test_without_redirect(app): + # this is what a web bot might do + res = app.get("/error/document") + assert "There is no error." in str(res) diff --git a/ckan/tests/legacy/functional/test_group.py b/ckan/tests/legacy/functional/test_group.py index 8fdd156932c..6db14637adf 100644 --- a/ckan/tests/legacy/functional/test_group.py +++ b/ckan/tests/legacy/functional/test_group.py @@ -1,130 +1,107 @@ # encoding: utf-8 +import pytest import mock import ckan.model as model -import ckan.lib.search as search from ckan.lib.create_test_data import CreateTestData from ckan.logic import get_action -from ckan.tests.legacy import * -from base import FunctionalTestCase - - -class TestGroup(FunctionalTestCase): - - @classmethod - def setup_class(self): - search.clear_all() - model.Session.remove() - CreateTestData.create() - - @classmethod - def teardown_class(self): - model.repo.rebuild_db() - - def test_sorting(self): - model.repo.rebuild_db() - - testsysadmin = model.User(name=u'testsysadmin') - testsysadmin.sysadmin = True - model.Session.add(testsysadmin) - - pkg1 = model.Package(name="pkg1") - pkg2 = model.Package(name="pkg2") - model.Session.add(pkg1) - model.Session.add(pkg2) - model.Session.commit() - - CreateTestData.create_groups([{'name': "alpha", - 'title': "Alpha", - 'packages': []}, - {'name': "beta", - 'title': "Beta", - 'packages': ["pkg1", "pkg2"]}, - {'name': "delta", - 'title': 'Delta', - 'packages': ["pkg1"]}, - {'name': "gamma", - 'title': "Gamma", - 'packages': []}], - admin_user_name='testsysadmin') - - context = {'model': model, 'session': model.Session, - 'user': 'testsysadmin', 'for_view': True, - 'with_private': False} - data_dict = {'all_fields': True} - results = get_action('group_list')(context, data_dict) - assert results[0]['name'] == u'alpha', results[0]['name'] - assert results[-1]['name'] == u'gamma', results[-1]['name'] - - # Test title forward - data_dict = {'all_fields': True, 'sort': 'title asc'} - results = get_action('group_list')(context, data_dict) - assert results[0]['name'] == u'alpha', results[0]['name'] - assert results[-1]['name'] == u'gamma', results[-1]['name'] - - # Test title reverse - data_dict = {'all_fields': True, 'sort': 'title desc'} - results = get_action('group_list')(context, data_dict) - assert results[0]['name'] == u'gamma', results[0]['name'] - assert results[-1]['name'] == u'alpha', results[-1]['name'] - - # Test name reverse - data_dict = {'all_fields': True, 'sort': 'name desc'} - results = get_action('group_list')(context, data_dict) - assert results[0]['name'] == u'gamma', results[0]['name'] - assert results[-1]['name'] == u'alpha', results[-1]['name'] - - # Test packages reversed - data_dict = {'all_fields': True, 'sort': 'package_count desc'} - results = get_action('group_list')(context, data_dict) - assert results[0]['name'] == u'beta', results[0]['name'] - assert results[1]['name'] == u'delta', results[1]['name'] - - # Test packages forward - data_dict = {'all_fields': True, 'sort': 'package_count asc'} - results = get_action('group_list')(context, data_dict) - assert results[-2]['name'] == u'delta', results[-2]['name'] - assert results[-1]['name'] == u'beta', results[-1]['name'] - - # Default ordering for packages - data_dict = {'all_fields': True, 'sort': 'package_count'} - results = get_action('group_list')(context, data_dict) - assert results[0]['name'] == u'beta', results[0]['name'] - assert results[1]['name'] == u'delta', results[1]['name'] - - def test_read_non_existent(self): - name = u'group_does_not_exist' - offset = url_for(controller='group', action='read', id=name) - res = self.app.get(offset, status=404) - - -class TestMemberInvite(FunctionalTestCase): - @classmethod - def setup_class(self): - model.Session.remove() - model.repo.rebuild_db() - - def teardown(self): - model.repo.rebuild_db() - - @mock.patch('ckan.lib.mailer.mail_user') - def test_member_new_invites_user_if_received_email(self, mail_user): - user = CreateTestData.create_user('a_user', sysadmin=True) - group_name = 'a_group' - CreateTestData.create_groups([{'name': group_name}], user.name) - group = model.Group.get(group_name) - url = url_for(controller='group', action='member_new', id=group.id) - email = 'invited_user@mailinator.com' - role = 'member' - - params = {'email': email, 'role': role} - res = self.app.post(url, params, - extra_environ={'REMOTE_USER': str(user.name)}) - - users = model.User.by_email(email) - assert len(users) == 1, users - user = users[0] - assert user.email == email, user - assert group.id in user.get_group_ids(capacity=role) +from ckan.lib.helpers import url_for +import ckan.tests.factories as factories + + +@pytest.mark.usefixtures("clean_db", "clean_index") +def test_sorting(): + testsysadmin = factories.Sysadmin(name=u"testsysadmin") + + pkg1 = model.Package(name="pkg1") + pkg2 = model.Package(name="pkg2") + model.Session.add(pkg1) + model.Session.add(pkg2) + + CreateTestData.create_groups( + [ + {"name": "alpha", "title": "Alpha", "packages": []}, + {"name": "beta", "title": "Beta", "packages": ["pkg1", "pkg2"]}, + {"name": "delta", "title": "Delta", "packages": ["pkg1"]}, + {"name": "gamma", "title": "Gamma", "packages": []}, + ], + admin_user_name="testsysadmin", + ) + + context = { + "model": model, + "session": model.Session, + "user": "testsysadmin", + "for_view": True, + "with_private": False, + } + data_dict = {"all_fields": True} + results = get_action("group_list")(context, data_dict) + assert results[0]["name"] == u"alpha", results[0]["name"] + assert results[-1]["name"] == u"gamma", results[-1]["name"] + + # Test title forward + data_dict = {"all_fields": True, "sort": "title asc"} + results = get_action("group_list")(context, data_dict) + assert results[0]["name"] == u"alpha", results[0]["name"] + assert results[-1]["name"] == u"gamma", results[-1]["name"] + + # Test title reverse + data_dict = {"all_fields": True, "sort": "title desc"} + results = get_action("group_list")(context, data_dict) + assert results[0]["name"] == u"gamma", results[0]["name"] + assert results[-1]["name"] == u"alpha", results[-1]["name"] + + # Test name reverse + data_dict = {"all_fields": True, "sort": "name desc"} + results = get_action("group_list")(context, data_dict) + assert results[0]["name"] == u"gamma", results[0]["name"] + assert results[-1]["name"] == u"alpha", results[-1]["name"] + + # Test packages reversed + data_dict = {"all_fields": True, "sort": "package_count desc"} + results = get_action("group_list")(context, data_dict) + assert results[0]["name"] == u"beta", results[0]["name"] + assert results[1]["name"] == u"delta", results[1]["name"] + + # Test packages forward + data_dict = {"all_fields": True, "sort": "package_count asc"} + results = get_action("group_list")(context, data_dict) + assert results[-2]["name"] == u"delta", results[-2]["name"] + assert results[-1]["name"] == u"beta", results[-1]["name"] + + # Default ordering for packages + data_dict = {"all_fields": True, "sort": "package_count"} + results = get_action("group_list")(context, data_dict) + assert results[0]["name"] == u"beta", results[0]["name"] + assert results[1]["name"] == u"delta", results[1]["name"] + + +@pytest.mark.usefixtures("clean_db") +def test_read_non_existent(app): + name = u"group_does_not_exist" + offset = url_for(controller="group", action="read", id=name) + app.get(offset, status=404) + + +@pytest.mark.usefixtures("clean_db") +@mock.patch("ckan.lib.mailer.mail_user") +def test_member_new_invites_user_if_received_email(_mail_user, app): + user = CreateTestData.create_user("a_user", sysadmin=True) + group_name = "a_group" + CreateTestData.create_groups([{"name": group_name}], user.name) + group = model.Group.get(group_name) + url = url_for(controller="group", action="member_new", id=group.id) + email = "invited_user@mailinator.com" + role = "member" + + params = {"email": email, "role": role} + app.post(url, params, extra_environ={"REMOTE_USER": str(user.name)}) + + users = model.User.by_email(email) + assert len(users) == 1, users + user = users[0] + assert user.email == email, user + assert group.id in user.get_group_ids(capacity=role) diff --git a/ckan/tests/legacy/functional/test_package.py b/ckan/tests/legacy/functional/test_package.py index faf638cb31a..4530fc4308d 100644 --- a/ckan/tests/legacy/functional/test_package.py +++ b/ckan/tests/legacy/functional/test_package.py @@ -1,12 +1,12 @@ # encoding: utf-8 -from __future__ import print_function +import pytest +from six import string_types from ckan.common import config from difflib import unified_diff -from nose.tools import assert_equal -from ckan.tests.legacy import * +from ckan.tests.legacy import url_for import ckan.tests.legacy as tests from ckan.tests.legacy.html_check import HtmlCheckMethods from base import FunctionalTestCase @@ -16,84 +16,93 @@ from ckan import plugins from ckan.lib.search.common import SolrSettings +existing_extra_html = ( + '', + '', +) +class TestPackageBase(object): + key1 = u"key1 Less-than: < Umlaut: \xfc" + value1 = u"value1 Less-than: < Umlaut: \xfc" -existing_extra_html = ('', '') - - -class TestPackageBase(FunctionalTestCase): - key1 = u'key1 Less-than: < Umlaut: \xfc' - value1 = u'value1 Less-than: < Umlaut: \xfc' # Note: Can't put a quotation mark in key1 or value1 because # paste.fixture doesn't unescape the value in an input field # on form submission. (But it works in real life.) def _assert_form_errors(self, res): - self.check_tag(res, '', ''), main_div_str - if isinstance(params['extras'], dict): + tag_names = list(params["tags"]) + self.check_named_element(main_div, "ul", *tag_names) + if params.has_key("state"): + assert "State: %s" % params["state"] in main_div.replace( + "", "" + ), main_div_str + if isinstance(params["extras"], dict): extras = [] - for key, value in params['extras'].items(): + for key, value in params["extras"].items(): extras.append((key, value, False)) - elif isinstance(params['extras'], (list, tuple)): - extras = params['extras'] + elif isinstance(params["extras"], (list, tuple)): + extras = params["extras"] else: raise NotImplementedError for key, value, deleted in extras: if not deleted: key_in_html_body = self.escape_for_html_body(key) value_in_html_body = self.escape_for_html_body(value) - self.check_named_element(main_div, 'tr', key_in_html_body, value_in_html_body) + self.check_named_element( + main_div, "tr", key_in_html_body, value_in_html_body + ) else: - self.check_named_element(main_div, 'tr', '!' + key) - self.check_named_element(main_div, 'tr', '!' + value) - + self.check_named_element(main_div, "tr", "!" + key) + self.check_named_element(main_div, "tr", "!" + value) def _get_resource_values(self, resources, by_resource=False): assert isinstance(resources, (list, tuple)) for res_index, resource in enumerate(resources): if by_resource: values = [] - for i, res_field in enumerate(model.Resource.get_columns(extra_columns = False)): - if isinstance(resource, (str, unicode)): - expected_value = resource if res_field == 'url' else '' + for i, res_field in enumerate( + model.Resource.get_columns(extra_columns=False) + ): + if isinstance(resource, string_types): + expected_value = resource if res_field == "url" else "" elif hasattr(resource, res_field): expected_value = getattr(resource, res_field) elif isinstance(resource, (list, tuple)): expected_value = resource[i] elif isinstance(resource, dict): - expected_value = resource.get(res_field, u'') + expected_value = resource.get(res_field, u"") else: raise NotImplemented if not by_resource: @@ -101,94 +110,108 @@ def _get_resource_values(self, resources, by_resource=False): else: values.append(expected_value) if by_resource: - yield(res_index, values) + yield (res_index, values) def escape_for_html_body(self, unescaped_str): # just deal with chars in tests - return unescaped_str.replace('<', '<') + return unescaped_str.replace("<", "<") def check_form_filled_correctly(self, res, **params): - if params.has_key('pkg'): - for key, value in params['pkg'].as_dict().items(): - if key == 'license': - key = 'license_id' + if params.has_key("pkg"): + for key, value in params["pkg"].as_dict().items(): + if key == "license": + key = "license_id" params[key] = value - prefix = '' + prefix = "" main_res = self.main_div(res) - self.check_tag(main_res, prefix+'name', params['name']) - self.check_tag(main_res, prefix+'title', params['title']) - self.check_tag(main_res, prefix+'version', params['version']) - self.check_tag(main_res, prefix+'url', params['url']) - #for res_index, res_field, expected_value in self._get_resource_values(params['resources']): + self.check_tag(main_res, prefix + "name", params["name"]) + self.check_tag(main_res, prefix + "title", params["title"]) + self.check_tag(main_res, prefix + "version", params["version"]) + self.check_tag(main_res, prefix + "url", params["url"]) + # for res_index, res_field, expected_value in self._get_resource_values(params['resources']): # ## only check fields that are on the form # if res_field not in ['url', 'id', 'description', 'hash']: # continue # self.check_tag(main_res, '%sresources__%i__%s' % (prefix, res_index, res_field), expected_value) - self.check_tag_and_data(main_res, prefix+'notes', params['notes']) - self.check_tag_and_data(main_res, 'selected', params['license_id']) - if isinstance(params['tags'], (str, unicode)): - tags = [s.strip() for s in params['tags'].split(',')] + self.check_tag_and_data(main_res, prefix + "notes", params["notes"]) + self.check_tag_and_data(main_res, "selected", params["license_id"]) + if isinstance(params["tags"], string_types): + tags = list(map(lambda s: s.strip(), params["tags"].split(","))) else: - tags = params['tags'] + tags = params["tags"] for tag in tags: - self.check_tag(main_res, prefix+'tag_string', tag) - if params.has_key('state'): - self.check_tag_and_data(main_res, 'selected', str(params['state'])) - if isinstance(params['extras'], dict): + self.check_tag(main_res, prefix + "tag_string", tag) + if params.has_key("state"): + self.check_tag_and_data(main_res, "selected", str(params["state"])) + if isinstance(params["extras"], dict): extras = [] - for key, value in params['extras'].items(): + for key, value in params["extras"].items(): extras.append((key, value, False)) else: - extras = params['extras'] + extras = params["extras"] for num, (key, value, deleted) in enumerate(sorted(extras)): key_in_html_body = self.escape_for_html_body(key) value_in_html_body = self.escape_for_html_body(value) key_escaped = key value_escaped = value - self.check_tag(main_res, 'extras__%s__key' % num, key_in_html_body) - self.check_tag(main_res, 'extras__%s__value' % num, value_escaped) + self.check_tag(main_res, "extras__%s__key" % num, key_in_html_body) + self.check_tag(main_res, "extras__%s__value" % num, value_escaped) if deleted: - self.check_tag(main_res, 'extras__%s__deleted' % num, 'checked') - - assert params['log_message'] in main_res, main_res - - def _check_redirect(self, return_url_param, expected_redirect, - pkg_name_to_edit='',extra_environ=None): - ''' + self.check_tag( + main_res, "extras__%s__deleted" % num, "checked" + ) + + assert params["log_message"] in main_res, main_res + + def _check_redirect( + self, + app, + return_url_param, + expected_redirect, + pkg_name_to_edit="", + extra_environ=None, + ): + """ @param return_url_param - encoded url to be given as param - if None then assume redirect is specified in pylons config @param expected_redirect - url we expect to redirect to (but not yet substituted) @param pkg_name_to_edit - '' means create a new dataset - ''' + """ try: - new_name = u'new-name' + new_name = u"new-name" offset_params = {} if pkg_name_to_edit: pkg_name = pkg_name_to_edit pkg = model.Package.by_name(pkg_name) assert pkg pkg_id = pkg.id - named_route = 'dataset.edit' - offset_params['id'] = pkg_name_to_edit + named_route = "dataset.edit" + offset_params["id"] = pkg_name_to_edit else: - named_route = 'dataset.new' - pkg_id = '' + named_route = "dataset.new" + pkg_id = "" if return_url_param: - offset_params['return_to'] = return_url_param + offset_params["return_to"] = return_url_param offset = url_for(named_route, **offset_params) - res = self.app.get(offset, extra_environ=extra_environ) - assert 'Datasets -' in res - fv = res.forms['dataset-edit'] - prefix = '' - fv[prefix + 'name'] = new_name - res = fv.submit('save', status=302, extra_environ=extra_environ) - assert not 'Error' in res, res - redirected_to = dict(res.headers).get('Location') or dict(res.headers)['location'] - expected_redirect_url = expected_redirect.replace('', new_name) - assert redirected_to == expected_redirect_url, \ - 'Redirected to %s but should have been %s' % \ - (redirected_to, expected_redirect_url) + res = app.get(offset, extra_environ=extra_environ) + assert "Datasets -" in res + fv = res.forms["dataset-edit"] + prefix = "" + fv[prefix + "name"] = new_name + res = fv.submit("save", status=302, extra_environ=extra_environ) + assert not "Error" in res, res + redirected_to = ( + dict(res.headers).get("Location") + or dict(res.headers)["location"] + ) + expected_redirect_url = expected_redirect.replace( + "", new_name + ) + assert redirected_to == expected_redirect_url, ( + "Redirected to %s but should have been %s" + % (redirected_to, expected_redirect_url) + ) finally: # revert name change or pkg creation pkg = model.Package.by_name(new_name) @@ -199,411 +222,364 @@ def _check_redirect(self, return_url_param, expected_redirect, pkg.purge() model.repo.commit_and_remove() -class TestReadOnly(TestPackageForm, HtmlCheckMethods): - @classmethod - def setup_class(cls): +class TestReadOnly(TestPackageForm, HtmlCheckMethods): + @pytest.fixture(autouse=True) + def initial_data(self, clean_db, clean_index): CreateTestData.create() - @classmethod - def teardown_class(cls): - model.repo.rebuild_db() - - - def test_read_nonexistentpackage(self): - name = 'anonexistentpackage' - offset = url_for('dataset.read', id=name) - res = self.app.get(offset, status=404) - - def test_read_internal_links(self): - pkg_name = u'link-test', - CreateTestData.create_arbitrary([ - {'name':pkg_name, - 'notes':'Decoy link here: decoy:decoy, real links here: dataset:pkg-1, ' \ - 'tag:tag_1 group:test-group-1 and a multi-word tag: tag:"multi word with punctuation."', - } - ]) - offset = url_for('dataset.read', id=pkg_name) - res = self.app.get(offset) + def test_read_nonexistentpackage(self, app): + name = "anonexistentpackage" + offset = url_for("dataset.read", id=name) + res = app.get(offset, status=404) + + def test_read_internal_links(self, app): + pkg_name = (u"link-test",) + CreateTestData.create_arbitrary( + [ + { + "name": pkg_name, + "notes": "Decoy link here: decoy:decoy, real links here: dataset:pkg-1, " + 'tag:tag_1 group:test-group-1 and a multi-word tag: tag:"multi word with punctuation."', + } + ] + ) + offset = url_for("dataset.read", id=pkg_name) + res = app.get(offset) + def check_link(res, controller, id): - id_in_uri = id.strip('"').replace(' ', '+') # remove quotes and percent-encode spaces - self.check_tag_and_data(res, 'a ', '%s/%s' % (controller, id_in_uri), - '%s:%s' % (controller, id.replace('"', '"'))) - check_link(res, 'dataset', 'pkg-1') - check_link(res, 'group', 'test-group-1') - assert 'decoy' not in res, res + id_in_uri = id.strip('"').replace( + " ", "+" + ) # remove quotes and percent-encode spaces + self.check_tag_and_data( + res, + "a ", + "%s/%s" % (controller, id_in_uri), + "%s:%s" % (controller, id.replace('"', """)), + ) + + check_link(res, "dataset", "pkg-1") + check_link(res, "group", "test-group-1") + assert "decoy" not in res, res assert 'decoy"' not in res, res - def test_read_plugin_hook(self): - plugins.load('test_package_controller_plugin') - plugin = plugins.get_plugin('test_package_controller_plugin') - name = u'annakarenina' - offset = url_for('dataset.read', id=name) - res = self.app.get(offset) + @pytest.mark.ckan_config("ckan.plugins", "test_package_controller_plugin") + @pytest.mark.usefixtures("with_plugins") + def test_read_plugin_hook(self, app): + plugin = plugins.get_plugin("test_package_controller_plugin") + name = u"annakarenina" + offset = url_for("dataset.read", id=name) + res = app.get(offset) - assert plugin.calls['read'] == 1, plugin.calls - assert plugin.calls['after_show'] == 1, plugin.calls - plugins.unload('test_package_controller_plugin') + assert plugin.calls["read"] == 1, plugin.calls + assert plugin.calls["after_show"] == 1, plugin.calls - def test_resource_list(self): + def test_resource_list(self, app): # TODO restore this test. It doesn't make much sense with the # present resource list design. - name = 'annakarenina' - cache_url = 'http://thedatahub.org/test_cache_url.csv' + name = "annakarenina" + cache_url = "http://thedatahub.org/test_cache_url.csv" # add a cache_url to the first resource in the package - context = {'model': model, 'session': model.Session, 'user': 'testsysadmin'} - data = {'id': 'annakarenina'} + context = { + "model": model, + "session": model.Session, + "user": "testsysadmin", + } + data = {"id": "annakarenina"} pkg = get.package_show(context, data) - pkg['resources'][0]['cache_url'] = cache_url + pkg["resources"][0]["cache_url"] = cache_url # FIXME need to pretend to be called by the api - context['api_version'] = 3 + context["api_version"] = 3 update.package_update(context, pkg) # check that the cache url is included on the dataset view page - offset = url_for('dataset.read', id=name) - res = self.app.get(offset) - #assert '[cached]'in res - #assert cache_url in res + offset = url_for("dataset.read", id=name) + res = app.get(offset) + # assert '[cached]'in res + # assert cache_url in res class TestEdit(TestPackageForm): - editpkg_name = u'editpkgtest' - - @classmethod - def setup_class(self): - CreateTestData.create() + editpkg_name = u"editpkgtest" - self._reset_data() - - def setup(self): - if not self.res: - self.res = self.app.get(self.offset,extra_environ=self.extra_environ_admin) - model.Session.remove() - - @classmethod - def _reset_data(self): - model.Session.remove() - model.repo.rebuild_db() + @pytest.fixture(autouse=True) + def initial_data(self, clean_db): CreateTestData.create() CreateTestData.create_arbitrary( - {'name':self.editpkg_name, - 'url':u'editpkgurl.com', - 'tags':[u'mytesttag'], - 'resources':[{'url':u'url escape: & umlaut: \xfc quote: "', - 'description':u'description escape: & umlaut: \xfc quote "', - }], - }) + { + "name": self.editpkg_name, + "url": u"editpkgurl.com", + "tags": [u"mytesttag"], + "resources": [ + { + "url": u'url escape: & umlaut: \xfc quote: "', + "description": u'description escape: & umlaut: \xfc quote "', + } + ], + } + ) self.editpkg = model.Package.by_name(self.editpkg_name) self.pkgid = self.editpkg.id - self.offset = url_for('dataset.edit', id=self.editpkg_name) + self.offset = url_for("dataset.edit", id=self.editpkg_name) self.editpkg = model.Package.by_name(self.editpkg_name) - self.admin = model.User.by_name(u'testsysadmin') + self.admin = model.User.by_name(u"testsysadmin") - self.extra_environ_admin = {'REMOTE_USER': self.admin.name.encode('utf8')} - self.extra_environ_russianfan = {'REMOTE_USER': 'russianfan'} - self.res = None #get's refreshed by setup - model.Session.remove() + self.extra_environ_admin = { + "REMOTE_USER": self.admin.name.encode("utf8") + } + self.extra_environ_russianfan = {"REMOTE_USER": "russianfan"} - @classmethod - def teardown_class(self): - model.repo.rebuild_db() - - def test_edit_2_not_groups(self): + def test_edit_2_not_groups(self, app): # not allowed to edit groups for now - prefix = 'Dataset-%s-' % self.pkgid - fv = self.res.forms['dataset-edit'] - assert not fv.fields.has_key(prefix + 'groups') - - - def test_redirect_after_edit_using_param(self): - return_url = 'http://random.site.com/dataset/?test=param' + prefix = "Dataset-%s-" % self.pkgid + fv = app.get( + self.offset, extra_environ=self.extra_environ_admin + ).forms["dataset-edit"] + assert not fv.fields.has_key(prefix + "groups") + + def test_redirect_after_edit_using_param(self, app): + return_url = "http://random.site.com/dataset/?test=param" # It's useful to know that this url encodes to: # 'http%3A%2F%2Frandom.site.com%2Fdataset%2F%3CNAME%3E%3Ftest%3Dparam' expected_redirect = return_url - self._check_redirect(return_url, expected_redirect, - pkg_name_to_edit=self.editpkg_name, extra_environ=self.extra_environ_admin) - - def test_redirect_after_edit_using_config(self): - return_url = '' # redirect comes from test.ini setting - expected_redirect = config['package_edit_return_url'] - self._check_redirect(return_url, expected_redirect, - pkg_name_to_edit=self.editpkg_name, extra_environ=self.extra_environ_admin) - - def test_edit_plugin_hook(self): - # just the absolute basics - try: - plugins.load('test_package_controller_plugin') - plugin = plugins.get_plugin('test_package_controller_plugin') - res = self.app.get(self.offset, extra_environ=self.extra_environ_admin) - new_name = u'new-name' - new_title = u'New Title' - fv = res.forms['dataset-edit'] - prefix = '' - fv[prefix + 'name'] = new_name - fv[prefix + 'title'] = new_title - res = fv.submit('save', extra_environ=self.extra_environ_admin) - # get redirected ... - assert plugin.calls['edit'] == 1, plugin.calls - plugins.unload('test_package_controller_plugin') - finally: - self._reset_data() - - def test_after_update_plugin_hook(self): - # just the absolute basics - try: - plugins.load('test_package_controller_plugin') - plugin = plugins.get_plugin('test_package_controller_plugin') - res = self.app.get(self.offset, extra_environ=self.extra_environ_admin) - new_name = u'new-name' - new_title = u'New Title' - fv = res.forms['dataset-edit'] - prefix = '' - fv[prefix + 'name'] = new_name - fv[prefix + 'title'] = new_title - res = fv.submit('save', extra_environ=self.extra_environ_admin) - # get redirected ... - assert plugin.calls['after_update'] == 1, plugin.calls - assert plugin.calls['after_create'] == 0, plugin.calls - plugins.unload('test_package_controller_plugin') - finally: - self._reset_data() - - - def test_edit_404(self): - self.offset = url_for('dataset.edit', id='random_name') - self.res = self.app.get(self.offset, status=404) - - - def test_edit_pkg_with_relationships(self): - # 1786 - try: - # add a relationship to a package - pkg = model.Package.by_name(self.editpkg_name) - anna = model.Package.by_name(u'annakarenina') - pkg.add_relationship(u'depends_on', anna) - model.repo.commit_and_remove() - - # check relationship before the test - rels = model.Package.by_name(self.editpkg_name).get_relationships() - assert_equal(str(rels), '[<*PackageRelationship editpkgtest depends_on annakarenina>]') - - # edit the package - self.offset = url_for('dataset.edit', id=self.editpkg_name) - self.res = self.app.get(self.offset, extra_environ=self.extra_environ_admin) - fv = self.res.forms['dataset-edit'] - fv['title'] = u'New Title' - res = fv.submit('save', extra_environ=self.extra_environ_admin) - - # check relationship still exists - rels = model.Package.by_name(self.editpkg_name).get_relationships() - assert_equal(str(rels), '[<*PackageRelationship editpkgtest depends_on annakarenina>]') - - finally: - self._reset_data() - - -class TestDelete(TestPackageForm): - - pkg_names = [] + self._check_redirect( + app, + return_url, + expected_redirect, + pkg_name_to_edit=self.editpkg_name, + extra_environ=self.extra_environ_admin, + ) + + def test_redirect_after_edit_using_config(self, app): + return_url = "" # redirect comes from test.ini setting + expected_redirect = config["package_edit_return_url"] + self._check_redirect( + app, + return_url, + expected_redirect, + pkg_name_to_edit=self.editpkg_name, + extra_environ=self.extra_environ_admin, + ) + + def test_edit_404(self, app): + self.offset = url_for("dataset.edit", id="random_name") + app.get(self.offset, status=404) + + def test_edit_pkg_with_relationships(self, app): + + # add a relationship to a package + pkg = model.Package.by_name(self.editpkg_name) + anna = model.Package.by_name(u"annakarenina") + pkg.add_relationship(u"depends_on", anna) + model.repo.commit_and_remove() - @classmethod - def setup_class(self): - model.repo.init_db() + # check relationship before the test + rels = model.Package.by_name(self.editpkg_name).get_relationships() + assert ( + str(rels) + == "[<*PackageRelationship editpkgtest depends_on annakarenina>]" + ) + + # edit the package + self.offset = url_for("dataset.edit", id=self.editpkg_name) + res = app.get(self.offset, extra_environ=self.extra_environ_admin) + fv = res.forms["dataset-edit"] + fv["title"] = u"New Title" + fv.submit("save", extra_environ=self.extra_environ_admin) + + # check relationship still exists + rels = model.Package.by_name(self.editpkg_name).get_relationships() + assert ( + str(rels) + == "[<*PackageRelationship editpkgtest depends_on annakarenina>]" + ) + + +class TestDelete(object): + @pytest.fixture + def initial_data(self, clean_db): CreateTestData.create() CreateTestData.create_test_user() - self.admin = model.User.by_name(u'testsysadmin') - - self.extra_environ_admin = {'REMOTE_USER': self.admin.name.encode('utf8')} - self.extra_environ_tester = {'REMOTE_USER': 'tester'} - - @classmethod - def teardown_class(self): - self.purge_packages(self.pkg_names) - model.repo.rebuild_db() + @pytest.fixture + def users(self, initial_data): + admin = model.User.by_name(u"testsysadmin") + return { + "admin": {"REMOTE_USER": admin.name.encode("utf8")}, + "tester": {"REMOTE_USER": "tester"}, + } - def test_delete(self): - plugins.load('test_package_controller_plugin') - plugin = plugins.get_plugin('test_package_controller_plugin') + @pytest.mark.ckan_config("ckan.plugins", "test_package_controller_plugin") + @pytest.mark.usefixtures("with_plugins") + def test_delete(self, app, users): + plugin = plugins.get_plugin("test_package_controller_plugin") - offset = url_for('dataset.delete', id='warandpeace') + offset = url_for("dataset.delete", id="warandpeace") # Since organizations, any owned dataset can be edited/deleted by any # user - self.app.post(offset, extra_environ=self.extra_environ_tester) + app.post(offset, extra_environ=users["tester"]) - self.app.post(offset, extra_environ=self.extra_environ_admin) + app.post(offset, extra_environ=users["admin"]) - assert model.Package.get('warandpeace').state == u'deleted' + assert model.Package.get("warandpeace").state == u"deleted" - assert plugin.calls['delete'] == 2 - assert plugin.calls['after_delete'] == 2 - plugins.unload('test_package_controller_plugin') + assert plugin.calls["delete"] == 2 + assert plugin.calls["after_delete"] == 2 -class TestNew(TestPackageForm): - pkg_names = [] - - @classmethod - def setup_class(self): - model.repo.init_db() +class TestNew: + @pytest.fixture + def env_user(self, clean_db): CreateTestData.create_test_user() -# self.admin = model.User.by_name(u'russianfan') - -# self.extra_environ_admin = {'REMOTE_USER': self.admin.name.encode('utf8')} - self.extra_environ_tester = {'REMOTE_USER': 'tester'} - - @classmethod - def teardown_class(self): - self.purge_packages(self.pkg_names) - model.repo.rebuild_db() - - def test_new_plugin_hook(self): - plugins.load('test_package_controller_plugin') - plugin = plugins.get_plugin('test_package_controller_plugin') - offset = url_for('dataset.new') - res = self.app.get(offset, extra_environ=self.extra_environ_tester) - new_name = u'plugged' - fv = res.forms['dataset-edit'] - prefix = '' - fv[prefix + 'name'] = new_name - res = fv.submit('save', extra_environ=self.extra_environ_tester) + + return {"REMOTE_USER": "tester"} + + @pytest.mark.ckan_config("ckan.plugins", "test_package_controller_plugin") + @pytest.mark.usefixtures("with_plugins") + def test_new_plugin_hook(self, env_user, app): + plugin = plugins.get_plugin("test_package_controller_plugin") + offset = url_for("dataset.new") + res = app.get(offset, extra_environ=env_user) + new_name = u"plugged" + fv = res.forms["dataset-edit"] + prefix = "" + fv[prefix + "name"] = new_name + res = fv.submit("save", extra_environ=env_user) # get redirected ... - assert plugin.calls['edit'] == 0, plugin.calls - assert plugin.calls['create'] == 1, plugin.calls - plugins.unload('test_package_controller_plugin') - - def test_after_create_plugin_hook(self): - plugins.load('test_package_controller_plugin') - plugin = plugins.get_plugin('test_package_controller_plugin') - offset = url_for('dataset.new') - res = self.app.get(offset, extra_environ=self.extra_environ_tester) - new_name = u'plugged2' - fv = res.forms['dataset-edit'] - prefix = '' - fv[prefix + 'name'] = new_name - res = fv.submit('save', extra_environ=self.extra_environ_tester) + assert plugin.calls["edit"] == 0, plugin.calls + assert plugin.calls["create"] == 1, plugin.calls + + @pytest.mark.ckan_config("ckan.plugins", "test_package_controller_plugin") + @pytest.mark.usefixtures("with_plugins") + def test_after_create_plugin_hook(self, env_user, app): + plugin = plugins.get_plugin("test_package_controller_plugin") + offset = url_for("dataset.new") + res = app.get(offset, extra_environ=env_user) + new_name = u"plugged2" + fv = res.forms["dataset-edit"] + prefix = "" + fv[prefix + "name"] = new_name + res = fv.submit("save", extra_environ=env_user) # get redirected ... - assert plugin.calls['after_update'] == 0, plugin.calls - assert plugin.calls['after_create'] == 1, plugin.calls + assert plugin.calls["after_update"] == 0, plugin.calls + assert plugin.calls["after_create"] == 1, plugin.calls assert plugin.id_in_dict - plugins.unload('test_package_controller_plugin') - def test_new_indexerror(self): - bad_solr_url = 'http://example.com/badsolrurl' + @pytest.mark.usefixtures("clean_db", "clean_index") + def test_new_indexerror(self, env_user, app): + bad_solr_url = "http://example.com/badsolrurl" solr_url = SolrSettings.get()[0] try: SolrSettings.init(bad_solr_url) - new_package_name = u'new-package-missing-solr' - - offset = url_for('dataset.new') - res = self.app.get(offset, extra_environ=self.extra_environ_tester) - fv = res.forms['dataset-edit'] - fv['name'] = new_package_name + new_package_name = u"new-package-missing-solr" - # this package shouldn't actually be created but - # add it to the list to purge just in case - self.pkg_names.append(new_package_name) + offset = url_for("dataset.new") + res = app.get(offset, extra_environ=env_user) + fv = res.forms["dataset-edit"] + fv["name"] = new_package_name - res = fv.submit('save', status=500, extra_environ=self.extra_environ_tester) - assert 'Unable to add package to search index' in res, res + res = fv.submit("save", status=500, extra_environ=env_user) + assert "Unable to add package to search index" in res, res finally: SolrSettings.init(solr_url) - def test_change_locale(self): - offset = url_for('dataset.new') - res = self.app.get(offset, extra_environ=self.extra_environ_tester) + def test_change_locale(self, env_user, app): + offset = url_for("dataset.new") + res = app.get(offset, extra_environ=env_user) - res = self.app.get('/de/dataset/new', extra_environ=self.extra_environ_tester) - try: - assert 'Datensatz' in res.body, res.body - finally: - self.clear_language_setting() - - -class TestNewPreview(TestPackageBase): - pkgname = u'testpkg' - pkgtitle = u'mytesttitle' + res = app.get("/de/dataset/new", extra_environ=env_user) + assert "Datensatz" in res.body, res.body - @classmethod - def setup_class(self): - pass - model.repo.init_db() - @classmethod - def teardown_class(self): - self.purge_packages([self.pkgname]) - model.repo.rebuild_db() +class TestNonActivePackages: + non_active_name = u"test_nonactive" -class TestNonActivePackages(TestPackageBase): - - @classmethod - def setup_class(self): + @pytest.fixture(autouse=True) + def initial_data(self, clean_db): CreateTestData.create() - self.non_active_name = u'test_nonactive' pkg = model.Package(name=self.non_active_name) model.Session.add(pkg) model.repo.commit_and_remove() - pkg = model.Session.query(model.Package).filter_by(name=self.non_active_name).one() - admin = model.User.by_name(u'joeadmin') + pkg = ( + model.Session.query(model.Package) + .filter_by(name=self.non_active_name) + .one() + ) + admin = model.User.by_name(u"joeadmin") model.repo.commit_and_remove() - pkg = model.Session.query(model.Package).filter_by(name=self.non_active_name).one() - pkg.delete() # becomes non active + pkg = ( + model.Session.query(model.Package) + .filter_by(name=self.non_active_name) + .one() + ) + pkg.delete() # becomes non active model.repo.commit_and_remove() + def test_read(self, app): + offset = url_for("dataset.read", id=self.non_active_name) + res = app.get(offset, status=[404]) - @classmethod - def teardown_class(self): - model.repo.rebuild_db() - - def test_read(self): - offset = url_for('dataset.read', id=self.non_active_name) - res = self.app.get(offset, status=[404]) - + def test_read_as_admin(self, app): + offset = url_for("dataset.read", id=self.non_active_name) + res = app.get( + offset, status=200, extra_environ={"REMOTE_USER": "testsysadmin"} + ) - def test_read_as_admin(self): - offset = url_for('dataset.read', id=self.non_active_name) - res = self.app.get(offset, status=200, extra_environ={'REMOTE_USER':'testsysadmin'}) - - -class TestResourceListing(TestPackageBase): - @classmethod - def setup_class(cls): +class TestResourceListing: + @pytest.fixture(autouse=True) + def initial_data(self, clean_db, app): CreateTestData.create() - cls.tester_user = model.User.by_name(u'tester') - cls.extra_environ_admin = {'REMOTE_USER': 'testsysadmin'} - cls.extra_environ_tester = {'REMOTE_USER': 'tester'} - cls.extra_environ_someone_else = {'REMOTE_USER': 'someone_else'} - - tests.call_action_api(cls.app, 'organization_create', - name='test_org_2', - apikey=cls.tester_user.apikey) - - tests.call_action_api(cls.app, 'package_create', - name='crimeandpunishment', - owner_org='test_org_2', - apikey=cls.tester_user.apikey) - - @classmethod - def teardown_class(cls): - model.repo.rebuild_db() - - def test_resource_listing_premissions_sysadmin(self): + users = {} + tester = model.User.by_name(u"tester") + tests.call_action_api( + app, "organization_create", name="test_org_2", apikey=tester.apikey + ) + + tests.call_action_api( + app, + "package_create", + name="crimeandpunishment", + owner_org="test_org_2", + apikey=tester.apikey, + ) + + @pytest.fixture + def users(self): + return { + "admin": {"REMOTE_USER": "testsysadmin"}, + "tester": {"REMOTE_USER": "tester"}, + "someone_else": {"REMOTE_USER": "someone_else"}, + } + + def test_resource_listing_premissions_sysadmin(self, app, users): # sysadmin 200 - self.app.get('/dataset/resources/crimeandpunishment', extra_environ=self.extra_environ_admin, status=200) + app.get( + "/dataset/resources/crimeandpunishment", + extra_environ=users["admin"], + status=200, + ) - def test_resource_listing_premissions_auth_user(self): + def test_resource_listing_premissions_auth_user(self, app, users): # auth user 200 - self.app.get('/dataset/resources/crimeandpunishment', extra_environ=self.extra_environ_tester, status=200) + app.get( + "/dataset/resources/crimeandpunishment", + extra_environ=users["tester"], + status=200, + ) - def test_resource_listing_premissions_non_auth_user(self): + def test_resource_listing_premissions_non_auth_user(self, app, users): # non auth user 403 - self.app.get('/dataset/resources/crimeandpunishment', extra_environ=self.extra_environ_someone_else, status=[403]) + app.get( + "/dataset/resources/crimeandpunishment", + extra_environ=users["someone_else"], + status=[403], + ) - def test_resource_listing_premissions_not_logged_in(self): + def test_resource_listing_premissions_not_logged_in(self, app): # not logged in 403 - self.app.get('/dataset/resources/crimeandpunishment', status=[403]) + app.get("/dataset/resources/crimeandpunishment", status=[403]) diff --git a/ckan/tests/legacy/functional/test_pagination.py b/ckan/tests/legacy/functional/test_pagination.py index f4fad6005d7..caf434d2704 100644 --- a/ckan/tests/legacy/functional/test_pagination.py +++ b/ckan/tests/legacy/functional/test_pagination.py @@ -2,25 +2,26 @@ import re -from nose.tools import assert_equal +import pytest from ckan.lib.create_test_data import CreateTestData -import ckan.model as model -from ckan.tests.legacy import TestController, url_for, setup_test_search_index +from ckan.tests.legacy import url_for + def scrape_search_results(response, object_type): - assert object_type in ('dataset', 'group_dataset', 'group', 'user') - if object_type != 'group_dataset': - results = re.findall('a href="/%s/%s_(\d\d)' % (object_type, object_type), - str(response)) + assert object_type in ("dataset", "group_dataset", "group", "user") + if object_type != "group_dataset": + exp = 'a href="/%s/%s_(\d\d)' % (object_type, object_type) + results = re.findall(exp, str(response)) else: - object_type = 'dataset' - results = re.findall('href="/%s/%s_(\d\d)' % (object_type, object_type), - str(response)) + object_type = "dataset" + exp = 'href="/%s/%s_(\d\d)' % (object_type, object_type) + results = re.findall(exp, str(response)) return results + def test_scrape_user(): - html = ''' + html = """
  • user_00
  • @@ -29,113 +30,232 @@ def test_scrape_user(): user_01 - ''' - res = scrape_search_results(html, 'user') - assert_equal(res, ['00', '01']) + """ + res = scrape_search_results(html, "user") + assert res == ["00", "01"] + + +@pytest.fixture +def fake_groups(): + # no. entities per page is hardcoded into the controllers, so + # create enough of each here so that we can test pagination + num_groups = 22 + + # CS: nasty_string ignore + groups = [u"group_%s" % str(i).zfill(2) for i in range(0, num_groups)] + CreateTestData.create_arbitrary([], extra_group_names=groups) -class TestPaginationPackage(TestController): - @classmethod - def setup_class(cls): - setup_test_search_index() - model.repo.init_db() - # no. entities per page is hardcoded into the controllers, so - # create enough of each here so that we can test pagination - cls.num_packages_in_large_group = 51 +@pytest.fixture +def fake_users(): + # no. entities per page is hardcoded into the controllers, so + # create enough of each here so that we can test pagination + num_users = 21 - packages = [] - for i in range(cls.num_packages_in_large_group): - packages.append({ + # CS: nasty_string ignore + users = [u"user_%s" % str(i).zfill(2) for i in range(num_users)] + + CreateTestData.create_arbitrary([], extra_user_names=users) + + +@pytest.fixture +def fake_packages(): + # no. entities per page is hardcoded into the controllers, so + # create enough of each here so that we can test pagination + num_packages_in_large_group = 51 + + packages = [] + for i in range(num_packages_in_large_group): + packages.append( + { # CS: nasty_string ignore - 'name': u'dataset_%s' % str(i).zfill(2), - 'groups': u'group_00' - }) - - CreateTestData.create_arbitrary(packages) - - @classmethod - def teardown_class(self): - model.repo.rebuild_db() - - def test_package_search_p1(self): - res = self.app.get(url_for('dataset.search', q='groups:group_00')) - assert 'href="/dataset/?q=groups%3Agroup_00&page=2"' in res - pkg_numbers = scrape_search_results(res, 'dataset') - assert_equal(['50', '49', '48', '47', '46', '45', '44', '43', '42', '41', '40', '39', '38', '37', '36', '35', '34', '33', '32', '31'], pkg_numbers) - - def test_package_search_p2(self): - res = self.app.get(url_for('dataset.search', q='groups:group_00', page=2)) - assert 'href="/dataset/?q=groups%3Agroup_00&page=1"' in res - pkg_numbers = scrape_search_results(res, 'dataset') - assert_equal(['30', '29', '28', '27', '26', '25', '24', '23', '22', '21', '20', '19', '18', '17', '16', '15', '14', '13', '12', '11'], pkg_numbers) - - def test_group_datasets_read_p1(self): - res = self.app.get(url_for(controller='group', action='read', id='group_00')) - assert 'href="/group/group_00?page=2' in res, res - pkg_numbers = scrape_search_results(res, 'group_dataset') - assert_equal(['50', '49', '48', '47', '46', '45', '44', '43', '42', '41', '40', '39', '38', '37', '36', '35', '34', '33', '32', '31'], pkg_numbers) - - def test_group_datasets_read_p2(self): - res = self.app.get(url_for(controller='group', action='read', id='group_00', page=2)) - assert 'href="/group/group_00?page=1' in res, res - pkg_numbers = scrape_search_results(res, 'group_dataset') - assert_equal(['30', '29', '28', '27', '26', '25', '24', '23', '22', '21', '20', '19', '18', '17', '16', '15', '14', '13', '12', '11'], pkg_numbers) - -class TestPaginationGroup(TestController): - @classmethod - def setup_class(cls): - # no. entities per page is hardcoded into the controllers, so - # create enough of each here so that we can test pagination - cls.num_groups = 22 - - # CS: nasty_string ignore - groups = [u'group_%s' % str(i).zfill(2) for i in range(0, cls.num_groups)] - - CreateTestData.create_arbitrary( - [], extra_group_names=groups + "name": u"dataset_%s" % str(i).zfill(2), + "groups": u"group_00", + } ) - @classmethod - def teardown_class(self): - model.repo.rebuild_db() - - def test_group_index(self): - res = self.app.get(url_for('group.index')) - assert 'href="/group/?q=&sort=&page=2"' in res, res - grp_numbers = scrape_search_results(res, 'group') - assert_equal(['00', '01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19'], grp_numbers) - - res = self.app.get(url_for('group.index', page=2)) - assert 'href="/group/?q=&sort=&page=1"' in res - grp_numbers = scrape_search_results(res, 'group') - assert_equal(['20', '21'], grp_numbers) - -class TestPaginationUsers(TestController): - @classmethod - def setup_class(cls): - # no. entities per page is hardcoded into the controllers, so - # create enough of each here so that we can test pagination - cls.num_users = 21 - - # CS: nasty_string ignore - users = [u'user_%s' % str(i).zfill(2) for i in range(cls.num_users)] - - CreateTestData.create_arbitrary( - [], extra_user_names = users, - ) + CreateTestData.create_arbitrary(packages) + + +@pytest.mark.usefixtures("clean_index", "clean_db", "fake_packages") +def test_package_search_p1(app): + res = app.get(url_for("dataset.search", q="groups:group_00")) + assert 'href="/dataset/?q=groups%3Agroup_00&page=2"' in res + pkg_numbers = scrape_search_results(res, "dataset") + assert [ + "50", + "49", + "48", + "47", + "46", + "45", + "44", + "43", + "42", + "41", + "40", + "39", + "38", + "37", + "36", + "35", + "34", + "33", + "32", + "31", + ] == pkg_numbers + + res = app.get(url_for("dataset.search", q="groups:group_00", page=2)) + assert 'href="/dataset/?q=groups%3Agroup_00&page=1"' in res + pkg_numbers = scrape_search_results(res, "dataset") + assert [ + "30", + "29", + "28", + "27", + "26", + "25", + "24", + "23", + "22", + "21", + "20", + "19", + "18", + "17", + "16", + "15", + "14", + "13", + "12", + "11", + ] == pkg_numbers + + +@pytest.mark.usefixtures("clean_index", "clean_db", "fake_packages") +def test_group_datasets_read_p1(app): + res = app.get(url_for(controller="group", action="read", id="group_00")) + assert 'href="/group/group_00?page=2' in res, res + pkg_numbers = scrape_search_results(res, "group_dataset") + assert [ + "50", + "49", + "48", + "47", + "46", + "45", + "44", + "43", + "42", + "41", + "40", + "39", + "38", + "37", + "36", + "35", + "34", + "33", + "32", + "31", + ] == pkg_numbers + + res = app.get( + url_for(controller="group", action="read", id="group_00", page=2) + ) + assert 'href="/group/group_00?page=1' in res, res + pkg_numbers = scrape_search_results(res, "group_dataset") + assert [ + "30", + "29", + "28", + "27", + "26", + "25", + "24", + "23", + "22", + "21", + "20", + "19", + "18", + "17", + "16", + "15", + "14", + "13", + "12", + "11", + ] == pkg_numbers + + +@pytest.mark.usefixtures("clean_index", "clean_db", "fake_groups") +def test_group_index(app): + res = app.get(url_for("group.index")) + assert 'href="/group/?q=&sort=&page=2"' in res, res + grp_numbers = scrape_search_results(res, "group") + assert [ + "00", + "01", + "02", + "03", + "04", + "05", + "06", + "07", + "08", + "09", + "10", + "11", + "12", + "13", + "14", + "15", + "16", + "17", + "18", + "19", + ] == grp_numbers + + res = app.get(url_for("group.index", page=2)) + assert 'href="/group/?q=&sort=&page=1"' in res + grp_numbers = scrape_search_results(res, "group") + assert ["20", "21"] == grp_numbers + - @classmethod - def teardown_class(self): - model.repo.rebuild_db() +@pytest.mark.usefixtures("clean_index", "clean_db", "fake_users") +def test_users_index(app): + res = app.get(url_for("user.index")) + assert 'href="/user/?q=&order_by=name&page=2"' in res + user_numbers = scrape_search_results(res, "user") - def test_users_index(self): - res = self.app.get(url_for('user.index')) - assert 'href="/user/?q=&order_by=name&page=2"' in res - user_numbers = scrape_search_results(res, 'user') - assert_equal(['00', '01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19'], user_numbers) + # this page will list default user, created after db reset, + # that is skipped by our scraper. So, actually there 20 items, + # but only 19 of them have been caught by regExp + assert [ + "00", + "01", + "02", + "03", + "04", + "05", + "06", + "07", + "08", + "09", + "10", + "11", + "12", + "13", + "14", + "15", + "16", + "17", + "18", + ] == user_numbers - res = self.app.get(url_for('user.index', page=2)) - assert 'href="/user/?q=&order_by=name&page=1"' in res - user_numbers = scrape_search_results(res, 'user') - assert_equal(['20'], user_numbers) + res = app.get(url_for("user.index", page=2)) + assert 'href="/user/?q=&order_by=name&page=1"' in res + user_numbers = scrape_search_results(res, "user") + assert ["19", "20"] == user_numbers diff --git a/ckan/tests/legacy/functional/test_preview_interface.py b/ckan/tests/legacy/functional/test_preview_interface.py index 69f92a5f812..0ea429b3df2 100644 --- a/ckan/tests/legacy/functional/test_preview_interface.py +++ b/ckan/tests/legacy/functional/test_preview_interface.py @@ -12,79 +12,93 @@ class TestPluggablePreviews(base.FunctionalTestCase): @classmethod def setup_class(cls): - plugins.load('test_resource_preview', 'test_json_resource_preview') - cls.plugin = plugins.get_plugin('test_resource_preview') + model.repo.rebuild_db() + plugins.load("test_resource_preview", "test_json_resource_preview") + cls.plugin = plugins.get_plugin("test_resource_preview") create_test_data.CreateTestData.create() - cls.package = model.Package.get('annakarenina') + cls.package = model.Package.get("annakarenina") cls.resource = cls.package.resources[0] - cls.url = h.url_for('resource.read', - id=cls.package.name, - resource_id=cls.resource.id) - cls.preview_url = h.url_for('resource.datapreview', + cls.url = h.url_for( + "resource.read", id=cls.package.name, resource_id=cls.resource.id + ) + cls.preview_url = h.url_for( + "resource.datapreview", id=cls.package.id, - resource_id=cls.resource.id) + resource_id=cls.resource.id, + ) @classmethod def teardown_class(cls): - model.repo.rebuild_db() - plugins.unload('test_resource_preview', 'test_json_resource_preview') + + plugins.unload("test_resource_preview", "test_json_resource_preview") def test_hook(self): testpackage = self.package - resource_dict = model_dictize.resource_dictize(self.resource, {'model': model}) + resource_dict = model_dictize.resource_dictize( + self.resource, {"model": model} + ) context = { - 'model': model, - 'session': model.Session, - 'user': model.User.get('testsysadmin').name + "model": model, + "session": model.Session, + "user": model.User.get("testsysadmin").name, } # no preview for type "plain text" preview_url = self.preview_url result = self.app.get(preview_url, status=409) - assert 'No preview' in result.body, result.body + assert "No preview" in result.body, result.body # no preview for type "ümlaut", should not fail - resource_dict['format'] = u'ümlaut' + resource_dict["format"] = u"ümlaut" l.action.update.resource_update(context, resource_dict) result = self.app.get(preview_url, status=409) - assert 'No preview' in result.body, result.body + assert "No preview" in result.body, result.body - resource_dict['format'] = 'mock' + resource_dict["format"] = "mock" l.action.update.resource_update(context, resource_dict) - #there should be a preview for type "json" + # there should be a preview for type "json" preview_url = self.preview_url result = self.app.get(preview_url, status=200) - assert 'mock-preview' in result.body - assert 'mock-preview.js' in result.body + assert "mock-preview" in result.body + assert "mock-preview.js" in result.body - assert self.plugin.calls['can_preview'] == 3, self.plugin.calls - assert self.plugin.calls['setup_template_variables'] == 1, self.plugin.calls - assert self.plugin.calls['preview_templates'] == 1, self.plugin.calls + assert self.plugin.calls["can_preview"] == 3, self.plugin.calls + assert ( + self.plugin.calls["setup_template_variables"] == 1 + ), self.plugin.calls + assert self.plugin.calls["preview_templates"] == 1, self.plugin.calls # test whether the json preview is used - preview_url = h.url_for('resource.datapreview', - id=testpackage.id, - resource_id=testpackage.resources[1].id) + preview_url = h.url_for( + "resource.datapreview", + id=testpackage.id, + resource_id=testpackage.resources[1].id, + ) result = self.app.get(preview_url, status=200) - assert 'mock-json-preview' in result.body - assert 'mock-json-preview.js' in result.body + assert "mock-json-preview" in result.body + assert "mock-json-preview.js" in result.body - assert self.plugin.calls['can_preview'] == 4, self.plugin.calls - assert self.plugin.calls['setup_template_variables'] == 1, self.plugin.calls - assert self.plugin.calls['preview_templates'] == 1, self.plugin.calls + assert self.plugin.calls["can_preview"] == 4, self.plugin.calls + assert ( + self.plugin.calls["setup_template_variables"] == 1 + ), self.plugin.calls + assert self.plugin.calls["preview_templates"] == 1, self.plugin.calls - def test_iframe_is_shown(self): + # def test_iframe_is_shown(self): result = self.app.get(self.url) assert 'data-module="data-viewer"' in result.body, result.body - assert ' 10 views is marked as 'popular'. - # Currently the popular logic is in the templates, will have to move - # that into the logic and add 'popular': True/False to package dicts - # to make this testable. - # Also test that a package with < 10 views is not marked as popular. - # Test what kind of views count towards popularity, recent or total, - # and which don't. - pass - - def test_popular_resource(self): - # TODO - # Test that a resource with > 10 views is marked as 'popular'. - # Currently the popular logic is in the templates, will have to move - # that into the logic and add 'popular': True/False to resource dicts - # to make this testable. - # Also test that a resource with < 10 views is not marked as popular. - # Test what kind of views count towards popularity, recent or total, - # and which don't. - pass - - def test_same_user_visiting_different_pages_on_same_day(self): - # TODO - # Test that if the same user visits multiple pages on the same say, - # each visit gets counted (this should not get throttled) - # (May need to test for packages, resources and pages separately) - pass - - def test_same_user_visiting_same_page_on_different_days(self): - # TODO - # Test that if the same user visits the same page on different days, - # each visit gets counted (this should not get throttled) - # (May need to test for packages, resources and pages separately) - # (Probably need to access the model directly to insert old visits - # into tracking_raw) - pass - - def test_posting_bad_data_to_tracking(self): - # TODO: Test how /_tracking handles unexpected and invalid data. - pass + + response = tests.call_action_api( + app, "package_search", sort="views_total desc" + ) + assert response["count"] == 3 + assert response["sort"] == "views_total desc" + packages = response["results"] + assert packages[0]["name"] == "use_of_weapons" + assert packages[1]["name"] == "the_player_of_games" + assert packages[2]["name"] == "consider_phlebas" def _export_tracking_summary(self): - '''Export CKAN's tracking data and return it. + """Export CKAN's tracking data and return it. This simulates calling `paster tracking export` on the command line. - ''' + """ # FIXME: Can this be done as more of a functional test where we # actually test calling the command and passing the args? By calling # the method directly, we're not testing the command-line parsing. import ckan.lib.cli import ckan.model + f = tempfile.NamedTemporaryFile() - ckan.lib.cli.Tracking('Tracking').export_tracking( - engine=ckan.model.meta.engine, output_filename=f.name) - lines = [line for line in csv.DictReader(open(f.name, 'r'))] + ckan.lib.cli.Tracking("Tracking").export_tracking( + engine=ckan.model.meta.engine, output_filename=f.name + ) + lines = [line for line in csv.DictReader(open(f.name, "r"))] return lines - def test_export(self): - '''`paster tracking export` should export tracking data for all + @pytest.mark.usefixtures("clean_db") + def test_export(self, app): + """`paster tracking export` should export tracking data for all datasets in CSV format. Only dataset tracking data is output to CSV file, not resource or page views. - ''' - app = self._get_app() + """ sysadmin_user, apikey = self._create_sysadmin(app) # Create a couple of packages. package_1 = self._create_package(app, apikey) - package_2 = self._create_package(app, apikey, name='another_package') + package_2 = self._create_package(app, apikey, name="another_package") # View the package_1 three times from different IPs. - url = url_for('dataset.read', - id=package_1['name']) - self._post_to_tracking(app, url, ip='111.222.333.44') - self._post_to_tracking(app, url, ip='111.222.333.55') - self._post_to_tracking(app, url, ip='111.222.333.66') + url = url_for("dataset.read", id=package_1["name"]) + self._post_to_tracking(app, url, ip="111.222.333.44") + self._post_to_tracking(app, url, ip="111.222.333.55") + self._post_to_tracking(app, url, ip="111.222.333.66") # View the package_2 twice from different IPs. - url = url_for('dataset.read', - id=package_2['name']) - self._post_to_tracking(app, url, ip='111.222.333.44') - self._post_to_tracking(app, url, ip='111.222.333.55') + url = url_for("dataset.read", id=package_2["name"]) + self._post_to_tracking(app, url, ip="111.222.333.44") + self._post_to_tracking(app, url, ip="111.222.333.55") self._update_tracking_summary() lines = self._export_tracking_summary() assert len(lines) == 2 package_1_data = lines[0] - assert package_1_data['total views'] == '3' - assert package_1_data['recent views (last 2 weeks)'] == '3' + assert package_1_data["total views"] == "3" + assert package_1_data["recent views (last 2 weeks)"] == "3" package_2_data = lines[1] - assert package_2_data['total views'] == '2' - assert package_2_data['recent views (last 2 weeks)'] == '2' - - def test_tracking_urls_with_languages(self): - # TODO - # Test that posting to eg /de/dataset/foo is counted the same as - # /dataset/foo. - # May need to test for dataset pages, resource previews, resource - # downloads, and other page views separately. - pass - - def test_templates_tracking_enabled(self): - # TODO - # Test the the page view tracking JS is in the templates when - # ckan.tracking_enabled = true. - # Test that the sort by populatiy option is shown on the datasets page. - pass - - def test_templates_tracking_disabled(self): - # TODO - # Test the the page view tracking JS is not in the templates when - # ckan.tracking_enabled = false. - # Test that the sort by populatiy option is not on the datasets page. - pass - - def test_tracking_disabled(self): - # TODO - # Just to make sure, set ckan.tracking_enabled = false and then post - # a bunch of stuff to /_tracking and test that no tracking data is - # recorded. Maybe /_tracking should return something other than 200, - # as well. - # Could also test that 'tracking_summary' is _not_ in package and - # resource dicts from api when tracking is disabled. - pass + assert package_2_data["total views"] == "2" + assert package_2_data["recent views (last 2 weeks)"] == "2" diff --git a/ckan/tests/legacy/functional/test_user.py b/ckan/tests/legacy/functional/test_user.py index d0d7b8a21b2..8d7cee97d34 100644 --- a/ckan/tests/legacy/functional/test_user.py +++ b/ckan/tests/legacy/functional/test_user.py @@ -1,164 +1,162 @@ # encoding: utf-8 -from ckan.lib.helpers import url_for -from ckan.common import config -import hashlib - -from ckan.tests.legacy import CreateTestData -from ckan.tests.legacy.html_check import HtmlCheckMethods -from ckan.tests.legacy.mock_mail_server import SmtpServerHarness +import pytest import ckan.model as model -from base import FunctionalTestCase +from ckan.lib.helpers import url_for from ckan.lib.mailer import create_reset_key +from ckan.tests.legacy import CreateTestData + + +@pytest.fixture(autouse=True) +def initial_data(clean_db): + + # SmtpServerHarness.setup_class() + CreateTestData.create() + + # make 3 changes, authored by annafan + for i in range(3): + pkg = model.Package.by_name(u"annakarenina") + pkg.notes = u"Changed notes %i" % i + model.repo.commit_and_remove() -class TestUserController(FunctionalTestCase, HtmlCheckMethods, SmtpServerHarness): - @classmethod - def setup_class(cls): - smtp_server = config.get('smtp.test_server') - if smtp_server: - host, port = smtp_server.split(':') - port = int(port) + int(str(hashlib.md5(cls.__name__).hexdigest())[0], 16) - config['smtp.test_server'] = '%s:%s' % (host, port) - - SmtpServerHarness.setup_class() - CreateTestData.create() - - # make 3 changes, authored by annafan - for i in range(3): - pkg = model.Package.by_name(u'annakarenina') - pkg.notes = u'Changed notes %i' % i - model.repo.commit_and_remove() - - CreateTestData.create_user('unfinisher', about='') - CreateTestData.create_user('spammer', about=u'mysite test2') - CreateTestData.create_user('spammer2', about=u'spamsite2\r\n') - - @classmethod - def teardown_class(self): - # clear routes 'id' so that next test to run doesn't get it - self.app.get(url_for('user.login', id=None)) - SmtpServerHarness.teardown_class() - model.repo.rebuild_db() - - def teardown(self): - # just ensure we're not logged in - self.app.get('/user/logout') - - def test_user_delete_redirects_to_user_index(self): - user = CreateTestData.create_user('a_user') - url = url_for('user.delete', id=user.id) - extra_environ = {'REMOTE_USER': 'testsysadmin'} - - redirect_url = url_for('user.index', - qualified=True) - res = self.app.post(url, status=302, extra_environ=extra_environ) - - assert user.is_deleted(), user - assert res.headers.get('Location').startswith(redirect_url), res.headers.get('Location') - - def test_user_delete_by_unauthorized_user(self): - user = model.User.by_name(u'annafan') - url = url_for('user.delete', id=user.id) - extra_environ = {'REMOTE_USER': 'an_unauthorized_user'} - - self.app.post(url, status=403, extra_environ=extra_environ) - - def test_user_read_without_id(self): - offset = '/user/' - self.app.get(offset, status=200) - - def test_user_read_me_without_id(self): - offset = '/user/me' - self.app.get(offset, status=302) - - def _get_cookie_headers(self, res): - # For a request response, returns the Set-Cookie header values. - cookie_headers = [] - for key, value in res.headers: - if key == 'Set-Cookie': - cookie_headers.append(value) - return cookie_headers - - def test_apikey(self): - username= u'okfntest' - user = model.User.by_name(u'okfntest') - if not user: - user = model.User(name=u'okfntest') - model.Session.add(user) - model.Session.commit() - model.Session.remove() - - # not logged in - offset = url_for('user.read', id=username) - res = self.app.get(offset) - assert not 'API key' in res - - offset = url_for('user.read', id='okfntest') - res = self.app.get(offset, extra_environ={'REMOTE_USER': 'okfntest'}) - assert user.apikey in res, res - - def test_perform_reset_user_password_link_key_incorrect(self): - CreateTestData.create_user(name='jack', password='TestPassword1') - # Make up a key - i.e. trying to hack this - user = model.User.by_name(u'jack') - offset = url_for(controller='user', - action='perform_reset', - id=user.id, - key='randomness') # i.e. incorrect - res = self.app.get(offset, status=403) # error - - def test_perform_reset_user_password_link_key_missing(self): - CreateTestData.create_user(name='jack', password='TestPassword1') - user = model.User.by_name(u'jack') - offset = url_for(controller='user', - action='perform_reset', - id=user.id) # not, no key specified - res = self.app.get(offset, status=403) # error - - - def test_perform_reset_user_password_link_user_incorrect(self): - # Make up a key - i.e. trying to hack this - user = model.User.by_name(u'jack') - offset = url_for(controller='user', - action='perform_reset', - id='randomness', # i.e. incorrect - key='randomness') - res = self.app.get(offset, status=404) - - def test_perform_reset_activates_pending_user(self): - password = 'TestPassword1' - params = { 'password1': password, 'password2': password } - user = CreateTestData.create_user(name='pending_user', - email='user@email.com') - user.set_pending() - create_reset_key(user) - assert user.is_pending(), user.state - - offset = url_for(controller='user', - action='perform_reset', - id=user.id, - key=user.reset_key) - res = self.app.post(offset, params=params, status=302) - - user = model.User.get(user.id) - assert user.is_active(), user - - def test_perform_reset_doesnt_activate_deleted_user(self): - password = 'TestPassword1' - params = { 'password1': password, 'password2': password } - user = CreateTestData.create_user(name='deleted_user', - email='user@email.com') - user.delete() - create_reset_key(user) - assert user.is_deleted(), user.state - - offset = url_for(controller='user', - action='perform_reset', - id=user.id, - key=user.reset_key) - res = self.app.post(offset, params=params, status=403) - - user = model.User.get(user.id) - assert user.is_deleted(), user + CreateTestData.create_user( + "unfinisher", about='mysite test2', + ) + CreateTestData.create_user( + "spammer2", + about=u'spamsite2\r\n', + ) + + +def test_user_delete_redirects_to_user_index(app): + user = CreateTestData.create_user("a_user") + url = url_for("user.delete", id=user.id) + extra_environ = {"REMOTE_USER": "testsysadmin"} + + redirect_url = url_for("user.index", qualified=True) + res = app.post(url, status=302, extra_environ=extra_environ) + + assert user.is_deleted(), user + assert res.headers.get("Location").startswith( + redirect_url + ), res.headers.get("Location") + + +def test_user_delete_by_unauthorized_user(app): + user = model.User.by_name(u"annafan") + url = url_for("user.delete", id=user.id) + extra_environ = {"REMOTE_USER": "an_unauthorized_user"} + + app.post(url, status=403, extra_environ=extra_environ) + + +def test_user_read_without_id(app): + offset = "/user/" + app.get(offset, status=200) + + +def test_user_read_me_without_id(app): + offset = "/user/me" + app.get(offset, status=302) + + +def test_apikey(app): + username = u"okfntest" + user = model.User.by_name(u"okfntest") + if not user: + user = model.User(name=u"okfntest") + model.Session.add(user) + model.Session.commit() + model.Session.remove() + + # not logged in + offset = url_for("user.read", id=username) + res = app.get(offset) + assert not "API key" in res + + offset = url_for("user.read", id="okfntest") + res = app.get(offset, extra_environ={"REMOTE_USER": "okfntest"}) + assert user.apikey in res, res + + +def test_perform_reset_user_password_link_key_incorrect(app): + CreateTestData.create_user(name="jack", password="TestPassword1") + # Make up a key - i.e. trying to hack this + user = model.User.by_name(u"jack") + offset = url_for( + controller="user", action="perform_reset", id=user.id, key="randomness" + ) # i.e. incorrect + res = app.get(offset, status=403) # error + + +def test_perform_reset_user_password_link_key_missing(app): + CreateTestData.create_user(name="jack", password="TestPassword1") + user = model.User.by_name(u"jack") + offset = url_for( + controller="user", action="perform_reset", id=user.id + ) # not, no key specified + res = app.get(offset, status=403) # error + + +def test_perform_reset_user_password_link_user_incorrect(app): + # Make up a key - i.e. trying to hack this + user = model.User.by_name(u"jack") + offset = url_for( + controller="user", + action="perform_reset", + id="randomness", # i.e. incorrect + key="randomness", + ) + res = app.get(offset, status=404) + + +def test_perform_reset_activates_pending_user(app): + password = "TestPassword1" + params = {"password1": password, "password2": password} + user = CreateTestData.create_user( + name="pending_user", email="user@email.com" + ) + user.set_pending() + create_reset_key(user) + assert user.is_pending(), user.state + + offset = url_for( + controller="user", + action="perform_reset", + id=user.id, + key=user.reset_key, + ) + res = app.post(offset, params=params, status=302) + + user = model.User.get(user.id) + assert user.is_active(), user + + +def test_perform_reset_doesnt_activate_deleted_user(app): + password = "TestPassword1" + params = {"password1": password, "password2": password} + user = CreateTestData.create_user( + name="deleted_user", email="user@email.com" + ) + user.delete() + create_reset_key(user) + assert user.is_deleted(), user.state + + offset = url_for( + controller="user", + action="perform_reset", + id=user.id, + key=user.reset_key, + ) + res = app.post(offset, params=params, status=403) + + user = model.User.get(user.id) + assert user.is_deleted(), user diff --git a/ckan/tests/legacy/html_check.py b/ckan/tests/legacy/html_check.py index 2b173594366..17c0d12300e 100644 --- a/ckan/tests/legacy/html_check.py +++ b/ckan/tests/legacy/html_check.py @@ -11,67 +11,73 @@ class HtmlCheckMethods(object): - '''A collection of methods to check properties of a html page, usually - in the form returned by paster.''' + """A collection of methods to check properties of a html page, usually + in the form returned by paster.""" def named_div(self, div_name, html): 'strips html to just the
    section' the_html = self._get_html_from_res(html) start_div = the_html.find(u'
    ' % div_name) + end_div = the_html.find(u"" % div_name) if end_div == -1: - end_div = the_html.find(u'' % div_name) + end_div = the_html.find(u"" % div_name) div_html = the_html[start_div:end_div] assert div_html return div_html def main_div(self, html): 'strips html to just the
    section' - return self.named_div('main', html) + return self.named_div("main", html) def sidebar(self, html): 'strips html to just the
    section' - return self.named_div('primary', html) + return self.named_div("primary", html) def strip_tags(self, res): - '''Call strip_tags on a TestResponse object to strip any and all HTML and normalise whitespace.''' + """Call strip_tags on a TestResponse object to strip any and all HTML and normalise whitespace.""" if not isinstance(res, string_types): - res = res.body.decode('utf-8') + res = res.body.decode("utf-8") return Stripper().strip(res) def check_named_element(self, html, tag_name, *html_to_find): - '''Searches in the html and returns True if it can find a particular + """Searches in the html and returns True if it can find a particular tag and all its subtags & data which contains all the of the - html_to_find''' - named_element_re = re.compile('(<(%(tag)s\w*).*?(>.*?)' % {'tag':tag_name}) + html_to_find""" + named_element_re = re.compile( + "(<(%(tag)s\w*).*?(>.*?)" % {"tag": tag_name} + ) html_str = self._get_html_from_res(html) - self._check_html(named_element_re, html_str.replace('\n', ''), html_to_find) + self._check_html( + named_element_re, html_str.replace("\n", ""), html_to_find + ) def check_tag_and_data(self, html, *html_to_find): - '''Searches in the html and returns True if it can find a tag and its + """Searches in the html and returns True if it can find a tag and its PC Data immediately following it which contains all the of the - html_to_find''' - if not hasattr(self, 'tag_and_data_re'): - self.tag_and_data_re = re.compile('(<(?P\w*)[^>]*>[^<]*?)') + html_to_find""" + if not hasattr(self, "tag_and_data_re"): + self.tag_and_data_re = re.compile( + "(<(?P\w*)[^>]*>[^<]*?)" + ) # matches " stuff " self._check_html(self.tag_and_data_re, html, html_to_find) def check_tag(self, html, *html_to_find): - '''Searches in the html and returns True if it can find a tag which - contains all the of the html_to_find''' - if not hasattr(self, 'tag_re'): - self.tag_re = re.compile('(<[^>]*>)') + """Searches in the html and returns True if it can find a tag which + contains all the of the html_to_find""" + if not hasattr(self, "tag_re"): + self.tag_re = re.compile("(<[^>]*>)") self._check_html(self.tag_re, html, html_to_find) def _get_html_from_res(self, html): if isinstance(html, paste.fixture.TestResponse): - html_str = html.body.decode('utf8') + html_str = html.body.decode("utf8") elif isinstance(html, text_type): html_str = html elif isinstance(html, str): - html_str = html.decode('utf8') + html_str = html.decode("utf8") elif isinstance(html, webtest.app.TestResponse): - html_str = html.body.decode('utf-8') + html_str = html.body.decode("utf-8") else: raise TypeError return html_str # always unicode @@ -81,29 +87,46 @@ def _check_html(self, regex_compiled, html, html_to_find): partly_matching_tags = [] html_str = self._get_html_from_res(html) for tag in regex_compiled.finditer(html_str): - found_all=True + found_all = True for i, html_bit_to_find in enumerate(html_to_find): - assert isinstance(html_bit_to_find, string_types), html_bit_to_find + assert isinstance( + html_bit_to_find, string_types + ), html_bit_to_find html_bit_to_find = text_type(html_bit_to_find) - find_inverse = html_bit_to_find.startswith('!') - if (find_inverse and html_bit_to_find[1:] in tag.group()) or \ - (not find_inverse and html_bit_to_find not in tag.group()): + find_inverse = html_bit_to_find.startswith("!") + if (find_inverse and html_bit_to_find[1:] in tag.group()) or ( + not find_inverse and html_bit_to_find not in tag.group() + ): found_all = False - if i>0: + if i > 0: partly_matching_tags.append(tag.group()) break if found_all: - return # found it + return # found it # didn't find it if partly_matching_tags: - assert 0, "Couldn't find %s in html. Closest matches were:\n%s" % (', '.join(["'%s'" % html.encode('utf8') for html in html_to_find]), '\n'.join([tag.encode('utf8') for tag in partly_matching_tags])) + assert 0, "Couldn't find %s in html. Closest matches were:\n%s" % ( + ", ".join( + ["'%s'" % html.encode("utf8") for html in html_to_find] + ), + "\n".join( + [tag.encode("utf8") for tag in partly_matching_tags] + ), + ) else: - assert 0, "Couldn't find %s in html. Tags matched were:\n%s" % (', '.join(["'%s'" % html.encode('utf8') for html in html_to_find]), '\n'.join([tag.group() for tag in regex_compiled.finditer(html_str)])) - + assert 0, "Couldn't find %s in html. Tags matched were:\n%s" % ( + ", ".join( + ["'%s'" % html.encode("utf8") for html in html_to_find] + ), + "\n".join( + [tag.group() for tag in regex_compiled.finditer(html_str)] + ), + ) class Stripper(sgmllib.SGMLParser): - '''A simple helper class to cleanly strip HTML from a response.''' + """A simple helper class to cleanly strip HTML from a response.""" + def __init__(self): sgmllib.SGMLParser.__init__(self) @@ -111,7 +134,7 @@ def strip(self, html): self.str = u"" self.feed(html) self.close() - return u' '.join(self.str.split()) + return u" ".join(self.str.split()) def handle_data(self, data): self.str += data diff --git a/ckan/tests/legacy/lib/__init__.py b/ckan/tests/legacy/lib/__init__.py index dae8fcd09c0..c2be6313ad2 100644 --- a/ckan/tests/legacy/lib/__init__.py +++ b/ckan/tests/legacy/lib/__init__.py @@ -1,18 +1,16 @@ # encoding: utf-8 -from nose.tools import assert_equal from six import text_type from ckan import model import ckan.lib.search as search + def check_search_results(terms, expected_count, expected_packages=[]): - query = { - 'q': text_type(terms), - } + query = {"q": text_type(terms)} result = search.query_for(model.Package).run(query) - pkgs = result['results'] - count = result['count'] - assert_equal(count, expected_count) + pkgs = result["results"] + count = result["count"] + assert count == expected_count for expected_pkg in expected_packages: - assert expected_pkg in pkgs, '%s : %s' % (expected_pkg, result) + assert expected_pkg in pkgs, "%s : %s" % (expected_pkg, result) diff --git a/ckan/tests/legacy/lib/test_authenticator.py b/ckan/tests/legacy/lib/test_authenticator.py index 7032ad51245..fd9137caff4 100644 --- a/ckan/tests/legacy/lib/test_authenticator.py +++ b/ckan/tests/legacy/lib/test_authenticator.py @@ -1,7 +1,7 @@ # encoding: utf-8 import ckan - +import pytest import ckan.lib.create_test_data as ctd import ckan.lib.authenticator as authenticator @@ -9,46 +9,42 @@ class TestUsernamePasswordAuthenticator(object): - @classmethod - def setup_class(cls): + @pytest.fixture(autouse=True) + def initial_data(self, clean_db): auth = authenticator.UsernamePasswordAuthenticator() - cls.authenticate = auth.authenticate - - @classmethod - def teardown(cls): - ckan.model.repo.rebuild_db() + self.authenticate = auth.authenticate def test_authenticate_succeeds_if_login_and_password_are_correct(self): environ = {} - password = 'somepass' - user = CreateTestData.create_user('a_user', **{'password': password}) - identity = {'login': user.name, 'password': password} + password = "somepass" + user = CreateTestData.create_user("a_user", **{"password": password}) + identity = {"login": user.name, "password": password} username = self.authenticate(environ, identity) assert username == user.name, username def test_authenticate_fails_if_user_is_deleted(self): environ = {} - password = 'somepass' - user = CreateTestData.create_user('a_user', **{'password': password}) - identity = {'login': user.name, 'password': password} + password = "somepass" + user = CreateTestData.create_user("a_user", **{"password": password}) + identity = {"login": user.name, "password": password} user.delete() assert self.authenticate(environ, identity) is None def test_authenticate_fails_if_user_is_pending(self): environ = {} - password = 'somepass' - user = CreateTestData.create_user('a_user', **{'password': password}) - identity = {'login': user.name, 'password': password} + password = "somepass" + user = CreateTestData.create_user("a_user", **{"password": password}) + identity = {"login": user.name, "password": password} user.set_pending() assert self.authenticate(environ, identity) is None def test_authenticate_fails_if_password_is_wrong(self): environ = {} - user = CreateTestData.create_user('a_user') - identity = {'login': user.name, 'password': 'wrong-password'} + user = CreateTestData.create_user("a_user") + identity = {"login": user.name, "password": "wrong-password"} assert self.authenticate(environ, identity) is None def test_authenticate_fails_if_received_no_login_or_pass(self): @@ -58,15 +54,15 @@ def test_authenticate_fails_if_received_no_login_or_pass(self): def test_authenticate_fails_if_received_just_login(self): environ = {} - identity = {'login': 'some-user'} + identity = {"login": "some-user"} assert self.authenticate(environ, identity) is None def test_authenticate_fails_if_received_just_password(self): environ = {} - identity = {'password': 'some-password'} + identity = {"password": "some-password"} assert self.authenticate(environ, identity) is None def test_authenticate_fails_if_user_doesnt_exist(self): environ = {} - identity = {'login': 'inexistent-user'} + identity = {"login": "inexistent-user"} assert self.authenticate(environ, identity) is None diff --git a/ckan/tests/legacy/lib/test_cli.py b/ckan/tests/legacy/lib/test_cli.py index f66c842f9a4..2440050a189 100644 --- a/ckan/tests/legacy/lib/test_cli.py +++ b/ckan/tests/legacy/lib/test_cli.py @@ -3,27 +3,24 @@ from ckan import model from ckan.lib.cli import SearchIndexCommand from ckan.lib.create_test_data import CreateTestData +import pytest +from ckan.lib.search import index_for, query_for -from ckan.lib.search import index_for,query_for - -class FakeOptions(): - def __init__(self,**kwargs): +class FakeOptions: + def __init__(self, **kwargs): for key in kwargs: - setattr(self,key,kwargs[key]) + setattr(self, key, kwargs[key]) + class TestSearch: - @classmethod - def setup_class(cls): - cls.search = SearchIndexCommand('search-index') - cls.index = index_for(model.Package) - cls.query = query_for(model.Package) + @pytest.fixture(autouse=True) + def initial_data(self, clean_db): + self.search = SearchIndexCommand("search-index") + self.index = index_for(model.Package) + self.query = query_for(model.Package) CreateTestData.create() - @classmethod - def teardown_class(cls): - model.repo.rebuild_db() - def test_clear_and_rebuild_index(self): # Clear index @@ -31,38 +28,52 @@ def test_clear_and_rebuild_index(self): self.search.options = FakeOptions() self.search.clear() - self.query.run({'q':'*:*'}) + self.query.run({"q": "*:*"}) assert self.query.count == 0 # Rebuild index self.search.args = () - self.search.options = FakeOptions(only_missing=False, force=False, refresh=False, commit_each=False, quiet=False) + self.search.options = FakeOptions( + only_missing=False, + force=False, + refresh=False, + commit_each=False, + quiet=False, + ) self.search.rebuild() - pkg_count = model.Session.query(model.Package).filter(model.Package.state==u'active').count() + pkg_count = ( + model.Session.query(model.Package) + .filter(model.Package.state == u"active") + .count() + ) - self.query.run({'q':'*:*'}) + self.query.run({"q": "*:*"}) assert self.query.count == pkg_count - def test_clear_and_rebuild_only_one(self): - - pkg_count = model.Session.query(model.Package).filter(model.Package.state==u'active').count() + pkg_count = ( + model.Session.query(model.Package) + .filter(model.Package.state == u"active") + .count() + ) # Clear index for annakarenina - self.search.args = ('clear annakarenina').split() + self.search.args = ("clear annakarenina").split() self.search.options = FakeOptions() self.search.clear() - self.query.run({'q':'*:*'}) + self.query.run({"q": "*:*"}) assert self.query.count == pkg_count - 1 # Rebuild index for annakarenina - self.search.args = ('rebuild annakarenina').split() - self.search.options = FakeOptions(only_missing=False,force=False,refresh=False,commit_each=False) + self.search.args = ("rebuild annakarenina").split() + self.search.options = FakeOptions( + only_missing=False, force=False, refresh=False, commit_each=False + ) self.search.rebuild() - self.query.run({'q':'*:*'}) + self.query.run({"q": "*:*"}) assert self.query.count == pkg_count diff --git a/ckan/tests/legacy/lib/test_dictization.py b/ckan/tests/legacy/lib/test_dictization.py index 08dfe296877..bbf7042d4a4 100644 --- a/ckan/tests/legacy/lib/test_dictization.py +++ b/ckan/tests/legacy/lib/test_dictization.py @@ -1,328 +1,268 @@ # encoding: utf-8 -from __future__ import print_function -from nose.tools import assert_equal, assert_not_in, assert_in -from pprint import pprint, pformat +import pytest + +from pprint import pformat from difflib import unified_diff import ckan.lib.search as search from ckan.lib.create_test_data import CreateTestData from ckan import model -from ckan.lib.dictization import (table_dictize, - table_dict_save) - -from ckan.lib.dictization.model_dictize import (package_dictize, - resource_dictize, - package_to_api1, - package_to_api2, - user_dictize, - ) -from ckan.lib.dictization.model_save import (package_dict_save, - resource_dict_save, - activity_dict_save, - package_api_to_dict, - group_api_to_dict, - package_tag_list_save, - ) +from ckan.lib.dictization import table_dictize, table_dict_save + +from ckan.lib.dictization.model_dictize import ( + package_dictize, + resource_dictize, + package_to_api1, + package_to_api2, + user_dictize, +) +from ckan.lib.dictization.model_save import ( + package_dict_save, + resource_dict_save, + activity_dict_save, + package_api_to_dict, + group_api_to_dict, + package_tag_list_save, +) import ckan.logic.action.get class TestBasicDictize: - @classmethod - def setup_class(cls): + @pytest.fixture(autouse=True) + def initial_data(self): # clean the db so we can run these tests on their own model.repo.rebuild_db() search.clear_all() CreateTestData.create() - - cls.package_expected = { - u'author': None, - u'author_email': None, - u'creator_user_id': None, - 'extras': [ - # extras are no longer revisioned so we get the latest version - {'key': u'david', 'state': u'active', 'value': u'new_value'}, - {'key': u'genre', 'state': u'active', 'value': u'new_value'}, - {'key': u'original media', 'state': u'active', - 'value': u'book'} - ], - 'groups': [{ - u'name': u'david', - u'capacity': u'public', - u'image_url': u'', - u'image_display_url': u'', - u'description': u'These are books that David likes.', - u'display_name': u"Dave's books", - u'type': u'group', - u'state': u'active', - u'is_organization': False, - u'title': u"Dave's books", - u"approval_status": u"approved"}, - { - u'name': u'roger', - u'capacity': u'public', - u'description': u'Roger likes these books.', - u'image_url': u'', - 'image_display_url': u'', - 'display_name': u"Roger's books", - u'type': u'group', - u'state': u'active', - u'is_organization': False, - u'title': u"Roger's books", - u"approval_status": u"approved"}], - 'isopen': True, - u'license_id': u'other-open', - 'license_title': u'Other (Open)', - 'organization': None, - u'owner_org': None, - u'maintainer': None, - u'maintainer_email': None, - u'name': u'annakarenina', - u'notes': u'Some test notes\n\n### A 3rd level heading\n\n**Some bolded text.**\n\n*Some italicized text.*\n\nForeign characters:\nu with umlaut \xfc\n66-style quote \u201c\nforeign word: th\xfcmb\n\nNeeds escaping:\nleft arrow <\n\n\n\n', - 'num_resources': 2, - 'num_tags': 3, - u'private': False, - 'relationships_as_object': [], - 'relationships_as_subject': [], - 'resources': [{u'alt_url': u'alt123', - u'cache_last_updated': None, - u'cache_url': None, - u'description': u'Full text. Needs escaping: " Umlaut: \xfc', - u'format': u'plain text', - u'hash': u'abc123', - u'last_modified': None, - u'mimetype': None, - u'mimetype_inner': None, - u'name': None, - u'position': 0, - u'resource_type': None, - u'size': None, - u'size_extra': u'123', - u'url_type': None, - u'state': u'active', - u'url': u'http://datahub.io/download/x=1&y=2',}, - {u'alt_url': u'alt345', - u'cache_last_updated': None, - u'cache_url': None, - u'description': u'Index of the novel', - u'format': u'JSON', - u'hash': u'def456', - u'last_modified': None, - u'mimetype': None, - u'mimetype_inner': None, - u'name': None, - u'position': 1, - u'resource_type': None, - u'url_type': None, - u'size': None, - u'size_extra': u'345', - u'state': u'active', - u'url': u'http://datahub.io/index.json'}], - u'state': u'active', - 'tags': [{u'name': u'Flexible \u30a1', - 'display_name': u'Flexible \u30a1', - u'state': u'active'}, - {'display_name': u'russian', - u'name': u'russian', - u'state': u'active'}, - {'display_name': u'tolstoy', - u'name': u'tolstoy', - u'state': u'active'}], - u'title': u'A Novel By Tolstoy', - u'type': u'dataset', - u'url': u'http://datahub.io', - u'version': u'0.7a', - } - - - @classmethod - def teardown_class(cls): + yield model.repo.rebuild_db() model.Session.remove() def remove_changable_columns(self, dict, remove_package_id=False): - ids_to_keep = ['license_id', 'creator_user_id'] + ids_to_keep = ["license_id", "creator_user_id"] if not remove_package_id: - ids_to_keep.append('package_id') + ids_to_keep.append("package_id") for key, value in dict.items(): - if key.endswith('id') and key not in ids_to_keep: + if key.endswith("id") and key not in ids_to_keep: dict.pop(key) - if key == 'created': + if key == "created": dict.pop(key) - if 'timestamp' in key: + if "timestamp" in key: dict.pop(key) - if key in ['metadata_created','metadata_modified']: + if key in ["metadata_created", "metadata_modified"]: dict.pop(key) if isinstance(value, list): for new_dict in value: - self.remove_changable_columns(new_dict, - key in ['resources', 'extras'] or remove_package_id) + self.remove_changable_columns( + new_dict, + key in ["resources", "extras"] or remove_package_id, + ) return dict def test_03_package_to_api1(self): - context = {"model": model, - "session": model.Session} + context = {"model": model, "session": model.Session} - pkg = model.Session.query(model.Package).filter_by(name='annakarenina').first() + pkg = ( + model.Session.query(model.Package) + .filter_by(name="annakarenina") + .first() + ) - pprint(package_to_api1(pkg, context)) - pprint(pkg.as_dict()) asdict = pkg.as_dict() - asdict['download_url'] = asdict['resources'][0]['url'] - asdict['license_title'] = u'Other (Open)' - asdict['num_tags'] = 3 - asdict['num_resources'] = 2 + asdict["download_url"] = asdict["resources"][0]["url"] + asdict["license_title"] = u"Other (Open)" + asdict["num_tags"] = 3 + asdict["num_resources"] = 2 dictize = package_to_api1(pkg, context) # the is_dict method doesn't care about organizations - del dictize['organization'] + del dictize["organization"] assert dictize == asdict def test_04_package_to_api1_with_relationship(self): - context = {"model": model, - "session": model.Session} + context = {"model": model, "session": model.Session} create = CreateTestData create.create_family_test_data() - pkg = model.Session.query(model.Package).filter_by(name='homer').one() + pkg = model.Session.query(model.Package).filter_by(name="homer").one() as_dict = pkg.as_dict() - as_dict['license_title'] = None - as_dict['num_tags'] = 0 - as_dict['num_resources'] = 0 + as_dict["license_title"] = None + as_dict["num_tags"] = 0 + as_dict["num_resources"] = 0 dictize = package_to_api1(pkg, context) - as_dict["relationships"].sort(key=lambda x:x.items()) - dictize["relationships"].sort(key=lambda x:x.items()) + as_dict["relationships"].sort(key=lambda x: x.items()) + dictize["relationships"].sort(key=lambda x: x.items()) # the is_dict method doesn't care about organizations - del dictize['organization'] + del dictize["organization"] as_dict_string = pformat(as_dict) dictize_string = pformat(dictize) - print(as_dict_string) - print(dictize_string) - assert as_dict == dictize, "\n".join(unified_diff(as_dict_string.split("\n"), dictize_string.split("\n"))) + assert as_dict == dictize, "\n".join( + unified_diff( + as_dict_string.split("\n"), dictize_string.split("\n") + ) + ) def test_05_package_to_api2(self): - context = {"model": model, - "session": model.Session} + context = {"model": model, "session": model.Session} - pkg = model.Session.query(model.Package).filter_by(name='annakarenina').first() + pkg = ( + model.Session.query(model.Package) + .filter_by(name="annakarenina") + .first() + ) - as_dict = pkg.as_dict(ref_package_by='id', ref_group_by='id') + as_dict = pkg.as_dict(ref_package_by="id", ref_group_by="id") dictize = package_to_api2(pkg, context) as_dict_string = pformat(as_dict) dictize_string = pformat(dictize) - print(as_dict_string) - print(dictize_string) - assert package_to_api2(pkg, context) == dictize, "\n".join(unified_diff(as_dict_string.split("\n"), dictize_string.split("\n"))) + assert package_to_api2(pkg, context) == dictize, "\n".join( + unified_diff( + as_dict_string.split("\n"), dictize_string.split("\n") + ) + ) def test_06_package_to_api2_with_relationship(self): - context = {"model": model, - "session": model.Session} + context = {"model": model, "session": model.Session} + + create = CreateTestData + create.create_family_test_data() - pkg = model.Session.query(model.Package).filter_by(name='homer').one() + pkg = model.Session.query(model.Package).filter_by(name="homer").one() - as_dict = pkg.as_dict(ref_package_by='id', ref_group_by='id') - as_dict['license_title'] = None - as_dict['num_tags'] = 0 - as_dict['num_resources'] = 0 + as_dict = pkg.as_dict(ref_package_by="id", ref_group_by="id") + as_dict["license_title"] = None + as_dict["num_tags"] = 0 + as_dict["num_resources"] = 0 dictize = package_to_api2(pkg, context) - as_dict["relationships"].sort(key=lambda x:x.items()) - dictize["relationships"].sort(key=lambda x:x.items()) + as_dict["relationships"].sort(key=lambda x: x.items()) + dictize["relationships"].sort(key=lambda x: x.items()) # the is_dict method doesn't care about organizations - del dictize['organization'] + del dictize["organization"] as_dict_string = pformat(as_dict) dictize_string = pformat(dictize) - print(as_dict_string) - print(dictize_string) - assert as_dict == dictize, "\n".join(unified_diff(as_dict_string.split("\n"), dictize_string.split("\n"))) + assert as_dict == dictize, "\n".join( + unified_diff( + as_dict_string.split("\n"), dictize_string.split("\n") + ) + ) def test_07_table_simple_save(self): - context = {"model": model, - "session": model.Session} + context = {"model": model, "session": model.Session} - anna1 = model.Session.query(model.Package).filter_by(name='annakarenina').one() + anna1 = ( + model.Session.query(model.Package) + .filter_by(name="annakarenina") + .one() + ) - anna_dictized = self.remove_changable_columns(table_dictize(anna1, context)) + anna_dictized = self.remove_changable_columns( + table_dictize(anna1, context) + ) - anna_dictized["name"] = 'annakarenina2' + anna_dictized["name"] = "annakarenina2" table_dict_save(anna_dictized, model.Package, context) model.Session.commit() - pkg = model.Session.query(model.Package).filter_by(name='annakarenina2').one() + pkg = ( + model.Session.query(model.Package) + .filter_by(name="annakarenina2") + .one() + ) - assert self.remove_changable_columns(table_dictize(pkg, context)) == anna_dictized, self.remove_changable_columns(table_dictize(pkg, context)) + assert ( + self.remove_changable_columns(table_dictize(pkg, context)) + == anna_dictized + ), self.remove_changable_columns(table_dictize(pkg, context)) def test_08_package_save(self): - context = {"model": model, - "user": 'testsysadmin', - "session": model.Session} + context = { + "model": model, + "user": "testsysadmin", + "session": model.Session, + } - anna1 = model.Session.query(model.Package).filter_by(name='annakarenina').one() + anna1 = ( + model.Session.query(model.Package) + .filter_by(name="annakarenina") + .one() + ) - anna_dictized = self.remove_changable_columns(package_dictize(anna1, context)) + anna_dictized = self.remove_changable_columns( + package_dictize(anna1, context) + ) - anna_dictized["name"] = u'annakarenina3' + anna_dictized["name"] = u"annakarenina3" package_dict_save(anna_dictized, context) model.Session.commit() # Re-clean anna_dictized - anna_dictized = self.remove_changable_columns(anna_dictized) + anna_dictized = self.remove_changable_columns(anna_dictized) - pkg = model.Session.query(model.Package).filter_by(name='annakarenina3').one() + pkg = ( + model.Session.query(model.Package) + .filter_by(name="annakarenina3") + .one() + ) - package_dictized = self.remove_changable_columns(package_dictize(pkg, context)) + package_dictized = self.remove_changable_columns( + package_dictize(pkg, context) + ) anna_original = pformat(anna_dictized) anna_after_save = pformat(package_dictized) - assert package_dictized == anna_dictized,\ - "\n".join(unified_diff(anna_original.split("\n"), anna_after_save.split("\n"))) + assert package_dictized == anna_dictized, "\n".join( + unified_diff( + anna_original.split("\n"), anna_after_save.split("\n") + ) + ) def test_14_resource_no_id(self): - context = {"model": model, - "session": model.Session} + context = {"model": model, "session": model.Session} model.Session.commit() new_resource = { - 'mimetype': None, - u'alt_url': u'empty resource group id', - 'hash': u'abc123', - 'description': u'Full text. Needs escaping: " Umlaut: \xfc', - 'format': u'plain text', - 'url': u'http://test_new', - 'cache_url': None, - 'cache_last_updated': None, - 'state': u'active', - 'mimetype_inner': None, - 'url_type': None, - 'last_modified': None, - 'position': 0, - 'size': None, - 'size_extra': u'123', - 'resource_type': None, - 'name': None, - 'package_id':'' # Just so we can save + "mimetype": None, + u"alt_url": u"empty resource group id", + "hash": u"abc123", + "description": u'Full text. Needs escaping: " Umlaut: \xfc', + "format": u"plain text", + "url": u"http://test_new", + "cache_url": None, + "cache_last_updated": None, + "state": u"active", + "mimetype_inner": None, + "url_type": None, + "last_modified": None, + "position": 0, + "size": None, + "size_extra": u"123", + "resource_type": None, + "name": None, + "package_id": "", # Just so we can save } resource_dict_save(new_resource, context) @@ -330,156 +270,169 @@ def test_14_resource_no_id(self): model.Session.remove() # Remove the package id - del new_resource['package_id'] + del new_resource["package_id"] - res = model.Session.query(model.Resource).filter_by(url=u'http://test_new').one() + res = ( + model.Session.query(model.Resource) + .filter_by(url=u"http://test_new") + .one() + ) - res_dictized = self.remove_changable_columns(resource_dictize(res, context), True) + res_dictized = self.remove_changable_columns( + resource_dictize(res, context), True + ) assert res_dictized == new_resource, res_dictized def test_15_api_to_dictize(self): - context = {"model": model, - 'api_version': 1, - "session": model.Session} + context = {"model": model, "api_version": 1, "session": model.Session} api_data = { - 'name' : u'testpkg', - 'title': u'Some Title', - 'url': u'http://blahblahblah.mydomain', - 'resources': [ { - u'url':u'http://blah.com/file2.xml', - u'format':u'xml', - u'description':u'Second file', - u'hash':u'def123', - u'alt_url':u'alt_url', - u'size':u'200', - }, + "name": u"testpkg", + "title": u"Some Title", + "url": u"http://blahblahblah.mydomain", + "resources": [ { - u'url':u'http://blah.com/file.xml', - u'format':u'xml', - u'description':u'Main file', - u'hash':u'abc123', - u'alt_url':u'alt_url', - u'size':u'200', - }, + u"url": u"http://blah.com/file2.xml", + u"format": u"xml", + u"description": u"Second file", + u"hash": u"def123", + u"alt_url": u"alt_url", + u"size": u"200", + }, + { + u"url": u"http://blah.com/file.xml", + u"format": u"xml", + u"description": u"Main file", + u"hash": u"abc123", + u"alt_url": u"alt_url", + u"size": u"200", + }, ], - 'tags': u'russion novel', - 'license_id': u'gpl-3.0', - 'extras': { - 'genre' : u'horror', - 'media' : u'dvd', - }, + "tags": u"russion novel", + "license_id": u"gpl-3.0", + "extras": {"genre": u"horror", "media": u"dvd"}, } dictized = package_api_to_dict(api_data, context) - assert dictized == {'extras': [{'key': 'genre', 'value': u'horror'}, - {'key': 'media', 'value': u'dvd'}], - 'license_id': u'gpl-3.0', - 'name': u'testpkg', - 'resources': [{u'alt_url': u'alt_url', - u'description': u'Second file', - u'size': u'200', - u'format': u'xml', - u'hash': u'def123', - u'url': u'http://blah.com/file2.xml'}, - {u'alt_url': u'alt_url', - u'description': u'Main file', - u'size': u'200', - u'format': u'xml', - u'hash': u'abc123', - u'url': u'http://blah.com/file.xml'}], - 'tags': [{'name': u'russion'}, {'name': u'novel'}], - 'title': u'Some Title', - 'url': u'http://blahblahblah.mydomain'} + assert dictized == { + "extras": [ + {"key": "genre", "value": u"horror"}, + {"key": "media", "value": u"dvd"}, + ], + "license_id": u"gpl-3.0", + "name": u"testpkg", + "resources": [ + { + u"alt_url": u"alt_url", + u"description": u"Second file", + u"size": u"200", + u"format": u"xml", + u"hash": u"def123", + u"url": u"http://blah.com/file2.xml", + }, + { + u"alt_url": u"alt_url", + u"description": u"Main file", + u"size": u"200", + u"format": u"xml", + u"hash": u"abc123", + u"url": u"http://blah.com/file.xml", + }, + ], + "tags": [{"name": u"russion"}, {"name": u"novel"}], + "title": u"Some Title", + "url": u"http://blahblahblah.mydomain", + } package_dict_save(dictized, context) model.Session.commit() model.Session.remove() - pkg = model.Session.query(model.Package).filter_by(name=u'testpkg').one() - - package_dictized = self.remove_changable_columns(package_dictize(pkg, context)) - + pkg = ( + model.Session.query(model.Package).filter_by(name=u"testpkg").one() + ) + package_dictized = self.remove_changable_columns( + package_dictize(pkg, context) + ) def test_17_group_apis_to_dict(self): - context = {"model": model, - "session": model.Session} + context = {"model": model, "session": model.Session} api_group = { - 'name' : u'testgroup', - 'title' : u'Some Group Title', - 'description' : u'Great group!', - 'packages' : [u'annakarenina', u'warandpeace'], + "name": u"testgroup", + "title": u"Some Group Title", + "description": u"Great group!", + "packages": [u"annakarenina", u"warandpeace"], } - - assert group_api_to_dict(api_group, context) == {'description': u'Great group!', - 'name': u'testgroup', - 'packages': [{'id': u'annakarenina'}, {'id': u'warandpeace'}], - 'title': u'Some Group Title'}, pformat(group_api_to_dict(api_group, context)) + assert group_api_to_dict(api_group, context) == { + "description": u"Great group!", + "name": u"testgroup", + "packages": [{"id": u"annakarenina"}, {"id": u"warandpeace"}], + "title": u"Some Group Title", + }, pformat(group_api_to_dict(api_group, context)) def test_18_package_tag_list_save(self): - name = u'testpkg18' - context = {'model': model, - 'session': model.Session} - pkg_dict = {'name': name} + name = u"testpkg18" + context = {"model": model, "session": model.Session} + pkg_dict = {"name": name} package = table_dict_save(pkg_dict, model.Package, context) - tag_dicts = [{'name': 'tag1'}, {'name': 'tag2'}] + tag_dicts = [{"name": "tag1"}, {"name": "tag2"}] package_tag_list_save(tag_dicts, package, context) model.repo.commit_and_remove() pkg = model.Package.by_name(name) - assert_equal(set([tag.name for tag in pkg.get_tags()]), - set(('tag1', 'tag2'))) + assert set([tag.name for tag in pkg.get_tags()]) == set( + ("tag1", "tag2") + ) def test_19_package_tag_list_save_duplicates(self): - name = u'testpkg19' - context = {'model': model, - 'session': model.Session} - pkg_dict = {'name': name} + name = u"testpkg19" + context = {"model": model, "session": model.Session} + pkg_dict = {"name": name} package = table_dict_save(pkg_dict, model.Package, context) - tag_dicts = [{'name': 'tag1'}, {'name': 'tag1'}] # duplicate + tag_dicts = [{"name": "tag1"}, {"name": "tag1"}] # duplicate package_tag_list_save(tag_dicts, package, context) model.repo.commit_and_remove() pkg = model.Package.by_name(name) - assert_equal(set([tag.name for tag in pkg.get_tags()]), set(('tag1',))) + assert set([tag.name for tag in pkg.get_tags()]) == set(("tag1",)) def test_20_activity_save(self): # Add a new Activity object to the database by passing a dict to # activity_dict_save() context = {"model": model, "session": model.Session} - user = model.User.by_name(u'tester') + user = model.User.by_name(u"tester") sent = { - 'user_id': user.id, - 'object_id': user.id, - 'activity_type': 'changed user' - } + "user_id": user.id, + "object_id": user.id, + "activity_type": "changed user", + } activity_dict_save(sent, context) model.Session.commit() # Retrieve the newest Activity object from the database, check that its # attributes match those of the dict we saved. - got = ckan.logic.action.get.user_activity_list(context, - {'id': user.id})[0] - assert got['user_id'] == sent['user_id'] - assert got['object_id'] == sent['object_id'] - assert got['activity_type'] == sent['activity_type'] + got = ckan.logic.action.get.user_activity_list( + context, {"id": user.id} + )[0] + assert got["user_id"] == sent["user_id"] + assert got["object_id"] == sent["object_id"] + assert got["activity_type"] == sent["activity_type"] # The activity object should also have an ID and timestamp. - assert got['id'] - assert got['timestamp'] - + assert got["id"] + assert got["timestamp"] def test_21_package_dictization_with_deleted_group(self): """ @@ -487,9 +440,9 @@ def test_21_package_dictization_with_deleted_group(self): been removed from. """ # Create a new dataset and 2 new groups - pkg = model.Package(name='testing-deleted-groups') - group_1 = model.Group(name='test-group-1') - group_2 = model.Group(name='test-group-2') + pkg = model.Package(name="testing-deleted-groups") + group_1 = model.Group(name="test-group-1") + group_2 = model.Group(name="test-group-2") model.Session.add(pkg) model.Session.add(group_1) model.Session.add(group_2) @@ -497,123 +450,114 @@ def test_21_package_dictization_with_deleted_group(self): # Add the dataset to group_1, and signal that the dataset used # to be a member of group_2 by setting its membership state to 'deleted' - membership_1 = model.Member(table_id = pkg.id, - table_name = 'package', - group = group_1, - group_id = group_1.id, - state = 'active') - - membership_2 = model.Member(table_id = pkg.id, - table_name = 'package', - group = group_2, - group_id = group_2.id, - state = 'deleted') + membership_1 = model.Member( + table_id=pkg.id, + table_name="package", + group=group_1, + group_id=group_1.id, + state="active", + ) + + membership_2 = model.Member( + table_id=pkg.id, + table_name="package", + group=group_2, + group_id=group_2.id, + state="deleted", + ) model.Session.add(membership_1) model.Session.add(membership_2) model.repo.commit() # Dictize the dataset - context = {"model": model, - "session": model.Session} + context = {"model": model, "session": model.Session} result = package_dictize(pkg, context) self.remove_changable_columns(result) - assert_not_in('test-group-2', [ g['name'] for g in result['groups'] ]) - assert_in('test-group-1', [ g['name'] for g in result['groups'] ]) + assert "test-group-2" not in [g["name"] for g in result["groups"]] + assert "test-group-1" in [g["name"] for g in result["groups"]] def test_22_user_dictize_as_sysadmin(self): - '''Sysadmins should be allowed to see certain sensitive data.''' + """Sysadmins should be allowed to see certain sensitive data.""" context = { - 'model': model, - 'session': model.Session, - 'user': 'testsysadmin', + "model": model, + "session": model.Session, + "user": "testsysadmin", } - user = model.User.by_name('tester') + user = model.User.by_name("tester") user_dict = user_dictize(user, context) # Check some of the non-sensitive data - assert 'name' in user_dict - assert 'about' in user_dict + assert "name" in user_dict + assert "about" in user_dict # Check sensitive data is available - assert 'apikey' in user_dict - assert 'email' in user_dict + assert "apikey" in user_dict + assert "email" in user_dict # Passwords and reset keys should never be available - assert 'password' not in user_dict - assert 'reset_key' not in user_dict + assert "password" not in user_dict + assert "reset_key" not in user_dict def test_23_user_dictize_as_same_user(self): - '''User should be able to see their own sensitive data.''' - context = { - 'model': model, - 'session': model.Session, - 'user': 'tester', - } + """User should be able to see their own sensitive data.""" + context = {"model": model, "session": model.Session, "user": "tester"} - user = model.User.by_name('tester') + user = model.User.by_name("tester") user_dict = user_dictize(user, context) # Check some of the non-sensitive data - assert 'name' in user_dict - assert 'about' in user_dict + assert "name" in user_dict + assert "about" in user_dict # Check sensitive data is available - assert 'apikey' in user_dict - assert 'email' in user_dict + assert "apikey" in user_dict + assert "email" in user_dict # Passwords and reset keys should never be available - assert 'password' not in user_dict - assert 'reset_key' not in user_dict + assert "password" not in user_dict + assert "reset_key" not in user_dict def test_24_user_dictize_as_other_user(self): - '''User should not be able to see other's sensitive data.''' - context = { - 'model': model, - 'session': model.Session, - 'user': 'annafan', - } + """User should not be able to see other's sensitive data.""" + context = {"model": model, "session": model.Session, "user": "annafan"} - user = model.User.by_name('tester') + user = model.User.by_name("tester") user_dict = user_dictize(user, context) # Check some of the non-sensitive data - assert 'name' in user_dict - assert 'about' in user_dict + assert "name" in user_dict + assert "about" in user_dict # Check sensitive data is not available - assert 'apikey' not in user_dict - assert 'reset_key' not in user_dict - assert 'email' not in user_dict + assert "apikey" not in user_dict + assert "reset_key" not in user_dict + assert "email" not in user_dict # Passwords should never be available - assert 'password' not in user_dict + assert "password" not in user_dict def test_25_user_dictize_as_anonymous(self): - '''Anonymous should not be able to see other's sensitive data.''' - context = { - 'model': model, - 'session': model.Session, - 'user': '', - } + """Anonymous should not be able to see other's sensitive data.""" + context = {"model": model, "session": model.Session, "user": ""} - user = model.User.by_name('tester') + user = model.User.by_name("tester") user_dict = user_dictize(user, context) # Check some of the non-sensitive data - assert 'name' in user_dict - assert 'about' in user_dict + assert "name" in user_dict + assert "about" in user_dict # Check sensitive data is not available - assert 'apikey' not in user_dict - assert 'reset_key' not in user_dict - assert 'email' not in user_dict + assert "apikey" not in user_dict + assert "reset_key" not in user_dict + assert "email" not in user_dict # Passwords should never be available - assert 'password' not in user_dict + assert "password" not in user_dict diff --git a/ckan/tests/legacy/lib/test_dictization_schema.py b/ckan/tests/legacy/lib/test_dictization_schema.py index 301c52d59a6..0f2c94df20b 100644 --- a/ckan/tests/legacy/lib/test_dictization_schema.py +++ b/ckan/tests/legacy/lib/test_dictization_schema.py @@ -1,39 +1,31 @@ # encoding: utf-8 -from pprint import pprint, pformat - +from pprint import pformat +import pytest from ckan.lib.create_test_data import CreateTestData from ckan.lib import search from ckan import model -from ckan.lib.dictization.model_dictize import (package_dictize, - group_dictize) -from ckan.logic.schema import (default_create_package_schema, - default_update_package_schema, - default_group_schema, - default_tags_schema) +from ckan.lib.dictization.model_dictize import package_dictize, group_dictize +from ckan.logic.schema import ( + default_create_package_schema, + default_update_package_schema, + default_group_schema, + default_tags_schema, +) from ckan.lib.navl.dictization_functions import validate -class TestBasicDictize: - def setup(self): - self.context = {'model': model, - 'session': model.Session} - - @classmethod - def setup_class(cls): - search.clear_all() +class TestBasicDictize(object): + @pytest.fixture(autouse=True) + def initial_data(self, clean_db, clean_index): CreateTestData.create() - - @classmethod - def teardown_class(cls): - model.repo.rebuild_db() - model.Session.remove() + self.context = {"model": model, "session": model.Session} def remove_changable_columns(self, dict): for key, value in dict.items(): - if key.endswith('id') and key != 'license_id': + if key.endswith("id") and key != "license_id": dict.pop(key) - if key == 'created': + if key == "created": dict.pop(key) if isinstance(value, list): @@ -42,81 +34,95 @@ def remove_changable_columns(self, dict): return dict def test_1_package_schema(self): - pkg = model.Session.query(model.Package)\ - .filter_by(name='annakarenina')\ + pkg = ( + model.Session.query(model.Package) + .filter_by(name="annakarenina") .first() + ) package_id = pkg.id result = package_dictize(pkg, self.context) self.remove_changable_columns(result) - result['name'] = 'anna2' + result["name"] = "anna2" # we need to remove these as they have been added - del result['relationships_as_object'] - del result['relationships_as_subject'] + del result["relationships_as_object"] + del result["relationships_as_subject"] - converted_data, errors = validate(result, - default_create_package_schema(), - self.context) + converted_data, errors = validate( + result, default_create_package_schema(), self.context + ) expected_data = { - 'extras': [{'key': u'genre', 'value': u'romantic novel'}, - {'key': u'original media', 'value': u'book'}], - 'groups': [{u'name': u'david', - u'title': u"Dave's books"}, - {u'name': u'roger', - u'title': u"Roger's books"}], - 'license_id': u'other-open', - 'name': u'anna2', - 'type': u'dataset', - 'notes': u'Some test notes\n\n### A 3rd level heading\n\n**Some bolded text.**\n\n*Some italicized text.*\n\nForeign characters:\nu with umlaut \xfc\n66-style quote \u201c\nforeign word: th\xfcmb\n\nNeeds escaping:\nleft arrow <\n\n\n\n', - 'private': False, - 'resources': [{'alt_url': u'alt123', - 'description': u'Full text. Needs escaping: " Umlaut: \xfc', - 'format': u'plain text', - 'hash': u'abc123', - 'size_extra': u'123', - 'url': u'http://datahub.io/download/x=1&y=2'}, - {'alt_url': u'alt345', - 'description': u'Index of the novel', - 'format': u'JSON', - 'hash': u'def456', - 'size_extra': u'345', - 'url': u'http://datahub.io/index.json'}], - 'tags': [{'name': u'Flexible \u30a1'}, - {'name': u'russian'}, - {'name': u'tolstoy'}], - 'title': u'A Novel By Tolstoy', - 'url': u'http://datahub.io', - 'version': u'0.7a' + "extras": [ + {"key": u"genre", "value": u"romantic novel"}, + {"key": u"original media", "value": u"book"}, + ], + "groups": [ + {u"name": u"david", u"title": u"Dave's books"}, + {u"name": u"roger", u"title": u"Roger's books"}, + ], + "license_id": u"other-open", + "name": u"anna2", + "type": u"dataset", + "notes": u"Some test notes\n\n### A 3rd level heading\n\n**Some bolded text.**\n\n*Some italicized text.*\n\nForeign characters:\nu with umlaut \xfc\n66-style quote \u201c\nforeign word: th\xfcmb\n\nNeeds escaping:\nleft arrow <\n\n\n\n", + "private": False, + "resources": [ + { + "alt_url": u"alt123", + "description": u'Full text. Needs escaping: " Umlaut: \xfc', + "format": u"plain text", + "hash": u"abc123", + "size_extra": u"123", + "url": u"http://datahub.io/download/x=1&y=2", + }, + { + "alt_url": u"alt345", + "description": u"Index of the novel", + "format": u"JSON", + "hash": u"def456", + "size_extra": u"345", + "url": u"http://datahub.io/index.json", + }, + ], + "tags": [ + {"name": u"Flexible \u30a1"}, + {"name": u"russian"}, + {"name": u"tolstoy"}, + ], + "title": u"A Novel By Tolstoy", + "url": u"http://datahub.io", + "version": u"0.7a", } assert converted_data == expected_data, pformat(converted_data) assert not errors, errors data = converted_data - data['name'] = u'annakarenina' + data["name"] = u"annakarenina" data.pop("title") - data["resources"][0]["url"] = 'fsdfafasfsaf' + data["resources"][0]["url"] = "fsdfafasfsaf" data["resources"][1].pop("url") - converted_data, errors = validate(data, - default_create_package_schema(), - self.context) + converted_data, errors = validate( + data, default_create_package_schema(), self.context + ) - assert errors == { - 'name': [u'That URL is already in use.'], - }, pformat(errors) + assert errors == {"name": [u"That URL is already in use."]}, pformat( + errors + ) data["id"] = package_id - data['name'] = '????jfaiofjioafjij' + data["name"] = "????jfaiofjioafjij" - converted_data, errors = validate(data, - default_update_package_schema(), - self.context) + converted_data, errors = validate( + data, default_update_package_schema(), self.context + ) assert errors == { - 'name': [u'Must be purely lowercase alphanumeric (ascii) ' - 'characters and these symbols: -_'], + "name": [ + u"Must be purely lowercase alphanumeric (ascii) " + "characters and these symbols: -_" + ] }, pformat(errors) def test_2_group_schema(self): @@ -124,61 +130,72 @@ def test_2_group_schema(self): data = group_dictize(group, self.context) # we don't want these here - del data['groups'] - del data['users'] - del data['tags'] - del data['extras'] - - converted_data, errors = validate(data, - default_group_schema(), - self.context) + del data["groups"] + del data["users"] + del data["tags"] + del data["extras"] + + converted_data, errors = validate( + data, default_group_schema(), self.context + ) group_pack = sorted(group.packages(), key=lambda x: x.id) - converted_data["packages"] = sorted(converted_data["packages"], - key=lambda x: x["id"]) + converted_data["packages"] = sorted( + converted_data["packages"], key=lambda x: x["id"] + ) expected = { - 'description': u'These are books that David likes.', - 'id': group.id, - 'name': u'david', - 'is_organization': False, - 'type': u'group', - 'image_url': u'', - 'image_display_url': u'', - 'packages': sorted([{'id': group_pack[0].id, - 'name': group_pack[0].name, - 'title': group_pack[0].title}, - {'id': group_pack[1].id, - 'name': group_pack[1].name, - 'title':group_pack[1].title}], - key=lambda x: x["id"]), - 'title': u"Dave's books", - 'approval_status': u'approved' + "description": u"These are books that David likes.", + "id": group.id, + "name": u"david", + "is_organization": False, + "type": u"group", + "image_url": u"", + "image_display_url": u"", + "packages": sorted( + [ + { + "id": group_pack[0].id, + "name": group_pack[0].name, + "title": group_pack[0].title, + }, + { + "id": group_pack[1].id, + "name": group_pack[1].name, + "title": group_pack[1].title, + }, + ], + key=lambda x: x["id"], + ), + "title": u"Dave's books", + "approval_status": u"approved", } assert not errors assert converted_data == expected, pformat(converted_data) data["packages"].sort(key=lambda x: x["id"]) - data["packages"][0]["id"] = 'fjdlksajfalsf' + data["packages"][0]["id"] = "fjdlksajfalsf" data["packages"][1].pop("id") data["packages"][1].pop("name") - converted_data, errors = validate(data, - default_group_schema(), - self.context) + converted_data, errors = validate( + data, default_group_schema(), self.context + ) assert errors == { - 'packages': [{'id': [u'Not found: Dataset']}, - {'id': [u'Missing value']}] + "packages": [ + {"id": [u"Not found: Dataset"]}, + {"id": [u"Missing value"]}, + ] }, pformat(errors) def test_3_tag_schema_allows_spaces(self): """Asserts that a tag name with space is valid""" ignored = "" data = { - 'name': u'with space', - 'revision_timestamp': ignored, - 'state': ignored + "name": u"with space", + "revision_timestamp": ignored, + "state": ignored, } _, errors = validate(data, default_tags_schema(), self.context) assert not errors, str(errors) @@ -187,9 +204,9 @@ def test_4_tag_schema_allows_limited_punctuation(self): """Asserts that a tag name with limited punctuation is valid""" ignored = "" data = { - 'name': u'.-_', - 'revision_timestamp': ignored, - 'state': ignored + "name": u".-_", + "revision_timestamp": ignored, + "state": ignored, } _, errors = validate(data, default_tags_schema(), self.context) assert not errors, str(errors) @@ -198,9 +215,9 @@ def test_5_tag_schema_allows_capital_letters(self): """Asserts that tag names can have capital letters""" ignored = "" data = { - 'name': u'CAPITALS', - 'revision_timestamp': ignored, - 'state': ignored + "name": u"CAPITALS", + "revision_timestamp": ignored, + "state": ignored, } _, errors = validate(data, default_tags_schema(), self.context) assert not errors, str(errors) @@ -209,32 +226,26 @@ def test_6_tag_schema_disallows_most_punctuation(self): """Asserts most punctuation is disallowed""" not_allowed = r'!?"\'+=:;@#~[]{}()*&^%$,' ignored = "" - data = { - 'revision_timestamp': ignored, - 'state': ignored - } + data = {"revision_timestamp": ignored, "state": ignored} for ch in not_allowed: - data['name'] = "Character " + ch + data["name"] = "Character " + ch _, errors = validate(data, default_tags_schema(), self.context) - assert errors, pprint(errors) - assert 'name' in errors - error_message = errors['name'][0] - assert data['name'] in error_message, error_message + assert errors + assert "name" in errors + error_message = errors["name"][0] + assert data["name"] in error_message, error_message assert "must be alphanumeric" in error_message def test_7_tag_schema_disallows_whitespace_other_than_spaces(self): """Asserts whitespace characters, such as tabs, are not allowed.""" - not_allowed = '\t\n\r\f\v' + not_allowed = "\t\n\r\f\v" ignored = "" - data = { - 'revision_timestamp': ignored, - 'state': ignored - } + data = {"revision_timestamp": ignored, "state": ignored} for ch in not_allowed: - data['name'] = "Bad " + ch + " character" + data["name"] = "Bad " + ch + " character" _, errors = validate(data, default_tags_schema(), self.context) assert errors, repr(ch) - assert 'name' in errors - error_message = errors['name'][0] - assert data['name'] in error_message, error_message + assert "name" in errors + error_message = errors["name"][0] + assert data["name"] in error_message, error_message assert "must be alphanumeric" in error_message diff --git a/ckan/tests/legacy/lib/test_email_notifications.py b/ckan/tests/legacy/lib/test_email_notifications.py index bd00246841a..35d25389cff 100644 --- a/ckan/tests/legacy/lib/test_email_notifications.py +++ b/ckan/tests/legacy/lib/test_email_notifications.py @@ -1,48 +1,68 @@ # encoding: utf-8 -'''Tests for the ckan.lib.email_notifications module. +"""Tests for the ckan.lib.email_notifications module. Note that email_notifications is used by an action function, so most of the tests for the module are done by testing the action function in ckan.test.functional.api. This test module contains some additional unit tests. -''' +""" import datetime -import nose.tools +import pytest import ckan.lib.email_notifications as email_notifications import ckan.logic as logic def test_string_to_time_delta(): - assert email_notifications.string_to_timedelta('1 day') == ( - datetime.timedelta(days=1)) - assert email_notifications.string_to_timedelta('1 day') == ( - datetime.timedelta(days=1)) - assert email_notifications.string_to_timedelta('2 days') == ( - datetime.timedelta(days=2)) - assert email_notifications.string_to_timedelta('2\tdays') == ( - datetime.timedelta(days=2)) - assert email_notifications.string_to_timedelta('14 days') == ( - datetime.timedelta(days=14)) - assert email_notifications.string_to_timedelta('4:35:00') == ( - datetime.timedelta(hours=4, minutes=35, seconds=00)) - assert email_notifications.string_to_timedelta('4:35:12.087465') == ( - datetime.timedelta(hours=4, minutes=35, seconds=12, - milliseconds=87, microseconds=465)) - assert email_notifications.string_to_timedelta('1 day, 3:23:34') == ( - datetime.timedelta(days=1, hours=3, minutes=23, seconds=34)) - assert email_notifications.string_to_timedelta('1 day, 3:23:34') == ( - datetime.timedelta(days=1, hours=3, minutes=23, seconds=34)) - assert email_notifications.string_to_timedelta('7 days, 3:23:34') == ( - datetime.timedelta(days=7, hours=3, minutes=23, seconds=34)) - assert email_notifications.string_to_timedelta('7 days,\t3:23:34') == ( - datetime.timedelta(days=7, hours=3, minutes=23, seconds=34)) + assert email_notifications.string_to_timedelta("1 day") == ( + datetime.timedelta(days=1) + ) + assert email_notifications.string_to_timedelta("1 day") == ( + datetime.timedelta(days=1) + ) + assert email_notifications.string_to_timedelta("2 days") == ( + datetime.timedelta(days=2) + ) + assert email_notifications.string_to_timedelta("2\tdays") == ( + datetime.timedelta(days=2) + ) + assert email_notifications.string_to_timedelta("14 days") == ( + datetime.timedelta(days=14) + ) + assert email_notifications.string_to_timedelta("4:35:00") == ( + datetime.timedelta(hours=4, minutes=35, seconds=00) + ) + assert email_notifications.string_to_timedelta("4:35:12.087465") == ( + datetime.timedelta( + hours=4, minutes=35, seconds=12, milliseconds=87, microseconds=465 + ) + ) + assert email_notifications.string_to_timedelta("1 day, 3:23:34") == ( + datetime.timedelta(days=1, hours=3, minutes=23, seconds=34) + ) + assert email_notifications.string_to_timedelta("1 day, 3:23:34") == ( + datetime.timedelta(days=1, hours=3, minutes=23, seconds=34) + ) + assert email_notifications.string_to_timedelta("7 days, 3:23:34") == ( + datetime.timedelta(days=7, hours=3, minutes=23, seconds=34) + ) + assert email_notifications.string_to_timedelta("7 days,\t3:23:34") == ( + datetime.timedelta(days=7, hours=3, minutes=23, seconds=34) + ) assert email_notifications.string_to_timedelta( - '7 days, 3:23:34.087465') == datetime.timedelta(days=7, hours=3, - minutes=23, seconds=34, milliseconds=87, microseconds=465) - assert email_notifications.string_to_timedelta('.123456') == ( - datetime.timedelta(milliseconds=123, microseconds=456)) - nose.tools.assert_raises(logic.ValidationError, - email_notifications.string_to_timedelta, 'foobar') + "7 days, 3:23:34.087465" + ) == datetime.timedelta( + days=7, + hours=3, + minutes=23, + seconds=34, + milliseconds=87, + microseconds=465, + ) + assert email_notifications.string_to_timedelta(".123456") == ( + datetime.timedelta(milliseconds=123, microseconds=456) + ) + with pytest.raises(logic.ValidationError): + email_notifications.string_to_timedelta("foobar") diff --git a/ckan/tests/legacy/lib/test_hash.py b/ckan/tests/legacy/lib/test_hash.py index bf67970700f..4e435d7761e 100644 --- a/ckan/tests/legacy/lib/test_hash.py +++ b/ckan/tests/legacy/lib/test_hash.py @@ -1,18 +1,16 @@ # encoding: utf-8 -from nose.tools import assert_equals from ckan.lib.hash import get_message_hash -class TestHash: - @classmethod - def setup_class(cls): - global secret - secret = '42' # so that these tests are repeatable - def test_get_message_hash(self): - assert_equals(len(get_message_hash(u'/tag/country-uk')), len('6f58ff51b42e6b2d2e700abd1a14c9699e115c61')) +def test_get_message_hash(): + assert len(get_message_hash(u"/tag/country-uk")) == len( + "6f58ff51b42e6b2d2e700abd1a14c9699e115c61" + ) - def test_get_message_hash_unicode(self): - assert_equals(len(get_message_hash(u'/tag/biocombust\xedveis')), len('d748fa890eb6a964cd317e6ff62905fad645b43d')) +def test_get_message_hash_unicode(): + assert len(get_message_hash(u"/tag/biocombust\xedveis")) == len( + "d748fa890eb6a964cd317e6ff62905fad645b43d" + ) diff --git a/ckan/tests/legacy/lib/test_helpers.py b/ckan/tests/legacy/lib/test_helpers.py index e1b2eda14ac..259248b9222 100644 --- a/ckan/tests/legacy/lib/test_helpers.py +++ b/ckan/tests/legacy/lib/test_helpers.py @@ -1,91 +1,92 @@ # encoding: utf-8 import datetime -from nose.tools import assert_equal, assert_raises - +import pytest from ckan.common import config -from ckan.tests.legacy import * import ckan.lib.helpers as h -WITH_HTML = u'''Data exposed: — +WITH_HTML = u"""Data exposed: — Size of dump and data set: size? Notes: this is the classic RDF source but historically has had some problems with RDF correctness. -''' - -WITH_UNICODE = u'''[From the project website] This project collects information on China’s foreign aid from the China Commerce Yearbook (中国商务年鉴) and the Almanac of China’s Foreign Economic Relations & Trade (中国对外经济贸易年间), published annually by China’s Ministry of Commerce (MOFCOM). Data is reported for each year between 1990 and 2005, with the exception of 2002, in which year China’s Ministry of Commerce published no project-level data on its foreign aid giving.''' +""" +WITH_UNICODE = u"""[From the project website] This project collects information on China’s foreign aid from the China Commerce Yearbook (中国商务年鉴) and the Almanac of China’s Foreign Economic Relations & Trade (中国对外经济贸易年间), published annually by China’s Ministry of Commerce (MOFCOM). Data is reported for each year between 1990 and 2005, with the exception of 2002, in which year China’s Ministry of Commerce published no project-level data on its foreign aid giving.""" -class TestHelpers(TestController): +class TestHelpers(object): def test_extract_markdown(self): assert "Data exposed" in h.markdown_extract(WITH_HTML) assert "collects information" in h.markdown_extract(WITH_UNICODE) def test_datetime_to_date_str(self): res = datetime.datetime(2008, 4, 13, 20, 40, 20, 123456).isoformat() - assert_equal(res, '2008-04-13T20:40:20.123456') + assert res == "2008-04-13T20:40:20.123456" def test_date_str_to_datetime_date_only(self): - res = h.date_str_to_datetime('2008-04-13') - assert_equal(res, datetime.datetime(2008, 4, 13)) + res = h.date_str_to_datetime("2008-04-13") + assert res == datetime.datetime(2008, 4, 13) def test_date_str_to_datetime(self): - res = h.date_str_to_datetime('2008-04-13T20:40:20.123456') - assert_equal(res, datetime.datetime(2008, 4, 13, 20, 40, 20, 123456)) + res = h.date_str_to_datetime("2008-04-13T20:40:20.123456") + assert res == datetime.datetime(2008, 4, 13, 20, 40, 20, 123456) def test_date_str_to_datetime_without_microseconds(self): # This occurs in ckan.net timestamps - not sure how they appeared - res = h.date_str_to_datetime('2008-04-13T20:40:20') - assert_equal(res, datetime.datetime(2008, 4, 13, 20, 40, 20)) + res = h.date_str_to_datetime("2008-04-13T20:40:20") + assert res == datetime.datetime(2008, 4, 13, 20, 40, 20) def test_date_str_to_datetime_with_timezone(self): - assert_raises(ValueError, - h.date_str_to_datetime, - '2008-04-13T20:40:20-01:30') + with pytest.raises(ValueError): + h.date_str_to_datetime("2008-04-13T20:40:20-01:30") def test_date_str_to_datetime_with_timezone_without_colon(self): - assert_raises(ValueError, - h.date_str_to_datetime, - '2008-04-13T20:40:20-0130') + with pytest.raises(ValueError): + h.date_str_to_datetime("2008-04-13T20:40:20-0130") def test_date_str_to_datetime_with_garbage_on_end(self): - assert_raises(ValueError, - h.date_str_to_datetime, - '2008-04-13T20:40:20foobar') + with pytest.raises(ValueError): + h.date_str_to_datetime("2008-04-13T20:40:20foobar") def test_date_str_to_datetime_with_ambiguous_microseconds(self): - assert_raises(ValueError, - h.date_str_to_datetime, - '2008-04-13T20:40:20.500') + with pytest.raises(ValueError): + h.date_str_to_datetime("2008-04-13T20:40:20.500") def test_time_ago_in_words_from_str(self): two_months_ago = datetime.datetime.now() - datetime.timedelta(days=65) two_months_ago_str = two_months_ago.isoformat() res = h.time_ago_in_words_from_str(two_months_ago_str) - assert_equal(res, '2 months ago') + assert res == "2 months ago" def test_gravatar(self): - email = 'zephod@gmail.com' - expected = [''] + email = "zephod@gmail.com" + expected = [ + '", + ] # Hash the email address import hashlib + email_hash = hashlib.md5(email).hexdigest() - res = h.linked_gravatar(email_hash, 200, default='mm') + res = h.linked_gravatar(email_hash, 200, default="mm") for e in expected: assert e in res, (e, res) def test_gravatar_config_set_default(self): """Test when default gravatar is None, it is pulled from the config file""" - email = 'zephod@gmail.com' - default = config.get('ckan.gravatar_default', 'identicon') - expected = [''] + email = "zephod@gmail.com" + default = config.get("ckan.gravatar_default", "identicon") + expected = [ + '", + ] # Hash the email address import hashlib + email_hash = hashlib.md5(email).hexdigest() res = h.linked_gravatar(email_hash, 200) for e in expected: @@ -93,13 +94,16 @@ def test_gravatar_config_set_default(self): def test_gravatar_encodes_url_correctly(self): """Test when the default gravatar is a url, it gets urlencoded""" - email = 'zephod@gmail.com' - default = 'http://example.com/images/avatar.jpg' - expected = [''] + email = "zephod@gmail.com" + default = "http://example.com/images/avatar.jpg" + expected = [ + '", + ] # Hash the email address import hashlib + email_hash = hashlib.md5(email).hexdigest() res = h.linked_gravatar(email_hash, 200, default=default) for e in expected: @@ -111,8 +115,8 @@ def test_parse_rfc_2822_no_timezone_specified(self): Assuming it's UTC. """ - dt = h.parse_rfc_2822_date('Tue, 15 Nov 1994 12:45:26') - assert_equal(dt.isoformat(), '1994-11-15T12:45:26+00:00') + dt = h.parse_rfc_2822_date("Tue, 15 Nov 1994 12:45:26") + assert dt.isoformat() == "1994-11-15T12:45:26+00:00" def test_parse_rfc_2822_no_timezone_specified_assuming_local(self): """ @@ -120,9 +124,11 @@ def test_parse_rfc_2822_no_timezone_specified_assuming_local(self): Assuming it's local. """ - dt = h.parse_rfc_2822_date('Tue, 15 Nov 1994 12:45:26', assume_utc=False) - assert_equal(dt.isoformat(), '1994-11-15T12:45:26') - assert_equal(dt.tzinfo, None) + dt = h.parse_rfc_2822_date( + "Tue, 15 Nov 1994 12:45:26", assume_utc=False + ) + assert dt.isoformat() == "1994-11-15T12:45:26" + assert dt.tzinfo is None def test_parse_rfc_2822_gmt_case(self): """ @@ -130,15 +136,15 @@ def test_parse_rfc_2822_gmt_case(self): GMT obs-zone specified """ - dt = h.parse_rfc_2822_date('Tue, 15 Nov 1994 12:45:26 GMT') - assert_equal(dt.isoformat(), '1994-11-15T12:45:26+00:00') + dt = h.parse_rfc_2822_date("Tue, 15 Nov 1994 12:45:26 GMT") + assert dt.isoformat() == "1994-11-15T12:45:26+00:00" def test_parse_rfc_2822_with_offset(self): """ Parse "Tue, 15 Nov 1994 12:45:26 +0700" successfully. """ - dt = h.parse_rfc_2822_date('Tue, 15 Nov 1994 12:45:26 +0700') - assert_equal(dt.isoformat(), '1994-11-15T12:45:26+07:00') + dt = h.parse_rfc_2822_date("Tue, 15 Nov 1994 12:45:26 +0700") + assert dt.isoformat() == "1994-11-15T12:45:26+07:00" def test_escape_js(self): @@ -148,8 +154,9 @@ def test_escape_js(self): output_str = h.escape_js(input_str) - assert_equal(output_str, expected_str) + assert output_str == expected_str + @pytest.mark.usefixtures("clean_db") def test_get_pkg_dict_extra(self): from ckan.lib.create_test_data import CreateTestData @@ -158,12 +165,17 @@ def test_get_pkg_dict_extra(self): CreateTestData.create() - pkg_dict = get_action('package_show')({'model': model, 'user': u'tester'}, {'id': 'annakarenina'}) + pkg_dict = get_action("package_show")( + {"model": model, "user": u"tester"}, {"id": "annakarenina"} + ) - assert_equal(h.get_pkg_dict_extra(pkg_dict, 'genre'), 'romantic novel') + assert h.get_pkg_dict_extra(pkg_dict, "genre") == "romantic novel" - assert_equal(h.get_pkg_dict_extra(pkg_dict, 'extra_not_found'), None) + assert h.get_pkg_dict_extra(pkg_dict, "extra_not_found") is None - assert_equal(h.get_pkg_dict_extra(pkg_dict, 'extra_not_found', 'default_value'), 'default_value') + assert ( + h.get_pkg_dict_extra(pkg_dict, "extra_not_found", "default_value") + == "default_value" + ) model.repo.rebuild_db() diff --git a/ckan/tests/legacy/lib/test_navl.py b/ckan/tests/legacy/lib/test_navl.py index f8e11222b57..e4fceca892d 100644 --- a/ckan/tests/legacy/lib/test_navl.py +++ b/ckan/tests/legacy/lib/test_navl.py @@ -2,23 +2,27 @@ from six import text_type -from ckan.lib.navl.dictization_functions import (flatten_schema, - get_all_key_combinations, - make_full_schema, - flatten_dict, - unflatten, - missing, - augment_data, - validate, - _validate) -from pprint import pprint, pformat -from ckan.lib.navl.validators import (identity_converter, - empty, - not_empty, - ignore_missing, - default, - convert_int, - ignore) +from ckan.lib.navl.dictization_functions import ( + flatten_schema, + get_all_key_combinations, + make_full_schema, + flatten_dict, + unflatten, + missing, + augment_data, + validate, + _validate, +) +from pprint import pformat +from ckan.lib.navl.validators import ( + identity_converter, + empty, + not_empty, + ignore_missing, + default, + convert_int, + ignore, +) schema = { @@ -32,30 +36,26 @@ "__after": [identity_converter], "20": [identity_converter], "22": [identity_converter], - "21": { - "210": [identity_converter], - }, + "21": {"210": [identity_converter]}, }, - "3": { - "30": [identity_converter], - } + "3": {"30": [identity_converter]}, } data = { ("0",): "0 value", - #key 1 missing + # key 1 missing ("2", 0, "20"): "20 value 0", - #key 2,22 missing + # key 2,22 missing ("2", 0, "21", 0, "210"): "210 value 0,0", - #key 3 missing subdict + # key 3 missing subdict ("2", 1, "20"): "20 value 1", ("2", 1, "22"): "22 value 1", ("2", 1, "21", 0, "210"): "210 value 1,0", ("2", 1, "21", 1, "210"): "210 value 1,1", - ("2", 1, "21", 3, "210"): "210 value 1,3", ##out of order sequence - ("4", 1, "30"): "30 value 1", #junk key as no 4 and no subdict - ("4",): "4 value", #extra key 4 -# ("2", 2, "21", 0, "210"): "210 value 2,0" #junk key as it does not have a parent + ("2", 1, "21", 3, "210"): "210 value 1,3", ##out of order sequence + ("4", 1, "30"): "30 value 1", # junk key as no 4 and no subdict + ("4",): "4 value", # extra key 4 + # ("2", 2, "21", 0, "210"): "210 value 2,0" #junk key as it does not have a parent } @@ -64,99 +64,105 @@ def test_flatten_schema(): flattened_schema = flatten_schema(schema) assert flattened_schema == { - ('0',): [identity_converter], - ('1',): [identity_converter], - ('2', '20'): [identity_converter], - ('2', '__after'): [identity_converter], - ('2', '__before'): [identity_converter], - ('2', '21', '210'): [identity_converter], - ('2', '22'): [identity_converter], - ('3', '30'): [identity_converter], - ('__after',): [identity_converter], - ('__extra',): [identity_converter], - ('__junk',): [identity_converter], - }, pprint(flattened_schema) + ("0",): [identity_converter], + ("1",): [identity_converter], + ("2", "20"): [identity_converter], + ("2", "__after"): [identity_converter], + ("2", "__before"): [identity_converter], + ("2", "21", "210"): [identity_converter], + ("2", "22"): [identity_converter], + ("3", "30"): [identity_converter], + ("__after",): [identity_converter], + ("__extra",): [identity_converter], + ("__junk",): [identity_converter], + } + def test_get_key_combination(): flattened_schema = flatten_schema(schema) - assert get_all_key_combinations(data, flattened_schema) ==\ - set([(), - ('2', 0), - ('2', 1), - ('2', 1, '21', 0), - ('2', 0, '21', 0), - ('2', 1, '21', 1), - ('2', 1, '21', 3), - ]), get_all_key_combinations(data, flattened_schema) - - #state = {} - #make_flattened_schema(data, schema, state) + assert get_all_key_combinations(data, flattened_schema) == set( + [ + (), + ("2", 0), + ("2", 1), + ("2", 1, "21", 0), + ("2", 0, "21", 0), + ("2", 1, "21", 1), + ("2", 1, "21", 3), + ] + ), get_all_key_combinations(data, flattened_schema) + + # state = {} + # make_flattened_schema(data, schema, state) + def test_make_full_schema(): full_schema = make_full_schema(data, schema) - print(set(full_schema.keys()) - set(data.keys())) - - assert set(full_schema.keys()) - set(data.keys()) == set([('2', 1, '__before'), - ('2', 0, '__after'), - ('2', 0, '22'), - ('1',), - ('2', 1, '__after'), - ('2', 0, '__before'), - ('__after',), - ('__extra',), - ('__junk',), - ]) - - print(set(data.keys()) - set(full_schema.keys())) - - assert set(data.keys()) - set(full_schema.keys()) == set([('4',), - ('4', 1, '30')]) + assert set(full_schema.keys()) - set(data.keys()) == set( + [ + ("2", 1, "__before"), + ("2", 0, "__after"), + ("2", 0, "22"), + ("1",), + ("2", 1, "__after"), + ("2", 0, "__before"), + ("__after",), + ("__extra",), + ("__junk",), + ] + ) + + assert set(data.keys()) - set(full_schema.keys()) == set( + [("4",), ("4", 1, "30")] + ) def test_augment_junk_and_extras(): assert augment_data(data, schema) == { - ('__junk',): {('4', 1, '30'): '30 value 1'}, - ('0',): '0 value', - ('1',): missing, - ('2', 0, '20'): '20 value 0', - ('2', 0, '21', 0, '210'): '210 value 0,0', - ('2', 0, '22'): missing, - ('2', 1, '20'): '20 value 1', - ('2', 1, '21', 0, '210'): '210 value 1,0', - ('2', 1, '21', 1, '210'): '210 value 1,1', - ('2', 1, '21', 3, '210'): '210 value 1,3', - ('2', 1, '22'): '22 value 1', - ('__extras',): {'4': '4 value'}}, pprint(augment_data(data, schema)) + ("__junk",): {("4", 1, "30"): "30 value 1"}, + ("0",): "0 value", + ("1",): missing, + ("2", 0, "20"): "20 value 0", + ("2", 0, "21", 0, "210"): "210 value 0,0", + ("2", 0, "22"): missing, + ("2", 1, "20"): "20 value 1", + ("2", 1, "21", 0, "210"): "210 value 1,0", + ("2", 1, "21", 1, "210"): "210 value 1,1", + ("2", 1, "21", 3, "210"): "210 value 1,3", + ("2", 1, "22"): "22 value 1", + ("__extras",): {"4": "4 value"}, + } def test_identity_validation(): - converted_data, errors = validate_flattened(data, schema) - print(errors) - print(converted_data) assert not errors - - assert sorted(converted_data) == sorted({ - ('__junk',): {('2', 2, '21', 0, '210'): '210 value 2,0', - ('4', 1, '30'): '30 value 1'}, - ('0',): '0 value', - ('1',): missing, - ('2', 0, '20'): '20 value 0', - ('2', 0, '21', 0, '210'): '210 value 0,0', - ('2', 0, '22'): missing, - ('2', 1, '20'): '20 value 1', - ('2', 1, '21', 0, '210'): '210 value 1,0', - ('2', 1, '21', 1, '210'): '210 value 1,1', - ('2', 1, '21', 3, '210'): '210 value 1,3', - ('2', 1, '22'): '22 value 1', - ('__extras',): {'4': '4 value'}}), pformat(sorted(converted_data)) + assert sorted(converted_data) == sorted( + { + ("__junk",): { + ("2", 2, "21", 0, "210"): "210 value 2,0", + ("4", 1, "30"): "30 value 1", + }, + ("0",): "0 value", + ("1",): missing, + ("2", 0, "20"): "20 value 0", + ("2", 0, "21", 0, "210"): "210 value 0,0", + ("2", 0, "22"): missing, + ("2", 1, "20"): "20 value 1", + ("2", 1, "21", 0, "210"): "210 value 1,0", + ("2", 1, "21", 1, "210"): "210 value 1,1", + ("2", 1, "21", 3, "210"): "210 value 1,3", + ("2", 1, "22"): "22 value 1", + ("__extras",): {"4": "4 value"}, + } + ), pformat(sorted(converted_data)) def test_basic_errors(): @@ -171,64 +177,76 @@ def test_basic_errors(): "20": [identity_converter], "22": [identity_converter], "__extras": [empty], - "21": { - "210": [identity_converter], - }, - }, - "3": { - "30": [identity_converter], + "21": {"210": [identity_converter]}, }, + "3": {"30": [identity_converter]}, } converted_data, errors = validate_flattened(data, schema) - assert errors == {('__junk',): [u"The input field [('4', 1, '30')] was not expected."], ('1',): [u'Missing value'], ('__extras',): [u'The input field __extras was not expected.']}, errors + assert errors == { + ("__junk",): [u"The input field [('4', 1, '30')] was not expected."], + ("1",): [u"Missing value"], + ("__extras",): [u"The input field __extras was not expected."], + }, errors def test_flatten(): - data = {'extras': [{'key': 'genre', 'value': u'horror'}, - {'key': 'media', 'value': u'dvd'}], - 'license_id': u'gpl-3.0', - 'name': u'testpkg', - 'resources': [{u'alt_url': u'alt_url', - u'description': u'Second file', - u'extras': {u'size': u'200'}, - u'format': u'xml', - u'hash': u'def123', - u'url': u'http://blah.com/file2.xml'}, - {u'alt_url': u'alt_url', - u'description': u'Main file', - u'extras': {u'size': u'200'}, - u'format': u'xml', - u'hash': u'abc123', - u'url': u'http://blah.com/file.xml'}], - 'tags': [{'name': u'russion'}, {'name': u'novel'}], - 'title': u'Some Title', - 'url': u'http://blahblahblah.mydomain'} - - assert flatten_dict(data) == {('extras', 0, 'key'): 'genre', - ('extras', 0, 'value'): u'horror', - ('extras', 1, 'key'): 'media', - ('extras', 1, 'value'): u'dvd', - ('license_id',): u'gpl-3.0', - ('name',): u'testpkg', - ('resources', 0, u'alt_url'): u'alt_url', - ('resources', 0, u'description'): u'Second file', - ('resources', 0, u'extras'): {u'size': u'200'}, - ('resources', 0, u'format'): u'xml', - ('resources', 0, u'hash'): u'def123', - ('resources', 0, u'url'): u'http://blah.com/file2.xml', - ('resources', 1, u'alt_url'): u'alt_url', - ('resources', 1, u'description'): u'Main file', - ('resources', 1, u'extras'): {u'size': u'200'}, - ('resources', 1, u'format'): u'xml', - ('resources', 1, u'hash'): u'abc123', - ('resources', 1, u'url'): u'http://blah.com/file.xml', - ('tags', 0, 'name'): u'russion', - ('tags', 1, 'name'): u'novel', - ('title',): u'Some Title', - ('url',): u'http://blahblahblah.mydomain'}, pformat(flatten_dict(data)) + data = { + "extras": [ + {"key": "genre", "value": u"horror"}, + {"key": "media", "value": u"dvd"}, + ], + "license_id": u"gpl-3.0", + "name": u"testpkg", + "resources": [ + { + u"alt_url": u"alt_url", + u"description": u"Second file", + u"extras": {u"size": u"200"}, + u"format": u"xml", + u"hash": u"def123", + u"url": u"http://blah.com/file2.xml", + }, + { + u"alt_url": u"alt_url", + u"description": u"Main file", + u"extras": {u"size": u"200"}, + u"format": u"xml", + u"hash": u"abc123", + u"url": u"http://blah.com/file.xml", + }, + ], + "tags": [{"name": u"russion"}, {"name": u"novel"}], + "title": u"Some Title", + "url": u"http://blahblahblah.mydomain", + } + + assert flatten_dict(data) == { + ("extras", 0, "key"): "genre", + ("extras", 0, "value"): u"horror", + ("extras", 1, "key"): "media", + ("extras", 1, "value"): u"dvd", + ("license_id",): u"gpl-3.0", + ("name",): u"testpkg", + ("resources", 0, u"alt_url"): u"alt_url", + ("resources", 0, u"description"): u"Second file", + ("resources", 0, u"extras"): {u"size": u"200"}, + ("resources", 0, u"format"): u"xml", + ("resources", 0, u"hash"): u"def123", + ("resources", 0, u"url"): u"http://blah.com/file2.xml", + ("resources", 1, u"alt_url"): u"alt_url", + ("resources", 1, u"description"): u"Main file", + ("resources", 1, u"extras"): {u"size": u"200"}, + ("resources", 1, u"format"): u"xml", + ("resources", 1, u"hash"): u"abc123", + ("resources", 1, u"url"): u"http://blah.com/file.xml", + ("tags", 0, "name"): u"russion", + ("tags", 1, "name"): u"novel", + ("title",): u"Some Title", + ("url",): u"http://blahblahblah.mydomain", + }, pformat(flatten_dict(data)) assert data == unflatten(flatten_dict(data)) @@ -240,48 +258,53 @@ def test_simple(): "gender": [default("female")], } - data = { - "name": "fred", - "age": "32", - } - + data = {"name": "fred", "age": "32"} converted_data, errors = validate(data, schema) assert not errors - assert converted_data == {'gender': 'female', 'age': 32, 'name': 'fred'}, converted_data + assert converted_data == { + "gender": "female", + "age": 32, + "name": "fred", + }, converted_data - data = { - "name": "", - "age": "dsa32", - "extra": "extra", - } + data = {"name": "", "age": "dsa32", "extra": "extra"} converted_data, errors = validate(data, schema) - assert errors == {'age': [u'Please enter an integer value'], 'name': [u'Missing value']}, errors - - assert converted_data == {'gender': 'female', 'age': 'dsa32', 'name': '', '__extras': {'extra': 'extra'}} + assert errors == { + "age": [u"Please enter an integer value"], + "name": [u"Missing value"], + }, errors + assert converted_data == { + "gender": "female", + "age": "dsa32", + "name": "", + "__extras": {"extra": "extra"}, + } - data = {"name": "fred", - "numbers": [{"number": "13221312"}, - {"number": "432423432", "code": "+44"}] - } + data = { + "name": "fred", + "numbers": [ + {"number": "13221312"}, + {"number": "432423432", "code": "+44"}, + ], + } schema = { - "name": [not_empty], - "numbers": { - "number": [convert_int], - "code": [not_empty], - "__extras": [ignore], - } - } + "name": [not_empty], + "numbers": { + "number": [convert_int], + "code": [not_empty], + "__extras": [ignore], + }, + } converted_data, errors = validate(data, schema) - print(errors) - assert errors == {'numbers': [{'code': [u'Missing value']}, {}]} + assert errors == {"numbers": [{"code": [u"Missing value"]}, {}]} def test_simple_converter_types(): @@ -291,14 +314,15 @@ def test_simple_converter_types(): "gender": [default("female")], } - data = { - "name": "fred", - "age": "32", - } + data = {"name": "fred", "age": "32"} converted_data, errors = validate(data, schema) assert not errors - assert converted_data == {'gender': 'female', 'age': 32, 'name': u'fred'}, converted_data + assert converted_data == { + "gender": "female", + "age": 32, + "name": u"fred", + }, converted_data assert isinstance(converted_data["name"], text_type) assert isinstance(converted_data["gender"], str) diff --git a/ckan/tests/legacy/lib/test_resource_search.py b/ckan/tests/legacy/lib/test_resource_search.py index ea568b3190f..0f1aa2bfc38 100644 --- a/ckan/tests/legacy/lib/test_resource_search.py +++ b/ckan/tests/legacy/lib/test_resource_search.py @@ -1,184 +1,208 @@ # encoding: utf-8 -from nose.tools import assert_raises, assert_equal, assert_set_equal - -from ckan.tests.legacy import * +import pytest from ckan.tests.legacy import is_search_supported import ckan.lib.search as search from ckan import model from ckan.lib.create_test_data import CreateTestData + class TestSearch(object): - @classmethod - def setup_class(self): - if not is_search_supported(): - raise SkipTest("Search not supported") - - self.ab = 'http://site.com/a/b.txt' - self.cd = 'http://site.com/c/d.txt' - self.ef = 'http://site.com/e/f.txt' + @pytest.fixture(autouse=True) + @pytest.mark.skipif( + not is_search_supported(), reason="Search not supported" + ) + def initial_data(self, clean_db): + self.ab = "http://site.com/a/b.txt" + self.cd = "http://site.com/c/d.txt" + self.ef = "http://site.com/e/f.txt" self.pkgs = [ - {'name':'pkg1', - 'resources':[ - {'url':self.ab, - 'description':'This is site ab.', - 'format':'Excel spreadsheet', - 'hash':'xyz-123', - 'alt_url': 'alt_1', - 'extras':{'size_extra': '100'}, - }, - {'url':self.cd, - 'description':'This is site cd.', - 'format':'Office spreadsheet', - 'hash':'qwe-456', - 'alt_url':'alt_2', - 'extras':{'size_extra':'200'}, - }, - ] - }, - {'name':'pkg2', - 'resources':[ - {'url':self.cd, - 'alt_url': 'alt_1', - 'description':'This is site cd.'}, - {'url':self.ef, - 'description':'This is site ef.'}, - {'url':self.ef, - 'description':'This is site gh.'}, - {'url':self.ef, - 'description':'This is site ij.'}, - ] - }, - ] + { + "name": "pkg1", + "resources": [ + { + "url": self.ab, + "description": "This is site ab.", + "format": "Excel spreadsheet", + "hash": "xyz-123", + "alt_url": "alt_1", + "extras": {"size_extra": "100"}, + }, + { + "url": self.cd, + "description": "This is site cd.", + "format": "Office spreadsheet", + "hash": "qwe-456", + "alt_url": "alt_2", + "extras": {"size_extra": "200"}, + }, + ], + }, + { + "name": "pkg2", + "resources": [ + { + "url": self.cd, + "alt_url": "alt_1", + "description": "This is site cd.", + }, + {"url": self.ef, "description": "This is site ef."}, + {"url": self.ef, "description": "This is site gh."}, + {"url": self.ef, "description": "This is site ij."}, + ], + }, + ] CreateTestData.create_arbitrary(self.pkgs) - @classmethod - def teardown_class(self): - model.repo.rebuild_db() - - def res_search(self, query='', fields={}, terms=[], options=search.QueryOptions()): - result = search.query_for(model.Resource).run(query=query, fields=fields, terms=terms, options=options) - resources = [model.Session.query(model.Resource).get(resource_id) for resource_id in result['results']] - urls = {resource.url for resource in resources} + def res_search( + self, query="", fields={}, terms=[], options=search.QueryOptions() + ): + result = search.query_for(model.Resource).run( + query=query, fields=fields, terms=terms, options=options + ) + resources = [ + model.Session.query(model.Resource).get(resource_id) + for resource_id in result["results"] + ] + urls = set([resource.url for resource in resources]) return urls def test_01_search_url(self): - fields = {'url':'site.com'} + fields = {"url": "site.com"} result = search.query_for(model.Resource).run(fields=fields) - assert result['count'] == 6, result - resources = [model.Session.query(model.Resource).get(resource_id) for resource_id in result['results']] - urls = {resource.url for resource in resources} + assert result["count"] == 6, result + resources = [ + model.Session.query(model.Resource).get(resource_id) + for resource_id in result["results"] + ] + urls = set([resource.url for resource in resources]) assert set([self.ab, self.cd, self.ef]) == urls, urls def test_02_search_url_2(self): - urls = self.res_search(fields={'url':'a/b'}) + urls = self.res_search(fields={"url": "a/b"}) assert set([self.ab]) == urls, urls def test_03_search_url_multiple_words(self): - fields = {'url': 'e f'} + fields = {"url": "e f"} urls = self.res_search(fields=fields) - assert_set_equal({self.ef}, urls) + assert {self.ef} == urls def test_04_search_url_none(self): - urls = self.res_search(fields={'url':'nothing'}) + urls = self.res_search(fields={"url": "nothing"}) assert set() == urls, urls def test_05_search_description(self): - urls = self.res_search(fields={'description':'cd'}) + urls = self.res_search(fields={"description": "cd"}) assert set([self.cd]) == urls, urls def test_06_search_format(self): - urls = self.res_search(fields={'format':'excel'}) + urls = self.res_search(fields={"format": "excel"}) assert set([self.ab]) == urls, urls def test_07_search_format_2(self): - urls = self.res_search(fields={'format':'sheet'}) + urls = self.res_search(fields={"format": "sheet"}) assert set([self.ab, self.cd]) == urls, urls def test_08_search_hash_complete(self): - urls = self.res_search(fields={'hash':'xyz-123'}) + urls = self.res_search(fields={"hash": "xyz-123"}) assert set([self.ab]) == urls, urls def test_09_search_hash_partial(self): - urls = self.res_search(fields={'hash':'xyz'}) + urls = self.res_search(fields={"hash": "xyz"}) assert set([self.ab]) == urls, urls def test_10_search_hash_partial_but_not_initial(self): - urls = self.res_search(fields={'hash':'123'}) + urls = self.res_search(fields={"hash": "123"}) assert set() == urls, urls def test_11_search_several_fields(self): - urls = self.res_search(fields={'description':'ab', 'format':'sheet'}) + urls = self.res_search(fields={"description": "ab", "format": "sheet"}) assert set([self.ab]) == urls, urls def test_12_search_all_fields(self): - fields = {'url':'a/b'} + fields = {"url": "a/b"} options = search.QueryOptions(all_fields=True) - result = search.query_for(model.Resource).run(fields=fields, options=options) - assert result['count'] == 1, result - res_dict = result['results'][0] + result = search.query_for(model.Resource).run( + fields=fields, options=options + ) + assert result["count"] == 1, result + res_dict = result["results"][0] assert isinstance(res_dict, dict) res_keys = set(res_dict.keys()) expected_res_keys = set(model.Resource.get_columns()) - expected_res_keys.update(['id', 'package_id', 'position', 'size_extra']) - assert_equal(res_keys, expected_res_keys) - pkg1 = model.Package.by_name(u'pkg1') + expected_res_keys.update( + ["id", "package_id", "position", "size_extra"] + ) + assert res_keys == expected_res_keys + pkg1 = model.Package.by_name(u"pkg1") ab = pkg1.resources[0] - assert res_dict['id'] == ab.id - assert res_dict['package_id'] == pkg1.id - assert res_dict['url'] == ab.url - assert res_dict['description'] == ab.description - assert res_dict['format'] == ab.format - assert res_dict['hash'] == ab.hash - assert res_dict['position'] == 0 + assert res_dict["id"] == ab.id + assert res_dict["package_id"] == pkg1.id + assert res_dict["url"] == ab.url + assert res_dict["description"] == ab.description + assert res_dict["format"] == ab.format + assert res_dict["hash"] == ab.hash + assert res_dict["position"] == 0 def test_13_pagination(self): # large search - options = search.QueryOptions(order_by='id') - fields = {'url':'site'} - all_results = search.query_for(model.Resource).run(fields=fields, options=options) - all_resources = all_results['results'] - all_resource_count = all_results['count'] + options = search.QueryOptions(order_by="id") + fields = {"url": "site"} + all_results = search.query_for(model.Resource).run( + fields=fields, options=options + ) + all_resources = all_results["results"] + all_resource_count = all_results["count"] assert all_resource_count >= 6, all_results # limit - options = search.QueryOptions(order_by='id') + options = search.QueryOptions(order_by="id") options.limit = 2 - result = search.query_for(model.Resource).run(fields=fields, options=options) - resources = result['results'] - count = result['count'] + result = search.query_for(model.Resource).run( + fields=fields, options=options + ) + resources = result["results"] + count = result["count"] assert len(resources) == 2, resources assert count == all_resource_count, (count, all_resource_count) - assert resources == all_resources[:2], '%r, %r' % (resources, all_resources) + assert resources == all_resources[:2], "%r, %r" % ( + resources, + all_resources, + ) # offset - options = search.QueryOptions(order_by='id') + options = search.QueryOptions(order_by="id") options.limit = 2 options.offset = 2 - result = search.query_for(model.Resource).run(fields=fields, options=options) - resources = result['results'] + result = search.query_for(model.Resource).run( + fields=fields, options=options + ) + resources = result["results"] assert len(resources) == 2, resources assert resources == all_resources[2:4] # larger offset - options = search.QueryOptions(order_by='id') + options = search.QueryOptions(order_by="id") options.limit = 2 options.offset = 4 - result = search.query_for(model.Resource).run(fields=fields, options=options) - resources = result['results'] + result = search.query_for(model.Resource).run( + fields=fields, options=options + ) + resources = result["results"] assert len(resources) == 2, resources assert resources == all_resources[4:6] def test_14_extra_info(self): - fields = {'alt_url':'alt_1'} + fields = {"alt_url": "alt_1"} result = search.query_for(model.Resource).run(fields=fields) - assert result['count'] == 2, result + assert result["count"] == 2, result - fields = {'alt_url':'alt_2'} + fields = {"alt_url": "alt_2"} result = search.query_for(model.Resource).run(fields=fields) - assert result['count'] == 1, result + assert result["count"] == 1, result # Document that resource extras not in ckan.extra_resource_fields # can't be searched - fields = {'size_extra':'100'} - assert_raises(search.SearchError, search.query_for(model.Resource).run, fields=fields) + fields = {"size_extra": "100"} + with pytest.raises(search.SearchError): + search.query_for(model.Resource).run(fields=fields) diff --git a/ckan/tests/legacy/lib/test_solr_package_search.py b/ckan/tests/legacy/lib/test_solr_package_search.py index 0453b040363..8194e3188ca 100644 --- a/ckan/tests/legacy/lib/test_solr_package_search.py +++ b/ckan/tests/legacy/lib/test_solr_package_search.py @@ -1,220 +1,295 @@ # encoding: utf-8 -from nose.tools import assert_equal, assert_raises - +import pytest from ckan import model import ckan.lib.search as search -from ckan.tests.legacy import TestController, CreateTestData, setup_test_search_index +from ckan.tests.legacy import CreateTestData, setup_test_search_index from ckan.tests.legacy.lib import check_search_results class TestQuery: def test_1_convert_legacy_params_to_solr(self): convert = search.convert_legacy_parameters_to_solr - assert_equal(convert({'title': 'bob'}), {'q': 'title:bob'}) - assert_equal(convert({'title': 'bob', 'fl': 'name'}), - {'q': 'title:bob', 'fl': 'name'}) - assert_equal(convert({'title': 'bob perkins'}), {'q': 'title:"bob perkins"'}) - assert_equal(convert({'q': 'high+wages'}), {'q': 'high wages'}) - assert_equal(convert({'q': 'high+wages summary'}), {'q': 'high wages summary'}) - assert_equal(convert({'title': 'high+wages'}), {'q': 'title:"high wages"'}) - assert_equal(convert({'title': 'bob', 'all_fields': 1}), {'q': 'title:bob', 'fl': '*'}) - assert_raises(search.SearchError, convert, {'title': 'bob', 'all_fields': 'non-boolean'}) - assert_equal(convert({'q': 'bob', 'order_by': 'name'}), {'q': 'bob', 'sort':'name asc'}) - assert_equal(convert({'q': 'bob', 'offset': '0', 'limit': '10'}), {'q': 'bob', 'start':'0', 'rows':'10'}) - assert_equal(convert({'tags': ['russian', 'tolstoy']}), {'q': 'tags:"russian" tags:"tolstoy"'}) - assert_equal(convert({'tags': ['russian', 'multi word']}), {'q': 'tags:"russian" tags:"multi word"'}) - assert_equal(convert({'tags': ['with CAPITALS']}), {'q': 'tags:"with CAPITALS"'}) - assert_equal(convert({'tags': [u'with greek omega \u03a9']}), {'q': u'tags:"with greek omega \u03a9"'}) - assert_equal(convert({'tags': ['tolstoy']}), {'q': 'tags:"tolstoy"'}) - assert_equal(convert({'tags': 'tolstoy'}), {'q': 'tags:"tolstoy"'}) - assert_equal(convert({'tags': 'more than one tolstoy'}), {'q': 'tags:"more than one tolstoy"'}) - assert_equal(convert({'tags': u'with greek omega \u03a9'}), {'q': u'tags:"with greek omega \u03a9"'}) - assert_equal(convert({'title': 'Seymour: An Introduction'}), {'q': 'title:"Seymour\: An Introduction"'}) - assert_equal(convert({'title': 'Pop!'}), {'q': 'title:Pop\!'}) - - - assert_raises(search.SearchError, convert, {'tags': {'tolstoy':1}}) - -class TestSearch(TestController): + assert convert({"title": "bob"}) == {"q": "title:bob"} + assert convert({"title": "bob", "fl": "name"}) == { + "q": "title:bob", + "fl": "name", + } + assert convert({"title": "bob perkins"}) == { + "q": 'title:"bob perkins"' + } + assert convert({"q": "high+wages"}) == {"q": "high wages"} + assert convert({"q": "high+wages summary"}) == { + "q": "high wages summary" + } + assert convert({"title": "high+wages"}) == {"q": 'title:"high wages"'} + assert convert({"title": "bob", "all_fields": 1}) == { + "q": "title:bob", + "fl": "*", + } + with pytest.raises(search.SearchError): + convert({"title": "bob", "all_fields": "non-boolean"}) + assert convert({"q": "bob", "order_by": "name"}) == { + "q": "bob", + "sort": "name asc", + } + assert convert({"q": "bob", "offset": "0", "limit": "10"}) == { + "q": "bob", + "start": "0", + "rows": "10", + } + assert convert({"tags": ["russian", "tolstoy"]}) == { + "q": 'tags:"russian" tags:"tolstoy"' + } + assert convert({"tags": ["russian", "multi word"]}) == { + "q": 'tags:"russian" tags:"multi word"' + } + assert convert({"tags": ["with CAPITALS"]}) == { + "q": 'tags:"with CAPITALS"' + } + assert convert({"tags": [u"with greek omega \u03a9"]}) == { + "q": u'tags:"with greek omega \u03a9"' + } + assert convert({"tags": ["tolstoy"]}) == {"q": 'tags:"tolstoy"'} + assert convert({"tags": "tolstoy"}) == {"q": 'tags:"tolstoy"'} + assert convert({"tags": "more than one tolstoy"}) == { + "q": 'tags:"more than one tolstoy"' + } + assert convert({"tags": u"with greek omega \u03a9"}) == { + "q": u'tags:"with greek omega \u03a9"' + } + assert convert({"title": "Seymour: An Introduction"}) == { + "q": 'title:"Seymour\: An Introduction"' + } + assert convert({"title": "Pop!"}) == {"q": "title:Pop\!"} + + with pytest.raises(search.SearchError): + convert({"tags": {"tolstoy": 1}}) + + +class TestSearch(object): # 'penguin' is in all test search packages - q_all = u'penguin' + q_all = u"penguin" - @classmethod - def setup_class(cls): - model.Session.remove() - setup_test_search_index() + @pytest.fixture(autouse=True) + def setup_class(self, clean_db, clean_index): CreateTestData.create_search_test_data() # now remove a tag so we can test search with deleted tags - gils = model.Package.by_name(u'gils') + gils = model.Package.by_name(u"gils") # an existing tag used only by gils - cls.tagname = u'registry' - idx = [t.name for t in gils.get_tags()].index(cls.tagname) + self.tagname = u"registry" + idx = [t.name for t in gils.get_tags()].index(self.tagname) gils.remove_tag(gils.get_tags()[idx]) model.repo.commit_and_remove() - @classmethod - def teardown_class(cls): - model.repo.rebuild_db() - search.clear_all() - def _pkg_names(self, result): - return ' '.join(result['results']) + return " ".join(result["results"]) def _check_entity_names(self, result, names_in_result): - names = result['results'] + names = result["results"] for name in names_in_result: if name not in names: return False return True def test_1_all_records(self): - result = search.query_for(model.Package).run({'q': self.q_all}) - assert 'gils' in result['results'], result['results'] - assert result['count'] == 6, result['count'] + result = search.query_for(model.Package).run({"q": self.q_all}) + assert "gils" in result["results"], result["results"] + assert result["count"] == 6, result["count"] def test_1_name(self): # exact name - result = search.query_for(model.Package).run({'q': u'gils'}) - assert result['count'] == 1, result - assert self._pkg_names(result) == 'gils', result + result = search.query_for(model.Package).run({"q": u"gils"}) + assert result["count"] == 1, result + assert self._pkg_names(result) == "gils", result def test_1_name_multiple_results(self): - result = search.query_for(model.Package).run({'q': u'gov'}) - assert self._check_entity_names(result, ('us-gov-images', 'usa-courts-gov')), self._pkg_names(result) - assert result['count'] == 4, self._pkg_names(result) + result = search.query_for(model.Package).run({"q": u"gov"}) + assert self._check_entity_names( + result, ("us-gov-images", "usa-courts-gov") + ), self._pkg_names(result) + assert result["count"] == 4, self._pkg_names(result) def test_1_name_token(self): - result = search.query_for(model.Package).run({'q': u'name:gils'}) - assert self._pkg_names(result) == 'gils', self._pkg_names(result) - result = search.query_for(model.Package).run({'q': u'title:gils'}) - assert not self._check_entity_names(result, ('gils')), self._pkg_names(result) + result = search.query_for(model.Package).run({"q": u"name:gils"}) + assert self._pkg_names(result) == "gils", self._pkg_names(result) + result = search.query_for(model.Package).run({"q": u"title:gils"}) + assert not self._check_entity_names(result, ("gils")), self._pkg_names( + result + ) def test_2_title(self): # exact title, one word - result = search.query_for(model.Package).run({'q': u'Opengov'}) + result = search.query_for(model.Package).run({"q": u"Opengov"}) - assert self._pkg_names(result) == 'se-opengov', self._pkg_names(result) + assert self._pkg_names(result) == "se-opengov", self._pkg_names(result) # multiple words - result = search.query_for(model.Package).run({'q': u'Government Expenditure'}) + result = search.query_for(model.Package).run( + {"q": u"Government Expenditure"} + ) # uk-government-expenditure is the best match but all other results should be retured - assert self._pkg_names(result).startswith('uk-government-expenditure'), self._pkg_names(result) + assert self._pkg_names(result).startswith( + "uk-government-expenditure" + ), self._pkg_names(result) # multiple words wrong order - result = search.query_for(model.Package).run({'q': u'Expenditure Government'}) - assert self._pkg_names(result).startswith('uk-government-expenditure'), self._pkg_names(result) + result = search.query_for(model.Package).run( + {"q": u"Expenditure Government"} + ) + assert self._pkg_names(result).startswith( + "uk-government-expenditure" + ), self._pkg_names(result) # multiple words all should match government - result = search.query_for(model.Package).run({'q': u'Expenditure Government China'}) - assert len(result['results']) == 1, self._pkg_names(result) + result = search.query_for(model.Package).run( + {"q": u"Expenditure Government China"} + ) + assert len(result["results"]) == 1, self._pkg_names(result) def test_3_license(self): # this should result, but it is here to check that at least it does not error - result = search.query_for(model.Package).run({'q': u'license:"OKD::Other (PublicsDomain)"'}) - assert result['count'] == 0, result + result = search.query_for(model.Package).run( + {"q": u'license:"OKD::Other (PublicsDomain)"'} + ) + assert result["count"] == 0, result def test_quotation(self): # multiple words quoted - result = search.query_for(model.Package).run({'q': u'"Government Expenditure"'}) - assert self._pkg_names(result) == 'uk-government-expenditure', self._pkg_names(result) + result = search.query_for(model.Package).run( + {"q": u'"Government Expenditure"'} + ) + assert ( + self._pkg_names(result) == "uk-government-expenditure" + ), self._pkg_names(result) # multiple words quoted wrong order - result = search.query_for(model.Package).run({'q': u'"Expenditure Government"'}) - assert self._pkg_names(result) == '', self._pkg_names(result) + result = search.query_for(model.Package).run( + {"q": u'"Expenditure Government"'} + ) + assert self._pkg_names(result) == "", self._pkg_names(result) def test_string_not_found(self): - result = search.query_for(model.Package).run({'q': u'randomthing'}) - assert self._pkg_names(result) == '', self._pkg_names(result) + result = search.query_for(model.Package).run({"q": u"randomthing"}) + assert self._pkg_names(result) == "", self._pkg_names(result) def test_tags_field(self): - result = search.query_for(model.Package).run({'q': u'country-sweden'}) - assert self._check_entity_names(result, ['se-publications', 'se-opengov']), self._pkg_names(result) + result = search.query_for(model.Package).run({"q": u"country-sweden"}) + assert self._check_entity_names( + result, ["se-publications", "se-opengov"] + ), self._pkg_names(result) def test_tags_field_split_word(self): - result = search.query_for(model.Package).run({'q': u'todo split'}) - assert self._check_entity_names(result, ['us-gov-images']), self._pkg_names(result) + result = search.query_for(model.Package).run({"q": u"todo split"}) + assert self._check_entity_names( + result, ["us-gov-images"] + ), self._pkg_names(result) def test_tags_field_with_capitals(self): - result = search.query_for(model.Package).run({'q': u'CAPITALS'}) - assert self._check_entity_names(result, ['se-publications']), self._pkg_names(result) + result = search.query_for(model.Package).run({"q": u"CAPITALS"}) + assert self._check_entity_names( + result, ["se-publications"] + ), self._pkg_names(result) def dont_test_tags_field_with_basic_unicode(self): - result = search.query_for(model.Package).run({'q': u'greek omega \u03a9'}) - assert self._check_entity_names(result, ['se-publications']), self._pkg_names(result) + result = search.query_for(model.Package).run( + {"q": u"greek omega \u03a9"} + ) + assert self._check_entity_names( + result, ["se-publications"] + ), self._pkg_names(result) def test_tags_token_simple(self): - result = search.query_for(model.Package).run({'q': u'tags:country-sweden'}) - assert self._check_entity_names(result, ['se-publications', 'se-opengov']), self._pkg_names(result) - result = search.query_for(model.Package).run({'q': u'tags:wildlife'}) - assert self._pkg_names(result) == 'us-gov-images', self._pkg_names(result) + result = search.query_for(model.Package).run( + {"q": u"tags:country-sweden"} + ) + assert self._check_entity_names( + result, ["se-publications", "se-opengov"] + ), self._pkg_names(result) + result = search.query_for(model.Package).run({"q": u"tags:wildlife"}) + assert self._pkg_names(result) == "us-gov-images", self._pkg_names( + result + ) def test_tags_token_with_multi_word_tag(self): - result = search.query_for(model.Package).run({'q': u'tags:"todo split"'}) - assert self._check_entity_names(result, ['us-gov-images']), self._pkg_names(result) + result = search.query_for(model.Package).run( + {"q": u'tags:"todo split"'} + ) + assert self._check_entity_names( + result, ["us-gov-images"] + ), self._pkg_names(result) def test_tags_token_simple_with_deleted_tag(self): # registry has been deleted - result = search.query_for(model.Package).run({'q': u'tags:registry'}) - assert self._pkg_names(result) == '', self._pkg_names(result) + result = search.query_for(model.Package).run({"q": u"tags:registry"}) + assert self._pkg_names(result) == "", self._pkg_names(result) def test_tags_token_multiple(self): - result = search.query_for(model.Package).run({'q': u'tags:country-sweden tags:format-pdf'}) - assert self._pkg_names(result) == 'se-publications', self._pkg_names(result) - result = search.query_for(model.Package).run({'q': u'tags:"todo split" tags:war'}) - assert self._pkg_names(result) == 'us-gov-images', self._pkg_names(result) + result = search.query_for(model.Package).run( + {"q": u"tags:country-sweden tags:format-pdf"} + ) + assert self._pkg_names(result) == "se-publications", self._pkg_names( + result + ) + result = search.query_for(model.Package).run( + {"q": u'tags:"todo split" tags:war'} + ) + assert self._pkg_names(result) == "us-gov-images", self._pkg_names( + result + ) def test_tags_token_complicated(self): - result = search.query_for(model.Package).run({'q': u'tags:country-sweden tags:somethingrandom'}) - assert self._pkg_names(result) == '', self._pkg_names(result) + result = search.query_for(model.Package).run( + {"q": u"tags:country-sweden tags:somethingrandom"} + ) + assert self._pkg_names(result) == "", self._pkg_names(result) def test_tags_token_with_capitals(self): - result = search.query_for(model.Package).run({'q': u'tags:"CAPITALS"'}) - assert self._check_entity_names(result, ['se-publications']), self._pkg_names(result) + result = search.query_for(model.Package).run({"q": u'tags:"CAPITALS"'}) + assert self._check_entity_names( + result, ["se-publications"] + ), self._pkg_names(result) def test_tags_token_with_punctuation(self): - result = search.query_for(model.Package).run({'q': u'tags:"surprise."'}) - assert self._check_entity_names(result, ['se-publications']), self._pkg_names(result) + result = search.query_for(model.Package).run( + {"q": u'tags:"surprise."'} + ) + assert self._check_entity_names( + result, ["se-publications"] + ), self._pkg_names(result) def test_tags_token_with_basic_unicode(self): - result = search.query_for(model.Package).run({'q': u'tags:"greek omega \u03a9"'}) - assert self._check_entity_names(result, ['se-publications']), self._pkg_names(result) + result = search.query_for(model.Package).run( + {"q": u'tags:"greek omega \u03a9"'} + ) + assert self._check_entity_names( + result, ["se-publications"] + ), self._pkg_names(result) def test_pagination(self): # large search - all_results = search.query_for(model.Package).run({'q': self.q_all}) - all_pkgs = all_results['results'] - all_pkg_count = all_results['count'] + all_results = search.query_for(model.Package).run({"q": self.q_all}) + all_pkgs = all_results["results"] + all_pkg_count = all_results["count"] # limit - query = { - 'q': self.q_all, - 'rows': 2 - } + query = {"q": self.q_all, "rows": 2} result = search.query_for(model.Package).run(query) - pkgs = result['results'] - count = result['count'] + pkgs = result["results"] + count = result["count"] assert len(pkgs) == 2, pkgs assert count == all_pkg_count assert pkgs == all_pkgs[:2] # offset - query = { - 'q': self.q_all, - 'rows': 2, - 'start': 2 - } + query = {"q": self.q_all, "rows": 2, "start": 2} result = search.query_for(model.Package).run(query) - pkgs = result['results'] + pkgs = result["results"] assert len(pkgs) == 2, pkgs assert pkgs == all_pkgs[2:4] # larger offset - query = { - 'q': self.q_all, - 'rows': 2, - 'start': 4 - } + query = {"q": self.q_all, "rows": 2, "start": 4} result = search.query_for(model.Package).run(query) - pkgs = result['results'] + pkgs = result["results"] assert len(pkgs) == 2, pkgs assert pkgs == all_pkgs[4:6] @@ -224,268 +299,242 @@ def test_order_by(self): # as we are not using the 'edismax' query parser now (requires solr >= 3.*), the # search weighting has been changed from nose import SkipTest + raise SkipTest() # large search - all_results = search.query_for(model.Package).run({'q': self.q_all}) - all_pkgs = all_results['results'] - all_pkg_count = all_results['count'] + all_results = search.query_for(model.Package).run({"q": self.q_all}) + all_pkgs = all_results["results"] + all_pkg_count = all_results["count"] # rank - query = { - 'q': 'government', - 'sort': 'rank' - } + query = {"q": "government", "sort": "rank"} result = search.query_for(model.Package).run(query) - pkgs = result['results'] + pkgs = result["results"] fields = [model.Package.by_name(pkg_name).name for pkg_name in pkgs] - assert fields[0] == 'gils', fields # has government in tags, title and notes + assert ( + fields[0] == "gils" + ), fields # has government in tags, title and notes # name - query = { - 'q': self.q_all, - 'sort': 'name asc' - } + query = {"q": self.q_all, "sort": "name asc"} result = search.query_for(model.Package).run(query) - pkgs = result['results'] + pkgs = result["results"] fields = [model.Package.by_name(pkg_name).name for pkg_name in pkgs] - sorted_fields = fields; sorted_fields.sort() + sorted_fields = fields + sorted_fields.sort() assert fields == sorted_fields, repr(fields) + repr(sorted_fields) # title - query = { - 'q': self.q_all, - 'sort': 'title asc' - } + query = {"q": self.q_all, "sort": "title asc"} result = search.query_for(model.Package).run(query) - pkgs = result['results'] + pkgs = result["results"] fields = [model.Package.by_name(pkg_name).title for pkg_name in pkgs] - sorted_fields = fields; sorted_fields.sort() + sorted_fields = fields + sorted_fields.sort() assert fields == sorted_fields, repr(fields) + repr(sorted_fields) # notes - query = { - 'q': self.q_all, - 'sort': 'notes asc' - } + query = {"q": self.q_all, "sort": "notes asc"} result = search.query_for(model.Package).run(query) - pkgs = result['results'] + pkgs = result["results"] fields = [model.Package.by_name(pkg_name).notes for pkg_name in pkgs] - sorted_fields = fields; sorted_fields.sort() + sorted_fields = fields + sorted_fields.sort() assert fields == sorted_fields, repr(fields) + repr(sorted_fields) # extra field - query = { - 'q': self.q_all, - 'sort': 'date_released asc' - } + query = {"q": self.q_all, "sort": "date_released asc"} result = search.query_for(model.Package).run(query) - pkgs = result['results'] + pkgs = result["results"] fields = [model.Package.by_name(pkg_name) for pkg_name in pkgs] - fields = [field.extras.get('date_released') for field in fields] - sorted_fields = fields; sorted_fields.sort() + fields = [field.extras.get("date_released") for field in fields] + sorted_fields = fields + sorted_fields.sort() assert fields == sorted_fields, repr(fields) + repr(sorted_fields) def test_search_notes_on(self): - result = search.query_for(model.Package).run({'q': u'restrictions'}) - pkgs = result['results'] - count = result['count'] + result = search.query_for(model.Package).run({"q": u"restrictions"}) + pkgs = result["results"] + count = result["count"] assert len(pkgs) == 2, pkgs def test_search_foreign_chars(self): - result = search.query_for(model.Package).run({'q': 'umlaut'}) - assert result['results'] == ['gils'], result['results'] - result = search.query_for(model.Package).run({'q': u'thumb'}) - assert result['results'] == ['gils'], result['results'] - result = search.query_for(model.Package).run({'q': u'th\xfcmb'}) - assert result['results'] == ['gils'], result['results'] + result = search.query_for(model.Package).run({"q": "umlaut"}) + assert result["results"] == ["gils"], result["results"] + result = search.query_for(model.Package).run({"q": u"thumb"}) + assert result["results"] == ["gils"], result["results"] + result = search.query_for(model.Package).run({"q": u"th\xfcmb"}) + assert result["results"] == ["gils"], result["results"] def test_groups(self): - result = search.query_for(model.Package).run({'q': u'groups:random'}) - assert self._pkg_names(result) == '', self._pkg_names(result) - result = search.query_for(model.Package).run({'q': u'groups:ukgov'}) - assert result['count'] == 4, self._pkg_names(result) - result = search.query_for(model.Package).run({'q': u'groups:ukgov tags:us'}) - assert result['count'] == 2, self._pkg_names(result) - -class TestSearchOverall(TestController): - @classmethod - def setup_class(cls): + result = search.query_for(model.Package).run({"q": u"groups:random"}) + assert self._pkg_names(result) == "", self._pkg_names(result) + result = search.query_for(model.Package).run({"q": u"groups:ukgov"}) + assert result["count"] == 4, self._pkg_names(result) + result = search.query_for(model.Package).run( + {"q": u"groups:ukgov tags:us"} + ) + assert result["count"] == 2, self._pkg_names(result) + + +class TestSearchOverall(object): + @pytest.fixture(autouse=True) + def setup_class(self, clean_db, clean_index): setup_test_search_index() CreateTestData.create() - @classmethod - def teardown_class(cls): - model.repo.rebuild_db() - search.clear_all() - def test_overall(self): - check_search_results('annakarenina', 1, ['annakarenina']) - check_search_results('warandpeace', 1, ['warandpeace']) - check_search_results('', 2) - - check_search_results('Tolstoy', 1, ['annakarenina']) - check_search_results('title:Novel', 1, ['annakarenina']) - check_search_results('title:peace', 0) - check_search_results('name:warandpeace', 1) - check_search_results('groups:david', 2) - check_search_results('groups:roger', 1) - check_search_results('groups:lenny', 0) + check_search_results("annakarenina", 1, ["annakarenina"]) + check_search_results("warandpeace", 1, ["warandpeace"]) + check_search_results("", 2) + + check_search_results("Tolstoy", 1, ["annakarenina"]) + check_search_results("title:Novel", 1, ["annakarenina"]) + check_search_results("title:peace", 0) + check_search_results("name:warandpeace", 1) + check_search_results("groups:david", 2) + check_search_results("groups:roger", 1) + check_search_results("groups:lenny", 0) check_search_results('tags:"russian"', 2) check_search_results(u'tags:"Flexible \u30a1"', 2) - check_search_results(u'Flexible \u30a1', 2) - check_search_results(u'Flexible', 2) - check_search_results(u'flexible', 2) + check_search_results(u"Flexible \u30a1", 2) + check_search_results(u"Flexible", 2) + check_search_results(u"flexible", 2) -class TestGeographicCoverage(TestController): - @classmethod - def setup_class(cls): - setup_test_search_index() +class TestGeographicCoverage(object): + @pytest.fixture(autouse=True) + def setup_class(self, clean_db, clean_index): init_data = [ - {'name':'eng', - 'extras':{'geographic_coverage':'100000: England'},}, - {'name':'eng_ni', - 'extras':{'geographic_coverage':'100100: England, Northern Ireland'},}, - {'name':'uk', - 'extras':{'geographic_coverage':'111100: United Kingdom (England, Scotland, Wales, Northern Ireland'},}, - {'name':'gb', - 'extras':{'geographic_coverage':'111000: Great Britain (England, Scotland, Wales)'},}, - {'name':'none', - 'extras':{'geographic_coverage':'000000:'},}, + { + "name": "eng", + "extras": {"geographic_coverage": "100000: England"}, + }, + { + "name": "eng_ni", + "extras": { + "geographic_coverage": "100100: England, Northern Ireland" + }, + }, + { + "name": "uk", + "extras": { + "geographic_coverage": "111100: United Kingdom (England, Scotland, Wales, Northern Ireland" + }, + }, + { + "name": "gb", + "extras": { + "geographic_coverage": "111000: Great Britain (England, Scotland, Wales)" + }, + }, + {"name": "none", "extras": {"geographic_coverage": "000000:"}}, ] CreateTestData.create_arbitrary(init_data) - @classmethod - def teardown_class(self): - model.repo.rebuild_db() - search.clear_all() - def _do_search(self, q, expected_pkgs, count=None): - query = { - 'q': q, - 'sort': 'score desc, name asc' - } + query = {"q": q, "sort": "score desc, name asc"} result = search.query_for(model.Package).run(query) - pkgs = result['results'] + pkgs = result["results"] fields = [model.Package.by_name(pkg_name).name for pkg_name in pkgs] if not (count is None): - assert result['count'] == count, result['count'] + assert result["count"] == count, result["count"] for expected_pkg in expected_pkgs: assert expected_pkg in fields, expected_pkg def _filtered_search(self, value, expected_pkgs, count=None): - query = { - 'q': 'geographic_coverage:%s' % value, - 'sort': 'rank' - } + query = {"q": "geographic_coverage:%s" % value, "sort": "rank"} result = search.query_for(model.Package).run(query) - pkgs = result['results'] + pkgs = result["results"] fields = [model.Package.by_name(pkg_name).name for pkg_name in pkgs] if not (count is None): - assert result['count'] == count, result['count'] + assert result["count"] == count, result["count"] for expected_pkg in expected_pkgs: assert expected_pkg in fields, expected_pkg def test_0_basic(self): - self._do_search(u'england', ['eng', 'eng_ni', 'uk', 'gb'], 4) - self._do_search(u'northern ireland', ['eng_ni', 'uk'], 2) - self._do_search(u'united kingdom', ['uk'], 1) - self._do_search(u'great britain', ['gb'], 1) - - def test_1_filtered(self): - # TODO: solr is not currently set up to allow partial matches - # and extras are not saved as multivalued so this - # test will fail. Make multivalued or remove? - from ckan.tests.legacy import SkipTest - raise SkipTest - - self._filtered_search(u'england', ['eng', 'eng_ni', 'uk', 'gb'], 4) - -class TestExtraFields(TestController): - @classmethod - def setup_class(cls): - setup_test_search_index() + self._do_search(u"england", ["eng", "eng_ni", "uk", "gb"], 4) + self._do_search(u"northern ireland", ["eng_ni", "uk"], 2) + self._do_search(u"united kingdom", ["uk"], 1) + self._do_search(u"great britain", ["gb"], 1) + + +class TestExtraFields(object): + @pytest.fixture(autouse=True) + def initial_data(self, clean_db, clean_index): init_data = [ - {'name':'a', - 'extras':{'department':'abc', - 'agency':'ag-a'},}, - {'name':'b', - 'extras':{'department':'bcd', - 'agency':'ag-b'},}, - {'name':'c', - 'extras':{'department':'cde abc'},}, - {'name':'none', - 'extras':{'department':''},}, - ] + {"name": "a", "extras": {"department": "abc", "agency": "ag-a"}}, + {"name": "b", "extras": {"department": "bcd", "agency": "ag-b"}}, + {"name": "c", "extras": {"department": "cde abc"}}, + {"name": "none", "extras": {"department": ""}}, + ] CreateTestData.create_arbitrary(init_data) - @classmethod - def teardown_class(self): - model.repo.rebuild_db() - search.clear_all() - def _do_search(self, department, expected_pkgs, count=None): - result = search.query_for(model.Package).run({'q': 'department: %s' % department}) - pkgs = result['results'] + result = search.query_for(model.Package).run( + {"q": "department: %s" % department} + ) + pkgs = result["results"] fields = [model.Package.by_name(pkg_name).name for pkg_name in pkgs] if not (count is None): - assert result['count'] == count, result['count'] + assert result["count"] == count, result["count"] for expected_pkg in expected_pkgs: assert expected_pkg in fields, expected_pkg def test_0_basic(self): - self._do_search(u'bcd', 'b', 1) - self._do_search(u'"cde abc"', 'c', 1) + self._do_search(u"bcd", "b", 1) + self._do_search(u'"cde abc"', "c", 1) def test_1_extras_in_all_fields(self): - response = search.query_for(model.Package).run({'q': 'abc', 'fl': '*'}) - assert response['count'] == 2 + response = search.query_for(model.Package).run({"q": "abc", "fl": "*"}) + assert response["count"] == 2 - results = response['results'] + results = response["results"] for result in results: - assert 'extras' in result.keys(), result - assert 'department' in result['extras'], result['extras'] - assert result['extras']['department'] in ['abc', 'cde abc'], result['extras']['department'] + assert "extras" in result.keys(), result + assert "department" in result["extras"], result["extras"] + assert result["extras"]["department"] in [ + "abc", + "cde abc", + ], result["extras"]["department"] -class TestRank(TestController): - @classmethod - def setup_class(cls): + +class TestRank(object): + @pytest.fixture(autouse=True) + def initial_data(self, clean_db, clean_index): setup_test_search_index() - init_data = [{'name':u'test1-penguin-canary', - 'title':u'penguin', - 'tags':u'canary goose squirrel wombat wombat'.split()}, - {'name':u'test2-squirrel-squirrel-canary-goose', - 'title':u'squirrel goose', - 'tags':u'penguin wombat'.split()}, - ] + init_data = [ + { + "name": u"test1-penguin-canary", + "title": u"penguin", + "tags": u"canary goose squirrel wombat wombat".split(), + }, + { + "name": u"test2-squirrel-squirrel-canary-goose", + "title": u"squirrel goose", + "tags": u"penguin wombat".split(), + }, + ] CreateTestData.create_arbitrary(init_data) - cls.pkg_names = [ - u'test1-penguin-canary', - u'test2-squirrel-squirrel-canary-goose' + self.pkg_names = [ + u"test1-penguin-canary", + u"test2-squirrel-squirrel-canary-goose", ] - @classmethod - def teardown_class(self): - model.repo.rebuild_db() - search.clear_all() - def _do_search(self, q, wanted_results): - query = { - 'q': q, - 'sort': 'score desc, name asc', - } + query = {"q": q, "sort": "score desc, name asc"} result = search.query_for(model.Package).run(query) - results = result['results'] - err = 'Wanted %r, got %r' % (wanted_results, results) + results = result["results"] + err = "Wanted %r, got %r" % (wanted_results, results) assert wanted_results[0] == results[0], err assert wanted_results[1] == results[1], err def test_0_basic(self): - self._do_search(u'wombat', self.pkg_names) - self._do_search(u'squirrel', self.pkg_names[::-1]) - self._do_search(u'canary', self.pkg_names) + self._do_search(u"wombat", self.pkg_names) + self._do_search(u"squirrel", self.pkg_names[::-1]) + self._do_search(u"canary", self.pkg_names) def test_1_weighting(self): - self._do_search(u'penguin', self.pkg_names) - self._do_search(u'goose', self.pkg_names[::-1]) + self._do_search(u"penguin", self.pkg_names) + self._do_search(u"goose", self.pkg_names[::-1]) diff --git a/ckan/tests/legacy/lib/test_solr_package_search_synchronous_update.py b/ckan/tests/legacy/lib/test_solr_package_search_synchronous_update.py index 42180834dd3..a44c7e47d3d 100644 --- a/ckan/tests/legacy/lib/test_solr_package_search_synchronous_update.py +++ b/ckan/tests/legacy/lib/test_solr_package_search_synchronous_update.py @@ -2,108 +2,100 @@ from ckan import model import ckan.lib.search as search - +import pytest from ckan.tests.legacy import CreateTestData, setup_test_search_index from ckan.tests.legacy.lib import check_search_results import json -class TestSearchOverallWithSynchronousIndexing: - '''Repeat test from test_package_search with synchronous indexing - ''' - @classmethod - def setup_class(cls): - setup_test_search_index() +class TestSearchOverallWithSynchronousIndexing(object): + """Repeat test from test_package_search with synchronous indexing + """ + + @pytest.fixture(autouse=True) + def setup_class(self, clean_db, clean_index): # Force a garbage collection to trigger issue #695 import gc + gc.collect() CreateTestData.create() - cls.new_pkg_dict = { - 'name': 'council-owned-litter-bins', - 'notes': 'Location of Council owned litter bins within Borough.', - 'resources': [{'description': 'Resource locator', - 'format': 'Unverified', - 'url': 'http://www.barrowbc.gov.uk'}], - 'tags': ['Utility and governmental services'], - 'title': 'Council Owned Litter Bins', - 'extras': { - 'INSPIRE': 'True', - 'bbox-east-long': '-3.12442', - 'bbox-north-lat': '54.218407', - 'bbox-south-lat': '54.039634', - 'bbox-west-long': '-3.32485', - 'constraint': 'conditions unknown; (e) intellectual property rights;', - 'dataset-reference-date': json.dumps( - [{'type': 'creation', - 'value': '2008-10-10'}, - {'type': 'revision', - 'value': '2009-10-08'}]), - 'guid': '00a743bf-cca4-4c19-a8e5-e64f7edbcadd', - 'metadata-date': '2009-10-16', - 'metadata-language': 'eng', - 'published_by': 0, - 'resource-type': 'dataset', - 'spatial-reference-system': 'test-spatial', - 'temporal_coverage-from': '1977-03-10T11:45:30', - 'temporal_coverage-to': '2005-01-15T09:10:00' - } + new_pkg_dict = { + "name": "council-owned-litter-bins", + "notes": "Location of Council owned litter bins within Borough.", + "resources": [ + { + "description": "Resource locator", + "format": "Unverified", + "url": "http://www.barrowbc.gov.uk", + } + ], + "tags": ["Utility and governmental services"], + "title": "Council Owned Litter Bins", + "extras": { + "INSPIRE": "True", + "bbox-east-long": "-3.12442", + "bbox-north-lat": "54.218407", + "bbox-south-lat": "54.039634", + "bbox-west-long": "-3.32485", + "constraint": "conditions unknown; (e) intellectual property rights;", + "dataset-reference-date": json.dumps( + [ + {"type": "creation", "value": "2008-10-10"}, + {"type": "revision", "value": "2009-10-08"}, + ] + ), + "guid": "00a743bf-cca4-4c19-a8e5-e64f7edbcadd", + "metadata-date": "2009-10-16", + "metadata-language": "eng", + "published_by": 0, + "resource-type": "dataset", + "spatial-reference-system": "test-spatial", + "temporal_coverage-from": "1977-03-10T11:45:30", + "temporal_coverage-to": "2005-01-15T09:10:00", + }, } - @classmethod - def teardown_class(cls): - model.repo.rebuild_db() - search.clear_all() - - def setup(self): - self._create_package() - - def teardown(self): - self._remove_package() - self._remove_package(u'new_name') - - def _create_package(self, package=None): - CreateTestData.create_arbitrary(self.new_pkg_dict) - return model.Package.by_name(self.new_pkg_dict['name']) + CreateTestData.create_arbitrary(new_pkg_dict) def _remove_package(self, name=None): - package = model.Package.by_name(name or 'council-owned-litter-bins') + package = model.Package.by_name(name or "council-owned-litter-bins") if package: package.purge() model.repo.commit_and_remove() def test_02_add_package_from_dict(self): - check_search_results('', 3) - check_search_results('spatial', 1, ['council-owned-litter-bins']) + check_search_results("", 3) + check_search_results("spatial", 1, ["council-owned-litter-bins"]) def test_03_update_package_from_dict(self): - package = model.Package.by_name('council-owned-litter-bins') + package = model.Package.by_name("council-owned-litter-bins") # update package - package.name = u'new_name' - extra = model.PackageExtra(key='published_by', value='barrow') + package.name = u"new_name" + extra = model.PackageExtra(key="published_by", value="barrow") package._extras[extra.key] = extra model.repo.commit_and_remove() - check_search_results('', 3) - check_search_results('barrow', 1, ['new_name']) + check_search_results("", 3) + check_search_results("barrow", 1, ["new_name"]) # update package again - package = model.Package.by_name('new_name') - package.name = u'council-owned-litter-bins' + package = model.Package.by_name("new_name") + package.name = u"council-owned-litter-bins" model.repo.commit_and_remove() - check_search_results('', 3) - check_search_results('spatial', 1, ['council-owned-litter-bins']) + check_search_results("", 3) + check_search_results("spatial", 1, ["council-owned-litter-bins"]) def test_04_delete_package_from_dict(self): - package = model.Package.by_name('council-owned-litter-bins') + package = model.Package.by_name("council-owned-litter-bins") assert package - check_search_results('', 3) + check_search_results("", 3) # delete it package.delete() model.repo.commit_and_remove() - check_search_results('', 2) + check_search_results("", 2) diff --git a/ckan/tests/legacy/lib/test_solr_schema_version.py b/ckan/tests/legacy/lib/test_solr_schema_version.py index dfbef778b0f..2e17e27d366 100644 --- a/ckan/tests/legacy/lib/test_solr_schema_version.py +++ b/ckan/tests/legacy/lib/test_solr_schema_version.py @@ -1,18 +1,16 @@ # encoding: utf-8 import os -from ckan.tests.legacy import TestController -class TestSolrSchemaVersionCheck(TestController): - @classmethod - def setup_class(cls): - - cls.root_dir = os.path.dirname(os.path.realpath(__file__)) +class TestSolrSchemaVersionCheck(object): + root_dir = os.path.dirname(os.path.realpath(__file__)) def _get_current_schema(self): - current_schema = os.path.join(self.root_dir,'..','..','..','config','solr','schema.xml') + current_schema = os.path.join( + self.root_dir, "..", "..", "..", "config", "solr", "schema.xml" + ) return current_schema @@ -22,7 +20,6 @@ def test_current_schema_exists(self): assert os.path.exists(current_schema) - def test_solr_schema_version_check(self): from ckan.lib.search import check_solr_schema_version, SearchError @@ -34,20 +31,24 @@ def test_solr_schema_version_check(self): # An exception is thrown if version could not be extracted try: - schema_file = os.path.join(self.root_dir,'solr','schema-no-version.xml') + schema_file = os.path.join( + self.root_dir, "solr", "schema-no-version.xml" + ) check_solr_schema_version(schema_file) - #Should not happen + # Should not happen assert False except SearchError as e: - assert 'Could not extract version info' in str(e) + assert "Could not extract version info" in str(e) # An exception is thrown if the schema version is not supported try: - schema_file = os.path.join(self.root_dir,'solr','schema-wrong-version.xml') + schema_file = os.path.join( + self.root_dir, "solr", "schema-wrong-version.xml" + ) check_solr_schema_version(schema_file) - #Should not happen + # Should not happen assert False except SearchError as e: - assert 'SOLR schema version not supported' in str(e) + assert "SOLR schema version not supported" in str(e) diff --git a/ckan/tests/legacy/lib/test_solr_search_index.py b/ckan/tests/legacy/lib/test_solr_search_index.py index 94630cbcdab..c6dcc488d88 100644 --- a/ckan/tests/legacy/lib/test_solr_search_index.py +++ b/ckan/tests/legacy/lib/test_solr_search_index.py @@ -1,20 +1,25 @@ # encoding: utf-8 -from nose.tools import assert_equal - +import pytest import pysolr from ckan.common import config -from ckan import model import ckan.lib.search as search -from ckan.tests.legacy import TestController, CreateTestData, setup_test_search_index, is_search_supported +from ckan.tests.legacy import ( + CreateTestData, + setup_test_search_index, + is_search_supported, +) + -class TestSolrConfig(TestController): +class TestSolrConfig(object): """ Make sure that solr is enabled for this ckan instance. """ + def test_solr_url_exists(self): if not is_search_supported(): from nose import SkipTest + raise SkipTest("Search not supported") conn = search.make_connection() @@ -23,36 +28,37 @@ def test_solr_url_exists(self): # can't connect to the SOLR instance q = conn.search(q="*:*", rows=1) except pysolr.SolrError as e: - if not config.get('solr_url'): - raise AssertionError("Config option 'solr_url' needs to be defined in this CKAN's development.ini. Default of %s didn't work: %s" % (search.DEFAULT_SOLR_URL, e)) + if not config.get("solr_url"): + raise AssertionError( + "Config option 'solr_url' needs to be defined in this CKAN's development.ini. Default of %s didn't work: %s" + % (search.DEFAULT_SOLR_URL, e) + ) else: - raise AssertionError('SOLR connection problem. Connection defined in development.ini as: solr_url=%s Error: %s' % (config['solr_url'], e)) + raise AssertionError( + "SOLR connection problem. Connection defined in development.ini as: solr_url=%s Error: %s" + % (config["solr_url"], e) + ) -class TestSolrSearch: - @classmethod - def setup_class(cls): +class TestSolrSearch(object): + @pytest.fixture(autouse=True) + def initial_data(self, clean_db, clean_index): setup_test_search_index() CreateTestData.create_search_test_data() - cls.solr = search.make_connection() - cls.fq = " +site_id:\"%s\" " % config['ckan.site_id'] + self.solr = search.make_connection() + self.fq = ' +site_id:"%s" ' % config["ckan.site_id"] search.rebuild() - @classmethod - def teardown_class(cls): - model.repo.rebuild_db() - search.index_for('Package').clear() - def test_0_indexing(self): """ Make sure that all packages created by CreateTestData.create_search_test_data have been added to the search index. """ - results = self.solr.search(q='*:*', fq=self.fq) + results = self.solr.search(q="*:*", fq=self.fq) assert len(results) == 6, len(results) def test_1_basic(self): - results = self.solr.search(q='sweden', fq=self.fq) - result_names = sorted(r['name'] for r in results) + results = self.solr.search(q="sweden", fq=self.fq) + result_names = sorted([r["name"] for r in results]) - assert_equal([u'se-opengov', u'se-publications'], result_names) + assert [u"se-opengov", u"se-publications"] == result_names diff --git a/ckan/tests/legacy/lib/test_tag_search.py b/ckan/tests/legacy/lib/test_tag_search.py index 95879c3004d..84ee0c2423e 100644 --- a/ckan/tests/legacy/lib/test_tag_search.py +++ b/ckan/tests/legacy/lib/test_tag_search.py @@ -1,77 +1,74 @@ # encoding: utf-8 -from ckan.tests.legacy import * from ckan.tests.legacy import is_search_supported import ckan.lib.search as search from ckan import model from ckan.lib.create_test_data import CreateTestData +import pytest + class TestTagSearch(object): - @classmethod - def setup_class(self): - if not is_search_supported(): - raise SkipTest("Search not supported") + @pytest.fixture(autouse=True) + @pytest.mark.skipif( + not is_search_supported(), reason="Search not supported" + ) + def initial_data(self, clean_db, clean_index): CreateTestData.create() - @classmethod - def teardown_class(self): - model.repo.rebuild_db() - def test_good_search_query(self): - result = search.query_for(model.Tag).run(query=u'ru') - assert result['count'] == 1, result - assert 'russian' in result['results'], result + result = search.query_for(model.Tag).run(query=u"ru") + assert result["count"] == 1, result + assert "russian" in result["results"], result - result = search.query_for(model.Tag).run(query=u's') - assert result['count'] == 2, result - assert 'russian' in result['results'], result - assert 'tolstoy' in result['results'], result + result = search.query_for(model.Tag).run(query=u"s") + assert result["count"] == 2, result + assert "russian" in result["results"], result + assert "tolstoy" in result["results"], result def test_good_search_queries(self): - result = search.query_for(model.Tag).run(query=[u'ru', u's']) - assert result['count'] == 1, result - assert 'russian' in result['results'], result + result = search.query_for(model.Tag).run(query=[u"ru", u"s"]) + assert result["count"] == 1, result + assert "russian" in result["results"], result def test_bad_search_query(self): - result = search.query_for(model.Tag).run(query=u'asdf') - assert result['count'] == 0, result + result = search.query_for(model.Tag).run(query=u"asdf") + assert result["count"] == 0, result def test_search_with_capital_letter_in_tagname(self): """ Asserts that it doesn't matter if the tagname has capital letters in it. """ - result = search.query_for(model.Tag).run(query=u'lexible') - assert u'Flexible \u30a1' in result['results'] + result = search.query_for(model.Tag).run(query=u"lexible") + assert u"Flexible \u30a1" in result["results"] def test_search_with_capital_letter_in_search_query(self): """ Asserts that search works with a capital letter in the search query. """ - result = search.query_for(model.Tag).run(query=u'Flexible') - assert u'Flexible \u30a1' in result['results'] + result = search.query_for(model.Tag).run(query=u"Flexible") + assert u"Flexible \u30a1" in result["results"] def test_search_with_unicode_in_search_query(self): """ Asserts that search works with a unicode character above \u00ff. """ - result = search.query_for(model.Tag).run(query=u' \u30a1') - assert u'Flexible \u30a1' in result['results'] + result = search.query_for(model.Tag).run(query=u" \u30a1") + assert u"Flexible \u30a1" in result["results"] def test_search_is_case_insensitive(self): - result = search.query_for(model.Tag).run(query=u'flexible') - assert u'Flexible \u30a1' in result['results'] - + result = search.query_for(model.Tag).run(query=u"flexible") + assert u"Flexible \u30a1" in result["results"] def test_good_search_fields(self): - result = search.query_for(model.Tag).run(fields={'tags': u'ru'}) - assert result['count'] == 1, result - assert 'russian' in result['results'], result + result = search.query_for(model.Tag).run(fields={"tags": u"ru"}) + assert result["count"] == 1, result + assert "russian" in result["results"], result - result = search.query_for(model.Tag).run(fields={'tags': u's'}) - assert result['count'] == 2, result - assert 'russian' in result['results'], result - assert 'tolstoy' in result['results'], result + result = search.query_for(model.Tag).run(fields={"tags": u"s"}) + assert result["count"] == 2, result + assert "russian" in result["results"], result + assert "tolstoy" in result["results"], result def test_bad_search_fields(self): - result = search.query_for(model.Tag).run(fields={'tags': u'asdf'}) - assert result['count'] == 0, result + result = search.query_for(model.Tag).run(fields={"tags": u"asdf"}) + assert result["count"] == 0, result diff --git a/ckan/tests/legacy/logic/test_action.py b/ckan/tests/legacy/logic/test_action.py index b3e586757f0..7298e5d0975 100644 --- a/ckan/tests/legacy/logic/test_action.py +++ b/ckan/tests/legacy/logic/test_action.py @@ -1,1197 +1,1364 @@ # encoding: utf-8 import json -from pprint import pprint -from nose.tools import assert_equal, assert_raises from ckan.common import config - +import pytest import ckan from ckan.lib.create_test_data import CreateTestData from ckan.lib.dictization.model_dictize import resource_dictize import ckan.model as model import ckan.tests.legacy as tests -from ckan.tests.legacy import WsgiAppCase -from ckan.tests.legacy import setup_test_search_index from ckan.tests.legacy import StatusCodes from ckan.logic import get_action, NotAuthorized from ckan.logic.action import get_domain_object from ckan.tests.legacy import call_action_api -import ckan.lib.search as search -import ckan.tests.helpers as helpers +import ckan.tests.factories as factories from ckan.plugins import SingletonPlugin, implements, IPackageController -class TestAction(WsgiAppCase): +def _add_basic_package(app, package_name=u"test_package", **kwargs): + package = { + "name": package_name, + "title": u"A Novel By Tolstoy", + "resources": [ + { + "description": u"Full text.", + "format": u"plain text", + "url": u"http://datahub.io/download/", + } + ], + } + package.update(kwargs) + + postparams = "%s=1" % json.dumps(package) + res = app.post( + "/api/action/package_create", + params=postparams, + extra_environ={"Authorization": "tester"}, + ) + return json.loads(res.body)["result"] + + +class TestAction(object): sysadmin_user = None normal_user = None - @classmethod - def setup_class(cls): - model.repo.rebuild_db() - search.clear_all() + @pytest.fixture(autouse=True) + def initial_data(self, clean_db, clean_index): CreateTestData.create() - cls.sysadmin_user = model.User.get('testsysadmin') - cls.normal_user = model.User.get('annafan') + self.sysadmin_user = model.User.get("testsysadmin") + self.normal_user = model.User.get("annafan") CreateTestData.make_some_vocab_tags() - @classmethod - def teardown_class(cls): - model.repo.rebuild_db() - - def _add_basic_package(self, package_name=u'test_package', **kwargs): - package = { - 'name': package_name, - 'title': u'A Novel By Tolstoy', - 'resources': [{ - 'description': u'Full text.', - 'format': u'plain text', - 'url': u'http://datahub.io/download/' - }] - } - package.update(kwargs) - - postparams = '%s=1' % json.dumps(package) - res = self.app.post('/api/action/package_create', params=postparams, - extra_environ={'Authorization': 'tester'}) - return json.loads(res.body)['result'] - - def test_01_package_list(self): - res = json.loads(self.app.post('/api/action/package_list', - headers={'content-type': 'application/json'}).body) - assert res['success'] is True - assert len(res['result']) == 2 - assert 'warandpeace' in res['result'] - assert 'annakarenina' in res['result'] - assert "/api/3/action/help_show?name=package_list" in res['help'] - - postparams = '%s=1' % json.dumps({'limit': 1}) - res = json.loads(self.app.post('/api/action/package_list', - params=postparams).body) - assert res['success'] is True - assert len(res['result']) == 1 - assert 'warandpeace' in res['result'] or 'annakarenina' in res['result'] + def test_01_package_list(self, app): + res = json.loads( + app.post( + "/api/action/package_list", + headers={"content-type": "application/json"}, + ).body + ) + assert res["success"] is True + assert len(res["result"]) == 2 + assert "warandpeace" in res["result"] + assert "annakarenina" in res["result"] + assert "/api/3/action/help_show?name=package_list" in res["help"] + + postparams = "%s=1" % json.dumps({"limit": 1}) + res = json.loads( + app.post("/api/action/package_list", params=postparams).body + ) + assert res["success"] is True + assert len(res["result"]) == 1 + assert ( + "warandpeace" in res["result"] or "annakarenina" in res["result"] + ) # Test GET request - res = json.loads(self.app.get('/api/action/package_list').body) - assert len(res['result']) == 2 - assert 'warandpeace' in res['result'] - assert 'annakarenina' in res['result'] - - def test_01_package_list_private(self): - tests.call_action_api(self.app, 'organization_create', - name='test_org_2', - apikey=self.sysadmin_user.apikey) + res = json.loads(app.get("/api/action/package_list").body) + assert len(res["result"]) == 2 + assert "warandpeace" in res["result"] + assert "annakarenina" in res["result"] + + # def test_01_package_list_private(self): + tests.call_action_api( + app, + "organization_create", + name="test_org_2", + apikey=self.sysadmin_user.apikey, + ) - tests.call_action_api(self.app, 'package_create', - name='public_dataset', - owner_org='test_org_2', - apikey=self.sysadmin_user.apikey) + tests.call_action_api( + app, + "package_create", + name="public_dataset", + owner_org="test_org_2", + apikey=self.sysadmin_user.apikey, + ) - res = tests.call_action_api(self.app, 'package_list') + res = tests.call_action_api(app, "package_list") assert len(res) == 3 - assert 'warandpeace' in res - assert 'annakarenina' in res - assert 'public_dataset' in res - - tests.call_action_api(self.app, 'package_create', - name='private_dataset', - owner_org='test_org_2', - private=True, - apikey=self.sysadmin_user.apikey) + assert "warandpeace" in res + assert "annakarenina" in res + assert "public_dataset" in res + + tests.call_action_api( + app, + "package_create", + name="private_dataset", + owner_org="test_org_2", + private=True, + apikey=self.sysadmin_user.apikey, + ) - res = tests.call_action_api(self.app, 'package_list') + res = tests.call_action_api(app, "package_list") assert len(res) == 3 - assert 'warandpeace' in res - assert 'annakarenina' in res - assert 'public_dataset' in res - assert 'private_dataset' not in res - - def test_02_package_autocomplete_match_name(self): - postparams = '%s=1' % json.dumps({'q': 'war', 'limit': 5}) - res = self.app.post('/api/action/package_autocomplete', params=postparams) + assert "warandpeace" in res + assert "annakarenina" in res + assert "public_dataset" in res + assert "private_dataset" not in res + + # def test_02_package_autocomplete_match_name(self): + postparams = "%s=1" % json.dumps({"q": "war", "limit": 5}) + res = app.post("/api/action/package_autocomplete", params=postparams) res_obj = json.loads(res.body) - assert_equal(res_obj['success'], True) - pprint(res_obj['result'][0]['name']) - assert_equal(res_obj['result'][0]['name'], 'warandpeace') - assert_equal(res_obj['result'][0]['title'], 'A Wonderful Story') - assert_equal(res_obj['result'][0]['match_field'], 'name') - assert_equal(res_obj['result'][0]['match_displayed'], 'warandpeace') - - def test_02_package_autocomplete_match_title(self): - postparams = '%s=1' % json.dumps({'q': 'won', 'limit': 5}) - res = self.app.post('/api/action/package_autocomplete', params=postparams) + assert res_obj["success"] + assert res_obj["result"][0]["name"] == "warandpeace" + assert res_obj["result"][0]["title"] == "A Wonderful Story" + assert res_obj["result"][0]["match_field"] == "name" + assert res_obj["result"][0]["match_displayed"] == "warandpeace" + + # def test_02_package_autocomplete_match_title(self): + postparams = "%s=1" % json.dumps({"q": "won", "limit": 5}) + res = app.post("/api/action/package_autocomplete", params=postparams) res_obj = json.loads(res.body) - assert_equal(res_obj['success'], True) - pprint(res_obj['result'][0]['name']) - assert_equal(res_obj['result'][0]['name'], 'warandpeace') - assert_equal(res_obj['result'][0]['title'], 'A Wonderful Story') - assert_equal(res_obj['result'][0]['match_field'], 'title') - assert_equal(res_obj['result'][0]['match_displayed'], 'A Wonderful Story (warandpeace)') + assert res_obj["success"] + assert res_obj["result"][0]["name"] == "warandpeace" + assert res_obj["result"][0]["title"] == "A Wonderful Story" + assert res_obj["result"][0]["match_field"] == "title" + assert ( + res_obj["result"][0]["match_displayed"] + == "A Wonderful Story (warandpeace)" + ) - def test_03_create_private_package(self): + # def test_03_create_private_package(self): # Make an organization, because private datasets must belong to one. - organization = tests.call_action_api(self.app, 'organization_create', - name='test_org', - apikey=self.sysadmin_user.apikey) + organization = tests.call_action_api( + app, + "organization_create", + name="test_org", + apikey=self.sysadmin_user.apikey, + ) # Create a dataset without specifying visibility package_dict = { - 'extras': [{'key': u'original media','value': u'"book"'}], - 'license_id': u'other-open', - 'maintainer_email': None, - 'name': u'annakarenina_vis', - 'notes': u'Some test now', - 'resources': [{'alt_url': u'alt123', - 'description': u'Full text.', - 'extras': {u'alt_url': u'alt123', u'size': u'123'}, - 'format': u'plain text', - 'hash': u'abc123', - 'position': 0, - 'url': u'http://datahub.io/download/'}, - {'alt_url': u'alt345', - 'description': u'Index of the novel', - 'extras': {u'alt_url': u'alt345', u'size': u'345'}, - 'format': u'JSON', - 'hash': u'def456', - 'position': 1, - 'url': u'http://datahub.io/index.json'}], - 'tags': [{'name': u'russian'}, {'name': u'tolstoy'}], - 'title': u'A Novel By Tolstoy', - 'url': u'http://datahub.io', - 'owner_org': organization['id'], - 'version': u'0.7a', + "extras": [{"key": u"original media", "value": u'"book"'}], + "license_id": u"other-open", + "maintainer_email": None, + "name": u"annakarenina_vis", + "notes": u"Some test now", + "resources": [ + { + "alt_url": u"alt123", + "description": u"Full text.", + "extras": {u"alt_url": u"alt123", u"size": u"123"}, + "format": u"plain text", + "hash": u"abc123", + "position": 0, + "url": u"http://datahub.io/download/", + }, + { + "alt_url": u"alt345", + "description": u"Index of the novel", + "extras": {u"alt_url": u"alt345", u"size": u"345"}, + "format": u"JSON", + "hash": u"def456", + "position": 1, + "url": u"http://datahub.io/index.json", + }, + ], + "tags": [{"name": u"russian"}, {"name": u"tolstoy"}], + "title": u"A Novel By Tolstoy", + "url": u"http://datahub.io", + "owner_org": organization["id"], + "version": u"0.7a", } - package_created = tests.call_action_api(self.app, 'package_create', - apikey=self.sysadmin_user.apikey, - **package_dict) - assert package_created['private'] is False + package_created = tests.call_action_api( + app, + "package_create", + apikey=self.sysadmin_user.apikey, + **package_dict + ) + assert package_created["private"] is False # Create a new one, explicitly saying it is public - package_dict['name'] = u'annakareninanew_vis_public' - package_dict['private'] = False - - package_created_public = tests.call_action_api(self.app, - 'package_create', - apikey=self.sysadmin_user.apikey, - **package_dict) - assert package_created_public['private'] is False + package_dict["name"] = u"annakareninanew_vis_public" + package_dict["private"] = False + + package_created_public = tests.call_action_api( + app, + "package_create", + apikey=self.sysadmin_user.apikey, + **package_dict + ) + assert package_created_public["private"] is False # Create a new one, explicitly saying it is private - package_dict['name'] = u'annakareninanew_vis_private' - package_dict['private'] = True - - package_created_private = tests.call_action_api(self.app, - 'package_create', - apikey=self.sysadmin_user.apikey, - **package_dict) - assert package_created_private['private'] is True - - def test_41_create_resource(self): + package_dict["name"] = u"annakareninanew_vis_private" + package_dict["private"] = True + + package_created_private = tests.call_action_api( + app, + "package_create", + apikey=self.sysadmin_user.apikey, + **package_dict + ) + assert package_created_private["private"] is True - anna_id = model.Package.by_name(u'annakarenina').id - resource = {'package_id': anna_id, 'url': 'http://new_url'} - api_key = model.User.get('testsysadmin').apikey.encode('utf8') - postparams = '%s=1' % json.dumps(resource) - res = self.app.post('/api/action/resource_create', params=postparams, - extra_environ={'Authorization': api_key }) + # def test_41_create_resource(self): - resource = json.loads(res.body)['result'] + anna_id = model.Package.by_name(u"annakarenina").id + resource = {"package_id": anna_id, "url": "http://new_url"} + api_key = model.User.get("testsysadmin").apikey.encode("utf8") + postparams = "%s=1" % json.dumps(resource) + res = app.post( + "/api/action/resource_create", + params=postparams, + extra_environ={"Authorization": api_key}, + ) - assert resource['url'] == 'http://new_url' + resource = json.loads(res.body)["result"] - def test_42_create_resource_with_error(self): + assert resource["url"] == "http://new_url" - anna_id = model.Package.by_name(u'annakarenina').id - resource = {'package_id': anna_id, 'url': 'new_url', 'created': 'bad_date'} - api_key = model.User.get('testsysadmin').apikey.encode('utf8') + # def test_42_create_resource_with_error(self): - postparams = '%s=1' % json.dumps(resource) - res = self.app.post('/api/action/resource_create', params=postparams, - extra_environ={'Authorization': api_key}, - status=StatusCodes.STATUS_409_CONFLICT) + anna_id = model.Package.by_name(u"annakarenina").id + resource = { + "package_id": anna_id, + "url": "new_url", + "created": "bad_date", + } + api_key = model.User.get("testsysadmin").apikey.encode("utf8") - assert json.loads(res.body)['error'] == {"__type": "Validation Error", "created": ["Date format incorrect"]} + postparams = "%s=1" % json.dumps(resource) + res = app.post( + "/api/action/resource_create", + params=postparams, + extra_environ={"Authorization": api_key}, + status=StatusCodes.STATUS_409_CONFLICT, + ) + assert json.loads(res.body)["error"] == { + "__type": "Validation Error", + "created": ["Date format incorrect"], + } - def test_10_user_create_parameters_missing(self): + # def test_10_user_create_parameters_missing(self): user_dict = {} - postparams = '%s=1' % json.dumps(user_dict) - res = self.app.post('/api/action/user_create', params=postparams, - extra_environ={'Authorization': str(self.sysadmin_user.apikey)}, - status=StatusCodes.STATUS_409_CONFLICT) + postparams = "%s=1" % json.dumps(user_dict) + res = app.post( + "/api/action/user_create", + params=postparams, + extra_environ={"Authorization": str(self.sysadmin_user.apikey)}, + status=StatusCodes.STATUS_409_CONFLICT, + ) res_obj = json.loads(res.body) - assert res_obj['error'] == { - '__type': 'Validation Error', - 'name': ['Missing value'], - 'email': ['Missing value'], - 'password': ['Missing value'] - } - assert "/api/3/action/help_show?name=user_create" in res_obj['help'] - assert res_obj['success'] is False + assert res_obj["error"] == { + "__type": "Validation Error", + "name": ["Missing value"], + "email": ["Missing value"], + "password": ["Missing value"], + } + assert "/api/3/action/help_show?name=user_create" in res_obj["help"] + assert res_obj["success"] is False + + # def test_11_user_create_wrong_password(self): + user_dict = { + "name": "test_create_from_action_api_2", + "email": "me@test.org", + "password": "tes", + } # Too short + + postparams = "%s=1" % json.dumps(user_dict) + res = app.post( + "/api/action/user_create", + params=postparams, + extra_environ={"Authorization": str(self.sysadmin_user.apikey)}, + status=StatusCodes.STATUS_409_CONFLICT, + ) - def test_11_user_create_wrong_password(self): - user_dict = {'name':'test_create_from_action_api_2', - 'email':'me@test.org', - 'password':'tes'} #Too short + res_obj = json.loads(res.body) + assert "/api/3/action/help_show?name=user_create" in res_obj["help"] + assert res_obj["success"] is False + assert res_obj["error"] == { + "__type": "Validation Error", + "password": ["Your password must be 8 characters or longer"], + } - postparams = '%s=1' % json.dumps(user_dict) - res = self.app.post('/api/action/user_create', params=postparams, - extra_environ={'Authorization': str(self.sysadmin_user.apikey)}, - status=StatusCodes.STATUS_409_CONFLICT) + # def test_12_user_update(self): + normal_user_dict = { + "id": self.normal_user.id, + "name": self.normal_user.name, + "fullname": "Updated normal user full name", + "email": "me@test.org", + "about": "Updated normal user about", + } - res_obj = json.loads(res.body) - assert "/api/3/action/help_show?name=user_create" in res_obj['help'] - assert res_obj['success'] is False - assert_equal(res_obj['error'], { '__type': 'Validation Error', - 'password': ['Your password must be 8 characters or longer']}) - - def test_12_user_update(self): - normal_user_dict = {'id': self.normal_user.id, - 'name': self.normal_user.name, - 'fullname': 'Updated normal user full name', - 'email': 'me@test.org', - 'about':'Updated normal user about'} - - sysadmin_user_dict = {'id': self.sysadmin_user.id, - 'fullname': 'Updated sysadmin user full name', - 'email': 'me@test.org', - 'about':'Updated sysadmin user about'} - - #Normal users can update themselves - postparams = '%s=1' % json.dumps(normal_user_dict) - res = self.app.post('/api/action/user_update', params=postparams, - extra_environ={'Authorization': str(self.normal_user.apikey)}) + sysadmin_user_dict = { + "id": self.sysadmin_user.id, + "fullname": "Updated sysadmin user full name", + "email": "me@test.org", + "about": "Updated sysadmin user about", + } + + # Normal users can update themselves + postparams = "%s=1" % json.dumps(normal_user_dict) + res = app.post( + "/api/action/user_update", + params=postparams, + extra_environ={"Authorization": str(self.normal_user.apikey)}, + ) res_obj = json.loads(res.body) - assert "/api/3/action/help_show?name=user_update" in res_obj['help'] - assert res_obj['success'] == True - result = res_obj['result'] - assert result['id'] == self.normal_user.id - assert result['name'] == self.normal_user.name - assert result['fullname'] == normal_user_dict['fullname'] - assert result['about'] == normal_user_dict['about'] - assert 'apikey' in result - assert 'created' in result - assert 'display_name' in result - assert 'number_created_packages' in result - assert not 'password' in result - - #Sysadmin users can update themselves - postparams = '%s=1' % json.dumps(sysadmin_user_dict) - res = self.app.post('/api/action/user_update', params=postparams, - extra_environ={'Authorization': str(self.sysadmin_user.apikey)}) + assert "/api/3/action/help_show?name=user_update" in res_obj["help"] + assert res_obj["success"] == True + result = res_obj["result"] + assert result["id"] == self.normal_user.id + assert result["name"] == self.normal_user.name + assert result["fullname"] == normal_user_dict["fullname"] + assert result["about"] == normal_user_dict["about"] + assert "apikey" in result + assert "created" in result + assert "display_name" in result + assert "number_created_packages" in result + assert not "password" in result + + # Sysadmin users can update themselves + postparams = "%s=1" % json.dumps(sysadmin_user_dict) + res = app.post( + "/api/action/user_update", + params=postparams, + extra_environ={"Authorization": str(self.sysadmin_user.apikey)}, + ) res_obj = json.loads(res.body) - assert "/api/3/action/help_show?name=user_update" in res_obj['help'] - assert res_obj['success'] == True - result = res_obj['result'] - assert result['id'] == self.sysadmin_user.id - assert result['name'] == self.sysadmin_user.name - assert result['fullname'] == sysadmin_user_dict['fullname'] - assert result['about'] == sysadmin_user_dict['about'] - - #Sysadmin users can update all users - postparams = '%s=1' % json.dumps(normal_user_dict) - res = self.app.post('/api/action/user_update', params=postparams, - extra_environ={'Authorization': str(self.sysadmin_user.apikey)}) + assert "/api/3/action/help_show?name=user_update" in res_obj["help"] + assert res_obj["success"] == True + result = res_obj["result"] + assert result["id"] == self.sysadmin_user.id + assert result["name"] == self.sysadmin_user.name + assert result["fullname"] == sysadmin_user_dict["fullname"] + assert result["about"] == sysadmin_user_dict["about"] + + # Sysadmin users can update all users + postparams = "%s=1" % json.dumps(normal_user_dict) + res = app.post( + "/api/action/user_update", + params=postparams, + extra_environ={"Authorization": str(self.sysadmin_user.apikey)}, + ) res_obj = json.loads(res.body) - assert "/api/3/action/help_show?name=user_update" in res_obj['help'] - assert res_obj['success'] == True - result = res_obj['result'] - assert result['id'] == self.normal_user.id - assert result['name'] == self.normal_user.name - assert result['fullname'] == normal_user_dict['fullname'] - assert result['about'] == normal_user_dict['about'] - - #Normal users can not update other users - postparams = '%s=1' % json.dumps(sysadmin_user_dict) - res = self.app.post('/api/action/user_update', params=postparams, - extra_environ={'Authorization': str(self.normal_user.apikey)}, - status=StatusCodes.STATUS_403_ACCESS_DENIED) + assert "/api/3/action/help_show?name=user_update" in res_obj["help"] + assert res_obj["success"] == True + result = res_obj["result"] + assert result["id"] == self.normal_user.id + assert result["name"] == self.normal_user.name + assert result["fullname"] == normal_user_dict["fullname"] + assert result["about"] == normal_user_dict["about"] + + # Normal users can not update other users + postparams = "%s=1" % json.dumps(sysadmin_user_dict) + res = app.post( + "/api/action/user_update", + params=postparams, + extra_environ={"Authorization": str(self.normal_user.apikey)}, + status=StatusCodes.STATUS_403_ACCESS_DENIED, + ) res_obj = json.loads(res.body) - assert "/api/3/action/help_show?name=user_update" in res_obj['help'] - assert res_obj['error']['__type'] == 'Authorization Error' - assert res_obj['success'] is False + assert "/api/3/action/help_show?name=user_update" in res_obj["help"] + assert res_obj["error"]["__type"] == "Authorization Error" + assert res_obj["success"] is False - def test_12_user_update_errors(self): + # def test_12_user_update_errors(self): test_calls = ( # Empty name - {'user_dict': {'id': self.normal_user.id, - 'name':'', - 'email':'test@test.com'}, - 'messages': [('name','Must be at least 2 characters long')]}, - + { + "user_dict": { + "id": self.normal_user.id, + "name": "", + "email": "test@test.com", + }, + "messages": [("name", "Must be at least 2 characters long")], + }, # Invalid characters in name - {'user_dict': {'id': self.normal_user.id, - 'name':'i++%', - 'email':'test@test.com'}, - 'messages': [('name','Must be purely lowercase alphanumeric')]}, + { + "user_dict": { + "id": self.normal_user.id, + "name": "i++%", + "email": "test@test.com", + }, + "messages": [ + ("name", "Must be purely lowercase alphanumeric") + ], + }, # Existing name - {'user_dict': {'id': self.normal_user.id, - 'name':self.sysadmin_user.name, - 'email':'test@test.com'}, - 'messages': [('name','That login name is not available')]}, + { + "user_dict": { + "id": self.normal_user.id, + "name": self.sysadmin_user.name, + "email": "test@test.com", + }, + "messages": [("name", "That login name is not available")], + }, # Missing email - {'user_dict': {'id': self.normal_user.id, - 'name':self.normal_user.name}, - 'messages': [('email','Missing value')]}, - ) + { + "user_dict": { + "id": self.normal_user.id, + "name": self.normal_user.name, + }, + "messages": [("email", "Missing value")], + }, + ) for test_call in test_calls: - postparams = '%s=1' % json.dumps(test_call['user_dict']) - res = self.app.post('/api/action/user_update', params=postparams, - extra_environ={'Authorization': str(self.normal_user.apikey)}, - status=StatusCodes.STATUS_409_CONFLICT) + postparams = "%s=1" % json.dumps(test_call["user_dict"]) + res = app.post( + "/api/action/user_update", + params=postparams, + extra_environ={"Authorization": str(self.normal_user.apikey)}, + status=StatusCodes.STATUS_409_CONFLICT, + ) res_obj = json.loads(res.body) - for expected_message in test_call['messages']: - assert expected_message[1] in ''.join(res_obj['error'][expected_message[0]]) + for expected_message in test_call["messages"]: + assert expected_message[1] in "".join( + res_obj["error"][expected_message[0]] + ) - def test_user_delete(self): - name = 'normal_user' + # def test_user_delete(self): + name = "normal_user" CreateTestData.create_user(name) user = model.User.get(name) - user_dict = {'id': user.id} - postparams = '%s=1' % json.dumps(user_dict) + user_dict = {"id": user.id} + postparams = "%s=1" % json.dumps(user_dict) - res = self.app.post('/api/action/user_delete', params=postparams, - extra_environ={'Authorization': str(self.sysadmin_user.apikey)}) + res = app.post( + "/api/action/user_delete", + params=postparams, + extra_environ={"Authorization": str(self.sysadmin_user.apikey)}, + ) res_obj = json.loads(res.body) deleted_user = model.User.get(name) - assert res_obj['success'] is True + assert res_obj["success"] is True assert deleted_user.is_deleted(), deleted_user - def test_user_delete_requires_data_dict_with_key_id(self): - user_dict = {'name': 'normal_user'} - postparams = '%s=1' % json.dumps(user_dict) + # def test_user_delete_requires_data_dict_with_key_id(self): + user_dict = {"name": "normal_user"} + postparams = "%s=1" % json.dumps(user_dict) - res = self.app.post('/api/action/user_delete', params=postparams, - extra_environ={'Authorization': str(self.sysadmin_user.apikey)}, - status=StatusCodes.STATUS_409_CONFLICT) + res = app.post( + "/api/action/user_delete", + params=postparams, + extra_environ={"Authorization": str(self.sysadmin_user.apikey)}, + status=StatusCodes.STATUS_409_CONFLICT, + ) res_obj = json.loads(res.body) - assert res_obj['success'] is False - assert res_obj['error']['id'] == ['Missing value'] + assert res_obj["success"] is False + assert res_obj["error"]["id"] == ["Missing value"] - def test_16_user_autocomplete(self): + # def test_16_user_autocomplete(self): # Create deleted user to make sure he won't appear in the user_list - deleted_user = CreateTestData.create_user('joe') + deleted_user = CreateTestData.create_user("joe") deleted_user.delete() model.repo.commit() - #Empty query - postparams = '%s=1' % json.dumps({}) - res = self.app.post( - '/api/action/user_autocomplete', + # Empty query + postparams = "%s=1" % json.dumps({}) + res = app.post( + "/api/action/user_autocomplete", params=postparams, - status=StatusCodes.STATUS_409_CONFLICT) + status=StatusCodes.STATUS_409_CONFLICT, + ) res_obj = json.loads(res.body) - assert "/api/3/action/help_show?name=user_autocomplete" in res_obj['help'] - assert res_obj['success'] is False + assert ( + "/api/3/action/help_show?name=user_autocomplete" in res_obj["help"] + ) + assert res_obj["success"] is False - #Normal query - postparams = '%s=1' % json.dumps({'q':'joe'}) - res = self.app.post('/api/action/user_autocomplete', params=postparams) + # Normal query + postparams = "%s=1" % json.dumps({"q": "joe"}) + res = app.post("/api/action/user_autocomplete", params=postparams) res_obj = json.loads(res.body) - assert res_obj['result'][0]['name'] == 'joeadmin' - assert 'id','fullname' in res_obj['result'][0] - - def test_17_bad_action(self): - #Empty query - postparams = '%s=1' % json.dumps({}) - res = self.app.post('/api/action/bad_action_name', params=postparams, - status=400) + assert res_obj["result"][0]["name"] == "joeadmin" + assert "id", "fullname" in res_obj["result"][0] + + # def test_17_bad_action(self): + # Empty query + postparams = "%s=1" % json.dumps({}) + res = app.post( + "/api/action/bad_action_name", params=postparams, status=400 + ) res_obj = json.loads(res.body) - assert_equal(res_obj, u'Bad request - Action name not known: bad_action_name') + assert ( + res_obj == u"Bad request - Action name not known: bad_action_name" + ) - def test_20_task_status_update(self): - package_created = self._add_basic_package(u'test_task_status_update') + # def test_20_task_status_update(self): + package_created = _add_basic_package(app, u"test_task_status_update") task_status = { - 'entity_id': package_created['id'], - 'entity_type': u'package', - 'task_type': u'test_task', - 'key': u'test_key', - 'value': u'test_value', - 'state': u'test_state', - 'error': u'test_error', + "entity_id": package_created["id"], + "entity_type": u"package", + "task_type": u"test_task", + "key": u"test_key", + "value": u"test_value", + "state": u"test_state", + "error": u"test_error", } - postparams = '%s=1' % json.dumps(task_status) - res = self.app.post( - '/api/action/task_status_update', params=postparams, - extra_environ={'Authorization': str(self.sysadmin_user.apikey)}, - ) - task_status_updated = json.loads(res.body)['result'] - - task_status_id = task_status_updated.pop('id') - task_status_updated.pop('last_updated') - assert task_status_updated == task_status, (task_status_updated, task_status) - - task_status_updated['id'] = task_status_id - task_status_updated['value'] = u'test_value_2' - postparams = '%s=1' % json.dumps(task_status_updated) - res = self.app.post( - '/api/action/task_status_update', params=postparams, - extra_environ={'Authorization': str(self.sysadmin_user.apikey)}, - ) - task_status_updated_2 = json.loads(res.body)['result'] - task_status_updated_2.pop('last_updated') - assert task_status_updated_2 == task_status_updated, task_status_updated_2 - - def test_21_task_status_update_many(self): - package_created = self._add_basic_package(u'test_task_status_update_many') + postparams = "%s=1" % json.dumps(task_status) + res = app.post( + "/api/action/task_status_update", + params=postparams, + extra_environ={"Authorization": str(self.sysadmin_user.apikey)}, + ) + task_status_updated = json.loads(res.body)["result"] + + task_status_id = task_status_updated.pop("id") + task_status_updated.pop("last_updated") + assert task_status_updated == task_status + + task_status_updated["id"] = task_status_id + task_status_updated["value"] = u"test_value_2" + postparams = "%s=1" % json.dumps(task_status_updated) + res = app.post( + "/api/action/task_status_update", + params=postparams, + extra_environ={"Authorization": str(self.sysadmin_user.apikey)}, + ) + task_status_updated_2 = json.loads(res.body)["result"] + task_status_updated_2.pop("last_updated") + assert task_status_updated_2 == task_status_updated + + # def test_21_task_status_update_many(self): + package_created = _add_basic_package( + app, u"test_task_status_update_many" + ) task_statuses = { - 'data': [ + "data": [ { - 'entity_id': package_created['id'], - 'entity_type': u'package', - 'task_type': u'test_task', - 'key': u'test_task_1', - 'value': u'test_value_1', - 'state': u'test_state', - 'error': u'test_error' + "entity_id": package_created["id"], + "entity_type": u"package", + "task_type": u"test_task", + "key": u"test_task_1", + "value": u"test_value_1", + "state": u"test_state", + "error": u"test_error", }, { - 'entity_id': package_created['id'], - 'entity_type': u'package', - 'task_type': u'test_task', - 'key': u'test_task_2', - 'value': u'test_value_2', - 'state': u'test_state', - 'error': u'test_error' - } + "entity_id": package_created["id"], + "entity_type": u"package", + "task_type": u"test_task", + "key": u"test_task_2", + "value": u"test_value_2", + "state": u"test_state", + "error": u"test_error", + }, ] } - postparams = '%s=1' % json.dumps(task_statuses) - res = self.app.post( - '/api/action/task_status_update_many', params=postparams, - extra_environ={'Authorization': str(self.sysadmin_user.apikey)}, - ) - task_statuses_updated = json.loads(res.body)['result']['results'] - for i in range(len(task_statuses['data'])): - task_status = task_statuses['data'][i] + postparams = "%s=1" % json.dumps(task_statuses) + res = app.post( + "/api/action/task_status_update_many", + params=postparams, + extra_environ={"Authorization": str(self.sysadmin_user.apikey)}, + ) + task_statuses_updated = json.loads(res.body)["result"]["results"] + for i in range(len(task_statuses["data"])): + task_status = task_statuses["data"][i] task_status_updated = task_statuses_updated[i] - task_status_updated.pop('id') - task_status_updated.pop('last_updated') - assert task_status == task_status_updated, (task_status_updated, task_status, i) - - def test_22_task_status_normal_user_not_authorized(self): + task_status_updated.pop("id") + task_status_updated.pop("last_updated") + assert task_status == task_status_updated, ( + task_status_updated, + task_status, + i, + ) + + # def test_22_task_status_normal_user_not_authorized(self): task_status = {} - postparams = '%s=1' % json.dumps(task_status) - res = self.app.post( - '/api/action/task_status_update', params=postparams, - extra_environ={'Authorization': str(self.normal_user.apikey)}, - status=StatusCodes.STATUS_403_ACCESS_DENIED + postparams = "%s=1" % json.dumps(task_status) + res = app.post( + "/api/action/task_status_update", + params=postparams, + extra_environ={"Authorization": str(self.normal_user.apikey)}, + status=StatusCodes.STATUS_403_ACCESS_DENIED, ) res_obj = json.loads(res.body) - assert "/api/3/action/help_show?name=task_status_update" in res_obj['help'] - assert res_obj['success'] is False - assert res_obj['error']['__type'] == 'Authorization Error' + assert ( + "/api/3/action/help_show?name=task_status_update" + in res_obj["help"] + ) + assert res_obj["success"] is False + assert res_obj["error"]["__type"] == "Authorization Error" - def test_23_task_status_validation(self): + # def test_23_task_status_validation(self): task_status = {} - postparams = '%s=1' % json.dumps(task_status) - res = self.app.post( - '/api/action/task_status_update', params=postparams, - extra_environ={'Authorization': str(self.sysadmin_user.apikey)}, - status=StatusCodes.STATUS_409_CONFLICT + postparams = "%s=1" % json.dumps(task_status) + res = app.post( + "/api/action/task_status_update", + params=postparams, + extra_environ={"Authorization": str(self.sysadmin_user.apikey)}, + status=StatusCodes.STATUS_409_CONFLICT, ) - def test_24_task_status_show(self): - package_created = self._add_basic_package(u'test_task_status_show') + # def test_24_task_status_show(self): + package_created = _add_basic_package(app, u"test_task_status_show") task_status = { - 'entity_id': package_created['id'], - 'entity_type': u'package', - 'task_type': u'test_task', - 'key': u'test_task_status_show', - 'value': u'test_value', - 'state': u'test_state', - 'error': u'test_error' + "entity_id": package_created["id"], + "entity_type": u"package", + "task_type": u"test_task", + "key": u"test_task_status_show", + "value": u"test_value", + "state": u"test_state", + "error": u"test_error", } - postparams = '%s=1' % json.dumps(task_status) - res = self.app.post( - '/api/action/task_status_update', params=postparams, - extra_environ={'Authorization': str(self.sysadmin_user.apikey)}, + postparams = "%s=1" % json.dumps(task_status) + res = app.post( + "/api/action/task_status_update", + params=postparams, + extra_environ={"Authorization": str(self.sysadmin_user.apikey)}, ) - task_status_updated = json.loads(res.body)['result'] + task_status_updated = json.loads(res.body)["result"] # make sure show works when giving a task status ID - postparams = '%s=1' % json.dumps({'id': task_status_updated['id']}) - res = self.app.post( - '/api/action/task_status_show', params=postparams, - extra_environ={'Authorization': str(self.sysadmin_user.apikey)}, + postparams = "%s=1" % json.dumps({"id": task_status_updated["id"]}) + res = app.post( + "/api/action/task_status_show", + params=postparams, + extra_environ={"Authorization": str(self.sysadmin_user.apikey)}, ) - task_status_show = json.loads(res.body)['result'] + task_status_show = json.loads(res.body)["result"] - task_status_show.pop('last_updated') - task_status_updated.pop('last_updated') - assert task_status_show == task_status_updated, (task_status_show, task_status_updated) + task_status_show.pop("last_updated") + task_status_updated.pop("last_updated") + assert task_status_show == task_status_updated, ( + task_status_show, + task_status_updated, + ) # make sure show works when giving a (entity_id, task_type, key) tuple - postparams = '%s=1' % json.dumps({ - 'entity_id': task_status['entity_id'], - 'task_type': task_status['task_type'], - 'key': task_status['key'] - }) - res = self.app.post( - '/api/action/task_status_show', params=postparams, - extra_environ={'Authorization': str(self.sysadmin_user.apikey)}, + postparams = "%s=1" % json.dumps( + { + "entity_id": task_status["entity_id"], + "task_type": task_status["task_type"], + "key": task_status["key"], + } + ) + res = app.post( + "/api/action/task_status_show", + params=postparams, + extra_environ={"Authorization": str(self.sysadmin_user.apikey)}, ) - task_status_show = json.loads(res.body)['result'] + task_status_show = json.loads(res.body)["result"] - task_status_show.pop('last_updated') - assert task_status_show == task_status_updated, (task_status_show, task_status_updated) + task_status_show.pop("last_updated") + assert task_status_show == task_status_updated, ( + task_status_show, + task_status_updated, + ) - def test_25_task_status_delete(self): - package_created = self._add_basic_package(u'test_task_status_delete') + # def test_25_task_status_delete(self): + package_created = _add_basic_package(app, u"test_task_status_delete") task_status = { - 'entity_id': package_created['id'], - 'entity_type': u'package', - 'task_type': u'test_task', - 'key': u'test_task_status_delete', - 'value': u'test_value', - 'state': u'test_state', - 'error': u'test_error' + "entity_id": package_created["id"], + "entity_type": u"package", + "task_type": u"test_task", + "key": u"test_task_status_delete", + "value": u"test_value", + "state": u"test_state", + "error": u"test_error", } - postparams = '%s=1' % json.dumps(task_status) - res = self.app.post( - '/api/action/task_status_update', params=postparams, - extra_environ={'Authorization': str(self.sysadmin_user.apikey)}, + postparams = "%s=1" % json.dumps(task_status) + res = app.post( + "/api/action/task_status_update", + params=postparams, + extra_environ={"Authorization": str(self.sysadmin_user.apikey)}, ) - task_status_updated = json.loads(res.body)['result'] + task_status_updated = json.loads(res.body)["result"] - postparams = '%s=1' % json.dumps({'id': task_status_updated['id']}) - res = self.app.post( - '/api/action/task_status_delete', params=postparams, - extra_environ={'Authorization': str(self.sysadmin_user.apikey)}, + postparams = "%s=1" % json.dumps({"id": task_status_updated["id"]}) + res = app.post( + "/api/action/task_status_delete", + params=postparams, + extra_environ={"Authorization": str(self.sysadmin_user.apikey)}, ) task_status_delete = json.loads(res.body) - assert task_status_delete['success'] == True + assert task_status_delete["success"] == True - def test_26_resource_show(self): - pkg = model.Package.get('annakarenina') + # def test_26_resource_show(self): + pkg = model.Package.get("annakarenina") resource = pkg.resources[0] - postparams = '%s=1' % json.dumps({'id': resource.id}) - res = self.app.post('/api/action/resource_show', params=postparams) - result = json.loads(res.body)['result'] + postparams = "%s=1" % json.dumps({"id": resource.id}) + res = app.post("/api/action/resource_show", params=postparams) + result = json.loads(res.body)["result"] - resource_dict = resource_dictize(resource, {'model': model}) + resource_dict = resource_dictize(resource, {"model": model}) assert result == resource_dict, (result, resource_dict) - def test_27_get_site_user_not_authorized(self): - assert_raises(NotAuthorized, - get_action('get_site_user'), - {'model': model}, {}) - user = model.User.get('test.ckan.net') - assert not user + # def test_27_get_site_user_not_authorized(self): + with pytest.raises(NotAuthorized): + get_action("get_site_user")({"model": model, "user": ""}, {}) + # user = model.User.get('test.ckan.net') + # assert not user - site_id = config.get('ckan.site_id') - user = get_action('get_site_user')({'model': model, 'ignore_auth': True}, {}) - assert user['name'] == site_id + site_id = config.get("ckan.site_id") + user = get_action("get_site_user")( + {"model": model, "ignore_auth": True}, {} + ) + assert user["name"] == site_id user = model.User.get(site_id) assert user - user=get_action('get_site_user')({'model': model, 'ignore_auth': True}, {}) - assert user['name'] == site_id + user = get_action("get_site_user")( + {"model": model, "ignore_auth": True}, {} + ) + assert user["name"] == site_id user = model.Session.query(model.User).filter_by(name=site_id).one() assert user - def test_28_group_package_show(self): - group_id = model.Group.get('david').id - group_packages = get_action('group_package_show')( - {'model': model, 'user': self.normal_user.name, 'ignore_auth': True}, - {'id': group_id} + # def test_28_group_package_show(self): + group_id = model.Group.get("david").id + group_packages = get_action("group_package_show")( + { + "model": model, + "user": self.normal_user.name, + "ignore_auth": True, + }, + {"id": group_id}, ) assert len(group_packages) == 2, group_packages - group_names = {g.get('name') for g in group_packages} - assert group_names == set(['annakarenina', 'warandpeace']), group_names - - - def test_30_status_show(self): - postparams = '%s=1' % json.dumps({}) - res = self.app.post('/api/action/status_show', params=postparams) - status = json.loads(res.body)['result'] - assert_equal(status['site_title'], 'CKAN') - assert_equal(status['ckan_version'], ckan.__version__) - assert_equal(status['site_url'], 'http://test.ckan.net') - - def test_31_bad_request_format(self): - postparams = '%s=1' % json.dumps('not a dict') - res = self.app.post('/api/action/package_list', params=postparams, - status=400) - assert "Bad request - JSON Error: Request data JSON decoded to 'not a dict' but it needs to be a dictionary." in res.body, res.body - - def test_31_bad_request_format_not_json(self): - postparams = '=1' - res = self.app.post('/api/action/package_list', params=postparams, - status=400) - assert "Bad request - JSON Error: Error decoding JSON data." in res.body, res.body - - def test_32_get_domain_object(self): - anna = model.Package.by_name(u'annakarenina') - assert_equal(get_domain_object(model, anna.name).name, anna.name) - assert_equal(get_domain_object(model, anna.id).name, anna.name) - group = model.Group.by_name(u'david') - assert_equal(get_domain_object(model, group.name).name, group.name) - assert_equal(get_domain_object(model, group.id).name, group.name) - - def test_41_missing_action(self): + group_names = set([g.get("name") for g in group_packages]) + assert group_names == set(["annakarenina", "warandpeace"]), group_names + + # def test_30_status_show(self): + postparams = "%s=1" % json.dumps({}) + res = app.post("/api/action/status_show", params=postparams) + status = json.loads(res.body)["result"] + assert status["site_title"] == "CKAN" + assert status["ckan_version"] == ckan.__version__ + assert status["site_url"] == "http://test.ckan.net" + + # def test_31_bad_request_format(self): + postparams = "%s=1" % json.dumps("not a dict") + res = app.post( + "/api/action/package_list", params=postparams, status=400 + ) + assert ( + "Bad request - JSON Error: Request data JSON decoded to 'not a dict' but it needs to be a dictionary." + in res.body + ), res.body + + # def test_31_bad_request_format_not_json(self): + postparams = "=1" + res = app.post( + "/api/action/package_list", params=postparams, status=400 + ) + assert ( + "Bad request - JSON Error: Error decoding JSON data." in res.body + ), res.body + + # def test_32_get_domain_object(self): + anna = model.Package.by_name(u"annakarenina") + assert get_domain_object(model, anna.name).name == anna.name + assert get_domain_object(model, anna.id).name == anna.name + group = model.Group.by_name(u"david") + assert get_domain_object(model, group.name).name == group.name + assert get_domain_object(model, group.id).name == group.name + + # def test_41_missing_action(self): try: - get_action('unicorns') + get_action("unicorns") assert False, "We found a non-existent action" except KeyError: assert True - def test_42_resource_search_with_single_field_query(self): - request_body = { - 'query': ["description:index"], - } + # def test_42_resource_search_with_single_field_query(self): + request_body = {"query": ["description:index"]} postparams = json.dumps(request_body) - response = self.app.post('/api/action/resource_search', - params=postparams) - result = json.loads(response.body)['result']['results'] - count = json.loads(response.body)['result']['count'] + response = app.post("/api/action/resource_search", params=postparams) + result = json.loads(response.body)["result"]["results"] + count = json.loads(response.body)["result"]["count"] ## Due to the side-effect of previously run tests, there may be extra ## resources in the results. So just check that each found Resource ## matches the search criteria assert count > 0 for resource in result: - assert "index" in resource['description'].lower() + assert "index" in resource["description"].lower() - def test_42_resource_search_across_multiple_fields(self): - request_body = { - 'query': ["description:index", "format:json"], - } + # def test_42_resource_search_across_multiple_fields(self): + request_body = {"query": ["description:index", "format:json"]} postparams = json.dumps(request_body) - response = self.app.post('/api/action/resource_search', - params=postparams) - result = json.loads(response.body)['result']['results'] - count = json.loads(response.body)['result']['count'] + response = app.post("/api/action/resource_search", params=postparams) + result = json.loads(response.body)["result"]["results"] + count = json.loads(response.body)["result"]["count"] ## Due to the side-effect of previously run tests, there may be extra ## resources in the results. So just check that each found Resource ## matches the search criteria assert count > 0 for resource in result: - assert "index" in resource['description'].lower() - assert "json" in resource['format'].lower() + assert "index" in resource["description"].lower() + assert "json" in resource["format"].lower() - def test_42_resource_search_test_percentage_is_escaped(self): - request_body = { - 'query': ["description:index%"], - } + # def test_42_resource_search_test_percentage_is_escaped(self): + request_body = {"query": ["description:index%"]} postparams = json.dumps(request_body) - response = self.app.post('/api/action/resource_search', - params=postparams) - count = json.loads(response.body)['result']['count'] + response = app.post("/api/action/resource_search", params=postparams) + count = json.loads(response.body)["result"]["count"] # There shouldn't be any results. If the '%' character wasn't # escaped correctly, then the search would match because of the # unescaped wildcard. assert count == 0 - def test_42_resource_search_fields_parameter_still_accepted(self): - '''The fields parameter is deprecated, but check it still works. + # def test_42_resource_search_fields_parameter_still_accepted(self): + """The fields parameter is deprecated, but check it still works. Remove this test when removing the fields parameter. (#2603) - ''' - request_body = { - 'fields': {"description": "index"}, - } + """ + request_body = {"fields": {"description": "index"}} postparams = json.dumps(request_body) - response = self.app.post('/api/action/resource_search', - params=postparams) - result = json.loads(response.body)['result']['results'] - count = json.loads(response.body)['result']['count'] + response = app.post("/api/action/resource_search", params=postparams) + result = json.loads(response.body)["result"]["results"] + count = json.loads(response.body)["result"]["count"] ## Due to the side-effect of previously run tests, there may be extra ## resources in the results. So just check that each found Resource ## matches the search criteria assert count > 0 for resource in result: - assert "index" in resource['description'].lower() + assert "index" in resource["description"].lower() - def test_42_resource_search_accessible_via_get_request(self): - response = self.app.get('/api/action/resource_search' - '?query=description:index&query=format:json') + # def test_42_resource_search_accessible_via_get_request(self): + response = app.get( + "/api/action/resource_search" + "?query=description:index&query=format:json" + ) - result = json.loads(response.body)['result']['results'] - count = json.loads(response.body)['result']['count'] + result = json.loads(response.body)["result"]["results"] + count = json.loads(response.body)["result"]["count"] ## Due to the side-effect of previously run tests, there may be extra ## resources in the results. So just check that each found Resource ## matches the search criteria assert count > 0 for resource in result: - assert "index" in resource['description'].lower() - assert "json" in resource['format'].lower() + assert "index" in resource["description"].lower() + assert "json" in resource["format"].lower() - def test_package_create_duplicate_extras_error(self): + def test_package_create_duplicate_extras_error(self, app): # Posting a dataset dict to package_create containing two extras dicts # with the same key, should return a Validation Error. - app = helpers._get_test_app() - error = call_action_api(app, 'package_create', - apikey=self.sysadmin_user.apikey, status=409, - name='foobar', extras=[{'key': 'foo', 'value': 'bar'}, - {'key': 'foo', 'value': 'gar'}]) - assert error['__type'] == 'Validation Error' - assert error['extras_validation'] == ['Duplicate key "foo"'] - - def test_package_update_remove_org_error(self): - - app = helpers._get_test_app() - org = call_action_api(app, 'organization_create', - apikey=self.sysadmin_user.apikey, name='myorganization') - package = call_action_api(app, 'package_create', - apikey=self.sysadmin_user.apikey, name='foobarbaz', owner_org=org['id']) - - assert package['owner_org'] - package['owner_org'] = '' - res = call_action_api(app, 'package_update', - apikey=self.sysadmin_user.apikey, **package) - assert not res['owner_org'], res['owner_org'] - - def test_package_update_duplicate_extras_error(self): + + error = call_action_api( + app, + "package_create", + apikey=self.sysadmin_user.apikey, + status=409, + name="foobar", + extras=[ + {"key": "foo", "value": "bar"}, + {"key": "foo", "value": "gar"}, + ], + ) + assert error["__type"] == "Validation Error" + assert error["extras_validation"] == ['Duplicate key "foo"'] + + def test_package_update_remove_org_error(self, app): + + org = call_action_api( + app, + "organization_create", + apikey=self.sysadmin_user.apikey, + name="myorganization", + ) + package = call_action_api( + app, + "package_create", + apikey=self.sysadmin_user.apikey, + name="foobarbaz", + owner_org=org["id"], + ) + + assert package["owner_org"] + package["owner_org"] = "" + res = call_action_api( + app, "package_update", apikey=self.sysadmin_user.apikey, **package + ) + assert not res["owner_org"], res["owner_org"] + + def test_package_update_duplicate_extras_error(self, app): # We need to create a package first, so that we can update it. - app = helpers._get_test_app() - package = call_action_api(app, 'package_create', - apikey=self.sysadmin_user.apikey, name='foobar') + package = call_action_api( + app, + "package_create", + apikey=self.sysadmin_user.apikey, + name="foobar", + ) # Posting a dataset dict to package_update containing two extras dicts # with the same key, should return a Validation Error. - package['extras'] = [{'key': 'foo', 'value': 'bar'}, - {'key': 'foo', 'value': 'gar'}] - error = call_action_api(app, 'package_update', - apikey=self.sysadmin_user.apikey, status=409, **package) - assert error['__type'] == 'Validation Error' - assert error['extras_validation'] == ['Duplicate key "foo"'] - + package["extras"] = [ + {"key": "foo", "value": "bar"}, + {"key": "foo", "value": "gar"}, + ] + error = call_action_api( + app, + "package_update", + apikey=self.sysadmin_user.apikey, + status=409, + **package + ) + assert error["__type"] == "Validation Error" + assert error["extras_validation"] == ['Duplicate key "foo"'] -class TestActionTermTranslation(WsgiAppCase): - @classmethod - def setup_class(self): +class TestActionTermTranslation(object): + @pytest.fixture(autouse=True) + def initial_data(self, clean_db, clean_index): CreateTestData.create() - self.sysadmin_user = model.User.get('testsysadmin') - self.normal_user = model.User.get('annafan') - - @classmethod - def teardown_class(self): - model.repo.rebuild_db() - - def test_1_update_single(self): - postparams = '%s=1' % json.dumps( - {"term" : "moo", - "term_translation": "moo", - "lang_code" : "fr" - } + self.sysadmin_user = model.User.get("testsysadmin") + self.normal_user = model.User.get("annafan") + + def test_1_update_single(self, app): + postparams = "%s=1" % json.dumps( + {"term": "moo", "term_translation": "moo", "lang_code": "fr"} ) - res = self.app.post('/api/action/term_translation_update', params=postparams, - extra_environ={'Authorization': str(self.sysadmin_user.apikey)}, - status=200) + res = app.post( + "/api/action/term_translation_update", + params=postparams, + extra_environ={"Authorization": str(self.sysadmin_user.apikey)}, + status=200, + ) - assert json.loads(res.body)['success'] + assert json.loads(res.body)["success"] - postparams = '%s=1' % json.dumps( - {"term" : "moo", - "term_translation": "moomoo", - "lang_code" : "fr" - } + postparams = "%s=1" % json.dumps( + {"term": "moo", "term_translation": "moomoo", "lang_code": "fr"} ) - res = self.app.post('/api/action/term_translation_update', params=postparams, - extra_environ={'Authorization': str(self.sysadmin_user.apikey)}, - status=200) + res = app.post( + "/api/action/term_translation_update", + params=postparams, + extra_environ={"Authorization": str(self.sysadmin_user.apikey)}, + status=200, + ) - assert json.loads(res.body)['success'] + assert json.loads(res.body)["success"] - postparams = '%s=1' % json.dumps( - {"term" : "moo", - "term_translation": "moomoo", - "lang_code" : "en" - } + postparams = "%s=1" % json.dumps( + {"term": "moo", "term_translation": "moomoo", "lang_code": "en"} ) - res = self.app.post('/api/action/term_translation_update', params=postparams, - extra_environ={'Authorization': str(self.sysadmin_user.apikey)}, - status=200) + res = app.post( + "/api/action/term_translation_update", + params=postparams, + extra_environ={"Authorization": str(self.sysadmin_user.apikey)}, + status=200, + ) - assert json.loads(res.body)['success'] + assert json.loads(res.body)["success"] - postparams = '%s=1' % json.dumps({"terms" : ["moo"]}) + postparams = "%s=1" % json.dumps({"terms": ["moo"]}) - res = self.app.post('/api/action/term_translation_show', params=postparams, - extra_environ={'Authorization': str(self.sysadmin_user.apikey)}, - status=200) + res = app.post( + "/api/action/term_translation_show", + params=postparams, + extra_environ={"Authorization": str(self.sysadmin_user.apikey)}, + status=200, + ) - assert json.loads(res.body)['success'] + assert json.loads(res.body)["success"] # sort the result since the order is not important and is implementation # dependent - assert sorted(json.loads(res.body)['result']) == sorted( - [{u'lang_code': u'fr', u'term': u'moo', u'term_translation': u'moomoo'}, - {u'lang_code': u'en', u'term': u'moo', u'term_translation': u'moomoo'}]),\ - json.loads(res.body) - - def test_2_update_many(self): - - postparams = '%s=1' % json.dumps({'data': [ - {"term" : "many", - "term_translation": "manymoo", - "lang_code" : "fr" - }, - {"term" : "many", - "term_translation": "manymoo", - "lang_code" : "en" - }, - {"term" : "many", - "term_translation": "manymoomoo", - "lang_code" : "en" - } + assert sorted(json.loads(res.body)["result"]) == sorted( + [ + { + u"lang_code": u"fr", + u"term": u"moo", + u"term_translation": u"moomoo", + }, + { + u"lang_code": u"en", + u"term": u"moo", + u"term_translation": u"moomoo", + }, ] - } + ), json.loads(res.body) + + def test_2_update_many(self, app): + + postparams = "%s=1" % json.dumps( + { + "data": [ + { + "term": "many", + "term_translation": "manymoo", + "lang_code": "fr", + }, + { + "term": "many", + "term_translation": "manymoo", + "lang_code": "en", + }, + { + "term": "many", + "term_translation": "manymoomoo", + "lang_code": "en", + }, + ] + } + ) + res = app.post( + "/api/action/term_translation_update_many", + params=postparams, + extra_environ={"Authorization": str(self.sysadmin_user.apikey)}, + status=200, ) - res = self.app.post('/api/action/term_translation_update_many', params=postparams, - extra_environ={'Authorization': str(self.sysadmin_user.apikey)}, - status=200) - assert json.loads(res.body)['result']['success'] == '3 rows updated', json.loads(res.body) + assert ( + json.loads(res.body)["result"]["success"] == "3 rows updated" + ), json.loads(res.body) - postparams = '%s=1' % json.dumps({"terms" : ["many"]}) - res = self.app.post('/api/action/term_translation_show', params=postparams, - extra_environ={'Authorization': str(self.sysadmin_user.apikey)}, - status=200) + postparams = "%s=1" % json.dumps({"terms": ["many"]}) + res = app.post( + "/api/action/term_translation_show", + params=postparams, + extra_environ={"Authorization": str(self.sysadmin_user.apikey)}, + status=200, + ) # sort the result since the order is not important and is implementation # dependent - assert sorted(json.loads(res.body)['result']) == sorted( - [{u'lang_code': u'fr', u'term': u'many', u'term_translation': u'manymoo'}, - {u'lang_code': u'en', u'term': u'many', u'term_translation': u'manymoomoo'}]),\ - json.loads(res.body) + assert sorted(json.loads(res.body)["result"]) == sorted( + [ + { + u"lang_code": u"fr", + u"term": u"many", + u"term_translation": u"manymoo", + }, + { + u"lang_code": u"en", + u"term": u"many", + u"term_translation": u"manymoomoo", + }, + ] + ), json.loads(res.body) class MockPackageSearchPlugin(SingletonPlugin): implements(IPackageController, inherit=True) def before_index(self, data_dict): - data_dict['extras_test'] = 'abcabcabc' + data_dict["extras_test"] = "abcabcabc" return data_dict def before_search(self, search_params): - if 'extras' in search_params and 'ext_avoid' in search_params['extras']: - assert 'q' in search_params - - if 'extras' in search_params and 'ext_abort' in search_params['extras']: - assert 'q' in search_params + if ( + "extras" in search_params + and "ext_avoid" in search_params["extras"] + ): + assert "q" in search_params + + if ( + "extras" in search_params + and "ext_abort" in search_params["extras"] + ): + assert "q" in search_params # Prevent the actual query - search_params['abort_search'] = True + search_params["abort_search"] = True return search_params def after_search(self, search_results, search_params): - assert 'results' in search_results - assert 'count' in search_results - assert 'search_facets' in search_results + assert "results" in search_results + assert "count" in search_results + assert "search_facets" in search_results - if 'extras' in search_params and 'ext_avoid' in search_params['extras']: + if ( + "extras" in search_params + and "ext_avoid" in search_params["extras"] + ): # Remove results with a certain value - avoid = search_params['extras']['ext_avoid'] + avoid = search_params["extras"]["ext_avoid"] - for i,result in enumerate(search_results['results']): - if avoid.lower() in result['name'].lower() or avoid.lower() in result['title'].lower(): - search_results['results'].pop(i) - search_results['count'] -= 1 + for i, result in enumerate(search_results["results"]): + if ( + avoid.lower() in result["name"].lower() + or avoid.lower() in result["title"].lower() + ): + search_results["results"].pop(i) + search_results["count"] -= 1 return search_results def before_view(self, data_dict): - data_dict['title'] = 'string_not_found_in_rest_of_template' + data_dict["title"] = "string_not_found_in_rest_of_template" return data_dict -MockPackageSearchPlugin().disable() -class TestSearchPluginInterface(WsgiAppCase): +# MockPackageSearchPlugin().disable() + - @classmethod - def setup_class(cls): - MockPackageSearchPlugin().activate() - MockPackageSearchPlugin().enable() - setup_test_search_index() +class TestSearchPluginInterface(object): + @pytest.fixture(autouse=True) + @pytest.mark.ckan_config("ckan.plugins", "legacy_mock_search_plugin") + def initial_data(self, clean_db, clean_index, with_plugins): CreateTestData.create() - MockPackageSearchPlugin().disable() - cls.sysadmin_user = model.User.get('testsysadmin') + self.sysadmin_user = model.User.get("testsysadmin") + + @pytest.mark.ckan_config("ckan.plugins", "legacy_mock_search_plugin") + @pytest.mark.usefixtures("with_plugins") + def test_search_plugin_interface_search(self, app): + avoid = "Tolstoy" + search_params = "%s=1" % json.dumps( + {"q": "*:*", "extras": {"ext_avoid": avoid}} + ) - @classmethod - def teardown_class(cls): - model.repo.rebuild_db() + res = app.post("/api/action/package_search", params=search_params) - def setup(self): - MockPackageSearchPlugin().enable() + results_dict = json.loads(res.body)["result"] + for result in results_dict["results"]: + assert not avoid.lower() in result["title"].lower() - def teardown(self): - MockPackageSearchPlugin().disable() + assert results_dict["count"] == 1 - def test_search_plugin_interface_search(self): - avoid = 'Tolstoy' - search_params = '%s=1' % json.dumps({ - 'q': '*:*', - 'extras' : {'ext_avoid':avoid} - }) + @pytest.mark.ckan_config("ckan.plugins", "legacy_mock_search_plugin") + @pytest.mark.usefixtures("with_plugins") + def test_search_plugin_interface_abort(self, app): - res = self.app.post('/api/action/package_search', params=search_params) + search_params = "%s=1" % json.dumps( + {"q": "*:*", "extras": {"ext_abort": True}} + ) - results_dict = json.loads(res.body)['result'] - for result in results_dict['results']: - assert not avoid.lower() in result['title'].lower() + res = app.post("/api/action/package_search", params=search_params) - assert results_dict['count'] == 1 + # Check that the query was aborted and no results returned + res_dict = json.loads(res.body)["result"] + assert res_dict["count"] == 0 + assert len(res_dict["results"]) == 0 - def test_search_plugin_interface_abort(self): + @pytest.mark.ckan_config("ckan.plugins", "legacy_mock_search_plugin") + @pytest.mark.usefixtures("with_plugins") + def test_before_index(self, app): - search_params = '%s=1' % json.dumps({ - 'q': '*:*', - 'extras' : {'ext_abort':True} - }) + # no datasets get aaaaaaaa + search_params = "%s=1" % json.dumps({"q": "aaaaaaaa"}) - res = self.app.post('/api/action/package_search', params=search_params) + res = app.post("/api/action/package_search", params=search_params) - # Check that the query was aborted and no results returned - res_dict = json.loads(res.body)['result'] - assert res_dict['count'] == 0 - assert len(res_dict['results']) == 0 + res_dict = json.loads(res.body)["result"] + assert res_dict["count"] == 0 + assert len(res_dict["results"]) == 0 - def test_before_index(self): + # all datasets should get abcabcabc + search_params = "%s=1" % json.dumps({"q": "abcabcabc"}) + res = app.post("/api/action/package_search", params=search_params) - # no datasets get aaaaaaaa - search_params = '%s=1' % json.dumps({ - 'q': 'aaaaaaaa', - }) + res_dict = json.loads(res.body)["result"] + assert res_dict["count"] == 2, res_dict["count"] + assert len(res_dict["results"]) == 2 - res = self.app.post('/api/action/package_search', params=search_params) + @pytest.mark.ckan_config("ckan.plugins", "legacy_mock_search_plugin") + @pytest.mark.usefixtures("with_plugins") + def test_before_view(self, app): + res = app.get("/dataset/annakarenina") - res_dict = json.loads(res.body)['result'] - assert res_dict['count'] == 0 - assert len(res_dict['results']) == 0 + assert "string_not_found_in_rest_of_template" in res.body - # all datasets should get abcabcabc - search_params = '%s=1' % json.dumps({ - 'q': 'abcabcabc', - }) - res = self.app.post('/api/action/package_search', params=search_params) - - res_dict = json.loads(res.body)['result'] - assert res_dict['count'] == 2, res_dict['count'] - assert len(res_dict['results']) == 2 - - def test_before_view(self): - res = self.app.get('/dataset/annakarenina') - - assert 'string_not_found_in_rest_of_template' in res.body - - res = self.app.get('/dataset?q=') - assert res.body.count('string_not_found_in_rest_of_template') == 2 - - -class TestBulkActions(WsgiAppCase): - - @classmethod - def setup_class(cls): - search.clear_all() - model.Session.add_all([ - model.User(name=u'sysadmin', apikey=u'sysadmin', - password=u'sysadmin', sysadmin=True), - ]) - model.Session.commit() - - data_dict = '%s=1' % json.dumps({ - 'name': 'org', - }) - res = cls.app.post('/api/action/organization_create', - extra_environ={'Authorization': 'sysadmin'}, - params=data_dict) - cls.org_id = json.loads(res.body)['result']['id'] - - cls.package_ids = [] - for i in range(0,12): - data_dict = '%s=1' % json.dumps({ - 'name': 'name{i}'.format(i=i), - 'owner_org': 'org', - }) - res = cls.app.post('/api/action/package_create', - extra_environ={'Authorization': 'sysadmin'}, - params=data_dict) - cls.package_ids.append(json.loads(res.body)['result']['id']) - - - @classmethod - def teardown_class(self): - model.repo.rebuild_db() - - def test_01_make_private_then_public(self): - data_dict = '%s=1' % json.dumps({ - 'datasets': self.package_ids, - 'org_id': self.org_id, - }) - res = self.app.post('/api/action/bulk_update_private', - extra_environ={'Authorization': 'sysadmin'}, - params=data_dict) - - dataset_list = [row.private for row in - model.Session.query(model.Package.private).all()] + res = app.get("/dataset?q=") + assert res.body.count("string_not_found_in_rest_of_template") == 2 + + +class TestBulkActions(object): + @pytest.fixture(autouse=True) + def initial_data(self, clean_db, clean_index, app): + factories.Sysadmin(apikey=u"sysadmin") + + data_dict = "%s=1" % json.dumps({"name": "org"}) + org = factories.Organization(name="org") + self.org_id = org["id"] + + self.package_ids = [ + factories.Dataset(owner_org=org["id"])["id"] for _ in range(12) + ] + + def test_01_make_private_then_public(self, app): + data_dict = "%s=1" % json.dumps( + {"datasets": self.package_ids, "org_id": self.org_id} + ) + res = app.post( + "/api/action/bulk_update_private", + extra_environ={"Authorization": "sysadmin"}, + params=data_dict, + ) + + dataset_list = [ + row.private + for row in model.Session.query(model.Package.private).all() + ] assert len(dataset_list) == 12, len(dataset_list) assert all(dataset_list) - res = self.app.get('/api/action/package_search?q=*:*') - assert json.loads(res.body)['result']['count'] == 0 + res = app.get("/api/action/package_search?q=*:*") + assert json.loads(res.body)["result"]["count"] == 0 - res = self.app.post('/api/action/bulk_update_public', - extra_environ={'Authorization': 'sysadmin'}, - params=data_dict) + res = app.post( + "/api/action/bulk_update_public", + extra_environ={"Authorization": "sysadmin"}, + params=data_dict, + ) - dataset_list = [row.private for row in - model.Session.query(model.Package.private).all()] + dataset_list = [ + row.private + for row in model.Session.query(model.Package.private).all() + ] assert len(dataset_list) == 12, len(dataset_list) assert not any(dataset_list) - res = self.app.get('/api/action/package_search?q=*:*') - assert json.loads(res.body)['result']['count'] == 12 + res = app.get("/api/action/package_search?q=*:*") + assert json.loads(res.body)["result"]["count"] == 12 - def test_02_bulk_delete(self): + def test_02_bulk_delete(self, app): - data_dict = '%s=1' % json.dumps({ - 'datasets': self.package_ids, - 'org_id': self.org_id, - }) - res = self.app.post('/api/action/bulk_update_delete', - extra_environ={'Authorization': 'sysadmin'}, - params=data_dict) + data_dict = "%s=1" % json.dumps( + {"datasets": self.package_ids, "org_id": self.org_id} + ) + res = app.post( + "/api/action/bulk_update_delete", + extra_environ={"Authorization": "sysadmin"}, + params=data_dict, + ) - dataset_list = [row.state for row in - model.Session.query(model.Package.state).all()] + dataset_list = [ + row.state for row in model.Session.query(model.Package.state).all() + ] assert len(dataset_list) == 12, len(dataset_list) - assert all(state == 'deleted' for state in dataset_list) + assert all(state == "deleted" for state in dataset_list) - res = self.app.get('/api/action/package_search?q=*:*') - assert json.loads(res.body)['result']['count'] == 0 + res = app.get("/api/action/package_search?q=*:*") + assert json.loads(res.body)["result"]["count"] == 0 -class TestResourceAction(WsgiAppCase): +class TestResourceAction(object): sysadmin_user = None normal_user = None - @classmethod - def setup_class(cls): - search.clear_all() + @pytest.fixture(autouse=True) + def setup_class(self, clean_db, clean_index): CreateTestData.create() - cls.sysadmin_user = model.User.get('testsysadmin') - - @classmethod - def teardown_class(cls): - model.repo.rebuild_db() - - def _add_basic_package(self, package_name=u'test_package', **kwargs): - package = { - 'name': package_name, - 'title': u'A Novel By Tolstoy', - 'resources': [{ - 'description': u'Full text.', - 'format': u'plain text', - 'url': u'http://datahub.io/download/' - }] - } - package.update(kwargs) - - postparams = '%s=1' % json.dumps(package) - res = self.app.post('/api/action/package_create', params=postparams, - extra_environ={'Authorization': 'tester'}) - return json.loads(res.body)['result'] + self.sysadmin_user = model.User.get("testsysadmin") - def test_01_delete_resource(self): - res_dict = self._add_basic_package() - pkg_id = res_dict['id'] + def test_01_delete_resource(self, app): + res_dict = _add_basic_package(app) + pkg_id = res_dict["id"] - resource_count = len(res_dict['resources']) - id = res_dict['resources'][0]['id'] - url = '/api/action/resource_delete' + resource_count = len(res_dict["resources"]) + id = res_dict["resources"][0]["id"] + url = "/api/action/resource_delete" # Use the sysadmin user because this package doesn't belong to an org - res = self.app.post(url, params=json.dumps({'id': id}), - extra_environ={'Authorization': str(self.sysadmin_user.apikey)}) + res = app.post( + url, + params=json.dumps({"id": id}), + extra_environ={"Authorization": str(self.sysadmin_user.apikey)}, + ) res_dict = json.loads(res.body) - assert res_dict['success'] is True + assert res_dict["success"] is True - url = '/api/action/package_show' - res = self.app.get(url, {'id': pkg_id}) + url = "/api/action/package_show" + res = app.get(url, {"id": pkg_id}) res_dict = json.loads(res.body) - assert res_dict['success'] is True - assert len(res_dict['result']['resources']) == resource_count - 1 + assert res_dict["success"] is True + assert len(res_dict["result"]["resources"]) == resource_count - 1 -class TestMember(WsgiAppCase): +class TestMember(object): sysadmin = None group = None - def setup(self): - username = 'sysadmin' - groupname = 'test group' - organization_name = 'test organization' - CreateTestData.create_user('sysadmin', **{ 'sysadmin': True }) - CreateTestData.create_groups([{ 'name': groupname }, - { 'name': organization_name, - 'type': 'organization'}]) + @pytest.fixture(autouse=True) + def initial_data(self, clean_db): + username = "sysadmin" + groupname = "test group" + organization_name = "test organization" + CreateTestData.create_user("sysadmin", **{"sysadmin": True}) + CreateTestData.create_groups( + [ + {"name": groupname}, + {"name": organization_name, "type": "organization"}, + ] + ) self.sysadmin = model.User.get(username) self.group = model.Group.get(groupname) - def teardown(self): - model.repo.rebuild_db() - - def test_group_member_create_works_user_id_and_group_id(self): - self._assert_we_can_add_user_to_group(self.sysadmin.id, self.group.id) - - def test_group_member_create_works_with_user_id_and_group_name(self): - self._assert_we_can_add_user_to_group(self.sysadmin.id, self.group.name) + def test_group_member_create_works_user_id_and_group_id(self, app): + _assert_we_can_add_user_to_group(app, self.sysadmin.id, self.group.id) - def test_group_member_create_works_with_user_name_and_group_name(self): - self._assert_we_can_add_user_to_group(self.sysadmin.name, self.group.name) + def test_group_member_create_works_with_user_id_and_group_name(self, app): + _assert_we_can_add_user_to_group( + app, self.sysadmin.id, self.group.name + ) - def _assert_we_can_add_user_to_group(self, user_id, group_id): - user = model.User.get(user_id) - group = model.Group.get(group_id) - url = '/api/action/group_member_create' - role = 'member' - postparams = '%s=1' % json.dumps({ - 'id': group_id, - 'username': user_id, - 'role': role}) + def test_group_member_create_works_with_user_name_and_group_name( + self, app + ): + _assert_we_can_add_user_to_group( + app, self.sysadmin.name, self.group.name + ) - res = self.app.post(url, params=postparams, - extra_environ={'Authorization': str(user.apikey)}) - res = json.loads(res.body) - groups = user.get_groups(group.type, role) - group_ids = [g.id for g in groups] - assert res['success'] is True, res - assert group.id in group_ids, (group, group_ids) +def _assert_we_can_add_user_to_group(app, user_id, group_id): + user = model.User.get(user_id) + group = model.Group.get(group_id) + url = "/api/action/group_member_create" + role = "member" + postparams = "%s=1" % json.dumps( + {"id": group_id, "username": user_id, "role": role} + ) + + res = app.post( + url, + params=postparams, + extra_environ={"Authorization": str(user.apikey)}, + ) + + res = json.loads(res.body) + groups = user.get_groups(group.type, role) + group_ids = [g.id for g in groups] + assert res["success"] is True, res + assert group.id in group_ids, (group, group_ids) diff --git a/ckan/tests/legacy/logic/test_auth.py b/ckan/tests/legacy/logic/test_auth.py index 41f9c8f4457..6a3a0c13681 100644 --- a/ckan/tests/legacy/logic/test_auth.py +++ b/ckan/tests/legacy/logic/test_auth.py @@ -1,538 +1,574 @@ # encoding: utf-8 -from ckan.logic import get_action -import ckan.model as model -import ckan.authz as authz -from ckan.lib.create_test_data import CreateTestData import json +import pytest -from ckan.tests.helpers import FunctionalTestBase, reset_db - - -class TestAuth(FunctionalTestBase): - - @classmethod - def setup_class(cls): - - super(TestAuth, cls).setup_class() - - admin_api = get_action('get_site_user')( - {'model': model, 'ignore_auth': True}, {})['apikey'] - ## This is a mutable dict on the class level so tests can - ## add apikeys as they go along - cls.apikeys = {'sysadmin': str(admin_api), 'random_key': 'moo'} - - cls.app = cls._get_test_app() - - @classmethod - def teardown_class(cls): - - super(TestAuth, cls).teardown_class() - - reset_db() - - def setup(self): - # Override default setup as these tests don't work if we reset the - # database after each test - pass - - @classmethod - def _apply_config_changes(cls, config): - - config['ckan.auth.anon_create_dataset'] = False - config['ckan.auth.create_dataset_if_not_in_organization'] = False - config['ckan.auth.user_create_groups'] = False - config['ckan.auth.user_create_organizations'] = False - config['ckan.auth.user_delete_groups'] = False - config['ckan.auth.user_delete_organizations'] = False - config['ckan.auth.create_unowned_dataset'] = False - config['ckan.auth.create_user_via_api'] = False - config['ckan.auth.create_user_via_web'] = True - config['ckan.auth.roles_that_cascade_to_sub_groups'] = 'admin' - - @classmethod - def _call_api(cls, action, data, user, status=None): - params = '%s=1' % json.dumps(data) - res = cls.app.post('/api/action/%s' % action, - params=params, - extra_environ={'Authorization': cls.apikeys[user]}, - status=[200, 403, 409]) +import ckan.authz as authz +import ckan.logic as logic +import ckan.model as model +import ckan.tests.factories as factories +from ckan.lib.create_test_data import CreateTestData +from ckan.logic import get_action +from ckan.tests.helpers import call_auth + + +@pytest.fixture +def auth_config(ckan_config, monkeypatch): + options = ( + ("ckan.auth.anon_create_dataset", False), + ("ckan.auth.create_dataset_if_not_in_organization", False), + ("ckan.auth.user_create_groups", False), + ("ckan.auth.user_create_organizations", False), + ("ckan.auth.user_delete_groups", False), + ("ckan.auth.user_delete_organizations", False), + ("ckan.auth.create_unowned_dataset", False), + ("ckan.auth.create_user_via_api", False), + ("ckan.auth.create_user_via_web", True), + ("ckan.auth.roles_that_cascade_to_sub_groups", "admin"), + ) + for key, value in options: + monkeypatch.setitem(ckan_config, key, value) + + +@pytest.fixture +def apikeys(clean_db): + admin_api = get_action("get_site_user")( + {"model": model, "ignore_auth": True}, {} + )["apikey"] + return {"sysadmin": str(admin_api), "random_key": "moo"} + + +@pytest.fixture +def call_api(app, apikeys): + def call(action, data, user, status=None): + params = "%s=1" % json.dumps(data) + res = app.post( + "/api/action/%s" % action, + params=params, + extra_environ={"Authorization": apikeys[user]}, + status=[200, 403, 409], + ) if res.status_int != (status or 200): - error = json.loads(res.body)['error'] - raise AssertionError('Status was %s but should be %s. Error: %s' % - (res.status, status, error)) + error = json.loads(res.body)["error"] + raise AssertionError( + "Status was %s but should be %s. Error: %s" + % (res.status, status, error) + ) return res - @classmethod - def create_user(cls, name): - user = {'name': name, - 'password': 'TestPassword1', - 'email': 'moo@moo.com'} - res = cls._call_api('user_create', user, 'sysadmin', 200) - cls.apikeys[name] = str(json.loads(res.body)['result']['apikey']) + return call -class TestAuthUsers(TestAuth): - def test_only_sysadmins_can_delete_users(self): - username = 'username' - user = {'id': username} - self.create_user(username) +@pytest.fixture +def create_user(apikeys, call_api): + def create(name): + user = { + "name": name, + "password": "TestPassword1", + "email": "moo@moo.com", + } + res = call_api("user_create", user, "sysadmin", 200) + apikeys[name] = str(json.loads(res.body)["result"]["apikey"]) - self._call_api('user_delete', user, username, 403) - self._call_api('user_delete', user, 'sysadmin', 200) + return create - def test_auth_deleted_users_are_always_unauthorized(self): - always_success = lambda x,y: {'success': True} - authz._AuthFunctions._build() - authz._AuthFunctions._functions['always_success'] = always_success - # We can't reuse the username with the other tests because we can't - # rebuild_db(), because in the setup_class we get the sysadmin. If we - # rebuild the DB, we would delete the sysadmin as well. - username = 'deleted_user' - self.create_user(username) - user = model.User.get(username) - user.delete() - assert not authz.is_authorized_boolean('always_success', {'user': username}) - del authz._AuthFunctions._functions['always_success'] +@pytest.mark.usefixtures("auth_config") +def test_only_sysadmins_can_delete_users(): + user = factories.User() + sysadmin = factories.Sysadmin() -class TestAuthOrgs(TestAuth): + context = {"model": model, "user": user["name"]} + with pytest.raises(logic.NotAuthorized): + assert not call_auth("user_delete", context=context, id=user["id"]) - @classmethod - def setup_class(cls): + context = {"model": model, "user": sysadmin["name"]} + assert call_auth("user_delete", context=context, id=user["id"]) - super(TestAuthOrgs, cls).setup_class() - # actual roles assigned later - cls.create_user('org_admin') - cls.create_user('no_org') - cls.create_user('org_editor') - cls.create_user('editor_wannabe') +@pytest.mark.usefixtures("auth_config") +def test_auth_deleted_users_are_always_unauthorized(): + always_success = lambda x, y: {"success": True} + authz._AuthFunctions._build() + authz._AuthFunctions._functions["always_success"] = always_success + username = "deleted_user" + user_obj = factories.User() + username = user_obj["name"] + user = model.User.get(username) + user.delete() + assert not authz.is_authorized_boolean( + "always_success", {"user": username} + ) + del authz._AuthFunctions._functions["always_success"] + - def test_01_create_users(self): - user = {'name': 'user_no_auth', - 'password': 'TestPassword1', - 'email': 'moo@moo.com'} +class TestAuthOrgs(object): + @pytest.fixture(autouse=True) + def initial_data(self, create_user): + # actual roles assigned later + create_user("org_admin") + create_user("no_org") + create_user("org_editor") + create_user("editor_wannabe") - self._call_api('user_create', user, 'random_key', 403) - self._call_api('user_create', user, 'no_org', 403) + def _add_datasets(self, user, call_api): + # org admin/editor should be able to add dataset to org. + dataset = {"name": user + "_dataset", "owner_org": "org_with_user"} + call_api("package_create", dataset, user, 200) - def test_02_create_orgs(self): - org = {'name': 'org_no_user'} - self._call_api('organization_create', org, 'random_key', 403) - self._call_api('organization_create', org, 'sysadmin') + # not able to add dataset to org admin does not belong to. + dataset = {"name": user + "_dataset_bad", "owner_org": "org_no_user"} + call_api("package_create", dataset, user, 403) - org = {'name': 'org_with_user'} - self._call_api('organization_create', org, 'random_key', 403) - self._call_api('organization_create', org, 'sysadmin') + # admin not able to make dataset not owned by a org + dataset = {"name": user + "_dataset_bad"} + call_api("package_create", dataset, user, 409) - #no user should be able to create org - org = {'name': 'org_should_not_be_created'} - self._call_api('organization_create', org, 'org_admin', 403) + # not able to add org to not existant org + dataset = {"name": user + "_dataset_bad", "owner_org": "org_not_exist"} + call_api("package_create", dataset, user, 403) - def test_03_create_dataset_no_org(self): + def _update_datasets(self, user, call_api): + ##editor/admin should be able to update dataset + dataset = {"id": "org_editor_dataset", "title": "test"} + call_api("package_update", dataset, user, 200) + # editor/admin tries to change owner org + dataset = {"id": "org_editor_dataset", "owner_org": "org_no_user"} + call_api("package_update", dataset, user, 409) + # editor/admin tries to update dataset in different org + dataset = {"id": "sysadmin_create_no_user", "title": "test"} + call_api("package_update", dataset, user, 403) + # non existant owner org + dataset = {"id": "org_editor_dataset", "owner_org": "org_not_exist"} + call_api("package_update", dataset, user, 409) + + def _delete_datasets(self, user, call_api): + # editor/admin should be able to update dataset + dataset = {"id": "org_editor_dataset"} + call_api("package_delete", dataset, user, 200) + # not able to delete dataset in org user does not belong to + dataset = {"id": "sysadmin_create_no_user"} + call_api("package_delete", dataset, user, 403) + + def test_create_users(self, call_api): + user = { + "name": "user_no_auth", + "password": "TestPassword1", + "email": "moo@moo.com", + } + + call_api("user_create", user, "random_key", 403) + call_api("user_create", user, "no_org", 403) + + @pytest.mark.usefixtures("auth_config") + def test_create_dataset_no_org(self, call_api): # no owner_org supplied - dataset = {'name': 'admin_create_no_org'} - self._call_api('package_create', dataset, 'sysadmin', 409) - - dataset = {'name': 'should_not_be_created'} - self._call_api('package_create', dataset, 'no_org', 403) - - def test_04_create_dataset_with_org(self): - org_with_user = self._call_api('organization_show', {'id': - 'org_with_user'}, 'sysadmin') - dataset = {'name': 'admin_create_with_user', - 'owner_org': org_with_user.json['result']['id']} - self._call_api('package_create', dataset, 'sysadmin', 200) - - org_no_user = self._call_api('organization_show', {'id': - 'org_no_user'}, 'sysadmin') - dataset = {'name': 'sysadmin_create_no_user', - 'owner_org': org_no_user.json['result']['id']} - self._call_api('package_create', dataset, 'sysadmin', 200) - dataset = {'name': 'user_create_with_org', - 'owner_org': org_with_user.json['result']['id']} - - def test_05_add_users_to_org(self): - - member = {'username': 'org_admin', - 'role': 'admin', - 'id': 'org_with_user'} - self._call_api('organization_member_create', member, 'sysadmin') + dataset = {"name": "admin_create_no_org"} + call_api("package_create", dataset, "sysadmin", 409) + + dataset = {"name": "should_not_be_created"} + call_api("package_create", dataset, "no_org", 403) + + @pytest.mark.usefixtures("auth_config") + def test_02_create_orgs(self, call_api): + org = {"name": "org_no_user"} + call_api("organization_create", org, "random_key", 403) + call_api("organization_create", org, "sysadmin") + + org = {"name": "org_with_user"} + call_api("organization_create", org, "random_key", 403) + call_api("organization_create", org, "sysadmin") + + # no user should be able to create org + org = {"name": "org_should_not_be_created"} + call_api("organization_create", org, "org_admin", 403) + + # def test_04_create_dataset_with_org(self): + org_with_user = call_api( + "organization_show", {"id": "org_with_user"}, "sysadmin" + ) + dataset = { + "name": "admin_create_with_user", + "owner_org": org_with_user.json["result"]["id"], + } + call_api("package_create", dataset, "sysadmin", 200) + + org_no_user = call_api( + "organization_show", {"id": "org_no_user"}, "sysadmin" + ) + dataset = { + "name": "sysadmin_create_no_user", + "owner_org": org_no_user.json["result"]["id"], + } + call_api("package_create", dataset, "sysadmin", 200) + dataset = { + "name": "user_create_with_org", + "owner_org": org_with_user.json["result"]["id"], + } + + # def test_05_add_users_to_org(self): + + member = { + "username": "org_admin", + "role": "admin", + "id": "org_with_user", + } + call_api("organization_member_create", member, "sysadmin") ## admin user should be able to add users now - member = {'username': 'org_editor', - 'role': 'editor', - 'id': 'org_with_user'} - self._call_api('organization_member_create', member, 'org_admin') + member = { + "username": "org_editor", + "role": "editor", + "id": "org_with_user", + } + call_api("organization_member_create", member, "org_admin") ## editor should not be able to approve others as editors - member = {'username': 'editor_wannabe', - 'role': 'editor', - 'id': 'org_with_user'} - self._call_api('organization_member_create', member, 'org_editor', 403) - - def _add_datasets(self, user): - #org admin/editor should be able to add dataset to org. - dataset = {'name': user + '_dataset', 'owner_org': 'org_with_user'} - self._call_api('package_create', dataset, user, 200) - - #not able to add dataset to org admin does not belong to. - dataset = {'name': user + '_dataset_bad', 'owner_org': 'org_no_user'} - self._call_api('package_create', dataset, user, 403) - - #admin not able to make dataset not owned by a org - dataset = {'name': user + '_dataset_bad'} - self._call_api('package_create', dataset, user, 409) - - #not able to add org to not existant org - dataset = {'name': user + '_dataset_bad', 'owner_org': 'org_not_exist'} - self._call_api('package_create', dataset, user, 403) - - def test_07_add_datasets(self): - self._add_datasets('org_admin') - self._add_datasets('org_editor') - - def _update_datasets(self, user): - ##editor/admin should be able to update dataset - dataset = {'id': 'org_editor_dataset', 'title': 'test'} - self._call_api('package_update', dataset, user, 200) - # editor/admin tries to change owner org - dataset = {'id': 'org_editor_dataset', 'owner_org': 'org_no_user'} - self._call_api('package_update', dataset, user, 409) - # editor/admin tries to update dataset in different org - dataset = {'id': 'sysadmin_create_no_user', 'title': 'test'} - self._call_api('package_update', dataset, user, 403) - #non existant owner org - dataset = {'id': 'org_editor_dataset', 'owner_org': 'org_not_exist'} - self._call_api('package_update', dataset, user, 409) - - def test_08_update_datasets(self): - self._update_datasets('org_admin') - self._update_datasets('org_editor') - - def _delete_datasets(self, user): - #editor/admin should be able to update dataset - dataset = {'id': 'org_editor_dataset'} - self._call_api('package_delete', dataset, user, 200) - #not able to delete dataset in org user does not belong to - dataset = {'id': 'sysadmin_create_no_user'} - self._call_api('package_delete', dataset, user, 403) - - def test_09_delete_datasets(self): - self._delete_datasets('org_admin') - self._delete_datasets('org_editor') - - def test_10_edit_org(self): - org = {'id': 'org_no_user', 'title': 'test'} - #change an org user does not belong to - self._call_api('organization_update', org, 'org_editor', 403) - self._call_api('organization_update', org, 'org_admin', 403) - - #change an org a user belongs to - org = {'id': 'org_with_user', 'title': 'test'} - self._call_api('organization_update', org, 'org_editor', 403) - self._call_api('organization_update', org, 'org_admin', 200) - - def test_11_delete_org(self): - org = {'id': 'org_no_user', 'title': 'test'} - self._call_api('organization_delete', org, 'org_editor', 403) - self._call_api('organization_delete', org, 'org_admin', 403) - org = {'id': 'org_with_user'} - self._call_api('organization_delete', org, 'org_editor', 403) - self._call_api('organization_delete', org, 'org_admin', 403) - - -class TestAuthOrgHierarchy(TestAuth): + member = { + "username": "editor_wannabe", + "role": "editor", + "id": "org_with_user", + } + call_api("organization_member_create", member, "org_editor", 403) + + # def test_07_add_datasets(self): + self._add_datasets("org_admin", call_api) + self._add_datasets("org_editor", call_api) + + # def test_08_update_datasets(self): + self._update_datasets("org_admin", call_api) + self._update_datasets("org_editor", call_api) + + # def test_09_delete_datasets(self): + self._delete_datasets("org_admin", call_api) + self._delete_datasets("org_editor", call_api) + + # def test_10_edit_org(self): + org = {"id": "org_no_user", "title": "test"} + # change an org user does not belong to + call_api("organization_update", org, "org_editor", 403) + call_api("organization_update", org, "org_admin", 403) + + # change an org a user belongs to + org = {"id": "org_with_user", "title": "test"} + call_api("organization_update", org, "org_editor", 403) + call_api("organization_update", org, "org_admin", 200) + + # def test_11_delete_org(self): + org = {"id": "org_no_user", "title": "test"} + call_api("organization_delete", org, "org_editor", 403) + call_api("organization_delete", org, "org_admin", 403) + org = {"id": "org_with_user"} + call_api("organization_delete", org, "org_editor", 403) + call_api("organization_delete", org, "org_admin", 403) + + +class TestAuthOrgHierarchy(object): # Tests are in the same vein as TestAuthOrgs, testing the cases where the # group hierarchy provides extra permissions through cascading - @classmethod - def setup_class(cls): - - super(TestAuth, cls).setup_class() - + @pytest.fixture(autouse=True) + def initial_data(self, apikeys, monkeypatch, ckan_config): + monkeypatch.setitem( + ckan_config, "ckan.auth.roles_that_cascade_to_sub_groups", "admin" + ) CreateTestData.create_group_hierarchy_test_data() - - cls.apikeys = {} for user in model.Session.query(model.User): - cls.apikeys[user.name] = str(user.apikey) + apikeys[user.name] = str(user.apikey) - cls.sysadmin = get_action('get_site_user')( - {'model': model, 'ignore_auth': True}, {}) + self.sysadmin = get_action("get_site_user")( + {"model": model, "ignore_auth": True}, {} + ) CreateTestData.create_arbitrary( - package_dicts= [{'name': 'adataset', - 'groups': ['national-health-service']}], - extra_user_names=['john']) - - cls.app = cls._get_test_app() - - @classmethod - def _apply_config_changes(cls, config): - - config['ckan.auth.roles_that_cascade_to_sub_groups'] = 'admin' + package_dicts=[ + {"name": "adataset", "groups": ["national-health-service"]} + ], + extra_user_names=["john"], + ) def _reset_a_datasets_owner_org(self): - get_action('package_owner_org_update')( - {'model': model, 'user': self.sysadmin['name'], - 'ignore_auth': True}, - {'id': 'adataset', - 'organization_id': 'national-health-service'}) + get_action("package_owner_org_update")( + { + "model": model, + "user": self.sysadmin["name"], + "ignore_auth": True, + }, + {"id": "adataset", "organization_id": "national-health-service"}, + ) def _undelete_package_if_needed(self, package_name): pkg = model.Package.by_name(package_name) - if pkg and pkg.state == 'deleted': - pkg.state = 'active' + if pkg and pkg.state == "deleted": + pkg.state = "active" model.repo.commit_and_remove() - def test_05_add_users_to_org_1(self): - member = {'username': 'john', 'role': 'admin', - 'id': 'department-of-health'} - self._call_api('organization_member_create', member, 'nhsadmin', 403) - def test_05_add_users_to_org_2(self): - member = {'username': 'john', 'role': 'editor', - 'id': 'department-of-health'} - self._call_api('organization_member_create', member, 'nhsadmin', 403) - def test_05_add_users_to_org_3(self): - member = {'username': 'john', 'role': 'admin', - 'id': 'national-health-service'} - self._call_api('organization_member_create', member, 'nhsadmin', 200) - def test_05_add_users_to_org_4(self): - member = {'username': 'john', 'role': 'editor', - 'id': 'national-health-service'} - self._call_api('organization_member_create', member, 'nhsadmin', 200) - def test_05_add_users_to_org_5(self): - member = {'username': 'john', 'role': 'admin', - 'id': 'nhs-wirral-ccg'} - self._call_api('organization_member_create', member, 'nhsadmin', 200) - def test_05_add_users_to_org_6(self): - member = {'username': 'john', 'role': 'editor', - 'id': 'nhs-wirral-ccg'} - self._call_api('organization_member_create', member, 'nhsadmin', 200) - def test_05_add_users_to_org_7(self): - member = {'username': 'john', 'role': 'editor', - 'id': 'national-health-service'} - self._call_api('organization_member_create', member, 'nhseditor', 403) - - def test_07_add_datasets_1(self): - dataset = {'name': 't1', 'owner_org': 'department-of-health'} - self._call_api('package_create', dataset, 'nhsadmin', 403) - - def test_07_add_datasets_2(self): - dataset = {'name': 't2', 'owner_org': 'national-health-service'} - self._call_api('package_create', dataset, 'nhsadmin', 200) - - def test_07_add_datasets_3(self): - dataset = {'name': 't3', 'owner_org': 'nhs-wirral-ccg'} - self._call_api('package_create', dataset, 'nhsadmin', 200) - - def test_07_add_datasets_4(self): - dataset = {'name': 't4', 'owner_org': 'department-of-health'} - self._call_api('package_create', dataset, 'nhseditor', 403) - - def test_07_add_datasets_5(self): - dataset = {'name': 't5', 'owner_org': 'national-health-service'} - self._call_api('package_create', dataset, 'nhseditor', 200) - - def test_07_add_datasets_6(self): - dataset = {'name': 't6', 'owner_org': 'nhs-wirral-ccg'} - self._call_api('package_create', dataset, 'nhseditor', 403) - - def test_08_update_datasets_1(self): - dataset = {'name': 'adataset', 'owner_org': 'department-of-health'} - self._call_api('package_update', dataset, 'nhsadmin', 409) - - def test_08_update_datasets_2(self): - dataset = {'name': 'adataset', 'owner_org': 'national-health-service'} - self._call_api('package_update', dataset, 'nhsadmin', 200) - - def test_08_update_datasets_3(self): - dataset = {'name': 'adataset', 'owner_org': 'nhs-wirral-ccg'} + def test_05_add_users_to_org_1(self, call_api): + member = { + "username": "john", + "role": "admin", + "id": "department-of-health", + } + call_api("organization_member_create", member, "nhsadmin", 403) + # def test_05_add_users_to_org_2(self): + member = { + "username": "john", + "role": "editor", + "id": "department-of-health", + } + call_api("organization_member_create", member, "nhsadmin", 403) + # def test_05_add_users_to_org_3(self): + member = { + "username": "john", + "role": "admin", + "id": "national-health-service", + } + call_api("organization_member_create", member, "nhsadmin", 200) + # def test_05_add_users_to_org_4(self): + member = { + "username": "john", + "role": "editor", + "id": "national-health-service", + } + call_api("organization_member_create", member, "nhsadmin", 200) + # def test_05_add_users_to_org_5(self): + member = {"username": "john", "role": "admin", "id": "nhs-wirral-ccg"} + call_api("organization_member_create", member, "nhsadmin", 200) + # def test_05_add_users_to_org_6(self): + member = {"username": "john", "role": "editor", "id": "nhs-wirral-ccg"} + call_api("organization_member_create", member, "nhsadmin", 200) + # def test_05_add_users_to_org_7(self): + member = { + "username": "john", + "role": "editor", + "id": "national-health-service", + } + call_api("organization_member_create", member, "nhseditor", 403) + + # def test_07_add_datasets_1(self, call_api): + dataset = {"name": "t1", "owner_org": "department-of-health"} + call_api("package_create", dataset, "nhsadmin", 403) + + # def test_07_add_datasets_2(self): + dataset = {"name": "t2", "owner_org": "national-health-service"} + call_api("package_create", dataset, "nhsadmin", 200) + + # def test_07_add_datasets_3(self): + dataset = {"name": "t3", "owner_org": "nhs-wirral-ccg"} + call_api("package_create", dataset, "nhsadmin", 200) + + # def test_07_add_datasets_4(self): + dataset = {"name": "t4", "owner_org": "department-of-health"} + call_api("package_create", dataset, "nhseditor", 403) + + # def test_07_add_datasets_5(self): + dataset = {"name": "t5", "owner_org": "national-health-service"} + call_api("package_create", dataset, "nhseditor", 200) + + # def test_07_add_datasets_6(self): + dataset = {"name": "t6", "owner_org": "nhs-wirral-ccg"} + call_api("package_create", dataset, "nhseditor", 403) + + # def test_08_update_datasets_1(self, call_api): + dataset = {"name": "adataset", "owner_org": "department-of-health"} + call_api("package_update", dataset, "nhsadmin", 409) + + # def test_08_update_datasets_2(self): + dataset = {"name": "adataset", "owner_org": "national-health-service"} + call_api("package_update", dataset, "nhsadmin", 200) + + # def test_08_update_datasets_3(self): + dataset = {"name": "adataset", "owner_org": "nhs-wirral-ccg"} try: - self._call_api('package_update', dataset, 'nhsadmin', 200) + call_api("package_update", dataset, "nhsadmin", 200) finally: self._reset_a_datasets_owner_org() - def test_08_update_datasets_4(self): - dataset = {'name': 'adataset', 'owner_org': 'department-of-health'} - self._call_api('package_update', dataset, 'nhseditor', 409) + # def test_08_update_datasets_4(self): + dataset = {"name": "adataset", "owner_org": "department-of-health"} + call_api("package_update", dataset, "nhseditor", 409) - def test_08_update_datasets_5(self): - dataset = {'name': 'adataset', 'owner_org': 'national-health-service'} + # def test_08_update_datasets_5(self): + dataset = {"name": "adataset", "owner_org": "national-health-service"} try: - self._call_api('package_update', dataset, 'nhseditor', 200) + call_api("package_update", dataset, "nhseditor", 200) finally: self._reset_a_datasets_owner_org() - def test_08_update_datasets_6(self): - dataset = {'name': 'adataset', 'owner_org': 'nhs-wirral-ccg'} - self._call_api('package_update', dataset, 'nhseditor', 409) + # def test_08_update_datasets_6(self): + dataset = {"name": "adataset", "owner_org": "nhs-wirral-ccg"} + call_api("package_update", dataset, "nhseditor", 409) - def test_09_delete_datasets_1(self): - dataset = {'id': 'doh-spend'} + # def test_09_delete_datasets_1(self, call_api): + dataset = {"id": "doh-spend"} try: - self._call_api('package_delete', dataset, 'nhsadmin', 403) + call_api("package_delete", dataset, "nhsadmin", 403) finally: - self._undelete_package_if_needed(dataset['id']) + self._undelete_package_if_needed(dataset["id"]) - def test_09_delete_datasets_2(self): - dataset = {'id': 'nhs-spend'} + # def test_09_delete_datasets_2(self): + dataset = {"id": "nhs-spend"} try: - self._call_api('package_delete', dataset, 'nhsadmin', 200) + call_api("package_delete", dataset, "nhsadmin", 200) finally: - self._undelete_package_if_needed(dataset['id']) + self._undelete_package_if_needed(dataset["id"]) - def test_09_delete_datasets_3(self): - dataset = {'id': 'wirral-spend'} + # def test_09_delete_datasets_3(self): + dataset = {"id": "wirral-spend"} try: - self._call_api('package_delete', dataset, 'nhsadmin', 200) + call_api("package_delete", dataset, "nhsadmin", 200) finally: - self._undelete_package_if_needed(dataset['id']) + self._undelete_package_if_needed(dataset["id"]) - def test_09_delete_datasets_4(self): - dataset = {'id': 'nhs-spend'} + # def test_09_delete_datasets_4(self): + dataset = {"id": "nhs-spend"} try: - self._call_api('package_delete', dataset, 'nhseditor', 200) + call_api("package_delete", dataset, "nhseditor", 200) finally: - self._undelete_package_if_needed(dataset['id']) + self._undelete_package_if_needed(dataset["id"]) - def test_09_delete_datasets_5(self): - dataset = {'id': 'wirral-spend'} + # def test_09_delete_datasets_5(self): + dataset = {"id": "wirral-spend"} try: - self._call_api('package_delete', dataset, 'nhseditor', 403) + call_api("package_delete", dataset, "nhseditor", 403) finally: - self._undelete_package_if_needed(dataset['id']) - - def _flesh_out_organization(self, org): - # When calling organization_update, unless you include the list of - # editor and admin users and parent groups, it will remove them. So - # get the current list - existing_org = get_action('organization_show')( - {'model': model, 'ignore_auth': True}, {'id': org['id']}) - org.update(existing_org) + self._undelete_package_if_needed(dataset["id"]) - def test_10_edit_org_1(self): - org = {'id': 'department-of-health', 'title': 'test'} + # def test_10_edit_org_1(self, call_api): + org = {"id": "department-of-health", "title": "test"} self._flesh_out_organization(org) - self._call_api('organization_update', org, 'nhsadmin', 403) + call_api("organization_update", org, "nhsadmin", 403) - def test_10_edit_org_2(self): - org = {'id': 'national-health-service', 'title': 'test'} + # def test_10_edit_org_2(self): + org = {"id": "national-health-service", "title": "test"} self._flesh_out_organization(org) - self._call_api('organization_update', org, 'nhsadmin', 200) + call_api("organization_update", org, "nhsadmin", 200) - def test_10_edit_org_3(self): - org = {'id': 'nhs-wirral-ccg', 'title': 'test'} + # def test_10_edit_org_3(self): + org = {"id": "nhs-wirral-ccg", "title": "test"} self._flesh_out_organization(org) - self._call_api('organization_update', org, 'nhsadmin', 200) + call_api("organization_update", org, "nhsadmin", 200) - def test_10_edit_org_4(self): - org = {'id': 'department-of-health', 'title': 'test'} + # def test_10_edit_org_4(self): + org = {"id": "department-of-health", "title": "test"} self._flesh_out_organization(org) - self._call_api('organization_update', org, 'nhseditor', 403) + call_api("organization_update", org, "nhseditor", 403) - def test_10_edit_org_5(self): - org = {'id': 'national-health-service', 'title': 'test'} + # def test_10_edit_org_5(self): + org = {"id": "national-health-service", "title": "test"} self._flesh_out_organization(org) - self._call_api('organization_update', org, 'nhseditor', 403) + call_api("organization_update", org, "nhseditor", 403) - def test_10_edit_org_6(self): - org = {'id': 'nhs-wirral-ccg', 'title': 'test'} + # def test_10_edit_org_6(self): + org = {"id": "nhs-wirral-ccg", "title": "test"} self._flesh_out_organization(org) - self._call_api('organization_update', org, 'nhseditor', 403) + call_api("organization_update", org, "nhseditor", 403) - def test_11_delete_org_1(self): - org = {'id': 'department-of-health'} - self._call_api('organization_delete', org, 'nhsadmin', 403) - self._call_api('organization_delete', org, 'nhseditor', 403) + # def test_11_delete_org_1(self, call_api): + org = {"id": "department-of-health"} + call_api("organization_delete", org, "nhsadmin", 403) + call_api("organization_delete", org, "nhseditor", 403) - def test_11_delete_org_2(self): - org = {'id': 'national-health-service'} - self._call_api('organization_delete', org, 'nhsadmin', 200) - self._call_api('organization_delete', org, 'nhseditor', 403) + # def test_11_delete_org_2(self): + org = {"id": "national-health-service"} + call_api("organization_delete", org, "nhsadmin", 200) + call_api("organization_delete", org, "nhseditor", 403) - def test_11_delete_org_3(self): - org = {'id': 'nhs-wirral-ccg'} - self._call_api('organization_delete', org, 'nhsadmin', 403) - self._call_api('organization_delete', org, 'nhseditor', 403) + # def test_11_delete_org_3(self): + org = {"id": "nhs-wirral-ccg"} + call_api("organization_delete", org, "nhsadmin", 403) + call_api("organization_delete", org, "nhseditor", 403) + def _flesh_out_organization(self, org): + # When calling organization_update, unless you include the list of + # editor and admin users and parent groups, it will remove them. So + # get the current list + existing_org = get_action("organization_show")( + {"model": model, "ignore_auth": True}, {"id": org["id"]} + ) + org.update(existing_org) -class TestAuthGroups(TestAuth): - def test_01_create_groups(self): - group = {'name': 'group_no_user'} - self._call_api('group_create', group, 'random_key', 403) - self._call_api('group_create', group, 'sysadmin') +class TestAuthGroups(object): + @pytest.mark.usefixtures("auth_config") + def test_auth_groups(self, call_api, create_user): + group = {"name": "group_no_user"} + call_api("group_create", group, "random_key", 403) + call_api("group_create", group, "sysadmin") - group = {'name': 'group_with_user'} - self._call_api('group_create', group, 'random_key', 403) - self._call_api('group_create', group, 'sysadmin') + group = {"name": "group_with_user"} + call_api("group_create", group, "random_key", 403) + call_api("group_create", group, "sysadmin") - def test_02_add_users_to_group(self): - self.create_user('org_admin') - self.create_user('org_editor') - self.create_user('org_editor_wannabe') - self.create_user('no_group') + # def test_02_add_users_to_group(self): + create_user("org_admin") + create_user("org_editor") + create_user("org_editor_wannabe") + create_user("no_group") - member = {'username': 'org_admin', - 'role': 'admin', - 'id': 'group_with_user'} - self._call_api('group_member_create', member, 'sysadmin') + member = { + "username": "org_admin", + "role": "admin", + "id": "group_with_user", + } + call_api("group_member_create", member, "sysadmin") ## admin user should be able to add users now - member = {'username': 'org_editor', - 'role': 'editor', - 'id': 'group_with_user'} - self._call_api('group_member_create', member, 'org_admin') + member = { + "username": "org_editor", + "role": "editor", + "id": "group_with_user", + } + call_api("group_member_create", member, "org_admin") ## editor should not be able to approve others as editors - member = {'username': 'org_editor_wannabe', - 'role': 'editor', - 'id': 'group_with_user'} - self._call_api('group_member_create', member, 'org_editor', 403) - - def test_03_add_dataset_to_group(self): - org = {'name': 'org'} - self._call_api('organization_create', org, 'sysadmin') - package = {'name': 'package_added_by_admin', 'owner_org': 'org'} - self._call_api('package_create', package, 'sysadmin') - package = {'name': 'package_added_by_editor', 'owner_org': 'org'} - self._call_api('package_create', package, 'sysadmin') - - res = self._call_api('group_show', - {'id': 'group_with_user'}, - 'org_admin') - group = json.loads(res.body)['result'] - self._call_api('group_update', group, 'no_group', 403) - self._call_api('group_update', group, 'org_admin') - - group = {'id': 'group_with_user', - 'packages': [{'id': 'package_added_by_admin'}, - {'id': 'package_added_by_editor'}]} + member = { + "username": "org_editor_wannabe", + "role": "editor", + "id": "group_with_user", + } + call_api("group_member_create", member, "org_editor", 403) + + # def test_03_add_dataset_to_group(self): + org = {"name": "org"} + call_api("organization_create", org, "sysadmin") + package = {"name": "package_added_by_admin", "owner_org": "org"} + call_api("package_create", package, "sysadmin") + package = {"name": "package_added_by_editor", "owner_org": "org"} + call_api("package_create", package, "sysadmin") + + res = call_api("group_show", {"id": "group_with_user"}, "org_admin") + group = json.loads(res.body)["result"] + call_api("group_update", group, "no_group", 403) + call_api("group_update", group, "org_admin") + + group = { + "id": "group_with_user", + "packages": [ + {"id": "package_added_by_admin"}, + {"id": "package_added_by_editor"}, + ], + } # org editor doesn't have edit rights - self._call_api('group_update', group, 'org_editor', 403) - - def test_04_modify_group(self): - res = self._call_api('group_show', - {'id': 'group_with_user'}, - 'org_admin') - group = json.loads(res.body)['result'] - group.update({ - 'title': 'moo', - 'packages': [{'id': 'package_added_by_admin'}] - }) - self._call_api('group_update', group, 'org_admin') + call_api("group_update", group, "org_editor", 403) + + # def test_04_modify_group(self): + res = call_api("group_show", {"id": "group_with_user"}, "org_admin") + group = json.loads(res.body)["result"] + group.update( + {"title": "moo", "packages": [{"id": "package_added_by_admin"}]} + ) + call_api("group_update", group, "org_admin") # need to think about this as is horrible may just let editor edit # group for this case even though spec says otherwise - self._call_api('group_update', group, 'org_editor', 403) - - def test_05_delete_group(self): - org = {'id': 'group_with_user'} - self._call_api('group_delete', org, 'org_editor', 403) - self._call_api('group_delete', org, 'org_admin', 403) - org = {'id': 'group_with_user'} - self._call_api('group_delete', org, 'org_editor', 403) - self._call_api('group_delete', org, 'org_admin', 403) + call_api("group_update", group, "org_editor", 403) + + # def test_05_delete_group(self): + org = {"id": "group_with_user"} + call_api("group_delete", org, "org_editor", 403) + call_api("group_delete", org, "org_admin", 403) + org = {"id": "group_with_user"} + call_api("group_delete", org, "org_editor", 403) + call_api("group_delete", org, "org_admin", 403) diff --git a/ckan/tests/legacy/logic/test_init.py b/ckan/tests/legacy/logic/test_init.py index 91476c6782a..33e075b37eb 100644 --- a/ckan/tests/legacy/logic/test_init.py +++ b/ckan/tests/legacy/logic/test_init.py @@ -1,7 +1,6 @@ # encoding: utf-8 -import nose.tools as tools - +import pytest import ckan.model as model import ckan.logic as logic @@ -10,61 +9,49 @@ class TestMemberLogic(object): def test_model_name_to_class(self): - assert logic.model_name_to_class(model, 'package') == model.Package - tools.assert_raises(logic.ValidationError, - logic.model_name_to_class, - model, - 'inexistent_model_name') + assert logic.model_name_to_class(model, "package") == model.Package + with pytest.raises(logic.ValidationError): + logic.model_name_to_class(model, "inexistent_model_name") class TestCheckAccess(object): - - @classmethod - def setup_class(cls): - model.Session.close_all() - model.repo.delete_all() - - @classmethod - def teardown_class(cls): - model.repo.rebuild_db() - - def setup(self): - model.repo.rebuild_db() - + @pytest.mark.usefixtures("clean_db") def test_check_access_auth_user_obj_is_set(self): create_test_data.CreateTestData.create_test_user() - user_name = 'tester' - context = {'user': user_name} + user_name = "tester" + context = {"user": user_name} - result = logic.check_access('package_create', context) + result = logic.check_access("package_create", context) assert result - assert context['__auth_user_obj_checked'] - assert context['auth_user_obj'].name == user_name + assert context["__auth_user_obj_checked"] + assert context["auth_user_obj"].name == user_name + @pytest.mark.usefixtures("clean_db") def test_check_access_auth_user_obj_is_not_set_when_ignoring_auth(self): create_test_data.CreateTestData.create_test_user() - user_name = 'tester' - context = {'user': user_name, 'ignore_auth': True} + user_name = "tester" + context = {"user": user_name, "ignore_auth": True} - result = logic.check_access('package_create', context) + result = logic.check_access("package_create", context) assert result - assert '__auth_user_obj_checked' not in context - assert context['auth_user_obj'] is None + assert "__auth_user_obj_checked" not in context + assert context["auth_user_obj"] is None + @pytest.mark.usefixtures("clean_db") def test_check_access_auth_user_obj_is_not_set(self): - user_names = ('unknown_user', '', None,) + user_names = ("unknown_user", "", None) for user_name in user_names: - context = {'user': user_name} + context = {"user": user_name} - result = logic.check_access('package_search', context) + result = logic.check_access("package_search", context) assert result - assert context['__auth_user_obj_checked'] - assert context['auth_user_obj'] is None + assert context["__auth_user_obj_checked"] + assert context["auth_user_obj"] is None diff --git a/ckan/tests/legacy/logic/test_member.py b/ckan/tests/legacy/logic/test_member.py index 5269517d93e..73d3db9e8d8 100644 --- a/ckan/tests/legacy/logic/test_member.py +++ b/ckan/tests/legacy/logic/test_member.py @@ -1,206 +1,223 @@ # encoding: utf-8 -from nose.tools import assert_raises import ckan.model as model import ckan.logic as logic import ckan.lib.create_test_data as create_test_data +import pytest class TestMemberLogic(object): - @classmethod - def setup_class(cls): + @pytest.fixture(autouse=True) + def setup_method(self, clean_db): create_test_data.CreateTestData.create() - cls.user = model.User.get('testsysadmin') - cls.tester = model.User.get('tester') - cls.group = model.Group.get('david') - cls.roger = model.Group.get('roger') - cls.pkgs = [model.Package.by_name('warandpeace'), - model.Package.by_name('annakarenina')] + self.user = model.User.get("testsysadmin") + self.tester = model.User.get("tester") + self.group = model.Group.get("david") + self.roger = model.Group.get("roger") + self.pkgs = [ + model.Package.by_name("warandpeace"), + model.Package.by_name("annakarenina"), + ] # 'Tester' becomes an admin for the 'roger' group - model.Member(group=cls.roger, table_id=cls.tester.id, - table_name='user', capacity='admin') + model.Member( + group=self.roger, + table_id=self.tester.id, + table_name="user", + capacity="admin", + ) model.repo.commit_and_remove() - @classmethod - def teardown_class(cls): - model.repo.rebuild_db() - def test_member_create(self): - self._member_create(self.pkgs[0].id, 'package', 'public') + self._member_create(self.pkgs[0].id, "package", "public") res = self._member_list() - assert (self.pkgs[0].id, 'package', 'public') in res, res + assert (self.pkgs[0].id, "package", "public") in res, res def test_member_create_should_update_member_if_it_already_exists(self): - initial = self._member_create(self.pkgs[0].id, 'package', 'public') - final = self._member_create(self.pkgs[0].id, 'package', 'private') - assert initial['id'] == final['id'], [initial, final] - assert initial['capacity'] == u'public' - assert final['capacity'] == u'private' + initial = self._member_create(self.pkgs[0].id, "package", "public") + final = self._member_create(self.pkgs[0].id, "package", "private") + assert initial["id"] == final["id"], [initial, final] + assert initial["capacity"] == u"public" + assert final["capacity"] == u"private" def test_member_create_raises_if_user_unauthorized_to_update_group(self): - ctx, dd = self._build_context(self.pkgs[0].id, 'package', - user='unauthorized_user') - assert_raises(logic.NotAuthorized, - logic.get_action('member_create'), ctx, dd) + ctx, dd = self._build_context( + self.pkgs[0].id, "package", user="unauthorized_user" + ) + with pytest.raises(logic.NotAuthorized): + logic.get_action("member_create")(ctx, dd) def test_member_create_with_child_group_permission(self): # 'tester' has admin priviledge for roger, so anyone can make it # a child group. - self._member_create_group_hierarchy(parent_group=self.group, - child_group=self.roger, - user=self.tester) + self._member_create_group_hierarchy( + parent_group=self.group, child_group=self.roger, user=self.tester + ) def test_member_create_raises_when_only_have_parent_group_permission(self): - assert_raises(logic.NotAuthorized, - self._member_create_group_hierarchy, - self.roger, # parent - self.group, # child - self.tester) + with pytest.raises(logic.NotAuthorized): + self._member_create_group_hierarchy( + self.roger, self.group, self.tester # parent # child + ) def test_member_create_accepts_group_name_or_id(self): - by_name = self._member_create_in_group(self.pkgs[0].id, 'package', - 'public', self.group.name) - by_id = self._member_create_in_group(self.pkgs[0].id, 'package', - 'public', self.group.id) - assert by_name['id'] == by_id['id'] + by_name = self._member_create_in_group( + self.pkgs[0].id, "package", "public", self.group.name + ) + by_id = self._member_create_in_group( + self.pkgs[0].id, "package", "public", self.group.id + ) + assert by_name["id"] == by_id["id"] def test_member_create_accepts_object_name_or_id(self): - test_cases = ((self.pkgs[0], 'package', 'public'), - (self.user, 'user', 'admin')) + test_cases = ( + (self.pkgs[0], "package", "public"), + (self.user, "user", "admin"), + ) for case in test_cases: obj = case[0] by_name = self._member_create(obj.name, case[1], case[2]) by_id = self._member_create(obj.id, case[1], case[2]) - assert by_name['id'] == by_id['id'] + assert by_name["id"] == by_id["id"] def test_member_create_raises_if_any_required_parameter_isnt_defined(self): - ctx, dd = self._build_context(self.pkgs[0].id, 'package') + ctx, dd = self._build_context(self.pkgs[0].id, "package") for key in dd.keys(): new_dd = dd.copy() del new_dd[key] - assert_raises(logic.ValidationError, - logic.get_action('member_create'), ctx, new_dd) + with pytest.raises(logic.ValidationError): + logic.get_action("member_create")(ctx, new_dd) def test_member_create_raises_if_group_wasnt_found(self): - assert_raises(logic.NotFound, - self._member_create_in_group, - self.pkgs[0].id, 'package', 'public', 'inexistent_group') + with pytest.raises(logic.NotFound): + self._member_create_in_group( + self.pkgs[0].id, "package", "public", "inexistent_group" + ) def test_member_create_raises_if_object_wasnt_found(self): - assert_raises(logic.NotFound, - self._member_create, - 'inexistent_package', 'package', 'public') + with pytest.raises(logic.NotFound): + self._member_create("inexistent_package", "package", "public") def test_member_create_raises_if_object_type_is_invalid(self): - assert_raises(logic.ValidationError, - self._member_create, - 'obj_id', 'invalid_obj_type', 'public') + with pytest.raises(logic.ValidationError): + self._member_create("obj_id", "invalid_obj_type", "public") def test_member_list(self): - self._member_create(self.pkgs[0].id, 'package', 'public') - self._member_create(self.pkgs[1].id, 'package', 'public') - res = self._member_list('package') - assert (self.pkgs[0].id, 'package', 'public') in res - assert (self.pkgs[1].id, 'package', 'public') in res + self._member_create(self.pkgs[0].id, "package", "public") + self._member_create(self.pkgs[1].id, "package", "public") + res = self._member_list("package") + assert (self.pkgs[0].id, "package", "public") in res + assert (self.pkgs[1].id, "package", "public") in res - res = self._member_list('user', 'admin') + res = self._member_list("user", "admin") assert len(res) == 0, res - assert_raises(logic.NotFound, - self._member_list, 'user', 'admin', 'inexistent_group') + with pytest.raises(logic.NotFound): + self._member_list("user", "admin", "inexistent_group") - self._member_create(self.user.id, 'user', 'admin') - res = self._member_list('user', 'admin') - assert (self.user.id, 'user', 'Admin') in res, res + self._member_create(self.user.id, "user", "admin") + res = self._member_list("user", "admin") + assert (self.user.id, "user", "Admin") in res, res def test_member_create_accepts_group_name_or_id(self): for group_key in [self.group.id, self.group.name]: - self._member_create(self.user.id, 'user', 'admin') + self._member_create(self.user.id, "user", "admin") - self._member_delete_in_group(self.user.id, 'user', group_key) + self._member_delete_in_group(self.user.id, "user", group_key) - res = self._member_list('user', 'admin') - assert (self.user.id, 'user', 'Admin') not in res, res + res = self._member_list("user", "admin") + assert (self.user.id, "user", "Admin") not in res, res def test_member_delete_accepts_object_name_or_id(self): for key in [self.user.id, self.user.name]: - self._member_create(key, 'user', 'admin') + self._member_create(key, "user", "admin") - self._member_delete(key, 'user') + self._member_delete(key, "user") - res = self._member_list('user', 'admin') - assert (self.user.id, 'user', 'Admin') not in res, res + res = self._member_list("user", "admin") + assert (self.user.id, "user", "Admin") not in res, res def test_member_delete_raises_if_user_unauthorized_to_update_group(self): - ctx, dd = self._build_context(self.pkgs[0].id, 'package', - user='unauthorized_user') - assert_raises(logic.NotAuthorized, - logic.get_action('member_delete'), ctx, dd) + ctx, dd = self._build_context( + self.pkgs[0].id, "package", user="unauthorized_user" + ) + with pytest.raises(logic.NotAuthorized): + logic.get_action("member_delete")(ctx, dd) def test_member_delete_raises_if_any_required_parameter_isnt_defined(self): - ctx, dd = self._build_context(self.pkgs[0].id, 'package') - for key in ['id', 'object', 'object_type']: + ctx, dd = self._build_context(self.pkgs[0].id, "package") + for key in ["id", "object", "object_type"]: new_dd = dd.copy() del new_dd[key] - assert_raises(logic.ValidationError, - logic.get_action('member_delete'), ctx, new_dd) + with pytest.raises(logic.ValidationError): + logic.get_action("member_delete")(ctx, new_dd) def test_member_delete_raises_if_group_wasnt_found(self): - assert_raises(logic.NotFound, - self._member_delete_in_group, - self.pkgs[0].id, 'package', 'inexistent_group') + with pytest.raises(logic.NotFound): + self._member_delete_in_group( + self.pkgs[0].id, "package", "inexistent_group" + ) def test_member_delete_raises_if_object_wasnt_found(self): - assert_raises(logic.NotFound, - self._member_delete, 'unexistent_package', 'package') + with pytest.raises(logic.NotFound): + self._member_delete("unexistent_package", "package") def test_member_delete_raises_if_object_type_is_invalid(self): - assert_raises(logic.ValidationError, - self._member_delete, 'obj_id', 'invalid_obj_type') + with pytest.raises(logic.ValidationError): + self._member_delete("obj_id", "invalid_obj_type") def _member_create(self, obj, obj_type, capacity): - '''Makes the given object a member of cls.group.''' + """Makes the given object a member of cls.group.""" ctx, dd = self._build_context(obj, obj_type, capacity) - return logic.get_action('member_create')(ctx, dd) + return logic.get_action("member_create")(ctx, dd) def _member_create_in_group(self, obj, obj_type, capacity, group_id): - '''Makes the given object a member of the given group.''' + """Makes the given object a member of the given group.""" ctx, dd = self._build_context(obj, obj_type, capacity, group_id) - return logic.get_action('member_create')(ctx, dd) + return logic.get_action("member_create")(ctx, dd) def _member_create_as_user(self, obj, obj_type, capacity, user): - '''Makes the given object a member of cls.group using privileges of - the given user.''' + """Makes the given object a member of cls.group using privileges of + the given user.""" ctx, dd = self._build_context(obj, obj_type, capacity, user=user) - return logic.get_action('member_create')(ctx, dd) + return logic.get_action("member_create")(ctx, dd) def _member_list(self, obj_type=None, capacity=None, group_id=None): ctx, dd = self._build_context(None, obj_type, capacity, group_id) - return logic.get_action('member_list')(ctx, dd) + return logic.get_action("member_list")(ctx, dd) def _member_delete(self, obj, obj_type): ctx, dd = self._build_context(obj, obj_type) - return logic.get_action('member_delete')(ctx, dd) + return logic.get_action("member_delete")(ctx, dd) def _member_delete_in_group(self, obj, obj_type, group_id): ctx, dd = self._build_context(obj, obj_type, group_id=group_id) - return logic.get_action('member_delete')(ctx, dd) + return logic.get_action("member_delete")(ctx, dd) def _member_create_group_hierarchy(self, parent_group, child_group, user): - ctx, dd = self._build_context(parent_group.name, 'group', 'parent', - group_id=child_group.name, user=user.id) - return logic.get_action('member_create')(ctx, dd) - - def _build_context(self, obj, obj_type, capacity='public', - group_id=None, user=None): - ctx = {'model': model, - 'session': model.Session, - 'user': user or self.user.id} - ctx['auth_user_obj'] = model.User.get(ctx['user']) - dd = {'id': group_id or self.group.name, - 'object': obj, - 'object_type': obj_type, - 'capacity': capacity} + ctx, dd = self._build_context( + parent_group.name, + "group", + "parent", + group_id=child_group.name, + user=user.id, + ) + return logic.get_action("member_create")(ctx, dd) + + def _build_context( + self, obj, obj_type, capacity="public", group_id=None, user=None + ): + ctx = { + "model": model, + "session": model.Session, + "user": user or self.user.id, + } + ctx["auth_user_obj"] = model.User.get(ctx["user"]) + dd = { + "id": group_id or self.group.name, + "object": obj, + "object_type": obj_type, + "capacity": capacity, + } return ctx, dd diff --git a/ckan/tests/legacy/logic/test_tag.py b/ckan/tests/legacy/logic/test_tag.py index d9979f829b9..2df3a988fca 100644 --- a/ckan/tests/legacy/logic/test_tag.py +++ b/ckan/tests/legacy/logic/test_tag.py @@ -1,372 +1,434 @@ # encoding: utf-8 import json -from nose.tools import assert_equal -import ckan.lib.search as search - +import pytest import ckan.model as model from ckan.lib.create_test_data import CreateTestData -from ckan.tests.legacy import WsgiAppCase from ckan.tests.legacy import StatusCodes -class TestAction(WsgiAppCase): - @classmethod - def setup_class(cls): - search.clear_all() + +class TestAction(object): + @pytest.fixture(autouse=True) + def setup_class(self, clean_db, clean_index): CreateTestData.create() - cls.sysadmin_user = model.User.get('testsysadmin') - cls.normal_user = model.User.get('annafan') + self.sysadmin_user = model.User.get("testsysadmin") CreateTestData.make_some_vocab_tags() - @classmethod - def teardown_class(cls): - model.repo.rebuild_db() - - def test_08_user_create_not_authorized(self): - postparams = '%s=1' % json.dumps({'name':'test_create_from_action_api', 'password':'testpass'}) - res = self.app.post('/api/action/user_create', params=postparams, - status=StatusCodes.STATUS_403_ACCESS_DENIED) + def test_08_user_create_not_authorized(self, app): + postparams = "%s=1" % json.dumps( + {"name": "test_create_from_action_api", "password": "testpass"} + ) + res = app.post( + "/api/action/user_create", + params=postparams, + status=StatusCodes.STATUS_403_ACCESS_DENIED, + ) res_obj = json.loads(res.body) - assert '/api/3/action/help_show?name=user_create' in res_obj['help'] - assert res_obj['success'] is False - assert res_obj['error']['__type'] == 'Authorization Error' - - def test_09_user_create(self): - user_dict = {'name':'test_create_from_action_api', - 'about': 'Just a test user', - 'email': 'me@test.org', - 'password':'testpass'} - - postparams = '%s=1' % json.dumps(user_dict) - res = self.app.post('/api/action/user_create', params=postparams, - extra_environ={'Authorization': str(self.sysadmin_user.apikey)}) + assert "/api/3/action/help_show?name=user_create" in res_obj["help"] + assert res_obj["success"] is False + assert res_obj["error"]["__type"] == "Authorization Error" + + def test_09_user_create(self, app): + user_dict = { + "name": "test_create_from_action_api", + "about": "Just a test user", + "email": "me@test.org", + "password": "testpass", + } + + postparams = "%s=1" % json.dumps(user_dict) + res = app.post( + "/api/action/user_create", + params=postparams, + extra_environ={"Authorization": str(self.sysadmin_user.apikey)}, + ) res_obj = json.loads(res.body) - assert '/api/3/action/help_show?name=user_create' in res_obj['help'] - assert res_obj['success'] == True - result = res_obj['result'] - assert result['name'] == user_dict['name'] - assert result['about'] == user_dict['about'] - assert 'apikey' in result - assert 'created' in result - assert 'display_name' in result - assert 'number_created_packages' in result - assert not 'password' in result - - def test_15a_tag_search_with_empty_query(self): - for q in ('missing', None, '', ' '): + assert "/api/3/action/help_show?name=user_create" in res_obj["help"] + assert res_obj["success"] == True + result = res_obj["result"] + assert result["name"] == user_dict["name"] + assert result["about"] == user_dict["about"] + assert "apikey" in result + assert "created" in result + assert "display_name" in result + assert "number_created_packages" in result + assert not "password" in result + + def test_15a_tag_search_with_empty_query(self, app): + for q in ("missing", None, "", " "): paramd = {} - if q != 'missing': - paramd['q'] = q + if q != "missing": + paramd["q"] = q params = json.dumps(paramd) - res = self.app.post('/api/action/tag_search', params=params) - assert res.json['success'] is True - assert res.json['result']['count'] == 0 - assert res.json['result']['results'] == [] + res = app.post("/api/action/tag_search", params=params) + assert res.json["success"] is True + assert res.json["result"]["count"] == 0 + assert res.json["result"]["results"] == [] - def test_15a_tag_search_with_no_matches(self): - paramd = {'q': 'no matches' } + def test_15a_tag_search_with_no_matches(self, app): + paramd = {"q": "no matches"} params = json.dumps(paramd) - res = self.app.post('/api/action/tag_search', params=params) - assert res.json['success'] is True - assert res.json['result']['count'] == 0 - assert res.json['result']['results'] == [] + res = app.post("/api/action/tag_search", params=params) + assert res.json["success"] is True + assert res.json["result"]["count"] == 0 + assert res.json["result"]["results"] == [] - def test_15a_tag_search_with_one_match(self): - paramd = {'q': 'russ' } + def test_15a_tag_search_with_one_match(self, app): + paramd = {"q": "russ"} params = json.dumps(paramd) - res = self.app.post('/api/action/tag_search', params=params) - assert res.json['success'] is True - assert res.json['result']['count'] == 1 - tag_dicts = res.json['result']['results'] + res = app.post("/api/action/tag_search", params=params) + assert res.json["success"] is True + assert res.json["result"]["count"] == 1 + tag_dicts = res.json["result"]["results"] assert len(tag_dicts) == 1 - assert tag_dicts[0]['name'] == 'russian' + assert tag_dicts[0]["name"] == "russian" - def test_15a_tag_search_with_one_match_using_fields_parameter(self): - paramd = {'fields': {'tags': 'russ'} } + def test_15a_tag_search_with_one_match_using_fields_parameter(self, app): + paramd = {"fields": {"tags": "russ"}} params = json.dumps(paramd) - res = self.app.post('/api/action/tag_search', params=params) - assert res.json['success'] is True - assert res.json['result']['count'] == 1 - tag_dicts = res.json['result']['results'] + res = app.post("/api/action/tag_search", params=params) + assert res.json["success"] is True + assert res.json["result"]["count"] == 1 + tag_dicts = res.json["result"]["results"] assert len(tag_dicts) == 1 - assert tag_dicts[0]['name'] == 'russian' + assert tag_dicts[0]["name"] == "russian" - def test_15a_tag_search_with_many_matches(self): - paramd = {'q': 'tol' } + def test_15a_tag_search_with_many_matches(self, app): + paramd = {"q": "tol"} params = json.dumps(paramd) - res = self.app.post('/api/action/tag_search', params=params) - assert res.json['success'] is True - assert res.json['result']['count'] == 5 - tag_dicts = res.json['result']['results'] - assert ([tag['name'] for tag in tag_dicts] == - sorted(['tolkien', 'toledo', 'tolerance', 'tollbooth', 'tolstoy'])) - - def test_15a_tag_search_with_many_matches_paged(self): - paramd = {'q': 'tol', 'limit': 2, 'offset': 2 } + res = app.post("/api/action/tag_search", params=params) + assert res.json["success"] is True + assert res.json["result"]["count"] == 5 + tag_dicts = res.json["result"]["results"] + assert [tag["name"] for tag in tag_dicts] == sorted( + ["tolkien", "toledo", "tolerance", "tollbooth", "tolstoy"] + ) + + def test_15a_tag_search_with_many_matches_paged(self, app): + paramd = {"q": "tol", "limit": 2, "offset": 2} params = json.dumps(paramd) - res = self.app.post('/api/action/tag_search', params=params) - assert res.json['success'] is True - assert res.json['result']['count'] == 5 - tag_dicts = res.json['result']['results'] - assert_equal ([tag['name'] for tag in tag_dicts], - [u'tolkien', u'tollbooth']) - - def test_15a_tag_search_with_vocab_and_empty_query(self): - for q in ('missing', None, '', ' '): - paramd = {'vocabulary_id': 'genre'} - if q != 'missing': - paramd['q'] = q + res = app.post("/api/action/tag_search", params=params) + assert res.json["success"] is True + assert res.json["result"]["count"] == 5 + tag_dicts = res.json["result"]["results"] + assert [tag["name"] for tag in tag_dicts] == [u"tolkien", u"tollbooth"] + + def test_15a_tag_search_with_vocab_and_empty_query(self, app): + for q in ("missing", None, "", " "): + paramd = {"vocabulary_id": "genre"} + if q != "missing": + paramd["q"] = q params = json.dumps(paramd) - res = self.app.post('/api/action/tag_search', params=params) - assert res.json['success'] is True - assert res.json['result']['count'] == 0 - assert res.json['result']['results'] == [] + res = app.post("/api/action/tag_search", params=params) + assert res.json["success"] is True + assert res.json["result"]["count"] == 0 + assert res.json["result"]["results"] == [] - def test_15a_tag_search_with_vocab_and_one_match(self): - paramd = {'q': 'son', 'vocabulary_id': 'genre' } + def test_15a_tag_search_with_vocab_and_one_match(self, app): + paramd = {"q": "son", "vocabulary_id": "genre"} params = json.dumps(paramd) - res = self.app.post('/api/action/tag_search', params=params) - assert res.json['success'] is True - assert res.json['result']['count'] == 1 - tag_dicts = res.json['result']['results'] + res = app.post("/api/action/tag_search", params=params) + assert res.json["success"] is True + assert res.json["result"]["count"] == 1 + tag_dicts = res.json["result"]["results"] assert len(tag_dicts) == 1 - assert tag_dicts[0]['name'] == 'sonata' + assert tag_dicts[0]["name"] == "sonata" - def test_15a_tag_search_with_vocab_and_multiple_matches(self): - paramd = {'q': 'neo', 'vocabulary_id': 'genre' } + def test_15a_tag_search_with_vocab_and_multiple_matches(self, app): + paramd = {"q": "neo", "vocabulary_id": "genre"} params = json.dumps(paramd) - res = self.app.post('/api/action/tag_search', params=params) - assert res.json['success'] is True - assert res.json['result']['count'] == 6 - tag_dicts = res.json['result']['results'] - assert [tag['name'] for tag in tag_dicts] == sorted(('neoclassical', - 'neofolk', 'neomedieval', 'neoprog', 'neopsychedelia', 'neosoul')) - - def test_15a_tag_search_with_vocab_and_no_matches(self): - paramd = {'q': 'xxxxxxx', 'vocabulary_id': 'genre' } + res = app.post("/api/action/tag_search", params=params) + assert res.json["success"] is True + assert res.json["result"]["count"] == 6 + tag_dicts = res.json["result"]["results"] + assert [tag["name"] for tag in tag_dicts] == sorted( + ( + "neoclassical", + "neofolk", + "neomedieval", + "neoprog", + "neopsychedelia", + "neosoul", + ) + ) + + def test_15a_tag_search_with_vocab_and_no_matches(self, app): + paramd = {"q": "xxxxxxx", "vocabulary_id": "genre"} params = json.dumps(paramd) - res = self.app.post('/api/action/tag_search', params=params) - assert res.json['success'] is True - assert res.json['result']['count'] == 0 - tag_dicts = res.json['result']['results'] + res = app.post("/api/action/tag_search", params=params) + assert res.json["success"] is True + assert res.json["result"]["count"] == 0 + tag_dicts = res.json["result"]["results"] assert tag_dicts == [] - def test_15a_tag_search_with_vocab_that_does_not_exist(self): - paramd = {'q': 'neo', 'vocabulary_id': 'xxxxxx' } + def test_15a_tag_search_with_vocab_that_does_not_exist(self, app): + paramd = {"q": "neo", "vocabulary_id": "xxxxxx"} params = json.dumps(paramd) - self.app.post('/api/action/tag_search', params=params, status=404) + app.post("/api/action/tag_search", params=params, status=404) - def test_15a_tag_search_with_invalid_vocab(self): - for vocab_name in (None, '', 'a', 'e'*200): - paramd = {'q': 'neo', 'vocabulary_id': vocab_name } + def test_15a_tag_search_with_invalid_vocab(self, app): + for vocab_name in (None, "", "a", "e" * 200): + paramd = {"q": "neo", "vocabulary_id": vocab_name} params = json.dumps(paramd) - self.app.post('/api/action/tag_search', params=params, status=404) + app.post("/api/action/tag_search", params=params, status=404) - def test_15_tag_autocomplete(self): - #Empty query - postparams = '%s=1' % json.dumps({}) - res = self.app.post('/api/action/tag_autocomplete', params=postparams) + def test_15_tag_autocomplete(self, app): + # Empty query + postparams = "%s=1" % json.dumps({}) + res = app.post("/api/action/tag_autocomplete", params=postparams) res_obj = json.loads(res.body) - assert res_obj['success'] == True - assert res_obj['result'] == [] - assert '/api/3/action/help_show?name=tag_autocomplete' in res_obj['help'] - - #Normal query - postparams = '%s=1' % json.dumps({'q':'r'}) - res = self.app.post('/api/action/tag_autocomplete', params=postparams) + assert res_obj["success"] == True + assert res_obj["result"] == [] + assert ( + "/api/3/action/help_show?name=tag_autocomplete" in res_obj["help"] + ) + + # Normal query + postparams = "%s=1" % json.dumps({"q": "r"}) + res = app.post("/api/action/tag_autocomplete", params=postparams) res_obj = json.loads(res.body) - assert res_obj['success'] == True - assert res_obj['result'] == ['russian', 'tolerance'] - assert '/api/3/action/help_show?name=tag_autocomplete' in res_obj['help'] + assert res_obj["success"] == True + assert res_obj["result"] == ["russian", "tolerance"] + assert ( + "/api/3/action/help_show?name=tag_autocomplete" in res_obj["help"] + ) - def test_15_tag_autocomplete_tag_with_spaces(self): + def test_15_tag_autocomplete_tag_with_spaces(self, app): """Asserts autocomplete finds tags that contain spaces""" - CreateTestData.create_arbitrary([{ - 'name': u'package-with-tag-that-has-a-space-1', - 'tags': [u'with space'], - 'license': 'never_heard_of_it', - }]) - - postparams = '%s=1' % json.dumps({'q':'w'}) - res = self.app.post('/api/action/tag_autocomplete', params=postparams) + CreateTestData.create_arbitrary( + [ + { + "name": u"package-with-tag-that-has-a-space-1", + "tags": [u"with space"], + "license": "never_heard_of_it", + } + ] + ) + + postparams = "%s=1" % json.dumps({"q": "w"}) + res = app.post("/api/action/tag_autocomplete", params=postparams) res_obj = json.loads(res.body) - assert res_obj['success'] - assert 'with space' in res_obj['result'], res_obj['result'] + assert res_obj["success"] + assert "with space" in res_obj["result"], res_obj["result"] - def test_15_tag_autocomplete_tag_with_foreign_characters(self): + def test_15_tag_autocomplete_tag_with_foreign_characters(self, app): """Asserts autocomplete finds tags that contain foreign characters""" - CreateTestData.create_arbitrary([{ - 'name': u'package-with-tag-that-has-a-foreign-character-1', - 'tags': [u'greek beta \u03b2'], - 'license': 'never_heard_of_it', - }]) - - postparams = '%s=1' % json.dumps({'q':'greek'}) - res = self.app.post('/api/action/tag_autocomplete', params=postparams) + CreateTestData.create_arbitrary( + [ + { + "name": u"package-with-tag-that-has-a-foreign-character-1", + "tags": [u"greek beta \u03b2"], + "license": "never_heard_of_it", + } + ] + ) + + postparams = "%s=1" % json.dumps({"q": "greek"}) + res = app.post("/api/action/tag_autocomplete", params=postparams) res_obj = json.loads(res.body) - assert res_obj['success'] - assert u'greek beta \u03b2' in res_obj['result'], res_obj['result'] + assert res_obj["success"] + assert u"greek beta \u03b2" in res_obj["result"], res_obj["result"] - def test_15_tag_autocomplete_tag_with_punctuation(self): + def test_15_tag_autocomplete_tag_with_punctuation(self, app): """Asserts autocomplete finds tags that contain punctuation""" - CreateTestData.create_arbitrary([{ - 'name': u'package-with-tag-that-has-a-fullstop-1', - 'tags': [u'fullstop.'], - 'license': 'never_heard_of_it', - }]) - - postparams = '%s=1' % json.dumps({'q':'fullstop'}) - res = self.app.post('/api/action/tag_autocomplete', params=postparams) + CreateTestData.create_arbitrary( + [ + { + "name": u"package-with-tag-that-has-a-fullstop-1", + "tags": [u"fullstop."], + "license": "never_heard_of_it", + } + ] + ) + + postparams = "%s=1" % json.dumps({"q": "fullstop"}) + res = app.post("/api/action/tag_autocomplete", params=postparams) res_obj = json.loads(res.body) - assert res_obj['success'] - assert u'fullstop.' in res_obj['result'], res_obj['result'] + assert res_obj["success"] + assert u"fullstop." in res_obj["result"], res_obj["result"] - def test_15_tag_autocomplete_tag_with_capital_letters(self): + def test_15_tag_autocomplete_tag_with_capital_letters(self, app): """ Asserts autocomplete finds tags that contain capital letters """ - CreateTestData.create_arbitrary([{ - 'name': u'package-with-tag-that-has-a-capital-letter-1', - 'tags': [u'CAPITAL idea old chap'], - 'license': 'never_heard_of_it', - }]) - - postparams = '%s=1' % json.dumps({'q':'idea'}) - res = self.app.post('/api/action/tag_autocomplete', params=postparams) + CreateTestData.create_arbitrary( + [ + { + "name": u"package-with-tag-that-has-a-capital-letter-1", + "tags": [u"CAPITAL idea old chap"], + "license": "never_heard_of_it", + } + ] + ) + + postparams = "%s=1" % json.dumps({"q": "idea"}) + res = app.post("/api/action/tag_autocomplete", params=postparams) res_obj = json.loads(res.body) - assert res_obj['success'] - assert u'CAPITAL idea old chap' in res_obj['result'], res_obj['result'] + assert res_obj["success"] + assert u"CAPITAL idea old chap" in res_obj["result"], res_obj["result"] - def test_15_tag_autocomplete_search_with_space(self): + def test_15_tag_autocomplete_search_with_space(self, app): """ Asserts that a search term containing a space works correctly """ - CreateTestData.create_arbitrary([{ - 'name': u'package-with-tag-that-has-a-space-2', - 'tags': [u'with space'], - 'license': 'never_heard_of_it', - }]) - - postparams = '%s=1' % json.dumps({'q':'th sp'}) - res = self.app.post('/api/action/tag_autocomplete', params=postparams) + CreateTestData.create_arbitrary( + [ + { + "name": u"package-with-tag-that-has-a-space-2", + "tags": [u"with space"], + "license": "never_heard_of_it", + } + ] + ) + + postparams = "%s=1" % json.dumps({"q": "th sp"}) + res = app.post("/api/action/tag_autocomplete", params=postparams) res_obj = json.loads(res.body) - assert res_obj['success'] - assert 'with space' in res_obj['result'], res_obj['result'] + assert res_obj["success"] + assert "with space" in res_obj["result"], res_obj["result"] - def test_15_tag_autocomplete_search_with_foreign_character(self): + def test_15_tag_autocomplete_search_with_foreign_character(self, app): """ Asserts that a search term containing a foreign character works correctly """ - CreateTestData.create_arbitrary([{ - 'name': u'package-with-tag-that-has-a-foreign-character-2', - 'tags': [u'greek beta \u03b2'], - 'license': 'never_heard_of_it', - }]) - - postparams = '%s=1' % json.dumps({'q':u'\u03b2'}) - res = self.app.post('/api/action/tag_autocomplete', params=postparams) + CreateTestData.create_arbitrary( + [ + { + "name": u"package-with-tag-that-has-a-foreign-character-2", + "tags": [u"greek beta \u03b2"], + "license": "never_heard_of_it", + } + ] + ) + + postparams = "%s=1" % json.dumps({"q": u"\u03b2"}) + res = app.post("/api/action/tag_autocomplete", params=postparams) res_obj = json.loads(res.body) - assert res_obj['success'] - assert u'greek beta \u03b2' in res_obj['result'], res_obj['result'] + assert res_obj["success"] + assert u"greek beta \u03b2" in res_obj["result"], res_obj["result"] - def test_15_tag_autocomplete_search_with_punctuation(self): + def test_15_tag_autocomplete_search_with_punctuation(self, app): """ Asserts that a search term containing punctuation works correctly """ - CreateTestData.create_arbitrary([{ - 'name': u'package-with-tag-that-has-a-fullstop-2', - 'tags': [u'fullstop.'], - 'license': 'never_heard_of_it', - }]) - - postparams = '%s=1' % json.dumps({'q':u'stop.'}) - res = self.app.post('/api/action/tag_autocomplete', params=postparams) + CreateTestData.create_arbitrary( + [ + { + "name": u"package-with-tag-that-has-a-fullstop-2", + "tags": [u"fullstop."], + "license": "never_heard_of_it", + } + ] + ) + + postparams = "%s=1" % json.dumps({"q": u"stop."}) + res = app.post("/api/action/tag_autocomplete", params=postparams) res_obj = json.loads(res.body) - assert res_obj['success'] - assert 'fullstop.' in res_obj['result'], res_obj['result'] + assert res_obj["success"] + assert "fullstop." in res_obj["result"], res_obj["result"] - def test_15_tag_autocomplete_search_with_capital_letters(self): + def test_15_tag_autocomplete_search_with_capital_letters(self, app): """ Asserts that a search term containing capital letters works correctly """ - CreateTestData.create_arbitrary([{ - 'name': u'package-with-tag-that-has-a-capital-letter-2', - 'tags': [u'CAPITAL idea old chap'], - 'license': 'never_heard_of_it', - }]) - - postparams = '%s=1' % json.dumps({'q':u'CAPITAL'}) - res = self.app.post('/api/action/tag_autocomplete', params=postparams) + CreateTestData.create_arbitrary( + [ + { + "name": u"package-with-tag-that-has-a-capital-letter-2", + "tags": [u"CAPITAL idea old chap"], + "license": "never_heard_of_it", + } + ] + ) + + postparams = "%s=1" % json.dumps({"q": u"CAPITAL"}) + res = app.post("/api/action/tag_autocomplete", params=postparams) res_obj = json.loads(res.body) - assert res_obj['success'] - assert 'CAPITAL idea old chap' in res_obj['result'], res_obj['result'] - - def test_15_tag_autocomplete_is_case_insensitive(self): - CreateTestData.create_arbitrary([{ - 'name': u'package-with-tag-that-has-a-capital-letter-3', - 'tags': [u'MIX of CAPITALS and LOWER case'], - 'license': 'never_heard_of_it', - }]) - - postparams = '%s=1' % json.dumps({'q':u'lower case'}) - res = self.app.post('/api/action/tag_autocomplete', params=postparams) + assert res_obj["success"] + assert "CAPITAL idea old chap" in res_obj["result"], res_obj["result"] + + def test_15_tag_autocomplete_is_case_insensitive(self, app): + CreateTestData.create_arbitrary( + [ + { + "name": u"package-with-tag-that-has-a-capital-letter-3", + "tags": [u"MIX of CAPITALS and LOWER case"], + "license": "never_heard_of_it", + } + ] + ) + + postparams = "%s=1" % json.dumps({"q": u"lower case"}) + res = app.post("/api/action/tag_autocomplete", params=postparams) res_obj = json.loads(res.body) - assert res_obj['success'] - assert 'MIX of CAPITALS and LOWER case' in res_obj['result'], res_obj['result'] - - def test_15_tag_autocomplete_with_vocab_and_empty_query(self): - for q in ('missing', None, '', ' '): - paramd = {'vocabulary_id': u'genre'} - if q != 'missing': - paramd['q'] = q + assert res_obj["success"] + assert "MIX of CAPITALS and LOWER case" in res_obj["result"], res_obj[ + "result" + ] + + def test_15_tag_autocomplete_with_vocab_and_empty_query(self, app): + for q in ("missing", None, "", " "): + paramd = {"vocabulary_id": u"genre"} + if q != "missing": + paramd["q"] = q params = json.dumps(paramd) - res = self.app.post('/api/action/tag_autocomplete', params=params) - assert res.json['success'] is True - assert res.json['result'] == [] + res = app.post("/api/action/tag_autocomplete", params=params) + assert res.json["success"] is True + assert res.json["result"] == [] - def test_15_tag_autocomplete_with_vocab_and_single_match(self): - paramd = {'vocabulary_id': u'genre', 'q': 'son'} + def test_15_tag_autocomplete_with_vocab_and_single_match(self, app): + paramd = {"vocabulary_id": u"genre", "q": "son"} params = json.dumps(paramd) - res = self.app.post('/api/action/tag_autocomplete', params=params) - assert res.json['success'] is True - assert res.json['result'] == ['sonata'], res.json['result'] + res = app.post("/api/action/tag_autocomplete", params=params) + assert res.json["success"] is True + assert res.json["result"] == ["sonata"], res.json["result"] - def test_15_tag_autocomplete_with_vocab_and_multiple_matches(self): - paramd = {'vocabulary_id': 'genre', 'q': 'neo'} + def test_15_tag_autocomplete_with_vocab_and_multiple_matches(self, app): + paramd = {"vocabulary_id": "genre", "q": "neo"} params = json.dumps(paramd) - res = self.app.post('/api/action/tag_autocomplete', params=params) - assert res.json['success'] is True - assert res.json['result'] == sorted(('neoclassical', 'neofolk', - 'neomedieval', 'neoprog', 'neopsychedelia', 'neosoul')) - - def test_15_tag_autocomplete_with_vocab_and_no_matches(self): - paramd = {'vocabulary_id': 'composers', 'q': 'Jonny Greenwood'} + res = app.post("/api/action/tag_autocomplete", params=params) + assert res.json["success"] is True + assert res.json["result"] == sorted( + ( + "neoclassical", + "neofolk", + "neomedieval", + "neoprog", + "neopsychedelia", + "neosoul", + ) + ) + + def test_15_tag_autocomplete_with_vocab_and_no_matches(self, app): + paramd = {"vocabulary_id": "composers", "q": "Jonny Greenwood"} params = json.dumps(paramd) - res = self.app.post('/api/action/tag_autocomplete', params=params) - assert res.json['success'] is True - assert res.json['result'] == [] + res = app.post("/api/action/tag_autocomplete", params=params) + assert res.json["success"] is True + assert res.json["result"] == [] - def test_15_tag_autocomplete_with_vocab_that_does_not_exist(self): - for q in ('', 'neo'): - paramd = {'vocabulary_id': 'does_not_exist', 'q': q} + def test_15_tag_autocomplete_with_vocab_that_does_not_exist(self, app): + for q in ("", "neo"): + paramd = {"vocabulary_id": "does_not_exist", "q": q} params = json.dumps(paramd) - res = self.app.post('/api/action/tag_autocomplete', params=params, - status=404) - assert res.json['success'] is False - - def test_15_tag_autocomplete_with_invalid_vocab(self): - for vocab_name in (None, '', 'a', 'e'*200): - for q in (None, '', 'son'): - paramd = {'vocabulary_id': vocab_name, 'q': q} + res = app.post( + "/api/action/tag_autocomplete", params=params, status=404 + ) + assert res.json["success"] is False + + def test_15_tag_autocomplete_with_invalid_vocab(self, app): + for vocab_name in (None, "", "a", "e" * 200): + for q in (None, "", "son"): + paramd = {"vocabulary_id": vocab_name, "q": q} params = json.dumps(paramd) - res = self.app.post('/api/action/tag_autocomplete', params=params, - status=404) - assert res.json['success'] is False + res = app.post( + "/api/action/tag_autocomplete", params=params, status=404 + ) + assert res.json["success"] is False diff --git a/ckan/tests/legacy/logic/test_tag_vocab.py b/ckan/tests/legacy/logic/test_tag_vocab.py index 4c2f8818abc..2449d433340 100644 --- a/ckan/tests/legacy/logic/test_tag_vocab.py +++ b/ckan/tests/legacy/logic/test_tag_vocab.py @@ -1,5 +1,6 @@ # encoding: utf-8 +import pytest import ckan.lib.create_test_data as ctd import ckan.lib.navl.dictization_functions as df import ckan.logic as logic @@ -7,88 +8,94 @@ import ckan.model as model import ckan.tests.legacy as tests -TEST_VOCAB_NAME = 'test-vocab' +TEST_VOCAB_NAME = "test-vocab" class TestConverters(object): - @classmethod - def setup_class(cls): - cls.vocab = model.Vocabulary(TEST_VOCAB_NAME) - model.Session.add(cls.vocab) + @pytest.fixture(autouse=True) + def initial_data(self, clean_db): + self.vocab = model.Vocabulary(TEST_VOCAB_NAME) + model.Session.add(self.vocab) model.Session.commit() - vocab_tag_1 = model.Tag('tag1', cls.vocab.id) - vocab_tag_2 = model.Tag('tag2', cls.vocab.id) + vocab_tag_1 = model.Tag("tag1", self.vocab.id) + vocab_tag_2 = model.Tag("tag2", self.vocab.id) model.Session.add(vocab_tag_1) model.Session.add(vocab_tag_2) model.Session.commit() model.Session.remove() - @classmethod - def teardown_class(cls): - model.repo.rebuild_db() - def test_convert_to_tags(self): def convert(tag_string, vocab): - key = 'vocab_tags' + key = "vocab_tags" data = {key: tag_string} errors = [] - context = {'model': model, 'session': model.Session} + context = {"model": model, "session": model.Session} converters.convert_to_tags(vocab)(key, data, errors, context) del data[key] return data - data = df.unflatten(convert(['tag1', 'tag2'], 'test-vocab')) - for tag in data['tags']: - assert tag['name'] in ['tag1', 'tag2'], tag['name'] - assert tag['vocabulary_id'] == self.vocab.id, tag['vocabulary_id'] + data = df.unflatten(convert(["tag1", "tag2"], "test-vocab")) + for tag in data["tags"]: + assert tag["name"] in ["tag1", "tag2"], tag["name"] + assert tag["vocabulary_id"] == self.vocab.id, tag["vocabulary_id"] def test_convert_from_tags(self): - key = 'tags' + key = "tags" data = { - ('tags', 0, '__extras'): {'name': 'tag1', 'vocabulary_id': self.vocab.id}, - ('tags', 1, '__extras'): {'name': 'tag2', 'vocabulary_id': self.vocab.id} + ("tags", 0, "__extras"): { + "name": "tag1", + "vocabulary_id": self.vocab.id, + }, + ("tags", 1, "__extras"): { + "name": "tag2", + "vocabulary_id": self.vocab.id, + }, } errors = [] - context = {'model': model, 'session': model.Session} - converters.convert_from_tags(self.vocab.name)(key, data, errors, context) - assert 'tag1' in data['tags'] - assert 'tag2' in data['tags'] + context = {"model": model, "session": model.Session} + converters.convert_from_tags(self.vocab.name)( + key, data, errors, context + ) + assert "tag1" in data["tags"] + assert "tag2" in data["tags"] def test_free_tags_only(self): - key = ('tags', 0, '__extras') + key = ("tags", 0, "__extras") data = { - ('tags', 0, '__extras'): {'name': 'tag1', 'vocabulary_id': self.vocab.id}, - ('tags', 0, 'vocabulary_id'): self.vocab.id, - ('tags', 1, '__extras'): {'name': 'tag2', 'vocabulary_id': None}, - ('tags', 1, 'vocabulary_id'): None + ("tags", 0, "__extras"): { + "name": "tag1", + "vocabulary_id": self.vocab.id, + }, + ("tags", 0, "vocabulary_id"): self.vocab.id, + ("tags", 1, "__extras"): {"name": "tag2", "vocabulary_id": None}, + ("tags", 1, "vocabulary_id"): None, } errors = [] - context = {'model': model, 'session': model.Session} + context = {"model": model, "session": model.Session} converters.free_tags_only(key, data, errors, context) assert len(data) == 2 - assert ('tags', 1, 'vocabulary_id') in data.keys() - assert ('tags', 1, '__extras') in data.keys() + assert ("tags", 1, "vocabulary_id") in data.keys() + assert ("tags", 1, "__extras") in data.keys() class TestVocabFacets(object): - @classmethod - def setup_class(cls): + @pytest.fixture(autouse=True) + def initial_data(self, clean_db, clean_index): if not tests.is_search_supported(): raise tests.SkipTest("Search not supported") - tests.setup_test_search_index() ctd.CreateTestData.create() - cls.vocab = model.Vocabulary(TEST_VOCAB_NAME) - model.Session.add(cls.vocab) + self.vocab = model.Vocabulary(TEST_VOCAB_NAME) + model.Session.add(self.vocab) model.Session.commit() - vocab_tag_1 = model.Tag('tag1', cls.vocab.id) - vocab_tag_2 = model.Tag('tag2', cls.vocab.id) + vocab_tag_1 = model.Tag("tag1", self.vocab.id) + vocab_tag_2 = model.Tag("tag2", self.vocab.id) model.Session.add(vocab_tag_1) model.Session.add(vocab_tag_2) - pkg = model.Package.get('warandpeace') + pkg = model.Package.get("warandpeace") pkg_tag1 = model.PackageTag(pkg, vocab_tag_1) pkg_tag2 = model.PackageTag(pkg, vocab_tag_2) model.Session.add(pkg_tag1) @@ -97,39 +104,35 @@ def setup_class(cls): model.Session.commit() model.Session.remove() - @classmethod - def teardown_class(cls): - model.repo.rebuild_db() - def test_vocab_facets(self): - vocab_facet = 'vocab_%s' % TEST_VOCAB_NAME + vocab_facet = "vocab_%s" % TEST_VOCAB_NAME - context = {'model': model, 'session': model.Session} + context = {"model": model, "session": model.Session} data = { - 'q': 'warandpeace', - 'facet': 'true', - 'facet.field': ['groups', 'tags', vocab_facet], - 'facet.limit': '50', - 'facet.mincount': 1, + "q": "warandpeace", + "facet": "true", + "facet.field": ["groups", "tags", vocab_facet], + "facet.limit": "50", + "facet.mincount": 1, } - result = logic.get_action('package_search')(context, data) - facets = result['search_facets'] - facet_tags = [t['name'] for t in facets['tags']['items']] + result = logic.get_action("package_search")(context, data) + facets = result["search_facets"] + facet_tags = [t["name"] for t in facets["tags"]["items"]] assert len(facet_tags) # make sure vocab tags are not in 'tags' facet - assert 'tag1' not in facet_tags - assert 'tag2' not in facet_tags + assert "tag1" not in facet_tags + assert "tag2" not in facet_tags # make sure vocab tags are in vocab_ facet - vocab_facet_tags = [t['name'] for t in facets[vocab_facet]['items']] - assert 'tag1' in vocab_facet_tags - assert 'tag2' in vocab_facet_tags + vocab_facet_tags = [t["name"] for t in facets[vocab_facet]["items"]] + assert "tag1" in vocab_facet_tags + assert "tag2" in vocab_facet_tags def test_vocab_facets_searchable(self): - context = {'model': model, 'session': model.Session} - data = {'q': 'tag1', 'facet': 'false'} - result = logic.get_action('package_search')(context, data) - assert result['count'] == 1 - assert result['results'][0]['name'] == 'warandpeace' + context = {"model": model, "session": model.Session} + data = {"q": "tag1", "facet": "false"} + result = logic.get_action("package_search")(context, data) + assert result["count"] == 1 + assert result["results"][0]["name"] == "warandpeace" diff --git a/ckan/tests/legacy/logic/test_validators.py b/ckan/tests/legacy/logic/test_validators.py index 3895d0aca02..697a451c00c 100644 --- a/ckan/tests/legacy/logic/test_validators.py +++ b/ckan/tests/legacy/logic/test_validators.py @@ -1,34 +1,34 @@ # encoding: utf-8 -from nose.tools import assert_equal from ckan import model from ckan.logic.validators import tag_string_convert + class TestValidators: def test_01_tag_string_convert(self): def convert(tag_string): - key = 'tag_string' + key = "tag_string" data = {key: tag_string} errors = [] - context = {'model': model, 'session': model.Session} + context = {"model": model, "session": model.Session} tag_string_convert(key, data, errors, context) tags = [] i = 0 while True: - tag = data.get(('tags', i, 'name')) + tag = data.get(("tags", i, "name")) if not tag: break tags.append(tag) i += 1 return tags - assert_equal(convert('big, good'), ['big', 'good']) - assert_equal(convert('one, several word tag, with-hyphen'), - ['one', 'several word tag', 'with-hyphen']) - assert_equal(convert(''), - []) - assert_equal(convert('trailing comma,'), - ['trailing comma']) - assert_equal(convert('trailing comma space, '), - ['trailing comma space']) + assert convert("big, good") == ["big", "good"] + assert convert("one, several word tag, with-hyphen") == [ + "one", + "several word tag", + "with-hyphen", + ] + assert convert("") == [] + assert convert("trailing comma,") == ["trailing comma"] + assert convert("trailing comma space, ") == ["trailing comma space"] diff --git a/ckan/tests/legacy/misc/test_mock_mail_server.py b/ckan/tests/legacy/misc/test_mock_mail_server.py index 56d0e5d902b..ef03d0d2c28 100644 --- a/ckan/tests/legacy/misc/test_mock_mail_server.py +++ b/ckan/tests/legacy/misc/test_mock_mail_server.py @@ -1,38 +1,39 @@ # encoding: utf-8 import time -from nose.tools import assert_equal + from ckan.common import config import hashlib from ckan.tests.legacy.mock_mail_server import SmtpServerHarness from ckan.lib.mailer import mail_recipient + class TestMockMailServer(SmtpServerHarness): @classmethod def setup_class(cls): - smtp_server = config.get('smtp.test_server') + smtp_server = config.get("smtp.test_server") if smtp_server: - host, port = smtp_server.split(':') - port = int(port) + int(str(hashlib.md5(cls.__name__).hexdigest())[0], 16) - config['smtp.test_server'] = '%s:%s' % (host, port) + host, port = smtp_server.split(":") + port = int(port) + int( + str(hashlib.md5(cls.__name__).hexdigest())[0], 16 + ) + config["smtp.test_server"] = "%s:%s" % (host, port) SmtpServerHarness.setup_class() - @classmethod - def teardown_class(cls): - SmtpServerHarness.teardown_class() - def test_basic(self): msgs = self.get_smtp_messages() - assert_equal(msgs, []) + assert msgs == [] - test_email = {'recipient_name': 'Bob', - 'recipient_email':'bob@bob.net', - 'subject': 'Meeting', - 'body': 'The meeting is cancelled.', - 'headers': {'header1': 'value1'}} + test_email = { + "recipient_name": "Bob", + "recipient_email": "bob@bob.net", + "subject": "Meeting", + "body": "The meeting is cancelled.", + "headers": {"header1": "value1"}, + } mail_recipient(**test_email) time.sleep(0.1) msgs = self.get_smtp_messages() - assert_equal(len(msgs), 1) + assert len(msgs) == 1 diff --git a/ckan/tests/legacy/mock_mail_server.py b/ckan/tests/legacy/mock_mail_server.py index 501cdd1055c..7402b21a4fc 100644 --- a/ckan/tests/legacy/mock_mail_server.py +++ b/ckan/tests/legacy/mock_mail_server.py @@ -9,7 +9,8 @@ class MockSmtpServer(SMTPServer): - '''A mock SMTP server that operates in an asyncore loop''' + """A mock SMTP server that operates in an asyncore loop""" + def __init__(self, host, port): self.msgs = [] SMTPServer.__init__(self, (host, port), None) @@ -25,7 +26,8 @@ def clear_smtp_messages(self): class MockSmtpServerThread(threading.Thread): - '''Runs the mock SMTP server in a thread''' + """Runs the mock SMTP server in a thread""" + def __init__(self, host, port): self.assert_port_free(host, port) # init thread @@ -37,9 +39,11 @@ def __init__(self, host, port): def assert_port_free(self, host, port): test_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - test_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, - test_socket.getsockopt(socket.SOL_SOCKET, - socket.SO_REUSEADDR) | 1) + test_socket.setsockopt( + socket.SOL_SOCKET, + socket.SO_REUSEADDR, + test_socket.getsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR) | 1, + ) test_socket.bind((host, port)) test_socket.close() @@ -60,14 +64,14 @@ def clear_smtp_messages(self): class SmtpServerHarness(object): - '''Derive from this class to run MockSMTP - a test harness that - records what email messages are requested to be sent by it.''' + """Derive from this class to run MockSMTP - a test harness that + records what email messages are requested to be sent by it.""" @classmethod def setup_class(cls): - smtp_server = config.get('smtp.test_server') or config['smtp_server'] - if ':' in smtp_server: - host, port = smtp_server.split(':') + smtp_server = config.get("smtp.test_server") or config["smtp_server"] + if ":" in smtp_server: + host, port = smtp_server.split(":") else: host, port = smtp_server, 25 cls.port = port diff --git a/ckan/tests/legacy/mock_plugin.py b/ckan/tests/legacy/mock_plugin.py index d940591283f..16c7b25a986 100644 --- a/ckan/tests/legacy/mock_plugin.py +++ b/ckan/tests/legacy/mock_plugin.py @@ -2,6 +2,7 @@ from ckan.plugins import Plugin, SingletonPlugin + class _MockPlugin(object): """ MockPlugin tracks method calls via __getattr__ for rapid mocking of @@ -13,6 +14,7 @@ class _MockPlugin(object): class MockMethod(object): registry = {} + def __init__(self, boundto, name): self.name = name self.calls = [] @@ -40,11 +42,13 @@ def reset_calls(self): self.__mockmethods__ = {} self.calls = [] + class MockPlugin(_MockPlugin, Plugin): """ Mock a plugin """ + class MockSingletonPlugin(_MockPlugin, SingletonPlugin): """ Mock a singleton plugin diff --git a/ckan/tests/legacy/models/test_follower.py b/ckan/tests/legacy/models/test_follower.py index bf0bd9bb2c3..deb61679e7d 100644 --- a/ckan/tests/legacy/models/test_follower.py +++ b/ckan/tests/legacy/models/test_follower.py @@ -1,5 +1,6 @@ # encoding: utf-8 +import pytest import ckan.model as model import ckan.lib.create_test_data as ctd @@ -17,16 +18,18 @@ def test_get(self): assert following.object_id == self.followee.id, following def test_get_returns_none_if_couldnt_find_users(self): - following = self.FOLLOWER_CLASS.get('some-id', 'other-id') + following = self.FOLLOWER_CLASS.get("some-id", "other-id") assert following is None, following def test_is_following(self): - assert self.FOLLOWER_CLASS.is_following(self.follower.id, - self.followee.id) + assert self.FOLLOWER_CLASS.is_following( + self.follower.id, self.followee.id + ) def test_is_following_returns_false_if_user_isnt_following(self): - assert not self.FOLLOWER_CLASS.is_following(self.followee.id, - self.follower.id) + assert not self.FOLLOWER_CLASS.is_following( + self.followee.id, self.follower.id + ) def test_followee_count(self): count = self.FOLLOWER_CLASS.followee_count(self.follower.id) @@ -53,14 +56,14 @@ class TestUserFollowingUser(FollowerClassesTests): @classmethod def setup_class(cls): model.repo.rebuild_db() - cls.follower = CreateTestData.create_user('follower') - cls.followee = CreateTestData.create_user('followee') + cls.follower = CreateTestData.create_user("follower") + cls.followee = CreateTestData.create_user("followee") cls.FOLLOWER_CLASS(cls.follower.id, cls.followee.id).save() cls._create_deleted_models() @classmethod def _create_deleted_models(cls): - deleted_user = CreateTestData.create_user('deleted_user') + deleted_user = CreateTestData.create_user("deleted_user") cls.FOLLOWER_CLASS(deleted_user.id, cls.followee.id).save() cls.FOLLOWER_CLASS(cls.follower.id, deleted_user.id).save() deleted_user.delete() @@ -73,25 +76,25 @@ class TestUserFollowingDataset(FollowerClassesTests): @classmethod def setup_class(cls): model.repo.rebuild_db() - cls.follower = CreateTestData.create_user('follower') - cls.followee = cls._create_dataset('followee') + cls.follower = CreateTestData.create_user("follower") + cls.followee = cls._create_dataset("followee") cls.FOLLOWER_CLASS(cls.follower.id, cls.followee.id).save() cls._create_deleted_models() @classmethod def _create_deleted_models(cls): - deleted_user = CreateTestData.create_user('deleted_user') + deleted_user = CreateTestData.create_user("deleted_user") cls.FOLLOWER_CLASS(deleted_user.id, cls.followee.id).save() deleted_user.delete() deleted_user.save() - deleted_dataset = cls._create_dataset('deleted_dataset') + deleted_dataset = cls._create_dataset("deleted_dataset") cls.FOLLOWER_CLASS(cls.follower.id, deleted_dataset.id).save() deleted_dataset.delete() deleted_dataset.save() @classmethod def _create_dataset(self, name): - CreateTestData.create_arbitrary({'name': name}) + CreateTestData.create_arbitrary({"name": name}) return model.Package.get(name) @@ -101,19 +104,19 @@ class TestUserFollowingGroup(FollowerClassesTests): @classmethod def setup_class(cls): model.repo.rebuild_db() - cls.follower = CreateTestData.create_user('follower') - cls.followee = cls._create_group('followee') + cls.follower = CreateTestData.create_user("follower") + cls.followee = cls._create_group("followee") cls.FOLLOWER_CLASS(cls.follower.id, cls.followee.id).save() cls._create_deleted_models() model.repo.commit_and_remove() @classmethod def _create_deleted_models(cls): - deleted_user = CreateTestData.create_user('deleted_user') + deleted_user = CreateTestData.create_user("deleted_user") cls.FOLLOWER_CLASS(deleted_user.id, cls.followee.id).save() deleted_user.delete() deleted_user.save() - deleted_group = cls._create_group('deleted_group') + deleted_group = cls._create_group("deleted_group") cls.FOLLOWER_CLASS(cls.follower.id, deleted_group.id).save() deleted_group.delete() deleted_group.save() diff --git a/ckan/tests/legacy/models/test_group.py b/ckan/tests/legacy/models/test_group.py index 4016168e863..f41fc2425f3 100644 --- a/ckan/tests/legacy/models/test_group.py +++ b/ckan/tests/legacy/models/test_group.py @@ -1,188 +1,235 @@ # encoding: utf-8 -from nose.tools import assert_in, assert_not_in, assert_equal - from ckan.tests.legacy import CreateTestData import ckan.model as model +import pytest class TestGroup(object): - - @classmethod - def setup_class(self): + @pytest.fixture(autouse=True) + def initial_data(self, clean_db): CreateTestData.create() - model.Session.remove() - - @classmethod - def teardown_class(self): - model.Session.remove() - model.repo.rebuild_db() - model.Session.remove() def test_1_basic(self): - group1 = model.Group(name=u'group1', title=u'Test Group', - description=u'This is a test group') + group1 = model.Group( + name=u"group1", + title=u"Test Group", + description=u"This is a test group", + ) model.Session.add(group1) model.repo.commit_and_remove() - grp = model.Group.by_name(u'group1') - assert grp.title == u'Test Group' - assert grp.description == u'This is a test group' + grp = model.Group.by_name(u"group1") + assert grp.title == u"Test Group" + assert grp.description == u"This is a test group" assert grp.packages() == [] def test_2_add_packages(self): - self.russian_group = model.Group(name=u'russian', - title=u'Russian Group', - description=u'This is the russian group') + self.russian_group = model.Group( + name=u"russian", + title=u"Russian Group", + description=u"This is the russian group", + ) + model.Session.add(self.russian_group) - anna = model.Package.by_name(u'annakarenina') - war = model.Package.by_name(u'warandpeace') - model.Session.add(model.Member(group=self.russian_group, - table_id=anna.id, - table_name='package') - ) - model.Session.add(model.Member(group=self.russian_group, - table_id=war.id, - table_name='package') - ) + anna = model.Package.by_name(u"annakarenina") + war = model.Package.by_name(u"warandpeace") + model.Session.add( + model.Member( + group=self.russian_group, + table_id=anna.id, + table_name="package", + ) + ) + model.Session.add( + model.Member( + group=self.russian_group, table_id=war.id, table_name="package" + ) + ) model.repo.commit_and_remove() - grp = model.Group.by_name(u'russian') - assert grp.title == u'Russian Group' - anna = model.Package.by_name(u'annakarenina') - war = model.Package.by_name(u'warandpeace') + grp = model.Group.by_name(u"russian") + assert grp.title == u"Russian Group" + anna = model.Package.by_name(u"annakarenina") + war = model.Package.by_name(u"warandpeace") assert set(grp.packages()) == set((anna, war)), grp.packages() assert grp in anna.get_groups() def test_3_search(self): - model.Session.add(model.Group(name=u'test_org', - title=u'Test org', - type=u'organization' - )) - model.repo.commit_and_remove() + model.Session.add( + model.Group( + name=u"test_org", title=u"Test org", type=u"organization" + ) + ) + model.repo.commit_and_remove() - assert_equal(self._search_results('random'), set([])) - assert_equal(self._search_results('david'), set(['david'])) - assert_equal(self._search_results('roger'), set(['roger'])) - assert_equal(self._search_results('roger '), set(['roger'])) - assert_equal(self._search_results('David'), set(['david'])) - assert_equal(self._search_results('Dave'), set(['david'])) - assert_equal(self._search_results('Dave\'s'), set(['david'])) - assert_equal(self._search_results('Dave\'s books'), set(['david'])) - assert_equal(self._search_results('Books'), set(['david', 'roger'])) - assert_equal(self._search_results('Books', is_org=True), set([])) - assert_equal(self._search_results('Test', is_org=True), set(['test_org'])) + assert self._search_results("random") == set([]) + assert self._search_results("david") == set(["david"]) + assert self._search_results("roger") == set(["roger"]) + assert self._search_results("roger ") == set(["roger"]) + assert self._search_results("David") == set(["david"]) + assert self._search_results("Dave") == set(["david"]) + assert self._search_results("Dave's") == set(["david"]) + assert self._search_results("Dave's books") == set(["david"]) + assert self._search_results("Books") == set(["david", "roger"]) + assert self._search_results("Books", is_org=True) == set([]) + assert self._search_results("Test", is_org=True) == set(["test_org"]) def test_search_by_name_or_title_only_returns_active_groups(self): - active_group = model.Group(name=u'active_group') - active_group.state = u'active' - inactive_group = model.Group(name=u'inactive_group') - inactive_group.state = u'inactive' + active_group = model.Group(name=u"active_group") + active_group.state = u"active" + inactive_group = model.Group(name=u"inactive_group") + inactive_group.state = u"inactive" + model.Session.add(active_group) model.Session.add(inactive_group) model.repo.commit_and_remove() - assert_equal(self._search_results('active_group'), set(['active_group'])) - assert_equal(self._search_results('inactive_group'), set([])) + assert self._search_results("active_group") == set(["active_group"]) + assert self._search_results("inactive_group") == set([]) def _search_results(self, query, is_org=False): - results = model.Group.search_by_name_or_title(query,is_org=is_org) - return {group.name for group in results} + results = model.Group.search_by_name_or_title(query, is_org=is_org) + return set([group.name for group in results]) -name_set_from_dicts = lambda groups: {group['name'] for group in groups} -name_set_from_group_tuple = lambda tuples: {t[1] for t in tuples} -name_set_from_groups = lambda groups: {group.name for group in groups} + +name_set_from_dicts = lambda groups: set([group["name"] for group in groups]) +name_set_from_group_tuple = lambda tuples: set([t[1] for t in tuples]) +name_set_from_groups = lambda groups: set([group.name for group in groups]) names_from_groups = lambda groups: [group.name for group in groups] -group_type = 'organization' +group_type = "organization" + class TestHierarchy: - @classmethod - def setup_class(self): + @pytest.fixture(autouse=True) + def initial_data(self, clean_db): CreateTestData.create_group_hierarchy_test_data() def test_get_children_groups(self): - res = model.Group.by_name(u'department-of-health').\ - get_children_groups(type=group_type) + res = model.Group.by_name(u"department-of-health").get_children_groups( + type=group_type + ) # check groups - assert_equal(name_set_from_groups(res), - set(('national-health-service', - 'food-standards-agency'))) + assert name_set_from_groups(res) == set( + ("national-health-service", "food-standards-agency") + ) # check each group is a Group assert isinstance(res[0], model.Group) - assert_in(res[0].name, ('national-health-service', 'food-standards-agency')) - assert_in(res[0].title, ('National Health Service', 'Food Standards Agency')) + assert res[0].name in ( + "national-health-service", + "food-standards-agency", + ) + assert res[0].title in ( + "National Health Service", + "Food Standards Agency", + ) def test_get_children_group_hierarchy__from_top_2(self): - groups = model.Group.by_name(u'department-of-health').\ - get_children_group_hierarchy(type=group_type) + groups = model.Group.by_name( + u"department-of-health" + ).get_children_group_hierarchy(type=group_type) # the first group must be NHS or Food Standards Agency - i.e. on the # first level down nhs = groups[0] - assert_in(nhs[1], ('national-health-service', 'food-standards-agency')) - assert_equal(model.Group.get(nhs[3]).name, 'department-of-health') + assert nhs[1] in ("national-health-service", "food-standards-agency") + assert model.Group.get(nhs[3]).name == "department-of-health" def test_get_children_group_hierarchy__from_top(self): - assert_equal(name_set_from_group_tuple(model.Group.by_name(u'department-of-health').\ - get_children_group_hierarchy(type=group_type)), - set(('national-health-service', 'food-standards-agency', - 'nhs-wirral-ccg', 'nhs-southwark-ccg'))) + assert name_set_from_group_tuple( + model.Group.by_name( + u"department-of-health" + ).get_children_group_hierarchy(type=group_type) + ) == set( + ( + "national-health-service", + "food-standards-agency", + "nhs-wirral-ccg", + "nhs-southwark-ccg", + ) + ) # i.e. not cabinet-office def test_get_children_group_hierarchy__from_tier_two(self): - assert_equal(name_set_from_group_tuple(model.Group.by_name(u'national-health-service').\ - get_children_group_hierarchy(type=group_type)), - set(('nhs-wirral-ccg', - 'nhs-southwark-ccg'))) + assert name_set_from_group_tuple( + model.Group.by_name( + u"national-health-service" + ).get_children_group_hierarchy(type=group_type) + ) == set(("nhs-wirral-ccg", "nhs-southwark-ccg")) # i.e. not department-of-health or food-standards-agency def test_get_children_group_hierarchy__from_bottom_tier(self): - assert_equal(name_set_from_group_tuple(model.Group.by_name(u'nhs-wirral-ccg').\ - get_children_group_hierarchy(type=group_type)), - set()) + assert ( + name_set_from_group_tuple( + model.Group.by_name( + u"nhs-wirral-ccg" + ).get_children_group_hierarchy(type=group_type) + ) + == set() + ) def test_get_parents__top(self): - assert_equal(names_from_groups(model.Group.by_name(u'department-of-health').\ - get_parent_groups(type=group_type)), - []) + assert ( + names_from_groups( + model.Group.by_name(u"department-of-health").get_parent_groups( + type=group_type + ) + ) + == [] + ) def test_get_parents__tier_two(self): - assert_equal(names_from_groups(model.Group.by_name(u'national-health-service').\ - get_parent_groups(type=group_type)), - ['department-of-health']) + assert names_from_groups( + model.Group.by_name(u"national-health-service").get_parent_groups( + type=group_type + ) + ) == ["department-of-health"] def test_get_parents__tier_three(self): - assert_equal(names_from_groups(model.Group.by_name(u'nhs-wirral-ccg').\ - get_parent_groups(type=group_type)), - ['national-health-service']) + assert names_from_groups( + model.Group.by_name(u"nhs-wirral-ccg").get_parent_groups( + type=group_type + ) + ) == ["national-health-service"] def test_get_parent_groups_up_hierarchy__from_top(self): - assert_equal(names_from_groups(model.Group.by_name(u'department-of-health').\ - get_parent_group_hierarchy(type=group_type)), - []) + assert ( + names_from_groups( + model.Group.by_name( + u"department-of-health" + ).get_parent_group_hierarchy(type=group_type) + ) + == [] + ) def test_get_parent_groups_up_hierarchy__from_tier_two(self): - assert_equal(names_from_groups(model.Group.by_name(u'national-health-service').\ - get_parent_group_hierarchy(type=group_type)), - ['department-of-health']) + assert names_from_groups( + model.Group.by_name( + u"national-health-service" + ).get_parent_group_hierarchy(type=group_type) + ) == ["department-of-health"] def test_get_parent_groups_up_hierarchy__from_tier_three(self): - assert_equal(names_from_groups(model.Group.by_name(u'nhs-wirral-ccg').\ - get_parent_group_hierarchy(type=group_type)), - ['department-of-health', - 'national-health-service']) + assert names_from_groups( + model.Group.by_name(u"nhs-wirral-ccg").get_parent_group_hierarchy( + type=group_type + ) + ) == ["department-of-health", "national-health-service"] def test_get_top_level_groups(self): - assert_equal(names_from_groups(model.Group.by_name(u'nhs-wirral-ccg').\ - get_top_level_groups(type=group_type)), - ['cabinet-office', 'department-of-health']) + assert names_from_groups( + model.Group.by_name(u"nhs-wirral-ccg").get_top_level_groups( + type=group_type + ) + ) == ["cabinet-office", "department-of-health"] def test_groups_allowed_to_be_its_parent(self): - groups = model.Group.by_name(u'national-health-service').\ - groups_allowed_to_be_its_parent(type=group_type) + groups = model.Group.by_name( + u"national-health-service" + ).groups_allowed_to_be_its_parent(type=group_type) names = names_from_groups(groups) - assert_in('department-of-health', names) - assert_in('cabinet-office', names) - assert_not_in('natonal-health-service', names) - assert_not_in('nhs-wirral-ccg', names) + assert "department-of-health" in names + assert "cabinet-office" in names + assert "natonal-health-service" not in names + assert "nhs-wirral-ccg" not in names diff --git a/ckan/tests/legacy/models/test_misc.py b/ckan/tests/legacy/models/test_misc.py index 92a5ba49623..aeb5c72c398 100644 --- a/ckan/tests/legacy/models/test_misc.py +++ b/ckan/tests/legacy/models/test_misc.py @@ -1,13 +1,13 @@ # encoding: utf-8 -from nose.tools import assert_equal - -from ckan.tests.legacy import * +import pytest +from ckan.tests.legacy import CreateTestData import ckan.model as model from ckan.model.misc import escape_sql_like_special_characters - _sql_escape = escape_sql_like_special_characters + + class TestEscapeSqlLikeCharacters(object): """ Tests for model.misc.escape_sql_like_special_characters @@ -15,37 +15,31 @@ class TestEscapeSqlLikeCharacters(object): def test_identity(self): """Asserts that it escapes nothing if nothing needs escaping""" - terms = ['', - 'word', - 'two words'] + terms = ["", "word", "two words"] for term, expected_term in zip(terms, terms): - assert_equal(_sql_escape(term), expected_term) + assert _sql_escape(term) == expected_term def test_escape_chararacter_is_escaped(self): """Asserts that the escape character is escaped""" - term = r'backslash \ character' - assert_equal (_sql_escape(term, escape='\\'), - r'backslash \\ character') + term = r"backslash \ character" + assert _sql_escape(term, escape="\\") == r"backslash \\ character" - term = 'surprise!' - assert_equal (_sql_escape(term, escape='!'), - r'surprise!!') + term = "surprise!" + assert _sql_escape(term, escape="!") == r"surprise!!" def test_default_escape_character_is_a_backslash(self): """Asserts that the default escape character is the backslash""" - term = r'backslash \ character' - assert_equal (_sql_escape(term), - r'backslash \\ character') + term = r"backslash \ character" + assert _sql_escape(term) == r"backslash \\ character" def test_sql_like_special_characters_are_escaped(self): """Asserts that '%' and '_' are escaped correctly""" terms = [ - (r'percents %', r'percents \%'), - (r'underscores _', r'underscores \_'), - (r'backslash \ ', r'backslash \\ '), - (r'all three \ _%', r'all three \\ \_\%'), - ] + (r"percents %", r"percents \%"), + (r"underscores _", r"underscores \_"), + (r"backslash \ ", r"backslash \\ "), + (r"all three \ _%", r"all three \\ \_\%"), + ] for term, expected_result in terms: - assert_equal(_sql_escape(term), expected_result) - + assert _sql_escape(term) == expected_result diff --git a/ckan/tests/legacy/models/test_package.py b/ckan/tests/legacy/models/test_package.py index b17dbdf5ad6..9ae9c499517 100644 --- a/ckan/tests/legacy/models/test_package.py +++ b/ckan/tests/legacy/models/test_package.py @@ -1,100 +1,92 @@ # encoding: utf-8 -from nose.tools import assert_equal - -from ckan.tests.legacy import * +import pytest +from ckan.tests.legacy import CreateTestData import ckan.model as model # Todo: More domain logic tests e.g. for isopen() and other domain logic. + class TestPackage: - @classmethod - def setup_class(self): + name = u"geodata" + + @pytest.fixture(autouse=True) + def initial_data(self, clean_db): CreateTestData.create() - self.name = u'geodata' - self.notes = 'A great package