From 66c9731bfa3647ab42d63c27a2626ad892e3ceb0 Mon Sep 17 00:00:00 2001 From: Roger Luethi Date: Thu, 6 Aug 2026 10:10:09 +0200 Subject: [PATCH] tests: narrow the secrets-file existence mock The setup_password fixture patched the existence check with a flat return_value: mocker.patch("osism.utils.rabbitmq.os.path.exists", return_value=exists) That target reads as module-scoped but resolves to the attribute on the shared os.path module, so for the duration of the patch os.path.exists answers the same way for every caller in the process. load_rabbitmq_password() then calls osism.tasks.conductor.utils.load_yaml_file, whose import pulls in ansible. Ansible's config manager reads ansible/config/base.yml and checks os.path.exists first, gets the mocked answer, and raises: AnsibleError: Missing base YAML definition file (bad install?): .../site-packages/ansible/config/base.yml so test_password_file_missing_returns_none fails -- with a message that points at ansible's installation rather than at the mock -- whenever ansible-core happens to be importable. It passes today only because the unit-test environment has no ansible-core, which makes the suite quietly dependent on that staying true. Answer for the path under test and defer everything else to the real function. The test keeps asserting what it asserted; it just stops answering questions it was not asked. Verified in two virtualenvs differing only in whether ansible-core is installed: before, the test failed in the one that had it; after, the module passes in both. Assisted-by: Claude:claude-opus-5 Signed-off-by: Roger Luethi --- tests/unit/utils/test_rabbitmq.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/tests/unit/utils/test_rabbitmq.py b/tests/unit/utils/test_rabbitmq.py index d061b297..4c6a03c4 100644 --- a/tests/unit/utils/test_rabbitmq.py +++ b/tests/unit/utils/test_rabbitmq.py @@ -18,6 +18,7 @@ """ import json +import os import subprocess from types import SimpleNamespace from unittest.mock import call @@ -91,12 +92,29 @@ def _setup(*, hosts, redis_side_effect, check_output): return _setup +# The path ``load_rabbitmq_password()`` checks, named so the fixture below can +# answer for it alone rather than for every ``os.path.exists`` caller. +SECRETS_PATH = "/opt/configuration/environments/kolla/secrets.yml" + + @pytest.fixture def setup_password(mocker): """Factory patching the secrets-file existence check and the YAML loader.""" def _setup(*, exists=True, load_yaml=None, load_raises=None): - mocker.patch("osism.utils.rabbitmq.os.path.exists", return_value=exists) + # Answer only for the path under test. Patching os.path.exists with a + # flat return_value answers for every caller, and the loader imported + # below pulls in ansible, whose config manager reads + # ansible/config/base.yml through the same function -- so a False here + # surfaced as "Missing base YAML definition file (bad install?)" + # instead of as the case being tested. + real_exists = os.path.exists + mocker.patch( + "osism.utils.rabbitmq.os.path.exists", + side_effect=lambda path, *args, **kwargs: ( + exists if path == SECRETS_PATH else real_exists(path, *args, **kwargs) + ), + ) if load_raises is not None: mocker.patch( "osism.tasks.conductor.utils.load_yaml_file", side_effect=load_raises