Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
aws
GitHub Repository: aws/aws-cli
Path: blob/develop/tests/unit/customizations/test_utils.py
1567 views
1
# Copyright 2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
#
3
# Licensed under the Apache License, Version 2.0 (the "License"). You
4
# may not use this file except in compliance with the License. A copy of
5
# the License is located at
6
#
7
# http://aws.amazon.com/apache2.0/
8
#
9
# or in the "license" file accompanying this file. This file is
10
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11
# ANY KIND, either express or implied. See the License for the specific
12
# language governing permissions and limitations under the License.
13
import io
14
import argparse
15
from botocore.exceptions import ClientError
16
17
from awscli.customizations import utils
18
from awscli.testutils import mock
19
from awscli.testutils import unittest
20
from awscli.testutils import BaseAWSHelpOutputTest
21
22
23
class FakeParsedArgs(object):
24
def __init__(self, **kwargs):
25
self.__dict__.update(kwargs)
26
27
28
class TestCommandTableRenames(BaseAWSHelpOutputTest):
29
30
def test_rename_command_table(self):
31
handler = lambda command_table, **kwargs: utils.rename_command(
32
command_table, 'ec2', 'fooec2')
33
# Verify that we can rename a top level command.
34
self.session.register('building-command-table.main', handler)
35
self.driver.main(['fooec2', 'help'])
36
self.assert_contains('fooec2')
37
38
# We can also see subcommands help as well.
39
self.driver.main(['fooec2', 'run-instances', 'help'])
40
self.assert_contains('run-instances')
41
42
43
class TestCommandTableAlias(BaseAWSHelpOutputTest):
44
45
def test_alias_command_table(self):
46
old_name = 'cloudhsmv2'
47
new_name = 'nopossiblewaythisisalreadythere'
48
49
def handler(command_table, **kwargs):
50
utils.alias_command(command_table, old_name, new_name)
51
52
self._assert_command_exists(old_name, handler)
53
self._assert_command_exists(new_name, handler)
54
55
# Verify that the new name is documented
56
self.driver.main(['help'])
57
self.assert_contains(new_name)
58
self.assert_not_contains(old_name)
59
60
def test_make_hidden_alias(self):
61
old_name = 'cloudhsmv2'
62
new_name = 'nopossiblewaythisisalreadythere'
63
64
def handler(command_table, **kwargs):
65
utils.make_hidden_command_alias(command_table, old_name, new_name)
66
67
self._assert_command_exists(old_name, handler)
68
self._assert_command_exists(new_name, handler)
69
70
# Verify that the new isn't documented
71
self.driver.main(['help'])
72
self.assert_not_contains(new_name)
73
self.assert_contains(old_name)
74
75
def _assert_command_exists(self, command_name, handler):
76
# Verify that we can alias a top level command.
77
self.session.register('building-command-table.main', handler)
78
self.driver.main([command_name, 'help'])
79
self.assert_contains(command_name)
80
81
# We can also see subcommands help as well.
82
self.driver.main([command_name, 'describe-clusters', 'help'])
83
self.assert_contains('describe-clusters')
84
85
86
class TestHiddenAlias(unittest.TestCase):
87
def test_not_marked_as_required_if_not_needed(self):
88
original_arg_required = mock.Mock()
89
original_arg_required.required = False
90
arg_table = {'original': original_arg_required}
91
utils.make_hidden_alias(arg_table, 'original', 'new-name')
92
self.assertIn('new-name', arg_table)
93
# Note: the _DOCUMENT_AS_REQUIRED is tested as a functional
94
# test because it only affects how the doc synopsis is
95
# rendered.
96
self.assertFalse(arg_table['original'].required)
97
self.assertFalse(arg_table['new-name'].required)
98
99
def test_hidden_alias_marks_as_not_required(self):
100
original_arg_required = mock.Mock()
101
original_arg_required.required = True
102
arg_table = {'original': original_arg_required}
103
utils.make_hidden_alias(arg_table, 'original', 'new-name')
104
self.assertIn('new-name', arg_table)
105
self.assertFalse(arg_table['original'].required)
106
self.assertFalse(arg_table['new-name'].required)
107
108
109
class TestValidateMututuallyExclusiveGroups(unittest.TestCase):
110
def test_two_single_groups(self):
111
# The most basic example of mutually exclusive args.
112
# If foo is specified, but bar is not, then we're fine.
113
parsed = FakeParsedArgs(foo='one', bar=None)
114
utils.validate_mutually_exclusive(parsed, ['foo'], ['bar'])
115
# If bar is specified and foo is not, then we're fine.
116
parsed = FakeParsedArgs(foo=None, bar='one')
117
utils.validate_mutually_exclusive(parsed, ['foo'], ['bar'])
118
# But if we specify both foo and bar then we get an error.
119
parsed = FakeParsedArgs(foo='one', bar='two')
120
with self.assertRaises(ValueError):
121
utils.validate_mutually_exclusive(parsed, ['foo'], ['bar'])
122
123
def test_multiple_groups(self):
124
groups = (['one', 'two', 'three'], ['foo', 'bar', 'baz'],
125
['qux', 'bad', 'morebad'])
126
# This is fine.
127
parsed = FakeParsedArgs(foo='foo', bar='bar', baz='baz')
128
utils.validate_mutually_exclusive(parsed, *groups)
129
# But this is bad.
130
parsed = FakeParsedArgs(foo='one', bar=None, qux='three')
131
with self.assertRaises(ValueError):
132
utils.validate_mutually_exclusive(parsed, *groups)
133
134
135
class TestS3BucketExists(unittest.TestCase):
136
def setUp(self):
137
self.s3_client = mock.Mock()
138
self.bucket_name = 'mybucket'
139
self.error_response = {
140
'Error': {
141
'Code': '404',
142
'Message': 'Not Found'
143
}
144
}
145
self.bucket_no_exists_error = ClientError(
146
self.error_response,
147
'HeadBucket'
148
)
149
150
def test_bucket_exists(self):
151
self.assertTrue(
152
utils.s3_bucket_exists(self.s3_client, self.bucket_name))
153
154
def test_bucket_not_exists(self):
155
self.s3_client.head_bucket.side_effect = self.bucket_no_exists_error
156
self.assertFalse(
157
utils.s3_bucket_exists(self.s3_client, self.bucket_name))
158
159
def test_bucket_exists_with_non_404(self):
160
self.error_response['Error']['Code'] = '403'
161
self.error_response['Error']['Message'] = 'Forbidden'
162
forbidden_error = ClientError(self.error_response, 'HeadBucket')
163
self.s3_client.head_bucket.side_effect = forbidden_error
164
self.assertTrue(
165
utils.s3_bucket_exists(self.s3_client, self.bucket_name))
166
167
168
class TestClientCreationFromGlobals(unittest.TestCase):
169
def setUp(self):
170
self.fake_client = {}
171
self.session = mock.Mock()
172
self.session.create_client.return_value = self.fake_client
173
self.parsed_globals = argparse.Namespace()
174
self.parsed_globals.region = 'us-west-2'
175
self.parsed_globals.endpoint_url = 'https://foo.bar.com'
176
self.parsed_globals.verify_ssl = False
177
178
def test_creates_clients_with_no_overrides(self):
179
client = utils.create_client_from_parsed_globals(
180
self.session, 'ec2', self.parsed_globals)
181
self.assertEqual(self.fake_client, client)
182
self.session.create_client.assert_called_once_with(
183
'ec2',
184
region_name='us-west-2',
185
verify=False,
186
endpoint_url='https://foo.bar.com'
187
)
188
189
def test_creates_clients_with_overrides(self):
190
overrides = {
191
'region_name': 'custom',
192
'verify': True,
193
'other_thing': 'more custom'
194
}
195
client = utils.create_client_from_parsed_globals(
196
self.session, 'ec2', self.parsed_globals, overrides)
197
self.assertEqual(self.fake_client, client)
198
self.session.create_client.assert_called_once_with(
199
'ec2',
200
region_name='custom',
201
verify=True,
202
other_thing='more custom',
203
endpoint_url='https://foo.bar.com'
204
)
205
206
def test_creates_clients_with_no_parsed_globals(self):
207
client = utils.create_client_from_parsed_globals(
208
self.session, 'ec2', argparse.Namespace())
209
self.assertEqual(self.fake_client, client)
210
self.session.create_client.assert_called_once_with('ec2')
211
212
213
class MockPipedStdout(io.BytesIO):
214
"""Mocks `sys.stdout`.
215
216
We can't use `TextIOWrapper` because calling
217
`TextIOWrapper(.., encoding=None)` sets the ``encoding`` attribute to
218
`UTF-8`. The attribute is also `readonly` in `TextIOWrapper` and
219
`TextIOBase` so it cannot be overwritten in subclasses.
220
"""
221
def __init__(self):
222
self.encoding = None
223
224
super(MockPipedStdout, self).__init__()
225
226
def write(self, data):
227
# sys.stdout.write() will default to encoding to ascii, when its
228
# `encoding` is `None`.
229
if self.encoding is None:
230
data = data.encode('ascii')
231
else:
232
data = data.encode(self.encoding)
233
super(MockPipedStdout, self).write(data)
234
235
236
class TestUniPrint(unittest.TestCase):
237
238
def test_out_file_with_encoding_attribute(self):
239
buf = io.BytesIO()
240
out = io.TextIOWrapper(buf, encoding='utf-8')
241
utils.uni_print(u'\u2713', out)
242
self.assertEqual(buf.getvalue(), u'\u2713'.encode('utf-8'))
243
244
def test_encoding_with_encoding_none(self):
245
'''When the output of the aws command is being piped,
246
the `encoding` attribute of `sys.stdout` is `None`.'''
247
out = MockPipedStdout()
248
utils.uni_print(u'SomeChars\u2713\u2714OtherChars', out)
249
self.assertEqual(out.getvalue(), b'SomeChars??OtherChars')
250
251
def test_encoding_statement_fails_are_replaced(self):
252
buf = io.BytesIO()
253
out = io.TextIOWrapper(buf, encoding='ascii')
254
utils.uni_print(u'SomeChars\u2713\u2714OtherChars', out)
255
# We replace the characters that can't be encoded
256
# with '?'.
257
self.assertEqual(buf.getvalue(), b'SomeChars??OtherChars')
258
259
260
class TestGetPolicyARNSuffix(unittest.TestCase):
261
def test_get_policy_arn_suffix(self):
262
self.assertEqual("aws-cn", utils.get_policy_arn_suffix("cn-northwest-1"))
263
self.assertEqual("aws-cn", utils.get_policy_arn_suffix("cn-northwest-2"))
264
self.assertEqual("aws-cn", utils.get_policy_arn_suffix("cn-north-1"))
265
self.assertEqual("aws-us-gov", utils.get_policy_arn_suffix("us-gov-west-1"))
266
self.assertEqual("aws", utils.get_policy_arn_suffix("ca-central-1"))
267
self.assertEqual("aws", utils.get_policy_arn_suffix("us-east-1"))
268
self.assertEqual("aws", utils.get_policy_arn_suffix("sa-east-1"))
269
self.assertEqual("aws", utils.get_policy_arn_suffix("ap-south-1"))
270
271