refactor(importing): inject FileStreamOpener to remove test-only seam

DocumentImporter exposed a package-private openFileStream(File) so a
Mockito spy could force the IO-error branch of isPdfMagicBytes. The
test-only seam leaked into production: the method existed for testing,
not for any production extensibility.

Replace with a constructor-injected FileStreamOpener interface (single
abstract method, @FunctionalInterface) and a one-line
@Component DefaultFileStreamOpener delegate. Tests now inject a mock
opener instead of spying on the importer itself, which is also a more
idiomatic Mockito usage.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-05-28 10:29:41 +02:00
parent 535594378a
commit 4cc725d546
3 changed files with 49 additions and 13 deletions

View File

@@ -49,12 +49,18 @@ class DocumentImporterTest {
@Mock TagService tagService;
@Mock S3Client s3Client;
@Mock ThumbnailAsyncRunner thumbnailAsyncRunner;
@Mock FileStreamOpener fileStreamOpener;
DocumentImporter importer;
@BeforeEach
void setUp() {
importer = new DocumentImporter(documentService, personService, tagService, s3Client, thumbnailAsyncRunner);
void setUp() throws java.io.IOException {
// Default opener delegates to FileInputStream — tests that need to force an IOException
// override this stub locally (load_skipsFile_whenMagicByteCheckThrowsIoException).
lenient().when(fileStreamOpener.open(any(File.class)))
.thenAnswer(inv -> new java.io.FileInputStream(inv.getArgument(0, File.class)));
importer = new DocumentImporter(documentService, personService, tagService, s3Client,
thumbnailAsyncRunner, fileStreamOpener);
ReflectionTestUtils.setField(importer, "bucketName", "test-bucket");
}
@@ -305,11 +311,11 @@ class DocumentImporterTest {
lenient().when(documentService.findByOriginalFilename(any())).thenReturn(Optional.empty());
Path xlsx = writeDocs(tempDir, docRow("W-0001", "", "", "", "", "", "", "", ""));
DocumentImporter spyImporter = org.mockito.Mockito.spy(importer);
org.mockito.Mockito.doThrow(new java.io.IOException("read error"))
.when(spyImporter).openFileStream(any(File.class));
// FileStreamOpener is injected — stub it to throw, no spy on the importer needed.
org.mockito.Mockito.when(fileStreamOpener.open(any(File.class)))
.thenThrow(new java.io.IOException("read error"));
DocumentImporter.LoadResult result = spyImporter.load(xlsx.toFile());
DocumentImporter.LoadResult result = importer.load(xlsx.toFile());
assertThat(result.skippedFiles())
.extracting(ImportStatus.SkippedFile::reason)