Files
michaelschiemer/tests/debug/test-counter-component-strict.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

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";