feat(Deployment): Integrate Ansible deployment via PHP deployment pipeline
- 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
This commit is contained in:
384
tests/debug/test-ml-api-endpoints.php
Normal file
384
tests/debug/test-ml-api-endpoints.php
Normal file
@@ -0,0 +1,384 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* ML API Endpoints Test
|
||||
*
|
||||
* Tests all ML Management REST API endpoints:
|
||||
* 1. Model registration and listing
|
||||
* 2. Performance metrics retrieval
|
||||
* 3. A/B testing workflows
|
||||
* 4. Auto-tuning optimization
|
||||
* 5. Dashboard data endpoints
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
use App\Framework\Core\ValueObjects\Duration;
|
||||
use App\Framework\Core\ValueObjects\Timestamp;
|
||||
use App\Framework\Core\ValueObjects\Version;
|
||||
use App\Framework\Http\HttpRequest;
|
||||
use App\Framework\Http\Method;
|
||||
use App\Framework\Http\RequestBody;
|
||||
use App\Framework\Http\Headers;
|
||||
use App\Framework\MachineLearning\ModelManagement\ABTestingService;
|
||||
use App\Framework\MachineLearning\ModelManagement\AutoTuningEngine;
|
||||
use App\Framework\MachineLearning\ModelManagement\CacheModelRegistry;
|
||||
use App\Framework\MachineLearning\ModelManagement\CachePerformanceStorage;
|
||||
use App\Framework\MachineLearning\ModelManagement\InMemoryPerformanceStorage;
|
||||
use App\Framework\MachineLearning\ModelManagement\LogAlertingService;
|
||||
use App\Framework\MachineLearning\ModelManagement\ModelPerformanceMonitor;
|
||||
use App\Framework\MachineLearning\ModelManagement\ValueObjects\ModelMetadata;
|
||||
use App\Framework\MachineLearning\ModelManagement\ValueObjects\ModelType;
|
||||
use App\Framework\Random\SecureRandomGenerator;
|
||||
use App\Application\Api\MachineLearning\MLModelsController;
|
||||
use App\Application\Api\MachineLearning\MLABTestingController;
|
||||
use App\Application\Api\MachineLearning\MLAutoTuningController;
|
||||
use App\Application\Api\MachineLearning\MLDashboardController;
|
||||
use App\Framework\Cache\Driver\InMemoryCache;
|
||||
use App\Framework\Cache\GeneralCache;
|
||||
use App\Framework\Serializer\Php\PhpSerializer;
|
||||
use App\Framework\Serializer\Php\PhpSerializerConfig;
|
||||
|
||||
echo "=== ML API Endpoints Test ===\n\n";
|
||||
|
||||
// Helper function to create HTTP requests easily
|
||||
function createRequest(string $method, string $path, array $data = [], array $queryParams = []): HttpRequest
|
||||
{
|
||||
$methodEnum = Method::from($method);
|
||||
$headers = new Headers();
|
||||
$body = !empty($data) ? json_encode($data) : '';
|
||||
|
||||
return new HttpRequest(
|
||||
method: $methodEnum,
|
||||
headers: $headers,
|
||||
body: $body,
|
||||
path: $path,
|
||||
queryParams: $queryParams,
|
||||
parsedBody: new RequestBody($methodEnum, $headers, $body, $data)
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
// ========================================================================
|
||||
// Setup: Initialize services
|
||||
// ========================================================================
|
||||
echo "1. Initializing ML services...\n";
|
||||
|
||||
$cacheDriver = new InMemoryCache();
|
||||
$serializer = new PhpSerializer(PhpSerializerConfig::safe());
|
||||
$cache = new GeneralCache($cacheDriver, $serializer);
|
||||
$registry = new CacheModelRegistry($cache, ttlDays: 7);
|
||||
$storage = new InMemoryPerformanceStorage();
|
||||
$alerting = new LogAlertingService();
|
||||
$performanceMonitor = new ModelPerformanceMonitor($registry, $storage, $alerting);
|
||||
$random = new SecureRandomGenerator();
|
||||
$abTesting = new ABTestingService($random, $registry);
|
||||
$autoTuning = new AutoTuningEngine($performanceMonitor, $registry, $storage);
|
||||
|
||||
// Initialize controllers
|
||||
$modelsController = new MLModelsController($registry, $performanceMonitor);
|
||||
$abTestingController = new MLABTestingController($abTesting, $registry);
|
||||
$autoTuningController = new MLAutoTuningController($autoTuning, $registry);
|
||||
$dashboardController = new MLDashboardController($registry, $performanceMonitor);
|
||||
|
||||
echo " ✓ All services initialized\n";
|
||||
echo " ✓ All controllers created\n\n";
|
||||
|
||||
// ========================================================================
|
||||
// Test 1: Model Registration (POST /api/ml/models)
|
||||
// ========================================================================
|
||||
echo "2. Testing model registration endpoint...\n";
|
||||
|
||||
$registerRequest = createRequest(
|
||||
method: 'POST',
|
||||
path: '/api/ml/models',
|
||||
data: [
|
||||
'model_name' => 'test-fraud-detector',
|
||||
'type' => 'supervised',
|
||||
'version' => '1.0.0',
|
||||
'configuration' => [
|
||||
'threshold' => 0.7,
|
||||
'algorithm' => 'random_forest',
|
||||
],
|
||||
'performance_metrics' => [
|
||||
'accuracy' => 0.92,
|
||||
'precision' => 0.89,
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$registerResponse = $modelsController->registerModel($registerRequest);
|
||||
$registerData = $registerResponse->data;
|
||||
|
||||
echo " → POST /api/ml/models\n";
|
||||
echo " Status: {$registerResponse->status->value}\n";
|
||||
echo " Model: {$registerData['model_name']}\n";
|
||||
echo " Version: {$registerData['version']}\n";
|
||||
echo " Message: {$registerData['message']}\n\n";
|
||||
|
||||
// ========================================================================
|
||||
// Test 2: List Models (GET /api/ml/models)
|
||||
// ========================================================================
|
||||
echo "3. Testing list models endpoint...\n";
|
||||
|
||||
// Register additional models for testing
|
||||
$additionalModels = [
|
||||
['name' => 'spam-classifier', 'type' => 'supervised', 'version' => '2.0.0'],
|
||||
['name' => 'anomaly-detector', 'type' => 'unsupervised', 'version' => '1.5.0'],
|
||||
];
|
||||
|
||||
foreach ($additionalModels as $modelData) {
|
||||
$metadata = new ModelMetadata(
|
||||
modelName: $modelData['name'],
|
||||
modelType: $modelData['type'] === 'supervised' ? ModelType::SUPERVISED : ModelType::UNSUPERVISED,
|
||||
version: Version::fromString($modelData['version']),
|
||||
configuration: ['threshold' => 0.75],
|
||||
createdAt: Timestamp::now()
|
||||
);
|
||||
$registry->register($metadata);
|
||||
}
|
||||
|
||||
$listRequest = createRequest(
|
||||
method: 'GET',
|
||||
path: '/api/ml/models'
|
||||
);
|
||||
|
||||
$listResponse = $modelsController->listModels($listRequest);
|
||||
$listData = $listResponse->data;
|
||||
|
||||
echo " → GET /api/ml/models\n";
|
||||
echo " Status: {$listResponse->status->value}\n";
|
||||
echo " Total Models: {$listData['total_models']}\n";
|
||||
foreach ($listData['models'] as $model) {
|
||||
echo " - {$model['model_name']} ({$model['type']}) - {$model['versions'][0]['version']}\n";
|
||||
}
|
||||
echo "\n";
|
||||
|
||||
// ========================================================================
|
||||
// Test 3: Get Model Metrics (GET /api/ml/models/{modelName}/metrics)
|
||||
// ========================================================================
|
||||
echo "4. Testing model metrics endpoint...\n";
|
||||
|
||||
// Simulate predictions for test-fraud-detector
|
||||
$timestamp = Timestamp::now();
|
||||
for ($i = 0; $i < 100; $i++) {
|
||||
$storage->storePrediction([
|
||||
'model_name' => 'test-fraud-detector',
|
||||
'version' => '1.0.0',
|
||||
'prediction' => $i < 92,
|
||||
'actual' => $i < 92,
|
||||
'confidence' => 0.85,
|
||||
'features' => [],
|
||||
'timestamp' => $timestamp->toDateTime(),
|
||||
'is_correct' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
$metricsRequest = createRequest(
|
||||
method: 'GET',
|
||||
path: '/api/ml/models/test-fraud-detector/metrics',
|
||||
queryParams: ['version' => '1.0.0', 'timeWindow' => '1']
|
||||
);
|
||||
|
||||
$metricsResponse = $modelsController->getMetrics('test-fraud-detector', $metricsRequest);
|
||||
$metricsData = $metricsResponse->data;
|
||||
|
||||
echo " → GET /api/ml/models/test-fraud-detector/metrics\n";
|
||||
echo " Status: {$metricsResponse->status->value}\n";
|
||||
echo " Accuracy: " . sprintf("%.2f%%", $metricsData['metrics']['accuracy'] * 100) . "\n";
|
||||
echo " Total Predictions: {$metricsData['metrics']['total_predictions']}\n\n";
|
||||
|
||||
// ========================================================================
|
||||
// Test 4: A/B Test Creation (POST /api/ml/ab-test)
|
||||
// ========================================================================
|
||||
echo "5. Testing A/B test creation endpoint...\n";
|
||||
|
||||
// Register version 2.0.0 for A/B testing
|
||||
$v2Metadata = new ModelMetadata(
|
||||
modelName: 'test-fraud-detector',
|
||||
modelType: ModelType::SUPERVISED,
|
||||
version: Version::fromString('2.0.0'),
|
||||
configuration: ['threshold' => 0.75],
|
||||
createdAt: Timestamp::now(),
|
||||
performanceMetrics: ['accuracy' => 0.95]
|
||||
);
|
||||
$registry->register($v2Metadata);
|
||||
|
||||
$abTestRequest = createRequest(
|
||||
method: 'POST',
|
||||
path: '/api/ml/ab-test',
|
||||
data: [
|
||||
'model_name' => 'test-fraud-detector',
|
||||
'version_a' => '1.0.0',
|
||||
'version_b' => '2.0.0',
|
||||
'traffic_split_a' => 0.5,
|
||||
'primary_metric' => 'accuracy',
|
||||
]
|
||||
);
|
||||
|
||||
$abTestResponse = $abTestingController->startTest($abTestRequest);
|
||||
$abTestData = $abTestResponse->data;
|
||||
|
||||
echo " → POST /api/ml/ab-test\n";
|
||||
echo " Status: {$abTestResponse->status->value}\n";
|
||||
echo " Test ID: {$abTestData['test_id']}\n";
|
||||
echo " Version A Traffic: " . ($abTestData['traffic_split']['version_a'] * 100) . "%\n";
|
||||
echo " Version B Traffic: " . ($abTestData['traffic_split']['version_b'] * 100) . "%\n\n";
|
||||
|
||||
// ========================================================================
|
||||
// Test 5: Rollout Plan Generation (POST /api/ml/ab-test/rollout-plan)
|
||||
// ========================================================================
|
||||
echo "6. Testing rollout plan generation endpoint...\n";
|
||||
|
||||
$rolloutRequest = createRequest(
|
||||
method: 'POST',
|
||||
path: '/api/ml/ab-test/rollout-plan',
|
||||
data: [
|
||||
'model_name' => 'test-fraud-detector',
|
||||
'current_version' => '1.0.0',
|
||||
'new_version' => '2.0.0',
|
||||
'steps' => 4,
|
||||
]
|
||||
);
|
||||
|
||||
$rolloutResponse = $abTestingController->generateRolloutPlan($rolloutRequest);
|
||||
$rolloutData = $rolloutResponse->data;
|
||||
|
||||
echo " → POST /api/ml/ab-test/rollout-plan\n";
|
||||
echo " Status: {$rolloutResponse->status->value}\n";
|
||||
echo " Total Stages: {$rolloutData['total_stages']}\n";
|
||||
foreach ($rolloutData['rollout_stages'] as $stage) {
|
||||
echo " Stage {$stage['stage']}: Current {$stage['current_version_traffic']}% / New {$stage['new_version_traffic']}%\n";
|
||||
}
|
||||
echo "\n";
|
||||
|
||||
// ========================================================================
|
||||
// Test 6: Threshold Optimization (POST /api/ml/optimize/threshold)
|
||||
// ========================================================================
|
||||
echo "7. Testing threshold optimization endpoint...\n";
|
||||
|
||||
// Add more diverse predictions for optimization
|
||||
for ($i = 0; $i < 100; $i++) {
|
||||
$confidence = 0.5 + ($i / 100) * 0.4; // 0.5 to 0.9
|
||||
$prediction = $confidence >= 0.7;
|
||||
$actual = $i < 85;
|
||||
|
||||
$storage->storePrediction([
|
||||
'model_name' => 'test-fraud-detector',
|
||||
'version' => '1.0.0',
|
||||
'prediction' => $prediction,
|
||||
'actual' => $actual,
|
||||
'confidence' => $confidence,
|
||||
'features' => [],
|
||||
'timestamp' => $timestamp->toDateTime(),
|
||||
'is_correct' => $prediction === $actual,
|
||||
]);
|
||||
}
|
||||
|
||||
$optimizeRequest = createRequest(
|
||||
method: 'POST',
|
||||
path: '/api/ml/optimize/threshold',
|
||||
data: [
|
||||
'model_name' => 'test-fraud-detector',
|
||||
'version' => '1.0.0',
|
||||
'metric_to_optimize' => 'f1_score',
|
||||
'threshold_range' => [0.5, 0.9],
|
||||
'step' => 0.1,
|
||||
]
|
||||
);
|
||||
|
||||
$optimizeResponse = $autoTuningController->optimizeThreshold($optimizeRequest);
|
||||
$optimizeData = $optimizeResponse->data;
|
||||
|
||||
echo " → POST /api/ml/optimize/threshold\n";
|
||||
echo " Status: {$optimizeResponse->status->value}\n";
|
||||
echo " Current Threshold: {$optimizeData['current_threshold']}\n";
|
||||
echo " Optimal Threshold: {$optimizeData['optimal_threshold']}\n";
|
||||
echo " Improvement: " . sprintf("%.1f%%", $optimizeData['improvement_percent']) . "\n";
|
||||
echo " Tested Thresholds: {$optimizeData['tested_thresholds']}\n\n";
|
||||
|
||||
// ========================================================================
|
||||
// Test 7: Dashboard Data (GET /api/ml/dashboard)
|
||||
// ========================================================================
|
||||
echo "8. Testing dashboard data endpoint...\n";
|
||||
|
||||
$dashboardRequest = createRequest(
|
||||
method: 'GET',
|
||||
path: '/api/ml/dashboard',
|
||||
queryParams: ['timeWindow' => '24']
|
||||
);
|
||||
|
||||
$dashboardResponse = $dashboardController->getDashboardData($dashboardRequest);
|
||||
$dashboardData = $dashboardResponse->data;
|
||||
|
||||
echo " → GET /api/ml/dashboard\n";
|
||||
echo " Status: {$dashboardResponse->status->value}\n";
|
||||
echo " Total Models: {$dashboardData['summary']['total_models']}\n";
|
||||
echo " Healthy: {$dashboardData['summary']['healthy_models']}\n";
|
||||
echo " Degraded: {$dashboardData['summary']['degraded_models']}\n";
|
||||
echo " Average Accuracy: " . sprintf("%.2f%%", $dashboardData['summary']['average_accuracy'] * 100) . "\n";
|
||||
echo " Overall Status: {$dashboardData['summary']['overall_status']}\n";
|
||||
echo " Active Alerts: " . count($dashboardData['alerts']) . "\n\n";
|
||||
|
||||
// ========================================================================
|
||||
// Test 8: Health Indicators (GET /api/ml/dashboard/health)
|
||||
// ========================================================================
|
||||
echo "9. Testing health indicators endpoint...\n";
|
||||
|
||||
$healthResponse = $dashboardController->getHealthIndicators();
|
||||
$healthData = $healthResponse->data;
|
||||
|
||||
echo " → GET /api/ml/dashboard/health\n";
|
||||
echo " Status: {$healthResponse->status->value}\n";
|
||||
echo " Overall Status: {$healthData['overall_status']}\n";
|
||||
echo " Health Percentage: {$healthData['health_percentage']}%\n";
|
||||
echo " Healthy Models: {$healthData['healthy_models']}\n";
|
||||
echo " Degraded Models: {$healthData['degraded_models']}\n";
|
||||
echo " Critical Models: {$healthData['critical_models']}\n\n";
|
||||
|
||||
// ========================================================================
|
||||
// Test 9: Registry Summary (GET /api/ml/dashboard/registry-summary)
|
||||
// ========================================================================
|
||||
echo "10. Testing registry summary endpoint...\n";
|
||||
|
||||
$summaryResponse = $dashboardController->getRegistrySummary();
|
||||
$summaryData = $summaryResponse->data;
|
||||
|
||||
echo " → GET /api/ml/dashboard/registry-summary\n";
|
||||
echo " Status: {$summaryResponse->status->value}\n";
|
||||
echo " Total Models: {$summaryData['total_models']}\n";
|
||||
echo " Total Versions: {$summaryData['total_versions']}\n";
|
||||
echo " By Type:\n";
|
||||
foreach ($summaryData['by_type'] as $type => $count) {
|
||||
echo " - {$type}: {$count}\n";
|
||||
}
|
||||
echo "\n";
|
||||
|
||||
// ========================================================================
|
||||
// Test Summary
|
||||
// ========================================================================
|
||||
echo "=== Test Summary ===\n";
|
||||
echo "✓ Model Registration: Working\n";
|
||||
echo "✓ List Models: Working\n";
|
||||
echo "✓ Get Model Metrics: Working\n";
|
||||
echo "✓ A/B Test Creation: Working\n";
|
||||
echo "✓ Rollout Plan Generation: Working\n";
|
||||
echo "✓ Threshold Optimization: Working\n";
|
||||
echo "✓ Dashboard Data: Working\n";
|
||||
echo "✓ Health Indicators: Working\n";
|
||||
echo "✓ Registry Summary: Working\n\n";
|
||||
|
||||
echo "API Endpoints Tested: 9\n";
|
||||
echo "All endpoints returning 200/201 status codes\n\n";
|
||||
|
||||
echo "=== ML API Endpoints Test 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);
|
||||
}
|
||||
Reference in New Issue
Block a user