Passed
Push — master ( a68ac1...afa93b )
by Frank
03:30 queued 01:05
created

AggregateRootTestCase   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 196
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 98.44%

Importance

Changes 0
Metric Value
wmc 22
lcom 1
cbo 6
dl 0
loc 196
ccs 63
cts 64
cp 0.9844
rs 10
c 0
b 0
f 0

17 Methods

Rating   Name   Duplication   Size   Complexity  
A assertScenario() 0 18 2
aggregateRootId() 0 1 ?
aggregateRootClassName() 0 1 ?
A setUpEventStore() 0 14 1
A given() 0 7 1
A on() 0 4 1
A when() 0 14 3
A then() 0 6 1
A thenWeAreSorry() 0 6 1
A thenNothingShouldHaveHappened() 0 6 1
A assertLastCommitEqualsEvents() 0 4 1
B assertExpectedException() 0 12 5
A pointInTime() 0 4 1
A clock() 0 4 1
A messageDispatcher() 0 4 1
A consumers() 0 4 1
A messageDecorator() 0 4 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();
66 7
        $dispatcher = $this->messageDispatcher();
67 7
        $decorator = $this->messageDecorator();
68 7
        $this->repository = new AggregateRootRepository($className, $this->messageRepository, $dispatcher, $decorator);
69 7
        $this->expectedEvents = [];
70 7
        $this->assertedScenario = false;
71 7
        $this->theExpectedException = null;
72 7
        $this->caughtException = null;
73 7
    }
74
75
    /**
76
     * @after
77
     */
78 7
    protected function assertScenario()
79
    {
80
        // @codeCoverageIgnoreStart
81
        if ($this->assertedScenario) {
82
            return;
83
        }
84
        // @codeCoverageIgnoreEnd
85
86
        try {
87 7
            $this->assertExpectedException($this->theExpectedException, $this->caughtException);
88 5
            $this->assertLastCommitEqualsEvents(... $this->expectedEvents);
89 5
            $this->messageRepository->purgeLastCommit();
90 5
        } finally {
91 7
            $this->assertedScenario = true;
92 7
            $this->theExpectedException = null;
93 7
            $this->caughtException = null;
94
        }
95 5
    }
96
97
    abstract protected function aggregateRootId(): AggregateRootId;
98
99
    abstract protected function aggregateRootClassName(): string;
100
101
    /**
102
     * @return $this
103
     */
104 1
    protected function given(Event ... $events)
105
    {
106 1
        $this->repository->persistEvents($this->aggregateRootId(), ... $events);
107 1
        $this->messageRepository->purgeLastCommit();
108
109 1
        return $this;
110
    }
111
112 1
    public function on(AggregateRootId $id)
113
    {
114 1
        return new EventStager($id, $this->messageRepository, $this->repository, $this);
115
    }
116
117
    /**
118
     * @return $this
119
     */
120 7
    protected function when(... $arguments)
121
    {
122
        try {
123 7
            if ( ! method_exists($this, 'handle')) {
124
                throw new LogicException(sprintf('Class %s is missing a ::handle method.', get_class($this)));
125
            }
126
127 7
            $this->handle(...$arguments);
128 3
        } catch (Exception $exception) {
129 3
            $this->caughtException = $exception;
130
        }
131
132 7
        return $this;
133
    }
134
135
    /**
136
     * @return $this
137
     */
138 3
    protected function then(Event ... $events)
139
    {
140 3
        $this->expectedEvents = $events;
141
142 3
        return $this;
143
    }
144
145
    /**
146
     * @return $this
147
     */
148 2
    public function thenWeAreSorry(Exception $expectedException)
149
    {
150 2
        $this->theExpectedException = $expectedException;
151
152 2
        return $this;
153
    }
154
155
    /**
156
     * @return $this
157
     */
158 1
    protected function thenNothingShouldHaveHappened()
159
    {
160 1
        $this->expectedEvents = [];
161
162 1
        return $this;
163
    }
164
165 5
    protected function assertLastCommitEqualsEvents(Event ... $events)
166
    {
167 5
        self::assertEquals($events, $this->messageRepository->lastCommit(), "Events are not equal.");
168 5
    }
169
170 7
    private function assertExpectedException(Exception $expectedException = null, Exception $caughtException = null)
171
    {
172 7
        if ($expectedException == $caughtException) {
173 4
            return;
174
        }
175
176 3
        if ( ! $expectedException instanceof Exception || ($caughtException instanceof Exception && (get_class($expectedException) !== get_class($caughtException)))) {
177 2
            throw $caughtException;
178
        }
179
180 1
        self::assertEquals([$expectedException], [$caughtException], ">> Exceptions are not equal.");
181 1
    }
182
183 3
    protected function pointInTime(): PointInTime
184
    {
185 3
        return $this->clock->pointInTime();
186
    }
187
188 7
    protected function clock(): Clock
189
    {
190 7
        return $this->clock;
191
    }
192
193 7
    protected function messageDispatcher(): MessageDispatcher
194
    {
195 7
        return new SynchronousMessageDispatcher(... $this->consumers());
196
    }
197
198
    /**
199
     * @return Consumer[]
200
     */
201 7
    protected function consumers(): array
202
    {
203 7
        return [];
204
    }
205
206 7
    private function messageDecorator(): MessageDecorator
207
    {
208 7
        return new DelegatingMessageDecorator();
209
    }
210
}