Completed
Push — master ( 39f0ce...88519e )
by Constantin
02:54
created

BddAggregateTestHelper::executeCommand()   B

Complexity

Conditions 3
Paths 4

Size

Total Lines 26
Code Lines 13

Duplication

Lines 8
Ratio 30.77 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
dl 8
loc 26
ccs 0
cts 17
cp 0
rs 8.8571
c 0
b 0
f 0
cc 3
eloc 13
nc 4
nop 1
crap 12
1
<?php
2
/******************************************************************************
3
 * Copyright (c) 2016 Constantin Galbenu <[email protected]>             *
4
 ******************************************************************************/
5
6
namespace Gica\Cqrs\Testing;
7
8
9
use Gica\Cqrs\Command;
10
use Gica\Cqrs\Command\CommandSubscriber;
11
use Gica\Cqrs\Event;
12
use Gica\Cqrs\Event\EventWithMetaData;
13
use Gica\Types\Guid;
14
15
abstract class BddAggregateTestHelper extends \PHPUnit_Framework_TestCase
16
{
17
    private $aggregateId;
18
19
    /** @var \Gica\Cqrs\Event\EventDispatcher */
20
    private $eventDispatcher;
21
22
    private $priorEvents = [];
23
24
    private $command;
25
    private $aggregate;
26
    private $aggregateHandlerMethodName;
27
28
    /** @var Event\EventsApplier\EventsApplierOnAggregate */
29
    private $eventsApplierOnAggregate;
30
31
    /** @var \Gica\Cqrs\Command\CommandApplier */
32
    private $commandApplier;
33
34
    abstract protected function getCommandSubscriber(): CommandSubscriber;
35
36
    protected function setUp()
37
    {
38
        $subscriber = new Event\EventSubscriber\ManualEventSubscriber();
39
        $this->eventDispatcher = new Event\EventDispatcher\EventDispatcherBySubscriber($subscriber);
40
        $this->eventsApplierOnAggregate = new Event\EventsApplier\EventsApplierOnAggregate();
41
        $this->commandApplier = new \Gica\Cqrs\Command\CommandApplier();
42
43
        $this->priorEvents = [];
44
        $this->command = null;
45
    }
46
47
    protected function onAggregate($aggregate)
48
    {
49
        $this->aggregate = $aggregate;
50
        $this->aggregateId = new \Gica\Types\Guid();
51
    }
52
53
    protected function given(...$priorEvents)
54
    {
55
        $this->priorEvents = $this->decorateEventsWithMetadata($priorEvents);
56
    }
57
58
    /**
59
     * @param \Gica\Cqrs\Event[] $priorEvents
60
     * @return \Gica\Cqrs\Event\EventWithMetaData[]
61
     */
62
    private function decorateEventsWithMetadata(array $priorEvents)
63
    {
64
        return array_map(function (\Gica\Cqrs\Event $event) {
65
            return $this->decorateEventWithMetaData($event);
66
        }, $priorEvents);
67
    }
68
69
70
    protected function when($command)
71
    {
72
        $this->command = $command;
73
    }
74
75
    protected function then(...$expectedEvents)
76
    {
77
        $this->eventsApplierOnAggregate->applyEventsOnAggregate($this->aggregate, $this->priorEvents);
78
79
        $newEvents = $this->executeCommand($this->command);
0 ignored issues
show
Documentation introduced by
$this->command is of type null, but the function expects a object<Gica\Cqrs\Command>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
80
81
        $this->assertTheseEvents($expectedEvents, $newEvents);
82
    }
83
84
    protected function executeCommand(Command $command)
85
    {
86
        $handler = $this->getCommandSubscriber()->getHandlerForCommand($command);
87
88
        $newEventsGenerator = $this->commandApplier->applyCommand($this->aggregate, $command, $handler->getMethodName());
89
90
        /** @var EventWithMetaData[] $eventsWithMetaData */
91
        $eventsWithMetaData = [];
92
93
        $newEvents = [];
94
95 View Code Duplication
        foreach ($newEventsGenerator as $event) {
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
96
            $eventWithMetaData = $this->decorateEventWithMetaData($event);
97
98
            $this->eventsApplierOnAggregate->applyEventsOnAggregate($this->aggregate, [$eventWithMetaData]);
99
100
            $eventsWithMetaData[] = $eventWithMetaData;
101
            $newEvents[] = $event;
102
        }
103
104
        foreach ($eventsWithMetaData as $eventWithMetaData) {
105
            $this->eventDispatcher->dispatchEvent($eventWithMetaData);
106
        }
107
108
        return $newEvents;
109
    }
110
111
    private function decorateEventWithMetaData(Event $event): EventWithMetaData
112
    {
113
        return new \Gica\Cqrs\Event\EventWithMetaData($event, $this->factoryMetaData());
114
    }
115
116
    protected function thenShouldFailWith($exceptionClass, $exceptionMessage = null)
117
    {
118
        $this->expectException($exceptionClass);
119
        if (null !== $exceptionMessage) {
120
            $this->expectExceptionMessage($exceptionMessage);
121
        }
122
123
        $handler = $this->getCommandSubscriber()->getHandlerForCommand($this->command);
0 ignored issues
show
Documentation introduced by
$this->command is of type null, but the function expects a object<Gica\Cqrs\Command>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
124
125
        $this->eventsApplierOnAggregate->applyEventsOnAggregate($this->aggregate, $this->priorEvents);
126
127
        iterator_to_array($this->commandApplier->applyCommand($this->aggregate, $this->command, $handler->getMethodName()));
0 ignored issues
show
Documentation introduced by
$this->command is of type null, but the function expects a object<Gica\Cqrs\Command>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
128
    }
129
130
    protected function assertTheseEvents(array $expectedEvents, array $actualEvents)
131
    {
132
        $expectedEvents = array_values($expectedEvents);
133
        $actualEvents = array_values($actualEvents);
134
135 View Code Duplication
        foreach ($expectedEvents as $k => $expectedEvent) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
136
            if (!isset($actualEvents[$k])) {
137
                $this->fail("Expected event #$k not fired (should have class " . get_class($expectedEvent) . ")");
138
                $this->assertEventsCountAreEqual($expectedEvents, $actualEvents);
139
            }
140
141
            $actualEvent = $actualEvents[$k];
142
143
            $this->assertEquals($this->hashEvent($expectedEvent), $this->hashEvent($actualEvent), "Wrong event #{$k} of class " . get_class($expectedEvent) . " emitted");
144
        }
145
146 View Code Duplication
        foreach ($actualEvents as $k => $actualEvent) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
147
            if (!isset($expectedEvents[$k])) {
148
                $this->fail("Actual event #$k fired when it should't (should have class " . get_class($actualEvent) . ")");
149
                $this->assertEventsCountAreEqual($expectedEvents, $actualEvents);
150
            }
151
152
            $expectedEvent = $expectedEvents[$k];
153
154
            $this->assertEquals($this->hashEvent($expectedEvent), $this->hashEvent($actualEvent), "Wrong event #{$k} of class " . get_class($expectedEvent) . " emitted");
155
        }
156
157
    }
158
159
    protected function hashEvents(array $events)
160
    {
161
        return array_map([$this, 'hashEvent'], $events);
162
    }
163
164
    protected function hashEvent($event)
165
    {
166
        if (null === $event) {
167
            $this->fail("No event emitted!");
168
        }
169
170
        return array_merge(['___class' => get_class($event)], (array)($event));
171
    }
172
173
    /**
174
     * @return \Gica\Cqrs\Event\EventDispatcher
175
     */
176
    public function getEventDispatcher(): \Gica\Cqrs\Event\EventDispatcher
177
    {
178
        return $this->eventDispatcher;
179
    }
180
181
    public function getAggregateId()
182
    {
183
        return $this->aggregateId;
184
    }
185
186
    protected function assertEventsCountAreEqual(array $expectedEvents, array $actualEvents): void
187
    {
188
        $expectedCount = count($expectedEvents);
189
        $actualCount = count($actualEvents);
190
        $this->assertEquals($expectedCount, $actualCount, sprintf("%d number of events were expected but %d number of events were generated", $expectedCount, $actualCount));
191
    }
192
193
    private function factoryMetaData(): Event\MetaData
194
    {
195
        return new \Gica\Cqrs\Event\MetaData(
196
            $this->aggregateId, get_class($this->aggregate), new \DateTimeImmutable(), new Guid()
197
        );
198
    }
199
}