- Create AnsibleDeployStage using framework's Process module for secure command execution - Integrate AnsibleDeployStage into DeploymentPipelineCommands for production deployments - Add force_deploy flag support in Ansible playbook to override stale locks - Use PHP deployment module as orchestrator (php console.php deploy:production) - Fix ErrorAggregationInitializer to use Environment class instead of $_ENV superglobal Architecture: - BuildStage → AnsibleDeployStage → HealthCheckStage for production - Process module provides timeout, error handling, and output capture - Ansible playbook supports rollback via rollback-git-based.yml - Zero-downtime deployments with health checks
272 lines
12 KiB
PHP
272 lines
12 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
/**
|
||
* ML Adapter Integration Tests
|
||
*
|
||
* Tests all 3 ML adapters with ModelRegistry and ModelPerformanceMonitor:
|
||
* 1. QueueAnomalyModelAdapter
|
||
* 2. WafBehavioralModelAdapter
|
||
* 3. NPlusOneModelAdapter
|
||
*/
|
||
|
||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||
|
||
use App\Framework\Queue\MachineLearning\QueueAnomalyModelAdapter;
|
||
use App\Framework\Queue\MachineLearning\JobAnomalyDetector;
|
||
use App\Framework\Queue\MachineLearning\ValueObjects\JobFeatures;
|
||
use App\Framework\Waf\MachineLearning\WafBehavioralModelAdapter;
|
||
use App\Framework\Waf\MachineLearning\BehaviorAnomalyDetector;
|
||
use App\Framework\Waf\MachineLearning\ValueObjects\BehaviorFeatures;
|
||
use App\Framework\Database\NPlusOneDetection\MachineLearning\NPlusOneModelAdapter;
|
||
use App\Framework\Database\NPlusOneDetection\MachineLearning\NPlusOneDetectionEngine;
|
||
use App\Framework\MachineLearning\ModelManagement\InMemoryModelRegistry;
|
||
use App\Framework\MachineLearning\ModelManagement\ModelPerformanceMonitor;
|
||
use App\Framework\MachineLearning\ModelManagement\InMemoryPerformanceStorage;
|
||
use App\Framework\MachineLearning\ModelManagement\NullAlertingService;
|
||
use App\Framework\Core\ValueObjects\Score;
|
||
|
||
echo "=== ML Adapter Integration Tests ===\n\n";
|
||
|
||
try {
|
||
// Initialize shared infrastructure
|
||
echo "1. Initializing Model Management Infrastructure...\n";
|
||
$registry = new InMemoryModelRegistry();
|
||
$storage = new InMemoryPerformanceStorage();
|
||
$alerting = new NullAlertingService();
|
||
$performanceMonitor = new ModelPerformanceMonitor($registry, $storage, $alerting);
|
||
echo " ✓ ModelRegistry created\n";
|
||
echo " ✓ PerformanceStorage created\n";
|
||
echo " ✓ ModelPerformanceMonitor created\n\n";
|
||
|
||
// ========================================================================
|
||
// Test 1: QueueAnomalyModelAdapter
|
||
// ========================================================================
|
||
echo "2. Testing QueueAnomalyModelAdapter...\n";
|
||
|
||
// Create detector and adapter
|
||
$queueDetector = new JobAnomalyDetector(
|
||
anomalyThreshold: new Score(0.4),
|
||
zScoreThreshold: 3.0,
|
||
iqrMultiplier: 1.5
|
||
);
|
||
$queueAdapter = new QueueAnomalyModelAdapter(
|
||
$registry,
|
||
$performanceMonitor,
|
||
$queueDetector
|
||
);
|
||
|
||
// Register model
|
||
echo " → Registering queue-anomaly model...\n";
|
||
$queueMetadata = $queueAdapter->registerCurrentModel();
|
||
echo " ✓ Model registered: {$queueMetadata->modelName} v{$queueMetadata->version->toString()}\n";
|
||
|
||
// Test with normal features
|
||
echo " → Testing with normal job features...\n";
|
||
$normalFeatures = new JobFeatures(
|
||
executionTimeVariance: 0.15,
|
||
memoryUsagePattern: 0.10,
|
||
retryFrequency: 0.0,
|
||
failureRate: 0.05,
|
||
queueDepthCorrelation: 0.10,
|
||
dependencyChainComplexity: 0.08,
|
||
payloadSizeAnomaly: 0.05,
|
||
executionTimingRegularity: 0.30
|
||
);
|
||
|
||
$normalResult = $queueAdapter->analyzeWithTracking($normalFeatures, groundTruth: false);
|
||
echo " ✓ Analysis: " . ($normalResult['is_anomalous'] ? "ANOMALOUS" : "NORMAL") . "\n";
|
||
echo " ✓ Score: " . sprintf("%.2f%%", $normalResult['anomaly_score'] * 100) . "\n";
|
||
echo " ✓ Tracking: {$normalResult['tracking']['prediction']} (ground truth: false)\n";
|
||
|
||
// Test with anomalous features
|
||
echo " → Testing with anomalous job features...\n";
|
||
$anomalousFeatures = new JobFeatures(
|
||
executionTimeVariance: 0.85,
|
||
memoryUsagePattern: 0.75,
|
||
retryFrequency: 0.85,
|
||
failureRate: 0.65,
|
||
queueDepthCorrelation: 0.50,
|
||
dependencyChainComplexity: 0.30,
|
||
payloadSizeAnomaly: 0.35,
|
||
executionTimingRegularity: 0.20
|
||
);
|
||
|
||
$anomalousResult = $queueAdapter->analyzeWithTracking($anomalousFeatures, groundTruth: true);
|
||
echo " ✓ Analysis: " . ($anomalousResult['is_anomalous'] ? "ANOMALOUS" : "NORMAL") . "\n";
|
||
echo " ✓ Score: " . sprintf("%.2f%%", $anomalousResult['anomaly_score'] * 100) . "\n";
|
||
echo " ✓ Tracking: {$anomalousResult['tracking']['prediction']} (ground truth: true)\n";
|
||
|
||
// Get performance metrics
|
||
echo " → Checking performance metrics...\n";
|
||
$queueMetrics = $queueAdapter->getCurrentPerformanceMetrics();
|
||
echo " ✓ Total predictions: {$queueMetrics['total_predictions']}\n";
|
||
echo " ✓ Accuracy: " . sprintf("%.2f%%", $queueMetrics['accuracy'] * 100) . "\n\n";
|
||
|
||
// ========================================================================
|
||
// Test 2: WafBehavioralModelAdapter
|
||
// ========================================================================
|
||
echo "3. Testing WafBehavioralModelAdapter...\n";
|
||
|
||
// Create detector and adapter
|
||
$wafDetector = new BehaviorAnomalyDetector(
|
||
anomalyThreshold: new Score(0.5),
|
||
zScoreThreshold: 2.5,
|
||
iqrMultiplier: 1.5
|
||
);
|
||
$wafAdapter = new WafBehavioralModelAdapter(
|
||
$registry,
|
||
$performanceMonitor,
|
||
$wafDetector
|
||
);
|
||
|
||
// Register model
|
||
echo " → Registering waf-behavioral model...\n";
|
||
$wafMetadata = $wafAdapter->registerCurrentModel();
|
||
echo " ✓ Model registered: {$wafMetadata->modelName} v{$wafMetadata->version->toString()}\n";
|
||
|
||
// Test with benign request
|
||
echo " → Testing with benign request features...\n";
|
||
$benignFeatures = new BehaviorFeatures(
|
||
requestFrequency: 0.2,
|
||
endpointDiversity: 2.5, // Moderate diversity
|
||
parameterEntropy: 3.0, // Normal entropy
|
||
userAgentConsistency: 0.9, // Consistent UA
|
||
geographicAnomaly: 0.1, // Same location
|
||
timePatternRegularity: 0.3, // Human-like timing
|
||
payloadSimilarity: 0.4, // Varied payloads
|
||
httpMethodDistribution: 0.6 // Mixed methods
|
||
);
|
||
|
||
$benignResult = $wafAdapter->analyzeWithTracking($benignFeatures, historicalBaseline: [], groundTruth: false);
|
||
echo " ✓ Analysis: " . ($benignResult['is_anomalous'] ? "MALICIOUS" : "BENIGN") . "\n";
|
||
echo " ✓ Score: " . sprintf("%.2f%%", $benignResult['anomaly_score'] * 100) . "\n";
|
||
echo " ✓ Tracking: {$benignResult['tracking']['prediction']} (ground truth: false)\n";
|
||
|
||
// Test with malicious request
|
||
echo " → Testing with malicious request features...\n";
|
||
$maliciousFeatures = new BehaviorFeatures(
|
||
requestFrequency: 20.0, // Very high frequency (>10/s)
|
||
endpointDiversity: 0.5, // Low diversity (scanning)
|
||
parameterEntropy: 7.0, // High entropy (probing)
|
||
userAgentConsistency: 0.1, // Inconsistent UA
|
||
geographicAnomaly: 0.85, // Suspicious location changes
|
||
timePatternRegularity: 0.95, // Automated timing
|
||
payloadSimilarity: 0.9, // Repetitive payloads
|
||
httpMethodDistribution: 0.2 // Limited methods
|
||
);
|
||
|
||
$maliciousResult = $wafAdapter->analyzeWithTracking($maliciousFeatures, historicalBaseline: [], groundTruth: true);
|
||
echo " ✓ Analysis: " . ($maliciousResult['is_anomalous'] ? "MALICIOUS" : "BENIGN") . "\n";
|
||
echo " ✓ Score: " . sprintf("%.2f%%", $maliciousResult['anomaly_score'] * 100) . "\n";
|
||
echo " ✓ Tracking: {$maliciousResult['tracking']['prediction']} (ground truth: true)\n";
|
||
|
||
// Get performance metrics
|
||
echo " → Checking performance metrics...\n";
|
||
$wafMetrics = $wafAdapter->getCurrentPerformanceMetrics();
|
||
echo " ✓ Total predictions: {$wafMetrics['total_predictions']}\n";
|
||
echo " ✓ Accuracy: " . sprintf("%.2f%%", $wafMetrics['accuracy'] * 100) . "\n\n";
|
||
|
||
// ========================================================================
|
||
// Test 3: NPlusOneModelAdapter
|
||
// ========================================================================
|
||
echo "4. Testing NPlusOneModelAdapter...\n";
|
||
echo " ℹ️ Requires QueryExecutionContext and full NPlusOneDetectionEngine\n";
|
||
echo " ℹ️ Skipping for now (database-dependent)\n\n";
|
||
|
||
// ========================================================================
|
||
// Model Registry Tests
|
||
// ========================================================================
|
||
echo "5. Testing ModelRegistry Integration...\n";
|
||
|
||
// List all registered models
|
||
echo " → Listing registered models...\n";
|
||
$modelNames = $registry->getAllModelNames();
|
||
echo " ✓ Total model types registered: " . count($modelNames) . "\n";
|
||
|
||
foreach ($modelNames as $modelName) {
|
||
$versions = $registry->getAll($modelName);
|
||
foreach ($versions as $metadata) {
|
||
echo " - {$metadata->modelName} v{$metadata->version->toString()}\n";
|
||
echo " Type: {$metadata->modelType->value}\n";
|
||
echo " Created: {$metadata->createdAt->format('Y-m-d H:i:s')}\n";
|
||
}
|
||
}
|
||
|
||
// Test model existence
|
||
echo " → Testing model existence checks...\n";
|
||
$queueExists = $registry->exists('queue-anomaly', \App\Framework\Core\ValueObjects\Version::fromString('1.0.0'));
|
||
$wafExists = $registry->exists('waf-behavioral', \App\Framework\Core\ValueObjects\Version::fromString('1.0.0'));
|
||
echo " ✓ queue-anomaly exists: " . ($queueExists ? "YES" : "NO") . "\n";
|
||
echo " ✓ waf-behavioral exists: " . ($wafExists ? "YES" : "NO") . "\n\n";
|
||
|
||
// ========================================================================
|
||
// Performance Monitor Tests
|
||
// ========================================================================
|
||
echo "6. Testing ModelPerformanceMonitor Integration...\n";
|
||
|
||
// Get metrics for each registered model
|
||
echo " → Getting metrics for all registered models...\n";
|
||
$allMetrics = [];
|
||
|
||
foreach ($modelNames as $modelName) {
|
||
$versions = $registry->getAll($modelName);
|
||
foreach ($versions as $metadata) {
|
||
$metrics = $performanceMonitor->getCurrentMetrics(
|
||
$metadata->modelName,
|
||
$metadata->version
|
||
);
|
||
$modelKey = "{$metadata->modelName}@{$metadata->version->toString()}";
|
||
$allMetrics[$modelKey] = $metrics;
|
||
}
|
||
}
|
||
|
||
echo " ✓ Models tracked: " . count($allMetrics) . "\n";
|
||
|
||
foreach ($allMetrics as $modelKey => $metrics) {
|
||
echo " - $modelKey:\n";
|
||
echo " Predictions: {$metrics['total_predictions']}\n";
|
||
echo " Accuracy: " . sprintf("%.2f%%", $metrics['accuracy'] * 100) . "\n";
|
||
if ($metrics['total_predictions'] > 0) {
|
||
echo " Avg Confidence: " . sprintf("%.2f%%", $metrics['average_confidence'] * 100) . "\n";
|
||
}
|
||
}
|
||
|
||
// Check for performance degradation
|
||
echo "\n → Checking for performance degradation...\n";
|
||
$queueDegradation = $queueAdapter->checkPerformanceDegradation(0.05);
|
||
$wafDegradation = $wafAdapter->checkPerformanceDegradation(0.05);
|
||
|
||
echo " ✓ queue-anomaly degraded: " . ($queueDegradation['has_degraded'] ? "YES" : "NO") . "\n";
|
||
echo " ✓ waf-behavioral degraded: " . ($wafDegradation['has_degraded'] ? "YES" : "NO") . "\n\n";
|
||
|
||
// ========================================================================
|
||
// Test Summary
|
||
// ========================================================================
|
||
echo "=== Test Summary ===\n";
|
||
echo "✓ QueueAnomalyModelAdapter: Working\n";
|
||
echo "✓ WafBehavioralModelAdapter: Working\n";
|
||
echo "✓ NPlusOneModelAdapter: Skipped (database-dependent)\n";
|
||
echo "✓ ModelRegistry: Working\n";
|
||
echo "✓ ModelPerformanceMonitor: Working\n";
|
||
echo "✓ Model registration: Working\n";
|
||
echo "✓ Performance tracking: Working\n";
|
||
echo "✓ Accuracy calculation: Working\n\n";
|
||
|
||
echo "Test Results:\n";
|
||
echo " - Queue Adapter: 2 predictions, " . sprintf("%.0f%%", $queueMetrics['accuracy'] * 100) . " accuracy\n";
|
||
echo " - WAF Adapter: 2 predictions, " . sprintf("%.0f%%", $wafMetrics['accuracy'] * 100) . " accuracy\n";
|
||
echo " - Total models registered: " . $registry->getTotalCount() . "\n";
|
||
echo " - Total predictions tracked: " . array_sum(array_column($allMetrics, 'total_predictions')) . "\n\n";
|
||
|
||
echo "=== ML Adapter Tests PASSED ===\n";
|
||
|
||
} catch (\Throwable $e) {
|
||
echo "\n!!! TEST FAILED !!!\n";
|
||
echo "Error: " . $e->getMessage() . "\n";
|
||
echo "File: " . $e->getFile() . ":" . $e->getLine() . "\n";
|
||
echo "\nStack trace:\n" . $e->getTraceAsString() . "\n";
|
||
exit(1);
|
||
}
|