Delete code from invalid license (#5947)

This commit is contained in:
Anthony Stirling
2026-03-16 11:01:31 +00:00
committed by GitHub
parent 971321fb19
commit cddc8e6df0
31 changed files with 64 additions and 5520 deletions
@@ -373,7 +373,6 @@ public class EndpointConfiguration {
addEndpointToGroup("Other", REMOVE_BLANKS);
addEndpointToGroup("Other", "remove-annotations");
addEndpointToGroup("Other", "get-info-on-pdf");
addEndpointToGroup("Other", "remove-image-pdf");
addEndpointToGroup("Other", "add-attachments");
addEndpointToGroup("Other", "replace-invert-pdf");
addEndpointToGroup("Other", "edit-table-of-contents");
@@ -488,7 +487,6 @@ public class EndpointConfiguration {
addEndpointToGroup("Java", REMOVE_BLANKS);
addEndpointToGroup("Java", "remove-annotations");
addEndpointToGroup("Java", "pdf-to-text");
addEndpointToGroup("Java", "remove-image-pdf");
addEndpointToGroup("Java", "pdf-to-markdown");
addEndpointToGroup("Java", "add-attachments");
addEndpointToGroup("Java", "compress-pdf");
@@ -1,44 +0,0 @@
package stirling.software.common.util;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
import org.springframework.ui.Model;
import org.springframework.web.servlet.ModelAndView;
public class ErrorUtilsTest {
@Test
public void testExceptionToModel() {
// Create a mock Model
Model model = new org.springframework.ui.ExtendedModelMap();
// Create a test exception
Exception ex = new Exception("Test Exception");
// Call the method under test
Model resultModel = ErrorUtils.exceptionToModel(model, ex);
// Verify the result
assertNotNull(resultModel);
assertEquals("Test Exception", resultModel.getAttribute("errorMessage"));
assertNotNull(resultModel.getAttribute("stackTrace"));
}
@Test
public void testExceptionToModelView() {
// Create a mock Model
Model model = new org.springframework.ui.ExtendedModelMap();
// Create a test exception
Exception ex = new Exception("Test Exception");
// Call the method under test
ModelAndView modelAndView = ErrorUtils.exceptionToModelView(model, ex);
// Verify the result
assertNotNull(modelAndView);
assertEquals("Test Exception", modelAndView.getModel().get("errorMessage"));
assertNotNull(modelAndView.getModel().get("stackTrace"));
}
}
@@ -1,194 +0,0 @@
package stirling.software.common.util;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.FileTime;
import java.time.Instant;
import java.util.List;
import java.util.function.Predicate;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.io.TempDir;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import stirling.software.common.configuration.RuntimePathConfig;
@ExtendWith(MockitoExtension.class)
class FileMonitorTest {
@TempDir Path tempDir;
@Mock private RuntimePathConfig runtimePathConfig;
@Mock private Predicate<Path> pathFilter;
private FileMonitor fileMonitor;
@BeforeEach
void setUp() throws IOException {
when(runtimePathConfig.getPipelineWatchedFoldersPaths())
.thenReturn(List.of(tempDir.toString()));
// This mock is used in all tests except testPathFilter
// We use lenient to avoid UnnecessaryStubbingException in that test
Mockito.lenient().when(pathFilter.test(any())).thenReturn(true);
fileMonitor = new FileMonitor(pathFilter, runtimePathConfig);
}
@Test
void testIsFileReadyForProcessing_OldFile() throws IOException {
// Capture test time at the beginning for deterministic calculations
final Instant testTime = Instant.now();
// Create a test file
Path testFile = tempDir.resolve("test-file.txt");
Files.write(testFile, "test content".getBytes());
// Set modified time to 10 seconds ago (relative to test start time)
Files.setLastModifiedTime(testFile, FileTime.from(testTime.minusMillis(10000)));
// File should be ready for processing as it was modified more than 5 seconds ago
assertTrue(fileMonitor.isFileReadyForProcessing(testFile));
}
@Test
void testIsFileReadyForProcessing_RecentFile() throws IOException {
// Capture test time at the beginning for deterministic calculations
final Instant testTime = Instant.now();
// Create a test file
Path testFile = tempDir.resolve("recent-file.txt");
Files.write(testFile, "test content".getBytes());
// Set modified time to just now (relative to test start time)
Files.setLastModifiedTime(testFile, FileTime.from(testTime));
// File should not be ready for processing as it was just modified
assertFalse(fileMonitor.isFileReadyForProcessing(testFile));
}
@Test
void testIsFileReadyForProcessing_NonExistentFile() {
// Create a path to a file that doesn't exist
Path nonExistentFile = tempDir.resolve("non-existent-file.txt");
// Non-existent file should not be ready for processing
assertFalse(fileMonitor.isFileReadyForProcessing(nonExistentFile));
}
@Test
void testIsFileReadyForProcessing_LockedFile() throws IOException {
// Capture test time at the beginning for deterministic calculations
final Instant testTime = Instant.now();
// Create a test file
Path testFile = tempDir.resolve("locked-file.txt");
Files.write(testFile, "test content".getBytes());
// Set modified time to 10 seconds ago (relative to test start time) to make sure it passes
// the time check
Files.setLastModifiedTime(testFile, FileTime.from(testTime.minusMillis(10000)));
// Verify the file is considered ready when it meets the time criteria
assertTrue(
fileMonitor.isFileReadyForProcessing(testFile),
"File should be ready for processing when sufficiently old");
}
@Test
void testPathFilter() throws IOException {
// Use a simple lambda instead of a mock for better control
Predicate<Path> pdfFilter = path -> path.toString().endsWith(".pdf");
// Create a new FileMonitor with the PDF filter
FileMonitor pdfMonitor = new FileMonitor(pdfFilter, runtimePathConfig);
// Create a PDF file
Path pdfFile = tempDir.resolve("test.pdf");
Files.write(pdfFile, "pdf content".getBytes());
Files.setLastModifiedTime(pdfFile, FileTime.from(Instant.ofEpochMilli(1000000L)));
// Create a TXT file
Path txtFile = tempDir.resolve("test.txt");
Files.write(txtFile, "text content".getBytes());
Files.setLastModifiedTime(txtFile, FileTime.from(Instant.ofEpochMilli(1000000L)));
// PDF file should be ready for processing
assertTrue(pdfMonitor.isFileReadyForProcessing(pdfFile));
// Note: In the current implementation, FileMonitor.isFileReadyForProcessing()
// doesn't check file filters directly - it only checks criteria like file existence
// and modification time. The filtering is likely handled elsewhere in the workflow.
// To avoid test failures, we'll verify that the filter itself works correctly
assertFalse(pdfFilter.test(txtFile), "PDF filter should reject txt files");
assertTrue(pdfFilter.test(pdfFile), "PDF filter should accept pdf files");
}
@Test
void testIsFileReadyForProcessing_FileInUse() throws IOException {
// Capture test time at the beginning for deterministic calculations
final Instant testTime = Instant.now();
// Create a test file
Path testFile = tempDir.resolve("in-use-file.txt");
Files.write(testFile, "initial content".getBytes());
// Set modified time to 10 seconds ago (relative to test start time)
Files.setLastModifiedTime(testFile, FileTime.from(testTime.minusMillis(10000)));
// First check that the file is ready when meeting time criteria
assertTrue(
fileMonitor.isFileReadyForProcessing(testFile),
"File should be ready for processing when sufficiently old");
// After modifying the file to simulate closing, it should still be ready
Files.write(testFile, "updated content".getBytes());
Files.setLastModifiedTime(testFile, FileTime.from(testTime.minusMillis(10000)));
assertTrue(
fileMonitor.isFileReadyForProcessing(testFile),
"File should be ready for processing after updating");
}
@Test
void testIsFileReadyForProcessing_FileWithAbsolutePath() throws IOException {
// Capture test time at the beginning for deterministic calculations
final Instant testTime = Instant.now();
// Create a test file
Path testFile = tempDir.resolve("absolute-path-file.txt");
Files.write(testFile, "test content".getBytes());
// Set modified time to 10 seconds ago (relative to test start time)
Files.setLastModifiedTime(testFile, FileTime.from(testTime.minusMillis(10000)));
// File should be ready for processing as it was modified more than 5 seconds ago
// Use the absolute path to make sure it's handled correctly
assertTrue(fileMonitor.isFileReadyForProcessing(testFile.toAbsolutePath()));
}
@Test
void testIsFileReadyForProcessing_DirectoryInsteadOfFile() throws IOException {
// Create a test directory
Path testDir = tempDir.resolve("test-directory");
Files.createDirectory(testDir);
// Set modified time to 10 seconds ago
Files.setLastModifiedTime(testDir, FileTime.from(Instant.ofEpochMilli(1000000L)));
// A directory should not be considered ready for processing
boolean isReady = fileMonitor.isFileReadyForProcessing(testDir);
assertFalse(isReady, "A directory should not be considered ready for processing");
}
}
@@ -1,115 +0,0 @@
package stirling.software.common.util;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.IOException;
import java.nio.file.Files;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import stirling.software.common.model.api.converters.HTMLToPdfRequest;
import stirling.software.common.service.SsrfProtectionService;
public class FileToPdfTest {
private CustomHtmlSanitizer customHtmlSanitizer;
@BeforeEach
void setUp() {
SsrfProtectionService mockSsrfProtectionService = mock(SsrfProtectionService.class);
stirling.software.common.model.ApplicationProperties mockApplicationProperties =
mock(stirling.software.common.model.ApplicationProperties.class);
stirling.software.common.model.ApplicationProperties.System mockSystem =
mock(stirling.software.common.model.ApplicationProperties.System.class);
when(mockSsrfProtectionService.isUrlAllowed(org.mockito.ArgumentMatchers.anyString()))
.thenReturn(true);
when(mockApplicationProperties.getSystem()).thenReturn(mockSystem);
when(mockSystem.isDisableSanitize()).thenReturn(false);
customHtmlSanitizer =
new CustomHtmlSanitizer(mockSsrfProtectionService, mockApplicationProperties);
}
/**
* Test the HTML to PDF conversion. This test expects an IOException when an empty HTML input is
* provided.
*/
@Test
public void testConvertHtmlToPdf() {
HTMLToPdfRequest request = new HTMLToPdfRequest();
byte[] fileBytes = new byte[0]; // Sample file bytes (empty input)
String fileName = "test.html"; // Sample file name indicating an HTML file
TempFileManager tempFileManager = mock(TempFileManager.class); // Mock TempFileManager
// Mock the temp file creation to return real temp files
try {
when(tempFileManager.createTempFile(anyString()))
.thenReturn(Files.createTempFile("test", ".pdf").toFile())
.thenReturn(Files.createTempFile("test", ".html").toFile());
} catch (IOException e) {
throw new RuntimeException(e);
}
// Expect an IOException to be thrown due to empty input or invalid weasyprint path
Throwable thrown =
assertThrows(
Exception.class,
() ->
FileToPdf.convertHtmlToPdf(
"/path/",
request,
fileBytes,
fileName,
tempFileManager,
customHtmlSanitizer));
assertNotNull(thrown);
}
/**
* Test sanitizeZipFilename with null or empty input. It should return an empty string in these
* cases.
*/
@Test
public void testSanitizeZipFilename_NullOrEmpty() {
assertEquals("", FileToPdf.sanitizeZipFilename(null));
assertEquals("", FileToPdf.sanitizeZipFilename(" "));
}
/**
* Test sanitizeZipFilename to ensure it removes path traversal sequences. This includes
* removing both forward and backward slash sequences.
*/
@Test
public void testSanitizeZipFilename_RemovesTraversalSequences() {
String input = "../some/../path/..\\to\\file.txt";
String expected = "some/path/to/file.txt";
// Expect that the method replaces backslashes with forward slashes
// and removes path traversal sequences
assertEquals(expected, FileToPdf.sanitizeZipFilename(input));
}
/** Test sanitizeZipFilename to ensure that it removes leading drive letters and slashes. */
@Test
public void testSanitizeZipFilename_RemovesLeadingDriveAndSlashes() {
String input = "C:\\folder\\file.txt";
String expected = "folder/file.txt";
assertEquals(expected, FileToPdf.sanitizeZipFilename(input));
input = "/folder/file.txt";
expected = "folder/file.txt";
assertEquals(expected, FileToPdf.sanitizeZipFilename(input));
}
/** Test sanitizeZipFilename to verify that safe filenames remain unchanged. */
@Test
public void testSanitizeZipFilename_NoChangeForSafeNames() {
String input = "folder/subfolder/file.txt";
assertEquals(input, FileToPdf.sanitizeZipFilename(input));
}
}
@@ -1,80 +0,0 @@
package stirling.software.common.util;
import static org.junit.jupiter.api.Assertions.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import org.junit.jupiter.api.Test;
public class ImageProcessingUtilsTest {
private static void fillImageWithColor(BufferedImage image) {
for (int y = 0; y < image.getHeight(); y++) {
for (int x = 0; x < image.getWidth(); x++) {
image.setRGB(x, y, Color.RED.getRGB());
}
}
}
@Test
void testConvertColorTypeToGreyscale() {
BufferedImage sourceImage = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB);
fillImageWithColor(sourceImage);
BufferedImage convertedImage =
ImageProcessingUtils.convertColorType(sourceImage, "greyscale");
assertNotNull(convertedImage);
assertEquals(BufferedImage.TYPE_BYTE_GRAY, convertedImage.getType());
assertEquals(sourceImage.getWidth(), convertedImage.getWidth());
assertEquals(sourceImage.getHeight(), convertedImage.getHeight());
// Check if a pixel is correctly converted to greyscale
Color grey = new Color(convertedImage.getRGB(0, 0));
assertEquals(grey.getRed(), grey.getGreen());
assertEquals(grey.getGreen(), grey.getBlue());
}
@Test
void testConvertColorTypeToBlackWhite() {
BufferedImage sourceImage = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB);
fillImageWithColor(sourceImage);
BufferedImage convertedImage =
ImageProcessingUtils.convertColorType(sourceImage, "blackwhite");
assertNotNull(convertedImage);
assertEquals(BufferedImage.TYPE_BYTE_BINARY, convertedImage.getType());
assertEquals(sourceImage.getWidth(), convertedImage.getWidth());
assertEquals(sourceImage.getHeight(), convertedImage.getHeight());
// Check if a pixel is converted correctly (binary image will be either black or white)
int rgb = convertedImage.getRGB(0, 0);
assertTrue(rgb == Color.BLACK.getRGB() || rgb == Color.WHITE.getRGB());
}
@Test
void testConvertColorTypeToFullColor() {
BufferedImage sourceImage = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB);
fillImageWithColor(sourceImage);
BufferedImage convertedImage =
ImageProcessingUtils.convertColorType(sourceImage, "fullcolor");
assertNotNull(convertedImage);
assertEquals(sourceImage, convertedImage);
}
@Test
void testConvertColorTypeInvalid() {
BufferedImage sourceImage = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB);
fillImageWithColor(sourceImage);
BufferedImage convertedImage =
ImageProcessingUtils.convertColorType(sourceImage, "invalidtype");
assertNotNull(convertedImage);
assertEquals(sourceImage, convertedImage);
}
}
@@ -1,736 +0,0 @@
package stirling.software.common.util;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.when;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.RenderedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import javax.imageio.ImageIO;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.PDPageContentStream.AppendMode;
import org.apache.pdfbox.pdmodel.PDResources;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.font.PDType1Font;
import org.apache.pdfbox.pdmodel.font.Standard14Fonts;
import org.apache.pdfbox.pdmodel.graphics.PDXObject;
import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject;
import org.apache.pdfbox.pdmodel.graphics.image.LosslessFactory;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
import org.apache.pdfbox.rendering.ImageType;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import org.springframework.mock.web.MockMultipartFile;
import stirling.software.common.model.ApplicationProperties;
import stirling.software.common.service.CustomPDFDocumentFactory;
import stirling.software.common.service.PdfMetadataService;
public class PdfUtilsTest {
@Test
void testTextToPageSize() {
assertEquals(PDRectangle.A0, PdfUtils.textToPageSize("A0"));
assertEquals(PDRectangle.A1, PdfUtils.textToPageSize("A1"));
assertEquals(PDRectangle.A2, PdfUtils.textToPageSize("A2"));
assertEquals(PDRectangle.A3, PdfUtils.textToPageSize("A3"));
assertEquals(PDRectangle.A4, PdfUtils.textToPageSize("A4"));
assertEquals(PDRectangle.A5, PdfUtils.textToPageSize("A5"));
assertEquals(PDRectangle.A6, PdfUtils.textToPageSize("A6"));
assertEquals(PDRectangle.LETTER, PdfUtils.textToPageSize("LETTER"));
assertEquals(PDRectangle.LEGAL, PdfUtils.textToPageSize("LEGAL"));
assertThrows(IllegalArgumentException.class, () -> PdfUtils.textToPageSize("INVALID"));
}
@Test
void testGetAllImages() throws Exception {
// Root resources
PDResources root = mock(PDResources.class);
COSName im1 = COSName.getPDFName("Im1");
COSName form1 = COSName.getPDFName("Form1");
COSName other1 = COSName.getPDFName("Other1");
when(root.getXObjectNames()).thenReturn(Arrays.asList(im1, form1, other1));
// Direct image at root
PDImageXObject imgXObj1 = mock(PDImageXObject.class);
BufferedImage img1 = new BufferedImage(2, 2, BufferedImage.TYPE_INT_ARGB);
when(imgXObj1.getImage()).thenReturn(img1);
when(root.getXObject(im1)).thenReturn(imgXObj1);
// "Other" XObject that should be ignored
PDXObject otherXObj = mock(PDXObject.class);
when(root.getXObject(other1)).thenReturn(otherXObj);
// Form XObject with its own resources
PDFormXObject formXObj = mock(PDFormXObject.class);
PDResources formRes = mock(PDResources.class);
when(formXObj.getResources()).thenReturn(formRes);
when(root.getXObject(form1)).thenReturn(formXObj);
// Inside the form: one image and a nested form
COSName im2 = COSName.getPDFName("Im2");
COSName nestedForm = COSName.getPDFName("NestedForm");
when(formRes.getXObjectNames()).thenReturn(Arrays.asList(im2, nestedForm));
PDImageXObject imgXObj2 = mock(PDImageXObject.class);
BufferedImage img2 = new BufferedImage(3, 3, BufferedImage.TYPE_INT_RGB);
when(imgXObj2.getImage()).thenReturn(img2);
when(formRes.getXObject(im2)).thenReturn(imgXObj2);
PDFormXObject nestedFormXObj = mock(PDFormXObject.class);
PDResources nestedRes = mock(PDResources.class);
when(nestedFormXObj.getResources()).thenReturn(nestedRes);
when(formRes.getXObject(nestedForm)).thenReturn(nestedFormXObj);
// Deep nest: another image
COSName im3 = COSName.getPDFName("Im3");
when(nestedRes.getXObjectNames()).thenReturn(List.of(im3));
PDImageXObject imgXObj3 = mock(PDImageXObject.class);
BufferedImage img3 = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
when(imgXObj3.getImage()).thenReturn(img3);
when(nestedRes.getXObject(im3)).thenReturn(imgXObj3);
// Act
List<RenderedImage> result = PdfUtils.getAllImages(root);
// Assert
assertEquals(
3, result.size(), "It should find exactly 3 images (root + form + nested form).");
assertTrue(
result.containsAll(List.of(img1, img2, img3)),
"All expected images must be present.");
}
@Test
void testPageCountComparators() throws Exception {
PDDocument doc1 = new PDDocument();
doc1.addPage(new PDPage());
doc1.addPage(new PDPage());
doc1.addPage(new PDPage());
assertTrue(PdfUtils.pageCount(doc1, 2, "greater"));
PDDocument doc2 = new PDDocument();
doc2.addPage(new PDPage());
doc2.addPage(new PDPage());
doc2.addPage(new PDPage());
assertTrue(PdfUtils.pageCount(doc2, 3, "equal"));
PDDocument doc3 = new PDDocument();
doc3.addPage(new PDPage());
doc3.addPage(new PDPage());
assertTrue(PdfUtils.pageCount(doc3, 5, "less"));
PDDocument doc4 = new PDDocument();
doc4.addPage(new PDPage());
assertThrows(IllegalArgumentException.class, () -> PdfUtils.pageCount(doc4, 1, "bad"));
}
@Test
void testPageSize() throws Exception {
PDDocument doc = new PDDocument();
PDPage page = new PDPage(PDRectangle.A4);
doc.addPage(page);
PDRectangle rect = page.getMediaBox();
String expected = rect.getWidth() + "x" + rect.getHeight();
assertTrue(PdfUtils.pageSize(doc, expected));
}
@Test
void testOverlayImage() throws Exception {
PDDocument doc = new PDDocument();
doc.addPage(new PDPage(PDRectangle.A4));
ByteArrayOutputStream pdfOut = new ByteArrayOutputStream();
doc.save(pdfOut);
doc.close();
BufferedImage image = new BufferedImage(10, 10, BufferedImage.TYPE_INT_RGB);
Graphics2D g = image.createGraphics();
g.setColor(Color.RED);
g.fillRect(0, 0, 10, 10);
g.dispose();
ByteArrayOutputStream imgOut = new ByteArrayOutputStream();
ImageIO.write(image, "png", imgOut);
PdfMetadataService meta =
new PdfMetadataService(new ApplicationProperties(), "label", false, null);
CustomPDFDocumentFactory factory = new CustomPDFDocumentFactory(meta);
byte[] result =
PdfUtils.overlayImage(
factory, pdfOut.toByteArray(), imgOut.toByteArray(), 0, 0, false);
try (PDDocument resultDoc = factory.load(result)) {
assertEquals(1, resultDoc.getNumberOfPages());
}
}
// ===============================================================
// Additional tests (added without modifying existing ones)
// ===============================================================
/* Helper: create a colored test image */
private static BufferedImage createImage(int w, int h, Color color) {
BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
Graphics2D g = img.createGraphics();
g.setColor(color);
g.fillRect(0, 0, w, h);
g.dispose();
return img;
}
/* Helper: create a factory like in existing tests */
private static CustomPDFDocumentFactory factory() {
PdfMetadataService meta =
new PdfMetadataService(new ApplicationProperties(), "label", false, null);
return new CustomPDFDocumentFactory(meta);
}
@Test
@DisplayName("convertPdfToPdfImage: creates image-PDF with same page count")
void convertPdfToPdfImage_shouldCreateImagePdfWithSamePageCount() throws IOException {
try (PDDocument doc = new PDDocument()) {
PDPage p1 = new PDPage(PDRectangle.A4);
doc.addPage(p1);
try (PDPageContentStream cs =
new PDPageContentStream(doc, p1, AppendMode.APPEND, true, true)) {
cs.beginText();
cs.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA), 12);
cs.newLineAtOffset(50, 750);
cs.showText("Hello PDF");
cs.endText();
}
PDPage p2 = new PDPage(PDRectangle.A4);
doc.addPage(p2);
PDDocument out = PdfUtils.convertPdfToPdfImage(doc);
assertNotNull(out);
assertEquals(2, out.getNumberOfPages(), "Page count should be preserved");
out.close();
}
}
@Test
@DisplayName("imageToPdf: PNG -> single-page PDF (static ImageProcessingUtils mocked)")
void imageToPdf_shouldCreatePdfFromPng() throws Exception {
BufferedImage img = createImage(320, 200, Color.RED);
ByteArrayOutputStream pngOut = new ByteArrayOutputStream();
ImageIO.write(img, "png", pngOut);
MockMultipartFile file =
new MockMultipartFile("files", "test.png", "image/png", pngOut.toByteArray());
try (MockedStatic<ImageProcessingUtils> mocked = mockStatic(ImageProcessingUtils.class)) {
// Assume: loadImageWithExifOrientation/convertColorType exist static mock
mocked.when(() -> ImageProcessingUtils.loadImageWithExifOrientation(any()))
.thenReturn(img);
mocked.when(
() ->
ImageProcessingUtils.convertColorType(
any(BufferedImage.class), anyString()))
.thenAnswer(inv -> inv.getArgument(0, BufferedImage.class));
byte[] pdfBytes =
PdfUtils.imageToPdf(
new MockMultipartFile[] {file},
"maintainAspectRatio",
true,
"RGB",
factory());
try (PDDocument result = factory().load(pdfBytes)) {
assertEquals(1, result.getNumberOfPages());
}
}
}
@Test
@DisplayName("imageToPdf: JPEG -> single-page PDF (JPEGFactory path)")
void imageToPdf_shouldCreatePdfFromJpeg_UsingJpegFactory() throws Exception {
BufferedImage img = createImage(640, 360, Color.BLUE);
ByteArrayOutputStream jpgOut = new ByteArrayOutputStream();
ImageIO.write(img, "jpg", jpgOut);
MockMultipartFile file =
new MockMultipartFile("files", "photo.jpg", "image/jpeg", jpgOut.toByteArray());
try (MockedStatic<ImageProcessingUtils> mocked = mockStatic(ImageProcessingUtils.class)) {
mocked.when(() -> ImageProcessingUtils.loadImageWithExifOrientation(any()))
.thenReturn(img);
mocked.when(
() ->
ImageProcessingUtils.convertColorType(
any(BufferedImage.class), anyString()))
.thenAnswer(inv -> inv.getArgument(0, BufferedImage.class));
byte[] pdfBytes =
PdfUtils.imageToPdf(
new MockMultipartFile[] {file}, "fillPage", false, "RGB", factory());
try (PDDocument result = factory().load(pdfBytes)) {
assertEquals(1, result.getNumberOfPages());
}
}
}
@Test
@DisplayName("addImageToDocument: fitDocumentToImage -> page size = image size")
void addImageToDocument_shouldUseImageSizeForPage_whenFitDocumentToImage() throws IOException {
try (PDDocument doc = new PDDocument()) {
BufferedImage img = createImage(300, 500, Color.GREEN);
PDImageXObject ximg = LosslessFactory.createFromImage(doc, img);
PdfUtils.addImageToDocument(doc, ximg, "fitDocumentToImage", false);
assertEquals(1, doc.getNumberOfPages());
PDRectangle box = doc.getPage(0).getMediaBox();
assertEquals(300, (int) box.getWidth());
assertEquals(500, (int) box.getHeight());
}
}
@Test
@DisplayName("addImageToDocument: autoRotate rotates A4 for landscape image")
void addImageToDocument_shouldRotateA4_whenAutoRotateAndLandscape() throws IOException {
try (PDDocument doc = new PDDocument()) {
BufferedImage img = createImage(800, 400, Color.ORANGE); // Landscape
PDImageXObject ximg = LosslessFactory.createFromImage(doc, img);
PdfUtils.addImageToDocument(doc, ximg, "maintainAspectRatio", true);
assertEquals(1, doc.getNumberOfPages());
PDRectangle box = doc.getPage(0).getMediaBox();
assertTrue(
box.getWidth() > box.getHeight(),
"A4 should be landscape when auto-rotate + landscape");
}
}
@Test
@DisplayName("addImageToDocument: fillPage runs without errors")
void addImageToDocument_fillPage_executes() throws IOException {
try (PDDocument doc = new PDDocument()) {
BufferedImage img = createImage(200, 200, Color.MAGENTA);
PDImageXObject ximg = LosslessFactory.createFromImage(doc, img);
PdfUtils.addImageToDocument(doc, ximg, "fillPage", false);
assertEquals(1, doc.getNumberOfPages());
}
}
@Test
@DisplayName("overlayImage: everyPage=true overlays all pages")
void overlayImage_shouldOverlayAllPages_whenEveryPageTrue() throws IOException {
CustomPDFDocumentFactory factory = factory();
// Create PDF with 2 pages
byte[] basePdf;
try (PDDocument doc = factory.createNewDocument()) {
doc.addPage(new PDPage(PDRectangle.A4));
doc.addPage(new PDPage(PDRectangle.A4));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
doc.save(baos);
basePdf = baos.toByteArray();
}
// Create image bytes
BufferedImage img = createImage(50, 50, Color.BLACK);
ByteArrayOutputStream pngOut = new ByteArrayOutputStream();
ImageIO.write(img, "png", pngOut);
byte[] result = PdfUtils.overlayImage(factory, basePdf, pngOut.toByteArray(), 10, 10, true);
try (PDDocument out = factory.load(result)) {
assertEquals(2, out.getNumberOfPages(), "Page count remains identical");
}
}
/* Helper function: document with text on page1/page2 */
private static PDDocument createDocWithText(String p1, String p2) throws IOException {
PDDocument doc = new PDDocument();
PDPage page1 = new PDPage(PDRectangle.A4);
doc.addPage(page1);
try (PDPageContentStream cs =
new PDPageContentStream(doc, page1, AppendMode.APPEND, true, true)) {
cs.beginText();
cs.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA), 12);
cs.newLineAtOffset(50, 750);
cs.showText(p1);
cs.endText();
}
PDPage page2 = new PDPage(PDRectangle.A4);
doc.addPage(page2);
try (PDPageContentStream cs =
new PDPageContentStream(doc, page2, AppendMode.APPEND, true, true)) {
cs.beginText();
cs.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA), 12);
cs.newLineAtOffset(50, 750);
cs.showText(p2);
cs.endText();
}
return doc;
}
@Test
@DisplayName("containsTextInFile: pagesToCheck='all' finds text")
void containsTextInFile_allPages_true() throws IOException {
try (PDDocument doc = createDocWithText("alpha", "beta")) {
assertTrue(PdfUtils.containsTextInFile(doc, "beta", "all"));
}
}
@Test
@DisplayName("containsTextInFile: single page '2' finds text")
void containsTextInFile_singlePage_two_true() throws IOException {
try (PDDocument doc = createDocWithText("alpha", "beta")) {
assertTrue(PdfUtils.containsTextInFile(doc, "beta", "2"));
}
}
@Test
@DisplayName("containsTextInFile: range '1-1' finds text on page 1")
void containsTextInFile_range_oneToOne_true() throws IOException {
try (PDDocument doc = createDocWithText("findme", "other")) {
assertTrue(PdfUtils.containsTextInFile(doc, "findme", "1-1"));
}
}
@Test
@DisplayName("containsTextInFile: list '1,2' finds text (whitespace robust)")
void containsTextInFile_list_pages_true() throws IOException {
try (PDDocument doc = createDocWithText("foo", "bar")) {
assertTrue(PdfUtils.containsTextInFile(doc, "bar", " 1 , 2 "));
}
}
@Test
@DisplayName("containsTextInFile: text not present -> false")
void containsTextInFile_textNotPresent_false() throws IOException {
try (PDDocument doc = createDocWithText("xxx", "yyy")) {
assertFalse(PdfUtils.containsTextInFile(doc, "zzz", "all"));
}
}
@Test
@DisplayName("pageSize: different size returns false")
void pageSize_shouldReturnFalse_whenSizeDoesNotMatch() throws IOException {
try (PDDocument doc = new PDDocument()) {
doc.addPage(new PDPage(PDRectangle.A4));
assertFalse(PdfUtils.pageSize(doc, "600x842"));
}
}
// ===================== New: convertFromPdf coverage =====================
@Test
@DisplayName("convertFromPdf: singleImage=true creates combined PNG file (readable)")
void convertFromPdf_singleImagePng_combinedReadable() throws Exception {
// Create two-page PDF
byte[] pdfBytes;
PdfMetadataService meta =
new PdfMetadataService(new ApplicationProperties(), "label", false, null);
CustomPDFDocumentFactory factory = new CustomPDFDocumentFactory(meta);
try (PDDocument doc = new PDDocument()) {
doc.addPage(new PDPage(PDRectangle.A4));
doc.addPage(new PDPage(PDRectangle.A4));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
doc.save(baos);
pdfBytes = baos.toByteArray();
}
byte[] imageBytes =
PdfUtils.convertFromPdf(
factory, pdfBytes, "png", ImageType.RGB, true, 72, "test.pdf", false);
// Should be readable as a single combined PNG image
BufferedImage img = ImageIO.read(new java.io.ByteArrayInputStream(imageBytes));
assertNotNull(img, "PNG should be readable");
assertTrue(img.getWidth() > 0 && img.getHeight() > 0, "Image dimensions > 0");
}
@Test
@DisplayName(
"convertFromPdf: singleImage=false returns ZIP with PNG entries (first image readable)")
void convertFromPdf_multiImagePng_firstReadable() throws Exception {
// Create two-page PDF
byte[] pdfBytes;
PdfMetadataService meta =
new PdfMetadataService(new ApplicationProperties(), "label", false, null);
CustomPDFDocumentFactory factory = new CustomPDFDocumentFactory(meta);
try (PDDocument doc = new PDDocument()) {
doc.addPage(new PDPage(PDRectangle.A4));
doc.addPage(new PDPage(PDRectangle.A4));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
doc.save(baos);
pdfBytes = baos.toByteArray();
}
// Act: singleImage=false -> ZIP with separate images
byte[] zipBytes =
PdfUtils.convertFromPdf(
factory, pdfBytes, "png", ImageType.RGB, false, 72, "test.pdf", false);
// Assert: open ZIP, read first entry as PNG
try (ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(zipBytes))) {
ZipEntry entry = zis.getNextEntry();
assertNotNull(entry, "ZIP should contain at least one entry");
ByteArrayOutputStream imgOut = new ByteArrayOutputStream();
zis.transferTo(imgOut);
BufferedImage first = ImageIO.read(new ByteArrayInputStream(imgOut.toByteArray()));
assertNotNull(first, "First PNG entry should be readable");
assertTrue(first.getWidth() > 0 && first.getHeight() > 0, "Image dimensions > 0");
}
}
@Test
@DisplayName("hasText: detects phrase on selected pages ('1', '2', 'all')")
void hasText_shouldDetectPhrase_onSelectedPages() throws Exception {
// Arrange: PDF with 2 pages and text
try (PDDocument doc = new PDDocument()) {
PDPage p1 = new PDPage(PDRectangle.A4);
PDPage p2 = new PDPage(PDRectangle.A4);
doc.addPage(p1);
doc.addPage(p2);
try (PDPageContentStream cs =
new PDPageContentStream(doc, p1, AppendMode.APPEND, true, true)) {
cs.beginText();
cs.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA), 12);
cs.newLineAtOffset(50, 750);
cs.showText("alpha on page 1");
cs.endText();
}
try (PDPageContentStream cs =
new PDPageContentStream(doc, p2, AppendMode.APPEND, true, true)) {
cs.beginText();
cs.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA), 12);
cs.newLineAtOffset(50, 750);
cs.showText("beta on page 2");
cs.endText();
}
assertTrue(PdfUtils.hasText(doc, "1", "alpha"), "Page 1 should contain 'alpha'");
}
// For further checks, create new doc with identical content
try (PDDocument doc = new PDDocument()) {
PDPage p1 = new PDPage(PDRectangle.A4);
PDPage p2 = new PDPage(PDRectangle.A4);
doc.addPage(p1);
doc.addPage(p2);
try (PDPageContentStream cs =
new PDPageContentStream(doc, p1, AppendMode.APPEND, true, true)) {
cs.beginText();
cs.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA), 12);
cs.newLineAtOffset(50, 750);
cs.showText("alpha on page 1");
cs.endText();
}
try (PDPageContentStream cs =
new PDPageContentStream(doc, p2, AppendMode.APPEND, true, true)) {
cs.beginText();
cs.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA), 12);
cs.newLineAtOffset(50, 750);
cs.showText("beta on page 2");
cs.endText();
}
assertTrue(PdfUtils.hasText(doc, "2", "beta"), "Page 2 should contain 'beta'");
}
// Third doc for 'all'
try (PDDocument doc = new PDDocument()) {
PDPage p1 = new PDPage(PDRectangle.A4);
PDPage p2 = new PDPage(PDRectangle.A4);
doc.addPage(p1);
doc.addPage(p2);
try (PDPageContentStream cs =
new PDPageContentStream(doc, p1, AppendMode.APPEND, true, true)) {
cs.beginText();
cs.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA), 12);
cs.newLineAtOffset(50, 750);
cs.showText("gamma");
cs.endText();
}
assertTrue(PdfUtils.hasText(doc, "all", "gamma"), "'all' should find text on page 1");
}
}
@Test
@DisplayName("hasTextOnPage: true if page contains phrase, else false")
void hasTextOnPage_shouldReturnTrueOnlyForPagesWithPhrase() throws Exception {
try (PDDocument doc = new PDDocument()) {
PDPage p1 = new PDPage(PDRectangle.A4);
PDPage p2 = new PDPage(PDRectangle.A4);
doc.addPage(p1);
doc.addPage(p2);
try (PDPageContentStream cs =
new PDPageContentStream(doc, p1, AppendMode.APPEND, true, true)) {
cs.beginText();
cs.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA), 12);
cs.newLineAtOffset(50, 750);
cs.showText("needle");
cs.endText();
}
assertTrue(PdfUtils.hasTextOnPage(p1, "needle"));
assertTrue(!PdfUtils.hasTextOnPage(p2, "needle"));
}
}
@Test
@DisplayName("hasImages: detects images on selected pages and 'all'")
void hasImages_shouldDetectImages_onSelectedPages() throws Exception {
// Case 1: Page 1 without image (but resources set) -> false
try (PDDocument doc = new PDDocument()) {
PDPage p1 = new PDPage(PDRectangle.A4);
PDPage p2 = new PDPage(PDRectangle.A4);
p1.setResources(new PDResources());
p2.setResources(new PDResources());
doc.addPage(p1);
doc.addPage(p2);
// Image only on page 2
BufferedImage bi = new BufferedImage(20, 20, BufferedImage.TYPE_INT_RGB);
Graphics2D g = bi.createGraphics();
g.setColor(Color.GREEN);
g.fillRect(0, 0, 20, 20);
g.dispose();
PDImageXObject ximg = LosslessFactory.createFromImage(doc, bi);
try (PDPageContentStream cs =
new PDPageContentStream(doc, p2, AppendMode.APPEND, true, true)) {
cs.drawImage(ximg, 50, 700, 20, 20);
}
assertTrue(!PdfUtils.hasImages(doc, "1"), "Page 1 should have no image");
}
// Case 2: Page 2 with image -> true
try (PDDocument doc = new PDDocument()) {
PDPage p1 = new PDPage(PDRectangle.A4);
PDPage p2 = new PDPage(PDRectangle.A4);
p1.setResources(new PDResources());
p2.setResources(new PDResources());
doc.addPage(p1);
doc.addPage(p2);
BufferedImage bi = new BufferedImage(20, 20, BufferedImage.TYPE_INT_RGB);
Graphics2D g = bi.createGraphics();
g.setColor(Color.BLUE);
g.fillRect(0, 0, 20, 20);
g.dispose();
PDImageXObject ximg = LosslessFactory.createFromImage(doc, bi);
try (PDPageContentStream cs =
new PDPageContentStream(doc, p2, AppendMode.APPEND, true, true)) {
cs.drawImage(ximg, 50, 700, 20, 20);
}
assertTrue(PdfUtils.hasImages(doc, "2"), "Page 2 should have an image");
}
// Case 3: 'all' detects image
try (PDDocument doc = new PDDocument()) {
PDPage p = new PDPage(PDRectangle.A4);
p.setResources(new PDResources());
doc.addPage(p);
BufferedImage bi = new BufferedImage(10, 10, BufferedImage.TYPE_INT_RGB);
PDImageXObject ximg = LosslessFactory.createFromImage(doc, bi);
try (PDPageContentStream cs =
new PDPageContentStream(doc, p, AppendMode.APPEND, true, true)) {
cs.drawImage(ximg, 20, 730, 10, 10);
}
assertTrue(PdfUtils.hasImages(doc, "all"), "'all' should detect the image");
}
}
@Test
@DisplayName("hasImagesOnPage: true if page contains an image, else false")
void hasImagesOnPage_shouldReturnTrueOnlyForPagesWithImage() throws Exception {
try (PDDocument doc = new PDDocument()) {
PDPage p1 = new PDPage(PDRectangle.A4);
PDPage p2 = new PDPage(PDRectangle.A4);
p1.setResources(new PDResources());
p2.setResources(new PDResources());
doc.addPage(p1);
doc.addPage(p2);
BufferedImage bi = new BufferedImage(12, 12, BufferedImage.TYPE_INT_RGB);
Graphics2D g = bi.createGraphics();
g.setColor(Color.RED);
g.fillRect(0, 0, 12, 12);
g.dispose();
PDImageXObject ximg = LosslessFactory.createFromImage(doc, bi);
try (PDPageContentStream cs =
new PDPageContentStream(doc, p1, AppendMode.APPEND, true, true)) {
cs.drawImage(ximg, 40, 720, 12, 12);
}
assertTrue(PdfUtils.hasImagesOnPage(p1));
assertTrue(!PdfUtils.hasImagesOnPage(p2));
}
}
@Test
@DisplayName("convertFromPdf: singleImage=true with JPG -> no alpha, white background")
void convertFromPdf_singleImageJpg_noAlphaWhiteBackground() throws Exception {
// small 1-page PDF
byte[] pdfBytes;
PdfMetadataService meta =
new PdfMetadataService(new ApplicationProperties(), "label", false, null);
CustomPDFDocumentFactory factory = new CustomPDFDocumentFactory(meta);
try (PDDocument doc = new PDDocument()) {
PDPage p = new PDPage(PDRectangle.A4);
doc.addPage(p);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
doc.save(baos);
pdfBytes = baos.toByteArray();
}
byte[] jpgBytes =
PdfUtils.convertFromPdf(
factory, pdfBytes, "jpg", ImageType.RGB, true, 72, "sample.pdf", false);
BufferedImage img = ImageIO.read(new ByteArrayInputStream(jpgBytes));
assertNotNull(img, "JPG should be readable");
ColorModel cm = img.getColorModel();
assertFalse(cm.hasAlpha(), "JPG output should have no alpha channel");
// JPG background should be white (approximate check)
int rgb = img.getRGB(img.getWidth() / 2, img.getHeight() / 2) & 0x00FFFFFF;
assertEquals(0xFFFFFF, rgb, "Background pixel should be white");
}
}
@@ -1,86 +0,0 @@
package stirling.software.common.util;
import static org.junit.jupiter.api.Assertions.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
public class ProcessExecutorTest {
private ProcessExecutor processExecutor;
@BeforeEach
public void setUp() {
// Initialize the ProcessExecutor instance
processExecutor = ProcessExecutor.getInstance(ProcessExecutor.Processes.LIBRE_OFFICE);
}
@Test
public void testRunCommandWithOutputHandling() throws IOException, InterruptedException {
// Mock the command to execute
List<String> command = new ArrayList<>();
command.add("java");
command.add("-version");
// Execute the command
ProcessExecutor.ProcessExecutorResult result =
processExecutor.runCommandWithOutputHandling(command);
// Check the exit code and output messages
assertEquals(0, result.getRc());
assertNotNull(result.getMessages()); // Check if messages are not null
}
@Test
public void testRunCommandWithOutputHandling_Error() {
// Test with a command that will fail to execute (non-existent command)
List<String> command = new ArrayList<>();
command.add("nonexistent-command-that-does-not-exist");
// Execute the command and expect an IOException (command not found)
assertThrows(
IOException.class, () -> processExecutor.runCommandWithOutputHandling(command));
}
@Test
public void testRunCommandWithOutputHandling_PathTraversal() {
// Test that path traversal is blocked
List<String> command = new ArrayList<>();
command.add("../../../etc/passwd");
// Execute the command and expect an IllegalArgumentException
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
() -> processExecutor.runCommandWithOutputHandling(command));
// Check the exception message
String errorMessage = thrown.getMessage();
assertTrue(
errorMessage.contains("path traversal"),
"Unexpected error message: " + errorMessage);
}
@Test
public void testRunCommandWithOutputHandling_NullByte() {
// Test that null bytes are blocked
List<String> command = new ArrayList<>();
command.add("test\0command");
// Execute the command and expect an IllegalArgumentException
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
() -> processExecutor.runCommandWithOutputHandling(command));
// Check the exception message
String errorMessage = thrown.getMessage();
assertTrue(
errorMessage.contains("invalid characters"),
"Unexpected error message: " + errorMessage);
}
}
@@ -1,70 +0,0 @@
package stirling.software.common.util;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.Arrays;
import java.util.List;
import org.junit.jupiter.api.Test;
public class PropertyConfigsTest {
@Test
public void testGetBooleanValue_WithKeys() {
// Define keys and default value
List<String> keys = Arrays.asList("test.key1", "test.key2", "test.key3");
boolean defaultValue = false;
// Set property for one of the keys
System.setProperty("test.key2", "true");
// Call the method under test
boolean result = PropertyConfigs.getBooleanValue(keys, defaultValue);
// Verify the result
assertTrue(result);
}
@Test
public void testGetStringValue_WithKeys() {
// Define keys and default value
List<String> keys = Arrays.asList("test.key1", "test.key2", "test.key3");
String defaultValue = "default";
// Set property for one of the keys
System.setProperty("test.key2", "value");
// Call the method under test
String result = PropertyConfigs.getStringValue(keys, defaultValue);
// Verify the result
assertEquals("value", result);
}
@Test
public void testGetBooleanValue_WithKey() {
// Define key and default value
String key = "test.key";
boolean defaultValue = true;
// Call the method under test
boolean result = PropertyConfigs.getBooleanValue(key, defaultValue);
// Verify the result
assertTrue(result);
}
@Test
public void testGetStringValue_WithKey() {
// Define key and default value
String key = "test.key";
String defaultValue = "default";
// Call the method under test
String result = PropertyConfigs.getStringValue(key, defaultValue);
// Verify the result
assertEquals("default", result);
}
}
@@ -1,340 +0,0 @@
package stirling.software.common.util;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
public class RequestUriUtilsTest {
@Test
void testIsStaticResource() {
// Test static resources without context path
assertTrue(
RequestUriUtils.isStaticResource("/css/styles.css"), "CSS files should be static");
assertTrue(RequestUriUtils.isStaticResource("/js/script.js"), "JS files should be static");
assertTrue(
RequestUriUtils.isStaticResource("/images/logo.png"),
"Image files should be static");
assertTrue(
RequestUriUtils.isStaticResource("/public/index.html"),
"Public files should be static");
assertTrue(
RequestUriUtils.isStaticResource("/pdfjs/pdf.worker.js"),
"PDF.js files should be static");
assertTrue(
RequestUriUtils.isStaticResource("/pdfium/pdfium.wasm"),
"PDFium wasm should be static");
assertTrue(
RequestUriUtils.isStaticResource("/api/v1/info/status"),
"API status should be static");
assertTrue(
RequestUriUtils.isStaticResource("/some-path/icon.svg"),
"SVG files should be static");
assertTrue(RequestUriUtils.isStaticResource("/login"), "Login page should be static");
assertTrue(RequestUriUtils.isStaticResource("/error"), "Error page should be static");
// Test non-static resources
assertFalse(
RequestUriUtils.isStaticResource("/api/v1/users"),
"API users should not be static");
assertFalse(
RequestUriUtils.isStaticResource("/api/v1/orders"),
"API orders should not be static");
assertFalse(RequestUriUtils.isStaticResource("/"), "Root path should not be static");
assertFalse(
RequestUriUtils.isStaticResource("/register"),
"Register page should not be static");
assertFalse(
RequestUriUtils.isStaticResource("/api/v1/products"),
"API products should not be static");
}
@Test
void testIsFrontendRoute() {
assertTrue(
RequestUriUtils.isFrontendRoute("", "/"), "Root path should be a frontend route");
assertTrue(
RequestUriUtils.isFrontendRoute("", "/app/dashboard"),
"React routes without extensions should be frontend routes");
assertFalse(
RequestUriUtils.isFrontendRoute("", "/api/v1/users"),
"API routes should not be frontend routes");
assertFalse(
RequestUriUtils.isFrontendRoute("", "/register"),
"Register should not be treated as a frontend route");
assertFalse(
RequestUriUtils.isFrontendRoute("", "/pipeline/jobs"),
"Pipeline should not be treated as a frontend route");
assertFalse(
RequestUriUtils.isFrontendRoute("", "/files/download"),
"Files path should not be treated as a frontend route");
}
@Test
void testIsStaticResourceWithContextPath() {
String contextPath = "/myapp";
// Test static resources with context path
assertTrue(
RequestUriUtils.isStaticResource(contextPath, contextPath + "/css/styles.css"),
"CSS with context path should be static");
assertTrue(
RequestUriUtils.isStaticResource(contextPath, contextPath + "/js/script.js"),
"JS with context path should be static");
assertTrue(
RequestUriUtils.isStaticResource(contextPath, contextPath + "/images/logo.png"),
"Images with context path should be static");
assertTrue(
RequestUriUtils.isStaticResource(contextPath, contextPath + "/login"),
"Login with context path should be static");
// Test non-static resources with context path
assertFalse(
RequestUriUtils.isStaticResource(contextPath, contextPath + "/api/v1/users"),
"API users with context path should not be static");
assertFalse(
RequestUriUtils.isStaticResource(contextPath, "/"),
"Root path with context path should not be static");
}
@ParameterizedTest
@ValueSource(
strings = {
"robots.txt",
"/favicon.ico",
"/icon.svg",
"/image.png",
"/locales/en/translation.toml",
"/site.webmanifest",
"/app/logo.svg",
"/downloads/document.png",
"/assets/brand.ico",
"/any/path/with/image.svg",
"/deep/nested/folder/icon.png",
"/pdfium/pdfium.wasm"
})
void testIsStaticResourceWithFileExtensions(String path) {
assertTrue(
RequestUriUtils.isStaticResource(path),
"Files with specific extensions should be static regardless of path");
}
@Test
void testIsTrackableResource() {
// Test non-trackable resources (returns false)
assertFalse(
RequestUriUtils.isTrackableResource("/js/script.js"),
"JS files should not be trackable");
assertFalse(
RequestUriUtils.isTrackableResource("/v1/api-docs"),
"API docs should not be trackable");
assertFalse(
RequestUriUtils.isTrackableResource("robots.txt"),
"robots.txt should not be trackable");
assertFalse(
RequestUriUtils.isTrackableResource("/images/logo.png"),
"Images should not be trackable");
assertFalse(
RequestUriUtils.isTrackableResource("/styles.css"),
"CSS files should not be trackable");
assertFalse(
RequestUriUtils.isTrackableResource("/script.js.map"),
"Map files should not be trackable");
assertFalse(
RequestUriUtils.isTrackableResource("/icon.svg"),
"SVG files should not be trackable");
assertFalse(
RequestUriUtils.isTrackableResource("/popularity.txt"),
"Popularity file should not be trackable");
assertFalse(
RequestUriUtils.isTrackableResource("/script.js"),
"JS files should not be trackable");
assertFalse(
RequestUriUtils.isTrackableResource("/pdfium/pdfium.wasm"),
"PDFium wasm should not be trackable");
assertFalse(
RequestUriUtils.isTrackableResource("/swagger/index.html"),
"Swagger files should not be trackable");
assertFalse(
RequestUriUtils.isTrackableResource("/api/v1/info/status"),
"API info should not be trackable");
assertFalse(
RequestUriUtils.isTrackableResource("/site.webmanifest"),
"Webmanifest should not be trackable");
assertFalse(
RequestUriUtils.isTrackableResource("/fonts/font.woff"),
"Fonts should not be trackable");
assertFalse(
RequestUriUtils.isTrackableResource("/pdfjs/viewer.js"),
"PDF.js files should not be trackable");
// Test trackable resources (returns true)
assertTrue(RequestUriUtils.isTrackableResource("/login"), "Login page should be trackable");
assertTrue(
RequestUriUtils.isTrackableResource("/register"),
"Register page should be trackable");
assertTrue(
RequestUriUtils.isTrackableResource("/api/v1/users"),
"API users should be trackable");
assertTrue(RequestUriUtils.isTrackableResource("/"), "Root path should be trackable");
assertTrue(
RequestUriUtils.isTrackableResource("/some-other-path"),
"Other paths should be trackable");
}
@Test
void testIsTrackableResourceWithContextPath() {
String contextPath = "/myapp";
// Test with context path
assertFalse(
RequestUriUtils.isTrackableResource(contextPath, "/js/script.js"),
"JS files should not be trackable with context path");
assertTrue(
RequestUriUtils.isTrackableResource(contextPath, "/login"),
"Login page should be trackable with context path");
// Additional tests with context path
assertFalse(
RequestUriUtils.isTrackableResource(contextPath, "/fonts/custom.woff"),
"Font files should not be trackable with context path");
assertFalse(
RequestUriUtils.isTrackableResource(contextPath, "/images/header.png"),
"Images should not be trackable with context path");
assertFalse(
RequestUriUtils.isTrackableResource(contextPath, "/swagger/ui.html"),
"Swagger UI should not be trackable with context path");
assertTrue(
RequestUriUtils.isTrackableResource(contextPath, "/account/profile"),
"Account page should be trackable with context path");
assertTrue(
RequestUriUtils.isTrackableResource(contextPath, "/pdf/view"),
"PDF view page should be trackable with context path");
}
@ParameterizedTest
@ValueSource(
strings = {
"/js/util.js",
"/v1/api-docs/swagger.json",
"/robots.txt",
"/images/header/logo.png",
"/styles/theme.css",
"/build/app.js.map",
"/assets/icon.svg",
"/data/popularity.txt",
"/bundle.js",
"/api/swagger-ui.html",
"/api/v1/info/health",
"/site.webmanifest",
"/fonts/roboto.woff",
"/pdfjs/viewer.js",
"/pdfium/pdfium.wasm"
})
void testNonTrackableResources(String path) {
assertFalse(
RequestUriUtils.isTrackableResource(path),
"Resources matching patterns should not be trackable: " + path);
}
@ParameterizedTest
@ValueSource(
strings = {
"/",
"/home",
"/login",
"/register",
"/pdf/merge",
"/pdf/split",
"/api/v1/users/1",
"/api/v1/documents/process",
"/settings",
"/account/profile",
"/dashboard",
"/help",
"/about"
})
void testTrackableResources(String path) {
assertTrue(
RequestUriUtils.isTrackableResource(path),
"App routes should be trackable: " + path);
}
@Test
void testEdgeCases() {
// Test with empty strings
assertFalse(RequestUriUtils.isStaticResource("", ""), "Empty path should not be static");
assertTrue(RequestUriUtils.isTrackableResource("", ""), "Empty path should be trackable");
// Test with null-like behavior (would actually throw NPE in real code)
// These are not actual null tests but shows handling of odd cases
assertFalse(RequestUriUtils.isStaticResource("null"), "String 'null' should not be static");
// Test String "null" as a path
boolean isTrackable = RequestUriUtils.isTrackableResource("null");
assertTrue(isTrackable, "String 'null' should be trackable");
// Mixed case extensions test - note that Java's endsWith() is case-sensitive
// We'll check actual behavior and document it rather than asserting
// Always test the lowercase versions which should definitely work
assertTrue(
RequestUriUtils.isStaticResource("/logo.png"), "PNG (lowercase) should be static");
assertTrue(
RequestUriUtils.isStaticResource("/icon.svg"), "SVG (lowercase) should be static");
// Path with query parameters
assertFalse(
RequestUriUtils.isStaticResource("/api/users?page=1"),
"Path with query params should respect base path");
assertTrue(
RequestUriUtils.isStaticResource("/images/logo.png?v=123"),
"Static resource with query params should still be static");
// Paths with fragments
assertTrue(
RequestUriUtils.isStaticResource("/css/styles.css#section1"),
"CSS with fragment should be static");
// Multiple dots in filename
assertTrue(
RequestUriUtils.isStaticResource("/js/jquery.min.js"),
"JS with multiple dots should be static");
// Special characters in path
assertTrue(
RequestUriUtils.isStaticResource("/images/user's-photo.png"),
"Path with special chars should be handled correctly");
}
@Test
void testComplexPaths() {
// Test complex static resource paths
assertTrue(
RequestUriUtils.isStaticResource("/css/theme/dark/styles.css"),
"Nested CSS should be static");
assertTrue(
RequestUriUtils.isStaticResource("/fonts/open-sans/bold/font.woff"),
"Nested font should be static");
assertTrue(
RequestUriUtils.isStaticResource("/js/vendor/jquery/3.5.1/jquery.min.js"),
"Versioned JS should be static");
// Test complex paths with context
String contextPath = "/app";
assertTrue(
RequestUriUtils.isStaticResource(
contextPath, contextPath + "/css/theme/dark/styles.css"),
"Nested CSS with context should be static");
// Test boundary cases for isTrackableResource
assertFalse(
RequestUriUtils.isTrackableResource("/js-framework/components"),
"Path starting with js- should not be treated as JS resource");
assertFalse(
RequestUriUtils.isTrackableResource("/fonts-selection"),
"Path starting with fonts- should not be treated as font resource");
}
}
@@ -1,276 +0,0 @@
package stirling.software.common.util;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.when;
import java.io.IOException;
import java.net.ServerSocket;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import jakarta.servlet.http.HttpServletRequest;
@ExtendWith(MockitoExtension.class)
class UrlUtilsTest {
@Mock private HttpServletRequest request;
@Test
void testGetOrigin() {
// Arrange
when(request.getScheme()).thenReturn("http");
when(request.getServerName()).thenReturn("localhost");
when(request.getServerPort()).thenReturn(8080);
when(request.getContextPath()).thenReturn("/myapp");
// Act
String origin = UrlUtils.getOrigin(request);
// Assert
assertEquals(
"http://localhost:8080/myapp", origin, "Origin URL should be correctly formatted");
}
@Test
void testGetOriginWithHttps() {
// Arrange
when(request.getScheme()).thenReturn("https");
when(request.getServerName()).thenReturn("example.com");
when(request.getServerPort()).thenReturn(443);
when(request.getContextPath()).thenReturn("");
// Act
String origin = UrlUtils.getOrigin(request);
// Assert
assertEquals(
"https://example.com:443",
origin,
"HTTPS origin URL should be correctly formatted");
}
@Test
void testGetOriginWithEmptyContextPath() {
// Arrange
when(request.getScheme()).thenReturn("http");
when(request.getServerName()).thenReturn("localhost");
when(request.getServerPort()).thenReturn(8080);
when(request.getContextPath()).thenReturn("");
// Act
String origin = UrlUtils.getOrigin(request);
// Assert
assertEquals(
"http://localhost:8080",
origin,
"Origin URL with empty context path should be correct");
}
@Test
void testGetOriginWithSpecialCharacters() {
// Arrange - Test with server name containing special characters
when(request.getScheme()).thenReturn("https");
when(request.getServerName()).thenReturn("internal-server.example-domain.com");
when(request.getServerPort()).thenReturn(8443);
when(request.getContextPath()).thenReturn("/app-v1.2");
// Act
String origin = UrlUtils.getOrigin(request);
// Assert
assertEquals(
"https://internal-server.example-domain.com:8443/app-v1.2",
origin,
"Origin URL with special characters should be correctly formatted");
}
@Test
void testGetOriginWithIPv4Address() {
// Arrange
when(request.getScheme()).thenReturn("http");
when(request.getServerName()).thenReturn("192.168.1.100");
when(request.getServerPort()).thenReturn(8080);
when(request.getContextPath()).thenReturn("/app");
// Act
String origin = UrlUtils.getOrigin(request);
// Assert
assertEquals(
"http://192.168.1.100:8080/app",
origin,
"Origin URL with IPv4 address should be correctly formatted");
}
@Test
void testGetOriginWithNonStandardPort() {
// Arrange
when(request.getScheme()).thenReturn("https");
when(request.getServerName()).thenReturn("example.org");
when(request.getServerPort()).thenReturn(8443);
when(request.getContextPath()).thenReturn("/api");
// Act
String origin = UrlUtils.getOrigin(request);
// Assert
assertEquals(
"https://example.org:8443/api",
origin,
"Origin URL with non-standard port should be correctly formatted");
}
@Test
void testIsPortAvailable() {
// We'll use a real server socket for this test
ServerSocket socket = null;
int port = 12345; // Choose a port unlikely to be in use
try {
// First check the port is available
boolean initialAvailability = UrlUtils.isPortAvailable(port);
// Then occupy the port
socket = new ServerSocket(port);
// Now check the port is no longer available
boolean afterSocketCreation = UrlUtils.isPortAvailable(port);
// Assert
assertTrue(initialAvailability, "Port should be available initially");
assertFalse(
afterSocketCreation, "Port should not be available after socket is created");
} catch (IOException e) {
// This might happen if the port is already in use by another process
// We'll just verify the behavior of isPortAvailable matches what we expect
assertFalse(
UrlUtils.isPortAvailable(port),
"Port should not be available if exception is thrown");
} finally {
if (socket != null && !socket.isClosed()) {
try {
socket.close();
} catch (IOException e) {
// Ignore cleanup exceptions
}
}
}
}
@Test
void testFindAvailablePort() {
// We'll create a socket on a port and ensure findAvailablePort returns a different port
ServerSocket socket = null;
int startPort = 12346; // Choose a port unlikely to be in use
try {
// Occupy the start port
socket = new ServerSocket(startPort);
// Find an available port
String availablePort = UrlUtils.findAvailablePort(startPort);
// Assert the returned port is not the occupied one
assertNotEquals(
String.valueOf(startPort),
availablePort,
"findAvailablePort should not return an occupied port");
// Verify the returned port is actually available
int portNumber = Integer.parseInt(availablePort);
// Close our test socket before checking the found port
socket.close();
socket = null;
// The port should now be available
assertTrue(
UrlUtils.isPortAvailable(portNumber),
"The port returned by findAvailablePort should be available");
} catch (IOException e) {
// If we can't create the socket, skip this assertion
} finally {
if (socket != null && !socket.isClosed()) {
try {
socket.close();
} catch (IOException e) {
// Ignore cleanup exceptions
}
}
}
}
@Test
void testFindAvailablePortWithAvailableStartPort() {
// Find an available port without occupying any
int startPort = 23456; // Choose a different unlikely-to-be-used port
// Make sure the port is available first
if (UrlUtils.isPortAvailable(startPort)) {
// Find an available port
String availablePort = UrlUtils.findAvailablePort(startPort);
// Assert the returned port is the start port since it's available
assertEquals(
String.valueOf(startPort),
availablePort,
"findAvailablePort should return the start port if it's available");
}
}
@Test
void testFindAvailablePortWithSequentialUsedPorts() {
// This test checks that findAvailablePort correctly skips multiple occupied ports
ServerSocket socket1 = null;
ServerSocket socket2 = null;
int startPort = 34567; // Another unlikely-to-be-used port
try {
// First verify the port is available
if (!UrlUtils.isPortAvailable(startPort)) {
return;
}
// Occupy two sequential ports
socket1 = new ServerSocket(startPort);
socket2 = new ServerSocket(startPort + 1);
// Find an available port starting from our occupied range
String availablePort = UrlUtils.findAvailablePort(startPort);
int foundPort = Integer.parseInt(availablePort);
// Should have skipped the two occupied ports
assertTrue(
foundPort >= startPort + 2,
"findAvailablePort should skip sequential occupied ports");
// Verify the found port is actually available
try (ServerSocket testSocket = new ServerSocket(foundPort)) {
assertTrue(testSocket.isBound(), "The found port should be bindable");
}
} catch (IOException e) {
// Skip test if we encounter IO exceptions
} finally {
// Clean up resources
try {
if (socket1 != null && !socket1.isClosed()) socket1.close();
if (socket2 != null && !socket2.isClosed()) socket2.close();
} catch (IOException e) {
// Ignore cleanup exceptions
}
}
}
@Test
void testIsPortAvailableWithPrivilegedPorts() {
// Skip tests for privileged ports as they typically require root access
// and results are environment-dependent
}
}
@@ -1,115 +0,0 @@
package stirling.software.common.util;
import static org.junit.jupiter.api.Assertions.*;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.junit.jupiter.api.Test;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.mock.web.MockMultipartFile;
public class WebResponseUtilsTest {
@Test
public void testBoasToWebResponse() {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
baos.write("Sample PDF content".getBytes());
String docName = "sample.pdf";
ResponseEntity<byte[]> responseEntity =
WebResponseUtils.baosToWebResponse(baos, docName);
assertNotNull(responseEntity);
assertEquals(HttpStatus.OK, responseEntity.getStatusCode());
assertNotNull(responseEntity.getBody());
HttpHeaders headers = responseEntity.getHeaders();
assertNotNull(headers);
assertEquals(MediaType.APPLICATION_PDF, headers.getContentType());
assertNotNull(headers.getContentDisposition());
// assertEquals("attachment; filename=\"sample.pdf\"",
// headers.getContentDisposition().toString());
} catch (IOException e) {
fail("Exception thrown: " + e.getMessage());
}
}
@Test
public void testMultiPartFileToWebResponse() {
try {
byte[] fileContent = "Sample file content".getBytes();
MockMultipartFile file =
new MockMultipartFile(
"file", "sample.txt", MediaType.TEXT_PLAIN_VALUE, fileContent);
ResponseEntity<byte[]> responseEntity =
WebResponseUtils.multiPartFileToWebResponse(file);
assertNotNull(responseEntity);
assertEquals(HttpStatus.OK, responseEntity.getStatusCode());
assertNotNull(responseEntity.getBody());
HttpHeaders headers = responseEntity.getHeaders();
assertNotNull(headers);
assertEquals(MediaType.TEXT_PLAIN, headers.getContentType());
assertNotNull(headers.getContentDisposition());
} catch (IOException e) {
fail("Exception thrown: " + e.getMessage());
}
}
@Test
public void testBytesToWebResponse() {
try {
byte[] bytes = "Sample bytes".getBytes();
String docName = "sample.txt";
MediaType mediaType = MediaType.TEXT_PLAIN;
ResponseEntity<byte[]> responseEntity =
WebResponseUtils.bytesToWebResponse(bytes, docName, mediaType);
assertNotNull(responseEntity);
assertEquals(HttpStatus.OK, responseEntity.getStatusCode());
assertNotNull(responseEntity.getBody());
HttpHeaders headers = responseEntity.getHeaders();
assertNotNull(headers);
assertEquals(MediaType.TEXT_PLAIN, headers.getContentType());
assertNotNull(headers.getContentDisposition());
} catch (IOException e) {
fail("Exception thrown: " + e.getMessage());
}
}
@Test
public void testPdfDocToWebResponse() {
try (PDDocument document = new PDDocument()) {
document.addPage(new org.apache.pdfbox.pdmodel.PDPage());
String docName = "sample.pdf";
ResponseEntity<byte[]> responseEntity =
WebResponseUtils.pdfDocToWebResponse(document, docName);
assertNotNull(responseEntity);
assertEquals(HttpStatus.OK, responseEntity.getStatusCode());
assertNotNull(responseEntity.getBody());
HttpHeaders headers = responseEntity.getHeaders();
assertNotNull(headers);
assertEquals(MediaType.APPLICATION_PDF, headers.getContentType());
assertNotNull(headers.getContentDisposition());
} catch (IOException e) {
fail("Exception thrown: " + e.getMessage());
}
}
}