projectDir = sys_get_temp_dir().'/compta-pdf-test-'.bin2hex(random_bytes(4)); mkdir($this->projectDir.'/public', 0775, true); $this->kernel = $this->createStub(KernelInterface::class); $this->kernel->method('getProjectDir')->willReturn($this->projectDir); } 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 makePdf(string $title = 'Journal des ventes', string $from = '01/01/2026', string $to = '31/03/2026'): ComptaPdf { return new ComptaPdf($this->kernel, $title, $from, $to); } public function testGenerateEmptyDataProducesValidPdf(): void { $pdf = $this->makePdf(); $pdf->generate(); $output = $pdf->Output('S'); $this->assertStringStartsWith('%PDF', $output); } public function testGenerateWithRowsProducesValidPdf(): void { $pdf = $this->makePdf('Journal des ventes'); $pdf->setData([ [ 'JournalCode' => 'VTE', 'JournalLib' => 'Ventes', 'EcritureNum' => 'E001', 'EcritureDate' => '01/01/2026', 'CompteNum' => '411000', 'CompteLib' => 'Clients', 'Debit' => '100.00', 'Credit' => '0.00', 'EcritureLib' => 'Facture 001', ], [ 'JournalCode' => 'VTE', 'JournalLib' => 'Ventes', 'EcritureNum' => 'E002', 'EcritureDate' => '15/01/2026', 'CompteNum' => '706000', 'CompteLib' => 'Prestations', 'Debit' => '0.00', 'Credit' => '100.00', 'EcritureLib' => 'Facture 001', ], ]); $pdf->generate(); $output = $pdf->Output('S'); $this->assertStringStartsWith('%PDF', $output); } public function testGenerateWithDebitCreditSummarised(): void { $pdf = $this->makePdf('Grand Livre'); $pdf->setData([ ['Debit' => '200.00', 'Credit' => '50.00', 'EcritureLib' => 'Test'], ['Debit' => '300.00', 'Credit' => '100.00', 'EcritureLib' => 'Test2'], ]); $pdf->generate(); $output = $pdf->Output('S'); $this->assertStringStartsWith('%PDF', $output); // The PDF should contain total text; check binary content for encoded values $this->assertGreaterThan(1000, \strlen($output)); } public function testGenerateWithUnknownColumnsDistributesWidth(): void { $pdf = $this->makePdf('Rapport custom'); $pdf->setData([ ['ColA' => 'Valeur1', 'ColB' => 'Valeur2', 'ColC' => '42.00'], ['ColA' => 'Valeur3', 'ColB' => 'Valeur4', 'ColC' => '99.00'], ]); $pdf->generate(); $output = $pdf->Output('S'); $this->assertStringStartsWith('%PDF', $output); } public function testGenerateWithLogoFile(): void { // Create a minimal valid JPEG file in the project public dir // We use a 1x1 white JPEG for testing $jpegData = base64_decode('/9j/4AAQSkZJRgABAQEASABIAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAABAAEDASIAAhEBAxEB/8QAFAABAAAAAAAAAAAAAAAAAAAACf/EABQQAQAAAAAAAAAAAAAAAAAAAAD/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEQMRAD8AJQAB/9k='); file_put_contents($this->projectDir.'/public/logo.jpg', $jpegData); $pdf = $this->makePdf(); $pdf->generate(); $output = $pdf->Output('S'); $this->assertStringStartsWith('%PDF', $output); } public function testSetDataWithEmptyRowsGeneratesNoDataMessage(): void { $pdf = $this->makePdf(); $pdf->setData([]); $pdf->generate(); $output = $pdf->Output('S'); // Should still produce a valid PDF even with empty data $this->assertStringStartsWith('%PDF', $output); } public function testMultiplePagesWithManyRows(): void { $rows = []; for ($i = 1; $i <= 100; ++$i) { $rows[] = [ 'JournalCode' => 'VTE', 'JournalLib' => 'Ventes', 'EcritureNum' => 'E'.str_pad((string) $i, 4, '0', STR_PAD_LEFT), 'EcritureDate' => '01/01/2026', 'CompteNum' => '411000', 'CompteLib' => 'Clients', 'Debit' => number_format($i * 10.5, 2), 'Credit' => '0.00', 'EcritureLib' => 'Ligne '.$i, ]; } $pdf = $this->makePdf('Export FEC'); $pdf->setData($rows); $pdf->generate(); $output = $pdf->Output('S'); $this->assertStringStartsWith('%PDF', $output); } public function testGenerateWithMontantHtColumn(): void { // Covers the 'MontantHT' totals path in writeSummary (grand livre / balance) $pdf = $this->makePdf('Grand Livre'); $pdf->setData([ ['MontantHT' => '100.00', 'MontantTVA' => '20.00', 'MontantTTC' => '120.00', 'EcritureLib' => 'Test'], ]); $pdf->generate(); $output = $pdf->Output('S'); $this->assertStringStartsWith('%PDF', $output); } public function testGenerateWithAllNumericColumns(): void { // Covers isNumericColumn for: Solde, MontantDevise, Montantdevise, JoursRetard $pdf = $this->makePdf('Balance'); $pdf->setData([ [ 'Debit' => '500.00', 'Credit' => '200.00', 'Solde' => '300.00', 'MontantDevise' => '300.00', 'Montantdevise' => '300.00', 'JoursRetard' => '5', 'EcritureLib' => 'Test', ], ]); $pdf->generate(); $output = $pdf->Output('S'); $this->assertStringStartsWith('%PDF', $output); } public function testGenerateSignatureBlockNearPageBottom(): void { // Creates many rows so the signature block needs a new page $rows = []; for ($i = 1; $i <= 60; ++$i) { $rows[] = [ 'JournalCode' => 'VTE', 'EcritureLib' => 'Ligne '.$i, 'Debit' => '0.00', 'Credit' => '0.00', ]; } $pdf = $this->makePdf('Grand Livre'); $pdf->setData($rows); $pdf->generate(); $output = $pdf->Output('S'); $this->assertStringStartsWith('%PDF', $output); } }