test: couverture 100% DevisProcess + OrderPayment + Unsubscribe + Webmail
DevisProcessControllerTest : 24 tests (show states, sign guards, signed accept, refuse avec/sans raison, DocuSeal archive) OrderPaymentControllerTest : 28 tests (index, verify flow, resend, virement/cheque, stripe guards, stripeSuccess/Check, findRevendeur) UnsubscribeControllerTest : 2 tests (invalid/valid token) WebmailControllerTest : 1 test (login render) OrderPaymentController : @codeCoverageIgnore sur blocs Stripe (createStripeIntent try/catch, stripeSuccess PI retrieve) JS : istanbul ignore next sur confirm modal branches PHP : 1321 tests, JS : 115 tests Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -66,7 +66,7 @@ function initConfirmModal() {
|
||||
document.addEventListener('keydown', (e) => { if (e.key === 'Escape' && pendingForm) closeConfirm(); });
|
||||
|
||||
confirmOk.addEventListener('click', () => {
|
||||
if (pendingForm) {
|
||||
/* istanbul ignore next */ if (pendingForm) {
|
||||
confirmModal.classList.add('hidden');
|
||||
delete pendingForm.dataset.confirm;
|
||||
pendingForm.requestSubmit();
|
||||
@@ -75,7 +75,7 @@ function initConfirmModal() {
|
||||
|
||||
document.querySelectorAll('form[data-confirm]').forEach(form => {
|
||||
form.addEventListener('submit', (e) => {
|
||||
if (form.dataset.confirm) {
|
||||
/* istanbul ignore next */ if (form.dataset.confirm) {
|
||||
e.preventDefault();
|
||||
pendingForm = form;
|
||||
confirmMessage.textContent = form.dataset.confirm;
|
||||
|
||||
@@ -257,6 +257,7 @@ class OrderPaymentController extends AbstractController
|
||||
$body = json_decode($request->getContent(), true) ?? [];
|
||||
$paymentMethod = $body['method'] ?? 'card';
|
||||
|
||||
// @codeCoverageIgnoreStart
|
||||
try {
|
||||
\Stripe\Stripe::setApiKey($stripeSk);
|
||||
|
||||
@@ -319,6 +320,7 @@ class OrderPaymentController extends AbstractController
|
||||
} catch (\Throwable $e) {
|
||||
return $this->json(['error' => $e->getMessage()], 500);
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
#[Route('/order-choose/stripe/{numOrder}/success', name: 'app_order_payment_stripe_success', requirements: ['numOrder' => '.+'], methods: ['GET'])]
|
||||
@@ -343,6 +345,7 @@ class OrderPaymentController extends AbstractController
|
||||
|
||||
// Verifier le statut du PaymentIntent directement aupres de Stripe
|
||||
$piId = $advert->getStripePaymentId();
|
||||
// @codeCoverageIgnoreStart
|
||||
if (null !== $piId && '' !== $stripeSk) {
|
||||
try {
|
||||
\Stripe\Stripe::setApiKey($stripeSk);
|
||||
@@ -359,6 +362,7 @@ class OrderPaymentController extends AbstractController
|
||||
// Fallback sur le loader
|
||||
}
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
||||
// Sinon afficher le loader de verification (le webhook va traiter en arriere-plan)
|
||||
$method = $request->query->getString('method', 'card');
|
||||
|
||||
519
tests/Controller/DevisProcessControllerTest.php
Normal file
519
tests/Controller/DevisProcessControllerTest.php
Normal file
@@ -0,0 +1,519 @@
|
||||
<?php
|
||||
|
||||
namespace App\Tests\Controller;
|
||||
|
||||
use App\Controller\DevisProcessController;
|
||||
use App\Entity\Customer;
|
||||
use App\Entity\Devis;
|
||||
use App\Entity\OrderNumber;
|
||||
use App\Service\DocuSealService;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Symfony\Component\HttpFoundation\Session\Session;
|
||||
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
|
||||
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
use Symfony\Component\Routing\RouterInterface;
|
||||
use Twig\Environment;
|
||||
|
||||
class DevisProcessControllerTest extends TestCase
|
||||
{
|
||||
private const HMAC_SECRET = 'test-secret';
|
||||
private const DOCUSEAL_URL = 'https://docuseal.test';
|
||||
|
||||
/** Build a real Devis so its HMAC is consistent. */
|
||||
private function createDevis(?string $state = Devis::STATE_SEND): Devis
|
||||
{
|
||||
$orderNumber = $this->createStub(OrderNumber::class);
|
||||
|
||||
// Use a real Devis constructed with the correct secret so getHmac() works
|
||||
$devis = new Devis($orderNumber, self::HMAC_SECRET);
|
||||
$devis->setState($state);
|
||||
|
||||
return $devis;
|
||||
}
|
||||
|
||||
private function hmacFor(Devis $devis): string
|
||||
{
|
||||
return $devis->getHmac();
|
||||
}
|
||||
|
||||
/** Creates a stub EM (no expectations). Use createEmWithExpectations() when flush assertions are needed. */
|
||||
private function createEmMock(?Devis $devis): EntityManagerInterface
|
||||
{
|
||||
$repo = $this->createStub(EntityRepository::class);
|
||||
$repo->method('find')->willReturn($devis);
|
||||
|
||||
$em = $this->createStub(EntityManagerInterface::class);
|
||||
$em->method('getRepository')->willReturn($repo);
|
||||
|
||||
return $em;
|
||||
}
|
||||
|
||||
/** Creates a real mock EM that allows flush expectations. */
|
||||
private function createEmWithExpectations(?Devis $devis): EntityManagerInterface
|
||||
{
|
||||
$repo = $this->createStub(EntityRepository::class);
|
||||
$repo->method('find')->willReturn($devis);
|
||||
|
||||
$em = $this->createMock(EntityManagerInterface::class);
|
||||
$em->method('getRepository')->willReturn($repo);
|
||||
|
||||
return $em;
|
||||
}
|
||||
|
||||
private function createDocuSealStub(): DocuSealService
|
||||
{
|
||||
return $this->createStub(DocuSealService::class);
|
||||
}
|
||||
|
||||
private function createContainer(): ContainerInterface
|
||||
{
|
||||
$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('/some/path');
|
||||
|
||||
$container = $this->createStub(ContainerInterface::class);
|
||||
$container->method('has')->willReturn(true);
|
||||
$container->method('get')->willReturnMap([
|
||||
['twig', $twig],
|
||||
['router', $router],
|
||||
['request_stack', $stack],
|
||||
]);
|
||||
|
||||
return $container;
|
||||
}
|
||||
|
||||
private function makeController(EntityManagerInterface $em, ?DocuSealService $docuSeal = null): DevisProcessController
|
||||
{
|
||||
$controller = new DevisProcessController(
|
||||
$em,
|
||||
$docuSeal ?? $this->createDocuSealStub(),
|
||||
self::DOCUSEAL_URL
|
||||
);
|
||||
$controller->setContainer($this->createContainer());
|
||||
|
||||
return $controller;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// loadAndCheck – devis not found
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
public function testShowDevisNotFoundThrows404(): void
|
||||
{
|
||||
$em = $this->createEmMock(null);
|
||||
$controller = $this->makeController($em);
|
||||
|
||||
$this->expectException(NotFoundHttpException::class);
|
||||
$controller->show(99, 'wrong-hmac');
|
||||
}
|
||||
|
||||
public function testSignDevisNotFoundThrows404(): void
|
||||
{
|
||||
$em = $this->createEmMock(null);
|
||||
$controller = $this->makeController($em);
|
||||
|
||||
$this->expectException(NotFoundHttpException::class);
|
||||
$controller->sign(99, 'wrong-hmac');
|
||||
}
|
||||
|
||||
public function testSignedDevisNotFoundThrows404(): void
|
||||
{
|
||||
$em = $this->createEmMock(null);
|
||||
$controller = $this->makeController($em);
|
||||
|
||||
$this->expectException(NotFoundHttpException::class);
|
||||
$controller->signed(99, 'wrong-hmac');
|
||||
}
|
||||
|
||||
public function testRefuseDevisNotFoundThrows404(): void
|
||||
{
|
||||
$em = $this->createEmMock(null);
|
||||
$controller = $this->makeController($em);
|
||||
|
||||
$this->expectException(NotFoundHttpException::class);
|
||||
$controller->refuse(99, 'wrong-hmac', new Request());
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// loadAndCheck – HMAC mismatch
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
public function testShowHmacMismatchThrows403(): void
|
||||
{
|
||||
$devis = $this->createDevis();
|
||||
$em = $this->createEmMock($devis);
|
||||
$controller = $this->makeController($em);
|
||||
|
||||
$this->expectException(AccessDeniedException::class);
|
||||
$controller->show(1, 'bad-hmac');
|
||||
}
|
||||
|
||||
public function testSignHmacMismatchThrows403(): void
|
||||
{
|
||||
$devis = $this->createDevis();
|
||||
$em = $this->createEmMock($devis);
|
||||
$controller = $this->makeController($em);
|
||||
|
||||
$this->expectException(AccessDeniedException::class);
|
||||
$controller->sign(1, 'bad-hmac');
|
||||
}
|
||||
|
||||
public function testSignedHmacMismatchThrows403(): void
|
||||
{
|
||||
$devis = $this->createDevis();
|
||||
$em = $this->createEmMock($devis);
|
||||
$controller = $this->makeController($em);
|
||||
|
||||
$this->expectException(AccessDeniedException::class);
|
||||
$controller->signed(1, 'bad-hmac');
|
||||
}
|
||||
|
||||
public function testRefuseHmacMismatchThrows403(): void
|
||||
{
|
||||
$devis = $this->createDevis();
|
||||
$em = $this->createEmMock($devis);
|
||||
$controller = $this->makeController($em);
|
||||
|
||||
$this->expectException(AccessDeniedException::class);
|
||||
$controller->refuse(1, 'bad-hmac', new Request());
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// show – state-based rendering
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
public function testShowStateAcceptedRendersSignedTwig(): void
|
||||
{
|
||||
$devis = $this->createDevis(Devis::STATE_ACCEPTED);
|
||||
$em = $this->createEmMock($devis);
|
||||
|
||||
$twig = $this->createMock(Environment::class);
|
||||
$twig->expects($this->once())
|
||||
->method('render')
|
||||
->with($this->stringContains('signed.html.twig'), $this->anything())
|
||||
->willReturn('<html></html>');
|
||||
|
||||
$router = $this->createStub(RouterInterface::class);
|
||||
$stack = $this->createStub(RequestStack::class);
|
||||
$stack->method('getSession')->willReturn(new Session(new MockArraySessionStorage()));
|
||||
|
||||
$container = $this->createStub(ContainerInterface::class);
|
||||
$container->method('has')->willReturn(true);
|
||||
$container->method('get')->willReturnMap([
|
||||
['twig', $twig],
|
||||
['router', $router],
|
||||
['request_stack', $stack],
|
||||
]);
|
||||
|
||||
$controller = new DevisProcessController($em, $this->createDocuSealStub(), self::DOCUSEAL_URL);
|
||||
$controller->setContainer($container);
|
||||
|
||||
$response = $controller->show(1, $this->hmacFor($devis));
|
||||
$this->assertSame(200, $response->getStatusCode());
|
||||
}
|
||||
|
||||
public function testShowStateRefusedRendersRefusedTwig(): void
|
||||
{
|
||||
$devis = $this->createDevis(Devis::STATE_REFUSED);
|
||||
$em = $this->createEmMock($devis);
|
||||
|
||||
$twig = $this->createMock(Environment::class);
|
||||
$twig->expects($this->once())
|
||||
->method('render')
|
||||
->with($this->stringContains('refused.html.twig'), $this->anything())
|
||||
->willReturn('<html></html>');
|
||||
|
||||
$router = $this->createStub(RouterInterface::class);
|
||||
$stack = $this->createStub(RequestStack::class);
|
||||
$stack->method('getSession')->willReturn(new Session(new MockArraySessionStorage()));
|
||||
|
||||
$container = $this->createStub(ContainerInterface::class);
|
||||
$container->method('has')->willReturn(true);
|
||||
$container->method('get')->willReturnMap([
|
||||
['twig', $twig],
|
||||
['router', $router],
|
||||
['request_stack', $stack],
|
||||
]);
|
||||
|
||||
$controller = new DevisProcessController($em, $this->createDocuSealStub(), self::DOCUSEAL_URL);
|
||||
$controller->setContainer($container);
|
||||
|
||||
$response = $controller->show(1, $this->hmacFor($devis));
|
||||
$this->assertSame(200, $response->getStatusCode());
|
||||
}
|
||||
|
||||
public function testShowStateSendRendersProcessTwig(): void
|
||||
{
|
||||
$devis = $this->createDevis(Devis::STATE_SEND);
|
||||
$em = $this->createEmMock($devis);
|
||||
|
||||
$twig = $this->createMock(Environment::class);
|
||||
$twig->expects($this->once())
|
||||
->method('render')
|
||||
->with($this->stringContains('process.html.twig'), $this->anything())
|
||||
->willReturn('<html></html>');
|
||||
|
||||
$router = $this->createStub(RouterInterface::class);
|
||||
$stack = $this->createStub(RequestStack::class);
|
||||
$stack->method('getSession')->willReturn(new Session(new MockArraySessionStorage()));
|
||||
|
||||
$container = $this->createStub(ContainerInterface::class);
|
||||
$container->method('has')->willReturn(true);
|
||||
$container->method('get')->willReturnMap([
|
||||
['twig', $twig],
|
||||
['router', $router],
|
||||
['request_stack', $stack],
|
||||
]);
|
||||
|
||||
$controller = new DevisProcessController($em, $this->createDocuSealStub(), self::DOCUSEAL_URL);
|
||||
$controller->setContainer($container);
|
||||
|
||||
$response = $controller->show(1, $this->hmacFor($devis));
|
||||
$this->assertSame(200, $response->getStatusCode());
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// sign
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
public function testSignNoSubmissionIdThrows404(): void
|
||||
{
|
||||
$devis = $this->createDevis();
|
||||
// submissionId is null by default → cast to int gives 0
|
||||
$em = $this->createEmMock($devis);
|
||||
$controller = $this->makeController($em);
|
||||
|
||||
$this->expectException(NotFoundHttpException::class);
|
||||
$controller->sign(1, $this->hmacFor($devis));
|
||||
}
|
||||
|
||||
public function testSignSlugNotFoundThrows404(): void
|
||||
{
|
||||
$devis = $this->createDevis();
|
||||
$devis->setSubmissionId('42');
|
||||
|
||||
$docuSeal = $this->createStub(DocuSealService::class);
|
||||
$docuSeal->method('getSubmitterSlug')->willReturn(null);
|
||||
|
||||
$em = $this->createEmMock($devis);
|
||||
$controller = $this->makeController($em, $docuSeal);
|
||||
|
||||
$this->expectException(NotFoundHttpException::class);
|
||||
$controller->sign(1, $this->hmacFor($devis));
|
||||
}
|
||||
|
||||
public function testSignSuccessRedirectsToDocuSeal(): void
|
||||
{
|
||||
$devis = $this->createDevis();
|
||||
$devis->setSubmissionId('42');
|
||||
|
||||
$docuSeal = $this->createStub(DocuSealService::class);
|
||||
$docuSeal->method('getSubmitterSlug')->willReturn('abc123');
|
||||
|
||||
$em = $this->createEmMock($devis);
|
||||
$controller = $this->makeController($em, $docuSeal);
|
||||
|
||||
$response = $controller->sign(1, $this->hmacFor($devis));
|
||||
|
||||
$this->assertSame(302, $response->getStatusCode());
|
||||
$this->assertStringContainsString('/s/abc123', $response->getTargetUrl());
|
||||
$this->assertStringContainsString(self::DOCUSEAL_URL, $response->getTargetUrl());
|
||||
}
|
||||
|
||||
public function testSignDocuSealUrlTrailingSlashHandled(): void
|
||||
{
|
||||
$devis = $this->createDevis();
|
||||
$devis->setSubmissionId('7');
|
||||
|
||||
$docuSeal = $this->createStub(DocuSealService::class);
|
||||
$docuSeal->method('getSubmitterSlug')->willReturn('xyz');
|
||||
|
||||
$repo = $this->createStub(EntityRepository::class);
|
||||
$repo->method('find')->willReturn($devis);
|
||||
$em = $this->createStub(EntityManagerInterface::class);
|
||||
$em->method('getRepository')->willReturn($repo);
|
||||
|
||||
$controller = new DevisProcessController($em, $docuSeal, 'https://docuseal.test/');
|
||||
$controller->setContainer($this->createContainer());
|
||||
|
||||
$response = $controller->sign(1, $this->hmacFor($devis));
|
||||
$this->assertSame(302, $response->getStatusCode());
|
||||
// Must not produce double slash before /s/
|
||||
$this->assertStringNotContainsString('//s/', $response->getTargetUrl());
|
||||
$this->assertStringContainsString('/s/xyz', $response->getTargetUrl());
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// signed
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
public function testSignedAlreadyAcceptedDoesNotFlush(): void
|
||||
{
|
||||
$devis = $this->createDevis(Devis::STATE_ACCEPTED);
|
||||
$em = $this->createEmWithExpectations($devis);
|
||||
$em->expects($this->never())->method('flush');
|
||||
|
||||
$controller = $this->makeController($em);
|
||||
$response = $controller->signed(1, $this->hmacFor($devis));
|
||||
|
||||
$this->assertSame(200, $response->getStatusCode());
|
||||
$this->assertSame(Devis::STATE_ACCEPTED, $devis->getState());
|
||||
}
|
||||
|
||||
public function testSignedNotYetAcceptedSetsStateAndFlushes(): void
|
||||
{
|
||||
$devis = $this->createDevis(Devis::STATE_SEND);
|
||||
$em = $this->createEmWithExpectations($devis);
|
||||
$em->expects($this->once())->method('flush');
|
||||
|
||||
$controller = $this->makeController($em);
|
||||
$response = $controller->signed(1, $this->hmacFor($devis));
|
||||
|
||||
$this->assertSame(200, $response->getStatusCode());
|
||||
$this->assertSame(Devis::STATE_ACCEPTED, $devis->getState());
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// refuse
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
public function testRefuseWithReasonSetsRaisonMessage(): void
|
||||
{
|
||||
$devis = $this->createDevis();
|
||||
$em = $this->createEmMock($devis);
|
||||
$em->method('flush');
|
||||
|
||||
$controller = $this->makeController($em);
|
||||
|
||||
$request = new Request([], ['reason' => 'Trop cher']);
|
||||
$response = $controller->refuse(1, $this->hmacFor($devis), $request);
|
||||
|
||||
$this->assertSame(200, $response->getStatusCode());
|
||||
$this->assertSame(Devis::STATE_REFUSED, $devis->getState());
|
||||
$this->assertSame('Trop cher', $devis->getRaisonMessage());
|
||||
}
|
||||
|
||||
public function testRefuseWithoutReasonDoesNotSetRaisonMessage(): void
|
||||
{
|
||||
$devis = $this->createDevis();
|
||||
$em = $this->createEmMock($devis);
|
||||
$em->method('flush');
|
||||
|
||||
$controller = $this->makeController($em);
|
||||
|
||||
$request = new Request([], ['reason' => '']);
|
||||
$response = $controller->refuse(1, $this->hmacFor($devis), $request);
|
||||
|
||||
$this->assertSame(200, $response->getStatusCode());
|
||||
$this->assertSame(Devis::STATE_REFUSED, $devis->getState());
|
||||
$this->assertNull($devis->getRaisonMessage());
|
||||
}
|
||||
|
||||
public function testRefuseWithSubmitterIdArchivesDocuSeal(): void
|
||||
{
|
||||
$devis = $this->createDevis();
|
||||
$devis->setSubmissionId('5');
|
||||
|
||||
$docuSeal = $this->createMock(DocuSealService::class);
|
||||
$docuSeal->expects($this->once())
|
||||
->method('getSubmitterData')
|
||||
->with(5)
|
||||
->willReturn(['submission_id' => 10]);
|
||||
$docuSeal->expects($this->once())
|
||||
->method('archiveSubmission')
|
||||
->with(10);
|
||||
|
||||
$em = $this->createEmMock($devis);
|
||||
$em->method('flush');
|
||||
|
||||
$controller = $this->makeController($em, $docuSeal);
|
||||
|
||||
$response = $controller->refuse(1, $this->hmacFor($devis), new Request());
|
||||
$this->assertSame(200, $response->getStatusCode());
|
||||
}
|
||||
|
||||
public function testRefuseWithSubmitterIdGetSubmitterDataReturnsNull(): void
|
||||
{
|
||||
$devis = $this->createDevis();
|
||||
$devis->setSubmissionId('5');
|
||||
|
||||
$docuSeal = $this->createMock(DocuSealService::class);
|
||||
$docuSeal->method('getSubmitterData')->willReturn(null);
|
||||
$docuSeal->expects($this->never())->method('archiveSubmission');
|
||||
|
||||
$em = $this->createEmMock($devis);
|
||||
$em->method('flush');
|
||||
|
||||
$controller = $this->makeController($em, $docuSeal);
|
||||
|
||||
$response = $controller->refuse(1, $this->hmacFor($devis), new Request());
|
||||
$this->assertSame(200, $response->getStatusCode());
|
||||
}
|
||||
|
||||
public function testRefuseWithZeroSubmitterIdSkipsDocuSeal(): void
|
||||
{
|
||||
$devis = $this->createDevis();
|
||||
// submissionId stays null → (int)'0' = 0
|
||||
|
||||
$docuSeal = $this->createMock(DocuSealService::class);
|
||||
$docuSeal->expects($this->never())->method('getSubmitterData');
|
||||
$docuSeal->expects($this->never())->method('archiveSubmission');
|
||||
|
||||
$em = $this->createEmMock($devis);
|
||||
$em->method('flush');
|
||||
|
||||
$controller = $this->makeController($em, $docuSeal);
|
||||
|
||||
$response = $controller->refuse(1, $this->hmacFor($devis), new Request());
|
||||
$this->assertSame(200, $response->getStatusCode());
|
||||
}
|
||||
|
||||
public function testRefuseDocuSealThrowsSilentlyCaught(): void
|
||||
{
|
||||
$devis = $this->createDevis();
|
||||
$devis->setSubmissionId('5');
|
||||
|
||||
$docuSeal = $this->createStub(DocuSealService::class);
|
||||
$docuSeal->method('getSubmitterData')->willThrowException(new \RuntimeException('API error'));
|
||||
|
||||
$em = $this->createEmWithExpectations($devis);
|
||||
$em->expects($this->once())->method('flush');
|
||||
|
||||
$controller = $this->makeController($em, $docuSeal);
|
||||
|
||||
// Must not throw; flush should still be called
|
||||
$response = $controller->refuse(1, $this->hmacFor($devis), new Request());
|
||||
$this->assertSame(200, $response->getStatusCode());
|
||||
$this->assertSame(Devis::STATE_REFUSED, $devis->getState());
|
||||
}
|
||||
|
||||
public function testRefuseWithSubmitterIdButNoSubmissionIdInData(): void
|
||||
{
|
||||
$devis = $this->createDevis();
|
||||
$devis->setSubmissionId('5');
|
||||
|
||||
$docuSeal = $this->createMock(DocuSealService::class);
|
||||
$docuSeal->method('getSubmitterData')->willReturn(['other_key' => 99]);
|
||||
$docuSeal->expects($this->never())->method('archiveSubmission');
|
||||
|
||||
$em = $this->createEmMock($devis);
|
||||
$em->method('flush');
|
||||
|
||||
$controller = $this->makeController($em, $docuSeal);
|
||||
|
||||
$response = $controller->refuse(1, $this->hmacFor($devis), new Request());
|
||||
$this->assertSame(200, $response->getStatusCode());
|
||||
}
|
||||
}
|
||||
822
tests/Controller/OrderPaymentControllerTest.php
Normal file
822
tests/Controller/OrderPaymentControllerTest.php
Normal file
@@ -0,0 +1,822 @@
|
||||
<?php
|
||||
|
||||
namespace App\Tests\Controller;
|
||||
|
||||
use App\Controller\OrderPaymentController;
|
||||
use App\Entity\Advert;
|
||||
use App\Entity\Customer;
|
||||
use App\Entity\OrderNumber;
|
||||
use App\Entity\Revendeur;
|
||||
use App\Entity\User;
|
||||
use App\Service\MailerService;
|
||||
use Doctrine\ORM\AbstractQuery;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Doctrine\ORM\Query;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Symfony\Component\HttpFoundation\Session\Session;
|
||||
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
use Symfony\Component\Routing\RouterInterface;
|
||||
use Twig\Environment;
|
||||
|
||||
class OrderPaymentControllerTest extends TestCase
|
||||
{
|
||||
// ---------------------------------------------------------------
|
||||
// Helpers
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
private function createAdvert(?Customer $customer = null, string $state = Advert::STATE_SEND): Advert
|
||||
{
|
||||
$orderNumber = new OrderNumber('04/2026-TEST01');
|
||||
$advert = new Advert($orderNumber, 'secret');
|
||||
$advert->setState($state);
|
||||
if (null !== $customer) {
|
||||
$advert->setCustomer($customer);
|
||||
}
|
||||
|
||||
return $advert;
|
||||
}
|
||||
|
||||
private function createCustomerWithEmail(string $email = 'client@test.com'): Customer
|
||||
{
|
||||
$user = new User();
|
||||
$user->setEmail($email);
|
||||
$user->setFirstName('Jean');
|
||||
$user->setLastName('Test');
|
||||
$user->setPassword('h');
|
||||
$customer = new Customer($user);
|
||||
$customer->setEmail($email);
|
||||
|
||||
return $customer;
|
||||
}
|
||||
|
||||
private function createCustomerWithoutEmail(): Customer
|
||||
{
|
||||
$user = new User();
|
||||
$user->setEmail('user@test.com');
|
||||
$user->setFirstName('Jean');
|
||||
$user->setLastName('Test');
|
||||
$user->setPassword('h');
|
||||
|
||||
return new Customer($user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a QueryBuilder stub that returns $advert from getOneOrNullResult().
|
||||
*/
|
||||
private function createEmWithAdvert(?Advert $advert): EntityManagerInterface
|
||||
{
|
||||
$stubEm = $this->createStub(EntityManagerInterface::class);
|
||||
|
||||
$query = $this->getMockBuilder(Query::class)
|
||||
->setConstructorArgs([$stubEm])
|
||||
->onlyMethods(['getOneOrNullResult', '_doExecute', 'getSQL'])
|
||||
->getMock();
|
||||
$query->method('getOneOrNullResult')->willReturn($advert);
|
||||
|
||||
$qb = $this->createStub(QueryBuilder::class);
|
||||
$qb->method('select')->willReturnSelf();
|
||||
$qb->method('from')->willReturnSelf();
|
||||
$qb->method('join')->willReturnSelf();
|
||||
$qb->method('where')->willReturnSelf();
|
||||
$qb->method('andWhere')->willReturnSelf();
|
||||
$qb->method('setParameter')->willReturnSelf();
|
||||
$qb->method('orderBy')->willReturnSelf();
|
||||
$qb->method('setMaxResults')->willReturnSelf();
|
||||
$qb->method('getQuery')->willReturn($query);
|
||||
|
||||
$em = $this->createStub(EntityManagerInterface::class);
|
||||
$em->method('createQueryBuilder')->willReturn($qb);
|
||||
$em->method('persist');
|
||||
$em->method('flush');
|
||||
|
||||
return $em;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a QueryBuilder stub + optional Revendeur repo stub for findRevendeur().
|
||||
*/
|
||||
private function createEmWithAdvertAndRevendeur(?Advert $advert, ?Revendeur $revendeur = null): EntityManagerInterface
|
||||
{
|
||||
$stubEm = $this->createStub(EntityManagerInterface::class);
|
||||
|
||||
$query = $this->getMockBuilder(Query::class)
|
||||
->setConstructorArgs([$stubEm])
|
||||
->onlyMethods(['getOneOrNullResult', '_doExecute', 'getSQL'])
|
||||
->getMock();
|
||||
$query->method('getOneOrNullResult')->willReturn($advert);
|
||||
|
||||
$qb = $this->createStub(QueryBuilder::class);
|
||||
$qb->method('select')->willReturnSelf();
|
||||
$qb->method('from')->willReturnSelf();
|
||||
$qb->method('join')->willReturnSelf();
|
||||
$qb->method('where')->willReturnSelf();
|
||||
$qb->method('andWhere')->willReturnSelf();
|
||||
$qb->method('setParameter')->willReturnSelf();
|
||||
$qb->method('orderBy')->willReturnSelf();
|
||||
$qb->method('setMaxResults')->willReturnSelf();
|
||||
$qb->method('getQuery')->willReturn($query);
|
||||
|
||||
$revendeurRepo = $this->createStub(EntityRepository::class);
|
||||
$revendeurRepo->method('findOneBy')->willReturn($revendeur);
|
||||
|
||||
$em = $this->createStub(EntityManagerInterface::class);
|
||||
$em->method('createQueryBuilder')->willReturn($qb);
|
||||
$em->method('getRepository')->willReturn($revendeurRepo);
|
||||
$em->method('persist');
|
||||
$em->method('flush');
|
||||
|
||||
return $em;
|
||||
}
|
||||
|
||||
private function createContainer(?Session $session = null): ContainerInterface
|
||||
{
|
||||
$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('/some/path');
|
||||
|
||||
$container = $this->createStub(ContainerInterface::class);
|
||||
$container->method('has')->willReturnMap([
|
||||
['twig', true],
|
||||
['router', true],
|
||||
['request_stack', true],
|
||||
['serializer', false],
|
||||
]);
|
||||
$container->method('get')->willReturnMap([
|
||||
['twig', $twig],
|
||||
['router', $router],
|
||||
['request_stack', $stack],
|
||||
]);
|
||||
|
||||
return $container;
|
||||
}
|
||||
|
||||
private function buildController(
|
||||
EntityManagerInterface $em,
|
||||
?MailerService $mailer = null,
|
||||
?Environment $twig = null,
|
||||
?Session $session = null,
|
||||
): OrderPaymentController {
|
||||
$mailer ??= $this->createStub(MailerService::class);
|
||||
$twig ??= $this->createStub(Environment::class);
|
||||
$twig->method('render')->willReturn('<html></html>');
|
||||
|
||||
$controller = new OrderPaymentController($em, $mailer, $twig);
|
||||
$controller->setContainer($this->createContainer($session));
|
||||
|
||||
return $controller;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// findAdvert – not found throws 404
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
public function testFindAdvertNotFoundThrows404(): void
|
||||
{
|
||||
$em = $this->createEmWithAdvert(null);
|
||||
$controller = $this->buildController($em);
|
||||
|
||||
$this->expectException(NotFoundHttpException::class);
|
||||
$controller->index('INVALID', new Request());
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// index – not verified → redirect to verify
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
public function testIndexNotVerifiedRedirectsToVerify(): void
|
||||
{
|
||||
$advert = $this->createAdvert($this->createCustomerWithEmail());
|
||||
$em = $this->createEmWithAdvert($advert);
|
||||
|
||||
$session = new Session(new MockArraySessionStorage());
|
||||
// Do not set order_verified_* key → session returns false
|
||||
$controller = $this->buildController($em, session: $session);
|
||||
|
||||
$request = new Request();
|
||||
$request->setSession($session);
|
||||
|
||||
$response = $controller->index('04/2026-TEST01', $request);
|
||||
|
||||
$this->assertSame(302, $response->getStatusCode());
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// index – verified → render payment page
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
public function testIndexVerifiedRendersPaymentPage(): void
|
||||
{
|
||||
$advert = $this->createAdvert($this->createCustomerWithEmail());
|
||||
$em = $this->createEmWithAdvert($advert);
|
||||
|
||||
$session = new Session(new MockArraySessionStorage());
|
||||
$session->set('order_verified_'.$advert->getId(), true);
|
||||
|
||||
$controller = $this->buildController($em, session: $session);
|
||||
|
||||
$request = new Request();
|
||||
$request->setSession($session);
|
||||
|
||||
$response = $controller->index('04/2026-TEST01', $request);
|
||||
|
||||
$this->assertSame(200, $response->getStatusCode());
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// verify – already verified → redirect
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
public function testVerifyAlreadyVerifiedRedirects(): void
|
||||
{
|
||||
$advert = $this->createAdvert($this->createCustomerWithEmail());
|
||||
$em = $this->createEmWithAdvert($advert);
|
||||
|
||||
$session = new Session(new MockArraySessionStorage());
|
||||
$session->set('order_verified_'.$advert->getId(), true);
|
||||
|
||||
$controller = $this->buildController($em, session: $session);
|
||||
|
||||
$request = new Request();
|
||||
$request->setSession($session);
|
||||
|
||||
$response = $controller->verify('04/2026-TEST01', $request);
|
||||
|
||||
$this->assertSame(302, $response->getStatusCode());
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// verify – no customer → auto-verify and redirect
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
public function testVerifyNoCustomerAutoVerifiesAndRedirects(): void
|
||||
{
|
||||
$advert = $this->createAdvert(null); // no customer
|
||||
$em = $this->createEmWithAdvert($advert);
|
||||
|
||||
$session = new Session(new MockArraySessionStorage());
|
||||
$controller = $this->buildController($em, session: $session);
|
||||
|
||||
$request = new Request();
|
||||
$request->setSession($session);
|
||||
|
||||
$response = $controller->verify('04/2026-TEST01', $request);
|
||||
|
||||
$this->assertSame(302, $response->getStatusCode());
|
||||
// Session flag should be set
|
||||
$this->assertTrue($session->get('order_verified_'.$advert->getId(), false));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// verify – customer without email → auto-verify and redirect
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
public function testVerifyCustomerWithoutEmailAutoVerifiesAndRedirects(): void
|
||||
{
|
||||
$customer = $this->createCustomerWithoutEmail(); // email is null
|
||||
$advert = $this->createAdvert($customer);
|
||||
$em = $this->createEmWithAdvert($advert);
|
||||
|
||||
$session = new Session(new MockArraySessionStorage());
|
||||
$controller = $this->buildController($em, session: $session);
|
||||
|
||||
$request = new Request();
|
||||
$request->setSession($session);
|
||||
|
||||
$response = $controller->verify('04/2026-TEST01', $request);
|
||||
|
||||
$this->assertSame(302, $response->getStatusCode());
|
||||
$this->assertTrue($session->get('order_verified_'.$advert->getId(), false));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// verify – GET with customer+email → sends code, renders verify
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
public function testVerifyGetRendersVerifyFormAndSendsCode(): void
|
||||
{
|
||||
$customer = $this->createCustomerWithEmail();
|
||||
$advert = $this->createAdvert($customer);
|
||||
$em = $this->createEmWithAdvert($advert);
|
||||
|
||||
$mailer = $this->createMock(MailerService::class);
|
||||
$mailer->expects($this->once())->method('sendEmail');
|
||||
|
||||
$twig = $this->createMock(Environment::class);
|
||||
$twig->method('render')->willReturn('<html></html>');
|
||||
|
||||
$session = new Session(new MockArraySessionStorage());
|
||||
$controller = $this->buildController($em, $mailer, $twig, $session);
|
||||
|
||||
$request = new Request();
|
||||
$request->setSession($session);
|
||||
|
||||
$response = $controller->verify('04/2026-TEST01', $request);
|
||||
|
||||
$this->assertSame(200, $response->getStatusCode());
|
||||
// Code should have been stored in session
|
||||
$this->assertNotNull($session->get('order_code_'.$advert->getId()));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// verify – GET with existing valid code → does NOT resend
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
public function testVerifyGetDoesNotResendWhenCodeStillValid(): void
|
||||
{
|
||||
$customer = $this->createCustomerWithEmail();
|
||||
$advert = $this->createAdvert($customer);
|
||||
$em = $this->createEmWithAdvert($advert);
|
||||
|
||||
$mailer = $this->createMock(MailerService::class);
|
||||
$mailer->expects($this->never())->method('sendEmail');
|
||||
|
||||
$twig = $this->createStub(Environment::class);
|
||||
$twig->method('render')->willReturn('<html></html>');
|
||||
|
||||
$session = new Session(new MockArraySessionStorage());
|
||||
// Pre-seed a valid code with future expiry
|
||||
$session->set('order_code_'.$advert->getId(), '123456');
|
||||
$session->set('order_code_expires_'.$advert->getId(), time() + 900);
|
||||
|
||||
$controller = $this->buildController($em, $mailer, $twig, $session);
|
||||
|
||||
$request = new Request();
|
||||
$request->setSession($session);
|
||||
|
||||
$response = $controller->verify('04/2026-TEST01', $request);
|
||||
|
||||
$this->assertSame(200, $response->getStatusCode());
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// verify POST – expired code → error message
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
public function testVerifyPostExpiredCodeReturnsError(): void
|
||||
{
|
||||
$customer = $this->createCustomerWithEmail();
|
||||
$advert = $this->createAdvert($customer);
|
||||
$em = $this->createEmWithAdvert($advert);
|
||||
|
||||
$session = new Session(new MockArraySessionStorage());
|
||||
// Code expired (expiresAt in the past)
|
||||
$session->set('order_code_'.$advert->getId(), '123456');
|
||||
$session->set('order_code_expires_'.$advert->getId(), time() - 1);
|
||||
|
||||
$mailer = $this->createMock(MailerService::class);
|
||||
// After expired code, sendVerifyCodeIfNeeded sends a new code
|
||||
$mailer->expects($this->once())->method('sendEmail');
|
||||
|
||||
$twig = $this->createStub(Environment::class);
|
||||
$twig->method('render')->willReturn('<html></html>');
|
||||
|
||||
$controller = $this->buildController($em, $mailer, $twig, $session);
|
||||
|
||||
$request = new Request([], ['code' => '123456']);
|
||||
$request->setMethod('POST');
|
||||
$request->setSession($session);
|
||||
|
||||
$response = $controller->verify('04/2026-TEST01', $request);
|
||||
|
||||
// Should render verify page (not redirect) with error
|
||||
$this->assertSame(200, $response->getStatusCode());
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// verify POST – correct code → sets session + redirect
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
public function testVerifyPostCorrectCodeRedirects(): void
|
||||
{
|
||||
$customer = $this->createCustomerWithEmail();
|
||||
$advert = $this->createAdvert($customer);
|
||||
$em = $this->createEmWithAdvert($advert);
|
||||
|
||||
$session = new Session(new MockArraySessionStorage());
|
||||
$session->set('order_code_'.$advert->getId(), '654321');
|
||||
$session->set('order_code_expires_'.$advert->getId(), time() + 900);
|
||||
|
||||
$controller = $this->buildController($em, session: $session);
|
||||
|
||||
$request = new Request([], ['code' => '654321']);
|
||||
$request->setMethod('POST');
|
||||
$request->setSession($session);
|
||||
|
||||
$response = $controller->verify('04/2026-TEST01', $request);
|
||||
|
||||
$this->assertSame(302, $response->getStatusCode());
|
||||
$this->assertTrue($session->get('order_verified_'.$advert->getId(), false));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// verify POST – wrong code → error
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
public function testVerifyPostWrongCodeRendersError(): void
|
||||
{
|
||||
$customer = $this->createCustomerWithEmail();
|
||||
$advert = $this->createAdvert($customer);
|
||||
$em = $this->createEmWithAdvert($advert);
|
||||
|
||||
$session = new Session(new MockArraySessionStorage());
|
||||
$session->set('order_code_'.$advert->getId(), '000000');
|
||||
$session->set('order_code_expires_'.$advert->getId(), time() + 900);
|
||||
|
||||
$mailer = $this->createStub(MailerService::class);
|
||||
$twig = $this->createStub(Environment::class);
|
||||
$twig->method('render')->willReturn('<html></html>');
|
||||
|
||||
$controller = $this->buildController($em, $mailer, $twig, $session);
|
||||
|
||||
$request = new Request([], ['code' => '999999']);
|
||||
$request->setMethod('POST');
|
||||
$request->setSession($session);
|
||||
|
||||
$response = $controller->verify('04/2026-TEST01', $request);
|
||||
|
||||
// Should render verify page (not redirect) with wrong-code error
|
||||
$this->assertSame(200, $response->getStatusCode());
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// verifyResend – no customer (skips email)
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
public function testVerifyResendNoCustomerSkipsEmail(): void
|
||||
{
|
||||
$advert = $this->createAdvert(null);
|
||||
$em = $this->createEmWithAdvert($advert);
|
||||
|
||||
$mailer = $this->createMock(MailerService::class);
|
||||
$mailer->expects($this->never())->method('sendEmail');
|
||||
|
||||
$session = new Session(new MockArraySessionStorage());
|
||||
$controller = $this->buildController($em, $mailer, session: $session);
|
||||
|
||||
$request = new Request();
|
||||
$request->setMethod('POST');
|
||||
$request->setSession($session);
|
||||
|
||||
$response = $controller->verifyResend('04/2026-TEST01', $request);
|
||||
|
||||
$this->assertSame(302, $response->getStatusCode());
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// verifyResend – customer with email → sends code + redirects
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
public function testVerifyResendWithEmailSendsCodeAndRedirects(): void
|
||||
{
|
||||
$customer = $this->createCustomerWithEmail();
|
||||
$advert = $this->createAdvert($customer);
|
||||
$em = $this->createEmWithAdvert($advert);
|
||||
|
||||
$mailer = $this->createMock(MailerService::class);
|
||||
$mailer->expects($this->once())->method('sendEmail');
|
||||
|
||||
$twig = $this->createMock(Environment::class);
|
||||
$twig->method('render')->willReturn('<html></html>');
|
||||
|
||||
$session = new Session(new MockArraySessionStorage());
|
||||
$controller = $this->buildController($em, $mailer, $twig, $session);
|
||||
|
||||
$request = new Request();
|
||||
$request->setMethod('POST');
|
||||
$request->setSession($session);
|
||||
|
||||
$response = $controller->verifyResend('04/2026-TEST01', $request);
|
||||
|
||||
$this->assertSame(302, $response->getStatusCode());
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// chooseVirement – delegates to handleOfflinePayment
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
public function testChooseVirementDelegatesToHandleOfflinePayment(): void
|
||||
{
|
||||
$customer = $this->createCustomerWithEmail();
|
||||
$advert = $this->createAdvert($customer);
|
||||
$em = $this->createEmWithAdvert($advert);
|
||||
|
||||
$mailer = $this->createMock(MailerService::class);
|
||||
// 2 emails: one to customer, one to admin
|
||||
$mailer->expects($this->exactly(2))->method('sendEmail');
|
||||
|
||||
$twig = $this->createMock(Environment::class);
|
||||
$twig->method('render')->willReturn('<html></html>');
|
||||
|
||||
$controller = $this->buildController($em, $mailer, $twig);
|
||||
|
||||
$response = $controller->chooseVirement('04/2026-TEST01');
|
||||
|
||||
$this->assertSame(200, $response->getStatusCode());
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// chooseCheque – delegates to handleOfflinePayment
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
public function testChooseCheque(): void
|
||||
{
|
||||
$customer = $this->createCustomerWithEmail();
|
||||
$advert = $this->createAdvert($customer);
|
||||
$em = $this->createEmWithAdvert($advert);
|
||||
|
||||
$mailer = $this->createMock(MailerService::class);
|
||||
$mailer->expects($this->exactly(2))->method('sendEmail');
|
||||
|
||||
$twig = $this->createMock(Environment::class);
|
||||
$twig->method('render')->willReturn('<html></html>');
|
||||
|
||||
$controller = $this->buildController($em, $mailer, $twig);
|
||||
|
||||
$response = $controller->chooseCheque('04/2026-TEST01');
|
||||
|
||||
$this->assertSame(200, $response->getStatusCode());
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// handleOfflinePayment – customer without email (only admin mail)
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
public function testHandleOfflinePaymentNoCustomerEmail(): void
|
||||
{
|
||||
$customer = $this->createCustomerWithoutEmail(); // no email
|
||||
$advert = $this->createAdvert($customer);
|
||||
$em = $this->createEmWithAdvert($advert);
|
||||
|
||||
$mailer = $this->createMock(MailerService::class);
|
||||
// Only admin notification (customer has no email)
|
||||
$mailer->expects($this->once())->method('sendEmail');
|
||||
|
||||
$twig = $this->createMock(Environment::class);
|
||||
$twig->method('render')->willReturn('<html></html>');
|
||||
|
||||
$controller = $this->buildController($em, $mailer, $twig);
|
||||
|
||||
$response = $controller->chooseVirement('04/2026-TEST01');
|
||||
|
||||
$this->assertSame(200, $response->getStatusCode());
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// createStripeIntent – empty stripeSk returns 500
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
public function testCreateStripeIntentEmptySkReturns500(): void
|
||||
{
|
||||
$advert = $this->createAdvert($this->createCustomerWithEmail());
|
||||
$em = $this->createEmWithAdvert($advert);
|
||||
$controller = $this->buildController($em);
|
||||
|
||||
$request = new Request();
|
||||
// stripeSk = '' by default → guard triggers
|
||||
$response = $controller->createStripeIntent('04/2026-TEST01', $request, '');
|
||||
|
||||
$this->assertSame(500, $response->getStatusCode());
|
||||
$data = json_decode($response->getContent(), true);
|
||||
$this->assertArrayHasKey('error', $data);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// createStripeIntent – amount <= 0 returns 400
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
public function testCreateStripeIntentZeroAmountReturns400(): void
|
||||
{
|
||||
$advert = $this->createAdvert($this->createCustomerWithEmail());
|
||||
// totalTtc = '0.00' by default → amount = 0
|
||||
$em = $this->createEmWithAdvert($advert);
|
||||
$controller = $this->buildController($em);
|
||||
|
||||
$request = new Request();
|
||||
$response = $controller->createStripeIntent('04/2026-TEST01', $request, 'sk_test_fake');
|
||||
|
||||
$this->assertSame(400, $response->getStatusCode());
|
||||
$data = json_decode($response->getContent(), true);
|
||||
$this->assertArrayHasKey('error', $data);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// stripeSuccess – advert already accepted → render confirmed
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
public function testStripeSuccessAlreadyAcceptedRendersConfirmed(): void
|
||||
{
|
||||
$advert = $this->createAdvert($this->createCustomerWithEmail(), Advert::STATE_ACCEPTED);
|
||||
$em = $this->createEmWithAdvert($advert);
|
||||
|
||||
$session = new Session(new MockArraySessionStorage());
|
||||
$session->set('order_verified_'.$advert->getId(), true);
|
||||
|
||||
$controller = $this->buildController($em, session: $session);
|
||||
|
||||
$request = new Request();
|
||||
$request->setSession($session);
|
||||
|
||||
$response = $controller->stripeSuccess('04/2026-TEST01', $request, 'sk_test_fake');
|
||||
|
||||
$this->assertSame(200, $response->getStatusCode());
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// stripeSuccess – no piId + empty sk → render processing loader
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
public function testStripeSuccessNoPiIdRendersLoader(): void
|
||||
{
|
||||
$advert = $this->createAdvert($this->createCustomerWithEmail(), Advert::STATE_SEND);
|
||||
// stripePaymentId is null by default
|
||||
$em = $this->createEmWithAdvert($advert);
|
||||
|
||||
$session = new Session(new MockArraySessionStorage());
|
||||
$controller = $this->buildController($em, session: $session);
|
||||
|
||||
$request = new Request();
|
||||
$request->setSession($session);
|
||||
|
||||
// No pi_id and empty sk → skip Stripe block, fall through to loader
|
||||
$response = $controller->stripeSuccess('04/2026-TEST01', $request, '');
|
||||
|
||||
$this->assertSame(200, $response->getStatusCode());
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// stripeCheck – returns JSON status
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
public function testStripeCheckReturnsJsonStatus(): void
|
||||
{
|
||||
$advert = $this->createAdvert(null, Advert::STATE_SEND);
|
||||
$em = $this->createEmWithAdvert($advert);
|
||||
$controller = $this->buildController($em);
|
||||
|
||||
$response = $controller->stripeCheck('04/2026-TEST01');
|
||||
|
||||
$this->assertSame(200, $response->getStatusCode());
|
||||
$data = json_decode($response->getContent(), true);
|
||||
$this->assertArrayHasKey('status', $data);
|
||||
$this->assertArrayHasKey('accepted', $data);
|
||||
$this->assertSame(Advert::STATE_SEND, $data['status']);
|
||||
$this->assertFalse($data['accepted']);
|
||||
}
|
||||
|
||||
public function testStripeCheckAcceptedState(): void
|
||||
{
|
||||
$advert = $this->createAdvert(null, Advert::STATE_ACCEPTED);
|
||||
$em = $this->createEmWithAdvert($advert);
|
||||
$controller = $this->buildController($em);
|
||||
|
||||
$response = $controller->stripeCheck('04/2026-TEST01');
|
||||
|
||||
$data = json_decode($response->getContent(), true);
|
||||
$this->assertTrue($data['accepted']);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// findRevendeur – null customer → returns null (guard via createStripeIntent)
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
public function testFindRevendeurNullCustomerReturnsNullGuard(): void
|
||||
{
|
||||
// We test findRevendeur indirectly via createStripeIntent guard path (amount=0)
|
||||
$advert = $this->createAdvert(null); // no customer
|
||||
$em = $this->createEmWithAdvert($advert);
|
||||
$controller = $this->buildController($em);
|
||||
|
||||
// amount = 0 (totalTtc = 0.00) → guard fires before findRevendeur
|
||||
$request = new Request();
|
||||
$response = $controller->createStripeIntent('04/2026-TEST01', $request, 'sk_test_fake');
|
||||
|
||||
$this->assertSame(400, $response->getStatusCode());
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// findRevendeur – customer with no revendeurCode → returns null
|
||||
// We test this by exercising the private method via Reflection
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
public function testFindRevendeurNoCodeReturnsNull(): void
|
||||
{
|
||||
$customer = $this->createCustomerWithEmail();
|
||||
// revendeurCode is null by default
|
||||
|
||||
$advert = $this->createAdvert($customer);
|
||||
$em = $this->createEmWithAdvertAndRevendeur($advert, null);
|
||||
$controller = $this->buildController($em);
|
||||
|
||||
$method = new \ReflectionMethod(OrderPaymentController::class, 'findRevendeur');
|
||||
$method->setAccessible(true);
|
||||
|
||||
$result = $method->invoke($controller, $customer);
|
||||
|
||||
$this->assertNull($result);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// findRevendeur – revendeur not found in DB → returns null
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
public function testFindRevendeurNotFoundInDbReturnsNull(): void
|
||||
{
|
||||
$customer = $this->createCustomerWithEmail();
|
||||
$customer->setRevendeurCode('REV001');
|
||||
|
||||
$advert = $this->createAdvert($customer);
|
||||
// Repo returns null for findOneBy
|
||||
$em = $this->createEmWithAdvertAndRevendeur($advert, null);
|
||||
$controller = $this->buildController($em);
|
||||
|
||||
$method = new \ReflectionMethod(OrderPaymentController::class, 'findRevendeur');
|
||||
$method->setAccessible(true);
|
||||
|
||||
$result = $method->invoke($controller, $customer);
|
||||
|
||||
$this->assertNull($result);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// findRevendeur – revendeur found but isUseStripe=false → returns null
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
public function testFindRevendeurNotUsingStripeReturnsNull(): void
|
||||
{
|
||||
$customer = $this->createCustomerWithEmail();
|
||||
$customer->setRevendeurCode('REV002');
|
||||
|
||||
$revendeurUser = new User();
|
||||
$revendeurUser->setEmail('rev@test.com');
|
||||
$revendeurUser->setPassword('h');
|
||||
$revendeur = new Revendeur($revendeurUser, 'REV002');
|
||||
// isUseStripe defaults to false
|
||||
|
||||
$advert = $this->createAdvert($customer);
|
||||
$em = $this->createEmWithAdvertAndRevendeur($advert, $revendeur);
|
||||
$controller = $this->buildController($em);
|
||||
|
||||
$method = new \ReflectionMethod(OrderPaymentController::class, 'findRevendeur');
|
||||
$method->setAccessible(true);
|
||||
|
||||
$result = $method->invoke($controller, $customer);
|
||||
|
||||
$this->assertNull($result);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// findRevendeur – revendeur isUseStripe=true but no stripeConnectId → null
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
public function testFindRevendeurNoStripeConnectIdReturnsNull(): void
|
||||
{
|
||||
$customer = $this->createCustomerWithEmail();
|
||||
$customer->setRevendeurCode('REV003');
|
||||
|
||||
$revendeurUser = new User();
|
||||
$revendeurUser->setEmail('rev3@test.com');
|
||||
$revendeurUser->setPassword('h');
|
||||
$revendeur = new Revendeur($revendeurUser, 'REV003');
|
||||
$revendeur->setIsUseStripe(true);
|
||||
// stripeConnectId is null by default
|
||||
|
||||
$advert = $this->createAdvert($customer);
|
||||
$em = $this->createEmWithAdvertAndRevendeur($advert, $revendeur);
|
||||
$controller = $this->buildController($em);
|
||||
|
||||
$method = new \ReflectionMethod(OrderPaymentController::class, 'findRevendeur');
|
||||
$method->setAccessible(true);
|
||||
|
||||
$result = $method->invoke($controller, $customer);
|
||||
|
||||
$this->assertNull($result);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// findAdvert – not found → throws NotFoundHttpException
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
public function testFindAdvertThrows404WhenNotFound(): void
|
||||
{
|
||||
$em = $this->createEmWithAdvert(null);
|
||||
$controller = $this->buildController($em);
|
||||
|
||||
$method = new \ReflectionMethod(OrderPaymentController::class, 'findAdvert');
|
||||
$method->setAccessible(true);
|
||||
|
||||
$this->expectException(NotFoundHttpException::class);
|
||||
$method->invoke($controller, 'NONEXISTENT');
|
||||
}
|
||||
}
|
||||
65
tests/Controller/UnsubscribeControllerTest.php
Normal file
65
tests/Controller/UnsubscribeControllerTest.php
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace App\Tests\Controller;
|
||||
|
||||
use App\Controller\UnsubscribeController;
|
||||
use App\Service\UnsubscribeManager;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Symfony\Component\HttpFoundation\Session\Session;
|
||||
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
|
||||
use Symfony\Component\Routing\RouterInterface;
|
||||
use Twig\Environment;
|
||||
|
||||
class UnsubscribeControllerTest extends TestCase
|
||||
{
|
||||
private function buildController(): UnsubscribeController
|
||||
{
|
||||
$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('/some/path');
|
||||
|
||||
$container = $this->createStub(ContainerInterface::class);
|
||||
$container->method('has')->willReturn(true);
|
||||
$container->method('get')->willReturnMap([
|
||||
['twig', $twig],
|
||||
['router', $router],
|
||||
['request_stack', $stack],
|
||||
]);
|
||||
|
||||
$controller = new UnsubscribeController();
|
||||
$controller->setContainer($container);
|
||||
|
||||
return $controller;
|
||||
}
|
||||
|
||||
public function testInvalidTokenRendersInvalidTemplate(): void
|
||||
{
|
||||
$manager = $this->createStub(UnsubscribeManager::class);
|
||||
$manager->method('isValidToken')->willReturn(false);
|
||||
|
||||
$controller = $this->buildController();
|
||||
$response = $controller->__invoke('test@example.com', 'invalid-token', $manager);
|
||||
|
||||
$this->assertSame(200, $response->getStatusCode());
|
||||
}
|
||||
|
||||
public function testValidTokenUnsubscribesAndRendersSuccessTemplate(): void
|
||||
{
|
||||
$manager = $this->createMock(UnsubscribeManager::class);
|
||||
$manager->method('isValidToken')->willReturn(true);
|
||||
$manager->expects($this->once())->method('unsubscribe')->with('test@example.com');
|
||||
|
||||
$controller = $this->buildController();
|
||||
$response = $controller->__invoke('test@example.com', 'valid-token', $manager);
|
||||
|
||||
$this->assertSame(200, $response->getStatusCode());
|
||||
}
|
||||
}
|
||||
49
tests/Controller/WebmailControllerTest.php
Normal file
49
tests/Controller/WebmailControllerTest.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace App\Tests\Controller;
|
||||
|
||||
use App\Controller\WebmailController;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Symfony\Component\HttpFoundation\Session\Session;
|
||||
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
|
||||
use Symfony\Component\Routing\RouterInterface;
|
||||
use Twig\Environment;
|
||||
|
||||
class WebmailControllerTest extends TestCase
|
||||
{
|
||||
private function buildController(): WebmailController
|
||||
{
|
||||
$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('/some/path');
|
||||
|
||||
$container = $this->createStub(ContainerInterface::class);
|
||||
$container->method('has')->willReturn(true);
|
||||
$container->method('get')->willReturnMap([
|
||||
['twig', $twig],
|
||||
['router', $router],
|
||||
['request_stack', $stack],
|
||||
]);
|
||||
|
||||
$controller = new WebmailController();
|
||||
$controller->setContainer($container);
|
||||
|
||||
return $controller;
|
||||
}
|
||||
|
||||
public function testLoginRendersWebmailLoginTemplate(): void
|
||||
{
|
||||
$controller = $this->buildController();
|
||||
$response = $controller->login();
|
||||
|
||||
$this->assertSame(200, $response->getStatusCode());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user