From: ambrishest Date: Mon, 18 Sep 2023 08:33:24 +0000 (+0100) Subject: Improve Test coverage of pmproducer X-Git-Tag: 1.1.0~10^2 X-Git-Url: https://gerrit.o-ran-sc.org/r/gitweb?a=commitdiff_plain;h=refs%2Fchanges%2F95%2F11795%2F4;p=nonrtric%2Fplt%2Franpm.git Improve Test coverage of pmproducer Issue-ID: NONRTRIC-877 Change-Id: I8e997a973d92a7712339bc63d909d4076b6e48d2 Signed-off-by: ambrishest --- diff --git a/pmproducer/pom.xml b/pmproducer/pom.xml index 4a4d9fd..5f55f0b 100644 --- a/pmproducer/pom.xml +++ b/pmproducer/pom.xml @@ -61,6 +61,7 @@ 2.0.2 1.6.14 true + @@ -162,6 +163,11 @@ mockito-core test + + org.mockito + mockito-inline + test + io.projectreactor.kafka reactor-kafka diff --git a/pmproducer/src/main/java/org/oran/pmproducer/Application.java b/pmproducer/src/main/java/org/oran/pmproducer/Application.java index 0ffb73f..e39848a 100644 --- a/pmproducer/src/main/java/org/oran/pmproducer/Application.java +++ b/pmproducer/src/main/java/org/oran/pmproducer/Application.java @@ -23,7 +23,6 @@ package org.oran.pmproducer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.boot.ApplicationArguments; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.context.properties.EnableConfigurationProperties; @@ -51,20 +50,4 @@ public class Application { }); } - private static void restartApplication() { - if (applicationContext == null) { - logger.info("Cannot restart in unittest"); - return; - } - ApplicationArguments args = applicationContext.getBean(ApplicationArguments.class); - - Thread thread = new Thread(() -> { - applicationContext.close(); - applicationContext = SpringApplication.run(Application.class, args.getSourceArgs()); - }); - - thread.setDaemon(false); - thread.start(); - } - } diff --git a/pmproducer/src/main/java/org/oran/pmproducer/clients/AsyncRestClient.java b/pmproducer/src/main/java/org/oran/pmproducer/clients/AsyncRestClient.java index 1fc0620..4031559 100644 --- a/pmproducer/src/main/java/org/oran/pmproducer/clients/AsyncRestClient.java +++ b/pmproducer/src/main/java/org/oran/pmproducer/clients/AsyncRestClient.java @@ -145,8 +145,7 @@ public class AsyncRestClient { } private void onError(Throwable t) { - if (t instanceof WebClientResponseException) { - WebClientResponseException e = (WebClientResponseException) t; + if (t instanceof WebClientResponseException e) { logger.debug("Response error: {}", e.getResponseBodyAsString()); } } diff --git a/pmproducer/src/main/java/org/oran/pmproducer/clients/AsyncRestClientFactory.java b/pmproducer/src/main/java/org/oran/pmproducer/clients/AsyncRestClientFactory.java index 7d50fa0..81d3335 100644 --- a/pmproducer/src/main/java/org/oran/pmproducer/clients/AsyncRestClientFactory.java +++ b/pmproducer/src/main/java/org/oran/pmproducer/clients/AsyncRestClientFactory.java @@ -117,6 +117,7 @@ public class AsyncRestClientFactory { } } + @SuppressWarnings("java:S6204") private SslContext createSslContextRejectingUntrustedPeers(String trustStorePath, String trustStorePass, KeyManagerFactory keyManager) throws NoSuchAlgorithmException, CertificateException, IOException, KeyStoreException { diff --git a/pmproducer/src/main/java/org/oran/pmproducer/controllers/ErrorResponse.java b/pmproducer/src/main/java/org/oran/pmproducer/controllers/ErrorResponse.java index 8411181..7c9bd2e 100644 --- a/pmproducer/src/main/java/org/oran/pmproducer/controllers/ErrorResponse.java +++ b/pmproducer/src/main/java/org/oran/pmproducer/controllers/ErrorResponse.java @@ -108,11 +108,8 @@ public class ErrorResponse { public static ResponseEntity create(Throwable e, HttpStatus code) { if (e instanceof RuntimeException) { code = HttpStatus.INTERNAL_SERVER_ERROR; - } else if (e instanceof ServiceException) { - ServiceException se = (ServiceException) e; - if (se.getHttpStatus() != null) { - code = se.getHttpStatus(); - } + } else if (e instanceof ServiceException se && se.getHttpStatus() != null) { + code = se.getHttpStatus(); } return create(e.toString(), code); } diff --git a/pmproducer/src/main/java/org/oran/pmproducer/datastore/FileStore.java b/pmproducer/src/main/java/org/oran/pmproducer/datastore/FileStore.java index d2d2ef1..d4fef8a 100644 --- a/pmproducer/src/main/java/org/oran/pmproducer/datastore/FileStore.java +++ b/pmproducer/src/main/java/org/oran/pmproducer/datastore/FileStore.java @@ -139,7 +139,7 @@ class FileStore implements DataStore { return Mono.just("OK"); } - private Path path(String name) { + public Path path(String name) { return Path.of(applicationConfig.getPmFilesPath(), name); } diff --git a/pmproducer/src/main/java/org/oran/pmproducer/datastore/S3ObjectStore.java b/pmproducer/src/main/java/org/oran/pmproducer/datastore/S3ObjectStore.java index be30969..fd56d62 100644 --- a/pmproducer/src/main/java/org/oran/pmproducer/datastore/S3ObjectStore.java +++ b/pmproducer/src/main/java/org/oran/pmproducer/datastore/S3ObjectStore.java @@ -67,6 +67,12 @@ class S3ObjectStore implements DataStore { getS3AsynchClient(applicationConfig); } + @SuppressWarnings({"java:S3010", "java:S2209"}) + public S3ObjectStore(ApplicationConfig applicationConfig, S3AsyncClient s3AsynchClient) { + this.applicationConfig = applicationConfig; + this.s3AsynchClient = s3AsynchClient; + } + private static synchronized S3AsyncClient getS3AsynchClient(ApplicationConfig applicationConfig) { if (applicationConfig.isS3Enabled() && s3AsynchClient == null) { s3AsynchClient = getS3AsyncClientBuilder(applicationConfig).build(); diff --git a/pmproducer/src/main/java/org/oran/pmproducer/filter/FilterFactory.java b/pmproducer/src/main/java/org/oran/pmproducer/filter/FilterFactory.java index 85133de..8af1e7a 100644 --- a/pmproducer/src/main/java/org/oran/pmproducer/filter/FilterFactory.java +++ b/pmproducer/src/main/java/org/oran/pmproducer/filter/FilterFactory.java @@ -21,15 +21,9 @@ package org.oran.pmproducer.filter; import com.google.gson.GsonBuilder; - -import java.lang.invoke.MethodHandles; import java.util.Collection; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - public class FilterFactory { - private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); private static com.google.gson.Gson gson = new GsonBuilder().disableHtmlEscaping().create(); private FilterFactory() {} diff --git a/pmproducer/src/main/java/org/oran/pmproducer/oauth2/OAuthBearerTokenJwt.java b/pmproducer/src/main/java/org/oran/pmproducer/oauth2/OAuthBearerTokenJwt.java index 1160657..d00903b 100644 --- a/pmproducer/src/main/java/org/oran/pmproducer/oauth2/OAuthBearerTokenJwt.java +++ b/pmproducer/src/main/java/org/oran/pmproducer/oauth2/OAuthBearerTokenJwt.java @@ -1,38 +1,33 @@ -// ============LICENSE_START=============================================== -// Copyright (C) 2023 Nordix Foundation. All rights reserved. -// ======================================================================== -// 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. -// ============LICENSE_END================================================= -// +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2023 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. + * ========================LICENSE_END=================================== + */ package org.oran.pmproducer.oauth2; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonMappingException; - import java.util.Base64; import java.util.HashSet; import java.util.Set; - import lombok.ToString; - import org.apache.kafka.common.security.oauthbearer.OAuthBearerToken; import org.oran.pmproducer.exceptions.ServiceException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; public class OAuthBearerTokenJwt implements OAuthBearerToken { - private static final Logger logger = LoggerFactory.getLogger(OAuthBearerTokenJwt.class); private static final com.google.gson.Gson gson = new com.google.gson.GsonBuilder().disableHtmlEscaping().create(); private final String jwtTokenRaw; @@ -47,7 +42,7 @@ public class OAuthBearerTokenJwt implements OAuthBearerToken { } public static OAuthBearerTokenJwt create(String tokenRaw) - throws ServiceException, JsonMappingException, JsonProcessingException { + throws ServiceException { String[] chunks = tokenRaw.split("\\."); Base64.Decoder decoder = Base64.getUrlDecoder(); if (chunks.length < 2) { diff --git a/pmproducer/src/main/java/org/oran/pmproducer/oauth2/OAuthKafkaAuthenticateLoginCallbackHandler.java b/pmproducer/src/main/java/org/oran/pmproducer/oauth2/OAuthKafkaAuthenticateLoginCallbackHandler.java index b209da3..e2aa85a 100644 --- a/pmproducer/src/main/java/org/oran/pmproducer/oauth2/OAuthKafkaAuthenticateLoginCallbackHandler.java +++ b/pmproducer/src/main/java/org/oran/pmproducer/oauth2/OAuthKafkaAuthenticateLoginCallbackHandler.java @@ -1,19 +1,21 @@ -// ============LICENSE_START=============================================== -// Copyright (C) 2023 Nordix Foundation. All rights reserved. -// ======================================================================== -// 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. -// ============LICENSE_END================================================= -// +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * Copyright (C) 2023 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. + * ========================LICENSE_END=================================== + */ package org.oran.pmproducer.oauth2; @@ -51,7 +53,10 @@ public class OAuthKafkaAuthenticateLoginCallbackHandler implements AuthenticateC } @Override - public void close() {} + public void close() { + /*This method intentionally left empty. + Close functionality will be implemented later.*/ + } @Override public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { @@ -59,11 +64,11 @@ public class OAuthKafkaAuthenticateLoginCallbackHandler implements AuthenticateC if (!this.isConfigured) throw new IllegalStateException("Callback handler not configured"); for (Callback callback : callbacks) { - logger.debug("callback " + callback.toString()); - if (callback instanceof OAuthBearerTokenCallback) { - handleCallback((OAuthBearerTokenCallback) callback); - } else if (callback instanceof SaslExtensionsCallback) { - handleCallback((SaslExtensionsCallback) callback); + logger.debug("callback {}", callback); + if (callback instanceof OAuthBearerTokenCallback oAuthBearerTokenCallback) { + handleCallback(oAuthBearerTokenCallback); + } else if (callback instanceof SaslExtensionsCallback saslExtensionsCallback) { + handleCallback(saslExtensionsCallback); } else { logger.error("Unsupported callback: {}", callback); throw new UnsupportedCallbackException(callback); @@ -90,4 +95,8 @@ public class OAuthKafkaAuthenticateLoginCallbackHandler implements AuthenticateC } } + public boolean isConfigured() { + return isConfigured; + } + } diff --git a/pmproducer/src/main/java/org/oran/pmproducer/oauth2/SecurityContext.java b/pmproducer/src/main/java/org/oran/pmproducer/oauth2/SecurityContext.java index 936d4a9..5633b37 100644 --- a/pmproducer/src/main/java/org/oran/pmproducer/oauth2/SecurityContext.java +++ b/pmproducer/src/main/java/org/oran/pmproducer/oauth2/SecurityContext.java @@ -49,10 +49,11 @@ public class SecurityContext { private static SecurityContext instance; @Setter + @Getter private Path authTokenFilePath; public SecurityContext(@Value("${app.auth-token-file}") String authTokenFilename) { - instance = this; + instance = this; //NOSONAR if (!authTokenFilename.isEmpty()) { this.authTokenFilePath = Path.of(authTokenFilename); } diff --git a/pmproducer/src/main/java/org/oran/pmproducer/r1/ProducerInfoTypeInfo.java b/pmproducer/src/main/java/org/oran/pmproducer/r1/ProducerInfoTypeInfo.java index 500081e..5e8a47a 100644 --- a/pmproducer/src/main/java/org/oran/pmproducer/r1/ProducerInfoTypeInfo.java +++ b/pmproducer/src/main/java/org/oran/pmproducer/r1/ProducerInfoTypeInfo.java @@ -28,11 +28,13 @@ import io.swagger.v3.oas.annotations.media.Schema; @Schema(name = "producer_info_type_info", description = "Information for an Information Type") public class ProducerInfoTypeInfo { + @SuppressWarnings("java:S1874") @Schema(name = "info_job_data_schema", description = "Json schema for the job data", required = true) @SerializedName("info_job_data_schema") @JsonProperty(value = "info_job_data_schema", required = true) public Object jobDataSchema; + @SuppressWarnings("java:S1874") @Schema(name = "info_type_information", description = "Type specific information for the information type", required = true) @SerializedName("info_type_information") diff --git a/pmproducer/src/main/java/org/oran/pmproducer/r1/ProducerJobInfo.java b/pmproducer/src/main/java/org/oran/pmproducer/r1/ProducerJobInfo.java index f421e1b..eb9b956 100644 --- a/pmproducer/src/main/java/org/oran/pmproducer/r1/ProducerJobInfo.java +++ b/pmproducer/src/main/java/org/oran/pmproducer/r1/ProducerJobInfo.java @@ -29,6 +29,7 @@ import io.swagger.v3.oas.annotations.media.Schema; description = "The body of the Information Producer callbacks for Information Job creation and deletion") public class ProducerJobInfo { + @SuppressWarnings("java:S1874") @Schema(name = "info_job_identity", description = "Identity of the Information Job", required = true) @SerializedName("info_job_identity") @JsonProperty("info_job_identity") diff --git a/pmproducer/src/main/java/org/oran/pmproducer/r1/ProducerRegistrationInfo.java b/pmproducer/src/main/java/org/oran/pmproducer/r1/ProducerRegistrationInfo.java index 502abb6..e3fbdcb 100644 --- a/pmproducer/src/main/java/org/oran/pmproducer/r1/ProducerRegistrationInfo.java +++ b/pmproducer/src/main/java/org/oran/pmproducer/r1/ProducerRegistrationInfo.java @@ -33,16 +33,19 @@ import lombok.Builder; @Schema(name = "producer_registration_info", description = "Information for an Information Producer") public class ProducerRegistrationInfo { + @SuppressWarnings("java:S1874") @Schema(name = "supported_info_types", description = "Supported Information Type IDs", required = true) @SerializedName("supported_info_types") @JsonProperty(value = "supported_info_types", required = true) public Collection supportedTypeIds; + @SuppressWarnings("java:S1874") @Schema(name = "info_job_callback_url", description = "callback for Information Job", required = true) @SerializedName("info_job_callback_url") @JsonProperty(value = "info_job_callback_url", required = true) public String jobCallbackUrl; + @SuppressWarnings("java:S1874") @Schema(name = "info_producer_supervision_callback_url", description = "callback for producer supervision", required = true) @SerializedName("info_producer_supervision_callback_url") diff --git a/pmproducer/src/main/java/org/oran/pmproducer/repository/Jobs.java b/pmproducer/src/main/java/org/oran/pmproducer/repository/Jobs.java index 2b48b62..6a5e261 100644 --- a/pmproducer/src/main/java/org/oran/pmproducer/repository/Jobs.java +++ b/pmproducer/src/main/java/org/oran/pmproducer/repository/Jobs.java @@ -119,6 +119,7 @@ public class Jobs { private final List observers = new ArrayList<>(); private final ApplicationConfig appConfig; + @SuppressWarnings("java:S1172") public Jobs(@Autowired ApplicationConfig applicationConfig, @Autowired SecurityContext securityContext, @Autowired ApplicationConfig appConfig) { this.appConfig = appConfig; @@ -137,8 +138,7 @@ public class Jobs { } public void addJob(String id, InfoType type, String owner, String lastUpdated, Parameters parameters) - throws ServiceException { - + { Job job = new Job(id, type, owner, lastUpdated, parameters, this.appConfig); this.put(job); } diff --git a/pmproducer/src/main/java/org/oran/pmproducer/tasks/JobDataDistributor.java b/pmproducer/src/main/java/org/oran/pmproducer/tasks/JobDataDistributor.java index b224945..130cbd3 100644 --- a/pmproducer/src/main/java/org/oran/pmproducer/tasks/JobDataDistributor.java +++ b/pmproducer/src/main/java/org/oran/pmproducer/tasks/JobDataDistributor.java @@ -79,6 +79,7 @@ public class JobDataDistributor { this.consumerFaultCounter = 0; } + @SuppressWarnings("java:S1172") public void handleException(Throwable t) { ++this.consumerFaultCounter; } diff --git a/pmproducer/src/main/java/org/oran/pmproducer/tasks/ProducerRegstrationTask.java b/pmproducer/src/main/java/org/oran/pmproducer/tasks/ProducerRegstrationTask.java index d4b29be..c836c81 100644 --- a/pmproducer/src/main/java/org/oran/pmproducer/tasks/ProducerRegstrationTask.java +++ b/pmproducer/src/main/java/org/oran/pmproducer/tasks/ProducerRegstrationTask.java @@ -157,6 +157,7 @@ public class ProducerRegstrationTask { } } + @SuppressWarnings("java:S1172") private Object jsonSchemaObject(InfoType type) throws IOException, ServiceException { final String schemaFile = "/typeSchemaPmData.json"; return jsonObject(readSchemaFile(schemaFile)); @@ -181,12 +182,6 @@ public class ProducerRegstrationTask { } } - private boolean isEqual(ProducerRegistrationInfo a, ProducerRegistrationInfo b) { - return a.jobCallbackUrl.equals(b.jobCallbackUrl) // - && a.producerSupervisionCallbackUrl.equals(b.producerSupervisionCallbackUrl) // - && a.supportedTypeIds.size() == b.supportedTypeIds.size(); - } - private ProducerRegistrationInfo producerRegistrationInfo() { return ProducerRegistrationInfo.builder() // .jobCallbackUrl(baseUrl() + ProducerCallbacksController.JOB_URL) // diff --git a/pmproducer/src/main/java/org/oran/pmproducer/tasks/TopicListeners.java b/pmproducer/src/main/java/org/oran/pmproducer/tasks/TopicListeners.java index 7b41a6f..581a057 100644 --- a/pmproducer/src/main/java/org/oran/pmproducer/tasks/TopicListeners.java +++ b/pmproducer/src/main/java/org/oran/pmproducer/tasks/TopicListeners.java @@ -43,6 +43,7 @@ import org.springframework.stereotype.Component; public class TopicListeners { private static final Logger logger = LoggerFactory.getLogger(TopicListeners.class); + @SuppressWarnings("java:S1700") @Getter private final Map topicListeners = new HashMap<>(); // Key is typeId diff --git a/pmproducer/src/test/java/org/oran/pmproducer/IntegrationWithKafka.java b/pmproducer/src/test/java/org/oran/pmproducer/IntegrationWithKafka.java index 04b501b..27d3170 100644 --- a/pmproducer/src/test/java/org/oran/pmproducer/IntegrationWithKafka.java +++ b/pmproducer/src/test/java/org/oran/pmproducer/IntegrationWithKafka.java @@ -459,8 +459,9 @@ class IntegrationWithKafka { } String msgString = kafkaReceiver.receivedKafkaOutput.valueAsString(); - assertThat(msgString).contains("pmCounterNumber0"); - assertThat(msgString).doesNotContain("pmCounterNumber1"); + assertThat(msgString) + .contains("pmCounterNumber0") + .doesNotContain("pmCounterNumber1"); assertThat(kafkaReceiver.receivedKafkaOutput.getTypeIdFromHeaders()).isEqualTo(PM_TYPE_ID); assertThat(kafkaReceiver.receivedKafkaOutput.getSourceNameFromHeaders()).isEqualTo("HTTPST2-0"); // This is from // the file diff --git a/pmproducer/src/test/java/org/oran/pmproducer/datastore/DataStoreTest.java b/pmproducer/src/test/java/org/oran/pmproducer/datastore/DataStoreTest.java new file mode 100644 index 0000000..8c787cb --- /dev/null +++ b/pmproducer/src/test/java/org/oran/pmproducer/datastore/DataStoreTest.java @@ -0,0 +1,54 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2023 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. + * ========================LICENSE_END=================================== + */ + +package org.oran.pmproducer.datastore; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.when; + +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.oran.pmproducer.configuration.ApplicationConfig; + +@ExtendWith(MockitoExtension.class) +class DataStoreTest { + + @Mock + private ApplicationConfig mockAppConfig; + + @Test + void testCreateWithS3Enabled() { + when(mockAppConfig.isS3Enabled()).thenReturn(true); + when(mockAppConfig.getS3EndpointOverride()).thenReturn("https://dummy-s3-bucket.s3.amazonaws.com"); + when(mockAppConfig.getS3AccessKeyId()).thenReturn("test-access-key-id"); + when(mockAppConfig.getS3SecretAccessKey()).thenReturn("test-access-key-secret"); + DataStore dataStore = DataStore.create(mockAppConfig); + assertTrue(dataStore instanceof S3ObjectStore); + } + + @Test + void testCreateWithS3Disabled() { + when(mockAppConfig.isS3Enabled()).thenReturn(false); + DataStore dataStore = DataStore.create(mockAppConfig); + assertTrue(dataStore instanceof FileStore); + } +} diff --git a/pmproducer/src/test/java/org/oran/pmproducer/datastore/FileStoreTest.java b/pmproducer/src/test/java/org/oran/pmproducer/datastore/FileStoreTest.java new file mode 100644 index 0000000..8196140 --- /dev/null +++ b/pmproducer/src/test/java/org/oran/pmproducer/datastore/FileStoreTest.java @@ -0,0 +1,247 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2019-2023 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.oran.pmproducer.datastore; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.atLeast; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.nio.file.FileVisitor; +import java.nio.file.Files; +import java.nio.file.LinkOption; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +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.MockedStatic; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.mockito.junit.jupiter.MockitoExtension; +import org.oran.pmproducer.configuration.ApplicationConfig; +import org.springframework.test.context.ContextConfiguration; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +@ContextConfiguration(classes = {FileStore.class}) +@ExtendWith(MockitoExtension.class) +class FileStoreTest { + + @Mock + private ApplicationConfig appConfig; + + private FileStore fileStore; + + @Mock + private Path mockPath; + + @BeforeEach + void setup() { + MockitoAnnotations.initMocks(this); + fileStore = new FileStore(appConfig); + + when(appConfig.getPmFilesPath()).thenReturn("/path/to/pm/files"); + } + + @Test + void testListObjects() { + when(appConfig.getPmFilesPath()).thenReturn("PM Files Path"); + fileStore.listObjects(DataStore.Bucket.FILES, "Prefix"); + verify(appConfig).getPmFilesPath(); + } + @Test + void testListObjects3() { + when(appConfig.getPmFilesPath()).thenReturn("PM Files Path"); + fileStore.listObjects(DataStore.Bucket.LOCKS, "Prefix"); + verify(appConfig).getPmFilesPath(); + } + + @Test + void testListObjects_WithExistingFiles() { + List fileList = new ArrayList<>(); + fileList.add(Path.of("/path/to/pm/files/file1.txt")); + fileList.add(Path.of("/path/to/pm/files/file2.txt")); + + when(appConfig.getPmFilesPath()).thenReturn("/path/to/pm/files"); + + // Mock Files.walk() to return the prepared stream + try (MockedStatic filesMockedStatic = mockStatic(Files.class)) { + filesMockedStatic.when(() -> Files.walk(any(), anyInt())) + .thenReturn(fileList.stream()); + + StepVerifier.create(fileStore.listObjects(DataStore.Bucket.FILES, "")) + .expectNext("file1.txt") + .expectNext("file2.txt") + .expectComplete(); + } + } + @Test + void testReadObject() { + when(appConfig.getPmFilesPath()).thenReturn("PM Files Path"); + fileStore.readObject(DataStore.Bucket.FILES, "foo.txt"); + verify(appConfig).getPmFilesPath(); + } + @Test + void testReadObject2() { + when(appConfig.getPmFilesPath()).thenReturn("PM Files Path"); + fileStore.readObject(DataStore.Bucket.LOCKS, "foo.txt"); + verify(appConfig).getPmFilesPath(); + } + + @Test + void testReadObject_WithExistingFile() { + byte[] content = "Hello, world!".getBytes(); + Path filePath = Path.of("/path/to/pm/files/test.txt"); + + try (MockedStatic filesMockedStatic = mockStatic(Files.class)) { + filesMockedStatic.when(() -> Files.readAllBytes(eq(filePath))) + .thenReturn(content); + + StepVerifier.create(fileStore.readObject(DataStore.Bucket.FILES, "test.txt")) + .expectNext(content) + .verifyComplete(); + } + } + @Test + void testCreateLock() { + when(appConfig.getPmFilesPath()).thenReturn("PM Files Path"); + fileStore.createLock("Name"); + verify(appConfig, atLeast(1)).getPmFilesPath(); + } + @Test + void testCreateLock3() { + when(appConfig.getPmFilesPath()).thenReturn(""); + fileStore.createLock("/"); + verify(appConfig, atLeast(1)).getPmFilesPath(); + } + @Test + void testDeleteLock() { + when(appConfig.getPmFilesPath()).thenReturn("PM Files Path"); + fileStore.deleteLock("Name"); + verify(appConfig).getPmFilesPath(); + } + @Test + void testDeleteLock2() { + when(appConfig.getPmFilesPath()).thenReturn(""); + fileStore.deleteLock("//"); + verify(appConfig).getPmFilesPath(); + } + @Test + void testDeleteObject() { + when(appConfig.getPmFilesPath()).thenReturn("PM Files Path"); + fileStore.deleteObject(DataStore.Bucket.FILES, "Name"); + verify(appConfig).getPmFilesPath(); + } + @Test + void testDeleteObject2() { + when(appConfig.getPmFilesPath()).thenReturn("PM Files Path"); + fileStore.deleteObject(DataStore.Bucket.LOCKS, "Name"); + verify(appConfig).getPmFilesPath(); + } + + @Test + void testPath() { + when(appConfig.getPmFilesPath()).thenReturn("PM Files Path"); + fileStore.path("Name"); + verify(appConfig).getPmFilesPath(); + } + + @Test + void testDeleteBucket() { + when(appConfig.getPmFilesPath()).thenReturn("PM Files Path"); + fileStore.deleteBucket(DataStore.Bucket.FILES); + verify(appConfig).getPmFilesPath(); + } + @Test + void testDeleteBucket2() throws IOException { + try (MockedStatic mockFiles = mockStatic(Files.class)) { + mockFiles.when(() -> Files.walkFileTree(Mockito.any(), Mockito.>any())) + .thenReturn(Paths.get(System.getProperty("java.io.tmpdir"), "test.txt")); + mockFiles.when(() -> Files.exists(Mockito.any(), (LinkOption[]) any())).thenReturn(true); + when(appConfig.getPmFilesPath()).thenReturn(""); + fileStore.deleteBucket(DataStore.Bucket.LOCKS); + mockFiles.verify(() -> Files.exists(Mockito.any(), (LinkOption[]) any())); + mockFiles.verify(() -> Files.walkFileTree(Mockito.any(), Mockito.>any())); + verify(appConfig).getPmFilesPath(); + } + } + @Test + void testDeleteBucket3() throws IOException { + try (MockedStatic mockFiles = mockStatic(Files.class)) { + mockFiles.when(() -> Files.walkFileTree(Mockito.any(), Mockito.>any())) + .thenThrow(new IOException("OK")); + mockFiles.when(() -> Files.exists(Mockito.any(), (LinkOption[]) any())).thenReturn(true); + when(appConfig.getPmFilesPath()).thenReturn(""); + fileStore.deleteBucket(DataStore.Bucket.LOCKS); + mockFiles.verify(() -> Files.exists(Mockito.any(), (LinkOption[]) any())); + mockFiles.verify(() -> Files.walkFileTree(Mockito.any(), Mockito.>any())); + verify(appConfig, atLeast(1)).getPmFilesPath(); + } + } + + @Test + void testCreateLock_Success() throws IOException { + Path lockPath = Path.of("/path/to/pm/files/locks/lock.txt"); + + when(appConfig.getPmFilesPath()).thenReturn("/path/to/pm/files"); + + try (MockedStatic filesMockedStatic = mockStatic(Files.class)) { + filesMockedStatic.when(() -> Files.createDirectories(lockPath.getParent())) + .thenReturn(lockPath.getParent()); + + try (MockedStatic pathMockedStatic = mockStatic(Path.class)) { + filesMockedStatic.when(() -> Files.createFile(any(Path.class))).thenReturn(lockPath); + + String name = "test.txt"; + String[] pathComponents = {"pmFiles", name}; + + when(fileStore.path(Arrays.toString(pathComponents))).thenReturn(mockPath); + Path path = fileStore.path(Arrays.toString(pathComponents)); + assertEquals(mockPath, path); + } + } + } + + @Test + void testCopyFileTo_Failure() { + // Define dummy values for testing + Path from = Paths.get("non-existent-file.txt"); + String to = "destination-folder"; + + // Use StepVerifier to test the method + Mono resultMono = fileStore.copyFileTo(from, to); + + StepVerifier.create(resultMono) + .expectError(IOException.class) + .verify(); + } +} + diff --git a/pmproducer/src/test/java/org/oran/pmproducer/datastore/S3ObjectStoreTest.java b/pmproducer/src/test/java/org/oran/pmproducer/datastore/S3ObjectStoreTest.java new file mode 100644 index 0000000..2e5f7d8 --- /dev/null +++ b/pmproducer/src/test/java/org/oran/pmproducer/datastore/S3ObjectStoreTest.java @@ -0,0 +1,321 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2023 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. + * ========================LICENSE_END=================================== + */ + +package org.oran.pmproducer.datastore; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.atLeast; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import org.junit.jupiter.api.Assertions; +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.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; +import org.oran.pmproducer.configuration.ApplicationConfig; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; +import software.amazon.awssdk.core.ResponseBytes; +import software.amazon.awssdk.core.async.AsyncRequestBody; +import software.amazon.awssdk.core.async.AsyncResponseTransformer; +import software.amazon.awssdk.services.s3.S3AsyncClient; +import software.amazon.awssdk.services.s3.model.CreateBucketRequest; +import software.amazon.awssdk.services.s3.model.CreateBucketResponse; +import software.amazon.awssdk.services.s3.model.DeleteBucketRequest; +import software.amazon.awssdk.services.s3.model.DeleteBucketResponse; +import software.amazon.awssdk.services.s3.model.DeleteObjectRequest; +import software.amazon.awssdk.services.s3.model.DeleteObjectResponse; +import software.amazon.awssdk.services.s3.model.DeleteObjectsRequest; +import software.amazon.awssdk.services.s3.model.DeleteObjectsResponse; +import software.amazon.awssdk.services.s3.model.GetObjectRequest; +import software.amazon.awssdk.services.s3.model.GetObjectResponse; +import software.amazon.awssdk.services.s3.model.HeadObjectRequest; +import software.amazon.awssdk.services.s3.model.HeadObjectResponse; +import software.amazon.awssdk.services.s3.model.ListObjectsRequest; +import software.amazon.awssdk.services.s3.model.ListObjectsResponse; +import software.amazon.awssdk.services.s3.model.PutObjectRequest; +import software.amazon.awssdk.services.s3.model.PutObjectResponse; +import software.amazon.awssdk.services.s3.model.S3Object; + + +@ExtendWith(MockitoExtension.class) +class S3ObjectStoreTest { + + @Mock + private ApplicationConfig appConfig; + + @Mock + private S3AsyncClient s3AsynchClient; + + private S3ObjectStore s3ObjectStore; + + @BeforeEach + void setup() { + Mockito.lenient().when(appConfig.getS3EndpointOverride()).thenReturn("https://dummy-s3-bucket.s3.amazonaws.com"); + Mockito.lenient().when(appConfig.getS3AccessKeyId()).thenReturn("test-access-key-id"); + Mockito.lenient().when(appConfig.getS3SecretAccessKey()).thenReturn("test-access-key-secret"); + + Mockito.lenient().when(appConfig.getS3Bucket()).thenReturn("test-bucket"); + Mockito.lenient().when(appConfig.getS3LocksBucket()).thenReturn("test-lock-bucket"); + Mockito.lenient().when(appConfig.isS3Enabled()).thenReturn(true); + + s3ObjectStore = new S3ObjectStore(appConfig, s3AsynchClient); + } + + @Test + void createS3Bucket() { + CreateBucketRequest request = CreateBucketRequest.builder() + .bucket("test-bucket") + .build(); + + when(s3AsynchClient.createBucket(any(CreateBucketRequest.class))) + .thenReturn(CompletableFuture.completedFuture(CreateBucketResponse.builder().build())); + + Mono result = s3ObjectStore.create(DataStore.Bucket.FILES); + + verify(s3AsynchClient, atLeast(1)).createBucket(any(CreateBucketRequest.class)); + + StepVerifier.create(result).expectNext("test-bucket").verifyComplete(); + + assertThat(result.block()).isEqualTo("test-bucket"); + } + + @Test + void listObjects() { + String prefix = "prefix/"; + + ListObjectsResponse response1 = ListObjectsResponse.builder() + .contents(createS3Object("object1")) + .isTruncated(true) + .nextMarker("marker1") + .build(); + + ListObjectsResponse response2 = ListObjectsResponse.builder() + .contents(createS3Object("object2")) + .isTruncated(false) + .build(); + + when(s3AsynchClient.listObjects(any(ListObjectsRequest.class))) + .thenReturn(CompletableFuture.completedFuture(response1), + CompletableFuture.completedFuture(response2)); + + Flux result = s3ObjectStore.listObjects(DataStore.Bucket.FILES, prefix); + + verify(s3AsynchClient, atLeast(1)).listObjects(any(ListObjectsRequest.class)); + + StepVerifier.create(result) + .expectNext("object1") + .expectNext("object2") + .verifyComplete(); + + // Collect the results into a list + List resultList = result.collectList().block(); + + assertEquals(Arrays.asList("object1", "object2"), resultList); + } + + @Test + void testCreateLockWithExistingHead() { + HeadObjectResponse headObjectResponse = HeadObjectResponse.builder().build(); + + when(s3AsynchClient.headObject(any(HeadObjectRequest.class))) + .thenReturn(CompletableFuture.completedFuture(headObjectResponse)); + + Mono result = s3ObjectStore.createLock("lockName"); + + StepVerifier.create(result) + .expectNext(false) + .verifyComplete(); + + assertThat(result.block()).isFalse(); + } + + @Test + void testCreateLockWithoutExistingHead() { + HeadObjectResponse headObjectResponse = null; + Mockito.doReturn(CompletableFuture.completedFuture(headObjectResponse)) + .when(s3AsynchClient) + .headObject(any(HeadObjectRequest.class)); + + Mono result = s3ObjectStore.createLock("lockName"); + + StepVerifier.create(result) + .expectComplete() + .verify(); + + Boolean resultVal = result.block(); + + assertThat(resultVal).isNull(); + } + + + @Test + void deleteLock() { + when(s3AsynchClient.deleteObject(any(DeleteObjectRequest.class))) + .thenReturn(CompletableFuture.completedFuture(DeleteObjectResponse.builder().build())); + + Mono result = s3ObjectStore.deleteLock("lock-name"); + + StepVerifier.create(result) + .expectNext(true) + .verifyComplete(); + + assertThat(result.block()).isTrue(); + } + + @Test + void testDeleteObject() { + when(s3AsynchClient.deleteObject(any(DeleteObjectRequest.class))) + .thenReturn(CompletableFuture.completedFuture(DeleteObjectResponse.builder().build())); + + Mono result = s3ObjectStore.deleteObject(DataStore.Bucket.LOCKS, "objectName"); + + StepVerifier.create(result) + .expectNext(true) + .verifyComplete(); + + assertThat(result.block()).isTrue(); + } + + @Test + void testDeleteBucket_Success() { + DeleteBucketRequest request = DeleteBucketRequest.builder() // + .bucket("test-bucket") + .build(); + + Mockito.lenient().when(s3AsynchClient.deleteBucket(any(DeleteBucketRequest.class))) + .thenReturn(CompletableFuture.completedFuture(DeleteBucketResponse.builder().build())); + + DeleteObjectsRequest objectRequest = DeleteObjectsRequest.builder() // + .bucket("test-bucket") + .build(); + + Mockito.lenient().when(s3AsynchClient.deleteObjects(any(DeleteObjectsRequest.class))) + .thenReturn(CompletableFuture.completedFuture(DeleteObjectsResponse.builder().build())); + + String prefix = "prefix/"; + + ListObjectsResponse response1 = ListObjectsResponse.builder() + .contents(createS3Object("object1")) + .isTruncated(true) + .nextMarker("marker1") + .build(); + + ListObjectsResponse response2 = ListObjectsResponse.builder() + .contents(createS3Object("object2")) + .isTruncated(false) + .build(); + + when(s3AsynchClient.listObjects(any(ListObjectsRequest.class))) + .thenReturn(CompletableFuture.completedFuture(response1), + CompletableFuture.completedFuture(response2)); + + Mono result = s3ObjectStore.deleteBucket(DataStore.Bucket.FILES); + + StepVerifier.create(result) + .expectNext("NOK") + .verifyComplete(); + } + + @Test + void testCopyFileTo_Success() throws URISyntaxException { + PutObjectRequest request = PutObjectRequest.builder() // + .bucket("test-bucket") // + .key("test-access-key-id") // + .build(); + + when(s3AsynchClient.putObject(any(PutObjectRequest.class), any(AsyncRequestBody.class))) + .thenAnswer(invocation -> { + CompletableFuture future = CompletableFuture.completedFuture( + PutObjectResponse.builder().build() + ); + return future; + }); + + Path testFile = Paths.get(getClass().getResource("/org/oran/pmproducer/datastore/file.txt").toURI()); + + Mono result = s3ObjectStore.copyFileTo(testFile, "test-key"); + + StepVerifier.create(result) + .expectNext("test-key") + .verifyComplete(); + } + + @Test + void testReadObject() { + // Mock the getObject method to return a CompletableFuture with ResponseBytes + when(s3AsynchClient.getObject(any(GetObjectRequest.class), any(AsyncResponseTransformer.class))) + .thenAnswer(invocation -> { + ResponseBytes responseBytes = ResponseBytes.fromByteArray( + GetObjectResponse.builder().build(), + "Hello, World!".getBytes(StandardCharsets.UTF_8) + ); + CompletableFuture> future = CompletableFuture.completedFuture( + responseBytes + ); + return future; + }); + + // Call the method under test + Mono result = s3ObjectStore.readObject(DataStore.Bucket.FILES, "test-key"); + + byte[] expectedBytes = "Hello, World!".getBytes(StandardCharsets.UTF_8); + StepVerifier.create(result) + .consumeNextWith(actualBytes -> Assertions.assertArrayEquals(expectedBytes, actualBytes)) + .verifyComplete(); + } + + @Test + void testPutObject() { + // Mock the putObject method to return a CompletableFuture with PutObjectResponse + when(s3AsynchClient.putObject(any(PutObjectRequest.class), any(AsyncRequestBody.class))) + .thenAnswer(invocation -> { + CompletableFuture future = CompletableFuture.completedFuture( + PutObjectResponse.builder().build() + ); + return future; + }); + + // Call the method under test + Mono result = s3ObjectStore.putObject(DataStore.Bucket.FILES, "test-key", "Hello, World!"); + + // Verify the Mono's behavior using StepVerifier + StepVerifier.create(result) + .expectNext("test-key") + .verifyComplete(); + } + + private S3Object createS3Object(String key) { + return S3Object.builder().key(key).build(); + } +} diff --git a/pmproducer/src/test/java/org/oran/pmproducer/oauth2/OAuthBearerTokenJwtTest.java b/pmproducer/src/test/java/org/oran/pmproducer/oauth2/OAuthBearerTokenJwtTest.java new file mode 100644 index 0000000..6706824 --- /dev/null +++ b/pmproducer/src/test/java/org/oran/pmproducer/oauth2/OAuthBearerTokenJwtTest.java @@ -0,0 +1,111 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2023 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. + * ========================LICENSE_END=================================== + */ + +package org.oran.pmproducer.oauth2; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import com.fasterxml.jackson.core.JsonProcessingException; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; +import org.oran.pmproducer.exceptions.ServiceException; +import org.springframework.test.context.ContextConfiguration; + +@ContextConfiguration(classes = {OAuthBearerTokenJwtTest.class}) +@ExtendWith(MockitoExtension.class) +class OAuthBearerTokenJwtTest { + + private OAuthBearerTokenJwt token; + + @BeforeEach + void setUp() throws ServiceException, JsonProcessingException { + String validJwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"; // Replace with a valid JWT token for testing + token = OAuthBearerTokenJwt.create(validJwt); + } + + @Test + void testCreateValidToken() { + assertNotNull(token); + } + + @Test + void testCreateInvalidToken() { + assertThrows(ServiceException.class, () -> OAuthBearerTokenJwt.create("invalid_token")); + } + + @Test + void testTokenValue() { + assertEquals("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c", token.value()); + } + + @Test + void testTokenScope() { + assertEquals(0, token.scope().size()); + assertFalse(token.scope().contains("")); + } + + @Test + void testTokenLifetimeMs() { + assertEquals(Long.MAX_VALUE, token.lifetimeMs()); + } + + @Test + void testTokenPrincipalName() { + assertEquals("1234567890", token.principalName()); + } + + @Test + void testTokenStartTimeMs() { + assertEquals(1516239022L, token.startTimeMs()); + } + + @Test + void testCreateTokenFromInvalidPayload() throws ServiceException { + // Create a JWT with an invalid payload (missing fields) + String invalidPayload = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"; + assertThrows(ServiceException.class, () -> OAuthBearerTokenJwt.create(invalidPayload)); + } + + @Test + void testCreateTokenWithValidPayload() throws ServiceException, JsonProcessingException { + // Create a JWT with a valid payload + String validPayload = "eyJzdWIiOiAiVGVzdCIsICJleHAiOiAxNjM1MTUwMDAwLCAiaWF0IjogMTYzNTA5NTAwMCwgInNjb3BlIjogInNjb3BlX3Rva2VuIiwgImp0aSI6ICJmb28ifQ=="; + OAuthBearerTokenJwt jwt = OAuthBearerTokenJwt.create("header." + validPayload + ".signature"); + + assertNotNull(jwt); + assertEquals("header." + validPayload + ".signature", jwt.value()); + assertEquals(1, jwt.scope().size()); + assertEquals("scope_token", jwt.scope().iterator().next()); + assertEquals("Test", jwt.principalName()); + assertEquals(1635095000, jwt.startTimeMs()); + } + + @Test + void testCreateThrowsExceptionWithInvalidToken() throws ServiceException { + String tokenRaw = "your_mocked_token_here"; + assertThrows(ServiceException.class, () -> OAuthBearerTokenJwt.create(tokenRaw)); + } +} + diff --git a/pmproducer/src/test/java/org/oran/pmproducer/oauth2/OAuthKafkaAuthenticateLoginCallbackHandlerTest.java b/pmproducer/src/test/java/org/oran/pmproducer/oauth2/OAuthKafkaAuthenticateLoginCallbackHandlerTest.java new file mode 100644 index 0000000..260724a --- /dev/null +++ b/pmproducer/src/test/java/org/oran/pmproducer/oauth2/OAuthKafkaAuthenticateLoginCallbackHandlerTest.java @@ -0,0 +1,129 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2023 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. + * ========================LICENSE_END=================================== + */ + +package org.oran.pmproducer.oauth2; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.UnsupportedCallbackException; +import javax.security.auth.login.AppConfigurationEntry; +import org.apache.kafka.common.security.auth.SaslExtensionsCallback; +import org.apache.kafka.common.security.oauthbearer.OAuthBearerLoginModule; +import org.apache.kafka.common.security.oauthbearer.OAuthBearerTokenCallback; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +class OAuthKafkaAuthenticateLoginCallbackHandlerTest { + + private OAuthKafkaAuthenticateLoginCallbackHandler callbackHandler; + + @BeforeEach + void setUp() { + callbackHandler = new OAuthKafkaAuthenticateLoginCallbackHandler(); + } + + @Test + void testConfigureWithValidSaslMechanismAndConfigEntry() { + String saslMechanism = OAuthBearerLoginModule.OAUTHBEARER_MECHANISM; + List jaasConfigEntries = Collections.singletonList(Mockito.mock(AppConfigurationEntry.class)); + + callbackHandler.configure(new HashMap<>(), saslMechanism, jaasConfigEntries); + + assertTrue(callbackHandler.isConfigured()); + } + + @SuppressWarnings("java:S5778") + @Test + void testConfigureWithInvalidSaslMechanism() { + String invalidSaslMechanism = "InvalidMechanism"; + List jaasConfigEntries = Collections.singletonList(Mockito.mock(AppConfigurationEntry.class)); + + assertThrows(IllegalArgumentException.class, () -> callbackHandler.configure(new HashMap<>(), invalidSaslMechanism, jaasConfigEntries)); + + assertFalse(callbackHandler.isConfigured()); + } + + @SuppressWarnings("java:S5778") + @Test + void testConfigureWithEmptyJaasConfigEntries() { + String saslMechanism = OAuthBearerLoginModule.OAUTHBEARER_MECHANISM; + List emptyJaasConfigEntries = Collections.emptyList(); + + assertThrows(IllegalArgumentException.class, () -> callbackHandler.configure(new HashMap<>(), saslMechanism, emptyJaasConfigEntries)); + + assertFalse(callbackHandler.isConfigured()); + } + + @Test + void testHandleSaslExtensionsCallback() throws IOException, UnsupportedCallbackException { + String saslMechanism = OAuthBearerLoginModule.OAUTHBEARER_MECHANISM; + List jaasConfigEntries = Collections.singletonList(Mockito.mock(AppConfigurationEntry.class)); + + callbackHandler.configure(new HashMap<>(), saslMechanism, jaasConfigEntries); + SaslExtensionsCallback callback = mock(SaslExtensionsCallback.class); + + callbackHandler.handle(new Callback[]{callback}); + verify(callback).extensions(any()); + } + + @Test + void testHandleUnsupportedCallback() { + Callback unsupportedCallback = mock(Callback.class); + String saslMechanism = OAuthBearerLoginModule.OAUTHBEARER_MECHANISM; + List jaasConfigEntries = Collections.singletonList(Mockito.mock(AppConfigurationEntry.class)); + + callbackHandler.configure(new HashMap<>(), saslMechanism, jaasConfigEntries); + assertThrows(UnsupportedCallbackException.class, () -> callbackHandler.handle(new Callback[]{unsupportedCallback})); + } + + @Test + void testHandleOAuthBearerTokenCallback() throws IOException, UnsupportedCallbackException { + + String saslMechanism = OAuthBearerLoginModule.OAUTHBEARER_MECHANISM; + List jaasConfigEntries = Collections.singletonList(Mockito.mock(AppConfigurationEntry.class)); + String validJwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"; + + callbackHandler.configure(new HashMap<>(), saslMechanism, jaasConfigEntries); + + OAuthBearerTokenCallback oauthBearerTokenCallback = Mockito.mock(OAuthBearerTokenCallback.class); + SecurityContext securityContextMock = Mockito.mock(SecurityContext.class); + when(oauthBearerTokenCallback.token()).thenReturn(null); // Ensure the callback has no token initially + when(oauthBearerTokenCallback.token()).thenAnswer(invocation -> { + return OAuthBearerTokenJwt.create(validJwt); + }); + + when(securityContextMock.getBearerAuthToken()).thenReturn(validJwt); + callbackHandler.handle(new Callback[]{oauthBearerTokenCallback}); + verify(oauthBearerTokenCallback).token(); + } +} + diff --git a/pmproducer/src/test/java/org/oran/pmproducer/oauth2/SecurityContextTest.java b/pmproducer/src/test/java/org/oran/pmproducer/oauth2/SecurityContextTest.java new file mode 100644 index 0000000..43d83a7 --- /dev/null +++ b/pmproducer/src/test/java/org/oran/pmproducer/oauth2/SecurityContextTest.java @@ -0,0 +1,81 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2023 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. + * ========================LICENSE_END=================================== + */ + +package org.oran.pmproducer.oauth2; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.nio.file.Path; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.MockitoAnnotations; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class SecurityContextTest { + + @BeforeEach + void setUp() { + MockitoAnnotations.initMocks(this); + } + + @Test + void testConstructorWithAuthTokenFilename() { + SecurityContext securityContext = new SecurityContext("auth-token-file.txt"); + assertNotNull(securityContext.getAuthTokenFilePath()); + assertEquals(Path.of("auth-token-file.txt"), securityContext.getAuthTokenFilePath()); + } + + @Test + void testConstructorWithoutAuthTokenFilename() { + SecurityContext securityContext = new SecurityContext(""); + assertNull(securityContext.getAuthTokenFilePath()); + } + + @Test + void testIsConfigured() { + SecurityContext securityContext = new SecurityContext("auth-token-file.txt"); + assertTrue(securityContext.isConfigured()); + } + + @Test + void testIsNotConfigured() { + SecurityContext securityContext = new SecurityContext(""); + assertFalse(securityContext.isConfigured()); + } + + @Test + void testGetBearerAuthToken() { + assertEquals("", SecurityContext.getInstance().getBearerAuthToken()); + assertEquals("", (new SecurityContext("foo.txt")).getBearerAuthToken()); + } + + @Test + void testGetBearerAuthTokenWhenNotConfigured() { + SecurityContext securityContext = new SecurityContext(""); + assertEquals("", securityContext.getBearerAuthToken()); + } +} + diff --git a/pmproducer/src/test/resources/org/oran/pmproducer/datastore/file.txt b/pmproducer/src/test/resources/org/oran/pmproducer/datastore/file.txt new file mode 100644 index 0000000..c95df2d --- /dev/null +++ b/pmproducer/src/test/resources/org/oran/pmproducer/datastore/file.txt @@ -0,0 +1 @@ +Hi, How are you? \ No newline at end of file