diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index ecb0c97c..53f78b0a 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -18,7 +18,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ['3.7', '3.8', '3.9', '3.10', '3.11', '3.12', '3.13'] + python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13', '3.14'] steps: - uses: actions/checkout@v4 with: diff --git a/conftest.py b/conftest.py new file mode 100644 index 00000000..62c6a7e9 --- /dev/null +++ b/conftest.py @@ -0,0 +1,5 @@ + + +# Configure pytest-asyncio +pytest_plugins = ('pytest_asyncio',) + diff --git a/pyproject.toml b/pyproject.toml index a2d32fe0..901df4a5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,6 +42,8 @@ crypto = ["pycryptodome"] vcdiff = ["vcdiff-decoder>=0.1.0,<0.2.0"] dev = [ "pytest>=7.1,<8.0", + "pytest-asyncio>=0.21.0,<0.23.0; python_version=='3.7'", + "pytest-asyncio>=0.23.0,<1.0.0; python_version>='3.8'", "mock>=4.0.3,<5.0.0", "pytest-cov>=2.4,<3.0", "ruff>=0.14.0,<1.0.0", @@ -91,6 +93,7 @@ packages = ["ably"] [tool.pytest.ini_options] timeout = 30 +asyncio_mode = "auto" [[tool.uv.index]] name = "experimental" diff --git a/test/ably/conftest.py b/test/ably/conftest.py index 6b3e529b..01483272 100644 --- a/test/ably/conftest.py +++ b/test/ably/conftest.py @@ -1,13 +1,10 @@ -import asyncio - -import pytest +import pytest_asyncio from test.ably.testapp import TestApp -@pytest.fixture(scope='session', autouse=True) -def event_loop(): - loop = asyncio.get_event_loop_policy().new_event_loop() - loop.run_until_complete(TestApp.get_test_vars()) - yield loop - loop.run_until_complete(TestApp.clear_test_vars()) +@pytest_asyncio.fixture(scope='session', autouse=True) +async def test_app_setup(): + await TestApp.get_test_vars() + yield + await TestApp.clear_test_vars() diff --git a/test/ably/realtime/eventemitter_test.py b/test/ably/realtime/eventemitter_test.py index 32205b4f..71db2c74 100644 --- a/test/ably/realtime/eventemitter_test.py +++ b/test/ably/realtime/eventemitter_test.py @@ -1,12 +1,15 @@ import asyncio +import pytest + from ably.realtime.connection import ConnectionState from test.ably.testapp import TestApp from test.ably.utils import BaseAsyncTestCase class TestEventEmitter(BaseAsyncTestCase): - async def asyncSetUp(self): + @pytest.fixture(autouse=True) + async def setup(self): self.test_vars = await TestApp.get_test_vars() async def test_event_listener_error(self): diff --git a/test/ably/realtime/realtimechannel_publish_test.py b/test/ably/realtime/realtimechannel_publish_test.py index 539f55bb..fb940f35 100644 --- a/test/ably/realtime/realtimechannel_publish_test.py +++ b/test/ably/realtime/realtimechannel_publish_test.py @@ -14,7 +14,8 @@ class TestRealtimeChannelPublish(BaseAsyncTestCase): """Tests for RTN7 spec - Message acknowledgment""" - async def asyncSetUp(self): + @pytest.fixture(autouse=True) + async def setup(self): self.test_vars = await TestApp.get_test_vars() # RTN7a - Basic ACK/NACK functionality diff --git a/test/ably/realtime/realtimechannel_test.py b/test/ably/realtime/realtimechannel_test.py index 9b9dd15a..f12fbea1 100644 --- a/test/ably/realtime/realtimechannel_test.py +++ b/test/ably/realtime/realtimechannel_test.py @@ -12,7 +12,8 @@ class TestRealtimeChannel(BaseAsyncTestCase): - async def asyncSetUp(self): + @pytest.fixture(autouse=True) + async def setup(self): self.test_vars = await TestApp.get_test_vars() self.valid_key_format = "api:key" diff --git a/test/ably/realtime/realtimechannel_vcdiff_test.py b/test/ably/realtime/realtimechannel_vcdiff_test.py index 086f355c..af778089 100644 --- a/test/ably/realtime/realtimechannel_vcdiff_test.py +++ b/test/ably/realtime/realtimechannel_vcdiff_test.py @@ -1,6 +1,8 @@ import asyncio import json +import pytest + from ably import AblyVCDiffDecoder from ably.realtime.connection import ConnectionState from ably.realtime.realtime_channel import ChannelOptions @@ -31,7 +33,8 @@ def decode(self, delta: bytes, base: bytes) -> bytes: class TestRealtimeChannelVCDiff(BaseAsyncTestCase): - async def asyncSetUp(self): + @pytest.fixture(autouse=True) + async def setup(self): self.test_vars = await TestApp.get_test_vars() self.valid_key_format = "api:key" diff --git a/test/ably/realtime/realtimeconnection_test.py b/test/ably/realtime/realtimeconnection_test.py index b4e53ed7..deab3263 100644 --- a/test/ably/realtime/realtimeconnection_test.py +++ b/test/ably/realtime/realtimeconnection_test.py @@ -11,7 +11,8 @@ class TestRealtimeConnection(BaseAsyncTestCase): - async def asyncSetUp(self): + @pytest.fixture(autouse=True) + async def setup(self): self.test_vars = await TestApp.get_test_vars() self.valid_key_format = "api:key" diff --git a/test/ably/realtime/realtimeinit_test.py b/test/ably/realtime/realtimeinit_test.py index b10c3748..4009d046 100644 --- a/test/ably/realtime/realtimeinit_test.py +++ b/test/ably/realtime/realtimeinit_test.py @@ -10,7 +10,8 @@ class TestRealtimeInit(BaseAsyncTestCase): - async def asyncSetUp(self): + @pytest.fixture(autouse=True) + async def setup(self): self.test_vars = await TestApp.get_test_vars() self.valid_key_format = "api:key" diff --git a/test/ably/realtime/realtimeresume_test.py b/test/ably/realtime/realtimeresume_test.py index 3ce90963..8aae598f 100644 --- a/test/ably/realtime/realtimeresume_test.py +++ b/test/ably/realtime/realtimeresume_test.py @@ -1,5 +1,7 @@ import asyncio +import pytest + from ably.realtime.connection import ConnectionState from ably.realtime.realtime_channel import ChannelState from ably.transport.websockettransport import ProtocolMessageAction @@ -22,7 +24,8 @@ def on_message(_): class TestRealtimeResume(BaseAsyncTestCase): - async def asyncSetUp(self): + @pytest.fixture(autouse=True) + async def setup(self): self.test_vars = await TestApp.get_test_vars() self.valid_key_format = "api:key" diff --git a/test/ably/rest/encoders_test.py b/test/ably/rest/encoders_test.py index 9c30ded9..f8023c5d 100644 --- a/test/ably/rest/encoders_test.py +++ b/test/ably/rest/encoders_test.py @@ -5,6 +5,7 @@ from unittest import mock import msgpack +import pytest from ably import CipherParams from ably.types.message import Message @@ -21,10 +22,10 @@ class TestTextEncodersNoEncryption(BaseAsyncTestCase): - async def asyncSetUp(self): + @pytest.fixture(autouse=True) + async def setup(self): self.ably = await TestApp.get_ably_rest(use_binary_protocol=False) - - async def asyncTearDown(self): + yield await self.ably.close() async def test_text_utf8(self): @@ -143,12 +144,11 @@ def test_decode_with_invalid_encoding(self): class TestTextEncodersEncryption(BaseAsyncTestCase): - async def asyncSetUp(self): + @pytest.fixture(autouse=True) + async def setup(self): self.ably = await TestApp.get_ably_rest(use_binary_protocol=False) - self.cipher_params = CipherParams(secret_key='keyfordecrypt_16', - algorithm='aes') - - async def asyncTearDown(self): + self.cipher_params = CipherParams(secret_key='keyfordecrypt_16', algorithm='aes') + yield await self.ably.close() def decrypt(self, payload, options=None): @@ -257,10 +257,10 @@ async def test_with_json_list_data_decode(self): class TestBinaryEncodersNoEncryption(BaseAsyncTestCase): - async def asyncSetUp(self): + @pytest.fixture(autouse=True) + async def setup(self): self.ably = await TestApp.get_ably_rest() - - async def asyncTearDown(self): + yield await self.ably.close() def decode(self, data): @@ -348,11 +348,11 @@ async def test_with_json_list_data_decode(self): class TestBinaryEncodersEncryption(BaseAsyncTestCase): - async def asyncSetUp(self): + @pytest.fixture(autouse=True) + async def setup(self): self.ably = await TestApp.get_ably_rest() self.cipher_params = CipherParams(secret_key='keyfordecrypt_16', algorithm='aes') - - async def asyncTearDown(self): + yield await self.ably.close() def decrypt(self, payload, options=None): diff --git a/test/ably/rest/restauth_test.py b/test/ably/rest/restauth_test.py index 854691e3..9c0495ba 100644 --- a/test/ably/rest/restauth_test.py +++ b/test/ably/rest/restauth_test.py @@ -26,7 +26,8 @@ # does not make any request, no need to vary by protocol class TestAuth(BaseAsyncTestCase): - async def asyncSetUp(self): + @pytest.fixture(autouse=True) + async def setup(self): self.test_vars = await TestApp.get_test_vars() def test_auth_init_key_only(self): @@ -167,11 +168,11 @@ def test_with_default_token_params(self): class TestAuthAuthorize(BaseAsyncTestCase, metaclass=VaryByProtocolTestsMetaclass): - async def asyncSetUp(self): + @pytest.fixture(autouse=True) + async def setup(self): self.ably = await TestApp.get_ably_rest() self.test_vars = await TestApp.get_test_vars() - - async def asyncTearDown(self): + yield await self.ably.close() def per_protocol_setup(self, use_binary_protocol): @@ -322,7 +323,8 @@ async def test_client_id_precedence(self): class TestRequestToken(BaseAsyncTestCase, metaclass=VaryByProtocolTestsMetaclass): - async def asyncSetUp(self): + @pytest.fixture(autouse=True) + async def setup(self): self.test_vars = await TestApp.get_test_vars() def per_protocol_setup(self, use_binary_protocol): @@ -480,7 +482,8 @@ async def test_client_id_null_until_auth(self): class TestRenewToken(BaseAsyncTestCase): - async def asyncSetUp(self): + @pytest.fixture(autouse=True) + async def setup(self): self.test_vars = await TestApp.get_test_vars() self.host = 'fake-host.ably.io' self.ably = await TestApp.get_ably_rest(use_binary_protocol=False, rest_host=self.host) @@ -522,8 +525,7 @@ def call_back(request): name="publish_attempt_route") self.publish_attempt_route.side_effect = call_back self.mocked_api.start() - - async def asyncTearDown(self): + yield # We need to have quiet here in order to do not have check if all endpoints were called self.mocked_api.stop(quiet=True) self.mocked_api.reset() @@ -583,7 +585,8 @@ async def test_when_not_renewable_with_token_details(self): class TestRenewExpiredToken(BaseAsyncTestCase): - async def asyncSetUp(self): + @pytest.fixture(autouse=True) + async def setup(self): self.test_vars = await TestApp.get_test_vars() self.publish_attempts = 0 self.channel = uuid.uuid4().hex @@ -629,8 +632,7 @@ def cb_publish(request): self.publish_message_route.side_effect = cb_publish self.mocked_api.start() - - async def asyncTearDown(self): + yield self.mocked_api.stop(quiet=True) self.mocked_api.reset() diff --git a/test/ably/rest/restcapability_test.py b/test/ably/rest/restcapability_test.py index b516799e..c95c651d 100644 --- a/test/ably/rest/restcapability_test.py +++ b/test/ably/rest/restcapability_test.py @@ -8,11 +8,11 @@ class TestRestCapability(BaseAsyncTestCase, metaclass=VaryByProtocolTestsMetaclass): - async def asyncSetUp(self): + @pytest.fixture(autouse=True) + async def setup(self): self.test_vars = await TestApp.get_test_vars() self.ably = await TestApp.get_ably_rest() - - async def asyncTearDown(self): + yield await self.ably.close() def per_protocol_setup(self, use_binary_protocol): diff --git a/test/ably/rest/restchannelhistory_test.py b/test/ably/rest/restchannelhistory_test.py index c8fe2d49..a9a2245b 100644 --- a/test/ably/rest/restchannelhistory_test.py +++ b/test/ably/rest/restchannelhistory_test.py @@ -13,11 +13,11 @@ class TestRestChannelHistory(BaseAsyncTestCase, metaclass=VaryByProtocolTestsMetaclass): - async def asyncSetUp(self): + @pytest.fixture(autouse=True) + async def setup(self): self.ably = await TestApp.get_ably_rest(fallback_hosts=[]) self.test_vars = await TestApp.get_test_vars() - - async def asyncTearDown(self): + yield await self.ably.close() def per_protocol_setup(self, use_binary_protocol): diff --git a/test/ably/rest/restchannelpublish_test.py b/test/ably/rest/restchannelpublish_test.py index 56d1eeb0..71528b42 100644 --- a/test/ably/rest/restchannelpublish_test.py +++ b/test/ably/rest/restchannelpublish_test.py @@ -26,13 +26,13 @@ @pytest.mark.filterwarnings('ignore::DeprecationWarning') class TestRestChannelPublish(BaseAsyncTestCase, metaclass=VaryByProtocolTestsMetaclass): - async def asyncSetUp(self): + @pytest.fixture(autouse=True) + async def setup(self): self.test_vars = await TestApp.get_test_vars() self.ably = await TestApp.get_ably_rest() self.client_id = uuid.uuid4().hex self.ably_with_client_id = await TestApp.get_ably_rest(client_id=self.client_id, use_token_auth=True) - - async def asyncTearDown(self): + yield await self.ably.close() await self.ably_with_client_id.close() @@ -450,11 +450,11 @@ async def test_publish_params(self): class TestRestChannelPublishIdempotent(BaseAsyncTestCase, metaclass=VaryByProtocolTestsMetaclass): - async def asyncSetUp(self): + @pytest.fixture(autouse=True) + async def setup(self): self.ably = await TestApp.get_ably_rest() self.ably_idempotent = await TestApp.get_ably_rest(idempotent_rest_publishing=True) - - async def asyncTearDown(self): + yield await self.ably.close() await self.ably_idempotent.close() diff --git a/test/ably/rest/restchannels_test.py b/test/ably/rest/restchannels_test.py index c6e1d058..b5e59957 100644 --- a/test/ably/rest/restchannels_test.py +++ b/test/ably/rest/restchannels_test.py @@ -12,11 +12,11 @@ # makes no request, no need to use different protocols class TestChannels(BaseAsyncTestCase): - async def asyncSetUp(self): + @pytest.fixture(autouse=True) + async def setup(self): self.test_vars = await TestApp.get_test_vars() self.ably = await TestApp.get_ably_rest() - - async def asyncTearDown(self): + yield await self.ably.close() def test_rest_channels_attr(self): diff --git a/test/ably/rest/restchannelstatus_test.py b/test/ably/rest/restchannelstatus_test.py index 6bc429d4..cb455362 100644 --- a/test/ably/rest/restchannelstatus_test.py +++ b/test/ably/rest/restchannelstatus_test.py @@ -1,5 +1,7 @@ import logging +import pytest + from test.ably.testapp import TestApp from test.ably.utils import BaseAsyncTestCase, VaryByProtocolTestsMetaclass @@ -8,10 +10,10 @@ class TestRestChannelStatus(BaseAsyncTestCase, metaclass=VaryByProtocolTestsMetaclass): - async def asyncSetUp(self): + @pytest.fixture(autouse=True) + async def setup(self): self.ably = await TestApp.get_ably_rest() - - async def asyncTearDown(self): + yield await self.ably.close() def per_protocol_setup(self, use_binary_protocol): diff --git a/test/ably/rest/restcrypto_test.py b/test/ably/rest/restcrypto_test.py index 1ee02995..94812b29 100644 --- a/test/ably/rest/restcrypto_test.py +++ b/test/ably/rest/restcrypto_test.py @@ -18,12 +18,12 @@ class TestRestCrypto(BaseAsyncTestCase, metaclass=VaryByProtocolTestsMetaclass): - async def asyncSetUp(self): + @pytest.fixture(autouse=True) + async def setup(self): self.test_vars = await TestApp.get_test_vars() self.ably = await TestApp.get_ably_rest() self.ably2 = await TestApp.get_ably_rest() - - async def asyncTearDown(self): + yield await self.ably.close() await self.ably2.close() @@ -201,20 +201,20 @@ def test_cipher_params(self): class AbstractTestCryptoWithFixture: - @classmethod - def setUpClass(cls): - resources_path = os.path.join(utils.get_submodule_dir(__file__), 'test-resources', cls.fixture_file) + @pytest.fixture(autouse=True) + def setUpClass(self): + resources_path = os.path.join(utils.get_submodule_dir(__file__), 'test-resources', self.fixture_file) with open(resources_path) as f: - cls.fixture = json.loads(f.read()) - cls.params = { - 'secret_key': base64.b64decode(cls.fixture['key'].encode('ascii')), - 'mode': cls.fixture['mode'], - 'algorithm': cls.fixture['algorithm'], - 'iv': base64.b64decode(cls.fixture['iv'].encode('ascii')), + self.fixture = json.loads(f.read()) + self.params = { + 'secret_key': base64.b64decode(self.fixture['key'].encode('ascii')), + 'mode': self.fixture['mode'], + 'algorithm': self.fixture['algorithm'], + 'iv': base64.b64decode(self.fixture['iv'].encode('ascii')), } - cls.cipher_params = CipherParams(**cls.params) - cls.cipher = get_cipher(cls.cipher_params) - cls.items = cls.fixture['items'] + self.cipher_params = CipherParams(**self.params) + self.cipher = get_cipher(self.cipher_params) + self.items = self.fixture['items'] def get_encoded(self, encoded_item): if encoded_item.get('encoding') == 'base64': diff --git a/test/ably/rest/restinit_test.py b/test/ably/rest/restinit_test.py index 86aae3b6..8e8197d8 100644 --- a/test/ably/rest/restinit_test.py +++ b/test/ably/rest/restinit_test.py @@ -12,7 +12,8 @@ class TestRestInit(BaseAsyncTestCase, metaclass=VaryByProtocolTestsMetaclass): - async def asyncSetUp(self): + @pytest.fixture(autouse=True) + async def setup(self): self.test_vars = await TestApp.get_test_vars() @dont_vary_protocol diff --git a/test/ably/rest/restpaginatedresult_test.py b/test/ably/rest/restpaginatedresult_test.py index 67ca9c59..0ec6bb95 100644 --- a/test/ably/rest/restpaginatedresult_test.py +++ b/test/ably/rest/restpaginatedresult_test.py @@ -1,3 +1,4 @@ +import pytest import respx from httpx import Response @@ -26,7 +27,8 @@ def callback(request): return callback - async def asyncSetUp(self): + @pytest.fixture(autouse=True) + async def setup(self): self.ably = await TestApp.get_ably_rest(use_binary_protocol=False) # Mocked responses # without specific headers @@ -60,8 +62,7 @@ async def asyncSetUp(self): self.ably.http, url='http://rest.ably.io/channels/channel_name/ch2', response_processor=lambda response: response.to_native()) - - async def asyncTearDown(self): + yield self.mocked_api.stop() self.mocked_api.reset() await self.ably.close() diff --git a/test/ably/rest/restpresence_test.py b/test/ably/rest/restpresence_test.py index 626be969..8767b0c6 100644 --- a/test/ably/rest/restpresence_test.py +++ b/test/ably/rest/restpresence_test.py @@ -11,13 +11,13 @@ class TestPresence(BaseAsyncTestCase, metaclass=VaryByProtocolTestsMetaclass): - async def asyncSetUp(self): + @pytest.fixture(autouse=True) + async def setup(self): self.test_vars = await TestApp.get_test_vars() self.ably = await TestApp.get_ably_rest() self.channel = self.ably.channels.get('persisted:presence_fixtures') self.ably.options.use_binary_protocol = True - - async def asyncTearDown(self): + yield self.ably.channels.release('persisted:presence_fixtures') await self.ably.close() @@ -188,12 +188,12 @@ async def test_with_start_gt_end(self): class TestPresenceCrypt(BaseAsyncTestCase, metaclass=VaryByProtocolTestsMetaclass): - async def asyncSetUp(self): + @pytest.fixture(autouse=True) + async def setup(self): self.ably = await TestApp.get_ably_rest() key = b'0123456789abcdef' self.channel = self.ably.channels.get('persisted:presence_fixtures', cipher={'key': key}) - - async def asyncTearDown(self): + yield self.ably.channels.release('persisted:presence_fixtures') await self.ably.close() diff --git a/test/ably/rest/restpush_test.py b/test/ably/rest/restpush_test.py index dba3d6a4..867e8b90 100644 --- a/test/ably/rest/restpush_test.py +++ b/test/ably/rest/restpush_test.py @@ -21,7 +21,8 @@ class TestPush(BaseAsyncTestCase, metaclass=VaryByProtocolTestsMetaclass): - async def asyncSetUp(self): + @pytest.fixture(autouse=True) + async def setup(self): self.ably = await TestApp.get_ably_rest() # Register several devices for later use @@ -35,8 +36,7 @@ async def asyncSetUp(self): device = self.devices[key] await self.save_subscription(channel, device_id=device.id) assert len(list(itertools.chain(*self.channels.values()))) == len(self.devices) - - async def asyncTearDown(self): + yield for key, channel in zip(self.devices, itertools.cycle(self.channels)): device = self.devices[key] await self.remove_subscription(channel, device_id=device.id) diff --git a/test/ably/rest/restrequest_test.py b/test/ably/rest/restrequest_test.py index 51cbae7b..7380ea07 100644 --- a/test/ably/rest/restrequest_test.py +++ b/test/ably/rest/restrequest_test.py @@ -12,7 +12,8 @@ # RSC19 class TestRestRequest(BaseAsyncTestCase, metaclass=VaryByProtocolTestsMetaclass): - async def asyncSetUp(self): + @pytest.fixture(autouse=True) + async def setup(self): self.ably = await TestApp.get_ably_rest() self.test_vars = await TestApp.get_test_vars() @@ -22,8 +23,7 @@ async def asyncSetUp(self): for i in range(20): body = {'name': f'event{i}', 'data': f'lorem ipsum {i}'} await self.ably.request('POST', self.path, body=body, version=Defaults.protocol_version) - - async def asyncTearDown(self): + yield await self.ably.close() def per_protocol_setup(self, use_binary_protocol): diff --git a/test/ably/rest/reststats_test.py b/test/ably/rest/reststats_test.py index e2c63d46..cef28817 100644 --- a/test/ably/rest/reststats_test.py +++ b/test/ably/rest/reststats_test.py @@ -23,7 +23,8 @@ def get_params(self): 'limit': 1 } - async def asyncSetUp(self): + @pytest.fixture(autouse=True) + async def setup(self): self.ably = await TestApp.get_ably_rest() self.ably_text = await TestApp.get_ably_rest(use_binary_protocol=False) @@ -67,12 +68,10 @@ async def asyncSetUp(self): } ) # asynctest does not support setUpClass method - if TestRestAppStatsSetup.__stats_added: - return - await self.ably.http.post('/stats', body=stats + previous_stats) - TestRestAppStatsSetup.__stats_added = True - - async def asyncTearDown(self): + if not TestRestAppStatsSetup.__stats_added: + await self.ably.http.post('/stats', body=stats + previous_stats) + TestRestAppStatsSetup.__stats_added = True + yield await self.ably.close() await self.ably_text.close() diff --git a/test/ably/rest/resttime_test.py b/test/ably/rest/resttime_test.py index ff64a029..a0e962fd 100644 --- a/test/ably/rest/resttime_test.py +++ b/test/ably/rest/resttime_test.py @@ -13,10 +13,10 @@ def per_protocol_setup(self, use_binary_protocol): self.ably.options.use_binary_protocol = use_binary_protocol self.use_binary_protocol = use_binary_protocol - async def asyncSetUp(self): + @pytest.fixture(autouse=True) + async def setup(self): self.ably = await TestApp.get_ably_rest() - - async def asyncTearDown(self): + yield await self.ably.close() async def test_time_accuracy(self): diff --git a/test/ably/rest/resttoken_test.py b/test/ably/rest/resttoken_test.py index 727d81ee..5052f1be 100644 --- a/test/ably/rest/resttoken_test.py +++ b/test/ably/rest/resttoken_test.py @@ -19,12 +19,12 @@ class TestRestToken(BaseAsyncTestCase, metaclass=VaryByProtocolTestsMetaclass): async def server_time(self): return await self.ably.time() - async def asyncSetUp(self): + @pytest.fixture(autouse=True) + async def setup(self): capability = {"*": ["*"]} self.permit_all = str(Capability(capability)) self.ably = await TestApp.get_ably_rest() - - async def asyncTearDown(self): + yield await self.ably.close() def per_protocol_setup(self, use_binary_protocol): @@ -157,12 +157,12 @@ async def test_request_token_float_and_timedelta(self): class TestCreateTokenRequest(BaseAsyncTestCase, metaclass=VaryByProtocolTestsMetaclass): - async def asyncSetUp(self): + @pytest.fixture(autouse=True) + async def setup(self): self.ably = await TestApp.get_ably_rest() self.key_name = self.ably.options.key_name self.key_secret = self.ably.options.key_secret - - async def asyncTearDown(self): + yield await self.ably.close() def per_protocol_setup(self, use_binary_protocol): diff --git a/test/ably/utils.py b/test/ably/utils.py index ae89c632..09658fc0 100644 --- a/test/ably/utils.py +++ b/test/ably/utils.py @@ -3,16 +3,8 @@ import os import random import string -import sys import time -import unittest from typing import Awaitable, Callable - -if sys.version_info >= (3, 8): - from unittest import IsolatedAsyncioTestCase -else: - from async_case import IsolatedAsyncioTestCase - from unittest import mock import msgpack @@ -22,7 +14,7 @@ from ably.http.http import Http -class BaseTestCase(unittest.TestCase): +class BaseTestCase: def respx_add_empty_msg_pack(self, url, method='GET'): respx.route(method=method, url=url).return_value = Response( @@ -41,7 +33,7 @@ def get_channel(cls, prefix=''): return cls.ably.channels.get(name) -class BaseAsyncTestCase(IsolatedAsyncioTestCase): +class BaseAsyncTestCase: def respx_add_empty_msg_pack(self, url, method='GET'): respx.route(method=method, url=url).return_value = Response( diff --git a/test/conftest.py b/test/conftest.py new file mode 100644 index 00000000..e5bc4004 --- /dev/null +++ b/test/conftest.py @@ -0,0 +1,4 @@ +# Configure pytest-asyncio +pytest_plugins = ( + 'pytest_asyncio', +) diff --git a/uv.lock b/uv.lock index 0a0c446a..ceef5fdf 100644 --- a/uv.lock +++ b/uv.lock @@ -10,7 +10,7 @@ resolution-markers = [ [[package]] name = "ably" -version = "2.1.2" +version = "2.1.3" source = { editable = "." } dependencies = [ { name = "h2", version = "4.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, @@ -36,6 +36,8 @@ dev = [ { name = "importlib-metadata" }, { name = "mock" }, { name = "pytest" }, + { name = "pytest-asyncio", version = "0.21.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, + { name = "pytest-asyncio", version = "0.23.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.8'" }, { name = "pytest-cov" }, { name = "pytest-rerunfailures", version = "13.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, { name = "pytest-rerunfailures", version = "14.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.8'" }, @@ -70,6 +72,8 @@ requires-dist = [ { name = "pyee", marker = "python_full_version == '3.7.*'", specifier = ">=9.0.4,<10.0.0" }, { name = "pyee", marker = "python_full_version >= '3.8'", specifier = ">=11.1.0,<14.0.0" }, { name = "pytest", marker = "extra == 'dev'", specifier = ">=7.1,<8.0" }, + { name = "pytest-asyncio", marker = "python_full_version == '3.7.*' and extra == 'dev'", specifier = ">=0.21.0,<0.23.0" }, + { name = "pytest-asyncio", marker = "python_full_version >= '3.8' and extra == 'dev'", specifier = ">=0.23.0,<1.0.0" }, { name = "pytest-cov", marker = "extra == 'dev'", specifier = ">=2.4,<3.0" }, { name = "pytest-rerunfailures", marker = "python_full_version == '3.7.*' and extra == 'dev'", specifier = ">=13.0,<14.0" }, { name = "pytest-rerunfailures", marker = "python_full_version >= '3.8' and extra == 'dev'", specifier = ">=14.0,<15.0" }, @@ -1235,6 +1239,39 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/51/ff/f6e8b8f39e08547faece4bd80f89d5a8de68a38b2d179cc1c4490ffa3286/pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8", size = 325287, upload-time = "2023-12-31T12:00:13.963Z" }, ] +[[package]] +name = "pytest-asyncio" +version = "0.21.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.8'", +] +dependencies = [ + { name = "pytest", marker = "python_full_version < '3.8'" }, + { name = "typing-extensions", version = "4.7.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ae/53/57663d99acaac2fcdafdc697e52a9b1b7d6fcf36616281ff9768a44e7ff3/pytest_asyncio-0.21.2.tar.gz", hash = "sha256:d67738fc232b94b326b9d060750beb16e0074210b98dd8b58a5239fa2a154f45", size = 30656, upload-time = "2024-04-29T13:23:24.738Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9c/ce/1e4b53c213dce25d6e8b163697fbce2d43799d76fa08eea6ad270451c370/pytest_asyncio-0.21.2-py3-none-any.whl", hash = "sha256:ab664c88bb7998f711d8039cacd4884da6430886ae8bbd4eded552ed2004f16b", size = 13368, upload-time = "2024-04-29T13:23:23.126Z" }, +] + +[[package]] +name = "pytest-asyncio" +version = "0.23.8" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.10'", + "python_full_version == '3.9.*'", + "python_full_version == '3.8.*'", +] +dependencies = [ + { name = "pytest", marker = "python_full_version >= '3.8'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/de/b4/0b378b7bf26a8ae161c3890c0b48a91a04106c5713ce81b4b080ea2f4f18/pytest_asyncio-0.23.8.tar.gz", hash = "sha256:759b10b33a6dc61cce40a8bd5205e302978bbbcc00e279a8b61d9a6a3c82e4d3", size = 46920, upload-time = "2024-07-17T17:39:34.617Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ee/82/62e2d63639ecb0fbe8a7ee59ef0bc69a4669ec50f6d3459f74ad4e4189a2/pytest_asyncio-0.23.8-py3-none-any.whl", hash = "sha256:50265d892689a5faefb84df80819d1ecef566eb3549cf915dfb33569359d1ce2", size = 17663, upload-time = "2024-07-17T17:39:32.478Z" }, +] + [[package]] name = "pytest-cov" version = "2.12.1"