From 4e8df66a79e4a70ae4c6e99dd2b7d7734eaf1cf9 Mon Sep 17 00:00:00 2001 From: Marcel Date: Tue, 28 Apr 2026 20:12:53 +0200 Subject: [PATCH] test(transcription): 400 + VALIDATION_ERROR when mention displayName exceeds 200 chars Wires @Valid on the @RequestBody parameter of TranscriptionBlockController's createBlock and updateBlock methods so JSR-303 actually fires for incoming DTOs. With @Valid on the field-level mentionedPersons in the DTO (added in the previous commit), Jakarta validation now recurses into each PersonMention element and rejects displayName values past the @Size(max=200) ceiling. The test posts a 201-char displayName and asserts the global handler maps the resulting MethodArgumentNotValidException to 400 + code:VALIDATION_ERROR. Refs #362 Co-Authored-By: Claude Opus 4.7 --- .../controller/TranscriptionBlockController.java | 5 +++-- .../TranscriptionBlockControllerTest.java | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/backend/src/main/java/org/raddatz/familienarchiv/controller/TranscriptionBlockController.java b/backend/src/main/java/org/raddatz/familienarchiv/controller/TranscriptionBlockController.java index 4d206cd7..da162ffa 100644 --- a/backend/src/main/java/org/raddatz/familienarchiv/controller/TranscriptionBlockController.java +++ b/backend/src/main/java/org/raddatz/familienarchiv/controller/TranscriptionBlockController.java @@ -1,5 +1,6 @@ package org.raddatz.familienarchiv.controller; +import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.raddatz.familienarchiv.dto.CreateTranscriptionBlockDTO; @@ -45,7 +46,7 @@ public class TranscriptionBlockController { @RequirePermission(Permission.WRITE_ALL) public TranscriptionBlock createBlock( @PathVariable UUID documentId, - @RequestBody CreateTranscriptionBlockDTO dto, + @Valid @RequestBody CreateTranscriptionBlockDTO dto, Authentication authentication) { UUID userId = requireUserId(authentication); return transcriptionService.createBlock(documentId, dto, userId); @@ -56,7 +57,7 @@ public class TranscriptionBlockController { public TranscriptionBlock updateBlock( @PathVariable UUID documentId, @PathVariable UUID blockId, - @RequestBody UpdateTranscriptionBlockDTO dto, + @Valid @RequestBody UpdateTranscriptionBlockDTO dto, Authentication authentication) { UUID userId = requireUserId(authentication); return transcriptionService.updateBlock(documentId, blockId, dto, userId); diff --git a/backend/src/test/java/org/raddatz/familienarchiv/controller/TranscriptionBlockControllerTest.java b/backend/src/test/java/org/raddatz/familienarchiv/controller/TranscriptionBlockControllerTest.java index 255d19ee..8b75dae3 100644 --- a/backend/src/test/java/org/raddatz/familienarchiv/controller/TranscriptionBlockControllerTest.java +++ b/backend/src/test/java/org/raddatz/familienarchiv/controller/TranscriptionBlockControllerTest.java @@ -183,6 +183,22 @@ class TranscriptionBlockControllerTest { .andExpect(status().isUnauthorized()); } + @Test + @WithMockUser(authorities = "WRITE_ALL") + void createBlock_returns400_whenMentionedPersonDisplayNameExceeds200Chars() throws Exception { + when(userService.findByEmail(any())).thenReturn(mockUser()); + String longName = "A".repeat(201); + String body = "{\"pageNumber\":1,\"x\":0.1,\"y\":0.2,\"width\":0.3,\"height\":0.4,\"text\":\"x\"," + + "\"mentionedPersons\":[{\"personId\":\"" + UUID.randomUUID() + + "\",\"displayName\":\"" + longName + "\"}]}"; + + mockMvc.perform(post(URL_BASE) + .contentType(MediaType.APPLICATION_JSON) + .content(body)) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.code").value("VALIDATION_ERROR")); + } + // ─── PUT /api/documents/{id}/transcription-blocks/{blockId} ───────────── @Test