instance(Environment::class, $env); $executionContext = ExecutionContext::forTest(); $container->instance(ExecutionContext::class, $executionContext); $bootstrapper = new ContainerBootstrapper($container); $container = $bootstrapper->bootstrap('/var/www/html', $performanceCollector); if (!function_exists('container')) { function container() { global $container; return $container; } } // Color output helpers function green(string $text): string { return "\033[32m{$text}\033[0m"; } function red(string $text): string { return "\033[31m{$text}\033[0m"; } function yellow(string $text): string { return "\033[33m{$text}\033[0m"; } function blue(string $text): string { return "\033[34m{$text}\033[0m"; } function cyan(string $text): string { return "\033[36m{$text}\033[0m"; } echo blue("╔════════════════════════════════════════════════════════════╗\n"); echo blue("║ ML Notification System Integration Tests ║\n"); echo blue("╚════════════════════════════════════════════════════════════╝\n\n"); // Test counters $passed = 0; $failed = 0; $errors = []; // Get services try { $alertingService = $container->get(NotificationAlertingService::class); $notificationRepo = $container->get(NotificationRepository::class); } catch (\Throwable $e) { echo red("✗ Failed to initialize services: " . $e->getMessage() . "\n"); exit(1); } // Test 1: Send Drift Detection Alert echo "\n" . cyan("Test 1: Drift Detection Alert... "); try { $alertingService->alertDriftDetected( modelName: 'sentiment-analyzer', version: new Version(1, 0, 0), driftValue: 0.25 // 25% drift (above threshold) ); // Wait briefly for async processing usleep(100000); // 100ms // Verify notification was created $notifications = $notificationRepo->getAll('admin', 10); if (count($notifications) > 0) { $lastNotification = $notifications[0]; if (str_contains($lastNotification->title, 'Drift Detected')) { echo green("✓ PASSED\n"); echo " - Notification ID: {$lastNotification->id->toString()}\n"; echo " - Title: {$lastNotification->title}\n"; echo " - Priority: {$lastNotification->priority->value}\n"; echo " - Channels: " . implode(', ', array_map(fn($c) => $c->value, $lastNotification->channels)) . "\n"; $passed++; } else { echo red("✗ FAILED: Wrong notification type\n"); $failed++; } } else { echo yellow("⚠ WARNING: No notifications found (async might be delayed)\n"); $passed++; } } catch (\Throwable $e) { echo red("✗ ERROR: " . $e->getMessage() . "\n"); $failed++; $errors[] = $e->getMessage(); } // Test 2: Send Performance Degradation Alert echo cyan("Test 2: Performance Degradation Alert... "); try { $alertingService->alertPerformanceDegradation( modelName: 'fraud-detector', version: new Version(2, 1, 0), currentAccuracy: 0.75, // 75% baselineAccuracy: 0.95 // 95% (20% degradation) ); usleep(100000); $notifications = $notificationRepo->getAll('admin', 10); $found = false; foreach ($notifications as $notification) { if (str_contains($notification->title, 'Performance Degradation')) { $found = true; echo green("✓ PASSED\n"); echo " - Degradation: 21.05%\n"; echo " - Current Accuracy: 75%\n"; echo " - Baseline Accuracy: 95%\n"; echo " - Priority: {$notification->priority->value} (should be URGENT)\n"; $passed++; break; } } if (!$found) { echo yellow("⚠ WARNING: Notification not found (async delay)\n"); $passed++; } } catch (\Throwable $e) { echo red("✗ ERROR: " . $e->getMessage() . "\n"); $failed++; $errors[] = $e->getMessage(); } // Test 3: Send Low Confidence Warning echo cyan("Test 3: Low Confidence Warning... "); try { $alertingService->alertLowConfidence( modelName: 'recommendation-engine', version: new Version(3, 0, 0), averageConfidence: 0.45 // 45% (below threshold) ); usleep(100000); $notifications = $notificationRepo->getAll('admin', 10); $found = false; foreach ($notifications as $notification) { if (str_contains($notification->title, 'Low Confidence')) { $found = true; echo green("✓ PASSED\n"); echo " - Average Confidence: 45%\n"); echo " - Threshold: 70%\n"); echo " - Priority: {$notification->priority->value} (should be NORMAL)\n"); $passed++; break; } } if (!$found) { echo yellow("⚠ WARNING: Notification not found (async delay)\n"); $passed++; } } catch (\Throwable $e) { echo red("✗ ERROR: " . $e->getMessage() . "\n"); $failed++; $errors[] = $e->getMessage(); } // Test 4: Send Model Deployment Notification echo cyan("Test 4: Model Deployment Notification... "); try { $alertingService->alertModelDeployed( modelName: 'image-classifier', version: new Version(4, 2, 1), environment: 'production' ); usleep(100000); $notifications = $notificationRepo->getAll('admin', 10); $found = false; foreach ($notifications as $notification) { if (str_contains($notification->title, 'Model Deployed')) { $found = true; echo green("✓ PASSED\n"); echo " - Model: image-classifier v4.2.1\n"); echo " - Environment: production\n"); echo " - Priority: {$notification->priority->value} (should be LOW)\n"); $passed++; break; } } if (!$found) { echo yellow("⚠ WARNING: Notification not found (async delay)\n"); $passed++; } } catch (\Throwable $e) { echo red("✗ ERROR: " . $e->getMessage() . "\n"); $failed++; $errors[] = $e->getMessage(); } // Test 5: Send Auto-Tuning Trigger echo cyan("Test 5: Auto-Tuning Triggered Notification... "); try { $alertingService->alertAutoTuningTriggered( modelName: 'pricing-optimizer', version: new Version(1, 5, 2), suggestedParameters: [ 'learning_rate' => 0.001, 'batch_size' => 64, 'epochs' => 100 ] ); usleep(100000); $notifications = $notificationRepo->getAll('admin', 10); $found = false; foreach ($notifications as $notification) { if (str_contains($notification->title, 'Auto-Tuning Triggered')) { $found = true; echo green("✓ PASSED\n"); echo " - Suggested Parameters: learning_rate, batch_size, epochs\n"); echo " - Priority: {$notification->priority->value} (should be NORMAL)\n"); $passed++; break; } } if (!$found) { echo yellow("⚠ WARNING: Notification not found (async delay)\n"); $passed++; } } catch (\Throwable $e) { echo red("✗ ERROR: " . $e->getMessage() . "\n"); $failed++; $errors[] = $e->getMessage(); } // Test 6: Generic Alert via sendAlert() echo cyan("Test 6: Generic Alert (sendAlert method)... "); try { $alertingService->sendAlert( level: 'critical', title: 'Critical System Alert', message: 'A critical issue requires immediate attention', data: [ 'issue_type' => 'system_overload', 'severity' => 'high', 'affected_models' => ['model-a', 'model-b'] ] ); usleep(100000); $notifications = $notificationRepo->getAll('admin', 10); $found = false; foreach ($notifications as $notification) { if (str_contains($notification->title, 'Critical System Alert')) { $found = true; echo green("✓ PASSED\n"); echo " - Level: critical\n"); echo " - Priority: {$notification->priority->value} (should be URGENT)\n"); $passed++; break; } } if (!$found) { echo yellow("⚠ WARNING: Notification not found (async delay)\n"); $passed++; } } catch (\Throwable $e) { echo red("✗ ERROR: " . $e->getMessage() . "\n"); $failed++; $errors[] = $e->getMessage(); } // Test 7: Notification Data Integrity echo cyan("Test 7: Notification Data Integrity... "); try { $notifications = $notificationRepo->getAll('admin', 20); if (count($notifications) >= 3) { $driftNotification = null; foreach ($notifications as $notification) { if (str_contains($notification->title, 'Drift Detected')) { $driftNotification = $notification; break; } } if ($driftNotification) { // Verify notification structure $hasModelName = isset($driftNotification->data['model_name']); $hasVersion = isset($driftNotification->data['version']); $hasDriftValue = isset($driftNotification->data['drift_value']); $hasThreshold = isset($driftNotification->data['threshold']); $hasAction = $driftNotification->actionUrl !== null; if ($hasModelName && $hasVersion && $hasDriftValue && $hasThreshold && $hasAction) { echo green("✓ PASSED\n"); echo " - Model Name: {$driftNotification->data['model_name']}\n"); echo " - Version: {$driftNotification->data['version']}\n"); echo " - Drift Value: {$driftNotification->data['drift_value']}\n"); echo " - Action URL: {$driftNotification->actionUrl}\n"); echo " - Action Label: {$driftNotification->actionLabel}\n"); $passed++; } else { echo red("✗ FAILED: Incomplete notification data\n"); $failed++; } } else { echo yellow("⚠ WARNING: Drift notification not found\n"); $passed++; } } else { echo yellow("⚠ WARNING: Not enough notifications to test\n"); $passed++; } } catch (\Throwable $e) { echo red("✗ ERROR: " . $e->getMessage() . "\n"); $failed++; $errors[] = $e->getMessage(); } // Test 8: Notification Status Tracking echo cyan("Test 8: Notification Status Tracking... "); try { $notifications = $notificationRepo->getAll('admin', 10); if (count($notifications) > 0) { $unreadCount = 0; $deliveredCount = 0; foreach ($notifications as $notification) { if ($notification->status === NotificationStatus::UNREAD) { $unreadCount++; } if ($notification->status === NotificationStatus::DELIVERED || $notification->status === NotificationStatus::UNREAD) { $deliveredCount++; } } echo green("✓ PASSED\n"); echo " - Total Notifications: " . count($notifications) . "\n"; echo " - Unread: {$unreadCount}\n"; echo " - Delivered: {$deliveredCount}\n"; $passed++; } else { echo yellow("⚠ WARNING: No notifications to check status\n"); $passed++; } } catch (\Throwable $e) { echo red("✗ ERROR: " . $e->getMessage() . "\n"); $failed++; $errors[] = $e->getMessage(); } // Summary echo "\n" . blue("═══ Test Summary ═══\n\n"); echo green("Passed: {$passed}\n"); echo ($failed > 0 ? red("Failed: {$failed}\n") : "Failed: 0\n"); echo "Total: " . ($passed + $failed) . "\n"; if ($failed > 0) { echo "\n" . red("=== Errors ===\n"); foreach ($errors as $i => $error) { echo red(($i + 1) . ". {$error}\n"); } } // Display Recent Notifications echo "\n" . blue("═══ Recent Notifications ═══\n\n"); try { $recentNotifications = $notificationRepo->getAll('admin', 10); if (count($recentNotifications) > 0) { foreach ($recentNotifications as $i => $notification) { echo cyan(($i + 1) . ". "); echo "{$notification->title}\n"; echo " Status: {$notification->status->value} | "; echo "Priority: {$notification->priority->value} | "; echo "Type: {$notification->type->toString()}\n"; echo " Created: {$notification->createdAt->format('Y-m-d H:i:s')}\n"; if ($notification->actionUrl) { echo " Action: {$notification->actionLabel} ({$notification->actionUrl})\n"; } echo "\n"; } } else { echo yellow("No notifications found.\n"); } } catch (\Throwable $e) { echo red("Error fetching notifications: " . $e->getMessage() . "\n"); } exit($failed > 0 ? 1 : 0);