From 8565291e43e17cd1ee5333e82e57aeb230e6574e Mon Sep 17 00:00:00 2001 From: Hao Liu <44379968+TheRealHaoLiu@users.noreply.github.com> Date: Wed, 28 Oct 2026 18:27:06 -0400 Subject: [PATCH] Fix awxkit config monkeypatching under pytest 9.2 (#16763) Use monkeypatch.setitem for awxkit config in tests awxkit's `config` is a PseudoNamespace, a dict subclass that exposes its keys as attributes. pytest 9.2.0 changed monkeypatch.setattr to read the previous value from the target's __dict__ instead of via getattr, so for a dict-backed namespace it always records the attribute as "did not exist" and schedules a delattr on undo. Patching the same key twice (autouse fixture plus test body) then deletes it twice, and PseudoNamespace.__delattr__ raises KeyError at teardown. This breaks 13 tests in test/cli/test_authentication.py on pytest >= 9.2.0. monkeypatch.setitem records the old value with dict.get, which sees the dict-backed storage correctly, so undo restores or removes exactly what was there on any pytest version. test/api/pages/test_base.py is updated for the same reason: under 9.2.0 its undo would delete a pre-existing base_url or credentials value instead of restoring it. Claude-Session: https://claude.ai/code/session_01SbeYPdRRLaGCk74gJHq9yS Signed-off-by: Hao Liu <44379968+TheRealHaoLiu@users.noreply.github.com> Co-authored-by: Claude Fable 5.1 --- awxkit/test/api/pages/test_base.py | 4 ++-- awxkit/test/cli/test_authentication.py | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/awxkit/test/api/pages/test_base.py b/awxkit/test/api/pages/test_base.py index 3126e333d8..137d78ab22 100644 --- a/awxkit/test/api/pages/test_base.py +++ b/awxkit/test/api/pages/test_base.py @@ -6,8 +6,8 @@ from awxkit.config import config @pytest.fixture(autouse=True) def setup_config(monkeypatch: pytest.MonkeyPatch) -> None: - monkeypatch.setattr(config, "credentials", {"default": {"username": "foo", "password": "bar"}}, raising=False) - monkeypatch.setattr(config, "base_url", "", raising=False) + monkeypatch.setitem(config, "credentials", {"default": {"username": "foo", "password": "bar"}}) + monkeypatch.setitem(config, "base_url", "") @pytest.fixture diff --git a/awxkit/test/cli/test_authentication.py b/awxkit/test/cli/test_authentication.py index f196f66043..c8428679d8 100644 --- a/awxkit/test/cli/test_authentication.py +++ b/awxkit/test/cli/test_authentication.py @@ -9,8 +9,8 @@ from awxkit import config @pytest.fixture(autouse=True) def reset_config_state(monkeypatch: pytest.MonkeyPatch) -> None: """Ensure clean config state for each test to prevent parallel test interference""" - monkeypatch.setattr(config, 'force_basic_auth', False, raising=False) - monkeypatch.setattr(config, 'use_sessions', False, raising=False) + monkeypatch.setitem(config, 'force_basic_auth', False) + monkeypatch.setitem(config, 'use_sessions', False) # ============================================================================ @@ -47,7 +47,7 @@ def setup_session_auth(cli_args: Optional[List[str]] = None) -> Tuple[CLI, Mock, def test_basic_auth_enabled(monkeypatch): """Test that AWXKIT_FORCE_BASIC_AUTH=true enables Basic authentication""" cli, mock_root, mock_connection = setup_basic_auth() - monkeypatch.setattr(config, 'force_basic_auth', True) + monkeypatch.setitem(config, 'force_basic_auth', True) cli.authenticate() mock_connection.login.assert_called_once_with('testuser', 'testpass') @@ -58,7 +58,7 @@ def test_basic_auth_enabled(monkeypatch): def test_session_auth_default(monkeypatch): """Test that session auth is used by default (backward compatibility)""" cli, mock_root, mock_load_session = setup_session_auth() - monkeypatch.setattr(config, 'force_basic_auth', False) + monkeypatch.setitem(config, 'force_basic_auth', False) cli.authenticate() mock_root.load_session.assert_called_once() @@ -71,7 +71,7 @@ def test_aap_gateway_scenario(monkeypatch): cli, mock_root, mock_connection = setup_basic_auth( ['awx', '--conf.host', 'https://aap-sbx.cambiahealth.com', '--conf.username', 'puretest', '--conf.password', 'testpass'] ) - monkeypatch.setattr(config, 'force_basic_auth', True) + monkeypatch.setitem(config, 'force_basic_auth', True) cli.authenticate() mock_connection.login.assert_called_once_with('puretest', 'testpass') @@ -82,7 +82,7 @@ def test_aap_gateway_scenario(monkeypatch): def test_empty_credentials_error(monkeypatch): """Test error handling for explicitly empty credentials""" cli, mock_root, mock_connection = setup_basic_auth(['awx', '--conf.username', '', '--conf.password', '']) - monkeypatch.setattr(config, 'force_basic_auth', True) + monkeypatch.setitem(config, 'force_basic_auth', True) with pytest.raises(ValueError, match="Basic authentication requires both username and password"): cli.authenticate() @@ -94,7 +94,7 @@ def test_connection_failure(monkeypatch): """Test error handling when Basic auth connection fails""" cli, mock_root, mock_connection = setup_basic_auth() mock_connection.login.side_effect = Exception("Connection failed") - monkeypatch.setattr(config, 'force_basic_auth', True) + monkeypatch.setitem(config, 'force_basic_auth', True) with pytest.raises(RuntimeError, match="Basic authentication failed: Connection failed"): cli.authenticate()