Files
michaelschiemer/tests/debug/test-livecomponents-integration.php
Michael Schiemer fc3d7e6357 feat(Production): Complete production deployment infrastructure
- 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.
2025-10-25 19:18:37 +02:00

282 lines
8.9 KiB
PHP

<?php
declare(strict_types=1);
require_once __DIR__ . '/../../vendor/autoload.php';
use App\Framework\LiveComponents\ComponentEventDispatcher;
use App\Framework\LiveComponents\ValueObjects\ActionParameters;
use App\Framework\LiveComponents\ValueObjects\ComponentData;
use App\Framework\LiveComponents\ValueObjects\ComponentEvent;
use App\Framework\LiveComponents\ValueObjects\ComponentId;
use App\Framework\LiveComponents\ValueObjects\EventPayload;
echo "Testing LiveComponents Integration with Value Objects\n";
echo "=====================================================\n\n";
$testsPassed = 0;
$testsFailed = 0;
function test(string $name, callable $fn): void
{
global $testsPassed, $testsFailed;
try {
$fn();
echo "{$name}\n";
$testsPassed++;
} catch (Throwable $e) {
echo "{$name}\n";
echo " Error: {$e->getMessage()}\n";
echo " File: {$e->getFile()}:{$e->getLine()}\n";
$testsFailed++;
}
}
// Test 1: ComponentId creation and usage
test('ComponentId: Create and parse', function () {
$id = ComponentId::create('chart', 'user-123');
assert($id->name === 'chart');
assert($id->instanceId === 'user-123');
assert($id->toString() === 'chart:user-123');
$parsed = ComponentId::fromString('chart:user-123');
assert($parsed->equals($id));
});
// Test 2: ComponentData immutability
test('ComponentData: Immutable updates', function () {
$data1 = ComponentData::fromArray(['count' => 0]);
$data2 = $data1->with('count', 5);
assert($data1->get('count') === 0); // Original unchanged
assert($data2->get('count') === 5); // New instance updated
});
// Test 3: ActionParameters type coercion
test('ActionParameters: Type coercion', function () {
$params = ActionParameters::fromArray([
'count' => '42',
'price' => '19.99',
'active' => '1',
]);
assert($params->getInt('count') === 42);
assert($params->getFloat('price') === 19.99);
assert($params->getBool('active') === true);
});
// Test 4: EventPayload creation and access
test('EventPayload: Create and access', function () {
$payload = EventPayload::fromArray([
'user_id' => 123,
'action' => 'clicked',
'timestamp' => time(),
]);
assert($payload->getInt('user_id') === 123);
assert($payload->getString('action') === 'clicked');
assert($payload->has('timestamp'));
});
// Test 5: ComponentEvent broadcast
test('ComponentEvent: Broadcast event', function () {
$payload = EventPayload::fromArray(['value' => 42]);
$event = ComponentEvent::broadcast('counter:updated', $payload);
assert($event->name === 'counter:updated');
assert($event->isBroadcast());
assert(! $event->isTargeted());
assert($event->payload->getInt('value') === 42);
});
// Test 6: ComponentEvent targeted
test('ComponentEvent: Targeted event', function () {
$payload = EventPayload::fromArray(['message' => 'Hello']);
$event = ComponentEvent::target('notification:show', 'notification:instance-1', $payload);
assert($event->name === 'notification:show');
assert($event->isTargeted());
assert(! $event->isBroadcast());
assert($event->targetsComponent('notification:instance-1'));
assert($event->payload->getString('message') === 'Hello');
});
// Test 7: ComponentEventDispatcher
test('ComponentEventDispatcher: Dispatch events', function () {
$dispatcher = new ComponentEventDispatcher();
$payload1 = EventPayload::fromArray(['count' => 1]);
$dispatcher->dispatch('event1', $payload1);
$payload2 = EventPayload::fromArray(['count' => 2]);
$dispatcher->dispatchTo('event2', 'target:123', $payload2);
assert($dispatcher->hasEvents());
$events = $dispatcher->getEvents();
assert(count($events) === 2);
assert($events[0]->name === 'event1');
assert($events[0]->isBroadcast());
assert($events[1]->name === 'event2');
assert($events[1]->isTargeted());
});
// Test 8: Full integration scenario
test('Integration: Complete component lifecycle', function () {
// 1. Create component identity
$componentId = ComponentId::create('shopping-cart', 'session-abc');
// 2. Initialize component data
$initialData = ComponentData::fromArray([
'items' => [],
'total' => 0,
]);
// 3. Simulate action with parameters
$actionParams = ActionParameters::fromArray([
'product_id' => 'prod-123',
'quantity' => 2,
'price' => 29.99,
]);
// 4. Update component data
$items = $initialData->get('items', []);
$items[] = [
'product_id' => $actionParams->getString('product_id'),
'quantity' => $actionParams->getInt('quantity'),
'price' => $actionParams->getFloat('price'),
];
$newData = $initialData
->with('items', $items)
->with('total', $actionParams->getFloat('price') * $actionParams->getInt('quantity'));
// 5. Dispatch event
$dispatcher = new ComponentEventDispatcher();
$eventPayload = EventPayload::fromArray([
'item_count' => count($items),
'total' => $newData->get('total'),
]);
$dispatcher->dispatch('cart:updated', $eventPayload);
// Assertions
assert($componentId->toString() === 'shopping-cart:session-abc');
assert(count($newData->get('items')) === 1);
assert($newData->get('total') === 59.98);
assert($dispatcher->hasEvents());
$events = $dispatcher->getEvents();
assert($events[0]->name === 'cart:updated');
assert($events[0]->payload->getInt('item_count') === 1);
});
// Test 9: Empty payload handling
test('EventPayload: Empty payload', function () {
$event = ComponentEvent::broadcast('notification:clear');
assert($event->payload->isEmpty());
assert($event->payload->size() === 0);
});
// Test 10: ComponentData merge
test('ComponentData: Merge operations', function () {
$data1 = ComponentData::fromArray(['a' => 1, 'b' => 2]);
$data2 = ComponentData::fromArray(['c' => 3, 'd' => 4]);
$merged = $data1->merge($data2);
assert($merged->get('a') === 1);
assert($merged->get('b') === 2);
assert($merged->get('c') === 3);
assert($merged->get('d') === 4);
});
// Test 11: ActionParameters validation
test('ActionParameters: Required parameters', function () {
$params = ActionParameters::fromArray(['name' => 'John']);
try {
$params->requireString('email');
assert(false, 'Should have thrown exception');
} catch (InvalidArgumentException $e) {
assert(str_contains($e->getMessage(), 'missing'));
}
});
// Test 12: ComponentEvent serialization
test('ComponentEvent: Array serialization', function () {
$payload = EventPayload::fromArray(['key' => 'value']);
$event = ComponentEvent::broadcast('test:event', $payload);
$array = $event->toArray();
assert($array['name'] === 'test:event');
assert($array['payload']['key'] === 'value');
assert($array['target'] === null);
});
// Test 13: ComponentData filtering
test('ComponentData: Only/Except filtering', function () {
$data = ComponentData::fromArray(['a' => 1, 'b' => 2, 'c' => 3, 'd' => 4]);
$only = $data->only(['a', 'c']);
assert($only->size() === 2);
assert($only->has('a') && $only->has('c'));
assert(! $only->has('b') && ! $only->has('d'));
$except = $data->except(['b', 'd']);
assert($except->size() === 2);
assert($except->has('a') && $except->has('c'));
assert(! $except->has('b') && ! $except->has('d'));
});
// Test 14: Type safety enforcement
test('Type Safety: No primitive arrays allowed', function () {
// ComponentEvent only accepts EventPayload, not arrays
$payload = EventPayload::fromArray(['test' => 'data']);
$event = ComponentEvent::broadcast('test', $payload);
assert($event->payload instanceof EventPayload);
});
// Test 15: Complex nested data
test('Complex: Nested component state', function () {
$componentId = ComponentId::generate('data-table');
$data = ComponentData::fromArray([
'columns' => [
['name' => 'id', 'sortable' => true],
['name' => 'name', 'sortable' => true],
['name' => 'email', 'sortable' => false],
],
'rows' => [
['id' => 1, 'name' => 'John', 'email' => 'john@example.com'],
['id' => 2, 'name' => 'Jane', 'email' => 'jane@example.com'],
],
'pagination' => [
'page' => 1,
'per_page' => 10,
'total' => 2,
],
]);
$columns = $data->getArray('columns');
assert(count($columns) === 3);
$pagination = $data->get('pagination');
assert($pagination['page'] === 1);
assert($pagination['total'] === 2);
});
echo "\n";
echo "=====================================================\n";
echo "Tests passed: {$testsPassed}\n";
echo "Tests failed: {$testsFailed}\n";
echo "=====================================================\n";
if ($testsFailed > 0) {
exit(1);
}
echo "\n✅ All LiveComponents integration tests passed!\n";
echo "✅ Type safety refactoring complete!\n";