""" Integration tests for Claude Skills. Tests Skills with mocked API responses to verify AI integration functionality. """ import pytest import json from unittest.mock import AsyncMock, patch, Mock from agent_core import CommandContext, SkillResult from skills.claude_skill import ClaudeAnalyzeSkill, ClaudeTransformSkill class TestClaudeAnalyzeSkill: """Integration tests for ClaudeAnalyzeSkill""" @pytest.fixture def skill(self): """Create ClaudeAnalyzeSkill instance""" return ClaudeAnalyzeSkill() @pytest.fixture def context(self, sample_config): """Create command context with analysis parameters""" return CommandContext( command_name="analyze", args={ "content": """# 2024-01-15 Daily Journal ## 今天的经历 - 完成了项目的重要里程碑 - 与团队进行了有效的沟通 ## 学到的东西 - 学会了新的Python异步编程技巧 - 理解了更好的错误处理模式 ## 遇到的问题 - API调用偶尔超时 - 配置文件格式需要改进 ## 明天的计划 - 优化API调用的重试机制 - 更新文档 """, "analysis_type": "extract_experiences" }, config=sample_config ) @pytest.mark.asyncio async def test_analyze_journal_success(self, skill, context): """Test successful journal analysis""" mock_response = { "experiences": [ { "title": "项目里程碑完成", "description": "成功完成了项目的重要里程碑,展现了良好的项目管理能力", "category": "项目管理", "importance": "high" }, { "title": "团队沟通改进", "description": "与团队进行了有效的沟通,提升了协作效率", "category": "团队协作", "importance": "medium" } ], "lessons": [ { "title": "Python异步编程", "description": "学会了新的Python异步编程技巧,提升了代码效率", "category": "技术学习", "application": "可以应用到当前项目的API调用优化中" } ], "problems": [ { "title": "API调用超时", "description": "API调用偶尔出现超时问题", "severity": "medium", "suggested_solution": "实现重试机制和超时处理" } ] } # Mock the Claude API client with patch('skills.claude_skill.anthropic') as mock_anthropic: mock_client = Mock() mock_anthropic.Anthropic.return_value = mock_client # Mock the messages.create method mock_message = Mock() mock_message.content = [Mock(text=json.dumps(mock_response, ensure_ascii=False))] mock_client.messages.create.return_value = mock_message result = await skill.execute(context) assert result.success is True assert "experiences" in result.data assert "lessons" in result.data assert "problems" in result.data assert len(result.data["experiences"]) == 2 assert len(result.data["lessons"]) == 1 assert len(result.data["problems"]) == 1 # Verify API was called with correct parameters mock_client.messages.create.assert_called_once() call_args = mock_client.messages.create.call_args assert call_args[1]["model"] == "claude-3-5-sonnet-20241022" assert call_args[1]["max_tokens"] == 4096 @pytest.mark.asyncio async def test_analyze_empty_content(self, skill, sample_config): """Test analysis with empty content""" context = CommandContext( command_name="analyze", args={"content": "", "analysis_type": "extract_experiences"}, config=sample_config ) result = await skill.execute(context) assert result.success is False assert "content" in result.error.lower() @pytest.mark.asyncio async def test_analyze_api_error(self, skill, context): """Test handling of Claude API errors""" with patch('skills.claude_skill.anthropic') as mock_anthropic: mock_client = Mock() mock_anthropic.Anthropic.return_value = mock_client # Mock API error mock_client.messages.create.side_effect = Exception("API rate limit exceeded") result = await skill.execute(context) assert result.success is False assert "api" in result.error.lower() or "rate limit" in result.error.lower() @pytest.mark.asyncio async def test_analyze_invalid_json_response(self, skill, context): """Test handling of invalid JSON response from Claude""" with patch('skills.claude_skill.anthropic') as mock_anthropic: mock_client = Mock() mock_anthropic.Anthropic.return_value = mock_client # Mock invalid JSON response mock_message = Mock() mock_message.content = [Mock(text="Invalid JSON response")] mock_client.messages.create.return_value = mock_message result = await skill.execute(context) assert result.success is False assert "json" in result.error.lower() or "parse" in result.error.lower() @pytest.mark.asyncio async def test_analyze_different_types(self, skill, sample_config): """Test different analysis types""" analysis_types = ["extract_experiences", "extract_lessons", "extract_problems", "summarize"] for analysis_type in analysis_types: context = CommandContext( command_name="analyze", args={ "content": "Sample journal content for testing", "analysis_type": analysis_type }, config=sample_config ) mock_response = {"result": f"Analysis result for {analysis_type}"} with patch('skills.claude_skill.anthropic') as mock_anthropic: mock_client = Mock() mock_anthropic.Anthropic.return_value = mock_client mock_message = Mock() mock_message.content = [Mock(text=json.dumps(mock_response))] mock_client.messages.create.return_value = mock_message result = await skill.execute(context) assert result.success is True assert result.data["analysis_type"] == analysis_type class TestClaudeTransformSkill: """Integration tests for ClaudeTransformSkill""" @pytest.fixture def skill(self): """Create ClaudeTransformSkill instance""" return ClaudeTransformSkill() @pytest.fixture def context(self, sample_config): """Create command context with transformation parameters""" return CommandContext( command_name="transform", args={ "content": { "title": "项目里程碑完成", "description": "成功完成了项目的重要里程碑", "category": "项目管理" }, "transform_type": "create_experience_note", "target_format": "markdown" }, config=sample_config ) @pytest.mark.asyncio async def test_transform_to_markdown_success(self, skill, context): """Test successful content transformation to markdown""" mock_response = """# 项目里程碑完成 ## 经验描述 成功完成了项目的重要里程碑,这次经历展现了良好的项目管理能力和团队协作精神。 ## 关键要点 - 项目管理技能得到提升 - 团队协作效率显著改善 - 里程碑按时完成 ## 应用场景 这个经验可以应用到未来的项目管理中,特别是在设定和跟踪项目里程碑方面。 ## 相关标签 #项目管理 #里程碑 #团队协作 --- *创建时间: 2024-01-15* *来源: 日记整理* """ with patch('skills.claude_skill.anthropic') as mock_anthropic: mock_client = Mock() mock_anthropic.Anthropic.return_value = mock_client mock_message = Mock() mock_message.content = [Mock(text=mock_response)] mock_client.messages.create.return_value = mock_message result = await skill.execute(context) assert result.success is True assert result.data["transformed_content"] == mock_response assert result.data["transform_type"] == "create_experience_note" assert result.data["target_format"] == "markdown" # Verify the content contains expected markdown elements assert "# 项目里程碑完成" in result.data["transformed_content"] assert "## 经验描述" in result.data["transformed_content"] assert "#项目管理" in result.data["transformed_content"] @pytest.mark.asyncio async def test_transform_different_types(self, skill, sample_config): """Test different transformation types""" transform_types = [ "create_experience_note", "create_lesson_note", "create_problem_note", "create_summary" ] for transform_type in transform_types: context = CommandContext( command_name="transform", args={ "content": {"title": "Test", "description": "Test content"}, "transform_type": transform_type, "target_format": "markdown" }, config=sample_config ) mock_response = f"# Transformed Content\n\nContent for {transform_type}" with patch('skills.claude_skill.anthropic') as mock_anthropic: mock_client = Mock() mock_anthropic.Anthropic.return_value = mock_client mock_message = Mock() mock_message.content = [Mock(text=mock_response)] mock_client.messages.create.return_value = mock_message result = await skill.execute(context) assert result.success is True assert result.data["transform_type"] == transform_type @pytest.mark.asyncio async def test_transform_missing_content(self, skill, sample_config): """Test transformation with missing content""" context = CommandContext( command_name="transform", args={ "transform_type": "create_experience_note", "target_format": "markdown" # Missing content }, config=sample_config ) result = await skill.execute(context) assert result.success is False assert "content" in result.error.lower() @pytest.mark.asyncio async def test_transform_api_error(self, skill, context): """Test handling of Claude API errors during transformation""" with patch('skills.claude_skill.anthropic') as mock_anthropic: mock_client = Mock() mock_anthropic.Anthropic.return_value = mock_client # Mock API error mock_client.messages.create.side_effect = Exception("API authentication failed") result = await skill.execute(context) assert result.success is False assert "api" in result.error.lower() or "authentication" in result.error.lower() class TestClaudeSkillsIntegration: """Integration tests combining Claude skills""" @pytest.mark.asyncio async def test_analyze_then_transform_workflow(self, sample_config): """Test complete analyze-then-transform workflow""" analyze_skill = ClaudeAnalyzeSkill() transform_skill = ClaudeTransformSkill() # Step 1: Analyze journal content analyze_context = CommandContext( command_name="analyze", args={ "content": sample_journal_content, "analysis_type": "extract_experiences" }, config=sample_config ) # Step 2: Transform extracted experience to note experience_data = { "title": "项目里程碑完成", "description": "成功完成了项目的重要里程碑", "category": "项目管理", "importance": "high" } transform_context = CommandContext( command_name="transform", args={ "content": experience_data, "transform_type": "create_experience_note", "target_format": "markdown" }, config=sample_config ) # Mock responses analyze_response = { "experiences": [experience_data], "lessons": [], "problems": [] } transform_response = """# 项目里程碑完成 ## 经验描述 成功完成了项目的重要里程碑 ## 分类 项目管理 ## 重要程度 高 #项目管理 #里程碑 """ with patch('skills.claude_skill.anthropic') as mock_anthropic: mock_client = Mock() mock_anthropic.Anthropic.return_value = mock_client # Mock analyze response mock_analyze_message = Mock() mock_analyze_message.content = [Mock(text=json.dumps(analyze_response, ensure_ascii=False))] # Mock transform response mock_transform_message = Mock() mock_transform_message.content = [Mock(text=transform_response)] # Set up side_effect to return different responses for different calls mock_client.messages.create.side_effect = [mock_analyze_message, mock_transform_message] # Execute analyze analyze_result = await analyze_skill.execute(analyze_context) assert analyze_result.success is True assert len(analyze_result.data["experiences"]) == 1 # Execute transform using analyze result transform_result = await transform_skill.execute(transform_context) assert transform_result.success is True assert "项目里程碑完成" in transform_result.data["transformed_content"] # Verify both API calls were made assert mock_client.messages.create.call_count == 2 @pytest.mark.asyncio async def test_batch_analysis_and_transformation(self, sample_config): """Test batch processing of multiple content pieces""" analyze_skill = ClaudeAnalyzeSkill() transform_skill = ClaudeTransformSkill() # Multiple journal entries to process journal_entries = [ "今天学会了新的编程技巧", "解决了一个复杂的技术问题", "与客户进行了重要的项目讨论" ] with patch('skills.claude_skill.anthropic') as mock_anthropic: mock_client = Mock() mock_anthropic.Anthropic.return_value = mock_client # Mock responses for each entry mock_responses = [] for i, entry in enumerate(journal_entries): analyze_response = { "experiences": [{ "title": f"Experience {i+1}", "description": entry, "category": "学习" }] } transform_response = f"# Experience {i+1}\n\n{entry}\n\n#学习" mock_analyze_message = Mock() mock_analyze_message.content = [Mock(text=json.dumps(analyze_response, ensure_ascii=False))] mock_transform_message = Mock() mock_transform_message.content = [Mock(text=transform_response)] mock_responses.extend([mock_analyze_message, mock_transform_message]) mock_client.messages.create.side_effect = mock_responses # Process each entry results = [] for entry in journal_entries: # Analyze analyze_context = CommandContext( command_name="analyze", args={"content": entry, "analysis_type": "extract_experiences"}, config=sample_config ) analyze_result = await analyze_skill.execute(analyze_context) # Transform if analyze_result.success and analyze_result.data["experiences"]: experience = analyze_result.data["experiences"][0] transform_context = CommandContext( command_name="transform", args={ "content": experience, "transform_type": "create_experience_note", "target_format": "markdown" }, config=sample_config ) transform_result = await transform_skill.execute(transform_context) results.append((analyze_result, transform_result)) # Verify all entries were processed successfully assert len(results) == len(journal_entries) for analyze_result, transform_result in results: assert analyze_result.success is True assert transform_result.success is True # Verify correct number of API calls (2 per entry: analyze + transform) assert mock_client.messages.create.call_count == len(journal_entries) * 2 # Sample journal content for testing sample_journal_content = """# 2024-01-15 Daily Journal ## 今天的经历 - 完成了项目的重要里程碑 - 与团队进行了有效的沟通 - 参加了技术分享会议 ## 学到的东西 - 学会了新的Python异步编程技巧 - 理解了更好的错误处理模式 - 掌握了新的项目管理方法 ## 遇到的问题 - API调用偶尔超时 - 配置文件格式需要改进 - 团队沟通中存在信息不对称 ## 明天的计划 - 优化API调用的重试机制 - 更新文档 - 组织团队同步会议 """