refactor(import): route MassImportService through DocumentService

MassImportService injected DocumentRepository for the find-or-create pattern
during ODS/Excel import. Move the two repository touchpoints (findByOriginalFilename,
save) onto DocumentService as 1-line delegations and update the consumer.

Refs #417 (C6.2 violation #1).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-05-05 07:27:30 +02:00
parent 8b177b9430
commit 0ca95d5ad7
4 changed files with 82 additions and 55 deletions

View File

@@ -94,6 +94,14 @@ public class DocumentService {
return documentRepository.save(doc); return documentRepository.save(doc);
} }
public Optional<Document> findByOriginalFilename(String originalFilename) {
return documentRepository.findByOriginalFilename(originalFilename);
}
public Document save(Document doc) {
return documentRepository.save(doc);
}
public List<org.raddatz.familienarchiv.repository.TranscriptionQueueProjection> findSegmentationQueue(int limit) { public List<org.raddatz.familienarchiv.repository.TranscriptionQueueProjection> findSegmentationQueue(int limit) {
return documentRepository.findSegmentationQueue(limit); return documentRepository.findSegmentationQueue(limit);
} }

View File

@@ -10,7 +10,6 @@ import org.raddatz.familienarchiv.model.Document;
import org.raddatz.familienarchiv.model.DocumentStatus; import org.raddatz.familienarchiv.model.DocumentStatus;
import org.raddatz.familienarchiv.model.Person; import org.raddatz.familienarchiv.model.Person;
import org.raddatz.familienarchiv.model.Tag; import org.raddatz.familienarchiv.model.Tag;
import org.raddatz.familienarchiv.repository.DocumentRepository;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@@ -55,7 +54,7 @@ public class MassImportService {
return currentStatus; return currentStatus;
} }
private final DocumentRepository documentRepository; private final DocumentService documentService;
private final PersonService personService; private final PersonService personService;
private final TagService tagService; private final TagService tagService;
private final S3Client s3Client; private final S3Client s3Client;
@@ -257,7 +256,7 @@ public class MassImportService {
@Transactional @Transactional
protected void importSingleDocument(List<String> cells, Optional<File> file, String originalFilename, String index) { protected void importSingleDocument(List<String> cells, Optional<File> file, String originalFilename, String index) {
Optional<Document> existing = documentRepository.findByOriginalFilename(originalFilename); Optional<Document> existing = documentService.findByOriginalFilename(originalFilename);
if (existing.isPresent() && existing.get().getStatus() != DocumentStatus.PLACEHOLDER) { if (existing.isPresent() && existing.get().getStatus() != DocumentStatus.PLACEHOLDER) {
log.info("Dokument {} existiert bereits, überspringe.", originalFilename); log.info("Dokument {} existiert bereits, überspringe.", originalFilename);
return; return;
@@ -333,7 +332,7 @@ public class MassImportService {
if (tag != null) doc.getTags().add(tag); if (tag != null) doc.getTags().add(tag);
doc.setMetadataComplete(metadataComplete); doc.setMetadataComplete(metadataComplete);
Document saved = documentRepository.save(doc); Document saved = documentService.save(doc);
if (file.isPresent()) { if (file.isPresent()) {
thumbnailAsyncRunner.dispatchAfterCommit(saved.getId()); thumbnailAsyncRunner.dispatchAfterCommit(saved.getId());
} }

View File

@@ -2306,4 +2306,25 @@ class DocumentServiceTest {
assertThat(documentService.updateThumbnailMetadata(doc)).isEqualTo(doc); assertThat(documentService.updateThumbnailMetadata(doc)).isEqualTo(doc);
verify(documentRepository).save(doc); verify(documentRepository).save(doc);
} }
// ─── findByOriginalFilename ────────────────────────────────────────────────
@Test
void findByOriginalFilename_returnsRepositoryResult() {
Document doc = Document.builder().id(UUID.randomUUID()).title("T").build();
when(documentRepository.findByOriginalFilename("scan.pdf")).thenReturn(Optional.of(doc));
assertThat(documentService.findByOriginalFilename("scan.pdf")).contains(doc);
}
// ─── save ──────────────────────────────────────────────────────────────────
@Test
void save_delegatesToRepository() {
Document doc = Document.builder().id(UUID.randomUUID()).title("T").build();
when(documentRepository.save(doc)).thenReturn(doc);
assertThat(documentService.save(doc)).isEqualTo(doc);
verify(documentRepository).save(doc);
}
} }

View File

@@ -11,7 +11,6 @@ import org.raddatz.familienarchiv.model.Document;
import org.raddatz.familienarchiv.model.DocumentStatus; import org.raddatz.familienarchiv.model.DocumentStatus;
import org.raddatz.familienarchiv.model.Person; import org.raddatz.familienarchiv.model.Person;
import org.raddatz.familienarchiv.model.Tag; import org.raddatz.familienarchiv.model.Tag;
import org.raddatz.familienarchiv.repository.DocumentRepository;
import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.util.ReflectionTestUtils;
import software.amazon.awssdk.core.sync.RequestBody; import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.services.s3.S3Client; import software.amazon.awssdk.services.s3.S3Client;
@@ -35,7 +34,7 @@ import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
class MassImportServiceTest { class MassImportServiceTest {
@Mock DocumentRepository documentRepository; @Mock DocumentService documentService;
@Mock PersonService personService; @Mock PersonService personService;
@Mock TagService tagService; @Mock TagService tagService;
@Mock S3Client s3Client; @Mock S3Client s3Client;
@@ -45,7 +44,7 @@ class MassImportServiceTest {
@BeforeEach @BeforeEach
void setUp() { void setUp() {
service = new MassImportService(documentRepository, personService, tagService, s3Client, thumbnailAsyncRunner); service = new MassImportService(documentService, personService, tagService, s3Client, thumbnailAsyncRunner);
ReflectionTestUtils.setField(service, "bucketName", "test-bucket"); ReflectionTestUtils.setField(service, "bucketName", "test-bucket");
ReflectionTestUtils.setField(service, "colIndex", 0); ReflectionTestUtils.setField(service, "colIndex", 0);
ReflectionTestUtils.setField(service, "colBox", 1); ReflectionTestUtils.setField(service, "colBox", 1);
@@ -96,23 +95,23 @@ class MassImportServiceTest {
.originalFilename("doc001.pdf") .originalFilename("doc001.pdf")
.status(DocumentStatus.UPLOADED) .status(DocumentStatus.UPLOADED)
.build(); .build();
when(documentRepository.findByOriginalFilename("doc001.pdf")).thenReturn(Optional.of(existing)); when(documentService.findByOriginalFilename("doc001.pdf")).thenReturn(Optional.of(existing));
service.importSingleDocument(minimalCells("doc001.pdf"), Optional.empty(), "doc001.pdf", "doc001"); service.importSingleDocument(minimalCells("doc001.pdf"), Optional.empty(), "doc001.pdf", "doc001");
verify(documentRepository, never()).save(any()); verify(documentService, never()).save(any());
} }
// ─── importSingleDocument — create new document (metadata only) ─────────── // ─── importSingleDocument — create new document (metadata only) ───────────
@Test @Test
void importSingleDocument_createsNewDocument_whenNotExists() { void importSingleDocument_createsNewDocument_whenNotExists() {
when(documentRepository.findByOriginalFilename("doc002.pdf")).thenReturn(Optional.empty()); when(documentService.findByOriginalFilename("doc002.pdf")).thenReturn(Optional.empty());
when(documentRepository.save(any())).thenAnswer(inv -> inv.getArgument(0)); when(documentService.save(any())).thenAnswer(inv -> inv.getArgument(0));
service.importSingleDocument(minimalCells("doc002.pdf"), Optional.empty(), "doc002.pdf", "doc002"); service.importSingleDocument(minimalCells("doc002.pdf"), Optional.empty(), "doc002.pdf", "doc002");
verify(documentRepository).save(argThat(d -> verify(documentService).save(argThat(d ->
d.getOriginalFilename().equals("doc002.pdf") d.getOriginalFilename().equals("doc002.pdf")
&& d.getStatus() == DocumentStatus.PLACEHOLDER)); && d.getStatus() == DocumentStatus.PLACEHOLDER));
} }
@@ -126,12 +125,12 @@ class MassImportServiceTest {
.originalFilename("existing.pdf") .originalFilename("existing.pdf")
.status(DocumentStatus.PLACEHOLDER) .status(DocumentStatus.PLACEHOLDER)
.build(); .build();
when(documentRepository.findByOriginalFilename("existing.pdf")).thenReturn(Optional.of(placeholder)); when(documentService.findByOriginalFilename("existing.pdf")).thenReturn(Optional.of(placeholder));
when(documentRepository.save(any())).thenAnswer(inv -> inv.getArgument(0)); when(documentService.save(any())).thenAnswer(inv -> inv.getArgument(0));
service.importSingleDocument(minimalCells("existing.pdf"), Optional.empty(), "existing.pdf", "existing"); service.importSingleDocument(minimalCells("existing.pdf"), Optional.empty(), "existing.pdf", "existing");
verify(documentRepository).save(same(placeholder)); verify(documentService).save(same(placeholder));
} }
// ─── importSingleDocument — with file (S3 upload) ───────────────────────── // ─── importSingleDocument — with file (S3 upload) ─────────────────────────
@@ -141,14 +140,14 @@ class MassImportServiceTest {
Path tempFile = tempDir.resolve("doc003.pdf"); Path tempFile = tempDir.resolve("doc003.pdf");
Files.write(tempFile, "PDF content".getBytes()); Files.write(tempFile, "PDF content".getBytes());
when(documentRepository.findByOriginalFilename("doc003.pdf")).thenReturn(Optional.empty()); when(documentService.findByOriginalFilename("doc003.pdf")).thenReturn(Optional.empty());
when(documentRepository.save(any())).thenAnswer(inv -> inv.getArgument(0)); when(documentService.save(any())).thenAnswer(inv -> inv.getArgument(0));
service.importSingleDocument( service.importSingleDocument(
minimalCells("doc003.pdf"), Optional.of(tempFile.toFile()), "doc003.pdf", "doc003"); minimalCells("doc003.pdf"), Optional.of(tempFile.toFile()), "doc003.pdf", "doc003");
verify(s3Client).putObject(any(PutObjectRequest.class), any(RequestBody.class)); verify(s3Client).putObject(any(PutObjectRequest.class), any(RequestBody.class));
verify(documentRepository).save(argThat(d -> d.getStatus() == DocumentStatus.UPLOADED)); verify(documentService).save(argThat(d -> d.getStatus() == DocumentStatus.UPLOADED));
} }
@Test @Test
@@ -156,42 +155,42 @@ class MassImportServiceTest {
Path tempFile = tempDir.resolve("fail.pdf"); Path tempFile = tempDir.resolve("fail.pdf");
Files.write(tempFile, "data".getBytes()); Files.write(tempFile, "data".getBytes());
when(documentRepository.findByOriginalFilename("fail.pdf")).thenReturn(Optional.empty()); when(documentService.findByOriginalFilename("fail.pdf")).thenReturn(Optional.empty());
doThrow(new RuntimeException("S3 error")) doThrow(new RuntimeException("S3 error"))
.when(s3Client).putObject(any(PutObjectRequest.class), any(RequestBody.class)); .when(s3Client).putObject(any(PutObjectRequest.class), any(RequestBody.class));
service.importSingleDocument( service.importSingleDocument(
minimalCells("fail.pdf"), Optional.of(tempFile.toFile()), "fail.pdf", "fail"); minimalCells("fail.pdf"), Optional.of(tempFile.toFile()), "fail.pdf", "fail");
verify(documentRepository, never()).save(any()); verify(documentService, never()).save(any());
} }
// ─── importSingleDocument — sender handling ─────────────────────────────── // ─── importSingleDocument — sender handling ───────────────────────────────
@Test @Test
void importSingleDocument_setsNullSender_whenSenderCellIsBlank() { void importSingleDocument_setsNullSender_whenSenderCellIsBlank() {
when(documentRepository.findByOriginalFilename("nosender.pdf")).thenReturn(Optional.empty()); when(documentService.findByOriginalFilename("nosender.pdf")).thenReturn(Optional.empty());
when(documentRepository.save(any())).thenAnswer(inv -> inv.getArgument(0)); when(documentService.save(any())).thenAnswer(inv -> inv.getArgument(0));
List<String> cells = buildCells("nosender.pdf", "", "", ""); List<String> cells = buildCells("nosender.pdf", "", "", "");
service.importSingleDocument(cells, Optional.empty(), "nosender.pdf", "nosender"); service.importSingleDocument(cells, Optional.empty(), "nosender.pdf", "nosender");
verify(documentRepository).save(argThat(d -> d.getSender() == null)); verify(documentService).save(argThat(d -> d.getSender() == null));
verify(personService, never()).findOrCreateByAlias(any()); verify(personService, never()).findOrCreateByAlias(any());
} }
@Test @Test
void importSingleDocument_createsSender_whenSenderCellIsNonBlank() { void importSingleDocument_createsSender_whenSenderCellIsNonBlank() {
Person sender = Person.builder().id(UUID.randomUUID()).firstName("Walter").lastName("Müller").build(); Person sender = Person.builder().id(UUID.randomUUID()).firstName("Walter").lastName("Müller").build();
when(documentRepository.findByOriginalFilename("withsender.pdf")).thenReturn(Optional.empty()); when(documentService.findByOriginalFilename("withsender.pdf")).thenReturn(Optional.empty());
when(documentRepository.save(any())).thenAnswer(inv -> inv.getArgument(0)); when(documentService.save(any())).thenAnswer(inv -> inv.getArgument(0));
when(personService.findOrCreateByAlias("Walter Müller")).thenReturn(sender); when(personService.findOrCreateByAlias("Walter Müller")).thenReturn(sender);
List<String> cells = buildCells("withsender.pdf", "Walter Müller", "", ""); List<String> cells = buildCells("withsender.pdf", "Walter Müller", "", "");
service.importSingleDocument(cells, Optional.empty(), "withsender.pdf", "withsender"); service.importSingleDocument(cells, Optional.empty(), "withsender.pdf", "withsender");
verify(personService).findOrCreateByAlias("Walter Müller"); verify(personService).findOrCreateByAlias("Walter Müller");
verify(documentRepository).save(argThat(d -> d.getSender() == sender)); verify(documentService).save(argThat(d -> d.getSender() == sender));
} }
// ─── importSingleDocument — tag handling ───────────────────────────────── // ─── importSingleDocument — tag handling ─────────────────────────────────
@@ -199,8 +198,8 @@ class MassImportServiceTest {
@Test @Test
void importSingleDocument_createsTag_whenTagCellIsNonBlank() { void importSingleDocument_createsTag_whenTagCellIsNonBlank() {
Tag tag = Tag.builder().id(UUID.randomUUID()).name("Familie").build(); Tag tag = Tag.builder().id(UUID.randomUUID()).name("Familie").build();
when(documentRepository.findByOriginalFilename("tagged.pdf")).thenReturn(Optional.empty()); when(documentService.findByOriginalFilename("tagged.pdf")).thenReturn(Optional.empty());
when(documentRepository.save(any())).thenAnswer(inv -> inv.getArgument(0)); when(documentService.save(any())).thenAnswer(inv -> inv.getArgument(0));
when(tagService.findOrCreate("Familie")).thenReturn(tag); when(tagService.findOrCreate("Familie")).thenReturn(tag);
List<String> cells = buildCells("tagged.pdf", "", "", "Familie"); List<String> cells = buildCells("tagged.pdf", "", "", "Familie");
@@ -211,8 +210,8 @@ class MassImportServiceTest {
@Test @Test
void importSingleDocument_doesNotCreateTag_whenTagCellIsBlank() { void importSingleDocument_doesNotCreateTag_whenTagCellIsBlank() {
when(documentRepository.findByOriginalFilename("notag.pdf")).thenReturn(Optional.empty()); when(documentService.findByOriginalFilename("notag.pdf")).thenReturn(Optional.empty());
when(documentRepository.save(any())).thenAnswer(inv -> inv.getArgument(0)); when(documentService.save(any())).thenAnswer(inv -> inv.getArgument(0));
List<String> cells = buildCells("notag.pdf", "", "", ""); List<String> cells = buildCells("notag.pdf", "", "", "");
service.importSingleDocument(cells, Optional.empty(), "notag.pdf", "notag"); service.importSingleDocument(cells, Optional.empty(), "notag.pdf", "notag");
@@ -225,38 +224,38 @@ class MassImportServiceTest {
@Test @Test
void importSingleDocument_metadataComplete_whenSenderPresent() { void importSingleDocument_metadataComplete_whenSenderPresent() {
Person sender = Person.builder().id(UUID.randomUUID()).firstName("A").lastName("B").build(); Person sender = Person.builder().id(UUID.randomUUID()).firstName("A").lastName("B").build();
when(documentRepository.findByOriginalFilename("meta.pdf")).thenReturn(Optional.empty()); when(documentService.findByOriginalFilename("meta.pdf")).thenReturn(Optional.empty());
when(documentRepository.save(any())).thenAnswer(inv -> inv.getArgument(0)); when(documentService.save(any())).thenAnswer(inv -> inv.getArgument(0));
when(personService.findOrCreateByAlias("A B")).thenReturn(sender); when(personService.findOrCreateByAlias("A B")).thenReturn(sender);
List<String> cells = buildCells("meta.pdf", "A B", "", ""); List<String> cells = buildCells("meta.pdf", "A B", "", "");
service.importSingleDocument(cells, Optional.empty(), "meta.pdf", "meta"); service.importSingleDocument(cells, Optional.empty(), "meta.pdf", "meta");
verify(documentRepository).save(argThat(Document::isMetadataComplete)); verify(documentService).save(argThat(Document::isMetadataComplete));
} }
@Test @Test
void importSingleDocument_metadataIncomplete_whenNoKeyFieldsPresent() { void importSingleDocument_metadataIncomplete_whenNoKeyFieldsPresent() {
when(documentRepository.findByOriginalFilename("nometa.pdf")).thenReturn(Optional.empty()); when(documentService.findByOriginalFilename("nometa.pdf")).thenReturn(Optional.empty());
when(documentRepository.save(any())).thenAnswer(inv -> inv.getArgument(0)); when(documentService.save(any())).thenAnswer(inv -> inv.getArgument(0));
List<String> cells = buildCells("nometa.pdf", "", "", ""); List<String> cells = buildCells("nometa.pdf", "", "", "");
service.importSingleDocument(cells, Optional.empty(), "nometa.pdf", "nometa"); service.importSingleDocument(cells, Optional.empty(), "nometa.pdf", "nometa");
verify(documentRepository).save(argThat(d -> !d.isMetadataComplete())); verify(documentService).save(argThat(d -> !d.isMetadataComplete()));
} }
// ─── importSingleDocument — blank fields set to null ───────────────────── // ─── importSingleDocument — blank fields set to null ─────────────────────
@Test @Test
void importSingleDocument_setsBlankFieldsToNull() { void importSingleDocument_setsBlankFieldsToNull() {
when(documentRepository.findByOriginalFilename("blank.pdf")).thenReturn(Optional.empty()); when(documentService.findByOriginalFilename("blank.pdf")).thenReturn(Optional.empty());
when(documentRepository.save(any())).thenAnswer(inv -> inv.getArgument(0)); when(documentService.save(any())).thenAnswer(inv -> inv.getArgument(0));
List<String> cells = buildCells("blank.pdf", "", "", ""); List<String> cells = buildCells("blank.pdf", "", "", "");
service.importSingleDocument(cells, Optional.empty(), "blank.pdf", "blank"); service.importSingleDocument(cells, Optional.empty(), "blank.pdf", "blank");
verify(documentRepository).save(argThat(d -> verify(documentService).save(argThat(d ->
d.getLocation() == null && d.getLocation() == null &&
d.getSummary() == null && d.getSummary() == null &&
d.getTranscription() == null && d.getTranscription() == null &&
@@ -281,13 +280,13 @@ class MassImportServiceTest {
); );
Integer result = ReflectionTestUtils.invokeMethod(service, "processRows", rows); Integer result = ReflectionTestUtils.invokeMethod(service, "processRows", rows);
assertThat(result).isEqualTo(0); assertThat(result).isEqualTo(0);
verify(documentRepository, never()).findByOriginalFilename(any()); verify(documentService, never()).findByOriginalFilename(any());
} }
@Test @Test
void processRows_addsExtension_whenIndexHasNoDot() { void processRows_addsExtension_whenIndexHasNoDot() {
when(documentRepository.findByOriginalFilename("doc001.pdf")).thenReturn(Optional.empty()); when(documentService.findByOriginalFilename("doc001.pdf")).thenReturn(Optional.empty());
when(documentRepository.save(any())).thenAnswer(inv -> inv.getArgument(0)); when(documentService.save(any())).thenAnswer(inv -> inv.getArgument(0));
List<List<String>> rows = List.of( List<List<String>> rows = List.of(
List.of("header"), List.of("header"),
@@ -296,13 +295,13 @@ class MassImportServiceTest {
Integer result = ReflectionTestUtils.invokeMethod(service, "processRows", rows); Integer result = ReflectionTestUtils.invokeMethod(service, "processRows", rows);
assertThat(result).isEqualTo(1); assertThat(result).isEqualTo(1);
verify(documentRepository).findByOriginalFilename("doc001.pdf"); verify(documentService).findByOriginalFilename("doc001.pdf");
} }
@Test @Test
void processRows_usesFilenameAsIs_whenIndexHasDot() { void processRows_usesFilenameAsIs_whenIndexHasDot() {
when(documentRepository.findByOriginalFilename("doc002.pdf")).thenReturn(Optional.empty()); when(documentService.findByOriginalFilename("doc002.pdf")).thenReturn(Optional.empty());
when(documentRepository.save(any())).thenAnswer(inv -> inv.getArgument(0)); when(documentService.save(any())).thenAnswer(inv -> inv.getArgument(0));
List<List<String>> rows = List.of( List<List<String>> rows = List.of(
List.of("header"), List.of("header"),
@@ -311,15 +310,15 @@ class MassImportServiceTest {
Integer result = ReflectionTestUtils.invokeMethod(service, "processRows", rows); Integer result = ReflectionTestUtils.invokeMethod(service, "processRows", rows);
assertThat(result).isEqualTo(1); assertThat(result).isEqualTo(1);
verify(documentRepository).findByOriginalFilename("doc002.pdf"); verify(documentService).findByOriginalFilename("doc002.pdf");
} }
// ─── importSingleDocument — non-blank optional fields ──────────────────── // ─── importSingleDocument — non-blank optional fields ────────────────────
@Test @Test
void importSingleDocument_setsNonNullOptionalFields_whenPresent() { void importSingleDocument_setsNonNullOptionalFields_whenPresent() {
when(documentRepository.findByOriginalFilename("rich.pdf")).thenReturn(Optional.empty()); when(documentService.findByOriginalFilename("rich.pdf")).thenReturn(Optional.empty());
when(documentRepository.save(any())).thenAnswer(inv -> inv.getArgument(0)); when(documentService.save(any())).thenAnswer(inv -> inv.getArgument(0));
// box=1, folder=2, location=9, summary=11, transcription=13 // box=1, folder=2, location=9, summary=11, transcription=13
List<String> cells = List.of( List<String> cells = List.of(
@@ -341,7 +340,7 @@ class MassImportServiceTest {
service.importSingleDocument(cells, Optional.empty(), "rich.pdf", "rich"); service.importSingleDocument(cells, Optional.empty(), "rich.pdf", "rich");
verify(documentRepository).save(argThat(d -> verify(documentService).save(argThat(d ->
"Box A".equals(d.getArchiveBox()) && "Box A".equals(d.getArchiveBox()) &&
"Folder B".equals(d.getArchiveFolder()) && "Folder B".equals(d.getArchiveFolder()) &&
"Hamburg".equals(d.getLocation()) && "Hamburg".equals(d.getLocation()) &&
@@ -352,27 +351,27 @@ class MassImportServiceTest {
@Test @Test
void importSingleDocument_setsMetadataComplete_whenReceiversArePresent() { void importSingleDocument_setsMetadataComplete_whenReceiversArePresent() {
Person receiver = Person.builder().id(UUID.randomUUID()).firstName("Walter").lastName("Müller").build(); Person receiver = Person.builder().id(UUID.randomUUID()).firstName("Walter").lastName("Müller").build();
when(documentRepository.findByOriginalFilename("rcv.pdf")).thenReturn(Optional.empty()); when(documentService.findByOriginalFilename("rcv.pdf")).thenReturn(Optional.empty());
when(documentRepository.save(any())).thenAnswer(inv -> inv.getArgument(0)); when(documentService.save(any())).thenAnswer(inv -> inv.getArgument(0));
when(personService.findOrCreateByAlias("Walter Müller")).thenReturn(receiver); when(personService.findOrCreateByAlias("Walter Müller")).thenReturn(receiver);
List<String> cells = List.of( List<String> cells = List.of(
"rcv.pdf", "", "", "", "", "Walter Müller", "", "", "", "", "", "", "", ""); "rcv.pdf", "", "", "", "", "Walter Müller", "", "", "", "", "", "", "", "");
service.importSingleDocument(cells, Optional.empty(), "rcv.pdf", "rcv"); service.importSingleDocument(cells, Optional.empty(), "rcv.pdf", "rcv");
verify(documentRepository).save(argThat(Document::isMetadataComplete)); verify(documentService).save(argThat(Document::isMetadataComplete));
} }
@Test @Test
void importSingleDocument_setsMetadataComplete_whenDateIsPresent() { void importSingleDocument_setsMetadataComplete_whenDateIsPresent() {
when(documentRepository.findByOriginalFilename("dated.pdf")).thenReturn(Optional.empty()); when(documentService.findByOriginalFilename("dated.pdf")).thenReturn(Optional.empty());
when(documentRepository.save(any())).thenAnswer(inv -> inv.getArgument(0)); when(documentService.save(any())).thenAnswer(inv -> inv.getArgument(0));
List<String> cells = List.of( List<String> cells = List.of(
"dated.pdf", "", "", "", "", "", "", "2024-03-15", "", "", "", "", "", ""); "dated.pdf", "", "", "", "", "", "", "2024-03-15", "", "", "", "", "", "");
service.importSingleDocument(cells, Optional.empty(), "dated.pdf", "dated"); service.importSingleDocument(cells, Optional.empty(), "dated.pdf", "dated");
verify(documentRepository).save(argThat(Document::isMetadataComplete)); verify(documentService).save(argThat(Document::isMetadataComplete));
} }
// ─── buildTitle — null location ─────────────────────────────────────────── // ─── buildTitle — null location ───────────────────────────────────────────