mirror of
https://github.com/aljazceru/enclava.git
synced 2025-12-17 07:24:34 +01:00
clean commit
This commit is contained in:
459
backend/tests/test_modules.py
Normal file
459
backend/tests/test_modules.py
Normal file
@@ -0,0 +1,459 @@
|
||||
"""
|
||||
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 both backend and modules directories to path
|
||||
backend_path = Path(__file__).parent.parent
|
||||
sys.path.insert(0, str(backend_path))
|
||||
sys.path.insert(0, str(backend_path / "modules"))
|
||||
|
||||
try:
|
||||
from modules.cache.main import CacheModule
|
||||
from modules.rag.main import RAGModule
|
||||
from modules.chatbot.main import ChatbotModule
|
||||
from modules.workflow.main import WorkflowModule
|
||||
|
||||
from app.services.module_manager import ModuleManager, ModuleConfig
|
||||
except ImportError as e:
|
||||
print(f"Import error: {e}")
|
||||
print("Available modules path:", backend_path / "modules")
|
||||
# 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}
|
||||
|
||||
CacheModule = MockModule
|
||||
RAGModule = MockModule
|
||||
ChatbotModule = MockModule
|
||||
WorkflowModule = MockModule
|
||||
|
||||
# Mock ModuleManager for testing
|
||||
class MockModuleManager:
|
||||
def __init__(self):
|
||||
self.initialized = False
|
||||
self.modules = {}
|
||||
self.module_order = ['cache', 'rag', 'chatbot', 'workflow']
|
||||
|
||||
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_cache_module_initialization(self):
|
||||
"""Test cache module initialization and basic operations"""
|
||||
cache_module = CacheModule()
|
||||
|
||||
# Test initialization
|
||||
result = await cache_module.initialize()
|
||||
assert result is True
|
||||
assert cache_module.initialized is True
|
||||
|
||||
# Test stats retrieval
|
||||
stats = cache_module.get_stats()
|
||||
assert isinstance(stats, dict)
|
||||
assert 'hits' in stats
|
||||
assert 'misses' in stats
|
||||
assert 'errors' in stats
|
||||
|
||||
# Test cleanup
|
||||
await cache_module.cleanup()
|
||||
|
||||
@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()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_workflow_module_initialization(self):
|
||||
"""Test workflow module initialization and basic operations"""
|
||||
workflow_module = WorkflowModule()
|
||||
|
||||
# Test initialization
|
||||
result = await workflow_module.initialize()
|
||||
assert result is True
|
||||
assert workflow_module.initialized is True
|
||||
|
||||
# Test stats retrieval
|
||||
stats = workflow_module.get_stats()
|
||||
assert isinstance(stats, dict)
|
||||
|
||||
await workflow_module.cleanup()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_signal_module_initialization(self):
|
||||
"""Test signal bot module initialization and basic operations"""
|
||||
try:
|
||||
from modules.signal.main import SignalBotModule
|
||||
except ImportError as e:
|
||||
# Skip test if SignalBot dependencies not available
|
||||
pytest.skip(f"Signal bot module dependencies not available: {e}")
|
||||
|
||||
signal_module = SignalBotModule()
|
||||
|
||||
# Test initialization (may fail if SignalBot not available, which is OK)
|
||||
try:
|
||||
result = await signal_module.initialize()
|
||||
assert signal_module.module_id == "signal"
|
||||
|
||||
# Test configuration
|
||||
assert hasattr(signal_module, 'config')
|
||||
assert hasattr(signal_module, 'stats')
|
||||
|
||||
# Test permissions
|
||||
permissions = signal_module.get_required_permissions()
|
||||
assert len(permissions) == 3
|
||||
assert any(p.resource == "signal" and p.action == "manage" for p in permissions)
|
||||
|
||||
except ImportError:
|
||||
# SignalBot library not available, skip functionality tests
|
||||
pytest.skip("SignalBot library not available")
|
||||
except Exception as e:
|
||||
# Other initialization errors are acceptable for testing
|
||||
logger.info(f"Signal module initialization failed (expected): {e}")
|
||||
|
||||
# Test cleanup
|
||||
try:
|
||||
await signal_module.cleanup()
|
||||
except Exception:
|
||||
pass # Cleanup errors are acceptable
|
||||
|
||||
|
||||
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 = ['cache', 'chatbot', 'rag', 'workflow', 'signal']
|
||||
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
|
||||
cache_module = module_manager.get_module('cache')
|
||||
assert cache_module is not None
|
||||
|
||||
rag_module = module_manager.get_module('rag')
|
||||
assert rag_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('cache')
|
||||
assert initial_module is not None
|
||||
|
||||
# Test reload
|
||||
await module_manager.reload_module('cache')
|
||||
|
||||
# Get module after reload
|
||||
reloaded_module = module_manager.get_module('cache')
|
||||
assert reloaded_module is not None
|
||||
|
||||
# Module should be reloaded (may be same object or different)
|
||||
assert module_manager.is_module_loaded('cache')
|
||||
|
||||
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()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_cache_module_performance(self):
|
||||
"""Test cache module performance"""
|
||||
cache_module = CacheModule()
|
||||
await cache_module.initialize()
|
||||
|
||||
# Test multiple rapid operations
|
||||
start_time = time.time()
|
||||
|
||||
for i in range(10):
|
||||
stats = cache_module.get_stats()
|
||||
assert isinstance(stats, dict)
|
||||
|
||||
operations_time = time.time() - start_time
|
||||
|
||||
# 10 operations should complete quickly
|
||||
assert operations_time < 1.0, f"Cache operations took {operations_time:.2f}s"
|
||||
|
||||
await cache_module.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()) >= 5
|
||||
|
||||
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 cache module...")
|
||||
await test_individual.test_cache_module_initialization()
|
||||
print("✓ Cache module test passed")
|
||||
|
||||
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")
|
||||
|
||||
print("Testing workflow module...")
|
||||
await test_individual.test_workflow_module_initialization()
|
||||
print("✓ Workflow module test passed")
|
||||
|
||||
print("Testing signal bot module...")
|
||||
await test_individual.test_signal_module_initialization()
|
||||
print("✓ Signal bot module 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())
|
||||
Reference in New Issue
Block a user