Makes ai rule generation content work good.
This commit is contained in:
270
tests/integration/test_ai_rule_endpoints.py
Normal file
270
tests/integration/test_ai_rule_endpoints.py
Normal file
@@ -0,0 +1,270 @@
|
||||
import pytest
|
||||
from unittest.mock import patch, Mock
|
||||
from app import create_app, db
|
||||
from app.models import User, Folder, AIRuleCache
|
||||
from app.ai_service import AIService
|
||||
|
||||
class TestAIRuleEndpoints:
|
||||
"""Test cases for AI rule generation API endpoints."""
|
||||
|
||||
@pytest.fixture
|
||||
def app(self):
|
||||
"""Create and configure a test app."""
|
||||
app = create_app('testing')
|
||||
|
||||
with app.app_context():
|
||||
db.create_all()
|
||||
yield app
|
||||
db.drop_all()
|
||||
|
||||
@pytest.fixture
|
||||
def client(self, app):
|
||||
"""Create a test client."""
|
||||
return app.test_client()
|
||||
|
||||
@pytest.fixture
|
||||
def user(self, app):
|
||||
"""Create a test user."""
|
||||
with app.app_context():
|
||||
user = User(
|
||||
first_name='Test',
|
||||
last_name='User',
|
||||
email='test@example.com',
|
||||
password_hash='hashed_password'
|
||||
)
|
||||
db.session.add(user)
|
||||
db.session.commit()
|
||||
# Refresh the user to ensure it's attached to the session
|
||||
db.session.refresh(user)
|
||||
return user
|
||||
|
||||
@pytest.fixture
|
||||
def authenticated_client(self, client, user):
|
||||
"""Create a test client with authenticated user."""
|
||||
with client.session_transaction() as sess:
|
||||
sess['_user_id'] = str(user.id)
|
||||
sess['_fresh'] = True
|
||||
return client
|
||||
|
||||
def test_generate_rule_success(self, authenticated_client, user):
|
||||
"""Test successful rule generation."""
|
||||
with patch('app.routes.folders.ai_service') as mock_ai_service:
|
||||
# Mock AI service response
|
||||
mock_ai_service.generate_single_rule.return_value = (
|
||||
"Move emails from 'boss@company.com' to this folder",
|
||||
{'quality_score': 85, 'model_used': 'test-model'}
|
||||
)
|
||||
|
||||
response = authenticated_client.post('/api/folders/generate-rule', data={
|
||||
'name': 'Work',
|
||||
'folder_type': 'destination',
|
||||
'rule_type': 'single'
|
||||
})
|
||||
|
||||
assert response.status_code == 200
|
||||
# Check that the response contains HTML
|
||||
assert b'generated-rule-text' in response.data
|
||||
assert b'Move emails from' in response.data
|
||||
|
||||
def test_generate_rule_missing_folder_name(self, authenticated_client, user):
|
||||
"""Test rule generation with missing folder name."""
|
||||
response = authenticated_client.post('/api/folders/generate-rule', data={
|
||||
'folder_type': 'destination',
|
||||
'rule_type': 'single'
|
||||
})
|
||||
|
||||
assert response.status_code == 200
|
||||
# Should return error in HTML format
|
||||
assert b'Folder name is required' in response.data
|
||||
|
||||
def test_generate_rule_invalid_folder_type(self, authenticated_client, user):
|
||||
"""Test rule generation with invalid folder type."""
|
||||
response = authenticated_client.post('/api/folders/generate-rule', data={
|
||||
'name': 'Work',
|
||||
'folder_type': 'invalid',
|
||||
'rule_type': 'single'
|
||||
})
|
||||
|
||||
assert response.status_code == 200
|
||||
# Should return error in HTML format
|
||||
assert b'Invalid folder type' in response.data
|
||||
|
||||
def test_generate_rule_multiple_options(self, authenticated_client, user):
|
||||
"""Test multiple rule options generation."""
|
||||
with patch('app.routes.folders.ai_service') as mock_ai_service:
|
||||
# Mock AI service response
|
||||
mock_ai_service.generate_multiple_rules.return_value = (
|
||||
[
|
||||
{'text': 'Rule 1', 'quality_score': 85},
|
||||
{'text': 'Rule 2', 'quality_score': 75}
|
||||
],
|
||||
{'total_generated': 2}
|
||||
)
|
||||
|
||||
response = authenticated_client.post('/api/folders/generate-rule', data={
|
||||
'name': 'Work',
|
||||
'folder_type': 'destination',
|
||||
'rule_type': 'multiple'
|
||||
})
|
||||
|
||||
assert response.status_code == 200
|
||||
# Check that multiple rules are displayed
|
||||
assert b'Rule 1' in response.data
|
||||
assert b'Rule 2' in response.data
|
||||
|
||||
def test_generate_rule_ai_service_failure(self, authenticated_client, user):
|
||||
"""Test rule generation when AI service fails."""
|
||||
with patch('app.routes.folders.ai_service') as mock_ai_service:
|
||||
# Mock AI service failure
|
||||
mock_ai_service.generate_single_rule.return_value = (None, {'error': 'Service unavailable'})
|
||||
mock_ai_service.get_fallback_rule.return_value = 'Fallback rule'
|
||||
|
||||
response = authenticated_client.post('/api/folders/generate-rule', data={
|
||||
'name': 'Work',
|
||||
'folder_type': 'destination',
|
||||
'rule_type': 'single'
|
||||
})
|
||||
|
||||
assert response.status_code == 200
|
||||
# Should return fallback rule
|
||||
assert b'Fallback rule' in response.data
|
||||
assert b'AI service unavailable' in response.data
|
||||
|
||||
def test_assess_rule_success(self, authenticated_client, user):
|
||||
"""Test successful rule assessment."""
|
||||
with patch('app.routes.folders.ai_service') as mock_ai_service:
|
||||
# Mock AI service response
|
||||
mock_ai_service.assess_rule_quality.return_value = {
|
||||
'score': 85,
|
||||
'grade': 'good',
|
||||
'feedback': 'Good rule with room for improvement'
|
||||
}
|
||||
|
||||
response = authenticated_client.post('/api/folders/assess-rule', data={
|
||||
'rule_text': 'Move emails from boss@company.com to this folder',
|
||||
'folder_name': 'Work',
|
||||
'folder_type': 'destination'
|
||||
})
|
||||
|
||||
assert response.status_code == 200
|
||||
# Check that the response contains HTML
|
||||
assert b'generated-rule-text' in response.data
|
||||
assert b'85%' in response.data
|
||||
|
||||
def test_assess_rule_missing_inputs(self, authenticated_client, user):
|
||||
"""Test rule assessment with missing inputs."""
|
||||
response = authenticated_client.post('/api/folders/assess-rule', data={
|
||||
'folder_name': 'Work',
|
||||
'folder_type': 'destination'
|
||||
})
|
||||
|
||||
assert response.status_code == 200
|
||||
# Should return error in HTML format
|
||||
assert b'Rule text is required' in response.data
|
||||
|
||||
def test_cache_functionality(self, authenticated_client, user):
|
||||
"""Test rule caching functionality."""
|
||||
with patch('app.routes.folders.ai_service') as mock_ai_service:
|
||||
# Mock AI service response
|
||||
mock_ai_service.generate_single_rule.return_value = (
|
||||
"Cached rule",
|
||||
{'quality_score': 90}
|
||||
)
|
||||
|
||||
# First request - should generate new rule
|
||||
response1 = authenticated_client.post('/api/folders/generate-rule', data={
|
||||
'name': 'Work',
|
||||
'folder_type': 'destination',
|
||||
'rule_type': 'single'
|
||||
})
|
||||
|
||||
assert response1.status_code == 200
|
||||
assert b'Cached rule' in response1.data
|
||||
|
||||
# Verify cache entry was created
|
||||
cache_entry = AIRuleCache.query.filter_by(
|
||||
user_id=user.id,
|
||||
folder_name='Work',
|
||||
folder_type='destination'
|
||||
).first()
|
||||
assert cache_entry is not None
|
||||
assert cache_entry.rule_text == 'Cached rule'
|
||||
|
||||
# Second request - should use cached rule
|
||||
with patch('app.routes.folders.ai_service') as mock_ai_service:
|
||||
response2 = authenticated_client.post('/api/folders/generate-rule', data={
|
||||
'name': 'Work',
|
||||
'folder_type': 'destination',
|
||||
'rule_type': 'single'
|
||||
})
|
||||
|
||||
assert response2.status_code == 200
|
||||
assert b'Using cached rule' in response2.data
|
||||
|
||||
def test_cache_expiration(self, authenticated_client, user):
|
||||
"""Test cache expiration functionality."""
|
||||
with patch('app.routes.folders.ai_service') as mock_ai_service:
|
||||
# Mock AI service response
|
||||
mock_ai_service.generate_single_rule.return_value = (
|
||||
"Expired rule",
|
||||
{'quality_score': 90}
|
||||
)
|
||||
|
||||
# Create expired cache entry
|
||||
from datetime import datetime, timedelta
|
||||
expired_entry = AIRuleCache(
|
||||
user_id=user.id,
|
||||
folder_name='Work',
|
||||
folder_type='destination',
|
||||
rule_text='Expired rule',
|
||||
rule_metadata={'quality_score': 90},
|
||||
cache_key='test-key',
|
||||
expires_at=datetime.utcnow() - timedelta(hours=1),
|
||||
is_active=True
|
||||
)
|
||||
db.session.add(expired_entry)
|
||||
db.session.commit()
|
||||
|
||||
# Request should generate new rule despite cache entry
|
||||
response = authenticated_client.post('/api/folders/generate-rule', data={
|
||||
'name': 'Work',
|
||||
'folder_type': 'destination',
|
||||
'rule_type': 'single'
|
||||
})
|
||||
|
||||
assert response.status_code == 200
|
||||
# Should not show cached message
|
||||
assert b'Using cached rule' not in response.data
|
||||
|
||||
def test_unauthorized_access(self, client):
|
||||
"""Test unauthorized access to AI rule endpoints."""
|
||||
response = client.post('/api/folders/generate-rule', data={
|
||||
'folder_name': 'Work',
|
||||
'folder_type': 'destination',
|
||||
'rule_type': 'single'
|
||||
})
|
||||
|
||||
# Should be redirected to login
|
||||
assert response.status_code == 302
|
||||
|
||||
def test_database_error_handling(self, authenticated_client, user):
|
||||
"""Test handling of database errors."""
|
||||
with patch('app.routes.folders.db.session.commit') as mock_commit:
|
||||
# Mock database commit failure
|
||||
mock_commit.side_effect = Exception("Database error")
|
||||
|
||||
with patch('app.routes.folders.ai_service') as mock_ai_service:
|
||||
mock_ai_service.generate_single_rule.return_value = (
|
||||
"Test rule",
|
||||
{'quality_score': 85}
|
||||
)
|
||||
|
||||
response = authenticated_client.post('/api/folders/generate-rule', data={
|
||||
'name': 'Work',
|
||||
'folder_type': 'destination',
|
||||
'rule_type': 'single'
|
||||
})
|
||||
|
||||
assert response.status_code == 200
|
||||
# Should return error message
|
||||
assert b'An unexpected error occurred' in response.data
|
||||
Reference in New Issue
Block a user