def test_shell_command_action_basic(self):
        models = self.fixtures_loader.load_models(
            fixtures_pack='generic', fixtures_dict={'actions': ['local.yaml']})
        action_db = models['actions']['local.yaml']

        runner = self._get_runner(action_db, cmd='echo 10')
        runner.pre_run()
        status, result, _ = runner.run({})
        runner.post_run(status, result)

        self.assertEquals(status, action_constants.LIVEACTION_STATUS_SUCCEEDED)
        self.assertEquals(result['stdout'], 10)

        # End result should be the same when streaming is enabled
        cfg.CONF.set_override(name='stream_output',
                              group='actionrunner',
                              override=True)

        # Verify initial state
        output_dbs = ActionExecutionOutput.get_all()
        self.assertEqual(len(output_dbs), 0)

        runner = self._get_runner(action_db, cmd='echo 10')
        runner.pre_run()
        status, result, _ = runner.run({})
        runner.post_run(status, result)

        self.assertEquals(status, action_constants.LIVEACTION_STATUS_SUCCEEDED)
        self.assertEquals(result['stdout'], 10)

        output_dbs = ActionExecutionOutput.get_all()
        self.assertEqual(len(output_dbs), 1)
        self.assertEqual(output_dbs[0].output_type, 'stdout')
        self.assertEqual(output_dbs[0].data, '10\n')
Пример #2
0
    def test_shell_command_action_basic(self):
        models = self.fixtures_loader.load_models(
            fixtures_pack='generic', fixtures_dict={'actions': ['local.yaml']})
        action_db = models['actions']['local.yaml']

        runner = self._get_runner(action_db, cmd='echo 10')
        runner.pre_run()
        status, result, _ = runner.run({})
        runner.post_run(status, result)

        self.assertEquals(status, action_constants.LIVEACTION_STATUS_SUCCEEDED)
        self.assertEquals(result['stdout'], 10)

        # End result should be the same when streaming is enabled
        cfg.CONF.set_override(name='stream_output', group='actionrunner', override=True)

        # Verify initial state
        output_dbs = ActionExecutionOutput.get_all()
        self.assertEqual(len(output_dbs), 0)

        runner = self._get_runner(action_db, cmd='echo 10')
        runner.pre_run()
        status, result, _ = runner.run({})
        runner.post_run(status, result)

        self.assertEquals(status, action_constants.LIVEACTION_STATUS_SUCCEEDED)
        self.assertEquals(result['stdout'], 10)

        output_dbs = ActionExecutionOutput.get_all()
        self.assertEqual(len(output_dbs), 1)
        self.assertEqual(output_dbs[0].output_type, 'stdout')
        self.assertEqual(output_dbs[0].data, '10\n')
Пример #3
0
    def test_shell_command_action_basic(self):
        models = self.fixtures_loader.load_models(
            fixtures_pack="generic", fixtures_dict={"actions": ["local.yaml"]})
        action_db = models["actions"]["local.yaml"]

        runner = self._get_runner(action_db, cmd="echo 10")
        runner.pre_run()
        status, result, _ = runner.run({})
        runner.post_run(status, result)

        self.assertEqual(status, action_constants.LIVEACTION_STATUS_SUCCEEDED)
        self.assertEqual(result["stdout"], 10)

        # End result should be the same when streaming is enabled
        cfg.CONF.set_override(name="stream_output",
                              group="actionrunner",
                              override=True)

        # Verify initial state
        output_dbs = ActionExecutionOutput.get_all()
        self.assertEqual(len(output_dbs), 0)

        runner = self._get_runner(action_db, cmd="echo 10")
        runner.pre_run()
        status, result, _ = runner.run({})
        runner.post_run(status, result)

        self.assertEqual(status, action_constants.LIVEACTION_STATUS_SUCCEEDED)
        self.assertEqual(result["stdout"], 10)

        output_dbs = ActionExecutionOutput.get_all()
        self.assertEqual(len(output_dbs), 1)
        self.assertEqual(output_dbs[0].output_type, "stdout")
        self.assertEqual(output_dbs[0].data, "10\n")
Пример #4
0
    def test_real_time_output_streaming_bufsize(self):
        # Test various values for bufsize and verify it works / doesn't hang the process
        cfg.CONF.set_override(name='stream_output', group='actionrunner', override=True)

        bufsize_values = [-100, -2, -1, 0, 1, 2, 1024, 2048, 4096, 10000]

        for index, bufsize in enumerate(bufsize_values, 1):
            cfg.CONF.set_override(name='stream_output_buffer_size', override=bufsize,
                                  group='actionrunner')

            output_dbs = ActionExecutionOutput.get_all()
            # Unexpected third party warnings will also inflate this number
            self.assertGreaterEqual(len(output_dbs), (index - 1) * 4)

            runner = self._get_mock_runner_obj()
            runner.entry_point = PRINT_TO_STDOUT_STDERR_ACTION
            runner.pre_run()
            (_, output, _) = runner.run({'stdout_count': 2, 'stderr_count': 2})

            # assertMultiLineEqual displays a diff if the two don't match
            self.assertMultiLineEqual(output['stdout'], 'stdout line 0\nstdout line 1\n')
            # Third party packages can unexpectedly emit warnings and add more
            # output to the streamed stderr, so we check that the expected
            # lines occurred, but we allow additional lines to exist
            self.assertIn('stderr line 0\n', output['stderr'])
            self.assertIn('stderr line 1\n', output['stderr'])
            self.assertEqual(output['exit_code'], 0)

            output_dbs = ActionExecutionOutput.get_all()
            # Unexpected third party warnings will also inflate this number
            self.assertGreaterEqual(len(output_dbs), (index) * 4)
Пример #5
0
    def test_real_time_output_streaming_bufsize(self):
        # Test various values for bufsize and verify it works / doesn't hang the process
        cfg.CONF.set_override(name='stream_output',
                              group='actionrunner',
                              override=True)

        bufsize_values = [-100, -2, -1, 0, 1, 2, 1024, 2048, 4096, 10000]

        for index, bufsize in enumerate(bufsize_values, 1):
            cfg.CONF.set_override(name='stream_output_buffer_size',
                                  override=bufsize,
                                  group='actionrunner')

            output_dbs = ActionExecutionOutput.get_all()
            self.assertEqual(len(output_dbs), (index - 1) * 4)

            runner = self._get_mock_runner_obj()
            runner.entry_point = PRINT_TO_STDOUT_STDERR_ACTION
            runner.pre_run()
            (_, output, _) = runner.run({'stdout_count': 2, 'stderr_count': 2})

            self.assertEqual(output['stdout'],
                             'stdout line 0\nstdout line 1\n')
            self.assertEqual(output['stderr'],
                             'stderr line 0\nstderr line 1\n')
            self.assertEqual(output['exit_code'], 0)

            output_dbs = ActionExecutionOutput.get_all()
            self.assertEqual(len(output_dbs), (index) * 4)
    def test_script_with_paramters_parameter_serialization(self):
        models = self.fixtures_loader.load_models(
            fixtures_pack='generic',
            fixtures_dict={'actions': ['local_script_with_params.yaml']})
        action_db = models['actions']['local_script_with_params.yaml']
        entry_point = os.path.join(
            get_fixtures_base_path(),
            'generic/actions/local_script_with_params.sh')

        action_parameters = {
            'param_string': 'test string',
            'param_integer': 1,
            'param_float': 2.55,
            'param_boolean': True,
            'param_list': ['a', 'b', 'c'],
            'param_object': {
                'foo': 'bar'
            }
        }

        runner = self._get_runner(action_db=action_db, entry_point=entry_point)
        runner.pre_run()
        status, result, _ = runner.run(action_parameters=action_parameters)
        runner.post_run(status, result)

        self.assertEqual(status, action_constants.LIVEACTION_STATUS_SUCCEEDED)
        self.assertTrue('PARAM_STRING=test string' in result['stdout'])
        self.assertTrue('PARAM_INTEGER=1' in result['stdout'])
        self.assertTrue('PARAM_FLOAT=2.55' in result['stdout'])
        self.assertTrue('PARAM_BOOLEAN=1' in result['stdout'])
        self.assertTrue('PARAM_LIST=a,b,c' in result['stdout'])
        self.assertTrue('PARAM_OBJECT={"foo": "bar"}' in result['stdout'])

        action_parameters = {
            'param_string': 'test string',
            'param_integer': 1,
            'param_float': 2.55,
            'param_boolean': False,
            'param_list': ['a', 'b', 'c'],
            'param_object': {
                'foo': 'bar'
            }
        }

        runner = self._get_runner(action_db=action_db, entry_point=entry_point)
        runner.pre_run()
        status, result, _ = runner.run(action_parameters=action_parameters)
        runner.post_run(status, result)

        self.assertEqual(status, action_constants.LIVEACTION_STATUS_SUCCEEDED)
        self.assertTrue('PARAM_BOOLEAN=0' in result['stdout'])

        action_parameters = {
            'param_string': '',
            'param_integer': None,
            'param_float': None,
        }

        runner = self._get_runner(action_db=action_db, entry_point=entry_point)
        runner.pre_run()
        status, result, _ = runner.run(action_parameters=action_parameters)
        runner.post_run(status, result)

        self.assertEqual(status, action_constants.LIVEACTION_STATUS_SUCCEEDED)
        self.assertTrue('PARAM_STRING=\n' in result['stdout'])
        self.assertTrue('PARAM_INTEGER=\n' in result['stdout'])
        self.assertTrue('PARAM_FLOAT=\n' in result['stdout'])

        # End result should be the same when streaming is enabled
        cfg.CONF.set_override(name='stream_output',
                              group='actionrunner',
                              override=True)

        # Verify initial state
        output_dbs = ActionExecutionOutput.get_all()
        self.assertEqual(len(output_dbs), 0)

        action_parameters = {
            'param_string': 'test string',
            'param_integer': 1,
            'param_float': 2.55,
            'param_boolean': True,
            'param_list': ['a', 'b', 'c'],
            'param_object': {
                'foo': 'bar'
            }
        }

        runner = self._get_runner(action_db=action_db, entry_point=entry_point)
        runner.pre_run()
        status, result, _ = runner.run(action_parameters=action_parameters)
        runner.post_run(status, result)

        self.assertEqual(status, action_constants.LIVEACTION_STATUS_SUCCEEDED)
        self.assertTrue('PARAM_STRING=test string' in result['stdout'])
        self.assertTrue('PARAM_INTEGER=1' in result['stdout'])
        self.assertTrue('PARAM_FLOAT=2.55' in result['stdout'])
        self.assertTrue('PARAM_BOOLEAN=1' in result['stdout'])
        self.assertTrue('PARAM_LIST=a,b,c' in result['stdout'])
        self.assertTrue('PARAM_OBJECT={"foo": "bar"}' in result['stdout'])

        output_dbs = ActionExecutionOutput.query(output_type='stdout')
        self.assertEqual(len(output_dbs), 6)
        self.assertEqual(output_dbs[0].data, 'PARAM_STRING=test string\n')
        self.assertEqual(output_dbs[5].data, 'PARAM_OBJECT={"foo": "bar"}\n')

        output_dbs = ActionExecutionOutput.query(output_type='stderr')
        self.assertEqual(len(output_dbs), 0)
Пример #7
0
    def test_action_stdout_and_stderr_is_not_stored_in_db_by_default(
            self, mock_spawn, mock_popen):
        # Feature should be disabled by default
        values = {'delimiter': ACTION_OUTPUT_RESULT_DELIMITER}

        # Note: We need to mock spawn function so we can test everything in single event loop
        # iteration
        mock_spawn.side_effect = blocking_eventlet_spawn

        # No output to stdout and no result (implicit None)
        mock_stdout = [
            'pre result line 1\n',
            '%(delimiter)sTrue%(delimiter)s' % values, 'post result line 1'
        ]
        mock_stderr = ['stderr line 1\n', 'stderr line 2\n', 'stderr line 3\n']

        mock_process = mock.Mock()
        mock_process.returncode = 0
        mock_popen.return_value = mock_process
        mock_process.stdout.closed = False
        mock_process.stderr.closed = False
        mock_process.stdout.readline = make_mock_stream_readline(
            mock_process.stdout, mock_stdout, stop_counter=3)
        mock_process.stderr.readline = make_mock_stream_readline(
            mock_process.stderr, mock_stderr, stop_counter=3)

        runner = self._get_mock_runner_obj()
        runner.entry_point = PASCAL_ROW_ACTION_PATH
        runner.pre_run()
        (_, output, _) = runner.run({'row_index': 4})

        self.assertEqual(output['stdout'],
                         'pre result line 1\npost result line 1')
        self.assertEqual(output['stderr'],
                         'stderr line 1\nstderr line 2\nstderr line 3\n')
        self.assertEqual(output['result'], 'True')
        self.assertEqual(output['exit_code'], 0)

        output_dbs = ActionExecutionOutput.get_all()
        self.assertEqual(len(output_dbs), 0)

        # False is a default behavior so end result should be the same
        cfg.CONF.set_override(name='stream_output',
                              group='actionrunner',
                              override=False)

        mock_process = mock.Mock()
        mock_process.returncode = 0
        mock_popen.return_value = mock_process
        mock_process.stdout.closed = False
        mock_process.stderr.closed = False
        mock_process.stdout.readline = make_mock_stream_readline(
            mock_process.stdout, mock_stdout, stop_counter=3)
        mock_process.stderr.readline = make_mock_stream_readline(
            mock_process.stderr, mock_stderr, stop_counter=3)

        runner.pre_run()
        (_, output, _) = runner.run({'row_index': 4})

        self.assertEqual(output['stdout'],
                         'pre result line 1\npost result line 1')
        self.assertEqual(output['stderr'],
                         'stderr line 1\nstderr line 2\nstderr line 3\n')
        self.assertEqual(output['result'], 'True')
        self.assertEqual(output['exit_code'], 0)

        output_dbs = ActionExecutionOutput.get_all()
        self.assertEqual(len(output_dbs), 0)
Пример #8
0
    def test_script_with_parameters_parameter_serialization(self):
        models = self.fixtures_loader.load_models(
            fixtures_pack="generic",
            fixtures_dict={"actions": ["local_script_with_params.yaml"]},
        )
        action_db = models["actions"]["local_script_with_params.yaml"]
        entry_point = os.path.join(
            get_fixtures_base_path(),
            "generic/actions/local_script_with_params.sh")

        action_parameters = {
            "param_string": "test string",
            "param_integer": 1,
            "param_float": 2.55,
            "param_boolean": True,
            "param_list": ["a", "b", "c"],
            "param_object": {
                "foo": "bar"
            },
        }

        runner = self._get_runner(action_db=action_db, entry_point=entry_point)
        runner.pre_run()
        status, result, _ = runner.run(action_parameters=action_parameters)
        runner.post_run(status, result)
        self.assertEqual(status, action_constants.LIVEACTION_STATUS_SUCCEEDED)
        self.assertIn("PARAM_STRING=test string", result["stdout"])
        self.assertIn("PARAM_INTEGER=1", result["stdout"])
        self.assertIn("PARAM_FLOAT=2.55", result["stdout"])
        self.assertIn("PARAM_BOOLEAN=1", result["stdout"])
        self.assertIn("PARAM_LIST=a,b,c", result["stdout"])
        self.assertIn('PARAM_OBJECT={"foo":"bar"}', result["stdout"])

        action_parameters = {
            "param_string": "test string",
            "param_integer": 1,
            "param_float": 2.55,
            "param_boolean": False,
            "param_list": ["a", "b", "c"],
            "param_object": {
                "foo": "bar"
            },
        }

        runner = self._get_runner(action_db=action_db, entry_point=entry_point)
        runner.pre_run()
        status, result, _ = runner.run(action_parameters=action_parameters)
        runner.post_run(status, result)

        self.assertEqual(status, action_constants.LIVEACTION_STATUS_SUCCEEDED)
        self.assertIn("PARAM_BOOLEAN=0", result["stdout"])

        action_parameters = {
            "param_string": "",
            "param_integer": None,
            "param_float": None,
        }

        runner = self._get_runner(action_db=action_db, entry_point=entry_point)
        runner.pre_run()
        status, result, _ = runner.run(action_parameters=action_parameters)
        runner.post_run(status, result)

        self.assertEqual(status, action_constants.LIVEACTION_STATUS_SUCCEEDED)
        self.assertIn("PARAM_STRING=\n", result["stdout"])
        self.assertIn("PARAM_INTEGER=\n", result["stdout"])
        self.assertIn("PARAM_FLOAT=\n", result["stdout"])

        # End result should be the same when streaming is enabled
        cfg.CONF.set_override(name="stream_output",
                              group="actionrunner",
                              override=True)

        # Verify initial state
        output_dbs = ActionExecutionOutput.get_all()
        self.assertEqual(len(output_dbs), 0)

        action_parameters = {
            "param_string": "test string",
            "param_integer": 1,
            "param_float": 2.55,
            "param_boolean": True,
            "param_list": ["a", "b", "c"],
            "param_object": {
                "foo": "bar"
            },
        }

        runner = self._get_runner(action_db=action_db, entry_point=entry_point)
        runner.pre_run()
        status, result, _ = runner.run(action_parameters=action_parameters)
        runner.post_run(status, result)

        self.assertEqual(status, action_constants.LIVEACTION_STATUS_SUCCEEDED)
        self.assertIn("PARAM_STRING=test string", result["stdout"])
        self.assertIn("PARAM_INTEGER=1", result["stdout"])
        self.assertIn("PARAM_FLOAT=2.55", result["stdout"])
        self.assertIn("PARAM_BOOLEAN=1", result["stdout"])
        self.assertIn("PARAM_LIST=a,b,c", result["stdout"])
        self.assertIn('PARAM_OBJECT={"foo":"bar"}', result["stdout"])

        output_dbs = ActionExecutionOutput.query(output_type="stdout")
        self.assertEqual(len(output_dbs), 6)
        self.assertEqual(output_dbs[0].data, "PARAM_STRING=test string\n")
        self.assertEqual(output_dbs[5].data, 'PARAM_OBJECT={"foo":"bar"}\n')

        output_dbs = ActionExecutionOutput.query(output_type="stderr")
        self.assertEqual(len(output_dbs), 0)
Пример #9
0
    def test_script_with_parameters_parameter_serialization(self):
        models = self.fixtures_loader.load_models(
            fixtures_pack='generic', fixtures_dict={'actions': ['local_script_with_params.yaml']})
        action_db = models['actions']['local_script_with_params.yaml']
        entry_point = os.path.join(get_fixtures_base_path(),
                                   'generic/actions/local_script_with_params.sh')

        action_parameters = {
            'param_string': 'test string',
            'param_integer': 1,
            'param_float': 2.55,
            'param_boolean': True,
            'param_list': ['a', 'b', 'c'],
            'param_object': {'foo': 'bar'}
        }

        runner = self._get_runner(action_db=action_db, entry_point=entry_point)
        runner.pre_run()
        status, result, _ = runner.run(action_parameters=action_parameters)
        runner.post_run(status, result)
        self.assertEqual(status, action_constants.LIVEACTION_STATUS_SUCCEEDED)
        self.assertTrue('PARAM_STRING=test string' in result['stdout'])
        self.assertTrue('PARAM_INTEGER=1' in result['stdout'])
        self.assertTrue('PARAM_FLOAT=2.55' in result['stdout'])
        self.assertTrue('PARAM_BOOLEAN=1' in result['stdout'])
        self.assertTrue('PARAM_LIST=a,b,c' in result['stdout'])
        self.assertTrue('PARAM_OBJECT={"foo": "bar"}' in result['stdout'])

        action_parameters = {
            'param_string': 'test string',
            'param_integer': 1,
            'param_float': 2.55,
            'param_boolean': False,
            'param_list': ['a', 'b', 'c'],
            'param_object': {'foo': 'bar'}
        }

        runner = self._get_runner(action_db=action_db, entry_point=entry_point)
        runner.pre_run()
        status, result, _ = runner.run(action_parameters=action_parameters)
        runner.post_run(status, result)

        self.assertEqual(status, action_constants.LIVEACTION_STATUS_SUCCEEDED)
        self.assertTrue('PARAM_BOOLEAN=0' in result['stdout'])

        action_parameters = {
            'param_string': '',
            'param_integer': None,
            'param_float': None,
        }

        runner = self._get_runner(action_db=action_db, entry_point=entry_point)
        runner.pre_run()
        status, result, _ = runner.run(action_parameters=action_parameters)
        runner.post_run(status, result)

        self.assertEqual(status, action_constants.LIVEACTION_STATUS_SUCCEEDED)
        self.assertTrue('PARAM_STRING=\n' in result['stdout'])
        self.assertTrue('PARAM_INTEGER=\n' in result['stdout'])
        self.assertTrue('PARAM_FLOAT=\n' in result['stdout'])

        # End result should be the same when streaming is enabled
        cfg.CONF.set_override(name='stream_output', group='actionrunner', override=True)

        # Verify initial state
        output_dbs = ActionExecutionOutput.get_all()
        self.assertEqual(len(output_dbs), 0)

        action_parameters = {
            'param_string': 'test string',
            'param_integer': 1,
            'param_float': 2.55,
            'param_boolean': True,
            'param_list': ['a', 'b', 'c'],
            'param_object': {'foo': 'bar'}
        }

        runner = self._get_runner(action_db=action_db, entry_point=entry_point)
        runner.pre_run()
        status, result, _ = runner.run(action_parameters=action_parameters)
        runner.post_run(status, result)

        self.assertEqual(status, action_constants.LIVEACTION_STATUS_SUCCEEDED)
        self.assertTrue('PARAM_STRING=test string' in result['stdout'])
        self.assertTrue('PARAM_INTEGER=1' in result['stdout'])
        self.assertTrue('PARAM_FLOAT=2.55' in result['stdout'])
        self.assertTrue('PARAM_BOOLEAN=1' in result['stdout'])
        self.assertTrue('PARAM_LIST=a,b,c' in result['stdout'])
        self.assertTrue('PARAM_OBJECT={"foo": "bar"}' in result['stdout'])

        output_dbs = ActionExecutionOutput.query(output_type='stdout')
        self.assertEqual(len(output_dbs), 6)
        self.assertEqual(output_dbs[0].data, 'PARAM_STRING=test string\n')
        self.assertEqual(output_dbs[5].data, 'PARAM_OBJECT={"foo": "bar"}\n')

        output_dbs = ActionExecutionOutput.query(output_type='stderr')
        self.assertEqual(len(output_dbs), 0)
Пример #10
0
    def test_action_stdout_and_stderr_is_not_stored_in_db_by_default(self, mock_spawn, mock_popen):
        # Feature should be disabled by default
        values = {'delimiter': ACTION_OUTPUT_RESULT_DELIMITER}

        # Note: We need to mock spawn function so we can test everything in single event loop
        # iteration
        mock_spawn.side_effect = blocking_eventlet_spawn

        # No output to stdout and no result (implicit None)
        mock_stdout = [
            'pre result line 1\n',
            '%(delimiter)sTrue%(delimiter)s' % values,
            'post result line 1'
        ]
        mock_stderr = [
            'stderr line 1\n',
            'stderr line 2\n',
            'stderr line 3\n'
        ]

        mock_process = mock.Mock()
        mock_process.returncode = 0
        mock_popen.return_value = mock_process
        mock_process.stdout.closed = False
        mock_process.stderr.closed = False
        mock_process.stdout.readline = make_mock_stream_readline(mock_process.stdout, mock_stdout,
                                                                 stop_counter=3)
        mock_process.stderr.readline = make_mock_stream_readline(mock_process.stderr, mock_stderr,
                                                                 stop_counter=3)

        runner = self._get_mock_runner_obj()
        runner.entry_point = PASCAL_ROW_ACTION_PATH
        runner.pre_run()
        (_, output, _) = runner.run({'row_index': 4})

        self.assertEqual(output['stdout'], 'pre result line 1\npost result line 1')
        self.assertEqual(output['stderr'], 'stderr line 1\nstderr line 2\nstderr line 3\n')
        self.assertEqual(output['result'], 'True')
        self.assertEqual(output['exit_code'], 0)

        output_dbs = ActionExecutionOutput.get_all()
        self.assertEqual(len(output_dbs), 0)

        # False is a default behavior so end result should be the same
        cfg.CONF.set_override(name='stream_output', group='actionrunner', override=False)

        mock_process = mock.Mock()
        mock_process.returncode = 0
        mock_popen.return_value = mock_process
        mock_process.stdout.closed = False
        mock_process.stderr.closed = False
        mock_process.stdout.readline = make_mock_stream_readline(mock_process.stdout, mock_stdout,
                                                                 stop_counter=3)
        mock_process.stderr.readline = make_mock_stream_readline(mock_process.stderr, mock_stderr,
                                                                 stop_counter=3)

        runner.pre_run()
        (_, output, _) = runner.run({'row_index': 4})

        self.assertEqual(output['stdout'], 'pre result line 1\npost result line 1')
        self.assertEqual(output['stderr'], 'stderr line 1\nstderr line 2\nstderr line 3\n')
        self.assertEqual(output['result'], 'True')
        self.assertEqual(output['exit_code'], 0)

        output_dbs = ActionExecutionOutput.get_all()
        self.assertEqual(len(output_dbs), 0)