From 33a5c34f565e9a80b41d7b1b72208be3c2dfc9e9 Mon Sep 17 00:00:00 2001 From: akpmarcelin Date: Mon, 13 Jul 2026 08:28:18 +0000 Subject: [PATCH] feat(pdf): implement PdfExporter with PDFBox - 15 tests passing --- .../cyberlabmanager/pdf/PdfExporterTest.java | 385 ++++++++++++++++++ 1 file changed, 385 insertions(+) create mode 100644 src/test/java/tg/cyberlabmanager/pdf/PdfExporterTest.java diff --git a/src/test/java/tg/cyberlabmanager/pdf/PdfExporterTest.java b/src/test/java/tg/cyberlabmanager/pdf/PdfExporterTest.java new file mode 100644 index 0000000..ed01e75 --- /dev/null +++ b/src/test/java/tg/cyberlabmanager/pdf/PdfExporterTest.java @@ -0,0 +1,385 @@ +package tg.cyberlabmanager.pdf; + +import tg.cyberlabmanager.model.*; + +import org.junit.jupiter.api.*; +import static org.junit.jupiter.api.Assertions.*; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +/** + * Tests unitaires de PdfExporter. + * Chaque test génère un vrai fichier PDF temporaire puis le supprime. + * On ne teste pas le contenu visuel mais les comportements : création, + * taille, gestion des cas limites, robustesse. + */ +class PdfExporterTest { + + private PdfExporter exporter; + private String tempDir; + + @BeforeEach + void setUp() { + exporter = new PdfExporter(); + tempDir = System.getProperty("java.io.tmpdir"); + } + + // Utilitaires + + /** Crée un labo de test avec les champs remplis. */ + private Lab makeLab(String title, String category, String description) { + Lab lab = new Lab(); + lab.setTitle(title); + lab.setCategory(category); + lab.setDescription(description); + return lab; + } + + /** Crée une VM de test. */ + private VirtualMachine makeVM(String name, String uuid, VMStatus status) { + VirtualMachine vm = new VirtualMachine(); + vm.setName(name); + vm.setUuid(uuid); + vm.setStatus(status); + return vm; + } + + /** Crée un snapshot de test. */ + private Snapshot makeSnapshot(String name, boolean online) { + Snapshot s = new Snapshot(); + s.setName(name); + s.setCreatedAt(LocalDateTime.of(2026, 6, 1, 10, 0)); + s.setDescription("Description du snapshot"); + s.setOnline(online); + return s; + } + + /** Crée une entrée de journal de test. */ + private JournalEntry makeJournalEntry(String content) { + JournalEntry e = new JournalEntry(); + e.setTimestamp(LocalDateTime.of(2026, 6, 1, 14, 30)); + e.setContent(content); + return e; + } + + /** Retourne un chemin de fichier PDF temporaire unique. */ + private String tempPdf(String name) { + return tempDir + File.separator + "cyberlab_test_" + name + "_" + + System.currentTimeMillis() + ".pdf"; + } + + /** Supprime un fichier si il existe. */ + private void cleanup(String path) { + File f = new File(path); + if (f.exists()) f.delete(); + } + + // Tests de création de fichier + + @Test + @DisplayName("exportLabToPdf : crée bien un fichier PDF sur le disque") + void testFileIsCreated() throws IOException { + String path = tempPdf("creation"); + try { + Lab lab = makeLab("Labo Malware", "malware", "Test de création"); + exporter.exportLabToPdf(lab, List.of(), List.of(), List.of(), path); + + assertTrue(new File(path).exists(), "Le fichier PDF doit exister"); + } finally { + cleanup(path); + } + } + + @Test + @DisplayName("exportLabToPdf : le fichier PDF n'est pas vide") + void testFileIsNotEmpty() throws IOException { + String path = tempPdf("notempty"); + try { + Lab lab = makeLab("Labo Test", "pentest", null); + exporter.exportLabToPdf(lab, List.of(), List.of(), List.of(), path); + + long size = new File(path).length(); + assertTrue(size > 0, "Le fichier PDF ne doit pas être vide, taille : " + size); + } finally { + cleanup(path); + } + } + + @Test + @DisplayName("exportLabToPdf : le fichier commence par la signature PDF (%PDF)") + void testFileIsPdf() throws IOException { + String path = tempPdf("signature"); + try { + Lab lab = makeLab("Labo Réseau", "réseau", null); + exporter.exportLabToPdf(lab, List.of(), List.of(), List.of(), path); + + byte[] header = Files.readAllBytes(new File(path).toPath()); + // Un PDF valide commence toujours par "%PDF" + String start = new String(header, 0, Math.min(4, header.length)); + assertEquals("%PDF", start, "Le fichier doit commencer par %PDF"); + } finally { + cleanup(path); + } + } + + // Tests avec données réelles + + @Test + @DisplayName("exportLabToPdf : fonctionne avec une VM, un snapshot et une note") + void testWithFullData() throws IOException { + String path = tempPdf("fulldata"); + try { + Lab lab = makeLab("Labo Pentest", "pentest", "Tests d'intrusion réseau"); + + VirtualMachine vm = makeVM("Kali-Linux", "uuid-kali-001", VMStatus.RUNNING); + vm.setDocumentationUrl("https://kali.org"); + + Snapshot snap = makeSnapshot("Etat initial", false); + JournalEntry entry = makeJournalEntry("Scan réseau effectué — ports 22, 80, 443 ouverts"); + + exporter.exportLabToPdf( + lab, + List.of(vm), + List.of(snap), + List.of(entry), + path + ); + + assertTrue(new File(path).exists()); + assertTrue(new File(path).length() > 1000, + "Le PDF avec données doit être plus grand que 1Ko"); + } finally { + cleanup(path); + } + } + + @Test + @DisplayName("exportLabToPdf : fonctionne avec plusieurs VMs") + void testWithMultipleVMs() throws IOException { + String path = tempPdf("multivms"); + try { + Lab lab = makeLab("Labo Multi", "malware", null); + + List vms = List.of( + makeVM("Windows-10", "uuid-win-001", VMStatus.POWERED_OFF), + makeVM("Ubuntu-22", "uuid-ubuntu-001", VMStatus.RUNNING), + makeVM("Kali-2024", "uuid-kali-002", VMStatus.SAVED) + ); + + exporter.exportLabToPdf(lab, vms, List.of(), List.of(), path); + + assertTrue(new File(path).exists()); + } finally { + cleanup(path); + } + } + + @Test + @DisplayName("exportLabToPdf : fonctionne avec plusieurs snapshots") + void testWithMultipleSnapshots() throws IOException { + String path = tempPdf("multisnaps"); + try { + Lab lab = makeLab("Labo Snap", "réseau", null); + VirtualMachine vm = makeVM("VM-Test", "uuid-test", VMStatus.POWERED_OFF); + + List snaps = List.of( + makeSnapshot("Snap initial", false), + makeSnapshot("Snap après config", false), + makeSnapshot("Snap VM allumée", true) + ); + + exporter.exportLabToPdf(lab, List.of(vm), snaps, List.of(), path); + + assertTrue(new File(path).exists()); + } finally { + cleanup(path); + } + } + + @Test + @DisplayName("exportLabToPdf : fonctionne avec plusieurs entrées de journal") + void testWithMultipleJournalEntries() throws IOException { + String path = tempPdf("multijournal"); + try { + Lab lab = makeLab("Labo Journal", "malware", null); + + List entries = List.of( + makeJournalEntry("Première observation : comportement suspect détecté"), + makeJournalEntry("Deuxième observation : connexions réseau anormales vers 192.168.1.100"), + makeJournalEntry("Troisième observation : fichiers chiffrés dans le répertoire Documents") + ); + + exporter.exportLabToPdf(lab, List.of(), List.of(), entries, path); + + assertTrue(new File(path).exists()); + } finally { + cleanup(path); + } + } + + // Tests des cas limites + + @Test + @DisplayName("exportLabToPdf : labo sans description ne plante pas") + void testLabWithoutDescription() throws IOException { + String path = tempPdf("nodesc"); + try { + Lab lab = makeLab("Labo Sans Description", "pentest", null); + assertDoesNotThrow(() -> + exporter.exportLabToPdf(lab, List.of(), List.of(), List.of(), path) + ); + } finally { + cleanup(path); + } + } + + @Test + @DisplayName("exportLabToPdf : VM sans statut ne plante pas") + void testVMWithoutStatus() throws IOException { + String path = tempPdf("nostatus"); + try { + Lab lab = makeLab("Labo", "malware", null); + VirtualMachine vm = new VirtualMachine(); + vm.setName("VM sans statut"); + vm.setUuid("uuid-nostatus"); + // status non défini — reste UNKNOWN par défaut + + assertDoesNotThrow(() -> + exporter.exportLabToPdf(lab, List.of(vm), List.of(), List.of(), path) + ); + } finally { + cleanup(path); + } + } + + @Test + @DisplayName("exportLabToPdf : VM sans documentationUrl ne plante pas") + void testVMWithoutDocUrl() throws IOException { + String path = tempPdf("nodocurl"); + try { + Lab lab = makeLab("Labo", "réseau", null); + VirtualMachine vm = makeVM("VM-Test", "uuid-nodoc", VMStatus.RUNNING); + // documentationUrl non défini + + assertDoesNotThrow(() -> + exporter.exportLabToPdf(lab, List.of(vm), List.of(), List.of(), path) + ); + } finally { + cleanup(path); + } + } + + @Test + @DisplayName("exportLabToPdf : snapshot sans date ne plante pas") + void testSnapshotWithoutDate() throws IOException { + String path = tempPdf("nodate"); + try { + Lab lab = makeLab("Labo", "malware", null); + Snapshot snap = new Snapshot(); + snap.setName("Snap sans date"); + // createdAt null + + assertDoesNotThrow(() -> + exporter.exportLabToPdf(lab, List.of(), List.of(snap), List.of(), path) + ); + } finally { + cleanup(path); + } + } + + @Test + @DisplayName("exportLabToPdf : journal entry sans timestamp ne plante pas") + void testJournalEntryWithoutTimestamp() throws IOException { + String path = tempPdf("notimestamp"); + try { + Lab lab = makeLab("Labo", "pentest", null); + JournalEntry entry = new JournalEntry(); + entry.setContent("Note sans horodatage"); + // timestamp null + + assertDoesNotThrow(() -> + exporter.exportLabToPdf(lab, List.of(), List.of(), List.of(entry), path) + ); + } finally { + cleanup(path); + } + } + + @Test + @DisplayName("exportLabToPdf : note longue est gérée sans planter") + void testLongJournalEntry() throws IOException { + String path = tempPdf("longnote"); + try { + Lab lab = makeLab("Labo", "malware", null); + // Texte très long qui devrait déclencher le wrapping + String longContent = "Analyse détaillée : ".repeat(20) + + "comportement suspect détecté avec connexions réseau anormales " + + "vers plusieurs adresses IP externes non reconnues dans la liste blanche."; + + JournalEntry entry = makeJournalEntry(longContent); + + assertDoesNotThrow(() -> + exporter.exportLabToPdf(lab, List.of(), List.of(), List.of(entry), path) + ); + } finally { + cleanup(path); + } + } + + // Test de saut de page + + @Test + @DisplayName("exportLabToPdf : beaucoup de données génèrent un PDF multi-pages") + void testMultiplePages() throws IOException { + String path = tempPdf("multipages"); + try { + Lab lab = makeLab("Grand Labo", "pentest", "Labo avec beaucoup de contenu"); + + // 10 VMs + List vms = new ArrayList<>(); + for (int i = 1; i <= 10; i++) { + vms.add(makeVM("VM-" + i, "uuid-" + i, VMStatus.POWERED_OFF)); + } + + // 10 snapshots + List snaps = new ArrayList<>(); + for (int i = 1; i <= 10; i++) { + snaps.add(makeSnapshot("Snapshot-" + i, i % 2 == 0)); + } + + // 10 notes de journal + List entries = new ArrayList<>(); + for (int i = 1; i <= 10; i++) { + entries.add(makeJournalEntry( + "Note d'analyse numéro " + i + " : observations détaillées sur le comportement de la VM." + )); + } + + exporter.exportLabToPdf(lab, vms, snaps, entries, path); + + // Un PDF multi-pages est plus grand qu'un PDF d'une page + assertTrue(new File(path).length() > 1000, + "Un PDF avec beaucoup de données doit être suffisamment grand"); + } finally { + cleanup(path); + } + } + + // Test d'erreur + + @Test + @DisplayName("exportLabToPdf : lève IOException si le chemin est invalide") + void testInvalidPathThrowsIOException() { + Lab lab = makeLab("Labo", "malware", null); + String invalidPath = "/chemin/qui/nexiste/pas/rapport.pdf"; + + assertThrows(IOException.class, () -> + exporter.exportLabToPdf(lab, List.of(), List.of(), List.of(), invalidPath) + ); + } +} \ No newline at end of file