@@ -36,40 +36,63 @@ def mock_component(mock_tool):
3636 return component
3737
3838
39+ def _make_fake_tg (** overrides ):
40+ """Build a fake toolguard-imports dict for use with `_import_toolguard` patching.
41+
42+ Mirrors the keys returned by `PoliciesComponent._import_toolguard()`; tests
43+ can override individual entries to assert specific call wiring.
44+ """
45+ fake = {
46+ "PolicySpecOptions" : MagicMock (),
47+ "ToolGuardsCodeGenerationResult" : MagicMock (),
48+ "generate_guard_specs" : MagicMock (),
49+ "generate_guards_code" : MagicMock (),
50+ "langchain_tools_to_openapi" : MagicMock (),
51+ "load_toolguards" : MagicMock (),
52+ "load_toolguards_from_memory" : MagicMock (),
53+ "RESULTS_FILENAME" : "results.json" ,
54+ "sync_generated_guard_code_inputs" : MagicMock (),
55+ "GuardedTool" : MagicMock (),
56+ "LangchainModelWrapper" : MagicMock (),
57+ }
58+ fake .update (overrides )
59+ return fake
60+
61+
3962@pytest .mark .asyncio
4063async def test_cache_mode_success (mock_component , mock_tool ):
4164 """Test PoliciesComponent in cache mode with valid cached guards."""
4265 code_dir = mock_component .work_dir / STEP2
4366
67+ fake_tg = _make_fake_tg ()
68+ mock_tg_result = MagicMock ()
69+ mock_tg_runtime = MagicMock ()
70+ fake_tg ["load_toolguards_from_memory" ].return_value = mock_tg_runtime
71+ mock_guarded_instance = MagicMock ()
72+ fake_tg ["GuardedTool" ].return_value = mock_guarded_instance
73+
4474 # Mock the cache directory exists and toolguard loading
4575 with (
4676 patch .object (Path , "exists" , return_value = True ),
47- patch ( "lfx.components.models_and_agents.policies_component.load_toolguards" ) as mock_load_guards ,
77+ patch . object ( PoliciesComponent , "_import_toolguard" , return_value = fake_tg ) ,
4878 patch .object (mock_component , "make_toolguard_result" ) as mock_make_result ,
49- patch ("lfx.components.models_and_agents.policies_component.load_toolguards_from_memory" ) as mock_load_memory ,
50- patch ("lfx.components.models_and_agents.policies_component.GuardedTool" ) as mock_guarded_tool ,
5179 ):
52- mock_tg_result = MagicMock ()
5380 mock_make_result .return_value = mock_tg_result
54- mock_tg_runtime = MagicMock ()
55- mock_load_memory .return_value = mock_tg_runtime
56- mock_guarded_instance = MagicMock ()
57- mock_guarded_tool .return_value = mock_guarded_instance
5881
5982 result = await mock_component .guard_tools ()
6083
6184 # Verify load_toolguards was called during validation
62- mock_load_guards .assert_called_once_with (code_dir )
85+ fake_tg [ "load_toolguards" ] .assert_called_once_with (code_dir )
6386
6487 # Verify make_toolguard_result was called
6588 mock_make_result .assert_called_once ()
6689
6790 # Verify load_toolguards_from_memory was called with the result
68- mock_load_memory .assert_called_once_with (mock_tg_result )
91+ fake_tg [ "load_toolguards_from_memory" ] .assert_called_once_with (mock_tg_result )
6992
7093 # Verify GuardedTool was created for each tool
71- assert mock_guarded_tool .call_count == len (mock_component .in_tools )
72- mock_guarded_tool .assert_called_with (mock_tool , mock_component .in_tools , mock_tg_runtime )
94+ assert fake_tg [ "GuardedTool" ] .call_count == len (mock_component .in_tools )
95+ fake_tg [ "GuardedTool" ] .assert_called_with (mock_tool , mock_component .in_tools , mock_tg_runtime )
7396
7497 # Verify result contains guarded tools
7598 assert len (result ) == 1
@@ -79,9 +102,11 @@ async def test_cache_mode_success(mock_component, mock_tool):
79102@pytest .mark .asyncio
80103async def test_cache_mode_directory_not_found (mock_component ):
81104 """Test PoliciesComponent in cache mode when cache directory doesn't exist."""
105+ fake_tg = _make_fake_tg ()
82106 # Mock the cache directory does not exist
83107 with (
84108 patch .object (Path , "exists" , return_value = False ),
109+ patch .object (PoliciesComponent , "_import_toolguard" , return_value = fake_tg ),
85110 pytest .raises (ValueError , match = "Cache directory not found" ),
86111 ):
87112 await mock_component .guard_tools ()
@@ -90,29 +115,29 @@ async def test_cache_mode_directory_not_found(mock_component):
90115@pytest .mark .asyncio
91116async def test_cache_mode_file_not_found (mock_component ):
92117 """Test PoliciesComponent in cache mode when required files are missing."""
118+ fake_tg = _make_fake_tg ()
119+ fake_tg ["load_toolguards" ].side_effect = FileNotFoundError ("Guard file not found" )
93120 # Mock the cache directory exists but files are missing
94121 with (
95122 patch .object (Path , "exists" , return_value = True ),
96- patch ("lfx.components.models_and_agents.policies_component.load_toolguards" ) as mock_load_guards ,
123+ patch .object (PoliciesComponent , "_import_toolguard" , return_value = fake_tg ),
124+ pytest .raises (ValueError , match = "Required guard code files missing" ),
97125 ):
98- mock_load_guards .side_effect = FileNotFoundError ("Guard file not found" )
99-
100- with pytest .raises (ValueError , match = "Required guard code files missing" ):
101- await mock_component .guard_tools ()
126+ await mock_component .guard_tools ()
102127
103128
104129@pytest .mark .asyncio
105130async def test_cache_mode_corrupted_cache (mock_component ):
106131 """Test PoliciesComponent in cache mode when cached code is corrupted."""
132+ fake_tg = _make_fake_tg ()
133+ fake_tg ["load_toolguards" ].side_effect = Exception ("Invalid Python syntax" )
107134 # Mock the cache directory exists but code is corrupted
108135 with (
109136 patch .object (Path , "exists" , return_value = True ),
110- patch ("lfx.components.models_and_agents.policies_component.load_toolguards" ) as mock_load_guards ,
137+ patch .object (PoliciesComponent , "_import_toolguard" , return_value = fake_tg ),
138+ pytest .raises (ValueError , match = "Failed to load guard code" ),
111139 ):
112- mock_load_guards .side_effect = Exception ("Invalid Python syntax" )
113-
114- with pytest .raises (ValueError , match = "Failed to load guard code" ):
115- await mock_component .guard_tools ()
140+ await mock_component .guard_tools ()
116141
117142
118143# @pytest.mark.asyncio
@@ -163,29 +188,31 @@ async def test_inenabled_returns_original_tools(mock_component, mock_tool):
163188async def test_generate_mode_validation_errors (mock_component ):
164189 """Test PoliciesComponent in generate mode with validation errors."""
165190 mock_component .mode = MODE_GENERATE
191+ fake_tg = _make_fake_tg ()
166192
167- # Test empty project
168- mock_component .project = ""
169- with pytest .raises (ValueError ): # noqa: PT011
170- await mock_component .guard_tools ()
193+ with patch .object (PoliciesComponent , "_import_toolguard" , return_value = fake_tg ):
194+ # Test empty project
195+ mock_component .project = ""
196+ with pytest .raises (ValueError ): # noqa: PT011
197+ await mock_component .guard_tools ()
171198
172- # Test empty policies
173- mock_component .project = "test_project"
174- mock_component .policies = []
175- with pytest .raises (ValueError , match = "policies cannot be empty" ):
176- await mock_component .guard_tools ()
199+ # Test empty policies
200+ mock_component .project = "test_project"
201+ mock_component .policies = []
202+ with pytest .raises (ValueError , match = "policies cannot be empty" ):
203+ await mock_component .guard_tools ()
177204
178- # Test empty tools
179- mock_component .policies = ["Policy 1" ]
180- mock_component .in_tools = []
181- with pytest .raises (ValueError , match = "in_tools cannot be empty" ):
182- await mock_component .guard_tools ()
205+ # Test empty tools
206+ mock_component .policies = ["Policy 1" ]
207+ mock_component .in_tools = []
208+ with pytest .raises (ValueError , match = "in_tools cannot be empty" ):
209+ await mock_component .guard_tools ()
183210
184- # Test missing model
185- mock_component .in_tools = [MagicMock ()]
186- mock_component .model = None
187- with pytest .raises (ValueError , match = "model or api_key cannot be empty" ):
188- await mock_component .guard_tools ()
211+ # Test missing model
212+ mock_component .in_tools = [MagicMock ()]
213+ mock_component .model = None
214+ with pytest .raises (ValueError , match = "model or api_key cannot be empty" ):
215+ await mock_component .guard_tools ()
189216
190217 # # Test non-recommended model
191218 # mock_component.model = [{"name": "gpt-3.5-turbo", "provider": "OpenAI"}]
@@ -250,32 +277,36 @@ async def test_verify_cached_guards_error_messages(mock_component):
250277 code_dir = mock_component .work_dir / STEP2
251278
252279 # Test directory not found error message
253- with patch .object (Path , "exists" , return_value = False ):
280+ fake_tg = _make_fake_tg ()
281+ with (
282+ patch .object (Path , "exists" , return_value = False ),
283+ patch .object (PoliciesComponent , "_import_toolguard" , return_value = fake_tg ),
284+ ):
254285 with pytest .raises (ValueError , match = "Cache directory not found" ) as exc_info :
255286 mock_component ._verify_cached_guards (code_dir )
256287
257288 assert "Generate" in str (exc_info .value )
258289 assert str (code_dir ) in str (exc_info .value )
259290
260291 # Test file not found error message
292+ fake_tg = _make_fake_tg ()
293+ fake_tg ["load_toolguards" ].side_effect = FileNotFoundError ("Missing file" )
261294 with (
262295 patch .object (Path , "exists" , return_value = True ),
263- patch ( "lfx.components.models_and_agents.policies_component.load_toolguards" ) as mock_load ,
296+ patch . object ( PoliciesComponent , "_import_toolguard" , return_value = fake_tg ) ,
264297 ):
265- mock_load .side_effect = FileNotFoundError ("Missing file" )
266-
267298 with pytest .raises (ValueError , match = "Required guard code files missing" ) as exc_info :
268299 mock_component ._verify_cached_guards (code_dir )
269300
270301 assert "Generate" in str (exc_info .value )
271302
272303 # Test general error message
304+ fake_tg = _make_fake_tg ()
305+ fake_tg ["load_toolguards" ].side_effect = RuntimeError ("Unexpected error" )
273306 with (
274307 patch .object (Path , "exists" , return_value = True ),
275- patch ( "lfx.components.models_and_agents.policies_component.load_toolguards" ) as mock_load ,
308+ patch . object ( PoliciesComponent , "_import_toolguard" , return_value = fake_tg ) ,
276309 ):
277- mock_load .side_effect = RuntimeError ("Unexpected error" )
278-
279310 with pytest .raises (ValueError , match = "Failed to load guard code" ) as exc_info :
280311 mock_component ._verify_cached_guards (code_dir )
281312
0 commit comments