Skip to main content
Glama
test_living_reports_integration.py12.3 kB
"""Integration tests for complete Living Reports workflows.""" import uuid import pytest from igloo_mcp.config import get_config from igloo_mcp.living_reports.models import Insight from igloo_mcp.living_reports.service import ReportService from igloo_mcp.mcp.tools.evolve_report import EvolveReportTool from igloo_mcp.mcp.tools.render_report import RenderReportTool @pytest.mark.asyncio async def test_full_lifecycle_create_evolve_render_revert(tmp_path, monkeypatch): """Test complete report lifecycle.""" service = ReportService(reports_root=tmp_path / "reports") # 1. Create with template report_id = service.create_report("Q1 Report", template="analyst_v1") # Verify template was applied outline = service.get_report_outline(report_id) assert len(outline.sections) == 5 # analyst_v1 has 5 sections assert outline.sections[0].title == "Executive Summary" # 2. Evolve - add insight evolve_tool = EvolveReportTool(get_config(), service) # Get first section ID to reference the insight outline = service.get_report_outline(report_id) first_section_id = outline.sections[0].section_id insight_id = str(uuid.uuid4()) result = await evolve_tool.execute( report_selector=report_id, instruction="Add Q1 revenue insight", proposed_changes={ "insights_to_add": [ { "insight_id": insight_id, "importance": 9, "summary": "Revenue up 25% YoY", "supporting_queries": [], } ], # Reference the insight in a section "sections_to_modify": [ { "section_id": first_section_id, "insight_ids_to_add": [insight_id], } ], }, constraints={"skip_citation_validation": True}, ) assert result["status"] == "success" # Verify insight was added outline = service.get_report_outline(report_id) assert len(outline.insights) == 1 assert outline.insights[0].insight_id == insight_id # Get action_id for revert storage = service.global_storage.get_report_storage(report_id) events = storage.load_audit_events() evolve_action_id = events[-1].action_id # 3. Render (dry run to avoid Quarto dependency) render_tool = RenderReportTool(get_config(), service) result = await render_tool.execute( report_selector=report_id, dry_run=True, ) assert result["status"] == "success" # 4. Revert revert_result = service.revert_report(report_id, evolve_action_id) assert revert_result["success"] is True # Verify reverted state outline = service.get_report_outline(report_id) assert len(outline.insights) == 0 # Insight removed by revert def test_bulk_operations_workflow(tmp_path): """Test bulk operations workflow.""" service = ReportService(reports_root=tmp_path / "reports") # Create multiple reports report_ids = [] for i in range(3): report_id = service.create_report(f"Report {i}", tags=["bulk_test"]) report_ids.append(report_id) # Archive all reports for report_id in report_ids: service.archive_report(report_id) # Verify all archived reports = service.list_reports(status="archived") archived_ids = {r["id"] for r in reports} assert archived_ids >= set(report_ids) # Tag reports for report_id in report_ids[:2]: service.tag_report(report_id, tags_to_add=["selected"]) # Verify tags selected_reports = service.list_reports(tags=["selected"]) selected_ids = {r["id"] for r in selected_reports} assert selected_ids == set(report_ids[:2]) # Delete one report trash_location = service.delete_report(report_ids[0]) # Verify deleted reports = service.list_reports() remaining_ids = {r["id"] for r in reports} assert report_ids[0] not in remaining_ids assert report_ids[1] in remaining_ids assert report_ids[2] in remaining_ids # Verify trash location exists import os assert os.path.exists(trash_location) def test_fork_and_synthesize_workflow(tmp_path): """Test fork and synthesize operations.""" service = ReportService(reports_root=tmp_path / "reports") # Create base report with template (has sections) base_id = service.create_report("Base Report", template="deep_dive") # Add content to base - reference insight in section outline = service.get_report_outline(base_id) insight_id = str(uuid.uuid4()) outline.insights.append( Insight( insight_id=insight_id, importance=8, summary="Base insight", supporting_queries=[], status="active", ) ) outline.sections[0].insight_ids = [insight_id] # Reference insight in section service.update_report_outline(base_id, outline) # Fork the report forked_id = service.fork_report(base_id, "Forked Report") # Verify fork forked_outline = service.get_report_outline(forked_id) assert forked_outline.title == "Forked Report" assert len(forked_outline.insights) == 1 assert forked_outline.metadata["forked_from"] == base_id # Create another report for synthesis with template other_id = service.create_report("Other Report", template="deep_dive") other_outline = service.get_report_outline(other_id) other_insight_id = str(uuid.uuid4()) other_outline.insights.append( Insight( insight_id=other_insight_id, importance=7, summary="Other insight", supporting_queries=[], status="active", ) ) other_outline.sections[0].insight_ids = [other_insight_id] # Reference insight service.update_report_outline(other_id, other_outline) # Synthesize reports synth_id = service.synthesize_reports([base_id, other_id], "Combined Report") # Verify synthesis synth_outline = service.get_report_outline(synth_id) assert synth_outline.title == "Combined Report" assert len(synth_outline.insights) == 2 assert set(synth_outline.metadata["synthesized_from"]) == {base_id, other_id} assert "synthesis_note" in synth_outline.metadata def test_concurrent_agents_version_conflict(tmp_path): """Test version conflict detection with concurrent modifications.""" service = ReportService(reports_root=tmp_path / "reports") report_id = service.create_report("Test Report") # Get initial outline to capture version outline1 = service.get_report_outline(report_id) initial_version = outline1.outline_version # Simulate concurrent modification outline2 = service.get_report_outline(report_id) outline2.title = "Modified by Agent 2" service.update_report_outline(report_id, outline2, actor="agent") # Try to update with stale version outline1.title = "Modified by Agent 1" with pytest.raises(ValueError, match="Version mismatch"): service.update_report_outline(report_id, outline1, actor="agent", expected_version=initial_version) def test_template_to_advanced_workflow(tmp_path): """Test workflow from template creation through advanced operations.""" service = ReportService(reports_root=tmp_path / "reports") # Start with template report_id = service.create_report("Template Report", template="deep_dive", tags=["template"]) # Verify template structure outline = service.get_report_outline(report_id) assert len(outline.sections) == 4 # deep_dive has 4 sections assert outline.sections[0].title == "Overview" # Evolve by adding insights insight1_id = str(uuid.uuid4()) insight2_id = str(uuid.uuid4()) outline.insights.extend( [ Insight( insight_id=insight1_id, importance=9, summary="Key finding 1", supporting_queries=[], status="active", ), Insight( insight_id=insight2_id, importance=7, summary="Key finding 2", supporting_queries=[], status="active", ), ] ) # Link insights to sections outline.sections[1].insight_ids = [insight1_id] outline.sections[2].insight_ids = [insight2_id] service.update_report_outline(report_id, outline) # Fork for variation variant_id = service.fork_report(report_id, "Variant Report") # Modify variant variant_outline = service.get_report_outline(variant_id) variant_outline.sections[0].title = "Modified Overview" service.update_report_outline(variant_id, variant_outline) # Archive original service.archive_report(report_id) # Verify final state reports = service.list_reports() active_reports = [r for r in reports if r["status"] == "active"] archived_reports = [r for r in reports if r["status"] == "archived"] assert len(active_reports) == 1 assert len(archived_reports) == 1 assert active_reports[0]["title"] == "Variant Report" assert archived_reports[0]["title"] == "Template Report" @pytest.mark.asyncio async def test_mcp_tool_integration_workflow(tmp_path): """Test MCP tools working together in sequence.""" service = ReportService(reports_root=tmp_path / "reports") evolve_tool = EvolveReportTool(get_config(), service) render_tool = RenderReportTool(get_config(), service) # Create report via service with a template that has sections report_id = service.create_report("MCP Test Report", template="deep_dive") # Get a section ID to reference the insight outline = service.get_report_outline(report_id) section_id = outline.sections[0].section_id # Evolve via MCP tool - add insight and reference it in section insight_id = str(uuid.uuid4()) result = await evolve_tool.execute( report_selector=report_id, instruction="Add test insight", proposed_changes={ "insights_to_add": [ { "insight_id": insight_id, "importance": 8, "summary": "MCP-generated insight", "supporting_queries": [], } ], "sections_to_modify": [ { "section_id": section_id, "insight_ids_to_add": [insight_id], } ], }, constraints={"skip_citation_validation": True}, ) assert result["status"] == "success" # Render via MCP tool result = await render_tool.execute(report_selector=report_id, dry_run=True) assert result["status"] == "success" # Verify final state outline = service.get_report_outline(report_id) assert len(outline.insights) == 1 assert outline.insights[0].summary == "MCP-generated insight" def test_error_recovery_workflow(tmp_path): """Test error detection and recovery workflows.""" service = ReportService(reports_root=tmp_path / "reports") # Create report report_id = service.create_report("Error Test Report") # Make a change outline = service.get_report_outline(report_id) outline.title = "Modified Title" service.update_report_outline(report_id, outline) # Get action for revert storage = service.global_storage.get_report_storage(report_id) events = storage.load_audit_events() modify_action = events[-1] # Simulate backup corruption (delete backup file) backup_filename = modify_action.payload.get("backup_filename") if backup_filename: backup_path = storage.backups_dir / backup_filename if backup_path.exists(): backup_path.unlink() # Attempt revert should fail with pytest.raises(ValueError, match="Backup file missing"): service.revert_report(report_id, modify_action.action_id) # Create new change (should succeed) outline = service.get_report_outline(report_id) outline.title = "Recovered Title" service.update_report_outline(report_id, outline) # Verify recovery outline = service.get_report_outline(report_id) assert outline.title == "Recovered Title"

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Evan-Kim2028/igloo-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server