test(api) : tests fonctionnels pagination Category (5 cas) + non-regression AuditLog (2 cas)
This commit is contained in:
@@ -0,0 +1,171 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Tests\Module\Catalog\Api;
|
||||||
|
|
||||||
|
use DateTimeImmutable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests du contrat de pagination sur GET /api/categories (ERP-72).
|
||||||
|
*
|
||||||
|
* Invariants testes :
|
||||||
|
* - la collection expose les metadonnees Hydra (totalItems, view, member) ;
|
||||||
|
* - itemsPerPage est plafonne au maximum global (50) ;
|
||||||
|
* - une page hors limites retourne une collection vide, pas une 500 ;
|
||||||
|
* - ?pagination=false retourne tous les items sans troncature (select-box) ;
|
||||||
|
* - la pagination est compatible avec le flag ?includeDeleted=true.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
final class CategoryPaginationTest extends AbstractCatalogApiTestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* La collection expose les metadonnees de pagination JSON-LD sans prefixe :
|
||||||
|
* `totalItems`, `view`, `member` (convention API Platform 4, pas hydra:*).
|
||||||
|
*
|
||||||
|
* On cree 12 categories pour depasser la limite par page (10) : la cle
|
||||||
|
* `view` n'est presente que lorsqu'il y a plus d'items que la taille de page.
|
||||||
|
*/
|
||||||
|
public function testCollectionExposesHydraPaginationMetadata(): void
|
||||||
|
{
|
||||||
|
$type = $this->createCategoryType();
|
||||||
|
for ($i = 1; $i <= 12; ++$i) {
|
||||||
|
$this->createCategory(self::TEST_CATEGORY_PREFIX.'meta_'.$i, $type);
|
||||||
|
}
|
||||||
|
|
||||||
|
$client = $this->createAdminClient();
|
||||||
|
$response = $client->request('GET', '/api/categories');
|
||||||
|
|
||||||
|
self::assertSame(200, $response->getStatusCode());
|
||||||
|
|
||||||
|
$data = $response->toArray();
|
||||||
|
self::assertArrayHasKey('totalItems', $data, 'La collection doit exposer totalItems.');
|
||||||
|
self::assertArrayHasKey('view', $data, 'La collection doit exposer view (pagination) quand totalItems > itemsPerPage.');
|
||||||
|
self::assertIsArray($data['member'], 'member doit etre un tableau.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Un itemsPerPage arbitrairement grand (99999) doit etre plafonne au
|
||||||
|
* maximum global configure (50). On cree 12 categories pour etre certain
|
||||||
|
* de disposer de donnees ; le cap doit s'appliquer quelle que soit la taille
|
||||||
|
* reelle de la collection.
|
||||||
|
*/
|
||||||
|
public function testItemsPerPageIsCappedAtMaximum(): void
|
||||||
|
{
|
||||||
|
$type = $this->createCategoryType();
|
||||||
|
for ($i = 1; $i <= 12; ++$i) {
|
||||||
|
$this->createCategory(self::TEST_CATEGORY_PREFIX.'cap_'.$i, $type);
|
||||||
|
}
|
||||||
|
|
||||||
|
$client = $this->createAdminClient();
|
||||||
|
$response = $client->request('GET', '/api/categories?itemsPerPage=99999');
|
||||||
|
|
||||||
|
self::assertSame(200, $response->getStatusCode());
|
||||||
|
// Le cap global est 50 : jamais plus d'items par page que le maximum.
|
||||||
|
self::assertLessThanOrEqual(
|
||||||
|
50,
|
||||||
|
count($response->toArray()['member']),
|
||||||
|
'itemsPerPage doit etre plafonne au maximum global (50).',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Une page tres elevee (99999) sur une petite collection ne doit pas
|
||||||
|
* produire une 500 PG (OFFSET negatif ou depassement de capacite) mais
|
||||||
|
* retourner 200 avec un tableau member vide.
|
||||||
|
*/
|
||||||
|
public function testOutOfBoundPageReturnsEmptyCollectionNot500(): void
|
||||||
|
{
|
||||||
|
$type = $this->createCategoryType();
|
||||||
|
$this->createCategory(self::TEST_CATEGORY_PREFIX.'oob', $type);
|
||||||
|
|
||||||
|
$client = $this->createAdminClient();
|
||||||
|
$response = $client->request('GET', '/api/categories?page=99999');
|
||||||
|
|
||||||
|
self::assertSame(200, $response->getStatusCode());
|
||||||
|
// La page 99999 est forcement vide (on a bien moins que 99999*10 items).
|
||||||
|
self::assertSame(
|
||||||
|
[],
|
||||||
|
$response->toArray()['member'],
|
||||||
|
'Une page hors limites doit retourner un member vide, jamais une 500.',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ?pagination=false permet au frontend de desactiver la pagination pour
|
||||||
|
* alimenter un select-box. On cree exactement 12 categories dont les noms
|
||||||
|
* commencent par `test_cat_select_` : le filtre sur ce prefixe isole nos
|
||||||
|
* entrees des donnees concurrentes et prouve que les 12 items sont tous
|
||||||
|
* retournes (et pas seulement les 10 premiers de la page 1).
|
||||||
|
*/
|
||||||
|
public function testClientCanDisablePaginationToFeedASelect(): void
|
||||||
|
{
|
||||||
|
$type = $this->createCategoryType();
|
||||||
|
for ($i = 1; $i <= 12; ++$i) {
|
||||||
|
$this->createCategory(self::TEST_CATEGORY_PREFIX.'select_'.$i, $type);
|
||||||
|
}
|
||||||
|
|
||||||
|
$client = $this->createAdminClient();
|
||||||
|
$response = $client->request('GET', '/api/categories?pagination=false');
|
||||||
|
|
||||||
|
self::assertSame(200, $response->getStatusCode());
|
||||||
|
|
||||||
|
$members = $response->toArray()['member'];
|
||||||
|
|
||||||
|
// Filtre sur le sous-prefixe pour ne pas comptabiliser les categories
|
||||||
|
// d'autres tests qui partagent la meme base de donnees.
|
||||||
|
$selectItems = array_values(array_filter(
|
||||||
|
$members,
|
||||||
|
fn (array $m): bool => str_starts_with($m['name'], self::TEST_CATEGORY_PREFIX.'select_'),
|
||||||
|
));
|
||||||
|
|
||||||
|
self::assertCount(
|
||||||
|
12,
|
||||||
|
$selectItems,
|
||||||
|
'?pagination=false doit retourner toutes les categories (pas seulement la page 1).',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* La pagination doit fonctionner conjointement avec le flag ?includeDeleted=true.
|
||||||
|
* On seed 3 categories actives + 2 soft-deleted, on demande itemsPerPage=5 :
|
||||||
|
* la page 1 doit contenir exactement 5 items et totalItems doit valoir >= 5.
|
||||||
|
*/
|
||||||
|
public function testPaginationCombinedWithIncludeDeletedFlag(): void
|
||||||
|
{
|
||||||
|
$type = $this->createCategoryType();
|
||||||
|
|
||||||
|
// 3 categories actives.
|
||||||
|
for ($i = 1; $i <= 3; ++$i) {
|
||||||
|
$this->createCategory(self::TEST_CATEGORY_PREFIX.'pag_active_'.$i, $type);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2 categories soft-deleted.
|
||||||
|
for ($i = 1; $i <= 2; ++$i) {
|
||||||
|
$this->createCategory(
|
||||||
|
self::TEST_CATEGORY_PREFIX.'pag_deleted_'.$i,
|
||||||
|
$type,
|
||||||
|
new DateTimeImmutable(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$client = $this->createAdminClient();
|
||||||
|
$response = $client->request('GET', '/api/categories?includeDeleted=true&itemsPerPage=5');
|
||||||
|
|
||||||
|
self::assertSame(200, $response->getStatusCode());
|
||||||
|
|
||||||
|
$data = $response->toArray();
|
||||||
|
// La page retournee ne doit pas exceder itemsPerPage=5.
|
||||||
|
self::assertCount(
|
||||||
|
5,
|
||||||
|
$data['member'],
|
||||||
|
'La page 1 doit contenir exactement 5 items (itemsPerPage=5 avec >= 5 categories disponibles).',
|
||||||
|
);
|
||||||
|
self::assertGreaterThanOrEqual(
|
||||||
|
5,
|
||||||
|
$data['totalItems'],
|
||||||
|
'totalItems doit refleter au moins les 5 categories seedees (actives + soft-deleted).',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Tests\Module\Core\Api;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Regression test de pagination sur GET /api/audit-logs (ERP-72).
|
||||||
|
*
|
||||||
|
* Avant ce ticket, `paginationItemsPerPage` etait fixe a 30 dans
|
||||||
|
* AuditLogResource. Apres migration vers les defaults globaux (10/50),
|
||||||
|
* ce fichier verrouille le nouveau contrat :
|
||||||
|
* - la reponse est paginee (max 10 items par page par defaut) ;
|
||||||
|
* - un itemsPerPage excessif est plafonne a 50.
|
||||||
|
*
|
||||||
|
* Pas de seed : la table audit_log contient deja des lignes issues des
|
||||||
|
* fixtures / autres tests. Les assertions utilisent des inegalites pour
|
||||||
|
* rester robustes quelle que soit la quantite exacte de donnees presentes.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
final class AuditLogPaginationRegressionTest extends AbstractApiTestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* La collection /api/audit-logs doit etre paginee avec les defaults globaux :
|
||||||
|
* - `member`, `totalItems`, `view` presentes dans la reponse JSON-LD ;
|
||||||
|
* - au plus 10 items par page (nouveau defaut, etait 30 avant ce ticket).
|
||||||
|
*/
|
||||||
|
public function testAuditLogCollectionStillPaginated(): void
|
||||||
|
{
|
||||||
|
$client = $this->authenticatedClient('admin', 'admin');
|
||||||
|
$response = $client->request('GET', '/api/audit-logs');
|
||||||
|
|
||||||
|
self::assertSame(200, $response->getStatusCode());
|
||||||
|
|
||||||
|
$data = $response->toArray();
|
||||||
|
self::assertArrayHasKey('totalItems', $data, 'La collection audit-logs doit exposer totalItems.');
|
||||||
|
self::assertArrayHasKey('view', $data, 'La collection audit-logs doit exposer view (pagination active).');
|
||||||
|
self::assertIsArray($data['member'], 'member doit etre un tableau.');
|
||||||
|
|
||||||
|
// Le nouveau defaut global est 10 (etait 30 dans AuditLogResource avant ERP-72).
|
||||||
|
self::assertLessThanOrEqual(
|
||||||
|
10,
|
||||||
|
count($data['member']),
|
||||||
|
'La page par defaut ne doit pas depasser 10 items (default global ERP-72).',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Un itemsPerPage excessif (99999) doit etre plafonne au maximum global (50).
|
||||||
|
* Teste la regression specifique du paginator DBAL custom (DbalPaginator) qui
|
||||||
|
* pourrait ignorer la limite si la logique de cap n'est pas appliquee cote provider.
|
||||||
|
*/
|
||||||
|
public function testAuditLogItemsPerPageCappedAt50(): void
|
||||||
|
{
|
||||||
|
$client = $this->authenticatedClient('admin', 'admin');
|
||||||
|
$response = $client->request('GET', '/api/audit-logs?itemsPerPage=99999');
|
||||||
|
|
||||||
|
self::assertSame(200, $response->getStatusCode());
|
||||||
|
|
||||||
|
$data = $response->toArray();
|
||||||
|
self::assertIsArray($data['member'], 'member doit etre un tableau.');
|
||||||
|
|
||||||
|
// Le cap global est 50 : jamais plus d'items par page que le maximum.
|
||||||
|
self::assertLessThanOrEqual(
|
||||||
|
50,
|
||||||
|
count($data['member']),
|
||||||
|
'itemsPerPage=99999 doit etre plafonne a 50 (maximum global ERP-72).',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user