1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
|
# Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
# http://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
import io
import os
import sys
from argparse import Namespace
import pytest
from botocore.paginate import PageIterator
from awscli.compat import StringIO, contextlib
from awscli.formatter import JSONFormatter, StreamedYAMLFormatter, YAMLDumper
from awscli.testutils import mock, unittest
class FakePageIterator(PageIterator):
def __init__(self, responses):
self._responses = responses
def __iter__(self):
for response in self._responses:
yield response
class TestYAMLDumper(unittest.TestCase):
def setUp(self):
self.dumper = YAMLDumper()
self.output = StringIO()
def test_dump_int(self):
self.dumper.dump(1, self.output)
self.assertEqual(self.output.getvalue(), '1\n')
def test_dump_float(self):
self.dumper.dump(1.2, self.output)
self.assertEqual(self.output.getvalue(), '1.2\n')
def test_dump_bool(self):
self.dumper.dump(True, self.output)
self.assertEqual(self.output.getvalue(), 'true\n')
def test_dump_str(self):
self.dumper.dump('foo', self.output)
self.assertEqual(self.output.getvalue(), '"foo"\n')
def test_dump_structure(self):
self.dumper.dump({'key': 'val'}, self.output)
self.assertEqual(self.output.getvalue(), 'key: val\n')
def test_dump_list(self):
self.dumper.dump(['val1', 'val2'], self.output)
self.assertEqual(self.output.getvalue(), '- val1\n- val2\n')
class TestStreamedYAMLFormatter:
def setup_method(self):
self.args = Namespace(query=None)
self.formatter = StreamedYAMLFormatter(self.args)
self.output = StringIO()
def test_format_single_response(self):
response = {'TableNames': ['MyTable']}
self.formatter('list-tables', response, self.output)
assert self.output.getvalue() == ('- TableNames:\n' ' - MyTable\n')
def test_format_paginated_response(self):
response = FakePageIterator(
[
{'TableNames': ['MyTable']},
{'TableNames': ['MyTable2']},
]
)
self.formatter('list-tables', response, self.output)
assert self.output.getvalue() == (
'- TableNames:\n'
' - MyTable\n'
'- TableNames:\n'
' - MyTable2\n'
)
def test_flushes_after_io_error(self):
io_error_dumper = mock.Mock(YAMLDumper)
mock_output = mock.Mock()
io_error_dumper.dump.side_effect = OSError()
response = {'TableNames': ['MyTable']}
formatter = StreamedYAMLFormatter(self.args, io_error_dumper)
formatter('list-tables', response, mock_output)
assert mock_output.flush.called
def test_stops_paginating_after_io_error(self):
io_error_dumper = mock.Mock(YAMLDumper)
mock_output = mock.Mock()
io_error_dumper.dump.side_effect = OSError()
response = FakePageIterator(
[
{'TableNames': ['MyTable']},
{'TableNames': ['MyTable2']},
]
)
formatter = StreamedYAMLFormatter(self.args, io_error_dumper)
formatter('list-tables', response, mock_output)
# The dumper should have only been called once as the io error is
# immediately raised and we should not have kept paginating.
assert len(io_error_dumper.dump.call_args_list) == 1
assert mock_output.flush.called
@pytest.mark.parametrize(
'env_vars',
[
{'AWS_CLI_OUTPUT_ENCODING': 'UTF-8'},
{'PYTHONUTF8': '1'},
],
)
def test_encoding_override(self, env_vars):
response = {'TableNames': ['桌子']}
stdout_b = io.BytesIO()
stdout = io.TextIOWrapper(stdout_b, encoding="cp1252", newline='\n')
formatter = StreamedYAMLFormatter(self.args)
with mock.patch.dict(os.environ, env_vars):
with contextlib.redirect_stdout(stdout):
assert 'cp1252' == sys.stdout.encoding
formatter('list-tables', response, sys.stdout)
# we expect the formatter to have changed the output stream
# encoding based on AWS_CLI_OUTPUT_ENCODING
assert 'UTF-8' == sys.stdout.encoding
stdout.flush()
assert stdout_b.getvalue() == ('- TableNames:\n' ' - 桌子\n').encode()
class TestJSONFormatter:
def setup_method(self):
self.args = Namespace(query=None)
self.formatter = JSONFormatter(self.args)
@pytest.mark.parametrize(
'env_vars',
[
{'AWS_CLI_OUTPUT_ENCODING': 'UTF-8'},
{'PYTHONUTF8': '1'},
],
)
def test_encoding_override(self, env_vars):
"""
StreamedYAMLFormatter is tested above since it doesn't inherit from
FullyBufferedFormatter, this is implicitly testing all other
formatters that do.
"""
response = {'TableNames': ['桌子']}
stdout_b = io.BytesIO()
stdout = io.TextIOWrapper(stdout_b, encoding="cp1252", newline='\n')
with mock.patch.dict(os.environ, env_vars):
with contextlib.redirect_stdout(stdout):
assert 'cp1252' == sys.stdout.encoding
self.formatter('list-tables', response, sys.stdout)
# we expect the formatter to have changed the output stream
# encoding based on AWS_CLI_OUTPUT_ENCODING
assert 'UTF-8' == sys.stdout.encoding
stdout.flush()
assert (
stdout_b.getvalue()
== (
'{\n'
' "TableNames": [\n'
' "桌子"\n'
' ]\n'
'}\n'
).encode()
)
|