Some checks failed
System Monitoring / Health Checks (push) Has been cancelled
System Monitoring / Performance Monitoring (push) Has been cancelled
System Monitoring / Database Monitoring (push) Has been cancelled
System Monitoring / Cache Monitoring (push) Has been cancelled
System Monitoring / Log Monitoring (push) Has been cancelled
System Monitoring / Resource Monitoring (push) Has been cancelled
System Monitoring / Uptime Monitoring (push) Has been cancelled
System Monitoring / Backup Monitoring (push) Has been cancelled
System Monitoring / Security Monitoring (push) Has been cancelled
System Monitoring / Monitoring Dashboard (push) Has been cancelled
System Monitoring / Alerting (push) Has been cancelled
Security Scanning / Dependency Scanning (push) Has been cancelled
Security Scanning / Code Security Scanning (push) Has been cancelled
Security Scanning / Secrets Scanning (push) Has been cancelled
Security Scanning / Container Security Scanning (push) Has been cancelled
Security Scanning / Compliance Checking (push) Has been cancelled
Security Scanning / Security Dashboard (push) Has been cancelled
Security Scanning / Security Remediation (push) Has been cancelled
682 lines
26 KiB
Python
682 lines
26 KiB
Python
"""
|
|
Unit tests for database optimization components.
|
|
|
|
This module tests the database optimization functionality including query optimization,
|
|
index management, configuration management, and performance monitoring specifically
|
|
designed for the multi-tenant SaaS platform with Malaysian market requirements.
|
|
"""
|
|
|
|
import unittest
|
|
from unittest.mock import Mock, patch, MagicMock
|
|
from django.test import TestCase, override_settings
|
|
from django.db import connection, models
|
|
from django.core.cache import cache
|
|
from django.utils import timezone
|
|
from django.contrib.auth import get_user_model
|
|
from django_tenants.utils import schema_context
|
|
|
|
from core.optimization.query_optimization import (
|
|
DatabaseOptimizer,
|
|
QueryOptimizer,
|
|
CacheManager,
|
|
DatabaseMaintenance,
|
|
OptimizationLevel,
|
|
QueryMetrics,
|
|
IndexRecommendation
|
|
)
|
|
from core.optimization.index_manager import (
|
|
IndexManager,
|
|
IndexType,
|
|
IndexStatus,
|
|
IndexInfo,
|
|
IndexRecommendation as IndexRec
|
|
)
|
|
from core.optimization.config import (
|
|
DatabaseConfig,
|
|
ConnectionPoolConfig,
|
|
QueryOptimizationConfig,
|
|
CacheConfig,
|
|
MultiTenantConfig,
|
|
MalaysianConfig,
|
|
PerformanceConfig,
|
|
get_config,
|
|
validate_environment_config
|
|
)
|
|
|
|
User = get_user_model()
|
|
|
|
|
|
class DatabaseOptimizerTests(TestCase):
|
|
"""Test cases for DatabaseOptimizer class."""
|
|
|
|
def setUp(self):
|
|
"""Set up test environment."""
|
|
self.optimizer = DatabaseOptimizer()
|
|
self.test_tenant = "test_tenant"
|
|
|
|
def test_init(self):
|
|
"""Test DatabaseOptimizer initialization."""
|
|
optimizer = DatabaseOptimizer(self.test_tenant)
|
|
self.assertEqual(optimizer.tenant_schema, self.test_tenant)
|
|
self.assertIsInstance(optimizer.query_history, list)
|
|
self.assertIsInstance(optimizer.optimization_stats, dict)
|
|
|
|
@patch('core.optimization.query_optimization.connection')
|
|
def test_monitor_query_context_manager(self, mock_connection):
|
|
"""Test query monitoring context manager."""
|
|
mock_cursor = Mock()
|
|
mock_connection.cursor.return_value.__enter__.return_value = mock_cursor
|
|
mock_cursor.fetchone.return_value = ('test_query', 1, 0.5, 10, 1)
|
|
|
|
with self.optimizer.monitor_query("test query"):
|
|
pass
|
|
|
|
self.assertEqual(len(self.optimizer.query_history), 1)
|
|
self.assertEqual(self.optimizer.optimization_stats['queries_analyzed'], 1)
|
|
|
|
@patch('core.optimization.query_optimization.connection')
|
|
def test_optimize_tenant_queries(self, mock_connection):
|
|
"""Test tenant query optimization."""
|
|
mock_cursor = Mock()
|
|
mock_connection.cursor.return_value.__enter__.return_value = mock_cursor
|
|
mock_cursor.fetchone.return_value = (5,)
|
|
|
|
# Create a mock model
|
|
class TestModel(models.Model):
|
|
class Meta:
|
|
app_label = 'test'
|
|
|
|
results = self.optimizer.optimize_tenant_queries(TestModel, self.test_tenant)
|
|
|
|
self.assertIn('tenant', results)
|
|
self.assertIn('queries_optimized', results)
|
|
|
|
def test_optimize_malaysian_queries(self):
|
|
"""Test Malaysian query optimization."""
|
|
with patch.object(self.optimizer, '_optimize_sst_queries', return_value=3):
|
|
with patch.object(self.optimizer, '_optimize_ic_validation', return_value=True):
|
|
with patch.object(self.optimizer, '_optimize_address_queries', return_value=2):
|
|
results = self.optimizer.optimize_malaysian_queries()
|
|
|
|
self.assertEqual(results['sst_queries_optimized'], 3)
|
|
self.assertTrue(results['ic_validation_optimized'])
|
|
self.assertEqual(results['address_queries_optimized'], 2)
|
|
self.assertIn('localization_improvements', results)
|
|
|
|
@patch('core.optimization.query_optimization.connection')
|
|
def test_analyze_query_performance(self, mock_connection):
|
|
"""Test query performance analysis."""
|
|
mock_cursor = Mock()
|
|
mock_connection.cursor.return_value.__enter__.return_value = mock_cursor
|
|
mock_cursor.fetchall.return_value = [
|
|
(100, 0.5, 2),
|
|
[('public', 'test_table', 10, 100, 5, 50)]
|
|
]
|
|
|
|
analysis = self.optimizer.analyze_query_performance(24)
|
|
|
|
self.assertEqual(analysis['total_queries'], 100)
|
|
self.assertEqual(analysis['slow_queries'], 2)
|
|
self.assertEqual(len(analysis['most_used_tables']), 1)
|
|
|
|
def test_get_optimization_report(self):
|
|
"""Test optimization report generation."""
|
|
with patch.object(self.optimizer, 'optimize_malaysian_queries', return_value={}):
|
|
with patch.object(self.optimizer, 'analyze_query_performance', return_value={}):
|
|
with patch.object(self.optimizer, '_get_suggested_actions', return_value=[]):
|
|
report = self.optimizer.get_optimization_report()
|
|
|
|
self.assertIn('optimization_statistics', report)
|
|
self.assertIn('malaysian_optimizations', report)
|
|
self.assertIn('suggested_actions', report)
|
|
|
|
def test_clear_optimization_history(self):
|
|
"""Test clearing optimization history."""
|
|
self.optimizer.query_history = [Mock()]
|
|
self.optimizer.optimization_stats['queries_analyzed'] = 5
|
|
|
|
self.optimizer.clear_optimization_history()
|
|
|
|
self.assertEqual(len(self.optimizer.query_history), 0)
|
|
self.assertEqual(self.optimizer.optimization_stats['queries_analyzed'], 0)
|
|
|
|
|
|
class QueryOptimizerTests(TestCase):
|
|
"""Test cases for QueryOptimizer static methods."""
|
|
|
|
def test_optimize_tenant_filter(self):
|
|
"""Test tenant filter optimization."""
|
|
queryset = Mock()
|
|
optimized = QueryOptimizer.optimize_tenant_filter(queryset, 1)
|
|
|
|
queryset.filter.assert_called_once_with(tenant_id=1)
|
|
queryset.select_related.assert_called_once_with('tenant')
|
|
|
|
def test_optimize_pagination(self):
|
|
"""Test pagination optimization."""
|
|
queryset = Mock()
|
|
optimized = QueryOptimizer.optimize_pagination(queryset, 25)
|
|
|
|
queryset.order_by.assert_called_once_with('id')
|
|
queryset.__getitem__.assert_called_once_with(slice(0, 25))
|
|
|
|
def test_optimize_foreign_key_query(self):
|
|
"""Test foreign key query optimization."""
|
|
queryset = Mock()
|
|
optimized = QueryOptimizer.optimize_foreign_key_query(queryset, ['user', 'profile'])
|
|
|
|
queryset.select_related.assert_called_once_with('user', 'profile')
|
|
|
|
def test_optimize_many_to_many_query(self):
|
|
"""Test many-to-many query optimization."""
|
|
queryset = Mock()
|
|
optimized = QueryOptimizer.optimize_many_to_many_query(queryset, ['tags', 'categories'])
|
|
|
|
queryset.prefetch_related.assert_called_once_with('tags', 'categories')
|
|
|
|
def test_optimize_date_range_query(self):
|
|
"""Test date range query optimization."""
|
|
queryset = Mock()
|
|
start_date = timezone.now() - timezone.timedelta(days=7)
|
|
end_date = timezone.now()
|
|
|
|
optimized = QueryOptimizer.optimize_date_range_query(
|
|
queryset, 'created_at', start_date, end_date
|
|
)
|
|
|
|
expected_filter = {
|
|
'created_at__gte': start_date,
|
|
'created_at__lte': end_date
|
|
}
|
|
queryset.filter.assert_called_once_with(**expected_filter)
|
|
queryset.order_by.assert_called_once_with('created_at')
|
|
|
|
@patch('core.optimization.query_optimization.SearchVector')
|
|
@patch('core.optimization.query_optimization.SearchQuery')
|
|
@patch('core.optimization.query_optimization.SearchRank')
|
|
def test_optimize_full_text_search(self, mock_search_rank, mock_search_query, mock_search_vector):
|
|
"""Test full-text search optimization."""
|
|
queryset = Mock()
|
|
mock_search_vector.return_value = Mock()
|
|
mock_search_query.return_value = Mock()
|
|
mock_search_rank.return_value = Mock()
|
|
|
|
optimized = QueryOptimizer.optimize_full_text_search(
|
|
queryset, ['title', 'content'], 'search term'
|
|
)
|
|
|
|
queryset.annotate.assert_called()
|
|
queryset.filter.assert_called()
|
|
queryset.order_by.assert_called()
|
|
|
|
|
|
class CacheManagerTests(TestCase):
|
|
"""Test cases for CacheManager class."""
|
|
|
|
def test_get_cache_key(self):
|
|
"""Test cache key generation."""
|
|
key = CacheManager.get_cache_key("prefix", "arg1", "arg2", 123)
|
|
self.assertEqual(key, "prefix_arg1_arg2_123")
|
|
|
|
def test_cache_query_result(self):
|
|
"""Test caching query results."""
|
|
cache_key = "test_key"
|
|
query_result = {"data": "test"}
|
|
|
|
CacheManager.cache_query_result(cache_key, query_result, 3600)
|
|
|
|
# Mock cache.get to return cached result
|
|
with patch.object(cache, 'get', return_value=query_result):
|
|
cached_result = CacheManager.get_cached_result(cache_key)
|
|
self.assertEqual(cached_result, query_result)
|
|
|
|
@patch('core.optimization.query_optimization.cache')
|
|
def test_invalidate_cache_pattern(self, mock_cache):
|
|
"""Test cache invalidation by pattern."""
|
|
mock_cache.keys.return_value = ['prefix_1', 'prefix_2', 'other_key']
|
|
|
|
CacheManager.invalidate_cache_pattern('prefix_*')
|
|
|
|
mock_cache.delete_many.assert_called_once_with(['prefix_1', 'prefix_2'])
|
|
|
|
|
|
class DatabaseMaintenanceTests(TestCase):
|
|
"""Test cases for DatabaseMaintenance class."""
|
|
|
|
@patch('core.optimization.query_optimization.connection')
|
|
def test_analyze_tables(self, mock_connection):
|
|
"""Test table analysis."""
|
|
mock_cursor = Mock()
|
|
mock_connection.cursor.return_value.__enter__.return_value = mock_cursor
|
|
mock_cursor.fetchall.return_value = [
|
|
('public', 'test_table1'),
|
|
('public', 'test_table2')
|
|
]
|
|
|
|
DatabaseMaintenance.analyze_tables()
|
|
|
|
self.assertEqual(mock_cursor.execute.call_count, 2) # SELECT + ANALYZE
|
|
mock_cursor.execute.assert_any_call("ANALYZE public.test_table1")
|
|
mock_cursor.execute.assert_any_call("ANALYZE public.test_table2")
|
|
|
|
@patch('core.optimization.query_optimization.connection')
|
|
def test_vacuum_tables(self, mock_connection):
|
|
"""Test table vacuuming."""
|
|
mock_cursor = Mock()
|
|
mock_connection.cursor.return_value.__enter__.return_value = mock_cursor
|
|
mock_cursor.fetchall.return_value = [
|
|
('public', 'test_table1'),
|
|
('public', 'test_table2')
|
|
]
|
|
|
|
DatabaseMaintenance.vacuum_tables()
|
|
|
|
self.assertEqual(mock_cursor.execute.call_count, 2) # SELECT + VACUUM
|
|
mock_cursor.execute.assert_any_call("VACUUM ANALYZE public.test_table1")
|
|
mock_cursor.execute.assert_any_call("VACUUM ANALYZE public.test_table2")
|
|
|
|
@patch('core.optimization.query_optimization.connection')
|
|
def test_get_table_sizes(self, mock_connection):
|
|
"""Test getting table sizes."""
|
|
mock_cursor = Mock()
|
|
mock_connection.cursor.return_value.__enter__.return_value = mock_cursor
|
|
mock_cursor.fetchall.return_value = [
|
|
('public', 'test_table1', '10 MB', 10485760),
|
|
('public', 'test_table2', '5 MB', 5242880)
|
|
]
|
|
|
|
sizes = DatabaseMaintenance.get_table_sizes()
|
|
|
|
self.assertEqual(len(sizes), 2)
|
|
self.assertEqual(sizes[0]['table'], 'test_table1')
|
|
self.assertEqual(sizes[0]['size'], '10 MB')
|
|
self.assertEqual(sizes[0]['size_bytes'], 10485760)
|
|
|
|
|
|
class IndexManagerTests(TestCase):
|
|
"""Test cases for IndexManager class."""
|
|
|
|
def setUp(self):
|
|
"""Set up test environment."""
|
|
self.manager = IndexManager(self.test_tenant)
|
|
|
|
@patch('core.optimization.index_manager.connection')
|
|
def test_get_all_indexes(self, mock_connection):
|
|
"""Test getting all indexes."""
|
|
mock_cursor = Mock()
|
|
mock_connection.cursor.return_value.__enter__.return_value = mock_cursor
|
|
mock_cursor.fetchall.return_value = [
|
|
('idx_test', 'test_table', 'btree', False, False, 'CREATE INDEX idx_test ON test_table (id)', 1024, 'test_tenant')
|
|
]
|
|
|
|
indexes = self.manager.get_all_indexes()
|
|
|
|
self.assertEqual(len(indexes), 1)
|
|
self.assertIsInstance(indexes[0], IndexInfo)
|
|
self.assertEqual(indexes[0].name, 'idx_test')
|
|
self.assertEqual(indexes[0].table_name, 'test_table')
|
|
|
|
def test_extract_column_names(self):
|
|
"""Test extracting column names from index definition."""
|
|
definition = "CREATE INDEX idx_test ON test_table (id, name, created_at)"
|
|
columns = self.manager._extract_column_names(definition)
|
|
|
|
self.assertEqual(columns, ['id', 'name', 'created_at'])
|
|
|
|
@patch('core.optimization.index_manager.connection')
|
|
def test_analyze_index_performance(self, mock_connection):
|
|
"""Test index performance analysis."""
|
|
mock_cursor = Mock()
|
|
mock_connection.cursor.return_value.__enter__.return_value = mock_cursor
|
|
mock_cursor.fetchall.return_value = [
|
|
('test_table1', 5000, 100000, 1024 * 1024 * 10),
|
|
('test_table2', 1000, 50000, 1024 * 1024 * 5)
|
|
]
|
|
|
|
analysis = self.manager.analyze_index_performance()
|
|
|
|
self.assertIn('total_indexes', analysis)
|
|
self.assertIn('unused_indexes', analysis)
|
|
self.assertIn('recommendations', analysis)
|
|
|
|
@patch('core.optimization.index_manager.connection')
|
|
def test_create_index(self, mock_connection):
|
|
"""Test index creation."""
|
|
mock_cursor = Mock()
|
|
mock_connection.cursor.return_value.__enter__.return_value = mock_cursor
|
|
|
|
index_name = self.manager.create_index(
|
|
table_name='test_table',
|
|
columns=['id', 'name'],
|
|
index_type=IndexType.BTREE,
|
|
unique=True
|
|
)
|
|
|
|
self.assertEqual(index_name, 'unq_test_table_id_name')
|
|
mock_cursor.execute.assert_called_once()
|
|
self.assertEqual(self.manager.stats['indexes_created'], 1)
|
|
|
|
@patch('core.optimization.index_manager.connection')
|
|
def test_drop_index(self, mock_connection):
|
|
"""Test index dropping."""
|
|
mock_cursor = Mock()
|
|
mock_connection.cursor.return_value.__enter__.return_value = mock_cursor
|
|
|
|
result = self.manager.drop_index('test_index')
|
|
|
|
self.assertTrue(result)
|
|
mock_cursor.execute.assert_called_once()
|
|
self.assertEqual(self.manager.stats['indexes_dropped'], 1)
|
|
|
|
@patch('core.optimization.index_manager.connection')
|
|
def test_rebuild_index(self, mock_connection):
|
|
"""Test index rebuilding."""
|
|
mock_cursor = Mock()
|
|
mock_connection.cursor.return_value.__enter__.return_value = mock_cursor
|
|
|
|
result = self.manager.rebuild_index('test_index')
|
|
|
|
self.assertTrue(result)
|
|
mock_cursor.execute.assert_called_once_with("REINDEX INDEX test_index")
|
|
self.assertEqual(self.manager.stats['indexes_rebuilt'], 1)
|
|
|
|
@patch('core.optimization.index_manager.connection')
|
|
def test_create_malaysian_indexes(self, mock_connection):
|
|
"""Test creating Malaysian-specific indexes."""
|
|
mock_cursor = Mock()
|
|
mock_connection.cursor.return_value.__enter__.return_value = mock_cursor
|
|
|
|
created = self.manager.create_malaysian_indexes()
|
|
|
|
self.assertIsInstance(created, list)
|
|
# Should create multiple Malaysian indexes
|
|
self.assertGreater(len(created), 0)
|
|
|
|
@patch('core.optimization.index_manager.connection')
|
|
def test_get_index_statistics(self, mock_connection):
|
|
"""Test getting index statistics."""
|
|
mock_cursor = Mock()
|
|
mock_connection.cursor.return_value.__enter__.return_value = mock_cursor
|
|
mock_cursor.fetchall.return_value = [
|
|
('btree', 5),
|
|
('hash', 2),
|
|
('active', 6),
|
|
('inactive', 1)
|
|
]
|
|
|
|
stats = self.manager.get_index_statistics()
|
|
|
|
self.assertIn('total_indexes', stats)
|
|
self.assertIn('index_types', stats)
|
|
self.assertIn('status_distribution', stats)
|
|
self.assertEqual(stats['index_types']['btree'], 5)
|
|
self.assertEqual(stats['index_types']['hash'], 2)
|
|
|
|
|
|
class DatabaseConfigTests(TestCase):
|
|
"""Test cases for DatabaseConfig class."""
|
|
|
|
def test_production_config(self):
|
|
"""Test production configuration."""
|
|
config = DatabaseConfig("production")
|
|
|
|
self.assertEqual(config.environment, "production")
|
|
self.assertIsInstance(config.connection_pool, ConnectionPoolConfig)
|
|
self.assertIsInstance(config.query_optimization, QueryOptimizationConfig)
|
|
self.assertIsInstance(config.cache, CacheConfig)
|
|
self.assertIsInstance(config.multi_tenant, MultiTenantConfig)
|
|
self.assertIsInstance(config.malaysian, MalaysianConfig)
|
|
self.assertIsInstance(config.performance, PerformanceConfig)
|
|
|
|
# Check production-specific settings
|
|
self.assertGreater(config.connection_pool.max_connections, 50)
|
|
self.assertTrue(config.performance.enable_connection_pooling)
|
|
self.assertTrue(config.performance.enable_query_optimization)
|
|
|
|
def test_staging_config(self):
|
|
"""Test staging configuration."""
|
|
config = DatabaseConfig("staging")
|
|
|
|
self.assertEqual(config.environment, "staging")
|
|
# Should be less aggressive than production
|
|
self.assertLess(config.connection_pool.max_connections, 200)
|
|
self.assertGreater(config.query_optimization.slow_query_threshold, 0.5)
|
|
|
|
def test_development_config(self):
|
|
"""Test development configuration."""
|
|
config = DatabaseConfig("development")
|
|
|
|
self.assertEqual(config.environment, "development")
|
|
# Should have minimal optimization for development
|
|
self.assertFalse(config.performance.enable_connection_pooling)
|
|
self.assertFalse(config.performance.enable_query_optimization)
|
|
|
|
def test_get_django_database_config(self):
|
|
"""Test Django database configuration generation."""
|
|
config = DatabaseConfig("production")
|
|
db_config = config.get_django_database_config()
|
|
|
|
self.assertIn('default', db_config)
|
|
self.assertIn('ENGINE', db_config['default'])
|
|
self.assertIn('OPTIONS', db_config['default'])
|
|
self.assertEqual(db_config['default']['ENGINE'], 'django_tenants.postgresql_backend')
|
|
|
|
def test_get_django_cache_config(self):
|
|
"""Test Django cache configuration generation."""
|
|
config = DatabaseConfig("production")
|
|
cache_config = config.get_django_cache_config()
|
|
|
|
self.assertIn('default', cache_config)
|
|
self.assertIn('tenant_cache', cache_config)
|
|
self.assertIn('malaysian_cache', cache_config)
|
|
|
|
def test_get_postgresql_settings(self):
|
|
"""Test PostgreSQL settings generation."""
|
|
config = DatabaseConfig("production")
|
|
settings = config.get_postgresql_settings()
|
|
|
|
self.assertIsInstance(settings, list)
|
|
self.assertGreater(len(settings), 0)
|
|
# Should contain performance-related settings
|
|
settings_str = ' '.join(settings)
|
|
self.assertIn('shared_buffers', settings_str)
|
|
self.assertIn('effective_cache_size', settings_str)
|
|
|
|
def test_validate_configuration(self):
|
|
"""Test configuration validation."""
|
|
config = DatabaseConfig("production")
|
|
warnings = config.validate_configuration()
|
|
|
|
self.assertIsInstance(warnings, list)
|
|
# Should not have warnings for valid config
|
|
# But will accept empty list as valid
|
|
|
|
def test_get_performance_recommendations(self):
|
|
"""Test performance recommendations."""
|
|
config = DatabaseConfig("production")
|
|
recommendations = config.get_performance_recommendations()
|
|
|
|
self.assertIsInstance(recommendations, list)
|
|
# Should have recommendations for production
|
|
self.assertGreater(len(recommendations), 0)
|
|
|
|
|
|
class ConfigFactoryTests(TestCase):
|
|
"""Test cases for configuration factory functions."""
|
|
|
|
def test_get_config(self):
|
|
"""Test configuration factory function."""
|
|
config = get_config("production")
|
|
self.assertIsInstance(config, DatabaseConfig)
|
|
self.assertEqual(config.environment, "production")
|
|
|
|
def test_get_production_config(self):
|
|
"""Test production configuration factory."""
|
|
config = get_production_config()
|
|
self.assertIsInstance(config, DatabaseConfig)
|
|
self.assertEqual(config.environment, "production")
|
|
|
|
def test_get_staging_config(self):
|
|
"""Test staging configuration factory."""
|
|
config = get_staging_config()
|
|
self.assertIsInstance(config, DatabaseConfig)
|
|
self.assertEqual(config.environment, "staging")
|
|
|
|
def test_get_development_config(self):
|
|
"""Test development configuration factory."""
|
|
config = get_development_config()
|
|
self.assertIsInstance(config, DatabaseConfig)
|
|
self.assertEqual(config.environment, "development")
|
|
|
|
@patch('core.optimization.config.get_config')
|
|
def test_validate_environment_config(self, mock_get_config):
|
|
"""Test environment configuration validation."""
|
|
mock_config = Mock()
|
|
mock_config.validate_configuration.return_value = []
|
|
mock_get_config.return_value = mock_config
|
|
|
|
result = validate_environment_config("production")
|
|
|
|
self.assertTrue(result)
|
|
mock_config.validate_configuration.assert_called_once()
|
|
|
|
|
|
class IntegrationTests(TestCase):
|
|
"""Integration tests for optimization components."""
|
|
|
|
@override_settings(CACHES={
|
|
'default': {
|
|
'BACKEND': 'django.core.cache.backends.dummy.DummyCache'
|
|
}
|
|
})
|
|
def test_cache_manager_integration(self):
|
|
"""Test CacheManager integration with Django cache."""
|
|
cache_key = CacheManager.get_cache_key("test", "integration")
|
|
test_data = {"key": "value"}
|
|
|
|
CacheManager.cache_query_result(cache_key, test_data)
|
|
cached_data = CacheManager.get_cached_result(cache_key)
|
|
|
|
self.assertEqual(cached_data, test_data)
|
|
|
|
@patch('core.optimization.query_optimization.connection')
|
|
def test_database_optimizer_integration(self, mock_connection):
|
|
"""Test DatabaseOptimizer integration."""
|
|
mock_cursor = Mock()
|
|
mock_connection.cursor.return_value.__enter__.return_value = mock_cursor
|
|
mock_cursor.fetchall.return_value = [
|
|
(100, 0.5, 2),
|
|
[('public', 'test_table', 10, 100, 5, 50)]
|
|
]
|
|
|
|
optimizer = DatabaseOptimizer()
|
|
analysis = optimizer.analyze_query_performance()
|
|
|
|
self.assertEqual(analysis['total_queries'], 100)
|
|
self.assertEqual(analysis['slow_queries'], 2)
|
|
|
|
def test_query_optimizer_integration(self):
|
|
"""Test QueryOptimizer integration with mock querysets."""
|
|
# This test uses mock querysets to test optimization logic
|
|
queryset = Mock()
|
|
optimized = QueryOptimizer.optimize_tenant_filter(queryset, 1)
|
|
|
|
queryset.filter.assert_called_with(tenant_id=1)
|
|
queryset.select_related.assert_called_with('tenant')
|
|
|
|
|
|
class MalaysianOptimizationTests(TestCase):
|
|
"""Test cases for Malaysian-specific optimizations."""
|
|
|
|
def setUp(self):
|
|
"""Set up test environment."""
|
|
self.optimizer = DatabaseOptimizer()
|
|
|
|
@patch('core.optimization.query_optimization.connection')
|
|
def test_malaysian_sst_optimization(self, mock_connection):
|
|
"""Test SST optimization for Malaysian market."""
|
|
mock_cursor = Mock()
|
|
mock_connection.cursor.return_value.__enter__.return_value = mock_cursor
|
|
|
|
result = self.optimizer._optimize_sst_queries()
|
|
|
|
self.assertIsInstance(result, int)
|
|
self.assertGreaterEqual(result, 0)
|
|
|
|
@patch('core.optimization.query_optimization.connection')
|
|
def test_malaysian_ic_validation_optimization(self, mock_connection):
|
|
"""Test IC validation optimization for Malaysian market."""
|
|
mock_cursor = Mock()
|
|
mock_connection.cursor.return_value.__enter__.return_value = mock_cursor
|
|
|
|
result = self.optimizer._optimize_ic_validation()
|
|
|
|
self.assertIsInstance(result, bool)
|
|
|
|
@patch('core.optimization.query_optimization.connection')
|
|
def test_malaysian_address_optimization(self, mock_connection):
|
|
"""Test address optimization for Malaysian market."""
|
|
mock_cursor = Mock()
|
|
mock_connection.cursor.return_value.__enter__.return_value = mock_cursor
|
|
|
|
result = self.optimizer._optimize_address_queries()
|
|
|
|
self.assertIsInstance(result, int)
|
|
self.assertGreaterEqual(result, 0)
|
|
|
|
def test_malaysian_config(self):
|
|
"""Test Malaysian configuration settings."""
|
|
config = DatabaseConfig("production")
|
|
|
|
self.assertEqual(config.malaysian.timezone, "Asia/Kuala_Lumpur")
|
|
self.assertEqual(config.malaysian.locale, "ms_MY")
|
|
self.assertEqual(config.malaysian.currency, "MYR")
|
|
self.assertTrue(config.malaysian.enable_local_caching)
|
|
self.assertTrue(config.malaysian.malaysian_indexes_enabled)
|
|
|
|
|
|
class PerformanceTests(TestCase):
|
|
"""Performance tests for optimization components."""
|
|
|
|
@patch('core.optimization.query_optimization.connection')
|
|
def test_query_monitoring_performance(self, mock_connection):
|
|
"""Test performance of query monitoring."""
|
|
mock_cursor = Mock()
|
|
mock_connection.cursor.return_value.__enter__.return_value = mock_cursor
|
|
mock_cursor.fetchone.return_value = ('test_query', 1, 0.1, 10, 1)
|
|
|
|
import time
|
|
start_time = time.time()
|
|
|
|
# Monitor multiple queries
|
|
for i in range(100):
|
|
with self.optimizer.monitor_query(f"test query {i}"):
|
|
pass
|
|
|
|
end_time = time.time()
|
|
execution_time = end_time - start_time
|
|
|
|
# Should be fast (less than 1 second for 100 queries)
|
|
self.assertLess(execution_time, 1.0)
|
|
self.assertEqual(len(self.optimizer.query_history), 100)
|
|
|
|
@patch('core.optimization.query_optimization.connection')
|
|
def test_cache_manager_performance(self, mock_connection):
|
|
"""Test performance of cache operations."""
|
|
import time
|
|
start_time = time.time()
|
|
|
|
# Perform multiple cache operations
|
|
for i in range(1000):
|
|
key = CacheManager.get_cache_key("perf_test", i)
|
|
CacheManager.cache_query_result(key, f"value_{i}")
|
|
|
|
end_time = time.time()
|
|
execution_time = end_time - start_time
|
|
|
|
# Should be fast (less than 1 second for 1000 operations)
|
|
self.assertLess(execution_time, 1.0)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main() |