diff options
author | raviteja.karumuri <raviteja.karumuri@est.tech> | 2024-03-28 18:22:42 +0000 |
---|---|---|
committer | raviteja.karumuri <raviteja.karumuri@est.tech> | 2024-05-29 17:09:25 +0100 |
commit | 87ac816229c2bf3049402d727a2c3edc0592965a (patch) | |
tree | 6eac8d105afb929ed3711a83e8c23567ad50781a | |
parent | 6de5c037f336ac5e7bee62e1dd2062a797a83e51 (diff) |
Implementing Create Policy for V3
Issue-ID: CCSDK-4003
Change-Id: I0da2525c8ef273f23519e9bb4332f64e92e23cb8
Signed-off-by: Raviteja Karumuri <raviteja.karumuri@est.tech>
11 files changed, 1038 insertions, 13 deletions
diff --git a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/Consts.java b/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/Consts.java index c554dbd5..21da2f77 100644 --- a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/Consts.java +++ b/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/Consts.java @@ -32,6 +32,8 @@ public class Consts { public static final String V2_API_ROOT = "/a1-policy/v2"; + public static final String V3_API_ROOT = "/a1policymanagement/v1"; + public static final String V2_API_SERVICE_CALLBACKS_NAME = "Service callbacks"; public static final String V2_API_SERVICE_CALLBACKS_DESCRIPTION = ""; diff --git a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v3/PolicyControllerV3.java b/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v3/PolicyControllerV3.java new file mode 100644 index 00000000..36da71da --- /dev/null +++ b/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v3/PolicyControllerV3.java @@ -0,0 +1,95 @@ +/*- + * ========================LICENSE_START================================= + * ONAP : ccsdk oran + * ====================================================================== + * Copyright (C) 2024 OpenInfra Foundation Europe. 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=================================== + */ + +package org.onap.ccsdk.oran.a1policymanagementservice.controllers.v3; + +import io.swagger.v3.oas.annotations.tags.Tag; +import org.onap.ccsdk.oran.a1policymanagementservice.controllers.api.v3.A1PolicyManagementApi; +import org.onap.ccsdk.oran.a1policymanagementservice.controllers.v2.Consts; +import org.onap.ccsdk.oran.a1policymanagementservice.controllers.v2.PolicyController; +import org.onap.ccsdk.oran.a1policymanagementservice.models.v3.PolicyInformation; +import org.onap.ccsdk.oran.a1policymanagementservice.models.v3.PolicyObjectInformation; +import org.onap.ccsdk.oran.a1policymanagementservice.models.v3.PolicyTypeInformation; +import org.onap.ccsdk.oran.a1policymanagementservice.repository.PolicyType; +import org.onap.ccsdk.oran.a1policymanagementservice.repository.Ric; +import org.onap.ccsdk.oran.a1policymanagementservice.service.v3.PolicyService; +import org.onap.ccsdk.oran.a1policymanagementservice.util.v3.Helper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +@RestController("PolicyControllerV3") +@Tag(// + name = PolicyController.API_NAME, // + description = PolicyController.API_DESCRIPTION // +) +@RequestMapping(Consts.V3_API_ROOT) +public class PolicyControllerV3 implements A1PolicyManagementApi { + public static final String API_NAME = "A1 Policy Management"; + public static final String API_DESCRIPTION = "API to create,update and get policies or policy definitions"; + @Autowired + private PolicyService policyService; + + @Autowired + private Helper helper; + + private PolicyType policyType; + + private Ric ric; + @Override + public Mono<ResponseEntity<PolicyObjectInformation>> createPolicy(Mono<PolicyObjectInformation> policyObjectInformation, ServerWebExchange exchange) { + return policyObjectInformation.flatMap(policyObjectInfo -> { + return policyService.createPolicyService(policyObjectInfo, exchange); + }); + } + + @Override + public Mono<ResponseEntity<Void>> deletePolicy(String policyId, String accept, ServerWebExchange exchange) throws Exception { + return null; + } + + @Override + public Mono<ResponseEntity<Object>> getPolicy(String policyId, String accept, ServerWebExchange exchange) throws Exception { + return null; + } + + @Override + public Mono<ResponseEntity<Flux<PolicyInformation>>> getPolicyIds(String policyTypeId, String nearRtRicId, String serviceId, String typeName, String accept, ServerWebExchange exchange) throws Exception { + return null; + } + + @Override + public Mono<ResponseEntity<Object>> getPolicyTypeDefinition(String policyTypeId, String accept, ServerWebExchange exchange) throws Exception { + return null; + } + + @Override + public Mono<ResponseEntity<Flux<PolicyTypeInformation>>> getPolicyTypes(String nearRtRicId, String typeName, String compatibleWithVersion, String accept, ServerWebExchange exchange) throws Exception { + return null; + } + + @Override + public Mono<ResponseEntity<Object>> putPolicy(String policyId, Mono<Object> body, ServerWebExchange exchange) throws Exception { + return null; + } +} diff --git a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/service/v3/AuthorizationService.java b/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/service/v3/AuthorizationService.java new file mode 100644 index 00000000..ac4320a1 --- /dev/null +++ b/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/service/v3/AuthorizationService.java @@ -0,0 +1,42 @@ +/*- + * ========================LICENSE_START================================= + * ONAP : ccsdk oran + * ====================================================================== + * Copyright (C) 2024 OpenInfra Foundation Europe. 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=================================== + */ + +package org.onap.ccsdk.oran.a1policymanagementservice.service.v3; + + +import org.onap.ccsdk.oran.a1policymanagementservice.controllers.authorization.AuthorizationCheck; +import org.onap.ccsdk.oran.a1policymanagementservice.controllers.authorization.PolicyAuthorizationRequest.Input.AccessType; +import org.onap.ccsdk.oran.a1policymanagementservice.repository.Policy; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +@Service +public class AuthorizationService { + + @Autowired + private AuthorizationCheck authorization; + + public Mono<Policy> authCheck (ServerWebExchange serverWebExchange, Policy policy, AccessType accessType){ + return authorization.doAccessControl(serverWebExchange.getRequest().getHeaders().toSingleValueMap(), + policy, AccessType.WRITE); + } +} diff --git a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/service/v3/ErrorHandlingService.java b/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/service/v3/ErrorHandlingService.java new file mode 100644 index 00000000..693ae513 --- /dev/null +++ b/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/service/v3/ErrorHandlingService.java @@ -0,0 +1,65 @@ +/*- + * ========================LICENSE_START================================= + * ONAP : ccsdk oran + * ====================================================================== + * Copyright (C) 2024 OpenInfra Foundation Europe. 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=================================== + */ + +package org.onap.ccsdk.oran.a1policymanagementservice.service.v3; + +import org.onap.ccsdk.oran.a1policymanagementservice.controllers.v2.PolicyController; +import org.onap.ccsdk.oran.a1policymanagementservice.exceptions.ServiceException; +import org.onap.ccsdk.oran.a1policymanagementservice.models.v3.ProblemDetails; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.*; +import org.springframework.stereotype.Service; +import org.springframework.web.reactive.function.client.WebClientException; +import org.springframework.web.reactive.function.client.WebClientResponseException; +import reactor.core.publisher.Mono; + +import java.lang.invoke.MethodHandles; +import java.math.BigDecimal; + +@Service +public class ErrorHandlingService { + + private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + public Mono<ResponseEntity<ProblemDetails>> handleError(Throwable throwable) { + if (throwable instanceof WebClientResponseException) { + WebClientResponseException e = (WebClientResponseException) throwable; + return createErrorResponse(e.getResponseBodyAsString(), e.getStatusCode()); + } else if (throwable instanceof WebClientException) { + WebClientException e = (WebClientException) throwable; + return createErrorResponse(e.getMessage(), HttpStatus.BAD_GATEWAY); + } else if (throwable instanceof ServiceException) { + ServiceException e = (ServiceException) throwable; + return createErrorResponse(e.getMessage(), e.getHttpStatus()); + } else { + return createErrorResponse(throwable.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR); + } + } + + public Mono<ResponseEntity<ProblemDetails>> createErrorResponse(String errorBody, HttpStatusCode statusCode) { + logger.debug("Error content: {}, with status code {}", errorBody, statusCode); + ProblemDetails problemDetail = new ProblemDetails().type("about:blank"); + problemDetail.setDetail(errorBody); + problemDetail.setStatus(new BigDecimal(statusCode.value())); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_PROBLEM_JSON); + return Mono.just(new ResponseEntity<>(problemDetail, headers, statusCode)); + } +} diff --git a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/service/v3/PolicyService.java b/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/service/v3/PolicyService.java new file mode 100644 index 00000000..6c994b42 --- /dev/null +++ b/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/service/v3/PolicyService.java @@ -0,0 +1,96 @@ +/*- + * ========================LICENSE_START================================= + * ONAP : ccsdk oran + * ====================================================================== + * Copyright (C) 2024 OpenInfra Foundation Europe. 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=================================== + */ + +package org.onap.ccsdk.oran.a1policymanagementservice.service.v3; + +import org.onap.ccsdk.oran.a1policymanagementservice.clients.A1ClientFactory; +import org.onap.ccsdk.oran.a1policymanagementservice.controllers.authorization.PolicyAuthorizationRequest.Input.AccessType; +import org.onap.ccsdk.oran.a1policymanagementservice.exceptions.ServiceException; +import org.onap.ccsdk.oran.a1policymanagementservice.models.v3.PolicyObjectInformation; +import org.onap.ccsdk.oran.a1policymanagementservice.repository.*; +import org.onap.ccsdk.oran.a1policymanagementservice.util.v3.Helper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +@Service +public class PolicyService { + + @Autowired + private Helper helper; + + @Autowired + private Rics rics; + + @Autowired + private PolicyTypes policyTypes; + + @Autowired + private Policies policies; + + @Autowired + private AuthorizationService authorizationService; + + @Autowired + private A1ClientFactory a1ClientFactory; + + @Autowired + private ErrorHandlingService errorHandlingService; + + public Mono<ResponseEntity<PolicyObjectInformation>> createPolicyService + (PolicyObjectInformation policyObjectInfo, ServerWebExchange serverWebExchange) { + try { + if (!helper.jsonSchemaValidation(policyObjectInfo.getPolicyObject())) + return Mono.error(new ServiceException("Schema validation failed", HttpStatus.BAD_REQUEST)); + Ric ric = rics.getRic(policyObjectInfo.getNearRtRicId()); + PolicyType policyType = policyTypes.getType(policyObjectInfo.getPolicyTypeId()); + Policy policy = helper.buildPolicy(policyObjectInfo, policyType, ric, helper.policyIdGeneration()); + return helper.isPolicyAlreadyCreated(policy,policies) + .doOnError(error -> errorHandlingService.handleError(error)) + .flatMap(policyBuilt -> authorizationService.authCheck(serverWebExchange, policy, AccessType.WRITE)) + .doOnError(error -> errorHandlingService.handleError(error)) + .flatMap(policyNotUsed -> ric.getLock().lock(Lock.LockType.SHARED, "createPolicy")) + .flatMap(grant -> postPolicy(policy, grant)) + .map(locationHeaderValue -> + new ResponseEntity<PolicyObjectInformation>(policyObjectInfo,helper.createHttpHeaders( + "location",helper.buildURI(policy.getId(), serverWebExchange)), HttpStatus.CREATED)) + .doOnError(error -> errorHandlingService.handleError(error)); + } catch (Exception ex) { + return Mono.error(ex); + } + + } + + private Mono<String> postPolicy(Policy policy, Lock.Grant grant) { + return helper.checkRicStateIdle(policy.getRic()) + .doOnError(error -> errorHandlingService.handleError(error)) + .flatMap(ric -> helper.checkSupportedType(ric, policy.getType())) + .doOnError(error -> errorHandlingService.handleError(error)) + .flatMap(ric -> a1ClientFactory.createA1Client(ric)) + .flatMap(a1Client -> a1Client.putPolicy(policy)) + .doOnError(error -> errorHandlingService.handleError(error)) + .doOnNext(policyString -> policies.put(policy)) + .doFinally(releaseLock -> grant.unlockBlocking()) + .doOnError(error -> errorHandlingService.handleError(error)); + } +} diff --git a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/util/v3/Helper.java b/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/util/v3/Helper.java new file mode 100644 index 00000000..11edbfd9 --- /dev/null +++ b/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/util/v3/Helper.java @@ -0,0 +1,128 @@ +/*- + * ========================LICENSE_START================================= + * ONAP : ccsdk oran + * ====================================================================== + * Copyright (C) 2024 OpenInfra Foundation Europe. 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=================================== + */ + +package org.onap.ccsdk.oran.a1policymanagementservice.util.v3; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import org.onap.ccsdk.oran.a1policymanagementservice.exceptions.ServiceException; +import org.onap.ccsdk.oran.a1policymanagementservice.models.v3.PolicyObjectInformation; +import org.onap.ccsdk.oran.a1policymanagementservice.repository.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Component; +import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.util.UriComponentsBuilder; +import reactor.core.publisher.Mono; + +import java.lang.invoke.MethodHandles; +import java.time.Instant; +import java.util.UUID; + +@Component +public class Helper { + + @Autowired + private Services services; + + private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + + private static Gson gson = new GsonBuilder().create(); + public void keepServiceAlive(String name) { + Service s = this.services.get(name); + if (s != null) { + s.keepAlive(); + } + } + + public Mono<Ric> checkRicStateIdle(Ric ric) { + if (ric.getState() == Ric.RicState.AVAILABLE) { + return Mono.just(ric); + } else { + logger.debug("Request rejected Near-RT RIC not IDLE, ric: {}", ric); + ServiceException e = new ServiceException( + "Near-RT RIC: is not operational, id: " + ric.id() + ", state: " + ric.getState(), + HttpStatus.LOCKED); + return Mono.error(e); + } + } + + public Mono<Ric> checkSupportedType(Ric ric, PolicyType type) { + if (!ric.isSupportingType(type.getId())) { + logger.debug("Request rejected, type not supported, RIC: {}", ric); + ServiceException e = new ServiceException( + "Type: " + type.getId() + " not supported by RIC: " + ric.id(), HttpStatus.BAD_REQUEST); + return Mono.error(e); + } + return Mono.just(ric); + } + + public Policy buildPolicy(PolicyObjectInformation policyObjectInformation, PolicyType policyType, Ric ric, String policyId) { + return Policy.builder() + .id(policyId) + .json(toJson(policyObjectInformation.getPolicyObject())) + .type(policyType) + .ric(ric) + .ownerServiceId(policyObjectInformation.getServiceId() == null ? "" + : policyObjectInformation.getServiceId()) + .lastModified(Instant.now()) + .isTransient(policyObjectInformation.getTransient()) + .statusNotificationUri(policyObjectInformation.getStatusNotificationUri() == null ? "" + : policyObjectInformation.getStatusNotificationUri()) + .build(); + } + + public Boolean jsonSchemaValidation(Object jsonObject) { + String jsonString = toJson(jsonObject); + return true; + } + + public String policyIdGeneration() { + return UUID.randomUUID().toString(); + } + + public String toJson(Object jsonObject) { + return gson.toJson(jsonObject); + } + + public HttpHeaders createHttpHeaders(String headerName, String headerValue) { + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.add(headerName, headerValue); + return httpHeaders; + } + + public String buildURI(String policyId, ServerWebExchange serverWebExchange) { + return UriComponentsBuilder.fromHttpRequest(serverWebExchange.getRequest()) + .path("/{id}") + .buildAndExpand(policyId) + .toString(); + } + + public Mono<Policy> isPolicyAlreadyCreated(Policy policy, Policies policies) { + if (policies.get(policy.getId()) != null) { + return Mono.error(new ServiceException + ("Policy already created with ID: " + policy.getId(), HttpStatus.CONFLICT)); + } + return Mono.just(policy); + } +} diff --git a/a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/config/TestConfig.java b/a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/config/TestConfig.java new file mode 100644 index 00000000..19e4d797 --- /dev/null +++ b/a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/config/TestConfig.java @@ -0,0 +1,57 @@ +/*- + * ========================LICENSE_START================================= + * ONAP : ccsdk oran + * ====================================================================== + * Copyright (C) 2024 OpenInfra Foundation Europe. 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=================================== + */ + +package org.onap.ccsdk.oran.a1policymanagementservice.config; + +import org.onap.ccsdk.oran.a1policymanagementservice.clients.A1ClientFactory; +import org.onap.ccsdk.oran.a1policymanagementservice.configuration.ApplicationConfig; +import org.onap.ccsdk.oran.a1policymanagementservice.repository.Policies; +import org.onap.ccsdk.oran.a1policymanagementservice.repository.PolicyTypes; +import org.onap.ccsdk.oran.a1policymanagementservice.repository.Services; +import org.onap.ccsdk.oran.a1policymanagementservice.tasks.ServiceSupervision; +import org.onap.ccsdk.oran.a1policymanagementservice.utils.MockA1ClientFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; +import org.springframework.boot.web.servlet.server.ServletWebServerFactory; +import org.springframework.context.annotation.Bean; + +import java.time.Duration; + +@TestConfiguration +public class TestConfig { + + @Bean + A1ClientFactory getA1ClientFactory(@Autowired ApplicationConfig appConfig, @Autowired PolicyTypes types) { + return new MockA1ClientFactory(appConfig, types); + } + + @Bean + public ServiceSupervision getServiceSupervision(@Autowired Services services, + @Autowired A1ClientFactory a1ClientFactory, @Autowired Policies policies) { + Duration checkInterval = Duration.ofMillis(1); + return new ServiceSupervision(services, policies, a1ClientFactory, checkInterval); + } + + @Bean + public ServletWebServerFactory servletContainer() { + return new TomcatServletWebServerFactory(); + } +}
\ No newline at end of file diff --git a/a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/ApplicationTest.java b/a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/ApplicationTest.java index 2ff44169..4f3fa5f5 100644 --- a/a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/ApplicationTest.java +++ b/a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/ApplicationTest.java @@ -497,19 +497,6 @@ class ApplicationTest { return objectMapper.writeValueAsString(policyInfo); } - private String putPolicyBod(String serviceName, String ricId, String policyTypeName, String policyInstanceId, - boolean isTransient, String statusNotificationUri) throws JsonProcessingException { - PolicyInfo policyInfo = new PolicyInfo(); - policyInfo.setPolicyId(policyInstanceId); - policyInfo.setPolicytypeId(policyTypeName); - policyInfo.setRicId(ricId); - policyInfo.setServiceId(serviceName); - policyInfo.setPolicyData(jsonString()); - policyInfo.setTransient(isTransient); - policyInfo.setStatusNotificationUri(statusNotificationUri); - return objectMapper.writeValueAsString(policyInfo); - } - private String putPolicyBody(String serviceName, String ricId, String policyTypeName, String policyInstanceId) throws JsonProcessingException { return putPolicyBody(serviceName, ricId, policyTypeName, policyInstanceId, false, "statusUri"); } diff --git a/a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v3/PolicyControllerTest.java b/a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v3/PolicyControllerTest.java new file mode 100644 index 00000000..b8404a61 --- /dev/null +++ b/a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v3/PolicyControllerTest.java @@ -0,0 +1,180 @@ +/*- + * ========================LICENSE_START================================= + * ONAP : ccsdk oran + * ====================================================================== + * Copyright (C) 2024 OpenInfra Foundation Europe. 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=================================== + */ + +package org.onap.ccsdk.oran.a1policymanagementservice.controllers.v3; + +import org.junit.jupiter.api.*; +import org.mockito.Mockito; +import org.onap.ccsdk.oran.a1policymanagementservice.clients.SecurityContext; +import org.onap.ccsdk.oran.a1policymanagementservice.config.TestConfig; +import org.onap.ccsdk.oran.a1policymanagementservice.configuration.ApplicationConfig; +import org.onap.ccsdk.oran.a1policymanagementservice.controllers.OpenPolicyAgentSimulatorController; +import org.onap.ccsdk.oran.a1policymanagementservice.controllers.v2.RappSimulatorController; +import org.onap.ccsdk.oran.a1policymanagementservice.repository.Policies; +import org.onap.ccsdk.oran.a1policymanagementservice.repository.PolicyTypes; +import org.onap.ccsdk.oran.a1policymanagementservice.repository.Rics; +import org.onap.ccsdk.oran.a1policymanagementservice.repository.Services; +import org.onap.ccsdk.oran.a1policymanagementservice.util.v3.Helper; +import org.onap.ccsdk.oran.a1policymanagementservice.utils.MockA1ClientFactory; +import org.onap.ccsdk.oran.a1policymanagementservice.utils.v3.TestHelper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestPropertySource; +import org.springframework.util.FileSystemUtils; +import reactor.core.publisher.Mono; + +import java.lang.invoke.MethodHandles; +import java.nio.file.Path; + +import static org.mockito.Mockito.when; + +@TestMethodOrder(MethodOrderer.MethodName.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@ContextConfiguration(classes = TestConfig.class) +@TestPropertySource(properties = { // + "server.ssl.key-store=./config/keystore.jks", // + "app.webclient.trust-store=./config/truststore.jks", // + "app.webclient.trust-store-used=true", // + "app.vardata-directory=/tmp/pmstestv3", // + "app.filepath=", // + "app.s3.bucket=" // If this is set, S3 will be used to store data. +}) +public class PolicyControllerTest { + private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + + @Autowired + private ApplicationConfig applicationConfig; + @Autowired + private TestHelper testHelper; + + @Autowired + private Rics rics; + + @Autowired + private Policies policies; + + @Autowired + private PolicyTypes policyTypes; + + @Autowired + private Services services; + + @Autowired + private MockA1ClientFactory a1ClientFactory; + + @Autowired + private RappSimulatorController rAppSimulator; + + @Autowired + private SecurityContext securityContext; + + @Autowired + private OpenPolicyAgentSimulatorController openPolicyAgentSimulatorController; + + @LocalServerPort + private int port; + + @SpyBean + private Helper helper; + + @BeforeEach + void init() { + testHelper.port = port; + this.applicationConfig.setAuthProviderUrl(testHelper.baseUrl() + OpenPolicyAgentSimulatorController.ACCESS_CONTROL_URL); + } + + @AfterEach + void reset() { + rics.clear(); + policies.clear(); + policyTypes.clear(); + services.clear(); + a1ClientFactory.reset(); + this.rAppSimulator.getTestResults().clear(); + this.a1ClientFactory.setPolicyTypes(policyTypes); // Default same types in RIC and in this app + this.securityContext.setAuthTokenFilePath(null); + this.openPolicyAgentSimulatorController.getTestResults().reset(); + } + + @AfterAll + static void clearTestDir() { + try { + FileSystemUtils.deleteRecursively(Path.of("/tmp/pmstestv3")); + } catch (Exception e) { + logger.warn("Could test directory : {}", e.getMessage()); + } + } + + @Test + @DisplayName("test Create Policy") + void testPostPolicy() throws Exception { + String nonRtRicId = "ric.1"; + String policyTypeName = "type1_1.2.3"; + String url = "/policies"; + testHelper.addPolicyType(policyTypeName, nonRtRicId); + String policyBody = testHelper.postPolicyBody(nonRtRicId, policyTypeName); + Mono<ResponseEntity<String>> responseMono = testHelper.restClientV3().postForEntity(url, policyBody); + testHelper.testSuccessResponse(responseMono, HttpStatus.CREATED, "{\"servingCellNrcgi\":\"1\"}"); + } + + @Test + @DisplayName("test Create Policy schema validation fail case") + void testPolicySchemaValidationFail() throws Exception { + String nonRtRicId = "ric.1"; + String policyTypeName = "type1_1.2.3"; + String url = "/policies"; + testHelper.addPolicyType(policyTypeName, nonRtRicId); + + when(helper.jsonSchemaValidation(Mockito.any())).thenReturn(Boolean.FALSE); + String policyBody = testHelper.postPolicyBody(nonRtRicId, policyTypeName); + Mono<ResponseEntity<String>> responseMono = testHelper.restClientV3().postForEntity(url, policyBody); + testHelper.testErrorCode(responseMono, HttpStatus.BAD_REQUEST, " Schema validation failed"); + } + + @Test + @DisplayName("test Create Policy No Ric fail case") + void testCreatePolicyNoRic() throws Exception { + String policyTypeName = "type1_1.2.3"; + String url = "/policies"; + testHelper.addPolicyType(policyTypeName, " "); + String policyBody = testHelper.postPolicyBody("noRic", policyTypeName); + Mono<ResponseEntity<String>> responseMono = testHelper.restClientV3().postForEntity(url, policyBody); + testHelper.testErrorCode(responseMono, HttpStatus.NOT_FOUND, " Could not find ric: noRic"); + } + + @Test + @DisplayName("test Create Policy with No Policy Type fail case") + void testCreatePolicyNoPolicyType() throws Exception { + String policyTypeName = "type1_1.2.3"; + String nonRtRicId = "ricOne"; + String url = "/policies"; + testHelper.addPolicyType(policyTypeName, nonRtRicId); + String policyBody = testHelper.postPolicyBody(nonRtRicId, "noPolicyType"); + Mono<ResponseEntity<String>> responseMono = testHelper.restClientV3().postForEntity(url, policyBody); + testHelper.testErrorCode(responseMono, HttpStatus.NOT_FOUND, "Could not find type: noPolicyType"); + } +} diff --git a/a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/service/v3/PolicyServiceTest.java b/a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/service/v3/PolicyServiceTest.java new file mode 100644 index 00000000..a67a0bb3 --- /dev/null +++ b/a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/service/v3/PolicyServiceTest.java @@ -0,0 +1,106 @@ +/*- + * ========================LICENSE_START================================= + * ONAP : ccsdk oran + * ====================================================================== + * Copyright (C) 2024 OpenInfra Foundation Europe. 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=================================== + */ + +package org.onap.ccsdk.oran.a1policymanagementservice.service.v3; + +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.mockito.Mockito; +import org.onap.ccsdk.oran.a1policymanagementservice.clients.A1ClientFactory; +import org.onap.ccsdk.oran.a1policymanagementservice.exceptions.ServiceException; +import org.onap.ccsdk.oran.a1policymanagementservice.models.v3.PolicyObjectInformation; +import org.onap.ccsdk.oran.a1policymanagementservice.repository.Policies; +import org.onap.ccsdk.oran.a1policymanagementservice.repository.Policy; +import org.onap.ccsdk.oran.a1policymanagementservice.repository.PolicyTypes; +import org.onap.ccsdk.oran.a1policymanagementservice.repository.Rics; +import org.onap.ccsdk.oran.a1policymanagementservice.util.v3.Helper; +import org.onap.ccsdk.oran.a1policymanagementservice.utils.v3.TestHelper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.server.adapter.DefaultServerWebExchange; +import reactor.core.publisher.Mono; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +@TestMethodOrder(MethodOrderer.MethodName.class) +@SpringBootTest() +public class PolicyServiceTest { + + @Autowired + private Rics rics; + + @Autowired + private PolicyTypes policyTypes; + + @Autowired + private Policies policies; + + @Autowired + private A1ClientFactory a1ClientFactory; + + @Autowired + private ErrorHandlingService errorHandlingService; + + @Autowired + private PolicyService policyService; + + @Autowired + private TestHelper testHelper; + + @MockBean + private Helper helper; + + @MockBean + private AuthorizationService authorizationService; + + @Test + public void testPolicyAlreadyCreatedFail() { + + String policyTypeName = "uri_type_123"; + String nonRtRicId = "Ric_347"; + testHelper.addPolicyType(policyTypeName, nonRtRicId); + ServerWebExchange serverWebExchange = Mockito.mock(DefaultServerWebExchange.class); + when(helper.jsonSchemaValidation(any())).thenReturn(Boolean.TRUE); + when(helper.isPolicyAlreadyCreated(any(), any())).thenReturn(Mono.error(new ServiceException + ("Policy already created", HttpStatus.CONFLICT))); + Mono<ResponseEntity<PolicyObjectInformation>> responseMono = policyService.createPolicyService(testHelper.policyObjectInfo(nonRtRicId, policyTypeName), serverWebExchange); + testHelper.verifyMockError(responseMono, "Policy already created"); + } + + @Test + public void testPolicyNotAuthorizedFail() { + + String policyTypeName = "uri_type_123"; + String nonRtRicId = "Ric_347"; + testHelper.addPolicyType(policyTypeName, nonRtRicId); + ServerWebExchange serverWebExchange = Mockito.mock(DefaultServerWebExchange.class); + when(helper.jsonSchemaValidation(any())).thenReturn(Boolean.TRUE); + when(helper.isPolicyAlreadyCreated(any(), any())).thenReturn(Mono.just(Policy.builder().build())); + when(authorizationService.authCheck(any(), any(), any())).thenReturn(Mono.error(new ServiceException("Not authorized", HttpStatus.UNAUTHORIZED))); + Mono<ResponseEntity<PolicyObjectInformation>> responseMono = policyService.createPolicyService(testHelper.policyObjectInfo(nonRtRicId, policyTypeName), serverWebExchange); + testHelper.verifyMockError(responseMono, "Not authorized"); + } +} diff --git a/a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/utils/v3/TestHelper.java b/a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/utils/v3/TestHelper.java new file mode 100644 index 00000000..bfad88a1 --- /dev/null +++ b/a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/utils/v3/TestHelper.java @@ -0,0 +1,267 @@ +/*- + * ========================LICENSE_START================================= + * ONAP : ccsdk oran + * ====================================================================== + * Copyright (C) 2024 OpenInfra Foundation Europe. 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=================================== + */ + +package org.onap.ccsdk.oran.a1policymanagementservice.utils.v3; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.onap.ccsdk.oran.a1policymanagementservice.clients.AsyncRestClient; +import org.onap.ccsdk.oran.a1policymanagementservice.clients.AsyncRestClientFactory; +import org.onap.ccsdk.oran.a1policymanagementservice.clients.SecurityContext; +import org.onap.ccsdk.oran.a1policymanagementservice.configuration.ApplicationConfig; +import org.onap.ccsdk.oran.a1policymanagementservice.configuration.RicConfig; +import org.onap.ccsdk.oran.a1policymanagementservice.configuration.WebClientConfig; +import org.onap.ccsdk.oran.a1policymanagementservice.controllers.v2.Consts; +import org.onap.ccsdk.oran.a1policymanagementservice.controllers.v2.RappSimulatorController; +import org.onap.ccsdk.oran.a1policymanagementservice.models.v3.PolicyObjectInformation; +import org.onap.ccsdk.oran.a1policymanagementservice.repository.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.http.HttpStatus; +import org.springframework.http.HttpStatusCode; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.client.WebClientResponseException; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; +import reactor.util.annotation.Nullable; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.*; + +@Component +public class TestHelper { + + @Autowired + ApplicationConfig applicationConfig; + + @Autowired + ApplicationContext context; + + @Autowired + private Rics rics; + + @Autowired + private Policies policies; + + @Autowired + private PolicyTypes policyTypes; + + @Autowired + private ObjectMapper objectMapper; + + public int port; + + public AsyncRestClient restClientV3() { + return restClientV3(false); + } + + public AsyncRestClient restClient() { + return restClient(false); + } + + public AsyncRestClient restClient(String baseUrl, boolean useTrustValidation) { + WebClientConfig config = this.applicationConfig.getWebClientConfig(); + config = WebClientConfig.builder() + .keyStoreType(config.getKeyStoreType()) + .keyStorePassword(config.getKeyStorePassword()) + .keyStore(config.getKeyStore()) + .keyPassword(config.getKeyPassword()) + .isTrustStoreUsed(useTrustValidation) + .trustStore(config.getTrustStore()) + .trustStorePassword(config.getTrustStorePassword()) + .httpProxyConfig(config.getHttpProxyConfig()) + .build(); + + AsyncRestClientFactory f = new AsyncRestClientFactory(config, new SecurityContext("")); + return f.createRestClientNoHttpProxy(baseUrl); + + } + + public String baseUrl() { + return "https://localhost:" + port; + } + + public AsyncRestClient restClientV3(boolean useTrustValidation) { + return restClient(baseUrl() + Consts.V3_API_ROOT, useTrustValidation); + } + + public AsyncRestClient restClient(boolean useTrustValidation) { + return restClient(baseUrl() + Consts.V2_API_ROOT, useTrustValidation); + } + + public void putService(String name) throws JsonProcessingException { + putService(name, 0, null); + } + + public void putService(String name, long keepAliveIntervalSeconds, @Nullable HttpStatus expectedStatus) throws JsonProcessingException { + String url = "/services"; + String body = createServiceJson(name, keepAliveIntervalSeconds); + ResponseEntity<String> resp = restClient().putForEntity(url, body).block(); + if (expectedStatus != null) { + assertNotNull(resp); + assertEquals(expectedStatus, resp.getStatusCode(), ""); + } + } + + public PolicyType createPolicyType(String policyTypeName) { + return PolicyType.builder() + .id(policyTypeName) + .schema("{\"title\":\"" + policyTypeName + "\"}") + .build(); + } + + public Ric addRic(String ricId) { + return addRic(ricId, null); + } + + public RicConfig ricConfig(String ricId, String managedElement) { + List<String> mes = new ArrayList<>(); + if (managedElement != null) { + mes.add(managedElement); + } + return RicConfig.builder() + .ricId(ricId) + .baseUrl(ricId) + .managedElementIds(mes) + .build(); + } + public Ric addRic(String ricId, String managedElement) { + if (rics.get(ricId) != null) { + return rics.get(ricId); + } + + RicConfig conf = ricConfig(ricId, managedElement); + Ric ric = new Ric(conf); + ric.setState(Ric.RicState.AVAILABLE); + this.rics.put(ric); + return ric; + } + public PolicyType addPolicyType(String policyTypeName, String ricId) { + PolicyType type = createPolicyType(policyTypeName); + policyTypes.put(type); + addRic(ricId).addSupportedPolicyType(type); + return type; + } + + public Map<String,String> jsonString() { + Map<String,String> policyDataInMap = new HashMap<>(); + policyDataInMap.put("servingCellNrcgi","1"); + return policyDataInMap; + } + + public String postPolicyBody(String nearRtRicId, String policyTypeName) throws JsonProcessingException { + PolicyObjectInformation policyObjectInfo = new PolicyObjectInformation(nearRtRicId, jsonString()); + policyObjectInfo.setPolicyTypeId(policyTypeName); + policyObjectInfo.setPolicyObject(dummyPolicyObject()); + return objectMapper.writeValueAsString(policyObjectInfo); + } + + public PolicyObjectInformation policyObjectInfo(String nearRtRicId, String policyTypeName) { + PolicyObjectInformation policyObjectInfo = new PolicyObjectInformation(nearRtRicId, jsonString()); + policyObjectInfo.setPolicyTypeId(policyTypeName); + policyObjectInfo.setPolicyObject(dummyPolicyObject()); + return policyObjectInfo; + } + + private Map<String,String> dummyPolicyObject() { + Map<String,String> policyDataInMap = new HashMap<>(); + policyDataInMap.put("servingCellNrcgi","1"); + return policyDataInMap; + } + + public String createServiceJson(String name, long keepAliveIntervalSeconds) throws JsonProcessingException { + String callbackUrl = baseUrl() + RappSimulatorController.SERVICE_CALLBACK_URL; + return createServiceJson(name, keepAliveIntervalSeconds, callbackUrl); + } + + public String createServiceJson(String name, long keepAliveIntervalSeconds, String url) throws JsonProcessingException { + org.onap.ccsdk.oran.a1policymanagementservice.models.v2.ServiceRegistrationInfo service = new org.onap.ccsdk.oran.a1policymanagementservice.models.v2.ServiceRegistrationInfo(name) + .keepAliveIntervalSeconds(keepAliveIntervalSeconds) + .callbackUrl(url); + + return objectMapper.writeValueAsString(service); + } + + public void testSuccessResponse(Mono<ResponseEntity<String>> responseEntityMono, HttpStatus httpStatusCode, + String responseContains) { + StepVerifier.create(responseEntityMono) + .expectNextMatches(responseEntity -> { + // Assert status code + HttpStatusCode status = responseEntity.getStatusCode(); + String res = responseEntity.getBody(); + assertThat(res).contains(responseContains); + return status.value() == httpStatusCode.value(); + }) + .expectComplete() + .verify(); + } + + public void testErrorCode(Mono<?> request, HttpStatus expStatus) { + testErrorCode(request, expStatus, "", true); + } + + public void testErrorCode(Mono<?> request, HttpStatus expStatus, boolean expectApplicationProblemJsonMediaType) { + testErrorCode(request, expStatus, "", expectApplicationProblemJsonMediaType); + } + + public void testErrorCode(Mono<?> request, HttpStatus expStatus, String responseContains) { + testErrorCode(request, expStatus, responseContains, true); + } + + public void testErrorCode(Mono<?> request, HttpStatus expStatus, String responseContains, + boolean expectApplicationProblemJsonMediaType) { + StepVerifier.create(request) + .expectSubscription() + .expectErrorMatches( + t -> checkWebClientError(t, expStatus, responseContains, expectApplicationProblemJsonMediaType)) + .verify(); + } + + private boolean checkWebClientError(Throwable throwable, HttpStatus expStatus, String responseContains, + boolean expectApplicationProblemJsonMediaType) { + assertTrue(throwable instanceof WebClientResponseException); + WebClientResponseException responseException = (WebClientResponseException) throwable; + String body = responseException.getResponseBodyAsString(); + assertThat(body).contains(responseContains); + assertThat(responseException.getStatusCode()).isEqualTo(expStatus); + + if (expectApplicationProblemJsonMediaType) { + assertThat(responseException.getHeaders().getContentType()).isEqualTo(MediaType.APPLICATION_PROBLEM_JSON); + } + return true; + } + + public void verifyMockError(Mono<?> responseMono, String responseCheck) { + StepVerifier.create(responseMono) + .expectSubscription() + .expectErrorMatches(response -> { + String status = response.getMessage(); + return status.contains(responseCheck); + }) + .verify(); + } +} |