```
✨ feat(ReserverController): Ajoute vérification de disponibilité produit. 🛠️ refactor(BackupCommand): Utilise DatabaseDumper et ZipArchiver. ✨ feat(GitSyncLogCommand): Utilise Gemini pour messages plus clairs. ✨ feat(GenerateVideoThumbsCommand): Utilise VideoThumbnailer service. ✨ feat(AppWarmupImagesCommand): Utilise StorageInterface pour warmup. 🔒️ security(nelmio_security): Renforce la sécurité avec des en-têtes. 🔧 chore(caddy): Améliore la configuration de Caddy pour la performance. 🐛 fix(makefile): Corrige les commandes de test. 🧪 chore(.env.test): Supprime la ligne vide à la fin du fichier. 🔧 chore(doctrine): Active native_lazy_objects. 🔧 chore(cache): Ajoute un cache system. ```
This commit is contained in:
94
tests/Command/AppWarmupImagesCommandTest.php
Normal file
94
tests/Command/AppWarmupImagesCommandTest.php
Normal file
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
namespace App\Tests\Command;
|
||||
|
||||
use App\Command\AppWarmupImagesCommand;
|
||||
use App\Repository\OptionsRepository;
|
||||
use App\Repository\ProductRepository;
|
||||
use Liip\ImagineBundle\Binary\BinaryInterface;
|
||||
use Liip\ImagineBundle\Imagine\Cache\CacheManager;
|
||||
use Liip\ImagineBundle\Imagine\Data\DataManager;
|
||||
use Liip\ImagineBundle\Imagine\Filter\FilterManager;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Console\Application;
|
||||
use Symfony\Component\Console\Tester\CommandTester;
|
||||
use Vich\UploaderBundle\Storage\StorageInterface;
|
||||
|
||||
class AppWarmupImagesCommandTest extends TestCase
|
||||
{
|
||||
public function testExecute()
|
||||
{
|
||||
// Mocks
|
||||
$productRepo = $this->createMock(ProductRepository::class);
|
||||
$optionsRepo = $this->createMock(OptionsRepository::class);
|
||||
$cacheManager = $this->createMock(CacheManager::class);
|
||||
$dataManager = $this->createMock(DataManager::class);
|
||||
$filterManager = $this->createMock(FilterManager::class);
|
||||
$storage = $this->createMock(StorageInterface::class);
|
||||
$binary = $this->createMock(BinaryInterface::class);
|
||||
|
||||
// Dummy data
|
||||
$product = new \stdClass();
|
||||
$option = new \stdClass();
|
||||
|
||||
$productRepo->method('findAll')->willReturn([$product]);
|
||||
$optionsRepo->method('findAll')->willReturn([$option]);
|
||||
|
||||
// Helper behavior
|
||||
$storage->method('resolveUri')
|
||||
->willReturnMap([
|
||||
[$product, 'imageFile', null, '/uploads/product.jpg'],
|
||||
[$option, 'imageFile', null, '/uploads/option.jpg'],
|
||||
]);
|
||||
|
||||
// Note: resolveUri signature is (obj, fieldName, className). willReturnMap matches arguments exactly.
|
||||
// We need to be careful with arguments.
|
||||
// The command calls: $this->storage->resolveUri($entity, $fieldName);
|
||||
// So arguments are: $entity, $fieldName. The third argument is optional (null default).
|
||||
// PHPUnit willReturnMap might be strict about argument count or we can use `with` and `willReturn`.
|
||||
|
||||
// Let's use `willReturnCallback` or just simpler `willReturn` if we don't care about args,
|
||||
// but we want distinct return values.
|
||||
|
||||
$storage->method('resolveUri')
|
||||
->willReturnCallback(function($entity, $field) use ($product, $option) {
|
||||
if ($entity === $product && $field === 'imageFile') return '/uploads/product.jpg';
|
||||
if ($entity === $option && $field === 'imageFile') return '/uploads/option.jpg';
|
||||
return null;
|
||||
});
|
||||
|
||||
// Filters defined in Command (must match private const FILTERS)
|
||||
$filters = ['webp', 'logo', 'product_card', 'poster_hero'];
|
||||
$filtersCount = count($filters);
|
||||
|
||||
// 2 entities * 4 filters = 8 operations
|
||||
$cacheManager->expects($this->exactly(2 * $filtersCount))->method('remove');
|
||||
$dataManager->expects($this->exactly(2 * $filtersCount))->method('find')->willReturn($binary);
|
||||
$filterManager->expects($this->exactly(2 * $filtersCount))->method('applyFilter')->willReturn($binary);
|
||||
$cacheManager->expects($this->exactly(2 * $filtersCount))->method('store');
|
||||
|
||||
// Instantiate Command
|
||||
$command = new AppWarmupImagesCommand(
|
||||
$productRepo,
|
||||
$optionsRepo,
|
||||
$cacheManager,
|
||||
$dataManager,
|
||||
$filterManager,
|
||||
$storage
|
||||
);
|
||||
|
||||
$application = new Application();
|
||||
$application->add($command);
|
||||
|
||||
$command = $application->find('app:images:warmup');
|
||||
$commandTester = new CommandTester($command);
|
||||
|
||||
$commandTester->execute([]);
|
||||
|
||||
$commandTester->assertCommandIsSuccessful();
|
||||
|
||||
$output = $commandTester->getDisplay();
|
||||
$this->assertStringContainsString('Régénération FORCÉE du cache LiipImagine', $output);
|
||||
$this->assertStringContainsString('Toutes les images ont été régénérées avec succès.', $output);
|
||||
}
|
||||
}
|
||||
171
tests/Command/BackupCommandTest.php
Normal file
171
tests/Command/BackupCommandTest.php
Normal file
@@ -0,0 +1,171 @@
|
||||
<?php
|
||||
|
||||
namespace App\Tests\Command;
|
||||
|
||||
use App\Command\BackupCommand;
|
||||
use App\Entity\Backup;
|
||||
use App\Service\Mailer\Mailer;
|
||||
use App\Service\System\DatabaseDumper;
|
||||
use App\Service\System\ZipArchiver;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Query;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Console\Application;
|
||||
use Symfony\Component\Console\Tester\CommandTester;
|
||||
use Symfony\Component\HttpKernel\KernelInterface;
|
||||
|
||||
class BackupCommandTest extends TestCase
|
||||
{
|
||||
private MockObject&KernelInterface $kernel;
|
||||
private MockObject&EntityManagerInterface $entityManager;
|
||||
private MockObject&Mailer $mailer;
|
||||
private MockObject&DatabaseDumper $databaseDumper;
|
||||
private MockObject&ZipArchiver $zipArchiver;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->kernel = $this->createMock(KernelInterface::class);
|
||||
$this->entityManager = $this->createMock(EntityManagerInterface::class);
|
||||
$this->mailer = $this->createMock(Mailer::class);
|
||||
$this->databaseDumper = $this->createMock(DatabaseDumper::class);
|
||||
$this->zipArchiver = $this->createMock(ZipArchiver::class);
|
||||
}
|
||||
|
||||
public function testExecuteSuccess()
|
||||
{
|
||||
// 1. Setup Data
|
||||
$projectDir = sys_get_temp_dir() . '/backup_test_' . uniqid();
|
||||
mkdir($projectDir . '/sauvegarde', 0777, true);
|
||||
|
||||
$this->kernel->method('getProjectDir')->willReturn($projectDir);
|
||||
|
||||
// 2. Expectations
|
||||
|
||||
// EntityManager
|
||||
$this->entityManager->expects($this->once())->method('persist')->with($this->isInstanceOf(Backup::class));
|
||||
$this->entityManager->expects($this->atLeast(2))->method('flush'); // Once at start, once at end
|
||||
|
||||
// Cleanup expectations
|
||||
// Mocking Query class which might be final. If so, we'll see an error.
|
||||
// But commonly in Doctrine mocks, we have to deal with this.
|
||||
$query = $this->createMock(Query::class);
|
||||
$query->method('setParameter')->willReturnSelf();
|
||||
$query->method('execute');
|
||||
$this->entityManager->method('createQuery')->willReturn($query);
|
||||
|
||||
// Database Dumper
|
||||
$this->databaseDumper->expects($this->once())
|
||||
->method('dump')
|
||||
->with($this->anything(), $this->stringEndsWith('temp_db.sql'));
|
||||
|
||||
// Zip Archiver
|
||||
$this->zipArchiver->expects($this->once())
|
||||
->method('createArchive')
|
||||
->willReturnCallback(function($zipPath, $sqlPath, $dirs) {
|
||||
// Create a dummy zip file so filesize() works
|
||||
touch($zipPath);
|
||||
});
|
||||
|
||||
// Mailer
|
||||
$this->mailer->expects($this->once())
|
||||
->method('send')
|
||||
->with(
|
||||
'notification@siteconseil.fr',
|
||||
'Intranet Ludievent',
|
||||
$this->stringContains('✅ Sauvegarde'),
|
||||
'mails/backup_notification.twig',
|
||||
$this->callback(function($context) {
|
||||
return isset($context['backup']) && $context['backup']->getStatus() === 'SUCCESS';
|
||||
})
|
||||
);
|
||||
|
||||
// 3. Execution
|
||||
$command = new BackupCommand(
|
||||
$this->kernel,
|
||||
$this->entityManager,
|
||||
$this->mailer,
|
||||
$this->databaseDumper,
|
||||
$this->zipArchiver
|
||||
);
|
||||
|
||||
$application = new Application();
|
||||
$application->add($command);
|
||||
$commandTester = new CommandTester($application->find('app:backup'));
|
||||
|
||||
$commandTester->execute([]);
|
||||
|
||||
// 4. Assertions
|
||||
$output = $commandTester->getDisplay();
|
||||
$this->assertStringContainsString('Sauvegarde réussie', $output);
|
||||
|
||||
// Cleanup
|
||||
$this->removeDirectory($projectDir);
|
||||
}
|
||||
|
||||
public function testExecuteFailure()
|
||||
{
|
||||
// 1. Setup Data
|
||||
$projectDir = sys_get_temp_dir() . '/backup_test_fail_' . uniqid();
|
||||
mkdir($projectDir . '/sauvegarde', 0777, true);
|
||||
|
||||
$this->kernel->method('getProjectDir')->willReturn($projectDir);
|
||||
|
||||
// 2. Expectations - Fail at Dump
|
||||
$this->databaseDumper->expects($this->once())
|
||||
->method('dump')
|
||||
->willThrowException(new \Exception("Simulated Dump Error"));
|
||||
|
||||
// Mailer - Should send Error notification
|
||||
$this->mailer->expects($this->once())
|
||||
->method('send')
|
||||
->with(
|
||||
'notification@siteconseil.fr',
|
||||
'Intranet Ludievent',
|
||||
$this->stringContains('❌ Sauvegarde'),
|
||||
'mails/backup_notification.twig',
|
||||
$this->callback(function($context) {
|
||||
return isset($context['backup'])
|
||||
&& $context['backup']->getStatus() === 'ERROR'
|
||||
&& $context['backup']->getErrorMessage() === 'Simulated Dump Error';
|
||||
})
|
||||
);
|
||||
|
||||
// Cleanup expectations
|
||||
$query = $this->createMock(Query::class);
|
||||
$query->method('setParameter')->willReturnSelf();
|
||||
$query->method('execute');
|
||||
$this->entityManager->method('createQuery')->willReturn($query);
|
||||
|
||||
// 3. Execution
|
||||
$command = new BackupCommand(
|
||||
$this->kernel,
|
||||
$this->entityManager,
|
||||
$this->mailer,
|
||||
$this->databaseDumper,
|
||||
$this->zipArchiver
|
||||
);
|
||||
|
||||
$application = new Application();
|
||||
$application->add($command);
|
||||
$commandTester = new CommandTester($application->find('app:backup'));
|
||||
|
||||
$commandTester->execute([]);
|
||||
|
||||
// 4. Assertions
|
||||
$output = $commandTester->getDisplay();
|
||||
$this->assertStringContainsString('Échec : Simulated Dump Error', $output);
|
||||
|
||||
// Cleanup
|
||||
$this->removeDirectory($projectDir);
|
||||
}
|
||||
|
||||
private function removeDirectory($dir) {
|
||||
if (!is_dir($dir)) return;
|
||||
$files = array_diff(scandir($dir), array('.','..'));
|
||||
foreach ($files as $file) {
|
||||
(is_dir("$dir/$file")) ? $this->removeDirectory("$dir/$file") : unlink("$dir/$file");
|
||||
}
|
||||
rmdir($dir);
|
||||
}
|
||||
}
|
||||
107
tests/Command/CleanCommandTest.php
Normal file
107
tests/Command/CleanCommandTest.php
Normal file
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
namespace App\Tests\Command;
|
||||
|
||||
use App\Command\CleanCommand;
|
||||
use App\Entity\CustomerTracking;
|
||||
use App\Entity\SitePerformance;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Query;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Console\Application;
|
||||
use Symfony\Component\Console\Tester\CommandTester;
|
||||
|
||||
class CleanCommandTest extends TestCase
|
||||
{
|
||||
private MockObject&EntityManagerInterface $entityManager;
|
||||
private MockObject&QueryBuilder $qb1;
|
||||
private MockObject&QueryBuilder $qb2;
|
||||
private MockObject&Query $query1;
|
||||
private MockObject&Query $query2;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->entityManager = $this->createMock(EntityManagerInterface::class);
|
||||
$this->qb1 = $this->createMock(QueryBuilder::class);
|
||||
$this->qb2 = $this->createMock(QueryBuilder::class);
|
||||
$this->query1 = $this->createMock(Query::class);
|
||||
$this->query2 = $this->createMock(Query::class);
|
||||
}
|
||||
|
||||
public function testExecute()
|
||||
{
|
||||
// Configure EntityManager to return two different QueryBuilders in sequence
|
||||
$this->entityManager->expects($this->exactly(2))
|
||||
->method('createQueryBuilder')
|
||||
->willReturnOnConsecutiveCalls($this->qb1, $this->qb2);
|
||||
|
||||
// --- Sequence 1: SitePerformance ---
|
||||
$this->qb1->expects($this->once())
|
||||
->method('delete')
|
||||
->with(SitePerformance::class, 'p')
|
||||
->willReturnSelf();
|
||||
|
||||
$this->qb1->expects($this->once())
|
||||
->method('where')
|
||||
->with('p.createdAt <= :limit')
|
||||
->willReturnSelf();
|
||||
|
||||
$this->qb1->expects($this->once())
|
||||
->method('setParameter')
|
||||
->with('limit', $this->isInstanceOf(\DateTime::class))
|
||||
->willReturnSelf();
|
||||
|
||||
$this->qb1->expects($this->once())
|
||||
->method('getQuery')
|
||||
->willReturn($this->query1);
|
||||
|
||||
$this->query1->expects($this->once())
|
||||
->method('execute')
|
||||
->willReturn(5); // Simulate 5 records deleted
|
||||
|
||||
// --- Sequence 2: CustomerTracking ---
|
||||
$this->qb2->expects($this->once())
|
||||
->method('delete')
|
||||
->with(CustomerTracking::class, 't')
|
||||
->willReturnSelf();
|
||||
|
||||
$this->qb2->expects($this->once())
|
||||
->method('where')
|
||||
->with('t.createAT <= :limit')
|
||||
->willReturnSelf();
|
||||
|
||||
$this->qb2->expects($this->once())
|
||||
->method('setParameter')
|
||||
->with('limit', $this->isInstanceOf(\DateTime::class))
|
||||
->willReturnSelf();
|
||||
|
||||
$this->qb2->expects($this->once())
|
||||
->method('getQuery')
|
||||
->willReturn($this->query2);
|
||||
|
||||
$this->query2->expects($this->once())
|
||||
->method('execute')
|
||||
->willReturn(10); // Simulate 10 records deleted
|
||||
|
||||
// Expect final flush
|
||||
$this->entityManager->expects($this->once())
|
||||
->method('flush');
|
||||
|
||||
// Instantiate and run Command
|
||||
$command = new CleanCommand($this->entityManager);
|
||||
$application = new Application();
|
||||
$application->add($command);
|
||||
|
||||
$commandTester = new CommandTester($application->find('app:clean'));
|
||||
$commandTester->execute([]);
|
||||
|
||||
$output = $commandTester->getDisplay();
|
||||
|
||||
// Assertions on output
|
||||
$this->assertStringContainsString('5 entrées de performance supprimées', $output);
|
||||
$this->assertStringContainsString('10 entrées de tracking supprimées', $output);
|
||||
$this->assertStringContainsString('Nettoyage terminé avec succès', $output);
|
||||
}
|
||||
}
|
||||
135
tests/Command/DeployConfigCommandTest.php
Normal file
135
tests/Command/DeployConfigCommandTest.php
Normal file
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
|
||||
namespace App\Tests\Command;
|
||||
|
||||
use App\Command\DeployConfigCommand;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Console\Application;
|
||||
use Symfony\Component\Console\Tester\CommandTester;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||
use Symfony\Contracts\HttpClient\ResponseInterface;
|
||||
|
||||
class DeployConfigCommandTest extends TestCase
|
||||
{
|
||||
private MockObject&ParameterBagInterface $parameterBag;
|
||||
private MockObject&HttpClientInterface $httpClient;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->parameterBag = $this->createMock(ParameterBagInterface::class);
|
||||
$this->httpClient = $this->createMock(HttpClientInterface::class);
|
||||
}
|
||||
|
||||
public function testExecuteMissingToken()
|
||||
{
|
||||
// Setup
|
||||
$this->parameterBag->method('get')->willReturn('/tmp');
|
||||
|
||||
// Remove CLOUDFLARE_DEPLOY from env if it exists (for this test)
|
||||
$originalEnv = $_ENV['CLOUDFLARE_DEPLOY'] ?? null;
|
||||
unset($_ENV['CLOUDFLARE_DEPLOY']);
|
||||
unset($_SERVER['CLOUDFLARE_DEPLOY']); // Safety
|
||||
|
||||
// Execute
|
||||
$command = new DeployConfigCommand($this->parameterBag, $this->httpClient);
|
||||
$application = new Application();
|
||||
$application->add($command);
|
||||
$commandTester = new CommandTester($application->find('app:deploy:config'));
|
||||
|
||||
$commandTester->execute([]);
|
||||
|
||||
// Restore Env
|
||||
if ($originalEnv) $_ENV['CLOUDFLARE_DEPLOY'] = $originalEnv;
|
||||
|
||||
// Assert
|
||||
$output = $commandTester->getDisplay();
|
||||
$this->assertStringContainsString('La clé API Cloudflare (CLOUDFLARE_DEPLOY) est manquante', $output);
|
||||
$this->assertEquals(1, $commandTester->getStatusCode());
|
||||
}
|
||||
|
||||
public function testExecuteSuccess()
|
||||
{
|
||||
// Setup
|
||||
$this->parameterBag->method('get')->willReturn(sys_get_temp_dir());
|
||||
$_ENV['CLOUDFLARE_DEPLOY'] = 'test_token'; // Mock environment variable
|
||||
|
||||
// --- Mocking Cloudflare API Responses ---
|
||||
|
||||
// 1. Zone ID Request
|
||||
$zoneResponse = $this->createMock(ResponseInterface::class);
|
||||
$zoneResponse->method('toArray')->willReturn([
|
||||
'result' => [['id' => 'zone_123']]
|
||||
]);
|
||||
|
||||
// 2. Rulesets List Request (Found existing ruleset)
|
||||
$rulesetsListResponse = $this->createMock(ResponseInterface::class);
|
||||
$rulesetsListResponse->method('toArray')->willReturn([
|
||||
'result' => [
|
||||
['id' => 'rs_123', 'phase' => 'http_request_cache_settings']
|
||||
]
|
||||
]);
|
||||
|
||||
// 3. Get Specific Ruleset Rules
|
||||
$rulesResponse = $this->createMock(ResponseInterface::class);
|
||||
$rulesResponse->method('toArray')->willReturn([
|
||||
'result' => ['rules' => []]
|
||||
]);
|
||||
|
||||
// 4. Update Ruleset (PUT)
|
||||
$updateResponse = $this->createMock(ResponseInterface::class);
|
||||
$updateResponse->method('toArray')->willReturn(['success' => true]);
|
||||
|
||||
// Configure HttpClient Sequence using Consecutive Calls
|
||||
$this->httpClient->expects($this->exactly(4))
|
||||
->method('request')
|
||||
->willReturnOnConsecutiveCalls(
|
||||
$zoneResponse,
|
||||
$rulesetsListResponse,
|
||||
$rulesResponse,
|
||||
$updateResponse
|
||||
);
|
||||
|
||||
// Execute
|
||||
$command = new DeployConfigCommand($this->parameterBag, $this->httpClient);
|
||||
$application = new Application();
|
||||
$application->add($command);
|
||||
$commandTester = new CommandTester($application->find('app:deploy:config'));
|
||||
|
||||
$commandTester->execute([]);
|
||||
|
||||
// Assert
|
||||
$output = $commandTester->getDisplay();
|
||||
$this->assertStringContainsString('Ruleset Cloudflare mis à jour', $output);
|
||||
$this->assertEquals(0, $commandTester->getStatusCode());
|
||||
}
|
||||
|
||||
public function testExecuteZoneNotFound()
|
||||
{
|
||||
// Setup
|
||||
$this->parameterBag->method('get')->willReturn(sys_get_temp_dir());
|
||||
$_ENV['CLOUDFLARE_DEPLOY'] = 'test_token';
|
||||
|
||||
// Zone Request - Empty Result
|
||||
$zoneResponse = $this->createMock(ResponseInterface::class);
|
||||
$zoneResponse->method('toArray')->willReturn(['result' => []]);
|
||||
|
||||
$this->httpClient->expects($this->once())
|
||||
->method('request')
|
||||
->willReturn($zoneResponse);
|
||||
|
||||
// Execute
|
||||
$command = new DeployConfigCommand($this->parameterBag, $this->httpClient);
|
||||
$application = new Application();
|
||||
$application->add($command);
|
||||
$commandTester = new CommandTester($application->find('app:deploy:config'));
|
||||
|
||||
$commandTester->execute([]);
|
||||
|
||||
// Assert
|
||||
$output = $commandTester->getDisplay();
|
||||
$this->assertStringContainsString('Zone introuvable', $output);
|
||||
$this->assertEquals(1, $commandTester->getStatusCode());
|
||||
}
|
||||
}
|
||||
87
tests/Command/GenerateVideoThumbsCommandTest.php
Normal file
87
tests/Command/GenerateVideoThumbsCommandTest.php
Normal file
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
namespace App\Tests\Command;
|
||||
|
||||
use App\Command\GenerateVideoThumbsCommand;
|
||||
use App\Service\Media\VideoThumbnailer;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Console\Application;
|
||||
use Symfony\Component\Console\Tester\CommandTester;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||
|
||||
class GenerateVideoThumbsCommandTest extends TestCase
|
||||
{
|
||||
private MockObject&ParameterBagInterface $parameterBag;
|
||||
private MockObject&VideoThumbnailer $videoThumbnailer;
|
||||
private string $tempDir;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->parameterBag = $this->createMock(ParameterBagInterface::class);
|
||||
$this->videoThumbnailer = $this->createMock(VideoThumbnailer::class);
|
||||
|
||||
$this->tempDir = sys_get_temp_dir() . '/thumbs_test_' . uniqid();
|
||||
mkdir($this->tempDir . '/public/provider/video', 0777, true);
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
$this->removeDirectory($this->tempDir);
|
||||
}
|
||||
|
||||
public function testExecuteVideoNotFound()
|
||||
{
|
||||
// Setup mock
|
||||
$this->parameterBag->method('get')->with('kernel.project_dir')->willReturn($this->tempDir);
|
||||
|
||||
// Execute
|
||||
$command = new GenerateVideoThumbsCommand($this->parameterBag, $this->videoThumbnailer);
|
||||
$application = new Application();
|
||||
$application->add($command);
|
||||
$commandTester = new CommandTester($application->find('app:generate-video-thumbs'));
|
||||
|
||||
$commandTester->execute([]);
|
||||
|
||||
// Assert
|
||||
$output = $commandTester->getDisplay();
|
||||
$this->assertStringContainsString('Vidéo introuvable', $output);
|
||||
}
|
||||
|
||||
public function testExecuteSuccess()
|
||||
{
|
||||
// Setup file
|
||||
touch($this->tempDir . '/public/provider/video/video.mp4');
|
||||
|
||||
// Setup mock
|
||||
$this->parameterBag->method('get')->with('kernel.project_dir')->willReturn($this->tempDir);
|
||||
|
||||
$this->videoThumbnailer->expects($this->once())
|
||||
->method('generateThumbnail')
|
||||
->with(
|
||||
$this->stringEndsWith('video.mp4'),
|
||||
$this->stringEndsWith('video.jpg')
|
||||
);
|
||||
|
||||
// Execute
|
||||
$command = new GenerateVideoThumbsCommand($this->parameterBag, $this->videoThumbnailer);
|
||||
$application = new Application();
|
||||
$application->add($command);
|
||||
$commandTester = new CommandTester($application->find('app:generate-video-thumbs'));
|
||||
|
||||
$commandTester->execute([]);
|
||||
|
||||
// Assert
|
||||
$output = $commandTester->getDisplay();
|
||||
$this->assertStringContainsString('[OK] Miniature générée', $output);
|
||||
}
|
||||
|
||||
private function removeDirectory($dir) {
|
||||
if (!is_dir($dir)) return;
|
||||
$files = array_diff(scandir($dir), array('.','..'));
|
||||
foreach ($files as $file) {
|
||||
(is_dir("$dir/$file")) ? $this->removeDirectory("$dir/$file") : unlink("$dir/$file");
|
||||
}
|
||||
rmdir($dir);
|
||||
}
|
||||
}
|
||||
138
tests/Command/GitSyncLogCommandTest.php
Normal file
138
tests/Command/GitSyncLogCommandTest.php
Normal file
@@ -0,0 +1,138 @@
|
||||
<?php
|
||||
|
||||
namespace App\Tests\Command;
|
||||
|
||||
use App\Command\GitSyncLogCommand;
|
||||
use App\Service\AI\GeminiClient;
|
||||
use App\Service\System\GitClient;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Console\Application;
|
||||
use Symfony\Component\Console\Tester\CommandTester;
|
||||
use Symfony\Component\HttpKernel\KernelInterface;
|
||||
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||
use Symfony\Contracts\HttpClient\ResponseInterface;
|
||||
|
||||
class GitSyncLogCommandTest extends TestCase
|
||||
{
|
||||
private MockObject&HttpClientInterface $httpClient;
|
||||
private MockObject&KernelInterface $kernel;
|
||||
private MockObject&GitClient $gitClient;
|
||||
private MockObject&GeminiClient $geminiClient;
|
||||
private string $tempDir;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->httpClient = $this->createMock(HttpClientInterface::class);
|
||||
$this->kernel = $this->createMock(KernelInterface::class);
|
||||
$this->gitClient = $this->createMock(GitClient::class);
|
||||
$this->geminiClient = $this->createMock(GeminiClient::class);
|
||||
|
||||
$this->tempDir = sys_get_temp_dir() . '/git_log_test_' . uniqid();
|
||||
mkdir($this->tempDir . '/var', 0777, true);
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
$this->removeDirectory($this->tempDir);
|
||||
}
|
||||
|
||||
public function testExecuteNewCommit()
|
||||
{
|
||||
// 1. Setup Mocks
|
||||
$this->kernel->method('getProjectDir')->willReturn($this->tempDir);
|
||||
|
||||
$this->gitClient->expects($this->once())
|
||||
->method('getLastCommitInfo')
|
||||
->willReturn([
|
||||
'message' => 'feat: add awesome feature',
|
||||
'date' => '2026-01-30 10:00:00',
|
||||
'hash' => 'hash123'
|
||||
]);
|
||||
|
||||
$this->geminiClient->expects($this->once())
|
||||
->method('generateFriendlyMessage')
|
||||
->with('feat: add awesome feature')
|
||||
->willReturn('Super nouvelle fonctionnalité ajoutée !');
|
||||
|
||||
$this->httpClient->expects($this->once())
|
||||
->method('request')
|
||||
->with('POST', $this->stringContains('discord.com'), $this->arrayHasKey('json'));
|
||||
|
||||
// 2. Execute
|
||||
$command = new GitSyncLogCommand(
|
||||
$this->httpClient,
|
||||
$this->kernel,
|
||||
$this->gitClient,
|
||||
$this->geminiClient
|
||||
);
|
||||
$application = new Application();
|
||||
$application->add($command);
|
||||
$commandTester = new CommandTester($application->find('app:git-log-update'));
|
||||
|
||||
$commandTester->execute([]);
|
||||
|
||||
// 3. Assert Output
|
||||
$output = $commandTester->getDisplay();
|
||||
$this->assertStringContainsString('Journal client mis à jour avec succès', $output);
|
||||
|
||||
// 4. Assert File Content
|
||||
$filePath = $this->tempDir . '/var/update.json';
|
||||
$this->assertFileExists($filePath);
|
||||
$data = json_decode(file_get_contents($filePath), true);
|
||||
$this->assertCount(1, $data);
|
||||
$this->assertEquals('feature', $data[0]['type']);
|
||||
$this->assertEquals('Super nouvelle fonctionnalité ajoutée !', $data[0]['message']);
|
||||
$this->assertEquals('hash123', $data[0]['hash']);
|
||||
}
|
||||
|
||||
public function testExecuteAlreadyUpToDate()
|
||||
{
|
||||
// 1. Setup File
|
||||
$filePath = $this->tempDir . '/var/update.json';
|
||||
file_put_contents($filePath, json_encode([
|
||||
['hash' => 'hash123']
|
||||
]));
|
||||
|
||||
// 2. Setup Mocks
|
||||
$this->kernel->method('getProjectDir')->willReturn($this->tempDir);
|
||||
|
||||
$this->gitClient->expects($this->once())
|
||||
->method('getLastCommitInfo')
|
||||
->willReturn([
|
||||
'message' => 'fix: bug',
|
||||
'date' => '2026-01-30 12:00:00',
|
||||
'hash' => 'hash123' // Same hash
|
||||
]);
|
||||
|
||||
// Gemini & Discord should NOT be called
|
||||
$this->geminiClient->expects($this->never())->method('generateFriendlyMessage');
|
||||
$this->httpClient->expects($this->never())->method('request');
|
||||
|
||||
// 3. Execute
|
||||
$command = new GitSyncLogCommand(
|
||||
$this->httpClient,
|
||||
$this->kernel,
|
||||
$this->gitClient,
|
||||
$this->geminiClient
|
||||
);
|
||||
$application = new Application();
|
||||
$application->add($command);
|
||||
$commandTester = new CommandTester($application->find('app:git-log-update'));
|
||||
|
||||
$commandTester->execute([]);
|
||||
|
||||
// 4. Assert Output
|
||||
$output = $commandTester->getDisplay();
|
||||
$this->assertStringContainsString('déjà à jour', $output);
|
||||
}
|
||||
|
||||
private function removeDirectory($dir) {
|
||||
if (!is_dir($dir)) return;
|
||||
$files = array_diff(scandir($dir), array('.','..'));
|
||||
foreach ($files as $file) {
|
||||
(is_dir("$dir/$file")) ? $this->removeDirectory("$dir/$file") : unlink("$dir/$file");
|
||||
}
|
||||
rmdir($dir);
|
||||
}
|
||||
}
|
||||
150
tests/Command/MailCommandTest.php
Normal file
150
tests/Command/MailCommandTest.php
Normal file
@@ -0,0 +1,150 @@
|
||||
<?php
|
||||
|
||||
namespace App\Tests\Command;
|
||||
|
||||
use App\Command\MailCommand;
|
||||
use App\Entity\Contrats;
|
||||
use App\Entity\Customer;
|
||||
use App\Entity\Devis;
|
||||
use App\Service\Mailer\Mailer;
|
||||
use App\Service\Signature\Client;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Doctrine\ORM\Query;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Console\Application;
|
||||
use Symfony\Component\Console\Tester\CommandTester;
|
||||
use Symfony\Component\HttpKernel\KernelInterface;
|
||||
|
||||
class MailCommandTest extends TestCase
|
||||
{
|
||||
private MockObject&KernelInterface $kernel;
|
||||
private MockObject&Client $client;
|
||||
private MockObject&Mailer $mailer;
|
||||
private MockObject&EntityManagerInterface $entityManager;
|
||||
private MockObject&EntityRepository $devisRepository;
|
||||
private MockObject&EntityRepository $contratsRepository;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->kernel = $this->createMock(KernelInterface::class);
|
||||
$this->client = $this->createMock(Client::class);
|
||||
$this->mailer = $this->createMock(Mailer::class);
|
||||
$this->entityManager = $this->createMock(EntityManagerInterface::class);
|
||||
$this->devisRepository = $this->createMock(EntityRepository::class);
|
||||
$this->contratsRepository = $this->createMock(EntityRepository::class);
|
||||
|
||||
// Setup repository mocks
|
||||
$this->entityManager->method('getRepository')
|
||||
->willReturnMap([
|
||||
[Devis::class, $this->devisRepository],
|
||||
[Contrats::class, $this->contratsRepository],
|
||||
]);
|
||||
}
|
||||
|
||||
public function testExecuteProcessUnsignedDevisDeletion()
|
||||
{
|
||||
// Setup Devis to be deleted (> 3 days)
|
||||
$devis = $this->createMock(Devis::class);
|
||||
$devis->method('getCreateA')->willReturn(new \DateTimeImmutable('-4 days'));
|
||||
$devis->method('getNum')->willReturn('D-123');
|
||||
// Ensure getDevisLines/Options return iterables
|
||||
$devis->method('getDevisLines')->willReturn(new ArrayCollection([]));
|
||||
$devis->method('getDevisOptions')->willReturn(new ArrayCollection([]));
|
||||
|
||||
$this->devisRepository->expects($this->once())
|
||||
->method('findBy')
|
||||
->with(['state' => 'wait-sign'])
|
||||
->willReturn([$devis]);
|
||||
|
||||
// Expect removal
|
||||
$this->entityManager->expects($this->once())->method('remove')->with($devis);
|
||||
|
||||
// Mock other calls to return empty to isolate this test case
|
||||
$this->contratsRepository->method('findBy')->willReturn([]);
|
||||
|
||||
// Mock QueryBuilder for sendEventReminders and Satisfaction which use createQueryBuilder
|
||||
$qb = $this->createMock(QueryBuilder::class);
|
||||
$query = $this->createMock(Query::class);
|
||||
$qb->method('where')->willReturnSelf();
|
||||
$qb->method('andWhere')->willReturnSelf();
|
||||
$qb->method('setParameter')->willReturnSelf();
|
||||
$qb->method('getQuery')->willReturn($query);
|
||||
$query->method('getResult')->willReturn([]);
|
||||
|
||||
$this->contratsRepository->method('createQueryBuilder')->willReturn($qb);
|
||||
|
||||
// Execute
|
||||
$command = new MailCommand(
|
||||
$this->kernel,
|
||||
$this->client,
|
||||
$this->mailer,
|
||||
$this->entityManager
|
||||
);
|
||||
$application = new Application();
|
||||
$application->add($command);
|
||||
$commandTester = new CommandTester($application->find('app:mail'));
|
||||
|
||||
$commandTester->execute([]);
|
||||
|
||||
$output = $commandTester->getDisplay();
|
||||
$this->assertStringContainsString('Suppression du devis N°D-123', $output);
|
||||
}
|
||||
|
||||
public function testExecuteProcessUnsignedDevisReminder()
|
||||
{
|
||||
// Setup Devis to be reminded (1 day)
|
||||
$customer = $this->createMock(Customer::class);
|
||||
$customer->method('getEmail')->willReturn('test@example.com');
|
||||
$customer->method('getName')->willReturn('John');
|
||||
|
||||
$devis = $this->createMock(Devis::class);
|
||||
$devis->method('getCreateA')->willReturn(new \DateTimeImmutable('-1 day -2 hours'));
|
||||
$devis->method('getNum')->willReturn('D-456');
|
||||
$devis->method('getCustomer')->willReturn($customer);
|
||||
$devis->method('getSignatureId')->willReturn('sign_123');
|
||||
|
||||
$this->devisRepository->expects($this->once())
|
||||
->method('findBy')
|
||||
->with(['state' => 'wait-sign'])
|
||||
->willReturn([$devis]);
|
||||
|
||||
// Expect Mailer call
|
||||
$this->mailer->expects($this->once())
|
||||
->method('send')
|
||||
->with('test@example.com', 'John', $this->stringContains('Devis N°D-456'));
|
||||
|
||||
// Mock other calls to return empty
|
||||
$this->contratsRepository->method('findBy')->willReturn([]);
|
||||
|
||||
// Mock QueryBuilder
|
||||
$qb = $this->createMock(QueryBuilder::class);
|
||||
$query = $this->createMock(Query::class);
|
||||
$qb->method('where')->willReturnSelf();
|
||||
$qb->method('andWhere')->willReturnSelf();
|
||||
$qb->method('setParameter')->willReturnSelf();
|
||||
$qb->method('getQuery')->willReturn($query);
|
||||
$query->method('getResult')->willReturn([]);
|
||||
|
||||
$this->contratsRepository->method('createQueryBuilder')->willReturn($qb);
|
||||
|
||||
// Execute
|
||||
$command = new MailCommand(
|
||||
$this->kernel,
|
||||
$this->client,
|
||||
$this->mailer,
|
||||
$this->entityManager
|
||||
);
|
||||
$application = new Application();
|
||||
$application->add($command);
|
||||
$commandTester = new CommandTester($application->find('app:mail'));
|
||||
|
||||
$commandTester->execute([]);
|
||||
|
||||
$output = $commandTester->getDisplay();
|
||||
$this->assertStringContainsString('Relance envoyée pour le devis : D-456', $output);
|
||||
}
|
||||
}
|
||||
64
tests/Command/MaintenanceCommandTest.php
Normal file
64
tests/Command/MaintenanceCommandTest.php
Normal file
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace App\Tests\Command;
|
||||
|
||||
use App\Command\MaintenanceCommand;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Console\Application;
|
||||
use Symfony\Component\Console\Tester\CommandTester;
|
||||
|
||||
class MaintenanceCommandTest extends TestCase
|
||||
{
|
||||
private string $tempDir;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->tempDir = sys_get_temp_dir() . '/maintenance_test_' . uniqid();
|
||||
mkdir($this->tempDir . '/var', 0777, true);
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
$this->removeDirectory($this->tempDir);
|
||||
}
|
||||
|
||||
public function testExecuteOn()
|
||||
{
|
||||
$command = new MaintenanceCommand($this->tempDir);
|
||||
$application = new Application();
|
||||
$application->add($command);
|
||||
$commandTester = new CommandTester($application->find('app:maintenance'));
|
||||
|
||||
$commandTester->execute(['status' => 'on']);
|
||||
|
||||
$output = $commandTester->getDisplay();
|
||||
$this->assertStringContainsString('Mode maintenance ACTIVÉ', $output);
|
||||
$this->assertFileExists($this->tempDir . '/var/.maintenance');
|
||||
}
|
||||
|
||||
public function testExecuteOff()
|
||||
{
|
||||
// Ensure file exists first
|
||||
touch($this->tempDir . '/var/.maintenance');
|
||||
|
||||
$command = new MaintenanceCommand($this->tempDir);
|
||||
$application = new Application();
|
||||
$application->add($command);
|
||||
$commandTester = new CommandTester($application->find('app:maintenance'));
|
||||
|
||||
$commandTester->execute(['status' => 'off']);
|
||||
|
||||
$output = $commandTester->getDisplay();
|
||||
$this->assertStringContainsString('Mode maintenance DÉSACTIVÉ', $output);
|
||||
$this->assertFileDoesNotExist($this->tempDir . '/var/.maintenance');
|
||||
}
|
||||
|
||||
private function removeDirectory($dir) {
|
||||
if (!is_dir($dir)) return;
|
||||
$files = array_diff(scandir($dir), array('.','..'));
|
||||
foreach ($files as $file) {
|
||||
(is_dir("$dir/$file")) ? $this->removeDirectory("$dir/$file") : unlink("$dir/$file");
|
||||
}
|
||||
rmdir($dir);
|
||||
}
|
||||
}
|
||||
65
tests/Command/PurgeCommandTest.php
Normal file
65
tests/Command/PurgeCommandTest.php
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace App\Tests\Command;
|
||||
|
||||
use App\Command\PurgeCommand;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Console\Application;
|
||||
use Symfony\Component\Console\Tester\CommandTester;
|
||||
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||
use Symfony\Contracts\HttpClient\ResponseInterface;
|
||||
|
||||
class PurgeCommandTest extends TestCase
|
||||
{
|
||||
private MockObject&HttpClientInterface $httpClient;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->httpClient = $this->createMock(HttpClientInterface::class);
|
||||
}
|
||||
|
||||
public function testExecuteSuccess()
|
||||
{
|
||||
$response = $this->createMock(ResponseInterface::class);
|
||||
$response->method('toArray')->willReturn(['success' => true]);
|
||||
|
||||
$this->httpClient->expects($this->once())
|
||||
->method('request')
|
||||
->with('POST', $this->stringContains('purge_cache'), $this->anything())
|
||||
->willReturn($response);
|
||||
|
||||
$command = new PurgeCommand($this->httpClient, 'zone_id', 'api_token');
|
||||
$application = new Application();
|
||||
$application->add($command);
|
||||
$commandTester = new CommandTester($application->find('app:purge-cloudflare'));
|
||||
|
||||
$commandTester->execute([]);
|
||||
|
||||
$this->assertStringContainsString('entièrement vidé', $commandTester->getDisplay());
|
||||
$this->assertEquals(0, $commandTester->getStatusCode());
|
||||
}
|
||||
|
||||
public function testExecuteFailure()
|
||||
{
|
||||
$response = $this->createMock(ResponseInterface::class);
|
||||
$response->method('toArray')->willReturn([
|
||||
'success' => false,
|
||||
'errors' => [['message' => 'Simulated API Error']]
|
||||
]);
|
||||
|
||||
$this->httpClient->expects($this->once())
|
||||
->method('request')
|
||||
->willReturn($response);
|
||||
|
||||
$command = new PurgeCommand($this->httpClient, 'zone_id', 'api_token');
|
||||
$application = new Application();
|
||||
$application->add($command);
|
||||
$commandTester = new CommandTester($application->find('app:purge-cloudflare'));
|
||||
|
||||
$commandTester->execute([]);
|
||||
|
||||
$this->assertStringContainsString('Erreur Cloudflare : Simulated API Error', $commandTester->getDisplay());
|
||||
$this->assertEquals(1, $commandTester->getStatusCode());
|
||||
}
|
||||
}
|
||||
64
tests/Command/PurgeTxtCommandTest.php
Normal file
64
tests/Command/PurgeTxtCommandTest.php
Normal file
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace App\Tests\Command;
|
||||
|
||||
use App\Command\PurgeTxtCommand;
|
||||
use App\Entity\Formules;
|
||||
use App\Entity\Product;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Console\Application;
|
||||
use Symfony\Component\Console\Tester\CommandTester;
|
||||
|
||||
class PurgeTxtCommandTest extends TestCase
|
||||
{
|
||||
private MockObject&EntityManagerInterface $entityManager;
|
||||
private MockObject&EntityRepository $productRepository;
|
||||
private MockObject&EntityRepository $formulesRepository;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->entityManager = $this->createMock(EntityManagerInterface::class);
|
||||
$this->productRepository = $this->createMock(EntityRepository::class);
|
||||
$this->formulesRepository = $this->createMock(EntityRepository::class);
|
||||
|
||||
$this->entityManager->method('getRepository')->willReturnMap([
|
||||
[Product::class, $this->productRepository],
|
||||
[Formules::class, $this->formulesRepository],
|
||||
]);
|
||||
}
|
||||
|
||||
public function testExecute()
|
||||
{
|
||||
// 1. Setup Product Data
|
||||
$product = new Product();
|
||||
$product->setDescription('<p>Description <b>HTML</b> é purger.</p>');
|
||||
|
||||
$this->productRepository->method('findAll')->willReturn([$product]);
|
||||
|
||||
// 2. Setup Formules Data
|
||||
$formule = new Formules();
|
||||
$formule->setDescription('<div>Une autre <br> description.</div>');
|
||||
|
||||
$this->formulesRepository->method('findAll')->willReturn([$formule]);
|
||||
|
||||
// 3. Expect Flush
|
||||
$this->entityManager->expects($this->once())->method('flush');
|
||||
|
||||
// 4. Execute
|
||||
$command = new PurgeTxtCommand($this->entityManager);
|
||||
$application = new Application();
|
||||
$application->add($command);
|
||||
$commandTester = new CommandTester($application->find('app:txt:purge'));
|
||||
|
||||
$commandTester->execute([]);
|
||||
|
||||
// 5. Assertions
|
||||
$this->assertEquals('Description HTML é purger.', $product->getDescription());
|
||||
$this->assertEquals('Une autre description.', $formule->getDescription());
|
||||
|
||||
$this->assertStringContainsString('Purge terminée', $commandTester->getDisplay());
|
||||
}
|
||||
}
|
||||
147
tests/Command/SearchCommandTest.php
Normal file
147
tests/Command/SearchCommandTest.php
Normal file
@@ -0,0 +1,147 @@
|
||||
<?php
|
||||
|
||||
namespace App\Tests\Command;
|
||||
|
||||
use App\Command\SearchCommand;
|
||||
use App\Entity\Account;
|
||||
use App\Entity\Contrats;
|
||||
use App\Entity\Customer;
|
||||
use App\Entity\Options;
|
||||
use App\Entity\Product;
|
||||
use App\Service\Search\Client;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Console\Application;
|
||||
use Symfony\Component\Console\Tester\CommandTester;
|
||||
|
||||
class SearchCommandTest extends TestCase
|
||||
{
|
||||
private MockObject&EntityManagerInterface $entityManager;
|
||||
private MockObject&Client $client;
|
||||
|
||||
// Repositories
|
||||
private MockObject&EntityRepository $accountRepo;
|
||||
private MockObject&EntityRepository $customerRepo;
|
||||
private MockObject&EntityRepository $productRepo;
|
||||
private MockObject&EntityRepository $optionsRepo;
|
||||
private MockObject&EntityRepository $contratsRepo;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->entityManager = $this->createMock(EntityManagerInterface::class);
|
||||
$this->client = $this->createMock(Client::class);
|
||||
|
||||
$this->accountRepo = $this->createMock(EntityRepository::class);
|
||||
$this->customerRepo = $this->createMock(EntityRepository::class);
|
||||
$this->productRepo = $this->createMock(EntityRepository::class);
|
||||
$this->optionsRepo = $this->createMock(EntityRepository::class);
|
||||
$this->contratsRepo = $this->createMock(EntityRepository::class);
|
||||
|
||||
$this->entityManager->method('getRepository')->willReturnMap([
|
||||
[Account::class, $this->accountRepo],
|
||||
[Customer::class, $this->customerRepo],
|
||||
[Product::class, $this->productRepo],
|
||||
[Options::class, $this->optionsRepo],
|
||||
[Contrats::class, $this->contratsRepo],
|
||||
]);
|
||||
}
|
||||
|
||||
public function testExecute()
|
||||
{
|
||||
// 1. Setup Data
|
||||
|
||||
// Account (one ROOT to skip, one normal to index)
|
||||
$rootAccount = $this->createMock(Account::class);
|
||||
$rootAccount->method('getRoles')->willReturn(['ROLE_ROOT', 'ROLE_USER']);
|
||||
|
||||
$adminAccount = $this->createMock(Account::class);
|
||||
$adminAccount->method('getRoles')->willReturn(['ROLE_ADMIN']);
|
||||
$adminAccount->method('getId')->willReturn(1);
|
||||
$adminAccount->method('getName')->willReturn('Admin');
|
||||
$adminAccount->method('getFirstName')->willReturn('User'); // surname mapped to getFirstName in command
|
||||
$adminAccount->method('getEmail')->willReturn('admin@test.com');
|
||||
|
||||
$this->accountRepo->method('findAll')->willReturn([$rootAccount, $adminAccount]);
|
||||
|
||||
// Customer
|
||||
$customer = $this->createMock(Customer::class);
|
||||
$customer->method('getId')->willReturn(10);
|
||||
$customer->method('getName')->willReturn('Cust');
|
||||
$customer->method('getSurname')->willReturn('Omer');
|
||||
$customer->method('getSiret')->willReturn('123');
|
||||
$customer->method('getCiv')->willReturn('Mr');
|
||||
$customer->method('getType')->willReturn('pro');
|
||||
$customer->method('getPhone')->willReturn('0102030405');
|
||||
$customer->method('getEmail')->willReturn('cust@test.com');
|
||||
|
||||
$this->customerRepo->method('findAll')->willReturn([$customer]);
|
||||
|
||||
// Product
|
||||
$product = $this->createMock(Product::class);
|
||||
$product->method('getId')->willReturn(20);
|
||||
$product->method('getName')->willReturn('Prod');
|
||||
$product->method('getRef')->willReturn('REF001');
|
||||
|
||||
$this->productRepo->method('findAll')->willReturn([$product]);
|
||||
|
||||
// Options
|
||||
$option = $this->createMock(Options::class);
|
||||
$option->method('getId')->willReturn(30);
|
||||
$option->method('getName')->willReturn('Opt');
|
||||
|
||||
$this->optionsRepo->method('findAll')->willReturn([$option]);
|
||||
|
||||
// Contrats (Note: command uses findAll on Contrats::class but variable named $options)
|
||||
$contrat = $this->createMock(Contrats::class);
|
||||
$contrat->method('getId')->willReturn(40);
|
||||
$contrat->method('getNumReservation')->willReturn('RES-100');
|
||||
|
||||
$this->contratsRepo->method('findAll')->willReturn([$contrat]);
|
||||
|
||||
// 2. Expectations
|
||||
$this->client->expects($this->once())->method('init');
|
||||
|
||||
$capturedArgs = [];
|
||||
$this->client->expects($this->exactly(5))
|
||||
->method('indexDocuments')
|
||||
->willReturnCallback(function($data, $index) use (&$capturedArgs) {
|
||||
$capturedArgs[] = [$data, $index];
|
||||
return true;
|
||||
});
|
||||
|
||||
// 3. Execute
|
||||
$command = new SearchCommand($this->entityManager, $this->client);
|
||||
$application = new Application();
|
||||
$application->add($command);
|
||||
$commandTester = new CommandTester($application->find('app:search'));
|
||||
|
||||
$commandTester->execute([]);
|
||||
|
||||
$this->assertStringContainsString('Indexation terminée', $commandTester->getDisplay());
|
||||
|
||||
// Check captured args
|
||||
$this->assertCount(5, $capturedArgs);
|
||||
|
||||
// Admin
|
||||
$this->assertEquals(['id' => 1, 'name' => 'Admin', 'surname' => 'User', 'email' => 'admin@test.com'], $capturedArgs[0][0]);
|
||||
$this->assertEquals('admin', $capturedArgs[0][1]);
|
||||
|
||||
// Customer
|
||||
$this->assertEquals(['id' => 10, 'name' => 'Cust', 'surname' => 'Omer', 'siret' => '123', 'civ' => 'Mr', 'type' => 'pro', 'phone' => '0102030405', 'email' => 'cust@test.com'], $capturedArgs[1][0]);
|
||||
$this->assertEquals('customer', $capturedArgs[1][1]);
|
||||
|
||||
// Product
|
||||
$this->assertEquals(['id' => 20, 'name' => 'Prod', 'ref' => 'REF001'], $capturedArgs[2][0]);
|
||||
$this->assertEquals('product', $capturedArgs[2][1]);
|
||||
|
||||
// Options
|
||||
$this->assertEquals(['id' => 30, 'name' => 'Opt'], $capturedArgs[3][0]);
|
||||
$this->assertEquals('options', $capturedArgs[3][1]);
|
||||
|
||||
// Contrat
|
||||
$this->assertEquals(['id' => 40, 'num' => 'RES-100'], $capturedArgs[4][0]);
|
||||
$this->assertEquals('contrat', $capturedArgs[4][1]);
|
||||
}
|
||||
}
|
||||
75
tests/Command/SitemapCommandTest.php
Normal file
75
tests/Command/SitemapCommandTest.php
Normal file
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
namespace App\Tests\Command;
|
||||
|
||||
use App\Command\SitemapCommand;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Presta\SitemapBundle\Service\DumperInterface;
|
||||
use Symfony\Component\Console\Application;
|
||||
use Symfony\Component\Console\Tester\CommandTester;
|
||||
use Symfony\Component\HttpKernel\KernelInterface;
|
||||
|
||||
class SitemapCommandTest extends TestCase
|
||||
{
|
||||
private MockObject&KernelInterface $kernel;
|
||||
private MockObject&DumperInterface $dumper;
|
||||
private string $tempDir;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->kernel = $this->createMock(KernelInterface::class);
|
||||
$this->dumper = $this->createMock(DumperInterface::class);
|
||||
|
||||
$this->tempDir = sys_get_temp_dir() . '/sitemap_test_' . uniqid();
|
||||
mkdir($this->tempDir . '/public/seo', 0777, true);
|
||||
$_ENV['DEFAULT_URI'] = 'https://test.com';
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
$this->removeDirectory($this->tempDir);
|
||||
}
|
||||
|
||||
public function testExecute()
|
||||
{
|
||||
// 1. Setup Files (Old sitemap to delete)
|
||||
touch($this->tempDir . '/public/seo/old.xml');
|
||||
|
||||
$this->kernel->method('getProjectDir')->willReturn($this->tempDir);
|
||||
|
||||
// 2. Expect Dumper call
|
||||
$this->dumper->expects($this->once())
|
||||
->method('dump')
|
||||
->with(
|
||||
$this->stringEndsWith('/public/seo'),
|
||||
'https://test.com/seo',
|
||||
'',
|
||||
[]
|
||||
);
|
||||
|
||||
// 3. Execute
|
||||
$command = new SitemapCommand($this->kernel, $this->dumper);
|
||||
$application = new Application();
|
||||
$application->add($command);
|
||||
$commandTester = new CommandTester($application->find('app:sitemap'));
|
||||
|
||||
$commandTester->execute([]);
|
||||
|
||||
// 4. Assertions
|
||||
$output = $commandTester->getDisplay();
|
||||
$this->assertStringContainsString('Anciens fichiers sitemap supprimés', $output);
|
||||
$this->assertStringContainsString('Sitemap généré avec succès', $output);
|
||||
|
||||
$this->assertFileDoesNotExist($this->tempDir . '/public/seo/old.xml');
|
||||
}
|
||||
|
||||
private function removeDirectory($dir) {
|
||||
if (!is_dir($dir)) return;
|
||||
$files = array_diff(scandir($dir), array('.','..'));
|
||||
foreach ($files as $file) {
|
||||
(is_dir("$dir/$file")) ? $this->removeDirectory("$dir/$file") : unlink("$dir/$file");
|
||||
}
|
||||
rmdir($dir);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user