diff --git a/infection.json.dist b/infection.json.dist index e8a9af1a6..b60949ec1 100644 --- a/infection.json.dist +++ b/infection.json.dist @@ -16,7 +16,7 @@ "mutators": { "@default": true }, - "minMsi": 72, + "minMsi": 87, "minCoveredMsi": 87, "testFrameworkOptions": "--testsuite=unit" } diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 2ee97190e..e33de6824 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -156,18 +156,18 @@ parameters: count: 1 path: src/Subscription/Store/DoctrineSubscriptionStore.php + - + message: '#^Trait Patchlevel\\EventSourcing\\Subscription\\Subscriber\\SubscriberUtil is used zero times and is not analysed\.$#' + identifier: trait.unused + count: 1 + path: src/Subscription/Subscriber/SubscriberUtil.php + - message: '#^Cannot cast mixed to string\.$#' identifier: cast.string count: 3 path: src/Subscription/ThrowableToErrorContextTransformer.php - - - message: '#^Property Patchlevel\\EventSourcing\\Tests\\Benchmark\\BasicImplementation\\ProfileWithCommands\:\:\$id is never read, only written\.$#' - identifier: property.onlyWritten - count: 1 - path: tests/Benchmark/BasicImplementation/ProfileWithCommands.php - - message: '#^Cannot access offset ''name'' on array\\|false\.$#' identifier: offsetAccess.nonOffsetAccessible @@ -463,7 +463,7 @@ parameters: path: tests/Unit/Message/Translator/ReplaceEventTranslatorTest.php - - message: '#^Method class@anonymous/tests/Unit/Metadata/Subscriber/AttributeSubscriberMetadataFactoryTest\.php\:212\:\:profileVisited\(\) has parameter \$message with no type specified\.$#' + message: '#^Method class@anonymous/tests/Unit/Metadata/Subscriber/AttributeSubscriberMetadataFactoryTest\.php\:290\:\:profileVisited\(\) has parameter \$message with no type specified\.$#' identifier: missingType.parameter count: 1 path: tests/Unit/Metadata/Subscriber/AttributeSubscriberMetadataFactoryTest.php diff --git a/src/Attribute/Processor.php b/src/Attribute/Processor.php index d79af61df..f6d67c7a5 100644 --- a/src/Attribute/Processor.php +++ b/src/Attribute/Processor.php @@ -11,7 +11,7 @@ final class Processor extends Subscriber { public function __construct( - string $id, + string|null $id = null, string $group = 'processor', RunMode $runMode = RunMode::FromNow, ) { diff --git a/src/Attribute/Projector.php b/src/Attribute/Projector.php index c344f4b71..cb1e7aadc 100644 --- a/src/Attribute/Projector.php +++ b/src/Attribute/Projector.php @@ -11,7 +11,7 @@ final class Projector extends Subscriber { public function __construct( - string $id, + string|null $id = null, string $group = 'projector', RunMode $runMode = RunMode::FromBeginning, ) { diff --git a/src/Attribute/Subscriber.php b/src/Attribute/Subscriber.php index 7df0fc1da..faff49994 100644 --- a/src/Attribute/Subscriber.php +++ b/src/Attribute/Subscriber.php @@ -11,7 +11,7 @@ class Subscriber { public function __construct( - public readonly string $id, + public readonly string|null $id, public readonly RunMode $runMode, public readonly string $group = 'default', ) { diff --git a/src/Attribute/SubscriptionId.php b/src/Attribute/SubscriptionId.php new file mode 100644 index 000000000..86a61ec4e --- /dev/null +++ b/src/Attribute/SubscriptionId.php @@ -0,0 +1,12 @@ +id; + + foreach ($reflector->getReflectionConstants() as $constant) { + if (!$constant->getAttributes(SubscriptionId::class)) { + continue; + } + + $subscriptionIdValue = $constant->getValue(); + + if (!is_string($subscriptionIdValue)) { + throw new SubscriptionIdWrongType($subscriber, $subscriptionIdValue); + } + + if ($subscriptionId !== null) { + throw new SubscriberHasMultipleSubscriptionIds( + $subscriber, + [$subscriptionId, $subscriptionIdValue], + ); + } + + $subscriptionId = $subscriptionIdValue; + } + + if ($subscriptionId === null) { + throw new SubscriptionIdMissing($subscriber); + } + $metadata = new SubscriberMetadata( - $subscriberInfo->id, + $subscriptionId, $subscriberInfo->group, $subscriberInfo->runMode, $subscribeMethods, diff --git a/src/Metadata/Subscriber/SubscriberHasMultipleSubscriptionIds.php b/src/Metadata/Subscriber/SubscriberHasMultipleSubscriptionIds.php new file mode 100644 index 000000000..5e2ebacca --- /dev/null +++ b/src/Metadata/Subscriber/SubscriberHasMultipleSubscriptionIds.php @@ -0,0 +1,28 @@ + $ids + */ + public function __construct(string $subscriberClass, array $ids) + { + parent::__construct( + sprintf( + 'There are multiple subscription ids (%s) defined for the subscriber "%s".', + $subscriberClass, + implode(', ', $ids), + ), + ); + } +} diff --git a/src/Metadata/Subscriber/SubscriptionIdMissing.php b/src/Metadata/Subscriber/SubscriptionIdMissing.php new file mode 100644 index 000000000..8953db34a --- /dev/null +++ b/src/Metadata/Subscriber/SubscriptionIdMissing.php @@ -0,0 +1,23 @@ + */ private array $nameChanged = []; @@ -30,20 +33,23 @@ public function __construct( #[Setup] public function create(): void { - $this->connection->executeStatement("CREATE TABLE IF NOT EXISTS {$this->table()} (id VARCHAR PRIMARY KEY, name VARCHAR);"); + $this->connection->executeStatement(sprintf( + 'CREATE TABLE IF NOT EXISTS %s (id VARCHAR PRIMARY KEY, name VARCHAR);', + self::TABLE_NAME, + )); } #[Teardown] public function drop(): void { - $this->connection->executeStatement("DROP TABLE IF EXISTS {$this->table()};"); + $this->connection->executeStatement(sprintf('DROP TABLE IF EXISTS %s;', self::TABLE_NAME)); } #[Subscribe(ProfileCreated::class)] public function onProfileCreated(ProfileCreated $profileCreated): void { $this->connection->insert( - $this->table(), + self::TABLE_NAME, [ 'id' => $profileCreated->profileId->toString(), 'name' => $profileCreated->name, @@ -57,11 +63,6 @@ public function onNameChanged(NameChanged $nameChanged): void $this->nameChanged[$nameChanged->profileId->toString()] = $nameChanged->name; } - public function table(): string - { - return 'projection_' . $this->subscriberId(); - } - public function beginBatch(): void { $this->nameChanged = []; @@ -73,7 +74,7 @@ public function commitBatch(): void $this->connection->transactional(function (): void { foreach ($this->nameChanged as $profileId => $name) { $this->connection->update( - $this->table(), + self::TABLE_NAME, ['name' => $name], ['id' => $profileId], ); diff --git a/tests/Benchmark/BasicImplementation/Projection/ProfileProjector.php b/tests/Benchmark/BasicImplementation/Projection/ProfileProjector.php index 98f16b3c2..566d9b5a2 100644 --- a/tests/Benchmark/BasicImplementation/Projection/ProfileProjector.php +++ b/tests/Benchmark/BasicImplementation/Projection/ProfileProjector.php @@ -9,16 +9,19 @@ use Patchlevel\EventSourcing\Attribute\Projector; use Patchlevel\EventSourcing\Attribute\Setup; use Patchlevel\EventSourcing\Attribute\Subscribe; +use Patchlevel\EventSourcing\Attribute\SubscriptionId; use Patchlevel\EventSourcing\Attribute\Teardown; -use Patchlevel\EventSourcing\Subscription\Subscriber\SubscriberUtil; use Patchlevel\EventSourcing\Tests\Benchmark\BasicImplementation\Events\NameChanged; use Patchlevel\EventSourcing\Tests\Benchmark\BasicImplementation\Events\ProfileCreated; use Patchlevel\EventSourcing\Tests\Benchmark\BasicImplementation\Query\QueryProfileName; +use function sprintf; + #[Projector('profile')] final class ProfileProjector { - use SubscriberUtil; + #[SubscriptionId] + private const TABLE_NAME = 'projection_profile'; public function __construct( private Connection $connection, @@ -28,20 +31,23 @@ public function __construct( #[Setup] public function create(): void { - $this->connection->executeStatement("CREATE TABLE IF NOT EXISTS {$this->table()} (id VARCHAR PRIMARY KEY, name VARCHAR);"); + $this->connection->executeStatement(sprintf( + 'CREATE TABLE IF NOT EXISTS %s (id VARCHAR PRIMARY KEY, name VARCHAR);', + self::TABLE_NAME, + )); } #[Teardown] public function drop(): void { - $this->connection->executeStatement("DROP TABLE IF EXISTS {$this->table()};"); + $this->connection->executeStatement(sprintf('DROP TABLE IF EXISTS %s;', self::TABLE_NAME)); } #[Subscribe(ProfileCreated::class)] public function onProfileCreated(ProfileCreated $profileCreated): void { $this->connection->insert( - $this->table(), + self::TABLE_NAME, [ 'id' => $profileCreated->profileId->toString(), 'name' => $profileCreated->name, @@ -53,7 +59,7 @@ public function onProfileCreated(ProfileCreated $profileCreated): void public function onNameChanged(NameChanged $nameChanged): void { $this->connection->update( - $this->table(), + self::TABLE_NAME, ['name' => $nameChanged->name], ['id' => $nameChanged->profileId->toString()], ); @@ -63,13 +69,8 @@ public function onNameChanged(NameChanged $nameChanged): void public function getProfileName(QueryProfileName $queryProfileName): string { return $this->connection->fetchAssociative( - "SELECT name FROM {$this->table()} WHERE id = :id;", + sprintf('SELECT name FROM %s WHERE id = :id;', self::TABLE_NAME), ['id' => $queryProfileName->id->toString()], )['name']; } - - public function table(): string - { - return 'projection_' . $this->subscriberId(); - } } diff --git a/tests/Integration/Subscription/Subscriber/LookupSubscriber.php b/tests/Integration/Subscription/Subscriber/LookupSubscriber.php index 491e65b19..561058341 100644 --- a/tests/Integration/Subscription/Subscriber/LookupSubscriber.php +++ b/tests/Integration/Subscription/Subscriber/LookupSubscriber.php @@ -9,20 +9,21 @@ use Patchlevel\EventSourcing\Attribute\Setup; use Patchlevel\EventSourcing\Attribute\Subscribe; use Patchlevel\EventSourcing\Attribute\Subscriber; +use Patchlevel\EventSourcing\Attribute\SubscriptionId; use Patchlevel\EventSourcing\Attribute\Teardown; use Patchlevel\EventSourcing\Message\Message; use Patchlevel\EventSourcing\Message\Reducer; use Patchlevel\EventSourcing\Subscription\Lookup\Lookup; use Patchlevel\EventSourcing\Subscription\RunMode; -use Patchlevel\EventSourcing\Subscription\Subscriber\SubscriberUtil; use Patchlevel\EventSourcing\Tests\Integration\Subscription\Events\AdminPromoted; use Patchlevel\EventSourcing\Tests\Integration\Subscription\Events\NameChanged; use Patchlevel\EventSourcing\Tests\Integration\Subscription\Events\ProfileCreated; -#[Subscriber('lookup', RunMode::FromBeginning)] +#[Subscriber(null, runMode: RunMode::FromBeginning)] final class LookupSubscriber { - use SubscriberUtil; + #[SubscriptionId] + private const TABLE_NAME = 'projection_lookup'; public function __construct( private Connection $connection, @@ -51,7 +52,7 @@ public function onAdminPromoted(AdminPromoted $event, Lookup $lookup): void ->reduce($messages); $this->connection->insert( - $this->tableName(), + self::TABLE_NAME, [ 'id' => $event->profileId->toString(), 'name' => $state['name'], @@ -62,7 +63,7 @@ public function onAdminPromoted(AdminPromoted $event, Lookup $lookup): void #[Setup] public function create(): void { - $table = new Table($this->tableName()); + $table = new Table(self::TABLE_NAME); $table->addColumn('id', 'string')->setLength(36); $table->addColumn('name', 'string')->setLength(255); $table->setPrimaryKey(['id']); @@ -73,11 +74,6 @@ public function create(): void #[Teardown] public function drop(): void { - $this->connection->createSchemaManager()->dropTable($this->tableName()); - } - - private function tableName(): string - { - return 'projection_' . $this->subscriberId(); + $this->connection->createSchemaManager()->dropTable(self::TABLE_NAME); } } diff --git a/tests/Integration/Subscription/Subscriber/ProfileNewProjection.php b/tests/Integration/Subscription/Subscriber/ProfileNewProjection.php index f2bae566c..8c41e9c0c 100644 --- a/tests/Integration/Subscription/Subscriber/ProfileNewProjection.php +++ b/tests/Integration/Subscription/Subscriber/ProfileNewProjection.php @@ -9,14 +9,15 @@ use Patchlevel\EventSourcing\Attribute\Projector; use Patchlevel\EventSourcing\Attribute\Setup; use Patchlevel\EventSourcing\Attribute\Subscribe; +use Patchlevel\EventSourcing\Attribute\SubscriptionId; use Patchlevel\EventSourcing\Attribute\Teardown; -use Patchlevel\EventSourcing\Subscription\Subscriber\SubscriberUtil; use Patchlevel\EventSourcing\Tests\Integration\Subscription\Events\ProfileCreated; -#[Projector('profile_2')] +#[Projector] final class ProfileNewProjection { - use SubscriberUtil; + #[SubscriptionId] + private const TABLE_NAME = 'projection_profile_2'; public function __construct( private Connection $connection, @@ -26,7 +27,7 @@ public function __construct( #[Setup] public function create(): void { - $table = new Table($this->tableName()); + $table = new Table(self::TABLE_NAME); $table->addColumn('id', 'string')->setLength(36); $table->addColumn('firstname', 'string')->setLength(255); $table->setPrimaryKey(['id']); @@ -37,23 +38,18 @@ public function create(): void #[Teardown] public function drop(): void { - $this->connection->createSchemaManager()->dropTable($this->tableName()); + $this->connection->createSchemaManager()->dropTable(self::TABLE_NAME); } #[Subscribe(ProfileCreated::class)] public function handleProfileCreated(ProfileCreated $profileCreated): void { $this->connection->executeStatement( - 'INSERT INTO ' . $this->tableName() . ' (id, firstname) VALUES(:id, :firstname);', + 'INSERT INTO ' . self::TABLE_NAME . ' (id, firstname) VALUES(:id, :firstname);', [ 'id' => $profileCreated->profileId->toString(), 'firstname' => $profileCreated->name, ], ); } - - private function tableName(): string - { - return 'projection_' . $this->subscriberId(); - } } diff --git a/tests/Integration/Subscription/Subscriber/ProfileProjection.php b/tests/Integration/Subscription/Subscriber/ProfileProjection.php index 0ecf43e3e..c3aa43a67 100644 --- a/tests/Integration/Subscription/Subscriber/ProfileProjection.php +++ b/tests/Integration/Subscription/Subscriber/ProfileProjection.php @@ -9,15 +9,16 @@ use Patchlevel\EventSourcing\Attribute\Projector; use Patchlevel\EventSourcing\Attribute\Setup; use Patchlevel\EventSourcing\Attribute\Subscribe; +use Patchlevel\EventSourcing\Attribute\SubscriptionId; use Patchlevel\EventSourcing\Attribute\Teardown; use Patchlevel\EventSourcing\Subscription\Subscriber\BatchableSubscriber; -use Patchlevel\EventSourcing\Subscription\Subscriber\SubscriberUtil; use Patchlevel\EventSourcing\Tests\Integration\Subscription\Events\ProfileCreated; -#[Projector('profile_1')] +#[Projector] final class ProfileProjection implements BatchableSubscriber { - use SubscriberUtil; + #[SubscriptionId] + private const TABLE_NAME = 'projection_profile_1'; public function __construct( private Connection $connection, @@ -27,7 +28,7 @@ public function __construct( #[Setup] public function create(): void { - $table = new Table($this->tableName()); + $table = new Table(self::TABLE_NAME); $table->addColumn('id', 'string')->setLength(36); $table->addColumn('name', 'string')->setLength(255); $table->setPrimaryKey(['id']); @@ -38,14 +39,14 @@ public function create(): void #[Teardown] public function drop(): void { - $this->connection->createSchemaManager()->dropTable($this->tableName()); + $this->connection->createSchemaManager()->dropTable(self::TABLE_NAME); } #[Subscribe(ProfileCreated::class)] public function handleProfileCreated(ProfileCreated $profileCreated): void { $this->connection->executeStatement( - 'INSERT INTO ' . $this->tableName() . ' (id, name) VALUES(:id, :name);', + 'INSERT INTO ' . self::TABLE_NAME . ' (id, name) VALUES(:id, :name);', [ 'id' => $profileCreated->profileId->toString(), 'name' => $profileCreated->name, @@ -53,11 +54,6 @@ public function handleProfileCreated(ProfileCreated $profileCreated): void ); } - private function tableName(): string - { - return 'projection_' . $this->subscriberId(); - } - public function beginBatch(): void { $this->connection->beginTransaction(); diff --git a/tests/Integration/Subscription/SubscriptionTest.php b/tests/Integration/Subscription/SubscriptionTest.php index 9da41172f..87f2e2928 100644 --- a/tests/Integration/Subscription/SubscriptionTest.php +++ b/tests/Integration/Subscription/SubscriptionTest.php @@ -112,7 +112,7 @@ public function testHappyPath(): void self::assertEquals( [ new Subscription( - 'profile_1', + 'projection_profile_1', 'projector', lastSavedAt: new DateTimeImmutable('2021-01-01T00:00:00'), ), @@ -132,7 +132,7 @@ public function testHappyPath(): void self::assertEquals( [ new Subscription( - 'profile_1', + 'projection_profile_1', 'projector', RunMode::FromBeginning, Status::Active, @@ -154,7 +154,7 @@ public function testHappyPath(): void self::assertEquals( [ new Subscription( - 'profile_1', + 'projection_profile_1', 'projector', RunMode::FromBeginning, Status::Active, @@ -181,7 +181,7 @@ public function testHappyPath(): void self::assertEquals( [ new Subscription( - 'profile_1', + 'projection_profile_1', 'projector', RunMode::FromBeginning, Status::New, @@ -238,7 +238,7 @@ public function testGapResolver(): void self::assertEquals( [ new Subscription( - 'profile_1', + 'projection_profile_1', 'projector', lastSavedAt: new DateTimeImmutable('2021-01-01T00:00:00'), ), @@ -258,7 +258,7 @@ public function testGapResolver(): void self::assertEquals( [ new Subscription( - 'profile_1', + 'projection_profile_1', 'projector', RunMode::FromBeginning, Status::Active, @@ -280,7 +280,7 @@ public function testGapResolver(): void self::assertEquals( [ new Subscription( - 'profile_1', + 'projection_profile_1', 'projector', RunMode::FromBeginning, Status::Active, @@ -307,7 +307,7 @@ public function testGapResolver(): void self::assertEquals( [ new Subscription( - 'profile_1', + 'projection_profile_1', 'projector', RunMode::FromBeginning, Status::New, @@ -853,7 +853,7 @@ public function testBlueGreenDeployment(): void self::assertEquals( [ new Subscription( - 'profile_1', + 'projection_profile_1', 'projector', RunMode::FromBeginning, Status::Active, @@ -873,7 +873,7 @@ public function testBlueGreenDeployment(): void self::assertEquals( [ new Subscription( - 'profile_1', + 'projection_profile_1', 'projector', RunMode::FromBeginning, Status::Active, @@ -898,7 +898,7 @@ public function testBlueGreenDeployment(): void self::assertEquals( [ new Subscription( - 'profile_1', + 'projection_profile_1', 'projector', RunMode::FromBeginning, Status::Active, @@ -906,7 +906,7 @@ public function testBlueGreenDeployment(): void lastSavedAt: new DateTimeImmutable('2021-01-01T00:00:00'), ), new Subscription( - 'profile_2', + 'projection_profile_2', 'projector', RunMode::FromBeginning, Status::Active, @@ -924,7 +924,7 @@ public function testBlueGreenDeployment(): void self::assertEquals( [ new Subscription( - 'profile_1', + 'projection_profile_1', 'projector', RunMode::FromBeginning, Status::Detached, @@ -932,7 +932,7 @@ public function testBlueGreenDeployment(): void lastSavedAt: new DateTimeImmutable('2021-01-01T00:00:00'), ), new Subscription( - 'profile_2', + 'projection_profile_2', 'projector', RunMode::FromBeginning, Status::Active, @@ -950,7 +950,7 @@ public function testBlueGreenDeployment(): void self::assertEquals( [ new Subscription( - 'profile_2', + 'projection_profile_2', 'projector', RunMode::FromBeginning, Status::Active, @@ -1009,7 +1009,7 @@ public function testBlueGreenDeploymentRollback(): void self::assertEquals( [ new Subscription( - 'profile_1', + 'projection_profile_1', 'projector', RunMode::FromBeginning, Status::Active, @@ -1029,7 +1029,7 @@ public function testBlueGreenDeploymentRollback(): void self::assertEquals( [ new Subscription( - 'profile_1', + 'projection_profile_1', 'projector', RunMode::FromBeginning, Status::Active, @@ -1054,7 +1054,7 @@ public function testBlueGreenDeploymentRollback(): void self::assertEquals( [ new Subscription( - 'profile_1', + 'projection_profile_1', 'projector', RunMode::FromBeginning, Status::Active, @@ -1062,7 +1062,7 @@ public function testBlueGreenDeploymentRollback(): void lastSavedAt: new DateTimeImmutable('2021-01-01T00:00:00'), ), new Subscription( - 'profile_2', + 'projection_profile_2', 'projector', RunMode::FromBeginning, Status::Active, @@ -1080,7 +1080,7 @@ public function testBlueGreenDeploymentRollback(): void self::assertEquals( [ new Subscription( - 'profile_1', + 'projection_profile_1', 'projector', RunMode::FromBeginning, Status::Detached, @@ -1088,7 +1088,7 @@ public function testBlueGreenDeploymentRollback(): void lastSavedAt: new DateTimeImmutable('2021-01-01T00:00:00'), ), new Subscription( - 'profile_2', + 'projection_profile_2', 'projector', RunMode::FromBeginning, Status::Active, @@ -1107,7 +1107,7 @@ public function testBlueGreenDeploymentRollback(): void self::assertEquals( [ new Subscription( - 'profile_1', + 'projection_profile_1', 'projector', RunMode::FromBeginning, Status::Detached, @@ -1115,7 +1115,7 @@ public function testBlueGreenDeploymentRollback(): void lastSavedAt: new DateTimeImmutable('2021-01-01T00:00:00'), ), new Subscription( - 'profile_2', + 'projection_profile_2', 'projector', RunMode::FromBeginning, Status::Active, @@ -1129,7 +1129,7 @@ public function testBlueGreenDeploymentRollback(): void // reactivating detached subscription $firstEngine->reactivate(new SubscriptionEngineCriteria( - ids: ['profile_1'], + ids: ['projection_profile_1'], )); // switch traffic @@ -1139,7 +1139,7 @@ public function testBlueGreenDeploymentRollback(): void self::assertEquals( [ new Subscription( - 'profile_1', + 'projection_profile_1', 'projector', RunMode::FromBeginning, Status::Active, @@ -1147,7 +1147,7 @@ public function testBlueGreenDeploymentRollback(): void lastSavedAt: new DateTimeImmutable('2021-01-01T00:00:00'), ), new Subscription( - 'profile_2', + 'projection_profile_2', 'projector', RunMode::FromBeginning, Status::Detached, @@ -1165,7 +1165,7 @@ public function testBlueGreenDeploymentRollback(): void self::assertEquals( [ new Subscription( - 'profile_1', + 'projection_profile_1', 'projector', RunMode::FromBeginning, Status::Active, diff --git a/tests/Unit/Metadata/Subscriber/AttributeSubscriberMetadataFactoryTest.php b/tests/Unit/Metadata/Subscriber/AttributeSubscriberMetadataFactoryTest.php index cf33e29bb..a0b03b88e 100644 --- a/tests/Unit/Metadata/Subscriber/AttributeSubscriberMetadataFactoryTest.php +++ b/tests/Unit/Metadata/Subscriber/AttributeSubscriberMetadataFactoryTest.php @@ -9,6 +9,7 @@ use Patchlevel\EventSourcing\Attribute\Setup; use Patchlevel\EventSourcing\Attribute\Subscribe; use Patchlevel\EventSourcing\Attribute\Subscriber; +use Patchlevel\EventSourcing\Attribute\SubscriptionId; use Patchlevel\EventSourcing\Attribute\Teardown; use Patchlevel\EventSourcing\Message\Message; use Patchlevel\EventSourcing\Metadata\Subscriber\ArgumentMetadata; @@ -19,6 +20,9 @@ use Patchlevel\EventSourcing\Metadata\Subscriber\DuplicateSubscribeMethod; use Patchlevel\EventSourcing\Metadata\Subscriber\DuplicateTeardownMethod; use Patchlevel\EventSourcing\Metadata\Subscriber\SubscribeMethodMetadata; +use Patchlevel\EventSourcing\Metadata\Subscriber\SubscriberHasMultipleSubscriptionIds; +use Patchlevel\EventSourcing\Metadata\Subscriber\SubscriptionIdMissing; +use Patchlevel\EventSourcing\Metadata\Subscriber\SubscriptionIdWrongType; use Patchlevel\EventSourcing\Subscription\RunMode; use Patchlevel\EventSourcing\Tests\Unit\Fixture\ProfileCreated; use Patchlevel\EventSourcing\Tests\Unit\Fixture\ProfileVisited; @@ -55,6 +59,80 @@ class { self::assertSame('foo', $metadata->id); } + public function testSubscriberWithoutId(): void + { + $subscriber = new #[Subscriber(null, RunMode::FromBeginning)] + class { + }; + + $metadataFactory = new AttributeSubscriberMetadataFactory(); + + $this->expectException(SubscriptionIdMissing::class); + $metadataFactory->metadata($subscriber::class); + } + + public function testSubscriberWithSubscriptionIdAsConst(): void + { + $subscriber = new #[Subscriber(null, RunMode::FromBeginning)] + class { + #[SubscriptionId] + public const NAME = 'my_name'; + }; + + $metadataFactory = new AttributeSubscriberMetadataFactory(); + $metadata = $metadataFactory->metadata($subscriber::class); + + self::assertSame([], $metadata->subscribeMethods); + self::assertNull($metadata->setupMethod); + self::assertNull($metadata->teardownMethod); + self::assertSame('my_name', $metadata->id); + } + + public function testSubscriberWithMultipleSubscriptionIdsSubscriberAndConst(): void + { + $subscriber = new #[Subscriber('foo', RunMode::FromBeginning)] + class { + #[SubscriptionId] + public const NAME = 'my_name'; + }; + + $metadataFactory = new AttributeSubscriberMetadataFactory(); + + $this->expectException(SubscriberHasMultipleSubscriptionIds::class); + $metadataFactory->metadata($subscriber::class); + } + + public function testSubscriberWithMultipleSubscriptionIdsInConsts(): void + { + $subscriber = new #[Subscriber(null, RunMode::FromBeginning)] + class { + #[SubscriptionId] + public const NAME = 'my_name'; + + #[SubscriptionId] + public const NAME_AGAIN = 'my_name_2'; + }; + + $metadataFactory = new AttributeSubscriberMetadataFactory(); + + $this->expectException(SubscriberHasMultipleSubscriptionIds::class); + $metadataFactory->metadata($subscriber::class); + } + + public function testSubscriberWithSubscriptionIdAsInteger(): void + { + $subscriber = new #[Subscriber(null, RunMode::FromBeginning)] + class { + #[SubscriptionId] + public const NAME = 123; + }; + + $metadataFactory = new AttributeSubscriberMetadataFactory(); + + $this->expectException(SubscriptionIdWrongType::class); + $metadataFactory->metadata($subscriber::class); + } + public function testProjector(): void { $subscriber = new #[Projector('foo')] diff --git a/tests/Unit/Subscription/Subscriber/SubscriberHelperTest.php b/tests/Unit/Subscription/Subscriber/SubscriberHelperTest.php deleted file mode 100644 index 8220e0786..000000000 --- a/tests/Unit/Subscription/Subscriber/SubscriberHelperTest.php +++ /dev/null @@ -1,26 +0,0 @@ -subscriberId($subscriber)); - } -}