1
|
|
|
<?php |
|
|
|
|
2
|
|
|
|
3
|
|
|
namespace Governor\Tests\Plugin\SymfonyBundle; |
4
|
|
|
|
5
|
|
|
use Governor\Bundle\GovernorBundle\DependencyInjection\Compiler\AggregateCommandHandlerPass; |
6
|
|
|
use Governor\Framework\CommandHandling\Distributed\AnnotationRoutingStrategy; |
7
|
|
|
use Governor\Framework\CommandHandling\Distributed\DistributedCommandBus; |
8
|
|
|
use Governor\Framework\CommandHandling\Distributed\MetaDataRoutingStrategy; |
9
|
|
|
use Psr\Log\LoggerInterface; |
10
|
|
|
use Governor\Framework\CommandHandling\CommandBusInterface; |
11
|
|
|
use Governor\Framework\CommandHandling\CommandHandlerInterface; |
12
|
|
|
use Governor\Framework\CommandHandling\GenericCommandMessage; |
13
|
|
|
use Governor\Framework\CommandHandling\NoHandlerForCommandException; |
14
|
|
|
use Governor\Framework\Domain\AbstractAggregateRoot; |
15
|
|
|
use Governor\Framework\Domain\IdentifierFactory; |
16
|
|
|
use Governor\Framework\EventSourcing\Annotation\AbstractAnnotatedAggregateRoot; |
17
|
|
|
use Symfony\Component\DependencyInjection\ContainerInterface; |
18
|
|
|
use Symfony\Component\DependencyInjection\ContainerBuilder; |
19
|
|
|
use Symfony\Component\DependencyInjection\Definition; |
20
|
|
|
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; |
21
|
|
|
use Symfony\Component\DependencyInjection\Compiler\PassConfig; |
22
|
|
|
use Governor\Bundle\GovernorBundle\DependencyInjection\GovernorFrameworkExtension; |
23
|
|
|
use Governor\Bundle\GovernorBundle\DependencyInjection\Compiler\CommandHandlerPass; |
24
|
|
|
use Governor\Bundle\GovernorBundle\DependencyInjection\Compiler\EventHandlerPass; |
25
|
|
|
use Governor\Framework\Repository\RepositoryInterface; |
26
|
|
|
use Governor\Framework\Annotations\EventHandler; |
27
|
|
|
use Governor\Framework\Annotations\CommandHandler; |
28
|
|
|
use Symfony\Bridge\ProxyManager\LazyProxy\Instantiator\RuntimeInstantiator; |
29
|
|
|
use Governor\Framework\EventHandling\EventBusInterface; |
30
|
|
|
use Governor\Framework\EventHandling\EventListenerInterface; |
31
|
|
|
use Governor\Framework\CommandHandling\SimpleCommandBus; |
32
|
|
|
use Governor\Framework\EventHandling\SimpleEventBus; |
33
|
|
|
use Governor\Framework\CommandHandling\InMemoryCommandHandlerRegistry; |
34
|
|
|
use Governor\Framework\EventHandling\InMemoryEventListenerRegistry; |
35
|
|
|
use Symfony\Component\Stopwatch\Stopwatch; |
36
|
|
|
use Governor\Framework\EventHandling\Amqp\AmqpTerminal; |
37
|
|
|
|
38
|
|
|
$loader = require __DIR__."/../../vendor/autoload.php"; |
39
|
|
|
$loader->add('Governor', __DIR__); |
40
|
|
|
|
41
|
|
|
\Doctrine\Common\Annotations\AnnotationRegistry::registerLoader(array($loader, 'loadClass')); |
42
|
|
|
|
43
|
|
|
class GovernorFrameworkExtensionTest extends \PHPUnit_Framework_TestCase |
44
|
|
|
{ |
45
|
|
|
/** |
46
|
|
|
* @var ContainerInterface |
47
|
|
|
*/ |
48
|
|
|
private $testSubject; |
49
|
|
|
|
50
|
|
|
public function setUp() |
51
|
|
|
{ |
52
|
|
|
$this->testSubject = $this->createTestContainer(); |
53
|
|
|
} |
54
|
|
|
|
55
|
|
|
public function testRepositories() |
56
|
|
|
{ |
57
|
|
|
$repo1 = $this->testSubject->get('dummy1.repository'); |
58
|
|
|
$repo2 = $this->testSubject->get('dummy2.repository'); |
59
|
|
|
|
60
|
|
|
$this->assertInstanceOf(RepositoryInterface::class, $repo1); |
61
|
|
|
$this->assertInstanceOf(RepositoryInterface::class, $repo2); |
62
|
|
|
$this->assertNotSame($repo1, $repo2); |
63
|
|
|
$this->assertEquals( |
64
|
|
|
Dummy1Aggregate::class, |
65
|
|
|
$repo1->getClass() |
66
|
|
|
); |
67
|
|
|
$this->assertEquals( |
68
|
|
|
Dummy2Aggregate::class, |
69
|
|
|
$repo2->getClass() |
70
|
|
|
); |
71
|
|
|
} |
72
|
|
|
|
73
|
|
|
public function testCommandHandlers() |
74
|
|
|
{ |
75
|
|
|
/** @var CommandBusInterface $commandBus */ |
76
|
|
|
$commandBus = $this->testSubject->get('governor.command_bus.default'); |
77
|
|
|
$this->assertInstanceOf(CommandBusInterface::class, $commandBus); |
78
|
|
|
|
79
|
|
|
$handler = $commandBus->findCommandHandlerFor(GenericCommandMessage::asCommandMessage(new ContainerCommand1())); |
|
|
|
|
80
|
|
|
$this->assertInstanceOf(CommandHandlerInterface::class, $handler); |
81
|
|
|
|
82
|
|
|
try { |
83
|
|
|
$commandBus->findCommandHandlerFor(GenericCommandMessage::asCommandMessage(new \stdClass())); |
|
|
|
|
84
|
|
|
$this->fail('NoHandlerForCommandException expected'); |
85
|
|
|
} catch (NoHandlerForCommandException $ex) { |
|
|
|
|
86
|
|
|
|
87
|
|
|
} |
88
|
|
|
} |
89
|
|
|
|
90
|
|
|
public function testEventHandlers() |
91
|
|
|
{ |
92
|
|
|
/** @var EventBusInterface $eventBus */ |
93
|
|
|
$eventBus = $this->testSubject->get('governor.event_bus.default'); |
94
|
|
|
|
95
|
|
|
$this->assertInstanceOf(EventBusInterface::class, $eventBus); |
96
|
|
|
|
97
|
|
|
$registry = $eventBus->getEventListenerRegistry(); |
98
|
|
|
$this->assertInstanceOf(InMemoryEventListenerRegistry::class, $registry); |
99
|
|
|
|
100
|
|
|
$listeners = $registry->getListeners(); |
101
|
|
|
|
102
|
|
|
$this->assertCount(2, $listeners); |
103
|
|
|
$this->assertContainsOnlyInstancesOf(EventListenerInterface::class, $listeners); |
104
|
|
|
} |
105
|
|
|
|
106
|
|
View Code Duplication |
public function testEventHandlerLazyLoading() |
|
|
|
|
107
|
|
|
{ |
108
|
|
|
foreach ($this->testSubject->getServiceIds() as $id) { |
109
|
|
|
if (preg_match('/^governor.event_handler.*/', $id)) { |
110
|
|
|
$def = $this->testSubject->getDefinition($id); |
|
|
|
|
111
|
|
|
$this->assertTrue($def->isLazy()); |
112
|
|
|
} |
113
|
|
|
} |
114
|
|
|
} |
115
|
|
|
|
116
|
|
View Code Duplication |
public function testCommandHandlerLazyLoading() |
|
|
|
|
117
|
|
|
{ |
118
|
|
|
foreach ($this->testSubject->getServiceIds() as $id) { |
119
|
|
|
if (preg_match('/^governor.command_handler.*/', $id) || preg_match( |
120
|
|
|
'/^governor.aggregate_command_handler.*/', |
121
|
|
|
$id |
122
|
|
|
) |
123
|
|
|
) { |
124
|
|
|
$def = $this->testSubject->getDefinition($id); |
|
|
|
|
125
|
|
|
$this->assertTrue($def->isLazy()); |
126
|
|
|
} |
127
|
|
|
} |
128
|
|
|
} |
129
|
|
|
|
130
|
|
|
public function testIdentifierFactory() |
131
|
|
|
{ |
132
|
|
|
$factory = $this->testSubject->get('governor.identifier_factory'); |
133
|
|
|
|
134
|
|
|
$this->assertInstanceOf(IdentifierFactory::class, $factory); |
135
|
|
|
} |
136
|
|
|
|
137
|
|
|
public function testAggregates() |
138
|
|
|
{ |
139
|
|
|
$aggregates = $this->testSubject->getParameter('governor.aggregates'); |
140
|
|
|
$this->assertCount(2, $aggregates); |
141
|
|
|
|
142
|
|
|
$this->assertTrue($aggregates['dummy1']['handler']); |
143
|
|
|
$this->assertFalse($aggregates['dummy2']['handler']); |
144
|
|
|
|
145
|
|
|
$this->assertEquals('default', $aggregates['dummy1']['command_bus']); |
146
|
|
|
$this->assertEquals('third', $aggregates['dummy2']['command_bus']); |
147
|
|
|
|
148
|
|
|
$count = 0; |
149
|
|
|
|
150
|
|
|
foreach ($this->testSubject->getServiceIds() as $id) { |
151
|
|
|
if (preg_match('/^governor.aggregate_command_handler.*/', $id)) { |
152
|
|
|
$def = $this->testSubject->getDefinition($id); |
|
|
|
|
153
|
|
|
|
154
|
|
|
$this->assertEquals(Dummy1Aggregate::class, $def->getArgument(0)); |
155
|
|
|
$count++;; |
156
|
|
|
} |
157
|
|
|
} |
158
|
|
|
|
159
|
|
|
$this->assertEquals(1, $count); |
160
|
|
|
} |
161
|
|
|
|
162
|
|
|
public function testAmqp() |
163
|
|
|
{ |
164
|
|
|
/** @var AmqpTerminal $terminal */ |
165
|
|
|
$terminal = $this->testSubject->get('governor.terminal.amqp.default'); |
166
|
|
|
$this->assertInstanceOf(AmqpTerminal::class, $terminal); |
167
|
|
|
|
168
|
|
|
$connection = $this->testSubject->get('governor.amqp.connection.default'); |
|
|
|
|
169
|
|
|
} |
170
|
|
|
|
171
|
|
|
public function testEventBuses() |
172
|
|
|
{ |
173
|
|
|
/** @var EventBusInterface $first */ |
174
|
|
|
$first = $this->testSubject->get('governor.event_bus.default'); |
175
|
|
|
/** @var EventBusInterface $second */ |
176
|
|
|
$second = $this->testSubject->get('governor.event_bus.second'); |
177
|
|
|
|
178
|
|
|
$this->assertInstanceOf(EventBusInterface::class, $first); |
179
|
|
|
$this->assertInstanceOf(EventBusInterface::class, $second); |
180
|
|
|
} |
181
|
|
|
|
182
|
|
|
public function testRoutingStrategies() |
183
|
|
|
{ |
184
|
|
|
/** @var MetaDataRoutingStrategy $default */ |
185
|
|
|
$default = $this->testSubject->get('governor.routing_strategy.default'); |
186
|
|
|
$this->assertInstanceOf(MetaDataRoutingStrategy::class, $default); |
187
|
|
|
|
188
|
|
|
/** @var AnnotationRoutingStrategy $annotated */ |
189
|
|
|
$annotated = $this->testSubject->get('governor.routing_strategy.annotated'); |
190
|
|
|
$this->assertInstanceOf(AnnotationRoutingStrategy::class, $annotated); |
191
|
|
|
} |
192
|
|
|
|
193
|
|
|
public function testCommandBuses() |
194
|
|
|
{ |
195
|
|
|
/** @var SimpleCommandBus $simple */ |
196
|
|
|
$simple = $this->testSubject->get('governor.command_bus.default'); |
197
|
|
|
$this->assertInstanceOf(SimpleCommandBus::class, $simple); |
198
|
|
|
|
199
|
|
|
/** @var DistributedCommandBus $distributed */ |
200
|
|
|
$distributed = $this->testSubject->get('governor.command_bus.distributed'); |
201
|
|
|
$this->assertInstanceOf(DistributedCommandBus::class, $distributed); |
202
|
|
|
} |
203
|
|
|
|
204
|
|
|
public function createTestContainer() |
205
|
|
|
{ |
206
|
|
|
|
207
|
|
|
$config = [ |
208
|
|
|
'governor' => [ |
209
|
|
|
'node_name' => 'test-01', |
210
|
|
|
'terminals' => [ |
211
|
|
|
'amqp' => [ |
212
|
|
|
'default' => [ |
213
|
|
|
] |
214
|
|
|
] |
215
|
|
|
], |
216
|
|
|
'connectors' => [ |
217
|
|
|
'redis' => [ |
218
|
|
|
'default' => [ |
219
|
|
|
'routing_strategy' => 'default', |
220
|
|
|
'local_segment' => 'default' |
221
|
|
|
] |
222
|
|
|
] |
223
|
|
|
], |
224
|
|
|
'routing_strategies' => [ |
225
|
|
|
'default' => [ |
226
|
|
|
'type' => 'metadata', |
227
|
|
|
'parameters' => [ |
228
|
|
|
'key_name' => 'aggregateIdentifier' |
229
|
|
|
] |
230
|
|
|
], |
231
|
|
|
'annotated' => [ |
232
|
|
|
'type' => 'annotation' |
233
|
|
|
] |
234
|
|
|
], |
235
|
|
|
'annotation_reader' => [ |
236
|
|
|
'type' => 'file_cache', |
237
|
|
|
'parameters' => [ |
238
|
|
|
'debug' => false, |
239
|
|
|
'path' => '%kernel.cache_dir%' |
240
|
|
|
] |
241
|
|
|
], |
242
|
|
|
'aggregates' => [ |
243
|
|
|
'dummy1' => [ |
244
|
|
|
'class' => Dummy1Aggregate::class, |
245
|
|
|
'repository' => 'event_sourcing', |
246
|
|
|
'handler' => true |
247
|
|
|
], |
248
|
|
|
'dummy2' => [ |
249
|
|
|
'class' => Dummy2Aggregate::class, |
250
|
|
|
'repository' => 'hybrid', |
251
|
|
|
'handler' => false, |
252
|
|
|
'command_bus' => 'third' |
253
|
|
|
] |
254
|
|
|
], |
255
|
|
|
'command_buses' => [ |
256
|
|
|
'default' => [ |
257
|
|
|
'type' => 'simple' |
258
|
|
|
], |
259
|
|
|
'distributed' => [ |
260
|
|
|
'type' => 'distributed', |
261
|
|
|
'connector' => 'default', |
262
|
|
|
'routing_strategy' => 'default' |
263
|
|
|
], |
264
|
|
|
'third' => [ |
265
|
|
|
'type' => 'simple' |
266
|
|
|
], |
267
|
|
|
], |
268
|
|
|
'event_buses' => [ |
269
|
|
|
'default' => [ |
270
|
|
|
'class' => SimpleEventBus::class, |
271
|
|
|
'registry' => 'governor.event_bus_registry.in_memory', |
272
|
|
|
'terminals' => [ |
273
|
|
|
'governor.terminal.amqp.default' |
274
|
|
|
] |
275
|
|
|
], |
276
|
|
|
'second' => [ |
277
|
|
|
'class' => SimpleEventBus::class, |
278
|
|
|
'registry' => 'governor.event_bus_registry.in_memory' |
279
|
|
|
] |
280
|
|
|
], |
281
|
|
|
'event_store' => [ |
282
|
|
|
'mongo_templates' => [ |
283
|
|
|
'default' => [ |
284
|
|
|
'server' => 'mongodb://localhost:27017', |
285
|
|
|
'database' => 'test' |
286
|
|
|
] |
287
|
|
|
], |
288
|
|
|
'type' => 'mongo', |
289
|
|
|
'parameters' => [ |
290
|
|
|
|
291
|
|
|
] |
292
|
|
|
], |
293
|
|
|
'command_gateways' => [ |
294
|
|
|
'default' => [ |
295
|
|
|
'class' => 'Governor\Framework\CommandHandling\Gateway\DefaultCommandGateway' |
296
|
|
|
] |
297
|
|
|
], |
298
|
|
|
'saga_repository' => [ |
299
|
|
|
'mongo_templates' => [ |
300
|
|
|
'default' => [ |
301
|
|
|
'server' => 'mongodb://localhost:27017', |
302
|
|
|
'database' => 'test2' |
303
|
|
|
] |
304
|
|
|
], |
305
|
|
|
'type' => 'mongo', |
306
|
|
|
'parameters' => [ |
307
|
|
|
] |
308
|
|
|
], |
309
|
|
|
'saga_manager' => [ |
310
|
|
|
'type' => 'annotation', |
311
|
|
|
'saga_locations' => [ |
312
|
|
|
sys_get_temp_dir() |
313
|
|
|
] |
314
|
|
|
], |
315
|
|
|
'order_resolver' => 'annotation' |
316
|
|
|
] |
317
|
|
|
]; |
318
|
|
|
|
319
|
|
|
$container = new ContainerBuilder( |
320
|
|
|
new ParameterBag( |
321
|
|
|
[ |
322
|
|
|
'kernel.debug' => false, |
323
|
|
|
'kernel.bundles' => [], |
324
|
|
|
'kernel.cache_dir' => sys_get_temp_dir(), |
325
|
|
|
'kernel.environment' => 'test', |
326
|
|
|
'kernel.root_dir' => __DIR__.'/../../../../' // src dir |
327
|
|
|
] |
328
|
|
|
) |
329
|
|
|
); |
330
|
|
|
|
331
|
|
|
$loader = new GovernorFrameworkExtension(); |
332
|
|
|
|
333
|
|
|
$container->setProxyInstantiator(new RuntimeInstantiator()); |
334
|
|
|
|
335
|
|
|
$container->registerExtension($loader); |
336
|
|
|
$container->set( |
337
|
|
|
'doctrine.orm.default_entity_manager', |
338
|
|
|
$this->getMock( |
339
|
|
|
\Doctrine\ORM\EntityManager::class, |
340
|
|
|
[ |
341
|
|
|
'find', |
342
|
|
|
'flush', |
343
|
|
|
'persist', |
344
|
|
|
'remove' |
345
|
|
|
], |
346
|
|
|
[], |
347
|
|
|
'', |
348
|
|
|
false |
349
|
|
|
) |
350
|
|
|
); |
351
|
|
|
|
352
|
|
|
$container->set('logger', $this->getMock(LoggerInterface::class)); |
353
|
|
|
$container->set('debug.stopwatch', $this->getMock(Stopwatch::class)); |
354
|
|
|
$container->set('jms_serializer', $this->getMock(\JMS\Serializer\SerializerInterface::class)); |
355
|
|
|
$container->set('validator', $this->getMock(\Symfony\Component\Validator\ValidatorInterface::class)); |
356
|
|
|
|
357
|
|
|
$this->addTaggedCommandHandlers($container); |
358
|
|
|
$this->addTaggedEventListeners($container); |
359
|
|
|
|
360
|
|
|
$loader->load($config, $container); |
361
|
|
|
|
362
|
|
|
$container->addCompilerPass(new CommandHandlerPass(), PassConfig::TYPE_BEFORE_REMOVING); |
363
|
|
|
$container->addCompilerPass(new EventHandlerPass(), PassConfig::TYPE_BEFORE_REMOVING); |
364
|
|
|
$container->addCompilerPass(new AggregateCommandHandlerPass(), PassConfig::TYPE_BEFORE_REMOVING); |
365
|
|
|
|
366
|
|
|
$container->compile(); |
367
|
|
|
|
368
|
|
|
return $container; |
369
|
|
|
} |
370
|
|
|
|
371
|
|
View Code Duplication |
private function addTaggedCommandHandlers(ContainerBuilder $container) |
|
|
|
|
372
|
|
|
{ |
373
|
|
|
$definition = new Definition(ContainerCommandHandler1::class); |
374
|
|
|
$definition->addTag('governor.command_handler') |
375
|
|
|
->setPublic(true); |
376
|
|
|
|
377
|
|
|
$container->setDefinition('test.command_handler', $definition); |
378
|
|
|
} |
379
|
|
|
|
380
|
|
View Code Duplication |
private function addTaggedEventListeners(ContainerBuilder $container) |
|
|
|
|
381
|
|
|
{ |
382
|
|
|
$definition = new Definition(ContainerEventListener1::class); |
383
|
|
|
$definition->addTag('governor.event_handler') |
384
|
|
|
->setPublic(true); |
385
|
|
|
|
386
|
|
|
$container->setDefinition('test.event_handler', $definition); |
387
|
|
|
} |
388
|
|
|
|
389
|
|
|
} |
390
|
|
|
|
391
|
|
|
class Dummy1Aggregate extends AbstractAnnotatedAggregateRoot |
|
|
|
|
392
|
|
|
{ |
393
|
|
|
|
394
|
|
|
public function getIdentifier() |
395
|
|
|
{ |
396
|
|
|
// TODO: Implement getIdentifier() method. |
397
|
|
|
} |
398
|
|
|
|
399
|
|
|
/** |
400
|
|
|
* @CommandHandler() |
401
|
|
|
* @param ContainerCommand2 $command |
402
|
|
|
*/ |
403
|
|
|
public function doSomething(ContainerCommand2 $command) |
|
|
|
|
404
|
|
|
{ |
405
|
|
|
|
406
|
|
|
} |
407
|
|
|
|
408
|
|
|
} |
409
|
|
|
|
410
|
|
|
class Dummy2Aggregate extends AbstractAggregateRoot |
|
|
|
|
411
|
|
|
{ |
412
|
|
|
|
413
|
|
|
public function getIdentifier() |
414
|
|
|
{ |
415
|
|
|
// TODO: Implement getIdentifier() method. |
416
|
|
|
} |
417
|
|
|
|
418
|
|
|
} |
419
|
|
|
|
420
|
|
|
class ContainerCommand1 |
|
|
|
|
421
|
|
|
{ |
422
|
|
|
|
423
|
|
|
} |
424
|
|
|
|
425
|
|
|
class ContainerCommand2 |
|
|
|
|
426
|
|
|
{ |
427
|
|
|
|
428
|
|
|
} |
429
|
|
|
|
430
|
|
|
class ContainerEvent1 |
|
|
|
|
431
|
|
|
{ |
432
|
|
|
|
433
|
|
|
} |
434
|
|
|
|
435
|
|
|
class ContainerCommandHandler1 |
|
|
|
|
436
|
|
|
{ |
437
|
|
|
|
438
|
|
|
/** |
439
|
|
|
* @param ContainerCommand1 $command |
440
|
|
|
* @CommandHandler |
441
|
|
|
*/ |
442
|
|
|
public function onCommand1(ContainerCommand1 $command) |
|
|
|
|
443
|
|
|
{ |
444
|
|
|
|
445
|
|
|
} |
446
|
|
|
|
447
|
|
|
} |
448
|
|
|
|
449
|
|
|
class ContainerEventListener1 |
|
|
|
|
450
|
|
|
{ |
451
|
|
|
|
452
|
|
|
/** |
453
|
|
|
* @EventHandler |
454
|
|
|
*/ |
455
|
|
|
public function onEvent1(ContainerEvent1 $event) |
|
|
|
|
456
|
|
|
{ |
457
|
|
|
|
458
|
|
|
} |
459
|
|
|
|
460
|
|
|
} |
461
|
|
|
|
The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.
The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.
To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.