Path: blob/develop/tests/unit/customizations/gamelift/test_uploadbuild.py
2632 views
# Copyright 2015 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.0/7#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.12from argparse import Namespace13import contextlib14import os15import zipfile1617from botocore.session import get_session18from botocore.exceptions import ClientError1920from awscli.testutils import unittest, mock, FileCreator21from awscli.customizations.gamelift.uploadbuild import UploadBuildCommand22from awscli.customizations.gamelift.uploadbuild import zip_directory23from awscli.customizations.gamelift.uploadbuild import validate_directory24from awscli.customizations.gamelift.uploadbuild import parse_tags25from awscli.compat import StringIO262728class TestGetGameSessionLogCommand(unittest.TestCase):29def setUp(self):30self.create_client_patch = mock.patch(31'botocore.session.Session.create_client')32self.mock_create_client = self.create_client_patch.start()33self.session = get_session()3435self.gamelift_client = mock.Mock()36self.s3_client = mock.Mock()37self.mock_create_client.side_effect = [38self.gamelift_client, self.s3_client39]4041self.file_creator = FileCreator()42self.upload_file_patch = mock.patch(43'awscli.customizations.gamelift.uploadbuild.S3Transfer.upload_file'44)45self.upload_file_mock = self.upload_file_patch.start()4647self.cmd = UploadBuildCommand(self.session)48self._setup_input_output()4950def tearDown(self):51self.create_client_patch.stop()52self.file_creator.remove_all()53self.upload_file_patch.stop()5455def _setup_input_output(self):56# Input values57self.region = 'us-west-2'58self.build_name = 'mybuild'59self.build_version = 'myversion'60self.build_root = self.file_creator.rootdir6162self.args = [63'--name', self.build_name, '--build-version', self.build_version,64'--build-root', self.build_root65]6667self.global_args = Namespace()68self.global_args.region = self.region69self.global_args.endpoint_url = None70self.global_args.verify_ssl = None7172# Output values73self.build_id = 'myid'74self.bucket = 'mybucket'75self.key = 'mykey'76self.access_key = 'myaccesskey'77self.secret_key = 'mysecretkey'78self.session_token = 'mytoken'7980self.gamelift_client.create_build.return_value = {81'Build': {82'BuildId': self.build_id83}84}8586self.gamelift_client.request_upload_credentials.return_value = {87'StorageLocation': {88'Bucket': self.bucket,89'Key': self.key90},91'UploadCredentials': {92'AccessKeyId': self.access_key,93'SecretAccessKey': self.secret_key,94'SessionToken': self.session_token95}96}9798def test_upload_build(self):99self.file_creator.create_file('tmpfile', 'Some contents')100self.cmd(self.args, self.global_args)101# Ensure the clients were instantiated correctly.102client_creation_args = self.mock_create_client.call_args_list103self.assertEqual(104client_creation_args,105[mock.call('gamelift', region_name=self.region,106endpoint_url=None, verify=None),107mock.call('s3', aws_access_key_id=self.access_key,108aws_secret_access_key=self.secret_key,109aws_session_token=self.session_token,110region_name=self.region,111verify=None)]112)113114# Ensure the GameLift client was called correctly.115self.gamelift_client.create_build.assert_called_once_with(116Name=self.build_name, Version=self.build_version)117118self.gamelift_client.request_upload_credentials.\119assert_called_once_with(BuildId=self.build_id)120121# Ensure the underlying S3 transfer call was correct.122self.upload_file_mock.assert_called_once_with(123mock.ANY, self.bucket, self.key, callback=mock.ANY)124125tempfile_path = self.upload_file_mock.call_args[0][0]126# Ensure the temporary zipfile is deleted at the end.127self.assertFalse(os.path.exists(tempfile_path))128129def test_upload_build_when_operating_system_is_provided(self):130operating_system = 'WINDOWS_2012'131self.file_creator.create_file('tmpfile', 'Some contents')132self.args = [133'--name', self.build_name, '--build-version', self.build_version,134'--build-root', self.build_root,135'--operating-system', operating_system136]137self.cmd(self.args, self.global_args)138139# Ensure the GameLift client was called correctly.140self.gamelift_client.create_build.assert_called_once_with(141Name=self.build_name, Version=self.build_version,142OperatingSystem=operating_system)143144def test_error_message_when_directory_is_empty(self):145with mock.patch('sys.stderr', StringIO()) as mock_stderr:146self.cmd(self.args, self.global_args)147self.assertEqual(148mock_stderr.getvalue(),149f'Fail to upload {self.build_root}. '150'The build root directory is empty or does not exist.\n'151)152153def test_error_message_when_directory_is_not_provided(self):154self.args = [155'--name', self.build_name,156'--build-version', self.build_version,157'--build-root', ''158]159160with mock.patch('sys.stderr', StringIO()) as mock_stderr:161self.cmd(self.args, self.global_args)162self.assertEqual(163mock_stderr.getvalue(),164'Fail to upload {}. '165'The build root directory is empty or does not exist.\n'.format('')166)167168def test_error_message_when_directory_does_not_exist(self):169dir_not_exist = os.path.join(self.build_root, 'does_not_exist')170171self.args = [172'--name', self.build_name,173'--build-version', self.build_version,174'--build-root', dir_not_exist175]176177with mock.patch('sys.stderr', StringIO()) as mock_stderr:178self.cmd(self.args, self.global_args)179self.assertEqual(180mock_stderr.getvalue(),181f'Fail to upload {dir_not_exist}. '182'The build root directory is empty or does not exist.\n'183)184185def test_temporary_file_does_exist_when_fails(self):186self.upload_file_mock.side_effect = ClientError(187{'Error': {'Code': 403, 'Message': 'No Access'}}, 'PutObject')188with self.assertRaises(ClientError):189self.file_creator.create_file('tmpfile', 'Some contents')190self.cmd(self.args, self.global_args)191tempfile_path = self.upload_file_mock.call_args[0][0]192# Make sure the temporary file is removed.193self.assertFalse(os.path.exists(tempfile_path))194195def test_upload_build_when_server_sdk_version_is_provided(self):196server_sdk_version = '4.0.2'197self.file_creator.create_file('tmpfile', 'Some contents')198self.args = [199'--name', self.build_name, '--build-version', self.build_version,200'--build-root', self.build_root,201'--server-sdk-version', server_sdk_version202]203self.cmd(self.args, self.global_args)204205# Ensure the GameLift client was called correctly.206self.gamelift_client.create_build.assert_called_once_with(207Name=self.build_name, Version=self.build_version,208ServerSdkVersion=server_sdk_version)209210def test_upload_build_when_tags_are_provided(self):211self.file_creator.create_file('tmpfile', 'Some contents')212self.args = [213'--name', self.build_name, '--build-version', self.build_version,214'--build-root', self.build_root,215'--tags', 'Environment=Production', 'Team=GameDev'216]217self.cmd(self.args, self.global_args)218219self.gamelift_client.create_build.assert_called_once_with(220Name=self.build_name, Version=self.build_version,221Tags=[222{'Key': 'Environment', 'Value': 'Production'},223{'Key': 'Team', 'Value': 'GameDev'}224])225226227class TestParseTags(unittest.TestCase):228def test_parse_tags_with_key_value_pairs(self):229result = parse_tags(['Key1=Value1', 'Key2=Value2'])230self.assertEqual(result, [231{'Key': 'Key1', 'Value': 'Value1'},232{'Key': 'Key2', 'Value': 'Value2'}233])234235def test_parse_tags_with_empty_value(self):236result = parse_tags(['Key1='])237self.assertEqual(result, [{'Key': 'Key1', 'Value': ''}])238239def test_parse_tags_without_equals(self):240result = parse_tags(['Key1'])241self.assertEqual(result, [{'Key': 'Key1', 'Value': ''}])242243def test_parse_tags_with_equals_in_value(self):244result = parse_tags(['Key1=Value=WithEquals'])245self.assertEqual(result, [{'Key': 'Key1', 'Value': 'Value=WithEquals'}])246247def test_parse_tags_with_none(self):248result = parse_tags(None)249self.assertEqual(result, [])250251def test_parse_tags_with_empty_list(self):252result = parse_tags([])253self.assertEqual(result, [])254255256class TestZipDirectory(unittest.TestCase):257def setUp(self):258self.file_creator = FileCreator()259self.zip_file = self.file_creator.create_file('build.zip', '')260self._dir_root = 'mybuild'261262def tearDown(self):263self.file_creator.remove_all()264265@property266def dir_root(self):267return self.file_creator.full_path(self._dir_root)268269def add_to_directory(self, filename):270self.file_creator.create_file(271os.path.join(self._dir_root, filename), 'Some contents')272273def assert_contents_of_zip_file(self, filenames):274zip_file_object = zipfile.ZipFile(275self.zip_file, 'r', zipfile.ZIP_DEFLATED)276with contextlib.closing(zip_file_object) as zf:277ref_zipfiles = []278zipfile_contents = zf.namelist()279for ref_zipfile in zipfile_contents:280if os.sep == '\\':281# Internally namelist() represent directories with282# forward slashes so we need to account for that if283# the separator is a backslash depending on the operating284# system.285ref_zipfile = ref_zipfile.replace('/', '\\')286ref_zipfiles.append(ref_zipfile)287self.assertEqual(sorted(ref_zipfiles), filenames)288289def test_single_file(self):290self.add_to_directory('foo')291zip_directory(self.zip_file, self.dir_root)292self.assert_contents_of_zip_file(['foo'])293294def test_multiple_files(self):295self.add_to_directory('foo')296self.add_to_directory('bar')297zip_directory(self.zip_file, self.dir_root)298self.assert_contents_of_zip_file(['bar', 'foo'])299300def test_nested_file(self):301filename = os.path.join('mydir', 'foo')302self.add_to_directory(filename)303zip_directory(self.zip_file, self.dir_root)304self.assert_contents_of_zip_file([filename])305306307class TestValidateDirectory(unittest.TestCase):308def setUp(self):309self.file_creator = FileCreator()310self.dir_root = self.file_creator.rootdir311312def tearDown(self):313self.file_creator.remove_all()314315def test_directory_contains_single_file(self):316self.file_creator.create_file('foo', '')317self.assertTrue(validate_directory(self.dir_root))318319def test_directory_contains_file_and_empty_directory(self):320dirname = os.path.join(self.dir_root, 'foo')321os.makedirs(dirname)322self.file_creator.create_file('bar', '')323self.assertTrue(validate_directory(self.dir_root))324325def test_nested_file(self):326self.file_creator.create_file('mydir/bar', '')327self.assertTrue(validate_directory(self.dir_root))328329def test_empty_directory(self):330self.assertFalse(validate_directory(self.dir_root))331332def test_nonexistent_directory(self):333dir_not_exist = os.path.join(self.dir_root, 'does_not_exist')334self.assertFalse(validate_directory(dir_not_exist))335336def test_nonprovided_directory(self):337self.assertFalse(validate_directory(''))338339def test_empty_nested_directory(self):340dirname = os.path.join(self.dir_root, 'foo')341os.makedirs(dirname)342self.assertFalse(validate_directory(self.dir_root))343344345