diff --git a/watcher/common/exception.py b/watcher/common/exception.py index 706fc89d7..63cdc598c 100644 --- a/watcher/common/exception.py +++ b/watcher/common/exception.py @@ -248,6 +248,14 @@ class ActionFilterCombinationProhibited(Invalid): "prohibited") +class EfficacyIndicatorNotFound(ResourceNotFound): + msg_fmt = _("Efficacy indicator %(efficacy_indicator)s could not be found") + + +class EfficacyIndicatorAlreadyExists(Conflict): + msg_fmt = _("An action with UUID %(uuid)s already exists") + + class HTTPNotFound(ResourceNotFound): pass diff --git a/watcher/db/api.py b/watcher/db/api.py index f32118b5e..1416b2fe1 100644 --- a/watcher/db/api.py +++ b/watcher/db/api.py @@ -560,3 +560,93 @@ class BaseConnection(object): :raises: :py:class:`~.ActionPlanReferenced` :raises: :py:class:`~.Invalid` """ + + @abc.abstractmethod + def get_efficacy_indicator_list(self, context, filters=None, limit=None, + marker=None, sort_key=None, sort_dir=None): + """Get specific columns for matching efficacy indicators. + + Return a list of the specified columns for all efficacy indicators that + match the specified filters. + + :param context: The security context + :param columns: List of column names to return. + Defaults to 'id' column when columns == None. + :param filters: Filters to apply. Defaults to None. + + :param limit: Maximum number of efficacy indicators to return. + :param marker: The last item of the previous page; we return the next + result set. + :param sort_key: Attribute by which results should be sorted. + :param sort_dir: Direction in which results should be sorted. + (asc, desc) + :returns: A list of tuples of the specified columns. + """ + + @abc.abstractmethod + def create_efficacy_indicator(self, values): + """Create a new efficacy indicator. + + :param values: A dict containing items used to identify + and track the efficacy indicator. For example: + + :: + + { + 'id': 1, + 'uuid': utils.generate_uuid(), + 'name': 'my_efficacy_indicator', + 'display_name': 'My efficacy indicator', + 'goal_uuid': utils.generate_uuid(), + } + :returns: An efficacy_indicator + :raises: :py:class:`~.EfficacyIndicatorAlreadyExists` + """ + + @abc.abstractmethod + def get_efficacy_indicator_by_id(self, context, efficacy_indicator_id): + """Return an efficacy indicator given its ID. + + :param context: The security context + :param efficacy_indicator_id: The ID of an efficacy indicator + :returns: An efficacy indicator + :raises: :py:class:`~.EfficacyIndicatorNotFound` + """ + + @abc.abstractmethod + def get_efficacy_indicator_by_uuid(self, context, efficacy_indicator_uuid): + """Return an efficacy indicator given its UUID. + + :param context: The security context + :param efficacy_indicator_uuid: The UUID of an efficacy indicator + :returns: An efficacy indicator + :raises: :py:class:`~.EfficacyIndicatorNotFound` + """ + + @abc.abstractmethod + def get_efficacy_indicator_by_name(self, context, efficacy_indicator_name): + """Return an efficacy indicator given its name. + + :param context: The security context + :param efficacy_indicator_name: The name of an efficacy indicator + :returns: An efficacy indicator + :raises: :py:class:`~.EfficacyIndicatorNotFound` + """ + + @abc.abstractmethod + def destroy_efficacy_indicator(self, efficacy_indicator_uuid): + """Destroy an efficacy indicator. + + :param efficacy_indicator_uuid: The UUID of an efficacy indicator + :raises: :py:class:`~.EfficacyIndicatorNotFound` + """ + + @abc.abstractmethod + def update_efficacy_indicator(self, efficacy_indicator_uuid, values): + """Update properties of an efficacy indicator. + + :param efficacy_indicator_uuid: The UUID of an efficacy indicator + :returns: An efficacy indicator + :raises: :py:class:`~.EfficacyIndicatorNotFound` + :raises: :py:class:`~.Invalid` + """ diff --git a/watcher/db/sqlalchemy/api.py b/watcher/db/sqlalchemy/api.py index 39be50390..0bb1b64db 100644 --- a/watcher/db/sqlalchemy/api.py +++ b/watcher/db/sqlalchemy/api.py @@ -17,6 +17,8 @@ """SQLAlchemy storage backend.""" +import collections + from oslo_config import cfg from oslo_db import exception as db_exc from oslo_db.sqlalchemy import session as db_session @@ -102,6 +104,14 @@ def _paginate_query(model, limit=None, marker=None, sort_key=None, return query.all() +class JoinMap(utils.Struct): + """Mapping for the Join-based queries""" + + +NaturalJoinFilter = collections.namedtuple( + 'NaturalJoinFilter', ['join_fieldname', 'join_model']) + + class Connection(api.BaseConnection): """SqlAlchemy connection.""" @@ -185,9 +195,11 @@ class Connection(api.BaseConnection): def __add_simple_filter(self, query, model, fieldname, value): return query.filter(getattr(model, fieldname) == value) - def __add_join_filter(self, query, model, join_model, fieldname, value): + def __add_natural_join_filter(self, query, join_model, + join_fieldname, value): query = query.join(join_model) - return self.__add_simple_filter(query, join_model, fieldname, value) + return self.__add_simple_filter( + query, join_model, join_fieldname, value) def _add_filters(self, query, model, filters=None, plain_fields=None, join_fieldmap=None): @@ -198,20 +210,19 @@ class Connection(api.BaseConnection): :param filters: dict with the following structure {"fieldname": value} :param plain_fields: a :py:class:`sqlalchemy.orm.query.Query` instance :param join_fieldmap: a :py:class:`sqlalchemy.orm.query.Query` instance - """ filters = filters or {} plain_fields = plain_fields or () - join_fieldmap = join_fieldmap or {} + join_fieldmap = join_fieldmap or JoinMap() for fieldname, value in filters.items(): if fieldname in plain_fields: query = self.__add_simple_filter( query, model, fieldname, value) elif fieldname in join_fieldmap: - join_field, join_model = join_fieldmap[fieldname] - query = self.__add_join_filter( - query, model, join_model, join_field, value) + join_fieldname, join_model = join_fieldmap[fieldname] + query = self.__add_natural_join_filter( + query, join_model, join_fieldname, value) query = self.__add_soft_delete_mixin_filters(query, filters, model) query = self.__add_timestamp_mixin_filters(query, filters, model) @@ -281,11 +292,11 @@ class Connection(api.BaseConnection): def _add_strategies_filters(self, query, filters): plain_fields = ['uuid', 'name', 'display_name', 'goal_id'] - join_fieldmap = { - 'goal_uuid': ("uuid", models.Goal), - 'goal_name': ("name", models.Goal) - } - + join_fieldmap = JoinMap( + goal_uuid=NaturalJoinFilter( + join_fieldname="uuid", join_model=models.Goal), + goal_name=NaturalJoinFilter( + join_fieldname="name", join_model=models.Goal)) return self._add_filters( query=query, model=models.Strategy, filters=filters, plain_fields=plain_fields, join_fieldmap=join_fieldmap) @@ -296,12 +307,16 @@ class Connection(api.BaseConnection): plain_fields = ['uuid', 'name', 'host_aggregate', 'goal_id', 'strategy_id'] - join_fieldmap = { - 'goal_uuid': ("uuid", models.Goal), - 'goal_name': ("name", models.Goal), - 'strategy_uuid': ("uuid", models.Strategy), - 'strategy_name': ("name", models.Strategy), - } + join_fieldmap = JoinMap( + goal_uuid=NaturalJoinFilter( + join_fieldname="uuid", join_model=models.Goal), + goal_name=NaturalJoinFilter( + join_fieldname="name", join_model=models.Goal), + strategy_uuid=NaturalJoinFilter( + join_fieldname="uuid", join_model=models.Strategy), + strategy_name=NaturalJoinFilter( + join_fieldname="name", join_model=models.Strategy), + ) return self._add_filters( query=query, model=models.AuditTemplate, filters=filters, @@ -394,6 +409,20 @@ class Connection(api.BaseConnection): return query + def _add_efficacy_indicators_filters(self, query, filters): + if filters is None: + filters = {} + + plain_fields = ['uuid', 'name', 'unit', 'schema', 'action_plan_id'] + join_fieldmap = JoinMap( + action_plan_uuid=NaturalJoinFilter( + join_fieldname="uuid", join_model=models.ActionPlan), + ) + + return self._add_filters( + query=query, model=models.EfficacyIndicator, filters=filters, + plain_fields=plain_fields, join_fieldmap=join_fieldmap) + # ### GOALS ### # def get_goal_list(self, context, filters=None, limit=None, @@ -923,3 +952,76 @@ class Connection(api.BaseConnection): raise exception.ActionPlanNotFound(action_plan=action_plan_id) query.soft_delete() + + # ### EFFICACY INDICATORS ### # + + def get_efficacy_indicator_list(self, context, filters=None, limit=None, + marker=None, sort_key=None, sort_dir=None): + + query = model_query(models.EfficacyIndicator) + query = self._add_efficacy_indicators_filters(query, filters) + if not context.show_deleted: + query = query.filter_by(deleted_at=None) + return _paginate_query(models.EfficacyIndicator, limit, marker, + sort_key, sort_dir, query) + + def create_efficacy_indicator(self, values): + # ensure defaults are present for new efficacy indicators + if not values.get('uuid'): + values['uuid'] = utils.generate_uuid() + + efficacy_indicator = models.EfficacyIndicator() + efficacy_indicator.update(values) + + try: + efficacy_indicator.save() + except db_exc.DBDuplicateEntry: + raise exception.EfficacyIndicatorAlreadyExists(uuid=values['uuid']) + return efficacy_indicator + + def _get_efficacy_indicator(self, context, fieldname, value): + try: + return self._get(context, model=models.EfficacyIndicator, + fieldname=fieldname, value=value) + except exception.ResourceNotFound: + raise exception.EfficacyIndicatorNotFound(efficacy_indicator=value) + + def get_efficacy_indicator_by_id(self, context, efficacy_indicator_id): + return self._get_efficacy_indicator( + context, fieldname="id", value=efficacy_indicator_id) + + def get_efficacy_indicator_by_uuid(self, context, efficacy_indicator_uuid): + return self._get_efficacy_indicator( + context, fieldname="uuid", value=efficacy_indicator_uuid) + + def get_efficacy_indicator_by_name(self, context, efficacy_indicator_name): + return self._get_efficacy_indicator( + context, fieldname="name", value=efficacy_indicator_name) + + def update_efficacy_indicator(self, efficacy_indicator_id, values): + if 'uuid' in values: + raise exception.Invalid( + message=_("Cannot overwrite UUID for an existing " + "efficacy indicator.")) + + try: + return self._update( + models.EfficacyIndicator, efficacy_indicator_id, values) + except exception.ResourceNotFound: + raise exception.EfficacyIndicatorNotFound( + efficacy_indicator=efficacy_indicator_id) + + def soft_delete_efficacy_indicator(self, efficacy_indicator_id): + try: + self._soft_delete(models.EfficacyIndicator, efficacy_indicator_id) + except exception.ResourceNotFound: + raise exception.EfficacyIndicatorNotFound( + efficacy_indicator=efficacy_indicator_id) + + def destroy_efficacy_indicator(self, efficacy_indicator_id): + try: + return self._destroy( + models.EfficacyIndicator, efficacy_indicator_id) + except exception.ResourceNotFound: + raise exception.EfficacyIndicatorNotFound( + efficacy_indicator=efficacy_indicator_id) diff --git a/watcher/db/sqlalchemy/models.py b/watcher/db/sqlalchemy/models.py index d3379ec0a..f97af8ab9 100644 --- a/watcher/db/sqlalchemy/models.py +++ b/watcher/db/sqlalchemy/models.py @@ -27,6 +27,7 @@ from sqlalchemy import DateTime from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import ForeignKey from sqlalchemy import Integer +from sqlalchemy import Numeric from sqlalchemy import schema from sqlalchemy import String from sqlalchemy.types import TypeDecorator, TEXT @@ -205,6 +206,24 @@ class ActionPlan(Base): id = Column(Integer, primary_key=True) uuid = Column(String(36)) first_action_id = Column(Integer) - audit_id = Column(Integer, ForeignKey('audits.id'), - nullable=True) + audit_id = Column(Integer, ForeignKey('audits.id'), nullable=True) state = Column(String(20), nullable=True) + global_efficacy = Column(JSONEncodedDict, nullable=True) + + +class EfficacyIndicator(Base): + """Represents an efficacy indicator.""" + + __tablename__ = 'efficacy_indicators' + __table_args__ = ( + schema.UniqueConstraint('uuid', name='uniq_efficacy_indicators0uuid'), + table_args() + ) + id = Column(Integer, primary_key=True) + uuid = Column(String(36)) + name = Column(String(63)) + description = Column(String(255), nullable=True) + unit = Column(String(63), nullable=True) + value = Column(Numeric()) + action_plan_id = Column(Integer, ForeignKey('action_plans.id'), + nullable=False) diff --git a/watcher/objects/utils.py b/watcher/objects/utils.py index f399b71a3..5a5dd57ee 100644 --- a/watcher/objects/utils.py +++ b/watcher/objects/utils.py @@ -46,6 +46,15 @@ def datetime_or_str_or_none(val): return datetime_or_none(val) +def numeric_or_none(val): + """Attempt to parse an integer value, or None.""" + if val is None: + return val + else: + f_val = float(val) + return f_val if not f_val.is_integer() else val + + def int_or_none(val): """Attempt to parse an integer value, or None.""" if val is None: diff --git a/watcher/tests/db/test_efficacy_indicator.py b/watcher/tests/db/test_efficacy_indicator.py new file mode 100644 index 000000000..80d511dd8 --- /dev/null +++ b/watcher/tests/db/test_efficacy_indicator.py @@ -0,0 +1,385 @@ +# Copyright 2015 OpenStack Foundation +# All Rights Reserved. +# +# 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. + +"""Tests for manipulating EfficacyIndicator via the DB API""" + +import freezegun +import six + +from watcher.common import exception +from watcher.common import utils as w_utils +from watcher.tests.db import base +from watcher.tests.db import utils + + +class TestDbEfficacyIndicatorFilters(base.DbTestCase): + + FAKE_OLDER_DATE = '2014-01-01T09:52:05.219414' + FAKE_OLD_DATE = '2015-01-01T09:52:05.219414' + FAKE_TODAY = '2016-02-24T09:52:05.219414' + + def setUp(self): + super(TestDbEfficacyIndicatorFilters, self).setUp() + self.context.show_deleted = True + self._data_setup() + + def _data_setup(self): + self.audit_template_name = "Audit Template" + + self.audit_template = utils.create_test_audit_template( + name=self.audit_template_name, id=1, uuid=None) + self.audit = utils.create_test_audit( + audit_template_id=self.audit_template.id, id=1, uuid=None) + self.action_plan = utils.create_test_action_plan( + audit_id=self.audit.id, id=1, uuid=None) + + with freezegun.freeze_time(self.FAKE_TODAY): + self.efficacy_indicator1 = utils.create_test_efficacy_indicator( + action_plan_id=self.action_plan.id, id=1, uuid=None, + name="efficacy_indicator1", description="Test Indicator 1") + with freezegun.freeze_time(self.FAKE_OLD_DATE): + self.efficacy_indicator2 = utils.create_test_efficacy_indicator( + action_plan_id=self.action_plan.id, id=2, uuid=None, + name="efficacy_indicator2", description="Test Indicator 2") + with freezegun.freeze_time(self.FAKE_OLDER_DATE): + self.efficacy_indicator3 = utils.create_test_efficacy_indicator( + action_plan_id=self.action_plan.id, id=3, uuid=None, + name="efficacy_indicator3", description="Test Indicator 3") + + def _soft_delete_efficacy_indicators(self): + with freezegun.freeze_time(self.FAKE_TODAY): + self.dbapi.soft_delete_efficacy_indicator( + self.efficacy_indicator1.uuid) + with freezegun.freeze_time(self.FAKE_OLD_DATE): + self.dbapi.soft_delete_efficacy_indicator( + self.efficacy_indicator2.uuid) + with freezegun.freeze_time(self.FAKE_OLDER_DATE): + self.dbapi.soft_delete_efficacy_indicator( + self.efficacy_indicator3.uuid) + + def _update_efficacy_indicators(self): + with freezegun.freeze_time(self.FAKE_TODAY): + self.dbapi.update_efficacy_indicator( + self.efficacy_indicator1.uuid, + values={"description": "New decription 1"}) + with freezegun.freeze_time(self.FAKE_OLD_DATE): + self.dbapi.update_efficacy_indicator( + self.efficacy_indicator2.uuid, + values={"description": "New decription 2"}) + with freezegun.freeze_time(self.FAKE_OLDER_DATE): + self.dbapi.update_efficacy_indicator( + self.efficacy_indicator3.uuid, + values={"description": "New decription 3"}) + + def test_get_efficacy_indicator_filter_deleted_true(self): + with freezegun.freeze_time(self.FAKE_TODAY): + self.dbapi.soft_delete_efficacy_indicator( + self.efficacy_indicator1.uuid) + + res = self.dbapi.get_efficacy_indicator_list( + self.context, filters={'deleted': True}) + + self.assertEqual([self.efficacy_indicator1['id']], [r.id for r in res]) + + def test_get_efficacy_indicator_filter_deleted_false(self): + with freezegun.freeze_time(self.FAKE_TODAY): + self.dbapi.soft_delete_efficacy_indicator( + self.efficacy_indicator1.uuid) + + res = self.dbapi.get_efficacy_indicator_list( + self.context, filters={'deleted': False}) + + self.assertEqual([self.efficacy_indicator2['id'], + self.efficacy_indicator3['id']], + [r.id for r in res]) + + def test_get_efficacy_indicator_filter_deleted_at_eq(self): + self._soft_delete_efficacy_indicators() + + res = self.dbapi.get_efficacy_indicator_list( + self.context, filters={'deleted_at__eq': self.FAKE_TODAY}) + + self.assertEqual([self.efficacy_indicator1['id']], [r.id for r in res]) + + def test_get_efficacy_indicator_filter_deleted_at_lt(self): + self._soft_delete_efficacy_indicators() + + res = self.dbapi.get_efficacy_indicator_list( + self.context, filters={'deleted_at__lt': self.FAKE_TODAY}) + + self.assertEqual( + [self.efficacy_indicator2['id'], self.efficacy_indicator3['id']], + [r.id for r in res]) + + def test_get_efficacy_indicator_filter_deleted_at_lte(self): + self._soft_delete_efficacy_indicators() + + res = self.dbapi.get_efficacy_indicator_list( + self.context, filters={'deleted_at__lte': self.FAKE_OLD_DATE}) + + self.assertEqual( + [self.efficacy_indicator2['id'], self.efficacy_indicator3['id']], + [r.id for r in res]) + + def test_get_efficacy_indicator_filter_deleted_at_gt(self): + self._soft_delete_efficacy_indicators() + + res = self.dbapi.get_efficacy_indicator_list( + self.context, filters={'deleted_at__gt': self.FAKE_OLD_DATE}) + + self.assertEqual([self.efficacy_indicator1['id']], [r.id for r in res]) + + def test_get_efficacy_indicator_filter_deleted_at_gte(self): + self._soft_delete_efficacy_indicators() + + res = self.dbapi.get_efficacy_indicator_list( + self.context, filters={'deleted_at__gte': self.FAKE_OLD_DATE}) + + self.assertEqual( + [self.efficacy_indicator1['id'], self.efficacy_indicator2['id']], + [r.id for r in res]) + + # created_at # + + def test_get_efficacy_indicator_filter_created_at_eq(self): + res = self.dbapi.get_efficacy_indicator_list( + self.context, filters={'created_at__eq': self.FAKE_TODAY}) + + self.assertEqual([self.efficacy_indicator1['id']], [r.id for r in res]) + + def test_get_efficacy_indicator_filter_created_at_lt(self): + with freezegun.freeze_time(self.FAKE_TODAY): + res = self.dbapi.get_efficacy_indicator_list( + self.context, filters={'created_at__lt': self.FAKE_TODAY}) + + self.assertEqual( + [self.efficacy_indicator2['id'], self.efficacy_indicator3['id']], + [r.id for r in res]) + + def test_get_efficacy_indicator_filter_created_at_lte(self): + res = self.dbapi.get_efficacy_indicator_list( + self.context, filters={'created_at__lte': self.FAKE_OLD_DATE}) + + self.assertEqual( + [self.efficacy_indicator2['id'], self.efficacy_indicator3['id']], + [r.id for r in res]) + + def test_get_efficacy_indicator_filter_created_at_gt(self): + res = self.dbapi.get_efficacy_indicator_list( + self.context, filters={'created_at__gt': self.FAKE_OLD_DATE}) + + self.assertEqual([self.efficacy_indicator1['id']], [r.id for r in res]) + + def test_get_efficacy_indicator_filter_created_at_gte(self): + res = self.dbapi.get_efficacy_indicator_list( + self.context, filters={'created_at__gte': self.FAKE_OLD_DATE}) + + self.assertEqual( + [self.efficacy_indicator1['id'], self.efficacy_indicator2['id']], + [r.id for r in res]) + + # updated_at # + + def test_get_efficacy_indicator_filter_updated_at_eq(self): + self._update_efficacy_indicators() + + res = self.dbapi.get_efficacy_indicator_list( + self.context, filters={'updated_at__eq': self.FAKE_TODAY}) + + self.assertEqual([self.efficacy_indicator1['id']], [r.id for r in res]) + + def test_get_efficacy_indicator_filter_updated_at_lt(self): + self._update_efficacy_indicators() + + res = self.dbapi.get_efficacy_indicator_list( + self.context, filters={'updated_at__lt': self.FAKE_TODAY}) + + self.assertEqual( + [self.efficacy_indicator2['id'], self.efficacy_indicator3['id']], + [r.id for r in res]) + + def test_get_efficacy_indicator_filter_updated_at_lte(self): + self._update_efficacy_indicators() + + res = self.dbapi.get_efficacy_indicator_list( + self.context, filters={'updated_at__lte': self.FAKE_OLD_DATE}) + + self.assertEqual( + [self.efficacy_indicator2['id'], self.efficacy_indicator3['id']], + [r.id for r in res]) + + def test_get_efficacy_indicator_filter_updated_at_gt(self): + self._update_efficacy_indicators() + + res = self.dbapi.get_efficacy_indicator_list( + self.context, filters={'updated_at__gt': self.FAKE_OLD_DATE}) + + self.assertEqual([self.efficacy_indicator1['id']], [r.id for r in res]) + + def test_get_efficacy_indicator_filter_updated_at_gte(self): + self._update_efficacy_indicators() + + res = self.dbapi.get_efficacy_indicator_list( + self.context, filters={'updated_at__gte': self.FAKE_OLD_DATE}) + + self.assertEqual( + [self.efficacy_indicator1['id'], self.efficacy_indicator2['id']], + [r.id for r in res]) + + +class DbEfficacyIndicatorTestCase(base.DbTestCase): + + def _create_test_efficacy_indicator(self, **kwargs): + efficacy_indicator_dict = utils.get_test_efficacy_indicator(**kwargs) + efficacy_indicator = self.dbapi.create_efficacy_indicator( + efficacy_indicator_dict) + return efficacy_indicator + + def _create_test_action_plan(self, **kwargs): + action_plan_dict = utils.get_test_action_plan(**kwargs) + action_plan = self.dbapi.create_action_plan(action_plan_dict) + return action_plan + + def test_get_efficacy_indicator_list(self): + uuids = [] + action_plan = self._create_test_action_plan() + for id_ in range(1, 6): + efficacy_indicator = utils.create_test_efficacy_indicator( + action_plan_id=action_plan.id, id=id_, uuid=None, + name="efficacy_indicator", description="Test Indicator ") + uuids.append(six.text_type(efficacy_indicator['uuid'])) + res = self.dbapi.get_efficacy_indicator_list(self.context) + res_uuids = [r.uuid for r in res] + self.assertEqual(uuids.sort(), res_uuids.sort()) + + def test_get_efficacy_indicator_list_with_filters(self): + audit = utils.create_test_audit(uuid=w_utils.generate_uuid()) + action_plan = self._create_test_action_plan( + id=1, + uuid=w_utils.generate_uuid(), + audit_id=audit.id, + first_efficacy_indicator_id=None, + state='RECOMMENDED') + efficacy_indicator1 = self._create_test_efficacy_indicator( + id=1, + name='indicator_1', + uuid=w_utils.generate_uuid(), + action_plan_id=1, + description='Description efficacy indicator 1', + unit='%') + efficacy_indicator2 = self._create_test_efficacy_indicator( + id=2, + name='indicator_2', + uuid=w_utils.generate_uuid(), + action_plan_id=2, + description='Description efficacy indicator 2', + unit='%') + efficacy_indicator3 = self._create_test_efficacy_indicator( + id=3, + name='indicator_3', + uuid=w_utils.generate_uuid(), + action_plan_id=1, + description='Description efficacy indicator 3', + unit='%') + res = self.dbapi.get_efficacy_indicator_list( + self.context, filters={'name': 'indicator_3'}) + self.assertEqual([efficacy_indicator3['id']], [r.id for r in res]) + + res = self.dbapi.get_efficacy_indicator_list( + self.context, filters={'unit': 'kWh'}) + self.assertEqual([], [r.id for r in res]) + + res = self.dbapi.get_efficacy_indicator_list( + self.context, + filters={'action_plan_id': 2}) + self.assertEqual([efficacy_indicator2['id']], [r.id for r in res]) + + res = self.dbapi.get_efficacy_indicator_list( + self.context, + filters={'action_plan_uuid': action_plan['uuid']}) + self.assertEqual( + [efficacy_indicator1['id'], efficacy_indicator3['id']].sort(), + [r.id for r in res].sort()) + + def test_get_efficacy_indicator_list_with_filter_by_uuid(self): + efficacy_indicator = self._create_test_efficacy_indicator() + res = self.dbapi.get_efficacy_indicator_list( + self.context, filters={'uuid': efficacy_indicator.uuid}) + + self.assertEqual(len(res), 1) + self.assertEqual(efficacy_indicator.uuid, res[0].uuid) + + def test_get_efficacy_indicator_by_id(self): + efficacy_indicator = self._create_test_efficacy_indicator() + efficacy_indicator = self.dbapi.get_efficacy_indicator_by_id( + self.context, efficacy_indicator.id) + self.assertEqual(efficacy_indicator.uuid, efficacy_indicator.uuid) + + def test_get_efficacy_indicator_by_uuid(self): + efficacy_indicator = self._create_test_efficacy_indicator() + efficacy_indicator = self.dbapi.get_efficacy_indicator_by_uuid( + self.context, efficacy_indicator.uuid) + self.assertEqual(efficacy_indicator['id'], efficacy_indicator.id) + + def test_get_efficacy_indicator_that_does_not_exist(self): + self.assertRaises( + exception.EfficacyIndicatorNotFound, + self.dbapi.get_efficacy_indicator_by_id, self.context, 1234) + + def test_update_efficacy_indicator(self): + efficacy_indicator = self._create_test_efficacy_indicator() + res = self.dbapi.update_efficacy_indicator( + efficacy_indicator.id, {'state': 'CANCELLED'}) + self.assertEqual('CANCELLED', res.state) + + def test_update_efficacy_indicator_that_does_not_exist(self): + self.assertRaises( + exception.EfficacyIndicatorNotFound, + self.dbapi.update_efficacy_indicator, 1234, {'state': ''}) + + def test_update_efficacy_indicator_uuid(self): + efficacy_indicator = self._create_test_efficacy_indicator() + self.assertRaises( + exception.Invalid, + self.dbapi.update_efficacy_indicator, efficacy_indicator.id, + {'uuid': 'hello'}) + + def test_destroy_efficacy_indicator(self): + efficacy_indicator = self._create_test_efficacy_indicator() + self.dbapi.destroy_efficacy_indicator(efficacy_indicator['id']) + self.assertRaises(exception.EfficacyIndicatorNotFound, + self.dbapi.get_efficacy_indicator_by_id, + self.context, efficacy_indicator['id']) + + def test_destroy_efficacy_indicator_by_uuid(self): + uuid = w_utils.generate_uuid() + self._create_test_efficacy_indicator(uuid=uuid) + self.assertIsNotNone(self.dbapi.get_efficacy_indicator_by_uuid( + self.context, uuid)) + self.dbapi.destroy_efficacy_indicator(uuid) + self.assertRaises( + exception.EfficacyIndicatorNotFound, + self.dbapi.get_efficacy_indicator_by_uuid, self.context, uuid) + + def test_destroy_efficacy_indicator_that_does_not_exist(self): + self.assertRaises(exception.EfficacyIndicatorNotFound, + self.dbapi.destroy_efficacy_indicator, 1234) + + def test_create_efficacy_indicator_already_exists(self): + uuid = w_utils.generate_uuid() + self._create_test_efficacy_indicator(id=1, uuid=uuid) + self.assertRaises(exception.EfficacyIndicatorAlreadyExists, + self._create_test_efficacy_indicator, + id=2, uuid=uuid) diff --git a/watcher/tests/db/test_goal.py b/watcher/tests/db/test_goal.py index 841ea64be..703a4bac2 100644 --- a/watcher/tests/db/test_goal.py +++ b/watcher/tests/db/test_goal.py @@ -273,7 +273,12 @@ class DbGoalTestCase(base.DbTestCase): self.assertEqual([goal2['uuid']], [r.uuid for r in res]) def test_get_goal_by_uuid(self): - created_goal = self._create_test_goal() + efficacy_spec = [{"unit": "%", "name": "dummy", + "schema": "Range(min=0, max=100, min_included=True, " + "max_included=True, msg=None)", + "description": "Dummy indicator"}] + created_goal = self._create_test_goal( + efficacy_specification=efficacy_spec) goal = self.dbapi.get_goal_by_uuid(self.context, created_goal['uuid']) self.assertEqual(goal.uuid, created_goal['uuid']) diff --git a/watcher/tests/db/utils.py b/watcher/tests/db/utils.py index b29fd6114..8e42e1d5b 100644 --- a/watcher/tests/db/utils.py +++ b/watcher/tests/db/utils.py @@ -189,3 +189,33 @@ def create_test_strategy(**kwargs): strategy = get_test_strategy(**kwargs) dbapi = db_api.get_instance() return dbapi.create_strategy(strategy) + + +def get_test_efficacy_indicator(**kwargs): + return { + 'id': kwargs.get('id', 1), + 'uuid': kwargs.get('uuid', '202cfcf9-811c-411a-8a35-d8351f64eb24'), + 'name': kwargs.get('name', 'test_indicator'), + 'description': kwargs.get('description', 'Test indicator'), + 'unit': kwargs.get('unit', '%'), + 'value': kwargs.get('value', 0), + 'action_plan_id': kwargs.get('action_plan_id', 1), + 'created_at': kwargs.get('created_at'), + 'updated_at': kwargs.get('updated_at'), + 'deleted_at': kwargs.get('deleted_at'), + } + + +def create_test_efficacy_indicator(**kwargs): + """Create and return a test efficacy indicator entry in DB. + + Function to be used to create test EfficacyIndicator objects in the DB. + :param kwargs: kwargs for overriding the values of the attributes + :returns: Test EfficacyIndicator DB object. + """ + efficacy_indicator = get_test_efficacy_indicator(**kwargs) + # Let DB generate ID if it isn't specified explicitly + if 'id' not in kwargs: + del efficacy_indicator['id'] + dbapi = db_api.get_instance() + return dbapi.create_efficacy_indicator(efficacy_indicator)