Files
AccountForger-neuerUpload/tests/test_method_rotation.py
Claude Project Manager 04585e95b6 Initial commit
2025-08-01 23:50:28 +02:00

611 Zeilen
22 KiB
Python

"""
Comprehensive tests for the method rotation system.
Tests all components: entities, repositories, use cases, and integration.
"""
import unittest
import os
import sys
import tempfile
import sqlite3
from datetime import datetime, timedelta
from unittest.mock import Mock, patch, MagicMock
# Add project root to path
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from domain.entities.method_rotation import (
MethodStrategy, RotationSession, RotationEvent, PlatformMethodState,
RiskLevel, RotationEventType, RotationStrategy
)
from application.use_cases.method_rotation_use_case import MethodRotationUseCase, RotationContext
from infrastructure.repositories.method_strategy_repository import MethodStrategyRepository
from infrastructure.repositories.rotation_session_repository import RotationSessionRepository
from infrastructure.repositories.platform_method_state_repository import PlatformMethodStateRepository
class MockDBManager:
"""Mock database manager for testing"""
def __init__(self):
self.db_path = tempfile.mktemp(suffix='.db')
self.connection = None
self._setup_test_database()
def _setup_test_database(self):
"""Create test database with rotation tables"""
conn = sqlite3.connect(self.db_path)
# Create rotation system tables
with open('database/migrations/add_method_rotation_system.sql', 'r') as f:
sql_script = f.read()
# Remove the INSERT statements for tests
sql_lines = sql_script.split('\n')
create_statements = [line for line in sql_lines if line.strip() and not line.strip().startswith('INSERT')]
clean_sql = '\n'.join(create_statements)
conn.executescript(clean_sql)
conn.close()
def get_connection(self):
if not self.connection:
self.connection = sqlite3.connect(self.db_path)
return self.connection
def execute_query(self, query, params=None):
conn = self.get_connection()
cursor = conn.cursor()
if params:
cursor.execute(query, params)
else:
cursor.execute(query)
conn.commit()
return cursor
def fetch_one(self, query, params=None):
conn = self.get_connection()
cursor = conn.cursor()
if params:
cursor.execute(query, params)
else:
cursor.execute(query)
return cursor.fetchone()
def fetch_all(self, query, params=None):
conn = self.get_connection()
cursor = conn.cursor()
if params:
cursor.execute(query, params)
else:
cursor.execute(query)
return cursor.fetchall()
def close(self):
if self.connection:
self.connection.close()
if os.path.exists(self.db_path):
os.unlink(self.db_path)
class TestMethodStrategy(unittest.TestCase):
"""Test MethodStrategy entity"""
def test_method_strategy_creation(self):
"""Test creating a method strategy"""
strategy = MethodStrategy(
strategy_id="test_id",
platform="instagram",
method_name="email",
priority=8,
risk_level=RiskLevel.LOW
)
self.assertEqual(strategy.strategy_id, "test_id")
self.assertEqual(strategy.platform, "instagram")
self.assertEqual(strategy.method_name, "email")
self.assertEqual(strategy.priority, 8)
self.assertEqual(strategy.risk_level, RiskLevel.LOW)
self.assertTrue(strategy.is_active)
def test_effectiveness_score_calculation(self):
"""Test effectiveness score calculation"""
strategy = MethodStrategy(
strategy_id="test_id",
platform="instagram",
method_name="email",
priority=8,
success_rate=0.9,
failure_rate=0.1,
risk_level=RiskLevel.LOW
)
score = strategy.effectiveness_score
self.assertGreater(score, 0.8) # High priority, high success rate should score well
def test_cooldown_functionality(self):
"""Test cooldown period functionality"""
strategy = MethodStrategy(
strategy_id="test_id",
platform="instagram",
method_name="email",
cooldown_period=300,
last_failure=datetime.now() - timedelta(seconds=100)
)
self.assertTrue(strategy.is_on_cooldown)
self.assertGreater(strategy.cooldown_remaining_seconds, 0)
# Test expired cooldown
strategy.last_failure = datetime.now() - timedelta(seconds=400)
self.assertFalse(strategy.is_on_cooldown)
def test_performance_update(self):
"""Test performance metrics update"""
strategy = MethodStrategy(
strategy_id="test_id",
platform="instagram",
method_name="email",
success_rate=0.5,
failure_rate=0.5
)
# Update with success
strategy.update_performance(True, 120.0)
self.assertGreater(strategy.success_rate, 0.5)
self.assertLess(strategy.failure_rate, 0.5)
self.assertIsNotNone(strategy.last_success)
# Update with failure
original_success_rate = strategy.success_rate
strategy.update_performance(False)
self.assertLess(strategy.success_rate, original_success_rate)
self.assertIsNotNone(strategy.last_failure)
class TestRotationSession(unittest.TestCase):
"""Test RotationSession entity"""
def test_rotation_session_creation(self):
"""Test creating a rotation session"""
session = RotationSession(
session_id="test_session",
platform="instagram",
current_method="email"
)
self.assertEqual(session.session_id, "test_session")
self.assertEqual(session.platform, "instagram")
self.assertEqual(session.current_method, "email")
self.assertTrue(session.is_active)
self.assertEqual(session.rotation_count, 0)
def test_session_metrics(self):
"""Test session metrics calculation"""
session = RotationSession(
session_id="test_session",
platform="instagram",
current_method="email"
)
# Add some attempts
session.add_attempt("email", True)
session.add_attempt("email", False)
session.add_attempt("phone", True)
self.assertEqual(session.success_count, 2)
self.assertEqual(session.failure_count, 1)
self.assertAlmostEqual(session.success_rate, 2/3, places=2)
def test_rotation_logic(self):
"""Test rotation decision logic"""
session = RotationSession(
session_id="test_session",
platform="instagram",
current_method="email"
)
# Add failures to trigger rotation
session.add_attempt("email", False)
session.add_attempt("email", False)
self.assertTrue(session.should_rotate)
# Test rotation
session.rotate_to_method("phone", "consecutive_failures")
self.assertEqual(session.current_method, "phone")
self.assertEqual(session.rotation_count, 1)
self.assertEqual(session.rotation_reason, "consecutive_failures")
class TestMethodStrategyRepository(unittest.TestCase):
"""Test MethodStrategyRepository"""
def setUp(self):
self.db_manager = MockDBManager()
self.repo = MethodStrategyRepository(self.db_manager)
def tearDown(self):
self.db_manager.close()
def test_save_and_find_strategy(self):
"""Test saving and finding strategies"""
strategy = MethodStrategy(
strategy_id="test_strategy",
platform="instagram",
method_name="email",
priority=8,
risk_level=RiskLevel.LOW
)
# Save strategy
self.repo.save(strategy)
# Find by ID
found_strategy = self.repo.find_by_id("test_strategy")
self.assertIsNotNone(found_strategy)
self.assertEqual(found_strategy.strategy_id, "test_strategy")
self.assertEqual(found_strategy.platform, "instagram")
self.assertEqual(found_strategy.method_name, "email")
def test_find_active_by_platform(self):
"""Test finding active strategies by platform"""
# Create multiple strategies
strategies = [
MethodStrategy("s1", "instagram", "email", 8, risk_level=RiskLevel.LOW, success_rate=0.9),
MethodStrategy("s2", "instagram", "phone", 6, risk_level=RiskLevel.MEDIUM, success_rate=0.7),
MethodStrategy("s3", "instagram", "social", 4, risk_level=RiskLevel.HIGH, success_rate=0.3, is_active=False),
MethodStrategy("s4", "tiktok", "email", 8, risk_level=RiskLevel.LOW, success_rate=0.8)
]
for strategy in strategies:
self.repo.save(strategy)
# Find active Instagram strategies
active_strategies = self.repo.find_active_by_platform("instagram")
self.assertEqual(len(active_strategies), 2) # Only active ones
self.assertEqual(active_strategies[0].method_name, "email") # Highest effectiveness
def test_get_next_available_method(self):
"""Test getting next available method"""
# Create strategies
strategies = [
MethodStrategy("s1", "instagram", "email", 8, risk_level=RiskLevel.LOW, success_rate=0.9),
MethodStrategy("s2", "instagram", "phone", 6, risk_level=RiskLevel.MEDIUM, success_rate=0.7),
]
for strategy in strategies:
self.repo.save(strategy)
# Get next method excluding email
next_method = self.repo.get_next_available_method("instagram", ["email"])
self.assertIsNotNone(next_method)
self.assertEqual(next_method.method_name, "phone")
# Get next method with no exclusions
best_method = self.repo.get_next_available_method("instagram")
self.assertIsNotNone(best_method)
self.assertEqual(best_method.method_name, "email") # Best strategy
def test_platform_statistics(self):
"""Test platform statistics calculation"""
# Create strategies with different metrics
strategies = [
MethodStrategy("s1", "instagram", "email", 8, risk_level=RiskLevel.LOW,
success_rate=0.9, last_success=datetime.now()),
MethodStrategy("s2", "instagram", "phone", 6, risk_level=RiskLevel.MEDIUM,
success_rate=0.6, last_failure=datetime.now()),
]
for strategy in strategies:
self.repo.save(strategy)
stats = self.repo.get_platform_statistics("instagram")
self.assertEqual(stats['total_methods'], 2)
self.assertEqual(stats['active_methods'], 2)
self.assertGreater(stats['avg_success_rate'], 0)
self.assertEqual(stats['recent_successes_24h'], 1)
class TestRotationUseCase(unittest.TestCase):
"""Test MethodRotationUseCase"""
def setUp(self):
self.db_manager = MockDBManager()
self.strategy_repo = MethodStrategyRepository(self.db_manager)
self.session_repo = RotationSessionRepository(self.db_manager)
self.state_repo = PlatformMethodStateRepository(self.db_manager)
self.use_case = MethodRotationUseCase(
self.strategy_repo, self.session_repo, self.state_repo
)
# Setup test data
self._setup_test_strategies()
def tearDown(self):
self.db_manager.close()
def _setup_test_strategies(self):
"""Setup test strategies"""
strategies = [
MethodStrategy("instagram_email", "instagram", "email", 8,
risk_level=RiskLevel.LOW, success_rate=0.9),
MethodStrategy("instagram_phone", "instagram", "phone", 6,
risk_level=RiskLevel.MEDIUM, success_rate=0.7),
MethodStrategy("tiktok_email", "tiktok", "email", 8,
risk_level=RiskLevel.LOW, success_rate=0.8),
]
for strategy in strategies:
self.strategy_repo.save(strategy)
def test_start_rotation_session(self):
"""Test starting a rotation session"""
context = RotationContext(
platform="instagram",
account_id="test_account"
)
session = self.use_case.start_rotation_session(context)
self.assertIsNotNone(session)
self.assertEqual(session.platform, "instagram")
self.assertEqual(session.current_method, "email") # Best method
self.assertTrue(session.is_active)
def test_get_optimal_method(self):
"""Test getting optimal method"""
context = RotationContext(platform="instagram")
method = self.use_case.get_optimal_method(context)
self.assertIsNotNone(method)
self.assertEqual(method.method_name, "email") # Best strategy
# Test with exclusions
context.excluded_methods = ["email"]
method = self.use_case.get_optimal_method(context)
self.assertEqual(method.method_name, "phone")
def test_method_rotation(self):
"""Test method rotation"""
# Start session
context = RotationContext(platform="instagram")
session = self.use_case.start_rotation_session(context)
# Record failure to trigger rotation
self.use_case.record_method_result(
session.session_id, "email", False, 0.0,
{'error_type': 'rate_limit', 'message': 'Rate limited'}
)
# Check if rotation should occur
should_rotate = self.use_case.should_rotate_method(session.session_id)
if should_rotate:
# Attempt rotation
next_method = self.use_case.rotate_method(session.session_id, "rate_limit")
self.assertIsNotNone(next_method)
self.assertEqual(next_method.method_name, "phone")
def test_emergency_mode(self):
"""Test emergency mode functionality"""
# Enable emergency mode
self.use_case.enable_emergency_mode("instagram", "test_emergency")
# Check that platform state reflects emergency mode
state = self.state_repo.find_by_platform("instagram")
self.assertTrue(state.emergency_mode)
# Disable emergency mode
self.use_case.disable_emergency_mode("instagram")
state = self.state_repo.find_by_platform("instagram")
self.assertFalse(state.emergency_mode)
def test_performance_tracking(self):
"""Test performance tracking and metrics"""
context = RotationContext(platform="instagram")
session = self.use_case.start_rotation_session(context)
# Record success
self.use_case.record_method_result(
session.session_id, "email", True, 120.0
)
# Get recommendations
recommendations = self.use_case.get_platform_method_recommendations("instagram")
self.assertIn('platform', recommendations)
self.assertIn('recommended_methods', recommendations)
self.assertGreater(len(recommendations['recommended_methods']), 0)
class TestIntegration(unittest.TestCase):
"""Integration tests for the complete rotation system"""
def setUp(self):
self.db_manager = MockDBManager()
def tearDown(self):
self.db_manager.close()
def test_complete_rotation_workflow(self):
"""Test complete rotation workflow from start to finish"""
# Initialize components
strategy_repo = MethodStrategyRepository(self.db_manager)
session_repo = RotationSessionRepository(self.db_manager)
state_repo = PlatformMethodStateRepository(self.db_manager)
use_case = MethodRotationUseCase(strategy_repo, session_repo, state_repo)
# Setup strategies
strategies = [
MethodStrategy("instagram_email", "instagram", "email", 8,
risk_level=RiskLevel.LOW, success_rate=0.9, max_daily_attempts=20),
MethodStrategy("instagram_phone", "instagram", "phone", 6,
risk_level=RiskLevel.MEDIUM, success_rate=0.7, max_daily_attempts=10),
]
for strategy in strategies:
strategy_repo.save(strategy)
# 1. Start rotation session
context = RotationContext(platform="instagram", account_id="test_account")
session = use_case.start_rotation_session(context)
self.assertIsNotNone(session)
self.assertEqual(session.current_method, "email")
# 2. Simulate failure and rotation
use_case.record_method_result(
session.session_id, "email", False, 0.0,
{'error_type': 'rate_limit', 'message': 'Rate limited'}
)
# Check rotation trigger
if use_case.should_rotate_method(session.session_id):
next_method = use_case.rotate_method(session.session_id, "rate_limit")
self.assertEqual(next_method.method_name, "phone")
# 3. Simulate success with new method
use_case.record_method_result(
session.session_id, "phone", True, 180.0
)
# 4. Verify session is completed
session_status = use_case.get_session_status(session.session_id)
self.assertIsNotNone(session_status)
def test_error_handling_and_fallback(self):
"""Test error handling and fallback mechanisms"""
# Test with invalid platform
strategy_repo = MethodStrategyRepository(self.db_manager)
session_repo = RotationSessionRepository(self.db_manager)
state_repo = PlatformMethodStateRepository(self.db_manager)
use_case = MethodRotationUseCase(strategy_repo, session_repo, state_repo)
# Try to get method for platform with no strategies
context = RotationContext(platform="nonexistent")
method = use_case.get_optimal_method(context)
self.assertIsNone(method) # Should handle gracefully
def test_concurrent_sessions(self):
"""Test handling multiple concurrent sessions"""
strategy_repo = MethodStrategyRepository(self.db_manager)
session_repo = RotationSessionRepository(self.db_manager)
state_repo = PlatformMethodStateRepository(self.db_manager)
use_case = MethodRotationUseCase(strategy_repo, session_repo, state_repo)
# Setup strategy
strategy = MethodStrategy("instagram_email", "instagram", "email", 8,
risk_level=RiskLevel.LOW, success_rate=0.9)
strategy_repo.save(strategy)
# Start multiple sessions
sessions = []
for i in range(3):
context = RotationContext(platform="instagram", account_id=f"account_{i}")
session = use_case.start_rotation_session(context)
sessions.append(session)
# Verify all sessions are active and distinct
self.assertEqual(len(sessions), 3)
session_ids = [s.session_id for s in sessions]
self.assertEqual(len(set(session_ids)), 3) # All unique
class TestMixinIntegration(unittest.TestCase):
"""Test mixin integration with controllers"""
def test_controller_mixin_integration(self):
"""Test that controller mixins work correctly"""
from controllers.platform_controllers.method_rotation_mixin import MethodRotationMixin
# Create mock controller with mixin
class MockController(MethodRotationMixin):
def __init__(self):
self.platform_name = "instagram"
self.db_manager = MockDBManager()
self.logger = Mock()
self._init_method_rotation_system()
controller = MockController()
# Test that rotation system is initialized
self.assertIsNotNone(controller.method_rotation_use_case)
# Test availability check
self.assertTrue(controller._should_use_rotation_system())
# Cleanup
controller.db_manager.close()
def test_worker_mixin_integration(self):
"""Test worker thread mixin integration"""
from controllers.platform_controllers.method_rotation_worker_mixin import MethodRotationWorkerMixin
# Create mock worker with mixin
class MockWorker(MethodRotationWorkerMixin):
def __init__(self):
self.params = {'registration_method': 'email'}
self.log_signal = Mock()
self.rotation_retry_count = 0
self.max_rotation_retries = 3
self.controller_instance = None
worker = MockWorker()
# Test initialization
worker._init_rotation_support()
# Test availability check
available = worker._is_rotation_available()
self.assertFalse(available) # No controller instance
# Test error classification
error_type = worker._classify_error("Rate limit exceeded")
self.assertEqual(error_type, "rate_limit")
if __name__ == '__main__':
# Create test suite
test_suite = unittest.TestSuite()
# Add test cases
test_cases = [
TestMethodStrategy,
TestRotationSession,
TestMethodStrategyRepository,
TestRotationUseCase,
TestIntegration,
TestMixinIntegration
]
for test_case in test_cases:
tests = unittest.TestLoader().loadTestsFromTestCase(test_case)
test_suite.addTests(tests)
# Run tests
runner = unittest.TextTestRunner(verbosity=2)
result = runner.run(test_suite)
# Print summary
print(f"\nTest Summary:")
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}")
# Exit with appropriate code
sys.exit(0 if result.wasSuccessful() else 1)