mirror of
https://github.com/arsvendg/Stirling-PDF.git
synced 2026-07-16 03:20:46 +02:00
Ensure Pixel gets disabled, PDF ToC support (#3659)
# Description of Changes 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/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/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/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/DeveloperGuide.md#6-testing) for more details. --------- Co-authored-by: Dario Ghunney Ware <[email protected]> Co-authored-by: Connor Yoh <[email protected]> Co-authored-by: a <a> Co-authored-by: Reece <[email protected]>
This commit is contained in:
co-authored by
Dario Ghunney Ware
Connor Yoh
a <a>
Reece
parent
bdc35519da
commit
1f2365f03c
+382
@@ -0,0 +1,382 @@
|
||||
package stirling.software.SPDF.controller.api;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.apache.pdfbox.pdmodel.PDDocumentCatalog;
|
||||
import org.apache.pdfbox.pdmodel.PDPage;
|
||||
import org.apache.pdfbox.pdmodel.PDPageTree;
|
||||
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDDocumentOutline;
|
||||
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDOutlineItem;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.mock.web.MockMultipartFile;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import stirling.software.SPDF.controller.api.EditTableOfContentsController.BookmarkItem;
|
||||
import stirling.software.SPDF.model.api.EditTableOfContentsRequest;
|
||||
import stirling.software.common.service.CustomPDFDocumentFactory;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class EditTableOfContentsControllerTest {
|
||||
|
||||
@Mock
|
||||
private CustomPDFDocumentFactory pdfDocumentFactory;
|
||||
|
||||
@Mock
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
@InjectMocks
|
||||
private EditTableOfContentsController editTableOfContentsController;
|
||||
|
||||
private MockMultipartFile mockFile;
|
||||
private PDDocument mockDocument;
|
||||
private PDDocumentCatalog mockCatalog;
|
||||
private PDPageTree mockPages;
|
||||
private PDPage mockPage1;
|
||||
private PDPage mockPage2;
|
||||
private PDDocumentOutline mockOutline;
|
||||
private PDOutlineItem mockOutlineItem;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
mockFile = new MockMultipartFile("file", "test.pdf", "application/pdf", "PDF content".getBytes());
|
||||
mockDocument = mock(PDDocument.class);
|
||||
mockCatalog = mock(PDDocumentCatalog.class);
|
||||
mockPages = mock(PDPageTree.class);
|
||||
mockPage1 = mock(PDPage.class);
|
||||
mockPage2 = mock(PDPage.class);
|
||||
mockOutline = mock(PDDocumentOutline.class);
|
||||
mockOutlineItem = mock(PDOutlineItem.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testExtractBookmarks_WithExistingBookmarks_Success() throws Exception {
|
||||
// Given
|
||||
when(pdfDocumentFactory.load(mockFile)).thenReturn(mockDocument);
|
||||
when(mockDocument.getDocumentCatalog()).thenReturn(mockCatalog);
|
||||
when(mockCatalog.getDocumentOutline()).thenReturn(mockOutline);
|
||||
when(mockOutline.getFirstChild()).thenReturn(mockOutlineItem);
|
||||
|
||||
when(mockOutlineItem.getTitle()).thenReturn("Chapter 1");
|
||||
when(mockOutlineItem.findDestinationPage(mockDocument)).thenReturn(mockPage1);
|
||||
when(mockDocument.getPages()).thenReturn(mockPages);
|
||||
when(mockPages.indexOf(mockPage1)).thenReturn(0);
|
||||
when(mockOutlineItem.getFirstChild()).thenReturn(null);
|
||||
when(mockOutlineItem.getNextSibling()).thenReturn(null);
|
||||
|
||||
// When
|
||||
List<Map<String, Object>> result = editTableOfContentsController.extractBookmarks(mockFile);
|
||||
|
||||
// Then
|
||||
assertNotNull(result);
|
||||
assertEquals(1, result.size());
|
||||
|
||||
Map<String, Object> bookmark = result.get(0);
|
||||
assertEquals("Chapter 1", bookmark.get("title"));
|
||||
assertEquals(1, bookmark.get("pageNumber")); // 1-based
|
||||
assertInstanceOf(List.class, bookmark.get("children"));
|
||||
|
||||
verify(mockDocument).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testExtractBookmarks_NoOutline_ReturnsEmptyList() throws Exception {
|
||||
// Given
|
||||
when(pdfDocumentFactory.load(mockFile)).thenReturn(mockDocument);
|
||||
when(mockDocument.getDocumentCatalog()).thenReturn(mockCatalog);
|
||||
when(mockCatalog.getDocumentOutline()).thenReturn(null);
|
||||
|
||||
// When
|
||||
List<Map<String, Object>> result = editTableOfContentsController.extractBookmarks(mockFile);
|
||||
|
||||
// Then
|
||||
assertNotNull(result);
|
||||
assertTrue(result.isEmpty());
|
||||
verify(mockDocument).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testExtractBookmarks_WithNestedBookmarks_Success() throws Exception {
|
||||
// Given
|
||||
PDOutlineItem childItem = mock(PDOutlineItem.class);
|
||||
|
||||
when(pdfDocumentFactory.load(mockFile)).thenReturn(mockDocument);
|
||||
when(mockDocument.getDocumentCatalog()).thenReturn(mockCatalog);
|
||||
when(mockCatalog.getDocumentOutline()).thenReturn(mockOutline);
|
||||
when(mockOutline.getFirstChild()).thenReturn(mockOutlineItem);
|
||||
|
||||
// Parent bookmark
|
||||
when(mockOutlineItem.getTitle()).thenReturn("Chapter 1");
|
||||
when(mockOutlineItem.findDestinationPage(mockDocument)).thenReturn(mockPage1);
|
||||
when(mockDocument.getPages()).thenReturn(mockPages);
|
||||
when(mockPages.indexOf(mockPage1)).thenReturn(0);
|
||||
when(mockOutlineItem.getFirstChild()).thenReturn(childItem);
|
||||
when(mockOutlineItem.getNextSibling()).thenReturn(null);
|
||||
|
||||
// Child bookmark
|
||||
when(childItem.getTitle()).thenReturn("Section 1.1");
|
||||
when(childItem.findDestinationPage(mockDocument)).thenReturn(mockPage2);
|
||||
when(mockPages.indexOf(mockPage2)).thenReturn(1);
|
||||
when(childItem.getFirstChild()).thenReturn(null);
|
||||
when(childItem.getNextSibling()).thenReturn(null);
|
||||
|
||||
// When
|
||||
List<Map<String, Object>> result = editTableOfContentsController.extractBookmarks(mockFile);
|
||||
|
||||
// Then
|
||||
assertNotNull(result);
|
||||
assertEquals(1, result.size());
|
||||
|
||||
Map<String, Object> parentBookmark = result.get(0);
|
||||
assertEquals("Chapter 1", parentBookmark.get("title"));
|
||||
assertEquals(1, parentBookmark.get("pageNumber"));
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Map<String, Object>> children = (List<Map<String, Object>>) parentBookmark.get("children");
|
||||
assertEquals(1, children.size());
|
||||
|
||||
Map<String, Object> childBookmark = children.get(0);
|
||||
assertEquals("Section 1.1", childBookmark.get("title"));
|
||||
assertEquals(2, childBookmark.get("pageNumber"));
|
||||
|
||||
verify(mockDocument).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testExtractBookmarks_PageNotFound_UsesPageOne() throws Exception {
|
||||
// Given
|
||||
when(pdfDocumentFactory.load(mockFile)).thenReturn(mockDocument);
|
||||
when(mockDocument.getDocumentCatalog()).thenReturn(mockCatalog);
|
||||
when(mockCatalog.getDocumentOutline()).thenReturn(mockOutline);
|
||||
when(mockOutline.getFirstChild()).thenReturn(mockOutlineItem);
|
||||
|
||||
when(mockOutlineItem.getTitle()).thenReturn("Chapter 1");
|
||||
when(mockOutlineItem.findDestinationPage(mockDocument)).thenReturn(null); // Page not found
|
||||
when(mockOutlineItem.getFirstChild()).thenReturn(null);
|
||||
when(mockOutlineItem.getNextSibling()).thenReturn(null);
|
||||
|
||||
// When
|
||||
List<Map<String, Object>> result = editTableOfContentsController.extractBookmarks(mockFile);
|
||||
|
||||
// Then
|
||||
assertNotNull(result);
|
||||
assertEquals(1, result.size());
|
||||
|
||||
Map<String, Object> bookmark = result.get(0);
|
||||
assertEquals("Chapter 1", bookmark.get("title"));
|
||||
assertEquals(1, bookmark.get("pageNumber")); // Default to page 1
|
||||
|
||||
verify(mockDocument).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEditTableOfContents_Success() throws Exception {
|
||||
// Given
|
||||
EditTableOfContentsRequest request = new EditTableOfContentsRequest();
|
||||
request.setFileInput(mockFile);
|
||||
request.setBookmarkData("[{\"title\":\"Chapter 1\",\"pageNumber\":1,\"children\":[]}]");
|
||||
request.setReplaceExisting(true);
|
||||
|
||||
List<BookmarkItem> bookmarks = new ArrayList<>();
|
||||
BookmarkItem bookmark = new BookmarkItem();
|
||||
bookmark.setTitle("Chapter 1");
|
||||
bookmark.setPageNumber(1);
|
||||
bookmark.setChildren(new ArrayList<>());
|
||||
bookmarks.add(bookmark);
|
||||
|
||||
when(pdfDocumentFactory.load(mockFile)).thenReturn(mockDocument);
|
||||
when(objectMapper.readValue(eq(request.getBookmarkData()), any(TypeReference.class))).thenReturn(bookmarks);
|
||||
when(mockDocument.getDocumentCatalog()).thenReturn(mockCatalog);
|
||||
when(mockDocument.getNumberOfPages()).thenReturn(5);
|
||||
when(mockDocument.getPage(0)).thenReturn(mockPage1);
|
||||
|
||||
// Mock saving behavior
|
||||
doAnswer(invocation -> {
|
||||
ByteArrayOutputStream baos = invocation.getArgument(0);
|
||||
baos.write("mocked pdf content".getBytes());
|
||||
return null;
|
||||
}).when(mockDocument).save(any(ByteArrayOutputStream.class));
|
||||
|
||||
// When
|
||||
ResponseEntity<byte[]> result = editTableOfContentsController.editTableOfContents(request);
|
||||
|
||||
// Then
|
||||
assertNotNull(result);
|
||||
assertNotNull(result.getBody());
|
||||
|
||||
ArgumentCaptor<PDDocumentOutline> outlineCaptor = ArgumentCaptor.forClass(PDDocumentOutline.class);
|
||||
verify(mockCatalog).setDocumentOutline(outlineCaptor.capture());
|
||||
|
||||
PDDocumentOutline capturedOutline = outlineCaptor.getValue();
|
||||
assertNotNull(capturedOutline);
|
||||
|
||||
verify(mockDocument).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEditTableOfContents_WithNestedBookmarks_Success() throws Exception {
|
||||
// Given
|
||||
EditTableOfContentsRequest request = new EditTableOfContentsRequest();
|
||||
request.setFileInput(mockFile);
|
||||
|
||||
String bookmarkJson = "[{\"title\":\"Chapter 1\",\"pageNumber\":1,\"children\":[{\"title\":\"Section 1.1\",\"pageNumber\":2,\"children\":[]}]}]";
|
||||
request.setBookmarkData(bookmarkJson);
|
||||
|
||||
List<BookmarkItem> bookmarks = new ArrayList<>();
|
||||
BookmarkItem parentBookmark = new BookmarkItem();
|
||||
parentBookmark.setTitle("Chapter 1");
|
||||
parentBookmark.setPageNumber(1);
|
||||
|
||||
BookmarkItem childBookmark = new BookmarkItem();
|
||||
childBookmark.setTitle("Section 1.1");
|
||||
childBookmark.setPageNumber(2);
|
||||
childBookmark.setChildren(new ArrayList<>());
|
||||
|
||||
List<BookmarkItem> children = new ArrayList<>();
|
||||
children.add(childBookmark);
|
||||
parentBookmark.setChildren(children);
|
||||
bookmarks.add(parentBookmark);
|
||||
|
||||
when(pdfDocumentFactory.load(mockFile)).thenReturn(mockDocument);
|
||||
when(objectMapper.readValue(eq(bookmarkJson), any(TypeReference.class))).thenReturn(bookmarks);
|
||||
when(mockDocument.getDocumentCatalog()).thenReturn(mockCatalog);
|
||||
when(mockDocument.getNumberOfPages()).thenReturn(5);
|
||||
when(mockDocument.getPage(0)).thenReturn(mockPage1);
|
||||
when(mockDocument.getPage(1)).thenReturn(mockPage2);
|
||||
|
||||
doAnswer(invocation -> {
|
||||
ByteArrayOutputStream baos = invocation.getArgument(0);
|
||||
baos.write("mocked pdf content".getBytes());
|
||||
return null;
|
||||
}).when(mockDocument).save(any(ByteArrayOutputStream.class));
|
||||
|
||||
// When
|
||||
ResponseEntity<byte[]> result = editTableOfContentsController.editTableOfContents(request);
|
||||
|
||||
// Then
|
||||
assertNotNull(result);
|
||||
verify(mockCatalog).setDocumentOutline(any(PDDocumentOutline.class));
|
||||
verify(mockDocument).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEditTableOfContents_PageNumberBounds_ClampsValues() throws Exception {
|
||||
// Given
|
||||
EditTableOfContentsRequest request = new EditTableOfContentsRequest();
|
||||
request.setFileInput(mockFile);
|
||||
request.setBookmarkData("[{\"title\":\"Chapter 1\",\"pageNumber\":-5,\"children\":[]},{\"title\":\"Chapter 2\",\"pageNumber\":100,\"children\":[]}]");
|
||||
|
||||
List<BookmarkItem> bookmarks = new ArrayList<>();
|
||||
|
||||
BookmarkItem bookmark1 = new BookmarkItem();
|
||||
bookmark1.setTitle("Chapter 1");
|
||||
bookmark1.setPageNumber(-5); // Negative page number
|
||||
bookmark1.setChildren(new ArrayList<>());
|
||||
|
||||
BookmarkItem bookmark2 = new BookmarkItem();
|
||||
bookmark2.setTitle("Chapter 2");
|
||||
bookmark2.setPageNumber(100); // Page number exceeds document pages
|
||||
bookmark2.setChildren(new ArrayList<>());
|
||||
|
||||
bookmarks.add(bookmark1);
|
||||
bookmarks.add(bookmark2);
|
||||
|
||||
when(pdfDocumentFactory.load(mockFile)).thenReturn(mockDocument);
|
||||
when(objectMapper.readValue(eq(request.getBookmarkData()), any(TypeReference.class))).thenReturn(bookmarks);
|
||||
when(mockDocument.getDocumentCatalog()).thenReturn(mockCatalog);
|
||||
when(mockDocument.getNumberOfPages()).thenReturn(5);
|
||||
when(mockDocument.getPage(0)).thenReturn(mockPage1); // For negative page number
|
||||
when(mockDocument.getPage(4)).thenReturn(mockPage2); // For page number exceeding bounds
|
||||
|
||||
doAnswer(invocation -> {
|
||||
ByteArrayOutputStream baos = invocation.getArgument(0);
|
||||
baos.write("mocked pdf content".getBytes());
|
||||
return null;
|
||||
}).when(mockDocument).save(any(ByteArrayOutputStream.class));
|
||||
|
||||
// When
|
||||
ResponseEntity<byte[]> result = editTableOfContentsController.editTableOfContents(request);
|
||||
|
||||
// Then
|
||||
assertNotNull(result);
|
||||
verify(mockDocument).getPage(0); // Clamped to first page
|
||||
verify(mockDocument).getPage(4); // Clamped to last page
|
||||
verify(mockDocument).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCreateOutlineItem_ValidPageNumber_Success() throws Exception {
|
||||
// Given
|
||||
BookmarkItem bookmark = new BookmarkItem();
|
||||
bookmark.setTitle("Test Chapter");
|
||||
bookmark.setPageNumber(3);
|
||||
|
||||
when(mockDocument.getNumberOfPages()).thenReturn(5);
|
||||
when(mockDocument.getPage(2)).thenReturn(mockPage1); // 0-indexed
|
||||
|
||||
// When
|
||||
Method createOutlineItemMethod = EditTableOfContentsController.class.getDeclaredMethod("createOutlineItem", PDDocument.class, BookmarkItem.class);
|
||||
createOutlineItemMethod.setAccessible(true);
|
||||
PDOutlineItem result = (PDOutlineItem) createOutlineItemMethod.invoke(editTableOfContentsController, mockDocument, bookmark);
|
||||
|
||||
// Then
|
||||
assertNotNull(result);
|
||||
verify(mockDocument).getPage(2);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void testBookmarkItem_GettersAndSetters() {
|
||||
// Given
|
||||
BookmarkItem bookmark = new BookmarkItem();
|
||||
List<BookmarkItem> children = new ArrayList<>();
|
||||
|
||||
// When
|
||||
bookmark.setTitle("Test Title");
|
||||
bookmark.setPageNumber(5);
|
||||
bookmark.setChildren(children);
|
||||
|
||||
// Then
|
||||
assertEquals("Test Title", bookmark.getTitle());
|
||||
assertEquals(5, bookmark.getPageNumber());
|
||||
assertEquals(children, bookmark.getChildren());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEditTableOfContents_IOExceptionDuringLoad_ThrowsException() throws Exception {
|
||||
// Given
|
||||
EditTableOfContentsRequest request = new EditTableOfContentsRequest();
|
||||
request.setFileInput(mockFile);
|
||||
|
||||
when(pdfDocumentFactory.load(mockFile)).thenThrow(new RuntimeException("Failed to load PDF"));
|
||||
|
||||
// When & Then
|
||||
assertThrows(RuntimeException.class, () -> editTableOfContentsController.editTableOfContents(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testExtractBookmarks_IOExceptionDuringLoad_ThrowsException() throws Exception {
|
||||
// Given
|
||||
when(pdfDocumentFactory.load(mockFile)).thenThrow(new RuntimeException("Failed to load PDF"));
|
||||
|
||||
// When & Then
|
||||
assertThrows(RuntimeException.class, () -> editTableOfContentsController.extractBookmarks(mockFile));
|
||||
}
|
||||
}
|
||||
+279
@@ -0,0 +1,279 @@
|
||||
package stirling.software.SPDF.controller.api;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.apache.pdfbox.pdmodel.PDDocumentCatalog;
|
||||
import org.apache.pdfbox.pdmodel.PDPage;
|
||||
import org.apache.pdfbox.pdmodel.PDPageTree;
|
||||
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDDocumentOutline;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.mock.web.MockMultipartFile;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import stirling.software.common.service.CustomPDFDocumentFactory;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class MergeControllerTest {
|
||||
|
||||
@Mock
|
||||
private CustomPDFDocumentFactory pdfDocumentFactory;
|
||||
|
||||
@InjectMocks
|
||||
private MergeController mergeController;
|
||||
|
||||
private MockMultipartFile mockFile1;
|
||||
private MockMultipartFile mockFile2;
|
||||
private MockMultipartFile mockFile3;
|
||||
private PDDocument mockDocument;
|
||||
private PDDocument mockMergedDocument;
|
||||
private PDDocumentCatalog mockCatalog;
|
||||
private PDPageTree mockPages;
|
||||
private PDPage mockPage1;
|
||||
private PDPage mockPage2;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
mockFile1 = new MockMultipartFile("file1", "document1.pdf", "application/pdf", "PDF content 1".getBytes());
|
||||
mockFile2 = new MockMultipartFile("file2", "document2.pdf", "application/pdf", "PDF content 2".getBytes());
|
||||
mockFile3 = new MockMultipartFile("file3", "chapter3.pdf", "application/pdf", "PDF content 3".getBytes());
|
||||
|
||||
mockDocument = mock(PDDocument.class);
|
||||
mockMergedDocument = mock(PDDocument.class);
|
||||
mockCatalog = mock(PDDocumentCatalog.class);
|
||||
mockPages = mock(PDPageTree.class);
|
||||
mockPage1 = mock(PDPage.class);
|
||||
mockPage2 = mock(PDPage.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAddTableOfContents_WithMultipleFiles_Success() throws Exception {
|
||||
// Given
|
||||
MultipartFile[] files = {mockFile1, mockFile2, mockFile3};
|
||||
|
||||
// Mock the merged document setup
|
||||
when(mockMergedDocument.getDocumentCatalog()).thenReturn(mockCatalog);
|
||||
when(mockMergedDocument.getNumberOfPages()).thenReturn(6);
|
||||
when(mockMergedDocument.getPage(0)).thenReturn(mockPage1);
|
||||
when(mockMergedDocument.getPage(2)).thenReturn(mockPage2);
|
||||
when(mockMergedDocument.getPage(4)).thenReturn(mockPage1);
|
||||
|
||||
// Mock individual document loading for page count
|
||||
PDDocument doc1 = mock(PDDocument.class);
|
||||
PDDocument doc2 = mock(PDDocument.class);
|
||||
PDDocument doc3 = mock(PDDocument.class);
|
||||
|
||||
when(pdfDocumentFactory.load(mockFile1)).thenReturn(doc1);
|
||||
when(pdfDocumentFactory.load(mockFile2)).thenReturn(doc2);
|
||||
when(pdfDocumentFactory.load(mockFile3)).thenReturn(doc3);
|
||||
|
||||
when(doc1.getNumberOfPages()).thenReturn(2);
|
||||
when(doc2.getNumberOfPages()).thenReturn(2);
|
||||
when(doc3.getNumberOfPages()).thenReturn(2);
|
||||
|
||||
// When
|
||||
Method addTableOfContentsMethod = MergeController.class.getDeclaredMethod("addTableOfContents", PDDocument.class, MultipartFile[].class);
|
||||
addTableOfContentsMethod.setAccessible(true);
|
||||
addTableOfContentsMethod.invoke(mergeController, mockMergedDocument, files);
|
||||
|
||||
// Then
|
||||
ArgumentCaptor<PDDocumentOutline> outlineCaptor = ArgumentCaptor.forClass(PDDocumentOutline.class);
|
||||
verify(mockCatalog).setDocumentOutline(outlineCaptor.capture());
|
||||
|
||||
PDDocumentOutline capturedOutline = outlineCaptor.getValue();
|
||||
assertNotNull(capturedOutline);
|
||||
|
||||
// Verify that documents were loaded for page count
|
||||
verify(pdfDocumentFactory).load(mockFile1);
|
||||
verify(pdfDocumentFactory).load(mockFile2);
|
||||
verify(pdfDocumentFactory).load(mockFile3);
|
||||
|
||||
// Verify document closing
|
||||
verify(doc1).close();
|
||||
verify(doc2).close();
|
||||
verify(doc3).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAddTableOfContents_WithSingleFile_Success() throws Exception {
|
||||
// Given
|
||||
MultipartFile[] files = {mockFile1};
|
||||
|
||||
when(mockMergedDocument.getDocumentCatalog()).thenReturn(mockCatalog);
|
||||
when(mockMergedDocument.getNumberOfPages()).thenReturn(3);
|
||||
when(mockMergedDocument.getPage(0)).thenReturn(mockPage1);
|
||||
|
||||
PDDocument doc1 = mock(PDDocument.class);
|
||||
when(pdfDocumentFactory.load(mockFile1)).thenReturn(doc1);
|
||||
when(doc1.getNumberOfPages()).thenReturn(3);
|
||||
|
||||
// When
|
||||
Method addTableOfContentsMethod = MergeController.class.getDeclaredMethod("addTableOfContents", PDDocument.class, MultipartFile[].class);
|
||||
addTableOfContentsMethod.setAccessible(true);
|
||||
addTableOfContentsMethod.invoke(mergeController, mockMergedDocument, files);
|
||||
|
||||
// Then
|
||||
verify(mockCatalog).setDocumentOutline(any(PDDocumentOutline.class));
|
||||
verify(pdfDocumentFactory).load(mockFile1);
|
||||
verify(doc1).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAddTableOfContents_WithEmptyArray_Success() throws Exception {
|
||||
// Given
|
||||
MultipartFile[] files = {};
|
||||
when(mockMergedDocument.getDocumentCatalog()).thenReturn(mockCatalog);
|
||||
|
||||
// When
|
||||
Method addTableOfContentsMethod = MergeController.class.getDeclaredMethod("addTableOfContents", PDDocument.class, MultipartFile[].class);
|
||||
addTableOfContentsMethod.setAccessible(true);
|
||||
addTableOfContentsMethod.invoke(mergeController, mockMergedDocument, files);
|
||||
|
||||
// Then
|
||||
verify(mockMergedDocument).getDocumentCatalog();
|
||||
verify(mockCatalog).setDocumentOutline(any(PDDocumentOutline.class));
|
||||
verifyNoInteractions(pdfDocumentFactory);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAddTableOfContents_WithIOException_HandlesGracefully() throws Exception {
|
||||
// Given
|
||||
MultipartFile[] files = {mockFile1, mockFile2};
|
||||
|
||||
when(mockMergedDocument.getDocumentCatalog()).thenReturn(mockCatalog);
|
||||
when(mockMergedDocument.getNumberOfPages()).thenReturn(4);
|
||||
when(mockMergedDocument.getPage(anyInt())).thenReturn(mockPage1); // Use anyInt() to avoid stubbing conflicts
|
||||
|
||||
// First document loads successfully
|
||||
PDDocument doc1 = mock(PDDocument.class);
|
||||
when(pdfDocumentFactory.load(mockFile1)).thenReturn(doc1);
|
||||
when(doc1.getNumberOfPages()).thenReturn(2);
|
||||
|
||||
// Second document throws IOException
|
||||
when(pdfDocumentFactory.load(mockFile2)).thenThrow(new IOException("Failed to load document"));
|
||||
|
||||
// When
|
||||
Method addTableOfContentsMethod = MergeController.class.getDeclaredMethod("addTableOfContents", PDDocument.class, MultipartFile[].class);
|
||||
addTableOfContentsMethod.setAccessible(true);
|
||||
|
||||
// Should not throw exception
|
||||
assertDoesNotThrow(() ->
|
||||
addTableOfContentsMethod.invoke(mergeController, mockMergedDocument, files)
|
||||
);
|
||||
|
||||
// Then
|
||||
verify(mockCatalog).setDocumentOutline(any(PDDocumentOutline.class));
|
||||
verify(pdfDocumentFactory).load(mockFile1);
|
||||
verify(pdfDocumentFactory).load(mockFile2);
|
||||
verify(doc1).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAddTableOfContents_FilenameWithoutExtension_UsesFullName() throws Exception {
|
||||
// Given
|
||||
MockMultipartFile fileWithoutExtension = new MockMultipartFile("file", "document_no_ext", "application/pdf", "PDF content".getBytes());
|
||||
MultipartFile[] files = {fileWithoutExtension};
|
||||
|
||||
when(mockMergedDocument.getDocumentCatalog()).thenReturn(mockCatalog);
|
||||
when(mockMergedDocument.getNumberOfPages()).thenReturn(1);
|
||||
when(mockMergedDocument.getPage(0)).thenReturn(mockPage1);
|
||||
|
||||
PDDocument doc = mock(PDDocument.class);
|
||||
when(pdfDocumentFactory.load(fileWithoutExtension)).thenReturn(doc);
|
||||
when(doc.getNumberOfPages()).thenReturn(1);
|
||||
|
||||
// When
|
||||
Method addTableOfContentsMethod = MergeController.class.getDeclaredMethod("addTableOfContents", PDDocument.class, MultipartFile[].class);
|
||||
addTableOfContentsMethod.setAccessible(true);
|
||||
addTableOfContentsMethod.invoke(mergeController, mockMergedDocument, files);
|
||||
|
||||
// Then
|
||||
verify(mockCatalog).setDocumentOutline(any(PDDocumentOutline.class));
|
||||
verify(doc).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAddTableOfContents_PageIndexExceedsDocumentPages_HandlesGracefully() throws Exception {
|
||||
// Given
|
||||
MultipartFile[] files = {mockFile1};
|
||||
|
||||
when(mockMergedDocument.getDocumentCatalog()).thenReturn(mockCatalog);
|
||||
when(mockMergedDocument.getNumberOfPages()).thenReturn(0); // No pages in merged document
|
||||
|
||||
PDDocument doc1 = mock(PDDocument.class);
|
||||
when(pdfDocumentFactory.load(mockFile1)).thenReturn(doc1);
|
||||
when(doc1.getNumberOfPages()).thenReturn(3);
|
||||
|
||||
// When
|
||||
Method addTableOfContentsMethod = MergeController.class.getDeclaredMethod("addTableOfContents", PDDocument.class, MultipartFile[].class);
|
||||
addTableOfContentsMethod.setAccessible(true);
|
||||
|
||||
// Should not throw exception
|
||||
assertDoesNotThrow(() ->
|
||||
addTableOfContentsMethod.invoke(mergeController, mockMergedDocument, files)
|
||||
);
|
||||
|
||||
// Then
|
||||
verify(mockCatalog).setDocumentOutline(any(PDDocumentOutline.class));
|
||||
verify(mockMergedDocument, never()).getPage(anyInt());
|
||||
verify(doc1).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMergeDocuments_Success() throws IOException {
|
||||
// Given
|
||||
PDDocument doc1 = mock(PDDocument.class);
|
||||
PDDocument doc2 = mock(PDDocument.class);
|
||||
List<PDDocument> documents = Arrays.asList(doc1, doc2);
|
||||
|
||||
PDPageTree pages1 = mock(PDPageTree.class);
|
||||
PDPageTree pages2 = mock(PDPageTree.class);
|
||||
PDPage page1 = mock(PDPage.class);
|
||||
PDPage page2 = mock(PDPage.class);
|
||||
|
||||
when(pdfDocumentFactory.createNewDocument()).thenReturn(mockMergedDocument);
|
||||
when(doc1.getPages()).thenReturn(pages1);
|
||||
when(doc2.getPages()).thenReturn(pages2);
|
||||
when(pages1.iterator()).thenReturn(Arrays.asList(page1).iterator());
|
||||
when(pages2.iterator()).thenReturn(Arrays.asList(page2).iterator());
|
||||
|
||||
// When
|
||||
PDDocument result = mergeController.mergeDocuments(documents);
|
||||
|
||||
// Then
|
||||
assertNotNull(result);
|
||||
assertEquals(mockMergedDocument, result);
|
||||
verify(mockMergedDocument).addPage(page1);
|
||||
verify(mockMergedDocument).addPage(page2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMergeDocuments_EmptyList_ReturnsEmptyDocument() throws IOException {
|
||||
// Given
|
||||
List<PDDocument> documents = Arrays.asList();
|
||||
|
||||
when(pdfDocumentFactory.createNewDocument()).thenReturn(mockMergedDocument);
|
||||
|
||||
// When
|
||||
PDDocument result = mergeController.mergeDocuments(documents);
|
||||
|
||||
// Then
|
||||
assertNotNull(result);
|
||||
assertEquals(mockMergedDocument, result);
|
||||
verify(mockMergedDocument, never()).addPage(any(PDPage.class));
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user