Skip to content

Commit

Permalink
Update matthiasnoback/symfony-dependency-injection-test requirement f…
Browse files Browse the repository at this point in the history
…rom ^4 to ^5 (facile-it#169)

* Update matthiasnoback/symfony-dependency-injection-test requirement

Updates the requirements on [matthiasnoback/symfony-dependency-injection-test](https://github.com/matthiasnoback/SymfonyDependencyInjectionTest) to permit the latest version.
- [Release notes](https://github.com/matthiasnoback/SymfonyDependencyInjectionTest/releases)
- [Changelog](https://github.com/SymfonyTest/SymfonyDependencyInjectionTest/blob/master/CHANGELOG.md)
- [Commits](SymfonyTest/SymfonyDependencyInjectionTest@4.0.0...5.1.0)

---
updated-dependencies:
- dependency-name: matthiasnoback/symfony-dependency-injection-test
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <[email protected]>

* Allow both major versions

* Rework CI matrix to mark Symfony 7 and Mongo 7

* Rework ProfileControllerTest into full functional test

* Add Monolog to E2E tests to avoid logs in stdout

* Drop usage of ContainerAwareInterface

* Simplify E2E app configuration

* Silence deprecation from 5.1

* Require dom-crawler explicitly

* Use Symfony Flex to lock Symfony version in CI

---------

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Alessandro Lai <[email protected]>
Co-authored-by: Ilario Pierbattista <[email protected]>
  • Loading branch information
3 people authored Jul 17, 2024
1 parent 7badee0 commit 9ee761a
Show file tree
Hide file tree
Showing 20 changed files with 232 additions and 172 deletions.
11 changes: 6 additions & 5 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ jobs:
tests:
name: Tests (PHP ${{ matrix.php }}, ext-mongodb ${{ matrix.mongo-ext }}, MongoDB ${{ matrix.mongo-img }}, Symfony ${{ matrix.symfony }})
runs-on: ubuntu-latest
env:
SYMFONY_REQUIRE: ${{ matrix.symfony }}
services:
mongo:
image: mongo:${{ matrix.mongo-img }}
Expand Down Expand Up @@ -38,9 +40,10 @@ jobs:
- php: 8.2
mongo-ext: 1.15.0
mongo-img: 6.0
symfony: "^6.4"
- php: 8.3
mongo-ext: 1.16.0
mongo-img: 6.0
mongo-ext: 1.19.0
mongo-img: 7.0

steps:
- name: Checkout
Expand All @@ -50,12 +53,10 @@ jobs:
with:
php-version: ${{ matrix.php }}
extensions: mongodb-${{ matrix.mongo-ext }}
tools: flex
- name: Allow unstable dependencies
run: composer config minimum-stability dev
if: matrix.symfony == 'dev-master'
- name: Restrict Symfony version
run: composer require "symfony/symfony:${{ matrix.symfony }}" --no-update
if: matrix.symfony
- name: Install dependencies
uses: ramsey/composer-install@v3
- name: Await a bit for Mongo to spin up...
Expand Down
8 changes: 6 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,22 @@
"symfony/framework-bundle": "^4.4 || ^5.0 || ^6.0 || ^7.0"
},
"require-dev": {
"matthiasnoback/symfony-dependency-injection-test": "^4",
"matthiasnoback/symfony-dependency-injection-test": "^4 || ^5",
"symfony/web-profiler-bundle": "^4.4 || ^5.0 || ^6.0 || ^7.0",
"symfony/console": "^4.4 || ^5.0 || ^6.0 || ^7.0",
"phpunit/phpunit": "^9.6.13 || ^10.5.27",
"symfony/browser-kit": "^4.4 || ^5.0 || ^6.0 || ^7.0",
"symfony/dom-crawler": "^4.4 || ^5.0 || ^6.0 || ^7.0",
"symfony/phpunit-bridge": "^7.0",
"symfony/routing": "^4.4 || ^5.0 || ^6.0 || ^7.0",
"facile-it/facile-coding-standard": "1.2.0",
"phpstan/phpstan": "1.11.7",
"phpstan/extension-installer": "1.4.1",
"jangregor/phpstan-prophecy": "1.0.2",
"phpspec/prophecy": "^1.17",
"rector/rector": "^1.0.3",
"phpspec/prophecy-phpunit": "^2.0"
"phpspec/prophecy-phpunit": "^2.0",
"symfony/monolog-bundle": "*"
},
"minimum-stability": "stable",
"suggest": {
Expand Down
42 changes: 19 additions & 23 deletions src/Controller/ProfilerController.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,49 +6,45 @@

use Facile\MongoDbBundle\DataCollector\MongoDbDataCollector;
use Facile\MongoDbBundle\DataCollector\MongoQuerySerializer;
use Facile\MongoDbBundle\Services\Explain\ExplainQueryService;
use MongoDB\BSON\UTCDateTime;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpKernel\Profiler\Profiler;

class ProfilerController implements ContainerAwareInterface
class ProfilerController
{
private ?ContainerInterface $container = null;
private ExplainQueryService $explain;

/**
* Sets the container.
*
* @param ContainerInterface|null $container A ContainerInterface instance or null
*/
public function setContainer(ContainerInterface $container = null): void
private ?Profiler $profiler;

public function __construct(ExplainQueryService $explain, ?Profiler $profiler)
{
$this->container = $container;
$this->explain = $explain;
$this->profiler = $profiler;
}

/**
* @throws \Exception
*/
public function explainAction(string $token, $queryNumber): JsonResponse
{
/** @var Profiler $profiler */
$profiler = $this->container->get('profiler');
$profiler->disable();
$this->profiler->disable();

$profile = $this->profiler->loadProfile($token);
if (! $profile) {
throw new \RuntimeException('No profile found');
}

$profile = $profiler->loadProfile($token);
/** @var MongoDbDataCollector $dataCollector */
$dataCollector = $profile->getCollector('mongodb');
if (! $dataCollector instanceof MongoDbDataCollector) {
throw new \RuntimeException('MongoDb data collector not found');
}

$queries = $dataCollector->getQueries();

$query = $queries[$queryNumber];

$query->setFilters($this->walkAndConvertToUTCDatetime($query->getFilters()));

$service = $this->container->get('mongo.explain_query_service');

try {
$result = $service->execute($query);
$result = $this->explain->execute($query);
} catch (\InvalidArgumentException $e) {
return new JsonResponse([
'err' => $e->getMessage(),
Expand Down
7 changes: 7 additions & 0 deletions src/Resources/config/profiler.xml
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,12 @@
<tag name="twig.extension"/>
</service>

<!-- controller -->

<service id="facile_mongo_db.profiler_controller" class="Facile\MongoDbBundle\Controller\ProfilerController">
<argument type="service" id="mongo.explain_query_service" />
<argument type="service" id="profiler" on-invalid="null" />
</service>

</services>
</container>
137 changes: 63 additions & 74 deletions tests/Functional/Controller/ProfilerControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,99 +4,88 @@

namespace Facile\MongoDbBundle\Tests\Functional\Controller;

use Facile\MongoDbBundle\Tests\Functional\TestApp\TestKernelWithProfiler;
use Prophecy\PhpUnit\ProphecyTrait;
use Facile\MongoDbBundle\Controller\ProfilerController;
use Facile\MongoDbBundle\DataCollector\MongoDbDataCollector;
use Facile\MongoDbBundle\Models\Query;
use Facile\MongoDbBundle\Tests\Functional\AppTestCase;
use MongoDB\BSON\UTCDateTime;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpKernel\Profiler\Profile;
use Symfony\Component\HttpKernel\Profiler\Profiler;

class ProfilerControllerTest extends AppTestCase
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\DomCrawler\Crawler;

class ProfilerControllerTest extends WebTestCase
{
use ProphecyTrait;

protected static function getKernelClass(): string
{
return TestKernelWithProfiler::class;
}

protected function setUp(): void
{
$this->setEnvDev();
$this->cleanUpDir(__DIR__ . '/../../../var/cache');
parent::setUp();
}

public function test_explainAction(): void
{
$query = new Query();
$query->setClient('test_client');
$query->setDatabase('testFunctionaldb');
$query->setCollection('fooCollection');
$query->setMethod('count');
$query->setFilters(['date' => new UTCDateTime((new \DateTime())->getTimestamp() * 1_000)]);

$collector = $this->prophesize(MongoDbDataCollector::class);
$collector->getQueries()->shouldBeCalledTimes(1)->willReturn([$query]);

$profile = $this->prophesize(Profile::class);
$profile->getCollector('mongodb')->shouldBeCalledTimes(1)->willReturn($collector->reveal());

$profiler = $this->prophesize(Profiler::class);
$profiler->loadProfile('fooToken')->shouldBeCalledTimes(1)->willReturn($profile->reveal());
$profiler->disable()->shouldBeCalledTimes(1);

$explainService = $this->getContainer()->get('mongo.explain_query_service');

$container = $this->prophesize(Container::class);
$container->get('profiler')->willReturn($profiler->reveal());
$container->get('mongo.explain_query_service')->willReturn($explainService);

$controller = new ProfilerController();
$controller->setContainer($container->reveal());

$response = $controller->explainAction('fooToken', 0);

$this->assertInstanceOf(JsonResponse::class, $response);
$this->assertEquals(200, $response->getStatusCode());

$data = json_decode($response->getContent(), true);
$this->assertEquals(JSON_ERROR_NONE, json_last_error());

$this->assertTrue(is_array($data));
$this->assertArrayNotHasKey('err', $data);
$client = self::createClient();

$client->request('GET', '/trigger_query');
$this->assertResponseIsSuccessful();
$crawler = $client->request('GET', '/_profiler/latest?panel=mongodb');

$this->assertResponseIsSuccessful();
$this->assertHeadersArePresent($crawler, 'http://localhost/trigger_query');
$explainTable = $crawler->filterXPath('//table[2]');
$this->assertCrawlerTextContainsString('insertOne', $explainTable);
$this->assertCrawlerTextContainsString('test_collection', $explainTable);
$this->assertCrawlerTextContainsString('{ "foo": "bar" }', $explainTable);
}

public function test_explainAction_error(): void
{
$query = new Query();
$query->setMethod('fooo');

$collector = $this->prophesize(MongoDbDataCollector::class);
$collector->getQueries()->shouldBeCalledTimes(1)->willReturn([$query]);

$profile = $this->prophesize(Profile::class);
$profile->getCollector('mongodb')->shouldBeCalledTimes(1)->willReturn($collector->reveal());

$profiler = $this->prophesize(Profiler::class);
$profiler->loadProfile('fooToken')->shouldBeCalledTimes(1)->willReturn($profile->reveal());
$profiler->disable()->shouldBeCalledTimes(1);

$explainService = $this->getContainer()->get('mongo.explain_query_service');

$container = $this->prophesize(Container::class);
$container->get('profiler')->willReturn($profiler->reveal());
$container->get('mongo.explain_query_service')->willReturn($explainService);
$client = self::createClient();

$controller = new ProfilerController();
$controller->setContainer($container->reveal());
$client->request('GET', '/noop');
$this->assertResponseIsSuccessful();
$crawler = $client->request('GET', '/_profiler/latest?panel=mongodb');
$this->assertResponseIsSuccessful();

$response = $controller->explainAction('fooToken', 0);
$this->assertHeadersArePresent($crawler, 'http://localhost/noop');
$explainTable = $crawler->filterXPath('//table[2]');
$this->assertCrawlerTextContainsString('No queries', $explainTable);
}

$this->assertInstanceOf(JsonResponse::class, $response);
$this->assertEquals(200, $response->getStatusCode());
private function cleanUpDir(string $dir): bool
{
if (! file_exists($dir)) {
return false;
}

$it = new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS);
$files = new \RecursiveIteratorIterator($it, \RecursiveIteratorIterator::CHILD_FIRST);

foreach ($files as $file) {
if ($file->isDir()) {
$this->cleanUpDir($file->getRealPath());
} else {
unlink($file->getRealPath());
}
}

return rmdir($dir);
}

$data = json_decode($response->getContent(), true);
private function assertHeadersArePresent(Crawler $crawler, string $expectedTitle): void
{
$this->assertCrawlerTextContainsString($expectedTitle, $crawler->filterXPath('//h2[1]'));
$this->assertCrawlerTextContainsString('Mongo DB Query Metrics', $crawler->filterXPath('//h2[2]'));
$this->assertCrawlerTextContainsString('Connections list', $crawler->filterXPath('//h2[3]'));
$this->assertCrawlerTextContainsString('Queries Detail', $crawler->filterXPath('//h2[4]'));
$this->assertCrawlerTextContainsString('test_client.testFunctionaldb', $crawler->filterXPath('//table[1]'));
}

$this->assertTrue(is_array($data));
$this->assertArrayHasKey('err', $data);
private function assertCrawlerTextContainsString(string $needle, Crawler $explainTable): void
{
// silence 4.4 deprecation about whitespace normalization
$this->assertStringContainsString($needle, $explainTable->text('', true));
}
}
31 changes: 31 additions & 0 deletions tests/Functional/TestApp/MainController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

declare(strict_types=1);

namespace Facile\MongoDbBundle\Tests\Functional\TestApp;

use MongoDB\Database;
use Symfony\Component\HttpFoundation\Response;

class MainController
{
private Database $database;

public function __construct(Database $database)
{
$this->database = $database;
}

public function noop(): Response
{
return new Response('Hello there');
}

public function triggerQuery(): Response
{
$this->database->selectCollection('test_collection')
->insertOne(['foo' => 'bar']);

return new Response('Hello there');
}
}
14 changes: 6 additions & 8 deletions tests/Functional/TestApp/TestKernel.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use Facile\MongoDbBundle\FacileMongoDbBundle;
use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
use Symfony\Bundle\MonologBundle\MonologBundle;
use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Component\HttpKernel\Bundle\Bundle;
use Symfony\Component\HttpKernel\Kernel;
Expand All @@ -21,6 +22,7 @@ public function registerBundles(): array
{
return [
new FrameworkBundle(),
new MonologBundle(),
new FacileMongoDbBundle(),
];
}
Expand All @@ -30,22 +32,18 @@ public function registerBundles(): array
*/
public function registerContainerConfiguration(LoaderInterface $loader): void
{
$suffix = '';
$version = '';
$loader->load(__DIR__ . '/config.yaml');

if ('docker' === getenv('TEST_ENV')) {
$suffix = '_docker';
$loader->load(__DIR__ . '/docker.yaml');
}

if (version_compare(Kernel::VERSION, '6.1.0') >= 0) {
$version = '_61';
$loader->load(__DIR__ . '/deprecations_6.1.yml');
}

if (version_compare(Kernel::VERSION, '6.4.0') >= 0) {
$version = '_64';
$loader->load(__DIR__ . '/deprecations_6.4.yml');
}

$configFile = sprintf('/config_test%s%s.yml', $version, $suffix);
$loader->load(__DIR__ . $configFile);
}
}
Loading

0 comments on commit 9ee761a

Please sign in to comment.