diff --git a/baseline.xml b/baseline.xml
index 2e51abc62..3e15b6e95 100644
--- a/baseline.xml
+++ b/baseline.xml
@@ -159,11 +159,6 @@
convertToPHPValue($data['recorded_on'], $platform)]]>
-
-
- messages]]>
-
-
diff --git a/deptrac-baseline.yaml b/deptrac-baseline.yaml
index fe42e99fd..83ffb4da8 100644
--- a/deptrac-baseline.yaml
+++ b/deptrac-baseline.yaml
@@ -14,3 +14,12 @@ deptrac:
- Patchlevel\EventSourcing\Aggregate\AggregateRoot
Patchlevel\EventSourcing\Attribute\Subscriber:
- Patchlevel\EventSourcing\Subscription\RunMode
+ Patchlevel\EventSourcing\Store\DoctrineDbalStore:
+ - Patchlevel\EventSourcing\Aggregate\AggregateHeader
+ Patchlevel\EventSourcing\Store\DoctrineDbalStoreStream:
+ - Patchlevel\EventSourcing\Aggregate\AggregateHeader
+ Patchlevel\EventSourcing\Store\InMemoryStore:
+ - Patchlevel\EventSourcing\Aggregate\AggregateHeader
+ - Patchlevel\EventSourcing\Metadata\Event\EventRegistry
+ Patchlevel\EventSourcing\Store\MissingEventRegistry:
+ - Patchlevel\EventSourcing\Metadata\Event\EventRegistry
diff --git a/deptrac.yaml b/deptrac.yaml
index ded380b15..9d7c64eb8 100644
--- a/deptrac.yaml
+++ b/deptrac.yaml
@@ -186,8 +186,6 @@ deptrac:
- Cryptography
- MetadataAggregate
Store:
- - Aggregate
- - Attribute
- Clock
- Message
- Metadata
diff --git a/src/Store/Header/IndexHeader.php b/src/Store/Header/IndexHeader.php
index e5baa9816..8e659b6ec 100644
--- a/src/Store/Header/IndexHeader.php
+++ b/src/Store/Header/IndexHeader.php
@@ -4,10 +4,7 @@
namespace Patchlevel\EventSourcing\Store\Header;
-/**
- * @psalm-immutable
- * @experimental
- */
+/** @psalm-immutable */
final class IndexHeader
{
/** @param positive-int $index */
diff --git a/src/Store/InMemoryStore.php b/src/Store/InMemoryStore.php
index e2833430c..55b88ccb1 100644
--- a/src/Store/InMemoryStore.php
+++ b/src/Store/InMemoryStore.php
@@ -6,21 +6,30 @@
use Closure;
use Patchlevel\EventSourcing\Aggregate\AggregateHeader;
+use Patchlevel\EventSourcing\Clock\SystemClock;
use Patchlevel\EventSourcing\Message\HeaderNotFound;
use Patchlevel\EventSourcing\Message\Message;
+use Patchlevel\EventSourcing\Metadata\Event\EventRegistry;
use Patchlevel\EventSourcing\Store\Criteria\AggregateIdCriterion;
use Patchlevel\EventSourcing\Store\Criteria\AggregateNameCriterion;
use Patchlevel\EventSourcing\Store\Criteria\ArchivedCriterion;
use Patchlevel\EventSourcing\Store\Criteria\Criteria;
+use Patchlevel\EventSourcing\Store\Criteria\EventsCriterion;
use Patchlevel\EventSourcing\Store\Criteria\FromIndexCriterion;
use Patchlevel\EventSourcing\Store\Criteria\FromPlayheadCriterion;
use Patchlevel\EventSourcing\Store\Criteria\StreamCriterion;
+use Patchlevel\EventSourcing\Store\Criteria\ToIndexCriterion;
+use Patchlevel\EventSourcing\Store\Header\EventIdHeader;
+use Patchlevel\EventSourcing\Store\Header\IndexHeader;
use Patchlevel\EventSourcing\Store\Header\PlayheadHeader;
+use Patchlevel\EventSourcing\Store\Header\RecordedOnHeader;
use Patchlevel\EventSourcing\Store\Header\StreamNameHeader;
+use Psr\Clock\ClockInterface;
+use Ramsey\Uuid\Uuid;
+use Throwable;
use function array_filter;
use function array_map;
-use function array_push;
use function array_reverse;
use function array_slice;
use function array_unique;
@@ -35,10 +44,16 @@
final class InMemoryStore implements StreamStore
{
- /** @param array $messages */
+ /** @var array<0|positive-int, Message> */
+ private array $messages = [];
+
+ /** @param list $messages */
public function __construct(
- private array $messages = [],
+ array $messages = [],
+ private readonly EventRegistry|null $eventRegistry = null,
+ private readonly ClockInterface $clock = new SystemClock(),
) {
+ $this->save(...$messages);
}
public function load(
@@ -71,7 +86,27 @@ public function count(Criteria|null $criteria = null): int
public function save(Message ...$messages): void
{
- array_push($this->messages, ...$messages);
+ $this->transactional(function () use ($messages): void {
+ $count = count($this->messages);
+
+ foreach ($messages as $message) {
+ $count++;
+
+ if (!$message->hasHeader(IndexHeader::class)) {
+ $message = $message->withHeader(new IndexHeader($count));
+ }
+
+ if (!$message->hasHeader(EventIdHeader::class)) {
+ $message = $message->withHeader(new EventIdHeader(Uuid::uuid7()->toString()));
+ }
+
+ if (!$message->hasHeader(RecordedOnHeader::class)) {
+ $message = $message->withHeader(new RecordedOnHeader($this->clock->now()));
+ }
+
+ $this->messages[] = $message;
+ }
+ });
}
/**
@@ -81,7 +116,14 @@ public function save(Message ...$messages): void
*/
public function transactional(Closure $function): void
{
- $function();
+ $messages = $this->messages;
+ try {
+ $function();
+ } catch (Throwable $e) {
+ $this->messages = $messages;
+
+ throw $e;
+ }
}
/** @return list */
@@ -134,9 +176,11 @@ private function filter(Criteria|null $criteria): array
return $this->messages;
}
+ $eventRegistry = $this->eventRegistry;
+
return array_filter(
$this->messages,
- static function (Message $message, int $index) use ($criteria): bool {
+ static function (Message $message) use ($criteria, $eventRegistry): bool {
foreach ($criteria->all() as $criterion) {
switch ($criterion::class) {
case AggregateIdCriterion::class:
@@ -222,7 +266,35 @@ static function (Message $message, int $index) use ($criteria): bool {
break;
case FromIndexCriterion::class:
- if ($index < $criterion->fromIndex) {
+ try {
+ $index = $message->header(IndexHeader::class)->index;
+ } catch (HeaderNotFound) {
+ return false;
+ }
+
+ if ($index <= $criterion->fromIndex) {
+ return false;
+ }
+
+ break;
+ case ToIndexCriterion::class:
+ try {
+ $index = $message->header(IndexHeader::class)->index;
+ } catch (HeaderNotFound) {
+ return false;
+ }
+
+ if ($index >= $criterion->toIndex) {
+ return false;
+ }
+
+ break;
+ case EventsCriterion::class:
+ if ($eventRegistry === null) {
+ throw new MissingEventRegistry($criterion::class);
+ }
+
+ if (!in_array($eventRegistry->eventName($message->event()::class), $criterion->events)) {
return false;
}
diff --git a/src/Store/MissingEventRegistry.php b/src/Store/MissingEventRegistry.php
new file mode 100644
index 000000000..0987425f0
--- /dev/null
+++ b/src/Store/MissingEventRegistry.php
@@ -0,0 +1,19 @@
+withHeader(new EventIdHeader('019aa600-56ef-7ca3-b92a-37c53851e2c2'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(1)),
+ (new Message(new ProfileVisited(ProfileId::fromString('2'))))
+ ->withHeader(new EventIdHeader('019aa600-8834-752a-ae2e-d8650e84f403'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(2)),
];
$store = new InMemoryStore($expected);
@@ -57,10 +73,19 @@ public function testLoadMessages(): void
public function testLoadByAggregateId(): void
{
$message1 = (new Message(new ProfileVisited(ProfileId::fromString('1'))))
- ->withHeader(new AggregateHeader('profile', '1', 1, new DateTimeImmutable()));
+ ->withHeader(new AggregateHeader('profile', '1', 1, new DateTimeImmutable()))
+ ->withHeader(new EventIdHeader('019aa600-56ef-7ca3-b92a-37c53851e2c2'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(1));
$message2 = (new Message(new ProfileVisited(ProfileId::fromString('2'))))
- ->withHeader(new AggregateHeader('profile', '2', 1, new DateTimeImmutable()));
- $message3 = new Message(new ProfileVisited(ProfileId::fromString('3')));
+ ->withHeader(new AggregateHeader('profile', '2', 1, new DateTimeImmutable()))
+ ->withHeader(new EventIdHeader('019aa600-8834-752a-ae2e-d8650e84f403'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(2));
+ $message3 = (new Message(new ProfileVisited(ProfileId::fromString('3'))))
+ ->withHeader(new EventIdHeader('019aa604-94b8-7182-b1dc-f5d4aa9652ca'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(3));
$store = new InMemoryStore([$message1, $message2, $message3]);
@@ -74,10 +99,19 @@ public function testLoadByAggregateId(): void
public function testLoadByAggregateName(): void
{
$message1 = (new Message(new ProfileVisited(ProfileId::fromString('1'))))
- ->withHeader(new AggregateHeader('foo', '1', 1, new DateTimeImmutable()));
+ ->withHeader(new AggregateHeader('foo', '1', 1, new DateTimeImmutable()))
+ ->withHeader(new EventIdHeader('019aa600-56ef-7ca3-b92a-37c53851e2c2'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(1));
$message2 = (new Message(new ProfileVisited(ProfileId::fromString('2'))))
- ->withHeader(new AggregateHeader('bar', '2', 1, new DateTimeImmutable()));
- $message3 = new Message(new ProfileVisited(ProfileId::fromString('3')));
+ ->withHeader(new AggregateHeader('bar', '2', 1, new DateTimeImmutable()))
+ ->withHeader(new EventIdHeader('019aa600-8834-752a-ae2e-d8650e84f403'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(2));
+ $message3 = (new Message(new ProfileVisited(ProfileId::fromString('3'))))
+ ->withHeader(new EventIdHeader('019aa604-94b8-7182-b1dc-f5d4aa9652ca'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(3));
$store = new InMemoryStore([$message1, $message2, $message3]);
@@ -91,10 +125,19 @@ public function testLoadByAggregateName(): void
public function testLoadByStreamName(): void
{
$message1 = (new Message(new ProfileVisited(ProfileId::fromString('1'))))
- ->withHeader(new StreamNameHeader('foo'));
+ ->withHeader(new StreamNameHeader('foo'))
+ ->withHeader(new EventIdHeader('019aa600-56ef-7ca3-b92a-37c53851e2c2'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(1));
$message2 = (new Message(new ProfileVisited(ProfileId::fromString('2'))))
- ->withHeader(new StreamNameHeader('bar'));
- $message3 = new Message(new ProfileVisited(ProfileId::fromString('3')));
+ ->withHeader(new StreamNameHeader('bar'))
+ ->withHeader(new EventIdHeader('019aa600-8834-752a-ae2e-d8650e84f403'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(2));
+ $message3 = (new Message(new ProfileVisited(ProfileId::fromString('3'))))
+ ->withHeader(new EventIdHeader('019aa604-94b8-7182-b1dc-f5d4aa9652ca'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(3));
$store = new InMemoryStore([$message1, $message2, $message3]);
@@ -108,11 +151,20 @@ public function testLoadByStreamName(): void
public function testLoadByStreamNameWithLike(): void
{
$message1 = (new Message(new ProfileVisited(ProfileId::fromString('1'))))
- ->withHeader(new StreamNameHeader('foo-3'));
+ ->withHeader(new StreamNameHeader('foo-3'))
+ ->withHeader(new EventIdHeader('019aa600-56ef-7ca3-b92a-37c53851e2c2'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(1));
$message2 = (new Message(new ProfileVisited(ProfileId::fromString('2'))))
- ->withHeader(new StreamNameHeader('bar-1'));
+ ->withHeader(new StreamNameHeader('bar-1'))
+ ->withHeader(new EventIdHeader('019aa600-8834-752a-ae2e-d8650e84f403'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(2));
$message3 = (new Message(new ProfileVisited(ProfileId::fromString('3'))))
- ->withHeader(new StreamNameHeader('bar-2'));
+ ->withHeader(new StreamNameHeader('bar-2'))
+ ->withHeader(new EventIdHeader('019aa604-94b8-7182-b1dc-f5d4aa9652ca'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(3));
$store = new InMemoryStore([$message1, $message2, $message3]);
@@ -126,13 +178,25 @@ public function testLoadByStreamNameWithLike(): void
public function testLoadFromPlayhead(): void
{
$message1 = (new Message(new ProfileVisited(ProfileId::fromString('1'))))
- ->withHeader(new AggregateHeader('foo', '1', 1, new DateTimeImmutable()));
+ ->withHeader(new AggregateHeader('foo', '1', 1, new DateTimeImmutable()))
+ ->withHeader(new EventIdHeader('019aa600-56ef-7ca3-b92a-37c53851e2c2'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(1));
$message2 = (new Message(new ProfileVisited(ProfileId::fromString('2'))))
- ->withHeader(new AggregateHeader('foo', '1', 2, new DateTimeImmutable()));
+ ->withHeader(new AggregateHeader('foo', '1', 2, new DateTimeImmutable()))
+ ->withHeader(new EventIdHeader('019aa600-8834-752a-ae2e-d8650e84f403'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(2));
$message3 = (new Message(new ProfileVisited(ProfileId::fromString('3'))))
->withHeader(new StreamNameHeader('foo-1'))
- ->withHeader(new PlayheadHeader(3));
- $message4 = new Message(new ProfileVisited(ProfileId::fromString('3')));
+ ->withHeader(new PlayheadHeader(3))
+ ->withHeader(new EventIdHeader('019aa604-94b8-7182-b1dc-f5d4aa9652ca'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(3));
+ $message4 = (new Message(new ProfileVisited(ProfileId::fromString('3'))))
+ ->withHeader(new EventIdHeader('019aa607-3a33-7f47-bb66-4223f2390a30'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(4));
$store = new InMemoryStore([$message1, $message2, $message3, $message4]);
@@ -146,13 +210,25 @@ public function testLoadFromPlayhead(): void
public function testLoadFromIndex(): void
{
$message1 = (new Message(new ProfileVisited(ProfileId::fromString('1'))))
- ->withHeader(new AggregateHeader('foo', '1', 1, new DateTimeImmutable()));
+ ->withHeader(new AggregateHeader('foo', '1', 1, new DateTimeImmutable()))
+ ->withHeader(new EventIdHeader('019aa600-56ef-7ca3-b92a-37c53851e2c2'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(1));
$message2 = (new Message(new ProfileVisited(ProfileId::fromString('2'))))
- ->withHeader(new AggregateHeader('foo', '1', 2, new DateTimeImmutable()));
+ ->withHeader(new AggregateHeader('foo', '1', 2, new DateTimeImmutable()))
+ ->withHeader(new EventIdHeader('019aa600-8834-752a-ae2e-d8650e84f403'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(2));
$message3 = (new Message(new ProfileVisited(ProfileId::fromString('3'))))
->withHeader(new StreamNameHeader('foo-1'))
- ->withHeader(new PlayheadHeader(3));
- $message4 = new Message(new ProfileVisited(ProfileId::fromString('3')));
+ ->withHeader(new PlayheadHeader(3))
+ ->withHeader(new EventIdHeader('019aa604-94b8-7182-b1dc-f5d4aa9652ca'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(3));
+ $message4 = (new Message(new ProfileVisited(ProfileId::fromString('3'))))
+ ->withHeader(new EventIdHeader('019aa607-3a33-7f47-bb66-4223f2390a30'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(4));
$store = new InMemoryStore([$message1, $message2, $message3, $message4]);
@@ -160,17 +236,87 @@ public function testLoadFromIndex(): void
$messages = iterator_to_array($stream);
- self::assertSame([$message3, $message4], $messages);
+ self::assertCount(2, $messages);
+ self::assertSame(
+ $message3->header(PlayheadHeader::class)->playhead,
+ $messages[0]->header(PlayheadHeader::class)->playhead,
+ );
+ self::assertSame(
+ 3,
+ $messages[0]->header(IndexHeader::class)->index,
+ );
+ self::assertFalse($message4->hasHeader(PlayheadHeader::class));
+ self::assertSame(
+ 4,
+ $messages[1]->header(IndexHeader::class)->index,
+ );
+ }
+
+ public function testLoadToIndex(): void
+ {
+ $message1 = (new Message(new ProfileVisited(ProfileId::fromString('1'))))
+ ->withHeader(new AggregateHeader('foo', '1', 1, new DateTimeImmutable()))
+ ->withHeader(new EventIdHeader('019aa600-56ef-7ca3-b92a-37c53851e2c2'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(1));
+ $message2 = (new Message(new ProfileVisited(ProfileId::fromString('2'))))
+ ->withHeader(new AggregateHeader('foo', '1', 2, new DateTimeImmutable()))
+ ->withHeader(new EventIdHeader('019aa600-8834-752a-ae2e-d8650e84f403'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(2));
+ $message3 = (new Message(new ProfileVisited(ProfileId::fromString('3'))))
+ ->withHeader(new StreamNameHeader('foo-1'))
+ ->withHeader(new PlayheadHeader(3))
+ ->withHeader(new EventIdHeader('019aa604-94b8-7182-b1dc-f5d4aa9652ca'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(3));
+ $message4 = (new Message(new ProfileVisited(ProfileId::fromString('3'))))
+ ->withHeader(new EventIdHeader('019aa607-3a33-7f47-bb66-4223f2390a30'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(4));
+
+ $store = new InMemoryStore([$message1, $message2, $message3, $message4]);
+
+ $stream = $store->load(new Criteria(new ToIndexCriterion(3)));
+
+ $messages = iterator_to_array($stream);
+
+ self::assertCount(2, $messages);
+ self::assertSame(
+ $message1->header(AggregateHeader::class)->playhead,
+ $messages[0]->header(AggregateHeader::class)->playhead,
+ );
+ self::assertSame(
+ 1,
+ $messages[0]->header(IndexHeader::class)->index,
+ );
+ self::assertSame(
+ $message2->header(AggregateHeader::class)->playhead,
+ $messages[1]->header(AggregateHeader::class)->playhead,
+ );
+ self::assertSame(
+ 2,
+ $messages[1]->header(IndexHeader::class)->index,
+ );
}
public function testLoadByStreamNameWithLikeAll(): void
{
$message1 = (new Message(new ProfileVisited(ProfileId::fromString('1'))))
- ->withHeader(new StreamNameHeader('foo-3'));
+ ->withHeader(new StreamNameHeader('foo-3'))
+ ->withHeader(new EventIdHeader('019aa600-56ef-7ca3-b92a-37c53851e2c2'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(1));
$message2 = (new Message(new ProfileVisited(ProfileId::fromString('2'))))
- ->withHeader(new StreamNameHeader('bar-1'));
+ ->withHeader(new StreamNameHeader('bar-1'))
+ ->withHeader(new EventIdHeader('019aa600-8834-752a-ae2e-d8650e84f403'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(2));
$message3 = (new Message(new ProfileVisited(ProfileId::fromString('3'))))
- ->withHeader(new StreamNameHeader('bar-2'));
+ ->withHeader(new StreamNameHeader('bar-2'))
+ ->withHeader(new EventIdHeader('019aa604-94b8-7182-b1dc-f5d4aa9652ca'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(3));
$store = new InMemoryStore([$message1, $message2, $message3]);
@@ -184,8 +330,14 @@ public function testLoadByStreamNameWithLikeAll(): void
public function testLoadArchived(): void
{
$message1 = (new Message(new ProfileVisited(ProfileId::fromString('1'))))
- ->withHeader(new ArchivedHeader());
- $message2 = new Message(new ProfileVisited(ProfileId::fromString('2')));
+ ->withHeader(new ArchivedHeader())
+ ->withHeader(new EventIdHeader('019aa600-56ef-7ca3-b92a-37c53851e2c2'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(1));
+ $message2 = (new Message(new ProfileVisited(ProfileId::fromString('2'))))
+ ->withHeader(new EventIdHeader('019aa600-8834-752a-ae2e-d8650e84f403'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(2));
$store = new InMemoryStore([$message1, $message2]);
@@ -196,11 +348,77 @@ public function testLoadArchived(): void
self::assertSame([$message1], $messages);
}
+ public function testLoadByEventName(): void
+ {
+ $message1 = (new Message(new ProfileCreated(ProfileId::fromString('1'), Email::fromString('s@b.de'))))
+ ->withHeader(new StreamNameHeader('foo'))
+ ->withHeader(new EventIdHeader('019aa600-56ef-7ca3-b92a-37c53851e2c2'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(1));
+ $message2 = (new Message(new ProfileVisited(ProfileId::fromString('2'))))
+ ->withHeader(new StreamNameHeader('bar'))
+ ->withHeader(new EventIdHeader('019aa600-8834-752a-ae2e-d8650e84f403'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(2));
+ $message3 = (new Message(new ProfileVisited(ProfileId::fromString('3'))))
+ ->withHeader(new EventIdHeader('019aa604-94b8-7182-b1dc-f5d4aa9652ca'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(3));
+
+ $store = new InMemoryStore(
+ [$message1, $message2, $message3],
+ new EventRegistry([
+ 'profile_created' => ProfileCreated::class,
+ 'profile_visited' => ProfileVisited::class,
+ ]),
+ );
+
+ $stream = $store->load(new Criteria(new EventsCriterion(['profile_created'])));
+ $messages = iterator_to_array($stream);
+
+ self::assertSame([$message1], $messages);
+
+ $stream = $store->load(new Criteria(new EventsCriterion(['profile_visited'])));
+ $messages = iterator_to_array($stream);
+
+ self::assertSame([$message2, $message3], $messages);
+ self::assertSame([], iterator_to_array($store->load(new Criteria(new EventsCriterion(['profile_deleted'])))));
+ }
+
+ public function testLoadByEventNameWithoutRegistry(): void
+ {
+ $message1 = (new Message(new ProfileCreated(ProfileId::fromString('1'), Email::fromString('s@b.de'))))
+ ->withHeader(new StreamNameHeader('foo'))
+ ->withHeader(new EventIdHeader('019aa600-56ef-7ca3-b92a-37c53851e2c2'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(1));
+ $message2 = (new Message(new ProfileVisited(ProfileId::fromString('2'))))
+ ->withHeader(new StreamNameHeader('bar'))
+ ->withHeader(new EventIdHeader('019aa600-8834-752a-ae2e-d8650e84f403'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(2));
+ $message3 = (new Message(new ProfileVisited(ProfileId::fromString('3'))))
+ ->withHeader(new EventIdHeader('019aa604-94b8-7182-b1dc-f5d4aa9652ca'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(3));
+
+ $store = new InMemoryStore([$message1, $message2, $message3]);
+
+ $this->expectException(MissingEventRegistry::class);
+ $store->load(new Criteria(new EventsCriterion(['profile_created'])));
+ }
+
public function testLoadUnsupportedCriterion(): void
{
$store = new InMemoryStore([
- new Message(new ProfileVisited(ProfileId::fromString('1'))),
- new Message(new ProfileVisited(ProfileId::fromString('2'))),
+ (new Message(new ProfileVisited(ProfileId::fromString('1'))))
+ ->withHeader(new EventIdHeader('019aa600-56ef-7ca3-b92a-37c53851e2c2'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(1)),
+ (new Message(new ProfileVisited(ProfileId::fromString('2'))))
+ ->withHeader(new EventIdHeader('019aa600-8834-752a-ae2e-d8650e84f403'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(2)),
]);
$this->expectException(UnsupportedCriterion::class);
@@ -210,8 +428,14 @@ public function testLoadUnsupportedCriterion(): void
public function testLoadLimit(): void
{
- $message1 = new Message(new ProfileVisited(ProfileId::fromString('1')));
- $message2 = new Message(new ProfileVisited(ProfileId::fromString('2')));
+ $message1 = (new Message(new ProfileVisited(ProfileId::fromString('1'))))
+ ->withHeader(new EventIdHeader('019aa600-56ef-7ca3-b92a-37c53851e2c2'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(1));
+ $message2 = (new Message(new ProfileVisited(ProfileId::fromString('2'))))
+ ->withHeader(new EventIdHeader('019aa600-8834-752a-ae2e-d8650e84f403'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(2));
$store = new InMemoryStore([$message1, $message2]);
@@ -224,8 +448,14 @@ public function testLoadLimit(): void
public function testLoadOffset(): void
{
- $message1 = new Message(new ProfileVisited(ProfileId::fromString('1')));
- $message2 = new Message(new ProfileVisited(ProfileId::fromString('2')));
+ $message1 = (new Message(new ProfileVisited(ProfileId::fromString('1'))))
+ ->withHeader(new EventIdHeader('019aa600-56ef-7ca3-b92a-37c53851e2c2'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(1));
+ $message2 = (new Message(new ProfileVisited(ProfileId::fromString('2'))))
+ ->withHeader(new EventIdHeader('019aa600-8834-752a-ae2e-d8650e84f403'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(2));
$store = new InMemoryStore([$message1, $message2]);
@@ -238,8 +468,14 @@ public function testLoadOffset(): void
public function testLoadBackwards(): void
{
- $message1 = new Message(new ProfileVisited(ProfileId::fromString('1')));
- $message2 = new Message(new ProfileVisited(ProfileId::fromString('2')));
+ $message1 = (new Message(new ProfileVisited(ProfileId::fromString('1'))))
+ ->withHeader(new EventIdHeader('019aa600-56ef-7ca3-b92a-37c53851e2c2'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(1));
+ $message2 = (new Message(new ProfileVisited(ProfileId::fromString('2'))))
+ ->withHeader(new EventIdHeader('019aa600-8834-752a-ae2e-d8650e84f403'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(2));
$store = new InMemoryStore([$message1, $message2]);
@@ -253,8 +489,14 @@ public function testLoadBackwards(): void
public function testCount(): void
{
$message1 = (new Message(new ProfileVisited(ProfileId::fromString('1'))))
- ->withHeader(new ArchivedHeader());
- $message2 = new Message(new ProfileVisited(ProfileId::fromString('2')));
+ ->withHeader(new ArchivedHeader())
+ ->withHeader(new EventIdHeader('019aa600-56ef-7ca3-b92a-37c53851e2c2'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(1));
+ $message2 = (new Message(new ProfileVisited(ProfileId::fromString('2'))))
+ ->withHeader(new EventIdHeader('019aa600-8834-752a-ae2e-d8650e84f403'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(2));
$store = new InMemoryStore([$message1, $message2]);
@@ -264,8 +506,14 @@ public function testCount(): void
public function testSaveEmpty(): void
{
$expected = [
- new Message(new ProfileVisited(ProfileId::fromString('1'))),
- new Message(new ProfileVisited(ProfileId::fromString('2'))),
+ (new Message(new ProfileVisited(ProfileId::fromString('1'))))
+ ->withHeader(new EventIdHeader('019aa600-56ef-7ca3-b92a-37c53851e2c2'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(1)),
+ (new Message(new ProfileVisited(ProfileId::fromString('2'))))
+ ->withHeader(new EventIdHeader('019aa600-8834-752a-ae2e-d8650e84f403'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(2)),
];
$store = new InMemoryStore([]);
@@ -282,11 +530,20 @@ public function testSaveEmpty(): void
public function testSaveWithExistingMessages(): void
{
$startMessages = [
- new Message(new ProfileVisited(ProfileId::fromString('1'))),
- new Message(new ProfileVisited(ProfileId::fromString('2'))),
+ (new Message(new ProfileVisited(ProfileId::fromString('1'))))
+ ->withHeader(new EventIdHeader('019aa600-56ef-7ca3-b92a-37c53851e2c2'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(1)),
+ (new Message(new ProfileVisited(ProfileId::fromString('2'))))
+ ->withHeader(new EventIdHeader('019aa600-8834-752a-ae2e-d8650e84f403'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(2)),
];
- $message1 = new Message(new ProfileVisited(ProfileId::fromString('3')));
+ $message1 = (new Message(new ProfileVisited(ProfileId::fromString('3'))))
+ ->withHeader(new EventIdHeader('019aa604-94b8-7182-b1dc-f5d4aa9652ca'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(3));
$store = new InMemoryStore($startMessages);
@@ -299,15 +556,44 @@ public function testSaveWithExistingMessages(): void
self::assertSame([...$startMessages, $message1], $messages);
}
+ public function testSaveWithoutHeaders(): void
+ {
+ $store = new InMemoryStore([new Message(new ProfileVisited(ProfileId::fromString('3')))]);
+ $store->save(new Message(new ProfileVisited(ProfileId::fromString('1'))));
+
+ $stream = $store->load();
+ $messages = iterator_to_array($stream);
+
+ self::assertCount(2, $messages);
+
+ foreach ($messages as $message) {
+ self::assertTrue($message->hasHeader(EventIdHeader::class));
+ self::assertTrue($message->hasHeader(RecordedOnHeader::class));
+ self::assertTrue($message->hasHeader(IndexHeader::class));
+ }
+ }
+
public function testStreams(): void
{
$message1 = (new Message(new ProfileVisited(ProfileId::fromString('1'))))
- ->withHeader(new StreamNameHeader('foo'));
+ ->withHeader(new StreamNameHeader('foo'))
+ ->withHeader(new EventIdHeader('019aa600-56ef-7ca3-b92a-37c53851e2c2'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(1));
$message2 = (new Message(new ProfileVisited(ProfileId::fromString('2'))))
- ->withHeader(new StreamNameHeader('bar'));
+ ->withHeader(new StreamNameHeader('bar'))
+ ->withHeader(new EventIdHeader('019aa600-8834-752a-ae2e-d8650e84f403'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(2));
$message3 = (new Message(new ProfileVisited(ProfileId::fromString('3'))))
- ->withHeader(new StreamNameHeader('bar'));
- $message4 = new Message(new ProfileVisited(ProfileId::fromString('3')));
+ ->withHeader(new StreamNameHeader('bar'))
+ ->withHeader(new EventIdHeader('019aa604-94b8-7182-b1dc-f5d4aa9652ca'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(3));
+ $message4 = (new Message(new ProfileVisited(ProfileId::fromString('3'))))
+ ->withHeader(new EventIdHeader('019aa607-3a33-7f47-bb66-4223f2390a30'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(4));
$store = new InMemoryStore([$message1, $message2, $message3, $message4]);
@@ -317,12 +603,24 @@ public function testStreams(): void
public function testRemove(): void
{
$message1 = (new Message(new ProfileVisited(ProfileId::fromString('1'))))
- ->withHeader(new StreamNameHeader('foo'));
+ ->withHeader(new StreamNameHeader('foo'))
+ ->withHeader(new EventIdHeader('019aa600-56ef-7ca3-b92a-37c53851e2c2'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(1));
$message2 = (new Message(new ProfileVisited(ProfileId::fromString('2'))))
- ->withHeader(new StreamNameHeader('bar'));
+ ->withHeader(new StreamNameHeader('bar'))
+ ->withHeader(new EventIdHeader('019aa600-8834-752a-ae2e-d8650e84f403'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(2));
$message3 = (new Message(new ProfileVisited(ProfileId::fromString('3'))))
- ->withHeader(new StreamNameHeader('bar'));
- $message4 = new Message(new ProfileVisited(ProfileId::fromString('3')));
+ ->withHeader(new StreamNameHeader('bar'))
+ ->withHeader(new EventIdHeader('019aa604-94b8-7182-b1dc-f5d4aa9652ca'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(3));
+ $message4 = (new Message(new ProfileVisited(ProfileId::fromString('3'))))
+ ->withHeader(new EventIdHeader('019aa607-3a33-7f47-bb66-4223f2390a30'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(4));
$store = new InMemoryStore([$message1, $message2, $message3, $message4]);
@@ -349,15 +647,63 @@ static function () use (&$called): void {
self::assertTrue($called);
}
+ public function testTransactionalThrowAndResetting(): void
+ {
+ $message1 = (new Message(new ProfileVisited(ProfileId::fromString('1'))))
+ ->withHeader(new StreamNameHeader('foo'))
+ ->withHeader(new EventIdHeader('019aa600-56ef-7ca3-b92a-37c53851e2c2'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(1));
+ $message2 = (new Message(new ProfileVisited(ProfileId::fromString('2'))))
+ ->withHeader(new StreamNameHeader('bar'))
+ ->withHeader(new EventIdHeader('019aa600-8834-752a-ae2e-d8650e84f403'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(2));
+
+ $called = false;
+ $catched = false;
+
+ $store = new InMemoryStore([$message1]);
+
+ try {
+ $store->transactional(
+ static function () use (&$called, $message2, $store): void {
+ $called = true;
+ $store->save($message2);
+
+ throw new RuntimeException('test');
+ },
+ );
+ } catch (RuntimeException) {
+ $catched = true;
+ }
+
+ self::assertTrue($catched);
+ self::assertTrue($called);
+ self::assertCount(1, iterator_to_array($store->load()));
+ }
+
public function testClear(): void
{
$message1 = (new Message(new ProfileVisited(ProfileId::fromString('1'))))
- ->withHeader(new StreamNameHeader('foo'));
+ ->withHeader(new StreamNameHeader('foo'))
+ ->withHeader(new EventIdHeader('019aa600-56ef-7ca3-b92a-37c53851e2c2'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(1));
$message2 = (new Message(new ProfileVisited(ProfileId::fromString('2'))))
- ->withHeader(new StreamNameHeader('bar'));
+ ->withHeader(new StreamNameHeader('bar'))
+ ->withHeader(new EventIdHeader('019aa600-8834-752a-ae2e-d8650e84f403'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(2));
$message3 = (new Message(new ProfileVisited(ProfileId::fromString('3'))))
- ->withHeader(new StreamNameHeader('bar'));
- $message4 = new Message(new ProfileVisited(ProfileId::fromString('3')));
+ ->withHeader(new StreamNameHeader('bar'))
+ ->withHeader(new EventIdHeader('019aa604-94b8-7182-b1dc-f5d4aa9652ca'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(3));
+ $message4 = (new Message(new ProfileVisited(ProfileId::fromString('3'))))
+ ->withHeader(new EventIdHeader('019aa607-3a33-7f47-bb66-4223f2390a30'))
+ ->withHeader(new RecordedOnHeader(new DateTimeImmutable()))
+ ->withHeader(new IndexHeader(4));
$store = new InMemoryStore([$message1, $message2, $message3, $message4]);