Skip to content

Commit d67119e

Browse files
committed
Update backend component unit tests
1 parent fcd79dc commit d67119e

File tree

8 files changed

+1912
-741
lines changed

8 files changed

+1912
-741
lines changed

tests/unit/backend/test_backend.py

Lines changed: 283 additions & 130 deletions
Large diffs are not rendered by default.

tests/unit/backend/test_interface.py

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
"""
2+
Unit tests for the BackendInterface abstract class.
3+
4+
### WRITTEN BY AI ###
5+
"""
6+
7+
from typing import Any, Optional
8+
9+
import pytest
10+
11+
from guidellm.backend.interface import BackendInterface
12+
13+
14+
class TestBackendInterface:
15+
"""Test cases for BackendInterface abstract class."""
16+
17+
@pytest.mark.sanity
18+
def test_backend_interface_properties_are_abstract(self):
19+
"""Test that required properties are abstract.
20+
21+
### WRITTEN BY AI ###
22+
"""
23+
24+
# Create a partial implementation to verify abstract nature
25+
class PartialBackend(BackendInterface):
26+
# Missing required properties/methods
27+
pass
28+
29+
with pytest.raises(TypeError):
30+
PartialBackend()
31+
32+
@pytest.mark.sanity
33+
def test_minimal_concrete_implementation(self):
34+
"""Test that a minimal concrete implementation can be created.
35+
36+
### WRITTEN BY AI ###
37+
"""
38+
39+
class MinimalBackend(BackendInterface):
40+
@property
41+
def processes_limit(self) -> Optional[int]:
42+
return None
43+
44+
@property
45+
def requests_limit(self) -> Optional[int]:
46+
return None
47+
48+
def info(self) -> dict[str, Any]:
49+
return {}
50+
51+
async def process_startup(self) -> None:
52+
pass
53+
54+
async def validate(self) -> None:
55+
pass
56+
57+
async def process_shutdown(self) -> None:
58+
pass
59+
60+
async def resolve(self, request, request_info, history=None):
61+
yield request, request_info
62+
63+
# Should be able to instantiate
64+
backend = MinimalBackend()
65+
assert backend is not None
66+
assert isinstance(backend, BackendInterface)
67+
68+
@pytest.mark.regression
69+
def test_backend_interface_method_signatures(self):
70+
"""Test that BackendInterface methods have correct signatures.
71+
72+
### WRITTEN BY AI ###
73+
"""
74+
import inspect
75+
76+
# Check resolve method signature
77+
resolve_sig = inspect.signature(BackendInterface.resolve)
78+
params = list(resolve_sig.parameters.keys())
79+
80+
expected_params = ["self", "request", "request_info", "history"]
81+
assert params == expected_params
82+
83+
# Check that history has default value
84+
history_param = resolve_sig.parameters["history"]
85+
assert history_param.default is None
Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
"""
2+
Unit tests for the MockBackend implementation.
3+
4+
### WRITTEN BY AI ###
5+
"""
6+
7+
import pytest
8+
9+
from guidellm.backend import Backend
10+
from guidellm.backend.objects import GenerationRequest, GenerationRequestTimings
11+
from guidellm.scheduler import ScheduledRequestInfo
12+
from tests.unit.mock_backend import MockBackend
13+
14+
15+
class TestMockBackend:
16+
"""Test cases for MockBackend."""
17+
18+
@pytest.mark.smoke
19+
def test_mock_backend_creation(self):
20+
"""Test MockBackend can be created.
21+
22+
### WRITTEN BY AI ###
23+
"""
24+
backend = MockBackend()
25+
assert backend.type_ == "mock"
26+
assert backend.model == "mock-model"
27+
assert backend.target == "mock-target"
28+
29+
@pytest.mark.smoke
30+
def test_mock_backend_registration(self):
31+
"""Test MockBackend is properly registered.
32+
33+
### WRITTEN BY AI ###
34+
"""
35+
backend = Backend.create("mock")
36+
assert isinstance(backend, MockBackend)
37+
assert backend.type_ == "mock"
38+
39+
@pytest.mark.smoke
40+
def test_mock_backend_info(self):
41+
"""Test MockBackend info method.
42+
43+
### WRITTEN BY AI ###
44+
"""
45+
backend = MockBackend(model="test-model", target="test-target")
46+
info = backend.info()
47+
48+
assert info["type"] == "mock"
49+
assert info["model"] == "test-model"
50+
assert info["target"] == "test-target"
51+
52+
@pytest.mark.sanity
53+
@pytest.mark.asyncio
54+
async def test_mock_backend_lifecycle(self):
55+
"""Test MockBackend process lifecycle.
56+
57+
### WRITTEN BY AI ###
58+
"""
59+
backend = MockBackend()
60+
61+
# Test startup
62+
await backend.process_startup()
63+
assert backend._in_process is True
64+
65+
# Test validation
66+
await backend.validate() # Should not raise
67+
68+
# Test default model
69+
model = await backend.default_model()
70+
assert model == "mock-model"
71+
72+
# Test shutdown
73+
await backend.process_shutdown()
74+
assert backend._in_process is False
75+
76+
@pytest.mark.sanity
77+
@pytest.mark.asyncio
78+
async def test_mock_backend_validate_not_started(self):
79+
"""Test validation fails when backend not started.
80+
81+
### WRITTEN BY AI ###
82+
"""
83+
backend = MockBackend()
84+
85+
with pytest.raises(RuntimeError, match="Backend not started up"):
86+
await backend.validate()
87+
88+
@pytest.mark.regression
89+
@pytest.mark.asyncio
90+
async def test_mock_backend_resolve(self):
91+
"""Test MockBackend resolve method.
92+
93+
### WRITTEN BY AI ###
94+
"""
95+
backend = MockBackend(iter_delay=0.001) # Small delay for testing
96+
await backend.process_startup()
97+
98+
try:
99+
request = GenerationRequest(
100+
request_id="test-id",
101+
content="Test prompt",
102+
constraints={"output_tokens": 3},
103+
)
104+
request_info = ScheduledRequestInfo(
105+
request_id="test-id",
106+
status="pending",
107+
scheduler_node_id=1,
108+
scheduler_process_id=1,
109+
scheduler_start_time=123.0,
110+
request_timings=GenerationRequestTimings(),
111+
)
112+
113+
responses = []
114+
async for response, info in backend.resolve(request, request_info):
115+
responses.append((response, info))
116+
117+
# Should get multiple responses (one per token + final)
118+
assert len(responses) >= 2
119+
120+
# Check final response
121+
final_response = responses[-1][0]
122+
assert final_response.request_id == "test-id"
123+
assert final_response.iterations > 0
124+
assert len(final_response.value) > 0
125+
assert final_response.delta is None # Final response has no delta
126+
127+
# Check timing information
128+
final_info = responses[-1][1]
129+
assert final_info.request_timings.request_start is not None
130+
assert final_info.request_timings.request_end is not None
131+
assert final_info.request_timings.first_iteration is not None
132+
assert final_info.request_timings.last_iteration is not None
133+
134+
finally:
135+
await backend.process_shutdown()
136+
137+
@pytest.mark.regression
138+
@pytest.mark.asyncio
139+
async def test_mock_backend_resolve_not_started(self):
140+
"""Test resolve fails when backend not started.
141+
142+
### WRITTEN BY AI ###
143+
"""
144+
backend = MockBackend()
145+
146+
request = GenerationRequest(content="test")
147+
request_info = ScheduledRequestInfo(
148+
request_id="test",
149+
status="pending",
150+
scheduler_node_id=1,
151+
scheduler_process_id=1,
152+
scheduler_start_time=123.0,
153+
request_timings=GenerationRequestTimings(),
154+
)
155+
156+
with pytest.raises(RuntimeError, match="Backend not started up"):
157+
async for _ in backend.resolve(request, request_info):
158+
pass
159+
160+
@pytest.mark.regression
161+
@pytest.mark.asyncio
162+
async def test_mock_backend_resolve_with_history(self):
163+
"""Test resolve method raises error with conversation history.
164+
165+
### WRITTEN BY AI ###
166+
"""
167+
backend = MockBackend()
168+
await backend.process_startup()
169+
170+
try:
171+
request = GenerationRequest(content="test")
172+
request_info = ScheduledRequestInfo(
173+
request_id="test",
174+
status="pending",
175+
scheduler_node_id=1,
176+
scheduler_process_id=1,
177+
scheduler_start_time=123.0,
178+
request_timings=GenerationRequestTimings(),
179+
)
180+
history = [(request, None)] # Mock history
181+
182+
with pytest.raises(
183+
NotImplementedError, match="Multi-turn requests not supported"
184+
):
185+
async for _ in backend.resolve(request, request_info, history):
186+
pass
187+
finally:
188+
await backend.process_shutdown()
189+
190+
@pytest.mark.regression
191+
def test_mock_backend_token_generation(self):
192+
"""Test token generation methods.
193+
194+
### WRITTEN BY AI ###
195+
"""
196+
# Test with specific token count
197+
tokens = MockBackend._get_tokens(5)
198+
assert len(tokens) == 5
199+
assert tokens[-1] == "." # Should end with period
200+
201+
# Test with None (random count)
202+
tokens_random = MockBackend._get_tokens(None)
203+
assert len(tokens_random) >= 8
204+
assert len(tokens_random) <= 512
205+
206+
# Test prompt token estimation
207+
estimated = MockBackend._estimate_prompt_tokens("hello world test")
208+
assert estimated == 3 # Three words

0 commit comments

Comments
 (0)