diff --git a/baseline.xml b/baseline.xml index 22ff0c1fe..1009d302a 100644 --- a/baseline.xml +++ b/baseline.xml @@ -121,7 +121,7 @@ - + diff --git a/src/Subscription/Subscriber/NoSuitableResolver.php b/src/Subscription/Subscriber/NoSuitableResolver.php index a7a096410..f106931a3 100644 --- a/src/Subscription/Subscriber/NoSuitableResolver.php +++ b/src/Subscription/Subscriber/NoSuitableResolver.php @@ -8,7 +8,7 @@ use function sprintf; -class NoSuitableResolver extends RuntimeException +final class NoSuitableResolver extends RuntimeException { public function __construct(string $class, string $methodName, string $argumentName) { diff --git a/tests/Unit/Metadata/Subscriber/AttributeSubscriberMetadataFactoryTest.php b/tests/Unit/Metadata/Subscriber/AttributeSubscriberMetadataFactoryTest.php index d8a917ec7..999e397e5 100644 --- a/tests/Unit/Metadata/Subscriber/AttributeSubscriberMetadataFactoryTest.php +++ b/tests/Unit/Metadata/Subscriber/AttributeSubscriberMetadataFactoryTest.php @@ -10,6 +10,8 @@ use Patchlevel\EventSourcing\Attribute\Subscribe; use Patchlevel\EventSourcing\Attribute\Subscriber; use Patchlevel\EventSourcing\Attribute\Teardown; +use Patchlevel\EventSourcing\Message\Message; +use Patchlevel\EventSourcing\Metadata\Subscriber\ArgumentMetadata; use Patchlevel\EventSourcing\Metadata\Subscriber\AttributeSubscriberMetadataFactory; use Patchlevel\EventSourcing\Metadata\Subscriber\ClassIsNotASubscriber; use Patchlevel\EventSourcing\Metadata\Subscriber\DuplicateSetupMethod; @@ -163,6 +165,42 @@ public function handle(): void ); } + public function testSubscribeAttributes(): void + { + $subscriber = new #[Subscriber('foo', RunMode::FromBeginning)] + class { + #[Subscribe(ProfileVisited::class)] + public function profileVisited(Message $message): void + { + } + + #[Subscribe(ProfileCreated::class)] + public function profileCreated(ProfileCreated $profileCreated, string $aggregateId): void + { + } + }; + + $metadataFactory = new AttributeSubscriberMetadataFactory(); + $metadata = $metadataFactory->metadata($subscriber::class); + + self::assertEquals( + [ + ProfileVisited::class => [ + new SubscribeMethodMetadata('profileVisited', [ + new ArgumentMetadata('message', Message::class), + ]), + ], + ProfileCreated::class => [ + new SubscribeMethodMetadata('profileCreated', [ + new ArgumentMetadata('profileCreated', ProfileCreated::class), + new ArgumentMetadata('aggregateId', 'string'), + ]), + ], + ], + $metadata->subscribeMethods, + ); + } + public function testDuplicateSetupAttributeException(): void { $this->expectException(DuplicateSetupMethod::class); diff --git a/tests/Unit/Subscription/Subscriber/ArgumentResolver/AggregateIdArgumentResolverTest.php b/tests/Unit/Subscription/Subscriber/ArgumentResolver/AggregateIdArgumentResolverTest.php new file mode 100644 index 000000000..00db10f22 --- /dev/null +++ b/tests/Unit/Subscription/Subscriber/ArgumentResolver/AggregateIdArgumentResolverTest.php @@ -0,0 +1,63 @@ +support( + new ArgumentMetadata('aggregateId', 'string'), + ProfileCreated::class, + ), + ); + + self::assertTrue( + $resolver->support( + new ArgumentMetadata('aggregateRootId', 'string'), + ProfileCreated::class, + ), + ); + + self::assertFalse( + $resolver->support( + new ArgumentMetadata('foo', 'string'), + ProfileCreated::class, + ), + ); + } + + public function testResolve(): void + { + $event = new ProfileVisited(ProfileId::fromString('1')); + + $resolver = new AggregateIdArgumentResolver(); + $message = (new Message($event))->withHeader( + new AggregateHeader('foo', 'bar', 1, new DateTimeImmutable()), + ); + + self::assertSame( + 'bar', + $resolver->resolve( + new ArgumentMetadata('foo', 'string'), + $message, + ), + ); + } +} diff --git a/tests/Unit/Subscription/Subscriber/ArgumentResolver/EventArgumentResolverTest.php b/tests/Unit/Subscription/Subscriber/ArgumentResolver/EventArgumentResolverTest.php new file mode 100644 index 000000000..14bb807c4 --- /dev/null +++ b/tests/Unit/Subscription/Subscriber/ArgumentResolver/EventArgumentResolverTest.php @@ -0,0 +1,52 @@ +support( + new ArgumentMetadata('foo', ProfileCreated::class), + ProfileCreated::class, + ), + ); + + self::assertFalse( + $resolver->support( + new ArgumentMetadata('foo', ProfileVisited::class), + ProfileCreated::class, + ), + ); + } + + public function testResolve(): void + { + $event = new ProfileVisited(ProfileId::fromString('1')); + + $resolver = new EventArgumentResolver(); + $message = new Message($event); + + self::assertSame( + $event, + $resolver->resolve( + new ArgumentMetadata('foo', ProfileVisited::class), + $message, + ), + ); + } +} diff --git a/tests/Unit/Subscription/Subscriber/ArgumentResolver/MessageArgumentResolverTest.php b/tests/Unit/Subscription/Subscriber/ArgumentResolver/MessageArgumentResolverTest.php new file mode 100644 index 000000000..6dc808a5c --- /dev/null +++ b/tests/Unit/Subscription/Subscriber/ArgumentResolver/MessageArgumentResolverTest.php @@ -0,0 +1,48 @@ +support( + new ArgumentMetadata('foo', Message::class), + 'qux', + ), + ); + + self::assertFalse( + $resolver->support( + new ArgumentMetadata('foo', 'bar'), + 'qux', + ), + ); + } + + public function testResolve(): void + { + $resolver = new MessageArgumentResolver(); + $message = new Message(new stdClass()); + + self::assertSame( + $message, + $resolver->resolve( + new ArgumentMetadata('foo', Message::class), + $message, + ), + ); + } +} diff --git a/tests/Unit/Subscription/Subscriber/ArgumentResolver/RecordedOnArgumentResolverTest.php b/tests/Unit/Subscription/Subscriber/ArgumentResolver/RecordedOnArgumentResolverTest.php new file mode 100644 index 000000000..6a4480166 --- /dev/null +++ b/tests/Unit/Subscription/Subscriber/ArgumentResolver/RecordedOnArgumentResolverTest.php @@ -0,0 +1,59 @@ +support( + new ArgumentMetadata('foo', DateTimeImmutable::class), + 'qux', + ), + ); + + self::assertFalse( + $resolver->support( + new ArgumentMetadata('foo', 'bar'), + 'qux', + ), + ); + } + + public function testResolve(): void + { + $date = new DateTimeImmutable(); + + $resolver = new RecordedOnArgumentResolver(); + $message = (new Message(new stdClass()))->withHeader( + new AggregateHeader( + 'foo', + 'bar', + 1, + $date, + ), + ); + + self::assertSame( + $date, + $resolver->resolve( + new ArgumentMetadata('foo', DateTimeImmutable::class), + $message, + ), + ); + } +} diff --git a/tests/Unit/Subscription/Subscriber/MetadataSubscriberAccessorRepositoryTest.php b/tests/Unit/Subscription/Subscriber/MetadataSubscriberAccessorRepositoryTest.php index 96d02c610..2970e4088 100644 --- a/tests/Unit/Subscription/Subscriber/MetadataSubscriberAccessorRepositoryTest.php +++ b/tests/Unit/Subscription/Subscriber/MetadataSubscriberAccessorRepositoryTest.php @@ -5,6 +5,8 @@ namespace Patchlevel\EventSourcing\Tests\Unit\Subscription\Subscriber; use Patchlevel\EventSourcing\Attribute\Subscriber; +use Patchlevel\EventSourcing\Message\Message; +use Patchlevel\EventSourcing\Metadata\Subscriber\ArgumentMetadata; use Patchlevel\EventSourcing\Metadata\Subscriber\AttributeSubscriberMetadataFactory; use Patchlevel\EventSourcing\Subscription\RunMode; use Patchlevel\EventSourcing\Subscription\Subscriber\ArgumentResolver; @@ -32,15 +34,29 @@ class { }; $metadataFactory = new AttributeSubscriberMetadataFactory(); + $customResolver = new class implements ArgumentResolver\ArgumentResolver { + public function resolve(ArgumentMetadata $argument, Message $message): mixed + { + return null; + } + + public function support(ArgumentMetadata $argument, string $eventClass): bool + { + return false; + } + }; + $repository = new MetadataSubscriberAccessorRepository( [$subscriber], $metadataFactory, + [$customResolver], ); $accessor = new MetadataSubscriberAccessor( $subscriber, $metadataFactory->metadata($subscriber::class), [ + $customResolver, new ArgumentResolver\MessageArgumentResolver(), new ArgumentResolver\EventArgumentResolver(), new ArgumentResolver\AggregateIdArgumentResolver(), diff --git a/tests/Unit/Subscription/Subscriber/MetadataSubscriberAccessorTest.php b/tests/Unit/Subscription/Subscriber/MetadataSubscriberAccessorTest.php index dff93706e..1991d6c5b 100644 --- a/tests/Unit/Subscription/Subscriber/MetadataSubscriberAccessorTest.php +++ b/tests/Unit/Subscription/Subscriber/MetadataSubscriberAccessorTest.php @@ -11,9 +11,13 @@ use Patchlevel\EventSourcing\Message\Message; use Patchlevel\EventSourcing\Metadata\Subscriber\AttributeSubscriberMetadataFactory; use Patchlevel\EventSourcing\Subscription\RunMode; +use Patchlevel\EventSourcing\Subscription\Subscriber\ArgumentResolver\EventArgumentResolver; use Patchlevel\EventSourcing\Subscription\Subscriber\ArgumentResolver\MessageArgumentResolver; use Patchlevel\EventSourcing\Subscription\Subscriber\MetadataSubscriberAccessor; +use Patchlevel\EventSourcing\Subscription\Subscriber\NoSuitableResolver; use Patchlevel\EventSourcing\Tests\Unit\Fixture\ProfileCreated; +use Patchlevel\EventSourcing\Tests\Unit\Fixture\ProfileId; +use Patchlevel\EventSourcing\Tests\Unit\Fixture\ProfileVisited; use PHPUnit\Framework\TestCase; /** @covers \Patchlevel\EventSourcing\Subscription\Subscriber\MetadataSubscriberAccessor */ @@ -68,9 +72,12 @@ public function testSubscribeMethod(): void { $subscriber = new #[Subscriber('profile', RunMode::FromBeginning)] class { - #[Subscribe(ProfileCreated::class)] - public function onProfileCreated(Message $message): void + public Message|null $message = null; + + #[Subscribe(ProfileVisited::class)] + public function onProfileVisited(Message $message): void { + $this->message = $message; } }; @@ -82,11 +89,15 @@ public function onProfileCreated(Message $message): void ], ); - $result = $accessor->subscribeMethods(ProfileCreated::class); + $result = $accessor->subscribeMethods(ProfileVisited::class); - self::assertEquals([ - $subscriber->onProfileCreated(...), - ], $result); + self::assertArrayHasKey(0, $result); + + $message = new Message(new ProfileVisited(ProfileId::fromString('1'))); + + $result[0]($message); + + self::assertSame($message, $subscriber->message); } public function testMultipleSubscribeMethod(): void @@ -114,6 +125,8 @@ public function onFoo(Message $message): void $result = $accessor->subscribeMethods(ProfileCreated::class); + self::assertCount(2, $result); + self::assertEquals([ $subscriber->onProfileCreated(...), $subscriber->onFoo(...), @@ -124,9 +137,12 @@ public function testSubscribeAllMethod(): void { $subscriber = new #[Subscriber('profile', RunMode::FromBeginning)] class { + public Message|null $message = null; + #[Subscribe('*')] - public function onProfileCreated(Message $message): void + public function on(Message $message): void { + $this->message = $message; } }; @@ -138,11 +154,69 @@ public function onProfileCreated(Message $message): void ], ); - $result = $accessor->subscribeMethods(ProfileCreated::class); + $result = $accessor->subscribeMethods(ProfileVisited::class); - self::assertEquals([ - $subscriber->onProfileCreated(...), - ], $result); + self::assertArrayHasKey(0, $result); + + $message = new Message(new ProfileVisited(ProfileId::fromString('1'))); + + $result[0]($message); + + self::assertSame($message, $subscriber->message); + } + + public function testNoResolver(): void + { + $this->expectException(NoSuitableResolver::class); + + $subscriber = new #[Subscriber('profile', RunMode::FromBeginning)] + class { + #[Subscribe(ProfileVisited::class)] + public function on(Message $message): void + { + } + }; + + $accessor = new MetadataSubscriberAccessor( + $subscriber, + (new AttributeSubscriberMetadataFactory())->metadata($subscriber::class), + [], + ); + + $accessor->subscribeMethods(ProfileVisited::class); + } + + public function testMultipleResolver(): void + { + $subscriber = new #[Subscriber('profile', RunMode::FromBeginning)] + class { + public Message|null $message = null; + + #[Subscribe(ProfileVisited::class)] + public function on(Message $message): void + { + $this->message = $message; + } + }; + + $accessor = new MetadataSubscriberAccessor( + $subscriber, + (new AttributeSubscriberMetadataFactory())->metadata($subscriber::class), + [ + new EventArgumentResolver(), + new MessageArgumentResolver(), + ], + ); + + $result = $accessor->subscribeMethods(ProfileVisited::class); + + self::assertArrayHasKey(0, $result); + + $message = new Message(new ProfileVisited(ProfileId::fromString('1'))); + + $result[0]($message); + + self::assertSame($message, $subscriber->message); } public function testSetupMethod(): void