from __future__ import annotations import json import time from pathlib import Path import pytest from yuxi.channels.adapters.yuanbao.analysis.orchestrator import YuanbaoAnalyzer from yuxi.channels.adapters.yuanbao.analysis.report_generator import GapReport, generate_gap_report from yuxi.channels.adapters.yuanbao.analysis.sub_agents import ( AnalysisResult, Finding, SubAgentFunctionalResearch, SubAgentIssueResearch, SubAgentWebResearch, ) class TestSubAgentWebResearch: @pytest.mark.asyncio async def test_research_returns_analysis_result(self): agent = SubAgentWebResearch() result = await agent.research() assert isinstance(result, AnalysisResult) assert result.agent_name == "WebResearchAgent" assert result.category == "external_research" assert len(result.findings) > 0 assert result.summary assert "critical" in result.severity_counts assert "warning" in result.severity_counts @pytest.mark.asyncio async def test_research_includes_platform_info(self): agent = SubAgentWebResearch() result = await agent.research() titles = [f["title"] for f in result.findings] assert any("月活" in t or "用户" in t or "MAU" in t for t in titles) assert any("Hermes" in t or "OpenClaw" in t or "适配器" in t for t in titles) class TestSubAgentFunctionalResearch: @pytest.mark.asyncio async def test_research_returns_analysis_result(self): agent = SubAgentFunctionalResearch() result = await agent.research() assert isinstance(result, AnalysisResult) assert result.agent_name == "FunctionalResearchAgent" assert result.category == "code_functional_analysis" assert len(result.findings) > 0 assert result.summary @pytest.mark.asyncio async def test_research_includes_module_files(self): agent = SubAgentFunctionalResearch() result = await agent.research() files = [f for f in result.findings if f.get("file")] file_names = [f["file"] for f in files] assert "adapter.py" in file_names assert "token.py" in file_names assert "security.py" in file_names @pytest.mark.asyncio async def test_research_identifies_missing_features(self): agent = SubAgentFunctionalResearch() result = await agent.research() missing = [f for f in result.findings if f.get("status") == "missing"] assert len(missing) > 0 missing_titles = [f["title"] for f in missing] assert any("send_media" in t for t in missing_titles) @pytest.mark.asyncio async def test_research_identifies_dead_code(self): agent = SubAgentFunctionalResearch() result = await agent.research() dead = [f for f in result.findings if f.get("status") == "dead_code"] assert len(dead) > 0 class TestSubAgentIssueResearch: @pytest.mark.asyncio async def test_research_returns_analysis_result(self): agent = SubAgentIssueResearch() result = await agent.research() assert isinstance(result, AnalysisResult) assert result.agent_name == "IssueResearchAgent" assert result.category == "code_quality_and_bugs" assert len(result.findings) > 0 assert result.summary @pytest.mark.asyncio async def test_research_finds_high_severity_issues(self): agent = SubAgentIssueResearch() result = await agent.research() critical_or_warning = [f for f in result.findings if f.get("severity") in ("critical", "warning")] assert len(critical_or_warning) > 0 @pytest.mark.asyncio async def test_research_finds_warning_issues(self): agent = SubAgentIssueResearch() result = await agent.research() warnings = [f for f in result.findings if f.get("severity") == "warning"] assert len(warnings) > 0 @pytest.mark.asyncio async def test_finding_severity_counts_are_accurate(self): agent = SubAgentIssueResearch() result = await agent.research() total_by_count = sum(result.severity_counts.values()) assert total_by_count == len(result.findings) class TestFinding: def test_finding_creation(self): f = Finding( file="test.py", line=10, severity="warning", category="error_handling", title="Test issue", description="A test description", suggestion="Fix it", code_snippet="print('hello')", ) assert f.file == "test.py" assert f.line == 10 assert f.severity == "warning" class TestReportGenerator: def test_generate_gap_report_basic(self): results = [ AnalysisResult( agent_name="WebAgent", category="external_research", findings=[], summary="Web research done", ), AnalysisResult( agent_name="FuncAgent", category="code_functional_analysis", findings=[ {"title": "Missing Feature X", "status": "missing", "detail": "Not implemented"}, {"title": "Implemented Feature Y", "status": "implemented", "detail": "Done"}, ], summary="Functional analysis done", ), AnalysisResult( agent_name="IssueAgent", category="code_quality_and_bugs", findings=[ {"file": "x.py", "line": 1, "severity": "critical", "category": "bug", "title": "Bug 1", "description": "Bad bug", "suggestion": "Fix it"}, ], summary="Issues found", ), ] report = generate_gap_report(results) assert isinstance(report, GapReport) assert report.title assert report.module_path == "yuxi/channels/adapters/yuanbao" assert len(report.missing_features) > 0 assert len(report.bug_list) > 0 assert len(report.fix_plan) > 0 assert report.summary class TestYuanbaoAnalyzer: @pytest.mark.asyncio async def test_run_full_analysis(self, tmp_path): analyzer = YuanbaoAnalyzer(output_dir=str(tmp_path)) report = await analyzer.run_full_analysis() assert isinstance(report, GapReport) assert report.title assert report.generated_at assert report.summary assert len(report.missing_features) > 0 assert len(report.unimplemented_interfaces) > 0 assert len(report.industry_gaps) > 0 assert len(report.security_issues) > 0 assert len(report.bug_list) > 0 assert len(report.fix_plan) > 0 @pytest.mark.asyncio async def test_run_full_analysis_saves_report(self, tmp_path): analyzer = YuanbaoAnalyzer(output_dir=str(tmp_path)) report = await analyzer.run_full_analysis() report_path = tmp_path / "gap_analysis_report.json" assert report_path.exists() data = json.loads(report_path.read_text(encoding="utf-8")) assert data["title"] == report.title assert data["summary"] == report.summary def test_print_report_does_not_raise(self, capsys): analyzer = YuanbaoAnalyzer() report = GapReport( title="Test Report", generated_at="2026-01-01", module_path="test", summary="Test summary", missing_features=[{"feature": "F1", "detail": "d", "priority": "high"}], security_issues=[ {"severity": "critical", "location": "x.py:1", "issue": "Bad", "fix": "Do better"} ], fix_plan=[{"priority": "P0", "action": "Fix bug"}], ) analyzer.print_report(report) captured = capsys.readouterr() assert "Test Report" in captured.out assert "F1" in captured.out