120058049c
Auto Tag Develop / tag (push) Successful in 7s
Dernier wagon de la stack back M1. ERP-60 = polish stack + couverture de tests PHPUnit NON dépendante des rôles métier (cf. spec § 7 / § 8.1). ## Phase 0 — polish stack (déjà mergé dans les branches basses via rebase) - ERP-59 : route sidebar `/clients` (au lieu de `/commercial/clients`), cohérente avec `/suppliers`. - One-liner pagination Client abandonné : `pagination_client_enabled: true` est déjà le défaut global → `?pagination=false` marche déjà sur `/api/clients` (décision P7). ## Phase 1 — tests (combler les trous, zéro duplication) 8 nouvelles suites couvrant les RG non encore testées par ERP-55/56/57/58 : - `ClientFormulaireMainTest` — RG-1.02 (téléphone secondaire, max 2). - `ClientAddressTest` — RG-1.06/07/08 + RG-1.11 (CHECK BDD prospect/billing). - `ClientUniquenessTest` — RG-1.15/1.17 (Q4 : SIREN/email NON uniques). - `ClientArchiveTest` — **RG-1.23 : 409 restauration en conflit (gap P1)**. - `ClientAuditTest` — RG-1.27 (created* figés / updatedBy modificateur) + iban/bic présents dans le diff audité. - `ClientMigrationTest` — index partiel unique `uq_client_company_name_active` (1 seul) ; pas d'index siren/email. - `ClientSecurityTest` — 401 anonyme + 403 sans `commercial.clients.view`. - `ClientPatchStrictTest` — RG-1.28 (403 strict mix de groupes, fonctionnel). Cahier de test complet (mapping de TOUTES les RG → test) : `docs/specs/M1-clients/cahier-test-back-M1.md`. ## Délégué à ERP-74 (#493) Matrice RBAC différenciée (bureau/compta/commerciale/usine) + RG-1.04 fonctionnel — exigent les rôles métier seedés après le merge de la stack. ## Gaps documentés (cahier) - RG-1.29 validation écriture (catégorie type sur adresse → 422) non implémentée back (hors § 8.1, ticket test-only). - Violations CHECK adresse → rejet (≥400) sans mapping fin 422 (amélioration possible). ## Vérifs `make db-reset && make php-cs-fixer-allow-risky && make test` → **421 tests OK, 1386 assertions, 0 risky**. Nouveaux tests : 17, 71 assertions. --------- Co-authored-by: Matthieu <contact@malio.fr> Reviewed-on: #38 Co-authored-by: THOLOT DECHENE Matthieu <matthieu@yuno.malio.fr> Co-committed-by: THOLOT DECHENE Matthieu <matthieu@yuno.malio.fr>
100 lines
3.0 KiB
PHP
100 lines
3.0 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Tests\Shared\Infrastructure\Export;
|
|
|
|
use App\Shared\Infrastructure\Export\PhpSpreadsheetExporter;
|
|
use Generator;
|
|
use PhpOffice\PhpSpreadsheet\IOFactory;
|
|
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
/**
|
|
* Test unitaire du service Shared d'export XLSX. Verifie que le binaire produit
|
|
* est un vrai fichier XLSX relisible, que l'en-tete et les lignes sont ecrits
|
|
* dans le bon ordre, qu'un iterable paresseux (generator) est accepte et que le
|
|
* titre d'onglet est assaini.
|
|
*
|
|
* @internal
|
|
*/
|
|
final class PhpSpreadsheetExporterTest extends TestCase
|
|
{
|
|
public function testExportProducesReadableXlsxWithHeadersAndRows(): void
|
|
{
|
|
$binary = new PhpSpreadsheetExporter()->export(
|
|
'Feuille test',
|
|
['Nom', 'Email'],
|
|
[
|
|
['Alpha', 'alpha@test.fr'],
|
|
['Beta', null],
|
|
],
|
|
);
|
|
|
|
self::assertNotSame('', $binary);
|
|
// Un fichier XLSX (OOXML) est une archive ZIP : signature "PK\x03\x04".
|
|
self::assertStringStartsWith("PK\x03\x04", $binary);
|
|
|
|
$grid = $this->grid($binary);
|
|
self::assertSame(['Nom', 'Email'], $grid[0]);
|
|
self::assertSame('Alpha', $grid[1][0]);
|
|
self::assertSame('alpha@test.fr', $grid[1][1]);
|
|
self::assertSame('Beta', $grid[2][0]);
|
|
// Cellule null a l'ecriture -> vide a la relecture.
|
|
self::assertNull($grid[2][1]);
|
|
}
|
|
|
|
public function testExportAcceptsGeneratorRows(): void
|
|
{
|
|
$rows = (static function (): Generator {
|
|
yield ['L1'];
|
|
|
|
yield ['L2'];
|
|
})();
|
|
|
|
$grid = $this->grid(new PhpSpreadsheetExporter()->export('Gen', ['H'], $rows));
|
|
|
|
self::assertSame('H', $grid[0][0]);
|
|
self::assertSame('L1', $grid[1][0]);
|
|
self::assertSame('L2', $grid[2][0]);
|
|
}
|
|
|
|
public function testLongOrInvalidSheetTitleIsSanitized(): void
|
|
{
|
|
// Titre > 31 caracteres + caracteres interdits par Excel ([ ] : * etc.).
|
|
$binary = new PhpSpreadsheetExporter()->export(
|
|
str_repeat('A', 50).'[]:*?/\\',
|
|
['H'],
|
|
[['x']],
|
|
);
|
|
|
|
$title = $this->load($binary)->getActiveSheet()->getTitle();
|
|
self::assertLessThanOrEqual(31, mb_strlen($title));
|
|
self::assertStringNotContainsString('[', $title);
|
|
self::assertStringNotContainsString(':', $title);
|
|
}
|
|
|
|
/**
|
|
* Relit le binaire XLSX et renvoie la grille de cellules (ligne 0 = entete).
|
|
*
|
|
* @return array<int, array<int, mixed>>
|
|
*/
|
|
private function grid(string $binary): array
|
|
{
|
|
return $this->load($binary)->getActiveSheet()->toArray();
|
|
}
|
|
|
|
private function load(string $binary): Spreadsheet
|
|
{
|
|
$tmp = tempnam(sys_get_temp_dir(), 'xlsx_test_');
|
|
self::assertIsString($tmp);
|
|
file_put_contents($tmp, $binary);
|
|
|
|
try {
|
|
return IOFactory::load($tmp);
|
|
} finally {
|
|
@unlink($tmp);
|
|
}
|
|
}
|
|
}
|