Skip to content

Commit

Permalink
Added unit tests and completed README
Browse files Browse the repository at this point in the history
  • Loading branch information
kreemer committed Aug 9, 2023
1 parent b2b9cde commit 78be17a
Show file tree
Hide file tree
Showing 6 changed files with 322 additions and 41 deletions.
35 changes: 34 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

<img src="https://github.com/SymSensor/ActuatorDoctrineBundle/blob/main/docs/logo.png?raw=true" align="right" width="250"/>

ActuatorDoctrineBundle extends [ActuatorBundle](https://github.com/SymSensor/ActuatorBundle) by providing health indicator and information collector for doctrine.

## Installation

Make sure Composer is installed globally, as explained in the
Expand Down Expand Up @@ -39,4 +41,35 @@ return [
// ...
SymSensor\ActuatorBundle\SymSensorActuatorDoctrineBundle::class => ['all' => true],
];
```
```


## Configuration

The Bundle can be configured with a configuration file named `config/packages/sym_sensor_actuator.yaml`. Following snippet shows the default value for all configurations:

```yaml
sym_sensor_actuator_doctrine:
connections:
default:
service: doctrine.dbal.default_connection
check_sql: SELECT 1
```
Following table outlines the configuration:
| key | default | description |
| --------------------------------------------------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| sym_sensor_actuator_doctrine.connections | Array | Contains a list of names, where each represents an connection to e database. The name itself can be chosen at will |
| sym_sensor_actuator_doctrine.connections.`name`.enabled | true | If the connection associated with this name should monitored |
| sym_sensor_actuator_doctrine.connections.`name`.service | 'Doctrine\DBAL\Connection' | The service name inside the dependency injection container. You can lookup your connection name with `bin/console debug:container` |
| sym_sensor_actuator_doctrine.connections.`name`.check_sql | 'Select 1' | The SQL which will be executed to determine if the database is up. The response will be ignored, it only matters if the sql can be executed without error. If you set this to `~` it will only check if a connection to the database can be established |


## License

ActuatorBundle is released under the MIT Licence. See the bundled LICENSE file for details.

## Author

Originally developed by [Arkadiusz Kondas](https://twitter.com/ArkadiuszKondas)
5 changes: 0 additions & 5 deletions config/doctrine_info.yaml

This file was deleted.

File renamed without changes.
42 changes: 7 additions & 35 deletions src/DependencyInjection/SymSensorActuatorDoctrineExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,26 +30,16 @@ public function load(array $configs, ContainerBuilder $container): void
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);

$this->processHealthConfiguration($config, $container);
$this->processInfoConfiguration($config, $container);
}

/**
* @param mixed[] $config
*/
private function processHealthConfiguration(array $config, ContainerBuilder $container): void
{
if (
$container->willBeAvailable('doctrine/doctrine-bundle', Connection::class, [])
&& $this->isConfigEnabled($container, $config)
) {
$loader = new YamlFileLoader($container, new FileLocator(__DIR__.'/../../config'));
$loader->load('doctrine_health.yaml');

$definition = $container->getDefinition(\SymSensor\ActuatorDoctrineBundle\Service\Health\Indicator\Doctrine::class);
$loader->load('services.yaml');

if (\is_array($config['connections'])) {
if (isset($config['connections']) && \is_array($config['connections'])) {
$constructorArgument = [];
$infoArgument = [];
foreach ($config['connections'] as $name => $connection) {
if (!\is_array($connection)) {
continue;
Expand All @@ -59,32 +49,14 @@ private function processHealthConfiguration(array $config, ContainerBuilder $con
'connection' => new Reference($connection['service']),
'sql' => $connection['check_sql'],
];
$infoArgument[$name] = new Reference($connection['service']);
}

$definition = $container->getDefinition(\SymSensor\ActuatorDoctrineBundle\Service\Health\Indicator\Doctrine::class);
$definition->replaceArgument(0, $constructorArgument);
}
}
}

/**
* @param mixed[] $config
*/
private function processInfoConfiguration(array $config, ContainerBuilder $container): void
{
if (
$container->willBeAvailable('doctrine/doctrine-bundle', Connection::class, [])
&& $this->isConfigEnabled($container, $config)
) {
$loader = new YamlFileLoader($container, new FileLocator(__DIR__.'/../../config'));
$loader->load('doctrine_info.yaml');

if (isset($config['connections']) && \is_array($config['connections'])) {
$connectionReferences = [];
foreach ($config['connections'] as $name => $connectionDefinition) {
$connectionReferences[$name] = new Reference($connectionDefinition['service']);
}
$definition = $container->getDefinition(\SymSensor\ActuatorDoctrineBundle\Service\Info\Collector\Doctrine::class);
$definition->replaceArgument(0, $connectionReferences);
$infoDefinition = $container->getDefinition(\SymSensor\ActuatorDoctrineBundle\Service\Info\Collector\Doctrine::class);
$infoDefinition->replaceArgument(0, $infoArgument);
}
}
}
Expand Down
187 changes: 187 additions & 0 deletions tests/Service/Health/Indicator/DoctrineTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
<?php

declare(strict_types=1);

/*
* This file is part of the symsensor/actuator-doctrine-bundle package.
*
* (c) Kevin Studer <kreemer@me.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace SymSensor\ActuatorDoctrineBundle\Tests\Service\Health\Indicator;

use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Exception\DriverException;
use PHPUnit\Framework\TestCase;
use SymSensor\ActuatorBundle\Service\Health\HealthStack;
use SymSensor\ActuatorBundle\Service\Health\HealthState;
use SymSensor\ActuatorDoctrineBundle\Service\Health\Indicator\Doctrine;

class DoctrineTest extends TestCase
{
/**
* @test
*/
public function nameOfIndicator(): void
{
$databaseHealthIndicator = new Doctrine([]);

self::assertEquals('doctrine', $databaseHealthIndicator->name());
}

/**
* @test
*/
public function willThrowIfConnectionIsNotConnection(): void
{
$connection = new \stdClass();
$checks = [
[
'connection' => $connection,
'sql' => null,
],
];

$healthIndicator = new Doctrine($checks); // @phpstan-ignore-line

$health = $healthIndicator->health();

self::assertEquals(HealthState::UNKNOWN, $health->getStatus());
}

/**
* @test
*/
public function willConnectToConnectionIfNoSql(): void
{
$connection = self::createMock(Connection::class);

$connection->expects(self::once())
->method('connect');

$checks = [
[
'connection' => $connection,
'sql' => null,
],
];
$healthIndicator = new Doctrine($checks);

$health = $healthIndicator->health();

self::assertTrue($health->isUp());
}

/**
* @test
*/
public function healthIsNotUpIfConnectionFails(): void
{
$connection = self::createMock(Connection::class);

$connection->expects(self::once())
->method('connect')
->willThrowException(self::createMock(DriverException::class))
;

$checks = [
[
'connection' => $connection,
'sql' => null,
],
];
$healthIndicator = new Doctrine($checks);

$health = $healthIndicator->health();

self::assertFalse($health->isUp());
}

/**
* @test
*/
public function willCheckConnectionWithSql(): void
{
$connection = self::createMock(Connection::class);

$connection->expects(self::once())
->method('executeQuery')
;

$checks = [
[
'connection' => $connection,
'sql' => 'SELECT 1=1',
],
];
$healthIndicator = new Doctrine($checks);

$health = $healthIndicator->health();

self::assertTrue($health->isUp());
}

/**
* @test
*/
public function healthIsNotUpIfSqlFails(): void
{
$connection = self::createMock(Connection::class);

$connection->expects(self::once())
->method('executeQuery')
->willThrowException(self::createMock(DriverException::class))
;

$checks = [
[
'connection' => $connection,
'sql' => 'SELECT 1=1',
],
];
$healthIndicator = new Doctrine($checks);

$health = $healthIndicator->health();

self::assertFalse($health->isUp());
}

/**
* @test
*/
public function healthChecksEveryConnection(): void
{
$connection1 = self::createMock(Connection::class);
$connection2 = self::createMock(Connection::class);

$connection1->expects(self::once())
->method('executeQuery')
;

$connection2->expects(self::once())
->method('connect')
;

$checks = [
'conn1' => [
'connection' => $connection1,
'sql' => 'SELECT 1=1',
],
'conn2' => [
'connection' => $connection2,
'sql' => null,
],
];
$healthIndicator = new Doctrine($checks);

$health = $healthIndicator->health();

self::assertInstanceOf(HealthStack::class, $health);
self::assertCount(3, $health->jsonSerialize());
self::assertArrayHasKey('conn1', $health->jsonSerialize());
self::assertArrayHasKey('conn2', $health->jsonSerialize());
}
}
Loading

0 comments on commit 78be17a

Please sign in to comment.