Files
temp/features/steps/application_context_analysis_agent_steps.py
T

226 lines
7.8 KiB
Python

"""Step definitions for application context analysis agent coverage."""
from __future__ import annotations
from collections.abc import Callable
from typing import Any, cast
from unittest.mock import patch
from behave import ( # type: ignore[import]
given as behave_given,
)
from behave import (
then as behave_then,
)
from behave import (
when as behave_when,
)
from cleveragents.application.agents.context_analysis import (
ContextAnalysisAgent,
ContextAnalysisState,
)
given = cast(Callable[..., Any], behave_given)
then = cast(Callable[..., Any], behave_then)
when = cast(Callable[..., Any], behave_when)
@when(
"I instantiate the application context agent with max files per batch {batch_size:d}"
)
def step_instantiate_application_agent(context: Any, batch_size: int) -> None:
with patch(
"cleveragents.application.agents.context_analysis.BaseAgent.__init__",
return_value=None,
) as base_init:
context.agent = ContextAnalysisAgent(max_files_per_batch=batch_size)
context.base_init_call = base_init.call_args
@then("the agent should track max files per batch of {batch_size:d}")
def step_assert_batch_size(context: Any, batch_size: int) -> None:
assert getattr(context.agent, "max_files_per_batch", None) == batch_size
@then("the base agent initializer should have been invoked with default settings")
def step_assert_base_init_called(context: Any) -> None:
call = getattr(context, "base_init_call", None)
assert call is not None, "Expected BaseAgent.__init__ to be called"
args, kwargs = call
assert args == ("openai", "gpt-4", 0.3)
assert kwargs == {}
@when("I construct the application context workflow graph")
def step_build_application_graph(context: Any) -> None:
created_graphs: list[CapturingStateGraph] = []
class CapturingStateGraph:
def __init__(self, state_cls: Any):
self.state_cls = state_cls
self.nodes: dict[str, Any] = {}
self.edges: list[tuple[Any, Any]] = []
self.entry_point: str | None = None
created_graphs.append(self)
def add_node(self, name: str, handler: Any) -> None:
self.nodes[name] = handler
def add_edge(self, start: Any, end: Any) -> None:
self.edges.append((start, end))
def set_entry_point(self, node: str) -> None:
self.entry_point = node
with (
patch(
"cleveragents.application.agents.context_analysis.StateGraph",
CapturingStateGraph,
),
patch("cleveragents.application.agents.context_analysis.END", "END"),
):
agent = ContextAnalysisAgent.__new__(ContextAnalysisAgent)
build_graph: Callable[[ContextAnalysisAgent], Any] = (
ContextAnalysisAgent._build_graph
)
context.graph = build_graph(agent) # type: ignore[misc]
context.captured_graph = created_graphs[-1]
@then("the workflow should target ContextAnalysisState as the graph state")
def step_assert_graph_state_type(context: Any) -> None:
graph = context.captured_graph
assert graph.state_cls is ContextAnalysisState
@then("the workflow should include nodes:")
def step_assert_graph_nodes(context: Any) -> None:
expected_nodes = [
line.strip() for line in context.text.strip().splitlines() if line.strip()
]
graph = context.captured_graph
assert set(expected_nodes) == set(graph.nodes.keys())
@then("the workflow should define ordered edges:")
def step_assert_graph_edges(context: Any) -> None:
lines = [line.strip() for line in context.text.strip().splitlines() if line.strip()]
expected_edges: list[tuple[str, str]] = []
for line in lines:
parts = [segment.strip() for segment in line.split("->")]
assert len(parts) == 2, f"Edge '{line}' must be formatted as 'source -> target'"
expected_edges.append((parts[0], parts[1]))
graph = context.captured_graph
actual_edges = [(str(start), str(end)) for start, end in graph.edges]
assert actual_edges == expected_edges
@given("I have a fresh application context agent state")
def step_create_fresh_state(context: Any) -> None:
state: ContextAnalysisState = {
"messages": [],
"context": {},
"result": None,
"error": None,
"metadata": {},
"files_to_analyze": [],
"analyzed_files": {},
"dependencies": {},
"project_structure": {},
"relevant_contexts": [],
}
context.state = state
@when("I run the placeholder context workflow nodes")
def step_run_placeholder_nodes(context: Any) -> None:
agent = ContextAnalysisAgent.__new__(ContextAnalysisAgent)
state: ContextAnalysisState = context.state
identify_files: Callable[[ContextAnalysisState], ContextAnalysisState] = (
agent._identify_files
)
analyze_files: Callable[[ContextAnalysisState], ContextAnalysisState] = (
agent._analyze_files
)
extract_dependencies: Callable[[ContextAnalysisState], ContextAnalysisState] = (
agent._extract_dependencies
)
build_structure: Callable[[ContextAnalysisState], ContextAnalysisState] = (
agent._build_structure
)
select_contexts: Callable[[ContextAnalysisState], ContextAnalysisState] = (
agent._select_contexts
)
finalize: Callable[[ContextAnalysisState], ContextAnalysisState] = agent._finalize
state = identify_files(state) # type: ignore[misc]
state = analyze_files(state) # type: ignore[misc]
state = extract_dependencies(state) # type: ignore[misc]
state = build_structure(state) # type: ignore[misc]
state = select_contexts(state) # type: ignore[misc]
state = finalize(state) # type: ignore[misc]
context.state = state
@then("the state should include an empty files_to_analyze list")
def step_assert_empty_files_list(context: Any) -> None:
assert context.state.get("files_to_analyze") == []
@then("the state should include an empty analyzed_files map")
def step_assert_empty_analysis(context: Any) -> None:
assert context.state.get("analyzed_files") == {}
@then("the state should include an empty dependencies map")
def step_assert_empty_dependencies(context: Any) -> None:
assert context.state.get("dependencies") == {}
@then("the state should include a project structure skeleton")
def step_assert_project_structure(context: Any) -> None:
expected_structure: dict[str, list[str]] = {
"directories": [],
"modules": [],
"entry_points": [],
}
assert context.state.get("project_structure") == expected_structure
@then("the state should include an empty relevant_contexts list")
def step_assert_empty_relevant_contexts(context: Any) -> None:
assert context.state.get("relevant_contexts") == []
@then("the state should include a finalized result summary")
def step_assert_final_result(context: Any) -> None:
result = context.state.get("result")
assert isinstance(result, dict)
typed_result = cast(dict[str, Any], result)
expected_keys = {
"analyzed_files",
"dependencies",
"project_structure",
"relevant_contexts",
"file_count",
}
key_list: list[str] = list(typed_result.keys())
result_keys: set[str] = set(key_list)
assert expected_keys.issubset(result_keys)
analyzed_files = cast(dict[str, Any], typed_result["analyzed_files"])
dependencies = cast(dict[str, Any], typed_result["dependencies"])
project_structure = cast(dict[str, Any], typed_result["project_structure"])
relevant_contexts = cast(list[Any], typed_result["relevant_contexts"])
file_count = cast(int, typed_result["file_count"])
assert dependencies == context.state.get("dependencies")
assert project_structure == context.state.get("project_structure")
assert relevant_contexts == context.state.get("relevant_contexts")
assert file_count == len(analyzed_files)