From 36af2cd19280f36c8130f3ebebbfa6dbbefe0b34 Mon Sep 17 00:00:00 2001 From: Daniel McKnight Date: Thu, 14 May 2026 16:59:19 -0700 Subject: [PATCH 01/10] Update test dependency installation and GHA test automation --- .github/workflows/unit_tests.yml | 8 +++---- setup.py | 36 ++++++++++++++++++++------------ 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index a13dc86..9f46dd7 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -12,7 +12,7 @@ jobs: timeout-minutes: 5 strategy: matrix: - python-version: [3.8, 3.9, '3.10', '3.11'] + python-version: ['3.10', '3.11', '3.12'] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -27,11 +27,11 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install . -r requirements/test_requirements.txt + pip install .[test] - - name: Test Client + - name: Run Tests run: | - pytest tests/test_client.py --doctest-modules --junitxml=tests/client-test-results.xml + pytest tests - name: Upload client test results uses: actions/upload-artifact@v4 with: diff --git a/setup.py b/setup.py index ed5a94a..b7f644b 100644 --- a/setup.py +++ b/setup.py @@ -36,17 +36,25 @@ def get_requirements(requirements_filename: str): - requirements_file = path.join(BASE_PATH, "requirements", requirements_filename) - with open(requirements_file, 'r', encoding='utf-8') as r: + requirements_file = path.join( + BASE_PATH, "requirements", requirements_filename + ) + with open(requirements_file, "r", encoding="utf-8") as r: requirements = r.readlines() - requirements = [r.strip() for r in requirements if r.strip() and not r.strip().startswith("#")] + requirements = [ + r.strip() + for r in requirements + if r.strip() and not r.strip().startswith("#") + ] return requirements with open(path.join(BASE_PATH, "README.md"), "r") as f: long_description = f.read() -with open(path.join(BASE_PATH, "neon_iris", "version.py"), "r", encoding="utf-8") as v: +with open( + path.join(BASE_PATH, "neon_iris", "version.py"), "r", encoding="utf-8" +) as v: for line in v.readlines(): if line.startswith("__version__"): if '"' in line: @@ -57,9 +65,9 @@ def get_requirements(requirements_filename: str): setuptools.setup( name="neon-iris", version=version, - author='Neongecko', - author_email='developers@neon.ai', - license='BSD-3-Clause', + author="Neongecko", + author_email="developers@neon.ai", + license="BSD-3-Clause", description="Interactive Relay for Intelligence Systems", long_description=long_description, long_description_content_type="text/markdown", @@ -68,15 +76,17 @@ def get_requirements(requirements_filename: str): include_package_data=True, classifiers=[ "Programming Language :: Python :: 3", - "Operating System :: OS Independent" + "Operating System :: OS Independent", ], - python_requires='>=3.7', + python_requires=">=3.7", install_requires=get_requirements("requirements.txt"), - extras_require={"gradio": get_requirements("gradio.txt"), "web_sat": get_requirements("web_sat.txt")}, - entry_points={ - 'console_scripts': ['iris=neon_iris.cli:neon_iris_cli'] + extras_require={ + "gradio": get_requirements("gradio.txt"), + "web_sat": get_requirements("web_sat.txt"), + "test": get_requirements("test_requirements.txt"), }, + entry_points={"console_scripts": ["iris=neon_iris.cli:neon_iris_cli"]}, package_data={ "neon_iris": ["static/*", "templates/*", "res/*", "wakeword_models/*"] - } + }, ) From 5555cfc044e52e91e55a0a1dd07450938e81972d Mon Sep 17 00:00:00 2001 From: Daniel McKnight Date: Thu, 14 May 2026 16:43:39 -0700 Subject: [PATCH 02/10] Update server used for MQ connection tests --- tests/test_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_client.py b/tests/test_client.py index 2efa182..912e0fc 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -37,7 +37,7 @@ _test_config = { "MQ": { - "server": "mq.2022.us", + "server": "mq.neonaialpha.com", "port": 25672, "users": { "mq_handler": { From 2c2a574186bec05dbf0f374aa4176399030cd856 Mon Sep 17 00:00:00 2001 From: Daniel McKnight Date: Thu, 14 May 2026 17:02:37 -0700 Subject: [PATCH 03/10] Update neon-utils dependency to resolve ovos-utils compat bug --- requirements/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/requirements.txt b/requirements/requirements.txt index b875544..cd26184 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -1,6 +1,6 @@ click~=8.0 click-default-group~=1.2 -neon-utils[sentry]~=1.12 +neon-utils[sentry]~=1.12,>=1.14.1a1 pyyaml>=5.4,<7.0.0 neon-mq-connector~=0.9,>=0.9.1a1 ovos-bus-client~=0.0 From 6855e972b21a2b39959d9414b24c41997212e724 Mon Sep 17 00:00:00 2001 From: Daniel McKnight Date: Thu, 14 May 2026 17:06:18 -0700 Subject: [PATCH 04/10] Clean up tests --- tests/test_client.py | 6 ------ tests/test_mq_connector.py | 3 --- 2 files changed, 9 deletions(-) diff --git a/tests/test_client.py b/tests/test_client.py index 912e0fc..5f27bc2 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -27,12 +27,9 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import os -import sys import unittest from neon_iris.mq_connector import IrisConnector - -sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))) from neon_iris.client import NeonAIClient _test_config = { @@ -61,6 +58,3 @@ def test_client_create(self): self.assertEqual(client.connection.vhost, "/neon_chat_api") client.shutdown() - -if __name__ == '__main__': - unittest.main() diff --git a/tests/test_mq_connector.py b/tests/test_mq_connector.py index 32e5a5f..f7cc927 100644 --- a/tests/test_mq_connector.py +++ b/tests/test_mq_connector.py @@ -72,6 +72,3 @@ def test_lifecycle(self): thread.join(timeout=5) self.assertFalse(thread.is_alive()) - -if __name__ == '__main__': - unittest.main() From 44bf7ff26c3e219d3588f741e5b7c9fd2329d372 Mon Sep 17 00:00:00 2001 From: Daniel McKnight Date: Thu, 14 May 2026 17:06:28 -0700 Subject: [PATCH 05/10] Pin `pytest` version to troubleshoot GHA failures --- requirements/test_requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements/test_requirements.txt b/requirements/test_requirements.txt index 2a4cbf7..b8f8327 100644 --- a/requirements/test_requirements.txt +++ b/requirements/test_requirements.txt @@ -1,2 +1,2 @@ -pytest -neon-minerva[rmq]>=0.2.1a3 \ No newline at end of file +pytest~=8.0 +neon-minerva[rmq]>=0.2.1a3 From 6b67cacbdd92ce3c6f804e8ae453a432c210844e Mon Sep 17 00:00:00 2001 From: Daniel McKnight Date: Thu, 14 May 2026 17:19:41 -0700 Subject: [PATCH 06/10] Apply Cursor-suggested changes to resolve GHA test failures --- .github/workflows/unit_tests.yml | 9 +++++---- .gitignore | 1 + tests/test_mq_connector.py | 5 +++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index 9f46dd7..d3641f6 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -31,9 +31,10 @@ jobs: - name: Run Tests run: | - pytest tests - - name: Upload client test results + pytest tests \ + --junitxml=tests/test-results-${{ matrix.python-version }}.xml + - name: Upload test results uses: actions/upload-artifact@v4 with: - name: client-test-results - path: tests/client-test-results-${{ matrix.python-version }}.xml + name: test-results-${{ matrix.python-version }} + path: tests/test-results-${{ matrix.python-version }}.xml diff --git a/.gitignore b/.gitignore index 7f6b817..3a495a1 100644 --- a/.gitignore +++ b/.gitignore @@ -46,6 +46,7 @@ htmlcov/ .cache nosetests.xml coverage.xml +tests/client-test-results-*.xml *.cover *.py,cover .hypothesis/ diff --git a/tests/test_mq_connector.py b/tests/test_mq_connector.py index f7cc927..eb43b89 100644 --- a/tests/test_mq_connector.py +++ b/tests/test_mq_connector.py @@ -60,8 +60,9 @@ def test_lifecycle(self): self.assertIsInstance(connector.connection, SelectConnection) self.assertFalse(connector.ready) - # Start the connector - thread = Thread(target=connector.run) + # Start the connector (daemon so interpreter shutdown never blocks on the + # pika SelectConnection ioloop if cleanup is slow or flaky in CI). + thread = Thread(target=connector.run, daemon=True) thread.start() connector.wait_for_connection() self.assertTrue(connector.ready) From 2df9142c27e4cf259c189f1184dcfebe97bb32aa Mon Sep 17 00:00:00 2001 From: Daniel McKnight Date: Thu, 14 May 2026 17:43:12 -0700 Subject: [PATCH 07/10] Apply Cursor fixes to test --- tests/conftest.py | 147 +++++++++++++++++++++++++++++++++++++ tests/test_mq_connector.py | 9 ++- 2 files changed, 153 insertions(+), 3 deletions(-) create mode 100644 tests/conftest.py diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..100dafe --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,147 @@ +# NEON AI (TM) SOFTWARE, Software Development Kit & Application Framework +# All trademark and other rights reserved by their respective owners +# Copyright 2008-2025 Neongecko.com Inc. +# BSD-3 License + +""" +Test session helpers. + +Why this file exists +-------------------- +The upstream ``neon_minerva.integration.rabbit_mq.rmq_instance`` fixture +starts a RabbitMQ subprocess but never tears it down. It relies on +``mirakuru``'s ``atexit`` cleanup hook to SIGKILL the process during +interpreter shutdown. + +In GitHub Actions the Erlang VM that backs RabbitMQ does not always die +cleanly when the wrapper process is signalled at interpreter exit, which +leaves ``pytest`` blocked during shutdown. The tests themselves print as +``PASSED``, but the job hits the workflow timeout because the python process +never returns. + +We: + +* override the fixture with an explicit ``yield``/teardown so the broker is + stopped as soon as the test class is done with it, and +* register a ``pytest_sessionfinish`` hook that arms a watchdog which forces + the interpreter to exit if the normal shutdown hangs for too long. +""" + +from __future__ import annotations + +import os +import sys +import threading +import time + +import pytest +from os import environ + +from port_for import get_port +from pytest_rabbitmq.factories.executor import RabbitMqExecutor +from pytest_rabbitmq.factories.process import get_config + + +_ACTIVE_EXECUTORS: list[RabbitMqExecutor] = [] +# Seconds we'll allow normal interpreter shutdown to take after the test +# session has finished before we force-exit. Generous enough that pytest can +# print its summary; short enough that the GHA job-level timeout won't fire. +_FORCE_EXIT_GRACE_SECONDS = 30 + + +@pytest.fixture(scope="class") +def rmq_instance(request, tmp_path_factory): + """Start a RabbitMQ subprocess for the test class and stop it after.""" + config = get_config(request) + rabbit_ctl = config["ctl"] + rabbit_server = config["server"] + rabbit_host = "127.0.0.1" + rabbit_port = get_port(config["port"]) + rabbit_distribution_port = get_port( + config["distribution_port"], [rabbit_port] + ) + assert rabbit_distribution_port + assert rabbit_distribution_port != rabbit_port, ( + "rabbit_port and distribution_port can not be the same!" + ) + + tmpdir = tmp_path_factory.mktemp(f"pytest-rabbitmq-{request.fixturename}") + rabbit_logpath = config["logsdir"] or (tmpdir / "logs") + + executor = RabbitMqExecutor( + rabbit_server, + rabbit_host, + rabbit_port, + rabbit_distribution_port, + rabbit_ctl, + logpath=rabbit_logpath, + path=tmpdir, + plugin_path=config["plugindir"], + node_name=config["node"], + ) + executor.start() + _ACTIVE_EXECUTORS.append(executor) + + rmq_username = environ.get("TEST_RMQ_USERNAME", "test_user") + rmq_password = environ.get("TEST_RMQ_PASSWORD", "test_password") + rmq_vhosts = environ.get("TEST_RMQ_VHOSTS", "/test") + executor.rabbitctl_output("add_user", rmq_username, rmq_password) + for vhost in rmq_vhosts.split(","): + executor.rabbitctl_output("add_vhost", vhost) + executor.rabbitctl_output( + "set_permissions", "-p", vhost, rmq_username, ".*", ".*", ".*" + ) + + request.cls.rmq_instance = executor + try: + yield executor + finally: + _stop_executor_quietly(executor) + try: + _ACTIVE_EXECUTORS.remove(executor) + except ValueError: + pass + + +def _stop_executor_quietly(executor: RabbitMqExecutor | None) -> None: + """Best-effort teardown so a stuck broker cannot hang the session.""" + if executor is None: + return + try: + if executor.running(): + executor.stop() + except Exception: + try: + executor.kill(wait=False) + except Exception: + pass + + +def _arm_force_exit(exitstatus: int, grace: int) -> None: + """Force the interpreter to exit if normal shutdown stalls. + + The watchdog runs in a daemon thread so it cannot itself prevent exit. + If the process has not terminated within ``grace`` seconds of pytest + finishing, we ``os._exit`` so CI sees a clean (non-timeout) result. + """ + + def _watchdog() -> None: + time.sleep(grace) + sys.stderr.write( + f"\n[conftest] forcing process exit after {grace}s grace period; " + f"normal shutdown stalled.\n" + ) + sys.stderr.flush() + os._exit(int(exitstatus)) + + threading.Thread( + target=_watchdog, name="conftest-force-exit", daemon=True + ).start() + + +def pytest_sessionfinish(session, exitstatus): # noqa: D401 + """Stop tracked executors and arm a hard-exit watchdog.""" + for executor in list(_ACTIVE_EXECUTORS): + _stop_executor_quietly(executor) + _ACTIVE_EXECUTORS.clear() + _arm_force_exit(int(exitstatus), _FORCE_EXIT_GRACE_SECONDS) diff --git a/tests/test_mq_connector.py b/tests/test_mq_connector.py index eb43b89..ae54f70 100644 --- a/tests/test_mq_connector.py +++ b/tests/test_mq_connector.py @@ -33,10 +33,13 @@ import pytest from os import environ -from neon_minerva.integration.rabbit_mq import rmq_instance from neon_mq_connector import MQConnector from pika.adapters.select_connection import SelectConnection +# `rmq_instance` is provided by ``tests/conftest.py``; we deliberately avoid +# importing the upstream fixture here because it has no teardown, which +# leaves a RabbitMQ subprocess alive past the session and hangs CI shutdown. + environ['TEST_RMQ_USERNAME'] = "test_user" environ['TEST_RMQ_PASSWORD'] = "test_password" environ['TEST_RMQ_VHOSTS'] = "/neon_chat_api" @@ -60,8 +63,8 @@ def test_lifecycle(self): self.assertIsInstance(connector.connection, SelectConnection) self.assertFalse(connector.ready) - # Start the connector (daemon so interpreter shutdown never blocks on the - # pika SelectConnection ioloop if cleanup is slow or flaky in CI). + # Start the connector (daemon so interpreter shutdown never blocks on + # the pika SelectConnection ioloop if cleanup is slow or flaky in CI). thread = Thread(target=connector.run, daemon=True) thread.start() connector.wait_for_connection() From a0550066c18b164e8bd8f6a7780912f752a580f3 Mon Sep 17 00:00:00 2001 From: Daniel McKnight Date: Thu, 14 May 2026 17:56:27 -0700 Subject: [PATCH 08/10] Clean up Cursor changes --- .gitignore | 2 +- tests/conftest.py | 147 ------------------------------------- tests/test_mq_connector.py | 10 +-- 3 files changed, 4 insertions(+), 155 deletions(-) delete mode 100644 tests/conftest.py diff --git a/.gitignore b/.gitignore index 3a495a1..90ec82f 100644 --- a/.gitignore +++ b/.gitignore @@ -46,7 +46,7 @@ htmlcov/ .cache nosetests.xml coverage.xml -tests/client-test-results-*.xml +tests/test-results-*.xml *.cover *.py,cover .hypothesis/ diff --git a/tests/conftest.py b/tests/conftest.py deleted file mode 100644 index 100dafe..0000000 --- a/tests/conftest.py +++ /dev/null @@ -1,147 +0,0 @@ -# NEON AI (TM) SOFTWARE, Software Development Kit & Application Framework -# All trademark and other rights reserved by their respective owners -# Copyright 2008-2025 Neongecko.com Inc. -# BSD-3 License - -""" -Test session helpers. - -Why this file exists --------------------- -The upstream ``neon_minerva.integration.rabbit_mq.rmq_instance`` fixture -starts a RabbitMQ subprocess but never tears it down. It relies on -``mirakuru``'s ``atexit`` cleanup hook to SIGKILL the process during -interpreter shutdown. - -In GitHub Actions the Erlang VM that backs RabbitMQ does not always die -cleanly when the wrapper process is signalled at interpreter exit, which -leaves ``pytest`` blocked during shutdown. The tests themselves print as -``PASSED``, but the job hits the workflow timeout because the python process -never returns. - -We: - -* override the fixture with an explicit ``yield``/teardown so the broker is - stopped as soon as the test class is done with it, and -* register a ``pytest_sessionfinish`` hook that arms a watchdog which forces - the interpreter to exit if the normal shutdown hangs for too long. -""" - -from __future__ import annotations - -import os -import sys -import threading -import time - -import pytest -from os import environ - -from port_for import get_port -from pytest_rabbitmq.factories.executor import RabbitMqExecutor -from pytest_rabbitmq.factories.process import get_config - - -_ACTIVE_EXECUTORS: list[RabbitMqExecutor] = [] -# Seconds we'll allow normal interpreter shutdown to take after the test -# session has finished before we force-exit. Generous enough that pytest can -# print its summary; short enough that the GHA job-level timeout won't fire. -_FORCE_EXIT_GRACE_SECONDS = 30 - - -@pytest.fixture(scope="class") -def rmq_instance(request, tmp_path_factory): - """Start a RabbitMQ subprocess for the test class and stop it after.""" - config = get_config(request) - rabbit_ctl = config["ctl"] - rabbit_server = config["server"] - rabbit_host = "127.0.0.1" - rabbit_port = get_port(config["port"]) - rabbit_distribution_port = get_port( - config["distribution_port"], [rabbit_port] - ) - assert rabbit_distribution_port - assert rabbit_distribution_port != rabbit_port, ( - "rabbit_port and distribution_port can not be the same!" - ) - - tmpdir = tmp_path_factory.mktemp(f"pytest-rabbitmq-{request.fixturename}") - rabbit_logpath = config["logsdir"] or (tmpdir / "logs") - - executor = RabbitMqExecutor( - rabbit_server, - rabbit_host, - rabbit_port, - rabbit_distribution_port, - rabbit_ctl, - logpath=rabbit_logpath, - path=tmpdir, - plugin_path=config["plugindir"], - node_name=config["node"], - ) - executor.start() - _ACTIVE_EXECUTORS.append(executor) - - rmq_username = environ.get("TEST_RMQ_USERNAME", "test_user") - rmq_password = environ.get("TEST_RMQ_PASSWORD", "test_password") - rmq_vhosts = environ.get("TEST_RMQ_VHOSTS", "/test") - executor.rabbitctl_output("add_user", rmq_username, rmq_password) - for vhost in rmq_vhosts.split(","): - executor.rabbitctl_output("add_vhost", vhost) - executor.rabbitctl_output( - "set_permissions", "-p", vhost, rmq_username, ".*", ".*", ".*" - ) - - request.cls.rmq_instance = executor - try: - yield executor - finally: - _stop_executor_quietly(executor) - try: - _ACTIVE_EXECUTORS.remove(executor) - except ValueError: - pass - - -def _stop_executor_quietly(executor: RabbitMqExecutor | None) -> None: - """Best-effort teardown so a stuck broker cannot hang the session.""" - if executor is None: - return - try: - if executor.running(): - executor.stop() - except Exception: - try: - executor.kill(wait=False) - except Exception: - pass - - -def _arm_force_exit(exitstatus: int, grace: int) -> None: - """Force the interpreter to exit if normal shutdown stalls. - - The watchdog runs in a daemon thread so it cannot itself prevent exit. - If the process has not terminated within ``grace`` seconds of pytest - finishing, we ``os._exit`` so CI sees a clean (non-timeout) result. - """ - - def _watchdog() -> None: - time.sleep(grace) - sys.stderr.write( - f"\n[conftest] forcing process exit after {grace}s grace period; " - f"normal shutdown stalled.\n" - ) - sys.stderr.flush() - os._exit(int(exitstatus)) - - threading.Thread( - target=_watchdog, name="conftest-force-exit", daemon=True - ).start() - - -def pytest_sessionfinish(session, exitstatus): # noqa: D401 - """Stop tracked executors and arm a hard-exit watchdog.""" - for executor in list(_ACTIVE_EXECUTORS): - _stop_executor_quietly(executor) - _ACTIVE_EXECUTORS.clear() - _arm_force_exit(int(exitstatus), _FORCE_EXIT_GRACE_SECONDS) diff --git a/tests/test_mq_connector.py b/tests/test_mq_connector.py index ae54f70..f7cc927 100644 --- a/tests/test_mq_connector.py +++ b/tests/test_mq_connector.py @@ -33,13 +33,10 @@ import pytest from os import environ +from neon_minerva.integration.rabbit_mq import rmq_instance from neon_mq_connector import MQConnector from pika.adapters.select_connection import SelectConnection -# `rmq_instance` is provided by ``tests/conftest.py``; we deliberately avoid -# importing the upstream fixture here because it has no teardown, which -# leaves a RabbitMQ subprocess alive past the session and hangs CI shutdown. - environ['TEST_RMQ_USERNAME'] = "test_user" environ['TEST_RMQ_PASSWORD'] = "test_password" environ['TEST_RMQ_VHOSTS'] = "/neon_chat_api" @@ -63,9 +60,8 @@ def test_lifecycle(self): self.assertIsInstance(connector.connection, SelectConnection) self.assertFalse(connector.ready) - # Start the connector (daemon so interpreter shutdown never blocks on - # the pika SelectConnection ioloop if cleanup is slow or flaky in CI). - thread = Thread(target=connector.run, daemon=True) + # Start the connector + thread = Thread(target=connector.run) thread.start() connector.wait_for_connection() self.assertTrue(connector.ready) From 66d011492572b451feb71ebc956eb972e7cd2ef4 Mon Sep 17 00:00:00 2001 From: Daniel McKnight Date: Fri, 15 May 2026 10:18:01 -0700 Subject: [PATCH 09/10] Remove extra import of Pytest fixture --- tests/test_mq_connector.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_mq_connector.py b/tests/test_mq_connector.py index f7cc927..48fba7d 100644 --- a/tests/test_mq_connector.py +++ b/tests/test_mq_connector.py @@ -33,7 +33,6 @@ import pytest from os import environ -from neon_minerva.integration.rabbit_mq import rmq_instance from neon_mq_connector import MQConnector from pika.adapters.select_connection import SelectConnection @@ -42,6 +41,7 @@ environ['TEST_RMQ_VHOSTS'] = "/neon_chat_api" +# Fixture defined in neon_minerva.integration.rabbit_mq @pytest.mark.usefixtures("rmq_instance") class TestClient(unittest.TestCase): mq_config = {"server": "localhost", From bab43a67f3024cc8f490bd9afb9583fdd3af0abc Mon Sep 17 00:00:00 2001 From: Daniel McKnight Date: Fri, 15 May 2026 10:24:28 -0700 Subject: [PATCH 10/10] Update neon-minerva dependency to include RMQ fix --- requirements/test_requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/test_requirements.txt b/requirements/test_requirements.txt index b8f8327..f747a2a 100644 --- a/requirements/test_requirements.txt +++ b/requirements/test_requirements.txt @@ -1,2 +1,2 @@ pytest~=8.0 -neon-minerva[rmq]>=0.2.1a3 +neon-minerva[rmq]>=0.3.1a4