294 lines
8.7 KiB
Python
294 lines
8.7 KiB
Python
"""
|
|
Comprehensive test suite for EU-Utility
|
|
Run with: pytest tests/ -v
|
|
"""
|
|
|
|
import pytest
|
|
import sys
|
|
import os
|
|
|
|
# Add project to path
|
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
|
|
|
|
class TestCoreServices:
|
|
"""Test suite for core services."""
|
|
|
|
def test_event_bus_singleton(self):
|
|
"""Test EventBus is singleton."""
|
|
from core.event_bus import get_event_bus
|
|
bus1 = get_event_bus()
|
|
bus2 = get_event_bus()
|
|
assert bus1 is bus2
|
|
|
|
def test_event_publish_subscribe(self):
|
|
"""Test event publishing and subscribing."""
|
|
from core.event_bus import get_event_bus, SkillGainEvent
|
|
|
|
bus = get_event_bus()
|
|
received = []
|
|
|
|
def handler(event):
|
|
received.append(event)
|
|
|
|
sub_id = bus.subscribe_typed(SkillGainEvent, handler)
|
|
|
|
event = SkillGainEvent(skill_name="Rifle", skill_value=25.0, gain_amount=0.01)
|
|
bus.publish(event)
|
|
|
|
# Give async time to process
|
|
import time
|
|
time.sleep(0.1)
|
|
|
|
assert len(received) == 1
|
|
assert received[0].skill_name == "Rifle"
|
|
|
|
bus.unsubscribe(sub_id)
|
|
|
|
def test_nexus_api_singleton(self):
|
|
"""Test NexusAPI is singleton."""
|
|
from core.nexus_api import get_nexus_api
|
|
api1 = get_nexus_api()
|
|
api2 = get_nexus_api()
|
|
assert api1 is api2
|
|
|
|
def test_data_store_operations(self):
|
|
"""Test DataStore operations."""
|
|
from core.data_store import get_data_store
|
|
|
|
store = get_data_store()
|
|
|
|
# Test save and load
|
|
store.save("test_plugin", "test_key", {"value": 42})
|
|
data = store.load("test_plugin", "test_key", None)
|
|
|
|
assert data == {"value": 42}
|
|
|
|
# Test delete
|
|
store.delete("test_plugin", "test_key")
|
|
data = store.load("test_plugin", "test_key", None)
|
|
assert data is None
|
|
|
|
|
|
class TestPluginAPI:
|
|
"""Test suite for PluginAPI."""
|
|
|
|
def test_plugin_api_singleton(self):
|
|
"""Test PluginAPI is singleton."""
|
|
from core.plugin_api import get_api
|
|
api1 = get_api()
|
|
api2 = get_api()
|
|
assert api1 is api2
|
|
|
|
def test_api_registration(self):
|
|
"""Test API endpoint registration."""
|
|
from core.plugin_api import get_api, APIEndpoint, APIType
|
|
|
|
api = get_api()
|
|
|
|
def test_handler():
|
|
return "test"
|
|
|
|
endpoint = APIEndpoint(
|
|
name="test_api",
|
|
api_type=APIType.CUSTOM,
|
|
description="Test API",
|
|
handler=test_handler,
|
|
plugin_id="test_plugin",
|
|
version="1.0.0"
|
|
)
|
|
|
|
result = api.register_api(endpoint)
|
|
assert result is True
|
|
|
|
# Cleanup
|
|
api.unregister_api("test_plugin", "test_api")
|
|
|
|
|
|
class TestSecurity:
|
|
"""Test suite for security features."""
|
|
|
|
def test_path_traversal_protection(self):
|
|
"""Test path traversal is blocked."""
|
|
from core.security_utils import validate_path
|
|
|
|
# Valid path
|
|
assert validate_path("/safe/path/file.txt", "/safe/path") is True
|
|
|
|
# Path traversal attempt
|
|
assert validate_path("/safe/path/../../../etc/passwd", "/safe/path") is False
|
|
|
|
# Null byte injection
|
|
assert validate_path("/safe/path/file.txt\x00.jpg", "/safe/path") is False
|
|
|
|
def test_input_sanitization(self):
|
|
"""Test input sanitization."""
|
|
from core.security_utils import sanitize_input
|
|
|
|
# Script tag removal
|
|
assert "<script>" not in sanitize_input("<script>alert('xss')</script>")
|
|
|
|
# SQL injection prevention
|
|
assert "'" not in sanitize_input("'; DROP TABLE users; --")
|
|
|
|
|
|
class TestThemeManager:
|
|
"""Test suite for theme system."""
|
|
|
|
def test_theme_manager_singleton(self):
|
|
"""Test ThemeManager is singleton."""
|
|
from core.theme_manager import theme_manager
|
|
from core.theme_manager import ThemeManager
|
|
|
|
tm1 = ThemeManager()
|
|
tm2 = ThemeManager()
|
|
assert tm1 is tm2
|
|
|
|
def test_theme_getters(self):
|
|
"""Test theme retrieval."""
|
|
from core.theme_manager import theme_manager
|
|
|
|
dark_theme = theme_manager.get_theme('dark')
|
|
assert 'bg_primary' in dark_theme
|
|
assert 'accent' in dark_theme
|
|
|
|
light_theme = theme_manager.get_theme('light')
|
|
assert light_theme['bg_primary'] != dark_theme['bg_primary']
|
|
|
|
def test_stylesheet_generation(self):
|
|
"""Test QSS stylesheet generation."""
|
|
from core.theme_manager import theme_manager
|
|
|
|
stylesheet = theme_manager.get_stylesheet('dark')
|
|
assert 'background-color' in stylesheet
|
|
assert 'color' in stylesheet
|
|
|
|
|
|
class TestUtilities:
|
|
"""Test suite for utility functions."""
|
|
|
|
def test_ped_formatting(self):
|
|
"""Test PED formatting."""
|
|
from plugins.base_plugin import BasePlugin
|
|
|
|
class TestPlugin(BasePlugin):
|
|
name = "Test"
|
|
version = "1.0.0"
|
|
author = "Test"
|
|
description = "Test"
|
|
|
|
plugin = TestPlugin(None, {})
|
|
|
|
assert "PED" in plugin.format_ped(123.45)
|
|
assert "PEC" in plugin.format_pec(50)
|
|
|
|
def test_dpp_calculation(self):
|
|
"""Test DPP calculation."""
|
|
from plugins.base_plugin import BasePlugin
|
|
|
|
class TestPlugin(BasePlugin):
|
|
name = "Test"
|
|
version = "1.0.0"
|
|
author = "Test"
|
|
description = "Test"
|
|
|
|
plugin = TestPlugin(None, {})
|
|
|
|
# DPP = damage / ((ammo * 0.01 + decay) / 100)
|
|
dpp = plugin.calculate_dpp(50, 100, 2.5)
|
|
assert dpp > 0
|
|
|
|
def test_markup_calculation(self):
|
|
"""Test markup calculation."""
|
|
from plugins.base_plugin import BasePlugin
|
|
|
|
class TestPlugin(BasePlugin):
|
|
name = "Test"
|
|
version = "1.0.0"
|
|
author = "Test"
|
|
description = "Test"
|
|
|
|
plugin = TestPlugin(None, {})
|
|
|
|
markup = plugin.calculate_markup(150, 100)
|
|
assert markup == 150.0 # 150% markup
|
|
|
|
|
|
class TestPluginLoading:
|
|
"""Test suite for plugin loading."""
|
|
|
|
def test_plugin_discovery(self):
|
|
"""Test plugin discovery."""
|
|
import os
|
|
plugins_dir = os.path.join(os.path.dirname(__file__), '..', 'plugins')
|
|
|
|
if os.path.exists(plugins_dir):
|
|
plugin_dirs = [d for d in os.listdir(plugins_dir)
|
|
if os.path.isdir(os.path.join(plugins_dir, d))
|
|
and not d.startswith('_')]
|
|
|
|
# Should have multiple plugins
|
|
assert len(plugin_dirs) > 5
|
|
|
|
def test_plugin_structure(self):
|
|
"""Test plugin directory structure."""
|
|
import os
|
|
plugins_dir = os.path.join(os.path.dirname(__file__), '..', 'plugins')
|
|
|
|
if os.path.exists(plugins_dir):
|
|
for plugin_name in os.listdir(plugins_dir):
|
|
plugin_path = os.path.join(plugins_dir, plugin_name)
|
|
if os.path.isdir(plugin_path) and not plugin_name.startswith('_'):
|
|
# Check for required files
|
|
init_file = os.path.join(plugin_path, '__init__.py')
|
|
plugin_file = os.path.join(plugin_path, 'plugin.py')
|
|
|
|
if os.path.exists(plugin_file):
|
|
assert os.path.exists(init_file) or os.path.exists(plugin_file)
|
|
|
|
|
|
# Integration tests
|
|
@pytest.mark.integration
|
|
def test_full_plugin_lifecycle():
|
|
"""Integration test: full plugin lifecycle."""
|
|
from plugins.base_plugin import BasePlugin
|
|
|
|
lifecycle_events = []
|
|
|
|
class LifecyclePlugin(BasePlugin):
|
|
name = "LifecycleTest"
|
|
version = "1.0.0"
|
|
author = "Test"
|
|
description = "Test lifecycle"
|
|
|
|
def initialize(self):
|
|
lifecycle_events.append('initialize')
|
|
|
|
def get_ui(self):
|
|
lifecycle_events.append('get_ui')
|
|
return None
|
|
|
|
def on_show(self):
|
|
lifecycle_events.append('on_show')
|
|
|
|
def on_hide(self):
|
|
lifecycle_events.append('on_hide')
|
|
|
|
def shutdown(self):
|
|
lifecycle_events.append('shutdown')
|
|
|
|
# Test lifecycle
|
|
plugin = LifecyclePlugin(None, {})
|
|
plugin.initialize()
|
|
plugin.get_ui()
|
|
plugin.on_show()
|
|
plugin.on_hide()
|
|
plugin.shutdown()
|
|
|
|
assert 'initialize' in lifecycle_events
|
|
assert 'shutdown' in lifecycle_events
|
|
|
|
|
|
if __name__ == '__main__':
|
|
pytest.main([__file__, '-v'])
|