# Copyright (c) 2014 Dark Secret Software Inc. # Copyright (c) 2015 Rackspace # # 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. import unittest2 as unittest import mock import datetime from winchester import db as winch_db from winchester import debugging from winchester import trigger_manager class TestTriggerManager(unittest.TestCase): def setUp(self): super(TestTriggerManager, self).setUp() self.debugger = debugging.NoOpDebugger() @mock.patch.object(trigger_manager.ConfigManager, 'wrap') def test_save_event(self, mock_config_wrap): tm = trigger_manager.TriggerManager('test') tm.db = mock.MagicMock(spec=tm.db) event = dict(message_id='1234-test-5678', timestamp=datetime.datetime(2014, 8, 1, 10, 9, 8, 77777), event_type='test.thing', test_trait="foobar", other_test_trait=42) self.assertTrue(tm.save_event(event)) tm.db.create_event.assert_called_once_with( '1234-test-5678', 'test.thing', datetime.datetime(2014, 8, 1, 10, 9, 8, 77777), dict(test_trait='foobar', other_test_trait=42)) @mock.patch.object(trigger_manager.ConfigManager, 'wrap') def test_save_event_dup(self, mock_config_wrap): tm = trigger_manager.TriggerManager('test') tm.db = mock.MagicMock(spec=tm.db) tm.db.create_event.side_effect = winch_db.DuplicateError("test boom!") event = dict(message_id='1234-test-5678', timestamp=datetime.datetime(2014, 8, 1, 10, 9, 8, 77777), event_type='test.thing', test_trait="foobar", other_test_trait=42) self.assertFalse(tm.save_event(event)) tm.db.create_event.assert_called_once_with( '1234-test-5678', 'test.thing', datetime.datetime(2014, 8, 1, 10, 9, 8, 77777), dict(test_trait='foobar', other_test_trait=42)) @mock.patch('winchester.trigger_manager.EventCondenser', autospec=True) @mock.patch.object(trigger_manager.ConfigManager, 'wrap') def test_convert_notification(self, mock_config_wrap, mock_condenser): tm = trigger_manager.TriggerManager('test') tm.db = mock.MagicMock(spec=tm.db) tm.distiller = mock.MagicMock(spec=tm.distiller) test_event = "I'm a test event!" tm.distiller.to_event.return_value = True cond = mock_condenser.return_value cond.validate.return_value = True cond.get_event.return_value = test_event tm.save_event = mock.MagicMock() tm.save_event.return_value = True res = tm.convert_notification('test notification here') mock_condenser.assert_called_once_with(tm.db) cond.clear.assert_called_once_with() cond.validate.assert_called_once_with() tm.distiller.to_event.assert_called_once_with('test notification here', cond) self.assertEqual(res, test_event) @mock.patch('winchester.trigger_manager.EventCondenser', autospec=True) @mock.patch.object(trigger_manager.ConfigManager, 'wrap') def test_convert_notification_dropped(self, mock_config_wrap, mock_condenser): tm = trigger_manager.TriggerManager('test') tm.db = mock.MagicMock(spec=tm.db) tm.distiller = mock.MagicMock(spec=tm.distiller) test_event = "I'm a test event!" tm.distiller.to_event.return_value = False cond = mock_condenser.return_value cond.validate.return_value = True cond.get_event.return_value = test_event tm.save_event = mock.MagicMock() tm.save_event.return_value = True test_notif = dict(event_type='test.notification.here', message_id='4242-4242') res = tm.convert_notification(test_notif) mock_condenser.assert_called_once_with(tm.db) cond.clear.assert_called_once_with() self.assertFalse(cond.validate.called) tm.distiller.to_event.assert_called_once_with(test_notif, cond) self.assertFalse(tm.save_event.called) self.assertIsNone(res) @mock.patch('winchester.trigger_manager.EventCondenser', autospec=True) @mock.patch.object(trigger_manager.ConfigManager, 'wrap') def test_convert_notification_invalid(self, mock_config_wrap, mock_condenser): tm = trigger_manager.TriggerManager('test') tm.db = mock.MagicMock(spec=tm.db) tm.distiller = mock.MagicMock(spec=tm.distiller) test_event = "I'm a test event!" tm.distiller.to_event.return_value = True cond = mock_condenser.return_value cond.validate.return_value = False cond.get_event.return_value = test_event tm.save_event = mock.MagicMock() tm.save_event.return_value = True test_notif = dict(event_type='test.notification.here', message_id='4242-4242') res = tm.convert_notification(test_notif) mock_condenser.assert_called_once_with(tm.db) cond.clear.assert_called_once_with() cond.validate.assert_called_once_with() tm.distiller.to_event.assert_called_once_with(test_notif, cond) self.assertFalse(tm.save_event.called) self.assertIsNone(res) @mock.patch.object(trigger_manager.ConfigManager, 'wrap') def test_add_or_create_stream(self, mock_config_wrap): tm = trigger_manager.TriggerManager('test') tm.db = mock.MagicMock(spec=tm.db) tm.db.get_active_stream.return_value = 'Existing Stream' tm.current_time = mock.MagicMock() trigger_def = mock.MagicMock() dist_traits = 'some traits' event = "eventful!" ret = tm._add_or_create_stream(trigger_def, event, dist_traits) tm.db.get_active_stream.assert_called_once_with( trigger_def.name, dist_traits, tm.current_time.return_value) self.assertFalse(tm.db.create_stream.called) tm.db.add_event_stream.assert_called_once_with( tm.db.get_active_stream.return_value, event, trigger_def.expiration) self.assertEqual(ret, tm.db.get_active_stream.return_value) @mock.patch.object(trigger_manager.ConfigManager, 'wrap') def test_add_or_create_stream_create(self, mock_config_wrap): tm = trigger_manager.TriggerManager('test') tm.db = mock.MagicMock(spec=tm.db) tm.db.get_active_stream.return_value = None tm.current_time = mock.MagicMock() trigger_def = mock.MagicMock() dist_traits = 'some traits' event = "eventful!" ret = tm._add_or_create_stream(trigger_def, event, dist_traits) tm.db.get_active_stream.assert_called_once_with( trigger_def.name, dist_traits, tm.current_time.return_value) tm.db.create_stream.assert_called_once_with( trigger_def.name, event, dist_traits, trigger_def.expiration) self.assertFalse(tm.db.add_event_stream.called) self.assertEqual(ret, tm.db.create_stream.return_value) @mock.patch.object(trigger_manager.ConfigManager, 'wrap') def test_ready_to_fire(self, mock_config_wrap): tm = trigger_manager.TriggerManager('test') tm.db = mock.MagicMock(spec=tm.db) tm.current_time = mock.MagicMock() trigger_def = mock.MagicMock() test_stream = mock.MagicMock() tm._ready_to_fire(test_stream, trigger_def) trigger_def.get_fire_timestamp.assert_called_once_with( tm.current_time.return_value) tm.db.stream_ready_to_fire.assert_called_once_with( test_stream, trigger_def.get_fire_timestamp.return_value) @mock.patch.object(trigger_manager.ConfigManager, 'wrap') def test_add_notification(self, mock_config_wrap): tm = trigger_manager.TriggerManager('test') tm.convert_notification = mock.MagicMock() tm.add_event = mock.MagicMock() tm.add_notification("test notification") tm.convert_notification.assert_called_once_with("test notification") tm.add_event.assert_called_once_with( tm.convert_notification.return_value) @mock.patch.object(trigger_manager.ConfigManager, 'wrap') def test_add_notification_invalid_or_dropped(self, mock_config_wrap): tm = trigger_manager.TriggerManager('test') tm.convert_notification = mock.MagicMock() tm.add_event = mock.MagicMock() tm.convert_notification.return_value = None tm.add_notification("test notification") tm.convert_notification.assert_called_once_with("test notification") self.assertFalse(tm.add_event.called) @mock.patch.object(trigger_manager.ConfigManager, 'wrap') def test_add_event(self, mock_config_wrap): tm = trigger_manager.TriggerManager('test') tm.db = mock.MagicMock(spec=tm.db) tm.trigger_definitions = [mock.MagicMock() for n in range(3)] for d in tm.trigger_definitions: d.debugger = self.debugger m_def = tm.trigger_definitions[2] tm.trigger_definitions[0].match.return_value = None tm.trigger_definitions[1].match.return_value = None event = mock.MagicMock(name='event', spec=dict) tm.save_event = mock.MagicMock() tm._add_or_create_stream = mock.MagicMock() tm._add_or_create_stream.return_value.fire_timestamp = None tm._ready_to_fire = mock.MagicMock() m_def.should_fire.return_value = True tm.add_event(event) tm.save_event.assert_called_once_with(event) for td in tm.trigger_definitions: td.match.assert_called_once_with(event) m_def.get_distinguishing_traits.assert_called_once_with( event, m_def.match.return_value) tm._add_or_create_stream.assert_called_once_with( m_def, event, m_def.get_distinguishing_traits.return_value) tm.db.get_stream_events.assert_called_once_with( tm._add_or_create_stream.return_value) m_def.should_fire.assert_called_once_with( tm.db.get_stream_events.return_value) tm._ready_to_fire.assert_called_once_with( tm._add_or_create_stream.return_value, m_def) @mock.patch.object(trigger_manager.ConfigManager, 'wrap') def test_add_event_on_ready_stream(self, mock_config_wrap): tm = trigger_manager.TriggerManager('test') tm.db = mock.MagicMock(spec=tm.db) tm.trigger_definitions = [mock.MagicMock() for n in range(3)] m_def = tm.trigger_definitions[2] tm.trigger_definitions[0].match.return_value = None tm.trigger_definitions[1].match.return_value = None event = mock.MagicMock(name='event', spec=dict) tm.save_event = mock.MagicMock() tm._add_or_create_stream = mock.MagicMock() tm._add_or_create_stream.return_value.fire_timestamp = "Fire!" tm._ready_to_fire = mock.MagicMock() m_def.should_fire.return_value = True tm.add_event(event) tm.save_event.assert_called_once_with(event) for td in tm.trigger_definitions: td.match.assert_called_once_with(event) m_def.get_distinguishing_traits.assert_called_once_with( event, m_def.match.return_value) tm._add_or_create_stream.assert_called_once_with( m_def, event, m_def.get_distinguishing_traits.return_value) self.assertFalse(tm.db.get_stream_events.called) self.assertFalse(m_def.should_fire.called) self.assertFalse(tm._ready_to_fire.called) @mock.patch.object(trigger_manager.ConfigManager, 'wrap') def test_add_event_no_match(self, mock_config_wrap): tm = trigger_manager.TriggerManager('test') tm.db = mock.MagicMock(spec=tm.db) tm.trigger_definitions = [mock.MagicMock() for n in range(3)] tm.trigger_definitions[0].match.return_value = None tm.trigger_definitions[1].match.return_value = None tm.trigger_definitions[2].match.return_value = None event = mock.MagicMock(name='event', spec=dict) tm.save_event = mock.MagicMock() tm._add_or_create_stream = mock.MagicMock() tm._add_or_create_stream.return_value.fire_timestamp = "Fire!" tm._ready_to_fire = mock.MagicMock() tm.add_event(event) tm.save_event.assert_called_once_with(event) for td in tm.trigger_definitions: td.match.assert_called_once_with(event) for td in tm.trigger_definitions: self.assertFalse(td.get_distinguishing_traits.called) self.assertFalse(td.should_fire.called) self.assertFalse(tm._add_or_create_stream.called) self.assertFalse(tm.db.get_stream_events.called) self.assertFalse(tm._ready_to_fire.called) @mock.patch.object(trigger_manager.ConfigManager, 'wrap') def test_add__del_trigger_definition(self, mock_config_wrap): tm = trigger_manager.TriggerManager('test') tm.db = mock.MagicMock(spec=tm.db) td1 = dict( name='test_trigger1', expiration='$last + 1d', fire_pipeline='test_pipeline', fire_criteria=[dict(event_type='test.thing')], match_criteria=[dict(event_type='test.*')]) tdlist = list() tdlist.append(td1) tm.add_trigger_definition(tdlist) self.assertTrue('test_trigger1' in tm.trigger_map) tm.delete_trigger_definition('test_trigger1') self.assertFalse('test_trigger1' in tm.trigger_map)