This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
declare(strict_types=1); | |
namespace Test\Feature; | |
use Shared\Domain\DomainEvent; | |
use Shared\Domain\EventSourcedAggregateRoot; | |
use Webmozart\Assert\Assert; | |
use function array_shift; | |
/** | |
* This class is designed for inheritance, and provides facilities to test a generic {@see EventSourcedAggregateRoot}. | |
* | |
* @psalm-template TestedAggregateRootType of EventSourcedAggregateRoot | |
*/ | |
abstract class EventSourcedAggregateContext | |
{ | |
/** | |
* @var DomainEvent[] | |
* @psalm-var list<DomainEvent> | |
*/ | |
private array $pastHistory = []; | |
/** | |
* @var DomainEvent[]|null | |
* @psalm-var null|list<DomainEvent> | |
*/ | |
private ?array $recordedEvents = null; | |
/** @psalm-var TestedAggregateRootType|null */ | |
private ?EventSourcedAggregateRoot $aggregateRoot = null; | |
/** @AfterScenario */ | |
final public function ensure_no_further_events_were_recorded() : void | |
{ | |
Assert::isEmpty($this->recordedEvents); | |
} | |
/** @psalm-return class-string<TestedAggregateRootType> */ | |
abstract protected function aggregateRootClassName() : string; | |
/** @psalm-param TestedAggregateRootType $aggregateRoot */ | |
final protected function useManuallyCreatedAggregateRoot(EventSourcedAggregateRoot $aggregateRoot) : void | |
{ | |
Assert::null( | |
$this->aggregateRoot, | |
'You are replacing a pre-existing aggregate root: why are there two aggregates in your test?' | |
); | |
$this->aggregateRoot = $aggregateRoot; | |
} | |
final protected function givenPastEvent(DomainEvent $event) : void | |
{ | |
$this->pastHistory[] = $event; | |
} | |
/** | |
* @psalm-template ExpectedEventType of DomainEvent | |
* @psalm-param class-string<ExpectedEventType> $expectedEventType | |
* @psalm-return ExpectedEventType | |
*/ | |
final protected function expectNextRecordedEvent(string $expectedEventType) : DomainEvent | |
{ | |
if ($this->recordedEvents === null) { | |
$this->recordedEvents = $this->getOrReconstituteAggregateRoot()->popRecordedEvents(); | |
} | |
$event = array_shift($this->recordedEvents); | |
Assert::isInstanceOf($event, $expectedEventType); | |
return $event; | |
} | |
/** @psalm-return TestedAggregateRootType */ | |
final protected function getOrReconstituteAggregateRoot() : EventSourcedAggregateRoot | |
{ | |
return $this->aggregateRoot | |
?? $this->aggregateRoot = $this->aggregateRootClassName()::reconstituteFromHistory($this->pastHistory); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment