import pytest from unittest.mock import Mock, patch from app.ai_service import AIService from datetime import datetime, timedelta class TestAIService: """Test cases for the AI service functionality.""" def setup_method(self): """Set up test fixtures.""" self.ai_service = AIService() # Set the attributes directly since they're not set in __init__ for tests self.ai_service.api_key = 'test-api-key' self.ai_service.model = 'test-model' self.ai_service.api_url = 'https://api.openai.com/v1' def test_init(self): """Test AI service initialization.""" assert self.ai_service.api_key == 'test-api-key' assert self.ai_service.model == 'test-model' assert self.ai_service.timeout == 30 assert self.ai_service.max_retries == 3 @patch('app.ai_service.requests.post') def test_generate_single_rule_success(self, mock_post): """Test successful single rule generation.""" # Mock successful API response mock_response = Mock() mock_response.json.return_value = { 'choices': [{ 'message': { 'content': 'Move emails from "boss@company.com" to this folder' } }] } mock_response.raise_for_status.return_value = None mock_post.return_value = mock_response rule_text, metadata = self.ai_service.generate_single_rule('Work', 'destination') assert rule_text == 'Move emails from "boss@company.com" to this folder' assert metadata is not None assert 'quality_score' in metadata assert 'model_used' in metadata assert 'generated_at' in metadata @patch('app.ai_service.requests.post') def test_generate_single_rule_failure(self, mock_post): """Test single rule generation failure.""" # Mock API failure mock_post.side_effect = Exception("API Error") rule_text, metadata = self.ai_service.generate_single_rule('Work', 'destination') assert rule_text is None assert metadata is not None assert 'error' in metadata def test_assess_rule_quality(self): """Test rule quality assessment.""" rule_text = "Move emails from 'boss@company.com' to this folder" folder_name = "Work" score = self.ai_service._assess_rule_quality(rule_text, folder_name, 'destination') assert isinstance(score, int) assert 0 <= score <= 100 def test_get_quality_grade(self): """Test quality grade determination.""" assert self.ai_service._get_quality_grade(90) == 'excellent' assert self.ai_service._get_quality_grade(70) == 'good' assert self.ai_service._get_quality_grade(50) == 'fair' assert self.ai_service._get_quality_grade(30) == 'poor' def test_generate_quality_feedback(self): """Test quality feedback generation.""" rule_text = "Move emails from 'boss@company.com' to this folder" folder_name = "Work" score = 85 feedback = self.ai_service._generate_quality_feedback(rule_text, folder_name, score) assert isinstance(feedback, str) assert len(feedback) > 0 def test_get_fallback_rule(self): """Test fallback rule generation.""" rule = self.ai_service.get_fallback_rule('Work', 'destination') assert isinstance(rule, str) assert len(rule) > 0 assert 'Work' in rule def test_cache_key_generation(self): """Test cache key generation.""" # Access the static method directly since it's not a bound method from app.ai_service import AIService key1 = AIService.generate_cache_key('Work', 'destination', 'single') key2 = AIService.generate_cache_key('Work', 'destination', 'single') key3 = AIService.generate_cache_key('Personal', 'destination', 'single') # Same inputs should produce same key assert key1 == key2 # Different inputs should produce different keys assert key1 != key3 def test_parse_multiple_rules_response(self): """Test parsing of multiple rules response.""" response_text = ''' { "rules": [ { "text": "Move emails from 'boss@company.com' to this folder", "criteria": "Filters emails from specific sender" }, { "text": "Move emails with 'urgent' in subject to this folder", "criteria": "Filters emails with urgent keywords" } ] } ''' rules = self.ai_service._parse_multiple_rules_response(response_text) assert len(rules) == 2 assert rules[0]['text'] == "Move emails from 'boss@company.com' to this folder" assert rules[0]['criteria'] == "Filters emails from specific sender" assert rules[1]['text'] == "Move emails with 'urgent' in subject to this folder" assert rules[1]['criteria'] == "Filters emails with urgent keywords" def test_parse_multiple_rules_response_manual(self): """Test manual parsing of multiple rules response.""" # Test with a more structured format that matches what the parser expects response_text = '''{ "rules": [ { "text": "Move emails from 'boss@company.com' to this folder", "criteria": "Filters emails from specific sender" }, { "text": "Move emails with 'urgent' in subject to this folder", "criteria": "Filters emails with urgent keywords" } ] }''' rules = self.ai_service._parse_multiple_rules_response(response_text) # Should parse JSON format correctly assert len(rules) == 2 assert rules[0]['text'] == "Move emails from 'boss@company.com' to this folder" assert rules[0]['criteria'] == "Filters emails from specific sender" assert rules[1]['text'] == "Move emails with 'urgent' in subject to this folder" assert rules[1]['criteria'] == "Filters emails with urgent keywords" def test_short_rule_penalty(self): """Test that short rules get penalized.""" rule_text = "short" folder_name = "Work" score = self.ai_service._assess_rule_quality(rule_text, folder_name, 'destination') # Short rules should get low scores assert score < 50 def test_long_rule_penalty(self): """Test that very long rules get penalized.""" rule_text = "This is a very long rule that exceeds the optimal length and should be penalized accordingly" folder_name = "Work" score = self.ai_service._assess_rule_quality(rule_text, folder_name, 'destination') # Very long rules should get lower scores (should be <= 80) assert score <= 80 def test_specific_keyword_bonus(self): """Test that specific keywords get bonus points.""" rule_text = "Move emails from 'boss@company.com' to this folder" folder_name = "Work" score = self.ai_service._assess_rule_quality(rule_text, folder_name, 'destination') # Rules with specific keywords should get higher scores assert score > 50 def test_action_word_bonus(self): """Test that action words get bonus points.""" rule_text = "Move emails from 'boss@company.com' to this folder" folder_name = "Work" score = self.ai_service._assess_rule_quality(rule_text, folder_name, 'destination') # Rules with action words should get higher scores assert score > 50 def test_folder_relevance_bonus(self): """Test that folder name relevance gets bonus points.""" rule_text = "Move emails related to 'Work' projects to this folder" folder_name = "Work" score = self.ai_service._assess_rule_quality(rule_text, folder_name, 'destination') # Rules relevant to folder name should get higher scores assert score > 50