diff --git a/scripts/smocker/add-mock.sh b/scripts/smocker/add-mock.sh new file mode 100755 index 0000000000000000000000000000000000000000..b62b5fb57a515d8dc9fb6515081a92fd582cd3e8 --- /dev/null +++ b/scripts/smocker/add-mock.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +YAML_MOCK_FILE=${1:-receive-one.yaml} +JSON_BODY=$(yq -j . < "$YAML_MOCK_FILE") +curl -v --json "$JSON_BODY" localhost:8081/mocks +curl localhost:8081/mocks \ No newline at end of file diff --git a/scripts/smocker/forward-smocker.sh b/scripts/smocker/forward-smocker.sh new file mode 100755 index 0000000000000000000000000000000000000000..a7fb507645396af8086e959a7da6380d842939f1 --- /dev/null +++ b/scripts/smocker/forward-smocker.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +set -e + +NAMESPACE=${NAMESPACE:-by-ozg4094-dev} +SMOCKER_POD=$(kubectl get pods -n "$NAMESPACE" | grep -E ^smocker | cut -d' ' -f1) +if [[ -z "$SMOCKER_POD" ]] +then + echo "No smocker pod found in namespace: $NAMESPACE" + exit 1 +fi +echo "Forwarding smocker pod: $SMOCKER_POD in namespace: $NAMESPACE" +exec kubectl port-forward "$SMOCKER_POD" 8080:8080 8081:8081 \ No newline at end of file diff --git a/scripts/smocker/receive-one.yaml b/scripts/smocker/receive-one.yaml new file mode 100644 index 0000000000000000000000000000000000000000..309326a721164b59d92759b64e1c4529039a04e5 --- /dev/null +++ b/scripts/smocker/receive-one.yaml @@ -0,0 +1,58 @@ +- request: + method: GET + path: /MessageExchange/v1/Receive + context: + times: 1 + response: + status: 200 + headers: + Content-Type: application/json + body: > + { + "messages":[{ + "guid":"2cec3eac-66d2-4de0-bc6b-652b8e985ceb", + "attachments":[] + }] + } + +- request: + method: GET + path: /MessageExchange/v1/Receive/2cec3eac-66d2-4de0-bc6b-652b8e985ceb + context: + times: 1 + response: + status: 200 + headers: + Content-Type: application/json + body: > + { + "sequencenumber":"00000000-0000-0000-0000-000000000000", + "subject":"AW: Test Subject", + "body":"Hier eine eine Antwort ohne Anhang.", + "displayName":"Sandy Smockia", + "originSender":"technischer Absender", + "replyAction":"Replypossible", + "eidasLevel":"Low", + "isObligatory":false, + "isHtml":false, + "files":[], + "guid":"2cec3eac-66d2-4de0-bc6b-652b8e985ceb", + "messageBox":"00000000-0000-0000-0000-000000000000", + "senderDisplayName":null, + "recipientDisplayName":null, + "responseTime":"2023-07-17T14:59:32.4802955+02:00" + } + +- request: + method: DELETE + path: /MessageExchange/v1/Delete/2cec3eac-66d2-4de0-bc6b-652b8e985ceb + context: + times: 1 + response: + status: 200 + headers: + Content-Type: application/json + body: > + { + "messageId": "2cec3eac-66d2-4de0-bc6b-652b8e985ceb" + } diff --git a/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/OsiPostfachRemoteService.java b/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/OsiPostfachRemoteService.java index c03d4e681f51ff7a38fb4741ac21e92e187638e6..42c45db565ca8bb736d62827193e7d9c498bdbb4 100644 --- a/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/OsiPostfachRemoteService.java +++ b/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/OsiPostfachRemoteService.java @@ -44,7 +44,7 @@ public class OsiPostfachRemoteService implements PostfachRemoteService { try { postfachApiFacadeService.deleteMessage(messageId); } catch (RuntimeException e) { - throw new OsiPostfachException("Failed to delete messages", e); + throw new OsiPostfachException("Failed to delete message", e); } } diff --git a/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2ResponseMapper.java b/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2ResponseMapper.java index 0b20678b622bcfc0df1bc2a009554693870c277e..ef8d1ea7f2d5e9d28499e8f05badc783b0854fc5 100644 --- a/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2ResponseMapper.java +++ b/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2ResponseMapper.java @@ -1,9 +1,12 @@ package de.ozgcloud.nachrichten.postfach.osiv2.transfer; -import java.time.ZoneOffset; +import java.time.OffsetDateTime; +import java.time.ZonedDateTime; +import java.util.UUID; import org.mapstruct.Mapper; import org.mapstruct.Mapping; +import org.mapstruct.Named; import org.mapstruct.ReportingPolicy; import de.ozgcloud.nachrichten.postfach.PostfachAddress; @@ -15,44 +18,72 @@ import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.V1ReplyMessage; import lombok.Builder; import lombok.Getter; -@Mapper(unmappedTargetPolicy = ReportingPolicy.ERROR, imports = { ZoneOffset.class, Osi2HtmlDocument.class }) +@Mapper(unmappedTargetPolicy = ReportingPolicy.ERROR, imports = { Osi2HtmlDocument.class }) public interface Osi2ResponseMapper { String POSTFACH_ADDRESS_VERSION = "2.0"; int POSTFACH_ADDRESS_TYPE = 2; - @Mapping(target = "id", source = "guid") - @Mapping(target = "postfachAddress", expression = "java(buildPostfachAddressByPostfachId(message.getMessageBox().toString()))") - @Mapping(target = "messageId", ignore = true) - @Mapping(target = "createdAt", expression = "java(java.time.ZonedDateTime.now())") - @Mapping(target = "createdBy", source = "displayName") - @Mapping(target = "sentAt", expression = "java(message.getResponseTime().toZonedDateTime())") + @Mapping(target = "id", ignore = true) + @Mapping(target = "vorgangId", source = "sequencenumber") + @Mapping(target = "postfachAddress", source = "messageBox") + @Mapping(target = "messageId", source = "guid") + @Mapping(target = "referencedNachricht", ignore = true) + + @Mapping(target = "createdAt", source = "responseTime", qualifiedByName = "mapOffsetDateTimeToZoned") + @Mapping(target = "createdBy", ignore = true) + + @Mapping(target = "sentAt", ignore = true) @Mapping(target = "sentSuccessful", ignore = true) @Mapping(target = "messageCode", ignore = true) + @Mapping(target = "direction", constant = "IN") - @Mapping(target = "vorgangId", source = "sequencenumber") - @Mapping(target = "referencedNachricht", ignore = true) - @Mapping(target = "mailBody", expression = "java( message.getIsHtml() ? Osi2HtmlDocument.renderToPlainText(message.getBody()) : message.getBody() )") + + @Mapping(target = "subject", source = "subject") + @Mapping(target = "mailBody", source = ".", qualifiedByName = "mapMailBody") @Mapping(target = "replyOption", source = "replyAction") @Mapping(target = "attachments", ignore = true) PostfachNachricht toPostfachNachricht(V1ReplyMessage message); - default PostfachAddress buildPostfachAddressByPostfachId(String postfachId) { - return PostfachAddress.builder() - .type(POSTFACH_ADDRESS_TYPE) - .version(POSTFACH_ADDRESS_VERSION) - .identifier(StringBasedIdentifier.builder().mailboxId(postfachId).build()) - .serviceKontoType(OsiPostfachRemoteService.POSTFACH_TYPE_OSI) - .build(); + default String mapNullToEmpty(String value) { + return value == null ? "" : value; + } + + @Named("mapOffsetDateTimeToZoned") + default ZonedDateTime mapOffsetDateTimeToZoned(OffsetDateTime offsetDateTime) { + return offsetDateTime == null ? null : offsetDateTime.toZonedDateTime(); + } + + @Named("mapMailBody") + default String mapMailBody(V1ReplyMessage message) { + var body = mapNullToEmpty(message.getBody()); + return Boolean.TRUE.equals(message.getIsHtml()) + ? Osi2HtmlDocument.renderToPlainText(body) + : body; + } + + default PostfachAddress buildPostfachAddressByPostfachId(UUID messageBox) { + return messageBox == null + ? null + : PostfachAddress.builder() + .type(POSTFACH_ADDRESS_TYPE) + .version(POSTFACH_ADDRESS_VERSION) + .identifier(StringBasedIdentifier.builder() + .mailboxId(messageBox.toString()) + .build()) + .serviceKontoType(OsiPostfachRemoteService.POSTFACH_TYPE_OSI) + .build(); } default PostfachNachricht.ReplyOption mapReplyAction(V1ReplyBehavior replyOption) { - return switch (replyOption) { - case REPLYFORBIDDEN -> PostfachNachricht.ReplyOption.FORBIDDEN; - case REPLYPOSSIBLE -> PostfachNachricht.ReplyOption.POSSIBLE; - case REPLYMANDATORY -> PostfachNachricht.ReplyOption.MANDATORY; - }; + return replyOption == null + ? PostfachNachricht.ReplyOption.FORBIDDEN + : switch (replyOption) { + case REPLYFORBIDDEN -> PostfachNachricht.ReplyOption.FORBIDDEN; + case REPLYPOSSIBLE -> PostfachNachricht.ReplyOption.POSSIBLE; + case REPLYMANDATORY -> PostfachNachricht.ReplyOption.MANDATORY; + }; } @Builder diff --git a/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/PostfachApiFacadeService.java b/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/PostfachApiFacadeService.java index b4012fd71ce827623b2204ac56a62ab82ca530bf..e1446a7fe8bdeaf2c4eb7e39b66324650f135314 100644 --- a/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/PostfachApiFacadeService.java +++ b/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/PostfachApiFacadeService.java @@ -9,9 +9,11 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Service; import de.ozgcloud.nachrichten.postfach.PostfachNachricht; +import de.ozgcloud.nachrichten.postfach.osiv2.OsiPostfachException; import de.ozgcloud.nachrichten.postfach.osiv2.config.Osi2PostfachProperties; import de.ozgcloud.nachrichten.postfach.osiv2.gen.api.MessageExchangeApi; import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.MessageExchangeReceiveMessage; +import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.MessageExchangeReceiveMessagesResponse; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; @@ -25,7 +27,7 @@ public class PostfachApiFacadeService { private final Osi2RequestMapper osi2RequestMapper; private final Osi2ResponseMapper osi2ResponseMapper; - private static final int MAX_NUMBER_RECEIVED_MESSAGES = 100; + static final int MAX_NUMBER_RECEIVED_MESSAGES = 100; public void sendMessage(PostfachNachricht nachricht) { messageExchangeApi.sendMessage( @@ -35,12 +37,16 @@ public class PostfachApiFacadeService { } public Stream<PostfachNachricht> receiveMessages() { - var response = messageExchangeApi.receiveMessages(MAX_NUMBER_RECEIVED_MESSAGES, 0); - return Optional.ofNullable(response.getMessages()) + return Optional.ofNullable(receiveMessagesResponse().getMessages()) .stream() .flatMap(Collection::stream) .map(this::fetchMessageByGuid); } + + private MessageExchangeReceiveMessagesResponse receiveMessagesResponse() { + return Optional.ofNullable(messageExchangeApi.receiveMessages(MAX_NUMBER_RECEIVED_MESSAGES, 0)) + .orElseThrow(() -> new OsiPostfachException("Expect non empty response!", null)); + } PostfachNachricht fetchMessageByGuid(final MessageExchangeReceiveMessage message) { var messageReply = messageExchangeApi.getMessage(message.getGuid()); diff --git a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/OsiPostfachRemoteServiceRemoteITCase.java b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/OsiPostfachRemoteServiceRemoteITCase.java index be0f329f70b0c21b27b6fadf053334f948b37c15..fd5c11bc956b400822c0dd6064fa7bb4dcc7794d 100644 --- a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/OsiPostfachRemoteServiceRemoteITCase.java +++ b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/OsiPostfachRemoteServiceRemoteITCase.java @@ -4,10 +4,8 @@ import static org.assertj.core.api.Assertions.*; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.stream.Stream; import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; import org.springframework.beans.factory.annotation.Autowired; @@ -20,24 +18,16 @@ import de.ozgcloud.nachrichten.postfach.PostfachNachricht; import de.ozgcloud.nachrichten.postfach.osiv2.factory.DummyStringBasedIdentifier; import de.ozgcloud.nachrichten.postfach.osiv2.factory.PostfachAddressTestFactory; import de.ozgcloud.nachrichten.postfach.osiv2.factory.PostfachNachrichtTestFactory; +import lombok.SneakyThrows; @SpringBootTest(classes = TestApplication.class) @ActiveProfiles({ "stage", "itcase" }) @EnabledIfEnvironmentVariable(named = "SH_STAGE_CLIENT_SECRET", matches = ".+") -public class OsiPostfachRemoteServiceRemoteITCase { +class OsiPostfachRemoteServiceRemoteITCase { @Autowired private OsiPostfachRemoteService osiPostfachRemoteService; - private final PostfachNachricht nachricht = PostfachNachrichtTestFactory.createBuilder() - .replyOption(PostfachNachricht.ReplyOption.POSSIBLE) - .postfachAddress(PostfachAddressTestFactory.createBuilder() - .identifier(DummyStringBasedIdentifier.builder() - .mailboxId("49b5a7e2-5e60-4baf-8ccf-1f5b94b570f3") - .build()) - .build()) - .build(); - @DynamicPropertySource static void dynamicProperties(DynamicPropertyRegistry registry) { registry.add( @@ -62,40 +52,42 @@ public class OsiPostfachRemoteServiceRemoteITCase { throw new IllegalArgumentException("Proxy host and port not found in '%s'".formatted(text)); } - @DisplayName("send message") - @Nested - class TestSendMessage { - - @DisplayName("should not fail") - @Test - void shouldNotFail() { - assertThatCode(() -> osiPostfachRemoteService.sendMessage(nachricht)) - .doesNotThrowAnyException(); - } + private PostfachNachricht createNachricht() { + return PostfachNachrichtTestFactory.createBuilder() + .replyOption(PostfachNachricht.ReplyOption.POSSIBLE) + .postfachAddress(PostfachAddressTestFactory.createBuilder() + .identifier(DummyStringBasedIdentifier.builder() + .mailboxId("49b5a7e2-5e60-4baf-8ccf-1f5b94b570f3") + .build()) + .build()) + .build(); } - @DisplayName("receive all messages") - @Nested - class TestReceiveAllMessages { + @DisplayName("should not fail sending message") + @Test + @SneakyThrows + void shouldNotFailSendingMessage() { + var nachricht = createNachricht(); - @Test - void shouldReceiveAllMessage() { - Stream<PostfachNachricht> allMessages = osiPostfachRemoteService.getAllMessages(); + assertThatCode(() -> osiPostfachRemoteService.sendMessage(nachricht)) + .doesNotThrowAnyException(); + } - var messages = allMessages.toList(); + @DisplayName("should receive messages") + @Test + void shouldReceiveMessages() { + var messages = osiPostfachRemoteService.getAllMessages().toList(); - assertThat(messages).isNotEmpty(); - } + assertThat(messages) + .isNotEmpty() + .extracting(PostfachNachricht::getMessageId) + .doesNotContainNull(); } - @DisplayName("delete message") - @Nested - class TestDeleteMessageById { - - @Test - void shouldDeleteMessage() { - assertThatCode(() -> osiPostfachRemoteService.deleteMessage("5dd65c1e-bd41-4c3d-bf98-be769ca341dc")) - .doesNotThrowAnyException(); - } + @DisplayName("should delete message") + @Test + void shouldDeleteMessage() { + assertThatCode(() -> osiPostfachRemoteService.deleteMessage("2cec3eac-66d2-4de0-bc6b-652b8e985ceb")) + .doesNotThrowAnyException(); } } diff --git a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/factory/MessageExchangeReceiveMessagesResponseTestFactory.java b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/factory/MessageExchangeReceiveMessagesResponseTestFactory.java index 7fa0ae8e0f9b23e5d3f5d416f5eddbb47f291b63..a104cfc9d8ee0dc6d185b871a27103b7f9d55542 100644 --- a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/factory/MessageExchangeReceiveMessagesResponseTestFactory.java +++ b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/factory/MessageExchangeReceiveMessagesResponseTestFactory.java @@ -7,6 +7,9 @@ import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.MessageExchangeReceiveMe public class MessageExchangeReceiveMessagesResponseTestFactory { + public static final String MESSAGE_ID_1 = UUID.randomUUID().toString(); + public static final String MESSAGE_ID_2 = UUID.randomUUID().toString(); + public static MessageExchangeReceiveMessagesResponse create(String... uuids) { return new MessageExchangeReceiveMessagesResponse() .messages(Arrays.stream(uuids) diff --git a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/factory/PostfachNachrichtTestFactory.java b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/factory/PostfachNachrichtTestFactory.java index e24b966431f23e9de36aac29539323a0c456a25b..aaf929ea7d99c7b3262df9458295e8bc7c647e90 100644 --- a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/factory/PostfachNachrichtTestFactory.java +++ b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/factory/PostfachNachrichtTestFactory.java @@ -1,12 +1,15 @@ package de.ozgcloud.nachrichten.postfach.osiv2.factory; import java.time.ZonedDateTime; +import java.util.UUID; + +import com.thedeanda.lorem.LoremIpsum; import de.ozgcloud.nachrichten.postfach.PostfachNachricht; public class PostfachNachrichtTestFactory { - public static final String MAIL_BODY = "mail body"; - public static final String MAIL_SUBJECT = "mail subject"; + public static final String MAIL_BODY = "Hallo,\n" + LoremIpsum.getInstance().getParagraphs(1, 4); + public static final String MAIL_SUBJECT = "AW: " + LoremIpsum.getInstance().getTitle(2, 6); public static final String VORGANG_ID = "test-vorgang-id"; public static final String USER_ID = "test-user-id"; diff --git a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/factory/V1ReplyMessageTestFactory.java b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/factory/V1ReplyMessageTestFactory.java index 84fec5a318a83aafc58fe20142b84367db2aa30f..2bb4c78fa25a9d2a80f4b249c9b0e2b57b8dea74 100644 --- a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/factory/V1ReplyMessageTestFactory.java +++ b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/factory/V1ReplyMessageTestFactory.java @@ -1,6 +1,7 @@ package de.ozgcloud.nachrichten.postfach.osiv2.factory; import java.time.OffsetDateTime; +import java.time.ZonedDateTime; import java.util.UUID; import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.V1EidasLevel; @@ -9,37 +10,28 @@ import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.V1ReplyMessage; public class V1ReplyMessageTestFactory { - private static final String SEQUENCE_NUMMER = "OZG-Cloud-VorgangId"; - private static final String SUBJECT = "Das ist das Subject"; + public static final String SEQUENCE_NUMMER = "OZG-Cloud-VorgangId"; + public static final String SUBJECT = "Das ist das Subject"; public static final String HTML_REPLY_BODY = """ Das ist das Multiline&<b>a</b><br><br/> Body"""; public static final String REPLY_BODY = """ Das ist das Multiline&<b>a</b><br><br/> Body"""; - private static final String DISPLAY_NAME = "Das ist der Absender"; - private static final String ORIGIN_SENDER = "das ist der original Sender"; - private static final String REPLAY_ACTION = "Replypossible"; - private static final String EIDAS_LEVEL = "Low"; - private static final Boolean IS_OBLIGATORY = Boolean.FALSE; - private static final Boolean IS_HTML = Boolean.FALSE; - private static final String GUID = "123-guid-456"; - private static final String MESSAGE_BOX = "Mailbox-Id-Antwortender"; - private static final OffsetDateTime RESPONSE_TIME = OffsetDateTime.now(); + public static final String MESSAGE_ID = UUID.randomUUID().toString(); + public static final String MESSAGE_BOX_ID = UUID.randomUUID().toString(); + public static final ZonedDateTime RESPONSE_TIME = ZonedDateTime.now(); public static V1ReplyMessage create() { return new V1ReplyMessage() .sequencenumber(SEQUENCE_NUMMER) .subject(SUBJECT) - .body(REPLY_BODY) - .displayName(DISPLAY_NAME) - .originSender(ORIGIN_SENDER) - .replyAction(V1ReplyBehavior.fromValue(REPLAY_ACTION)) - .eidasLevel(V1EidasLevel.fromValue(EIDAS_LEVEL)) - .isObligatory(IS_OBLIGATORY) - .isHtml(IS_HTML) - .guid(UUID.nameUUIDFromBytes(GUID.getBytes())) - .messageBox(UUID.nameUUIDFromBytes(MESSAGE_BOX.getBytes())) - .responseTime(RESPONSE_TIME); + .replyAction(V1ReplyBehavior.REPLYPOSSIBLE) + .isObligatory(false) + .eidasLevel(V1EidasLevel.LOW) + .isHtml(false) + .guid(UUID.fromString(MESSAGE_ID)) + .messageBox(UUID.fromString(MESSAGE_BOX_ID)) + .responseTime(OffsetDateTime.of(RESPONSE_TIME.toLocalDateTime(), RESPONSE_TIME.getOffset())); } } diff --git a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2ResponseMapperTest.java b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2ResponseMapperTest.java index 1c417e577b9134818309cc5a0ecdc9b976d8423a..8806cc719991a35cc7891a72b853542473eed084 100644 --- a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2ResponseMapperTest.java +++ b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2ResponseMapperTest.java @@ -3,18 +3,21 @@ package de.ozgcloud.nachrichten.postfach.osiv2.transfer; import static de.ozgcloud.nachrichten.postfach.osiv2.factory.V1ReplyMessageTestFactory.*; import static org.assertj.core.api.Assertions.*; -import java.time.ZonedDateTime; -import java.time.temporal.ChronoUnit; -import java.util.UUID; +import java.util.stream.Stream; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.mapstruct.factory.Mappers; import org.mockito.InjectMocks; import de.ozgcloud.nachrichten.postfach.PostfachNachricht; import de.ozgcloud.nachrichten.postfach.osiv2.factory.V1ReplyMessageTestFactory; +import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.V1ReplyBehavior; import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.V1ReplyMessage; class Osi2ResponseMapperTest { @@ -26,86 +29,126 @@ class Osi2ResponseMapperTest { @DisplayName("map V1ReplyMessage to PostfachNachricht") @Nested class V1ReplyMessageToPostfachNachricht { + @Test - void shouldHaveId() { - var postfachNachricht = mapper.toPostfachNachricht(message); + void shouldMapVorgangId() { + var result = doMapping(); - assertThat(postfachNachricht.getId()).isEqualTo(UUID.nameUUIDFromBytes("123-guid-456".getBytes()).toString()); + assertThat(result.getVorgangId()).isEqualTo(SEQUENCE_NUMMER); } @Test - void shouldHaveVorgangId() { - var postfachNachricht = mapper.toPostfachNachricht(message); + void shouldMapPostfachAddress() { + var result = doMapping(); - assertThat(postfachNachricht.getVorgangId()).isEqualTo("OZG-Cloud-VorgangId"); + assertThat(result.getPostfachAddress().getIdentifier()) + .hasToString(MESSAGE_BOX_ID); } @Test - void shouldHavePostfachAddress() { - var postfachNachricht = mapper.toPostfachNachricht(message); + void shouldMapCreatedAt() { + var result = doMapping(); - assertThat(postfachNachricht.getPostfachAddress().getIdentifier().toString()) - .hasToString(UUID.nameUUIDFromBytes("Mailbox-Id-Antwortender".getBytes()).toString()); + assertThat(result.getCreatedAt()).isEqualTo(RESPONSE_TIME); } @Test - void shouldHaveCreatedAt() { - var postfachNachricht = mapper.toPostfachNachricht(message); + void shouldMapDirection() { + var result = doMapping(); - assertThat(postfachNachricht.getCreatedAt()).isNotNull().isCloseTo(ZonedDateTime.now(), within(5, ChronoUnit.SECONDS)); + assertThat(result.getDirection()).isEqualTo(PostfachNachricht.Direction.IN); } @Test - void shouldHaveCreatedBy() { - var postfachNachricht = mapper.toPostfachNachricht(message); + void shouldMapSubject() { + var result = doMapping(); - assertThat(postfachNachricht.getCreatedBy()).isEqualTo("Das ist der Absender"); + assertThat(result.getSubject()).isEqualTo(SUBJECT); } @Test - void shouldHaveSentAt() { - var postfachNachricht = mapper.toPostfachNachricht(message); + void shouldMapNullBodyToEmptyString() { + var result = doMapping(); - assertThat(postfachNachricht.getSentAt()).isNotNull().isCloseTo(ZonedDateTime.now(), within(5, ChronoUnit.SECONDS)); + assertThat(result.getMailBody()).isEmpty(); } + @DisplayName("should map modified HTML body if HTML message") @Test - void shouldHaveDirection() { - var postfachNachricht = mapper.toPostfachNachricht(message); + void shouldMapModifiedHtmlBodyIfHtmlMessage() { + var htmlMessage = V1ReplyMessageTestFactory.create() + .body(HTML_REPLY_BODY) + .isHtml(true); - assertThat(postfachNachricht.getDirection()).isEqualTo(PostfachNachricht.Direction.IN); + var result = mapper.toPostfachNachricht(htmlMessage); + + assertThat(result.getMailBody()).isEqualTo(REPLY_BODY); } - @Test - void shouldHaveSubject() { - var postfachNachricht = mapper.toPostfachNachricht(message); + @DisplayName("should map unmodified body if not HTML message") + @ParameterizedTest + @ValueSource(strings = { REPLY_BODY, HTML_REPLY_BODY }) + void shouldMapUnmodifiedBodyIfNotHtmlMessage(String body) { + var htmlMessage = V1ReplyMessageTestFactory.create() + .body(body) + .isHtml(false); + + var result = mapper.toPostfachNachricht(htmlMessage); + + assertThat(result.getMailBody()).isEqualTo(body); + } - assertThat(postfachNachricht.getSubject()).isEqualTo("Das ist das Subject"); + static Stream<Arguments> replyOptionValues() { + return Stream.of( + Arguments.of(V1ReplyBehavior.REPLYPOSSIBLE, PostfachNachricht.ReplyOption.POSSIBLE), + Arguments.of(V1ReplyBehavior.REPLYMANDATORY, PostfachNachricht.ReplyOption.MANDATORY), + Arguments.of(V1ReplyBehavior.REPLYFORBIDDEN, PostfachNachricht.ReplyOption.FORBIDDEN), + Arguments.of(null, PostfachNachricht.ReplyOption.FORBIDDEN) + ); } + @ParameterizedTest + @MethodSource("replyOptionValues") + void shouldMapReplyOption(V1ReplyBehavior replyAction, PostfachNachricht.ReplyOption expected) { + var replyActionMessage = V1ReplyMessageTestFactory.create() + .replyAction(replyAction) + .isHtml(false); + + var result = mapper.toPostfachNachricht(replyActionMessage); + + assertThat(result.getReplyOption()).isEqualTo(expected); + } + + @DisplayName("should map messageId") @Test - void shouldHaveBody() { - var postfachNachricht = mapper.toPostfachNachricht(message); + void shouldMapMessageId() { + var result = doMapping(); - assertThat(postfachNachricht.getMailBody()).isEqualTo(REPLY_BODY); + assertThat(result.getMessageId()).isEqualTo(MESSAGE_ID); } + @DisplayName("should not fail if all fields are null") @Test - void shouldMapHTMLBody() { - var postfachNachricht = mapper.toPostfachNachricht(message - .body(HTML_REPLY_BODY) - .isHtml(true)); + void shouldNotFailIfAllFieldsAreNull() { + var nullMessage = new V1ReplyMessage(); - assertThat(postfachNachricht.getMailBody()).isEqualTo(REPLY_BODY); + assertThatCode(() -> mapper.toPostfachNachricht(nullMessage)) + .doesNotThrowAnyException(); } + @DisplayName("should not fail if all fields are null and isHtml") @Test - void shouldHaveReplyOption() { - var postfachNachricht = mapper.toPostfachNachricht(message); + void shouldNotFailIfAllFieldsAreNullAndIsHtml() { + var nullMessage = new V1ReplyMessage() + .isHtml(true); + + assertThatCode(() -> mapper.toPostfachNachricht(nullMessage)) + .doesNotThrowAnyException(); + } - assertThat(postfachNachricht.getReplyOption()).isEqualTo(PostfachNachricht.ReplyOption.POSSIBLE); + private PostfachNachricht doMapping() { + return mapper.toPostfachNachricht(message); } - // TODO:prüfen das Attachments in der PostfachNachricht enthalten sind } } diff --git a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/PostfachApiFacadeServiceTest.java b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/PostfachApiFacadeServiceTest.java index c1abebba22f3e88935c0465e0bba99d6840a07e5..d2ceb286eaf9a6dfe46d5f79841fa0f63b08ea18 100644 --- a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/PostfachApiFacadeServiceTest.java +++ b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/PostfachApiFacadeServiceTest.java @@ -1,9 +1,12 @@ package de.ozgcloud.nachrichten.postfach.osiv2.transfer; +import static de.ozgcloud.nachrichten.postfach.osiv2.factory.MessageExchangeReceiveMessagesResponseTestFactory.*; import static de.ozgcloud.nachrichten.postfach.osiv2.factory.PostfachAddressTestFactory.*; +import static de.ozgcloud.nachrichten.postfach.osiv2.transfer.PostfachApiFacadeService.*; import static org.assertj.core.api.Assertions.*; import static org.mockito.Mockito.*; +import java.util.List; import java.util.UUID; import org.junit.jupiter.api.BeforeEach; @@ -12,8 +15,11 @@ 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.nachrichten.postfach.PostfachNachricht; +import de.ozgcloud.nachrichten.postfach.osiv2.OsiPostfachException; +import de.ozgcloud.nachrichten.postfach.osiv2.factory.MessageExchangeReceiveMessagesResponseTestFactory; import de.ozgcloud.nachrichten.postfach.osiv2.factory.PostfachNachrichtTestFactory; import de.ozgcloud.nachrichten.postfach.osiv2.gen.api.MessageExchangeApi; import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.MessageExchangeDeleteMessageResponse; @@ -26,6 +32,7 @@ import de.ozgcloud.nachrichten.postfach.osiv2.gen.model.V1ReplyMessage; class PostfachApiFacadeServiceTest { @InjectMocks + @Spy PostfachApiFacadeService postfachApiFacadeService; @Mock @@ -68,27 +75,85 @@ class PostfachApiFacadeServiceTest { @DisplayName("receive messages") @Nested - class TestReceiveMessage{ + class TestReceiveMessage { + + @DisplayName("with two pending messages") + @Nested + class TestWithTwoPendingMessages { + private final MessageExchangeReceiveMessagesResponse response = MessageExchangeReceiveMessagesResponseTestFactory.create( + MESSAGE_ID_1, MESSAGE_ID_2 + ); + + @BeforeEach + void mock() { + when(messageExchangeApi.receiveMessages(anyInt(), anyInt())).thenReturn(response); + doReturn(PostfachNachrichtTestFactory.createBuilder().messageId(MESSAGE_ID_1).build()) + .when(postfachApiFacadeService).fetchMessageByGuid(response.getMessages().get(0)); + doReturn(PostfachNachrichtTestFactory.createBuilder().messageId(MESSAGE_ID_2).build()) + .when(postfachApiFacadeService).fetchMessageByGuid(response.getMessages().get(1)); + } + + @DisplayName("should return") + @Test + void shouldReturn() { + var messages = receiveMessageList(); + + assertThat(messages) + .extracting(PostfachNachricht::getMessageId) + .containsExactly(MESSAGE_ID_1, MESSAGE_ID_2); + } + + @DisplayName("should call receiveMessages api method") + @Test + void shouldCallReceiveMessagesApiMethod() { + receiveMessageList(); + + verify(messageExchangeApi).receiveMessages(MAX_NUMBER_RECEIVED_MESSAGES, 0); + } - @Mock - MessageExchangeReceiveMessagesResponse messageExchangeReceiveMessagesResponse; + } + + @DisplayName("with no pending messages") + @Nested + class TestWithNoPendingMessages { + private final MessageExchangeReceiveMessagesResponse emptyResponse = MessageExchangeReceiveMessagesResponseTestFactory.create(); + + @BeforeEach + void mock() { + when(messageExchangeApi.receiveMessages(anyInt(), anyInt())).thenReturn(emptyResponse); + } + + @DisplayName("should return") + @Test + void shouldReturn() { + var messages = receiveMessageList(); + + assertThat(messages).isEmpty(); + } - @BeforeEach - void mock(){ - when(messageExchangeApi.receiveMessages(anyInt(),anyInt())).thenReturn(messageExchangeReceiveMessagesResponse); } - @Test - void responseShouldNotBeEmpty(){ - postfachApiFacadeService.receiveMessages(); + @DisplayName("with null response") + @Nested + class TestWithNullResponse { - verify(messageExchangeApi).receiveMessages(anyInt(), anyInt()); + @DisplayName("should throw") + @Test + void shouldThrow() { + assertThatThrownBy(TestReceiveMessage.this::receiveMessageList) + .isInstanceOf(OsiPostfachException.class); + } } + + private List<PostfachNachricht> receiveMessageList() { + return postfachApiFacadeService.receiveMessages().toList(); + } + } @DisplayName("fetch Message by guid") @Nested - class TestFetchMessageByGuid{ + class TestFetchMessageByGuid { @Mock V1ReplyMessage replyMessage; @@ -96,7 +161,7 @@ class PostfachApiFacadeServiceTest { MessageExchangeReceiveMessage receiveMessage; @Test - void shouldCallGetMessage(){ + void shouldCallGetMessage() { when(messageExchangeApi.getMessage(any())).thenReturn(replyMessage); postfachApiFacadeService.fetchMessageByGuid(receiveMessage); @@ -105,7 +170,7 @@ class PostfachApiFacadeServiceTest { } @Test - void shouldCallResponseMapper(){ + void shouldCallResponseMapper() { when(messageExchangeApi.getMessage(any())).thenReturn(replyMessage); when(osi2ResponseMapper.toPostfachNachricht(any())).thenReturn(PostfachNachrichtTestFactory.create()); @@ -115,7 +180,7 @@ class PostfachApiFacadeServiceTest { } @Test - void shouldReturnPostfachNachricht(){ + void shouldReturnPostfachNachricht() { when(messageExchangeApi.getMessage(any())).thenReturn(replyMessage); when(osi2ResponseMapper.toPostfachNachricht(any())).thenReturn(PostfachNachrichtTestFactory.create()); @@ -127,12 +192,12 @@ class PostfachApiFacadeServiceTest { @DisplayName("Delete Message") @Nested - class TestDeleteMessage{ + class TestDeleteMessage { @Mock MessageExchangeDeleteMessageResponse replyMessage; @Test - void shouldCallDeleteMessage(){ + void shouldCallDeleteMessage() { when(messageExchangeApi.deleteMessage(any())).thenReturn(replyMessage); postfachApiFacadeService.deleteMessage(UUID.randomUUID().toString()); diff --git a/src/test/resources/application-dev.yml b/src/test/resources/application-dev.yml new file mode 100644 index 0000000000000000000000000000000000000000..42c79c97a9e25c34d6547d9dee3b96bbddf5a308 --- /dev/null +++ b/src/test/resources/application-dev.yml @@ -0,0 +1,24 @@ +spring: + security: + oauth2: + client: + registration: + osi2: + client-id: 'OZG-Kopfstelle' + client-secret: 'changeme' + scope: default, access_urn:dataport:osi:sh:stage:ozgkopfstelle + authorization-grant-type: 'client_credentials' + client-authentication-method: client_secret_post + provider: + osi2: + token-uri: 'http://localhost:8080/osi-postfach-v2-token' +ozgcloud: + osiv2: + enabled: false + api: + resource: 'urn:dataport:osi:postfach:rz2:stage:sh' + url: 'http://localhost:8080' + tenant: 'SH' + name-identifier: 'ozgkopfstelle' + proxy: + enabled: false \ No newline at end of file