diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/historie/AktenzeichenChangeHistoryBuilder.java b/alfa-service/src/main/java/de/ozgcloud/alfa/historie/AktenzeichenChangeHistoryBuilder.java new file mode 100644 index 0000000000000000000000000000000000000000..9af4e5c41d71d45c297dc5bec47ccfcb40896401 --- /dev/null +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/historie/AktenzeichenChangeHistoryBuilder.java @@ -0,0 +1,48 @@ +package de.ozgcloud.alfa.historie; + +import java.util.Optional; + +import org.apache.commons.lang3.StringUtils; + +import de.ozgcloud.alfa.common.command.Command; +import de.ozgcloud.alfa.common.command.CommandOrder; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +class AktenzeichenChangeHistoryBuilder extends ChangeHistoryBuilder<AktenzeichenChangeHistoryBuilder> { + + static final String BODY_PROPERTY_AKTENZEICHEN = "aktenzeichen"; + + public static AktenzeichenChangeHistoryBuilder builder() { + return new AktenzeichenChangeHistoryBuilder(); + } + + @Override + boolean isRelevant(Command command) { + return command.getOrder().equals(CommandOrder.SET_AKTENZEICHEN); + } + + @Override + CommandWithChangeValues toCommandWithChangeValues(CommandWithPrevious commandWithPrevious) { + return new CommandWithChangeValues( + commandWithPrevious.getCommand(), + getAktenzeichenBeforeChange(commandWithPrevious), + getAktenzeichenAfterChange(commandWithPrevious) + ); + } + + String getAktenzeichenBeforeChange(CommandWithPrevious commandWithPrevious) { + return Optional.ofNullable(commandWithPrevious.getPrevious()) + .map(this::getAktenzeichen) + .orElse(StringUtils.EMPTY); + } + + String getAktenzeichenAfterChange(CommandWithPrevious commandWithPrevious) { + return getAktenzeichen(commandWithPrevious.getCommand()); + } + + String getAktenzeichen(Command command) { + return getValueFromCommandBody(BODY_PROPERTY_AKTENZEICHEN, command); + } +} diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/historie/AssignedUserChangeHistoryBuilder.java b/alfa-service/src/main/java/de/ozgcloud/alfa/historie/AssignedUserChangeHistoryBuilder.java new file mode 100644 index 0000000000000000000000000000000000000000..b48b74cc1c04e70cf1d5a3e0cbd117bcb114c3d0 --- /dev/null +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/historie/AssignedUserChangeHistoryBuilder.java @@ -0,0 +1,56 @@ +package de.ozgcloud.alfa.historie; + +import java.util.Optional; + +import org.apache.commons.lang3.StringUtils; + +import de.ozgcloud.alfa.common.command.Command; +import de.ozgcloud.alfa.common.command.CommandOrder; +import de.ozgcloud.alfa.common.user.UserId; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +class AssignedUserChangeHistoryBuilder extends ChangeHistoryBuilder<AssignedUserChangeHistoryBuilder> { + + static final String BODY_PROPERTY_ASSIGNED_USER = "assignedTo"; + + private UserProfileCache userProfileCache; + + public static AssignedUserChangeHistoryBuilder builder() { + return new AssignedUserChangeHistoryBuilder(); + } + + public AssignedUserChangeHistoryBuilder withUserProfileCache(UserProfileCache userProfileCache) { + this.userProfileCache = userProfileCache; + return this; + } + + @Override + boolean isRelevant(Command command) { + return command.getOrder().equals(CommandOrder.ASSIGN_USER); + } + + @Override + CommandWithChangeValues toCommandWithChangeValues(CommandWithPrevious commandWithPrevious) { + return new CommandWithChangeValues( + commandWithPrevious.getCommand(), + getAssignedUserBeforeChange(commandWithPrevious), + getAssignedUserAfterChange(commandWithPrevious)); + } + + String getAssignedUserBeforeChange(CommandWithPrevious commandWithPrevious) { + return Optional.ofNullable(commandWithPrevious.getPrevious()) + .map(this::getAssignedUserFullNameFromCommand) + .orElse(StringUtils.EMPTY); + } + + String getAssignedUserAfterChange(CommandWithPrevious commandWithPrevious) { + return getAssignedUserFullNameFromCommand(commandWithPrevious.getCommand()); + } + + String getAssignedUserFullNameFromCommand(Command command) { + var assignedUserId = UserId.from(getValueFromCommandBody(BODY_PROPERTY_ASSIGNED_USER, command)); + return userProfileCache.getUserProfile(assignedUserId).getFullName(); + } +} diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/historie/ChangeHistoryBuilder.java b/alfa-service/src/main/java/de/ozgcloud/alfa/historie/ChangeHistoryBuilder.java new file mode 100644 index 0000000000000000000000000000000000000000..886ceafa109be774f0f07c071438264041679132 --- /dev/null +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/historie/ChangeHistoryBuilder.java @@ -0,0 +1,89 @@ +package de.ozgcloud.alfa.historie; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Optional; +import java.util.stream.Stream; + +import org.apache.commons.lang3.StringUtils; + +import de.ozgcloud.alfa.common.command.Command; +import de.ozgcloud.alfa.historie.CommandWithPrevious.CommandWithPreviousBuilder; + +abstract class ChangeHistoryBuilder<T extends ChangeHistoryBuilder<T>> { + + List<Command> commands; + String organisationseinheitenID; + + @SuppressWarnings("unchecked") + public T withCommands(List<Command> commands) { + this.commands = commands; + return (T) this; + } + + @SuppressWarnings("unchecked") + public T withOrganisationseinheitenID(String organisationseinheitenID) { + this.organisationseinheitenID = organisationseinheitenID; + return (T) this; + } + + public Stream<VorgangChange> build() { + return Stream.of(retrieveRelevantCommands()) + .map(this::inChronologicalOrder) + .map(this::addPreviousCommand) + .map(this::toCommandsWithChangeValues) + .flatMap(this::toVorgangChanges); + } + + Stream<Command> retrieveRelevantCommands() { + return commands.stream().filter(this::isRelevant); + } + + abstract boolean isRelevant(Command command); + + Stream<Command> inChronologicalOrder(Stream<Command> commands) { + return commands.sorted(Comparator.comparing(Command::getFinishedAt)); + } + + Stream<CommandWithPrevious> addPreviousCommand(Stream<Command> commands) { + var commandsList = commands.toList(); + var result = new ArrayList<CommandWithPrevious>(commandsList.size()); + for (int i = 0; i < commandsList.size(); i++) { + CommandWithPreviousBuilder builder = new CommandWithPreviousBuilder().command(commandsList.get(i)); + if (i > 0) { + builder.previous(commandsList.get(i - 1)); + } + result.add(builder.build()); + } + return result.stream(); + } + + Stream<CommandWithChangeValues> toCommandsWithChangeValues(Stream<CommandWithPrevious> commands) { + return commands.map(this::toCommandWithChangeValues); + } + + abstract CommandWithChangeValues toCommandWithChangeValues(CommandWithPrevious commandWithPrevious); + + Stream<VorgangChange> toVorgangChanges(Stream<CommandWithChangeValues> commandsWithChangeValues) { + return commandsWithChangeValues.map(this::toVorgangChange); + } + + VorgangChange toVorgangChange(CommandWithChangeValues commandChangeValues) { + return VorgangChange.builder() + .valueBeforeChange(commandChangeValues.valueBeforeChange()) + .valueAfterChange(commandChangeValues.valueAfterChange()) + .authorFullName(commandChangeValues.command().getCreatedByName()) + .finishedAt(commandChangeValues.command().getFinishedAt()) + .order(commandChangeValues.command().getOrder()) + .organisationseinheitenID(organisationseinheitenID) + .build(); + } + + String getValueFromCommandBody(String propertyName, Command command) { + return Optional.ofNullable(command.getBody()).map(body -> body.get(propertyName)).map(Object::toString).orElse(StringUtils.EMPTY); + } + + record CommandWithChangeValues(Command command, String valueBeforeChange, String valueAfterChange) { + } +} diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/historie/CommandWithPrevious.java b/alfa-service/src/main/java/de/ozgcloud/alfa/historie/CommandWithPrevious.java new file mode 100644 index 0000000000000000000000000000000000000000..07b785475ecdae9c63dc39353cb2bccc4479200d --- /dev/null +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/historie/CommandWithPrevious.java @@ -0,0 +1,18 @@ +package de.ozgcloud.alfa.historie; + +import de.ozgcloud.alfa.common.command.Command; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Builder(toBuilder = true) +@Getter +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +class CommandWithPrevious { + + private Command command; + private Command previous; +} diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/historie/HistorieService.java b/alfa-service/src/main/java/de/ozgcloud/alfa/historie/HistorieService.java index 6fd5dbfd9ccb84d5982961afa222bfc09ed23164..a6c233ae6b62d7643defae7f2dd0e0e5731a3b4b 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/historie/HistorieService.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/historie/HistorieService.java @@ -41,7 +41,6 @@ class HistorieService { @Autowired private CommandService commandService; - @Autowired private HistorieCommandHandler historieCommandHandler; diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/historie/StatusChangeHistoryBuilder.java b/alfa-service/src/main/java/de/ozgcloud/alfa/historie/StatusChangeHistoryBuilder.java new file mode 100644 index 0000000000000000000000000000000000000000..db75ab13a8f3b04a378f95dc18768c7f9df5ebed --- /dev/null +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/historie/StatusChangeHistoryBuilder.java @@ -0,0 +1,77 @@ +package de.ozgcloud.alfa.historie; + +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +import de.ozgcloud.alfa.common.command.Command; +import de.ozgcloud.alfa.common.command.CommandOrder; +import de.ozgcloud.alfa.vorgang.Vorgang.VorgangStatus; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class StatusChangeHistoryBuilder extends ChangeHistoryBuilder<StatusChangeHistoryBuilder> { + + static final String GELOESCHT_NAME = "Gelöscht"; + static final Map<VorgangStatus, String> VORGANG_STATUS_TO_NAME = Map.of( + VorgangStatus.NEU, "Neu", + VorgangStatus.ANGENOMMEN, "Angenommen", + VorgangStatus.VERWORFEN, "Verworfen", + VorgangStatus.IN_BEARBEITUNG, "In Bearbeitung", + VorgangStatus.BESCHIEDEN, "Beschieden", + VorgangStatus.ABGESCHLOSSEN, "Abgeschlossen", + VorgangStatus.WEITERGELEITET, "Weitergeleitet", + VorgangStatus.ZU_LOESCHEN, "Zu Löschen" + ); + static final Set<CommandOrder> STATUS_CHANGE_COMMAND_ORDER = Set.of( + CommandOrder.VORGANG_ANNEHMEN, + CommandOrder.VORGANG_VERWERFEN, + CommandOrder.VORGANG_ZURUECKHOLEN, + CommandOrder.VORGANG_BEARBEITEN, + CommandOrder.VORGANG_BESCHEIDEN, + CommandOrder.VORGANG_ABSCHLIESSEN, + CommandOrder.VORGANG_ZUM_LOESCHEN_MARKIEREN, + CommandOrder.VORGANG_LOESCHEN, + CommandOrder.VORGANG_ZURUECKSTELLEN); + + public static StatusChangeHistoryBuilder builder() { + return new StatusChangeHistoryBuilder(); + } + + @Override + boolean isRelevant(Command command) { + return STATUS_CHANGE_COMMAND_ORDER.contains(command.getOrder()); + } + + @Override + CommandWithChangeValues toCommandWithChangeValues(CommandWithPrevious commandWithPrevious) { + return new CommandWithChangeValues(commandWithPrevious.getCommand(), + getStatusBeforeChange(commandWithPrevious), + getStatusAfterChange(commandWithPrevious)); + } + + String getStatusBeforeChange(CommandWithPrevious commandWithPrevious) { + return Optional.ofNullable(commandWithPrevious.getPrevious()) + .map(this::getStatus) + .orElse(VORGANG_STATUS_TO_NAME.get(VorgangStatus.NEU)); + } + + String getStatusAfterChange(CommandWithPrevious commandWithPrevious) { + return getStatus(commandWithPrevious.getCommand()); + } + + String getStatus(Command command) { + return switch (command.getOrder()) { + case VORGANG_ANNEHMEN, VORGANG_ZURUECKSTELLEN -> VORGANG_STATUS_TO_NAME.get(VorgangStatus.ANGENOMMEN); + case VORGANG_VERWERFEN -> VORGANG_STATUS_TO_NAME.get(VorgangStatus.VERWORFEN); + case VORGANG_ZURUECKHOLEN -> VORGANG_STATUS_TO_NAME.get(VorgangStatus.NEU); + case VORGANG_BEARBEITEN, VORGANG_WIEDEREROEFFNEN -> VORGANG_STATUS_TO_NAME.get(VorgangStatus.IN_BEARBEITUNG); + case VORGANG_BESCHEIDEN -> VORGANG_STATUS_TO_NAME.get(VorgangStatus.BESCHIEDEN); + case VORGANG_ABSCHLIESSEN -> VORGANG_STATUS_TO_NAME.get(VorgangStatus.ABGESCHLOSSEN); + case VORGANG_ZUM_LOESCHEN_MARKIEREN -> VORGANG_STATUS_TO_NAME.get(VorgangStatus.ZU_LOESCHEN); + case VORGANG_LOESCHEN -> GELOESCHT_NAME; + default -> throw new IllegalArgumentException("Unexpected or unknown command order " + command.getOrder()); + }; + } +} diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/historie/UserProfileCache.java b/alfa-service/src/main/java/de/ozgcloud/alfa/historie/UserProfileCache.java new file mode 100644 index 0000000000000000000000000000000000000000..70cb3ebf4fed00ea537ceeeb4ae9f97985e48067 --- /dev/null +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/historie/UserProfileCache.java @@ -0,0 +1,38 @@ +package de.ozgcloud.alfa.historie; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; + +import de.ozgcloud.alfa.common.user.UserId; +import de.ozgcloud.alfa.common.user.UserProfile; +import de.ozgcloud.common.errorhandling.TechnicalException; +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +class UserProfileCache { + + private final Function<UserId, UserProfile> findUserProfileForUserId; + + private final Map<UserId, UserProfile> cache = new HashMap<>(); + + public static UserProfileCache create(Function<UserId, UserProfile> findUserProfileForUserId) { + return new UserProfileCache(findUserProfileForUserId); + } + + public UserProfile getUserProfile(UserId userId) { + if (!cache.containsKey(userId)) { + findAndAddToCache(userId); + } + return cache.get(userId); + } + + private void findAndAddToCache(UserId userId) { + var userProfile = findUserProfileForUserId.apply(userId); + if (userProfile == null) { + throw new TechnicalException("UserProfile not found for user with ID " + userId); + } + cache.put(userId, userProfile); + } +} diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/historie/VorgangChange.java b/alfa-service/src/main/java/de/ozgcloud/alfa/historie/VorgangChange.java new file mode 100644 index 0000000000000000000000000000000000000000..cff5cac6fe5ccb7b08ebf9fdcf201a7cb5061b48 --- /dev/null +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/historie/VorgangChange.java @@ -0,0 +1,24 @@ +package de.ozgcloud.alfa.historie; + +import java.time.ZonedDateTime; + +import de.ozgcloud.alfa.common.command.CommandOrder; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Builder(toBuilder = true) +@Getter +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class VorgangChange { + + private String valueBeforeChange; + private String valueAfterChange; + private String authorFullName; + private ZonedDateTime finishedAt; + private CommandOrder order; + private String organisationseinheitenID; +} diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/historie/VorgangChangeHistory.java b/alfa-service/src/main/java/de/ozgcloud/alfa/historie/VorgangChangeHistory.java new file mode 100644 index 0000000000000000000000000000000000000000..2fef260aa22d04cad7226090c43cdee68885ca13 --- /dev/null +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/historie/VorgangChangeHistory.java @@ -0,0 +1,20 @@ +package de.ozgcloud.alfa.historie; + +import java.util.List; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Builder(toBuilder = true) +@Getter +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class VorgangChangeHistory { + + private List<VorgangChange> statusChangeHistory; + private List<VorgangChange> aktenzeichenChangeHistory; + private List<VorgangChange> assignedUserChangeHistory; +} diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/historie/VorgangChangeHistoryService.java b/alfa-service/src/main/java/de/ozgcloud/alfa/historie/VorgangChangeHistoryService.java new file mode 100644 index 0000000000000000000000000000000000000000..f855cc97a4cfcadd6511c91ec289c383934830ef --- /dev/null +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/historie/VorgangChangeHistoryService.java @@ -0,0 +1,58 @@ +package de.ozgcloud.alfa.historie; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Stream; + +import org.springframework.stereotype.Service; + +import de.ozgcloud.alfa.common.command.Command; +import de.ozgcloud.alfa.common.user.UserService; +import de.ozgcloud.alfa.vorgang.Eingang; +import de.ozgcloud.alfa.vorgang.VorgangWithEingang; +import de.ozgcloud.alfa.vorgang.ZustaendigeStelle; +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +public class VorgangChangeHistoryService { + + private final HistorieService historieService; + private final UserService userService; + + public VorgangChangeHistory createVorgangChangeHistory(VorgangWithEingang vorgang) { + var commands = historieService.findFinishedCommands(vorgang.getId()).toList(); + return VorgangChangeHistory.builder() + .statusChangeHistory(createStatusChangeHistory(vorgang, commands).toList()) + .aktenzeichenChangeHistory(createAktenzeichenChangeHistory(vorgang, commands).toList()) + .assignedUserChangeHistory(createAssignedUserChangeHistory(vorgang, commands).toList()) + .build(); + } + + Stream<VorgangChange> createStatusChangeHistory(VorgangWithEingang vorgang, List<Command> commands) { + return StatusChangeHistoryBuilder.builder() + .withCommands(commands) + .withOrganisationseinheitenID(getOrganisationseinheitenID(vorgang)) + .build(); + } + + Stream<VorgangChange> createAktenzeichenChangeHistory(VorgangWithEingang vorgang, List<Command> commands) { + return AktenzeichenChangeHistoryBuilder.builder() + .withCommands(commands) + .withOrganisationseinheitenID(getOrganisationseinheitenID(vorgang)) + .build(); + } + + Stream<VorgangChange> createAssignedUserChangeHistory(VorgangWithEingang vorgang, List<Command> commands) { + return AssignedUserChangeHistoryBuilder.builder() + .withCommands(commands) + .withOrganisationseinheitenID(getOrganisationseinheitenID(vorgang)) + .withUserProfileCache(UserProfileCache.create(userService::getById)) + .build(); + } + + String getOrganisationseinheitenID(VorgangWithEingang vorgang) { + return Optional.ofNullable(vorgang.getEingang()).map(Eingang::getZustaendigeStelle).map(ZustaendigeStelle::getOrganisationseinheitenId) + .orElse(null); + } +} diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/common/command/CommandTestFactory.java b/alfa-service/src/test/java/de/ozgcloud/alfa/common/command/CommandTestFactory.java index f0b608d73e33551e1f6ac87db6200a0d275ae6e7..7a56836a827ce418a16646a969b5bc3f6370856f 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/common/command/CommandTestFactory.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/common/command/CommandTestFactory.java @@ -52,7 +52,8 @@ public class CommandTestFactory { .relationId(RELATION_ID) .status(STATUS) .order(ORDER) - .createdBy(UserProfileTestFactory.ID); + .createdBy(UserProfileTestFactory.ID) + .createdByName(UserProfileTestFactory.FULLNAME); } public static CreateCommand createCreateCommand() { diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/historie/AktenzeichenChangeHistoryBuilderTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/historie/AktenzeichenChangeHistoryBuilderTest.java new file mode 100644 index 0000000000000000000000000000000000000000..578007c995c76a52ecd9ecca1f0ebabc4644f5b1 --- /dev/null +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/historie/AktenzeichenChangeHistoryBuilderTest.java @@ -0,0 +1,177 @@ +package de.ozgcloud.alfa.historie; + +import static de.ozgcloud.alfa.historie.AktenzeichenChangeHistoryBuilder.*; +import static de.ozgcloud.alfa.historie.ChangeHistoryBuilderTest.*; +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.UUID; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.Spy; + +import com.thedeanda.lorem.LoremIpsum; + +import de.ozgcloud.alfa.common.command.Command; +import de.ozgcloud.alfa.common.command.CommandOrder; +import de.ozgcloud.alfa.common.command.CommandTestFactory; + +public class AktenzeichenChangeHistoryBuilderTest { + + private static final String ORGANISATIONSEINHEITEN_ID = UUID.randomUUID().toString(); + + private final Command previousCommand = commandFinishedAt(LocalDateTime.of(2023, 5, 1, 12, 0)); + private final Command command = commandFinishedAt(LocalDateTime.of(2023, 6, 1, 12, 0)); + private final List<Command> commands = List.of(previousCommand, command); + + @Spy + private AktenzeichenChangeHistoryBuilder builder = AktenzeichenChangeHistoryBuilder.builder() + .withCommands(commands) + .withOrganisationseinheitenID(ORGANISATIONSEINHEITEN_ID); + + @Nested + class TestIsRelevant { + + @Test + void shouldReturnTrue() { + var command = CommandTestFactory.createBuilder() + .order(CommandOrder.SET_AKTENZEICHEN) + .build(); + + var result = builder.isRelevant(command); + + assertThat(result).isTrue(); + } + + @Test + void shouldReturnFalse() { + var command = CommandTestFactory.createBuilder() + .order(CommandOrder.VORGANG_WIEDEREROEFFNEN) + .build(); + + var result = builder.isRelevant(command); + + assertThat(result).isFalse(); + } + } + + @Nested + class TestToCommandWithChangeValues { + + private static final String EXPECTED_AKTENZEICHEN_BEFORE_CHANGE = LoremIpsum.getInstance().getWords(2); + private static final String EXPECTED_AKTENZEICHEN_AFTER_CHANGE = LoremIpsum.getInstance().getWords(2); + + private final CommandWithPrevious commandWithPrevious = CommandWithPreviousTestFactory.createBuilder().command(command).previous(previousCommand).build(); + + @BeforeEach + void init() { + when(builder.getAktenzeichenBeforeChange(commandWithPrevious)).thenReturn(EXPECTED_AKTENZEICHEN_BEFORE_CHANGE); + when(builder.getAktenzeichenAfterChange(commandWithPrevious)).thenReturn(EXPECTED_AKTENZEICHEN_AFTER_CHANGE); + } + + @Test + void shouldSetCommand() { + var commandWithChangeValues = callBuilder(); + + assertThat(commandWithChangeValues.command()).isEqualTo(command); + } + + @Test + void shouldSetValueBeforeChange() { + var commandWithChangeValues = callBuilder(); + + assertThat(commandWithChangeValues.valueBeforeChange()).isEqualTo(EXPECTED_AKTENZEICHEN_BEFORE_CHANGE); + } + + @Test + void shouldSetValueAfterChange() { + var commandWithChangeValues = callBuilder(); + + assertThat(commandWithChangeValues.valueAfterChange()).isEqualTo(EXPECTED_AKTENZEICHEN_AFTER_CHANGE); + } + + private ChangeHistoryBuilder.CommandWithChangeValues callBuilder() { + return builder.toCommandWithChangeValues(commandWithPrevious); + } + } + + @Nested + class TestGetAktenzeichenBeforeChange { + + private final CommandWithPrevious commandWithoutPrevious = CommandWithPreviousTestFactory.createBuilder().command(previousCommand).previous(null).build(); + private final CommandWithPrevious commandWithPrevious = CommandWithPreviousTestFactory.createBuilder().command(command).previous(previousCommand).build(); + + @Test + void shouldReturnEmptyIfPreviousIsNull() { + var aktenzeichen = builder.getAktenzeichenBeforeChange(commandWithoutPrevious); + + assertThat(aktenzeichen).isEmpty(); + } + + @Test + void shouldGetAktenzeichenFromPreviousCommand() { + var expectedValue = LoremIpsum.getInstance().getWords(2); + when(builder.getAktenzeichen(previousCommand)).thenReturn(expectedValue); + + var aktenzeichen = builder.getAktenzeichenBeforeChange(commandWithPrevious); + + assertThat(aktenzeichen).isEqualTo(expectedValue); + } + } + + @Nested + class TestGetAktenzeichenAfterChange { + + private static final String EXPECTED_AKTENZEICHEN = LoremIpsum.getInstance().getWords(2); + + private final CommandWithPrevious commandWithPrevious = CommandWithPreviousTestFactory.createBuilder().command(command).previous(previousCommand).build(); + + @BeforeEach + void init() { + doReturn(EXPECTED_AKTENZEICHEN).when(builder).getAktenzeichen(command); + } + + @Test + void shouldGetAktenzeichenFromCommand() { + builder.getAktenzeichenAfterChange(commandWithPrevious); + + verify(builder).getAktenzeichen(command); + } + + @Test + void shouldReturnAktenzeichen() { + var result = builder.getAktenzeichenAfterChange(commandWithPrevious); + + assertThat(result).isEqualTo(EXPECTED_AKTENZEICHEN); + } + } + + @Nested + class TestGetAktenzeichen { + + private static final String AKTENZEICHEN = LoremIpsum.getInstance().getWords(1); + + @BeforeEach + void init() { + when(builder.getValueFromCommandBody(BODY_PROPERTY_AKTENZEICHEN, previousCommand)).thenReturn(AKTENZEICHEN); + } + + @Test + void shouldGetValueFromCommandBody() { + builder.getAktenzeichen(previousCommand); + + verify(builder).getValueFromCommandBody(BODY_PROPERTY_AKTENZEICHEN, previousCommand); + } + + @Test + void shouldReturnValueFromCommandBody() { + var value = builder.getAktenzeichen(previousCommand); + + assertThat(value).isEqualTo(AKTENZEICHEN); + } + } +} diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/historie/AssignedUserChangeHistoryBuilderTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/historie/AssignedUserChangeHistoryBuilderTest.java new file mode 100644 index 0000000000000000000000000000000000000000..fe8e03ecd2ea716f1427f5b56a020e19f054b9d9 --- /dev/null +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/historie/AssignedUserChangeHistoryBuilderTest.java @@ -0,0 +1,226 @@ +package de.ozgcloud.alfa.historie; + +import static de.ozgcloud.alfa.historie.AssignedUserChangeHistoryBuilder.*; +import static de.ozgcloud.alfa.historie.ChangeHistoryBuilderTest.*; +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.UUID; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; + +import com.thedeanda.lorem.LoremIpsum; + +import de.ozgcloud.alfa.common.command.Command; +import de.ozgcloud.alfa.common.command.CommandOrder; +import de.ozgcloud.alfa.common.command.CommandTestFactory; +import de.ozgcloud.alfa.common.user.UserId; +import de.ozgcloud.alfa.common.user.UserProfile; +import de.ozgcloud.alfa.common.user.UserProfileTestFactory; + +public class AssignedUserChangeHistoryBuilderTest { + + private static final String ORGANISATIONSEINHEITEN_ID = UUID.randomUUID().toString(); + + private final Command previousCommand = commandFinishedAt(LocalDateTime.of(2023, 5, 1, 12, 0)); + private final Command command = commandFinishedAt(LocalDateTime.of(2023, 6, 1, 12, 0)); + private final List<Command> commands = List.of(previousCommand, command); + + @Mock + private UserProfileCache userProfileCache; + + @Spy + @InjectMocks + private AssignedUserChangeHistoryBuilder builder = AssignedUserChangeHistoryBuilder.builder() + .withCommands(commands) + .withOrganisationseinheitenID(ORGANISATIONSEINHEITEN_ID); + + @Nested + class TestIsRelevant { + + @Test + void shouldReturnTrue() { + var command = CommandTestFactory.createBuilder() + .order(CommandOrder.ASSIGN_USER) + .build(); + + var result = builder.isRelevant(command); + + assertThat(result).isTrue(); + } + + @Test + void shouldReturnFalse() { + var command = CommandTestFactory.createBuilder() + .order(CommandOrder.VORGANG_WIEDEREROEFFNEN) + .build(); + + var result = builder.isRelevant(command); + + assertThat(result).isFalse(); + } + } + + @Nested + class TestToCommandWithChangeValues { + + private static final String ASSIGNED_USER_BEFORE_CHANGE = LoremIpsum.getInstance().getWords(2); + private static final String ASSIGNED_USER_AFTER_CHANGE = LoremIpsum.getInstance().getWords(2); + + private final CommandWithPrevious commandWithPrevious = CommandWithPreviousTestFactory.createBuilder().command(command).previous(previousCommand).build(); + + @BeforeEach + void init() { + doReturn(ASSIGNED_USER_BEFORE_CHANGE).when(builder).getAssignedUserBeforeChange(commandWithPrevious); + doReturn(ASSIGNED_USER_AFTER_CHANGE).when(builder).getAssignedUserAfterChange(commandWithPrevious); + } + + @Test + void shouldSetCommand() { + var commandWithChangeValues = callBuilder(); + + assertThat(commandWithChangeValues.command()).isEqualTo(command); + } + + @Test + void shouldSetValueBeforeChange() { + var commandWithChangeValues = callBuilder(); + + assertThat(commandWithChangeValues.valueBeforeChange()).isEqualTo(ASSIGNED_USER_BEFORE_CHANGE); + } + + @Test + void shouldSetValueAfterChange() { + var commandWithChangeValues = callBuilder(); + + assertThat(commandWithChangeValues.valueAfterChange()).isEqualTo(ASSIGNED_USER_AFTER_CHANGE); + } + + private ChangeHistoryBuilder.CommandWithChangeValues callBuilder() { + return builder.toCommandWithChangeValues(commandWithPrevious); + } + } + + @Nested + class TestGetAssignedUserBeforeChange { + + private static final String EXPECTED_ASSIGNED_USER = LoremIpsum.getInstance().getWords(2); + + private final CommandWithPrevious commandWithoutPrevious = CommandWithPreviousTestFactory.createBuilder().command(previousCommand).previous(null).build(); + private final CommandWithPrevious commandWithPrevious = CommandWithPreviousTestFactory.createBuilder().command(command).previous(previousCommand).build(); + + @Test + void shouldReturnEmptyStringIfPreviousIsNull() { + var assignedUser = builder.getAssignedUserBeforeChange(commandWithoutPrevious); + + assertThat(assignedUser).isEmpty(); + } + + @Test + void shouldGetAssignedUserFromPreviousCommand() { + givenAssignedUserFromPreviousCommand(); + + callBuilder(); + + verify(builder).getAssignedUserFullNameFromCommand(previousCommand); + } + + @Test + void shouldReturnAssignedUserRetrievedFromPreviousCommand() { + givenAssignedUserFromPreviousCommand(); + + var assignedUser = callBuilder(); + + assertThat(assignedUser).isEqualTo(EXPECTED_ASSIGNED_USER); + } + + private void givenAssignedUserFromPreviousCommand() { + doReturn(EXPECTED_ASSIGNED_USER).when(builder).getAssignedUserFullNameFromCommand(previousCommand); + } + + private String callBuilder() { + return builder.getAssignedUserBeforeChange(commandWithPrevious); + } + } + + @Nested + class TestGetAssignedUserAfterChange { + + private static final String EXPECTED_ASSIGNED_USER = LoremIpsum.getInstance().getWords(2); + + private final CommandWithPrevious commandWithPrevious = CommandWithPreviousTestFactory.createBuilder().command(command).previous(previousCommand).build(); + + @Test + void shouldGetAssignedUserFromCommand() { + givenAssignedUserFromCurrentCommand(); + + callBuilder(); + + verify(builder).getAssignedUserFullNameFromCommand(command); + } + + @Test + void shouldReturnAssignedUserRetrievedFromCommand() { + givenAssignedUserFromCurrentCommand(); + + var assignedUser = callBuilder(); + + assertThat(assignedUser).isEqualTo(EXPECTED_ASSIGNED_USER); + } + + private void givenAssignedUserFromCurrentCommand() { + doReturn(EXPECTED_ASSIGNED_USER).when(builder).getAssignedUserFullNameFromCommand(command); + } + + private String callBuilder() { + return builder.getAssignedUserAfterChange(commandWithPrevious); + } + } + + @Nested + class TestGetAssignedUserFullNameFromCommand { + + private static final UserId USER_ID = UserId.from(UUID.randomUUID()); + private static final UserProfile USER_PROFILE = UserProfileTestFactory.create(); + private final Command command = previousCommand; + + @BeforeEach + void init() { + when(builder.getValueFromCommandBody(BODY_PROPERTY_ASSIGNED_USER, previousCommand)).thenReturn(USER_ID.toString()); + when(userProfileCache.getUserProfile(USER_ID)).thenReturn(USER_PROFILE); + } + + @Test + void shouldGetUserIdFromCommand() { + callBuilder(); + + verify(builder).getValueFromCommandBody(BODY_PROPERTY_ASSIGNED_USER, command); + } + + @Test + void shouldGetUserProfileForUserId() { + callBuilder(); + + verify(userProfileCache).getUserProfile(USER_ID); + } + + @Test + void shouldReturnUserFullName() { + var assignedUser = callBuilder(); + + assertThat(assignedUser).isEqualTo(UserProfileTestFactory.FULLNAME); + } + + private String callBuilder() { + return builder.getAssignedUserFullNameFromCommand(command); + } + } +} + diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/historie/ChangeHistoryBuilderTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/historie/ChangeHistoryBuilderTest.java new file mode 100644 index 0000000000000000000000000000000000000000..823baf3713395824cd0902c0ba3e0d45d4a0d903 --- /dev/null +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/historie/ChangeHistoryBuilderTest.java @@ -0,0 +1,276 @@ +package de.ozgcloud.alfa.historie; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.stream.Stream; + +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.mockito.Mockito; +import org.mockito.Spy; +import org.testcontainers.shaded.org.apache.commons.lang3.NotImplementedException; + +import com.thedeanda.lorem.LoremIpsum; + +import de.ozgcloud.alfa.common.command.Command; +import de.ozgcloud.alfa.common.command.CommandTestFactory; +import de.ozgcloud.alfa.common.user.UserProfileTestFactory; +import de.ozgcloud.alfa.historie.ChangeHistoryBuilder.CommandWithChangeValues; + +public class ChangeHistoryBuilderTest { + + private static final String ORGANISATIONSEINHEITEN_ID = UUID.randomUUID().toString(); + + private final Command previousCommand = commandFinishedAt(LocalDateTime.of(2023, 5, 1, 12, 0)); + private final Command command = commandFinishedAt(LocalDateTime.of(2023, 6, 1, 12, 0)); + private final List<Command> commands = List.of(previousCommand, command); + + @Spy + private TestChangeHistoryBuilder builder = new TestChangeHistoryBuilder() + .withCommands(commands) + .withOrganisationseinheitenID(ORGANISATIONSEINHEITEN_ID); + + @Nested + class TestBuild { + + private final CommandWithChangeValues previousCommandWithChangeValues = new CommandWithChangeValues( + previousCommand, "a", "b"); + private final CommandWithChangeValues commandWithChangeValues = new CommandWithChangeValues( + command, "b", "c"); + private final List<CommandWithChangeValues> commandsWithChangeValues = List.of(previousCommandWithChangeValues, + commandWithChangeValues); + + @Test + void shouldCallInOrder() { + var orderVerifier = Mockito.inOrder(builder); + doReturn(commands.stream()).when(builder).retrieveRelevantCommands(); + doReturn(commandsWithChangeValues.stream()).when(builder).toCommandsWithChangeValues(notNull()); + + builder.build().toList(); + + orderVerifier.verify(builder).retrieveRelevantCommands(); + orderVerifier.verify(builder).inChronologicalOrder(notNull()); + orderVerifier.verify(builder).addPreviousCommand(notNull()); + orderVerifier.verify(builder).toCommandsWithChangeValues(notNull()); + orderVerifier.verify(builder).toVorgangChanges(notNull()); + } + } + + @Nested + class TestRetrieveRelevantCommands { + + @BeforeEach + void init() { + doReturn(false).when(builder).isRelevant(previousCommand); + doReturn(true).when(builder).isRelevant(command); + } + + @Test + void shouldCallIsRelevant() { + builder.retrieveRelevantCommands().toList(); + + verify(builder).isRelevant(previousCommand); + verify(builder).isRelevant(command); + } + + @Test + void shouldFilterOutIrrelevantCommands() { + var result = builder.retrieveRelevantCommands().toList(); + + assertThat(result).containsExactly(command); + } + } + + @Nested + class TestInChronologicalOrder { + + private final Stream<Command> unorderedCommands = Stream.of(command, previousCommand); + + @Test + void shouldSortAscendingByFinishedAt() { + var orderedCommands = builder.inChronologicalOrder(unorderedCommands); + + assertThat(orderedCommands).containsExactly(previousCommand, command); + } + } + + @Nested + class TestAddPreviousCommand { + + @Test + void shouldSetPreviousOfFirstCommandToNull() { + var commands = Stream.of(previousCommand); + + var commandsWithPrevious = builder.addPreviousCommand(commands); + + assertThat(commandsWithPrevious).first().extracting("previous").isEqualTo(null); + } + + @Test + void shouldSetPreviousOfSubsequentCommand() { + var commands = Stream.of(previousCommand, command); + + var commandsWithPrevious = builder.addPreviousCommand(commands); + + assertThat(commandsWithPrevious).element(1).extracting("previous").isEqualTo(previousCommand); + } + } + + @Nested + class TestToCommandsWithChangeValues { + + private final CommandWithPrevious commandWithoutPrevious = CommandWithPreviousTestFactory.createBuilder().command(previousCommand).previous(null).build(); + private final CommandWithPrevious commandWithPrevious = CommandWithPreviousTestFactory.createBuilder().command(command).previous(previousCommand).build(); + private final List<CommandWithPrevious> commandsWithPrevious = List.of(commandWithoutPrevious, commandWithPrevious); + + private final ChangeHistoryBuilder.CommandWithChangeValues previousCommandWithChangeValues = new ChangeHistoryBuilder.CommandWithChangeValues( + previousCommand, "a", "b"); + private final ChangeHistoryBuilder.CommandWithChangeValues commandWithChangeValues = new ChangeHistoryBuilder.CommandWithChangeValues( + command, "b", "c"); + private final List<ChangeHistoryBuilder.CommandWithChangeValues> commandsWithChangeValues = List.of(previousCommandWithChangeValues, + commandWithChangeValues); + + @BeforeEach + void init() { + doReturn(previousCommandWithChangeValues).when(builder).toCommandWithChangeValues(commandWithoutPrevious); + doReturn(commandWithChangeValues).when(builder).toCommandWithChangeValues(commandWithPrevious); + } + + @Test + void shouldMapToCommandWithChangeValues() { + callBuilder().toList(); + + verify(builder).toCommandWithChangeValues(commandWithoutPrevious); + verify(builder).toCommandWithChangeValues(commandWithPrevious); + } + + @Test + void shouldReturnMappedCommandWithChangeValues() { + var historieEntries = callBuilder().toList(); + + assertThat(historieEntries).containsExactly(previousCommandWithChangeValues, commandWithChangeValues); + } + + private Stream<CommandWithChangeValues> callBuilder() { + return builder.toCommandsWithChangeValues(commandsWithPrevious.stream()); + } + } + + @Nested + class TestToVorgangChange { + + private final ChangeHistoryBuilder.CommandWithChangeValues commandWithChangeValues = new ChangeHistoryBuilder.CommandWithChangeValues( + previousCommand, "a", "b"); + + @Test + void shouldSeValueBeforeChange() { + var vorgangChange = callBuilder(); + + assertThat(vorgangChange.getValueBeforeChange()).isNotBlank().isEqualTo(commandWithChangeValues.valueBeforeChange()); + } + + @Test + void shouldSeValueAfterChange() { + var vorgangChange = callBuilder(); + + assertThat(vorgangChange.getValueAfterChange()).isNotBlank().isEqualTo(commandWithChangeValues.valueAfterChange()); + } + + @Test + void shouldSetAuthorFullName() { + var vorgangChange = callBuilder(); + + assertThat(vorgangChange.getAuthorFullName()).isEqualTo(UserProfileTestFactory.FULLNAME); + } + + @Test + void shouldSetFinishedAt() { + var vorgangChange = callBuilder(); + + assertThat(vorgangChange.getFinishedAt()).isNotNull().isEqualTo(previousCommand.getFinishedAt()); + } + + @Test + void shouldSetOrder() { + var vorgangChange = callBuilder(); + + assertThat(vorgangChange.getOrder()).isNotNull().isEqualTo(previousCommand.getOrder()); + } + + @Test + void shouldSetOrganisationseinheitenID() { + var vorgangChange = callBuilder(); + + assertThat(vorgangChange.getOrganisationseinheitenID()).isEqualTo(ORGANISATIONSEINHEITEN_ID); + } + + private VorgangChange callBuilder() { + return builder.toVorgangChange(commandWithChangeValues); + } + } + + @Nested + class TestGetValueFromCommandBody { + + private static final String PROPERTY_NAME = LoremIpsum.getInstance().getWords(1); + + @ParameterizedTest + @NullAndEmptySource + void shouldReturnEmptyIfBodyIsNullOrEmpty(Map<String, ?> body) { + var command = previousCommand.toBuilder().body(body).build(); + + var value = builder.getValueFromCommandBody(PROPERTY_NAME, command); + + assertThat(value).isEmpty(); + } + + @Test + void shouldReturnEmptyIfPropertyIsNotPresentInBody() { + var command = previousCommand.toBuilder().body(Map.of("a", "b")).build(); + + var value = builder.getValueFromCommandBody(PROPERTY_NAME, command); + + assertThat(value).isEmpty(); + } + + @Test + void shouldReturnValueFromBody() { + var expectedValue = LoremIpsum.getInstance().getWords(2); + var command = previousCommand.toBuilder().body(Map.of(PROPERTY_NAME, expectedValue)).build(); + + var value = builder.getValueFromCommandBody(PROPERTY_NAME, command); + + assertThat(value).isEqualTo(expectedValue); + } + } + + + static Command commandFinishedAt(LocalDateTime finishedAt) { + return CommandTestFactory.createBuilder() + .finishedAt(ZonedDateTime.of(finishedAt, ZoneId.of("UTC"))) + .build(); + } + + private static class TestChangeHistoryBuilder extends ChangeHistoryBuilder<TestChangeHistoryBuilder> { + + @Override + boolean isRelevant(Command command) { + throw new NotImplementedException(); + } + + @Override + CommandWithChangeValues toCommandWithChangeValues(CommandWithPrevious commandWithPrevious) { + throw new NotImplementedException(); + } + } +} diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/historie/CommandWithPreviousTestFactory.java b/alfa-service/src/test/java/de/ozgcloud/alfa/historie/CommandWithPreviousTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..7c875f11d9e9de3b3178244fe83c4e149899f2b2 --- /dev/null +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/historie/CommandWithPreviousTestFactory.java @@ -0,0 +1,21 @@ +package de.ozgcloud.alfa.historie; + +import de.ozgcloud.alfa.common.command.Command; +import de.ozgcloud.alfa.common.command.CommandTestFactory; +import de.ozgcloud.alfa.historie.CommandWithPrevious.CommandWithPreviousBuilder; + +public class CommandWithPreviousTestFactory { + + public static final Command COMMAND = CommandTestFactory.create(); + public static final Command PREVIOUS_COMMAND = CommandTestFactory.create(); + + public static CommandWithPrevious create() { + return createBuilder().build(); + } + + public static CommandWithPreviousBuilder createBuilder() { + return new CommandWithPreviousBuilder() + .command(COMMAND) + .previous(PREVIOUS_COMMAND); + } +} diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/historie/HistorieServiceTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/historie/HistorieServiceTest.java index 3a5be826fa3dd6b2b4b12afa5232d57585482eba..c077ec5d7ac5bec75832117f08d395c119223c12 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/historie/HistorieServiceTest.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/historie/HistorieServiceTest.java @@ -37,6 +37,7 @@ import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; +import org.mockito.Spy; import de.ozgcloud.alfa.common.command.Command; import de.ozgcloud.alfa.common.command.CommandOrder; @@ -47,6 +48,7 @@ import de.ozgcloud.alfa.vorgang.VorgangHeaderTestFactory; class HistorieServiceTest { + @Spy @InjectMocks private HistorieService service; @Mock diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/historie/StatusChangeHistoryBuilderTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/historie/StatusChangeHistoryBuilderTest.java new file mode 100644 index 0000000000000000000000000000000000000000..a1616f1a22a508790191315f61d77fbaa2f2b675 --- /dev/null +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/historie/StatusChangeHistoryBuilderTest.java @@ -0,0 +1,208 @@ +package de.ozgcloud.alfa.historie; + +import static de.ozgcloud.alfa.historie.ChangeHistoryBuilderTest.*; +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.UUID; + +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.EnumSource; +import org.junit.jupiter.params.provider.EnumSource.Mode; +import org.mockito.Spy; + +import com.thedeanda.lorem.LoremIpsum; + +import de.ozgcloud.alfa.common.command.Command; +import de.ozgcloud.alfa.common.command.CommandOrder; +import de.ozgcloud.alfa.common.command.CommandTestFactory; +import de.ozgcloud.alfa.vorgang.Vorgang.VorgangStatus; + +public class StatusChangeHistoryBuilderTest { + + private static final String ORGANISATIONSEINHEITEN_ID = UUID.randomUUID().toString(); + + private final Command previousCommand = commandFinishedAt(LocalDateTime.of(2023, 5, 1, 12, 0)); + private final Command command = commandFinishedAt(LocalDateTime.of(2023, 6, 1, 12, 0)); + private final List<Command> commands = List.of(previousCommand, command); + + @Spy + private StatusChangeHistoryBuilder builder = StatusChangeHistoryBuilder.builder() + .withCommands(commands) + .withOrganisationseinheitenID(ORGANISATIONSEINHEITEN_ID); + + @Nested + class TestIsRelevant { + + @EnumSource(mode = Mode.INCLUDE, names = { "VORGANG_ANNEHMEN", "VORGANG_VERWERFEN", "VORGANG_ZURUECKHOLEN", "VORGANG_BEARBEITEN", + "VORGANG_BESCHEIDEN", "VORGANG_ABSCHLIESSEN", "VORGANG_ZUM_LOESCHEN_MARKIEREN", "VORGANG_LOESCHEN", "VORGANG_ZURUECKSTELLEN" }) + @ParameterizedTest + void shouldReturnTrue(CommandOrder order) { + var isStatusChangeCommand = builder.isRelevant(CommandTestFactory.createBuilder().order(order).build()); + + assertThat(isStatusChangeCommand).isTrue(); + } + + @EnumSource(mode = Mode.EXCLUDE, names = { "VORGANG_ANNEHMEN", "VORGANG_VERWERFEN", "VORGANG_ZURUECKHOLEN", "VORGANG_BEARBEITEN", + "VORGANG_BESCHEIDEN", "VORGANG_ABSCHLIESSEN", "VORGANG_ZUM_LOESCHEN_MARKIEREN", "VORGANG_LOESCHEN", "VORGANG_ZURUECKSTELLEN" }) + @ParameterizedTest + void shouldReturnFalse(CommandOrder order) { + var isStatusChangeCommand = builder.isRelevant(CommandTestFactory.createBuilder().order(order).build()); + + assertThat(isStatusChangeCommand).isFalse(); + } + } + + @Nested + class TestToCommandWithChangeValues { + + private static final String EXPECTED_STATUS_BEFORE_CHANGE = LoremIpsum.getInstance().getWords(2); + private static final String EXPECTED_STATUS_AFTER_CHANGE = LoremIpsum.getInstance().getWords(2); + + private final CommandWithPrevious commandWithPrevious = CommandWithPreviousTestFactory.createBuilder().command(command).previous(previousCommand).build(); + + @BeforeEach + void init() { + doReturn(EXPECTED_STATUS_BEFORE_CHANGE).when(builder).getStatusBeforeChange(commandWithPrevious); + doReturn(EXPECTED_STATUS_AFTER_CHANGE).when(builder).getStatusAfterChange(commandWithPrevious); + } + + @Test + void shouldSetCommand() { + var commandWithChangeValues = callBuilder(); + + assertThat(commandWithChangeValues.command()).isEqualTo(command); + } + + @Test + void shouldSetValueBeforeChange() { + var commandWithChangeValues = callBuilder(); + + assertThat(commandWithChangeValues.valueBeforeChange()).isEqualTo(EXPECTED_STATUS_BEFORE_CHANGE); + } + + @Test + void shouldSetValueAfterChange() { + var commandWithChangeValues = callBuilder(); + + assertThat(commandWithChangeValues.valueAfterChange()).isEqualTo(EXPECTED_STATUS_AFTER_CHANGE); + } + + private ChangeHistoryBuilder.CommandWithChangeValues callBuilder() { + return builder.toCommandWithChangeValues(commandWithPrevious); + } + } + + @Nested + class TestGetStatusBeforeChange { + + private final CommandWithPrevious commandWithoutPrevious = CommandWithPreviousTestFactory.createBuilder().command(previousCommand).previous(null).build(); + private final CommandWithPrevious commandWithPrevious = CommandWithPreviousTestFactory.createBuilder().command(command).previous(previousCommand).build(); + + @Test + void shouldReturnStatusBeforeChange() { + var status = "Status"; + doReturn(status).when(builder).getStatus(previousCommand); + + var statusBeforeChange = builder.getStatusBeforeChange(commandWithPrevious); + + assertThat(statusBeforeChange).isEqualTo(status); + } + + @Test + void shouldReturnDefaultValue() { + var status = builder.getStatusBeforeChange(commandWithoutPrevious); + + assertThat(status).isEqualTo(StatusChangeHistoryBuilder.VORGANG_STATUS_TO_NAME.get(VorgangStatus.NEU)); + } + } + + @Nested + class TestGetStatusAfterChange { + + private final CommandWithPrevious commandWithPrevious = CommandWithPreviousTestFactory.createBuilder().command(command).previous(previousCommand).build(); + + @Test + void shouldReturnStatusAfterChange() { + var status = "Status"; + doReturn(status).when(builder).getStatus(command); + + var statusAfterChange = builder.getStatusAfterChange(commandWithPrevious); + + assertThat(statusAfterChange).isEqualTo(status); + } + } + + @Nested + class TestGetStatus { + + @ParameterizedTest + @EnumSource(mode = Mode.INCLUDE, names = { "VORGANG_ANNEHMEN", "VORGANG_ZURUECKSTELLEN" }) + void shouldReturnAngenommen(CommandOrder order) { + var status = builder.getStatus(CommandTestFactory.createBuilder().order(order).build()); + + assertThat(status).isEqualTo(StatusChangeHistoryBuilder.VORGANG_STATUS_TO_NAME.get(VorgangStatus.ANGENOMMEN)); + } + + @Test + void shouldReturnVerworfen() { + var status = builder.getStatus(CommandTestFactory.createBuilder().order(CommandOrder.VORGANG_VERWERFEN).build()); + + assertThat(status).isEqualTo(StatusChangeHistoryBuilder.VORGANG_STATUS_TO_NAME.get(VorgangStatus.VERWORFEN)); + } + + @Test + void shouldReturnNeu() { + var status = builder.getStatus(CommandTestFactory.createBuilder().order(CommandOrder.VORGANG_ZURUECKHOLEN).build()); + + assertThat(status).isEqualTo(StatusChangeHistoryBuilder.VORGANG_STATUS_TO_NAME.get(VorgangStatus.NEU)); + } + + @ParameterizedTest + @EnumSource(mode = Mode.INCLUDE, names = { "VORGANG_BEARBEITEN", "VORGANG_WIEDEREROEFFNEN" }) + void shouldReturnInBearbeitung(CommandOrder order) { + var status = builder.getStatus(CommandTestFactory.createBuilder().order(order).build()); + + assertThat(status).isEqualTo(StatusChangeHistoryBuilder.VORGANG_STATUS_TO_NAME.get(VorgangStatus.IN_BEARBEITUNG)); + } + + @Test + void shouldReturnBeschieden() { + var status = builder.getStatus(CommandTestFactory.createBuilder().order(CommandOrder.VORGANG_BESCHEIDEN).build()); + + assertThat(status).isEqualTo(StatusChangeHistoryBuilder.VORGANG_STATUS_TO_NAME.get(VorgangStatus.BESCHIEDEN)); + } + + @Test + void shouldReturnAbschliessen() { + var status = builder.getStatus(CommandTestFactory.createBuilder().order(CommandOrder.VORGANG_ABSCHLIESSEN).build()); + + assertThat(status).isEqualTo(StatusChangeHistoryBuilder.VORGANG_STATUS_TO_NAME.get(VorgangStatus.ABGESCHLOSSEN)); + } + + @Test + void shouldReturnZuLoeschen() { + var status = builder.getStatus(CommandTestFactory.createBuilder().order(CommandOrder.VORGANG_ZUM_LOESCHEN_MARKIEREN).build()); + + assertThat(status).isEqualTo(StatusChangeHistoryBuilder.VORGANG_STATUS_TO_NAME.get(VorgangStatus.ZU_LOESCHEN)); + } + + @Test + void shouldReturnGeloescht() { + var status = builder.getStatus(CommandTestFactory.createBuilder().order(CommandOrder.VORGANG_LOESCHEN).build()); + + assertThat(status).isEqualTo(StatusChangeHistoryBuilder.GELOESCHT_NAME); + } + + @Test + void shouldtThrowIllegalArgumentException() { + assertThatThrownBy(() -> builder.getStatus(CommandTestFactory.createBuilder().order(CommandOrder.ASSIGN_USER).build())).isInstanceOf( + IllegalArgumentException.class); + } + } +} diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/historie/UserProfileCacheTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/historie/UserProfileCacheTest.java new file mode 100644 index 0000000000000000000000000000000000000000..001726a186d01137015228abec157856847b1585 --- /dev/null +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/historie/UserProfileCacheTest.java @@ -0,0 +1,98 @@ +package de.ozgcloud.alfa.historie; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.function.Function; + +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.springframework.test.util.ReflectionTestUtils; + +import de.ozgcloud.alfa.common.user.UserId; +import de.ozgcloud.alfa.common.user.UserProfile; +import de.ozgcloud.alfa.common.user.UserProfileTestFactory; +import de.ozgcloud.common.errorhandling.TechnicalException; + +public class UserProfileCacheTest { + + private static final UserId USER_ID = UserProfileTestFactory.ID; + private static final UserProfile EXPECTED_USER_PROFILE = UserProfileTestFactory.create(); + + @Mock + private Function<UserId, UserProfile> findUserProfileForUserId; + @InjectMocks + private UserProfileCache cache; + + @Test + void shouldSearchForUserProfileThatWasNotCached() { + givenFindUserProfileForUserIdReturnsUserProfile(); + + cache.getUserProfile(USER_ID); + + verify(findUserProfileForUserId).apply(USER_ID); + } + + @Test + void shouldReturnFoundUserProfile() { + givenFindUserProfileForUserIdReturnsUserProfile(); + + var userProfile = cache.getUserProfile(USER_ID); + + assertThat(userProfile).isEqualTo(EXPECTED_USER_PROFILE); + } + + @Test + void shouldAddFoundUserProfileToCache() { + givenFindUserProfileForUserIdReturnsUserProfile(); + + cache.getUserProfile(USER_ID); + + assertThat(getUserProfileFromCache()).isEqualTo(EXPECTED_USER_PROFILE); + } + + @Test + void shouldNotSearchForCachedUserProfile() { + givenUserProfileIsInCache(); + + cache.getUserProfile(USER_ID); + + verifyNoInteractions(findUserProfileForUserId); + } + + @Test + void shouldReturnUserProfileFromCache() { + givenUserProfileIsInCache(); + + var userProfile = cache.getUserProfile(USER_ID); + + assertThat(userProfile).isEqualTo(EXPECTED_USER_PROFILE); + } + + @Test + void shouldThrowExceptionIfUserProfileCannotBeFound() { + when(findUserProfileForUserId.apply(USER_ID)).thenReturn(null); + + assertThatExceptionOfType(TechnicalException.class).isThrownBy(() -> cache.getUserProfile(USER_ID)) + .withMessageStartingWith("UserProfile not found for user with ID " + USER_ID); + } + + private void givenFindUserProfileForUserIdReturnsUserProfile() { + when(findUserProfileForUserId.apply(USER_ID)).thenReturn(EXPECTED_USER_PROFILE); + } + + private void givenUserProfileIsInCache() { + var cache = new HashMap<UserId, UserProfile>(); + cache.put(USER_ID, EXPECTED_USER_PROFILE); + ReflectionTestUtils.setField(this.cache, "cache", cache); + } + + @SuppressWarnings("unchecked") + private UserProfile getUserProfileFromCache() { + return ((Map<String, UserProfile>) Objects.requireNonNull(ReflectionTestUtils.getField(cache, "cache"))).get(USER_ID); + } +} diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/historie/VorgangChangeHistoryServiceITCase.java b/alfa-service/src/test/java/de/ozgcloud/alfa/historie/VorgangChangeHistoryServiceITCase.java new file mode 100644 index 0000000000000000000000000000000000000000..2d29753b49568b416448a85371e608dd38b7e99c --- /dev/null +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/historie/VorgangChangeHistoryServiceITCase.java @@ -0,0 +1,342 @@ +package de.ozgcloud.alfa.historie; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.List; +import java.util.Map; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import com.thedeanda.lorem.LoremIpsum; + +import de.ozgcloud.alfa.common.command.Command; +import de.ozgcloud.alfa.common.command.CommandOrder; +import de.ozgcloud.alfa.common.command.CommandTestFactory; +import de.ozgcloud.alfa.common.user.UserId; +import de.ozgcloud.alfa.common.user.UserProfile; +import de.ozgcloud.alfa.common.user.UserProfileTestFactory; +import de.ozgcloud.alfa.common.user.UserService; +import de.ozgcloud.alfa.vorgang.Vorgang.VorgangStatus; +import de.ozgcloud.alfa.vorgang.VorgangHeaderTestFactory; +import de.ozgcloud.alfa.vorgang.VorgangWithEingang; +import de.ozgcloud.alfa.vorgang.VorgangWithEingangTestFactory; + +public class VorgangChangeHistoryServiceITCase { + + private static final UserId USER_1_ID = UserId.from("user-1"); + private static final String USER_1_FIRST_NAME = LoremIpsum.getInstance().getFirstName(); + private static final String USER_1_LAST_NAME = LoremIpsum.getInstance().getLastName(); + private static final String USER_1_FULL_NAME = String.format("%s %s", USER_1_FIRST_NAME, USER_1_LAST_NAME); + + private static final UserId USER_2_ID = UserId.from("user-2"); + private static final String USER_2_FIRST_NAME = LoremIpsum.getInstance().getFirstName(); + private static final String USER_2_LAST_NAME = LoremIpsum.getInstance().getLastName(); + private static final String USER_2_FULL_NAME = String.format("%s %s", USER_2_FIRST_NAME, USER_2_LAST_NAME); + + private static final String AKTENZEICHEN_1 = "ABC-1"; + private static final String AKTENZEICHEN_2 = "ABC-2"; + + private final VorgangWithEingang vorgangWithEingang = VorgangWithEingangTestFactory.create(); + + @Mock + private HistorieService historieService; + @Mock + private UserService userService; + + @InjectMocks + private VorgangChangeHistoryService service; + + @Test + void shouldReturnEmptyHistory() { + givenHistorieServiceReturnsCommands(List.of()); + + var history = service.createVorgangChangeHistory(vorgangWithEingang); + + assertThat(history.getStatusChangeHistory()).isEmpty(); + assertThat(history.getAktenzeichenChangeHistory()).isEmpty(); + assertThat(history.getAssignedUserChangeHistory()).isEmpty(); + } + + @Test + void shouldReturnNonEmptyHistories() { + givenHistorieServiceReturnsCommands(createMixedCommands()); + givenUserServiceReturnsUser1Profile(); + + var history = service.createVorgangChangeHistory(vorgangWithEingang); + + assertThat(history.getStatusChangeHistory()).hasSize(1); + assertThat(history.getAktenzeichenChangeHistory()).hasSize(1); + assertThat(history.getAssignedUserChangeHistory()).hasSize(1); + } + + @Test + void shouldSetCreatedByName() { + givenHistorieServiceReturnsCommands(List.of(new CommandFactory().statusChange(CommandOrder.VORGANG_ANNEHMEN))); + + var history = service.createVorgangChangeHistory(vorgangWithEingang).getStatusChangeHistory(); + + assertThat(history.get(0).getAuthorFullName()).isEqualTo(USER_1_FULL_NAME); + } + + @Test + void shouldSetFinishedAt() { + var command = new CommandFactory().statusChange(CommandOrder.VORGANG_ANNEHMEN); + givenHistorieServiceReturnsCommands(List.of(command)); + + var history = service.createVorgangChangeHistory(vorgangWithEingang).getStatusChangeHistory(); + + assertThat(history.get(0).getFinishedAt()).isNotNull().isEqualTo(command.getFinishedAt()); + } + + @Nested + class TestStatusChangeHistory { + @Test + void shouldSetBeforeAndAfterValues_untilAbgeschlossen() { + givenHistorieServiceReturnsCommands(createStatusChangeCommandsUntilAbgeschlossen()); + + var history = service.createVorgangChangeHistory(vorgangWithEingang).getStatusChangeHistory(); + + assertThat(history).hasSize(6); + checkBeforeAndAfterValues(history.get(0), + StatusChangeHistoryBuilder.VORGANG_STATUS_TO_NAME.get(VorgangStatus.NEU), + StatusChangeHistoryBuilder.VORGANG_STATUS_TO_NAME.get(VorgangStatus.ANGENOMMEN)); + checkBeforeAndAfterValues(history.get(1), + StatusChangeHistoryBuilder.VORGANG_STATUS_TO_NAME.get(VorgangStatus.ANGENOMMEN), + StatusChangeHistoryBuilder.VORGANG_STATUS_TO_NAME.get(VorgangStatus.IN_BEARBEITUNG)); + checkBeforeAndAfterValues(history.get(2), + StatusChangeHistoryBuilder.VORGANG_STATUS_TO_NAME.get(VorgangStatus.IN_BEARBEITUNG), + StatusChangeHistoryBuilder.VORGANG_STATUS_TO_NAME.get(VorgangStatus.BESCHIEDEN)); + checkBeforeAndAfterValues(history.get(3), + StatusChangeHistoryBuilder.VORGANG_STATUS_TO_NAME.get(VorgangStatus.BESCHIEDEN), + StatusChangeHistoryBuilder.VORGANG_STATUS_TO_NAME.get(VorgangStatus.ABGESCHLOSSEN)); + checkBeforeAndAfterValues(history.get(4), + StatusChangeHistoryBuilder.VORGANG_STATUS_TO_NAME.get(VorgangStatus.ABGESCHLOSSEN), + StatusChangeHistoryBuilder.VORGANG_STATUS_TO_NAME.get(VorgangStatus.ZU_LOESCHEN)); + checkBeforeAndAfterValues(history.get(5), + StatusChangeHistoryBuilder.VORGANG_STATUS_TO_NAME.get(VorgangStatus.ZU_LOESCHEN), + StatusChangeHistoryBuilder.VORGANG_STATUS_TO_NAME.get(VorgangStatus.ABGESCHLOSSEN)); + } + + @Test + void shouldSetBeforeAndAfterValues_untilVerworfen() { + givenHistorieServiceReturnsCommands(createStatusChangeCommandsUntilVerworfen()); + + var history = service.createVorgangChangeHistory(vorgangWithEingang).getStatusChangeHistory(); + + assertThat(history).hasSize(3); + checkBeforeAndAfterValues(history.get(0), + StatusChangeHistoryBuilder.VORGANG_STATUS_TO_NAME.get(VorgangStatus.NEU), + StatusChangeHistoryBuilder.VORGANG_STATUS_TO_NAME.get(VorgangStatus.VERWORFEN)); + checkBeforeAndAfterValues(history.get(1), + StatusChangeHistoryBuilder.VORGANG_STATUS_TO_NAME.get(VorgangStatus.VERWORFEN), + StatusChangeHistoryBuilder.VORGANG_STATUS_TO_NAME.get(VorgangStatus.ZU_LOESCHEN)); + checkBeforeAndAfterValues(history.get(2), + StatusChangeHistoryBuilder.VORGANG_STATUS_TO_NAME.get(VorgangStatus.ZU_LOESCHEN), + StatusChangeHistoryBuilder.VORGANG_STATUS_TO_NAME.get(VorgangStatus.VERWORFEN)); + } + + @Test + void shouldSetBeforeAndAfterValues_untilZurueckholen() { + givenHistorieServiceReturnsCommands(createStatusChangeCommandsUntilZurueckholen()); + + var history = service.createVorgangChangeHistory(vorgangWithEingang).getStatusChangeHistory(); + + assertThat(history).hasSize(2); + checkBeforeAndAfterValues(history.get(0), + StatusChangeHistoryBuilder.VORGANG_STATUS_TO_NAME.get(VorgangStatus.NEU), + StatusChangeHistoryBuilder.VORGANG_STATUS_TO_NAME.get(VorgangStatus.VERWORFEN)); + checkBeforeAndAfterValues(history.get(1), + StatusChangeHistoryBuilder.VORGANG_STATUS_TO_NAME.get(VorgangStatus.VERWORFEN), + StatusChangeHistoryBuilder.VORGANG_STATUS_TO_NAME.get(VorgangStatus.NEU)); + } + + @Test + void shouldSetOrder() { + var command = new CommandFactory().statusChange(CommandOrder.VORGANG_ANNEHMEN); + givenHistorieServiceReturnsCommands(List.of(command)); + + var history = service.createVorgangChangeHistory(vorgangWithEingang).getStatusChangeHistory(); + + assertThat(history.get(0).getOrder()).isNotNull().isEqualTo(CommandOrder.VORGANG_ANNEHMEN); + } + } + + @Nested + class TestAktenzeichenChangeHistory { + + @Test + void shouldSetBeforeAndAfterValues() { + givenHistorieServiceReturnsCommands(createAktenzeichenChangeCommands()); + + var history = service.createVorgangChangeHistory(vorgangWithEingang).getAktenzeichenChangeHistory(); + + assertThat(history).hasSize(3); + checkBeforeAndAfterValues(history.get(0), "", AKTENZEICHEN_1); + checkBeforeAndAfterValues(history.get(1), AKTENZEICHEN_1, AKTENZEICHEN_2); + checkBeforeAndAfterValues(history.get(2), AKTENZEICHEN_2, ""); + } + + @Test + void shouldSetOrder() { + var command = new CommandFactory().aktenzeichenChange(AKTENZEICHEN_1); + givenHistorieServiceReturnsCommands(List.of(command)); + + var history = service.createVorgangChangeHistory(vorgangWithEingang).getAktenzeichenChangeHistory(); + + assertThat(history.get(0).getOrder()).isNotNull().isEqualTo(CommandOrder.SET_AKTENZEICHEN); + } + } + + @Nested + class TestAssignedUserChangeHistory { + + @Test + void shouldSetBeforeAndAfterValues() { + givenHistorieServiceReturnsCommands(createUserChangeCommands()); + givenUserServiceReturnsUserProfiles(); + + var history = service.createVorgangChangeHistory(vorgangWithEingang).getAssignedUserChangeHistory(); + + assertThat(history).hasSize(2); + checkBeforeAndAfterValues(history.get(0), "", USER_1_FULL_NAME); + checkBeforeAndAfterValues(history.get(1), USER_1_FULL_NAME, USER_2_FULL_NAME); + } + + @Test + void shouldSetOrder() { + var command = new CommandFactory().userChange(USER_1_ID.toString()); + givenHistorieServiceReturnsCommands(List.of(command)); + givenUserServiceReturnsUser1Profile(); + + var history = service.createVorgangChangeHistory(vorgangWithEingang).getAssignedUserChangeHistory(); + + assertThat(history.get(0).getOrder()).isNotNull().isEqualTo(CommandOrder.ASSIGN_USER); + } + } + + private void checkBeforeAndAfterValues(VorgangChange vorgangChange, String expectedBefore, String expectedAfter) { + assertThat(vorgangChange.getValueBeforeChange()).isEqualTo(expectedBefore); + assertThat(vorgangChange.getValueAfterChange()).isEqualTo(expectedAfter); + } + + private void givenHistorieServiceReturnsCommands(List<Command> commands) { + when(historieService.findFinishedCommands(VorgangHeaderTestFactory.ID)).thenReturn(commands.stream()); + } + + private void givenUserServiceReturnsUserProfiles() { + givenUserServiceReturnsUser1Profile(); + givenUserServiceReturnsUser2Profile(); + } + + private void givenUserServiceReturnsUser1Profile() { + when(userService.getById(USER_1_ID)).thenReturn(userProfile(USER_1_FIRST_NAME, USER_1_LAST_NAME)); + } + + private void givenUserServiceReturnsUser2Profile() { + when(userService.getById(USER_2_ID)).thenReturn(userProfile(USER_2_FIRST_NAME, USER_2_LAST_NAME)); + } + + private static UserProfile userProfile(String firstName, String lastName) { + return UserProfileTestFactory.createBuilder().firstName(firstName).lastName(lastName).build(); + } + + private List<Command> createStatusChangeCommandsUntilAbgeschlossen() { + var commandFactory = new CommandFactory(); + return List.of( + commandFactory.statusChange(CommandOrder.VORGANG_ANNEHMEN), + commandFactory.statusChange(CommandOrder.VORGANG_BEARBEITEN), + commandFactory.statusChange(CommandOrder.VORGANG_BESCHEIDEN), + commandFactory.statusChange(CommandOrder.VORGANG_ABSCHLIESSEN), + commandFactory.statusChange(CommandOrder.VORGANG_ZUM_LOESCHEN_MARKIEREN), + commandFactory.statusChange(CommandOrder.VORGANG_ABSCHLIESSEN), + commandFactory.statusChange(CommandOrder.LOESCH_ANFORDERUNG_ZURUECKNEHMEN) + ); + } + + private List<Command> createStatusChangeCommandsUntilVerworfen() { + var commandFactory = new CommandFactory(); + return List.of( + commandFactory.statusChange(CommandOrder.VORGANG_VERWERFEN), + commandFactory.statusChange(CommandOrder.VORGANG_ZUM_LOESCHEN_MARKIEREN), + commandFactory.statusChange(CommandOrder.VORGANG_VERWERFEN), + commandFactory.statusChange(CommandOrder.LOESCH_ANFORDERUNG_ZURUECKNEHMEN) + ); + } + + private List<Command> createStatusChangeCommandsUntilZurueckholen() { + var commandFactory = new CommandFactory(); + return List.of( + commandFactory.statusChange(CommandOrder.VORGANG_VERWERFEN), + commandFactory.statusChange(CommandOrder.VORGANG_ZURUECKHOLEN) + ); + } + + private List<Command> createAktenzeichenChangeCommands() { + var commandFactory = new CommandFactory(); + return List.of( + commandFactory.aktenzeichenChange(AKTENZEICHEN_1), + commandFactory.aktenzeichenChange(AKTENZEICHEN_2), + commandFactory.aktenzeichenChange(null) + ); + } + + private List<Command> createUserChangeCommands() { + var commandFactory = new CommandFactory(); + return List.of( + commandFactory.userChange(USER_1_ID.toString()), + commandFactory.userChange(USER_2_ID.toString()) + ); + } + + private List<Command> createMixedCommands() { + var commandFactory = new CommandFactory(); + return List.of( + commandFactory.statusChange(CommandOrder.VORGANG_ANNEHMEN), + commandFactory.aktenzeichenChange(AKTENZEICHEN_1), + commandFactory.userChange(USER_1_ID.toString()) + ); + } + + private static class CommandFactory { + + private ZonedDateTime dateTime = ZonedDateTime.now(ZoneId.of("UTC")); + + Command userChange(String assignedTo) { + return CommandTestFactory.createBuilder() + .order(CommandOrder.ASSIGN_USER) + .body(assignedTo != null ? Map.of(AssignedUserChangeHistoryBuilder.BODY_PROPERTY_ASSIGNED_USER, assignedTo) : Map.of()) + .createdByName(USER_1_FULL_NAME) + .finishedAt(getAndIncrementDateTime()) + .build(); + } + + Command aktenzeichenChange(String aktenzeichen) { + return CommandTestFactory.createBuilder() + .order(CommandOrder.SET_AKTENZEICHEN) + .body(aktenzeichen != null ? Map.of(AktenzeichenChangeHistoryBuilder.BODY_PROPERTY_AKTENZEICHEN, aktenzeichen) : Map.of()) + .createdByName(USER_1_FULL_NAME) + .finishedAt(getAndIncrementDateTime()) + .build(); + } + + Command statusChange(CommandOrder order) { + return CommandTestFactory.createBuilder() + .order(order) + .createdByName(USER_1_FULL_NAME) + .finishedAt(getAndIncrementDateTime()) + .build(); + } + + private ZonedDateTime getAndIncrementDateTime() { + var current = dateTime; + dateTime = dateTime.plusMinutes(1); + return current; + } + } +} diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/historie/VorgangChangeHistoryServiceTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/historie/VorgangChangeHistoryServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..4e1d9d4dfdc3639e69f7ea725d696aeaabf2d79d --- /dev/null +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/historie/VorgangChangeHistoryServiceTest.java @@ -0,0 +1,345 @@ +package de.ozgcloud.alfa.historie; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.UUID; +import java.util.function.Function; +import java.util.stream.Stream; + +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.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.Spy; + +import com.thedeanda.lorem.LoremIpsum; + +import de.ozgcloud.alfa.common.command.Command; +import de.ozgcloud.alfa.common.user.UserId; +import de.ozgcloud.alfa.common.user.UserProfile; +import de.ozgcloud.alfa.common.user.UserProfileTestFactory; +import de.ozgcloud.alfa.common.user.UserService; +import de.ozgcloud.alfa.vorgang.EingangTestFactory; +import de.ozgcloud.alfa.vorgang.VorgangHeaderTestFactory; +import de.ozgcloud.alfa.vorgang.VorgangWithEingang; +import de.ozgcloud.alfa.vorgang.VorgangWithEingangTestFactory; +import de.ozgcloud.alfa.vorgang.ZustaendigeStelleTestFactory; + +public class VorgangChangeHistoryServiceTest { + + private static final String ORGANISATIONSEINHEITEN_ID = UUID.randomUUID().toString(); + + private final VorgangWithEingang vorgang = VorgangWithEingangTestFactory.create(); + private final Command command0105 = ChangeHistoryBuilderTest.commandFinishedAt(LocalDateTime.of(2023, 5, 1, 12, 0)); + private final Command command0106 = ChangeHistoryBuilderTest.commandFinishedAt(LocalDateTime.of(2023, 6, 1, 12, 0)); + private final List<Command> commands = List.of(command0105, command0106); + + private final VorgangChange vorgangChange0105 = VorgangChangeTestFactory.createBuilder() + .valueBeforeChange(LoremIpsum.getInstance().getWords(2)) + .valueAfterChange(LoremIpsum.getInstance().getWords(2)) + .build(); + private final VorgangChange vorgangChange0106 = VorgangChangeTestFactory.createBuilder() + .valueBeforeChange(LoremIpsum.getInstance().getWords(2)) + .valueAfterChange(LoremIpsum.getInstance().getWords(2)) + .build(); + private final List<VorgangChange> vorgangChanges = List.of(vorgangChange0105, vorgangChange0106); + + @Mock + private UserService userService; + @Mock + private HistorieService historieService; + + @Spy + @InjectMocks + private VorgangChangeHistoryService service; + + @Nested + class TestCreateVorgangChangeHistory { + private final Stream<Command> commandStream = commands.stream(); + + @BeforeEach + void init() { + when(historieService.findFinishedCommands(VorgangHeaderTestFactory.ID)).thenReturn(commandStream); + } + + @Test + void shouldSetStatusChangeHistory() { + doReturn(vorgangChanges.stream()).when(service).createStatusChangeHistory(vorgang, commands); + + var history = callService(); + + assertThat(history.getStatusChangeHistory()).isEqualTo(vorgangChanges); + } + + @Test + void shouldSetAktenzeichenChangeHistory() { + doReturn(vorgangChanges.stream()).when(service).createAktenzeichenChangeHistory(vorgang, commands); + + var history = callService(); + + assertThat(history.getAktenzeichenChangeHistory()).isEqualTo(vorgangChanges); + } + + @Test + void shouldSetAssignedUserChangeHistory() { + doReturn(vorgangChanges.stream()).when(service).createAssignedUserChangeHistory(vorgang, commands); + + var history = callService(); + + assertThat(history.getAssignedUserChangeHistory()).isEqualTo(vorgangChanges); + } + + private VorgangChangeHistory callService() { + return service.createVorgangChangeHistory(vorgang); + } + } + + @Nested + class TestCreateStatusChangeHistory { + + private MockedStatic<StatusChangeHistoryBuilder> staticBuilder; + @Mock + private StatusChangeHistoryBuilder builder; + + @BeforeEach + void init() { + mockBuilderFactoryMethod(); + mockBuilderWiths(); + mockGetOrganisationseinheitenID(); + } + + private void mockBuilderFactoryMethod() { + staticBuilder = mockStatic(StatusChangeHistoryBuilder.class); + staticBuilder.when(StatusChangeHistoryBuilder::builder).thenReturn(builder); + } + + private void mockBuilderWiths() { + when(builder.withCommands(commands)).thenReturn(builder); + when(builder.withOrganisationseinheitenID(ORGANISATIONSEINHEITEN_ID)).thenReturn(builder); + } + + @AfterEach + void cleanup() { + staticBuilder.close(); + } + + @Test + void shouldSetCommands() { + callService(); + + verify(builder).withCommands(commands); + } + + @Test + void shouldSetOrganisationseinheitenID() { + callService(); + + verify(builder).withOrganisationseinheitenID(ORGANISATIONSEINHEITEN_ID); + } + + @Test + void shouldReturnBuiltStream() { + when(builder.build()).thenReturn(vorgangChanges.stream()); + + var changeHistory = callService(); + + assertThat(changeHistory).containsExactlyElementsOf(vorgangChanges); + } + + private Stream<VorgangChange> callService() { + return service.createStatusChangeHistory(vorgang, commands); + } + } + + @Nested + class TestCreateAktenzeichenChangeHistory { + + private MockedStatic<AktenzeichenChangeHistoryBuilder> staticBuilder; + @Mock + private AktenzeichenChangeHistoryBuilder builder; + + @BeforeEach + void init() { + mockBuilderFactoryMethod(); + mockBuilderWiths(); + mockGetOrganisationseinheitenID(); + } + + private void mockBuilderFactoryMethod() { + staticBuilder = mockStatic(AktenzeichenChangeHistoryBuilder.class); + staticBuilder.when(AktenzeichenChangeHistoryBuilder::builder).thenReturn(builder); + } + + private void mockBuilderWiths() { + when(builder.withCommands(commands)).thenReturn(builder); + when(builder.withOrganisationseinheitenID(ORGANISATIONSEINHEITEN_ID)).thenReturn(builder); + } + + @AfterEach + void cleanup() { + staticBuilder.close(); + } + + @Test + void shouldSetCommands() { + callService(); + + verify(builder).withCommands(commands); + } + + @Test + void shouldSetOrganisationseinheitenID() { + callService(); + + verify(builder).withOrganisationseinheitenID(ORGANISATIONSEINHEITEN_ID); + } + + @Test + void shouldReturnBuiltStream() { + when(builder.build()).thenReturn(vorgangChanges.stream()); + + var changeHistory = callService(); + + assertThat(changeHistory).containsExactlyElementsOf(vorgangChanges); + } + + private Stream<VorgangChange> callService() { + return service.createAktenzeichenChangeHistory(vorgang, commands); + } + } + + @Nested + class TestCreateAssignedUserChangeHistory { + + private MockedStatic<AssignedUserChangeHistoryBuilder> staticBuilder; + private MockedStatic<UserProfileCache> staticUserProfileCache; + + @Captor + private ArgumentCaptor<Function<UserId, UserProfile>> userProfileCacheArgCaptor; + + @Mock + private AssignedUserChangeHistoryBuilder builder; + @Mock + private UserProfileCache userProfileCache; + + @BeforeEach + void init() { + mockBuilderFactoryMethod(); + mockBuilderWiths(); + mockUserProfileCacheFactoryMethod(); + mockGetOrganisationseinheitenID(); + } + + private void mockBuilderFactoryMethod() { + staticBuilder = mockStatic(AssignedUserChangeHistoryBuilder.class); + staticBuilder.when(AssignedUserChangeHistoryBuilder::builder).thenReturn(builder); + } + + private void mockBuilderWiths() { + when(builder.withCommands(commands)).thenReturn(builder); + when(builder.withOrganisationseinheitenID(ORGANISATIONSEINHEITEN_ID)).thenReturn(builder); + when(builder.withUserProfileCache(userProfileCache)).thenReturn(builder); + } + + private void mockUserProfileCacheFactoryMethod() { + staticUserProfileCache = mockStatic(UserProfileCache.class); + staticUserProfileCache.when(() -> UserProfileCache.create(any())).thenReturn(userProfileCache); + } + + @AfterEach + void cleanup() { + staticBuilder.close(); + staticUserProfileCache.close(); + } + + @Test + void shouldSetCommands() { + callService(); + + verify(builder).withCommands(commands); + } + + @Test + void shouldSetOrganisationseinheitenID() { + callService(); + + verify(builder).withOrganisationseinheitenID(ORGANISATIONSEINHEITEN_ID); + } + + @Test + void shouldCreateUserProfileCache() { + callService(); + + staticUserProfileCache.verify(() -> UserProfileCache.create(userProfileCacheArgCaptor.capture())); + assertArgumentToUserProfileCacheCallsUserService(); + } + + private void assertArgumentToUserProfileCacheCallsUserService() { + userProfileCacheArgCaptor.getValue().apply(UserProfileTestFactory.ID); + + verify(userService).getById(UserProfileTestFactory.ID); + } + + @Test + void shouldSetUserProfileCache() { + callService(); + + verify(builder).withUserProfileCache(userProfileCache); + } + + @Test + void shouldReturnBuiltStream() { + when(builder.build()).thenReturn(vorgangChanges.stream()); + + var changeHistory = callService(); + + assertThat(changeHistory).containsExactlyElementsOf(vorgangChanges); + } + + private Stream<VorgangChange> callService() { + return service.createAssignedUserChangeHistory(vorgang, commands); + } + } + + @Nested + class TestGetOrganisationseinheitenID { + + @Test + void shouldReturnNullWhenEingangIsNull() { + var vorgang = VorgangWithEingangTestFactory.createBuilder().eingang(null).build(); + + var id = service.getOrganisationseinheitenID(vorgang); + + assertThat(id).isNull(); + } + + @Test + void shouldReturnNullIfZustaendigeStelleIsNull() { + var eingang = EingangTestFactory.createBuilder().zustaendigeStelle(null).build(); + var vorgang = VorgangWithEingangTestFactory.createBuilder().eingang(eingang).build(); + + var id = service.getOrganisationseinheitenID(vorgang); + + assertThat(id).isNull(); + } + + @Test + void shouldReturnOrganisationseinheitenID() { + var id = service.getOrganisationseinheitenID(vorgang); + + assertThat(id).isEqualTo(ZustaendigeStelleTestFactory.ORGANISATIONSEINHEITEN_ID); + } + } + + private void mockGetOrganisationseinheitenID() { + when(service.getOrganisationseinheitenID(vorgang)).thenReturn(ORGANISATIONSEINHEITEN_ID); + } +} diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/historie/VorgangChangeHistoryTestFactory.java b/alfa-service/src/test/java/de/ozgcloud/alfa/historie/VorgangChangeHistoryTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..3b4269eee0600e44d6ad3b56e71896ffd082c0da --- /dev/null +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/historie/VorgangChangeHistoryTestFactory.java @@ -0,0 +1,24 @@ +package de.ozgcloud.alfa.historie; + +import java.util.List; + +import de.ozgcloud.alfa.common.command.CommandOrder; + +class VorgangChangeHistoryTestFactory { + + public static final List<VorgangChange> STATUS_CHANGE_HISTORY = List.of(VorgangChangeTestFactory.createBuilder() + .order(CommandOrder.VORGANG_ANNEHMEN).build()); + public static final List<VorgangChange> AKTENZEICHEN_CHANGE_HISTORY = List.of(VorgangChangeTestFactory.createBuilder() + .order(CommandOrder.SET_AKTENZEICHEN).build()); + public static final List<VorgangChange> ASSIGNED_USER_CHANGE_HISTORY = List.of(VorgangChangeTestFactory.createBuilder() + .order(CommandOrder.ASSIGN_USER).build()); + + public static VorgangChangeHistory create() { + return VorgangChangeHistory.builder() + .statusChangeHistory(STATUS_CHANGE_HISTORY) + .aktenzeichenChangeHistory(AKTENZEICHEN_CHANGE_HISTORY) + .assignedUserChangeHistory(ASSIGNED_USER_CHANGE_HISTORY) + .build(); + } + +} diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/historie/VorgangChangeTestFactory.java b/alfa-service/src/test/java/de/ozgcloud/alfa/historie/VorgangChangeTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..f4c629a920970785272caa0e3901b1d1424fd336 --- /dev/null +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/historie/VorgangChangeTestFactory.java @@ -0,0 +1,30 @@ +package de.ozgcloud.alfa.historie; + +import java.time.ZonedDateTime; + +import de.ozgcloud.alfa.common.command.CommandOrder; + +class VorgangChangeTestFactory { + + public static final String VALUE_BEFORE_CHANGE = "Value before"; + public static final String VALUE_AFTER_CHANGE = "Value after"; + public static final String ORGANISATIONSEINHEITEN_ID = "ORGA1"; + public static final String CREATED_BY_NAME = "User1"; + public static final ZonedDateTime FINISHED_AT = ZonedDateTime.now(); + public static final CommandOrder ORDER = CommandOrder.SET_AKTENZEICHEN; + + public static VorgangChange create() { + return createBuilder().build(); + } + + public static VorgangChange.VorgangChangeBuilder createBuilder() { + return VorgangChange.builder() + .valueBeforeChange(VALUE_BEFORE_CHANGE) + .valueAfterChange(VALUE_AFTER_CHANGE) + .authorFullName(CREATED_BY_NAME) + .finishedAt(FINISHED_AT) + .order(ORDER) + .organisationseinheitenID(ORGANISATIONSEINHEITEN_ID); + } + +} diff --git a/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/ExportService.java b/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/ExportService.java index 95015618af8cf272e6a03120e4890a5304adf5cd..215e744ec785632a082e875708dca983e90730a9 100644 --- a/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/ExportService.java +++ b/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/ExportService.java @@ -18,6 +18,7 @@ import org.springframework.stereotype.Service; import de.ozgcloud.alfa.common.ExportFilenameGenerator; import de.ozgcloud.alfa.common.file.OzgFile; import de.ozgcloud.alfa.file.ExportFileService; +import de.ozgcloud.alfa.historie.ExportHistorieService; import de.ozgcloud.alfa.kommentar.ExportKommentarService; import de.ozgcloud.alfa.vorgang.Eingang; import de.ozgcloud.alfa.vorgang.EingangHeader; @@ -39,6 +40,7 @@ class ExportService { private final ExportFileService exportFileService; private final ExportVorgangService exportVorgangService; + private final ExportHistorieService exportHistorieService; private final ExportKommentarService exportKommentarService; public void writeExport(String vorgangId, String filenameId, OutputStream out) { @@ -58,6 +60,7 @@ class ExportService { .withAktenzeichen(exportVorgangService.createAkteType(vorgang)) .withRepresentations(exportFileService.createDokumentTypes(representations, formEngineName).toList()) .withAttachments(exportFileService.createDokumentTypes(attachments, formEngineName).toList()) + .withHistorie(exportHistorieService.createHistorienProtokollInformationTypes(vorgang).toList()) .withKommentare(exportKommentarService.createDokumentTypes(vorgang).toList()) .build(); var exportFiles = Stream.concat(representations.stream(), attachments.stream()).toList(); diff --git a/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/XdomeaNachrichtBuilder.java b/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/XdomeaNachrichtBuilder.java index 1f0e6db45378dda2b1e8a2d07d99a071f03fb821..e7752d7a65b88c8b20350d4429b7e1f6beaeb24f 100644 --- a/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/XdomeaNachrichtBuilder.java +++ b/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/XdomeaNachrichtBuilder.java @@ -7,6 +7,7 @@ import de.xoev.xdomea.AbgabeAbgabe0401; import de.xoev.xdomea.AbgabeAbgabe0401.Schriftgutobjekt; import de.xoev.xdomea.AkteType; import de.xoev.xdomea.DokumentType; +import de.xoev.xdomea.HistorienProtokollInformationType; import de.xoev.xdomea.NkAbgabeType; import de.xoev.xdomea.VorgangType; import lombok.AccessLevel; @@ -17,10 +18,11 @@ class XdomeaNachrichtBuilder { private VorgangType vorgang; private NkAbgabeType kopf; + private AkteType aktenzeichen; private List<DokumentType> representations = Collections.emptyList(); private List<DokumentType> attachments = Collections.emptyList(); private List<DokumentType> kommentare = Collections.emptyList(); - private AkteType aktenzeichen; + private List<HistorienProtokollInformationType> historie = Collections.emptyList(); public static XdomeaNachrichtBuilder builder() { return new XdomeaNachrichtBuilder(); @@ -51,18 +53,23 @@ class XdomeaNachrichtBuilder { return this; } + public XdomeaNachrichtBuilder withHistorie(List<HistorienProtokollInformationType> historie) { + this.historie = historie; + return this; + } + public XdomeaNachrichtBuilder withKommentare(List<DokumentType> kommentare) { this.kommentare = kommentare; return this; } public AbgabeAbgabe0401 build() { - var schriftgutobjekt = createSchriftgutobjekt(); addVorgangDokumente(); + addVorgangChangeHistory(); var abgabeType = new AbgabeAbgabe0401(); abgabeType.setKopf(kopf); - abgabeType.getSchriftgutobjekt().add(schriftgutobjekt); + abgabeType.getSchriftgutobjekt().add(createSchriftgutobjekt()); return abgabeType; } @@ -79,4 +86,7 @@ class XdomeaNachrichtBuilder { kommentare.forEach(vorgang.getDokument()::add); } + void addVorgangChangeHistory() { + historie.forEach(vorgang.getHistorienProtokollInformation()::add); + } } diff --git a/alfa-xdomea/src/main/java/de/ozgcloud/alfa/historie/ExportHistorieService.java b/alfa-xdomea/src/main/java/de/ozgcloud/alfa/historie/ExportHistorieService.java index 547d58ab0e021d411c1c9e16af3db8096906febe..c2b699793e93bcdeac8a56f4e88430bd31890bca 100644 --- a/alfa-xdomea/src/main/java/de/ozgcloud/alfa/historie/ExportHistorieService.java +++ b/alfa-xdomea/src/main/java/de/ozgcloud/alfa/historie/ExportHistorieService.java @@ -1,10 +1,57 @@ package de.ozgcloud.alfa.historie; +import java.util.function.Function; +import java.util.stream.Stream; + import org.springframework.stereotype.Service; +import de.ozgcloud.alfa.common.DateConverter; +import de.ozgcloud.alfa.common.command.CommandOrder; +import de.ozgcloud.alfa.vorgang.VorgangWithEingang; +import de.xoev.xdomea.HistorienProtokollInformationType; import lombok.RequiredArgsConstructor; @RequiredArgsConstructor @Service public class ExportHistorieService { + + private final VorgangChangeHistoryService vorgangChangeHistoryService; + + public Stream<HistorienProtokollInformationType> createHistorienProtokollInformationTypes(VorgangWithEingang vorgang) { + var history = vorgangChangeHistoryService.createVorgangChangeHistory(vorgang); + return Stream.of( + history.getStatusChangeHistory().stream().map(this::createHistorienProtokollInformationType), + history.getAktenzeichenChangeHistory().stream().map(this::createHistorienProtokollInformationType), + history.getAssignedUserChangeHistory().stream().map(this::createHistorienProtokollInformationType)) + .flatMap(Function.identity()); + } + + HistorienProtokollInformationType createHistorienProtokollInformationType(VorgangChange vorgangChange) { + var historienProtokollInformationType = new HistorienProtokollInformationType(); + historienProtokollInformationType.setMetadatumAlterWert(createValueBeforeChange(vorgangChange)); + historienProtokollInformationType.setMetadatumNeuerWert(createValueAfterChange(vorgangChange)); + historienProtokollInformationType.setAkteur(createAkteur(vorgangChange)); + historienProtokollInformationType.setDatumUhrzeit(DateConverter.toXmlGregorianCalendar(vorgangChange.getFinishedAt())); + historienProtokollInformationType.setAktion(vorgangChange.getOrder().name()); + return historienProtokollInformationType; + } + + String createAkteur(VorgangChange vorgangChange) { + return vorgangChange.getAuthorFullName() + "; " + vorgangChange.getOrganisationseinheitenID(); + } + + String createValueBeforeChange(VorgangChange vorgangChange) { + if (vorgangChange.getOrder() == CommandOrder.ASSIGN_USER) { + return vorgangChange.getValueBeforeChange() + "; " + vorgangChange.getOrganisationseinheitenID(); + } + return vorgangChange.getValueBeforeChange(); + } + + String createValueAfterChange(VorgangChange vorgangChange) { + if (vorgangChange.getOrder() == CommandOrder.ASSIGN_USER) { + return vorgangChange.getValueAfterChange() + "; " + vorgangChange.getOrganisationseinheitenID(); + } + return vorgangChange.getValueAfterChange(); + } + } diff --git a/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/ExportServiceITCase.java b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/ExportServiceITCase.java index 5707c63e0fcb2168c4b5792be7ec6de3c40482ad..2f597713f7b883e44e032aeba21470f99f79dc04 100644 --- a/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/ExportServiceITCase.java +++ b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/ExportServiceITCase.java @@ -21,6 +21,7 @@ import de.ozgcloud.alfa.common.binaryfile.FileId; import de.ozgcloud.alfa.common.file.OzgFile; import de.ozgcloud.alfa.common.file.OzgFileTestFactory; import de.ozgcloud.alfa.file.ExportFileService; +import de.ozgcloud.alfa.historie.ExportHistorieService; import de.ozgcloud.alfa.kommentar.ExportKommentarService; import de.ozgcloud.alfa.vorgang.ExportVorgangService; import de.ozgcloud.alfa.vorgang.VorgangHeaderTestFactory; @@ -36,6 +37,8 @@ class ExportServiceITCase { @SpyBean private ExportVorgangService exportVorgangService; @MockBean + private ExportHistorieService exportHistorieService; + @MockBean private ExportKommentarService exportKommentarService; @Autowired private ExportService exportService; @@ -51,6 +54,7 @@ class ExportServiceITCase { doReturn(Stream.of(createOzgFile())).when(exportFileService).getRepresentations(vorgang); doReturn(Stream.of(createOzgFile())).when(exportFileService).getAttachments(vorgang); doNothing().when(exportFileService).writeOzgFile(any(), any()); + when(exportHistorieService.createHistorienProtokollInformationTypes(vorgang)).thenReturn(Stream.empty()); when(exportKommentarService.createDokumentTypes(vorgang)).thenReturn(Stream.empty()); } diff --git a/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/ExportServiceTest.java b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/ExportServiceTest.java index ff7fb7bdd34403759f8ca946d467d3b59ba1654e..8a2abcc4ee045bc64c47870ad5bbab1d23365ca1 100644 --- a/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/ExportServiceTest.java +++ b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/ExportServiceTest.java @@ -31,6 +31,7 @@ import de.ozgcloud.alfa.common.TestUtils; import de.ozgcloud.alfa.common.file.OzgFile; import de.ozgcloud.alfa.common.file.OzgFileTestFactory; import de.ozgcloud.alfa.file.ExportFileService; +import de.ozgcloud.alfa.historie.ExportHistorieService; import de.ozgcloud.alfa.kommentar.ExportKommentarService; import de.ozgcloud.alfa.vorgang.EingangHeaderTestFactory; import de.ozgcloud.alfa.vorgang.EingangTestFactory; @@ -43,6 +44,7 @@ import de.ozgcloud.common.errorhandling.TechnicalException; import de.xoev.xdomea.AbgabeAbgabe0401; import de.xoev.xdomea.AkteType; import de.xoev.xdomea.DokumentType; +import de.xoev.xdomea.HistorienProtokollInformationType; import de.xoev.xdomea.NkAbgabeType; import de.xoev.xdomea.VorgangType; import lombok.SneakyThrows; @@ -61,6 +63,8 @@ class ExportServiceTest { @Mock private ExportVorgangService exportVorgangService; @Mock + private ExportHistorieService exportHistorieService; + @Mock private ExportKommentarService exportKommentarService; @DisplayName("Write exportToXdomea") @@ -123,6 +127,8 @@ class ExportServiceTest { private final List<OzgFile> attachments = List.of(OzgFileTestFactory.create()); private final List<DokumentType> representationsDokumentTypes = List.of(DokumentTypeTestFactory.create()); private final List<DokumentType> attachmentsDokumentTypes = List.of(DokumentTypeTestFactory.create()); + private final List<HistorienProtokollInformationType> historienProtokollInformationTypes = List.of( + HistorienProtokollInformationTypeTestFactory.create()); private final List<DokumentType> kommentareDokumentTypes = List.of(DokumentTypeTestFactory.create()); @Mock @@ -133,6 +139,7 @@ class ExportServiceTest { setUpVorgangService(); setUpXdomeaNachrichtBuilder(); setUpExportFileService(); + setUpExportHistorieService(); doReturn(FILE_NAME).when(service).buildXmlFilename(FILENAME_ID); doReturn(EingangHeaderTestFactory.FORM_ENGINE_NAME).when(service).getFormEngineName(vorgang); @@ -153,6 +160,7 @@ class ExportServiceTest { when(xdomeaNachrichtBuilder.withAktenzeichen(akteType)).thenReturn(xdomeaNachrichtBuilder); when(xdomeaNachrichtBuilder.withRepresentations(representationsDokumentTypes)).thenReturn(xdomeaNachrichtBuilder); when(xdomeaNachrichtBuilder.withAttachments(attachmentsDokumentTypes)).thenReturn(xdomeaNachrichtBuilder); + when(xdomeaNachrichtBuilder.withHistorie(historienProtokollInformationTypes)).thenReturn(xdomeaNachrichtBuilder); when(xdomeaNachrichtBuilder.withKommentare(kommentareDokumentTypes)).thenReturn(xdomeaNachrichtBuilder); xdomeaNachrichtBuilderMockedStatic.when(XdomeaNachrichtBuilder::builder).thenReturn(xdomeaNachrichtBuilder); when(xdomeaNachrichtBuilder.build()).thenReturn(abgabe); @@ -170,6 +178,11 @@ class ExportServiceTest { mockStreamToList(kommentareDokumentTypes, stream -> when(exportKommentarService.createDokumentTypes(vorgang)).thenReturn(stream)); } + private void setUpExportHistorieService() { + mockStreamToList(historienProtokollInformationTypes, + stream -> when(exportHistorieService.createHistorienProtokollInformationTypes(vorgang)).thenReturn(stream)); + } + @AfterEach void tearDown() { xdomeaNachrichtBuilderMockedStatic.close(); @@ -273,6 +286,13 @@ class ExportServiceTest { verify(xdomeaNachrichtBuilder).withAktenzeichen(akteType); } + @Test + void shouldSetHistorie() { + callService(); + + verify(xdomeaNachrichtBuilder).withHistorie(historienProtokollInformationTypes); + } + @Test void shouldCreateAbgabe() { callService(); diff --git a/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/HistorienProtokollInformationTypeTestFactory.java b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/HistorienProtokollInformationTypeTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..ddf72dde4fcb9a47ddd3e1275794826552a6e2ac --- /dev/null +++ b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/HistorienProtokollInformationTypeTestFactory.java @@ -0,0 +1,11 @@ +package de.ozgcloud.alfa.export; + +import de.xoev.xdomea.HistorienProtokollInformationType; + +public class HistorienProtokollInformationTypeTestFactory { + + public static HistorienProtokollInformationType create() { + return new HistorienProtokollInformationType(); + } + +} diff --git a/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/XdomeaNachrichtBuilderTest.java b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/XdomeaNachrichtBuilderTest.java index 9a71c4d90605dc701a3d044fcaa954e7022abefb..0a0b7be258bfe4ff9810f2bfc9b10bc8b6158717 100644 --- a/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/XdomeaNachrichtBuilderTest.java +++ b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/XdomeaNachrichtBuilderTest.java @@ -8,6 +8,7 @@ import org.junit.jupiter.api.Test; import de.ozgcloud.alfa.vorgang.VorgangTypeTestFactory; import de.xoev.xdomea.DokumentType; +import de.xoev.xdomea.HistorienProtokollInformationType; import de.xoev.xdomea.NkAbgabeType; import de.xoev.xdomea.VorgangType; @@ -17,6 +18,8 @@ class XdomeaNachrichtBuilderTest { private final VorgangType vorgangType = VorgangTypeTestFactory.create(); private final List<DokumentType> representations = List.of(DokumentTypeTestFactory.create(), DokumentTypeTestFactory.create()); private final List<DokumentType> attachments = List.of(DokumentTypeTestFactory.create(), DokumentTypeTestFactory.create()); + private final List<HistorienProtokollInformationType> historie = List.of(HistorienProtokollInformationTypeTestFactory.create(), + HistorienProtokollInformationTypeTestFactory.create()); private final List<DokumentType> kommentare = List.of(DokumentTypeTestFactory.create(), DokumentTypeTestFactory.create()); private final XdomeaNachrichtBuilder builder = XdomeaNachrichtBuilder.builder().withVorgang(vorgangType); @@ -55,6 +58,20 @@ class XdomeaNachrichtBuilderTest { assertThat(abgabeType.getSchriftgutobjekt().get(0).getVorgang().getDokument()).isEqualTo(attachments); } + @Test + void shouldAddHistorie() { + var abgabeType = builder.withHistorie(historie).build(); + + assertThat(abgabeType.getSchriftgutobjekt().get(0).getVorgang().getHistorienProtokollInformation()).isEqualTo(historie); + } + + @Test + void shouldNotAddHistorie() { + var abgabeType = builder.build(); + + assertThat(abgabeType.getSchriftgutobjekt().get(0).getVorgang().getHistorienProtokollInformation()).isEmpty(); + } + @Test void shouldAddKommentare() { var abgabeType = builder.withKommentare(kommentare).build(); diff --git a/alfa-xdomea/src/test/java/de/ozgcloud/alfa/historie/ExportHistorieServiceTest.java b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/historie/ExportHistorieServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..ca25dd2f4d70211b4102c874de51016340564073 --- /dev/null +++ b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/historie/ExportHistorieServiceTest.java @@ -0,0 +1,227 @@ +package de.ozgcloud.alfa.historie; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +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.EnumSource; +import org.junit.jupiter.params.provider.EnumSource.Mode; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; + +import de.ozgcloud.alfa.common.DateConverter; +import de.ozgcloud.alfa.common.command.CommandOrder; +import de.ozgcloud.alfa.export.HistorienProtokollInformationTypeTestFactory; +import de.ozgcloud.alfa.vorgang.VorgangWithEingang; +import de.ozgcloud.alfa.vorgang.VorgangWithEingangTestFactory; +import de.xoev.xdomea.HistorienProtokollInformationType; + +class ExportHistorieServiceTest { + + @Spy + @InjectMocks + private ExportHistorieService service; + + @Mock + private VorgangChangeHistoryService vorgangChangeHistoryService; + + @Nested + class TestCreateHistorienProtokollInformationTypes { + + private final VorgangWithEingang vorgang = VorgangWithEingangTestFactory.create(); + private final VorgangChangeHistory history = VorgangChangeHistoryTestFactory.create(); + private final HistorienProtokollInformationType statusChangeHistorienProtokollInformationType = HistorienProtokollInformationTypeTestFactory.create(); + private final HistorienProtokollInformationType aktenzeichenChangeHistorienProtokollInformationType = HistorienProtokollInformationTypeTestFactory.create(); + private final HistorienProtokollInformationType assignUserChangeHistorienProtokollInformationType = HistorienProtokollInformationTypeTestFactory.create(); + + @BeforeEach + void setUp() { + when(vorgangChangeHistoryService.createVorgangChangeHistory(vorgang)).thenReturn(history); + doReturn(statusChangeHistorienProtokollInformationType).when(service) + .createHistorienProtokollInformationType(VorgangChangeHistoryTestFactory.STATUS_CHANGE_HISTORY.get(0)); + doReturn(aktenzeichenChangeHistorienProtokollInformationType).when(service) + .createHistorienProtokollInformationType(VorgangChangeHistoryTestFactory.AKTENZEICHEN_CHANGE_HISTORY.get(0)); + doReturn(assignUserChangeHistorienProtokollInformationType).when(service) + .createHistorienProtokollInformationType(VorgangChangeHistoryTestFactory.ASSIGNED_USER_CHANGE_HISTORY.get(0)); + } + + @Test + void shouldGetVorgangChangeHistory() { + service.createHistorienProtokollInformationTypes(vorgang).toList(); + + verify(vorgangChangeHistoryService).createVorgangChangeHistory(vorgang); + } + + @Test + void shouldCreateStatusChangeHistory() { + service.createHistorienProtokollInformationTypes(vorgang).toList(); + + verify(service).createHistorienProtokollInformationType(VorgangChangeHistoryTestFactory.STATUS_CHANGE_HISTORY.get(0)); + } + + @Test + void shouldReturnStatusChangeHistorienProtokollInformationType() { + var historienProtokollInformationTypes = service.createHistorienProtokollInformationTypes(vorgang); + + assertThat(historienProtokollInformationTypes).contains(statusChangeHistorienProtokollInformationType); + } + + @Test + void shouldCreateAktenzeichenChangeHistory() { + service.createHistorienProtokollInformationTypes(vorgang).toList(); + + verify(service).createHistorienProtokollInformationType(VorgangChangeHistoryTestFactory.AKTENZEICHEN_CHANGE_HISTORY.get(0)); + } + + @Test + void shouldReturnAktenzeichenHistorienProtokollInformationType() { + var historienProtokollInformationTypes = service.createHistorienProtokollInformationTypes(vorgang); + + assertThat(historienProtokollInformationTypes).contains(aktenzeichenChangeHistorienProtokollInformationType); + } + + @Test + void shouldCreateAssignUserChangeHistory() { + service.createHistorienProtokollInformationTypes(vorgang).toList(); + + verify(service).createHistorienProtokollInformationType(VorgangChangeHistoryTestFactory.ASSIGNED_USER_CHANGE_HISTORY.get(0)); + } + + @Test + void shouldReturnAssignUserHistorienProtokollInformationType() { + var historienProtokollInformationTypes = service.createHistorienProtokollInformationTypes(vorgang); + + assertThat(historienProtokollInformationTypes).contains(assignUserChangeHistorienProtokollInformationType); + } + } + + @Nested + class TestCreateHistorienProtokollInformationType { + + private static final String CREATED_VALUE_BEFORE_CHANGE = + VorgangChangeTestFactory.VALUE_BEFORE_CHANGE + VorgangChangeTestFactory.ORGANISATIONSEINHEITEN_ID; + private static final String CREATED_VALUE_AFTER_CHANGE = + VorgangChangeTestFactory.VALUE_AFTER_CHANGE + VorgangChangeTestFactory.ORGANISATIONSEINHEITEN_ID; + private final VorgangChange vorgangChange = VorgangChangeTestFactory.create(); + + @BeforeEach + void setUp() { + doReturn(VorgangChangeTestFactory.CREATED_BY_NAME).when(service).createAkteur(vorgangChange); + doReturn(CREATED_VALUE_BEFORE_CHANGE).when(service).createValueBeforeChange(vorgangChange); + doReturn(CREATED_VALUE_AFTER_CHANGE).when(service).createValueAfterChange(vorgangChange); + } + + @Test + void shouldCreateValueBeforeChange() { + service.createHistorienProtokollInformationType(vorgangChange); + + verify(service).createValueBeforeChange(vorgangChange); + } + + @Test + void shouldHaveAlterWert() { + var created = service.createHistorienProtokollInformationType(vorgangChange); + + assertThat(created.getMetadatumAlterWert()).isEqualTo(CREATED_VALUE_BEFORE_CHANGE); + } + + @Test + void shouldCreateValueAfterChange() { + service.createHistorienProtokollInformationType(vorgangChange); + + verify(service).createValueAfterChange(vorgangChange); + } + + @Test + void shouldHaveNeuerWert() { + var created = service.createHistorienProtokollInformationType(vorgangChange); + + assertThat(created.getMetadatumNeuerWert()).isEqualTo(CREATED_VALUE_AFTER_CHANGE); + } + + @Test + void shouldHaveAkteur() { + var created = service.createHistorienProtokollInformationType(vorgangChange); + + assertThat(created.getAkteur()).isEqualTo(VorgangChangeTestFactory.CREATED_BY_NAME); + } + + @Test + void shouldHaveDatumUhrzeit() { + var created = service.createHistorienProtokollInformationType(vorgangChange); + + assertThat(created.getDatumUhrzeit()).isEqualTo(DateConverter.toXmlGregorianCalendar(VorgangChangeTestFactory.FINISHED_AT)); + } + + @Test + void shouldHaveAktion() { + var created = service.createHistorienProtokollInformationType(vorgangChange); + + assertThat(created.getAktion()).isEqualTo(VorgangChangeTestFactory.ORDER.name()); + } + + @Test + void shouldCreateAkteur() { + service.createHistorienProtokollInformationType(vorgangChange); + + verify(service).createAkteur(vorgangChange); + } + } + + @Nested + class TestCreateAkteur { + + @Test + void shouldReturnAkteur() { + var akteur = service.createAkteur(VorgangChangeTestFactory.create()); + + assertThat(akteur).isEqualTo( + String.format("%s; %s", VorgangChangeTestFactory.CREATED_BY_NAME, VorgangChangeTestFactory.ORGANISATIONSEINHEITEN_ID)); + } + } + + @Nested + class TestCreateValueBeforeChange { + + @ParameterizedTest + @EnumSource(mode = Mode.EXCLUDE, names = "ASSIGN_USER") + void shouldReturnValueForOrder(CommandOrder order) { + var value = service.createValueBeforeChange(VorgangChangeTestFactory.createBuilder().order(order).build()); + + assertThat(value).isEqualTo(VorgangChangeTestFactory.VALUE_BEFORE_CHANGE); + } + + @Test + void shouldReturnValueForAssignUserOrder() { + var value = service.createValueBeforeChange(VorgangChangeTestFactory.createBuilder().order(CommandOrder.ASSIGN_USER).build()); + + assertThat(value).isEqualTo( + String.format("%s; %s", VorgangChangeTestFactory.VALUE_BEFORE_CHANGE, VorgangChangeTestFactory.ORGANISATIONSEINHEITEN_ID)); + } + } + + @Nested + class TestCreateValueAfterChange { + + @ParameterizedTest + @EnumSource(mode = Mode.EXCLUDE, names = "ASSIGN_USER") + void shouldReturnValueForOrder(CommandOrder order) { + var value = service.createValueAfterChange(VorgangChangeTestFactory.createBuilder().order(order).build()); + + assertThat(value).isEqualTo(VorgangChangeTestFactory.VALUE_AFTER_CHANGE); + } + + @Test + void shouldReturnValueForAssignUserOrder() { + var value = service.createValueAfterChange(VorgangChangeTestFactory.createBuilder().order(CommandOrder.ASSIGN_USER).build()); + + assertThat(value).isEqualTo( + String.format("%s; %s", VorgangChangeTestFactory.VALUE_AFTER_CHANGE, VorgangChangeTestFactory.ORGANISATIONSEINHEITEN_ID)); + } + } + +} \ No newline at end of file