From 6d0754bb65e8e702191d958b1926f5c419d966dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vincent=20Fran=C3=A7oise?= Date: Tue, 12 Jan 2016 15:21:15 +0100 Subject: [PATCH] API Tempest tests on Audits This patchset adds CRUD tests on Audits via the API. Many of them are currently skipped as they revealed some underlying bugs which are referenced on launchpad. Partially Implements: blueprint tempest-basic-set-up Change-Id: I5769f601f9d1cb94bb541959f94f0fa2e17d15c9 --- .../services/infra_optim/v1/json/client.py | 78 ++++++-- .../tests/api/admin/base.py | 35 +++- .../tests/api/admin/test_audit.py | 187 ++++++++++++++++++ 3 files changed, 281 insertions(+), 19 deletions(-) create mode 100644 watcher_tempest_plugin/tests/api/admin/test_audit.py diff --git a/watcher_tempest_plugin/services/infra_optim/v1/json/client.py b/watcher_tempest_plugin/services/infra_optim/v1/json/client.py index ebc6e222c..b7bfc9cc1 100644 --- a/watcher_tempest_plugin/services/infra_optim/v1/json/client.py +++ b/watcher_tempest_plugin/services/infra_optim/v1/json/client.py @@ -40,12 +40,6 @@ class InfraOptimClientJSON(base.BaseInfraOptimClient): """List all existing audit templates.""" return self._list_request('audit_templates', **kwargs) - @base.handle_errors - def list_audit_template_audits(self, audit_template_uuid): - """Lists all audits associated with a audit template.""" - return self._list_request( - '/audit_templates/%s/audits' % audit_template_uuid) - @base.handle_errors def list_audit_templates_detail(self, **kwargs): """Lists details of all existing audit templates.""" @@ -110,18 +104,6 @@ class InfraOptimClientJSON(base.BaseInfraOptimClient): return self._create_request('audit_templates', audit_template) - @base.handle_errors - def create_audit(self, audit_template_uuid, **kwargs): - """Create an audit with the specified parameters. - - :param audit_template_uuid: Audit template ID used by the audit - :return: A tuple with the server response and the created audit. - """ - audit = {'audit_template_uuid': audit_template_uuid} - audit.update(kwargs) - - return self._create_request('audits', audit) - @base.handle_errors def delete_audit_template(self, audit_template_uuid): """Deletes an audit template having the specified UUID. @@ -144,3 +126,63 @@ class InfraOptimClientJSON(base.BaseInfraOptimClient): return self._patch_request('audit_templates', audit_template_uuid, patch) + + # ### AUDITS ### # + + @base.handle_errors + def list_audits(self, **kwargs): + """List all existing audit templates.""" + return self._list_request('audits', **kwargs) + + @base.handle_errors + def list_audits_detail(self, **kwargs): + """Lists details of all existing audit templates.""" + return self._list_request('/audits/detail', **kwargs) + + @base.handle_errors + def list_audit_by_audit_template(self, audit_template_uuid): + """Lists all audits associated with an audit template.""" + return self._list_request( + '/audits', audit_template=audit_template_uuid) + + @base.handle_errors + def show_audit(self, audit_uuid): + """Gets a specific audit template. + + :param audit_uuid: Unique identifier of the audit template + :return: Serialized audit template as a dictionary + """ + return self._show_request('audits', audit_uuid) + + @base.handle_errors + def create_audit(self, audit_template_uuid, **kwargs): + """Create an audit with the specified parameters + + :param audit_template_uuid: Audit template ID used by the audit + :return: A tuple with the server response and the created audit + """ + audit = {'audit_template_uuid': audit_template_uuid} + audit.update(kwargs) + + return self._create_request('audits', audit) + + @base.handle_errors + def delete_audit(self, audit_uuid): + """Deletes an audit having the specified UUID + + :param audit_uuid: The unique identifier of the audit + :return: A tuple with the server response and the response body + """ + + return self._delete_request('audits', audit_uuid) + + @base.handle_errors + def update_audit(self, audit_uuid, patch): + """Update the specified audit. + + :param audit_uuid: The unique identifier of the audit + :param patch: List of dicts representing json patches. + :return: Tuple with the server response and the updated audit + """ + + return self._patch_request('audits', uuid, patch) diff --git a/watcher_tempest_plugin/tests/api/admin/base.py b/watcher_tempest_plugin/tests/api/admin/base.py index c40714766..085220824 100644 --- a/watcher_tempest_plugin/tests/api/admin/base.py +++ b/watcher_tempest_plugin/tests/api/admin/base.py @@ -24,7 +24,7 @@ from watcher_tempest_plugin import infra_optim_clients as clients # Resources must be deleted in a specific order, this list # defines the resource types to clean up, and the correct order. -RESOURCE_TYPES = ['audit_template'] +RESOURCE_TYPES = ['audit_template', 'audit'] # RESOURCE_TYPES = ['action', 'action_plan', 'audit', 'audit_template'] @@ -137,3 +137,36 @@ class BaseInfraOptimTest(test.BaseTestCase): cls.created_objects['audit_template'].remove(uuid) return resp + + # ### AUDITS ### # + + @classmethod + @creates('audit') + def create_audit(cls, audit_template_uuid, type='ONESHOT', + state='PENDING', deadline=None): + """Wrapper utility for creating a test audit + + :param audit_template_uuid: Audit Template UUID this audit will use + :param type: Audit type (either ONESHOT or CONTINUOUS) + :param state: Audit state (str) + :param deadline: Audit deadline (datetime) + :return: A tuple with The HTTP response and its body + """ + resp, body = cls.client.create_audit( + audit_template_uuid=audit_template_uuid, type=type, + state=state, deadline=deadline) + return resp, body + + @classmethod + def delete_audit(cls, audit_uuid): + """Deletes an audit having the specified UUID + + :param audit_uuid: The unique identifier of the audit. + :return: the HTTP response + """ + resp, body = cls.client.delete_audit(audit_uuid) + + if audit_uuid in cls.created_objects['audit']: + cls.created_objects['audit'].remove(audit_uuid) + + return resp diff --git a/watcher_tempest_plugin/tests/api/admin/test_audit.py b/watcher_tempest_plugin/tests/api/admin/test_audit.py new file mode 100644 index 000000000..dadb3fade --- /dev/null +++ b/watcher_tempest_plugin/tests/api/admin/test_audit.py @@ -0,0 +1,187 @@ +# -*- encoding: utf-8 -*- +# Copyright (c) 2016 b<>com +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import unicode_literals + +from tempest import test +from tempest_lib import decorators +from tempest_lib import exceptions as lib_exc + +from watcher_tempest_plugin.tests.api.admin import base + + +class TestCreateUpdateDeleteAudit(base.BaseInfraOptimTest): + """Tests for audit.""" + + def assert_expected(self, expected, actual, + keys=('created_at', 'updated_at', + 'deleted_at', 'state')): + super(TestCreateUpdateDeleteAudit, self).assert_expected( + expected, actual, keys) + + @test.attr(type='smoke') + def test_create_audit_oneshot(self): + _, audit_template = self.create_audit_template() + + audit_params = dict( + audit_template_uuid=audit_template['uuid'], + type='ONESHOT', + ) + + _, body = self.create_audit(**audit_params) + self.assert_expected(audit_params, body) + + _, audit = self.client.show_audit(body['uuid']) + self.assert_expected(audit, body) + + @test.attr(type='smoke') + def test_create_audit_continuous(self): + _, audit_template = self.create_audit_template() + + audit_params = dict( + audit_template_uuid=audit_template['uuid'], + type='CONTINUOUS', + ) + + _, body = self.create_audit(**audit_params) + self.assert_expected(audit_params, body) + + _, audit = self.client.show_audit(body['uuid']) + self.assert_expected(audit, body) + + @test.attr(type='smoke') + def test_create_audit_with_wrong_audit_template(self): + audit_params = dict( + audit_template_uuid='INVALID', + type='ONESHOT', + ) + + self.assertRaises( + lib_exc.BadRequest, self.create_audit, **audit_params) + + @decorators.skip_because(bug="1532843") + @test.attr(type='smoke') + def test_create_audit_with_invalid_state(self): + _, audit_template = self.create_audit_template() + + audit_params = dict( + audit_template_uuid=audit_template['uuid'], + state='INVALID', + ) + + self.assertRaises( + lib_exc.BadRequest, self.create_audit, **audit_params) + + @decorators.skip_because(bug="1533210") + @test.attr(type='smoke') + def test_create_audit_with_no_state(self): + _, audit_template = self.create_audit_template() + + audit_params = dict( + audit_template_uuid=audit_template['uuid'], + state='', + ) + + _, body = self.create_audit(**audit_params) + self.assert_expected(audit_params, body) + + _, audit = self.client.show_audit(body['uuid']) + + initial_audit_state = audit.pop('state') + self.assertEqual(initial_audit_state, 'PENDING') + + self.assert_expected(audit, body) + + @test.attr(type='smoke') + def test_delete_audit(self): + _, audit_template = self.create_audit_template() + _, body = self.create_audit(audit_template['uuid']) + audit_uuid = body['uuid'] + + self.delete_audit(audit_uuid) + + self.assertRaises(lib_exc.NotFound, self.client.show_audit, audit_uuid) + + +class TestShowListAudit(base.BaseInfraOptimTest): + """Tests for audit.""" + + audit_states = ['ONGOING', 'SUCCEEDED', 'SUBMITTED', 'FAILED', + 'CANCELLED', 'DELETED', 'PENDING'] + + @classmethod + def resource_setup(cls): + super(TestShowListAudit, cls).resource_setup() + _, cls.audit_template = cls.create_audit_template() + _, cls.audit = cls.create_audit(cls.audit_template['uuid']) + + def assert_expected(self, expected, actual, + keys=('created_at', 'updated_at', + 'deleted_at', 'state')): + super(TestShowListAudit, self).assert_expected( + expected, actual, keys) + + @test.attr(type='smoke') + def test_show_audit(self): + _, audit = self.client.show_audit( + self.audit['uuid']) + + initial_audit = self.audit.copy() + del initial_audit['state'] + audit_state = audit['state'] + actual_audit = audit.copy() + del actual_audit['state'] + + self.assertIn(audit_state, self.audit_states) + self.assert_expected(initial_audit, actual_audit) + + @test.attr(type='smoke') + def test_show_audit_with_links(self): + _, audit = self.client.show_audit( + self.audit['uuid']) + self.assertIn('links', audit.keys()) + self.assertEqual(2, len(audit['links'])) + self.assertIn(audit['uuid'], + audit['links'][0]['href']) + + @test.attr(type="smoke") + def test_list_audits(self): + _, body = self.client.list_audits() + self.assertIn(self.audit['uuid'], + [i['uuid'] for i in body['audits']]) + # Verify self links. + for audit in body['audits']: + self.validate_self_link('audits', audit['uuid'], + audit['links'][0]['href']) + + @test.attr(type='smoke') + def test_list_with_limit(self): + # We create 3 extra audits to exceed the limit we fix + for _ in range(3): + self.create_audit(self.audit_template['uuid']) + + _, body = self.client.list_audits(limit=3) + + next_marker = body['audits'][-1]['uuid'] + self.assertEqual(len(body['audits']), 3) + self.assertIn(next_marker, body['next']) + + # @decorators.skip_because(bug="1533220") + @test.attr(type='smoke') + def test_list_audits_related_to_given_audit_template(self): + _, body = self.client.list_audit_by_audit_template( + self.audit_template['uuid']) + self.assertIn(self.audit['uuid'], [n['uuid'] for n in body['audits']])