66
77import os
88import sys
9- from unittest .mock import MagicMock
9+ from contextlib import contextmanager
10+ from unittest .mock import MagicMock , patch
1011
1112
1213# --- Mock OpenStack dependencies before any test file imports instanceha ---
1314
1415if 'novaclient' not in sys .modules :
15- sys .modules ['novaclient' ] = MagicMock ()
16- sys .modules ['novaclient.client' ] = MagicMock ()
16+ _novaclient_mock = MagicMock ()
17+ _novaclient_client_mock = MagicMock ()
18+ _novaclient_mock .client = _novaclient_client_mock
19+ sys .modules ['novaclient' ] = _novaclient_mock
20+ sys .modules ['novaclient.client' ] = _novaclient_client_mock
1721
1822 class NotFound (Exception ):
1923 pass
@@ -32,12 +36,18 @@ class Unauthorized(Exception):
3236 novaclient_exceptions .Conflict = Conflict
3337 novaclient_exceptions .Forbidden = Forbidden
3438 novaclient_exceptions .Unauthorized = Unauthorized
39+ _novaclient_mock .exceptions = novaclient_exceptions
3540 sys .modules ['novaclient.exceptions' ] = novaclient_exceptions
3641
3742if 'keystoneauth1' not in sys .modules :
38- sys .modules ['keystoneauth1' ] = MagicMock ()
39- sys .modules ['keystoneauth1.loading' ] = MagicMock ()
40- sys .modules ['keystoneauth1.session' ] = MagicMock ()
43+ _keystoneauth_mock = MagicMock ()
44+ _loading_mock = MagicMock ()
45+ _session_mock = MagicMock ()
46+ _keystoneauth_mock .loading = _loading_mock
47+ _keystoneauth_mock .session = _session_mock
48+ sys .modules ['keystoneauth1' ] = _keystoneauth_mock
49+ sys .modules ['keystoneauth1.loading' ] = _loading_mock
50+ sys .modules ['keystoneauth1.session' ] = _session_mock
4151
4252 class DiscoveryFailure (Exception ):
4353 pass
@@ -48,6 +58,7 @@ class DiscoveryFailure(Exception):
4858 exceptions_module = MagicMock ()
4959 exceptions_module .discovery = discovery_module
5060
61+ _keystoneauth_mock .exceptions = exceptions_module
5162 sys .modules ['keystoneauth1.exceptions' ] = exceptions_module
5263 sys .modules ['keystoneauth1.exceptions.discovery' ] = discovery_module
5364
@@ -59,3 +70,32 @@ class DiscoveryFailure(Exception):
5970_abs_path = os .path .abspath (_instanceha_path )
6071if _abs_path not in sys .path :
6172 sys .path .insert (0 , _abs_path )
73+
74+
75+ # --- Shared test fixtures ---
76+
77+ import instanceha # noqa: E402
78+
79+
80+ @contextmanager
81+ def patch_pipeline (conn = None , fence = True , disable = True ,
82+ reserved = None , evacuate = True , recovery = True ):
83+ """Patch all process_service pipeline steps with configurable return values.
84+
85+ Yields a dict of mock objects keyed by step name, so tests can assert
86+ on individual steps (e.g. mocks['disable'].assert_not_called()).
87+ """
88+ if reserved is None :
89+ reserved = instanceha .ReservedHostResult (success = True , hostname = None )
90+ with patch ('instanceha._get_nova_connection' , return_value = conn ) as m_conn , \
91+ patch ('instanceha._host_fence' , return_value = fence ) as m_fence , \
92+ patch ('instanceha._host_disable' , return_value = disable ) as m_disable , \
93+ patch ('instanceha._manage_reserved_hosts' , return_value = reserved ) as m_reserved , \
94+ patch ('instanceha._host_evacuate' , return_value = evacuate ) as m_evacuate , \
95+ patch ('instanceha._post_evacuation_recovery' , return_value = recovery ) as m_recovery , \
96+ patch ('instanceha.track_host_processing' ) as m_track :
97+ yield {
98+ 'conn' : m_conn , 'fence' : m_fence , 'disable' : m_disable ,
99+ 'reserved' : m_reserved , 'evacuate' : m_evacuate ,
100+ 'recovery' : m_recovery , 'track' : m_track ,
101+ }
0 commit comments