DevisControllerTest : 35 tests (create GET/POST, edit GET/POST, generatePdf 404, send/resend guards + success, createAdvert guards + success, createDevisLine all branches, sendDevisSignEmail indirect) 1387 PHP tests, 115 JS tests Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1217 lines
45 KiB
PHP
1217 lines
45 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());
|
||
}
|
||
}
|