def test_add_sub_parser_adds_new_subparser(self): hello_said = False cli = CommandLineInterface(['say_hello']) def _callback(): hello_said = True parser = cli.add_sub_parser('say_hello') parser.set_defaults(function=_callback) cli.execute() self.assertTrue(True)
def test_submit_forwards_specified_arguments_to_command_line_job_submission(self): self.patch('foundations_core_cli.job_submission.submit_job.submit', MockCommandLineJobDeployer) expected_arguments = Mock() expected_arguments.scheduler_config = self.fake_env expected_arguments.job_directory = self.fake_directory expected_arguments.entrypoint = self.fake_script_file_name expected_arguments.project_name = self.fake_project_name expected_arguments.ram = self.ram expected_arguments.num_gpus = self.num_gpus expected_arguments.stream_job_logs = False expected_arguments.command = [self.command] command_to_run = [ 'submit', f'--entrypoint={self.fake_script_file_name}', f'--project-name={self.fake_project_name}', f'--ram={self.ram}', f'--num-gpus={self.num_gpus}', f'--stream-job-logs=False', self.fake_env, self.fake_directory, self.command ] CommandLineInterface(command_to_run).execute() arguments = MockCommandLineJobDeployer.arguments self._assert_submit_arguments_equal(expected_arguments, arguments)
def _execute_command_line_interface_in_patched_open(self, hostname): open_mock = mock_open() with patch('builtins.open', open_mock): mock_file = open_mock() try: CommandLineInterface(['login', f'{hostname}']).execute() finally: return mock_file
def test_get_job_logs_for_job_that_exists_and_is_not_queued_does_not_call_exit(self): self._set_job_status(self.fake_job_status) self.mock_job_deployment.get_job_logs.return_value = self.fake_job_logs load_mock = Mock() self.patch('foundations_core_cli.job_submission.config.load', load_mock) load_mock.return_value = None CommandLineInterface(['get', 'logs', self.fake_env, self.mock_job_id]).execute() self.exit_mock.assert_not_called()
def test_correct_option_setup(self, parser_class_mock): mock_str_to_bool = self.patch('foundations_core_cli.command_line_interface.CommandLineInterface._str_to_bool') parser_mock = Mock() parser_class_mock.return_value = parser_mock parser_mock.add_subparsers.return_value = self.level_1_subparsers_mock self.level_1_subparsers_mock.add_parser.return_value = self.level_2_parser_mock self.level_2_parser_mock.add_subparsers.return_value = self.level_2_subparsers_mock self.level_2_subparsers_mock.add_parser.return_value = self.level_3_parser_mock CommandLineInterface([]) parser_class_mock.assert_called_with(prog='foundations') version_call = call('--version', action='store_true', help='Displays the current Foundations version') debug_call = call('--debug', action='store_true', help='Sets debug mode for the CLI') parser_mock.add_argument.assert_has_calls( [ version_call, debug_call ] ) init_call = call('init', help='Creates a new Foundations project in the current directory') info_call = call('info', help='Provides information about your Foundations project') self.level_1_subparsers_mock.add_parser.assert_has_calls( [ init_call, info_call ], any_order=True ) init_argument_call = call('project_name', type=str, help='Name of the project to create') info_argument_env_call = call('--env', action='store_true') self.level_2_parser_mock.add_argument.assert_has_calls( [ init_argument_call, info_argument_env_call ], any_order=True )
def test_submit_forwards_default_arguments_to_command_line_job_submission(self): self.patch('foundations_core_cli.job_submission.submit_job.submit', MockCommandLineJobDeployer) expected_arguments = Mock() expected_arguments.scheduler_config = self.fake_env expected_arguments.job_directory = self.fake_directory expected_arguments.entrypoint = None expected_arguments.project_name = None expected_arguments.ram = None expected_arguments.num_gpus = None expected_arguments.stream_job_logs = True expected_arguments.command = [self.command] CommandLineInterface(['submit', self.fake_env, self.fake_directory, self.command]).execute() arguments = MockCommandLineJobDeployer.arguments self._assert_submit_arguments_equal(expected_arguments, arguments)
def test_retrieve_artifact_has_correct_options(self, parser_class_mock): parser_mock = Mock() parser_class_mock.return_value = parser_mock parser_mock.add_subparsers.return_value = self.level_1_subparsers_mock self.level_1_subparsers_mock.add_parser.return_value = self.level_2_parser_mock self.level_2_parser_mock.add_subparsers.return_value = self.level_2_subparsers_mock self.level_2_subparsers_mock.add_parser.return_value = self.level_3_parser_mock CommandLineInterface([]) parser_class_mock.assert_called_with(prog='foundations') version_call = call('--version', action='store_true', help='Displays the current Foundations version') debug_call = call('--debug', action='store_true', help='Sets debug mode for the CLI') parser_mock.add_argument.assert_has_calls( [ version_call, debug_call ] ) retrieve_call = call('get', help='Get file types from execution environments') self.level_1_subparsers_mock.add_parser.assert_has_calls([retrieve_call]) retrieve_argument_call = call('job', help='Specify job to retrieve artifacts from') job_id_call = call('job_id', type=str, help='Specify job uuid of already deployed job') env_call = call('scheduler_config', type=str, help='Environment to get from') save_directory_call = call('--save_dir', type=str, default=None, help='Specify local directory path for artifacts to save to. Defaults to directory within current working directory') source_directory_call = call('--source_dir', type=str, default='', help='Specify relative directory path to download artifacts from. Default will download all artifacts from job') self.level_2_subparsers_mock.add_parser.assert_has_calls([retrieve_argument_call]) self.level_3_parser_mock.add_argument.assert_has_calls( [ job_id_call, env_call, save_directory_call, source_directory_call ], any_order=True )
def test_retrieve_logs_has_correct_options(self, parser_class_mock): parser_mock = Mock() parser_class_mock.return_value = parser_mock parser_mock.add_subparsers.return_value = self.level_1_subparsers_mock self.level_1_subparsers_mock.add_parser.return_value = self.level_2_parser_mock self.level_2_parser_mock.add_subparsers.return_value = self.level_2_subparsers_mock self.level_2_subparsers_mock.add_parser.return_value = self.level_3_parser_mock CommandLineInterface([]) parser_class_mock.assert_called_with(prog='foundations') version_call = call('--version', action='store_true', help='Displays the current Foundations version') debug_call = call('--debug', action='store_true', help='Sets debug mode for the CLI') parser_mock.add_argument.assert_has_calls( [ version_call, debug_call ] ) retrieve_call = call('get', help='Get file types from execution environments') self.level_1_subparsers_mock.add_parser.assert_has_calls([retrieve_call]) retrieve_argument_call = call('logs', help='Get logs for jobs') job_id_call = call('scheduler_config', type=str, help='Environment to get from') env_call = call('job_id', type=str, help='Specify job uuid of already deployed job') self.level_2_subparsers_mock.add_parser.assert_has_calls([retrieve_argument_call]) self.level_3_parser_mock.add_argument.assert_has_calls( [ job_id_call, env_call ], any_order=True )
def test_scaffold_prints_failure_message_different_project(self): self.scaffold_project_mock.return_value = False CommandLineInterface(['init', 'your project']).execute() self.print_mock.assert_called_with('Error: project directory for `your project` already exists')
def test_scaffold_prints_success_message_different_project(self): self.scaffold_project_mock.return_value = True CommandLineInterface(['init', 'your project']).execute() self.print_mock.assert_called_with('Success: New Foundations project `your project` created!')
def test_scaffold_scaffolds_with_project_name_different_project(self): CommandLineInterface(['init', 'my project']).execute() self.scaffold_project_mock.assert_called()
def test_scaffold_creates_scaffold_with_project_name_different_project(self, scaffold_mock): CommandLineInterface(['init', 'my different project']).execute() scaffold_mock.assert_called_with('my different project')
def test_get_job_logs_for_environment_that_does_not_exist_exits_with_code_1(self): self.find_environment_mock.return_value = [] CommandLineInterface(['get', 'logs', self.fake_env, self.mock_job_id]).execute() self.exit_mock.assert_called_with(1)
def test_retrieve_artifacts_fails_if_missing_environment(self): self.find_environment_mock.return_value = [] CommandLineInterface(['get', 'job', self.fake_env, self.mock_job_id]).execute() self.exit_mock.assert_called_with(1)
def test_info_env_flag_returns_environment_local_and_global_available(self, mock_print): self.environment_fetcher_mock.return_value = (['/home/local.config.yaml'],['~/foundations/local.config.yaml']) CommandLineInterface(['info', '--env']).execute() project_call = call([['local', '/home/local.config.yaml']]) global_call = call([['local','~/foundations/local.config.yaml']]) mock_print.assert_has_calls([project_call, global_call], any_order = True)
def test_get_job_logs_for_job_that_exists_and_is_not_queued_prints_logs(self): self._set_job_status(self.fake_job_status) self.mock_job_deployment.get_job_logs.return_value = self.fake_job_logs self.find_environment_mock.return_value = [self.fake_config_path(self.fake_env)] CommandLineInterface(['get', 'logs', self.fake_env, self.mock_job_id]).execute() self.print_mock.assert_called_with(self.fake_job_logs)
def test_retrieve_artifacts_prints_error_if_missing_environment(self): self.find_environment_mock.return_value = [] CommandLineInterface(['get', 'job', self.fake_env, self.mock_job_id]).execute() self.print_mock.assert_any_call('Could not find submission configuration with name: `{}`'.format(self.fake_env))
def test_get_job_logs_for_queued_job_exits_with_code_1(self): self._set_job_status('queued') self.find_environment_mock.return_value = [self.fake_config_path(self.fake_env)] CommandLineInterface(['get', 'logs', self.fake_env, self.mock_job_id]).execute() self.exit_mock.assert_called_with(1)
def test_get_job_logs_for_queued_job_prints_error_message(self): self._set_job_status('queued') self.find_environment_mock.return_value = [self.fake_config_path(self.fake_env)] CommandLineInterface(['get', 'logs', self.fake_env, self.mock_job_id]).execute() self.print_mock.assert_called_with('Error: Job `{}` is queued and has not produced any logs'.format(self.mock_job_id))
def test_get_job_logs_for_environment_that_exists_for_job_that_does_not_exist_prints_error_message(self): self._set_job_status(None) self.find_environment_mock.return_value = [self.fake_config_path(self.fake_env)] CommandLineInterface(['get', 'logs', self.fake_env, self.mock_job_id]).execute() self.print_mock.assert_called_with('Error: Job `{}` does not exist for environment `{}`'.format(self.mock_job_id, self.fake_env))
def test_info_env_flag_returns_environment_none_available_not_local(self): self.environment_fetcher_mock.return_value = (None, []) CommandLineInterface(['info', '--env']).execute() self.print_mock.assert_called_with('No environments available')
def test_login_with_username_password_makes_token_request_basic_auth(self): CommandLineInterface(f'login {self.hostname} -u {self.username} -p {self.password}'.split()).execute() self.mock_get.assert_called_once_with(self.hostname + '/api/v2beta/auth/cli_login', auth=(self.username, self.password))
def test_info_env_flag_returns_environment_one_available_global_no_local(self, mock_print): self.environment_fetcher_mock.return_value = (None, ['/home/config/uat.config.yaml']) CommandLineInterface(['info', '--env']).execute() mock_print.assert_called_once() mock_print.assert_called_with([['uat', '/home/config/uat.config.yaml']])
def test_login_prints_failure_message_when_credentials_invalid(self): failure_message = self.faker.sentence() self._set_up_mock_get_for_request(400, text=failure_message) CommandLineInterface(['login', f'{self.hostname}']).execute() self.print_mock.assert_has_calls([call("\nLogin Failed!"), call(f"Error response: {failure_message}")])
def test_login_without_username_password_args_prompts_user_for_info(self): CommandLineInterface(['login', f'{self.hostname}']).execute() self.mock_input.assert_called_once() self.mock_getpass.assert_called_once()
def test_get_job_logs_for_environment_that_does_not_exist_prints_error_message(self): self.find_environment_mock.return_value = [] CommandLineInterface(['get', 'logs', self.fake_env, self.mock_job_id]).execute() self.print_mock.assert_any_call('Could not find submission configuration with name: `{}`'.format(self.fake_env))
def test_login_makes_token_request_using_basic_auth(self): self.mock_input.return_value = self.username self.mock_getpass.return_value = self.password CommandLineInterface(['login', f'{self.hostname}']).execute() self.mock_get.assert_called_once_with(self.hostname + '/api/v2beta/auth/cli_login', auth=(self.username, self.password))
def test_execute_spits_out_help(self): with patch('argparse.ArgumentParser.print_help') as mock: CommandLineInterface([]).execute() mock.assert_called()
def test_sub_parser_setup_parser_on_cli_instantiation(self): mock_add_parser = self.patch('foundations_atlas_cli.sub_parsers.atlas.atlas_parser.AtlasParser.add_sub_parser') CommandLineInterface(['']) mock_add_parser.assert_called_once()
def test_execute_spits_out_version_different_version(self): CommandLineInterface(['--version']).execute() self.print_mock.assert_called_with('Running Foundations version 7.3.3')