diff --git a/pom.xml b/pom.xml index 89aeabe7bc1dbd241930a1c1612acdd3f6ca9d7c..a10e6b554ecaedaa58f9d6056c279432c30eee7a 100644 --- a/pom.xml +++ b/pom.xml @@ -97,6 +97,11 @@ <scope>provided</scope> </dependency> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-core</artifactId> + </dependency> + <!-- Test --> <dependency> <groupId>org.apache.commons</groupId> @@ -137,7 +142,6 @@ <artifactId>snakeyaml</artifactId> <scope>test</scope> </dependency> - </dependencies> <build> @@ -145,6 +149,29 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> + <executions> + <execution> + <id>log4j2-plugin-processor</id> + <goals> + <goal>compile</goal> + <goal>testCompile</goal> + </goals> + <phase>process-classes</phase> + <configuration> + <proc>only</proc> + <annotationProcessorPaths> + <path> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-core</artifactId> + <version>${log4j2.version}</version> + </path> + </annotationProcessorPaths> + <annotationProcessors> + <processor>org.apache.logging.log4j.core.config.plugins.processor.PluginProcessor</processor> + </annotationProcessors> + </configuration> + </execution> + </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> diff --git a/src/main/java/de/ozgcloud/xta/client/FetchMessageParameter.java b/src/main/java/de/ozgcloud/xta/client/FetchMessageParameter.java new file mode 100644 index 0000000000000000000000000000000000000000..b2ae07f21cede17f6b93ffc23531ec347381340b --- /dev/null +++ b/src/main/java/de/ozgcloud/xta/client/FetchMessageParameter.java @@ -0,0 +1,36 @@ +package de.ozgcloud.xta.client; + +import java.util.List; +import java.util.Set; +import java.util.function.Consumer; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import de.ozgcloud.xta.client.model.XtaIdentifier; +import de.ozgcloud.xta.client.model.XtaMessage; +import de.ozgcloud.xta.client.model.XtaMessageMetaData; + +record FetchMessageParameter( + XtaIdentifier clientIdentifier, + Consumer<XtaMessage> processMessage, + Set<String> viewedMessageIds +) { + + public FetchMessageParameter withViewedMessageIdsFrom(List<XtaMessageMetaData> messageMetaData) { + return new FetchMessageParameter(this.clientIdentifier, this.processMessage, + collectSetOfViewedMessageIds(messageMetaData, this.viewedMessageIds)); + } + + private Set<String> collectSetOfViewedMessageIds(List<XtaMessageMetaData> messageMetaData, Set<String> processedMessageIds) { + return Stream.concat( + processedMessageIds.stream(), + messageMetaData.stream() + .map(XtaMessageMetaData::messageId) + ).collect(Collectors.toSet()); + } + + public boolean hasMessageAlreadyBeenViewed(XtaMessageMetaData messageMetaData) { + return viewedMessageIds.contains(messageMetaData.messageId()); + } + +} diff --git a/src/main/java/de/ozgcloud/xta/client/XtaClient.java b/src/main/java/de/ozgcloud/xta/client/XtaClient.java index a22c002cbb4d5bea9d0881ac7e4378a028523ed9..8d5d8b47a2af6bc8968e555b855185015b06d1e7 100644 --- a/src/main/java/de/ozgcloud/xta/client/XtaClient.java +++ b/src/main/java/de/ozgcloud/xta/client/XtaClient.java @@ -1,113 +1,171 @@ package de.ozgcloud.xta.client; +import static java.util.Collections.*; + +import java.util.List; +import java.util.Optional; +import java.util.function.Consumer; +import java.util.stream.Stream; + import jakarta.validation.Valid; -import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; import de.ozgcloud.xta.client.config.XtaClientConfig; -import de.ozgcloud.xta.client.core.WrappedXtaService; +import de.ozgcloud.xta.client.core.XtaClientService; +import de.ozgcloud.xta.client.core.XtaExceptionHandler; +import de.ozgcloud.xta.client.exception.XtaClientRuntimeException; +import de.ozgcloud.xta.client.exception.XtaClientException; +import de.ozgcloud.xta.client.exception.XtaClientInitializationException; import de.ozgcloud.xta.client.model.XtaIdentifier; import de.ozgcloud.xta.client.model.XtaMessage; -import de.ozgcloud.xta.client.model.XtaMessageAndTransportReport; +import de.ozgcloud.xta.client.model.XtaMessageMetaData; import de.ozgcloud.xta.client.model.XtaMessageMetaDataListing; +import de.ozgcloud.xta.client.model.XtaMessageStatus; import de.ozgcloud.xta.client.model.XtaTransportReport; -import genv3.de.xoev.transport.xta.x211.InvalidMessageIDException; -import genv3.de.xoev.transport.xta.x211.MessageSchemaViolationException; -import genv3.de.xoev.transport.xta.x211.MessageVirusDetectionException; -import genv3.de.xoev.transport.xta.x211.ParameterIsNotValidException; -import genv3.de.xoev.transport.xta.x211.PermissionDeniedException; -import genv3.de.xoev.transport.xta.x211.SyncAsyncException; -import genv3.de.xoev.transport.xta.x211.XTAWSTechnicalProblemException; import lombok.AccessLevel; import lombok.Builder; -import lombok.Getter; import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; @RequiredArgsConstructor(access = AccessLevel.MODULE) @Builder(access = AccessLevel.MODULE) -@Getter(AccessLevel.MODULE) +@Log4j2 public class XtaClient { - private final WrappedXtaService service; + private final XtaClientService service; private final XtaClientConfig config; + private final XtaExceptionHandler exceptionHandler; - /** - * Fetch metadata of pending messages sent to the {@code xtaIdentifier}. The returned listing contains at most - * {@link de.ozgcloud.xta.client.config.XtaClientConfig#getMaxListItems() maxListItems} messages. To fetch the next messages, use - * {@link #getNextMessagesMetadata(String)}. Note that {@code xtaIdentifier} has to be configured as a - * {@link de.ozgcloud.xta.client.config.XtaClientConfig#getClientIdentifiers() clientIdentifiers}. - * - * @param clientIdentifier the client identifier value to fetch messages for - * @return the listing result with metadata of messages - */ - public XtaMessageMetaDataListing getMessagesMetadata(@NotBlank String clientIdentifier) - throws XTAWSTechnicalProblemException, PermissionDeniedException { - var identifier = deriveIdentifier(clientIdentifier); - service.checkAccountActive(identifier); - return getStatusList(identifier); - } - - /** - * Fetch metadata of pending messages sent to the {@code xtaIdentifier}. This method skips checks but otherwise behaves exactly as - * {@link #getMessagesMetadata(String)}. - */ - public XtaMessageMetaDataListing getNextMessagesMetadata(@NotBlank String clientIdentifier) - throws XTAWSTechnicalProblemException, PermissionDeniedException { - return getStatusList(deriveIdentifier(clientIdentifier)); - } - - private XtaMessageMetaDataListing getStatusList(XtaIdentifier clientIdentifier) throws XTAWSTechnicalProblemException, PermissionDeniedException { - return service.getStatusList(clientIdentifier, config.getMaxListItems()); - } - - /** - * Fetch the message content, close the message, and then fetch the transport report for the given {@code messageId} and reader identifier - * {@code clientIdentifier}. - * - * @param clientIdentifier Identifier of the reading client - * @param messageId Identifier of the message to fetch - * @return The message and transport report - */ - public XtaMessageAndTransportReport getMessage(@NotBlank String clientIdentifier, @NotBlank String messageId) - throws XTAWSTechnicalProblemException, PermissionDeniedException, InvalidMessageIDException { - var identifier = deriveIdentifier(clientIdentifier); - - var message = service.getMessage(messageId, identifier); - service.close(messageId, identifier); - - var transportReport = service.getTransportReport(messageId, identifier); - return XtaMessageAndTransportReport.builder() - .message(message) - .transportReport(transportReport) - .build(); - } - - public XtaTransportReport sendMessage(@Valid XtaMessage messageWithoutMessageId) - throws XTAWSTechnicalProblemException, PermissionDeniedException, InvalidMessageIDException, SyncAsyncException, - MessageVirusDetectionException, MessageSchemaViolationException, ParameterIsNotValidException { + static final String NO_MESSAGE_CLOSED_WARNING = "No message has been closed although more are pending. Try increasing max list items."; - var metaData = messageWithoutMessageId.metaData(); - var authorIdentifier = metaData.authorIdentifier(); - service.checkAccountActive(authorIdentifier); - service.lookupService(metaData.service(), metaData.readerIdentifier(), authorIdentifier); + public static XtaClient from(XtaClientConfig config) throws XtaClientInitializationException { + return XtaClientFactory.from(config).create(); + } + + public List<XtaTransportReport> fetchMessages(@NotNull Consumer<XtaMessage> processMessage) throws XtaClientException { + try { + return fetchMessagesRaw(processMessage); + } catch (RuntimeException exception) { + throw exceptionHandler.deriveXtaClientException(exception); + } + } - var messageId = service.createMessageId(authorIdentifier); - service.sendMessage(createXtaMessageWithMessageId(messageWithoutMessageId, messageId)); + List<XtaTransportReport> fetchMessagesRaw(Consumer<XtaMessage> processMessage) { + return config.getClientIdentifiers().stream() + .filter(service::checkAccountActive) + .map(clientIdentifier -> initializeFetchMessageParameter(clientIdentifier, processMessage)) + .flatMap(this::fetchMessagesForClientIdentifier) + .toList(); + } - return service.getTransportReport(messageId, authorIdentifier); + FetchMessageParameter initializeFetchMessageParameter(XtaIdentifier clientIdentifier, Consumer<XtaMessage> processMessage) { + return new FetchMessageParameter(clientIdentifier, processMessage, emptySet()); } - XtaMessage createXtaMessageWithMessageId(XtaMessage messageWithoutMessageId, String messageId) { - return messageWithoutMessageId.toBuilder() - .metaData(messageWithoutMessageId.metaData().toBuilder() - .messageId(messageId) - .build()) - .build(); + Stream<XtaTransportReport> fetchMessagesForClientIdentifier(FetchMessageParameter parameter) { + return service.getStatusList(parameter.clientIdentifier()) + .map(listing -> { + var transportReports = fetchMessagesForListing(listing.messages(), parameter); + return Stream.concat(transportReports.stream(), + checkMoreMessagesAvailable(listing, transportReports) + ? fetchMessagesForClientIdentifier(parameter.withViewedMessageIdsFrom(listing.messages())) + : Stream.empty()); + }) + .orElse(Stream.empty()); } - XtaIdentifier deriveIdentifier(String xtaIdentifier) { - return config.getClientIdentifiers().stream() - .filter(id -> id.value().equals(xtaIdentifier)) - .findFirst() - .orElseThrow(() -> new IllegalArgumentException("Unknown identifier: " + xtaIdentifier)); + boolean checkMoreMessagesAvailable(XtaMessageMetaDataListing listing, List<XtaTransportReport> transportReports) { + return checkExtraPendingMessagesAvailable(listing) && checkSomeMessageHasBeenClosed(transportReports); + } + + boolean checkExtraPendingMessagesAvailable(XtaMessageMetaDataListing listing) { + return listing.messages().size() < listing.pendingMessageCount().intValue(); + } + + boolean checkSomeMessageHasBeenClosed(List<XtaTransportReport> transportReports) { + var someTransportReportHasClosedStatus = transportReports.stream() + .anyMatch(t -> !t.status().equals(XtaMessageStatus.OPEN)); + if (!someTransportReportHasClosedStatus) { + logWarnForNoMessageClosed(); + } + return someTransportReportHasClosedStatus; + } + + void logWarnForNoMessageClosed() { + log.warn(NO_MESSAGE_CLOSED_WARNING); } + + List<XtaTransportReport> fetchMessagesForListing( + List<XtaMessageMetaData> messageMetaDataItems, + FetchMessageParameter parameter + ) { + return messageMetaDataItems.stream() + .filter(metaData -> checkMessageShouldBeFetched(metaData, parameter)) + .map(metaData -> fetchMessage(metaData, parameter)) + .flatMap(Optional::stream) + .toList(); + } + + boolean checkMessageShouldBeFetched(XtaMessageMetaData messageMetaData, FetchMessageParameter parameter) { + return !parameter.hasMessageAlreadyBeenViewed(messageMetaData) && isMessageSupported(messageMetaData); + } + + boolean isMessageSupported(XtaMessageMetaData messageMetaData) { + return Optional.ofNullable(config.getIsMessageSupported()) + .map(predicate -> predicate.test(messageMetaData)) + .orElse(true); + } + + Optional<XtaTransportReport> fetchMessage(XtaMessageMetaData messageMetaData, FetchMessageParameter parameter) { + return service.getMessage(messageMetaData) + .flatMap(message -> processMesssageAndFetchTransportReport(message, parameter)); + } + + Optional<XtaTransportReport> processMesssageAndFetchTransportReport(XtaMessage message, FetchMessageParameter parameter) { + processMessageAndCloseIfNoException(message, parameter.processMessage()); + return service.getTransportReport(message.metaData()); + } + + void processMessageAndCloseIfNoException(XtaMessage message, Consumer<XtaMessage> processMessage) { + var messageId = message.metaData().messageId(); + try { + processMessage.accept(message); + log.debug("Processing of message '{}' succeeded! Closing message.", messageId); + service.closeMessage(message); + } catch (RuntimeException exception) { + logErrorForMessageProcessingFailure(messageId, exception); + } + } + + void logErrorForMessageProcessingFailure(String messageId, RuntimeException exception) { + log.error("Processing of message '%s' failed! Not closing message.".formatted(messageId), exception); + } + + public XtaTransportReport sendMessage(@Valid XtaMessage message) throws XtaClientException { + try { + return sendMessageRaw(message); + } catch (RuntimeException exception) { + throw exceptionHandler.deriveXtaClientException(exception); + } + } + + XtaTransportReport sendMessageRaw(@Valid XtaMessage messageWithoutMessageId) { + var metaData = messageWithoutMessageId.metaData(); + throwExceptionIfAccountInactive(metaData.authorIdentifier()); + throwExceptionIfServiceNotAvailable(metaData); + return service.sendMessage(messageWithoutMessageId); + } + + void throwExceptionIfServiceNotAvailable(XtaMessageMetaData metaData) { + if (!service.lookupService(metaData)) { + throw new XtaClientRuntimeException("Service %s is not available!".formatted(metaData.service())); + } + } + + void throwExceptionIfAccountInactive(XtaIdentifier clientIdentifier) { + if (!service.checkAccountActive(clientIdentifier)) { + throw new XtaClientRuntimeException("Account %s is not active!".formatted(clientIdentifier.value())); + } + } + } diff --git a/src/main/java/de/ozgcloud/xta/client/XtaClientFactory.java b/src/main/java/de/ozgcloud/xta/client/XtaClientFactory.java index 8d9c3ec43f972419a8b33ee3dd2335d0fb51ba01..e5429aec7a7c5ee0762362333985eab6b22a74d4 100644 --- a/src/main/java/de/ozgcloud/xta/client/XtaClientFactory.java +++ b/src/main/java/de/ozgcloud/xta/client/XtaClientFactory.java @@ -2,8 +2,9 @@ package de.ozgcloud.xta.client; import de.ozgcloud.xta.client.config.XtaClientConfig; import de.ozgcloud.xta.client.config.XtaConfigValidator; -import de.ozgcloud.xta.client.core.WrappedXtaServiceFactory; -import de.ozgcloud.xta.client.exception.ClientInitializationException; +import de.ozgcloud.xta.client.core.XtaClientServiceFactory; +import de.ozgcloud.xta.client.core.XtaExceptionHandlerFactory; +import de.ozgcloud.xta.client.exception.XtaClientInitializationException; import lombok.Builder; import lombok.RequiredArgsConstructor; @@ -12,22 +13,25 @@ import lombok.RequiredArgsConstructor; public class XtaClientFactory { private final XtaConfigValidator configValidator; - private final WrappedXtaServiceFactory wrappedXtaServiceFactory; + private final XtaClientServiceFactory xtaClientServiceFactory; private final XtaClientConfig config; + private final XtaExceptionHandlerFactory exceptionHandlerFactory; public static XtaClientFactory from(final XtaClientConfig config) { return XtaClientFactory.builder() .config(config) .configValidator(XtaConfigValidator.builder().build()) - .wrappedXtaServiceFactory(WrappedXtaServiceFactory.from(config)) + .xtaClientServiceFactory(XtaClientServiceFactory.from(config)) + .exceptionHandlerFactory(XtaExceptionHandlerFactory.builder().build()) .build(); } - public XtaClient create() throws ClientInitializationException { + public XtaClient create() throws XtaClientInitializationException { configValidator.validate(config); return XtaClient.builder() .config(config) - .service(wrappedXtaServiceFactory.create()) + .service(xtaClientServiceFactory.create()) + .exceptionHandler(exceptionHandlerFactory.create()) .build(); } } diff --git a/src/main/java/de/ozgcloud/xta/client/codes/BusinessScenario.java b/src/main/java/de/ozgcloud/xta/client/codes/BusinessScenario.java deleted file mode 100644 index 46bd4247bceaab4aa30f1b1a394e078d729cf110..0000000000000000000000000000000000000000 --- a/src/main/java/de/ozgcloud/xta/client/codes/BusinessScenario.java +++ /dev/null @@ -1,76 +0,0 @@ -/** - * XTA-Client Java Library - * Copyright (C) 2021 Koordinierungsstelle für IT-Standards (KoSIT) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <https://www.gnu.org/licenses/>. - */ -package de.ozgcloud.xta.client.codes; - -import lombok.Getter; - -/** - * Einige vordefinierte Werte für "business.scenario" (Fachkontext/Verfahren). Werte die nicht auftauchen können über den - * Stringsetter gesetzt werden. - * - * @see <a href="https://www.xrepository.de/details/urn:de:xta:codeliste:business.scenario">business.scenario</a> - */ -@Getter -public enum BusinessScenario { - - /** - * Fachkontext XBAULEITPLANUNG. - */ - XBAULEITPLANUNG_DATA("XBAULEITPLANUNG_DATA"), - - /** - * Fachkontext XDOMEAREG. - */ - XDOMEAREG_DATA("XDOMEAREG_DATA"), - - /** - * Fachkontext XInneres. - */ - XINNERES_DATA("XINNERES_DATA"), - - /** - * Fachkontext XAusländer. - */ - XAUSLAENDER_DATA("XAUSLAENDER_DATA"), - - /** - * Fachkontext OSCI-XMeld. - */ - XMELD_DATA("XMELD_DATA"), - - /** - * Fachkontext XPersonenstand. - */ - XPERSONENSTAND_DATA("XPERSONENSTAND_DATA"), - - /** - * Fachkontext XhD. - */ - XHD_DATA("XHD_DATA"), - - /** - * Fachkontext XGewerbeanzeige. - */ - GEWERBE_DATA("GEWERBE_DATA"); - - private String code; - - BusinessScenario(String code) { - this.code = code; - } -} diff --git a/src/main/java/de/ozgcloud/xta/client/codes/IdentifierType.java b/src/main/java/de/ozgcloud/xta/client/codes/IdentifierType.java deleted file mode 100644 index 7b80214807430ef96ed0af1f915e38a247c8e716..0000000000000000000000000000000000000000 --- a/src/main/java/de/ozgcloud/xta/client/codes/IdentifierType.java +++ /dev/null @@ -1,48 +0,0 @@ -/** - * XTA-Client Java Library - * Copyright (C) 2021 Koordinierungsstelle für IT-Standards (KoSIT) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <https://www.gnu.org/licenses/>. - */ -package de.ozgcloud.xta.client.codes; - -import lombok.Getter; - -/** - * Einige vordefinierte Werte für "type.of.party.identifier". Werte die nicht auftauchen können über den Stringsetter gestzt werden * {@link - * Identifier (final String, String)} - * - * @see <a href"https://www.xrepository.de/details/urn:de:xta:codeliste:type.of.party.identifier">type.of.party.identifier</a> - */ -@Getter -public enum IdentifierType { - - /** - * XÖV-DVDV-Infrastruktur: Identifizierungsschema gemäß DVDV-Behördenschlüssel Nach den Regeln dieses Identifizierungsschemas - * ist z.B. die Kennung "psw:01002110" aufgebaut. - */ - XOEV("xoev"), - - /** - * SAFE-Infrastruktur des Justizressorts: Identifizierungsschema gemäß SAFE Nach den Regeln dieses Identifizierungsschemas ist - * z.B. die Kennung "safe-1363359638330-001060793" aufgebaut. - */ - JUSTIZ("justiz"); - - private String code; - - IdentifierType(String code) { - this.code = code; - } -} diff --git a/src/main/java/de/ozgcloud/xta/client/config/XtaClientConfig.java b/src/main/java/de/ozgcloud/xta/client/config/XtaClientConfig.java index 184ce5b94144de8d8104d8da8fc46ffaf151cd69..14bdf0499aa8265eb430f31a09c266200a26e64e 100644 --- a/src/main/java/de/ozgcloud/xta/client/config/XtaClientConfig.java +++ b/src/main/java/de/ozgcloud/xta/client/config/XtaClientConfig.java @@ -1,25 +1,17 @@ -/** - * XTA-Client Java Library Copyright (C) 2021 Koordinierungsstelle für IT-Standards (KoSIT) - * - * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. - */ package de.ozgcloud.xta.client.config; +import static java.util.Collections.*; + import java.util.List; +import java.util.function.Predicate; import jakarta.validation.Valid; import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Positive; import de.ozgcloud.xta.client.model.XtaIdentifier; +import de.ozgcloud.xta.client.model.XtaMessageMetaData; import lombok.Builder; import lombok.Getter; import lombok.RequiredArgsConstructor; @@ -31,8 +23,11 @@ import lombok.ToString; @ToString public class XtaClientConfig { - @NotEmpty(message = "at least one client identifier is required") - private final List<@Valid XtaIdentifier> clientIdentifiers; + @Builder.Default + private final List<@Valid XtaIdentifier> clientIdentifiers = emptyList(); + + @Builder.Default + private final Predicate<XtaMessageMetaData> isMessageSupported = null; @NotBlank private final String managementServiceUrl; diff --git a/src/main/java/de/ozgcloud/xta/client/config/XtaConfigValidator.java b/src/main/java/de/ozgcloud/xta/client/config/XtaConfigValidator.java index 95821648a72da25c33e0eaed5598b30df0239606..f0fd961da8aadf56d9cb7b64c243e03e2208c8e6 100644 --- a/src/main/java/de/ozgcloud/xta/client/config/XtaConfigValidator.java +++ b/src/main/java/de/ozgcloud/xta/client/config/XtaConfigValidator.java @@ -7,7 +7,7 @@ import java.util.stream.Collectors; import jakarta.validation.Validation; import jakarta.validation.Validator; -import de.ozgcloud.xta.client.exception.ClientInitializationException; +import de.ozgcloud.xta.client.exception.XtaClientInitializationException; import lombok.Builder; import lombok.RequiredArgsConstructor; @@ -22,10 +22,10 @@ public class XtaConfigValidator { } } - public void validate(final XtaClientConfig config) throws ClientInitializationException { + public void validate(final XtaClientConfig config) throws XtaClientInitializationException { var violations = VALIDATOR.validate(config); if (!violations.isEmpty()) { - throw new ClientInitializationException("Client configuration is invalid:\n" + violations.stream() + throw new XtaClientInitializationException("Client configuration is invalid:\n" + violations.stream() .map(v -> "'%s' %s".formatted(v.getPropertyPath().toString(), v.getMessage())) .collect(Collectors.joining("\n"))); } diff --git a/src/main/java/de/ozgcloud/xta/client/core/WrappedXtaServiceFactory.java b/src/main/java/de/ozgcloud/xta/client/core/WrappedXtaServiceFactory.java index 6a909b5709cce39cb5e369519fb28da62c56ee4a..9d89e96b5885d64c69c69687fb4bfd250c04dd5b 100644 --- a/src/main/java/de/ozgcloud/xta/client/core/WrappedXtaServiceFactory.java +++ b/src/main/java/de/ozgcloud/xta/client/core/WrappedXtaServiceFactory.java @@ -3,7 +3,7 @@ package de.ozgcloud.xta.client.core; import org.mapstruct.factory.Mappers; import de.ozgcloud.xta.client.config.XtaClientConfig; -import de.ozgcloud.xta.client.exception.ClientInitializationException; +import de.ozgcloud.xta.client.exception.XtaClientInitializationException; import de.ozgcloud.xta.client.mapper.RequestMapper; import de.ozgcloud.xta.client.mapper.ResponseMapper; import lombok.Builder; @@ -25,7 +25,7 @@ public class WrappedXtaServiceFactory { .build(); } - public WrappedXtaService create() throws ClientInitializationException { + public WrappedXtaService create() throws XtaClientInitializationException { return WrappedXtaService.builder() .ports(xtaPortTripleFactory.create()) .requestMapper(requestMapper) diff --git a/src/main/java/de/ozgcloud/xta/client/core/XtaClientService.java b/src/main/java/de/ozgcloud/xta/client/core/XtaClientService.java new file mode 100644 index 0000000000000000000000000000000000000000..d61028941a5ed2ec58f05e29deb9f0f0050ce9e6 --- /dev/null +++ b/src/main/java/de/ozgcloud/xta/client/core/XtaClientService.java @@ -0,0 +1,144 @@ +package de.ozgcloud.xta.client.core; + +import java.util.Optional; + +import de.ozgcloud.xta.client.config.XtaClientConfig; +import de.ozgcloud.xta.client.exception.XtaClientRuntimeException; +import de.ozgcloud.xta.client.model.XtaIdentifier; +import de.ozgcloud.xta.client.model.XtaMessage; +import de.ozgcloud.xta.client.model.XtaMessageMetaData; +import de.ozgcloud.xta.client.model.XtaMessageMetaDataListing; +import de.ozgcloud.xta.client.model.XtaTransportReport; +import genv3.de.xoev.transport.xta.x211.InvalidMessageIDException; +import genv3.de.xoev.transport.xta.x211.MessageSchemaViolationException; +import genv3.de.xoev.transport.xta.x211.MessageVirusDetectionException; +import genv3.de.xoev.transport.xta.x211.ParameterIsNotValidException; +import genv3.de.xoev.transport.xta.x211.PermissionDeniedException; +import genv3.de.xoev.transport.xta.x211.SyncAsyncException; +import genv3.de.xoev.transport.xta.x211.XTAWSTechnicalProblemException; +import lombok.Builder; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +@Builder +@RequiredArgsConstructor +@Slf4j +public class XtaClientService { + + private final WrappedXtaService service; + private final XtaClientConfig config; + + static final String TRANSPORT_REPORT_FAILED_ERROR = "Failed to get transport report!"; + + public Optional<XtaTransportReport> getTransportReport(XtaMessageMetaData messageMetaData) { + try { + return Optional.of(getTransportReportOrThrowException(messageMetaData)); + } catch (XtaClientRuntimeException e) { + logError(TRANSPORT_REPORT_FAILED_ERROR, e); + return Optional.empty(); + } + } + + public void closeMessage(XtaMessage message) { + var messageId = message.metaData().messageId(); + var readerClientIdentifier = message.metaData().readerIdentifier(); + try { + service.close(messageId, readerClientIdentifier); + } catch (XTAWSTechnicalProblemException | PermissionDeniedException | InvalidMessageIDException | RuntimeException e) { + logError("Failed to close message! '%s' (reader: %s)".formatted(messageId, readerClientIdentifier), e); + } + } + + public Optional<XtaMessage> getMessage(XtaMessageMetaData messageMetaData) { + var messageId = messageMetaData.messageId(); + var readerClientIdentifier = messageMetaData.readerIdentifier(); + try { + return Optional.of(service.getMessage(messageId, readerClientIdentifier)); + } catch (XTAWSTechnicalProblemException | PermissionDeniedException | InvalidMessageIDException | RuntimeException e) { + logError("Failed to get message by id ! '%s' (reader: %s)".formatted(messageId, readerClientIdentifier.value()), e); + return Optional.empty(); + } + } + + public Optional<XtaMessageMetaDataListing> getStatusList(XtaIdentifier clientIdentifier) { + try { + return Optional.of(service.getStatusList(clientIdentifier, config.getMaxListItems())); + } catch (PermissionDeniedException | XTAWSTechnicalProblemException | RuntimeException e) { + logError("Failed to get status list!", e); + return Optional.empty(); + } + } + + void logError(String message, Exception e) { + log.error(message, e); + } + + public boolean checkAccountActive(XtaIdentifier clientIdentifier) { + try { + service.checkAccountActive(clientIdentifier); + return true; + } catch (XTAWSTechnicalProblemException | RuntimeException e) { + throw new XtaClientRuntimeException("Failed to check account active!", e); + } catch (PermissionDeniedException e) { + return false; + } + } + + public XtaTransportReport sendMessage(XtaMessage messageWithoutMessageId) { + var message = getXtaMessageWithMessageId(messageWithoutMessageId); + try { + service.sendMessage(message); + return getTransportReportOrThrowException(message.metaData()); + } catch (XTAWSTechnicalProblemException | PermissionDeniedException | SyncAsyncException | ParameterIsNotValidException | + MessageVirusDetectionException | MessageSchemaViolationException | RuntimeException e) { + throw new XtaClientRuntimeException("Failed to send message!", e); + } + } + + XtaMessage getXtaMessageWithMessageId(XtaMessage messageWithoutMessageId) { + var authorIdentifier = messageWithoutMessageId.metaData().authorIdentifier(); + try { + var messageId = service.createMessageId(authorIdentifier); + return createXtaMessageWithMessageId(messageWithoutMessageId, messageId); + } catch (XTAWSTechnicalProblemException | PermissionDeniedException | RuntimeException e) { + throw new XtaClientRuntimeException("Failed to create message id!", e); + } + } + + XtaMessage createXtaMessageWithMessageId(XtaMessage messageWithoutMessageId, String messageId) { + return messageWithoutMessageId.toBuilder() + .metaData(messageWithoutMessageId.metaData().toBuilder() + .messageId(messageId) + .build()) + .build(); + } + + XtaTransportReport getTransportReportOrThrowException(XtaMessageMetaData messageMetaData) { + var messageId = messageMetaData.messageId(); + var authorId = messageMetaData.authorIdentifier(); + try { + return service.getTransportReport(messageId, authorId); + } catch (XTAWSTechnicalProblemException | PermissionDeniedException | InvalidMessageIDException | RuntimeException e) { + throw new XtaClientRuntimeException( + "Failed to get transport report! (messageId: %s, reader: %s)".formatted(messageId, authorId.value()), e); + } + } + + public boolean lookupService(XtaMessageMetaData messageMetaData) { + try { + return service.lookupService( + messageMetaData.service(), + messageMetaData.readerIdentifier(), + messageMetaData.authorIdentifier() + ); + } catch (XTAWSTechnicalProblemException | PermissionDeniedException | ParameterIsNotValidException | RuntimeException exception) { + logWarning("Failed to lookup service for message '%s'!".formatted(messageMetaData.messageId()), exception); + return false; + } + } + + void logWarning(String message, Exception exception) { + log.warn(message, exception); + } + +} diff --git a/src/main/java/de/ozgcloud/xta/client/core/XtaClientServiceFactory.java b/src/main/java/de/ozgcloud/xta/client/core/XtaClientServiceFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..0343c5060aa5669b1b41c10ba1e74d4b95f2aec4 --- /dev/null +++ b/src/main/java/de/ozgcloud/xta/client/core/XtaClientServiceFactory.java @@ -0,0 +1,26 @@ +package de.ozgcloud.xta.client.core; + +import de.ozgcloud.xta.client.config.XtaClientConfig; +import de.ozgcloud.xta.client.exception.XtaClientInitializationException; +import lombok.Builder; + +@Builder +public class XtaClientServiceFactory { + + private final WrappedXtaServiceFactory wrappedXtaServiceFactory; + private final XtaClientConfig config; + + public static XtaClientServiceFactory from(XtaClientConfig config) { + return XtaClientServiceFactory.builder() + .config(config) + .wrappedXtaServiceFactory(WrappedXtaServiceFactory.from(config)) + .build(); + } + + public XtaClientService create() throws XtaClientInitializationException { + return XtaClientService.builder() + .config(config) + .service(wrappedXtaServiceFactory.create()) + .build(); + } +} diff --git a/src/main/java/de/ozgcloud/xta/client/core/XtaExceptionHandler.java b/src/main/java/de/ozgcloud/xta/client/core/XtaExceptionHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..a31641c1ada3c3743ec3500a731703948469f59c --- /dev/null +++ b/src/main/java/de/ozgcloud/xta/client/core/XtaExceptionHandler.java @@ -0,0 +1,67 @@ +package de.ozgcloud.xta.client.core; + +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import de.ozgcloud.xta.client.exception.XtaClientRuntimeException; +import de.ozgcloud.xta.client.exception.XtaClientException; +import genv3.de.xoev.transport.xta.x211.ExceptionType; +import lombok.Builder; +import lombok.RequiredArgsConstructor; + +@Builder +@RequiredArgsConstructor +public class XtaExceptionHandler { + + static final String UNEXPECTED_ERROR_MESSAGE = "An unexpected error occurred. Please report this to the xta-client maintainers."; + + public XtaClientException deriveXtaClientException(RuntimeException exception) { + if (exception instanceof XtaClientRuntimeException xtaClientRuntimeException) { + return deriveXtaClientExceptionFromClientRuntimeException(xtaClientRuntimeException); + } + return new XtaClientException(UNEXPECTED_ERROR_MESSAGE, exception); + } + + XtaClientException deriveXtaClientExceptionFromClientRuntimeException(XtaClientRuntimeException exception) { + var cause = exception.getCause(); + var detailMessageLines = getDetailLines(cause); + var message = Stream.concat( + Stream.of(exception.getMessage()), + detailMessageLines + ).collect(Collectors.joining("\n")); + return new XtaClientException(message, cause); + } + + Stream<String> getDetailLines(Throwable cause) { + return Optional.ofNullable(cause) + .filter(Exception.class::isInstance) + .map(Exception.class::cast) + .map(this::deriveDetailLinesFromException) + .orElse(Stream.empty()); + } + + Stream<String> deriveDetailLinesFromException(Exception exception) { + return getExceptionType(exception) + .map(ExceptionType::getErrorCode) + .map(num -> Stream.of(exception.getMessage(), "[%s] %s".formatted(num.getCode(), num.getName()))) + .orElse(Stream.empty()); + } + + private Optional<ExceptionType> getExceptionType(Exception exception) { + return getPrivateFieldValue(exception, "faultInfo") + .filter(ExceptionType.class::isInstance) + .map(ExceptionType.class::cast); + } + + private Optional<Object> getPrivateFieldValue(Object object, String fieldName) { + try { + var field = object.getClass().getDeclaredField(fieldName); + field.setAccessible(true); + return Optional.of(field.get(object)); + } catch (NoSuchFieldException | IllegalAccessException | RuntimeException e) { + return Optional.empty(); + } + } + +} diff --git a/src/main/java/de/ozgcloud/xta/client/core/XtaExceptionHandlerFactory.java b/src/main/java/de/ozgcloud/xta/client/core/XtaExceptionHandlerFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..67cb8a8d0ec426a01843e9c70ca56da7a93b673d --- /dev/null +++ b/src/main/java/de/ozgcloud/xta/client/core/XtaExceptionHandlerFactory.java @@ -0,0 +1,14 @@ +package de.ozgcloud.xta.client.core; + +import lombok.Builder; +import lombok.RequiredArgsConstructor; + +@Builder +@RequiredArgsConstructor +public class XtaExceptionHandlerFactory { + + public XtaExceptionHandler create() { + return XtaExceptionHandler.builder() + .build(); + } +} diff --git a/src/main/java/de/ozgcloud/xta/client/core/XtaPortTripleFactory.java b/src/main/java/de/ozgcloud/xta/client/core/XtaPortTripleFactory.java index 3da0a62c8fbd0c2e44f2aaf9eb5a35eeea2e8bbd..908dc1e6270c9a2d85b524c33976af5b15ea6018 100644 --- a/src/main/java/de/ozgcloud/xta/client/core/XtaPortTripleFactory.java +++ b/src/main/java/de/ozgcloud/xta/client/core/XtaPortTripleFactory.java @@ -14,7 +14,7 @@ import org.apache.cxf.message.Message; import org.apache.cxf.transport.http.HTTPConduit; import de.ozgcloud.xta.client.config.XtaClientConfig; -import de.ozgcloud.xta.client.exception.ClientInitializationException; +import de.ozgcloud.xta.client.exception.XtaClientInitializationException; import genv3.de.xoev.transport.xta.x211.XTAService; import lombok.Builder; import lombok.RequiredArgsConstructor; @@ -41,7 +41,7 @@ public class XtaPortTripleFactory { .build(); } - public XtaPortTriple create() throws ClientInitializationException { + public XtaPortTriple create() throws XtaClientInitializationException { log.debug("[createXtaService] Using config: {}", config); return new XtaPortTriple( configurePort(config.getManagementServiceUrl(), xtaService.getManagementPort()), @@ -50,13 +50,13 @@ public class XtaPortTripleFactory { ); } - private <T> T configurePort(final String endpointUrl, final T port) throws ClientInitializationException { + private <T> T configurePort(final String endpointUrl, final T port) throws XtaClientInitializationException { var bindingProvider = (BindingProvider) port; configureBinding(endpointUrl, bindingProvider); return port; } - void configureBinding(final String endpointUrl, final BindingProvider port) throws ClientInitializationException { + void configureBinding(final String endpointUrl, final BindingProvider port) throws XtaClientInitializationException { configureRequestContext(endpointUrl, port.getRequestContext()); configureClient(getClientFromPort(port)); } @@ -74,7 +74,7 @@ public class XtaPortTripleFactory { )); } - void configureClient(Client client) throws ClientInitializationException { + void configureClient(Client client) throws XtaClientInitializationException { if (config.isLogSoapRequests()) { configureOutInterceptors(client.getOutInterceptors()); } @@ -105,7 +105,7 @@ public class XtaPortTripleFactory { return inInterceptor; } - void configureHttpConduit(HTTPConduit conduit) throws ClientInitializationException { + void configureHttpConduit(HTTPConduit conduit) throws XtaClientInitializationException { conduit.setTlsClientParameters(tlsClientParametersFactory.create()); } } diff --git a/src/main/java/de/ozgcloud/xta/client/core/XtaTLSClientParametersFactory.java b/src/main/java/de/ozgcloud/xta/client/core/XtaTLSClientParametersFactory.java index fee35b47d2f185f4b17fa617780ecfaa274b8891..f314f4f2e39274bdb1d94767de35d2b4454adc6c 100644 --- a/src/main/java/de/ozgcloud/xta/client/core/XtaTLSClientParametersFactory.java +++ b/src/main/java/de/ozgcloud/xta/client/core/XtaTLSClientParametersFactory.java @@ -15,7 +15,7 @@ import org.apache.cxf.configuration.jsse.TLSClientParameters; import org.apache.hc.core5.ssl.SSLContextBuilder; import de.ozgcloud.xta.client.config.XtaClientConfig; -import de.ozgcloud.xta.client.exception.ClientInitializationException; +import de.ozgcloud.xta.client.exception.XtaClientInitializationException; import lombok.Builder; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -29,7 +29,7 @@ public class XtaTLSClientParametersFactory { private final XtaClientConfig config; - public TLSClientParameters create() throws ClientInitializationException { + public TLSClientParameters create() throws XtaClientInitializationException { log.debug("[createXtaTLsParameters] Using config: {}", config); var sslContext = createXtaSslContext(); @@ -38,7 +38,7 @@ public class XtaTLSClientParametersFactory { return parameters; } - public SSLContext createXtaSslContext() throws ClientInitializationException { + public SSLContext createXtaSslContext() throws XtaClientInitializationException { try { var sslContextBuilder = createSSLContextBuilder(); var clientCertKeystore = config.getClientCertKeystore(); @@ -52,7 +52,7 @@ public class XtaTLSClientParametersFactory { return sslContextBuilder.build(); } catch (NoSuchAlgorithmException | KeyStoreException | KeyManagementException | UnrecoverableKeyException | IOException | CertificateException e) { - throw new ClientInitializationException("Failed to create SSL context: " + e.getMessage(), e); + throw new XtaClientInitializationException("Failed to create SSL context: " + e.getMessage(), e); } } diff --git a/src/main/java/de/ozgcloud/xta/client/exception/ClientException.java b/src/main/java/de/ozgcloud/xta/client/exception/ClientException.java deleted file mode 100644 index 0b898337a04b230eb57d217e42f025a854de6eda..0000000000000000000000000000000000000000 --- a/src/main/java/de/ozgcloud/xta/client/exception/ClientException.java +++ /dev/null @@ -1,33 +0,0 @@ -/** - * XTA-Client Java Library - * Copyright (C) 2021 Koordinierungsstelle für IT-Standards (KoSIT) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <https://www.gnu.org/licenses/>. - */ -package de.ozgcloud.xta.client.exception; - - -/** - * Allgemeiner Fehler zum signalisieren von clientseitigen Problemen. - */ -public class ClientException extends Exception { - - public ClientException(final String message, final Throwable cause) { - super(message, cause); - } - - public ClientException(final String message) { - super(message); - } -} diff --git a/src/main/java/de/ozgcloud/xta/client/exception/ClientInitializationException.java b/src/main/java/de/ozgcloud/xta/client/exception/ClientInitializationException.java deleted file mode 100644 index 5a1b2a792bf7e5f7ba5c823ec790facbf0dca490..0000000000000000000000000000000000000000 --- a/src/main/java/de/ozgcloud/xta/client/exception/ClientInitializationException.java +++ /dev/null @@ -1,34 +0,0 @@ -/** - * XTA-Client Java Library - * Copyright (C) 2021 Koordinierungsstelle für IT-Standards (KoSIT) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <https://www.gnu.org/licenses/>. - */ -package de.ozgcloud.xta.client.exception; - -/** - * Spezieller Fehler zum signalisieren von clientseitigen Problemen während der Initialisierung. - * - * @author hmaterny - */ -public class ClientInitializationException extends ClientException { - - public ClientInitializationException(final String message, final Throwable cause) { - super(message, cause); - } - - public ClientInitializationException(final String message) { - super(message); - } -} diff --git a/src/main/java/de/ozgcloud/xta/client/exception/XtaClientException.java b/src/main/java/de/ozgcloud/xta/client/exception/XtaClientException.java new file mode 100644 index 0000000000000000000000000000000000000000..f2530d0647cd923735513241270c51bdbfd12b11 --- /dev/null +++ b/src/main/java/de/ozgcloud/xta/client/exception/XtaClientException.java @@ -0,0 +1,17 @@ +package de.ozgcloud.xta.client.exception; + + +/** + * A generic exception during xta client method execution. + * + */ +public class XtaClientException extends Exception { + + public XtaClientException(final String message, final Throwable cause) { + super(message, cause); + } + + public XtaClientException(final String message) { + super(message); + } +} diff --git a/src/main/java/de/ozgcloud/xta/client/exception/XtaClientInitializationException.java b/src/main/java/de/ozgcloud/xta/client/exception/XtaClientInitializationException.java new file mode 100644 index 0000000000000000000000000000000000000000..0e361320c45fc3be0580d3f4ffdd3733fdba072d --- /dev/null +++ b/src/main/java/de/ozgcloud/xta/client/exception/XtaClientInitializationException.java @@ -0,0 +1,16 @@ +package de.ozgcloud.xta.client.exception; + +/** + * A generic exception during xta client initialization. + * + */ +public class XtaClientInitializationException extends XtaClientException { + + public XtaClientInitializationException(final String message, final Throwable cause) { + super(message, cause); + } + + public XtaClientInitializationException(final String message) { + super(message); + } +} diff --git a/src/main/java/de/ozgcloud/xta/client/exception/XtaClientRuntimeException.java b/src/main/java/de/ozgcloud/xta/client/exception/XtaClientRuntimeException.java new file mode 100644 index 0000000000000000000000000000000000000000..9c7d4b89b154d0d76a18d9a00044413fa911f816 --- /dev/null +++ b/src/main/java/de/ozgcloud/xta/client/exception/XtaClientRuntimeException.java @@ -0,0 +1,12 @@ +package de.ozgcloud.xta.client.exception; + +public class XtaClientRuntimeException extends RuntimeException { + + public XtaClientRuntimeException(String message) { + super(message); + } + + public XtaClientRuntimeException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/de/ozgcloud/xta/client/model/XtaFile.java b/src/main/java/de/ozgcloud/xta/client/model/XtaFile.java index 89b2d1e6d1d02da3b75c5ac44b209bf5552894f2..2561f45d22fcc92d3d4e729c78a40d741d470bc3 100644 --- a/src/main/java/de/ozgcloud/xta/client/model/XtaFile.java +++ b/src/main/java/de/ozgcloud/xta/client/model/XtaFile.java @@ -10,7 +10,7 @@ import jakarta.validation.constraints.PositiveOrZero; import lombok.Builder; -@Builder +@Builder(toBuilder = true) public record XtaFile( @NotNull DataHandler content, @NotBlank String contentType, diff --git a/src/main/java/de/ozgcloud/xta/client/xdomea/XdomeaMetaDataValidator.java b/src/main/java/de/ozgcloud/xta/client/xdomea/XdomeaMetaDataValidator.java index eafbad0b39346f495d20d52f05b770d214a983c5..7a5c363a7bb2d942438b6009188716dc1bd76e5b 100644 --- a/src/main/java/de/ozgcloud/xta/client/xdomea/XdomeaMetaDataValidator.java +++ b/src/main/java/de/ozgcloud/xta/client/xdomea/XdomeaMetaDataValidator.java @@ -5,7 +5,7 @@ import static de.ozgcloud.xta.client.xdomea.mapper.MetadataMapper.*; import java.util.List; import java.util.Set; -import de.ozgcloud.xta.client.exception.ClientException; +import de.ozgcloud.xta.client.exception.XtaClientException; import de.ozgcloud.xta.client.model.XtaFile; import de.ozgcloud.xta.client.model.XtaMessageMetaData; import de.ozgcloud.xta.client.xdomea.reader.XdomeaXmlValues; @@ -26,7 +26,7 @@ public class XdomeaMetaDataValidator { XtaFile xdomeaZipFile, XdomeaXmlValues xdomeaXmlValues, XtaMessageMetaData messageMetadataData - ) throws ClientException { + ) throws XtaClientException { validateIdentifierPrefixes( xdomeaXmlValues.authorIdPrefix(), xdomeaXmlValues.readerIdPrefix() @@ -39,14 +39,14 @@ public class XdomeaMetaDataValidator { validatePrimaryDocumentReferences(xdomeaZipFile, xdomeaXmlValues.primaryDocumentNames()); } - void validateIdentifierPrefixes(String authorIdPrefix, String readerIdPrefix) throws ClientException { + void validateIdentifierPrefixes(String authorIdPrefix, String readerIdPrefix) throws XtaClientException { validateIdentifierPrefix(authorIdPrefix, AUTHOR_ID_PREFIX, AUTHOR_ID_PREFIX_NAME); validateIdentifierPrefix(readerIdPrefix, READER_ID_PREFIX, READER_ID_PREFIX_NAME); } - void validateIdentifierPrefix(String prefix, String expectedPrefix, String prefixName) throws ClientException { + void validateIdentifierPrefix(String prefix, String expectedPrefix, String prefixName) throws XtaClientException { if (!expectedPrefix.equals(normalizePrefix(prefix))) { - throw new ClientException("Expect prefix of %s identifier to be '%s'! (actual: %s)".formatted(prefixName, expectedPrefix, prefix)); + throw new XtaClientException("Expect prefix of %s identifier to be '%s'! (actual: %s)".formatted(prefixName, expectedPrefix, prefix)); } } @@ -54,19 +54,19 @@ public class XdomeaMetaDataValidator { return prefix.endsWith(":") ? prefix.substring(0, prefix.length() - 1) : prefix; } - void validatePrimaryDocumentReferences(XtaFile xdomeaZipFile, List<String> primaryDocumentNames) throws ClientException { + void validatePrimaryDocumentReferences(XtaFile xdomeaZipFile, List<String> primaryDocumentNames) throws XtaClientException { var entryNames = Set.copyOf(zipFileEntryReader.getEntryNames(xdomeaZipFile.content())); for (var primaryDocumentName : primaryDocumentNames) { if (!entryNames.contains(primaryDocumentName)) { - throw new ClientException("Primary document reference '%s' not found in xdomea zip file!".formatted(primaryDocumentName)); + throw new XtaClientException("Primary document reference '%s' not found in xdomea zip file!".formatted(primaryDocumentName)); } } } - void validateZipFileName(String xdomeaZipFileName, String processId, String messageTypeCode) throws ClientException { + void validateZipFileName(String xdomeaZipFileName, String processId, String messageTypeCode) throws XtaClientException { var expectedXdomeaZipFileName = "%s_%s.zip".formatted(processId, messageTypeCode); if (!expectedXdomeaZipFileName.equals(xdomeaZipFileName)) { - throw new ClientException( + throw new XtaClientException( "Expect xdomea zip file name to equal '%s'! (actual: '%s')".formatted(expectedXdomeaZipFileName, xdomeaZipFileName) ); } diff --git a/src/main/java/de/ozgcloud/xta/client/xdomea/XdomeaXtaMessageCreator.java b/src/main/java/de/ozgcloud/xta/client/xdomea/XdomeaXtaMessageCreator.java index 2bd66d76bee063031959336990e92acea12daeb2..05c1949899aef4596294624f72055859fd210620 100644 --- a/src/main/java/de/ozgcloud/xta/client/xdomea/XdomeaXtaMessageCreator.java +++ b/src/main/java/de/ozgcloud/xta/client/xdomea/XdomeaXtaMessageCreator.java @@ -11,7 +11,7 @@ import org.w3c.dom.Document; import org.xml.sax.SAXException; import de.ozgcloud.common.errorhandling.TechnicalException; -import de.ozgcloud.xta.client.exception.ClientException; +import de.ozgcloud.xta.client.exception.XtaClientException; import de.ozgcloud.xta.client.model.XtaFile; import de.ozgcloud.xta.client.model.XtaMessage; import de.ozgcloud.xta.client.model.XtaMessageMetaData; @@ -32,7 +32,11 @@ public class XdomeaXtaMessageCreator { private final ZipFileEntryReader zipFileEntryReader; private final XdomeaMetaDataValidator metaDataValidator; - public XtaMessage createMessage(XtaFile xdomeaZipFile) throws ClientException { + public static XdomeaXtaMessageCreator createInstance() { + return XdomeaXtaMessageCreatorFactory.createInstance().create(); + } + + public XtaMessage createMessage(XtaFile xdomeaZipFile) throws XtaClientException { return XtaMessage.builder() .metaData(deriveValidMetaData(xdomeaZipFile)) .messageFile(xdomeaZipFile) @@ -40,18 +44,18 @@ public class XdomeaXtaMessageCreator { .build(); } - XtaMessageMetaData deriveValidMetaData(XtaFile xdomeaZipFile) throws ClientException { + XtaMessageMetaData deriveValidMetaData(XtaFile xdomeaZipFile) throws XtaClientException { try { var xdomeaXmlValues = readXdomeaXmlValues(xdomeaZipFile); var messageMetadataData = metadataMapper.mapXtaMessageMetadata(xdomeaXmlValues); metaDataValidator.validate(xdomeaZipFile, xdomeaXmlValues, messageMetadataData); return messageMetadataData; } catch (TechnicalException e) { - throw new ClientException("Failed to derive valid message metadata from xdomea document!", e); + throw new XtaClientException("Failed to derive valid message metadata from xdomea document!", e); } } - XdomeaXmlValues readXdomeaXmlValues(XtaFile xdomeaZipFile) throws ClientException { + XdomeaXmlValues readXdomeaXmlValues(XtaFile xdomeaZipFile) throws XtaClientException { var document = readXdomeaXmlDocument(xdomeaZipFile); return xdomeaValueReader.readValues(document); } diff --git a/src/main/java/de/ozgcloud/xta/client/xdomea/reader/XdomeaValueReader.java b/src/main/java/de/ozgcloud/xta/client/xdomea/reader/XdomeaValueReader.java index 90dcbeb54d465a6b50381ce74dc0ad77d744301f..52f3a87e813bf7f8666c6c8d7a08c3ec09c65aaf 100644 --- a/src/main/java/de/ozgcloud/xta/client/xdomea/reader/XdomeaValueReader.java +++ b/src/main/java/de/ozgcloud/xta/client/xdomea/reader/XdomeaValueReader.java @@ -7,7 +7,7 @@ import java.util.stream.Collectors; import org.w3c.dom.Document; -import de.ozgcloud.xta.client.exception.ClientException; +import de.ozgcloud.xta.client.exception.XtaClientException; import lombok.Builder; import lombok.RequiredArgsConstructor; @@ -34,7 +34,7 @@ public class XdomeaValueReader { private final Map<String, XmlValueReader> xmlValueReaders; - public XdomeaXmlValues readValues(Document xdomeaXmlDocument) throws ClientException { + public XdomeaXmlValues readValues(Document xdomeaXmlDocument) throws XtaClientException { return XdomeaXmlValues.builder() .processId(readRequiredValue(xdomeaXmlDocument, PROCESS_ID_XPATH)) .messageTypeCode(readRequiredValue(xdomeaXmlDocument, MESSAGE_TYPE_ID_SUFFIX_XPATH)) @@ -52,11 +52,11 @@ public class XdomeaValueReader { .toList(); } - String readRequiredValue(Document xdomeaXmlDocument, String xpathString) throws ClientException { + String readRequiredValue(Document xdomeaXmlDocument, String xpathString) throws XtaClientException { return getXmlValueReader(xpathString) .readNonEmptyTexts(xdomeaXmlDocument) .findFirst() - .orElseThrow(() -> new ClientException("Required value " + xpathString + " not found in xdomea xml document!")); + .orElseThrow(() -> new XtaClientException("Required value " + xpathString + " not found in xdomea xml document!")); } XmlValueReader getXmlValueReader(String xpath) { diff --git a/src/test/java/de/ozgcloud/xta/client/FetchMessageParameterTest.java b/src/test/java/de/ozgcloud/xta/client/FetchMessageParameterTest.java new file mode 100644 index 0000000000000000000000000000000000000000..4ae4671f86a94f8c4d55f714ef651c9ea8bb4649 --- /dev/null +++ b/src/test/java/de/ozgcloud/xta/client/FetchMessageParameterTest.java @@ -0,0 +1,108 @@ +package de.ozgcloud.xta.client; + +import static de.ozgcloud.xta.client.factory.MessageMetaDataTestFactory.*; +import static de.ozgcloud.xta.client.factory.XtaClientConfigTestFactory.*; +import static org.assertj.core.api.Assertions.*; + +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.function.Consumer; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import de.ozgcloud.xta.client.factory.XtaMessageMetaDataTestFactory; +import de.ozgcloud.xta.client.model.XtaMessage; + +class FetchMessageParameterTest { + + private Consumer<XtaMessage> processMessage; + + private FetchMessageParameter parameter; + + @BeforeEach + void setup() { + processMessage = message -> { + }; + parameter = new FetchMessageParameter(SELF_IDENTIFIER, processMessage, Set.of(MESSAGE_ID2)); + + } + + @DisplayName("with viewed message ids from") + @Nested + class TestWithViewedMessageIdsFrom { + + @DisplayName("should keep client identifier") + @Test + void shouldKeepClientIdentifier() { + var result = parameter.withViewedMessageIdsFrom(Collections.emptyList()); + + assertThat(result.clientIdentifier()).isEqualTo(SELF_IDENTIFIER); + } + + @DisplayName("should keep process message") + @Test + void shouldKeepProcessMessage() { + var result = parameter.withViewedMessageIdsFrom(Collections.emptyList()); + + assertThat(result.processMessage()).isEqualTo(processMessage); + } + + @DisplayName("should return copy") + @Test + void shouldReturnCopy() { + var result = parameter.withViewedMessageIdsFrom(Collections.emptyList()); + + assertThat(result.viewedMessageIds()).containsExactly(MESSAGE_ID2); + } + + @DisplayName("should return copy with additional viewed message ids") + @Test + void shouldReturnCopyWithAdditionalViewedMessageIds() { + var result = parameter.withViewedMessageIdsFrom(List.of(XtaMessageMetaDataTestFactory.create())); + + assertThat(result.viewedMessageIds()).containsExactlyInAnyOrder(MESSAGE_ID2, MESSAGE_ID); + } + + @DisplayName("should not change original with additional viewed message ids") + @Test + void shouldNotChangeOriginalWithAdditionalViewedMessageIds() { + parameter.withViewedMessageIdsFrom(List.of(XtaMessageMetaDataTestFactory.create())); + + assertThat(parameter.viewedMessageIds()).containsExactly(MESSAGE_ID2); + } + } + + @DisplayName("has message already been viewed") + @Nested + class TestHasMessageAlreadyBeenViewed { + + @DisplayName("should return true if message id is in viewed message ids") + @Test + void shouldReturnTrueIfMessageIdIsInViewedMessageIds() { + var messageMetaData = XtaMessageMetaDataTestFactory.createBuilder() + .messageId(MESSAGE_ID2) + .build(); + + var result = parameter.hasMessageAlreadyBeenViewed(messageMetaData); + + assertThat(result).isTrue(); + } + + @DisplayName("should return false if message id is not in viewed message ids") + @Test + void shouldReturnFalseIfMessageIdIsNotInViewedMessageIds() { + var messageMetaData = XtaMessageMetaDataTestFactory.createBuilder() + .messageId(MESSAGE_ID3) + .build(); + + var result = parameter.hasMessageAlreadyBeenViewed(messageMetaData); + + assertThat(result).isFalse(); + } + } + +} \ No newline at end of file diff --git a/src/test/java/de/ozgcloud/xta/client/XtaClientFactoryTest.java b/src/test/java/de/ozgcloud/xta/client/XtaClientFactoryTest.java index 30771668e180b8e605fce3fda7579e4d36e92f35..2461d7d296a5830831890f4dec672de33a3a2e53 100644 --- a/src/test/java/de/ozgcloud/xta/client/XtaClientFactoryTest.java +++ b/src/test/java/de/ozgcloud/xta/client/XtaClientFactoryTest.java @@ -12,8 +12,10 @@ import org.mockito.Mock; import de.ozgcloud.xta.client.config.XtaClientConfig; import de.ozgcloud.xta.client.config.XtaConfigValidator; -import de.ozgcloud.xta.client.core.WrappedXtaService; -import de.ozgcloud.xta.client.core.WrappedXtaServiceFactory; +import de.ozgcloud.xta.client.core.XtaClientService; +import de.ozgcloud.xta.client.core.XtaClientServiceFactory; +import de.ozgcloud.xta.client.core.XtaExceptionHandler; +import de.ozgcloud.xta.client.core.XtaExceptionHandlerFactory; import lombok.SneakyThrows; class XtaClientFactoryTest { @@ -21,9 +23,11 @@ class XtaClientFactoryTest { @Mock private XtaConfigValidator configValidator; @Mock - private WrappedXtaServiceFactory wrappedXtaServiceFactory; + private XtaClientServiceFactory xtaClientServiceFactory; @Mock private XtaClientConfig config; + @Mock + private XtaExceptionHandlerFactory xtaExceptionHandlerFactory; @InjectMocks private XtaClientFactory factory; @@ -31,32 +35,16 @@ class XtaClientFactoryTest { @DisplayName("create") @Nested class TestCreate { - @Mock - private WrappedXtaService service; + private XtaClientService service; + @Mock + private XtaExceptionHandler exceptionHandler; @BeforeEach @SneakyThrows void mock() { - when(wrappedXtaServiceFactory.create()).thenReturn(service); - } - - @DisplayName("should have service") - @Test - @SneakyThrows - void shouldHaveService() { - var client = factory.create(); - - assertThat(client.getService()).isEqualTo(service); - } - - @DisplayName("should have config") - @Test - @SneakyThrows - void shouldHaveConfig() { - var client = factory.create(); - - assertThat(client.getConfig()).isEqualTo(config); + when(xtaClientServiceFactory.create()).thenReturn(service); + when(xtaExceptionHandlerFactory.create()).thenReturn(exceptionHandler); } @DisplayName("should call validate") @@ -67,5 +55,18 @@ class XtaClientFactoryTest { verify(configValidator).validate(config); } + + @DisplayName("should return") + @Test + @SneakyThrows + void shouldReturn() { + assertThat(factory.create()) + .usingRecursiveComparison() + .isEqualTo(XtaClient.builder() + .config(config) + .service(service) + .exceptionHandler(exceptionHandler) + .build()); + } } } \ No newline at end of file diff --git a/src/test/java/de/ozgcloud/xta/client/XtaClientITCase.java b/src/test/java/de/ozgcloud/xta/client/XtaClientITCase.java index cf0c0bc44e15d973e457666aeeb5ac3332943a1a..64681cfe9abead1d25b6f0d8626241b0f90244a7 100644 --- a/src/test/java/de/ozgcloud/xta/client/XtaClientITCase.java +++ b/src/test/java/de/ozgcloud/xta/client/XtaClientITCase.java @@ -1,149 +1,312 @@ package de.ozgcloud.xta.client; +import static de.ozgcloud.xta.client.XtaClient.*; import static de.ozgcloud.xta.client.extension.XtaServerSetupExtensionTestUtil.*; +import static java.util.Collections.*; import static org.assertj.core.api.Assertions.*; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Predicate; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; +import de.ozgcloud.xta.client.exception.XtaClientException; +import de.ozgcloud.xta.client.extension.StaticStringListAppender; import de.ozgcloud.xta.client.extension.XtaMessageExampleLoader; -import de.ozgcloud.xta.client.extension.XtaServerSetupExtensionTestUtil; import de.ozgcloud.xta.client.extension.XtaTestServerSetupExtension; +import de.ozgcloud.xta.client.model.XtaIdentifier; import de.ozgcloud.xta.client.model.XtaMessage; +import de.ozgcloud.xta.client.model.XtaMessageMetaData; import de.ozgcloud.xta.client.model.XtaMessageStatus; -import genv3.de.xoev.transport.xta.x211.InvalidMessageIDException; +import de.ozgcloud.xta.client.model.XtaTransportReport; import lombok.SneakyThrows; class XtaClientITCase { @RegisterExtension static final XtaTestServerSetupExtension XTA_TEST_SERVER_SETUP_EXTENSION = new XtaTestServerSetupExtension(); + static final int TWO_MAX_LIST_ITEMS = 2; + + private XtaClient silentTestClient; + private XtaClient testClient; - private XtaClient client; + private List<XtaMessageMetaData> supportCheckedMetadataItems; + private List<XtaMessage> processedMessages; + private Consumer<XtaMessage> processMessageDummy; + private Predicate<XtaMessageMetaData> isSupportedDummy; @BeforeEach @SneakyThrows void setup() { - client = XTA_TEST_SERVER_SETUP_EXTENSION.getClient(); + processMessageDummy = (message) -> { + }; + supportCheckedMetadataItems = new ArrayList<>(); + isSupportedDummy = (metaData) -> true; + processedMessages = new ArrayList<>(); + silentTestClient = XTA_TEST_SERVER_SETUP_EXTENSION.getSilentTestClient(); + + StaticStringListAppender.clearLogLines(); + closeMessagesForAllReaders(); } - @DisplayName("get messages metadata") - @Nested - class TestGetMessagesMetadata { + private void closeMessagesForAllReaders() { + var silentTestClientConfig = XTA_TEST_SERVER_SETUP_EXTENSION.getSilentTestClientConfig(); + closeAllMessages(silentTestClientConfig, READER_CLIENT_IDENTIFIER1); + closeAllMessages(silentTestClientConfig, READER_CLIENT_IDENTIFIER2); + closeAllMessages(silentTestClientConfig, READER_CLIENT_IDENTIFIER3); + } - @DisplayName("with no messages") - @Nested - class TestWithNoMessages { + @DisplayName("fetch messages") + @Nested + class TestFetchMessages { - @DisplayName("should return zero pending messages") - @Test - @SneakyThrows - void shouldReturnZeroPendingMessages() { - var result = client.getMessagesMetadata(READER_CLIENT_IDENTIFIER1.value()); + private List<XtaMessage> sendMessages; + private List<String> sendMessageIds; - assertThat(result.pendingMessageCount()).isZero(); - } + @BeforeEach + void setup() { + sendMessages = List.of( + createMessage("dfoerdermittel", AUTHOR_CLIENT_IDENTIFIER, READER_CLIENT_IDENTIFIER1), + createMessage("dfoerdermittel", AUTHOR_CLIENT_IDENTIFIER, READER_CLIENT_IDENTIFIER2), + createMessage("abgabe0401-kleiner-waffenschein", AUTHOR_CLIENT_IDENTIFIER, READER_CLIENT_IDENTIFIER2), + createMessage("versammlungsanzeige", AUTHOR_CLIENT_IDENTIFIER2, READER_CLIENT_IDENTIFIER2), + createMessage("versammlungsanzeige", AUTHOR_CLIENT_IDENTIFIER2, READER_CLIENT_IDENTIFIER3), + createMessage("versammlungsanzeige", AUTHOR_CLIENT_IDENTIFIER3, READER_CLIENT_IDENTIFIER3) + ); + sendMessageIds = sendMessages.stream() + .map(message -> sendTestMessage(silentTestClient, message)) + .toList(); } - @DisplayName("with one message") - @Nested - class TestWithOneMessage { - - @BeforeEach - void setup() { - XTA_TEST_SERVER_SETUP_EXTENSION.sendTestMessage(); - } + private XtaMessage createMessage(String messageLabel, XtaIdentifier author, XtaIdentifier reader) { + return XtaMessageExampleLoader.load( + XtaMessageExampleLoader.MessageExampleConfig.builder() + .messageLabel(messageLabel) + .reader(reader) + .author(author) + .build()); + } - @DisplayName("should return one pending message for client") - @Test - @SneakyThrows - void shouldReturnOnePendingMessageClient() { - var result = client.getMessagesMetadata(READER_CLIENT_IDENTIFIER1.value()); + @DisplayName("should throw exception on connection failure") + @Test + @SneakyThrows + void shouldThrowExceptionOnConnectionFailure() { + setupClientWithoutTrustStore(); - assertThat(result.pendingMessageCount()).isOne(); - } + assertThatThrownBy(() -> testClient.fetchMessages((message) -> fail("Should not process any message!"))) + .isInstanceOf(XtaClientException.class); + } - @DisplayName("should return no pending message for another client") - @Test - @SneakyThrows - void shouldReturnNoPendingMessageForAnotherClient() { - var result = client.getMessagesMetadata(READER_CLIENT_IDENTIFIER2.value()); + @DisplayName("should fetch no messages if no client identifier is configured") + @Test + void shouldFetchNoMessagesIfNoClientIdentifierIsConfigured() { + setupClientWithIdentifiers(emptyList()); - assertThat(result.pendingMessageCount()).isZero(); - } + var messages = fetchMessages(); + assertThat(supportCheckedMetadataItems).isEmpty(); + assertThat(processedMessages).isEmpty(); + assertThat(messages).isEmpty(); } - } + @DisplayName("should fetch no messages if client identifier has no messages pending") + @Test + void shouldFetchNoMessagesIfClientIdentifierHasNoMessagesPending() { + setupClientWithIdentifiers(List.of(AUTHOR_CLIENT_IDENTIFIER)); - @DisplayName("get message") - @Nested - class TestGetMessage { + var messages = fetchMessages(); - private String messageId; - private XtaMessage message; + assertThat(supportCheckedMetadataItems).isEmpty(); + assertThat(processedMessages).isEmpty(); + assertThat(messages).isEmpty(); + } - @BeforeEach + @DisplayName("should fetch messages from first reader") + @Test @SneakyThrows - void setup() { - var messageConfig = XtaMessageExampleLoader.MessageExampleConfig.builder() - .messageLabel("dfoerdermittel") - .reader(READER_CLIENT_IDENTIFIER1) - .author(AUTHOR_CLIENT_IDENTIFIER) - .build(); - message = XtaMessageExampleLoader.load(messageConfig); - messageId = XtaServerSetupExtensionTestUtil.sendTestMessage(client, message); + void shouldFetchMessagesFromFirstReader() { + setupClientWithIdentifiers(List.of(READER_CLIENT_IDENTIFIER1)); + + var transportReports = fetchMessages(); + + assertThat(supportCheckedMetadataItems).hasSize(1); + assertThatMessages(processedMessages).containExactlyInAnyOrder(sendMessages.getFirst()); + assertThatTransportReports(transportReports) + .reportExactlyFor(processedMessages) + .haveExactlyClosedStatusFor(messageIdBySendIndex(0)); } - @DisplayName("should return message with green status") + @DisplayName("should fetch messages from second reader") @Test - @SneakyThrows - void shouldReturnMessageWithGreenStatus() { - var result = client.getMessage(READER_CLIENT_IDENTIFIER1.value(), messageId); + void shouldFetchMessagesFromSecondReader() { + setupClientWithIdentifiers(List.of(READER_CLIENT_IDENTIFIER2)); - assertThat(result.message().metaData().messageId()).isEqualTo(messageId); - assertThat(result.transportReport().metaData().messageId()).isEqualTo(messageId); - assertThat(result.transportReport().status()).isEqualTo(XtaMessageStatus.GREEN); + var transportReports = fetchMessages(); + + assertThat(supportCheckedMetadataItems).hasSize(3); + assertThatMessages(processedMessages).containExactlyInAnyOrder(sendMessages.get(1), sendMessages.get(2), sendMessages.get(3)); + assertThatTransportReports(transportReports) + .reportExactlyFor(processedMessages) + .haveExactlyClosedStatusFor(messageIdBySendIndex(1), messageIdBySendIndex(2), messageIdBySendIndex(3)); } - @DisplayName("should return message with correct message file content") + @DisplayName("should fetch messages from first and second reader") @Test - @SneakyThrows - void shouldReturnMessageWithCorrectMessageFileContent() { - var messageContent = extractMessageFileContent(message); + void shouldFetchMessagesFromFirstAndSecondReader() { + setupClientWithIdentifiers(List.of(READER_CLIENT_IDENTIFIER1, READER_CLIENT_IDENTIFIER2)); + + var transportReports = fetchMessages(); + + assertThat(supportCheckedMetadataItems).hasSize(1 + 3); + assertThatMessages(processedMessages).containExactlyInAnyOrder( + sendMessages.get(0), + sendMessages.get(1), sendMessages.get(2), sendMessages.get(3) + ); + assertThatTransportReports(transportReports) + .reportExactlyFor(processedMessages) + .haveExactlyClosedStatusFor( + messageIdBySendIndex(0), + messageIdBySendIndex(1), messageIdBySendIndex(2), messageIdBySendIndex(3) + ); + } - var result = client.getMessage(READER_CLIENT_IDENTIFIER1.value(), messageId); - var resultContent = extractMessageFileContent(result.message()); + @DisplayName("should fetch messages from first, second and third reader") + @Test + void shouldFetchMessagesFromFirstSecondAndThirdReader() { + setupClientWithIdentifiers(List.of(READER_CLIENT_IDENTIFIER1, READER_CLIENT_IDENTIFIER2, READER_CLIENT_IDENTIFIER3)); + + var transportReports = fetchMessages(); + + assertThat(supportCheckedMetadataItems).hasSize(1 + 3 + 2); + assertThatMessages(processedMessages).containExactlyInAnyOrder( + sendMessages.get(0), + sendMessages.get(1), sendMessages.get(2), sendMessages.get(3), + sendMessages.get(4), sendMessages.get(5) + ); + assertThatTransportReports(transportReports) + .reportExactlyFor(processedMessages) + .haveExactlyClosedStatusFor( + messageIdBySendIndex(0), + messageIdBySendIndex(1), messageIdBySendIndex(2), messageIdBySendIndex(3), + messageIdBySendIndex(4), messageIdBySendIndex(5) + ); + } - assertThat(messageContent).isEqualTo(resultContent); + @DisplayName("should close messages only if no exception occurs during processing, with no exception for author1") + @Test + void shouldCloseMessagesOnlyIfNoExceptionOccursDuringProcessingWithNoExceptionForAuthor1() { + setupClientWithIdentifiers(List.of(READER_CLIENT_IDENTIFIER1, READER_CLIENT_IDENTIFIER2, READER_CLIENT_IDENTIFIER3)); + processMessageDummy = message -> throwRuntimeExceptionExceptForAuthorIdentifier(message, AUTHOR_CLIENT_IDENTIFIER); + + var transportReports = fetchMessages(); + + assertThat(supportCheckedMetadataItems).hasSize(1 + 3 + 2); + assertThat(hasLogLineContaining(NO_MESSAGE_CLOSED_WARNING)).isFalse(); + assertThatMessages(processedMessages).containExactlyInAnyOrder( + sendMessages.get(0), + sendMessages.get(1), sendMessages.get(2), sendMessages.get(3), + sendMessages.get(4), sendMessages.get(5) + ); + assertThatTransportReports(transportReports) + .reportExactlyFor(processedMessages) + .haveExactlyClosedStatusFor( + messageIdBySendIndex(0), + messageIdBySendIndex(1), messageIdBySendIndex(2) + ); } - @DisplayName("should not show message id for a closed message in status list") + @DisplayName("should close messages only if no exception occurs during processing, with no exception for author3") @Test - @SneakyThrows - void shouldNotShowMessageIdForClosedMessageInStatusList() { - assertThatNoException().isThrownBy(() -> client.getMessage(READER_CLIENT_IDENTIFIER1.value(), messageId)); - var metadataResult = client.getMessagesMetadata(READER_CLIENT_IDENTIFIER1.value()); - if (!metadataResult.messages().isEmpty()) { - assertThat(metadataResult.messages()).allMatch(metadata -> !messageId.equals(metadata.messageId())); + void shouldCloseMessagesOnlyIfNoExceptionOccursDuringProcessingWithNoExceptionForAuthor3() { + setupClientWithIdentifiers(List.of(READER_CLIENT_IDENTIFIER1, READER_CLIENT_IDENTIFIER2, READER_CLIENT_IDENTIFIER3)); + processMessageDummy = message -> throwRuntimeExceptionExceptForAuthorIdentifier(message, AUTHOR_CLIENT_IDENTIFIER3); + + var transportReports = fetchMessages(); + + assertThat(supportCheckedMetadataItems).hasSize(1 + 2 + 2); + assertThat(hasLogLineContaining(NO_MESSAGE_CLOSED_WARNING)).isTrue(); + assertThatMessages(processedMessages).containMetaDataExactlyInAnyOrder( + sendMessages.get(0).metaData(), + supportCheckedMetadataItems.get(1), supportCheckedMetadataItems.get(2), + sendMessages.get(4).metaData(), sendMessages.get(5).metaData() + ); + assertThatTransportReports(transportReports) + .reportExactlyFor(processedMessages) + .haveExactlyClosedStatusFor( + messageIdBySendIndex(5) + ); + } + + private void throwRuntimeExceptionExceptForAuthorIdentifier(XtaMessage message, XtaIdentifier authorIdentifier) { + var authorId = message.metaData().authorIdentifier().value(); + var readerId = message.metaData().readerIdentifier().value(); + if (!authorId.equals(authorIdentifier.value())) { + throw new RuntimeException("Test exception for message with author '%s' and reader '%s'!".formatted(authorId, readerId)); } } - @DisplayName("should throw invalid message id exception for modified message id") + @DisplayName("should process messages only if supported, with support for author1") @Test - void shouldThrowInvalidMessageIdExceptionForModifiedMessageId() { - assertThatThrownBy(() -> client.getMessage(READER_CLIENT_IDENTIFIER1.value(), messageId + "1")) - .isInstanceOf(InvalidMessageIDException.class); + void shouldProcessMessagesOnlyIfSupportedWithSupportForAuthor1() { + setupClientWithIdentifiers(List.of(READER_CLIENT_IDENTIFIER1, READER_CLIENT_IDENTIFIER2, READER_CLIENT_IDENTIFIER3)); + isSupportedDummy = metaData -> metaData.authorIdentifier().value().equals(AUTHOR_CLIENT_IDENTIFIER.value()); + + var transportReports = fetchMessages(); + + assertThat(supportCheckedMetadataItems).hasSize(1 + 3 + 2); + assertThat(hasLogLineContaining(NO_MESSAGE_CLOSED_WARNING)).isFalse(); + assertThatMessages(processedMessages).containExactlyInAnyOrder(sendMessages.get(0), sendMessages.get(1), sendMessages.get(2)); + assertThatTransportReports(transportReports) + .reportExactlyFor(processedMessages) + .haveExactlyClosedStatusFor(messageIdBySendIndex(0), messageIdBySendIndex(1), messageIdBySendIndex(2)); } - @DisplayName("should throw invalid message id exception for other client") + @DisplayName("should process messages only if supported, with support for author3") @Test - void shouldThrowInvalidMessageIdExceptionForOtherClient() { - assertThatThrownBy(() -> client.getMessage(READER_CLIENT_IDENTIFIER2.value(), messageId)) - .isInstanceOf(InvalidMessageIDException.class); + void shouldProcessMessagesOnlyIfSupportedWithSupportForAuthor3() { + setupClientWithIdentifiers(List.of(READER_CLIENT_IDENTIFIER1, READER_CLIENT_IDENTIFIER2, READER_CLIENT_IDENTIFIER3)); + isSupportedDummy = metaData -> metaData.authorIdentifier().value().equals(AUTHOR_CLIENT_IDENTIFIER3.value()); + + var transportReports = fetchMessages(); + + assertThat(supportCheckedMetadataItems).hasSize(1 + 2 + 2); + assertThat(hasLogLineContaining(NO_MESSAGE_CLOSED_WARNING)).isTrue(); + assertThatMessages(processedMessages).containExactlyInAnyOrder(sendMessages.get(5)); + assertThatTransportReports(transportReports) + .reportExactlyFor(processedMessages) + .haveExactlyClosedStatusFor(messageIdBySendIndex(5)); + } + + @SneakyThrows + private void setupClientWithIdentifiers(List<XtaIdentifier> identifiers) { + testClient = XtaClient.from( + XTA_TEST_SERVER_SETUP_EXTENSION.createSpecificClientConfigBuilder() + .clientIdentifiers(identifiers) + .maxListItems(TWO_MAX_LIST_ITEMS) + .isMessageSupported(metaData -> { + supportCheckedMetadataItems.add(metaData); + return isSupportedDummy.test(metaData); + }) + .build() + ); + } + + private String messageIdBySendIndex(int sendIndex) { + return sendMessageIds.get(sendIndex); + } + + @SneakyThrows + private List<XtaTransportReport> fetchMessages() { + return testClient.fetchMessages((message) -> { + processedMessages.add(message); + processMessageDummy.accept(message); + }); } } @@ -158,20 +321,41 @@ class XtaClientITCase { var messageConfig = XtaMessageExampleLoader.MessageExampleConfig.builder() .messageLabel("dfoerdermittel") .reader(READER_CLIENT_IDENTIFIER1) - .author(READER_CLIENT_IDENTIFIER1) + .author(AUTHOR_CLIENT_IDENTIFIER) .build(); message = XtaMessageExampleLoader.load(messageConfig); } + @DisplayName("should throw exception on connection failure") + @Test + @SneakyThrows + void shouldThrowExceptionOnConnectionFailure() { + setupClientWithoutTrustStore(); + + assertThatThrownBy(() -> testClient.sendMessage(message)) + .isInstanceOf(XtaClientException.class); + } + @DisplayName("should return transport report with open status") @Test @SneakyThrows void shouldReturnTransportReportWithOpenStatus() { - var transportReport = client.sendMessage(message); + var transportReport = silentTestClient.sendMessage(message); assertThat(transportReport.status()).isEqualTo(XtaMessageStatus.OPEN); } + } + @SneakyThrows + private void setupClientWithoutTrustStore() { + testClient = XtaClient.from( + XTA_TEST_SERVER_SETUP_EXTENSION.createSpecificClientConfigBuilder() + .trustStore(null) + .clientIdentifiers(List.of(READER_CLIENT_IDENTIFIER1)) + .maxListItems(TWO_MAX_LIST_ITEMS) + .isMessageSupported(metaData -> fail("Should not process any message!")) + .build() + ); } } diff --git a/src/test/java/de/ozgcloud/xta/client/XtaClientRemoteITCase.java b/src/test/java/de/ozgcloud/xta/client/XtaClientRemoteITCase.java index 42f008a07a14ec9c27d72fd802907dcead5db1af..0ceac4857e86725204502ed1d2828a864c891ce7 100644 --- a/src/test/java/de/ozgcloud/xta/client/XtaClientRemoteITCase.java +++ b/src/test/java/de/ozgcloud/xta/client/XtaClientRemoteITCase.java @@ -1,8 +1,17 @@ package de.ozgcloud.xta.client; +import static de.ozgcloud.xta.client.XtaClientITCase.*; import static de.ozgcloud.xta.client.extension.XtaServerSetupExtensionTestUtil.*; +import static java.util.Collections.*; import static org.assertj.core.api.Assertions.*; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.stream.Stream; + +import org.junit.Ignore; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -13,14 +22,17 @@ import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; +import de.ozgcloud.xta.client.config.XtaClientConfig; +import de.ozgcloud.xta.client.extension.StaticStringListAppender; import de.ozgcloud.xta.client.extension.XtaMessageExampleLoader; import de.ozgcloud.xta.client.extension.XtaRemoteServerSetupExtension; import de.ozgcloud.xta.client.model.XtaFile; +import de.ozgcloud.xta.client.model.XtaIdentifier; import de.ozgcloud.xta.client.model.XtaMessage; +import de.ozgcloud.xta.client.model.XtaMessageMetaData; import de.ozgcloud.xta.client.model.XtaMessageStatus; -import de.ozgcloud.xta.client.xdomea.XdomeaXtaMessageCreatorFactory; -import genv3.de.xoev.transport.xta.x211.InvalidMessageIDException; -import genv3.de.xoev.transport.xta.x211.MessageSchemaViolationException; +import de.ozgcloud.xta.client.model.XtaTransportReport; +import de.ozgcloud.xta.client.xdomea.XdomeaXtaMessageCreator; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; @@ -32,134 +44,205 @@ import lombok.extern.slf4j.Slf4j; "This test requires the path KOP_SH_KIEL_{DEV,TEST}_PATH and password KOP_SH_KIEL_{DEV,TEST}_PASSWORD of KOP_SH_KIEL_DEV.p12 and OZG-CLOUD_SH_KIEL_TEST.pfx. " + "Additionally, the endpoint of the DEV-xta-server at li33-0005.dp.dsecurecloud.de must be reachable." ) +@Ignore class XtaClientRemoteITCase { @RegisterExtension static final XtaRemoteServerSetupExtension XTA_REMOTE_SERVER_SETUP_EXTENSION = new XtaRemoteServerSetupExtension(); - private XtaClient authorClient; - private XtaClient readerClient; + private static final XdomeaXtaMessageCreator XDOMEA_XTA_MESSAGE_CREATOR = XdomeaXtaMessageCreator.createInstance(); + + private XtaClient testClient; + private XtaClient silentTestClient; + private XtaClient devClient; + private XtaClient silentDevClient; + + private List<XtaMessageMetaData> supportCheckedMetadataItems; + private List<XtaMessage> processedMessages; + private Consumer<XtaMessage> processMessageDummy; + private Predicate<XtaMessageMetaData> isSupportedDummy; @BeforeEach @SneakyThrows void setup() { - authorClient = XTA_REMOTE_SERVER_SETUP_EXTENSION.getAuthorClient(); - readerClient = XTA_REMOTE_SERVER_SETUP_EXTENSION.getReaderClient(); + processMessageDummy = (message) -> { + }; + supportCheckedMetadataItems = new ArrayList<>(); + isSupportedDummy = (metaData) -> true; + processedMessages = new ArrayList<>(); + + testClient = XTA_REMOTE_SERVER_SETUP_EXTENSION.getTestClient(); + silentTestClient = XTA_REMOTE_SERVER_SETUP_EXTENSION.getSilentTestClient(); + devClient = XTA_REMOTE_SERVER_SETUP_EXTENSION.getDevClient(); + silentDevClient = XTA_REMOTE_SERVER_SETUP_EXTENSION.getSilentDevClient(); + + // Fail if any message pending, to ensure that we do not clear existing messages in the DEV environment + failIfAnyMessagePending(XTA_REMOTE_SERVER_SETUP_EXTENSION.getSilentDevClientConfig(), DEV_READER_CLIENT_IDENTIFIER); + failIfAnyMessagePending(XTA_REMOTE_SERVER_SETUP_EXTENSION.getSilentTestClientConfig(), TEST_READER_CLIENT_IDENTIFIER); + + StaticStringListAppender.clearLogLines(); } - @DisplayName("get messages metadata") - @Nested - class TestGetMessagesMetadata { + @AfterEach + void cleanup() { + closeMessagesForAllReaders(); + } + + private void closeMessagesForAllReaders() { + closeAllMessages(XTA_REMOTE_SERVER_SETUP_EXTENSION.getSilentDevClientConfig(), DEV_READER_CLIENT_IDENTIFIER); + closeAllMessages(XTA_REMOTE_SERVER_SETUP_EXTENSION.getSilentTestClientConfig(), TEST_READER_CLIENT_IDENTIFIER); + } - @DisplayName("with no messages") - @Nested - class TestWithNoMessages { + @DisplayName("fetch messages") + @Nested + class TestFetchMessages { - @DisplayName("should return zero pending messages") - @Test - @SneakyThrows - void shouldReturnZeroPendingMessages() { - var result = readerClient.getMessagesMetadata(READER_CLIENT_IDENTIFIER1.value()); + private List<XtaMessage> sendMessages; + private List<String> sendMessageIds; - assertThat(result.pendingMessageCount()).isZero(); - } + @BeforeEach + void setup() { + sendMessages = List.of( + createMessage("dfoerdermittel", DEV_READER_CLIENT_IDENTIFIER, DEV_READER_CLIENT_IDENTIFIER), + createMessage("dfoerdermittel", DEV_READER_CLIENT_IDENTIFIER, TEST_READER_CLIENT_IDENTIFIER), + createMessage("abgabe0401-kleiner-waffenschein", TEST_AUTHOR_CLIENT_IDENTIFIER, TEST_READER_CLIENT_IDENTIFIER), + createMessage("dfoerdermittel", TEST_READER_CLIENT_IDENTIFIER, TEST_READER_CLIENT_IDENTIFIER) + ); + sendMessageIds = sendMessages.stream() + .map(message -> sendTestMessage( + message.metaData().authorIdentifier().equals(DEV_READER_CLIENT_IDENTIFIER) + ? silentDevClient + : silentTestClient, + message)) + .toList(); } - @DisplayName("with one message") - @Nested - class TestWithOneMessage { + @DisplayName("should fetch no messages if no client identifier is configured") + @Test + void shouldFetchNoMessagesIfNoClientIdentifierIsConfigured() { + setupClientsWithIdentifiers(emptyList()); - @BeforeEach - void setup() { - XTA_REMOTE_SERVER_SETUP_EXTENSION.sendTestMessage(); - } + var messages = fetchMessages(); - @DisplayName("should return one pending message for client") - @Test - @SneakyThrows - void shouldReturnOnePendingMessageClient() { - var result = readerClient.getMessagesMetadata(READER_CLIENT_IDENTIFIER1.value()); + assertThat(supportCheckedMetadataItems).isEmpty(); + assertThat(processedMessages).isEmpty(); + assertThat(messages).isEmpty(); + } - assertThat(result.pendingMessageCount()).isOne(); - } + @DisplayName("should fetch no messages if client identifier has no messages pending") + @Test + void shouldFetchNoMessagesIfClientIdentifierHasNoMessagesPending() { + setupClientsWithIdentifiers(List.of(TEST_AUTHOR_CLIENT_IDENTIFIER)); - @DisplayName("should return no pending message for another client") - @Test - @SneakyThrows - void shouldReturnNoPendingMessageForAnotherClient() { - var result = readerClient.getMessagesMetadata(READER_CLIENT_IDENTIFIER2.value()); + var messages = fetchMessages(); - assertThat(result.pendingMessageCount()).isZero(); - } + assertThat(supportCheckedMetadataItems).isEmpty(); + assertThat(processedMessages).isEmpty(); + assertThat(messages).isEmpty(); } - } - - @DisplayName("get message") - @Nested - class TestGetMessage { + @DisplayName("should fetch messages from first reader") + @Test + @SneakyThrows + void shouldFetchMessagesFromFirstReader() { + setupClientsWithIdentifiers(List.of(DEV_READER_CLIENT_IDENTIFIER)); - private String messageId; - private XtaMessage message; + var transportReports = fetchMessages(); - @BeforeEach - @SneakyThrows - void setup() { - var messageConfig = XtaMessageExampleLoader.MessageExampleConfig.builder() - .messageLabel("dfoerdermittel") - .reader(READER_CLIENT_IDENTIFIER1) - .author(AUTHOR_CLIENT_IDENTIFIER) - .build(); - message = XtaMessageExampleLoader.load(messageConfig); - messageId = XTA_REMOTE_SERVER_SETUP_EXTENSION.sendTestMessage(message); + assertThat(supportCheckedMetadataItems).hasSize(1); + assertThatMessages(processedMessages).containExactlyInAnyOrder(sendMessages.getFirst()); + assertThatTransportReports(transportReports) + .reportExactlyFor(processedMessages) + .haveExactlyClosedStatusFor(messageIdBySendIndex(0)); } - @DisplayName("should return message with green status") + @DisplayName("should fetch messages from second reader") @Test - @SneakyThrows - void shouldReturnMessageWithGreenStatus() { - var result = readerClient.getMessage(READER_CLIENT_IDENTIFIER1.value(), messageId); + void shouldFetchMessagesFromSecondReader() { + setupClientsWithIdentifiers(List.of(TEST_READER_CLIENT_IDENTIFIER)); + + var transportReports = fetchMessages(); - assertThat(result.message().metaData().messageId()).isEqualTo(messageId); - assertThat(result.transportReport().metaData().messageId()).isEqualTo(messageId); - assertThat(result.transportReport().status()).isEqualTo(XtaMessageStatus.GREEN); + assertThat(supportCheckedMetadataItems).hasSize(3); + assertThatMessages(processedMessages).containExactlyInAnyOrder(sendMessages.get(1), sendMessages.get(2), sendMessages.get(3)); + assertThatTransportReports(transportReports) + .reportExactlyFor(processedMessages) + .haveExactlyClosedStatusFor(messageIdBySendIndex(1), messageIdBySendIndex(2), messageIdBySendIndex(3)); } - @DisplayName("should return message with correct message file content") + @DisplayName("should fetch messages from first and second reader") @Test + void shouldFetchMessagesFromFirstAndSecondReader() { + setupClientsWithIdentifiers(List.of(DEV_READER_CLIENT_IDENTIFIER, TEST_READER_CLIENT_IDENTIFIER)); + + var transportReports = fetchMessages(); + + assertThat(supportCheckedMetadataItems).hasSize(1 + 3); + assertThatMessages(processedMessages).containExactlyInAnyOrder( + sendMessages.get(0), + sendMessages.get(1), sendMessages.get(2), sendMessages.get(3) + ); + assertThatTransportReports(transportReports) + .reportExactlyFor(processedMessages) + .haveExactlyClosedStatusFor( + messageIdBySendIndex(0), + messageIdBySendIndex(1), messageIdBySendIndex(2), messageIdBySendIndex(3) + ); + } + @SneakyThrows - void shouldReturnMessageWithCorrectMessageFileContent() { - var messageContent = extractMessageFileContent(message); + private void setupClientsWithIdentifiers(List<XtaIdentifier> identifiers) { + devClient = createClientWithIdentifiersAndClientCert( + identifiers, + XTA_REMOTE_SERVER_SETUP_EXTENSION.getDevClientConfig().getClientCertKeystore() + ); + testClient = createClientWithIdentifiersAndClientCert( + identifiers, + XTA_REMOTE_SERVER_SETUP_EXTENSION.getTestClientConfig().getClientCertKeystore() + ); + } - var result = readerClient.getMessage(READER_CLIENT_IDENTIFIER1.value(), messageId); - var resultContent = extractMessageFileContent(result.message()); + @SneakyThrows + private XtaClient createClientWithIdentifiersAndClientCert(List<XtaIdentifier> identifiers, XtaClientConfig.KeyStore clientCertKeyStore) { + return XtaClient.from( + XTA_REMOTE_SERVER_SETUP_EXTENSION.createSpecificClientConfigBuilder() + .clientCertKeystore(clientCertKeyStore) + .clientIdentifiers(identifiers) + .maxListItems(TWO_MAX_LIST_ITEMS) + .isMessageSupported(this::isSupported) + .build() + ); + } - assertThat(messageContent).isEqualTo(resultContent); + private boolean isSupported(XtaMessageMetaData metaData) { + supportCheckedMetadataItems.add(metaData); + return isSupportedDummy.test(metaData); + } + + private String messageIdBySendIndex(int sendIndex) { + return sendMessageIds.get(sendIndex); + } + + private List<XtaTransportReport> fetchMessages() { + return Stream.concat( + fetchDevMessages().stream(), + fetchTestMessages().stream() + ).toList(); } - @DisplayName("should not show message id for a closed message in status list") - @Test @SneakyThrows - void shouldNotShowMessageIdForClosedMessageInStatusList() { - assertThatNoException().isThrownBy(() -> readerClient.getMessage(READER_CLIENT_IDENTIFIER1.value(), messageId)); - var metadataResult = readerClient.getMessagesMetadata(READER_CLIENT_IDENTIFIER1.value()); - if (!metadataResult.messages().isEmpty()) { - assertThat(metadataResult.messages()).allMatch(metadata -> !messageId.equals(metadata.messageId())); - } + private List<XtaTransportReport> fetchDevMessages() { + return devClient.fetchMessages(this::processMessage); } - @DisplayName("should throw invalid message id exception for modified message id") - @Test - void shouldThrowInvalidMessageIdExceptionForModifiedMessageId() { - assertThatThrownBy(() -> readerClient.getMessage(READER_CLIENT_IDENTIFIER1.value(), messageId + "1")) - .isInstanceOf(InvalidMessageIDException.class); + @SneakyThrows + private List<XtaTransportReport> fetchTestMessages() { + return testClient.fetchMessages(this::processMessage); } - @DisplayName("should throw invalid message id exception for other client") - @Test - void shouldThrowInvalidMessageIdExceptionForOtherClient() { - assertThatThrownBy(() -> readerClient.getMessage(READER_CLIENT_IDENTIFIER2.value(), messageId)) - .isInstanceOf(InvalidMessageIDException.class); + private void processMessage(XtaMessage message) { + processedMessages.add(message); + processMessageDummy.accept(message); } } @@ -168,48 +251,39 @@ class XtaClientRemoteITCase { @Nested class TestSendMessage { - @AfterEach - @SneakyThrows - void afterEach() { - closeAllMessages(readerClient, READER_CLIENT_IDENTIFIER1); - } - @DisplayName("should return transport report with open status") @SneakyThrows @ParameterizedTest @ValueSource(strings = { "dfoerdermittel", "abgabe0401-kleiner-waffenschein" }) void shouldReturn(String messageLabel) { - XtaMessage xtaMessage = createXdomeaMessage(messageLabel); + XtaMessage xtaMessage = createXdomeaMessage(loadMessage(messageLabel).messageFile()); - try { - var result = authorClient.sendMessage(xtaMessage); - - assertThat(result.status()).isEqualTo(XtaMessageStatus.OPEN); - } catch (MessageSchemaViolationException exception) { - log.error(exception.getFaultInfo().getErrorCode().toString(), exception); - fail(exception.getFaultInfo().getErrorCode().toString()); - } + var result = testClient.sendMessage(xtaMessage); + assertThat(result.status()).isEqualTo(XtaMessageStatus.OPEN); } + } - @SneakyThrows - private static XtaMessage createXdomeaMessage( - String messageLabel) { - var creator = XdomeaXtaMessageCreatorFactory - .createInstance() - .create(); + private XtaMessage createMessage(String messageLabel, XtaIdentifier author, XtaIdentifier reader) { + return XtaMessageExampleLoader.load( + XtaMessageExampleLoader.MessageExampleConfig.builder() + .messageLabel(messageLabel) + .reader(reader) + .author(author) + .build()); + } - return creator.createMessage(loadMessageFile(messageLabel)); - } + @SneakyThrows + private static XtaMessage createXdomeaMessage(XtaFile messageFile) { + return XDOMEA_XTA_MESSAGE_CREATOR.createMessage(messageFile); + } - private static XtaFile loadMessageFile(String messageLabel) { - var message = XtaMessageExampleLoader.load(XtaMessageExampleLoader.MessageExampleConfig.builder() - .messageLabel(messageLabel) - .author(AUTHOR_CLIENT_IDENTIFIER) - .reader(READER_CLIENT_IDENTIFIER1) - .build()); - return message.messageFile(); - } + private static XtaMessage loadMessage(String messageLabel) { + return XtaMessageExampleLoader.load(XtaMessageExampleLoader.MessageExampleConfig.builder() + .messageLabel(messageLabel) + .author(AUTHOR_CLIENT_IDENTIFIER) + .reader(READER_CLIENT_IDENTIFIER1) + .build()); } } diff --git a/src/test/java/de/ozgcloud/xta/client/XtaClientTest.java b/src/test/java/de/ozgcloud/xta/client/XtaClientTest.java index 76f2e929c6a21c066c41cd1545fdc9036e5f696f..d5a5d38085709d72f8b82e36e7ee71dcfb495040 100644 --- a/src/test/java/de/ozgcloud/xta/client/XtaClientTest.java +++ b/src/test/java/de/ozgcloud/xta/client/XtaClientTest.java @@ -5,165 +5,656 @@ import static de.ozgcloud.xta.client.factory.XtaClientConfigTestFactory.*; import static org.assertj.core.api.Assertions.*; import static org.mockito.Mockito.*; +import java.math.BigInteger; import java.util.List; +import java.util.Optional; +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.stream.Stream; import org.junit.jupiter.api.BeforeEach; 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.ValueSource; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; import de.ozgcloud.xta.client.config.XtaClientConfig; -import de.ozgcloud.xta.client.core.WrappedXtaService; +import de.ozgcloud.xta.client.core.XtaClientService; +import de.ozgcloud.xta.client.core.XtaExceptionHandler; +import de.ozgcloud.xta.client.exception.XtaClientRuntimeException; +import de.ozgcloud.xta.client.factory.ClientRuntimeExceptionTestFactory; +import de.ozgcloud.xta.client.factory.XtaClientExceptionTestFactory; +import de.ozgcloud.xta.client.factory.XtaMessageMetaDataListingTestFactory; +import de.ozgcloud.xta.client.factory.XtaMessageMetaDataTestFactory; import de.ozgcloud.xta.client.factory.XtaMessageTestFactory; +import de.ozgcloud.xta.client.factory.XtaTransportReportTestFactory; import de.ozgcloud.xta.client.model.XtaMessage; import de.ozgcloud.xta.client.model.XtaMessageMetaData; import de.ozgcloud.xta.client.model.XtaMessageMetaDataListing; +import de.ozgcloud.xta.client.model.XtaMessageStatus; import de.ozgcloud.xta.client.model.XtaTransportReport; import lombok.SneakyThrows; class XtaClientTest { @Mock - private WrappedXtaService service; + private XtaClientService service; @Mock private XtaClientConfig config; + @Mock + private XtaExceptionHandler exceptionHandler; + @Spy @InjectMocks private XtaClient client; - @DisplayName("get messages metadata") + private XtaMessageMetaDataListing listing; + + private List<XtaTransportReport> transportReports; + + private XtaMessage message; + + @BeforeEach + void setup() { + message = XtaMessageTestFactory.create(); + listing = XtaMessageMetaDataListingTestFactory.create(); + transportReports = listing.messages().stream() + .map(messageMetaData -> XtaTransportReportTestFactory.createBuilder() + .metaData(messageMetaData) + .build()) + .toList(); + } + + @DisplayName("fetch messages") @Nested - class TestGetMessagesMetadata { + class TestFetchMessages { @Mock - XtaMessageMetaDataListing xtaMessageMetaDataListing; + private Consumer<XtaMessage> processMessage; - @BeforeEach + @Mock + private XtaTransportReport transportReport; + + @DisplayName("should return") + @Test @SneakyThrows - void mock() { - doReturn(SELF_IDENTIFIER).when(client).deriveIdentifier(SELF_IDENTIFIER_VALUE); - when(service.getStatusList(SELF_IDENTIFIER, MAX_LIST_ITEMS)).thenReturn(xtaMessageMetaDataListing); - when(config.getMaxListItems()).thenReturn(MAX_LIST_ITEMS); + void shouldReturn() { + doReturn(List.of(transportReport)).when(client).fetchMessagesRaw(processMessage); + + var result = client.fetchMessages(processMessage); + + assertThat(result).containsExactly(transportReport); } - @DisplayName("should call checkAccountActive") + @DisplayName("should throw checked exception on runtime exception") @Test + void shouldThrowCheckedExceptionOnRuntimeException() { + var clientException = XtaClientExceptionTestFactory.create(); + var runTimeException = ClientRuntimeExceptionTestFactory.create(); + doThrow(runTimeException).when(client).fetchMessagesRaw(processMessage); + when(exceptionHandler.deriveXtaClientException(runTimeException)).thenReturn(clientException); + + assertThatThrownBy(() -> client.fetchMessages(processMessage)) + .isEqualTo(clientException); + } + + } + + @DisplayName("fetch messages raw") + @Nested + class TestFetchMessagesRaw { + + @Mock + private Consumer<XtaMessage> processMessage; + + @Mock + private FetchMessageParameter parameter; + + @Mock + private XtaTransportReport transportReport; + + @BeforeEach + void mock() { + doReturn(List.of(SELF_IDENTIFIER)).when(config).getClientIdentifiers(); + } + + @DisplayName("with active account") + @Nested + class TestWithActiveAccount { + + @DisplayName("should fetch messages for client identifier") + @Test + void shouldFetchMessagesForClientIdentifier() { + doReturn(true).when(service).checkAccountActive(SELF_IDENTIFIER); + doReturn(parameter).when(client).initializeFetchMessageParameter(SELF_IDENTIFIER, processMessage); + doReturn(Stream.of(transportReport)).when(client).fetchMessagesForClientIdentifier(parameter); + + var result = fetchMessages(); + + assertThat(result).containsExactly(transportReport); + } + } + + @DisplayName("with inactive account") + @Nested + class TestWithInactiveAccount { + + @DisplayName("should fetch no messages") + @Test + void shouldFetchNoMessages() { + doReturn(false).when(service).checkAccountActive(SELF_IDENTIFIER); + + var result = fetchMessages(); + + assertThat(result).isEmpty(); + } + } + @SneakyThrows - void shouldCallCheckAccountActive() { - client.getMessagesMetadata(SELF_IDENTIFIER_VALUE); + private List<XtaTransportReport> fetchMessages() { + return client.fetchMessages(processMessage); + } + } - verify(service).checkAccountActive(SELF_IDENTIFIER); + @DisplayName("initialize fetch message parameter") + @Nested + class TestInitializeFetchMessageParameter { + + @Mock + private Consumer<XtaMessage> processMessage; + + @DisplayName("should init empty viewed message ids") + @Test + void shouldInitEmptyViewedMessageIds() { + var result = initializeFetchMessageParameter(); + + assertThat(result.viewedMessageIds()).isEmpty(); } - @DisplayName("should return get status list response") + @DisplayName("should set client identifier") @Test - @SneakyThrows - void shouldReturnGetStatusListResponse() { - var result = client.getMessagesMetadata(SELF_IDENTIFIER_VALUE); + void shouldSetClientIdentifier() { + var result = initializeFetchMessageParameter(); + + assertThat(result.clientIdentifier()).isEqualTo(SELF_IDENTIFIER); + } + + @DisplayName("should set process message") + @Test + void shouldSetProcessMessage() { + var result = initializeFetchMessageParameter(); - assertThat(result).isEqualTo(xtaMessageMetaDataListing); + assertThat(result.processMessage()).isEqualTo(processMessage); } + FetchMessageParameter initializeFetchMessageParameter() { + return client.initializeFetchMessageParameter(SELF_IDENTIFIER, processMessage); + } } - @DisplayName("get next messages meta data") + @DisplayName("fetch messages for client identifier") @Nested - class TestGetNextMessagesMetaData { + class TestFetchMessagesForClientIdentifier { @Mock - XtaMessageMetaDataListing xtaMessageMetaDataListing; + private FetchMessageParameter parameter; @BeforeEach - @SneakyThrows void mock() { - doReturn(SELF_IDENTIFIER).when(client).deriveIdentifier(SELF_IDENTIFIER_VALUE); - when(service.getStatusList(SELF_IDENTIFIER, MAX_LIST_ITEMS)).thenReturn(xtaMessageMetaDataListing); - when(config.getMaxListItems()).thenReturn(MAX_LIST_ITEMS); + when(parameter.clientIdentifier()).thenReturn(SELF_IDENTIFIER); + } + + @DisplayName("with successful listing") + @Nested + class TestWithSuccessfulListing { + + @Mock + private FetchMessageParameter nextParameter; + + @BeforeEach + void mock() { + when(service.getStatusList(SELF_IDENTIFIER)).thenReturn(Optional.of(listing)); + doReturn(transportReports).when(client).fetchMessagesForListing(listing.messages(), parameter); + } + + @DisplayName("should return transport reports if no more messages available\"") + @Test + void shouldReturnTransportReportsIfNoMoreMessagesAvailable() { + doReturn(false).when(client).checkMoreMessagesAvailable(listing, transportReports); + + var result = client.fetchMessagesForClientIdentifier(parameter).toList(); + + assertThat(result) + .extracting(XtaTransportReport::metaData) + .extracting(XtaMessageMetaData::messageId) + .containsExactly(MESSAGE_ID, MESSAGE_ID2, MESSAGE_ID3); + } + + @DisplayName("should return concatenated transport reports if more messages available") + @Test + void shouldReturnConcatenatedTransportReportsIfMoreMessagesAvailable() { + var messageId4 = "messageId4"; + var additionalTransportReport = XtaTransportReportTestFactory.createBuilder() + .metaData(XtaMessageMetaDataTestFactory.createBuilder() + .messageId(messageId4) + .build()) + .build(); + doReturn(true).when(client).checkMoreMessagesAvailable(listing, transportReports); + doReturn(nextParameter).when(parameter).withViewedMessageIdsFrom(listing.messages()); + doReturn(Stream.of(additionalTransportReport)).when(client).fetchMessagesForClientIdentifier(nextParameter); + + var result = client.fetchMessagesForClientIdentifier(parameter).toList(); + + assertThat(result) + .extracting(XtaTransportReport::metaData) + .extracting(XtaMessageMetaData::messageId) + .containsExactly(MESSAGE_ID, MESSAGE_ID2, MESSAGE_ID3, messageId4); + } + } + + @DisplayName("with missing listing") + @Nested + class TestWithMissingListing { + + @BeforeEach + void mock() { + when(service.getStatusList(SELF_IDENTIFIER)).thenReturn(Optional.empty()); + } + + @DisplayName("should return empty stream") + @Test + void shouldReturnEmptyStream() { + var result = client.fetchMessagesForClientIdentifier(parameter).toList(); + + assertThat(result).isEmpty(); + } + } + } + + @DisplayName("check more messages available") + @Nested + class TestCheckMoreMessagesAvailable { + + @DisplayName("with no extra pending messages") + @Nested + class TestWithNoExtraPendingMessages { + @DisplayName("should return false") + @Test + void shouldReturnFalse() { + doReturn(false).when(client).checkExtraPendingMessagesAvailable(listing); + + var result = client.checkMoreMessagesAvailable(listing, transportReports); + + assertThat(result).isFalse(); + } + } + + @DisplayName("with extra pending messages but no message closed") + @Nested + class TestWithExtraPendingMessagesButNoMessageClosed { + @DisplayName("should return false") + @Test + void shouldReturnFalse() { + doReturn(true).when(client).checkExtraPendingMessagesAvailable(listing); + doReturn(false).when(client).checkSomeMessageHasBeenClosed(transportReports); + + var result = client.checkMoreMessagesAvailable(listing, transportReports); + + assertThat(result).isFalse(); + } + } + } + + @DisplayName("check extra pending messages available") + @Nested + class TestCheckExtraPendingMessagesAvailable { + @DisplayName("should return true if more pending than received") + @Test + void shouldReturnTrueIfMorePendingThanReceived() { + var result = client.checkExtraPendingMessagesAvailable(listing); + + assertThat(result).isTrue(); } - @DisplayName("should return get status list response") + @DisplayName("should return false if no more pending than received") @Test - @SneakyThrows - void shouldReturnGetStatusListResponse() { - var result = client.getNextMessagesMetadata(SELF_IDENTIFIER_VALUE); + void shouldReturnFalseIfNoMorePendingThanReceived() { + var listingWithNoExtraPendingMessages = XtaMessageMetaDataListingTestFactory.createBuilder() + .pendingMessageCount(BigInteger.valueOf(listing.messages().size())) + .build(); + + var result = client.checkExtraPendingMessagesAvailable(listingWithNoExtraPendingMessages); + + assertThat(result).isFalse(); + } + } + + @DisplayName("check some message has been closed") + @Nested + class TestCheckSomeMessageHasBeenClosed { + @DisplayName("should return false if no message has green status") + @Test + void shouldReturnFalseIfNoMessageHasGreenStatus() { + var result = client.checkSomeMessageHasBeenClosed(transportReports); + + assertThat(result).isFalse(); + } + + @DisplayName("should log warning if no message has been closed") + @Test + void shouldLogWarningIfNoMessageHasBeenClosed() { + client.checkSomeMessageHasBeenClosed(transportReports); + + verify(client).logWarnForNoMessageClosed(); + } + + @DisplayName("should return true if some message has green status") + @Test + void shouldReturnTrueIfSomeMessageHasGreenStatus() { + var transportReportsWithGreenStatus = Stream.concat( + transportReports.stream(), + Stream.of(XtaTransportReportTestFactory.createBuilder() + .status(XtaMessageStatus.GREEN) + .build()) + ).toList(); + + var result = client.checkSomeMessageHasBeenClosed(transportReportsWithGreenStatus); + + assertThat(result).isTrue(); + } + } + + @DisplayName("fetch messages for listing") + @Nested + class TestFetchMessagesForListing { + + @Mock + private FetchMessageParameter parameter; + + @Captor + private ArgumentCaptor<XtaMessageMetaData> messageMetaDataArgumentCaptor; + + @DisplayName("with messages which should not be fetched") + @Nested + class TestWithMessagesWhichShouldNotBeFetched { + + @BeforeEach + void mock() { + doReturn(false).when(client).checkMessageShouldBeFetched(any(), any()); + } + + @DisplayName("should check if message has already been viewed") + @Test + void shouldCheckIfMessageHasAlreadyBeenViewed() { + client.fetchMessagesForListing(listing.messages(), parameter); + + verify(client, times(3)).checkMessageShouldBeFetched(messageMetaDataArgumentCaptor.capture(), eq(parameter)); + assertThat(messageMetaDataArgumentCaptor.getAllValues()) + .containsExactlyElementsOf(listing.messages()); + } + } - assertThat(result).isEqualTo(xtaMessageMetaDataListing); + @DisplayName("with messages which should be fetched") + @Nested + class TestWithMessagesWhichShouldBeFetched { + + @BeforeEach + void mock() { + doReturn(true).when(client).checkMessageShouldBeFetched(any(), any()); + doAnswer(args -> { + XtaMessageMetaData metaData = args.getArgument(0); + assertThat(metaData).isNotNull(); + return getTestTransportReportByMessageId(metaData.messageId()); + }).when(client).fetchMessage(any(), any()); + } + + private Optional<XtaTransportReport> getTestTransportReportByMessageId(String messageId) { + assertThat(messageId).isNotNull(); + return transportReports.stream().filter(d -> { + assertThat(d.metaData().messageId()).isNotNull(); + return d.metaData().messageId().equals(messageId); + }).findFirst(); + } + + @DisplayName("should return transport reports") + @Test + void shouldReturnTransportReports() { + var result = client.fetchMessagesForListing(listing.messages(), parameter); + + assertThat(result).containsExactlyElementsOf(transportReports); + } } } - @DisplayName("derive identifier") + @DisplayName("check message should be fetched") @Nested - class TestDeriveIdentifier { + class TestCheckMessageShouldBeFetched { + @Mock + private FetchMessageParameter parameter; + + @Mock + private XtaMessageMetaData messageMetaData; - @DisplayName("should use value") + @DisplayName("should return false if message already viewed") @Test - void shouldUseValue() { - when(config.getClientIdentifiers()).thenReturn(List.of(SELF_IDENTIFIER2, SELF_IDENTIFIER)); + void shouldReturnFalseIfMessageAlreadyViewed() { + when(parameter.hasMessageAlreadyBeenViewed(messageMetaData)).thenReturn(true); - var result = client.deriveIdentifier(SELF_IDENTIFIER_VALUE); + var result = checkMessageShouldBeFetched(); - assertThat(result.value()).isEqualTo(SELF_IDENTIFIER_VALUE); + assertThat(result).isFalse(); } - @DisplayName("should throw when unknown") + @DisplayName("with message not viewed") + @Nested + class TestWithMessageNotViewed { + + @BeforeEach + void mock() { + when(parameter.hasMessageAlreadyBeenViewed(messageMetaData)).thenReturn(false); + } + + @DisplayName("should return false if message is not supported") + @Test + void shouldReturnFalseIfMessageIsNotSupported() { + doReturn(false).when(client).isMessageSupported(messageMetaData); + + var result = checkMessageShouldBeFetched(); + + assertThat(result).isFalse(); + } + + @DisplayName("should return true if message is supported") + @Test + void shouldReturnTrueIfMessageIsSupported() { + doReturn(true).when(client).isMessageSupported(messageMetaData); + + var result = checkMessageShouldBeFetched(); + + assertThat(result).isTrue(); + } + } + + private boolean checkMessageShouldBeFetched() { + return client.checkMessageShouldBeFetched(messageMetaData, parameter); + } + } + + @DisplayName("is message supported") + @Nested + class TestIsMessageSupported { + + @Mock + private Predicate<XtaMessageMetaData> isSupportedPredicate; + + @Mock + private XtaMessageMetaData messageMetaData; + + @DisplayName("should return true if predicate is null") @Test - void shouldThrowWhenUnknown() { - when(config.getClientIdentifiers()).thenReturn(List.of(SELF_IDENTIFIER2)); + void shouldReturnTrueIfPredicateIsNull() { + when(config.getIsMessageSupported()).thenReturn(null); + + var result = client.isMessageSupported(messageMetaData); - assertThatThrownBy(() -> client.deriveIdentifier(SELF_IDENTIFIER_VALUE)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("Unknown identifier: " + SELF_IDENTIFIER_VALUE); + assertThat(result).isTrue(); + } + + @DisplayName("should return predicate value") + @ParameterizedTest + @ValueSource(booleans = { true, false }) + void shouldReturnPredicateValue(boolean value) { + when(config.getIsMessageSupported()).thenReturn(isSupportedPredicate); + when(isSupportedPredicate.test(messageMetaData)).thenReturn(value); + + var result = client.isMessageSupported(messageMetaData); + + assertThat(result).isEqualTo(value); } } - @DisplayName("get message") + @DisplayName("fetch message") @Nested - class TestGetMessage { + class TestFetchMessage { @Mock - XtaMessage xtaMessage; + private XtaMessageMetaData messageMetaData; @Mock - XtaTransportReport xtaTransportReport; + private FetchMessageParameter parameter; + + @Mock + private XtaMessage message; + + @Mock + private XtaTransportReport transportReport; + + @DisplayName("should return transport report") + @Test + void shouldReturnTransportReport() { + when(service.getMessage(messageMetaData)).thenReturn(Optional.of(message)); + doReturn(Optional.of(transportReport)).when(client).processMesssageAndFetchTransportReport(message, parameter); + + var result = client.fetchMessage(messageMetaData, parameter); + + assertThat(result).contains(transportReport); + } + } + + @DisplayName("process message and fetch transport report") + @Nested + class TestProcessMessageAndFetchTransportReport { + + @Mock + private XtaMessageMetaData messageMetaData; + + @Mock + private FetchMessageParameter parameter; + + @Mock + private Consumer<XtaMessage> processMessageConsumer; + + @Mock + private XtaMessage message; + + @Mock + private XtaTransportReport transportReport; @BeforeEach - @SneakyThrows void mock() { - doReturn(SELF_IDENTIFIER).when(client).deriveIdentifier(SELF_IDENTIFIER_VALUE); - when(service.getMessage(MESSAGE_ID, SELF_IDENTIFIER)).thenReturn(xtaMessage); - when(service.getTransportReport(MESSAGE_ID, SELF_IDENTIFIER)).thenReturn(xtaTransportReport); + when(message.metaData()).thenReturn(messageMetaData); + when(parameter.processMessage()).thenReturn(processMessageConsumer); + doNothing().when(client).processMessageAndCloseIfNoException(any(), any()); + when(service.getTransportReport(messageMetaData)).thenReturn(Optional.of(transportReport)); } - @DisplayName("should call close") + @DisplayName("should call process message and close if no exception") @Test - @SneakyThrows - void shouldCallClose() { - client.getMessage(SELF_IDENTIFIER_VALUE, MESSAGE_ID); + void shouldCallProcessMessageAndCloseIfNoException() { + client.processMesssageAndFetchTransportReport(message, parameter); - verify(service).close(MESSAGE_ID, SELF_IDENTIFIER); + verify(client).processMessageAndCloseIfNoException(message, processMessageConsumer); } - @DisplayName("should return with message") + @DisplayName("should return") @Test - @SneakyThrows void shouldReturn() { - var result = client.getMessage(SELF_IDENTIFIER_VALUE, MESSAGE_ID); + var result = client.processMesssageAndFetchTransportReport(message, parameter); - assertThat(result.message()).isEqualTo(xtaMessage); + assertThat(result).contains(transportReport); } + } + + @DisplayName("process message and close if no exception") + @Nested + class TestProcessMessageAndCloseIfNoException { - @DisplayName("should return with transport report") + @Mock + private Consumer<XtaMessage> processMessageConsumer; + + @Mock + private XtaMessage message; + + @BeforeEach + void mock() { + when(message.metaData()).thenReturn(listing.messages().getFirst()); + } + + @DisplayName("should consume message") @Test - @SneakyThrows - void shouldReturnWithTransportReport() { - var result = client.getMessage(SELF_IDENTIFIER_VALUE, MESSAGE_ID); + void shouldConsumeMessage() { + processMessageAndCloseIfNoException(); + + verify(processMessageConsumer).accept(message); + } + + @DisplayName("should close message") + @Test + void shouldCloseMessage() { + processMessageAndCloseIfNoException(); + + verify(service).closeMessage(message); + } + + @DisplayName("with runtime exception") + @Nested + class TestWithRuntimeException { + + @Mock + private RuntimeException exception; - assertThat(result.transportReport()).isEqualTo(xtaTransportReport); + @BeforeEach + void mock() { + doThrow(exception).when(processMessageConsumer).accept(message); + } + + @DisplayName("should log error") + @Test + void shouldLogError() { + processMessageAndCloseIfNoException(); + + verify(client).logErrorForMessageProcessingFailure(MESSAGE_ID, exception); + } + + @DisplayName("should not close message") + @Test + void shouldNotCloseMessage() { + processMessageAndCloseIfNoException(); + + verify(service, times(0)).closeMessage(any()); + } + } + + private void processMessageAndCloseIfNoException() { + client.processMessageAndCloseIfNoException(message, processMessageConsumer); } } @@ -172,80 +663,125 @@ class XtaClientTest { class TestSendMessage { @Mock - XtaMessage xtaMessageWithoutMessageId; + private XtaTransportReport transportReport; - @Mock - XtaMessageMetaData xtaMessageMetaDataWithoutMessageId; + @DisplayName("should return") + @Test + @SneakyThrows + void shouldReturn() { + doReturn(transportReport).when(client).sendMessageRaw(message); - @Mock - XtaMessage xtaMessage; + var result = client.sendMessage(message); + + assertThat(result).isEqualTo(transportReport); + } + + @DisplayName("should throw checked exception on runtime exception") + @Test + @SneakyThrows + void shouldThrowCheckedExceptionOnRuntimeException() { + var clientException = XtaClientExceptionTestFactory.create(); + var runtimeException = ClientRuntimeExceptionTestFactory.create(); + doThrow(runtimeException).when(client).sendMessageRaw(message); + when(exceptionHandler.deriveXtaClientException(runtimeException)).thenReturn(clientException); + + assertThatThrownBy(() -> client.sendMessage(message)) + .isEqualTo(clientException); + } + } + + @DisplayName("send message raw") + @Nested + class TestSendMessageRaw { @Mock - XtaTransportReport xtaTransportReport; + private XtaTransportReport transportReport; @BeforeEach - @SneakyThrows void mock() { - when(xtaMessageWithoutMessageId.metaData()).thenReturn(xtaMessageMetaDataWithoutMessageId); - when(xtaMessageMetaDataWithoutMessageId.authorIdentifier()).thenReturn(AUTHOR_IDENTIFIER); - when(xtaMessageMetaDataWithoutMessageId.readerIdentifier()).thenReturn(READER_IDENTIFIER); - when(xtaMessageMetaDataWithoutMessageId.service()).thenReturn(MESSAGE_SERVICE); - - when(service.createMessageId(AUTHOR_IDENTIFIER)).thenReturn(MESSAGE_ID); - doReturn(xtaMessage).when(client).createXtaMessageWithMessageId(xtaMessageWithoutMessageId, MESSAGE_ID); - when(service.getTransportReport(MESSAGE_ID, AUTHOR_IDENTIFIER)).thenReturn(xtaTransportReport); + doNothing().when(client).throwExceptionIfAccountInactive(any()); + doNothing().when(client).throwExceptionIfServiceNotAvailable(any()); + when(service.sendMessage(message)).thenReturn(transportReport); } @DisplayName("should call checkAccountActive") @Test @SneakyThrows void shouldCallCheckAccountActive() { - client.sendMessage(xtaMessageWithoutMessageId); + sendMessage(); - verify(service).checkAccountActive(AUTHOR_IDENTIFIER); + verify(client).throwExceptionIfAccountInactive(AUTHOR_IDENTIFIER); } @DisplayName("should call lookup service") @Test @SneakyThrows void shouldCallLookupService() { - client.sendMessage(xtaMessageWithoutMessageId); + sendMessage(); - verify(service).lookupService(MESSAGE_SERVICE, READER_IDENTIFIER, AUTHOR_IDENTIFIER); + verify(client).throwExceptionIfServiceNotAvailable(message.metaData()); } - @DisplayName("should call send message") + @DisplayName("should return") @Test + void shouldReturn() { + var result = sendMessage(); + + assertThat(result).isEqualTo(transportReport); + } + @SneakyThrows - void shouldCallSendMessage() { - client.sendMessage(xtaMessageWithoutMessageId); + private XtaTransportReport sendMessage() { + return client.sendMessage(message); + } + } - verify(service).sendMessage(xtaMessage); + @DisplayName("throw exception if service not available") + @Nested + class TestThrowExceptionIfServiceNotAvailable { + + @DisplayName("should call lookupService") + @Test + void shouldCallLookupService() { + doReturn(true).when(service).lookupService(any()); + + client.throwExceptionIfServiceNotAvailable(message.metaData()); + + verify(service).lookupService(message.metaData()); } - @DisplayName("should return with transport report") + @DisplayName("should throw exception if service not available") @Test - @SneakyThrows - void shouldReturnWithTransportReport() { - var result = client.sendMessage(xtaMessageWithoutMessageId); + void shouldThrowExceptionIfServiceNotAvailable() { + var metaData = message.metaData(); + doReturn(false).when(service).lookupService(message.metaData()); - assertThat(result).isEqualTo(xtaTransportReport); + assertThatThrownBy(() -> client.throwExceptionIfServiceNotAvailable(metaData)) + .isInstanceOf(XtaClientRuntimeException.class); } } - @DisplayName("create xta message with message id") + @DisplayName("throw exception if account inactive") @Nested - class TestCreateXtaMessageWithMessageId { - private final XtaMessage xtaMessage = XtaMessageTestFactory.create(); + class TestThrowExceptionIfAccountInactive { - @DisplayName("should set message id") + @DisplayName("should call checkAccountActive") @Test - void shouldSetMessageId() { - var newMessageId = "newMessageId"; + void shouldCallCheckAccountActive() { + doReturn(true).when(service).checkAccountActive(any()); - var result = client.createXtaMessageWithMessageId(xtaMessage, newMessageId); + client.throwExceptionIfAccountInactive(SELF_IDENTIFIER); + + verify(service).checkAccountActive(SELF_IDENTIFIER); + } + + @DisplayName("should throw exception if account inactive") + @Test + void shouldThrowExceptionIfAccountInactive() { + doReturn(false).when(service).checkAccountActive(AUTHOR_IDENTIFIER); - assertThat(result.metaData().messageId()).isEqualTo(newMessageId); + assertThatThrownBy(() -> client.throwExceptionIfAccountInactive(AUTHOR_IDENTIFIER)) + .isInstanceOf(XtaClientRuntimeException.class); } } diff --git a/src/test/java/de/ozgcloud/xta/client/XtaTestServerContainer.java b/src/test/java/de/ozgcloud/xta/client/XtaTestServerContainer.java index da3b366c818be7b6e7add8eeca14c574af488554..5642170c9da97c68b2ebd86f9374cefe2992d012 100644 --- a/src/test/java/de/ozgcloud/xta/client/XtaTestServerContainer.java +++ b/src/test/java/de/ozgcloud/xta/client/XtaTestServerContainer.java @@ -21,19 +21,19 @@ public class XtaTestServerContainer extends GenericContainer<XtaTestServerContai } public String getBaseUrl() { - return "https://%s:%d/services/XTAService/".formatted(getHost(), getMappedPort(PORT)); + return "https://%s:%d/MB_XTA-WS/XTA210".formatted(getHost(), getMappedPort(PORT)); } public String getMsgBoxPortUrl() { - return getBaseUrl() + "MsgBoxPort"; + return getBaseUrl() + "msgBoxPort.svc"; } public String getManagementPortUrl() { - return getBaseUrl() + "ManagementPort"; + return getBaseUrl() + "managementPort.svc"; } public String getSendPortUrl() { - return getBaseUrl() + "SendXtaPort"; + return getBaseUrl() + "sendPort.svc"; } } diff --git a/src/test/java/de/ozgcloud/xta/client/config/XtaConfigValidatorTest.java b/src/test/java/de/ozgcloud/xta/client/config/XtaConfigValidatorTest.java index 88672c08a8a8895e2073bf7e479fcd7811083e80..4e0a9583cf73810be1814cd72cf68c27acf665a8 100644 --- a/src/test/java/de/ozgcloud/xta/client/config/XtaConfigValidatorTest.java +++ b/src/test/java/de/ozgcloud/xta/client/config/XtaConfigValidatorTest.java @@ -1,8 +1,8 @@ package de.ozgcloud.xta.client.config; -import static java.util.Collections.*; import static org.assertj.core.api.Assertions.*; +import java.util.List; import java.util.function.UnaryOperator; import org.junit.jupiter.api.DisplayName; @@ -10,8 +10,9 @@ import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; +import de.ozgcloud.xta.client.exception.XtaClientInitializationException; import de.ozgcloud.xta.client.factory.XtaClientConfigTestFactory; -import de.ozgcloud.xta.client.exception.ClientInitializationException; +import de.ozgcloud.xta.client.model.XtaIdentifier; import lombok.SneakyThrows; class XtaConfigValidatorTest { @@ -43,15 +44,15 @@ class XtaConfigValidatorTest { validator.validate(config); } - @DisplayName("should throw without client identifiers") + @DisplayName("should throw with blank client identifier") @Test - void shouldThrowWithoutIdentifiers() { + void shouldThrowWithBlankClientIdentifier() { var config = XtaClientConfigTestFactory.createBuilder() - .clientIdentifiers(emptyList()) + .clientIdentifiers(List.of(XtaIdentifier.builder().value("").build())) .build(); assertThatThrownBy(() -> validator.validate(config)) - .isInstanceOf(ClientInitializationException.class) + .isInstanceOf(XtaClientInitializationException.class) .hasMessageContaining("clientIdentifiers"); } @@ -63,7 +64,7 @@ class XtaConfigValidatorTest { .build(); assertThatThrownBy(() -> validator.validate(config)) - .isInstanceOf(ClientInitializationException.class) + .isInstanceOf(XtaClientInitializationException.class) .hasMessageContaining("managementServiceUrl"); } @@ -75,7 +76,7 @@ class XtaConfigValidatorTest { .build(); assertThatThrownBy(() -> validator.validate(config)) - .isInstanceOf(ClientInitializationException.class) + .isInstanceOf(XtaClientInitializationException.class) .hasMessageContaining("sendServiceUrl"); } @@ -87,7 +88,7 @@ class XtaConfigValidatorTest { .build(); assertThatThrownBy(() -> validator.validate(config)) - .isInstanceOf(ClientInitializationException.class) + .isInstanceOf(XtaClientInitializationException.class) .hasMessageContaining("msgBoxServiceUrl"); } @@ -99,7 +100,7 @@ class XtaConfigValidatorTest { .build(); assertThatThrownBy(() -> validator.validate(config)) - .isInstanceOf(ClientInitializationException.class) + .isInstanceOf(XtaClientInitializationException.class) .hasMessageContaining("maxListItems"); } @@ -112,7 +113,7 @@ class XtaConfigValidatorTest { var config = createKeystoreWithClientCertKeyStore(o -> o.password(null)); assertThatThrownBy(() -> validator.validate(config)) - .isInstanceOf(ClientInitializationException.class) + .isInstanceOf(XtaClientInitializationException.class) .hasMessageContaining("password"); } @@ -122,7 +123,7 @@ class XtaConfigValidatorTest { var config = createKeystoreWithClientCertKeyStore(o -> o.type("")); assertThatThrownBy(() -> validator.validate(config)) - .isInstanceOf(ClientInitializationException.class) + .isInstanceOf(XtaClientInitializationException.class) .hasMessageContaining("type"); } @@ -132,7 +133,7 @@ class XtaConfigValidatorTest { var config = createKeystoreWithClientCertKeyStore(o -> o.content(null)); assertThatThrownBy(() -> validator.validate(config)) - .isInstanceOf(ClientInitializationException.class) + .isInstanceOf(XtaClientInitializationException.class) .hasMessageContaining("content"); } diff --git a/src/test/java/de/ozgcloud/xta/client/core/XtaClientServiceTest.java b/src/test/java/de/ozgcloud/xta/client/core/XtaClientServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..44f5255784cdd6308a28dc908641fdefa0790d94 --- /dev/null +++ b/src/test/java/de/ozgcloud/xta/client/core/XtaClientServiceTest.java @@ -0,0 +1,431 @@ +package de.ozgcloud.xta.client.core; + +import static de.ozgcloud.xta.client.factory.MessageMetaDataTestFactory.*; +import static de.ozgcloud.xta.client.factory.XtaClientConfigTestFactory.*; +import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.anyString; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.*; + +import org.junit.jupiter.api.BeforeEach; +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.ValueSource; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; + +import de.ozgcloud.xta.client.config.XtaClientConfig; +import de.ozgcloud.xta.client.exception.XtaClientRuntimeException; +import de.ozgcloud.xta.client.factory.XtaMessageMetaDataListingTestFactory; +import de.ozgcloud.xta.client.factory.XtaMessageMetaDataTestFactory; +import de.ozgcloud.xta.client.factory.XtaMessageTestFactory; +import de.ozgcloud.xta.client.factory.XtaTransportReportTestFactory; +import de.ozgcloud.xta.client.model.XtaMessage; +import de.ozgcloud.xta.client.model.XtaMessageMetaData; +import de.ozgcloud.xta.client.model.XtaMessageMetaDataListing; +import de.ozgcloud.xta.client.model.XtaTransportReport; +import genv3.de.xoev.transport.xta.x211.PermissionDeniedException; +import genv3.de.xoev.transport.xta.x211.XTAWSTechnicalProblemException; +import lombok.SneakyThrows; + +class XtaClientServiceTest { + + @Mock + private WrappedXtaService wrapper; + + @Mock + private XtaClientConfig config; + + @Spy + @InjectMocks + private XtaClientService service; + + private final XtaMessageMetaData messageMetaData = XtaMessageMetaDataTestFactory.create(); + private final XtaMessage message = XtaMessageTestFactory.create(); + private final XtaTransportReport transportReport = XtaTransportReportTestFactory.create(); + + @DisplayName("get transport report") + @Nested + class TestGetTransportReport { + + @Mock + private XtaClientRuntimeException exception; + + @DisplayName("should return") + @Test + void shouldReturn() { + doReturn(transportReport).when(service).getTransportReportOrThrowException(messageMetaData); + + var result = service.getTransportReport(messageMetaData); + + assertThat(result).contains(transportReport); + } + + @DisplayName("should return empty if exception") + @Test + void shouldReturnEmptyIfException() { + doThrow(exception).when(service).getTransportReportOrThrowException(messageMetaData); + + var result = service.getTransportReport(messageMetaData); + + assertThat(result).isEmpty(); + } + + @DisplayName("should log error if exception") + @Test + void shouldLogErrorIfException() { + doThrow(exception).when(service).getTransportReportOrThrowException(messageMetaData); + + service.getTransportReport(messageMetaData); + + verify(service).logError(anyString(), eq(exception)); + } + } + + @DisplayName("close message") + @Nested + class TestCloseMessage { + @Mock + private XTAWSTechnicalProblemException xtaWSTechnicalProblemException; + + @DisplayName("should call close") + @Test + @SneakyThrows + void shouldCallClose() { + service.closeMessage(message); + + verify(wrapper).close(MESSAGE_ID, READER_IDENTIFIER); + } + + @DisplayName("should log error if exception") + @Test + @SneakyThrows + void shouldLogErrorIfException() { + doThrow(xtaWSTechnicalProblemException).when(wrapper).close(MESSAGE_ID, READER_IDENTIFIER); + + service.closeMessage(message); + + verify(service).logError(anyString(), eq(xtaWSTechnicalProblemException)); + } + + } + + @DisplayName("get message") + @Nested + class TestGetMessage { + + @Mock + private XTAWSTechnicalProblemException xtaWSTechnicalProblemException; + + @DisplayName("should return") + @Test + @SneakyThrows + void shouldReturn() { + doReturn(message).when(wrapper).getMessage(MESSAGE_ID, READER_IDENTIFIER); + + var result = service.getMessage(messageMetaData); + + assertThat(result).contains(message); + } + + @DisplayName("should return empty if exception") + @Test + @SneakyThrows + void shouldReturnEmptyIfException() { + doThrow(xtaWSTechnicalProblemException).when(wrapper).getMessage(MESSAGE_ID, READER_IDENTIFIER); + + var result = service.getMessage(messageMetaData); + + assertThat(result).isEmpty(); + } + + @DisplayName("should log error if exception") + @Test + @SneakyThrows + void shouldLogErrorIfException() { + doThrow(xtaWSTechnicalProblemException).when(wrapper).getMessage(MESSAGE_ID, READER_IDENTIFIER); + + service.getMessage(messageMetaData); + + verify(service).logError(anyString(), eq(xtaWSTechnicalProblemException)); + } + } + + @DisplayName("get status list") + @Nested + class TestGetStatusList { + + @Mock + private XTAWSTechnicalProblemException xtaWSTechnicalProblemException; + + private final XtaMessageMetaDataListing listing = XtaMessageMetaDataListingTestFactory.create(); + + @BeforeEach + void mock() { + when(config.getMaxListItems()).thenReturn(MAX_LIST_ITEMS); + } + + @DisplayName("should return") + @Test + @SneakyThrows + void shouldReturn() { + doReturn(listing).when(wrapper).getStatusList(SELF_IDENTIFIER, MAX_LIST_ITEMS); + + var result = service.getStatusList(SELF_IDENTIFIER); + + assertThat(result).contains(listing); + } + + @DisplayName("should return empty if exception") + @Test + @SneakyThrows + void shouldReturnEmptyIfException() { + doThrow(xtaWSTechnicalProblemException).when(wrapper).getStatusList(SELF_IDENTIFIER, MAX_LIST_ITEMS); + + var result = service.getStatusList(SELF_IDENTIFIER); + + assertThat(result).isEmpty(); + } + + @DisplayName("should log error if exception") + @Test + @SneakyThrows + void shouldLogErrorIfException() { + doThrow(xtaWSTechnicalProblemException).when(wrapper).getStatusList(SELF_IDENTIFIER, MAX_LIST_ITEMS); + + service.getStatusList(SELF_IDENTIFIER); + + verify(service).logError(anyString(), eq(xtaWSTechnicalProblemException)); + } + } + + @DisplayName("check account active") + @Nested + class TestCheckAccountActive { + + @DisplayName("should call check account active") + @Test + @SneakyThrows + void shouldCallCheckAccountActive() { + checkAccountActive(); + + verify(wrapper).checkAccountActive(SELF_IDENTIFIER); + } + + @DisplayName("should return true if no exception") + @Test + void shouldReturnTrueIfNoException() { + var result = checkAccountActive(); + + assertThat(result).isTrue(); + } + + @DisplayName("should return false if permission denied exception") + @Test + @SneakyThrows + void shouldReturnFalseIfPermissionDeniedException() { + doThrow(new PermissionDeniedException()).when(wrapper).checkAccountActive(any()); + + var result = checkAccountActive(); + + assertThat(result).isFalse(); + } + + @DisplayName("should throw on technical problem exception") + @Test + @SneakyThrows + void shouldThrowOnTechnicalProblemException() { + var technicalException = new XTAWSTechnicalProblemException(); + doThrow(technicalException).when(wrapper).checkAccountActive(any()); + + assertThatThrownBy(this::checkAccountActive) + .isInstanceOf(XtaClientRuntimeException.class) + .hasCause(technicalException); + } + + private boolean checkAccountActive() { + return service.checkAccountActive(SELF_IDENTIFIER); + } + } + + @DisplayName("send message") + @Nested + class TestSendMessage { + + @Mock + private XtaMessage messageWithoutMessageId; + + @Mock + private XTAWSTechnicalProblemException exception; + + @BeforeEach + @SneakyThrows + void mock() { + doReturn(message).when(service).getXtaMessageWithMessageId(messageWithoutMessageId); + } + + @DisplayName("should call send message") + @Test + @SneakyThrows + void shouldCallSendMessage() { + sendMessage(); + + verify(wrapper).sendMessage(message); + } + + @DisplayName("should return with transport report") + @Test + @SneakyThrows + void shouldReturnWithTransportReport() { + doReturn(transportReport).when(service).getTransportReportOrThrowException(message.metaData()); + + var result = sendMessage(); + + assertThat(result).isEqualTo(transportReport); + } + + @DisplayName("should throw client runtime exception if send message fails") + @Test + @SneakyThrows + void shouldThrowClientRuntimeExceptionIfSendMessageFails() { + doThrow(exception).when(wrapper).sendMessage(message); + + assertThatThrownBy(this::sendMessage) + .isInstanceOf(XtaClientRuntimeException.class) + .hasCause(exception); + } + + private XtaTransportReport sendMessage() { + return service.sendMessage(messageWithoutMessageId); + } + } + + @DisplayName("get xta message with message id") + @Nested + class TestGetXtaMessageWithMessageId { + + @Mock + private XtaMessage messageWithMessageId; + + @Mock + private XTAWSTechnicalProblemException exception; + + @DisplayName("should return message") + @Test + @SneakyThrows + void shouldReturnMessage() { + doReturn(MESSAGE_ID2).when(wrapper).createMessageId(AUTHOR_IDENTIFIER); + doReturn(messageWithMessageId).when(service).createXtaMessageWithMessageId(message, MESSAGE_ID2); + + var result = service.getXtaMessageWithMessageId(message); + + assertThat(result).isEqualTo(messageWithMessageId); + } + + @DisplayName("should throw client runtime exception if create message fails") + @Test + @SneakyThrows + void shouldThrowClientRuntimeExceptionIfCreateMessageFails() { + doThrow(exception).when(wrapper).createMessageId(AUTHOR_IDENTIFIER); + + assertThatThrownBy(() -> service.getXtaMessageWithMessageId(message)) + .isInstanceOf(XtaClientRuntimeException.class) + .hasCause(exception); + } + } + + @DisplayName("get transport report or throw exception") + @Nested + class TestGetTransportReportOrThrowException { + @Mock + private XTAWSTechnicalProblemException exception; + + @DisplayName("should return") + @Test + @SneakyThrows + void shouldReturn() { + when(wrapper.getTransportReport(MESSAGE_ID, AUTHOR_IDENTIFIER)).thenReturn(transportReport); + + var result = service.getTransportReportOrThrowException(messageMetaData); + + assertThat(result).isEqualTo(transportReport); + } + + @DisplayName("should throw client exception if exception") + @Test + @SneakyThrows + void shouldThrowClientExceptionIfException() { + when(wrapper.getTransportReport(MESSAGE_ID, AUTHOR_IDENTIFIER)).thenThrow(exception); + + assertThatThrownBy(() -> service.getTransportReportOrThrowException(messageMetaData)) + .isInstanceOf(XtaClientRuntimeException.class) + .hasCause(exception); + } + } + + @DisplayName("create xta message with message id") + @Nested + class TestCreateXtaMessageWithMessageId { + private final XtaMessage xtaMessage = XtaMessageTestFactory.create(); + + @DisplayName("should set message id") + @Test + void shouldSetMessageId() { + var newMessageId = "newMessageId"; + + var result = service.createXtaMessageWithMessageId(xtaMessage, newMessageId); + + assertThat(result.metaData().messageId()).isEqualTo(newMessageId); + } + } + + @DisplayName("lookup service") + @Nested + class TestLookupService { + @Mock + private XTAWSTechnicalProblemException exception; + + @DisplayName("should return lookup result") + @ParameterizedTest + @ValueSource(booleans = { true, false }) + @SneakyThrows + void shouldReturnLookupResult(boolean lookupResult) { + when(wrapper.lookupService(MESSAGE_SERVICE, READER_IDENTIFIER, AUTHOR_IDENTIFIER)).thenReturn(lookupResult); + + var result = lookupService(); + + assertThat(result).isEqualTo(lookupResult); + } + + @DisplayName("should return false if exception") + @Test + @SneakyThrows + void shouldReturnFalseIfException() { + when(wrapper.lookupService(MESSAGE_SERVICE, READER_IDENTIFIER, AUTHOR_IDENTIFIER)).thenThrow(exception); + + var result = lookupService(); + + assertThat(result).isFalse(); + } + + @DisplayName("should log warning if exception") + @Test + @SneakyThrows + void shouldLogWarningIfException() { + when(wrapper.lookupService(MESSAGE_SERVICE, READER_IDENTIFIER, AUTHOR_IDENTIFIER)).thenThrow(exception); + + lookupService(); + + verify(service).logWarning(anyString(), eq(exception)); + } + + private boolean lookupService() { + return service.lookupService(messageMetaData); + } + } + +} \ No newline at end of file diff --git a/src/test/java/de/ozgcloud/xta/client/core/XtaExceptionHandlerFactoryTest.java b/src/test/java/de/ozgcloud/xta/client/core/XtaExceptionHandlerFactoryTest.java new file mode 100644 index 0000000000000000000000000000000000000000..985c7aa712c5a886c8716e1db445771f83314d8a --- /dev/null +++ b/src/test/java/de/ozgcloud/xta/client/core/XtaExceptionHandlerFactoryTest.java @@ -0,0 +1,29 @@ +package de.ozgcloud.xta.client.core; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Spy; + +class XtaExceptionHandlerFactoryTest { + + @InjectMocks + @Spy + private XtaExceptionHandlerFactory exceptionHandlerFactory; + + @DisplayName("create") + @Nested + class TestCreate { + @DisplayName("should return") + @Test + void shouldReturn() { + var result = exceptionHandlerFactory.create(); + + assertThat(result).isNotNull(); + } + } + +} \ No newline at end of file diff --git a/src/test/java/de/ozgcloud/xta/client/core/XtaExceptionHandlerTest.java b/src/test/java/de/ozgcloud/xta/client/core/XtaExceptionHandlerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..be02999134d1f2003672d7e256a04fe35be9af8b --- /dev/null +++ b/src/test/java/de/ozgcloud/xta/client/core/XtaExceptionHandlerTest.java @@ -0,0 +1,176 @@ +package de.ozgcloud.xta.client.core; + +import static de.ozgcloud.xta.client.core.XtaExceptionHandler.*; +import static de.ozgcloud.xta.client.factory.ClientRuntimeExceptionTestFactory.*; +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.util.stream.Stream; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +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.xta.client.exception.XtaClientRuntimeException; +import de.ozgcloud.xta.client.exception.XtaClientException; +import de.ozgcloud.xta.client.factory.ClientRuntimeExceptionTestFactory; +import genv3.de.xoev.transport.xta.x211.PermissionDeniedException; + +class XtaExceptionHandlerTest { + + @InjectMocks + @Spy + XtaExceptionHandler exceptionHandler; + + private XtaClientRuntimeException exception; + + @BeforeEach + void setUp() { + exception = ClientRuntimeExceptionTestFactory.create(); + } + + @DisplayName("derive xta client exception") + @Nested + class TestDeriveXtaClientException { + + @Mock + private XtaClientException derivedClientException; + + @DisplayName("should derive xta client exception from xta client runtime exception") + @Test + void shouldDeriveXtaClientExceptionFromXtaClientRuntimeException() { + doReturn(derivedClientException).when(exceptionHandler).deriveXtaClientExceptionFromClientRuntimeException(exception); + + var result = exceptionHandler.deriveXtaClientException(exception); + + assertThat(result).isEqualTo(derivedClientException); + } + + @DisplayName("without xta client runtime exception instance") + @Nested + class TestWithoutXtaClientRuntimeExceptionInstance { + + @Mock + private RuntimeException runtimeException; + + @DisplayName("should return with unknown error message") + @Test + void shouldReturnWithUnknownErrorMessage() { + var result = exceptionHandler.deriveXtaClientException(runtimeException); + + assertThat(result.getMessage()).isEqualTo(UNEXPECTED_ERROR_MESSAGE); + } + + @DisplayName("should have cause") + @Test + void shouldHaveCause() { + var result = exceptionHandler.deriveXtaClientException(runtimeException); + + assertThat(result.getCause()).isEqualTo(runtimeException); + } + } + } + + @DisplayName("derive xta client exception from xta client runtime exception") + @Nested + class TestDeriveXtaClientExceptionFromXtaClientRuntimeException { + + @DisplayName("should keep message if no cause") + @Test + void shouldKeepMessageIfNoCause() { + var exceptionWithoutCause = new XtaClientRuntimeException(MESSAGE); + + var result = exceptionHandler.deriveXtaClientExceptionFromClientRuntimeException(exceptionWithoutCause); + + assertThat(result.getMessage()).isEqualTo(MESSAGE); + } + + @DisplayName("should keep message if cause is not an xta exception") + @Test + void shouldKeepMessageIfCauseIsNotAnXtaException() { + var cause = new Exception(); + var exceptionWithNoXtaCause = new XtaClientRuntimeException(MESSAGE, cause); + + var result = exceptionHandler.deriveXtaClientExceptionFromClientRuntimeException(exceptionWithNoXtaCause); + + assertThat(result.getMessage()).isEqualTo(MESSAGE); + + } + + @DisplayName("should extend detail message if cause is an xta exception") + @Test + void shouldExtendDetailMessageIfCauseIsAnXtaException() { + var result = exceptionHandler.deriveXtaClientExceptionFromClientRuntimeException(exception); + + assertThat(result.getMessage()).contains(MESSAGE, CAUSE_MESSAGE, CAUSE_CODE, CAUSE_NAME); + } + } + + @DisplayName("get detail lines") + @Nested + class TestGetDetailLines { + + @DisplayName("should return empty stream if cause is null") + @Test + void shouldReturnEmptyStreamIfCauseIsNull() { + var result = exceptionHandler.getDetailLines(null).toList(); + + assertThat(result).isEmpty(); + } + + @DisplayName("should return empty stream if cause is not an exception") + @Test + void shouldReturnEmptyStreamIfCauseIsNotAnException() { + var result = exceptionHandler.getDetailLines(new Throwable()).toList(); + + assertThat(result).isEmpty(); + } + + @DisplayName("should return empty stream if cause is not an xta exception") + @Test + void shouldReturnEmptyStreamIfCauseIsNotAnXtaException() { + var result = exceptionHandler.getDetailLines(new Exception()).toList(); + + assertThat(result).isEmpty(); + } + + @DisplayName("should return detail lines if cause is an xta exception") + @Test + void shouldReturnDetailLinesIfCauseIsAnXtaException() { + var causeException = (PermissionDeniedException) exception.getCause(); + doReturn(Stream.of(CAUSE_MESSAGE)).when(exceptionHandler).deriveDetailLinesFromException(causeException); + + var result = exceptionHandler.getDetailLines(causeException).toList(); + + assertThat(result).containsExactly(CAUSE_MESSAGE); + } + } + + @DisplayName("derive detail lines from exception") + @Nested + class TestDeriveDetailLinesFromException { + + @DisplayName("should return empty stream if exception type is not present") + @Test + void shouldReturnEmptyStreamIfExceptionTypeIsNotPresent() { + var result = exceptionHandler.deriveDetailLinesFromException(new Exception()).toList(); + + assertThat(result).isEmpty(); + } + + @DisplayName("should return stream with error code and name if exception type is present") + @Test + void shouldReturnStreamWithErrorCodeAndNameIfExceptionTypeIsPresent() { + var causeException = (PermissionDeniedException) exception.getCause(); + + var result = exceptionHandler.deriveDetailLinesFromException(causeException).toList(); + + assertThat(result).containsExactly(CAUSE_MESSAGE, "[%s] %s".formatted(CAUSE_CODE, CAUSE_NAME)); + } + } + +} \ No newline at end of file diff --git a/src/test/java/de/ozgcloud/xta/client/core/XtaTLSClientParametersFactoryTest.java b/src/test/java/de/ozgcloud/xta/client/core/XtaTLSClientParametersFactoryTest.java index f9077cf24e4ac1ce0218d3784a1f501b349f2b32..8047c2dddd27d5918427a4c5f687b8f8da4bcd7e 100644 --- a/src/test/java/de/ozgcloud/xta/client/core/XtaTLSClientParametersFactoryTest.java +++ b/src/test/java/de/ozgcloud/xta/client/core/XtaTLSClientParametersFactoryTest.java @@ -22,7 +22,7 @@ import org.mockito.Spy; import de.ozgcloud.xta.client.factory.XtaClientConfigTestFactory; import de.ozgcloud.xta.client.config.XtaClientConfig; -import de.ozgcloud.xta.client.exception.ClientInitializationException; +import de.ozgcloud.xta.client.exception.XtaClientInitializationException; import lombok.SneakyThrows; class XtaTLSClientParametersFactoryTest { @@ -123,7 +123,7 @@ class XtaTLSClientParametersFactoryTest { doThrow(new KeyStoreException("something")).when(sslContextBuilder).loadKeyMaterial(any(), any()); assertThatThrownBy(() -> factory.createXtaSslContext()) - .isInstanceOf(ClientInitializationException.class); + .isInstanceOf(XtaClientInitializationException.class); } } diff --git a/src/test/java/de/ozgcloud/xta/client/extension/StaticStringListAppender.java b/src/test/java/de/ozgcloud/xta/client/extension/StaticStringListAppender.java new file mode 100644 index 0000000000000000000000000000000000000000..5ea33622fcb4131b0e9722df63cc13b9346c0056 --- /dev/null +++ b/src/test/java/de/ozgcloud/xta/client/extension/StaticStringListAppender.java @@ -0,0 +1,46 @@ +package de.ozgcloud.xta.client.extension; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ConcurrentLinkedQueue; + +import org.apache.logging.log4j.core.Filter; +import org.apache.logging.log4j.core.Layout; +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.appender.AbstractAppender; +import org.apache.logging.log4j.core.config.Node; +import org.apache.logging.log4j.core.config.Property; +import org.apache.logging.log4j.core.config.plugins.Plugin; +import org.apache.logging.log4j.core.config.plugins.PluginAttribute; +import org.apache.logging.log4j.core.config.plugins.PluginFactory; + +@Plugin(name = "StaticStringList", category = Node.CATEGORY) +public class StaticStringListAppender extends AbstractAppender { + + private static final ConcurrentLinkedQueue<String> LOG_LINES = new ConcurrentLinkedQueue<>(); + + protected StaticStringListAppender(String name, Filter filter, + Layout<? extends Serializable> layout, boolean ignoreExceptions, + Property[] properties) { + super(name, filter, layout, ignoreExceptions, properties); + } + + @PluginFactory + public static StaticStringListAppender createAppender(@PluginAttribute("name") String name) { + return new StaticStringListAppender(name, null, null, true, Property.EMPTY_ARRAY); + } + + public static List<String> getLogLines() { + return new ArrayList<>(LOG_LINES); + } + + public static void clearLogLines() { + LOG_LINES.clear(); + } + + @Override + public void append(LogEvent event) { + LOG_LINES.add(event.getMessage().getFormattedMessage()); + } +} diff --git a/src/test/java/de/ozgcloud/xta/client/extension/XtaMessageExampleLoader.java b/src/test/java/de/ozgcloud/xta/client/extension/XtaMessageExampleLoader.java index acd02145e99684623327c7b8df12de151447fd15..8fa45ece8e7128c5eae684261ad4fd84c89faaee 100644 --- a/src/test/java/de/ozgcloud/xta/client/extension/XtaMessageExampleLoader.java +++ b/src/test/java/de/ozgcloud/xta/client/extension/XtaMessageExampleLoader.java @@ -131,7 +131,7 @@ public class XtaMessageExampleLoader { private static List<XtaFile> mapXtaFiles(List<Map<String, Object>> attachmentFiles, String resourcePrefix) { return attachmentFiles.stream() - .map(messageFileMap -> mapXtaFile(messageFileMap, resourcePrefix, null)) + .map(messageFileMap -> mapXtaFile(messageFileMap, resourcePrefix, (path, content) -> content)) .toList(); } diff --git a/src/test/java/de/ozgcloud/xta/client/extension/XtaRemoteServerSetupExtension.java b/src/test/java/de/ozgcloud/xta/client/extension/XtaRemoteServerSetupExtension.java index d59023bc848ab56920d9a2eed43d36f6a3b3d893..cf59130cf1751c7160074766f8741f3055d4c8a7 100644 --- a/src/test/java/de/ozgcloud/xta/client/extension/XtaRemoteServerSetupExtension.java +++ b/src/test/java/de/ozgcloud/xta/client/extension/XtaRemoteServerSetupExtension.java @@ -6,7 +6,6 @@ import java.io.File; import java.util.Objects; import org.junit.jupiter.api.extension.BeforeAllCallback; -import org.junit.jupiter.api.extension.BeforeEachCallback; import org.junit.jupiter.api.extension.ExtensionContext; import com.google.common.io.Files; @@ -15,8 +14,6 @@ import de.ozgcloud.xta.client.XtaClient; import de.ozgcloud.xta.client.XtaClientFactory; import de.ozgcloud.xta.client.config.XtaClientConfig; import de.ozgcloud.xta.client.core.WrappedXtaService; -import de.ozgcloud.xta.client.model.XtaMessage; - import lombok.Getter; import lombok.Setter; import lombok.SneakyThrows; @@ -25,12 +22,18 @@ import lombok.extern.slf4j.Slf4j; @Getter @Setter @Slf4j -public class XtaRemoteServerSetupExtension implements BeforeAllCallback, BeforeEachCallback { +public class XtaRemoteServerSetupExtension implements BeforeAllCallback { + + private XtaClient testClient; + private XtaClientConfig testClientConfig; + private XtaClient silentTestClient; + private XtaClientConfig silentTestClientConfig; + private XtaClient devClient; + private XtaClientConfig devClientConfig; + private XtaClient silentDevClient; + private XtaClientConfig silentDevClientConfig; - private XtaClient authorClient; - private XtaClient readerClient; private WrappedXtaService service; - private XtaClientConfig config; private XtaClientFactory clientFactory; static final String BASE_URL = "https://li33-0005.dp.dsecurecloud.de/MB_XTA-WS/XTA210"; @@ -38,54 +41,57 @@ public class XtaRemoteServerSetupExtension implements BeforeAllCallback, BeforeE @Override @SneakyThrows public void beforeAll(ExtensionContext context) { - authorClient = setupClient(getEnvVar("KOP_SH_KIEL_TEST_PATH"), getEnvVar("KOP_SH_KIEL_TEST_PASSWORD")); - readerClient = setupClient(getEnvVar("KOP_SH_KIEL_DEV_PATH"), getEnvVar("KOP_SH_KIEL_DEV_PASSWORD")); + testClientConfig = createClientConfig( + createClientCertKeyStore(getEnvVar("KOP_SH_KIEL_TEST_PATH"), getEnvVar("KOP_SH_KIEL_TEST_PASSWORD")), + true); + testClient = XtaClient.from(testClientConfig); + silentTestClientConfig = createClientConfig( + testClientConfig.getClientCertKeystore(), + false); + silentTestClient = XtaClient.from(silentTestClientConfig); + + devClientConfig = createClientConfig( + createClientCertKeyStore(getEnvVar("KOP_SH_KIEL_DEV_PATH"), getEnvVar("KOP_SH_KIEL_DEV_PASSWORD")), + true); + silentDevClientConfig = createClientConfig( + devClientConfig.getClientCertKeystore(), + false); + devClient = XtaClient.from(devClientConfig); + silentDevClient = XtaClient.from(silentDevClientConfig); } - @SneakyThrows - XtaClient setupClient(String clientCertKeystorePath, String clientCertKeystorePassword) { - var clientCertKeyStore = XtaClientConfig.KeyStore.builder() + private XtaClientConfig.KeyStore createClientCertKeyStore(String clientCertKeystorePath, String clientCertKeystorePassword) { + return XtaClientConfig.KeyStore.builder() .content(readBytesFromFile(clientCertKeystorePath)) .type("PKCS12") .password(clientCertKeystorePassword.toCharArray()) .build(); - config = createClientConfigBuilder() - .managementServiceUrl(BASE_URL + "managementPort.svc") - .sendServiceUrl(BASE_URL + "sendPort.svc") - .msgBoxServiceUrl(BASE_URL + "msgBoxPort.svc") + } + + @SneakyThrows + XtaClientConfig createClientConfig(XtaClientConfig.KeyStore clientCertKeyStore, boolean verbose) { + return createSpecificClientConfigBuilder() + .logSoapRequests(verbose) + .logSoapResponses(verbose) .clientCertKeystore(clientCertKeyStore) .build(); - clientFactory = XtaClientFactory.from(config); - return clientFactory.create(); } private String getEnvVar(String name) { return Objects.requireNonNull(System.getenv(name), "Environment variable " + name + " is required!"); } - @Override @SneakyThrows - public void beforeEach(ExtensionContext context) { - XtaServerSetupExtensionTestUtil.closeAllMessages(readerClient, READER_CLIENT_IDENTIFIER1); - } - - @SneakyThrows - public String sendTestMessage() { - return XtaServerSetupExtensionTestUtil.sendTestMessage(authorClient, XtaMessageExampleLoader.MessageExampleConfig.builder() - .messageLabel("dfoerdermittel") - .reader(READER_CLIENT_IDENTIFIER1) - .author(AUTHOR_CLIENT_IDENTIFIER) - .build()); - } - - @SneakyThrows - public String sendTestMessage(XtaMessage message) { - return XtaServerSetupExtensionTestUtil.sendTestMessage(authorClient, message); + private static byte[] readBytesFromFile(String path) { + return Files.toByteArray(new File(path)); } @SneakyThrows - private static byte[] readBytesFromFile(String path) { - return Files.toByteArray(new File(path)); + public XtaClientConfig.XtaClientConfigBuilder createSpecificClientConfigBuilder() { + return createClientConfigBuilder() + .managementServiceUrl(BASE_URL + "managementPort.svc") + .sendServiceUrl(BASE_URL + "sendPort.svc") + .msgBoxServiceUrl(BASE_URL + "msgBoxPort.svc"); } } diff --git a/src/test/java/de/ozgcloud/xta/client/extension/XtaServerSetupExtensionTestUtil.java b/src/test/java/de/ozgcloud/xta/client/extension/XtaServerSetupExtensionTestUtil.java index 45888f62bca53e16bcd8d6c2f29ab48c405d6144..0432d3445846171a7908c47377e0d1b32baa48be 100644 --- a/src/test/java/de/ozgcloud/xta/client/extension/XtaServerSetupExtensionTestUtil.java +++ b/src/test/java/de/ozgcloud/xta/client/extension/XtaServerSetupExtensionTestUtil.java @@ -1,13 +1,26 @@ package de.ozgcloud.xta.client.extension; +import static java.util.Collections.*; +import static org.assertj.core.api.Assertions.*; + +import java.util.Arrays; import java.util.List; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.IntStream; import de.ozgcloud.xta.client.XtaClient; import de.ozgcloud.xta.client.config.XtaClientConfig; import de.ozgcloud.xta.client.core.WrappedXtaService; +import de.ozgcloud.xta.client.core.WrappedXtaServiceFactory; +import de.ozgcloud.xta.client.exception.XtaClientRuntimeException; +import de.ozgcloud.xta.client.model.XtaFile; import de.ozgcloud.xta.client.model.XtaIdentifier; import de.ozgcloud.xta.client.model.XtaMessage; import de.ozgcloud.xta.client.model.XtaMessageMetaData; +import de.ozgcloud.xta.client.model.XtaMessageStatus; +import de.ozgcloud.xta.client.model.XtaTransportReport; import genv3.de.xoev.transport.xta.x211.CodeFehlernummer; import genv3.de.xoev.transport.xta.x211.MessageSchemaViolationException; import genv3.de.xoev.transport.xta.x211.ParameterIsNotValidException; @@ -19,52 +32,67 @@ import lombok.extern.log4j.Log4j2; @Log4j2 public class XtaServerSetupExtensionTestUtil { - public static final XtaIdentifier AUTHOR_CLIENT_IDENTIFIER = XtaIdentifier.builder() + public static final XtaIdentifier DEV_READER_CLIENT_IDENTIFIER = XtaIdentifier.builder() + .value("gae:dev-environment@ozg-cloud.de") + .category("Generischer Antragsempfänger") + .name("OZG-Cloud Leser Dev") + .build(); + public static final XtaIdentifier TEST_READER_CLIENT_IDENTIFIER = XtaIdentifier.builder() + .value("gae:test-environment@ozg-cloud.de") + .category("Generischer Antragsempfänger") + .name("OZG-Cloud Leser Test") + .build(); + + public static final XtaIdentifier TEST_AUTHOR_CLIENT_IDENTIFIER = XtaIdentifier.builder() .value("gad:010103000000") .category("DMS Schleswig-Holstein") .name("Generischer Antragsdienst") .build(); - public static final XtaIdentifier READER_CLIENT_IDENTIFIER2 = XtaIdentifier.builder() - .value("gae:test-environment@ozg-cloud.de") - .category("Generischer Antragsempfänger") - .name("OZG-Cloud Test") + + public static final XtaIdentifier AUTHOR_CLIENT_IDENTIFIER = TEST_AUTHOR_CLIENT_IDENTIFIER; + public static final XtaIdentifier AUTHOR_CLIENT_IDENTIFIER2 = XtaIdentifier.builder() + .value("ehp:010100100000") + .category("Engagement- und Hobbyportal (FIM Sender)") + .name("OSI-Onlinedienst Schleswig-Holstein Versammlungsanzeige Test") .build(); - public static final XtaIdentifier READER_CLIENT_IDENTIFIER1 = XtaIdentifier.builder() - .value("gae:dev-environment@ozg-cloud.de") - .category("Generischer Antragsempfänger") - .name("OZG-Cloud Dev") + public static final XtaIdentifier AUTHOR_CLIENT_IDENTIFIER3 = XtaIdentifier.builder() + .value("ehp:010200100000") + .category("Engagement- und Hobbyportal (FIM Sender2)") + .name("OSI-Onlinedienst Hamburg Versammlungsanzeige Test") + .build(); + + public static final XtaIdentifier READER_CLIENT_IDENTIFIER1 = DEV_READER_CLIENT_IDENTIFIER; + public static final XtaIdentifier READER_CLIENT_IDENTIFIER2 = TEST_READER_CLIENT_IDENTIFIER; + public static final XtaIdentifier READER_CLIENT_IDENTIFIER3 = XtaIdentifier.builder() + .value("vbe:010510440100") + .category("Versammlungsbehörde (FIM Empfänger)") + .name("Kreisordnungsbehörde Dithmarschen") .build(); public static XtaClientConfig.XtaClientConfigBuilder createClientConfigBuilder() { return XtaClientConfig.builder() - .clientIdentifiers(List.of(AUTHOR_CLIENT_IDENTIFIER, READER_CLIENT_IDENTIFIER2, READER_CLIENT_IDENTIFIER1)) + .clientIdentifiers(emptyList()) .logSoapRequests(true) .logSoapResponses(true); } - @SneakyThrows - public static String sendTestMessage(XtaClient client, XtaMessageExampleLoader.MessageExampleConfig messageExampleConfig) { - var message = XtaMessageExampleLoader.load(messageExampleConfig); - return sendTestMessage(client, message); - } - @SneakyThrows public static String sendTestMessage(XtaClient client, XtaMessage message) { try { log.info("Sending from author {} to reader {}.", message.metaData().authorIdentifier(), message.metaData().readerIdentifier()); var transportReport = client.sendMessage(message); return transportReport.metaData().messageId(); - } catch (ParameterIsNotValidException e) { - logCodeFehlerNummer(e.getFaultInfo().getErrorCode()); - throw e; - } catch (PermissionDeniedException e) { - logCodeFehlerNummer(e.getFaultInfo().getErrorCode()); - throw e; - } catch (MessageSchemaViolationException e) { - logCodeFehlerNummer(e.getFaultInfo().getErrorCode()); - throw e; - } catch (XTAWSTechnicalProblemException e) { - logCodeFehlerNummer(e.getFaultInfo().getErrorCode()); + } catch (XtaClientRuntimeException e) { + var cause = e.getCause(); + if (cause instanceof ParameterIsNotValidException) { + logCodeFehlerNummer(((ParameterIsNotValidException) cause).getFaultInfo().getErrorCode()); + } else if (cause instanceof PermissionDeniedException) { + logCodeFehlerNummer(((PermissionDeniedException) cause).getFaultInfo().getErrorCode()); + } else if (cause instanceof MessageSchemaViolationException) { + logCodeFehlerNummer(((MessageSchemaViolationException) cause).getFaultInfo().getErrorCode()); + } else if (cause instanceof XTAWSTechnicalProblemException) { + logCodeFehlerNummer(((XTAWSTechnicalProblemException) cause).getFaultInfo().getErrorCode()); + } throw e; } } @@ -74,23 +102,268 @@ public class XtaServerSetupExtensionTestUtil { } @SneakyThrows - public static void closeAllMessages(XtaClient client, XtaIdentifier clientId) { - var field = XtaClient.class.getDeclaredField("service"); - field.setAccessible(true); - var service = (WrappedXtaService) field.get(client); + public static void failIfAnyMessagePending(XtaClientConfig config, XtaIdentifier clientId) { + var wrappedService = createWrappedService(config); + var result = wrappedService.getStatusList(clientId, 1); + if (!result.messages().isEmpty()) { + fail("Expect no pending xta-messages for reader %s! Ensure that the mailbox is empty before running this test.".formatted( + clientId.value())); + } + } - var result = service.getStatusList(clientId, 100); + @SneakyThrows + public static void closeAllMessages(XtaClientConfig config, XtaIdentifier clientId) { + var wrappedService = createWrappedService(config); + + var result = wrappedService.getStatusList(clientId, 100); var messageIds = result.messages().stream() .map(XtaMessageMetaData::messageId) .toList(); for (var messageId : messageIds) { - service.close(messageId, clientId); + wrappedService.close(messageId, clientId); + } + } + + @SneakyThrows + private static WrappedXtaService createWrappedService(XtaClientConfig config) { + return WrappedXtaServiceFactory.from(config).create(); + } + + public static XtaMessageMetaData withoutMessageIdAndSize(XtaMessageMetaData metaData) { + return metaData.toBuilder() + .messageId(null) + .messageSize(null) + .build(); + } + + public static XtaMessageMetaData withoutMessageSize(XtaMessageMetaData metaData) { + return metaData.toBuilder() + .messageSize(null) + .build(); + } + + public record MessagesAssert(List<XtaMessage> processedMessages) { + + public MessagesAssert containExactlyInAnyOrder(XtaMessage... messages) { + try { + containExactlyInAnyOrderRaw(messages); + } catch (AssertionError | RuntimeException e) { + log.error("Messages do not exactly contain excepted messages!"); + throw e; + } + return this; + } + + private void containExactlyInAnyOrderRaw(XtaMessage... messages) { + var messageMetaData = Arrays.stream(messages) + .map(XtaMessage::metaData) + .toArray(XtaMessageMetaData[]::new); + containMetaDataExactlyInAnyOrder(messageMetaData); + + assertEqualMessageFileWithoutContent(messages); + assertEqualAttachmentFilesWithoutContent(messages); + + assertEqualContentOfMessageFile(messages); + assertEqualContentOfAttachmentFiles(messages); + } + + private void assertEqualMessageFileWithoutContent(XtaMessage... messages) { + // ignoring size since it may be null before sending + assertThat(processedMessages) + .extracting(XtaMessage::messageFile) + .extracting(this::withoutContentAndSize) + .containsExactlyInAnyOrderElementsOf(Arrays.stream(messages) + .map(XtaMessage::messageFile) + .map(this::withoutContentAndSize) + .toList()); + } + + private void assertEqualAttachmentFilesWithoutContent(XtaMessage... messages) { + // ignoring size since it may be null before sending + assertThat(processedMessages) + .extracting(XtaMessage::attachmentFiles) + .extracting(this::filesWithoutContentAndSize) + .containsExactlyInAnyOrderElementsOf(Arrays.stream(messages) + .map(XtaMessage::attachmentFiles) + .map(this::filesWithoutContentAndSize) + .toList()); + } + + private List<XtaFile> filesWithoutContentAndSize(List<XtaFile> fileList) { + return fileList.stream().map(this::withoutContentAndSize).toList(); + } + + private XtaFile withoutContentAndSize(XtaFile xtaFile) { + return xtaFile.toBuilder() + .content(null) + .size(null) + .build(); + } + + private void assertEqualContentOfMessageFile(XtaMessage... messages) { + var listOfMessageFileContents = processedMessages.stream() + .map(XtaMessage::messageFile) + .map(XtaServerSetupExtensionTestUtil::readBytesOfXtaFile) + .toList(); + var exceptedListOfMessageFileContents = Arrays.stream(messages) + .map(XtaMessage::messageFile) + .map(XtaServerSetupExtensionTestUtil::readBytesOfXtaFile) + .toList(); + assertThat(listOfMessageFileContents) + .extracting(b -> b.length) + .containsExactlyInAnyOrderElementsOf(exceptedListOfMessageFileContents.stream().map(b -> b.length).toList()); + assertThat(listOfMessageFileContents).containsExactlyInAnyOrderElementsOf(exceptedListOfMessageFileContents); + } + + private void assertEqualContentOfAttachmentFiles(XtaMessage... messages) { + var attachmentFileContents = processedMessages.stream() + .map(XtaMessage::attachmentFiles) + .map(XtaServerSetupExtensionTestUtil::readBytesOfXtaFiles) + .toList(); + var exceptedAttachmentFileContents = Arrays.stream(messages) + .map(XtaMessage::attachmentFiles) + .map(XtaServerSetupExtensionTestUtil::readBytesOfXtaFiles) + .toList(); + assertEqualLengthOfAttachmentFiles(attachmentFileContents, exceptedAttachmentFileContents); + assertEqualRawContentOfAttachmentFiles(attachmentFileContents, exceptedAttachmentFileContents); + } + + private void assertEqualLengthOfAttachmentFiles(List<List<byte[]>> attachmentFileContents, + List<List<byte[]>> exceptedAttachmentFileContents) { + Function<List<byte[]>, List<Integer>> contentLengths = contentList -> contentList.stream().map(b -> b.length).toList(); + assertThat(attachmentFileContents) + .extracting(contentLengths) + .containsExactlyInAnyOrderElementsOf(exceptedAttachmentFileContents.stream().map(contentLengths).toList()); } + + private void assertEqualRawContentOfAttachmentFiles(List<List<byte[]>> attachmentFileContents, + List<List<byte[]>> exceptedAttachmentFileContents) { + assertThat(attachmentFileContents) + .usingElementComparator(this::compareAttachmentFiles) + .containsExactlyInAnyOrderElementsOf(exceptedAttachmentFileContents); + } + + private int compareAttachmentFiles(List<byte[]> a, List<byte[]> b) { + var sizeComparison = Integer.compare(a.size(), b.size()); + return sizeComparison != 0 + ? sizeComparison + : IntStream.range(0, a.size()) + .map(i -> Arrays.compare(a.get(i), b.get(i))) + .filter(i -> i != 0) + .findFirst() + .orElse(0); + } + + public void containMetaDataExactlyInAnyOrder(XtaMessageMetaData... messageMetaDataItems) { + try { + // Assert equal message counts + assertThat(processedMessages).hasSize(messageMetaDataItems.length); + + // Assert equal metadata (ignoring message id and size since they should be null before sending) + assertThat(processedMessages) + .extracting(XtaMessage::metaData) + .extracting(XtaServerSetupExtensionTestUtil::withoutMessageIdAndSize) + .containsExactlyInAnyOrderElementsOf(Arrays.stream(messageMetaDataItems) + .map(XtaServerSetupExtensionTestUtil::withoutMessageIdAndSize) + .toList()); + } catch (AssertionError | RuntimeException e) { + log.error("Messages do not exactly contain excepted metadata!"); + throw e; + } + } + } + + public static MessagesAssert assertThatMessages(List<XtaMessage> processedMessages) { + return new MessagesAssert(processedMessages); + } + + public record TransportReportsAssert(List<XtaTransportReport> transportReports) { + public TransportReportsAssert reportExactlyFor(List<XtaMessage> processedMessages) { + try { + reportExactlyForRaw(processedMessages); + } catch (AssertionError | RuntimeException e) { + log.error("TransportReports do not exactly match messages metadata!"); + throw e; + } + return this; + } + + private void reportExactlyForRaw(List<XtaMessage> processedMessages) { + assertThat(transportReports).hasSize(processedMessages.size()); + assertEqualMessageId(processedMessages); + assertEqualMessageMetadata(processedMessages); + } + + private void assertEqualMessageId(List<XtaMessage> processedMessages) { + assertThat(transportReports) + .extracting(XtaTransportReport::metaData) + .extracting(XtaMessageMetaData::messageId) + .containsExactlyElementsOf(processedMessages.stream() + .map(XtaMessage::metaData) + .map(XtaMessageMetaData::messageId) + .toList()); + } + + private void assertEqualMessageMetadata(List<XtaMessage> processedMessages) { + // ignoring size since it may be null for transport report (due to message closed?) + assertThat(transportReports) + .extracting(XtaTransportReport::metaData) + .extracting(XtaServerSetupExtensionTestUtil::withoutMessageSize) + .containsExactlyElementsOf(processedMessages.stream() + .map(XtaMessage::metaData) + .map(XtaServerSetupExtensionTestUtil::withoutMessageSize) + .toList()); + } + + public TransportReportsAssert haveExactlyClosedStatusFor(String... messageIds) { + try { + haveExactlyClosedStatusForRaw(messageIds); + } catch (AssertionError | RuntimeException e) { + log.error("TransportReports do not have excepted closed status for messageIds!"); + throw e; + } + return this; + } + + private void haveExactlyClosedStatusForRaw(String... messageIds) { + var setOfMessageIds = Arrays.stream(messageIds).collect(Collectors.toSet()); + + assertThat(transportReports) + .allMatch(transportReport -> + isClosed(transportReport) == expectIsClosed(transportReport, setOfMessageIds) + + ); + } + + private boolean expectIsClosed(XtaTransportReport transportReport, Set<String> messageIds) { + return messageIds.contains(transportReport.metaData().messageId()); + } + + private boolean isClosed(XtaTransportReport transportReport) { + return !transportReport.status().equals(XtaMessageStatus.OPEN); + } + } + + public static TransportReportsAssert assertThatTransportReports(List<XtaTransportReport> transportReports) { + return new TransportReportsAssert(transportReports); + } + + public static boolean hasLogLineContaining(String logLine) { + return StaticStringListAppender.getLogLines().stream() + .anyMatch(line -> line.contains(logLine)); + } + + private static List<byte[]> readBytesOfXtaFiles(List<XtaFile> xtaFiles) { + return xtaFiles.stream() + .map(XtaServerSetupExtensionTestUtil::readBytesOfXtaFile) + .toList(); } @SneakyThrows - public static byte[] extractMessageFileContent(XtaMessage xtaMessage) { - return xtaMessage.messageFile().content().getInputStream().readAllBytes(); + private static byte[] readBytesOfXtaFile(XtaFile xtaFile) { + try (var inputStream = xtaFile.content().getInputStream()) { + return inputStream.readAllBytes(); + } } } diff --git a/src/test/java/de/ozgcloud/xta/client/extension/XtaTestServerSetupExtension.java b/src/test/java/de/ozgcloud/xta/client/extension/XtaTestServerSetupExtension.java index c66e62a9a48f220288bddc86a476daa4ebf34991..9001ba631dc7387f7f1d2af6fe76399307c8650d 100644 --- a/src/test/java/de/ozgcloud/xta/client/extension/XtaTestServerSetupExtension.java +++ b/src/test/java/de/ozgcloud/xta/client/extension/XtaTestServerSetupExtension.java @@ -6,7 +6,6 @@ import java.util.Objects; import org.junit.jupiter.api.extension.AfterAllCallback; import org.junit.jupiter.api.extension.BeforeAllCallback; -import org.junit.jupiter.api.extension.BeforeEachCallback; import org.junit.jupiter.api.extension.ExtensionContext; import org.testcontainers.utility.DockerImageName; @@ -22,10 +21,10 @@ import lombok.extern.slf4j.Slf4j; @Getter @Setter @Slf4j -public class XtaTestServerSetupExtension implements BeforeAllCallback, AfterAllCallback, BeforeEachCallback { +public class XtaTestServerSetupExtension implements BeforeAllCallback, AfterAllCallback { private static final DockerImageName XTA_TEST_SERVER_IMAGE = DockerImageName.parse("docker.ozg-sh.de/xta-test-server") - .withTag("latest"); + .withTag("1.6.0"); private static final String JOHN_SMITH_KEYSTORE_PATH = "store/john-smith-client-cert-keystore.p12"; private static final String JOHN_SMITH_KEYSTORE_PASSWORD = "password"; @@ -33,10 +32,12 @@ public class XtaTestServerSetupExtension implements BeforeAllCallback, AfterAllC private static final String XTA_TEST_SERVER_TRUSTSTORE_PATH = "store/xta-test-server-truststore.jks"; private static final String XTA_TEST_SERVER_TRUSTSTORE_PASSWORD = "password"; - private XtaClient client; - private XtaClientConfig config; + private XtaClient silentTestClient; + private XtaClientConfig silentTestClientConfig; private XtaClientFactory clientFactory; private XtaTestServerContainer xtaServerContainer; + private XtaClientConfig.KeyStore clientCertKeyStore; + private XtaClientConfig.KeyStore trustStore; @Override @SneakyThrows @@ -44,9 +45,27 @@ public class XtaTestServerSetupExtension implements BeforeAllCallback, AfterAllC if (xtaServerContainer != null) { return; } - setupServer(); - client = setupClient(); + setupClient(); + } + + @SneakyThrows + private void setupClient() { + clientCertKeyStore = XtaClientConfig.KeyStore.builder() + .content(readBytesFromResource(JOHN_SMITH_KEYSTORE_PATH)) + .type("PKCS12") + .password(JOHN_SMITH_KEYSTORE_PASSWORD.toCharArray()) + .build(); + trustStore = XtaClientConfig.KeyStore.builder() + .content(readBytesFromResource(XTA_TEST_SERVER_TRUSTSTORE_PATH)) + .type("JKS") + .password(XTA_TEST_SERVER_TRUSTSTORE_PASSWORD.toCharArray()) + .build(); + silentTestClientConfig = createSpecificClientConfigBuilder() + .logSoapRequests(false) + .logSoapResponses(false) + .build(); + silentTestClient = XtaClient.from(silentTestClientConfig); } private void setupServer() { @@ -66,43 +85,13 @@ public class XtaTestServerSetupExtension implements BeforeAllCallback, AfterAllC } @SneakyThrows - XtaClient setupClient() { - - var clientCertKeyStore = XtaClientConfig.KeyStore.builder() - .content(readBytesFromResource(JOHN_SMITH_KEYSTORE_PATH)) - .type("PKCS12") - .password(JOHN_SMITH_KEYSTORE_PASSWORD.toCharArray()) - .build(); - var trustStore = XtaClientConfig.KeyStore.builder() - .content(readBytesFromResource(XTA_TEST_SERVER_TRUSTSTORE_PATH)) - .type("JKS") - .password(XTA_TEST_SERVER_TRUSTSTORE_PASSWORD.toCharArray()) - .build(); - - config = createClientConfigBuilder() + public XtaClientConfig.XtaClientConfigBuilder createSpecificClientConfigBuilder() { + return createClientConfigBuilder() .managementServiceUrl(xtaServerContainer.getManagementPortUrl()) .sendServiceUrl(xtaServerContainer.getSendPortUrl()) .msgBoxServiceUrl(xtaServerContainer.getMsgBoxPortUrl()) .clientCertKeystore(clientCertKeyStore) - .trustStore(trustStore) - .build(); - clientFactory = XtaClientFactory.from(config); - return clientFactory.create(); - } - - @Override - @SneakyThrows - public void beforeEach(ExtensionContext context) { - closeAllMessages(client, READER_CLIENT_IDENTIFIER1); - } - - @SneakyThrows - public String sendTestMessage() { - return XtaServerSetupExtensionTestUtil.sendTestMessage(client, XtaMessageExampleLoader.MessageExampleConfig.builder() - .messageLabel("dfoerdermittel") - .reader(READER_CLIENT_IDENTIFIER1) - .author(READER_CLIENT_IDENTIFIER1) - .build()); + .trustStore(trustStore); } @SneakyThrows diff --git a/src/test/java/de/ozgcloud/xta/client/factory/ClientRuntimeExceptionTestFactory.java b/src/test/java/de/ozgcloud/xta/client/factory/ClientRuntimeExceptionTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..7b2d14bd1bad08067e1b6a1212d05717c08ab754 --- /dev/null +++ b/src/test/java/de/ozgcloud/xta/client/factory/ClientRuntimeExceptionTestFactory.java @@ -0,0 +1,33 @@ +package de.ozgcloud.xta.client.factory; + +import de.ozgcloud.xta.client.exception.XtaClientRuntimeException; +import genv3.de.xoev.transport.xta.x211.CodeFehlernummer; +import genv3.de.xoev.transport.xta.x211.PermissionDeniedException; +import genv3.de.xoev.transport.xta.x211.PermissionDeniedExceptionType; + +public class ClientRuntimeExceptionTestFactory { + + public static final String MESSAGE = "message"; + public static final String CAUSE_MESSAGE = "cause message"; + public static final String CAUSE_CODE = "cause code"; + public static final String CAUSE_NAME = "cause name"; + + public static XtaClientRuntimeException create() { + return new XtaClientRuntimeException(MESSAGE, createPermissionDeniedException()); + } + + private static PermissionDeniedException createPermissionDeniedException() { + return new PermissionDeniedException(CAUSE_MESSAGE, createPermissionDeniedExceptionType()); + } + + private static PermissionDeniedExceptionType createPermissionDeniedExceptionType() { + var code = new CodeFehlernummer(); + code.setCode(CAUSE_CODE); + code.setName(CAUSE_NAME); + + var exceptionType = new PermissionDeniedExceptionType(); + exceptionType.setErrorCode(code); + return exceptionType; + } + +} diff --git a/src/test/java/de/ozgcloud/xta/client/factory/XtaClientExceptionTestFactory.java b/src/test/java/de/ozgcloud/xta/client/factory/XtaClientExceptionTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..ac6767647f556c06078c2ac90025f966ba5207a4 --- /dev/null +++ b/src/test/java/de/ozgcloud/xta/client/factory/XtaClientExceptionTestFactory.java @@ -0,0 +1,12 @@ +package de.ozgcloud.xta.client.factory; + +import de.ozgcloud.xta.client.exception.XtaClientException; + +public class XtaClientExceptionTestFactory { + + public static final String CLIENT_EXCEPTION_MESSAGE = "CLIENT_EXCEPTION_MESSAGE"; + + public static XtaClientException create() { + return new XtaClientException(CLIENT_EXCEPTION_MESSAGE, ClientRuntimeExceptionTestFactory.create().getCause()); + } +} diff --git a/src/test/java/de/ozgcloud/xta/client/factory/XtaMessageMetaDataListingTestFactory.java b/src/test/java/de/ozgcloud/xta/client/factory/XtaMessageMetaDataListingTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..3da8ca5d25feb8ad894c7cb5ab43f1f955da93b5 --- /dev/null +++ b/src/test/java/de/ozgcloud/xta/client/factory/XtaMessageMetaDataListingTestFactory.java @@ -0,0 +1,31 @@ +package de.ozgcloud.xta.client.factory; + +import static de.ozgcloud.xta.client.factory.MessageMetaDataTestFactory.*; + +import java.math.BigInteger; +import java.util.List; + +import de.ozgcloud.xta.client.model.XtaMessageMetaDataListing; + +public class XtaMessageMetaDataListingTestFactory { + + public final static BigInteger PENDING_MESSAGES_COUNT = BigInteger.valueOf(7); + + public static XtaMessageMetaDataListing create() { + return createBuilder().build(); + } + + public static XtaMessageMetaDataListing.XtaMessageMetaDataListingBuilder createBuilder() { + return XtaMessageMetaDataListing.builder() + .messages(List.of(XtaMessageMetaDataTestFactory.create(), + XtaMessageMetaDataTestFactory.createBuilder() + .messageId(MESSAGE_ID2) + .messageTypeCode(MESSAGE_TYPE_CODE2) + .build(), + XtaMessageMetaDataTestFactory.createBuilder() + .messageId(MESSAGE_ID3) + .messageTypeCode(MESSAGE_TYPE_CODE3) + .build())) + .pendingMessageCount(PENDING_MESSAGES_COUNT); + } +} diff --git a/src/test/java/de/ozgcloud/xta/client/factory/XtaTransportReportTestFactory.java b/src/test/java/de/ozgcloud/xta/client/factory/XtaTransportReportTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..33d54a78cce10cf194d8c9b9a99f37057a68f580 --- /dev/null +++ b/src/test/java/de/ozgcloud/xta/client/factory/XtaTransportReportTestFactory.java @@ -0,0 +1,21 @@ +package de.ozgcloud.xta.client.factory; + +import static de.ozgcloud.xta.client.factory.TransportReportTestFactory.*; + + +import de.ozgcloud.xta.client.model.XtaMessageStatus; +import de.ozgcloud.xta.client.model.XtaTransportReport; + +public class XtaTransportReportTestFactory { + + public static XtaTransportReport create() { + return createBuilder().build(); + } + + public static XtaTransportReport.XtaTransportReportBuilder createBuilder() { + return XtaTransportReport.builder() + .reportTime(REPORT_TIME) + .metaData(XtaMessageMetaDataTestFactory.create()) + .status(XtaMessageStatus.OPEN); + } +} diff --git a/src/test/java/de/ozgcloud/xta/client/xdomea/XdomeaMetaDataValidatorTest.java b/src/test/java/de/ozgcloud/xta/client/xdomea/XdomeaMetaDataValidatorTest.java index b3251bedc7fb829bd22a35a6d6c6f648c678626c..97dd894d8b6c47a01863919b007736e7363bb99a 100644 --- a/src/test/java/de/ozgcloud/xta/client/xdomea/XdomeaMetaDataValidatorTest.java +++ b/src/test/java/de/ozgcloud/xta/client/xdomea/XdomeaMetaDataValidatorTest.java @@ -21,7 +21,7 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; -import de.ozgcloud.xta.client.exception.ClientException; +import de.ozgcloud.xta.client.exception.XtaClientException; import de.ozgcloud.xta.client.factory.XdomeaXmlValuesTestFactory; import de.ozgcloud.xta.client.factory.XtaFileTestFactory; import de.ozgcloud.xta.client.factory.XtaMessageMetaDataTestFactory; @@ -170,7 +170,7 @@ class XdomeaMetaDataValidatorTest { }) void shouldThrowClientExceptionForWrongPrefix(String prefix) { assertThatThrownBy(() -> validator.validateIdentifierPrefix(prefix, EXPECTED_PREFIX, PREFIX_NAME)) - .isInstanceOf(ClientException.class) + .isInstanceOf(XtaClientException.class) .hasMessageContaining(PREFIX_NAME); } @@ -200,7 +200,7 @@ class XdomeaMetaDataValidatorTest { }) void shouldThrowClientExceptionForWrongZipFileName(String wrongZipFileName) { assertThatThrownBy(() -> validator.validateZipFileName(wrongZipFileName, PROCESS_ID, MESSAGE_TYPE_CODE)) - .isInstanceOf(ClientException.class); + .isInstanceOf(XtaClientException.class); } } @@ -241,7 +241,7 @@ class XdomeaMetaDataValidatorTest { when(zipFileEntryReader.getEntryNames(contentDataHandler)).thenReturn(INVALID_ZIP_ENTRY_NAMES); assertThatThrownBy(() -> validator.validatePrimaryDocumentReferences(xdomeaZipFile, PRIMARY_DOCUMENT_NAMES)) - .isInstanceOf(ClientException.class); + .isInstanceOf(XtaClientException.class); } } diff --git a/src/test/java/de/ozgcloud/xta/client/xdomea/XdomeaXtaMessageCreatorITCase.java b/src/test/java/de/ozgcloud/xta/client/xdomea/XdomeaXtaMessageCreatorITCase.java index 6256c854dbd400a3fabd26ed0f5520ac5dd89f13..7ff3ffd6754a4682db3cf3c660134f77127a6ee0 100644 --- a/src/test/java/de/ozgcloud/xta/client/xdomea/XdomeaXtaMessageCreatorITCase.java +++ b/src/test/java/de/ozgcloud/xta/client/xdomea/XdomeaXtaMessageCreatorITCase.java @@ -11,7 +11,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -import de.ozgcloud.xta.client.exception.ClientException; +import de.ozgcloud.xta.client.exception.XtaClientException; import de.ozgcloud.xta.client.extension.XtaMessageExampleLoader; import de.ozgcloud.xta.client.model.XtaFile; import lombok.SneakyThrows; @@ -77,7 +77,7 @@ class XdomeaXtaMessageCreatorITCase { ); assertThatThrownBy(() -> creator.createMessage(invalidMessageZipFile)) - .isInstanceOf(ClientException.class); + .isInstanceOf(XtaClientException.class); } @DisplayName("should not throw with valid message 0201") @@ -118,7 +118,7 @@ class XdomeaXtaMessageCreatorITCase { ); assertThatThrownBy(() -> creator.createMessage(invalidMessageZipFile)) - .isInstanceOf(ClientException.class); + .isInstanceOf(XtaClientException.class); } @DisplayName("should not throw with valid message 0401") diff --git a/src/test/java/de/ozgcloud/xta/client/xdomea/XdomeaXtaMessageCreatorTest.java b/src/test/java/de/ozgcloud/xta/client/xdomea/XdomeaXtaMessageCreatorTest.java index 6ffa37b6177f2022c068f847707a02c5d8e36d3f..a183007f589a4083b82296c8c9331825a434c727 100644 --- a/src/test/java/de/ozgcloud/xta/client/xdomea/XdomeaXtaMessageCreatorTest.java +++ b/src/test/java/de/ozgcloud/xta/client/xdomea/XdomeaXtaMessageCreatorTest.java @@ -20,7 +20,7 @@ import org.mockito.Spy; import org.w3c.dom.Document; import de.ozgcloud.common.errorhandling.TechnicalException; -import de.ozgcloud.xta.client.exception.ClientException; +import de.ozgcloud.xta.client.exception.XtaClientException; import de.ozgcloud.xta.client.factory.XdomeaXmlValuesTestFactory; import de.ozgcloud.xta.client.factory.XtaFileTestFactory; import de.ozgcloud.xta.client.factory.XtaMessageMetaDataTestFactory; @@ -156,7 +156,7 @@ class XdomeaXtaMessageCreatorTest { doThrow(technicalException).when(metadataMapper).mapXtaMessageMetadata(xdomeaXmlValues); assertThatThrownBy(() -> creator.deriveValidMetaData(xdomeaZipFile)) - .isInstanceOf(ClientException.class); + .isInstanceOf(XtaClientException.class); } } diff --git a/src/test/java/de/ozgcloud/xta/client/xdomea/reader/XdomeaValueReaderTest.java b/src/test/java/de/ozgcloud/xta/client/xdomea/reader/XdomeaValueReaderTest.java index 9c72e7ab2ab52438c42b923abbc78b4b519d335e..5b8fd71b95ed7d76f2527f3cbce0442311f636f2 100644 --- a/src/test/java/de/ozgcloud/xta/client/xdomea/reader/XdomeaValueReaderTest.java +++ b/src/test/java/de/ozgcloud/xta/client/xdomea/reader/XdomeaValueReaderTest.java @@ -16,7 +16,7 @@ import org.mockito.Mock; import org.mockito.Spy; import org.w3c.dom.Document; -import de.ozgcloud.xta.client.exception.ClientException; +import de.ozgcloud.xta.client.exception.XtaClientException; import lombok.SneakyThrows; class XdomeaValueReaderTest { @@ -183,7 +183,7 @@ class XdomeaValueReaderTest { when(xmlValueReader.readNonEmptyTexts(xdomeaXmlDocument)).thenReturn(Stream.empty()); assertThatThrownBy(() -> xdomeaValueReader.readRequiredValue(xdomeaXmlDocument, XPATH_STRING)) - .isInstanceOf(ClientException.class); + .isInstanceOf(XtaClientException.class); } } diff --git a/src/test/resources/log4j2.xml b/src/test/resources/log4j2.xml index 71f815985778d395c5f74d051be77a7e04cbf83b..b2ca53a3d66fedb98bddcef50e5e133fcb135777 100644 --- a/src/test/resources/log4j2.xml +++ b/src/test/resources/log4j2.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<Configuration name="log4j2Config" status="WARN"> +<Configuration name="log4j2Config" status="WARN" packages="de.ozgcloud.xta.client.extension"> <Appenders> <Console name="Console" target="SYSTEM_OUT"> @@ -9,11 +9,13 @@ onMismatch="DENY" /> </filters> </Console> + <StaticStringList name="StaticStringList" /> </Appenders> <Loggers> <Logger name="de.ozgcloud.xta" level="debug" additivity="false"> <AppenderRef ref="Console" level="${env:LOG_LEVEL_STDOUT:-debug}" /> + <AppenderRef ref="StaticStringList" level="debug" /> </Logger> <Root level="info"> diff --git a/src/test/resources/messages/abgabe0401-kleiner-waffenschein/xta-message.yaml b/src/test/resources/messages/abgabe0401-kleiner-waffenschein/xta-message.yaml index 1b1adb5a6c724aa8b16aefbb1f79956713cdac01..4be291a5f51c45b7c77fbda95b9c7bc9bf70abb7 100644 --- a/src/test/resources/messages/abgabe0401-kleiner-waffenschein/xta-message.yaml +++ b/src/test/resources/messages/abgabe0401-kleiner-waffenschein/xta-message.yaml @@ -1,5 +1,5 @@ metaData: - service: urn:xoev-de:xdomea:schema:3.0.0/xdomea300Antrag.wsdl + service: urn:xoev-de:xdomea:schema:3.0.0/xdomea300Abgabe.wsdl businessScenarioCode: XDOMEAGAD_DATA businessScenarioListUri: urn:de:dataport:codeliste:business.scenario businessScenarioListVersionId: 1.0