Path: blob/develop/tests/unit/customizations/s3/test_subcommands.py
1569 views
# Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.1#2# Licensed under the Apache License, Version 2.0 (the "License"). You3# may not use this file except in compliance with the License. A copy of4# the License is located at5#6# http://aws.amazon.com/apache2.0e7#8# or in the "license" file accompanying this file. This file is9# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF10# ANY KIND, either express or implied. See the License for the specific11# language governing permissions and limitations under the License.12import argparse13import os14import sys1516import botocore.session17from awscli.customizations.s3.s3 import S318from awscli.customizations.s3.subcommands import CommandParameters, \19CommandArchitecture, CpCommand, SyncCommand, ListCommand, \20RbCommand, get_client21from awscli.customizations.s3.transferconfig import RuntimeConfig22from awscli.customizations.s3.syncstrategy.base import \23SizeAndLastModifiedSync, NeverSync, MissingFileSync24from awscli.testutils import mock, unittest, BaseAWSHelpOutputTest, \25BaseAWSCommandParamsTest, FileCreator26from tests.unit.customizations.s3 import make_loc_files, clean_loc_files27from awscli.compat import StringIO282930class FakeArgs(object):31def __init__(self, **kwargs):32self.__dict__.update(kwargs)3334def __contains__(self, key):35return key in self.__dict__363738class TestGetClient(unittest.TestCase):39def test_client(self):40session = mock.Mock()41endpoint = get_client(session, region='us-west-1', endpoint_url='URL',42verify=True)43session.create_client.assert_called_with(44's3', region_name='us-west-1', endpoint_url='URL', verify=True,45config=None)464748class TestRbCommand(unittest.TestCase):49def setUp(self):50self.session = mock.Mock()51self.session.get_scoped_config.return_value = {}52self.rb_command = RbCommand(self.session)53self.parsed_args = FakeArgs(path='s3://mybucket/',54force=True, dir_op=False)55self.parsed_globals = FakeArgs(region=None, endpoint_url=None,56verify_ssl=None)57self.cmd_name = 'awscli.customizations.s3.subcommands.RmCommand'58self.arch_name = 'awscli.customizations.s3.subcommands.CommandArchitecture'5960def test_rb_command_with_force_deletes_objects_in_bucket(self):61with mock.patch(self.cmd_name) as rm_command:62with mock.patch(self.arch_name):63# RmCommand returns an RmCommand instance whose __call__64# should be the RC of the command.65# In this case we'll have it return an RC of 0 which indicates66# success.67rm_command.return_value.return_value = 068self.rb_command._run_main(self.parsed_args,69parsed_globals=self.parsed_globals)70# Because of --force we should have called the71# rm_command with the --recursive option.72rm_command.return_value.assert_called_with(73['s3://mybucket/', '--recursive'], mock.ANY)7475def test_rb_command_with_force_requires_strict_path(self):76with self.assertRaises(ValueError):77self.parsed_args.path = 's3://mybucket/mykey'78self.rb_command._run_main(self.parsed_args,79parsed_globals=self.parsed_globals)808182class TestLSCommand(unittest.TestCase):83def setUp(self):84self.session = mock.Mock()85self.session.create_client.return_value.list_buckets.return_value\86= {'Buckets': []}87self.session.create_client.return_value.get_paginator.return_value\88.paginate.return_value = [{'Contents': [], 'CommonPrefixes': []}]8990def _get_fake_kwargs(self, override=None):91fake_kwargs = {92'paths': 's3://',93'dir_op': False,94'human_readable': False,95'summarize': False,96'page_size': None,97'request_payer': None,98'bucket_name_prefix': None,99'bucket_region': None,100}101fake_kwargs.update(override or {})102103return fake_kwargs104105def test_ls_command_for_bucket(self):106ls_command = ListCommand(self.session)107parsed_args = FakeArgs(**self._get_fake_kwargs({108'paths': 's3://mybucket/',109'page_size': '5',110}))111parsed_globals = mock.Mock()112ls_command._run_main(parsed_args, parsed_globals)113call = self.session.create_client.return_value.list_objects_v2114paginate = self.session.create_client.return_value.get_paginator\115.return_value.paginate116# We should make no operation calls.117self.assertEqual(call.call_count, 0)118# And only a single pagination call to ListObjectsV2.119self.session.create_client.return_value.get_paginator.\120assert_called_with('list_objects_v2')121ref_call_args = {'Bucket': u'mybucket', 'Delimiter': '/',122'Prefix': u'',123'PaginationConfig': {'PageSize': u'5'}}124125paginate.assert_called_with(**ref_call_args)126127def test_ls_command_with_no_args(self):128ls_command = ListCommand(self.session)129parsed_global = FakeArgs(region=None, endpoint_url=None,130verify_ssl=None)131parsed_args = FakeArgs(**self._get_fake_kwargs())132ls_command._run_main(parsed_args, parsed_global)133call = self.session.create_client.return_value.list_buckets134paginate = self.session.create_client.return_value.get_paginator\135.return_value.paginate136137# We should make no operation calls.138self.assertEqual(call.call_count, 0)139# And only a single pagination call to ListBuckets.140self.session.create_client.return_value.get_paginator.\141assert_called_with('list_buckets')142ref_call_args = {'PaginationConfig': {'PageSize': None}}143144paginate.assert_called_with(**ref_call_args)145146# Verify get_client147get_client = self.session.create_client148args = get_client.call_args149self.assertEqual(args, mock.call(150's3', region_name=None, endpoint_url=None, verify=None,151config=None))152153def test_ls_with_bucket_name_prefix(self):154ls_command = ListCommand(self.session)155parsed_args = FakeArgs(**self._get_fake_kwargs({156'bucket_name_prefix': 'myprefix',157}))158parsed_globals = FakeArgs(159region=None,160endpoint_url=None,161verify_ssl=None,162)163ls_command._run_main(parsed_args, parsed_globals)164call = self.session.create_client.return_value.list_objects165paginate = self.session.create_client.return_value.get_paginator\166.return_value.paginate167# We should make no operation calls.168self.assertEqual(call.call_count, 0)169self.session.create_client.return_value.get_paginator.\170assert_called_with('list_buckets')171ref_call_args = {172'PaginationConfig': {'PageSize': None},173'Prefix': 'myprefix',174}175176paginate.assert_called_with(**ref_call_args)177178def test_ls_with_bucket_region(self):179ls_command = ListCommand(self.session)180parsed_args = FakeArgs(**self._get_fake_kwargs({181'bucket_region': 'us-west-1',182}))183parsed_globals = FakeArgs(184region=None,185endpoint_url=None,186verify_ssl=None,187)188ls_command._run_main(parsed_args, parsed_globals)189call = self.session.create_client.return_value.list_objects190paginate = self.session.create_client.return_value.get_paginator\191.return_value.paginate192# We should make no operation calls.193self.assertEqual(call.call_count, 0)194self.session.create_client.return_value.get_paginator.\195assert_called_with('list_buckets')196ref_call_args = {197'PaginationConfig': {'PageSize': None},198'BucketRegion': 'us-west-1',199}200201paginate.assert_called_with(**ref_call_args)202203def test_ls_with_verify_argument(self):204ls_command = ListCommand(self.session)205parsed_global = FakeArgs(region='us-west-2', endpoint_url=None,206verify_ssl=False)207parsed_args = FakeArgs(**self._get_fake_kwargs({}))208ls_command._run_main(parsed_args, parsed_global)209# Verify get_client210get_client = self.session.create_client211args = get_client.call_args212self.assertEqual(args, mock.call(213's3', region_name='us-west-2', endpoint_url=None, verify=False,214config=None))215216def test_ls_with_requester_pays(self):217ls_command = ListCommand(self.session)218parsed_args = FakeArgs(**self._get_fake_kwargs({219'paths': 's3://mybucket/',220'page_size': '5',221'request_payer': 'requester',222}))223parsed_globals = mock.Mock()224ls_command._run_main(parsed_args, parsed_globals)225call = self.session.create_client.return_value.list_objects226paginate = self.session.create_client.return_value.get_paginator\227.return_value.paginate228# We should make no operation calls.229self.assertEqual(call.call_count, 0)230# And only a single pagination call to ListObjectsV2.231self.session.create_client.return_value.get_paginator.\232assert_called_with('list_objects_v2')233ref_call_args = {234'Bucket': u'mybucket', 'Delimiter': '/',235'Prefix': u'', 'PaginationConfig': {'PageSize': '5'},236'RequestPayer': 'requester',237}238239paginate.assert_called_with(**ref_call_args)240241242class CommandArchitectureTest(BaseAWSCommandParamsTest):243def setUp(self):244super(CommandArchitectureTest, self).setUp()245self.session = self.driver.session246self.bucket = 'mybucket'247self.file_creator = FileCreator()248self.loc_files = make_loc_files(self.file_creator)249self.output = StringIO()250self.err_output = StringIO()251self.saved_stdout = sys.stdout252self.saved_stderr = sys.stderr253sys.stdout = self.output254sys.stderr = self.err_output255256def tearDown(self):257self.output.close()258self.err_output.close()259sys.stdout = self.saved_stdout260sys.stderr = self.saved_stderr261262super(CommandArchitectureTest, self).tearDown()263clean_loc_files(self.file_creator)264265def _get_file_path(self, file):266try:267return os.path.relpath(file)268except ValueError:269# In some cases (usually it happens inside Windows based GitHub270# Action) tests are situated on one volume and temp folder on271# another one, in such a case there is no relative path between272# them and we use absolute path instead273return os.path.abspath(file)274275def test_set_client_no_source(self):276session = mock.Mock()277cmd_arc = CommandArchitecture(session, 'sync',278{'region': 'us-west-1',279'endpoint_url': None,280'verify_ssl': None,281'source_region': None})282cmd_arc.set_clients()283self.assertEqual(session.create_client.call_count, 2)284self.assertEqual(285session.create_client.call_args_list[0],286mock.call(287's3', region_name='us-west-1', endpoint_url=None, verify=None,288config=None)289)290# A client created with the same arguments as the first should be used291# for the source client since no source region was provided.292self.assertEqual(293session.create_client.call_args_list[1],294mock.call(295's3', region_name='us-west-1', endpoint_url=None, verify=None,296config=None)297)298299def test_set_client_with_source(self):300session = mock.Mock()301cmd_arc = CommandArchitecture(session, 'sync',302{'region': 'us-west-1',303'endpoint_url': None,304'verify_ssl': None,305'paths_type': 's3s3',306'source_region': 'us-west-2'})307cmd_arc.set_clients()308create_client_args = session.create_client.call_args_list309# Assert that two clients were created310self.assertEqual(len(create_client_args), 3)311self.assertEqual(312create_client_args[0][1],313{'region_name': 'us-west-1', 'verify': None, 'endpoint_url': None,314'config': None}315)316self.assertEqual(317create_client_args[1][1],318{'region_name': 'us-west-1', 'verify': None, 'endpoint_url': None,319'config': None}320)321# Assert override the second client created with the one needed for the322# source region.323self.assertEqual(324create_client_args[2][1],325{'region_name': 'us-west-2', 'verify': None, 'endpoint_url': None,326'config': None}327)328329def test_set_sigv4_clients_with_sse_kms(self):330session = mock.Mock()331cmd_arc = CommandArchitecture(332session, 'sync',333{'region': 'us-west-1', 'endpoint_url': None, 'verify_ssl': None,334'source_region': None, 'sse': 'aws:kms'})335cmd_arc.set_clients()336self.assertEqual( session.create_client.call_count, 2)337create_client_call = session.create_client.call_args_list[0]338create_source_client_call = session.create_client.call_args_list[1]339340# Make sure that both clients are using sigv4 if kms is enabled.341self.assertEqual(342create_client_call[1]['config'].signature_version, 's3v4')343self.assertEqual(344create_source_client_call[1]['config'].signature_version, 's3v4')345346def test_create_instructions(self):347"""348This tests to make sure the instructions for any command is generated349properly.350"""351cmds = ['cp', 'mv', 'rm', 'sync']352353instructions = {'cp': ['file_generator', 'file_info_builder',354's3_handler'],355'mv': ['file_generator', 'file_info_builder',356's3_handler'],357'rm': ['file_generator', 'file_info_builder',358's3_handler'],359'sync': ['file_generator', 'comparator',360'file_info_builder', 's3_handler']}361362params = {'filters': True, 'region': 'us-east-1', 'endpoint_url': None,363'verify_ssl': None, 'is_stream': False}364for cmd in cmds:365cmd_arc = CommandArchitecture(self.session, cmd,366{'region': 'us-east-1',367'endpoint_url': None,368'verify_ssl': None,369'is_stream': False})370cmd_arc.create_instructions()371self.assertEqual(cmd_arc.instructions, instructions[cmd])372373# Test if there is a filter.374cmd_arc = CommandArchitecture(self.session, 'cp', params)375cmd_arc.create_instructions()376self.assertEqual(cmd_arc.instructions, ['file_generator', 'filters',377'file_info_builder',378's3_handler'])379380def test_choose_sync_strategy_default(self):381session = mock.Mock()382cmd_arc = CommandArchitecture(session, 'sync',383{'region': 'us-east-1',384'endpoint_url': None,385'verify_ssl': None})386# Check if no plugins return their sync strategy. Should387# result in the default strategies388session.emit.return_value = None389sync_strategies = cmd_arc.choose_sync_strategies()390self.assertEqual(391sync_strategies['file_at_src_and_dest_sync_strategy'].__class__,392SizeAndLastModifiedSync393)394self.assertEqual(395sync_strategies['file_not_at_dest_sync_strategy'].__class__,396MissingFileSync397)398self.assertEqual(399sync_strategies['file_not_at_src_sync_strategy'].__class__,400NeverSync401)402403def test_choose_sync_strategy_overwrite(self):404session = mock.Mock()405cmd_arc = CommandArchitecture(session, 'sync',406{'region': 'us-east-1',407'endpoint_url': None,408'verify_ssl': None})409# Check that the default sync strategy is overwritten if a plugin410# returns its sync strategy.411mock_strategy = mock.Mock()412mock_strategy.sync_type = 'file_at_src_and_dest'413414mock_not_at_dest_sync_strategy = mock.Mock()415mock_not_at_dest_sync_strategy.sync_type = 'file_not_at_dest'416417mock_not_at_src_sync_strategy = mock.Mock()418mock_not_at_src_sync_strategy.sync_type = 'file_not_at_src'419420responses = [(None, mock_strategy),421(None, mock_not_at_dest_sync_strategy),422(None, mock_not_at_src_sync_strategy)]423424session.emit.return_value = responses425sync_strategies = cmd_arc.choose_sync_strategies()426self.assertEqual(427sync_strategies['file_at_src_and_dest_sync_strategy'],428mock_strategy429)430self.assertEqual(431sync_strategies['file_not_at_dest_sync_strategy'],432mock_not_at_dest_sync_strategy433)434self.assertEqual(435sync_strategies['file_not_at_src_sync_strategy'],436mock_not_at_src_sync_strategy437)438439def test_run_cp_put(self):440# This ensures that the architecture sets up correctly for a ``cp`` put441# command. It is just just a dry run, but all of the components need442# to be wired correctly for it to work.443s3_file = 's3://' + self.bucket + '/' + 'text1.txt'444local_file = self.loc_files[0]445rel_local_file = self._get_file_path(local_file)446filters = [['--include', '*']]447params = {'dir_op': False, 'dryrun': True, 'quiet': False,448'src': local_file, 'dest': s3_file, 'filters': filters,449'paths_type': 'locals3', 'region': 'us-east-1',450'endpoint_url': None, 'verify_ssl': None,451'follow_symlinks': True, 'page_size': None,452'is_stream': False, 'source_region': None, 'metadata': None}453config = RuntimeConfig().build_config()454cmd_arc = CommandArchitecture(self.session, 'cp', params, config)455cmd_arc.set_clients()456cmd_arc.create_instructions()457self.patch_make_request()458cmd_arc.run()459output_str = "(dryrun) upload: %s to %s" % (rel_local_file, s3_file)460self.assertIn(output_str, self.output.getvalue())461462def test_error_on_same_line_as_status(self):463s3_file = 's3://' + 'bucket-does-not-exist' + '/' + 'text1.txt'464local_file = self.loc_files[0]465rel_local_file = self._get_file_path(local_file)466filters = [['--include', '*']]467params = {'dir_op': False, 'dryrun': False, 'quiet': False,468'src': local_file, 'dest': s3_file, 'filters': filters,469'paths_type': 'locals3', 'region': 'us-east-1',470'endpoint_url': None, 'verify_ssl': None,471'follow_symlinks': True, 'page_size': None,472'is_stream': False, 'source_region': None, 'metadata': None}473self.http_response.status_code = 400474self.parsed_responses = [{'Error': {475'Code': 'BucketNotExists',476'Message': 'Bucket does not exist'}}]477cmd_arc = CommandArchitecture(478self.session, 'cp', params, RuntimeConfig().build_config())479cmd_arc.set_clients()480cmd_arc.create_instructions()481self.patch_make_request()482cmd_arc.run()483# Also, we need to verify that the error message is on the *same* line484# as the upload failed line, to make it easier to track.485output_str = (486"upload failed: %s to %s An error" % (487rel_local_file, s3_file))488self.assertIn(output_str, self.err_output.getvalue())489490def test_run_cp_get(self):491# This ensures that the architecture sets up correctly for a ``cp`` get492# command. It is just just a dry run, but all of the components need493# to be wired correctly for it to work.494s3_file = 's3://' + self.bucket + '/' + 'text1.txt'495local_file = self.loc_files[0]496rel_local_file = self._get_file_path(local_file)497filters = [['--include', '*']]498params = {'dir_op': False, 'dryrun': True, 'quiet': False,499'src': s3_file, 'dest': local_file, 'filters': filters,500'paths_type': 's3local', 'region': 'us-east-1',501'endpoint_url': None, 'verify_ssl': None,502'follow_symlinks': True, 'page_size': None,503'is_stream': False, 'source_region': None}504self.parsed_responses = [{"ETag": "abcd", "ContentLength": 100,505"LastModified": "2014-01-09T20:45:49.000Z"}]506config = RuntimeConfig().build_config()507cmd_arc = CommandArchitecture(self.session, 'cp', params, config)508cmd_arc.set_clients()509cmd_arc.create_instructions()510self.patch_make_request()511cmd_arc.run()512output_str = "(dryrun) download: %s to %s" % (s3_file, rel_local_file)513self.assertIn(output_str, self.output.getvalue())514515def test_run_cp_copy(self):516# This ensures that the architecture sets up correctly for a ``cp``517# copy command. It is just just a dry run, but all of the518# components need to be wired correctly for it to work.519s3_file = 's3://' + self.bucket + '/' + 'text1.txt'520filters = [['--include', '*']]521params = {'dir_op': False, 'dryrun': True, 'quiet': False,522'src': s3_file, 'dest': s3_file, 'filters': filters,523'paths_type': 's3s3', 'region': 'us-east-1',524'endpoint_url': None, 'verify_ssl': None,525'follow_symlinks': True, 'page_size': None,526'is_stream': False, 'source_region': None}527self.parsed_responses = [{"ETag": "abcd", "ContentLength": 100,528"LastModified": "2014-01-09T20:45:49.000Z"}]529config = RuntimeConfig().build_config()530cmd_arc = CommandArchitecture(self.session, 'cp', params, config)531cmd_arc.set_clients()532cmd_arc.create_instructions()533self.patch_make_request()534cmd_arc.run()535output_str = "(dryrun) copy: %s to %s" % (s3_file, s3_file)536self.assertIn(output_str, self.output.getvalue())537538def test_run_mv(self):539# This ensures that the architecture sets up correctly for a ``mv``540# command. It is just just a dry run, but all of the components need541# to be wired correctly for it to work.542s3_file = 's3://' + self.bucket + '/' + 'text1.txt'543filters = [['--include', '*']]544params = {'dir_op': False, 'dryrun': True, 'quiet': False,545'src': s3_file, 'dest': s3_file, 'filters': filters,546'paths_type': 's3s3', 'region': 'us-east-1',547'endpoint_url': None, 'verify_ssl': None,548'follow_symlinks': True, 'page_size': None,549'is_stream': False, 'source_region': None,550'is_move': True}551self.parsed_responses = [{"ETag": "abcd", "ContentLength": 100,552"LastModified": "2014-01-09T20:45:49.000Z"}]553config = RuntimeConfig().build_config()554cmd_arc = CommandArchitecture(self.session, 'mv', params, config)555cmd_arc.set_clients()556cmd_arc.create_instructions()557self.patch_make_request()558cmd_arc.run()559output_str = "(dryrun) move: %s to %s" % (s3_file, s3_file)560self.assertIn(output_str, self.output.getvalue())561562def test_run_remove(self):563# This ensures that the architecture sets up correctly for a ``rm``564# command. It is just just a dry run, but all of the components need565# to be wired correctly for it to work.566s3_file = 's3://' + self.bucket + '/' + 'text1.txt'567filters = [['--include', '*']]568params = {'dir_op': False, 'dryrun': True, 'quiet': False,569'src': s3_file, 'dest': s3_file, 'filters': filters,570'paths_type': 's3', 'region': 'us-east-1',571'endpoint_url': None, 'verify_ssl': None,572'follow_symlinks': True, 'page_size': None,573'is_stream': False, 'source_region': None}574self.parsed_responses = [{"ETag": "abcd", "ContentLength": 100,575"LastModified": "2014-01-09T20:45:49.000Z"}]576config = RuntimeConfig().build_config()577cmd_arc = CommandArchitecture(self.session, 'rm', params, config)578cmd_arc.set_clients()579cmd_arc.create_instructions()580self.patch_make_request()581cmd_arc.run()582output_str = "(dryrun) delete: %s" % s3_file583self.assertIn(output_str, self.output.getvalue())584585def test_run_sync(self):586# This ensures that the architecture sets up correctly for a ``sync``587# command. It is just just a dry run, but all of the components need588# to be wired correctly for it to work.589s3_file = 's3://' + self.bucket + '/' + 'text1.txt'590local_file = self.loc_files[0]591s3_prefix = 's3://' + self.bucket + '/'592local_dir = self.loc_files[3]593rel_local_file = self._get_file_path(local_file)594filters = [['--include', '*']]595params = {'dir_op': True, 'dryrun': True, 'quiet': False,596'src': local_dir, 'dest': s3_prefix, 'filters': filters,597'paths_type': 'locals3', 'region': 'us-east-1',598'endpoint_url': None, 'verify_ssl': None,599'follow_symlinks': True, 'page_size': None,600'is_stream': False, 'source_region': 'us-west-2'}601self.parsed_responses = [602{"CommonPrefixes": [], "Contents": [603{"Key": "text1.txt", "Size": 100,604"LastModified": "2014-01-09T20:45:49.000Z"}]},605{"CommonPrefixes": [], "Contents": []}]606config = RuntimeConfig().build_config()607cmd_arc = CommandArchitecture(self.session, 'sync', params, config)608cmd_arc.create_instructions()609cmd_arc.set_clients()610self.patch_make_request()611cmd_arc.run()612output_str = "(dryrun) upload: %s to %s" % (rel_local_file, s3_file)613self.assertIn(output_str, self.output.getvalue())614615616class CommandParametersTest(unittest.TestCase):617def setUp(self):618self.environ = {}619self.environ_patch = mock.patch('os.environ', self.environ)620self.environ_patch.start()621self.mock = mock.MagicMock()622self.mock.get_config = mock.MagicMock(return_value={'region': None})623self.file_creator = FileCreator()624self.loc_files = make_loc_files(self.file_creator)625self.bucket = 's3testbucket'626self.session = mock.Mock()627self.parsed_global = FakeArgs(628region='us-west-2',629endpoint_url=None,630verify_ssl=False)631632def tearDown(self):633self.environ_patch.stop()634clean_loc_files(self.file_creator)635636def test_check_path_type_pass(self):637# This tests the class's ability to determine whether the correct638# path types have been passed for a particular command. It test every639# possible combination that is correct for every command.640cmds = {'cp': ['locals3', 's3s3', 's3local'],641'mv': ['locals3', 's3s3', 's3local'],642'rm': ['s3'], 'mb': ['s3'], 'rb': ['s3'],643'sync': ['locals3', 's3s3', 's3local']}644s3_file = 's3://' + self.bucket + '/' + 'text1.txt'645local_file = self.loc_files[0]646647combos = {'s3s3': [s3_file, s3_file],648's3local': [s3_file, local_file],649'locals3': [local_file, s3_file],650's3': [s3_file],651'local': [local_file],652'locallocal': [local_file, local_file]}653654for cmd in cmds.keys():655cmd_param = CommandParameters(cmd, {}, '',656self.session, self.parsed_global)657cmd_param.add_region(mock.Mock())658correct_paths = cmds[cmd]659for path_args in correct_paths:660cmd_param.check_path_type(combos[path_args])661662def test_check_path_type_fail(self):663# This tests the class's ability to determine whether the correct664# path types have been passed for a particular command. It test every665# possible combination that is incorrect for every command.666cmds = {'cp': ['local', 'locallocal', 's3'],667'mv': ['local', 'locallocal', 's3'],668'rm': ['local', 'locallocal', 's3s3', 'locals3', 's3local'],669'ls': ['local', 'locallocal', 's3s3', 'locals3', 's3local'],670'sync': ['local', 'locallocal', 's3'],671'mb': ['local', 'locallocal', 's3s3', 'locals3', 's3local'],672'rb': ['local', 'locallocal', 's3s3', 'locals3', 's3local']}673s3_file = 's3://' + self.bucket + '/' + 'text1.txt'674local_file = self.loc_files[0]675676combos = {'s3s3': [s3_file, s3_file],677's3local': [s3_file, local_file],678'locals3': [local_file, s3_file],679's3': [s3_file],680'local': [local_file],681'locallocal': [local_file, local_file]}682683for cmd in cmds.keys():684cmd_param = CommandParameters(cmd, {}, '',685self.session, self.parsed_global)686cmd_param.add_region(mock.Mock())687wrong_paths = cmds[cmd]688for path_args in wrong_paths:689with self.assertRaises(TypeError):690cmd_param.check_path_type(combos[path_args])691692def test_validate_streaming_paths_upload(self):693paths = ['-', 's3://bucket']694cmd_params = CommandParameters('cp', {}, '')695cmd_params.add_paths(paths)696self.assertTrue(cmd_params.parameters['is_stream'])697self.assertTrue(cmd_params.parameters['only_show_errors'])698self.assertFalse(cmd_params.parameters['dir_op'])699700def test_validate_streaming_paths_download(self):701paths = ['s3://bucket/key', '-']702cmd_params = CommandParameters('cp', {}, '')703cmd_params.add_paths(paths)704self.assertTrue(cmd_params.parameters['is_stream'])705self.assertTrue(cmd_params.parameters['only_show_errors'])706self.assertFalse(cmd_params.parameters['dir_op'])707708def test_validate_no_streaming_paths(self):709paths = [self.file_creator.rootdir, 's3://bucket']710cmd_params = CommandParameters('cp', {}, '')711cmd_params.add_paths(paths)712self.assertFalse(cmd_params.parameters['is_stream'])713714def test_validate_checksum_algorithm_download_error(self):715paths = ['s3://bucket/key', self.file_creator.rootdir]716parameters = {'checksum_algorithm': 'CRC32'}717cmd_params = CommandParameters('cp', parameters, '')718with self.assertRaises(ValueError) as cm:719cmd_params.add_paths(paths)720self.assertIn('Expected checksum-algorithm parameter to be used with one of following path formats', cm.msg)721722def test_validate_checksum_algorithm_sync_download_error(self):723paths = ['s3://bucket/key', self.file_creator.rootdir]724parameters = {'checksum_algorithm': 'CRC32C'}725cmd_params = CommandParameters('sync', parameters, '')726with self.assertRaises(ValueError) as cm:727cmd_params.add_paths(paths)728self.assertIn('Expected checksum-algorithm parameter to be used with one of following path formats', cm.msg)729730def test_validate_checksum_mode_upload_error(self):731paths = [self.file_creator.rootdir, 's3://bucket/key']732parameters = {'checksum_mode': 'ENABLED'}733cmd_params = CommandParameters('cp', parameters, '')734with self.assertRaises(ValueError) as cm:735cmd_params.add_paths(paths)736self.assertIn('Expected checksum-mode parameter to be used with one of following path formats', cm.msg)737738def test_validate_checksum_mode_sync_upload_error(self):739paths = [self.file_creator.rootdir, 's3://bucket/key']740parameters = {'checksum_mode': 'ENABLED'}741cmd_params = CommandParameters('sync', parameters, '')742with self.assertRaises(ValueError) as cm:743cmd_params.add_paths(paths)744self.assertIn('Expected checksum-mode parameter to be used with one of following path formats', cm.msg)745746def test_validate_checksum_mode_move_error(self):747paths = ['s3://bucket/key', 's3://bucket2/key']748parameters = {'checksum_mode': 'ENABLED'}749cmd_params = CommandParameters('mv', parameters, '')750with self.assertRaises(ValueError) as cm:751cmd_params.add_paths(paths)752self.assertIn('Expected checksum-mode parameter to be used with one of following path formats', cm.msg)753754def test_validate_streaming_paths_error(self):755parameters = {'src': '-', 'dest': 's3://bucket'}756cmd_params = CommandParameters('sync', parameters, '')757with self.assertRaises(ValueError):758cmd_params._validate_streaming_paths()759760def test_validate_non_existent_local_path_upload(self):761non_existent_path = os.path.join(self.file_creator.rootdir, 'foo')762paths = [non_existent_path, 's3://bucket/']763cmd_param = CommandParameters('cp', {}, '')764with self.assertRaises(RuntimeError):765cmd_param.add_paths(paths)766767def test_add_path_for_non_existsent_local_path_download(self):768non_existent_path = os.path.join(self.file_creator.rootdir, 'foo')769paths = ['s3://bucket', non_existent_path]770cmd_param = CommandParameters('cp', {'dir_op': True}, '')771cmd_param.add_paths(paths)772self.assertTrue(os.path.exists(non_existent_path))773774def test_validate_sse_c_args_missing_sse(self):775paths = ['s3://bucket/foo', 's3://bucket/bar']776params = {'dir_op': False, 'sse_c_key': 'foo'}777cmd_param = CommandParameters('cp', params, '')778with self.assertRaisesRegex(ValueError, '--sse-c must be specified'):779cmd_param.add_paths(paths)780781def test_validate_sse_c_args_missing_sse_c_key(self):782paths = ['s3://bucket/foo', 's3://bucket/bar']783params = {'dir_op': False, 'sse_c': 'AES256'}784cmd_param = CommandParameters('cp', params, '')785with self.assertRaisesRegex(ValueError,786'--sse-c-key must be specified'):787cmd_param.add_paths(paths)788789def test_validate_sse_c_args_missing_sse_c_copy_source(self):790paths = ['s3://bucket/foo', 's3://bucket/bar']791params = {'dir_op': False, 'sse_c_copy_source_key': 'foo'}792cmd_param = CommandParameters('cp', params, '')793with self.assertRaisesRegex(ValueError,794'--sse-c-copy-source must be specified'):795cmd_param.add_paths(paths)796797def test_validate_sse_c_args_missing_sse_c_copy_source_key(self):798paths = ['s3://bucket/foo', 's3://bucket/bar']799params = {'dir_op': False, 'sse_c_copy_source': 'AES256'}800cmd_param = CommandParameters('cp', params, '')801with self.assertRaisesRegex(ValueError,802'--sse-c-copy-source-key must be specified'):803cmd_param.add_paths(paths)804805def test_validate_sse_c_args_wrong_path_type(self):806paths = ['s3://bucket/foo', self.file_creator.rootdir]807params = {'dir_op': False, 'sse_c_copy_source': 'AES256',808'sse_c_copy_source_key': 'foo'}809cmd_param = CommandParameters('cp', params, '')810with self.assertRaisesRegex(ValueError,811'only supported for copy operations'):812cmd_param.add_paths(paths)813814def test_adds_is_move(self):815params = {}816CommandParameters('mv', params, '',817session=self.session,818parsed_globals=self.parsed_global)819self.assertTrue(params.get('is_move'))820821# is_move should only be true for mv822params = {}823CommandParameters('cp', params, '')824self.assertFalse(params.get('is_move'))825826827class HelpDocTest(BaseAWSHelpOutputTest):828def setUp(self):829super(HelpDocTest, self).setUp()830self.session = botocore.session.get_session()831832def tearDown(self):833super(HelpDocTest, self).tearDown()834835def test_s3_help(self):836# This tests the help command for the s3 service. This837# checks to make sure the appropriate descriptions are838# added including the tutorial.839s3 = S3(self.session)840parser = argparse.ArgumentParser()841parser.add_argument('--paginate', action='store_true')842parsed_global = parser.parse_args(['--paginate'])843help_command = s3.create_help_command()844help_command([], parsed_global)845self.assert_contains(846"This section explains prominent concepts "847"and notations in the set of high-level S3 commands provided.")848self.assert_contains("Every command takes one or two positional")849self.assert_contains("* rb")850851def test_s3command_help(self):852# This tests the help command for an s3 command. This853# checks to make sure the command prints appropriate854# parts. Note the examples are not included because855# the event was not registered.856s3command = CpCommand(self.session)857s3command._arg_table = s3command._build_arg_table()858parser = argparse.ArgumentParser()859parser.add_argument('--paginate', action='store_true')860parsed_global = parser.parse_args(['--paginate'])861help_command = s3command.create_help_command()862help_command([], parsed_global)863self.assert_contains("cp")864self.assert_contains("[--acl <value>]")865self.assert_contains("Displays the operations that would be")866867def test_help(self):868# This ensures that the file appropriately redirects to help object869# if help is the only argument left to be parsed. There should not870# have any contents in the docs.871s3_command = SyncCommand(self.session)872s3_command(['help'], [])873self.assert_contains('sync')874self.assert_contains("Synopsis")875876877if __name__ == "__main__":878unittest.main()879880881