Skip to content

Commit 3bbe8e8

Browse files
add more coverage to processor
1 parent 431830e commit 3bbe8e8

4 files changed

Lines changed: 1072 additions & 0 deletions

File tree

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
# Copyright (c) Microsoft Corporation.
2+
# Licensed under the MIT License.
3+
4+
from __future__ import annotations
5+
6+
import asyncio
7+
from unittest.mock import AsyncMock, MagicMock, patch
8+
9+
import pytest
10+
11+
from steps.convert.models.step_output import Yaml_ExtendedBooleanResult
12+
from steps.documentation.orchestration.documentation_orchestrator import (
13+
DocumentationOrchestrator,
14+
)
15+
16+
17+
def _run(coro):
18+
return asyncio.run(coro)
19+
20+
21+
def _make_orch():
22+
"""Create an instance bypassing __init__ to keep tests isolated."""
23+
orch = DocumentationOrchestrator.__new__(DocumentationOrchestrator)
24+
orch.initialized = True
25+
orch.step_name = "Documentation"
26+
orch.app_context = MagicMock()
27+
orch.memory_store = None
28+
orch.agents = {}
29+
return orch
30+
31+
32+
class TestPrepareAgentInfos:
33+
def test_raises_when_mcp_tools_none(self):
34+
orch = _make_orch()
35+
orch.mcp_tools = None
36+
with pytest.raises(ValueError, match=r"MCP tools must be prepared"):
37+
_run(orch.prepare_agent_infos())
38+
39+
def test_builds_agents_with_registry_entries(self, tmp_path):
40+
orch = _make_orch()
41+
orch.mcp_tools = [MagicMock(), MagicMock(), MagicMock(), MagicMock()]
42+
orch.task_param = Yaml_ExtendedBooleanResult(process_id="proc-X")
43+
44+
registry_entries = [
45+
{"agent_name": "EKS Expert", "prompt_file": "prompt_eks_expert.txt"},
46+
{"agent_name": "GKE Expert", "prompt_file": "prompt_gke_expert.txt"},
47+
{"agent_name": "", "prompt_file": "skip.txt"}, # invalid agent_name
48+
{"agent_name": "Bad", "prompt_file": ""}, # invalid prompt_file
49+
{"agent_name": "Missing", "prompt_file": "nonexistent.txt"}, # path missing
50+
{"agent_name": 42, "prompt_file": "x.txt"}, # wrong type
51+
]
52+
53+
# Patch helpers on the instance
54+
with patch.object(
55+
DocumentationOrchestrator,
56+
"load_platform_registry",
57+
return_value=registry_entries,
58+
), patch.object(
59+
DocumentationOrchestrator,
60+
"read_prompt_file",
61+
return_value="PROMPT BODY",
62+
), patch(
63+
"steps.documentation.orchestration.documentation_orchestrator.Path"
64+
) as path_cls:
65+
# Make Path(...).exists() True only for known prompt files.
66+
existing = {
67+
"prompt_eks_expert.txt",
68+
"prompt_gke_expert.txt",
69+
}
70+
71+
class _FakePath:
72+
def __init__(self, *parts):
73+
self._parts = [str(p) for p in parts]
74+
75+
def __truediv__(self, other):
76+
return _FakePath(*self._parts, other)
77+
78+
def resolve(self):
79+
return self
80+
81+
@property
82+
def parents(self):
83+
# Pretend parents[3] returns repo root that supports __truediv__
84+
return [self, self, self, _FakePath("repo_root")]
85+
86+
@property
87+
def parent(self):
88+
return self
89+
90+
def exists(self):
91+
name = self._parts[-1]
92+
return name in existing
93+
94+
def __str__(self):
95+
return "/".join(self._parts)
96+
97+
def __fspath__(self):
98+
return str(self)
99+
100+
path_cls.side_effect = lambda *a, **k: _FakePath(*a)
101+
102+
agent_infos = _run(orch.prepare_agent_infos())
103+
104+
names = [a.agent_name for a in agent_infos]
105+
# Built-ins
106+
assert "Technical Writer" in names
107+
assert "AKS Expert" in names
108+
assert "Azure Architect" in names
109+
assert "Chief Architect" in names
110+
# Registry experts that exist
111+
assert "EKS Expert" in names
112+
assert "GKE Expert" in names
113+
# Coordinator + ResultGenerator are appended last
114+
assert "Coordinator" in names
115+
assert "ResultGenerator" in names
116+
# Skipped invalid entries
117+
assert "Bad" not in names
118+
assert "Missing" not in names
119+
assert 42 not in names
120+
121+
122+
class TestForwardingHooks:
123+
def test_on_agent_response_calls_super(self):
124+
orch = _make_orch()
125+
with patch(
126+
"libs.base.orchestrator_base.OrchestratorBase.on_agent_response",
127+
new_callable=AsyncMock,
128+
) as super_call:
129+
_run(orch.on_agent_response(MagicMock()))
130+
assert super_call.await_count == 1
131+
132+
def test_on_agent_response_stream_calls_super(self):
133+
orch = _make_orch()
134+
with patch(
135+
"libs.base.orchestrator_base.OrchestratorBase.on_agent_response_stream",
136+
new_callable=AsyncMock,
137+
) as super_call:
138+
_run(orch.on_agent_response_stream(MagicMock()))
139+
assert super_call.await_count == 1
140+
141+
def test_on_orchestration_complete_logs(self, caplog):
142+
orch = _make_orch()
143+
result = MagicMock()
144+
result.execution_time_seconds = 12.5
145+
with caplog.at_level("INFO"):
146+
_run(orch.on_orchestration_complete(result))
147+
assert any(
148+
"Documentation Orchestration complete" in r.message for r in caplog.records
149+
)

0 commit comments

Comments
 (0)