def test_automatic_auth_skipped_on_auth_command(self): self._write_mock_config() shell = Shell() shell._get_auth_token = mock.Mock() argv = ['auth', 'testu', '-p', 'testp'] args = shell.parser.parse_args(args=argv) shell.get_client(args=args) self.assertEqual(shell._get_auth_token.call_count, 0)
def test_automatic_auth_skipped_if_token_provided_as_env_variable(self): self._write_mock_config() shell = Shell() shell._get_auth_token = mock.Mock() os.environ['ST2_AUTH_TOKEN'] = 'fooo' argv = ['action', 'list'] args = shell.parser.parse_args(args=argv) shell.get_client(args=args) self.assertEqual(shell._get_auth_token.call_count, 0)
def test_get_one_unicode_character_in_name(self): self._write_mock_config() shell = Shell() shell._get_auth_token = mock.Mock() os.environ["ST2_AUTH_TOKEN"] = "fooo" argv = ["action", "get", "examples.test_rule_utf8_náme"] args = shell.parser.parse_args(args=argv) shell.get_client(args=args) self.assertEqual(args.ref_or_id, ["examples.test_rule_utf8_náme"])
def test_automatic_auth_skipped_if_token_provided_as_cli_argument(self): self._write_mock_config() shell = Shell() shell._get_auth_token = mock.Mock() argv = ['action', 'list', '--token=bar'] args = shell.parser.parse_args(args=argv) shell.get_client(args=args) self.assertEqual(shell._get_auth_token.call_count, 0) argv = ['action', 'list', '-t', 'bar'] args = shell.parser.parse_args(args=argv) shell.get_client(args=args) self.assertEqual(shell._get_auth_token.call_count, 0)
class TestShell(base.BaseCLITestCase): capture_output = True def __init__(self, *args, **kwargs): super(TestShell, self).__init__(*args, **kwargs) self.shell = Shell() def test_endpoints_default(self): base_url = 'http://127.0.0.1' auth_url = 'http://127.0.0.1:9100' api_url = 'http://127.0.0.1:9101/v1' args = ['trigger', 'list'] parsed_args = self.shell.parser.parse_args(args) client = self.shell.get_client(parsed_args) self.assertEqual(client.endpoints['base'], base_url) self.assertEqual(client.endpoints['auth'], auth_url) self.assertEqual(client.endpoints['api'], api_url) def test_endpoints_base_url_from_cli(self): base_url = 'http://www.st2.com' auth_url = 'http://www.st2.com:9100' api_url = 'http://www.st2.com:9101/v1' args = ['--url', base_url, 'trigger', 'list'] parsed_args = self.shell.parser.parse_args(args) client = self.shell.get_client(parsed_args) self.assertEqual(client.endpoints['base'], base_url) self.assertEqual(client.endpoints['auth'], auth_url) self.assertEqual(client.endpoints['api'], api_url) def test_endpoints_base_url_from_env(self): base_url = 'http://www.st2.com' auth_url = 'http://www.st2.com:9100' api_url = 'http://www.st2.com:9101/v1' os.environ['ST2_BASE_URL'] = base_url args = ['trigger', 'list'] parsed_args = self.shell.parser.parse_args(args) client = self.shell.get_client(parsed_args) self.assertEqual(client.endpoints['base'], base_url) self.assertEqual(client.endpoints['auth'], auth_url) self.assertEqual(client.endpoints['api'], api_url) def test_endpoints_override_from_cli(self): base_url = 'http://www.st2.com' auth_url = 'http://www.st2.com:8888' api_url = 'http://www.stackstorm1.com:9101/v1' args = [ '--url', base_url, '--auth-url', auth_url, '--api-url', api_url, 'trigger', 'list' ] parsed_args = self.shell.parser.parse_args(args) client = self.shell.get_client(parsed_args) self.assertEqual(client.endpoints['base'], base_url) self.assertEqual(client.endpoints['auth'], auth_url) self.assertEqual(client.endpoints['api'], api_url) def test_endpoints_override_from_env(self): base_url = 'http://www.st2.com' auth_url = 'http://www.st2.com:8888' api_url = 'http://www.stackstorm1.com:9101/v1' os.environ['ST2_BASE_URL'] = base_url os.environ['ST2_AUTH_URL'] = auth_url os.environ['ST2_API_URL'] = api_url args = ['trigger', 'list'] parsed_args = self.shell.parser.parse_args(args) client = self.shell.get_client(parsed_args) self.assertEqual(client.endpoints['base'], base_url) self.assertEqual(client.endpoints['auth'], auth_url) self.assertEqual(client.endpoints['api'], api_url) @mock.patch.object(httpclient.HTTPClient, 'get', mock.MagicMock(return_value=base.FakeResponse( json.dumps(base.RESOURCES), 200, 'OK'))) def test_exit_code_on_success(self): argv = ['trigger', 'list'] self.assertEqual(self.shell.run(argv), 0) @mock.patch.object( httpclient.HTTPClient, 'get', mock.MagicMock( return_value=base.FakeResponse(None, 500, 'INTERNAL SERVER ERROR')) ) def test_exit_code_on_error(self): argv = ['trigger', 'list'] self.assertEqual(self.shell.run(argv), 1) def _validate_parser(self, args_list, is_subcommand=True): for args in args_list: ns = self.shell.parser.parse_args(args) func = ( self.shell.commands[args[0]].run_and_print if not is_subcommand else self.shell.commands[args[0]].commands[args[1]].run_and_print) self.assertEqual(ns.func, func) def test_action(self): args_list = [ ['action', 'list'], ['action', 'get', 'abc'], ['action', 'create', '/tmp/action.json'], ['action', 'update', '123', '/tmp/action.json'], ['action', 'delete', 'abc'], ['action', 'execute', '-h'], ['action', 'execute', 'remote', '-h'], [ 'action', 'execute', 'remote', 'hosts=192.168.1.1', 'user=st2', 'cmd="ls -l"' ], ['action', 'execute', 'remote-fib', 'hosts=192.168.1.1', '3', '8'] ] self._validate_parser(args_list) def test_action_execution(self): args_list = [['execution', 'list'], ['execution', 'get', '123'], ['execution', 'get', '123', '-d'], ['execution', 'get', '123', '-k', 'localhost.stdout'], ['execution', 're-run', '123'], ['execution', 're-run', '123', '--tasks', 'x', 'y', 'z'], [ 'execution', 're-run', '123', '--tasks', 'x', 'y', 'z', '--no-reset', 'x' ], ['execution', 're-run', '123', 'a=1', 'b=x', 'c=True']] self._validate_parser(args_list) # Test mutually exclusive argument groups self.assertRaises( SystemExit, self._validate_parser, [['execution', 'get', '123', '-d', '-k', 'localhost.stdout']]) def test_key(self): args_list = [['key', 'list'], ['key', 'get', 'abc'], ['key', 'set', 'abc', '123'], ['key', 'delete', 'abc'], ['key', 'load', '/tmp/keys.json']] self._validate_parser(args_list) def test_policy(self): args_list = [ ['policy', 'list'], ['policy', 'list', '-p', 'core'], ['policy', 'list', '--pack', 'core'], ['policy', 'list', '-r', 'core.local'], ['policy', 'list', '--resource-ref', 'core.local'], ['policy', 'list', '-pt', 'action.type1'], ['policy', 'list', '--policy-type', 'action.type1'], ['policy', 'list', '-r', 'core.local', '-pt', 'action.type1'], [ 'policy', 'list', '--resource-ref', 'core.local', '--policy-type', 'action.type1' ], ['policy', 'get', 'abc'], ['policy', 'create', '/tmp/policy.json'], ['policy', 'update', '123', '/tmp/policy.json'], ['policy', 'delete', 'abc'] ] self._validate_parser(args_list) def test_policy_type(self): args_list = [['policy-type', 'list'], ['policy-type', 'list', '-r', 'action'], ['policy-type', 'list', '--resource-type', 'action'], ['policy-type', 'get', 'abc']] self._validate_parser(args_list) @mock.patch('st2client.shell.ST2_CONFIG_PATH', '/home/does/not/exist') def test_print_config_default_config_no_config(self): os.environ['ST2_CONFIG_FILE'] = '/home/does/not/exist' argv = ['--print-config'] self.assertEqual(self.shell.run(argv), 3) self.stdout.seek(0) stdout = self.stdout.read() self.assertTrue('username = None' in stdout) self.assertTrue('cache_token = True' in stdout) def test_print_config_custom_config_as_env_variable(self): os.environ['ST2_CONFIG_FILE'] = CONFIG_FILE_PATH_FULL argv = ['--print-config'] self.assertEqual(self.shell.run(argv), 3) self.stdout.seek(0) stdout = self.stdout.read() self.assertTrue('username = test1' in stdout) self.assertTrue('cache_token = False' in stdout) def test_print_config_custom_config_as_command_line_argument(self): argv = ['--print-config', '--config-file=%s' % (CONFIG_FILE_PATH_FULL)] self.assertEqual(self.shell.run(argv), 3) self.stdout.seek(0) stdout = self.stdout.read() self.assertTrue('username = test1' in stdout) self.assertTrue('cache_token = False' in stdout) def test_run(self): args_list = [['run', '-h'], ['run', 'abc', '-h'], [ 'run', 'remote', 'hosts=192.168.1.1', 'user=st2', 'cmd="ls -l"' ], ['run', 'remote-fib', 'hosts=192.168.1.1', '3', '8']] self._validate_parser(args_list, is_subcommand=False) def test_runner(self): args_list = [['runner', 'list'], ['runner', 'get', 'abc']] self._validate_parser(args_list) def test_rule(self): args_list = [['rule', 'list'], ['rule', 'get', 'abc'], ['rule', 'create', '/tmp/rule.json'], ['rule', 'update', '123', '/tmp/rule.json'], ['rule', 'delete', 'abc']] self._validate_parser(args_list) def test_trigger(self): args_list = [['trigger', 'list'], ['trigger', 'get', 'abc'], ['trigger', 'create', '/tmp/trigger.json'], ['trigger', 'update', '123', '/tmp/trigger.json'], ['trigger', 'delete', 'abc']] self._validate_parser(args_list)
class TestShell(base.BaseCLITestCase): capture_output = True def __init__(self, *args, **kwargs): super(TestShell, self).__init__(*args, **kwargs) self.shell = Shell() def test_endpoints_default(self): base_url = 'http://127.0.0.1' auth_url = 'http://127.0.0.1:9100' api_url = 'http://127.0.0.1:9101/v1' args = ['trigger', 'list'] parsed_args = self.shell.parser.parse_args(args) client = self.shell.get_client(parsed_args) self.assertEqual(client.endpoints['base'], base_url) self.assertEqual(client.endpoints['auth'], auth_url) self.assertEqual(client.endpoints['api'], api_url) def test_endpoints_base_url_from_cli(self): base_url = 'http://www.st2.com' auth_url = 'http://www.st2.com:9100' api_url = 'http://www.st2.com:9101/v1' args = ['--url', base_url, 'trigger', 'list'] parsed_args = self.shell.parser.parse_args(args) client = self.shell.get_client(parsed_args) self.assertEqual(client.endpoints['base'], base_url) self.assertEqual(client.endpoints['auth'], auth_url) self.assertEqual(client.endpoints['api'], api_url) def test_endpoints_base_url_from_env(self): base_url = 'http://www.st2.com' auth_url = 'http://www.st2.com:9100' api_url = 'http://www.st2.com:9101/v1' os.environ['ST2_BASE_URL'] = base_url args = ['trigger', 'list'] parsed_args = self.shell.parser.parse_args(args) client = self.shell.get_client(parsed_args) self.assertEqual(client.endpoints['base'], base_url) self.assertEqual(client.endpoints['auth'], auth_url) self.assertEqual(client.endpoints['api'], api_url) def test_endpoints_override_from_cli(self): base_url = 'http://www.st2.com' auth_url = 'http://www.st2.com:8888' api_url = 'http://www.stackstorm1.com:9101/v1' args = ['--url', base_url, '--auth-url', auth_url, '--api-url', api_url, 'trigger', 'list'] parsed_args = self.shell.parser.parse_args(args) client = self.shell.get_client(parsed_args) self.assertEqual(client.endpoints['base'], base_url) self.assertEqual(client.endpoints['auth'], auth_url) self.assertEqual(client.endpoints['api'], api_url) def test_endpoints_override_from_env(self): base_url = 'http://www.st2.com' auth_url = 'http://www.st2.com:8888' api_url = 'http://www.stackstorm1.com:9101/v1' os.environ['ST2_BASE_URL'] = base_url os.environ['ST2_AUTH_URL'] = auth_url os.environ['ST2_API_URL'] = api_url args = ['trigger', 'list'] parsed_args = self.shell.parser.parse_args(args) client = self.shell.get_client(parsed_args) self.assertEqual(client.endpoints['base'], base_url) self.assertEqual(client.endpoints['auth'], auth_url) self.assertEqual(client.endpoints['api'], api_url) @mock.patch.object( httpclient.HTTPClient, 'get', mock.MagicMock(return_value=base.FakeResponse(json.dumps(base.RESOURCES), 200, 'OK'))) def test_exit_code_on_success(self): argv = ['trigger', 'list'] self.assertEqual(self.shell.run(argv), 0) @mock.patch.object( httpclient.HTTPClient, 'get', mock.MagicMock(return_value=base.FakeResponse(None, 500, 'INTERNAL SERVER ERROR'))) def test_exit_code_on_error(self): argv = ['trigger', 'list'] self.assertEqual(self.shell.run(argv), 1) def _validate_parser(self, args_list, is_subcommand=True): for args in args_list: ns = self.shell.parser.parse_args(args) func = (self.shell.commands[args[0]].run_and_print if not is_subcommand else self.shell.commands[args[0]].commands[args[1]].run_and_print) self.assertEqual(ns.func, func) def test_action(self): args_list = [ ['action', 'list'], ['action', 'get', 'abc'], ['action', 'create', '/tmp/action.json'], ['action', 'update', '123', '/tmp/action.json'], ['action', 'delete', 'abc'], ['action', 'execute', '-h'], ['action', 'execute', 'remote', '-h'], ['action', 'execute', 'remote', 'hosts=192.168.1.1', 'user=st2', 'cmd="ls -l"'], ['action', 'execute', 'remote-fib', 'hosts=192.168.1.1', '3', '8'] ] self._validate_parser(args_list) def test_action_execution(self): args_list = [ ['execution', 'list'], ['execution', 'get', '123'], ['execution', 'get', '123', '-d'], ['execution', 'get', '123', '-k', 'localhost.stdout'] ] self._validate_parser(args_list) # Test mutually exclusive argument groups self.assertRaises(SystemExit, self._validate_parser, [['execution', 'get', '123', '-d', '-k', 'localhost.stdout']]) def test_key(self): args_list = [ ['key', 'list'], ['key', 'get', 'abc'], ['key', 'set', 'abc', '123'], ['key', 'delete', 'abc'], ['key', 'load', '/tmp/keys.json'] ] self._validate_parser(args_list) def test_policy(self): args_list = [ ['policy', 'list'], ['policy', 'list', '-p', 'core'], ['policy', 'list', '--pack', 'core'], ['policy', 'list', '-r', 'core.local'], ['policy', 'list', '--resource-ref', 'core.local'], ['policy', 'list', '-pt', 'action.type1'], ['policy', 'list', '--policy-type', 'action.type1'], ['policy', 'list', '-r', 'core.local', '-pt', 'action.type1'], ['policy', 'list', '--resource-ref', 'core.local', '--policy-type', 'action.type1'], ['policy', 'get', 'abc'], ['policy', 'create', '/tmp/policy.json'], ['policy', 'update', '123', '/tmp/policy.json'], ['policy', 'delete', 'abc'] ] self._validate_parser(args_list) def test_policy_type(self): args_list = [ ['policy-type', 'list'], ['policy-type', 'list', '-r', 'action'], ['policy-type', 'list', '--resource-type', 'action'], ['policy-type', 'get', 'abc'] ] self._validate_parser(args_list) @mock.patch('st2client.shell.ST2_CONFIG_PATH', '/home/does/not/exist') def test_print_config_default_config_no_config(self): os.environ['ST2_CONFIG_FILE'] = '/home/does/not/exist' argv = ['--print-config'] self.assertEqual(self.shell.run(argv), 3) self.stdout.seek(0) stdout = self.stdout.read() self.assertTrue('username = None' in stdout) self.assertTrue('cache_token = True' in stdout) def test_print_config_custom_config_as_env_variable(self): os.environ['ST2_CONFIG_FILE'] = CONFIG_FILE_PATH_FULL argv = ['--print-config'] self.assertEqual(self.shell.run(argv), 3) self.stdout.seek(0) stdout = self.stdout.read() self.assertTrue('username = test1' in stdout) self.assertTrue('cache_token = False' in stdout) def test_print_config_custom_config_as_command_line_argument(self): argv = ['--print-config', '--config-file=%s' % (CONFIG_FILE_PATH_FULL)] self.assertEqual(self.shell.run(argv), 3) self.stdout.seek(0) stdout = self.stdout.read() self.assertTrue('username = test1' in stdout) self.assertTrue('cache_token = False' in stdout) def test_run(self): args_list = [ ['run', '-h'], ['run', 'abc', '-h'], ['run', 'remote', 'hosts=192.168.1.1', 'user=st2', 'cmd="ls -l"'], ['run', 'remote-fib', 'hosts=192.168.1.1', '3', '8'] ] self._validate_parser(args_list, is_subcommand=False) def test_runner(self): args_list = [ ['runner', 'list'], ['runner', 'get', 'abc'] ] self._validate_parser(args_list) def test_rule(self): args_list = [ ['rule', 'list'], ['rule', 'get', 'abc'], ['rule', 'create', '/tmp/rule.json'], ['rule', 'update', '123', '/tmp/rule.json'], ['rule', 'delete', 'abc'] ] self._validate_parser(args_list) def test_trigger(self): args_list = [ ['trigger', 'list'], ['trigger', 'get', 'abc'], ['trigger', 'create', '/tmp/trigger.json'], ['trigger', 'update', '123', '/tmp/trigger.json'], ['trigger', 'delete', 'abc'] ] self._validate_parser(args_list)
class ShellTestCase(base.BaseCLITestCase): capture_output = True def __init__(self, *args, **kwargs): super(ShellTestCase, self).__init__(*args, **kwargs) self.shell = Shell() def setUp(self): super(ShellTestCase, self).setUp() if six.PY3: # In python --version outputs to stdout and in 2.x to stderr self.version_output = self.stdout else: self.version_output = self.stderr def test_commands_usage_and_help_strings(self): # No command, should print out user friendly usage / help string self.assertEqual(self.shell.run([]), 2) self.stderr.seek(0) stderr = self.stderr.read() self.assertIn('Usage: ', stderr) self.assertIn('For example:', stderr) self.assertIn('CLI for StackStorm', stderr) self.assertIn('positional arguments:', stderr) self.stdout.truncate() self.stderr.truncate() # --help should result in the same output try: self.assertEqual(self.shell.run(['--help']), 0) except SystemExit as e: self.assertEqual(e.code, 0) self.stdout.seek(0) stdout = self.stdout.read() self.assertIn('Usage: ', stdout) self.assertIn('For example:', stdout) self.assertIn('CLI for StackStorm', stdout) self.assertIn('positional arguments:', stdout) self.stdout.truncate() self.stderr.truncate() # Sub command with no args try: self.assertEqual(self.shell.run(['action']), 2) except SystemExit as e: self.assertEqual(e.code, 2) self.stderr.seek(0) stderr = self.stderr.read() self.assertIn('usage', stderr) if six.PY2: self.assertIn('{list,get,create,update', stderr) self.assertIn('error: too few arguments', stderr) def test_endpoints_default(self): base_url = 'http://127.0.0.1' auth_url = 'http://127.0.0.1:9100' api_url = 'http://127.0.0.1:9101/v1' stream_url = 'http://127.0.0.1:9102/v1' args = ['trigger', 'list'] parsed_args = self.shell.parser.parse_args(args) client = self.shell.get_client(parsed_args) self.assertEqual(client.endpoints['base'], base_url) self.assertEqual(client.endpoints['auth'], auth_url) self.assertEqual(client.endpoints['api'], api_url) self.assertEqual(client.endpoints['stream'], stream_url) def test_endpoints_base_url_from_cli(self): base_url = 'http://www.st2.com' auth_url = 'http://www.st2.com:9100' api_url = 'http://www.st2.com:9101/v1' stream_url = 'http://www.st2.com:9102/v1' args = ['--url', base_url, 'trigger', 'list'] parsed_args = self.shell.parser.parse_args(args) client = self.shell.get_client(parsed_args) self.assertEqual(client.endpoints['base'], base_url) self.assertEqual(client.endpoints['auth'], auth_url) self.assertEqual(client.endpoints['api'], api_url) self.assertEqual(client.endpoints['stream'], stream_url) def test_endpoints_base_url_from_env(self): base_url = 'http://www.st2.com' auth_url = 'http://www.st2.com:9100' api_url = 'http://www.st2.com:9101/v1' stream_url = 'http://www.st2.com:9102/v1' os.environ['ST2_BASE_URL'] = base_url args = ['trigger', 'list'] parsed_args = self.shell.parser.parse_args(args) client = self.shell.get_client(parsed_args) self.assertEqual(client.endpoints['base'], base_url) self.assertEqual(client.endpoints['auth'], auth_url) self.assertEqual(client.endpoints['api'], api_url) self.assertEqual(client.endpoints['stream'], stream_url) def test_endpoints_override_from_cli(self): base_url = 'http://www.st2.com' auth_url = 'http://www.st2.com:8888' api_url = 'http://www.stackstorm1.com:9101/v1' stream_url = 'http://www.stackstorm1.com:9102/v1' args = [ '--url', base_url, '--auth-url', auth_url, '--api-url', api_url, '--stream-url', stream_url, 'trigger', 'list' ] parsed_args = self.shell.parser.parse_args(args) client = self.shell.get_client(parsed_args) self.assertEqual(client.endpoints['base'], base_url) self.assertEqual(client.endpoints['auth'], auth_url) self.assertEqual(client.endpoints['api'], api_url) self.assertEqual(client.endpoints['stream'], stream_url) def test_endpoints_override_from_env(self): base_url = 'http://www.st2.com' auth_url = 'http://www.st2.com:8888' api_url = 'http://www.stackstorm1.com:9101/v1' stream_url = 'http://www.stackstorm1.com:9102/v1' os.environ['ST2_BASE_URL'] = base_url os.environ['ST2_AUTH_URL'] = auth_url os.environ['ST2_API_URL'] = api_url os.environ['ST2_STREAM_URL'] = stream_url args = ['trigger', 'list'] parsed_args = self.shell.parser.parse_args(args) client = self.shell.get_client(parsed_args) self.assertEqual(client.endpoints['base'], base_url) self.assertEqual(client.endpoints['auth'], auth_url) self.assertEqual(client.endpoints['api'], api_url) self.assertEqual(client.endpoints['stream'], stream_url) @mock.patch.object(httpclient.HTTPClient, 'get', mock.MagicMock(return_value=base.FakeResponse( json.dumps(base.RESOURCES), 200, 'OK'))) def test_exit_code_on_success(self): argv = ['trigger', 'list'] self.assertEqual(self.shell.run(argv), 0) @mock.patch.object( httpclient.HTTPClient, 'get', mock.MagicMock( return_value=base.FakeResponse(None, 500, 'INTERNAL SERVER ERROR')) ) def test_exit_code_on_error(self): argv = ['trigger', 'list'] self.assertEqual(self.shell.run(argv), 1) def _validate_parser(self, args_list, is_subcommand=True): for args in args_list: ns = self.shell.parser.parse_args(args) func = ( self.shell.commands[args[0]].run_and_print if not is_subcommand else self.shell.commands[args[0]].commands[args[1]].run_and_print) self.assertEqual(ns.func, func) def test_action(self): args_list = [ ['action', 'list'], ['action', 'get', 'abc'], ['action', 'create', '/tmp/action.json'], ['action', 'update', '123', '/tmp/action.json'], ['action', 'delete', 'abc'], ['action', 'execute', '-h'], ['action', 'execute', 'remote', '-h'], [ 'action', 'execute', 'remote', 'hosts=192.168.1.1', 'user=st2', 'cmd="ls -l"' ], ['action', 'execute', 'remote-fib', 'hosts=192.168.1.1', '3', '8'] ] self._validate_parser(args_list) def test_action_execution(self): args_list = [['execution', 'list'], ['execution', 'list', '-a', 'all'], ['execution', 'list', '--attr=all'], ['execution', 'get', '123'], ['execution', 'get', '123', '-d'], ['execution', 'get', '123', '-k', 'localhost.stdout'], ['execution', 're-run', '123'], ['execution', 're-run', '123', '--tasks', 'x', 'y', 'z'], [ 'execution', 're-run', '123', '--tasks', 'x', 'y', 'z', '--no-reset', 'x' ], ['execution', 're-run', '123', 'a=1', 'b=x', 'c=True'], ['execution', 'cancel', '123'], ['execution', 'cancel', '123', '456'], ['execution', 'pause', '123'], ['execution', 'pause', '123', '456'], ['execution', 'resume', '123'], ['execution', 'resume', '123', '456']] self._validate_parser(args_list) # Test mutually exclusive argument groups self.assertRaises( SystemExit, self._validate_parser, [['execution', 'get', '123', '-d', '-k', 'localhost.stdout']]) def test_key(self): args_list = [['key', 'list'], ['key', 'list', '-n', '2'], ['key', 'get', 'abc'], ['key', 'set', 'abc', '123'], ['key', 'delete', 'abc'], ['key', 'load', '/tmp/keys.json']] self._validate_parser(args_list) def test_policy(self): args_list = [ ['policy', 'list'], ['policy', 'list', '-p', 'core'], ['policy', 'list', '--pack', 'core'], ['policy', 'list', '-r', 'core.local'], ['policy', 'list', '--resource-ref', 'core.local'], ['policy', 'list', '-pt', 'action.type1'], ['policy', 'list', '--policy-type', 'action.type1'], ['policy', 'list', '-r', 'core.local', '-pt', 'action.type1'], [ 'policy', 'list', '--resource-ref', 'core.local', '--policy-type', 'action.type1' ], ['policy', 'get', 'abc'], ['policy', 'create', '/tmp/policy.json'], ['policy', 'update', '123', '/tmp/policy.json'], ['policy', 'delete', 'abc'] ] self._validate_parser(args_list) def test_policy_type(self): args_list = [['policy-type', 'list'], ['policy-type', 'list', '-r', 'action'], ['policy-type', 'list', '--resource-type', 'action'], ['policy-type', 'get', 'abc']] self._validate_parser(args_list) def test_pack(self): args_list = [['pack', 'list'], ['pack', 'get', 'abc'], ['pack', 'search', 'abc'], ['pack', 'show', 'abc'], ['pack', 'remove', 'abc'], ['pack', 'remove', 'abc', '--detail'], ['pack', 'install', 'abc'], ['pack', 'install', 'abc', '--force'], ['pack', 'install', 'abc', '--detail'], ['pack', 'config', 'abc']] self._validate_parser(args_list) @mock.patch('st2client.base.ST2_CONFIG_PATH', '/home/does/not/exist') def test_print_config_default_config_no_config(self): os.environ['ST2_CONFIG_FILE'] = '/home/does/not/exist' argv = ['--print-config'] self.assertEqual(self.shell.run(argv), 3) self.stdout.seek(0) stdout = self.stdout.read() self.assertIn('username = None', stdout) self.assertIn('cache_token = True', stdout) def test_print_config_custom_config_as_env_variable(self): os.environ['ST2_CONFIG_FILE'] = CONFIG_FILE_PATH_FULL argv = ['--print-config'] self.assertEqual(self.shell.run(argv), 3) self.stdout.seek(0) stdout = self.stdout.read() self.assertIn('username = test1', stdout) self.assertIn('cache_token = False', stdout) def test_print_config_custom_config_as_command_line_argument(self): argv = ['--print-config', '--config-file=%s' % (CONFIG_FILE_PATH_FULL)] self.assertEqual(self.shell.run(argv), 3) self.stdout.seek(0) stdout = self.stdout.read() self.assertIn('username = test1', stdout) self.assertIn('cache_token = False', stdout) def test_run(self): args_list = [['run', '-h'], ['run', 'abc', '-h'], [ 'run', 'remote', 'hosts=192.168.1.1', 'user=st2', 'cmd="ls -l"' ], ['run', 'remote-fib', 'hosts=192.168.1.1', '3', '8']] self._validate_parser(args_list, is_subcommand=False) def test_runner(self): args_list = [['runner', 'list'], ['runner', 'get', 'abc']] self._validate_parser(args_list) def test_rule(self): args_list = [['rule', 'list'], ['rule', 'list', '-n', '1'], ['rule', 'get', 'abc'], ['rule', 'create', '/tmp/rule.json'], ['rule', 'update', '123', '/tmp/rule.json'], ['rule', 'delete', 'abc']] self._validate_parser(args_list) def test_trigger(self): args_list = [['trigger', 'list'], ['trigger', 'get', 'abc'], ['trigger', 'create', '/tmp/trigger.json'], ['trigger', 'update', '123', '/tmp/trigger.json'], ['trigger', 'delete', 'abc']] self._validate_parser(args_list) def test_workflow(self): args_list = [[ 'workflow', 'inspect', '--file', '/path/to/workflow/definition' ], ['workflow', 'inspect', '--action', 'mock.foobar']] self._validate_parser(args_list) @mock.patch('sys.exit', mock.Mock()) @mock.patch('st2client.shell.__version__', 'v2.8.0') def test_get_version_no_package_metadata_file_stable_version(self): # stable version, package metadata file doesn't exist on disk - no git revision should be # included shell = Shell() shell.parser.parse_args(args=['--version']) self.version_output.seek(0) stderr = self.version_output.read() self.assertIn('v2.8.0, on Python', stderr) @mock.patch('sys.exit', mock.Mock()) @mock.patch('st2client.shell.__version__', 'v2.8.0') def test_get_version_package_metadata_file_exists_stable_version(self): # stable version, package metadata file exists on disk - no git revision should be included package_metadata_path = self._write_mock_package_metadata_file() st2client.shell.PACKAGE_METADATA_FILE_PATH = package_metadata_path shell = Shell() shell.run(argv=['--version']) self.version_output.seek(0) stderr = self.version_output.read() self.assertIn('v2.8.0, on Python', stderr) @mock.patch('sys.exit', mock.Mock()) @mock.patch('st2client.shell.__version__', 'v2.9dev') @mock.patch('st2client.shell.PACKAGE_METADATA_FILE_PATH', '/tmp/doesnt/exist.1') def test_get_version_no_package_metadata_file_dev_version(self): # dev version, package metadata file doesn't exist on disk - no git revision should be # included since package metadata file doesn't exist on disk shell = Shell() shell.parser.parse_args(args=['--version']) self.version_output.seek(0) stderr = self.version_output.read() self.assertIn('v2.9dev, on Python', stderr) @mock.patch('sys.exit', mock.Mock()) @mock.patch('st2client.shell.__version__', 'v2.9dev') def test_get_version_package_metadata_file_exists_dev_version(self): # dev version, package metadata file exists on disk - git revision should be included # since package metadata file exists on disk and contains server.git_sha attribute package_metadata_path = self._write_mock_package_metadata_file() st2client.shell.PACKAGE_METADATA_FILE_PATH = package_metadata_path shell = Shell() shell.parser.parse_args(args=['--version']) self.version_output.seek(0) stderr = self.version_output.read() self.assertIn('v2.9dev (abcdefg), on Python', stderr) @mock.patch('locale.getdefaultlocale', mock.Mock(return_value=['en_US'])) @mock.patch('locale.getpreferredencoding', mock.Mock(return_value='iso')) @mock.patch.object(httpclient.HTTPClient, 'get', mock.MagicMock(return_value=base.FakeResponse( json.dumps(base.RESOURCES), 200, 'OK'))) @mock.patch('st2client.shell.LOGGER') def test_non_unicode_encoding_locale_warning_is_printed(self, mock_logger): shell = Shell() shell.run(argv=['trigger', 'list']) call_args = mock_logger.warn.call_args[0][0] self.assertIn( 'Locale en_US with encoding iso which is not UTF-8 is used.', call_args) @mock.patch('locale.getdefaultlocale', mock.Mock(side_effect=ValueError('bar'))) @mock.patch('locale.getpreferredencoding', mock.Mock(side_effect=ValueError('bar'))) @mock.patch.object(httpclient.HTTPClient, 'get', mock.MagicMock(return_value=base.FakeResponse( json.dumps(base.RESOURCES), 200, 'OK'))) @mock.patch('st2client.shell.LOGGER') def test_failed_to_get_locale_encoding_warning_is_printed( self, mock_logger): shell = Shell() shell.run(argv=['trigger', 'list']) call_args = mock_logger.warn.call_args[0][0] self.assertTrue( 'Locale unknown with encoding unknown which is not UTF-8 is used.' in call_args) def _write_mock_package_metadata_file(self): _, package_metadata_path = tempfile.mkstemp() with open(package_metadata_path, 'w') as fp: fp.write(MOCK_PACKAGE_METADATA) return package_metadata_path @unittest2.skipIf(True, 'skipping until checks are re-enabled') @mock.patch.object( requests, 'get', mock.MagicMock(return_value=base.FakeResponse("{}", 200, 'OK'))) def test_dont_warn_multiple_times(self): mock_temp_dir_path = tempfile.mkdtemp() mock_config_dir_path = os.path.join(mock_temp_dir_path, 'testconfig') mock_config_path = os.path.join(mock_config_dir_path, 'config') # Make the temporary config directory os.makedirs(mock_config_dir_path) old_perms = os.stat(mock_config_dir_path).st_mode new_perms = old_perms | 0o7 os.chmod(mock_config_dir_path, new_perms) # Make the temporary config file shutil.copyfile(CONFIG_FILE_PATH_FULL, mock_config_path) os.chmod(mock_config_path, 0o777) # nosec shell = Shell() shell.LOG = mock.Mock() # Test without token. shell.run(['--config-file', mock_config_path, 'action', 'list']) self.assertEqual(shell.LOG.warn.call_count, 2) self.assertEqual( shell.LOG.warn.call_args_list[0][0][0][:63], 'The StackStorm configuration directory permissions are insecure') self.assertEqual( shell.LOG.warn.call_args_list[1][0][0][:58], 'The StackStorm configuration file permissions are insecure') self.assertEqual(shell.LOG.info.call_count, 2) self.assertEqual( shell.LOG.info.call_args_list[0][0][0], "The SGID bit is not " "set on the StackStorm configuration directory.") self.assertEqual(shell.LOG.info.call_args_list[1][0][0], 'Skipping parsing CLI config')
class ShellTestCase(base.BaseCLITestCase): capture_output = True def __init__(self, *args, **kwargs): super(ShellTestCase, self).__init__(*args, **kwargs) self.shell = Shell() def setUp(self): super(ShellTestCase, self).setUp() if six.PY3: # In python --version outputs to stdout and in 2.x to stderr self.version_output = self.stdout else: self.version_output = self.stderr def test_commands_usage_and_help_strings(self): # No command, should print out user friendly usage / help string self.assertEqual(self.shell.run([]), 2) self.stderr.seek(0) stderr = self.stderr.read() self.assertTrue('Usage: ' in stderr) self.assertTrue('For example:' in stderr) self.assertTrue('CLI for StackStorm' in stderr) self.assertTrue('positional arguments:' in stderr) self.stdout.truncate() self.stderr.truncate() # --help should result in the same output try: self.assertEqual(self.shell.run(['--help']), 0) except SystemExit as e: self.assertEqual(e.code, 0) self.stdout.seek(0) stdout = self.stdout.read() self.assertTrue('Usage: ' in stdout) self.assertTrue('For example:' in stdout) self.assertTrue('CLI for StackStorm' in stdout) self.assertTrue('positional arguments:' in stdout) self.stdout.truncate() self.stderr.truncate() # Sub command with no args try: self.assertEqual(self.shell.run(['action']), 2) except SystemExit as e: self.assertEqual(e.code, 2) self.stderr.seek(0) stderr = self.stderr.read() self.assertTrue('usage' in stderr) if six.PY2: self.assertTrue('{list,get,create,update' in stderr) self.assertTrue('error: too few arguments' in stderr) def test_endpoints_default(self): base_url = 'http://127.0.0.1' auth_url = 'http://127.0.0.1:9100' api_url = 'http://127.0.0.1:9101/v1' stream_url = 'http://127.0.0.1:9102/v1' args = ['trigger', 'list'] parsed_args = self.shell.parser.parse_args(args) client = self.shell.get_client(parsed_args) self.assertEqual(client.endpoints['base'], base_url) self.assertEqual(client.endpoints['auth'], auth_url) self.assertEqual(client.endpoints['api'], api_url) self.assertEqual(client.endpoints['stream'], stream_url) def test_endpoints_base_url_from_cli(self): base_url = 'http://www.st2.com' auth_url = 'http://www.st2.com:9100' api_url = 'http://www.st2.com:9101/v1' stream_url = 'http://www.st2.com:9102/v1' args = ['--url', base_url, 'trigger', 'list'] parsed_args = self.shell.parser.parse_args(args) client = self.shell.get_client(parsed_args) self.assertEqual(client.endpoints['base'], base_url) self.assertEqual(client.endpoints['auth'], auth_url) self.assertEqual(client.endpoints['api'], api_url) self.assertEqual(client.endpoints['stream'], stream_url) def test_endpoints_base_url_from_env(self): base_url = 'http://www.st2.com' auth_url = 'http://www.st2.com:9100' api_url = 'http://www.st2.com:9101/v1' stream_url = 'http://www.st2.com:9102/v1' os.environ['ST2_BASE_URL'] = base_url args = ['trigger', 'list'] parsed_args = self.shell.parser.parse_args(args) client = self.shell.get_client(parsed_args) self.assertEqual(client.endpoints['base'], base_url) self.assertEqual(client.endpoints['auth'], auth_url) self.assertEqual(client.endpoints['api'], api_url) self.assertEqual(client.endpoints['stream'], stream_url) def test_endpoints_override_from_cli(self): base_url = 'http://www.st2.com' auth_url = 'http://www.st2.com:8888' api_url = 'http://www.stackstorm1.com:9101/v1' stream_url = 'http://www.stackstorm1.com:9102/v1' args = ['--url', base_url, '--auth-url', auth_url, '--api-url', api_url, '--stream-url', stream_url, 'trigger', 'list'] parsed_args = self.shell.parser.parse_args(args) client = self.shell.get_client(parsed_args) self.assertEqual(client.endpoints['base'], base_url) self.assertEqual(client.endpoints['auth'], auth_url) self.assertEqual(client.endpoints['api'], api_url) self.assertEqual(client.endpoints['stream'], stream_url) def test_endpoints_override_from_env(self): base_url = 'http://www.st2.com' auth_url = 'http://www.st2.com:8888' api_url = 'http://www.stackstorm1.com:9101/v1' stream_url = 'http://www.stackstorm1.com:9102/v1' os.environ['ST2_BASE_URL'] = base_url os.environ['ST2_AUTH_URL'] = auth_url os.environ['ST2_API_URL'] = api_url os.environ['ST2_STREAM_URL'] = stream_url args = ['trigger', 'list'] parsed_args = self.shell.parser.parse_args(args) client = self.shell.get_client(parsed_args) self.assertEqual(client.endpoints['base'], base_url) self.assertEqual(client.endpoints['auth'], auth_url) self.assertEqual(client.endpoints['api'], api_url) self.assertEqual(client.endpoints['stream'], stream_url) @mock.patch.object( httpclient.HTTPClient, 'get', mock.MagicMock(return_value=base.FakeResponse(json.dumps(base.RESOURCES), 200, 'OK'))) def test_exit_code_on_success(self): argv = ['trigger', 'list'] self.assertEqual(self.shell.run(argv), 0) @mock.patch.object( httpclient.HTTPClient, 'get', mock.MagicMock(return_value=base.FakeResponse(None, 500, 'INTERNAL SERVER ERROR'))) def test_exit_code_on_error(self): argv = ['trigger', 'list'] self.assertEqual(self.shell.run(argv), 1) def _validate_parser(self, args_list, is_subcommand=True): for args in args_list: ns = self.shell.parser.parse_args(args) func = (self.shell.commands[args[0]].run_and_print if not is_subcommand else self.shell.commands[args[0]].commands[args[1]].run_and_print) self.assertEqual(ns.func, func) def test_action(self): args_list = [ ['action', 'list'], ['action', 'get', 'abc'], ['action', 'create', '/tmp/action.json'], ['action', 'update', '123', '/tmp/action.json'], ['action', 'delete', 'abc'], ['action', 'execute', '-h'], ['action', 'execute', 'remote', '-h'], ['action', 'execute', 'remote', 'hosts=192.168.1.1', 'user=st2', 'cmd="ls -l"'], ['action', 'execute', 'remote-fib', 'hosts=192.168.1.1', '3', '8'] ] self._validate_parser(args_list) def test_action_execution(self): args_list = [ ['execution', 'list'], ['execution', 'list', '-a', 'all'], ['execution', 'list', '--attr=all'], ['execution', 'get', '123'], ['execution', 'get', '123', '-d'], ['execution', 'get', '123', '-k', 'localhost.stdout'], ['execution', 're-run', '123'], ['execution', 're-run', '123', '--tasks', 'x', 'y', 'z'], ['execution', 're-run', '123', '--tasks', 'x', 'y', 'z', '--no-reset', 'x'], ['execution', 're-run', '123', 'a=1', 'b=x', 'c=True'], ['execution', 'cancel', '123'], ['execution', 'cancel', '123', '456'], ['execution', 'pause', '123'], ['execution', 'pause', '123', '456'], ['execution', 'resume', '123'], ['execution', 'resume', '123', '456'] ] self._validate_parser(args_list) # Test mutually exclusive argument groups self.assertRaises(SystemExit, self._validate_parser, [['execution', 'get', '123', '-d', '-k', 'localhost.stdout']]) def test_key(self): args_list = [ ['key', 'list'], ['key', 'list', '-n', '2'], ['key', 'get', 'abc'], ['key', 'set', 'abc', '123'], ['key', 'delete', 'abc'], ['key', 'load', '/tmp/keys.json'] ] self._validate_parser(args_list) def test_policy(self): args_list = [ ['policy', 'list'], ['policy', 'list', '-p', 'core'], ['policy', 'list', '--pack', 'core'], ['policy', 'list', '-r', 'core.local'], ['policy', 'list', '--resource-ref', 'core.local'], ['policy', 'list', '-pt', 'action.type1'], ['policy', 'list', '--policy-type', 'action.type1'], ['policy', 'list', '-r', 'core.local', '-pt', 'action.type1'], ['policy', 'list', '--resource-ref', 'core.local', '--policy-type', 'action.type1'], ['policy', 'get', 'abc'], ['policy', 'create', '/tmp/policy.json'], ['policy', 'update', '123', '/tmp/policy.json'], ['policy', 'delete', 'abc'] ] self._validate_parser(args_list) def test_policy_type(self): args_list = [ ['policy-type', 'list'], ['policy-type', 'list', '-r', 'action'], ['policy-type', 'list', '--resource-type', 'action'], ['policy-type', 'get', 'abc'] ] self._validate_parser(args_list) def test_pack(self): args_list = [ ['pack', 'list'], ['pack', 'get', 'abc'], ['pack', 'search', 'abc'], ['pack', 'show', 'abc'], ['pack', 'remove', 'abc'], ['pack', 'remove', 'abc', '--detail'], ['pack', 'install', 'abc'], ['pack', 'install', 'abc', '--force'], ['pack', 'install', 'abc', '--detail'], ['pack', 'config', 'abc'] ] self._validate_parser(args_list) @mock.patch('st2client.base.ST2_CONFIG_PATH', '/home/does/not/exist') def test_print_config_default_config_no_config(self): os.environ['ST2_CONFIG_FILE'] = '/home/does/not/exist' argv = ['--print-config'] self.assertEqual(self.shell.run(argv), 3) self.stdout.seek(0) stdout = self.stdout.read() self.assertTrue('username = None' in stdout) self.assertTrue('cache_token = True' in stdout) def test_print_config_custom_config_as_env_variable(self): os.environ['ST2_CONFIG_FILE'] = CONFIG_FILE_PATH_FULL argv = ['--print-config'] self.assertEqual(self.shell.run(argv), 3) self.stdout.seek(0) stdout = self.stdout.read() self.assertTrue('username = test1' in stdout) self.assertTrue('cache_token = False' in stdout) def test_print_config_custom_config_as_command_line_argument(self): argv = ['--print-config', '--config-file=%s' % (CONFIG_FILE_PATH_FULL)] self.assertEqual(self.shell.run(argv), 3) self.stdout.seek(0) stdout = self.stdout.read() self.assertTrue('username = test1' in stdout) self.assertTrue('cache_token = False' in stdout) def test_run(self): args_list = [ ['run', '-h'], ['run', 'abc', '-h'], ['run', 'remote', 'hosts=192.168.1.1', 'user=st2', 'cmd="ls -l"'], ['run', 'remote-fib', 'hosts=192.168.1.1', '3', '8'] ] self._validate_parser(args_list, is_subcommand=False) def test_runner(self): args_list = [ ['runner', 'list'], ['runner', 'get', 'abc'] ] self._validate_parser(args_list) def test_rule(self): args_list = [ ['rule', 'list'], ['rule', 'list', '-n', '1'], ['rule', 'get', 'abc'], ['rule', 'create', '/tmp/rule.json'], ['rule', 'update', '123', '/tmp/rule.json'], ['rule', 'delete', 'abc'] ] self._validate_parser(args_list) def test_trigger(self): args_list = [ ['trigger', 'list'], ['trigger', 'get', 'abc'], ['trigger', 'create', '/tmp/trigger.json'], ['trigger', 'update', '123', '/tmp/trigger.json'], ['trigger', 'delete', 'abc'] ] self._validate_parser(args_list) def test_workflow(self): args_list = [ ['workflow', 'inspect', '--file', '/path/to/workflow/definition'], ['workflow', 'inspect', '--action', 'mock.foobar'] ] self._validate_parser(args_list) @mock.patch('sys.exit', mock.Mock()) @mock.patch('st2client.shell.__version__', 'v2.8.0') def test_get_version_no_package_metadata_file_stable_version(self): # stable version, package metadata file doesn't exist on disk - no git revision should be # included shell = Shell() shell.parser.parse_args(args=['--version']) self.version_output.seek(0) stderr = self.version_output.read() self.assertTrue('v2.8.0, on Python' in stderr) @mock.patch('sys.exit', mock.Mock()) @mock.patch('st2client.shell.__version__', 'v2.8.0') def test_get_version_package_metadata_file_exists_stable_version(self): # stable version, package metadata file exists on disk - no git revision should be included package_metadata_path = self._write_mock_package_metadata_file() st2client.shell.PACKAGE_METADATA_FILE_PATH = package_metadata_path shell = Shell() shell.run(argv=['--version']) self.version_output.seek(0) stderr = self.version_output.read() self.assertTrue('v2.8.0, on Python' in stderr) @mock.patch('sys.exit', mock.Mock()) @mock.patch('st2client.shell.__version__', 'v2.9dev') @mock.patch('st2client.shell.PACKAGE_METADATA_FILE_PATH', '/tmp/doesnt/exist.1') def test_get_version_no_package_metadata_file_dev_version(self): # dev version, package metadata file doesn't exist on disk - no git revision should be # included since package metadata file doesn't exist on disk shell = Shell() shell.parser.parse_args(args=['--version']) self.version_output.seek(0) stderr = self.version_output.read() self.assertTrue('v2.9dev, on Python' in stderr) @mock.patch('sys.exit', mock.Mock()) @mock.patch('st2client.shell.__version__', 'v2.9dev') def test_get_version_package_metadata_file_exists_dev_version(self): # dev version, package metadata file exists on disk - git revision should be included # since package metadata file exists on disk and contains server.git_sha attribute package_metadata_path = self._write_mock_package_metadata_file() st2client.shell.PACKAGE_METADATA_FILE_PATH = package_metadata_path shell = Shell() shell.parser.parse_args(args=['--version']) self.version_output.seek(0) stderr = self.version_output.read() self.assertTrue('v2.9dev (abcdefg), on Python' in stderr) @mock.patch('locale.getdefaultlocale', mock.Mock(return_value=['en_US'])) @mock.patch('locale.getpreferredencoding', mock.Mock(return_value='iso')) @mock.patch.object( httpclient.HTTPClient, 'get', mock.MagicMock(return_value=base.FakeResponse(json.dumps(base.RESOURCES), 200, 'OK'))) @mock.patch('st2client.shell.LOGGER') def test_non_unicode_encoding_locale_warning_is_printed(self, mock_logger): shell = Shell() shell.run(argv=['trigger', 'list']) call_args = mock_logger.warn.call_args[0][0] self.assertTrue('Locale en_US with encoding iso which is not UTF-8 is used.' in call_args) @mock.patch('locale.getdefaultlocale', mock.Mock(side_effect=ValueError('bar'))) @mock.patch('locale.getpreferredencoding', mock.Mock(side_effect=ValueError('bar'))) @mock.patch.object( httpclient.HTTPClient, 'get', mock.MagicMock(return_value=base.FakeResponse(json.dumps(base.RESOURCES), 200, 'OK'))) @mock.patch('st2client.shell.LOGGER') def test_failed_to_get_locale_encoding_warning_is_printed(self, mock_logger): shell = Shell() shell.run(argv=['trigger', 'list']) call_args = mock_logger.warn.call_args[0][0] self.assertTrue('Locale unknown with encoding unknown which is not UTF-8 is used.' in call_args) def _write_mock_package_metadata_file(self): _, package_metadata_path = tempfile.mkstemp() with open(package_metadata_path, 'w') as fp: fp.write(MOCK_PACKAGE_METADATA) return package_metadata_path @unittest2.skipIf(True, 'skipping until checks are re-enabled') @mock.patch.object( requests, 'get', mock.MagicMock(return_value=base.FakeResponse("{}", 200, 'OK'))) def test_dont_warn_multiple_times(self): mock_temp_dir_path = tempfile.mkdtemp() mock_config_dir_path = os.path.join(mock_temp_dir_path, 'testconfig') mock_config_path = os.path.join(mock_config_dir_path, 'config') # Make the temporary config directory os.makedirs(mock_config_dir_path) old_perms = os.stat(mock_config_dir_path).st_mode new_perms = old_perms | 0o7 os.chmod(mock_config_dir_path, new_perms) # Make the temporary config file shutil.copyfile(CONFIG_FILE_PATH_FULL, mock_config_path) os.chmod(mock_config_path, 0o777) # nosec shell = Shell() shell.LOG = mock.Mock() # Test without token. shell.run(['--config-file', mock_config_path, 'action', 'list']) self.assertEqual(shell.LOG.warn.call_count, 2) self.assertEqual( shell.LOG.warn.call_args_list[0][0][0][:63], 'The StackStorm configuration directory permissions are insecure') self.assertEqual( shell.LOG.warn.call_args_list[1][0][0][:58], 'The StackStorm configuration file permissions are insecure') self.assertEqual(shell.LOG.info.call_count, 2) self.assertEqual( shell.LOG.info.call_args_list[0][0][0], "The SGID bit is not " "set on the StackStorm configuration directory.") self.assertEqual( shell.LOG.info.call_args_list[1][0][0], 'Skipping parsing CLI config')
class ShellTestCase(base.BaseCLITestCase): capture_output = True def __init__(self, *args, **kwargs): super(ShellTestCase, self).__init__(*args, **kwargs) self.shell = Shell() def setUp(self): super(ShellTestCase, self).setUp() if six.PY3: # In python --version outputs to stdout and in 2.x to stderr self.version_output = self.stdout else: self.version_output = self.stderr def test_commands_usage_and_help_strings(self): # No command, should print out user friendly usage / help string self.assertEqual(self.shell.run([]), 2) self.stderr.seek(0) stderr = self.stderr.read() self.assertIn("Usage: ", stderr) self.assertIn("For example:", stderr) self.assertIn("CLI for StackStorm", stderr) self.assertIn("positional arguments:", stderr) self.stdout.truncate() self.stderr.truncate() # --help should result in the same output try: self.assertEqual(self.shell.run(["--help"]), 0) except SystemExit as e: self.assertEqual(e.code, 0) self.stdout.seek(0) stdout = self.stdout.read() self.assertIn("Usage: ", stdout) self.assertIn("For example:", stdout) self.assertIn("CLI for StackStorm", stdout) self.assertIn("positional arguments:", stdout) self.stdout.truncate() self.stderr.truncate() # Sub command with no args try: self.assertEqual(self.shell.run(["action"]), 2) except SystemExit as e: self.assertEqual(e.code, 2) self.stderr.seek(0) stderr = self.stderr.read() self.assertIn("usage", stderr) if six.PY2: self.assertIn("{list,get,create,update", stderr) self.assertIn("error: too few arguments", stderr) def test_endpoints_default(self): base_url = "http://127.0.0.1" auth_url = "http://127.0.0.1:9100" api_url = "http://127.0.0.1:9101/v1" stream_url = "http://127.0.0.1:9102/v1" args = ["trigger", "list"] parsed_args = self.shell.parser.parse_args(args) client = self.shell.get_client(parsed_args) self.assertEqual(client.endpoints["base"], base_url) self.assertEqual(client.endpoints["auth"], auth_url) self.assertEqual(client.endpoints["api"], api_url) self.assertEqual(client.endpoints["stream"], stream_url) def test_endpoints_base_url_from_cli(self): base_url = "http://www.st2.com" auth_url = "http://www.st2.com:9100" api_url = "http://www.st2.com:9101/v1" stream_url = "http://www.st2.com:9102/v1" args = ["--url", base_url, "trigger", "list"] parsed_args = self.shell.parser.parse_args(args) client = self.shell.get_client(parsed_args) self.assertEqual(client.endpoints["base"], base_url) self.assertEqual(client.endpoints["auth"], auth_url) self.assertEqual(client.endpoints["api"], api_url) self.assertEqual(client.endpoints["stream"], stream_url) def test_endpoints_base_url_from_env(self): base_url = "http://www.st2.com" auth_url = "http://www.st2.com:9100" api_url = "http://www.st2.com:9101/v1" stream_url = "http://www.st2.com:9102/v1" os.environ["ST2_BASE_URL"] = base_url args = ["trigger", "list"] parsed_args = self.shell.parser.parse_args(args) client = self.shell.get_client(parsed_args) self.assertEqual(client.endpoints["base"], base_url) self.assertEqual(client.endpoints["auth"], auth_url) self.assertEqual(client.endpoints["api"], api_url) self.assertEqual(client.endpoints["stream"], stream_url) def test_endpoints_override_from_cli(self): base_url = "http://www.st2.com" auth_url = "http://www.st2.com:8888" api_url = "http://www.stackstorm1.com:9101/v1" stream_url = "http://www.stackstorm1.com:9102/v1" args = [ "--url", base_url, "--auth-url", auth_url, "--api-url", api_url, "--stream-url", stream_url, "trigger", "list", ] parsed_args = self.shell.parser.parse_args(args) client = self.shell.get_client(parsed_args) self.assertEqual(client.endpoints["base"], base_url) self.assertEqual(client.endpoints["auth"], auth_url) self.assertEqual(client.endpoints["api"], api_url) self.assertEqual(client.endpoints["stream"], stream_url) def test_endpoints_override_from_env(self): base_url = "http://www.st2.com" auth_url = "http://www.st2.com:8888" api_url = "http://www.stackstorm1.com:9101/v1" stream_url = "http://www.stackstorm1.com:9102/v1" os.environ["ST2_BASE_URL"] = base_url os.environ["ST2_AUTH_URL"] = auth_url os.environ["ST2_API_URL"] = api_url os.environ["ST2_STREAM_URL"] = stream_url args = ["trigger", "list"] parsed_args = self.shell.parser.parse_args(args) client = self.shell.get_client(parsed_args) self.assertEqual(client.endpoints["base"], base_url) self.assertEqual(client.endpoints["auth"], auth_url) self.assertEqual(client.endpoints["api"], api_url) self.assertEqual(client.endpoints["stream"], stream_url) @mock.patch.object( httpclient.HTTPClient, "get", mock.MagicMock(return_value=base.FakeResponse( json.dumps(base.RESOURCES), 200, "OK")), ) def test_exit_code_on_success(self): argv = ["trigger", "list"] self.assertEqual(self.shell.run(argv), 0) @mock.patch.object( httpclient.HTTPClient, "get", mock.MagicMock(return_value=base.FakeResponse( None, 500, "INTERNAL SERVER ERROR")), ) def test_exit_code_on_error(self): argv = ["trigger", "list"] self.assertEqual(self.shell.run(argv), 1) def _validate_parser(self, args_list, is_subcommand=True): for args in args_list: ns = self.shell.parser.parse_args(args) func = ( self.shell.commands[args[0]].run_and_print if not is_subcommand else self.shell.commands[args[0]].commands[args[1]].run_and_print) self.assertEqual(ns.func, func) def test_action(self): args_list = [ ["action", "list"], ["action", "get", "abc"], ["action", "create", "/tmp/action.json"], ["action", "update", "123", "/tmp/action.json"], ["action", "delete", "abc"], ["action", "execute", "-h"], ["action", "execute", "remote", "-h"], [ "action", "execute", "remote", "hosts=192.168.1.1", "user=st2", 'cmd="ls -l"', ], ["action", "execute", "remote-fib", "hosts=192.168.1.1", "3", "8"], ] self._validate_parser(args_list) def test_action_execution(self): args_list = [ ["execution", "list"], ["execution", "list", "-a", "all"], ["execution", "list", "--attr=all"], ["execution", "get", "123"], ["execution", "get", "123", "-d"], ["execution", "get", "123", "-k", "localhost.stdout"], ["execution", "re-run", "123"], ["execution", "re-run", "123", "--tasks", "x", "y", "z"], [ "execution", "re-run", "123", "--tasks", "x", "y", "z", "--no-reset", "x" ], ["execution", "re-run", "123", "a=1", "b=x", "c=True"], ["execution", "cancel", "123"], ["execution", "cancel", "123", "456"], ["execution", "pause", "123"], ["execution", "pause", "123", "456"], ["execution", "resume", "123"], ["execution", "resume", "123", "456"], ] self._validate_parser(args_list) # Test mutually exclusive argument groups self.assertRaises( SystemExit, self._validate_parser, [["execution", "get", "123", "-d", "-k", "localhost.stdout"]], ) def test_key(self): args_list = [ ["key", "list"], ["key", "list", "-n", "2"], ["key", "get", "abc"], ["key", "set", "abc", "123"], ["key", "delete", "abc"], ["key", "load", "/tmp/keys.json"], ] self._validate_parser(args_list) def test_policy(self): args_list = [ ["policy", "list"], ["policy", "list", "-p", "core"], ["policy", "list", "--pack", "core"], ["policy", "list", "-r", "core.local"], ["policy", "list", "--resource-ref", "core.local"], ["policy", "list", "-pt", "action.type1"], ["policy", "list", "--policy-type", "action.type1"], ["policy", "list", "-r", "core.local", "-pt", "action.type1"], [ "policy", "list", "--resource-ref", "core.local", "--policy-type", "action.type1", ], ["policy", "get", "abc"], ["policy", "create", "/tmp/policy.json"], ["policy", "update", "123", "/tmp/policy.json"], ["policy", "delete", "abc"], ] self._validate_parser(args_list) def test_policy_type(self): args_list = [ ["policy-type", "list"], ["policy-type", "list", "-r", "action"], ["policy-type", "list", "--resource-type", "action"], ["policy-type", "get", "abc"], ] self._validate_parser(args_list) def test_pack(self): args_list = [ ["pack", "list"], ["pack", "get", "abc"], ["pack", "search", "abc"], ["pack", "show", "abc"], ["pack", "remove", "abc"], ["pack", "remove", "abc", "--detail"], ["pack", "install", "abc"], ["pack", "install", "abc", "--force"], ["pack", "install", "abc", "--detail"], ["pack", "config", "abc"], ] self._validate_parser(args_list) @mock.patch("st2client.base.ST2_CONFIG_PATH", "/home/does/not/exist") def test_print_config_default_config_no_config(self): os.environ["ST2_CONFIG_FILE"] = "/home/does/not/exist" argv = ["--print-config"] self.assertEqual(self.shell.run(argv), 3) self.stdout.seek(0) stdout = self.stdout.read() self.assertIn("username = None", stdout) self.assertIn("cache_token = True", stdout) def test_print_config_custom_config_as_env_variable(self): os.environ["ST2_CONFIG_FILE"] = CONFIG_FILE_PATH_FULL argv = ["--print-config"] self.assertEqual(self.shell.run(argv), 3) self.stdout.seek(0) stdout = self.stdout.read() self.assertIn("username = test1", stdout) self.assertIn("cache_token = False", stdout) def test_print_config_custom_config_as_command_line_argument(self): argv = ["--print-config", "--config-file=%s" % (CONFIG_FILE_PATH_FULL)] self.assertEqual(self.shell.run(argv), 3) self.stdout.seek(0) stdout = self.stdout.read() self.assertIn("username = test1", stdout) self.assertIn("cache_token = False", stdout) def test_run(self): args_list = [ ["run", "-h"], ["run", "abc", "-h"], ["run", "remote", "hosts=192.168.1.1", "user=st2", 'cmd="ls -l"'], ["run", "remote-fib", "hosts=192.168.1.1", "3", "8"], ] self._validate_parser(args_list, is_subcommand=False) def test_runner(self): args_list = [["runner", "list"], ["runner", "get", "abc"]] self._validate_parser(args_list) def test_rule(self): args_list = [ ["rule", "list"], ["rule", "list", "-n", "1"], ["rule", "get", "abc"], ["rule", "create", "/tmp/rule.json"], ["rule", "update", "123", "/tmp/rule.json"], ["rule", "delete", "abc"], ] self._validate_parser(args_list) def test_trigger(self): args_list = [ ["trigger", "list"], ["trigger", "get", "abc"], ["trigger", "create", "/tmp/trigger.json"], ["trigger", "update", "123", "/tmp/trigger.json"], ["trigger", "delete", "abc"], ] self._validate_parser(args_list) def test_workflow(self): args_list = [ ["workflow", "inspect", "--file", "/path/to/workflow/definition"], ["workflow", "inspect", "--action", "mock.foobar"], ] self._validate_parser(args_list) @mock.patch("sys.exit", mock.Mock()) @mock.patch("st2client.shell.__version__", "v2.8.0") def test_get_version_no_package_metadata_file_stable_version(self): # stable version, package metadata file doesn't exist on disk - no git revision should be # included shell = Shell() shell.parser.parse_args(args=["--version"]) self.version_output.seek(0) stderr = self.version_output.read() self.assertIn("v2.8.0, on Python", stderr) @mock.patch("sys.exit", mock.Mock()) @mock.patch("st2client.shell.__version__", "v2.8.0") def test_get_version_package_metadata_file_exists_stable_version(self): # stable version, package metadata file exists on disk - no git revision should be included package_metadata_path = self._write_mock_package_metadata_file() st2client.shell.PACKAGE_METADATA_FILE_PATH = package_metadata_path shell = Shell() shell.run(argv=["--version"]) self.version_output.seek(0) stderr = self.version_output.read() self.assertIn("v2.8.0, on Python", stderr) @mock.patch("sys.exit", mock.Mock()) @mock.patch("st2client.shell.__version__", "v2.9dev") @mock.patch("st2client.shell.PACKAGE_METADATA_FILE_PATH", "/tmp/doesnt/exist.1") def test_get_version_no_package_metadata_file_dev_version(self): # dev version, package metadata file doesn't exist on disk - no git revision should be # included since package metadata file doesn't exist on disk shell = Shell() shell.parser.parse_args(args=["--version"]) self.version_output.seek(0) stderr = self.version_output.read() self.assertIn("v2.9dev, on Python", stderr) @mock.patch("sys.exit", mock.Mock()) @mock.patch("st2client.shell.__version__", "v2.9dev") def test_get_version_package_metadata_file_exists_dev_version(self): # dev version, package metadata file exists on disk - git revision should be included # since package metadata file exists on disk and contains server.git_sha attribute package_metadata_path = self._write_mock_package_metadata_file() st2client.shell.PACKAGE_METADATA_FILE_PATH = package_metadata_path shell = Shell() shell.parser.parse_args(args=["--version"]) self.version_output.seek(0) stderr = self.version_output.read() self.assertIn("v2.9dev (abcdefg), on Python", stderr) @mock.patch("locale.getdefaultlocale", mock.Mock(return_value=["en_US"])) @mock.patch("locale.getpreferredencoding", mock.Mock(return_value="iso")) @mock.patch.object( httpclient.HTTPClient, "get", mock.MagicMock(return_value=base.FakeResponse( json.dumps(base.RESOURCES), 200, "OK")), ) @mock.patch("st2client.shell.LOGGER") def test_non_unicode_encoding_locale_warning_is_printed(self, mock_logger): shell = Shell() shell.run(argv=["trigger", "list"]) call_args = mock_logger.warn.call_args[0][0] self.assertIn( "Locale en_US with encoding iso which is not UTF-8 is used.", call_args) @mock.patch("locale.getdefaultlocale", mock.Mock(side_effect=ValueError("bar"))) @mock.patch("locale.getpreferredencoding", mock.Mock(side_effect=ValueError("bar"))) @mock.patch.object( httpclient.HTTPClient, "get", mock.MagicMock(return_value=base.FakeResponse( json.dumps(base.RESOURCES), 200, "OK")), ) @mock.patch("st2client.shell.LOGGER") def test_failed_to_get_locale_encoding_warning_is_printed( self, mock_logger): shell = Shell() shell.run(argv=["trigger", "list"]) call_args = mock_logger.warn.call_args[0][0] self.assertTrue( "Locale unknown with encoding unknown which is not UTF-8 is used." in call_args) def _write_mock_package_metadata_file(self): _, package_metadata_path = tempfile.mkstemp() with open(package_metadata_path, "w") as fp: fp.write(MOCK_PACKAGE_METADATA) return package_metadata_path @unittest2.skipIf(True, "skipping until checks are re-enabled") @mock.patch.object( requests, "get", mock.MagicMock(return_value=base.FakeResponse("{}", 200, "OK"))) def test_dont_warn_multiple_times(self): mock_temp_dir_path = tempfile.mkdtemp() mock_config_dir_path = os.path.join(mock_temp_dir_path, "testconfig") mock_config_path = os.path.join(mock_config_dir_path, "config") # Make the temporary config directory os.makedirs(mock_config_dir_path) old_perms = os.stat(mock_config_dir_path).st_mode new_perms = old_perms | 0o7 os.chmod(mock_config_dir_path, new_perms) # Make the temporary config file shutil.copyfile(CONFIG_FILE_PATH_FULL, mock_config_path) os.chmod(mock_config_path, 0o777) # nosec shell = Shell() shell.LOG = mock.Mock() # Test without token. shell.run(["--config-file", mock_config_path, "action", "list"]) self.assertEqual(shell.LOG.warn.call_count, 2) self.assertEqual( shell.LOG.warn.call_args_list[0][0][0][:63], "The StackStorm configuration directory permissions are insecure", ) self.assertEqual( shell.LOG.warn.call_args_list[1][0][0][:58], "The StackStorm configuration file permissions are insecure", ) self.assertEqual(shell.LOG.info.call_count, 2) self.assertEqual( shell.LOG.info.call_args_list[0][0][0], "The SGID bit is not " "set on the StackStorm configuration directory.", ) self.assertEqual(shell.LOG.info.call_args_list[1][0][0], "Skipping parsing CLI config") def test_policy_list_with_pack_option(self): argv = ["policy", "list", "-p", "test"] mock_obj = mock.MagicMock(return_value=base.FakeResponse( json.dumps(base.RESOURCES), 200, "OK")) with mock.patch.object(httpclient.HTTPClient, "get", mock_obj): self.shell.run(argv) self.assertEqual( mock_obj.mock_calls[0], mock.call("/policies/?include_attributes=ref%2Cresource_ref%2C" "policy_type%2Cenabled&pack=test"), )