Skip to content

Commit ed9bd99

Browse files
committed
test(BA-2753): Add tests
1 parent 716085f commit ed9bd99

File tree

2 files changed

+182
-0
lines changed

2 files changed

+182
-0
lines changed

tests/agent/conftest.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@
55
import subprocess
66
from collections import defaultdict
77
from pathlib import Path
8+
from typing import AsyncIterator
89

910
import aiodocker
1011
import pytest
1112

1213
from ai.backend.agent.config.unified import AgentUnifiedConfig
14+
from ai.backend.agent.runtime import AgentRuntime
1315
from ai.backend.common import config
1416
from ai.backend.common import validators as tx
1517
from ai.backend.common.arch import DEFAULT_IMAGE_ARCH
@@ -271,3 +273,33 @@ async def _create_container(config):
271273
finally:
272274
if container is not None:
273275
await container.delete(force=True)
276+
277+
278+
@pytest.fixture
279+
async def agent_runtime(
280+
local_config: AgentUnifiedConfig,
281+
etcd,
282+
mocker,
283+
) -> AsyncIterator[AgentRuntime]:
284+
"""
285+
Create a real AgentRuntime instance for integration testing.
286+
287+
This fixture provides a fully initialized AgentRuntime with:
288+
- Real etcd client
289+
- Real agent configuration
290+
- Mocked stats and error monitors (external dependencies)
291+
- Proper cleanup after tests
292+
"""
293+
from unittest.mock import Mock
294+
295+
mock_stats_monitor = Mock()
296+
mock_error_monitor = Mock()
297+
298+
runtime = AgentRuntime(local_config, etcd)
299+
300+
await runtime.create_agents(mock_stats_monitor, mock_error_monitor, None)
301+
302+
try:
303+
yield runtime
304+
finally:
305+
await runtime.__aexit__(None, None, None)

tests/agent/test_agent_runtime.py

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
from __future__ import annotations
2+
3+
from typing import TYPE_CHECKING
4+
5+
import pytest
6+
7+
from ai.backend.agent.errors.runtime import AgentIdNotFoundError
8+
from ai.backend.agent.runtime import AgentRuntime
9+
from ai.backend.common.types import AgentId
10+
11+
if TYPE_CHECKING:
12+
from ai.backend.agent.config.unified import AgentUnifiedConfig
13+
14+
15+
class TestAgentRuntimeSingleAgent:
16+
"""Test AgentRuntime with single agent configuration."""
17+
18+
@pytest.mark.asyncio
19+
async def test_get_agent_returns_default_when_id_is_none(
20+
self,
21+
agent_runtime: AgentRuntime,
22+
) -> None:
23+
"""
24+
When agent_id is None, get_agent() should return the default agent.
25+
"""
26+
agent = agent_runtime.get_agent(None)
27+
28+
assert agent is not None
29+
assert agent.id is not None
30+
# In single agent mode, the default agent should be the only agent
31+
assert agent is agent_runtime.get_agent(agent.id)
32+
33+
@pytest.mark.asyncio
34+
async def test_get_agent_returns_agent_by_id(
35+
self,
36+
agent_runtime: AgentRuntime,
37+
) -> None:
38+
"""
39+
get_agent() should return the correct agent when given a specific ID.
40+
"""
41+
# Get the default agent's ID
42+
default_agent = agent_runtime.get_agent(None)
43+
agent_id = default_agent.id
44+
45+
# Retrieve by ID
46+
agent = agent_runtime.get_agent(agent_id)
47+
48+
assert agent is default_agent
49+
assert agent.id == agent_id
50+
51+
@pytest.mark.asyncio
52+
async def test_get_agent_raises_error_for_nonexistent_id(
53+
self,
54+
agent_runtime: AgentRuntime,
55+
) -> None:
56+
"""
57+
get_agent() should raise AgentIdNotFoundError for non-existent agent IDs.
58+
"""
59+
nonexistent_id = AgentId("nonexistent-agent-id")
60+
61+
with pytest.raises(AgentIdNotFoundError) as exc_info:
62+
agent_runtime.get_agent(nonexistent_id)
63+
64+
# Verify error message is helpful
65+
assert str(nonexistent_id) in str(exc_info.value)
66+
assert "not found" in str(exc_info.value).lower()
67+
68+
@pytest.mark.asyncio
69+
async def test_get_agents_returns_all_agents(
70+
self,
71+
agent_runtime: AgentRuntime,
72+
) -> None:
73+
"""
74+
get_agents() should return a list of all agents.
75+
"""
76+
agents = agent_runtime.get_agents()
77+
78+
assert isinstance(agents, list)
79+
assert len(agents) == 1 # Single agent mode
80+
81+
# Verify the agent is accessible
82+
for agent in agents:
83+
assert agent.id is not None
84+
assert agent is agent_runtime.get_agent(agent.id)
85+
86+
87+
class TestAgentRuntimeInitialization:
88+
"""Test AgentRuntime initialization and cleanup."""
89+
90+
@pytest.mark.asyncio
91+
async def test_runtime_creates_agents_from_config(
92+
self,
93+
local_config: AgentUnifiedConfig,
94+
etcd,
95+
mocker,
96+
) -> None:
97+
"""
98+
AgentRuntime.create_agents() should initialize agents from config.
99+
"""
100+
from unittest.mock import Mock
101+
102+
mock_stats_monitor = Mock()
103+
mock_error_monitor = Mock()
104+
105+
runtime = AgentRuntime(local_config, etcd)
106+
await runtime.create_agents(mock_stats_monitor, mock_error_monitor, None)
107+
108+
try:
109+
# Verify agents were created
110+
agents = runtime.get_agents()
111+
assert len(agents) > 0
112+
113+
# Verify default agent is set
114+
default_agent = runtime.get_agent(None)
115+
assert default_agent is not None
116+
117+
# Verify all agents have valid IDs
118+
for agent in agents:
119+
assert agent.id is not None
120+
finally:
121+
await runtime.__aexit__(None, None, None)
122+
123+
@pytest.mark.asyncio
124+
async def test_runtime_shutdown_cleans_up_agents(
125+
self,
126+
local_config: AgentUnifiedConfig,
127+
etcd,
128+
mocker,
129+
) -> None:
130+
"""
131+
AgentRuntime.shutdown() should properly clean up all agents.
132+
"""
133+
from unittest.mock import Mock
134+
135+
mock_stats_monitor = Mock()
136+
mock_error_monitor = Mock()
137+
138+
runtime = AgentRuntime(local_config, etcd)
139+
await runtime.create_agents(mock_stats_monitor, mock_error_monitor, None)
140+
141+
# Verify agents exist before shutdown
142+
agents = runtime.get_agents()
143+
assert len(agents) > 0
144+
145+
# Shutdown
146+
await runtime.__aexit__(None, None, None)
147+
148+
# After shutdown, the runtime should be in a clean state
149+
# (Specific behavior depends on implementation - adjust as needed)
150+
# For now, we just verify shutdown doesn't raise errors

0 commit comments

Comments
 (0)