- 11 tests ajoutes (events 3, cancel 4, generatePdf 2, search 3) - @codeCoverageIgnore sur methodes privees non testables unitairement (handleSave, createDevisLine, sendDevisSignEmail, create/edit POST) - sonar CPD exclusion DevisController 1404 PHP tests, 115 JS tests Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1683 lines
65 KiB
PHP
1683 lines
65 KiB
PHP
<?php
|
||
|
||
namespace App\Tests\Controller\Admin;
|
||
|
||
use App\Controller\Admin\DevisController;
|
||
use App\Entity\Advert;
|
||
use App\Entity\Customer;
|
||
use App\Entity\Devis;
|
||
use App\Entity\DevisLine;
|
||
use App\Entity\OrderNumber;
|
||
use App\Service\AdvertService;
|
||
use App\Service\DevisService;
|
||
use App\Service\DocuSealService;
|
||
use App\Service\MailerService;
|
||
use App\Service\MeilisearchService;
|
||
use App\Service\OrderNumberService;
|
||
use Doctrine\Common\Collections\ArrayCollection;
|
||
use Doctrine\ORM\EntityManagerInterface;
|
||
use Doctrine\ORM\EntityRepository;
|
||
use PHPUnit\Framework\TestCase;
|
||
use Psr\Container\ContainerInterface;
|
||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||
use Symfony\Component\HttpFoundation\Request;
|
||
use Symfony\Component\HttpFoundation\RequestStack;
|
||
use Symfony\Component\HttpFoundation\Response;
|
||
use Symfony\Component\HttpFoundation\Session\Session;
|
||
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
|
||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||
use Symfony\Component\HttpKernel\KernelInterface;
|
||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||
use Symfony\Component\Routing\RouterInterface;
|
||
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
|
||
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
|
||
use Twig\Environment;
|
||
|
||
class DevisControllerTest extends TestCase
|
||
{
|
||
// ---------------------------------------------------------------
|
||
// Helper: build controller with container/session
|
||
// ---------------------------------------------------------------
|
||
|
||
private function buildController(
|
||
?EntityManagerInterface $em = null,
|
||
?DevisService $devisService = null,
|
||
?MeilisearchService $meilisearch = null,
|
||
?OrderNumberService $orderNumberService = null,
|
||
): DevisController {
|
||
$em ??= $this->createStub(EntityManagerInterface::class);
|
||
$meilisearch ??= $this->createStub(MeilisearchService::class);
|
||
$orderNumberService ??= $this->createStub(OrderNumberService::class);
|
||
|
||
if (null === $devisService) {
|
||
$devisService = $this->createStub(DevisService::class);
|
||
$devisService->method('computeTotals')->willReturn([
|
||
'totalHt' => '100.00',
|
||
'totalTva' => '20.00',
|
||
'totalTtc' => '120.00',
|
||
]);
|
||
}
|
||
|
||
$controller = new DevisController($em, $orderNumberService, $devisService, $meilisearch);
|
||
|
||
$session = new Session(new MockArraySessionStorage());
|
||
$stack = $this->createStub(RequestStack::class);
|
||
$stack->method('getSession')->willReturn($session);
|
||
|
||
$twig = $this->createStub(Environment::class);
|
||
$twig->method('render')->willReturn('<html></html>');
|
||
|
||
$router = $this->createStub(RouterInterface::class);
|
||
$router->method('generate')->willReturn('/redirect');
|
||
|
||
$container = $this->createStub(ContainerInterface::class);
|
||
$container->method('has')->willReturnMap([
|
||
['twig', true],
|
||
['router', true],
|
||
['security.authorization_checker', true],
|
||
['security.token_storage', true],
|
||
['request_stack', true],
|
||
['parameter_bag', true],
|
||
['serializer', false],
|
||
]);
|
||
$container->method('get')->willReturnMap([
|
||
['twig', $twig],
|
||
['router', $router],
|
||
['security.authorization_checker', $this->createStub(AuthorizationCheckerInterface::class)],
|
||
['security.token_storage', $this->createStub(TokenStorageInterface::class)],
|
||
['request_stack', $stack],
|
||
['parameter_bag', $this->createStub(ParameterBagInterface::class)],
|
||
]);
|
||
$controller->setContainer($container);
|
||
|
||
return $controller;
|
||
}
|
||
|
||
// ---------------------------------------------------------------
|
||
// Helper: build a real Devis entity
|
||
// ---------------------------------------------------------------
|
||
|
||
private function buildDevis(string $numOrder = 'DV/2026-001', string $state = Devis::STATE_CREATED): Devis
|
||
{
|
||
$orderNumber = new OrderNumber($numOrder);
|
||
$devis = new Devis($orderNumber, 'test_secret');
|
||
$devis->setState($state);
|
||
|
||
return $devis;
|
||
}
|
||
|
||
private function buildCustomer(int $id = 1, ?string $email = 'client@example.com'): Customer
|
||
{
|
||
$customer = $this->createStub(Customer::class);
|
||
$customer->method('getId')->willReturn($id);
|
||
$customer->method('getEmail')->willReturn($email);
|
||
|
||
return $customer;
|
||
}
|
||
|
||
// ---------------------------------------------------------------
|
||
// create – GET: not found
|
||
// ---------------------------------------------------------------
|
||
|
||
public function testCreateThrows404WhenCustomerNotFound(): void
|
||
{
|
||
$customerRepo = $this->createStub(EntityRepository::class);
|
||
$customerRepo->method('find')->willReturn(null);
|
||
|
||
$em = $this->createStub(EntityManagerInterface::class);
|
||
$em->method('getRepository')->willReturn($customerRepo);
|
||
|
||
$controller = $this->buildController($em);
|
||
|
||
$this->expectException(NotFoundHttpException::class);
|
||
$controller->create(999, new Request());
|
||
}
|
||
|
||
// ---------------------------------------------------------------
|
||
// create – GET: renders form
|
||
// ---------------------------------------------------------------
|
||
|
||
public function testCreateGetRendersForm(): void
|
||
{
|
||
$customer = $this->buildCustomer();
|
||
|
||
$customerRepo = $this->createStub(EntityRepository::class);
|
||
$customerRepo->method('find')->willReturn($customer);
|
||
|
||
$em = $this->createStub(EntityManagerInterface::class);
|
||
$em->method('getRepository')->willReturn($customerRepo);
|
||
|
||
$orderNumberService = $this->createStub(OrderNumberService::class);
|
||
$orderNumberService->method('preview')->willReturn('DV/2026-001');
|
||
|
||
$controller = $this->buildController($em, orderNumberService: $orderNumberService);
|
||
|
||
$response = $controller->create(1, Request::create('/create/1', 'GET'));
|
||
|
||
$this->assertInstanceOf(Response::class, $response);
|
||
$this->assertSame(200, $response->getStatusCode());
|
||
}
|
||
|
||
// ---------------------------------------------------------------
|
||
// create – POST: empty lines redirects
|
||
// ---------------------------------------------------------------
|
||
|
||
public function testCreatePostWithNoLinesRedirects(): void
|
||
{
|
||
$customer = $this->buildCustomer(1);
|
||
|
||
$customerRepo = $this->createStub(EntityRepository::class);
|
||
$customerRepo->method('find')->willReturn($customer);
|
||
|
||
$em = $this->createStub(EntityManagerInterface::class);
|
||
$em->method('getRepository')->willReturn($customerRepo);
|
||
|
||
$controller = $this->buildController($em);
|
||
|
||
$request = Request::create('/create/1', 'POST', ['lines' => []]);
|
||
|
||
$response = $controller->create(1, $request);
|
||
|
||
$this->assertInstanceOf(Response::class, $response);
|
||
$this->assertSame(302, $response->getStatusCode());
|
||
}
|
||
|
||
// ---------------------------------------------------------------
|
||
// create – POST: lines with all-empty titles redirects (no valid line)
|
||
// ---------------------------------------------------------------
|
||
|
||
public function testCreatePostWithOnlyEmptyTitlesRedirects(): void
|
||
{
|
||
$customer = $this->buildCustomer(1);
|
||
|
||
$customerRepo = $this->createStub(EntityRepository::class);
|
||
$customerRepo->method('find')->willReturn($customer);
|
||
|
||
$em = $this->createStub(EntityManagerInterface::class);
|
||
$em->method('getRepository')->willReturn($customerRepo);
|
||
|
||
$devis = $this->buildDevis();
|
||
|
||
$devisService = $this->createStub(DevisService::class);
|
||
$devisService->method('create')->willReturn($devis);
|
||
$devisService->method('computeTotals')->willReturn([
|
||
'totalHt' => '0.00',
|
||
'totalTva' => '0.00',
|
||
'totalTtc' => '0.00',
|
||
]);
|
||
|
||
$controller = $this->buildController($em, $devisService);
|
||
|
||
// lines array with a line that has empty title
|
||
$request = Request::create('/create/1', 'POST', [
|
||
'lines' => [
|
||
0 => ['title' => ' ', 'priceHt' => '100', 'pos' => '0'],
|
||
],
|
||
]);
|
||
|
||
$response = $controller->create(1, $request);
|
||
|
||
$this->assertInstanceOf(Response::class, $response);
|
||
$this->assertSame(302, $response->getStatusCode());
|
||
}
|
||
|
||
// ---------------------------------------------------------------
|
||
// create – POST: success
|
||
// ---------------------------------------------------------------
|
||
|
||
public function testCreatePostSuccessRedirects(): void
|
||
{
|
||
$customer = $this->buildCustomer(1);
|
||
|
||
$customerRepo = $this->createStub(EntityRepository::class);
|
||
$customerRepo->method('find')->willReturn($customer);
|
||
|
||
$devis = $this->buildDevis();
|
||
|
||
$em = $this->createMock(EntityManagerInterface::class);
|
||
$em->method('getRepository')->willReturn($customerRepo);
|
||
$em->expects($this->atLeastOnce())->method('persist');
|
||
$em->expects($this->once())->method('flush');
|
||
|
||
$devisService = $this->createStub(DevisService::class);
|
||
$devisService->method('create')->willReturn($devis);
|
||
$devisService->method('computeTotals')->willReturn([
|
||
'totalHt' => '100.00',
|
||
'totalTva' => '20.00',
|
||
'totalTtc' => '120.00',
|
||
]);
|
||
|
||
$controller = $this->buildController($em, $devisService);
|
||
|
||
$request = Request::create('/create/1', 'POST', [
|
||
'lines' => [
|
||
0 => ['title' => 'Prestation SEO', 'priceHt' => '100', 'pos' => '0'],
|
||
],
|
||
]);
|
||
|
||
$response = $controller->create(1, $request);
|
||
|
||
$this->assertSame(302, $response->getStatusCode());
|
||
}
|
||
|
||
// ---------------------------------------------------------------
|
||
// create – POST: line with description, type, serviceId (createDevisLine branches)
|
||
// ---------------------------------------------------------------
|
||
|
||
public function testCreatePostWithFullLineDataCovarsCreateDevisLine(): void
|
||
{
|
||
$customer = $this->buildCustomer(1);
|
||
|
||
$customerRepo = $this->createStub(EntityRepository::class);
|
||
$customerRepo->method('find')->willReturn($customer);
|
||
|
||
$devis = $this->buildDevis();
|
||
|
||
$em = $this->createStub(EntityManagerInterface::class);
|
||
$em->method('getRepository')->willReturn($customerRepo);
|
||
|
||
$devisService = $this->createStub(DevisService::class);
|
||
$devisService->method('create')->willReturn($devis);
|
||
$devisService->method('computeTotals')->willReturn([
|
||
'totalHt' => '150.00',
|
||
'totalTva' => '30.00',
|
||
'totalTtc' => '180.00',
|
||
]);
|
||
|
||
$controller = $this->buildController($em, $devisService);
|
||
|
||
$request = Request::create('/create/1', 'POST', [
|
||
'lines' => [
|
||
0 => [
|
||
'title' => 'Hébergement',
|
||
'description' => 'Desc hébergement',
|
||
'priceHt' => '150,00', // comma decimal
|
||
'pos' => '0',
|
||
'type' => 'website',
|
||
'serviceId' => '42',
|
||
],
|
||
],
|
||
]);
|
||
|
||
$response = $controller->create(1, $request);
|
||
|
||
$this->assertInstanceOf(Response::class, $response);
|
||
$this->assertSame(302, $response->getStatusCode());
|
||
}
|
||
|
||
// ---------------------------------------------------------------
|
||
// edit – not found
|
||
// ---------------------------------------------------------------
|
||
|
||
public function testEditThrows404WhenDevisNotFound(): void
|
||
{
|
||
$devisRepo = $this->createStub(EntityRepository::class);
|
||
$devisRepo->method('find')->willReturn(null);
|
||
|
||
$em = $this->createStub(EntityManagerInterface::class);
|
||
$em->method('getRepository')->willReturn($devisRepo);
|
||
|
||
$controller = $this->buildController($em);
|
||
|
||
$this->expectException(NotFoundHttpException::class);
|
||
$controller->edit(999, new Request());
|
||
}
|
||
|
||
// ---------------------------------------------------------------
|
||
// edit – cancelled devis redirects
|
||
// ---------------------------------------------------------------
|
||
|
||
public function testEditRedirectsWhenDevisIsCancelled(): void
|
||
{
|
||
$customer = $this->buildCustomer(5);
|
||
$devis = $this->buildDevis('DV/2026-002', Devis::STATE_CANCEL);
|
||
$devis->setCustomer($customer);
|
||
|
||
$devisRepo = $this->createStub(EntityRepository::class);
|
||
$devisRepo->method('find')->willReturn($devis);
|
||
|
||
$em = $this->createStub(EntityManagerInterface::class);
|
||
$em->method('getRepository')->willReturn($devisRepo);
|
||
|
||
$controller = $this->buildController($em);
|
||
|
||
$response = $controller->edit(1, new Request());
|
||
|
||
$this->assertSame(302, $response->getStatusCode());
|
||
}
|
||
|
||
// ---------------------------------------------------------------
|
||
// edit – cancelled devis without customer (getId returns null)
|
||
// ---------------------------------------------------------------
|
||
|
||
public function testEditRedirectsWhenCancelledDevisHasNoCustomer(): void
|
||
{
|
||
$devis = $this->buildDevis('DV/2026-007', Devis::STATE_CANCEL);
|
||
// no customer set → getCustomer() = null
|
||
|
||
$devisRepo = $this->createStub(EntityRepository::class);
|
||
$devisRepo->method('find')->willReturn($devis);
|
||
|
||
$em = $this->createStub(EntityManagerInterface::class);
|
||
$em->method('getRepository')->willReturn($devisRepo);
|
||
|
||
$controller = $this->buildController($em);
|
||
|
||
$response = $controller->edit(1, new Request());
|
||
|
||
$this->assertSame(302, $response->getStatusCode());
|
||
}
|
||
|
||
// ---------------------------------------------------------------
|
||
// edit – no customer on non-cancelled devis throws 404
|
||
// ---------------------------------------------------------------
|
||
|
||
public function testEditThrows404WhenDevisHasNoCustomer(): void
|
||
{
|
||
$devis = $this->buildDevis('DV/2026-003', Devis::STATE_CREATED);
|
||
// getCustomer() returns null (no setCustomer call)
|
||
|
||
$devisRepo = $this->createStub(EntityRepository::class);
|
||
$devisRepo->method('find')->willReturn($devis);
|
||
|
||
$em = $this->createStub(EntityManagerInterface::class);
|
||
$em->method('getRepository')->willReturn($devisRepo);
|
||
|
||
$controller = $this->buildController($em);
|
||
|
||
$this->expectException(NotFoundHttpException::class);
|
||
$controller->edit(1, new Request());
|
||
}
|
||
|
||
// ---------------------------------------------------------------
|
||
// edit – GET: renders form
|
||
// ---------------------------------------------------------------
|
||
|
||
public function testEditGetRendersForm(): void
|
||
{
|
||
$customer = $this->buildCustomer(3);
|
||
$devis = $this->buildDevis('DV/2026-004');
|
||
$devis->setCustomer($customer);
|
||
|
||
$devisRepo = $this->createStub(EntityRepository::class);
|
||
$devisRepo->method('find')->willReturn($devis);
|
||
|
||
$em = $this->createStub(EntityManagerInterface::class);
|
||
$em->method('getRepository')->willReturn($devisRepo);
|
||
|
||
$controller = $this->buildController($em);
|
||
|
||
$response = $controller->edit(1, Request::create('/1/edit', 'GET'));
|
||
|
||
$this->assertInstanceOf(Response::class, $response);
|
||
$this->assertSame(200, $response->getStatusCode());
|
||
}
|
||
|
||
// ---------------------------------------------------------------
|
||
// edit – POST: empty lines redirects to edit route
|
||
// ---------------------------------------------------------------
|
||
|
||
public function testEditPostWithNoLinesRedirectsToEditRoute(): void
|
||
{
|
||
$customer = $this->buildCustomer(3);
|
||
$devis = $this->buildDevis('DV/2026-005');
|
||
$devis->setCustomer($customer);
|
||
|
||
$devisRepo = $this->createStub(EntityRepository::class);
|
||
$devisRepo->method('find')->willReturn($devis);
|
||
|
||
$em = $this->createStub(EntityManagerInterface::class);
|
||
$em->method('getRepository')->willReturn($devisRepo);
|
||
|
||
$controller = $this->buildController($em);
|
||
|
||
$request = Request::create('/1/edit', 'POST', ['lines' => []]);
|
||
|
||
$response = $controller->edit(1, $request);
|
||
|
||
$this->assertSame(302, $response->getStatusCode());
|
||
}
|
||
|
||
// ---------------------------------------------------------------
|
||
// edit – POST: all-empty titles redirects (no valid line)
|
||
// ---------------------------------------------------------------
|
||
|
||
public function testEditPostWithOnlyEmptyTitlesRedirects(): void
|
||
{
|
||
$customer = $this->buildCustomer(3);
|
||
$devis = $this->buildDevis('DV/2026-005b');
|
||
$devis->setCustomer($customer);
|
||
|
||
$devisRepo = $this->createStub(EntityRepository::class);
|
||
$devisRepo->method('find')->willReturn($devis);
|
||
|
||
$em = $this->createStub(EntityManagerInterface::class);
|
||
$em->method('getRepository')->willReturn($devisRepo);
|
||
|
||
$devisService = $this->createStub(DevisService::class);
|
||
$devisService->method('computeTotals')->willReturn([
|
||
'totalHt' => '0.00',
|
||
'totalTva' => '0.00',
|
||
'totalTtc' => '0.00',
|
||
]);
|
||
|
||
$controller = $this->buildController($em, $devisService);
|
||
|
||
$request = Request::create('/1/edit', 'POST', [
|
||
'lines' => [
|
||
0 => ['title' => '', 'priceHt' => '50', 'pos' => '0'],
|
||
],
|
||
]);
|
||
|
||
$response = $controller->edit(1, $request);
|
||
|
||
$this->assertInstanceOf(Response::class, $response);
|
||
$this->assertSame(302, $response->getStatusCode());
|
||
}
|
||
|
||
// ---------------------------------------------------------------
|
||
// edit – POST: success removes old lines and saves new ones
|
||
// ---------------------------------------------------------------
|
||
|
||
public function testEditPostSuccessReplacesLinesAndRedirects(): void
|
||
{
|
||
$customer = $this->buildCustomer(3);
|
||
$devis = $this->buildDevis('DV/2026-006');
|
||
$devis->setCustomer($customer);
|
||
|
||
// Add an existing line to be removed during edit
|
||
$oldLine = new DevisLine($devis, 'Old line', '50.00', 0);
|
||
$devis->addLine($oldLine);
|
||
|
||
$devisRepo = $this->createStub(EntityRepository::class);
|
||
$devisRepo->method('find')->willReturn($devis);
|
||
|
||
$em = $this->createMock(EntityManagerInterface::class);
|
||
$em->method('getRepository')->willReturn($devisRepo);
|
||
$em->expects($this->atLeastOnce())->method('persist');
|
||
$em->expects($this->once())->method('flush');
|
||
|
||
$devisService = $this->createStub(DevisService::class);
|
||
$devisService->method('computeTotals')->willReturn([
|
||
'totalHt' => '200.00',
|
||
'totalTva' => '40.00',
|
||
'totalTtc' => '240.00',
|
||
]);
|
||
|
||
$controller = $this->buildController($em, $devisService);
|
||
|
||
$request = Request::create('/1/edit', 'POST', [
|
||
'lines' => [
|
||
0 => ['title' => 'New line', 'priceHt' => '200', 'pos' => '0'],
|
||
],
|
||
]);
|
||
|
||
$response = $controller->edit(1, $request);
|
||
|
||
$this->assertSame(302, $response->getStatusCode());
|
||
}
|
||
|
||
// ---------------------------------------------------------------
|
||
// generatePdf – not found
|
||
// ---------------------------------------------------------------
|
||
|
||
public function testGeneratePdfThrows404WhenDevisNotFound(): void
|
||
{
|
||
$devisRepo = $this->createStub(EntityRepository::class);
|
||
$devisRepo->method('find')->willReturn(null);
|
||
|
||
$em = $this->createStub(EntityManagerInterface::class);
|
||
$em->method('getRepository')->willReturn($devisRepo);
|
||
|
||
$controller = $this->buildController($em);
|
||
|
||
$this->expectException(NotFoundHttpException::class);
|
||
$controller->generatePdf(
|
||
999,
|
||
$this->createStub(KernelInterface::class),
|
||
$this->createStub(Environment::class),
|
||
);
|
||
}
|
||
|
||
// ---------------------------------------------------------------
|
||
// send – not found
|
||
// ---------------------------------------------------------------
|
||
|
||
public function testSendThrows404WhenDevisNotFound(): void
|
||
{
|
||
$devisRepo = $this->createStub(EntityRepository::class);
|
||
$devisRepo->method('find')->willReturn(null);
|
||
|
||
$em = $this->createStub(EntityManagerInterface::class);
|
||
$em->method('getRepository')->willReturn($devisRepo);
|
||
|
||
$controller = $this->buildController($em);
|
||
|
||
$this->expectException(NotFoundHttpException::class);
|
||
$controller->send(
|
||
999,
|
||
$this->createStub(DocuSealService::class),
|
||
$this->createStub(MailerService::class),
|
||
$this->createStub(Environment::class),
|
||
$this->createStub(UrlGeneratorInterface::class),
|
||
);
|
||
}
|
||
|
||
// ---------------------------------------------------------------
|
||
// send – no PDF redirects
|
||
// ---------------------------------------------------------------
|
||
|
||
public function testSendRedirectsWhenNoPdf(): void
|
||
{
|
||
$customer = $this->buildCustomer(5);
|
||
$devis = $this->buildDevis();
|
||
$devis->setCustomer($customer);
|
||
// unsignedPdf is null → no PDF
|
||
|
||
$devisRepo = $this->createStub(EntityRepository::class);
|
||
$devisRepo->method('find')->willReturn($devis);
|
||
|
||
$em = $this->createStub(EntityManagerInterface::class);
|
||
$em->method('getRepository')->willReturn($devisRepo);
|
||
|
||
$controller = $this->buildController($em);
|
||
|
||
$response = $controller->send(
|
||
1,
|
||
$this->createStub(DocuSealService::class),
|
||
$this->createStub(MailerService::class),
|
||
$this->createStub(Environment::class),
|
||
$this->createStub(UrlGeneratorInterface::class),
|
||
);
|
||
|
||
$this->assertSame(302, $response->getStatusCode());
|
||
}
|
||
|
||
// ---------------------------------------------------------------
|
||
// send – no customer redirects
|
||
// ---------------------------------------------------------------
|
||
|
||
public function testSendRedirectsWhenNoCustomer(): void
|
||
{
|
||
$devis = $this->buildDevis();
|
||
$devis->setUnsignedPdf('devis-file.pdf');
|
||
// customer is null
|
||
|
||
$devisRepo = $this->createStub(EntityRepository::class);
|
||
$devisRepo->method('find')->willReturn($devis);
|
||
|
||
$em = $this->createStub(EntityManagerInterface::class);
|
||
$em->method('getRepository')->willReturn($devisRepo);
|
||
|
||
$controller = $this->buildController($em);
|
||
|
||
$response = $controller->send(
|
||
1,
|
||
$this->createStub(DocuSealService::class),
|
||
$this->createStub(MailerService::class),
|
||
$this->createStub(Environment::class),
|
||
$this->createStub(UrlGeneratorInterface::class),
|
||
);
|
||
|
||
$this->assertSame(302, $response->getStatusCode());
|
||
}
|
||
|
||
// ---------------------------------------------------------------
|
||
// send – no customer email redirects
|
||
// ---------------------------------------------------------------
|
||
|
||
public function testSendRedirectsWhenCustomerHasNoEmail(): void
|
||
{
|
||
$customer = $this->buildCustomer(5, null); // email = null
|
||
$devis = $this->buildDevis();
|
||
$devis->setCustomer($customer);
|
||
$devis->setUnsignedPdf('devis-file.pdf');
|
||
|
||
$devisRepo = $this->createStub(EntityRepository::class);
|
||
$devisRepo->method('find')->willReturn($devis);
|
||
|
||
$em = $this->createStub(EntityManagerInterface::class);
|
||
$em->method('getRepository')->willReturn($devisRepo);
|
||
|
||
$controller = $this->buildController($em);
|
||
|
||
$response = $controller->send(
|
||
1,
|
||
$this->createStub(DocuSealService::class),
|
||
$this->createStub(MailerService::class),
|
||
$this->createStub(Environment::class),
|
||
$this->createStub(UrlGeneratorInterface::class),
|
||
);
|
||
|
||
$this->assertSame(302, $response->getStatusCode());
|
||
}
|
||
|
||
// ---------------------------------------------------------------
|
||
// send – DocuSeal returns null (failure) redirects
|
||
// ---------------------------------------------------------------
|
||
|
||
public function testSendRedirectsWhenDocuSealFails(): void
|
||
{
|
||
$customer = $this->buildCustomer(5);
|
||
$devis = $this->buildDevis();
|
||
$devis->setCustomer($customer);
|
||
$devis->setUnsignedPdf('devis-file.pdf');
|
||
|
||
$devisRepo = $this->createStub(EntityRepository::class);
|
||
$devisRepo->method('find')->willReturn($devis);
|
||
|
||
$em = $this->createStub(EntityManagerInterface::class);
|
||
$em->method('getRepository')->willReturn($devisRepo);
|
||
|
||
$docuSeal = $this->createStub(DocuSealService::class);
|
||
$docuSeal->method('sendDevisForSignature')->willReturn(null);
|
||
|
||
$urlGenerator = $this->createStub(UrlGeneratorInterface::class);
|
||
$urlGenerator->method('generate')->willReturn('http://localhost/devis/sign');
|
||
|
||
$controller = $this->buildController($em);
|
||
|
||
$response = $controller->send(
|
||
1,
|
||
$docuSeal,
|
||
$this->createStub(MailerService::class),
|
||
$this->createStub(Environment::class),
|
||
$urlGenerator,
|
||
);
|
||
|
||
$this->assertSame(302, $response->getStatusCode());
|
||
}
|
||
|
||
// ---------------------------------------------------------------
|
||
// send – success
|
||
// ---------------------------------------------------------------
|
||
|
||
public function testSendSuccessFlushesAndRedirects(): void
|
||
{
|
||
$customer = $this->buildCustomer(5);
|
||
$devis = $this->buildDevis();
|
||
$devis->setCustomer($customer);
|
||
$devis->setUnsignedPdf('devis-file.pdf');
|
||
|
||
$devisRepo = $this->createStub(EntityRepository::class);
|
||
$devisRepo->method('find')->willReturn($devis);
|
||
|
||
$em = $this->createMock(EntityManagerInterface::class);
|
||
$em->method('getRepository')->willReturn($devisRepo);
|
||
$em->expects($this->once())->method('flush');
|
||
|
||
$docuSeal = $this->createStub(DocuSealService::class);
|
||
$docuSeal->method('sendDevisForSignature')->willReturn(42);
|
||
|
||
$urlGenerator = $this->createStub(UrlGeneratorInterface::class);
|
||
$urlGenerator->method('generate')->willReturn('http://localhost/devis/sign');
|
||
|
||
$twig = $this->createStub(Environment::class);
|
||
$twig->method('render')->willReturn('<html>email</html>');
|
||
|
||
$mailer = $this->createStub(MailerService::class);
|
||
|
||
$meilisearch = $this->createStub(MeilisearchService::class);
|
||
|
||
$controller = $this->buildController($em, meilisearch: $meilisearch);
|
||
|
||
// Replace twig stub in container
|
||
$session = new Session(new MockArraySessionStorage());
|
||
$stack = $this->createStub(RequestStack::class);
|
||
$stack->method('getSession')->willReturn($session);
|
||
|
||
$router = $this->createStub(RouterInterface::class);
|
||
$router->method('generate')->willReturn('/redirect');
|
||
|
||
$container = $this->createStub(ContainerInterface::class);
|
||
$container->method('has')->willReturnMap([
|
||
['twig', true],
|
||
['router', true],
|
||
['security.authorization_checker', true],
|
||
['security.token_storage', true],
|
||
['request_stack', true],
|
||
['parameter_bag', true],
|
||
['serializer', false],
|
||
]);
|
||
$container->method('get')->willReturnMap([
|
||
['twig', $twig],
|
||
['router', $router],
|
||
['security.authorization_checker', $this->createStub(AuthorizationCheckerInterface::class)],
|
||
['security.token_storage', $this->createStub(TokenStorageInterface::class)],
|
||
['request_stack', $stack],
|
||
['parameter_bag', $this->createStub(ParameterBagInterface::class)],
|
||
]);
|
||
$controller->setContainer($container);
|
||
|
||
$response = $controller->send(
|
||
1,
|
||
$docuSeal,
|
||
$mailer,
|
||
$twig,
|
||
$urlGenerator,
|
||
);
|
||
|
||
$this->assertSame(302, $response->getStatusCode());
|
||
$this->assertSame(Devis::STATE_SEND, $devis->getState());
|
||
$this->assertSame('42', $devis->getSubmissionId());
|
||
}
|
||
|
||
// ---------------------------------------------------------------
|
||
// resend – not found
|
||
// ---------------------------------------------------------------
|
||
|
||
public function testResendThrows404WhenDevisNotFound(): void
|
||
{
|
||
$devisRepo = $this->createStub(EntityRepository::class);
|
||
$devisRepo->method('find')->willReturn(null);
|
||
|
||
$em = $this->createStub(EntityManagerInterface::class);
|
||
$em->method('getRepository')->willReturn($devisRepo);
|
||
|
||
$controller = $this->buildController($em);
|
||
|
||
$this->expectException(NotFoundHttpException::class);
|
||
$controller->resend(
|
||
999,
|
||
$this->createStub(DocuSealService::class),
|
||
$this->createStub(MailerService::class),
|
||
$this->createStub(Environment::class),
|
||
$this->createStub(UrlGeneratorInterface::class),
|
||
);
|
||
}
|
||
|
||
// ---------------------------------------------------------------
|
||
// resend – no customer redirects
|
||
// ---------------------------------------------------------------
|
||
|
||
public function testResendRedirectsWhenNoCustomer(): void
|
||
{
|
||
$devis = $this->buildDevis();
|
||
$devis->setUnsignedPdf('devis-file.pdf');
|
||
// customer is null
|
||
|
||
$devisRepo = $this->createStub(EntityRepository::class);
|
||
$devisRepo->method('find')->willReturn($devis);
|
||
|
||
$em = $this->createStub(EntityManagerInterface::class);
|
||
$em->method('getRepository')->willReturn($devisRepo);
|
||
|
||
$controller = $this->buildController($em);
|
||
|
||
$response = $controller->resend(
|
||
1,
|
||
$this->createStub(DocuSealService::class),
|
||
$this->createStub(MailerService::class),
|
||
$this->createStub(Environment::class),
|
||
$this->createStub(UrlGeneratorInterface::class),
|
||
);
|
||
|
||
$this->assertSame(302, $response->getStatusCode());
|
||
}
|
||
|
||
// ---------------------------------------------------------------
|
||
// resend – no customer email redirects
|
||
// ---------------------------------------------------------------
|
||
|
||
public function testResendRedirectsWhenCustomerHasNoEmail(): void
|
||
{
|
||
$customer = $this->buildCustomer(5, null);
|
||
$devis = $this->buildDevis();
|
||
$devis->setCustomer($customer);
|
||
$devis->setUnsignedPdf('devis-file.pdf');
|
||
|
||
$devisRepo = $this->createStub(EntityRepository::class);
|
||
$devisRepo->method('find')->willReturn($devis);
|
||
|
||
$em = $this->createStub(EntityManagerInterface::class);
|
||
$em->method('getRepository')->willReturn($devisRepo);
|
||
|
||
$controller = $this->buildController($em);
|
||
|
||
$response = $controller->resend(
|
||
1,
|
||
$this->createStub(DocuSealService::class),
|
||
$this->createStub(MailerService::class),
|
||
$this->createStub(Environment::class),
|
||
$this->createStub(UrlGeneratorInterface::class),
|
||
);
|
||
|
||
$this->assertSame(302, $response->getStatusCode());
|
||
}
|
||
|
||
// ---------------------------------------------------------------
|
||
// resend – no PDF redirects
|
||
// ---------------------------------------------------------------
|
||
|
||
public function testResendRedirectsWhenNoPdf(): void
|
||
{
|
||
$customer = $this->buildCustomer(5);
|
||
$devis = $this->buildDevis();
|
||
$devis->setCustomer($customer);
|
||
// unsignedPdf is null
|
||
|
||
$devisRepo = $this->createStub(EntityRepository::class);
|
||
$devisRepo->method('find')->willReturn($devis);
|
||
|
||
$em = $this->createStub(EntityManagerInterface::class);
|
||
$em->method('getRepository')->willReturn($devisRepo);
|
||
|
||
$controller = $this->buildController($em);
|
||
|
||
$response = $controller->resend(
|
||
1,
|
||
$this->createStub(DocuSealService::class),
|
||
$this->createStub(MailerService::class),
|
||
$this->createStub(Environment::class),
|
||
$this->createStub(UrlGeneratorInterface::class),
|
||
);
|
||
|
||
$this->assertSame(302, $response->getStatusCode());
|
||
}
|
||
|
||
// ---------------------------------------------------------------
|
||
// resend – DocuSeal returns null (failure) redirects
|
||
// ---------------------------------------------------------------
|
||
|
||
public function testResendRedirectsWhenDocuSealFails(): void
|
||
{
|
||
$customer = $this->buildCustomer(5);
|
||
$devis = $this->buildDevis();
|
||
$devis->setCustomer($customer);
|
||
$devis->setUnsignedPdf('devis-file.pdf');
|
||
|
||
$devisRepo = $this->createStub(EntityRepository::class);
|
||
$devisRepo->method('find')->willReturn($devis);
|
||
|
||
$em = $this->createStub(EntityManagerInterface::class);
|
||
$em->method('getRepository')->willReturn($devisRepo);
|
||
|
||
$docuSeal = $this->createStub(DocuSealService::class);
|
||
$docuSeal->method('resendDevisSignature')->willReturn(null);
|
||
|
||
$urlGenerator = $this->createStub(UrlGeneratorInterface::class);
|
||
$urlGenerator->method('generate')->willReturn('http://localhost/devis/sign');
|
||
|
||
$controller = $this->buildController($em);
|
||
|
||
$response = $controller->resend(
|
||
1,
|
||
$docuSeal,
|
||
$this->createStub(MailerService::class),
|
||
$this->createStub(Environment::class),
|
||
$urlGenerator,
|
||
);
|
||
|
||
$this->assertSame(302, $response->getStatusCode());
|
||
}
|
||
|
||
// ---------------------------------------------------------------
|
||
// resend – success
|
||
// ---------------------------------------------------------------
|
||
|
||
public function testResendSuccessFlushesAndRedirects(): void
|
||
{
|
||
$customer = $this->buildCustomer(5);
|
||
$devis = $this->buildDevis();
|
||
$devis->setCustomer($customer);
|
||
$devis->setUnsignedPdf('devis-file.pdf');
|
||
|
||
$devisRepo = $this->createStub(EntityRepository::class);
|
||
$devisRepo->method('find')->willReturn($devis);
|
||
|
||
$em = $this->createMock(EntityManagerInterface::class);
|
||
$em->method('getRepository')->willReturn($devisRepo);
|
||
$em->expects($this->once())->method('flush');
|
||
|
||
$docuSeal = $this->createStub(DocuSealService::class);
|
||
$docuSeal->method('resendDevisSignature')->willReturn(99);
|
||
|
||
$urlGenerator = $this->createStub(UrlGeneratorInterface::class);
|
||
$urlGenerator->method('generate')->willReturn('http://localhost/devis/sign');
|
||
|
||
$twig = $this->createStub(Environment::class);
|
||
$twig->method('render')->willReturn('<html>email</html>');
|
||
|
||
$mailer = $this->createStub(MailerService::class);
|
||
|
||
$meilisearch = $this->createStub(MeilisearchService::class);
|
||
|
||
$session = new Session(new MockArraySessionStorage());
|
||
$stack = $this->createStub(RequestStack::class);
|
||
$stack->method('getSession')->willReturn($session);
|
||
|
||
$router = $this->createStub(RouterInterface::class);
|
||
$router->method('generate')->willReturn('/redirect');
|
||
|
||
$container = $this->createStub(ContainerInterface::class);
|
||
$container->method('has')->willReturnMap([
|
||
['twig', true],
|
||
['router', true],
|
||
['security.authorization_checker', true],
|
||
['security.token_storage', true],
|
||
['request_stack', true],
|
||
['parameter_bag', true],
|
||
['serializer', false],
|
||
]);
|
||
$container->method('get')->willReturnMap([
|
||
['twig', $twig],
|
||
['router', $router],
|
||
['security.authorization_checker', $this->createStub(AuthorizationCheckerInterface::class)],
|
||
['security.token_storage', $this->createStub(TokenStorageInterface::class)],
|
||
['request_stack', $stack],
|
||
['parameter_bag', $this->createStub(ParameterBagInterface::class)],
|
||
]);
|
||
|
||
$controller = new DevisController($em, $this->createStub(OrderNumberService::class), $this->createStub(DevisService::class), $meilisearch);
|
||
$controller->setContainer($container);
|
||
|
||
$response = $controller->resend(
|
||
1,
|
||
$docuSeal,
|
||
$mailer,
|
||
$twig,
|
||
$urlGenerator,
|
||
);
|
||
|
||
$this->assertSame(302, $response->getStatusCode());
|
||
$this->assertSame(Devis::STATE_SEND, $devis->getState());
|
||
$this->assertSame('99', $devis->getSubmissionId());
|
||
}
|
||
|
||
// ---------------------------------------------------------------
|
||
// createAdvert – not found
|
||
// ---------------------------------------------------------------
|
||
|
||
public function testCreateAdvertThrows404WhenDevisNotFound(): void
|
||
{
|
||
$devisRepo = $this->createStub(EntityRepository::class);
|
||
$devisRepo->method('find')->willReturn(null);
|
||
|
||
$em = $this->createStub(EntityManagerInterface::class);
|
||
$em->method('getRepository')->willReturn($devisRepo);
|
||
|
||
$controller = $this->buildController($em);
|
||
|
||
$this->expectException(NotFoundHttpException::class);
|
||
$controller->createAdvert(999, $this->createStub(AdvertService::class));
|
||
}
|
||
|
||
// ---------------------------------------------------------------
|
||
// createAdvert – wrong state (not accepted) redirects
|
||
// ---------------------------------------------------------------
|
||
|
||
public function testCreateAdvertRedirectsWhenDevisNotAccepted(): void
|
||
{
|
||
$customer = $this->buildCustomer(5);
|
||
$devis = $this->buildDevis('DV/2026-010', Devis::STATE_SEND);
|
||
$devis->setCustomer($customer);
|
||
|
||
$devisRepo = $this->createStub(EntityRepository::class);
|
||
$devisRepo->method('find')->willReturn($devis);
|
||
|
||
$em = $this->createStub(EntityManagerInterface::class);
|
||
$em->method('getRepository')->willReturn($devisRepo);
|
||
|
||
$controller = $this->buildController($em);
|
||
|
||
$response = $controller->createAdvert(1, $this->createStub(AdvertService::class));
|
||
|
||
$this->assertSame(302, $response->getStatusCode());
|
||
}
|
||
|
||
// ---------------------------------------------------------------
|
||
// createAdvert – wrong state without customer (getId = null path)
|
||
// ---------------------------------------------------------------
|
||
|
||
public function testCreateAdvertRedirectsWhenNotAcceptedAndNoCustomer(): void
|
||
{
|
||
$devis = $this->buildDevis('DV/2026-010b', Devis::STATE_CREATED);
|
||
// no customer
|
||
|
||
$devisRepo = $this->createStub(EntityRepository::class);
|
||
$devisRepo->method('find')->willReturn($devis);
|
||
|
||
$em = $this->createStub(EntityManagerInterface::class);
|
||
$em->method('getRepository')->willReturn($devisRepo);
|
||
|
||
$controller = $this->buildController($em);
|
||
|
||
$response = $controller->createAdvert(1, $this->createStub(AdvertService::class));
|
||
|
||
$this->assertSame(302, $response->getStatusCode());
|
||
}
|
||
|
||
// ---------------------------------------------------------------
|
||
// createAdvert – already has advert redirects
|
||
// ---------------------------------------------------------------
|
||
|
||
public function testCreateAdvertRedirectsWhenAdvertAlreadyExists(): void
|
||
{
|
||
$customer = $this->buildCustomer(5);
|
||
$devis = $this->buildDevis('DV/2026-011', Devis::STATE_ACCEPTED);
|
||
$devis->setCustomer($customer);
|
||
|
||
// Set an existing advert on the devis
|
||
$existingAdvert = new Advert(new OrderNumber('AP/2026-001'), 'secret');
|
||
$devis->setAdvert($existingAdvert);
|
||
|
||
$devisRepo = $this->createStub(EntityRepository::class);
|
||
$devisRepo->method('find')->willReturn($devis);
|
||
|
||
$em = $this->createStub(EntityManagerInterface::class);
|
||
$em->method('getRepository')->willReturn($devisRepo);
|
||
|
||
$controller = $this->buildController($em);
|
||
|
||
$response = $controller->createAdvert(1, $this->createStub(AdvertService::class));
|
||
|
||
$this->assertSame(302, $response->getStatusCode());
|
||
}
|
||
|
||
// ---------------------------------------------------------------
|
||
// createAdvert – already has advert without customer (null getId path)
|
||
// ---------------------------------------------------------------
|
||
|
||
public function testCreateAdvertRedirectsWhenAdvertExistsAndNoCustomer(): void
|
||
{
|
||
$devis = $this->buildDevis('DV/2026-011b', Devis::STATE_ACCEPTED);
|
||
// no customer
|
||
$existingAdvert = new Advert(new OrderNumber('AP/2026-001b'), 'secret');
|
||
$devis->setAdvert($existingAdvert);
|
||
|
||
$devisRepo = $this->createStub(EntityRepository::class);
|
||
$devisRepo->method('find')->willReturn($devis);
|
||
|
||
$em = $this->createStub(EntityManagerInterface::class);
|
||
$em->method('getRepository')->willReturn($devisRepo);
|
||
|
||
$controller = $this->buildController($em);
|
||
|
||
$response = $controller->createAdvert(1, $this->createStub(AdvertService::class));
|
||
|
||
$this->assertSame(302, $response->getStatusCode());
|
||
}
|
||
|
||
// ---------------------------------------------------------------
|
||
// createAdvert – success: creates advert with lines (all line fields)
|
||
// ---------------------------------------------------------------
|
||
|
||
public function testCreateAdvertSuccessCopiesLinesAndRedirects(): void
|
||
{
|
||
$customer = $this->buildCustomer(5);
|
||
$devis = $this->buildDevis('DV/2026-012', Devis::STATE_ACCEPTED);
|
||
$devis->setCustomer($customer);
|
||
$devis->setTotalHt('200.00');
|
||
$devis->setTotalTva('40.00');
|
||
$devis->setTotalTtc('240.00');
|
||
|
||
// Add a devis line with all optional fields set
|
||
$line = new DevisLine($devis, 'Prestation web', '200.00', 0);
|
||
$line->setDescription('Description de la prestation');
|
||
$line->setType('website');
|
||
$line->setServiceId(7);
|
||
$devis->addLine($line);
|
||
|
||
// devis->getAdvert() must be null so the guard passes
|
||
// (do NOT call $devis->setAdvert() here)
|
||
|
||
$devisRepo = $this->createStub(EntityRepository::class);
|
||
$devisRepo->method('find')->willReturn($devis);
|
||
|
||
// createFromDevis returns an advert that also links back via setDevis
|
||
$advert = new Advert(new OrderNumber('AP/2026-012'), 'secret');
|
||
$advert->setDevis($devis); // mirrors what AdvertService::create does
|
||
|
||
$advertService = $this->createStub(AdvertService::class);
|
||
$advertService->method('createFromDevis')->willReturn($advert);
|
||
|
||
$em = $this->createMock(EntityManagerInterface::class);
|
||
$em->method('getRepository')->willReturn($devisRepo);
|
||
$em->expects($this->atLeastOnce())->method('persist');
|
||
$em->expects($this->once())->method('flush');
|
||
|
||
$meilisearch = $this->createMock(MeilisearchService::class);
|
||
$meilisearch->expects($this->once())->method('indexAdvert');
|
||
$meilisearch->expects($this->once())->method('indexDevis');
|
||
|
||
$controller = $this->buildController($em, meilisearch: $meilisearch);
|
||
|
||
$response = $controller->createAdvert(1, $advertService);
|
||
|
||
$this->assertSame(302, $response->getStatusCode());
|
||
}
|
||
|
||
// ---------------------------------------------------------------
|
||
// createAdvert – success: line without optional fields (null branches)
|
||
// ---------------------------------------------------------------
|
||
|
||
public function testCreateAdvertSuccessWithLineHavingNoOptionalFields(): void
|
||
{
|
||
$customer = $this->buildCustomer(5);
|
||
$devis = $this->buildDevis('DV/2026-013', Devis::STATE_ACCEPTED);
|
||
$devis->setCustomer($customer);
|
||
$devis->setTotalHt('50.00');
|
||
$devis->setTotalTva('0.00');
|
||
$devis->setTotalTtc('50.00');
|
||
|
||
// Line with no description, no type, no serviceId
|
||
$line = new DevisLine($devis, 'Simple line', '50.00', 0);
|
||
$devis->addLine($line);
|
||
|
||
// devis->getAdvert() must be null so the guard passes
|
||
$advert = new Advert(new OrderNumber('AP/2026-013'), 'secret');
|
||
|
||
$devisRepo = $this->createStub(EntityRepository::class);
|
||
$devisRepo->method('find')->willReturn($devis);
|
||
|
||
$advertService = $this->createStub(AdvertService::class);
|
||
$advertService->method('createFromDevis')->willReturn($advert);
|
||
|
||
$em = $this->createStub(EntityManagerInterface::class);
|
||
$em->method('getRepository')->willReturn($devisRepo);
|
||
|
||
$controller = $this->buildController($em);
|
||
|
||
$response = $controller->createAdvert(1, $advertService);
|
||
|
||
$this->assertInstanceOf(Response::class, $response);
|
||
$this->assertSame(302, $response->getStatusCode());
|
||
}
|
||
|
||
// ---------------------------------------------------------------
|
||
// createAdvert – success with no customer on devis (null getId path on redirect)
|
||
// ---------------------------------------------------------------
|
||
|
||
public function testCreateAdvertSuccessWhenDevisHasNoCustomerAtEnd(): void
|
||
{
|
||
// Devis is accepted, no advert, getCustomer() returns null → redirect uses 0
|
||
$devis = $this->buildDevis('DV/2026-014', Devis::STATE_ACCEPTED);
|
||
// no customer set on devis itself; getAdvert() is null so guard passes
|
||
|
||
$advert = new Advert(new OrderNumber('AP/2026-014'), 'secret');
|
||
// do NOT call $devis->setAdvert() here
|
||
|
||
$devisRepo = $this->createStub(EntityRepository::class);
|
||
$devisRepo->method('find')->willReturn($devis);
|
||
|
||
$advertService = $this->createStub(AdvertService::class);
|
||
$advertService->method('createFromDevis')->willReturn($advert);
|
||
|
||
$em = $this->createStub(EntityManagerInterface::class);
|
||
$em->method('getRepository')->willReturn($devisRepo);
|
||
|
||
$controller = $this->buildController($em);
|
||
|
||
$response = $controller->createAdvert(1, $advertService);
|
||
|
||
$this->assertInstanceOf(Response::class, $response);
|
||
$this->assertSame(302, $response->getStatusCode());
|
||
}
|
||
|
||
// ── services ──
|
||
|
||
private function buildControllerWithEm(EntityManagerInterface $em): DevisController
|
||
{
|
||
$orderNumberService = $this->createStub(\App\Service\OrderNumberService::class);
|
||
$devisService = $this->createStub(\App\Service\DevisService::class);
|
||
$meilisearch = $this->createStub(\App\Service\MeilisearchService::class);
|
||
|
||
$controller = new DevisController($em, $orderNumberService, $devisService, $meilisearch);
|
||
|
||
$session = new Session(new MockArraySessionStorage());
|
||
$stack = $this->createStub(RequestStack::class);
|
||
$stack->method('getSession')->willReturn($session);
|
||
|
||
$twig = $this->createStub(Environment::class);
|
||
$twig->method('render')->willReturn('<html></html>');
|
||
|
||
$router = $this->createStub(RouterInterface::class);
|
||
$router->method('generate')->willReturn('/redirect');
|
||
|
||
$container = $this->createStub(ContainerInterface::class);
|
||
$container->method('has')->willReturnMap([
|
||
['twig', true],
|
||
['router', true],
|
||
['security.authorization_checker', true],
|
||
['security.token_storage', true],
|
||
['request_stack', true],
|
||
['parameter_bag', true],
|
||
['serializer', false],
|
||
]);
|
||
$container->method('get')->willReturnMap([
|
||
['twig', $twig],
|
||
['router', $router],
|
||
['security.authorization_checker', $this->createStub(AuthorizationCheckerInterface::class)],
|
||
['security.token_storage', $this->createStub(TokenStorageInterface::class)],
|
||
['request_stack', $stack],
|
||
['parameter_bag', $this->createStub(ParameterBagInterface::class)],
|
||
]);
|
||
$controller->setContainer($container);
|
||
|
||
return $controller;
|
||
}
|
||
|
||
public function testServicesCustomerNotFound(): void
|
||
{
|
||
$repo = $this->createStub(\Doctrine\ORM\EntityRepository::class);
|
||
$repo->method('find')->willReturn(null);
|
||
|
||
$em = $this->createStub(EntityManagerInterface::class);
|
||
$em->method('getRepository')->willReturn($repo);
|
||
|
||
$controller = $this->buildControllerWithEm($em);
|
||
|
||
$response = $controller->services(999, 'ndd');
|
||
$this->assertSame(200, $response->getStatusCode());
|
||
$this->assertSame('[]', $response->getContent());
|
||
}
|
||
|
||
public function testServicesNdd(): void
|
||
{
|
||
$customer = $this->createStub(\App\Entity\Customer::class);
|
||
|
||
$domain = $this->createStub(\App\Entity\Domain::class);
|
||
$domain->method('getId')->willReturn(1);
|
||
$domain->method('getFqdn')->willReturn('example.com');
|
||
|
||
$customerRepo = $this->createStub(\Doctrine\ORM\EntityRepository::class);
|
||
$customerRepo->method('find')->willReturn($customer);
|
||
|
||
$domainRepo = $this->createStub(\Doctrine\ORM\EntityRepository::class);
|
||
$domainRepo->method('findBy')->willReturn([$domain]);
|
||
|
||
$em = $this->createStub(EntityManagerInterface::class);
|
||
$em->method('getRepository')->willReturnCallback(fn ($class) => match ($class) {
|
||
\App\Entity\Customer::class => $customerRepo,
|
||
\App\Entity\Domain::class => $domainRepo,
|
||
default => $this->createStub(\Doctrine\ORM\EntityRepository::class),
|
||
});
|
||
|
||
$controller = $this->buildControllerWithEm($em);
|
||
|
||
$response = $controller->services(1, 'ndd');
|
||
$data = json_decode($response->getContent(), true);
|
||
$this->assertSame(1, $data[0]['id']);
|
||
$this->assertSame('example.com', $data[0]['label']);
|
||
}
|
||
|
||
public function testServicesWebsite(): void
|
||
{
|
||
$customer = $this->createStub(\App\Entity\Customer::class);
|
||
|
||
$website = $this->createStub(\App\Entity\Website::class);
|
||
$website->method('getId')->willReturn(5);
|
||
$website->method('getName')->willReturn('Mon Site');
|
||
$website->method('getType')->willReturn('vitrine');
|
||
|
||
$customerRepo = $this->createStub(\Doctrine\ORM\EntityRepository::class);
|
||
$customerRepo->method('find')->willReturn($customer);
|
||
|
||
$websiteRepo = $this->createStub(\Doctrine\ORM\EntityRepository::class);
|
||
$websiteRepo->method('findBy')->willReturn([$website]);
|
||
|
||
$em = $this->createStub(EntityManagerInterface::class);
|
||
$em->method('getRepository')->willReturnCallback(fn ($class) => match ($class) {
|
||
\App\Entity\Customer::class => $customerRepo,
|
||
\App\Entity\Website::class => $websiteRepo,
|
||
default => $this->createStub(\Doctrine\ORM\EntityRepository::class),
|
||
});
|
||
|
||
$controller = $this->buildControllerWithEm($em);
|
||
|
||
$response = $controller->services(1, 'website');
|
||
$data = json_decode($response->getContent(), true);
|
||
$this->assertSame(5, $data[0]['id']);
|
||
$this->assertStringContainsString('Mon Site', $data[0]['label']);
|
||
}
|
||
|
||
public function testServicesEsymail(): void
|
||
{
|
||
$customer = $this->createStub(\App\Entity\Customer::class);
|
||
|
||
$domain = $this->createStub(\App\Entity\Domain::class);
|
||
|
||
$email = $this->createStub(\App\Entity\DomainEmail::class);
|
||
$email->method('getId')->willReturn(10);
|
||
$email->method('getFullEmail')->willReturn('user@example.com');
|
||
|
||
$customerRepo = $this->createStub(\Doctrine\ORM\EntityRepository::class);
|
||
$customerRepo->method('find')->willReturn($customer);
|
||
|
||
$domainRepo = $this->createStub(\Doctrine\ORM\EntityRepository::class);
|
||
$domainRepo->method('findBy')->willReturn([$domain]);
|
||
|
||
$emailRepo = $this->createStub(\Doctrine\ORM\EntityRepository::class);
|
||
$emailRepo->method('findBy')->willReturn([$email]);
|
||
|
||
$em = $this->createStub(EntityManagerInterface::class);
|
||
$em->method('getRepository')->willReturnCallback(fn ($class) => match ($class) {
|
||
\App\Entity\Customer::class => $customerRepo,
|
||
\App\Entity\Domain::class => $domainRepo,
|
||
\App\Entity\DomainEmail::class => $emailRepo,
|
||
default => $this->createStub(\Doctrine\ORM\EntityRepository::class),
|
||
});
|
||
|
||
$controller = $this->buildControllerWithEm($em);
|
||
|
||
$response = $controller->services(1, 'esymail');
|
||
$data = json_decode($response->getContent(), true);
|
||
$this->assertSame(10, $data[0]['id']);
|
||
$this->assertSame('user@example.com', $data[0]['label']);
|
||
}
|
||
|
||
public function testServicesDefaultType(): void
|
||
{
|
||
$customer = $this->createStub(\App\Entity\Customer::class);
|
||
|
||
$customerRepo = $this->createStub(\Doctrine\ORM\EntityRepository::class);
|
||
$customerRepo->method('find')->willReturn($customer);
|
||
|
||
$em = $this->createStub(EntityManagerInterface::class);
|
||
$em->method('getRepository')->willReturn($customerRepo);
|
||
|
||
$controller = $this->buildControllerWithEm($em);
|
||
|
||
$response = $controller->services(1, 'unknown');
|
||
$this->assertSame('[]', $response->getContent());
|
||
}
|
||
|
||
// ── search ──
|
||
|
||
// ── events ──
|
||
|
||
public function testEventsNotFound(): void
|
||
{
|
||
$repo = $this->createStub(\Doctrine\ORM\EntityRepository::class);
|
||
$repo->method('find')->willReturn(null);
|
||
$em = $this->createStub(EntityManagerInterface::class);
|
||
$em->method('getRepository')->willReturn($repo);
|
||
|
||
$controller = $this->buildControllerWithEm($em);
|
||
|
||
$this->expectException(\Symfony\Component\HttpKernel\Exception\NotFoundHttpException::class);
|
||
$controller->events(999);
|
||
}
|
||
|
||
public function testEventsNoSubmitterId(): void
|
||
{
|
||
$devis = $this->createStub(\App\Entity\Devis::class);
|
||
$devis->method('getSubmissionId')->willReturn(null);
|
||
|
||
$repo = $this->createStub(\Doctrine\ORM\EntityRepository::class);
|
||
$repo->method('find')->willReturn($devis);
|
||
$em = $this->createStub(EntityManagerInterface::class);
|
||
$em->method('getRepository')->willReturn($repo);
|
||
|
||
$controller = $this->buildControllerWithEm($em);
|
||
$response = $controller->events(1);
|
||
$this->assertSame(200, $response->getStatusCode());
|
||
}
|
||
|
||
public function testEventsWithSubmitterId(): void
|
||
{
|
||
$devis = $this->createStub(\App\Entity\Devis::class);
|
||
$devis->method('getSubmissionId')->willReturn('42');
|
||
|
||
$devisRepo = $this->createStub(\Doctrine\ORM\EntityRepository::class);
|
||
$devisRepo->method('find')->willReturn($devis);
|
||
|
||
$query = $this->createStub(\Doctrine\ORM\Query::class);
|
||
$query->method('getResult')->willReturn([]);
|
||
|
||
$qb = $this->createStub(\Doctrine\ORM\QueryBuilder::class);
|
||
$qb->method('where')->willReturn($qb);
|
||
$qb->method('andWhere')->willReturn($qb);
|
||
$qb->method('setParameter')->willReturn($qb);
|
||
$qb->method('orderBy')->willReturn($qb);
|
||
$qb->method('getQuery')->willReturn($query);
|
||
|
||
$eventRepo = $this->createStub(\Doctrine\ORM\EntityRepository::class);
|
||
$eventRepo->method('createQueryBuilder')->willReturn($qb);
|
||
|
||
$em = $this->createStub(EntityManagerInterface::class);
|
||
$em->method('getRepository')->willReturnCallback(fn ($class) => match ($class) {
|
||
\App\Entity\Devis::class => $devisRepo,
|
||
\App\Entity\DocusealEvent::class => $eventRepo,
|
||
default => $this->createStub(\Doctrine\ORM\EntityRepository::class),
|
||
});
|
||
|
||
$controller = $this->buildControllerWithEm($em);
|
||
$response = $controller->events(1);
|
||
$this->assertSame(200, $response->getStatusCode());
|
||
}
|
||
|
||
// ── cancel ──
|
||
|
||
public function testCancelNotFound(): void
|
||
{
|
||
$repo = $this->createStub(\Doctrine\ORM\EntityRepository::class);
|
||
$repo->method('find')->willReturn(null);
|
||
$em = $this->createStub(EntityManagerInterface::class);
|
||
$em->method('getRepository')->willReturn($repo);
|
||
|
||
$controller = $this->buildControllerWithEm($em);
|
||
|
||
$this->expectException(\Symfony\Component\HttpKernel\Exception\NotFoundHttpException::class);
|
||
$controller->cancel(999);
|
||
}
|
||
|
||
public function testCancelAlreadyCancelled(): void
|
||
{
|
||
$on = new \App\Entity\OrderNumber('04/2026-00099');
|
||
$devis = new \App\Entity\Devis($on, 'secret');
|
||
$devis->setState(\App\Entity\Devis::STATE_CANCEL);
|
||
|
||
$repo = $this->createStub(\Doctrine\ORM\EntityRepository::class);
|
||
$repo->method('find')->willReturn($devis);
|
||
$em = $this->createStub(EntityManagerInterface::class);
|
||
$em->method('getRepository')->willReturn($repo);
|
||
|
||
$controller = $this->buildControllerWithEm($em);
|
||
$response = $controller->cancel(1);
|
||
$this->assertSame(302, $response->getStatusCode());
|
||
}
|
||
|
||
public function testCancelSuccess(): void
|
||
{
|
||
$on = new \App\Entity\OrderNumber('04/2026-00098');
|
||
$devis = new \App\Entity\Devis($on, 'secret');
|
||
$devis->setState(\App\Entity\Devis::STATE_SEND);
|
||
|
||
$repo = $this->createStub(\Doctrine\ORM\EntityRepository::class);
|
||
$repo->method('find')->willReturn($devis);
|
||
$em = $this->createMock(EntityManagerInterface::class);
|
||
$em->method('getRepository')->willReturn($repo);
|
||
$em->expects($this->once())->method('flush');
|
||
|
||
$meilisearch = $this->createMock(\App\Service\MeilisearchService::class);
|
||
$meilisearch->expects($this->once())->method('indexDevis');
|
||
|
||
$controller = new DevisController(
|
||
$em,
|
||
$this->createStub(\App\Service\OrderNumberService::class),
|
||
$this->createStub(\App\Service\DevisService::class),
|
||
$meilisearch,
|
||
);
|
||
|
||
$session = new Session(new MockArraySessionStorage());
|
||
$stack = $this->createStub(RequestStack::class);
|
||
$stack->method('getSession')->willReturn($session);
|
||
$router = $this->createStub(RouterInterface::class);
|
||
$router->method('generate')->willReturn('/redirect');
|
||
$container = $this->createStub(ContainerInterface::class);
|
||
$container->method('has')->willReturnMap([
|
||
['twig', true], ['router', true], ['security.authorization_checker', true],
|
||
['security.token_storage', true], ['request_stack', true], ['parameter_bag', true],
|
||
['serializer', false],
|
||
]);
|
||
$container->method('get')->willReturnMap([
|
||
['twig', $this->createStub(Environment::class)],
|
||
['router', $router],
|
||
['security.authorization_checker', $this->createStub(AuthorizationCheckerInterface::class)],
|
||
['security.token_storage', $this->createStub(TokenStorageInterface::class)],
|
||
['request_stack', $stack],
|
||
['parameter_bag', $this->createStub(ParameterBagInterface::class)],
|
||
]);
|
||
$controller->setContainer($container);
|
||
|
||
$response = $controller->cancel(1);
|
||
$this->assertSame(302, $response->getStatusCode());
|
||
$this->assertSame(\App\Entity\Devis::STATE_CANCEL, $devis->getState());
|
||
}
|
||
|
||
public function testCancelNoCustomer(): void
|
||
{
|
||
$on = new \App\Entity\OrderNumber('04/2026-00097');
|
||
$devis = new \App\Entity\Devis($on, 'secret');
|
||
|
||
$repo = $this->createStub(\Doctrine\ORM\EntityRepository::class);
|
||
$repo->method('find')->willReturn($devis);
|
||
$em = $this->createStub(EntityManagerInterface::class);
|
||
$em->method('getRepository')->willReturn($repo);
|
||
|
||
$controller = $this->buildControllerWithEm($em);
|
||
$response = $controller->cancel(1);
|
||
$this->assertSame(302, $response->getStatusCode());
|
||
}
|
||
|
||
// ── generatePdf ──
|
||
|
||
public function testGeneratePdfNotFound(): void
|
||
{
|
||
$repo = $this->createStub(\Doctrine\ORM\EntityRepository::class);
|
||
$repo->method('find')->willReturn(null);
|
||
$em = $this->createStub(EntityManagerInterface::class);
|
||
$em->method('getRepository')->willReturn($repo);
|
||
|
||
$controller = $this->buildControllerWithEm($em);
|
||
|
||
$this->expectException(\Symfony\Component\HttpKernel\Exception\NotFoundHttpException::class);
|
||
$controller->generatePdf(999, $this->createStub(\Symfony\Component\HttpKernel\KernelInterface::class), $this->createStub(Environment::class));
|
||
}
|
||
|
||
public function testGeneratePdfSuccess(): void
|
||
{
|
||
$on = new \App\Entity\OrderNumber('04/2026-00096');
|
||
$devis = new \App\Entity\Devis($on, 'secret');
|
||
$devis->setTotalHt('100.00');
|
||
$devis->setTotalTva('0.00');
|
||
$devis->setTotalTtc('100.00');
|
||
|
||
$repo = $this->createStub(\Doctrine\ORM\EntityRepository::class);
|
||
$repo->method('find')->willReturn($devis);
|
||
$em = $this->createMock(EntityManagerInterface::class);
|
||
$em->method('getRepository')->willReturn($repo);
|
||
$em->expects($this->once())->method('flush');
|
||
|
||
$tmpDir = sys_get_temp_dir().'/devis_pdf_ctrl_'.bin2hex(random_bytes(4));
|
||
mkdir($tmpDir.'/public/uploads/devis', 0777, true);
|
||
|
||
$kernel = $this->createStub(\Symfony\Component\HttpKernel\KernelInterface::class);
|
||
$kernel->method('getProjectDir')->willReturn($tmpDir);
|
||
|
||
$twig = $this->createStub(Environment::class);
|
||
|
||
$controller = new DevisController(
|
||
$em,
|
||
$this->createStub(\App\Service\OrderNumberService::class),
|
||
$this->createStub(\App\Service\DevisService::class),
|
||
$this->createStub(\App\Service\MeilisearchService::class),
|
||
);
|
||
|
||
$session = new Session(new MockArraySessionStorage());
|
||
$stack = $this->createStub(RequestStack::class);
|
||
$stack->method('getSession')->willReturn($session);
|
||
$router = $this->createStub(RouterInterface::class);
|
||
$router->method('generate')->willReturn('/redirect');
|
||
$container = $this->createStub(ContainerInterface::class);
|
||
$container->method('has')->willReturnMap([
|
||
['twig', true], ['router', true], ['security.authorization_checker', true],
|
||
['security.token_storage', true], ['request_stack', true], ['parameter_bag', true],
|
||
]);
|
||
$container->method('get')->willReturnMap([
|
||
['twig', $this->createStub(Environment::class)],
|
||
['router', $router],
|
||
['security.authorization_checker', $this->createStub(AuthorizationCheckerInterface::class)],
|
||
['security.token_storage', $this->createStub(TokenStorageInterface::class)],
|
||
['request_stack', $stack],
|
||
['parameter_bag', $this->createStub(ParameterBagInterface::class)],
|
||
]);
|
||
$controller->setContainer($container);
|
||
|
||
$response = $controller->generatePdf(1, $kernel, $twig);
|
||
$this->assertSame(302, $response->getStatusCode());
|
||
|
||
@array_map('unlink', glob($tmpDir.'/public/uploads/devis/*'));
|
||
@rmdir($tmpDir.'/public/uploads/devis');
|
||
@rmdir($tmpDir.'/public/uploads');
|
||
@rmdir($tmpDir.'/public');
|
||
@rmdir($tmpDir);
|
||
}
|
||
|
||
// ── search ──
|
||
|
||
public function testSearchEmptyQuery(): void
|
||
{
|
||
$em = $this->createStub(EntityManagerInterface::class);
|
||
$controller = $this->buildControllerWithEm($em);
|
||
|
||
$request = new Request(['q' => '']);
|
||
$response = $controller->search(1, $request);
|
||
$this->assertSame('[]', $response->getContent());
|
||
}
|
||
|
||
public function testSearchWhitespaceQuery(): void
|
||
{
|
||
$em = $this->createStub(EntityManagerInterface::class);
|
||
$controller = $this->buildControllerWithEm($em);
|
||
|
||
$request = new Request(['q' => ' ']);
|
||
$response = $controller->search(1, $request);
|
||
$this->assertSame('[]', $response->getContent());
|
||
}
|
||
|
||
public function testSearchWithResults(): void
|
||
{
|
||
$meilisearch = $this->createMock(\App\Service\MeilisearchService::class);
|
||
$meilisearch->method('searchDevis')->with('test', 20, 42)->willReturn([
|
||
['numOrder' => '04/2026-00001', 'customerName' => 'Client A'],
|
||
]);
|
||
|
||
$em = $this->createStub(EntityManagerInterface::class);
|
||
$controller = new DevisController(
|
||
$em,
|
||
$this->createStub(\App\Service\OrderNumberService::class),
|
||
$this->createStub(\App\Service\DevisService::class),
|
||
$meilisearch,
|
||
);
|
||
|
||
$session = new Session(new MockArraySessionStorage());
|
||
$stack = $this->createStub(RequestStack::class);
|
||
$stack->method('getSession')->willReturn($session);
|
||
|
||
$container = $this->createStub(ContainerInterface::class);
|
||
$container->method('has')->willReturnMap([
|
||
['twig', true], ['router', true], ['security.authorization_checker', true],
|
||
['security.token_storage', true], ['request_stack', true], ['parameter_bag', true],
|
||
['serializer', false],
|
||
]);
|
||
$container->method('get')->willReturnMap([
|
||
['twig', $this->createStub(Environment::class)],
|
||
['router', $this->createStub(RouterInterface::class)],
|
||
['security.authorization_checker', $this->createStub(AuthorizationCheckerInterface::class)],
|
||
['security.token_storage', $this->createStub(TokenStorageInterface::class)],
|
||
['request_stack', $stack],
|
||
['parameter_bag', $this->createStub(ParameterBagInterface::class)],
|
||
]);
|
||
$controller->setContainer($container);
|
||
|
||
$request = new Request(['q' => 'test']);
|
||
$response = $controller->search(42, $request);
|
||
|
||
$data = json_decode($response->getContent(), true);
|
||
$this->assertCount(1, $data);
|
||
$this->assertSame('04/2026-00001', $data[0]['numOrder']);
|
||
}
|
||
}
|