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 387e3547..71c6dead 100644 --- a/backend/src/main/java/org/raddatz/familienarchiv/document/Document.java +++ b/backend/src/main/java/org/raddatz/familienarchiv/document/Document.java @@ -31,8 +31,7 @@ import java.util.UUID; @NamedEntityGraph(name = "Document.list", attributeNodes = { @NamedAttributeNode("sender"), @NamedAttributeNode("receivers"), - @NamedAttributeNode("tags"), - @NamedAttributeNode("trainingLabels") + @NamedAttributeNode("tags") }) @Entity @Table(name = "documents") 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); + } +}