36e947fd8e
Auto Tag Develop / tag (push) Successful in 8s
## ERP-187 (1.7) — Tests PHPUnit RG-5.01→5.10 + capture contrat JSON
Couvre les règles de gestion du M5 (tickets de pesée) par des tests PHPUnit et capture la **réponse JSON réelle** (DoD § 4.0.bis) collée dans `spec-back.md` avant les écrans front.
### Tests unitaires (Processor / Normalizer / Callback — sans BDD ni HTTP)
- **NetWeightTest** (RG-5.05) : net = plein − vide, `null` si une pesée manque, recalcul au PATCH.
- **CounterpartyValidationTest** (RG-5.03) : présence du champ requis par branche (propertyPath `client`/`supplier`/`otherLabel`) + exclusivité (null-ification hors-branche).
- **ImmatriculationNormalizationTest** (RG-5.01/5.10) : masque `XX-000-XX`, « Tout format », mapping 422 sur `immatriculation`.
### Tests fonctionnels (API réelle)
- **WeighingTicketNumberingTest** (RG-5.02/5.09) : format `{siteCode}-TP-{NNNN}`, séquence par site, isolation inter-sites, immuabilité numéro/site au PATCH.
- **WeighingTicketSerializationContractTest** (DoD § 4.0.bis) : 4 pièges verts (client embarqué, `plateFreeFormat` présent, `number` formaté, `netWeight` = full − empty) + dump JSON via `WEIGHING_TICKET_DOD_DUMP`.
- **WeighingTicketRBACMatrixTest** (§ 5.2) : admin/bureau/usine OK, compta/commerciale 403, anonyme 401.
> DSD / stub pont bascule / endpoint pesée déjà couverts (ERP-184/185).
### DoD
- `spec-back.md § 4.0.bis` : **JSON réel** (liste + détail) collé, 4 pièges marqués ✅ — feu vert front.
### Vérifications
- `make test` complet **vert** : 848 tests, 6302 assertions (0 échec ; deprecations/notices PHPUnit seuls).
- `make php-cs-fixer-allow-risky` : 0 correction.
Empilée sur ERP-186 (stack M5).
---------
Co-authored-by: Matthieu <contact@malio.fr>
Reviewed-on: #137
122 lines
3.9 KiB
PHP
122 lines
3.9 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Tests\Module\Logistique\Api;
|
|
|
|
use App\Module\Core\Infrastructure\DataFixtures\RbacDemoFixtures;
|
|
use Symfony\Bundle\FrameworkBundle\Console\Application;
|
|
use Symfony\Component\Console\Input\ArrayInput;
|
|
use Symfony\Component\Console\Output\NullOutput;
|
|
|
|
/**
|
|
* Matrice RBAC du ticket de pesee par role metier (spec-back M5 § 5.2). Jumeau de
|
|
* {@see \App\Tests\Module\Transport\Api\CarrierRBACMatrixTest}.
|
|
*
|
|
* Matrice § 5.2 (V0.2) :
|
|
* - admin / bureau / usine : view + manage (200 lecture, 201 creation)
|
|
* - compta / commerciale : AUCUN acces (403 sur view ET manage)
|
|
* - anonyme : 401
|
|
*
|
|
* La creation (POST -> 201) suppose un site courant (numerotation + cloisonnement,
|
|
* § 2.3) : on le positionne pour chaque role autorise a ecrire.
|
|
*
|
|
* @internal
|
|
*/
|
|
final class WeighingTicketRBACMatrixTest extends AbstractWeighingTicketApiTestCase
|
|
{
|
|
private const string PWD = RbacDemoFixtures::DEMO_PASSWORD;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
|
|
// Seed idempotent des roles metier + matrice § 5.2 + comptes demo (meme
|
|
// chemin qu'en recette).
|
|
self::bootKernel();
|
|
$application = new Application(self::$kernel);
|
|
$application->setAutoExit(false);
|
|
$exit = $application->run(
|
|
new ArrayInput([
|
|
'command' => 'app:seed-rbac',
|
|
'--with-demo-users' => true,
|
|
'--password' => self::PWD,
|
|
]),
|
|
new NullOutput(),
|
|
);
|
|
self::assertSame(
|
|
0,
|
|
$exit,
|
|
'app:seed-rbac a echoue : les permissions logistique.weighing_tickets.* sont-elles synchronisees (app:sync-permissions) ?',
|
|
);
|
|
|
|
self::ensureKernelShutdown();
|
|
}
|
|
|
|
public function testAdminCanViewAndManage(): void
|
|
{
|
|
$this->assertCanViewAndManage('admin', 'admin');
|
|
}
|
|
|
|
public function testBureauCanViewAndManage(): void
|
|
{
|
|
$this->assertCanViewAndManage('bureau', self::PWD);
|
|
}
|
|
|
|
public function testUsineCanViewAndManage(): void
|
|
{
|
|
$this->assertCanViewAndManage('usine', self::PWD);
|
|
}
|
|
|
|
public function testComptaHasNoAccess(): void
|
|
{
|
|
$this->assertHasNoAccess('compta');
|
|
}
|
|
|
|
public function testCommercialeHasNoAccess(): void
|
|
{
|
|
$this->assertHasNoAccess('commerciale');
|
|
}
|
|
|
|
public function testAnonymousIsUnauthorized(): void
|
|
{
|
|
$client = self::createClient();
|
|
$client->request('GET', '/api/weighing_tickets', ['headers' => ['Accept' => self::LD]]);
|
|
self::assertResponseStatusCodeSame(401);
|
|
}
|
|
|
|
/**
|
|
* Role autorise : GET 200 (view) + POST 201 (manage). Le site courant est
|
|
* positionne avant le POST pour permettre la numerotation.
|
|
*/
|
|
private function assertCanViewAndManage(string $username, string $password): void
|
|
{
|
|
$site = $this->firstSite();
|
|
$this->setCurrentSite($username, $site);
|
|
|
|
$clientEntity = $this->seedTestClient('Rbac '.$username);
|
|
$http = $this->authenticatedClient($username, $password);
|
|
|
|
$http->request('GET', '/api/weighing_tickets', ['headers' => ['Accept' => self::LD]]);
|
|
self::assertResponseStatusCodeSame(200);
|
|
|
|
$this->postTicket($http, $this->validClientTicketPayload($clientEntity));
|
|
self::assertResponseStatusCodeSame(201);
|
|
}
|
|
|
|
/**
|
|
* Role sans acces : 403 en lecture (view absent) ET en ecriture (manage absent).
|
|
*/
|
|
private function assertHasNoAccess(string $username): void
|
|
{
|
|
$clientEntity = $this->seedTestClient('Rbac '.$username);
|
|
$http = $this->authenticatedClient($username, self::PWD);
|
|
|
|
$http->request('GET', '/api/weighing_tickets', ['headers' => ['Accept' => self::LD]]);
|
|
self::assertResponseStatusCodeSame(403);
|
|
|
|
$this->postTicket($http, $this->validClientTicketPayload($clientEntity));
|
|
self::assertResponseStatusCodeSame(403);
|
|
}
|
|
}
|