diff --git a/Makefile b/Makefile index d400651..9f7d861 100644 --- a/Makefile +++ b/Makefile @@ -30,7 +30,7 @@ docs: install-deps: @echo "> installing dependencies..." - pip install -r requirements-dev.txt + uv pip install -r requirements-dev.txt pre-commit install tox: diff --git a/rabbit/background_tasks.py b/rabbit/background_tasks.py index 703051b..d5ffab5 100644 --- a/rabbit/background_tasks.py +++ b/rabbit/background_tasks.py @@ -37,6 +37,9 @@ def discard(self, task: asyncio.Task) -> None: def tasks_by_name(self) -> List[str]: return [task_name for task_name in self._tasks.keys()] + def __getitem__(self, name: str) -> asyncio.Task: + return self._tasks[name] + def __iter__(self) -> Generator[asyncio.Task, None, None]: for _, task in self._tasks.items(): yield task diff --git a/rabbit/client.py b/rabbit/client.py index e2f4a65..ec8e7e8 100644 --- a/rabbit/client.py +++ b/rabbit/client.py @@ -1,6 +1,6 @@ import asyncio import random -from typing import List, Optional +from typing import List, Union from uuid import uuid4 import aioamqp @@ -40,7 +40,7 @@ def __repr__(self) -> str: return f"AioRabbitClient(connected={connected}, channels={channels}, max_channels={max_channels}, background_tasks={self._background_tasks})" @property - def server_properties(self) -> Optional[dict]: + def server_properties(self) -> Union[None, dict]: """Get server properties from the current connection.""" try: return self.protocol.server_properties # type: ignore diff --git a/rabbit/subscribe.py b/rabbit/subscribe.py index 3d6b5f4..1bc648d 100644 --- a/rabbit/subscribe.py +++ b/rabbit/subscribe.py @@ -1,7 +1,7 @@ import asyncio import os from contextlib import suppress -from typing import Callable, Optional +from typing import Callable, Union from aioamqp.channel import Channel from aioamqp.envelope import Envelope @@ -72,7 +72,6 @@ def __attrs_post_init__(self) -> None: @property def name(self) -> str: - """Object name.""" return "Subscribe" @property @@ -87,7 +86,7 @@ def channel(self, channel: Channel) -> None: self._dlx.channel = channel self._channel = channel - async def configure(self, channel: Optional[Channel] = None) -> None: + async def configure(self, channel: Union[None, Channel] = None) -> None: """Configure subscriber channel, queues and exchange.""" await self.qos(prefetch_count=self.concurrent) with suppress(SynchronizationError): diff --git a/tests/unit/test_background_tasks.py b/tests/unit/test_background_tasks.py new file mode 100644 index 0000000..bd2d063 --- /dev/null +++ b/tests/unit/test_background_tasks.py @@ -0,0 +1,42 @@ +import asyncio + +import pytest + +from rabbit.background_tasks import BackgroundTasks +from rabbit.job import async_echo_job + + +@pytest.fixture +def background_tasks(): + return BackgroundTasks() + + +async def test_background_tasks_add(background_tasks): + background_tasks.add("test-task", async_echo_job, b'{"message": "test"}') + assert len(background_tasks) == 1 + + +async def test_background_tasks_multiple_add(background_tasks): + background_tasks.add("test-task-1", async_echo_job, b'{"message": "test"}') + background_tasks.add("test-task-1", async_echo_job, b'{"message": "test"}') + background_tasks.add("test-task-2", async_echo_job, b'{"message": "test2"}') + assert len(background_tasks) == 2 + + +async def test_background_tasks_by_name(background_tasks): + background_tasks.add("test-task", async_echo_job, b'{"message": "test"}') + for task in background_tasks: + assert task.get_name() == "test-task" + + +async def test_background_tasks_getitem(background_tasks): + background_tasks.add("test-task", async_echo_job, b'{"message": "test"}') + assert isinstance(background_tasks["test-task"], asyncio.Task) + + +def test_background_tasks_len(background_tasks): + assert len(background_tasks) == 0 + + +def test_background_tasks_repr(background_tasks): + assert repr(background_tasks) == "BackgroundTasks(tasks=0, tasks_by_name=[])" diff --git a/tests/unit/test_client.py b/tests/unit/test_client.py index 7a23cdb..fa30e71 100644 --- a/tests/unit/test_client.py +++ b/tests/unit/test_client.py @@ -2,7 +2,7 @@ import pytest -from rabbit.client import aioamqp +from rabbit.client import AioRabbitClient, aioamqp from rabbit.exceptions import AttributeNotInitialized from tests.conftest import AioAmqpMock @@ -44,3 +44,8 @@ def test_server_properties_with_client_not_connected(client): def test_server_properties(client_mock): assert isinstance(client_mock.server_properties, dict) + + +@pytest.mark.parametrize("attribute", ["transport", "server_properties", "protocol"]) +def test_client_attributes(attribute): + assert hasattr(AioRabbitClient, attribute) diff --git a/tests/unit/test_dlx.py b/tests/unit/test_dlx.py index d1fb0a9..2ee4542 100644 --- a/tests/unit/test_dlx.py +++ b/tests/unit/test_dlx.py @@ -1,5 +1,6 @@ import pytest +from rabbit.dlx import DLX from rabbit.exceptions import OperationError from tests.conftest import EnvelopeMock, PropertiesMock @@ -20,3 +21,10 @@ async def test_send_event_error_without_client_connection(dlx): def test_dlx_repr(dlx): assert isinstance(repr(dlx), str) + + +@pytest.mark.parametrize( + "attribute", ["exchange", "dlq_exchange", "queue", "delay_strategy", "channel"] +) +def test_dlx_attributes(attribute): + assert hasattr(DLX, attribute) diff --git a/tests/unit/test_exceptions.py b/tests/unit/test_exceptions.py new file mode 100644 index 0000000..990ffa2 --- /dev/null +++ b/tests/unit/test_exceptions.py @@ -0,0 +1,14 @@ +from rabbit.exceptions import ClientNotConnectedError, ExchangeNotFound + + +def test_exchange_not_found_message(): + assert ( + str(ExchangeNotFound("test-exchange")) == "Exchange 'test-exchange' not found" + ) + + +def test_client_not_connected_error(): + assert ( + str(ClientNotConnectedError()) + == "AioRabbitClient was not connected with RabbitMQ" + ) diff --git a/tests/unit/test_jobs.py b/tests/unit/test_jobs.py index 186df2d..9ef8dd3 100644 --- a/tests/unit/test_jobs.py +++ b/tests/unit/test_jobs.py @@ -1,6 +1,14 @@ -from rabbit.job import async_echo_job +import pytest + +from rabbit.job import async_chaos_job, async_echo_job async def test_async_echo_job(): resp = await async_echo_job(b'{"process": 123}', skip_wait=True) assert resp == b'{"process": 123}' + + +@pytest.mark.xfail(reason="The test may fail due to randomness issue") +async def test_async_chaos_job(): + resp = await async_chaos_job(b'{"process": 123}', skip_wait=True) + assert resp == b'{"process": 123}' diff --git a/tests/unit/test_publish.py b/tests/unit/test_publish.py index 7029862..d85845d 100644 --- a/tests/unit/test_publish.py +++ b/tests/unit/test_publish.py @@ -24,3 +24,10 @@ async def test_publish_confirms_disabled(publish): async def test_publish_confirms_enabled(): publish = Publish(True) assert publish.publish_confirms is True + + +@pytest.mark.parametrize( + "attribute", ["publish_confirms", "name", "channel_id", "channel"] +) +def test_publish_attributes(attribute): + assert hasattr(Publish, attribute) diff --git a/tests/unit/test_queue.py b/tests/unit/test_queue.py index 0159755..3d349be 100644 --- a/tests/unit/test_queue.py +++ b/tests/unit/test_queue.py @@ -1,7 +1,6 @@ -import attr +import pytest -def test_attributes(queue): - values = ["queue", True, {}] - for value in values: - assert value in attr.asdict(queue).values() +@pytest.mark.parametrize("attribute", ["name", "durable", "arguments"]) +def test_attributes(queue, attribute): + assert hasattr(queue, attribute) diff --git a/tests/unit/test_subscribe.py b/tests/unit/test_subscribe.py index 4849c56..2fb6662 100644 --- a/tests/unit/test_subscribe.py +++ b/tests/unit/test_subscribe.py @@ -1,10 +1,9 @@ import pytest from rabbit.exceptions import ClientNotConnectedError +from rabbit.subscribe import Subscribe from tests.conftest import EnvelopeMock -PAYLOAD = b'{"a": 1}' - async def test_register_subscribe_without_client_connected(subscribe): with pytest.raises(ClientNotConnectedError): @@ -25,3 +24,10 @@ def test_subscribe_repr(subscribe_mock): async def test_nack_event(subscribe_mock): await subscribe_mock.nack_event(EnvelopeMock()) + + +@pytest.mark.parametrize( + "attribute", ["task", "exchange", "queue", "concurrent", "delay_strategy"] +) +def test_subscribe_attributes(attribute): + assert hasattr(Subscribe, attribute)