Skip to content

Commit

Permalink
[EventSourcing] Quality of life updates (#108)
Browse files Browse the repository at this point in the history
  • Loading branch information
JoshuaEstes authored Nov 5, 2023
1 parent 7382e12 commit 983ece4
Show file tree
Hide file tree
Showing 9 changed files with 260 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@ final public function getPendingEvents(): iterable
return $events;
}

final public function peekPendingEvents(): iterable
{
return $this->pendingEvents;
}

final public static function buildFromEvents(AggregateIdInterface $id, \Generator $events): AggregateInterface
{
$aggregate = new static($id);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ public function getAggregateVersion(): AggregateVersionInterface;
*/
public function getPendingEvents(): iterable;

/**
* Returns pending events, but does not clear them out
*
* @return MessageInterface[]
*/
public function peekPendingEvents(): iterable;

/**
* Build Aggregate from a collection of Domain Events.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

namespace SonsOfPHP\Component\EventSourcing\Test;

use SonsOfPHP\Component\EventSourcing\Aggregate\AggregateInterface;

/**
* @author [email protected]
*/
trait AggregateAssertionsTrait
{
final public function assertEventRaised(string $eventClass, AggregateInterface $aggregate, string $message): void
{
$constraint = new EventRaised($eventClass);

\PHPUnit\Framework\Assert::assertThat($aggregate, $constraint, $message);
}

final public function assertCountEventsRaised(int $count, AggregateInterface $aggregate, string $message): void
{
$constraint = new CountEventsRaised($count);

\PHPUnit\Framework\Assert::assertThat($aggregate, $constraint, $message);
}
}
36 changes: 36 additions & 0 deletions src/SonsOfPHP/Component/EventSourcing/Test/CountEventsRaised.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

declare(strict_types=1);

namespace SonsOfPHP\Component\EventSourcing\Test;

use PHPUnit\Framework\Constraint\Constraint;
use SonsOfPHP\Component\EventSourcing\Aggregate\AggregateInterface;

/**
* Usage:
* $this->assertThat($aggregate, new CountEventsRaised(0));
*
* @author [email protected]
*/
final class CountEventsRaised extends Constraint
{
/**
* @codeCoverageIgnore
*/
public function __construct(private int $count) {}

protected function matches(mixed $other): bool
{
if (!$other instanceof AggregateInterface) {
throw \LogicException(sprintf('Object must implement "%s"', AggregateInterface::class));
}

return $this->count === count($other->peekPendingEvents());
}

public function toString(): string
{
return sprintf('has raised "%d" events', $this->count);
}
}
45 changes: 45 additions & 0 deletions src/SonsOfPHP/Component/EventSourcing/Test/EventRaised.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

declare(strict_types=1);

namespace SonsOfPHP\Component\EventSourcing\Test;

use PHPUnit\Framework\Constraint\Constraint;
use SonsOfPHP\Component\EventSourcing\Aggregate\AggregateInterface;

/**
* Usage:
* $this->assertThat($aggregate, new EventRaised(Event::class));
*
* @author [email protected]
*/
final class EventRaised extends Constraint
{
/**
* @codeCoverageIgnore
*/
public function __construct(private string $eventClass) {}

/**
* AggregateInterface $other
*/
protected function matches(mixed $other): bool
{
if (!$other instanceof AggregateInterface) {
throw \LogicException(sprintf('Object must implement "%s"', AggregateInterface::class));
}

foreach ($other->peekPendingEvents() as $event) {
if ($event instanceof $this->eventClass) {
return true;
}
}

return false;
}

public function toString(): string
{
return sprintf('has raised the event "%s"', $this->eventClass);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,20 @@ public function testItWillRaiseExceptionWithInvalidId(): void
$this->expectException(\TypeError::class);
$this->getMockForAbstractClass(AbstractAggregate::class, [new \stdClass()]);
}

/**
* @covers ::peekPendingEvents
*/
public function testPeekWillNotRemoveAnyPendingEvents(): void
{
$aggregate = new FakeAggregate('id');
$this->assertCount(0, $aggregate->peekPendingEvents());
$method = new \ReflectionMethod($aggregate, 'raiseEvent');

$message = $this->createMock(MessageInterface::class);
$method->invoke($aggregate, $message);

$this->assertCount(1, $aggregate->peekPendingEvents());
$this->assertCount(1, $aggregate->peekPendingEvents());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php

declare(strict_types=1);

namespace SonsOfPHP\Component\EventSourcing\Tests\Test;

use PHPUnit\Framework\TestCase;
use SonsOfPHP\Component\EventSourcing\Test\CountEventsRaised;
use SonsOfPHP\Component\EventSourcing\Aggregate\AggregateInterface;

/**
* @coversDefaultClass \SonsOfPHP\Component\EventSourcing\Test\CountEventsRaised
*/
final class CountEventsRaisedTest extends TestCase
{
private $aggregate;

public function setUp(): void
{
$this->aggregate = $this->createMock(AggregateInterface::class);
}

/**
* @covers ::matches
*/
public function testMatchesWorksAsExpectedWhenNoEvents(): void
{
$this->aggregate->method('peekPendingEvents')->willReturn([]);

$constraint = new CountEventsRaised(1);
$method = new \ReflectionMethod($constraint, 'matches');

$this->assertFalse($method->invoke($constraint, $this->aggregate));
}

/**
* @covers ::matches
*/
public function testMatchesWorksAsExpectedWhenEventsWereRaised(): void
{
$this->aggregate->method('peekPendingEvents')->willReturn([
new \stdClass(),
]);

$constraint = new CountEventsRaised(1);
$method = new \ReflectionMethod($constraint, 'matches');

$this->assertTrue($method->invoke($constraint, $this->aggregate));
}

/**
* @covers ::toString
*/
public function testToStringReturnsCorrectMessage(): void
{
$constraint = new CountEventsRaised(123);

$this->assertSame('has raised "123" events', $constraint->toString());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php

declare(strict_types=1);

namespace SonsOfPHP\Component\EventSourcing\Tests\Test;

use PHPUnit\Framework\TestCase;
use SonsOfPHP\Component\EventSourcing\Test\EventRaised;
use SonsOfPHP\Component\EventSourcing\Aggregate\AggregateInterface;

/**
* @coversDefaultClass \SonsOfPHP\Component\EventSourcing\Test\EventRaised
*/
final class EventRaisedTest extends TestCase
{
private $aggregate;

public function setUp(): void
{
$this->aggregate = $this->createMock(AggregateInterface::class);
}

/**
* @covers ::matches
*/
public function testMatchesWorksAsExpectedWhenEventWasNotRaised(): void
{
$this->aggregate->method('peekPendingEvents')->willReturn([]);

$constraint = new EventRaised('stdClass');
$method = new \ReflectionMethod($constraint, 'matches');

$this->assertFalse($method->invoke($constraint, $this->aggregate));
}

/**
* @covers ::matches
*/
public function testMatchesWorksAsExpectedWhenEventWasRaised(): void
{
$this->aggregate->method('peekPendingEvents')->willReturn([
new \stdClass(),
]);

$constraint = new EventRaised('stdClass');
$method = new \ReflectionMethod($constraint, 'matches');

$this->assertTrue($method->invoke($constraint, $this->aggregate));
}

/**
* @covers ::toString
*/
public function testToStringReturnsCorrectMessage(): void
{
$constraint = new EventRaised('stdClass');

$this->assertSame('has raised the event "stdClass"', $constraint->toString());
}
}
5 changes: 4 additions & 1 deletion src/SonsOfPHP/Component/EventSourcing/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@
"php": ">=8.1",
"sonsofphp/event-dispatcher": "^0.3.x-dev"
},
"require-dev": {
"phpunit/phpunit": "^10.4"
}
"suggest": {
"sonsofphp/event-sourcing-doctrine": "Adds additional functionality using Doctrine",
"sonsofphp/event-sourcing-symfony": "Adds additional functionality using Symfony Components"
Expand All @@ -53,4 +56,4 @@
"url": "https://tidelift.com/subscription/pkg/packagist-sonsofphp-sonsofphp"
}
]
}
}

0 comments on commit 983ece4

Please sign in to comment.