test: Add comprehensive unit tests for GetInfoOnPDF, covering metadata extraction, encryption, permissions, compliance, validation, and error handling scenarios (#4706)

# Description of Changes


Added 27 JUnit tests for getInfoOnPDF covering the following
functionality:

### Basic Functionality
- Valid PDF processing
- Basic info extraction (file size, page count, word count)
- Multi-page PDF handling

### Metadata Extraction
- All metadata fields (title, author, dates, etc.)
- Missing metadata handling

### Encryption & Permissions
- Encryption detection
- All permission types

### Form Fields
- Form field structure
- PDFs without forms

### Per-Page Information
- Page dimensions and standard sizes (A4, Letter, etc.)
- Page rotation

### Validation & Error Handling
- Null file rejection
- Empty file rejection
- File size limit (100MB) validation

### Static Helper Methods
- Page orientation detection (Landscape/Portrait/Square)
- Page size identification (parameterized tests)
- PDF/A standard checking
- Conformance level extraction

### Real PDF Files
- Testing with actual PDF files from resources

### Compliance Testing
- PDF/A, PDF/X, PDF/E, PDF/UA compliance detection

### Image Statistics
- Image statistics extraction



<!--
Please provide a summary of the changes, including:

- What was changed
- Why the change was made
- Any challenges encountered

Closes #(issue_number)
-->

---

## Checklist

### General

- [ ] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [ ] I have read the [Stirling-PDF Developer
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md)
(if applicable)
- [ ] I have read the [How to add new languages to
Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md)
(if applicable)
- [ ] I have performed a self-review of my own code
- [ ] My changes generate no new warnings

### Documentation

- [ ] I have updated relevant docs on [Stirling-PDF's doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
(if functionality has heavily changed)
- [ ] I have read the section [Add New Translation
Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags)
(for new translation tags only)

### UI Changes (if applicable)

- [ ] Screenshots or videos demonstrating the UI changes are attached
(e.g., as comments or direct attachments in the PR)

### Testing (if applicable)

- [ ] I have tested my changes locally. Refer to the [Testing
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md#6-testing)
for more details.

---------

Signed-off-by: Balázs Szücs <bszucs1209@gmail.com>
This commit is contained in:
Balázs Szücs 2025-10-30 19:03:34 +01:00 committed by GitHub
parent 311f6c8a3a
commit 9197c24606
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -0,0 +1,811 @@
package stirling.software.SPDF.controller.api.security;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.GregorianCalendar;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.*;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.encryption.AccessPermission;
import org.apache.pdfbox.pdmodel.encryption.ProtectionPolicy;
import org.apache.pdfbox.pdmodel.encryption.StandardProtectionPolicy;
import org.apache.pdfbox.pdmodel.font.PDType1Font;
import org.apache.pdfbox.pdmodel.font.Standard14Fonts;
import org.junit.jupiter.api.*;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.mockito.ArgumentMatchers;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.multipart.MultipartFile;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import stirling.software.common.model.api.PDFFile;
import stirling.software.common.service.CustomPDFDocumentFactory;
@DisplayName("GetInfoOnPDF Controller Tests")
@ExtendWith(MockitoExtension.class)
class GetInfoOnPDFTest {
@Mock private CustomPDFDocumentFactory pdfDocumentFactory;
@InjectMocks private GetInfoOnPDF getInfoOnPDF;
private ObjectMapper objectMapper;
private static final java.time.ZonedDateTime FIXED_NOW =
java.time.ZonedDateTime.parse("2020-01-01T00:00:00Z");
@BeforeEach
void setUp() {
objectMapper = new ObjectMapper();
}
/** Helper method to load a PDF file from test resources */
private MockMultipartFile loadPdfFromResources(String filename) throws IOException {
String[] possiblePaths = {
"app/common/src/test/resources/" + filename,
"testing/cucumber/exampleFiles/" + filename,
"app/core/src/test/resources/" + filename
};
for (String path : possiblePaths) {
File file = new File(path);
if (file.exists()) {
byte[] content = Files.readAllBytes(file.toPath());
return new MockMultipartFile(
"file", filename, MediaType.APPLICATION_PDF_VALUE, content);
}
}
throw new IOException("PDF file not found: " + filename);
}
/** Helper method to create a simple PDF document with text */
private PDDocument createSimplePdfWithText(String text) throws IOException {
PDDocument document = new PDDocument();
PDPage page = new PDPage(PDRectangle.A4);
document.addPage(page);
try (PDPageContentStream contentStream = new PDPageContentStream(document, page)) {
contentStream.beginText();
contentStream.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA), 12);
contentStream.newLineAtOffset(100, 700);
contentStream.showText(text);
contentStream.endText();
}
return document;
}
/** Helper method to create a PDF with metadata */
private PDDocument createPdfWithMetadata() throws IOException {
PDDocument document = createSimplePdfWithText("Test document with metadata");
PDDocumentInformation info = new PDDocumentInformation();
info.setTitle("Test Title");
info.setAuthor("Test Author");
info.setSubject("Test Subject");
info.setKeywords("test, pdf, metadata");
info.setCreator("Test Creator");
info.setProducer("Test Producer");
GregorianCalendar cal = GregorianCalendar.from(FIXED_NOW);
info.setCreationDate(cal);
info.setModificationDate(cal);
document.setDocumentInformation(info);
return document;
}
/** Helper method to create an encrypted PDF */
private PDDocument createEncryptedPdf() throws IOException {
PDDocument document = createSimplePdfWithText("Encrypted content");
AccessPermission accessPermission = new AccessPermission();
accessPermission.setCanPrint(false);
accessPermission.setCanModify(false);
ProtectionPolicy protectionPolicy =
new StandardProtectionPolicy("owner", "user", accessPermission);
document.protect(protectionPolicy);
return document;
}
/** Helper method to convert PDDocument to MockMultipartFile */
private MockMultipartFile documentToMultipartFile(PDDocument document, String filename)
throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
document.save(baos);
document.close();
return new MockMultipartFile(
"file", filename, MediaType.APPLICATION_PDF_VALUE, baos.toByteArray());
}
@Nested
@DisplayName("Basic Functionality Tests")
class BasicFunctionalityTests {
@Test
@DisplayName("Should successfully extract info from a valid PDF")
void testGetPdfInfo_ValidPdf() throws IOException {
PDDocument document = createPdfWithMetadata();
MockMultipartFile mockFile = documentToMultipartFile(document, "test.pdf");
PDFFile request = new PDFFile();
request.setFileInput(mockFile);
try (PDDocument loadedDoc = Loader.loadPDF(mockFile.getBytes())) {
Mockito.when(
pdfDocumentFactory.load(
ArgumentMatchers.any(MultipartFile.class),
ArgumentMatchers.anyBoolean()))
.thenReturn(loadedDoc);
ResponseEntity<byte[]> response = getInfoOnPDF.getPdfInfo(request);
Assertions.assertNotNull(response);
Assertions.assertEquals(HttpStatus.OK, response.getStatusCode());
Assertions.assertNotNull(response.getBody());
String jsonResponse = new String(response.getBody(), StandardCharsets.UTF_8);
JsonNode jsonNode = objectMapper.readTree(jsonResponse);
Assertions.assertTrue(jsonNode.has("Metadata"));
Assertions.assertTrue(jsonNode.has("BasicInfo"));
Assertions.assertTrue(jsonNode.has("DocumentInfo"));
Assertions.assertTrue(jsonNode.has("Compliancy"));
Assertions.assertTrue(jsonNode.has("Encryption"));
Assertions.assertTrue(jsonNode.has("Permissions"));
JsonNode metadata = jsonNode.get("Metadata");
Assertions.assertEquals("Test Title", metadata.get("Title").asText());
Assertions.assertEquals("Test Author", metadata.get("Author").asText());
}
}
@Test
@DisplayName("Should extract basic info correctly")
void testGetPdfInfo_BasicInfo() throws IOException {
PDDocument document = createSimplePdfWithText("Test content with some words");
MockMultipartFile mockFile = documentToMultipartFile(document, "basic.pdf");
PDFFile request = new PDFFile();
request.setFileInput(mockFile);
try (PDDocument loadedDoc = Loader.loadPDF(mockFile.getBytes())) {
Mockito.when(
pdfDocumentFactory.load(
ArgumentMatchers.any(MultipartFile.class),
ArgumentMatchers.anyBoolean()))
.thenReturn(loadedDoc);
ResponseEntity<byte[]> response = getInfoOnPDF.getPdfInfo(request);
String jsonResponse = new String(response.getBody(), StandardCharsets.UTF_8);
JsonNode jsonNode = objectMapper.readTree(jsonResponse);
JsonNode basicInfo = jsonNode.get("BasicInfo");
Assertions.assertTrue(basicInfo.has("Number of pages"));
Assertions.assertTrue(basicInfo.has("FileSizeInBytes"));
Assertions.assertTrue(basicInfo.has("WordCount"));
Assertions.assertTrue(basicInfo.has("CharacterCount"));
Assertions.assertEquals(1, basicInfo.get("Number of pages").asInt());
Assertions.assertTrue(basicInfo.get("FileSizeInBytes").asLong() > 0);
}
}
@Test
@DisplayName("Should handle PDF with multiple pages")
void testGetPdfInfo_MultiplePages() throws IOException {
PDDocument document = new PDDocument();
document.addPage(new PDPage(PDRectangle.A4));
document.addPage(new PDPage(PDRectangle.A4));
document.addPage(new PDPage(PDRectangle.LETTER));
MockMultipartFile mockFile = documentToMultipartFile(document, "multipage.pdf");
PDFFile request = new PDFFile();
request.setFileInput(mockFile);
try (PDDocument loadedDoc = Loader.loadPDF(mockFile.getBytes())) {
Mockito.when(
pdfDocumentFactory.load(
ArgumentMatchers.any(MultipartFile.class),
ArgumentMatchers.anyBoolean()))
.thenReturn(loadedDoc);
ResponseEntity<byte[]> response = getInfoOnPDF.getPdfInfo(request);
String jsonResponse = new String(response.getBody(), StandardCharsets.UTF_8);
JsonNode jsonNode = objectMapper.readTree(jsonResponse);
Assertions.assertEquals(
3, jsonNode.get("BasicInfo").get("Number of pages").asInt());
Assertions.assertTrue(jsonNode.has("PerPageInfo"));
JsonNode perPageInfo = jsonNode.get("PerPageInfo");
Assertions.assertTrue(perPageInfo.has("Page 1"));
Assertions.assertTrue(perPageInfo.has("Page 2"));
Assertions.assertTrue(perPageInfo.has("Page 3"));
}
}
}
@Nested
@DisplayName("Metadata Extraction Tests")
class MetadataExtractionTests {
@Test
@DisplayName("Should extract all metadata fields")
void testExtractMetadata_AllFields() throws IOException {
PDDocument document = createPdfWithMetadata();
MockMultipartFile mockFile = documentToMultipartFile(document, "metadata.pdf");
PDFFile request = new PDFFile();
request.setFileInput(mockFile);
PDDocument loadedDoc = Loader.loadPDF(mockFile.getBytes());
Mockito.when(
pdfDocumentFactory.load(
ArgumentMatchers.any(MultipartFile.class),
ArgumentMatchers.anyBoolean()))
.thenReturn(loadedDoc);
ResponseEntity<byte[]> response = getInfoOnPDF.getPdfInfo(request);
String jsonResponse = new String(response.getBody(), StandardCharsets.UTF_8);
JsonNode jsonNode = objectMapper.readTree(jsonResponse);
JsonNode metadata = jsonNode.get("Metadata");
Assertions.assertEquals("Test Title", metadata.get("Title").asText());
Assertions.assertEquals("Test Author", metadata.get("Author").asText());
Assertions.assertEquals("Test Subject", metadata.get("Subject").asText());
Assertions.assertEquals("test, pdf, metadata", metadata.get("Keywords").asText());
Assertions.assertEquals("Test Creator", metadata.get("Creator").asText());
Assertions.assertEquals("Test Producer", metadata.get("Producer").asText());
Assertions.assertTrue(metadata.has("CreationDate"));
Assertions.assertTrue(metadata.has("ModificationDate"));
loadedDoc.close();
}
@Test
@DisplayName("Should handle PDF with missing metadata")
void testExtractMetadata_MissingFields() throws IOException {
PDDocument document = createSimplePdfWithText("No metadata");
MockMultipartFile mockFile = documentToMultipartFile(document, "no-metadata.pdf");
PDFFile request = new PDFFile();
request.setFileInput(mockFile);
PDDocument loadedDoc = Loader.loadPDF(mockFile.getBytes());
Mockito.when(
pdfDocumentFactory.load(
ArgumentMatchers.any(MultipartFile.class),
ArgumentMatchers.anyBoolean()))
.thenReturn(loadedDoc);
ResponseEntity<byte[]> response = getInfoOnPDF.getPdfInfo(request);
Assertions.assertNotNull(response);
Assertions.assertEquals(HttpStatus.OK, response.getStatusCode());
String jsonResponse = new String(response.getBody(), StandardCharsets.UTF_8);
JsonNode jsonNode = objectMapper.readTree(jsonResponse);
JsonNode metadata = jsonNode.get("Metadata");
Assertions.assertNotNull(metadata);
loadedDoc.close();
}
}
@Nested
@DisplayName("Encryption and Permissions Tests")
class EncryptionPermissionsTests {
@Test
@DisplayName("Should detect unencrypted PDF")
void testEncryption_UnencryptedPdf() throws IOException {
PDDocument document = createSimplePdfWithText("Not encrypted");
MockMultipartFile mockFile = documentToMultipartFile(document, "unencrypted.pdf");
PDFFile request = new PDFFile();
request.setFileInput(mockFile);
PDDocument loadedDoc = Loader.loadPDF(mockFile.getBytes());
Mockito.when(
pdfDocumentFactory.load(
ArgumentMatchers.any(MultipartFile.class),
ArgumentMatchers.anyBoolean()))
.thenReturn(loadedDoc);
ResponseEntity<byte[]> response = getInfoOnPDF.getPdfInfo(request);
String jsonResponse = new String(response.getBody(), StandardCharsets.UTF_8);
JsonNode jsonNode = objectMapper.readTree(jsonResponse);
JsonNode encryption = jsonNode.get("Encryption");
Assertions.assertFalse(encryption.get("IsEncrypted").asBoolean());
loadedDoc.close();
}
@Test
@DisplayName("Should extract all permissions")
void testPermissions_AllPermissions() throws IOException {
PDDocument document = createSimplePdfWithText("Test permissions");
MockMultipartFile mockFile = documentToMultipartFile(document, "permissions.pdf");
PDFFile request = new PDFFile();
request.setFileInput(mockFile);
PDDocument loadedDoc = Loader.loadPDF(mockFile.getBytes());
Mockito.when(
pdfDocumentFactory.load(
ArgumentMatchers.any(MultipartFile.class),
ArgumentMatchers.anyBoolean()))
.thenReturn(loadedDoc);
ResponseEntity<byte[]> response = getInfoOnPDF.getPdfInfo(request);
String jsonResponse = new String(response.getBody(), StandardCharsets.UTF_8);
JsonNode jsonNode = objectMapper.readTree(jsonResponse);
JsonNode permissions = jsonNode.get("Permissions");
Assertions.assertTrue(permissions.has("Document Assembly"));
Assertions.assertTrue(permissions.has("Extracting Content"));
Assertions.assertTrue(permissions.has("Form Filling"));
Assertions.assertTrue(permissions.has("Modifying"));
Assertions.assertTrue(permissions.has("Printing"));
loadedDoc.close();
}
}
@Nested
@DisplayName("Form Fields Tests")
class FormFieldsTests {
@Test
@DisplayName("Should extract form fields section from PDF")
void testFormFields_Structure() throws IOException {
PDDocument document = createSimplePdfWithText("Document to test form fields section");
MockMultipartFile mockFile = documentToMultipartFile(document, "test-forms.pdf");
PDFFile request = new PDFFile();
request.setFileInput(mockFile);
PDDocument loadedDoc = Loader.loadPDF(mockFile.getBytes());
Mockito.when(
pdfDocumentFactory.load(
ArgumentMatchers.any(MultipartFile.class),
ArgumentMatchers.anyBoolean()))
.thenReturn(loadedDoc);
ResponseEntity<byte[]> response = getInfoOnPDF.getPdfInfo(request);
String jsonResponse = new String(response.getBody(), StandardCharsets.UTF_8);
JsonNode jsonNode = objectMapper.readTree(jsonResponse);
Assertions.assertTrue(jsonNode.has("FormFields"));
JsonNode formFields = jsonNode.get("FormFields");
Assertions.assertNotNull(formFields);
loadedDoc.close();
}
@Test
@DisplayName("Should handle PDF without form fields")
void testFormFields_NoFields() throws IOException {
PDDocument document = createSimplePdfWithText("No form fields");
MockMultipartFile mockFile = documentToMultipartFile(document, "no-forms.pdf");
PDFFile request = new PDFFile();
request.setFileInput(mockFile);
PDDocument loadedDoc = Loader.loadPDF(mockFile.getBytes());
Mockito.when(
pdfDocumentFactory.load(
ArgumentMatchers.any(MultipartFile.class),
ArgumentMatchers.anyBoolean()))
.thenReturn(loadedDoc);
ResponseEntity<byte[]> response = getInfoOnPDF.getPdfInfo(request);
String jsonResponse = new String(response.getBody(), StandardCharsets.UTF_8);
JsonNode jsonNode = objectMapper.readTree(jsonResponse);
JsonNode formFields = jsonNode.get("FormFields");
Assertions.assertEquals(0, formFields.size());
loadedDoc.close();
}
}
@Nested
@DisplayName("Per-Page Information Tests")
class PerPageInfoTests {
@Test
@DisplayName("Should extract page dimensions")
void testPerPageInfo_Dimensions() throws IOException {
PDDocument document = new PDDocument();
document.addPage(new PDPage(PDRectangle.A4));
document.addPage(new PDPage(PDRectangle.LETTER));
MockMultipartFile mockFile = documentToMultipartFile(document, "dimensions.pdf");
PDFFile request = new PDFFile();
request.setFileInput(mockFile);
PDDocument loadedDoc = Loader.loadPDF(mockFile.getBytes());
Mockito.when(
pdfDocumentFactory.load(
ArgumentMatchers.any(MultipartFile.class),
ArgumentMatchers.anyBoolean()))
.thenReturn(loadedDoc);
ResponseEntity<byte[]> response = getInfoOnPDF.getPdfInfo(request);
String jsonResponse = new String(response.getBody(), StandardCharsets.UTF_8);
JsonNode jsonNode = objectMapper.readTree(jsonResponse);
JsonNode perPageInfo = jsonNode.get("PerPageInfo");
JsonNode page1 = perPageInfo.get("Page 1");
Assertions.assertTrue(page1.has("Size"));
Assertions.assertTrue(page1.get("Size").has("Standard Page"));
Assertions.assertEquals("A4", page1.get("Size").get("Standard Page").asText());
JsonNode page2 = perPageInfo.get("Page 2");
Assertions.assertEquals("Letter", page2.get("Size").get("Standard Page").asText());
loadedDoc.close();
}
@Test
@DisplayName("Should extract page rotation")
void testPerPageInfo_Rotation() throws IOException {
PDDocument document = new PDDocument();
PDPage page = new PDPage(PDRectangle.A4);
page.setRotation(90);
document.addPage(page);
MockMultipartFile mockFile = documentToMultipartFile(document, "rotated.pdf");
PDFFile request = new PDFFile();
request.setFileInput(mockFile);
PDDocument loadedDoc = Loader.loadPDF(mockFile.getBytes());
Mockito.when(
pdfDocumentFactory.load(
ArgumentMatchers.any(MultipartFile.class),
ArgumentMatchers.anyBoolean()))
.thenReturn(loadedDoc);
ResponseEntity<byte[]> response = getInfoOnPDF.getPdfInfo(request);
String jsonResponse = new String(response.getBody(), StandardCharsets.UTF_8);
JsonNode jsonNode = objectMapper.readTree(jsonResponse);
JsonNode page1 = jsonNode.get("PerPageInfo").get("Page 1");
Assertions.assertEquals(90, page1.get("Rotation").asInt());
loadedDoc.close();
}
}
@Nested
@DisplayName("Validation and Error Handling Tests")
class ValidationErrorTests {
@Test
@DisplayName("Should reject null file")
void testValidation_NullFile() throws IOException {
PDFFile request = new PDFFile();
request.setFileInput(null);
ResponseEntity<byte[]> response = getInfoOnPDF.getPdfInfo(request);
Assertions.assertEquals(
HttpStatus.OK, response.getStatusCode()); // Returns error JSON with 200
String jsonResponse = new String(response.getBody(), StandardCharsets.UTF_8);
JsonNode jsonNode = objectMapper.readTree(jsonResponse);
Assertions.assertTrue(jsonNode.has("error"));
Assertions.assertTrue(jsonNode.get("error").asText().contains("PDF file is required"));
}
@Test
@DisplayName("Should reject empty file")
void testValidation_EmptyFile() throws IOException {
MockMultipartFile emptyFile =
new MockMultipartFile(
"file", "empty.pdf", MediaType.APPLICATION_PDF_VALUE, new byte[0]);
PDFFile request = new PDFFile();
request.setFileInput(emptyFile);
ResponseEntity<byte[]> response = getInfoOnPDF.getPdfInfo(request);
String jsonResponse = new String(response.getBody(), StandardCharsets.UTF_8);
JsonNode jsonNode = objectMapper.readTree(jsonResponse);
Assertions.assertTrue(jsonNode.has("error"));
}
@Test
@DisplayName("Should reject file that exceeds max size")
void testValidation_TooLargeFile() throws IOException {
MultipartFile largeFile =
new MultipartFile() {
@Override
public String getName() {
return "file";
}
@Override
public String getOriginalFilename() {
return "large.pdf";
}
@Override
public String getContentType() {
return MediaType.APPLICATION_PDF_VALUE;
}
@Override
public boolean isEmpty() {
return false;
}
@Override
public long getSize() {
// Report 101 MB without allocating memory
return 101L * 1024L * 1024L;
}
@Override
public byte[] getBytes() {
return new byte[0];
}
@Override
public java.io.InputStream getInputStream() {
return java.io.InputStream.nullInputStream();
}
@Override
public void transferTo(java.io.File dest) throws IllegalStateException {}
};
PDFFile request = new PDFFile();
request.setFileInput(largeFile);
ResponseEntity<byte[]> response = getInfoOnPDF.getPdfInfo(request);
String jsonResponse = new String(response.getBody(), StandardCharsets.UTF_8);
JsonNode jsonNode = objectMapper.readTree(jsonResponse);
Assertions.assertTrue(jsonNode.has("error"));
Assertions.assertTrue(
jsonNode.get("error").asText().contains("exceeds maximum allowed size"));
}
}
@Nested
@DisplayName("Static Helper Methods Tests")
class HelperMethodsTests {
@Test
@DisplayName("Should determine page orientation correctly")
void testGetPageOrientation() {
Assertions.assertEquals("Landscape", GetInfoOnPDF.getPageOrientation(800, 600));
Assertions.assertEquals("Portrait", GetInfoOnPDF.getPageOrientation(600, 800));
Assertions.assertEquals("Square", GetInfoOnPDF.getPageOrientation(600, 600));
}
@ParameterizedTest
@CsvSource({
"612, 792, Letter",
"595.276, 841.89, A4",
"2383.937, 3370.394, A0",
"100, 100, Custom"
})
@DisplayName("Should identify standard page sizes")
void testGetPageSize(float width, float height, String expected) {
Assertions.assertEquals(expected, GetInfoOnPDF.getPageSize(width, height));
}
@Test
@DisplayName("Should check for PDF/A standard")
void testCheckForStandard_PdfA() throws IOException {
// This would require a real PDF/A document or mocking
PDDocument document = createSimplePdfWithText("Test");
boolean result = GetInfoOnPDF.checkForStandard(document, "PDF/A");
Assertions.assertFalse(result); // Simple PDF is not PDF/A compliant
document.close();
}
@Test
@DisplayName("Should handle null document in checkForStandard")
void testCheckForStandard_NullDocument() {
boolean result = GetInfoOnPDF.checkForStandard(null, "PDF/A");
Assertions.assertFalse(result);
}
@Test
@DisplayName("Should get PDF/A conformance level")
void testGetPdfAConformanceLevel() throws IOException {
PDDocument document = createSimplePdfWithText("Test");
String level = GetInfoOnPDF.getPdfAConformanceLevel(document);
Assertions.assertNull(level);
document.close();
}
@Test
@DisplayName("Should handle encrypted document in getPdfAConformanceLevel")
void testGetPdfAConformanceLevel_EncryptedDocument() throws IOException {
PDDocument document = createEncryptedPdf();
String level = GetInfoOnPDF.getPdfAConformanceLevel(document);
Assertions.assertNull(level); // Encrypted documents return null
document.close();
}
}
@Nested
@DisplayName("Real PDF Files Tests")
class RealPdfFilesTests {
@Test
@DisplayName("Should process example.pdf from test resources")
void testRealPdf_Example() {
try {
MockMultipartFile mockFile = loadPdfFromResources("example.pdf");
PDFFile request = new PDFFile();
request.setFileInput(mockFile);
try (PDDocument loadedDoc = Loader.loadPDF(mockFile.getBytes())) {
Mockito.when(
pdfDocumentFactory.load(
ArgumentMatchers.any(MultipartFile.class),
ArgumentMatchers.anyBoolean()))
.thenReturn(loadedDoc);
ResponseEntity<byte[]> response = getInfoOnPDF.getPdfInfo(request);
Assertions.assertNotNull(response);
Assertions.assertEquals(HttpStatus.OK, response.getStatusCode());
String jsonResponse = new String(response.getBody(), StandardCharsets.UTF_8);
JsonNode jsonNode = objectMapper.readTree(jsonResponse);
Assertions.assertFalse(
jsonNode.has("error"), "Should not have error in response");
Assertions.assertTrue(jsonNode.has("BasicInfo"));
Assertions.assertTrue(jsonNode.has("DocumentInfo"));
Assertions.assertTrue(jsonNode.get("DocumentInfo").has("PDF version"));
}
} catch (IOException e) {
Assumptions.assumeTrue(
false, "Skipping test - example.pdf not found: " + e.getMessage());
}
}
@Test
@DisplayName("Should process tables.pdf")
void testRealPdf_Tables() {
try {
MockMultipartFile mockFile = loadPdfFromResources("tables.pdf");
PDFFile request = new PDFFile();
request.setFileInput(mockFile);
try (PDDocument loadedDoc = Loader.loadPDF(mockFile.getBytes())) {
Mockito.when(
pdfDocumentFactory.load(
ArgumentMatchers.any(MultipartFile.class),
ArgumentMatchers.anyBoolean()))
.thenReturn(loadedDoc);
ResponseEntity<byte[]> response = getInfoOnPDF.getPdfInfo(request);
Assertions.assertNotNull(response);
String jsonResponse = new String(response.getBody(), StandardCharsets.UTF_8);
JsonNode jsonNode = objectMapper.readTree(jsonResponse);
Assertions.assertFalse(jsonNode.has("error"));
Assertions.assertTrue(jsonNode.has("BasicInfo"));
}
} catch (IOException e) {
Assumptions.assumeTrue(
false, "Skipping test - tables.pdf not found: " + e.getMessage());
}
}
}
@Nested
@DisplayName("Compliance Tests")
class ComplianceTests {
@Test
@DisplayName("Should check PDF/A compliance")
void testCompliance_PdfA() throws IOException {
PDDocument document = createSimplePdfWithText("Test PDF/A");
MockMultipartFile mockFile = documentToMultipartFile(document, "pdfa.pdf");
PDFFile request = new PDFFile();
request.setFileInput(mockFile);
PDDocument loadedDoc = Loader.loadPDF(mockFile.getBytes());
Mockito.when(
pdfDocumentFactory.load(
ArgumentMatchers.any(MultipartFile.class),
ArgumentMatchers.anyBoolean()))
.thenReturn(loadedDoc);
ResponseEntity<byte[]> response = getInfoOnPDF.getPdfInfo(request);
String jsonResponse = new String(response.getBody(), StandardCharsets.UTF_8);
JsonNode jsonNode = objectMapper.readTree(jsonResponse);
JsonNode compliancy = jsonNode.get("Compliancy");
Assertions.assertTrue(compliancy.has("IsPDF/ACompliant"));
Assertions.assertTrue(compliancy.has("IsPDF/XCompliant"));
Assertions.assertTrue(compliancy.has("IsPDF/ECompliant"));
Assertions.assertTrue(compliancy.has("IsPDF/UACompliant"));
loadedDoc.close();
}
}
@Nested
@DisplayName("Image Statistics Tests")
class ImageStatisticsTests {
@Test
@DisplayName("Should extract image statistics from PDF")
void testImageStatistics() throws IOException {
PDDocument document = createSimplePdfWithText("Document for image statistics");
MockMultipartFile mockFile = documentToMultipartFile(document, "no-images.pdf");
PDFFile request = new PDFFile();
request.setFileInput(mockFile);
PDDocument loadedDoc = Loader.loadPDF(mockFile.getBytes());
Mockito.when(
pdfDocumentFactory.load(
ArgumentMatchers.any(MultipartFile.class),
ArgumentMatchers.anyBoolean()))
.thenReturn(loadedDoc);
ResponseEntity<byte[]> response = getInfoOnPDF.getPdfInfo(request);
String jsonResponse = new String(response.getBody(), StandardCharsets.UTF_8);
JsonNode jsonNode = objectMapper.readTree(jsonResponse);
JsonNode basicInfo = jsonNode.get("BasicInfo");
Assertions.assertTrue(basicInfo.has("TotalImages"));
Assertions.assertTrue(basicInfo.has("UniqueImages"));
Assertions.assertEquals(0, basicInfo.get("TotalImages").asInt());
Assertions.assertEquals(0, basicInfo.get("UniqueImages").asInt());
loadedDoc.close();
}
}
}