def test_handling_message_for_faulthandler_by_faulthandler(self): """Test that message for a handler is handled by the handler.""" self.seq.state = 'aborting' self.seq.faults.state = 'active' self.seq.context.throw(code='TestError', message='Some error message') with patch('bureaucrat.flowexpression.Message') as MockMsg: msg = Message(name='start', target='fake-id_0_faults_2', origin='fake-id_0_faults') result = self.seq.handle_message(self.ch, msg) self.assertEqual(result, 'consumed') MockMsg.assert_called_once_with(name='start', target='fake-id_0_faults_2_0', origin='fake-id_0_faults_2') self.assertEqual(self.seq.state, 'aborting') msg = Message(name='start', target='fake-id_0_faults_2_0', origin='fake-id_0_faults_2') result = self.seq.handle_message(self.ch, msg) self.assertEqual(result, 'consumed') self.ch.elaborate.assert_called_once_with( "test2", "fake-id_0_faults_2_0", { 'status': 'done', 'inst:fault': { 'message': 'Some error message', 'code': 'TestError' } }) self.assertEqual(self.seq.state, 'aborting')
def test_handle_message_canceled_all_final(self): """Test FlowExpression.handle_message() with last canceled message. When a complex activity in the 'aborting' state recieves a 'canceled' or 'aborted' message, all its children are in a final state and there are no fault handlers registered for the fault signal the activity is supposed to send a 'fault' message to its parent. """ msg_canceled = Message(name='canceled', target='fake-id', origin='fake-id_3') self.root.state = 'aborting' self.root.children[0].state = 'aborted' self.root.children[1].state = 'canceled' self.root.children[2].state = 'aborted' self.root.children[3].state = 'canceled' self.root.context.throw() with patch('bureaucrat.flowexpression.Message') as MockMsg: newmsg = Message(name='fault', target='', origin='fake-id') MockMsg.return_value = newmsg result = self.root.handle_message(self.ch, msg_canceled) self.assertEqual(result, 'consumed') MockMsg.assert_called_once_with(name='fault', target='', origin='fake-id', payload={ 'code': 'GenericError', 'message': '' }) self.ch.send.assert_called_once_with(newmsg) self.assertEqual(self.root.state, 'aborted')
def test_starting_particular_handler(self): """Test correct fault handler is triggered.""" msg = Message(name='start', target='fake-id_0_faults', origin='fake-id_0') self.seq.state = 'aborting' self.seq.children[0].state = 'aborted' self.seq.children[1].state = 'canceled' with patch('bureaucrat.flowexpression.Message') as MockMsg: # last handler should be triggered for 'TestError' self.seq.context.throw(code='TestError', message='Some error message') newmsg = Message(name='start', target='fake-id_0_faults_2', origin='fake-id_0_faults') MockMsg.return_value = newmsg result = self.seq.handle_message(self.ch, msg) self.assertEqual(result, 'consumed') MockMsg.assert_called_once_with(name='start', target='fake-id_0_faults_2', origin='fake-id_0_faults') self.ch.send.assert_called_once_with(newmsg) self.assertEqual(self.seq.state, 'aborting') self.assertEqual(self.seq.faults.state, 'active')
def _was_sequence_completed(self, channel, msg, guard=lambda: True, compensate=lambda: None): """Check if all children in sequence were completed.""" res = '' if self._is_complete_message(msg): for index, child in zip(range(0, len(self.children)), self.children): if child.id == msg.origin: if (index + 1) < len(self.children): channel.send( Message(name='start', origin=self.id, target="%s_%d" % (self.id, index + 1))) else: if guard(): self.state = 'completed' channel.send( Message(name='completed', origin=self.id, target=self.parent_id)) else: compensate() res = 'consumed' break return res
def _was_activated(self, channel, msg, guard=lambda: True): """Check if it's activation message.""" if self._is_start_message(msg): if not guard(): LOG.debug("Conditions for %r don't hold", self) self.state = 'completed' channel.send( Message(name='completed', origin=self.id, target=self.parent_id)) return 'consumed' if len(self.children) > 0: self.state = 'active' channel.send( Message(name='start', origin=self.id, target=self.children[0].id)) else: self.state = 'completed' channel.send( Message(name='completed', origin=self.id, target=self.parent_id)) return 'consumed' else: return ''
def test_handle_message_completed_from_non_last_child(self): """Test Foreach.handle_message() with complete msg from non last child. """ msg = Message(name='completed', target='fake-id_0', origin='fake-id_0_0') self.root.state = 'active' self.foreach.state = 'active' self.foreach.context._props["inst:iteration"] = 1 self.foreach.context._props["inst:selection"] = ["one", "two"] with patch('bureaucrat.flowexpression.Message') as MockMessage: newmsg = Message(name='start', target='fake-id_0_1', origin='fake-id_0') MockMessage.return_value = newmsg result = self.root.handle_message(self.ch, msg) self.assertEqual(result, 'consumed') self.assertEqual(self.foreach.state, 'active') MockMessage.assert_called_once_with(name='start', target='fake-id_0_1', origin='fake-id_0') self.ch.send.assert_called_once_with(newmsg) self.assertEqual(self.foreach.context.get('inst:iteration'), 1) self.assertEqual(self.root.context.get("prop2"), {"subkey": ["one", "two"]}) self.assertEqual(self.foreach.context.get("prop2"), 2)
def test_handle_message_canceled_complex_in_canceling_state_2(self): """Test canceling complex activity when last 'canceled' msg received. When a complex activity in the 'canceling' state recieves a 'canceled' or 'aborted' message, all its children are in a final state the activity is supposed to change its state to 'canceled' and to send a 'canceled' message to its parent. """ msg_canceled = Message(name='canceled', target='fake-id', origin='fake-id_3') self.root.state = 'canceling' self.root.children[0].state = 'canceled' self.root.children[1].state = 'canceled' self.root.children[2].state = 'canceled' self.root.children[3].state = 'canceled' with patch('bureaucrat.flowexpression.Message') as MockMsg: newmsg = Message(name='canceled', target='', origin='fake-id') MockMsg.return_value = newmsg result = self.root.handle_message(self.ch, msg_canceled) self.assertEqual(result, 'consumed') MockMsg.assert_called_once_with(name='canceled', target='', origin='fake-id') self.ch.send.assert_called_once_with(newmsg) self.assertEqual(self.root.state, 'canceled')
def test_handle_message_terminate_complex_in_active_state(self): """Test active complex FlowExpression.handle_message() with terminate msg. When a complex activity in 'active' state receives 'terminate' message it's supposed to 1. send 'terminate' message to all its children which are not in a final state, 2. transition to 'aborting' state. """ msg = Message(name='terminate', target='fake-id_2', origin='fake-id') self.root.children[2].state = 'active' with patch('bureaucrat.flowexpression.Message') as MockMsg: msg0 = Message(name='terminate', target='fake-id_2_0', origin='fake-id_2') msg1 = Message(name='terminate', target='fake-id_2_1', origin='fake-id_2') MockMsg.side_effect = [msg0, msg1] result = self.root.children[2].handle_message(self.ch, msg) self.assertEqual(self.root.children[2].state, 'aborting') self.assertEqual(result, 'consumed') expected = [ call(name='terminate', target='fake-id_2_0', origin='fake-id_2'), call(name='terminate', target='fake-id_2_1', origin='fake-id_2'), ] self.assertEqual(MockMsg.call_args_list, expected) expected = [call(msg0), call(msg1)] self.assertEqual(self.ch.send.call_args_list, expected)
def handle_message(self, channel, msg): """Handle message.""" res = '' if self.state == 'active' and msg.name == 'fault' and \ msg.target == self.id: self.state = 'aborted' channel.send( Message(name='fault', target=self.parent_id, origin=self.id, payload=msg.payload)) res = 'consumed' if res: return res # TODO: check why there is double 'start' message res = '' if self._is_start_message(msg): code = self.context.get('inst:fault')["code"] default = None for child in self.children: if code in child.codes: channel.send( Message(name='start', target=child.id, origin=self.id)) self.state = 'active' res = "consumed" break if not child.codes: default = child else: if not default is None: channel.send( Message(name='start', target=default.id, origin=self.id)) self.state = 'active' res = "consumed" else: pass # TODO: throw fault again for the last time if res: return res if self._is_complete_message(msg): channel.send( Message(name='completed', target=self.parent_id, origin=self.id)) self.state = 'completed' res = 'consumed' if res: return res res = self._was_consumed_by_child(channel, msg) if res: return res return "ignored"
def handle_message(self, channel, msg): """Handle message.""" res = FlowExpression.handle_message(self, channel, msg) if res: return res result = 'ignore' if self._is_start_message(msg): LOG.debug("Activate participant %s", self.participant) self.state = 'active' context = self.context.as_dictionary() context[ "status"] = "done" # FIXME: consider setting status in task queue channel.elaborate(self.participant, self.id, context) result = 'consumed' elif self.state == 'active' and msg.name == 'response': LOG.debug("Got response for action %s. Payload: %s", self.id, msg.payload) if 'status' in msg.payload: status = msg.payload["status"] if status == 'critical': LOG.warning("Got critical error: %s. Aborting...", msg.payload["error"]) self.state = 'aborting' payload = { "code": "ActionError", "error": msg.payload["error"] } channel.send( Message(name='fault', origin=self.id, target=self.parent_id, payload=payload)) elif status == 'error': LOG.error("Got recoverable error: %s. Suspending...", msg.payload["error"]) elif status == 'done': del msg.payload["status"] self.context.update(msg.payload) self.state = 'completed' # reply to parent that the child is done channel.send( Message(name='completed', origin=self.id, target=self.parent_id)) else: LOG.error("Unknown status: %s", status) else: LOG.error("No status received") result = 'consumed' else: LOG.debug("%r ignores %r", self, msg) return result
def test_handle_message_start(self): """Test Process.handle_message() with start message.""" msg = Message(name='start', target='fake-id', origin='') newmsg = Message(name='start', target='fake-id_0', origin='fake-id') self.fexpr.state = 'ready' with patch('bureaucrat.flowexpression.Message') as MockMessage: MockMessage.return_value = newmsg result = self.fexpr.handle_message(self.ch, msg) self.assertEqual(result, 'consumed') self.assertEqual(self.fexpr.state, 'active') MockMessage.assert_called_once_with(name='start', target='fake-id_0', origin='fake-id') self.ch.send.assert_called_once_with(newmsg)
def test_handle_message_terminate_simple_in_aborting_state(self): """Test terminating a simple activity in aborting state.""" msg = Message(name='terminate', target='fake-id_0', origin='fake-id') self.root.children[0].state = 'aborting' with patch('bureaucrat.flowexpression.Message') as MockMsg: newmsg = Message(name='aborted', target='fake-id', origin='fake-id_0') MockMsg.return_value = newmsg result = self.root.children[0].handle_message(self.ch, msg) self.assertEqual(result, 'consumed') MockMsg.assert_called_once_with(name='aborted', target='fake-id', origin='fake-id_0') self.ch.send.assert_called_once_with(newmsg) self.assertEqual(self.root.children[0].state, 'aborted')
def test_handle_message_completed2(self): """Test Process.handle_message() with completed msg from last child.""" msg = Message(name='completed', target='fake-id', origin='fake-id_1') self.fexpr.state = 'active' newmsg = Message(name='completed', target='', origin='fake-id') with patch('bureaucrat.flowexpression.Message') as MockMessage: MockMessage.return_value = newmsg result = self.fexpr.handle_message(self.ch, msg) self.assertEqual(result, 'consumed') self.assertEqual(self.fexpr.state, 'completed') MockMessage.assert_called_once_with(name='completed', target='', origin='fake-id') self.ch.send.assert_called_once_with(newmsg)
def test_handle_message_start(self): """Test handling message 'start'.""" msg = Message(name='start', target='fake-id_0', origin='fake-id') with patch('bureaucrat.flowexpression.Message') as MockMsg: newmsg = Message(name='completed', target='fake-id', origin='fake-id_0') MockMsg.return_value = newmsg result = self.root.handle_message(self.ch, msg) self.assertEqual(result, 'consumed') MockMsg.assert_called_once_with(name='completed', target='fake-id', origin='fake-id_0') self.ch.send.assert_called_once_with(newmsg) self.assertEqual(self.root.children[0].state, "completed") self.assertEqual(self.root.context.get("prop1"), 7)
def handle_message(self, channel, method, header, body): """Handle message.""" LOG.debug("Method: %r", method) LOG.debug("Header: %r", header) LOG.debug("Handling message with Body: %r", body) try: msg = Message.loads(body) except (ValueError, KeyError) as err: # Report error and accept message LOG.error("%s", err) channel.basic_ack(method.delivery_tag) return if msg.target != '': wflow = Workflow.load(msg.target_pid) wflow.process.handle_message(ChannelWrapper(channel), msg) wflow.save() if msg.origin == msg.origin_pid and msg.name == 'completed': LOG.debug("The process %s has finished", msg.origin) Workflow.load(msg.origin).delete() elif msg.origin == msg.origin_pid and msg.name == 'fault': LOG.error("The process %s has faulted with %s. " + \ "The state is preserved.", msg.origin, msg.payload) channel.basic_ack(method.delivery_tag)
def test_handle_message_completed_state(self): """Test While.handle_message() when While is completed.""" msg = Message(name='start', target='fake-id_0', origin='fake-id') self.fexpr.state = 'completed' result = self.fexpr.handle_message(self.ch, msg) self.assertEqual(result, 'ignored')
def handle_message(self, channel, msg): """Handle msg.""" res = FlowExpression.handle_message(self, channel, msg) if res: return res result = 'ignore' if self._is_start_message(msg): LOG.debug("Wait for %s", self.duration) self.state = 'active' instant = int(time.time()) + self.duration channel.schedule_event(target=self.id, code="timeout", instant=instant) result = 'consumed' elif self.state == 'active' and msg.name == 'timeout': LOG.debug("Time is out for %s", self.id) self.state = 'completed' # reply to parent that the child is done channel.send( Message(name='completed', origin=self.id, target=self.parent_id)) result = 'consumed' else: LOG.debug("%r ignores %r", self, msg) return result
def handle_message(self, channel, msg): """Handle message.""" res = FlowExpression.handle_message(self, channel, msg) if res: return res result = 'ignore' if self._is_start_message(msg): LOG.debug("Wait for %s", self.event) self.state = 'active' _subscribe(event=self.event, target=self.id) result = 'consumed' elif self.state == 'active' and msg.name == 'triggered': LOG.debug("Event '%s' triggered for %s", self.event, self.id) if not self.evaluate(): LOG.debug("Conditions for %r don't hold", self) return 'ignored' self.state = 'completed' # reply to parent that the child is done channel.send( Message(name='completed', origin=self.id, target=self.parent_id)) result = 'consumed' else: LOG.debug("%r ignores %r", self, msg) return result
def test_handle_message_wrong_target(self): """Test While.handle_message() when message targeted not to it.""" msg = Message(name='start', target='fake-id_10', origin='fake-id') self.fexpr.state = 'active' result = self.fexpr.handle_message(self.ch, msg) self.assertEqual(result, 'ignored')
def test_handle_message_start(self): """Test Await.handle_message() with 'start' message.""" confparser = ConfigParser() confparser.add_section('bureaucrat') confparser.set('bureaucrat', 'storage_dir', STORAGE_DIR) Configs.instance(confparser) subscriptions = [{"target": "some-id"}] Storage.instance().save("subscriptions", "test_event", json.dumps(subscriptions)) msg = Message(name='start', target='fake-id_0', origin='fake-id') self.fexpr.state = 'ready' result = self.fexpr.handle_message(self.ch, msg) self.assertEqual(result, 'consumed') self.assertEqual(self.fexpr.state, 'active') filename = os.path.join(STORAGE_DIR, "subscriptions/test_event") with open(filename) as fhdl: subscriptions.append({'target': 'fake-id_0'}) self.assertEqual(json.load(fhdl), subscriptions) Configs._instance = None Storage._instance = None os.unlink(filename) os.rmdir(os.path.join(STORAGE_DIR, "subscriptions")) os.removedirs(STORAGE_DIR)
def test_handle_message_completed_state(self): """Test All.handle_message() when While is completed.""" self.fexpr.state = 'completed' result = self.fexpr.handle_message( self.ch, Message(name='fake', target='fake-id_0', origin='fake-id')) self.assertTrue(result == 'ignored')
def test_handle_message_start(self): """Test All.handle_message() with start event.""" msg = Message(name='start', target='fake-id_0', origin='fake-id') self.fexpr.state = 'ready' msg1 = Message(name='start', target='fake-id_0_0', origin='fake-id_0') msg2 = Message(name='start', target='fake-id_0_1', origin='fake-id_0') with patch('bureaucrat.flowexpression.Message') as MockMessage: MockMessage.side_effect = [msg1, msg2] result = self.fexpr.handle_message(self.ch, msg) self.assertTrue(result == 'consumed') self.assertTrue(self.fexpr.state == 'active') expected = [ call(msg1), call(msg2), ] self.assertEqual(self.ch.send.call_args_list, expected)
def test_handle_message_timeout(self): """Test Await.handle_message() with 'triggered' message.""" msg = Message(name='triggered', target='fake-id_0', origin='fake-id_0') newmsg = Message(name='completed', target='fake-id', origin='fake-id_0') self.fexpr.state = 'active' with patch('bureaucrat.flowexpression.Message') as MockMessage: MockMessage.return_value = newmsg result = self.fexpr.handle_message(self.ch, msg) self.assertEqual(result, 'consumed') self.assertEqual(self.fexpr.state, 'completed') MockMessage.assert_called_once_with(name='completed', target='fake-id', origin='fake-id_0') self.ch.send.assert_called_once_with(newmsg)
def test_handle_message_completed_state(self): """Test Foreach.handle_message() when Foreach is completed.""" msg = Message(name='start', target='fake-id_0', origin='fake-id') self.root.state = 'active' self.foreach.state = 'completed' result = self.root.handle_message(self.ch, msg) self.assertEqual(result, 'ignored')
def test_handle_message_wrong_target(self): """Test All.handle_message() when message targeted not to it.""" self.fexpr.state = 'active' result = self.fexpr.handle_message( self.ch, Message(name='start', target='fake-id_1', origin='fake-id')) self.assertTrue(result == 'ignored')
def test_handle_message_start_with_empty_select(self): """Test Foreach.handle_message() with start msg and empty select.""" msg = Message(name='start', target='fake-id_0', origin='fake-id') self.root.state = 'active' self.root.context.set('prop2', {"subkey": []}) with patch('bureaucrat.flowexpression.Message') as MockMessage: newmsg = Message(name='completed', target='fake-id', origin='fake-id_0') MockMessage.return_value = newmsg result = self.root.handle_message(self.ch, msg) self.assertEqual(result, 'consumed') self.assertEqual(self.foreach.state, 'completed') MockMessage.assert_called_once_with(name='completed', target='fake-id', origin='fake-id_0') self.ch.send.assert_called_once_with(newmsg)
def test_handle_message_terminate_simple_in_ready_state(self): """Test ready simple FlowExpression.handle_message() with terminate msg. When a simple activity in 'ready' state receives 'terminate' message it's supposed to get canceled and to notify its parent about that. """ msg = Message(name='terminate', target='fake-id_0', origin='fake-id') with patch('bureaucrat.flowexpression.Message') as MockMsg: newmsg = Message(name='canceled', target='fake-id', origin='fake-id_0') MockMsg.return_value = newmsg result = self.root.children[0].handle_message(self.ch, msg) self.assertEqual(result, 'consumed') MockMsg.assert_called_once_with(name='canceled', target='fake-id', origin='fake-id_0') self.ch.send.assert_called_once_with(newmsg) self.assertEqual(self.root.children[0].state, 'canceled')
def test_handle_message_response(self): """Test While.handle_message() with response message.""" msg = Message(name='response', target='fake-id_0_0', origin='fake-id_0_0') self.fexpr.state = 'active' self.fexpr.children[0].state = 'active' result = self.fexpr.handle_message(self.ch, msg) self.assertEqual(result, 'consumed')
def test_handle_message_start(self): """Test Foreach.handle_message() with start msg.""" msg = Message(name='start', target='fake-id_0', origin='fake-id') self.root.state = 'active' with patch('bureaucrat.flowexpression.Message') as MockMessage: newmsg = Message(name='start', target='fake-id_0_0', origin='fake-id_0') MockMessage.return_value = newmsg result = self.root.handle_message(self.ch, msg) self.assertEqual(result, 'consumed') self.assertEqual(self.foreach.state, 'active') MockMessage.assert_called_once_with(name='start', target='fake-id_0_0', origin='fake-id_0') self.ch.send.assert_called_once_with(newmsg) self.assertEqual(self.foreach.context.get('inst:iteration'), 1) self.assertEqual(self.foreach.context.get('inst:current'), 'one')
def test_handle_message_fault_received(self): """Test FlowExpression.handle_message() with fault message. The 'fault' message is received from the first child which is in the 'active' state. The root activity is supposed to send 'terminate' message to all its children which are not in a final state. Also the activity is supposed to raise the 'inst:fault' singal in its context. """ msg = Message(name='fault', target='fake-id', origin='fake-id_0', payload={'code': 'GenericError'}) self.root.state = 'active' self.root.children[0].state = 'aborted' with patch('bureaucrat.flowexpression.Message') as MockMsg: msg1 = Message(name='terminate', target='fake-id_1', origin='fake-id') msg2 = Message(name='terminate', target='fake-id_2', origin='fake-id') msg3 = Message(name='terminate', target='fake-id_3', origin='fake-id') MockMsg.side_effect = [msg1, msg2, msg3] result = self.root.handle_message(self.ch, msg) self.assertEqual(self.root.state, 'aborting') self.assertEqual(result, 'consumed') expected = [ call(name='terminate', target='fake-id_1', origin='fake-id'), call(name='terminate', target='fake-id_2', origin='fake-id'), call(name='terminate', target='fake-id_3', origin='fake-id') ] self.assertEqual(MockMsg.call_args_list, expected) expected = [call(msg1), call(msg2), call(msg3)] self.assertEqual(self.ch.send.call_args_list, expected) self.assertEqual(self.root.context.get('inst:fault'), { 'code': 'GenericError', 'message': '' })