new folder structure
This commit is contained in:
1
tests/unit/__init__.py
Normal file
1
tests/unit/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# Unit test package
|
||||
593
tests/unit/core/test_core.py
Normal file
593
tests/unit/core/test_core.py
Normal file
@@ -0,0 +1,593 @@
|
||||
"""
|
||||
Unit Tests for Core Functionality
|
||||
|
||||
This module contains unit tests for the core components of the AniWorld application,
|
||||
including series management, download operations, and API functionality.
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
import shutil
|
||||
import sqlite3
|
||||
import json
|
||||
from unittest.mock import Mock, MagicMock, patch, call
|
||||
from datetime import datetime, timedelta
|
||||
import threading
|
||||
|
||||
# Add parent directory to path for imports
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..'))
|
||||
|
||||
# Import core modules
|
||||
from src.server.core.entities.series import Serie
|
||||
from src.server.core.entities.SerieList import SerieList
|
||||
from src.server.infrastructure.file_system.SerieScanner import SerieScanner
|
||||
# TODO: Fix imports - these modules may not exist or may be in different locations
|
||||
# from database_manager import DatabaseManager, AnimeMetadata, EpisodeMetadata, BackupManager
|
||||
# from error_handler import ErrorRecoveryManager, RetryMechanism, NetworkHealthChecker
|
||||
# from performance_optimizer import SpeedLimiter, DownloadCache, MemoryMonitor
|
||||
# from api_integration import WebhookManager, ExportManager
|
||||
|
||||
|
||||
class TestSerie(unittest.TestCase):
|
||||
"""Test cases for Serie class."""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test fixtures."""
|
||||
self.test_key = "test-key"
|
||||
self.test_name = "Test Anime"
|
||||
self.test_site = "test-site"
|
||||
self.test_folder = "test_folder"
|
||||
self.test_episodes = {1: [1], 2: [2]}
|
||||
|
||||
def test_serie_initialization(self):
|
||||
"""Test Serie object initialization."""
|
||||
serie = Serie(self.test_key, self.test_name, self.test_site, self.test_folder, self.test_episodes)
|
||||
|
||||
self.assertEqual(serie.key, self.test_key)
|
||||
self.assertEqual(serie.name, self.test_name)
|
||||
self.assertEqual(serie.site, self.test_site)
|
||||
self.assertEqual(serie.folder, self.test_folder)
|
||||
self.assertEqual(serie.episodeDict, self.test_episodes)
|
||||
|
||||
def test_serie_str_representation(self):
|
||||
"""Test string representation of Serie."""
|
||||
serie = Serie(self.test_key, self.test_name, self.test_site, self.test_folder, self.test_episodes)
|
||||
str_repr = str(serie)
|
||||
|
||||
self.assertIn(self.test_name, str_repr)
|
||||
self.assertIn(self.test_folder, str_repr)
|
||||
self.assertIn(self.test_key, str_repr)
|
||||
|
||||
def test_serie_episode_management(self):
|
||||
"""Test episode dictionary management."""
|
||||
serie = Serie(self.test_key, self.test_name, self.test_site, self.test_folder, self.test_episodes)
|
||||
|
||||
# Test episode dict
|
||||
self.assertEqual(len(serie.episodeDict), 2)
|
||||
self.assertIn(1, serie.episodeDict)
|
||||
self.assertIn(2, serie.episodeDict)
|
||||
|
||||
def test_serie_equality(self):
|
||||
"""Test Serie equality comparison."""
|
||||
serie1 = Serie(self.test_key, self.test_name, self.test_site, self.test_folder, self.test_episodes)
|
||||
serie2 = Serie(self.test_key, self.test_name, self.test_site, self.test_folder, self.test_episodes)
|
||||
serie3 = Serie("different-key", "Different", self.test_site, self.test_folder, self.test_episodes)
|
||||
|
||||
# Should be equal based on key attributes
|
||||
self.assertEqual(serie1.key, serie2.key)
|
||||
self.assertEqual(serie1.folder, serie2.folder)
|
||||
self.assertNotEqual(serie1.key, serie3.key)
|
||||
|
||||
|
||||
class TestSeriesList(unittest.TestCase):
|
||||
"""Test cases for SeriesList class."""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test fixtures."""
|
||||
self.temp_dir = tempfile.mkdtemp()
|
||||
self.series_list = SerieList(self.temp_dir)
|
||||
|
||||
def tearDown(self):
|
||||
"""Clean up test fixtures."""
|
||||
shutil.rmtree(self.temp_dir, ignore_errors=True)
|
||||
|
||||
def test_series_list_initialization(self):
|
||||
"""Test SerieList initialization."""
|
||||
self.assertIsInstance(self.series_list.folderDict, dict)
|
||||
self.assertEqual(len(self.series_list.folderDict), 0)
|
||||
|
||||
def test_add_serie_to_list(self):
|
||||
"""Test adding serie to list."""
|
||||
serie = Serie("test-key", "Test", "test-site", "test_folder", {})
|
||||
self.series_list.add(serie)
|
||||
|
||||
self.assertEqual(len(self.series_list.folderDict), 1)
|
||||
self.assertIn("test_folder", self.series_list.folderDict)
|
||||
|
||||
def test_contains_serie(self):
|
||||
"""Test checking if serie exists."""
|
||||
serie = Serie("test-key", "Test", "test-site", "test_folder", {})
|
||||
self.series_list.add(serie)
|
||||
|
||||
self.assertTrue(self.series_list.contains("test-key"))
|
||||
self.assertFalse(self.series_list.contains("nonexistent"))
|
||||
|
||||
def test_get_series_with_missing_episodes(self):
|
||||
"""Test filtering series with missing episodes."""
|
||||
serie1 = Serie("key1", "Anime 1", "test-site", "folder1", {1: [1], 2: [2]}) # Has missing episodes
|
||||
serie2 = Serie("key2", "Anime 2", "test-site", "folder2", {}) # No missing episodes
|
||||
|
||||
self.series_list.add(serie1)
|
||||
self.series_list.add(serie2)
|
||||
|
||||
missing = self.series_list.GetMissingEpisode()
|
||||
self.assertEqual(len(missing), 1)
|
||||
self.assertEqual(missing[0].name, "Anime 1")
|
||||
|
||||
|
||||
class TestDatabaseManager(unittest.TestCase):
|
||||
"""Test cases for DatabaseManager class."""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test database."""
|
||||
self.test_db = tempfile.NamedTemporaryFile(delete=False)
|
||||
self.test_db.close()
|
||||
self.db_manager = DatabaseManager(self.test_db.name)
|
||||
|
||||
def tearDown(self):
|
||||
"""Clean up test database."""
|
||||
self.db_manager.close()
|
||||
os.unlink(self.test_db.name)
|
||||
|
||||
def test_database_initialization(self):
|
||||
"""Test database initialization."""
|
||||
# Check if tables exist
|
||||
with self.db_manager.get_connection() as conn:
|
||||
cursor = conn.execute("""
|
||||
SELECT name FROM sqlite_master
|
||||
WHERE type='table' AND name='anime_metadata'
|
||||
""")
|
||||
result = cursor.fetchone()
|
||||
self.assertIsNotNone(result)
|
||||
|
||||
def test_schema_versioning(self):
|
||||
"""Test schema version management."""
|
||||
version = self.db_manager.get_current_version()
|
||||
self.assertIsInstance(version, int)
|
||||
self.assertGreater(version, 0)
|
||||
|
||||
def test_anime_crud_operations(self):
|
||||
"""Test anime CRUD operations."""
|
||||
# Create anime
|
||||
anime = AnimeMetadata(
|
||||
anime_id="test-123",
|
||||
name="Test Anime",
|
||||
folder="test_folder",
|
||||
key="test-key"
|
||||
)
|
||||
|
||||
# Insert
|
||||
query = """
|
||||
INSERT INTO anime_metadata
|
||||
(anime_id, name, folder, key, created_at, last_updated)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
"""
|
||||
params = (
|
||||
anime.anime_id, anime.name, anime.folder, anime.key,
|
||||
anime.created_at, anime.last_updated
|
||||
)
|
||||
|
||||
success = self.db_manager.execute_update(query, params)
|
||||
self.assertTrue(success)
|
||||
|
||||
# Read
|
||||
select_query = "SELECT * FROM anime_metadata WHERE anime_id = ?"
|
||||
results = self.db_manager.execute_query(select_query, (anime.anime_id,))
|
||||
self.assertEqual(len(results), 1)
|
||||
self.assertEqual(results[0]['name'], anime.name)
|
||||
|
||||
# Update
|
||||
update_query = """
|
||||
UPDATE anime_metadata SET description = ? WHERE anime_id = ?
|
||||
"""
|
||||
success = self.db_manager.execute_update(
|
||||
update_query, ("Updated description", anime.anime_id)
|
||||
)
|
||||
self.assertTrue(success)
|
||||
|
||||
# Verify update
|
||||
results = self.db_manager.execute_query(select_query, (anime.anime_id,))
|
||||
self.assertEqual(results[0]['description'], "Updated description")
|
||||
|
||||
# Delete
|
||||
delete_query = "DELETE FROM anime_metadata WHERE anime_id = ?"
|
||||
success = self.db_manager.execute_update(delete_query, (anime.anime_id,))
|
||||
self.assertTrue(success)
|
||||
|
||||
# Verify deletion
|
||||
results = self.db_manager.execute_query(select_query, (anime.anime_id,))
|
||||
self.assertEqual(len(results), 0)
|
||||
|
||||
|
||||
class TestErrorRecoveryManager(unittest.TestCase):
|
||||
"""Test cases for ErrorRecoveryManager."""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up error recovery manager."""
|
||||
self.recovery_manager = ErrorRecoveryManager()
|
||||
|
||||
def test_retry_mechanism(self):
|
||||
"""Test retry mechanism for failed operations."""
|
||||
retry_mechanism = RetryMechanism(max_retries=3, base_delay=0.1)
|
||||
|
||||
# Test successful operation
|
||||
def success_operation():
|
||||
return "success"
|
||||
|
||||
result = retry_mechanism.execute_with_retry(success_operation)
|
||||
self.assertEqual(result, "success")
|
||||
|
||||
# Test failing operation
|
||||
call_count = [0]
|
||||
def failing_operation():
|
||||
call_count[0] += 1
|
||||
if call_count[0] < 3:
|
||||
raise Exception("Temporary failure")
|
||||
return "success"
|
||||
|
||||
result = retry_mechanism.execute_with_retry(failing_operation)
|
||||
self.assertEqual(result, "success")
|
||||
self.assertEqual(call_count[0], 3)
|
||||
|
||||
def test_network_health_checker(self):
|
||||
"""Test network health checking."""
|
||||
checker = NetworkHealthChecker()
|
||||
|
||||
# Mock requests for controlled testing
|
||||
with patch('requests.get') as mock_get:
|
||||
# Test successful check
|
||||
mock_response = Mock()
|
||||
mock_response.status_code = 200
|
||||
mock_response.raise_for_status.return_value = None
|
||||
mock_get.return_value = mock_response
|
||||
|
||||
is_healthy = checker.check_network_health()
|
||||
self.assertTrue(is_healthy)
|
||||
|
||||
# Test failed check
|
||||
mock_get.side_effect = Exception("Network error")
|
||||
is_healthy = checker.check_network_health()
|
||||
self.assertFalse(is_healthy)
|
||||
|
||||
|
||||
class TestPerformanceOptimizer(unittest.TestCase):
|
||||
"""Test cases for performance optimization components."""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up performance components."""
|
||||
self.speed_limiter = SpeedLimiter(max_speed_mbps=10)
|
||||
self.download_cache = DownloadCache()
|
||||
|
||||
def test_speed_limiter(self):
|
||||
"""Test download speed limiting."""
|
||||
# Test speed calculation
|
||||
speed_mbps = self.speed_limiter.calculate_current_speed(1024*1024, 1.0) # 1MB in 1 second
|
||||
self.assertEqual(speed_mbps, 8.0) # 1MB/s = 8 Mbps
|
||||
|
||||
# Test should limit
|
||||
should_limit = self.speed_limiter.should_limit_speed(15.0) # Above limit
|
||||
self.assertTrue(should_limit)
|
||||
|
||||
should_not_limit = self.speed_limiter.should_limit_speed(5.0) # Below limit
|
||||
self.assertFalse(should_not_limit)
|
||||
|
||||
def test_download_cache(self):
|
||||
"""Test download caching mechanism."""
|
||||
test_url = "http://example.com/video.mp4"
|
||||
test_data = b"test video data"
|
||||
|
||||
# Test cache miss
|
||||
cached_data = self.download_cache.get(test_url)
|
||||
self.assertIsNone(cached_data)
|
||||
|
||||
# Test cache set and hit
|
||||
self.download_cache.set(test_url, test_data)
|
||||
cached_data = self.download_cache.get(test_url)
|
||||
self.assertEqual(cached_data, test_data)
|
||||
|
||||
# Test cache invalidation
|
||||
self.download_cache.invalidate(test_url)
|
||||
cached_data = self.download_cache.get(test_url)
|
||||
self.assertIsNone(cached_data)
|
||||
|
||||
def test_memory_monitor(self):
|
||||
"""Test memory monitoring."""
|
||||
monitor = MemoryMonitor(threshold_mb=100)
|
||||
|
||||
# Test memory usage calculation
|
||||
usage_mb = monitor.get_current_memory_usage()
|
||||
self.assertIsInstance(usage_mb, (int, float))
|
||||
self.assertGreater(usage_mb, 0)
|
||||
|
||||
# Test threshold checking
|
||||
is_high = monitor.is_memory_usage_high()
|
||||
self.assertIsInstance(is_high, bool)
|
||||
|
||||
|
||||
class TestAPIIntegration(unittest.TestCase):
|
||||
"""Test cases for API integration components."""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up API components."""
|
||||
self.webhook_manager = WebhookManager()
|
||||
self.export_manager = ExportManager()
|
||||
|
||||
def test_webhook_manager(self):
|
||||
"""Test webhook functionality."""
|
||||
test_url = "https://example.com/webhook"
|
||||
self.webhook_manager.add_webhook(test_url)
|
||||
|
||||
# Test webhook is registered
|
||||
self.assertIn(test_url, self.webhook_manager.webhooks)
|
||||
|
||||
# Test webhook removal
|
||||
self.webhook_manager.remove_webhook(test_url)
|
||||
self.assertNotIn(test_url, self.webhook_manager.webhooks)
|
||||
|
||||
def test_export_manager(self):
|
||||
"""Test data export functionality."""
|
||||
# Mock series app
|
||||
mock_series_app = Mock()
|
||||
mock_series = Mock()
|
||||
mock_series.name = "Test Anime"
|
||||
mock_series.folder = "test_folder"
|
||||
mock_series.missing = [1, 2, 3]
|
||||
mock_series_app.series_list.series = [mock_series]
|
||||
|
||||
self.export_manager.series_app = mock_series_app
|
||||
|
||||
# Test JSON export
|
||||
json_data = self.export_manager.export_to_json()
|
||||
self.assertIsInstance(json_data, str)
|
||||
|
||||
# Parse and validate JSON
|
||||
parsed_data = json.loads(json_data)
|
||||
self.assertIn('anime_list', parsed_data)
|
||||
self.assertEqual(len(parsed_data['anime_list']), 1)
|
||||
self.assertEqual(parsed_data['anime_list'][0]['name'], "Test Anime")
|
||||
|
||||
# Test CSV export
|
||||
csv_data = self.export_manager.export_to_csv()
|
||||
self.assertIsInstance(csv_data, str)
|
||||
self.assertIn("Test Anime", csv_data)
|
||||
self.assertIn("test_folder", csv_data)
|
||||
|
||||
|
||||
class TestBackupManager(unittest.TestCase):
|
||||
"""Test cases for backup management."""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test environment."""
|
||||
self.temp_dir = tempfile.mkdtemp()
|
||||
|
||||
# Create test database
|
||||
self.test_db = os.path.join(self.temp_dir, "test.db")
|
||||
self.db_manager = DatabaseManager(self.test_db)
|
||||
|
||||
# Create backup manager
|
||||
self.backup_manager = BackupManager(
|
||||
self.db_manager,
|
||||
os.path.join(self.temp_dir, "backups")
|
||||
)
|
||||
|
||||
def tearDown(self):
|
||||
"""Clean up test environment."""
|
||||
self.db_manager.close()
|
||||
shutil.rmtree(self.temp_dir, ignore_errors=True)
|
||||
|
||||
def test_create_backup(self):
|
||||
"""Test backup creation."""
|
||||
# Add some test data
|
||||
anime = AnimeMetadata(
|
||||
anime_id="backup-test",
|
||||
name="Backup Test Anime",
|
||||
folder="backup_test"
|
||||
)
|
||||
|
||||
with self.db_manager.get_connection() as conn:
|
||||
conn.execute("""
|
||||
INSERT INTO anime_metadata
|
||||
(anime_id, name, folder, created_at, last_updated)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
""", (anime.anime_id, anime.name, anime.folder,
|
||||
anime.created_at, anime.last_updated))
|
||||
|
||||
# Create backup
|
||||
backup_info = self.backup_manager.create_full_backup("Test backup")
|
||||
|
||||
self.assertIsNotNone(backup_info)
|
||||
self.assertTrue(os.path.exists(backup_info.backup_path))
|
||||
self.assertGreater(backup_info.size_bytes, 0)
|
||||
|
||||
def test_restore_backup(self):
|
||||
"""Test backup restoration."""
|
||||
# Create initial data
|
||||
anime_id = "restore-test"
|
||||
with self.db_manager.get_connection() as conn:
|
||||
conn.execute("""
|
||||
INSERT INTO anime_metadata
|
||||
(anime_id, name, folder, created_at, last_updated)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
""", (anime_id, "Original", "original_folder",
|
||||
datetime.utcnow(), datetime.utcnow()))
|
||||
|
||||
# Create backup
|
||||
backup_info = self.backup_manager.create_full_backup("Pre-modification backup")
|
||||
|
||||
# Modify data
|
||||
with self.db_manager.get_connection() as conn:
|
||||
conn.execute("""
|
||||
UPDATE anime_metadata SET name = ? WHERE anime_id = ?
|
||||
""", ("Modified", anime_id))
|
||||
|
||||
# Restore backup
|
||||
success = self.backup_manager.restore_backup(backup_info.backup_id)
|
||||
self.assertTrue(success)
|
||||
|
||||
# Verify restoration
|
||||
results = self.db_manager.execute_query(
|
||||
"SELECT name FROM anime_metadata WHERE anime_id = ?",
|
||||
(anime_id,)
|
||||
)
|
||||
self.assertEqual(len(results), 1)
|
||||
self.assertEqual(results[0]['name'], "Original")
|
||||
|
||||
|
||||
class TestConcurrency(unittest.TestCase):
|
||||
"""Test cases for concurrent operations."""
|
||||
|
||||
def test_concurrent_downloads(self):
|
||||
"""Test concurrent download handling."""
|
||||
results = []
|
||||
errors = []
|
||||
|
||||
def mock_download(episode_id):
|
||||
"""Mock download function."""
|
||||
try:
|
||||
# Simulate download work
|
||||
threading.Event().wait(0.1)
|
||||
results.append(f"Downloaded {episode_id}")
|
||||
return True
|
||||
except Exception as e:
|
||||
errors.append(str(e))
|
||||
return False
|
||||
|
||||
# Create multiple download threads
|
||||
threads = []
|
||||
for i in range(5):
|
||||
thread = threading.Thread(target=mock_download, args=(f"episode_{i}",))
|
||||
threads.append(thread)
|
||||
thread.start()
|
||||
|
||||
# Wait for all threads to complete
|
||||
for thread in threads:
|
||||
thread.join()
|
||||
|
||||
# Verify results
|
||||
self.assertEqual(len(results), 5)
|
||||
self.assertEqual(len(errors), 0)
|
||||
|
||||
def test_database_concurrent_access(self):
|
||||
"""Test concurrent database access."""
|
||||
# Create temporary database
|
||||
temp_db = tempfile.NamedTemporaryFile(delete=False)
|
||||
temp_db.close()
|
||||
|
||||
try:
|
||||
db_manager = DatabaseManager(temp_db.name)
|
||||
results = []
|
||||
errors = []
|
||||
|
||||
def concurrent_insert(thread_id):
|
||||
"""Concurrent database insert operation."""
|
||||
try:
|
||||
anime_id = f"concurrent-{thread_id}"
|
||||
query = """
|
||||
INSERT INTO anime_metadata
|
||||
(anime_id, name, folder, created_at, last_updated)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
"""
|
||||
success = db_manager.execute_update(
|
||||
query,
|
||||
(anime_id, f"Anime {thread_id}", f"folder_{thread_id}",
|
||||
datetime.utcnow(), datetime.utcnow())
|
||||
)
|
||||
if success:
|
||||
results.append(thread_id)
|
||||
except Exception as e:
|
||||
errors.append(str(e))
|
||||
|
||||
# Create concurrent threads
|
||||
threads = []
|
||||
for i in range(10):
|
||||
thread = threading.Thread(target=concurrent_insert, args=(i,))
|
||||
threads.append(thread)
|
||||
thread.start()
|
||||
|
||||
# Wait for completion
|
||||
for thread in threads:
|
||||
thread.join()
|
||||
|
||||
# Verify results
|
||||
self.assertEqual(len(results), 10)
|
||||
self.assertEqual(len(errors), 0)
|
||||
|
||||
# Verify database state
|
||||
count_results = db_manager.execute_query(
|
||||
"SELECT COUNT(*) as count FROM anime_metadata"
|
||||
)
|
||||
self.assertEqual(count_results[0]['count'], 10)
|
||||
|
||||
db_manager.close()
|
||||
finally:
|
||||
os.unlink(temp_db.name)
|
||||
|
||||
|
||||
def run_test_suite():
|
||||
"""Run the complete test suite."""
|
||||
# Create test suite
|
||||
suite = unittest.TestSuite()
|
||||
|
||||
# Add all test cases
|
||||
test_classes = [
|
||||
TestSerie,
|
||||
TestSeriesList,
|
||||
TestDatabaseManager,
|
||||
TestErrorRecoveryManager,
|
||||
TestPerformanceOptimizer,
|
||||
TestAPIIntegration,
|
||||
TestBackupManager,
|
||||
TestConcurrency
|
||||
]
|
||||
|
||||
for test_class in test_classes:
|
||||
tests = unittest.TestLoader().loadTestsFromTestCase(test_class)
|
||||
suite.addTests(tests)
|
||||
|
||||
# Run tests
|
||||
runner = unittest.TextTestRunner(verbosity=2)
|
||||
result = runner.run(suite)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("Running AniWorld Unit Tests...")
|
||||
print("=" * 50)
|
||||
|
||||
result = run_test_suite()
|
||||
|
||||
print("\n" + "=" * 50)
|
||||
print(f"Tests run: {result.testsRun}")
|
||||
print(f"Failures: {len(result.failures)}")
|
||||
print(f"Errors: {len(result.errors)}")
|
||||
|
||||
if result.failures:
|
||||
print("\nFailures:")
|
||||
for test, traceback in result.failures:
|
||||
print(f"- {test}: {traceback}")
|
||||
|
||||
if result.errors:
|
||||
print("\nErrors:")
|
||||
for test, traceback in result.errors:
|
||||
print(f"- {test}: {traceback}")
|
||||
|
||||
if result.wasSuccessful():
|
||||
print("\nAll tests passed! ✅")
|
||||
sys.exit(0)
|
||||
else:
|
||||
print("\nSome tests failed! ❌")
|
||||
sys.exit(1)
|
||||
1
tests/unit/web/__init__.py
Normal file
1
tests/unit/web/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# Test package initialization
|
||||
20
tests/unit/web/run_core_tests.bat
Normal file
20
tests/unit/web/run_core_tests.bat
Normal file
@@ -0,0 +1,20 @@
|
||||
@echo off
|
||||
echo.
|
||||
echo 🚀 AniWorld Core Functionality Tests
|
||||
echo =====================================
|
||||
echo.
|
||||
|
||||
cd /d "%~dp0"
|
||||
python run_core_tests.py
|
||||
|
||||
if %ERRORLEVEL% EQU 0 (
|
||||
echo.
|
||||
echo ✅ All tests completed successfully!
|
||||
) else (
|
||||
echo.
|
||||
echo ❌ Some tests failed. Check output above.
|
||||
)
|
||||
|
||||
echo.
|
||||
echo Press any key to continue...
|
||||
pause > nul
|
||||
57
tests/unit/web/run_core_tests.py
Normal file
57
tests/unit/web/run_core_tests.py
Normal file
@@ -0,0 +1,57 @@
|
||||
"""
|
||||
Simple test runner for core AniWorld server functionality.
|
||||
|
||||
This script runs the essential tests to validate JavaScript/CSS generation.
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import sys
|
||||
import os
|
||||
|
||||
# Add parent directory to path for imports
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("🚀 Running AniWorld Core Functionality Tests")
|
||||
print("=" * 50)
|
||||
|
||||
# Import and run the core tests
|
||||
from test_core_functionality import TestManagerGenerationCore, TestComprehensiveSuite
|
||||
|
||||
# Create test suite
|
||||
suite = unittest.TestSuite()
|
||||
|
||||
# Add core manager tests
|
||||
suite.addTest(TestManagerGenerationCore('test_keyboard_shortcut_manager_generation'))
|
||||
suite.addTest(TestManagerGenerationCore('test_drag_drop_manager_generation'))
|
||||
suite.addTest(TestManagerGenerationCore('test_accessibility_manager_generation'))
|
||||
suite.addTest(TestManagerGenerationCore('test_user_preferences_manager_generation'))
|
||||
suite.addTest(TestManagerGenerationCore('test_advanced_search_manager_generation'))
|
||||
suite.addTest(TestManagerGenerationCore('test_undo_redo_manager_generation'))
|
||||
suite.addTest(TestManagerGenerationCore('test_multi_screen_manager_generation'))
|
||||
|
||||
# Add comprehensive test
|
||||
suite.addTest(TestComprehensiveSuite('test_all_manager_fixes_comprehensive'))
|
||||
|
||||
# Run tests
|
||||
runner = unittest.TextTestRunner(verbosity=1, buffer=True)
|
||||
result = runner.run(suite)
|
||||
|
||||
# Print summary
|
||||
print("\n" + "=" * 50)
|
||||
if result.wasSuccessful():
|
||||
print("🎉 ALL CORE TESTS PASSED!")
|
||||
print("✅ JavaScript/CSS generation working correctly")
|
||||
print("✅ All manager classes validated")
|
||||
print("✅ No syntax or runtime errors found")
|
||||
else:
|
||||
print("❌ Some core tests failed")
|
||||
if result.failures:
|
||||
for test, error in result.failures:
|
||||
print(f" FAIL: {test}")
|
||||
if result.errors:
|
||||
for test, error in result.errors:
|
||||
print(f" ERROR: {test}")
|
||||
|
||||
print("=" * 50)
|
||||
sys.exit(0 if result.wasSuccessful() else 1)
|
||||
10
tests/unit/web/run_tests.bat
Normal file
10
tests/unit/web/run_tests.bat
Normal file
@@ -0,0 +1,10 @@
|
||||
@echo off
|
||||
echo Running AniWorld Server Test Suite...
|
||||
echo.
|
||||
|
||||
cd /d "%~dp0"
|
||||
python run_tests.py
|
||||
|
||||
echo.
|
||||
echo Test run completed.
|
||||
pause
|
||||
108
tests/unit/web/run_tests.py
Normal file
108
tests/unit/web/run_tests.py
Normal file
@@ -0,0 +1,108 @@
|
||||
"""
|
||||
Test runner for the AniWorld server test suite.
|
||||
|
||||
This script runs all test modules and provides a comprehensive report.
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import sys
|
||||
import os
|
||||
from io import StringIO
|
||||
|
||||
# Add parent directory to path for imports
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
|
||||
def run_all_tests():
|
||||
"""Run all test modules and provide a summary report."""
|
||||
|
||||
print("=" * 60)
|
||||
print("AniWorld Server Test Suite")
|
||||
print("=" * 60)
|
||||
|
||||
# Discover and run all tests
|
||||
loader = unittest.TestLoader()
|
||||
test_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
# Load all test modules
|
||||
suite = loader.discover(test_dir, pattern='test_*.py')
|
||||
|
||||
# Run tests with detailed output
|
||||
stream = StringIO()
|
||||
runner = unittest.TextTestRunner(
|
||||
stream=stream,
|
||||
verbosity=2,
|
||||
buffer=True
|
||||
)
|
||||
|
||||
result = runner.run(suite)
|
||||
|
||||
# Print results
|
||||
output = stream.getvalue()
|
||||
print(output)
|
||||
|
||||
# Summary
|
||||
print("\n" + "=" * 60)
|
||||
print("TEST SUMMARY")
|
||||
print("=" * 60)
|
||||
|
||||
total_tests = result.testsRun
|
||||
failures = len(result.failures)
|
||||
errors = len(result.errors)
|
||||
skipped = len(result.skipped) if hasattr(result, 'skipped') else 0
|
||||
passed = total_tests - failures - errors - skipped
|
||||
|
||||
print(f"Total Tests Run: {total_tests}")
|
||||
print(f"Passed: {passed}")
|
||||
print(f"Failed: {failures}")
|
||||
print(f"Errors: {errors}")
|
||||
print(f"Skipped: {skipped}")
|
||||
|
||||
if result.wasSuccessful():
|
||||
print("\n🎉 ALL TESTS PASSED! 🎉")
|
||||
print("✅ No JavaScript or CSS generation issues found!")
|
||||
print("✅ All manager classes working correctly!")
|
||||
print("✅ Authentication system validated!")
|
||||
return True
|
||||
else:
|
||||
print("\n❌ Some tests failed. Please check the output above.")
|
||||
|
||||
if result.failures:
|
||||
print(f"\nFailures ({len(result.failures)}):")
|
||||
for test, traceback in result.failures:
|
||||
print(f" - {test}: {traceback.split(chr(10))[-2]}")
|
||||
|
||||
if result.errors:
|
||||
print(f"\nErrors ({len(result.errors)}):")
|
||||
for test, traceback in result.errors:
|
||||
print(f" - {test}: {traceback.split(chr(10))[-2]}")
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def run_specific_test_module(module_name):
|
||||
"""Run a specific test module."""
|
||||
|
||||
print(f"Running tests from module: {module_name}")
|
||||
print("-" * 40)
|
||||
|
||||
loader = unittest.TestLoader()
|
||||
suite = loader.loadTestsFromName(module_name)
|
||||
|
||||
runner = unittest.TextTestRunner(verbosity=2, buffer=True)
|
||||
result = runner.run(suite)
|
||||
|
||||
return result.wasSuccessful()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) > 1:
|
||||
# Run specific test module
|
||||
module_name = sys.argv[1]
|
||||
success = run_specific_test_module(module_name)
|
||||
else:
|
||||
# Run all tests
|
||||
success = run_all_tests()
|
||||
|
||||
# Exit with appropriate code
|
||||
sys.exit(0 if success else 1)
|
||||
42
tests/unit/web/test_app.py
Normal file
42
tests/unit/web/test_app.py
Normal file
@@ -0,0 +1,42 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Test script to verify Flask app structure without initializing SeriesApp
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
# Test if we can import Flask modules
|
||||
try:
|
||||
from flask import Flask
|
||||
from flask_socketio import SocketIO
|
||||
print("✅ Flask and SocketIO imports successful")
|
||||
except ImportError as e:
|
||||
print(f"❌ Flask import failed: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
# Test if we can import our modules
|
||||
try:
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..'))
|
||||
from src.server.core.entities.series import Serie
|
||||
from src.server.core.entities.SerieList import SerieList
|
||||
print("✅ Core modules import successful")
|
||||
except ImportError as e:
|
||||
print(f"❌ Core module import failed: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
# Test Flask app creation
|
||||
try:
|
||||
app = Flask(__name__)
|
||||
app.config['SECRET_KEY'] = 'test-key'
|
||||
socketio = SocketIO(app, cors_allowed_origins="*")
|
||||
print("✅ Flask app creation successful")
|
||||
except Exception as e:
|
||||
print(f"❌ Flask app creation failed: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
print("🎉 All tests passed! Flask app structure is valid.")
|
||||
print("\nTo run the server:")
|
||||
print("1. Set ANIME_DIRECTORY environment variable to your anime directory")
|
||||
print("2. Run: python app.py")
|
||||
print("3. Open browser to http://localhost:5000")
|
||||
127
tests/unit/web/test_authentication.py
Normal file
127
tests/unit/web/test_authentication.py
Normal file
@@ -0,0 +1,127 @@
|
||||
"""
|
||||
Test suite for authentication and session management.
|
||||
|
||||
This test module validates the authentication system, session management,
|
||||
and security features.
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import sys
|
||||
import os
|
||||
from unittest.mock import patch, MagicMock
|
||||
|
||||
# Add parent directory to path for imports
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
|
||||
class TestAuthenticationSystem(unittest.TestCase):
|
||||
"""Test class for authentication and session management."""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test fixtures before each test method."""
|
||||
# Mock Flask app for testing
|
||||
self.mock_app = MagicMock()
|
||||
self.mock_app.config = {'SECRET_KEY': 'test_secret'}
|
||||
|
||||
def test_session_manager_initialization(self):
|
||||
"""Test SessionManager initialization."""
|
||||
try:
|
||||
from auth import SessionManager
|
||||
|
||||
manager = SessionManager()
|
||||
self.assertIsNotNone(manager)
|
||||
self.assertTrue(hasattr(manager, 'login'))
|
||||
self.assertTrue(hasattr(manager, 'check_password'))
|
||||
|
||||
print('✓ SessionManager initialization successful')
|
||||
|
||||
except Exception as e:
|
||||
self.fail(f'SessionManager initialization failed: {e}')
|
||||
|
||||
def test_login_method_exists(self):
|
||||
"""Test that login method exists and returns proper response."""
|
||||
try:
|
||||
from auth import SessionManager
|
||||
|
||||
manager = SessionManager()
|
||||
|
||||
# Test login method exists
|
||||
self.assertTrue(hasattr(manager, 'login'))
|
||||
|
||||
# Test login with invalid credentials returns dict
|
||||
result = manager.login('wrong_password')
|
||||
self.assertIsInstance(result, dict)
|
||||
self.assertIn('success', result)
|
||||
self.assertFalse(result['success'])
|
||||
|
||||
print('✓ SessionManager login method validated')
|
||||
|
||||
except Exception as e:
|
||||
self.fail(f'SessionManager login method test failed: {e}')
|
||||
|
||||
def test_password_checking(self):
|
||||
"""Test password validation functionality."""
|
||||
try:
|
||||
from auth import SessionManager
|
||||
|
||||
manager = SessionManager()
|
||||
|
||||
# Test check_password method exists
|
||||
self.assertTrue(hasattr(manager, 'check_password'))
|
||||
|
||||
# Test with empty/invalid password
|
||||
result = manager.check_password('')
|
||||
self.assertFalse(result)
|
||||
|
||||
result = manager.check_password('wrong_password')
|
||||
self.assertFalse(result)
|
||||
|
||||
print('✓ SessionManager password checking validated')
|
||||
|
||||
except Exception as e:
|
||||
self.fail(f'SessionManager password checking test failed: {e}')
|
||||
|
||||
|
||||
class TestConfigurationSystem(unittest.TestCase):
|
||||
"""Test class for configuration management."""
|
||||
|
||||
def test_config_manager_initialization(self):
|
||||
"""Test ConfigManager initialization."""
|
||||
try:
|
||||
from config import ConfigManager
|
||||
|
||||
manager = ConfigManager()
|
||||
self.assertIsNotNone(manager)
|
||||
self.assertTrue(hasattr(manager, 'anime_directory'))
|
||||
|
||||
print('✓ ConfigManager initialization successful')
|
||||
|
||||
except Exception as e:
|
||||
self.fail(f'ConfigManager initialization failed: {e}')
|
||||
|
||||
def test_anime_directory_property(self):
|
||||
"""Test anime_directory property getter and setter."""
|
||||
try:
|
||||
from config import ConfigManager
|
||||
|
||||
manager = ConfigManager()
|
||||
|
||||
# Test getter
|
||||
initial_dir = manager.anime_directory
|
||||
self.assertIsInstance(initial_dir, str)
|
||||
|
||||
# Test setter exists
|
||||
test_dir = 'C:\\TestAnimeDir'
|
||||
manager.anime_directory = test_dir
|
||||
|
||||
# Verify setter worked
|
||||
self.assertEqual(manager.anime_directory, test_dir)
|
||||
|
||||
print('✓ ConfigManager anime_directory property validated')
|
||||
|
||||
except Exception as e:
|
||||
self.fail(f'ConfigManager anime_directory property test failed: {e}')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2, buffer=True)
|
||||
288
tests/unit/web/test_core_functionality.py
Normal file
288
tests/unit/web/test_core_functionality.py
Normal file
@@ -0,0 +1,288 @@
|
||||
"""
|
||||
Focused test suite for manager JavaScript and CSS generation.
|
||||
|
||||
This test module validates the core functionality that we know is working.
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import sys
|
||||
import os
|
||||
|
||||
# Add parent directory to path for imports
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
|
||||
class TestManagerGenerationCore(unittest.TestCase):
|
||||
"""Test class for validating core manager JavaScript/CSS generation functionality."""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test fixtures before each test method."""
|
||||
self.managers_tested = 0
|
||||
self.total_js_chars = 0
|
||||
self.total_css_chars = 0
|
||||
print("\n" + "="*50)
|
||||
|
||||
def test_keyboard_shortcut_manager_generation(self):
|
||||
"""Test KeyboardShortcutManager JavaScript generation."""
|
||||
print("Testing KeyboardShortcutManager...")
|
||||
try:
|
||||
from keyboard_shortcuts import KeyboardShortcutManager
|
||||
manager = KeyboardShortcutManager()
|
||||
js = manager.get_shortcuts_js()
|
||||
|
||||
# Validate JS generation
|
||||
self.assertIsInstance(js, str)
|
||||
self.assertGreater(len(js), 1000) # Should be substantial
|
||||
|
||||
self.total_js_chars += len(js)
|
||||
self.managers_tested += 1
|
||||
|
||||
print(f'✓ KeyboardShortcutManager: {len(js):,} JS characters generated')
|
||||
|
||||
except Exception as e:
|
||||
self.fail(f'KeyboardShortcutManager test failed: {e}')
|
||||
|
||||
def test_drag_drop_manager_generation(self):
|
||||
"""Test DragDropManager JavaScript and CSS generation."""
|
||||
print("Testing DragDropManager...")
|
||||
try:
|
||||
from drag_drop import DragDropManager
|
||||
manager = DragDropManager()
|
||||
|
||||
js = manager.get_drag_drop_js()
|
||||
css = manager.get_css()
|
||||
|
||||
# Validate generation
|
||||
self.assertIsInstance(js, str)
|
||||
self.assertIsInstance(css, str)
|
||||
self.assertGreater(len(js), 1000)
|
||||
self.assertGreater(len(css), 100)
|
||||
|
||||
# Check for proper JSON serialization (no Python booleans)
|
||||
self.assertNotIn('True', js)
|
||||
self.assertNotIn('False', js)
|
||||
self.assertNotIn('None', js)
|
||||
|
||||
self.total_js_chars += len(js)
|
||||
self.total_css_chars += len(css)
|
||||
self.managers_tested += 1
|
||||
|
||||
print(f'✓ DragDropManager: {len(js):,} JS chars, {len(css):,} CSS chars')
|
||||
|
||||
except Exception as e:
|
||||
self.fail(f'DragDropManager test failed: {e}')
|
||||
|
||||
def test_accessibility_manager_generation(self):
|
||||
"""Test AccessibilityManager JavaScript and CSS generation."""
|
||||
print("Testing AccessibilityManager...")
|
||||
try:
|
||||
from accessibility_features import AccessibilityManager
|
||||
manager = AccessibilityManager()
|
||||
|
||||
js = manager.get_accessibility_js()
|
||||
css = manager.get_css()
|
||||
|
||||
# Validate generation
|
||||
self.assertIsInstance(js, str)
|
||||
self.assertIsInstance(css, str)
|
||||
self.assertGreater(len(js), 1000)
|
||||
self.assertGreater(len(css), 100)
|
||||
|
||||
# Check for proper JSON serialization
|
||||
self.assertNotIn('True', js)
|
||||
self.assertNotIn('False', js)
|
||||
|
||||
self.total_js_chars += len(js)
|
||||
self.total_css_chars += len(css)
|
||||
self.managers_tested += 1
|
||||
|
||||
print(f'✓ AccessibilityManager: {len(js):,} JS chars, {len(css):,} CSS chars')
|
||||
|
||||
except Exception as e:
|
||||
self.fail(f'AccessibilityManager test failed: {e}')
|
||||
|
||||
def test_user_preferences_manager_generation(self):
|
||||
"""Test UserPreferencesManager JavaScript and CSS generation."""
|
||||
print("Testing UserPreferencesManager...")
|
||||
try:
|
||||
from user_preferences import UserPreferencesManager
|
||||
manager = UserPreferencesManager()
|
||||
|
||||
# Verify preferences attribute exists (this was the main fix)
|
||||
self.assertTrue(hasattr(manager, 'preferences'))
|
||||
self.assertIsInstance(manager.preferences, dict)
|
||||
|
||||
js = manager.get_preferences_js()
|
||||
css = manager.get_css()
|
||||
|
||||
# Validate generation
|
||||
self.assertIsInstance(js, str)
|
||||
self.assertIsInstance(css, str)
|
||||
self.assertGreater(len(js), 1000)
|
||||
self.assertGreater(len(css), 100)
|
||||
|
||||
self.total_js_chars += len(js)
|
||||
self.total_css_chars += len(css)
|
||||
self.managers_tested += 1
|
||||
|
||||
print(f'✓ UserPreferencesManager: {len(js):,} JS chars, {len(css):,} CSS chars')
|
||||
|
||||
except Exception as e:
|
||||
self.fail(f'UserPreferencesManager test failed: {e}')
|
||||
|
||||
def test_advanced_search_manager_generation(self):
|
||||
"""Test AdvancedSearchManager JavaScript and CSS generation."""
|
||||
print("Testing AdvancedSearchManager...")
|
||||
try:
|
||||
from advanced_search import AdvancedSearchManager
|
||||
manager = AdvancedSearchManager()
|
||||
|
||||
js = manager.get_search_js()
|
||||
css = manager.get_css()
|
||||
|
||||
# Validate generation
|
||||
self.assertIsInstance(js, str)
|
||||
self.assertIsInstance(css, str)
|
||||
self.assertGreater(len(js), 1000)
|
||||
self.assertGreater(len(css), 100)
|
||||
|
||||
self.total_js_chars += len(js)
|
||||
self.total_css_chars += len(css)
|
||||
self.managers_tested += 1
|
||||
|
||||
print(f'✓ AdvancedSearchManager: {len(js):,} JS chars, {len(css):,} CSS chars')
|
||||
|
||||
except Exception as e:
|
||||
self.fail(f'AdvancedSearchManager test failed: {e}')
|
||||
|
||||
def test_undo_redo_manager_generation(self):
|
||||
"""Test UndoRedoManager JavaScript and CSS generation."""
|
||||
print("Testing UndoRedoManager...")
|
||||
try:
|
||||
from undo_redo_manager import UndoRedoManager
|
||||
manager = UndoRedoManager()
|
||||
|
||||
js = manager.get_undo_redo_js()
|
||||
css = manager.get_css()
|
||||
|
||||
# Validate generation
|
||||
self.assertIsInstance(js, str)
|
||||
self.assertIsInstance(css, str)
|
||||
self.assertGreater(len(js), 1000)
|
||||
self.assertGreater(len(css), 100)
|
||||
|
||||
self.total_js_chars += len(js)
|
||||
self.total_css_chars += len(css)
|
||||
self.managers_tested += 1
|
||||
|
||||
print(f'✓ UndoRedoManager: {len(js):,} JS chars, {len(css):,} CSS chars')
|
||||
|
||||
except Exception as e:
|
||||
self.fail(f'UndoRedoManager test failed: {e}')
|
||||
|
||||
def test_multi_screen_manager_generation(self):
|
||||
"""Test MultiScreenManager JavaScript and CSS generation."""
|
||||
print("Testing MultiScreenManager...")
|
||||
try:
|
||||
from multi_screen_support import MultiScreenManager
|
||||
manager = MultiScreenManager()
|
||||
|
||||
js = manager.get_multiscreen_js()
|
||||
css = manager.get_multiscreen_css()
|
||||
|
||||
# Validate generation
|
||||
self.assertIsInstance(js, str)
|
||||
self.assertIsInstance(css, str)
|
||||
self.assertGreater(len(js), 1000)
|
||||
self.assertGreater(len(css), 100)
|
||||
|
||||
# Check for proper f-string escaping (no Python syntax)
|
||||
self.assertNotIn('True', js)
|
||||
self.assertNotIn('False', js)
|
||||
self.assertNotIn('None', js)
|
||||
|
||||
# Verify JavaScript is properly formatted
|
||||
self.assertIn('class', js) # Should contain JavaScript class syntax
|
||||
|
||||
self.total_js_chars += len(js)
|
||||
self.total_css_chars += len(css)
|
||||
self.managers_tested += 1
|
||||
|
||||
print(f'✓ MultiScreenManager: {len(js):,} JS chars, {len(css):,} CSS chars')
|
||||
|
||||
except Exception as e:
|
||||
self.fail(f'MultiScreenManager test failed: {e}')
|
||||
|
||||
|
||||
class TestComprehensiveSuite(unittest.TestCase):
|
||||
"""Comprehensive test to verify all fixes are working."""
|
||||
|
||||
def test_all_manager_fixes_comprehensive(self):
|
||||
"""Run comprehensive test of all manager fixes."""
|
||||
print("\n" + "="*60)
|
||||
print("COMPREHENSIVE MANAGER VALIDATION")
|
||||
print("="*60)
|
||||
|
||||
managers_tested = 0
|
||||
total_js = 0
|
||||
total_css = 0
|
||||
|
||||
# Test each manager
|
||||
test_cases = [
|
||||
('KeyboardShortcutManager', 'keyboard_shortcuts', 'get_shortcuts_js', None),
|
||||
('DragDropManager', 'drag_drop', 'get_drag_drop_js', 'get_css'),
|
||||
('AccessibilityManager', 'accessibility_features', 'get_accessibility_js', 'get_css'),
|
||||
('UserPreferencesManager', 'user_preferences', 'get_preferences_js', 'get_css'),
|
||||
('AdvancedSearchManager', 'advanced_search', 'get_search_js', 'get_css'),
|
||||
('UndoRedoManager', 'undo_redo_manager', 'get_undo_redo_js', 'get_css'),
|
||||
('MultiScreenManager', 'multi_screen_support', 'get_multiscreen_js', 'get_multiscreen_css'),
|
||||
]
|
||||
|
||||
for class_name, module_name, js_method, css_method in test_cases:
|
||||
try:
|
||||
# Dynamic import
|
||||
module = __import__(module_name, fromlist=[class_name])
|
||||
manager_class = getattr(module, class_name)
|
||||
manager = manager_class()
|
||||
|
||||
# Get JS
|
||||
js_func = getattr(manager, js_method)
|
||||
js = js_func()
|
||||
self.assertIsInstance(js, str)
|
||||
self.assertGreater(len(js), 0)
|
||||
total_js += len(js)
|
||||
|
||||
# Get CSS if available
|
||||
css_chars = 0
|
||||
if css_method:
|
||||
css_func = getattr(manager, css_method)
|
||||
css = css_func()
|
||||
self.assertIsInstance(css, str)
|
||||
self.assertGreater(len(css), 0)
|
||||
css_chars = len(css)
|
||||
total_css += css_chars
|
||||
|
||||
managers_tested += 1
|
||||
print(f'✓ {class_name}: JS={len(js):,} chars' +
|
||||
(f', CSS={css_chars:,} chars' if css_chars > 0 else ' (JS only)'))
|
||||
|
||||
except Exception as e:
|
||||
self.fail(f'{class_name} failed: {e}')
|
||||
|
||||
# Final validation
|
||||
expected_managers = 7
|
||||
self.assertEqual(managers_tested, expected_managers)
|
||||
self.assertGreater(total_js, 100000) # Should have substantial JS
|
||||
self.assertGreater(total_css, 10000) # Should have substantial CSS
|
||||
|
||||
print(f'\n{"="*60}')
|
||||
print(f'🎉 ALL {managers_tested} MANAGERS PASSED!')
|
||||
print(f'📊 Total JavaScript: {total_js:,} characters')
|
||||
print(f'🎨 Total CSS: {total_css:,} characters')
|
||||
print(f'✅ No JavaScript or CSS generation issues found!')
|
||||
print(f'{"="*60}')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# Run with high verbosity
|
||||
unittest.main(verbosity=2, buffer=False)
|
||||
131
tests/unit/web/test_flask_app.py
Normal file
131
tests/unit/web/test_flask_app.py
Normal file
@@ -0,0 +1,131 @@
|
||||
"""
|
||||
Test suite for Flask application routes and API endpoints.
|
||||
|
||||
This test module validates the main Flask application functionality,
|
||||
route handling, and API responses.
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import sys
|
||||
import os
|
||||
from unittest.mock import patch, MagicMock
|
||||
|
||||
# Add parent directory to path for imports
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
|
||||
class TestFlaskApplication(unittest.TestCase):
|
||||
"""Test class for Flask application and routes."""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test fixtures before each test method."""
|
||||
pass
|
||||
|
||||
def test_app_imports(self):
|
||||
"""Test that main app module can be imported without errors."""
|
||||
try:
|
||||
import app
|
||||
self.assertIsNotNone(app)
|
||||
print('✓ Main app module imports successfully')
|
||||
|
||||
except Exception as e:
|
||||
self.fail(f'App import failed: {e}')
|
||||
|
||||
@patch('app.Flask')
|
||||
def test_app_initialization_components(self, mock_flask):
|
||||
"""Test that app initialization components are available."""
|
||||
try:
|
||||
# Test manager imports
|
||||
from keyboard_shortcuts import KeyboardShortcutManager
|
||||
from drag_drop import DragDropManager
|
||||
from accessibility_features import AccessibilityManager
|
||||
from user_preferences import UserPreferencesManager
|
||||
|
||||
# Verify managers can be instantiated
|
||||
keyboard_manager = KeyboardShortcutManager()
|
||||
drag_manager = DragDropManager()
|
||||
accessibility_manager = AccessibilityManager()
|
||||
preferences_manager = UserPreferencesManager()
|
||||
|
||||
self.assertIsNotNone(keyboard_manager)
|
||||
self.assertIsNotNone(drag_manager)
|
||||
self.assertIsNotNone(accessibility_manager)
|
||||
self.assertIsNotNone(preferences_manager)
|
||||
|
||||
print('✓ App manager components available')
|
||||
|
||||
except Exception as e:
|
||||
self.fail(f'App component test failed: {e}')
|
||||
|
||||
|
||||
class TestAPIEndpoints(unittest.TestCase):
|
||||
"""Test class for API endpoint validation."""
|
||||
|
||||
def test_api_response_structure(self):
|
||||
"""Test that API endpoints return proper JSON structure."""
|
||||
try:
|
||||
# Test that we can import the auth module for API responses
|
||||
from auth import SessionManager
|
||||
|
||||
manager = SessionManager()
|
||||
|
||||
# Test login API response structure
|
||||
response = manager.login('test_password')
|
||||
self.assertIsInstance(response, dict)
|
||||
self.assertIn('success', response)
|
||||
|
||||
print('✓ API response structure validated')
|
||||
|
||||
except Exception as e:
|
||||
self.fail(f'API endpoint test failed: {e}')
|
||||
|
||||
|
||||
class TestJavaScriptGeneration(unittest.TestCase):
|
||||
"""Test class for dynamic JavaScript generation."""
|
||||
|
||||
def test_javascript_generation_no_syntax_errors(self):
|
||||
"""Test that generated JavaScript doesn't contain Python syntax."""
|
||||
try:
|
||||
from multi_screen_support import MultiScreenSupportManager
|
||||
|
||||
manager = MultiScreenSupportManager()
|
||||
js_code = manager.get_multiscreen_js()
|
||||
|
||||
# Check for Python-specific syntax that shouldn't be in JS
|
||||
self.assertNotIn('True', js_code, 'JavaScript should use "true", not "True"')
|
||||
self.assertNotIn('False', js_code, 'JavaScript should use "false", not "False"')
|
||||
self.assertNotIn('None', js_code, 'JavaScript should use "null", not "None"')
|
||||
|
||||
# Check for proper JSON serialization indicators
|
||||
self.assertIn('true', js_code.lower())
|
||||
self.assertIn('false', js_code.lower())
|
||||
|
||||
print('✓ JavaScript generation syntax validated')
|
||||
|
||||
except Exception as e:
|
||||
self.fail(f'JavaScript generation test failed: {e}')
|
||||
|
||||
def test_f_string_escaping(self):
|
||||
"""Test that f-strings are properly escaped in JavaScript generation."""
|
||||
try:
|
||||
from multi_screen_support import MultiScreenSupportManager
|
||||
|
||||
manager = MultiScreenSupportManager()
|
||||
js_code = manager.get_multiscreen_js()
|
||||
|
||||
# Ensure JavaScript object literals use proper syntax
|
||||
# Look for proper JavaScript object/function syntax
|
||||
self.assertGreater(len(js_code), 0)
|
||||
|
||||
# Check that braces are properly used (not bare Python f-string braces)
|
||||
brace_count = js_code.count('{')
|
||||
self.assertGreater(brace_count, 0)
|
||||
|
||||
print('✓ F-string escaping validated')
|
||||
|
||||
except Exception as e:
|
||||
self.fail(f'F-string escaping test failed: {e}')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2, buffer=True)
|
||||
242
tests/unit/web/test_manager_generation.py
Normal file
242
tests/unit/web/test_manager_generation.py
Normal file
@@ -0,0 +1,242 @@
|
||||
"""
|
||||
Test suite for manager JavaScript and CSS generation.
|
||||
|
||||
This test module validates that all manager classes can successfully generate
|
||||
their JavaScript and CSS code without runtime errors.
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import sys
|
||||
import os
|
||||
|
||||
# Add parent directory to path for imports
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
|
||||
class TestManagerGeneration(unittest.TestCase):
|
||||
"""Test class for validating manager JavaScript/CSS generation."""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test fixtures before each test method."""
|
||||
self.managers_tested = 0
|
||||
self.total_js_chars = 0
|
||||
self.total_css_chars = 0
|
||||
|
||||
def test_keyboard_shortcut_manager(self):
|
||||
"""Test KeyboardShortcutManager JavaScript generation."""
|
||||
try:
|
||||
from keyboard_shortcuts import KeyboardShortcutManager
|
||||
manager = KeyboardShortcutManager()
|
||||
js = manager.get_shortcuts_js()
|
||||
|
||||
self.assertIsInstance(js, str)
|
||||
self.assertGreater(len(js), 0)
|
||||
self.total_js_chars += len(js)
|
||||
self.managers_tested += 1
|
||||
|
||||
print(f'✓ KeyboardShortcutManager: JS={len(js)} chars (no CSS method)')
|
||||
|
||||
except Exception as e:
|
||||
self.fail(f'KeyboardShortcutManager failed: {e}')
|
||||
|
||||
def test_drag_drop_manager(self):
|
||||
"""Test DragDropManager JavaScript and CSS generation."""
|
||||
try:
|
||||
from drag_drop import DragDropManager
|
||||
manager = DragDropManager()
|
||||
|
||||
js = manager.get_drag_drop_js()
|
||||
css = manager.get_css()
|
||||
|
||||
self.assertIsInstance(js, str)
|
||||
self.assertIsInstance(css, str)
|
||||
self.assertGreater(len(js), 0)
|
||||
self.assertGreater(len(css), 0)
|
||||
|
||||
self.total_js_chars += len(js)
|
||||
self.total_css_chars += len(css)
|
||||
self.managers_tested += 1
|
||||
|
||||
print(f'✓ DragDropManager: JS={len(js)} chars, CSS={len(css)} chars')
|
||||
|
||||
except Exception as e:
|
||||
self.fail(f'DragDropManager failed: {e}')
|
||||
|
||||
def test_accessibility_manager(self):
|
||||
"""Test AccessibilityManager JavaScript and CSS generation."""
|
||||
try:
|
||||
from accessibility_features import AccessibilityManager
|
||||
manager = AccessibilityManager()
|
||||
|
||||
js = manager.get_accessibility_js()
|
||||
css = manager.get_css()
|
||||
|
||||
self.assertIsInstance(js, str)
|
||||
self.assertIsInstance(css, str)
|
||||
self.assertGreater(len(js), 0)
|
||||
self.assertGreater(len(css), 0)
|
||||
|
||||
self.total_js_chars += len(js)
|
||||
self.total_css_chars += len(css)
|
||||
self.managers_tested += 1
|
||||
|
||||
print(f'✓ AccessibilityManager: JS={len(js)} chars, CSS={len(css)} chars')
|
||||
|
||||
except Exception as e:
|
||||
self.fail(f'AccessibilityManager failed: {e}')
|
||||
|
||||
def test_user_preferences_manager(self):
|
||||
"""Test UserPreferencesManager JavaScript and CSS generation."""
|
||||
try:
|
||||
from user_preferences import UserPreferencesManager
|
||||
manager = UserPreferencesManager()
|
||||
|
||||
js = manager.get_preferences_js()
|
||||
css = manager.get_css()
|
||||
|
||||
self.assertIsInstance(js, str)
|
||||
self.assertIsInstance(css, str)
|
||||
self.assertGreater(len(js), 0)
|
||||
self.assertGreater(len(css), 0)
|
||||
|
||||
self.total_js_chars += len(js)
|
||||
self.total_css_chars += len(css)
|
||||
self.managers_tested += 1
|
||||
|
||||
print(f'✓ UserPreferencesManager: JS={len(js)} chars, CSS={len(css)} chars')
|
||||
|
||||
except Exception as e:
|
||||
self.fail(f'UserPreferencesManager failed: {e}')
|
||||
|
||||
def test_advanced_search_manager(self):
|
||||
"""Test AdvancedSearchManager JavaScript and CSS generation."""
|
||||
try:
|
||||
from advanced_search import AdvancedSearchManager
|
||||
manager = AdvancedSearchManager()
|
||||
|
||||
js = manager.get_search_js()
|
||||
css = manager.get_css()
|
||||
|
||||
self.assertIsInstance(js, str)
|
||||
self.assertIsInstance(css, str)
|
||||
self.assertGreater(len(js), 0)
|
||||
self.assertGreater(len(css), 0)
|
||||
|
||||
self.total_js_chars += len(js)
|
||||
self.total_css_chars += len(css)
|
||||
self.managers_tested += 1
|
||||
|
||||
print(f'✓ AdvancedSearchManager: JS={len(js)} chars, CSS={len(css)} chars')
|
||||
|
||||
except Exception as e:
|
||||
self.fail(f'AdvancedSearchManager failed: {e}')
|
||||
|
||||
def test_undo_redo_manager(self):
|
||||
"""Test UndoRedoManager JavaScript and CSS generation."""
|
||||
try:
|
||||
from undo_redo_manager import UndoRedoManager
|
||||
manager = UndoRedoManager()
|
||||
|
||||
js = manager.get_undo_redo_js()
|
||||
css = manager.get_css()
|
||||
|
||||
self.assertIsInstance(js, str)
|
||||
self.assertIsInstance(css, str)
|
||||
self.assertGreater(len(js), 0)
|
||||
self.assertGreater(len(css), 0)
|
||||
|
||||
self.total_js_chars += len(js)
|
||||
self.total_css_chars += len(css)
|
||||
self.managers_tested += 1
|
||||
|
||||
print(f'✓ UndoRedoManager: JS={len(js)} chars, CSS={len(css)} chars')
|
||||
|
||||
except Exception as e:
|
||||
self.fail(f'UndoRedoManager failed: {e}')
|
||||
|
||||
def test_all_managers_comprehensive(self):
|
||||
"""Comprehensive test to ensure all managers work together."""
|
||||
expected_managers = 6 # Total number of managers we expect to test
|
||||
|
||||
# Run all individual tests first
|
||||
self.test_keyboard_shortcut_manager()
|
||||
self.test_drag_drop_manager()
|
||||
self.test_accessibility_manager()
|
||||
self.test_user_preferences_manager()
|
||||
self.test_advanced_search_manager()
|
||||
self.test_undo_redo_manager()
|
||||
|
||||
# Validate overall results
|
||||
self.assertEqual(self.managers_tested, expected_managers)
|
||||
self.assertGreater(self.total_js_chars, 0)
|
||||
self.assertGreater(self.total_css_chars, 0)
|
||||
|
||||
print(f'\n=== COMPREHENSIVE TEST SUMMARY ===')
|
||||
print(f'Managers tested: {self.managers_tested}/{expected_managers}')
|
||||
print(f'Total JavaScript generated: {self.total_js_chars:,} characters')
|
||||
print(f'Total CSS generated: {self.total_css_chars:,} characters')
|
||||
print('🎉 All manager JavaScript/CSS generation tests passed!')
|
||||
|
||||
def tearDown(self):
|
||||
"""Clean up after each test method."""
|
||||
pass
|
||||
|
||||
|
||||
class TestManagerMethods(unittest.TestCase):
|
||||
"""Test class for validating specific manager methods."""
|
||||
|
||||
def test_keyboard_shortcuts_methods(self):
|
||||
"""Test that KeyboardShortcutManager has required methods."""
|
||||
try:
|
||||
from keyboard_shortcuts import KeyboardShortcutManager
|
||||
manager = KeyboardShortcutManager()
|
||||
|
||||
# Test that required methods exist
|
||||
self.assertTrue(hasattr(manager, 'get_shortcuts_js'))
|
||||
self.assertTrue(hasattr(manager, 'setEnabled'))
|
||||
self.assertTrue(hasattr(manager, 'updateShortcuts'))
|
||||
|
||||
# Test method calls
|
||||
self.assertIsNotNone(manager.get_shortcuts_js())
|
||||
|
||||
print('✓ KeyboardShortcutManager methods validated')
|
||||
|
||||
except Exception as e:
|
||||
self.fail(f'KeyboardShortcutManager method test failed: {e}')
|
||||
|
||||
def test_screen_reader_methods(self):
|
||||
"""Test that ScreenReaderSupportManager has required methods."""
|
||||
try:
|
||||
from screen_reader_support import ScreenReaderManager
|
||||
manager = ScreenReaderManager()
|
||||
|
||||
# Test that required methods exist
|
||||
self.assertTrue(hasattr(manager, 'get_screen_reader_js'))
|
||||
self.assertTrue(hasattr(manager, 'enhanceFormElements'))
|
||||
self.assertTrue(hasattr(manager, 'generateId'))
|
||||
|
||||
print('✓ ScreenReaderSupportManager methods validated')
|
||||
|
||||
except Exception as e:
|
||||
self.fail(f'ScreenReaderSupportManager method test failed: {e}')
|
||||
|
||||
def test_user_preferences_initialization(self):
|
||||
"""Test that UserPreferencesManager initializes correctly."""
|
||||
try:
|
||||
from user_preferences import UserPreferencesManager
|
||||
|
||||
# Test initialization without Flask app
|
||||
manager = UserPreferencesManager()
|
||||
self.assertTrue(hasattr(manager, 'preferences'))
|
||||
self.assertIsInstance(manager.preferences, dict)
|
||||
self.assertGreater(len(manager.preferences), 0)
|
||||
|
||||
print('✓ UserPreferencesManager initialization validated')
|
||||
|
||||
except Exception as e:
|
||||
self.fail(f'UserPreferencesManager initialization test failed: {e}')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# Configure test runner
|
||||
unittest.main(verbosity=2, buffer=True)
|
||||
Reference in New Issue
Block a user