Files
enclava/backend/tests/test_modules.py
2025-10-30 06:12:35 +01:00

348 lines
11 KiB
Python

"""
Comprehensive test suite for all dynamic modules
Tests individual module functionality, integration, and hot-reload capabilities
"""
import pytest
import asyncio
import json
import time
from pathlib import Path
from unittest.mock import Mock, patch, MagicMock
from typing import Dict, Any
import tempfile
import os
# Import modules for testing
import sys
from pathlib import Path
# Add backend directory to Python path for app package imports
backend_path = Path(__file__).parent.parent
sys.path.insert(0, str(backend_path))
try:
from app.modules.rag.main import RAGModule
from app.modules.chatbot.main import ChatbotModule
from app.services.module_manager import ModuleManager, ModuleConfig
except ImportError as e:
print(f"Import error: {e}")
# Create mock modules for testing if imports fail
class MockModule:
def __init__(self):
self.name = "mock"
self.version = "1.0.0"
self.description = ""
self.initialized = False
async def initialize(self):
self.initialized = True
return True
async def cleanup(self):
self.initialized = False
def get_stats(self):
return {"mock": True}
RAGModule = MockModule
ChatbotModule = MockModule
# Mock ModuleManager for testing
class MockModuleManager:
def __init__(self):
self.initialized = False
self.modules = {}
self.module_order = ['rag', 'chatbot']
async def initialize(self):
self.initialized = True
async def cleanup(self):
self.initialized = False
def list_modules(self):
return self.module_order
def get_module(self, name):
return MockModule()
def is_module_loaded(self, name):
return True
async def reload_module(self, name):
pass
ModuleManager = MockModuleManager
class MockModuleConfig:
def __init__(self, name, enabled=True, config=None):
self.name = name
self.enabled = enabled
self.config = config or {}
ModuleConfig = MockModuleConfig
class TestModuleIndividual:
"""Test individual module functionality"""
@pytest.mark.asyncio
async def test_chatbot_module_initialization(self):
"""Test chatbot module initialization and basic operations"""
chatbot_module = ChatbotModule()
# Test initialization
result = await chatbot_module.initialize()
assert result is True
assert chatbot_module.initialized is True
# Test stats retrieval
stats = chatbot_module.get_stats()
assert isinstance(stats, dict)
await chatbot_module.cleanup()
@pytest.mark.asyncio
async def test_rag_module_initialization(self):
"""Test RAG module initialization with integrated content processing"""
rag_module = RAGModule()
# Test initialization
result = await rag_module.initialize()
assert result is True
assert rag_module.initialized is True
# Test content processing capabilities
stats = rag_module.get_stats()
assert stats['supported_types'] == 8 # Should support 8 file types
assert 'documents_processed' in stats
assert 'documents_indexed' in stats
await rag_module.cleanup()
class TestModuleIntegration:
"""Test module integration and interactions"""
@pytest.mark.asyncio
async def test_module_manager_initialization(self):
"""Test module manager loads all modules correctly"""
module_manager = ModuleManager()
# Test initialization
await module_manager.initialize()
assert module_manager.initialized is True
# Check all expected modules are loaded
expected_modules = ['chatbot', 'rag']
loaded_modules = module_manager.list_modules()
for module_name in expected_modules:
assert module_name in loaded_modules, f"Module {module_name} not loaded"
# Test module retrieval
rag_module = module_manager.get_module('rag')
assert rag_module is not None
chatbot_module = module_manager.get_module('chatbot')
assert chatbot_module is not None
await module_manager.cleanup()
@pytest.mark.asyncio
async def test_module_dependencies(self):
"""Test module dependency resolution"""
module_manager = ModuleManager()
await module_manager.initialize()
# Test that modules are loaded in correct order
# (This is implicit since no dependency errors should occur)
assert len(module_manager.module_order) >= 5
await module_manager.cleanup()
@pytest.mark.asyncio
async def test_module_stats_collection(self):
"""Test that all modules provide stats correctly"""
module_manager = ModuleManager()
await module_manager.initialize()
for module_name in module_manager.list_modules():
module = module_manager.get_module(module_name)
if hasattr(module, 'get_stats'):
stats = module.get_stats()
assert isinstance(stats, dict)
assert len(stats) > 0
await module_manager.cleanup()
class TestModuleHotReload:
"""Test hot-reload functionality"""
@pytest.mark.asyncio
async def test_module_reload(self):
"""Test module hot-reload functionality"""
module_manager = ModuleManager()
await module_manager.initialize()
# Get initial module reference
initial_module = module_manager.get_module('rag')
assert initial_module is not None
# Test reload
await module_manager.reload_module('rag')
# Get module after reload
reloaded_module = module_manager.get_module('rag')
assert reloaded_module is not None
# Module should be reloaded (may be same object or different)
assert module_manager.is_module_loaded('rag')
await module_manager.cleanup()
class TestModulePerformance:
"""Test module performance characteristics"""
@pytest.mark.asyncio
async def test_module_initialization_speed(self):
"""Test that modules initialize within reasonable time"""
start_time = time.time()
module_manager = ModuleManager()
await module_manager.initialize()
initialization_time = time.time() - start_time
# All modules should initialize within 30 seconds
assert initialization_time < 30.0, f"Module initialization took {initialization_time:.2f}s"
await module_manager.cleanup()
class TestModuleErrorHandling:
"""Test module error handling and recovery"""
@pytest.mark.asyncio
async def test_module_initialization_failure_recovery(self):
"""Test module manager handles initialization failures gracefully"""
module_manager = ModuleManager()
# This should not raise an exception even if some modules fail to load
await module_manager.initialize()
# At least some modules should load successfully
assert len(module_manager.list_modules()) >= 2
await module_manager.cleanup()
@pytest.mark.asyncio
async def test_module_cleanup_robustness(self):
"""Test that module cleanup handles errors gracefully"""
module_manager = ModuleManager()
await module_manager.initialize()
# This should not raise exceptions even if individual cleanups fail
await module_manager.cleanup()
# Manager should be properly shut down
assert module_manager.initialized is False
class TestModuleAPI:
"""Test module API endpoints and interactions"""
@pytest.mark.asyncio
async def test_modules_api_response_structure(self):
"""Test that module API responses have correct structure"""
module_manager = ModuleManager()
await module_manager.initialize()
# Simulate API response structure
modules_list = []
for module_name in module_manager.list_modules():
module = module_manager.get_module(module_name)
module_info = {
"name": module_name,
"version": getattr(module, 'version', '1.0.0'),
"description": getattr(module, 'description', ''),
"initialized": getattr(module, 'initialized', False),
"enabled": True
}
# Add stats if available
if hasattr(module, 'get_stats'):
module_info["stats"] = module.get_stats()
modules_list.append(module_info)
api_response = {
"total": len(modules_list),
"modules": modules_list,
"module_count": len(modules_list),
"initialized": True
}
# Verify response structure
assert isinstance(api_response["total"], int)
assert api_response["total"] >= 5 # Should have at least 5 modules
assert isinstance(api_response["modules"], list)
assert len(api_response["modules"]) == api_response["total"]
assert api_response["initialized"] is True
# Verify each module has required fields
for module_info in api_response["modules"]:
assert "name" in module_info
assert "version" in module_info
assert "description" in module_info
assert "initialized" in module_info
assert "enabled" in module_info
await module_manager.cleanup()
# Test configuration for pytest
@pytest.fixture(scope="session")
def event_loop():
"""Create an instance of the default event loop for the test session."""
loop = asyncio.get_event_loop_policy().new_event_loop()
yield loop
loop.close()
if __name__ == "__main__":
# Run basic test manually if executed directly
async def run_basic_tests():
print("Running basic module tests...")
# Test individual modules
test_individual = TestModuleIndividual()
print("Testing chatbot module...")
await test_individual.test_chatbot_module_initialization()
print("✓ Chatbot module test passed")
print("Testing RAG module with content processing...")
await test_individual.test_rag_module_initialization()
print("✓ RAG module with content processing test passed")
# Test integration
test_integration = TestModuleIntegration()
print("Testing module manager integration...")
await test_integration.test_module_manager_initialization()
print("✓ Module manager integration test passed")
print("\nAll basic tests completed successfully! 🎉")
# Run the tests
asyncio.run(run_basic_tests())