From da53eede6c66a8d6cfc0c08b46f58627c6e81c43 Mon Sep 17 00:00:00 2001 From: Daniel Badura Date: Mon, 5 May 2025 11:26:12 +0200 Subject: [PATCH 1/2] Remove prophecy, change tests to use phpunit native mocking --- phpstan-baseline.neon | 48 - tests/ReturnCallback.php | 26 + tests/Unit/Aggregate/UuidTest.php | 12 +- .../AggregateHandlerProviderTest.php | 19 +- .../CommandBus/ChainHandlerProviderTest.php | 26 +- .../Handler/CreateAggregateHandlerTest.php | 30 +- .../Handler/DefaultParameterResolverTest.php | 44 +- .../Handler/UpdateAggregateHandlerTest.php | 24 +- tests/Unit/CommandBus/HandlerFinderTest.php | 5 +- .../CommandBus/ServiceHandlerProviderTest.php | 3 - tests/Unit/CommandBus/SyncCommandBusTest.php | 21 +- .../Command/DatabaseCreateCommandTest.php | 49 +- .../Command/DatabaseDropCommandTest.php | 61 +- .../Unit/Console/Command/DebugCommandTest.php | 3 - .../Command/SchemaCreateCommandTest.php | 19 +- .../Console/Command/SchemaDropCommandTest.php | 25 +- .../Command/SchemaUpdateCommandTest.php | 25 +- .../Command/ShowAggregateCommandTest.php | 137 +- .../Unit/Console/Command/WatchCommandTest.php | 19 +- tests/Unit/Console/DoctrineHelperTest.php | 55 +- tests/Unit/Console/OutputStyleTest.php | 30 +- tests/Unit/EventBus/DefaultConsumerTest.php | 13 +- tests/Unit/EventBus/DefaultEventBusTest.php | 35 +- tests/Unit/EventBus/Psr14EventBusTest.php | 17 +- tests/Unit/Message/PipeTest.php | 3 - tests/Unit/Message/ReducerTest.php | 3 - .../DefaultHeadersSerializerTest.php | 3 - .../AggregateToStreamHeaderTranslatorTest.php | 3 - .../Translator/ChainTranslatorTest.php | 35 +- .../QueryBus/ChainHandlerProviderTest.php | 15 +- tests/Unit/QueryBus/HandlerFinderTest.php | 5 +- .../QueryBus/ServiceHandlerProviderTest.php | 3 - tests/Unit/QueryBus/SyncQueryBusTest.php | 25 +- .../DefaultRepositoryManagerTest.php | 27 +- .../Unit/Repository/DefaultRepositoryTest.php | 694 ++++---- .../ChainMessageDecoratorTest.php | 15 +- .../SplitStreamDecoratorTest.php | 3 - .../ChainDoctrineSchemaConfiguratorTest.php | 19 +- .../DoctrineMigrationSchemaProviderTest.php | 9 +- .../Schema/DoctrineSchemaDirectorTest.php | 344 ++-- .../Schema/DoctrineSchemaListenerTest.php | 17 +- .../Schema/DoctrineSchemaSubscriberTest.php | 21 +- .../Normalizer/IdNormalizerTest.php | 3 - .../Adapter/Psr16SnapshotAdapterTest.php | 21 +- .../Adapter/Psr6SnapshotAdapterTest.php | 37 +- .../Snapshot/ArrayAdapterRepositoryTest.php | 9 +- .../Snapshot/DefaultSnapshotStoreTest.php | 56 +- tests/Unit/Store/ArrayStreamTest.php | 3 - tests/Unit/Store/DoctrineDbalStoreTest.php | 1565 +++++++++-------- tests/Unit/Store/DoctrineDbalStreamTest.php | 196 ++- tests/Unit/Store/InMemoryStoreTest.php | 3 - tests/Unit/Store/ReadOnlyStoreTest.php | 31 +- .../Store/StreamDoctrineDbalStoreTest.php | 1318 +++++++------- .../Store/StreamDoctrineDbalStreamTest.php | 196 ++- tests/Unit/Store/StreamReadOnlyStoreTest.php | 45 +- .../Engine/CatchUpSubscriptionEngineTest.php | 83 +- .../Engine/DefaultSubscriptionEngineTest.php | 920 ++++++---- .../GapResolverStoreMessageLoaderTest.php | 299 ++-- .../Engine/SubscriptionManagerTest.php | 79 +- .../ThrowOnErrorSubscriptionEngineTest.php | 93 +- tests/Unit/Subscription/Lookup/LookupTest.php | 106 +- ...ubscriptionEngineRepositoryManagerTest.php | 15 +- .../RunSubscriptionEngineRepositoryTest.php | 47 +- .../ArgumentResolver/LookupResolverTest.php | 11 +- 64 files changed, 3686 insertions(+), 3440 deletions(-) create mode 100644 tests/ReturnCallback.php diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 3df0cf30..514dfbe7 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -330,24 +330,6 @@ parameters: count: 1 path: tests/Unit/CommandBus/Handler/CreateAggregateHandlerTest.php - - - message: '#^Method class@anonymous/tests/Unit/CommandBus/Handler/DefaultParameterResolverTest\.php\:69\:\:handle\(\) has parameter \$foo with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: tests/Unit/CommandBus/Handler/DefaultParameterResolverTest.php - - - - message: '#^Method class@anonymous/tests/Unit/CommandBus/Handler/DefaultParameterResolverTest\.php\:95\:\:handle\(\) has parameter \$foo with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: tests/Unit/CommandBus/Handler/DefaultParameterResolverTest.php - - - - message: '#^Method class@anonymous/tests/Unit/CommandBus/HandlerFinderTest\.php\:40\:\:handle\(\) has parameter \$command with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: tests/Unit/CommandBus/HandlerFinderTest.php - - message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertTrue\(\) with true will always evaluate to true\.$#' identifier: method.alreadyNarrowedType @@ -462,12 +444,6 @@ parameters: count: 1 path: tests/Unit/Metadata/Subscriber/AttributeSubscriberMetadataFactoryTest.php - - - message: '#^Method class@anonymous/tests/Unit/QueryBus/HandlerFinderTest\.php\:40\:\:handle\(\) has parameter \$query with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: tests/Unit/QueryBus/HandlerFinderTest.php - - message: '#^Cannot access offset 0 on iterable\\.$#' identifier: offsetAccess.nonOffsetAccessible @@ -480,18 +456,6 @@ parameters: count: 2 path: tests/Unit/QueryBus/ServiceHandlerProviderTest.php - - - message: '#^Trying to invoke mixed but it''s not a callable\.$#' - identifier: callable.nonCallable - count: 15 - path: tests/Unit/Store/DoctrineDbalStoreTest.php - - - - message: '#^Trying to invoke mixed but it''s not a callable\.$#' - identifier: callable.nonCallable - count: 13 - path: tests/Unit/Store/StreamDoctrineDbalStoreTest.php - - message: '#^Match expression does not handle remaining value\: string$#' identifier: match.unhandled @@ -504,18 +468,6 @@ parameters: count: 8 path: tests/Unit/Subscription/Engine/DefaultSubscriptionEngineTest.php - - - message: '#^Trying to invoke mixed but it''s not a callable\.$#' - identifier: callable.nonCallable - count: 2 - path: tests/Unit/Subscription/Engine/DefaultSubscriptionEngineTest.php - - - - message: '#^Trying to invoke mixed but it''s not a callable\.$#' - identifier: callable.nonCallable - count: 1 - path: tests/Unit/Subscription/Engine/SubscriptionManagerTest.php - - message: '#^Offset ''args'' on array\{file\: literal\-string&non\-falsy\-string, line\: int, function\: ''createException'', class\: ''Patchlevel\\\\EventSourcing\\\\Tests\\\\Unit\\\\Subscription\\\\ErrorContextTest'', type\: ''\-\>'', args\: array\\} on left side of \?\? always exists and is not nullable\.$#' identifier: nullCoalesce.offset diff --git a/tests/ReturnCallback.php b/tests/ReturnCallback.php new file mode 100644 index 00000000..82cfe247 --- /dev/null +++ b/tests/ReturnCallback.php @@ -0,0 +1,26 @@ +, mixed} $series */ + public function __construct( + private array $series, + ) { + } + + public function __invoke(mixed ...$args): mixed + { + [$expectedArgs, $return] = array_shift($this->series); + Assert::assertEquals($expectedArgs, $args); + + return $return; + } +} diff --git a/tests/Unit/Aggregate/UuidTest.php b/tests/Unit/Aggregate/UuidTest.php index 9d039f36..744ef4d0 100644 --- a/tests/Unit/Aggregate/UuidTest.php +++ b/tests/Unit/Aggregate/UuidTest.php @@ -32,9 +32,15 @@ public function uuid7(DateTimeInterface|null $dateTime = null): UuidInterface } }; - RamseyUuid::setFactory($factory); - $id = Uuid::generate(); + $oldFactory = RamseyUuid::getFactory(); - self::assertSame('018d6a97-6aba-7104-825f-67313a77a2a4', $id->toString()); + try { + RamseyUuid::setFactory($factory); + $id = Uuid::generate(); + + self::assertSame('018d6a97-6aba-7104-825f-67313a77a2a4', $id->toString()); + } finally { + RamseyUuid::setFactory($oldFactory); + } } } diff --git a/tests/Unit/CommandBus/AggregateHandlerProviderTest.php b/tests/Unit/CommandBus/AggregateHandlerProviderTest.php index 990f2b96..5db923df 100644 --- a/tests/Unit/CommandBus/AggregateHandlerProviderTest.php +++ b/tests/Unit/CommandBus/AggregateHandlerProviderTest.php @@ -15,20 +15,17 @@ use Patchlevel\EventSourcing\Tests\Unit\Fixture\ProfileWithHandler; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; #[CoversClass(AggregateHandlerProvider::class)] final class AggregateHandlerProviderTest extends TestCase { - use ProphecyTrait; - public function testEmpty(): void { - $repositoryManager = $this->prophesize(RepositoryManager::class); + $repositoryManager = $this->createMock(RepositoryManager::class); $provider = new AggregateHandlerProvider( new AggregateRootRegistry([]), - $repositoryManager->reveal(), + $repositoryManager, ); $result = $provider->handlerForCommand(CreateProfile::class); @@ -38,17 +35,17 @@ public function testEmpty(): void public function testGetCreateHandler(): void { - $repositoryManager = $this->prophesize(RepositoryManager::class); + $repositoryManager = $this->createMock(RepositoryManager::class); $provider = new AggregateHandlerProvider( new AggregateRootRegistry(['profile' => ProfileWithHandler::class]), - $repositoryManager->reveal(), + $repositoryManager, ); $result = $provider->handlerForCommand(CreateProfile::class); $handler = new CreateAggregateHandler( - $repositoryManager->reveal(), + $repositoryManager, ProfileWithHandler::class, 'create', new DefaultParameterResolver(), @@ -60,17 +57,17 @@ public function testGetCreateHandler(): void public function testGetUpdateHandler(): void { - $repositoryManager = $this->prophesize(RepositoryManager::class); + $repositoryManager = $this->createMock(RepositoryManager::class); $provider = new AggregateHandlerProvider( new AggregateRootRegistry(['profile' => ProfileWithHandler::class]), - $repositoryManager->reveal(), + $repositoryManager, ); $result = $provider->handlerForCommand(ChangeProfileName::class); $handler = new UpdateAggregateHandler( - $repositoryManager->reveal(), + $repositoryManager, ProfileWithHandler::class, 'updateName', new DefaultParameterResolver(), diff --git a/tests/Unit/CommandBus/ChainHandlerProviderTest.php b/tests/Unit/CommandBus/ChainHandlerProviderTest.php index b5d18894..5823c41b 100644 --- a/tests/Unit/CommandBus/ChainHandlerProviderTest.php +++ b/tests/Unit/CommandBus/ChainHandlerProviderTest.php @@ -10,13 +10,10 @@ use Patchlevel\EventSourcing\Tests\Unit\Fixture\CreateProfile; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; #[CoversClass(ChainHandlerProvider::class)] final class ChainHandlerProviderTest extends TestCase { - use ProphecyTrait; - public function testEmpty(): void { $provider = new ChainHandlerProvider([]); @@ -32,18 +29,23 @@ public function testFindHandler(): void $handler2 = new HandlerDescriptor(static fn () => null); $handler3 = new HandlerDescriptor(static fn () => null); - $provider1 = $this->prophesize(HandlerProvider::class); - $provider1->handlerForCommand(CreateProfile::class)->willReturn([ - $handler1, - $handler2, - ]); + $provider1 = $this->createMock(HandlerProvider::class); + $provider1 + ->method('handlerForCommand') + ->with(CreateProfile::class) + ->willReturn( + [ + $handler1, + $handler2, + ], + ); - $provider2 = $this->prophesize(HandlerProvider::class); - $provider2->handlerForCommand(CreateProfile::class)->willReturn([$handler3]); + $provider2 = $this->createMock(HandlerProvider::class); + $provider2->method('handlerForCommand')->with(CreateProfile::class)->willReturn([$handler3]); $chainProvider = new ChainHandlerProvider([ - $provider1->reveal(), - $provider2->reveal(), + $provider1, + $provider2, ]); $result = $chainProvider->handlerForCommand(CreateProfile::class); diff --git a/tests/Unit/CommandBus/Handler/CreateAggregateHandlerTest.php b/tests/Unit/CommandBus/Handler/CreateAggregateHandlerTest.php index 06b6c1f0..b969f2ed 100644 --- a/tests/Unit/CommandBus/Handler/CreateAggregateHandlerTest.php +++ b/tests/Unit/CommandBus/Handler/CreateAggregateHandlerTest.php @@ -14,27 +14,21 @@ use Patchlevel\EventSourcing\Tests\Unit\Fixture\ProfileWithHandler; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Prophecy\Argument; -use Prophecy\PhpUnit\ProphecyTrait; #[CoversClass(CreateAggregateHandler::class)] final class CreateAggregateHandlerTest extends TestCase { - use ProphecyTrait; - public function testSuccess(): void { - $repository = $this->prophesize(Repository::class); - $repository->save(Argument::type(ProfileWithHandler::class))->shouldBeCalled(); + $repository = $this->createMock(Repository::class); + $repository->expects($this->atLeastOnce())->method('save')->with($this->isInstanceOf(ProfileWithHandler::class)); - $repositoryManager = $this->prophesize(RepositoryManager::class); - $repositoryManager - ->get(ProfileWithHandler::class) - ->willReturn($repository->reveal()) - ->shouldBeCalled(); + $repositoryManager = $this->createMock(RepositoryManager::class); + $repositoryManager->expects($this->atLeastOnce())->method('get')->with(ProfileWithHandler::class) + ->willReturn($repository); $handler = new CreateAggregateHandler( - $repositoryManager->reveal(), + $repositoryManager, ProfileWithHandler::class, 'create', new DefaultParameterResolver(), @@ -57,16 +51,14 @@ public static function create(): string } }; - $repository = $this->prophesize(Repository::class); + $repository = $this->createMock(Repository::class); - $repositoryManager = $this->prophesize(RepositoryManager::class); - $repositoryManager - ->get($class::class) - ->willReturn($repository->reveal()) - ->shouldBeCalled(); + $repositoryManager = $this->createMock(RepositoryManager::class); + $repositoryManager->expects($this->atLeastOnce())->method('get')->with($class::class) + ->willReturn($repository); $handler = new CreateAggregateHandler( - $repositoryManager->reveal(), + $repositoryManager, $class::class, 'create', new DefaultParameterResolver(), diff --git a/tests/Unit/CommandBus/Handler/DefaultParameterResolverTest.php b/tests/Unit/CommandBus/Handler/DefaultParameterResolverTest.php index 623e3322..7f2cad66 100644 --- a/tests/Unit/CommandBus/Handler/DefaultParameterResolverTest.php +++ b/tests/Unit/CommandBus/Handler/DefaultParameterResolverTest.php @@ -10,7 +10,6 @@ use Patchlevel\EventSourcing\CommandBus\ServiceNotFound; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; use Psr\Container\ContainerInterface; use ReflectionMethod; use stdClass; @@ -18,8 +17,6 @@ #[CoversClass(DefaultParameterResolver::class)] final class DefaultParameterResolverTest extends TestCase { - use ProphecyTrait; - public function testNoParameters(): void { $class = new class () { @@ -68,7 +65,7 @@ public function testMissingContainer(): void $class = new class () { // phpcs:disable - public function handle(stdClass $command, $foo): void + public function handle(stdClass $command, mixed $foo): void { } // phpcs:enable @@ -94,15 +91,15 @@ public function testNoType(): void $class = new class () { // phpcs:disable - public function handle(stdClass $command, $foo): void + public function handle(stdClass $command, mixed $foo): void { } // phpcs:enable }; - $container = $this->prophesize(ContainerInterface::class); + $container = $this->createMock(ContainerInterface::class); - $resolver = new DefaultParameterResolver($container->reveal()); + $resolver = new DefaultParameterResolver($container); $command = new stdClass(); @@ -126,9 +123,9 @@ public function handle(stdClass $command, string $foo): void } }; - $container = $this->prophesize(ContainerInterface::class); + $container = $this->createMock(ContainerInterface::class); - $resolver = new DefaultParameterResolver($container->reveal()); + $resolver = new DefaultParameterResolver($container); $command = new stdClass(); @@ -152,10 +149,14 @@ public function handle(stdClass $command, stdClass $foo): void } }; - $container = $this->prophesize(ContainerInterface::class); - $container->get(stdClass::class)->willThrow(new ServiceNotFound(stdClass::class))->shouldBeCalledOnce(); + $container = $this->createMock(ContainerInterface::class); + $container + ->expects($this->once()) + ->method('get') + ->with(stdClass::class) + ->willThrowException(new ServiceNotFound(stdClass::class)); - $resolver = new DefaultParameterResolver($container->reveal()); + $resolver = new DefaultParameterResolver($container); $command = new stdClass(); @@ -179,10 +180,14 @@ public function handle(stdClass $command, stdClass $foo): void $service = new stdClass(); - $container = $this->prophesize(ContainerInterface::class); - $container->get(stdClass::class)->willReturn($service)->shouldBeCalledOnce(); + $container = $this->createMock(ContainerInterface::class); + $container + ->expects($this->once()) + ->method('get') + ->with(stdClass::class) + ->willReturn($service); - $resolver = new DefaultParameterResolver($container->reveal()); + $resolver = new DefaultParameterResolver($container); $command = new stdClass(); @@ -209,10 +214,13 @@ public function handle( $service = new stdClass(); - $container = $this->prophesize(ContainerInterface::class); - $container->get('foo')->willReturn($service)->shouldBeCalledOnce(); + $container = $this->createMock(ContainerInterface::class); + $container->expects($this->once()) + ->method('get') + ->with('foo') + ->willReturn($service); - $resolver = new DefaultParameterResolver($container->reveal()); + $resolver = new DefaultParameterResolver($container); $command = new stdClass(); diff --git a/tests/Unit/CommandBus/Handler/UpdateAggregateHandlerTest.php b/tests/Unit/CommandBus/Handler/UpdateAggregateHandlerTest.php index c6566f9d..28e3138e 100644 --- a/tests/Unit/CommandBus/Handler/UpdateAggregateHandlerTest.php +++ b/tests/Unit/CommandBus/Handler/UpdateAggregateHandlerTest.php @@ -15,30 +15,28 @@ use Patchlevel\EventSourcing\Tests\Unit\Fixture\ProfileWithHandler; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; #[CoversClass(UpdateAggregateHandler::class)] final class UpdateAggregateHandlerTest extends TestCase { - use ProphecyTrait; - public function testSuccess(): void { $profileId = ProfileId::fromString('123'); $profile = ProfileWithHandler::createEmpty(); - $repository = $this->prophesize(Repository::class); - $repository->load($profileId)->willReturn($profile)->shouldBeCalled(); - $repository->save($profile)->shouldBeCalled(); + $repository = $this->createMock(Repository::class); + $repository->expects($this->atLeastOnce())->method('load')->with($profileId)->willReturn($profile); + $repository->expects($this->atLeastOnce())->method('save')->with($profile); - $repositoryManager = $this->prophesize(RepositoryManager::class); + $repositoryManager = $this->createMock(RepositoryManager::class); $repositoryManager - ->get(ProfileWithHandler::class) - ->willReturn($repository->reveal()) - ->shouldBeCalled(); + ->expects($this->atLeastOnce()) + ->method('get') + ->with(ProfileWithHandler::class) + ->willReturn($repository); $handler = new UpdateAggregateHandler( - $repositoryManager->reveal(), + $repositoryManager, ProfileWithHandler::class, 'changeName', new DefaultParameterResolver(), @@ -54,10 +52,10 @@ public function testSuccess(): void public function testMissingAggregateId(): void { - $repositoryManager = $this->prophesize(RepositoryManager::class); + $repositoryManager = $this->createMock(RepositoryManager::class); $handler = new UpdateAggregateHandler( - $repositoryManager->reveal(), + $repositoryManager, ProfileWithHandler::class, 'changeName', new DefaultParameterResolver(), diff --git a/tests/Unit/CommandBus/HandlerFinderTest.php b/tests/Unit/CommandBus/HandlerFinderTest.php index 671476b0..6a038def 100644 --- a/tests/Unit/CommandBus/HandlerFinderTest.php +++ b/tests/Unit/CommandBus/HandlerFinderTest.php @@ -11,13 +11,10 @@ use Patchlevel\EventSourcing\Tests\Unit\Fixture\CreateProfile; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; #[CoversClass(HandlerFinder::class)] final class HandlerFinderTest extends TestCase { - use ProphecyTrait; - public function testNoParameters(): void { $this->expectException(InvalidHandleMethod::class); @@ -40,7 +37,7 @@ public function testNoType(): void $class = new class () { // phpcs:disable #[Handle] - public function handle($command): void + public function handle(mixed $command): void { } // phpcs:enable diff --git a/tests/Unit/CommandBus/ServiceHandlerProviderTest.php b/tests/Unit/CommandBus/ServiceHandlerProviderTest.php index d7928476..571f874a 100644 --- a/tests/Unit/CommandBus/ServiceHandlerProviderTest.php +++ b/tests/Unit/CommandBus/ServiceHandlerProviderTest.php @@ -9,13 +9,10 @@ use Patchlevel\EventSourcing\Tests\Unit\Fixture\CreateProfile; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; #[CoversClass(ServiceHandlerProvider::class)] final class ServiceHandlerProviderTest extends TestCase { - use ProphecyTrait; - public function testEmpty(): void { $provider = new ServiceHandlerProvider([]); diff --git a/tests/Unit/CommandBus/SyncCommandBusTest.php b/tests/Unit/CommandBus/SyncCommandBusTest.php index f138abd0..4b8713af 100644 --- a/tests/Unit/CommandBus/SyncCommandBusTest.php +++ b/tests/Unit/CommandBus/SyncCommandBusTest.php @@ -11,22 +11,19 @@ use Patchlevel\EventSourcing\CommandBus\SyncCommandBus; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; #[CoversClass(SyncCommandBus::class)] final class SyncCommandBusTest extends TestCase { - use ProphecyTrait; - public function testHandlerNotFound(): void { $command = new class { }; - $handlerProvider = $this->prophesize(HandlerProvider::class); - $handlerProvider->handlerForCommand($command::class)->willReturn([]); + $handlerProvider = $this->createMock(HandlerProvider::class); + $handlerProvider->method('handlerForCommand')->with($command::class)->willReturn([]); - $commandBus = new SyncCommandBus($handlerProvider->reveal()); + $commandBus = new SyncCommandBus($handlerProvider); $this->expectException(HandlerNotFound::class); @@ -38,13 +35,13 @@ public function testMultipleHandlersFound(): void $command = new class { }; - $handlerProvider = $this->prophesize(HandlerProvider::class); - $handlerProvider->handlerForCommand($command::class)->willReturn([ + $handlerProvider = $this->createMock(HandlerProvider::class); + $handlerProvider->method('handlerForCommand')->with($command::class)->willReturn([ new HandlerDescriptor(static fn () => null), new HandlerDescriptor(static fn () => null), ]); - $commandBus = new SyncCommandBus($handlerProvider->reveal()); + $commandBus = new SyncCommandBus($handlerProvider); $this->expectException(MultipleHandlersFound::class); @@ -65,12 +62,12 @@ public function __invoke(object $command): void } }; - $handlerProvider = $this->prophesize(HandlerProvider::class); - $handlerProvider->handlerForCommand($command::class)->willReturn([ + $handlerProvider = $this->createMock(HandlerProvider::class); + $handlerProvider->method('handlerForCommand')->with($command::class)->willReturn([ new HandlerDescriptor($handler), ]); - $commandBus = new SyncCommandBus($handlerProvider->reveal()); + $commandBus = new SyncCommandBus($handlerProvider); $commandBus->dispatch($command); diff --git a/tests/Unit/Console/Command/DatabaseCreateCommandTest.php b/tests/Unit/Console/Command/DatabaseCreateCommandTest.php index e26aecc7..1f1a7070 100644 --- a/tests/Unit/Console/Command/DatabaseCreateCommandTest.php +++ b/tests/Unit/Console/Command/DatabaseCreateCommandTest.php @@ -9,7 +9,6 @@ use Patchlevel\EventSourcing\Console\DoctrineHelper; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; use RuntimeException; use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Console\Output\BufferedOutput; @@ -17,21 +16,19 @@ #[CoversClass(DatabaseCreateCommand::class)] final class DatabaseCreateCommandTest extends TestCase { - use ProphecyTrait; - public function testSuccessful(): void { - $connection = $this->prophesize(Connection::class); + $connection = $this->createMock(Connection::class); - $helper = $this->prophesize(DoctrineHelper::class); - $helper->copyConnectionWithoutDatabase($connection)->willReturn($connection); - $helper->databaseName($connection)->willReturn('test'); - $helper->hasDatabase($connection, 'test')->willReturn(false); - $helper->createDatabase($connection, 'test')->shouldBeCalled(); + $helper = $this->createMock(DoctrineHelper::class); + $helper->method('copyConnectionWithoutDatabase')->with($connection)->willReturn($connection); + $helper->method('databaseName')->with($connection)->willReturn('test'); + $helper->method('hasDatabase')->with($connection, 'test')->willReturn(false); + $helper->expects($this->atLeastOnce())->method('createDatabase')->with($connection, 'test'); $command = new DatabaseCreateCommand( - $connection->reveal(), - $helper->reveal(), + $connection, + $helper, ); $input = new ArrayInput([]); @@ -48,16 +45,16 @@ public function testSuccessful(): void public function testSkip(): void { - $connection = $this->prophesize(Connection::class); + $connection = $this->createMock(Connection::class); - $helper = $this->prophesize(DoctrineHelper::class); - $helper->copyConnectionWithoutDatabase($connection)->willReturn($connection); - $helper->databaseName($connection)->willReturn('test'); - $helper->hasDatabase($connection, 'test')->willReturn(true); + $helper = $this->createMock(DoctrineHelper::class); + $helper->method('copyConnectionWithoutDatabase')->with($connection)->willReturn($connection); + $helper->method('databaseName')->with($connection)->willReturn('test'); + $helper->method('hasDatabase')->with($connection, 'test')->willReturn(true); $command = new DatabaseCreateCommand( - $connection->reveal(), - $helper->reveal(), + $connection, + $helper, ); $input = new ArrayInput(['--if-not-exists' => true]); @@ -74,17 +71,17 @@ public function testSkip(): void public function testError(): void { - $connection = $this->prophesize(Connection::class); + $connection = $this->createMock(Connection::class); - $helper = $this->prophesize(DoctrineHelper::class); - $helper->copyConnectionWithoutDatabase($connection)->willReturn($connection); - $helper->databaseName($connection)->willReturn('test'); - $helper->hasDatabase($connection, 'test')->willReturn(false); - $helper->createDatabase($connection, 'test')->willThrow(new RuntimeException('error')); + $helper = $this->createMock(DoctrineHelper::class); + $helper->method('copyConnectionWithoutDatabase')->with($connection)->willReturn($connection); + $helper->method('databaseName')->with($connection)->willReturn('test'); + $helper->method('hasDatabase')->with($connection, 'test')->willReturn(false); + $helper->method('createDatabase')->with($connection, 'test')->willThrowException(new RuntimeException('error')); $command = new DatabaseCreateCommand( - $connection->reveal(), - $helper->reveal(), + $connection, + $helper, ); $input = new ArrayInput([]); diff --git a/tests/Unit/Console/Command/DatabaseDropCommandTest.php b/tests/Unit/Console/Command/DatabaseDropCommandTest.php index deaee5a8..03003b64 100644 --- a/tests/Unit/Console/Command/DatabaseDropCommandTest.php +++ b/tests/Unit/Console/Command/DatabaseDropCommandTest.php @@ -9,7 +9,6 @@ use Patchlevel\EventSourcing\Console\DoctrineHelper; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; use RuntimeException; use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Console\Output\BufferedOutput; @@ -17,19 +16,17 @@ #[CoversClass(DatabaseDropCommand::class)] final class DatabaseDropCommandTest extends TestCase { - use ProphecyTrait; - public function testMissingForce(): void { - $connection = $this->prophesize(Connection::class); + $connection = $this->createMock(Connection::class); - $helper = $this->prophesize(DoctrineHelper::class); - $helper->copyConnectionWithoutDatabase($connection)->willReturn($connection); - $helper->databaseName($connection)->willReturn('test'); + $helper = $this->createMock(DoctrineHelper::class); + $helper->method('copyConnectionWithoutDatabase')->with($connection)->willReturn($connection); + $helper->method('databaseName')->with($connection)->willReturn('test'); $command = new DatabaseDropCommand( - $connection->reveal(), - $helper->reveal(), + $connection, + $helper, ); $input = new ArrayInput([]); @@ -46,17 +43,17 @@ public function testMissingForce(): void public function testSuccessful(): void { - $connection = $this->prophesize(Connection::class); + $connection = $this->createMock(Connection::class); - $helper = $this->prophesize(DoctrineHelper::class); - $helper->copyConnectionWithoutDatabase($connection)->willReturn($connection); - $helper->hasDatabase($connection, 'test')->willReturn(true); - $helper->databaseName($connection)->willReturn('test'); - $helper->dropDatabase($connection, 'test')->shouldBeCalled(); + $helper = $this->createMock(DoctrineHelper::class); + $helper->method('copyConnectionWithoutDatabase')->with($connection)->willReturn($connection); + $helper->method('hasDatabase')->with($connection, 'test')->willReturn(true); + $helper->method('databaseName')->with($connection)->willReturn('test'); + $helper->expects($this->atLeastOnce())->method('dropDatabase')->with($connection, 'test'); $command = new DatabaseDropCommand( - $connection->reveal(), - $helper->reveal(), + $connection, + $helper, ); $input = new ArrayInput(['--force' => true]); @@ -73,16 +70,16 @@ public function testSuccessful(): void public function testSkip(): void { - $connection = $this->prophesize(Connection::class); + $connection = $this->createMock(Connection::class); - $helper = $this->prophesize(DoctrineHelper::class); - $helper->copyConnectionWithoutDatabase($connection)->willReturn($connection); - $helper->databaseName($connection)->willReturn('test'); - $helper->hasDatabase($connection, 'test')->willReturn(false); + $helper = $this->createMock(DoctrineHelper::class); + $helper->method('copyConnectionWithoutDatabase')->with($connection)->willReturn($connection); + $helper->method('databaseName')->with($connection)->willReturn('test'); + $helper->method('hasDatabase')->with($connection, 'test')->willReturn(false); $command = new DatabaseDropCommand( - $connection->reveal(), - $helper->reveal(), + $connection, + $helper, ); $input = new ArrayInput(['--force' => true, '--if-exists' => true]); @@ -99,17 +96,17 @@ public function testSkip(): void public function testError(): void { - $connection = $this->prophesize(Connection::class); + $connection = $this->createMock(Connection::class); - $helper = $this->prophesize(DoctrineHelper::class); - $helper->copyConnectionWithoutDatabase($connection)->willReturn($connection); - $helper->hasDatabase($connection, 'test')->willReturn(true); - $helper->databaseName($connection)->willReturn('test'); - $helper->dropDatabase($connection, 'test')->willThrow(new RuntimeException('error')); + $helper = $this->createMock(DoctrineHelper::class); + $helper->method('copyConnectionWithoutDatabase')->with($connection)->willReturn($connection); + $helper->method('hasDatabase')->with($connection, 'test')->willReturn(true); + $helper->method('databaseName')->with($connection)->willReturn('test'); + $helper->method('dropDatabase')->with($connection, 'test')->willThrowException(new RuntimeException('error')); $command = new DatabaseDropCommand( - $connection->reveal(), - $helper->reveal(), + $connection, + $helper, ); $input = new ArrayInput(['--force' => true]); diff --git a/tests/Unit/Console/Command/DebugCommandTest.php b/tests/Unit/Console/Command/DebugCommandTest.php index 730b9e5a..723e7947 100644 --- a/tests/Unit/Console/Command/DebugCommandTest.php +++ b/tests/Unit/Console/Command/DebugCommandTest.php @@ -16,15 +16,12 @@ use Patchlevel\EventSourcing\Tests\Unit\Fixture\ProfileCreated; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Console\Output\BufferedOutput; #[CoversClass(DebugCommand::class)] final class DebugCommandTest extends TestCase { - use ProphecyTrait; - public function testSuccessful(): void { $command = new DebugCommand( diff --git a/tests/Unit/Console/Command/SchemaCreateCommandTest.php b/tests/Unit/Console/Command/SchemaCreateCommandTest.php index feaf0c22..aec1619e 100644 --- a/tests/Unit/Console/Command/SchemaCreateCommandTest.php +++ b/tests/Unit/Console/Command/SchemaCreateCommandTest.php @@ -9,22 +9,19 @@ use Patchlevel\EventSourcing\Schema\SchemaDirector; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Console\Output\BufferedOutput; #[CoversClass(SchemaCreateCommand::class)] final class SchemaCreateCommandTest extends TestCase { - use ProphecyTrait; - public function testSuccessful(): void { - $schemaManager = $this->prophesize(SchemaDirector::class); - $schemaManager->create()->shouldBeCalled(); + $schemaManager = $this->createMock(SchemaDirector::class); + $schemaManager->expects($this->atLeastOnce())->method('create'); $command = new SchemaCreateCommand( - $schemaManager->reveal(), + $schemaManager, ); $input = new ArrayInput([]); @@ -41,15 +38,15 @@ public function testSuccessful(): void public function testDryRun(): void { - $schemaManager = $this->prophesize(DryRunSchemaDirector::class); - $schemaManager->dryRunCreate()->willReturn([ + $schemaManager = $this->createMock(DryRunSchemaDirector::class); + $schemaManager->method('dryRunCreate')->willReturn([ 'create table 1;', 'create table 2;', 'create table 3;', ]); $command = new SchemaCreateCommand( - $schemaManager->reveal(), + $schemaManager, ); $input = new ArrayInput(['--dry-run' => true]); @@ -69,10 +66,10 @@ public function testDryRun(): void public function testDryRunNotSupported(): void { - $schemaManager = $this->prophesize(SchemaDirector::class); + $schemaManager = $this->createMock(SchemaDirector::class); $command = new SchemaCreateCommand( - $schemaManager->reveal(), + $schemaManager, ); $input = new ArrayInput(['--dry-run' => true]); diff --git a/tests/Unit/Console/Command/SchemaDropCommandTest.php b/tests/Unit/Console/Command/SchemaDropCommandTest.php index cd4656ae..91a0ad76 100644 --- a/tests/Unit/Console/Command/SchemaDropCommandTest.php +++ b/tests/Unit/Console/Command/SchemaDropCommandTest.php @@ -9,22 +9,19 @@ use Patchlevel\EventSourcing\Schema\SchemaDirector; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Console\Output\BufferedOutput; #[CoversClass(SchemaDropCommand::class)] final class SchemaDropCommandTest extends TestCase { - use ProphecyTrait; - public function testSuccessful(): void { - $schemaManager = $this->prophesize(SchemaDirector::class); - $schemaManager->drop()->shouldBeCalled(); + $schemaManager = $this->createMock(SchemaDirector::class); + $schemaManager->expects($this->atLeastOnce())->method('drop'); $command = new SchemaDropCommand( - $schemaManager->reveal(), + $schemaManager, ); $input = new ArrayInput(['--force' => true]); @@ -41,11 +38,11 @@ public function testSuccessful(): void public function testMissingForce(): void { - $schemaManager = $this->prophesize(SchemaDirector::class); - $schemaManager->drop()->shouldNotBeCalled(); + $schemaManager = $this->createMock(SchemaDirector::class); + $schemaManager->expects($this->never())->method('drop'); $command = new SchemaDropCommand( - $schemaManager->reveal(), + $schemaManager, ); $input = new ArrayInput([]); @@ -65,15 +62,15 @@ public function testMissingForce(): void public function testDryRun(): void { - $schemaManager = $this->prophesize(DryRunSchemaDirector::class); - $schemaManager->dryRunDrop()->willReturn([ + $schemaManager = $this->createMock(DryRunSchemaDirector::class); + $schemaManager->method('dryRunDrop')->willReturn([ 'drop table 1;', 'drop table 2;', 'drop table 3;', ]); $command = new SchemaDropCommand( - $schemaManager->reveal(), + $schemaManager, ); $input = new ArrayInput(['--dry-run' => true]); @@ -93,10 +90,10 @@ public function testDryRun(): void public function testDryRunNotSupported(): void { - $schemaManager = $this->prophesize(SchemaDirector::class); + $schemaManager = $this->createMock(SchemaDirector::class); $command = new SchemaDropCommand( - $schemaManager->reveal(), + $schemaManager, ); $input = new ArrayInput(['--dry-run' => true]); diff --git a/tests/Unit/Console/Command/SchemaUpdateCommandTest.php b/tests/Unit/Console/Command/SchemaUpdateCommandTest.php index c8471a72..60659882 100644 --- a/tests/Unit/Console/Command/SchemaUpdateCommandTest.php +++ b/tests/Unit/Console/Command/SchemaUpdateCommandTest.php @@ -9,22 +9,19 @@ use Patchlevel\EventSourcing\Schema\SchemaDirector; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Console\Output\BufferedOutput; #[CoversClass(SchemaUpdateCommand::class)] final class SchemaUpdateCommandTest extends TestCase { - use ProphecyTrait; - public function testSuccessful(): void { - $schemaManager = $this->prophesize(SchemaDirector::class); - $schemaManager->update()->shouldBeCalled(); + $schemaManager = $this->createMock(SchemaDirector::class); + $schemaManager->expects($this->atLeastOnce())->method('update'); $command = new SchemaUpdateCommand( - $schemaManager->reveal(), + $schemaManager, ); $input = new ArrayInput(['--force' => true]); @@ -41,11 +38,11 @@ public function testSuccessful(): void public function testMissingForce(): void { - $schemaManager = $this->prophesize(SchemaDirector::class); - $schemaManager->update()->shouldNotBeCalled(); + $schemaManager = $this->createMock(SchemaDirector::class); + $schemaManager->expects($this->never())->method('update'); $command = new SchemaUpdateCommand( - $schemaManager->reveal(), + $schemaManager, ); $input = new ArrayInput([]); @@ -65,15 +62,15 @@ public function testMissingForce(): void public function testDryRun(): void { - $schemaManager = $this->prophesize(DryRunSchemaDirector::class); - $schemaManager->dryRunUpdate()->willReturn([ + $schemaManager = $this->createMock(DryRunSchemaDirector::class); + $schemaManager->method('dryRunUpdate')->willReturn([ 'update table 1;', 'update table 2;', 'update table 3;', ]); $command = new SchemaUpdateCommand( - $schemaManager->reveal(), + $schemaManager, ); $input = new ArrayInput(['--dry-run' => true]); @@ -92,10 +89,10 @@ public function testDryRun(): void public function testDryRunNotSupported(): void { - $schemaManager = $this->prophesize(SchemaDirector::class); + $schemaManager = $this->createMock(SchemaDirector::class); $command = new SchemaUpdateCommand( - $schemaManager->reveal(), + $schemaManager, ); $input = new ArrayInput(['--dry-run' => true]); diff --git a/tests/Unit/Console/Command/ShowAggregateCommandTest.php b/tests/Unit/Console/Command/ShowAggregateCommandTest.php index 1a32da42..1d95b4c9 100644 --- a/tests/Unit/Console/Command/ShowAggregateCommandTest.php +++ b/tests/Unit/Console/Command/ShowAggregateCommandTest.php @@ -24,7 +24,6 @@ use Patchlevel\EventSourcing\Tests\Unit\Fixture\ProfileVisited; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; use Symfony\Component\Console\Exception\MissingInputException; use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Console\Output\BufferedOutput; @@ -33,46 +32,50 @@ #[CoversClass(ShowAggregateCommand::class)] final class ShowAggregateCommandTest extends TestCase { - use ProphecyTrait; - public function testSuccessful(): void { $event = new ProfileVisited(ProfileId::fromString('1')); $message = Message::create($event) ->withHeader(new AggregateHeader('profile', '1', 1, new DateTimeImmutable())); - $store = $this->prophesize(Store::class); - $store->load(new Criteria( - new AggregateNameCriterion('profile'), - new AggregateIdCriterion('1'), - ))->willReturn( - new ArrayStream([$message]), - ); - - $serializer = $this->prophesize(EventSerializer::class); - $serializer->serialize($event, [Encoder::OPTION_PRETTY_PRINT => true])->willReturn( - new SerializedEvent( - 'profile.visited', - '{"visitorId": "1"}', - ), - ); - - $headersSerializer = $this->prophesize(HeadersSerializer::class); - $headersSerializer->serialize($message->headers())->willReturn( - [ - 'aggregate' => [ - 'aggregateName' => 'profile', - 'aggregateId' => '1', - 'playhead' => 1, - 'recordedOn' => '2020-01-01T20:00:00+01:00', - ], - ], + $store = $this->createMock(Store::class); + $store + ->method('load') + ->with(new Criteria( + new AggregateNameCriterion('profile'), + new AggregateIdCriterion('1'), + )) + ->willReturn(new ArrayStream([$message])); + + $serializer = $this->createMock(EventSerializer::class); + $serializer + ->method('serialize') + ->with($event, [Encoder::OPTION_PRETTY_PRINT => true]) + ->willReturn( + new SerializedEvent( + 'profile.visited', + '{"visitorId": "1"}', + ), + ); + + $headersSerializer = $this->createMock(HeadersSerializer::class); + $headersSerializer->method('serialize')->with($message->headers())->willReturn( + <<<'JSON' +{ + "aggregate": { + "aggregateName": "profile", + "aggregateId": "1", + "playhead": 1, + "recordedOn": "2020-01-01T20:00:00+01:00" + } +} +JSON, ); $command = new ShowAggregateCommand( - $store->reveal(), - $serializer->reveal(), - $headersSerializer->reveal(), + $store, + $serializer, + $headersSerializer, new AggregateRootRegistry(['profile' => Profile::class]), ); @@ -94,13 +97,13 @@ public function testSuccessful(): void public function testAggregateNotAString(): void { - $store = $this->prophesize(Store::class); - $serializer = $this->prophesize(EventSerializer::class); + $store = $this->createMock(Store::class); + $serializer = $this->createMock(EventSerializer::class); $command = new ShowAggregateCommand( - $store->reveal(), - $serializer->reveal(), - $this->prophesize(HeadersSerializer::class)->reveal(), + $store, + $serializer, + $this->createMock(HeadersSerializer::class), new AggregateRootRegistry(['profile' => Profile::class]), ); @@ -117,13 +120,13 @@ public function testAggregateNotAString(): void public function testIdNotAString(): void { - $store = $this->prophesize(Store::class); - $serializer = $this->prophesize(EventSerializer::class); + $store = $this->createMock(Store::class); + $serializer = $this->createMock(EventSerializer::class); $command = new ShowAggregateCommand( - $store->reveal(), - $serializer->reveal(), - $this->prophesize(HeadersSerializer::class)->reveal(), + $store, + $serializer, + $this->createMock(HeadersSerializer::class), new AggregateRootRegistry(['profile' => Profile::class]), ); @@ -140,13 +143,13 @@ public function testIdNotAString(): void public function testWrongAggregate(): void { - $store = $this->prophesize(Store::class); - $serializer = $this->prophesize(EventSerializer::class); + $store = $this->createMock(Store::class); + $serializer = $this->createMock(EventSerializer::class); $command = new ShowAggregateCommand( - $store->reveal(), - $serializer->reveal(), - $this->prophesize(HeadersSerializer::class)->reveal(), + $store, + $serializer, + $this->createMock(HeadersSerializer::class), new AggregateRootRegistry(['profile' => Profile::class]), ); @@ -168,18 +171,18 @@ public function testWrongAggregate(): void public function testNotFound(): void { - $store = $this->prophesize(Store::class); - $store->load(new Criteria( + $store = $this->createMock(Store::class); + $store->method('load')->with(new Criteria( new AggregateNameCriterion('profile'), new AggregateIdCriterion('test'), ))->willReturn(new ArrayStream()); - $serializer = $this->prophesize(EventSerializer::class); + $serializer = $this->createMock(EventSerializer::class); $command = new ShowAggregateCommand( - $store->reveal(), - $serializer->reveal(), - $this->prophesize(HeadersSerializer::class)->reveal(), + $store, + $serializer, + $this->createMock(HeadersSerializer::class), new AggregateRootRegistry(['profile' => Profile::class]), ); @@ -203,9 +206,9 @@ public function testInteractiveMissingAggregateShouldRaiseException(): void { $commandTest = new CommandTester( new ShowAggregateCommand( - $this->prophesize(Store::class)->reveal(), - $this->prophesize(EventSerializer::class)->reveal(), - $this->prophesize(HeadersSerializer::class)->reveal(), + $this->createMock(Store::class), + $this->createMock(EventSerializer::class), + $this->createMock(HeadersSerializer::class), new AggregateRootRegistry(['test' => Profile::class]), ), ); @@ -218,9 +221,9 @@ public function testInteractiveMissingIdShouldRaiseException(): void { $commandTest = new CommandTester( new ShowAggregateCommand( - $this->prophesize(Store::class)->reveal(), - $this->prophesize(EventSerializer::class)->reveal(), - $this->prophesize(HeadersSerializer::class)->reveal(), + $this->createMock(Store::class), + $this->createMock(EventSerializer::class), + $this->createMock(HeadersSerializer::class), new AggregateRootRegistry(['test' => Profile::class]), ), ); @@ -238,29 +241,29 @@ public function testInteractiveSuccessful(): void $message = Message::create($event) ->withHeader(new AggregateHeader('profile', '1', 1, new DateTimeImmutable())); - $store = $this->prophesize(Store::class); - $store->load(new Criteria( + $store = $this->createMock(Store::class); + $store->method('load')->with(new Criteria( new AggregateNameCriterion('profile'), new AggregateIdCriterion('1'), ))->willReturn( new ArrayStream([$message]), ); - $eventSerializer = $this->prophesize(EventSerializer::class); - $eventSerializer->serialize($event, [Encoder::OPTION_PRETTY_PRINT => true])->willReturn( + $eventSerializer = $this->createMock(EventSerializer::class); + $eventSerializer->method('serialize')->with($event, [Encoder::OPTION_PRETTY_PRINT => true])->willReturn( new SerializedEvent( 'profile.visited', '{"visitorId": "1"}', ), ); - $headersSerializer = $this->prophesize(HeadersSerializer::class); + $headersSerializer = $this->createMock(HeadersSerializer::class); $commandTest = new CommandTester( new ShowAggregateCommand( - $store->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $store, + $eventSerializer, + $headersSerializer, new AggregateRootRegistry(['profile' => Profile::class]), ), ); diff --git a/tests/Unit/Console/Command/WatchCommandTest.php b/tests/Unit/Console/Command/WatchCommandTest.php index de9eb9b9..f10ddeca 100644 --- a/tests/Unit/Console/Command/WatchCommandTest.php +++ b/tests/Unit/Console/Command/WatchCommandTest.php @@ -10,7 +10,6 @@ use Patchlevel\EventSourcing\Store\InMemoryStore; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Tester\CommandTester; @@ -19,21 +18,19 @@ #[CoversClass(WatchCommand::class)] final class WatchCommandTest extends TestCase { - use ProphecyTrait; - public function testSuccessfulWithLogger(): void { $store = new InMemoryStore(); - $serializer = $this->prophesize(EventSerializer::class); + $serializer = $this->createMock(EventSerializer::class); - $headersSerializer = $this->prophesize(HeadersSerializer::class); + $headersSerializer = $this->createMock(HeadersSerializer::class); $commandTest = new CommandTester( new WatchCommand( $store, - $serializer->reveal(), - $headersSerializer->reveal(), + $serializer, + $headersSerializer, ), ); @@ -55,15 +52,15 @@ public function testMaxIterationReached(): void { $store = new InMemoryStore(); - $serializer = $this->prophesize(EventSerializer::class); + $serializer = $this->createMock(EventSerializer::class); - $headersSerializer = $this->prophesize(HeadersSerializer::class); + $headersSerializer = $this->createMock(HeadersSerializer::class); $commandTest = new CommandTester( new WatchCommand( $store, - $serializer->reveal(), - $headersSerializer->reveal(), + $serializer, + $headersSerializer, ), ); diff --git a/tests/Unit/Console/DoctrineHelperTest.php b/tests/Unit/Console/DoctrineHelperTest.php index 2ab021ec..3d89398f 100644 --- a/tests/Unit/Console/DoctrineHelperTest.php +++ b/tests/Unit/Console/DoctrineHelperTest.php @@ -11,31 +11,28 @@ use Patchlevel\EventSourcing\Console\DoctrineHelper; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; #[CoversClass(DoctrineHelper::class)] final class DoctrineHelperTest extends TestCase { - use ProphecyTrait; - public function testDatabaseNameWithPath(): void { $helper = new DoctrineHelper(); - $connection = $this->prophesize(Connection::class); - $connection->getParams()->willReturn(['path' => 'test']); + $connection = $this->createMock(Connection::class); + $connection->method('getParams')->willReturn(['path' => 'test']); - self::assertSame('test', $helper->databaseName($connection->reveal())); + self::assertSame('test', $helper->databaseName($connection)); } public function testDatabaseNameWithDatabaseName(): void { $helper = new DoctrineHelper(); - $connection = $this->prophesize(Connection::class); - $connection->getParams()->willReturn(['dbname' => 'test']); + $connection = $this->createMock(Connection::class); + $connection->method('getParams')->willReturn(['dbname' => 'test']); - self::assertSame('test', $helper->databaseName($connection->reveal())); + self::assertSame('test', $helper->databaseName($connection)); } public function testDatabaseNameThrowException(): void @@ -44,50 +41,50 @@ public function testDatabaseNameThrowException(): void $helper = new DoctrineHelper(); - $connection = $this->prophesize(Connection::class); - $connection->getParams()->willReturn([]); + $connection = $this->createMock(Connection::class); + $connection->method('getParams')->willReturn([]); - $helper->databaseName($connection->reveal()); + $helper->databaseName($connection); } public function testHasDatabase(): void { $helper = new DoctrineHelper(); - $schemaManager = $this->prophesize(AbstractSchemaManager::class); - $schemaManager->listDatabases()->willReturn(['test']); + $schemaManager = $this->createMock(AbstractSchemaManager::class); + $schemaManager->method('listDatabases')->willReturn(['test']); - $connection = $this->prophesize(Connection::class); - $connection->createSchemaManager()->willReturn($schemaManager); + $connection = $this->createMock(Connection::class); + $connection->method('createSchemaManager')->willReturn($schemaManager); - self::assertSame(true, $helper->hasDatabase($connection->reveal(), 'test')); + self::assertSame(true, $helper->hasDatabase($connection, 'test')); } public function testCreateDatabase(): void { $helper = new DoctrineHelper(); - $schemaManager = $this->prophesize(AbstractSchemaManager::class); - $schemaManager->createDatabase('`test`')->shouldBeCalled(); + $schemaManager = $this->createMock(AbstractSchemaManager::class); + $schemaManager->expects($this->atLeastOnce())->method('createDatabase')->with('`test`'); - $connection = $this->prophesize(Connection::class); - $connection->getDatabasePlatform()->willReturn(new MySQLPlatform()); - $connection->createSchemaManager()->willReturn($schemaManager); + $connection = $this->createMock(Connection::class); + $connection->method('getDatabasePlatform')->willReturn(new MySQLPlatform()); + $connection->method('createSchemaManager')->willReturn($schemaManager); - $helper->createDatabase($connection->reveal(), 'test'); + $helper->createDatabase($connection, 'test'); } public function testDropDatabase(): void { $helper = new DoctrineHelper(); - $schemaManager = $this->prophesize(AbstractSchemaManager::class); - $schemaManager->dropDatabase('`test`')->shouldBeCalled(); + $schemaManager = $this->createMock(AbstractSchemaManager::class); + $schemaManager->expects($this->atLeastOnce())->method('dropDatabase')->with('`test`'); - $connection = $this->prophesize(Connection::class); - $connection->getDatabasePlatform()->willReturn(new MySQLPlatform()); - $connection->createSchemaManager()->willReturn($schemaManager); + $connection = $this->createMock(Connection::class); + $connection->method('getDatabasePlatform')->willReturn(new MySQLPlatform()); + $connection->method('createSchemaManager')->willReturn($schemaManager); - $helper->dropDatabase($connection->reveal(), 'test'); + $helper->dropDatabase($connection, 'test'); } } diff --git a/tests/Unit/Console/OutputStyleTest.php b/tests/Unit/Console/OutputStyleTest.php index 0817e543..95dd35dd 100644 --- a/tests/Unit/Console/OutputStyleTest.php +++ b/tests/Unit/Console/OutputStyleTest.php @@ -18,16 +18,12 @@ use Patchlevel\EventSourcing\Tests\Unit\Fixture\ProfileId; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Prophecy\Argument; -use Prophecy\PhpUnit\ProphecyTrait; use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Console\Output\BufferedOutput; #[CoversClass(OutputStyle::class)] final class OutputStyleTest extends TestCase { - use ProphecyTrait; - public function testMessage(): void { $input = new ArrayInput([]); @@ -41,20 +37,20 @@ public function testMessage(): void $message = Message::create($event) ->withHeader(new AggregateHeader('profile', '1', 1, new DateTimeImmutable())); - $eventSerializer = $this->prophesize(EventSerializer::class); - $eventSerializer->serialize($event, [Encoder::OPTION_PRETTY_PRINT => true])->willReturn(new SerializedEvent( + $eventSerializer = $this->createMock(EventSerializer::class); + $eventSerializer->method('serialize')->with($event, [Encoder::OPTION_PRETTY_PRINT => true])->willReturn(new SerializedEvent( 'profile.created', '{"id":"1","email":"foo@bar.com"}', )); - $headersSerializer = $this->prophesize(HeadersSerializer::class); - $headersSerializer->serialize(Argument::any())->shouldNotBeCalled(); + $headersSerializer = $this->createMock(HeadersSerializer::class); + $headersSerializer->expects($this->never())->method('serialize'); $console = new OutputStyle($input, $output); $console->message( - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $eventSerializer, + $headersSerializer, $message, ); @@ -83,22 +79,22 @@ public function testMessageWithCustomHeaders(): void ->withHeader(new AggregateHeader('profile', '1', 1, new DateTimeImmutable())) ->withHeader($fooHeader); - $eventSerializer = $this->prophesize(EventSerializer::class); - $eventSerializer->serialize($event, [Encoder::OPTION_PRETTY_PRINT => true])->willReturn(new SerializedEvent( + $eventSerializer = $this->createMock(EventSerializer::class); + $eventSerializer->method('serialize')->with($event, [Encoder::OPTION_PRETTY_PRINT => true])->willReturn(new SerializedEvent( 'profile.created', '{"id":"1","email":"foo@bar.com"}', )); - $headersSerializer = $this->prophesize(HeadersSerializer::class); - $headersSerializer->serialize([$fooHeader])->willReturn( + $headersSerializer = $this->createMock(HeadersSerializer::class); + $headersSerializer->expects($this->atLeastOnce())->method('serialize')->with([$fooHeader])->willReturn( '{"aggregate":{"aggregateName":"profile","aggregateId":"1","playhead":1,"recordedOn":"2020-01-01T20:00:00+01:00"},"archived":[]}', - )->shouldBeCalled(); + ); $console = new OutputStyle($input, $output); $console->message( - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $eventSerializer, + $headersSerializer, $message, ); diff --git a/tests/Unit/EventBus/DefaultConsumerTest.php b/tests/Unit/EventBus/DefaultConsumerTest.php index 3a2b5413..563bae60 100644 --- a/tests/Unit/EventBus/DefaultConsumerTest.php +++ b/tests/Unit/EventBus/DefaultConsumerTest.php @@ -14,13 +14,10 @@ use Patchlevel\EventSourcing\Tests\Unit\Fixture\ProfileId; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; #[CoversClass(DefaultConsumer::class)] final class DefaultConsumerTest extends TestCase { - use ProphecyTrait; - public function testConsumeEvent(): void { $listener = new class { @@ -39,10 +36,14 @@ public function __invoke(Message $message): void ), ); - $provider = $this->prophesize(ListenerProvider::class); - $provider->listenersForEvent(ProfileCreated::class)->willReturn([new ListenerDescriptor($listener->__invoke(...))]); + $provider = $this->createMock(ListenerProvider::class); + $provider + ->expects($this->once()) + ->method('listenersForEvent') + ->with(ProfileCreated::class) + ->willReturn([new ListenerDescriptor($listener->__invoke(...))]); - $eventBus = new DefaultConsumer($provider->reveal()); + $eventBus = new DefaultConsumer($provider); $eventBus->consume($message); self::assertSame($message, $listener->message); diff --git a/tests/Unit/EventBus/DefaultEventBusTest.php b/tests/Unit/EventBus/DefaultEventBusTest.php index edd90da6..c71a640a 100644 --- a/tests/Unit/EventBus/DefaultEventBusTest.php +++ b/tests/Unit/EventBus/DefaultEventBusTest.php @@ -4,6 +4,7 @@ namespace Patchlevel\EventSourcing\Tests\Unit\EventBus; +use LogicException; use Patchlevel\EventSourcing\Attribute\Subscribe; use Patchlevel\EventSourcing\EventBus\Consumer; use Patchlevel\EventSourcing\EventBus\DefaultEventBus; @@ -15,13 +16,10 @@ use Patchlevel\EventSourcing\Tests\Unit\Fixture\ProfileVisited; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; #[CoversClass(DefaultEventBus::class)] final class DefaultEventBusTest extends TestCase { - use ProphecyTrait; - public function testDispatchEvent(): void { $message = new Message( @@ -31,10 +29,10 @@ public function testDispatchEvent(): void ), ); - $consumer = $this->prophesize(Consumer::class); - $consumer->consume($message)->shouldBeCalled(); + $consumer = $this->createMock(Consumer::class); + $consumer->expects($this->atLeastOnce())->method('consume')->with($message); - $eventBus = new DefaultEventBus($consumer->reveal()); + $eventBus = new DefaultEventBus($consumer); $eventBus->dispatch($message); } @@ -54,11 +52,11 @@ public function testDispatchMultipleMessages(): void ), ); - $consumer = $this->prophesize(Consumer::class); - $consumer->consume($message1)->shouldBeCalled(); - $consumer->consume($message2)->shouldBeCalled(); + $consumer = $this->createMock(Consumer::class); + $consumer->expects($this->atLeastOnce())->method('consume')->with($message1); + $consumer->expects($this->atLeastOnce())->method('consume')->with($message2); - $eventBus = new DefaultEventBus($consumer->reveal()); + $eventBus = new DefaultEventBus($consumer); $eventBus->dispatch($message1, $message2); } @@ -83,14 +81,17 @@ public function testMultipleMessagesAddingNewEventInListener(): void ), ); - $consumer = $this->prophesize(Consumer::class); - $eventBus = new DefaultEventBus($consumer->reveal()); + $consumer = $this->createMock(Consumer::class); + $eventBus = new DefaultEventBus($consumer); - $consumer->consume($messageA)->shouldBeCalled()->will(static function () use ($eventBus, $messageC): void { - $eventBus->dispatch($messageC); - }); - $consumer->consume($messageB)->shouldBeCalled(); - $consumer->consume($messageC)->shouldBeCalled(); + $consumer->expects($this->exactly(3))->method('consume')->willReturnCallback( + static fn (Message $message) => match ($message) { + $messageA => $eventBus->dispatch($messageC), + $messageB => true, + $messageC => true, + default => throw new LogicException('Unmatched case!'), + }, + ); $eventBus->dispatch($messageA, $messageB); } diff --git a/tests/Unit/EventBus/Psr14EventBusTest.php b/tests/Unit/EventBus/Psr14EventBusTest.php index 7ba35afb..9667e2ff 100644 --- a/tests/Unit/EventBus/Psr14EventBusTest.php +++ b/tests/Unit/EventBus/Psr14EventBusTest.php @@ -11,14 +11,11 @@ use Patchlevel\EventSourcing\Tests\Unit\Fixture\ProfileId; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; use Psr\EventDispatcher\EventDispatcherInterface; #[CoversClass(Psr14EventBus::class)] final class Psr14EventBusTest extends TestCase { - use ProphecyTrait; - public function testDispatchEvent(): void { $message = new Message( @@ -28,10 +25,10 @@ public function testDispatchEvent(): void ), ); - $eventDispatcher = $this->prophesize(EventDispatcherInterface::class); - $eventDispatcher->dispatch($message)->shouldBeCalled(); + $eventDispatcher = $this->createMock(EventDispatcherInterface::class); + $eventDispatcher->expects($this->atLeastOnce())->method('dispatch')->with($message); - $eventBus = new Psr14EventBus($eventDispatcher->reveal()); + $eventBus = new Psr14EventBus($eventDispatcher); $eventBus->dispatch($message); } @@ -51,11 +48,11 @@ public function testDispatchMultipleMessages(): void ), ); - $eventDispatcher = $this->prophesize(EventDispatcherInterface::class); - $eventDispatcher->dispatch($message1)->shouldBeCalled(); - $eventDispatcher->dispatch($message2)->shouldBeCalled(); + $eventDispatcher = $this->createMock(EventDispatcherInterface::class); + $eventDispatcher->expects($this->atLeastOnce())->method('dispatch')->with($message1); + $eventDispatcher->expects($this->atLeastOnce())->method('dispatch')->with($message2); - $eventBus = new Psr14EventBus($eventDispatcher->reveal()); + $eventBus = new Psr14EventBus($eventDispatcher); $eventBus->dispatch($message1, $message2); } } diff --git a/tests/Unit/Message/PipeTest.php b/tests/Unit/Message/PipeTest.php index ee6ef481..117f6eee 100644 --- a/tests/Unit/Message/PipeTest.php +++ b/tests/Unit/Message/PipeTest.php @@ -16,15 +16,12 @@ use Patchlevel\EventSourcing\Tests\Unit\Fixture\ProfileVisited; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; use function iterator_to_array; #[CoversClass(Pipe::class)] final class PipeTest extends TestCase { - use ProphecyTrait; - public function testEmpty(): void { $stream = new Pipe([]); diff --git a/tests/Unit/Message/ReducerTest.php b/tests/Unit/Message/ReducerTest.php index 34f3478b..daa8b894 100644 --- a/tests/Unit/Message/ReducerTest.php +++ b/tests/Unit/Message/ReducerTest.php @@ -14,15 +14,12 @@ use Patchlevel\EventSourcing\Tests\Unit\Fixture\ProfileVisited; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; use function count; #[CoversClass(Reducer::class)] final class ReducerTest extends TestCase { - use ProphecyTrait; - public function testEmpty(): void { $reducer = new Reducer(); diff --git a/tests/Unit/Message/Serializer/DefaultHeadersSerializerTest.php b/tests/Unit/Message/Serializer/DefaultHeadersSerializerTest.php index 408fa3d8..73d87478 100644 --- a/tests/Unit/Message/Serializer/DefaultHeadersSerializerTest.php +++ b/tests/Unit/Message/Serializer/DefaultHeadersSerializerTest.php @@ -13,13 +13,10 @@ use Patchlevel\Hydrator\MetadataHydrator; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; #[CoversClass(DefaultHeadersSerializer::class)] final class DefaultHeadersSerializerTest extends TestCase { - use ProphecyTrait; - public function testSerialize(): void { $serializer = DefaultHeadersSerializer::createFromPaths([ diff --git a/tests/Unit/Message/Translator/AggregateToStreamHeaderTranslatorTest.php b/tests/Unit/Message/Translator/AggregateToStreamHeaderTranslatorTest.php index 11f34b90..f0f6e043 100644 --- a/tests/Unit/Message/Translator/AggregateToStreamHeaderTranslatorTest.php +++ b/tests/Unit/Message/Translator/AggregateToStreamHeaderTranslatorTest.php @@ -16,13 +16,10 @@ use Patchlevel\EventSourcing\Tests\Unit\Fixture\ProfileId; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; #[CoversClass(AggregateToStreamHeaderTranslator::class)] final class AggregateToStreamHeaderTranslatorTest extends TestCase { - use ProphecyTrait; - public function testMissingHeader(): void { $message = new Message( diff --git a/tests/Unit/Message/Translator/ChainTranslatorTest.php b/tests/Unit/Message/Translator/ChainTranslatorTest.php index 9fcc173c..1a710cf8 100644 --- a/tests/Unit/Message/Translator/ChainTranslatorTest.php +++ b/tests/Unit/Message/Translator/ChainTranslatorTest.php @@ -7,18 +7,16 @@ use Patchlevel\EventSourcing\Message\Message; use Patchlevel\EventSourcing\Message\Translator\ChainTranslator; use Patchlevel\EventSourcing\Message\Translator\Translator; +use Patchlevel\EventSourcing\Tests\ReturnCallback; use Patchlevel\EventSourcing\Tests\Unit\Fixture\Email; use Patchlevel\EventSourcing\Tests\Unit\Fixture\ProfileCreated; use Patchlevel\EventSourcing\Tests\Unit\Fixture\ProfileId; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; #[CoversClass(ChainTranslator::class)] final class ChainTranslatorTest extends TestCase { - use ProphecyTrait; - public function testEmptyChain(): void { $message = new Message( @@ -68,16 +66,33 @@ public function testChain(): void ), ); - $child1 = $this->prophesize(Translator::class); - $child1->__invoke($message1)->willReturn([$message2, $message3])->shouldBeCalled(); + $child1 = $this->createMock(Translator::class); + $child1 + ->expects($this->once()) + ->method('__invoke') + ->with($message1) + ->willReturn([$message2, $message3]); - $child2 = $this->prophesize(Translator::class); - $child2->__invoke($message2)->willReturn([$message4, $message5])->shouldBeCalled(); - $child2->__invoke($message3)->willReturn([$message3])->shouldBeCalled(); + $child2 = $this->createMock(Translator::class); + $child2 + ->expects($this->exactly(2)) + ->method('__invoke') + ->willReturnCallback( + new ReturnCallback([ + [ + [$message2], + [$message4, $message5], + ], + [ + [$message3], + [$message3], + ], + ]), + ); $translator = new ChainTranslator([ - $child1->reveal(), - $child2->reveal(), + $child1, + $child2, ]); self::assertSame([$message4, $message5, $message3], $translator($message1)); diff --git a/tests/Unit/QueryBus/ChainHandlerProviderTest.php b/tests/Unit/QueryBus/ChainHandlerProviderTest.php index 0d395e28..0919c289 100644 --- a/tests/Unit/QueryBus/ChainHandlerProviderTest.php +++ b/tests/Unit/QueryBus/ChainHandlerProviderTest.php @@ -9,13 +9,10 @@ use Patchlevel\EventSourcing\QueryBus\HandlerProvider; use Patchlevel\EventSourcing\Tests\Unit\Fixture\QueryProfile; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; /** @covers \Patchlevel\EventSourcing\QueryBus\ChainHandlerProvider */ final class ChainHandlerProviderTest extends TestCase { - use ProphecyTrait; - public function testEmpty(): void { $provider = new ChainHandlerProvider([]); @@ -31,18 +28,18 @@ public function testFindHandler(): void $handler2 = new HandlerDescriptor(static fn () => null); $handler3 = new HandlerDescriptor(static fn () => null); - $provider1 = $this->prophesize(HandlerProvider::class); - $provider1->handlerForQuery(QueryProfile::class)->willReturn([ + $provider1 = $this->createMock(HandlerProvider::class); + $provider1->method('handlerForQuery')->with(QueryProfile::class)->willReturn([ $handler1, $handler2, ]); - $provider2 = $this->prophesize(HandlerProvider::class); - $provider2->handlerForQuery(QueryProfile::class)->willReturn([$handler3]); + $provider2 = $this->createMock(HandlerProvider::class); + $provider2->method('handlerForQuery')->with(QueryProfile::class)->willReturn([$handler3]); $chainProvider = new ChainHandlerProvider([ - $provider1->reveal(), - $provider2->reveal(), + $provider1, + $provider2, ]); $result = $chainProvider->handlerForQuery(QueryProfile::class); diff --git a/tests/Unit/QueryBus/HandlerFinderTest.php b/tests/Unit/QueryBus/HandlerFinderTest.php index fd600938..29c94843 100644 --- a/tests/Unit/QueryBus/HandlerFinderTest.php +++ b/tests/Unit/QueryBus/HandlerFinderTest.php @@ -11,13 +11,10 @@ use Patchlevel\EventSourcing\Tests\Unit\Fixture\OtherQueryProfile; use Patchlevel\EventSourcing\Tests\Unit\Fixture\QueryProfile; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; /** @covers \Patchlevel\EventSourcing\QueryBus\HandlerFinder */ final class HandlerFinderTest extends TestCase { - use ProphecyTrait; - public function testNoParameters(): void { $this->expectException(InvalidHandleMethod::class); @@ -40,7 +37,7 @@ public function testNoType(): void $class = new class () { // phpcs:disable #[Answer] - public function handle($query): void + public function handle(mixed $query): void { } // phpcs:enable diff --git a/tests/Unit/QueryBus/ServiceHandlerProviderTest.php b/tests/Unit/QueryBus/ServiceHandlerProviderTest.php index b6d1b230..2854be51 100644 --- a/tests/Unit/QueryBus/ServiceHandlerProviderTest.php +++ b/tests/Unit/QueryBus/ServiceHandlerProviderTest.php @@ -8,13 +8,10 @@ use Patchlevel\EventSourcing\QueryBus\ServiceHandlerProvider; use Patchlevel\EventSourcing\Tests\Unit\Fixture\QueryProfile; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; /** @covers \Patchlevel\EventSourcing\QueryBus\ServiceHandlerProvider */ final class ServiceHandlerProviderTest extends TestCase { - use ProphecyTrait; - public function testEmpty(): void { $provider = new ServiceHandlerProvider([]); diff --git a/tests/Unit/QueryBus/SyncQueryBusTest.php b/tests/Unit/QueryBus/SyncQueryBusTest.php index 6d269bac..bae242f8 100644 --- a/tests/Unit/QueryBus/SyncQueryBusTest.php +++ b/tests/Unit/QueryBus/SyncQueryBusTest.php @@ -9,23 +9,20 @@ use Patchlevel\EventSourcing\QueryBus\InvalidQueryHandler; use Patchlevel\EventSourcing\QueryBus\SyncQueryBus; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; use Psr\Log\LoggerInterface; /** @covers \Patchlevel\EventSourcing\QueryBus\SyncQueryBus */ final class SyncQueryBusTest extends TestCase { - use ProphecyTrait; - public function testHandlerNotFound(): void { $query = new class { }; - $handlerProvider = $this->prophesize(HandlerProvider::class); - $handlerProvider->handlerForQuery($query::class)->willReturn([]); + $handlerProvider = $this->createMock(HandlerProvider::class); + $handlerProvider->method('handlerForQuery')->with($query::class)->willReturn([]); - $queryBus = new SyncQueryBus($handlerProvider->reveal()); + $queryBus = new SyncQueryBus($handlerProvider); $this->expectException(InvalidQueryHandler::class); $this->expectExceptionMessage('No handler found for query ' . $query::class); @@ -38,13 +35,13 @@ public function testMultipleHandlersFound(): void $query = new class { }; - $handlerProvider = $this->prophesize(HandlerProvider::class); - $handlerProvider->handlerForQuery($query::class)->willReturn([ + $handlerProvider = $this->createMock(HandlerProvider::class); + $handlerProvider->method('handlerForQuery')->with($query::class)->willReturn([ new HandlerDescriptor(static fn () => null), new HandlerDescriptor(static fn () => null), ]); - $queryBus = new SyncQueryBus($handlerProvider->reveal()); + $queryBus = new SyncQueryBus($handlerProvider); $this->expectException(InvalidQueryHandler::class); $this->expectExceptionMessage('Multiple handlers found for query ' . $query::class); @@ -66,15 +63,15 @@ public function __invoke(object $query): void } }; - $handlerProvider = $this->prophesize(HandlerProvider::class); - $handlerProvider->handlerForQuery($query::class)->willReturn([ + $handlerProvider = $this->createMock(HandlerProvider::class); + $handlerProvider->method('handlerForQuery')->with($query::class)->willReturn([ new HandlerDescriptor($handler), ]); - $logger = $this->prophesize(LoggerInterface::class); - $logger->debug('QueryBus: dispatch query', ['query' => $query::class])->shouldBeCalledOnce(); + $logger = $this->createMock(LoggerInterface::class); + $logger->expects($this->once())->method('debug')->with('QueryBus: dispatch query', ['query' => $query::class]); - $queryBus = new SyncQueryBus($handlerProvider->reveal(), $logger->reveal()); + $queryBus = new SyncQueryBus($handlerProvider, $logger); $queryBus->dispatch($query); self::assertSame($query, $handler->query); diff --git a/tests/Unit/Repository/DefaultRepositoryManagerTest.php b/tests/Unit/Repository/DefaultRepositoryManagerTest.php index 76cf8360..52ac9a5f 100644 --- a/tests/Unit/Repository/DefaultRepositoryManagerTest.php +++ b/tests/Unit/Repository/DefaultRepositoryManagerTest.php @@ -13,25 +13,22 @@ use Patchlevel\EventSourcing\Tests\Unit\Fixture\ProfileWithSnapshot; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; #[CoversClass(DefaultRepositoryManager::class)] final class DefaultRepositoryManagerTest extends TestCase { - use ProphecyTrait; - public function testGetNewRepository(): void { - $store = $this->prophesize(Store::class); - $eventBus = $this->prophesize(EventBus::class); + $store = $this->createMock(Store::class); + $eventBus = $this->createMock(EventBus::class); $repositoryManager = new DefaultRepositoryManager( new AggregateRootRegistry([ 'profile' => Profile::class, 'profile2' => ProfileWithSnapshot::class, ]), - $store->reveal(), - $eventBus->reveal(), + $store, + $eventBus, ); $repository1 = $repositoryManager->get(Profile::class); @@ -42,13 +39,13 @@ public function testGetNewRepository(): void public function testSameRepository(): void { - $store = $this->prophesize(Store::class); - $eventBus = $this->prophesize(EventBus::class); + $store = $this->createMock(Store::class); + $eventBus = $this->createMock(EventBus::class); $repositoryManager = new DefaultRepositoryManager( new AggregateRootRegistry(['profile' => Profile::class]), - $store->reveal(), - $eventBus->reveal(), + $store, + $eventBus, ); $repository1 = $repositoryManager->get(Profile::class); @@ -61,13 +58,13 @@ public function testNotDefined(): void { $this->expectException(AggregateRootClassNotRegistered::class); - $store = $this->prophesize(Store::class); - $eventBus = $this->prophesize(EventBus::class); + $store = $this->createMock(Store::class); + $eventBus = $this->createMock(EventBus::class); $repositoryManager = new DefaultRepositoryManager( new AggregateRootRegistry([]), - $store->reveal(), - $eventBus->reveal(), + $store, + $eventBus, ); $repositoryManager->get(Profile::class); diff --git a/tests/Unit/Repository/DefaultRepositoryTest.php b/tests/Unit/Repository/DefaultRepositoryTest.php index c61370e0..c1b033c2 100644 --- a/tests/Unit/Repository/DefaultRepositoryTest.php +++ b/tests/Unit/Repository/DefaultRepositoryTest.php @@ -43,21 +43,19 @@ use Patchlevel\EventSourcing\Tests\Unit\Fixture\ProfileWithStream; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Prophecy\Argument; -use Prophecy\PhpUnit\ProphecyTrait; use RuntimeException; use Throwable; #[CoversClass(DefaultRepository::class)] final class DefaultRepositoryTest extends TestCase { - use ProphecyTrait; - public function testSaveAggregate(): void { - $store = $this->prophesize(Store::class); - $store->save( - Argument::that(static function (Message $message) { + $store = $this->createMock(Store::class); + $store + ->expects($this->atLeastOnce()) + ->method('save') + ->willReturnCallback(static function (Message $message) { if ($message->header(AggregateHeader::class)->aggregateName !== 'profile') { return false; } @@ -67,25 +65,9 @@ public function testSaveAggregate(): void } return $message->header(AggregateHeader::class)->playhead === 1; - }), - Argument::that(static function (Message $message) { - if ($message->header(AggregateHeader::class)->aggregateName !== 'profile') { - return false; - } - - if ($message->header(AggregateHeader::class)->aggregateId !== '1') { - return false; - } - - return $message->header(AggregateHeader::class)->playhead === 2; - }), - )->shouldBeCalled(); - - $repository = new DefaultRepository( - $store->reveal(), - Profile::metadata(), - ); + }); + $repository = new DefaultRepository($store, Profile::metadata()); $aggregate = Profile::createProfile( ProfileId::fromString('1'), Email::fromString('hallo@patchlevel.de'), @@ -98,9 +80,11 @@ public function testSaveAggregate(): void public function testUpdateAggregate(): void { - $store = $this->prophesize(Store::class); - $store->save( - Argument::that(static function (Message $message) { + $store = $this->createMock(Store::class); + $store + ->expects($this->exactly(2)) + ->method('save') + ->willReturnCallback(static function (Message $message) { if ($message->header(AggregateHeader::class)->aggregateName !== 'profile') { return false; } @@ -109,75 +93,34 @@ public function testUpdateAggregate(): void return false; } - return $message->header(AggregateHeader::class)->playhead === 1; - }), - )->shouldBeCalled(); - - $store->save( - Argument::that(static function (Message $message) { - if ($message->header(AggregateHeader::class)->aggregateName !== 'profile') { - return false; + if ($message->header(AggregateHeader::class)->playhead === 1 && $message->event()::class === ProfileCreated::class) { + return true; } - if ($message->header(AggregateHeader::class)->aggregateId !== '1') { - return false; - } - - return $message->header(AggregateHeader::class)->playhead === 2; - }), - )->shouldBeCalled(); - - $repository = new DefaultRepository( - $store->reveal(), - Profile::metadata(), - ); + return $message->header(AggregateHeader::class)->playhead === 2 && $message->event()::class === ProfileVisited::class; + }); + $repository = new DefaultRepository($store, Profile::metadata()); $aggregate = Profile::createProfile( ProfileId::fromString('1'), Email::fromString('hallo@patchlevel.de'), ); - $repository->save($aggregate); $aggregate->visitProfile(ProfileId::fromString('2')); - $repository->save($aggregate); } public function testEventBus(): void { - $store = $this->prophesize(Store::class); - $store->save( - Argument::that(static function (Message $message) { - if ($message->header(AggregateHeader::class)->aggregateName !== 'profile') { - return false; - } - - if ($message->header(AggregateHeader::class)->aggregateId !== '1') { - return false; - } - - return $message->header(AggregateHeader::class)->playhead === 1; - }), - )->shouldBeCalled(); - - $store->save( - Argument::that(static function (Message $message) { - if ($message->header(AggregateHeader::class)->aggregateName !== 'profile') { - return false; - } - - if ($message->header(AggregateHeader::class)->aggregateId !== '1') { - return false; - } - - return $message->header(AggregateHeader::class)->playhead === 2; - }), - )->shouldBeCalled(); - - $eventBus = $this->prophesize(EventBus::class); - $eventBus->dispatch( - Argument::that(static function (Message $message) { + $store = $this->createMock(Store::class); + $store->expects($this->exactly(2))->method('save'); + + $eventBus = $this->createMock(EventBus::class); + $eventBus + ->expects($this->exactly(2)) + ->method('dispatch') + ->willReturnCallback(static function (Message $message) { if ($message->header(AggregateHeader::class)->aggregateName !== 'profile') { return false; } @@ -186,47 +129,31 @@ public function testEventBus(): void return false; } - return $message->header(AggregateHeader::class)->playhead === 1; - }), - )->shouldBeCalled(); - - $eventBus->dispatch( - Argument::that(static function (Message $message) { - if ($message->header(AggregateHeader::class)->aggregateName !== 'profile') { - return false; - } - - if ($message->header(AggregateHeader::class)->aggregateId !== '1') { - return false; + if ($message->header(AggregateHeader::class)->playhead === 1 && $message->event()::class === ProfileCreated::class) { + return true; } - return $message->header(AggregateHeader::class)->playhead === 2; - }), - )->shouldBeCalled(); - - $repository = new DefaultRepository( - $store->reveal(), - Profile::metadata(), - $eventBus->reveal(), - ); + return $message->header(AggregateHeader::class)->playhead === 2 && $message->event()::class === ProfileVisited::class; + }); + $repository = new DefaultRepository($store, Profile::metadata(), $eventBus); $aggregate = Profile::createProfile( ProfileId::fromString('1'), Email::fromString('hallo@patchlevel.de'), ); - $repository->save($aggregate); $aggregate->visitProfile(ProfileId::fromString('2')); - $repository->save($aggregate); } public function testDecorator(): void { - $store = $this->prophesize(Store::class); - $store->save( - Argument::that(static function (Message $message) { + $store = $this->createMock(Store::class); + $store + ->expects($this->atLeastOnce()) + ->method('save') + ->willReturnCallback(static function (Message $message) { if ($message->header(AggregateHeader::class)->aggregateName !== 'profile') { return false; } @@ -240,8 +167,7 @@ public function testDecorator(): void } return $message->header(AggregateHeader::class)->playhead === 1; - }), - )->shouldBeCalled(); + }); $decorator = new class implements MessageDecorator { public function __invoke(Message $message): Message @@ -251,7 +177,7 @@ public function __invoke(Message $message): Message }; $repository = new DefaultRepository( - $store->reveal(), + $store, Profile::metadata(), null, null, @@ -268,10 +194,10 @@ public function __invoke(Message $message): Message public function testSaveWrongAggregate(): void { - $store = $this->prophesize(Store::class); + $store = $this->createMock(Store::class); $repository = new DefaultRepository( - $store->reveal(), + $store, Profile::metadata(), ); @@ -288,9 +214,11 @@ public function testSaveWrongAggregate(): void public function testSaveAggregateWithEmptyEventStream(): void { - $store = $this->prophesize(Store::class); - $store->save( - Argument::that(static function (Message $message) { + $store = $this->createMock(Store::class); + $store + ->expects($this->once()) + ->method('save') + ->willReturnCallback(static function (Message $message) { if ($message->header(AggregateHeader::class)->aggregateName !== 'profile') { return false; } @@ -300,14 +228,9 @@ public function testSaveAggregateWithEmptyEventStream(): void } return $message->header(AggregateHeader::class)->playhead === 1; - }), - )->shouldBeCalledOnce(); - - $repository = new DefaultRepository( - $store->reveal(), - Profile::metadata(), - ); + }); + $repository = new DefaultRepository($store, Profile::metadata()); $aggregate = Profile::createProfile( ProfileId::fromString('1'), Email::fromString('hallo@patchlevel.de'), @@ -319,15 +242,14 @@ public function testSaveAggregateWithEmptyEventStream(): void public function testDetachedException(): void { - $store = $this->prophesize(Store::class); - $store->save( - Argument::type(Message::class), - )->willThrow(new RuntimeException()); + $store = $this->createMock(Store::class); + $store + ->expects($this->atLeastOnce()) + ->method('save') + ->with($this->isInstanceOf(Message::class)) + ->willThrowException(new RuntimeException()); - $repository = new DefaultRepository( - $store->reveal(), - Profile::metadata(), - ); + $repository = new DefaultRepository($store, Profile::metadata()); $aggregate = Profile::createProfile( ProfileId::fromString('1'), @@ -341,7 +263,6 @@ public function testDetachedException(): void } $this->expectException(AggregateDetached::class); - $repository->save($aggregate); } @@ -349,23 +270,19 @@ public function testUnknownException(): void { $this->expectException(AggregateUnknown::class); - $store = $this->prophesize(Store::class); - $store->save( - Argument::type(Message::class), - )->shouldNotBeCalled(); + $store = $this->createMock(Store::class); + $store + ->expects($this->never()) + ->method('save') + ->with($this->isInstanceOf(Message::class)); - $repository = new DefaultRepository( - $store->reveal(), - Profile::metadata(), - ); + $repository = new DefaultRepository($store, Profile::metadata()); $aggregate = Profile::createProfile( ProfileId::fromString('1'), Email::fromString('hallo@patchlevel.de'), ); - $aggregate->releaseEvents(); - $aggregate->visitProfile(ProfileId::fromString('2')); $repository->save($aggregate); @@ -375,15 +292,14 @@ public function testDuplicate(): void { $this->expectException(AggregateAlreadyExists::class); - $store = $this->prophesize(Store::class); - $store->save( - Argument::type(Message::class), - )->willThrow(new UniqueConstraintViolation()); + $store = $this->createMock(Store::class); + $store + ->expects($this->once()) + ->method('save') + ->with($this->isInstanceOf(Message::class)) + ->willThrowException(new UniqueConstraintViolation()); - $repository = new DefaultRepository( - $store->reveal(), - Profile::metadata(), - ); + $repository = new DefaultRepository($store, Profile::metadata()); $aggregate = Profile::createProfile( ProfileId::fromString('1'), @@ -397,42 +313,34 @@ public function testOutdated(): void { $this->expectException(AggregateOutdated::class); - $store = $this->prophesize(Store::class); - - $store->save( - Argument::that(static function (Message $message) { - return $message->header(AggregateHeader::class)->playhead === 1; - }), - )->shouldBeCalled(); - - $store->save( - Argument::that(static function (Message $message) { - return $message->header(AggregateHeader::class)->playhead === 2; - }), - )->willThrow(new UniqueConstraintViolation()); + $store = $this->createMock(Store::class); + $store + ->expects($this->exactly(2)) + ->method('save') + ->willReturnOnConsecutiveCalls( + true, + $this->throwException(new UniqueConstraintViolation()), + ); - $repository = new DefaultRepository( - $store->reveal(), - Profile::metadata(), - ); + $repository = new DefaultRepository($store, Profile::metadata()); $aggregate = Profile::createProfile( ProfileId::fromString('1'), Email::fromString('hallo@patchlevel.de'), ); - $repository->save($aggregate); $aggregate->visitProfile(ProfileId::fromString('2')); - $repository->save($aggregate); } public function testSaveAggregateWithSplitStream(): void { - $store = $this->prophesize(Store::class); - $store->save( - Argument::that(static function (Message $message) { + $store = $this->createMock(Store::class); + $store + ->expects($this->atLeastOnce()) + ->method('save') + ->willReturnCallback(static function (Message $message) { if ($message->header(AggregateHeader::class)->aggregateName !== 'profile') { return false; } @@ -442,33 +350,10 @@ public function testSaveAggregateWithSplitStream(): void } return $message->header(AggregateHeader::class)->playhead === 1; - }), - Argument::that(static function (Message $message) { - if ($message->header(AggregateHeader::class)->aggregateName !== 'profile') { - return false; - } - - if ($message->header(AggregateHeader::class)->aggregateId !== '1') { - return false; - } - - return $message->header(AggregateHeader::class)->playhead === 2; - }), - Argument::that(static function (Message $message) { - if ($message->header(AggregateHeader::class)->aggregateName !== 'profile') { - return false; - } - - if ($message->header(AggregateHeader::class)->aggregateId !== '1') { - return false; - } - - return $message->header(AggregateHeader::class)->playhead === 3; - }), - )->shouldBeCalled(); + }); $repository = new DefaultRepository( - $store->reveal(), + $store, Profile::metadata(), null, null, @@ -487,24 +372,24 @@ public function testSaveAggregateWithSplitStream(): void public function testLoadAggregate(): void { - $store = $this->prophesize(Store::class); - $store->load(new Criteria( - new AggregateNameCriterion('profile'), - new AggregateIdCriterion('1'), - new ArchivedCriterion(false), - ))->willReturn(new ArrayStream([ - Message::create( - new ProfileCreated( - ProfileId::fromString('1'), - Email::fromString('hallo@patchlevel.de'), - ), - )->withHeader(new AggregateHeader('profile', '1', 1, new DateTimeImmutable())), - ])); + $store = $this->createMock(Store::class); + $store + ->expects($this->once()) + ->method('load') + ->with(new Criteria( + new AggregateNameCriterion('profile'), + new AggregateIdCriterion('1'), + new ArchivedCriterion(false), + ))->willReturn(new ArrayStream([ + Message::create( + new ProfileCreated( + ProfileId::fromString('1'), + Email::fromString('hallo@patchlevel.de'), + ), + )->withHeader(new AggregateHeader('profile', '1', 1, new DateTimeImmutable())), + ])); - $repository = new DefaultRepository( - $store->reveal(), - Profile::metadata(), - ); + $repository = new DefaultRepository($store, Profile::metadata()); $aggregate = $repository->load(ProfileId::fromString('1')); @@ -516,34 +401,34 @@ public function testLoadAggregate(): void public function testLoadAggregateTwice(): void { - $store = $this->prophesize(Store::class); - $store->load(new Criteria( - new AggregateNameCriterion('profile'), - new AggregateIdCriterion('1'), - new ArchivedCriterion(false), - ))->willReturn( - new ArrayStream([ - Message::create( - new ProfileCreated( - ProfileId::fromString('1'), - Email::fromString('hallo@patchlevel.de'), - ), - )->withHeader(new AggregateHeader('profile', '1', 1, new DateTimeImmutable())), - ]), - new ArrayStream([ - Message::create( - new ProfileCreated( - ProfileId::fromString('1'), - Email::fromString('hallo@patchlevel.de'), - ), - )->withHeader(new AggregateHeader('profile', '1', 1, new DateTimeImmutable())), - ]), - ); - - $repository = new DefaultRepository( - $store->reveal(), - Profile::metadata(), - ); + $store = $this->createMock(Store::class); + $store + ->expects($this->exactly(2)) + ->method('load') + ->with(new Criteria( + new AggregateNameCriterion('profile'), + new AggregateIdCriterion('1'), + new ArchivedCriterion(false), + ))->willReturn( + new ArrayStream([ + Message::create( + new ProfileCreated( + ProfileId::fromString('1'), + Email::fromString('hallo@patchlevel.de'), + ), + )->withHeader(new AggregateHeader('profile', '1', 1, new DateTimeImmutable())), + ]), + new ArrayStream([ + Message::create( + new ProfileCreated( + ProfileId::fromString('1'), + Email::fromString('hallo@patchlevel.de'), + ), + )->withHeader(new AggregateHeader('profile', '1', 1, new DateTimeImmutable())), + ]), + ); + + $repository = new DefaultRepository($store, Profile::metadata()); $aggregate1 = $repository->load(ProfileId::fromString('1')); $aggregate2 = $repository->load(ProfileId::fromString('1')); @@ -556,49 +441,52 @@ public function testAggregateNotFound(): void { $this->expectException(AggregateNotFound::class); - $store = $this->prophesize(Store::class); - $store->load(new Criteria( - new AggregateNameCriterion('profile'), - new AggregateIdCriterion('1'), - new ArchivedCriterion(false), - ))->willReturn(new ArrayStream()); + $store = $this->createMock(Store::class); + $store + ->expects($this->once()) + ->method('load') + ->with(new Criteria( + new AggregateNameCriterion('profile'), + new AggregateIdCriterion('1'), + new ArchivedCriterion(false), + )) + ->willReturn(new ArrayStream()); - $repository = new DefaultRepository( - $store->reveal(), - Profile::metadata(), - ); + $repository = new DefaultRepository($store, Profile::metadata()); $repository->load(ProfileId::fromString('1')); } public function testHasAggregate(): void { - $store = $this->prophesize(Store::class); - $store->count(new Criteria( - new AggregateNameCriterion('profile'), - new AggregateIdCriterion('1'), - ))->willReturn(1); + $store = $this->createMock(Store::class); + $store + ->expects($this->once()) + ->method('count') + ->with(new Criteria( + new AggregateNameCriterion('profile'), + new AggregateIdCriterion('1'), + )) + ->willReturn(1); - $repository = new DefaultRepository( - $store->reveal(), - Profile::metadata(), - ); + $repository = new DefaultRepository($store, Profile::metadata()); self::assertTrue($repository->has(ProfileId::fromString('1'))); } public function testNotHasAggregate(): void { - $store = $this->prophesize(Store::class); - $store->count(new Criteria( - new AggregateNameCriterion('profile'), - new AggregateIdCriterion('1'), - ))->willReturn(0); + $store = $this->createMock(Store::class); + $store + ->expects($this->once()) + ->method('count') + ->with(new Criteria( + new AggregateNameCriterion('profile'), + new AggregateIdCriterion('1'), + )) + ->willReturn(0); - $repository = new DefaultRepository( - $store->reveal(), - Profile::metadata(), - ); + $repository = new DefaultRepository($store, Profile::metadata()); self::assertFalse($repository->has(ProfileId::fromString('1'))); } @@ -612,24 +500,25 @@ public function testLoadAggregateWithSnapshot(): void Email::fromString('hallo@patchlevel.de'), ); - $store = $this->prophesize(Store::class); - $store->load(new Criteria( - new AggregateNameCriterion('profile_with_snapshot'), - new AggregateIdCriterion('1'), - new FromPlayheadCriterion(1), - ))->willReturn(new ArrayStream()); + $store = $this->createMock(Store::class); + $store + ->expects($this->once()) + ->method('load') + ->with(new Criteria( + new AggregateNameCriterion('profile_with_snapshot'), + new AggregateIdCriterion('1'), + new FromPlayheadCriterion(1), + )) + ->willReturn(new ArrayStream()); - $snapshotStore = $this->prophesize(SnapshotStore::class); - $snapshotStore->load( - ProfileWithSnapshot::class, - $id, - )->willReturn($profile); + $snapshotStore = $this->createMock(SnapshotStore::class); + $snapshotStore->method('load')->with(ProfileWithSnapshot::class, $id)->willReturn($profile); $repository = new DefaultRepository( - $store->reveal(), + $store, ProfileWithSnapshot::metadata(), null, - $snapshotStore->reveal(), + $snapshotStore, ); $aggregate = $repository->load(ProfileId::fromString('1')); @@ -642,47 +531,59 @@ public function testLoadAggregateWithSnapshot(): void public function testLoadAggregateWithSnapshotFirstTime(): void { - $store = $this->prophesize(Store::class); - $store->load( - new Criteria( + $store = $this->createMock(Store::class); + $store + ->expects($this->once()) + ->method('load') + ->with(new Criteria( new AggregateNameCriterion('profile_with_snapshot'), new AggregateIdCriterion('1'), new ArchivedCriterion(false), - ), - )->willReturn( - new ArrayStream([ - Message::create( - new ProfileCreated( - ProfileId::fromString('1'), - Email::fromString('hallo@patchlevel.de'), - ), - )->withHeader(new AggregateHeader('profile', '1', 1, new DateTimeImmutable())), - Message::create( - new ProfileVisited( - ProfileId::fromString('1'), - ), - )->withHeader(new AggregateHeader('profile', '1', 2, new DateTimeImmutable())), - Message::create( - new ProfileVisited( - ProfileId::fromString('1'), - ), - )->withHeader(new AggregateHeader('profile', '1', 3, new DateTimeImmutable())), - ]), - ); - - $snapshotStore = $this->prophesize(SnapshotStore::class); - $snapshotStore->load( - ProfileWithSnapshot::class, - ProfileId::fromString('1'), - )->willThrow(new SnapshotNotFound(ProfileWithSnapshot::class, ProfileId::fromString('1'))); - - $snapshotStore->save(Argument::type(ProfileWithSnapshot::class))->shouldBeCalled(); + )) + ->willReturn( + new ArrayStream([ + Message::create( + new ProfileCreated( + ProfileId::fromString('1'), + Email::fromString('hallo@patchlevel.de'), + ), + )->withHeader(new AggregateHeader('profile', '1', 1, new DateTimeImmutable())), + Message::create( + new ProfileVisited( + ProfileId::fromString('1'), + ), + )->withHeader(new AggregateHeader('profile', '1', 2, new DateTimeImmutable())), + Message::create( + new ProfileVisited( + ProfileId::fromString('1'), + ), + )->withHeader(new AggregateHeader('profile', '1', 3, new DateTimeImmutable())), + ]), + ); + + $snapshotStore = $this->createMock(SnapshotStore::class); + $snapshotStore + ->expects($this->once()) + ->method('load') + ->with( + ProfileWithSnapshot::class, + ProfileId::fromString('1'), + ) + ->willThrowException(new SnapshotNotFound( + ProfileWithSnapshot::class, + ProfileId::fromString('1'), + )); + + $snapshotStore + ->expects($this->once()) + ->method('save') + ->with($this->isInstanceOf(ProfileWithSnapshot::class)); $repository = new DefaultRepository( - $store->reveal(), + $store, ProfileWithSnapshot::metadata(), null, - $snapshotStore->reveal(), + $snapshotStore, ); $aggregate = $repository->load(ProfileId::fromString('1')); @@ -700,44 +601,50 @@ public function testLoadAggregateWithSnapshotAndSaveNewVersion(): void Email::fromString('hallo@patchlevel.de'), ); - $store = $this->prophesize(Store::class); - $store->load( - new Criteria( + $store = $this->createMock(Store::class); + $store + ->expects($this->once()) + ->method('load') + ->with(new Criteria( new AggregateNameCriterion('profile_with_snapshot'), new AggregateIdCriterion('1'), new FromPlayheadCriterion(1), - ), - )->willReturn(new ArrayStream([ - Message::create( - new ProfileVisited( - ProfileId::fromString('1'), - ), - )->withHeader(new AggregateHeader('profile', '1', 1, new DateTimeImmutable())), - Message::create( - new ProfileVisited( - ProfileId::fromString('1'), - ), - )->withHeader(new AggregateHeader('profile', '1', 2, new DateTimeImmutable())), - Message::create( - new ProfileVisited( - ProfileId::fromString('1'), - ), - )->withHeader(new AggregateHeader('profile', '1', 3, new DateTimeImmutable())), - ])); - - $snapshotStore = $this->prophesize(SnapshotStore::class); - $snapshotStore->load( - ProfileWithSnapshot::class, - ProfileId::fromString('1'), - )->willReturn($profile); + )) + ->willReturn(new ArrayStream([ + Message::create( + new ProfileVisited( + ProfileId::fromString('1'), + ), + )->withHeader(new AggregateHeader('profile', '1', 1, new DateTimeImmutable())), + Message::create( + new ProfileVisited( + ProfileId::fromString('1'), + ), + )->withHeader(new AggregateHeader('profile', '1', 2, new DateTimeImmutable())), + Message::create( + new ProfileVisited( + ProfileId::fromString('1'), + ), + )->withHeader(new AggregateHeader('profile', '1', 3, new DateTimeImmutable())), + ])); - $snapshotStore->save($profile)->shouldBeCalled(); + $snapshotStore = $this->createMock(SnapshotStore::class); + $snapshotStore + ->expects($this->once()) + ->method('load') + ->with(ProfileWithSnapshot::class, ProfileId::fromString('1')) + ->willReturn($profile); + + $snapshotStore + ->expects($this->once()) + ->method('save') + ->with($profile); $repository = new DefaultRepository( - $store->reveal(), + $store, ProfileWithSnapshot::metadata(), null, - $snapshotStore->reveal(), + $snapshotStore, ); $aggregate = $repository->load(ProfileId::fromString('1')); @@ -750,12 +657,15 @@ public function testLoadAggregateWithSnapshotAndSaveNewVersion(): void public function testLoadAggregateWithoutSnapshot(): void { - $store = $this->prophesize(Store::class); - $store->load(new Criteria( - new AggregateNameCriterion('profile_with_snapshot'), - new AggregateIdCriterion('1'), - new ArchivedCriterion(false), - )) + $store = $this->createMock(Store::class); + $store + ->expects($this->once()) + ->method('load') + ->with(new Criteria( + new AggregateNameCriterion('profile_with_snapshot'), + new AggregateIdCriterion('1'), + new ArchivedCriterion(false), + )) ->willReturn(new ArrayStream([ Message::create( new ProfileCreated( @@ -765,15 +675,18 @@ public function testLoadAggregateWithoutSnapshot(): void )->withHeader(new AggregateHeader('profile', '1', 1, new DateTimeImmutable())), ])); - $snapshotStore = $this->prophesize(SnapshotStore::class); - $snapshotStore->load(ProfileWithSnapshot::class, ProfileId::fromString('1')) - ->willThrow(SnapshotNotFound::class); + $snapshotStore = $this->createMock(SnapshotStore::class); + $snapshotStore + ->expects($this->once()) + ->method('load') + ->with(ProfileWithSnapshot::class, ProfileId::fromString('1')) + ->willThrowException(new SnapshotNotFound(ProfileWithSnapshot::class, ProfileId::fromString('1'))); $repository = new DefaultRepository( - $store->reveal(), + $store, ProfileWithSnapshot::metadata(), null, - $snapshotStore->reveal(), + $snapshotStore, ); $aggregate = $repository->load(ProfileId::fromString('1')); @@ -786,64 +699,51 @@ public function testLoadAggregateWithoutSnapshot(): void public function testSaveAggregateInOtherStream(): void { - $store = $this->prophesize(Store::class); - $store->willImplement(StreamStore::class); - $store->save( - Argument::that(static function (Message $message) { + $store = $this->createMock(StreamStore::class); + $store + ->expects($this->once()) + ->method('save') + ->willReturnCallback(static function (Message $message) { if ($message->header(StreamNameHeader::class)->streamName !== 'other-1') { return false; } return $message->header(PlayheadHeader::class)->playhead === 1; - }), - Argument::that(static function (Message $message) { - if ($message->header(StreamNameHeader::class)->streamName !== 'other-1') { - return false; - } - - return $message->header(PlayheadHeader::class)->playhead === 2; - }), - )->shouldBeCalled(); - - $repository = new DefaultRepository( - $store->reveal(), - ProfileWithStream::metadata(), - ); + }); + $repository = new DefaultRepository($store, ProfileWithStream::metadata()); $aggregate = ProfileWithStream::createProfile( ProfileId::fromString('1'), Email::fromString('hallo@patchlevel.de'), ); $aggregate->visitProfile(ProfileId::fromString('2')); - $repository->save($aggregate); } public function testLoadAggregateFromOtherStream(): void { - $store = $this->prophesize(Store::class); - $store->willImplement(StreamStore::class); - - $store->load(new Criteria( - new StreamCriterion('other-1'), - new ArchivedCriterion(false), - ))->willReturn(new ArrayStream([ - Message::create( - new ProfileCreated( - ProfileId::fromString('1'), - Email::fromString('hallo@patchlevel.de'), - ), - )->withHeader(new StreamNameHeader('other-1')) - ->withHeader(new PlayheadHeader(1)) - ->withHeader(new RecordedOnHeader(new DateTimeImmutable())), - ])); + $store = $this->createMock(StreamStore::class); - $repository = new DefaultRepository( - $store->reveal(), - ProfileWithStream::metadata(), - ); + $store + ->expects($this->once()) + ->method('load') + ->with(new Criteria( + new StreamCriterion('other-1'), + new ArchivedCriterion(false), + )) + ->willReturn(new ArrayStream([ + Message::create( + new ProfileCreated( + ProfileId::fromString('1'), + Email::fromString('hallo@patchlevel.de'), + ), + )->withHeader(new StreamNameHeader('other-1')) + ->withHeader(new PlayheadHeader(1)) + ->withHeader(new RecordedOnHeader(new DateTimeImmutable())), + ])); + $repository = new DefaultRepository($store, ProfileWithStream::metadata()); $aggregate = $repository->load(ProfileId::fromString('1')); self::assertInstanceOf(ProfileWithStream::class, $aggregate); diff --git a/tests/Unit/Repository/MessageDecorator/ChainMessageDecoratorTest.php b/tests/Unit/Repository/MessageDecorator/ChainMessageDecoratorTest.php index a4363c77..aef38c4f 100644 --- a/tests/Unit/Repository/MessageDecorator/ChainMessageDecoratorTest.php +++ b/tests/Unit/Repository/MessageDecorator/ChainMessageDecoratorTest.php @@ -12,13 +12,10 @@ use Patchlevel\EventSourcing\Tests\Unit\Fixture\ProfileId; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; #[CoversClass(ChainMessageDecorator::class)] final class ChainMessageDecoratorTest extends TestCase { - use ProphecyTrait; - public function testChain(): void { $message = new Message( @@ -28,15 +25,15 @@ public function testChain(): void ), ); - $decorator1 = $this->prophesize(MessageDecorator::class); - $decorator1->__invoke($message)->willReturn($message)->shouldBeCalled(); + $decorator1 = $this->createMock(MessageDecorator::class); + $decorator1->expects($this->atLeastOnce())->method('__invoke')->with($message)->willReturn($message); - $decorator2 = $this->prophesize(MessageDecorator::class); - $decorator2->__invoke($message)->willReturn($message)->shouldBeCalled(); + $decorator2 = $this->createMock(MessageDecorator::class); + $decorator2->expects($this->atLeastOnce())->method('__invoke')->with($message)->willReturn($message); $chain = new ChainMessageDecorator([ - $decorator1->reveal(), - $decorator2->reveal(), + $decorator1, + $decorator2, ]); $chain($message); diff --git a/tests/Unit/Repository/MessageDecorator/SplitStreamDecoratorTest.php b/tests/Unit/Repository/MessageDecorator/SplitStreamDecoratorTest.php index b95ca207..cd163638 100644 --- a/tests/Unit/Repository/MessageDecorator/SplitStreamDecoratorTest.php +++ b/tests/Unit/Repository/MessageDecorator/SplitStreamDecoratorTest.php @@ -14,13 +14,10 @@ use Patchlevel\EventSourcing\Tests\Unit\Fixture\SplittingEvent; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; #[CoversClass(SplitStreamDecorator::class)] final class SplitStreamDecoratorTest extends TestCase { - use ProphecyTrait; - public function testWithoutSplittingStream(): void { $message = new Message( diff --git a/tests/Unit/Schema/ChainDoctrineSchemaConfiguratorTest.php b/tests/Unit/Schema/ChainDoctrineSchemaConfiguratorTest.php index 5f4e1f5e..047e6cdd 100644 --- a/tests/Unit/Schema/ChainDoctrineSchemaConfiguratorTest.php +++ b/tests/Unit/Schema/ChainDoctrineSchemaConfiguratorTest.php @@ -10,26 +10,23 @@ use Patchlevel\EventSourcing\Schema\DoctrineSchemaConfigurator; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; #[CoversClass(ChainDoctrineSchemaConfigurator::class)] final class ChainDoctrineSchemaConfiguratorTest extends TestCase { - use ProphecyTrait; - public function testChain(): void { - $schema = $this->prophesize(Schema::class)->reveal(); - $connection = $this->prophesize(Connection::class)->reveal(); + $schema = $this->createMock(Schema::class); + $connection = $this->createMock(Connection::class); - $configurator1 = $this->prophesize(DoctrineSchemaConfigurator::class); - $configurator1->configureSchema($schema, $connection)->shouldBeCalledOnce(); - $configurator2 = $this->prophesize(DoctrineSchemaConfigurator::class); - $configurator2->configureSchema($schema, $connection)->shouldBeCalledOnce(); + $configurator1 = $this->createMock(DoctrineSchemaConfigurator::class); + $configurator1->expects($this->once())->method('configureSchema')->with($schema, $connection); + $configurator2 = $this->createMock(DoctrineSchemaConfigurator::class); + $configurator2->expects($this->once())->method('configureSchema')->with($schema, $connection); $chain = new ChainDoctrineSchemaConfigurator([ - $configurator1->reveal(), - $configurator2->reveal(), + $configurator1, + $configurator2, ]); $chain->configureSchema($schema, $connection); diff --git a/tests/Unit/Schema/DoctrineMigrationSchemaProviderTest.php b/tests/Unit/Schema/DoctrineMigrationSchemaProviderTest.php index 25066721..014ecb2c 100644 --- a/tests/Unit/Schema/DoctrineMigrationSchemaProviderTest.php +++ b/tests/Unit/Schema/DoctrineMigrationSchemaProviderTest.php @@ -9,21 +9,18 @@ use Patchlevel\EventSourcing\Schema\DoctrineSchemaProvider; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; #[CoversClass(DoctrineMigrationSchemaProvider::class)] final class DoctrineMigrationSchemaProviderTest extends TestCase { - use ProphecyTrait; - public function testCreateSchema(): void { $expectedSchema = new Schema(); - $schemaProvider = $this->prophesize(DoctrineSchemaProvider::class); - $schemaProvider->schema()->willReturn($expectedSchema); + $schemaProvider = $this->createMock(DoctrineSchemaProvider::class); + $schemaProvider->method('schema')->willReturn($expectedSchema); - $doctrineSchemaManager = new DoctrineMigrationSchemaProvider($schemaProvider->reveal()); + $doctrineSchemaManager = new DoctrineMigrationSchemaProvider($schemaProvider); $schema = $doctrineSchemaManager->createSchema(); $this->assertEquals($expectedSchema, $schema); diff --git a/tests/Unit/Schema/DoctrineSchemaDirectorTest.php b/tests/Unit/Schema/DoctrineSchemaDirectorTest.php index 20b18cab..df0982c8 100644 --- a/tests/Unit/Schema/DoctrineSchemaDirectorTest.php +++ b/tests/Unit/Schema/DoctrineSchemaDirectorTest.php @@ -15,60 +15,63 @@ use Patchlevel\EventSourcing\Schema\DoctrineSchemaDirector; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Prophecy\Argument; -use Prophecy\PhpUnit\ProphecyTrait; #[CoversClass(DoctrineSchemaDirector::class)] final class DoctrineSchemaDirectorTest extends TestCase { - use ProphecyTrait; - public function testCreate(): void { - $schemaManager = $this->prophesize(AbstractSchemaManager::class); - $schemaManager->createSchemaConfig()->willReturn(new SchemaConfig()); - - $platform = $this->prophesize(AbstractPlatform::class); - $platform->getCreateTablesSQL(Argument::any())->willReturn(['this is sql!']); - $platform->supportsSchemas()->willReturn(false); + $schemaManager = $this->createMock(AbstractSchemaManager::class); + $schemaManager->method('createSchemaConfig')->willReturn(new SchemaConfig()); - $connection = $this->prophesize(Connection::class); - $connection->createSchemaManager()->willReturn($schemaManager->reveal()); - $connection->getDatabasePlatform()->willReturn($platform->reveal()); - $connection->executeStatement('this is sql!')->shouldBeCalledOnce(); + $platform = $this->createMock(AbstractPlatform::class); + $platform->method('getCreateTablesSQL')->willReturn(['this is sql!']); + $platform->method('supportsSchemas')->willReturn(false); - $schemaConfigurator = $this->prophesize(DoctrineSchemaConfigurator::class); - $schemaConfigurator->configureSchema(Argument::type(Schema::class), $connection->reveal())->shouldBeCalledOnce(); + $connection = $this->createMock(Connection::class); + $connection->method('createSchemaManager')->willReturn($schemaManager); + $connection->method('getDatabasePlatform')->willReturn($platform); + $connection->expects($this->once())->method('executeStatement')->with('this is sql!'); - $doctrineSchemaManager = new DoctrineSchemaDirector( - $connection->reveal(), - $schemaConfigurator->reveal(), - ); + $schemaConfigurator = $this->createMock(DoctrineSchemaConfigurator::class); + $schemaConfigurator->expects($this->once())->method('configureSchema')->with($this->isInstanceOf(Schema::class), $connection); + $doctrineSchemaManager = new DoctrineSchemaDirector($connection, $schemaConfigurator); $doctrineSchemaManager->create(); } public function testDryRunCreate(): void { - $schemaManager = $this->prophesize(AbstractSchemaManager::class); - $schemaManager->createSchemaConfig()->willReturn(new SchemaConfig()); - - $platform = $this->prophesize(AbstractPlatform::class); - $platform->getCreateTablesSQL(Argument::any())->willReturn(['this is sql!']); - $platform->supportsSchemas()->willReturn(false); - - $connection = $this->prophesize(Connection::class); - $connection->createSchemaManager()->willReturn($schemaManager->reveal()); - $connection->getDatabasePlatform()->willReturn($platform->reveal()); - - $schemaConfigurator = $this->prophesize(DoctrineSchemaConfigurator::class); - $schemaConfigurator->configureSchema(Argument::type(Schema::class), $connection->reveal())->shouldBeCalledOnce(); - - $doctrineSchemaManager = new DoctrineSchemaDirector( - $connection->reveal(), - $schemaConfigurator->reveal(), - ); - + $schemaManager = $this->createMock(AbstractSchemaManager::class); + $schemaManager + ->expects($this->once()) + ->method('createSchemaConfig') + ->willReturn(new SchemaConfig()); + + $platform = $this->createMock(AbstractPlatform::class); + $platform + ->expects($this->once()) + ->method('getCreateTablesSQL') + ->willReturn(['this is sql!']); + $platform + ->expects($this->once()) + ->method('supportsSchemas') + ->willReturn(false); + + $connection = $this->createMock(Connection::class); + $connection + ->expects($this->once()) + ->method('createSchemaManager') + ->willReturn($schemaManager); + $connection + ->expects($this->once()) + ->method('getDatabasePlatform') + ->willReturn($platform); + + $schemaConfigurator = $this->createMock(DoctrineSchemaConfigurator::class); + $schemaConfigurator->expects($this->once())->method('configureSchema')->with($this->isInstanceOf(Schema::class), $connection); + + $doctrineSchemaManager = new DoctrineSchemaDirector($connection, $schemaConfigurator); $sqlStatements = $doctrineSchemaManager->dryRunCreate(); self::assertSame(['this is sql!'], $sqlStatements); @@ -76,67 +79,109 @@ public function testDryRunCreate(): void public function testUpdate(): void { - $diff = $this->prophesize(SchemaDiff::class); - - $fromSchema = $this->prophesize(Schema::class); - - $comperator = $this->prophesize(Comparator::class); - $comperator->compareSchemas($fromSchema->reveal(), Argument::type(Schema::class))->willReturn($diff); - - $schemaManager = $this->prophesize(AbstractSchemaManager::class); - $schemaManager->createComparator()->willReturn($comperator->reveal()); - $schemaManager->introspectSchema()->willReturn($fromSchema->reveal()); - $schemaManager->createSchemaConfig()->willReturn(new SchemaConfig()); - - $platform = $this->prophesize(AbstractPlatform::class); - $platform->getAlterSchemaSQL($diff->reveal())->willReturn(['x', 'y']); - - $connection = $this->prophesize(Connection::class); - $connection->createSchemaManager()->willReturn($schemaManager->reveal()); - $connection->getDatabasePlatform()->willReturn($platform->reveal()); - $connection->executeStatement('x')->shouldBeCalledOnce(); - $connection->executeStatement('y')->shouldBeCalledOnce(); - - $schemaConfigurator = $this->prophesize(DoctrineSchemaConfigurator::class); - $schemaConfigurator->configureSchema(Argument::type(Schema::class), $connection->reveal())->shouldBeCalledOnce(); - - $doctrineSchemaManager = new DoctrineSchemaDirector( - $connection->reveal(), - $schemaConfigurator->reveal(), - ); - + $diff = $this->createMock(SchemaDiff::class); + + $fromSchema = $this->createMock(Schema::class); + + $comperator = $this->createMock(Comparator::class); + $comperator + ->expects($this->once()) + ->method('compareSchemas') + ->with($fromSchema, $this->isInstanceOf(Schema::class)) + ->willReturn($diff); + + $schemaManager = $this->createMock(AbstractSchemaManager::class); + $schemaManager + ->expects($this->once()) + ->method('createComparator') + ->willReturn($comperator); + $schemaManager + ->expects($this->once()) + ->method('introspectSchema') + ->willReturn($fromSchema); + $schemaManager + ->expects($this->once()) + ->method('createSchemaConfig') + ->willReturn(new SchemaConfig()); + + $platform = $this->createMock(AbstractPlatform::class); + $platform + ->expects($this->once()) + ->method('getAlterSchemaSQL') + ->with($diff) + ->willReturn(['x', 'y']); + + $connection = $this->createMock(Connection::class); + $connection + ->expects($this->exactly(2)) + ->method('createSchemaManager') + ->willReturn($schemaManager); + $connection + ->expects($this->once()) + ->method('getDatabasePlatform') + ->willReturn($platform); + $connection + ->expects($this->exactly(2)) + ->method('executeStatement') + ->willReturnMap([ + ['x', 1], + ['y', 1], + ]); + + $schemaConfigurator = $this->createMock(DoctrineSchemaConfigurator::class); + $schemaConfigurator->expects($this->once())->method('configureSchema')->with($this->isInstanceOf(Schema::class), $connection); + + $doctrineSchemaManager = new DoctrineSchemaDirector($connection, $schemaConfigurator); $doctrineSchemaManager->update(); } public function testDryRunUpdate(): void { - $diff = $this->prophesize(SchemaDiff::class); - - $fromSchema = $this->prophesize(Schema::class); - - $comperator = $this->prophesize(Comparator::class); - $comperator->compareSchemas($fromSchema->reveal(), Argument::type(Schema::class))->willReturn($diff); - - $schemaManager = $this->prophesize(AbstractSchemaManager::class); - $schemaManager->createComparator()->willReturn($comperator->reveal()); - $schemaManager->introspectSchema()->willReturn($fromSchema->reveal()); - $schemaManager->createSchemaConfig()->willReturn(new SchemaConfig()); - - $platform = $this->prophesize(AbstractPlatform::class); - $platform->getAlterSchemaSQL($diff->reveal())->willReturn(['x', 'y']); - - $connection = $this->prophesize(Connection::class); - $connection->createSchemaManager()->willReturn($schemaManager->reveal()); - $connection->getDatabasePlatform()->willReturn($platform->reveal()); - - $schemaConfigurator = $this->prophesize(DoctrineSchemaConfigurator::class); - $schemaConfigurator->configureSchema(Argument::type(Schema::class), $connection->reveal())->shouldBeCalledOnce(); - - $doctrineSchemaManager = new DoctrineSchemaDirector( - $connection->reveal(), - $schemaConfigurator->reveal(), - ); - + $diff = $this->createMock(SchemaDiff::class); + + $fromSchema = $this->createMock(Schema::class); + + $comperator = $this->createMock(Comparator::class); + $comperator + ->expects($this->once()) + ->method('compareSchemas') + ->with($fromSchema, $this->isInstanceOf(Schema::class)) + ->willReturn($diff); + + $schemaManager = $this->createMock(AbstractSchemaManager::class); + $schemaManager + ->expects($this->once()) + ->method('createComparator') + ->willReturn($comperator); + $schemaManager + ->expects($this->once()) + ->method('introspectSchema') + ->willReturn($fromSchema); + $schemaManager + ->expects($this->once()) + ->method('createSchemaConfig') + ->willReturn(new SchemaConfig()); + + $platform = $this->createMock(AbstractPlatform::class); + $platform + ->expects($this->once()) + ->method('getAlterSchemaSQL') + ->with($diff)->willReturn(['x', 'y']); + + $connection = $this->createMock(Connection::class); + $connection + ->expects($this->exactly(2)) + ->method('createSchemaManager') + ->willReturn($schemaManager); + $connection + ->expects($this->once()) + ->method('getDatabasePlatform') + ->willReturn($platform); + + $schemaConfigurator = $this->createMock(DoctrineSchemaConfigurator::class); + $schemaConfigurator->expects($this->once())->method('configureSchema')->with($this->isInstanceOf(Schema::class), $connection); + + $doctrineSchemaManager = new DoctrineSchemaDirector($connection, $schemaConfigurator); $sqlStatements = $doctrineSchemaManager->dryRunUpdate(); self::assertSame(['x', 'y'], $sqlStatements); @@ -144,62 +189,69 @@ public function testDryRunUpdate(): void public function testDrop(): void { - $connection = $this->prophesize(Connection::class); - $currentSchema = $this->prophesize(Schema::class); - $schemaManager = $this->prophesize(AbstractSchemaManager::class); - $schemaManager->createSchemaConfig()->willReturn(new SchemaConfig()); - - $currentSchema->hasTable('foo')->willReturn(true); - $currentSchema->hasTable('bar')->willReturn(false); - - $schemaManager->introspectSchema()->willReturn($currentSchema->reveal()); - $connection->createSchemaManager()->willReturn($schemaManager->reveal()); - - $connection->executeStatement('DROP TABLE foo;')->shouldBeCalled(); - $connection->executeStatement('DROP TABLE bar;')->shouldNotBeCalled(); - - $schemaConfigurator = $this->prophesize(DoctrineSchemaConfigurator::class); - $schemaConfigurator->configureSchema(Argument::that(static function (Schema $schema) { - $schema->createTable('foo'); - $schema->createTable('bar'); - - return true; - }), $connection->reveal())->shouldBeCalledOnce(); - - $doctrineSchemaManager = new DoctrineSchemaDirector( - $connection->reveal(), - $schemaConfigurator->reveal(), - ); - + $connection = $this->createMock(Connection::class); + $currentSchema = $this->createMock(Schema::class); + $schemaManager = $this->createMock(AbstractSchemaManager::class); + $schemaManager->expects($this->once())->method('createSchemaConfig')->willReturn(new SchemaConfig()); + + $currentSchema + ->expects($this->exactly(2)) + ->method('hasTable') + ->willReturnMap([ + ['foo', true], + ['bar', false], + ]); + + $schemaManager->expects($this->once())->method('introspectSchema')->willReturn($currentSchema); + $connection->expects($this->exactly(2))->method('createSchemaManager')->willReturn($schemaManager); + + $connection->expects($this->once())->method('executeStatement')->with('DROP TABLE foo;'); + + $schemaConfigurator = $this->createMock(DoctrineSchemaConfigurator::class); + $schemaConfigurator + ->expects($this->once()) + ->method('configureSchema') + ->willReturnCallback(static function (Schema $schema) { + $schema->createTable('foo'); + $schema->createTable('bar'); + + return true; + }); + + $doctrineSchemaManager = new DoctrineSchemaDirector($connection, $schemaConfigurator); $doctrineSchemaManager->drop(); } public function testDryRunDrop(): void { - $connection = $this->prophesize(Connection::class); - $currentSchema = $this->prophesize(Schema::class); - $schemaManager = $this->prophesize(AbstractSchemaManager::class); - $schemaManager->createSchemaConfig()->willReturn(new SchemaConfig()); - - $currentSchema->hasTable('foo')->willReturn(true); - $currentSchema->hasTable('bar')->willReturn(false); - - $schemaManager->introspectSchema()->willReturn($currentSchema->reveal()); - $connection->createSchemaManager()->willReturn($schemaManager->reveal()); - - $schemaConfigurator = $this->prophesize(DoctrineSchemaConfigurator::class); - $schemaConfigurator->configureSchema(Argument::that(static function (Schema $schema) { - $schema->createTable('foo'); - $schema->createTable('bar'); - - return true; - }), $connection->reveal())->shouldBeCalledOnce(); - - $doctrineSchemaManager = new DoctrineSchemaDirector( - $connection->reveal(), - $schemaConfigurator->reveal(), - ); - + $connection = $this->createMock(Connection::class); + $currentSchema = $this->createMock(Schema::class); + $schemaManager = $this->createMock(AbstractSchemaManager::class); + $schemaManager->expects($this->once())->method('createSchemaConfig')->willReturn(new SchemaConfig()); + + $currentSchema + ->expects($this->exactly(2)) + ->method('hasTable') + ->willReturnMap([ + ['foo', true], + ['bar', false], + ]); + + $schemaManager->expects($this->once())->method('introspectSchema')->willReturn($currentSchema); + $connection->expects($this->exactly(2))->method('createSchemaManager')->willReturn($schemaManager); + + $schemaConfigurator = $this->createMock(DoctrineSchemaConfigurator::class); + $schemaConfigurator + ->expects($this->once()) + ->method('configureSchema') + ->willReturnCallback(static function (Schema $schema) { + $schema->createTable('foo'); + $schema->createTable('bar'); + + return true; + }); + + $doctrineSchemaManager = new DoctrineSchemaDirector($connection, $schemaConfigurator); $queries = $doctrineSchemaManager->dryRunDrop(); self::assertSame(['DROP TABLE foo;'], $queries); diff --git a/tests/Unit/Schema/DoctrineSchemaListenerTest.php b/tests/Unit/Schema/DoctrineSchemaListenerTest.php index c0ee9861..d17e3afa 100644 --- a/tests/Unit/Schema/DoctrineSchemaListenerTest.php +++ b/tests/Unit/Schema/DoctrineSchemaListenerTest.php @@ -12,26 +12,23 @@ use Patchlevel\EventSourcing\Schema\DoctrineSchemaListener; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; #[CoversClass(DoctrineSchemaListener::class)] final class DoctrineSchemaListenerTest extends TestCase { - use ProphecyTrait; - public function testPostGenerateSchema(): void { - $connection = $this->prophesize(Connection::class)->reveal(); - $em = $this->prophesize(EntityManagerInterface::class); - $em->getConnection()->willReturn($connection); + $connection = $this->createMock(Connection::class); + $em = $this->createMock(EntityManagerInterface::class); + $em->method('getConnection')->willReturn($connection); $expectedSchema = new Schema(); - $schemaConfigurator = $this->prophesize(DoctrineSchemaConfigurator::class); - $schemaConfigurator->configureSchema($expectedSchema, $connection)->shouldBeCalled(); + $schemaConfigurator = $this->createMock(DoctrineSchemaConfigurator::class); + $schemaConfigurator->expects($this->atLeastOnce())->method('configureSchema')->with($expectedSchema, $connection); - $event = new GenerateSchemaEventArgs($em->reveal(), $expectedSchema); + $event = new GenerateSchemaEventArgs($em, $expectedSchema); - $doctrineSchemaSubscriber = new DoctrineSchemaListener($schemaConfigurator->reveal()); + $doctrineSchemaSubscriber = new DoctrineSchemaListener($schemaConfigurator); $doctrineSchemaSubscriber->postGenerateSchema($event); } } diff --git a/tests/Unit/Schema/DoctrineSchemaSubscriberTest.php b/tests/Unit/Schema/DoctrineSchemaSubscriberTest.php index 541aed25..d9256cc3 100644 --- a/tests/Unit/Schema/DoctrineSchemaSubscriberTest.php +++ b/tests/Unit/Schema/DoctrineSchemaSubscriberTest.php @@ -12,34 +12,31 @@ use Patchlevel\EventSourcing\Schema\DoctrineSchemaSubscriber; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; #[CoversClass(DoctrineSchemaSubscriber::class)] final class DoctrineSchemaSubscriberTest extends TestCase { - use ProphecyTrait; - public function testPostGenerateSchema(): void { - $connection = $this->prophesize(Connection::class)->reveal(); - $em = $this->prophesize(EntityManagerInterface::class); - $em->getConnection()->willReturn($connection); + $connection = $this->createMock(Connection::class); + $em = $this->createMock(EntityManagerInterface::class); + $em->method('getConnection')->willReturn($connection); $expectedSchema = new Schema(); - $schemaConfigurator = $this->prophesize(DoctrineSchemaConfigurator::class); - $schemaConfigurator->configureSchema($expectedSchema, $connection)->shouldBeCalled(); + $schemaConfigurator = $this->createMock(DoctrineSchemaConfigurator::class); + $schemaConfigurator->expects($this->atLeastOnce())->method('configureSchema')->with($expectedSchema, $connection); - $event = new GenerateSchemaEventArgs($em->reveal(), $expectedSchema); + $event = new GenerateSchemaEventArgs($em, $expectedSchema); - $doctrineSchemaSubscriber = new DoctrineSchemaSubscriber($schemaConfigurator->reveal()); + $doctrineSchemaSubscriber = new DoctrineSchemaSubscriber($schemaConfigurator); $doctrineSchemaSubscriber->postGenerateSchema($event); } public function testGetSubscribedEvents(): void { - $schemaConfigurator = $this->prophesize(DoctrineSchemaConfigurator::class); + $schemaConfigurator = $this->createMock(DoctrineSchemaConfigurator::class); - $doctrineSchemaSubscriber = new DoctrineSchemaSubscriber($schemaConfigurator->reveal()); + $doctrineSchemaSubscriber = new DoctrineSchemaSubscriber($schemaConfigurator); $events = $doctrineSchemaSubscriber->getSubscribedEvents(); self::assertEquals(['postGenerateSchema'], $events); diff --git a/tests/Unit/Serializer/Normalizer/IdNormalizerTest.php b/tests/Unit/Serializer/Normalizer/IdNormalizerTest.php index 676546ef..9f0722cf 100644 --- a/tests/Unit/Serializer/Normalizer/IdNormalizerTest.php +++ b/tests/Unit/Serializer/Normalizer/IdNormalizerTest.php @@ -14,7 +14,6 @@ use Patchlevel\Hydrator\Normalizer\InvalidType; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; use Ramsey\Uuid\Exception\InvalidUuidStringException; use ReflectionClass; use ReflectionType; @@ -24,8 +23,6 @@ #[Attribute(Attribute::TARGET_PROPERTY)] final class IdNormalizerTest extends TestCase { - use ProphecyTrait; - public function testNormalizeWithNull(): void { $normalizer = new IdNormalizer(CustomId::class); diff --git a/tests/Unit/Snapshot/Adapter/Psr16SnapshotAdapterTest.php b/tests/Unit/Snapshot/Adapter/Psr16SnapshotAdapterTest.php index 92253add..519db8a9 100644 --- a/tests/Unit/Snapshot/Adapter/Psr16SnapshotAdapterTest.php +++ b/tests/Unit/Snapshot/Adapter/Psr16SnapshotAdapterTest.php @@ -8,30 +8,27 @@ use Patchlevel\EventSourcing\Snapshot\Adapter\SnapshotNotFound; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; use Psr\SimpleCache\CacheInterface; #[CoversClass(Psr16SnapshotAdapter::class)] final class Psr16SnapshotAdapterTest extends TestCase { - use ProphecyTrait; - public function testSaveSnapshot(): void { - $cache = $this->prophesize(CacheInterface::class); - $cache->set('key', ['foo' => 'bar'])->shouldBeCalled(); + $cache = $this->createMock(CacheInterface::class); + $cache->expects($this->atLeastOnce())->method('set')->with('key', ['foo' => 'bar']); - $store = new Psr16SnapshotAdapter($cache->reveal()); + $store = new Psr16SnapshotAdapter($cache); $store->save('key', ['foo' => 'bar']); } public function testLoadSnapshot(): void { - $cache = $this->prophesize(CacheInterface::class); - $cache->get('key')->willReturn(['foo' => 'bar']); + $cache = $this->createMock(CacheInterface::class); + $cache->method('get')->with('key')->willReturn(['foo' => 'bar']); - $store = new Psr16SnapshotAdapter($cache->reveal()); + $store = new Psr16SnapshotAdapter($cache); self::assertEquals(['foo' => 'bar'], $store->load('key')); } @@ -40,10 +37,10 @@ public function testSnapshotNotFound(): void { $this->expectException(SnapshotNotFound::class); - $cache = $this->prophesize(CacheInterface::class); - $cache->get('key')->willReturn(null); + $cache = $this->createMock(CacheInterface::class); + $cache->method('get')->with('key')->willReturn(null); - $store = new Psr16SnapshotAdapter($cache->reveal()); + $store = new Psr16SnapshotAdapter($cache); $store->load('key'); } } diff --git a/tests/Unit/Snapshot/Adapter/Psr6SnapshotAdapterTest.php b/tests/Unit/Snapshot/Adapter/Psr6SnapshotAdapterTest.php index fe4cb340..f22c954a 100644 --- a/tests/Unit/Snapshot/Adapter/Psr6SnapshotAdapterTest.php +++ b/tests/Unit/Snapshot/Adapter/Psr6SnapshotAdapterTest.php @@ -8,39 +8,36 @@ use Patchlevel\EventSourcing\Snapshot\Adapter\SnapshotNotFound; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; use Psr\Cache\CacheItemInterface; use Psr\Cache\CacheItemPoolInterface; #[CoversClass(Psr6SnapshotAdapter::class)] final class Psr6SnapshotAdapterTest extends TestCase { - use ProphecyTrait; - public function testSaveSnapshot(): void { - $item = $this->prophesize(CacheItemInterface::class); - $item->set(['foo' => 'bar'])->shouldBeCalled()->willReturn($item); + $item = $this->createMock(CacheItemInterface::class); + $item->expects($this->atLeastOnce())->method('set')->with(['foo' => 'bar'])->willReturn($item); - $cache = $this->prophesize(CacheItemPoolInterface::class); - $cache->getItem('key')->willReturn($item); - $cache->save($item)->shouldBeCalled(); + $cache = $this->createMock(CacheItemPoolInterface::class); + $cache->method('getItem')->with('key')->willReturn($item); + $cache->expects($this->atLeastOnce())->method('save')->with($item); - $store = new Psr6SnapshotAdapter($cache->reveal()); + $store = new Psr6SnapshotAdapter($cache); $store->save('key', ['foo' => 'bar']); } public function testLoadSnapshot(): void { - $item = $this->prophesize(CacheItemInterface::class); - $item->isHit()->willReturn(true); - $item->get()->willReturn(['foo' => 'bar']); + $item = $this->createMock(CacheItemInterface::class); + $item->method('isHit')->willReturn(true); + $item->method('get')->willReturn(['foo' => 'bar']); - $cache = $this->prophesize(CacheItemPoolInterface::class); - $cache->getItem('key')->willReturn($item); + $cache = $this->createMock(CacheItemPoolInterface::class); + $cache->method('getItem')->with('key')->willReturn($item); - $store = new Psr6SnapshotAdapter($cache->reveal()); + $store = new Psr6SnapshotAdapter($cache); self::assertEquals(['foo' => 'bar'], $store->load('key')); } @@ -49,13 +46,13 @@ public function testSnapshotNotFound(): void { $this->expectException(SnapshotNotFound::class); - $item = $this->prophesize(CacheItemInterface::class); - $item->isHit()->willReturn(false); + $item = $this->createMock(CacheItemInterface::class); + $item->method('isHit')->willReturn(false); - $cache = $this->prophesize(CacheItemPoolInterface::class); - $cache->getItem('key')->willReturn($item); + $cache = $this->createMock(CacheItemPoolInterface::class); + $cache->method('getItem')->with('key')->willReturn($item); - $store = new Psr6SnapshotAdapter($cache->reveal()); + $store = new Psr6SnapshotAdapter($cache); $store->load('key'); } } diff --git a/tests/Unit/Snapshot/ArrayAdapterRepositoryTest.php b/tests/Unit/Snapshot/ArrayAdapterRepositoryTest.php index 9f2bb761..c790654e 100644 --- a/tests/Unit/Snapshot/ArrayAdapterRepositoryTest.php +++ b/tests/Unit/Snapshot/ArrayAdapterRepositoryTest.php @@ -9,19 +9,16 @@ use Patchlevel\EventSourcing\Snapshot\ArrayAdapterRepository; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; #[CoversClass(ArrayAdapterRepository::class)] final class ArrayAdapterRepositoryTest extends TestCase { - use ProphecyTrait; - public function testGetAdapter(): void { - $adapter = $this->prophesize(SnapshotAdapter::class); - $repository = new ArrayAdapterRepository(['memory' => $adapter->reveal()]); + $adapter = $this->createMock(SnapshotAdapter::class); + $repository = new ArrayAdapterRepository(['memory' => $adapter]); - self::assertSame($adapter->reveal(), $repository->get('memory')); + self::assertSame($adapter, $repository->get('memory')); } public function testAdapterNotFound(): void diff --git a/tests/Unit/Snapshot/DefaultSnapshotStoreTest.php b/tests/Unit/Snapshot/DefaultSnapshotStoreTest.php index b841ae6e..a97cc126 100644 --- a/tests/Unit/Snapshot/DefaultSnapshotStoreTest.php +++ b/tests/Unit/Snapshot/DefaultSnapshotStoreTest.php @@ -16,26 +16,20 @@ use Patchlevel\EventSourcing\Tests\Unit\Fixture\ProfileWithSnapshot; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; use RuntimeException; #[CoversClass(DefaultSnapshotStore::class)] final class DefaultSnapshotStoreTest extends TestCase { - use ProphecyTrait; - public function testSave(): void { - $adapter = $this->prophesize(SnapshotAdapter::class); - $adapter->save( - 'profile_with_snapshot-1', - [ - 'version' => '1', - 'payload' => ['id' => '1', 'email' => 'info@patchlevel.de', 'messages' => [], '_playhead' => 2], - ], - )->shouldBeCalled(); + $adapter = $this->createMock(SnapshotAdapter::class); + $adapter->expects($this->atLeastOnce())->method('save')->with('profile_with_snapshot-1', [ + 'version' => '1', + 'payload' => ['id' => '1', 'email' => 'info@patchlevel.de', 'messages' => [], '_playhead' => 2], + ]); - $store = new DefaultSnapshotStore(['memory' => $adapter->reveal()]); + $store = new DefaultSnapshotStore(['memory' => $adapter]); $aggregate = ProfileWithSnapshot::createProfile( ProfileId::fromString('1'), @@ -49,17 +43,15 @@ public function testSave(): void public function testLoad(): void { - $adapter = $this->prophesize(SnapshotAdapter::class); - $adapter->load( - 'profile_with_snapshot-1', - )->willReturn( + $adapter = $this->createMock(SnapshotAdapter::class); + $adapter->method('load')->with('profile_with_snapshot-1')->willReturn( [ 'version' => '1', 'payload' => ['id' => '1', 'email' => 'info@patchlevel.de', 'messages' => [], '_playhead' => 2], ], ); - $store = new DefaultSnapshotStore(['memory' => $adapter->reveal()]); + $store = new DefaultSnapshotStore(['memory' => $adapter]); $aggregate = $store->load(ProfileWithSnapshot::class, ProfileId::fromString('1')); @@ -70,12 +62,14 @@ public function testLoad(): void public function testLoadNotFound(): void { - $adapter = $this->prophesize(SnapshotAdapter::class); - $adapter->load( - 'profile_with_snapshot-1', - )->willThrow(RuntimeException::class); + $adapter = $this->createMock(SnapshotAdapter::class); + $adapter + ->expects($this->once()) + ->method('load') + ->with('profile_with_snapshot-1') + ->willThrowException(new RuntimeException()); - $store = new DefaultSnapshotStore(['memory' => $adapter->reveal()]); + $store = new DefaultSnapshotStore(['memory' => $adapter]); $this->expectException(SnapshotNotFound::class); $store->load(ProfileWithSnapshot::class, ProfileId::fromString('1')); @@ -85,12 +79,10 @@ public function testLoadLegacySnapshots(): void { $this->expectException(SnapshotVersionInvalid::class); - $adapter = $this->prophesize(SnapshotAdapter::class); - $adapter->load( - 'profile_with_snapshot-1', - )->willReturn(['id' => '1', 'email' => 'info@patchlevel.de', 'messages' => [], '_playhead' => 2]); + $adapter = $this->createMock(SnapshotAdapter::class); + $adapter->method('load')->with('profile_with_snapshot-1')->willReturn(['id' => '1', 'email' => 'info@patchlevel.de', 'messages' => [], '_playhead' => 2]); - $store = new DefaultSnapshotStore(['memory' => $adapter->reveal()]); + $store = new DefaultSnapshotStore(['memory' => $adapter]); $store->load(ProfileWithSnapshot::class, ProfileId::fromString('1')); } @@ -99,17 +91,15 @@ public function testLoadExpiredSnapshot(): void { $this->expectException(SnapshotVersionInvalid::class); - $adapter = $this->prophesize(SnapshotAdapter::class); - $adapter->load( - 'profile_with_snapshot-1', - )->willReturn( + $adapter = $this->createMock(SnapshotAdapter::class); + $adapter->method('load')->with('profile_with_snapshot-1')->willReturn( [ 'version' => '2', 'payload' => ['id' => '1', 'email' => 'info@patchlevel.de', 'messages' => [], '_playhead' => 2], ], ); - $store = new DefaultSnapshotStore(['memory' => $adapter->reveal()]); + $store = new DefaultSnapshotStore(['memory' => $adapter]); $store->load(ProfileWithSnapshot::class, ProfileId::fromString('1')); } @@ -132,7 +122,7 @@ public function testSnapshotConfigIsMissing(): void public function testGetAdapter(): void { - $adapter = $this->prophesize(SnapshotAdapter::class)->reveal(); + $adapter = $this->createMock(SnapshotAdapter::class); $store = new DefaultSnapshotStore(['memory' => $adapter]); self::assertSame($adapter, $store->adapter(ProfileWithSnapshot::class)); diff --git a/tests/Unit/Store/ArrayStreamTest.php b/tests/Unit/Store/ArrayStreamTest.php index a041db55..35286bd2 100644 --- a/tests/Unit/Store/ArrayStreamTest.php +++ b/tests/Unit/Store/ArrayStreamTest.php @@ -12,15 +12,12 @@ use Patchlevel\EventSourcing\Tests\Unit\Fixture\ProfileId; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; use function iterator_to_array; #[CoversClass(ArrayStream::class)] final class ArrayStreamTest extends TestCase { - use ProphecyTrait; - public function testEmpty(): void { $stream = new ArrayStream(); diff --git a/tests/Unit/Store/DoctrineDbalStoreTest.php b/tests/Unit/Store/DoctrineDbalStoreTest.php index dd6af5ed..b6807935 100644 --- a/tests/Unit/Store/DoctrineDbalStoreTest.php +++ b/tests/Unit/Store/DoctrineDbalStoreTest.php @@ -5,8 +5,10 @@ namespace Patchlevel\EventSourcing\Tests\Unit\Store; use ArrayIterator; +use Closure; use DateTimeImmutable; use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Driver\PDO\Exception; use Doctrine\DBAL\Exception\UniqueConstraintViolationException; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Platforms\MariaDBPlatform; @@ -31,6 +33,7 @@ use Patchlevel\EventSourcing\Store\StreamStartHeader; use Patchlevel\EventSourcing\Store\UniqueConstraintViolation; use Patchlevel\EventSourcing\Store\WrongQueryResult; +use Patchlevel\EventSourcing\Tests\ReturnCallback; use Patchlevel\EventSourcing\Tests\Unit\Fixture\Email; use Patchlevel\EventSourcing\Tests\Unit\Fixture\Header\BazHeader; use Patchlevel\EventSourcing\Tests\Unit\Fixture\Header\FooHeader; @@ -41,8 +44,6 @@ use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\RequiresPhp; use PHPUnit\Framework\TestCase; -use Prophecy\Argument; -use Prophecy\PhpUnit\ProphecyTrait; use RuntimeException; use function iterator_to_array; @@ -51,43 +52,48 @@ #[CoversClass(DoctrineDbalStore::class)] final class DoctrineDbalStoreTest extends TestCase { - use ProphecyTrait; - public function testLoadWithNoEvents(): void { - $connection = $this->prophesize(Connection::class); - $result = $this->prophesize(Result::class); - $result->iterateAssociative()->willReturn(new EmptyIterator()); + $connection = $this->createMock(Connection::class); + $result = $this->createMock(Result::class); + $result + ->expects($this->once()) + ->method('iterateAssociative') + ->willReturn(new EmptyIterator()); - $connection->executeQuery( - 'SELECT * FROM eventstore WHERE (aggregate = :aggregate) AND (aggregate_id = :id) AND (playhead > :playhead) AND (archived = :archived) ORDER BY id ASC', - [ - 'aggregate' => 'profile', - 'id' => '1', - 'playhead' => 0, - 'archived' => false, - ], - Argument::type('array'), - )->willReturn($result->reveal()); + $connection + ->expects($this->once()) + ->method('executeQuery') + ->with( + 'SELECT * FROM eventstore WHERE (aggregate = :aggregate) AND (aggregate_id = :id) AND (playhead > :playhead) AND (archived = :archived) ORDER BY id ASC', + [ + 'aggregate' => 'profile', + 'id' => '1', + 'playhead' => 0, + 'archived' => false, + ], + $this->isArray(), + ) + ->willReturn($result); - $abstractPlatform = $this->prophesize(AbstractPlatform::class); - $abstractPlatform->createSelectSQLBuilder()->shouldBeCalledOnce()->willReturn(new DefaultSelectSQLBuilder( - $abstractPlatform->reveal(), + $abstractPlatform = $this->createMock(AbstractPlatform::class); + $abstractPlatform->expects($this->once())->method('createSelectSQLBuilder')->willReturn(new DefaultSelectSQLBuilder( + $abstractPlatform, 'FOR UPDATE', 'SKIP LOCKED', )); - $connection->getDatabasePlatform()->willReturn($abstractPlatform->reveal()); - $queryBuilder = new QueryBuilder($connection->reveal()); - $connection->createQueryBuilder()->willReturn($queryBuilder); + $connection->method('getDatabasePlatform')->willReturn($abstractPlatform); + $queryBuilder = new QueryBuilder($connection); + $connection->method('createQueryBuilder')->willReturn($queryBuilder); - $eventSerializer = $this->prophesize(EventSerializer::class); - $headersSerializer = $this->prophesize(HeadersSerializer::class); + $eventSerializer = $this->createMock(EventSerializer::class); + $headersSerializer = $this->createMock(HeadersSerializer::class); $doctrineDbalStore = new DoctrineDbalStore( - $connection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $connection, + $eventSerializer, + $headersSerializer, ); $stream = $doctrineDbalStore->load( @@ -105,39 +111,43 @@ public function testLoadWithNoEvents(): void public function testLoadWithLimit(): void { - $connection = $this->prophesize(Connection::class); - $result = $this->prophesize(Result::class); - $result->iterateAssociative()->willReturn(new EmptyIterator()); + $connection = $this->createMock(Connection::class); + $result = $this->createMock(Result::class); + $result->expects($this->once())->method('iterateAssociative')->willReturn(new EmptyIterator()); - $connection->executeQuery( - 'SELECT * FROM eventstore WHERE (aggregate = :aggregate) AND (aggregate_id = :id) AND (playhead > :playhead) AND (archived = :archived) ORDER BY id ASC LIMIT 10', - [ - 'aggregate' => 'profile', - 'id' => '1', - 'playhead' => 0, - 'archived' => false, - ], - Argument::type('array'), - )->willReturn($result->reveal()); + $connection + ->expects($this->once()) + ->method('executeQuery') + ->with( + 'SELECT * FROM eventstore WHERE (aggregate = :aggregate) AND (aggregate_id = :id) AND (playhead > :playhead) AND (archived = :archived) ORDER BY id ASC LIMIT 10', + [ + 'aggregate' => 'profile', + 'id' => '1', + 'playhead' => 0, + 'archived' => false, + ], + $this->isArray(), + ) + ->willReturn($result); - $abstractPlatform = $this->prophesize(AbstractPlatform::class); - $abstractPlatform->createSelectSQLBuilder()->shouldBeCalledOnce()->willReturn(new DefaultSelectSQLBuilder( - $abstractPlatform->reveal(), + $abstractPlatform = $this->createMock(AbstractPlatform::class); + $abstractPlatform->expects($this->once())->method('createSelectSQLBuilder')->willReturn(new DefaultSelectSQLBuilder( + $abstractPlatform, 'FOR UPDATE', 'SKIP LOCKED', )); - $connection->getDatabasePlatform()->willReturn($abstractPlatform->reveal()); - $queryBuilder = new QueryBuilder($connection->reveal()); - $connection->createQueryBuilder()->willReturn($queryBuilder); + $connection->expects($this->exactly(2))->method('getDatabasePlatform')->willReturn($abstractPlatform); + $queryBuilder = new QueryBuilder($connection); + $connection->expects($this->once())->method('createQueryBuilder')->willReturn($queryBuilder); - $eventSerializer = $this->prophesize(EventSerializer::class); - $headersSerializer = $this->prophesize(HeadersSerializer::class); + $eventSerializer = $this->createMock(EventSerializer::class); + $headersSerializer = $this->createMock(HeadersSerializer::class); $doctrineDbalStore = new DoctrineDbalStore( - $connection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $connection, + $eventSerializer, + $headersSerializer, ); $stream = $doctrineDbalStore->load( @@ -160,41 +170,40 @@ public function testLoadWithOffset(): void $this->markTestSkipped('In older DBAL versions platforms did not need to support this'); } - $connection = $this->prophesize(Connection::class); - $result = $this->prophesize(Result::class); - $result->iterateAssociative()->willReturn(new EmptyIterator()); + $connection = $this->createMock(Connection::class); + $result = $this->createMock(Result::class); + $result->expects($this->once())->method('iterateAssociative')->willReturn(new EmptyIterator()); - $connection->executeQuery( - 'SELECT * FROM eventstore WHERE (aggregate = :aggregate) AND (aggregate_id = :id) AND (playhead > :playhead) AND (archived = :archived) ORDER BY id ASC OFFSET 5', - [ - 'aggregate' => 'profile', - 'id' => '1', - 'playhead' => 0, - 'archived' => false, - ], - Argument::type('array'), - )->willReturn($result->reveal()); + $connection + ->expects($this->once()) + ->method('executeQuery') + ->with( + 'SELECT * FROM eventstore WHERE (aggregate = :aggregate) AND (aggregate_id = :id) AND (playhead > :playhead) AND (archived = :archived) ORDER BY id ASC OFFSET 5', + [ + 'aggregate' => 'profile', + 'id' => '1', + 'playhead' => 0, + 'archived' => false, + ], + $this->isArray(), + ) + ->willReturn($result); - $abstractPlatform = $this->prophesize(AbstractPlatform::class); - $abstractPlatform->createSelectSQLBuilder()->shouldBeCalledOnce()->willReturn(new DefaultSelectSQLBuilder( - $abstractPlatform->reveal(), + $abstractPlatform = $this->createMock(AbstractPlatform::class); + $abstractPlatform->expects($this->once())->method('createSelectSQLBuilder')->willReturn(new DefaultSelectSQLBuilder( + $abstractPlatform, 'FOR UPDATE', 'SKIP LOCKED', )); - $connection->getDatabasePlatform()->willReturn($abstractPlatform->reveal()); - $queryBuilder = new QueryBuilder($connection->reveal()); - $connection->createQueryBuilder()->willReturn($queryBuilder); + $connection->expects($this->exactly(2))->method('getDatabasePlatform')->willReturn($abstractPlatform); + $queryBuilder = new QueryBuilder($connection); + $connection->expects($this->once())->method('createQueryBuilder')->willReturn($queryBuilder); - $eventSerializer = $this->prophesize(EventSerializer::class); - $headersSerializer = $this->prophesize(HeadersSerializer::class); - - $doctrineDbalStore = new DoctrineDbalStore( - $connection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), - ); + $eventSerializer = $this->createMock(EventSerializer::class); + $headersSerializer = $this->createMock(HeadersSerializer::class); + $doctrineDbalStore = new DoctrineDbalStore($connection, $eventSerializer, $headersSerializer); $stream = $doctrineDbalStore->load( (new CriteriaBuilder()) ->aggregateName('profile') @@ -211,41 +220,41 @@ public function testLoadWithOffset(): void public function testLoadWithIndex(): void { - $connection = $this->prophesize(Connection::class); - $result = $this->prophesize(Result::class); - $result->iterateAssociative()->willReturn(new EmptyIterator()); + $connection = $this->createMock(Connection::class); + $result = $this->createMock(Result::class); + $result->expects($this->once())->method('iterateAssociative')->willReturn(new EmptyIterator()); - $connection->executeQuery( - 'SELECT * FROM eventstore WHERE (aggregate = :aggregate) AND (aggregate_id = :id) AND (playhead > :playhead) AND (id > :fromIndex) AND (archived = :archived) ORDER BY id ASC', - [ - 'aggregate' => 'profile', - 'id' => '1', - 'playhead' => 0, - 'archived' => false, - 'fromIndex' => 1, - ], - Argument::type('array'), - )->willReturn($result->reveal()); + $connection + ->expects($this->once()) + ->method('executeQuery') + ->with( + 'SELECT * FROM eventstore WHERE (aggregate = :aggregate) AND (aggregate_id = :id) AND (playhead > :playhead) AND (id > :fromIndex) AND (archived = :archived) ORDER BY id ASC', + [ + 'aggregate' => 'profile', + 'id' => '1', + 'playhead' => 0, + 'archived' => false, + 'fromIndex' => 1, + ], + $this->isArray(), + ) + ->willReturn($result); - $abstractPlatform = $this->prophesize(AbstractPlatform::class); - $abstractPlatform->createSelectSQLBuilder()->shouldBeCalledOnce()->willReturn(new DefaultSelectSQLBuilder( - $abstractPlatform->reveal(), + $abstractPlatform = $this->createMock(AbstractPlatform::class); + $abstractPlatform->expects($this->once())->method('createSelectSQLBuilder')->willReturn(new DefaultSelectSQLBuilder( + $abstractPlatform, 'FOR UPDATE', 'SKIP LOCKED', )); - $connection->getDatabasePlatform()->willReturn($abstractPlatform->reveal()); - $queryBuilder = new QueryBuilder($connection->reveal()); - $connection->createQueryBuilder()->willReturn($queryBuilder); + $connection->expects($this->exactly(2))->method('getDatabasePlatform')->willReturn($abstractPlatform); + $queryBuilder = new QueryBuilder($connection); + $connection->expects($this->once())->method('createQueryBuilder')->willReturn($queryBuilder); - $eventSerializer = $this->prophesize(EventSerializer::class); - $headersSerializer = $this->prophesize(HeadersSerializer::class); + $eventSerializer = $this->createMock(EventSerializer::class); + $headersSerializer = $this->createMock(HeadersSerializer::class); - $doctrineDbalStore = new DoctrineDbalStore( - $connection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), - ); + $doctrineDbalStore = new DoctrineDbalStore($connection, $eventSerializer, $headersSerializer); $stream = $doctrineDbalStore->load( (new CriteriaBuilder()) @@ -263,62 +272,73 @@ public function testLoadWithIndex(): void public function testLoadWithOneEvent(): void { - $connection = $this->prophesize(Connection::class); - $result = $this->prophesize(Result::class); - $result->iterateAssociative()->willReturn(new ArrayIterator( - [ + $connection = $this->createMock(Connection::class); + $result = $this->createMock(Result::class); + $result + ->expects($this->once()) + ->method('iterateAssociative') + ->willReturn(new ArrayIterator( [ - 'id' => 1, - 'aggregate' => 'profile', - 'aggregate_id' => '1', - 'playhead' => '1', - 'event' => 'profile.created', - 'payload' => '{"profileId": "1", "email": "s"}', - 'recorded_on' => '2021-02-17 10:00:00', - 'archived' => '0', - 'new_stream_start' => '0', - 'custom_headers' => '[]', + [ + 'id' => 1, + 'aggregate' => 'profile', + 'aggregate_id' => '1', + 'playhead' => '1', + 'event' => 'profile.created', + 'payload' => '{"profileId": "1", "email": "s"}', + 'recorded_on' => '2021-02-17 10:00:00', + 'archived' => '0', + 'new_stream_start' => '0', + 'custom_headers' => '[]', + ], ], - ], - )); + )); - $connection->executeQuery( - 'SELECT * FROM eventstore WHERE (aggregate = :aggregate) AND (aggregate_id = :id) AND (playhead > :playhead) AND (archived = :archived) ORDER BY id ASC', - [ - 'aggregate' => 'profile', - 'id' => '1', - 'playhead' => 0, - 'archived' => false, - ], - Argument::type('array'), - )->willReturn($result->reveal()); + $connection + ->expects($this->once()) + ->method('executeQuery') + ->with( + 'SELECT * FROM eventstore WHERE (aggregate = :aggregate) AND (aggregate_id = :id) AND (playhead > :playhead) AND (archived = :archived) ORDER BY id ASC', + [ + 'aggregate' => 'profile', + 'id' => '1', + 'playhead' => 0, + 'archived' => false, + ], + $this->isArray(), + ) + ->willReturn($result); - $abstractPlatform = $this->prophesize(AbstractPlatform::class); + $abstractPlatform = $this->createMock(AbstractPlatform::class); - $abstractPlatform->createSelectSQLBuilder()->shouldBeCalledOnce()->willReturn(new DefaultSelectSQLBuilder( - $abstractPlatform->reveal(), + $abstractPlatform->expects($this->once())->method('createSelectSQLBuilder')->willReturn(new DefaultSelectSQLBuilder( + $abstractPlatform, 'FOR UPDATE', 'SKIP LOCKED', )); - $abstractPlatform->getDateTimeTzFormatString()->shouldBeCalledOnce()->willReturn('Y-m-d H:i:s'); + $abstractPlatform->expects($this->once())->method('getDateTimeTzFormatString')->willReturn('Y-m-d H:i:s'); - $connection->getDatabasePlatform()->willReturn($abstractPlatform->reveal()); + $connection->expects($this->exactly(2))->method('getDatabasePlatform')->willReturn($abstractPlatform); - $queryBuilder = new QueryBuilder($connection->reveal()); - $connection->createQueryBuilder()->willReturn($queryBuilder); + $queryBuilder = new QueryBuilder($connection); + $connection->expects($this->once())->method('createQueryBuilder')->willReturn($queryBuilder); - $eventSerializer = $this->prophesize(EventSerializer::class); - $eventSerializer->deserialize( - new SerializedEvent('profile.created', '{"profileId": "1", "email": "s"}'), - )->willReturn(new ProfileCreated(ProfileId::fromString('1'), Email::fromString('s'))); + $eventSerializer = $this->createMock(EventSerializer::class); + $eventSerializer->expects($this->once())->method('deserialize')->with(new SerializedEvent( + 'profile.created', + '{"profileId": "1", "email": "s"}', + ))->willReturn(new ProfileCreated( + ProfileId::fromString('1'), + Email::fromString('s'), + )); - $headersSerializer = $this->prophesize(HeadersSerializer::class); - $headersSerializer->deserialize('[]')->willReturn([]); + $headersSerializer = $this->createMock(HeadersSerializer::class); + $headersSerializer->expects($this->once())->method('deserialize')->with('[]')->willReturn([]); $doctrineDbalStore = new DoctrineDbalStore( - $connection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $connection, + $eventSerializer, + $headersSerializer, ); $stream = $doctrineDbalStore->load( @@ -355,76 +375,94 @@ public function testLoadWithOneEvent(): void public function testLoadWithTwoEvents(): void { - $connection = $this->prophesize(Connection::class); - $result = $this->prophesize(Result::class); - $result->iterateAssociative()->willReturn(new ArrayIterator( - [ + $connection = $this->createMock(Connection::class); + $result = $this->createMock(Result::class); + $result + ->expects($this->once()) + ->method('iterateAssociative') + ->willReturn(new ArrayIterator( [ - 'id' => 1, - 'aggregate' => 'profile', - 'aggregate_id' => '1', - 'playhead' => '1', - 'event' => 'profile.created', - 'payload' => '{"profileId": "1", "email": "s"}', - 'recorded_on' => '2021-02-17 10:00:00', - 'archived' => '0', - 'new_stream_start' => '0', - 'custom_headers' => '[]', + [ + 'id' => 1, + 'aggregate' => 'profile', + 'aggregate_id' => '1', + 'playhead' => '1', + 'event' => 'profile.created', + 'payload' => '{"profileId": "1", "email": "s"}', + 'recorded_on' => '2021-02-17 10:00:00', + 'archived' => '0', + 'new_stream_start' => '0', + 'custom_headers' => '[]', + ], + [ + 'id' => 2, + 'aggregate' => 'profile', + 'aggregate_id' => '1', + 'playhead' => '2', + 'event' => 'profile.email_changed', + 'payload' => '{"profileId": "1", "email": "d"}', + 'recorded_on' => '2021-02-17 11:00:00', + 'archived' => '0', + 'new_stream_start' => '0', + 'custom_headers' => '[]', + ], ], + )); + + $connection + ->expects($this->once()) + ->method('executeQuery') + ->with( + 'SELECT * FROM eventstore WHERE (aggregate = :aggregate) AND (aggregate_id = :id) AND (playhead > :playhead) AND (archived = :archived) ORDER BY id ASC', [ - 'id' => 2, 'aggregate' => 'profile', - 'aggregate_id' => '1', - 'playhead' => '2', - 'event' => 'profile.email_changed', - 'payload' => '{"profileId": "1", "email": "d"}', - 'recorded_on' => '2021-02-17 11:00:00', - 'archived' => '0', - 'new_stream_start' => '0', - 'custom_headers' => '[]', + 'id' => '1', + 'playhead' => 0, + 'archived' => false, ], - ], - )); + $this->isArray(), + ) + ->willReturn($result); - $connection->executeQuery( - 'SELECT * FROM eventstore WHERE (aggregate = :aggregate) AND (aggregate_id = :id) AND (playhead > :playhead) AND (archived = :archived) ORDER BY id ASC', - [ - 'aggregate' => 'profile', - 'id' => '1', - 'playhead' => 0, - 'archived' => false, - ], - Argument::type('array'), - )->willReturn($result->reveal()); - - $abstractPlatform = $this->prophesize(AbstractPlatform::class); - $abstractPlatform->createSelectSQLBuilder()->shouldBeCalledOnce()->willReturn(new DefaultSelectSQLBuilder( - $abstractPlatform->reveal(), + $abstractPlatform = $this->createMock(AbstractPlatform::class); + $abstractPlatform->expects($this->once())->method('createSelectSQLBuilder')->willReturn(new DefaultSelectSQLBuilder( + $abstractPlatform, 'FOR UPDATE', 'SKIP LOCKED', )); - $abstractPlatform->getDateTimeTzFormatString()->shouldBeCalledTimes(2)->willReturn('Y-m-d H:i:s'); + $abstractPlatform->expects($this->exactly(2))->method('getDateTimeTzFormatString')->willReturn('Y-m-d H:i:s'); - $connection->getDatabasePlatform()->willReturn($abstractPlatform->reveal()); + $connection->expects($this->exactly(2))->method('getDatabasePlatform')->willReturn($abstractPlatform); - $queryBuilder = new QueryBuilder($connection->reveal()); - $connection->createQueryBuilder()->willReturn($queryBuilder); + $queryBuilder = new QueryBuilder($connection); + $connection->expects($this->once())->method('createQueryBuilder')->willReturn($queryBuilder); - $eventSerializer = $this->prophesize(EventSerializer::class); - $eventSerializer->deserialize( - new SerializedEvent('profile.created', '{"profileId": "1", "email": "s"}'), - )->willReturn(new ProfileCreated(ProfileId::fromString('1'), Email::fromString('s'))); - $eventSerializer->deserialize( - new SerializedEvent('profile.email_changed', '{"profileId": "1", "email": "d"}'), - )->willReturn(new ProfileEmailChanged(ProfileId::fromString('1'), Email::fromString('d'))); + $eventSerializer = $this->createMock(EventSerializer::class); + $eventSerializer + ->expects($this->exactly(2)) + ->method('deserialize') + ->willReturnCallback(new ReturnCallback([ + [ + [new SerializedEvent('profile.created', '{"profileId": "1", "email": "s"}'), []], + new ProfileCreated(ProfileId::fromString('1'), Email::fromString('s')), + ], + [ + [new SerializedEvent('profile.email_changed', '{"profileId": "1", "email": "d"}'), []], + new ProfileEmailChanged(ProfileId::fromString('1'), Email::fromString('d')), + ], + ])); - $headersSerializer = $this->prophesize(HeadersSerializer::class); - $headersSerializer->deserialize('[]')->willReturn([]); + $headersSerializer = $this->createMock(HeadersSerializer::class); + $headersSerializer + ->expects($this->exactly(2)) + ->method('deserialize') + ->with('[]') + ->willReturn([]); $doctrineDbalStore = new DoctrineDbalStore( - $connection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $connection, + $eventSerializer, + $headersSerializer, ); $stream = $doctrineDbalStore->load( @@ -480,20 +518,19 @@ public function __invoke(): void } }; - $connection = $this->prophesize(Connection::class); - $connection->getDatabasePlatform()->willReturn(new SQLitePlatform()); - $connection->transactional(Argument::any())->will( - /** @param array{0: callable} $args */ - static fn (array $args): mixed => $args[0](), - )->shouldBeCalled(); + $connection = $this->createMock(Connection::class); + $connection->expects($this->exactly(2))->method('getDatabasePlatform')->willReturn(new SQLitePlatform()); + $connection->expects($this->atLeastOnce())->method('transactional')->willReturnCallback( + static fn (Closure $closure): mixed => $closure(), + ); - $eventSerializer = $this->prophesize(EventSerializer::class); - $headersSerializer = $this->prophesize(HeadersSerializer::class); + $eventSerializer = $this->createMock(EventSerializer::class); + $headersSerializer = $this->createMock(HeadersSerializer::class); $store = new DoctrineDbalStore( - $connection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $connection, + $eventSerializer, + $headersSerializer, ); $store->transactional($callback(...)); @@ -512,23 +549,33 @@ public function __invoke(): void } }; - $connection = $this->prophesize(Connection::class); - $connection->getDatabasePlatform()->willReturn(new MySQLPlatform()); - $connection->fetchAllAssociative('SELECT GET_LOCK("133742", -1)')->shouldBeCalledOnce()->willReturn([]); - $connection->fetchAllAssociative('SELECT RELEASE_LOCK("133742")')->shouldBeCalledOnce()->willReturn([]); - - $connection->transactional(Argument::any())->will( - /** @param array{0: callable} $args */ - static fn (array $args): mixed => $args[0](), - )->shouldBeCalled(); - - $eventSerializer = $this->prophesize(EventSerializer::class); - $headersSerializer = $this->prophesize(HeadersSerializer::class); + $connection = $this->createMock(Connection::class); + $connection + ->expects($this->exactly(2)) + ->method('getDatabasePlatform') + ->willReturn(new MySQLPlatform()); + $connection + ->expects($this->exactly(2)) + ->method('fetchAllAssociative') + ->willReturnMap([ + ['SELECT GET_LOCK("133742", -1)', []], + ['SELECT RELEASE_LOCK("133742")', []], + ]); + + $connection + ->expects($this->atLeastOnce()) + ->method('transactional') + ->willReturnCallback( + static fn (Closure $closure): mixed => $closure(), + ); + + $eventSerializer = $this->createMock(EventSerializer::class); + $headersSerializer = $this->createMock(HeadersSerializer::class); $store = new DoctrineDbalStore( - $connection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $connection, + $eventSerializer, + $headersSerializer, ); $store->transactional($callback(...)); @@ -547,23 +594,26 @@ public function __invoke(): void } }; - $connection = $this->prophesize(Connection::class); - $connection->getDatabasePlatform()->willReturn(new MariaDBPlatform()); - $connection->fetchAllAssociative('SELECT GET_LOCK("133742", -1)')->shouldBeCalledOnce()->willReturn([]); - $connection->fetchAllAssociative('SELECT RELEASE_LOCK("133742")')->shouldBeCalledOnce()->willReturn([]); - - $connection->transactional(Argument::any())->will( - /** @param array{0: callable} $args */ - static fn (array $args): mixed => $args[0](), - )->shouldBeCalled(); - - $eventSerializer = $this->prophesize(EventSerializer::class); - $headersSerializer = $this->prophesize(HeadersSerializer::class); + $connection = $this->createMock(Connection::class); + $connection->expects($this->exactly(2))->method('getDatabasePlatform')->willReturn(new MariaDBPlatform()); + $connection + ->expects($this->exactly(2)) + ->method('fetchAllAssociative') + ->willReturnMap([ + ['SELECT GET_LOCK("133742", -1)', []], + ['SELECT RELEASE_LOCK("133742")', []], + ]); + + $connection->expects($this->atLeastOnce())->method('transactional')->willReturnCallback( + static fn (Closure $closure): mixed => $closure(), + ); + $eventSerializer = $this->createMock(EventSerializer::class); + $headersSerializer = $this->createMock(HeadersSerializer::class); $store = new DoctrineDbalStore( - $connection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $connection, + $eventSerializer, + $headersSerializer, ); $store->transactional($callback(...)); @@ -582,22 +632,21 @@ public function __invoke(): void } }; - $connection = $this->prophesize(Connection::class); - $connection->getDatabasePlatform()->willReturn(new PostgreSQLPlatform()); - $connection->executeStatement('SELECT pg_advisory_xact_lock(133742)')->shouldBeCalledOnce(); + $connection = $this->createMock(Connection::class); + $connection->expects($this->exactly(2))->method('getDatabasePlatform')->willReturn(new PostgreSQLPlatform()); + $connection->expects($this->once())->method('executeStatement')->with('SELECT pg_advisory_xact_lock(133742)'); - $connection->transactional(Argument::any())->will( - /** @param array{0: callable} $args */ - static fn (array $args): mixed => $args[0](), - )->shouldBeCalled(); + $connection->expects($this->atLeastOnce())->method('transactional')->willReturnCallback( + static fn (Closure $closure): mixed => $closure(), + ); - $eventSerializer = $this->prophesize(EventSerializer::class); - $headersSerializer = $this->prophesize(HeadersSerializer::class); + $eventSerializer = $this->createMock(EventSerializer::class); + $headersSerializer = $this->createMock(HeadersSerializer::class); $store = new DoctrineDbalStore( - $connection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $connection, + $eventSerializer, + $headersSerializer, ); $store->transactional($callback(...)); @@ -616,22 +665,21 @@ public function __invoke(): void } }; - $connection = $this->prophesize(Connection::class); - $connection->getDatabasePlatform()->willReturn(new PostgreSQLPlatform()); - $connection->executeStatement('SELECT pg_advisory_xact_lock(133742)')->shouldBeCalledOnce(); + $connection = $this->createMock(Connection::class); + $connection->expects($this->exactly(2))->method('getDatabasePlatform')->willReturn(new PostgreSQLPlatform()); + $connection->expects($this->once())->method('executeStatement')->with('SELECT pg_advisory_xact_lock(133742)'); - $connection->transactional(Argument::any())->will( - /** @param array{0: callable} $args */ - static fn (array $args): mixed => $args[0](), - )->shouldBeCalledTimes(2); + $connection->expects($this->exactly(2))->method('transactional')->willReturnCallback( + static fn (Closure $closure): mixed => $closure(), + ); - $eventSerializer = $this->prophesize(EventSerializer::class); - $headersSerializer = $this->prophesize(HeadersSerializer::class); + $eventSerializer = $this->createMock(EventSerializer::class); + $headersSerializer = $this->createMock(HeadersSerializer::class); $store = new DoctrineDbalStore( - $connection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $connection, + $eventSerializer, + $headersSerializer, ); $store->transactional(static function () use ($store, $callback): void { @@ -652,22 +700,21 @@ public function __invoke(): void } }; - $connection = $this->prophesize(Connection::class); - $connection->getDatabasePlatform()->willReturn(new PostgreSQLPlatform()); - $connection->executeStatement('SELECT pg_advisory_xact_lock(133742)')->shouldBeCalledTimes(2); + $connection = $this->createMock(Connection::class); + $connection->expects($this->exactly(4))->method('getDatabasePlatform')->willReturn(new PostgreSQLPlatform()); + $connection->expects($this->exactly(2))->method('executeStatement')->with('SELECT pg_advisory_xact_lock(133742)'); - $connection->transactional(Argument::any())->will( - /** @param array{0: callable} $args */ - static fn (array $args): mixed => $args[0](), - )->shouldBeCalled(); + $connection->expects($this->exactly(2))->method('transactional')->willReturnCallback( + static fn (Closure $closure): mixed => $closure(), + ); - $eventSerializer = $this->prophesize(EventSerializer::class); - $headersSerializer = $this->prophesize(HeadersSerializer::class); + $eventSerializer = $this->createMock(EventSerializer::class); + $headersSerializer = $this->createMock(HeadersSerializer::class); $store = new DoctrineDbalStore( - $connection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $connection, + $eventSerializer, + $headersSerializer, ); $store->transactional($callback(...)); @@ -685,23 +732,27 @@ public function __invoke(): void } }; - $connection = $this->prophesize(Connection::class); - $connection->getDatabasePlatform()->willReturn(new MariaDBPlatform()); - $connection->fetchAllAssociative('SELECT GET_LOCK("133742", -1)')->shouldBeCalledOnce()->willReturn([]); - $connection->fetchAllAssociative('SELECT RELEASE_LOCK("133742")')->shouldBeCalledOnce()->willReturn([]); - - $connection->transactional(Argument::any())->will( - /** @param array{0: callable} $args */ - static fn (array $args): mixed => $args[0](), - )->shouldBeCalled(); + $connection = $this->createMock(Connection::class); + $connection->expects($this->exactly(2))->method('getDatabasePlatform')->willReturn(new MariaDBPlatform()); + $connection + ->expects($this->exactly(2)) + ->method('fetchAllAssociative') + ->willReturnMap([ + ['SELECT GET_LOCK("133742", -1)', []], + ['SELECT RELEASE_LOCK("133742")', []], + ]); + + $connection->expects($this->once())->method('transactional')->willReturnCallback( + static fn (Closure $closure): mixed => $closure(), + ); - $eventSerializer = $this->prophesize(EventSerializer::class); - $headersSerializer = $this->prophesize(HeadersSerializer::class); + $eventSerializer = $this->createMock(EventSerializer::class); + $headersSerializer = $this->createMock(HeadersSerializer::class); $store = new DoctrineDbalStore( - $connection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $connection, + $eventSerializer, + $headersSerializer, ); $this->expectException(RuntimeException::class); @@ -720,23 +771,22 @@ public function testSaveWithOneEvent(): void $recordedOn, )); - $eventSerializer = $this->prophesize(EventSerializer::class); - $eventSerializer->serialize($message->event())->shouldBeCalledOnce()->willReturn(new SerializedEvent( + $eventSerializer = $this->createMock(EventSerializer::class); + $eventSerializer->expects($this->once())->method('serialize')->with($message->event())->willReturn(new SerializedEvent( 'profile_created', '', )); - $headersSerializer = $this->prophesize(HeadersSerializer::class); - $headersSerializer->serialize([])->willReturn('[]'); + $headersSerializer = $this->createMock(HeadersSerializer::class); + $headersSerializer->expects($this->once())->method('serialize')->with([])->willReturn('[]'); - $mockedConnection = $this->prophesize(Connection::class); - $mockedConnection->getDatabasePlatform()->willReturn(new SQLitePlatform()); - $mockedConnection->transactional(Argument::any())->will( - /** @param array{0: callable} $args */ - static fn (array $args): mixed => $args[0](), + $mockedConnection = $this->createMock(Connection::class); + $mockedConnection->expects($this->exactly(2))->method('getDatabasePlatform')->willReturn(new SQLitePlatform()); + $mockedConnection->expects($this->once())->method('transactional')->willReturnCallback( + static fn (Closure $closure): mixed => $closure(), ); - $mockedConnection->executeStatement( + $mockedConnection->expects($this->once())->method('executeStatement')->with( "INSERT INTO eventstore (aggregate, aggregate_id, playhead, event, payload, recorded_on, new_stream_start, archived, custom_headers) VALUES\n(?, ?, ?, ?, ?, ?, ?, ?, ?)", ['profile', '1', 1, 'profile_created', '', $recordedOn, false, false, '[]'], [ @@ -744,12 +794,12 @@ public function testSaveWithOneEvent(): void 6 => Type::getType(Types::BOOLEAN), 7 => Type::getType(Types::BOOLEAN), ], - )->shouldBeCalledOnce(); + ); $singleTableStore = new DoctrineDbalStore( - $mockedConnection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $mockedConnection, + $eventSerializer, + $headersSerializer, ); $singleTableStore->save($message); } @@ -759,36 +809,42 @@ public function testSaveWithoutAggregateHeader(): void $recordedOn = new DateTimeImmutable(); $message = Message::create(new ProfileCreated(ProfileId::fromString('1'), Email::fromString('s'))); - $eventSerializer = $this->prophesize(EventSerializer::class); - $eventSerializer->serialize($message->event())->shouldBeCalledOnce()->willReturn(new SerializedEvent( - 'profile_created', - '', - )); - - $headersSerializer = $this->prophesize(HeadersSerializer::class); - $headersSerializer->serialize([])->willReturn('[]'); - - $mockedConnection = $this->prophesize(Connection::class); - $mockedConnection->getDatabasePlatform()->willReturn(new SQLitePlatform()); - $mockedConnection->transactional(Argument::any())->will( - /** @param array{0: callable} $args */ - static fn (array $args): mixed => $args[0](), + $eventSerializer = $this->createMock(EventSerializer::class); + $eventSerializer + ->expects($this->once()) + ->method('serialize') + ->with($message->event()) + ->willReturn(new SerializedEvent('profile_created', '')); + + $headersSerializer = $this->createMock(HeadersSerializer::class); + $headersSerializer + ->expects($this->never()) + ->method('serialize') + ->with([]) + ->willReturn('[]'); + + $mockedConnection = $this->createMock(Connection::class); + $mockedConnection->expects($this->exactly(2))->method('getDatabasePlatform')->willReturn(new SQLitePlatform()); + $mockedConnection->expects($this->once())->method('transactional')->willReturnCallback( + static fn (Closure $closure): mixed => $closure(), ); - - $mockedConnection->executeStatement( - "INSERT INTO eventstore (aggregate, aggregate_id, playhead, event, payload, recorded_on, new_stream_start, archived, custom_headers) VALUES\n(?, ?, ?, ?, ?, ?, ?, ?, ?)", - ['profile', '1', 1, 'profile_created', '', $recordedOn, false, false, []], - [ - 5 => Type::getType(Types::DATETIMETZ_IMMUTABLE), - 6 => Type::getType(Types::BOOLEAN), - 7 => Type::getType(Types::BOOLEAN), - ], - )->shouldNotBeCalled(); + $mockedConnection + ->expects($this->never()) + ->method('executeStatement') + ->with( + "INSERT INTO eventstore (aggregate, aggregate_id, playhead, event, payload, recorded_on, new_stream_start, archived, custom_headers) VALUES\n(?, ?, ?, ?, ?, ?, ?, ?, ?)", + ['profile', '1', 1, 'profile_created', '', $recordedOn, false, false, []], + [ + 5 => Type::getType(Types::DATETIMETZ_IMMUTABLE), + 6 => Type::getType(Types::BOOLEAN), + 7 => Type::getType(Types::BOOLEAN), + ], + ); $singleTableStore = new DoctrineDbalStore( - $mockedConnection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $mockedConnection, + $eventSerializer, + $headersSerializer, ); $this->expectException(MissingDataForStorage::class); @@ -813,27 +869,31 @@ public function testSaveWithTwoEvents(): void $recordedOn, )); - $eventSerializer = $this->prophesize(EventSerializer::class); - $eventSerializer->serialize($message1->event())->shouldBeCalledOnce()->willReturn(new SerializedEvent( - 'profile_created', - '', - )); - $eventSerializer->serialize($message2->event())->shouldBeCalledOnce()->willReturn(new SerializedEvent( - 'profile_email_changed', - '', - )); + $eventSerializer = $this->createMock(EventSerializer::class); + $eventSerializer + ->expects($this->exactly(2)) + ->method('serialize') + ->willReturnCallback(new ReturnCallback([ + [ + [$message1->event(), []], + new SerializedEvent('profile_created', ''), + ], + [ + [$message2->event(), []], + new SerializedEvent('profile_email_changed', ''), + ], + ])); - $headersSerializer = $this->prophesize(HeadersSerializer::class); - $headersSerializer->serialize([])->willReturn('[]'); + $headersSerializer = $this->createMock(HeadersSerializer::class); + $headersSerializer->expects($this->exactly(2))->method('serialize')->with([])->willReturn('[]'); - $mockedConnection = $this->prophesize(Connection::class); - $mockedConnection->getDatabasePlatform()->willReturn(new SQLitePlatform()); - $mockedConnection->transactional(Argument::any())->will( - /** @param array{0: callable} $args */ - static fn (array $args): mixed => $args[0](), + $mockedConnection = $this->createMock(Connection::class); + $mockedConnection->expects($this->exactly(2))->method('getDatabasePlatform')->willReturn(new SQLitePlatform()); + $mockedConnection->expects($this->once())->method('transactional')->willReturnCallback( + static fn (Closure $closure): mixed => $closure(), ); - $mockedConnection->executeStatement( + $mockedConnection->expects($this->once())->method('executeStatement')->with( "INSERT INTO eventstore (aggregate, aggregate_id, playhead, event, payload, recorded_on, new_stream_start, archived, custom_headers) VALUES\n(?, ?, ?, ?, ?, ?, ?, ?, ?),\n(?, ?, ?, ?, ?, ?, ?, ?, ?)", [ 'profile', @@ -863,12 +923,12 @@ public function testSaveWithTwoEvents(): void 15 => Type::getType(Types::BOOLEAN), 16 => Type::getType(Types::BOOLEAN), ], - )->shouldBeCalledOnce(); + ); $singleTableStore = new DoctrineDbalStore( - $mockedConnection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $mockedConnection, + $eventSerializer, + $headersSerializer, ); $singleTableStore->save($message1, $message2); } @@ -891,58 +951,73 @@ public function testSaveWithUniqueConstraintViolation(): void $recordedOn, )); - $eventSerializer = $this->prophesize(EventSerializer::class); - $eventSerializer->serialize($message1->event())->shouldBeCalledTimes(2)->willReturn(new SerializedEvent( - 'profile_created', - '', - )); - - $headersSerializer = $this->prophesize(HeadersSerializer::class); - $headersSerializer->serialize([])->willReturn('[]'); - - $mockedConnection = $this->prophesize(Connection::class); - $mockedConnection->getDatabasePlatform()->willReturn(new SQLitePlatform()); - $mockedConnection->transactional(Argument::any())->will( - /** @param array{0: callable} $args */ - static fn (array $args): mixed => $args[0](), - ); - - $mockedConnection->executeStatement( - "INSERT INTO eventstore (aggregate, aggregate_id, playhead, event, payload, recorded_on, new_stream_start, archived, custom_headers) VALUES\n(?, ?, ?, ?, ?, ?, ?, ?, ?),\n(?, ?, ?, ?, ?, ?, ?, ?, ?)", - [ - 'profile', - '1', - 1, + $eventSerializer = $this->createMock(EventSerializer::class); + $eventSerializer + ->expects($this->exactly(2)) + ->method('serialize') + ->with($message1->event()) + ->willReturn(new SerializedEvent( 'profile_created', '', - $recordedOn, - false, - false, - '[]', - 'profile', - '1', - 1, - 'profile_created', - '', - $recordedOn, - false, - false, - '[]', - ], - [ - 5 => Type::getType(Types::DATETIMETZ_IMMUTABLE), - 6 => Type::getType(Types::BOOLEAN), - 7 => Type::getType(Types::BOOLEAN), - 14 => Type::getType(Types::DATETIMETZ_IMMUTABLE), - 15 => Type::getType(Types::BOOLEAN), - 16 => Type::getType(Types::BOOLEAN), - ], - )->shouldBeCalledOnce()->willThrow(UniqueConstraintViolationException::class); + )); + + $headersSerializer = $this->createMock(HeadersSerializer::class); + $headersSerializer + ->expects($this->exactly(2)) + ->method('serialize') + ->with([]) + ->willReturn('[]'); + + $mockedConnection = $this->createMock(Connection::class); + $mockedConnection + ->expects($this->exactly(2)) + ->method('getDatabasePlatform') + ->willReturn(new SQLitePlatform()); + $mockedConnection + ->expects($this->once()) + ->method('transactional') + ->willReturnCallback(static fn (Closure $closure): mixed => $closure()); + + $mockedConnection + ->expects($this->once()) + ->method('executeStatement') + ->with( + "INSERT INTO eventstore (aggregate, aggregate_id, playhead, event, payload, recorded_on, new_stream_start, archived, custom_headers) VALUES\n(?, ?, ?, ?, ?, ?, ?, ?, ?),\n(?, ?, ?, ?, ?, ?, ?, ?, ?)", + [ + 'profile', + '1', + 1, + 'profile_created', + '', + $recordedOn, + false, + false, + '[]', + 'profile', + '1', + 1, + 'profile_created', + '', + $recordedOn, + false, + false, + '[]', + ], + [ + 5 => Type::getType(Types::DATETIMETZ_IMMUTABLE), + 6 => Type::getType(Types::BOOLEAN), + 7 => Type::getType(Types::BOOLEAN), + 14 => Type::getType(Types::DATETIMETZ_IMMUTABLE), + 15 => Type::getType(Types::BOOLEAN), + 16 => Type::getType(Types::BOOLEAN), + ], + ) + ->willThrowException(new UniqueConstraintViolationException(new Exception('foo'), null)); $singleTableStore = new DoctrineDbalStore( - $mockedConnection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $mockedConnection, + $eventSerializer, + $headersSerializer, ); $this->expectException(UniqueConstraintViolation::class); @@ -964,29 +1039,36 @@ public function testSaveWithThousandEvents(): void )); } - $eventSerializer = $this->prophesize(EventSerializer::class); - $eventSerializer->serialize($messages[0]->event())->shouldBeCalledTimes(10000)->willReturn(new SerializedEvent( - 'profile_email_changed', - '', - )); - - $headersSerializer = $this->prophesize(HeadersSerializer::class); - $headersSerializer->serialize([])->willReturn('[]'); - - $mockedConnection = $this->prophesize(Connection::class); - $mockedConnection->getDatabasePlatform()->willReturn(new SQLitePlatform()); - $mockedConnection->transactional(Argument::any())->will( - /** @param array{0: callable} $args */ - static fn (array $args): mixed => $args[0](), - ); + $eventSerializer = $this->createMock(EventSerializer::class); + $eventSerializer + ->expects($this->exactly(10000)) + ->method('serialize') + ->with($messages[0]->event()) + ->willReturn(new SerializedEvent('profile_email_changed', '')); + + $headersSerializer = $this->createMock(HeadersSerializer::class); + $headersSerializer + ->expects($this->exactly(10000)) + ->method('serialize') + ->with([]) + ->willReturn('[]'); + + $mockedConnection = $this->createMock(Connection::class); + $mockedConnection + ->expects($this->exactly(2)) + ->method('getDatabasePlatform') + ->willReturn(new SQLitePlatform()); + $mockedConnection + ->expects($this->once()) + ->method('transactional') + ->willReturnCallback(static fn (Closure $closure): mixed => $closure()); - $mockedConnection->executeStatement(Argument::any(), Argument::any(), Argument::any()) - ->shouldBeCalledTimes(2); + $mockedConnection->expects($this->exactly(2))->method('executeStatement'); $singleTableStore = new DoctrineDbalStore( - $mockedConnection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $mockedConnection, + $eventSerializer, + $headersSerializer, ); $singleTableStore->save(...$messages); } @@ -1008,72 +1090,87 @@ public function testSaveWithCustomHeaders(): void )) ->withHeaders($customHeaders); - $eventSerializer = $this->prophesize(EventSerializer::class); - $eventSerializer->serialize($message->event())->shouldBeCalledOnce()->willReturn(new SerializedEvent( - 'profile_created', - '', - )); - - $headersSerializer = $this->prophesize(HeadersSerializer::class); - $headersSerializer->serialize($customHeaders)->willReturn('{foo: "foo", baz: "baz"}'); + $eventSerializer = $this->createMock(EventSerializer::class); + $eventSerializer + ->expects($this->once()) + ->method('serialize') + ->with($message->event()) + ->willReturn(new SerializedEvent('profile_created', '')); - $mockedConnection = $this->prophesize(Connection::class); - $mockedConnection->getDatabasePlatform()->willReturn(new SQLitePlatform()); - $mockedConnection->transactional(Argument::any())->will( - /** @param array{0: callable} $args */ - static fn (array $args): mixed => $args[0](), - ); + $headersSerializer = $this->createMock(HeadersSerializer::class); + $headersSerializer + ->expects($this->once()) + ->method('serialize') + ->with($customHeaders) + ->willReturn('{foo: "foo", baz: "baz"}'); + + $mockedConnection = $this->createMock(Connection::class); + $mockedConnection + ->expects($this->exactly(2)) + ->method('getDatabasePlatform') + ->willReturn(new SQLitePlatform()); + $mockedConnection + ->expects($this->once()) + ->method('transactional') + ->willReturnCallback(static fn (Closure $closure): mixed => $closure()); - $mockedConnection->executeStatement( - "INSERT INTO eventstore (aggregate, aggregate_id, playhead, event, payload, recorded_on, new_stream_start, archived, custom_headers) VALUES\n(?, ?, ?, ?, ?, ?, ?, ?, ?)", - ['profile', '1', 1, 'profile_created', '', $recordedOn, false, false, '{foo: "foo", baz: "baz"}'], - [ - 5 => Type::getType(Types::DATETIMETZ_IMMUTABLE), - 6 => Type::getType(Types::BOOLEAN), - 7 => Type::getType(Types::BOOLEAN), - ], - )->shouldBeCalledOnce(); + $mockedConnection + ->expects($this->once()) + ->method('executeStatement') + ->with( + "INSERT INTO eventstore (aggregate, aggregate_id, playhead, event, payload, recorded_on, new_stream_start, archived, custom_headers) VALUES\n(?, ?, ?, ?, ?, ?, ?, ?, ?)", + ['profile', '1', 1, 'profile_created', '', $recordedOn, false, false, '{foo: "foo", baz: "baz"}'], + [ + 5 => Type::getType(Types::DATETIMETZ_IMMUTABLE), + 6 => Type::getType(Types::BOOLEAN), + 7 => Type::getType(Types::BOOLEAN), + ], + ); $singleTableStore = new DoctrineDbalStore( - $mockedConnection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $mockedConnection, + $eventSerializer, + $headersSerializer, ); $singleTableStore->save($message); } public function testCount(): void { - $connection = $this->prophesize(Connection::class); - $connection->fetchOne( - 'SELECT COUNT(*) FROM eventstore WHERE (aggregate = :aggregate) AND (aggregate_id = :id) AND (playhead > :playhead) AND (archived = :archived)', - [ - 'aggregate' => 'profile', - 'id' => '1', - 'playhead' => 0, - 'archived' => false, - ], - Argument::type('array'), - )->willReturn('1'); + $connection = $this->createMock(Connection::class); + $connection + ->expects($this->once()) + ->method('fetchOne') + ->with( + 'SELECT COUNT(*) FROM eventstore WHERE (aggregate = :aggregate) AND (aggregate_id = :id) AND (playhead > :playhead) AND (archived = :archived)', + [ + 'aggregate' => 'profile', + 'id' => '1', + 'playhead' => 0, + 'archived' => false, + ], + $this->isArray(), + ) + ->willReturn('1'); - $abstractPlatform = $this->prophesize(AbstractPlatform::class); - $abstractPlatform->createSelectSQLBuilder()->shouldBeCalledOnce()->willReturn(new DefaultSelectSQLBuilder( - $abstractPlatform->reveal(), + $abstractPlatform = $this->createMock(AbstractPlatform::class); + $abstractPlatform->expects($this->once())->method('createSelectSQLBuilder')->willReturn(new DefaultSelectSQLBuilder( + $abstractPlatform, 'FOR UPDATE', 'SKIP LOCKED', )); - $connection->getDatabasePlatform()->willReturn($abstractPlatform->reveal()); + $connection->expects($this->once())->method('getDatabasePlatform')->willReturn($abstractPlatform); - $queryBuilder = new QueryBuilder($connection->reveal()); - $connection->createQueryBuilder()->willReturn($queryBuilder); + $queryBuilder = new QueryBuilder($connection); + $connection->expects($this->once())->method('createQueryBuilder')->willReturn($queryBuilder); - $eventSerializer = $this->prophesize(EventSerializer::class); - $headersSerializer = $this->prophesize(HeadersSerializer::class); + $eventSerializer = $this->createMock(EventSerializer::class); + $headersSerializer = $this->createMock(HeadersSerializer::class); $doctrineDbalStore = new DoctrineDbalStore( - $connection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $connection, + $eventSerializer, + $headersSerializer, ); $count = $doctrineDbalStore->count( @@ -1090,8 +1187,8 @@ public function testCount(): void public function testCountWrongResult(): void { - $connection = $this->prophesize(Connection::class); - $connection->fetchOne( + $connection = $this->createMock(Connection::class); + $connection->expects($this->once())->method('fetchOne')->with( 'SELECT COUNT(*) FROM eventstore WHERE (aggregate = :aggregate) AND (aggregate_id = :id) AND (playhead > :playhead) AND (archived = :archived)', [ 'aggregate' => 'profile', @@ -1099,27 +1196,27 @@ public function testCountWrongResult(): void 'playhead' => 0, 'archived' => false, ], - Argument::type('array'), + $this->isArray(), )->willReturn([]); - $abstractPlatform = $this->prophesize(AbstractPlatform::class); - $abstractPlatform->createSelectSQLBuilder()->shouldBeCalledOnce()->willReturn(new DefaultSelectSQLBuilder( - $abstractPlatform->reveal(), + $abstractPlatform = $this->createMock(AbstractPlatform::class); + $abstractPlatform->expects($this->once())->method('createSelectSQLBuilder')->willReturn(new DefaultSelectSQLBuilder( + $abstractPlatform, 'FOR UPDATE', 'SKIP LOCKED', )); - $connection->getDatabasePlatform()->willReturn($abstractPlatform->reveal()); + $connection->expects($this->once())->method('getDatabasePlatform')->willReturn($abstractPlatform); - $queryBuilder = new QueryBuilder($connection->reveal()); - $connection->createQueryBuilder()->willReturn($queryBuilder); + $queryBuilder = new QueryBuilder($connection); + $connection->expects($this->once())->method('createQueryBuilder')->willReturn($queryBuilder); - $eventSerializer = $this->prophesize(EventSerializer::class); - $headersSerializer = $this->prophesize(HeadersSerializer::class); + $eventSerializer = $this->createMock(EventSerializer::class); + $headersSerializer = $this->createMock(HeadersSerializer::class); $doctrineDbalStore = new DoctrineDbalStore( - $connection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $connection, + $eventSerializer, + $headersSerializer, ); $this->expectException(WrongQueryResult::class); @@ -1135,9 +1232,13 @@ public function testCountWrongResult(): void public function testSetupSubscription(): void { - $connection = $this->prophesize(Connection::class); - $connection->executeStatement( - <<<'SQL' + $connection = $this->createMock(Connection::class); + $connection + ->expects($this->exactly(3)) + ->method('executeStatement') + ->willReturnMap([ + [ + <<<'SQL' CREATE OR REPLACE FUNCTION notify_eventstore() RETURNS TRIGGER AS $$ BEGIN PERFORM pg_notify('eventstore', 'update'); @@ -1145,31 +1246,41 @@ public function testSetupSubscription(): void END; $$ LANGUAGE plpgsql; SQL, - )->shouldBeCalledOnce()->willReturn(1); - $connection->executeStatement('DROP TRIGGER IF EXISTS notify_trigger ON eventstore;') - ->shouldBeCalledOnce()->willReturn(1); - $connection->executeStatement('CREATE TRIGGER notify_trigger AFTER INSERT OR UPDATE ON eventstore FOR EACH ROW EXECUTE PROCEDURE notify_eventstore();') - ->shouldBeCalledOnce()->willReturn(1); + 1, + ], + ['DROP TRIGGER IF EXISTS notify_trigger ON eventstore;', 1], + [ + 'CREATE TRIGGER notify_trigger AFTER INSERT OR UPDATE ON eventstore FOR EACH ROW EXECUTE PROCEDURE notify_eventstore();', + 1, + ], + ]); - $abstractPlatform = $this->prophesize(PostgreSQLPlatform::class); - $connection->getDatabasePlatform()->willReturn($abstractPlatform->reveal()); + $abstractPlatform = $this->createMock(PostgreSQLPlatform::class); + $connection + ->expects($this->exactly(1)) + ->method('getDatabasePlatform') + ->willReturn($abstractPlatform); - $eventSerializer = $this->prophesize(EventSerializer::class); - $headersSerializer = $this->prophesize(HeadersSerializer::class); + $eventSerializer = $this->createMock(EventSerializer::class); + $headersSerializer = $this->createMock(HeadersSerializer::class); $doctrineDbalStore = new DoctrineDbalStore( - $connection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $connection, + $eventSerializer, + $headersSerializer, ); $doctrineDbalStore->setupSubscription(); } public function testSetupSubscriptionWithOtherStoreTableName(): void { - $connection = $this->prophesize(Connection::class); - $connection->executeStatement( - <<<'SQL' + $connection = $this->createMock(Connection::class); + $connection + ->expects($this->exactly(3)) + ->method('executeStatement') + ->willReturnMap([ + [ + <<<'SQL' CREATE OR REPLACE FUNCTION new.notify_eventstore() RETURNS TRIGGER AS $$ BEGIN PERFORM pg_notify('new.eventstore', 'update'); @@ -1177,22 +1288,28 @@ public function testSetupSubscriptionWithOtherStoreTableName(): void END; $$ LANGUAGE plpgsql; SQL, - )->shouldBeCalledOnce()->willReturn(1); - $connection->executeStatement('DROP TRIGGER IF EXISTS notify_trigger ON new.eventstore;') - ->shouldBeCalledOnce()->willReturn(1); - $connection->executeStatement('CREATE TRIGGER notify_trigger AFTER INSERT OR UPDATE ON new.eventstore FOR EACH ROW EXECUTE PROCEDURE new.notify_eventstore();') - ->shouldBeCalledOnce()->willReturn(1); + 1, + ], + ['DROP TRIGGER IF EXISTS notify_trigger ON new.eventstore;', 1], + [ + 'CREATE TRIGGER notify_trigger AFTER INSERT OR UPDATE ON new.eventstore FOR EACH ROW EXECUTE PROCEDURE new.notify_eventstore();', + 1, + ], + ]); - $abstractPlatform = $this->prophesize(PostgreSQLPlatform::class); - $connection->getDatabasePlatform()->willReturn($abstractPlatform->reveal()); + $abstractPlatform = $this->createMock(PostgreSQLPlatform::class); + $connection + ->expects($this->once()) + ->method('getDatabasePlatform') + ->willReturn($abstractPlatform); - $eventSerializer = $this->prophesize(EventSerializer::class); - $headersSerializer = $this->prophesize(HeadersSerializer::class); + $eventSerializer = $this->createMock(EventSerializer::class); + $headersSerializer = $this->createMock(HeadersSerializer::class); $doctrineDbalStore = new DoctrineDbalStore( - $connection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $connection, + $eventSerializer, + $headersSerializer, ['table_name' => 'new.eventstore'], ); $doctrineDbalStore->setupSubscription(); @@ -1200,32 +1317,28 @@ public function testSetupSubscriptionWithOtherStoreTableName(): void public function testSetupSubscriptionNotPostgres(): void { - $connection = $this->prophesize(Connection::class); - $connection->executeStatement( - <<<'SQL' + $connection = $this->createMock(Connection::class); + $connection->expects($this->never())->method('executeStatement')->with(<<<'SQL' CREATE OR REPLACE FUNCTION notify_eventstore() RETURNS TRIGGER AS $$ BEGIN PERFORM pg_notify('eventstore'); RETURN NEW; END; $$ LANGUAGE plpgsql; - SQL, - )->shouldNotBeCalled(); - $connection->executeStatement('DROP TRIGGER IF EXISTS notify_trigger ON eventstore;') - ->shouldNotBeCalled(); - $connection->executeStatement('CREATE TRIGGER notify_trigger AFTER INSERT OR UPDATE ON eventstore FOR EACH ROW EXECUTE PROCEDURE notify_eventstore();') - ->shouldNotBeCalled(); + SQL,); + $connection->expects($this->never())->method('executeStatement')->with('DROP TRIGGER IF EXISTS notify_trigger ON eventstore;'); + $connection->expects($this->never())->method('executeStatement')->with('CREATE TRIGGER notify_trigger AFTER INSERT OR UPDATE ON eventstore FOR EACH ROW EXECUTE PROCEDURE notify_eventstore();'); - $abstractPlatform = $this->prophesize(AbstractPlatform::class); - $connection->getDatabasePlatform()->willReturn($abstractPlatform->reveal()); + $abstractPlatform = $this->createMock(AbstractPlatform::class); + $connection->expects($this->once())->method('getDatabasePlatform')->willReturn($abstractPlatform); - $eventSerializer = $this->prophesize(EventSerializer::class); - $headersSerializer = $this->prophesize(HeadersSerializer::class); + $eventSerializer = $this->createMock(EventSerializer::class); + $headersSerializer = $this->createMock(HeadersSerializer::class); $doctrineDbalStore = new DoctrineDbalStore( - $connection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $connection, + $eventSerializer, + $headersSerializer, ); $doctrineDbalStore->setupSubscription(); } @@ -1242,55 +1355,53 @@ public function testWait(): void ->with(PDO::FETCH_ASSOC, 100) ->willReturn([]); - $connection = $this->prophesize(Connection::class); - $connection->executeStatement('LISTEN "eventstore"') - ->shouldBeCalledOnce() + $connection = $this->createMock(Connection::class); + $connection->expects($this->once())->method('executeStatement')->with('LISTEN "eventstore"') ->willReturn(1); - $connection->getNativeConnection() - ->shouldBeCalledOnce() + $connection->expects($this->once())->method('getNativeConnection') ->willReturn($nativeConnection); - $abstractPlatform = $this->prophesize(PostgreSQLPlatform::class); - $connection->getDatabasePlatform()->willReturn($abstractPlatform->reveal()); + $abstractPlatform = $this->createMock(PostgreSQLPlatform::class); + $connection->expects($this->once())->method('getDatabasePlatform')->willReturn($abstractPlatform); - $eventSerializer = $this->prophesize(EventSerializer::class); - $headersSerializer = $this->prophesize(HeadersSerializer::class); + $eventSerializer = $this->createMock(EventSerializer::class); + $headersSerializer = $this->createMock(HeadersSerializer::class); $doctrineDbalStore = new DoctrineDbalStore( - $connection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $connection, + $eventSerializer, + $headersSerializer, ); $doctrineDbalStore->wait(100); } public function testConfigureSchemaWithDifferentConnections(): void { - $connection = $this->prophesize(Connection::class); - $eventSerializer = $this->prophesize(EventSerializer::class); - $headersSerializer = $this->prophesize(HeadersSerializer::class); + $connection = $this->createMock(Connection::class); + $eventSerializer = $this->createMock(EventSerializer::class); + $headersSerializer = $this->createMock(HeadersSerializer::class); $doctrineDbalStore = new DoctrineDbalStore( - $connection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $connection, + $eventSerializer, + $headersSerializer, ); $schema = new Schema(); - $doctrineDbalStore->configureSchema($schema, $this->prophesize(Connection::class)->reveal()); + $doctrineDbalStore->configureSchema($schema, $this->createMock(Connection::class)); self::assertEquals(new Schema(), $schema); } public function testConfigureSchema(): void { - $connection = $this->prophesize(Connection::class); - $eventSerializer = $this->prophesize(EventSerializer::class); - $headersSerializer = $this->prophesize(HeadersSerializer::class); + $connection = $this->createMock(Connection::class); + $eventSerializer = $this->createMock(EventSerializer::class); + $headersSerializer = $this->createMock(HeadersSerializer::class); $doctrineDbalStore = new DoctrineDbalStore( - $connection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $connection, + $eventSerializer, + $headersSerializer, ); $expectedSchema = new Schema(); @@ -1327,21 +1438,21 @@ public function testConfigureSchema(): void $table->addIndex(['aggregate', 'aggregate_id', 'playhead', 'archived']); $schema = new Schema(); - $doctrineDbalStore->configureSchema($schema, $connection->reveal()); + $doctrineDbalStore->configureSchema($schema, $connection); self::assertEquals($expectedSchema, $schema); } public function testConfigureSchemaWithStringAsAggregateIdType(): void { - $connection = $this->prophesize(Connection::class); - $eventSerializer = $this->prophesize(EventSerializer::class); - $headersSerializer = $this->prophesize(HeadersSerializer::class); + $connection = $this->createMock(Connection::class); + $eventSerializer = $this->createMock(EventSerializer::class); + $headersSerializer = $this->createMock(HeadersSerializer::class); $doctrineDbalStore = new DoctrineDbalStore( - $connection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $connection, + $eventSerializer, + $headersSerializer, ['aggregate_id_type' => 'string'], ); @@ -1379,7 +1490,7 @@ public function testConfigureSchemaWithStringAsAggregateIdType(): void $table->addIndex(['aggregate', 'aggregate_id', 'playhead', 'archived']); $schema = new Schema(); - $doctrineDbalStore->configureSchema($schema, $connection->reveal()); + $doctrineDbalStore->configureSchema($schema, $connection); self::assertEquals($expectedSchema, $schema); } @@ -1406,76 +1517,92 @@ public function testArchiveMessagesDifferentAggregates(): void )) ->withHeader(new StreamStartHeader()); - $eventSerializer = $this->prophesize(EventSerializer::class); - $eventSerializer->serialize($message1->event())->shouldBeCalledOnce()->willReturn(new SerializedEvent( - 'profile_created', - '', - )); - $eventSerializer->serialize($message2->event())->shouldBeCalledOnce()->willReturn(new SerializedEvent( - 'profile_email_changed', - '', - )); - - $headersSerializer = $this->prophesize(HeadersSerializer::class); - $headersSerializer->serialize([])->willReturn('[]'); - - $mockedConnection = $this->prophesize(Connection::class); - $mockedConnection->getDatabasePlatform()->willReturn(new SQLitePlatform()); - $mockedConnection->transactional(Argument::any())->will( - /** @param array{0: callable} $args */ - static fn (array $args): mixed => $args[0](), - ); - - $mockedConnection->executeStatement( - "INSERT INTO eventstore (aggregate, aggregate_id, playhead, event, payload, recorded_on, new_stream_start, archived, custom_headers) VALUES\n(?, ?, ?, ?, ?, ?, ?, ?, ?),\n(?, ?, ?, ?, ?, ?, ?, ?, ?)", - [ - 'profile', - '1', - 5, - 'profile_created', - '', - $recordedOn, - true, - false, - '[]', - 'profile', - '2', - 42, - 'profile_email_changed', - '', - $recordedOn, - true, - false, - '[]', - ], - [ - 5 => Type::getType(Types::DATETIMETZ_IMMUTABLE), - 6 => Type::getType(Types::BOOLEAN), - 7 => Type::getType(Types::BOOLEAN), - 14 => Type::getType(Types::DATETIMETZ_IMMUTABLE), - 15 => Type::getType(Types::BOOLEAN), - 16 => Type::getType(Types::BOOLEAN), - ], - )->shouldBeCalledOnce(); - - $mockedConnection->executeStatement( - <<<'SQL' - UPDATE eventstore - SET archived = true - WHERE aggregate = :aggregate - AND aggregate_id = :aggregate_id - AND playhead < :playhead - AND archived = false - SQL, - [ - 'aggregate' => 'profile', - 'aggregate_id' => '1', - 'playhead' => 5, - ], - )->shouldBeCalledOnce(); + $eventSerializer = $this->createMock(EventSerializer::class); + $eventSerializer + ->expects($this->exactly(2)) + ->method('serialize') + ->willReturnCallback(new ReturnCallback([ + [ + [$message1->event(), []], + new SerializedEvent('profile_created', ''), + ], + [ + [$message2->event(), []], + new SerializedEvent('profile_email_changed', ''), + ], + ])); + + $headersSerializer = $this->createMock(HeadersSerializer::class); + $headersSerializer + ->expects($this->exactly(2)) + ->method('serialize') + ->with([]) + ->willReturn('[]'); + + $mockedConnection = $this->createMock(Connection::class); + $mockedConnection + ->expects($this->exactly(2)) + ->method('getDatabasePlatform') + ->willReturn(new SQLitePlatform()); + $mockedConnection + ->expects($this->once()) + ->method('transactional') + ->willReturnCallback(static fn (Closure $closure): mixed => $closure()); - $mockedConnection->executeStatement( - <<<'SQL' + $mockedConnection + ->expects($this->exactly(3)) + ->method('executeStatement') + ->willReturnMap([ + [ + "INSERT INTO eventstore (aggregate, aggregate_id, playhead, event, payload, recorded_on, new_stream_start, archived, custom_headers) VALUES\n(?, ?, ?, ?, ?, ?, ?, ?, ?),\n(?, ?, ?, ?, ?, ?, ?, ?, ?)", + [ + 'profile', + '1', + 5, + 'profile_created', + '', + $recordedOn, + true, + false, + '[]', + 'profile', + '2', + 42, + 'profile_email_changed', + '', + $recordedOn, + true, + false, + '[]', + ], + [ + 5 => Type::getType(Types::DATETIMETZ_IMMUTABLE), + 6 => Type::getType(Types::BOOLEAN), + 7 => Type::getType(Types::BOOLEAN), + 14 => Type::getType(Types::DATETIMETZ_IMMUTABLE), + 15 => Type::getType(Types::BOOLEAN), + 16 => Type::getType(Types::BOOLEAN), + ], + 1, + ], + [ + <<<'SQL' + UPDATE eventstore + SET archived = true + WHERE aggregate = :aggregate + AND aggregate_id = :aggregate_id + AND playhead < :playhead + AND archived = false + SQL, + [ + 'aggregate' => 'profile', + 'aggregate_id' => '1', + 'playhead' => 5, + ], + 1, + ], + [ + <<<'SQL' UPDATE eventstore SET archived = true WHERE aggregate = :aggregate @@ -1483,17 +1610,19 @@ public function testArchiveMessagesDifferentAggregates(): void AND playhead < :playhead AND archived = false SQL, - [ - 'aggregate' => 'profile', - 'aggregate_id' => '2', - 'playhead' => 42, - ], - )->shouldBeCalledOnce(); + [ + 'aggregate' => 'profile', + 'aggregate_id' => '2', + 'playhead' => 42, + ], + 1, + ], + ]); $singleTableStore = new DoctrineDbalStore( - $mockedConnection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $mockedConnection, + $eventSerializer, + $headersSerializer, ); $singleTableStore->save($message1, $message2); @@ -1521,78 +1650,90 @@ public function testArchiveMessagesSameAggregate(): void )) ->withHeader(new StreamStartHeader()); - $eventSerializer = $this->prophesize(EventSerializer::class); - $eventSerializer->serialize($message1->event())->shouldBeCalledOnce()->willReturn(new SerializedEvent( - 'profile_created', - '', - )); - $eventSerializer->serialize($message2->event())->shouldBeCalledOnce()->willReturn(new SerializedEvent( - 'profile_email_changed', - '', - )); - - $headersSerializer = $this->prophesize(HeadersSerializer::class); - $headersSerializer->serialize([])->willReturn('[]'); - - $mockedConnection = $this->prophesize(Connection::class); - $mockedConnection->getDatabasePlatform()->willReturn(new SQLitePlatform()); - $mockedConnection->transactional(Argument::any())->will( - /** @param array{0: callable} $args */ - static fn (array $args): mixed => $args[0](), - ); - - $mockedConnection->executeStatement( - "INSERT INTO eventstore (aggregate, aggregate_id, playhead, event, payload, recorded_on, new_stream_start, archived, custom_headers) VALUES\n(?, ?, ?, ?, ?, ?, ?, ?, ?),\n(?, ?, ?, ?, ?, ?, ?, ?, ?)", - [ - 'profile', - '1', - 5, - 'profile_created', - '', - $recordedOn, - true, - false, - '[]', - 'profile', - '1', - 42, - 'profile_email_changed', - '', - $recordedOn, - true, - false, - '[]', - ], - [ - 5 => Type::getType(Types::DATETIMETZ_IMMUTABLE), - 6 => Type::getType(Types::BOOLEAN), - 7 => Type::getType(Types::BOOLEAN), - 14 => Type::getType(Types::DATETIMETZ_IMMUTABLE), - 15 => Type::getType(Types::BOOLEAN), - 16 => Type::getType(Types::BOOLEAN), - ], - )->shouldBeCalledOnce(); + $eventSerializer = $this->createMock(EventSerializer::class); + $eventSerializer + ->expects($this->exactly(2)) + ->method('serialize') + ->willReturnMap([ + [$message1->event(), new SerializedEvent('profile_created', '')], + [$message2->event(), new SerializedEvent('profile_email_changed', '')], + ]); + + $headersSerializer = $this->createMock(HeadersSerializer::class); + $headersSerializer + ->expects($this->exactly(2)) + ->method('serialize') + ->with([]) + ->willReturn('[]'); + + $mockedConnection = $this->createMock(Connection::class); + $mockedConnection + ->expects($this->exactly(2)) + ->method('getDatabasePlatform') + ->willReturn(new SQLitePlatform()); + $mockedConnection + ->expects($this->once()) + ->method('transactional') + ->willReturnCallback(static fn (Closure $closure): mixed => $closure()); - $mockedConnection->executeStatement( - <<<'SQL' - UPDATE eventstore - SET archived = true - WHERE aggregate = :aggregate - AND aggregate_id = :aggregate_id - AND playhead < :playhead - AND archived = false - SQL, - [ - 'aggregate' => 'profile', - 'aggregate_id' => '1', - 'playhead' => 42, - ], - )->shouldBeCalledOnce(); + $mockedConnection + ->expects($this->exactly(2)) + ->method('executeStatement') + ->willReturnMap([ + [ + "INSERT INTO eventstore (aggregate, aggregate_id, playhead, event, payload, recorded_on, new_stream_start, archived, custom_headers) VALUES\n(?, ?, ?, ?, ?, ?, ?, ?, ?),\n(?, ?, ?, ?, ?, ?, ?, ?, ?)", + [ + 'profile', + '1', + 5, + 'profile_created', + '', + $recordedOn, + true, + false, + '[]', + 'profile', + '1', + 42, + 'profile_email_changed', + '', + $recordedOn, + true, + false, + '[]', + ], + [ + 5 => Type::getType(Types::DATETIMETZ_IMMUTABLE), + 6 => Type::getType(Types::BOOLEAN), + 7 => Type::getType(Types::BOOLEAN), + 14 => Type::getType(Types::DATETIMETZ_IMMUTABLE), + 15 => Type::getType(Types::BOOLEAN), + 16 => Type::getType(Types::BOOLEAN), + ], + 1, + ], + [ + <<<'SQL' + UPDATE eventstore + SET archived = true + WHERE aggregate = :aggregate + AND aggregate_id = :aggregate_id + AND playhead < :playhead + AND archived = false + SQL, + [ + 'aggregate' => 'profile', + 'aggregate_id' => '1', + 'playhead' => 42, + ], + 1, + ], + ]); $singleTableStore = new DoctrineDbalStore( - $mockedConnection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $mockedConnection, + $eventSerializer, + $headersSerializer, ); $singleTableStore->save($message1, $message2); diff --git a/tests/Unit/Store/DoctrineDbalStreamTest.php b/tests/Unit/Store/DoctrineDbalStreamTest.php index cf33c0b3..0856f0b2 100644 --- a/tests/Unit/Store/DoctrineDbalStreamTest.php +++ b/tests/Unit/Store/DoctrineDbalStreamTest.php @@ -16,12 +16,12 @@ use Patchlevel\EventSourcing\Store\DoctrineDbalStoreStream; use Patchlevel\EventSourcing\Store\Header\IndexHeader; use Patchlevel\EventSourcing\Store\StreamClosed; +use Patchlevel\EventSourcing\Tests\ReturnCallback; use Patchlevel\EventSourcing\Tests\Unit\Fixture\Email; use Patchlevel\EventSourcing\Tests\Unit\Fixture\ProfileCreated; use Patchlevel\EventSourcing\Tests\Unit\Fixture\ProfileId; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; use Throwable; use function iterator_to_array; @@ -29,22 +29,20 @@ #[CoversClass(DoctrineDbalStoreStream::class)] final class DoctrineDbalStreamTest extends TestCase { - use ProphecyTrait; - public function testEmpty(): void { - $eventSerializer = $this->prophesize(EventSerializer::class); - $headersSerializer = $this->prophesize(HeadersSerializer::class); - $platform = $this->prophesize(AbstractPlatform::class); + $eventSerializer = $this->createMock(EventSerializer::class); + $headersSerializer = $this->createMock(HeadersSerializer::class); + $platform = $this->createMock(AbstractPlatform::class); - $result = $this->prophesize(Result::class); - $result->iterateAssociative()->shouldBeCalledOnce()->willReturn(new ArrayIterator()); + $result = $this->createMock(Result::class); + $result->expects($this->once())->method('iterateAssociative')->willReturn(new ArrayIterator()); $stream = new DoctrineDbalStoreStream( - $result->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), - $platform->reveal(), + $result, + $eventSerializer, + $headersSerializer, + $platform, ); self::assertSame(null, $stream->position()); @@ -81,25 +79,24 @@ public function testOneMessage(): void ->withHeader(new IndexHeader(1)) ->withHeader(new AggregateHeader('profile', '1', 1, new DateTimeImmutable('2022-10-10 10:10:10'))); - $eventSerializer = $this->prophesize(EventSerializer::class); - $eventSerializer->deserialize(new SerializedEvent('profile_created', '{}')) - ->shouldBeCalledOnce() + $eventSerializer = $this->createMock(EventSerializer::class); + $eventSerializer->expects($this->once())->method('deserialize')->with(new SerializedEvent('profile_created', '{}')) ->willReturn($event); - $headersSerializer = $this->prophesize(HeadersSerializer::class); - $headersSerializer->deserialize('{}')->shouldBeCalledOnce()->willReturn([]); + $headersSerializer = $this->createMock(HeadersSerializer::class); + $headersSerializer->expects($this->once())->method('deserialize')->with('{}')->willReturn([]); - $platform = $this->prophesize(AbstractPlatform::class); - $platform->getDateTimeTzFormatString()->shouldBeCalledOnce()->willReturn('Y-m-d H:i:s'); + $platform = $this->createMock(AbstractPlatform::class); + $platform->expects($this->once())->method('getDateTimeTzFormatString')->willReturn('Y-m-d H:i:s'); - $result = $this->prophesize(Result::class); - $result->iterateAssociative()->shouldBeCalledOnce()->willReturn(new ArrayIterator($messages)); + $result = $this->createMock(Result::class); + $result->expects($this->once())->method('iterateAssociative')->willReturn(new ArrayIterator($messages)); $stream = new DoctrineDbalStoreStream( - $result->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), - $platform->reveal(), + $result, + $eventSerializer, + $headersSerializer, + $platform, ); self::assertSame(1, $stream->index()); @@ -173,31 +170,39 @@ public function testMultipleMessages(): void ->withHeader(new AggregateHeader('profile', '3', 1, new DateTimeImmutable('2022-10-10 10:10:10'))), ]; - $eventSerializer = $this->prophesize(EventSerializer::class); - $eventSerializer->deserialize(new SerializedEvent('profile_created', '{}')) - ->shouldBeCalledOnce() - ->willReturn($event); - $eventSerializer->deserialize(new SerializedEvent('profile_created2', '{}')) - ->shouldBeCalledOnce() - ->willReturn($event); - $eventSerializer->deserialize(new SerializedEvent('profile_created3', '{}')) - ->shouldBeCalledOnce() - ->willReturn($event); - - $headersSerializer = $this->prophesize(HeadersSerializer::class); - $headersSerializer->deserialize('{}')->shouldBeCalledTimes(3)->willReturn([]); - - $platform = $this->prophesize(AbstractPlatform::class); - $platform->getDateTimeTzFormatString()->shouldBeCalledTimes(3)->willReturn('Y-m-d H:i:s'); - - $result = $this->prophesize(Result::class); - $result->iterateAssociative()->shouldBeCalledOnce()->willReturn(new ArrayIterator($messagesArray)); + $eventSerializer = $this->createMock(EventSerializer::class); + $eventSerializer + ->expects($this->exactly(3)) + ->method('deserialize') + ->willReturnCallback(new ReturnCallback([ + [ + [new SerializedEvent('profile_created', '{}'), []], + $event, + ], + [ + [new SerializedEvent('profile_created2', '{}'), []], + $event, + ], + [ + [new SerializedEvent('profile_created3', '{}'), []], + $event, + ], + ])); + + $headersSerializer = $this->createMock(HeadersSerializer::class); + $headersSerializer->expects($this->exactly(3))->method('deserialize')->with('{}')->willReturn([]); + + $platform = $this->createMock(AbstractPlatform::class); + $platform->expects($this->exactly(3))->method('getDateTimeTzFormatString')->willReturn('Y-m-d H:i:s'); + + $result = $this->createMock(Result::class); + $result->expects($this->once())->method('iterateAssociative')->willReturn(new ArrayIterator($messagesArray)); $stream = new DoctrineDbalStoreStream( - $result->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), - $platform->reveal(), + $result, + $eventSerializer, + $headersSerializer, + $platform, ); self::assertSame(1, $stream->index()); @@ -252,25 +257,24 @@ public function testWithNoList(): void ->withHeader(new IndexHeader(5)) ->withHeader(new AggregateHeader('profile', '1', 1, new DateTimeImmutable('2022-10-10 10:10:10'))); - $eventSerializer = $this->prophesize(EventSerializer::class); - $eventSerializer->deserialize(new SerializedEvent('profile_created', '{}')) - ->shouldBeCalledOnce() + $eventSerializer = $this->createMock(EventSerializer::class); + $eventSerializer->expects($this->once())->method('deserialize')->with(new SerializedEvent('profile_created', '{}')) ->willReturn($event); - $headersSerializer = $this->prophesize(HeadersSerializer::class); - $headersSerializer->deserialize('{}')->shouldBeCalledOnce()->willReturn([]); + $headersSerializer = $this->createMock(HeadersSerializer::class); + $headersSerializer->expects($this->once())->method('deserialize')->with('{}')->willReturn([]); - $platform = $this->prophesize(AbstractPlatform::class); - $platform->getDateTimeTzFormatString()->shouldBeCalledOnce()->willReturn('Y-m-d H:i:s'); + $platform = $this->createMock(AbstractPlatform::class); + $platform->expects($this->once())->method('getDateTimeTzFormatString')->willReturn('Y-m-d H:i:s'); - $result = $this->prophesize(Result::class); - $result->iterateAssociative()->shouldBeCalledOnce()->willReturn(new ArrayIterator($messages)); + $result = $this->createMock(Result::class); + $result->expects($this->once())->method('iterateAssociative')->willReturn(new ArrayIterator($messages)); $stream = new DoctrineDbalStoreStream( - $result->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), - $platform->reveal(), + $result, + $eventSerializer, + $headersSerializer, + $platform, ); self::assertSame(5, $stream->index()); @@ -311,26 +315,25 @@ public function testClose(): void ->withHeader(new IndexHeader(1)) ->withHeader(new AggregateHeader('profile', '1', 1, new DateTimeImmutable('2022-10-10 10:10:10'))); - $eventSerializer = $this->prophesize(EventSerializer::class); - $eventSerializer->deserialize(new SerializedEvent('profile_created', '{}')) - ->shouldBeCalledOnce() + $eventSerializer = $this->createMock(EventSerializer::class); + $eventSerializer->expects($this->once())->method('deserialize')->with(new SerializedEvent('profile_created', '{}')) ->willReturn($event); - $headersSerializer = $this->prophesize(HeadersSerializer::class); - $headersSerializer->deserialize('{}')->shouldBeCalledOnce()->willReturn([]); + $headersSerializer = $this->createMock(HeadersSerializer::class); + $headersSerializer->expects($this->once())->method('deserialize')->with('{}')->willReturn([]); - $platform = $this->prophesize(AbstractPlatform::class); - $platform->getDateTimeTzFormatString()->shouldBeCalledOnce()->willReturn('Y-m-d H:i:s'); + $platform = $this->createMock(AbstractPlatform::class); + $platform->expects($this->once())->method('getDateTimeTzFormatString')->willReturn('Y-m-d H:i:s'); - $result = $this->prophesize(Result::class); - $result->iterateAssociative()->shouldBeCalledOnce()->willReturn(new ArrayIterator($messages)); - $result->free()->shouldBeCalledOnce(); + $result = $this->createMock(Result::class); + $result->expects($this->once())->method('iterateAssociative')->willReturn(new ArrayIterator($messages)); + $result->expects($this->once())->method('free'); $stream = new DoctrineDbalStoreStream( - $result->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), - $platform->reveal(), + $result, + $eventSerializer, + $headersSerializer, + $platform, ); self::assertSame(1, $stream->index()); @@ -346,18 +349,18 @@ public function testClose(): void public function testPositionEmpty(): void { - $eventSerializer = $this->prophesize(EventSerializer::class); - $headersSerializer = $this->prophesize(HeadersSerializer::class); - $platform = $this->prophesize(AbstractPlatform::class); + $eventSerializer = $this->createMock(EventSerializer::class); + $headersSerializer = $this->createMock(HeadersSerializer::class); + $platform = $this->createMock(AbstractPlatform::class); - $result = $this->prophesize(Result::class); - $result->iterateAssociative()->shouldBeCalledOnce()->willReturn(new ArrayIterator()); + $result = $this->createMock(Result::class); + $result->expects($this->once())->method('iterateAssociative')->willReturn(new ArrayIterator()); $stream = new DoctrineDbalStoreStream( - $result->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), - $platform->reveal(), + $result, + $eventSerializer, + $headersSerializer, + $platform, ); $position = $stream->position(); @@ -387,25 +390,24 @@ public function testPosition(): void Email::fromString('info@patchlevel.de'), ); - $eventSerializer = $this->prophesize(EventSerializer::class); - $eventSerializer->deserialize(new SerializedEvent('profile_created', '{}')) - ->shouldBeCalledOnce() + $eventSerializer = $this->createMock(EventSerializer::class); + $eventSerializer->expects($this->once())->method('deserialize')->with(new SerializedEvent('profile_created', '{}')) ->willReturn($event); - $headersSerializer = $this->prophesize(HeadersSerializer::class); - $headersSerializer->deserialize('{}')->shouldBeCalledOnce()->willReturn([]); + $headersSerializer = $this->createMock(HeadersSerializer::class); + $headersSerializer->expects($this->once())->method('deserialize')->with('{}')->willReturn([]); - $platform = $this->prophesize(AbstractPlatform::class); - $platform->getDateTimeTzFormatString()->shouldBeCalledOnce()->willReturn('Y-m-d H:i:s'); + $platform = $this->createMock(AbstractPlatform::class); + $platform->expects($this->once())->method('getDateTimeTzFormatString')->willReturn('Y-m-d H:i:s'); - $result = $this->prophesize(Result::class); - $result->iterateAssociative()->shouldBeCalledOnce()->willReturn(new ArrayIterator($messages)); + $result = $this->createMock(Result::class); + $result->expects($this->once())->method('iterateAssociative')->willReturn(new ArrayIterator($messages)); $stream = new DoctrineDbalStoreStream( - $result->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), - $platform->reveal(), + $result, + $eventSerializer, + $headersSerializer, + $platform, ); $position = $stream->position(); diff --git a/tests/Unit/Store/InMemoryStoreTest.php b/tests/Unit/Store/InMemoryStoreTest.php index 617fbd08..9d66afba 100644 --- a/tests/Unit/Store/InMemoryStoreTest.php +++ b/tests/Unit/Store/InMemoryStoreTest.php @@ -23,7 +23,6 @@ use Patchlevel\EventSourcing\Tests\Unit\Fixture\ProfileVisited; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; use stdClass; use function iterator_to_array; @@ -31,8 +30,6 @@ #[CoversClass(InMemoryStore::class)] final class InMemoryStoreTest extends TestCase { - use ProphecyTrait; - public function testLoadEmpty(): void { $store = new InMemoryStore(); diff --git a/tests/Unit/Store/ReadOnlyStoreTest.php b/tests/Unit/Store/ReadOnlyStoreTest.php index ae3fe565..fecae77f 100644 --- a/tests/Unit/Store/ReadOnlyStoreTest.php +++ b/tests/Unit/Store/ReadOnlyStoreTest.php @@ -13,29 +13,26 @@ use Patchlevel\EventSourcing\Store\StreamStore; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; #[CoversClass(ReadOnlyStore::class)] final class ReadOnlyStoreTest extends TestCase { - use ProphecyTrait; - public function testUnsupportedStore(): void { - $parentStore = $this->prophesize(StreamStore::class); + $parentStore = $this->createMock(StreamStore::class); $this->expectException(InvalidArgumentException::class); - new ReadOnlyStore($parentStore->reveal()); + new ReadOnlyStore($parentStore); } public function testLoad(): void { $criteria = new Criteria(); - $parentStore = $this->prophesize(Store::class); - $parentStore->load($criteria, 8, 42, true)->shouldBeCalled(); + $parentStore = $this->createMock(Store::class); + $parentStore->expects($this->atLeastOnce())->method('load')->with($criteria, 8, 42, true); - $store = new ReadOnlyStore($parentStore->reveal()); + $store = new ReadOnlyStore($parentStore); $store->load($criteria, 8, 42, true); } @@ -43,10 +40,10 @@ public function testCount(): void { $criteria = new Criteria(); - $parentStore = $this->prophesize(Store::class); - $parentStore->count($criteria)->shouldBeCalled(); + $parentStore = $this->createMock(Store::class); + $parentStore->expects($this->atLeastOnce())->method('count')->with($criteria); - $store = new ReadOnlyStore($parentStore->reveal()); + $store = new ReadOnlyStore($parentStore); $store->count($criteria); } @@ -55,10 +52,10 @@ public function testSave(): void $message = new Message(new class () { }); - $parentStore = $this->prophesize(Store::class); - $parentStore->save($message)->shouldNotBeCalled(); + $parentStore = $this->createMock(Store::class); + $parentStore->expects($this->never())->method('save')->with($message); - $store = new ReadOnlyStore($parentStore->reveal()); + $store = new ReadOnlyStore($parentStore); $this->expectException(StoreIsReadOnly::class); $store->save($message); } @@ -68,10 +65,10 @@ public function testTransactional(): void $callback = static function (): void { }; - $parentStore = $this->prophesize(Store::class); - $parentStore->transactional($callback)->shouldBeCalled(); + $parentStore = $this->createMock(Store::class); + $parentStore->expects($this->atLeastOnce())->method('transactional')->with($callback); - $store = new ReadOnlyStore($parentStore->reveal()); + $store = new ReadOnlyStore($parentStore); $store->transactional($callback); } } diff --git a/tests/Unit/Store/StreamDoctrineDbalStoreTest.php b/tests/Unit/Store/StreamDoctrineDbalStoreTest.php index d90085a2..1e9d472a 100644 --- a/tests/Unit/Store/StreamDoctrineDbalStoreTest.php +++ b/tests/Unit/Store/StreamDoctrineDbalStoreTest.php @@ -5,8 +5,10 @@ namespace Patchlevel\EventSourcing\Tests\Unit\Store; use ArrayIterator; +use Closure; use DateTimeImmutable; use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Driver\PDO\Exception; use Doctrine\DBAL\Exception\UniqueConstraintViolationException; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Platforms\MariaDBPlatform; @@ -34,6 +36,7 @@ use Patchlevel\EventSourcing\Store\StreamDoctrineDbalStore; use Patchlevel\EventSourcing\Store\UniqueConstraintViolation; use Patchlevel\EventSourcing\Store\WrongQueryResult; +use Patchlevel\EventSourcing\Tests\ReturnCallback; use Patchlevel\EventSourcing\Tests\Unit\Fixture\Email; use Patchlevel\EventSourcing\Tests\Unit\Fixture\Header\BazHeader; use Patchlevel\EventSourcing\Tests\Unit\Fixture\Header\FooHeader; @@ -43,8 +46,6 @@ use PDO; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Prophecy\Argument; -use Prophecy\PhpUnit\ProphecyTrait; use Psr\Clock\ClockInterface; use RuntimeException; @@ -54,43 +55,37 @@ #[CoversClass(StreamDoctrineDbalStore::class)] final class StreamDoctrineDbalStoreTest extends TestCase { - use ProphecyTrait; - public function testLoadWithNoEvents(): void { - $connection = $this->prophesize(Connection::class); - $result = $this->prophesize(Result::class); - $result->iterateAssociative()->willReturn(new EmptyIterator()); - - $connection->executeQuery( - 'SELECT * FROM event_store WHERE (stream = :stream_0) AND (playhead > :from_playhead) AND (archived = :archived) ORDER BY id ASC', - [ - 'stream_0' => 'profile-1', - 'from_playhead' => 0, - 'archived' => false, - ], - Argument::type('array'), - )->willReturn($result->reveal()); - - $abstractPlatform = $this->prophesize(AbstractPlatform::class); - $abstractPlatform->createSelectSQLBuilder()->shouldBeCalledOnce()->willReturn(new DefaultSelectSQLBuilder( - $abstractPlatform->reveal(), + $connection = $this->createMock(Connection::class); + $result = $this->createMock(Result::class); + $result->method('iterateAssociative')->willReturn(new EmptyIterator()); + + $connection->method('executeQuery')->with('SELECT * FROM event_store WHERE (stream = :stream_0) AND (playhead > :from_playhead) AND (archived = :archived) ORDER BY id ASC', [ + 'stream_0' => 'profile-1', + 'from_playhead' => 0, + 'archived' => false, + ], $this->isArray())->willReturn($result); + + $abstractPlatform = $this->createMock(AbstractPlatform::class); + $abstractPlatform->expects($this->once())->method('createSelectSQLBuilder')->willReturn(new DefaultSelectSQLBuilder( + $abstractPlatform, 'FOR UPDATE', 'SKIP LOCKED', )); - $connection->getDatabasePlatform()->willReturn($abstractPlatform->reveal()); - $queryBuilder = new QueryBuilder($connection->reveal()); - $connection->createQueryBuilder()->willReturn($queryBuilder); - $connection->createExpressionBuilder()->willReturn(new ExpressionBuilder($connection->reveal())); + $connection->method('getDatabasePlatform')->willReturn($abstractPlatform); + $queryBuilder = new QueryBuilder($connection); + $connection->method('createQueryBuilder')->willReturn($queryBuilder); + $connection->method('createExpressionBuilder')->willReturn(new ExpressionBuilder($connection)); - $eventSerializer = $this->prophesize(EventSerializer::class); - $headersSerializer = $this->prophesize(HeadersSerializer::class); + $eventSerializer = $this->createMock(EventSerializer::class); + $headersSerializer = $this->createMock(HeadersSerializer::class); $doctrineDbalStore = new StreamDoctrineDbalStore( - $connection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $connection, + $eventSerializer, + $headersSerializer, ); $stream = $doctrineDbalStore->load( @@ -107,39 +102,35 @@ public function testLoadWithNoEvents(): void public function testLoadWithLimit(): void { - $connection = $this->prophesize(Connection::class); - $result = $this->prophesize(Result::class); - $result->iterateAssociative()->willReturn(new EmptyIterator()); - - $connection->executeQuery( - 'SELECT * FROM event_store WHERE (stream = :stream_0) AND (playhead > :from_playhead) AND (archived = :archived) ORDER BY id ASC LIMIT 10', - [ - 'stream_0' => 'profile-1', - 'from_playhead' => 0, - 'archived' => false, - ], - Argument::type('array'), - )->willReturn($result->reveal()); - - $abstractPlatform = $this->prophesize(AbstractPlatform::class); - $abstractPlatform->createSelectSQLBuilder()->shouldBeCalledOnce()->willReturn(new DefaultSelectSQLBuilder( - $abstractPlatform->reveal(), + $connection = $this->createMock(Connection::class); + $result = $this->createMock(Result::class); + $result->method('iterateAssociative')->willReturn(new EmptyIterator()); + + $connection->method('executeQuery')->with('SELECT * FROM event_store WHERE (stream = :stream_0) AND (playhead > :from_playhead) AND (archived = :archived) ORDER BY id ASC LIMIT 10', [ + 'stream_0' => 'profile-1', + 'from_playhead' => 0, + 'archived' => false, + ], $this->isArray())->willReturn($result); + + $abstractPlatform = $this->createMock(AbstractPlatform::class); + $abstractPlatform->expects($this->once())->method('createSelectSQLBuilder')->willReturn(new DefaultSelectSQLBuilder( + $abstractPlatform, 'FOR UPDATE', 'SKIP LOCKED', )); - $connection->getDatabasePlatform()->willReturn($abstractPlatform->reveal()); - $queryBuilder = new QueryBuilder($connection->reveal()); - $connection->createQueryBuilder()->willReturn($queryBuilder); - $connection->createExpressionBuilder()->willReturn(new ExpressionBuilder($connection->reveal())); + $connection->method('getDatabasePlatform')->willReturn($abstractPlatform); + $queryBuilder = new QueryBuilder($connection); + $connection->method('createQueryBuilder')->willReturn($queryBuilder); + $connection->method('createExpressionBuilder')->willReturn(new ExpressionBuilder($connection)); - $eventSerializer = $this->prophesize(EventSerializer::class); - $headersSerializer = $this->prophesize(HeadersSerializer::class); + $eventSerializer = $this->createMock(EventSerializer::class); + $headersSerializer = $this->createMock(HeadersSerializer::class); $doctrineDbalStore = new StreamDoctrineDbalStore( - $connection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $connection, + $eventSerializer, + $headersSerializer, ); $stream = $doctrineDbalStore->load( @@ -161,39 +152,35 @@ public function testLoadWithOffset(): void $this->markTestSkipped('In older DBAL versions platforms did not need to support this'); } - $connection = $this->prophesize(Connection::class); - $result = $this->prophesize(Result::class); - $result->iterateAssociative()->willReturn(new EmptyIterator()); + $connection = $this->createMock(Connection::class); + $result = $this->createMock(Result::class); + $result->method('iterateAssociative')->willReturn(new EmptyIterator()); - $connection->executeQuery( - 'SELECT * FROM event_store WHERE (stream = :stream_0) AND (playhead > :from_playhead) AND (archived = :archived) ORDER BY id ASC OFFSET 5', - [ - 'stream_0' => 'profile-1', - 'from_playhead' => 0, - 'archived' => false, - ], - Argument::type('array'), - )->willReturn($result->reveal()); + $connection->method('executeQuery')->with('SELECT * FROM event_store WHERE (stream = :stream_0) AND (playhead > :from_playhead) AND (archived = :archived) ORDER BY id ASC OFFSET 5', [ + 'stream_0' => 'profile-1', + 'from_playhead' => 0, + 'archived' => false, + ], $this->isArray())->willReturn($result); - $abstractPlatform = $this->prophesize(AbstractPlatform::class); - $abstractPlatform->createSelectSQLBuilder()->shouldBeCalledOnce()->willReturn(new DefaultSelectSQLBuilder( - $abstractPlatform->reveal(), + $abstractPlatform = $this->createMock(AbstractPlatform::class); + $abstractPlatform->expects($this->once())->method('createSelectSQLBuilder')->willReturn(new DefaultSelectSQLBuilder( + $abstractPlatform, 'FOR UPDATE', 'SKIP LOCKED', )); - $connection->getDatabasePlatform()->willReturn($abstractPlatform->reveal()); - $queryBuilder = new QueryBuilder($connection->reveal()); - $connection->createQueryBuilder()->willReturn($queryBuilder); - $connection->createExpressionBuilder()->willReturn(new ExpressionBuilder($connection->reveal())); + $connection->method('getDatabasePlatform')->willReturn($abstractPlatform); + $queryBuilder = new QueryBuilder($connection); + $connection->method('createQueryBuilder')->willReturn($queryBuilder); + $connection->method('createExpressionBuilder')->willReturn(new ExpressionBuilder($connection)); - $eventSerializer = $this->prophesize(EventSerializer::class); - $headersSerializer = $this->prophesize(HeadersSerializer::class); + $eventSerializer = $this->createMock(EventSerializer::class); + $headersSerializer = $this->createMock(HeadersSerializer::class); $doctrineDbalStore = new StreamDoctrineDbalStore( - $connection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $connection, + $eventSerializer, + $headersSerializer, ); $stream = $doctrineDbalStore->load( @@ -211,40 +198,36 @@ public function testLoadWithOffset(): void public function testLoadWithIndex(): void { - $connection = $this->prophesize(Connection::class); - $result = $this->prophesize(Result::class); - $result->iterateAssociative()->willReturn(new EmptyIterator()); - - $connection->executeQuery( - 'SELECT * FROM event_store WHERE (stream = :stream_0) AND (playhead > :from_playhead) AND (id > :from_index) AND (archived = :archived) ORDER BY id ASC', - [ - 'stream_0' => 'profile-1', - 'from_playhead' => 0, - 'archived' => false, - 'from_index' => 1, - ], - Argument::type('array'), - )->willReturn($result->reveal()); - - $abstractPlatform = $this->prophesize(AbstractPlatform::class); - $abstractPlatform->createSelectSQLBuilder()->shouldBeCalledOnce()->willReturn(new DefaultSelectSQLBuilder( - $abstractPlatform->reveal(), + $connection = $this->createMock(Connection::class); + $result = $this->createMock(Result::class); + $result->method('iterateAssociative')->willReturn(new EmptyIterator()); + + $connection->method('executeQuery')->with('SELECT * FROM event_store WHERE (stream = :stream_0) AND (playhead > :from_playhead) AND (id > :from_index) AND (archived = :archived) ORDER BY id ASC', [ + 'stream_0' => 'profile-1', + 'from_playhead' => 0, + 'archived' => false, + 'from_index' => 1, + ], $this->isArray())->willReturn($result); + + $abstractPlatform = $this->createMock(AbstractPlatform::class); + $abstractPlatform->expects($this->once())->method('createSelectSQLBuilder')->willReturn(new DefaultSelectSQLBuilder( + $abstractPlatform, 'FOR UPDATE', 'SKIP LOCKED', )); - $connection->getDatabasePlatform()->willReturn($abstractPlatform->reveal()); - $queryBuilder = new QueryBuilder($connection->reveal()); - $connection->createQueryBuilder()->willReturn($queryBuilder); - $connection->createExpressionBuilder()->willReturn(new ExpressionBuilder($connection->reveal())); + $connection->method('getDatabasePlatform')->willReturn($abstractPlatform); + $queryBuilder = new QueryBuilder($connection); + $connection->method('createQueryBuilder')->willReturn($queryBuilder); + $connection->method('createExpressionBuilder')->willReturn(new ExpressionBuilder($connection)); - $eventSerializer = $this->prophesize(EventSerializer::class); - $headersSerializer = $this->prophesize(HeadersSerializer::class); + $eventSerializer = $this->createMock(EventSerializer::class); + $headersSerializer = $this->createMock(HeadersSerializer::class); $doctrineDbalStore = new StreamDoctrineDbalStore( - $connection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $connection, + $eventSerializer, + $headersSerializer, ); $stream = $doctrineDbalStore->load( @@ -262,39 +245,35 @@ public function testLoadWithIndex(): void public function testLoadWithLike(): void { - $connection = $this->prophesize(Connection::class); - $result = $this->prophesize(Result::class); - $result->iterateAssociative()->willReturn(new EmptyIterator()); - - $connection->executeQuery( - 'SELECT * FROM event_store WHERE (stream LIKE :stream_0) AND (playhead > :from_playhead) AND (archived = :archived) ORDER BY id ASC', - [ - 'stream_0' => 'profile-%', - 'from_playhead' => 0, - 'archived' => false, - ], - Argument::type('array'), - )->willReturn($result->reveal()); - - $abstractPlatform = $this->prophesize(AbstractPlatform::class); - $abstractPlatform->createSelectSQLBuilder()->shouldBeCalledOnce()->willReturn(new DefaultSelectSQLBuilder( - $abstractPlatform->reveal(), + $connection = $this->createMock(Connection::class); + $result = $this->createMock(Result::class); + $result->method('iterateAssociative')->willReturn(new EmptyIterator()); + + $connection->method('executeQuery')->with('SELECT * FROM event_store WHERE (stream LIKE :stream_0) AND (playhead > :from_playhead) AND (archived = :archived) ORDER BY id ASC', [ + 'stream_0' => 'profile-%', + 'from_playhead' => 0, + 'archived' => false, + ], $this->isArray())->willReturn($result); + + $abstractPlatform = $this->createMock(AbstractPlatform::class); + $abstractPlatform->expects($this->once())->method('createSelectSQLBuilder')->willReturn(new DefaultSelectSQLBuilder( + $abstractPlatform, 'FOR UPDATE', 'SKIP LOCKED', )); - $connection->getDatabasePlatform()->willReturn($abstractPlatform->reveal()); - $queryBuilder = new QueryBuilder($connection->reveal()); - $connection->createQueryBuilder()->willReturn($queryBuilder); - $connection->createExpressionBuilder()->willReturn(new ExpressionBuilder($connection->reveal())); + $connection->method('getDatabasePlatform')->willReturn($abstractPlatform); + $queryBuilder = new QueryBuilder($connection); + $connection->method('createQueryBuilder')->willReturn($queryBuilder); + $connection->method('createExpressionBuilder')->willReturn(new ExpressionBuilder($connection)); - $eventSerializer = $this->prophesize(EventSerializer::class); - $headersSerializer = $this->prophesize(HeadersSerializer::class); + $eventSerializer = $this->createMock(EventSerializer::class); + $headersSerializer = $this->createMock(HeadersSerializer::class); $doctrineDbalStore = new StreamDoctrineDbalStore( - $connection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $connection, + $eventSerializer, + $headersSerializer, ); $stream = $doctrineDbalStore->load( @@ -311,37 +290,33 @@ public function testLoadWithLike(): void public function testLoadWithLikeAll(): void { - $connection = $this->prophesize(Connection::class); - $result = $this->prophesize(Result::class); - $result->iterateAssociative()->willReturn(new EmptyIterator()); - - $connection->executeQuery( - 'SELECT * FROM event_store WHERE (playhead > :from_playhead) AND (archived = :archived) ORDER BY id ASC', - [ - 'from_playhead' => 0, - 'archived' => false, - ], - Argument::type('array'), - )->willReturn($result->reveal()); - - $abstractPlatform = $this->prophesize(AbstractPlatform::class); - $abstractPlatform->createSelectSQLBuilder()->shouldBeCalledOnce()->willReturn(new DefaultSelectSQLBuilder( - $abstractPlatform->reveal(), + $connection = $this->createMock(Connection::class); + $result = $this->createMock(Result::class); + $result->method('iterateAssociative')->willReturn(new EmptyIterator()); + + $connection->method('executeQuery')->with('SELECT * FROM event_store WHERE (playhead > :from_playhead) AND (archived = :archived) ORDER BY id ASC', [ + 'from_playhead' => 0, + 'archived' => false, + ], $this->isArray())->willReturn($result); + + $abstractPlatform = $this->createMock(AbstractPlatform::class); + $abstractPlatform->expects($this->once())->method('createSelectSQLBuilder')->willReturn(new DefaultSelectSQLBuilder( + $abstractPlatform, 'FOR UPDATE', 'SKIP LOCKED', )); - $connection->getDatabasePlatform()->willReturn($abstractPlatform->reveal()); - $queryBuilder = new QueryBuilder($connection->reveal()); - $connection->createQueryBuilder()->willReturn($queryBuilder); + $connection->method('getDatabasePlatform')->willReturn($abstractPlatform); + $queryBuilder = new QueryBuilder($connection); + $connection->method('createQueryBuilder')->willReturn($queryBuilder); - $eventSerializer = $this->prophesize(EventSerializer::class); - $headersSerializer = $this->prophesize(HeadersSerializer::class); + $eventSerializer = $this->createMock(EventSerializer::class); + $headersSerializer = $this->createMock(HeadersSerializer::class); $doctrineDbalStore = new StreamDoctrineDbalStore( - $connection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $connection, + $eventSerializer, + $headersSerializer, ); $stream = $doctrineDbalStore->load( @@ -358,40 +333,36 @@ public function testLoadWithLikeAll(): void public function testLoadMultipleStream(): void { - $connection = $this->prophesize(Connection::class); - $result = $this->prophesize(Result::class); - $result->iterateAssociative()->willReturn(new EmptyIterator()); - - $connection->executeQuery( - 'SELECT * FROM event_store WHERE ((stream LIKE :stream_0) OR (stream = :stream_1)) AND (playhead > :from_playhead) AND (archived = :archived) ORDER BY id ASC', - [ - 'stream_0' => 'profile-%', - 'stream_1' => 'foo', - 'from_playhead' => 0, - 'archived' => false, - ], - Argument::type('array'), - )->willReturn($result->reveal()); - - $abstractPlatform = $this->prophesize(AbstractPlatform::class); - $abstractPlatform->createSelectSQLBuilder()->shouldBeCalledOnce()->willReturn(new DefaultSelectSQLBuilder( - $abstractPlatform->reveal(), + $connection = $this->createMock(Connection::class); + $result = $this->createMock(Result::class); + $result->method('iterateAssociative')->willReturn(new EmptyIterator()); + + $connection->method('executeQuery')->with('SELECT * FROM event_store WHERE ((stream LIKE :stream_0) OR (stream = :stream_1)) AND (playhead > :from_playhead) AND (archived = :archived) ORDER BY id ASC', [ + 'stream_0' => 'profile-%', + 'stream_1' => 'foo', + 'from_playhead' => 0, + 'archived' => false, + ], $this->isArray())->willReturn($result); + + $abstractPlatform = $this->createMock(AbstractPlatform::class); + $abstractPlatform->expects($this->once())->method('createSelectSQLBuilder')->willReturn(new DefaultSelectSQLBuilder( + $abstractPlatform, 'FOR UPDATE', 'SKIP LOCKED', )); - $connection->getDatabasePlatform()->willReturn($abstractPlatform->reveal()); - $queryBuilder = new QueryBuilder($connection->reveal()); - $connection->createQueryBuilder()->willReturn($queryBuilder); - $connection->createExpressionBuilder()->willReturn(new ExpressionBuilder($connection->reveal())); + $connection->method('getDatabasePlatform')->willReturn($abstractPlatform); + $queryBuilder = new QueryBuilder($connection); + $connection->method('createQueryBuilder')->willReturn($queryBuilder); + $connection->method('createExpressionBuilder')->willReturn(new ExpressionBuilder($connection)); - $eventSerializer = $this->prophesize(EventSerializer::class); - $headersSerializer = $this->prophesize(HeadersSerializer::class); + $eventSerializer = $this->createMock(EventSerializer::class); + $headersSerializer = $this->createMock(HeadersSerializer::class); $doctrineDbalStore = new StreamDoctrineDbalStore( - $connection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $connection, + $eventSerializer, + $headersSerializer, ); $stream = $doctrineDbalStore->load( @@ -408,9 +379,9 @@ public function testLoadMultipleStream(): void public function testLoadWithOneEvent(): void { - $connection = $this->prophesize(Connection::class); - $result = $this->prophesize(Result::class); - $result->iterateAssociative()->willReturn(new ArrayIterator( + $connection = $this->createMock(Connection::class); + $result = $this->createMock(Result::class); + $result->method('iterateAssociative')->willReturn(new ArrayIterator( [ [ 'id' => 1, @@ -426,43 +397,37 @@ public function testLoadWithOneEvent(): void ], )); - $connection->executeQuery( - 'SELECT * FROM event_store WHERE (stream = :stream_0) AND (playhead > :from_playhead) AND (archived = :archived) ORDER BY id ASC', - [ - 'stream_0' => 'profile-1', - 'from_playhead' => 0, - 'archived' => false, - ], - Argument::type('array'), - )->willReturn($result->reveal()); + $connection->method('executeQuery')->with('SELECT * FROM event_store WHERE (stream = :stream_0) AND (playhead > :from_playhead) AND (archived = :archived) ORDER BY id ASC', [ + 'stream_0' => 'profile-1', + 'from_playhead' => 0, + 'archived' => false, + ], $this->isArray())->willReturn($result); - $abstractPlatform = $this->prophesize(AbstractPlatform::class); + $abstractPlatform = $this->createMock(AbstractPlatform::class); - $abstractPlatform->createSelectSQLBuilder()->shouldBeCalledOnce()->willReturn(new DefaultSelectSQLBuilder( - $abstractPlatform->reveal(), + $abstractPlatform->expects($this->once())->method('createSelectSQLBuilder')->willReturn(new DefaultSelectSQLBuilder( + $abstractPlatform, 'FOR UPDATE', 'SKIP LOCKED', )); - $abstractPlatform->getDateTimeTzFormatString()->shouldBeCalledOnce()->willReturn('Y-m-d H:i:s'); + $abstractPlatform->expects($this->once())->method('getDateTimeTzFormatString')->willReturn('Y-m-d H:i:s'); - $connection->getDatabasePlatform()->willReturn($abstractPlatform->reveal()); + $connection->method('getDatabasePlatform')->willReturn($abstractPlatform); - $queryBuilder = new QueryBuilder($connection->reveal()); - $connection->createQueryBuilder()->willReturn($queryBuilder); - $connection->createExpressionBuilder()->willReturn(new ExpressionBuilder($connection->reveal())); + $queryBuilder = new QueryBuilder($connection); + $connection->method('createQueryBuilder')->willReturn($queryBuilder); + $connection->method('createExpressionBuilder')->willReturn(new ExpressionBuilder($connection)); - $eventSerializer = $this->prophesize(EventSerializer::class); - $eventSerializer->deserialize( - new SerializedEvent('profile.created', '{"profileId": "1", "email": "s"}'), - )->willReturn(new ProfileCreated(ProfileId::fromString('1'), Email::fromString('s'))); + $eventSerializer = $this->createMock(EventSerializer::class); + $eventSerializer->method('deserialize')->with(new SerializedEvent('profile.created', '{"profileId": "1", "email": "s"}'))->willReturn(new ProfileCreated(ProfileId::fromString('1'), Email::fromString('s'))); - $headersSerializer = $this->prophesize(HeadersSerializer::class); - $headersSerializer->deserialize('[]')->willReturn([]); + $headersSerializer = $this->createMock(HeadersSerializer::class); + $headersSerializer->method('deserialize')->with('[]')->willReturn([]); $doctrineDbalStore = new StreamDoctrineDbalStore( - $connection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $connection, + $eventSerializer, + $headersSerializer, ); $stream = $doctrineDbalStore->load( @@ -498,74 +463,104 @@ public function testLoadWithOneEvent(): void public function testLoadWithTwoEvents(): void { - $connection = $this->prophesize(Connection::class); - $result = $this->prophesize(Result::class); - $result->iterateAssociative()->willReturn(new ArrayIterator( - [ + $connection = $this->createMock(Connection::class); + $result = $this->createMock(Result::class); + $result + ->expects($this->once()) + ->method('iterateAssociative') + ->willReturn(new ArrayIterator( [ - 'id' => 1, - 'stream' => 'profile-1', - 'playhead' => '1', - 'event_id' => '1', - 'event_name' => 'profile.created', - 'event_payload' => '{"profileId": "1", "email": "s"}', - 'recorded_on' => '2021-02-17 10:00:00', - 'archived' => '0', - 'custom_headers' => '[]', + [ + 'id' => 1, + 'stream' => 'profile-1', + 'playhead' => '1', + 'event_id' => '1', + 'event_name' => 'profile.created', + 'event_payload' => '{"profileId": "1", "email": "s"}', + 'recorded_on' => '2021-02-17 10:00:00', + 'archived' => '0', + 'custom_headers' => '[]', + ], + [ + 'id' => 2, + 'stream' => 'profile-1', + 'playhead' => '2', + 'event_id' => '2', + 'event_name' => 'profile.email_changed', + 'event_payload' => '{"profileId": "1", "email": "d"}', + 'recorded_on' => '2021-02-17 11:00:00', + 'archived' => '0', + 'custom_headers' => '[]', + ], ], + )); + + $connection + ->expects($this->once()) + ->method('executeQuery') + ->with( + 'SELECT * FROM event_store WHERE (stream = :stream_0) AND (playhead > :from_playhead) AND (archived = :archived) ORDER BY id ASC', [ - 'id' => 2, - 'stream' => 'profile-1', - 'playhead' => '2', - 'event_id' => '2', - 'event_name' => 'profile.email_changed', - 'event_payload' => '{"profileId": "1", "email": "d"}', - 'recorded_on' => '2021-02-17 11:00:00', - 'archived' => '0', - 'custom_headers' => '[]', + 'stream_0' => 'profile-1', + 'from_playhead' => 0, + 'archived' => false, ], - ], - )); + $this->isArray(), + ) + ->willReturn($result); - $connection->executeQuery( - 'SELECT * FROM event_store WHERE (stream = :stream_0) AND (playhead > :from_playhead) AND (archived = :archived) ORDER BY id ASC', - [ - 'stream_0' => 'profile-1', - 'from_playhead' => 0, - 'archived' => false, - ], - Argument::type('array'), - )->willReturn($result->reveal()); - - $abstractPlatform = $this->prophesize(AbstractPlatform::class); - $abstractPlatform->createSelectSQLBuilder()->shouldBeCalledOnce()->willReturn(new DefaultSelectSQLBuilder( - $abstractPlatform->reveal(), - 'FOR UPDATE', - 'SKIP LOCKED', - )); - $abstractPlatform->getDateTimeTzFormatString()->shouldBeCalledTimes(2)->willReturn('Y-m-d H:i:s'); - - $connection->getDatabasePlatform()->willReturn($abstractPlatform->reveal()); - - $queryBuilder = new QueryBuilder($connection->reveal()); - $connection->createQueryBuilder()->willReturn($queryBuilder); - $connection->createExpressionBuilder()->willReturn(new ExpressionBuilder($connection->reveal())); + $abstractPlatform = $this->createMock(AbstractPlatform::class); + $abstractPlatform + ->expects($this->once()) + ->method('createSelectSQLBuilder') + ->willReturn(new DefaultSelectSQLBuilder( + $abstractPlatform, + 'FOR UPDATE', + 'SKIP LOCKED', + )); + $abstractPlatform + ->expects($this->exactly(2)) + ->method('getDateTimeTzFormatString') + ->willReturn('Y-m-d H:i:s'); + + $connection + ->expects($this->exactly(2)) + ->method('getDatabasePlatform') + ->willReturn($abstractPlatform); + + $queryBuilder = new QueryBuilder($connection); + $connection + ->expects($this->once()) + ->method('createQueryBuilder') + ->willReturn($queryBuilder); - $eventSerializer = $this->prophesize(EventSerializer::class); - $eventSerializer->deserialize( - new SerializedEvent('profile.created', '{"profileId": "1", "email": "s"}'), - )->willReturn(new ProfileCreated(ProfileId::fromString('1'), Email::fromString('s'))); - $eventSerializer->deserialize( - new SerializedEvent('profile.email_changed', '{"profileId": "1", "email": "d"}'), - )->willReturn(new ProfileEmailChanged(ProfileId::fromString('1'), Email::fromString('d'))); + $connection + ->expects($this->once()) + ->method('createExpressionBuilder') + ->willReturn(new ExpressionBuilder($connection)); + + $eventSerializer = $this->createMock(EventSerializer::class); + $eventSerializer + ->expects($this->exactly(2)) + ->method('deserialize') + ->willReturnCallback(new ReturnCallback([ + [ + [new SerializedEvent('profile.created', '{"profileId": "1", "email": "s"}'), []], + new ProfileCreated(ProfileId::fromString('1'), Email::fromString('s')), + ], + [ + [new SerializedEvent('profile.email_changed', '{"profileId": "1", "email": "d"}'), []], + new ProfileEmailChanged(ProfileId::fromString('1'), Email::fromString('d')), + ], + ])); - $headersSerializer = $this->prophesize(HeadersSerializer::class); - $headersSerializer->deserialize('[]')->willReturn([]); + $headersSerializer = $this->createMock(HeadersSerializer::class); + $headersSerializer->method('deserialize')->with('[]')->willReturn([]); $doctrineDbalStore = new StreamDoctrineDbalStore( - $connection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $connection, + $eventSerializer, + $headersSerializer, ); $stream = $doctrineDbalStore->load( @@ -620,20 +615,23 @@ public function __invoke(): void } }; - $connection = $this->prophesize(Connection::class); - $connection->getDatabasePlatform()->willReturn(new SQLitePlatform()); - $connection->transactional(Argument::any())->will( - /** @param array{0: callable} $args */ - static fn (array $args): mixed => $args[0](), - )->shouldBeCalled(); + $connection = $this->createMock(Connection::class); + $connection + ->expects($this->exactly(2)) + ->method('getDatabasePlatform') + ->willReturn(new SQLitePlatform()); + $connection + ->expects($this->once()) + ->method('transactional') + ->willReturnCallback(static fn (Closure $closure): mixed => $closure()); - $eventSerializer = $this->prophesize(EventSerializer::class); - $headersSerializer = $this->prophesize(HeadersSerializer::class); + $eventSerializer = $this->createMock(EventSerializer::class); + $headersSerializer = $this->createMock(HeadersSerializer::class); $store = new StreamDoctrineDbalStore( - $connection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $connection, + $eventSerializer, + $headersSerializer, ); $store->transactional($callback(...)); @@ -652,23 +650,32 @@ public function __invoke(): void } }; - $connection = $this->prophesize(Connection::class); - $connection->getDatabasePlatform()->willReturn(new MySQLPlatform()); - $connection->fetchAllAssociative('SELECT GET_LOCK("133742", -1)')->shouldBeCalledOnce()->willReturn([]); - $connection->fetchAllAssociative('SELECT RELEASE_LOCK("133742")')->shouldBeCalledOnce()->willReturn([]); - - $connection->transactional(Argument::any())->will( - /** @param array{0: callable} $args */ - static fn (array $args): mixed => $args[0](), - )->shouldBeCalled(); + $connection = $this->createMock(Connection::class); + $connection + ->expects($this->exactly(2)) + ->method('getDatabasePlatform') + ->willReturn(new MySQLPlatform()); + + $connection + ->expects($this->exactly(2)) + ->method('fetchAllAssociative') + ->willReturnMap([ + ['SELECT GET_LOCK("133742", -1)', []], + ['SELECT RELEASE_LOCK("133742")', []], + ]); + + $connection + ->expects($this->once()) + ->method('transactional') + ->willReturnCallback(static fn (Closure $closure): mixed => $closure()); - $eventSerializer = $this->prophesize(EventSerializer::class); - $headersSerializer = $this->prophesize(HeadersSerializer::class); + $eventSerializer = $this->createMock(EventSerializer::class); + $headersSerializer = $this->createMock(HeadersSerializer::class); $store = new StreamDoctrineDbalStore( - $connection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $connection, + $eventSerializer, + $headersSerializer, ); $store->transactional($callback(...)); @@ -687,23 +694,32 @@ public function __invoke(): void } }; - $connection = $this->prophesize(Connection::class); - $connection->getDatabasePlatform()->willReturn(new MariaDBPlatform()); - $connection->fetchAllAssociative('SELECT GET_LOCK("133742", -1)')->shouldBeCalledOnce()->willReturn([]); - $connection->fetchAllAssociative('SELECT RELEASE_LOCK("133742")')->shouldBeCalledOnce()->willReturn([]); - - $connection->transactional(Argument::any())->will( - /** @param array{0: callable} $args */ - static fn (array $args): mixed => $args[0](), - )->shouldBeCalled(); + $connection = $this->createMock(Connection::class); + $connection + ->expects($this->exactly(2)) + ->method('getDatabasePlatform') + ->willReturn(new MariaDBPlatform()); + + $connection + ->expects($this->exactly(2)) + ->method('fetchAllAssociative') + ->willReturnMap([ + ['SELECT GET_LOCK("133742", -1)', []], + ['SELECT RELEASE_LOCK("133742")', []], + ]); + + $connection + ->expects($this->once()) + ->method('transactional') + ->willReturnCallback(static fn (Closure $closure): mixed => $closure()); - $eventSerializer = $this->prophesize(EventSerializer::class); - $headersSerializer = $this->prophesize(HeadersSerializer::class); + $eventSerializer = $this->createMock(EventSerializer::class); + $headersSerializer = $this->createMock(HeadersSerializer::class); $store = new StreamDoctrineDbalStore( - $connection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $connection, + $eventSerializer, + $headersSerializer, ); $store->transactional($callback(...)); @@ -722,22 +738,29 @@ public function __invoke(): void } }; - $connection = $this->prophesize(Connection::class); - $connection->getDatabasePlatform()->willReturn(new PostgreSQLPlatform()); - $connection->executeStatement('SELECT pg_advisory_xact_lock(133742)')->shouldBeCalledOnce(); + $connection = $this->createMock(Connection::class); + $connection + ->expects($this->exactly(2)) + ->method('getDatabasePlatform') + ->willReturn(new PostgreSQLPlatform()); + + $connection + ->expects($this->once()) + ->method('executeStatement') + ->with('SELECT pg_advisory_xact_lock(133742)'); - $connection->transactional(Argument::any())->will( - /** @param array{0: callable} $args */ - static fn (array $args): mixed => $args[0](), - )->shouldBeCalled(); + $connection + ->expects($this->atLeastOnce()) + ->method('transactional') + ->willReturnCallback(static fn (Closure $closure): mixed => $closure()); - $eventSerializer = $this->prophesize(EventSerializer::class); - $headersSerializer = $this->prophesize(HeadersSerializer::class); + $eventSerializer = $this->createMock(EventSerializer::class); + $headersSerializer = $this->createMock(HeadersSerializer::class); $store = new StreamDoctrineDbalStore( - $connection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $connection, + $eventSerializer, + $headersSerializer, ); $store->transactional($callback(...)); @@ -756,22 +779,29 @@ public function __invoke(): void } }; - $connection = $this->prophesize(Connection::class); - $connection->getDatabasePlatform()->willReturn(new PostgreSQLPlatform()); - $connection->executeStatement('SELECT pg_advisory_xact_lock(133742)')->shouldBeCalledOnce(); + $connection = $this->createMock(Connection::class); + $connection + ->expects($this->exactly(2)) + ->method('getDatabasePlatform') + ->willReturn(new PostgreSQLPlatform()); + + $connection + ->expects($this->once()) + ->method('executeStatement') + ->with('SELECT pg_advisory_xact_lock(133742)'); - $connection->transactional(Argument::any())->will( - /** @param array{0: callable} $args */ - static fn (array $args): mixed => $args[0](), - )->shouldBeCalledTimes(2); + $connection + ->expects($this->exactly(2)) + ->method('transactional') + ->willReturnCallback(static fn (Closure $closure): mixed => $closure()); - $eventSerializer = $this->prophesize(EventSerializer::class); - $headersSerializer = $this->prophesize(HeadersSerializer::class); + $eventSerializer = $this->createMock(EventSerializer::class); + $headersSerializer = $this->createMock(HeadersSerializer::class); $store = new StreamDoctrineDbalStore( - $connection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $connection, + $eventSerializer, + $headersSerializer, ); $store->transactional(static function () use ($store, $callback): void { @@ -792,22 +822,28 @@ public function __invoke(): void } }; - $connection = $this->prophesize(Connection::class); - $connection->getDatabasePlatform()->willReturn(new PostgreSQLPlatform()); - $connection->executeStatement('SELECT pg_advisory_xact_lock(133742)')->shouldBeCalledTimes(2); + $connection = $this->createMock(Connection::class); + $connection + ->expects($this->exactly(4)) + ->method('getDatabasePlatform') + ->willReturn(new PostgreSQLPlatform()); + $connection + ->expects($this->exactly(2)) + ->method('executeStatement') + ->with('SELECT pg_advisory_xact_lock(133742)'); - $connection->transactional(Argument::any())->will( - /** @param array{0: callable} $args */ - static fn (array $args): mixed => $args[0](), - )->shouldBeCalled(); + $connection + ->expects($this->exactly(2)) + ->method('transactional') + ->willReturnCallback(static fn (Closure $closure): mixed => $closure()); - $eventSerializer = $this->prophesize(EventSerializer::class); - $headersSerializer = $this->prophesize(HeadersSerializer::class); + $eventSerializer = $this->createMock(EventSerializer::class); + $headersSerializer = $this->createMock(HeadersSerializer::class); $store = new StreamDoctrineDbalStore( - $connection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $connection, + $eventSerializer, + $headersSerializer, ); $store->transactional($callback(...)); @@ -825,23 +861,32 @@ public function __invoke(): void } }; - $connection = $this->prophesize(Connection::class); - $connection->getDatabasePlatform()->willReturn(new MariaDBPlatform()); - $connection->fetchAllAssociative('SELECT GET_LOCK("133742", -1)')->shouldBeCalledOnce()->willReturn([]); - $connection->fetchAllAssociative('SELECT RELEASE_LOCK("133742")')->shouldBeCalledOnce()->willReturn([]); - - $connection->transactional(Argument::any())->will( - /** @param array{0: callable} $args */ - static fn (array $args): mixed => $args[0](), - )->shouldBeCalled(); + $connection = $this->createMock(Connection::class); + $connection + ->expects($this->exactly(2)) + ->method('getDatabasePlatform') + ->willReturn(new MariaDBPlatform()); + + $connection + ->expects($this->exactly(2)) + ->method('fetchAllAssociative') + ->willReturnMap([ + ['SELECT GET_LOCK("133742", -1)', []], + ['SELECT RELEASE_LOCK("133742")', []], + ]); + + $connection + ->expects($this->once()) + ->method('transactional') + ->willReturnCallback(static fn (Closure $closure): mixed => $closure()); - $eventSerializer = $this->prophesize(EventSerializer::class); - $headersSerializer = $this->prophesize(HeadersSerializer::class); + $eventSerializer = $this->createMock(EventSerializer::class); + $headersSerializer = $this->createMock(HeadersSerializer::class); $store = new StreamDoctrineDbalStore( - $connection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $connection, + $eventSerializer, + $headersSerializer, ); $this->expectException(RuntimeException::class); @@ -858,35 +903,30 @@ public function testSaveWithOneEvent(): void ->withHeader(new PlayheadHeader(1)) ->withHeader(new RecordedOnHeader($recordedOn)); - $eventSerializer = $this->prophesize(EventSerializer::class); - $eventSerializer->serialize($message->event())->shouldBeCalledOnce()->willReturn(new SerializedEvent( + $eventSerializer = $this->createMock(EventSerializer::class); + $eventSerializer->expects($this->once())->method('serialize')->with($message->event())->willReturn(new SerializedEvent( 'profile_created', '', )); - $headersSerializer = $this->prophesize(HeadersSerializer::class); - $headersSerializer->serialize([])->willReturn('[]'); + $headersSerializer = $this->createMock(HeadersSerializer::class); + $headersSerializer->method('serialize')->with([])->willReturn('[]'); - $mockedConnection = $this->prophesize(Connection::class); - $mockedConnection->getDatabasePlatform()->willReturn(new SQLitePlatform()); - $mockedConnection->transactional(Argument::any())->will( - /** @param array{0: callable} $args */ - static fn (array $args): mixed => $args[0](), + $mockedConnection = $this->createMock(Connection::class); + $mockedConnection->method('getDatabasePlatform')->willReturn(new SQLitePlatform()); + $mockedConnection->method('transactional')->willReturnCallback( + static fn (Closure $closure): mixed => $closure(), ); - $mockedConnection->executeStatement( - "INSERT INTO event_store (stream, playhead, event_id, event_name, event_payload, recorded_on, archived, custom_headers) VALUES\n(?, ?, ?, ?, ?, ?, ?, ?)", - ['profile-1', 1, '1', 'profile_created', '', $recordedOn, false, '[]'], - [ - 5 => Type::getType(Types::DATETIMETZ_IMMUTABLE), - 6 => Type::getType(Types::BOOLEAN), - ], - )->shouldBeCalledOnce(); + $mockedConnection->expects($this->once())->method('executeStatement')->with("INSERT INTO event_store (stream, playhead, event_id, event_name, event_payload, recorded_on, archived, custom_headers) VALUES\n(?, ?, ?, ?, ?, ?, ?, ?)", ['profile-1', 1, '1', 'profile_created', '', $recordedOn, false, '[]'], [ + 5 => Type::getType(Types::DATETIMETZ_IMMUTABLE), + 6 => Type::getType(Types::BOOLEAN), + ]); $singleTableStore = new StreamDoctrineDbalStore( - $mockedConnection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $mockedConnection, + $eventSerializer, + $headersSerializer, ); $singleTableStore->save($message); } @@ -895,28 +935,27 @@ public function testSaveWithoutStreamNameHeader(): void { $message = Message::create(new ProfileCreated(ProfileId::fromString('1'), Email::fromString('s'))); - $eventSerializer = $this->prophesize(EventSerializer::class); - $eventSerializer->serialize($message->event())->shouldBeCalledOnce()->willReturn(new SerializedEvent( + $eventSerializer = $this->createMock(EventSerializer::class); + $eventSerializer->expects($this->once())->method('serialize')->with($message->event())->willReturn(new SerializedEvent( 'profile_created', '', )); - $headersSerializer = $this->prophesize(HeadersSerializer::class); - $headersSerializer->serialize([])->willReturn('[]'); + $headersSerializer = $this->createMock(HeadersSerializer::class); + $headersSerializer->method('serialize')->with([])->willReturn('[]'); - $mockedConnection = $this->prophesize(Connection::class); - $mockedConnection->getDatabasePlatform()->willReturn(new SQLitePlatform()); - $mockedConnection->transactional(Argument::any())->will( - /** @param array{0: callable} $args */ - static fn (array $args): mixed => $args[0](), + $mockedConnection = $this->createMock(Connection::class); + $mockedConnection->method('getDatabasePlatform')->willReturn(new SQLitePlatform()); + $mockedConnection->method('transactional')->willReturnCallback( + static fn (Closure $closure): mixed => $closure(), ); - $mockedConnection->executeStatement(Argument::any(), Argument::any(), Argument::any())->shouldNotBeCalled(); + $mockedConnection->expects($this->never())->method('executeStatement'); $singleTableStore = new StreamDoctrineDbalStore( - $mockedConnection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $mockedConnection, + $eventSerializer, + $headersSerializer, ); $this->expectException(MissingDataForStorage::class); @@ -937,58 +976,67 @@ public function testSaveWithTwoEvents(): void ->withHeader(new RecordedOnHeader($recordedOn)) ->withHeader(new EventIdHeader('2')); - $eventSerializer = $this->prophesize(EventSerializer::class); - $eventSerializer->serialize($message1->event())->shouldBeCalledOnce()->willReturn(new SerializedEvent( - 'profile_created', - '', - )); - $eventSerializer->serialize($message2->event())->shouldBeCalledOnce()->willReturn(new SerializedEvent( - 'profile_email_changed', - '', - )); - - $headersSerializer = $this->prophesize(HeadersSerializer::class); - $headersSerializer->serialize([])->willReturn('[]'); - - $mockedConnection = $this->prophesize(Connection::class); - $mockedConnection->getDatabasePlatform()->willReturn(new SQLitePlatform()); - $mockedConnection->transactional(Argument::any())->will( - /** @param array{0: callable} $args */ - static fn (array $args): mixed => $args[0](), - ); + $eventSerializer = $this->createMock(EventSerializer::class); + $eventSerializer + ->expects($this->exactly(2)) + ->method('serialize') + ->willReturnMap([ + [$message1->event(), new SerializedEvent('profile_created', '')], + [$message2->event(), new SerializedEvent('profile_email_changed', '')], + ]); + + $headersSerializer = $this->createMock(HeadersSerializer::class); + $headersSerializer + ->expects($this->exactly(2)) + ->method('serialize') + ->with([]) + ->willReturn('[]'); + + $mockedConnection = $this->createMock(Connection::class); + $mockedConnection + ->expects($this->exactly(2)) + ->method('getDatabasePlatform') + ->willReturn(new SQLitePlatform()); + $mockedConnection + ->expects($this->once()) + ->method('transactional') + ->willReturnCallback(static fn (Closure $closure): mixed => $closure()); - $mockedConnection->executeStatement( - "INSERT INTO event_store (stream, playhead, event_id, event_name, event_payload, recorded_on, archived, custom_headers) VALUES\n(?, ?, ?, ?, ?, ?, ?, ?),\n(?, ?, ?, ?, ?, ?, ?, ?)", - [ - 'profile-1', - 1, - '1', - 'profile_created', - '', - $recordedOn, - false, - '[]', - 'profile-1', - 2, - '2', - 'profile_email_changed', - '', - $recordedOn, - false, - '[]', - ], - [ - 5 => Type::getType(Types::DATETIMETZ_IMMUTABLE), - 6 => Type::getType(Types::BOOLEAN), - 13 => Type::getType(Types::DATETIMETZ_IMMUTABLE), - 14 => Type::getType(Types::BOOLEAN), - ], - )->shouldBeCalledOnce(); + $mockedConnection + ->expects($this->once()) + ->method('executeStatement') + ->with( + "INSERT INTO event_store (stream, playhead, event_id, event_name, event_payload, recorded_on, archived, custom_headers) VALUES\n(?, ?, ?, ?, ?, ?, ?, ?),\n(?, ?, ?, ?, ?, ?, ?, ?)", + [ + 'profile-1', + 1, + '1', + 'profile_created', + '', + $recordedOn, + false, + '[]', + 'profile-1', + 2, + '2', + 'profile_email_changed', + '', + $recordedOn, + false, + '[]', + ], + [ + 5 => Type::getType(Types::DATETIMETZ_IMMUTABLE), + 6 => Type::getType(Types::BOOLEAN), + 13 => Type::getType(Types::DATETIMETZ_IMMUTABLE), + 14 => Type::getType(Types::BOOLEAN), + ], + ); $singleTableStore = new StreamDoctrineDbalStore( - $mockedConnection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $mockedConnection, + $eventSerializer, + $headersSerializer, ); $singleTableStore->save($message1, $message2); } @@ -1007,54 +1055,57 @@ public function testSaveWithUniqueConstraintViolation(): void ->withHeader(new EventIdHeader('2')) ->withHeader(new RecordedOnHeader($recordedOn)); - $eventSerializer = $this->prophesize(EventSerializer::class); - $eventSerializer->serialize($message1->event())->shouldBeCalledTimes(2)->willReturn(new SerializedEvent( + $eventSerializer = $this->createMock(EventSerializer::class); + $eventSerializer->expects($this->exactly(2))->method('serialize')->with($message1->event())->willReturn(new SerializedEvent( 'profile_created', '', )); - $headersSerializer = $this->prophesize(HeadersSerializer::class); - $headersSerializer->serialize([])->willReturn('[]'); + $headersSerializer = $this->createMock(HeadersSerializer::class); + $headersSerializer->method('serialize')->with([])->willReturn('[]'); - $mockedConnection = $this->prophesize(Connection::class); - $mockedConnection->getDatabasePlatform()->willReturn(new SQLitePlatform()); - $mockedConnection->transactional(Argument::any())->will( - /** @param array{0: callable} $args */ - static fn (array $args): mixed => $args[0](), + $mockedConnection = $this->createMock(Connection::class); + $mockedConnection->method('getDatabasePlatform')->willReturn(new SQLitePlatform()); + $mockedConnection->method('transactional')->willReturnCallback( + static fn (Closure $closure): mixed => $closure(), ); - $mockedConnection->executeStatement( - "INSERT INTO event_store (stream, playhead, event_id, event_name, event_payload, recorded_on, archived, custom_headers) VALUES\n(?, ?, ?, ?, ?, ?, ?, ?),\n(?, ?, ?, ?, ?, ?, ?, ?)", - [ - 'profile-1', - 1, - '1', - 'profile_created', - '', - $recordedOn, - false, - '[]', - 'profile-1', - 1, - '2', - 'profile_created', - '', - $recordedOn, - false, - '[]', - ], - [ - 5 => Type::getType(Types::DATETIMETZ_IMMUTABLE), - 6 => Type::getType(Types::BOOLEAN), - 13 => Type::getType(Types::DATETIMETZ_IMMUTABLE), - 14 => Type::getType(Types::BOOLEAN), - ], - )->shouldBeCalledOnce()->willThrow(UniqueConstraintViolationException::class); + $mockedConnection + ->expects($this->once()) + ->method('executeStatement') + ->with( + "INSERT INTO event_store (stream, playhead, event_id, event_name, event_payload, recorded_on, archived, custom_headers) VALUES\n(?, ?, ?, ?, ?, ?, ?, ?),\n(?, ?, ?, ?, ?, ?, ?, ?)", + [ + 'profile-1', + 1, + '1', + 'profile_created', + '', + $recordedOn, + false, + '[]', + 'profile-1', + 1, + '2', + 'profile_created', + '', + $recordedOn, + false, + '[]', + ], + [ + 5 => Type::getType(Types::DATETIMETZ_IMMUTABLE), + 6 => Type::getType(Types::BOOLEAN), + 13 => Type::getType(Types::DATETIMETZ_IMMUTABLE), + 14 => Type::getType(Types::BOOLEAN), + ], + ) + ->willThrowException(new UniqueConstraintViolationException(new Exception('foo'), null)); $singleTableStore = new StreamDoctrineDbalStore( - $mockedConnection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $mockedConnection, + $eventSerializer, + $headersSerializer, ); $this->expectException(UniqueConstraintViolation::class); @@ -1073,29 +1124,27 @@ public function testSaveWithThousandEvents(): void ->withHeader(new RecordedOnHeader($recordedOn)); } - $eventSerializer = $this->prophesize(EventSerializer::class); - $eventSerializer->serialize($messages[0]->event())->shouldBeCalledTimes(10000)->willReturn(new SerializedEvent( + $eventSerializer = $this->createMock(EventSerializer::class); + $eventSerializer->expects($this->exactly(10000))->method('serialize')->with($messages[0]->event())->willReturn(new SerializedEvent( 'profile_email_changed', '', )); - $headersSerializer = $this->prophesize(HeadersSerializer::class); - $headersSerializer->serialize([])->willReturn('[]'); + $headersSerializer = $this->createMock(HeadersSerializer::class); + $headersSerializer->method('serialize')->with([])->willReturn('[]'); - $mockedConnection = $this->prophesize(Connection::class); - $mockedConnection->getDatabasePlatform()->willReturn(new SQLitePlatform()); - $mockedConnection->transactional(Argument::any())->will( - /** @param array{0: callable} $args */ - static fn (array $args): mixed => $args[0](), + $mockedConnection = $this->createMock(Connection::class); + $mockedConnection->method('getDatabasePlatform')->willReturn(new SQLitePlatform()); + $mockedConnection->method('transactional')->willReturnCallback( + static fn (Closure $closure): mixed => $closure(), ); - $mockedConnection->executeStatement(Argument::any(), Argument::any(), Argument::any()) - ->shouldBeCalledTimes(2); + $mockedConnection->expects($this->exactly(2))->method('executeStatement'); $singleTableStore = new StreamDoctrineDbalStore( - $mockedConnection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $mockedConnection, + $eventSerializer, + $headersSerializer, ); $singleTableStore->save(...$messages); } @@ -1115,71 +1164,62 @@ public function testSaveWithCustomHeaders(): void ->withHeader(new EventIdHeader('1')) ->withHeaders($customHeaders); - $eventSerializer = $this->prophesize(EventSerializer::class); - $eventSerializer->serialize($message->event())->shouldBeCalledOnce()->willReturn(new SerializedEvent( + $eventSerializer = $this->createMock(EventSerializer::class); + $eventSerializer->expects($this->once())->method('serialize')->with($message->event())->willReturn(new SerializedEvent( 'profile_created', '', )); - $headersSerializer = $this->prophesize(HeadersSerializer::class); - $headersSerializer->serialize($customHeaders)->willReturn('{foo: "foo", baz: "baz"}'); + $headersSerializer = $this->createMock(HeadersSerializer::class); + $headersSerializer->method('serialize')->with($customHeaders)->willReturn('{foo: "foo", baz: "baz"}'); - $mockedConnection = $this->prophesize(Connection::class); - $mockedConnection->getDatabasePlatform()->willReturn(new SQLitePlatform()); - $mockedConnection->transactional(Argument::any())->will( - /** @param array{0: callable} $args */ - static fn (array $args): mixed => $args[0](), + $mockedConnection = $this->createMock(Connection::class); + $mockedConnection->method('getDatabasePlatform')->willReturn(new SQLitePlatform()); + $mockedConnection->method('transactional')->willReturnCallback( + static fn (Closure $closure): mixed => $closure(), ); - $mockedConnection->executeStatement( - "INSERT INTO event_store (stream, playhead, event_id, event_name, event_payload, recorded_on, archived, custom_headers) VALUES\n(?, ?, ?, ?, ?, ?, ?, ?)", - ['profile-1', 1, '1', 'profile_created', '', $recordedOn, false, '{foo: "foo", baz: "baz"}'], - [ - 5 => Type::getType(Types::DATETIMETZ_IMMUTABLE), - 6 => Type::getType(Types::BOOLEAN), - ], - )->shouldBeCalledOnce(); + $mockedConnection->expects($this->once())->method('executeStatement')->with("INSERT INTO event_store (stream, playhead, event_id, event_name, event_payload, recorded_on, archived, custom_headers) VALUES\n(?, ?, ?, ?, ?, ?, ?, ?)", ['profile-1', 1, '1', 'profile_created', '', $recordedOn, false, '{foo: "foo", baz: "baz"}'], [ + 5 => Type::getType(Types::DATETIMETZ_IMMUTABLE), + 6 => Type::getType(Types::BOOLEAN), + ]); $singleTableStore = new StreamDoctrineDbalStore( - $mockedConnection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $mockedConnection, + $eventSerializer, + $headersSerializer, ); $singleTableStore->save($message); } public function testCount(): void { - $connection = $this->prophesize(Connection::class); - $connection->fetchOne( - 'SELECT COUNT(*) FROM event_store WHERE (stream = :stream_0) AND (playhead > :from_playhead) AND (archived = :archived)', - [ - 'stream_0' => 'profile-1', - 'from_playhead' => 0, - 'archived' => false, - ], - Argument::type('array'), - )->willReturn('1'); - - $abstractPlatform = $this->prophesize(AbstractPlatform::class); - $abstractPlatform->createSelectSQLBuilder()->shouldBeCalledOnce()->willReturn(new DefaultSelectSQLBuilder( - $abstractPlatform->reveal(), + $connection = $this->createMock(Connection::class); + $connection->method('fetchOne')->with('SELECT COUNT(*) FROM event_store WHERE (stream = :stream_0) AND (playhead > :from_playhead) AND (archived = :archived)', [ + 'stream_0' => 'profile-1', + 'from_playhead' => 0, + 'archived' => false, + ], $this->isArray())->willReturn('1'); + + $abstractPlatform = $this->createMock(AbstractPlatform::class); + $abstractPlatform->expects($this->once())->method('createSelectSQLBuilder')->willReturn(new DefaultSelectSQLBuilder( + $abstractPlatform, 'FOR UPDATE', 'SKIP LOCKED', )); - $connection->getDatabasePlatform()->willReturn($abstractPlatform->reveal()); + $connection->method('getDatabasePlatform')->willReturn($abstractPlatform); - $queryBuilder = new QueryBuilder($connection->reveal()); - $connection->createQueryBuilder()->willReturn($queryBuilder); - $connection->createExpressionBuilder()->willReturn(new ExpressionBuilder($connection->reveal())); + $queryBuilder = new QueryBuilder($connection); + $connection->method('createQueryBuilder')->willReturn($queryBuilder); + $connection->method('createExpressionBuilder')->willReturn(new ExpressionBuilder($connection)); - $eventSerializer = $this->prophesize(EventSerializer::class); - $headersSerializer = $this->prophesize(HeadersSerializer::class); + $eventSerializer = $this->createMock(EventSerializer::class); + $headersSerializer = $this->createMock(HeadersSerializer::class); $doctrineDbalStore = new StreamDoctrineDbalStore( - $connection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $connection, + $eventSerializer, + $headersSerializer, ); $count = $doctrineDbalStore->count( @@ -1195,36 +1235,32 @@ public function testCount(): void public function testCountWrongResult(): void { - $connection = $this->prophesize(Connection::class); - $connection->fetchOne( - 'SELECT COUNT(*) FROM event_store WHERE (stream = :stream_0) AND (playhead > :from_playhead) AND (archived = :archived)', - [ - 'stream_0' => 'profile-1', - 'from_playhead' => 0, - 'archived' => false, - ], - Argument::type('array'), - )->willReturn([]); - - $abstractPlatform = $this->prophesize(AbstractPlatform::class); - $abstractPlatform->createSelectSQLBuilder()->shouldBeCalledOnce()->willReturn(new DefaultSelectSQLBuilder( - $abstractPlatform->reveal(), + $connection = $this->createMock(Connection::class); + $connection->method('fetchOne')->with('SELECT COUNT(*) FROM event_store WHERE (stream = :stream_0) AND (playhead > :from_playhead) AND (archived = :archived)', [ + 'stream_0' => 'profile-1', + 'from_playhead' => 0, + 'archived' => false, + ], $this->isArray())->willReturn([]); + + $abstractPlatform = $this->createMock(AbstractPlatform::class); + $abstractPlatform->expects($this->once())->method('createSelectSQLBuilder')->willReturn(new DefaultSelectSQLBuilder( + $abstractPlatform, 'FOR UPDATE', 'SKIP LOCKED', )); - $connection->getDatabasePlatform()->willReturn($abstractPlatform->reveal()); + $connection->method('getDatabasePlatform')->willReturn($abstractPlatform); - $queryBuilder = new QueryBuilder($connection->reveal()); - $connection->createQueryBuilder()->willReturn($queryBuilder); - $connection->createExpressionBuilder()->willReturn(new ExpressionBuilder($connection->reveal())); + $queryBuilder = new QueryBuilder($connection); + $connection->method('createQueryBuilder')->willReturn($queryBuilder); + $connection->method('createExpressionBuilder')->willReturn(new ExpressionBuilder($connection)); - $eventSerializer = $this->prophesize(EventSerializer::class); - $headersSerializer = $this->prophesize(HeadersSerializer::class); + $eventSerializer = $this->createMock(EventSerializer::class); + $headersSerializer = $this->createMock(HeadersSerializer::class); $doctrineDbalStore = new StreamDoctrineDbalStore( - $connection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $connection, + $eventSerializer, + $headersSerializer, ); $this->expectException(WrongQueryResult::class); @@ -1239,9 +1275,13 @@ public function testCountWrongResult(): void public function testSetupSubscription(): void { - $connection = $this->prophesize(Connection::class); - $connection->executeStatement( - <<<'SQL' + $connection = $this->createMock(Connection::class); + $connection + ->expects($this->exactly(3)) + ->method('executeStatement') + ->willReturnMap([ + [ + <<<'SQL' CREATE OR REPLACE FUNCTION notify_event_store() RETURNS TRIGGER AS $$ BEGIN PERFORM pg_notify('event_store', NEW.stream::text); @@ -1249,31 +1289,35 @@ public function testSetupSubscription(): void END; $$ LANGUAGE plpgsql; SQL, - )->shouldBeCalledOnce()->willReturn(1); - $connection->executeStatement('DROP TRIGGER IF EXISTS notify_trigger ON event_store;') - ->shouldBeCalledOnce()->willReturn(1); - $connection->executeStatement('CREATE TRIGGER notify_trigger AFTER INSERT OR UPDATE ON event_store FOR EACH ROW EXECUTE PROCEDURE notify_event_store();') - ->shouldBeCalledOnce()->willReturn(1); + 1, + ], + ['DROP TRIGGER IF EXISTS notify_trigger ON event_store;', 1], + ['CREATE TRIGGER notify_trigger AFTER INSERT OR UPDATE ON event_store FOR EACH ROW EXECUTE PROCEDURE notify_event_store();', 1], + ]); - $abstractPlatform = $this->prophesize(PostgreSQLPlatform::class); - $connection->getDatabasePlatform()->willReturn($abstractPlatform->reveal()); + $abstractPlatform = $this->createMock(PostgreSQLPlatform::class); + $connection->expects($this->once())->method('getDatabasePlatform')->willReturn($abstractPlatform); - $eventSerializer = $this->prophesize(EventSerializer::class); - $headersSerializer = $this->prophesize(HeadersSerializer::class); + $eventSerializer = $this->createMock(EventSerializer::class); + $headersSerializer = $this->createMock(HeadersSerializer::class); $doctrineDbalStore = new StreamDoctrineDbalStore( - $connection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $connection, + $eventSerializer, + $headersSerializer, ); $doctrineDbalStore->setupSubscription(); } public function testSetupSubscriptionWithOtherStoreTableName(): void { - $connection = $this->prophesize(Connection::class); - $connection->executeStatement( - <<<'SQL' + $connection = $this->createMock(Connection::class); + $connection + ->expects($this->exactly(3)) + ->method('executeStatement') + ->willReturnMap([ + [ + <<<'SQL' CREATE OR REPLACE FUNCTION new.notify_event_store() RETURNS TRIGGER AS $$ BEGIN PERFORM pg_notify('new.event_store', NEW.stream::text); @@ -1281,24 +1325,24 @@ public function testSetupSubscriptionWithOtherStoreTableName(): void END; $$ LANGUAGE plpgsql; SQL, - )->shouldBeCalledOnce()->willReturn(1); - $connection->executeStatement('DROP TRIGGER IF EXISTS notify_trigger ON new.event_store;') - ->shouldBeCalledOnce()->willReturn(1); - $connection->executeStatement('CREATE TRIGGER notify_trigger AFTER INSERT OR UPDATE ON new.event_store FOR EACH ROW EXECUTE PROCEDURE new.notify_event_store();') - ->shouldBeCalledOnce()->willReturn(1); + 1, + ], + ['DROP TRIGGER IF EXISTS notify_trigger ON new.event_store;', 1], + ['CREATE TRIGGER notify_trigger AFTER INSERT OR UPDATE ON new.event_store FOR EACH ROW EXECUTE PROCEDURE new.notify_event_store();', 1], + ]); - $abstractPlatform = $this->prophesize(PostgreSQLPlatform::class); - $connection->getDatabasePlatform()->willReturn($abstractPlatform->reveal()); + $abstractPlatform = $this->createMock(PostgreSQLPlatform::class); + $connection->expects($this->once())->method('getDatabasePlatform')->willReturn($abstractPlatform); - $eventSerializer = $this->prophesize(EventSerializer::class); - $headersSerializer = $this->prophesize(HeadersSerializer::class); - $clock = $this->prophesize(ClockInterface::class); + $eventSerializer = $this->createMock(EventSerializer::class); + $headersSerializer = $this->createMock(HeadersSerializer::class); + $clock = $this->createMock(ClockInterface::class); $doctrineDbalStore = new StreamDoctrineDbalStore( - $connection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), - $clock->reveal(), + $connection, + $eventSerializer, + $headersSerializer, + $clock, ['table_name' => 'new.event_store'], ); $doctrineDbalStore->setupSubscription(); @@ -1306,32 +1350,19 @@ public function testSetupSubscriptionWithOtherStoreTableName(): void public function testSetupSubscriptionNotPostgres(): void { - $connection = $this->prophesize(Connection::class); - $connection->executeStatement( - <<<'SQL' - CREATE OR REPLACE FUNCTION notify_event_store() RETURNS TRIGGER AS $$ - BEGIN - PERFORM pg_notify('event_store'); - RETURN NEW; - END; - $$ LANGUAGE plpgsql; - SQL, - )->shouldNotBeCalled(); - $connection->executeStatement('DROP TRIGGER IF EXISTS notify_trigger ON event_store;') - ->shouldNotBeCalled(); - $connection->executeStatement('CREATE TRIGGER notify_trigger AFTER INSERT OR UPDATE ON event_store FOR EACH ROW EXECUTE PROCEDURE notify_event_store();') - ->shouldNotBeCalled(); + $connection = $this->createMock(Connection::class); + $connection->expects($this->never())->method('executeStatement'); - $abstractPlatform = $this->prophesize(AbstractPlatform::class); - $connection->getDatabasePlatform()->willReturn($abstractPlatform->reveal()); + $abstractPlatform = $this->createMock(AbstractPlatform::class); + $connection->expects($this->once())->method('getDatabasePlatform')->willReturn($abstractPlatform); - $eventSerializer = $this->prophesize(EventSerializer::class); - $headersSerializer = $this->prophesize(HeadersSerializer::class); + $eventSerializer = $this->createMock(EventSerializer::class); + $headersSerializer = $this->createMock(HeadersSerializer::class); $doctrineDbalStore = new StreamDoctrineDbalStore( - $connection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $connection, + $eventSerializer, + $headersSerializer, ); $doctrineDbalStore->setupSubscription(); } @@ -1348,55 +1379,58 @@ public function testWait(): void ->with(PDO::FETCH_ASSOC, 100) ->willReturn([]); - $connection = $this->prophesize(Connection::class); - $connection->executeStatement('LISTEN "event_store"') - ->shouldBeCalledOnce() + $connection = $this->createMock(Connection::class); + $connection + ->expects($this->once()) + ->method('executeStatement') + ->with('LISTEN "event_store"') ->willReturn(1); - $connection->getNativeConnection() - ->shouldBeCalledOnce() + $connection + ->expects($this->once()) + ->method('getNativeConnection') ->willReturn($nativeConnection); - $abstractPlatform = $this->prophesize(PostgreSQLPlatform::class); - $connection->getDatabasePlatform()->willReturn($abstractPlatform->reveal()); + $abstractPlatform = $this->createMock(PostgreSQLPlatform::class); + $connection->method('getDatabasePlatform')->willReturn($abstractPlatform); - $eventSerializer = $this->prophesize(EventSerializer::class); - $headersSerializer = $this->prophesize(HeadersSerializer::class); + $eventSerializer = $this->createMock(EventSerializer::class); + $headersSerializer = $this->createMock(HeadersSerializer::class); $doctrineDbalStore = new StreamDoctrineDbalStore( - $connection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $connection, + $eventSerializer, + $headersSerializer, ); $doctrineDbalStore->wait(100); } public function testConfigureSchemaWithDifferentConnections(): void { - $connection = $this->prophesize(Connection::class); - $eventSerializer = $this->prophesize(EventSerializer::class); - $headersSerializer = $this->prophesize(HeadersSerializer::class); + $connection = $this->createMock(Connection::class); + $eventSerializer = $this->createMock(EventSerializer::class); + $headersSerializer = $this->createMock(HeadersSerializer::class); $doctrineDbalStore = new StreamDoctrineDbalStore( - $connection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $connection, + $eventSerializer, + $headersSerializer, ); $schema = new Schema(); - $doctrineDbalStore->configureSchema($schema, $this->prophesize(Connection::class)->reveal()); + $doctrineDbalStore->configureSchema($schema, $this->createMock(Connection::class)); self::assertEquals(new Schema(), $schema); } public function testConfigureSchema(): void { - $connection = $this->prophesize(Connection::class); - $eventSerializer = $this->prophesize(EventSerializer::class); - $headersSerializer = $this->prophesize(HeadersSerializer::class); + $connection = $this->createMock(Connection::class); + $eventSerializer = $this->createMock(EventSerializer::class); + $headersSerializer = $this->createMock(HeadersSerializer::class); $doctrineDbalStore = new StreamDoctrineDbalStore( - $connection->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), + $connection, + $eventSerializer, + $headersSerializer, ); $expectedSchema = new Schema(); @@ -1431,7 +1465,7 @@ public function testConfigureSchema(): void $table->addIndex(['stream', 'playhead', 'archived']); $schema = new Schema(); - $doctrineDbalStore->configureSchema($schema, $connection->reveal()); + $doctrineDbalStore->configureSchema($schema, $connection); self::assertEquals($expectedSchema, $schema); } diff --git a/tests/Unit/Store/StreamDoctrineDbalStreamTest.php b/tests/Unit/Store/StreamDoctrineDbalStreamTest.php index 1e22f053..e15c3fac 100644 --- a/tests/Unit/Store/StreamDoctrineDbalStreamTest.php +++ b/tests/Unit/Store/StreamDoctrineDbalStreamTest.php @@ -19,12 +19,12 @@ use Patchlevel\EventSourcing\Store\Header\StreamNameHeader; use Patchlevel\EventSourcing\Store\StreamClosed; use Patchlevel\EventSourcing\Store\StreamDoctrineDbalStoreStream; +use Patchlevel\EventSourcing\Tests\ReturnCallback; use Patchlevel\EventSourcing\Tests\Unit\Fixture\Email; use Patchlevel\EventSourcing\Tests\Unit\Fixture\ProfileCreated; use Patchlevel\EventSourcing\Tests\Unit\Fixture\ProfileId; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; use Throwable; use function iterator_to_array; @@ -32,22 +32,20 @@ #[CoversClass(StreamDoctrineDbalStoreStream::class)] final class StreamDoctrineDbalStreamTest extends TestCase { - use ProphecyTrait; - public function testEmpty(): void { - $eventSerializer = $this->prophesize(EventSerializer::class); - $headersSerializer = $this->prophesize(HeadersSerializer::class); - $platform = $this->prophesize(AbstractPlatform::class); + $eventSerializer = $this->createMock(EventSerializer::class); + $headersSerializer = $this->createMock(HeadersSerializer::class); + $platform = $this->createMock(AbstractPlatform::class); - $result = $this->prophesize(Result::class); - $result->iterateAssociative()->shouldBeCalledOnce()->willReturn(new ArrayIterator()); + $result = $this->createMock(Result::class); + $result->expects($this->once())->method('iterateAssociative')->willReturn(new ArrayIterator()); $stream = new StreamDoctrineDbalStoreStream( - $result->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), - $platform->reveal(), + $result, + $eventSerializer, + $headersSerializer, + $platform, ); self::assertSame(null, $stream->position()); @@ -87,25 +85,24 @@ public function testOneMessage(): void ->withHeader(new EventIdHeader('1')) ->withHeader(new RecordedOnHeader(new DateTimeImmutable('2022-10-10 10:10:10'))); - $eventSerializer = $this->prophesize(EventSerializer::class); - $eventSerializer->deserialize(new SerializedEvent('profile_created', '{}')) - ->shouldBeCalledOnce() + $eventSerializer = $this->createMock(EventSerializer::class); + $eventSerializer->expects($this->once())->method('deserialize')->with(new SerializedEvent('profile_created', '{}')) ->willReturn($event); - $headersSerializer = $this->prophesize(HeadersSerializer::class); - $headersSerializer->deserialize('{}')->shouldBeCalledOnce()->willReturn([]); + $headersSerializer = $this->createMock(HeadersSerializer::class); + $headersSerializer->expects($this->once())->method('deserialize')->with('{}')->willReturn([]); - $platform = $this->prophesize(AbstractPlatform::class); - $platform->getDateTimeTzFormatString()->shouldBeCalledOnce()->willReturn('Y-m-d H:i:s'); + $platform = $this->createMock(AbstractPlatform::class); + $platform->expects($this->once())->method('getDateTimeTzFormatString')->willReturn('Y-m-d H:i:s'); - $result = $this->prophesize(Result::class); - $result->iterateAssociative()->shouldBeCalledOnce()->willReturn(new ArrayIterator($messages)); + $result = $this->createMock(Result::class); + $result->expects($this->once())->method('iterateAssociative')->willReturn(new ArrayIterator($messages)); $stream = new StreamDoctrineDbalStoreStream( - $result->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), - $platform->reveal(), + $result, + $eventSerializer, + $headersSerializer, + $platform, ); self::assertSame(1, $stream->index()); @@ -187,31 +184,39 @@ public function testMultipleMessages(): void ->withHeader(new RecordedOnHeader(new DateTimeImmutable('2022-10-10 10:10:10'))), ]; - $eventSerializer = $this->prophesize(EventSerializer::class); - $eventSerializer->deserialize(new SerializedEvent('profile_created', '{}')) - ->shouldBeCalledOnce() - ->willReturn($event); - $eventSerializer->deserialize(new SerializedEvent('profile_created2', '{}')) - ->shouldBeCalledOnce() - ->willReturn($event); - $eventSerializer->deserialize(new SerializedEvent('profile_created3', '{}')) - ->shouldBeCalledOnce() - ->willReturn($event); - - $headersSerializer = $this->prophesize(HeadersSerializer::class); - $headersSerializer->deserialize('{}')->shouldBeCalledTimes(3)->willReturn([]); - - $platform = $this->prophesize(AbstractPlatform::class); - $platform->getDateTimeTzFormatString()->shouldBeCalledTimes(3)->willReturn('Y-m-d H:i:s'); - - $result = $this->prophesize(Result::class); - $result->iterateAssociative()->shouldBeCalledOnce()->willReturn(new ArrayIterator($messagesArray)); + $eventSerializer = $this->createMock(EventSerializer::class); + $eventSerializer + ->expects($this->exactly(3)) + ->method('deserialize') + ->willReturnCallback(new ReturnCallback([ + [ + [new SerializedEvent('profile_created', '{}'), []], + $event, + ], + [ + [new SerializedEvent('profile_created2', '{}'), []], + $event, + ], + [ + [new SerializedEvent('profile_created3', '{}'), []], + $event, + ], + ])); + + $headersSerializer = $this->createMock(HeadersSerializer::class); + $headersSerializer->expects($this->exactly(3))->method('deserialize')->with('{}')->willReturn([]); + + $platform = $this->createMock(AbstractPlatform::class); + $platform->expects($this->exactly(3))->method('getDateTimeTzFormatString')->willReturn('Y-m-d H:i:s'); + + $result = $this->createMock(Result::class); + $result->expects($this->once())->method('iterateAssociative')->willReturn(new ArrayIterator($messagesArray)); $stream = new StreamDoctrineDbalStoreStream( - $result->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), - $platform->reveal(), + $result, + $eventSerializer, + $headersSerializer, + $platform, ); self::assertSame(1, $stream->index()); @@ -269,25 +274,24 @@ public function testWithNoList(): void ->withHeader(new EventIdHeader('1')) ->withHeader(new RecordedOnHeader(new DateTimeImmutable('2022-10-10 10:10:10'))); - $eventSerializer = $this->prophesize(EventSerializer::class); - $eventSerializer->deserialize(new SerializedEvent('profile_created', '{}')) - ->shouldBeCalledOnce() + $eventSerializer = $this->createMock(EventSerializer::class); + $eventSerializer->expects($this->once())->method('deserialize')->with(new SerializedEvent('profile_created', '{}')) ->willReturn($event); - $headersSerializer = $this->prophesize(HeadersSerializer::class); - $headersSerializer->deserialize('{}')->shouldBeCalledOnce()->willReturn([]); + $headersSerializer = $this->createMock(HeadersSerializer::class); + $headersSerializer->expects($this->once())->method('deserialize')->with('{}')->willReturn([]); - $platform = $this->prophesize(AbstractPlatform::class); - $platform->getDateTimeTzFormatString()->shouldBeCalledOnce()->willReturn('Y-m-d H:i:s'); + $platform = $this->createMock(AbstractPlatform::class); + $platform->expects($this->once())->method('getDateTimeTzFormatString')->willReturn('Y-m-d H:i:s'); - $result = $this->prophesize(Result::class); - $result->iterateAssociative()->shouldBeCalledOnce()->willReturn(new ArrayIterator($messages)); + $result = $this->createMock(Result::class); + $result->expects($this->once())->method('iterateAssociative')->willReturn(new ArrayIterator($messages)); $stream = new StreamDoctrineDbalStoreStream( - $result->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), - $platform->reveal(), + $result, + $eventSerializer, + $headersSerializer, + $platform, ); self::assertSame(5, $stream->index()); @@ -331,26 +335,25 @@ public function testClose(): void ->withHeader(new EventIdHeader('1')) ->withHeader(new RecordedOnHeader(new DateTimeImmutable('2022-10-10 10:10:10'))); - $eventSerializer = $this->prophesize(EventSerializer::class); - $eventSerializer->deserialize(new SerializedEvent('profile_created', '{}')) - ->shouldBeCalledOnce() + $eventSerializer = $this->createMock(EventSerializer::class); + $eventSerializer->expects($this->once())->method('deserialize')->with(new SerializedEvent('profile_created', '{}')) ->willReturn($event); - $headersSerializer = $this->prophesize(HeadersSerializer::class); - $headersSerializer->deserialize('{}')->shouldBeCalledOnce()->willReturn([]); + $headersSerializer = $this->createMock(HeadersSerializer::class); + $headersSerializer->expects($this->once())->method('deserialize')->with('{}')->willReturn([]); - $platform = $this->prophesize(AbstractPlatform::class); - $platform->getDateTimeTzFormatString()->shouldBeCalledOnce()->willReturn('Y-m-d H:i:s'); + $platform = $this->createMock(AbstractPlatform::class); + $platform->expects($this->once())->method('getDateTimeTzFormatString')->willReturn('Y-m-d H:i:s'); - $result = $this->prophesize(Result::class); - $result->iterateAssociative()->shouldBeCalledOnce()->willReturn(new ArrayIterator($messages)); - $result->free()->shouldBeCalledOnce(); + $result = $this->createMock(Result::class); + $result->expects($this->once())->method('iterateAssociative')->willReturn(new ArrayIterator($messages)); + $result->expects($this->once())->method('free'); $stream = new StreamDoctrineDbalStoreStream( - $result->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), - $platform->reveal(), + $result, + $eventSerializer, + $headersSerializer, + $platform, ); self::assertSame(1, $stream->index()); @@ -366,18 +369,18 @@ public function testClose(): void public function testPositionEmpty(): void { - $eventSerializer = $this->prophesize(EventSerializer::class); - $headersSerializer = $this->prophesize(HeadersSerializer::class); - $platform = $this->prophesize(AbstractPlatform::class); + $eventSerializer = $this->createMock(EventSerializer::class); + $headersSerializer = $this->createMock(HeadersSerializer::class); + $platform = $this->createMock(AbstractPlatform::class); - $result = $this->prophesize(Result::class); - $result->iterateAssociative()->shouldBeCalledOnce()->willReturn(new ArrayIterator()); + $result = $this->createMock(Result::class); + $result->expects($this->once())->method('iterateAssociative')->willReturn(new ArrayIterator()); $stream = new StreamDoctrineDbalStoreStream( - $result->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), - $platform->reveal(), + $result, + $eventSerializer, + $headersSerializer, + $platform, ); $position = $stream->position(); @@ -407,25 +410,24 @@ public function testPosition(): void Email::fromString('info@patchlevel.de'), ); - $eventSerializer = $this->prophesize(EventSerializer::class); - $eventSerializer->deserialize(new SerializedEvent('profile_created', '{}')) - ->shouldBeCalledOnce() + $eventSerializer = $this->createMock(EventSerializer::class); + $eventSerializer->expects($this->once())->method('deserialize')->with(new SerializedEvent('profile_created', '{}')) ->willReturn($event); - $headersSerializer = $this->prophesize(HeadersSerializer::class); - $headersSerializer->deserialize('{}')->shouldBeCalledOnce()->willReturn([]); + $headersSerializer = $this->createMock(HeadersSerializer::class); + $headersSerializer->expects($this->once())->method('deserialize')->with('{}')->willReturn([]); - $platform = $this->prophesize(AbstractPlatform::class); - $platform->getDateTimeTzFormatString()->shouldBeCalledOnce()->willReturn('Y-m-d H:i:s'); + $platform = $this->createMock(AbstractPlatform::class); + $platform->expects($this->once())->method('getDateTimeTzFormatString')->willReturn('Y-m-d H:i:s'); - $result = $this->prophesize(Result::class); - $result->iterateAssociative()->shouldBeCalledOnce()->willReturn(new ArrayIterator($messages)); + $result = $this->createMock(Result::class); + $result->expects($this->once())->method('iterateAssociative')->willReturn(new ArrayIterator($messages)); $stream = new StreamDoctrineDbalStoreStream( - $result->reveal(), - $eventSerializer->reveal(), - $headersSerializer->reveal(), - $platform->reveal(), + $result, + $eventSerializer, + $headersSerializer, + $platform, ); $position = $stream->position(); diff --git a/tests/Unit/Store/StreamReadOnlyStoreTest.php b/tests/Unit/Store/StreamReadOnlyStoreTest.php index d6909514..f8eb8855 100644 --- a/tests/Unit/Store/StreamReadOnlyStoreTest.php +++ b/tests/Unit/Store/StreamReadOnlyStoreTest.php @@ -12,21 +12,18 @@ use Patchlevel\EventSourcing\Store\StreamStore; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; #[CoversClass(ReadOnlyStore::class)] final class StreamReadOnlyStoreTest extends TestCase { - use ProphecyTrait; - public function testLoad(): void { $criteria = new Criteria(); - $parentStore = $this->prophesize(StreamStore::class); - $parentStore->load($criteria, 8, 42, true)->shouldBeCalled(); + $parentStore = $this->createMock(StreamStore::class); + $parentStore->expects($this->atLeastOnce())->method('load')->with($criteria, 8, 42, true); - $store = new StreamReadOnlyStore($parentStore->reveal()); + $store = new StreamReadOnlyStore($parentStore); $store->load($criteria, 8, 42, true); } @@ -34,10 +31,10 @@ public function testCount(): void { $criteria = new Criteria(); - $parentStore = $this->prophesize(StreamStore::class); - $parentStore->count($criteria)->shouldBeCalled(); + $parentStore = $this->createMock(StreamStore::class); + $parentStore->expects($this->atLeastOnce())->method('count')->with($criteria); - $store = new StreamReadOnlyStore($parentStore->reveal()); + $store = new StreamReadOnlyStore($parentStore); $store->count($criteria); } @@ -46,10 +43,10 @@ public function testSave(): void $message = new Message(new class () { }); - $parentStore = $this->prophesize(StreamStore::class); - $parentStore->save($message)->shouldNotBeCalled(); + $parentStore = $this->createMock(StreamStore::class); + $parentStore->expects($this->never())->method('save')->with($message); - $store = new StreamReadOnlyStore($parentStore->reveal()); + $store = new StreamReadOnlyStore($parentStore); $this->expectException(StoreIsReadOnly::class); $store->save($message); } @@ -59,19 +56,19 @@ public function testTransactional(): void $callback = static function (): void { }; - $parentStore = $this->prophesize(StreamStore::class); - $parentStore->transactional($callback)->shouldBeCalled(); + $parentStore = $this->createMock(StreamStore::class); + $parentStore->expects($this->atLeastOnce())->method('transactional')->with($callback); - $store = new StreamReadOnlyStore($parentStore->reveal()); + $store = new StreamReadOnlyStore($parentStore); $store->transactional($callback); } public function testStreams(): void { - $parentStore = $this->prophesize(StreamStore::class); - $parentStore->streams()->willReturn(['foo', 'bar'])->shouldBeCalled(); + $parentStore = $this->createMock(StreamStore::class); + $parentStore->expects($this->atLeastOnce())->method('streams')->willReturn(['foo', 'bar']); - $store = new StreamReadOnlyStore($parentStore->reveal()); + $store = new StreamReadOnlyStore($parentStore); self::assertEquals(['foo', 'bar'], $store->streams()); } @@ -80,10 +77,10 @@ public function testRemove(): void { $criteria = new Criteria(); - $parentStore = $this->prophesize(StreamStore::class); - $parentStore->remove($criteria)->shouldNotBeCalled(); + $parentStore = $this->createMock(StreamStore::class); + $parentStore->expects($this->never())->method('remove')->with($criteria); - $store = new StreamReadOnlyStore($parentStore->reveal()); + $store = new StreamReadOnlyStore($parentStore); $this->expectException(StoreIsReadOnly::class); $store->remove($criteria); } @@ -92,10 +89,10 @@ public function testArchive(): void { $criteria = new Criteria(); - $parentStore = $this->prophesize(StreamStore::class); - $parentStore->archive($criteria)->shouldNotBeCalled(); + $parentStore = $this->createMock(StreamStore::class); + $parentStore->expects($this->never())->method('archive')->with($criteria); - $store = new StreamReadOnlyStore($parentStore->reveal()); + $store = new StreamReadOnlyStore($parentStore); $this->expectException(StoreIsReadOnly::class); $store->archive($criteria); } diff --git a/tests/Unit/Subscription/Engine/CatchUpSubscriptionEngineTest.php b/tests/Unit/Subscription/Engine/CatchUpSubscriptionEngineTest.php index 2833a2f6..47d04d2f 100644 --- a/tests/Unit/Subscription/Engine/CatchUpSubscriptionEngineTest.php +++ b/tests/Unit/Subscription/Engine/CatchUpSubscriptionEngineTest.php @@ -13,24 +13,21 @@ use Patchlevel\EventSourcing\Subscription\Subscription; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; use RuntimeException; #[CoversClass(CatchUpSubscriptionEngine::class)] final class CatchUpSubscriptionEngineTest extends TestCase { - use ProphecyTrait; - public function testSetup(): void { - $parent = $this->prophesize(SubscriptionEngine::class); + $parent = $this->createMock(SubscriptionEngine::class); - $engine = new CatchUpSubscriptionEngine($parent->reveal()); + $engine = new CatchUpSubscriptionEngine($parent); $criteria = new SubscriptionEngineCriteria(); $expectedResult = new Result(); - $parent->setup($criteria, true)->willReturn($expectedResult)->shouldBeCalledOnce(); + $parent->expects($this->once())->method('setup')->with($criteria, true)->willReturn($expectedResult); $result = $engine->setup($criteria, true); self::assertSame($expectedResult, $result); @@ -38,14 +35,14 @@ public function testSetup(): void public function testBootFinished(): void { - $parent = $this->prophesize(SubscriptionEngine::class); + $parent = $this->createMock(SubscriptionEngine::class); - $engine = new CatchUpSubscriptionEngine($parent->reveal()); + $engine = new CatchUpSubscriptionEngine($parent); $criteria = new SubscriptionEngineCriteria(); $expectedResult = new ProcessedResult(0); - $parent->boot($criteria, 42)->willReturn($expectedResult)->shouldBeCalledTimes(1); + $parent->expects($this->exactly(1))->method('boot')->with($criteria, 42)->willReturn($expectedResult); $result = $engine->boot($criteria, 42); self::assertEquals($expectedResult, $result); @@ -53,9 +50,9 @@ public function testBootFinished(): void public function testBootSecondTime(): void { - $parent = $this->prophesize(SubscriptionEngine::class); + $parent = $this->createMock(SubscriptionEngine::class); - $engine = new CatchUpSubscriptionEngine($parent->reveal()); + $engine = new CatchUpSubscriptionEngine($parent); $criteria = new SubscriptionEngineCriteria(); $error = new Error( @@ -64,10 +61,10 @@ public function testBootSecondTime(): void new RuntimeException('baz'), ); - $parent->boot($criteria, 42)->willReturn( + $parent->expects($this->exactly(2))->method('boot')->with($criteria, 42)->willReturn( new ProcessedResult(1), new ProcessedResult(0, true, [$error]), - )->shouldBeCalledTimes(2); + ); $result = $engine->boot($criteria, 42); @@ -76,15 +73,15 @@ public function testBootSecondTime(): void public function testBootLimit(): void { - $parent = $this->prophesize(SubscriptionEngine::class); + $parent = $this->createMock(SubscriptionEngine::class); - $engine = new CatchUpSubscriptionEngine($parent->reveal(), 2); + $engine = new CatchUpSubscriptionEngine($parent, 2); $criteria = new SubscriptionEngineCriteria(); - $parent->boot($criteria, 42)->willReturn( + $parent->expects($this->exactly(2))->method('boot')->with($criteria, 42)->willReturn( new ProcessedResult(1), new ProcessedResult(1), - )->shouldBeCalledTimes(2); + ); $result = $engine->boot($criteria, 42); @@ -93,14 +90,14 @@ public function testBootLimit(): void public function testRunFinished(): void { - $parent = $this->prophesize(SubscriptionEngine::class); + $parent = $this->createMock(SubscriptionEngine::class); - $engine = new CatchUpSubscriptionEngine($parent->reveal()); + $engine = new CatchUpSubscriptionEngine($parent); $criteria = new SubscriptionEngineCriteria(); $expectedResult = new ProcessedResult(0); - $parent->run($criteria, 42)->willReturn($expectedResult)->shouldBeCalledOnce(); + $parent->expects($this->once())->method('run')->with($criteria, 42)->willReturn($expectedResult); $result = $engine->run($criteria, 42); self::assertEquals($expectedResult, $result); @@ -108,9 +105,9 @@ public function testRunFinished(): void public function testRunSecondTime(): void { - $parent = $this->prophesize(SubscriptionEngine::class); + $parent = $this->createMock(SubscriptionEngine::class); - $engine = new CatchUpSubscriptionEngine($parent->reveal()); + $engine = new CatchUpSubscriptionEngine($parent); $criteria = new SubscriptionEngineCriteria(); $error = new Error( @@ -119,10 +116,10 @@ public function testRunSecondTime(): void new RuntimeException('baz'), ); - $parent->run($criteria, 42)->willReturn( + $parent->expects($this->exactly(2))->method('run')->with($criteria, 42)->willReturn( new ProcessedResult(1, true, [$error]), new ProcessedResult(0), - )->shouldBeCalledTimes(2); + ); $result = $engine->run($criteria, 42); self::assertEquals(new ProcessedResult(1, false, [$error]), $result); @@ -130,15 +127,15 @@ public function testRunSecondTime(): void public function testRunLimit(): void { - $parent = $this->prophesize(SubscriptionEngine::class); + $parent = $this->createMock(SubscriptionEngine::class); - $engine = new CatchUpSubscriptionEngine($parent->reveal(), 2); + $engine = new CatchUpSubscriptionEngine($parent, 2); $criteria = new SubscriptionEngineCriteria(); - $parent->run($criteria, 42)->willReturn( + $parent->expects($this->exactly(2))->method('run')->with($criteria, 42)->willReturn( new ProcessedResult(1), new ProcessedResult(1), - )->shouldBeCalledTimes(2); + ); $result = $engine->run($criteria, 42); @@ -147,14 +144,14 @@ public function testRunLimit(): void public function testTeardown(): void { - $parent = $this->prophesize(SubscriptionEngine::class); + $parent = $this->createMock(SubscriptionEngine::class); - $engine = new CatchUpSubscriptionEngine($parent->reveal()); + $engine = new CatchUpSubscriptionEngine($parent); $criteria = new SubscriptionEngineCriteria(); $expectedResult = new Result(); - $parent->teardown($criteria)->willReturn($expectedResult)->shouldBeCalledOnce(); + $parent->expects($this->once())->method('teardown')->with($criteria)->willReturn($expectedResult); $result = $engine->teardown($criteria); self::assertSame($expectedResult, $result); @@ -162,14 +159,14 @@ public function testTeardown(): void public function testRemove(): void { - $parent = $this->prophesize(SubscriptionEngine::class); + $parent = $this->createMock(SubscriptionEngine::class); - $engine = new CatchUpSubscriptionEngine($parent->reveal()); + $engine = new CatchUpSubscriptionEngine($parent); $criteria = new SubscriptionEngineCriteria(); $expectedResult = new Result(); - $parent->remove($criteria)->willReturn($expectedResult)->shouldBeCalledOnce(); + $parent->expects($this->once())->method('remove')->with($criteria)->willReturn($expectedResult); $result = $engine->remove($criteria); self::assertSame($expectedResult, $result); @@ -177,14 +174,14 @@ public function testRemove(): void public function testReactivate(): void { - $parent = $this->prophesize(SubscriptionEngine::class); + $parent = $this->createMock(SubscriptionEngine::class); - $engine = new CatchUpSubscriptionEngine($parent->reveal()); + $engine = new CatchUpSubscriptionEngine($parent); $criteria = new SubscriptionEngineCriteria(); $expectedResult = new Result(); - $parent->reactivate($criteria)->willReturn($expectedResult)->shouldBeCalledOnce(); + $parent->expects($this->once())->method('reactivate')->with($criteria)->willReturn($expectedResult); $result = $engine->reactivate($criteria); self::assertSame($expectedResult, $result); @@ -192,14 +189,14 @@ public function testReactivate(): void public function testPause(): void { - $parent = $this->prophesize(SubscriptionEngine::class); + $parent = $this->createMock(SubscriptionEngine::class); - $engine = new CatchUpSubscriptionEngine($parent->reveal()); + $engine = new CatchUpSubscriptionEngine($parent); $criteria = new SubscriptionEngineCriteria(); $expectedResult = new Result(); - $parent->pause($criteria)->willReturn($expectedResult)->shouldBeCalledOnce(); + $parent->expects($this->once())->method('pause')->with($criteria)->willReturn($expectedResult); $result = $engine->pause($criteria); self::assertSame($expectedResult, $result); @@ -207,14 +204,14 @@ public function testPause(): void public function testSubscriptions(): void { - $parent = $this->prophesize(SubscriptionEngine::class); + $parent = $this->createMock(SubscriptionEngine::class); - $engine = new CatchUpSubscriptionEngine($parent->reveal()); + $engine = new CatchUpSubscriptionEngine($parent); $criteria = new SubscriptionEngineCriteria(); $expectedSubscriptions = [new Subscription('foo')]; - $parent->subscriptions($criteria)->willReturn($expectedSubscriptions)->shouldBeCalledOnce(); + $parent->expects($this->once())->method('subscriptions')->with($criteria)->willReturn($expectedSubscriptions); $subscriptions = $engine->subscriptions($criteria); self::assertEquals($expectedSubscriptions, $subscriptions); diff --git a/tests/Unit/Subscription/Engine/DefaultSubscriptionEngineTest.php b/tests/Unit/Subscription/Engine/DefaultSubscriptionEngineTest.php index 30372e27..554116e5 100644 --- a/tests/Unit/Subscription/Engine/DefaultSubscriptionEngineTest.php +++ b/tests/Unit/Subscription/Engine/DefaultSubscriptionEngineTest.php @@ -4,7 +4,6 @@ namespace Patchlevel\EventSourcing\Tests\Unit\Subscription\Engine; -use Closure; use Generator; use Patchlevel\EventSourcing\Attribute\OnFailed; use Patchlevel\EventSourcing\Attribute\RetryStrategy as RetryStrategyName; @@ -33,6 +32,7 @@ use Patchlevel\EventSourcing\Subscription\Subscription; use Patchlevel\EventSourcing\Subscription\SubscriptionError; use Patchlevel\EventSourcing\Subscription\ThrowableToErrorContextTransformer; +use Patchlevel\EventSourcing\Tests\ReturnCallback; use Patchlevel\EventSourcing\Tests\Unit\Fixture\BatchingSubscriber; use Patchlevel\EventSourcing\Tests\Unit\Fixture\ProfileId; use Patchlevel\EventSourcing\Tests\Unit\Fixture\ProfileVisited; @@ -40,25 +40,21 @@ use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; -use Prophecy\Argument; -use Prophecy\PhpUnit\ProphecyTrait; use Psr\Log\NullLogger; use RuntimeException; #[CoversClass(DefaultSubscriptionEngine::class)] final class DefaultSubscriptionEngineTest extends TestCase { - use ProphecyTrait; - public function testNothingToSetup(): void { - $streamableStore = $this->prophesize(Store::class); - $streamableStore->load($this->criteria())->shouldNotBeCalled(); + $streamableStore = $this->createMock(Store::class); + $streamableStore->expects($this->never())->method('load')->with($this->criteria()); $store = new DummySubscriptionStore(); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $store, new MetadataSubscriberAccessorRepository([]), logger: new NullLogger(), @@ -79,13 +75,13 @@ class { $message1 = new Message(new ProfileVisited(ProfileId::fromString('test'))); - $streamableStore = $this->prophesize(Store::class); - $streamableStore->load(null, 1, null, true)->willReturn(new ArrayStream([$message1]))->shouldBeCalledOnce(); + $streamableStore = $this->createMock(Store::class); + $streamableStore->expects($this->once())->method('load')->with(null, 1, null, true)->willReturn(new ArrayStream([$message1])); $subscriptionStore = new DummySubscriptionStore(); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), logger: new NullLogger(), @@ -132,11 +128,11 @@ public function create(): void $message1 = new Message(new ProfileVisited(ProfileId::fromString('test'))); - $streamableStore = $this->prophesize(Store::class); - $streamableStore->load(null, 1, null, true)->willReturn(new ArrayStream([$message1]))->shouldBeCalledOnce(); + $streamableStore = $this->createMock(Store::class); + $streamableStore->expects($this->once())->method('load')->with(null, 1, null, true)->willReturn(new ArrayStream([$message1])); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), logger: new NullLogger(), @@ -190,11 +186,11 @@ public function create(): void $message1 = new Message(new ProfileVisited(ProfileId::fromString('test'))); - $streamableStore = $this->prophesize(Store::class); - $streamableStore->load(null, 1, null, true)->willReturn(new ArrayStream([$message1]))->shouldBeCalledOnce(); + $streamableStore = $this->createMock(Store::class); + $streamableStore->expects($this->once())->method('load')->with(null, 1, null, true)->willReturn(new ArrayStream([$message1])); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), logger: new NullLogger(), @@ -250,11 +246,11 @@ public function create(): void $message1 = new Message(new ProfileVisited(ProfileId::fromString('test'))); - $streamableStore = $this->prophesize(Store::class); - $streamableStore->load(null, 1, null, true)->willReturn(new ArrayStream([$message1]))->shouldBeCalledOnce(); + $streamableStore = $this->createMock(Store::class); + $streamableStore->expects($this->once())->method('load')->with(null, 1, null, true)->willReturn(new ArrayStream([$message1])); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), logger: new NullLogger(), @@ -314,11 +310,11 @@ public function onFailed(): void $message1 = new Message(new ProfileVisited(ProfileId::fromString('test'))); - $streamableStore = $this->prophesize(Store::class); - $streamableStore->load(null, 1, null, true)->willReturn(new ArrayStream([$message1]))->shouldBeCalledOnce(); + $streamableStore = $this->createMock(Store::class); + $streamableStore->expects($this->once())->method('load')->with(null, 1, null, true)->willReturn(new ArrayStream([$message1])); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), new NoRetryStrategy(), @@ -369,11 +365,11 @@ class { $message1 = new Message(new ProfileVisited(ProfileId::fromString('test'))); - $streamableStore = $this->prophesize(Store::class); - $streamableStore->load(null, 1, null, true)->willReturn(new ArrayStream([$message1]))->shouldBeCalledOnce(); + $streamableStore = $this->createMock(Store::class); + $streamableStore->expects($this->once())->method('load')->with(null, 1, null, true)->willReturn(new ArrayStream([$message1])); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), logger: new NullLogger(), @@ -411,11 +407,11 @@ class { $message1 = new Message(new ProfileVisited(ProfileId::fromString('test'))); - $streamableStore = $this->prophesize(Store::class); - $streamableStore->load(null, 1, null, true)->willReturn(new ArrayStream([$message1]))->shouldBeCalledOnce(); + $streamableStore = $this->createMock(Store::class); + $streamableStore->expects($this->once())->method('load')->with(null, 1, null, true)->willReturn(new ArrayStream([$message1])); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), logger: new NullLogger(), @@ -436,7 +432,7 @@ class { ); } - public function testSetupWithFromNowWithEmtpyStream(): void + public function testSetupWithFromNowWithEmptyStream(): void { $subscriptionId = 'test'; $subscriber = new #[Subscriber('test', RunMode::FromNow)] @@ -452,11 +448,11 @@ class { ), ]); - $streamableStore = $this->prophesize(Store::class); - $streamableStore->load(null, 1, null, true)->willReturn(new ArrayStream([]))->shouldBeCalledOnce(); + $streamableStore = $this->createMock(Store::class); + $streamableStore->expects($this->once())->method('load')->with(null, 1, null, true)->willReturn(new ArrayStream([])); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), logger: new NullLogger(), @@ -477,15 +473,63 @@ class { ); } + public function testSetupWithCriteria(): void + { + $subscriber = new #[Subscriber('id1', RunMode::FromBeginning)] + class { + }; + + $subscriptionStore = $this->createMock(SubscriptionStore::class); + $subscriptionStore->expects($this->exactly(3)) + ->method('find') + ->willReturnCallback( + new ReturnCallback([ + [ + [new SubscriptionCriteria()], + [new Subscription('id1')], + ], + [ + [new SubscriptionCriteria(['id1'], ['group1'], [Status::Error])], + [], + ], + [ + [new SubscriptionCriteria(['id1'], ['group1'], [Status::New])], + [], + ], + ]), + ); + + $streamableStore = $this->createMock(Store::class); + $streamableStore + ->expects($this->never()) + ->method('load') + ->with($this->criteria()) + ->willReturn(new ArrayStream([])); + + $engine = new DefaultSubscriptionEngine( + $streamableStore, + $subscriptionStore, + new MetadataSubscriberAccessorRepository([$subscriber]), + logger: new NullLogger(), + ); + + $engineCriteria = new SubscriptionEngineCriteria( + ids: ['id1'], + groups: ['group1'], + ); + + $engine->setup($engineCriteria); + } + public function testNothingToBoot(): void { - $streamableStore = $this->prophesize(Store::class); - $streamableStore->load($this->criteria())->shouldNotBeCalled(); + $streamableStore = $this->createMock(Store::class); + $streamableStore->expects($this->never())->method('load')->with($this->criteria()); $store = new DummySubscriptionStore(); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $store, new MetadataSubscriberAccessorRepository([]), logger: new NullLogger(), @@ -507,13 +551,13 @@ public function testBootDiscoverNewSubscribers(): void class { }; - $streamableStore = $this->prophesize(Store::class); - $streamableStore->load($this->criteria())->shouldNotBeCalled(); + $streamableStore = $this->createMock(Store::class); + $streamableStore->expects($this->never())->method('load')->with($this->criteria()); $subscriptionStore = new DummySubscriptionStore(); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), logger: new NullLogger(), @@ -562,11 +606,11 @@ public function handle(Message $message): void $message = new Message(new ProfileVisited(ProfileId::fromString('test'))); - $streamableStore = $this->prophesize(Store::class); - $streamableStore->load($this->criteria())->willReturn(new ArrayStream([$message]))->shouldBeCalledOnce(); + $streamableStore = $this->createMock(Store::class); + $streamableStore->expects($this->once())->method('load')->with($this->criteria())->willReturn(new ArrayStream([$message])); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), logger: new NullLogger(), @@ -621,11 +665,11 @@ public function handle(Message $message): void $message = new Message(new ProfileVisited(ProfileId::fromString('test'))); - $streamableStore = $this->prophesize(Store::class); - $streamableStore->load($this->criteria())->willReturn(new ArrayStream([$message]))->shouldBeCalledOnce(); + $streamableStore = $this->createMock(Store::class); + $streamableStore->expects($this->once())->method('load')->with($this->criteria())->willReturn(new ArrayStream([$message])); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), logger: new NullLogger(), @@ -688,11 +732,11 @@ public function handle(Message $message): void $message = new Message(new ProfileVisited(ProfileId::fromString('test'))); - $streamableStore = $this->prophesize(Store::class); - $streamableStore->load($this->criteria())->willReturn(new ArrayStream([$message]))->shouldBeCalledOnce(); + $streamableStore = $this->createMock(Store::class); + $streamableStore->expects($this->once())->method('load')->with($this->criteria())->willReturn(new ArrayStream([$message])); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), logger: new NullLogger(), @@ -759,11 +803,11 @@ public function onFailed(): void $message = new Message(new ProfileVisited(ProfileId::fromString('test'))); - $streamableStore = $this->prophesize(Store::class); - $streamableStore->load($this->criteria())->willReturn(new ArrayStream([$message]))->shouldBeCalledOnce(); + $streamableStore = $this->createMock(Store::class); + $streamableStore->expects($this->once())->method('load')->with($this->criteria())->willReturn(new ArrayStream([$message])); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), new NoRetryStrategy(), @@ -827,11 +871,11 @@ public function onFailed(): void $message = new Message(new ProfileVisited(ProfileId::fromString('test'))); - $streamableStore = $this->prophesize(Store::class); - $streamableStore->load($this->criteria())->willReturn(new ArrayStream([$message]))->shouldBeCalledOnce(); + $streamableStore = $this->createMock(Store::class); + $streamableStore->expects($this->once())->method('load')->with($this->criteria())->willReturn(new ArrayStream([$message])); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), new NoRetryStrategy(), @@ -919,11 +963,11 @@ public function forceCommit(): bool $message = new Message(new ProfileVisited(ProfileId::fromString('test'))); - $streamableStore = $this->prophesize(Store::class); - $streamableStore->load($this->criteria())->willReturn(new ArrayStream([$message]))->shouldBeCalledOnce(); + $streamableStore = $this->createMock(Store::class); + $streamableStore->expects($this->once())->method('load')->with($this->criteria())->willReturn(new ArrayStream([$message])); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), new NoRetryStrategy(), @@ -983,11 +1027,11 @@ public function handle(Message $message): void $message = new Message(new ProfileVisited(ProfileId::fromString('test'))); - $streamableStore = $this->prophesize(Store::class); - $streamableStore->load($this->criteria())->willReturn(new ArrayStream([$message]))->shouldBeCalledOnce(); + $streamableStore = $this->createMock(Store::class); + $streamableStore->expects($this->once())->method('load')->with($this->criteria())->willReturn(new ArrayStream([$message])); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), logger: new NullLogger(), @@ -1058,11 +1102,11 @@ public function handle(Message $message): void $message = new Message(new ProfileVisited(ProfileId::fromString('test'))); - $streamableStore = $this->prophesize(Store::class); - $streamableStore->load($this->criteria())->willReturn(new ArrayStream([$message]))->shouldBeCalledOnce(); + $streamableStore = $this->createMock(Store::class); + $streamableStore->expects($this->once())->method('load')->with($this->criteria())->willReturn(new ArrayStream([$message])); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber1, $subscriber2]), logger: new NullLogger(), @@ -1122,14 +1166,14 @@ public function handle(Message $message): void $message1 = new Message(new ProfileVisited(ProfileId::fromString('test'))); $message2 = new Message(new ProfileVisited(ProfileId::fromString('test'))); - $streamableStore = $this->prophesize(Store::class); - $streamableStore->load($this->criteria())->willReturn(new ArrayStream([ + $streamableStore = $this->createMock(Store::class); + $streamableStore->expects($this->once())->method('load')->with($this->criteria())->willReturn(new ArrayStream([ 1 => $message1, 3 => $message2, - ]))->shouldBeCalledOnce(); + ])); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), logger: new NullLogger(), @@ -1179,11 +1223,11 @@ public function handle(Message $message): void $message1 = new Message(new ProfileVisited(ProfileId::fromString('test'))); - $streamableStore = $this->prophesize(Store::class); - $streamableStore->load($this->criteria())->willReturn(new ArrayStream([$message1]))->shouldBeCalledOnce(); + $streamableStore = $this->createMock(Store::class); + $streamableStore->expects($this->once())->method('load')->with($this->criteria())->willReturn(new ArrayStream([$message1])); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), logger: new NullLogger(), @@ -1233,11 +1277,11 @@ public function handle(): void $message1 = new Message(new ProfileVisited(ProfileId::fromString('test'))); - $streamableStore = $this->prophesize(Store::class); - $streamableStore->load($this->criteria())->willReturn(new ArrayStream([$message1]))->shouldBeCalledOnce(); + $streamableStore = $this->createMock(Store::class); + $streamableStore->expects($this->once())->method('load')->with($this->criteria())->willReturn(new ArrayStream([$message1])); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), logger: new NullLogger(), @@ -1276,12 +1320,23 @@ public function handle(Message $message): void $message = new Message(new ProfileVisited(ProfileId::fromString('test'))); - $streamableStore = $this->prophesize(Store::class); - $streamableStore->load($this->criteria())->willReturn(new ArrayStream([$message]))->shouldBeCalledOnce(); - $streamableStore->load($this->criteria(1))->willReturn(new ArrayStream([]))->shouldBeCalledOnce(); + $streamableStore = $this->createMock(Store::class); + $streamableStore + ->expects($this->exactly(2)) + ->method('load') + ->willReturnCallback(new ReturnCallback([ + [ + [$this->criteria(), null, null, false], + new ArrayStream([$message]), + ], + [ + [$this->criteria(1), null, null, false], + new ArrayStream([]), + ], + ])); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), logger: new NullLogger(), @@ -1340,11 +1395,11 @@ public function testBootWithoutSubscriber(): void $message = new Message(new ProfileVisited(ProfileId::fromString('test'))); - $streamableStore = $this->prophesize(Store::class); - $streamableStore->load($this->criteria())->willReturn(new ArrayStream([$message]))->shouldBeCalledOnce(); + $streamableStore = $this->createMock(Store::class); + $streamableStore->expects($this->once())->method('load')->with($this->criteria())->willReturn(new ArrayStream([$message])); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([]), logger: new NullLogger(), @@ -1374,11 +1429,11 @@ public function testBootBatchingSuccess(): void $message = new Message(new ProfileVisited(ProfileId::fromString('test'))); - $streamableStore = $this->prophesize(Store::class); - $streamableStore->load($this->criteria())->willReturn(new ArrayStream([$message]))->shouldBeCalledOnce(); + $streamableStore = $this->createMock(Store::class); + $streamableStore->expects($this->once())->method('load')->with($this->criteria())->willReturn(new ArrayStream([$message])); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), logger: new NullLogger(), @@ -1426,14 +1481,14 @@ public function testBootBatchingSuccessForceCommit(): void $message1 = new Message(new ProfileVisited(ProfileId::fromString('test'))); $message2 = new Message(new ProfileVisited(ProfileId::fromString('test'))); - $streamableStore = $this->prophesize(Store::class); - $streamableStore->load($this->criteria())->willReturn(new ArrayStream([ + $streamableStore = $this->createMock(Store::class); + $streamableStore->expects($this->once())->method('load')->with($this->criteria())->willReturn(new ArrayStream([ $message1, $message2, - ]))->shouldBeCalledOnce(); + ])); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), logger: new NullLogger(), @@ -1480,11 +1535,11 @@ public function testBootBatchingWithHandleError(): void $message = new Message(new ProfileVisited(ProfileId::fromString('test'))); - $streamableStore = $this->prophesize(Store::class); - $streamableStore->load($this->criteria())->willReturn(new ArrayStream([$message]))->shouldBeCalledOnce(); + $streamableStore = $this->createMock(Store::class); + $streamableStore->expects($this->once())->method('load')->with($this->criteria())->willReturn(new ArrayStream([$message])); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), logger: new NullLogger(), @@ -1539,11 +1594,11 @@ public function testBootBatchingWithBeginBatchError(): void $message = new Message(new ProfileVisited(ProfileId::fromString('test'))); - $streamableStore = $this->prophesize(Store::class); - $streamableStore->load($this->criteria())->willReturn(new ArrayStream([$message]))->shouldBeCalledOnce(); + $streamableStore = $this->createMock(Store::class); + $streamableStore->expects($this->once())->method('load')->with($this->criteria())->willReturn(new ArrayStream([$message])); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), logger: new NullLogger(), @@ -1598,11 +1653,11 @@ public function testBootBatchingWithCommitBatchError(): void $message = new Message(new ProfileVisited(ProfileId::fromString('test'))); - $streamableStore = $this->prophesize(Store::class); - $streamableStore->load($this->criteria())->willReturn(new ArrayStream([$message]))->shouldBeCalledOnce(); + $streamableStore = $this->createMock(Store::class); + $streamableStore->expects($this->once())->method('load')->with($this->criteria())->willReturn(new ArrayStream([$message])); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), logger: new NullLogger(), @@ -1658,11 +1713,11 @@ public function testBootBatchingWithRollbackBatchError(): void $message = new Message(new ProfileVisited(ProfileId::fromString('test'))); - $streamableStore = $this->prophesize(Store::class); - $streamableStore->load($this->criteria())->willReturn(new ArrayStream([$message]))->shouldBeCalledOnce(); + $streamableStore = $this->createMock(Store::class); + $streamableStore->expects($this->once())->method('load')->with($this->criteria())->willReturn(new ArrayStream([$message])); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), logger: new NullLogger(), @@ -1700,6 +1755,52 @@ public function testBootBatchingWithRollbackBatchError(): void self::assertSame(1, $subscriber->rollbackBatchCalled); } + public function testBootWithCriteria(): void + { + $subscriber = new #[Subscriber('id1', RunMode::FromBeginning)] + class { + }; + + $subscriptionStore = $this->createMock(SubscriptionStore::class); + $subscriptionStore->expects($this->exactly(3)) + ->method('find') + ->willReturnCallback(new ReturnCallback([ + [ + [new SubscriptionCriteria()], + [new Subscription('id1')], + ], + [ + [new SubscriptionCriteria(['id1'], ['group1'], [Status::Error])], + [], + ], + [ + [new SubscriptionCriteria(['id1'], ['group1'], [Status::Booting])], + [], + ], + ])); + + $streamableStore = $this->createMock(Store::class); + $streamableStore + ->expects($this->never()) + ->method('load') + ->with($this->criteria()) + ->willReturn(new ArrayStream([])); + + $engine = new DefaultSubscriptionEngine( + $streamableStore, + $subscriptionStore, + new MetadataSubscriberAccessorRepository([$subscriber]), + logger: new NullLogger(), + ); + + $engineCriteria = new SubscriptionEngineCriteria( + ids: ['id1'], + groups: ['group1'], + ); + + $engine->boot($engineCriteria); + } + public function testRunDiscoverNewSubscribers(): void { $subscriptionId = 'test'; @@ -1707,11 +1808,11 @@ public function testRunDiscoverNewSubscribers(): void class { }; - $streamableStore = $this->prophesize(Store::class); + $streamableStore = $this->createMock(Store::class); $subscriptionStore = new DummySubscriptionStore(); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), logger: new NullLogger(), @@ -1758,11 +1859,11 @@ public function handle(Message $message): void $message = new Message(new ProfileVisited(ProfileId::fromString('test'))); - $streamableStore = $this->prophesize(Store::class); - $streamableStore->load($this->criteria())->willReturn(new ArrayStream([$message]))->shouldBeCalledOnce(); + $streamableStore = $this->createMock(Store::class); + $streamableStore->expects($this->once())->method('load')->with($this->criteria())->willReturn(new ArrayStream([$message])); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), logger: new NullLogger(), @@ -1813,14 +1914,12 @@ public function handle(Message $message): void $message1 = new Message(new ProfileVisited(ProfileId::fromString('test'))); $message2 = new Message(new ProfileVisited(ProfileId::fromString('test'))); - $streamableStore = $this->prophesize(Store::class); - $streamableStore - ->load($this->criteria()) - ->willReturn(new ArrayStream([$message1, $message2])) - ->shouldBeCalledOnce(); + $streamableStore = $this->createMock(Store::class); + $streamableStore->expects($this->once())->method('load')->with($this->criteria()) + ->willReturn(new ArrayStream([$message1, $message2])); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), logger: new NullLogger(), @@ -1889,11 +1988,11 @@ public function handle(Message $message): void $message = new Message(new ProfileVisited(ProfileId::fromString('test'))); - $streamableStore = $this->prophesize(Store::class); - $streamableStore->load($this->criteria())->willReturn(new ArrayStream([$message]))->shouldBeCalledOnce(); + $streamableStore = $this->createMock(Store::class); + $streamableStore->expects($this->once())->method('load')->with($this->criteria())->willReturn(new ArrayStream([$message])); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber1, $subscriber2]), logger: new NullLogger(), @@ -1954,11 +2053,11 @@ public function handle(Message $message): void $message = new Message(new ProfileVisited(ProfileId::fromString('test'))); - $streamableStore = $this->prophesize(Store::class); - $streamableStore->load($this->criteria())->willReturn(new ArrayStream([$message]))->shouldBeCalledOnce(); + $streamableStore = $this->createMock(Store::class); + $streamableStore->expects($this->once())->method('load')->with($this->criteria())->willReturn(new ArrayStream([$message])); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), logger: new NullLogger(), @@ -2021,11 +2120,11 @@ public function handle(Message $message): void $message = new Message(new ProfileVisited(ProfileId::fromString('test'))); - $streamableStore = $this->prophesize(Store::class); - $streamableStore->load($this->criteria())->willReturn(new ArrayStream([$message]))->shouldBeCalledOnce(); + $streamableStore = $this->createMock(Store::class); + $streamableStore->expects($this->once())->method('load')->with($this->criteria())->willReturn(new ArrayStream([$message])); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), logger: new NullLogger(), @@ -2092,11 +2191,11 @@ public function onFailed(): void $message = new Message(new ProfileVisited(ProfileId::fromString('test'))); - $streamableStore = $this->prophesize(Store::class); - $streamableStore->load($this->criteria())->willReturn(new ArrayStream([$message]))->shouldBeCalledOnce(); + $streamableStore = $this->createMock(Store::class); + $streamableStore->expects($this->once())->method('load')->with($this->criteria())->willReturn(new ArrayStream([$message])); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), new NoRetryStrategy(), @@ -2160,11 +2259,11 @@ public function onFailed(): void $message = new Message(new ProfileVisited(ProfileId::fromString('test'))); - $streamableStore = $this->prophesize(Store::class); - $streamableStore->load($this->criteria())->willReturn(new ArrayStream([$message]))->shouldBeCalledOnce(); + $streamableStore = $this->createMock(Store::class); + $streamableStore->expects($this->once())->method('load')->with($this->criteria())->willReturn(new ArrayStream([$message])); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), new NoRetryStrategy(), @@ -2212,11 +2311,11 @@ public function testRunningMarkDetached(): void ), ]); - $streamableStore = $this->prophesize(Store::class); - $streamableStore->load($this->criteria())->shouldNotBeCalled(); + $streamableStore = $this->createMock(Store::class); + $streamableStore->expects($this->never())->method('load')->with($this->criteria()); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([]), logger: new NullLogger(), @@ -2252,11 +2351,11 @@ public function testRunningWithoutActiveSubscribers(): void ), ]); - $streamableStore = $this->prophesize(Store::class); - $streamableStore->load($this->criteria())->shouldNotBeCalled(); + $streamableStore = $this->createMock(Store::class); + $streamableStore->expects($this->never())->method('load')->with($this->criteria()); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([]), logger: new NullLogger(), @@ -2298,14 +2397,14 @@ public function handle(Message $message): void $message1 = new Message(new ProfileVisited(ProfileId::fromString('test'))); $message2 = new Message(new ProfileVisited(ProfileId::fromString('test'))); - $streamableStore = $this->prophesize(Store::class); - $streamableStore->load($this->criteria())->willReturn(new ArrayStream([ + $streamableStore = $this->createMock(Store::class); + $streamableStore->expects($this->once())->method('load')->with($this->criteria())->willReturn(new ArrayStream([ 1 => $message1, 3 => $message2, - ]))->shouldBeCalledOnce(); + ])); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), logger: new NullLogger(), @@ -2355,11 +2454,11 @@ public function handle(Message $message): void $message1 = new Message(new ProfileVisited(ProfileId::fromString('test'))); - $streamableStore = $this->prophesize(Store::class); - $streamableStore->load($this->criteria())->willReturn(new ArrayStream([$message1]))->shouldBeCalledOnce(); + $streamableStore = $this->createMock(Store::class); + $streamableStore->expects($this->once())->method('load')->with($this->criteria())->willReturn(new ArrayStream([$message1])); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), logger: new NullLogger(), @@ -2409,11 +2508,11 @@ public function handle(): void $message1 = new Message(new ProfileVisited(ProfileId::fromString('test'))); - $streamableStore = $this->prophesize(Store::class); - $streamableStore->load($this->criteria())->willReturn(new ArrayStream([$message1]))->shouldBeCalledOnce(); + $streamableStore = $this->createMock(Store::class); + $streamableStore->expects($this->once())->method('load')->with($this->criteria())->willReturn(new ArrayStream([$message1])); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), logger: new NullLogger(), @@ -2452,12 +2551,23 @@ public function handle(Message $message): void $message = new Message(new ProfileVisited(ProfileId::fromString('test'))); - $streamableStore = $this->prophesize(Store::class); - $streamableStore->load($this->criteria())->willReturn(new ArrayStream([$message]))->shouldBeCalledOnce(); - $streamableStore->load($this->criteria(1))->willReturn(new ArrayStream([]))->shouldBeCalledOnce(); + $streamableStore = $this->createMock(Store::class); + $streamableStore + ->expects($this->exactly(2)) + ->method('load') + ->willReturnCallback(new ReturnCallback([ + [ + [$this->criteria(), null, null, false], + new ArrayStream([$message]), + ], + [ + [$this->criteria(1), null, null, false], + new ArrayStream([]), + ], + ])); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), logger: new NullLogger(), @@ -2507,11 +2617,11 @@ public function testRunningBatchingSuccess(): void $message = new Message(new ProfileVisited(ProfileId::fromString('test'))); - $streamableStore = $this->prophesize(Store::class); - $streamableStore->load($this->criteria())->willReturn(new ArrayStream([$message]))->shouldBeCalledOnce(); + $streamableStore = $this->createMock(Store::class); + $streamableStore->expects($this->once())->method('load')->with($this->criteria())->willReturn(new ArrayStream([$message])); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), logger: new NullLogger(), @@ -2558,14 +2668,14 @@ public function testRunningBatchingSuccessForceCommit(): void $message1 = new Message(new ProfileVisited(ProfileId::fromString('test'))); $message2 = new Message(new ProfileVisited(ProfileId::fromString('test'))); - $streamableStore = $this->prophesize(Store::class); - $streamableStore->load($this->criteria())->willReturn(new ArrayStream([ + $streamableStore = $this->createMock(Store::class); + $streamableStore->expects($this->once())->method('load')->with($this->criteria())->willReturn(new ArrayStream([ $message1, $message2, - ]))->shouldBeCalledOnce(); + ])); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), logger: new NullLogger(), @@ -2611,11 +2721,11 @@ public function testRunningBatchingWithHandleError(): void $message = new Message(new ProfileVisited(ProfileId::fromString('test'))); - $streamableStore = $this->prophesize(Store::class); - $streamableStore->load($this->criteria())->willReturn(new ArrayStream([$message]))->shouldBeCalledOnce(); + $streamableStore = $this->createMock(Store::class); + $streamableStore->expects($this->once())->method('load')->with($this->criteria())->willReturn(new ArrayStream([$message])); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), logger: new NullLogger(), @@ -2670,11 +2780,11 @@ public function testRunningBatchingWithBeginBatchError(): void $message = new Message(new ProfileVisited(ProfileId::fromString('test'))); - $streamableStore = $this->prophesize(Store::class); - $streamableStore->load($this->criteria())->willReturn(new ArrayStream([$message]))->shouldBeCalledOnce(); + $streamableStore = $this->createMock(Store::class); + $streamableStore->expects($this->once())->method('load')->with($this->criteria())->willReturn(new ArrayStream([$message])); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), logger: new NullLogger(), @@ -2729,11 +2839,11 @@ public function testRunningBatchingWithCommitBatchError(): void $message = new Message(new ProfileVisited(ProfileId::fromString('test'))); - $streamableStore = $this->prophesize(Store::class); - $streamableStore->load($this->criteria())->willReturn(new ArrayStream([$message]))->shouldBeCalledOnce(); + $streamableStore = $this->createMock(Store::class); + $streamableStore->expects($this->once())->method('load')->with($this->criteria())->willReturn(new ArrayStream([$message])); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), logger: new NullLogger(), @@ -2789,11 +2899,11 @@ public function testRunningBatchingWithRollbackBatchError(): void $message = new Message(new ProfileVisited(ProfileId::fromString('test'))); - $streamableStore = $this->prophesize(Store::class); - $streamableStore->load($this->criteria())->willReturn(new ArrayStream([$message]))->shouldBeCalledOnce(); + $streamableStore = $this->createMock(Store::class); + $streamableStore->expects($this->once())->method('load')->with($this->criteria())->willReturn(new ArrayStream([$message])); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), logger: new NullLogger(), @@ -2831,6 +2941,56 @@ public function testRunningBatchingWithRollbackBatchError(): void self::assertSame(1, $subscriber->rollbackBatchCalled); } + public function testRunWithCriteria(): void + { + $subscriber = new #[Subscriber('id1', RunMode::FromBeginning)] + class { + }; + + $subscriptionStore = $this->createMock(SubscriptionStore::class); + $subscriptionStore->expects($this->exactly(4)) + ->method('find') + ->willReturnCallback(new ReturnCallback([ + [ + [new SubscriptionCriteria()], + [new Subscription('id1')], + ], + [ + [new SubscriptionCriteria(['id1'], ['group1'], [Status::Active, Status::Paused, Status::Finished])], + [], + ], + [ + [new SubscriptionCriteria(['id1'], ['group1'], [Status::Error])], + [], + ], + [ + [new SubscriptionCriteria(['id1'], ['group1'], [Status::Active])], + [], + ], + ])); + + $streamableStore = $this->createMock(Store::class); + $streamableStore + ->expects($this->never()) + ->method('load') + ->with($this->criteria()) + ->willReturn(new ArrayStream([])); + + $engine = new DefaultSubscriptionEngine( + $streamableStore, + $subscriptionStore, + new MetadataSubscriberAccessorRepository([$subscriber]), + logger: new NullLogger(), + ); + + $engineCriteria = new SubscriptionEngineCriteria( + ids: ['id1'], + groups: ['group1'], + ); + + $engine->run($engineCriteria); + } + public function testTeardownDiscoverNewSubscribers(): void { $subscriptionId = 'test'; @@ -2838,11 +2998,11 @@ public function testTeardownDiscoverNewSubscribers(): void class { }; - $streamableStore = $this->prophesize(Store::class); + $streamableStore = $this->createMock(Store::class); $subscriptionStore = new DummySubscriptionStore(); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), logger: new NullLogger(), @@ -2878,10 +3038,10 @@ class { $subscriptionStore = new DummySubscriptionStore([$subscription]); - $streamableStore = $this->prophesize(Store::class); + $streamableStore = $this->createMock(Store::class); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), logger: new NullLogger(), @@ -2919,10 +3079,10 @@ public function drop(): void $subscriptionStore = new DummySubscriptionStore([$subscription]); - $streamableStore = $this->prophesize(Store::class); + $streamableStore = $this->createMock(Store::class); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), logger: new NullLogger(), @@ -2961,10 +3121,10 @@ public function drop(): void ), ]); - $streamableStore = $this->prophesize(Store::class); + $streamableStore = $this->createMock(Store::class); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), logger: new NullLogger(), @@ -2996,10 +3156,10 @@ public function testTeardownWithoutSubscriber(): void ), ]); - $streamableStore = $this->prophesize(Store::class); + $streamableStore = $this->createMock(Store::class); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([]), logger: new NullLogger(), @@ -3012,6 +3172,48 @@ public function testTeardownWithoutSubscriber(): void $subscriptionStore->assertNoChanges(); } + public function testTeardownWithCriteria(): void + { + $subscriber = new #[Subscriber('id1', RunMode::FromBeginning)] + class { + }; + + $subscriptionStore = $this->createMock(SubscriptionStore::class); + $subscriptionStore->expects($this->exactly(2)) + ->method('find') + ->willReturnCallback(new ReturnCallback([ + [ + [new SubscriptionCriteria()], + [new Subscription('id1')], + ], + [ + [new SubscriptionCriteria(['id1'], ['group1'], [Status::Detached])], + [], + ], + ])); + + $streamableStore = $this->createMock(Store::class); + $streamableStore + ->expects($this->never()) + ->method('load') + ->with($this->criteria()) + ->willReturn(new ArrayStream([])); + + $engine = new DefaultSubscriptionEngine( + $streamableStore, + $subscriptionStore, + new MetadataSubscriberAccessorRepository([$subscriber]), + logger: new NullLogger(), + ); + + $engineCriteria = new SubscriptionEngineCriteria( + ids: ['id1'], + groups: ['group1'], + ); + + $engine->teardown($engineCriteria); + } + public function testRemoveDiscoverNewSubscribers(): void { $subscriptionId = 'test'; @@ -3019,11 +3221,11 @@ public function testRemoveDiscoverNewSubscribers(): void class { }; - $streamableStore = $this->prophesize(Store::class); + $streamableStore = $this->createMock(Store::class); $subscriptionStore = new DummySubscriptionStore(); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), logger: new NullLogger(), @@ -3065,10 +3267,10 @@ public function drop(): void ); $subscriptionStore = new DummySubscriptionStore([$subscription]); - $streamableStore = $this->prophesize(Store::class); + $streamableStore = $this->createMock(Store::class); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), logger: new NullLogger(), @@ -3098,10 +3300,10 @@ class { ); $subscriptionStore = new DummySubscriptionStore([$subscription]); - $streamableStore = $this->prophesize(Store::class); + $streamableStore = $this->createMock(Store::class); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), logger: new NullLogger(), @@ -3137,10 +3339,10 @@ public function drop(): void ); $subscriptionStore = new DummySubscriptionStore([$subscription]); - $streamableStore = $this->prophesize(Store::class); + $streamableStore = $this->createMock(Store::class); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), logger: new NullLogger(), @@ -3183,10 +3385,10 @@ public function drop(): void $subscriptionStore = new DummySubscriptionStore([$subscription]); - $streamableStore = $this->prophesize(Store::class); + $streamableStore = $this->createMock(Store::class); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), logger: new NullLogger(), @@ -3213,10 +3415,10 @@ public function testRemoveWithoutSubscriber(): void ); $subscriptionStore = new DummySubscriptionStore([$subscription]); - $streamableStore = $this->prophesize(Store::class); + $streamableStore = $this->createMock(Store::class); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([]), logger: new NullLogger(), @@ -3230,6 +3432,48 @@ public function testRemoveWithoutSubscriber(): void $subscriptionStore->assertRemoved($subscription); } + public function testRemoveWithCriteria(): void + { + $subscriber = new #[Subscriber('id1', RunMode::FromBeginning)] + class { + }; + + $subscriptionStore = $this->createMock(SubscriptionStore::class); + $subscriptionStore->expects($this->exactly(2)) + ->method('find') + ->willReturnCallback(new ReturnCallback([ + [ + [new SubscriptionCriteria()], + [new Subscription('id1')], + ], + [ + [new SubscriptionCriteria(['id1'], ['group1'])], + [], + ], + ])); + + $streamableStore = $this->createMock(Store::class); + $streamableStore + ->expects($this->never()) + ->method('load') + ->with($this->criteria()) + ->willReturn(new ArrayStream([])); + + $engine = new DefaultSubscriptionEngine( + $streamableStore, + $subscriptionStore, + new MetadataSubscriberAccessorRepository([$subscriber]), + logger: new NullLogger(), + ); + + $engineCriteria = new SubscriptionEngineCriteria( + ids: ['id1'], + groups: ['group1'], + ); + + $engine->remove($engineCriteria); + } + public function testReactiveDiscoverNewSubscribers(): void { $subscriptionId = 'test'; @@ -3237,11 +3481,11 @@ public function testReactiveDiscoverNewSubscribers(): void class { }; - $streamableStore = $this->prophesize(Store::class); + $streamableStore = $this->createMock(Store::class); $subscriptionStore = new DummySubscriptionStore(); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), logger: new NullLogger(), @@ -3279,10 +3523,10 @@ class { ), ]); - $streamableStore = $this->prophesize(Store::class); + $streamableStore = $this->createMock(Store::class); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), logger: new NullLogger(), @@ -3319,10 +3563,10 @@ class { ), ]); - $streamableStore = $this->prophesize(Store::class); + $streamableStore = $this->createMock(Store::class); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), logger: new NullLogger(), @@ -3358,10 +3602,10 @@ class { ), ]); - $streamableStore = $this->prophesize(Store::class); + $streamableStore = $this->createMock(Store::class); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), logger: new NullLogger(), @@ -3397,10 +3641,10 @@ class { ), ]); - $streamableStore = $this->prophesize(Store::class); + $streamableStore = $this->createMock(Store::class); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), logger: new NullLogger(), @@ -3420,6 +3664,60 @@ class { ); } + public function testReactivateWithCriteria(): void + { + $subscriber = new #[Subscriber('id1', RunMode::FromBeginning)] + class { + }; + + $subscriptionStore = $this->createMock(SubscriptionStore::class); + $subscriptionStore->expects($this->exactly(2)) + ->method('find') + ->willReturnCallback(new ReturnCallback([ + [ + [new SubscriptionCriteria()], + [new Subscription('id1')], + ], + [ + [ + new SubscriptionCriteria( + ['id1'], + ['group1'], + [ + Status::Error, + Status::Failed, + Status::Detached, + Status::Paused, + Status::Finished, + ], + ), + ], + [], + ], + ])); + + $streamableStore = $this->createMock(Store::class); + $streamableStore + ->expects($this->never()) + ->method('load') + ->with($this->criteria()) + ->willReturn(new ArrayStream([])); + + $engine = new DefaultSubscriptionEngine( + $streamableStore, + $subscriptionStore, + new MetadataSubscriberAccessorRepository([$subscriber]), + logger: new NullLogger(), + ); + + $engineCriteria = new SubscriptionEngineCriteria( + ids: ['id1'], + groups: ['group1'], + ); + + $engine->reactivate($engineCriteria); + } + public function testPauseDiscoverNewSubscribers(): void { $subscriptionId = 'test'; @@ -3427,11 +3725,11 @@ public function testPauseDiscoverNewSubscribers(): void class { }; - $streamableStore = $this->prophesize(Store::class); + $streamableStore = $this->createMock(Store::class); $subscriptionStore = new DummySubscriptionStore(); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), logger: new NullLogger(), @@ -3467,10 +3765,10 @@ class { ), ]); - $streamableStore = $this->prophesize(Store::class); + $streamableStore = $this->createMock(Store::class); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), logger: new NullLogger(), @@ -3506,10 +3804,10 @@ class { ), ]); - $streamableStore = $this->prophesize(Store::class); + $streamableStore = $this->createMock(Store::class); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), logger: new NullLogger(), @@ -3547,10 +3845,10 @@ class { ), ]); - $streamableStore = $this->prophesize(Store::class); + $streamableStore = $this->createMock(Store::class); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), logger: new NullLogger(), @@ -3585,10 +3883,10 @@ public function testPauseWithoutSubscriber(): void ), ]); - $streamableStore = $this->prophesize(Store::class); + $streamableStore = $this->createMock(Store::class); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([]), logger: new NullLogger(), @@ -3608,6 +3906,48 @@ public function testPauseWithoutSubscriber(): void ); } + public function testPauseWithCriteria(): void + { + $subscriber = new #[Subscriber('id1', RunMode::FromBeginning)] + class { + }; + + $subscriptionStore = $this->createMock(SubscriptionStore::class); + $subscriptionStore->expects($this->exactly(2)) + ->method('find') + ->willReturnCallback(new ReturnCallback([ + [ + [new SubscriptionCriteria()], + [new Subscription('id1')], + ], + [ + [new SubscriptionCriteria(['id1'], ['group1'], [Status::Active, Status::Booting, Status::Error])], + [], + ], + ])); + + $streamableStore = $this->createMock(Store::class); + $streamableStore + ->expects($this->never()) + ->method('load') + ->with($this->criteria()) + ->willReturn(new ArrayStream([])); + + $engine = new DefaultSubscriptionEngine( + $streamableStore, + $subscriptionStore, + new MetadataSubscriberAccessorRepository([$subscriber]), + logger: new NullLogger(), + ); + + $engineCriteria = new SubscriptionEngineCriteria( + ids: ['id1'], + groups: ['group1'], + ); + + $engine->pause($engineCriteria); + } + public function testGetSubscriptionAndDiscoverNewSubscribers(): void { $subscriptionId = 'test'; @@ -3615,11 +3955,11 @@ public function testGetSubscriptionAndDiscoverNewSubscribers(): void class { }; - $streamableStore = $this->prophesize(Store::class); + $streamableStore = $this->createMock(Store::class); $subscriptionStore = new DummySubscriptionStore(); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), logger: new NullLogger(), @@ -3659,8 +3999,8 @@ public function subscribe(): void $message = new Message(new ProfileVisited(ProfileId::fromString('test'))); - $streamableStore = $this->prophesize(Store::class); - $streamableStore->load($this->criteria())->willReturn(new ArrayStream([$message]))->shouldBeCalledOnce(); + $streamableStore = $this->createMock(Store::class); + $streamableStore->expects($this->once())->method('load')->with($this->criteria())->willReturn(new ArrayStream([$message])); $subscription = new Subscription( $subscriptionId, @@ -3673,14 +4013,14 @@ public function subscribe(): void $subscriptionStore = new DummySubscriptionStore([$subscription]); - $retryStrategy = $this->prophesize(RetryStrategy::class); - $retryStrategy->shouldRetry($subscription)->willReturn(true); + $retryStrategy = $this->createMock(RetryStrategy::class); + $retryStrategy->method('shouldRetry')->with($subscription)->willReturn(true); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), - $retryStrategy->reveal(), + $retryStrategy, new NullLogger(), ); @@ -3721,7 +4061,7 @@ public function testShouldNotRetryOtherStatus(string $method, string $status): v class { }; - $streamableStore = $this->prophesize(Store::class); + $streamableStore = $this->createMock(Store::class); $subscription = new Subscription( $subscriptionId, @@ -3734,14 +4074,14 @@ class { $subscriptionStore = new DummySubscriptionStore([$subscription]); - $retryStrategy = $this->prophesize(RetryStrategy::class); - $retryStrategy->shouldRetry($subscription)->shouldNotBeCalled(); + $retryStrategy = $this->createMock(RetryStrategy::class); + $retryStrategy->expects($this->never())->method('shouldRetry')->with($subscription); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), - $retryStrategy->reveal(), + $retryStrategy, new NullLogger(), ); @@ -3772,7 +4112,7 @@ public function testShouldNotRetry(): void class { }; - $streamableStore = $this->prophesize(Store::class); + $streamableStore = $this->createMock(Store::class); $subscription = new Subscription( $subscriptionId, @@ -3785,14 +4125,14 @@ class { $subscriptionStore = new DummySubscriptionStore([$subscription]); - $retryStrategy = $this->prophesize(RetryStrategy::class); - $retryStrategy->shouldRetry($subscription)->willReturn(false); + $retryStrategy = $this->createMock(RetryStrategy::class); + $retryStrategy->method('shouldRetry')->with($subscription)->willReturn(false); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), - $retryStrategy->reveal(), + $retryStrategy, new NullLogger(), ); @@ -3804,109 +4144,41 @@ class { $subscriptionStore->assertNoChanges(); } - #[DataProvider('methodProvider')] - public function testCriteria(string $method): void - { - $subscriber = new #[Subscriber('id1', RunMode::FromBeginning)] - class { - }; - - $subscriptionStore = $this->prophesize(SubscriptionStore::class); - $subscriptionStore->find( - Argument::that( - static fn (SubscriptionCriteria $criteria, - ) => $criteria->ids === ['id1'] && $criteria->groups === ['group1'], - ), - )->willReturn([])->shouldBeCalled(); - - $subscriptionStore->find( - new SubscriptionCriteria(), - )->willReturn([ - new Subscription('id1'), - ])->shouldBeCalled(); - - $streamableStore = $this->prophesize(Store::class); - $streamableStore->load($this->criteria())->willReturn(new ArrayStream([])); - - $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), - $subscriptionStore->reveal(), - new MetadataSubscriberAccessorRepository([$subscriber]), - logger: new NullLogger(), - ); - - $engineCriteria = new SubscriptionEngineCriteria( - ids: ['id1'], - groups: ['group1'], - ); - - $engine->{$method}($engineCriteria); - } - - #[DataProvider('methodProvider')] - public function testWithLockableStore(string $method): void - { - $subscriber = new #[Subscriber('id1', RunMode::FromNow)] - class { - }; - - $subscriptionStore = $this->prophesize(LockableSubscriptionStore::class); - $subscriptionStore->inLock(Argument::type(Closure::class))->will( - /** @param array{Closure} $args */ - static fn (array $args): mixed => $args[0](), - )->shouldBeCalled(); - $subscriptionStore->find(Argument::any())->willReturn([])->shouldBeCalled(); - - $subscriptionStore->find( - new SubscriptionCriteria(), - )->willReturn([ - new Subscription('id1'), - ])->shouldBeCalled(); - - $subscriptionStore->remove(Argument::type(Subscription::class)); - $subscriptionStore->add(Argument::type(Subscription::class)); - - $streamableStore = $this->prophesize(Store::class); - $streamableStore->load($this->criteria())->willReturn(new ArrayStream([])); - - $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), - $subscriptionStore->reveal(), - new MetadataSubscriberAccessorRepository([$subscriber]), - logger: new NullLogger(), - ); - - $engine->{$method}(); - } - public function testDontLockGetSubscriptions(): void { $subscriber = new #[Subscriber('id1', RunMode::FromNow)] class { }; - $subscriptionStore = $this->prophesize(LockableSubscriptionStore::class); - $subscriptionStore->inLock(Argument::type(Closure::class))->will( - /** @param array{Closure} $args */ - static fn (array $args): mixed => $args[0](), - )->shouldNotBeCalled(); - $subscriptionStore->find(Argument::any())->willReturn([])->shouldBeCalled(); + $subscriptionStore = $this->createMock(LockableSubscriptionStore::class); + $subscriptionStore + ->expects($this->never()) + ->method('inLock'); - $subscriptionStore->find( - new SubscriptionCriteria(), - )->willReturn([ - new Subscription('id1'), - ])->shouldBeCalled(); + $subscriptionStore + ->expects($this->exactly(2)) + ->method('find') + ->with(new SubscriptionCriteria()) + ->willReturn([new Subscription('id1')]); - $subscriptionStore->remove(Argument::type(Subscription::class)); - $subscriptionStore->add(Argument::type(Subscription::class)); + $subscriptionStore + ->expects($this->never()) + ->method('remove') + ->with($this->isInstanceOf(Subscription::class)); - $streamableStore = $this->prophesize(Store::class); - $streamableStore->load($this->criteria())->willReturn(new ArrayStream([])); + $subscriptionStore + ->expects($this->never()) + ->method('add') + ->with($this->isInstanceOf(Subscription::class)); + + $streamableStore = $this->createMock(Store::class); + $streamableStore + ->expects($this->never()) + ->method('load'); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), - $subscriptionStore->reveal(), + $streamableStore, + $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), logger: new NullLogger(), ); @@ -3923,13 +4195,13 @@ class { $message1 = new Message(new ProfileVisited(ProfileId::fromString('test'))); - $streamableStore = $this->prophesize(Store::class); - $streamableStore->load(null, 1, null, true)->willReturn(new ArrayStream([$message1]))->shouldBeCalledOnce(); + $streamableStore = $this->createMock(Store::class); + $streamableStore->expects($this->once())->method('load')->with(null, 1, null, true)->willReturn(new ArrayStream([$message1])); $subscriptionStore = new DummySubscriptionStore(); $engine = new DefaultSubscriptionEngine( - $streamableStore->reveal(), + $streamableStore, $subscriptionStore, new MetadataSubscriberAccessorRepository([$subscriber]), logger: new NullLogger(), @@ -3950,16 +4222,6 @@ class { ); } - public static function methodProvider(): Generator - { - yield 'setup' => ['setup']; - yield 'boot' => ['boot']; - yield 'run' => ['run']; - yield 'teardown' => ['teardown']; - yield 'remove' => ['remove']; - yield 'reactivate' => ['reactivate']; - } - private function criteria(int $fromIndex = 0): Criteria { return new Criteria(new FromIndexCriterion($fromIndex)); diff --git a/tests/Unit/Subscription/Engine/GapResolverStoreMessageLoaderTest.php b/tests/Unit/Subscription/Engine/GapResolverStoreMessageLoaderTest.php index 91f07004..04f4f98b 100644 --- a/tests/Unit/Subscription/Engine/GapResolverStoreMessageLoaderTest.php +++ b/tests/Unit/Subscription/Engine/GapResolverStoreMessageLoaderTest.php @@ -15,42 +15,44 @@ use Patchlevel\EventSourcing\Store\Stream; use Patchlevel\EventSourcing\Subscription\Engine\GapResolverStoreMessageLoader; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; use Psr\Clock\ClockInterface; +use RuntimeException; use stdClass; final class GapResolverStoreMessageLoaderTest extends TestCase { - use ProphecyTrait; - public function testEmpty(): void { - $store = $this->prophesize(Store::class); - $store->load(new Criteria(new FromIndexCriterion(0)))->willReturn(new ArrayStream([])); + $store = $this->createMock(Store::class); + $store->method('load')->with(new Criteria(new FromIndexCriterion(0)))->willReturn(new ArrayStream([])); - $loader = new GapResolverStoreMessageLoader($store->reveal()); + $loader = new GapResolverStoreMessageLoader($store); $stream = $loader->load(0, []); - self::assertStream([], $stream); + $this->assertEqualsStream([], $stream); } public function testNoGap(): void { - $store = $this->prophesize(Store::class); + $store = $this->createMock(Store::class); - $store->load(new Criteria(new FromIndexCriterion(0)))->willReturn(new ArrayStream([ - 1 => new Message(new stdClass()), - 2 => new Message(new stdClass()), - 3 => new Message(new stdClass()), - 4 => new Message(new stdClass()), - ]))->shouldBeCalledOnce(); + $store + ->expects($this->once()) + ->method('load') + ->with(new Criteria(new FromIndexCriterion(0))) + ->willReturn(new ArrayStream([ + 1 => new Message(new stdClass()), + 2 => new Message(new stdClass()), + 3 => new Message(new stdClass()), + 4 => new Message(new stdClass()), + ])); - $loader = new GapResolverStoreMessageLoader($store->reveal()); + $loader = new GapResolverStoreMessageLoader($store); $stream = $loader->load(0, []); - self::assertStream([ + $this->assertEqualsStream([ 1 => new Message(new stdClass()), 2 => new Message(new stdClass()), 3 => new Message(new stdClass()), @@ -60,47 +62,61 @@ public function testNoGap(): void public function testNoGapFromHigherIndex(): void { - $store = $this->prophesize(Store::class); - - $store->load(new Criteria(new FromIndexCriterion(5)))->willReturn(new ArrayStream([ - 6 => new Message(new stdClass()), - 7 => new Message(new stdClass()), - 8 => new Message(new stdClass()), - 9 => new Message(new stdClass()), - ]))->shouldBeCalledOnce(); + $store = $this->createMock(Store::class); + + $store + ->expects($this->once()) + ->method('load') + ->with(new Criteria(new FromIndexCriterion(5))) + ->willReturn(new ArrayStream([ + 6 => new Message(new stdClass()), + 7 => new Message(new stdClass()), + 8 => new Message(new stdClass()), + 9 => new Message(new stdClass()), + ])); - $loader = new GapResolverStoreMessageLoader($store->reveal()); + $loader = new GapResolverStoreMessageLoader($store); $stream = $loader->load(5, []); - self::assertStream([ - 6 => new Message(new stdClass()), - 7 => new Message(new stdClass()), - 8 => new Message(new stdClass()), - 9 => new Message(new stdClass()), - ], $stream); + $this->assertEqualsStream( + [ + 6 => new Message(new stdClass()), + 7 => new Message(new stdClass()), + 8 => new Message(new stdClass()), + 9 => new Message(new stdClass()), + ], + $stream, + ); } public function testWithGapAndFill(): void { - $store = $this->prophesize(Store::class); - - $store->load(new Criteria(new FromIndexCriterion(5)))->willReturn(new ArrayStream([ - 6 => new Message(new stdClass()), - 7 => new Message(new stdClass()), - 9 => new Message(new stdClass()), - ]))->shouldBeCalledOnce(); - - $store->load(new Criteria(new FromIndexCriterion(7)))->willReturn(new ArrayStream([ - 8 => new Message(new stdClass()), - 9 => new Message(new stdClass()), - ]))->shouldBeCalledOnce(); - - $loader = new GapResolverStoreMessageLoader($store->reveal()); + $store = $this->createMock(Store::class); + + $store + ->expects($this->exactly(2)) + ->method('load') + ->willReturnCallback( + static fn (Criteria $criteria) => match ($criteria->get(FromIndexCriterion::class)->fromIndex) { + 5 => new ArrayStream([ + 6 => new Message(new stdClass()), + 7 => new Message(new stdClass()), + 9 => new Message(new stdClass()), + ]), + 7 => new ArrayStream([ + 8 => new Message(new stdClass()), + 9 => new Message(new stdClass()), + ]), + default => new RuntimeException('Unmatched case!') + }, + ); + + $loader = new GapResolverStoreMessageLoader($store); $stream = $loader->load(5, []); - self::assertStream([ + $this->assertEqualsStream([ 6 => new Message(new stdClass()), 7 => new Message(new stdClass()), 8 => new Message(new stdClass()), @@ -110,34 +126,28 @@ public function testWithGapAndFill(): void public function testWithGapWithoutFill(): void { - $store = $this->prophesize(Store::class); - - $store->load(new Criteria(new FromIndexCriterion(5)))->willReturn(new ArrayStream([ - 6 => new Message(new stdClass()), - 7 => new Message(new stdClass()), - 9 => new Message(new stdClass()), - ]))->shouldBeCalledOnce(); - - $store->load(new Criteria(new FromIndexCriterion(7)))->willReturn( - new ArrayStream([ - 9 => new Message(new stdClass()), - ]), - new ArrayStream([ - 9 => new Message(new stdClass()), - ]), - new ArrayStream([ - 9 => new Message(new stdClass()), - ]), - new ArrayStream([ - 9 => new Message(new stdClass()), - ]), - )->shouldBeCalledTimes(4); - - $loader = new GapResolverStoreMessageLoader($store->reveal()); + $store = $this->createMock(Store::class); + + $store + ->expects($this->exactly(5)) + ->method('load') + ->willReturnCallback(static fn (Criteria $criteria) => match ($criteria->get(FromIndexCriterion::class)->fromIndex) { + 5 => new ArrayStream([ + 6 => new Message(new stdClass()), + 7 => new Message(new stdClass()), + 9 => new Message(new stdClass()), + ]), + 7 => new ArrayStream([ + 9 => new Message(new stdClass()), + ]), + default => new RuntimeException('Unmatched case!') + }); + + $loader = new GapResolverStoreMessageLoader($store); $stream = $loader->load(5, []); - self::assertStream([ + $this->assertEqualsStream([ 6 => new Message(new stdClass()), 7 => new Message(new stdClass()), 9 => new Message(new stdClass()), @@ -146,37 +156,32 @@ public function testWithGapWithoutFill(): void public function testGapAndInDetectionWindowForAggregateHeader(): void { - $store = $this->prophesize(Store::class); - $clock = $this->prophesize(ClockInterface::class); - - $clock->now()->willReturn(new DateTimeImmutable('2023-10-01 00:00:03'))->shouldBeCalledTimes(5); - - $store->load(new Criteria(new FromIndexCriterion(5)))->willReturn(new ArrayStream([ - 6 => $this->createMessageWithAggregateHeader(new DateTimeImmutable('2023-10-01 00:00:00')), - 7 => $this->createMessageWithAggregateHeader(new DateTimeImmutable('2023-10-01 00:00:01')), - 9 => $this->createMessageWithAggregateHeader(new DateTimeImmutable('2023-10-01 00:00:02')), - ]))->shouldBeCalledOnce(); - - $store->load(new Criteria(new FromIndexCriterion(7)))->willReturn( - new ArrayStream([ - 9 => $this->createMessageWithAggregateHeader(new DateTimeImmutable('2023-10-01 00:00:02')), - ]), - new ArrayStream([ - 9 => $this->createMessageWithAggregateHeader(new DateTimeImmutable('2023-10-01 00:00:02')), - ]), - new ArrayStream([ - 9 => $this->createMessageWithAggregateHeader(new DateTimeImmutable('2023-10-01 00:00:02')), - ]), - new ArrayStream([ - 9 => $this->createMessageWithAggregateHeader(new DateTimeImmutable('2023-10-01 00:00:02')), - ]), - )->shouldBeCalledTimes(4); - - $loader = new GapResolverStoreMessageLoader($store->reveal(), $clock->reveal()); - + $clock = $this->createMock(ClockInterface::class); + $clock + ->expects($this->exactly(5)) + ->method('now') + ->willReturn(new DateTimeImmutable('2023-10-01 00:00:03')); + + $store = $this->createMock(Store::class); + $store + ->expects($this->exactly(5)) + ->method('load') + ->willReturnCallback(fn (Criteria $criteria) => match ($criteria->get(FromIndexCriterion::class)->fromIndex) { + 5 => new ArrayStream([ + 6 => $this->createMessageWithAggregateHeader(new DateTimeImmutable('2023-10-01 00:00:00')), + 7 => $this->createMessageWithAggregateHeader(new DateTimeImmutable('2023-10-01 00:00:01')), + 9 => $this->createMessageWithAggregateHeader(new DateTimeImmutable('2023-10-01 00:00:02')), + ]), + 7 => new ArrayStream([ + 9 => $this->createMessageWithAggregateHeader(new DateTimeImmutable('2023-10-01 00:00:02')), + ]), + default => new RuntimeException('Unmatched case!') + }); + + $loader = new GapResolverStoreMessageLoader($store, $clock); $stream = $loader->load(5, []); - self::assertStream([ + $this->assertEqualsStream([ 6 => $this->createMessageWithAggregateHeader(new DateTimeImmutable('2023-10-01 00:00:00')), 7 => $this->createMessageWithAggregateHeader(new DateTimeImmutable('2023-10-01 00:00:01')), 9 => $this->createMessageWithAggregateHeader(new DateTimeImmutable('2023-10-01 00:00:02')), @@ -185,22 +190,22 @@ public function testGapAndInDetectionWindowForAggregateHeader(): void public function testGapAndNotInDetectionWindowForAggregateHeader(): void { - $store = $this->prophesize(Store::class); - $clock = $this->prophesize(ClockInterface::class); + $store = $this->createMock(Store::class); + $clock = $this->createMock(ClockInterface::class); - $clock->now()->willReturn(new DateTimeImmutable('2023-12-01 00:00:00'))->shouldBeCalledTimes(1); + $clock->expects($this->exactly(1))->method('now')->willReturn(new DateTimeImmutable('2023-12-01 00:00:00')); - $store->load(new Criteria(new FromIndexCriterion(5)))->willReturn(new ArrayStream([ + $store->expects($this->once())->method('load')->with(new Criteria(new FromIndexCriterion(5)))->willReturn(new ArrayStream([ 6 => $this->createMessageWithAggregateHeader(new DateTimeImmutable('2023-10-01 00:00:00')), 7 => $this->createMessageWithAggregateHeader(new DateTimeImmutable('2023-10-01 00:00:01')), 9 => $this->createMessageWithAggregateHeader(new DateTimeImmutable('2023-10-01 00:00:02')), - ]))->shouldBeCalledOnce(); + ])); - $loader = new GapResolverStoreMessageLoader($store->reveal(), $clock->reveal()); + $loader = new GapResolverStoreMessageLoader($store, $clock); $stream = $loader->load(5, []); - self::assertStream([ + $this->assertEqualsStream([ 6 => $this->createMessageWithAggregateHeader(new DateTimeImmutable('2023-10-01 00:00:00')), 7 => $this->createMessageWithAggregateHeader(new DateTimeImmutable('2023-10-01 00:00:01')), 9 => $this->createMessageWithAggregateHeader(new DateTimeImmutable('2023-10-01 00:00:02')), @@ -209,61 +214,59 @@ public function testGapAndNotInDetectionWindowForAggregateHeader(): void public function testGapAndInDetectionWindowForRecordedOnHeader(): void { - $store = $this->prophesize(Store::class); - $clock = $this->prophesize(ClockInterface::class); - - $clock->now()->willReturn(new DateTimeImmutable('2023-10-01 00:00:03'))->shouldBeCalledTimes(5); - - $store->load(new Criteria(new FromIndexCriterion(5)))->willReturn(new ArrayStream([ - 6 => $this->createMessageWithRecordedOn(new DateTimeImmutable('2023-10-01 00:00:00')), - 7 => $this->createMessageWithRecordedOn(new DateTimeImmutable('2023-10-01 00:00:01')), - 9 => $this->createMessageWithRecordedOn(new DateTimeImmutable('2023-10-01 00:00:02')), - ]))->shouldBeCalledOnce(); - - $store->load(new Criteria(new FromIndexCriterion(7)))->willReturn( - new ArrayStream([ - 9 => $this->createMessageWithRecordedOn(new DateTimeImmutable('2023-10-01 00:00:02')), - ]), - new ArrayStream([ - 9 => $this->createMessageWithRecordedOn(new DateTimeImmutable('2023-10-01 00:00:02')), - ]), - new ArrayStream([ - 9 => $this->createMessageWithRecordedOn(new DateTimeImmutable('2023-10-01 00:00:02')), - ]), - new ArrayStream([ - 9 => $this->createMessageWithRecordedOn(new DateTimeImmutable('2023-10-01 00:00:02')), - ]), - )->shouldBeCalledTimes(4); - - $loader = new GapResolverStoreMessageLoader($store->reveal(), $clock->reveal()); - + $clock = $this->createMock(ClockInterface::class); + $clock + ->expects($this->exactly(5)) + ->method('now') + ->willReturn(new DateTimeImmutable('2023-10-01 00:00:03')); + + $store = $this->createMock(Store::class); + $store + ->expects($this->exactly(5)) + ->method('load') + ->willReturnCallback(fn (Criteria $criteria) => match ($criteria->get(FromIndexCriterion::class)->fromIndex) { + 5 => new ArrayStream([ + 6 => $this->createMessageWithRecordedOn(new DateTimeImmutable('2023-10-01 00:00:00')), + 7 => $this->createMessageWithRecordedOn(new DateTimeImmutable('2023-10-01 00:00:01')), + 9 => $this->createMessageWithRecordedOn(new DateTimeImmutable('2023-10-01 00:00:02')), + ]), + 7 => new ArrayStream([ + 9 => $this->createMessageWithRecordedOn(new DateTimeImmutable('2023-10-01 00:00:02')), + ]), + default => new RuntimeException('Unmatched case!') + }); + + $loader = new GapResolverStoreMessageLoader($store, $clock); $stream = $loader->load(5, []); - self::assertStream([ - 6 => $this->createMessageWithRecordedOn(new DateTimeImmutable('2023-10-01 00:00:00')), - 7 => $this->createMessageWithRecordedOn(new DateTimeImmutable('2023-10-01 00:00:01')), - 9 => $this->createMessageWithRecordedOn(new DateTimeImmutable('2023-10-01 00:00:02')), - ], $stream); + $this->assertEqualsStream( + [ + 6 => $this->createMessageWithRecordedOn(new DateTimeImmutable('2023-10-01 00:00:00')), + 7 => $this->createMessageWithRecordedOn(new DateTimeImmutable('2023-10-01 00:00:01')), + 9 => $this->createMessageWithRecordedOn(new DateTimeImmutable('2023-10-01 00:00:02')), + ], + $stream, + ); } public function testGapAndNotInDetectionWindowForRecordedOnHeader(): void { - $store = $this->prophesize(Store::class); - $clock = $this->prophesize(ClockInterface::class); + $store = $this->createMock(Store::class); + $clock = $this->createMock(ClockInterface::class); - $clock->now()->willReturn(new DateTimeImmutable('2023-12-01 00:00:00'))->shouldBeCalledTimes(1); + $clock->expects($this->exactly(1))->method('now')->willReturn(new DateTimeImmutable('2023-12-01 00:00:00')); - $store->load(new Criteria(new FromIndexCriterion(5)))->willReturn(new ArrayStream([ + $store->expects($this->once())->method('load')->with(new Criteria(new FromIndexCriterion(5)))->willReturn(new ArrayStream([ 6 => $this->createMessageWithRecordedOn(new DateTimeImmutable('2023-10-01 00:00:00')), 7 => $this->createMessageWithRecordedOn(new DateTimeImmutable('2023-10-01 00:00:01')), 9 => $this->createMessageWithRecordedOn(new DateTimeImmutable('2023-10-01 00:00:02')), - ]))->shouldBeCalledOnce(); + ])); - $loader = new GapResolverStoreMessageLoader($store->reveal(), $clock->reveal()); + $loader = new GapResolverStoreMessageLoader($store, $clock); $stream = $loader->load(5, []); - self::assertStream([ + $this->assertEqualsStream([ 6 => $this->createMessageWithRecordedOn(new DateTimeImmutable('2023-10-01 00:00:00')), 7 => $this->createMessageWithRecordedOn(new DateTimeImmutable('2023-10-01 00:00:01')), 9 => $this->createMessageWithRecordedOn(new DateTimeImmutable('2023-10-01 00:00:02')), @@ -283,7 +286,7 @@ private function createMessageWithRecordedOn(DateTimeImmutable $dateTime): Messa } /** @param array $expected */ - private function assertStream(array $expected, Stream $actual): void + private function assertEqualsStream(array $expected, Stream $actual): void { $result = []; diff --git a/tests/Unit/Subscription/Engine/SubscriptionManagerTest.php b/tests/Unit/Subscription/Engine/SubscriptionManagerTest.php index f9065808..08b9e7ce 100644 --- a/tests/Unit/Subscription/Engine/SubscriptionManagerTest.php +++ b/tests/Unit/Subscription/Engine/SubscriptionManagerTest.php @@ -4,6 +4,7 @@ namespace Patchlevel\EventSourcing\Tests\Unit\Subscription\Engine; +use Closure; use Patchlevel\EventSourcing\Subscription\Engine\SubscriptionManager; use Patchlevel\EventSourcing\Subscription\Store\LockableSubscriptionStore; use Patchlevel\EventSourcing\Subscription\Store\SubscriptionCriteria; @@ -11,24 +12,20 @@ use Patchlevel\EventSourcing\Subscription\Subscription; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Prophecy\Argument; -use Prophecy\PhpUnit\ProphecyTrait; use function iterator_to_array; #[CoversClass(SubscriptionManager::class)] final class SubscriptionManagerTest extends TestCase { - use ProphecyTrait; - public function testAdd(): void { $subscription = new Subscription('foo'); - $store = $this->prophesize(SubscriptionStore::class); - $store->add($subscription)->shouldBeCalledOnce(); + $store = $this->createMock(SubscriptionStore::class); + $store->expects($this->once())->method('add')->with($subscription); - $manager = new SubscriptionManager($store->reveal()); + $manager = new SubscriptionManager($store); $manager->add($subscription); $manager->flush(); } @@ -37,10 +34,10 @@ public function testUpdate(): void { $subscription = new Subscription('foo'); - $store = $this->prophesize(SubscriptionStore::class); - $store->update($subscription)->shouldBeCalledOnce(); + $store = $this->createMock(SubscriptionStore::class); + $store->expects($this->once())->method('update')->with($subscription); - $manager = new SubscriptionManager($store->reveal()); + $manager = new SubscriptionManager($store); $manager->update($subscription); $manager->flush(); } @@ -49,10 +46,10 @@ public function testRemove(): void { $subscription = new Subscription('foo'); - $store = $this->prophesize(SubscriptionStore::class); - $store->remove($subscription)->shouldBeCalledOnce(); + $store = $this->createMock(SubscriptionStore::class); + $store->expects($this->once())->method('remove')->with($subscription); - $manager = new SubscriptionManager($store->reveal()); + $manager = new SubscriptionManager($store); $manager->remove($subscription); $manager->flush(); } @@ -61,11 +58,11 @@ public function testDontUpdateIfNewAdded(): void { $subscription = new Subscription('foo'); - $store = $this->prophesize(SubscriptionStore::class); - $store->add($subscription)->shouldBeCalledOnce(); - $store->update($subscription)->shouldNotBeCalled(); + $store = $this->createMock(SubscriptionStore::class); + $store->expects($this->once())->method('add')->with($subscription); + $store->expects($this->never())->method('update')->with($subscription); - $manager = new SubscriptionManager($store->reveal()); + $manager = new SubscriptionManager($store); $manager->add($subscription); $manager->update($subscription); $manager->flush(); @@ -75,11 +72,11 @@ public function testDontUpdateIfRemoved(): void { $subscription = new Subscription('foo'); - $store = $this->prophesize(SubscriptionStore::class); - $store->remove($subscription)->shouldBeCalledOnce(); - $store->update($subscription)->shouldNotBeCalled(); + $store = $this->createMock(SubscriptionStore::class); + $store->expects($this->once())->method('remove')->with($subscription); + $store->expects($this->never())->method('update')->with($subscription); - $manager = new SubscriptionManager($store->reveal()); + $manager = new SubscriptionManager($store); $manager->update($subscription); $manager->remove($subscription); $manager->flush(); @@ -89,11 +86,11 @@ public function testDoNothingIfAddAndRemoved(): void { $subscription = new Subscription('foo'); - $store = $this->prophesize(SubscriptionStore::class); - $store->remove($subscription)->shouldNotBeCalled(); - $store->add($subscription)->shouldNotBeCalled(); + $store = $this->createMock(SubscriptionStore::class); + $store->expects($this->never())->method('remove')->with($subscription); + $store->expects($this->never())->method('add')->with($subscription); - $manager = new SubscriptionManager($store->reveal()); + $manager = new SubscriptionManager($store); $manager->add($subscription); $manager->remove($subscription); $manager->flush(); @@ -104,10 +101,10 @@ public function testFind(): void $subscription = new Subscription('foo'); $criteria = new SubscriptionCriteria(); - $store = $this->prophesize(SubscriptionStore::class); - $store->find($criteria)->shouldBeCalledOnce()->willReturn([$subscription]); + $store = $this->createMock(SubscriptionStore::class); + $store->expects($this->once())->method('find')->with($criteria)->willReturn([$subscription]); - $manager = new SubscriptionManager($store->reveal()); + $manager = new SubscriptionManager($store); $result = $manager->find($criteria); self::assertSame([$subscription], $result); @@ -118,11 +115,11 @@ public function testFindForUpdateWithoutLock(): void $subscription = new Subscription('foo'); $criteria = new SubscriptionCriteria(); - $store = $this->prophesize(SubscriptionStore::class); - $store->update($subscription)->shouldBeCalledOnce(); - $store->find($criteria)->shouldBeCalledOnce()->willReturn([$subscription]); + $store = $this->createMock(SubscriptionStore::class); + $store->expects($this->once())->method('update')->with($subscription); + $store->expects($this->once())->method('find')->with($criteria)->willReturn([$subscription]); - $manager = new SubscriptionManager($store->reveal()); + $manager = new SubscriptionManager($store); $result = $manager->findForUpdate($criteria, static function ($subscriptions) use ($manager) { $manager->update(...$subscriptions); @@ -137,17 +134,17 @@ public function testFindForUpdateWithLock(): void $subscription = new Subscription('foo'); $criteria = new SubscriptionCriteria(); - $store = $this->prophesize(SubscriptionStore::class); - $store->willImplement(LockableSubscriptionStore::class); - $store->update($subscription)->shouldBeCalledOnce(); - $store->find($criteria)->shouldBeCalledOnce()->willReturn([$subscription]); + $store = $this->createMock(LockableSubscriptionStore::class); + $store->expects($this->once())->method('update')->with($subscription); + $store->expects($this->once())->method('find')->with($criteria)->willReturn([$subscription]); - $store->inLock(Argument::any())->will( - /** @param array{0: callable} $args */ - static fn (array $args): mixed => $args[0](), - )->shouldBeCalledOnce(); + $store + ->expects($this->once()) + ->method('inLock') + ->with($this->isInstanceOf(Closure::class)) + ->willReturnCallback(static fn (Closure $closure): mixed => $closure()); - $manager = new SubscriptionManager($store->reveal()); + $manager = new SubscriptionManager($store); $result = $manager->findForUpdate($criteria, static function ($subscriptions) use ($manager) { $manager->update(...$subscriptions); diff --git a/tests/Unit/Subscription/Engine/ThrowOnErrorSubscriptionEngineTest.php b/tests/Unit/Subscription/Engine/ThrowOnErrorSubscriptionEngineTest.php index de62a042..e3941270 100644 --- a/tests/Unit/Subscription/Engine/ThrowOnErrorSubscriptionEngineTest.php +++ b/tests/Unit/Subscription/Engine/ThrowOnErrorSubscriptionEngineTest.php @@ -13,24 +13,21 @@ use Patchlevel\EventSourcing\Subscription\Engine\ThrowOnErrorSubscriptionEngine; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; use RuntimeException; #[CoversClass(ThrowOnErrorSubscriptionEngine::class)] final class ThrowOnErrorSubscriptionEngineTest extends TestCase { - use ProphecyTrait; - public function testSetupSuccess(): void { - $parent = $this->prophesize(SubscriptionEngine::class); + $parent = $this->createMock(SubscriptionEngine::class); - $engine = new ThrowOnErrorSubscriptionEngine($parent->reveal()); + $engine = new ThrowOnErrorSubscriptionEngine($parent); $criteria = new SubscriptionEngineCriteria(); $expectedResult = new Result(); - $parent->setup($criteria, true)->willReturn($expectedResult)->shouldBeCalledOnce(); + $parent->expects($this->once())->method('setup')->with($criteria, true)->willReturn($expectedResult); $result = $engine->setup($criteria, true); self::assertSame($expectedResult, $result); @@ -40,9 +37,9 @@ public function testSetupError(): void { $this->expectException(ErrorDetected::class); - $parent = $this->prophesize(SubscriptionEngine::class); + $parent = $this->createMock(SubscriptionEngine::class); - $engine = new ThrowOnErrorSubscriptionEngine($parent->reveal()); + $engine = new ThrowOnErrorSubscriptionEngine($parent); $criteria = new SubscriptionEngineCriteria(); $expectedResult = new Result([ @@ -50,20 +47,20 @@ public function testSetupError(): void new Error('id2', 'error2', new RuntimeException('error2')), ]); - $parent->setup($criteria, false)->willReturn($expectedResult)->shouldBeCalledOnce(); + $parent->expects($this->once())->method('setup')->with($criteria, false)->willReturn($expectedResult); $engine->setup($criteria); } public function testBootSuccess(): void { - $parent = $this->prophesize(SubscriptionEngine::class); + $parent = $this->createMock(SubscriptionEngine::class); - $engine = new ThrowOnErrorSubscriptionEngine($parent->reveal()); + $engine = new ThrowOnErrorSubscriptionEngine($parent); $criteria = new SubscriptionEngineCriteria(); $expectedResult = new ProcessedResult(5); - $parent->boot($criteria, 10)->willReturn($expectedResult)->shouldBeCalledOnce(); + $parent->expects($this->once())->method('boot')->with($criteria, 10)->willReturn($expectedResult); $result = $engine->boot($criteria, 10); self::assertSame($expectedResult, $result); @@ -73,9 +70,9 @@ public function testBootError(): void { $this->expectException(ErrorDetected::class); - $parent = $this->prophesize(SubscriptionEngine::class); + $parent = $this->createMock(SubscriptionEngine::class); - $engine = new ThrowOnErrorSubscriptionEngine($parent->reveal()); + $engine = new ThrowOnErrorSubscriptionEngine($parent); $criteria = new SubscriptionEngineCriteria(); $expectedResult = new ProcessedResult(5, false, [ @@ -83,20 +80,20 @@ public function testBootError(): void new Error('id2', 'error2', new RuntimeException('error2')), ]); - $parent->boot($criteria, 10)->willReturn($expectedResult)->shouldBeCalledOnce(); + $parent->expects($this->once())->method('boot')->with($criteria, 10)->willReturn($expectedResult); $engine->boot($criteria, 10); } public function testRunSuccess(): void { - $parent = $this->prophesize(SubscriptionEngine::class); + $parent = $this->createMock(SubscriptionEngine::class); - $engine = new ThrowOnErrorSubscriptionEngine($parent->reveal()); + $engine = new ThrowOnErrorSubscriptionEngine($parent); $criteria = new SubscriptionEngineCriteria(); $expectedResult = new ProcessedResult(5); - $parent->run($criteria, 10)->willReturn($expectedResult)->shouldBeCalledOnce(); + $parent->expects($this->once())->method('run')->with($criteria, 10)->willReturn($expectedResult); $result = $engine->run($criteria, 10); self::assertSame($expectedResult, $result); @@ -106,9 +103,9 @@ public function testRunError(): void { $this->expectException(ErrorDetected::class); - $parent = $this->prophesize(SubscriptionEngine::class); + $parent = $this->createMock(SubscriptionEngine::class); - $engine = new ThrowOnErrorSubscriptionEngine($parent->reveal()); + $engine = new ThrowOnErrorSubscriptionEngine($parent); $criteria = new SubscriptionEngineCriteria(); $expectedResult = new ProcessedResult(5, false, [ @@ -116,20 +113,20 @@ public function testRunError(): void new Error('id2', 'error2', new RuntimeException('error2')), ]); - $parent->run($criteria, 10)->willReturn($expectedResult)->shouldBeCalledOnce(); + $parent->expects($this->once())->method('run')->with($criteria, 10)->willReturn($expectedResult); $engine->run($criteria, 10); } public function testTeardownSuccess(): void { - $parent = $this->prophesize(SubscriptionEngine::class); + $parent = $this->createMock(SubscriptionEngine::class); - $engine = new ThrowOnErrorSubscriptionEngine($parent->reveal()); + $engine = new ThrowOnErrorSubscriptionEngine($parent); $criteria = new SubscriptionEngineCriteria(); $expectedResult = new Result(); - $parent->teardown($criteria)->willReturn($expectedResult)->shouldBeCalledOnce(); + $parent->expects($this->once())->method('teardown')->with($criteria)->willReturn($expectedResult); $result = $engine->teardown($criteria); self::assertSame($expectedResult, $result); @@ -139,9 +136,9 @@ public function testTeardownError(): void { $this->expectException(ErrorDetected::class); - $parent = $this->prophesize(SubscriptionEngine::class); + $parent = $this->createMock(SubscriptionEngine::class); - $engine = new ThrowOnErrorSubscriptionEngine($parent->reveal()); + $engine = new ThrowOnErrorSubscriptionEngine($parent); $criteria = new SubscriptionEngineCriteria(); $expectedResult = new Result([ @@ -149,20 +146,20 @@ public function testTeardownError(): void new Error('id2', 'error2', new RuntimeException('error2')), ]); - $parent->teardown($criteria)->willReturn($expectedResult)->shouldBeCalledOnce(); + $parent->expects($this->once())->method('teardown')->with($criteria)->willReturn($expectedResult); $engine->teardown($criteria); } public function testRemoveSuccess(): void { - $parent = $this->prophesize(SubscriptionEngine::class); + $parent = $this->createMock(SubscriptionEngine::class); - $engine = new ThrowOnErrorSubscriptionEngine($parent->reveal()); + $engine = new ThrowOnErrorSubscriptionEngine($parent); $criteria = new SubscriptionEngineCriteria(); $expectedResult = new Result(); - $parent->remove($criteria)->willReturn($expectedResult)->shouldBeCalledOnce(); + $parent->expects($this->once())->method('remove')->with($criteria)->willReturn($expectedResult); $result = $engine->remove($criteria); self::assertSame($expectedResult, $result); @@ -172,9 +169,9 @@ public function testRemoveError(): void { $this->expectException(ErrorDetected::class); - $parent = $this->prophesize(SubscriptionEngine::class); + $parent = $this->createMock(SubscriptionEngine::class); - $engine = new ThrowOnErrorSubscriptionEngine($parent->reveal()); + $engine = new ThrowOnErrorSubscriptionEngine($parent); $criteria = new SubscriptionEngineCriteria(); $expectedResult = new Result([ @@ -182,20 +179,20 @@ public function testRemoveError(): void new Error('id2', 'error2', new RuntimeException('error2')), ]); - $parent->remove($criteria)->willReturn($expectedResult)->shouldBeCalledOnce(); + $parent->expects($this->once())->method('remove')->with($criteria)->willReturn($expectedResult); $engine->remove($criteria); } public function testReactivateSuccess(): void { - $parent = $this->prophesize(SubscriptionEngine::class); + $parent = $this->createMock(SubscriptionEngine::class); - $engine = new ThrowOnErrorSubscriptionEngine($parent->reveal()); + $engine = new ThrowOnErrorSubscriptionEngine($parent); $criteria = new SubscriptionEngineCriteria(); $expectedResult = new Result(); - $parent->reactivate($criteria)->willReturn($expectedResult)->shouldBeCalledOnce(); + $parent->expects($this->once())->method('reactivate')->with($criteria)->willReturn($expectedResult); $result = $engine->reactivate($criteria); self::assertSame($expectedResult, $result); @@ -205,9 +202,9 @@ public function testReactivateError(): void { $this->expectException(ErrorDetected::class); - $parent = $this->prophesize(SubscriptionEngine::class); + $parent = $this->createMock(SubscriptionEngine::class); - $engine = new ThrowOnErrorSubscriptionEngine($parent->reveal()); + $engine = new ThrowOnErrorSubscriptionEngine($parent); $criteria = new SubscriptionEngineCriteria(); $expectedResult = new Result([ @@ -215,20 +212,20 @@ public function testReactivateError(): void new Error('id2', 'error2', new RuntimeException('error2')), ]); - $parent->reactivate($criteria)->willReturn($expectedResult)->shouldBeCalledOnce(); + $parent->expects($this->once())->method('reactivate')->with($criteria)->willReturn($expectedResult); $engine->reactivate($criteria); } public function testPauseSuccess(): void { - $parent = $this->prophesize(SubscriptionEngine::class); + $parent = $this->createMock(SubscriptionEngine::class); - $engine = new ThrowOnErrorSubscriptionEngine($parent->reveal()); + $engine = new ThrowOnErrorSubscriptionEngine($parent); $criteria = new SubscriptionEngineCriteria(); $expectedResult = new Result(); - $parent->pause($criteria)->willReturn($expectedResult)->shouldBeCalledOnce(); + $parent->expects($this->once())->method('pause')->with($criteria)->willReturn($expectedResult); $result = $engine->pause($criteria); self::assertSame($expectedResult, $result); @@ -238,9 +235,9 @@ public function testPauseError(): void { $this->expectException(ErrorDetected::class); - $parent = $this->prophesize(SubscriptionEngine::class); + $parent = $this->createMock(SubscriptionEngine::class); - $engine = new ThrowOnErrorSubscriptionEngine($parent->reveal()); + $engine = new ThrowOnErrorSubscriptionEngine($parent); $criteria = new SubscriptionEngineCriteria(); $expectedResult = new Result([ @@ -248,18 +245,18 @@ public function testPauseError(): void new Error('id2', 'error2', new RuntimeException('error2')), ]); - $parent->pause($criteria)->willReturn($expectedResult)->shouldBeCalledOnce(); + $parent->expects($this->once())->method('pause')->with($criteria)->willReturn($expectedResult); $engine->pause($criteria); } public function testSubscriptions(): void { - $parent = $this->prophesize(SubscriptionEngine::class); + $parent = $this->createMock(SubscriptionEngine::class); - $engine = new ThrowOnErrorSubscriptionEngine($parent->reveal()); + $engine = new ThrowOnErrorSubscriptionEngine($parent); $criteria = new SubscriptionEngineCriteria(); - $parent->subscriptions($criteria)->willReturn([])->shouldBeCalledOnce(); + $parent->expects($this->once())->method('subscriptions')->with($criteria)->willReturn([]); $result = $engine->subscriptions($criteria); self::assertSame([], $result); diff --git a/tests/Unit/Subscription/Lookup/LookupTest.php b/tests/Unit/Subscription/Lookup/LookupTest.php index 0521cdfc..51d24ef9 100644 --- a/tests/Unit/Subscription/Lookup/LookupTest.php +++ b/tests/Unit/Subscription/Lookup/LookupTest.php @@ -23,16 +23,13 @@ use Patchlevel\EventSourcing\Tests\Unit\Fixture\ProfileCreated; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; #[CoversClass(Lookup::class)] final class LookupTest extends TestCase { - use ProphecyTrait; - public function testMissingIndexHeader(): void { - $store = $this->prophesize(Store::class); + $store = $this->createMock(Store::class); $event = new class () { }; @@ -42,7 +39,7 @@ public function testMissingIndexHeader(): void $this->expectException(HeaderNotFound::class); new Lookup( - $store->reveal(), + $store, $message, ); } @@ -51,12 +48,11 @@ public function testEmpty(): void { $expectedResult = new ArrayStream([]); - $store = $this->prophesize(Store::class); + $store = $this->createMock(Store::class); $expectedCriteria = new Criteria(new ToIndexCriterion(1)); - $store->load($expectedCriteria, null, null, false) - ->willReturn($expectedResult) - ->shouldBeCalledOnce(); + $store->expects($this->once())->method('load')->with($expectedCriteria, null, null, false) + ->willReturn($expectedResult); $event = new class () { }; @@ -65,7 +61,7 @@ public function testEmpty(): void ->withHeader(new IndexHeader(1)); $lookup = new Lookup( - $store->reveal(), + $store, $message, ); @@ -82,10 +78,9 @@ public function testEvents(): void new ToIndexCriterion(1), ); - $store = $this->prophesize(Store::class); - $store->load($expectedCriteria, null, null, false) - ->willReturn($expectedResult) - ->shouldBeCalledOnce(); + $store = $this->createMock(Store::class); + $store->expects($this->once())->method('load')->with($expectedCriteria, null, null, false) + ->willReturn($expectedResult); $event = new class () { }; @@ -94,7 +89,7 @@ public function testEvents(): void ->withHeader(new IndexHeader(1)); $lookup = new Lookup( - $store->reveal(), + $store, $message, ); @@ -111,10 +106,9 @@ public function testEventClasses(): void new ToIndexCriterion(1), ); - $store = $this->prophesize(Store::class); - $store->load($expectedCriteria, null, null, false) - ->willReturn($expectedResult) - ->shouldBeCalledOnce(); + $store = $this->createMock(Store::class); + $store->expects($this->once())->method('load')->with($expectedCriteria, null, null, false) + ->willReturn($expectedResult); $event = new class () { }; @@ -123,7 +117,7 @@ public function testEventClasses(): void ->withHeader(new IndexHeader(1)); $lookup = new Lookup( - $store->reveal(), + $store, $message, new EventRegistry(['profile_created' => ProfileCreated::class]), ); @@ -140,10 +134,9 @@ public function testBackwards(): void new ToIndexCriterion(1), ); - $store = $this->prophesize(Store::class); - $store->load($expectedCriteria, null, null, true) - ->willReturn($expectedResult) - ->shouldBeCalledOnce(); + $store = $this->createMock(Store::class); + $store->expects($this->once())->method('load')->with($expectedCriteria, null, null, true) + ->willReturn($expectedResult); $event = new class () { }; @@ -152,7 +145,7 @@ public function testBackwards(): void ->withHeader(new IndexHeader(1)); $lookup = new Lookup( - $store->reveal(), + $store, $message, ); @@ -169,10 +162,9 @@ public function testStream(): void new ToIndexCriterion(1), ); - $store = $this->prophesize(Store::class); - $store->load($expectedCriteria, null, null, false) - ->willReturn($expectedResult) - ->shouldBeCalledOnce(); + $store = $this->createMock(Store::class); + $store->expects($this->once())->method('load')->with($expectedCriteria, null, null, false) + ->willReturn($expectedResult); $event = new class () { }; @@ -181,7 +173,7 @@ public function testStream(): void ->withHeader(new IndexHeader(1)); $lookup = new Lookup( - $store->reveal(), + $store, $message, ); @@ -198,10 +190,9 @@ public function testAggregateName(): void new ToIndexCriterion(1), ); - $store = $this->prophesize(Store::class); - $store->load($expectedCriteria, null, null, false) - ->willReturn($expectedResult) - ->shouldBeCalledOnce(); + $store = $this->createMock(Store::class); + $store->expects($this->once())->method('load')->with($expectedCriteria, null, null, false) + ->willReturn($expectedResult); $event = new class () { }; @@ -210,7 +201,7 @@ public function testAggregateName(): void ->withHeader(new IndexHeader(1)); $lookup = new Lookup( - $store->reveal(), + $store, $message, ); @@ -227,10 +218,9 @@ public function testAggregateId(): void new ToIndexCriterion(1), ); - $store = $this->prophesize(Store::class); - $store->load($expectedCriteria, null, null, false) - ->willReturn($expectedResult) - ->shouldBeCalledOnce(); + $store = $this->createMock(Store::class); + $store->expects($this->once())->method('load')->with($expectedCriteria, null, null, false) + ->willReturn($expectedResult); $event = new class () { }; @@ -239,7 +229,7 @@ public function testAggregateId(): void ->withHeader(new IndexHeader(1)); $lookup = new Lookup( - $store->reveal(), + $store, $message, ); @@ -256,10 +246,9 @@ public function testCurrentStream(): void new StreamCriterion('foo'), ); - $store = $this->prophesize(Store::class); - $store->load($expectedCriteria, null, null, false) - ->willReturn($expectedResult) - ->shouldBeCalledOnce(); + $store = $this->createMock(Store::class); + $store->expects($this->once())->method('load')->with($expectedCriteria, null, null, false) + ->willReturn($expectedResult); $event = new class () { }; @@ -269,7 +258,7 @@ public function testCurrentStream(): void ->withHeader(new IndexHeader(1)); $lookup = new Lookup( - $store->reveal(), + $store, $message, ); @@ -287,10 +276,9 @@ public function testCurrentAggregate(): void new AggregateIdCriterion('bar'), ); - $store = $this->prophesize(Store::class); - $store->load($expectedCriteria, null, null, false) - ->willReturn($expectedResult) - ->shouldBeCalledOnce(); + $store = $this->createMock(Store::class); + $store->expects($this->once())->method('load')->with($expectedCriteria, null, null, false) + ->willReturn($expectedResult); $event = new class () { }; @@ -305,7 +293,7 @@ public function testCurrentAggregate(): void ->withHeader(new IndexHeader(1)); $lookup = new Lookup( - $store->reveal(), + $store, $message, ); @@ -331,10 +319,9 @@ public function testFetchFirst(): void new ToIndexCriterion(1), ); - $store = $this->prophesize(Store::class); - $store->load($expectedCriteria, 1, null, false) - ->willReturn($expectedResult) - ->shouldBeCalledOnce(); + $store = $this->createMock(Store::class); + $store->expects($this->once())->method('load')->with($expectedCriteria, 1, null, false) + ->willReturn($expectedResult); $event = new class () { }; @@ -343,7 +330,7 @@ public function testFetchFirst(): void ->withHeader(new IndexHeader(1)); $lookup = new Lookup( - $store->reveal(), + $store, $message, ); @@ -369,10 +356,9 @@ public function testFetchLast(): void new ToIndexCriterion(1), ); - $store = $this->prophesize(Store::class); - $store->load($expectedCriteria, 1, null, true) - ->willReturn($expectedResult) - ->shouldBeCalledOnce(); + $store = $this->createMock(Store::class); + $store->expects($this->once())->method('load')->with($expectedCriteria, 1, null, true) + ->willReturn($expectedResult); $event = new class () { }; @@ -381,7 +367,7 @@ public function testFetchLast(): void ->withHeader(new IndexHeader(1)); $lookup = new Lookup( - $store->reveal(), + $store, $message, ); diff --git a/tests/Unit/Subscription/Repository/RunSubscriptionEngineRepositoryManagerTest.php b/tests/Unit/Subscription/Repository/RunSubscriptionEngineRepositoryManagerTest.php index a144abdc..a574ed29 100644 --- a/tests/Unit/Subscription/Repository/RunSubscriptionEngineRepositoryManagerTest.php +++ b/tests/Unit/Subscription/Repository/RunSubscriptionEngineRepositoryManagerTest.php @@ -11,25 +11,22 @@ use Patchlevel\EventSourcing\Tests\Unit\Fixture\Profile; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; #[CoversClass(RunSubscriptionEngineRepositoryManager::class)] final class RunSubscriptionEngineRepositoryManagerTest extends TestCase { - use ProphecyTrait; - public function testGet(): void { - $defaultRepository = $this->prophesize(Repository::class)->reveal(); + $defaultRepository = $this->createMock(Repository::class); - $defaultRepositoryManager = $this->prophesize(RepositoryManager::class); - $defaultRepositoryManager->get(Profile::class)->willReturn($defaultRepository)->shouldBeCalledOnce(); + $defaultRepositoryManager = $this->createMock(RepositoryManager::class); + $defaultRepositoryManager->expects($this->once())->method('get')->with(Profile::class)->willReturn($defaultRepository); - $engine = $this->prophesize(SubscriptionEngine::class); + $engine = $this->createMock(SubscriptionEngine::class); $repository = new RunSubscriptionEngineRepositoryManager( - $defaultRepositoryManager->reveal(), - $engine->reveal(), + $defaultRepositoryManager, + $engine, ['id1', 'id2'], ['group1', 'group2'], 42, diff --git a/tests/Unit/Subscription/Repository/RunSubscriptionEngineRepositoryTest.php b/tests/Unit/Subscription/Repository/RunSubscriptionEngineRepositoryTest.php index 551e8494..77f025c1 100644 --- a/tests/Unit/Subscription/Repository/RunSubscriptionEngineRepositoryTest.php +++ b/tests/Unit/Subscription/Repository/RunSubscriptionEngineRepositoryTest.php @@ -15,13 +15,10 @@ use Patchlevel\EventSourcing\Tests\Unit\Fixture\ProfileId; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; #[CoversClass(RunSubscriptionEngineRepository::class)] final class RunSubscriptionEngineRepositoryTest extends TestCase { - use ProphecyTrait; - public function testLoad(): void { $profileId = ProfileId::fromString('id1'); @@ -31,14 +28,14 @@ public function testLoad(): void Email::fromString('info@patchlevel.de'), ); - $defaultRepository = $this->prophesize(Repository::class); - $defaultRepository->load($profileId)->willReturn($aggregate)->shouldBeCalledOnce(); + $defaultRepository = $this->createMock(Repository::class); + $defaultRepository->expects($this->once())->method('load')->with($profileId)->willReturn($aggregate); - $engine = $this->prophesize(SubscriptionEngine::class); + $engine = $this->createMock(SubscriptionEngine::class); $repository = new RunSubscriptionEngineRepository( - $defaultRepository->reveal(), - $engine->reveal(), + $defaultRepository, + $engine, ['id1', 'id2'], ['group1', 'group2'], 42, @@ -51,14 +48,14 @@ public function testHas(): void { $profileId = ProfileId::fromString('id1'); - $defaultRepository = $this->prophesize(Repository::class); - $defaultRepository->has($profileId)->willReturn(true)->shouldBeCalledOnce(); + $defaultRepository = $this->createMock(Repository::class); + $defaultRepository->expects($this->once())->method('has')->with($profileId)->willReturn(true); - $engine = $this->prophesize(SubscriptionEngine::class); + $engine = $this->createMock(SubscriptionEngine::class); $repository = new RunSubscriptionEngineRepository( - $defaultRepository->reveal(), - $engine->reveal(), + $defaultRepository, + $engine, ['id1', 'id2'], ['group1', 'group2'], 42, @@ -79,15 +76,15 @@ public function testSave(): void Email::fromString('info@patchlevel.de'), ); - $defaultRepository = $this->prophesize(Repository::class); - $defaultRepository->save($aggregate)->shouldBeCalledOnce(); + $defaultRepository = $this->createMock(Repository::class); + $defaultRepository->expects($this->once())->method('save')->with($aggregate); - $engine = $this->prophesize(SubscriptionEngine::class); - $engine->run($criteria, 42)->willReturn(new ProcessedResult(21))->shouldBeCalledOnce(); + $engine = $this->createMock(SubscriptionEngine::class); + $engine->expects($this->once())->method('run')->with($criteria, 42)->willReturn(new ProcessedResult(21)); $repository = new RunSubscriptionEngineRepository( - $defaultRepository->reveal(), - $engine->reveal(), + $defaultRepository, + $engine, ['id1', 'id2'], ['group1', 'group2'], 42, @@ -108,15 +105,15 @@ public function testSaveWithAlreadyProcessing(): void Email::fromString('info@patchlevel.de'), ); - $defaultRepository = $this->prophesize(Repository::class); - $defaultRepository->save($aggregate)->shouldBeCalledOnce(); + $defaultRepository = $this->createMock(Repository::class); + $defaultRepository->expects($this->once())->method('save')->with($aggregate); - $engine = $this->prophesize(SubscriptionEngine::class); - $engine->run($criteria, 42)->willThrow(new AlreadyProcessing())->shouldBeCalledOnce(); + $engine = $this->createMock(SubscriptionEngine::class); + $engine->expects($this->once())->method('run')->with($criteria, 42)->willThrowException(new AlreadyProcessing()); $repository = new RunSubscriptionEngineRepository( - $defaultRepository->reveal(), - $engine->reveal(), + $defaultRepository, + $engine, ['id1', 'id2'], ['group1', 'group2'], 42, diff --git a/tests/Unit/Subscription/Subscriber/ArgumentResolver/LookupResolverTest.php b/tests/Unit/Subscription/Subscriber/ArgumentResolver/LookupResolverTest.php index 7f253da9..6a46c45f 100644 --- a/tests/Unit/Subscription/Subscriber/ArgumentResolver/LookupResolverTest.php +++ b/tests/Unit/Subscription/Subscriber/ArgumentResolver/LookupResolverTest.php @@ -19,19 +19,16 @@ use Patchlevel\EventSourcing\Tests\Unit\Fixture\ProfileVisited; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; #[CoversClass(AggregateIdArgumentResolver::class)] final class LookupResolverTest extends TestCase { - use ProphecyTrait; - public function testSupport(): void { - $store = $this->prophesize(Store::class); + $store = $this->createMock(Store::class); $eventRegistry = new EventRegistry([]); - $resolver = new LookupResolver($store->reveal(), $eventRegistry); + $resolver = new LookupResolver($store, $eventRegistry); self::assertTrue( $resolver->support( @@ -52,10 +49,10 @@ public function testResolve(): void { $event = new ProfileVisited(ProfileId::fromString('1')); - $store = $this->prophesize(Store::class); + $store = $this->createMock(Store::class); $eventRegistry = new EventRegistry([]); - $resolver = new LookupResolver($store->reveal(), $eventRegistry); + $resolver = new LookupResolver($store, $eventRegistry); $message = (new Message($event))->withHeader( new AggregateHeader('foo', 'bar', 1, new DateTimeImmutable()), From 8b5e9d49ad61aed4d0501f3bc6e3520eb5cec8aa Mon Sep 17 00:00:00 2001 From: Daniel Badura Date: Fri, 27 Jun 2025 10:16:08 +0200 Subject: [PATCH 2/2] Remove prophecy as dep and related SA plugins --- composer.json | 2 - composer.lock | 224 ++++------------------------------------------ phpstan.neon.dist | 1 - 3 files changed, 19 insertions(+), 208 deletions(-) diff --git a/composer.json b/composer.json index 3df2df90..bab3d8c0 100644 --- a/composer.json +++ b/composer.json @@ -39,13 +39,11 @@ "ext-pdo_sqlite": "~8.2.0 || ~8.3.0 || ~8.4.0", "doctrine/orm": "^2.18.0 || ^3.0.0", "infection/infection": "^0.29.12", - "jangregor/phpstan-prophecy": "^2.0", "league/commonmark": "^2.6.1", "patchlevel/coding-standard": "^1.3.0", "patchlevel/event-sourcing-psalm-plugin": "^3.1.0", "phpat/phpat": "^0.11.3", "phpbench/phpbench": "^1.4.1", - "phpspec/prophecy-phpunit": "^2.3.0", "phpstan/phpstan": "^2.1.11", "phpstan/phpstan-phpunit": "^2.0", "phpunit/phpunit": "^11.5.15", diff --git a/composer.lock b/composer.lock index 23d4e111..498990cb 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "b0b0f160244dc313aeddaba638afb3be", + "content-hash": "e47b04b41df0b48268165ffc8e103455", "packages": [ { "name": "brick/math", @@ -313,16 +313,16 @@ }, { "name": "doctrine/migrations", - "version": "3.9.0", + "version": "3.9.1", "source": { "type": "git", "url": "https://github.com/doctrine/migrations.git", - "reference": "325b61e41d032f5f7d7e2d11cbefff656eadc9ab" + "reference": "0f1e0c960ac29866d648a4f50142a74fe1cb6999" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/migrations/zipball/325b61e41d032f5f7d7e2d11cbefff656eadc9ab", - "reference": "325b61e41d032f5f7d7e2d11cbefff656eadc9ab", + "url": "https://api.github.com/repos/doctrine/migrations/zipball/0f1e0c960ac29866d648a4f50142a74fe1cb6999", + "reference": "0f1e0c960ac29866d648a4f50142a74fe1cb6999", "shasum": "" }, "require": { @@ -396,7 +396,7 @@ ], "support": { "issues": "https://github.com/doctrine/migrations/issues", - "source": "https://github.com/doctrine/migrations/tree/3.9.0" + "source": "https://github.com/doctrine/migrations/tree/3.9.1" }, "funding": [ { @@ -412,7 +412,7 @@ "type": "tidelift" } ], - "time": "2025-03-26T06:48:45+00:00" + "time": "2025-06-27T07:19:23+00:00" }, { "name": "patchlevel/hydrator", @@ -3914,16 +3914,16 @@ }, { "name": "doctrine/orm", - "version": "3.4.1", + "version": "3.4.2", "source": { "type": "git", "url": "https://github.com/doctrine/orm.git", - "reference": "92e2f6db831be44bc041fdfbc49402a063ec33cc" + "reference": "b4ca0cd5fbf74b33fdd3d54d89746ab9748803b9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/orm/zipball/92e2f6db831be44bc041fdfbc49402a063ec33cc", - "reference": "92e2f6db831be44bc041fdfbc49402a063ec33cc", + "url": "https://api.github.com/repos/doctrine/orm/zipball/b4ca0cd5fbf74b33fdd3d54d89746ab9748803b9", + "reference": "b4ca0cd5fbf74b33fdd3d54d89746ab9748803b9", "shasum": "" }, "require": { @@ -3998,9 +3998,9 @@ ], "support": { "issues": "https://github.com/doctrine/orm/issues", - "source": "https://github.com/doctrine/orm/tree/3.4.1" + "source": "https://github.com/doctrine/orm/tree/3.4.2" }, - "time": "2025-06-21T10:44:26+00:00" + "time": "2025-06-26T18:51:01+00:00" }, { "name": "doctrine/persistence", @@ -4572,67 +4572,6 @@ ], "time": "2025-04-29T08:19:52+00:00" }, - { - "name": "jangregor/phpstan-prophecy", - "version": "2.2.0", - "source": { - "type": "git", - "url": "https://github.com/Jan0707/phpstan-prophecy.git", - "reference": "aebda94b6b1c39055d8f2227e879c07bac651550" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Jan0707/phpstan-prophecy/zipball/aebda94b6b1c39055d8f2227e879c07bac651550", - "reference": "aebda94b6b1c39055d8f2227e879c07bac651550", - "shasum": "" - }, - "require": { - "php": "^7.4 || ^8.0", - "phpstan/phpstan": "^2.1.5" - }, - "conflict": { - "phpspec/prophecy": "<1.17.0 || >=2.0.0", - "phpspec/prophecy-phpunit": "<2.3.0 || >=3.0.0", - "phpunit/phpunit": "<9.1.0 || >=13.0.0" - }, - "require-dev": { - "ergebnis/composer-normalize": "^2.47.0", - "ergebnis/license": "^2.6.0", - "ergebnis/php-cs-fixer-config": "^6.46.0", - "phpspec/prophecy": "^1.7.0", - "phpspec/prophecy-phpunit": "^2.3", - "phpunit/phpunit": "^9.1.0" - }, - "type": "phpstan-extension", - "extra": { - "phpstan": { - "includes": [ - "extension.neon" - ] - } - }, - "autoload": { - "psr-4": { - "JanGregor\\Prophecy\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jan Gregor Emge-Triebel", - "email": "jan@jangregor.me" - } - ], - "description": "Provides a phpstan/phpstan extension for phpspec/prophecy", - "support": { - "issues": "https://github.com/Jan0707/phpstan-prophecy/issues", - "source": "https://github.com/Jan0707/phpstan-prophecy/tree/2.2.0" - }, - "time": "2025-05-22T08:21:52+00:00" - }, { "name": "justinrainbow/json-schema", "version": "6.4.2", @@ -6210,131 +6149,6 @@ }, "time": "2024-11-09T15:12:26+00:00" }, - { - "name": "phpspec/prophecy", - "version": "v1.22.0", - "source": { - "type": "git", - "url": "https://github.com/phpspec/prophecy.git", - "reference": "35f1adb388946d92e6edab2aa2cb2b60e132ebd5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/35f1adb388946d92e6edab2aa2cb2b60e132ebd5", - "reference": "35f1adb388946d92e6edab2aa2cb2b60e132ebd5", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.2 || ^2.0", - "php": "^7.4 || 8.0.* || 8.1.* || 8.2.* || 8.3.* || 8.4.*", - "phpdocumentor/reflection-docblock": "^5.2", - "sebastian/comparator": "^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0", - "sebastian/recursion-context": "^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^3.40", - "phpspec/phpspec": "^6.0 || ^7.0", - "phpstan/phpstan": "^2.1.13", - "phpunit/phpunit": "^8.0 || ^9.0 || ^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Prophecy\\": "src/Prophecy" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" - }, - { - "name": "Marcello Duarte", - "email": "marcello.duarte@gmail.com" - } - ], - "description": "Highly opinionated mocking framework for PHP 5.3+", - "homepage": "https://github.com/phpspec/prophecy", - "keywords": [ - "Double", - "Dummy", - "dev", - "fake", - "mock", - "spy", - "stub" - ], - "support": { - "issues": "https://github.com/phpspec/prophecy/issues", - "source": "https://github.com/phpspec/prophecy/tree/v1.22.0" - }, - "time": "2025-04-29T14:58:06+00:00" - }, - { - "name": "phpspec/prophecy-phpunit", - "version": "v2.4.0", - "source": { - "type": "git", - "url": "https://github.com/phpspec/prophecy-phpunit.git", - "reference": "d3c28041d9390c9bca325a08c5b2993ac855bded" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy-phpunit/zipball/d3c28041d9390c9bca325a08c5b2993ac855bded", - "reference": "d3c28041d9390c9bca325a08c5b2993ac855bded", - "shasum": "" - }, - "require": { - "php": "^7.3 || ^8", - "phpspec/prophecy": "^1.18", - "phpunit/phpunit": "^9.1 || ^10.1 || ^11.0 || ^12.0" - }, - "require-dev": { - "phpstan/phpstan": "^1.10" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.x-dev" - } - }, - "autoload": { - "psr-4": { - "Prophecy\\PhpUnit\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Christophe Coevoet", - "email": "stof@notk.org" - } - ], - "description": "Integrating the Prophecy mocking library in PHPUnit test cases", - "homepage": "http://phpspec.net", - "keywords": [ - "phpunit", - "prophecy" - ], - "support": { - "issues": "https://github.com/phpspec/prophecy-phpunit/issues", - "source": "https://github.com/phpspec/prophecy-phpunit/tree/v2.4.0" - }, - "time": "2025-05-13T13:52:32+00:00" - }, { "name": "phpstan/phpdoc-parser", "version": "2.1.0", @@ -6830,16 +6644,16 @@ }, { "name": "phpunit/phpunit", - "version": "11.5.24", + "version": "11.5.25", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "6b07ab1047155cf38f82dd691787a277782271dd" + "reference": "864ab32b3ff52058f917c5b19b3cef821e4a4f1b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/6b07ab1047155cf38f82dd691787a277782271dd", - "reference": "6b07ab1047155cf38f82dd691787a277782271dd", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/864ab32b3ff52058f917c5b19b3cef821e4a4f1b", + "reference": "864ab32b3ff52058f917c5b19b3cef821e4a4f1b", "shasum": "" }, "require": { @@ -6911,7 +6725,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.24" + "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.25" }, "funding": [ { @@ -6935,7 +6749,7 @@ "type": "tidelift" } ], - "time": "2025-06-20T11:31:02+00:00" + "time": "2025-06-27T04:36:07+00:00" }, { "name": "psr/http-factory", diff --git a/phpstan.neon.dist b/phpstan.neon.dist index bef19381..689e71a1 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -1,7 +1,6 @@ includes: - phpstan-baseline.neon - vendor/phpstan/phpstan-phpunit/extension.neon - - vendor/jangregor/phpstan-prophecy/extension.neon - vendor/phpat/phpat/extension.neon parameters: