Skip to content

Commit

Permalink
EndpointQueryCountTest: allow a range for the number of queries on co…
Browse files Browse the repository at this point in the history
…ntent_node endpoints

With the ResponsiveLayout, we now have mostly ContentNodes which are nested by 2 levels:
ColumnLayout -> DefaultLayout -> "Content Nodes with content"
This may lead to n+1 db queries that we experienced in the EndpointQueryCountTest.
This change now allows to define a range for the query count.
This is good enough to keep the performance at the same level.
  • Loading branch information
BacLuc committed Dec 10, 2023
1 parent e2d2c14 commit 3916453
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 18 deletions.
99 changes: 94 additions & 5 deletions api/tests/Api/SnapshotTests/EndpointQueryCountTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;

use function PHPUnit\Framework\assertThat;
use function PHPUnit\Framework\equalTo;
use function PHPUnit\Framework\greaterThanOrEqual;
use function PHPUnit\Framework\isEmpty;
use function PHPUnit\Framework\lessThanOrEqual;
use function PHPUnit\Framework\logicalAnd;

/**
* @internal
Expand All @@ -23,18 +27,18 @@ class EndpointQueryCountTest extends ECampApiTestCase {
* @throws ServerExceptionInterface
* @throws TransportExceptionInterface
*/
public function testNumberOfQueriesDidNotChange() {
public function testNumberOfQueriesDidNotChangeForStableEndpoints() {
$numberOfQueries = [];
$responseCodes = [];
$collectionEndpoints = $this->getCollectionEndpoints();
$collectionEndpoints = self::getCollectionEndpoints();
foreach ($collectionEndpoints as $collectionEndpoint) {
if ('/users' !== $collectionEndpoint) {
if ('/users' !== $collectionEndpoint && !str_contains($collectionEndpoint, '/content_node')) {
list($statusCode, $queryCount) = $this->measurePerformanceFor($collectionEndpoint);
$responseCodes[$collectionEndpoint] = $statusCode;
$numberOfQueries[$collectionEndpoint] = $queryCount;
}

if ('/content_nodes' !== $collectionEndpoint) {
if (!str_contains($collectionEndpoint, '/content_node')) {
$fixtureFor = $this->getFixtureFor($collectionEndpoint);
list($statusCode, $queryCount) = $this->measurePerformanceFor("{$collectionEndpoint}/{$fixtureFor->getId()}");
$responseCodes["{$collectionEndpoint}/item"] = $statusCode;
Expand All @@ -48,6 +52,58 @@ public function testNumberOfQueriesDidNotChange() {
$this->assertMatchesSnapshot($numberOfQueries);
}

/**
* @dataProvider getContentNodeEndpoints
*
* @throws ClientExceptionInterface
* @throws DecodingExceptionInterface
* @throws RedirectionExceptionInterface
* @throws ServerExceptionInterface
* @throws TransportExceptionInterface
*/
public function testNumberOfQueriesDidNotChangeForContentNodeCollectionEndpoints(string $collectionEndpoint) {
list($statusCode, $queryCount) = $this->measurePerformanceFor($collectionEndpoint);

assertThat($statusCode, equalTo(200));

$queryCountRanges = self::getContentNodeEndpointQueryCountRanges()[$collectionEndpoint];
assertThat(
$queryCount,
logicalAnd(
greaterThanOrEqual($queryCountRanges[0]),
lessThanOrEqual($queryCountRanges[1]),
)
);
}

/**
* @dataProvider getContentNodeEndpoints
*
* @throws ClientExceptionInterface
* @throws DecodingExceptionInterface
* @throws RedirectionExceptionInterface
* @throws ServerExceptionInterface
* @throws TransportExceptionInterface
*/
public function testNumberOfQueriesDidNotChangeForContentNodeItemEndpoints(string $collectionEndpoint) {
if ('/content_nodes' === $collectionEndpoint) {
self::markTestSkipped("{$collectionEndpoint} does not support get item endpoint");
}
$fixtureFor = $this->getFixtureFor($collectionEndpoint);
list($statusCode, $queryCount) = $this->measurePerformanceFor("{$collectionEndpoint}/{$fixtureFor->getId()}");

assertThat($statusCode, equalTo(200));

$queryCountRanges = self::getContentNodeEndpointQueryCountRanges()[$collectionEndpoint.'/item'];
assertThat(
$queryCount,
logicalAnd(
greaterThanOrEqual($queryCountRanges[0]),
lessThanOrEqual($queryCountRanges[1]),
)
);
}

/**
* @param mixed $collectionEndpoint
*
Expand All @@ -67,14 +123,47 @@ public function measurePerformanceFor(string $collectionEndpoint): array {
return [$statusCode, $queryCount];
}

public static function getContentNodeEndpoints(): array {
$collectionEndpoints = self::getCollectionEndpoints();
$normalEndpoints = array_filter($collectionEndpoints, function (string $endpoint) {
return str_contains($endpoint, '/content_node');
});

// @noinspection PhpUnnecessaryLocalVariableInspection
return array_reduce($normalEndpoints, function (?array $left, string $right) {
$newArray = $left ?? [];
$newArray[$right] = [$right];

return $newArray;
});
}

private static function getContentNodeEndpointQueryCountRanges(): array {
return [
'/content_nodes' => [8, 9],
'/content_node/column_layouts' => [6, 6],
'/content_node/column_layouts/item' => [10, 10],
'/content_node/material_nodes' => [6, 7],
'/content_node/material_nodes/item' => [9, 9],
'/content_node/multi_selects' => [6, 7],
'/content_node/multi_selects/item' => [9, 9],
'/content_node/responsive_layouts' => [6, 6],
'/content_node/responsive_layouts/item' => [9, 9],
'/content_node/single_texts' => [6, 7],
'/content_node/single_texts/item' => [9, 9],
'/content_node/storyboards' => [6, 7],
'/content_node/storyboards/item' => [9, 9],
];
}

/**
* @throws RedirectionExceptionInterface
* @throws DecodingExceptionInterface
* @throws ClientExceptionInterface
* @throws TransportExceptionInterface
* @throws ServerExceptionInterface
*/
private function getCollectionEndpoints() {
private static function getCollectionEndpoints() {
static::bootKernel();
$client = static::createClientWithCredentials();
$response = $client->request('GET', '/');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,6 @@
/camp_collaborations/item: 15
/categories: 11
/categories/item: 9
/content_nodes: 9
/content_node/column_layouts: 6
/content_node/column_layouts/item: 10
/content_node/material_nodes: 6
/content_node/material_nodes/item: 9
/content_node/multi_selects: 6
/content_node/multi_selects/item: 9
/content_node/responsive_layouts: 6
/content_node/responsive_layouts/item: 9
/content_node/single_texts: 6
/content_node/single_texts/item: 9
/content_node/storyboards: 6
/content_node/storyboards/item: 9
/content_types: 6
/content_types/item: 6
/days: 17
Expand Down

0 comments on commit 3916453

Please sign in to comment.