def test_execute_with_accumulator_missing_action_callbacks(self): action = Action(app_name='HelloWorld', action_name='Add Three', arguments=[ Argument('num1', reference='1'), Argument('num2', reference='action2'), Argument('num3', value='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} def callback_is_sent(sender, **kwargs): if isinstance(sender, Action): self.assertIn('event', kwargs) self.assertIn(kwargs['event'], (WalkoffEvent.ActionStarted, WalkoffEvent.ActionArgumentsInvalid)) if kwargs['event'] == WalkoffEvent.ActionStarted: result['started_triggered'] = True else: result['result_triggered'] = True WalkoffEvent.CommonWorkflowSignal.connect(callback_is_sent) action.execute(instance.instance, accumulator) self.assertTrue(result['started_triggered']) self.assertTrue(result['result_triggered'])
def test_execute_with_complex_args(self): action = Action(app_name='HelloWorld', action_name='Json Sample', arguments=[ Argument('json_in', value={ '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 = action.execute(instance.instance, {}) self.assertAlmostEqual(result.result, 11.0) self.assertEqual(result.status, 'Success') self.assertEqual(action._output, result)
def test_execute_sends_callbacks(self): action = Action(app_name='HelloWorld', action_name='Add Three', arguments=[ Argument('num1', value='-5.6'), Argument('num2', value='4.3'), Argument('num3', value='10.2') ]) instance = AppInstance.create(app_name='HelloWorld', device_name='device1') result = {'started_triggered': False, 'result_triggered': False} def callback_is_sent(sender, **kwargs): if isinstance(sender, Action): self.assertIn('event', kwargs) self.assertIn(kwargs['event'], (WalkoffEvent.ActionStarted, WalkoffEvent.ActionExecutionSuccess)) if kwargs['event'] == WalkoffEvent.ActionStarted: result['started_triggered'] = True else: self.assertIn('data', kwargs) data = kwargs['data'] self.assertEqual(data['status'], 'Success') self.assertAlmostEqual(data['result'], 8.9) result['result_triggered'] = True WalkoffEvent.CommonWorkflowSignal.connect(callback_is_sent) action.execute(instance.instance, {}) self.assertTrue(result['started_triggered']) self.assertTrue(result['result_triggered'])
def test_execute_no_args(self): action = Action(app_name='HelloWorld', action_name='helloWorld') instance = AppInstance.create(app_name='HelloWorld', device_name='device1') self.assertEqual(action.execute(instance.instance, {}), ActionResult({'message': 'HELLO WORLD'}, 'Success')) self.assertEqual(action._output, ActionResult({'message': 'HELLO WORLD'}, 'Success'))
def test_execute_global_action(self): action = Action(app_name='HelloWorld', action_name='global2', arguments=[Argument('arg1', value='something')]) instance = AppInstance.create(app_name='HelloWorld', device_name='') result = action.execute(instance.instance, {}) self.assertAlmostEqual(result.result, 'something') self.assertEqual(result.status, 'Success') self.assertEqual(action._output, result)
def test_get_branch_invalid_action(self): flag = Condition('HelloWorld', action_name='regMatch', arguments=[Argument('regex', value='aaa')]) branch = Branch(source_uid="1", destination_uid='next', conditions=[flag]) action = Action('HelloWorld', 'helloWorld', uid="2") action._output = ActionResult(result='bbb', status='Success') workflow = Workflow(actions=[action], branches=[branch]) self.assertIsNone(workflow.get_branch(action, {}))
def test_execute_with_accumulator_missing_action(self): action = Action(app_name='HelloWorld', action_name='Add Three', arguments=[ Argument('num1', reference='1'), Argument('num2', reference='action2'), Argument('num3', value='10.2') ]) accumulator = {'1': '-5.6', 'missing': '4.3', '3': '45'} instance = AppInstance.create(app_name='HelloWorld', device_name='device1') action.execute(instance.instance, accumulator)
def test_execute_with_args(self): action = Action(app_name='HelloWorld', action_name='Add Three', arguments=[ Argument('num1', value='-5.6'), Argument('num2', value='4.3'), Argument('num3', value='10.2') ]) instance = AppInstance.create(app_name='HelloWorld', device_name='device1') result = action.execute(instance.instance, {}) self.assertAlmostEqual(result.result, 8.9) self.assertEqual(result.status, 'Success') self.assertEqual(action._output, result)
def test_set_args_invalid_format(self): action = Action(app_name='HelloWorld', action_name='Add Three', arguments=[ Argument('num1', value='-5.6'), Argument('num2', value='4.3'), Argument('num3', value='10.2') ]) with self.assertRaises(InvalidArgument): action.set_arguments([ Argument('num1', value='-5.62'), Argument('num2', value='5'), Argument('num3', value='invalid') ])
def test_execute_with_accumulator_with_extra_actions(self): action = Action(app_name='HelloWorld', action_name='Add Three', arguments=[ Argument('num1', reference='1'), Argument('num2', reference='action2'), Argument('num3', value='10.2') ]) accumulator = {'1': '-5.6', 'action2': '4.3', '3': '45'} instance = AppInstance.create(app_name='HelloWorld', device_name='device1') result = action.execute(instance.instance, accumulator) self.assertAlmostEqual(result.result, 8.9) self.assertEqual(result.status, 'Success') self.assertEqual(action._output, result)
def create_action(self, name='', action='', app='', device='', arguments=None, risk=0): """Creates a new Action object and adds it to the Workflow's list of Actions. Args: name (str, optional): The name of the Action object. Defaults to an empty string. action (str, optional): The name of the action associated with a Action. Defaults to an empty string. app (str, optional): The name of the app associated with the Action. Defaults to an empty string. device (str, optional): The name of the device associated with the app associated with the Action. Defaults to an empty string. arguments (list[Argument]): A list of Argument objects that are parameters to the action execution. Defaults to None. risk (int, optional): The risk associated with the Action. Defaults to 0. """ arguments = arguments if arguments is not None else [] action = Action(name=name, action_name=action, app_name=app, device_id=device, arguments=arguments, risk=risk) self.actions[action.uid] = action self.branches[action.uid] = [] self._total_risk += risk logger.info('Action added to workflow {0}. Action: {1}'.format( self.name, self.actions[action.uid].read()))
def test_init_app_action_only_with_device(self): action = Action('HelloWorld', 'helloWorld', device_id='test') self.__compare_init(action, '', 'helloWorld', 'HelloWorld', device_id='test')
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_actions = [ action.read() for action in initial_workflow.actions.values() ] initial_actions[0]['position']['x'] = 0.0 initial_actions[0]['position']['y'] = 0.0 added_action = Action(name='new_id', app_name='HelloWorld', action_name='pause', arguments=[Argument("seconds", value=5)], position={ 'x': 0, 'y': 0 }).read() added_action['arguments'][0]['value'] = 'aaaa' initial_actions.append(added_action) data = {"actions": initial_actions} 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_save_workflow_new_start_action(self): initial_workflow = flask_server.running_context.controller.get_workflow( 'test', 'helloWorldWorkflow') workflow_name = initial_workflow.name initial_actions = [ action.read() for action in initial_workflow.actions.values() ] initial_actions[0]['position']['x'] = 0.0 initial_actions[0]['position']['y'] = 0.0 added_action = Action(name='new_id', app_name='HelloWorld', action_name='pause', arguments=[Argument('seconds', value=5)], position={ 'x': 0, 'y': 0 }).read() initial_actions.append(added_action) data = {"actions": initial_actions, "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 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_actions = self.deepcopy_actions_with_events() backup_branches = deepcopy(self.branches) self.actions = {} if 'name' in json_in: self.name = json_in['name'] uid = json_in['uid'] if 'uid' in json_in else self.uid try: if 'start' in json_in and json_in['start']: self.start = json_in['start'] self.actions = {} self.uid = uid for action_json in json_in['actions']: action = Action.create(action_json) self.actions[action_json['uid']] = action if "branches" in json_in: branches = [ Branch.create(cond_json) for cond_json in json_in['branches'] ] self.branches = {} for branch in branches: if branch.source_uid not in self.branches: self.branches[branch.source_uid] = [] self.branches[branch.source_uid].append(branch) except (UnknownApp, UnknownAppAction, InvalidArgument): self.restore_actions_and_events(backup_actions) self.branches = backup_branches raise
def test_branch_with_priority(self): flag = Condition('HelloWorld', action_name='regMatch', arguments=[Argument('regex', value='aaa')]) branch_one = Branch(source_uid="1", destination_uid='five', conditions=[flag], priority="5") branch_two = Branch(source_uid="1", destination_uid='one', conditions=[flag], priority="1") action = Action('HelloWorld', 'helloWorld', uid="1") action._output = ActionResult(result='aaa', status='Success') workflow = Workflow(actions=[action], branches=[branch_one, branch_two]) self.assertEqual(workflow.get_branch(action, {}), "one")
def test_init_with_arguments_with_conversion(self): action = Action('HelloWorld', 'returnPlusOne', arguments=[Argument('number', value='-5.6')]) self.__compare_init(action, '', 'returnPlusOne', 'HelloWorld', arguments=[Argument('number', value='-5.6')])
def test_init_templated(self): action = Action('HelloWorld', 'helloWorld', templated=True, raw_representation={'a': 42}) self.__compare_init(action, '', 'helloWorld', 'HelloWorld', templated=True, raw_representation={'a': 42})
def test_get_branch(self): flag = Condition('HelloWorld', action_name='regMatch', arguments=[Argument('regex', value='aaa')]) branch = Branch(source_uid="1", destination_uid="2", conditions=[flag]) action = Action('HelloWorld', 'helloWorld', uid="1") action._output = ActionResult(result='aaa', status='Success') workflow = Workflow(actions=[action], branches=[branch]) result = {'triggered': False} def validate_sent_data(sender, **kwargs): if isinstance(sender, Branch): self.assertIn('event', kwargs) self.assertEqual(kwargs['event'], WalkoffEvent.BranchTaken) result['triggered'] = True WalkoffEvent.CommonWorkflowSignal.connect(validate_sent_data) self.assertEqual(workflow.get_branch(action, {}), '2') self.assertTrue(result['triggered'])
def test_execute_multiple_triggers(self): triggers = [ Condition('HelloWorld', action_name='regMatch', arguments=[Argument('regex', value='aaa')]) ] action = Action(app_name='HelloWorld', action_name='helloWorld', triggers=triggers) instance = AppInstance.create(app_name='HelloWorld', device_name='device1') action.send_data_to_trigger({"data_in": {"data": 'a'}}) trigger_taken = {'triggered': 0} trigger_not_taken = {'triggered': 0} def callback_is_sent(sender, **kwargs): if kwargs['event'] == WalkoffEvent.TriggerActionTaken: trigger_taken['triggered'] += 1 elif kwargs['event'] == WalkoffEvent.TriggerActionNotTaken: action.send_data_to_trigger({"data_in": {"data": 'aaa'}}) trigger_not_taken['triggered'] += 1 WalkoffEvent.CommonWorkflowSignal.connect(callback_is_sent) action.execute(instance.instance, {}) self.assertEqual(trigger_taken['triggered'], 1) self.assertEqual(trigger_not_taken['triggered'], 1)
def test_execute_generates_uid(self): action = Action(app_name='HelloWorld', action_name='helloWorld') original_execution_uid = action.get_execution_uid() instance = AppInstance.create(app_name='HelloWorld', device_name='device1') action.execute(instance.instance, {}) self.assertNotEqual(action.get_execution_uid(), original_execution_uid)
def test_init_with_flags(self): triggers = [ Condition('HelloWorld', action_name='regMatch', arguments=[Argument('regex', value='(.*)')]), Condition('HelloWorld', action_name='regMatch', arguments=[Argument('regex', value='a')]) ] action = Action('HelloWorld', 'helloWorld', triggers=triggers) self.__compare_init(action, '', 'helloWorld', 'HelloWorld', triggers=['regMatch', 'regMatch'])
def test_set_args_valid(self): action = Action(app_name='HelloWorld', action_name='Add Three', arguments=[ Argument('num1', value='-5.6'), Argument('num2', value='4.3'), Argument('num3', value='10.2') ]) arguments = [ Argument('num1', value='-5.62'), Argument('num2', value='5'), Argument('num3', value='42.42') ] action.set_arguments(arguments) arguments = { 'num1': Argument('num1', value='-5.62'), 'num2': Argument('num2', value='5'), 'num3': Argument('num3', value='42.42') } self.assertEqual(len(action.arguments), len(arguments)) for arg_name, arg in action.arguments.items(): self.assertIn(arg_name, arguments) self.assertEqual(arg, arguments[arg_name])
def test_init_with_position(self): action = Action('HelloWorld', 'helloWorld', position={ 'x': 13, 'y': 42 }) self.__compare_init(action, '', 'helloWorld', 'HelloWorld', position={ 'x': 13, 'y': 42 })
def test_execute_action_which_raises_exception_sends_callbacks(self): action = Action(app_name='HelloWorld', action_name='Buggy') instance = AppInstance.create(app_name='HelloWorld', device_name='device1') result = {'started_triggered': False, 'result_triggered': False} def callback_is_sent(sender, **kwargs): if isinstance(sender, Action): self.assertIn('event', kwargs) self.assertIn(kwargs['event'], (WalkoffEvent.ActionStarted, WalkoffEvent.ActionExecutionError)) if kwargs['event'] == WalkoffEvent.ActionStarted: result['started_triggered'] = True elif kwargs['event'] == WalkoffEvent.ActionExecutionError: result['result_triggered'] = True WalkoffEvent.CommonWorkflowSignal.connect(callback_is_sent) action.execute(instance.instance, {}) self.assertTrue(result['started_triggered']) self.assertTrue(result['result_triggered'])
def test_save_workflow_invalid_app_reload_actions(self): initial_workflow = flask_server.running_context.controller.get_workflow( 'test', 'helloWorldWorkflow') workflow_name = initial_workflow.name initial_actions = [ action.read() for action in initial_workflow.actions.values() ] actions_unmod = deepcopy(initial_actions) initial_actions[0]['position']['x'] = 0.0 initial_actions[0]['position']['y'] = 0.0 added_action = Action(name='new_id', app_name='HelloWorld', action_name='pause', arguments=[Argument("seconds", value=5)], position={ 'x': 0, 'y': 0 }).read() added_action['app_name'] = 'Invalid' initial_actions.append(added_action) data = {"actions": initial_actions} 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) workflow = flask_server.running_context.controller.get_workflow( 'test', 'helloWorldWorkflow') new_actions = [action.read() for action in workflow.actions.values()] for action in actions_unmod: action.pop('position') for action in new_actions: action.pop('position') action.pop('event') self.assertListEqual(actions_unmod, new_actions)
def test_execute_with_triggers(self): triggers = [ Condition('HelloWorld', action_name='regMatch', arguments=[Argument('regex', value='aaa')]) ] action = Action(app_name='HelloWorld', action_name='helloWorld', triggers=triggers) instance = AppInstance.create(app_name='HelloWorld', device_name='device1') action.send_data_to_trigger({"data_in": {"data": 'aaa'}}) result = {'triggered': False} def callback_is_sent(sender, **kwargs): if kwargs['event'] == WalkoffEvent.TriggerActionTaken: result['triggered'] = True WalkoffEvent.CommonWorkflowSignal.connect(callback_is_sent) action.execute(instance.instance, {}) self.assertTrue(result['triggered'])
def test_init_with_name(self): action = Action('HelloWorld', 'helloWorld', name='test') self.__compare_init(action, 'test', 'helloWorld', 'HelloWorld')
def test_save_workflow(self): initial_workflow = flask_server.running_context.controller.get_workflow( 'test', 'helloWorldWorkflow') workflow_name = initial_workflow.name initial_actions = [ action.read() for action in initial_workflow.actions.values() ] initial_actions[0]['position']['x'] = 0.0 initial_actions[0]['position']['y'] = 0.0 added_action = Action('HelloWorld', 'pause', name='new_id', arguments=[Argument("seconds", value=5)], position={ 'x': 0, 'y': 0 }, uid="2").read() initial_actions.append(added_action) action_uid = "e1db14e0cc8d4179aff5f1080a2b7e91" added_branch = Branch(source_uid=action_uid, destination_uid="2").read() data = {"actions": initial_actions, "branches": [added_branch]} 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) # compare the actions in initial and final workflow self.assertEqual(len(resulting_workflow.actions.keys()), len(list(initial_actions))) for initial_action in initial_actions: self.assertIn(initial_action['uid'], resulting_workflow.actions.keys()) self.assertDictEqual( initial_action, resulting_workflow.actions[initial_action['uid']].read()) self.assertEqual(added_branch["source_uid"], resulting_workflow.branches[action_uid][0].source_uid) self.assertEqual( added_branch["destination_uid"], resulting_workflow.branches[action_uid][0].destination_uid) # assert that the file has been saved to a file workflows = [ path.splitext(workflow)[0] for workflow in os.listdir(core.config.paths.workflows_path) if workflow.endswith('.playbook') ] matching_workflows = [ workflow for workflow in workflows if workflow == 'test' ] self.assertEqual(len(matching_workflows), 1) # assert that the file loads properly after being saved flask_server.running_context.controller.workflows = {} flask_server.running_context.controller.load_playbook( os.path.join(core.config.paths.workflows_path, 'test.playbook')) loaded_workflow = flask_server.running_context.controller.get_workflow( 'test', workflow_name) # compare the actions in loaded and expected workflow self.assertEqual(len(loaded_workflow.actions.keys()), len(list(resulting_workflow.actions.keys()))) def remove_uids(action): action.uid = '' for action_name, loaded_action in loaded_workflow.actions.items(): self.assertIn(action_name, resulting_workflow.actions.keys()) remove_uids(loaded_action) remove_uids(resulting_workflow.actions[action_name]) self.assertDictEqual( loaded_action.read(), resulting_workflow.actions[action_name].read())
def test_init_with_uid(self): action = Action('HelloWorld', 'helloWorld', uid='test') self.__compare_init(action, '', 'helloWorld', 'HelloWorld', uid='test')