From fd92d8ec59c2f4d91f4a1c5463d95d1237f07b21 Mon Sep 17 00:00:00 2001 From: Rob Cresswell Date: Thu, 11 Feb 2016 14:45:32 +0000 Subject: [PATCH] Fix remaining Django 1.9 test failures This patch gets Horizon to a passing state in the Django 1.9 tests Co-Authored-By: Itxaka Co-Authored-By: Timur Sufiev Change-Id: Icbc1a3c039de658faa9fba4a2cdd5027345fe94d Partially-Implements: blueprint drop-dj17 --- horizon/forms/base.py | 2 +- horizon/forms/views.py | 14 +- horizon/test/settings.py | 3 +- horizon/test/tests/base.py | 6 +- horizon/test/tests/forms.py | 6 +- horizon/test/tests/middleware.py | 7 +- .../dashboards/admin/networks/tests.py | 18 ++- .../dashboards/admin/volumes/tests.py | 14 +- .../dashboards/identity/ngusers/panel.py | 5 - .../dashboards/identity/users/tests.py | 40 ++++++ .../security_groups/tests.py | 21 +++ .../dashboards/project/instances/tests.py | 3 +- .../dashboards/project/stacks/forms.py | 14 ++ .../templates/stacks/_preview_details.html | 2 +- .../project/volumes/backups/tests.py | 12 +- .../project/volumes/cgroups/tests.py | 10 +- .../project/volumes/snapshots/tests.py | 14 +- .../dashboards/project/volumes/test.py | 13 +- .../project/volumes/volumes/tests.py | 120 +++++++++++++----- openstack_dashboard/settings.py | 19 ++- openstack_dashboard/test/helpers.py | 7 +- 21 files changed, 256 insertions(+), 94 deletions(-) diff --git a/horizon/forms/base.py b/horizon/forms/base.py index 5364d38752..b54aa63643 100644 --- a/horizon/forms/base.py +++ b/horizon/forms/base.py @@ -41,7 +41,7 @@ class SelfHandlingForm(SelfHandlingMixin, forms.Form): wish for API errors to appear as errors on the form rather than using the messages framework. """ - self._errors[NON_FIELD_ERRORS] = self.error_class([message]) + self.add_error(NON_FIELD_ERRORS, message) def set_warning(self, message): """Sets a warning on the form. diff --git a/horizon/forms/views.py b/horizon/forms/views.py index f6276e31ca..a743ca2396 100644 --- a/horizon/forms/views.py +++ b/horizon/forms/views.py @@ -133,8 +133,7 @@ class ModalFormView(ModalBackdropMixin, ModalFormMixin, views.HorizonFormView): cancel_label = _("Cancel") cancel_url = None - def get_context_data(self, **kwargs): - context = super(ModalFormView, self).get_context_data(**kwargs) + def _populate_context(self, context): context['modal_id'] = self.modal_id context['modal_header'] = self.modal_header context['form_id'] = self.form_id @@ -144,6 +143,11 @@ class ModalFormView(ModalBackdropMixin, ModalFormMixin, views.HorizonFormView): context['cancel_url'] = self.get_cancel_url() return context + def get_context_data(self, **kwargs): + context = super(ModalFormView, self).get_context_data(**kwargs) + context = self._populate_context(context) + return context + def get_cancel_url(self): return self.cancel_url or self.success_url @@ -165,6 +169,12 @@ class ModalFormView(ModalBackdropMixin, ModalFormMixin, views.HorizonFormView): """Returns an instance of the form to be used in this view.""" return form_class(self.request, **self.get_form_kwargs()) + def form_invalid(self, form): + context = super(ModalFormView, self).get_context_data() + context = self._populate_context(context) + context['form'] = form + return self.render_to_response(context) + def form_valid(self, form): try: handled = form.handle(self.request, form.cleaned_data) diff --git a/horizon/test/settings.py b/horizon/test/settings.py index e231348fdd..7f5ab99995 100644 --- a/horizon/test/settings.py +++ b/horizon/test/settings.py @@ -59,7 +59,8 @@ INSTALLED_APPS = ( 'horizon', 'horizon.test', 'horizon.test.test_dashboards.cats', - 'horizon.test.test_dashboards.dogs' + 'horizon.test.test_dashboards.dogs', + 'openstack_auth' ) MIDDLEWARE_CLASSES = ( diff --git a/horizon/test/tests/base.py b/horizon/test/tests/base.py index 12b85e0a1b..53869c165e 100644 --- a/horizon/test/tests/base.py +++ b/horizon/test/tests/base.py @@ -17,6 +17,7 @@ # License for the specific language governing permissions and limitations # under the License. +import django from django.conf import settings from django.contrib.auth.models import User # noqa from django.core.exceptions import ImproperlyConfigured # noqa @@ -308,7 +309,10 @@ class HorizonTests(BaseHorizonTests): self.client.logout() resp = self.client.get(url) - self.assertRedirects(resp, redirect_url) + if django.VERSION >= (1, 9): + self.assertRedirects(resp, settings.TESTSERVER + redirect_url) + else: + self.assertRedirects(resp, redirect_url) # Set SSL settings for test server settings.SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTOCOL', diff --git a/horizon/test/tests/forms.py b/horizon/test/tests/forms.py index 16c7cbad7b..7f66415a4e 100644 --- a/horizon/test/tests/forms.py +++ b/horizon/test/tests/forms.py @@ -27,6 +27,8 @@ class FormMixinTests(test.TestCase): view.args = args view.kwargs = kwargs view.template_name = 'test_template' + # Note(Itxaka): ModalFormView requires a form_class to behave properly + view.form_class = TestForm return view def test_modal_form_mixin_hide_true_if_ajax(self): @@ -80,7 +82,9 @@ class FormErrorTests(test.TestCase): def setUp(self): super(FormErrorTests, self).setUp() - self.form = TestForm(self.request) + # Note(Itxaka): We pass data to the form so its bound and has the + # proper cleaned_data fields + self.form = TestForm(self.request, data={'fake': 'data'}) def _render_form(self): return shortcuts.render(self.request, self.template, diff --git a/horizon/test/tests/middleware.py b/horizon/test/tests/middleware.py index 694824b3d7..6328ed50dd 100644 --- a/horizon/test/tests/middleware.py +++ b/horizon/test/tests/middleware.py @@ -13,8 +13,8 @@ # License for the specific language governing permissions and limitations # under the License. +import django from django.conf import settings - from django.http import HttpResponseRedirect # noqa from django.utils import timezone @@ -41,7 +41,10 @@ class MiddlewareTests(test.TestCase): resp = mw.process_exception(request, exceptions.NotAuthorized()) resp.client = self.client - self.assertRedirects(resp, url) + if django.VERSION >= (1, 9): + self.assertRedirects(resp, settings.TESTSERVER + url) + else: + self.assertRedirects(resp, url) def test_process_response_redirect_on_ajax_request(self): url = settings.LOGIN_URL diff --git a/openstack_dashboard/dashboards/admin/networks/tests.py b/openstack_dashboard/dashboards/admin/networks/tests.py index c44a16b8b7..50d33ca01d 100644 --- a/openstack_dashboard/dashboards/admin/networks/tests.py +++ b/openstack_dashboard/dashboards/admin/networks/tests.py @@ -387,8 +387,9 @@ class NetworkTests(test.BaseAdminViewTests): tenant_id = self.tenants.first().id network = self.networks.first() extensions = self.api_extensions.list() - api.keystone.tenant_list(IsA(http.HttpRequest)).AndReturn([tenants, - False]) + api.keystone.tenant_list( + IsA(http.HttpRequest) + ).MultipleTimes().AndReturn([tenants, False]) api.neutron.list_extensions( IsA(http.HttpRequest)).AndReturn(extensions) self.mox.ReplayAll() @@ -414,8 +415,11 @@ class NetworkTests(test.BaseAdminViewTests): tenant_id = self.tenants.first().id network = self.networks.first() extensions = self.api_extensions.list() - api.keystone.tenant_list(IsA(http.HttpRequest)).AndReturn([tenants, - False]) + + api.keystone.tenant_list( + IsA(http.HttpRequest) + ).MultipleTimes().AndReturn([tenants, False]) + api.neutron.list_extensions( IsA(http.HttpRequest)).AndReturn(extensions) self.mox.ReplayAll() @@ -444,8 +448,10 @@ class NetworkTests(test.BaseAdminViewTests): tenant_id = self.tenants.first().id network = self.networks.first() extensions = self.api_extensions.list() - api.keystone.tenant_list(IsA(http.HttpRequest)).AndReturn([tenants, - False]) + api.keystone.tenant_list( + IsA(http.HttpRequest) + ).MultipleTimes().AndReturn([tenants, False]) + api.neutron.list_extensions( IsA(http.HttpRequest)).AndReturn(extensions) self.mox.ReplayAll() diff --git a/openstack_dashboard/dashboards/admin/volumes/tests.py b/openstack_dashboard/dashboards/admin/volumes/tests.py index c70d692a89..382382ae2f 100644 --- a/openstack_dashboard/dashboards/admin/volumes/tests.py +++ b/openstack_dashboard/dashboards/admin/volumes/tests.py @@ -18,6 +18,7 @@ from django.conf import settings from django.core.urlresolvers import reverse from django import http from django.test.utils import override_settings +from django.utils.http import urlunquote from mox3.mox import IsA # noqa from openstack_dashboard import api @@ -92,7 +93,7 @@ class VolumeTests(test.BaseAdminViewTests): self.mox.ReplayAll() - res = self.client.get(url) + res = self.client.get(urlunquote(url)) self.assertTemplateUsed(res, 'admin/volumes/index.html') self.assertEqual(res.status_code, 200) @@ -190,8 +191,8 @@ class VolumeTests(test.BaseAdminViewTests): .AndReturn(True) self.mox.ReplayAll() - res = self.client.get(reverse( - 'horizon:admin:volumes:volume_types_tab')) + url = reverse('horizon:admin:volumes:volume_types_tab') + res = self.client.get(urlunquote(url)) self.assertEqual(res.status_code, 200) self.assertTemplateUsed( @@ -216,10 +217,11 @@ class VolumeTests(test.BaseAdminViewTests): AndReturn([self.tenants.list(), False]) self.mox.ReplayAll() - res = self.client.get(reverse('horizon:admin:volumes:snapshots_tab')) + url = reverse('horizon:admin:volumes:snapshots_tab') + res = self.client.get(urlunquote(url)) self.assertEqual(res.status_code, 200) - self.assertTemplateUsed(res, 'horizon/common/_detail_table.html') + self.assertTemplateUsed(res, 'admin/volumes/index.html') snapshots = res.context['volume_snapshots_table'].data self.assertItemsEqual(snapshots, self.cinder_volume_snapshots.list()) @@ -240,7 +242,7 @@ class VolumeTests(test.BaseAdminViewTests): self.mox.ReplayAll() - res = self.client.get(url) + res = self.client.get(urlunquote(url)) self.assertTemplateUsed(res, 'admin/volumes/index.html') self.assertEqual(res.status_code, 200) diff --git a/openstack_dashboard/dashboards/identity/ngusers/panel.py b/openstack_dashboard/dashboards/identity/ngusers/panel.py index e414284015..77c4651b40 100644 --- a/openstack_dashboard/dashboards/identity/ngusers/panel.py +++ b/openstack_dashboard/dashboards/identity/ngusers/panel.py @@ -16,14 +16,9 @@ from django.utils.translation import ugettext_lazy as _ import horizon -from openstack_dashboard.dashboards.identity import dashboard - class NGUsers(horizon.Panel): name = _("Users") slug = 'ngusers' policy_rules = (("identity", "identity:get_user"), ("identity", "identity:list_users")) - - -dashboard.Identity.register(NGUsers) diff --git a/openstack_dashboard/dashboards/identity/users/tests.py b/openstack_dashboard/dashboards/identity/users/tests.py index 80f58a8526..1e1070dc5d 100644 --- a/openstack_dashboard/dashboards/identity/users/tests.py +++ b/openstack_dashboard/dashboards/identity/users/tests.py @@ -18,6 +18,7 @@ from socket import timeout as socket_timeout # noqa +import django from django.core.urlresolvers import reverse from django import http from django.test.utils import override_settings @@ -228,6 +229,19 @@ class UsersViewTests(test.BaseAdminViewTests): api.keystone.role_list(IgnoreArg()).AndReturn(self.roles.list()) api.keystone.get_default_role(IgnoreArg()) \ .AndReturn(self.roles.first()) + if django.VERSION >= (1, 9): + if api.keystone.VERSIONS.active >= 3: + api.keystone.tenant_list( + IgnoreArg(), domain=domain_id).AndReturn( + [self.tenants.list(), False]) + else: + api.keystone.tenant_list( + IgnoreArg(), user=None).AndReturn( + [self.tenants.list(), False]) + + api.keystone.role_list(IgnoreArg()).AndReturn(self.roles.list()) + api.keystone.get_default_role(IgnoreArg()) \ + .AndReturn(self.roles.first()) self.mox.ReplayAll() @@ -268,6 +282,19 @@ class UsersViewTests(test.BaseAdminViewTests): api.keystone.role_list(IgnoreArg()).AndReturn(self.roles.list()) api.keystone.get_default_role(IgnoreArg()) \ .AndReturn(self.roles.first()) + if django.VERSION >= (1, 9): + if api.keystone.VERSIONS.active >= 3: + api.keystone.tenant_list( + IgnoreArg(), domain=domain_id).AndReturn( + [self.tenants.list(), False]) + else: + api.keystone.tenant_list( + IgnoreArg(), user=None).AndReturn( + [self.tenants.list(), False]) + + api.keystone.role_list(IgnoreArg()).AndReturn(self.roles.list()) + api.keystone.get_default_role(IgnoreArg()) \ + .AndReturn(self.roles.first()) self.mox.ReplayAll() @@ -311,6 +338,19 @@ class UsersViewTests(test.BaseAdminViewTests): api.keystone.role_list(IgnoreArg()).AndReturn(self.roles.list()) api.keystone.get_default_role(IgnoreArg()) \ .AndReturn(self.roles.first()) + if django.VERSION >= (1, 9): + if api.keystone.VERSIONS.active >= 3: + api.keystone.tenant_list( + IgnoreArg(), domain=domain_id).AndReturn( + [self.tenants.list(), False]) + else: + api.keystone.tenant_list( + IgnoreArg(), user=None).AndReturn( + [self.tenants.list(), False]) + + api.keystone.role_list(IgnoreArg()).AndReturn(self.roles.list()) + api.keystone.get_default_role(IgnoreArg()) \ + .AndReturn(self.roles.first()) self.mox.ReplayAll() diff --git a/openstack_dashboard/dashboards/project/access_and_security/security_groups/tests.py b/openstack_dashboard/dashboards/project/access_and_security/security_groups/tests.py index bef6b3c51d..bcfe3a8683 100644 --- a/openstack_dashboard/dashboards/project/access_and_security/security_groups/tests.py +++ b/openstack_dashboard/dashboards/project/access_and_security/security_groups/tests.py @@ -18,6 +18,7 @@ import cgi +import django from django.conf import settings from django.core.urlresolvers import reverse from django import http @@ -410,6 +411,12 @@ class SecurityGroupsViewTests(test.TestCase): IsA(http.HttpRequest)).AndReturn(self.secgroup_backend) api.network.security_group_list( IsA(http.HttpRequest)).AndReturn(sec_group_list) + if django.VERSION >= (1, 9): + api.network.security_group_backend( + IsA(http.HttpRequest)).AndReturn(self.secgroup_backend) + api.network.security_group_list( + IsA(http.HttpRequest)).AndReturn(sec_group_list) + self.mox.ReplayAll() formData = {'method': 'AddRule', @@ -435,6 +442,13 @@ class SecurityGroupsViewTests(test.TestCase): IsA(http.HttpRequest)).AndReturn(self.secgroup_backend) api.network.security_group_list( IsA(http.HttpRequest)).AndReturn(sec_group_list) + if django.VERSION >= (1, 9): + for i in range(3): + api.network.security_group_backend( + IsA(http.HttpRequest)).AndReturn(self.secgroup_backend) + api.network.security_group_list( + IsA(http.HttpRequest)).AndReturn(sec_group_list) + self.mox.ReplayAll() formData = {'method': 'AddRule', @@ -490,6 +504,13 @@ class SecurityGroupsViewTests(test.TestCase): api.network.security_group_list( IsA(http.HttpRequest)).AndReturn(sec_group_list) + if django.VERSION >= (1, 9): + for i in range(4): + api.network.security_group_backend( + IsA(http.HttpRequest)).AndReturn(self.secgroup_backend) + api.network.security_group_list( + IsA(http.HttpRequest)).AndReturn(sec_group_list) + self.mox.ReplayAll() formData = {'method': 'AddRule', diff --git a/openstack_dashboard/dashboards/project/instances/tests.py b/openstack_dashboard/dashboards/project/instances/tests.py index 11210d6e53..25984039b8 100644 --- a/openstack_dashboard/dashboards/project/instances/tests.py +++ b/openstack_dashboard/dashboards/project/instances/tests.py @@ -4511,7 +4511,8 @@ class InstanceTests(helpers.TestCase): confirm_password=pass2, disk_config='MANUAL') - self.assertContains(res, "Passwords do not match.") + self.assertEqual(res.context['form'].errors['__all__'], + ["Passwords do not match."]) @helpers.create_stubs(instance_rebuild_post_stubs) def test_rebuild_instance_post_with_empty_string(self): diff --git a/openstack_dashboard/dashboards/project/stacks/forms.py b/openstack_dashboard/dashboards/project/stacks/forms.py index d18921fc49..ea8b458266 100644 --- a/openstack_dashboard/dashboards/project/stacks/forms.py +++ b/openstack_dashboard/dashboards/project/stacks/forms.py @@ -13,6 +13,7 @@ import json import logging +import django from django.conf import settings from django.utils import html from django.utils.translation import ugettext_lazy as _ @@ -125,6 +126,13 @@ class TemplateForm(forms.SelfHandlingForm): widget=forms.widgets.Textarea(attrs=attributes), required=False) + if django.VERSION >= (1, 9): + # Note(Itxaka): On django>=1.9 Charfield has an strip option that + # we need to set to False as to not hit + # https://bugs.launchpad.net/python-heatclient/+bug/1546166 + environment_data.strip = False + template_data.strip = False + def __init__(self, *args, **kwargs): self.next_view = kwargs.pop('next_view') super(TemplateForm, self).__init__(*args, **kwargs) @@ -252,6 +260,12 @@ class CreateStackForm(forms.SelfHandlingForm): environment_data = forms.CharField( widget=forms.widgets.HiddenInput, required=False) + if django.VERSION >= (1, 9): + # Note(Itxaka): On django>=1.9 Charfield has an strip option that + # we need to set to False as to not hit + # https://bugs.launchpad.net/python-heatclient/+bug/1546166 + environment_data.strip = False + parameters = forms.CharField( widget=forms.widgets.HiddenInput) stack_name = forms.RegexField( diff --git a/openstack_dashboard/dashboards/project/stacks/templates/stacks/_preview_details.html b/openstack_dashboard/dashboards/project/stacks/templates/stacks/_preview_details.html index b418edcc5e..12d131a488 100644 --- a/openstack_dashboard/dashboards/project/stacks/templates/stacks/_preview_details.html +++ b/openstack_dashboard/dashboards/project/stacks/templates/stacks/_preview_details.html @@ -54,5 +54,5 @@ {% endblock %} {% block modal-footer %} - {% trans "Close" %} + {% trans "Close" %} {% endblock %} diff --git a/openstack_dashboard/dashboards/project/volumes/backups/tests.py b/openstack_dashboard/dashboards/project/volumes/backups/tests.py index 42430810c7..7778674ae6 100644 --- a/openstack_dashboard/dashboards/project/volumes/backups/tests.py +++ b/openstack_dashboard/dashboards/project/volumes/backups/tests.py @@ -70,21 +70,17 @@ class VolumeBackupsViewTests(test.TestCase): AndReturn(volumes) api.cinder.volume_backup_delete(IsA(http.HttpRequest), backup.id) - api.cinder.volume_backup_list_paged( - IsA(http.HttpRequest), marker=None, sort_dir='desc', - paginate=True).AndReturn([vol_backups, False, False]) - api.cinder.volume_list(IsA(http.HttpRequest)). \ - AndReturn(volumes) self.mox.ReplayAll() formData = {'action': 'volume_backups__delete__%s' % backup.id} res = self.client.post(INDEX_URL + "?tab=volumes_and_snapshots__backups_tab", - formData, follow=True) + formData) - self.assertIn("Scheduled deletion of Volume Backup: backup1", - [m.message for m in res.context['messages']]) + self.assertRedirectsNoFollow(res, INDEX_URL + + "?tab=volumes_and_snapshots__backups_tab") + self.assertMessageCount(success=1) @test.create_stubs({api.cinder: ('volume_backup_get', 'volume_get')}) def test_volume_backup_detail_get(self): diff --git a/openstack_dashboard/dashboards/project/volumes/cgroups/tests.py b/openstack_dashboard/dashboards/project/volumes/cgroups/tests.py index b0eacb340f..19223782f5 100644 --- a/openstack_dashboard/dashboards/project/volumes/cgroups/tests.py +++ b/openstack_dashboard/dashboards/project/volumes/cgroups/tests.py @@ -10,8 +10,10 @@ # License for the specific language governing permissions and limitations # under the License. +import django from django.core.urlresolvers import reverse from django import http +from django.utils.http import urlunquote from mox3.mox import IsA # noqa from openstack_dashboard.api import cinder @@ -19,7 +21,8 @@ from openstack_dashboard.test import helpers as test VOLUME_INDEX_URL = reverse('horizon:project:volumes:index') -VOLUME_CGROUPS_TAB_URL = reverse('horizon:project:volumes:cgroups_tab') +VOLUME_CGROUPS_TAB_URL = urlunquote(reverse( + 'horizon:project:volumes:cgroups_tab')) class ConsistencyGroupTests(test.TestCase): @@ -111,8 +114,9 @@ class ConsistencyGroupTests(test.TestCase): AndReturn(cgroups) cinder.volume_cgroup_delete(IsA(http.HttpRequest), cgroup.id, force=False) - cinder.volume_cgroup_list_with_vol_type_names(IsA(http.HttpRequest)).\ - AndReturn(cgroups) + if django.VERSION < (1, 9): + cinder.volume_cgroup_list_with_vol_type_names( + IsA(http.HttpRequest)).AndReturn(cgroups) self.mox.ReplayAll() diff --git a/openstack_dashboard/dashboards/project/volumes/snapshots/tests.py b/openstack_dashboard/dashboards/project/volumes/snapshots/tests.py index a8cc74187d..d7975b5566 100644 --- a/openstack_dashboard/dashboards/project/volumes/snapshots/tests.py +++ b/openstack_dashboard/dashboards/project/volumes/snapshots/tests.py @@ -108,7 +108,8 @@ class VolumeSnapshotsViewTests(test.TestCase): @test.create_stubs({api.cinder: ('volume_snapshot_list_paged', 'volume_list', 'volume_backup_supported', - 'volume_snapshot_delete')}) + 'volume_snapshot_delete', + 'tenant_absolute_limits')}) def test_delete_volume_snapshot(self): vol_snapshots = self.cinder_volume_snapshots.list() volumes = self.cinder_volumes.list() @@ -123,19 +124,14 @@ class VolumeSnapshotsViewTests(test.TestCase): AndReturn(volumes) api.cinder.volume_snapshot_delete(IsA(http.HttpRequest), snapshot.id) - api.cinder.volume_snapshot_list_paged( - IsA(http.HttpRequest), paginate=True, marker=None, - sort_dir='desc').AndReturn([[], False, False]) - api.cinder.volume_list(IsA(http.HttpRequest)). \ - AndReturn(volumes) self.mox.ReplayAll() formData = {'action': 'volume_snapshots__delete__%s' % snapshot.id} - res = self.client.post(VOLUME_SNAPSHOTS_TAB_URL, formData, follow=True) + res = self.client.post(VOLUME_SNAPSHOTS_TAB_URL, formData) - self.assertIn("Scheduled deletion of Volume Snapshot: test snapshot", - [m.message for m in res.context['messages']]) + self.assertRedirectsNoFollow(res, VOLUME_SNAPSHOTS_TAB_URL) + self.assertMessageCount(success=1) @test.create_stubs({api.cinder: ('volume_snapshot_get', 'volume_get')}) def test_volume_snapshot_detail_get(self): diff --git a/openstack_dashboard/dashboards/project/volumes/test.py b/openstack_dashboard/dashboards/project/volumes/test.py index b632fbd0ff..10a72eb71a 100644 --- a/openstack_dashboard/dashboards/project/volumes/test.py +++ b/openstack_dashboard/dashboards/project/volumes/test.py @@ -18,6 +18,7 @@ from django.conf import settings from django.core.urlresolvers import reverse from django import http from django.test.utils import override_settings +from django.utils.http import urlunquote from mox3.mox import IsA # noqa @@ -32,8 +33,10 @@ from openstack_dashboard.test import helpers as test INDEX_URL = reverse('horizon:project:volumes:index') -VOLUME_SNAPSHOTS_TAB_URL = reverse('horizon:project:volumes:snapshots_tab') -VOLUME_BACKUPS_TAB_URL = reverse('horizon:project:volumes:backups_tab') +VOLUME_SNAPSHOTS_TAB_URL = urlunquote(reverse( + 'horizon:project:volumes:snapshots_tab')) +VOLUME_BACKUPS_TAB_URL = urlunquote(reverse( + 'horizon:project:volumes:backups_tab')) class VolumeAndSnapshotsAndBackupsTests(test.TestCase): @@ -126,7 +129,7 @@ class VolumeAndSnapshotsAndBackupsTests(test.TestCase): AndReturn(self.cinder_limits['absolute']) self.mox.ReplayAll() - res = self.client.get(url) + res = self.client.get(urlunquote(url)) self.assertEqual(res.status_code, 200) self.assertTemplateUsed(res, 'project/volumes/index.html') @@ -223,7 +226,7 @@ class VolumeAndSnapshotsAndBackupsTests(test.TestCase): self.cinder_volumes.list()) self.mox.ReplayAll() - res = self.client.get(url) + res = self.client.get(urlunquote(url)) self.assertEqual(res.status_code, 200) self.assertTemplateUsed(res, 'project/volumes/index.html') @@ -312,7 +315,7 @@ class VolumeAndSnapshotsAndBackupsTests(test.TestCase): self.cinder_volumes.list()) self.mox.ReplayAll() - res = self.client.get(url) + res = self.client.get(urlunquote(url)) self.assertEqual(res.status_code, 200) self.assertTemplateUsed(res, 'project/volumes/index.html') diff --git a/openstack_dashboard/dashboards/project/volumes/volumes/tests.py b/openstack_dashboard/dashboards/project/volumes/volumes/tests.py index 3eac769cb7..999bf3d234 100644 --- a/openstack_dashboard/dashboards/project/volumes/volumes/tests.py +++ b/openstack_dashboard/dashboards/project/volumes/volumes/tests.py @@ -16,10 +16,12 @@ # License for the specific language governing permissions and limitations # under the License. +import django from django.core.urlresolvers import reverse from django.forms import widgets from django import http from django.test.utils import override_settings +from django.utils.http import urlunquote from mox3.mox import IsA # noqa import six @@ -32,7 +34,8 @@ from openstack_dashboard.usage import quotas VOLUME_INDEX_URL = reverse('horizon:project:volumes:index') -VOLUME_VOLUMES_TAB_URL = reverse('horizon:project:volumes:volumes_tab') +VOLUME_VOLUMES_TAB_URL = urlunquote(reverse( + 'horizon:project:volumes:volumes_tab')) SEARCH_OPTS = dict(status=api.cinder.VOLUME_STATE_AVAILABLE) @@ -472,8 +475,9 @@ class VolumeViewTests(test.TestCase): cinder.volume_type_list(IsA(http.HttpRequest)).\ AndReturn(self.volume_types.list()) - cinder.volume_type_list(IsA(http.HttpRequest)).\ - AndReturn(self.volume_types.list()) + if django.VERSION >= (1, 9): + cinder.volume_type_list(IsA(http.HttpRequest)).\ + AndReturn(self.volume_types.list()) cinder.volume_type_default(IsA(http.HttpRequest)).\ AndReturn(self.volume_types.first()) quotas.tenant_limit_usages(IsA(http.HttpRequest)).\ @@ -482,9 +486,13 @@ class VolumeViewTests(test.TestCase): str(snapshot.id)).AndReturn(snapshot) cinder.volume_get(IsA(http.HttpRequest), snapshot.volume_id).\ AndReturn(self.cinder_volumes.first()) - - quotas.tenant_limit_usages(IsA(http.HttpRequest)).\ - AndReturn(usage_limit) + if django.VERSION >= (1, 9): + cinder.volume_type_default(IsA(http.HttpRequest)). \ + AndReturn(self.volume_types.first()) + cinder.volume_snapshot_get(IsA(http.HttpRequest), + str(snapshot.id)).AndReturn(snapshot) + cinder.volume_get(IsA(http.HttpRequest), snapshot.volume_id). \ + AndReturn(self.cinder_volumes.first()) self.mox.ReplayAll() @@ -649,8 +657,9 @@ class VolumeViewTests(test.TestCase): cinder.volume_type_list(IsA(http.HttpRequest)).\ AndReturn(self.volume_types.list()) - cinder.volume_type_list(IsA(http.HttpRequest)).\ - AndReturn(self.volume_types.list()) + if django.VERSION >= (1, 9): + cinder.volume_type_list(IsA(http.HttpRequest)).\ + AndReturn(self.volume_types.list()) cinder.volume_type_default(IsA(http.HttpRequest)).\ AndReturn(self.volume_types.first()) quotas.tenant_limit_usages(IsA(http.HttpRequest)).\ @@ -661,8 +670,15 @@ class VolumeViewTests(test.TestCase): .AndReturn(True) cinder.availability_zone_list(IsA(http.HttpRequest)).AndReturn( self.cinder_availability_zones.list()) - quotas.tenant_limit_usages(IsA(http.HttpRequest)).\ - AndReturn(usage_limit) + if django.VERSION >= (1, 9): + cinder.volume_type_default(IsA(http.HttpRequest)). \ + AndReturn(self.volume_types.first()) + api.glance.image_get(IsA(http.HttpRequest), + str(image.id)).AndReturn(image) + cinder.extension_supported(IsA(http.HttpRequest), + 'AvailabilityZones').AndReturn(True) + cinder.availability_zone_list(IsA(http.HttpRequest)).AndReturn( + self.cinder_availability_zones.list()) self.mox.ReplayAll() @@ -696,8 +712,9 @@ class VolumeViewTests(test.TestCase): cinder.volume_type_list(IsA(http.HttpRequest)).\ AndReturn(self.volume_types.list()) - cinder.volume_type_list(IsA(http.HttpRequest)).\ - AndReturn(self.volume_types.list()) + if django.VERSION >= (1, 9): + cinder.volume_type_list(IsA(http.HttpRequest)).\ + AndReturn(self.volume_types.list()) cinder.volume_type_default(IsA(http.HttpRequest)).\ AndReturn(self.volume_types.first()) quotas.tenant_limit_usages(IsA(http.HttpRequest)).\ @@ -708,8 +725,15 @@ class VolumeViewTests(test.TestCase): .AndReturn(True) cinder.availability_zone_list(IsA(http.HttpRequest)).AndReturn( self.cinder_availability_zones.list()) - quotas.tenant_limit_usages(IsA(http.HttpRequest)).\ - AndReturn(usage_limit) + if django.VERSION >= (1, 9): + cinder.volume_type_default(IsA(http.HttpRequest)).\ + AndReturn(self.volume_types.first()) + api.glance.image_get(IsA(http.HttpRequest), + str(image.id)).AndReturn(image) + cinder.extension_supported(IsA(http.HttpRequest), + 'AvailabilityZones').AndReturn(True) + cinder.availability_zone_list(IsA(http.HttpRequest)).AndReturn( + self.cinder_availability_zones.list()) self.mox.ReplayAll() @@ -753,8 +777,9 @@ class VolumeViewTests(test.TestCase): cinder.volume_type_list(IsA(http.HttpRequest)).\ AndReturn(self.volume_types.list()) - cinder.volume_type_list(IsA(http.HttpRequest)).\ - AndReturn(self.volume_types.list()) + if django.VERSION >= (1, 9): + cinder.volume_type_list(IsA(http.HttpRequest)).\ + AndReturn(self.volume_types.list()) cinder.volume_type_default(IsA(http.HttpRequest)).\ AndReturn(self.volume_types.first()) quotas.tenant_limit_usages(IsA(http.HttpRequest)).\ @@ -778,8 +803,29 @@ class VolumeViewTests(test.TestCase): .AndReturn(True) cinder.availability_zone_list(IsA(http.HttpRequest)).AndReturn( self.cinder_availability_zones.list()) - quotas.tenant_limit_usages(IsA(http.HttpRequest)).\ - AndReturn(usage_limit) + if django.VERSION >= (1, 9): + cinder.volume_type_default(IsA(http.HttpRequest)).\ + AndReturn(self.volume_types.first()) + cinder.volume_snapshot_list(IsA(http.HttpRequest), + search_opts=SEARCH_OPTS). \ + AndReturn(self.cinder_volume_snapshots.list()) + api.glance.image_list_detailed( + IsA(http.HttpRequest), + filters={'is_public': True, 'status': 'active'}) \ + .AndReturn([self.images.list(), False, False]) + api.glance.image_list_detailed( + IsA(http.HttpRequest), + filters={'property-owner_id': self.tenant.id, + 'status': 'active'}) \ + .AndReturn([[], False, False]) + cinder.volume_list(IsA( + http.HttpRequest), + search_opts=SEARCH_OPTS).AndReturn(self.cinder_volumes.list()) + cinder.extension_supported(IsA(http.HttpRequest), + 'AvailabilityZones') \ + .AndReturn(True) + cinder.availability_zone_list(IsA(http.HttpRequest)).AndReturn( + self.cinder_availability_zones.list()) self.mox.ReplayAll() @@ -810,8 +856,9 @@ class VolumeViewTests(test.TestCase): cinder.volume_type_list(IsA(http.HttpRequest)).\ AndReturn(self.volume_types.list()) - cinder.volume_type_list(IsA(http.HttpRequest)).\ - AndReturn(self.volume_types.list()) + if django.VERSION >= (1, 9): + cinder.volume_type_list(IsA(http.HttpRequest)).\ + AndReturn(self.volume_types.list()) cinder.volume_type_default(IsA(http.HttpRequest)).\ AndReturn(self.volume_types.first()) quotas.tenant_limit_usages(IsA(http.HttpRequest)).\ @@ -835,8 +882,29 @@ class VolumeViewTests(test.TestCase): .AndReturn(True) cinder.availability_zone_list(IsA(http.HttpRequest)).AndReturn( self.cinder_availability_zones.list()) - quotas.tenant_limit_usages(IsA(http.HttpRequest)).\ - AndReturn(usage_limit) + if django.VERSION >= (1, 9): + cinder.volume_type_default(IsA(http.HttpRequest)). \ + AndReturn(self.volume_types.first()) + cinder.volume_snapshot_list(IsA(http.HttpRequest), + search_opts=SEARCH_OPTS). \ + AndReturn(self.cinder_volume_snapshots.list()) + api.glance.image_list_detailed( + IsA(http.HttpRequest), + filters={'is_public': True, 'status': 'active'}) \ + .AndReturn([self.images.list(), False, False]) + api.glance.image_list_detailed( + IsA(http.HttpRequest), + filters={'property-owner_id': self.tenant.id, + 'status': 'active'}) \ + .AndReturn([[], False, False]) + cinder.volume_list(IsA( + http.HttpRequest), + search_opts=SEARCH_OPTS).AndReturn(self.cinder_volumes.list()) + cinder.extension_supported(IsA(http.HttpRequest), + 'AvailabilityZones') \ + .AndReturn(True) + cinder.availability_zone_list(IsA(http.HttpRequest)).AndReturn( + self.cinder_availability_zones.list()) self.mox.ReplayAll() @@ -1434,18 +1502,12 @@ class VolumeViewTests(test.TestCase): quotas: ('tenant_limit_usages',)}) def test_extend_volume_with_wrong_size(self): volume = self.cinder_volumes.first() - usage_limit = {'maxTotalVolumeGigabytes': 100, - 'gigabytesUsed': 20, - 'volumesUsed': len(self.cinder_volumes.list()), - 'maxTotalVolumes': 6} formData = {'name': u'A Volume I Am Making', 'orig_size': volume.size, 'new_size': 10} cinder.volume_get(IsA(http.HttpRequest), volume.id).\ AndReturn(self.cinder_volumes.first()) - quotas.tenant_limit_usages(IsA(http.HttpRequest)).\ - AndReturn(usage_limit) self.mox.ReplayAll() @@ -1574,8 +1636,6 @@ class VolumeViewTests(test.TestCase): AndReturn(usage_limit) cinder.volume_get(IsA(http.HttpRequest), volume.id).\ AndReturn(self.volumes.first()) - quotas.tenant_limit_usages(IsA(http.HttpRequest)).\ - AndReturn(usage_limit) self.mox.ReplayAll() diff --git a/openstack_dashboard/settings.py b/openstack_dashboard/settings.py index d4a63523f0..2a926a0678 100644 --- a/openstack_dashboard/settings.py +++ b/openstack_dashboard/settings.py @@ -303,7 +303,7 @@ if os.path.exists(LOCAL_SETTINGS_DIR_PATH): execfile(os.path.join(dirpath, filename)) except Exception as e: logging.exception( - "Can not exec settings snippet %s" % (filename)) + "Can not exec settings snippet %s" % filename) if not WEBROOT.endswith('/'): @@ -377,8 +377,14 @@ settings.update_dashboards( ) INSTALLED_APPS[0:0] = ADD_INSTALLED_APPS -from openstack_auth import policy -POLICY_CHECK_FUNCTION = policy.check + +def check(actions, request, target=None): + # Note(Itxaka): This is to prevent circular dependencies and apps not ready + # If you do django imports in your settings, you are gonna have a bad time + from openstack_auth import policy + return policy.check(actions, request, target=None) + +POLICY_CHECK_FUNCTION = check # This base context objects gets added to the offline context generator # for each theme configured. @@ -393,11 +399,4 @@ COMPRESS_OFFLINE_CONTEXT = 'horizon.themes.offline_context' if DEBUG: logging.basicConfig(level=logging.DEBUG) -# during django reloads and an active user is logged in, the monkey -# patch below will not otherwise be applied in time - resulting in developers -# appearing to be logged out. In typical production deployments this section -# below may be omitted, though it should not be harmful -from openstack_auth import utils as auth_utils -auth_utils.patch_middleware_get_user() - CSRF_COOKIE_AGE = None diff --git a/openstack_dashboard/test/helpers.py b/openstack_dashboard/test/helpers.py index dfca1e9d9d..2336bfdaea 100644 --- a/openstack_dashboard/test/helpers.py +++ b/openstack_dashboard/test/helpers.py @@ -29,6 +29,7 @@ from django.core.handlers import wsgi from django.core import urlresolvers from django.test.client import RequestFactory # noqa from django.test import utils as django_test_utils +from django.utils import http from ceilometerclient.v2 import client as ceilometer_client from cinderclient import client as cinder_client @@ -232,8 +233,10 @@ class TestCase(horizon_helpers.TestCase): processing the view which is redirected to. """ if django.VERSION >= (1, 9): - self.assertEqual(response._headers.get('location', None), - ('Location', expected_url)) + loc = six.text_type(response._headers.get('location', None)[1]) + loc = http.urlunquote(loc) + expected_url = http.urlunquote(expected_url) + self.assertEqual(loc, expected_url) else: self.assertEqual(response._headers.get('location', None), ('Location', settings.TESTSERVER + expected_url))