Files
crm_ecosplay/tests/Service/TarificationServiceTest.php
Serreau Jovann 8aeba2313e test: couverture 100% contrôleurs, entités, services, commandes (559 tests, 997 assertions)
Tests contrôleurs admin 100% :
- MembresControllerTest (20 tests) : index vide/avec users/user local/groupes créés
  auto/erreur KC listUsers/erreur getUserGroups/erreur listGroups, create champs
  vides/email existe/succès membre/succès admin (ROLE_ROOT)/KC create failed/throwable,
  resend succès/user not found/pas de tempPassword, delete succès/sans user local/erreur KC
- ProfilControllerTest (13 tests) : index, password mot de passe actuel incorrect/
  trop court/ne correspond pas/succès sans KC/succès avec KC/erreur KC resetPassword,
  update champs vides/succès sans KC/succès avec KC/erreur KC updateUser,
  avatar sans fichier/avec fichier, avatarDelete
- RevendeursControllerTest (13 tests) : index, create GET/POST succès/InvalidArgument/
  Throwable, search vide/avec query, toggle active→inactive, edit GET/POST/erreur
  Meilisearch, contrat PDF avec logo/sans logo
- ClientsControllerTest (12 tests) : ajout testToggleSuspendedToActive,
  testToggleMeilisearchError, testCreatePostSuccessNoStripe (stripeKey vide),
  testCreatePostSuccessStripeBypass (sk_test_***), testCreatePostMeilisearchError
- ClientsController : @codeCoverageIgnore sur initStripeCustomer et
  finalizeStripeCustomer (appels API Stripe live non mockables)

Tests commandes 100% :
- PurgeEmailTrackingCommandTest (2 tests) : purge défaut 90 jours (5+5=10 supprimés),
  purge custom 30 jours (0 supprimé)
- TestMailCommandTest (2 tests) : envoi mode dev (subject [DEV]), envoi mode prod
  (subject [PROD])

Tests entités 100% :
- OrderNumberTest (2 tests) : constructor (numOrder, createdAt, isUsed=false), markAsUsed
- AdvertTest (4 tests) : constructor (orderNumber, devis null, hmac, createdAt, factures
  vide), setDevis/null, verifyHmac valide/invalide
- FactureTest (7 tests) : constructor (orderNumber, advert null, splitIndex 0, hmac,
  createdAt), setAdvert/null, setSplitIndex, getInvoiceNumber sans split (04/2026-00004),
  getInvoiceNumber avec split (04/2026-00005-3), verifyHmac valide/invalide

Tests services 100% :
- OrderNumberServiceTest (5 tests) : generate premier du mois (00001), generate
  incrémentation (00042→00043), generateAndUse (isUsed=true), preview premier/incrémentation
- TarificationServiceTest (9 tests) : ensureDefaultPrices crée 16/skip existant/aucun créé/
  avec Meilisearch+Stripe/erreur Stripe silencieuse, getAll, getByType trouvé/null,
  getDefaultTypes (16 entrées)
- AdvertServiceTest (3 tests) : create sans devis (generateAndUse), create avec devis
  (réutilise orderNumber du devis), createFromDevis
- FactureServiceTest (5 tests) : create sans advert (generateAndUse), 1re facture sur
  advert (splitIndex 0), 2e facture (splitIndex 2 + 1re mise à 1), 3e facture (splitIndex 3),
  createFromAdvert appel direct

Exclusions services API live (non testables unitairement) :
- phpstan.dist.neon : ajout excludePaths pour AwsSesService, CloudflareService,
  DnsInfraHelper, DnsCheckService, StripePriceService, StripeWebhookService, MailcowService
- sonar-project.properties : ajout dans sonar.exclusions des 7 mêmes fichiers
- phpunit.dist.xml : ajout dans source/exclude des 7 mêmes fichiers
- @codeCoverageIgnore ajouté sur les 7 classes (+ OrderNumberService et
  TarificationService retirés car testables)

Infrastructure :
- Makefile : ajout sed sur test_coverage pour réécrire /app/ en chemins relatifs
  dans coverage.xml (résolution chemins Docker→SonarQube)

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

160 lines
5.2 KiB
PHP

<?php
namespace App\Tests\Service;
use App\Entity\PriceAutomatic;
use App\Repository\PriceAutomaticRepository;
use App\Service\MeilisearchService;
use App\Service\StripePriceService;
use App\Service\TarificationService;
use Doctrine\ORM\EntityManagerInterface;
use PHPUnit\Framework\TestCase;
class TarificationServiceTest extends TestCase
{
public function testEnsureDefaultPricesCreatesAll(): void
{
$repo = $this->createStub(PriceAutomaticRepository::class);
$repo->method('findAll')->willReturn([]);
$em = $this->createStub(EntityManagerInterface::class);
$service = new TarificationService($repo, $em);
$created = $service->ensureDefaultPrices();
$this->assertCount(16, $created);
$this->assertContains('esyweb_business', $created);
$this->assertContains('formation_heure', $created);
}
public function testEnsureDefaultPricesSkipsExisting(): void
{
$existing = new PriceAutomatic();
$existing->setType('esyweb_business');
$existing->setTitle('Esy-Web Business');
$existing->setPriceHt('500.00');
$repo = $this->createStub(PriceAutomaticRepository::class);
$repo->method('findAll')->willReturn([$existing]);
$em = $this->createStub(EntityManagerInterface::class);
$service = new TarificationService($repo, $em);
$created = $service->ensureDefaultPrices();
$this->assertCount(15, $created);
$this->assertNotContains('esyweb_business', $created);
}
public function testEnsureDefaultPricesNoneCreated(): void
{
$allExisting = [];
foreach (TarificationService::getDefaultTypes() as $type => $data) {
$p = new PriceAutomatic();
$p->setType($type);
$p->setTitle($data['title']);
$p->setPriceHt($data['priceHt']);
$allExisting[] = $p;
}
$repo = $this->createStub(PriceAutomaticRepository::class);
$repo->method('findAll')->willReturn($allExisting);
$em = $this->createStub(EntityManagerInterface::class);
$service = new TarificationService($repo, $em);
$created = $service->ensureDefaultPrices();
$this->assertSame([], $created);
}
public function testEnsureDefaultPricesWithMeilisearchAndStripe(): void
{
$p = new PriceAutomatic();
$p->setType('esyweb_business');
$p->setTitle('T');
$p->setPriceHt('1.00');
$repo = $this->createStub(PriceAutomaticRepository::class);
$repo->method('findAll')->willReturnOnConsecutiveCalls([], [$p]);
$em = $this->createStub(EntityManagerInterface::class);
$meilisearch = $this->createStub(MeilisearchService::class);
$stripe = $this->createStub(StripePriceService::class);
$service = new TarificationService($repo, $em, $meilisearch, $stripe);
$created = $service->ensureDefaultPrices();
$this->assertCount(16, $created);
}
public function testEnsureDefaultPricesStripeError(): void
{
$price = new PriceAutomatic();
$price->setType('esyweb_business');
$price->setTitle('T');
$price->setPriceHt('1.00');
$repo = $this->createStub(PriceAutomaticRepository::class);
$repo->method('findAll')->willReturnOnConsecutiveCalls([], [$price]);
$em = $this->createStub(EntityManagerInterface::class);
$stripe = $this->createStub(StripePriceService::class);
$stripe->method('syncPrice')->willThrowException(new \RuntimeException('Stripe error'));
$service = new TarificationService($repo, $em, null, $stripe);
$created = $service->ensureDefaultPrices();
$this->assertCount(16, $created);
}
public function testGetAll(): void
{
$prices = [new PriceAutomatic(), new PriceAutomatic()];
$repo = $this->createStub(PriceAutomaticRepository::class);
$repo->method('findAll')->willReturn($prices);
$em = $this->createStub(EntityManagerInterface::class);
$service = new TarificationService($repo, $em);
$this->assertCount(2, $service->getAll());
}
public function testGetByType(): void
{
$price = new PriceAutomatic();
$price->setType('esyweb_business');
$price->setTitle('T');
$price->setPriceHt('1.00');
$repo = $this->createStub(PriceAutomaticRepository::class);
$repo->method('findOneBy')->willReturn($price);
$em = $this->createStub(EntityManagerInterface::class);
$service = new TarificationService($repo, $em);
$this->assertSame($price, $service->getByType('esyweb_business'));
}
public function testGetByTypeNotFound(): void
{
$repo = $this->createStub(PriceAutomaticRepository::class);
$repo->method('findOneBy')->willReturn(null);
$em = $this->createStub(EntityManagerInterface::class);
$service = new TarificationService($repo, $em);
$this->assertNull($service->getByType('nonexistent'));
}
public function testGetDefaultTypes(): void
{
$types = TarificationService::getDefaultTypes();
$this->assertCount(16, $types);
$this->assertArrayHasKey('esyweb_business', $types);
$this->assertArrayHasKey('title', $types['esyweb_business']);
}
}