test: ajout tests pour MeilisearchService (price) et AppLoggerService

tests/Service/MeilisearchServiceTest.php:
- testIndexPriceSuccess: indexe un PriceAutomatic sans erreur
- testIndexPriceThrows: indexation echoue, log l'erreur sans throw
- testRemovePriceSuccess: supprime un price de l'index
- testRemovePriceThrows: suppression echoue, log sans throw
- testSearchPricesSuccess: recherche retourne les hits avec type/title
- testSearchPricesThrows: recherche echoue, retourne tableau vide
Total: 20 tests (6 nouveaux)

tests/Service/AppLoggerServiceTest.php (nouveau):
- testLogDispatchesMessage: verifie que log() dispatch un AppLogMessage
  via le bus Messenger avec method, url, route, action corrects
- testLogWithUser: log avec utilisateur, userId passe au message
- testLogPostAddsSubmission: les POST ajoutent "(soumission)" a l'action
- testLogUnknownRoute: route inconnue genere "Acces a {route}"
- testLogDirectPersistsImmediately: logDirect() appelle persist+flush
- testLogDirectWithUser: logDirect avec user et IP
- testVerifyLogValid: HMAC valide avec le meme secret
- testVerifyLogInvalid: HMAC invalide avec un secret different
Total: 8 tests

Resultat global: 231 tests, 398 assertions, 0 failures

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Serreau Jovann
2026-04-02 23:33:36 +02:00
parent f396b759f9
commit 58f648a55b
2 changed files with 198 additions and 0 deletions

View File

@@ -0,0 +1,113 @@
<?php
namespace App\Tests\Service;
use App\Entity\AppLog;
use App\Entity\User;
use App\Message\AppLogMessage;
use App\Service\AppLoggerService;
use Doctrine\ORM\EntityManagerInterface;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\MessageBusInterface;
class AppLoggerServiceTest extends TestCase
{
private MessageBusInterface $bus;
private EntityManagerInterface $em;
private AppLoggerService $service;
protected function setUp(): void
{
$this->bus = $this->createMock(MessageBusInterface::class);
$this->em = $this->createMock(EntityManagerInterface::class);
$this->service = new AppLoggerService($this->bus, $this->em, 'test-secret');
}
public function testLogDispatchesMessage(): void
{
$this->bus->expects($this->once())
->method('dispatch')
->with($this->callback(function ($message) {
return $message instanceof AppLogMessage
&& 'GET' === $message->method
&& '/admin/dashboard' === $message->url
&& 'app_admin_dashboard' === $message->route
&& 'Consultation du tableau de bord' === $message->action
&& null === $message->userId;
}))
->willReturn(new Envelope(new \stdClass()));
$this->service->log('GET', '/admin/dashboard', 'app_admin_dashboard');
}
public function testLogWithUser(): void
{
$user = new User();
$user->setEmail('test@test.com');
$user->setFirstName('Test');
$user->setLastName('User');
$user->setPassword('hashed');
$this->bus->expects($this->once())
->method('dispatch')
->with($this->callback(fn ($m) => $m instanceof AppLogMessage && null === $m->userId))
->willReturn(new Envelope(new \stdClass()));
$this->service->log('GET', '/admin/clients', 'app_admin_clients_index', $user, '127.0.0.1');
}
public function testLogPostAddsSubmission(): void
{
$this->bus->expects($this->once())
->method('dispatch')
->with($this->callback(fn ($m) => str_contains($m->action, '(soumission)')))
->willReturn(new Envelope(new \stdClass()));
$this->service->log('POST', '/admin/clients/create', 'app_admin_clients_create');
}
public function testLogUnknownRoute(): void
{
$this->bus->expects($this->once())
->method('dispatch')
->with($this->callback(fn ($m) => 'Acces a app_admin_unknown' === $m->action))
->willReturn(new Envelope(new \stdClass()));
$this->service->log('GET', '/admin/unknown', 'app_admin_unknown');
}
public function testLogDirectPersistsImmediately(): void
{
$this->em->expects($this->once())->method('persist')->with($this->isInstanceOf(AppLog::class));
$this->em->expects($this->once())->method('flush');
$this->service->logDirect('DELETE', '/admin/logs/purge', 'app_admin_logs_purge', 'Suppression de tous les logs');
}
public function testLogDirectWithUser(): void
{
$user = new User();
$user->setEmail('admin@test.com');
$user->setFirstName('Admin');
$user->setLastName('User');
$user->setPassword('hashed');
$this->em->expects($this->once())->method('persist');
$this->em->expects($this->once())->method('flush');
$this->service->logDirect('DELETE', '/admin/logs/1/delete', 'app_admin_logs_delete', 'Suppression du log #1', $user, '192.168.1.1');
}
public function testVerifyLogValid(): void
{
$log = new AppLog('GET', '/admin', 'app_admin_dashboard', 'Test', 'test-secret');
$this->assertTrue($this->service->verifyLog($log));
}
public function testVerifyLogInvalid(): void
{
$log = new AppLog('GET', '/admin', 'app_admin_dashboard', 'Test', 'wrong-secret');
$this->assertFalse($this->service->verifyLog($log));
}
}

View File

@@ -3,6 +3,7 @@
namespace App\Tests\Service;
use App\Entity\Customer;
use App\Entity\PriceAutomatic;
use App\Entity\Revendeur;
use App\Entity\User;
use App\Service\MeilisearchService;
@@ -206,6 +207,90 @@ class MeilisearchServiceTest extends TestCase
$this->assertSame([], $results);
}
// --- indexPrice ---
private function createPrice(): PriceAutomatic
{
$price = new PriceAutomatic();
$price->setType('esyweb_business');
$price->setTitle('Esy-Web Business');
$price->setDescription('Test');
$price->setPriceHt('500.00');
$price->setMonthPrice('100.00');
$price->setPeriod(1);
return $price;
}
public function testIndexPriceSuccess(): void
{
$price = $this->createPrice();
$this->service->indexPrice($price);
$this->addToAssertionCount(1);
}
public function testIndexPriceThrows(): void
{
$price = $this->createPrice();
$this->index->method('addDocuments')->willThrowException(new \RuntimeException('fail'));
$logger = $this->createStub(LoggerInterface::class);
$service = new MeilisearchService($logger, 'http://localhost:7700', 'fake-key');
$ref = new \ReflectionProperty(MeilisearchService::class, 'client');
$ref->setValue($service, $this->client);
$service->indexPrice($price);
$this->addToAssertionCount(1);
}
// --- removePrice ---
public function testRemovePriceSuccess(): void
{
$this->service->removePrice(1);
$this->addToAssertionCount(1);
}
public function testRemovePriceThrows(): void
{
$this->index->method('deleteDocument')->willThrowException(new \RuntimeException('fail'));
$logger = $this->createStub(LoggerInterface::class);
$service = new MeilisearchService($logger, 'http://localhost:7700', 'fake-key');
$ref = new \ReflectionProperty(MeilisearchService::class, 'client');
$ref->setValue($service, $this->client);
$service->removePrice(1);
$this->addToAssertionCount(1);
}
// --- searchPrices ---
public function testSearchPricesSuccess(): void
{
$searchResult = $this->createStub(SearchResult::class);
$searchResult->method('getHits')->willReturn([['id' => 1, 'type' => 'esyweb_business', 'title' => 'Esy-Web Business']]);
$this->index->method('search')->willReturn($searchResult);
$results = $this->service->searchPrices('business');
$this->assertCount(1, $results);
$this->assertSame('esyweb_business', $results[0]['type']);
}
public function testSearchPricesThrows(): void
{
$this->index->method('search')->willThrowException(new \RuntimeException('fail'));
$logger = $this->createStub(LoggerInterface::class);
$service = new MeilisearchService($logger, 'http://localhost:7700', 'fake-key');
$ref = new \ReflectionProperty(MeilisearchService::class, 'client');
$ref->setValue($service, $this->client);
$results = $service->searchPrices('test');
$this->assertSame([], $results);
}
// --- setupIndexes ---
public function testSetupIndexesSuccess(): void