Spaces:
Running
Running
File size: 13,406 Bytes
fabaa34 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 | """
Unit tests for backend optimization modules.
Tests core functionality of:
- JSON helpers (parsing, normalization)
- Configuration (LLM settings, scoring)
- Context injection (memory handling)
- SSE streaming (event formatting)
- Graph nodes (coordinate definitions)
"""
import pytest
import json
from utils.json_helpers import (
sanitize_fenced_json,
extract_first_json_object,
load_json_object,
clamp_score,
normalize_text,
normalize_errors,
normalize_serious_mistakes,
)
from core.config import LLMConfig, ScoringConfig, SeverityConfig, AgentConfig
from services.context_injector import inject_memory_context, has_memory_context
from services.base_stream_service import format_sse_event, create_sse_event
from utils.graph_nodes import (
get_coding_node_coords,
get_deep_research_node_coords,
get_standard_node_coords,
)
# βββ JSON Helpers Tests ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
class TestJsonHelpers:
"""Tests for JSON parsing and normalization utilities."""
def test_sanitize_fenced_json_with_backticks(self):
"""Test removal of markdown code fences."""
raw = "```json\n{\"key\": \"value\"}\n```"
result = sanitize_fenced_json(raw)
assert result == "{\"key\": \"value\"}"
def test_sanitize_fenced_json_with_plain_backticks(self):
"""Test removal of plain backticks."""
raw = "```\n{\"key\": \"value\"}\n```"
result = sanitize_fenced_json(raw)
assert result == "{\"key\": \"value\"}"
def test_sanitize_fenced_json_no_fences(self):
"""Test passthrough when no fences present."""
raw = "{\"key\": \"value\"}"
result = sanitize_fenced_json(raw)
assert result == "{\"key\": \"value\"}"
def test_sanitize_fenced_json_empty(self):
"""Test empty input."""
assert sanitize_fenced_json("") == ""
assert sanitize_fenced_json(" ") == ""
def test_extract_first_json_object_simple(self):
"""Test extraction of simple JSON object."""
text = "prefix {\"key\": \"value\"} suffix"
result = extract_first_json_object(text)
assert result == "{\"key\": \"value\"}"
def test_extract_first_json_object_nested(self):
"""Test extraction with nested objects."""
text = 'text {"outer": {"inner": "value"}} more'
result = extract_first_json_object(text)
assert result == '{"outer": {"inner": "value"}}'
def test_extract_first_json_object_not_found(self):
"""Test when no JSON object exists."""
text = "no json here"
result = extract_first_json_object(text)
assert result == ""
def test_load_json_object_simple(self):
"""Test JSON loading from valid input."""
raw = '{"key": "value"}'
result = load_json_object(raw)
assert result == {"key": "value"}
def test_load_json_object_with_fences(self):
"""Test JSON loading from fenced input."""
raw = "```json\n{\"key\": \"value\"}\n```"
result = load_json_object(raw)
assert result == {"key": "value"}
def test_load_json_object_invalid(self):
"""Test error handling for invalid JSON."""
with pytest.raises(ValueError):
load_json_object("not json at all")
def test_load_json_object_non_dict(self):
"""Test error when JSON root is not an object."""
with pytest.raises(ValueError):
load_json_object("[1, 2, 3]")
def test_clamp_score_in_range(self):
"""Test score clamping within valid range."""
assert clamp_score(50, default=0) == 50
assert clamp_score(0, default=50) == 0
assert clamp_score(100, default=50) == 100
def test_clamp_score_below_min(self):
"""Test score clamping below minimum."""
assert clamp_score(-10, default=50) == 0
def test_clamp_score_above_max(self):
"""Test score clamping above maximum."""
assert clamp_score(150, default=50) == 100
def test_clamp_score_invalid(self):
"""Test default value on invalid input."""
assert clamp_score("invalid", default=50) == 50
assert clamp_score(None, default=75) == 75
def test_normalize_text_string(self):
"""Test string normalization."""
assert normalize_text(" hello ") == "hello"
assert normalize_text("hello") == "hello"
def test_normalize_text_non_string(self):
"""Test non-string value normalization."""
assert normalize_text(123) == "123"
assert normalize_text(None) == ""
def test_normalize_errors_string(self):
"""Test error list normalization from string."""
result = normalize_errors("error message")
assert result == ["error message"]
def test_normalize_errors_list(self):
"""Test error list normalization from list."""
result = normalize_errors(["error 1", "error 2"])
assert result == ["error 1", "error 2"]
def test_normalize_errors_empty_removed(self):
"""Test that empty error strings are removed."""
result = normalize_errors(["error", "", " ", "another"])
assert result == ["error", "another"]
def test_normalize_serious_mistakes_string(self):
"""Test mistakes normalization from list of strings."""
result = normalize_serious_mistakes(["bug found"])
assert len(result) == 1
assert result[0]["severity"] == "high"
assert result[0]["description"] == "bug found"
def test_normalize_serious_mistakes_dict(self):
"""Test mistakes normalization from dict."""
input_data = [
{"description": "logic error", "severity": "critical", "action": "fix loop"}
]
result = normalize_serious_mistakes(input_data)
assert len(result) == 1
assert result[0]["severity"] == "critical"
assert result[0]["description"] == "logic error"
assert result[0]["action"] == "fix loop"
def test_normalize_serious_mistakes_invalid_severity(self):
"""Test that invalid severity defaults to high."""
input_data = [{"description": "bug", "severity": "invalid"}]
result = normalize_serious_mistakes(input_data)
assert result[0]["severity"] == "high"
def test_normalize_serious_mistakes_empty_description_skipped(self):
"""Test that items with empty descriptions are skipped."""
input_data = [
{"description": "", "severity": "high"},
{"description": "real error", "severity": "high"},
]
result = normalize_serious_mistakes(input_data)
assert len(result) == 1
assert result[0]["description"] == "real error"
# βββ Configuration Tests βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
class TestConfiguration:
"""Tests for configuration module."""
def test_llm_config_values(self):
"""Test LLM temperature values."""
assert LLMConfig.STRUCTURED == 0.0
assert LLMConfig.PRECISE == 0.1
assert LLMConfig.BALANCED == 0.3
assert LLMConfig.CREATIVE == 0.7
def test_scoring_config_values(self):
"""Test scoring configuration."""
assert ScoringConfig.MIN == 0
assert ScoringConfig.MAX == 100
assert ScoringConfig.DEFAULT == 50
def test_severity_config_valid_levels(self):
"""Test valid severity levels."""
assert "low" in SeverityConfig.VALID_LEVELS
assert "medium" in SeverityConfig.VALID_LEVELS
assert "high" in SeverityConfig.VALID_LEVELS
assert "critical" in SeverityConfig.VALID_LEVELS
def test_agent_config_coding(self):
"""Test coding agent configuration."""
assert AgentConfig.CodingAgent.PLANNER_TEMPERATURE == LLMConfig.PRECISE
assert AgentConfig.CodingAgent.CODER_TEMPERATURE == LLMConfig.PRECISE
assert AgentConfig.CodingAgent.REVIEWER_TEMPERATURE == LLMConfig.STRUCTURED
def test_agent_config_orchestrator(self):
"""Test orchestrator agent configuration."""
assert AgentConfig.OrchestratorAgent.DECOMPOSER_TEMPERATURE == LLMConfig.BALANCED
assert AgentConfig.OrchestratorAgent.RESEARCHER_TEMPERATURE == LLMConfig.CREATIVE
assert AgentConfig.OrchestratorAgent.CRITIC_TEMPERATURE == LLMConfig.STRUCTURED
# βββ Context Injection Tests βββββββββββββββββββββββββββββββββββββββββββββββββββ
class TestContextInjection:
"""Tests for memory context injection."""
def test_has_memory_context_with_content(self):
"""Test memory context detection with content."""
assert has_memory_context("some context")
assert has_memory_context(" not empty ")
def test_has_memory_context_without_content(self):
"""Test memory context detection without content."""
assert not has_memory_context(None)
assert not has_memory_context("")
assert not has_memory_context(" ")
def test_inject_memory_context_with_context(self):
"""Test context injection when memory available."""
memory = "Prior conversation"
task = "New task"
result = inject_memory_context(task, memory)
assert "Prior conversation" in result
assert "New task" in result
def test_inject_memory_context_without_context(self):
"""Test context injection when memory unavailable."""
task = "New task"
result = inject_memory_context(task, None)
assert result == task
# βββ SSE Streaming Tests βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
class TestSSEStreaming:
"""Tests for SSE event formatting."""
def test_create_sse_event(self):
"""Test SSE event creation."""
event = create_sse_event("token", content="hello")
assert event["type"] == "token"
assert event["content"] == "hello"
def test_format_sse_event(self):
"""Test SSE event formatting."""
event = {"type": "token", "content": "test"}
result = format_sse_event(event)
assert result.startswith("data: ")
assert result.endswith("\n\n")
assert "token" in result
assert "test" in result
def test_format_sse_event_json_valid(self):
"""Test that formatted SSE contains valid JSON."""
event = {"type": "done", "value": 42}
result = format_sse_event(event)
# Extract JSON from "data: {...}\n\n"
json_str = result.split("data: ")[1].strip()
parsed = json.loads(json_str)
assert parsed["type"] == "done"
assert parsed["value"] == 42
# βββ Graph Nodes Tests βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
class TestGraphNodes:
"""Tests for graph node coordinate definitions."""
def test_coding_node_coords(self):
"""Test coding agent node coordinates."""
coords = get_coding_node_coords()
assert "router" in coords
assert "code_planner" in coords
assert "coder_1" in coords
assert "code_aggregator" in coords
assert "code_reviewer" in coords
assert len(coords) == 8
def test_deep_research_node_coords(self):
"""Test deep research node coordinates."""
coords = get_deep_research_node_coords()
assert "router" in coords
assert "researcher_1" in coords
assert "researcher_2" in coords
assert "aggregator" in coords
assert "critic" in coords
assert len(coords) == 7
def test_standard_node_coords(self):
"""Test standard path node coordinates."""
coords = get_standard_node_coords()
assert "router" in coords
assert "output" in coords
assert len(coords) == 2
def test_node_coords_have_x_y(self):
"""Test that all node coordinates have x and y."""
for coord_func in [get_coding_node_coords, get_deep_research_node_coords, get_standard_node_coords]:
coords = coord_func()
for node_id, position in coords.items():
assert "x" in position, f"Missing x for {node_id}"
assert "y" in position, f"Missing y for {node_id}"
assert isinstance(position["x"], (int, float))
assert isinstance(position["y"], (int, float))
if __name__ == "__main__":
pytest.main([__file__, "-v"])
|