2  * ========================LICENSE_START=================================
 
   4  * ======================================================================
 
   5  * Copyright (C) 2019-2020 Nordix Foundation. All rights reserved.
 
   6  * ======================================================================
 
   7  * Licensed under the Apache License, Version 2.0 (the "License");
 
   8  * you may not use this file except in compliance with the License.
 
   9  * You may obtain a copy of the License at
 
  11  *      http://www.apache.org/licenses/LICENSE-2.0
 
  13  * Unless required by applicable law or agreed to in writing, software
 
  14  * distributed under the License is distributed on an "AS IS" BASIS,
 
  15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 
  16  * See the License for the specific language governing permissions and
 
  17  * limitations under the License.
 
  18  * ========================LICENSE_END===================================
 
  21 package org.onap.ccsdk.oran.a1policymanagementservice.controllers.v2;
 
  23 import static org.assertj.core.api.Assertions.assertThat;
 
  24 import static org.awaitility.Awaitility.await;
 
  25 import static org.junit.jupiter.api.Assertions.assertEquals;
 
  26 import static org.junit.jupiter.api.Assertions.assertTrue;
 
  27 import static org.mockito.ArgumentMatchers.any;
 
  28 import static org.mockito.Mockito.doReturn;
 
  30 import com.google.gson.Gson;
 
  31 import com.google.gson.GsonBuilder;
 
  33 import java.io.FileOutputStream;
 
  34 import java.io.IOException;
 
  35 import java.io.PrintStream;
 
  36 import java.nio.charset.StandardCharsets;
 
  37 import java.nio.file.Files;
 
  38 import java.nio.file.Paths;
 
  39 import java.time.Duration;
 
  40 import java.time.Instant;
 
  41 import java.util.ArrayList;
 
  42 import java.util.Collections;
 
  43 import java.util.List;
 
  45 import org.json.JSONObject;
 
  46 import org.junit.jupiter.api.AfterEach;
 
  47 import org.junit.jupiter.api.Test;
 
  48 import org.junit.jupiter.api.extension.ExtendWith;
 
  49 import org.onap.ccsdk.oran.a1policymanagementservice.clients.A1ClientFactory;
 
  50 import org.onap.ccsdk.oran.a1policymanagementservice.clients.AsyncRestClient;
 
  51 import org.onap.ccsdk.oran.a1policymanagementservice.clients.AsyncRestClientFactory;
 
  52 import org.onap.ccsdk.oran.a1policymanagementservice.configuration.ApplicationConfig;
 
  53 import org.onap.ccsdk.oran.a1policymanagementservice.configuration.ApplicationConfig.RicConfigUpdate;
 
  54 import org.onap.ccsdk.oran.a1policymanagementservice.configuration.ImmutableRicConfig;
 
  55 import org.onap.ccsdk.oran.a1policymanagementservice.configuration.ImmutableWebClientConfig;
 
  56 import org.onap.ccsdk.oran.a1policymanagementservice.configuration.RicConfig;
 
  57 import org.onap.ccsdk.oran.a1policymanagementservice.configuration.WebClientConfig;
 
  58 import org.onap.ccsdk.oran.a1policymanagementservice.controllers.ServiceCallbackInfo;
 
  59 import org.onap.ccsdk.oran.a1policymanagementservice.exceptions.ServiceException;
 
  60 import org.onap.ccsdk.oran.a1policymanagementservice.repository.Lock.LockType;
 
  61 import org.onap.ccsdk.oran.a1policymanagementservice.repository.Policies;
 
  62 import org.onap.ccsdk.oran.a1policymanagementservice.repository.Policy;
 
  63 import org.onap.ccsdk.oran.a1policymanagementservice.repository.PolicyType;
 
  64 import org.onap.ccsdk.oran.a1policymanagementservice.repository.PolicyTypes;
 
  65 import org.onap.ccsdk.oran.a1policymanagementservice.repository.Ric;
 
  66 import org.onap.ccsdk.oran.a1policymanagementservice.repository.Ric.RicState;
 
  67 import org.onap.ccsdk.oran.a1policymanagementservice.repository.Rics;
 
  68 import org.onap.ccsdk.oran.a1policymanagementservice.repository.Service;
 
  69 import org.onap.ccsdk.oran.a1policymanagementservice.repository.Services;
 
  70 import org.onap.ccsdk.oran.a1policymanagementservice.tasks.RefreshConfigTask;
 
  71 import org.onap.ccsdk.oran.a1policymanagementservice.tasks.RicSupervision;
 
  72 import org.onap.ccsdk.oran.a1policymanagementservice.tasks.ServiceSupervision;
 
  73 import org.onap.ccsdk.oran.a1policymanagementservice.utils.MockA1Client;
 
  74 import org.onap.ccsdk.oran.a1policymanagementservice.utils.MockA1ClientFactory;
 
  75 import org.slf4j.Logger;
 
  76 import org.slf4j.LoggerFactory;
 
  77 import org.springframework.beans.factory.annotation.Autowired;
 
  78 import org.springframework.boot.test.context.SpringBootTest;
 
  79 import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
 
  80 import org.springframework.boot.test.context.TestConfiguration;
 
  81 import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
 
  82 import org.springframework.boot.web.server.LocalServerPort;
 
  83 import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
 
  84 import org.springframework.context.ApplicationContext;
 
  85 import org.springframework.context.annotation.Bean;
 
  86 import org.springframework.http.HttpStatus;
 
  87 import org.springframework.http.MediaType;
 
  88 import org.springframework.http.ResponseEntity;
 
  89 import org.springframework.test.context.TestPropertySource;
 
  90 import org.springframework.test.context.junit.jupiter.SpringExtension;
 
  91 import org.springframework.web.reactive.function.client.WebClientResponseException;
 
  93 import reactor.core.publisher.Mono;
 
  94 import reactor.test.StepVerifier;
 
  95 import reactor.util.annotation.Nullable;
 
  97 @ExtendWith(SpringExtension.class)
 
  98 @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
 
  99 @TestPropertySource(properties = { //
 
 100         "server.ssl.key-store=./config/keystore.jks", //
 
 101         "app.webclient.trust-store=./config/truststore.jks", //
 
 102         "app.vardata-directory=./target/testdata", //
 
 105 class ApplicationTest {
 
 106     private static final Logger logger = LoggerFactory.getLogger(ApplicationTest.class);
 
 109     ApplicationContext context;
 
 115     private Policies policies;
 
 118     private PolicyTypes policyTypes;
 
 121     MockA1ClientFactory a1ClientFactory;
 
 124     RicSupervision supervision;
 
 127     ApplicationConfig applicationConfig;
 
 133     RappSimulatorController rAppSimulator;
 
 136     RefreshConfigTask refreshConfigTask;
 
 138     private static Gson gson = new GsonBuilder().create();
 
 141      * Overrides the BeanFactory.
 
 144     static class TestBeanFactory {
 
 147         A1ClientFactory getA1ClientFactory(@Autowired ApplicationConfig appConfig, @Autowired PolicyTypes types) {
 
 148             return new MockA1ClientFactory(appConfig, types);
 
 152         public ServiceSupervision getServiceSupervision(@Autowired Services services,
 
 153                 @Autowired A1ClientFactory a1ClientFactory, @Autowired Policies policies) {
 
 154             Duration checkInterval = Duration.ofMillis(1);
 
 155             return new ServiceSupervision(services, policies, a1ClientFactory, checkInterval);
 
 159         public ServletWebServerFactory servletContainer() {
 
 160             return new TomcatServletWebServerFactory();
 
 173         a1ClientFactory.reset();
 
 174         this.rAppSimulator.getTestResults().clear();
 
 175         this.a1ClientFactory.setPolicyTypes(policyTypes); // Default same types in RIC and in this app
 
 179     void verifyNoRicLocks() {
 
 180         for (Ric ric : this.rics.getRics()) {
 
 181             ric.getLock().lockBlocking(LockType.EXCLUSIVE);
 
 182             ric.getLock().unlockBlocking();
 
 183             assertThat(ric.getLock().getLockCounter()).isZero();
 
 184             assertThat(ric.getState()).isEqualTo(Ric.RicState.AVAILABLE);
 
 189     void generateApiDoc() throws IOException {
 
 190         String url = "https://localhost:" + this.port + "/v3/api-docs";
 
 191         ResponseEntity<String> resp = restClient("", false).getForEntity(url).block();
 
 192         assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
 
 193         JSONObject jsonObj = new JSONObject(resp.getBody());
 
 194         assertThat(jsonObj.remove("servers")).isNotNull();
 
 196         String indented = (jsonObj).toString(4);
 
 197         String docDir = "api/";
 
 198         Files.createDirectories(Paths.get(docDir));
 
 199         try (PrintStream out = new PrintStream(new FileOutputStream(docDir + "pms-api.json"))) {
 
 205     void testPersistencyPolicies() throws ServiceException {
 
 206         Ric ric = this.addRic("ric1");
 
 207         PolicyType type = this.addPolicyType("type1", ric.id());
 
 209         final int noOfPolicies = 100;
 
 210         for (int i = 0; i < noOfPolicies; ++i) {
 
 211             addPolicy("id" + i, type.getId(), "service", ric.id());
 
 215             Policies policies = new Policies(this.applicationConfig);
 
 216             policies.restoreFromDatabase(ric, this.policyTypes);
 
 217             assertThat(policies.size()).isEqualTo(noOfPolicies);
 
 221             restClient().delete("/policies/id2").block();
 
 222             Policies policies = new Policies(this.applicationConfig);
 
 223             policies.restoreFromDatabase(ric, this.policyTypes);
 
 224             assertThat(policies.size()).isEqualTo(noOfPolicies - 1);
 
 229     void testPersistencyPolicyTypes() throws ServiceException {
 
 230         Ric ric = this.addRic("ric1");
 
 231         this.addPolicyType("type1", ric.id());
 
 232         PolicyTypes types = new PolicyTypes(this.applicationConfig);
 
 233         assertThat(types.size()).isEqualTo(1);
 
 237     void testPersistencyService() throws ServiceException {
 
 238         final String SERVICE = "serviceName";
 
 239         putService(SERVICE, 1234, HttpStatus.CREATED);
 
 240         assertThat(this.services.size()).isEqualTo(1);
 
 241         Service service = this.services.getService(SERVICE);
 
 243         Services servicesRestored = new Services(this.applicationConfig);
 
 244         Service serviceRestored = servicesRestored.getService(SERVICE);
 
 245         assertThat(servicesRestored.size()).isEqualTo(1);
 
 246         assertThat(serviceRestored.getCallbackUrl()).isEqualTo(service.getCallbackUrl());
 
 247         assertThat(serviceRestored.getKeepAliveInterval()).isEqualTo(service.getKeepAliveInterval());
 
 249         // check that the service can be deleted
 
 250         this.services.remove(SERVICE);
 
 251         servicesRestored = new Services(this.applicationConfig);
 
 252         assertThat(servicesRestored.size()).isZero();
 
 256     void testAddingRicFromConfiguration() throws Exception {
 
 257         // Test adding the RIC from configuration
 
 259         final String RIC = "ric1";
 
 260         final String TYPE = "type123";
 
 261         PolicyTypes nearRtRicPolicyTypes = new PolicyTypes(this.applicationConfig);
 
 262         nearRtRicPolicyTypes.put(createPolicyType(TYPE));
 
 263         this.a1ClientFactory.setPolicyTypes(nearRtRicPolicyTypes);
 
 265         putService("service");
 
 267         refreshConfigTask.handleUpdatedRicConfig( //
 
 268                 new RicConfigUpdate(ricConfig(RIC, "me1"), RicConfigUpdate.Type.ADDED)) //
 
 270         waitForRicState(RIC, RicState.AVAILABLE);
 
 272         // Test that the type has been synched
 
 273         Ric addedRic = this.rics.getRic(RIC);
 
 274         assertThat(addedRic.getSupportedPolicyTypes()).hasSize(1);
 
 275         assertThat(addedRic.getSupportedPolicyTypes().iterator().next().getId()).isEqualTo(TYPE);
 
 277         // Check that a service callback for the AVAILABLE RIC is invoked
 
 278         RappSimulatorController.TestResults receivedCallbacks = rAppSimulator.getTestResults();
 
 279         assertThat(receivedCallbacks.getReceivedInfo().size()).isEqualTo(1);
 
 280         ServiceCallbackInfo callbackInfo = receivedCallbacks.getReceivedInfo().get(0);
 
 281         assertThat(callbackInfo.ricId).isEqualTo(RIC);
 
 282         assertThat(callbackInfo.eventType).isEqualTo(ServiceCallbackInfo.EventType.AVAILABLE);
 
 286     void testAddingRicFromConfiguration_nonRespondingRic() throws Exception {
 
 287         putService("service");
 
 289         final String RIC = "NonRespondingRic";
 
 290         MockA1Client a1Client = a1ClientFactory.getOrCreateA1Client(RIC);
 
 291         doReturn(MockA1Client.monoError("error", HttpStatus.BAD_GATEWAY)).when(a1Client).getPolicyTypeIdentities();
 
 293         refreshConfigTask.handleUpdatedRicConfig( //
 
 294                 new RicConfigUpdate(ricConfig(RIC, "me1"), RicConfigUpdate.Type.ADDED)) //
 
 297         waitForRicState(RIC, RicState.UNAVAILABLE);
 
 299         // Check that no service callback for the UNAVAILABLE RIC is invoked
 
 300         RappSimulatorController.TestResults receivedCallbacks = rAppSimulator.getTestResults();
 
 301         assertThat(receivedCallbacks.getReceivedInfo()).isEmpty();
 
 303         // Run a synch and check that the AVAILABLE notification is received
 
 304         a1ClientFactory.reset();
 
 305         supervision.checkAllRics();
 
 306         waitForRicState(RIC, RicState.AVAILABLE);
 
 307         receivedCallbacks = rAppSimulator.getTestResults();
 
 308         assertThat(receivedCallbacks.getReceivedInfo().size()).isEqualTo(1);
 
 312     void testGetRics() throws Exception {
 
 314         this.addPolicyType("type1", "ric1");
 
 315         String url = "/rics?policytype_id=type1";
 
 316         String rsp = restClient().get(url).block();
 
 317         assertThat(rsp).contains("ric1");
 
 319         // nameless type for ORAN A1 1.1
 
 321         this.addPolicyType("", "ric2");
 
 322         url = "/rics?policytype_id=";
 
 324         // This tests also validation of trusted certs restClient(true)
 
 325         rsp = restClient(true).get(url).block();
 
 326         assertThat(rsp).contains("ric2") //
 
 327                 .doesNotContain("ric1") //
 
 328                 .contains("AVAILABLE");
 
 331         rsp = restClient().get("/rics").block();
 
 332         assertThat(rsp).contains("ric2") //
 
 335         // Non existing policy type
 
 336         url = "/rics?policytype_id=XXXX";
 
 337         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
 
 341     void testSynchronization() throws Exception {
 
 342         // Two polictypes will be put in the NearRT RICs
 
 343         PolicyTypes nearRtRicPolicyTypes = new PolicyTypes(this.applicationConfig);
 
 344         nearRtRicPolicyTypes.put(createPolicyType("typeName"));
 
 345         nearRtRicPolicyTypes.put(createPolicyType("typeName2"));
 
 346         this.a1ClientFactory.setPolicyTypes(nearRtRicPolicyTypes);
 
 348         // One type and one instance added to the Policy Management Service's storage
 
 349         final String ric1Name = "ric1";
 
 350         Ric ric1 = addRic(ric1Name);
 
 351         Policy policy2 = addPolicy("policyId2", "typeName", "service", ric1Name);
 
 352         Ric ric2 = addRic("ric2");
 
 354         getA1Client(ric1Name).putPolicy(policy2); // put it in the RIC (Near-RT RIC)
 
 355         policies.remove(policy2); // Remove it from the repo -> should be deleted in the RIC
 
 357         String policyId = "policyId";
 
 358         Policy policy = addPolicy(policyId, "typeName", "service", ric1Name); // This should be created in the RIC
 
 359         supervision.checkAllRics(); // The created policy should be put in the RIC
 
 361         // Wait until synch is completed
 
 362         waitForRicState(ric1Name, RicState.SYNCHRONIZING);
 
 363         waitForRicState(ric1Name, RicState.AVAILABLE);
 
 364         waitForRicState("ric2", RicState.AVAILABLE);
 
 366         Policies ricPolicies = getA1Client(ric1Name).getPolicies();
 
 367         assertThat(ricPolicies.size()).isEqualTo(1);
 
 368         Policy ricPolicy = ricPolicies.get(policyId);
 
 369         assertThat(ricPolicy.getJson()).isEqualTo(policy.getJson());
 
 371         // Both types should be in the Policy Management Service's storage after the
 
 373         assertThat(ric1.getSupportedPolicyTypes()).hasSize(2);
 
 374         assertThat(ric2.getSupportedPolicyTypes()).hasSize(2);
 
 378     void testGetRic() throws Exception {
 
 379         String ricId = "ric1";
 
 380         String managedElementId = "kista_1";
 
 381         addRic(ricId, managedElementId);
 
 383         String url = "/rics/ric?managed_element_id=" + managedElementId;
 
 384         String rsp = restClient().get(url).block();
 
 385         RicInfo ricInfo = gson.fromJson(rsp, RicInfo.class);
 
 386         assertThat(ricInfo.ricId).isEqualTo(ricId);
 
 388         url = "/rics/ric?ric_id=" + ricId;
 
 389         rsp = restClient().get(url).block();
 
 390         ricInfo = gson.fromJson(rsp, RicInfo.class);
 
 391         assertThat(ricInfo.ricId).isEqualTo(ricId);
 
 393         // test GET RIC for ManagedElement that does not exist
 
 394         url = "/rics/ric?managed_element_id=" + "junk";
 
 395         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
 
 398         testErrorCode(restClient().get(url), HttpStatus.BAD_REQUEST);
 
 401     private String putPolicyBody(String serviceName, String ricId, String policyTypeName, String policyInstanceId,
 
 402             boolean isTransient) {
 
 403         PolicyInfo info = new PolicyInfo();
 
 404         info.policyId = policyInstanceId;
 
 405         info.policyTypeId = policyTypeName;
 
 407         info.serviceId = serviceName;
 
 408         info.policyData = gson.fromJson(jsonString(), Object.class);
 
 411             info.isTransient = isTransient;
 
 413         info.statusNotificationUri = "statusNotificationUri";
 
 414         return gson.toJson(info);
 
 417     private String putPolicyBody(String serviceName, String ricId, String policyTypeName, String policyInstanceId) {
 
 418         return putPolicyBody(serviceName, ricId, policyTypeName, policyInstanceId, false);
 
 422     void testPutPolicy() throws Exception {
 
 423         String serviceName = "service.1";
 
 424         String ricId = "ric.1";
 
 425         String policyTypeName = "type1_1.2.3";
 
 426         String policyInstanceId = "instance_1.2.3";
 
 428         putService(serviceName);
 
 429         addPolicyType(policyTypeName, ricId);
 
 431         // PUT a transient policy
 
 432         String url = "/policies";
 
 433         String policyBody = putPolicyBody(serviceName, ricId, policyTypeName, policyInstanceId, true);
 
 434         this.rics.getRic(ricId).setState(Ric.RicState.AVAILABLE);
 
 436         restClient().put(url, policyBody).block();
 
 438         Policy policy = policies.getPolicy(policyInstanceId);
 
 439         assertThat(policy).isNotNull();
 
 440         assertThat(policy.getId()).isEqualTo(policyInstanceId);
 
 441         assertThat(policy.getOwnerServiceId()).isEqualTo(serviceName);
 
 442         assertThat(policy.getRic().id()).isEqualTo(ricId);
 
 443         assertThat(policy.isTransient()).isTrue();
 
 445         // Put a non transient policy
 
 446         policyBody = putPolicyBody(serviceName, ricId, policyTypeName, policyInstanceId);
 
 447         restClient().put(url, policyBody).block();
 
 448         policy = policies.getPolicy(policyInstanceId);
 
 449         assertThat(policy.isTransient()).isFalse();
 
 451         url = "/policy-instances";
 
 452         String rsp = restClient().get(url).block();
 
 453         assertThat(rsp).as("Response contains policy instance ID.").contains(policyInstanceId);
 
 455         url = "/policies/" + policyInstanceId;
 
 456         rsp = restClient().get(url).block();
 
 457         assertThat(rsp).contains(policyBody);
 
 459         // Test of error codes
 
 461         policyBody = putPolicyBody(serviceName, ricId + "XX", policyTypeName, policyInstanceId);
 
 462         testErrorCode(restClient().put(url, policyBody), HttpStatus.NOT_FOUND);
 
 464         policyBody = putPolicyBody(serviceName, ricId, policyTypeName + "XX", policyInstanceId);
 
 465         addPolicyType(policyTypeName + "XX", "otherRic");
 
 466         testErrorCode(restClient().put(url, policyBody), HttpStatus.NOT_FOUND);
 
 468         policyBody = putPolicyBody(serviceName, ricId, policyTypeName, policyInstanceId);
 
 469         this.rics.getRic(ricId).setState(Ric.RicState.SYNCHRONIZING);
 
 470         testErrorCode(restClient().put(url, policyBody), HttpStatus.LOCKED);
 
 471         this.rics.getRic(ricId).setState(Ric.RicState.AVAILABLE);
 
 476      * Test that HttpStatus and body from failing REST call to A1 is passed on to
 
 479      * @throws ServiceException
 
 481     void testErrorFromRic() throws ServiceException {
 
 482         putService("service1");
 
 483         addPolicyType("type1", "ric1");
 
 485         MockA1Client a1Client = a1ClientFactory.getOrCreateA1Client("ric1");
 
 486         HttpStatus httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;
 
 487         String responseBody = "Refused";
 
 488         byte[] responseBodyBytes = responseBody.getBytes(StandardCharsets.UTF_8);
 
 490         WebClientResponseException a1Exception = new WebClientResponseException(httpStatus.value(), "statusText", null,
 
 491                 responseBodyBytes, StandardCharsets.UTF_8, null);
 
 492         doReturn(Mono.error(a1Exception)).when(a1Client).putPolicy(any());
 
 495         String putBody = putPolicyBody("service1", "ric1", "type1", "id1");
 
 496         String url = "/policies";
 
 497         testErrorCode(restClient().put(url, putBody), httpStatus, responseBody);
 
 500         this.addPolicy("instance1", "type1", "service1", "ric1");
 
 501         doReturn(Mono.error(a1Exception)).when(a1Client).deletePolicy(any());
 
 502         testErrorCode(restClient().delete("/policies/instance1"), httpStatus, responseBody);
 
 507     void testPutTypelessPolicy() throws Exception {
 
 508         putService("service1");
 
 509         addPolicyType("", "ric1");
 
 510         String body = putPolicyBody("service1", "ric1", "", "id1");
 
 511         restClient().put("/policies", body).block();
 
 513         String rsp = restClient().get("/policy-instances").block();
 
 514         PolicyInfoList info = gson.fromJson(rsp, PolicyInfoList.class);
 
 515         assertThat(info.policies).hasSize(1);
 
 516         PolicyInfo policyInfo = info.policies.iterator().next();
 
 517         assertThat(policyInfo.policyId).isEqualTo("id1");
 
 518         assertThat(policyInfo.policyTypeId).isEmpty();
 
 522     void testRefuseToUpdatePolicy() throws Exception {
 
 523         // Test that only the json can be changed for a already created policy
 
 524         // In this case service is attempted to be changed
 
 526         this.addRic("ricXXX");
 
 527         this.addPolicy("instance1", "type1", "service1", "ric1");
 
 528         this.addPolicy("instance2", "type1", "service1", "ricXXX");
 
 530         // Try change ric1 -> ricXXX
 
 531         String bodyWrongRic = putPolicyBody("service1", "ricXXX", "type1", "instance1");
 
 532         testErrorCode(restClient().put("/policies", bodyWrongRic), HttpStatus.CONFLICT);
 
 536     void testGetPolicy() throws Exception {
 
 537         String url = "/policies/id";
 
 538         Policy policy = addPolicy("id", "typeName", "service1", "ric1");
 
 540             String rsp = restClient().get(url).block();
 
 541             PolicyInfo info = gson.fromJson(rsp, PolicyInfo.class);
 
 542             String policyStr = gson.toJson(info.policyData);
 
 543             assertThat(policyStr).isEqualTo(policy.getJson());
 
 546             policies.remove(policy);
 
 547             testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
 
 552     void testDeletePolicy() throws Exception {
 
 553         String policyId = "id.1";
 
 554         addPolicy(policyId, "typeName", "service1", "ric1");
 
 555         assertThat(policies.size()).isEqualTo(1);
 
 557         String url = "/policies/" + policyId;
 
 558         ResponseEntity<String> entity = restClient().deleteForEntity(url).block();
 
 560         assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.NO_CONTENT);
 
 561         assertThat(policies.size()).isZero();
 
 563         // Delete a non existing policy
 
 564         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
 
 568     void testGetPolicyType() throws Exception {
 
 569         String typeId = "AC.D";
 
 570         addPolicyType(typeId, "ric1");
 
 572         waitForRicState("ric1", RicState.AVAILABLE);
 
 574         String url = "/policy-types/" + typeId;
 
 576         String rsp = this.restClient().get(url).block();
 
 578         PolicyTypeInfo info = gson.fromJson(rsp, PolicyTypeInfo.class);
 
 579         assertThat(info.schema).isNotNull();
 
 581         // Get non existing schema
 
 582         url = "/policy-types/JUNK";
 
 583         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
 
 586     String createPolicyTypesJson(String... types) {
 
 587         List<String> list = new ArrayList<>();
 
 588         Collections.addAll(list, types);
 
 589         PolicyTypeIdList ids = new PolicyTypeIdList(list);
 
 590         return gson.toJson(ids);
 
 594     void testGetPolicyTypes() throws Exception {
 
 595         addPolicyType("type1", "ric1");
 
 596         addPolicyType("type2", "ric2");
 
 598         String url = "/policy-types";
 
 599         String rsp = restClient().get(url).block();
 
 600         String expResp = createPolicyTypesJson("type2", "type1");
 
 601         assertThat(rsp).isEqualTo(expResp);
 
 603         url = "/policy-types?ric_id=ric1";
 
 604         rsp = restClient().get(url).block();
 
 605         expResp = createPolicyTypesJson("type1");
 
 606         assertThat(rsp).isEqualTo(expResp);
 
 608         // Get policy types for non existing RIC
 
 609         url = "/policy-types?ric_id=ric1XXX";
 
 610         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
 
 614     void testGetPolicyInstances() throws Exception {
 
 615         addPolicy("id1", "type1", "service1");
 
 617         String url = "/policy-instances";
 
 618         String rsp = restClient().get(url).block();
 
 620         PolicyInfoList info = gson.fromJson(rsp, PolicyInfoList.class);
 
 621         assertThat(info.policies).hasSize(1);
 
 622         PolicyInfo policyInfo = info.policies.iterator().next();
 
 623         assert (policyInfo.validate());
 
 624         assertThat(policyInfo.policyId).isEqualTo("id1");
 
 625         assertThat(policyInfo.policyTypeId).isEqualTo("type1");
 
 626         assertThat(policyInfo.serviceId).isEqualTo("service1");
 
 630     void testGetPolicyInstancesFilter() throws Exception {
 
 631         addPolicy("id1", "type1", "service1");
 
 632         addPolicy("id2", "type1", "service2");
 
 633         addPolicy("id3", "type2", "service1");
 
 635         String url = "/policy-instances?policytype_id=type1";
 
 636         String rsp = restClient().get(url).block();
 
 638         assertThat(rsp).contains("id1") //
 
 640                 .doesNotContain("id3");
 
 642         url = "/policy-instances?policytype_id=type1&service_id=service2";
 
 643         rsp = restClient().get(url).block();
 
 645         assertThat(rsp).doesNotContain("id1") //
 
 647                 .doesNotContain("id3");
 
 649         // Test get policies for non existing type
 
 650         url = "/policy-instances?policytype_id=type1XXX";
 
 651         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
 
 653         // Test get policies for non existing RIC
 
 654         url = "/policy-instances?ric_id=XXX";
 
 655         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
 
 659     void testGetPolicyIdsFilter() throws Exception {
 
 660         addPolicy("id1", "type1", "service1", "ric1");
 
 661         addPolicy("id2", "type1", "service2", "ric1");
 
 662         addPolicy("id3", "type2", "service1", "ric1");
 
 664         String url = "/policies?policytype_id=type1";
 
 665         String rsp = restClient().get(url).block();
 
 667         assertThat(rsp).contains("id1") //
 
 669                 .doesNotContain("id3");
 
 671         url = "/policies?policytype_id=type1&service_id=service1&ric=ric1";
 
 672         rsp = restClient().get(url).block();
 
 673         PolicyIdList respList = gson.fromJson(rsp, PolicyIdList.class);
 
 674         assertThat(respList.policyIds.iterator().next()).isEqualTo("id1");
 
 676         // Test get policy ids for non existing type
 
 677         url = "/policies?policytype_id=type1XXX";
 
 678         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
 
 680         // Test get policy ids for non existing RIC
 
 681         url = "/policies?ric_id=XXX";
 
 682         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
 
 686     void testPutAndGetService() throws Exception {
 
 688         String serviceName = "ac.dc";
 
 689         putService(serviceName, 0, HttpStatus.CREATED);
 
 690         putService(serviceName, 0, HttpStatus.OK);
 
 693         String url = "/services?service_id=" + serviceName;
 
 694         String rsp = restClient().get(url).block();
 
 695         ServiceStatusList info = gson.fromJson(rsp, ServiceStatusList.class);
 
 696         assertThat(info.statusList).hasSize(1);
 
 697         ServiceStatus status = info.statusList.iterator().next();
 
 698         assertThat(status.keepAliveIntervalSeconds).isZero();
 
 699         assertThat(status.serviceId).isEqualTo(serviceName);
 
 703         rsp = restClient().get(url).block();
 
 704         assertThat(rsp).as("Response contains service name").contains(serviceName);
 
 708         url = "/services/" + serviceName + "/keepalive";
 
 709         ResponseEntity<?> entity = restClient().putForEntity(url).block();
 
 710         assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
 
 713         assertThat(services.size()).isEqualTo(1);
 
 714         url = "/services/" + serviceName;
 
 715         restClient().delete(url).block();
 
 716         assertThat(services.size()).isZero();
 
 718         // Keep alive, no registered service
 
 719         testErrorCode(restClient().put("/services/junk/keepalive", ""), HttpStatus.NOT_FOUND);
 
 721         // PUT service with bad payload
 
 722         testErrorCode(restClient().put("/services", "crap"), HttpStatus.BAD_REQUEST, false);
 
 723         testErrorCode(restClient().put("/services", "{}"), HttpStatus.BAD_REQUEST, false);
 
 724         testErrorCode(restClient().put("/services", createServiceJson(serviceName, -123)), HttpStatus.BAD_REQUEST,
 
 726         testErrorCode(restClient().put("/services", createServiceJson(serviceName, 0, "missing.portandprotocol.com")),
 
 727                 HttpStatus.BAD_REQUEST, false);
 
 729         // GET non existing service
 
 730         testErrorCode(restClient().get("/services?service_id=XXX"), HttpStatus.NOT_FOUND);
 
 734     void testServiceSupervision() throws Exception {
 
 735         putService("service1", 1, HttpStatus.CREATED);
 
 736         addPolicyType("type1", "ric1");
 
 738         String policyBody = putPolicyBody("service1", "ric1", "type1", "instance1");
 
 739         restClient().put("/policies", policyBody).block();
 
 741         assertThat(policies.size()).isEqualTo(1);
 
 742         assertThat(services.size()).isEqualTo(1);
 
 744         // Timeout after ~1 second
 
 745         await().untilAsserted(() -> assertThat(policies.size()).isZero());
 
 746         assertThat(services.size()).isZero();
 
 750     void testGetPolicyStatus() throws Exception {
 
 751         addPolicy("id", "typeName", "service1", "ric1");
 
 752         assertThat(policies.size()).isEqualTo(1);
 
 754         String url = "/policies/id/status";
 
 755         String rsp = restClient().get(url).block();
 
 756         PolicyStatusInfo info = gson.fromJson(rsp, PolicyStatusInfo.class);
 
 757         assertThat(info.status).isEqualTo("OK");
 
 759         // GET non existing policy status
 
 760         url = "/policies/XXX/status";
 
 761         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
 
 763         // GET STATUS, the NearRT RIC returns error
 
 764         MockA1Client a1Client = a1ClientFactory.getOrCreateA1Client("ric1");
 
 765         url = "/policies/id/status";
 
 766         WebClientResponseException a1Exception = new WebClientResponseException(404, "", null, null, null);
 
 767         doReturn(Mono.error(a1Exception)).when(a1Client).getPolicyStatus(any());
 
 768         rsp = restClient().get(url).block();
 
 769         info = gson.fromJson(rsp, PolicyStatusInfo.class);
 
 770         assertThat(info.status).hasToString("{}");
 
 774     void testServiceNotification() throws ServiceException {
 
 775         putService("junkService");
 
 776         Service junkService = this.services.get("junkService");
 
 777         junkService.setCallbackUrl("https://junk");
 
 778         putService("service");
 
 780         Ric ric = addRic("ric1");
 
 781         ric.setState(Ric.RicState.UNAVAILABLE);
 
 782         supervision.checkAllRics();
 
 783         waitForRicState("ric1", RicState.AVAILABLE);
 
 785         RappSimulatorController.TestResults receivedCallbacks = rAppSimulator.getTestResults();
 
 786         assertThat(receivedCallbacks.getReceivedInfo().size()).isEqualTo(1);
 
 787         ServiceCallbackInfo callbackInfo = receivedCallbacks.getReceivedInfo().get(0);
 
 788         assertThat(callbackInfo.ricId).isEqualTo("ric1");
 
 789         assertThat(callbackInfo.eventType).isEqualTo(ServiceCallbackInfo.EventType.AVAILABLE);
 
 792     private Policy addPolicy(String id, String typeName, String service, String ric) throws ServiceException {
 
 794         Policy policy = Policy.builder() //
 
 796                 .json(jsonString()) //
 
 797                 .ownerServiceId(service) //
 
 798                 .ric(rics.getRic(ric)) //
 
 799                 .type(addPolicyType(typeName, ric)) //
 
 800                 .lastModified(Instant.now()) //
 
 801                 .isTransient(false) //
 
 802                 .statusNotificationUri("/policy-status?id=XXX") //
 
 804         policies.put(policy);
 
 808     private Policy addPolicy(String id, String typeName, String service) throws ServiceException {
 
 809         return addPolicy(id, typeName, service, "ric");
 
 812     private String createServiceJson(String name, long keepAliveIntervalSeconds) {
 
 813         String callbackUrl = baseUrl() + RappSimulatorController.SERVICE_CALLBACK_URL;
 
 814         return createServiceJson(name, keepAliveIntervalSeconds, callbackUrl);
 
 817     private String createServiceJson(String name, long keepAliveIntervalSeconds, String url) {
 
 818         ServiceRegistrationInfo service = new ServiceRegistrationInfo(name, keepAliveIntervalSeconds, url);
 
 820         String json = gson.toJson(service);
 
 824     private void putService(String name) {
 
 825         putService(name, 0, null);
 
 828     private void putService(String name, long keepAliveIntervalSeconds, @Nullable HttpStatus expectedStatus) {
 
 829         String url = "/services";
 
 830         String body = createServiceJson(name, keepAliveIntervalSeconds);
 
 831         ResponseEntity<String> resp = restClient().putForEntity(url, body).block();
 
 832         if (expectedStatus != null) {
 
 833             assertEquals(expectedStatus, resp.getStatusCode(), "");
 
 837     private String jsonString() {
 
 838         return "{\"servingCellNrcgi\":\"1\"}";
 
 842     void testConcurrency() throws Exception {
 
 843         logger.info("Concurrency test starting");
 
 844         final Instant startTime = Instant.now();
 
 845         List<Thread> threads = new ArrayList<>();
 
 846         List<ConcurrencyTestRunnable> tests = new ArrayList<>();
 
 847         a1ClientFactory.setResponseDelay(Duration.ofMillis(1));
 
 849         addPolicyType("type1", "ric");
 
 850         addPolicyType("type2", "ric");
 
 852         for (int i = 0; i < 10; ++i) {
 
 853             AsyncRestClient restClient = restClient();
 
 854             ConcurrencyTestRunnable test =
 
 855                     new ConcurrencyTestRunnable(restClient, supervision, a1ClientFactory, rics, policyTypes);
 
 856             Thread thread = new Thread(test, "TestThread_" + i);
 
 861         for (Thread t : threads) {
 
 864         for (ConcurrencyTestRunnable test : tests) {
 
 865             assertThat(test.isFailed()).isFalse();
 
 867         assertThat(policies.size()).isZero();
 
 868         logger.info("Concurrency test took " + Duration.between(startTime, Instant.now()));
 
 871     private AsyncRestClient restClient(String baseUrl, boolean useTrustValidation) {
 
 872         WebClientConfig config = this.applicationConfig.getWebClientConfig();
 
 873         config = ImmutableWebClientConfig.builder() //
 
 874                 .keyStoreType(config.keyStoreType()) //
 
 875                 .keyStorePassword(config.keyStorePassword()) //
 
 876                 .keyStore(config.keyStore()) //
 
 877                 .keyPassword(config.keyPassword()) //
 
 878                 .isTrustStoreUsed(useTrustValidation) //
 
 879                 .trustStore(config.trustStore()) //
 
 880                 .trustStorePassword(config.trustStorePassword()) //
 
 881                 .httpProxyConfig(config.httpProxyConfig()) //
 
 884         AsyncRestClientFactory f = new AsyncRestClientFactory(config);
 
 885         return f.createRestClientNoHttpProxy(baseUrl);
 
 889     private String baseUrl() {
 
 890         return "https://localhost:" + port;
 
 893     private AsyncRestClient restClient(boolean useTrustValidation) {
 
 894         String baseUrl = "https://localhost:" + port + Consts.V2_API_ROOT;
 
 895         return restClient(baseUrl, useTrustValidation);
 
 898     private AsyncRestClient restClient() {
 
 899         return restClient(false);
 
 902     private void testErrorCode(Mono<?> request, HttpStatus expStatus) {
 
 903         testErrorCode(request, expStatus, "", true);
 
 906     private void testErrorCode(Mono<?> request, HttpStatus expStatus, boolean expectApplicationProblemJsonMediaType) {
 
 907         testErrorCode(request, expStatus, "", expectApplicationProblemJsonMediaType);
 
 910     private void testErrorCode(Mono<?> request, HttpStatus expStatus, String responseContains) {
 
 911         testErrorCode(request, expStatus, responseContains, true);
 
 914     private void testErrorCode(Mono<?> request, HttpStatus expStatus, String responseContains,
 
 915             boolean expectApplicationProblemJsonMediaType) {
 
 916         StepVerifier.create(request) //
 
 917                 .expectSubscription() //
 
 919                         t -> checkWebClientError(t, expStatus, responseContains, expectApplicationProblemJsonMediaType)) //
 
 923     private void waitForRicState(String ricId, RicState state) throws ServiceException {
 
 924         Ric ric = rics.getRic(ricId);
 
 925         await().untilAsserted(() -> state.equals(ric.getState()));
 
 928     private boolean checkWebClientError(Throwable throwable, HttpStatus expStatus, String responseContains,
 
 929             boolean expectApplicationProblemJsonMediaType) {
 
 930         assertTrue(throwable instanceof WebClientResponseException);
 
 931         WebClientResponseException responseException = (WebClientResponseException) throwable;
 
 932         assertThat(responseException.getStatusCode()).isEqualTo(expStatus);
 
 933         assertThat(responseException.getResponseBodyAsString()).contains(responseContains);
 
 934         if (expectApplicationProblemJsonMediaType) {
 
 935             assertThat(responseException.getHeaders().getContentType()).isEqualTo(MediaType.APPLICATION_PROBLEM_JSON);
 
 940     private MockA1Client getA1Client(String ricId) throws ServiceException {
 
 941         return a1ClientFactory.getOrCreateA1Client(ricId);
 
 944     private PolicyType createPolicyType(String policyTypeName) {
 
 945         return PolicyType.builder() //
 
 946                 .id(policyTypeName) //
 
 947                 .schema("{\"title\":\"" + policyTypeName + "\"}") //
 
 951     private PolicyType addPolicyType(String policyTypeName, String ricId) {
 
 952         PolicyType type = createPolicyType(policyTypeName);
 
 953         policyTypes.put(type);
 
 954         addRic(ricId).addSupportedPolicyType(type);
 
 958     private Ric addRic(String ricId) {
 
 959         return addRic(ricId, null);
 
 962     private RicConfig ricConfig(String ricId, String managedElement) {
 
 963         List<String> mes = new ArrayList<>();
 
 964         if (managedElement != null) {
 
 965             mes.add(managedElement);
 
 967         return ImmutableRicConfig.builder() //
 
 970                 .managedElementIds(mes) //
 
 971                 .controllerName("") //
 
 975     private Ric addRic(String ricId, String managedElement) {
 
 976         if (rics.get(ricId) != null) {
 
 977             return rics.get(ricId);
 
 980         RicConfig conf = ricConfig(ricId, managedElement);
 
 981         Ric ric = new Ric(conf);
 
 982         ric.setState(Ric.RicState.AVAILABLE);