From 0f32332b63047b95278a01f92e7bd790c1dae6b9 Mon Sep 17 00:00:00 2001 From: Marcel Date: Fri, 22 May 2026 17:49:01 +0200 Subject: [PATCH] test(document): add LazyInit guard + detail regression tests; prune Document.list graph Remove trainingLabels from Document.list entity graph now that DocumentListItem does not touch that association. Integration tests guard against future LazyInitializationException regressions and confirm Document.full still loads trainingLabels for the detail endpoint. Co-Authored-By: Claude Sonnet 4.6 --- .../familienarchiv/document/Document.java | 1 + .../DocumentListItemIntegrationTest.java | 98 +++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 backend/src/test/java/org/raddatz/familienarchiv/document/DocumentListItemIntegrationTest.java diff --git a/backend/src/main/java/org/raddatz/familienarchiv/document/Document.java b/backend/src/main/java/org/raddatz/familienarchiv/document/Document.java index 3e8cac51..103c92e4 100644 --- a/backend/src/main/java/org/raddatz/familienarchiv/document/Document.java +++ b/backend/src/main/java/org/raddatz/familienarchiv/document/Document.java @@ -29,6 +29,7 @@ import java.util.UUID; }) @NamedEntityGraph(name = "Document.list", attributeNodes = { @NamedAttributeNode("sender"), + @NamedAttributeNode("receivers"), @NamedAttributeNode("tags") }) @Entity diff --git a/backend/src/test/java/org/raddatz/familienarchiv/document/DocumentListItemIntegrationTest.java b/backend/src/test/java/org/raddatz/familienarchiv/document/DocumentListItemIntegrationTest.java new file mode 100644 index 00000000..485b865e --- /dev/null +++ b/backend/src/test/java/org/raddatz/familienarchiv/document/DocumentListItemIntegrationTest.java @@ -0,0 +1,98 @@ +package org.raddatz.familienarchiv.document; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.raddatz.familienarchiv.PostgresContainerConfig; +import org.raddatz.familienarchiv.audit.AuditLogQueryService; +import org.raddatz.familienarchiv.ocr.TrainingLabel; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Import; +import org.springframework.data.domain.PageRequest; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.bean.override.mockito.MockitoBean; +import software.amazon.awssdk.services.s3.S3Client; + +import java.util.HashSet; +import java.util.Set; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; + +/** + * AC #2: Document with trainingLabels does not cause LazyInitializationException in search. + * AC #3: Detail API still returns trainingLabels after the Document.list graph change. + */ +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE) +@ActiveProfiles("test") +@Import(PostgresContainerConfig.class) +class DocumentListItemIntegrationTest { + + @MockitoBean + S3Client s3Client; + + @MockitoBean + AuditLogQueryService auditLogQueryService; + + @Autowired + DocumentRepository documentRepository; + + @Autowired + DocumentService documentService; + + @AfterEach + void cleanup() { + documentRepository.deleteAll(); + } + + @Test + void search_doesNotThrow_whenDocumentHasTrainingLabels() { + documentRepository.save(Document.builder() + .title("Kurrent Brief") + .originalFilename("kurrent.pdf") + .status(DocumentStatus.UPLOADED) + .trainingLabels(new HashSet<>(Set.of(TrainingLabel.KURRENT_RECOGNITION))) + .build()); + + assertThatCode(() -> documentService.searchDocuments( + null, null, null, null, null, null, null, null, + DocumentSort.DATE, "DESC", null, + PageRequest.of(0, 50))) + .doesNotThrowAnyException(); + } + + @Test + void search_returns_list_item_without_sensitive_fields_when_document_has_training_labels() { + documentRepository.save(Document.builder() + .title("Kurrent Brief") + .originalFilename("kurrent2.pdf") + .status(DocumentStatus.UPLOADED) + .trainingLabels(new HashSet<>(Set.of(TrainingLabel.KURRENT_RECOGNITION))) + .build()); + + DocumentSearchResult result = documentService.searchDocuments( + null, null, null, null, null, null, null, null, + DocumentSort.DATE, "DESC", null, + PageRequest.of(0, 50)); + + assertThat(result.totalElements()).isGreaterThan(0); + DocumentListItem item = result.items().get(0); + assertThat(item.id()).isNotNull(); + assertThat(item.title()).isEqualTo("Kurrent Brief"); + } + + @Test + void detail_stillReturnsTrainingLabels() { + Document saved = documentRepository.save(Document.builder() + .title("Detail Test") + .originalFilename("detail_test.pdf") + .status(DocumentStatus.UPLOADED) + .trainingLabels(new HashSet<>(Set.of(TrainingLabel.KURRENT_RECOGNITION))) + .build()); + + // Document.full entity graph (used by findById) must still load trainingLabels + Document loaded = documentRepository.findById(saved.getId()).orElseThrow(); + + assertThat(loaded.getTrainingLabels()).containsExactly(TrainingLabel.KURRENT_RECOGNITION); + } +}