Click app with `ask` subcommand that spawns the web researcher MCP server over stdio, calls the research tool, and pretty-prints the ResearchResult contract using rich (panels for answer/confidence/cost, tables for citations, gaps, discovery events, and open questions). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
132 lines
4 KiB
Python
132 lines
4 KiB
Python
"""Tests for the marchwarden CLI."""
|
|
|
|
from unittest.mock import patch
|
|
|
|
from click.testing import CliRunner
|
|
|
|
from cli.main import cli, render_result
|
|
from researchers.web.models import (
|
|
Citation,
|
|
ConfidenceFactors,
|
|
CostMetadata,
|
|
DiscoveryEvent,
|
|
Gap,
|
|
GapCategory,
|
|
OpenQuestion,
|
|
ResearchResult,
|
|
)
|
|
from rich.console import Console
|
|
|
|
|
|
def _fixture_result() -> ResearchResult:
|
|
return ResearchResult(
|
|
answer="Tomatoes, peppers, squash, and beans grow well in Utah.",
|
|
citations=[
|
|
Citation(
|
|
source="web",
|
|
locator="https://extension.usu.edu/yard-and-garden",
|
|
title="USU Extension — Yard and Garden",
|
|
snippet="USU recommends warm-season crops for Utah's climate.",
|
|
raw_excerpt="Tomatoes, peppers, and squash thrive in Utah summers.",
|
|
confidence=0.9,
|
|
),
|
|
],
|
|
gaps=[
|
|
Gap(
|
|
topic="Microclimate variation",
|
|
category=GapCategory.SCOPE_EXCEEDED,
|
|
detail="Did not investigate elevation-specific recommendations.",
|
|
),
|
|
],
|
|
discovery_events=[
|
|
DiscoveryEvent(
|
|
type="related_research",
|
|
suggested_researcher="docs",
|
|
query="Utah USDA hardiness zones",
|
|
reason="Zone-specific guidance would improve answer.",
|
|
),
|
|
],
|
|
open_questions=[
|
|
OpenQuestion(
|
|
question="What are the best cool-season crops?",
|
|
context="Answer focused on warm-season crops.",
|
|
priority="medium",
|
|
),
|
|
],
|
|
confidence=0.82,
|
|
confidence_factors=ConfidenceFactors(
|
|
num_corroborating_sources=3,
|
|
source_authority="high",
|
|
contradiction_detected=False,
|
|
query_specificity_match=0.85,
|
|
budget_exhausted=False,
|
|
recency="current",
|
|
),
|
|
cost_metadata=CostMetadata(
|
|
tokens_used=4321,
|
|
iterations_run=3,
|
|
wall_time_sec=12.5,
|
|
budget_exhausted=False,
|
|
model_id="claude-sonnet-4-6",
|
|
),
|
|
trace_id="trace-abc-123",
|
|
)
|
|
|
|
|
|
class TestRenderResult:
|
|
def test_renders_all_sections(self):
|
|
console = Console(record=True, width=120)
|
|
render_result(_fixture_result(), console)
|
|
out = console.export_text()
|
|
assert "Tomatoes" in out
|
|
assert "USU Extension" in out
|
|
assert "scope_exceeded" in out
|
|
assert "related_research" in out
|
|
assert "cool-season" in out
|
|
assert "Confidence" in out
|
|
assert "claude-sonnet-4-6" in out
|
|
assert "trace-abc-123" in out
|
|
|
|
|
|
class TestAskCommand:
|
|
def test_ask_invokes_mcp_and_renders(self):
|
|
runner = CliRunner()
|
|
fixture = _fixture_result()
|
|
|
|
async def fake_call(question, depth, max_iterations, token_budget):
|
|
assert question == "What grows in Utah?"
|
|
assert depth == "shallow"
|
|
assert max_iterations == 2
|
|
assert token_budget == 5000
|
|
return fixture
|
|
|
|
with patch("cli.main.call_research_tool", side_effect=fake_call):
|
|
result = runner.invoke(
|
|
cli,
|
|
[
|
|
"ask",
|
|
"What grows in Utah?",
|
|
"--depth",
|
|
"shallow",
|
|
"--max-iterations",
|
|
"2",
|
|
"--budget",
|
|
"5000",
|
|
],
|
|
)
|
|
|
|
assert result.exit_code == 0, result.output
|
|
assert "Tomatoes" in result.output
|
|
assert "trace-abc-123" in result.output
|
|
|
|
def test_ask_handles_error(self):
|
|
runner = CliRunner()
|
|
|
|
async def boom(**kwargs):
|
|
raise RuntimeError("mcp went sideways")
|
|
|
|
with patch("cli.main.call_research_tool", side_effect=boom):
|
|
result = runner.invoke(cli, ["ask", "anything"])
|
|
|
|
assert result.exit_code == 1
|
|
assert "mcp went sideways" in result.output
|