diff --git a/backend/src/main/java/org/raddatz/familienarchiv/document/DocumentService.java b/backend/src/main/java/org/raddatz/familienarchiv/document/DocumentService.java index e947ddc8..2f3edbc2 100644 --- a/backend/src/main/java/org/raddatz/familienarchiv/document/DocumentService.java +++ b/backend/src/main/java/org/raddatz/familienarchiv/document/DocumentService.java @@ -486,6 +486,12 @@ public class DocumentService { throw DomainException.badRequest(ErrorCode.INVALID_DATE_RANGE, "meta_date_end must not be before meta_date"); } + // Mirrors chk_meta_date_end_only_for_range. API-only: the edit form clears the + // end field off-RANGE, so this branch closes the same 500 class for direct clients. + if (doc.getMetaDateEnd() != null && doc.getMetaDatePrecision() != DatePrecision.RANGE) { + throw DomainException.badRequest(ErrorCode.INVALID_DATE_RANGE, + "meta_date_end is only allowed when meta_date_precision is RANGE"); + } } @Transactional diff --git a/backend/src/test/java/org/raddatz/familienarchiv/document/DocumentServiceTest.java b/backend/src/test/java/org/raddatz/familienarchiv/document/DocumentServiceTest.java index 58b64fd4..4c11bd30 100644 --- a/backend/src/test/java/org/raddatz/familienarchiv/document/DocumentServiceTest.java +++ b/backend/src/test/java/org/raddatz/familienarchiv/document/DocumentServiceTest.java @@ -204,10 +204,12 @@ class DocumentServiceTest { // Editing a doc (e.g. fixing a location typo) without touching the precision // controls must NOT fabricate a precision. The form omits the three precision // fields → they arrive null on the DTO → the stored values must be preserved. + // Stored combo is RANGE + end: the only DB-valid way to have a non-null end + // (chk_meta_date_end_only_for_range), so the carried-over state passes the guard. UUID id = UUID.randomUUID(); Document doc = Document.builder() .id(id) - .metaDatePrecision(DatePrecision.MONTH) + .metaDatePrecision(DatePrecision.RANGE) .metaDateEnd(LocalDate.of(1916, 6, 30)) .metaDateRaw("Juni 1916") .receivers(new HashSet<>()) @@ -221,7 +223,7 @@ class DocumentServiceTest { documentService.updateDocument(id, dto, null, null); - assertThat(doc.getMetaDatePrecision()).isEqualTo(DatePrecision.MONTH); + assertThat(doc.getMetaDatePrecision()).isEqualTo(DatePrecision.RANGE); assertThat(doc.getMetaDateEnd()).isEqualTo(LocalDate.of(1916, 6, 30)); assertThat(doc.getMetaDateRaw()).isEqualTo("Juni 1916"); } @@ -313,6 +315,26 @@ class DocumentServiceTest { verify(documentRepository, atLeastOnce()).save(any()); } + @Test + void updateDocument_rejectsEndDate_whenPrecisionNotRange() { + // AC6: an end date only makes sense for RANGE (mirrors chk_meta_date_end_only_for_range). + // API-only — the edit form clears the end field off-RANGE — so close the 500 class here too. + UUID id = UUID.randomUUID(); + Document doc = docForRangeUpdate(id); + when(documentRepository.findById(id)).thenReturn(Optional.of(doc)); + + DocumentUpdateDTO dto = new DocumentUpdateDTO(); + dto.setDocumentDate(LocalDate.of(1917, 1, 10)); + dto.setMetaDatePrecision(DatePrecision.MONTH); + dto.setMetaDateEnd(LocalDate.of(1917, 1, 31)); + + assertThatThrownBy(() -> documentService.updateDocument(id, dto, null, null)) + .isInstanceOf(DomainException.class) + .extracting(e -> ((DomainException) e).getCode()) + .isEqualTo(ErrorCode.INVALID_DATE_RANGE); + verify(documentRepository, never()).save(any()); + } + // ─── deleteTagCascading ─────────────────────────────────────────────────── @Test