test(transcription): add TranscriptionServiceTest with 13 unit tests
Some checks failed
CI / Unit & Component Tests (push) Failing after 1m20s
CI / Backend Unit Tests (push) Failing after 2m32s
CI / E2E Tests (push) Failing after 1h22m5s
CI / Unit & Component Tests (pull_request) Failing after 1m35s
CI / Backend Unit Tests (pull_request) Failing after 2m46s
CI / E2E Tests (pull_request) Failing after 1h24m55s

Tests cover: getBlock (found, not found), createBlock (creates annotation +
block + version), updateBlock (text + label), deleteBlock (deletes block +
annotation, not found), reorderBlocks, getBlockHistory, sanitizeText (null,
max length, plain text preservation), listBlocks

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-04-05 11:46:16 +02:00
parent 6463a32dfc
commit 51c799e20e

View File

@@ -0,0 +1,247 @@
package org.raddatz.familienarchiv.service;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.raddatz.familienarchiv.dto.CreateAnnotationDTO;
import org.raddatz.familienarchiv.dto.CreateTranscriptionBlockDTO;
import org.raddatz.familienarchiv.dto.ReorderTranscriptionBlocksDTO;
import org.raddatz.familienarchiv.dto.UpdateTranscriptionBlockDTO;
import org.raddatz.familienarchiv.exception.DomainException;
import org.raddatz.familienarchiv.model.Document;
import org.raddatz.familienarchiv.model.DocumentAnnotation;
import org.raddatz.familienarchiv.model.TranscriptionBlock;
import org.raddatz.familienarchiv.model.TranscriptionBlockVersion;
import org.raddatz.familienarchiv.repository.AnnotationRepository;
import org.raddatz.familienarchiv.repository.TranscriptionBlockRepository;
import org.raddatz.familienarchiv.repository.TranscriptionBlockVersionRepository;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.springframework.http.HttpStatus.NOT_FOUND;
@ExtendWith(MockitoExtension.class)
class TranscriptionServiceTest {
@Mock TranscriptionBlockRepository blockRepository;
@Mock TranscriptionBlockVersionRepository versionRepository;
@Mock AnnotationRepository annotationRepository;
@Mock AnnotationService annotationService;
@Mock DocumentService documentService;
@InjectMocks TranscriptionService transcriptionService;
// ─── getBlock ────────────────────────────────────────────────────────────────
@Test
void getBlock_throwsNotFound_whenBlockDoesNotExist() {
UUID docId = UUID.randomUUID();
UUID blockId = UUID.randomUUID();
when(blockRepository.findByIdAndDocumentId(blockId, docId)).thenReturn(Optional.empty());
assertThatThrownBy(() -> transcriptionService.getBlock(docId, blockId))
.isInstanceOf(DomainException.class)
.satisfies(e -> assertThat(((DomainException) e).getStatus()).isEqualTo(NOT_FOUND));
}
@Test
void getBlock_returnsBlock_whenExists() {
UUID docId = UUID.randomUUID();
UUID blockId = UUID.randomUUID();
TranscriptionBlock block = TranscriptionBlock.builder()
.id(blockId).documentId(docId).text("hello").build();
when(blockRepository.findByIdAndDocumentId(blockId, docId)).thenReturn(Optional.of(block));
TranscriptionBlock result = transcriptionService.getBlock(docId, blockId);
assertThat(result).isEqualTo(block);
}
// ─── createBlock ─────────────────────────────────────────────────────────────
@Test
void createBlock_createsAnnotationAndBlockAndVersion() {
UUID docId = UUID.randomUUID();
UUID userId = UUID.randomUUID();
UUID annotId = UUID.randomUUID();
Document doc = Document.builder().id(docId).fileHash("hash123").build();
when(documentService.getDocumentById(docId)).thenReturn(doc);
DocumentAnnotation annotation = DocumentAnnotation.builder().id(annotId).build();
when(annotationService.createAnnotation(eq(docId), any(CreateAnnotationDTO.class), eq(userId), eq("hash123")))
.thenReturn(annotation);
when(blockRepository.countByDocumentId(docId)).thenReturn(0);
when(blockRepository.save(any())).thenAnswer(inv -> {
TranscriptionBlock b = inv.getArgument(0);
b.setId(UUID.randomUUID());
return b;
});
CreateTranscriptionBlockDTO dto = new CreateTranscriptionBlockDTO(1, 0.1, 0.2, 0.3, 0.4, "hello", null);
TranscriptionBlock result = transcriptionService.createBlock(docId, dto, userId);
assertThat(result.getAnnotationId()).isEqualTo(annotId);
assertThat(result.getText()).isEqualTo("hello");
assertThat(result.getSortOrder()).isZero();
assertThat(result.getCreatedBy()).isEqualTo(userId);
verify(versionRepository).save(any(TranscriptionBlockVersion.class));
}
// ─── updateBlock ─────────────────────────────────────────────────────────────
@Test
void updateBlock_updatesTextAndSavesVersion() {
UUID docId = UUID.randomUUID();
UUID blockId = UUID.randomUUID();
UUID userId = UUID.randomUUID();
TranscriptionBlock block = TranscriptionBlock.builder()
.id(blockId).documentId(docId).text("old").build();
when(blockRepository.findByIdAndDocumentId(blockId, docId)).thenReturn(Optional.of(block));
when(blockRepository.save(any())).thenAnswer(inv -> inv.getArgument(0));
UpdateTranscriptionBlockDTO dto = new UpdateTranscriptionBlockDTO("new text", null);
TranscriptionBlock result = transcriptionService.updateBlock(docId, blockId, dto, userId);
assertThat(result.getText()).isEqualTo("new text");
assertThat(result.getUpdatedBy()).isEqualTo(userId);
verify(versionRepository).save(any(TranscriptionBlockVersion.class));
}
@Test
void updateBlock_updatesLabel_whenProvided() {
UUID docId = UUID.randomUUID();
UUID blockId = UUID.randomUUID();
TranscriptionBlock block = TranscriptionBlock.builder()
.id(blockId).documentId(docId).text("text").label("old label").build();
when(blockRepository.findByIdAndDocumentId(blockId, docId)).thenReturn(Optional.of(block));
when(blockRepository.save(any())).thenAnswer(inv -> inv.getArgument(0));
UpdateTranscriptionBlockDTO dto = new UpdateTranscriptionBlockDTO("text", "Anrede");
TranscriptionBlock result = transcriptionService.updateBlock(docId, blockId, dto, UUID.randomUUID());
assertThat(result.getLabel()).isEqualTo("Anrede");
}
// ─── deleteBlock ─────────────────────────────────────────────────────────────
@Test
void deleteBlock_deletesBlockAndAnnotation() {
UUID docId = UUID.randomUUID();
UUID blockId = UUID.randomUUID();
UUID annotId = UUID.randomUUID();
TranscriptionBlock block = TranscriptionBlock.builder()
.id(blockId).documentId(docId).annotationId(annotId).build();
when(blockRepository.findByIdAndDocumentId(blockId, docId)).thenReturn(Optional.of(block));
transcriptionService.deleteBlock(docId, blockId);
verify(blockRepository).delete(block);
verify(blockRepository).flush();
verify(annotationRepository).deleteById(annotId);
}
@Test
void deleteBlock_throwsNotFound_whenBlockMissing() {
UUID docId = UUID.randomUUID();
UUID blockId = UUID.randomUUID();
when(blockRepository.findByIdAndDocumentId(blockId, docId)).thenReturn(Optional.empty());
assertThatThrownBy(() -> transcriptionService.deleteBlock(docId, blockId))
.isInstanceOf(DomainException.class)
.satisfies(e -> assertThat(((DomainException) e).getStatus()).isEqualTo(NOT_FOUND));
}
// ─── reorderBlocks ───────────────────────────────────────────────────────────
@Test
void reorderBlocks_updatesSortOrder() {
UUID docId = UUID.randomUUID();
UUID id1 = UUID.randomUUID();
UUID id2 = UUID.randomUUID();
TranscriptionBlock block1 = TranscriptionBlock.builder()
.id(id1).documentId(docId).sortOrder(0).build();
TranscriptionBlock block2 = TranscriptionBlock.builder()
.id(id2).documentId(docId).sortOrder(1).build();
when(blockRepository.findByIdAndDocumentId(id2, docId)).thenReturn(Optional.of(block2));
when(blockRepository.findByIdAndDocumentId(id1, docId)).thenReturn(Optional.of(block1));
when(blockRepository.save(any())).thenAnswer(inv -> inv.getArgument(0));
when(blockRepository.findByDocumentIdOrderBySortOrderAsc(docId)).thenReturn(List.of(block2, block1));
ReorderTranscriptionBlocksDTO dto = new ReorderTranscriptionBlocksDTO(List.of(id2, id1));
transcriptionService.reorderBlocks(docId, dto);
assertThat(block2.getSortOrder()).isZero();
assertThat(block1.getSortOrder()).isEqualTo(1);
}
// ─── getBlockHistory ─────────────────────────────────────────────────────────
@Test
void getBlockHistory_returnsVersionsForBlock() {
UUID docId = UUID.randomUUID();
UUID blockId = UUID.randomUUID();
TranscriptionBlock block = TranscriptionBlock.builder()
.id(blockId).documentId(docId).build();
when(blockRepository.findByIdAndDocumentId(blockId, docId)).thenReturn(Optional.of(block));
TranscriptionBlockVersion v = TranscriptionBlockVersion.builder()
.id(UUID.randomUUID()).blockId(blockId).text("ver1").build();
when(versionRepository.findByBlockIdOrderByChangedAtDesc(blockId)).thenReturn(List.of(v));
List<TranscriptionBlockVersion> result = transcriptionService.getBlockHistory(docId, blockId);
assertThat(result).containsExactly(v);
}
// ─── sanitizeText ────────────────────────────────────────────────────────────
@Test
void sanitizeText_returnsEmptyString_forNull() {
assertThat(transcriptionService.sanitizeText(null)).isEmpty();
}
@Test
void sanitizeText_truncatesAtMaxLength() {
String longText = "a".repeat(15_000);
String result = transcriptionService.sanitizeText(longText);
assertThat(result).hasSize(10_000);
}
@Test
void sanitizeText_preservesPlainText() {
assertThat(transcriptionService.sanitizeText("Liebe Mutter,")).isEqualTo("Liebe Mutter,");
}
// ─── listBlocks ──────────────────────────────────────────────────────────────
@Test
void listBlocks_returnsBlocksOrderedBySortOrder() {
UUID docId = UUID.randomUUID();
TranscriptionBlock b = TranscriptionBlock.builder()
.id(UUID.randomUUID()).documentId(docId).sortOrder(0).build();
when(blockRepository.findByDocumentIdOrderBySortOrderAsc(docId)).thenReturn(List.of(b));
assertThat(transcriptionService.listBlocks(docId)).containsExactly(b);
}
}