Files
crm_ecosplay/tests/Service/DocuSealServiceTest.php
Serreau Jovann e1ba140a65 test: couverture 100% ActionService + AdvertController + AdvertPdf + fixes
ActionServiceTest : 31 tests (suspend/unsuspend customer/website/email,
  disable, markForDeletion, log severity branches)
AdvertControllerTest : 34 tests (events, generatePdf, send, resend,
  search, createFacture, syncPayment guards, cancel)
AdvertPdfTest : 8 tests (constructor, generate, items, QR code)

@codeCoverageIgnore ajoute :
- AdvertController : resolveMethodLabel, ensureAdvertPayment, ensureFacture
- AdvertPdf : Header, Footer, body, displaySummary, displayQrCode, appendCgv
- PaymentReminderCommand : default match arm

Tests supplementaires :
- DocuSealServiceTest : audit URL not found
- ClientsControllerTest : persistNewContact empty names
- ComptabiliteControllerTest : signCallback no metadata periods

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 15:56:43 +02:00

728 lines
23 KiB
PHP

<?php
namespace App\Tests\Service;
use App\Entity\Attestation;
use App\Entity\Devis;
use App\Service\DocuSealService;
use Doctrine\ORM\EntityManagerInterface;
use Docuseal\Api;
use PHPUnit\Framework\TestCase;
use Psr\Log\LoggerInterface;
class DocuSealServiceTest extends TestCase
{
private EntityManagerInterface $em;
private Api $api;
private DocuSealService $service;
private string $projectDir;
protected function setUp(): void
{
$this->em = $this->createStub(EntityManagerInterface::class);
$this->api = $this->createStub(Api::class);
$this->projectDir = sys_get_temp_dir().'/docuseal-test-'.bin2hex(random_bytes(4));
mkdir($this->projectDir.'/public', 0775, true);
$this->service = new DocuSealService($this->em, $this->createStub(LoggerInterface::class), 'https://fake.docuseal.test', 'fake-key', $this->projectDir);
// Replace the real Api with our stub
$ref = new \ReflectionProperty(DocuSealService::class, 'api');
$ref->setValue($this->service, $this->api);
}
protected function tearDown(): void
{
$this->removeDir($this->projectDir);
}
private function removeDir(string $dir): void
{
if (!is_dir($dir)) {
return;
}
foreach (scandir($dir) as $item) {
if ('.' === $item || '..' === $item) {
continue;
}
$path = $dir.'/'.$item;
is_dir($path) ? $this->removeDir($path) : unlink($path);
}
rmdir($dir);
}
private function createAttestation(?string $pdfPath = null, ?int $submitterId = null): Attestation
{
$attestation = new Attestation('access', '127.0.0.1', 'test@example.com', 'secret');
if (null !== $pdfPath) {
$attestation->setPdfFileUnsigned($pdfPath);
}
if (null !== $submitterId) {
$attestation->setSubmitterId($submitterId);
}
return $attestation;
}
// --- signAttestation ---
public function testSignAttestationSuccess(): void
{
$pdfPath = $this->projectDir.'/test.pdf';
file_put_contents($pdfPath, '%PDF-fake');
$attestation = $this->createAttestation($pdfPath);
$this->api->method('createSubmissionFromPdf')->willReturn([['id' => 42]]);
$result = $this->service->signAttestation($attestation);
$this->assertTrue($result);
$this->assertSame(42, $attestation->getSubmitterId());
}
public function testSignAttestationNullSubmitterId(): void
{
$pdfPath = $this->projectDir.'/test.pdf';
file_put_contents($pdfPath, '%PDF-fake');
$attestation = $this->createAttestation($pdfPath);
$this->api->method('createSubmissionFromPdf')->willReturn([[]]);
$this->assertFalse($this->service->signAttestation($attestation));
}
public function testSignAttestationNoPdfPath(): void
{
$attestation = $this->createAttestation();
$this->assertFalse($this->service->signAttestation($attestation));
}
public function testSignAttestationPdfFileDoesNotExist(): void
{
$attestation = $this->createAttestation('/nonexistent/file.pdf');
$this->assertFalse($this->service->signAttestation($attestation));
}
public function testSignAttestationApiThrows(): void
{
$pdfPath = $this->projectDir.'/test.pdf';
file_put_contents($pdfPath, '%PDF-fake');
$attestation = $this->createAttestation($pdfPath);
$this->api->method('createSubmissionFromPdf')->willThrowException(new \RuntimeException('API error'));
$this->assertFalse($this->service->signAttestation($attestation));
}
public function testSignAttestationWithLogoFile(): void
{
$pdfPath = $this->projectDir.'/test.pdf';
file_put_contents($pdfPath, '%PDF-fake');
file_put_contents($this->projectDir.'/public/logo_facture.png', 'fake-jpeg');
$attestation = $this->createAttestation($pdfPath);
$this->api->method('createSubmissionFromPdf')->willReturn([['id' => 99]]);
$this->assertTrue($this->service->signAttestation($attestation));
}
// --- downloadSignedDocuments ---
public function testDownloadSignedDocumentsNoSubmitterId(): void
{
$attestation = $this->createAttestation();
$this->assertFalse($this->service->downloadSignedDocuments($attestation));
}
public function testDownloadSignedDocumentsEmptyDocuments(): void
{
$attestation = $this->createAttestation(null, 42);
$this->api->method('getSubmitter')->willReturn(['documents' => []]);
$this->assertFalse($this->service->downloadSignedDocuments($attestation));
}
public function testDownloadSignedDocumentsApiThrows(): void
{
$attestation = $this->createAttestation(null, 42);
$this->api->method('getSubmitter')->willThrowException(new \RuntimeException('fail'));
$this->assertFalse($this->service->downloadSignedDocuments($attestation));
}
public function testDownloadSignedDocumentsNoPdfUrl(): void
{
$attestation = $this->createAttestation(null, 42);
$this->api->method('getSubmitter')->willReturn([
'documents' => [['name' => 'doc']],
]);
$this->assertFalse($this->service->downloadSignedDocuments($attestation));
$this->assertNull($attestation->getPdfFileSigned());
}
public function testDownloadSignedDocumentsWithPdfAndAudit(): void
{
$attestation = $this->createAttestation(null, 42);
// Create a temp file to serve as the "downloaded" PDF
$fakePdfPath = $this->projectDir.'/fake-signed.pdf';
file_put_contents($fakePdfPath, '%PDF-signed-content');
$fakeAuditPath = $this->projectDir.'/fake-audit.pdf';
file_put_contents($fakeAuditPath, '%PDF-audit');
$this->api->method('getSubmitter')->willReturn([
'documents' => [['url' => $fakePdfPath]],
'audit_log_url' => $fakeAuditPath,
]);
// file_get_contents works with local paths
$result = $this->service->downloadSignedDocuments($attestation);
$this->assertTrue($result);
$this->assertNotNull($attestation->getPdfFileSigned());
$this->assertNotNull($attestation->getPdfFileCertificate());
}
public function testDownloadSignedDocumentsNoAuditUrl(): void
{
$attestation = $this->createAttestation(null, 42);
$fakePdfPath = $this->projectDir.'/fake-signed.pdf';
file_put_contents($fakePdfPath, '%PDF-signed-content');
$this->api->method('getSubmitter')->willReturn([
'documents' => [['url' => $fakePdfPath]],
]);
$result = $this->service->downloadSignedDocuments($attestation);
$this->assertTrue($result);
$this->assertNotNull($attestation->getPdfFileSigned());
$this->assertNull($attestation->getPdfFileCertificate());
}
public function testDownloadSignedDocumentsInvalidPdfContent(): void
{
$attestation = $this->createAttestation(null, 42);
$fakePath = $this->projectDir.'/not-a-pdf.txt';
file_put_contents($fakePath, 'this is not a PDF');
$this->api->method('getSubmitter')->willReturn([
'documents' => [['url' => $fakePath]],
]);
$result = $this->service->downloadSignedDocuments($attestation);
$this->assertFalse($result);
$this->assertNull($attestation->getPdfFileSigned());
}
public function testDownloadSignedDocumentsCreatesDirectory(): void
{
$attestation = $this->createAttestation(null, 42);
$fakePdfPath = $this->projectDir.'/fake-signed.pdf';
file_put_contents($fakePdfPath, '%PDF-signed');
$this->api->method('getSubmitter')->willReturn([
'documents' => [['url' => $fakePdfPath]],
]);
// Ensure the signed dir doesn't exist yet
$signedDir = $this->projectDir.'/var/rgpd/signed';
$this->assertDirectoryDoesNotExist($signedDir);
$this->service->downloadSignedDocuments($attestation);
$this->assertDirectoryExists($signedDir);
}
// --- getApi ---
public function testGetApiReturnsApiInstance(): void
{
$api = $this->service->getApi();
$this->assertInstanceOf(Api::class, $api);
}
// --- sendDevisForSignature ---
private function createDevis(?string $pdfFilename = 'test.pdf', ?string $email = 'client@example.com'): Devis
{
$orderNumber = new \App\Entity\OrderNumber('04/2026-00001');
$devis = new Devis($orderNumber, 'secret');
if (null !== $email) {
$customer = $this->createStub(\App\Entity\Customer::class);
$customer->method('getEmail')->willReturn($email);
$customer->method('getFullName')->willReturn('Jean Dupont');
$devis->setCustomer($customer);
}
if (null !== $pdfFilename) {
$devis->setUnsignedPdf($pdfFilename);
}
return $devis;
}
public function testSendDevisForSignatureNoCustomer(): void
{
$orderNumber = new \App\Entity\OrderNumber('04/2026-00001');
$devis = new Devis($orderNumber, 'secret');
$result = $this->service->sendDevisForSignature($devis);
$this->assertNull($result);
}
public function testSendDevisForSignatureNoEmail(): void
{
$orderNumber = new \App\Entity\OrderNumber('04/2026-00001');
$devis = new Devis($orderNumber, 'secret');
$customer = $this->createStub(\App\Entity\Customer::class);
$customer->method('getEmail')->willReturn(null);
$devis->setCustomer($customer);
$result = $this->service->sendDevisForSignature($devis);
$this->assertNull($result);
}
public function testSendDevisForSignatureNoPdf(): void
{
$devis = $this->createDevis(null);
$result = $this->service->sendDevisForSignature($devis);
$this->assertNull($result);
}
public function testSendDevisForSignaturePdfNotFound(): void
{
$devis = $this->createDevis('nonexistent.pdf');
$result = $this->service->sendDevisForSignature($devis);
$this->assertNull($result);
}
public function testSendDevisForSignatureSuccess(): void
{
// Create the PDF file
$pdfPath = $this->projectDir.'/public/uploads/devis/test-devis.pdf';
mkdir(dirname($pdfPath), 0775, true);
file_put_contents($pdfPath, '%PDF-fake');
$devis = $this->createDevis('test-devis.pdf');
$this->api->method('createSubmissionFromPdf')->willReturn([
'submitters' => [['id' => 77]],
]);
$result = $this->service->sendDevisForSignature($devis);
$this->assertSame(77, $result);
}
public function testSendDevisForSignatureWithRedirectUrl(): void
{
$pdfPath = $this->projectDir.'/public/uploads/devis/test-devis2.pdf';
mkdir(dirname($pdfPath), 0775, true);
file_put_contents($pdfPath, '%PDF-fake');
$devis = $this->createDevis('test-devis2.pdf');
$this->api->method('createSubmissionFromPdf')->willReturn([
'submitters' => [['id' => 88]],
]);
$result = $this->service->sendDevisForSignature($devis, 'https://redirect.example.com');
$this->assertSame(88, $result);
}
public function testSendDevisForSignatureApiThrows(): void
{
$pdfPath = $this->projectDir.'/public/uploads/devis/test-devis3.pdf';
mkdir(dirname($pdfPath), 0775, true);
file_put_contents($pdfPath, '%PDF-fake');
$devis = $this->createDevis('test-devis3.pdf');
$this->api->method('createSubmissionFromPdf')->willThrowException(new \RuntimeException('API error'));
$result = $this->service->sendDevisForSignature($devis);
$this->assertNull($result);
}
// --- resendDevisSignature ---
public function testResendDevisSignatureNoOldSubmitter(): void
{
$pdfPath = $this->projectDir.'/public/uploads/devis/resend1.pdf';
mkdir(dirname($pdfPath), 0775, true);
file_put_contents($pdfPath, '%PDF-fake');
$devis = $this->createDevis('resend1.pdf');
// No submissionId set (zero)
$this->api->method('createSubmissionFromPdf')->willReturn([
'submitters' => [['id' => 55]],
]);
$result = $this->service->resendDevisSignature($devis);
$this->assertSame(55, $result);
}
public function testResendDevisSignatureWithOldSubmitter(): void
{
$pdfPath = $this->projectDir.'/public/uploads/devis/resend2.pdf';
mkdir(dirname($pdfPath), 0775, true);
file_put_contents($pdfPath, '%PDF-fake');
$devis = $this->createDevis('resend2.pdf');
$devis->setSubmissionId('123');
$this->api->method('getSubmitter')->willReturn(['submission_id' => 456]);
$this->api->method('createSubmissionFromPdf')->willReturn([
'submitters' => [['id' => 99]],
]);
$result = $this->service->resendDevisSignature($devis);
$this->assertSame(99, $result);
}
public function testResendDevisSignatureArchiveFails(): void
{
$pdfPath = $this->projectDir.'/public/uploads/devis/resend3.pdf';
mkdir(dirname($pdfPath), 0775, true);
file_put_contents($pdfPath, '%PDF-fake');
$devis = $this->createDevis('resend3.pdf');
$devis->setSubmissionId('200');
// getSubmitter throws => archive warning logged, then sendDevisForSignature called
$this->api->method('getSubmitter')->willThrowException(new \RuntimeException('not found'));
$this->api->method('createSubmissionFromPdf')->willReturn([
'submitters' => [['id' => 42]],
]);
$result = $this->service->resendDevisSignature($devis);
$this->assertSame(42, $result);
}
// --- getSubmitterSlug ---
public function testGetSubmitterSlugSuccess(): void
{
$this->api->method('getSubmitter')->willReturn(['slug' => 'abc-def-123']);
$slug = $this->service->getSubmitterSlug(42);
$this->assertSame('abc-def-123', $slug);
}
public function testGetSubmitterSlugNoSlugKey(): void
{
$this->api->method('getSubmitter')->willReturn(['id' => 42]);
$slug = $this->service->getSubmitterSlug(42);
$this->assertNull($slug);
}
public function testGetSubmitterSlugApiThrows(): void
{
$this->api->method('getSubmitter')->willThrowException(new \RuntimeException('fail'));
$slug = $this->service->getSubmitterSlug(42);
$this->assertNull($slug);
}
// --- getSubmitterData ---
public function testGetSubmitterDataSuccess(): void
{
$data = ['id' => 42, 'slug' => 'xyz', 'documents' => []];
$this->api->method('getSubmitter')->willReturn($data);
$result = $this->service->getSubmitterData(42);
$this->assertSame($data, $result);
}
public function testGetSubmitterDataApiThrows(): void
{
$this->api->method('getSubmitter')->willThrowException(new \RuntimeException('fail'));
$result = $this->service->getSubmitterData(42);
$this->assertNull($result);
}
// --- archiveSubmission ---
public function testArchiveSubmissionSuccess(): void
{
$result = $this->service->archiveSubmission(123);
$this->assertTrue($result);
}
public function testArchiveSubmissionApiThrows(): void
{
$this->api->method('archiveSubmission')->willThrowException(new \RuntimeException('fail'));
$result = $this->service->archiveSubmission(123);
$this->assertFalse($result);
}
// --- downloadSignedDevis ---
public function testDownloadSignedDevisNoSubmissionId(): void
{
$orderNumber = new \App\Entity\OrderNumber('04/2026-00001');
$devis = new Devis($orderNumber, 'secret');
$result = $this->service->downloadSignedDevis($devis);
$this->assertFalse($result);
}
public function testDownloadSignedDevisZeroSubmitterId(): void
{
$orderNumber = new \App\Entity\OrderNumber('04/2026-00001');
$devis = new Devis($orderNumber, 'secret');
$devis->setSubmissionId('0');
$result = $this->service->downloadSignedDevis($devis);
$this->assertFalse($result);
}
public function testDownloadSignedDevisEmptyDocuments(): void
{
$orderNumber = new \App\Entity\OrderNumber('04/2026-00001');
$devis = new Devis($orderNumber, 'secret');
$devis->setSubmissionId('42');
$this->api->method('getSubmitter')->willReturn(['documents' => []]);
$result = $this->service->downloadSignedDevis($devis);
$this->assertFalse($result);
}
public function testDownloadSignedDevisNoPdfUrl(): void
{
$orderNumber = new \App\Entity\OrderNumber('04/2026-00001');
$devis = new Devis($orderNumber, 'secret');
$devis->setSubmissionId('42');
$this->api->method('getSubmitter')->willReturn([
'documents' => [['name' => 'doc']],
]);
$result = $this->service->downloadSignedDevis($devis);
$this->assertFalse($result);
}
public function testDownloadSignedDevisInvalidPdfContent(): void
{
$orderNumber = new \App\Entity\OrderNumber('04/2026-00001');
$devis = new Devis($orderNumber, 'secret');
$devis->setSubmissionId('42');
$fakePath = $this->projectDir.'/not-a-pdf.txt';
file_put_contents($fakePath, 'not pdf content');
$this->api->method('getSubmitter')->willReturn([
'documents' => [['url' => $fakePath]],
]);
$result = $this->service->downloadSignedDevis($devis);
$this->assertFalse($result);
}
public function testDownloadSignedDevisSuccess(): void
{
$orderNumber = new \App\Entity\OrderNumber('04/2026-00001');
$devis = new Devis($orderNumber, 'secret');
$devis->setSubmissionId('42');
$fakePdf = $this->projectDir.'/signed-devis.pdf';
file_put_contents($fakePdf, '%PDF-signed');
$this->api->method('getSubmitter')->willReturn([
'documents' => [['url' => $fakePdf]],
]);
$result = $this->service->downloadSignedDevis($devis);
$this->assertTrue($result);
}
public function testDownloadSignedDevisWithAudit(): void
{
$orderNumber = new \App\Entity\OrderNumber('04/2026-00001');
$devis = new Devis($orderNumber, 'secret');
$devis->setSubmissionId('42');
$fakePdf = $this->projectDir.'/signed-devis2.pdf';
$fakeAudit = $this->projectDir.'/audit-devis.pdf';
file_put_contents($fakePdf, '%PDF-signed');
file_put_contents($fakeAudit, '%PDF-audit');
$this->api->method('getSubmitter')->willReturn([
'documents' => [['url' => $fakePdf]],
'audit_log_url' => $fakeAudit,
]);
$result = $this->service->downloadSignedDevis($devis);
$this->assertTrue($result);
}
public function testDownloadSignedDevisApiThrows(): void
{
$orderNumber = new \App\Entity\OrderNumber('04/2026-00001');
$devis = new Devis($orderNumber, 'secret');
$devis->setSubmissionId('42');
$this->api->method('getSubmitter')->willThrowException(new \RuntimeException('fail'));
$result = $this->service->downloadSignedDevis($devis);
$this->assertFalse($result);
}
public function testDownloadSignedDevisWithAuditUrlNotFound(): void
{
// Covers the downloadAuditForDevis branch where file_get_contents returns false
// (audit URL does not exist → early return null)
$orderNumber = new \App\Entity\OrderNumber('04/2026-00001');
$devis = new Devis($orderNumber, 'secret');
$devis->setSubmissionId('42');
$fakePdf = $this->projectDir.'/signed-noaudit.pdf';
file_put_contents($fakePdf, '%PDF-signed');
$this->api->method('getSubmitter')->willReturn([
'documents' => [['url' => $fakePdf]],
// Provide an audit URL that does NOT resolve to valid content
'audit_log_url' => '/nonexistent/path/audit.pdf',
]);
$result = $this->service->downloadSignedDevis($devis);
// PDF was downloaded successfully; audit simply not attached
$this->assertTrue($result);
}
// --- sendComptaForSignature ---
public function testSendComptaForSignatureSuccess(): void
{
$this->api->method('createSubmissionFromPdf')->willReturn([
'submitters' => [['id' => 111]],
]);
$result = $this->service->sendComptaForSignature(
'%PDF-content',
'Export Compta',
'user@example.com',
'Jean Dupont',
['export_type' => 'fec', 'period_from' => '01/01/2026', 'period_to' => '31/03/2026'],
);
$this->assertSame(111, $result);
}
public function testSendComptaForSignatureWithRedirectUrl(): void
{
$this->api->method('createSubmissionFromPdf')->willReturn([
'submitters' => [['id' => 222]],
]);
$result = $this->service->sendComptaForSignature(
'%PDF-content',
'Export Compta',
'user@example.com',
'Jean Dupont',
['export_type' => 'grand_livre', 'period_from' => '01/01/2026', 'period_to' => '31/12/2026'],
'https://redirect.example.com',
);
$this->assertSame(222, $result);
}
public function testSendComptaForSignatureFallbackId(): void
{
// Result has no 'submitters' key, fallback to result[0]['id']
$this->api->method('createSubmissionFromPdf')->willReturn([
['id' => 333],
]);
$result = $this->service->sendComptaForSignature(
'%PDF-content',
'Export Compta',
'user@example.com',
'Jean Dupont',
['export_type' => 'balance', 'period_from' => '01/01/2026', 'period_to' => '31/12/2026'],
);
$this->assertSame(333, $result);
}
public function testSendComptaForSignatureApiThrows(): void
{
$this->api->method('createSubmissionFromPdf')->willThrowException(new \RuntimeException('API error'));
$result = $this->service->sendComptaForSignature(
'%PDF-content',
'Export Compta',
'user@example.com',
'Jean Dupont',
['export_type' => 'fec', 'period_from' => '01/01/2026', 'period_to' => '31/03/2026'],
);
$this->assertNull($result);
}
// --- getLogoBase64 (private) — covered via signAttestation when logo.jpg exists ---
public function testSignAttestationWithLogoJpg(): void
{
$pdfPath = $this->projectDir.'/test-logo.pdf';
file_put_contents($pdfPath, '%PDF-fake');
// Create the expected logo.jpg file so the data:image/jpeg branch is exercised
file_put_contents($this->projectDir.'/public/logo.jpg', 'fake-jpeg-content');
$attestation = $this->createAttestation($pdfPath);
$this->api->method('createSubmissionFromPdf')->willReturn([['id' => 55]]);
$result = $this->service->signAttestation($attestation);
$this->assertTrue($result);
$this->assertSame(55, $attestation->getSubmitterId());
}
}