From a0cbdd86f72f97083190e54ca99d1ff33fc9f675 Mon Sep 17 00:00:00 2001 From: Robert Jasny Date: Wed, 18 Dec 2024 16:07:43 +0100 Subject: [PATCH 01/14] stimmzettelumschlaege implementiert --- .../Stimmzettelumschlaege.java | 27 +++++++ .../StimmzettelumschlaegeRepository.java | 47 +++++++++++ .../exception/ExceptionConstants.java | 17 +++- .../StimmzettelumschlaegeController.java | 80 +++++++++++++++++++ .../StimmzettelumschlaegeDTO.java | 11 +++ .../StimmzettelumschlaegeDTOMapper.java | 12 +++ .../StimmzettelumschlaegeModel.java | 11 +++ .../StimmzettelumschlaegeModelMapper.java | 12 +++ .../StimmzettelumschlaegeService.java | 54 +++++++++++++ .../StimmzettelumschlaegeValidator.java | 29 +++++++ ...V3_0__createTableStimmzettelumschlaege.sql | 10 +++ ...V3_0__createTableStimmzettelumschlaege.sql | 10 +++ .../test/resources/stimmzettelumschlaege.http | 37 +++++++++ 13 files changed, 353 insertions(+), 4 deletions(-) create mode 100644 wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/domain/stimmzettelumschlaege/Stimmzettelumschlaege.java create mode 100644 wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/domain/stimmzettelumschlaege/StimmzettelumschlaegeRepository.java create mode 100644 wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/rest/stimmzettelumschlaege/StimmzettelumschlaegeController.java create mode 100644 wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/rest/stimmzettelumschlaege/StimmzettelumschlaegeDTO.java create mode 100644 wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/rest/stimmzettelumschlaege/StimmzettelumschlaegeDTOMapper.java create mode 100644 wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeModel.java create mode 100644 wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeModelMapper.java create mode 100644 wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeService.java create mode 100644 wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeValidator.java create mode 100644 wls-ergebnismeldung-service/src/main/resources/db/migrations/h2/V3_0__createTableStimmzettelumschlaege.sql create mode 100644 wls-ergebnismeldung-service/src/main/resources/db/migrations/oracle/V3_0__createTableStimmzettelumschlaege.sql create mode 100644 wls-ergebnismeldung-service/src/test/resources/stimmzettelumschlaege.http diff --git a/wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/domain/stimmzettelumschlaege/Stimmzettelumschlaege.java b/wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/domain/stimmzettelumschlaege/Stimmzettelumschlaege.java new file mode 100644 index 000000000..1f3c6ca89 --- /dev/null +++ b/wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/domain/stimmzettelumschlaege/Stimmzettelumschlaege.java @@ -0,0 +1,27 @@ +package de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.domain.stimmzettelumschlaege; + +import de.muenchen.oss.wahllokalsystem.wls.common.security.domain.BezirkUndWahlID; +import jakarta.persistence.EmbeddedId; +import jakarta.persistence.Entity; +import jakarta.validation.constraints.NotNull; +import java.time.LocalDateTime; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Entity +@Data +@NoArgsConstructor +@AllArgsConstructor +public class Stimmzettelumschlaege { + + @EmbeddedId + private BezirkUndWahlID bezirkUndWahlID; + + private LocalDateTime urneneroeffnungsUhrzeit; + + @NotNull + private long anzahlWaehler; + + private Long anzahlWaehler2; +} diff --git a/wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/domain/stimmzettelumschlaege/StimmzettelumschlaegeRepository.java b/wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/domain/stimmzettelumschlaege/StimmzettelumschlaegeRepository.java new file mode 100644 index 000000000..f9a396b73 --- /dev/null +++ b/wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/domain/stimmzettelumschlaege/StimmzettelumschlaegeRepository.java @@ -0,0 +1,47 @@ +package de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.domain.stimmzettelumschlaege; + +import de.muenchen.oss.wahllokalsystem.wls.common.security.domain.BezirkUndWahlID; +import java.util.Optional; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.CachePut; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.data.repository.CrudRepository; +import org.springframework.security.access.prepost.PreAuthorize; + +@PreAuthorize("hasAuthority('Ergebnismeldung_READ_Stimmzettelumschlaege')") +public interface StimmzettelumschlaegeRepository extends CrudRepository { + + String CACHE = "STIMMZETTELUMSCHLAEGE_CACHE"; + + @Override + Iterable findAll(); + + @Override + @Cacheable(value = CACHE, key = "#p0") + Optional findById(BezirkUndWahlID bezirkUndWahlID); + + @Override + @CachePut(value = CACHE, key = "#p0.bezirkUndWahlID") + @PreAuthorize("hasAuthority('Ergebnismeldung_WRITE_Stimmzettelumschlaege')") + S save(S stimmzettelumschlaege); + + @Override + @CacheEvict(value = CACHE, key = "#p0") + @PreAuthorize("hasAuthority('Ergebnismeldung_DELETE_Stimmzettelumschlaege')") + void deleteById(BezirkUndWahlID bezirkUndWahlID); + + @Override + @CacheEvict(value = CACHE, key = "#p0.bezirkUndWahlID") + @PreAuthorize("hasAuthority('Ergebnismeldung_DELETE_Stimmzettelumschlaege')") + void delete(Stimmzettelumschlaege entity); + + @Override + @CacheEvict(value = CACHE, allEntries = true) + @PreAuthorize("hasAuthority('Ergebnismeldung_DELETE_Stimmzettelumschlaege')") + void deleteAll(Iterable entities); + + @Override + @CacheEvict(value = CACHE, allEntries = true) + @PreAuthorize("hasAuthority('Ergebnismeldung_DELETE_Stimmzettelumschlaege')") + void deleteAll(); +} diff --git a/wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/exception/ExceptionConstants.java b/wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/exception/ExceptionConstants.java index 5fbf9a234..1922034e6 100644 --- a/wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/exception/ExceptionConstants.java +++ b/wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/exception/ExceptionConstants.java @@ -9,13 +9,15 @@ public class ExceptionConstants { public static ExceptionDataWrapper GETAWERTE_PARAMETER_UNVOLLSTAENDIG = new ExceptionDataWrapper("600", "getAWerte: Parameter unvollstaendig"); - public static ExceptionDataWrapper GETAWERTE_UNSAVEABLE = new ExceptionDataWrapper("601", "getAWerte: Die AWerte vom Client konnten nicht gespeichert werden."); - public static final ExceptionDataWrapper GET_STATUS_PARAMETER_UNVOLLSTAENDIG = new ExceptionDataWrapper("606", "getStatus: Parameter unvollstaendig"); - public static final ExceptionDataWrapper POST_STATUS_PARAMETER_UNVOLLSTAENDIG = new ExceptionDataWrapper("607", "postStatus: Parameter unvollstaendig"); - public static final ExceptionDataWrapper STATUS_UNSAVEABLE = new ExceptionDataWrapper("622", "postStatus: Der Status konnte nicht gespeichert werden."); + public static final ExceptionDataWrapper GET_STATUS_PARAMETER_UNVOLLSTAENDIG = new ExceptionDataWrapper("606", + "getStatus: Parameter unvollstaendig"); + public static final ExceptionDataWrapper POST_STATUS_PARAMETER_UNVOLLSTAENDIG = new ExceptionDataWrapper("607", + "postStatus: Parameter unvollstaendig"); + public static final ExceptionDataWrapper STATUS_UNSAVEABLE = new ExceptionDataWrapper("622", + "postStatus: Der Status konnte nicht gespeichert werden."); public static final ExceptionDataWrapper GET_WAHLSCHEINE_PARAMETER_UNVOLLSTAENDIG = new ExceptionDataWrapper("612", "getWahlscheine: Parameter unvollstaendig"); @@ -24,6 +26,13 @@ public class ExceptionConstants { public static final ExceptionDataWrapper WAHLSCHEINE_UNSAVEABLE = new ExceptionDataWrapper("618", "postWahlscheine: Die Wahlscheine konnten nicht gespeichert werden."); + public static final ExceptionDataWrapper GET_STIMMZETTELUMSCHLAEGE_PARAMETER_UNVOLLSTAENDIG = new ExceptionDataWrapper("610", + "getStimmzettelumschlaege: Parameter unvollstaendig"); + public static final ExceptionDataWrapper POST_STIMMZETTELUMSCHLAEGE_PARAMETER_UNVOLLSTAENDIG = new ExceptionDataWrapper("611", + "postStimmzettelumschlaege: Parameter unvollstaendig"); + public static final ExceptionDataWrapper STIMMZETTELUMSCHLAEGE_UNSAVEABLE = new ExceptionDataWrapper("619", + "postStimmzettelumschlaege: Die Stimmzettelumschlaege konnten nicht gespeichert werden."); + public static final ExceptionDataWrapper KOMMUNIKATIONSFEHLER_MIT_MONITORING = new ExceptionDataWrapper("100", "Bei der Kommunikation mit dem MonitoringService kam es zu einem Fehler."); } diff --git a/wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/rest/stimmzettelumschlaege/StimmzettelumschlaegeController.java b/wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/rest/stimmzettelumschlaege/StimmzettelumschlaegeController.java new file mode 100644 index 000000000..7fb138f0d --- /dev/null +++ b/wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/rest/stimmzettelumschlaege/StimmzettelumschlaegeController.java @@ -0,0 +1,80 @@ +package de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.rest.stimmzettelumschlaege; + +import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.rest.AbstractController; +import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.service.stimmzettelumschlaege.StimmzettelumschlaegeService; +import de.muenchen.oss.wahllokalsystem.wls.common.exception.rest.model.WlsExceptionDTO; +import de.muenchen.oss.wahllokalsystem.wls.common.security.domain.BezirkUndWahlID; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import lombok.RequiredArgsConstructor; +import lombok.val; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/businessActions/stimmzettelumschlaege") +@RequiredArgsConstructor +public class StimmzettelumschlaegeController extends AbstractController { + + private final StimmzettelumschlaegeService stimmzettelumschlaegeService; + private final StimmzettelumschlaegeDTOMapper stimmzettelumschlaegeDTOMapper; + + @Operation(description = "Lesen der Anzahl an Stimmzettelumschlaegen eines Wahlbezirkes für eine Wahl") + @ApiResponses( + value = { + @ApiResponse( + responseCode = "200", description = "Es existiert eine Anzahl an Stimmzettelumschlaegen", + content = { @Content(mediaType = "application/json", schema = @Schema(implementation = StimmzettelumschlaegeDTO.class)) } + ), + @ApiResponse( + responseCode = "204", description = "Es existiert keine Anzahl an Stimmzettelumschlaegen zu den entsprechenden Kriterien", + content = { @Content() } + ), + @ApiResponse( + responseCode = "400", description = "Validierung der Anfrage war nicht erfolgreich", + content = { @Content(mediaType = "application/json", schema = @Schema(implementation = WlsExceptionDTO.class)) } + ), + @ApiResponse( + responseCode = "500", description = "Probleme bei der Verarbeitung der Anfrage", + content = { @Content(mediaType = "application/json", schema = @Schema(implementation = WlsExceptionDTO.class)) } + ) + } + ) + @GetMapping("{wahlID}/{wahlbezirkID}") + public ResponseEntity getStimmzettelumschlaege(@PathVariable("wahlID") final String wahlID, + @PathVariable("wahlbezirkID") final String wahlbezirkID) { + val stimmzettelumschlaege = stimmzettelumschlaegeService.getStimmzettelumschlaege(new BezirkUndWahlID(wahlID, wahlbezirkID)); + return okWithBodyOrNoContent(stimmzettelumschlaege.map(stimmzettelumschlaegeDTOMapper::toDTO)); + } + + @Operation(description = "Setzen der Anzahl an Stimmzettelumschlaegen eines Wahlbezirkes für eine Wahl") + @ApiResponses( + value = { + @ApiResponse( + responseCode = "200", description = "Anzahl an Stimmzettelumschlaegen erfolgreich gespeichert" + ), + @ApiResponse( + responseCode = "400", description = "Validierung der Anfrage war nicht erfolgreich", + content = { @Content(mediaType = "application/json", schema = @Schema(implementation = WlsExceptionDTO.class)) } + ), + @ApiResponse( + responseCode = "500", description = "Probleme bei der Verarbeitung der Anfrage", + content = { @Content(mediaType = "application/json", schema = @Schema(implementation = WlsExceptionDTO.class)) } + ) + } + ) + @PostMapping("{wahlID}/{wahlbezirkID}") + public void postStimmzettelumschlaege(@PathVariable("wahlID") final String wahlID, @PathVariable("wahlbezirkID") final String wahlbezirkID, + @RequestBody final StimmzettelumschlaegeDTO stimmzettelumschlaegeDTO) { + stimmzettelumschlaegeService.setStimmzettelumschlaege(new BezirkUndWahlID(wahlID, wahlbezirkID), + stimmzettelumschlaegeDTOMapper.toModel(stimmzettelumschlaegeDTO)); + } +} diff --git a/wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/rest/stimmzettelumschlaege/StimmzettelumschlaegeDTO.java b/wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/rest/stimmzettelumschlaege/StimmzettelumschlaegeDTO.java new file mode 100644 index 000000000..f482703d0 --- /dev/null +++ b/wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/rest/stimmzettelumschlaege/StimmzettelumschlaegeDTO.java @@ -0,0 +1,11 @@ +package de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.rest.stimmzettelumschlaege; + +import de.muenchen.oss.wahllokalsystem.wls.common.security.domain.BezirkUndWahlID; +import jakarta.validation.constraints.NotNull; +import java.time.LocalDateTime; + +public record StimmzettelumschlaegeDTO(@NotNull BezirkUndWahlID bezirkUndWahlID, + LocalDateTime urneneroeffnungsUhrzeit, + @NotNull long anzahlWaehler, + Long anzahlWaehler2) { +} diff --git a/wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/rest/stimmzettelumschlaege/StimmzettelumschlaegeDTOMapper.java b/wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/rest/stimmzettelumschlaege/StimmzettelumschlaegeDTOMapper.java new file mode 100644 index 000000000..40f056d5b --- /dev/null +++ b/wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/rest/stimmzettelumschlaege/StimmzettelumschlaegeDTOMapper.java @@ -0,0 +1,12 @@ +package de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.rest.stimmzettelumschlaege; + +import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.service.stimmzettelumschlaege.StimmzettelumschlaegeModel; +import org.mapstruct.Mapper; + +@Mapper +public interface StimmzettelumschlaegeDTOMapper { + + StimmzettelumschlaegeDTO toDTO(StimmzettelumschlaegeModel stimmzettelumschlaegeModel); + + StimmzettelumschlaegeModel toModel(StimmzettelumschlaegeDTO stimmzettelumschlaegeDTO); +} diff --git a/wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeModel.java b/wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeModel.java new file mode 100644 index 000000000..c9f6258e4 --- /dev/null +++ b/wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeModel.java @@ -0,0 +1,11 @@ +package de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.service.stimmzettelumschlaege; + +import de.muenchen.oss.wahllokalsystem.wls.common.security.domain.BezirkUndWahlID; +import jakarta.validation.constraints.NotNull; +import java.time.LocalDateTime; + +public record StimmzettelumschlaegeModel(@NotNull BezirkUndWahlID bezirkUndWahlID, + LocalDateTime urneneroeffnungsUhrzeit, + @NotNull long anzahlWaehler, + long anzahlWaehler2) { +} diff --git a/wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeModelMapper.java b/wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeModelMapper.java new file mode 100644 index 000000000..c261e17f4 --- /dev/null +++ b/wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeModelMapper.java @@ -0,0 +1,12 @@ +package de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.service.stimmzettelumschlaege; + +import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.domain.stimmzettelumschlaege.Stimmzettelumschlaege; +import org.mapstruct.Mapper; + +@Mapper +public interface StimmzettelumschlaegeModelMapper { + + StimmzettelumschlaegeModel toModel(Stimmzettelumschlaege entity); + + Stimmzettelumschlaege toEntity(StimmzettelumschlaegeModel model); +} diff --git a/wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeService.java b/wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeService.java new file mode 100644 index 000000000..6558153d2 --- /dev/null +++ b/wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeService.java @@ -0,0 +1,54 @@ +package de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.service.stimmzettelumschlaege; + +import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.domain.stimmzettelumschlaege.StimmzettelumschlaegeRepository; +import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.exception.ExceptionConstants; +import de.muenchen.oss.wahllokalsystem.wls.common.exception.util.ExceptionFactory; +import de.muenchen.oss.wahllokalsystem.wls.common.security.domain.BezirkUndWahlID; +import java.util.Optional; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import lombok.val; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.core.parameters.P; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +@Slf4j +public class StimmzettelumschlaegeService { + + private final StimmzettelumschlaegeRepository stimmzettelumschlaegeRepository; + private final StimmzettelumschlaegeModelMapper stimmzettelumschlaegeModelMapper; + private final StimmzettelumschlaegeValidator stimmzettelumschlaegeValidator; + private final ExceptionFactory exceptionFactory; + + @PreAuthorize("hasAuthority('Ergebnismeldung_BUSINESSACTION_GetStimmzettelumschlaege')") + public Optional getStimmzettelumschlaege(final BezirkUndWahlID id) { + log.info("#getStimmzettelumschlaege"); + + stimmzettelumschlaegeValidator.validBezirkUndWahlIdOrThrow(id, + exceptionFactory.createFachlicheWlsException(ExceptionConstants.GET_STIMMZETTELUMSCHLAEGE_PARAMETER_UNVOLLSTAENDIG)); + + val stimmzettelumschlaegeFromRepo = stimmzettelumschlaegeRepository.findById(id); + return stimmzettelumschlaegeFromRepo.map(stimmzettelumschlaegeModelMapper::toModel); + } + + @PreAuthorize( + "hasAuthority('Ergebnismeldung_BUSINESSACTION_PostStimmzettelumschlaege')" + + "and @bezirkIdPermisionEvaluator.tokenUserBezirkIdMatches(#param?.getWahlbezirkID(), authentication)" + ) + public void setStimmzettelumschlaege(@P("param") final BezirkUndWahlID id, final StimmzettelumschlaegeModel stimmzettelumschlaege) { + log.info("#postStimmzettelumschlaege"); + + stimmzettelumschlaegeValidator.validBezirkUndWahlIdOrThrow(id, + exceptionFactory.createFachlicheWlsException(ExceptionConstants.POST_STIMMZETTELUMSCHLAEGE_PARAMETER_UNVOLLSTAENDIG)); + stimmzettelumschlaegeValidator.validStimmzettelumschlaegeOrThrow(stimmzettelumschlaege); + + try { + stimmzettelumschlaegeRepository.save(stimmzettelumschlaegeModelMapper.toEntity(stimmzettelumschlaege)); + } catch (Exception e) { + log.error("#postStimmzettelumschlaege unsaveable:", e); + throw exceptionFactory.createTechnischeWlsException(ExceptionConstants.STIMMZETTELUMSCHLAEGE_UNSAVEABLE); + } + } +} diff --git a/wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeValidator.java b/wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeValidator.java new file mode 100644 index 000000000..31d1b0bd9 --- /dev/null +++ b/wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeValidator.java @@ -0,0 +1,29 @@ +package de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.service.stimmzettelumschlaege; + +import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.exception.ExceptionConstants; +import de.muenchen.oss.wahllokalsystem.wls.common.exception.FachlicheWlsException; +import de.muenchen.oss.wahllokalsystem.wls.common.exception.util.ExceptionFactory; +import de.muenchen.oss.wahllokalsystem.wls.common.security.domain.BezirkUndWahlID; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class StimmzettelumschlaegeValidator { + + private final ExceptionFactory exceptionFactory; + + public void validBezirkUndWahlIdOrThrow(final BezirkUndWahlID bezirkUndWahlId, final FachlicheWlsException exceptionOnInvalid) + throws FachlicheWlsException { + if (bezirkUndWahlId == null || StringUtils.isBlank(bezirkUndWahlId.getWahlID()) || StringUtils.isBlank(bezirkUndWahlId.getWahlbezirkID())) { + throw exceptionOnInvalid; + } + } + + public void validStimmzettelumschlaegeOrThrow(final StimmzettelumschlaegeModel stimmzettelumschlaegeModel) { + if (stimmzettelumschlaegeModel == null) { + throw exceptionFactory.createFachlicheWlsException(ExceptionConstants.POST_STIMMZETTELUMSCHLAEGE_PARAMETER_UNVOLLSTAENDIG); + } + } +} diff --git a/wls-ergebnismeldung-service/src/main/resources/db/migrations/h2/V3_0__createTableStimmzettelumschlaege.sql b/wls-ergebnismeldung-service/src/main/resources/db/migrations/h2/V3_0__createTableStimmzettelumschlaege.sql new file mode 100644 index 000000000..8b94237b4 --- /dev/null +++ b/wls-ergebnismeldung-service/src/main/resources/db/migrations/h2/V3_0__createTableStimmzettelumschlaege.sql @@ -0,0 +1,10 @@ +CREATE TABLE Stimmzettelumschlaege +( + wahlID VARCHAR(1024) NOT NULL, + wahlbezirkID VARCHAR(1024) NOT NULL, + urneneroeffnungsUhrzeit DATETIME, + anzahlWaehler BIGINT NOT NULL, + anzahlWaehler2 BIGINT, + + PRIMARY KEY (wahlID, wahlbezirkID) +); \ No newline at end of file diff --git a/wls-ergebnismeldung-service/src/main/resources/db/migrations/oracle/V3_0__createTableStimmzettelumschlaege.sql b/wls-ergebnismeldung-service/src/main/resources/db/migrations/oracle/V3_0__createTableStimmzettelumschlaege.sql new file mode 100644 index 000000000..6a79ced2a --- /dev/null +++ b/wls-ergebnismeldung-service/src/main/resources/db/migrations/oracle/V3_0__createTableStimmzettelumschlaege.sql @@ -0,0 +1,10 @@ +CREATE TABLE Stimmzettelumschlaege +( + wahlID VARCHAR(1024) NOT NULL, + wahlbezirkID VARCHAR(1024) NOT NULL, + urneneroeffnungsUhrzeit TIMESTAMP, + anzahlWaehler NUMBER(19, 0) NOT NULL, + anzahlWaehler2 NUMBER(19, 0), + + PRIMARY KEY (wahlID, wahlbezirkID) +); \ No newline at end of file diff --git a/wls-ergebnismeldung-service/src/test/resources/stimmzettelumschlaege.http b/wls-ergebnismeldung-service/src/test/resources/stimmzettelumschlaege.http new file mode 100644 index 000000000..53e921f3a --- /dev/null +++ b/wls-ergebnismeldung-service/src/test/resources/stimmzettelumschlaege.http @@ -0,0 +1,37 @@ +### Get token wls_all +POST {{ SSO_URL }}/auth/realms/wls_realm/protocol/openid-connect/token +Content-Type: application/x-www-form-urlencoded + +password = test & +grant_type = password & +client_secret = top-secret & +client_id = wls & +username = wls_all + +> {% + client.global.set("auth_token", response.body.access_token); + client.global.set("token_type", response.body.token_type); +%} + +### get userinfo with auth_token +GET {{ SSO_URL }}/auth/realms/wls_realm/protocol/openid-connect/userinfo +Authorization: {{ token_type }} {{ auth_token }} + +### GET Anzahl der Stimmzettelumschlaege für wahlID and wahlbezirkID +GET {{ WLS_ERGEBNISMELDUNG_SERVICE_URL }}/businessActions/stimmzettelumschlaege/wahlID/wahlbezirkID +Authorization: {{ token_type }} {{ auth_token }} + +### POST Anzahl der Stimmzettelumschlaege +POST {{ WLS_ERGEBNISMELDUNG_SERVICE_URL }}/businessActions/stimmzettelumschlaege/wahlID/wahlbezirkID +Content-Type: application/json +Authorization: {{ token_type }} {{ auth_token }} + +{ + "bezirkUndWahlID": { + "wahlID": "wahlID", + "wahlbezirkID": "wahlbezirkID" + }, + "urneneroeffnungsUhrzeit": "2024-12-18T14:52:41.263Z", + "anzahlWaehler": 47, + "anzahlWaehler2": 11 +} \ No newline at end of file From 86fc6e3f2bbc308ebd04a0046780127af31239f5 Mon Sep 17 00:00:00 2001 From: Robert Jasny Date: Wed, 18 Dec 2024 19:03:19 +0100 Subject: [PATCH 02/14] authorities and keycloak migration added --- ...-ergebnismeldung-stimmzettelumschlaege.yml | 46 +++++++++++++++++++ .../keycloak/migration/keycloak-changelog.yml | 1 + .../utils/Authorities.java | 16 +++++++ 3 files changed, 63 insertions(+) create mode 100644 stack/keycloak/migration/add-authorities-ergebnismeldung-stimmzettelumschlaege.yml diff --git a/stack/keycloak/migration/add-authorities-ergebnismeldung-stimmzettelumschlaege.yml b/stack/keycloak/migration/add-authorities-ergebnismeldung-stimmzettelumschlaege.yml new file mode 100644 index 000000000..9a417c84b --- /dev/null +++ b/stack/keycloak/migration/add-authorities-ergebnismeldung-stimmzettelumschlaege.yml @@ -0,0 +1,46 @@ +id: add authorities ergebnismeldung stimmzettelumschlaege +author: dragonfly28 +realm: ${SSO_REALM} +changes: + - addRole: + name: Ergebnismeldung_BUSINESSACTION_GetStimmzettelumschlaege + clientRole: true + clientId: ${SSO_CLIENT_ID} + - assignRoleToGroup: + group: allErgebnismeldungAuthorities + role: Ergebnismeldung_BUSINESSACTION_GetStimmzettelumschlaege + clientId: ${SSO_CLIENT_ID} + + - addRole: + name: Ergebnismeldung_BUSINESSACTION_PostStimmzettelumschlaege + clientRole: true + clientId: ${SSO_CLIENT_ID} + - assignRoleToGroup: + group: allErgebnismeldungAuthorities + role: Ergebnismeldung_BUSINESSACTION_PostStimmzettelumschlaege + clientId: ${SSO_CLIENT_ID} + + - addRole: + name: Ergebnismeldung_READ_Stimmzettelumschlaege + clientRole: true + clientId: ${SSO_CLIENT_ID} + - assignRoleToGroup: + group: allErgebnismeldungAuthorities + role: Ergebnismeldung_READ_Stimmzettelumschlaege + clientId: ${SSO_CLIENT_ID} + - addRole: + name: Ergebnismeldung_WRITE_Stimmzettelumschlaege + clientRole: true + clientId: ${SSO_CLIENT_ID} + - assignRoleToGroup: + group: allErgebnismeldungAuthorities + role: Ergebnismeldung_WRITE_Stimmzettelumschlaege + clientId: ${SSO_CLIENT_ID} + - addRole: + name: Ergebnismeldung_DELETE_Stimmzettelumschlaege + clientRole: true + clientId: ${SSO_CLIENT_ID} + - assignRoleToGroup: + group: allErgebnismeldungAuthorities + role: Ergebnismeldung_DELETE_Stimmzettelumschlaege + clientId: ${SSO_CLIENT_ID} \ No newline at end of file diff --git a/stack/keycloak/migration/keycloak-changelog.yml b/stack/keycloak/migration/keycloak-changelog.yml index aff89bf6a..3c4699529 100644 --- a/stack/keycloak/migration/keycloak-changelog.yml +++ b/stack/keycloak/migration/keycloak-changelog.yml @@ -55,3 +55,4 @@ includes: - path: add-authorities-ergebnismeldung-awerte.yml - path: add-authorities-ergebnismeldung-status.yml - path: add-authorities-ergebnismeldung-wahlscheine.yml + - path: add-authorities-ergebnismeldung-stimmzettelumschlaege.yml diff --git a/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/utils/Authorities.java b/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/utils/Authorities.java index 3c7799eac..5e89009b6 100644 --- a/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/utils/Authorities.java +++ b/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/utils/Authorities.java @@ -13,6 +13,8 @@ public class Authorities { public static final String SERVICE_SET_STATUS = "Ergebnismeldung_BUSINESSACTION_PostStatus"; public static final String SERVICE_GET_WAHLSCHEINE = "Ergebnismeldung_BUSINESSACTION_GetWahlscheine"; public static final String SERVICE_SET_WAHLSCHEINE = "Ergebnismeldung_BUSINESSACTION_PostWahlscheine"; + public static final String SERVICE_GET_STIMMZETTELUMSCHLAEGE = "Ergebnismeldung_BUSINESSACTION_GetStimmzettelumschlaege"; + public static final String SERVICE_SET_STIMMZETTELUMSCHLAEGE = "Ergebnismeldung_BUSINESSACTION_PostStimmzettelumschlaege"; public static final String REPOSITORY_READ_AWERTE = "Ergebnismeldung_READ_AWerte"; public static final String REPOSITORY_DELETE_AWERTE = "Ergebnismeldung_DELETE_AWerte"; @@ -26,6 +28,10 @@ public class Authorities { public static final String REPOSITORY_DELETE_WAHLSCHEINE = "Ergebnismeldung_DELETE_Wahlscheine"; public static final String REPOSITORY_WRITE_WAHLSCHEINE = "Ergebnismeldung_WRITE_Wahlscheine"; + public static final String REPOSITORY_READ_STIMMZETTELUMSCHLAEGE = "Ergebnismeldung_READ_Stimmzettelumschlaege"; + public static final String REPOSITORY_DELETE_STIMMZETTELUMSCHLAEGE = "Ergebnismeldung_DELETE_Stimmzettelumschlaege"; + public static final String REPOSITORY_WRITE_STIMMZETTELUMSCHLAEGE = "Ergebnismeldung_WRITE_Stimmzettelumschlaege"; + public static final String[] ALL_AUTHORITIES_USER_GET_AWERTE = new String[] { SERVICE_GET_AWERTE, REPOSITORY_READ_AWERTE, @@ -65,4 +71,14 @@ public class Authorities { REPOSITORY_WRITE_WAHLSCHEINE }; + public static final String[] ALL_AUTHORITIES_GET_STIMMZETTELUMSCHLAEGE = new String[] { + SERVICE_GET_STIMMZETTELUMSCHLAEGE, + REPOSITORY_READ_STIMMZETTELUMSCHLAEGE + }; + + public static final String[] ALL_AUTHORITIES_SET_STIMMZETTELUMSCHLAEGE = new String[] { + SERVICE_SET_STIMMZETTELUMSCHLAEGE, + REPOSITORY_WRITE_STIMMZETTELUMSCHLAEGE + }; + } From 874dbc500392ced4404ba118d840cd59dd7862e4 Mon Sep 17 00:00:00 2001 From: Robert Jasny Date: Thu, 19 Dec 2024 12:04:08 +0100 Subject: [PATCH 03/14] service tests added --- .../StimmzettelumschlaegeModelMapperTest.java | 64 ++++++++ ...mmzettelumschlaegeServiceSecurityTest.java | 142 +++++++++++++++++ .../StimmzettelumschlaegeServiceTest.java | 150 ++++++++++++++++++ .../StimmzettelumschlaegeValidatorTest.java | 82 ++++++++++ 4 files changed, 438 insertions(+) create mode 100644 wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeModelMapperTest.java create mode 100644 wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeServiceSecurityTest.java create mode 100644 wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeServiceTest.java create mode 100644 wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeValidatorTest.java diff --git a/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeModelMapperTest.java b/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeModelMapperTest.java new file mode 100644 index 000000000..1e6ac1035 --- /dev/null +++ b/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeModelMapperTest.java @@ -0,0 +1,64 @@ +package de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.service.stimmzettelumschlaege; + +import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.domain.stimmzettelumschlaege.Stimmzettelumschlaege; +import de.muenchen.oss.wahllokalsystem.wls.common.security.domain.BezirkUndWahlID; +import java.time.LocalDateTime; +import lombok.val; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mapstruct.factory.Mappers; + +class StimmzettelumschlaegeModelMapperTest { + + private final StimmzettelumschlaegeModelMapper unitUnderTest = Mappers.getMapper(StimmzettelumschlaegeModelMapper.class); + + @Nested + class ToModel { + + @Test + void should_returnNull_when_givenNull() { + Assertions.assertThat(unitUnderTest.toModel(null)).isNull(); + } + + @Test + void should_returnStimmzettelumschlaegeModel_when_givenStimmzettelumschlaegeEntity() { + val wahlID = "wahlID"; + val wahlbezirkID = "wahlbezirkID"; + val urneneroeffnungsUhrzeit = LocalDateTime.now(); + val anzahlWaehler = 47; + val anzahlWaehler2 = 11; + val wahlscheineEntity = new Stimmzettelumschlaege(new BezirkUndWahlID(wahlID, wahlbezirkID), urneneroeffnungsUhrzeit, anzahlWaehler, (long) anzahlWaehler2); + + val result = unitUnderTest.toModel(wahlscheineEntity); + val expectedResult = new StimmzettelumschlaegeModel( + new BezirkUndWahlID(wahlID, wahlbezirkID), urneneroeffnungsUhrzeit, anzahlWaehler, anzahlWaehler2); + + Assertions.assertThat(result).isEqualTo(expectedResult); + } + } + + @Nested + class ToEntity { + + @Test + void should_returnNull_when_givenNull() { + Assertions.assertThat(unitUnderTest.toEntity(null)).isNull(); + } + + @Test + void should_returnStimmzettelumschlaegeEntity_when_givenStimmzettelumschlaegeModel() { + val wahlID = "wahlID"; + val wahlbezirkID = "wahlbezirkID"; + val urneneroeffnungsUhrzeit = LocalDateTime.now(); + val anzahlWaehler = 47; + val anzahlWaehler2 = 11; + val wahlscheineModel = new StimmzettelumschlaegeModel(new BezirkUndWahlID(wahlID, wahlbezirkID), urneneroeffnungsUhrzeit, anzahlWaehler, anzahlWaehler2); + + val result = unitUnderTest.toEntity(wahlscheineModel); + val expectedResult = new Stimmzettelumschlaege(new BezirkUndWahlID(wahlID, wahlbezirkID), urneneroeffnungsUhrzeit, anzahlWaehler, (long) anzahlWaehler2); + + Assertions.assertThat(result).isEqualTo(expectedResult); + } + } +} diff --git a/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeServiceSecurityTest.java b/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeServiceSecurityTest.java new file mode 100644 index 000000000..45bc684ef --- /dev/null +++ b/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeServiceSecurityTest.java @@ -0,0 +1,142 @@ +package de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.service.stimmzettelumschlaege; + +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.notNull; + +import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.MicroServiceApplication; +import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.TestConstants; +import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.domain.stimmzettelumschlaege.StimmzettelumschlaegeRepository; +import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.utils.Authorities; +import de.muenchen.oss.wahllokalsystem.wls.common.exception.TechnischeWlsException; +import de.muenchen.oss.wahllokalsystem.wls.common.security.BezirkIDPermissionEvaluator; +import de.muenchen.oss.wahllokalsystem.wls.common.security.domain.BezirkUndWahlID; +import de.muenchen.oss.wahllokalsystem.wls.common.testing.SecurityUtils; +import java.time.LocalDateTime; +import java.util.stream.Stream; +import lombok.val; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.aggregator.ArgumentsAccessor; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.test.context.ActiveProfiles; + +@SpringBootTest(classes = MicroServiceApplication.class) +@ActiveProfiles({ TestConstants.SPRING_TEST_PROFILE }) +class StimmzettelumschlaegeServiceSecurityTest { + + @MockBean + BezirkIDPermissionEvaluator bezirkIDPermissionEvaluator; + + @Autowired + StimmzettelumschlaegeService unitUnderTest; + + @Autowired + StimmzettelumschlaegeRepository stimmzettelumschlaegeRepository; + + @BeforeEach + void setup() { + SecurityUtils.runWith(Authorities.REPOSITORY_DELETE_STIMMZETTELUMSCHLAEGE); + stimmzettelumschlaegeRepository.deleteAll(); + } + + @Nested + class GetStimmzettelumschlaege { + + @Test + void should_getAccess_when_allRequiredAuthoritiesArePresent() { + SecurityUtils.runWith(Authorities.ALL_AUTHORITIES_GET_STIMMZETTELUMSCHLAEGE); + + val id = new BezirkUndWahlID("wahlID", "wahlbezirkID"); + + Assertions.assertThatNoException().isThrownBy(() -> unitUnderTest.getStimmzettelumschlaege(id)); + } + + @ParameterizedTest(name = "{index} - {1} missing") + @MethodSource("getMissingAuthoritiesVariations") + void should_throwAccessDeniedException_when_anyRequiredAuthorityIsMissing(final ArgumentsAccessor arguments) { + SecurityUtils.runWith(arguments.get(0, String[].class)); + + val id = new BezirkUndWahlID("wahlID", "wahlbezirkID"); + + Assertions.assertThatException().isThrownBy(() -> unitUnderTest.getStimmzettelumschlaege(id)).isInstanceOf(AccessDeniedException.class); + } + + private static Stream getMissingAuthoritiesVariations() { + return SecurityUtils + .buildArgumentsForMissingAuthoritiesVariations(Authorities.ALL_AUTHORITIES_GET_STIMMZETTELUMSCHLAEGE); + } + } + + @Nested + class SetStimmzettelumschlaege { + + @Test + void should_getAccess_when_allRequiredAuthoritiesArePresent() { + SecurityUtils.runWith(Authorities.ALL_AUTHORITIES_SET_STIMMZETTELUMSCHLAEGE); + + val wahlbezirkID = "wahlbezirkID"; + val id = new BezirkUndWahlID("wahlID", wahlbezirkID); + val stimmzettelumschlaege = createStimmzettelumschlaegeModel(id); + + Mockito.when(bezirkIDPermissionEvaluator.tokenUserBezirkIdMatches(eq(wahlbezirkID), notNull())).thenReturn(true); + + Assertions.assertThatNoException().isThrownBy(() -> unitUnderTest.setStimmzettelumschlaege(id, stimmzettelumschlaege)); + } + + @Test + void should_throwAccessDeniedException_when_allRequiredAuthoritiesArePresentButBezirkIDEvaluatorReturnsFalse() { + SecurityUtils.runWith(Authorities.ALL_AUTHORITIES_SET_STIMMZETTELUMSCHLAEGE); + + val wahlbezirkID = "wahlbezirkID"; + val id = new BezirkUndWahlID("wahlID", wahlbezirkID); + val stimmzettelumschlaege = createStimmzettelumschlaegeModel(id); + + Mockito.when(bezirkIDPermissionEvaluator.tokenUserBezirkIdMatches(eq(wahlbezirkID), notNull())).thenReturn(false); + + Assertions.assertThatException().isThrownBy(() -> unitUnderTest.setStimmzettelumschlaege(id, stimmzettelumschlaege)).isInstanceOf(AccessDeniedException.class); + } + + @Test + void should_throwAccessDeniedException_when_serviceSetStimmzettelumschlaegeAuthorityIsMissing() { + SecurityUtils.runWith(Authorities.REPOSITORY_WRITE_STIMMZETTELUMSCHLAEGE); + + val wahlbezirkID = "wahlbezirkID"; + val id = new BezirkUndWahlID("wahlID", wahlbezirkID); + val stimmzettelumschlaege = createStimmzettelumschlaegeModel(id); + + Mockito.when(bezirkIDPermissionEvaluator.tokenUserBezirkIdMatches(eq(wahlbezirkID), notNull())).thenReturn(true); + + Assertions.assertThatException().isThrownBy(() -> unitUnderTest.setStimmzettelumschlaege(id, stimmzettelumschlaege)).isInstanceOf(AccessDeniedException.class); + } + + @Test + void should_throwTechnischeWlsException_when_repositoryWriteStimmzettelumschlaegeAuthorityIsMissing() { + SecurityUtils.runWith(Authorities.SERVICE_SET_STIMMZETTELUMSCHLAEGE); + + val wahlbezirkID = "wahlbezirkID"; + val id = new BezirkUndWahlID("wahlID", wahlbezirkID); + val stimmzettelumschlaege = createStimmzettelumschlaegeModel(id); + + Mockito.when(bezirkIDPermissionEvaluator.tokenUserBezirkIdMatches(eq(wahlbezirkID), notNull())).thenReturn(true); + + Assertions.assertThatException().isThrownBy(() -> unitUnderTest.setStimmzettelumschlaege(id, stimmzettelumschlaege)).isInstanceOf(TechnischeWlsException.class); + } + + private StimmzettelumschlaegeModel createStimmzettelumschlaegeModel(BezirkUndWahlID id) { + val urneneroeffnungsUhrzeit = LocalDateTime.now(); + val anzahlWaehler = 47; + val anzahlWaehler2 = 11; + return new StimmzettelumschlaegeModel(id, urneneroeffnungsUhrzeit, anzahlWaehler, anzahlWaehler2); + + } + } +} diff --git a/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeServiceTest.java b/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeServiceTest.java new file mode 100644 index 000000000..d66eaf781 --- /dev/null +++ b/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeServiceTest.java @@ -0,0 +1,150 @@ +package de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.service.stimmzettelumschlaege; + +import static org.mockito.ArgumentMatchers.eq; + +import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.domain.stimmzettelumschlaege.Stimmzettelumschlaege; +import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.domain.stimmzettelumschlaege.StimmzettelumschlaegeRepository; +import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.exception.ExceptionConstants; +import de.muenchen.oss.wahllokalsystem.wls.common.exception.FachlicheWlsException; +import de.muenchen.oss.wahllokalsystem.wls.common.exception.TechnischeWlsException; +import de.muenchen.oss.wahllokalsystem.wls.common.exception.util.ExceptionFactory; +import de.muenchen.oss.wahllokalsystem.wls.common.security.domain.BezirkUndWahlID; +import java.util.Optional; +import lombok.val; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class StimmzettelumschlaegeServiceTest { + + @Mock + StimmzettelumschlaegeRepository stimmzettelumschlaegeRepository; + + @Mock + StimmzettelumschlaegeModelMapper stimmzettelumschlaegeModelMapper; + + @Mock + StimmzettelumschlaegeValidator stimmzettelumschlaegeValidator; + + @Mock + ExceptionFactory exceptionFactory; + + @InjectMocks + StimmzettelumschlaegeService unitUnderTest; + + @Nested + class GetStimmzettelumschlaege { + + @Test + void should_submitFachlicheWlsExceptionForParameter_when_callingValidator() { + val id = new BezirkUndWahlID(); + + val mockedWlsException = FachlicheWlsException.withCode("").buildWithMessage("validation of parameters failed"); + Mockito.when(exceptionFactory.createFachlicheWlsException(ExceptionConstants.GET_STIMMZETTELUMSCHLAEGE_PARAMETER_UNVOLLSTAENDIG)) + .thenReturn(mockedWlsException); + + unitUnderTest.getStimmzettelumschlaege(id); + + Mockito.verify(stimmzettelumschlaegeValidator).validBezirkUndWahlIdOrThrow(eq(id), eq(mockedWlsException)); + } + + @Test + void should_returnStimmzettelumschlaegeModel_when_stimmzettelumschlaegeIsFoundFromRepo() { + val id = new BezirkUndWahlID(); + + val mockedEntity = new Stimmzettelumschlaege(); + val mockedMappedEntityAsModel = new StimmzettelumschlaegeModel(id, null, 0, 0); + + Mockito.when(stimmzettelumschlaegeRepository.findById(id)).thenReturn(Optional.of(mockedEntity)); + Mockito.when(stimmzettelumschlaegeModelMapper.toModel(mockedEntity)).thenReturn(mockedMappedEntityAsModel); + + val result = unitUnderTest.getStimmzettelumschlaege(id); + + Assertions.assertThat(result).isEqualTo(Optional.of(mockedMappedEntityAsModel)); + } + + @Test + void should_returnEmptyOptional_when_stimmzettelumschlaegeIsNotFoundFromRepo() { + val id = new BezirkUndWahlID(); + + Mockito.when(stimmzettelumschlaegeRepository.findById(id)).thenReturn(Optional.empty()); + + val result = unitUnderTest.getStimmzettelumschlaege(id); + + Assertions.assertThat(result).isEmpty(); + } + } + + @Nested + class SetStimmzettelumschlaege { + + @Test + void should_submitFachlicheWlsExceptionForParameter_when_callingValidator() { + val id = new BezirkUndWahlID(); + val stimmzettelumschlaegeToSet = createStimmzettelumschlaegeModel(id); + + val mockedWlsException = FachlicheWlsException.withCode("").buildWithMessage("validation of parameters failed"); + Mockito.when(exceptionFactory.createFachlicheWlsException(ExceptionConstants.POST_STIMMZETTELUMSCHLAEGE_PARAMETER_UNVOLLSTAENDIG)) + .thenReturn(mockedWlsException); + + unitUnderTest.setStimmzettelumschlaege(id, stimmzettelumschlaegeToSet); + + Mockito.verify(stimmzettelumschlaegeValidator).validBezirkUndWahlIdOrThrow(id, mockedWlsException); + } + + @Test + void should_validateStimmzettelumschlaegeModel_when_called() { + val id = new BezirkUndWahlID(); + val stimmzettelumschlaegeToSet = createStimmzettelumschlaegeModel(id); + + val mockedWlsException = FachlicheWlsException.withCode("").buildWithMessage("validation of parameters failed"); + Mockito.when(exceptionFactory.createFachlicheWlsException(ExceptionConstants.POST_STIMMZETTELUMSCHLAEGE_PARAMETER_UNVOLLSTAENDIG)) + .thenReturn(mockedWlsException); + + unitUnderTest.setStimmzettelumschlaege(id, stimmzettelumschlaegeToSet); + + Mockito.verify(stimmzettelumschlaegeValidator).validStimmzettelumschlaegeOrThrow(stimmzettelumschlaegeToSet); + } + + @Test + void should_saveMappedStimmzettelumschlaegeModel_when_called() { + val id = new BezirkUndWahlID(); + val stimmzettelumschlaegeToSet = createStimmzettelumschlaegeModel(id); + + val mockedModelAsEntity = Mockito.mock(Stimmzettelumschlaege.class); + + Mockito.when(stimmzettelumschlaegeModelMapper.toEntity(stimmzettelumschlaegeToSet)).thenReturn(mockedModelAsEntity); + + unitUnderTest.setStimmzettelumschlaege(id, stimmzettelumschlaegeToSet); + + Mockito.verify(stimmzettelumschlaegeRepository).save(mockedModelAsEntity); + } + + @Test + void should_throwTechnischeWlsException_when_savingFailed() { + val id = new BezirkUndWahlID(); + val stimmzettelumschlaegeToSet = createStimmzettelumschlaegeModel(id); + + val mockedModelAsEntity = Mockito.mock(Stimmzettelumschlaege.class); + val mockedRepositorySaveException = new RuntimeException("saving failed"); + val mockedExceptionFactoryWlsException = TechnischeWlsException.withCode("").buildWithMessage("save exception"); + + Mockito.when(stimmzettelumschlaegeModelMapper.toEntity(stimmzettelumschlaegeToSet)).thenReturn(mockedModelAsEntity); + Mockito.doThrow(mockedRepositorySaveException).when(stimmzettelumschlaegeRepository).save(mockedModelAsEntity); + Mockito.when(exceptionFactory.createTechnischeWlsException(ExceptionConstants.STIMMZETTELUMSCHLAEGE_UNSAVEABLE)) + .thenReturn(mockedExceptionFactoryWlsException); + + Assertions.assertThatThrownBy(() -> unitUnderTest.setStimmzettelumschlaege(id, stimmzettelumschlaegeToSet)).isSameAs(mockedExceptionFactoryWlsException); + } + + private StimmzettelumschlaegeModel createStimmzettelumschlaegeModel(final BezirkUndWahlID id) { + return new StimmzettelumschlaegeModel(id, null, 0, 0); + } + } +} diff --git a/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeValidatorTest.java b/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeValidatorTest.java new file mode 100644 index 000000000..ccc1aacd4 --- /dev/null +++ b/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeValidatorTest.java @@ -0,0 +1,82 @@ +package de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.service.stimmzettelumschlaege; + +import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.exception.ExceptionConstants; +import de.muenchen.oss.wahllokalsystem.wls.common.exception.FachlicheWlsException; +import de.muenchen.oss.wahllokalsystem.wls.common.exception.util.ExceptionFactory; +import de.muenchen.oss.wahllokalsystem.wls.common.security.domain.BezirkUndWahlID; +import java.util.stream.Stream; +import lombok.val; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.aggregator.ArgumentsAccessor; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class StimmzettelumschlaegeValidatorTest { + + @Mock + ExceptionFactory exceptionFactory; + + @InjectMocks + StimmzettelumschlaegeValidator unitUnderTest; + + @Nested + class ValidBezirkUndWahlIdOrThrow { + + final FachlicheWlsException providedException = FachlicheWlsException.withCode("").buildWithMessage("sth failed"); + + @Test + void should_notThrowException_when_bezirkUndWahlIDIsValid() { + val id = new BezirkUndWahlID("wahlID", "wahlbezirkID"); + + Assertions.assertThatNoException().isThrownBy(() -> unitUnderTest.validBezirkUndWahlIdOrThrow(id, null)); + } + + @ParameterizedTest(name = "provided exception when {1}") + @MethodSource("invalidWahlbezirkArgumentsWithTestcaseNameAppendix") + void should_throwProvidedException_when_bezirkUndWahlIdIsNotValid(final ArgumentsAccessor arguments) { + Assertions.assertThatException() + .isThrownBy(() -> unitUnderTest.validBezirkUndWahlIdOrThrow(arguments.get(0, BezirkUndWahlID.class), providedException)) + .isSameAs(providedException); + } + + public static Stream invalidWahlbezirkArgumentsWithTestcaseNameAppendix() { + return Stream.of( + Arguments.of(null, "argument is null"), + Arguments.of(new BezirkUndWahlID(null, "wahlbezirkID"), "wahlID is null"), + Arguments.of(new BezirkUndWahlID("", "wahlbezirkID"), "wahlID is empty"), + Arguments.of(new BezirkUndWahlID(" ", "wahlbezirkID"), "wahlID is blank"), + Arguments.of(new BezirkUndWahlID("wahlID", null), "wahlbezirkID is null"), + Arguments.of(new BezirkUndWahlID("wahlID", ""), "wahlbezirkID is is empty"), + Arguments.of(new BezirkUndWahlID("wahlID", " "), "wahlbezirkID is blank")); + } + } + + @Nested + class ValidStimmzettelumschlaegeOrThrow { + + @Test + void should_notThrowException_when_stimmzettelumschlaegeIsEmptyButNotNull() { + val stimmzettelumschlaegeModelToValidate = new StimmzettelumschlaegeModel(null, null, 0, 0); + + Assertions.assertThatNoException().isThrownBy(() -> unitUnderTest.validStimmzettelumschlaegeOrThrow(stimmzettelumschlaegeModelToValidate)); + } + + @Test + void should_throwFachlicheWlsException_when_stimmzettelumschlaegeIsNull() { + val mockedFachlicheWlsException = FachlicheWlsException.withCode("").buildWithMessage("sth failed"); + Mockito.when(exceptionFactory.createFachlicheWlsException(ExceptionConstants.POST_STIMMZETTELUMSCHLAEGE_PARAMETER_UNVOLLSTAENDIG)) + .thenReturn(mockedFachlicheWlsException); + + Assertions.assertThatException().isThrownBy(() -> unitUnderTest.validStimmzettelumschlaegeOrThrow(null)).isSameAs(mockedFachlicheWlsException); + } + } +} From cfb03ad8ebdb791a17d0e2b17fb7de58eadea7c6 Mon Sep 17 00:00:00 2001 From: Robert Jasny Date: Thu, 19 Dec 2024 14:25:42 +0100 Subject: [PATCH 04/14] controller tests added --- ...elumschlaegeControllerIntegrationTest.java | 198 ++++++++++++++++++ .../StimmzettelumschlaegeControllerTest.java | 86 ++++++++ .../StimmzettelumschlaegeDTOMapperTest.java | 66 ++++++ 3 files changed, 350 insertions(+) create mode 100644 wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/rest/stimmzettelumschlaege/StimmzettelumschlaegeControllerIntegrationTest.java create mode 100644 wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/rest/stimmzettelumschlaege/StimmzettelumschlaegeControllerTest.java create mode 100644 wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/rest/stimmzettelumschlaege/StimmzettelumschlaegeDTOMapperTest.java diff --git a/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/rest/stimmzettelumschlaege/StimmzettelumschlaegeControllerIntegrationTest.java b/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/rest/stimmzettelumschlaege/StimmzettelumschlaegeControllerIntegrationTest.java new file mode 100644 index 000000000..7637acc59 --- /dev/null +++ b/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/rest/stimmzettelumschlaege/StimmzettelumschlaegeControllerIntegrationTest.java @@ -0,0 +1,198 @@ +package de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.rest.stimmzettelumschlaege; + +import static de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.TestConstants.SPRING_NO_SECURITY_PROFILE; +import static de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.TestConstants.SPRING_TEST_PROFILE; +import static de.muenchen.oss.wahllokalsystem.wls.common.security.Profiles.NO_BEZIRKS_ID_CHECK; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.tomakehurst.wiremock.client.WireMock; +import com.github.tomakehurst.wiremock.matching.UrlPattern; +import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.MicroServiceApplication; +import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.domain.stimmzettelumschlaege.Stimmzettelumschlaege; +import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.domain.stimmzettelumschlaege.StimmzettelumschlaegeRepository; +import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.exception.ExceptionConstants; +import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.service.stimmzettelumschlaege.StimmzettelumschlaegeModelMapper; +import de.muenchen.oss.wahllokalsystem.wls.common.exception.rest.model.WlsExceptionCategory; +import de.muenchen.oss.wahllokalsystem.wls.common.exception.rest.model.WlsExceptionDTO; +import de.muenchen.oss.wahllokalsystem.wls.common.security.domain.BezirkUndWahlID; +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; +import lombok.val; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.contract.wiremock.AutoConfigureWireMock; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; + +@SpringBootTest(classes = MicroServiceApplication.class) +@AutoConfigureMockMvc +@AutoConfigureWireMock +@ActiveProfiles( + profiles = { SPRING_TEST_PROFILE, SPRING_NO_SECURITY_PROFILE, NO_BEZIRKS_ID_CHECK } +) +public class StimmzettelumschlaegeControllerIntegrationTest { + + @Autowired + StimmzettelumschlaegeRepository stimmzettelumschlaegeRepository; + + @Autowired + StimmzettelumschlaegeModelMapper stimmzettelumschlaegeModelMapper; + + @Autowired + StimmzettelumschlaegeDTOMapper stimmzettelumschlaegeDTOMapper; + + @Autowired + ObjectMapper objectMapper; + + @Autowired + MockMvc mockMvc; + + @AfterEach + void tearDown() { + stimmzettelumschlaegeRepository.deleteAll(); + } + + @Nested + class GetStimmzettelumschlaege { + + @Test + void should_returnData_when_dataIsPresentInRepository() throws Exception { + val wahlID = "wahlID"; + val wahlbezirkID = "wahlbezirkID"; + val urneneroeffnungsUhrzeit = LocalDateTime.now().truncatedTo(ChronoUnit.MILLIS); + val anzahlWaehler = 47; + val anzahlWaehler2 = 11L; + val request = MockMvcRequestBuilders.get(buildStimmzettelumschlaegeURI(wahlID, wahlbezirkID)); + + val entityToFind = new Stimmzettelumschlaege(new BezirkUndWahlID(wahlID, wahlbezirkID), urneneroeffnungsUhrzeit, anzahlWaehler, anzahlWaehler2); + stimmzettelumschlaegeRepository.save(entityToFind); + + val response = mockMvc.perform(request).andExpect(status().isOk()).andReturn().getResponse(); + val responseBodyAsDTO = objectMapper.readValue(response.getContentAsString(), StimmzettelumschlaegeDTO.class); + + val expectedResult = stimmzettelumschlaegeDTOMapper.toDTO(stimmzettelumschlaegeModelMapper.toModel(entityToFind)); + + Assertions.assertThat(responseBodyAsDTO) + .usingRecursiveComparison() + .isEqualTo(expectedResult); + } + + @Test + void should_returnNoContent_when_dataIsNotPresentInRepository() throws Exception { + val wahlID = "wahlID"; + val wahlbezirkID = "wahlbezirkID"; + val request = MockMvcRequestBuilders.get(buildStimmzettelumschlaegeURI(wahlID, wahlbezirkID)); + + val response = mockMvc.perform(request).andExpect(status().isNoContent()).andReturn().getResponse(); + + Assertions.assertThat(response.getContentAsString()).isEmpty(); + } + + @Test + void should_returnBadRequestWlsException_when_validationFailed() throws Exception { + val wahlID = " "; + val wahlbezirkID = "wahlbezirkID"; + val request = MockMvcRequestBuilders.get(buildStimmzettelumschlaegeURI(wahlID, wahlbezirkID)); + + val response = mockMvc.perform(request).andExpect(status().isBadRequest()).andReturn().getResponse(); + val receivedWlsException = objectMapper.readValue(response.getContentAsString(), WlsExceptionDTO.class); + + val expectedWlsExceptionDTO = new WlsExceptionDTO(WlsExceptionCategory.F, ExceptionConstants.GET_STIMMZETTELUMSCHLAEGE_PARAMETER_UNVOLLSTAENDIG.code(), + "WLS-ERGEBNISMELDUNG", ExceptionConstants.GET_STIMMZETTELUMSCHLAEGE_PARAMETER_UNVOLLSTAENDIG.message()); + Assertions.assertThat(receivedWlsException).isEqualTo(expectedWlsExceptionDTO); + } + } + + @Nested + class PostStimmzettelumschlaege { + + @Test + void should_persistData_when_noDataIsPresentInRepository() throws Exception { + val wahlID = "wahlID"; + val wahlbezirkID = "wahlbezirkID"; + val urneneroeffnungsUhrzeit = LocalDateTime.now().truncatedTo(ChronoUnit.MILLIS); + val anzahlWaehler = 47; + val anzahlWaehler2 = 11L; + val requestBody = new StimmzettelumschlaegeDTO(new BezirkUndWahlID(wahlID, wahlbezirkID), urneneroeffnungsUhrzeit, anzahlWaehler, anzahlWaehler2); + + val request = MockMvcRequestBuilders.post(buildStimmzettelumschlaegeURI(wahlID, wahlbezirkID)).with(csrf()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(requestBody)); + + WireMock.stubFor(WireMock.post(UrlPattern.ANY).willReturn(WireMock.aResponse().withStatus(HttpStatus.OK.value()))); + + mockMvc.perform(request).andExpect(status().isOk()).andReturn().getResponse(); + + val entityFromRepo = stimmzettelumschlaegeRepository.findById(requestBody.bezirkUndWahlID()).get(); + val expectedEntity = stimmzettelumschlaegeModelMapper.toEntity(stimmzettelumschlaegeDTOMapper.toModel(requestBody)); + Assertions.assertThat(entityFromRepo) + .usingRecursiveComparison() + .isEqualTo(expectedEntity); + } + + @Test + void should_replaceOldData_when_dataIsPresentInRepository() throws Exception { + val wahlID = "wahlID"; + val wahlbezirkID = "wahlbezirkID"; + val urneneroeffnungsUhrzeit = LocalDateTime.now().truncatedTo(ChronoUnit.MILLIS); + val anzahlWaehler = 47; + val anzahlWaehler2 = 11L; + val requestBody = new StimmzettelumschlaegeDTO(new BezirkUndWahlID(wahlID, wahlbezirkID), urneneroeffnungsUhrzeit, anzahlWaehler, anzahlWaehler2); + + val request = MockMvcRequestBuilders.post(buildStimmzettelumschlaegeURI(wahlID, wahlbezirkID)).with(csrf()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(requestBody)); + + val anzahlWaehlerToReplace = 8; + val anzahlWaehler2ToReplace = 15L; + val entityToReplace = new Stimmzettelumschlaege(requestBody.bezirkUndWahlID(), requestBody.urneneroeffnungsUhrzeit(), anzahlWaehlerToReplace, anzahlWaehler2ToReplace); + Assertions.assertThat(entityToReplace).usingRecursiveComparison().isNotEqualTo(requestBody); + stimmzettelumschlaegeRepository.save(entityToReplace); + + WireMock.stubFor(WireMock.post(UrlPattern.ANY).willReturn(WireMock.aResponse().withStatus(HttpStatus.OK.value()))); + + mockMvc.perform(request).andExpect(status().isOk()).andReturn().getResponse(); + + val entityFromRepo = stimmzettelumschlaegeRepository.findById(requestBody.bezirkUndWahlID()).get(); + val expectedEntity = stimmzettelumschlaegeModelMapper.toEntity(stimmzettelumschlaegeDTOMapper.toModel(requestBody)); + Assertions.assertThat(entityFromRepo) + .usingRecursiveComparison() + .isEqualTo(expectedEntity); + } + + @Test + void should_returnBadRequestWlsException_when_validationFailed() throws Exception { + val wahlID = " "; + val wahlbezirkID = "wahlbezirkID"; + val urneneroeffnungsUhrzeit = LocalDateTime.now().truncatedTo(ChronoUnit.MILLIS); + val anzahlWaehler = 47; + val anzahlWaehler2 = 11L; + val requestBody = new StimmzettelumschlaegeDTO(new BezirkUndWahlID(wahlID, wahlbezirkID), urneneroeffnungsUhrzeit, anzahlWaehler, anzahlWaehler2); + + val request = MockMvcRequestBuilders.post(buildStimmzettelumschlaegeURI(wahlID, wahlbezirkID)).with(csrf()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(requestBody)); + + val response = mockMvc.perform(request).andExpect(status().isBadRequest()).andReturn().getResponse(); + val receivedWlsException = objectMapper.readValue(response.getContentAsString(), WlsExceptionDTO.class); + + val expectedWlsExceptionDTO = new WlsExceptionDTO(WlsExceptionCategory.F, ExceptionConstants.POST_STIMMZETTELUMSCHLAEGE_PARAMETER_UNVOLLSTAENDIG.code(), + "WLS-ERGEBNISMELDUNG", ExceptionConstants.POST_STIMMZETTELUMSCHLAEGE_PARAMETER_UNVOLLSTAENDIG.message()); + Assertions.assertThat(receivedWlsException).isEqualTo(expectedWlsExceptionDTO); + } + } + + private String buildStimmzettelumschlaegeURI(final String wahlID, final String wahlbezirkID) { + return "/businessActions/stimmzettelumschlaege/" + wahlID + "/" + wahlbezirkID; + } +} diff --git a/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/rest/stimmzettelumschlaege/StimmzettelumschlaegeControllerTest.java b/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/rest/stimmzettelumschlaege/StimmzettelumschlaegeControllerTest.java new file mode 100644 index 000000000..bcd2fd3ff --- /dev/null +++ b/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/rest/stimmzettelumschlaege/StimmzettelumschlaegeControllerTest.java @@ -0,0 +1,86 @@ +package de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.rest.stimmzettelumschlaege; + +import static org.mockito.ArgumentMatchers.eq; + +import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.service.stimmzettelumschlaege.StimmzettelumschlaegeModel; +import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.service.stimmzettelumschlaege.StimmzettelumschlaegeService; +import de.muenchen.oss.wahllokalsystem.wls.common.security.domain.BezirkUndWahlID; +import java.util.Optional; +import lombok.val; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.http.HttpStatus; + +@ExtendWith(MockitoExtension.class) +class StimmzettelumschlaegeControllerTest { + + @Mock + StimmzettelumschlaegeService stimmzettelumschlaegeService; + + @Mock + StimmzettelumschlaegeDTOMapper stimmzettelumschlaegeDTOMapper; + + @InjectMocks + StimmzettelumschlaegeController unitUnderTest; + + @Nested + class GetStimmzettelumschlaege { + + @Test + void should_returnDTOWithHttpStatusOk_when_serviceReturnedData() { + val wahlID = "wahlID"; + val wahlbezirkID = "wahlbezirkID"; + + val bezirkUndWahlID = new BezirkUndWahlID("wahlID", "wahlbezirkID"); + val mockedServiceResponse = new StimmzettelumschlaegeModel(bezirkUndWahlID, null, 0,0); + val mockedServiceResponseAsDTO = new StimmzettelumschlaegeDTO(bezirkUndWahlID, null, 0,0L); + + Mockito.when(stimmzettelumschlaegeService.getStimmzettelumschlaege(bezirkUndWahlID)).thenReturn(Optional.of(mockedServiceResponse)); + Mockito.when(stimmzettelumschlaegeDTOMapper.toDTO(mockedServiceResponse)).thenReturn(mockedServiceResponseAsDTO); + + val result = unitUnderTest.getStimmzettelumschlaege(wahlID, wahlbezirkID); + + Assertions.assertThat(result.getBody()).isEqualTo(mockedServiceResponseAsDTO); + Assertions.assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); + } + + @Test + void should_returnEmptyWithHttpStatusNoContent_when_serviceReturnsNoData() { + val wahlID = "wahlID"; + val wahlbezirkID = "wahlbezirkID"; + + val bezirkUndWahlID = new BezirkUndWahlID("wahlID", "wahlbezirkID"); + + Mockito.when(stimmzettelumschlaegeService.getStimmzettelumschlaege(bezirkUndWahlID)).thenReturn(Optional.empty()); + + val result = unitUnderTest.getStimmzettelumschlaege(wahlID, wahlbezirkID); + + Assertions.assertThat(result.getBody()).isNull(); + Assertions.assertThat(result.getStatusCode()).isEqualTo(HttpStatus.NO_CONTENT); + } + } + + @Nested + class PostStimmzettelumschlaege { + + @Test + void should_callServiceWithModel_when_calledWithData() { + val wahlID = "wahlID"; + val wahlbezirkID = "wahlbezirkID"; + val stimmzettelumschlaegeDTO = new StimmzettelumschlaegeDTO(new BezirkUndWahlID(wahlID, wahlbezirkID), null, 0,0L); + + val mockedStimmzettelumschlaegeModel = new StimmzettelumschlaegeModel(new BezirkUndWahlID(wahlID, wahlbezirkID), null, 0,0); + Mockito.when(stimmzettelumschlaegeDTOMapper.toModel(stimmzettelumschlaegeDTO)).thenReturn(mockedStimmzettelumschlaegeModel); + + unitUnderTest.postStimmzettelumschlaege(wahlID, wahlbezirkID, stimmzettelumschlaegeDTO); + + Mockito.verify(stimmzettelumschlaegeService).setStimmzettelumschlaege(eq(new BezirkUndWahlID(wahlID, wahlbezirkID)), eq(mockedStimmzettelumschlaegeModel)); + } + } +} diff --git a/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/rest/stimmzettelumschlaege/StimmzettelumschlaegeDTOMapperTest.java b/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/rest/stimmzettelumschlaege/StimmzettelumschlaegeDTOMapperTest.java new file mode 100644 index 000000000..991644908 --- /dev/null +++ b/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/rest/stimmzettelumschlaege/StimmzettelumschlaegeDTOMapperTest.java @@ -0,0 +1,66 @@ +package de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.rest.stimmzettelumschlaege; + +import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.service.stimmzettelumschlaege.StimmzettelumschlaegeModel; +import de.muenchen.oss.wahllokalsystem.wls.common.security.domain.BezirkUndWahlID; +import java.time.LocalDateTime; +import lombok.val; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mapstruct.factory.Mappers; + +class StimmzettelumschlaegeDTOMapperTest { + + StimmzettelumschlaegeDTOMapper unitUnderTest = Mappers.getMapper(StimmzettelumschlaegeDTOMapper.class); + + @Nested + class ToDTO { + + @Test + void should_returnNull_when_givenNull() { + Assertions.assertThat(unitUnderTest.toDTO(null)).isNull(); + } + + @Test + void should_returnDTO_when_givenModel() { + val wahlID = "wahlID"; + val wahlbezirkID = "wahlbezirkID"; + val urneneroeffnungsUhrzeit = LocalDateTime.now(); + val anzahlWaehler = 47; + val anzahlWaehler2 = 11; + val modelToMap = new StimmzettelumschlaegeModel(new BezirkUndWahlID(wahlID, wahlbezirkID), urneneroeffnungsUhrzeit, anzahlWaehler, anzahlWaehler2); + + val result = unitUnderTest.toDTO(modelToMap); + + val expectedResult = new StimmzettelumschlaegeDTO(new BezirkUndWahlID(wahlID, wahlbezirkID), urneneroeffnungsUhrzeit, anzahlWaehler, + (long) anzahlWaehler2); + Assertions.assertThat(result).isEqualTo(expectedResult); + } + } + + @Nested + class ToModel { + + @Test + void should_returnNull_when_givenNull() { + Assertions.assertThat(unitUnderTest.toModel(null)).isNull(); + } + + @Test + void should_returnModel_when_givenDTO() { + val wahlID = "wahlID"; + val wahlbezirkID = "wahlbezirkID"; + val urneneroeffnungsUhrzeit = LocalDateTime.now(); + val anzahlWaehler = 47; + val anzahlWaehler2 = 11; + val dtoToMap = new StimmzettelumschlaegeDTO(new BezirkUndWahlID(wahlID, wahlbezirkID), urneneroeffnungsUhrzeit, anzahlWaehler, + (long) anzahlWaehler2); + + val result = unitUnderTest.toModel(dtoToMap); + + val expectedResult = new StimmzettelumschlaegeModel(new BezirkUndWahlID(wahlID, wahlbezirkID), urneneroeffnungsUhrzeit, anzahlWaehler, + anzahlWaehler2); + Assertions.assertThat(result).isEqualTo(expectedResult); + } + } +} From c0c7a1abb1c7a7646570017ef219a0a0f1a13915 Mon Sep 17 00:00:00 2001 From: Robert Jasny Date: Thu, 19 Dec 2024 14:59:43 +0100 Subject: [PATCH 05/14] Doku aktualisiert --- docs/src/services/ergebnismeldung-service/index.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/src/services/ergebnismeldung-service/index.md b/docs/src/services/ergebnismeldung-service/index.md index ae9cf06a8..e629345be 100644 --- a/docs/src/services/ergebnismeldung-service/index.md +++ b/docs/src/services/ergebnismeldung-service/index.md @@ -54,6 +54,11 @@ Welche Aktionen für die jeweiligen Dokumente bereits in einem Wahllokal vollzog #### Erfassung der Stimmabgaben mit Wahlschein Im Wählerverzeichnis wird bei einer Wahl durch den Schriftführer über den sogenannten "Stimmabgabevermerk" vermerkt, -wenn ein Wahlberechtigter mit Wahlschein seinen Stimmzettel in die Urne gelegt hat. +wenn ein Wahlberechtigter mit Wahlschein seinen Stimmzettel in die Urne im Wahllokal gelegt hat. Über das Schreiben und Lesen der Wahlscheine kann die aktuelle Anzahl an Stimmabgabevermerken an WLS übermittelt bzw. ausgelesen werden. + +### Erfassung der Anzahl an Stimmzettelumschlaegen +Bei der Briefwahl zählt der Wahlvorstand die Stimmzettelumschlaege. +Über das Schreiben und Lesen der Stimmzettelumschlaege kann die aktuelle Anzahl eines Wahlbezirkes für eine Wahl an WLS +übermittelt bzw. ausgelesen werden. \ No newline at end of file From 7d968bcd36023ecd18a4579a85925b7d0da33f8e Mon Sep 17 00:00:00 2001 From: Robert Jasny Date: Thu, 19 Dec 2024 15:21:34 +0100 Subject: [PATCH 06/14] Update wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/rest/stimmzettelumschlaege/StimmzettelumschlaegeControllerIntegrationTest.java Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- .../StimmzettelumschlaegeControllerIntegrationTest.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/rest/stimmzettelumschlaege/StimmzettelumschlaegeControllerIntegrationTest.java b/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/rest/stimmzettelumschlaege/StimmzettelumschlaegeControllerIntegrationTest.java index 7637acc59..fa7b80751 100644 --- a/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/rest/stimmzettelumschlaege/StimmzettelumschlaegeControllerIntegrationTest.java +++ b/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/rest/stimmzettelumschlaege/StimmzettelumschlaegeControllerIntegrationTest.java @@ -129,8 +129,10 @@ void should_persistData_when_noDataIsPresentInRepository() throws Exception { .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(requestBody)); - WireMock.stubFor(WireMock.post(UrlPattern.ANY).willReturn(WireMock.aResponse().withStatus(HttpStatus.OK.value()))); - + WireMock.stubFor(WireMock.post(WireMock.urlPathMatching("/businessActions/stimmzettelumschlaege/.*")) + .willReturn(WireMock.aResponse() + .withStatus(HttpStatus.OK.value()) + .withHeader("Content-Type", "application/json"))); mockMvc.perform(request).andExpect(status().isOk()).andReturn().getResponse(); val entityFromRepo = stimmzettelumschlaegeRepository.findById(requestBody.bezirkUndWahlID()).get(); From 755b851f53cfd322cf6644b160de6effd9eb8104 Mon Sep 17 00:00:00 2001 From: Robert Jasny Date: Thu, 19 Dec 2024 15:38:46 +0100 Subject: [PATCH 07/14] mvn spotless:apply --- ...timmzettelumschlaegeControllerIntegrationTest.java | 9 ++++++--- .../StimmzettelumschlaegeControllerTest.java | 11 ++++++----- .../StimmzettelumschlaegeDTOMapperTest.java | 6 +++--- .../StimmzettelumschlaegeModelMapperTest.java | 9 ++++++--- .../StimmzettelumschlaegeServiceSecurityTest.java | 9 ++++++--- .../StimmzettelumschlaegeServiceTest.java | 3 ++- .../ergebnismeldungservice/utils/Authorities.java | 8 ++++---- 7 files changed, 33 insertions(+), 22 deletions(-) diff --git a/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/rest/stimmzettelumschlaege/StimmzettelumschlaegeControllerIntegrationTest.java b/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/rest/stimmzettelumschlaege/StimmzettelumschlaegeControllerIntegrationTest.java index 7637acc59..1e66f4493 100644 --- a/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/rest/stimmzettelumschlaege/StimmzettelumschlaegeControllerIntegrationTest.java +++ b/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/rest/stimmzettelumschlaege/StimmzettelumschlaegeControllerIntegrationTest.java @@ -107,7 +107,8 @@ void should_returnBadRequestWlsException_when_validationFailed() throws Exceptio val response = mockMvc.perform(request).andExpect(status().isBadRequest()).andReturn().getResponse(); val receivedWlsException = objectMapper.readValue(response.getContentAsString(), WlsExceptionDTO.class); - val expectedWlsExceptionDTO = new WlsExceptionDTO(WlsExceptionCategory.F, ExceptionConstants.GET_STIMMZETTELUMSCHLAEGE_PARAMETER_UNVOLLSTAENDIG.code(), + val expectedWlsExceptionDTO = new WlsExceptionDTO(WlsExceptionCategory.F, + ExceptionConstants.GET_STIMMZETTELUMSCHLAEGE_PARAMETER_UNVOLLSTAENDIG.code(), "WLS-ERGEBNISMELDUNG", ExceptionConstants.GET_STIMMZETTELUMSCHLAEGE_PARAMETER_UNVOLLSTAENDIG.message()); Assertions.assertThat(receivedWlsException).isEqualTo(expectedWlsExceptionDTO); } @@ -155,7 +156,8 @@ void should_replaceOldData_when_dataIsPresentInRepository() throws Exception { val anzahlWaehlerToReplace = 8; val anzahlWaehler2ToReplace = 15L; - val entityToReplace = new Stimmzettelumschlaege(requestBody.bezirkUndWahlID(), requestBody.urneneroeffnungsUhrzeit(), anzahlWaehlerToReplace, anzahlWaehler2ToReplace); + val entityToReplace = new Stimmzettelumschlaege(requestBody.bezirkUndWahlID(), requestBody.urneneroeffnungsUhrzeit(), anzahlWaehlerToReplace, + anzahlWaehler2ToReplace); Assertions.assertThat(entityToReplace).usingRecursiveComparison().isNotEqualTo(requestBody); stimmzettelumschlaegeRepository.save(entityToReplace); @@ -186,7 +188,8 @@ void should_returnBadRequestWlsException_when_validationFailed() throws Exceptio val response = mockMvc.perform(request).andExpect(status().isBadRequest()).andReturn().getResponse(); val receivedWlsException = objectMapper.readValue(response.getContentAsString(), WlsExceptionDTO.class); - val expectedWlsExceptionDTO = new WlsExceptionDTO(WlsExceptionCategory.F, ExceptionConstants.POST_STIMMZETTELUMSCHLAEGE_PARAMETER_UNVOLLSTAENDIG.code(), + val expectedWlsExceptionDTO = new WlsExceptionDTO(WlsExceptionCategory.F, + ExceptionConstants.POST_STIMMZETTELUMSCHLAEGE_PARAMETER_UNVOLLSTAENDIG.code(), "WLS-ERGEBNISMELDUNG", ExceptionConstants.POST_STIMMZETTELUMSCHLAEGE_PARAMETER_UNVOLLSTAENDIG.message()); Assertions.assertThat(receivedWlsException).isEqualTo(expectedWlsExceptionDTO); } diff --git a/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/rest/stimmzettelumschlaege/StimmzettelumschlaegeControllerTest.java b/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/rest/stimmzettelumschlaege/StimmzettelumschlaegeControllerTest.java index bcd2fd3ff..d80de63d2 100644 --- a/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/rest/stimmzettelumschlaege/StimmzettelumschlaegeControllerTest.java +++ b/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/rest/stimmzettelumschlaege/StimmzettelumschlaegeControllerTest.java @@ -38,8 +38,8 @@ void should_returnDTOWithHttpStatusOk_when_serviceReturnedData() { val wahlbezirkID = "wahlbezirkID"; val bezirkUndWahlID = new BezirkUndWahlID("wahlID", "wahlbezirkID"); - val mockedServiceResponse = new StimmzettelumschlaegeModel(bezirkUndWahlID, null, 0,0); - val mockedServiceResponseAsDTO = new StimmzettelumschlaegeDTO(bezirkUndWahlID, null, 0,0L); + val mockedServiceResponse = new StimmzettelumschlaegeModel(bezirkUndWahlID, null, 0, 0); + val mockedServiceResponseAsDTO = new StimmzettelumschlaegeDTO(bezirkUndWahlID, null, 0, 0L); Mockito.when(stimmzettelumschlaegeService.getStimmzettelumschlaege(bezirkUndWahlID)).thenReturn(Optional.of(mockedServiceResponse)); Mockito.when(stimmzettelumschlaegeDTOMapper.toDTO(mockedServiceResponse)).thenReturn(mockedServiceResponseAsDTO); @@ -73,14 +73,15 @@ class PostStimmzettelumschlaege { void should_callServiceWithModel_when_calledWithData() { val wahlID = "wahlID"; val wahlbezirkID = "wahlbezirkID"; - val stimmzettelumschlaegeDTO = new StimmzettelumschlaegeDTO(new BezirkUndWahlID(wahlID, wahlbezirkID), null, 0,0L); + val stimmzettelumschlaegeDTO = new StimmzettelumschlaegeDTO(new BezirkUndWahlID(wahlID, wahlbezirkID), null, 0, 0L); - val mockedStimmzettelumschlaegeModel = new StimmzettelumschlaegeModel(new BezirkUndWahlID(wahlID, wahlbezirkID), null, 0,0); + val mockedStimmzettelumschlaegeModel = new StimmzettelumschlaegeModel(new BezirkUndWahlID(wahlID, wahlbezirkID), null, 0, 0); Mockito.when(stimmzettelumschlaegeDTOMapper.toModel(stimmzettelumschlaegeDTO)).thenReturn(mockedStimmzettelumschlaegeModel); unitUnderTest.postStimmzettelumschlaege(wahlID, wahlbezirkID, stimmzettelumschlaegeDTO); - Mockito.verify(stimmzettelumschlaegeService).setStimmzettelumschlaege(eq(new BezirkUndWahlID(wahlID, wahlbezirkID)), eq(mockedStimmzettelumschlaegeModel)); + Mockito.verify(stimmzettelumschlaegeService).setStimmzettelumschlaege(eq(new BezirkUndWahlID(wahlID, wahlbezirkID)), + eq(mockedStimmzettelumschlaegeModel)); } } } diff --git a/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/rest/stimmzettelumschlaege/StimmzettelumschlaegeDTOMapperTest.java b/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/rest/stimmzettelumschlaege/StimmzettelumschlaegeDTOMapperTest.java index 991644908..c8b5da035 100644 --- a/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/rest/stimmzettelumschlaege/StimmzettelumschlaegeDTOMapperTest.java +++ b/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/rest/stimmzettelumschlaege/StimmzettelumschlaegeDTOMapperTest.java @@ -33,7 +33,7 @@ void should_returnDTO_when_givenModel() { val result = unitUnderTest.toDTO(modelToMap); val expectedResult = new StimmzettelumschlaegeDTO(new BezirkUndWahlID(wahlID, wahlbezirkID), urneneroeffnungsUhrzeit, anzahlWaehler, - (long) anzahlWaehler2); + (long) anzahlWaehler2); Assertions.assertThat(result).isEqualTo(expectedResult); } } @@ -54,12 +54,12 @@ void should_returnModel_when_givenDTO() { val anzahlWaehler = 47; val anzahlWaehler2 = 11; val dtoToMap = new StimmzettelumschlaegeDTO(new BezirkUndWahlID(wahlID, wahlbezirkID), urneneroeffnungsUhrzeit, anzahlWaehler, - (long) anzahlWaehler2); + (long) anzahlWaehler2); val result = unitUnderTest.toModel(dtoToMap); val expectedResult = new StimmzettelumschlaegeModel(new BezirkUndWahlID(wahlID, wahlbezirkID), urneneroeffnungsUhrzeit, anzahlWaehler, - anzahlWaehler2); + anzahlWaehler2); Assertions.assertThat(result).isEqualTo(expectedResult); } } diff --git a/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeModelMapperTest.java b/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeModelMapperTest.java index 1e6ac1035..10c8d75ee 100644 --- a/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeModelMapperTest.java +++ b/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeModelMapperTest.java @@ -28,7 +28,8 @@ void should_returnStimmzettelumschlaegeModel_when_givenStimmzettelumschlaegeEnti val urneneroeffnungsUhrzeit = LocalDateTime.now(); val anzahlWaehler = 47; val anzahlWaehler2 = 11; - val wahlscheineEntity = new Stimmzettelumschlaege(new BezirkUndWahlID(wahlID, wahlbezirkID), urneneroeffnungsUhrzeit, anzahlWaehler, (long) anzahlWaehler2); + val wahlscheineEntity = new Stimmzettelumschlaege(new BezirkUndWahlID(wahlID, wahlbezirkID), urneneroeffnungsUhrzeit, anzahlWaehler, + (long) anzahlWaehler2); val result = unitUnderTest.toModel(wahlscheineEntity); val expectedResult = new StimmzettelumschlaegeModel( @@ -53,10 +54,12 @@ void should_returnStimmzettelumschlaegeEntity_when_givenStimmzettelumschlaegeMod val urneneroeffnungsUhrzeit = LocalDateTime.now(); val anzahlWaehler = 47; val anzahlWaehler2 = 11; - val wahlscheineModel = new StimmzettelumschlaegeModel(new BezirkUndWahlID(wahlID, wahlbezirkID), urneneroeffnungsUhrzeit, anzahlWaehler, anzahlWaehler2); + val wahlscheineModel = new StimmzettelumschlaegeModel(new BezirkUndWahlID(wahlID, wahlbezirkID), urneneroeffnungsUhrzeit, anzahlWaehler, + anzahlWaehler2); val result = unitUnderTest.toEntity(wahlscheineModel); - val expectedResult = new Stimmzettelumschlaege(new BezirkUndWahlID(wahlID, wahlbezirkID), urneneroeffnungsUhrzeit, anzahlWaehler, (long) anzahlWaehler2); + val expectedResult = new Stimmzettelumschlaege(new BezirkUndWahlID(wahlID, wahlbezirkID), urneneroeffnungsUhrzeit, anzahlWaehler, + (long) anzahlWaehler2); Assertions.assertThat(result).isEqualTo(expectedResult); } diff --git a/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeServiceSecurityTest.java b/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeServiceSecurityTest.java index 45bc684ef..47de3119e 100644 --- a/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeServiceSecurityTest.java +++ b/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeServiceSecurityTest.java @@ -102,7 +102,8 @@ void should_throwAccessDeniedException_when_allRequiredAuthoritiesArePresentButB Mockito.when(bezirkIDPermissionEvaluator.tokenUserBezirkIdMatches(eq(wahlbezirkID), notNull())).thenReturn(false); - Assertions.assertThatException().isThrownBy(() -> unitUnderTest.setStimmzettelumschlaege(id, stimmzettelumschlaege)).isInstanceOf(AccessDeniedException.class); + Assertions.assertThatException().isThrownBy(() -> unitUnderTest.setStimmzettelumschlaege(id, stimmzettelumschlaege)) + .isInstanceOf(AccessDeniedException.class); } @Test @@ -115,7 +116,8 @@ void should_throwAccessDeniedException_when_serviceSetStimmzettelumschlaegeAutho Mockito.when(bezirkIDPermissionEvaluator.tokenUserBezirkIdMatches(eq(wahlbezirkID), notNull())).thenReturn(true); - Assertions.assertThatException().isThrownBy(() -> unitUnderTest.setStimmzettelumschlaege(id, stimmzettelumschlaege)).isInstanceOf(AccessDeniedException.class); + Assertions.assertThatException().isThrownBy(() -> unitUnderTest.setStimmzettelumschlaege(id, stimmzettelumschlaege)) + .isInstanceOf(AccessDeniedException.class); } @Test @@ -128,7 +130,8 @@ void should_throwTechnischeWlsException_when_repositoryWriteStimmzettelumschlaeg Mockito.when(bezirkIDPermissionEvaluator.tokenUserBezirkIdMatches(eq(wahlbezirkID), notNull())).thenReturn(true); - Assertions.assertThatException().isThrownBy(() -> unitUnderTest.setStimmzettelumschlaege(id, stimmzettelumschlaege)).isInstanceOf(TechnischeWlsException.class); + Assertions.assertThatException().isThrownBy(() -> unitUnderTest.setStimmzettelumschlaege(id, stimmzettelumschlaege)) + .isInstanceOf(TechnischeWlsException.class); } private StimmzettelumschlaegeModel createStimmzettelumschlaegeModel(BezirkUndWahlID id) { diff --git a/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeServiceTest.java b/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeServiceTest.java index d66eaf781..970ae49ad 100644 --- a/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeServiceTest.java +++ b/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeServiceTest.java @@ -140,7 +140,8 @@ void should_throwTechnischeWlsException_when_savingFailed() { Mockito.when(exceptionFactory.createTechnischeWlsException(ExceptionConstants.STIMMZETTELUMSCHLAEGE_UNSAVEABLE)) .thenReturn(mockedExceptionFactoryWlsException); - Assertions.assertThatThrownBy(() -> unitUnderTest.setStimmzettelumschlaege(id, stimmzettelumschlaegeToSet)).isSameAs(mockedExceptionFactoryWlsException); + Assertions.assertThatThrownBy(() -> unitUnderTest.setStimmzettelumschlaege(id, stimmzettelumschlaegeToSet)) + .isSameAs(mockedExceptionFactoryWlsException); } private StimmzettelumschlaegeModel createStimmzettelumschlaegeModel(final BezirkUndWahlID id) { diff --git a/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/utils/Authorities.java b/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/utils/Authorities.java index 5e89009b6..01cb100bd 100644 --- a/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/utils/Authorities.java +++ b/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/utils/Authorities.java @@ -72,13 +72,13 @@ public class Authorities { }; public static final String[] ALL_AUTHORITIES_GET_STIMMZETTELUMSCHLAEGE = new String[] { - SERVICE_GET_STIMMZETTELUMSCHLAEGE, - REPOSITORY_READ_STIMMZETTELUMSCHLAEGE + SERVICE_GET_STIMMZETTELUMSCHLAEGE, + REPOSITORY_READ_STIMMZETTELUMSCHLAEGE }; public static final String[] ALL_AUTHORITIES_SET_STIMMZETTELUMSCHLAEGE = new String[] { - SERVICE_SET_STIMMZETTELUMSCHLAEGE, - REPOSITORY_WRITE_STIMMZETTELUMSCHLAEGE + SERVICE_SET_STIMMZETTELUMSCHLAEGE, + REPOSITORY_WRITE_STIMMZETTELUMSCHLAEGE }; } From c88c2f2e40fe7210c2ec0a972ede8ac106a042dd Mon Sep 17 00:00:00 2001 From: Robert Jasny Date: Fri, 20 Dec 2024 12:05:05 +0100 Subject: [PATCH 08/14] Wiremock not needed --- ...timmzettelumschlaegeControllerIntegrationTest.java | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/rest/stimmzettelumschlaege/StimmzettelumschlaegeControllerIntegrationTest.java b/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/rest/stimmzettelumschlaege/StimmzettelumschlaegeControllerIntegrationTest.java index 722566087..deb0b3ff8 100644 --- a/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/rest/stimmzettelumschlaege/StimmzettelumschlaegeControllerIntegrationTest.java +++ b/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/rest/stimmzettelumschlaege/StimmzettelumschlaegeControllerIntegrationTest.java @@ -7,8 +7,6 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import com.fasterxml.jackson.databind.ObjectMapper; -import com.github.tomakehurst.wiremock.client.WireMock; -import com.github.tomakehurst.wiremock.matching.UrlPattern; import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.MicroServiceApplication; import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.domain.stimmzettelumschlaege.Stimmzettelumschlaege; import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.domain.stimmzettelumschlaege.StimmzettelumschlaegeRepository; @@ -27,8 +25,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.cloud.contract.wiremock.AutoConfigureWireMock; -import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.web.servlet.MockMvc; @@ -36,7 +32,6 @@ @SpringBootTest(classes = MicroServiceApplication.class) @AutoConfigureMockMvc -@AutoConfigureWireMock @ActiveProfiles( profiles = { SPRING_TEST_PROFILE, SPRING_NO_SECURITY_PROFILE, NO_BEZIRKS_ID_CHECK } ) @@ -130,10 +125,6 @@ void should_persistData_when_noDataIsPresentInRepository() throws Exception { .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(requestBody)); - WireMock.stubFor(WireMock.post(WireMock.urlPathMatching("/businessActions/stimmzettelumschlaege/.*")) - .willReturn(WireMock.aResponse() - .withStatus(HttpStatus.OK.value()) - .withHeader("Content-Type", "application/json"))); mockMvc.perform(request).andExpect(status().isOk()).andReturn().getResponse(); val entityFromRepo = stimmzettelumschlaegeRepository.findById(requestBody.bezirkUndWahlID()).get(); @@ -163,8 +154,6 @@ void should_replaceOldData_when_dataIsPresentInRepository() throws Exception { Assertions.assertThat(entityToReplace).usingRecursiveComparison().isNotEqualTo(requestBody); stimmzettelumschlaegeRepository.save(entityToReplace); - WireMock.stubFor(WireMock.post(UrlPattern.ANY).willReturn(WireMock.aResponse().withStatus(HttpStatus.OK.value()))); - mockMvc.perform(request).andExpect(status().isOk()).andReturn().getResponse(); val entityFromRepo = stimmzettelumschlaegeRepository.findById(requestBody.bezirkUndWahlID()).get(); From 2d1569a2b4ecaf5545dd812dbb126bfe89cdd035 Mon Sep 17 00:00:00 2001 From: Robert Jasny Date: Fri, 20 Dec 2024 12:12:30 +0100 Subject: [PATCH 09/14] use LocalDateTimeComparators instead of truncating test data --- ...mmzettelumschlaegeControllerIntegrationTest.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/rest/stimmzettelumschlaege/StimmzettelumschlaegeControllerIntegrationTest.java b/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/rest/stimmzettelumschlaege/StimmzettelumschlaegeControllerIntegrationTest.java index deb0b3ff8..fd8bbe043 100644 --- a/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/rest/stimmzettelumschlaege/StimmzettelumschlaegeControllerIntegrationTest.java +++ b/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/rest/stimmzettelumschlaege/StimmzettelumschlaegeControllerIntegrationTest.java @@ -12,11 +12,11 @@ import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.domain.stimmzettelumschlaege.StimmzettelumschlaegeRepository; import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.exception.ExceptionConstants; import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.service.stimmzettelumschlaege.StimmzettelumschlaegeModelMapper; +import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.utils.LocalDateTimeComparators; import de.muenchen.oss.wahllokalsystem.wls.common.exception.rest.model.WlsExceptionCategory; import de.muenchen.oss.wahllokalsystem.wls.common.exception.rest.model.WlsExceptionDTO; import de.muenchen.oss.wahllokalsystem.wls.common.security.domain.BezirkUndWahlID; import java.time.LocalDateTime; -import java.time.temporal.ChronoUnit; import lombok.val; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.AfterEach; @@ -64,7 +64,7 @@ class GetStimmzettelumschlaege { void should_returnData_when_dataIsPresentInRepository() throws Exception { val wahlID = "wahlID"; val wahlbezirkID = "wahlbezirkID"; - val urneneroeffnungsUhrzeit = LocalDateTime.now().truncatedTo(ChronoUnit.MILLIS); + val urneneroeffnungsUhrzeit = LocalDateTime.now(); val anzahlWaehler = 47; val anzahlWaehler2 = 11L; val request = MockMvcRequestBuilders.get(buildStimmzettelumschlaegeURI(wahlID, wahlbezirkID)); @@ -79,6 +79,7 @@ void should_returnData_when_dataIsPresentInRepository() throws Exception { Assertions.assertThat(responseBodyAsDTO) .usingRecursiveComparison() + .withComparatorForType(LocalDateTimeComparators.PRECISION_MILLISECONDS, LocalDateTime.class) .isEqualTo(expectedResult); } @@ -116,7 +117,7 @@ class PostStimmzettelumschlaege { void should_persistData_when_noDataIsPresentInRepository() throws Exception { val wahlID = "wahlID"; val wahlbezirkID = "wahlbezirkID"; - val urneneroeffnungsUhrzeit = LocalDateTime.now().truncatedTo(ChronoUnit.MILLIS); + val urneneroeffnungsUhrzeit = LocalDateTime.now(); val anzahlWaehler = 47; val anzahlWaehler2 = 11L; val requestBody = new StimmzettelumschlaegeDTO(new BezirkUndWahlID(wahlID, wahlbezirkID), urneneroeffnungsUhrzeit, anzahlWaehler, anzahlWaehler2); @@ -131,6 +132,7 @@ void should_persistData_when_noDataIsPresentInRepository() throws Exception { val expectedEntity = stimmzettelumschlaegeModelMapper.toEntity(stimmzettelumschlaegeDTOMapper.toModel(requestBody)); Assertions.assertThat(entityFromRepo) .usingRecursiveComparison() + .withComparatorForType(LocalDateTimeComparators.PRECISION_MILLISECONDS, LocalDateTime.class) .isEqualTo(expectedEntity); } @@ -138,7 +140,7 @@ void should_persistData_when_noDataIsPresentInRepository() throws Exception { void should_replaceOldData_when_dataIsPresentInRepository() throws Exception { val wahlID = "wahlID"; val wahlbezirkID = "wahlbezirkID"; - val urneneroeffnungsUhrzeit = LocalDateTime.now().truncatedTo(ChronoUnit.MILLIS); + val urneneroeffnungsUhrzeit = LocalDateTime.now(); val anzahlWaehler = 47; val anzahlWaehler2 = 11L; val requestBody = new StimmzettelumschlaegeDTO(new BezirkUndWahlID(wahlID, wahlbezirkID), urneneroeffnungsUhrzeit, anzahlWaehler, anzahlWaehler2); @@ -160,6 +162,7 @@ void should_replaceOldData_when_dataIsPresentInRepository() throws Exception { val expectedEntity = stimmzettelumschlaegeModelMapper.toEntity(stimmzettelumschlaegeDTOMapper.toModel(requestBody)); Assertions.assertThat(entityFromRepo) .usingRecursiveComparison() + .withComparatorForType(LocalDateTimeComparators.PRECISION_MILLISECONDS, LocalDateTime.class) .isEqualTo(expectedEntity); } @@ -167,7 +170,7 @@ void should_replaceOldData_when_dataIsPresentInRepository() throws Exception { void should_returnBadRequestWlsException_when_validationFailed() throws Exception { val wahlID = " "; val wahlbezirkID = "wahlbezirkID"; - val urneneroeffnungsUhrzeit = LocalDateTime.now().truncatedTo(ChronoUnit.MILLIS); + val urneneroeffnungsUhrzeit = LocalDateTime.now(); val anzahlWaehler = 47; val anzahlWaehler2 = 11L; val requestBody = new StimmzettelumschlaegeDTO(new BezirkUndWahlID(wahlID, wahlbezirkID), urneneroeffnungsUhrzeit, anzahlWaehler, anzahlWaehler2); From 77aed0ad405237b47cb71ca322a0d4bbe28894f9 Mon Sep 17 00:00:00 2001 From: Robert Jasny Date: Fri, 20 Dec 2024 16:01:57 +0100 Subject: [PATCH 10/14] check for wahlbezirkArt from Authentication added --- .../common/security/AnonymousHandler.java | 21 ++++++ .../security/AuthenticationHandler.java | 11 +++ .../common/security/JWTHandler.java | 23 +++++++ .../exception/ExceptionConstants.java | 2 + .../StimmzettelumschlaegeService.java | 19 ++++++ .../StimmzettelumschlaegeValidator.java | 7 ++ .../WahlbezirkArtModel.java | 5 ++ ...mmzettelumschlaegeServiceSecurityTest.java | 13 ++-- .../StimmzettelumschlaegeServiceTest.java | 33 ++++++++- .../utils/WithMockUserAsJwt.java | 35 ++++++++++ ...thMockUserAsJwtSecurityContextFactory.java | 67 +++++++++++++++++++ 11 files changed, 230 insertions(+), 6 deletions(-) create mode 100644 wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/common/security/AnonymousHandler.java create mode 100644 wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/common/security/AuthenticationHandler.java create mode 100644 wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/common/security/JWTHandler.java create mode 100644 wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/WahlbezirkArtModel.java create mode 100644 wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/utils/WithMockUserAsJwt.java create mode 100644 wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/utils/WithMockUserAsJwtSecurityContextFactory.java diff --git a/wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/common/security/AnonymousHandler.java b/wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/common/security/AnonymousHandler.java new file mode 100644 index 000000000..acde97f95 --- /dev/null +++ b/wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/common/security/AnonymousHandler.java @@ -0,0 +1,21 @@ +package de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.common.security; + +import java.util.Optional; +import org.springframework.context.annotation.Profile; +import org.springframework.security.authentication.AnonymousAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.stereotype.Component; + +@Component +@Profile("no-security") +public class AnonymousHandler implements AuthenticationHandler { + @Override + public boolean canHandle(Authentication authentication) { + return authentication instanceof AnonymousAuthenticationToken; + } + + @Override + public Optional getDetail(String detailKey, Authentication authentication) { + return Optional.empty(); + } +} diff --git a/wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/common/security/AuthenticationHandler.java b/wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/common/security/AuthenticationHandler.java new file mode 100644 index 000000000..9c34abf52 --- /dev/null +++ b/wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/common/security/AuthenticationHandler.java @@ -0,0 +1,11 @@ +package de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.common.security; + +import java.util.Optional; +import org.springframework.security.core.Authentication; + +public interface AuthenticationHandler { + + boolean canHandle(Authentication authentication); + + Optional getDetail(String detailKey, Authentication authentication); +} diff --git a/wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/common/security/JWTHandler.java b/wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/common/security/JWTHandler.java new file mode 100644 index 000000000..6b41df8d6 --- /dev/null +++ b/wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/common/security/JWTHandler.java @@ -0,0 +1,23 @@ +package de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.common.security; + +import java.util.Optional; +import org.springframework.security.core.Authentication; +import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; +import org.springframework.stereotype.Component; + +@Component +public class JWTHandler implements AuthenticationHandler { + + @Override + public boolean canHandle(final Authentication authentication) { + return authentication instanceof JwtAuthenticationToken; + } + + public Optional getDetail(final String detailKey, final Authentication authentication) { + if (authentication instanceof JwtAuthenticationToken jwtToken) { + return Optional.ofNullable(jwtToken.getToken().getClaimAsString(detailKey)); + } else { + return Optional.empty(); + } + } +} diff --git a/wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/exception/ExceptionConstants.java b/wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/exception/ExceptionConstants.java index 1922034e6..c8b265849 100644 --- a/wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/exception/ExceptionConstants.java +++ b/wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/exception/ExceptionConstants.java @@ -32,6 +32,8 @@ public class ExceptionConstants { "postStimmzettelumschlaege: Parameter unvollstaendig"); public static final ExceptionDataWrapper STIMMZETTELUMSCHLAEGE_UNSAVEABLE = new ExceptionDataWrapper("619", "postStimmzettelumschlaege: Die Stimmzettelumschlaege konnten nicht gespeichert werden."); + public static final ExceptionDataWrapper WAHLBEZIRKART_NOT_LOADABLE = new ExceptionDataWrapper("800", + "Die Wahlbezirkart des Principals konnte nicht geladen werden."); public static final ExceptionDataWrapper KOMMUNIKATIONSFEHLER_MIT_MONITORING = new ExceptionDataWrapper("100", "Bei der Kommunikation mit dem MonitoringService kam es zu einem Fehler."); diff --git a/wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeService.java b/wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeService.java index 6558153d2..0911543a5 100644 --- a/wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeService.java +++ b/wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeService.java @@ -1,14 +1,17 @@ package de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.service.stimmzettelumschlaege; +import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.common.security.AuthenticationHandler; import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.domain.stimmzettelumschlaege.StimmzettelumschlaegeRepository; import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.exception.ExceptionConstants; import de.muenchen.oss.wahllokalsystem.wls.common.exception.util.ExceptionFactory; import de.muenchen.oss.wahllokalsystem.wls.common.security.domain.BezirkUndWahlID; +import java.util.Collection; import java.util.Optional; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import lombok.val; import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.parameters.P; import org.springframework.stereotype.Service; @@ -17,10 +20,13 @@ @Slf4j public class StimmzettelumschlaegeService { + private static final String WAHLBEZIRK_ART_USER_DETAIL_KEY = "wahlbezirksArt"; + private final StimmzettelumschlaegeRepository stimmzettelumschlaegeRepository; private final StimmzettelumschlaegeModelMapper stimmzettelumschlaegeModelMapper; private final StimmzettelumschlaegeValidator stimmzettelumschlaegeValidator; private final ExceptionFactory exceptionFactory; + private final Collection authenticationHandlers; @PreAuthorize("hasAuthority('Ergebnismeldung_BUSINESSACTION_GetStimmzettelumschlaege')") public Optional getStimmzettelumschlaege(final BezirkUndWahlID id) { @@ -43,6 +49,8 @@ public void setStimmzettelumschlaege(@P("param") final BezirkUndWahlID id, final stimmzettelumschlaegeValidator.validBezirkUndWahlIdOrThrow(id, exceptionFactory.createFachlicheWlsException(ExceptionConstants.POST_STIMMZETTELUMSCHLAEGE_PARAMETER_UNVOLLSTAENDIG)); stimmzettelumschlaegeValidator.validStimmzettelumschlaegeOrThrow(stimmzettelumschlaege); + val wahlbezirkArtOfRequest = getWahlbezirkArt(); + stimmzettelumschlaegeValidator.validHasBWBRequiredEroeffnungsUhrzeitOrThrow(wahlbezirkArtOfRequest, stimmzettelumschlaege.urneneroeffnungsUhrzeit()); try { stimmzettelumschlaegeRepository.save(stimmzettelumschlaegeModelMapper.toEntity(stimmzettelumschlaege)); @@ -51,4 +59,15 @@ public void setStimmzettelumschlaege(@P("param") final BezirkUndWahlID id, final throw exceptionFactory.createTechnischeWlsException(ExceptionConstants.STIMMZETTELUMSCHLAEGE_UNSAVEABLE); } } + + private WahlbezirkArtModel getWahlbezirkArt() { + val currentAuthentication = SecurityContextHolder.getContext().getAuthentication(); + val authenticationHandler = authenticationHandlers.stream().filter(handler -> handler.canHandle(currentAuthentication)).findFirst(); + try { + val wahlbezirkOfUser = authenticationHandler.get().getDetail(WAHLBEZIRK_ART_USER_DETAIL_KEY, currentAuthentication); + return wahlbezirkOfUser.map(WahlbezirkArtModel::valueOf).get(); + } catch (Exception e) { + throw exceptionFactory.createTechnischeWlsException(ExceptionConstants.WAHLBEZIRKART_NOT_LOADABLE); + } + } } diff --git a/wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeValidator.java b/wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeValidator.java index 31d1b0bd9..6f44931a1 100644 --- a/wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeValidator.java +++ b/wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeValidator.java @@ -4,6 +4,7 @@ import de.muenchen.oss.wahllokalsystem.wls.common.exception.FachlicheWlsException; import de.muenchen.oss.wahllokalsystem.wls.common.exception.util.ExceptionFactory; import de.muenchen.oss.wahllokalsystem.wls.common.security.domain.BezirkUndWahlID; +import java.time.LocalDateTime; import lombok.RequiredArgsConstructor; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; @@ -26,4 +27,10 @@ public void validStimmzettelumschlaegeOrThrow(final StimmzettelumschlaegeModel s throw exceptionFactory.createFachlicheWlsException(ExceptionConstants.POST_STIMMZETTELUMSCHLAEGE_PARAMETER_UNVOLLSTAENDIG); } } + + public void validHasBWBRequiredEroeffnungsUhrzeitOrThrow(final WahlbezirkArtModel wahlbezirkArt, LocalDateTime urnenEroeffnungsUhrzeit) { + if (wahlbezirkArt.equals(WahlbezirkArtModel.BWB) && urnenEroeffnungsUhrzeit == null) { + throw exceptionFactory.createFachlicheWlsException(ExceptionConstants.POST_STIMMZETTELUMSCHLAEGE_PARAMETER_UNVOLLSTAENDIG); + } + } } diff --git a/wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/WahlbezirkArtModel.java b/wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/WahlbezirkArtModel.java new file mode 100644 index 000000000..29b35d9dc --- /dev/null +++ b/wls-ergebnismeldung-service/src/main/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/WahlbezirkArtModel.java @@ -0,0 +1,5 @@ +package de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.service.stimmzettelumschlaege; + +public enum WahlbezirkArtModel { + UWB, BWB +} diff --git a/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeServiceSecurityTest.java b/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeServiceSecurityTest.java index 47de3119e..353248270 100644 --- a/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeServiceSecurityTest.java +++ b/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeServiceSecurityTest.java @@ -7,6 +7,7 @@ import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.TestConstants; import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.domain.stimmzettelumschlaege.StimmzettelumschlaegeRepository; import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.utils.Authorities; +import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.utils.WithMockUserAsJwt; import de.muenchen.oss.wahllokalsystem.wls.common.exception.TechnischeWlsException; import de.muenchen.oss.wahllokalsystem.wls.common.security.BezirkIDPermissionEvaluator; import de.muenchen.oss.wahllokalsystem.wls.common.security.domain.BezirkUndWahlID; @@ -15,7 +16,7 @@ import java.util.stream.Stream; import lombok.val; import org.assertj.core.api.Assertions; -import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -42,8 +43,8 @@ class StimmzettelumschlaegeServiceSecurityTest { @Autowired StimmzettelumschlaegeRepository stimmzettelumschlaegeRepository; - @BeforeEach - void setup() { + @AfterEach + void tearDown() { SecurityUtils.runWith(Authorities.REPOSITORY_DELETE_STIMMZETTELUMSCHLAEGE); stimmzettelumschlaegeRepository.deleteAll(); } @@ -80,9 +81,11 @@ private static Stream getMissingAuthoritiesVariations() { class SetStimmzettelumschlaege { @Test + @WithMockUserAsJwt( + authorities = { Authorities.SERVICE_SET_STIMMZETTELUMSCHLAEGE, Authorities.REPOSITORY_WRITE_STIMMZETTELUMSCHLAEGE }, + claimProperties = { "wahlbezirksArt=BWB" } + ) void should_getAccess_when_allRequiredAuthoritiesArePresent() { - SecurityUtils.runWith(Authorities.ALL_AUTHORITIES_SET_STIMMZETTELUMSCHLAEGE); - val wahlbezirkID = "wahlbezirkID"; val id = new BezirkUndWahlID("wahlID", wahlbezirkID); val stimmzettelumschlaege = createStimmzettelumschlaegeModel(id); diff --git a/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeServiceTest.java b/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeServiceTest.java index 970ae49ad..1cd066b21 100644 --- a/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeServiceTest.java +++ b/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/service/stimmzettelumschlaege/StimmzettelumschlaegeServiceTest.java @@ -1,7 +1,10 @@ package de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.service.stimmzettelumschlaege; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; +import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.common.security.AuthenticationHandler; +import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.common.security.JWTHandler; import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.domain.stimmzettelumschlaege.Stimmzettelumschlaege; import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.domain.stimmzettelumschlaege.StimmzettelumschlaegeRepository; import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.exception.ExceptionConstants; @@ -9,15 +12,18 @@ import de.muenchen.oss.wahllokalsystem.wls.common.exception.TechnischeWlsException; import de.muenchen.oss.wahllokalsystem.wls.common.exception.util.ExceptionFactory; import de.muenchen.oss.wahllokalsystem.wls.common.security.domain.BezirkUndWahlID; +import java.util.ArrayList; import java.util.Optional; import lombok.val; import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Mockito; +import org.mockito.Spy; import org.mockito.junit.jupiter.MockitoExtension; @ExtendWith(MockitoExtension.class) @@ -35,9 +41,21 @@ class StimmzettelumschlaegeServiceTest { @Mock ExceptionFactory exceptionFactory; + @Mock + JWTHandler jwtHandler; + + @Spy + ArrayList authenticationHandlers; + @InjectMocks StimmzettelumschlaegeService unitUnderTest; + @BeforeEach + void setup() { + authenticationHandlers.clear(); + authenticationHandlers.add(jwtHandler); + } + @Nested class GetStimmzettelumschlaege { @@ -84,18 +102,25 @@ void should_returnEmptyOptional_when_stimmzettelumschlaegeIsNotFoundFromRepo() { @Nested class SetStimmzettelumschlaege { + private static final String JWT_DETAIL_WAHLBEZIRKSART_KEY = "wahlbezirksArt"; + @Test - void should_submitFachlicheWlsExceptionForParameter_when_callingValidator() { + void should_callValidators_when_callingService() { val id = new BezirkUndWahlID(); val stimmzettelumschlaegeToSet = createStimmzettelumschlaegeModel(id); val mockedWlsException = FachlicheWlsException.withCode("").buildWithMessage("validation of parameters failed"); + Mockito.when(jwtHandler.canHandle(any())).thenReturn(true); + Mockito.when(jwtHandler.getDetail(eq(JWT_DETAIL_WAHLBEZIRKSART_KEY), any())).thenReturn(Optional.of("BWB")); Mockito.when(exceptionFactory.createFachlicheWlsException(ExceptionConstants.POST_STIMMZETTELUMSCHLAEGE_PARAMETER_UNVOLLSTAENDIG)) .thenReturn(mockedWlsException); unitUnderTest.setStimmzettelumschlaege(id, stimmzettelumschlaegeToSet); Mockito.verify(stimmzettelumschlaegeValidator).validBezirkUndWahlIdOrThrow(id, mockedWlsException); + Mockito.verify(stimmzettelumschlaegeValidator).validStimmzettelumschlaegeOrThrow(stimmzettelumschlaegeToSet); + Mockito.verify(stimmzettelumschlaegeValidator).validHasBWBRequiredEroeffnungsUhrzeitOrThrow(eq(WahlbezirkArtModel.BWB), + eq(stimmzettelumschlaegeToSet.urneneroeffnungsUhrzeit())); } @Test @@ -104,6 +129,8 @@ void should_validateStimmzettelumschlaegeModel_when_called() { val stimmzettelumschlaegeToSet = createStimmzettelumschlaegeModel(id); val mockedWlsException = FachlicheWlsException.withCode("").buildWithMessage("validation of parameters failed"); + Mockito.when(jwtHandler.canHandle(any())).thenReturn(true); + Mockito.when(jwtHandler.getDetail(eq(JWT_DETAIL_WAHLBEZIRKSART_KEY), any())).thenReturn(Optional.of("BWB")); Mockito.when(exceptionFactory.createFachlicheWlsException(ExceptionConstants.POST_STIMMZETTELUMSCHLAEGE_PARAMETER_UNVOLLSTAENDIG)) .thenReturn(mockedWlsException); @@ -119,6 +146,8 @@ void should_saveMappedStimmzettelumschlaegeModel_when_called() { val mockedModelAsEntity = Mockito.mock(Stimmzettelumschlaege.class); + Mockito.when(jwtHandler.canHandle(any())).thenReturn(true); + Mockito.when(jwtHandler.getDetail(eq(JWT_DETAIL_WAHLBEZIRKSART_KEY), any())).thenReturn(Optional.of("BWB")); Mockito.when(stimmzettelumschlaegeModelMapper.toEntity(stimmzettelumschlaegeToSet)).thenReturn(mockedModelAsEntity); unitUnderTest.setStimmzettelumschlaege(id, stimmzettelumschlaegeToSet); @@ -135,6 +164,8 @@ void should_throwTechnischeWlsException_when_savingFailed() { val mockedRepositorySaveException = new RuntimeException("saving failed"); val mockedExceptionFactoryWlsException = TechnischeWlsException.withCode("").buildWithMessage("save exception"); + Mockito.when(jwtHandler.canHandle(any())).thenReturn(true); + Mockito.when(jwtHandler.getDetail(eq(JWT_DETAIL_WAHLBEZIRKSART_KEY), any())).thenReturn(Optional.of("BWB")); Mockito.when(stimmzettelumschlaegeModelMapper.toEntity(stimmzettelumschlaegeToSet)).thenReturn(mockedModelAsEntity); Mockito.doThrow(mockedRepositorySaveException).when(stimmzettelumschlaegeRepository).save(mockedModelAsEntity); Mockito.when(exceptionFactory.createTechnischeWlsException(ExceptionConstants.STIMMZETTELUMSCHLAEGE_UNSAVEABLE)) diff --git a/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/utils/WithMockUserAsJwt.java b/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/utils/WithMockUserAsJwt.java new file mode 100644 index 000000000..737d6b75b --- /dev/null +++ b/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/utils/WithMockUserAsJwt.java @@ -0,0 +1,35 @@ +package de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.utils; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.springframework.core.annotation.AliasFor; +import org.springframework.security.test.context.support.TestExecutionEvent; +import org.springframework.security.test.context.support.WithSecurityContext; + +@Target({ ElementType.METHOD, ElementType.TYPE }) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@Documented +@WithSecurityContext(factory = WithMockUserAsJwtSecurityContextFactory.class) +public @interface WithMockUserAsJwt { + + String value() default "user"; + + String[] authorities() default {}; + + String[] claimProperties() default {}; + + String claimPropertiesSeparator() default "="; + + int issuedBeforeHours() default 1; + + int expiredInHours() default 1; + + @AliasFor(annotation = WithSecurityContext.class) + TestExecutionEvent setupBefore() default TestExecutionEvent.TEST_METHOD; + +} diff --git a/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/utils/WithMockUserAsJwtSecurityContextFactory.java b/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/utils/WithMockUserAsJwtSecurityContextFactory.java new file mode 100644 index 000000000..d96e6701a --- /dev/null +++ b/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/utils/WithMockUserAsJwtSecurityContextFactory.java @@ -0,0 +1,67 @@ +package de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.utils; + +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import lombok.val; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.context.SecurityContextHolderStrategy; +import org.springframework.security.oauth2.jwt.Jwt; +import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; +import org.springframework.security.test.context.support.WithSecurityContextFactory; +import org.springframework.util.Assert; + +/* + * inspired by org.springframework.security.test.context.support.WithMockUserSecurityContextFactory + */ +final public class WithMockUserAsJwtSecurityContextFactory implements WithSecurityContextFactory { + + private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder + .getContextHolderStrategy(); + + @Override + public SecurityContext createSecurityContext(WithMockUserAsJwt withUser) { + val username = withUser.value(); + Assert.notNull(username, () -> withUser + " cannot have null username on both username and value properties"); + + val issuedAt = Instant.now().minus(withUser.issuedBeforeHours(), ChronoUnit.HOURS); + val expiresAt = Instant.now().plus(withUser.expiredInHours(), ChronoUnit.HOURS); + val headers = Map.of("jwtDummyHeader", (Object) "jwtDummyValue"); + + val claims = new HashMap(); + claims.put("dummyClaim", "dummyClaimValue"); + claims.putAll(createClaimsMap(withUser.claimProperties(), withUser.claimPropertiesSeparator())); + + val jwt = new Jwt(username, issuedAt, expiresAt, headers, claims); + + List grantedAuthorities = new ArrayList<>(); + for (String authority : withUser.authorities()) { + grantedAuthorities.add(new SimpleGrantedAuthority(authority)); + } + + val authentication = new JwtAuthenticationToken(jwt, grantedAuthorities); + + val context = this.securityContextHolderStrategy.createEmptyContext(); + context.setAuthentication(authentication); + return context; + } + + @Autowired(required = false) + void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) { + this.securityContextHolderStrategy = securityContextHolderStrategy; + } + + private Map createClaimsMap(final String[] concatedClaimProperties, final String keyValueSeparator) { + return Arrays.stream(concatedClaimProperties).map(concatedClaimProperty -> concatedClaimProperty.split(keyValueSeparator)) + .collect(Collectors.toMap(propertyAsArray -> propertyAsArray[0], propertyAsArray -> propertyAsArray[1])); + } +} From 8b0b2cdd02f6c519dde1a9b5099a4a697b13f4d8 Mon Sep 17 00:00:00 2001 From: Robert Jasny Date: Fri, 20 Dec 2024 16:17:30 +0100 Subject: [PATCH 11/14] renamed after merging with dev --- ...lumschlaege.sql => V4_0__createTableStimmzettelumschlaege.sql} | 0 .../{V3_0__createBegruendung.sql => V4_0__createBegruendung.sql} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename wls-ergebnismeldung-service/src/main/resources/db/migrations/h2/{V3_0__createTableStimmzettelumschlaege.sql => V4_0__createTableStimmzettelumschlaege.sql} (100%) rename wls-ergebnismeldung-service/src/main/resources/db/migrations/oracle/{V3_0__createBegruendung.sql => V4_0__createBegruendung.sql} (100%) diff --git a/wls-ergebnismeldung-service/src/main/resources/db/migrations/h2/V3_0__createTableStimmzettelumschlaege.sql b/wls-ergebnismeldung-service/src/main/resources/db/migrations/h2/V4_0__createTableStimmzettelumschlaege.sql similarity index 100% rename from wls-ergebnismeldung-service/src/main/resources/db/migrations/h2/V3_0__createTableStimmzettelumschlaege.sql rename to wls-ergebnismeldung-service/src/main/resources/db/migrations/h2/V4_0__createTableStimmzettelumschlaege.sql diff --git a/wls-ergebnismeldung-service/src/main/resources/db/migrations/oracle/V3_0__createBegruendung.sql b/wls-ergebnismeldung-service/src/main/resources/db/migrations/oracle/V4_0__createBegruendung.sql similarity index 100% rename from wls-ergebnismeldung-service/src/main/resources/db/migrations/oracle/V3_0__createBegruendung.sql rename to wls-ergebnismeldung-service/src/main/resources/db/migrations/oracle/V4_0__createBegruendung.sql From 75b67a537e0087721554bdb9460be61625b86f31 Mon Sep 17 00:00:00 2001 From: Robert Jasny Date: Tue, 7 Jan 2025 18:01:41 +0100 Subject: [PATCH 12/14] =?UTF-8?q?tests=20mit=20security=20erforderlich=20w?= =?UTF-8?q?egen=20der=20Wahlbezirkart-Bestimmung=20=C3=BCber=20den=20Secur?= =?UTF-8?q?ity-Kontext=20im=20Service?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...elumschlaegeControllerIntegrationTest.java | 103 ++++++++++++------ 1 file changed, 72 insertions(+), 31 deletions(-) diff --git a/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/rest/stimmzettelumschlaege/StimmzettelumschlaegeControllerIntegrationTest.java b/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/rest/stimmzettelumschlaege/StimmzettelumschlaegeControllerIntegrationTest.java index fd8bbe043..60eb1fc78 100644 --- a/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/rest/stimmzettelumschlaege/StimmzettelumschlaegeControllerIntegrationTest.java +++ b/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/rest/stimmzettelumschlaege/StimmzettelumschlaegeControllerIntegrationTest.java @@ -1,6 +1,5 @@ package de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.rest.stimmzettelumschlaege; -import static de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.TestConstants.SPRING_NO_SECURITY_PROFILE; import static de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.TestConstants.SPRING_TEST_PROFILE; import static de.muenchen.oss.wahllokalsystem.wls.common.security.Profiles.NO_BEZIRKS_ID_CHECK; import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; @@ -12,20 +11,25 @@ import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.domain.stimmzettelumschlaege.StimmzettelumschlaegeRepository; import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.exception.ExceptionConstants; import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.service.stimmzettelumschlaege.StimmzettelumschlaegeModelMapper; +import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.utils.Authorities; import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.utils.LocalDateTimeComparators; +import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.utils.WithMockUserAsJwt; import de.muenchen.oss.wahllokalsystem.wls.common.exception.rest.model.WlsExceptionCategory; import de.muenchen.oss.wahllokalsystem.wls.common.exception.rest.model.WlsExceptionDTO; import de.muenchen.oss.wahllokalsystem.wls.common.security.domain.BezirkUndWahlID; +import de.muenchen.oss.wahllokalsystem.wls.common.testing.SecurityUtils; import java.time.LocalDateTime; import lombok.val; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.MediaType; +import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; @@ -33,7 +37,7 @@ @SpringBootTest(classes = MicroServiceApplication.class) @AutoConfigureMockMvc @ActiveProfiles( - profiles = { SPRING_TEST_PROFILE, SPRING_NO_SECURITY_PROFILE, NO_BEZIRKS_ID_CHECK } + profiles = { SPRING_TEST_PROFILE, NO_BEZIRKS_ID_CHECK } ) public class StimmzettelumschlaegeControllerIntegrationTest { @@ -54,6 +58,7 @@ public class StimmzettelumschlaegeControllerIntegrationTest { @AfterEach void tearDown() { + SecurityUtils.runWith(Authorities.REPOSITORY_DELETE_STIMMZETTELUMSCHLAEGE); stimmzettelumschlaegeRepository.deleteAll(); } @@ -61,6 +66,11 @@ void tearDown() { class GetStimmzettelumschlaege { @Test + @WithMockUser( + authorities = { Authorities.SERVICE_GET_STIMMZETTELUMSCHLAEGE, + Authorities.REPOSITORY_WRITE_STIMMZETTELUMSCHLAEGE, + Authorities.REPOSITORY_READ_STIMMZETTELUMSCHLAEGE } + ) void should_returnData_when_dataIsPresentInRepository() throws Exception { val wahlID = "wahlID"; val wahlbezirkID = "wahlbezirkID"; @@ -84,6 +94,10 @@ void should_returnData_when_dataIsPresentInRepository() throws Exception { } @Test + @WithMockUser( + authorities = { Authorities.SERVICE_GET_STIMMZETTELUMSCHLAEGE, + Authorities.REPOSITORY_READ_STIMMZETTELUMSCHLAEGE } + ) void should_returnNoContent_when_dataIsNotPresentInRepository() throws Exception { val wahlID = "wahlID"; val wahlbezirkID = "wahlbezirkID"; @@ -95,6 +109,9 @@ void should_returnNoContent_when_dataIsNotPresentInRepository() throws Exception } @Test + @WithMockUser( + authorities = { Authorities.SERVICE_GET_STIMMZETTELUMSCHLAEGE } + ) void should_returnBadRequestWlsException_when_validationFailed() throws Exception { val wahlID = " "; val wahlbezirkID = "wahlbezirkID"; @@ -114,6 +131,10 @@ void should_returnBadRequestWlsException_when_validationFailed() throws Exceptio class PostStimmzettelumschlaege { @Test + @WithMockUserAsJwt( + authorities = { Authorities.SERVICE_SET_STIMMZETTELUMSCHLAEGE, Authorities.REPOSITORY_WRITE_STIMMZETTELUMSCHLAEGE }, + claimProperties = { "wahlbezirksArt=BWB" } + ) void should_persistData_when_noDataIsPresentInRepository() throws Exception { val wahlID = "wahlID"; val wahlbezirkID = "wahlbezirkID"; @@ -128,37 +149,9 @@ void should_persistData_when_noDataIsPresentInRepository() throws Exception { mockMvc.perform(request).andExpect(status().isOk()).andReturn().getResponse(); + SecurityUtils.runWith(Authorities.REPOSITORY_READ_STIMMZETTELUMSCHLAEGE); val entityFromRepo = stimmzettelumschlaegeRepository.findById(requestBody.bezirkUndWahlID()).get(); - val expectedEntity = stimmzettelumschlaegeModelMapper.toEntity(stimmzettelumschlaegeDTOMapper.toModel(requestBody)); - Assertions.assertThat(entityFromRepo) - .usingRecursiveComparison() - .withComparatorForType(LocalDateTimeComparators.PRECISION_MILLISECONDS, LocalDateTime.class) - .isEqualTo(expectedEntity); - } - - @Test - void should_replaceOldData_when_dataIsPresentInRepository() throws Exception { - val wahlID = "wahlID"; - val wahlbezirkID = "wahlbezirkID"; - val urneneroeffnungsUhrzeit = LocalDateTime.now(); - val anzahlWaehler = 47; - val anzahlWaehler2 = 11L; - val requestBody = new StimmzettelumschlaegeDTO(new BezirkUndWahlID(wahlID, wahlbezirkID), urneneroeffnungsUhrzeit, anzahlWaehler, anzahlWaehler2); - - val request = MockMvcRequestBuilders.post(buildStimmzettelumschlaegeURI(wahlID, wahlbezirkID)).with(csrf()) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(requestBody)); - - val anzahlWaehlerToReplace = 8; - val anzahlWaehler2ToReplace = 15L; - val entityToReplace = new Stimmzettelumschlaege(requestBody.bezirkUndWahlID(), requestBody.urneneroeffnungsUhrzeit(), anzahlWaehlerToReplace, - anzahlWaehler2ToReplace); - Assertions.assertThat(entityToReplace).usingRecursiveComparison().isNotEqualTo(requestBody); - stimmzettelumschlaegeRepository.save(entityToReplace); - - mockMvc.perform(request).andExpect(status().isOk()).andReturn().getResponse(); - val entityFromRepo = stimmzettelumschlaegeRepository.findById(requestBody.bezirkUndWahlID()).get(); val expectedEntity = stimmzettelumschlaegeModelMapper.toEntity(stimmzettelumschlaegeDTOMapper.toModel(requestBody)); Assertions.assertThat(entityFromRepo) .usingRecursiveComparison() @@ -167,6 +160,9 @@ void should_replaceOldData_when_dataIsPresentInRepository() throws Exception { } @Test + @WithMockUserAsJwt( + authorities = { Authorities.SERVICE_SET_STIMMZETTELUMSCHLAEGE } + ) void should_returnBadRequestWlsException_when_validationFailed() throws Exception { val wahlID = " "; val wahlbezirkID = "wahlbezirkID"; @@ -189,6 +185,51 @@ void should_returnBadRequestWlsException_when_validationFailed() throws Exceptio } } + @Nested + class PostStimmzettelumschlaegeWithExistingSetupData { + + private Stimmzettelumschlaege entityFromRepo; + + @BeforeEach() + void setup() { + val entityToReplace = new Stimmzettelumschlaege( + new BezirkUndWahlID("wahlID", "wahlbezirkID"), + LocalDateTime.now(), 47, 11L); + entityFromRepo = stimmzettelumschlaegeRepository.save(entityToReplace); + } + + @Test + @WithMockUserAsJwt( + authorities = { Authorities.SERVICE_SET_STIMMZETTELUMSCHLAEGE, Authorities.REPOSITORY_WRITE_STIMMZETTELUMSCHLAEGE }, + claimProperties = { "wahlbezirksArt=BWB" } + ) + void should_replaceOldData_when_dataIsPresentInRepository() throws Exception { + val urneneroeffnungsUhrzeit = LocalDateTime.now(); + val anzahlWaehlerToReplace = 8; + val anzahlWaehler2ToReplace = 15L; + val requestBody = new StimmzettelumschlaegeDTO(new BezirkUndWahlID("wahlID", "wahlbezirkID"), + urneneroeffnungsUhrzeit, anzahlWaehlerToReplace, anzahlWaehler2ToReplace); + + val request = MockMvcRequestBuilders.post(buildStimmzettelumschlaegeURI("wahlID", "wahlbezirkID")).with(csrf()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(requestBody)); + + Assertions.assertThat(entityFromRepo).usingRecursiveComparison().isNotEqualTo(requestBody); + + mockMvc.perform(request).andExpect(status().isOk()).andReturn().getResponse(); + + SecurityUtils.runWith(Authorities.REPOSITORY_READ_STIMMZETTELUMSCHLAEGE); + val replacedEntityFromRepo = stimmzettelumschlaegeRepository.findById(requestBody.bezirkUndWahlID()).get(); + + val expectedEntity = stimmzettelumschlaegeModelMapper.toEntity(stimmzettelumschlaegeDTOMapper.toModel(requestBody)); + Assertions.assertThat(replacedEntityFromRepo) + .usingRecursiveComparison() + .withComparatorForType(LocalDateTimeComparators.PRECISION_MILLISECONDS, LocalDateTime.class) + .isEqualTo(expectedEntity); + } + + } + private String buildStimmzettelumschlaegeURI(final String wahlID, final String wahlbezirkID) { return "/businessActions/stimmzettelumschlaege/" + wahlID + "/" + wahlbezirkID; } From c4f2cf4b974222e69e24351d4645406e9a9e6b60 Mon Sep 17 00:00:00 2001 From: Robert Jasny Date: Tue, 7 Jan 2025 18:04:23 +0100 Subject: [PATCH 13/14] mvn spotless:apply --- ...elumschlaegeControllerIntegrationTest.java | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/rest/stimmzettelumschlaege/StimmzettelumschlaegeControllerIntegrationTest.java b/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/rest/stimmzettelumschlaege/StimmzettelumschlaegeControllerIntegrationTest.java index 60eb1fc78..e8ace7401 100644 --- a/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/rest/stimmzettelumschlaege/StimmzettelumschlaegeControllerIntegrationTest.java +++ b/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/rest/stimmzettelumschlaege/StimmzettelumschlaegeControllerIntegrationTest.java @@ -67,9 +67,9 @@ class GetStimmzettelumschlaege { @Test @WithMockUser( - authorities = { Authorities.SERVICE_GET_STIMMZETTELUMSCHLAEGE, - Authorities.REPOSITORY_WRITE_STIMMZETTELUMSCHLAEGE, - Authorities.REPOSITORY_READ_STIMMZETTELUMSCHLAEGE } + authorities = { Authorities.SERVICE_GET_STIMMZETTELUMSCHLAEGE, + Authorities.REPOSITORY_WRITE_STIMMZETTELUMSCHLAEGE, + Authorities.REPOSITORY_READ_STIMMZETTELUMSCHLAEGE } ) void should_returnData_when_dataIsPresentInRepository() throws Exception { val wahlID = "wahlID"; @@ -95,8 +95,8 @@ void should_returnData_when_dataIsPresentInRepository() throws Exception { @Test @WithMockUser( - authorities = { Authorities.SERVICE_GET_STIMMZETTELUMSCHLAEGE, - Authorities.REPOSITORY_READ_STIMMZETTELUMSCHLAEGE } + authorities = { Authorities.SERVICE_GET_STIMMZETTELUMSCHLAEGE, + Authorities.REPOSITORY_READ_STIMMZETTELUMSCHLAEGE } ) void should_returnNoContent_when_dataIsNotPresentInRepository() throws Exception { val wahlID = "wahlID"; @@ -110,7 +110,7 @@ void should_returnNoContent_when_dataIsNotPresentInRepository() throws Exception @Test @WithMockUser( - authorities = { Authorities.SERVICE_GET_STIMMZETTELUMSCHLAEGE } + authorities = { Authorities.SERVICE_GET_STIMMZETTELUMSCHLAEGE } ) void should_returnBadRequestWlsException_when_validationFailed() throws Exception { val wahlID = " "; @@ -132,8 +132,8 @@ class PostStimmzettelumschlaege { @Test @WithMockUserAsJwt( - authorities = { Authorities.SERVICE_SET_STIMMZETTELUMSCHLAEGE, Authorities.REPOSITORY_WRITE_STIMMZETTELUMSCHLAEGE }, - claimProperties = { "wahlbezirksArt=BWB" } + authorities = { Authorities.SERVICE_SET_STIMMZETTELUMSCHLAEGE, Authorities.REPOSITORY_WRITE_STIMMZETTELUMSCHLAEGE }, + claimProperties = { "wahlbezirksArt=BWB" } ) void should_persistData_when_noDataIsPresentInRepository() throws Exception { val wahlID = "wahlID"; @@ -161,7 +161,7 @@ void should_persistData_when_noDataIsPresentInRepository() throws Exception { @Test @WithMockUserAsJwt( - authorities = { Authorities.SERVICE_SET_STIMMZETTELUMSCHLAEGE } + authorities = { Authorities.SERVICE_SET_STIMMZETTELUMSCHLAEGE } ) void should_returnBadRequestWlsException_when_validationFailed() throws Exception { val wahlID = " "; @@ -193,26 +193,26 @@ class PostStimmzettelumschlaegeWithExistingSetupData { @BeforeEach() void setup() { val entityToReplace = new Stimmzettelumschlaege( - new BezirkUndWahlID("wahlID", "wahlbezirkID"), - LocalDateTime.now(), 47, 11L); + new BezirkUndWahlID("wahlID", "wahlbezirkID"), + LocalDateTime.now(), 47, 11L); entityFromRepo = stimmzettelumschlaegeRepository.save(entityToReplace); } @Test @WithMockUserAsJwt( - authorities = { Authorities.SERVICE_SET_STIMMZETTELUMSCHLAEGE, Authorities.REPOSITORY_WRITE_STIMMZETTELUMSCHLAEGE }, - claimProperties = { "wahlbezirksArt=BWB" } + authorities = { Authorities.SERVICE_SET_STIMMZETTELUMSCHLAEGE, Authorities.REPOSITORY_WRITE_STIMMZETTELUMSCHLAEGE }, + claimProperties = { "wahlbezirksArt=BWB" } ) void should_replaceOldData_when_dataIsPresentInRepository() throws Exception { val urneneroeffnungsUhrzeit = LocalDateTime.now(); val anzahlWaehlerToReplace = 8; val anzahlWaehler2ToReplace = 15L; val requestBody = new StimmzettelumschlaegeDTO(new BezirkUndWahlID("wahlID", "wahlbezirkID"), - urneneroeffnungsUhrzeit, anzahlWaehlerToReplace, anzahlWaehler2ToReplace); + urneneroeffnungsUhrzeit, anzahlWaehlerToReplace, anzahlWaehler2ToReplace); val request = MockMvcRequestBuilders.post(buildStimmzettelumschlaegeURI("wahlID", "wahlbezirkID")).with(csrf()) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(requestBody)); + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(requestBody)); Assertions.assertThat(entityFromRepo).usingRecursiveComparison().isNotEqualTo(requestBody); @@ -223,9 +223,9 @@ void should_replaceOldData_when_dataIsPresentInRepository() throws Exception { val expectedEntity = stimmzettelumschlaegeModelMapper.toEntity(stimmzettelumschlaegeDTOMapper.toModel(requestBody)); Assertions.assertThat(replacedEntityFromRepo) - .usingRecursiveComparison() - .withComparatorForType(LocalDateTimeComparators.PRECISION_MILLISECONDS, LocalDateTime.class) - .isEqualTo(expectedEntity); + .usingRecursiveComparison() + .withComparatorForType(LocalDateTimeComparators.PRECISION_MILLISECONDS, LocalDateTime.class) + .isEqualTo(expectedEntity); } } From 218fea56f67161f8544958879ada054f04c40bf8 Mon Sep 17 00:00:00 2001 From: Robert Jasny Date: Tue, 7 Jan 2025 18:08:02 +0100 Subject: [PATCH 14/14] mvn spotless:apply --- .../ergebnismeldungservice/utils/Authorities.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/utils/Authorities.java b/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/utils/Authorities.java index be3a87ffd..bce656131 100644 --- a/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/utils/Authorities.java +++ b/wls-ergebnismeldung-service/src/test/java/de/muenchen/oss/wahllokalsystem/ergebnismeldungservice/utils/Authorities.java @@ -33,7 +33,7 @@ public class Authorities { public static final String REPOSITORY_READ_BEGRUENDUNG = "Ergebnismeldung_READ_Begruendung"; public static final String REPOSITORY_DELETE_BEGRUENDUNG = "Ergebnismeldung_DELETE_Begruendung"; public static final String REPOSITORY_WRITE_BEGRUENDUNG = "Ergebnismeldung_WRITE_Begruendung"; - + public static final String REPOSITORY_READ_STIMMZETTELUMSCHLAEGE = "Ergebnismeldung_READ_Stimmzettelumschlaege"; public static final String REPOSITORY_DELETE_STIMMZETTELUMSCHLAEGE = "Ergebnismeldung_DELETE_Stimmzettelumschlaege"; public static final String REPOSITORY_WRITE_STIMMZETTELUMSCHLAEGE = "Ergebnismeldung_WRITE_Stimmzettelumschlaege"; @@ -93,7 +93,7 @@ public class Authorities { public static final String[] ALL_AUTHORITIES_SET_BEGRUENDUNG = ArrayUtils.addAll(ALL_AUTHORITIES_SET_BEGRUENDUNG_MISSING_WILL_RESULT_IN_ACCESS_DENIED, ALL_AUTHORITIES_SET_BEGRUENDUNG_MISSING_WILL_RESULT_IN_WLS_EXCEPTION); - + public static final String[] ALL_AUTHORITIES_GET_STIMMZETTELUMSCHLAEGE = new String[] { SERVICE_GET_STIMMZETTELUMSCHLAEGE, REPOSITORY_READ_STIMMZETTELUMSCHLAEGE