diff --git a/backend/src/test/java/org/raddatz/familienarchiv/document/DocumentTitleBackfillIntegrationTest.java b/backend/src/test/java/org/raddatz/familienarchiv/document/DocumentTitleBackfillIntegrationTest.java new file mode 100644 index 00000000..a209e1a2 --- /dev/null +++ b/backend/src/test/java/org/raddatz/familienarchiv/document/DocumentTitleBackfillIntegrationTest.java @@ -0,0 +1,90 @@ +package org.raddatz.familienarchiv.document; + +import org.junit.jupiter.api.Test; +import org.raddatz.familienarchiv.PostgresContainerConfig; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.bean.override.mockito.MockitoBean; +import org.springframework.transaction.annotation.Transactional; +import software.amazon.awssdk.services.s3.S3Client; + +import java.time.LocalDate; +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * End-to-end backfill against a real Postgres (#726, FR-003). H2 is unusable here — the + * {@code title} column is NOT NULL and the title-sync semantics depend on that — so this pins the + * behaviour on {@code postgres:16-alpine}: a stale auto-title is rewritten, the sweep is + * idempotent, prose is left alone, and the mechanical rename writes no {@code document_versions} + * rows. Permission enforcement (401/403) is covered faster by the {@code @WebMvcTest} slice in + * {@code AdminControllerTest}. + */ +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE) +@ActiveProfiles("test") +@Import(PostgresContainerConfig.class) +@Transactional +class DocumentTitleBackfillIntegrationTest { + + @MockitoBean S3Client s3Client; + @Autowired DocumentService documentService; + @Autowired DocumentRepository documentRepository; + @Autowired DocumentVersionRepository documentVersionRepository; + + private Document persist(String index, String title, LocalDate date, DatePrecision precision, String location) { + return documentRepository.save(Document.builder() + .originalFilename(index) + .title(title) + .documentDate(date) + .metaDatePrecision(precision) + .location(location) + .status(DocumentStatus.PLACEHOLDER) + .build()); + } + + @Test + void backfill_rewritesStaleAutoTitle() { + Document stale = persist("C-0029", "C-0029 – 2028 – Berlin", + LocalDate.of(1928, 1, 1), DatePrecision.YEAR, "Berlin"); + + int count = documentService.backfillTitles(); + + assertThat(count).isGreaterThanOrEqualTo(1); + assertThat(documentRepository.findById(stale.getId()).orElseThrow().getTitle()) + .isEqualTo("C-0029 – 1928 – Berlin"); + } + + @Test + void backfill_isIdempotent_secondRunChangesNothing() { + persist("C-0029", "C-0029 – 2028 – Berlin", LocalDate.of(1928, 1, 1), DatePrecision.YEAR, "Berlin"); + + documentService.backfillTitles(); + int secondRun = documentService.backfillTitles(); + + assertThat(secondRun).isZero(); + } + + @Test + void backfill_skipsProse() { + Document prose = persist("C-0030", "C-0030 – Brief an Mutter", + LocalDate.of(1928, 1, 1), DatePrecision.YEAR, null); + + documentService.backfillTitles(); + + assertThat(documentRepository.findById(prose.getId()).orElseThrow().getTitle()) + .isEqualTo("C-0030 – Brief an Mutter"); + } + + @Test + void backfill_addsNoDocumentVersionRows() { + persist("C-0029", "C-0029 – 2028 – Berlin", LocalDate.of(1928, 1, 1), DatePrecision.YEAR, "Berlin"); + long versionsBefore = documentVersionRepository.count(); + + documentService.backfillTitles(); + + assertThat(documentVersionRepository.count()).isEqualTo(versionsBefore); + } +}