diff --git a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/vorgang/Lock.java b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/vorgang/Lock.java index a888e0ea88582596dc39af16e2007bc66a507c3e..336a0335d93e27f781b0e1e119f9adbd383cdbe6 100644 --- a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/vorgang/Lock.java +++ b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/vorgang/Lock.java @@ -25,19 +25,23 @@ package de.ozgcloud.vorgang.vorgang; import java.time.ZonedDateTime; +import jakarta.validation.constraints.NotNull; import lombok.Builder; import lombok.Getter; @Builder @Getter -class Lock { +public class Lock { static final String FIELD_CLIENT_NAME = "clientName"; static final String FIELD_LOCKED_SINCE = "lockedSince"; static final String FIELD_REASON = "reason"; + @NotNull private final String clientName; + @NotNull private final ZonedDateTime lockedSince; + @NotNull private final String reason; } diff --git a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/vorgang/LockMapper.java b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/vorgang/LockMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..c94e7b76508223828c076c1873e39386a1972a99 --- /dev/null +++ b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/vorgang/LockMapper.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2025 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.vorgang.vorgang; + +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.Map; + +import org.apache.commons.collections.MapUtils; +import org.apache.commons.lang3.StringUtils; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +import de.ozgcloud.command.Command; + +@Mapper +interface LockMapper { + + @Mapping(target = "clientName", source = "createdByClientName") + @Mapping(target = "lockedSince", expression = "java(getLockedSince())") + @Mapping(target = "reason", source = "bodyObject") + Lock fromCommand(Command command); + + default String getReason(Map<String, Object> bodyObject) { + return StringUtils.trimToNull(MapUtils.getString(bodyObject, Lock.FIELD_REASON)); + } + + default ZonedDateTime getLockedSince() { + return ZonedDateTime.now(ZoneId.of("UTC")); + } +} diff --git a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/vorgang/VorgangEventListener.java b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/vorgang/VorgangEventListener.java index be036bb8112e61cf4047e4591806ea13caf39282..d44544422fb18c593bf381db34cd515b612f116d 100644 --- a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/vorgang/VorgangEventListener.java +++ b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/vorgang/VorgangEventListener.java @@ -37,6 +37,7 @@ import de.ozgcloud.command.CommandFailedEvent; import de.ozgcloud.command.CommandRevokeFailedEvent; import de.ozgcloud.command.CommandRevokedEvent; import de.ozgcloud.command.RevokeCommandEvent; +import de.ozgcloud.command.VorgangLockedEvent; import de.ozgcloud.command.VorgangUnlockedEvent; import de.ozgcloud.vorgang.command.CommandService; import de.ozgcloud.vorgang.command.Order; @@ -72,6 +73,7 @@ public class VorgangEventListener { private final VorgangHeaderService vorgangHeaderService; private final FileService fileService; private final CommandService commandService; + private final LockMapper lockMapper; private final ApplicationEventPublisher publisher; @@ -122,7 +124,8 @@ public class VorgangEventListener { public void onLockVorgang(CommandCreatedEvent event) { var command = event.getSource(); try { - vorgangService.lockVorgang(command); + vorgangService.lockVorgang(lockMapper.fromCommand(command), command.getVorgangId(), command.getRelationVersion()); + publisher.publishEvent(new VorgangLockedEvent(command)); } catch (RuntimeException e) { LOG.error("Error locking vorgang.", e); publisher.publishEvent(new CommandFailedEvent(command.getId(), e.getMessage())); diff --git a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/vorgang/VorgangService.java b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/vorgang/VorgangService.java index 3cbc342a0f46aad3c4131a1d2033996f0d372eb2..a4690f4c7a4ebc99ea5aaa2a25ce0b4d8f7b28a0 100644 --- a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/vorgang/VorgangService.java +++ b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/vorgang/VorgangService.java @@ -23,13 +23,10 @@ */ package de.ozgcloud.vorgang.vorgang; -import java.time.ZoneId; -import java.time.ZonedDateTime; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Map.Entry; -import java.util.Objects; import java.util.Optional; import java.util.stream.Stream; @@ -44,8 +41,6 @@ import org.springframework.validation.annotation.Validated; import de.ozgcloud.command.Command; import de.ozgcloud.command.VorgangAssignedEvent; import de.ozgcloud.command.VorgangCreatedEvent; -import de.ozgcloud.command.VorgangLockedEvent; -import de.ozgcloud.common.errorhandling.TechnicalException; import de.ozgcloud.vorgang.clientattribute.ClientAttributeMap; import de.ozgcloud.vorgang.clientattribute.ClientAttributeReadPermitted; import de.ozgcloud.vorgang.clientattribute.ClientAttributesMap; @@ -223,30 +218,15 @@ public class VorgangService { return repository.findById(vorgangId).orElseThrow(() -> new NotFoundException(Vorgang.class, vorgangId)); } - public void lockVorgang(Command command) { - validateLockCommand(command); - repository.patch(command.getVorgangId(), command.getRelationVersion(), buildLockPatch(command)); - publisher.publishEvent(new VorgangLockedEvent(command)); + public void lockVorgang(@Valid Lock lock, String vorgangId, Long version) { + repository.patch(vorgangId, version, buildLockPatch(lock)); } - void validateLockCommand(Command command) { - if (Objects.isNull(command.getCreatedByClientName())) { - throw new TechnicalException("Missing client name in lock command"); - } - if (Objects.isNull(getReason(command.getBodyObject()))) { - throw new TechnicalException("Missing reason in lock command"); - } - } - - Map<String, Object> buildLockPatch(Command command) { + Map<String, Object> buildLockPatch(Lock lock) { return Map.of(KEY_HEADER_LOCK, Map.of( - Lock.FIELD_CLIENT_NAME, command.getCreatedByClientName(), - Lock.FIELD_LOCKED_SINCE, ZonedDateTime.now(ZoneId.of("UTC")), - Lock.FIELD_REASON, getReason(command.getBodyObject()))); - } - - String getReason(Map<String, Object> commandBody) { - return StringUtils.trimToNull(MapUtils.getString(commandBody, Lock.FIELD_REASON)); + Lock.FIELD_CLIENT_NAME, lock.getClientName(), + Lock.FIELD_LOCKED_SINCE, lock.getLockedSince(), + Lock.FIELD_REASON, lock.getReason())); } public boolean isVorgangLocked(String vorgangId) { diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/vorgang/LockMapperTest.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/vorgang/LockMapperTest.java new file mode 100644 index 0000000000000000000000000000000000000000..990b9432410674f86f32c3e2e2558fa0f31d4d43 --- /dev/null +++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/vorgang/LockMapperTest.java @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2025 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.vorgang.vorgang; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.temporal.ChronoUnit; +import java.util.HashMap; +import java.util.Map; + +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.provider.NullAndEmptySource; +import org.junit.jupiter.params.provider.ValueSource; +import org.mapstruct.factory.Mappers; +import org.mockito.Spy; + +import com.thedeanda.lorem.LoremIpsum; + +import de.ozgcloud.command.Command; +import de.ozgcloud.vorgang.command.CommandTestFactory; + +class LockMapperTest { + + @Spy + private LockMapper mapper = Mappers.getMapper(LockMapper.class); + + @Nested + class TestFromCommand { + + private final Command command = CommandTestFactory.createBuilder().bodyObject(Map.of(Lock.FIELD_REASON, LockTestFactory.REASON)).build(); + + @BeforeEach + void init() { + doReturn(LockTestFactory.REASON).when(mapper).getReason(any()); + doReturn(LockTestFactory.LOCKED_SINCE).when(mapper).getLockedSince(); + } + + @Test + void shouldGetLockedSince() { + map(); + + verify(mapper).getLockedSince(); + } + + @Test + void shouldGetReason() { + map(); + + verify(mapper).getReason(command.getBodyObject()); + } + + @Test + void shouldMap() { + var mapped = map(); + + assertThat(mapped).usingRecursiveComparison().isEqualTo(LockTestFactory.create()); + } + + private Lock map() { + return mapper.fromCommand(command); + } + } + + @Nested + class TestGetReason { + + @Test + void shouldReturnNullIfKeyNotFound() { + var mapped = mapper.getReason(Map.of("dummyKey", "dummyValue")); + + assertThat(mapped).isNull(); + } + + @Test + void shouldReturnNullIfValueIsNull() { + Map<String, Object> bodyObject = new HashMap<>(); + bodyObject.put(Lock.FIELD_REASON, null); // need to put, because Map.of() does not accept null values + + var mapped = mapper.getReason(bodyObject); + + assertThat(mapped).isNull(); + } + + @Test + void shouldReturnReason() { + var reason = LoremIpsum.getInstance().getWords(2); + Map<String, Object> bodyObject = Map.of(Lock.FIELD_REASON, reason); + + var mapped = mapper.getReason(bodyObject); + + assertThat(mapped).isEqualTo(reason); + } + + @ParameterizedTest + @ValueSource(strings = { " " }) + @NullAndEmptySource + void shouldTrimToNull(String reason) { + Map<String, Object> bodyObject = new HashMap<>(); + bodyObject.put(Lock.FIELD_REASON, reason); // need to put, because Map.of() does not accept null values + + var mapped = mapper.getReason(bodyObject); + + assertThat(mapped).isNull(); + } + } + + @Nested + class TestLockedSince { + + @Test + void shouldReturnCurrentTime() { + var lockedSince = mapper.getLockedSince(); + + assertThat(lockedSince).isCloseTo(ZonedDateTime.now(ZoneId.of("UTC")), within(2, ChronoUnit.SECONDS)); + } + } +} diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/vorgang/VorgangEventListenerTest.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/vorgang/VorgangEventListenerTest.java index d8bb400c4d00f2573bbc4a922e3991d1155ada24..dc3152d7860268d0c1554f4bdfb1127363718722 100644 --- a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/vorgang/VorgangEventListenerTest.java +++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/vorgang/VorgangEventListenerTest.java @@ -50,6 +50,7 @@ import de.ozgcloud.command.CommandFailedEvent; import de.ozgcloud.command.CommandRevokeFailedEvent; import de.ozgcloud.command.CommandRevokedEvent; import de.ozgcloud.command.RevokeCommandEvent; +import de.ozgcloud.command.VorgangLockedEvent; import de.ozgcloud.command.VorgangUnlockedEvent; import de.ozgcloud.common.errorhandling.TechnicalException; import de.ozgcloud.vorgang.command.CommandCreatedEventTestFactory; @@ -77,6 +78,8 @@ class VorgangEventListenerTest { private FileService fileService; @Mock private CommandService commandService; + @Mock + private LockMapper lockMapper; @Mock private ApplicationEventPublisher publisher; @@ -239,32 +242,66 @@ class VorgangEventListenerTest { @Nested class TestOnLockVorgang { + private final Command command = CommandTestFactory.create(); + private final Lock lock = LockTestFactory.create(); @Captor - private ArgumentCaptor<CommandFailedEvent> eventCaptor; + private ArgumentCaptor<VorgangLockedEvent> lockedEventCaptor; + @Captor + private ArgumentCaptor<CommandFailedEvent> failedEventCaptor; + + @BeforeEach + void init() { + when(lockMapper.fromCommand(any())).thenReturn(lock); + } + + @Test + void shouldMapToLock() { + onLockVorgang(); + + verify(lockMapper).fromCommand(command); + } @Test void shouldCallLockVorgang() { - var command = CommandTestFactory.create(); + onLockVorgang(); - listener.onLockVorgang(CommandCreatedEventTestFactory.create(command)); + verify(service).lockVorgang(lock, VorgangTestFactory.ID, CommandTestFactory.RELATION_VERSION); + } + + @Test + void shouldCallPublishEvent() { + onLockVorgang(); - verify(service).lockVorgang(command); + verify(publisher).publishEvent(lockedEventCaptor.capture()); + assertThat(lockedEventCaptor.getValue().getCommand()).isSameAs(command); } @Test void shouldPublishCommandFailedEvent() { - var command = CommandTestFactory.create(); var errorMessage = "error message"; - doThrow(new RuntimeException(errorMessage)).when(service).lockVorgang(command); + doThrow(new RuntimeException(errorMessage)).when(service).lockVorgang(any(), any(), anyLong()); - listener.onLockVorgang(CommandCreatedEventTestFactory.create(command)); + onLockVorgang(); - verify(publisher).publishEvent(eventCaptor.capture()); - assertThat(eventCaptor.getValue()).satisfies(event -> { + verify(publisher).publishEvent(failedEventCaptor.capture()); + assertThat(failedEventCaptor.getValue()).satisfies(event -> { assertThat(event.getSource()).isEqualTo(CommandTestFactory.ID); assertThat(event.getErrorMessage()).isEqualTo(errorMessage); }); } + + @Test + void shouldNotPublishVorgangLockedEventOnFailure() { + doThrow(new RuntimeException("error message")).when(service).lockVorgang(any(), any(), anyLong()); + + onLockVorgang(); + + verify(publisher, never()).publishEvent(any(VorgangLockedEvent.class)); + } + + private void onLockVorgang() { + listener.onLockVorgang(CommandCreatedEventTestFactory.create(command)); + } } @Nested diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/vorgang/VorgangServiceITCase.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/vorgang/VorgangServiceITCase.java index 1ddb4dee4a51eee8accad0ed3cf06c913228bddf..f4a60955119cc46ea93b5add821b0d91533c3c1b 100644 --- a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/vorgang/VorgangServiceITCase.java +++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/vorgang/VorgangServiceITCase.java @@ -29,20 +29,19 @@ import static org.mockito.Mockito.*; import java.util.List; -import jakarta.validation.ConstraintViolationException; - import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import de.ozgcloud.common.test.DataITCase; import de.ozgcloud.notification.antragsteller.AntragstellerNotificationEventListener; import de.ozgcloud.notification.user.UserNotificationEventListener; import de.ozgcloud.vorgang.command.CommandService; import de.ozgcloud.vorgang.servicekonto.ServiceKontoTestFactory; +import jakarta.validation.ConstraintViolationException; @DataITCase @WithMockUser @@ -51,14 +50,14 @@ class VorgangServiceITCase { @Autowired private VorgangService vorgangService; - @MockBean + @MockitoBean private CommandService commandService; - - @MockBean + @MockitoBean private AntragstellerNotificationEventListener antragstellerNotificationEventListener; - - @MockBean + @MockitoBean private UserNotificationEventListener userNotificationEventListener; + @MockitoBean + private VorgangRepository vorgangRepository; @DisplayName("Test creating a new Vorgang") @Nested @@ -92,11 +91,49 @@ class VorgangServiceITCase { @Test void shouldBeInvalidOnMissingPostfachAddress() { var eingang = EingangTestFactory.createBuilder().header(EingangHeaderTestFactory.createBuilder() - .serviceKonto(ServiceKontoTestFactory.createBuilder().clearPostfachAddresses().build()).build()) + .serviceKonto(ServiceKontoTestFactory.createBuilder().clearPostfachAddresses().build()).build()) .build(); assertThatThrownBy(() -> vorgangService.startCreation(eingang)).isInstanceOf(ConstraintViolationException.class); } } } + + @Nested + class TestLockVorgang { + + @Nested + class TestValidation { + + @Test + void shouldBeInvalidOnMissingClientName() { + var lock = LockTestFactory.createBuilder().clientName(null).build(); + + assertThatThrownBy(() -> vorgangService.lockVorgang(lock, VorgangTestFactory.ID, VorgangTestFactory.VERSION)).isInstanceOf( + ConstraintViolationException.class); + } + + @Test + void shouldBeInvalidOnMissingLockedSince() { + var lock = LockTestFactory.createBuilder().lockedSince(null).build(); + + assertThatThrownBy(() -> vorgangService.lockVorgang(lock, VorgangTestFactory.ID, VorgangTestFactory.VERSION)).isInstanceOf( + ConstraintViolationException.class); + } + + @Test + void shouldBeInvalidOnMissingReason() { + var lock = LockTestFactory.createBuilder().reason(null).build(); + + assertThatThrownBy(() -> vorgangService.lockVorgang(lock, VorgangTestFactory.ID, VorgangTestFactory.VERSION)).isInstanceOf( + ConstraintViolationException.class); + } + + @Test + void shouldBeValidWhenRequiredFieldsAreNotNull() { + assertThatNoException().isThrownBy( + () -> vorgangService.lockVorgang(LockTestFactory.create(), VorgangTestFactory.ID, VorgangTestFactory.VERSION)); + } + } + } } \ No newline at end of file diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/vorgang/VorgangServiceTest.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/vorgang/VorgangServiceTest.java index 3449659b130bca698edbf9b3b88daf73fe867efc..dd60f6033b802cfcf4909ad46d8420ee73119dad 100644 --- a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/vorgang/VorgangServiceTest.java +++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/vorgang/VorgangServiceTest.java @@ -28,13 +28,10 @@ import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; -import java.time.ZonedDateTime; -import java.time.temporal.ChronoUnit; import java.util.List; import java.util.Map; import java.util.Optional; -import org.assertj.core.api.*; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; @@ -53,7 +50,6 @@ import de.ozgcloud.command.Command; import de.ozgcloud.command.VorgangAssignedEvent; import de.ozgcloud.command.VorgangCreatedEvent; import de.ozgcloud.command.VorgangLockedEvent; -import de.ozgcloud.common.errorhandling.TechnicalException; import de.ozgcloud.vorgang.clientattribute.ClientAttributeReadPermitted; import de.ozgcloud.vorgang.command.CommandTestFactory; import de.ozgcloud.vorgang.common.errorhandling.NotFoundException; @@ -352,7 +348,8 @@ class VorgangServiceTest { void shouldThrowAccessDeniedException() { when(vorgangAuthorizationService.authorizeByOrganisationseinheitenId(any(), any())).thenReturn(false); - assertThrows(AccessDeniedException.class, () -> service.getById(VorgangTestFactory.ID, FilterCriteriaTestFactory.create())); // NOSONAR + assertThrows(AccessDeniedException.class, + () -> service.getById(VorgangTestFactory.ID, FilterCriteriaTestFactory.create())); // NOSONAR } } @@ -516,7 +513,7 @@ class VorgangServiceTest { @Nested class TestLockVorgang { - private static final Command LOCK_COMMAND = CommandTestFactory.create(); + private final Lock lock = LockTestFactory.create(); private static final Map<String, Object> LOCK_PATCH = Map.of("key", "value"); @Captor @@ -524,92 +521,53 @@ class VorgangServiceTest { @BeforeEach void init() { - doNothing().when(service).validateLockCommand(any()); doReturn(LOCK_PATCH).when(service).buildLockPatch(any()); } - @Test - void shouldCallValidateLockCommand() { - service.lockVorgang(LOCK_COMMAND); - - verify(service).validateLockCommand(LOCK_COMMAND); - } - @Test void shouldCallBuildLockPatch() { - service.lockVorgang(LOCK_COMMAND); + lockVorgang(); - verify(service).buildLockPatch(LOCK_COMMAND); + verify(service).buildLockPatch(lock); } @Test void shouldCallRepository() { - service.lockVorgang(LOCK_COMMAND); + lockVorgang(); verify(repository).patch(VorgangTestFactory.ID, CommandTestFactory.RELATION_VERSION, LOCK_PATCH); } - @Test - void shouldCallPublishEvent() { - service.lockVorgang(LOCK_COMMAND); - - verify(publisher).publishEvent(eventCaptor.capture()); - assertThat(eventCaptor.getValue().getCommand()).isSameAs(LOCK_COMMAND); - } - } - - @Nested - class TestValidateLockCommand { - - @Test - void shouldThrowExceptionOnMissingClient() { - var command = CommandTestFactory.createBuilder().createdByClientName(null).build(); - - assertThrows(TechnicalException.class, () -> service.validateLockCommand(command)); - } - - @Test - void shouldThrowExceptionOnMissingReason() { - var command = CommandTestFactory.createBuilder().bodyObject(Map.of()).build(); - - assertThrows(TechnicalException.class, () -> service.validateLockCommand(command)); - } - - @Test - void shouldAcceptValidCommand() { - var command = CommandTestFactory.createBuilder().bodyObject(Map.of(Lock.FIELD_REASON, LockTestFactory.REASON)).build(); - - assertDoesNotThrow(() -> service.validateLockCommand(command)); + private void lockVorgang() { + service.lockVorgang(lock, VorgangTestFactory.ID, VorgangTestFactory.VERSION); } } @Nested class TestBuildLockPatch { - private final Command command = CommandTestFactory.createBuilder().bodyObject(Map.of(Lock.FIELD_REASON, LockTestFactory.REASON)).build(); - @Test void shouldSetClientName() { - var result = service.buildLockPatch(command); + var result = service.buildLockPatch(LockTestFactory.create()); assertThat(result).extractingByKey(VorgangService.KEY_HEADER_LOCK, MAP) - .containsEntry(Lock.FIELD_CLIENT_NAME, CommandTestFactory.CREATED_BY_CLIENT); + .containsEntry(Lock.FIELD_CLIENT_NAME, LockTestFactory.CLIENT_NAME); } @Test void shouldSetLockedSince() { - var result = service.buildLockPatch(command); + var result = service.buildLockPatch(LockTestFactory.create()); assertThat(result).extractingByKey(VorgangService.KEY_HEADER_LOCK, MAP) - .extractingByKey(Lock.FIELD_LOCKED_SINCE, InstanceOfAssertFactories.ZONED_DATE_TIME) - .isCloseTo(ZonedDateTime.now(), within(1, ChronoUnit.SECONDS)); + .containsEntry(Lock.FIELD_LOCKED_SINCE, LockTestFactory.LOCKED_SINCE); } @Test void shouldSetReason() { - var result = service.buildLockPatch(command); + var result = service.buildLockPatch(LockTestFactory.create()); - assertThat(result).extractingByKey(VorgangService.KEY_HEADER_LOCK, MAP).containsEntry(Lock.FIELD_REASON, LockTestFactory.REASON); + assertThat(result).extractingByKey(VorgangService.KEY_HEADER_LOCK, MAP) + .containsEntry(Lock.FIELD_REASON, LockTestFactory.REASON); } } @@ -760,7 +718,6 @@ class VorgangServiceTest { } } - @Nested class TestFindDeleted {