test: increase coverage
This commit is contained in:
@@ -0,0 +1,359 @@
|
||||
package org.raddatz.familienarchiv.controller;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.raddatz.familienarchiv.config.SecurityConfig;
|
||||
import org.raddatz.familienarchiv.exception.DomainException;
|
||||
import org.raddatz.familienarchiv.exception.ErrorCode;
|
||||
import org.raddatz.familienarchiv.model.AppUser;
|
||||
import org.raddatz.familienarchiv.model.TranscriptionBlock;
|
||||
import org.raddatz.familienarchiv.model.TranscriptionBlockVersion;
|
||||
import org.raddatz.familienarchiv.security.PermissionAspect;
|
||||
import org.raddatz.familienarchiv.service.CustomUserDetailsService;
|
||||
import org.raddatz.familienarchiv.service.TranscriptionService;
|
||||
import org.raddatz.familienarchiv.service.UserService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.aop.AopAutoConfiguration;
|
||||
import org.springframework.boot.webmvc.test.autoconfigure.WebMvcTest;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.security.test.context.support.WithMockUser;
|
||||
import org.springframework.test.context.bean.override.mockito.MockitoBean;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
|
||||
|
||||
@WebMvcTest(TranscriptionBlockController.class)
|
||||
@Import({SecurityConfig.class, PermissionAspect.class, AopAutoConfiguration.class})
|
||||
class TranscriptionBlockControllerTest {
|
||||
|
||||
@Autowired MockMvc mockMvc;
|
||||
|
||||
@MockitoBean TranscriptionService transcriptionService;
|
||||
@MockitoBean UserService userService;
|
||||
@MockitoBean CustomUserDetailsService customUserDetailsService;
|
||||
|
||||
private static final UUID DOC_ID = UUID.randomUUID();
|
||||
private static final UUID BLOCK_ID = UUID.randomUUID();
|
||||
private static final String URL_BASE = "/api/documents/" + DOC_ID + "/transcription-blocks";
|
||||
private static final String URL_BLOCK = URL_BASE + "/" + BLOCK_ID;
|
||||
private static final String URL_REORDER = URL_BASE + "/reorder";
|
||||
private static final String URL_HISTORY = URL_BLOCK + "/history";
|
||||
|
||||
private static final String CREATE_JSON =
|
||||
"{\"pageNumber\":1,\"x\":0.1,\"y\":0.2,\"width\":0.3,\"height\":0.4,\"text\":\"Liebe Mutter,\"}";
|
||||
private static final String UPDATE_JSON =
|
||||
"{\"text\":\"Neue Fassung\",\"label\":\"Anrede\"}";
|
||||
private static final String REORDER_JSON =
|
||||
"{\"blockIds\":[\"" + UUID.randomUUID() + "\",\"" + UUID.randomUUID() + "\"]}";
|
||||
|
||||
private AppUser mockUser() {
|
||||
return AppUser.builder().id(UUID.randomUUID()).username("user").build();
|
||||
}
|
||||
|
||||
private TranscriptionBlock sampleBlock() {
|
||||
return TranscriptionBlock.builder()
|
||||
.id(BLOCK_ID).documentId(DOC_ID)
|
||||
.annotationId(UUID.randomUUID())
|
||||
.text("Liebe Mutter,").sortOrder(0).build();
|
||||
}
|
||||
|
||||
// ─── GET /api/documents/{id}/transcription-blocks ────────────────────────
|
||||
|
||||
@Test
|
||||
void listBlocks_returns401_whenUnauthenticated() throws Exception {
|
||||
mockMvc.perform(get(URL_BASE))
|
||||
.andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser
|
||||
void listBlocks_returns403_whenMissingReadAllPermission() throws Exception {
|
||||
mockMvc.perform(get(URL_BASE))
|
||||
.andExpect(status().isForbidden());
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser(authorities = "READ_ALL")
|
||||
void listBlocks_returns200_withBlocks_whenAuthorised() throws Exception {
|
||||
TranscriptionBlock b = sampleBlock();
|
||||
when(transcriptionService.listBlocks(DOC_ID)).thenReturn(List.of(b));
|
||||
|
||||
mockMvc.perform(get(URL_BASE))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$[0].text").value("Liebe Mutter,"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser(authorities = "READ_ALL")
|
||||
void listBlocks_returns200_withEmptyArray_whenNoBlocksExist() throws Exception {
|
||||
when(transcriptionService.listBlocks(any())).thenReturn(List.of());
|
||||
|
||||
mockMvc.perform(get(URL_BASE))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$").isArray())
|
||||
.andExpect(jsonPath("$").isEmpty());
|
||||
}
|
||||
|
||||
// ─── GET /api/documents/{id}/transcription-blocks/{blockId} ─────────────
|
||||
|
||||
@Test
|
||||
void getBlock_returns401_whenUnauthenticated() throws Exception {
|
||||
mockMvc.perform(get(URL_BLOCK))
|
||||
.andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser
|
||||
void getBlock_returns403_whenMissingReadAllPermission() throws Exception {
|
||||
mockMvc.perform(get(URL_BLOCK))
|
||||
.andExpect(status().isForbidden());
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser(authorities = "READ_ALL")
|
||||
void getBlock_returns200_withBlockData_whenFound() throws Exception {
|
||||
when(transcriptionService.getBlock(DOC_ID, BLOCK_ID)).thenReturn(sampleBlock());
|
||||
|
||||
mockMvc.perform(get(URL_BLOCK))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.id").value(BLOCK_ID.toString()))
|
||||
.andExpect(jsonPath("$.text").value("Liebe Mutter,"))
|
||||
.andExpect(jsonPath("$.sortOrder").value(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser(authorities = "READ_ALL")
|
||||
void getBlock_returns404_whenBlockDoesNotExist() throws Exception {
|
||||
when(transcriptionService.getBlock(any(), any()))
|
||||
.thenThrow(DomainException.notFound(ErrorCode.TRANSCRIPTION_BLOCK_NOT_FOUND, "not found"));
|
||||
|
||||
mockMvc.perform(get(URL_BLOCK))
|
||||
.andExpect(status().isNotFound())
|
||||
.andExpect(jsonPath("$.code").value("TRANSCRIPTION_BLOCK_NOT_FOUND"));
|
||||
}
|
||||
|
||||
// ─── POST /api/documents/{id}/transcription-blocks ───────────────────────
|
||||
|
||||
@Test
|
||||
void createBlock_returns401_whenUnauthenticated() throws Exception {
|
||||
mockMvc.perform(post(URL_BASE)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(CREATE_JSON))
|
||||
.andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser
|
||||
void createBlock_returns403_whenMissingWriteAllPermission() throws Exception {
|
||||
mockMvc.perform(post(URL_BASE)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(CREATE_JSON))
|
||||
.andExpect(status().isForbidden());
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser(authorities = "WRITE_ALL")
|
||||
void createBlock_returns201_withSavedBlock_whenAuthorised() throws Exception {
|
||||
when(userService.findByUsername(any())).thenReturn(mockUser());
|
||||
when(transcriptionService.createBlock(eq(DOC_ID), any(), any())).thenReturn(sampleBlock());
|
||||
|
||||
mockMvc.perform(post(URL_BASE)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(CREATE_JSON))
|
||||
.andExpect(status().isCreated())
|
||||
.andExpect(jsonPath("$.text").value("Liebe Mutter,"))
|
||||
.andExpect(jsonPath("$.documentId").value(DOC_ID.toString()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser(authorities = "WRITE_ALL")
|
||||
void createBlock_returns401_whenUserNotFoundInDatabase() throws Exception {
|
||||
when(userService.findByUsername(any())).thenReturn(null);
|
||||
|
||||
mockMvc.perform(post(URL_BASE)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(CREATE_JSON))
|
||||
.andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
// ─── PUT /api/documents/{id}/transcription-blocks/{blockId} ─────────────
|
||||
|
||||
@Test
|
||||
void updateBlock_returns401_whenUnauthenticated() throws Exception {
|
||||
mockMvc.perform(put(URL_BLOCK)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(UPDATE_JSON))
|
||||
.andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser
|
||||
void updateBlock_returns403_whenMissingWriteAllPermission() throws Exception {
|
||||
mockMvc.perform(put(URL_BLOCK)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(UPDATE_JSON))
|
||||
.andExpect(status().isForbidden());
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser(authorities = "WRITE_ALL")
|
||||
void updateBlock_returns200_withUpdatedBlock_whenAuthorised() throws Exception {
|
||||
TranscriptionBlock updated = sampleBlock();
|
||||
updated.setText("Neue Fassung");
|
||||
updated.setLabel("Anrede");
|
||||
|
||||
when(userService.findByUsername(any())).thenReturn(mockUser());
|
||||
when(transcriptionService.updateBlock(eq(DOC_ID), eq(BLOCK_ID), any(), any()))
|
||||
.thenReturn(updated);
|
||||
|
||||
mockMvc.perform(put(URL_BLOCK)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(UPDATE_JSON))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.text").value("Neue Fassung"))
|
||||
.andExpect(jsonPath("$.label").value("Anrede"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser(authorities = "WRITE_ALL")
|
||||
void updateBlock_returns404_whenBlockDoesNotExist() throws Exception {
|
||||
when(userService.findByUsername(any())).thenReturn(mockUser());
|
||||
when(transcriptionService.updateBlock(any(), any(), any(), any()))
|
||||
.thenThrow(DomainException.notFound(ErrorCode.TRANSCRIPTION_BLOCK_NOT_FOUND, "not found"));
|
||||
|
||||
mockMvc.perform(put(URL_BLOCK)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(UPDATE_JSON))
|
||||
.andExpect(status().isNotFound());
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser(authorities = "WRITE_ALL")
|
||||
void updateBlock_returns401_whenUserNotFoundInDatabase() throws Exception {
|
||||
when(userService.findByUsername(any())).thenReturn(null);
|
||||
|
||||
mockMvc.perform(put(URL_BLOCK)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(UPDATE_JSON))
|
||||
.andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
// ─── DELETE /api/documents/{id}/transcription-blocks/{blockId} ───────────
|
||||
|
||||
@Test
|
||||
void deleteBlock_returns401_whenUnauthenticated() throws Exception {
|
||||
mockMvc.perform(delete(URL_BLOCK))
|
||||
.andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser
|
||||
void deleteBlock_returns403_whenMissingWriteAllPermission() throws Exception {
|
||||
mockMvc.perform(delete(URL_BLOCK))
|
||||
.andExpect(status().isForbidden());
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser(authorities = "WRITE_ALL")
|
||||
void deleteBlock_returns204_whenAuthorised() throws Exception {
|
||||
mockMvc.perform(delete(URL_BLOCK))
|
||||
.andExpect(status().isNoContent());
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser(authorities = "WRITE_ALL")
|
||||
void deleteBlock_returns404_whenBlockDoesNotExist() throws Exception {
|
||||
org.mockito.Mockito.doThrow(
|
||||
DomainException.notFound(ErrorCode.TRANSCRIPTION_BLOCK_NOT_FOUND, "not found"))
|
||||
.when(transcriptionService).deleteBlock(any(), any());
|
||||
|
||||
mockMvc.perform(delete(URL_BLOCK))
|
||||
.andExpect(status().isNotFound());
|
||||
}
|
||||
|
||||
// ─── PUT /api/documents/{id}/transcription-blocks/reorder ────────────────
|
||||
|
||||
@Test
|
||||
void reorderBlocks_returns401_whenUnauthenticated() throws Exception {
|
||||
mockMvc.perform(put(URL_REORDER)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(REORDER_JSON))
|
||||
.andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser
|
||||
void reorderBlocks_returns403_whenMissingWriteAllPermission() throws Exception {
|
||||
mockMvc.perform(put(URL_REORDER)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(REORDER_JSON))
|
||||
.andExpect(status().isForbidden());
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser(authorities = "WRITE_ALL")
|
||||
void reorderBlocks_returns200_withReorderedBlocks_whenAuthorised() throws Exception {
|
||||
when(transcriptionService.listBlocks(DOC_ID)).thenReturn(List.of(sampleBlock()));
|
||||
|
||||
mockMvc.perform(put(URL_REORDER)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(REORDER_JSON))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$").isArray());
|
||||
}
|
||||
|
||||
// ─── GET /api/documents/{id}/transcription-blocks/{blockId}/history ──────
|
||||
|
||||
@Test
|
||||
void getBlockHistory_returns401_whenUnauthenticated() throws Exception {
|
||||
mockMvc.perform(get(URL_HISTORY))
|
||||
.andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser
|
||||
void getBlockHistory_returns403_whenMissingReadAllPermission() throws Exception {
|
||||
mockMvc.perform(get(URL_HISTORY))
|
||||
.andExpect(status().isForbidden());
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser(authorities = "READ_ALL")
|
||||
void getBlockHistory_returns200_withVersionList_whenAuthorised() throws Exception {
|
||||
TranscriptionBlockVersion v = TranscriptionBlockVersion.builder()
|
||||
.id(UUID.randomUUID()).blockId(BLOCK_ID).text("v1").build();
|
||||
when(transcriptionService.getBlockHistory(DOC_ID, BLOCK_ID)).thenReturn(List.of(v));
|
||||
|
||||
mockMvc.perform(get(URL_HISTORY))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$[0].text").value("v1"))
|
||||
.andExpect(jsonPath("$[0].blockId").value(BLOCK_ID.toString()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser(authorities = "READ_ALL")
|
||||
void getBlockHistory_returns404_whenBlockDoesNotExist() throws Exception {
|
||||
when(transcriptionService.getBlockHistory(any(), any()))
|
||||
.thenThrow(DomainException.notFound(ErrorCode.TRANSCRIPTION_BLOCK_NOT_FOUND, "not found"));
|
||||
|
||||
mockMvc.perform(get(URL_HISTORY))
|
||||
.andExpect(status().isNotFound());
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser(authorities = "READ_ALL")
|
||||
void getBlockHistory_returns200_withEmptyList_whenNoVersionsExist() throws Exception {
|
||||
when(transcriptionService.getBlockHistory(any(), any())).thenReturn(List.of());
|
||||
|
||||
mockMvc.perform(get(URL_HISTORY))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$").isEmpty());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
package org.raddatz.familienarchiv.repository;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.raddatz.familienarchiv.PostgresContainerConfig;
|
||||
import org.raddatz.familienarchiv.config.FlywayConfig;
|
||||
import org.raddatz.familienarchiv.model.*;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.jdbc.test.autoconfigure.AutoConfigureTestDatabase;
|
||||
import org.springframework.boot.data.jpa.test.autoconfigure.DataJpaTest;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@DataJpaTest
|
||||
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
|
||||
@Import({PostgresContainerConfig.class, FlywayConfig.class})
|
||||
class TranscriptionBlockRepositoryTest {
|
||||
|
||||
@Autowired TranscriptionBlockRepository blockRepository;
|
||||
@Autowired TranscriptionBlockVersionRepository versionRepository;
|
||||
@Autowired DocumentRepository documentRepository;
|
||||
@Autowired AnnotationRepository annotationRepository;
|
||||
@Autowired EntityManager em;
|
||||
|
||||
private UUID documentId;
|
||||
private UUID annotationId;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
Document doc = documentRepository.save(Document.builder()
|
||||
.title("Testbrief")
|
||||
.originalFilename("brief.pdf")
|
||||
.status(DocumentStatus.UPLOADED)
|
||||
.build());
|
||||
documentId = doc.getId();
|
||||
|
||||
DocumentAnnotation annotation = annotationRepository.save(DocumentAnnotation.builder()
|
||||
.documentId(documentId)
|
||||
.pageNumber(1)
|
||||
.x(0.1).y(0.2).width(0.3).height(0.4)
|
||||
.color("#00C7B1")
|
||||
.build());
|
||||
annotationId = annotation.getId();
|
||||
}
|
||||
|
||||
// ─── findByDocumentIdOrderBySortOrderAsc ─────────────────────────────────
|
||||
|
||||
@Test
|
||||
void findByDocumentIdOrderBySortOrderAsc_returnsBlocksInSortOrder() {
|
||||
blockRepository.save(block("Block B", 1));
|
||||
blockRepository.save(block("Block A", 0));
|
||||
blockRepository.save(block("Block C", 2));
|
||||
|
||||
List<TranscriptionBlock> result = blockRepository.findByDocumentIdOrderBySortOrderAsc(documentId);
|
||||
|
||||
assertThat(result).hasSize(3);
|
||||
assertThat(result.get(0).getText()).isEqualTo("Block A");
|
||||
assertThat(result.get(1).getText()).isEqualTo("Block B");
|
||||
assertThat(result.get(2).getText()).isEqualTo("Block C");
|
||||
}
|
||||
|
||||
@Test
|
||||
void findByDocumentIdOrderBySortOrderAsc_returnsEmptyList_whenNoBlocksForDocument() {
|
||||
UUID otherId = UUID.randomUUID();
|
||||
List<TranscriptionBlock> result = blockRepository.findByDocumentIdOrderBySortOrderAsc(otherId);
|
||||
assertThat(result).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void findByDocumentIdOrderBySortOrderAsc_doesNotReturnBlocksFromOtherDocument() {
|
||||
blockRepository.save(block("My block", 0));
|
||||
|
||||
Document other = documentRepository.save(Document.builder()
|
||||
.title("Anderer Brief").originalFilename("other.pdf").status(DocumentStatus.PLACEHOLDER).build());
|
||||
|
||||
List<TranscriptionBlock> result = blockRepository.findByDocumentIdOrderBySortOrderAsc(other.getId());
|
||||
assertThat(result).isEmpty();
|
||||
}
|
||||
|
||||
// ─── findByIdAndDocumentId ────────────────────────────────────────────────
|
||||
|
||||
@Test
|
||||
void findByIdAndDocumentId_returnsBlock_whenBothMatch() {
|
||||
TranscriptionBlock saved = blockRepository.save(block("Liebe Tante,", 0));
|
||||
|
||||
Optional<TranscriptionBlock> found = blockRepository.findByIdAndDocumentId(saved.getId(), documentId);
|
||||
|
||||
assertThat(found).isPresent();
|
||||
assertThat(found.get().getText()).isEqualTo("Liebe Tante,");
|
||||
}
|
||||
|
||||
@Test
|
||||
void findByIdAndDocumentId_returnsEmpty_whenDocumentIdDoesNotMatch() {
|
||||
TranscriptionBlock saved = blockRepository.save(block("Liebe Tante,", 0));
|
||||
|
||||
Optional<TranscriptionBlock> found = blockRepository.findByIdAndDocumentId(saved.getId(), UUID.randomUUID());
|
||||
|
||||
assertThat(found).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void findByIdAndDocumentId_returnsEmpty_whenBlockIdDoesNotExist() {
|
||||
Optional<TranscriptionBlock> found = blockRepository.findByIdAndDocumentId(UUID.randomUUID(), documentId);
|
||||
assertThat(found).isEmpty();
|
||||
}
|
||||
|
||||
// ─── countByDocumentId ────────────────────────────────────────────────────
|
||||
|
||||
@Test
|
||||
void countByDocumentId_returnsZero_whenNoBlocksExist() {
|
||||
assertThat(blockRepository.countByDocumentId(documentId)).isZero();
|
||||
}
|
||||
|
||||
@Test
|
||||
void countByDocumentId_returnsCorrectCount_afterMultipleSaves() {
|
||||
blockRepository.save(block("Block 1", 0));
|
||||
blockRepository.save(block("Block 2", 1));
|
||||
blockRepository.save(block("Block 3", 2));
|
||||
|
||||
assertThat(blockRepository.countByDocumentId(documentId)).isEqualTo(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
void countByDocumentId_doesNotCountBlocksFromOtherDocument() {
|
||||
blockRepository.save(block("Block 1", 0));
|
||||
|
||||
UUID otherId = UUID.randomUUID();
|
||||
assertThat(blockRepository.countByDocumentId(otherId)).isZero();
|
||||
}
|
||||
|
||||
// ─── version (optimistic lock) ────────────────────────────────────────────
|
||||
|
||||
@Test
|
||||
void version_startsAtZero_andIncrementsOnEachSave() {
|
||||
TranscriptionBlock saved = blockRepository.saveAndFlush(block("initial", 0));
|
||||
assertThat(saved.getVersion()).isZero();
|
||||
|
||||
saved.setText("updated");
|
||||
TranscriptionBlock updated = blockRepository.saveAndFlush(saved);
|
||||
assertThat(updated.getVersion()).isEqualTo(1);
|
||||
}
|
||||
|
||||
// ─── cascade: deleting a block cascades to its versions ──────────────────
|
||||
|
||||
@Test
|
||||
@Transactional
|
||||
void delete_cascadesToVersions() {
|
||||
TranscriptionBlock block = blockRepository.saveAndFlush(block("text", 0));
|
||||
versionRepository.saveAndFlush(TranscriptionBlockVersion.builder()
|
||||
.blockId(block.getId()).text("text").build());
|
||||
|
||||
assertThat(versionRepository.findByBlockIdOrderByChangedAtDesc(block.getId())).hasSize(1);
|
||||
|
||||
blockRepository.delete(block);
|
||||
blockRepository.flush();
|
||||
em.clear();
|
||||
|
||||
assertThat(versionRepository.findByBlockIdOrderByChangedAtDesc(block.getId())).isEmpty();
|
||||
}
|
||||
|
||||
// ─── cascade: deleting a document cascades to its blocks ─────────────────
|
||||
|
||||
@Test
|
||||
@Transactional
|
||||
void deleteDocument_cascadesToBlocks() {
|
||||
blockRepository.saveAndFlush(block("text", 0));
|
||||
assertThat(blockRepository.findByDocumentIdOrderBySortOrderAsc(documentId)).hasSize(1);
|
||||
|
||||
documentRepository.deleteById(documentId);
|
||||
documentRepository.flush();
|
||||
em.clear();
|
||||
|
||||
assertThat(blockRepository.findByDocumentIdOrderBySortOrderAsc(documentId)).isEmpty();
|
||||
}
|
||||
|
||||
// ─── helper ──────────────────────────────────────────────────────────────
|
||||
|
||||
private TranscriptionBlock block(String text, int sortOrder) {
|
||||
return TranscriptionBlock.builder()
|
||||
.annotationId(annotationId)
|
||||
.documentId(documentId)
|
||||
.text(text)
|
||||
.sortOrder(sortOrder)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user