aad949c10c
Couvre les 20 nouveaux outils MCP Directory (5 par entite : create/get/list/ update/delete) avec un focus sur les guards et invariants : - exactly-one-parent (Contact/Address/CommercialReport) - ROLE_ADMIN - ISO 3166 alpha-2 + normalisation uppercase (Address) - enum ReportType + defaults note/today + parsing date (CommercialReport) - author auto-rempli par CommercialReportAuthorListener (token storage) - collections vides dans get-prestataire enrichi - ordre DESC sur occurredAt pour list-commercial-reports - delete renvoie null apres em.clear() 38 tests / 105 assertions. Suite complete passe a 217/217.
184 lines
6.6 KiB
PHP
184 lines
6.6 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Tests\Functional\Mcp\Directory;
|
|
|
|
use App\Module\Core\Domain\Entity\User;
|
|
use App\Module\Directory\Domain\Repository\AddressRepositoryInterface;
|
|
use App\Module\Directory\Domain\Repository\CommercialReportRepositoryInterface;
|
|
use App\Module\Directory\Domain\Repository\ContactRepositoryInterface;
|
|
use App\Module\Directory\Domain\Repository\PrestataireRepositoryInterface;
|
|
use App\Module\Directory\Infrastructure\Mcp\Tool\CreatePrestataireTool;
|
|
use App\Module\Directory\Infrastructure\Mcp\Tool\DeletePrestataireTool;
|
|
use App\Module\Directory\Infrastructure\Mcp\Tool\GetPrestataireTool;
|
|
use App\Module\Directory\Infrastructure\Mcp\Tool\ListPrestatairesTool;
|
|
use App\Module\Directory\Infrastructure\Mcp\Tool\UpdatePrestataireTool;
|
|
use Doctrine\ORM\EntityManagerInterface;
|
|
use InvalidArgumentException;
|
|
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
|
use Symfony\Bundle\SecurityBundle\Security;
|
|
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
|
|
|
|
/**
|
|
* @internal
|
|
*/
|
|
class PrestataireLifecycleTest extends KernelTestCase
|
|
{
|
|
private EntityManagerInterface $em;
|
|
private User $admin;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
self::bootKernel();
|
|
$this->em = self::getContainer()->get(EntityManagerInterface::class);
|
|
|
|
$this->admin = new User();
|
|
$this->admin->setUsername('mcp-prest-admin-'.uniqid());
|
|
$this->admin->setPassword('x');
|
|
$this->admin->setRoles(['ROLE_ADMIN']);
|
|
$this->em->persist($this->admin);
|
|
$this->em->flush();
|
|
}
|
|
|
|
public function testCreatePersistsAllFields(): void
|
|
{
|
|
$json = ($this->createTool(admin: true))('ACME Cleaning', 'contact@acme.example', '+33100000000', 'https://acme.example');
|
|
$data = json_decode($json, true);
|
|
|
|
self::assertIsInt($data['id']);
|
|
self::assertSame('ACME Cleaning', $data['name']);
|
|
self::assertSame('contact@acme.example', $data['email']);
|
|
self::assertSame('+33100000000', $data['phone']);
|
|
self::assertSame('https://acme.example', $data['website']);
|
|
}
|
|
|
|
public function testCreateRequiresAdmin(): void
|
|
{
|
|
$this->expectException(AccessDeniedException::class);
|
|
($this->createTool(admin: false))('Should not pass');
|
|
}
|
|
|
|
public function testGetReturnsEmptyCollectionsWhenNoChildren(): void
|
|
{
|
|
$created = json_decode(($this->createTool(admin: true))('Lonely Prest'), true);
|
|
|
|
$json = ($this->getTool(admin: true))((int) $created['id']);
|
|
$data = json_decode($json, true);
|
|
|
|
self::assertSame($created['id'], $data['id']);
|
|
self::assertSame([], $data['contacts']);
|
|
self::assertSame([], $data['addresses']);
|
|
self::assertSame([], $data['reports']);
|
|
}
|
|
|
|
public function testGetUnknownIdThrows(): void
|
|
{
|
|
$this->expectException(InvalidArgumentException::class);
|
|
$this->expectExceptionMessage('Prestataire with ID 999999 not found.');
|
|
($this->getTool(admin: true))(999999);
|
|
}
|
|
|
|
public function testUpdateOnlyTouchesProvidedFields(): void
|
|
{
|
|
$created = json_decode(($this->createTool(admin: true))('Before', 'before@x.test', '+33000000000', 'https://before.test'), true);
|
|
|
|
$json = ($this->updateTool(admin: true))((int) $created['id'], null, 'after@x.test', null, null);
|
|
$data = json_decode($json, true);
|
|
|
|
self::assertSame('Before', $data['name']); // unchanged
|
|
self::assertSame('after@x.test', $data['email']); // changed
|
|
self::assertSame('+33000000000', $data['phone']); // unchanged
|
|
self::assertSame('https://before.test', $data['website']); // unchanged
|
|
}
|
|
|
|
public function testListReturnsAllPrestatairesOrderedByName(): void
|
|
{
|
|
// Unique prefix isolates this test from data leaked by prior PHPUnit
|
|
// runs (DAMA rollback is not active in this project).
|
|
$prefix = 'list-test-'.uniqid().'-';
|
|
($this->createTool(admin: true))($prefix.'Zeta');
|
|
($this->createTool(admin: true))($prefix.'Alpha');
|
|
($this->createTool(admin: true))($prefix.'Mu');
|
|
|
|
$data = json_decode(($this->listTool(admin: true))(), true);
|
|
$names = array_values(array_filter(
|
|
array_column($data, 'name'),
|
|
fn ($n) => str_starts_with((string) $n, $prefix),
|
|
));
|
|
|
|
self::assertSame([$prefix.'Alpha', $prefix.'Mu', $prefix.'Zeta'], $names);
|
|
}
|
|
|
|
public function testDeleteRemovesPrestataire(): void
|
|
{
|
|
$created = json_decode(($this->createTool(admin: true))('To be removed'), true);
|
|
$id = (int) $created['id'];
|
|
|
|
$json = ($this->deleteTool(admin: true))($id);
|
|
$data = json_decode($json, true);
|
|
|
|
self::assertTrue($data['success']);
|
|
self::assertStringContainsString('"To be removed"', $data['message']);
|
|
|
|
$this->em->clear();
|
|
self::assertNull(self::getContainer()->get(PrestataireRepositoryInterface::class)->findById($id));
|
|
}
|
|
|
|
private function securityFor(bool $admin): Security
|
|
{
|
|
$security = $this->createMock(Security::class);
|
|
$security->method('isGranted')->willReturn($admin);
|
|
$security->method('getUser')->willReturn($admin ? $this->admin : null);
|
|
|
|
return $security;
|
|
}
|
|
|
|
private function createTool(bool $admin): CreatePrestataireTool
|
|
{
|
|
return new CreatePrestataireTool(
|
|
$this->em,
|
|
$this->securityFor($admin),
|
|
);
|
|
}
|
|
|
|
private function getTool(bool $admin): GetPrestataireTool
|
|
{
|
|
$c = self::getContainer();
|
|
|
|
return new GetPrestataireTool(
|
|
$c->get(PrestataireRepositoryInterface::class),
|
|
$c->get(ContactRepositoryInterface::class),
|
|
$c->get(AddressRepositoryInterface::class),
|
|
$c->get(CommercialReportRepositoryInterface::class),
|
|
$this->securityFor($admin),
|
|
);
|
|
}
|
|
|
|
private function updateTool(bool $admin): UpdatePrestataireTool
|
|
{
|
|
return new UpdatePrestataireTool(
|
|
self::getContainer()->get(PrestataireRepositoryInterface::class),
|
|
$this->em,
|
|
$this->securityFor($admin),
|
|
);
|
|
}
|
|
|
|
private function listTool(bool $admin): ListPrestatairesTool
|
|
{
|
|
return new ListPrestatairesTool(
|
|
self::getContainer()->get(PrestataireRepositoryInterface::class),
|
|
$this->securityFor($admin),
|
|
);
|
|
}
|
|
|
|
private function deleteTool(bool $admin): DeletePrestataireTool
|
|
{
|
|
return new DeletePrestataireTool(
|
|
self::getContainer()->get(PrestataireRepositoryInterface::class),
|
|
$this->em,
|
|
$this->securityFor($admin),
|
|
);
|
|
}
|
|
}
|