def test_accumulated_risk_with_error(self): workflow = Workflow(name='workflow') workflow._execution_uid = 'some_uid' step1 = Step(name="step_one", app='HelloWorld', action='Buggy', risk=1) step2 = Step(name="step_two", app='HelloWorld', action='Buggy', risk=2) step3 = Step(name="step_three", app='HelloWorld', action='Buggy', risk=3.5) workflow.steps = { 'step_one': step1, 'step_two': step2, 'step_three': step3 } workflow._total_risk = 6.5 instance = AppInstance.create(app_name='HelloWorld', device_name='test_device_name') workflow._Workflow__execute_step(workflow.steps["step_one"], instance) self.assertAlmostEqual(workflow.accumulated_risk, 1.0 / 6.5) workflow._Workflow__execute_step(workflow.steps["step_two"], instance) self.assertAlmostEqual(workflow.accumulated_risk, (1.0 / 6.5) + (2.0 / 6.5)) workflow._Workflow__execute_step(workflow.steps["step_three"], instance) self.assertAlmostEqual(workflow.accumulated_risk, 1.0)
def test_execute_with_accumulator_missing_step_callbacks(self): step = Step(app='HelloWorld', action='Add Three', inputs={ 'num1': '@1', 'num2': '@step2', 'num3': '10.2' }) accumulator = {'1': '-5.6', 'missing': '4.3', '3': '45'} instance = AppInstance.create(app_name='HelloWorld', device_name='device1') result = {'started_triggered': False, 'result_triggered': False} @callbacks.data_sent.connect def callback_is_sent(sender, **kwargs): if isinstance(sender, Step): self.assertIs(sender, step) self.assertIn('callback_name', kwargs) self.assertIn(kwargs['callback_name'], ('Step Started', 'Step Input Invalid')) self.assertIn('object_type', kwargs) self.assertEqual(kwargs['object_type'], 'Step') if kwargs['callback_name'] == 'Step Started': result['started_triggered'] = True else: result['result_triggered'] = True with self.assertRaises(InvalidInput): step.execute(instance.instance, accumulator) self.assertTrue(result['started_triggered']) self.assertTrue(result['result_triggered'])
def test_execute_with_complex_inputs(self): step = Step(app='HelloWorld', action='Json Sample', inputs={ 'json_in': { 'a': '-5.6', 'b': { 'a': '4.3', 'b': 5.3 }, 'c': ['1', '2', '3'], 'd': [{ 'a': '', 'b': 3 }, { 'a': '', 'b': -1.5 }, { 'a': '', 'b': -0.5 }] } }) instance = AppInstance.create(app_name='HelloWorld', device_name='device1') result = step.execute(instance.instance, {}) self.assertAlmostEqual(result.result, 11.0) self.assertEqual(result.status, 'Success') self.assertEqual(step._output, result)
def test_execute_action_which_raises_exception(self): from tests.testapps.HelloWorld.exceptions import CustomException step = Step(app='HelloWorld', action='Buggy') instance = AppInstance.create(app_name='HelloWorld', device_name='device1') with self.assertRaises(CustomException): step.execute(instance.instance, {})
def test_execute_no_args(self): step = Step(app='HelloWorld', action='helloWorld') instance = AppInstance.create(app_name='HelloWorld', device_name='device1') self.assertEqual(step.execute(instance.instance, {}), ActionResult({'message': 'HELLO WORLD'}, 'Success')) self.assertEqual(step._output, ActionResult({'message': 'HELLO WORLD'}, 'Success'))
def test_execute_global_action(self): step = Step(app='HelloWorld', action='global2', inputs={'arg1': 'something'}) instance = AppInstance.create(app_name='HelloWorld', device_name='') result = step.execute(instance.instance, {}) self.assertAlmostEqual(result.result, 'something') self.assertEqual(result.status, 'Success') self.assertEqual(step._output, result)
def test_set_input_invalid_format(self): step = Step(app='HelloWorld', action='Add Three', inputs={ 'num1': '-5.6', 'num2': '4.3', 'num3': '10.2' }) with self.assertRaises(InvalidInput): step.set_input({'num1': '-5.62', 'num2': '5', 'num3': 'invalid'})
def test_execute_with_accumulator_missing_step(self): step = Step(app='HelloWorld', action='Add Three', inputs={ 'num1': '@1', 'num2': '@step2', 'num3': '10.2' }) accumulator = {'1': '-5.6', 'missing': '4.3', '3': '45'} instance = AppInstance.create(app_name='HelloWorld', device_name='device1') with self.assertRaises(InvalidInput): step.execute(instance.instance, accumulator)
def test_execute_with_args(self): step = Step(app='HelloWorld', action='Add Three', inputs={ 'num1': '-5.6', 'num2': '4.3', 'num3': '10.2' }) instance = AppInstance.create(app_name='HelloWorld', device_name='device1') result = step.execute(instance.instance, {}) self.assertAlmostEqual(result.result, 8.9) self.assertEqual(result.status, 'Success') self.assertEqual(step._output, result)
def test_set_input_valid(self): step = Step(app='HelloWorld', action='Add Three', inputs={ 'num1': '-5.6', 'num2': '4.3', 'num3': '10.2' }) step.set_input({'num1': '-5.62', 'num2': '5', 'num3': '42.42'}) self.assertDictEqual(step.inputs, { 'num1': -5.62, 'num2': 5., 'num3': 42.42 })
def test_execute_with_accumulator_with_conversion(self): step = Step(app='HelloWorld', action='Add Three', inputs={ 'num1': '@1', 'num2': '@step2', 'num3': '10.2' }) accumulator = {'1': '-5.6', 'step2': '4.3'} instance = AppInstance.create(app_name='HelloWorld', device_name='device1') result = step.execute(instance.instance, accumulator) self.assertAlmostEqual(result.result, 8.9) self.assertEqual(result.status, 'Success') self.assertEqual(step._output, result)
def test_save_workflow_new_start_step(self): initial_workflow = flask_server.running_context.controller.get_workflow( 'test', 'helloWorldWorkflow') workflow_name = initial_workflow.name initial_steps = [ step.read() for step in initial_workflow.steps.values() ] initial_steps[0]['position']['x'] = 0.0 initial_steps[0]['position']['y'] = 0.0 added_step = Step(name='new_id', app='HelloWorld', action='pause', inputs={ 'seconds': 5 }, position={ 'x': 0, 'y': 0 }).read() initial_steps.append(added_step) data = {"steps": initial_steps, "start": "new_start"} self.post_with_status_check( '/api/playbooks/test/workflows/{0}/save'.format(workflow_name), data=json.dumps(data), headers=self.headers, content_type='application/json') resulting_workflow = flask_server.running_context.controller.get_workflow( 'test', workflow_name) self.assertEqual(resulting_workflow.start, "new_start")
def test_save_workflow_invalid_input_format(self): initial_workflow = flask_server.running_context.controller.get_workflow( 'test', 'helloWorldWorkflow') workflow_name = initial_workflow.name initial_steps = [ step.read() for step in initial_workflow.steps.values() ] initial_steps[0]['position']['x'] = 0.0 initial_steps[0]['position']['y'] = 0.0 added_step = Step(name='new_id', app='HelloWorld', action='pause', inputs={ 'seconds': 5 }, position={ 'x': 0, 'y': 0 }).read() added_step['inputs'][0]['value'] = 'aaaa' initial_steps.append(added_step) data = {"steps": initial_steps} self.post_with_status_check( '/api/playbooks/test/workflows/{0}/save'.format(workflow_name), data=json.dumps(data), headers=self.headers, content_type='application/json', status_code=INVALID_INPUT_ERROR)
def test_init_with_risk(self): step = Step('HelloWorld', 'helloWorld', risk=42) self.__compare_init(step, '', 'helloWorld', 'HelloWorld', '', {}, risk=42)
def test_init_with_uid(self): step = Step('HelloWorld', 'helloWorld', uid='test') self.__compare_init(step, '', 'helloWorld', 'HelloWorld', '', {}, uid='test')
def test_init_with_next_steps(self): next_steps = [NextStep(name=i) for i in range(3)] step = Step('HelloWorld', 'helloWorld', next_steps=next_steps) self.__compare_init(step, '', 'helloWorld', 'HelloWorld', '', {}, next_steps=list(range(3)))
def test_init_with_position(self): step = Step('HelloWorld', 'helloWorld', position={'x': 13, 'y': 42}) self.__compare_init(step, '', 'helloWorld', 'HelloWorld', '', {}, position={ 'x': 13, 'y': 42 })
def test_init_with_flags(self): triggers = [ Flag(action='regMatch', args={'regex': '(.*)'}), Flag(action='regMatch', args={'regex': 'a'}) ] step = Step('HelloWorld', 'helloWorld', triggers=triggers) self.__compare_init(step, '', 'helloWorld', 'HelloWorld', '', {}, triggers=['regMatch', 'regMatch'])
def test_init_templated(self): step = Step('HelloWorld', 'helloWorld', templated=True, raw_representation={'a': 42}) self.__compare_init(step, '', 'helloWorld', 'HelloWorld', '', {}, templated=True, raw_representation={'a': 42})
def test_execute_generates_uid(self): step = Step(app='HelloWorld', action='helloWorld') original_execution_uid = step.get_execution_uid() instance = AppInstance.create(app_name='HelloWorld', device_name='device1') step.execute(instance.instance, {}) self.assertNotEqual(step.get_execution_uid(), original_execution_uid)
def test_get_next_step(self): flag = Flag(action='regMatch', args={'regex': 'aaa'}) next_step = NextStep(name='next', flags=[flag], status='Success') step = Step('HelloWorld', 'helloWorld', next_steps=[next_step]) step._output = ActionResult(result='aaa', status='Success') result = {'triggered': False} @callbacks.data_sent.connect def validate_sent_data(sender, **kwargs): if isinstance(sender, Step): self.assertIs(sender, step) self.assertIn('callback_name', kwargs) self.assertEqual(kwargs['callback_name'], 'Conditionals Executed') self.assertIn('object_type', kwargs) self.assertEqual(kwargs['object_type'], 'Step') result['triggered'] = True self.assertEqual(step.get_next_step({}), 'next') self.assertEqual(step._next_up, 'next') self.assertTrue(result['triggered'])
def test_execute_action_which_raises_exception_sends_callbacks(self): from tests.testapps.HelloWorld.exceptions import CustomException step = Step(app='HelloWorld', action='Buggy') instance = AppInstance.create(app_name='HelloWorld', device_name='device1') result = {'started_triggered': False} @callbacks.data_sent.connect def callback_is_sent(sender, **kwargs): if isinstance(sender, Step): self.assertIs(sender, step) self.assertIn('callback_name', kwargs) self.assertEqual(kwargs['callback_name'], 'Step Started') self.assertIn('object_type', kwargs) self.assertEqual(kwargs['object_type'], 'Step') result['started_triggered'] = True with self.assertRaises(CustomException): step.execute(instance.instance, {}) self.assertTrue(result['started_triggered'])
def test_init_with_widgets(self): widget_tuples = [('aaa', 'bbb'), ('ccc', 'ddd'), ('eee', 'fff')] widgets = [{ 'app': widget[0], 'name': widget[1] } for widget in widget_tuples] step = Step('HelloWorld', 'helloWorld', widgets=widgets) self.__compare_init(step, '', 'helloWorld', 'HelloWorld', '', {}, widgets=widget_tuples)
def test_execute_event(self): step = Step(app='HelloWorld', action='Sample Event', inputs={'arg1': 1}) instance = AppInstance.create(app_name='HelloWorld', device_name='device1') import time from tests.testapps.HelloWorld.events import event1 import threading def sender(): time.sleep(0.1) event1.trigger(3) thread = threading.Thread(target=sender) start = time.time() thread.start() result = step.execute(instance.instance, {}) end = time.time() thread.join() self.assertEqual(result, ActionResult(4, 'Success')) self.assertGreater((end - start), 0.1)
def test_execute_sends_callbacks(self): step = Step(app='HelloWorld', action='Add Three', inputs={ 'num1': '-5.6', 'num2': '4.3', 'num3': '10.2' }) instance = AppInstance.create(app_name='HelloWorld', device_name='device1') result = {'started_triggered': False, 'result_triggered': False} @callbacks.data_sent.connect def callback_is_sent(sender, **kwargs): if isinstance(sender, Step): self.assertIs(sender, step) self.assertIn('callback_name', kwargs) self.assertIn(kwargs['callback_name'], ('Step Started', 'Function Execution Success')) self.assertIn('object_type', kwargs) self.assertEqual(kwargs['object_type'], 'Step') if kwargs['callback_name'] == 'Step Started': result['started_triggered'] = True else: self.assertIn('data', kwargs) data = json.loads(kwargs['data']) self.assertIn('result', data) data = data['result'] self.assertEqual(data['status'], 'Success') self.assertAlmostEqual(data['result'], 8.9) result['result_triggered'] = True step.execute(instance.instance, {}) self.assertTrue(result['started_triggered']) self.assertTrue(result['result_triggered'])
def create_step(self, name='', action='', app='', device='', arg_input=None, next_steps=None, risk=0): """Creates a new Step object and adds it to the Workflow's list of Steps. Args: name (str, optional): The name of the Step object. Defaults to an empty string. action (str, optional): The name of the action associated with a Step. Defaults to an empty string. app (str, optional): The name of the app associated with the Step. Defaults to an empty string. device (str, optional): The name of the device associated with the app associated with the Step. Defaults to an empty string. arg_input (dict, optional): A dictionary of Argument objects that are input to the step execution. Defaults to None. next_steps (list[NextStep], optional): A list of NextStep objects for the Step object. Defaults to None. risk (int, optional): The risk associated with the Step. Defaults to 0. """ arg_input = arg_input if arg_input is not None else {} next_steps = next_steps if next_steps is not None else [] self.steps[name] = Step(name=name, action=action, app=app, device=device, inputs=arg_input, next_steps=next_steps, risk=risk) self._total_risk += risk logger.info('Step added to workflow {0}. Step: {1}'.format(self.name, self.steps[name].read()))
def test_execute_with_triggers(self): triggers = [Flag(action='regMatch', args={'regex': 'aaa'})] step = Step(app='HelloWorld', action='helloWorld', triggers=triggers) instance = AppInstance.create(app_name='HelloWorld', device_name='device1') step.send_data_to_trigger({"data_in": {"data": 'aaa'}}) result = {'triggered': False} @callbacks.data_sent.connect def callback_is_sent(sender, **kwargs): if kwargs['callback_name'] == "Trigger Step Taken": result['triggered'] = True step.execute(instance.instance, {}) self.assertTrue(result['triggered'])
def update_from_json(self, json_in): """Reconstruct a Workflow object based on JSON data. Args: json_in (JSON dict): The JSON data to be parsed and reconstructed into a Workflow object. """ # backup_steps = deepcopy(self.steps) backup_steps = self.strip_async_result(with_deepcopy=True) self.steps = {} if 'name' in json_in: self.name = json_in['name'] uid = json_in['uid'] if 'uid' in json_in else uuid.uuid4().hex try: if 'start' in json_in and json_in['start']: self.start = json_in['start'] self.steps = {} self.uid = uid for step_json in json_in['steps']: step = Step.create(step_json) self.steps[step_json['name']] = step except (UnknownApp, UnknownAppAction, InvalidInput): self.reload_async_result(backup_steps, with_deepcopy=True) raise
def test_execute_multiple_triggers(self): triggers = [Flag(action='regMatch', args={'regex': 'aaa'})] step = Step(app='HelloWorld', action='helloWorld', triggers=triggers) instance = AppInstance.create(app_name='HelloWorld', device_name='device1') step.send_data_to_trigger({"data_in": {"data": 'a'}}) trigger_taken = {'triggered': 0} trigger_not_taken = {'triggered': 0} @callbacks.data_sent.connect def callback_is_sent(sender, **kwargs): if kwargs['callback_name'] == "Trigger Step Taken": trigger_taken['triggered'] += 1 elif kwargs['callback_name'] == "Trigger Step Not Taken": step.send_data_to_trigger({"data_in": {"data": 'aaa'}}) trigger_not_taken['triggered'] += 1 step.execute(instance.instance, {}) self.assertEqual(trigger_taken['triggered'], 1) self.assertEqual(trigger_not_taken['triggered'], 1)
def test_init_with_name(self): step = Step('HelloWorld', 'helloWorld', name='test') self.__compare_init(step, 'test', 'helloWorld', 'HelloWorld', '', {})