feat(pipeline): improve file processing with resource management and temp file handling (#5488)

# 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/devGuide/DeveloperGuide.md)
(if applicable)
- [ ] I have read the [How to add new languages to
Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md)
(if applicable)
- [ ] I have performed a self-review of my own code
- [ ] My changes generate no new warnings

### Documentation

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

### Translations (if applicable)

- [ ] I ran
[`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md)

### UI Changes (if applicable)

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

### Testing (if applicable)

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

Signed-off-by: Balázs Szücs <[email protected]>
This commit is contained in:
Balázs Szücs
2026-01-17 19:44:02 +00:00
committed by GitHub
parent 473021a13c
commit 59d816bfa7
4 changed files with 192 additions and 53 deletions
@@ -4,6 +4,8 @@ import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
@@ -13,6 +15,7 @@ import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
@@ -24,6 +27,7 @@ import stirling.software.SPDF.model.PipelineOperation;
import stirling.software.SPDF.model.PipelineResult;
import stirling.software.SPDF.service.ApiDocService;
import stirling.software.common.service.UserServiceInterface;
import stirling.software.common.util.TempFileManager;
@ExtendWith(MockitoExtension.class)
class PipelineProcessorTest {
@@ -34,11 +38,16 @@ class PipelineProcessorTest {
@Mock ServletContext servletContext;
@Mock TempFileManager tempFileManager;
PipelineProcessor pipelineProcessor;
@BeforeEach
void setUp() {
pipelineProcessor = spy(new PipelineProcessor(apiDocService, userService, servletContext));
pipelineProcessor =
spy(
new PipelineProcessor(
apiDocService, userService, servletContext, tempFileManager));
}
@Test
@@ -59,12 +68,18 @@ class PipelineProcessorTest {
when(apiDocService.isValidOperation(eq("/api/v1/filter/filter-page-count"), anyMap()))
.thenReturn(true);
doReturn(new ResponseEntity<>(new byte[0], HttpStatus.OK))
// Use a FileSystemResource backed by a temp file to avoid FileNotFoundException
Path emptyTemp = Files.createTempFile("empty", ".tmp");
Resource emptyResource = new FileSystemResource(emptyTemp.toFile());
doReturn(new ResponseEntity<>(emptyResource, HttpStatus.OK))
.when(pipelineProcessor)
.sendWebRequest(anyString(), any());
PipelineResult result = pipelineProcessor.runPipelineAgainstFiles(files, config);
Files.deleteIfExists(emptyTemp);
assertTrue(
result.isFiltersApplied(),
"Filter flag should be true when operation filters file");
@@ -72,6 +87,45 @@ class PipelineProcessorTest {
assertTrue(result.getOutputFiles().isEmpty(), "Filtered file list should be empty");
}
@Test
void testPipelineSuccessWithResource() throws Exception {
PipelineOperation op = new PipelineOperation();
op.setOperation("/api/v1/misc/compress");
op.setParameters(Map.of());
PipelineConfig config = new PipelineConfig();
config.setOperations(List.of(op));
Resource inputFile = new MyFileByteArrayResource();
List<Resource> files = List.of(inputFile);
Path tempPath = Files.createTempFile("test-output", ".pdf");
Files.write(tempPath, "processed_data".getBytes());
Resource outputResource =
new FileSystemResource(tempPath.toFile()) {
@Override
public String getFilename() {
return "processed.pdf";
}
};
when(apiDocService.isMultiInput(anyString())).thenReturn(false);
when(apiDocService.getExtensionTypes(anyBoolean(), anyString())).thenReturn(List.of("pdf"));
when(apiDocService.isValidOperation(anyString(), anyMap())).thenReturn(true);
doReturn(new ResponseEntity<>(outputResource, HttpStatus.OK))
.when(pipelineProcessor)
.sendWebRequest(anyString(), any());
PipelineResult result = pipelineProcessor.runPipelineAgainstFiles(files, config);
verify(pipelineProcessor).sendWebRequest(anyString(), any());
assertFalse(result.isHasErrors());
// Clean up
Files.deleteIfExists(tempPath);
}
private static class MyFileByteArrayResource extends ByteArrayResource {
public MyFileByteArrayResource() {
super("data".getBytes());