- 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.
197 lines
4.8 KiB
PHP
197 lines
4.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
/**
|
|
* Simple Test: SSE Channel Attribute Rendering
|
|
*
|
|
* Tests LiveComponentRenderer::renderWithWrapper() to verify
|
|
* data-sse-channel attribute is rendered when provided.
|
|
*/
|
|
|
|
require_once __DIR__ . '/../../vendor/autoload.php';
|
|
|
|
use App\Framework\Http\Session\SessionInterface;
|
|
use App\Framework\Security\Csrf\CsrfToken;
|
|
use App\Framework\View\LiveComponentRenderer;
|
|
use App\Framework\View\TemplateRenderer;
|
|
|
|
echo "=== SSE Channel Attribute Test ===\n\n";
|
|
|
|
// Create mock session for CSRF
|
|
$session = new class () implements SessionInterface {
|
|
public object $csrf;
|
|
|
|
public function __construct()
|
|
{
|
|
$this->csrf = new class () {
|
|
public function generateToken(string $formId): CsrfToken
|
|
{
|
|
return new CsrfToken('test-csrf-token-' . $formId);
|
|
}
|
|
};
|
|
}
|
|
|
|
public function getId(): string
|
|
{
|
|
return 'test-session';
|
|
}
|
|
|
|
public function start(): void
|
|
{
|
|
}
|
|
|
|
public function destroy(): void
|
|
{
|
|
}
|
|
|
|
public function regenerateId(): void
|
|
{
|
|
}
|
|
|
|
public function get(string $key, mixed $default = null): mixed
|
|
{
|
|
return $default;
|
|
}
|
|
|
|
public function set(string $key, mixed $value): void
|
|
{
|
|
}
|
|
|
|
public function has(string $key): bool
|
|
{
|
|
return false;
|
|
}
|
|
|
|
public function remove(string $key): void
|
|
{
|
|
}
|
|
|
|
public function clear(): void
|
|
{
|
|
}
|
|
|
|
public function getFlashBag(): mixed
|
|
{
|
|
return null;
|
|
}
|
|
};
|
|
|
|
// Create a mock TemplateRenderer (we won't use it for this test)
|
|
$templateRenderer = new class () implements TemplateRenderer {
|
|
public function render(...$args): string
|
|
{
|
|
return '';
|
|
}
|
|
|
|
public function renderPartial(...$args): string
|
|
{
|
|
return '';
|
|
}
|
|
};
|
|
|
|
// Create LiveComponentRenderer
|
|
$renderer = new LiveComponentRenderer($templateRenderer, $session);
|
|
|
|
// Test 1: Render WITHOUT SSE channel
|
|
echo "Test 1: Render without SSE channel\n";
|
|
echo "-----------------------------------\n";
|
|
|
|
$html1 = $renderer->renderWithWrapper(
|
|
componentId: 'test-component:demo',
|
|
componentHtml: '<p>Test Content</p>',
|
|
state: ['count' => 0],
|
|
sseChannel: null
|
|
);
|
|
|
|
if (str_contains($html1, 'data-sse-channel=')) {
|
|
echo "❌ FAIL: data-sse-channel should NOT be present when sseChannel is null\n";
|
|
} else {
|
|
echo "✅ SUCCESS: data-sse-channel not present (as expected)\n";
|
|
}
|
|
|
|
// Verify other attributes are present
|
|
$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: Render WITH SSE channel
|
|
echo "Test 2: Render with SSE channel\n";
|
|
echo "-----------------------------------\n";
|
|
|
|
$html2 = $renderer->renderWithWrapper(
|
|
componentId: 'notification-center:user-123',
|
|
componentHtml: '<div>Notifications</div>',
|
|
state: ['notifications' => []],
|
|
sseChannel: 'user:user-123'
|
|
);
|
|
|
|
if (str_contains($html2, 'data-sse-channel=')) {
|
|
echo "✅ SUCCESS: data-sse-channel attribute found\n";
|
|
|
|
// Extract and verify value
|
|
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 incorrect. Expected: user:user-123, Got: {$channel}\n";
|
|
}
|
|
}
|
|
} else {
|
|
echo "❌ FAIL: data-sse-channel should be present when sseChannel is provided\n";
|
|
echo "HTML: " . substr($html2, 0, 200) . "\n";
|
|
}
|
|
|
|
// Verify all attributes are present
|
|
foreach ($requiredAttrs as $attr) {
|
|
if (str_contains($html2, $attr . '=')) {
|
|
echo "✅ {$attr} present\n";
|
|
} else {
|
|
echo "❌ {$attr} MISSING\n";
|
|
}
|
|
}
|
|
|
|
echo "\n";
|
|
|
|
// Test 3: Full HTML structure verification
|
|
echo "Test 3: HTML Structure\n";
|
|
echo "-----------------------------------\n";
|
|
|
|
echo "Sample HTML (first 400 chars):\n";
|
|
echo substr($html2, 0, 400) . "\n\n";
|
|
|
|
// Test 4: Special characters in channel name
|
|
echo "Test 4: Special characters handling\n";
|
|
echo "-----------------------------------\n";
|
|
|
|
$html3 = $renderer->renderWithWrapper(
|
|
componentId: 'test:special',
|
|
componentHtml: '<p>Test</p>',
|
|
state: ['test' => true],
|
|
sseChannel: 'presence:room-<special>&"chars"'
|
|
);
|
|
|
|
if (preg_match('/data-sse-channel="([^"]+)"/', $html3, $matches)) {
|
|
$channel = $matches[1];
|
|
echo "Rendered channel: {$channel}\n";
|
|
|
|
// Check if HTML entities are escaped properly
|
|
if (str_contains($channel, '<') || str_contains($channel, '"')) {
|
|
echo "✅ Special characters properly escaped\n";
|
|
} else {
|
|
echo "⚠️ Special characters may not be escaped: {$channel}\n";
|
|
}
|
|
}
|
|
|
|
echo "\n=== Test Complete ===\n";
|