diff options
Diffstat (limited to 'datafile-app-server/src/test/java')
10 files changed, 504 insertions, 67 deletions
diff --git a/datafile-app-server/src/test/java/org/onap/dcaegen2/collectors/datafile/configuration/AppConfigTest.java b/datafile-app-server/src/test/java/org/onap/dcaegen2/collectors/datafile/configuration/AppConfigTest.java index b3710fe3..d9ca7871 100644 --- a/datafile-app-server/src/test/java/org/onap/dcaegen2/collectors/datafile/configuration/AppConfigTest.java +++ b/datafile-app-server/src/test/java/org/onap/dcaegen2/collectors/datafile/configuration/AppConfigTest.java @@ -19,17 +19,20 @@ package org.onap.dcaegen2.collectors.datafile.configuration; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.read.ListAppender; - import com.google.common.base.Charsets; import com.google.common.io.Resources; import com.google.gson.JsonElement; @@ -37,16 +40,15 @@ import com.google.gson.JsonIOException; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import com.google.gson.JsonSyntaxException; - import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; import java.nio.charset.StandardCharsets; +import java.util.HashMap; import java.util.Map; import java.util.Properties; - import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -56,9 +58,11 @@ import org.onap.dcaegen2.collectors.datafile.utils.LoggingUtils; import org.onap.dcaegen2.services.sdk.rest.services.cbs.client.http.configuration.EnvProperties; import org.onap.dcaegen2.services.sdk.rest.services.cbs.client.http.configuration.ImmutableEnvProperties; import org.onap.dcaegen2.services.sdk.rest.services.cbs.client.providers.CloudConfigurationProvider; +import org.onap.dcaegen2.services.sdk.rest.services.dmaap.client.config.DmaapConsumerConfiguration; +import org.onap.dcaegen2.services.sdk.rest.services.dmaap.client.config.DmaapPublisherConfiguration; import org.onap.dcaegen2.services.sdk.rest.services.dmaap.client.config.ImmutableDmaapConsumerConfiguration; import org.onap.dcaegen2.services.sdk.rest.services.dmaap.client.config.ImmutableDmaapPublisherConfiguration; - +import reactor.core.Disposable; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; @@ -73,7 +77,7 @@ class AppConfigTest { private static final String CHANGE_IDENTIFIER = "PM_MEAS_FILES"; - private static final ImmutableDmaapConsumerConfiguration CORRECT_DMAAP_CONSUMER_CONFIG = // + private static final DmaapConsumerConfiguration CORRECT_DMAAP_CONSUMER_CONFIG = // new ImmutableDmaapConsumerConfiguration.Builder() // .timeoutMs(-1) // .dmaapHostName("message-router.onap.svc.cluster.local") // @@ -116,7 +120,7 @@ class AppConfigTest { .passWord("password") // .build(); - private static final ImmutableFtpesConfig CORRECT_FTPES_CONFIGURATION = // + private static final FtpesConfig CORRECT_FTPES_CONFIGURATION = // new ImmutableFtpesConfig.Builder() // .keyCert("/config/dfc.jks") // .keyPassword("secret") // @@ -124,7 +128,7 @@ class AppConfigTest { .trustedCaPassword("secret") // .build(); - private static final ImmutableDmaapPublisherConfiguration CORRECT_DMAAP_PUBLISHER_CONFIG = // + private static final DmaapPublisherConfiguration CORRECT_DMAAP_PUBLISHER_CONFIG = // new ImmutableDmaapPublisherConfiguration.Builder() // .dmaapTopicName("/publish/1") // .dmaapUserPassword("password") // @@ -331,6 +335,37 @@ class AppConfigTest { Assertions.assertNotNull(appConfigUnderTest.getDmaapConsumerConfiguration()); } + @Test + public void whenStopSuccess() { + Disposable disposableMock = mock(Disposable.class); + appConfigUnderTest.refreshConfigTask = disposableMock; + + appConfigUnderTest.stop(); + + verify(disposableMock).dispose(); + verifyNoMoreInteractions(disposableMock); + assertNull(appConfigUnderTest.refreshConfigTask); + } + + @Test + public void whenNoPublisherConfigurationThrowException() throws DatafileTaskException { + appConfigUnderTest.publishingConfigurations = new HashMap<>(); + + DatafileTaskException exception = assertThrows(DatafileTaskException.class, + () -> appConfigUnderTest.getPublisherConfiguration(CHANGE_IDENTIFIER)); + assertEquals("Cannot find getPublishingConfiguration for changeIdentifier: " + CHANGE_IDENTIFIER, + exception.getMessage()); + } + + @Test + public void whenFeedIsConfiguredReturnTrue() { + HashMap<String, PublisherConfiguration> publishingConfigs = new HashMap<>(); + publishingConfigs.put(CHANGE_IDENTIFIER, null); + appConfigUnderTest.publishingConfigurations = publishingConfigs; + + assertTrue(appConfigUnderTest.isFeedConfigured(CHANGE_IDENTIFIER)); + } + private JsonObject getJsonRootObject() throws JsonIOException, JsonSyntaxException, IOException { JsonObject rootObject = (new JsonParser()).parse(new InputStreamReader(getCorrectJson())).getAsJsonObject(); return rootObject; diff --git a/datafile-app-server/src/test/java/org/onap/dcaegen2/collectors/datafile/configuration/ConsumerConfigurationTest.java b/datafile-app-server/src/test/java/org/onap/dcaegen2/collectors/datafile/configuration/ConsumerConfigurationTest.java new file mode 100644 index 00000000..bdeb1c1e --- /dev/null +++ b/datafile-app-server/src/test/java/org/onap/dcaegen2/collectors/datafile/configuration/ConsumerConfigurationTest.java @@ -0,0 +1,101 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2019 Nordix Foundation. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.dcaegen2.collectors.datafile.configuration; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; +import org.onap.dcaegen2.collectors.datafile.exceptions.DatafileTaskException; +import org.onap.dcaegen2.services.sdk.rest.services.dmaap.client.config.DmaapConsumerConfiguration; + +public class ConsumerConfigurationTest { + @Test + public void toDmaapSuccess() throws DatafileTaskException { + ConsumerConfiguration configurationUnderTest = ImmutableConsumerConfiguration.builder() // + .topicUrl( + "http://admin:admin@message-router.onap.svc.cluster.local:2222/events/unauthenticated.VES_NOTIFICATION_OUTPUT/OpenDcae-c12/C12") + .trustStorePath("") // + .trustStorePasswordPath("") // + .keyStorePath("") // + .keyStorePasswordPath("") // + .enableDmaapCertAuth(Boolean.FALSE) // + .build(); + + DmaapConsumerConfiguration dmaapConsumerConfiguration = configurationUnderTest.toDmaap(); + assertEquals("http", dmaapConsumerConfiguration.dmaapProtocol()); + assertEquals("message-router.onap.svc.cluster.local", dmaapConsumerConfiguration.dmaapHostName()); + assertEquals(Integer.valueOf("2222"), dmaapConsumerConfiguration.dmaapPortNumber()); + assertEquals("OpenDcae-c12", dmaapConsumerConfiguration.consumerGroup()); + assertEquals("C12", dmaapConsumerConfiguration.consumerId()); + } + + @Test + public void toDmaapNoUserInfoSuccess() throws DatafileTaskException { + ConsumerConfiguration configurationUnderTest = ImmutableConsumerConfiguration.builder() // + .topicUrl( + "http://message-router.onap.svc.cluster.local:2222/events/unauthenticated.VES_NOTIFICATION_OUTPUT/OpenDcae-c12/C12") + .trustStorePath("") // + .trustStorePasswordPath("") // + .keyStorePath("") // + .keyStorePasswordPath("") // + .enableDmaapCertAuth(Boolean.FALSE) // + .build(); + + DmaapConsumerConfiguration dmaapConsumerConfiguration = configurationUnderTest.toDmaap(); + assertEquals("http", dmaapConsumerConfiguration.dmaapProtocol()); + assertEquals("message-router.onap.svc.cluster.local", dmaapConsumerConfiguration.dmaapHostName()); + assertEquals(Integer.valueOf("2222"), dmaapConsumerConfiguration.dmaapPortNumber()); + assertEquals("OpenDcae-c12", dmaapConsumerConfiguration.consumerGroup()); + assertEquals("C12", dmaapConsumerConfiguration.consumerId()); + } + + @Test + public void toDmaapWhenInvalidUrlThrowException() throws DatafileTaskException { + ConsumerConfiguration configurationUnderTest = ImmutableConsumerConfiguration.builder() // + .topicUrl("//admin:admin@message-router.onap.svc.cluster.local:2222//events/").trustStorePath("") // + .trustStorePasswordPath("") // + .keyStorePath("") // + .keyStorePasswordPath("") // + .enableDmaapCertAuth(Boolean.FALSE) // + .build(); + + DatafileTaskException exception = + assertThrows(DatafileTaskException.class, () -> configurationUnderTest.toDmaap()); + assertEquals("Could not parse the URL", exception.getMessage()); + } + + @Test + public void toDmaapWhenInvalidPathThrowException() throws DatafileTaskException { + ConsumerConfiguration configurationUnderTest = ImmutableConsumerConfiguration.builder() // + .topicUrl("http://admin:admin@message-router.onap.svc.cluster.local:2222//events/") // + .trustStorePath("") // + .trustStorePasswordPath("") // + .keyStorePath("") // + .keyStorePasswordPath("") // + .enableDmaapCertAuth(Boolean.FALSE) // + .build(); + + DatafileTaskException exception = + assertThrows(DatafileTaskException.class, () -> configurationUnderTest.toDmaap()); + assertEquals("The path has incorrect syntax: //events/", exception.getMessage()); + } +} diff --git a/datafile-app-server/src/test/java/org/onap/dcaegen2/collectors/datafile/controllers/ScheduleControllerTest.java b/datafile-app-server/src/test/java/org/onap/dcaegen2/collectors/datafile/controllers/ScheduleControllerTest.java new file mode 100644 index 00000000..b630bd09 --- /dev/null +++ b/datafile-app-server/src/test/java/org/onap/dcaegen2/collectors/datafile/controllers/ScheduleControllerTest.java @@ -0,0 +1,124 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2019 Nordix Foundation. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.dcaegen2.collectors.datafile.controllers; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.when; + +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.read.ListAppender; +import org.apache.commons.lang3.StringUtils; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.onap.dcaegen2.collectors.datafile.configuration.SchedulerConfig; +import org.onap.dcaegen2.collectors.datafile.utils.LoggingUtils; +import org.onap.dcaegen2.services.sdk.rest.services.model.logging.MdcVariables; +import org.slf4j.MDC; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import reactor.core.publisher.Mono; + +@ExtendWith(MockitoExtension.class) +public class ScheduleControllerTest { + @Mock + private SchedulerConfig schedulerConfigMock; + + private ScheduleController scheduleControllerUnderTest; + + @BeforeEach + public void setup() { + scheduleControllerUnderTest = new ScheduleController(schedulerConfigMock); + } + + @Test + public void startTasksSuccess() { + when(schedulerConfigMock.tryToStartTask()).thenReturn(Boolean.TRUE); + + HttpHeaders httpHeaders = new HttpHeaders(); + + ListAppender<ILoggingEvent> logAppender = LoggingUtils.getLogListAppender(ScheduleController.class); + Mono<ResponseEntity<String>> response = scheduleControllerUnderTest.startTasks(httpHeaders); + + validateLogging(logAppender, "Start request"); + + String body = response.block().getBody(); + assertTrue(body.startsWith("Datafile Service has been started!")); + + assertFalse(StringUtils.isBlank(MDC.get(MdcVariables.REQUEST_ID))); + assertFalse(StringUtils.isBlank(MDC.get(MdcVariables.INVOCATION_ID))); + } + + @Test + public void startTasksFail() { + when(schedulerConfigMock.tryToStartTask()).thenReturn(Boolean.FALSE); + + HttpHeaders httpHeaders = new HttpHeaders(); + // The following headers are set to create branch coverage in MappedDiagnosticContext:initializeTraceContext(). + httpHeaders.set(MdcVariables.X_ONAP_REQUEST_ID, "Onap request ID"); + httpHeaders.set(MdcVariables.X_INVOCATION_ID, "Invocation ID"); + + ListAppender<ILoggingEvent> logAppender = LoggingUtils.getLogListAppender(ScheduleController.class); + Mono<ResponseEntity<String>> response = scheduleControllerUnderTest.startTasks(httpHeaders); + + validateLogging(logAppender, "Start request"); + + String body = response.block().getBody(); + assertTrue(body.startsWith("Datafile Service is still running!")); + + assertFalse(StringUtils.isBlank(MDC.get(MdcVariables.REQUEST_ID))); + assertFalse(StringUtils.isBlank(MDC.get(MdcVariables.INVOCATION_ID))); + } + + @Test + public void stopTaskSuccess() { + when(schedulerConfigMock.getResponseFromCancellationOfTasks()).thenReturn( + Mono.just(new ResponseEntity<>("Datafile Service has already been stopped!", HttpStatus.CREATED))); + + HttpHeaders httpHeaders = new HttpHeaders(); + + ListAppender<ILoggingEvent> logAppender = LoggingUtils.getLogListAppender(ScheduleController.class); + Mono<ResponseEntity<String>> actualResponse = scheduleControllerUnderTest.stopTask(httpHeaders); + + validateLogging(logAppender, "Stop request"); + + String body = actualResponse.block().getBody(); + assertTrue(body.startsWith("Datafile Service has already been stopped!")); + + assertFalse(StringUtils.isBlank(MDC.get(MdcVariables.REQUEST_ID))); + assertFalse(StringUtils.isBlank(MDC.get(MdcVariables.INVOCATION_ID))); + } + + private void validateLogging(ListAppender<ILoggingEvent> logAppender, String infoMessage) { + assertEquals(logAppender.list.get(0).getMarker().getName(), "ENTRY"); + assertNotNull(logAppender.list.get(0).getMDCPropertyMap().get("InvocationID")); + assertNotNull(logAppender.list.get(0).getMDCPropertyMap().get("RequestID")); + assertTrue("Info missing in log", logAppender.list.toString().contains("[INFO] " + infoMessage)); + assertEquals(logAppender.list.get(1).getMarker().getName(), "EXIT"); + logAppender.stop(); + } +} diff --git a/datafile-app-server/src/test/java/org/onap/dcaegen2/collectors/datafile/controller/StatusControllerTest.java b/datafile-app-server/src/test/java/org/onap/dcaegen2/collectors/datafile/controllers/StatusControllerTest.java index a80999ed..9b8197f9 100644 --- a/datafile-app-server/src/test/java/org/onap/dcaegen2/collectors/datafile/controller/StatusControllerTest.java +++ b/datafile-app-server/src/test/java/org/onap/dcaegen2/collectors/datafile/controllers/StatusControllerTest.java @@ -18,21 +18,22 @@ * ============LICENSE_END========================================================= */ -package org.onap.dcaegen2.collectors.datafile.controller; +package org.onap.dcaegen2.collectors.datafile.controllers; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.read.ListAppender; - import org.apache.commons.lang3.StringUtils; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.onap.dcaegen2.collectors.datafile.controllers.StatusController; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; import org.onap.dcaegen2.collectors.datafile.model.Counters; import org.onap.dcaegen2.collectors.datafile.tasks.ScheduledTasks; import org.onap.dcaegen2.collectors.datafile.utils.LoggingUtils; @@ -42,15 +43,22 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.ResponseEntity; import reactor.core.publisher.Mono; +@ExtendWith(MockitoExtension.class) public class StatusControllerTest { + @Mock + ScheduledTasks scheduledTasksMock; + + StatusController controllerUnderTest; + + @BeforeEach + public void setup() { + controllerUnderTest = new StatusController(scheduledTasksMock); + } + @Test public void heartbeat_success() { - ScheduledTasks scheduledTasksMock = mock(ScheduledTasks.class); - HttpHeaders httpHeaders = new HttpHeaders(); - StatusController controllerUnderTest = new StatusController(scheduledTasksMock); - ListAppender<ILoggingEvent> logAppender = LoggingUtils.getLogListAppender(StatusController.class); Mono<ResponseEntity<String>> result = controllerUnderTest.heartbeat(httpHeaders); @@ -65,14 +73,11 @@ public class StatusControllerTest { @Test public void status() { - ScheduledTasks scheduledTasksMock = mock(ScheduledTasks.class); Counters counters = new Counters(); doReturn(counters).when(scheduledTasksMock).getCounters(); HttpHeaders httpHeaders = new HttpHeaders(); - StatusController controllerUnderTest = new StatusController(scheduledTasksMock); - Mono<ResponseEntity<String>> result = controllerUnderTest.status(httpHeaders); String body = result.block().getBody(); diff --git a/datafile-app-server/src/test/java/org/onap/dcaegen2/collectors/datafile/ftp/SftpClientTest.java b/datafile-app-server/src/test/java/org/onap/dcaegen2/collectors/datafile/ftp/SftpClientTest.java index 74d9ecd1..5330a7f3 100644 --- a/datafile-app-server/src/test/java/org/onap/dcaegen2/collectors/datafile/ftp/SftpClientTest.java +++ b/datafile-app-server/src/test/java/org/onap/dcaegen2/collectors/datafile/ftp/SftpClientTest.java @@ -17,6 +17,8 @@ package org.onap.dcaegen2.collectors.datafile.ftp; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doReturn; @@ -31,16 +33,15 @@ import com.jcraft.jsch.JSch; import com.jcraft.jsch.JSchException; import com.jcraft.jsch.Session; import com.jcraft.jsch.SftpException; - import java.io.IOException; import java.nio.file.Paths; import java.util.Optional; - import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.onap.dcaegen2.collectors.datafile.exceptions.DatafileTaskException; +import org.onap.dcaegen2.collectors.datafile.exceptions.NonRetryableDatafileTaskException; @ExtendWith(MockitoExtension.class) public class SftpClientTest { @@ -109,7 +110,7 @@ public class SftpClientTest { } @Test - public void open_throwsException() + public void open_throwsExceptionWithRetry() throws DatafileTaskException, IOException, JSchException, SftpException, Exception { FileServerData expectedFileServerData = ImmutableFileServerData.builder() // .serverAddress(HOST) // @@ -123,7 +124,30 @@ public class SftpClientTest { doReturn(jschMock).when(sftpClientSpy).createJsch(); when(jschMock.getSession(anyString(), anyString(), anyInt())).thenThrow(new JSchException("Failed")); - assertThatThrownBy(() -> sftpClientSpy.open()).hasMessageStartingWith("Could not open Sftp client."); + DatafileTaskException exception = assertThrows(DatafileTaskException.class, () -> sftpClientSpy.open()); + assertEquals("Could not open Sftp client. com.jcraft.jsch.JSchException: Failed", exception.getMessage()); + } + + @Test + public void openAuthFail_throwsExceptionWithoutRetry() + throws DatafileTaskException, IOException, JSchException, SftpException, Exception { + FileServerData expectedFileServerData = ImmutableFileServerData.builder() // + .serverAddress(HOST) // + .userId(USERNAME) // + .password(PASSWORD) // + .port(SFTP_PORT) // + .build(); + + SftpClient sftpClientSpy = spy(new SftpClient(expectedFileServerData)); + + doReturn(jschMock).when(sftpClientSpy).createJsch(); + when(jschMock.getSession(anyString(), anyString(), anyInt())).thenThrow(new JSchException("Auth fail")); + + NonRetryableDatafileTaskException exception = + assertThrows(NonRetryableDatafileTaskException.class, () -> sftpClientSpy.open()); + assertEquals( + "Could not open Sftp client, no retry attempts will be done. com.jcraft.jsch.JSchException: Auth fail", + exception.getMessage()); } @SuppressWarnings("resource") @@ -146,7 +170,29 @@ public class SftpClientTest { } @Test - public void collectFile_throwsExceptionWithoutRetry() + public void collectFile_throwsExceptionWithRetry() + throws IOException, JSchException, SftpException, DatafileTaskException { + FileServerData expectedFileServerData = ImmutableFileServerData.builder() // + .serverAddress(HOST) // + .userId(USERNAME) // + .password(PASSWORD) // + .port(SFTP_PORT) // + .build(); + + try (SftpClient sftpClient = new SftpClient(expectedFileServerData)) { + sftpClient.sftpChannel = channelMock; + doThrow(new SftpException(ChannelSftp.SSH_FX_BAD_MESSAGE, "Failed")).when(channelMock).get(anyString(), + anyString()); + + assertThatThrownBy(() -> sftpClient.collectFile("remoteFile", Paths.get("localFile"))) + .isInstanceOf(DatafileTaskException.class).hasMessageStartingWith("Unable to get file from xNF. ") + .hasMessageContaining("Data: FileServerData{serverAddress=" + HOST + ", " + "userId=" + USERNAME + + ", password=####, port=" + SFTP_PORT); + } + } + + @Test + public void collectFileFileMissing_throwsExceptionWithoutRetry() throws IOException, JSchException, SftpException, DatafileTaskException { FileServerData expectedFileServerData = ImmutableFileServerData.builder() // .serverAddress(HOST) // @@ -161,7 +207,7 @@ public class SftpClientTest { anyString()); assertThatThrownBy(() -> sftpClient.collectFile("remoteFile", Paths.get("localFile"))) - .isInstanceOf(DatafileTaskException.class) + .isInstanceOf(NonRetryableDatafileTaskException.class) .hasMessageStartingWith("Unable to get file from xNF. No retry attempts will be done") .hasMessageContaining("Data: FileServerData{serverAddress=" + HOST + ", " + "userId=" + USERNAME + ", password=####, port=" + SFTP_PORT); diff --git a/datafile-app-server/src/test/java/org/onap/dcaegen2/collectors/datafile/service/DmaapWebClientTest.java b/datafile-app-server/src/test/java/org/onap/dcaegen2/collectors/datafile/service/DmaapWebClientTest.java index 9aaca4bf..1e54d29d 100644 --- a/datafile-app-server/src/test/java/org/onap/dcaegen2/collectors/datafile/service/DmaapWebClientTest.java +++ b/datafile-app-server/src/test/java/org/onap/dcaegen2/collectors/datafile/service/DmaapWebClientTest.java @@ -16,36 +16,91 @@ package org.onap.dcaegen2.collectors.datafile.service; +import static org.junit.Assert.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import org.junit.jupiter.api.BeforeEach; +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.read.ListAppender; +import java.net.URI; +import java.net.URISyntaxException; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.onap.dcaegen2.collectors.datafile.utils.LoggingUtils; import org.onap.dcaegen2.services.sdk.rest.services.dmaap.client.config.DmaapConsumerConfiguration; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.web.reactive.function.client.ClientRequest; +import org.springframework.web.reactive.function.client.ClientResponse; import org.springframework.web.reactive.function.client.WebClient; +import reactor.core.publisher.Mono; +@ExtendWith(MockitoExtension.class) class DmaapWebClientTest { @Mock - private DmaapConsumerConfiguration dmaapConsumerConfiguration; + private DmaapConsumerConfiguration dmaapConsumerConfigurationMock; - @BeforeEach - void setUp() { - dmaapConsumerConfiguration = mock(DmaapConsumerConfiguration.class); - } + @Mock + private ClientResponse clientResponseMock; + + @Mock + private ClientRequest clientRequesteMock; @Test void buildsDMaaPReactiveWebClientProperly() { - when(dmaapConsumerConfiguration.dmaapContentType()).thenReturn("*/*"); - WebClient dmaapWebClient = new DmaapWebClient() // - .fromConfiguration(dmaapConsumerConfiguration) // + when(dmaapConsumerConfigurationMock.dmaapContentType()).thenReturn("*/*"); + WebClient dmaapWebClientUndetTest = new DmaapWebClient() // + .fromConfiguration(dmaapConsumerConfigurationMock) // .build(); - verify(dmaapConsumerConfiguration, times(1)).dmaapContentType(); - assertNotNull(dmaapWebClient); + verify(dmaapConsumerConfigurationMock, times(1)).dmaapContentType(); + assertNotNull(dmaapWebClientUndetTest); + } + + @Test + public void logResponseSuccess() { + DmaapWebClient dmaapWebClientUndetTest = new DmaapWebClient(); + + when(clientResponseMock.statusCode()).thenReturn(HttpStatus.OK); + + ListAppender<ILoggingEvent> logAppender = LoggingUtils.getLogListAppender(DmaapWebClient.class, true); + Mono<ClientResponse> logResponse = dmaapWebClientUndetTest.logResponse(clientResponseMock); + + assertEquals(clientResponseMock, logResponse.block()); + + assertEquals(logAppender.list.get(0).getLevel(), Level.TRACE); + assertEquals(logAppender.list.get(0).getFormattedMessage(), "Response Status 200 OK"); + + logAppender.stop(); + } + + @Test + public void logRequestSuccess() throws URISyntaxException { + when(clientRequesteMock.url()).thenReturn(new URI("http://test")); + when(clientRequesteMock.method()).thenReturn(HttpMethod.GET); + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.add("header", "value"); + when(clientRequesteMock.headers()).thenReturn(httpHeaders); + + DmaapWebClient dmaapWebClientUndetTest = new DmaapWebClient(); + + ListAppender<ILoggingEvent> logAppender = LoggingUtils.getLogListAppender(DmaapWebClient.class, true); + Mono<ClientRequest> logRequest = dmaapWebClientUndetTest.logRequest(clientRequesteMock); + + assertEquals(clientRequesteMock, logRequest.block()); + + assertEquals(logAppender.list.get(0).getLevel(), Level.TRACE); + assertEquals("Request: GET http://test", logAppender.list.get(0).getFormattedMessage()); + assertEquals(logAppender.list.get(1).getLevel(), Level.TRACE); + assertEquals("HTTP request headers: [header:\"value\"]", logAppender.list.get(1).getFormattedMessage()); + + logAppender.stop(); } } diff --git a/datafile-app-server/src/test/java/org/onap/dcaegen2/collectors/datafile/tasks/DataRouterPublisherTest.java b/datafile-app-server/src/test/java/org/onap/dcaegen2/collectors/datafile/tasks/DataRouterPublisherTest.java index a585bf9c..ddc279c2 100644 --- a/datafile-app-server/src/test/java/org/onap/dcaegen2/collectors/datafile/tasks/DataRouterPublisherTest.java +++ b/datafile-app-server/src/test/java/org/onap/dcaegen2/collectors/datafile/tasks/DataRouterPublisherTest.java @@ -16,8 +16,8 @@ package org.onap.dcaegen2.collectors.datafile.tasks; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; @@ -29,7 +29,6 @@ import static org.mockito.Mockito.when; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.read.ListAppender; - import java.io.File; import java.net.URI; import java.nio.file.Path; @@ -39,13 +38,13 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; - import org.apache.http.Header; import org.apache.http.HttpResponse; import org.apache.http.StatusLine; import org.apache.http.client.methods.HttpPut; import org.apache.http.client.methods.HttpUriRequest; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.onap.dcaegen2.collectors.datafile.configuration.AppConfig; @@ -89,16 +88,17 @@ class DataRouterPublisherTest { private static final String PUBLISH_TOPIC = "publish"; private static final String FEED_ID = "1"; + // "https://54.45.333.2:1234/publish/1"; + private static final String PUBLISH_URL = + HTTPS_SCHEME + "://" + HOST + ":" + PORT + "/" + PUBLISH_TOPIC + "/" + FEED_ID; + private static FilePublishInformation filePublishInformation; private static DmaapProducerHttpClient httpClientMock; private static AppConfig appConfig; private static PublisherConfiguration publisherConfigurationMock = mock(PublisherConfiguration.class); private static Map<String, String> context = new HashMap<>(); private static DataRouterPublisher publisherTaskUnderTestSpy; - - // "https://54.45.333.2:1234/publish/1"; - private static final String PUBLISH_URL = - HTTPS_SCHEME + "://" + HOST + ":" + PORT + "/" + PUBLISH_TOPIC + "/" + FEED_ID; + private static final Counters counters = new Counters(); @BeforeAll public static void setUp() { @@ -121,7 +121,12 @@ class DataRouterPublisherTest { .changeIdentifier(CHANGE_IDENTIFIER) // .build(); // appConfig = mock(AppConfig.class); - publisherTaskUnderTestSpy = spy(new DataRouterPublisher(appConfig, new Counters())); + publisherTaskUnderTestSpy = spy(new DataRouterPublisher(appConfig, counters)); + } + + @BeforeEach + void setUpTest() { + counters.clear(); } @Test @@ -169,6 +174,9 @@ class DataRouterPublisherTest { // router. // This should be 10 unless the API is updated (which is the fields checked above) assertEquals(10, metaHash.size()); + + assertEquals("totalPublishedFiles should have been 1", 1, counters.getTotalPublishedFiles()); + assertEquals("noOfFailedPublishAttempts should have been 0", 0, counters.getNoOfFailedPublishAttempts()); } @Test @@ -182,6 +190,9 @@ class DataRouterPublisherTest { assertTrue("Warning missing in log", logAppender.list.toString().contains("[WARN] Publishing file " + PM_FILE_NAME + " to DR unsuccessful.")); + + assertEquals("totalPublishedFiles should have been 1", 1, counters.getTotalPublishedFiles()); + assertEquals("noOfFailedPublishAttempts should have been 1", 1, counters.getNoOfFailedPublishAttempts()); } @Test @@ -197,6 +208,9 @@ class DataRouterPublisherTest { verify(httpClientMock, times(2)).addUserCredentialsToHead(any(HttpUriRequest.class)); verify(httpClientMock, times(2)).getDmaapProducerResponseWithRedirect(any(HttpUriRequest.class), any()); verifyNoMoreInteractions(httpClientMock); + + assertEquals("totalPublishedFiles should have been 1", 1, counters.getTotalPublishedFiles()); + assertEquals("noOfFailedPublishAttempts should have been 1", 1, counters.getNoOfFailedPublishAttempts()); } @Test @@ -215,6 +229,9 @@ class DataRouterPublisherTest { verify(httpClientMock, times(2)).addUserCredentialsToHead(any(HttpUriRequest.class)); verify(httpClientMock, times(2)).getDmaapProducerResponseWithRedirect(any(HttpUriRequest.class), any()); verifyNoMoreInteractions(httpClientMock); + + assertEquals("totalPublishedFiles should have been 0", 0, counters.getTotalPublishedFiles()); + assertEquals("noOfFailedPublishAttempts should have been 2", 2, counters.getNoOfFailedPublishAttempts()); } @SafeVarargs diff --git a/datafile-app-server/src/test/java/org/onap/dcaegen2/collectors/datafile/tasks/FileCollectorTest.java b/datafile-app-server/src/test/java/org/onap/dcaegen2/collectors/datafile/tasks/FileCollectorTest.java index 1a3b2055..93f20077 100644 --- a/datafile-app-server/src/test/java/org/onap/dcaegen2/collectors/datafile/tasks/FileCollectorTest.java +++ b/datafile-app-server/src/test/java/org/onap/dcaegen2/collectors/datafile/tasks/FileCollectorTest.java @@ -16,6 +16,7 @@ package org.onap.dcaegen2.collectors.datafile.tasks; +import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; @@ -31,8 +32,8 @@ import java.nio.file.Paths; import java.time.Duration; import java.util.HashMap; import java.util.Map; - import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.onap.dcaegen2.collectors.datafile.configuration.AppConfig; import org.onap.dcaegen2.collectors.datafile.configuration.FtpesConfig; @@ -140,11 +141,8 @@ public class FileCollectorTest { .build(); } - /** - * Sets up the configuration. - */ @BeforeAll - public static void setUpConfiguration() { + static void setUpConfiguration() { when(appConfigMock.getFtpesConfiguration()).thenReturn(ftpesConfigMock); when(ftpesConfigMock.keyCert()).thenReturn(FTP_KEY_PATH); when(ftpesConfigMock.keyPassword()).thenReturn(FTP_KEY_PASSWORD); @@ -152,6 +150,11 @@ public class FileCollectorTest { when(ftpesConfigMock.trustedCaPassword()).thenReturn(TRUSTED_CA_PASSWORD); } + @BeforeEach + void setUpTest() { + counters.clear(); + } + @Test public void whenFtpesFile_returnCorrectResponse() throws Exception { FileCollector collectorUndetTest = spy(new FileCollector(appConfigMock, counters)); @@ -169,8 +172,10 @@ public class FileCollectorTest { verify(ftpsClientMock, times(1)).open(); verify(ftpsClientMock, times(1)).collectFile(REMOTE_FILE_LOCATION, LOCAL_FILE_LOCATION); verify(ftpsClientMock, times(1)).close(); - verifyNoMoreInteractions(ftpsClientMock); + + assertEquals("collectedFiles should have been 1", counters.getNoOfCollectedFiles(), 1); + assertEquals("failedFtpAttempts should have been 0", counters.getNoOfFailedFtpAttempts(), 0); } @Test @@ -198,6 +203,8 @@ public class FileCollectorTest { verify(sftpClientMock, times(2)).collectFile(REMOTE_FILE_LOCATION, LOCAL_FILE_LOCATION); verify(sftpClientMock, times(2)).close(); verifyNoMoreInteractions(sftpClientMock); + + assertEquals("collectedFiles should have been 2", counters.getNoOfCollectedFiles(), 2); } @Test @@ -214,11 +221,14 @@ public class FileCollectorTest { .verify(); verify(ftpsClientMock, times(4)).collectFile(REMOTE_FILE_LOCATION, LOCAL_FILE_LOCATION); + + assertEquals("collectedFiles should have been 0", counters.getNoOfCollectedFiles(), 0); + assertEquals("failedFtpAttempts should have been 4", counters.getNoOfFailedFtpAttempts(), 4); } @Test public void whenFtpesFileAlwaysFail_failWithoutRetry() throws Exception { - FileCollector collectorUndetTest = spy(new FileCollector(appConfigMock, new Counters())); + FileCollector collectorUndetTest = spy(new FileCollector(appConfigMock, counters)); doReturn(ftpsClientMock).when(collectorUndetTest).createFtpsClient(any()); FileData fileData = createFileData(FTPES_LOCATION, Scheme.FTPS); @@ -230,6 +240,9 @@ public class FileCollectorTest { .verify(); verify(ftpsClientMock, times(1)).collectFile(REMOTE_FILE_LOCATION, LOCAL_FILE_LOCATION); + + assertEquals("collectedFiles should have been 0", counters.getNoOfCollectedFiles(), 0); + assertEquals("failedFtpAttempts should have been 1", counters.getNoOfFailedFtpAttempts(), 1); } @Test @@ -249,5 +262,8 @@ public class FileCollectorTest { .verifyComplete(); verify(ftpsClientMock, times(2)).collectFile(REMOTE_FILE_LOCATION, LOCAL_FILE_LOCATION); + + assertEquals("collectedFiles should have been 1", counters.getNoOfCollectedFiles(), 1); + assertEquals("failedFtpAttempts should have been 1", counters.getNoOfFailedFtpAttempts(), 1); } } diff --git a/datafile-app-server/src/test/java/org/onap/dcaegen2/collectors/datafile/tasks/ScheduledTasksTest.java b/datafile-app-server/src/test/java/org/onap/dcaegen2/collectors/datafile/tasks/ScheduledTasksTest.java index edbf5880..5a8d962f 100644 --- a/datafile-app-server/src/test/java/org/onap/dcaegen2/collectors/datafile/tasks/ScheduledTasksTest.java +++ b/datafile-app-server/src/test/java/org/onap/dcaegen2/collectors/datafile/tasks/ScheduledTasksTest.java @@ -37,7 +37,6 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.read.ListAppender; - import java.nio.file.Paths; import java.time.Duration; import java.time.Instant; @@ -45,7 +44,6 @@ import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; - import org.apache.commons.lang3.StringUtils; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -67,7 +65,6 @@ import org.onap.dcaegen2.collectors.datafile.model.MessageMetaData; import org.onap.dcaegen2.collectors.datafile.utils.LoggingUtils; import org.onap.dcaegen2.services.sdk.rest.services.model.logging.MdcVariables; import org.slf4j.MDC; - import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; @@ -247,13 +244,16 @@ public class ScheduledTasksTest { testedObject.executeDatafileMainTask(); - await().untilAsserted(() -> assertEquals(0, testedObject.getCurrentNumberOfSubscriptions())); + await().untilAsserted(() -> assertEquals("currentNumberOfSubscriptions should have been 0", 0, + testedObject.getCurrentNumberOfSubscriptions())); assertFalse(StringUtils.isBlank(MDC.get(MdcVariables.REQUEST_ID))); verify(appConfig).getDmaapConsumerConfiguration(); verify(appConfig).isFeedConfigured(CHANGE_IDENTIFIER); verifyNoMoreInteractions(appConfig); + + assertEquals("totalReceivedEvents should have been 1", 1, testedObject.getCounters().getTotalReceivedEvents()); } @Test @@ -290,7 +290,8 @@ public class ScheduledTasksTest { ListAppender<ILoggingEvent> logAppender = LoggingUtils.getLogListAppender(ScheduledTasks.class); testedObject.executeDatafileMainTask(); - await().untilAsserted(() -> assertEquals(0, testedObject.getCurrentNumberOfSubscriptions())); + await().untilAsserted(() -> assertEquals("currentNumberOfSubscriptions should have been 0", 0, + testedObject.getCurrentNumberOfSubscriptions())); assertTrue("Error missing in log", logAppender.list.toString().contains( "[INFO] No feed is configured for: " + CHANGE_IDENTIFIER + ", file ignored: " + PM_FILE_NAME + "1")); @@ -339,12 +340,18 @@ public class ScheduledTasksTest { assertEquals(0, testedObject.getCurrentNumberOfTasks()); assertEquals(0, testedObject.getThreadPoolQueueSize()); + verify(consumerMock, times(1)).getMessageRouterResponse(); + verifyNoMoreInteractions(consumerMock); + verify(fileCollectorMock, times(noOfFiles)).collectFile(notNull(), anyLong(), notNull(), notNull()); + verifyNoMoreInteractions(fileCollectorMock); + verify(dataRouterMock, times(noOfFiles)).publishFile(notNull(), anyLong(), notNull()); verifyNoMoreInteractions(dataRouterMock); - verifyNoMoreInteractions(fileCollectorMock); - verifyNoMoreInteractions(consumerMock); + + assertEquals("totalReceivedEvents should have been 200", 200, + testedObject.getCounters().getTotalReceivedEvents()); } @Test @@ -375,12 +382,18 @@ public class ScheduledTasksTest { .verify(); // assertEquals(0, testedObject.getCurrentNumberOfTasks()); + verify(consumerMock, times(1)).getMessageRouterResponse(); + verifyNoMoreInteractions(consumerMock); + verify(fileCollectorMock, times(4)).collectFile(notNull(), anyLong(), notNull(), notNull()); + verifyNoMoreInteractions(fileCollectorMock); + verify(dataRouterMock, times(3)).publishFile(notNull(), anyLong(), notNull()); verifyNoMoreInteractions(dataRouterMock); - verifyNoMoreInteractions(fileCollectorMock); - verifyNoMoreInteractions(consumerMock); + + assertEquals("totalReceivedEvents should have been 2", 2, testedObject.getCounters().getTotalReceivedEvents()); + assertEquals("failedFtp should have been 1", 1, testedObject.getCounters().getNoOfFailedFtp()); } @Test @@ -412,12 +425,18 @@ public class ScheduledTasksTest { assertTrue("Error missing in log", logAppender.list.toString().contains("[ERROR] File publishing failed: ")); assertEquals(0, testedObject.getCurrentNumberOfTasks()); + verify(consumerMock, times(1)).getMessageRouterResponse(); + verifyNoMoreInteractions(consumerMock); + verify(fileCollectorMock, times(4)).collectFile(notNull(), anyLong(), notNull(), notNull()); + verifyNoMoreInteractions(fileCollectorMock); + verify(dataRouterMock, times(4)).publishFile(notNull(), anyLong(), notNull()); verifyNoMoreInteractions(dataRouterMock); - verifyNoMoreInteractions(fileCollectorMock); - verifyNoMoreInteractions(consumerMock); + + assertEquals("totalReceivedEvents should have been 2", 2, testedObject.getCounters().getTotalReceivedEvents()); + assertEquals("noOfFailedPublish should have been 1", 1, testedObject.getCounters().getNoOfFailedPublish()); } @Test @@ -444,13 +463,19 @@ public class ScheduledTasksTest { .verify(); // assertEquals(0, testedObject.getCurrentNumberOfTasks()); + verify(consumerMock, times(1)).getMessageRouterResponse(); + verifyNoMoreInteractions(consumerMock); + verify(fileCollectorMock, times(1)).collectFile(notNull(), anyLong(), notNull(), notNull()); - verify(dataRouterMock, times(1)).publishFile(notNull(), anyLong(), notNull()); - verify(publishedCheckerMock, times(1)).isFilePublished(notNull(), anyString(), notNull()); - verifyNoMoreInteractions(dataRouterMock); verifyNoMoreInteractions(fileCollectorMock); - verifyNoMoreInteractions(consumerMock); + + verify(dataRouterMock, times(1)).publishFile(notNull(), anyLong(), notNull()); verifyNoMoreInteractions(dataRouterMock); + + verify(publishedCheckerMock, times(1)).isFilePublished(notNull(), anyString(), notNull()); + verifyNoMoreInteractions(publishedCheckerMock); + + assertEquals("totalReceivedEvents should have been 1", 1, testedObject.getCounters().getTotalReceivedEvents()); } } diff --git a/datafile-app-server/src/test/java/org/onap/dcaegen2/collectors/datafile/utils/LoggingUtils.java b/datafile-app-server/src/test/java/org/onap/dcaegen2/collectors/datafile/utils/LoggingUtils.java index 604bba3e..68f3582f 100644 --- a/datafile-app-server/src/test/java/org/onap/dcaegen2/collectors/datafile/utils/LoggingUtils.java +++ b/datafile-app-server/src/test/java/org/onap/dcaegen2/collectors/datafile/utils/LoggingUtils.java @@ -20,10 +20,10 @@ package org.onap.dcaegen2.collectors.datafile.utils; +import ch.qos.logback.classic.Level; import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.read.ListAppender; - import org.slf4j.LoggerFactory; public class LoggingUtils { @@ -32,7 +32,20 @@ public class LoggingUtils { * Returns a ListAppender that contains all logging events. Call this method at the very beginning of the test */ public static ListAppender<ILoggingEvent> getLogListAppender(Class<?> logClass) { + return getLogListAppender(logClass, false); + } + + /** + * Returns a ListAppender that contains all logging events. Call this method at the very beginning of the test + * + * @param logClass class whose appender is wanted. + * @param allLevels true if all log levels should be activated. + */ + public static ListAppender<ILoggingEvent> getLogListAppender(Class<?> logClass, boolean allLevels) { Logger logger = (Logger) LoggerFactory.getLogger(logClass); + if (allLevels) { + logger.setLevel(Level.ALL); + } ListAppender<ILoggingEvent> listAppender = new ListAppender<>(); listAppender.start(); logger.addAppender(listAppender); |