Completed
Pull Request — master (#11)
by Frank
02:31
created

AggregateRootTestCase::on()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
crap 1
1
<?php
2
3
namespace EventSauce\EventSourcing;
4
5
use Exception;
6
use function func_get_args;
7
use function get_class;
8
use LogicException;
9
use function method_exists;
10
use PHPUnit\Framework\TestCase;
11
use EventSauce\EventSourcing\Time\Clock;
12
use EventSauce\EventSourcing\Time\TestClock;
13
use function sprintf;
14
15
abstract class AggregateRootTestCase extends TestCase
16
{
17
    /**
18
     * @var InMemoryMessageRepository
19
     */
20
    private $messageRepository;
21
22
    /**
23
     * @var AggregateRootRepository
24
     */
25
    protected $repository;
26
27
    /**
28
     * @var Exception|null
29
     */
30
    private $caughtException;
31
32
    /**
33
     * @var Event[]
34
     */
35
    private $expectedEvents = [];
36
37
    /**
38
     * @var Exception|null
39
     */
40
    private $theExpectedException;
41
42
    /**
43
     * @var TestClock
44
     */
45
    private $clock;
46
47
    /**
48
     * @var bool
49
     */
50
    private $assertedScenario = false;
51
52
    /**
53
     * @var AggregateRootId
54
     */
55
    protected $aggregateRootId;
56
57
    /**
58
     * @before
59
     */
60 7
    protected function setUpEventStore()
61
    {
62 7
        $className = $this->aggregateRootClassName();
63 7
        $this->clock = new TestClock();
64 7
        $this->aggregateRootId = $this->aggregateRootId();
65 7
        $this->messageRepository = new InMemoryMessageRepository($this->messageDispatcher());
66 7
        $this->repository = new AggregateRootRepository($className, $this->messageRepository, new DelegatingMessageDecorator());
67 7
        $this->expectedEvents = [];
68 7
        $this->assertedScenario = false;
69 7
        $this->theExpectedException = null;
70 7
        $this->caughtException = null;
71 7
    }
72
73
    /**
74
     * @after
75
     */
76 7
    protected function assertScenario()
77
    {
78
        // @codeCoverageIgnoreStart
79
        if ($this->assertedScenario) {
80
            return;
81
        }
82
        // @codeCoverageIgnoreEnd
83
84
        try {
85 7
            $this->assertExpectedException($this->theExpectedException, $this->caughtException);
86 5
            $this->assertLastCommitEqualsEvents(... $this->expectedEvents);
87 5
            $this->messageRepository->purgeLastCommit();
88 5
        } finally {
89 7
            $this->assertedScenario = true;
90 7
            $this->theExpectedException = null;
91 7
            $this->caughtException = null;
92
        }
93 5
    }
94
95
    abstract protected function aggregateRootId(): AggregateRootId;
96
97
    abstract protected function aggregateRootClassName(): string;
98
99
    /**
100
     * @return $this
101
     */
102 1
    protected function given(Event ... $events)
103
    {
104 1
        $this->repository->persistEvents($this->aggregateRootId(), ... $events);
105 1
        $this->messageRepository->purgeLastCommit();
106
107 1
        return $this;
108
    }
109
110 1
    public function on(AggregateRootId $id)
111
    {
112 1
        return new EventStager($id, $this->messageRepository, $this->repository, $this);
113
    }
114
115
    /**
116
     * @return $this
117
     */
118 7
    protected function when(... $arguments)
119
    {
120
        try {
121 7
            if ( ! method_exists($this, 'handle')) {
122
                throw new LogicException(sprintf('Class %s is missing a ::handle method.', get_class($this)));
123
            }
124
125 7
            $this->handle(...$arguments);
126 3
        } catch (Exception $exception) {
127 3
            $this->caughtException = $exception;
128
        }
129
130 7
        return $this;
131
    }
132
133
    /**
134
     * @return $this
135
     */
136 3
    protected function then(Event ... $events)
137
    {
138 3
        $this->expectedEvents = $events;
139
140 3
        return $this;
141
    }
142
143
    /**
144
     * @return $this
145
     */
146 2
    public function thenWeAreSorry(Exception $expectedException)
147
    {
148 2
        $this->theExpectedException = $expectedException;
149
150 2
        return $this;
151
    }
152
153
    /**
154
     * @return $this
155
     */
156 1
    protected function thenNothingShouldHaveHappened()
157
    {
158 1
        $this->expectedEvents = [];
159
160 1
        return $this;
161
    }
162
163 5
    protected function assertLastCommitEqualsEvents(Event ... $events)
164
    {
165 5
        self::assertEquals($events, $this->messageRepository->lastCommit(), "Events are not equal.");
166 5
    }
167
168 7
    private function assertExpectedException(Exception $expectedException = null, Exception $caughtException = null)
169
    {
170 7
        if ($expectedException == $caughtException) {
171 4
            return;
172
        }
173
174 3
        if ( ! $expectedException instanceof Exception || ($caughtException instanceof Exception && (get_class($expectedException) !== get_class($caughtException)))) {
175 2
            throw $caughtException;
176
        }
177
178 1
        self::assertEquals([$expectedException], [$caughtException], ">> Exceptions are not equal.");
179 1
    }
180
181 3
    protected function pointInTime(): PointInTime
182
    {
183 3
        return $this->clock->pointInTime();
184
    }
185
186 7
    protected function clock(): Clock
187
    {
188 7
        return $this->clock;
189
    }
190
191 7
    protected function messageDispatcher(): MessageDispatcher
192
    {
193 7
        return new SynchronousMessageDispatcher(... $this->consumers());
194
    }
195
196
    /**
197
     * @return Consumer[]
198
     */
199 7
    protected function consumers(): array
200
    {
201 7
        return [];
202
    }
203
}