From 622b6b85eae1716fe5df9b9f52c435174ab5d774 Mon Sep 17 00:00:00 2001 From: David Badura Date: Mon, 25 Dec 2023 10:26:37 +0100 Subject: [PATCH] refactor projector --- baseline.xml | 9 +- phpstan-baseline.neon | 2 +- .../AttributeProjectorMetadataFactory.php | 16 +- .../Projector/ClassIsNotAProjector.php | 23 ++ .../Projector/DuplicateCreateMethod.php | 3 +- .../Projector/DuplicateDropMethod.php | 3 +- .../Projector/DuplicateSubscribeMethod.php | 5 +- src/Metadata/Projector/ProjectorMetadata.php | 2 + .../Projector/ProjectorMetadataFactory.php | 4 +- .../Psr16ProjectorMetadataFactory.php | 3 +- .../Psr6ProjectorMetadataFactory.php | 3 +- src/Pipeline/Target/ProjectorTarget.php | 3 +- .../Projection/Store/ErrorSerializer.php | 6 +- .../Projectionist/DefaultProjectionist.php | 20 +- src/Projection/Projector/BasicProjector.php | 37 --- .../Projector/InMemoryProjectorRepository.php | 4 +- .../Projector/MetadataProjectorResolver.php | 17 +- .../Projector/ProjectionAttributeNotFound.php | 20 -- src/Projection/Projector/Projector.php | 12 - src/Projection/Projector/ProjectorHelper.php | 6 +- .../Projector/ProjectorRepository.php | 2 +- .../Projector/ProjectorResolver.php | 9 +- .../Projection/ProfileProjector.php | 3 +- .../Projection/BankAccountProjection.php | 11 +- .../Projection/ProfileProjection.php | 3 +- .../Outbox/Projection/ProfileProjection.php | 11 +- .../Projection/ProfileProjection.php | 18 +- tests/Unit/Fixture/Dummy2Projection.php | 5 +- tests/Unit/Fixture/DummyProjection.php | 5 +- .../AttributeProjectorMetadataFactoryTest.php | 56 ++--- .../DefaultProjectionistTest.php | 237 ++++++++---------- .../Projector/BasicProjectorTest.php | 37 --- .../InMemoryProjectorRepositoryTest.php | 8 +- .../MetadataProjectorResolverTest.php | 61 ++--- .../Projector/ProjectorHelperTest.php | 19 +- 35 files changed, 275 insertions(+), 408 deletions(-) create mode 100644 src/Metadata/Projector/ClassIsNotAProjector.php delete mode 100644 src/Projection/Projector/BasicProjector.php delete mode 100644 src/Projection/Projector/ProjectionAttributeNotFound.php delete mode 100644 src/Projection/Projector/Projector.php delete mode 100644 tests/Unit/Projection/Projector/BasicProjectorTest.php diff --git a/baseline.xml b/baseline.xml index 6ad294974..1a315c922 100644 --- a/baseline.xml +++ b/baseline.xml @@ -1,5 +1,5 @@ - + new static() @@ -55,6 +55,13 @@ projectors]]> + + + $method + $method + $subscribeMethod + + new WeakMap() diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index ad64f2441..b54320150 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -6,7 +6,7 @@ parameters: path: src/EventBus/Message.php - - message: "#^Method Patchlevel\\\\EventSourcing\\\\Projection\\\\Projector\\\\InMemoryProjectorRepository\\:\\:projectors\\(\\) should return array\\ but returns array\\\\.$#" + message: "#^Method Patchlevel\\\\EventSourcing\\\\Projection\\\\Projector\\\\InMemoryProjectorRepository\\:\\:projectors\\(\\) should return array\\ but returns array\\\\.$#" count: 1 path: src/Projection/Projector/InMemoryProjectorRepository.php diff --git a/src/Metadata/Projector/AttributeProjectorMetadataFactory.php b/src/Metadata/Projector/AttributeProjectorMetadataFactory.php index 3e0e65294..358028ce2 100644 --- a/src/Metadata/Projector/AttributeProjectorMetadataFactory.php +++ b/src/Metadata/Projector/AttributeProjectorMetadataFactory.php @@ -6,18 +6,18 @@ use Patchlevel\EventSourcing\Attribute\Create; use Patchlevel\EventSourcing\Attribute\Drop; +use Patchlevel\EventSourcing\Attribute\Projection; use Patchlevel\EventSourcing\Attribute\Subscribe; -use Patchlevel\EventSourcing\Projection\Projector\Projector; use ReflectionClass; use function array_key_exists; final class AttributeProjectorMetadataFactory implements ProjectorMetadataFactory { - /** @var array, ProjectorMetadata> */ + /** @var array */ private array $projectorMetadata = []; - /** @param class-string $projector */ + /** @param class-string $projector */ public function metadata(string $projector): ProjectorMetadata { if (array_key_exists($projector, $this->projectorMetadata)) { @@ -26,6 +26,14 @@ public function metadata(string $projector): ProjectorMetadata $reflector = new ReflectionClass($projector); + $attributes = $reflector->getAttributes(Projection::class); + + if ($attributes === []) { + throw new ClassIsNotAProjector($projector); + } + + $projection = $attributes[0]->newInstance(); + $methods = $reflector->getMethods(); $subscribeMethods = []; @@ -79,6 +87,8 @@ public function metadata(string $projector): ProjectorMetadata } $metadata = new ProjectorMetadata( + $projection->name(), + $projection->version(), $subscribeMethods, $createMethod, $dropMethod, diff --git a/src/Metadata/Projector/ClassIsNotAProjector.php b/src/Metadata/Projector/ClassIsNotAProjector.php new file mode 100644 index 000000000..feb9276ff --- /dev/null +++ b/src/Metadata/Projector/ClassIsNotAProjector.php @@ -0,0 +1,23 @@ + $projector */ + /** @param class-string $projector */ public function __construct(string $projector, string $fistMethod, string $secondMethod) { parent::__construct( diff --git a/src/Metadata/Projector/DuplicateDropMethod.php b/src/Metadata/Projector/DuplicateDropMethod.php index 33ba479f9..35bb77717 100644 --- a/src/Metadata/Projector/DuplicateDropMethod.php +++ b/src/Metadata/Projector/DuplicateDropMethod.php @@ -5,13 +5,12 @@ namespace Patchlevel\EventSourcing\Metadata\Projector; use Patchlevel\EventSourcing\Metadata\MetadataException; -use Patchlevel\EventSourcing\Projection\Projector\Projector; use function sprintf; final class DuplicateDropMethod extends MetadataException { - /** @param class-string $projector */ + /** @param class-string $projector */ public function __construct(string $projector, string $fistMethod, string $secondMethod) { parent::__construct( diff --git a/src/Metadata/Projector/DuplicateSubscribeMethod.php b/src/Metadata/Projector/DuplicateSubscribeMethod.php index ff72e1a5c..8896c478e 100644 --- a/src/Metadata/Projector/DuplicateSubscribeMethod.php +++ b/src/Metadata/Projector/DuplicateSubscribeMethod.php @@ -5,15 +5,14 @@ namespace Patchlevel\EventSourcing\Metadata\Projector; use Patchlevel\EventSourcing\Metadata\MetadataException; -use Patchlevel\EventSourcing\Projection\Projector\Projector; use function sprintf; final class DuplicateSubscribeMethod extends MetadataException { /** - * @param class-string $projector - * @param class-string $event + * @param class-string $projector + * @param class-string $event */ public function __construct(string $projector, string $event, string $fistMethod, string $secondMethod) { diff --git a/src/Metadata/Projector/ProjectorMetadata.php b/src/Metadata/Projector/ProjectorMetadata.php index c236a4515..11904cfa3 100644 --- a/src/Metadata/Projector/ProjectorMetadata.php +++ b/src/Metadata/Projector/ProjectorMetadata.php @@ -7,6 +7,8 @@ final class ProjectorMetadata { public function __construct( + public readonly string $name, + public readonly int $version, /** @var array */ public readonly array $subscribeMethods = [], public readonly string|null $createMethod = null, diff --git a/src/Metadata/Projector/ProjectorMetadataFactory.php b/src/Metadata/Projector/ProjectorMetadataFactory.php index 445d82616..cc910600f 100644 --- a/src/Metadata/Projector/ProjectorMetadataFactory.php +++ b/src/Metadata/Projector/ProjectorMetadataFactory.php @@ -4,10 +4,8 @@ namespace Patchlevel\EventSourcing\Metadata\Projector; -use Patchlevel\EventSourcing\Projection\Projector\Projector; - interface ProjectorMetadataFactory { - /** @param class-string $projector */ + /** @param class-string $projector */ public function metadata(string $projector): ProjectorMetadata; } diff --git a/src/Metadata/Projector/Psr16ProjectorMetadataFactory.php b/src/Metadata/Projector/Psr16ProjectorMetadataFactory.php index 847bf4199..89650daf3 100644 --- a/src/Metadata/Projector/Psr16ProjectorMetadataFactory.php +++ b/src/Metadata/Projector/Psr16ProjectorMetadataFactory.php @@ -4,7 +4,6 @@ namespace Patchlevel\EventSourcing\Metadata\Projector; -use Patchlevel\EventSourcing\Projection\Projector\Projector; use Psr\SimpleCache\CacheInterface; final class Psr16ProjectorMetadataFactory implements ProjectorMetadataFactory @@ -15,7 +14,7 @@ public function __construct( ) { } - /** @param class-string $projector */ + /** @param class-string $projector */ public function metadata(string $projector): ProjectorMetadata { /** @var ?ProjectorMetadata $metadata */ diff --git a/src/Metadata/Projector/Psr6ProjectorMetadataFactory.php b/src/Metadata/Projector/Psr6ProjectorMetadataFactory.php index 33514f076..0146cd24f 100644 --- a/src/Metadata/Projector/Psr6ProjectorMetadataFactory.php +++ b/src/Metadata/Projector/Psr6ProjectorMetadataFactory.php @@ -4,7 +4,6 @@ namespace Patchlevel\EventSourcing\Metadata\Projector; -use Patchlevel\EventSourcing\Projection\Projector\Projector; use Psr\Cache\CacheItemPoolInterface; use function assert; @@ -17,7 +16,7 @@ public function __construct( ) { } - /** @param class-string $projector */ + /** @param class-string $projector */ public function metadata(string $projector): ProjectorMetadata { $item = $this->cache->getItem($projector); diff --git a/src/Pipeline/Target/ProjectorTarget.php b/src/Pipeline/Target/ProjectorTarget.php index 2f9aa9304..560320e77 100644 --- a/src/Pipeline/Target/ProjectorTarget.php +++ b/src/Pipeline/Target/ProjectorTarget.php @@ -6,14 +6,13 @@ use Patchlevel\EventSourcing\EventBus\Message; use Patchlevel\EventSourcing\Projection\Projector\MetadataProjectorResolver; -use Patchlevel\EventSourcing\Projection\Projector\Projector; use Patchlevel\EventSourcing\Projection\Projector\ProjectorHelper; use Patchlevel\EventSourcing\Projection\Projector\ProjectorResolver; final class ProjectorTarget implements Target { public function __construct( - private readonly Projector $projector, + private readonly object $projector, private readonly ProjectorResolver $projectorResolver = new MetadataProjectorResolver(), ) { } diff --git a/src/Projection/Projection/Store/ErrorSerializer.php b/src/Projection/Projection/Store/ErrorSerializer.php index 6be0749dd..28a74a19c 100644 --- a/src/Projection/Projection/Store/ErrorSerializer.php +++ b/src/Projection/Projection/Store/ErrorSerializer.php @@ -17,7 +17,11 @@ public static function serialize(Throwable|null $error): string|null return null; } - return serialize($error); + try { + return serialize($error); + } catch (Throwable) { + return null; + } } public static function unserialize(string|null $error): Throwable|null diff --git a/src/Projection/Projectionist/DefaultProjectionist.php b/src/Projection/Projectionist/DefaultProjectionist.php index 979bf1367..2b3c340c9 100644 --- a/src/Projection/Projectionist/DefaultProjectionist.php +++ b/src/Projection/Projectionist/DefaultProjectionist.php @@ -12,7 +12,6 @@ use Patchlevel\EventSourcing\Projection\Projection\ProjectionStatus; use Patchlevel\EventSourcing\Projection\Projection\Store\ProjectionStore; use Patchlevel\EventSourcing\Projection\Projector\MetadataProjectorResolver; -use Patchlevel\EventSourcing\Projection\Projector\Projector; use Patchlevel\EventSourcing\Projection\Projector\ProjectorRepository; use Patchlevel\EventSourcing\Projection\Projector\ProjectorResolver; use Patchlevel\EventSourcing\Store\CriteriaBuilder; @@ -24,7 +23,7 @@ final class DefaultProjectionist implements Projectionist { - /** @var array|null */ + /** @var array|null */ private array|null $projectors = null; public function __construct( @@ -354,15 +353,16 @@ public function reactivate(ProjectionCriteria $criteria = new ProjectionCriteria public function projections(): ProjectionCollection { $projections = $this->projectionStore->all(); + $projectors = $this->projectors(); - foreach ($this->projectors() as $projector) { - $targetProjection = $projector->targetProjection(); + foreach ($projectors as $projector) { + $projectionId = $this->projectorResolver->projectionId($projector); - if ($projections->has($targetProjection)) { + if ($projections->has($projectionId)) { continue; } - $projections = $projections->add(new Projection($targetProjection)); + $projections = $projections->add(new Projection($projectionId)); } return $projections; @@ -419,23 +419,23 @@ private function handleMessage(Message $message, Projection $projection, bool $t $this->projectionStore->save($projection); } - private function projector(ProjectionId $projectorId): Projector|null + private function projector(ProjectionId $projectorId): object|null { $projectors = $this->projectors(); return $projectors[$projectorId->toString()] ?? null; } - /** @return array */ + /** @return array */ private function projectors(): array { if ($this->projectors === null) { $this->projectors = []; foreach ($this->projectorRepository->projectors() as $projector) { - $targetProjection = $projector->targetProjection(); + $projectionId = $this->projectorResolver->projectionId($projector); - $this->projectors[$targetProjection->toString()] = $projector; + $this->projectors[$projectionId->toString()] = $projector; } } diff --git a/src/Projection/Projector/BasicProjector.php b/src/Projection/Projector/BasicProjector.php deleted file mode 100644 index 82aa8ff7c..000000000 --- a/src/Projection/Projector/BasicProjector.php +++ /dev/null @@ -1,37 +0,0 @@ -projectionId) { - return $this->projectionId; - } - - $reflection = new ReflectionClass($this); - $attributes = $reflection->getAttributes(Projection::class); - - if ($attributes === []) { - throw new ProjectionAttributeNotFound($reflection->getName()); - } - - $attribute = $attributes[0]->newInstance(); - - $this->projectionId = new ProjectionId( - $attribute->name(), - $attribute->version(), - ); - - return $this->projectionId; - } -} diff --git a/src/Projection/Projector/InMemoryProjectorRepository.php b/src/Projection/Projector/InMemoryProjectorRepository.php index 4643784c2..ef2aac7df 100644 --- a/src/Projection/Projector/InMemoryProjectorRepository.php +++ b/src/Projection/Projector/InMemoryProjectorRepository.php @@ -6,13 +6,13 @@ final class InMemoryProjectorRepository implements ProjectorRepository { - /** @param iterable $projectors */ + /** @param iterable $projectors */ public function __construct( private readonly iterable $projectors = [], ) { } - /** @return list */ + /** @return list */ public function projectors(): array { return [...$this->projectors]; diff --git a/src/Projection/Projector/MetadataProjectorResolver.php b/src/Projection/Projector/MetadataProjectorResolver.php index 477381ad1..6a3678fe6 100644 --- a/src/Projection/Projector/MetadataProjectorResolver.php +++ b/src/Projection/Projector/MetadataProjectorResolver.php @@ -8,6 +8,7 @@ use Patchlevel\EventSourcing\EventBus\Message; use Patchlevel\EventSourcing\Metadata\Projector\AttributeProjectorMetadataFactory; use Patchlevel\EventSourcing\Metadata\Projector\ProjectorMetadataFactory; +use Patchlevel\EventSourcing\Projection\Projection\ProjectionId; use function array_key_exists; @@ -18,7 +19,7 @@ public function __construct( ) { } - public function resolveCreateMethod(Projector $projector): Closure|null + public function resolveCreateMethod(object $projector): Closure|null { $metadata = $this->metadataFactory->metadata($projector::class); $method = $metadata->createMethod; @@ -30,7 +31,7 @@ public function resolveCreateMethod(Projector $projector): Closure|null return $projector->$method(...); } - public function resolveDropMethod(Projector $projector): Closure|null + public function resolveDropMethod(object $projector): Closure|null { $metadata = $this->metadataFactory->metadata($projector::class); $method = $metadata->dropMethod; @@ -42,7 +43,7 @@ public function resolveDropMethod(Projector $projector): Closure|null return $projector->$method(...); } - public function resolveSubscribeMethod(Projector $projector, Message $message): Closure|null + public function resolveSubscribeMethod(object $projector, Message $message): Closure|null { $event = $message->event(); $metadata = $this->metadataFactory->metadata($projector::class); @@ -55,4 +56,14 @@ public function resolveSubscribeMethod(Projector $projector, Message $message): return $projector->$subscribeMethod(...); } + + public function projectionId(object $projector): ProjectionId + { + $metadata = $this->metadataFactory->metadata($projector::class); + + return new ProjectionId( + $metadata->name, + $metadata->version, + ); + } } diff --git a/src/Projection/Projector/ProjectionAttributeNotFound.php b/src/Projection/Projector/ProjectionAttributeNotFound.php deleted file mode 100644 index 3f76eb52d..000000000 --- a/src/Projection/Projector/ProjectionAttributeNotFound.php +++ /dev/null @@ -1,20 +0,0 @@ -projectorResolver->resolveSubscribeMethod($projector, $message); @@ -26,7 +26,7 @@ public function handleMessage(Message $message, Projector ...$projectors): void } } - public function createProjection(Projector ...$projectors): void + public function createProjection(object ...$projectors): void { foreach ($projectors as $projector) { $createMethod = $this->projectorResolver->resolveCreateMethod($projector); @@ -39,7 +39,7 @@ public function createProjection(Projector ...$projectors): void } } - public function dropProjection(Projector ...$projectors): void + public function dropProjection(object ...$projectors): void { foreach ($projectors as $projector) { $dropMethod = $this->projectorResolver->resolveDropMethod($projector); diff --git a/src/Projection/Projector/ProjectorRepository.php b/src/Projection/Projector/ProjectorRepository.php index 6840f83cf..f6c4c4e45 100644 --- a/src/Projection/Projector/ProjectorRepository.php +++ b/src/Projection/Projector/ProjectorRepository.php @@ -6,6 +6,6 @@ interface ProjectorRepository { - /** @return list */ + /** @return list */ public function projectors(): array; } diff --git a/src/Projection/Projector/ProjectorResolver.php b/src/Projection/Projector/ProjectorResolver.php index bb9906147..83fea7bd2 100644 --- a/src/Projection/Projector/ProjectorResolver.php +++ b/src/Projection/Projector/ProjectorResolver.php @@ -6,12 +6,15 @@ use Closure; use Patchlevel\EventSourcing\EventBus\Message; +use Patchlevel\EventSourcing\Projection\Projection\ProjectionId; interface ProjectorResolver { - public function resolveCreateMethod(Projector $projector): Closure|null; + public function resolveCreateMethod(object $projector): Closure|null; - public function resolveDropMethod(Projector $projector): Closure|null; + public function resolveDropMethod(object $projector): Closure|null; - public function resolveSubscribeMethod(Projector $projector, Message $message): Closure|null; + public function resolveSubscribeMethod(object $projector, Message $message): Closure|null; + + public function projectionId(object $projector): ProjectionId; } diff --git a/tests/Benchmark/BasicImplementation/Projection/ProfileProjector.php b/tests/Benchmark/BasicImplementation/Projection/ProfileProjector.php index e89ebf4f5..0376c17d7 100644 --- a/tests/Benchmark/BasicImplementation/Projection/ProfileProjector.php +++ b/tests/Benchmark/BasicImplementation/Projection/ProfileProjector.php @@ -10,13 +10,12 @@ use Patchlevel\EventSourcing\Attribute\Projection; use Patchlevel\EventSourcing\Attribute\Subscribe; use Patchlevel\EventSourcing\EventBus\Message; -use Patchlevel\EventSourcing\Projection\Projector\BasicProjector; use Patchlevel\EventSourcing\Tests\Benchmark\BasicImplementation\Events\ProfileCreated; use function assert; #[Projection('dummy', 1)] -final class ProfileProjector extends BasicProjector +final class ProfileProjector { public function __construct( private Connection $connection, diff --git a/tests/Integration/BankAccountSplitStream/Projection/BankAccountProjection.php b/tests/Integration/BankAccountSplitStream/Projection/BankAccountProjection.php index 24fca1cff..50de2991c 100644 --- a/tests/Integration/BankAccountSplitStream/Projection/BankAccountProjection.php +++ b/tests/Integration/BankAccountSplitStream/Projection/BankAccountProjection.php @@ -8,25 +8,20 @@ use Doctrine\DBAL\Schema\Table; use Patchlevel\EventSourcing\Attribute\Create; use Patchlevel\EventSourcing\Attribute\Drop; +use Patchlevel\EventSourcing\Attribute\Projection; use Patchlevel\EventSourcing\Attribute\Subscribe; use Patchlevel\EventSourcing\EventBus\Message; -use Patchlevel\EventSourcing\Projection\Projection\ProjectionId; -use Patchlevel\EventSourcing\Projection\Projector\Projector; use Patchlevel\EventSourcing\Tests\Integration\BankAccountSplitStream\Events\BalanceAdded; use Patchlevel\EventSourcing\Tests\Integration\BankAccountSplitStream\Events\BankAccountCreated; -final class BankAccountProjection implements Projector +#[Projection('dummy', 1)] +final class BankAccountProjection { public function __construct( private Connection $connection, ) { } - public function targetProjection(): ProjectionId - { - return new ProjectionId('dummy', 1); - } - #[Create] public function create(): void { diff --git a/tests/Integration/BasicImplementation/Projection/ProfileProjection.php b/tests/Integration/BasicImplementation/Projection/ProfileProjection.php index 60803c680..927c8271a 100644 --- a/tests/Integration/BasicImplementation/Projection/ProfileProjection.php +++ b/tests/Integration/BasicImplementation/Projection/ProfileProjection.php @@ -11,13 +11,12 @@ use Patchlevel\EventSourcing\Attribute\Projection; use Patchlevel\EventSourcing\Attribute\Subscribe; use Patchlevel\EventSourcing\EventBus\Message; -use Patchlevel\EventSourcing\Projection\Projector\BasicProjector; use Patchlevel\EventSourcing\Tests\Integration\BasicImplementation\Events\ProfileCreated; use function assert; #[Projection('profile', 1)] -final class ProfileProjection extends BasicProjector +final class ProfileProjection { public function __construct( private Connection $connection, diff --git a/tests/Integration/Outbox/Projection/ProfileProjection.php b/tests/Integration/Outbox/Projection/ProfileProjection.php index 5bbdc89de..e4535208b 100644 --- a/tests/Integration/Outbox/Projection/ProfileProjection.php +++ b/tests/Integration/Outbox/Projection/ProfileProjection.php @@ -8,24 +8,19 @@ use Doctrine\DBAL\Schema\Table; use Patchlevel\EventSourcing\Attribute\Create; use Patchlevel\EventSourcing\Attribute\Drop; +use Patchlevel\EventSourcing\Attribute\Projection; use Patchlevel\EventSourcing\Attribute\Subscribe; use Patchlevel\EventSourcing\EventBus\Message; -use Patchlevel\EventSourcing\Projection\Projection\ProjectionId; -use Patchlevel\EventSourcing\Projection\Projector\Projector; use Patchlevel\EventSourcing\Tests\Integration\Outbox\Events\ProfileCreated; -final class ProfileProjection implements Projector +#[Projection('dummy', 1)] +final class ProfileProjection { public function __construct( private Connection $connection, ) { } - public function targetProjection(): ProjectionId - { - return new ProjectionId('dummy', 1); - } - #[Create] public function create(): void { diff --git a/tests/Integration/Projectionist/Projection/ProfileProjection.php b/tests/Integration/Projectionist/Projection/ProfileProjection.php index 02084c979..16230abec 100644 --- a/tests/Integration/Projectionist/Projection/ProfileProjection.php +++ b/tests/Integration/Projectionist/Projection/ProfileProjection.php @@ -8,16 +8,15 @@ use Doctrine\DBAL\Schema\Table; use Patchlevel\EventSourcing\Attribute\Create; use Patchlevel\EventSourcing\Attribute\Drop; +use Patchlevel\EventSourcing\Attribute\Projection; use Patchlevel\EventSourcing\Attribute\Subscribe; use Patchlevel\EventSourcing\EventBus\Message; -use Patchlevel\EventSourcing\Projection\Projection\ProjectionId; -use Patchlevel\EventSourcing\Projection\Projector\Projector; use Patchlevel\EventSourcing\Tests\Integration\Projectionist\Events\ProfileCreated; use function assert; -use function sprintf; -final class ProfileProjection implements Projector +#[Projection('profile', 1)] +final class ProfileProjection { public function __construct( private Connection $connection, @@ -59,15 +58,6 @@ public function handleProfileCreated(Message $message): void private function tableName(): string { - return sprintf( - 'projection_%s_%s', - $this->targetProjection()->name(), - $this->targetProjection()->version(), - ); - } - - public function targetProjection(): ProjectionId - { - return new ProjectionId('profile', 1); + return 'projection_profile_1'; } } diff --git a/tests/Unit/Fixture/Dummy2Projection.php b/tests/Unit/Fixture/Dummy2Projection.php index 036a6ccd7..aef8dbb33 100644 --- a/tests/Unit/Fixture/Dummy2Projection.php +++ b/tests/Unit/Fixture/Dummy2Projection.php @@ -6,12 +6,13 @@ use Patchlevel\EventSourcing\Attribute\Create; use Patchlevel\EventSourcing\Attribute\Drop; +use Patchlevel\EventSourcing\Attribute\Projection; use Patchlevel\EventSourcing\Attribute\Subscribe; use Patchlevel\EventSourcing\EventBus\Message as EventMessage; use Patchlevel\EventSourcing\Projection\Projection\ProjectionId; -use Patchlevel\EventSourcing\Projection\Projector\Projector; -final class Dummy2Projection implements Projector +#[Projection('dummy2', 1)] +final class Dummy2Projection { public EventMessage|null $handledMessage = null; public bool $createCalled = false; diff --git a/tests/Unit/Fixture/DummyProjection.php b/tests/Unit/Fixture/DummyProjection.php index dcc76b35c..2d28f590b 100644 --- a/tests/Unit/Fixture/DummyProjection.php +++ b/tests/Unit/Fixture/DummyProjection.php @@ -6,12 +6,13 @@ use Patchlevel\EventSourcing\Attribute\Create; use Patchlevel\EventSourcing\Attribute\Drop; +use Patchlevel\EventSourcing\Attribute\Projection; use Patchlevel\EventSourcing\Attribute\Subscribe; use Patchlevel\EventSourcing\EventBus\Message as EventMessage; use Patchlevel\EventSourcing\Projection\Projection\ProjectionId; -use Patchlevel\EventSourcing\Projection\Projector\Projector; -final class DummyProjection implements Projector +#[Projection('dummy', 1)] +final class DummyProjection { public EventMessage|null $handledMessage = null; public bool $createCalled = false; diff --git a/tests/Unit/Metadata/Projector/AttributeProjectorMetadataFactoryTest.php b/tests/Unit/Metadata/Projector/AttributeProjectorMetadataFactoryTest.php index bfec4d5e0..ff02a17d3 100644 --- a/tests/Unit/Metadata/Projector/AttributeProjectorMetadataFactoryTest.php +++ b/tests/Unit/Metadata/Projector/AttributeProjectorMetadataFactoryTest.php @@ -6,25 +6,33 @@ use Patchlevel\EventSourcing\Attribute\Create; use Patchlevel\EventSourcing\Attribute\Drop; +use Patchlevel\EventSourcing\Attribute\Projection; use Patchlevel\EventSourcing\Attribute\Subscribe; use Patchlevel\EventSourcing\Metadata\Projector\AttributeProjectorMetadataFactory; +use Patchlevel\EventSourcing\Metadata\Projector\ClassIsNotAProjector; use Patchlevel\EventSourcing\Metadata\Projector\DuplicateCreateMethod; use Patchlevel\EventSourcing\Metadata\Projector\DuplicateDropMethod; -use Patchlevel\EventSourcing\Projection\Projection\ProjectionId; -use Patchlevel\EventSourcing\Projection\Projector\Projector; use Patchlevel\EventSourcing\Tests\Unit\Fixture\ProfileCreated; use Patchlevel\EventSourcing\Tests\Unit\Fixture\ProfileVisited; use PHPUnit\Framework\TestCase; final class AttributeProjectorMetadataFactoryTest extends TestCase { + public function testNotAProjection(): void + { + $this->expectException(ClassIsNotAProjector::class); + + $projection = new class { + }; + + $metadataFactory = new AttributeProjectorMetadataFactory(); + $metadataFactory->metadata($projection::class); + } + public function testEmptyProjection(): void { - $projection = new class implements Projector { - public function targetProjection(): ProjectionId - { - return new ProjectionId('foo', 1); - } + $projection = new #[Projection('foo', 1)] + class { }; $metadataFactory = new AttributeProjectorMetadataFactory(); @@ -33,16 +41,14 @@ public function targetProjection(): ProjectionId self::assertSame([], $metadata->subscribeMethods); self::assertNull($metadata->createMethod); self::assertNull($metadata->dropMethod); + self::assertSame('foo', $metadata->name); + self::assertSame(1, $metadata->version); } public function testStandardProjection(): void { - $projection = new class implements Projector { - public function targetProjection(): ProjectionId - { - return new ProjectionId('foo', 1); - } - + $projection = new #[Projection('foo', 1)] + class { #[Subscribe(ProfileVisited::class)] public function handle(): void { @@ -73,12 +79,8 @@ public function drop(): void public function testMultipleHandlerOnOneMethod(): void { - $projection = new class implements Projector { - public function targetProjection(): ProjectionId - { - return new ProjectionId('foo', 1); - } - + $projection = new #[Projection('foo', 1)] + class { #[Subscribe(ProfileVisited::class)] #[Subscribe(ProfileCreated::class)] public function handle(): void @@ -102,12 +104,8 @@ public function testDuplicateCreateAttributeException(): void { $this->expectException(DuplicateCreateMethod::class); - $projection = new class implements Projector { - public function targetProjection(): ProjectionId - { - return new ProjectionId('foo', 1); - } - + $projection = new #[Projection('foo', 1)] + class { #[Create] public function create1(): void { @@ -127,12 +125,8 @@ public function testDuplicateDropAttributeException(): void { $this->expectException(DuplicateDropMethod::class); - $projection = new class implements Projector { - public function targetProjection(): ProjectionId - { - return new ProjectionId('foo', 1); - } - + $projection = new #[Projection('foo', 1)] + class { #[Drop] public function drop1(): void { diff --git a/tests/Unit/Projection/Projectionist/DefaultProjectionistTest.php b/tests/Unit/Projection/Projectionist/DefaultProjectionistTest.php index 50b0a16f9..4c7dbff67 100644 --- a/tests/Unit/Projection/Projectionist/DefaultProjectionistTest.php +++ b/tests/Unit/Projection/Projectionist/DefaultProjectionistTest.php @@ -4,6 +4,7 @@ namespace Patchlevel\EventSourcing\Tests\Unit\Projection\Projectionist; +use Patchlevel\EventSourcing\Attribute\Projection as ProjectionAttribute; use Patchlevel\EventSourcing\EventBus\Message; use Patchlevel\EventSourcing\Projection\Projection\Projection; use Patchlevel\EventSourcing\Projection\Projection\ProjectionCollection; @@ -12,7 +13,6 @@ use Patchlevel\EventSourcing\Projection\Projection\ProjectionStatus; use Patchlevel\EventSourcing\Projection\Projection\Store\ProjectionStore; use Patchlevel\EventSourcing\Projection\Projectionist\DefaultProjectionist; -use Patchlevel\EventSourcing\Projection\Projector\Projector; use Patchlevel\EventSourcing\Projection\Projector\ProjectorRepository; use Patchlevel\EventSourcing\Projection\Projector\ProjectorResolver; use Patchlevel\EventSourcing\Store\ArrayStream; @@ -58,15 +58,13 @@ public function testNothingToBoot(): void public function testBootWithoutCreateMethod(): void { - $projector = new class implements Projector { - public function targetProjection(): ProjectionId - { - return new ProjectionId('test', 1); - } + $projectionId = new ProjectionId('test', 1); + $projector = new #[ProjectionAttribute('test', 1)] + class { }; $projectionStore = new DummyStore([ - new Projection($projector->targetProjection()), + new Projection($projectionId), ]); $message = new Message(new ProfileVisited(ProfileId::fromString('test'))); @@ -78,6 +76,9 @@ public function targetProjection(): ProjectionId $projectorRepository->projectors()->willReturn([$projector])->shouldBeCalledOnce(); $projectorResolver = $this->prophesize(ProjectorResolver::class); + $projectorResolver->projectionId($projector)->willReturn($projectionId); + $projectorResolver->resolveCreateMethod($projector)->willReturn(null); + $projectorResolver->resolveSubscribeMethod($projector, $message)->willReturn(null); $projectionist = new DefaultProjectionist( $streamableStore->reveal(), @@ -89,23 +90,20 @@ public function targetProjection(): ProjectionId $projectionist->boot(); self::assertEquals([ - new Projection($projector->targetProjection(), ProjectionStatus::Booting), - new Projection($projector->targetProjection(), ProjectionStatus::Booting, 1), - new Projection($projector->targetProjection(), ProjectionStatus::Active, 1), + new Projection($projectionId, ProjectionStatus::Booting), + new Projection($projectionId, ProjectionStatus::Booting, 1), + new Projection($projectionId, ProjectionStatus::Active, 1), ], $projectionStore->savedProjections); } public function testBootWithMethods(): void { - $projector = new class implements Projector { + $projectionId = new ProjectionId('test', 1); + $projector = new #[ProjectionAttribute('test', 1)] + class { public Message|null $message = null; public bool $created = false; - public function targetProjection(): ProjectionId - { - return new ProjectionId('test', 1); - } - public function create(): void { $this->created = true; @@ -130,6 +128,7 @@ public function handle(Message $message): void $projectorResolver = $this->prophesize(ProjectorResolver::class); $projectorResolver->resolveCreateMethod($projector)->willReturn($projector->create(...)); $projectorResolver->resolveSubscribeMethod($projector, $message)->willReturn($projector->handle(...)); + $projectorResolver->projectionId($projector)->willReturn($projectionId); $projectionist = new DefaultProjectionist( $streamableStore->reveal(), @@ -141,9 +140,9 @@ public function handle(Message $message): void $projectionist->boot(); self::assertEquals([ - new Projection($projector->targetProjection(), ProjectionStatus::Booting), - new Projection($projector->targetProjection(), ProjectionStatus::Booting, 1), - new Projection($projector->targetProjection(), ProjectionStatus::Active, 1), + new Projection($projectionId, ProjectionStatus::Booting), + new Projection($projectionId, ProjectionStatus::Booting, 1), + new Projection($projectionId, ProjectionStatus::Active, 1), ], $projectionStore->savedProjections); self::assertTrue($projector->created); @@ -152,15 +151,12 @@ public function handle(Message $message): void public function testBootWithLimit(): void { - $projector = new class implements Projector { + $projectionId = new ProjectionId('test', 1); + $projector = new #[ProjectionAttribute('test', 1)] + class { public Message|null $message = null; public bool $created = false; - public function targetProjection(): ProjectionId - { - return new ProjectionId('test', 1); - } - public function create(): void { $this->created = true; @@ -185,6 +181,7 @@ public function handle(Message $message): void $projectorResolver = $this->prophesize(ProjectorResolver::class); $projectorResolver->resolveCreateMethod($projector)->willReturn($projector->create(...)); $projectorResolver->resolveSubscribeMethod($projector, $message)->willReturn($projector->handle(...)); + $projectorResolver->projectionId($projector)->willReturn($projectionId); $projectionist = new DefaultProjectionist( $streamableStore->reveal(), @@ -196,8 +193,8 @@ public function handle(Message $message): void $projectionist->boot(new ProjectionCriteria(), 1); self::assertEquals([ - new Projection($projector->targetProjection(), ProjectionStatus::Booting), - new Projection($projector->targetProjection(), ProjectionStatus::Booting, 1), + new Projection($projectionId, ProjectionStatus::Booting), + new Projection($projectionId, ProjectionStatus::Booting, 1), ], $projectionStore->savedProjections); self::assertTrue($projector->created); @@ -206,17 +203,14 @@ public function handle(Message $message): void public function testBootWithCreateError(): void { - $projector = new class implements Projector { + $projectionId = new ProjectionId('test', 1); + $projector = new #[ProjectionAttribute('test', 1)] + class { public function __construct( public readonly RuntimeException $exception = new RuntimeException('ERROR'), ) { } - public function targetProjection(): ProjectionId - { - return new ProjectionId('test', 1); - } - public function create(): void { throw $this->exception; @@ -224,7 +218,7 @@ public function create(): void }; $projectionStore = new DummyStore([ - new Projection($projector->targetProjection()), + new Projection($projectionId), ]); $streamableStore = $this->prophesize(Store::class); @@ -235,6 +229,7 @@ public function create(): void $projectorResolver = $this->prophesize(ProjectorResolver::class); $projectorResolver->resolveCreateMethod($projector)->willReturn($projector->create(...)); + $projectorResolver->projectionId($projector)->willReturn($projectionId); $projectionist = new DefaultProjectionist( $streamableStore->reveal(), @@ -248,11 +243,11 @@ public function create(): void self::assertEquals( [ new Projection( - $projector->targetProjection(), + $projectionId, ProjectionStatus::Booting, ), new Projection( - $projector->targetProjection(), + $projectionId, ProjectionStatus::Error, 0, 'ERROR', @@ -265,21 +260,18 @@ public function create(): void public function testRunning(): void { - $projector = new class implements Projector { + $projectionId = new ProjectionId('test', 1); + $projector = new #[ProjectionAttribute('test', 1)] + class { public Message|null $message = null; - public function targetProjection(): ProjectionId - { - return new ProjectionId('test', 1); - } - public function handle(Message $message): void { $this->message = $message; } }; - $projectionStore = new DummyStore([new Projection($projector->targetProjection(), ProjectionStatus::Active)]); + $projectionStore = new DummyStore([new Projection($projectionId, ProjectionStatus::Active)]); $message = new Message(new ProfileVisited(ProfileId::fromString('test'))); @@ -291,6 +283,7 @@ public function handle(Message $message): void $projectorResolver = $this->prophesize(ProjectorResolver::class); $projectorResolver->resolveSubscribeMethod($projector, $message)->willReturn($projector->handle(...)); + $projectorResolver->projectionId($projector)->willReturn($projectionId); $projectionist = new DefaultProjectionist( $streamableStore->reveal(), @@ -302,7 +295,7 @@ public function handle(Message $message): void $projectionist->run(); self::assertEquals([ - new Projection($projector->targetProjection(), ProjectionStatus::Active, 1), + new Projection($projectionId, ProjectionStatus::Active, 1), ], $projectionStore->savedProjections); self::assertSame($message, $projector->message); @@ -310,21 +303,18 @@ public function handle(Message $message): void public function testRunningWithLimit(): void { - $projector = new class implements Projector { + $projectionId = new ProjectionId('test', 1); + $projector = new #[ProjectionAttribute('test', 1)] + class { public Message|null $message = null; - public function targetProjection(): ProjectionId - { - return new ProjectionId('test', 1); - } - public function handle(Message $message): void { $this->message = $message; } }; - $projectionStore = new DummyStore([new Projection($projector->targetProjection(), ProjectionStatus::Active)]); + $projectionStore = new DummyStore([new Projection($projectionId, ProjectionStatus::Active)]); $message1 = new Message(new ProfileVisited(ProfileId::fromString('test'))); $message2 = new Message(new ProfileVisited(ProfileId::fromString('test'))); @@ -340,6 +330,7 @@ public function handle(Message $message): void $projectorResolver = $this->prophesize(ProjectorResolver::class); $projectorResolver->resolveSubscribeMethod($projector, $message1)->willReturn($projector->handle(...)); + $projectorResolver->projectionId($projector)->willReturn($projectionId); $projectionist = new DefaultProjectionist( $streamableStore->reveal(), @@ -351,7 +342,7 @@ public function handle(Message $message): void $projectionist->run(new ProjectionCriteria(), 1); self::assertEquals([ - new Projection($projector->targetProjection(), ProjectionStatus::Active, 1), + new Projection($projectionId, ProjectionStatus::Active, 1), ], $projectionStore->savedProjections); self::assertSame($message1, $projector->message); @@ -359,28 +350,22 @@ public function handle(Message $message): void public function testRunningWithSkip(): void { - $projector1 = new class implements Projector { + $projectionId1 = new ProjectionId('test1', 1); + $projector1 = new #[ProjectionAttribute('test1', 1)] + class { public Message|null $message = null; - public function targetProjection(): ProjectionId - { - return new ProjectionId('test1', 1); - } - public function handle(Message $message): void { $this->message = $message; } }; - $projector2 = new class implements Projector { + $projectionId2 = new ProjectionId('test2', 1); + $projector2 = new #[ProjectionAttribute('test1', 1)] + class { public Message|null $message = null; - public function targetProjection(): ProjectionId - { - return new ProjectionId('test2', 1); - } - public function handle(Message $message): void { $this->message = $message; @@ -388,8 +373,8 @@ public function handle(Message $message): void }; $projectionStore = new DummyStore([ - new Projection($projector1->targetProjection(), ProjectionStatus::Active), - new Projection($projector2->targetProjection(), ProjectionStatus::Active, 1), + new Projection($projectionId1, ProjectionStatus::Active), + new Projection($projectionId2, ProjectionStatus::Active, 1), ]); $message = new Message(new ProfileVisited(ProfileId::fromString('test'))); @@ -402,6 +387,8 @@ public function handle(Message $message): void $projectorResolver = $this->prophesize(ProjectorResolver::class); $projectorResolver->resolveSubscribeMethod($projector1, $message)->willReturn($projector1->handle(...)); + $projectorResolver->projectionId($projector1)->willReturn($projectionId1); + $projectorResolver->projectionId($projector2)->willReturn($projectionId2); $projectionist = new DefaultProjectionist( $streamableStore->reveal(), @@ -413,7 +400,7 @@ public function handle(Message $message): void $projectionist->run(); self::assertEquals([ - new Projection($projector1->targetProjection(), ProjectionStatus::Active, 1), + new Projection($projectionId1, ProjectionStatus::Active, 1), ], $projectionStore->savedProjections); self::assertSame($message, $projector1->message); @@ -422,24 +409,21 @@ public function handle(Message $message): void public function testRunningWithError(): void { - $projector = new class implements Projector { + $projectionId = new ProjectionId('test', 1); + $projector = new #[ProjectionAttribute('test', 1)] + class { public function __construct( public readonly RuntimeException $exception = new RuntimeException('ERROR'), ) { } - public function targetProjection(): ProjectionId - { - return new ProjectionId('test', 1); - } - public function handle(Message $message): void { throw $this->exception; } }; - $projectionStore = new DummyStore([new Projection($projector->targetProjection(), ProjectionStatus::Active)]); + $projectionStore = new DummyStore([new Projection($projectionId, ProjectionStatus::Active)]); $message = new Message(new ProfileVisited(ProfileId::fromString('test'))); @@ -451,6 +435,7 @@ public function handle(Message $message): void $projectorResolver = $this->prophesize(ProjectorResolver::class); $projectorResolver->resolveSubscribeMethod($projector, $message)->willReturn($projector->handle(...)); + $projectorResolver->projectionId($projector)->willReturn($projectionId); $projectionist = new DefaultProjectionist( $streamableStore->reveal(), @@ -464,7 +449,7 @@ public function handle(Message $message): void self::assertEquals( [ new Projection( - $projector->targetProjection(), + $projectionId, ProjectionStatus::Error, 0, 'ERROR', @@ -477,9 +462,9 @@ public function handle(Message $message): void public function testRunningMarkOutdated(): void { - $projectorId = new ProjectionId('test', 1); + $projectionId = new ProjectionId('test', 1); - $projectionStore = new DummyStore([new Projection($projectorId, ProjectionStatus::Active)]); + $projectionStore = new DummyStore([new Projection($projectionId, ProjectionStatus::Active)]); $streamableStore = $this->prophesize(Store::class); $streamableStore->load($this->criteria())->shouldNotBeCalled(); @@ -499,27 +484,15 @@ public function testRunningMarkOutdated(): void $projectionist->run(); self::assertEquals([ - new Projection($projectorId, ProjectionStatus::Outdated, 0), + new Projection($projectionId, ProjectionStatus::Outdated, 0), ], $projectionStore->savedProjections); } public function testRunningWithoutActiveProjectors(): void { - $projector = new class implements Projector { - public Message|null $message = null; + $projectionId = new ProjectionId('test', 1); - public function targetProjection(): ProjectionId - { - return new ProjectionId('test', 1); - } - - public function handle(Message $message): void - { - $this->message = $message; - } - }; - - $projectionStore = new DummyStore([new Projection($projector->targetProjection(), ProjectionStatus::Booting)]); + $projectionStore = new DummyStore([new Projection($projectionId, ProjectionStatus::Booting)]); $streamableStore = $this->prophesize(Store::class); $streamableStore->load($this->criteria())->shouldNotBeCalled(); @@ -543,22 +516,19 @@ public function handle(Message $message): void public function testTeardownWithProjector(): void { - $projector = new class implements Projector { + $projectionId = new ProjectionId('test', 1); + $projector = new #[ProjectionAttribute('test', 1)] + class { public Message|null $message = null; public bool $dropped = false; - public function targetProjection(): ProjectionId - { - return new ProjectionId('test', 1); - } - public function drop(): void { $this->dropped = true; } }; - $projectionStore = new DummyStore([new Projection($projector->targetProjection(), ProjectionStatus::Outdated)]); + $projectionStore = new DummyStore([new Projection($projectionId, ProjectionStatus::Outdated)]); $streamableStore = $this->prophesize(Store::class); @@ -567,6 +537,7 @@ public function drop(): void $projectorResolver = $this->prophesize(ProjectorResolver::class); $projectorResolver->resolveDropMethod($projector)->willReturn($projector->drop(...)); + $projectorResolver->projectionId($projector)->willReturn($projectionId); $projectionist = new DefaultProjectionist( $streamableStore->reveal(), @@ -578,28 +549,25 @@ public function drop(): void $projectionist->teardown(); self::assertEquals([], $projectionStore->savedProjections); - self::assertEquals([$projector->targetProjection()], $projectionStore->removedProjectionIds); + self::assertEquals([$projectionId], $projectionStore->removedProjectionIds); self::assertTrue($projector->dropped); } public function testTeardownWithProjectorAndError(): void { - $projector = new class implements Projector { + $projectionId = new ProjectionId('test', 1); + $projector = new #[ProjectionAttribute('test', 1)] + class { public Message|null $message = null; public bool $dropped = false; - public function targetProjection(): ProjectionId - { - return new ProjectionId('test', 1); - } - public function drop(): void { throw new RuntimeException('ERROR'); } }; - $projectionStore = new DummyStore([new Projection($projector->targetProjection(), ProjectionStatus::Outdated)]); + $projectionStore = new DummyStore([new Projection($projectionId, ProjectionStatus::Outdated)]); $streamableStore = $this->prophesize(Store::class); @@ -608,6 +576,7 @@ public function drop(): void $projectorResolver = $this->prophesize(ProjectorResolver::class); $projectorResolver->resolveDropMethod($projector)->willReturn($projector->drop(...)); + $projectorResolver->projectionId($projector)->willReturn($projectionId); $projectionist = new DefaultProjectionist( $streamableStore->reveal(), @@ -650,21 +619,18 @@ public function testTeardownWithoutProjector(): void public function testRemoveWithProjector(): void { - $projector = new class implements Projector { + $projectionId = new ProjectionId('test', 1); + $projector = new #[ProjectionAttribute('test', 1)] + class { public bool $dropped = false; - public function targetProjection(): ProjectionId - { - return new ProjectionId('test', 1); - } - public function drop(): void { $this->dropped = true; } }; - $projectionStore = new DummyStore([new Projection($projector->targetProjection(), ProjectionStatus::Outdated)]); + $projectionStore = new DummyStore([new Projection($projectionId, ProjectionStatus::Outdated)]); $streamableStore = $this->prophesize(Store::class); @@ -673,6 +639,7 @@ public function drop(): void $projectorResolver = $this->prophesize(ProjectorResolver::class); $projectorResolver->resolveDropMethod($projector)->willReturn($projector->drop(...)); + $projectorResolver->projectionId($projector)->willReturn($projectionId); $projectionist = new DefaultProjectionist( $streamableStore->reveal(), @@ -684,20 +651,18 @@ public function drop(): void $projectionist->remove(); self::assertEquals([], $projectionStore->savedProjections); - self::assertEquals([$projector->targetProjection()], $projectionStore->removedProjectionIds); + self::assertEquals([$projectionId], $projectionStore->removedProjectionIds); self::assertTrue($projector->dropped); } public function testRemoveWithoutDropMethod(): void { - $projector = new class implements Projector { - public function targetProjection(): ProjectionId - { - return new ProjectionId('test', 1); - } + $projectionId = new ProjectionId('test', 1); + $projector = new #[ProjectionAttribute('test', 1)] + class { }; - $projectionStore = new DummyStore([new Projection($projector->targetProjection(), ProjectionStatus::Outdated)]); + $projectionStore = new DummyStore([new Projection($projectionId, ProjectionStatus::Outdated)]); $streamableStore = $this->prophesize(Store::class); @@ -706,6 +671,7 @@ public function targetProjection(): ProjectionId $projectorResolver = $this->prophesize(ProjectorResolver::class); $projectorResolver->resolveDropMethod($projector)->willReturn(null); + $projectorResolver->projectionId($projector)->willReturn($projectionId); $projectionist = new DefaultProjectionist( $streamableStore->reveal(), @@ -717,26 +683,23 @@ public function targetProjection(): ProjectionId $projectionist->remove(); self::assertEquals([], $projectionStore->savedProjections); - self::assertEquals([$projector->targetProjection()], $projectionStore->removedProjectionIds); + self::assertEquals([$projectionId], $projectionStore->removedProjectionIds); } public function testRemoveWithProjectorAndError(): void { - $projector = new class implements Projector { + $projectionId = new ProjectionId('test', 1); + $projector = new #[ProjectionAttribute('test', 1)] + class { public bool $dropped = false; - public function targetProjection(): ProjectionId - { - return new ProjectionId('test', 1); - } - public function drop(): void { throw new RuntimeException('ERROR'); } }; - $projectionStore = new DummyStore([new Projection($projector->targetProjection(), ProjectionStatus::Outdated)]); + $projectionStore = new DummyStore([new Projection($projectionId, ProjectionStatus::Outdated)]); $streamableStore = $this->prophesize(Store::class); @@ -745,6 +708,7 @@ public function drop(): void $projectorResolver = $this->prophesize(ProjectorResolver::class); $projectorResolver->resolveDropMethod($projector)->willReturn($projector->drop(...)); + $projectorResolver->projectionId($projector)->willReturn($projectionId); $projectionist = new DefaultProjectionist( $streamableStore->reveal(), @@ -756,7 +720,7 @@ public function drop(): void $projectionist->remove(); self::assertEquals([], $projectionStore->savedProjections); - self::assertEquals([$projector->targetProjection()], $projectionStore->removedProjectionIds); + self::assertEquals([$projectionId], $projectionStore->removedProjectionIds); } public function testRemoveWithoutProjector(): void @@ -787,14 +751,12 @@ public function testRemoveWithoutProjector(): void public function testReactivate(): void { - $projector = new class implements Projector { - public function targetProjection(): ProjectionId - { - return new ProjectionId('test', 1); - } + $projectionId = new ProjectionId('test', 1); + $projector = new #[ProjectionAttribute('test', 1)] + class { }; - $projectionStore = new DummyStore([new Projection($projector->targetProjection(), ProjectionStatus::Error)]); + $projectionStore = new DummyStore([new Projection($projectionId, ProjectionStatus::Error)]); $streamableStore = $this->prophesize(Store::class); @@ -802,6 +764,7 @@ public function targetProjection(): ProjectionId $projectorRepository->projectors()->willReturn([$projector])->shouldBeCalledOnce(); $projectorResolver = $this->prophesize(ProjectorResolver::class); + $projectorResolver->projectionId($projector)->willReturn($projectionId); $projectionist = new DefaultProjectionist( $streamableStore->reveal(), @@ -813,7 +776,7 @@ public function targetProjection(): ProjectionId $projectionist->reactivate(); self::assertEquals([ - new Projection($projector->targetProjection(), ProjectionStatus::Active, 0), + new Projection($projectionId, ProjectionStatus::Active, 0), ], $projectionStore->savedProjections); } diff --git a/tests/Unit/Projection/Projector/BasicProjectorTest.php b/tests/Unit/Projection/Projector/BasicProjectorTest.php deleted file mode 100644 index 2e244d455..000000000 --- a/tests/Unit/Projection/Projector/BasicProjectorTest.php +++ /dev/null @@ -1,37 +0,0 @@ -targetProjection(), - ); - } - - public function testMissingProjectionId(): void - { - $this->expectException(ProjectionAttributeNotFound::class); - - $projector = new class extends BasicProjector { - }; - - $projector->targetProjection(); - } -} diff --git a/tests/Unit/Projection/Projector/InMemoryProjectorRepositoryTest.php b/tests/Unit/Projection/Projector/InMemoryProjectorRepositoryTest.php index e2804f5a8..746408b9b 100644 --- a/tests/Unit/Projection/Projector/InMemoryProjectorRepositoryTest.php +++ b/tests/Unit/Projection/Projector/InMemoryProjectorRepositoryTest.php @@ -4,9 +4,7 @@ namespace Patchlevel\EventSourcing\Tests\Unit\Projection\Projector; -use Patchlevel\EventSourcing\Projection\Projection\ProjectionId; use Patchlevel\EventSourcing\Projection\Projector\InMemoryProjectorRepository; -use Patchlevel\EventSourcing\Projection\Projector\Projector; use PHPUnit\Framework\TestCase; /** @covers \Patchlevel\EventSourcing\Projection\Projector\InMemoryProjectorRepository */ @@ -20,11 +18,7 @@ public function testGetAllProjectorsIsEmpty(): void public function testGetAllProjectors(): void { - $projector = new class implements Projector { - public function targetProjection(): ProjectionId - { - return new ProjectionId('dummy', 1); - } + $projector = new class { }; $repository = new InMemoryProjectorRepository([$projector]); diff --git a/tests/Unit/Projection/Projector/MetadataProjectorResolverTest.php b/tests/Unit/Projection/Projector/MetadataProjectorResolverTest.php index ff9416287..b07880c54 100644 --- a/tests/Unit/Projection/Projector/MetadataProjectorResolverTest.php +++ b/tests/Unit/Projection/Projector/MetadataProjectorResolverTest.php @@ -6,11 +6,10 @@ use Patchlevel\EventSourcing\Attribute\Create; use Patchlevel\EventSourcing\Attribute\Drop; +use Patchlevel\EventSourcing\Attribute\Projection; use Patchlevel\EventSourcing\Attribute\Subscribe; use Patchlevel\EventSourcing\EventBus\Message; -use Patchlevel\EventSourcing\Projection\Projection\ProjectionId; use Patchlevel\EventSourcing\Projection\Projector\MetadataProjectorResolver; -use Patchlevel\EventSourcing\Projection\Projector\Projector; use Patchlevel\EventSourcing\Tests\Unit\Fixture\Email; use Patchlevel\EventSourcing\Tests\Unit\Fixture\ProfileCreated; use Patchlevel\EventSourcing\Tests\Unit\Fixture\ProfileId; @@ -22,14 +21,10 @@ final class MetadataProjectorResolverTest extends TestCase { public function testResolveHandleMethod(): void { - $projection = new class implements Projector { + $projection = new #[Projection('dummy')] + class { public static Message|null $handledMessage = null; - public function targetProjection(): ProjectionId - { - return new ProjectionId('dummy', 1); - } - #[Subscribe(ProfileCreated::class)] public function handleProfileCreated(Message $message): void { @@ -56,11 +51,8 @@ public function handleProfileCreated(Message $message): void public function testNotResolveHandleMethod(): void { - $projection = new class implements Projector { - public function targetProjection(): ProjectionId - { - return new ProjectionId('dummy', 1); - } + $projection = new #[Projection('dummy')] + class { }; $message = new Message( @@ -77,14 +69,10 @@ public function targetProjection(): ProjectionId public function testResolveCreateMethod(): void { - $projection = new class implements Projector { + $projection = new #[Projection('dummy')] + class { public static bool $called = false; - public function targetProjection(): ProjectionId - { - return new ProjectionId('dummy', 1); - } - #[Create] public function method(): void { @@ -104,11 +92,8 @@ public function method(): void public function testNotResolveCreateMethod(): void { - $projection = new class implements Projector { - public function targetProjection(): ProjectionId - { - return new ProjectionId('dummy', 1); - } + $projection = new #[Projection('dummy')] + class { }; $resolver = new MetadataProjectorResolver(); @@ -119,14 +104,10 @@ public function targetProjection(): ProjectionId public function testResolveDropMethod(): void { - $projection = new class implements Projector { + $projection = new #[Projection('dummy')] + class { public static bool $called = false; - public function targetProjection(): ProjectionId - { - return new ProjectionId('dummy', 1); - } - #[Drop] public function method(): void { @@ -146,11 +127,8 @@ public function method(): void public function testNotResolveDropMethod(): void { - $projection = new class implements Projector { - public function targetProjection(): ProjectionId - { - return new ProjectionId('dummy', 1); - } + $projection = new #[Projection('dummy')] + class { }; $resolver = new MetadataProjectorResolver(); @@ -158,4 +136,17 @@ public function targetProjection(): ProjectionId self::assertNull($result); } + + public function testProjectionId(): void + { + $projection = new #[Projection('dummy', 1)] + class { + }; + + $resolver = new MetadataProjectorResolver(); + $result = $resolver->projectionId($projection); + + self::assertEquals('dummy', $result->name()); + self::assertEquals(1, $result->version()); + } } diff --git a/tests/Unit/Projection/Projector/ProjectorHelperTest.php b/tests/Unit/Projection/Projector/ProjectorHelperTest.php index 257bfb23e..01721982b 100644 --- a/tests/Unit/Projection/Projector/ProjectorHelperTest.php +++ b/tests/Unit/Projection/Projector/ProjectorHelperTest.php @@ -6,10 +6,10 @@ use Patchlevel\EventSourcing\Attribute\Create; use Patchlevel\EventSourcing\Attribute\Drop; +use Patchlevel\EventSourcing\Attribute\Projection; use Patchlevel\EventSourcing\Attribute\Subscribe; use Patchlevel\EventSourcing\EventBus\Message; use Patchlevel\EventSourcing\Projection\Projection\ProjectionId; -use Patchlevel\EventSourcing\Projection\Projector\Projector; use Patchlevel\EventSourcing\Projection\Projector\ProjectorHelper; use Patchlevel\EventSourcing\Tests\Unit\Fixture\Email; use Patchlevel\EventSourcing\Tests\Unit\Fixture\ProfileCreated; @@ -22,14 +22,10 @@ final class ProjectorHelperTest extends TestCase { public function testHandle(): void { - $projector = new class implements Projector { + $projector = new #[Projection('dummy')] + class { public static Message|null $handledMessage = null; - public function targetProjection(): ProjectionId - { - return new ProjectionId('dummy', 1); - } - #[Subscribe(ProfileCreated::class)] public function handleProfileCreated(Message $message): void { @@ -54,7 +50,8 @@ public function handleProfileCreated(Message $message): void public function testHandleNotSupportedEvent(): void { - $projector = new class implements Projector { + $projector = new #[Projection('dummy')] + class { public static Message|null $handledMessage = null; public function targetProjection(): ProjectionId @@ -85,7 +82,8 @@ public function handleProfileCreated(Message $message): void public function testCreate(): void { - $projector = new class implements Projector { + $projector = new #[Projection('dummy')] + class { public static bool $called = false; public function targetProjection(): ProjectionId @@ -108,7 +106,8 @@ public function method(): void public function testDrop(): void { - $projector = new class implements Projector { + $projector = new #[Projection('dummy')] + class { public static bool $called = false; public function targetProjection(): ProjectionId