From 106df32cc0ca02497b9bc0ef142505b2c2d62a3a Mon Sep 17 00:00:00 2001 From: Owen Pearson Date: Fri, 9 Sep 2022 19:11:31 +0100 Subject: [PATCH 1/7] Add pytest-asyncio dev dependency --- poetry.lock | 27 +++++++++++++++++++++++---- pyproject.toml | 1 + 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/poetry.lock b/poetry.lock index 18243444..daca51cb 100644 --- a/poetry.lock +++ b/poetry.lock @@ -40,7 +40,7 @@ tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (> [[package]] name = "certifi" -version = "2022.6.15" +version = "2022.6.15.1" description = "Python package for providing Mozilla's CA Bundle." category = "main" optional = false @@ -360,6 +360,21 @@ tomli = ">=1.0.0" [package.extras] testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] +[[package]] +name = "pytest-asyncio" +version = "0.19.0" +description = "Pytest support for asyncio" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +pytest = ">=6.1.0" +typing-extensions = {version = ">=3.7.2", markers = "python_version < \"3.8\""} + +[package.extras] +testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)", "flaky (>=3.5.0)", "mypy (>=0.931)", "pytest-trio (>=0.7.0)"] + [[package]] name = "pytest-cov" version = "2.12.1" @@ -501,7 +516,7 @@ oldcrypto = ["pycrypto"] [metadata] lock-version = "1.1" python-versions = "^3.7" -content-hash = "0f5fa1c07bd116047635d4d34692f7f9ca1bb194988445ef61854469c2ce214d" +content-hash = "f89e97bf8246b7eb908e756dc1e88cb427ab9d6cb9b7c407af79191ee40de210" [metadata.files] anyio = [ @@ -517,8 +532,8 @@ attrs = [ {file = "attrs-22.1.0.tar.gz", hash = "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6"}, ] certifi = [ - {file = "certifi-2022.6.15-py3-none-any.whl", hash = "sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412"}, - {file = "certifi-2022.6.15.tar.gz", hash = "sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d"}, + {file = "certifi-2022.6.15.1-py3-none-any.whl", hash = "sha256:43dadad18a7f168740e66944e4fa82c6611848ff9056ad910f8f7a3e46ab89e0"}, + {file = "certifi-2022.6.15.1.tar.gz", hash = "sha256:cffdcd380919da6137f76633531a5817e3a9f268575c128249fb637e4f9e73fb"}, ] charset-normalizer = [ {file = "charset-normalizer-2.1.1.tar.gz", hash = "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845"}, @@ -761,6 +776,10 @@ pytest = [ {file = "pytest-7.1.3-py3-none-any.whl", hash = "sha256:1377bda3466d70b55e3f5cecfa55bb7cfcf219c7964629b967c37cf0bda818b7"}, {file = "pytest-7.1.3.tar.gz", hash = "sha256:4f365fec2dff9c1162f834d9f18af1ba13062db0c708bf7b946f8a5c76180c39"}, ] +pytest-asyncio = [ + {file = "pytest-asyncio-0.19.0.tar.gz", hash = "sha256:ac4ebf3b6207259750bc32f4c1d8fcd7e79739edbc67ad0c58dd150b1d072fed"}, + {file = "pytest_asyncio-0.19.0-py3-none-any.whl", hash = "sha256:7a97e37cfe1ed296e2e84941384bdd37c376453912d397ed39293e0916f521fa"}, +] pytest-cov = [ {file = "pytest-cov-2.12.1.tar.gz", hash = "sha256:261ceeb8c227b726249b376b8526b600f38667ee314f910353fa318caa01f4d7"}, {file = "pytest_cov-2.12.1-py2.py3-none-any.whl", hash = "sha256:261bb9e47e65bd099c89c3edf92972865210c36813f80ede5277dceb77a4a62a"}, diff --git a/pyproject.toml b/pyproject.toml index 355ed464..2f4f835b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,6 +48,7 @@ pytest-xdist = "^1.15" respx = "^0.17.1" asynctest = "^0.13" importlib-metadata = "^4.12" +pytest-asyncio = "^0.19" [build-system] requires = ["poetry-core>=1.0.0"] From 37c406e4b2986368c54c319622e95d5c3f9230a9 Mon Sep 17 00:00:00 2001 From: Owen Pearson Date: Fri, 9 Sep 2022 19:12:16 +0100 Subject: [PATCH 2/7] Add pytest-asyncio fixture to get rest client --- test/ably/conftest.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/ably/conftest.py b/test/ably/conftest.py index 3c1065ea..a13bcb18 100644 --- a/test/ably/conftest.py +++ b/test/ably/conftest.py @@ -1,6 +1,7 @@ import asyncio import pytest +import pytest_asyncio from test.ably.restsetup import RestSetup @@ -10,3 +11,12 @@ def event_loop(): loop.run_until_complete(RestSetup.get_test_vars()) yield loop loop.run_until_complete(RestSetup.clear_test_vars()) + + +@pytest_asyncio.fixture(params=["json", "msgpack"]) +async def rest(request): + protocol = request.param + use_binary_protocol = protocol == "msgpack" + ably = await RestSetup.get_ably_rest(use_binary_protocol=use_binary_protocol) + yield ably + await ably.close() From 29060932e3ff73d5a334c5cbb16a8416aad6920a Mon Sep 17 00:00:00 2001 From: Owen Pearson Date: Fri, 9 Sep 2022 19:12:56 +0100 Subject: [PATCH 3/7] Convert rest channel status tests to pytest-asyncio --- test/ably/restchannelstatus_test.py | 70 ++++++++++++----------------- 1 file changed, 29 insertions(+), 41 deletions(-) diff --git a/test/ably/restchannelstatus_test.py b/test/ably/restchannelstatus_test.py index ef120947..c8f106d6 100644 --- a/test/ably/restchannelstatus_test.py +++ b/test/ably/restchannelstatus_test.py @@ -1,47 +1,35 @@ import logging -from test.ably.restsetup import RestSetup -from test.ably.utils import VaryByProtocolTestsMetaclass, BaseAsyncTestCase +import pytest log = logging.getLogger(__name__) -class TestRestChannelStatus(BaseAsyncTestCase, metaclass=VaryByProtocolTestsMetaclass): - - async def setUp(self): - self.ably = await RestSetup.get_ably_rest() - - async def tearDown(self): - await self.ably.close() - - 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 test_channel_status(self): - channel_name = self.get_channel_name('test_channel_status') - channel = self.ably.channels[channel_name] - - channel_status = await channel.status() - - assert channel_status is not None, "Expected non-None channel_status" - assert channel_name == channel_status.channel_id, "Expected channel name to match" - assert channel_status.status.is_active is True, "Expected is_active to be True" - assert isinstance(channel_status.status.occupancy.metrics.publishers, int) and\ - channel_status.status.occupancy.metrics.publishers >= 0,\ - "Expected publishers to be a non-negative int" - assert isinstance(channel_status.status.occupancy.metrics.connections, int) and\ - channel_status.status.occupancy.metrics.connections >= 0,\ - "Expected connections to be a non-negative int" - assert isinstance(channel_status.status.occupancy.metrics.subscribers, int) and\ - channel_status.status.occupancy.metrics.subscribers >= 0,\ - "Expected subscribers to be a non-negative int" - assert isinstance(channel_status.status.occupancy.metrics.presence_members, int) and\ - channel_status.status.occupancy.metrics.presence_members >= 0,\ - "Expected presence_members to be a non-negative int" - assert isinstance(channel_status.status.occupancy.metrics.presence_connections, int) and\ - channel_status.status.occupancy.metrics.presence_connections >= 0,\ - "Expected presence_connections to be a non-negative int" - assert isinstance(channel_status.status.occupancy.metrics.presence_subscribers, int) and\ - channel_status.status.occupancy.metrics.presence_subscribers >= 0,\ - "Expected presence_subscribers to be a non-negative int" +@pytest.mark.asyncio() +async def test_channel_status(rest): + channel_name = 'test_channel_status' + channel = rest.channels[channel_name] + + channel_status = await channel.status() + + assert channel_status is not None, "Expected non-None channel_status" + assert channel_name == channel_status.channel_id, "Expected channel name to match" + assert channel_status.status.is_active is True, "Expected is_active to be True" + assert isinstance(channel_status.status.occupancy.metrics.publishers, int) and\ + channel_status.status.occupancy.metrics.publishers >= 0,\ + "Expected publishers to be a non-negative int" + assert isinstance(channel_status.status.occupancy.metrics.connections, int) and\ + channel_status.status.occupancy.metrics.connections >= 0,\ + "Expected connections to be a non-negative int" + assert isinstance(channel_status.status.occupancy.metrics.subscribers, int) and\ + channel_status.status.occupancy.metrics.subscribers >= 0,\ + "Expected subscribers to be a non-negative int" + assert isinstance(channel_status.status.occupancy.metrics.presence_members, int) and\ + channel_status.status.occupancy.metrics.presence_members >= 0,\ + "Expected presence_members to be a non-negative int" + assert isinstance(channel_status.status.occupancy.metrics.presence_connections, int) and\ + channel_status.status.occupancy.metrics.presence_connections >= 0,\ + "Expected presence_connections to be a non-negative int" + assert isinstance(channel_status.status.occupancy.metrics.presence_subscribers, int) and\ + channel_status.status.occupancy.metrics.presence_subscribers >= 0,\ + "Expected presence_subscribers to be a non-negative int" From fbb5d9ed9569b179ec97e5b82fec17a1495bb50a Mon Sep 17 00:00:00 2001 From: Owen Pearson Date: Fri, 9 Sep 2022 19:20:07 +0100 Subject: [PATCH 4/7] Convert rest time tests to pytest-asyncio --- test/ably/resttime_test.py | 45 +++++++++++++++----------------------- 1 file changed, 18 insertions(+), 27 deletions(-) diff --git a/test/ably/resttime_test.py b/test/ably/resttime_test.py index f76716f5..fd7762ca 100644 --- a/test/ably/resttime_test.py +++ b/test/ably/resttime_test.py @@ -5,39 +5,30 @@ from ably import AblyException from test.ably.restsetup import RestSetup -from test.ably.utils import VaryByProtocolTestsMetaclass, dont_vary_protocol, BaseAsyncTestCase -class TestRestTime(BaseAsyncTestCase, metaclass=VaryByProtocolTestsMetaclass): +@pytest.mark.asyncio +async def test_time_accuracy(rest): + reported_time = await rest.time() + actual_time = time.time() * 1000.0 - def per_protocol_setup(self, use_binary_protocol): - self.ably.options.use_binary_protocol = use_binary_protocol - self.use_binary_protocol = use_binary_protocol + seconds = 10 + assert abs(actual_time - reported_time) < seconds * 1000, "Time is not within %s seconds" % seconds - async def setUp(self): - self.ably = await RestSetup.get_ably_rest() - async def tearDown(self): - await self.ably.close() +@pytest.mark.asyncio +async def test_time_without_key_or_token(rest): + reported_time = await rest.time() + actual_time = time.time() * 1000.0 - async def test_time_accuracy(self): - reported_time = await self.ably.time() - actual_time = time.time() * 1000.0 + seconds = 10 + assert abs(actual_time - reported_time) < seconds * 1000, "Time is not within %s seconds" % seconds - seconds = 10 - assert abs(actual_time - reported_time) < seconds * 1000, "Time is not within %s seconds" % seconds - async def test_time_without_key_or_token(self): - reported_time = await self.ably.time() - actual_time = time.time() * 1000.0 +@pytest.mark.asyncio +async def test_time_fails_without_valid_host(): + ably = await RestSetup.get_ably_rest(key=None, token='foo', rest_host="this.host.does.not.exist") + with pytest.raises(AblyException): + await ably.time() - seconds = 10 - assert abs(actual_time - reported_time) < seconds * 1000, "Time is not within %s seconds" % seconds - - @dont_vary_protocol - async def test_time_fails_without_valid_host(self): - ably = await RestSetup.get_ably_rest(key=None, token='foo', rest_host="this.host.does.not.exist") - with pytest.raises(AblyException): - await ably.time() - - await ably.close() + await ably.close() From a8c00b26de7f437656478f5734e7319dfdc54805 Mon Sep 17 00:00:00 2001 From: Owen Pearson Date: Fri, 9 Sep 2022 19:58:16 +0100 Subject: [PATCH 5/7] Add test_vars fixture --- test/ably/conftest.py | 7 +++++++ test/ably/restsetup.py | 19 ++++++++++--------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/test/ably/conftest.py b/test/ably/conftest.py index a13bcb18..11b56e64 100644 --- a/test/ably/conftest.py +++ b/test/ably/conftest.py @@ -20,3 +20,10 @@ async def rest(request): ably = await RestSetup.get_ably_rest(use_binary_protocol=use_binary_protocol) yield ably await ably.close() + + +@pytest_asyncio.fixture(scope='session') +async def test_vars(): + result = await RestSetup.get_test_vars() + yield result + await RestSetup.clear_test_vars() diff --git a/test/ably/restsetup.py b/test/ably/restsetup.py index 3c681005..28e1227d 100644 --- a/test/ably/restsetup.py +++ b/test/ably/restsetup.py @@ -82,12 +82,13 @@ async def get_ably_rest(cls, **kw): @classmethod async def clear_test_vars(cls): test_vars = RestSetup.__test_vars - options = Options(key=test_vars["keys"][0]["key_str"]) - options.rest_host = test_vars["host"] - options.port = test_vars["port"] - options.tls_port = test_vars["tls_port"] - options.tls = test_vars["tls"] - ably = await cls.get_ably_rest() - await ably.http.delete('/apps/' + test_vars['app_id']) - RestSetup.__test_vars = None - await ably.close() + if test_vars: + options = Options(key=test_vars["keys"][0]["key_str"]) + options.rest_host = test_vars["host"] + options.port = test_vars["port"] + options.tls_port = test_vars["tls_port"] + options.tls = test_vars["tls"] + ably = await cls.get_ably_rest() + await ably.http.delete('/apps/' + test_vars['app_id']) + RestSetup.__test_vars = None + await ably.close() From ae84da7001d1a9c814da17b1acea11847b6b0581 Mon Sep 17 00:00:00 2001 From: Owen Pearson Date: Fri, 9 Sep 2022 19:58:35 +0100 Subject: [PATCH 6/7] Convert rest request tests to pytest-asyncio --- test/ably/restrequest_test.py | 242 +++++++++++++++++----------------- test/ably/utils.py | 4 + 2 files changed, 128 insertions(+), 118 deletions(-) diff --git a/test/ably/restrequest_test.py b/test/ably/restrequest_test.py index 124b7be0..f1a43f94 100644 --- a/test/ably/restrequest_test.py +++ b/test/ably/restrequest_test.py @@ -1,123 +1,129 @@ +"""RSC19""" +from test.ably.utils import get_channel_name + import httpx import pytest +import pytest_asyncio from ably import AblyRest from ably.http.paginatedresult import HttpPaginatedResponse -from test.ably.restsetup import RestSetup -from test.ably.utils import BaseAsyncTestCase -from test.ably.utils import VaryByProtocolTestsMetaclass, dont_vary_protocol - - -# RSC19 -class TestRestRequest(BaseAsyncTestCase, metaclass=VaryByProtocolTestsMetaclass): - - async def setUp(self): - self.ably = await RestSetup.get_ably_rest() - self.test_vars = await RestSetup.get_test_vars() - - # Populate the channel (using the new api) - self.channel = self.get_channel_name() - self.path = '/channels/%s/messages' % self.channel - for i in range(20): - body = {'name': 'event%s' % i, 'data': 'lorem ipsum %s' % i} - await self.ably.request('POST', self.path, body=body) - - async def tearDown(self): - await self.ably.close() - - 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 test_post(self): - body = {'name': 'test-post', 'data': 'lorem ipsum'} - result = await self.ably.request('POST', self.path, body=body) - - assert isinstance(result, HttpPaginatedResponse) # RSC19d - # HP3 - assert type(result.items) is list - assert len(result.items) == 1 - assert result.items[0]['channel'] == self.channel - assert 'messageId' in result.items[0] - - async def test_get(self): - params = {'limit': 10, 'direction': 'forwards'} - result = await self.ably.request('GET', self.path, params=params) - - assert isinstance(result, HttpPaginatedResponse) # RSC19d - - # HP2 - assert isinstance(await result.next(), HttpPaginatedResponse) - assert isinstance(await result.first(), HttpPaginatedResponse) - - # HP3 - assert isinstance(result.items, list) - item = result.items[0] - assert isinstance(item, dict) - assert 'timestamp' in item - assert 'id' in item - assert item['name'] == 'event0' - assert item['data'] == 'lorem ipsum 0' - - assert result.status_code == 200 # HP4 - assert result.success is True # HP5 - assert result.error_code is None # HP6 - assert result.error_message is None # HP7 - assert isinstance(result.headers, list) # HP7 - - @dont_vary_protocol - async def test_not_found(self): - result = await self.ably.request('GET', '/not-found') - assert isinstance(result, HttpPaginatedResponse) # RSC19d - assert result.status_code == 404 # HP4 - assert result.success is False # HP5 - - @dont_vary_protocol - async def test_error(self): - params = {'limit': 'abc'} - result = await self.ably.request('GET', self.path, params=params) - assert isinstance(result, HttpPaginatedResponse) # RSC19d - assert result.status_code == 400 # HP4 - assert not result.success - assert result.error_code - assert result.error_message - - async def test_headers(self): - key = 'X-Test' - value = 'lorem ipsum' - result = await self.ably.request('GET', '/time', headers={key: value}) - assert result.response.request.headers[key] == value - - # RSC19e - @dont_vary_protocol - async def test_timeout(self): - # Timeout - timeout = 0.000001 - ably = AblyRest(token="foo", http_request_timeout=timeout) - assert ably.http.http_request_timeout == timeout - with pytest.raises(httpx.ReadTimeout): - await ably.request('GET', '/time') - await ably.close() - - # Bad host, use fallback - ably = AblyRest(key=self.test_vars["keys"][0]["key_str"], - rest_host='some.other.host', - port=self.test_vars["port"], - tls_port=self.test_vars["tls_port"], - tls=self.test_vars["tls"], - fallback_hosts_use_default=True) - result = await ably.request('GET', '/time') - assert isinstance(result, HttpPaginatedResponse) - assert len(result.items) == 1 - assert isinstance(result.items[0], int) - await ably.close() - - # Bad host, no Fallback - ably = AblyRest(key=self.test_vars["keys"][0]["key_str"], - rest_host='some.other.host', - port=self.test_vars["port"], - tls_port=self.test_vars["tls_port"], - tls=self.test_vars["tls"]) - with pytest.raises(httpx.ConnectError): - await ably.request('GET', '/time') - await ably.close() + + +@pytest.fixture(name="channel") +def channel_fixture(): + yield get_channel_name() + + +@pytest.fixture(name="path") +def path_fixture(channel): + yield f'/channels/{channel}/messages' + + +@pytest_asyncio.fixture() +async def setup(rest, path): + for i in range(20): + body = {'name': 'event%s' % i, 'data': 'lorem ipsum %s' % i} + await rest.request('POST', path, body=body) + + +@pytest.mark.asyncio +async def test_post(path, rest, channel): + body = {'name': 'test-post', 'data': 'lorem ipsum'} + result = await rest.request('POST', path, body=body) + + assert isinstance(result, HttpPaginatedResponse) # RSC19d + # HP3 + assert type(result.items) is list + assert len(result.items) == 1 + assert result.items[0]['channel'] == channel + assert 'messageId' in result.items[0] + + +@pytest.mark.asyncio +@pytest.mark.usefixtures("setup") +async def test_get(rest, path): + params = {'limit': 10, 'direction': 'forwards'} + result = await rest.request('GET', path, params=params) + + assert isinstance(result, HttpPaginatedResponse) # RSC19d + + # HP2 + assert isinstance(await result.next(), HttpPaginatedResponse) + assert isinstance(await result.first(), HttpPaginatedResponse) + + # HP3 + assert isinstance(result.items, list) + item = result.items[0] + assert isinstance(item, dict) + assert 'timestamp' in item + assert 'id' in item + assert item['name'] == 'event0' + assert item['data'] == 'lorem ipsum 0' + + assert result.status_code == 200 # HP4 + assert result.success is True # HP5 + assert result.error_code is None # HP6 + assert result.error_message is None # HP7 + assert isinstance(result.headers, list) # HP7 + + +@pytest.mark.asyncio +async def test_not_found(rest): + result = await rest.request('GET', '/not-found') + assert isinstance(result, HttpPaginatedResponse) # RSC19d + assert result.status_code == 404 # HP4 + assert result.success is False # HP5 + + +@pytest.mark.asyncio +async def test_error(rest, path): + params = {'limit': 'abc'} + result = await rest.request('GET', path, params=params) + assert isinstance(result, HttpPaginatedResponse) # RSC19d + assert result.status_code == 400 # HP4 + assert not result.success + assert result.error_code + assert result.error_message + + +@pytest.mark.asyncio +async def test_headers(rest): + key = 'X-Test' + value = 'lorem ipsum' + result = await rest.request('GET', '/time', headers={key: value}) + assert result.response.request.headers[key] == value + + +# RSC19e +@pytest.mark.asyncio +async def test_timeout(test_vars): + # Timeout + timeout = 0.000001 + ably = AblyRest(token="foo", http_request_timeout=timeout) + assert ably.http.http_request_timeout == timeout + with pytest.raises(httpx.ReadTimeout): + await ably.request('GET', '/time') + await ably.close() + + # Bad host, use fallback + ably = AblyRest(key=test_vars["keys"][0]["key_str"], + rest_host='some.other.host', + port=test_vars["port"], + tls_port=test_vars["tls_port"], + tls=test_vars["tls"], + fallback_hosts_use_default=True) + result = await ably.request('GET', '/time') + assert isinstance(result, HttpPaginatedResponse) + assert len(result.items) == 1 + assert isinstance(result.items[0], int) + await ably.close() + + # Bad host, no Fallback + ably = AblyRest(key=test_vars["keys"][0]["key_str"], + rest_host='some.other.host', + port=test_vars["port"], + tls_port=test_vars["tls_port"], + tls=test_vars["tls"]) + with pytest.raises(httpx.ConnectError): + await ably.request('GET', '/time') + await ably.close() diff --git a/test/ably/utils.py b/test/ably/utils.py index 1914750e..a9584cdd 100644 --- a/test/ably/utils.py +++ b/test/ably/utils.py @@ -12,6 +12,10 @@ from ably.http.http import Http +def get_channel_name(prefix=''): + return prefix + random_string(10) + + class BaseTestCase(unittest.TestCase): def respx_add_empty_msg_pack(self, url, method='GET'): From a8fbda4a933e3afe2e975238aa1b8d455e50b163 Mon Sep 17 00:00:00 2001 From: Owen Pearson Date: Fri, 9 Sep 2022 23:07:41 +0100 Subject: [PATCH 7/7] Migrate encoders tests to pytest-asyncio --- test/ably/conftest.py | 23 ++++ test/ably/encoders_test.py | 258 +++++++++++++++++++------------------ 2 files changed, 159 insertions(+), 122 deletions(-) diff --git a/test/ably/conftest.py b/test/ably/conftest.py index 11b56e64..1714c933 100644 --- a/test/ably/conftest.py +++ b/test/ably/conftest.py @@ -22,8 +22,31 @@ async def rest(request): await ably.close() +@pytest_asyncio.fixture(name="new_rest") +async def new_rest_fixture(): + async def rest_factory(**kwargs): + ably = await RestSetup.get_ably_rest(**kwargs) + return ably + + yield rest_factory + + @pytest_asyncio.fixture(scope='session') async def test_vars(): result = await RestSetup.get_test_vars() yield result await RestSetup.clear_test_vars() + + +@pytest_asyncio.fixture(name="json_rest") +async def json_rest_fixture(new_rest): + ably = await new_rest(use_binary_protocol=False) + yield ably + await ably.close() + + +@pytest_asyncio.fixture(name="msgpack_rest") +async def msgpack_rest_fixture(new_rest): + ably = await new_rest(use_binary_protocol=True) + yield ably + await ably.close() diff --git a/test/ably/encoders_test.py b/test/ably/encoders_test.py index d1edc461..9a6c2529 100644 --- a/test/ably/encoders_test.py +++ b/test/ably/encoders_test.py @@ -1,6 +1,7 @@ import base64 import json import logging +import pytest import mock import msgpack @@ -9,21 +10,22 @@ from ably.util.crypto import get_cipher from ably.types.message import Message -from test.ably.restsetup import RestSetup -from test.ably.utils import BaseAsyncTestCase, AsyncMock +from test.ably.utils import AsyncMock + log = logging.getLogger(__name__) -class TestTextEncodersNoEncryption(BaseAsyncTestCase): - async def setUp(self): - self.ably = await RestSetup.get_ably_rest(use_binary_protocol=False) +@pytest.fixture(name="cipher_params") +def cipher_params_fixture(): + yield CipherParams(secret_key='keyfordecrypt_16', + algorithm='aes') - async def tearDown(self): - await self.ably.close() - async def test_text_utf8(self): - channel = self.ably.channels["persisted:publish"] +class TestTextEncodersNoEncryption: + @pytest.mark.asyncio + async def test_text_utf8(self, json_rest): + channel = json_rest.channels["persisted:publish"] with mock.patch('ably.rest.rest.Http.post', new_callable=AsyncMock) as post_mock: await channel.publish('event', 'foó') @@ -31,9 +33,10 @@ async def test_text_utf8(self): assert json.loads(kwargs['body'])['data'] == 'foó' assert not json.loads(kwargs['body']).get('encoding', '') - async def test_str(self): + @pytest.mark.asyncio + async def test_str(self, json_rest): # This test only makes sense for py2 - channel = self.ably.channels["persisted:publish"] + channel = json_rest.channels["persisted:publish"] with mock.patch('ably.rest.rest.Http.post', new_callable=AsyncMock) as post_mock: await channel.publish('event', 'foo') @@ -41,8 +44,9 @@ async def test_str(self): assert json.loads(kwargs['body'])['data'] == 'foo' assert not json.loads(kwargs['body']).get('encoding', '') - async def test_with_binary_type(self): - channel = self.ably.channels["persisted:publish"] + @pytest.mark.asyncio + async def test_with_binary_type(self, json_rest): + channel = json_rest.channels["persisted:publish"] with mock.patch('ably.rest.rest.Http.post', new_callable=AsyncMock) as post_mock: await channel.publish('event', bytearray(b'foo')) @@ -51,8 +55,9 @@ async def test_with_binary_type(self): assert base64.b64decode(raw_data.encode('ascii')) == bytearray(b'foo') assert json.loads(kwargs['body'])['encoding'].strip('/') == 'base64' - async def test_with_bytes_type(self): - channel = self.ably.channels["persisted:publish"] + @pytest.mark.asyncio + async def test_with_bytes_type(self, json_rest): + channel = json_rest.channels["persisted:publish"] with mock.patch('ably.rest.rest.Http.post', new_callable=AsyncMock) as post_mock: await channel.publish('event', b'foo') @@ -61,8 +66,9 @@ async def test_with_bytes_type(self): assert base64.b64decode(raw_data.encode('ascii')) == bytearray(b'foo') assert json.loads(kwargs['body'])['encoding'].strip('/') == 'base64' - async def test_with_json_dict_data(self): - channel = self.ably.channels["persisted:publish"] + @pytest.mark.asyncio + async def test_with_json_dict_data(self, json_rest): + channel = json_rest.channels["persisted:publish"] data = {'foó': 'bár'} with mock.patch('ably.rest.rest.Http.post', new_callable=AsyncMock) as post_mock: await channel.publish('event', data) @@ -71,8 +77,9 @@ async def test_with_json_dict_data(self): assert raw_data == data assert json.loads(kwargs['body'])['encoding'].strip('/') == 'json' - async def test_with_json_list_data(self): - channel = self.ably.channels["persisted:publish"] + @pytest.mark.asyncio + async def test_with_json_list_data(self, json_rest): + channel = json_rest.channels["persisted:publish"] data = ['foó', 'bár'] with mock.patch('ably.rest.rest.Http.post', new_callable=AsyncMock) as post_mock: await channel.publish('event', data) @@ -81,8 +88,9 @@ async def test_with_json_list_data(self): assert raw_data == data assert json.loads(kwargs['body'])['encoding'].strip('/') == 'json' - async def test_text_utf8_decode(self): - channel = self.ably.channels["persisted:stringdecode"] + @pytest.mark.asyncio + async def test_text_utf8_decode(self, json_rest): + channel = json_rest.channels["persisted:stringdecode"] await channel.publish('event', 'fóo') history = await channel.history() @@ -91,8 +99,9 @@ async def test_text_utf8_decode(self): assert isinstance(message.data, str) assert not message.encoding - async def test_text_str_decode(self): - channel = self.ably.channels["persisted:stringnonutf8decode"] + @pytest.mark.asyncio + async def test_text_str_decode(self, json_rest): + channel = json_rest.channels["persisted:stringnonutf8decode"] await channel.publish('event', 'foo') history = await channel.history() @@ -101,8 +110,9 @@ async def test_text_str_decode(self): assert isinstance(message.data, str) assert not message.encoding - async def test_with_binary_type_decode(self): - channel = self.ably.channels["persisted:binarydecode"] + @pytest.mark.asyncio + async def test_with_binary_type_decode(self, json_rest): + channel = json_rest.channels["persisted:binarydecode"] await channel.publish('event', bytearray(b'foob')) history = await channel.history() @@ -111,8 +121,9 @@ async def test_with_binary_type_decode(self): assert isinstance(message.data, bytearray) assert not message.encoding - async def test_with_json_dict_data_decode(self): - channel = self.ably.channels["persisted:jsondict"] + @pytest.mark.asyncio + async def test_with_json_dict_data_decode(self, json_rest): + channel = json_rest.channels["persisted:jsondict"] data = {'foó': 'bár'} await channel.publish('event', data) history = await channel.history() @@ -120,8 +131,9 @@ async def test_with_json_dict_data_decode(self): assert message.data == data assert not message.encoding - async def test_with_json_list_data_decode(self): - channel = self.ably.channels["persisted:jsonarray"] + @pytest.mark.asyncio + async def test_with_json_list_data_decode(self, json_rest): + channel = json_rest.channels["persisted:jsonarray"] data = ['foó', 'bár'] await channel.publish('event', data) history = await channel.history() @@ -137,15 +149,7 @@ def test_decode_with_invalid_encoding(self): assert decoded_data['encoding'] == 'foo/bar' -class TestTextEncodersEncryption(BaseAsyncTestCase): - async def setUp(self): - self.ably = await RestSetup.get_ably_rest(use_binary_protocol=False) - self.cipher_params = CipherParams(secret_key='keyfordecrypt_16', - algorithm='aes') - - async def tearDown(self): - await self.ably.close() - +class TestTextEncodersEncryption: def decrypt(self, payload, options=None): if options is None: options = {} @@ -153,9 +157,10 @@ def decrypt(self, payload, options=None): cipher = get_cipher({'key': b'keyfordecrypt_16'}) return cipher.decrypt(ciphertext) - async def test_text_utf8(self): - channel = self.ably.channels.get("persisted:publish_enc", - cipher=self.cipher_params) + @pytest.mark.asyncio + async def test_text_utf8(self, json_rest, cipher_params): + channel = json_rest.channels.get("persisted:publish_enc", + cipher=cipher_params) with mock.patch('ably.rest.rest.Http.post', new_callable=AsyncMock) as post_mock: await channel.publish('event', 'fóo') _, kwargs = post_mock.call_args @@ -163,9 +168,10 @@ async def test_text_utf8(self): data = self.decrypt(json.loads(kwargs['body'])['data']).decode('utf-8') assert data == 'fóo' - async def test_str(self): + @pytest.mark.asyncio + async def test_str(self, json_rest): # This test only makes sense for py2 - channel = self.ably.channels["persisted:publish"] + channel = json_rest.channels["persisted:publish"] with mock.patch('ably.rest.rest.Http.post', new_callable=AsyncMock) as post_mock: await channel.publish('event', 'foo') @@ -173,9 +179,10 @@ async def test_str(self): assert json.loads(kwargs['body'])['data'] == 'foo' assert not json.loads(kwargs['body']).get('encoding', '') - async def test_with_binary_type(self): - channel = self.ably.channels.get("persisted:publish_enc", - cipher=self.cipher_params) + @pytest.mark.asyncio + async def test_with_binary_type(self, json_rest, cipher_params): + channel = json_rest.channels.get("persisted:publish_enc", + cipher=cipher_params) with mock.patch('ably.rest.rest.Http.post', new_callable=AsyncMock) as post_mock: await channel.publish('event', bytearray(b'foo')) @@ -186,9 +193,10 @@ async def test_with_binary_type(self): assert data == bytearray(b'foo') assert isinstance(data, bytearray) - async def test_with_json_dict_data(self): - channel = self.ably.channels.get("persisted:publish_enc", - cipher=self.cipher_params) + @pytest.mark.asyncio + async def test_with_json_dict_data(self, json_rest, cipher_params): + channel = json_rest.channels.get("persisted:publish_enc", + cipher=cipher_params) data = {'foó': 'bár'} with mock.patch('ably.rest.rest.Http.post', new_callable=AsyncMock) as post_mock: await channel.publish('event', data) @@ -197,9 +205,10 @@ async def test_with_json_dict_data(self): raw_data = self.decrypt(json.loads(kwargs['body'])['data']).decode('ascii') assert json.loads(raw_data) == data - async def test_with_json_list_data(self): - channel = self.ably.channels.get("persisted:publish_enc", - cipher=self.cipher_params) + @pytest.mark.asyncio + async def test_with_json_list_data(self, json_rest, cipher_params): + channel = json_rest.channels.get("persisted:publish_enc", + cipher=cipher_params) data = ['foó', 'bár'] with mock.patch('ably.rest.rest.Http.post', new_callable=AsyncMock) as post_mock: await channel.publish('event', data) @@ -208,9 +217,10 @@ async def test_with_json_list_data(self): raw_data = self.decrypt(json.loads(kwargs['body'])['data']).decode('ascii') assert json.loads(raw_data) == data - async def test_text_utf8_decode(self): - channel = self.ably.channels.get("persisted:enc_stringdecode", - cipher=self.cipher_params) + @pytest.mark.asyncio + async def test_text_utf8_decode(self, json_rest, cipher_params): + channel = json_rest.channels.get("persisted:enc_stringdecode", + cipher=cipher_params) await channel.publish('event', 'foó') history = await channel.history() message = history.items[0] @@ -218,9 +228,10 @@ async def test_text_utf8_decode(self): assert isinstance(message.data, str) assert not message.encoding - async def test_with_binary_type_decode(self): - channel = self.ably.channels.get("persisted:enc_binarydecode", - cipher=self.cipher_params) + @pytest.mark.asyncio + async def test_with_binary_type_decode(self, json_rest, cipher_params): + channel = json_rest.channels.get("persisted:enc_binarydecode", + cipher=cipher_params) await channel.publish('event', bytearray(b'foob')) history = await channel.history() @@ -229,9 +240,10 @@ async def test_with_binary_type_decode(self): assert isinstance(message.data, bytearray) assert not message.encoding - async def test_with_json_dict_data_decode(self): - channel = self.ably.channels.get("persisted:enc_jsondict", - cipher=self.cipher_params) + @pytest.mark.asyncio + async def test_with_json_dict_data_decode(self, json_rest, cipher_params): + channel = json_rest.channels.get("persisted:enc_jsondict", + cipher=cipher_params) data = {'foó': 'bár'} await channel.publish('event', data) history = await channel.history() @@ -239,9 +251,10 @@ async def test_with_json_dict_data_decode(self): assert message.data == data assert not message.encoding - async def test_with_json_list_data_decode(self): - channel = self.ably.channels.get("persisted:enc_list", - cipher=self.cipher_params) + @pytest.mark.asyncio + async def test_with_json_list_data_decode(self, json_rest, cipher_params): + channel = json_rest.channels.get("persisted:enc_list", + cipher=cipher_params) data = ['foó', 'bár'] await channel.publish('event', data) history = await channel.history() @@ -250,19 +263,13 @@ async def test_with_json_list_data_decode(self): assert not message.encoding -class TestBinaryEncodersNoEncryption(BaseAsyncTestCase): - - async def setUp(self): - self.ably = await RestSetup.get_ably_rest() - - async def tearDown(self): - await self.ably.close() - +class TestBinaryEncodersNoEncryption: def decode(self, data): return msgpack.unpackb(data) - async def test_text_utf8(self): - channel = self.ably.channels["persisted:publish"] + @pytest.mark.asyncio + async def test_text_utf8(self, msgpack_rest): + channel = msgpack_rest.channels["persisted:publish"] with mock.patch('ably.rest.rest.Http.post', wraps=channel.ably.http.post) as post_mock: @@ -271,8 +278,9 @@ async def test_text_utf8(self): assert self.decode(kwargs['body'])['data'] == 'foó' assert self.decode(kwargs['body']).get('encoding', '').strip('/') == '' - async def test_with_binary_type(self): - channel = self.ably.channels["persisted:publish"] + @pytest.mark.asyncio + async def test_with_binary_type(self, msgpack_rest): + channel = msgpack_rest.channels["persisted:publish"] with mock.patch('ably.rest.rest.Http.post', wraps=channel.ably.http.post) as post_mock: @@ -281,8 +289,9 @@ async def test_with_binary_type(self): assert self.decode(kwargs['body'])['data'] == bytearray(b'foo') assert self.decode(kwargs['body']).get('encoding', '').strip('/') == '' - async def test_with_json_dict_data(self): - channel = self.ably.channels["persisted:publish"] + @pytest.mark.asyncio + async def test_with_json_dict_data(self, msgpack_rest): + channel = msgpack_rest.channels["persisted:publish"] data = {'foó': 'bár'} with mock.patch('ably.rest.rest.Http.post', wraps=channel.ably.http.post) as post_mock: @@ -292,8 +301,9 @@ async def test_with_json_dict_data(self): assert raw_data == data assert self.decode(kwargs['body'])['encoding'].strip('/') == 'json' - async def test_with_json_list_data(self): - channel = self.ably.channels["persisted:publish"] + @pytest.mark.asyncio + async def test_with_json_list_data(self, msgpack_rest): + channel = msgpack_rest.channels["persisted:publish"] data = ['foó', 'bár'] with mock.patch('ably.rest.rest.Http.post', wraps=channel.ably.http.post) as post_mock: @@ -303,8 +313,9 @@ async def test_with_json_list_data(self): assert raw_data == data assert self.decode(kwargs['body'])['encoding'].strip('/') == 'json' - async def test_text_utf8_decode(self): - channel = self.ably.channels["persisted:stringdecode-bin"] + @pytest.mark.asyncio + async def test_text_utf8_decode(self, msgpack_rest): + channel = msgpack_rest.channels["persisted:stringdecode-bin"] await channel.publish('event', 'fóo') history = await channel.history() @@ -313,8 +324,9 @@ async def test_text_utf8_decode(self): assert isinstance(message.data, str) assert not message.encoding - async def test_with_binary_type_decode(self): - channel = self.ably.channels["persisted:binarydecode-bin"] + @pytest.mark.asyncio + async def test_with_binary_type_decode(self, msgpack_rest): + channel = msgpack_rest.channels["persisted:binarydecode-bin"] await channel.publish('event', bytearray(b'foob')) history = await channel.history() @@ -322,8 +334,9 @@ async def test_with_binary_type_decode(self): assert message.data == bytearray(b'foob') assert not message.encoding - async def test_with_json_dict_data_decode(self): - channel = self.ably.channels["persisted:jsondict-bin"] + @pytest.mark.asyncio + async def test_with_json_dict_data_decode(self, msgpack_rest): + channel = msgpack_rest.channels["persisted:jsondict-bin"] data = {'foó': 'bár'} await channel.publish('event', data) history = await channel.history() @@ -331,8 +344,9 @@ async def test_with_json_dict_data_decode(self): assert message.data == data assert not message.encoding - async def test_with_json_list_data_decode(self): - channel = self.ably.channels["persisted:jsonarray-bin"] + @pytest.mark.asyncio + async def test_with_json_list_data_decode(self, msgpack_rest): + channel = msgpack_rest.channels["persisted:jsonarray-bin"] data = ['foó', 'bár'] await channel.publish('event', data) history = await channel.history() @@ -341,14 +355,7 @@ async def test_with_json_list_data_decode(self): assert not message.encoding -class TestBinaryEncodersEncryption(BaseAsyncTestCase): - - async def setUp(self): - self.ably = await RestSetup.get_ably_rest() - self.cipher_params = CipherParams(secret_key='keyfordecrypt_16', algorithm='aes') - - async def tearDown(self): - await self.ably.close() +class TestBinaryEncodersEncryption: def decrypt(self, payload, options=None): if options is None: @@ -359,9 +366,10 @@ def decrypt(self, payload, options=None): def decode(self, data): return msgpack.unpackb(data) - async def test_text_utf8(self): - channel = self.ably.channels.get("persisted:publish_enc", - cipher=self.cipher_params) + @pytest.mark.asyncio + async def test_text_utf8(self, msgpack_rest, cipher_params): + channel = msgpack_rest.channels.get("persisted:publish_enc", + cipher=cipher_params) with mock.patch('ably.rest.rest.Http.post', wraps=channel.ably.http.post) as post_mock: await channel.publish('event', 'fóo') @@ -370,12 +378,12 @@ async def test_text_utf8(self): data = self.decrypt(self.decode(kwargs['body'])['data']).decode('utf-8') assert data == 'fóo' - async def test_with_binary_type(self): - channel = self.ably.channels.get("persisted:publish_enc", - cipher=self.cipher_params) + @pytest.mark.asyncio + async def test_with_binary_type(self, msgpack_rest, cipher_params): + channel = msgpack_rest.channels.get("persisted:publish_enc", + cipher=cipher_params) - with mock.patch('ably.rest.rest.Http.post', - wraps=channel.ably.http.post) as post_mock: + with mock.patch('ably.rest.rest.Http.post', new_callable=AsyncMock) as post_mock: await channel.publish('event', bytearray(b'foo')) _, kwargs = post_mock.call_args @@ -384,9 +392,10 @@ async def test_with_binary_type(self): assert data == bytearray(b'foo') assert isinstance(data, bytearray) - async def test_with_json_dict_data(self): - channel = self.ably.channels.get("persisted:publish_enc", - cipher=self.cipher_params) + @pytest.mark.asyncio + async def test_with_json_dict_data(self, msgpack_rest, cipher_params): + channel = msgpack_rest.channels.get("persisted:publish_enc", + cipher=cipher_params) data = {'foó': 'bár'} with mock.patch('ably.rest.rest.Http.post', wraps=channel.ably.http.post) as post_mock: @@ -396,9 +405,10 @@ async def test_with_json_dict_data(self): raw_data = self.decrypt(self.decode(kwargs['body'])['data']).decode('ascii') assert json.loads(raw_data) == data - async def test_with_json_list_data(self): - channel = self.ably.channels.get("persisted:publish_enc", - cipher=self.cipher_params) + @pytest.mark.asyncio + async def test_with_json_list_data(self, msgpack_rest, cipher_params): + channel = msgpack_rest.channels.get("persisted:publish_enc", + cipher=cipher_params) data = ['foó', 'bár'] with mock.patch('ably.rest.rest.Http.post', wraps=channel.ably.http.post) as post_mock: @@ -408,9 +418,10 @@ async def test_with_json_list_data(self): raw_data = self.decrypt(self.decode(kwargs['body'])['data']).decode('ascii') assert json.loads(raw_data) == data - async def test_text_utf8_decode(self): - channel = self.ably.channels.get("persisted:enc_stringdecode-bin", - cipher=self.cipher_params) + @pytest.mark.asyncio + async def test_text_utf8_decode(self, msgpack_rest, cipher_params): + channel = msgpack_rest.channels.get("persisted:enc_stringdecode-bin", + cipher=cipher_params) await channel.publish('event', 'foó') history = await channel.history() message = history.items[0] @@ -418,9 +429,10 @@ async def test_text_utf8_decode(self): assert isinstance(message.data, str) assert not message.encoding - async def test_with_binary_type_decode(self): - channel = self.ably.channels.get("persisted:enc_binarydecode-bin", - cipher=self.cipher_params) + @pytest.mark.asyncio + async def test_with_binary_type_decode(self, msgpack_rest, cipher_params): + channel = msgpack_rest.channels.get("persisted:enc_binarydecode-bin", + cipher=cipher_params) await channel.publish('event', bytearray(b'foob')) history = await channel.history() @@ -429,9 +441,10 @@ async def test_with_binary_type_decode(self): assert isinstance(message.data, bytearray) assert not message.encoding - async def test_with_json_dict_data_decode(self): - channel = self.ably.channels.get("persisted:enc_jsondict-bin", - cipher=self.cipher_params) + @pytest.mark.asyncio + async def test_with_json_dict_data_decode(self, msgpack_rest, cipher_params): + channel = msgpack_rest.channels.get("persisted:enc_jsondict-bin", + cipher=cipher_params) data = {'foó': 'bár'} await channel.publish('event', data) history = await channel.history() @@ -439,9 +452,10 @@ async def test_with_json_dict_data_decode(self): assert message.data == data assert not message.encoding - async def test_with_json_list_data_decode(self): - channel = self.ably.channels.get("persisted:enc_list-bin", - cipher=self.cipher_params) + @pytest.mark.asyncio + async def test_with_json_list_data_decode(self, msgpack_rest, cipher_params): + channel = msgpack_rest.channels.get("persisted:enc_list-bin", + cipher=cipher_params) data = ['foó', 'bár'] await channel.publish('event', data) history = await channel.history()