diff --git a/Makefile b/Makefile index 581ed039..7ba9b3d8 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,7 @@ cov cover coverage: pre-commit mototest: docker pull alpine docker pull lambci/lambda:python3.8 - python -Wd -X tracemalloc=5 -X faulthandler -m pytest -vv -m moto -n auto --cov-report term --cov-report html --cov-report xml --cov=aiobotocore --cov=tests --log-cli-level=DEBUG $(FLAGS) aiobotocore tests + python -Wd -X tracemalloc=5 -X faulthandler -m pytest -vv -m "not localonly" -n auto --cov-report term --cov-report html --cov-report xml --cov=aiobotocore --cov=tests --log-cli-level=DEBUG $(FLAGS) aiobotocore tests @echo "open file://`pwd`/htmlcov/index.html" clean: diff --git a/pyproject.toml b/pyproject.toml index 51bb8214..371a68a5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -59,7 +59,7 @@ include = ["aiobotocore*"] asyncio_mode = "auto" cache_dir = "/tmp/pytest_aiobotocore_cache" markers = [ - "moto", + "localonly", "config_kwargs", "patch_attributes", ] diff --git a/tests/boto_tests/unit/__init__.py b/tests/boto_tests/unit/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/boto_tests/test_credentials.py b/tests/boto_tests/unit/test_credentials.py similarity index 96% rename from tests/boto_tests/test_credentials.py rename to tests/boto_tests/unit/test_credentials.py index 518dd3be..984b22e5 100644 --- a/tests/boto_tests/test_credentials.py +++ b/tests/boto_tests/unit/test_credentials.py @@ -44,8 +44,7 @@ AioSSOProvider, ) from aiobotocore.session import AioSession - -from .helpers import StubbedSession +from tests.boto_tests.helpers import StubbedSession def random_chars(num_chars): @@ -53,7 +52,6 @@ def random_chars(num_chars): # From class TestCredentials(BaseEnvVar): -@pytest.mark.moto @pytest.mark.parametrize( "access,secret", [('foo\xe2\x80\x99', 'bar\xe2\x80\x99'), ('foo', 'bar')] ) @@ -127,8 +125,6 @@ async def load(): return _f -@pytest.mark.moto -@pytest.mark.asyncio async def test_assumerolefetcher_no_cache(): response = { 'Credentials': { @@ -150,8 +146,6 @@ async def test_assumerolefetcher_no_cache(): assert response == expected_response -@pytest.mark.moto -@pytest.mark.asyncio async def test_assumerolefetcher_cache_key_with_role_session_name(): response = { 'Credentials': { @@ -180,8 +174,6 @@ async def test_assumerolefetcher_cache_key_with_role_session_name(): assert cache[cache_key] == response -@pytest.mark.moto -@pytest.mark.asyncio async def test_assumerolefetcher_cache_in_cache_but_expired(): response = { 'Credentials': { @@ -215,8 +207,6 @@ async def test_assumerolefetcher_cache_in_cache_but_expired(): assert response == expected -@pytest.mark.moto -@pytest.mark.asyncio async def test_assumerolefetcher_mfa(): response = { 'Credentials': { @@ -248,8 +238,6 @@ async def test_assumerolefetcher_mfa(): assert call_kwargs['TokenCode'] == 'token-code' -@pytest.mark.moto -@pytest.mark.asyncio async def test_recursive_assume_role(assume_role_setup): self = assume_role_setup @@ -306,8 +294,6 @@ async def __aexit__(self, exc_type, exc_val, exc_tb): return mock.Mock(return_value=_Client(with_response)) -@pytest.mark.moto -@pytest.mark.asyncio async def test_webidentfetcher_no_cache(): response = { 'Credentials': { @@ -329,8 +315,6 @@ async def test_webidentfetcher_no_cache(): assert response == expected_response -@pytest.mark.moto -@pytest.mark.asyncio async def test_credresolver_load_credentials_single_provider( credential_provider, ): @@ -347,8 +331,6 @@ async def test_credresolver_load_credentials_single_provider( assert creds.token == 'c' -@pytest.mark.moto -@pytest.mark.asyncio async def test_credresolver_no_providers(credential_provider): provider1 = credential_provider('provider1', 'CustomProvider1', None) resolver = credentials.AioCredentialResolver(providers=[provider1]) @@ -358,8 +340,6 @@ async def test_credresolver_no_providers(credential_provider): # From class TestCanonicalNameSourceProvider(BaseEnvVar): -@pytest.mark.moto -@pytest.mark.asyncio async def test_canonicalsourceprovider_source_creds(credential_provider): creds = credentials.AioCredentials('a', 'b', 'c') provider1 = credential_provider('provider1', 'CustomProvider1', creds) @@ -372,8 +352,6 @@ async def test_canonicalsourceprovider_source_creds(credential_provider): assert result is creds -@pytest.mark.moto -@pytest.mark.asyncio async def test_canonicalsourceprovider_source_creds_case_insensitive( credential_provider, ): @@ -418,8 +396,6 @@ def _f(config=None): return _f -@pytest.mark.moto -@pytest.mark.asyncio async def test_assumerolecredprovider_assume_role_no_cache( credential_provider, assumerolecredprovider_config_loader ): @@ -461,8 +437,6 @@ async def test_assumerolecredprovider_assume_role_no_cache( # MFA -@pytest.mark.moto -@pytest.mark.asyncio async def test_assumerolecredprovider_mfa( credential_provider, assumerolecredprovider_config_loader ): @@ -520,8 +494,6 @@ async def test_assumerolecredprovider_mfa( assert call_kwargs['TokenCode'] == 'token-code' -@pytest.mark.moto -@pytest.mark.asyncio async def test_assumerolecredprovider_mfa_cannot_refresh_credentials( credential_provider, assumerolecredprovider_config_loader ): @@ -577,8 +549,6 @@ async def test_assumerolecredprovider_mfa_cannot_refresh_credentials( # From class TestAssumeRoleWithWebIdentityCredentialProvider -@pytest.mark.moto -@pytest.mark.asyncio async def test_assumerolewebidentprovider_no_cache(): future = datetime.now(tzlocal()) + timedelta(hours=24) @@ -630,8 +600,6 @@ def full_url(url): # From class TestEnvVar(BaseEnvVar): -@pytest.mark.moto -@pytest.mark.asyncio async def test_envvarprovider_env_var_present(): environ = { 'AWS_ACCESS_KEY_ID': 'foo', @@ -646,8 +614,6 @@ async def test_envvarprovider_env_var_present(): assert creds.method == 'env' -@pytest.mark.moto -@pytest.mark.asyncio async def test_envvarprovider_env_var_absent(): environ = {} provider = credentials.AioEnvProvider(environ) @@ -655,8 +621,6 @@ async def test_envvarprovider_env_var_absent(): assert creds is None -@pytest.mark.moto -@pytest.mark.asyncio async def test_envvarprovider_env_var_expiry(): expiry_time = datetime.now(tzlocal()) - timedelta(hours=1) environ = { @@ -691,8 +655,6 @@ def profile_config(): return parser -@pytest.mark.moto -@pytest.mark.asyncio async def test_configprovider_file_exists(profile_config): provider = credentials.AioConfigProvider( 'cli.cfg', 'default', profile_config @@ -705,8 +667,6 @@ async def test_configprovider_file_exists(profile_config): assert creds.method == 'config-file' -@pytest.mark.moto -@pytest.mark.asyncio async def test_configprovider_file_missing_profile(profile_config): provider = credentials.AioConfigProvider( 'cli.cfg', 'NOT-default', profile_config @@ -716,8 +676,6 @@ async def test_configprovider_file_missing_profile(profile_config): # From class TestSharedCredentialsProvider(BaseEnvVar): -@pytest.mark.moto -@pytest.mark.asyncio async def test_sharedcredentials_file_exists(): parser = mock.Mock() parser.return_value = { @@ -740,8 +698,6 @@ async def test_sharedcredentials_file_exists(): assert creds.method == 'shared-credentials-file' -@pytest.mark.moto -@pytest.mark.asyncio async def test_sharedcredentials_file_missing(): parser = mock.Mock() parser.side_effect = botocore.exceptions.ConfigNotFound(path='foo') @@ -754,8 +710,6 @@ async def test_sharedcredentials_file_missing(): # From class TestBotoProvider(BaseEnvVar): -@pytest.mark.moto -@pytest.mark.asyncio async def test_botoprovider_file_exists(): parser = mock.Mock() parser.return_value = { @@ -774,8 +728,6 @@ async def test_botoprovider_file_exists(): assert creds.method == 'boto-config' -@pytest.mark.moto -@pytest.mark.asyncio async def test_botoprovider_file_missing(): parser = mock.Mock() parser.side_effect = botocore.exceptions.ConfigNotFound(path='foo') @@ -786,8 +738,6 @@ async def test_botoprovider_file_missing(): # From class TestOriginalEC2Provider(BaseEnvVar): -@pytest.mark.moto -@pytest.mark.asyncio async def test_originalec2provider_file_exists(): envrion = {'AWS_CREDENTIAL_FILE': 'foo.cfg'} parser = mock.Mock() @@ -807,8 +757,6 @@ async def test_originalec2provider_file_exists(): assert creds.method == 'ec2-credentials-file' -@pytest.mark.moto -@pytest.mark.asyncio async def test_originalec2provider_file_missing(): provider = credentials.AioOriginalEC2Provider(environ={}) creds = await provider.load() @@ -854,8 +802,6 @@ def fake_set_config_variable(self, logical_name, value): return _f -@pytest.mark.moto -@pytest.mark.asyncio async def test_createcredentialresolver(mock_session): session = mock_session() @@ -863,8 +809,6 @@ async def test_createcredentialresolver(mock_session): assert isinstance(resolver, credentials.AioCredentialResolver) -@pytest.mark.moto -@pytest.mark.asyncio async def test_get_credentials(mock_session): session = mock_session() @@ -1098,8 +1042,6 @@ def assume_role_setup(base_assume_role_test_setup): yield self -@pytest.mark.moto -@pytest.mark.asyncio async def test_sso_credential_fetcher_can_fetch_credentials( ssl_credential_fetcher_setup, ): @@ -1141,8 +1083,6 @@ async def test_sso_credential_fetcher_can_fetch_credentials( self.assertEqual(self.cache[cache_key], expected_cached_credentials) -@pytest.mark.moto -@pytest.mark.asyncio async def test_sso_cred_fetcher_raises_helpful_message_on_unauthorized_exception( ssl_credential_fetcher_setup, ): @@ -1246,8 +1186,6 @@ def _add_get_role_credentials_response(self): ) -@pytest.mark.moto -@pytest.mark.asyncio async def test_load_sso_credentials_without_cache(sso_provider_setup): self = sso_provider_setup _add_get_role_credentials_response(self) @@ -1259,8 +1197,6 @@ async def test_load_sso_credentials_without_cache(sso_provider_setup): self.assertEqual(credentials.token, 'baz') -@pytest.mark.moto -@pytest.mark.asyncio async def test_load_sso_credentials_with_cache(sso_provider_setup): self = sso_provider_setup @@ -1280,8 +1216,6 @@ async def test_load_sso_credentials_with_cache(sso_provider_setup): self.assertEqual(credentials.token, 'cached-st') -@pytest.mark.moto -@pytest.mark.asyncio async def test_load_sso_credentials_with_cache_expired(sso_provider_setup): self = sso_provider_setup cached_creds = { @@ -1304,8 +1238,6 @@ async def test_load_sso_credentials_with_cache_expired(sso_provider_setup): self.assertEqual(credentials.token, 'baz') -@pytest.mark.moto -@pytest.mark.asyncio async def test_required_config_not_set(sso_provider_setup): self = sso_provider_setup del self.config['sso_start_url'] diff --git a/tests/boto_tests/unit/test_eventstream.py b/tests/boto_tests/unit/test_eventstream.py new file mode 100644 index 00000000..4af1f33f --- /dev/null +++ b/tests/boto_tests/unit/test_eventstream.py @@ -0,0 +1,581 @@ +# Copyright 2018 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. +"""Unit tests for the binary event stream decoder.""" + +from unittest import mock + +import pytest +from botocore.eventstream import ( + ChecksumMismatch, + DecodeUtils, + DuplicateHeader, + EventStreamBuffer, + EventStreamHeaderParser, + EventStreamMessage, + InvalidHeadersLength, + InvalidPayloadLength, + MessagePrelude, + NoInitialResponseError, +) +from botocore.exceptions import EventStreamError +from botocore.parsers import EventStreamXMLParser + +from aiobotocore.eventstream import AioEventStream + +EMPTY_MESSAGE = ( + b'\x00\x00\x00\x10\x00\x00\x00\x00\x05\xc2H\xeb}\x98\xc8\xff', + EventStreamMessage( + prelude=MessagePrelude( + total_length=0x10, + headers_length=0, + crc=0x05C248EB, + ), + headers={}, + payload=b'', + crc=0x7D98C8FF, + ), +) + +INT8_HEADER = ( + ( + b"\x00\x00\x00\x17\x00\x00\x00\x07)\x86\x01X\x04" + b"byte\x02\xff\xc2\xf8i\xdc" + ), + EventStreamMessage( + prelude=MessagePrelude( + total_length=0x17, + headers_length=0x7, + crc=0x29860158, + ), + headers={'byte': -1}, + payload=b'', + crc=0xC2F869DC, + ), +) + +INT16_HEADER = ( + ( + b"\x00\x00\x00\x19\x00\x00\x00\tq\x0e\x92>\x05" + b"short\x03\xff\xff\xb2|\xb6\xcc" + ), + EventStreamMessage( + prelude=MessagePrelude( + total_length=0x19, + headers_length=0x9, + crc=0x710E923E, + ), + headers={'short': -1}, + payload=b'', + crc=0xB27CB6CC, + ), +) + +INT32_HEADER = ( + ( + b"\x00\x00\x00\x1d\x00\x00\x00\r\x83\xe3\xf0\xe7\x07" + b"integer\x04\xff\xff\xff\xff\x8b\x8e\x12\xeb" + ), + EventStreamMessage( + prelude=MessagePrelude( + total_length=0x1D, + headers_length=0xD, + crc=0x83E3F0E7, + ), + headers={'integer': -1}, + payload=b'', + crc=0x8B8E12EB, + ), +) + +INT64_HEADER = ( + ( + b"\x00\x00\x00\x1e\x00\x00\x00\x0e]J\xdb\x8d\x04" + b"long\x05\xff\xff\xff\xff\xff\xff\xff\xffK\xc22\xda" + ), + EventStreamMessage( + prelude=MessagePrelude( + total_length=0x1E, + headers_length=0xE, + crc=0x5D4ADB8D, + ), + headers={'long': -1}, + payload=b'', + crc=0x4BC232DA, + ), +) + +PAYLOAD_NO_HEADERS = ( + b"\x00\x00\x00\x1d\x00\x00\x00\x00\xfdR\x8cZ{'foo':'bar'}\xc3e96", + EventStreamMessage( + prelude=MessagePrelude( + total_length=0x1D, + headers_length=0, + crc=0xFD528C5A, + ), + headers={}, + payload=b"{'foo':'bar'}", + crc=0xC3653936, + ), +) + +PAYLOAD_ONE_STR_HEADER = ( + ( + b"\x00\x00\x00=\x00\x00\x00 \x07\xfd\x83\x96\x0ccontent-type\x07\x00\x10" + b"application/json{'foo':'bar'}\x8d\x9c\x08\xb1" + ), + EventStreamMessage( + prelude=MessagePrelude( + total_length=0x3D, + headers_length=0x20, + crc=0x07FD8396, + ), + headers={'content-type': 'application/json'}, + payload=b"{'foo':'bar'}", + crc=0x8D9C08B1, + ), +) + +ALL_HEADERS_TYPES = ( + ( + b"\x00\x00\x00\x62\x00\x00\x00\x52\x03\xb5\xcb\x9c" + b"\x010\x00\x011\x01\x012\x02\x02\x013\x03\x00\x03" + b"\x014\x04\x00\x00\x00\x04\x015\x05\x00\x00\x00\x00\x00\x00\x00\x05" + b"\x016\x06\x00\x05bytes\x017\x07\x00\x04utf8" + b"\x018\x08\x00\x00\x00\x00\x00\x00\x00\x08\x019\x090123456789abcdef" + b"\x63\x35\x36\x71" + ), + EventStreamMessage( + prelude=MessagePrelude( + total_length=0x62, + headers_length=0x52, + crc=0x03B5CB9C, + ), + headers={ + '0': True, + '1': False, + '2': 0x02, + '3': 0x03, + '4': 0x04, + '5': 0x05, + '6': b'bytes', + '7': 'utf8', + '8': 0x08, + '9': b'0123456789abcdef', + }, + payload=b"", + crc=0x63353671, + ), +) + +ERROR_EVENT_MESSAGE = ( + ( + b"\x00\x00\x00\x52\x00\x00\x00\x42\xbf\x23\x63\x7e" + b"\x0d:message-type\x07\x00\x05error" + b"\x0b:error-code\x07\x00\x04code" + b"\x0e:error-message\x07\x00\x07message" + b"\x6b\x6c\xea\x3d" + ), + EventStreamMessage( + prelude=MessagePrelude( + total_length=0x52, + headers_length=0x42, + crc=0xBF23637E, + ), + headers={ + ':message-type': 'error', + ':error-code': 'code', + ':error-message': 'message', + }, + payload=b'', + crc=0x6B6CEA3D, + ), +) + +# Tuples of encoded messages and their expected decoded output +POSITIVE_CASES = [ + EMPTY_MESSAGE, + INT8_HEADER, + INT16_HEADER, + INT32_HEADER, + INT64_HEADER, + PAYLOAD_NO_HEADERS, + PAYLOAD_ONE_STR_HEADER, + ALL_HEADERS_TYPES, + ERROR_EVENT_MESSAGE, +] + +CORRUPTED_HEADER_LENGTH = ( + ( + b"\x00\x00\x00=\xff\x00\x01\x02\x07\xfd\x83\x96\x0ccontent-type\x07\x00" + b"\x10application/json{'foo':'bar'}\x8d\x9c\x08\xb1" + ), + ChecksumMismatch, +) + +CORRUPTED_HEADERS = ( + ( + b"\x00\x00\x00=\x00\x00\x00 \x07\xfd\x83\x96\x0ccontent+type\x07\x00\x10" + b"application/json{'foo':'bar'}\x8d\x9c\x08\xb1" + ), + ChecksumMismatch, +) + +CORRUPTED_LENGTH = ( + b"\x01\x00\x00\x1d\x00\x00\x00\x00\xfdR\x8cZ{'foo':'bar'}\xc3e96", + ChecksumMismatch, +) + +CORRUPTED_PAYLOAD = ( + b"\x00\x00\x00\x1d\x00\x00\x00\x00\xfdR\x8cZ{'foo':'bar'\x8d\xc3e96", + ChecksumMismatch, +) + +DUPLICATE_HEADER = ( + ( + b"\x00\x00\x00\x24\x00\x00\x00\x14\x4b\xb9\x82\xd0" + b"\x04test\x04asdf\x04test\x04asdf\xf3\xf4\x75\x63" + ), + DuplicateHeader, +) + +# In contrast to the CORRUPTED_HEADERS case, this message is otherwise +# well-formed - the checksums match. +INVALID_HEADERS_LENGTH = ( + ( + b"\x00\x00\x00\x3d" # total length + b"\xff\x00\x01\x02" # headers length + b"\x15\x83\xf5\xc2" # prelude crc + b"\x0ccontent-type\x07\x00\x10application/json" # headers + b"{'foo':'bar'}" # payload + b"\x2f\x37\x7f\x5d" # message crc + ), + InvalidHeadersLength, +) + +# In contrast to the CORRUPTED_PAYLOAD case, this message is otherwise +# well-formed - the checksums match. +INVALID_PAYLOAD_LENGTH = ( + b"\x01\x00\x00\x11" # total length + + b"\x00\x00\x00\x00" # headers length + + b"\xf4\x08\x61\xc5" # prelude crc + + b"0" * (16 * 1024**2 + 1) # payload + + b"\x2a\xb4\xc5\xa5", # message crc + InvalidPayloadLength, +) + +# Tuples of encoded messages and their expected exception +NEGATIVE_CASES = [ + CORRUPTED_LENGTH, + CORRUPTED_PAYLOAD, + CORRUPTED_HEADERS, + CORRUPTED_HEADER_LENGTH, + DUPLICATE_HEADER, + INVALID_HEADERS_LENGTH, + INVALID_PAYLOAD_LENGTH, +] + + +def assert_message_equal(message_a, message_b): + """Asserts all fields for two messages are equal.""" + assert message_a.prelude.total_length == message_b.prelude.total_length + assert message_a.prelude.headers_length == message_b.prelude.headers_length + assert message_a.prelude.crc == message_b.prelude.crc + assert message_a.headers == message_b.headers + assert message_a.payload == message_b.payload + assert message_a.crc == message_b.crc + + +def test_partial_message(): + """Ensure that we can receive partial payloads.""" + data = EMPTY_MESSAGE[0] + event_buffer = EventStreamBuffer() + # This mid point is an arbitrary break in the middle of the headers + mid_point = 15 + event_buffer.add_data(data[:mid_point]) + messages = list(event_buffer) + assert messages == [] + event_buffer.add_data(data[mid_point : len(data)]) + for message in event_buffer: + assert_message_equal(message, EMPTY_MESSAGE[1]) + + +def check_message_decodes(encoded, decoded): + """Ensure the message decodes to what we expect.""" + event_buffer = EventStreamBuffer() + event_buffer.add_data(encoded) + messages = list(event_buffer) + assert len(messages) == 1 + assert_message_equal(messages[0], decoded) + + +@pytest.mark.parametrize("encoded, decoded", POSITIVE_CASES) +def test_positive_cases(encoded, decoded): + """Test that all positive cases decode how we expect.""" + check_message_decodes(encoded, decoded) + + +def test_all_positive_cases(): + """Test all positive cases can be decoded on the same buffer.""" + event_buffer = EventStreamBuffer() + # add all positive test cases to the same buffer + for encoded, _ in POSITIVE_CASES: + event_buffer.add_data(encoded) + # collect all of the expected messages + expected_messages = [decoded for (_, decoded) in POSITIVE_CASES] + # collect all of the decoded messages + decoded_messages = list(event_buffer) + # assert all messages match what we expect + for expected, decoded in zip(expected_messages, decoded_messages): + assert_message_equal(expected, decoded) + + +@pytest.mark.parametrize( + "encoded, exception", + NEGATIVE_CASES, + ids=[ + "corrupted-length", + "corrupted-payload", + "corrupted-headers", + "corrupted-headers-length", + "duplicate-headers", + "invalid-headers-length", + "invalid-payload-length", + ], +) +def test_negative_cases(encoded, exception): + """Test that all negative cases raise the expected exception.""" + with pytest.raises(exception): + check_message_decodes(encoded, None) + + +def test_header_parser(): + """Test that the header parser supports all header types.""" + headers_data = ( + b"\x010\x00\x011\x01\x012\x02\x02\x013\x03\x00\x03" + b"\x014\x04\x00\x00\x00\x04\x015\x05\x00\x00\x00\x00\x00\x00\x00\x05" + b"\x016\x06\x00\x05bytes\x017\x07\x00\x04utf8" + b"\x018\x08\x00\x00\x00\x00\x00\x00\x00\x08\x019\x090123456789abcdef" + ) + + expected_headers = { + '0': True, + '1': False, + '2': 0x02, + '3': 0x03, + '4': 0x04, + '5': 0x05, + '6': b'bytes', + '7': 'utf8', + '8': 0x08, + '9': b'0123456789abcdef', + } + + parser = EventStreamHeaderParser() + headers = parser.parse(headers_data) + assert headers == expected_headers + + +def test_message_prelude_properties(): + """Test that calculated properties from the payload are correct.""" + # Total length: 40, Headers Length: 15, random crc + prelude = MessagePrelude(40, 15, 0x00000000) + assert prelude.payload_length == 9 + assert prelude.headers_end == 27 + assert prelude.payload_end == 36 + + +def test_message_to_response_dict(): + response_dict = PAYLOAD_ONE_STR_HEADER[1].to_response_dict() + assert response_dict['status_code'] == 200 + + expected_headers = {'content-type': 'application/json'} + assert response_dict['headers'] == expected_headers + assert response_dict['body'] == b"{'foo':'bar'}" + + +def test_message_to_response_dict_error(): + response_dict = ERROR_EVENT_MESSAGE[1].to_response_dict() + assert response_dict['status_code'] == 400 + headers = { + ':message-type': 'error', + ':error-code': 'code', + ':error-message': 'message', + } + assert response_dict['headers'] == headers + assert response_dict['body'] == b'' + + +def test_unpack_uint8(): + (value, bytes_consumed) = DecodeUtils.unpack_uint8(b'\xde') + assert bytes_consumed == 1 + assert value == 0xDE + + +def test_unpack_uint32(): + (value, bytes_consumed) = DecodeUtils.unpack_uint32(b'\xde\xad\xbe\xef') + assert bytes_consumed == 4 + assert value == 0xDEADBEEF + + +def test_unpack_int8(): + (value, bytes_consumed) = DecodeUtils.unpack_int8(b'\xfe') + assert bytes_consumed == 1 + assert value == -2 + + +def test_unpack_int16(): + (value, bytes_consumed) = DecodeUtils.unpack_int16(b'\xff\xfe') + assert bytes_consumed == 2 + assert value == -2 + + +def test_unpack_int32(): + (value, bytes_consumed) = DecodeUtils.unpack_int32(b'\xff\xff\xff\xfe') + assert bytes_consumed == 4 + assert value == -2 + + +def test_unpack_int64(): + test_bytes = b'\xff\xff\xff\xff\xff\xff\xff\xfe' + (value, bytes_consumed) = DecodeUtils.unpack_int64(test_bytes) + assert bytes_consumed == 8 + assert value == -2 + + +def test_unpack_array_short(): + test_bytes = b'\x00\x10application/json' + (value, bytes_consumed) = DecodeUtils.unpack_byte_array(test_bytes) + assert bytes_consumed == 18 + assert value == b'application/json' + + +def test_unpack_byte_array_int(): + (value, array_bytes_consumed) = DecodeUtils.unpack_byte_array( + b'\x00\x00\x00\x10application/json', length_byte_size=4 + ) + assert array_bytes_consumed == 20 + assert value == b'application/json' + + +def test_unpack_utf8_string(): + length = b'\x00\x09' + utf8_string = b'\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e' + encoded = length + utf8_string + (value, bytes_consumed) = DecodeUtils.unpack_utf8_string(encoded) + assert bytes_consumed == 11 + assert value == utf8_string.decode('utf-8') + + +def test_unpack_prelude(): + data = b'\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03' + prelude = DecodeUtils.unpack_prelude(data) + assert prelude == ((1, 2, 3), 12) + + +def create_mock_raw_stream(*data: bytes): + data = list(data) + raw_stream = mock.Mock() + + class generator: + def __aiter__(self): + return self + + async def __anext__(self): + nonlocal data + + if not data: + raise StopAsyncIteration + + chunk = data.pop(0) + return chunk, True + + raw_stream.content.iter_chunks = generator + return raw_stream + + +async def test_event_stream_wrapper_iteration(): + raw_stream = create_mock_raw_stream( + b"\x00\x00\x00+\x00\x00\x00\x0e4\x8b\xec{\x08event-id\x04\x00", + b"\x00\xa0\x0c{'foo':'bar'}\xd3\x89\x02\x85", + ) + parser = mock.Mock(spec=EventStreamXMLParser) + output_shape = mock.Mock() + event_stream = AioEventStream(raw_stream, output_shape, parser, '') + events = [e async for e in event_stream] + assert len(events) == 1 + + response_dict = { + 'headers': {'event-id': 0x0000A00C}, + 'body': b"{'foo':'bar'}", + 'status_code': 200, + } + parser.parse.assert_called_with(response_dict, output_shape) + + +async def test_eventstream_wrapper_iteration_error(): + raw_stream = create_mock_raw_stream(ERROR_EVENT_MESSAGE[0]) + parser = mock.Mock(spec=EventStreamXMLParser) + parser.parse.return_value = {} + output_shape = mock.Mock() + event_stream = AioEventStream(raw_stream, output_shape, parser, '') + with pytest.raises(EventStreamError): + async for _ in event_stream: + pass + + +def test_event_stream_wrapper_close(): + raw_stream = mock.Mock() + event_stream = AioEventStream(raw_stream, None, None, '') + event_stream.close() + raw_stream.close.assert_called_once_with() + + +async def test_event_stream_initial_response(): + raw_stream = create_mock_raw_stream( + b'\x00\x00\x00~\x00\x00\x00O\xc5\xa3\xdd\xc6\r:message-type\x07\x00', + b'\x05event\x0b:event-type\x07\x00\x10initial-response\r:content-type', + b'\x07\x00\ttext/json{"InitialResponse": "sometext"}\xf6\x98$\x83', + ) + parser = mock.Mock(spec=EventStreamXMLParser) + output_shape = mock.Mock() + event_stream = AioEventStream(raw_stream, output_shape, parser, '') + event = await event_stream.get_initial_response() + headers = { + ':message-type': 'event', + ':event-type': 'initial-response', + ':content-type': 'text/json', + } + payload = b'{"InitialResponse": "sometext"}' + assert event.headers == headers + assert event.payload == payload + + +async def test_event_stream_initial_response_wrong_type(): + raw_stream = create_mock_raw_stream( + b"\x00\x00\x00+\x00\x00\x00\x0e4\x8b\xec{\x08event-id\x04\x00", + b"\x00\xa0\x0c{'foo':'bar'}\xd3\x89\x02\x85", + ) + parser = mock.Mock(spec=EventStreamXMLParser) + output_shape = mock.Mock() + event_stream = AioEventStream(raw_stream, output_shape, parser, '') + with pytest.raises(NoInitialResponseError): + await event_stream.get_initial_response() + + +async def test_event_stream_initial_response_no_event(): + raw_stream = create_mock_raw_stream(b'') + parser = mock.Mock(spec=EventStreamXMLParser) + output_shape = mock.Mock() + event_stream = AioEventStream(raw_stream, output_shape, parser, '') + with pytest.raises(NoInitialResponseError): + await event_stream.get_initial_response() diff --git a/tests/boto_tests/test_signers.py b/tests/boto_tests/unit/test_signers.py similarity index 95% rename from tests/boto_tests/test_signers.py rename to tests/boto_tests/unit/test_signers.py index 84a13e7e..1447b306 100644 --- a/tests/boto_tests/test_signers.py +++ b/tests/boto_tests/unit/test_signers.py @@ -2,15 +2,11 @@ from datetime import timezone from unittest import mock -import pytest - import aiobotocore.credentials import aiobotocore.session import aiobotocore.signers -@pytest.mark.moto -@pytest.mark.asyncio async def test_signers_generate_db_auth_token(rds_client): hostname = 'prod-instance.us-east-1.rds.amazonaws.com' port = 3306 diff --git a/tests/boto_tests/test_utils.py b/tests/boto_tests/unit/test_utils.py similarity index 93% rename from tests/boto_tests/test_utils.py rename to tests/boto_tests/unit/test_utils.py index 8a3cb7bd..05738dd4 100644 --- a/tests/boto_tests/test_utils.py +++ b/tests/boto_tests/unit/test_utils.py @@ -70,8 +70,6 @@ async def send(self, request): return FakeAioHttpSession() -@pytest.mark.moto -@pytest.mark.asyncio async def test_idmsfetcher_disabled(): env = {'AWS_EC2_METADATA_DISABLED': 'true'} fetcher = utils.AioIMDSFetcher(env=env) @@ -80,8 +78,6 @@ async def test_idmsfetcher_disabled(): await fetcher._get_request('path', None) -@pytest.mark.moto -@pytest.mark.asyncio async def test_idmsfetcher_get_token_success(): session = fake_aiohttp_session( ('blah', 200), @@ -94,8 +90,6 @@ async def test_idmsfetcher_get_token_success(): assert response == 'blah' -@pytest.mark.moto -@pytest.mark.asyncio async def test_idmsfetcher_get_token_not_found(): session = fake_aiohttp_session( ('blah', 404), @@ -108,8 +102,6 @@ async def test_idmsfetcher_get_token_not_found(): assert response is None -@pytest.mark.moto -@pytest.mark.asyncio async def test_idmsfetcher_get_token_bad_request(): session = fake_aiohttp_session( ('blah', 400), @@ -122,8 +114,6 @@ async def test_idmsfetcher_get_token_bad_request(): await fetcher._fetch_metadata_token() -@pytest.mark.moto -@pytest.mark.asyncio async def test_idmsfetcher_get_token_timeout(): session = fake_aiohttp_session( [ @@ -137,8 +127,6 @@ async def test_idmsfetcher_get_token_timeout(): assert response is None -@pytest.mark.moto -@pytest.mark.asyncio async def test_idmsfetcher_get_token_retry(): session = fake_aiohttp_session( [ @@ -154,8 +142,6 @@ async def test_idmsfetcher_get_token_retry(): assert response == 'token' -@pytest.mark.moto -@pytest.mark.asyncio async def test_idmsfetcher_retry(): session = fake_aiohttp_session( [ @@ -183,8 +169,6 @@ async def test_idmsfetcher_retry(): await fetcher._get_request('path', None) -@pytest.mark.moto -@pytest.mark.asyncio async def test_idmsfetcher_timeout(): session = fake_aiohttp_session( [ diff --git a/tests/python3.8/boto_tests/unit/__init__.py b/tests/python3.8/boto_tests/unit/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/python3.8/boto_tests/test_credentials.py b/tests/python3.8/boto_tests/unit/test_credentials.py similarity index 94% rename from tests/python3.8/boto_tests/test_credentials.py rename to tests/python3.8/boto_tests/unit/test_credentials.py index d6676bb8..5c24070a 100644 --- a/tests/python3.8/boto_tests/test_credentials.py +++ b/tests/python3.8/boto_tests/unit/test_credentials.py @@ -10,7 +10,7 @@ from aiobotocore import credentials from aiobotocore.session import AioSession -from tests.boto_tests.test_credentials import full_url +from tests.boto_tests.unit.test_credentials import full_url # From class TestRefreshableCredentials(TestCredentials): @@ -78,8 +78,6 @@ def _f(mock_time_return_value=None, refresher_return_value='METADATA'): return _f -@pytest.mark.moto -@pytest.mark.asyncio async def test_refreshablecredentials_get_credentials_set(refreshable_creds): creds = refreshable_creds( mock_time_return_value=( @@ -96,8 +94,6 @@ async def test_refreshablecredentials_get_credentials_set(refreshable_creds): assert credentials_set.token == 'ORIGINAL-TOKEN' -@pytest.mark.moto -@pytest.mark.asyncio async def test_refreshablecredentials_refresh_returns_empty_dict( refreshable_creds, ): @@ -112,8 +108,6 @@ async def test_refreshablecredentials_refresh_returns_empty_dict( await creds.get_frozen_credentials() -@pytest.mark.moto -@pytest.mark.asyncio async def test_refreshablecredentials_refresh_returns_none(refreshable_creds): creds = refreshable_creds( mock_time_return_value=datetime.now(tzlocal()), @@ -126,8 +120,6 @@ async def test_refreshablecredentials_refresh_returns_none(refreshable_creds): await creds.get_frozen_credentials() -@pytest.mark.moto -@pytest.mark.asyncio async def test_refreshablecredentials_refresh_returns_partial( refreshable_creds, ): @@ -142,8 +134,6 @@ async def test_refreshablecredentials_refresh_returns_partial( await creds.get_frozen_credentials() -@pytest.mark.moto -@pytest.mark.asyncio async def test_deferrablecredentials_get_credentials_set(deferrable_creds): creds = deferrable_creds() @@ -153,8 +143,6 @@ async def test_deferrablecredentials_get_credentials_set(deferrable_creds): assert creds._refresh_using.call_count == 1 -@pytest.mark.moto -@pytest.mark.asyncio async def test_deferrablecredentials_refresh_only_called_once( deferrable_creds, ): @@ -169,8 +157,6 @@ async def test_deferrablecredentials_refresh_only_called_once( # From class TestInstanceMetadataProvider(BaseEnvVar): -@pytest.mark.moto -@pytest.mark.asyncio async def test_instancemetadata_load(): timeobj = datetime.now(tzlocal()) timestamp = (timeobj + timedelta(hours=24)).isoformat() @@ -199,8 +185,6 @@ async def test_instancemetadata_load(): assert creds.token == 'c' -@pytest.mark.moto -@pytest.mark.asyncio async def test_containerprovider_assume_role_no_cache(): environ = { 'AWS_CONTAINER_CREDENTIALS_RELATIVE_URI': '/latest/credentials?id=foo' @@ -247,8 +231,6 @@ def _f(profile_name='default', loaded_config=None, invoked_process=None): return _f -@pytest.mark.moto -@pytest.mark.asyncio async def test_processprovider_retrieve_refereshable_creds(process_provider): config = { 'profiles': {'default': {'credential_process': 'my-process /somefile'}} @@ -286,8 +268,6 @@ async def test_processprovider_retrieve_refereshable_creds(process_provider): ) -@pytest.mark.moto -@pytest.mark.asyncio async def test_processprovider_retrieve_creds(process_provider): config = {'profiles': {'default': {'credential_process': 'my-process'}}} invoked_process = mock.AsyncMock() @@ -314,8 +294,6 @@ async def test_processprovider_retrieve_creds(process_provider): assert creds.method == 'custom-process' -@pytest.mark.moto -@pytest.mark.asyncio async def test_processprovider_bad_version(process_provider): config = {'profiles': {'default': {'credential_process': 'my-process'}}} invoked_process = mock.AsyncMock() @@ -338,8 +316,6 @@ async def test_processprovider_bad_version(process_provider): await provider.load() -@pytest.mark.moto -@pytest.mark.asyncio async def test_processprovider_missing_field(process_provider): config = {'profiles': {'default': {'credential_process': 'my-process'}}} invoked_process = mock.AsyncMock() @@ -361,8 +337,6 @@ async def test_processprovider_missing_field(process_provider): await provider.load() -@pytest.mark.moto -@pytest.mark.asyncio async def test_processprovider_bad_exitcode(process_provider): config = {'profiles': {'default': {'credential_process': 'my-process'}}} invoked_process = mock.AsyncMock() @@ -377,8 +351,6 @@ async def test_processprovider_bad_exitcode(process_provider): await provider.load() -@pytest.mark.moto -@pytest.mark.asyncio async def test_processprovider_bad_config(process_provider): config = {'profiles': {'default': {'credential_process': None}}} invoked_process = mock.AsyncMock() @@ -401,8 +373,6 @@ async def test_processprovider_bad_config(process_provider): assert creds is None -@pytest.mark.moto -@pytest.mark.asyncio async def test_session_credentials(): with mock.patch( 'aiobotocore.credentials.AioCredential' 'Resolver.load_credentials' diff --git a/tests/python3.8/boto_tests/test_signers.py b/tests/python3.8/boto_tests/unit/test_signers.py similarity index 96% rename from tests/python3.8/boto_tests/test_signers.py rename to tests/python3.8/boto_tests/unit/test_signers.py index e359ffde..12846716 100644 --- a/tests/python3.8/boto_tests/test_signers.py +++ b/tests/python3.8/boto_tests/unit/test_signers.py @@ -64,8 +64,6 @@ async def base_signer_setup_s3v4() -> dict: # From class TestGenerateUrl -@pytest.mark.moto -@pytest.mark.asyncio async def test_signers_generate_presigned_urls(): with mock.patch( 'aiobotocore.signers.AioRequestSigner.generate_presigned_url' @@ -108,8 +106,6 @@ async def test_signers_generate_presigned_urls(): await client.generate_presigned_url('lalala') -@pytest.mark.moto -@pytest.mark.asyncio async def test_signers_generate_presigned_post(): with mock.patch( 'aiobotocore.signers.AioS3PostPresigner.generate_presigned_post' @@ -145,8 +141,6 @@ async def test_signers_generate_presigned_post(): await client.generate_presigned_url('lalala') -@pytest.mark.moto -@pytest.mark.asyncio async def test_testsigner_get_auth(base_signer_setup: dict): auth_cls = mock.Mock() with mock.patch.dict(botocore.auth.AUTH_TYPE_MAPS, {'v4': auth_cls}): @@ -161,8 +155,6 @@ async def test_testsigner_get_auth(base_signer_setup: dict): ) -@pytest.mark.moto -@pytest.mark.asyncio async def test_testsigner_region_required_for_sig4(base_signer_setup: dict): signer = aiobotocore.signers.AioRequestSigner( ServiceId('service_name'), @@ -177,8 +169,6 @@ async def test_testsigner_region_required_for_sig4(base_signer_setup: dict): await signer.sign('operation_name', base_signer_setup['request']) -@pytest.mark.moto -@pytest.mark.asyncio async def test_testsigner_custom_sign_version(base_signer_setup: dict): signer = base_signer_setup['signer'] with pytest.raises(UnknownSignatureVersionError): @@ -187,8 +177,6 @@ async def test_testsigner_custom_sign_version(base_signer_setup: dict): ) -@pytest.mark.moto -@pytest.mark.asyncio async def test_testsigner_choose_signer_override(base_signer_setup: dict): auth_cls = mock.Mock() auth_cls.REQUIRES_REGION = False @@ -207,8 +195,6 @@ async def test_testsigner_choose_signer_override(base_signer_setup: dict): auth_cls.return_value.add_auth.assert_called_with(request) -@pytest.mark.moto -@pytest.mark.asyncio async def test_testsigner_generate_presigned_url(base_signer_setup: dict): auth_cls = mock.Mock() auth_cls.REQUIRES_REGION = True @@ -238,8 +224,6 @@ async def test_testsigner_generate_presigned_url(base_signer_setup: dict): # From class TestGeneratePresignedPost -@pytest.mark.moto -@pytest.mark.asyncio async def test_testsigner_generate_presigned_post( base_signer_setup_s3v4: dict, ): diff --git a/tests/python3.8/boto_tests/test_tokens.py b/tests/python3.8/boto_tests/unit/test_tokens.py similarity index 99% rename from tests/python3.8/boto_tests/test_tokens.py rename to tests/python3.8/boto_tests/unit/test_tokens.py index 98409717..9cf23a77 100644 --- a/tests/python3.8/boto_tests/test_tokens.py +++ b/tests/python3.8/boto_tests/unit/test_tokens.py @@ -119,7 +119,6 @@ def _run_token_provider_test_case(provider, test_case): assert auth_token is None -@pytest.mark.moto @parametrize(sso_provider_resolution_cases) def test_sso_token_provider_resolution(test_case): mock_session = _create_mock_session(test_case["config"]) @@ -128,7 +127,6 @@ def test_sso_token_provider_resolution(test_case): _run_token_provider_test_case(resolver, test_case) -@pytest.mark.moto @parametrize(sso_provider_resolution_cases) def test_sso_token_provider_profile_name_overrides_session_profile(test_case): mock_session = _create_mock_session(test_case["config"]) @@ -274,8 +272,6 @@ def test_sso_token_provider_profile_name_overrides_session_profile(test_case): ] -@pytest.mark.moto -@pytest.mark.asyncio @parametrize(sso_provider_refresh_cases) async def test_sso_token_provider_refresh(test_case): config = { diff --git a/tests/python3.8/boto_tests/test_utils.py b/tests/python3.8/boto_tests/unit/test_utils.py similarity index 92% rename from tests/python3.8/boto_tests/test_utils.py rename to tests/python3.8/boto_tests/unit/test_utils.py index 50666678..3b8bb39a 100644 --- a/tests/python3.8/boto_tests/test_utils.py +++ b/tests/python3.8/boto_tests/unit/test_utils.py @@ -13,7 +13,7 @@ from aiobotocore import utils from aiobotocore.awsrequest import AioAWSResponse from aiobotocore.utils import AioInstanceMetadataFetcher -from tests.boto_tests.test_utils import fake_aiohttp_session +from tests.boto_tests.unit.test_utils import fake_aiohttp_session from tests.test_response import AsyncBytesIO @@ -79,8 +79,6 @@ def get_imds_response(self, *args, **kwargs): raise response return response - @pytest.mark.moto - @pytest.mark.asyncio async def test_disabled_by_environment(self): env = {'AWS_EC2_METADATA_DISABLED': 'true'} fetcher = AioInstanceMetadataFetcher(env=env) @@ -88,8 +86,6 @@ async def test_disabled_by_environment(self): self.assertEqual(result, {}) self._send.assert_not_called() - @pytest.mark.moto - @pytest.mark.asyncio async def test_disabled_by_environment_mixed_case(self): env = {'AWS_EC2_METADATA_DISABLED': 'tRuE'} fetcher = AioInstanceMetadataFetcher(env=env) @@ -97,8 +93,6 @@ async def test_disabled_by_environment_mixed_case(self): self.assertEqual(result, {}) self._send.assert_not_called() - @pytest.mark.moto - @pytest.mark.asyncio async def test_disabling_env_var_not_true(self): url = 'https://example.com/' env = {'AWS_EC2_METADATA_DISABLED': 'false'} @@ -112,8 +106,6 @@ async def test_disabling_env_var_not_true(self): self.assertEqual(result, self._expected_creds) - @pytest.mark.moto - @pytest.mark.asyncio async def test_includes_user_agent_header(self): user_agent = 'my-user-agent' self.add_get_token_imds_response(token='token') @@ -128,8 +120,6 @@ async def test_includes_user_agent_header(self): for call in self._send.calls: self.assertTrue(call[0][0].headers['User-Agent'], user_agent) - @pytest.mark.moto - @pytest.mark.asyncio async def test_non_200_response_for_role_name_is_retried(self): # Response for role name that have a non 200 status code should # be retried. @@ -144,8 +134,6 @@ async def test_non_200_response_for_role_name_is_retried(self): ).retrieve_iam_role_credentials() self.assertEqual(result, self._expected_creds) - @pytest.mark.moto - @pytest.mark.asyncio async def test_http_connection_error_for_role_name_is_retried(self): # Connection related errors should be retried self.add_get_token_imds_response(token='token') @@ -157,8 +145,6 @@ async def test_http_connection_error_for_role_name_is_retried(self): ).retrieve_iam_role_credentials() self.assertEqual(result, self._expected_creds) - @pytest.mark.moto - @pytest.mark.asyncio async def test_empty_response_for_role_name_is_retried(self): # Response for role name that have a non 200 status code should # be retried. @@ -171,8 +157,6 @@ async def test_empty_response_for_role_name_is_retried(self): ).retrieve_iam_role_credentials() self.assertEqual(result, self._expected_creds) - @pytest.mark.moto - @pytest.mark.asyncio async def test_non_200_response_is_retried(self): self.add_get_token_imds_response(token='token') self.add_get_role_name_imds_response() @@ -187,8 +171,6 @@ async def test_non_200_response_is_retried(self): ).retrieve_iam_role_credentials() self.assertEqual(result, self._expected_creds) - @pytest.mark.moto - @pytest.mark.asyncio async def test_http_connection_errors_is_retried(self): self.add_get_token_imds_response(token='token') self.add_get_role_name_imds_response() @@ -200,8 +182,6 @@ async def test_http_connection_errors_is_retried(self): ).retrieve_iam_role_credentials() self.assertEqual(result, self._expected_creds) - @pytest.mark.moto - @pytest.mark.asyncio async def test_empty_response_is_retried(self): self.add_get_token_imds_response(token='token') self.add_get_role_name_imds_response() @@ -214,8 +194,6 @@ async def test_empty_response_is_retried(self): ).retrieve_iam_role_credentials() self.assertEqual(result, self._expected_creds) - @pytest.mark.moto - @pytest.mark.asyncio async def test_invalid_json_is_retried(self): self.add_get_token_imds_response(token='token') self.add_get_role_name_imds_response() @@ -228,8 +206,6 @@ async def test_invalid_json_is_retried(self): ).retrieve_iam_role_credentials() self.assertEqual(result, self._expected_creds) - @pytest.mark.moto - @pytest.mark.asyncio async def test_exhaust_retries_on_role_name_request(self): self.add_get_token_imds_response(token='token') self.add_imds_response(status_code=400, body=b'') @@ -238,8 +214,6 @@ async def test_exhaust_retries_on_role_name_request(self): ).retrieve_iam_role_credentials() self.assertEqual(result, {}) - @pytest.mark.moto - @pytest.mark.asyncio async def test_exhaust_retries_on_credentials_request(self): self.add_get_token_imds_response(token='token') self.add_get_role_name_imds_response() @@ -249,8 +223,6 @@ async def test_exhaust_retries_on_credentials_request(self): ).retrieve_iam_role_credentials() self.assertEqual(result, {}) - @pytest.mark.moto - @pytest.mark.asyncio async def test_missing_fields_in_credentials_response(self): self.add_get_token_imds_response(token='token') self.add_get_role_name_imds_response() @@ -264,8 +236,6 @@ async def test_missing_fields_in_credentials_response(self): ) self.assertEqual(result, {}) - @pytest.mark.moto - @pytest.mark.asyncio async def test_token_is_included(self): user_agent = 'my-user-agent' self.add_get_token_imds_response(token='token') @@ -284,8 +254,6 @@ async def test_token_is_included(self): ) self.assertEqual(result, self._expected_creds) - @pytest.mark.moto - @pytest.mark.asyncio async def test_metadata_token_not_supported_404(self): user_agent = 'my-user-agent' self.add_imds_response(b'', status_code=404) @@ -300,8 +268,6 @@ async def test_metadata_token_not_supported_404(self): self.assertNotIn('x-aws-ec2-metadata-token', call[0][0].headers) self.assertEqual(result, self._expected_creds) - @pytest.mark.moto - @pytest.mark.asyncio async def test_metadata_token_not_supported_403(self): user_agent = 'my-user-agent' self.add_imds_response(b'', status_code=403) @@ -316,8 +282,6 @@ async def test_metadata_token_not_supported_403(self): self.assertNotIn('x-aws-ec2-metadata-token', call[0][0].headers) self.assertEqual(result, self._expected_creds) - @pytest.mark.moto - @pytest.mark.asyncio async def test_metadata_token_not_supported_405(self): user_agent = 'my-user-agent' self.add_imds_response(b'', status_code=405) @@ -332,8 +296,6 @@ async def test_metadata_token_not_supported_405(self): self.assertNotIn('x-aws-ec2-metadata-token', call[0][0].headers) self.assertEqual(result, self._expected_creds) - @pytest.mark.moto - @pytest.mark.asyncio async def test_metadata_token_not_supported_timeout(self): user_agent = 'my-user-agent' self.add_imds_connection_error(ReadTimeoutError(endpoint_url='url')) @@ -348,8 +310,6 @@ async def test_metadata_token_not_supported_timeout(self): self.assertNotIn('x-aws-ec2-metadata-token', call[0][0].headers) self.assertEqual(result, self._expected_creds) - @pytest.mark.moto - @pytest.mark.asyncio async def test_token_not_supported_exhaust_retries(self): user_agent = 'my-user-agent' self.add_imds_connection_error(ConnectTimeoutError(endpoint_url='url')) @@ -364,8 +324,6 @@ async def test_token_not_supported_exhaust_retries(self): self.assertNotIn('x-aws-ec2-metadata-token', call[0][0].headers) self.assertEqual(result, self._expected_creds) - @pytest.mark.moto - @pytest.mark.asyncio async def test_metadata_token_bad_request_yields_no_credentials(self): user_agent = 'my-user-agent' self.add_imds_response(b'', status_code=400) @@ -375,8 +333,6 @@ async def test_metadata_token_bad_request_yields_no_credentials(self): self.assertEqual(result, {}) -@pytest.mark.moto -@pytest.mark.asyncio async def test_containermetadatafetcher_retrieve_url(): json_body = json.dumps( { @@ -406,8 +362,6 @@ async def test_containermetadatafetcher_retrieve_url(): assert resp['Expiration'] == 'd' -@pytest.mark.moto -@pytest.mark.asyncio async def test_containermetadatafetcher_retrieve_url_bad_status(): json_body = "not json" @@ -419,8 +373,6 @@ async def test_containermetadatafetcher_retrieve_url_bad_status(): await fetcher.retrieve_uri('/foo?id=1') -@pytest.mark.moto -@pytest.mark.asyncio async def test_containermetadatafetcher_retrieve_url_not_json(): json_body = "not json" diff --git a/tests/python3.8/test_eventstreams.py b/tests/python3.8/test_eventstreams.py index 5efa337d..37877880 100644 --- a/tests/python3.8/test_eventstreams.py +++ b/tests/python3.8/test_eventstreams.py @@ -6,7 +6,7 @@ import aiobotocore.session -@pytest.mark.asyncio +@pytest.mark.localonly async def test_kinesis_stream_json_parser(exit_stack: AsyncExitStack): # unfortunately moto doesn't support kinesis register_stream_consumer + # subscribe_to_shard yet diff --git a/tests/test_adaptive.py b/tests/test_adaptive.py index 6845b251..f9b014db 100644 --- a/tests/test_adaptive.py +++ b/tests/test_adaptive.py @@ -54,13 +54,11 @@ def create_client_limiter(self): ) return rate_limiter - @pytest.mark.asyncio async def test_bucket_bucket_acquisition_only_if_enabled(self): rate_limiter = self.create_client_limiter() await rate_limiter.on_sending_request(request=mock.sentinel.request) assert not self.token_bucket.acquire.called - @pytest.mark.asyncio async def test_token_bucket_enabled_on_throttling_error(self): rate_limiter = self.create_client_limiter() self.throttling_detector.is_throttling_error.return_value = True @@ -73,16 +71,14 @@ async def test_token_bucket_enabled_on_throttling_error(self): await rate_limiter.on_sending_request(request=mock.sentinel.request) assert self.token_bucket.acquire.called - @pytest.mark.asyncio async def test_max_rate_updated_on_success_response(self): rate_limiter = self.create_client_limiter() self.throttling_detector.is_throttling_error.return_value = False self.rate_adjustor.success_received.return_value = 20 self.rate_clocker.record.return_value = 21 await rate_limiter.on_receiving_response() - assert await self.token_bucket.set_max_rate.called_with(20) + self.token_bucket.set_max_rate.assert_called_with(20) - @pytest.mark.asyncio async def test_max_rate_cant_exceed_20_percent_max(self): rate_limiter = self.create_client_limiter() self.throttling_detector.is_throttling_error.return_value = False @@ -93,7 +89,7 @@ async def test_max_rate_cant_exceed_20_percent_max(self): # The most we should go up is 2.0 * 20 await rate_limiter.on_receiving_response() - assert await self.token_bucket.set_max_rate.called_with(2.0 * 20) + self.token_bucket.set_max_rate.assert_called_with(2.0 * 20) class TestAsyncTokenBucket: @@ -107,7 +103,6 @@ def create_token_bucket(self, max_rate=10, min_rate=0.1): max_rate=max_rate, clock=self.clock, min_rate=min_rate ) - @pytest.mark.asyncio async def test_can_acquire_amount(self): self.timestamp_sequences.extend( [ @@ -124,7 +119,6 @@ async def test_can_acquire_amount(self): for _ in range(5): assert await token_bucket.acquire(1, block=False) - @pytest.mark.asyncio async def test_can_change_max_capacity_lower(self): # Requests at 1 TPS. self.timestamp_sequences.extend([1, 2, 3, 4, 5]) @@ -140,7 +134,6 @@ async def test_can_change_max_capacity_lower(self): for _ in range(5): assert await token_bucket.acquire(1, block=False) - @pytest.mark.asyncio async def test_max_capacity_is_at_least_one(self): token_bucket = self.create_token_bucket() self.timestamp_sequences.append(1) @@ -148,7 +141,6 @@ async def test_max_capacity_is_at_least_one(self): assert token_bucket._fill_rate == 0.5 assert token_bucket._max_capacity == 1 - @pytest.mark.asyncio async def test_acquire_fails_on_non_block_mode_returns_false(self): self.timestamp_sequences.extend( [ @@ -162,7 +154,6 @@ async def test_acquire_fails_on_non_block_mode_returns_false(self): with pytest.raises(CapacityNotAvailableError): await token_bucket.acquire(100, block=False) - @pytest.mark.asyncio async def test_can_retrieve_at_max_send_rate(self): self.timestamp_sequences.extend( [ @@ -175,7 +166,6 @@ async def test_can_retrieve_at_max_send_rate(self): for _ in range(20): assert await token_bucket.acquire(1, block=False) - @pytest.mark.asyncio async def test_acquiring_blocks_when_capacity_reached(self): # This is 1 token every 0.1 seconds. token_bucket = self.create_token_bucket(max_rate=10) @@ -201,7 +191,6 @@ async def test_acquiring_blocks_when_capacity_reached(self): assert token_bucket._current_capacity == 0 assert await token_bucket.acquire(1, block=False) - @pytest.mark.asyncio async def test_rate_cant_go_below_min(self): token_bucket = self.create_token_bucket(max_rate=1, min_rate=0.2) self.timestamp_sequences.append(1) diff --git a/tests/test_basic_s3.py b/tests/test_basic_s3.py index 7ad3fb3a..1853b6d2 100644 --- a/tests/test_basic_s3.py +++ b/tests/test_basic_s3.py @@ -18,8 +18,6 @@ async def fetch_all(pages): return responses -@pytest.mark.moto -@pytest.mark.asyncio async def test_can_make_request(s3_client): # Basic smoke test to ensure we can talk to s3. result = await s3_client.list_buckets() @@ -29,9 +27,7 @@ async def test_can_make_request(s3_client): assert actual_keys == ['Buckets', 'Owner', 'ResponseMetadata'] -@pytest.mark.moto @pytest.mark.parametrize('s3_verify', [False]) -@pytest.mark.asyncio async def test_can_make_request_no_verify(s3_client): # Basic smoke test to ensure we can talk to s3. result = await s3_client.list_buckets() @@ -41,8 +37,6 @@ async def test_can_make_request_no_verify(s3_client): assert actual_keys == ['Buckets', 'Owner', 'ResponseMetadata'] -@pytest.mark.moto -@pytest.mark.asyncio async def test_fail_proxy_request( aa_fail_proxy_config, s3_client, monkeypatch ): @@ -51,16 +45,14 @@ async def test_fail_proxy_request( await s3_client.list_buckets() -@pytest.mark.asyncio @pytest.mark.parametrize('mocking_test', [False]) +@pytest.mark.localonly async def test_succeed_proxy_request(aa_succeed_proxy_config, s3_client): result = await s3_client.list_buckets() actual_keys = sorted(list(result.keys())) assert actual_keys == ['Buckets', 'Owner', 'ResponseMetadata'] -@pytest.mark.asyncio -@pytest.mark.moto async def test_can_get_bucket_location(s3_client, bucket_name): result = await s3_client.get_bucket_location(Bucket=bucket_name) assert 'LocationConstraint' in result @@ -69,8 +61,6 @@ async def test_can_get_bucket_location(s3_client, bucket_name): assert result['LocationConstraint'] in [None, 'us-west-2', 'us-east-1'] -@pytest.mark.moto -@pytest.mark.asyncio async def test_can_delete_urlencoded_object( s3_client, bucket_name, create_object ): @@ -91,8 +81,6 @@ async def test_can_delete_urlencoded_object( pytest.aio.assert_status_code(response, 204) -@pytest.mark.asyncio -@pytest.mark.moto async def test_can_paginate(s3_client, bucket_name, create_object): for i in range(5): key_name = f'key{i}' @@ -107,8 +95,6 @@ async def test_can_paginate(s3_client, bucket_name, create_object): assert key_names == ['key0', 'key1', 'key2', 'key3', 'key4'] -@pytest.mark.asyncio -@pytest.mark.moto async def test_can_paginate_with_page_size( s3_client, bucket_name, create_object ): @@ -128,8 +114,6 @@ async def test_can_paginate_with_page_size( assert key_names == ['key0', 'key1', 'key2', 'key3', 'key4'] -@pytest.mark.asyncio -@pytest.mark.moto async def test_can_search_paginate(s3_client, bucket_name, create_object): keys = [] for i in range(5): @@ -143,8 +127,6 @@ async def test_can_search_paginate(s3_client, bucket_name, create_object): assert key_name in keys -@pytest.mark.asyncio -@pytest.mark.moto async def test_can_paginate_iterator(s3_client, bucket_name, create_object): for i in range(5): key_name = f'key{i}' @@ -163,8 +145,6 @@ async def test_can_paginate_iterator(s3_client, bucket_name, create_object): assert key_names == ['key0', 'key1', 'key2', 'key3', 'key4'] -@pytest.mark.asyncio -@pytest.mark.moto async def test_result_key_iters(s3_client, bucket_name, create_object): for i in range(5): key_name = f'key/{i}/{i}' @@ -194,8 +174,6 @@ async def test_result_key_iters(s3_client, bucket_name, create_object): assert 'CommonPrefixes' in response -@pytest.mark.moto -@pytest.mark.asyncio async def test_can_get_and_put_object(s3_client, create_object, bucket_name): await create_object('foobarbaz', body='body contents') resp = await s3_client.get_object(Bucket=bucket_name, Key='foobarbaz') @@ -205,8 +183,6 @@ async def test_can_get_and_put_object(s3_client, create_object, bucket_name): assert data == b'body contents' -@pytest.mark.moto -@pytest.mark.asyncio @pytest.mark.patch_attributes( [ dict( @@ -259,8 +235,6 @@ async def test_adaptive_retry( patch_attributes[3].assert_not_called() -@pytest.mark.moto -@pytest.mark.asyncio async def test_get_object_stream_wrapper( s3_client, create_object, bucket_name ): @@ -274,8 +248,6 @@ async def test_get_object_stream_wrapper( response['Body'].close() -@pytest.mark.moto -@pytest.mark.asyncio async def test_get_object_stream_context( s3_client, create_object, bucket_name ): @@ -285,8 +257,6 @@ async def test_get_object_stream_context( await stream.read() -@pytest.mark.asyncio -@pytest.mark.moto async def test_paginate_max_items( s3_client, create_multipart_upload, bucket_name ): @@ -326,8 +296,6 @@ async def test_paginate_max_items( assert len(full_result['Uploads']) == 1 -@pytest.mark.moto -@pytest.mark.asyncio async def test_paginate_within_page_boundaries( s3_client, create_object, bucket_name ): @@ -370,8 +338,8 @@ async def test_paginate_within_page_boundaries( assert fourth['Contents'][-1]['Key'] == 'd' -@pytest.mark.asyncio @pytest.mark.parametrize('mocking_test', [False]) +@pytest.mark.localonly async def test_unicode_key_put_list(s3_client, bucket_name, create_object): # Verify we can upload a key with a unicode char and list it as well. key_name = '\u2713' @@ -385,8 +353,8 @@ async def test_unicode_key_put_list(s3_client, bucket_name, create_object): assert data == b'foo' -@pytest.mark.asyncio @pytest.mark.parametrize('mocking_test', [False]) +@pytest.mark.localonly async def test_unicode_system_character(s3_client, bucket_name, create_object): # Verify we can use a unicode system character which would normally # break the xml parser @@ -403,8 +371,6 @@ async def test_unicode_system_character(s3_client, bucket_name, create_object): assert parsed['Contents'][0]['Key'] == 'foo%08' -@pytest.mark.moto -@pytest.mark.asyncio async def test_non_normalized_key_paths(s3_client, bucket_name, create_object): # The create_object method has assertEqual checks for 200 status. await create_object('key./././name') @@ -415,7 +381,7 @@ async def test_non_normalized_key_paths(s3_client, bucket_name, create_object): @pytest.mark.skipif(True, reason='Not supported') -@pytest.mark.asyncio +@pytest.mark.localonly async def test_reset_stream_on_redirects(region, create_bucket): # Create a bucket in a non classic region. bucket_name = await create_bucket(region) @@ -423,8 +389,6 @@ async def test_reset_stream_on_redirects(region, create_bucket): assert bucket_name -@pytest.mark.moto -@pytest.mark.asyncio async def test_copy_with_quoted_char(s3_client, create_object, bucket_name): key_name = 'a+b/foo' await create_object(key_name=key_name) @@ -442,8 +406,6 @@ async def test_copy_with_quoted_char(s3_client, create_object, bucket_name): assert data == b'foo' -@pytest.mark.moto -@pytest.mark.asyncio async def test_copy_with_query_string(s3_client, create_object, bucket_name): key_name = 'a+b/foo?notVersionid=bar' await create_object(key_name=key_name) @@ -462,8 +424,6 @@ async def test_copy_with_query_string(s3_client, create_object, bucket_name): assert data == b'foo' -@pytest.mark.moto -@pytest.mark.asyncio async def test_can_copy_with_dict_form(s3_client, create_object, bucket_name): key_name = 'a+b/foo?versionId=abcd' await create_object(key_name=key_name) @@ -482,8 +442,6 @@ async def test_can_copy_with_dict_form(s3_client, create_object, bucket_name): assert data == b'foo' -@pytest.mark.moto -@pytest.mark.asyncio async def test_can_copy_with_dict_form_with_version( s3_client, create_object, bucket_name ): @@ -507,8 +465,6 @@ async def test_can_copy_with_dict_form_with_version( assert data == b'foo' -@pytest.mark.moto -@pytest.mark.asyncio async def test_copy_with_s3_metadata(s3_client, create_object, bucket_name): key_name = 'foo.txt' await create_object(key_name=key_name) @@ -527,7 +483,7 @@ async def test_copy_with_s3_metadata(s3_client, create_object, bucket_name): @pytest.mark.parametrize('signature_version', ['s3']) # 'Content-Disposition' not supported by moto yet @pytest.mark.parametrize('mocking_test', [False]) -@pytest.mark.asyncio +@pytest.mark.localonly async def test_presign_with_existing_query_string_values( s3_client, bucket_name, aio_session, create_object ): @@ -554,7 +510,7 @@ async def test_presign_with_existing_query_string_values( @pytest.mark.parametrize('signature_version', ['s3v4']) # moto host will be localhost @pytest.mark.parametrize('mocking_test', [False]) -@pytest.mark.asyncio +@pytest.mark.localonly async def test_presign_sigv4( s3_client, bucket_name, aio_session, create_object ): @@ -579,7 +535,7 @@ async def test_presign_sigv4( @pytest.mark.parametrize('signature_version', ['s3v4']) @pytest.mark.parametrize('mocking_test', [False]) -@pytest.mark.asyncio +@pytest.mark.localonly async def test_can_follow_signed_url_redirect( alternative_s3_client, create_object, bucket_name ): @@ -597,7 +553,7 @@ async def test_can_follow_signed_url_redirect( @pytest.mark.parametrize('region', ['eu-west-1']) @pytest.mark.parametrize('alternative_region', ['us-west-2']) @pytest.mark.parametrize('mocking_test', [False]) -@pytest.mark.asyncio +@pytest.mark.localonly async def test_bucket_redirect( s3_client, alternative_s3_client, region, create_bucket ): @@ -617,8 +573,6 @@ async def test_bucket_redirect( @pytest.mark.parametrize('signature_version', ['s3v4']) -@pytest.mark.asyncio -@pytest.mark.moto async def test_head_object_keys(s3_client, create_object, bucket_name): await create_object('foobarbaz') @@ -643,8 +597,6 @@ async def test_head_object_keys(s3_client, create_object, bucket_name): ) @pytest.mark.parametrize('server_scheme', ['https']) @pytest.mark.parametrize('s3_verify', [False]) -@pytest.mark.moto -@pytest.mark.asyncio async def test_put_object_sha256(s3_client, bucket_name): data = b'test1234' digest = hashlib.sha256(data).digest().hex() diff --git a/tests/test_batch.py b/tests/test_batch.py index c9fd5915..ee27cd2c 100644 --- a/tests/test_batch.py +++ b/tests/test_batch.py @@ -1,8 +1,3 @@ -import pytest - - -@pytest.mark.moto -@pytest.mark.asyncio async def test_batch(batch_client): job_queues = await batch_client.describe_job_queues() assert job_queues['jobQueues'] == [] diff --git a/tests/test_client.py b/tests/test_client.py index 87c9fca3..0a1ddaba 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -2,14 +2,12 @@ from botocore.exceptions import OperationNotPageableError -@pytest.mark.moto def test_get_paginator_not_supported_by_service(sns_client): operation_name = 'list_tags_for_resource' with pytest.raises(OperationNotPageableError): sns_client.get_paginator(operation_name) -@pytest.mark.moto def test_get_waiter_not_supported_by_service(sns_client): waiter_name = 'sns_does_not_support_waiters' with pytest.raises( @@ -18,7 +16,6 @@ def test_get_waiter_not_supported_by_service(sns_client): sns_client.get_waiter(waiter_name) -@pytest.mark.moto def test_get_waiter_invalid_waiter_name(cloudformation_client): waiter_name = 'this_name_is_invalid' with pytest.raises( diff --git a/tests/test_config.py b/tests/test_config.py index c42fb76a..38c7e739 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -11,9 +11,6 @@ from tests.mock_server import AIOServer -# NOTE: this doesn't require moto but needs to be marked to run with coverage -@pytest.mark.moto -@pytest.mark.asyncio async def test_connector_args(): with pytest.raises(ParamValidationError): # wrong type @@ -63,8 +60,6 @@ async def test_connector_args(): assert aio_cfg.connector_args['keepalive_timeout'] == 75 -@pytest.mark.moto -@pytest.mark.asyncio async def test_connector_timeout(): session = AioSession() config = AioConfig( @@ -96,8 +91,6 @@ async def get_and_wait(): task2.cancel() -@pytest.mark.moto -@pytest.mark.asyncio async def test_connector_timeout2(): session = AioSession() config = AioConfig( @@ -118,14 +111,11 @@ async def test_connector_timeout2(): await resp["Body"].read() -@pytest.mark.moto -@pytest.mark.asyncio async def test_get_session(): session = get_session() assert isinstance(session, AioSession) -@pytest.mark.moto def test_merge(): config = AioConfig() other_config = AioConfig() @@ -136,8 +126,6 @@ def test_merge(): # Check that it's possible to specify custom http_session_cls -@pytest.mark.moto -@pytest.mark.asyncio async def test_config_http_session_cls(): class SuccessExc(Exception): ... diff --git a/tests/test_configprovider.py b/tests/test_configprovider.py index 1c6636fb..712f780b 100644 --- a/tests/test_configprovider.py +++ b/tests/test_configprovider.py @@ -7,8 +7,6 @@ 'defaults_mode,retry_mode', [('legacy', 'legacy'), ('standard', 'standard'), ('auto', 'standard')], ) -@pytest.mark.moto -@pytest.mark.asyncio async def test_defaults_mode(monkeypatch, defaults_mode, retry_mode): monkeypatch.setenv('AWS_DEFAULTS_MODE', defaults_mode) diff --git a/tests/test_dynamodb.py b/tests/test_dynamodb.py index 3fe4d34e..7ad9fc7b 100644 --- a/tests/test_dynamodb.py +++ b/tests/test_dynamodb.py @@ -24,9 +24,7 @@ def dynamodb_table_def(): ) -@pytest.mark.moto @pytest.mark.parametrize('signature_version', ['v4']) -@pytest.mark.asyncio async def test_get_item(dynamodb_client, table_name, dynamodb_put_item): test_value = 'testValue' await dynamodb_put_item(test_value) @@ -37,9 +35,7 @@ async def test_get_item(dynamodb_client, table_name, dynamodb_put_item): assert response['Item']['testKey'] == {'S': test_value} -@pytest.mark.moto @pytest.mark.parametrize('signature_version', ['v4']) -@pytest.mark.asyncio async def test_create_waiter(dynamodb_client, dynamodb_table_def): table_name = dynamodb_table_def['TableName'] @@ -54,9 +50,7 @@ async def test_create_waiter(dynamodb_client, dynamodb_table_def): assert response['Table']['TableStatus'] == 'ACTIVE' -@pytest.mark.moto @pytest.mark.parametrize('signature_version', ['v4']) -@pytest.mark.asyncio async def test_batch_write_scan(dynamodb_client, table_name): response = await dynamodb_client.batch_write_item( RequestItems={ @@ -89,9 +83,7 @@ async def test_batch_write_scan(dynamodb_client, table_name): assert test_keys == ['key1', 'key3'] -@pytest.mark.moto @pytest.mark.parametrize('signature_version', ['v4']) -@pytest.mark.asyncio async def test_delete_table(dynamodb_client, dynamodb_table_def): table_name = dynamodb_table_def['TableName'] @@ -107,9 +99,7 @@ async def test_delete_table(dynamodb_client, dynamodb_table_def): assert table_name not in response['TableNames'] -@pytest.mark.moto @pytest.mark.parametrize('signature_version', ['v4']) -@pytest.mark.asyncio async def test_waiter_table_exists_failure(dynamodb_client): waiter = dynamodb_client.get_waiter('table_exists') with pytest.raises( @@ -120,19 +110,15 @@ async def test_waiter_table_exists_failure(dynamodb_client): ) -@pytest.mark.moto @pytest.mark.parametrize('signature_version', ['v4']) -@pytest.mark.asyncio -async def test_waiter_table_exists( - event_loop, dynamodb_client, dynamodb_table_def -): +async def test_waiter_table_exists(dynamodb_client, dynamodb_table_def): table_name = dynamodb_table_def['TableName'] async def _create_table(): await asyncio.sleep(2) await dynamodb_client.create_table(**dynamodb_table_def) - task = event_loop.create_task(_create_table()) + task = asyncio.create_task(_create_table()) assert not task.done() waiter = dynamodb_client.get_waiter('table_exists') diff --git a/tests/test_ec2.py b/tests/test_ec2.py index 8085d726..41983e21 100644 --- a/tests/test_ec2.py +++ b/tests/test_ec2.py @@ -1,8 +1,3 @@ -import pytest - - -@pytest.mark.moto -@pytest.mark.asyncio async def test_ec2_snapshot(ec2_client): # TODO: this needs to somehow validate the presigned url sent because moto is not volume_response = await ec2_client.create_volume( diff --git a/tests/test_endpoint.py b/tests/test_endpoint.py index 7d44b074..f9444b39 100644 --- a/tests/test_endpoint.py +++ b/tests/test_endpoint.py @@ -1,8 +1,6 @@ import pytest -@pytest.mark.moto -@pytest.mark.asyncio async def test_invalid_endpoint_url(session, region): endpoint_url = 'invalid_url' with pytest.raises(ValueError, match=f'Invalid endpoint: {endpoint_url}'): diff --git a/tests/test_eventstreams.py b/tests/test_eventstreams.py index 15c2297d..34bcb421 100644 --- a/tests/test_eventstreams.py +++ b/tests/test_eventstreams.py @@ -43,8 +43,6 @@ def iter_chunks(self): return self.ChunkedIterator(self.chunks) -@pytest.mark.moto -@pytest.mark.asyncio async def test_eventstream_chunking(s3_client): # These are the options passed to the EventStream class # during a normal run with botocore. @@ -74,8 +72,6 @@ async def test_eventstream_chunking(s3_client): assert 'End' in event3 -@pytest.mark.moto -@pytest.mark.asyncio async def test_eventstream_no_iter(s3_client): # These are the options passed to the EventStream class # during a normal run with botocore. diff --git a/tests/test_lambda.py b/tests/test_lambda.py index dd8da2eb..50b689ec 100644 --- a/tests/test_lambda.py +++ b/tests/test_lambda.py @@ -41,8 +41,6 @@ def lambda_handler(event, context): return _process_lambda(lambda_src) -@pytest.mark.moto -@pytest.mark.asyncio async def test_run_lambda(iam_client, lambda_client, aws_lambda_zip): role_arn = await _get_role_arn(iam_client, 'test-iam-role') lambda_response = await lambda_client.create_function( diff --git a/tests/test_monitor.py b/tests/test_monitor.py index eb05dc4a..853d15ca 100644 --- a/tests/test_monitor.py +++ b/tests/test_monitor.py @@ -1,10 +1,6 @@ -import pytest - from aiobotocore.session import AioSession -@pytest.mark.moto -@pytest.mark.asyncio async def test_monitor_response_received(session: AioSession, s3_client): # Basic smoke test to ensure we can talk to s3. handler_kwargs = {} diff --git a/tests/test_mturk.py b/tests/test_mturk.py index 865d0854..958ba22c 100644 --- a/tests/test_mturk.py +++ b/tests/test_mturk.py @@ -20,8 +20,7 @@ # Unfortunately moto does not support mturk yet # Also looks like we won't be able to support this (see notes from 1.0.6 release) -# @pytest.mark.moto -@pytest.mark.asyncio +@pytest.mark.localonly async def test_mturk_stubber(session): async with session.create_client( 'mturk', region_name='us-east-1' diff --git a/tests/test_patches.py b/tests/test_patches.py index 1652be73..5401db79 100644 --- a/tests/test_patches.py +++ b/tests/test_patches.py @@ -1,7 +1,6 @@ import hashlib import botocore -import pytest from botocore import retryhandler, stub from botocore.args import ClientArgsCreator from botocore.awsrequest import AWSResponse @@ -749,15 +748,12 @@ _PROTOCOL_PARSER_CONTENT = {'ec2', 'query', 'json', 'rest-json', 'rest-xml'} -@pytest.mark.moto def test_protocol_parsers(): # Check that no new parsers have been added current_parsers = set(PROTOCOL_PARSERS.keys()) assert current_parsers == _PROTOCOL_PARSER_CONTENT -# NOTE: this doesn't require moto but needs to be marked to run with coverage -@pytest.mark.moto def test_patches(): print(f"Botocore version: {botocore.__version__}") diff --git a/tests/test_response.py b/tests/test_response.py index 5a3bae26..85292e56 100644 --- a/tests/test_response.py +++ b/tests/test_response.py @@ -35,16 +35,12 @@ async def _tolist(aiter): return results -@pytest.mark.moto -@pytest.mark.asyncio async def test_streaming_wrapper_validates_content_length(): body = AsyncBytesIO(b'1234567890') stream = response.StreamingBody(body, content_length=10) assert await stream.read() == b'1234567890' -@pytest.mark.moto -@pytest.mark.asyncio async def test_streaming_body_with_invalid_length(): body = AsyncBytesIO(b'123456789') stream = response.StreamingBody(body, content_length=10) @@ -55,8 +51,6 @@ async def test_streaming_body_with_invalid_length(): await stream.read() -@pytest.mark.moto -@pytest.mark.asyncio async def test_streaming_body_with_zero_read(): body = AsyncBytesIO(b'1234567890') stream = response.StreamingBody(body, content_length=10) @@ -65,8 +59,6 @@ async def test_streaming_body_with_zero_read(): assert await stream.read() == b'1234567890' -@pytest.mark.moto -@pytest.mark.asyncio async def test_streaming_body_with_single_read(): body = AsyncBytesIO(b'123456789') stream = response.StreamingBody(body, content_length=10) @@ -74,8 +66,6 @@ async def test_streaming_body_with_single_read(): await stream.read() -@pytest.mark.moto -@pytest.mark.asyncio async def test_streaming_body_closes(): body = AsyncBytesIO(b'1234567890') stream = response.StreamingBody(body, content_length=10) @@ -84,8 +74,6 @@ async def test_streaming_body_closes(): assert body.closed is True -@pytest.mark.moto -@pytest.mark.asyncio async def test_default_iter_behavior(): body = AsyncBytesIO(b'a' * 2048) stream = response.StreamingBody(body, content_length=2048) @@ -94,8 +82,6 @@ async def test_default_iter_behavior(): assert chunks, [b'a' * 1024 == b'a' * 1024] -@pytest.mark.moto -@pytest.mark.asyncio async def test_iter_chunks_single_byte(): body = AsyncBytesIO(b'abcde') stream = response.StreamingBody(body, content_length=5) @@ -103,8 +89,6 @@ async def test_iter_chunks_single_byte(): assert chunks, [b'a', b'b', b'c', b'd' == b'e'] -@pytest.mark.moto -@pytest.mark.asyncio async def test_iter_chunks_with_leftover(): body = AsyncBytesIO(b'abcde') stream = response.StreamingBody(body, content_length=5) @@ -112,8 +96,6 @@ async def test_iter_chunks_with_leftover(): assert chunks, [b'ab', b'cd' == b'e'] -@pytest.mark.moto -@pytest.mark.asyncio async def test_iter_chunks_single_chunk(): body = AsyncBytesIO(b'abcde') stream = response.StreamingBody(body, content_length=5) @@ -121,8 +103,6 @@ async def test_iter_chunks_single_chunk(): assert chunks == [b'abcde'] -@pytest.mark.moto -@pytest.mark.asyncio async def test_streaming_line_iterator(): body = AsyncBytesIO(b'1234567890\n1234567890\n12345') stream = response.StreamingBody(body, content_length=27) @@ -132,8 +112,6 @@ async def test_streaming_line_iterator(): ) -@pytest.mark.moto -@pytest.mark.asyncio async def test_streaming_line_iterator_ends_newline(): body = AsyncBytesIO(b'1234567890\n1234567890\n12345\n') stream = response.StreamingBody(body, content_length=28) @@ -143,8 +121,6 @@ async def test_streaming_line_iterator_ends_newline(): ) -@pytest.mark.moto -@pytest.mark.asyncio async def test_streaming_line_iter_chunk_sizes(): for chunk_size in range(1, 30): body = AsyncBytesIO(b'1234567890\n1234567890\n12345') @@ -155,8 +131,6 @@ async def test_streaming_line_iter_chunk_sizes(): ) -@pytest.mark.moto -@pytest.mark.asyncio async def test_streaming_body_is_an_iterator(): body = AsyncBytesIO(b'a' * 1024 + b'b' * 1024 + b'c' * 2) stream = response.StreamingBody(body, content_length=2050) @@ -167,8 +141,6 @@ async def test_streaming_body_is_an_iterator(): await stream.__anext__() -@pytest.mark.moto -@pytest.mark.asyncio async def test_streaming_line_abstruse_newline_standard(): for chunk_size in range(1, 30): body = AsyncBytesIO(b'1234567890\r\n1234567890\r\n12345\r\n') @@ -179,8 +151,6 @@ async def test_streaming_line_abstruse_newline_standard(): ) -@pytest.mark.moto -@pytest.mark.asyncio async def test_streaming_line_empty_body(): stream = response.StreamingBody( AsyncBytesIO(b''), diff --git a/tests/test_session.py b/tests/test_session.py index 8bae67e2..0c8e232e 100644 --- a/tests/test_session.py +++ b/tests/test_session.py @@ -8,8 +8,6 @@ from aiobotocore.session import AioSession -@pytest.mark.moto -@pytest.mark.asyncio async def test_get_service_data(session): handler_called = False @@ -23,8 +21,6 @@ def handler(**kwargs): assert handler_called -@pytest.mark.moto -@pytest.mark.asyncio async def test_retry( session: AioSession, caplog: LogCaptureFixture, monkeypatch ): @@ -54,7 +50,6 @@ async def test_retry( assert 'sleeping for' in caplog.text -@pytest.mark.moto async def test_set_user_agent_for_session(session: AioSession): assert session.user_agent_name == "aiobotocore" assert session.user_agent_version == __version__ diff --git a/tests/test_sns.py b/tests/test_sns.py index 7dd06cbb..6832aab7 100644 --- a/tests/test_sns.py +++ b/tests/test_sns.py @@ -33,8 +33,6 @@ def _get_topic_policy(topic_arn: str): } -@pytest.mark.moto -@pytest.mark.asyncio async def test_topic_attributes(sns_client, topic_arn): response = await sns_client.list_topics() pytest.aio.assert_status_code(response, 200) @@ -56,8 +54,6 @@ async def test_topic_attributes(sns_client, topic_arn): assert attributes['DisplayName'] == display_name -@pytest.mark.moto -@pytest.mark.asyncio async def test_creating_subscription(sns_client, topic_arn): response = await sns_client.subscribe( TopicArn=topic_arn, Protocol="http", Endpoint="http://httpbin.org/" @@ -71,8 +67,6 @@ async def test_creating_subscription(sns_client, topic_arn): assert len([s for s in subscriptions if s['Protocol'] == 'http']) == 0 -@pytest.mark.moto -@pytest.mark.asyncio async def test_publish_to_http(sns_client, topic_arn): response = await sns_client.subscribe( TopicArn=topic_arn, @@ -90,15 +84,11 @@ async def test_publish_to_http(sns_client, topic_arn): await sns_client.unsubscribe(SubscriptionArn=subscription_arn) -@pytest.mark.moto -@pytest.mark.asyncio async def test_get_missing_endpoint_attributes(sns_client): with pytest.raises(botocore.exceptions.ClientError): await sns_client.get_endpoint_attributes(EndpointArn="arn1") -@pytest.mark.moto -@pytest.mark.asyncio async def test_platform_applications(sns_client): await sns_client.create_platform_application( Name="app1", diff --git a/tests/test_sqs.py b/tests/test_sqs.py index 3e63d6c6..a3b18d0d 100644 --- a/tests/test_sqs.py +++ b/tests/test_sqs.py @@ -3,8 +3,6 @@ import pytest -@pytest.mark.moto -@pytest.mark.asyncio async def test_list_queues(sqs_client, sqs_queue_url): response = await sqs_client.list_queues() pytest.aio.assert_status_code(response, 200) @@ -12,8 +10,6 @@ async def test_list_queues(sqs_client, sqs_queue_url): assert sqs_queue_url in response['QueueUrls'] -@pytest.mark.moto -@pytest.mark.asyncio async def test_get_queue_name(sqs_client, sqs_queue_url): queue_name = sqs_queue_url.rsplit('/', 1)[-1] @@ -23,8 +19,6 @@ async def test_get_queue_name(sqs_client, sqs_queue_url): assert sqs_queue_url == response['QueueUrl'] -@pytest.mark.moto -@pytest.mark.asyncio async def test_put_pull_delete_test(sqs_client, sqs_queue_url): response = await sqs_client.send_message( QueueUrl=sqs_queue_url, @@ -58,8 +52,6 @@ async def test_put_pull_delete_test(sqs_client, sqs_queue_url): assert len(response.get('Messages', [])) == 0 -@pytest.mark.moto -@pytest.mark.asyncio async def test_put_pull_wait(sqs_client, sqs_queue_url): start = time.perf_counter() response = await sqs_client.receive_message( diff --git a/tests/test_stubber.py b/tests/test_stubber.py index 786d8ac6..56ada5d5 100644 --- a/tests/test_stubber.py +++ b/tests/test_stubber.py @@ -1,5 +1,3 @@ -import pytest - from aiobotocore.awsrequest import AioAWSResponse from aiobotocore.session import AioSession from aiobotocore.stub import AioStubber @@ -7,8 +5,6 @@ from .mock_server import AIOServer -@pytest.mark.moto -@pytest.mark.asyncio async def test_add_response(): session = AioSession() @@ -36,8 +32,6 @@ async def test_add_response(): assert stubber._queue[0]['expected_params'] == expected_params -@pytest.mark.moto -@pytest.mark.asyncio async def test_add_client_error(): session = AioSession() diff --git a/tests/test_version.py b/tests/test_version.py index 13bdc168..6acb49bb 100644 --- a/tests/test_version.py +++ b/tests/test_version.py @@ -11,7 +11,6 @@ import docutils.nodes import docutils.parsers.rst import docutils.utils -import pytest import requests from packaging import version from pip._internal.req import InstallRequirement @@ -137,7 +136,6 @@ def _get_boto_module_versions( return module_versions -@pytest.mark.moto def test_release_versions(): # ensures versions in CHANGES.rst + __init__.py match init_version = version.parse(aiobotocore.__version__) diff --git a/tests/test_waiter.py b/tests/test_waiter.py index 387f466a..e29651e6 100644 --- a/tests/test_waiter.py +++ b/tests/test_waiter.py @@ -15,7 +15,6 @@ def cloudformation_waiter_model(cloudformation_client): return WaiterModel(config) -@pytest.mark.moto def test_create_waiter_with_client( cloudformation_client, cloudformation_waiter_model ): @@ -28,8 +27,6 @@ def test_create_waiter_with_client( assert asyncio.iscoroutinefunction(waiter.wait) -@pytest.mark.moto -@pytest.mark.asyncio async def test_sqs(cloudformation_client): cloudformation_template = """{ "AWSTemplateFormatVersion": "2010-09-09",