From 6ab23581d8f8b1073aca968deb00a9552e2aebed Mon Sep 17 00:00:00 2001 From: Brook Elgie Date: Wed, 15 Jul 2015 13:31:19 +0100 Subject: [PATCH 1/6] [#2534] Tests for list, add and removal of members --- ckan/templates/group/member_new.html | 2 +- ckan/templates/group/members.html | 2 +- ckan/tests/controllers/test_group.py | 142 +++++++++++++++++++++++++++ 3 files changed, 144 insertions(+), 2 deletions(-) diff --git a/ckan/templates/group/member_new.html b/ckan/templates/group/member_new.html index ce03a7f69a2..3ec0fc66912 100644 --- a/ckan/templates/group/member_new.html +++ b/ckan/templates/group/member_new.html @@ -10,7 +10,7 @@

{% block page_heading %}{{ _('Edit Member') if user else _('Add Member') }}{% endblock %}

{% block form %} -
+
{% if not user %} diff --git a/ckan/templates/group/members.html b/ckan/templates/group/members.html index 210562903b7..d54d89fd4f4 100644 --- a/ckan/templates/group/members.html +++ b/ckan/templates/group/members.html @@ -8,7 +8,7 @@ {% block primary_content_inner %}

{{ _('{0} members'.format(c.members|length)) }}

- +
diff --git a/ckan/tests/controllers/test_group.py b/ckan/tests/controllers/test_group.py index 1e85ef74a1e..544e9a1dd8a 100644 --- a/ckan/tests/controllers/test_group.py +++ b/ckan/tests/controllers/test_group.py @@ -1,3 +1,4 @@ +from bs4 import BeautifulSoup from nose.tools import assert_equal, assert_true from routes import url_for @@ -149,3 +150,144 @@ def test_all_fields_saved(self): assert_equal(group.title, u'Science') assert_equal(group.description, 'Sciencey datasets') assert_equal(group.image_url, 'http://example.com/image.png') + + +class TestGroupMembership(helpers.FunctionalTestBase): + + 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.''' + 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) + return group + + def _get_group_add_member_page(self, app, user, group_name): + env = {'REMOTE_USER': user['name'].encode('ascii')} + url = url_for(controller='group', + action='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') + + other_users = [ + {'name': user_two['id'], 'capacity': 'member'} + ] + + group = self._create_group(user_one['name'], other_users) + + member_list_url = url_for(controller='group', action='members', + id=group['id']) + member_list_response = app.get(member_list_url) + + assert_true('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_roles = dict(zip(user_names, roles)) + + assert_equal(user_roles['User One'], 'Admin') + assert_equal(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']) + + 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') + + assert_true('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_roles = dict(zip(user_names, roles)) + + assert_equal(user_roles['My Owner'], 'Admin') + assert_equal(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']) + + 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') + + assert_true('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_roles = dict(zip(user_names, roles)) + + assert_equal(user_roles['My Owner'], 'Admin') + assert_equal(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') + + other_users = [ + {'name': user_two['id'], 'capacity': 'member'} + ] + + group = self._create_group(user_one['name'], other_users) + + remove_url = url_for(controller='group', action='member_delete', + user=user_two['id'], id=group['id']) + + 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() + + assert_true('Group member has been deleted.' in remove_response) + assert_true('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_roles = dict(zip(user_names, roles)) + + assert_equal(len(user_roles.keys()), 1) + assert_equal(user_roles['User One'], 'Admin') From 93ba10bf369a18b72c620f86689884e87e10dd2d Mon Sep 17 00:00:00 2001 From: Brook Elgie Date: Wed, 15 Jul 2015 14:30:48 +0100 Subject: [PATCH 2/6] [#2534] Fix pep8 issues --- ckan/tests/controllers/test_group.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/ckan/tests/controllers/test_group.py b/ckan/tests/controllers/test_group.py index 544e9a1dd8a..91af231ee01 100644 --- a/ckan/tests/controllers/test_group.py +++ b/ckan/tests/controllers/test_group.py @@ -194,7 +194,8 @@ def test_membership_list(self): 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')] + for r + in member_response_html.select('#member-table td.media')] user_roles = dict(zip(user_names, roles)) @@ -222,7 +223,7 @@ def test_membership_add(self): 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')] + for r in add_response_html.select('#member-table td.media')] user_roles = dict(zip(user_names, roles)) @@ -251,7 +252,7 @@ def test_admin_add(self): 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')] + for r in add_response_html.select('#member-table td.media')] user_roles = dict(zip(user_names, roles)) @@ -285,7 +286,8 @@ def test_remove_member(self): 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')] + for r in + remove_response_html.select('#member-table td.media')] user_roles = dict(zip(user_names, roles)) From 9aadab1e7576ba11e2c9681580d40880f50d5448 Mon Sep 17 00:00:00 2001 From: Brook Elgie Date: Thu, 16 Jul 2015 16:54:07 +0100 Subject: [PATCH 3/6] [#2538] Test searching for an organization --- ckan/tests/controllers/test_organization.py | 73 +++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/ckan/tests/controllers/test_organization.py b/ckan/tests/controllers/test_organization.py index 2ef40edba1d..b155141c588 100644 --- a/ckan/tests/controllers/test_organization.py +++ b/ckan/tests/controllers/test_organization.py @@ -1,3 +1,4 @@ +from bs4 import BeautifulSoup from nose.tools import assert_equal, assert_true from routes import url_for @@ -259,3 +260,75 @@ def test_delete(self): for dataset in datasets: d = helpers.call_action('package_show', id=dataset['id']) assert_equal(d['state'], 'deleted') + + +class TestOrganizationSearch(helpers.FunctionalTestBase): + + def test_organization_search(self): + '''Requesting organization search (index) returns list of + organizations and search form.''' + app = self._get_test_app() + factories.Organization(name='org-one', title='Org One') + factories.Organization(name='org-two', title='Org Two') + factories.Organization(name='org-three', title='Org Three') + + search_url = url_for(controller='organization', action='index') + index_response = app.get(search_url) + index_response_html = BeautifulSoup(index_response.body) + 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('Org One' in org_names) + assert_true('Org 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.''' + 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') + + search_url = url_for(controller='organization', action='index') + index_response = app.get(search_url) + 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 = [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.''' + 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') + + search_url = url_for(controller='organization', action='index') + index_response = app.get(search_url) + 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 = [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) From b5f42d58e418370b2d6d0f0cb0b4fb2baafab6fc Mon Sep 17 00:00:00 2001 From: Brook Elgie Date: Fri, 17 Jul 2015 10:04:18 +0100 Subject: [PATCH 4/6] [#2538] Refactor Org search tests --- ckan/tests/controllers/test_organization.py | 35 +++++++++------------ 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/ckan/tests/controllers/test_organization.py b/ckan/tests/controllers/test_organization.py index b155141c588..a09be21008b 100644 --- a/ckan/tests/controllers/test_organization.py +++ b/ckan/tests/controllers/test_organization.py @@ -264,16 +264,21 @@ def test_delete(self): class TestOrganizationSearch(helpers.FunctionalTestBase): + '''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(controller='organization', action='index') + def test_organization_search(self): '''Requesting organization search (index) returns list of organizations and search form.''' - app = self._get_test_app() - factories.Organization(name='org-one', title='Org One') - factories.Organization(name='org-two', title='Org Two') - factories.Organization(name='org-three', title='Org Three') - search_url = url_for(controller='organization', action='index') - index_response = app.get(search_url) + index_response = self.app.get(self.search_url) index_response_html = BeautifulSoup(index_response.body) org_names = index_response_html.select('ul.media-grid ' 'li.media-item ' @@ -281,20 +286,15 @@ def test_organization_search(self): org_names = [n.string for n in org_names] assert_equal(len(org_names), 3) - assert_true('Org One' in org_names) - assert_true('Org Two' in org_names) + 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.''' - 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') - search_url = url_for(controller='organization', action='index') - index_response = app.get(search_url) + index_response = self.app.get(self.search_url) search_form = index_response.forms['organization-search-form'] search_form['q'] = 'AOrg' search_response = webtest_submit(search_form) @@ -312,13 +312,8 @@ def test_organization_search_results(self): def test_organization_search_no_results(self): '''Searching with a term that doesn't apply returns no results.''' - 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') - search_url = url_for(controller='organization', action='index') - index_response = app.get(search_url) + index_response = self.app.get(self.search_url) search_form = index_response.forms['organization-search-form'] search_form['q'] = 'No Results Here' search_response = webtest_submit(search_form) From 226c641308a3bdfca21db87f466480d6ed932d7a Mon Sep 17 00:00:00 2001 From: Brook Elgie Date: Fri, 17 Jul 2015 10:52:39 +0100 Subject: [PATCH 5/6] [#2538] Test search within an organization --- ckan/tests/controllers/test_organization.py | 98 +++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/ckan/tests/controllers/test_organization.py b/ckan/tests/controllers/test_organization.py index a09be21008b..f34526b81cc 100644 --- a/ckan/tests/controllers/test_organization.py +++ b/ckan/tests/controllers/test_organization.py @@ -327,3 +327,101 @@ def test_organization_search_no_results(self): assert_equal(len(org_names), 0) assert_true("No organizations found for "No Results Here"" in search_response) + + +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() + + 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(controller='organization', action='read', + id=org['id']) + 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 = [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() + + 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(controller='organization', action='read', + id=org['id']) + org_response = app.get(org_url) + 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) + + search_response_html = BeautifulSoup(search_response.body) + + 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) + + 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() + + 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(controller='organization', action='read', + id=org['id']) + org_response = app.get(org_url) + 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) + + search_response_html = BeautifulSoup(search_response.body) + + 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) From 7dfbfcf4e8f9b3ad8afefc4982c2d71233e475d7 Mon Sep 17 00:00:00 2001 From: Brook Elgie Date: Fri, 17 Jul 2015 13:26:11 +0100 Subject: [PATCH 6/6] [#2539] Package search tests --- ckan/tests/controllers/test_package.py | 126 ++++++++++++++++++++++++- 1 file changed, 123 insertions(+), 3 deletions(-) diff --git a/ckan/tests/controllers/test_package.py b/ckan/tests/controllers/test_package.py index 11859e27ea6..86739664ed3 100644 --- a/ckan/tests/controllers/test_package.py +++ b/ckan/tests/controllers/test_package.py @@ -1,3 +1,4 @@ +from bs4 import BeautifulSoup from nose.tools import ( assert_equal, assert_not_equal, @@ -734,9 +735,6 @@ def setup_class(cls): super(cls, cls).setup_class() helpers.reset_db() - def setup(self): - model.repo.rebuild_db() - def test_search_basic(self): dataset1 = factories.Dataset() @@ -765,6 +763,128 @@ def test_search_plugin_hooks(self): assert plugin.calls['before_search'] == 1, plugin.calls assert plugin.calls['after_search'] == 1, plugin.calls + 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') + + search_url = url_for(controller='package', action='search') + search_response = app.get(search_url) + + assert_true('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 = [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) + + 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') + + search_url = url_for(controller='package', action='search') + search_response = app.get(search_url) + + 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) + + search_response_html = BeautifulSoup(search_results.body) + 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) + + 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') + + search_url = url_for(controller='package', action='search') + search_response = app.get(search_url) + + 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) + + search_response_html = BeautifulSoup(search_results.body) + 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) + + 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') + + search_url = url_for(controller='package', action='search') + search_response = app.get(search_url) + + assert_true('/dataset?tags=my-tag' in search_response) + + tag_search_response = app.get('/dataset?tags=my-tag') + + assert_true('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 = [n.string for n in ds_titles] + + assert_equal(len(ds_titles), 1) + assert_true('Dataset One' in ds_titles) + + def test_search_page_results_private(self): + '''Private datasets don't show up in dataset search results.''' + app = self._get_test_app() + 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') + + search_url = url_for(controller='package', action='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 = [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) + class TestPackageFollow(helpers.FunctionalTestBase):