- Add comprehensive health check system with multiple endpoints - Add Prometheus metrics endpoint - Add production logging configurations (5 strategies) - Add complete deployment documentation suite: * QUICKSTART.md - 30-minute deployment guide * DEPLOYMENT_CHECKLIST.md - Printable verification checklist * DEPLOYMENT_WORKFLOW.md - Complete deployment lifecycle * PRODUCTION_DEPLOYMENT.md - Comprehensive technical reference * production-logging.md - Logging configuration guide * ANSIBLE_DEPLOYMENT.md - Infrastructure as Code automation * README.md - Navigation hub * DEPLOYMENT_SUMMARY.md - Executive summary - Add deployment scripts and automation - Add DEPLOYMENT_PLAN.md - Concrete plan for immediate deployment - Update README with production-ready features All production infrastructure is now complete and ready for deployment.
181 lines
4.8 KiB
PHP
181 lines
4.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
/**
|
|
* Unit Test: SSE Channel HTML Rendering
|
|
*
|
|
* Direct test of the HTML rendering logic for data-sse-channel attribute.
|
|
* Tests the sprintf logic without needing full framework bootstrap.
|
|
*/
|
|
|
|
require_once __DIR__ . '/../../vendor/autoload.php';
|
|
|
|
echo "=== SSE Channel HTML Rendering Unit Test ===\n\n";
|
|
|
|
// Test the HTML generation logic directly
|
|
function renderComponentWrapper(
|
|
string $componentId,
|
|
string $componentHtml,
|
|
array $state,
|
|
string $csrfToken,
|
|
?string $sseChannel = null
|
|
): string {
|
|
$componentName = explode(':', $componentId)[0] ?? 'unknown';
|
|
|
|
$stateJson = json_encode([
|
|
'id' => $componentId,
|
|
'component' => $componentName,
|
|
'data' => $state,
|
|
'version' => 1,
|
|
]);
|
|
|
|
// Build attributes
|
|
$attributes = [
|
|
'data-live-component' => $componentId,
|
|
'data-state' => $stateJson,
|
|
'data-csrf-token' => $csrfToken,
|
|
];
|
|
|
|
// Add SSE channel if provided
|
|
if ($sseChannel !== null) {
|
|
$attributes['data-sse-channel'] = $sseChannel;
|
|
}
|
|
|
|
// Build attribute string
|
|
$attributeString = implode(' ', array_map(
|
|
fn ($key, $value) => sprintf('%s="%s"', $key, htmlspecialchars($value)),
|
|
array_keys($attributes),
|
|
$attributes
|
|
));
|
|
|
|
return sprintf(
|
|
'<div %s>%s</div>',
|
|
$attributeString,
|
|
$componentHtml
|
|
);
|
|
}
|
|
|
|
// Test 1: Without SSE channel
|
|
echo "Test 1: Render without SSE channel\n";
|
|
echo "-----------------------------------\n";
|
|
|
|
$html1 = renderComponentWrapper(
|
|
componentId: 'counter:demo',
|
|
componentHtml: '<p>Count: 0</p>',
|
|
state: ['count' => 0],
|
|
csrfToken: 'test-csrf-123',
|
|
sseChannel: null
|
|
);
|
|
|
|
echo "HTML:\n{$html1}\n\n";
|
|
|
|
if (str_contains($html1, 'data-sse-channel')) {
|
|
echo "❌ FAIL: data-sse-channel should NOT be present\n";
|
|
} else {
|
|
echo "✅ PASS: data-sse-channel not present (correct)\n";
|
|
}
|
|
|
|
$requiredAttrs = ['data-live-component', 'data-state', 'data-csrf-token'];
|
|
foreach ($requiredAttrs as $attr) {
|
|
if (str_contains($html1, $attr)) {
|
|
echo "✅ {$attr} present\n";
|
|
} else {
|
|
echo "❌ {$attr} MISSING\n";
|
|
}
|
|
}
|
|
|
|
echo "\n";
|
|
|
|
// Test 2: With SSE channel
|
|
echo "Test 2: Render with SSE channel\n";
|
|
echo "-----------------------------------\n";
|
|
|
|
$html2 = renderComponentWrapper(
|
|
componentId: 'notification-center:user-123',
|
|
componentHtml: '<div class="notifications">...</div>',
|
|
state: ['notifications' => [], 'unreadCount' => 5],
|
|
csrfToken: 'test-csrf-456',
|
|
sseChannel: 'user:user-123'
|
|
);
|
|
|
|
echo "HTML (first 300 chars):\n" . substr($html2, 0, 300) . "...\n\n";
|
|
|
|
if (str_contains($html2, 'data-sse-channel')) {
|
|
echo "✅ PASS: data-sse-channel present\n";
|
|
|
|
if (preg_match('/data-sse-channel="([^"]+)"/', $html2, $matches)) {
|
|
$channel = $matches[1];
|
|
echo " Channel value: {$channel}\n";
|
|
|
|
if ($channel === 'user:user-123') {
|
|
echo "✅ Channel value correct\n";
|
|
} else {
|
|
echo "❌ Channel value wrong. Expected: user:user-123, Got: {$channel}\n";
|
|
}
|
|
}
|
|
} else {
|
|
echo "❌ FAIL: data-sse-channel should be present\n";
|
|
}
|
|
|
|
foreach ($requiredAttrs as $attr) {
|
|
if (str_contains($html2, $attr)) {
|
|
echo "✅ {$attr} present\n";
|
|
} else {
|
|
echo "❌ {$attr} MISSING\n";
|
|
}
|
|
}
|
|
|
|
echo "\n";
|
|
|
|
// Test 3: LivePresence with presence channel
|
|
echo "Test 3: LivePresence with presence: channel\n";
|
|
echo "-----------------------------------\n";
|
|
|
|
$html3 = renderComponentWrapper(
|
|
componentId: 'live-presence:room-lobby',
|
|
componentHtml: '<div class="presence">...</div>',
|
|
state: ['users' => []],
|
|
csrfToken: 'test-csrf-789',
|
|
sseChannel: 'presence:room-lobby'
|
|
);
|
|
|
|
if (preg_match('/data-sse-channel="([^"]+)"/', $html3, $matches)) {
|
|
$channel = $matches[1];
|
|
echo "Channel value: {$channel}\n";
|
|
|
|
if ($channel === 'presence:room-lobby') {
|
|
echo "✅ Presence channel correct\n";
|
|
} else {
|
|
echo "❌ Channel wrong. Expected: presence:room-lobby, Got: {$channel}\n";
|
|
}
|
|
}
|
|
|
|
echo "\n";
|
|
|
|
// Test 4: Special characters escaping
|
|
echo "Test 4: Special characters escaping\n";
|
|
echo "-----------------------------------\n";
|
|
|
|
$html4 = renderComponentWrapper(
|
|
componentId: 'test:special',
|
|
componentHtml: '<p>Test</p>',
|
|
state: ['test' => true],
|
|
csrfToken: 'test-csrf',
|
|
sseChannel: 'channel:<test>&"quotes"'
|
|
);
|
|
|
|
if (preg_match('/data-sse-channel="([^"]+)"/', $html4, $matches)) {
|
|
$channel = $matches[1];
|
|
echo "Original: channel:<test>&\"quotes\"\n";
|
|
echo "Rendered: {$channel}\n";
|
|
|
|
if (str_contains($channel, '<') && str_contains($channel, '"')) {
|
|
echo "✅ Special characters properly escaped\n";
|
|
} else {
|
|
echo "❌ Special characters NOT escaped properly\n";
|
|
}
|
|
}
|
|
|
|
echo "\n=== All Tests Complete ===\n";
|