Repository: ClearcodeHQ/pytest-rabbitmq (language: python)

Issue:
Switch from rabbitpy to a maintained client library such as pika
### What action do you want to perform

Update `pytest-rabbitmq` to use a maintained client library such as [pika](https://pypi.org/project/pika/) instead of rabbitpy.

### What are the results

[rabbitpy](https://github.com/gmr/rabbitpy) hasn't been updated on PyPI [since 2019](https://pypi.org/project/rabbitpy/#history) (4 years ago!) and does not work with 3.x releases of [pamqp](https://pypi.org/project/pamqp/):

```
>>> from rabbitpy import Exchange, Queue, Connection
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/thomas/.cache/pypoetry/virtualenvs/teal-sCn2En4E-py3.11/lib/python3.11/site-packages/rabbitpy/__init__.py", line 24, in <module>
    from rabbitpy.connection import Connection
  File "/home/thomas/.cache/pypoetry/virtualenvs/teal-sCn2En4E-py3.11/lib/python3.11/site-packages/rabbitpy/connection.py", line 17, in <module>
    from rabbitpy import base
  File "/home/thomas/.cache/pypoetry/virtualenvs/teal-sCn2En4E-py3.11/lib/python3.11/site-packages/rabbitpy/base.py", line 13, in <module>
    from pamqp import specification
ImportError: cannot import name 'specification' from 'pamqp' (/home/thomas/.cache/pypoetry/virtualenvs/teal-sCn2En4E-py3.11/lib/python3.11/site-packages/pamqp/__init__.py)
```

This is blocking when using aio-pika or aioamqp, official clients with asyncio supports from [rabbitmq.com's list](https://www.rabbitmq.com/devtools.html), which require pamqp >=3.1.

### What are the expected results

Being able to use `pytest-rabbitmq` with referenced asyncio compatible libraries for RabbitMQ. For this, we need to have a library that supports recent releases of pamqp. [rabbitmq.com recommends pika](https://www.rabbitmq.com/devtools.html).


Discussion / hints:

Fix patch:
diff --git a/setup.cfg b/setup.cfg
index d39891f..2ed6402 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -39,7 +39,7 @@ install_requires =
     pytest>=3.0.0
     port-for>=0.6.0
     mirakuru>=2.0.0
-    rabbitpy
+    pika
 
 [options.entry_points]
 pytest11 =


Test patch:
diff --git a/requirements-test.txt b/requirements-test.txt
index 0ea08db..352a1db 100644
--- a/requirements-test.txt
+++ b/requirements-test.txt
@@ -3,7 +3,7 @@ pytest==7.2.0
 pytest-xdist==2.5.0
 pytest-cov==4.0.0
 coverage==6.5.0 # pytest-cov
-rabbitpy==2.0.1
+pika==1.3.2
 port-for==0.6.2
 mirakuru==2.4.2
 -e .[tests]
diff --git a/src/pytest_rabbitmq/factories/client.py b/src/pytest_rabbitmq/factories/client.py
index e5c1a00..0f74729 100644
--- a/src/pytest_rabbitmq/factories/client.py
+++ b/src/pytest_rabbitmq/factories/client.py
@@ -20,8 +20,10 @@
 
 import pytest
 
-from rabbitpy import Exchange, Queue, Connection
-from rabbitpy.exceptions import ChannelClosedException
+from pika.credentials import PlainCredentials
+from pika import ConnectionParameters
+from pika.adapters.blocking_connection import BlockingConnection, BlockingChannel
+from pika.exceptions import ChannelClosed
 
 logger = logging.getLogger("pytest-rabbitmq")
 
@@ -31,7 +33,7 @@ def clear_rabbitmq(process, rabbitmq_connection):
     Clear queues and exchanges from given rabbitmq process.
 
     :param RabbitMqExecutor process: rabbitmq process
-    :param rabbitpy.Connection rabbitmq_connection: connection to rabbitmq
+    :param pika.connection.Connection rabbitmq_connection: connection to rabbitmq
 
     """
     channel = rabbitmq_connection.channel()
@@ -48,8 +50,7 @@ def clear_rabbitmq(process, rabbitmq_connection):
             # exchange already exists. Error code: access-refused
             # ----------------------------------------------------------------
             continue
-        ex = Exchange(channel, exchange)
-        ex.delete()
+        channel.exchange_delete(exchange)
 
     for queue_name in process.list_queues():
         if queue_name.startswith("amq."):
@@ -63,8 +64,7 @@ def clear_rabbitmq(process, rabbitmq_connection):
             # exists. Error code: access-refused
             # ----------------------------------------------------------------
             continue
-        queue = Queue(channel, queue_name)
-        queue.delete()
+        channel.queue_delete(queue_name)
 
 
 def rabbitmq(process_fixture_name, teardown=clear_rabbitmq):
@@ -95,19 +95,23 @@ def rabbitmq_factory(request):
 
         :param TCPExecutor rabbitmq_proc: tcp executor
         :param FixtureRequest request: fixture request object
-        :rtype: rabbitpy.adapters.blocking_connection.BlockingConnection
+        :rtype: pika.adapters.blocking_connection.BlockingConnection
         :returns: instance of :class:`BlockingConnection`
         """
         # load required process fixture
         process = request.getfixturevalue(process_fixture_name)
 
-        connection = Connection(f"amqp://guest:guest@{process.host}:{process.port}/%2F")
+        credentials = PlainCredentials("guest", "guest")
+        parameters = ConnectionParameters(
+            host=process.host, port=process.port, virtual_host="/", credentials=credentials
+        )
+        connection = BlockingConnection(parameters)
 
         yield connection
         teardown(process, connection)
         try:
             connection.close()
-        except ChannelClosedException as e:
+        except ChannelClosed as e:
             # at this stage this exception occurs when connection is being closed
             logger.warning(f"ChannelClosedException occured while closing connection {e}")
 
diff --git a/src/pytest_rabbitmq/factories/executor.py b/src/pytest_rabbitmq/factories/executor.py
index 5e0a388..59176e6 100644
--- a/src/pytest_rabbitmq/factories/executor.py
+++ b/src/pytest_rabbitmq/factories/executor.py
@@ -60,7 +60,7 @@ def list_exchanges(self):
         """Get exchanges defined on given rabbitmq."""
         exchanges = []
         output = self.rabbitctl_output("list_exchanges", "name")
-        unwanted_exchanges = ["Listing exchanges ...", "...done."]
+        unwanted_exchanges = ["Listing exchanges for vhost / ...", "...done."]
 
         for exchange in output.split("\n"):
             if exchange and exchange not in unwanted_exchanges:
diff --git a/tests/test_rabbitmq.py b/tests/test_rabbitmq.py
index 46b4dbb..7064877 100644
--- a/tests/test_rabbitmq.py
+++ b/tests/test_rabbitmq.py
@@ -1,5 +1,4 @@
 """Tests for RabbitMQ fixtures."""
-from rabbitpy import Exchange, Queue
 
 from pytest_rabbitmq.factories.client import clear_rabbitmq
 
@@ -7,31 +6,30 @@
 def test_rabbitmq(rabbitmq):
     """Check a signle. default rabbitmq."""
     channel = rabbitmq.channel()
-    assert channel.state == channel.OPEN
+    assert channel.is_open
 
 
 def test_second_rabbitmq(rabbitmq, rabbitmq2):
     """Check whether two rabbitmq are started correctly."""
     print("checking first channel")
     channel = rabbitmq.channel()
-    assert channel.state == channel.OPEN
+    assert channel.is_open
 
     print("checking second channel")
     channel2 = rabbitmq2.channel()
-    assert channel2.state == channel.OPEN
+    assert channel2.is_open
 
 
 def test_rabbitmq_clear_exchanges(rabbitmq, rabbitmq_proc):
     """Declare exchange, and clear it by clear_rabbitmq."""
     channel = rabbitmq.channel()
-    assert channel.state == channel.OPEN
+    assert channel.is_open
 
     # list exchanges
     no_exchanges = rabbitmq_proc.list_exchanges()
 
     # declare exchange and list exchanges afterwards
-    exchange = Exchange(channel, "cache-in")
-    exchange.declare()
+    channel.exchange_declare("cache-in")
     exchanges = rabbitmq_proc.list_exchanges()
 
     # make sure it differs
@@ -46,15 +44,14 @@ def test_rabbitmq_clear_exchanges(rabbitmq, rabbitmq_proc):
 def test_rabbitmq_clear_queues(rabbitmq, rabbitmq_proc):
     """Declare queue, and clear it by clear_rabbitmq."""
     channel = rabbitmq.channel()
-    assert channel.state == channel.OPEN
+    assert channel.is_open
 
     # list queues
     no_queues = rabbitmq_proc.list_queues()
     assert not no_queues
 
     # declare queue, and get new output
-    queue = Queue(channel, "fastlane")
-    queue.declare()
+    channel.queue_declare("fastlane")
     queues = rabbitmq_proc.list_queues()
     assert queues
 
@@ -70,7 +67,7 @@ def test_rabbitmq_clear_queues(rabbitmq, rabbitmq_proc):
 def test_random_port(rabbitmq_rand):
     """Test if rabbit fixture can be started on random port."""
     channel = rabbitmq_rand.channel()
-    assert channel.state == channel.OPEN
+    assert channel.is_open
 
 
 def test_random_port_node_names(rabbitmq_rand_proc2, rabbitmq_rand_proc3):