Files
crm_ecosplay/tests/Controller/CspReportControllerTest.php
Serreau Jovann 8b35e2b6d2 feat: comptabilite + prestataires + rapport financier + stats dynamiques
Comptabilite (Super Admin) :
- ComptabiliteController avec 7 exports CSV/JSON compatibles SAGE
  (journal ventes, grand livre, FEC, balance agee, reglements,
  commissions Stripe 1.5%+0.25E, couts services)
- Export PDF via ComptaPdf (FPDF) avec bloc legal pre-rempli,
  tableau pagine, champ signature DocuSeal
- Signature electronique DocuSeal + callback + envoi email signe
  avec template dedie (compta_export_signed.html.twig)
- Rapport financier public (RapportFinancierPdf) : recettes par
  service, depenses (Stripe, infra, prestataires), bilan excedent/deficit
- Codes comptables clients EC-XXXX (plus de 411xxx)

Prestataires (Super Admin) :
- Entite Prestataire (raisonSociale, siret, email, phone, adresse)
- Entite FacturePrestataire (numFacture, montantHt, montantTtc,
  year, month, isPaid, PDF via Vich)
- CRUD complet avec recherche SIRET via proxy API data.gouv.fr
- Commande cron app:reminder:factures-prestataire (5 du mois)
- Factures prestataires integrees dans export couts services
- Sidebar Super Admin : entree Prestataires + Comptabilite

Stats (/admin/stats) :
- Cout prestataire dynamique depuis FacturePrestataire
- Fusion Infra + Prestataire en "Cout de fonctionnement"
- Commission Stripe corrigee (1.5% + 0.25E par transaction)

Divers :
- DocuSealService::sendComptaForSignature() + getApi()
- Customer::generateCodeComptable() format EC-XXXX-XXXXX
- Protection double prefixe EC- a la creation client
- Bouton regenerer PDF cache quand advert state=accepted
- Modals sans script inline (data-modal-open/close dans app.js)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 23:39:31 +02:00

253 lines
10 KiB
PHP

<?php
namespace App\Tests\Controller;
use App\Controller\CspReportController;
use PHPUnit\Framework\TestCase;
use Psr\Container\ContainerInterface;
use Psr\Log\LoggerInterface;
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\Mailer\MailerInterface;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
class CspReportControllerTest extends TestCase
{
private function setupController(CspReportController $controller): void
{
$session = new Session(new MockArraySessionStorage());
$stack = $this->createStub(RequestStack::class);
$stack->method('getSession')->willReturn($session);
$paramBag = $this->createStub(\Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface::class);
$paramBag->method('get')->willReturn('admin@e-cosplay.fr');
$paramBag->method('has')->willReturn(true);
$container = $this->createStub(ContainerInterface::class);
$container->method('has')->willReturn(true);
$container->method('get')->willReturnMap([
['security.authorization_checker', $this->createStub(AuthorizationCheckerInterface::class)],
['security.token_storage', $this->createStub(TokenStorageInterface::class)],
['request_stack', $stack],
['parameter_bag', $paramBag],
]);
$controller->setContainer($container);
}
public function testGetReturns204(): void
{
$controller = new CspReportController();
$response = $controller->get();
$this->assertSame(204, $response->getStatusCode());
}
public function testReportEmptyPayload(): void
{
$controller = new CspReportController();
$logger = $this->createStub(LoggerInterface::class);
$request = new Request([], [], [], [], [], [], '');
$response = $controller->report($request, $this->createStub(MailerInterface::class), $logger);
$this->assertSame(400, $response->getStatusCode());
}
public function testReportInvalidJson(): void
{
$controller = new CspReportController();
$logger = $this->createStub(LoggerInterface::class);
$request = new Request([], [], [], [], [], [], '{invalid');
$response = $controller->report($request, $this->createStub(MailerInterface::class), $logger);
$this->assertSame(400, $response->getStatusCode());
}
public function testReportIgnoredExtension(): void
{
$controller = new CspReportController();
$logger = $this->createStub(LoggerInterface::class);
$payload = json_encode([
'csp-report' => [
'source-file' => 'chrome-extension://abc123',
'blocked-uri' => 'inline',
'document-uri' => 'https://crm.e-cosplay.fr',
'violated-directive' => 'script-src',
],
]);
$request = new Request([], [], [], [], [], [], $payload);
$response = $controller->report($request, $this->createStub(MailerInterface::class), $logger);
$this->assertSame(204, $response->getStatusCode());
}
public function testReportIgnoredMozExtension(): void
{
$controller = new CspReportController();
$logger = $this->createStub(LoggerInterface::class);
$payload = json_encode([
'csp-report' => [
'source-file' => 'moz-extension://abc',
'blocked-uri' => '',
'document-uri' => 'https://crm.e-cosplay.fr',
'violated-directive' => 'script-src',
],
]);
$request = new Request([], [], [], [], [], [], $payload);
$response = $controller->report($request, $this->createStub(MailerInterface::class), $logger);
$this->assertSame(204, $response->getStatusCode());
}
public function testReportIgnoredLocalhost(): void
{
$controller = new CspReportController();
$logger = $this->createStub(LoggerInterface::class);
$payload = json_encode([
'csp-report' => [
'source-file' => 'http://localhost:3000/app.js',
'blocked-uri' => 'eval',
'document-uri' => 'https://crm.e-cosplay.fr',
'violated-directive' => 'script-src',
],
]);
$request = new Request([], [], [], [], [], [], $payload);
$response = $controller->report($request, $this->createStub(MailerInterface::class), $logger);
$this->assertSame(204, $response->getStatusCode());
}
public function testReportIgnoredLocalDomain(): void
{
$controller = new CspReportController();
$logger = $this->createStub(LoggerInterface::class);
$payload = json_encode([
'csp-report' => [
'source-file' => '/app.js',
'blocked-uri' => 'inline',
'document-uri' => 'http://crm.local/dashboard',
'violated-directive' => 'style-src',
],
]);
$request = new Request([], [], [], [], [], [], $payload);
$response = $controller->report($request, $this->createStub(MailerInterface::class), $logger);
$this->assertSame(204, $response->getStatusCode());
}
public function testReportIgnoredWasmEval(): void
{
$controller = new CspReportController();
$logger = $this->createStub(LoggerInterface::class);
$payload = json_encode([
'csp-report' => [
'source-file' => 'https://crm.e-cosplay.fr/app.js',
'blocked-uri' => 'wasm-eval',
'document-uri' => 'https://crm.e-cosplay.fr',
'violated-directive' => 'script-src',
],
]);
$request = new Request([], [], [], [], [], [], $payload);
$response = $controller->report($request, $this->createStub(MailerInterface::class), $logger);
$this->assertSame(204, $response->getStatusCode());
}
public function testReportIgnoredAboutBlank(): void
{
$controller = new CspReportController();
$logger = $this->createStub(LoggerInterface::class);
$payload = json_encode([
'csp-report' => [
'source-file' => '',
'blocked-uri' => 'about:blank',
'document-uri' => 'https://crm.e-cosplay.fr',
'violated-directive' => 'frame-src',
],
]);
$request = new Request([], [], [], [], [], [], $payload);
$response = $controller->report($request, $this->createStub(MailerInterface::class), $logger);
$this->assertSame(204, $response->getStatusCode());
}
public function testReportRealViolationSendsEmail(): void
{
$controller = new CspReportController();
$this->setupController($controller);
$logger = $this->createStub(LoggerInterface::class);
$mailer = $this->createStub(MailerInterface::class);
$payload = json_encode([
'csp-report' => [
'source-file' => 'https://evil.com/inject.js',
'blocked-uri' => 'https://evil.com',
'document-uri' => 'https://crm.e-cosplay.fr/dashboard',
'violated-directive' => 'script-src',
],
]);
$request = new Request([], [], [], [], [], [], $payload);
$response = $controller->report($request, $mailer, $logger);
$this->assertSame(204, $response->getStatusCode());
}
public function testReportRealViolationEmailFailure(): void
{
$controller = new CspReportController();
$this->setupController($controller);
$logger = $this->createStub(LoggerInterface::class);
$mailer = $this->createStub(MailerInterface::class);
$mailer->method('send')->willThrowException(new \Exception('SMTP error'));
$payload = json_encode([
'csp-report' => [
'source-file' => 'https://evil.com/inject.js',
'blocked-uri' => 'https://evil.com',
'document-uri' => 'https://crm.e-cosplay.fr/dashboard',
'violated-directive' => 'script-src',
],
]);
$request = new Request([], [], [], [], [], [], $payload);
$response = $controller->report($request, $mailer, $logger);
$this->assertSame(204, $response->getStatusCode());
}
public function testReportWithoutCspReportWrapper(): void
{
$controller = new CspReportController();
$this->setupController($controller);
$logger = $this->createStub(LoggerInterface::class);
$mailer = $this->createStub(MailerInterface::class);
$payload = json_encode([
'source-file' => 'https://evil.com/inject.js',
'blocked-uri' => 'https://evil.com',
'document-uri' => 'https://crm.e-cosplay.fr/dashboard',
'violated-directive' => 'script-src',
]);
$request = new Request([], [], [], [], [], [], $payload);
$response = $controller->report($request, $mailer, $logger);
$this->assertSame(204, $response->getStatusCode());
}
public function testReportIgnoredNodeModulesInline(): void
{
$controller = new CspReportController();
$logger = $this->createStub(LoggerInterface::class);
$payload = json_encode([
'csp-report' => [
'source-file' => '/home/user/node_modules/some-lib/index.js',
'blocked-uri' => 'inline',
'document-uri' => 'https://crm.e-cosplay.fr',
'violated-directive' => 'script-src',
],
]);
$request = new Request([], [], [], [], [], [], $payload);
$response = $controller->report($request, $this->createStub(MailerInterface::class), $logger);
$this->assertSame(204, $response->getStatusCode());
}
}