- 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.
218 lines
7.0 KiB
PHP
218 lines
7.0 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
require_once __DIR__ . '/../../vendor/autoload.php';
|
|
|
|
use App\Application\Components\CounterComponent;
|
|
use App\Framework\LiveComponents\ComponentEventDispatcher;
|
|
use App\Framework\LiveComponents\ValueObjects\ActionParameters;
|
|
use App\Framework\LiveComponents\ValueObjects\ComponentData;
|
|
use App\Framework\LiveComponents\ValueObjects\ComponentId;
|
|
|
|
echo "Testing CounterComponent with Strict 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: Constructor accepts only Value Objects
|
|
test('Constructor: Only accepts ComponentId and ComponentData', function () {
|
|
$id = ComponentId::create('counter', 'test-1');
|
|
$data = ComponentData::fromArray(['count' => 5]);
|
|
|
|
$component = new CounterComponent($id, $data);
|
|
|
|
assert($component->getId() instanceof ComponentId);
|
|
assert($component->getData() instanceof ComponentData);
|
|
assert($component->getId()->equals($id));
|
|
assert($component->getData()->get('count') === 5);
|
|
});
|
|
|
|
// Test 2: Constructor with empty data
|
|
test('Constructor: Works with null initialData', function () {
|
|
$id = ComponentId::create('counter', 'test-2');
|
|
|
|
$component = new CounterComponent($id);
|
|
|
|
assert($component->getData()->isEmpty());
|
|
});
|
|
|
|
// Test 3: Increment returns ComponentData
|
|
test('increment(): Returns ComponentData', function () {
|
|
$id = ComponentId::create('counter', 'test-3');
|
|
$data = ComponentData::fromArray(['count' => 0]);
|
|
$component = new CounterComponent($id, $data);
|
|
|
|
$params = ActionParameters::empty();
|
|
$dispatcher = new ComponentEventDispatcher();
|
|
|
|
$result = $component->increment($params, $dispatcher);
|
|
|
|
assert($result instanceof ComponentData);
|
|
assert($result->get('count') === 1);
|
|
assert($result->has('last_update'));
|
|
|
|
// Verify event was dispatched
|
|
assert($dispatcher->hasEvents());
|
|
$events = $dispatcher->getEvents();
|
|
assert(count($events) === 1);
|
|
assert($events[0]->name === 'counter:changed');
|
|
});
|
|
|
|
// Test 4: Decrement returns ComponentData
|
|
test('decrement(): Returns ComponentData', function () {
|
|
$id = ComponentId::create('counter', 'test-4');
|
|
$data = ComponentData::fromArray(['count' => 5]);
|
|
$component = new CounterComponent($id, $data);
|
|
|
|
$params = ActionParameters::empty();
|
|
$dispatcher = new ComponentEventDispatcher();
|
|
|
|
$result = $component->decrement($params, $dispatcher);
|
|
|
|
assert($result instanceof ComponentData);
|
|
assert($result->get('count') === 4);
|
|
});
|
|
|
|
// Test 5: Reset returns ComponentData
|
|
test('reset(): Returns ComponentData', function () {
|
|
$id = ComponentId::create('counter', 'test-5');
|
|
$data = ComponentData::fromArray(['count' => 42]);
|
|
$component = new CounterComponent($id, $data);
|
|
|
|
$params = ActionParameters::empty();
|
|
$dispatcher = new ComponentEventDispatcher();
|
|
|
|
$result = $component->reset($params, $dispatcher);
|
|
|
|
assert($result instanceof ComponentData);
|
|
assert($result->get('count') === 0);
|
|
|
|
// Verify reset event was dispatched
|
|
$events = $dispatcher->getEvents();
|
|
assert(count($events) === 1);
|
|
assert($events[0]->name === 'counter:reset');
|
|
});
|
|
|
|
// Test 6: addAmount with ActionParameters
|
|
test('addAmount(): Uses ActionParameters correctly', function () {
|
|
$id = ComponentId::create('counter', 'test-6');
|
|
$data = ComponentData::fromArray(['count' => 10]);
|
|
$component = new CounterComponent($id, $data);
|
|
|
|
$params = ActionParameters::fromArray(['amount' => 5]);
|
|
$dispatcher = new ComponentEventDispatcher();
|
|
|
|
$result = $component->addAmount($params, $dispatcher);
|
|
|
|
assert($result instanceof ComponentData);
|
|
assert($result->get('count') === 15);
|
|
});
|
|
|
|
// Test 7: Milestone event at count 10
|
|
test('Milestone: Dispatches event at multiples of 10', function () {
|
|
$id = ComponentId::create('counter', 'test-7');
|
|
$data = ComponentData::fromArray(['count' => 9]);
|
|
$component = new CounterComponent($id, $data);
|
|
|
|
$params = ActionParameters::empty();
|
|
$dispatcher = new ComponentEventDispatcher();
|
|
|
|
$component->increment($params, $dispatcher);
|
|
|
|
// Should have both counter:changed AND counter:milestone
|
|
$events = $dispatcher->getEvents();
|
|
assert(count($events) === 2);
|
|
assert($events[0]->name === 'counter:changed');
|
|
assert($events[1]->name === 'counter:milestone');
|
|
assert($events[1]->payload->getInt('milestone') === 10);
|
|
});
|
|
|
|
// Test 8: Poll returns ComponentData
|
|
test('poll(): Returns ComponentData', function () {
|
|
$id = ComponentId::create('counter', 'test-8');
|
|
$data = ComponentData::fromArray(['count' => 5]);
|
|
$component = new CounterComponent($id, $data);
|
|
|
|
$params = ActionParameters::empty();
|
|
|
|
$result = $component->poll($params);
|
|
|
|
assert($result instanceof ComponentData);
|
|
assert($result->has('count'));
|
|
assert($result->has('last_update'));
|
|
assert($result->has('server_time'));
|
|
});
|
|
|
|
// Test 9: Immutability - original data unchanged
|
|
test('Immutability: Actions do not modify original data', function () {
|
|
$id = ComponentId::create('counter', 'test-9');
|
|
$originalData = ComponentData::fromArray(['count' => 5]);
|
|
$component = new CounterComponent($id, $originalData);
|
|
|
|
$params = ActionParameters::empty();
|
|
$dispatcher = new ComponentEventDispatcher();
|
|
|
|
$newData = $component->increment($params, $dispatcher);
|
|
|
|
// Original data unchanged
|
|
assert($originalData->get('count') === 5);
|
|
|
|
// New data has updated count
|
|
assert($newData->get('count') === 6);
|
|
|
|
// Component's data is still original
|
|
assert($component->getData()->get('count') === 5);
|
|
});
|
|
|
|
// Test 10: Event payloads are EventPayload objects
|
|
test('Events: Use EventPayload Value Objects', function () {
|
|
$id = ComponentId::create('counter', 'test-10');
|
|
$data = ComponentData::fromArray(['count' => 0]);
|
|
$component = new CounterComponent($id, $data);
|
|
|
|
$params = ActionParameters::empty();
|
|
$dispatcher = new ComponentEventDispatcher();
|
|
|
|
$component->increment($params, $dispatcher);
|
|
|
|
$events = $dispatcher->getEvents();
|
|
$event = $events[0];
|
|
|
|
// Event payload should be EventPayload
|
|
assert($event->payload instanceof \App\Framework\LiveComponents\ValueObjects\EventPayload);
|
|
assert($event->payload->getString('component_id') === 'counter:test-10');
|
|
assert($event->payload->getInt('old_value') === 0);
|
|
assert($event->payload->getInt('new_value') === 1);
|
|
});
|
|
|
|
echo "\n";
|
|
echo "==================================================\n";
|
|
echo "Tests passed: {$testsPassed}\n";
|
|
echo "Tests failed: {$testsFailed}\n";
|
|
echo "==================================================\n";
|
|
|
|
if ($testsFailed > 0) {
|
|
exit(1);
|
|
}
|
|
|
|
echo "\n✅ All CounterComponent strict typing tests passed!\n";
|
|
echo "✅ ComponentData return types work correctly!\n";
|