Some checks failed
CI - SharePoint Plugin with SonarQube / Test and SonarQube Analysis (push) Has been cancelled
287 lines
9.6 KiB
Python
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
|