Skip to content

Commit

Permalink
long running process doctrine connection listener
Browse files Browse the repository at this point in the history
  • Loading branch information
alli83 committed Dec 26, 2023
1 parent 7290b3a commit 88471c3
Show file tree
Hide file tree
Showing 3 changed files with 149 additions and 0 deletions.
4 changes: 4 additions & 0 deletions DependencyInjection/DoctrineExtension.php
Expand Up @@ -532,6 +532,10 @@ protected function ormLoad(array $config, ContainerBuilder $container)
$container->removeDefinition('doctrine.mapping_import_command');
}

if (!$container->hasParameter('kernel.runtime_mode') || !$container->hasParameter('kernel.runtime_mode.worker')) {
$container->removeDefinition('doctrine.orm.listeners.doctrine_connection_listener');
}

$entityManagers = [];
foreach (array_keys($config['entity_managers']) as $name) {
/** @psalm-suppress InvalidArrayOffset */
Expand Down
6 changes: 6 additions & 0 deletions Resources/config/orm.xml
Expand Up @@ -273,5 +273,11 @@

<tag name="console.command" command="doctrine:mapping:import" />
</service>
<service id="doctrine.orm.listeners.doctrine_connection_listener" class="Symfony\Bridge\Doctrine\Listener\DoctrineConnectionListener">
<argument type="service" id="doctrine" />
<argument type="service" id="service_container" />
<argument type="service" id="event_dispatcher" />
<tag name="kernel.event_subscriber" />
</service>
</services>
</container>
139 changes: 139 additions & 0 deletions Tests/DoctrineConnectionListenerTest.php
@@ -0,0 +1,139 @@
<?php

declare(strict_types=1);

namespace Doctrine\Bundle\DoctrineBundle\Tests;

use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Exception as DBALException;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\Persistence\ManagerRegistry;
use PHPUnit\Framework\TestCase;
use ProxyManager\Proxy\LazyLoadingInterface;
use Symfony\Bridge\Doctrine\Event\ForceKernelRebootEvent;
use Symfony\Bridge\Doctrine\Listener\DoctrineConnectionListener;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\HttpKernel\Event\RequestEvent;

/**
* Based on https://github.com/Baldinof/roadrunner-bundle/blob/3.x/src/Integration/Doctrine/DoctrineORMMiddleware.php
*/
class DoctrineConnectionListenerTest extends TestCase
{
public const CONNECTION_NAME = 'doctrine.connection';
public const MANAGER_NAME = 'doctrine.manager';

private $managerRegistryMock;

Check failure on line 28 in Tests/DoctrineConnectionListenerTest.php

View workflow job for this annotation

GitHub Actions / Coding Standards / Coding Standards (8.2)

Property \Doctrine\Bundle\DoctrineBundle\Tests\DoctrineConnectionListenerTest::$managerRegistryMock does not have native type hint nor @var annotation for its value.
private $connectionMock;

Check failure on line 29 in Tests/DoctrineConnectionListenerTest.php

View workflow job for this annotation

GitHub Actions / Coding Standards / Coding Standards (8.2)

Property \Doctrine\Bundle\DoctrineBundle\Tests\DoctrineConnectionListenerTest::$connectionMock does not have native type hint nor @var annotation for its value.
private $container;

Check failure on line 30 in Tests/DoctrineConnectionListenerTest.php

View workflow job for this annotation

GitHub Actions / Coding Standards / Coding Standards (8.2)

Property \Doctrine\Bundle\DoctrineBundle\Tests\DoctrineConnectionListenerTest::$container does not have native type hint nor @var annotation for its value.
private $dispatcher;

Check failure on line 31 in Tests/DoctrineConnectionListenerTest.php

View workflow job for this annotation

GitHub Actions / Coding Standards / Coding Standards (8.2)

Property \Doctrine\Bundle\DoctrineBundle\Tests\DoctrineConnectionListenerTest::$dispatcher does not have native type hint nor @var annotation for its value.
private $listener;

Check failure on line 32 in Tests/DoctrineConnectionListenerTest.php

View workflow job for this annotation

GitHub Actions / Coding Standards / Coding Standards (8.2)

Property \Doctrine\Bundle\DoctrineBundle\Tests\DoctrineConnectionListenerTest::$listener does not have native type hint nor @var annotation for its value.
private $event;

Check failure on line 33 in Tests/DoctrineConnectionListenerTest.php

View workflow job for this annotation

GitHub Actions / Coding Standards / Coding Standards (8.2)

Property \Doctrine\Bundle\DoctrineBundle\Tests\DoctrineConnectionListenerTest::$event does not have native type hint nor @var annotation for its value.

public function setUp(): void
{
$platform = $this->createMock(AbstractPlatform::class);
$platform->method('getDummySelectSQL')->willReturn('SELECT 1');

$this->managerRegistryMock = $this->createMock(ManagerRegistry::class);
$this->connectionMock = $this->createMock(Connection::class);

Check failure on line 41 in Tests/DoctrineConnectionListenerTest.php

View workflow job for this annotation

GitHub Actions / Coding Standards / Coding Standards (8.2)

Equals sign not aligned with surrounding assignments; expected 6 spaces but found 1 space
$this->connectionMock->method('getDatabasePlatform')->willReturn($platform);

$this->container = new Container();
$this->container->set(self::CONNECTION_NAME, $this->connectionMock);

$this->managerRegistryMock->method('getConnectionNames')->willReturn([self::CONNECTION_NAME]);
$this->managerRegistryMock->method('getManagerNames')->willReturn([self::MANAGER_NAME]);

$this->dispatcher = new EventDispatcher();
$this->event = $this->createMock(RequestEvent::class);

Check failure on line 51 in Tests/DoctrineConnectionListenerTest.php

View workflow job for this annotation

GitHub Actions / Coding Standards / Coding Standards (8.2)

Equals sign not aligned with surrounding assignments; expected 6 spaces but found 1 space
$this->listener = new DoctrineConnectionListener($this->managerRegistryMock, $this->container, $this->dispatcher);

Check failure on line 52 in Tests/DoctrineConnectionListenerTest.php

View workflow job for this annotation

GitHub Actions / Static Analysis with Psalm

UndefinedClass

Tests/DoctrineConnectionListenerTest.php:52:31: UndefinedClass: Class, interface or enum named Symfony\Bridge\Doctrine\Listener\DoctrineConnectionListener does not exist (see https://psalm.dev/019)

Check failure on line 52 in Tests/DoctrineConnectionListenerTest.php

View workflow job for this annotation

GitHub Actions / Coding Standards / Coding Standards (8.2)

Equals sign not aligned with surrounding assignments; expected 3 spaces but found 1 space
}

public function testSkipNotInitializedConnections()
{
$this->container->set(self::CONNECTION_NAME, null);

$this->connectionMock->expects($this->never())->method('isConnected');
$this->connectionMock->expects($this->never())->method('executeQuery');
$this->connectionMock->expects($this->never())->method('close');
$this->connectionMock->expects($this->never())->method('connect');

$this->listener->onKernelRequest($this->event);
}

public function testSkipWhenNotConnected(): void
{
$this->connectionMock->method('isConnected')->willReturn(false);
$this->connectionMock->expects($this->never())->method('executeQuery');
$this->connectionMock->expects($this->never())->method('close');
$this->connectionMock->expects($this->never())->method('connect');

$this->listener->onKernelRequest($this->event);
}

public function testItClosesNotPingableConnection(): void
{
$this->connectionMock->expects($this->once())->method('executeQuery')->will($this->throwException(new DBALException()));

$this->connectionMock->method('isConnected')->willReturn(true);
$this->connectionMock->expects($this->once())->method('close');
$this->connectionMock->expects($this->never())->method('connect');

$this->listener->onKernelRequest($this->event);
}

public function testItDoesNotClosePingableConnection(): void
{
$this->connectionMock->expects($this->once())->method('executeQuery');
$this->connectionMock->method('isConnected')->willReturn(true);
$this->connectionMock->expects($this->never())->method('close');
$this->connectionMock->expects($this->never())->method('connect');

$this->listener->onKernelRequest($this->event);
}

public function testItForcesRebootOnClosedManagerWhenMissingProxySupport()
{
$rebootForced = false;

$this->dispatcher->addListener(ForceKernelRebootEvent::class, function () use (&$rebootForced) {

Check failure on line 102 in Tests/DoctrineConnectionListenerTest.php

View workflow job for this annotation

GitHub Actions / Static Analysis with Psalm

UndefinedClass

Tests/DoctrineConnectionListenerTest.php:102:40: UndefinedClass: Class, interface or enum named Symfony\Bridge\Doctrine\Event\ForceKernelRebootEvent does not exist (see https://psalm.dev/019)

Check failure on line 102 in Tests/DoctrineConnectionListenerTest.php

View workflow job for this annotation

GitHub Actions / Coding Standards / Coding Standards (8.2)

Closure not using "$this" should be declared static.
$rebootForced = true;
});

$manager = $this->createMock(EntityManagerInterface::class);
$manager->expects($this->once())->method('isOpen')->willReturn(false);

$this->container->set(self::MANAGER_NAME, $manager);

$this->listener->onKernelRequest($this->event);

$this->assertTrue($rebootForced, 'A ForceKernelRebootEvent should have been dispatched');
}

public function testItSkipsLazyEntityManagers()
{
$rebootForced = false;

$this->dispatcher->addListener(ForceKernelRebootEvent::class, function () use (&$rebootForced) {

Check failure on line 120 in Tests/DoctrineConnectionListenerTest.php

View workflow job for this annotation

GitHub Actions / Static Analysis with Psalm

UndefinedClass

Tests/DoctrineConnectionListenerTest.php:120:40: UndefinedClass: Class, interface or enum named Symfony\Bridge\Doctrine\Event\ForceKernelRebootEvent does not exist (see https://psalm.dev/019)
$rebootForced = true;
});

$manager = $this->createMock(LazyEntityManager::class);
$manager->expects($this->never())->method('isOpen');

$this->container->set(self::MANAGER_NAME, $manager);

$this->listener->onKernelRequest($this->event);
$this->assertFalse($rebootForced, 'A ForceKernelRebootEvent should not have been dispatched');
}
}

/**
* @internal Allow mock of multiple interface
*/
interface LazyEntityManager extends EntityManagerInterface, LazyLoadingInterface

Check failure on line 137 in Tests/DoctrineConnectionListenerTest.php

View workflow job for this annotation

GitHub Actions / Static Analysis with Psalm

MissingTemplateParam

Tests/DoctrineConnectionListenerTest.php:137:61: MissingTemplateParam: Doctrine\Bundle\DoctrineBundle\Tests\LazyEntityManager has missing template params when extending ProxyManager\Proxy\LazyLoadingInterface, expecting 1 (see https://psalm.dev/182)
{
}

0 comments on commit 88471c3

Please sign in to comment.