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,163 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Unit\Framework\LiveComponents\Security;
|
||||
|
||||
require_once __DIR__ . '/TestComponents.php';
|
||||
|
||||
use App\Application\LiveComponents\Counter\CounterState;
|
||||
use App\Framework\Attributes\Execution\AttributeExecutionContext;
|
||||
use App\Framework\Core\ValueObjects\ClassName;
|
||||
use App\Framework\Core\ValueObjects\MethodName;
|
||||
use App\Framework\DI\DefaultContainer;
|
||||
use App\Framework\LiveComponents\Attributes\Action;
|
||||
use App\Framework\LiveComponents\Contracts\LiveComponentContract;
|
||||
use App\Framework\LiveComponents\Security\ActionValidator;
|
||||
use App\Framework\LiveComponents\Security\LiveComponentContextHelper;
|
||||
use App\Framework\LiveComponents\ValueObjects\ActionParameters;
|
||||
use App\Framework\LiveComponents\ValueObjects\ComponentId;
|
||||
use Tests\Unit\Framework\LiveComponents\Security\ArrayReturnTypeComponent;
|
||||
use Tests\Unit\Framework\LiveComponents\Security\PrimitiveReturnTypeComponent;
|
||||
use Tests\Unit\Framework\LiveComponents\Security\PrivateActionComponent;
|
||||
use Tests\Unit\Framework\LiveComponents\Security\StaticActionComponent;
|
||||
use Tests\Unit\Framework\LiveComponents\Security\ValidActionComponent;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
final class ActionValidatorTest extends TestCase
|
||||
{
|
||||
private ActionValidator $validator;
|
||||
private DefaultContainer $container;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->container = new DefaultContainer();
|
||||
$this->validator = new ActionValidator();
|
||||
}
|
||||
|
||||
public function test_validates_valid_action(): void
|
||||
{
|
||||
$component = $this->createValidComponent();
|
||||
$context = $this->createContext($component, 'validAction');
|
||||
$actionAttribute = new Action();
|
||||
|
||||
// Should not throw
|
||||
$this->validator->validate($context, $actionAttribute);
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
public function test_rejects_reserved_method(): void
|
||||
{
|
||||
$component = $this->createValidComponent();
|
||||
$context = $this->createContext($component, 'onMount');
|
||||
$actionAttribute = new Action();
|
||||
|
||||
$this->expectException(\BadMethodCallException::class);
|
||||
$this->expectExceptionMessage('reserved method');
|
||||
|
||||
$this->validator->validate($context, $actionAttribute);
|
||||
}
|
||||
|
||||
public function test_rejects_non_existent_method(): void
|
||||
{
|
||||
$component = $this->createValidComponent();
|
||||
$context = $this->createContext($component, 'nonExistentMethod');
|
||||
$actionAttribute = new Action();
|
||||
|
||||
$this->expectException(\BadMethodCallException::class);
|
||||
$this->expectExceptionMessage('not found');
|
||||
|
||||
$this->validator->validate($context, $actionAttribute);
|
||||
}
|
||||
|
||||
public function test_rejects_private_method(): void
|
||||
{
|
||||
$component = new PrivateActionComponent(
|
||||
ComponentId::create('test', 'demo'),
|
||||
CounterState::empty()
|
||||
);
|
||||
|
||||
$context = $this->createContext($component, 'privateAction');
|
||||
$actionAttribute = new Action();
|
||||
|
||||
$this->expectException(\BadMethodCallException::class);
|
||||
$this->expectExceptionMessage('must be public');
|
||||
|
||||
$this->validator->validate($context, $actionAttribute);
|
||||
}
|
||||
|
||||
public function test_rejects_static_method(): void
|
||||
{
|
||||
$component = new StaticActionComponent(
|
||||
ComponentId::create('test', 'demo'),
|
||||
CounterState::empty()
|
||||
);
|
||||
|
||||
$context = $this->createContext($component, 'staticAction');
|
||||
$actionAttribute = new Action();
|
||||
|
||||
$this->expectException(\BadMethodCallException::class);
|
||||
$this->expectExceptionMessage('cannot be static');
|
||||
|
||||
$this->validator->validate($context, $actionAttribute);
|
||||
}
|
||||
|
||||
public function test_rejects_primitive_return_type(): void
|
||||
{
|
||||
$component = new PrimitiveReturnTypeComponent(
|
||||
ComponentId::create('test', 'demo'),
|
||||
CounterState::empty()
|
||||
);
|
||||
|
||||
$context = $this->createContext($component, 'intAction');
|
||||
$actionAttribute = new Action();
|
||||
|
||||
$this->expectException(\BadMethodCallException::class);
|
||||
$this->expectExceptionMessage('must return a State object');
|
||||
|
||||
$this->validator->validate($context, $actionAttribute);
|
||||
}
|
||||
|
||||
public function test_rejects_array_return_type(): void
|
||||
{
|
||||
$component = new ArrayReturnTypeComponent(
|
||||
ComponentId::create('test', 'demo'),
|
||||
CounterState::empty()
|
||||
);
|
||||
|
||||
$context = $this->createContext($component, 'arrayAction');
|
||||
$actionAttribute = new Action();
|
||||
|
||||
$this->expectException(\BadMethodCallException::class);
|
||||
$this->expectExceptionMessage('must return a State object');
|
||||
|
||||
$this->validator->validate($context, $actionAttribute);
|
||||
}
|
||||
|
||||
private function createValidComponent(): LiveComponentContract
|
||||
{
|
||||
return new ValidActionComponent(
|
||||
ComponentId::create('test', 'demo'),
|
||||
CounterState::empty()
|
||||
);
|
||||
}
|
||||
|
||||
private function createContext(LiveComponentContract $component, string $methodName): AttributeExecutionContext
|
||||
{
|
||||
$componentClass = ClassName::create($component::class);
|
||||
$method = MethodName::create($methodName);
|
||||
$componentId = $component->id;
|
||||
$actionParameters = ActionParameters::fromArray([]);
|
||||
|
||||
return LiveComponentContextHelper::createForAction(
|
||||
container: $this->container,
|
||||
componentClass: $componentClass,
|
||||
actionMethod: $method,
|
||||
componentId: $componentId,
|
||||
actionParameters: $actionParameters,
|
||||
component: $component
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Unit\Framework\LiveComponents\Security;
|
||||
|
||||
use App\Application\LiveComponents\Counter\CounterState;
|
||||
use App\Framework\Attributes\Execution\AttributeExecutionContext;
|
||||
use App\Framework\Core\ValueObjects\ClassName;
|
||||
use App\Framework\Core\ValueObjects\MethodName;
|
||||
use App\Framework\DI\DefaultContainer;
|
||||
use App\Framework\Http\Session\SessionInterface;
|
||||
use App\Framework\LiveComponents\Contracts\LiveComponentContract;
|
||||
use App\Framework\LiveComponents\Security\Guards\LiveComponentCsrfGuard;
|
||||
use App\Framework\LiveComponents\Security\LiveComponentContextData;
|
||||
use App\Framework\LiveComponents\Security\LiveComponentContextHelper;
|
||||
use App\Framework\LiveComponents\ValueObjects\ActionParameters;
|
||||
use App\Framework\LiveComponents\ValueObjects\ComponentId;
|
||||
use App\Framework\LiveComponents\ValueObjects\ComponentRenderData;
|
||||
use App\Framework\Http\Session\CsrfProtection;
|
||||
use App\Framework\Security\CsrfToken;
|
||||
use App\Framework\Security\Guards\CsrfGuard;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
final class LiveComponentCsrfGuardTest extends TestCase
|
||||
{
|
||||
private LiveComponentCsrfGuard $guard;
|
||||
private CsrfGuard $csrfGuard;
|
||||
private SessionInterface&MockObject $session;
|
||||
private CsrfProtection&MockObject $csrfProtection;
|
||||
private DefaultContainer $container;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
// Since CsrfProtection is final, we skip mocking it
|
||||
// These tests focus on the LiveComponent-specific logic (context extraction, form ID generation)
|
||||
// The actual CSRF validation is tested in integration tests
|
||||
|
||||
$this->session = $this->createMock(SessionInterface::class);
|
||||
$this->csrfGuard = new CsrfGuard($this->session);
|
||||
$this->container = new DefaultContainer();
|
||||
$this->container->instance(CsrfGuard::class, $this->csrfGuard);
|
||||
$this->container->instance(SessionInterface::class, $this->session);
|
||||
|
||||
$this->guard = new LiveComponentCsrfGuard($this->csrfGuard);
|
||||
}
|
||||
|
||||
public function test_validates_valid_csrf_token(): void
|
||||
{
|
||||
// This test is skipped because CsrfProtection is final and cannot be mocked
|
||||
// The CSRF validation logic is tested in integration tests
|
||||
$this->markTestSkipped('CsrfProtection is final and cannot be mocked. Tested in integration tests.');
|
||||
}
|
||||
|
||||
public function test_rejects_missing_csrf_token(): void
|
||||
{
|
||||
$component = $this->createComponent();
|
||||
$params = ActionParameters::fromArray([]);
|
||||
$context = $this->createContext($component, $params);
|
||||
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('CSRF token is required');
|
||||
|
||||
$this->guard->validate($context);
|
||||
}
|
||||
|
||||
public function test_rejects_context_without_live_component_data(): void
|
||||
{
|
||||
$context = AttributeExecutionContext::forMethod(
|
||||
container: $this->container,
|
||||
className: ClassName::create('TestComponent'),
|
||||
methodName: MethodName::create('testMethod')
|
||||
);
|
||||
|
||||
$this->expectException(\RuntimeException::class);
|
||||
$this->expectExceptionMessage('LiveComponentContextData');
|
||||
|
||||
$this->guard->validate($context);
|
||||
}
|
||||
|
||||
public function test_uses_component_id_as_form_id(): void
|
||||
{
|
||||
// This test is skipped because CsrfProtection is final and cannot be mocked
|
||||
// The form ID generation logic is tested in integration tests
|
||||
$this->markTestSkipped('CsrfProtection is final and cannot be mocked. Tested in integration tests.');
|
||||
}
|
||||
|
||||
private function createComponent(): LiveComponentContract
|
||||
{
|
||||
return new TestComponent(
|
||||
ComponentId::create('test', 'demo'),
|
||||
CounterState::empty()
|
||||
);
|
||||
}
|
||||
|
||||
private function createContext(LiveComponentContract $component, ActionParameters $params): AttributeExecutionContext
|
||||
{
|
||||
return LiveComponentContextHelper::createForAction(
|
||||
container: $this->container,
|
||||
componentClass: ClassName::create($component::class),
|
||||
actionMethod: MethodName::create('testAction'),
|
||||
componentId: $component->id,
|
||||
actionParameters: $params,
|
||||
component: $component
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Unit\Framework\LiveComponents\Security;
|
||||
|
||||
require_once __DIR__ . '/TestComponents.php';
|
||||
|
||||
use App\Application\LiveComponents\Counter\CounterState;
|
||||
use App\Framework\Attributes\Execution\AttributeExecutionContext;
|
||||
use App\Framework\Core\ValueObjects\ClassName;
|
||||
use App\Framework\Core\ValueObjects\MethodName;
|
||||
use App\Framework\DI\DefaultContainer;
|
||||
use App\Framework\LiveComponents\Attributes\RequiresPermission;
|
||||
use App\Framework\LiveComponents\Contracts\LiveComponentContract;
|
||||
use App\Framework\LiveComponents\Exceptions\UnauthorizedActionException;
|
||||
use App\Framework\LiveComponents\Security\ActionAuthorizationChecker;
|
||||
use App\Framework\LiveComponents\Security\Guards\LiveComponentPermissionGuard;
|
||||
use App\Framework\LiveComponents\Security\LiveComponentContextHelper;
|
||||
use App\Framework\LiveComponents\ValueObjects\ActionParameters;
|
||||
use App\Framework\LiveComponents\ValueObjects\ComponentId;
|
||||
use App\Framework\LiveComponents\ValueObjects\ComponentRenderData;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
final class LiveComponentPermissionGuardTest extends TestCase
|
||||
{
|
||||
private LiveComponentPermissionGuard $guard;
|
||||
private ActionAuthorizationChecker&MockObject $authorizationChecker;
|
||||
private DefaultContainer $container;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->authorizationChecker = $this->createMock(ActionAuthorizationChecker::class);
|
||||
$this->container = new DefaultContainer();
|
||||
$this->container->instance(ActionAuthorizationChecker::class, $this->authorizationChecker);
|
||||
|
||||
$this->guard = new LiveComponentPermissionGuard($this->authorizationChecker);
|
||||
}
|
||||
|
||||
public function test_allows_authorized_action(): void
|
||||
{
|
||||
$component = $this->createComponent();
|
||||
$params = ActionParameters::fromArray([]);
|
||||
$context = $this->createContext($component, $params);
|
||||
$permissionAttribute = new RequiresPermission('edit_post');
|
||||
|
||||
$this->authorizationChecker
|
||||
->expects($this->once())
|
||||
->method('isAuthorized')
|
||||
->with($component, 'testAction', $permissionAttribute)
|
||||
->willReturn(true);
|
||||
|
||||
// Should not throw
|
||||
$this->guard->check($context, $permissionAttribute);
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
public function test_rejects_unauthorized_action(): void
|
||||
{
|
||||
$component = $this->createComponent();
|
||||
$params = ActionParameters::fromArray([]);
|
||||
$context = $this->createContext($component, $params);
|
||||
$permissionAttribute = new RequiresPermission('edit_post');
|
||||
|
||||
$this->authorizationChecker
|
||||
->expects($this->once())
|
||||
->method('isAuthorized')
|
||||
->willReturn(false);
|
||||
|
||||
$this->authorizationChecker
|
||||
->expects($this->once())
|
||||
->method('isAuthenticated')
|
||||
->willReturn(true);
|
||||
|
||||
$this->authorizationChecker
|
||||
->expects($this->once())
|
||||
->method('getUserPermissions')
|
||||
->willReturn(['view_post']);
|
||||
|
||||
$this->expectException(UnauthorizedActionException::class);
|
||||
|
||||
$this->guard->check($context, $permissionAttribute);
|
||||
}
|
||||
|
||||
public function test_rejects_unauthenticated_user(): void
|
||||
{
|
||||
$component = $this->createComponent();
|
||||
$params = ActionParameters::fromArray([]);
|
||||
$context = $this->createContext($component, $params);
|
||||
$permissionAttribute = new RequiresPermission('edit_post');
|
||||
|
||||
$this->authorizationChecker
|
||||
->expects($this->once())
|
||||
->method('isAuthorized')
|
||||
->willReturn(false);
|
||||
|
||||
$this->authorizationChecker
|
||||
->expects($this->once())
|
||||
->method('isAuthenticated')
|
||||
->willReturn(false);
|
||||
|
||||
$this->expectException(UnauthorizedActionException::class);
|
||||
$this->expectExceptionMessage('requires authentication');
|
||||
|
||||
$this->guard->check($context, $permissionAttribute);
|
||||
}
|
||||
|
||||
public function test_rejects_context_without_live_component_data(): void
|
||||
{
|
||||
$context = AttributeExecutionContext::forMethod(
|
||||
container: $this->container,
|
||||
className: ClassName::create('TestComponent'),
|
||||
methodName: MethodName::create('testMethod')
|
||||
);
|
||||
$permissionAttribute = new RequiresPermission('edit_post');
|
||||
|
||||
$this->expectException(\RuntimeException::class);
|
||||
$this->expectExceptionMessage('LiveComponentContextData');
|
||||
|
||||
$this->guard->check($context, $permissionAttribute);
|
||||
}
|
||||
|
||||
private function createComponent(): LiveComponentContract
|
||||
{
|
||||
return new TestComponent(
|
||||
ComponentId::create('test', 'demo'),
|
||||
CounterState::empty()
|
||||
);
|
||||
}
|
||||
|
||||
private function createContext(LiveComponentContract $component, ActionParameters $params): AttributeExecutionContext
|
||||
{
|
||||
return LiveComponentContextHelper::createForAction(
|
||||
container: $this->container,
|
||||
componentClass: ClassName::create($component::class),
|
||||
actionMethod: MethodName::create('testAction'),
|
||||
componentId: $component->id,
|
||||
actionParameters: $params,
|
||||
component: $component
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Unit\Framework\LiveComponents\Security;
|
||||
|
||||
require_once __DIR__ . '/TestComponents.php';
|
||||
|
||||
use App\Application\LiveComponents\Counter\CounterState;
|
||||
use App\Framework\Attributes\Execution\AttributeExecutionContext;
|
||||
use App\Framework\Core\ValueObjects\ClassName;
|
||||
use App\Framework\Core\ValueObjects\ClientIdentifier;
|
||||
use App\Framework\Core\ValueObjects\MethodName;
|
||||
use App\Framework\DI\DefaultContainer;
|
||||
use App\Framework\LiveComponents\Attributes\Action;
|
||||
use App\Framework\LiveComponents\Contracts\LiveComponentContract;
|
||||
use App\Framework\LiveComponents\Exceptions\RateLimitExceededException;
|
||||
use App\Framework\LiveComponents\Security\Guards\LiveComponentRateLimitGuard;
|
||||
use App\Framework\LiveComponents\Security\LiveComponentContextHelper;
|
||||
use App\Framework\LiveComponents\Services\LiveComponentRateLimiter;
|
||||
use App\Framework\LiveComponents\Services\RateLimitResult;
|
||||
use App\Framework\LiveComponents\ValueObjects\ActionParameters;
|
||||
use App\Framework\LiveComponents\ValueObjects\ComponentId;
|
||||
use App\Framework\Cache\Driver\InMemoryCache;
|
||||
use App\Framework\Cache\GeneralCache;
|
||||
use App\Framework\Serializer\Php\PhpSerializer;
|
||||
use App\Framework\Serializer\Php\PhpSerializerConfig;
|
||||
use App\Framework\RateLimit\RateLimiter;
|
||||
use App\Framework\RateLimit\Storage\CacheStorage;
|
||||
use Tests\Unit\Framework\LiveComponents\Security\TestComponent;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
final class LiveComponentRateLimitGuardTest extends TestCase
|
||||
{
|
||||
private LiveComponentRateLimitGuard $guard;
|
||||
private LiveComponentRateLimiter $rateLimiter;
|
||||
private DefaultContainer $container;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
// Use real instance since LiveComponentRateLimiter is final
|
||||
$this->container = new DefaultContainer();
|
||||
$cacheDriver = new InMemoryCache();
|
||||
$serializer = new PhpSerializer(PhpSerializerConfig::safe());
|
||||
$cache = new GeneralCache($cacheDriver, $serializer);
|
||||
$storage = new CacheStorage($cache);
|
||||
$baseRateLimiter = new RateLimiter($storage);
|
||||
$this->container->instance(\App\Framework\RateLimit\RateLimiter::class, $baseRateLimiter);
|
||||
$this->rateLimiter = new LiveComponentRateLimiter($baseRateLimiter);
|
||||
|
||||
$this->guard = new LiveComponentRateLimitGuard($this->rateLimiter);
|
||||
}
|
||||
|
||||
public function test_skips_check_when_no_client_identifier(): void
|
||||
{
|
||||
$component = $this->createComponent();
|
||||
$params = ActionParameters::fromArray([]);
|
||||
$context = $this->createContext($component, $params);
|
||||
$actionAttribute = new Action(rateLimit: 10);
|
||||
|
||||
// Should not throw and not call rate limiter (no client identifier)
|
||||
$this->guard->check($context, $actionAttribute);
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
public function test_rejects_context_without_live_component_data(): void
|
||||
{
|
||||
$context = AttributeExecutionContext::forMethod(
|
||||
container: $this->container,
|
||||
className: ClassName::create('TestComponent'),
|
||||
methodName: MethodName::create('testMethod')
|
||||
);
|
||||
$actionAttribute = new Action();
|
||||
|
||||
$this->expectException(\RuntimeException::class);
|
||||
$this->expectExceptionMessage('LiveComponentContextData');
|
||||
|
||||
$this->guard->check($context, $actionAttribute);
|
||||
}
|
||||
|
||||
private function createComponent(): LiveComponentContract
|
||||
{
|
||||
return new TestComponent(
|
||||
ComponentId::create('test', 'demo'),
|
||||
CounterState::empty()
|
||||
);
|
||||
}
|
||||
|
||||
private function createContext(LiveComponentContract $component, ActionParameters $params): AttributeExecutionContext
|
||||
{
|
||||
return LiveComponentContextHelper::createForAction(
|
||||
container: $this->container,
|
||||
componentClass: ClassName::create($component::class),
|
||||
actionMethod: MethodName::create('testAction'),
|
||||
componentId: $component->id,
|
||||
actionParameters: $params,
|
||||
component: $component
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Unit\Framework\LiveComponents\Security;
|
||||
|
||||
use App\Application\LiveComponents\Counter\CounterState;
|
||||
use App\Framework\LiveComponents\Contracts\LiveComponentContract;
|
||||
use App\Framework\LiveComponents\ValueObjects\ComponentId;
|
||||
use App\Framework\LiveComponents\ValueObjects\ComponentRenderData;
|
||||
|
||||
final class TestComponent implements LiveComponentContract
|
||||
{
|
||||
public function __construct(
|
||||
public ComponentId $id,
|
||||
public CounterState $state
|
||||
) {
|
||||
}
|
||||
|
||||
public function getRenderData(): ComponentRenderData
|
||||
{
|
||||
return new ComponentRenderData(templatePath: 'test', data: []);
|
||||
}
|
||||
}
|
||||
|
||||
127
tests/Unit/Framework/LiveComponents/Security/TestComponents.php
Normal file
127
tests/Unit/Framework/LiveComponents/Security/TestComponents.php
Normal file
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Unit\Framework\LiveComponents\Security;
|
||||
|
||||
use App\Application\LiveComponents\Counter\CounterState;
|
||||
use App\Framework\LiveComponents\Attributes\Action;
|
||||
use App\Framework\LiveComponents\Contracts\LiveComponentContract;
|
||||
use App\Framework\LiveComponents\ValueObjects\ComponentId;
|
||||
use App\Framework\LiveComponents\ValueObjects\ComponentRenderData;
|
||||
|
||||
/**
|
||||
* Test Component with valid action
|
||||
*/
|
||||
final class ValidActionComponent implements LiveComponentContract
|
||||
{
|
||||
public function __construct(
|
||||
public ComponentId $id,
|
||||
public CounterState $state
|
||||
) {
|
||||
}
|
||||
|
||||
public function getRenderData(): ComponentRenderData
|
||||
{
|
||||
return new ComponentRenderData(templatePath: 'test', data: []);
|
||||
}
|
||||
|
||||
#[Action]
|
||||
public function validAction(): CounterState
|
||||
{
|
||||
return $this->state;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test Component with private action (should fail validation)
|
||||
*/
|
||||
final class PrivateActionComponent implements LiveComponentContract
|
||||
{
|
||||
public function __construct(
|
||||
public ComponentId $id,
|
||||
public CounterState $state
|
||||
) {
|
||||
}
|
||||
|
||||
public function getRenderData(): ComponentRenderData
|
||||
{
|
||||
return new ComponentRenderData(templatePath: 'test', data: []);
|
||||
}
|
||||
|
||||
#[Action]
|
||||
private function privateAction(): CounterState
|
||||
{
|
||||
return $this->state;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test Component with static action (should fail validation)
|
||||
*/
|
||||
final class StaticActionComponent implements LiveComponentContract
|
||||
{
|
||||
public function __construct(
|
||||
public ComponentId $id,
|
||||
public CounterState $state
|
||||
) {
|
||||
}
|
||||
|
||||
public function getRenderData(): ComponentRenderData
|
||||
{
|
||||
return new ComponentRenderData(templatePath: 'test', data: []);
|
||||
}
|
||||
|
||||
#[Action]
|
||||
public static function staticAction(): CounterState
|
||||
{
|
||||
return CounterState::empty();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test Component with primitive return type (should fail validation)
|
||||
*/
|
||||
final class PrimitiveReturnTypeComponent implements LiveComponentContract
|
||||
{
|
||||
public function __construct(
|
||||
public ComponentId $id,
|
||||
public CounterState $state
|
||||
) {
|
||||
}
|
||||
|
||||
public function getRenderData(): ComponentRenderData
|
||||
{
|
||||
return new ComponentRenderData(templatePath: 'test', data: []);
|
||||
}
|
||||
|
||||
#[Action]
|
||||
public function intAction(): int
|
||||
{
|
||||
return 42;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test Component with array return type (should fail validation)
|
||||
*/
|
||||
final class ArrayReturnTypeComponent implements LiveComponentContract
|
||||
{
|
||||
public function __construct(
|
||||
public ComponentId $id,
|
||||
public CounterState $state
|
||||
) {
|
||||
}
|
||||
|
||||
public function getRenderData(): ComponentRenderData
|
||||
{
|
||||
return new ComponentRenderData(templatePath: 'test', data: []);
|
||||
}
|
||||
|
||||
#[Action]
|
||||
public function arrayAction(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user