Daniel Grozdanovic bcd0f8a227
Some checks failed
CI - SharePoint Plugin with SonarQube / Test and SonarQube Analysis (push) Has been cancelled
Initial commit: SharePoint connector and ToothFairyAI integration
2026-02-22 17:58:45 +02:00

287 lines
9.6 KiB
Python

"""
Unit tests for app.py Flask routes
"""
import pytest
import json
from unittest.mock import Mock, patch, MagicMock
from app import app
@pytest.fixture
def client():
"""Create a test client for the Flask app."""
app.config['TESTING'] = True
app.config['SECRET_KEY'] = 'test-secret-key'
with app.test_client() as client:
yield client
@pytest.fixture
def mock_session():
"""Create a mock session."""
return {"user_id": "test_user_123"}
class TestHealthCheck:
"""Test health check endpoint."""
def test_health_endpoint(self, client):
"""Test /health endpoint returns healthy status."""
response = client.get('/health')
assert response.status_code == 200
data = json.loads(response.data)
assert data['status'] == 'healthy'
class TestIndexRoute:
"""Test index route."""
def test_index_returns_template(self, client):
"""Test index route returns 200."""
with patch('app.render_template') as mock_render:
mock_render.return_value = '<html>Test</html>'
response = client.get('/')
assert response.status_code == 200
class TestConfigurationAPI:
"""Test configuration API endpoints."""
def test_check_config_not_configured(self, client):
"""Test check_config returns false when not configured."""
response = client.get('/api/config/check')
assert response.status_code == 200
data = json.loads(response.data)
assert 'configured' in data
def test_save_config_missing_credentials(self, client):
"""Test save_config fails without credentials."""
response = client.post(
'/api/config/save',
data=json.dumps({}),
content_type='application/json'
)
assert response.status_code == 400
data = json.loads(response.data)
assert 'error' in data
@patch('app.credentials_storage')
def test_save_config_success(self, mock_storage, client):
"""Test save_config succeeds with valid credentials."""
mock_storage.get_config.return_value = None
mock_storage.save_config.return_value = None
response = client.post(
'/api/config/save',
data=json.dumps({
'client_id': 'test-client-id',
'client_secret': 'test-client-secret',
'tenant_id': 'common'
}),
content_type='application/json'
)
assert response.status_code == 200
data = json.loads(response.data)
assert data['success'] is True
@patch('app.credentials_storage')
def test_reset_config(self, mock_storage, client):
"""Test reset_config clears configuration."""
mock_storage.delete_config.return_value = None
response = client.post('/api/config/reset')
assert response.status_code == 200
data = json.loads(response.data)
assert data['success'] is True
class TestSharePointAPI:
"""Test SharePoint API endpoints."""
def test_list_connections_not_configured(self, client):
"""Test list_connections fails without configuration."""
response = client.get('/api/sharepoint/connections')
assert response.status_code == 400
data = json.loads(response.data)
assert 'error' in data
def test_get_sites_missing_connection_id(self, client):
"""Test get_sites requires valid connection_id."""
response = client.get('/api/sharepoint/invalid_conn/sites')
assert response.status_code == 400
@patch('app.get_or_create_connector')
def test_get_files_missing_site_id(self, mock_connector, client):
"""Test get_files requires site_id parameter."""
mock_connector.return_value = Mock()
response = client.get('/api/sharepoint/test_conn/files')
assert response.status_code == 400
data = json.loads(response.data)
assert 'site_id is required' in data['error']
@patch('app.get_or_create_connector')
def test_read_file_missing_parameters(self, mock_connector, client):
"""Test read_file requires site_id and file_path."""
mock_connector.return_value = Mock()
response = client.get('/api/sharepoint/test_conn/read')
assert response.status_code == 400
data = json.loads(response.data)
assert 'required' in data['error']
class TestChatAPI:
"""Test chat API endpoints."""
def test_chat_send_missing_parameters(self, client):
"""Test chat_send requires all parameters."""
response = client.post(
'/api/chat/send',
data=json.dumps({}),
content_type='application/json'
)
assert response.status_code == 400
data = json.loads(response.data)
assert 'required' in data['error']
def test_chat_send_no_document_loaded(self, client):
"""Test chat_send fails without loaded document."""
response = client.post(
'/api/chat/send',
data=json.dumps({
'site_id': 'test_site',
'file_path': 'test.txt',
'message': 'Hello'
}),
content_type='application/json'
)
assert response.status_code == 400
data = json.loads(response.data)
assert 'No document loaded' in data['error']
def test_chat_history_missing_parameters(self, client):
"""Test chat_history requires parameters."""
response = client.get('/api/chat/history')
assert response.status_code == 400
data = json.loads(response.data)
assert 'required' in data['error']
def test_chat_clear_missing_parameters(self, client):
"""Test chat_clear requires parameters."""
response = client.post(
'/api/chat/clear',
data=json.dumps({}),
content_type='application/json'
)
assert response.status_code == 400
class TestLLMStatus:
"""Test LLM status endpoint."""
@patch('app.llm_client')
def test_llm_status_available(self, mock_llm, client):
"""Test LLM status returns available when service is up."""
mock_llm.is_available.return_value = True
response = client.get('/api/llm/status')
assert response.status_code == 200
data = json.loads(response.data)
assert data['available'] is True
assert 'provider' in data
@patch('app.llm_client')
def test_llm_status_unavailable(self, mock_llm, client):
"""Test LLM status returns unavailable when service is down."""
mock_llm.is_available.side_effect = Exception("Connection failed")
response = client.get('/api/llm/status')
assert response.status_code == 200
data = json.loads(response.data)
assert data['available'] is False
assert 'error' in data
class TestVectorStoreAPI:
"""Test vector store / multi-document chat API."""
@patch('app.vector_store', None)
def test_add_document_vector_store_unavailable(self, client):
"""Test add_document fails when vector store is unavailable."""
response = client.post(
'/api/documents/add',
data=json.dumps({}),
content_type='application/json'
)
assert response.status_code == 503
data = json.loads(response.data)
assert 'Vector store not available' in data['error']
@patch('app.vector_store', None)
def test_list_tags_vector_store_unavailable(self, client):
"""Test list_tags fails when vector store is unavailable."""
response = client.get('/api/documents/tags')
assert response.status_code == 503
@patch('app.vector_store', None)
def test_chat_multi_vector_store_unavailable(self, client):
"""Test chat_multi fails when vector store is unavailable."""
response = client.post(
'/api/chat/multi',
data=json.dumps({'message': 'test'}),
content_type='application/json'
)
assert response.status_code == 503
class TestIndexingAPI:
"""Test background indexing API."""
@patch('app.vector_store', None)
@patch('app.get_or_create_connector')
def test_start_indexing_vector_store_unavailable(self, mock_connector, client):
"""Test start_indexing fails without vector store."""
mock_connector.return_value = Mock()
response = client.post(
'/api/indexing/start',
data=json.dumps({}),
content_type='application/json'
)
assert response.status_code == 503
def test_get_indexing_status_not_found(self, client):
"""Test get_indexing_status returns 404 for unknown job."""
with patch('app.get_indexer') as mock_indexer:
mock_indexer.return_value.get_job_status.return_value = None
response = client.get('/api/indexing/status/unknown_job')
assert response.status_code == 404
class TestErrorHandlers:
"""Test error handlers."""
def test_404_handler(self, client):
"""Test 404 error handler."""
response = client.get('/nonexistent-route')
assert response.status_code == 404
data = json.loads(response.data)
assert 'error' in data
assert data['error'] == 'Not found'
class TestDecorators:
"""Test authentication decorators."""
def test_require_auth_creates_user_id(self, client):
"""Test require_auth decorator creates user_id in session."""
with client.session_transaction() as sess:
assert 'user_id' not in sess
response = client.get('/')
with client.session_transaction() as sess:
assert 'user_id' in sess