fix: DockerSecretsResolver - don't normalize absolute paths like /var/www/html/...
Some checks failed
Deploy Application / deploy (push) Has been cancelled
Some checks failed
Deploy Application / deploy (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,275 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Feature\Framework\LiveComponents;
|
||||
|
||||
use App\Application\LiveComponents\Counter\CounterComponent;
|
||||
use App\Application\LiveComponents\Dashboard\FailedJobsListComponent;
|
||||
use App\Application\LiveComponents\Dashboard\PerformanceMetricsComponent;
|
||||
use App\Application\LiveComponents\UserStatsComponent;
|
||||
use App\Framework\Core\ValueObjects\Duration;
|
||||
use App\Framework\LiveComponents\Contracts\Cacheable;
|
||||
use App\Framework\LiveComponents\Contracts\LifecycleAware;
|
||||
use App\Framework\LiveComponents\Contracts\LiveComponentContract;
|
||||
use App\Framework\LiveComponents\Contracts\Pollable;
|
||||
use App\Framework\LiveComponents\Contracts\SupportsFileUpload;
|
||||
use App\Framework\LiveComponents\Contracts\SupportsSlots;
|
||||
use App\Framework\LiveComponents\ValueObjects\ComponentId;
|
||||
use App\Framework\LiveComponents\ValueObjects\ComponentState;
|
||||
use ReflectionClass;
|
||||
use ReflectionMethod;
|
||||
|
||||
describe('Contract Compliance Tests', function () {
|
||||
describe('Pollable Interface', function () {
|
||||
it('verifies Pollable components implement required methods', function () {
|
||||
$component = new PerformanceMetricsComponent(
|
||||
ComponentId::create('performance-metrics', 'test'),
|
||||
ComponentState::empty()
|
||||
);
|
||||
|
||||
expect($component)->toBeInstanceOf(Pollable::class);
|
||||
expect($component)->toBeInstanceOf(LiveComponentContract::class);
|
||||
|
||||
// Check required methods exist
|
||||
$reflection = new ReflectionClass($component);
|
||||
expect($reflection->hasMethod('poll'))->toBeTrue();
|
||||
expect($reflection->hasMethod('getPollInterval'))->toBeTrue();
|
||||
|
||||
// Check method signatures
|
||||
$pollMethod = $reflection->getMethod('poll');
|
||||
expect($pollMethod->isPublic())->toBeTrue();
|
||||
expect($pollMethod->getReturnType()?->getName())->toBe('App\Application\LiveComponents\LiveComponentState');
|
||||
|
||||
$intervalMethod = $reflection->getMethod('getPollInterval');
|
||||
expect($intervalMethod->isPublic())->toBeTrue();
|
||||
expect($intervalMethod->getReturnType()?->getName())->toBe('int');
|
||||
});
|
||||
|
||||
it('verifies poll() returns LiveComponentState', function () {
|
||||
$component = new PerformanceMetricsComponent(
|
||||
ComponentId::create('performance-metrics', 'test'),
|
||||
ComponentState::empty()
|
||||
);
|
||||
|
||||
$result = $component->poll();
|
||||
expect($result)->toBeInstanceOf(\App\Application\LiveComponents\LiveComponentState::class);
|
||||
});
|
||||
|
||||
it('verifies getPollInterval() returns positive integer', function () {
|
||||
$component = new PerformanceMetricsComponent(
|
||||
ComponentId::create('performance-metrics', 'test'),
|
||||
ComponentState::empty()
|
||||
);
|
||||
|
||||
$interval = $component->getPollInterval();
|
||||
expect($interval)->toBeInt();
|
||||
expect($interval)->toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Cacheable Interface', function () {
|
||||
it('verifies Cacheable components implement required methods', function () {
|
||||
$component = new UserStatsComponent(
|
||||
ComponentId::create('user-stats', 'test'),
|
||||
ComponentState::empty()
|
||||
);
|
||||
|
||||
expect($component)->toBeInstanceOf(Cacheable::class);
|
||||
expect($component)->toBeInstanceOf(LiveComponentContract::class);
|
||||
|
||||
$reflection = new ReflectionClass($component);
|
||||
|
||||
// Check required methods
|
||||
expect($reflection->hasMethod('getCacheKey'))->toBeTrue();
|
||||
expect($reflection->hasMethod('getCacheTTL'))->toBeTrue();
|
||||
expect($reflection->hasMethod('shouldCache'))->toBeTrue();
|
||||
expect($reflection->hasMethod('getCacheTags'))->toBeTrue();
|
||||
expect($reflection->hasMethod('getVaryBy'))->toBeTrue();
|
||||
expect($reflection->hasMethod('getStaleWhileRevalidate'))->toBeTrue();
|
||||
|
||||
// Check return types
|
||||
$ttlMethod = $reflection->getMethod('getCacheTTL');
|
||||
expect($ttlMethod->getReturnType()?->getName())->toBe(Duration::class);
|
||||
|
||||
$varyByMethod = $reflection->getMethod('getVaryBy');
|
||||
$varyByReturnType = $varyByMethod->getReturnType();
|
||||
expect($varyByReturnType?->allowsNull())->toBeTrue();
|
||||
});
|
||||
|
||||
it('verifies getCacheKey() returns string', function () {
|
||||
$component = new UserStatsComponent(
|
||||
ComponentId::create('user-stats', 'test'),
|
||||
ComponentState::empty()
|
||||
);
|
||||
|
||||
$key = $component->getCacheKey();
|
||||
expect($key)->toBeString();
|
||||
expect($key)->not->toBeEmpty();
|
||||
});
|
||||
|
||||
it('verifies getCacheTTL() returns Duration', function () {
|
||||
$component = new UserStatsComponent(
|
||||
ComponentId::create('user-stats', 'test'),
|
||||
ComponentState::empty()
|
||||
);
|
||||
|
||||
$ttl = $component->getCacheTTL();
|
||||
expect($ttl)->toBeInstanceOf(Duration::class);
|
||||
});
|
||||
|
||||
it('verifies getCacheTags() returns array', function () {
|
||||
$component = new UserStatsComponent(
|
||||
ComponentId::create('user-stats', 'test'),
|
||||
ComponentState::empty()
|
||||
);
|
||||
|
||||
$tags = $component->getCacheTags();
|
||||
expect($tags)->toBeArray();
|
||||
});
|
||||
});
|
||||
|
||||
describe('LifecycleAware Interface', function () {
|
||||
it('verifies LifecycleAware components implement required methods', function () {
|
||||
// Find a component that implements LifecycleAware
|
||||
$componentClass = CounterComponent::class;
|
||||
$reflection = new ReflectionClass($componentClass);
|
||||
|
||||
if ($reflection->implementsInterface(LifecycleAware::class)) {
|
||||
$component = new $componentClass(
|
||||
ComponentId::create('counter', 'test'),
|
||||
\App\Application\LiveComponents\Counter\CounterState::empty()
|
||||
);
|
||||
|
||||
expect($component)->toBeInstanceOf(LifecycleAware::class);
|
||||
|
||||
// Check required methods
|
||||
expect($reflection->hasMethod('onMount'))->toBeTrue();
|
||||
expect($reflection->hasMethod('onUpdate'))->toBeTrue();
|
||||
expect($reflection->hasMethod('onDestroy'))->toBeTrue();
|
||||
|
||||
// Check method signatures (all should return void)
|
||||
$onMountMethod = $reflection->getMethod('onMount');
|
||||
expect($onMountMethod->getReturnType()?->getName())->toBe('void');
|
||||
} else {
|
||||
// Skip if no component implements LifecycleAware in test scope
|
||||
$this->markTestSkipped('No LifecycleAware component found for testing');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('SupportsFileUpload Interface', function () {
|
||||
it('verifies SupportsFileUpload components implement required methods', function () {
|
||||
// Find a component that implements SupportsFileUpload
|
||||
$componentClass = \App\Application\LiveComponents\ImageUploader\ImageUploaderComponent::class;
|
||||
|
||||
if (!class_exists($componentClass)) {
|
||||
$this->markTestSkipped('ImageUploaderComponent not available');
|
||||
return;
|
||||
}
|
||||
|
||||
$reflection = new ReflectionClass($componentClass);
|
||||
|
||||
if ($reflection->implementsInterface(SupportsFileUpload::class)) {
|
||||
expect($reflection->hasMethod('handleUpload'))->toBeTrue();
|
||||
expect($reflection->hasMethod('validateUpload'))->toBeTrue();
|
||||
expect($reflection->hasMethod('getAllowedMimeTypes'))->toBeTrue();
|
||||
expect($reflection->hasMethod('getMaxFileSize'))->toBeTrue();
|
||||
|
||||
// Check handleUpload signature
|
||||
$handleUploadMethod = $reflection->getMethod('handleUpload');
|
||||
$params = $handleUploadMethod->getParameters();
|
||||
expect(count($params))->toBeGreaterThanOrEqual(1);
|
||||
expect($params[0]->getType()?->getName())->toBe(\App\Framework\Http\UploadedFile::class);
|
||||
} else {
|
||||
$this->markTestSkipped('No SupportsFileUpload component found for testing');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('SupportsSlots Interface', function () {
|
||||
it('verifies SupportsSlots components implement required methods', function () {
|
||||
// Find a component that implements SupportsSlots
|
||||
$componentClass = \App\Application\LiveComponents\LayoutComponent::class;
|
||||
|
||||
if (!class_exists($componentClass)) {
|
||||
$this->markTestSkipped('LayoutComponent not available');
|
||||
return;
|
||||
}
|
||||
|
||||
$reflection = new ReflectionClass($componentClass);
|
||||
|
||||
if ($reflection->implementsInterface(SupportsSlots::class)) {
|
||||
expect($reflection->hasMethod('getSlotDefinitions'))->toBeTrue();
|
||||
expect($reflection->hasMethod('processSlotContent'))->toBeTrue();
|
||||
expect($reflection->hasMethod('getSlotContext'))->toBeTrue();
|
||||
} else {
|
||||
$this->markTestSkipped('No SupportsSlots component found for testing');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('LiveComponentContract Interface', function () {
|
||||
it('verifies all components implement LiveComponentContract', function () {
|
||||
$components = [
|
||||
CounterComponent::class,
|
||||
PerformanceMetricsComponent::class,
|
||||
UserStatsComponent::class,
|
||||
FailedJobsListComponent::class,
|
||||
];
|
||||
|
||||
foreach ($components as $componentClass) {
|
||||
$reflection = new ReflectionClass($componentClass);
|
||||
expect($reflection->implementsInterface(LiveComponentContract::class))->toBeTrue(
|
||||
"Component {$componentClass} must implement LiveComponentContract"
|
||||
);
|
||||
|
||||
// Check required properties
|
||||
expect($reflection->hasProperty('id'))->toBeTrue();
|
||||
expect($reflection->hasProperty('state'))->toBeTrue();
|
||||
|
||||
// Check required methods
|
||||
expect($reflection->hasMethod('getRenderData'))->toBeTrue();
|
||||
|
||||
$getRenderDataMethod = $reflection->getMethod('getRenderData');
|
||||
expect($getRenderDataMethod->isPublic())->toBeTrue();
|
||||
expect($getRenderDataMethod->getReturnType()?->getName())->toBe(
|
||||
\App\Framework\LiveComponents\ValueObjects\ComponentRenderData::class
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('Interface Method Signatures', function () {
|
||||
it('verifies Pollable::poll() signature', function () {
|
||||
$reflection = new ReflectionClass(Pollable::class);
|
||||
$pollMethod = $reflection->getMethod('poll');
|
||||
|
||||
expect($pollMethod->getReturnType()?->getName())->toBe(\App\Application\LiveComponents\LiveComponentState::class);
|
||||
expect($pollMethod->getParameters())->toBeEmpty();
|
||||
});
|
||||
|
||||
it('verifies Cacheable::getCacheTTL() signature', function () {
|
||||
$reflection = new ReflectionClass(Cacheable::class);
|
||||
$ttlMethod = $reflection->getMethod('getCacheTTL');
|
||||
|
||||
expect($ttlMethod->getReturnType()?->getName())->toBe(Duration::class);
|
||||
expect($ttlMethod->getParameters())->toBeEmpty();
|
||||
});
|
||||
|
||||
it('verifies SupportsFileUpload::handleUpload() signature', function () {
|
||||
$reflection = new ReflectionClass(SupportsFileUpload::class);
|
||||
$handleUploadMethod = $reflection->getMethod('handleUpload');
|
||||
|
||||
$params = $handleUploadMethod->getParameters();
|
||||
expect(count($params))->toBeGreaterThanOrEqual(1);
|
||||
expect($params[0]->getType()?->getName())->toBe(\App\Framework\Http\UploadedFile::class);
|
||||
expect($handleUploadMethod->getReturnType()?->getName())->toBe(\App\Application\LiveComponents\LiveComponentState::class);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user