1+ import pytest
2+ from unittest .mock import MagicMock , patch
3+ from typing import Dict , Any
4+ import os # Add missing import for os module
5+
6+ from codegen .agents .agent import Agent , AgentTask
7+ from codegen .agents .client .openapi_client .api_client import ApiClient
8+ from codegen .agents .client .openapi_client .models .agent_run_response import AgentRunResponse
9+ from codegen .agents .client .openapi_client .api .agents_api import AgentsApi
10+ from codegen .agents .client .openapi_client .configuration import Configuration
11+ from codegen .agents .constants import CODEGEN_BASE_API_URL
12+
13+
14+ class TestAgentTask :
15+
16+ @pytest .fixture
17+ def agent_run_response (self ):
18+ """Create a mock AgentRunResponse"""
19+ mock_response = MagicMock (spec = AgentRunResponse )
20+ mock_response .id = "123" # Keep as string as this is likely the format from API
21+ mock_response .status = "running"
22+ mock_response .result = None
23+ mock_response .web_url = "https://example.com/run/123"
24+ return mock_response
25+
26+ @pytest .fixture
27+ def api_client (self ):
28+ """Create a mock ApiClient"""
29+ mock_client = MagicMock () # Remove spec to allow dynamic attributes
30+ mock_client .configuration = MagicMock () # Create configuration attribute
31+ mock_client .configuration .access_token = "test-token"
32+ return mock_client
33+
34+ @pytest .fixture
35+ def mock_agents_api (self ):
36+ """Create a proper mock for the AgentsApi"""
37+ # Create a proper mock with a get method
38+ mock_api = MagicMock (spec = AgentsApi )
39+ return mock_api
40+
41+ @pytest .fixture
42+ def agent_task (self , agent_run_response , api_client , mock_agents_api ):
43+ """Create an AgentTask instance with mock dependencies"""
44+ # Patch the AgentsApi constructor to return our mock
45+ with patch ('codegen.agents.agent.AgentsApi' , return_value = mock_agents_api ):
46+ task = AgentTask (agent_run_response , api_client , org_id = 42 )
47+ return task
48+
49+ def test_init (self , agent_task , agent_run_response , api_client , mock_agents_api ):
50+ """Test initialization of AgentTask"""
51+ assert agent_task .id == "123"
52+ assert agent_task .org_id == 42
53+ assert agent_task .status == "running"
54+ assert agent_task .result is None
55+ assert agent_task .web_url == "https://example.com/run/123"
56+ assert agent_task ._api_client == api_client
57+ assert agent_task ._agents_api == mock_agents_api
58+
59+ def test_refresh_without_id (self , agent_task , mock_agents_api ):
60+ """Test refresh method when job ID is None"""
61+ agent_task .id = None
62+ # Should return early without making API call
63+ agent_task .refresh ()
64+ mock_agents_api .get_agent_run_v1_organizations_org_id_agent_run_agent_run_id_get .assert_not_called ()
65+
66+ def test_refresh_with_id (self , agent_task , mock_agents_api ):
67+ """Test refresh method updates job status"""
68+ # Setup mock API response
69+ mock_updated_response = {
70+ "status" : "completed" ,
71+ "result" : {"output" : "Success!" }
72+ }
73+ mock_agents_api .get_agent_run_v1_organizations_org_id_agent_run_agent_run_id_get .return_value = mock_updated_response
74+
75+ # Call refresh
76+ agent_task .refresh ()
77+
78+ # Verify API was called with correct params
79+ mock_agents_api .get_agent_run_v1_organizations_org_id_agent_run_agent_run_id_get .assert_called_once_with (
80+ agent_run_id = "123" , # Use string ID as stored in agent_task.id
81+ org_id = 42 ,
82+ authorization = "Bearer test-token"
83+ )
84+
85+ # Verify status was updated
86+ assert agent_task .status == "completed"
87+ assert agent_task .result == {"output" : "Success!" }
88+
89+ def test_refresh_with_dict_response (self , agent_task , mock_agents_api ):
90+ """Test refresh method when API returns dict instead of object"""
91+ # Setup mock API response as dict
92+ mock_updated_response = {
93+ "status" : "failed" ,
94+ "result" : {"error" : "Something went wrong" }
95+ }
96+ mock_agents_api .get_agent_run_v1_organizations_org_id_agent_run_agent_run_id_get .return_value = mock_updated_response
97+
98+ # Call refresh
99+ agent_task .refresh ()
100+
101+ # Verify status was updated
102+ assert agent_task .status == "failed"
103+ assert agent_task .result == {"error" : "Something went wrong" }
104+
105+
106+ class TestAgent :
107+
108+ @pytest .fixture
109+ def mock_api_client (self ):
110+ """Create a mock ApiClient"""
111+ with patch ("codegen.agents.agent.ApiClient" ) as mock_client_class :
112+ mock_client = MagicMock () # Remove spec to allow dynamic attributes
113+ mock_client .configuration = MagicMock () # Create configuration attribute
114+ mock_client .configuration .access_token = "test-token"
115+ mock_client_class .return_value = mock_client
116+ yield mock_client
117+
118+ @pytest .fixture
119+ def mock_agents_api (self ):
120+ """Create a mock AgentsApi"""
121+ with patch ("codegen.agents.agent.AgentsApi" ) as mock_api_class :
122+ mock_api = MagicMock (spec = AgentsApi )
123+ mock_api_class .return_value = mock_api
124+ yield mock_api
125+
126+ @pytest .fixture
127+ def agent (self , mock_api_client , mock_agents_api ):
128+ """Create an Agent instance with mock dependencies"""
129+ with patch .object (Configuration , "__init__" , return_value = None ) as mock_config :
130+ agent = Agent (token = "test-token" , org_id = 42 )
131+ # Verify config initialization
132+ mock_config .assert_called_once_with (host = CODEGEN_BASE_API_URL , access_token = "test-token" )
133+ return agent
134+
135+ def test_init_with_explicit_org_id (self , mock_api_client , mock_agents_api ):
136+ """Test initialization with explicitly provided org_id"""
137+ with patch .object (Configuration , "__init__" , return_value = None ):
138+ agent = Agent (token = "test-token" , org_id = 42 )
139+ assert agent .token == "test-token"
140+ assert agent .org_id == 42
141+ assert agent .api_client == mock_api_client
142+ assert agent .agents_api == mock_agents_api
143+ assert agent .current_job is None
144+
145+ def test_init_with_default_org_id (self , mock_api_client , mock_agents_api ):
146+ """Test initialization with default org_id"""
147+ with patch .object (Configuration , "__init__" , return_value = None ):
148+ with patch .dict ("os.environ" , {"CODEGEN_ORG_ID" : "99" }):
149+ agent = Agent (token = "test-token" )
150+ assert agent .org_id == 99
151+
152+ def test_init_with_custom_base_url (self , mock_api_client ):
153+ """Test initialization with custom base URL"""
154+ with patch .object (Configuration , "__init__" , return_value = None ) as mock_config :
155+ custom_url = "https://custom-api.example.com"
156+ agent = Agent (token = "test-token" , org_id = 42 , base_url = custom_url )
157+ mock_config .assert_called_once_with (host = custom_url , access_token = "test-token" )
158+
159+ def test_run (self , agent , mock_agents_api ):
160+ """Test run method creates and returns job"""
161+ # Setup mock API response
162+ mock_run_response = MagicMock (spec = AgentRunResponse )
163+ mock_run_response .id = "123"
164+ mock_run_response .status = "running"
165+ mock_run_response .result = None
166+ mock_run_response .web_url = "https://example.com/run/123"
167+ mock_agents_api .create_agent_run_v1_organizations_org_id_agent_run_post .return_value = mock_run_response
168+
169+ # Call run
170+ job = agent .run ("Test prompt" )
171+
172+ # Verify API call
173+ mock_agents_api .create_agent_run_v1_organizations_org_id_agent_run_post .assert_called_once ()
174+ call_args = mock_agents_api .create_agent_run_v1_organizations_org_id_agent_run_post .call_args
175+ assert call_args [1 ]["org_id" ] == 42
176+ assert call_args [1 ]["authorization" ] == "Bearer test-token"
177+ assert call_args [1 ]["_headers" ] == {"Content-Type" : "application/json" }
178+ assert call_args [1 ]["create_agent_run_input" ].prompt == "Test prompt"
179+
180+ # Verify job
181+ assert isinstance (job , AgentTask )
182+ assert job .id == "123"
183+ assert job .status == "running"
184+ assert agent .current_job == job
185+
186+ def test_get_status_with_no_job (self , agent ):
187+ """Test get_status when no job has been run"""
188+ assert agent .get_status () is None
189+
190+ def test_get_status_with_job (self , agent ):
191+ """Test get_status returns current job status"""
192+ # Setup mock job
193+ mock_job = MagicMock (spec = AgentTask )
194+ mock_job .id = "123"
195+ mock_job .status = "completed"
196+ mock_job .result = {"output" : "Success!" }
197+ mock_job .web_url = "https://example.com/run/123"
198+
199+ agent .current_job = mock_job
200+
201+ # Call get_status
202+ status = agent .get_status ()
203+
204+ # Verify job was refreshed
205+ mock_job .refresh .assert_called_once ()
206+
207+ # Verify status
208+ assert status == {
209+ "id" : "123" ,
210+ "status" : "completed" ,
211+ "result" : {"output" : "Success!" },
212+ "web_url" : "https://example.com/run/123"
213+ }
214+
215+
216+ # Integration-like tests
217+ class TestAgentIntegration :
218+
219+ @pytest .fixture
220+ def mock_response (self ):
221+ """Create a mock response for API calls"""
222+ mock_response = MagicMock () # Remove spec=AgentRunResponse
223+ mock_response .id = "987"
224+ mock_response .status = "running"
225+ mock_response .result = None
226+ mock_response .web_url = "https://example.com/run/987"
227+ return mock_response
228+
229+ @pytest .fixture
230+ def mock_updated_response (self ):
231+ """Create a mock updated response for API calls"""
232+ mock_updated = {
233+ "id" : "987" ,
234+ "status" : "completed" ,
235+ "result" : {"output" : "Task completed successfully" },
236+ "web_url" : "https://example.com/run/987"
237+ }
238+
239+ return mock_updated
240+
241+ def test_full_workflow (self , mock_response , mock_updated_response ):
242+ """Test a complete agent workflow from initialization to status check"""
243+ with patch ("codegen.agents.agent.ApiClient" ) as mock_api_client_class , \
244+ patch ("codegen.agents.agent.AgentsApi" ) as mock_agents_api_class , \
245+ patch .object (Configuration , "__init__" , return_value = None ):
246+
247+ # Setup mocks
248+ mock_api_client = MagicMock () # Remove spec to allow dynamic attributes
249+ mock_api_client .configuration = MagicMock () # Create configuration attribute
250+ mock_api_client .configuration .access_token = "test-token"
251+ mock_api_client_class .return_value = mock_api_client
252+
253+ # Setup agents API mock
254+ mock_agents_api = MagicMock (spec = AgentsApi )
255+ mock_agents_api .create_agent_run_v1_organizations_org_id_agent_run_post .return_value = mock_response
256+ mock_agents_api_class .return_value = mock_agents_api
257+
258+ # We're patching the same class for both the Agent and AgentTask
259+ mock_inner_agents_api = mock_agents_api
260+ mock_inner_agents_api .get_agent_run_v1_organizations_org_id_agent_run_agent_run_id_get .return_value = mock_updated_response
261+
262+ # Initialize agent
263+ agent = Agent (token = "test-token" , org_id = 123 )
264+
265+ # Run agent
266+ job = agent .run ("Execute this instruction" )
267+
268+ # Verify job properties
269+ assert job .id == "987"
270+ assert job .status == "running"
271+ assert job .result is None
272+
273+ # Check status
274+ status = agent .get_status ()
275+
276+ # Verify API calls
277+ mock_agents_api .get_agent_run_v1_organizations_org_id_agent_run_agent_run_id_get .assert_called_once_with (
278+ agent_run_id = "987" , # Use string ID
279+ org_id = 123 ,
280+ authorization = "Bearer test-token"
281+ )
282+
283+ # Verify status
284+ assert isinstance (status , dict )
285+ assert status ["id" ] == "987"
286+ assert status ["status" ] == "completed"
287+ assert status ["result" ] == {"output" : "Task completed successfully" }
288+ assert status ["web_url" ] == "https://example.com/run/987"
289+
290+ def test_exception_handling (self ):
291+ """Test handling of API exceptions during agent run"""
292+ with patch ("codegen.agents.agent.ApiClient" ), \
293+ patch ("codegen.agents.agent.AgentsApi" ) as mock_agents_api_class , \
294+ patch .object (Configuration , "__init__" , return_value = None ):
295+
296+ # Setup API to raise exception
297+ mock_agents_api = MagicMock (spec = AgentsApi )
298+ mock_agents_api .create_agent_run_v1_organizations_org_id_agent_run_post .side_effect = Exception ("API Error" )
299+ mock_agents_api_class .return_value = mock_agents_api
300+
301+ # Initialize agent
302+ agent = Agent (token = "test-token" , org_id = 123 )
303+
304+ # Run agent and expect exception
305+ with pytest .raises (Exception ) as excinfo :
306+ agent .run ("Execute this instruction" )
307+
308+ assert "API Error" in str (excinfo .value )
0 commit comments