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.ImmutableRicConfig;
 
  54 import org.onap.ccsdk.oran.a1policymanagementservice.configuration.ImmutableWebClientConfig;
 
  55 import org.onap.ccsdk.oran.a1policymanagementservice.configuration.RicConfig;
 
  56 import org.onap.ccsdk.oran.a1policymanagementservice.configuration.WebClientConfig;
 
  57 import org.onap.ccsdk.oran.a1policymanagementservice.controllers.ServiceCallbackInfo;
 
  58 import org.onap.ccsdk.oran.a1policymanagementservice.exceptions.ServiceException;
 
  59 import org.onap.ccsdk.oran.a1policymanagementservice.repository.Lock.LockType;
 
  60 import org.onap.ccsdk.oran.a1policymanagementservice.repository.Policies;
 
  61 import org.onap.ccsdk.oran.a1policymanagementservice.repository.Policy;
 
  62 import org.onap.ccsdk.oran.a1policymanagementservice.repository.PolicyType;
 
  63 import org.onap.ccsdk.oran.a1policymanagementservice.repository.PolicyTypes;
 
  64 import org.onap.ccsdk.oran.a1policymanagementservice.repository.Ric;
 
  65 import org.onap.ccsdk.oran.a1policymanagementservice.repository.Ric.RicState;
 
  66 import org.onap.ccsdk.oran.a1policymanagementservice.repository.Rics;
 
  67 import org.onap.ccsdk.oran.a1policymanagementservice.repository.Service;
 
  68 import org.onap.ccsdk.oran.a1policymanagementservice.repository.Services;
 
  69 import org.onap.ccsdk.oran.a1policymanagementservice.tasks.RefreshConfigTask;
 
  70 import org.onap.ccsdk.oran.a1policymanagementservice.tasks.RicSupervision;
 
  71 import org.onap.ccsdk.oran.a1policymanagementservice.tasks.ServiceSupervision;
 
  72 import org.onap.ccsdk.oran.a1policymanagementservice.utils.MockA1Client;
 
  73 import org.onap.ccsdk.oran.a1policymanagementservice.utils.MockA1ClientFactory;
 
  74 import org.slf4j.Logger;
 
  75 import org.slf4j.LoggerFactory;
 
  76 import org.springframework.beans.factory.annotation.Autowired;
 
  77 import org.springframework.boot.test.context.SpringBootTest;
 
  78 import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
 
  79 import org.springframework.boot.test.context.TestConfiguration;
 
  80 import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
 
  81 import org.springframework.boot.web.server.LocalServerPort;
 
  82 import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
 
  83 import org.springframework.context.ApplicationContext;
 
  84 import org.springframework.context.annotation.Bean;
 
  85 import org.springframework.http.HttpStatus;
 
  86 import org.springframework.http.MediaType;
 
  87 import org.springframework.http.ResponseEntity;
 
  88 import org.springframework.test.context.TestPropertySource;
 
  89 import org.springframework.test.context.junit.jupiter.SpringExtension;
 
  90 import org.springframework.web.reactive.function.client.WebClientResponseException;
 
  92 import reactor.core.publisher.Mono;
 
  93 import reactor.test.StepVerifier;
 
  94 import reactor.util.annotation.Nullable;
 
  96 @ExtendWith(SpringExtension.class)
 
  97 @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
 
  98 @TestPropertySource(properties = { //
 
  99         "server.ssl.key-store=./config/keystore.jks", //
 
 100         "app.webclient.trust-store=./config/truststore.jks", //
 
 101         "app.vardata-directory=./target/testdata", //
 
 104 class ApplicationTest {
 
 105     private static final Logger logger = LoggerFactory.getLogger(ApplicationTest.class);
 
 108     ApplicationContext context;
 
 114     private Policies policies;
 
 117     private PolicyTypes policyTypes;
 
 120     MockA1ClientFactory a1ClientFactory;
 
 123     RicSupervision supervision;
 
 126     ApplicationConfig applicationConfig;
 
 132     RappSimulatorController rAppSimulator;
 
 135     RefreshConfigTask refreshConfigTask;
 
 137     private static Gson gson = new GsonBuilder().create();
 
 140      * Overrides the BeanFactory.
 
 143     static class TestBeanFactory {
 
 146         A1ClientFactory getA1ClientFactory(@Autowired ApplicationConfig appConfig, @Autowired PolicyTypes types) {
 
 147             return new MockA1ClientFactory(appConfig, types);
 
 151         public ServiceSupervision getServiceSupervision(@Autowired Services services,
 
 152                 @Autowired A1ClientFactory a1ClientFactory, @Autowired Policies policies) {
 
 153             Duration checkInterval = Duration.ofMillis(1);
 
 154             return new ServiceSupervision(services, policies, a1ClientFactory, checkInterval);
 
 158         public ServletWebServerFactory servletContainer() {
 
 159             return new TomcatServletWebServerFactory();
 
 172         a1ClientFactory.reset();
 
 173         this.rAppSimulator.getTestResults().clear();
 
 177     void verifyNoRicLocks() {
 
 178         for (Ric ric : this.rics.getRics()) {
 
 179             ric.getLock().lockBlocking(LockType.EXCLUSIVE);
 
 180             ric.getLock().unlockBlocking();
 
 181             assertThat(ric.getLock().getLockCounter()).isZero();
 
 182             assertThat(ric.getState()).isEqualTo(Ric.RicState.AVAILABLE);
 
 187     void generateApiDoc() throws IOException {
 
 188         String url = "https://localhost:" + this.port + "/v3/api-docs";
 
 189         ResponseEntity<String> resp = restClient("", false).getForEntity(url).block();
 
 190         assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
 
 191         JSONObject jsonObj = new JSONObject(resp.getBody());
 
 192         assertThat(jsonObj.remove("servers")).isNotNull();
 
 194         String indented = (jsonObj).toString(4);
 
 195         String docDir = "api/";
 
 196         Files.createDirectories(Paths.get(docDir));
 
 197         try (PrintStream out = new PrintStream(new FileOutputStream(docDir + "pms-api.json"))) {
 
 203     void testPersistency() throws ServiceException {
 
 204         Ric ric = this.addRic("ric1");
 
 205         PolicyType type = this.addPolicyType("type1", ric.id());
 
 206         PolicyTypes types = new PolicyTypes(this.applicationConfig);
 
 207         assertThat(types.size()).isEqualTo(1);
 
 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, types);
 
 217             assertThat(policies.size()).isEqualTo(noOfPolicies);
 
 221             restClient().delete("/policies/id2").block();
 
 222             Policies policies = new Policies(this.applicationConfig);
 
 223             policies.restoreFromDatabase(ric, types);
 
 224             assertThat(policies.size()).isEqualTo(noOfPolicies - 1);
 
 228             // Test adding the RIC from configuration
 
 229             RicConfig config = ric.getConfig();
 
 230             this.rics.remove("ric1");
 
 231             ApplicationConfig.RicConfigUpdate update =
 
 232                     new ApplicationConfig.RicConfigUpdate(config, ApplicationConfig.RicConfigUpdate.Type.ADDED);
 
 233             refreshConfigTask.handleUpdatedRicConfig(update).block();
 
 234             ric = this.rics.getRic("ric1");
 
 235             assertThat(ric.getSupportedPolicyTypes().size()).isEqualTo(1);
 
 240     void testGetRics() throws Exception {
 
 242         this.addPolicyType("type1", "ric1");
 
 243         String url = "/rics?policytype_id=type1";
 
 244         String rsp = restClient().get(url).block();
 
 245         assertThat(rsp).contains("ric1");
 
 247         // nameless type for ORAN A1 1.1
 
 249         this.addPolicyType("", "ric2");
 
 250         url = "/rics?policytype_id=";
 
 252         // This tests also validation of trusted certs restClient(true)
 
 253         rsp = restClient(true).get(url).block();
 
 254         assertThat(rsp).contains("ric2") //
 
 255                 .doesNotContain("ric1") //
 
 256                 .contains("AVAILABLE");
 
 259         rsp = restClient().get("/rics").block();
 
 260         assertThat(rsp).contains("ric2") //
 
 263         // Non existing policy type
 
 264         url = "/rics?policytype_id=XXXX";
 
 265         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
 
 269     void testSynchronization() throws Exception {
 
 270         // Two polictypes will be put in the NearRT RICs
 
 271         PolicyTypes nearRtRicPolicyTypes = new PolicyTypes(this.applicationConfig);
 
 272         nearRtRicPolicyTypes.put(createPolicyType("typeName"));
 
 273         nearRtRicPolicyTypes.put(createPolicyType("typeName2"));
 
 274         this.a1ClientFactory.setPolicyTypes(nearRtRicPolicyTypes);
 
 276         // One type and one instance added to the Policy Management Service's storage
 
 277         final String ric1Name = "ric1";
 
 278         Ric ric1 = addRic(ric1Name);
 
 279         Policy policy2 = addPolicy("policyId2", "typeName", "service", ric1Name);
 
 280         Ric ric2 = addRic("ric2");
 
 282         getA1Client(ric1Name).putPolicy(policy2); // put it in the RIC (Near-RT RIC)
 
 283         policies.remove(policy2); // Remove it from the repo -> should be deleted in the RIC
 
 285         String policyId = "policyId";
 
 286         Policy policy = addPolicy(policyId, "typeName", "service", ric1Name); // This should be created in the RIC
 
 287         supervision.checkAllRics(); // The created policy should be put in the RIC
 
 289         // Wait until synch is completed
 
 290         waitForRicState(ric1Name, RicState.SYNCHRONIZING);
 
 291         waitForRicState(ric1Name, RicState.AVAILABLE);
 
 292         waitForRicState("ric2", RicState.AVAILABLE);
 
 294         Policies ricPolicies = getA1Client(ric1Name).getPolicies();
 
 295         assertThat(ricPolicies.size()).isEqualTo(1);
 
 296         Policy ricPolicy = ricPolicies.get(policyId);
 
 297         assertThat(ricPolicy.getJson()).isEqualTo(policy.getJson());
 
 299         // Both types should be in the Policy Management Service's storage after the
 
 301         assertThat(ric1.getSupportedPolicyTypes()).hasSize(2);
 
 302         assertThat(ric2.getSupportedPolicyTypes()).hasSize(2);
 
 306     void testGetRic() throws Exception {
 
 307         String ricId = "ric1";
 
 308         String managedElementId = "kista_1";
 
 309         addRic(ricId, managedElementId);
 
 311         String url = "/rics/ric?managed_element_id=" + managedElementId;
 
 312         String rsp = restClient().get(url).block();
 
 313         RicInfo ricInfo = gson.fromJson(rsp, RicInfo.class);
 
 314         assertThat(ricInfo.ricId).isEqualTo(ricId);
 
 316         url = "/rics/ric?ric_id=" + ricId;
 
 317         rsp = restClient().get(url).block();
 
 318         ricInfo = gson.fromJson(rsp, RicInfo.class);
 
 319         assertThat(ricInfo.ricId).isEqualTo(ricId);
 
 321         // test GET RIC for ManagedElement that does not exist
 
 322         url = "/rics/ric?managed_element_id=" + "junk";
 
 323         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
 
 326         testErrorCode(restClient().get(url), HttpStatus.BAD_REQUEST);
 
 329     private String putPolicyBody(String serviceName, String ricId, String policyTypeName, String policyInstanceId,
 
 330             boolean isTransient) {
 
 331         PolicyInfo info = new PolicyInfo();
 
 332         info.policyId = policyInstanceId;
 
 333         info.policyTypeId = policyTypeName;
 
 335         info.serviceId = serviceName;
 
 336         info.policyData = gson.fromJson(jsonString(), Object.class);
 
 339             info.isTransient = isTransient;
 
 341         info.statusNotificationUri = "statusNotificationUri";
 
 342         return gson.toJson(info);
 
 345     private String putPolicyBody(String serviceName, String ricId, String policyTypeName, String policyInstanceId) {
 
 346         return putPolicyBody(serviceName, ricId, policyTypeName, policyInstanceId, false);
 
 350     void testPutPolicy() throws Exception {
 
 351         String serviceName = "service.1";
 
 352         String ricId = "ric.1";
 
 353         String policyTypeName = "type1_1.2.3";
 
 354         String policyInstanceId = "instance_1.2.3";
 
 356         putService(serviceName);
 
 357         addPolicyType(policyTypeName, ricId);
 
 359         // PUT a transient policy
 
 360         String url = "/policies";
 
 361         String policyBody = putPolicyBody(serviceName, ricId, policyTypeName, policyInstanceId, true);
 
 362         this.rics.getRic(ricId).setState(Ric.RicState.AVAILABLE);
 
 364         restClient().put(url, policyBody).block();
 
 366         Policy policy = policies.getPolicy(policyInstanceId);
 
 367         assertThat(policy).isNotNull();
 
 368         assertThat(policy.getId()).isEqualTo(policyInstanceId);
 
 369         assertThat(policy.getOwnerServiceId()).isEqualTo(serviceName);
 
 370         assertThat(policy.getRic().id()).isEqualTo(ricId);
 
 371         assertThat(policy.isTransient()).isTrue();
 
 373         // Put a non transient policy
 
 374         policyBody = putPolicyBody(serviceName, ricId, policyTypeName, policyInstanceId);
 
 375         restClient().put(url, policyBody).block();
 
 376         policy = policies.getPolicy(policyInstanceId);
 
 377         assertThat(policy.isTransient()).isFalse();
 
 379         url = "/policy-instances";
 
 380         String rsp = restClient().get(url).block();
 
 381         assertThat(rsp).as("Response contains policy instance ID.").contains(policyInstanceId);
 
 383         url = "/policies/" + policyInstanceId;
 
 384         rsp = restClient().get(url).block();
 
 385         assertThat(rsp).contains(policyBody);
 
 387         // Test of error codes
 
 389         policyBody = putPolicyBody(serviceName, ricId + "XX", policyTypeName, policyInstanceId);
 
 390         testErrorCode(restClient().put(url, policyBody), HttpStatus.NOT_FOUND);
 
 392         policyBody = putPolicyBody(serviceName, ricId, policyTypeName + "XX", policyInstanceId);
 
 393         addPolicyType(policyTypeName + "XX", "otherRic");
 
 394         testErrorCode(restClient().put(url, policyBody), HttpStatus.NOT_FOUND);
 
 396         policyBody = putPolicyBody(serviceName, ricId, policyTypeName, policyInstanceId);
 
 397         this.rics.getRic(ricId).setState(Ric.RicState.SYNCHRONIZING);
 
 398         testErrorCode(restClient().put(url, policyBody), HttpStatus.LOCKED);
 
 399         this.rics.getRic(ricId).setState(Ric.RicState.AVAILABLE);
 
 404      * Test that HttpStatus and body from failing REST call to A1 is passed on to
 
 407      * @throws ServiceException
 
 409     void testErrorFromRic() throws ServiceException {
 
 410         putService("service1");
 
 411         addPolicyType("type1", "ric1");
 
 413         MockA1Client a1Client = a1ClientFactory.getOrCreateA1Client("ric1");
 
 414         HttpStatus httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;
 
 415         String responseBody = "Refused";
 
 416         byte[] responseBodyBytes = responseBody.getBytes(StandardCharsets.UTF_8);
 
 418         WebClientResponseException a1Exception = new WebClientResponseException(httpStatus.value(), "statusText", null,
 
 419                 responseBodyBytes, StandardCharsets.UTF_8, null);
 
 420         doReturn(Mono.error(a1Exception)).when(a1Client).putPolicy(any());
 
 423         String putBody = putPolicyBody("service1", "ric1", "type1", "id1");
 
 424         String url = "/policies";
 
 425         testErrorCode(restClient().put(url, putBody), httpStatus, responseBody);
 
 428         this.addPolicy("instance1", "type1", "service1", "ric1");
 
 429         doReturn(Mono.error(a1Exception)).when(a1Client).deletePolicy(any());
 
 430         testErrorCode(restClient().delete("/policies/instance1"), httpStatus, responseBody);
 
 435     void testPutTypelessPolicy() throws Exception {
 
 436         putService("service1");
 
 437         addPolicyType("", "ric1");
 
 438         String body = putPolicyBody("service1", "ric1", "", "id1");
 
 439         restClient().put("/policies", body).block();
 
 441         String rsp = restClient().get("/policy-instances").block();
 
 442         PolicyInfoList info = gson.fromJson(rsp, PolicyInfoList.class);
 
 443         assertThat(info.policies).hasSize(1);
 
 444         PolicyInfo policyInfo = info.policies.iterator().next();
 
 445         assertThat(policyInfo.policyId).isEqualTo("id1");
 
 446         assertThat(policyInfo.policyTypeId).isEmpty();
 
 450     void testRefuseToUpdatePolicy() throws Exception {
 
 451         // Test that only the json can be changed for a already created policy
 
 452         // In this case service is attempted to be changed
 
 454         this.addRic("ricXXX");
 
 455         this.addPolicy("instance1", "type1", "service1", "ric1");
 
 456         this.addPolicy("instance2", "type1", "service1", "ricXXX");
 
 458         // Try change ric1 -> ricXXX
 
 459         String bodyWrongRic = putPolicyBody("service1", "ricXXX", "type1", "instance1");
 
 460         testErrorCode(restClient().put("/policies", bodyWrongRic), HttpStatus.CONFLICT);
 
 464     void testGetPolicy() throws Exception {
 
 465         String url = "/policies/id";
 
 466         Policy policy = addPolicy("id", "typeName", "service1", "ric1");
 
 468             String rsp = restClient().get(url).block();
 
 469             PolicyInfo info = gson.fromJson(rsp, PolicyInfo.class);
 
 470             String policyStr = gson.toJson(info.policyData);
 
 471             assertThat(policyStr).isEqualTo(policy.getJson());
 
 474             policies.remove(policy);
 
 475             testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
 
 480     void testDeletePolicy() throws Exception {
 
 481         String policyId = "id.1";
 
 482         addPolicy(policyId, "typeName", "service1", "ric1");
 
 483         assertThat(policies.size()).isEqualTo(1);
 
 485         String url = "/policies/" + policyId;
 
 486         ResponseEntity<String> entity = restClient().deleteForEntity(url).block();
 
 488         assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.NO_CONTENT);
 
 489         assertThat(policies.size()).isZero();
 
 491         // Delete a non existing policy
 
 492         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
 
 496     void testGetPolicyType() throws Exception {
 
 497         String typeId = "AC.D";
 
 498         addPolicyType(typeId, "ric1");
 
 500         waitForRicState("ric1", RicState.AVAILABLE);
 
 502         String url = "/policy-types/" + typeId;
 
 504         String rsp = this.restClient().get(url).block();
 
 506         PolicyTypeInfo info = gson.fromJson(rsp, PolicyTypeInfo.class);
 
 507         assertThat(info.schema).isNotNull();
 
 509         // Get non existing schema
 
 510         url = "/policy-types/JUNK";
 
 511         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
 
 514     String createPolicyTypesJson(String... types) {
 
 515         List<String> list = new ArrayList<>();
 
 516         Collections.addAll(list, types);
 
 517         PolicyTypeIdList ids = new PolicyTypeIdList(list);
 
 518         return gson.toJson(ids);
 
 522     void testGetPolicyTypes() throws Exception {
 
 523         addPolicyType("type1", "ric1");
 
 524         addPolicyType("type2", "ric2");
 
 526         String url = "/policy-types";
 
 527         String rsp = restClient().get(url).block();
 
 528         String expResp = createPolicyTypesJson("type2", "type1");
 
 529         assertThat(rsp).isEqualTo(expResp);
 
 531         url = "/policy-types?ric_id=ric1";
 
 532         rsp = restClient().get(url).block();
 
 533         expResp = createPolicyTypesJson("type1");
 
 534         assertThat(rsp).isEqualTo(expResp);
 
 536         // Get policy types for non existing RIC
 
 537         url = "/policy-types?ric_id=ric1XXX";
 
 538         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
 
 542     void testGetPolicyInstances() throws Exception {
 
 543         addPolicy("id1", "type1", "service1");
 
 545         String url = "/policy-instances";
 
 546         String rsp = restClient().get(url).block();
 
 548         PolicyInfoList info = gson.fromJson(rsp, PolicyInfoList.class);
 
 549         assertThat(info.policies).hasSize(1);
 
 550         PolicyInfo policyInfo = info.policies.iterator().next();
 
 551         assert (policyInfo.validate());
 
 552         assertThat(policyInfo.policyId).isEqualTo("id1");
 
 553         assertThat(policyInfo.policyTypeId).isEqualTo("type1");
 
 554         assertThat(policyInfo.serviceId).isEqualTo("service1");
 
 558     void testGetPolicyInstancesFilter() throws Exception {
 
 559         addPolicy("id1", "type1", "service1");
 
 560         addPolicy("id2", "type1", "service2");
 
 561         addPolicy("id3", "type2", "service1");
 
 563         String url = "/policy-instances?policytype_id=type1";
 
 564         String rsp = restClient().get(url).block();
 
 566         assertThat(rsp).contains("id1") //
 
 568                 .doesNotContain("id3");
 
 570         url = "/policy-instances?policytype_id=type1&service_id=service2";
 
 571         rsp = restClient().get(url).block();
 
 573         assertThat(rsp).doesNotContain("id1") //
 
 575                 .doesNotContain("id3");
 
 577         // Test get policies for non existing type
 
 578         url = "/policy-instances?policytype_id=type1XXX";
 
 579         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
 
 581         // Test get policies for non existing RIC
 
 582         url = "/policy-instances?ric_id=XXX";
 
 583         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
 
 587     void testGetPolicyIdsFilter() throws Exception {
 
 588         addPolicy("id1", "type1", "service1", "ric1");
 
 589         addPolicy("id2", "type1", "service2", "ric1");
 
 590         addPolicy("id3", "type2", "service1", "ric1");
 
 592         String url = "/policies?policytype_id=type1";
 
 593         String rsp = restClient().get(url).block();
 
 595         assertThat(rsp).contains("id1") //
 
 597                 .doesNotContain("id3");
 
 599         url = "/policies?policytype_id=type1&service_id=service1&ric=ric1";
 
 600         rsp = restClient().get(url).block();
 
 601         PolicyIdList respList = gson.fromJson(rsp, PolicyIdList.class);
 
 602         assertThat(respList.policyIds.iterator().next()).isEqualTo("id1");
 
 604         // Test get policy ids for non existing type
 
 605         url = "/policies?policytype_id=type1XXX";
 
 606         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
 
 608         // Test get policy ids for non existing RIC
 
 609         url = "/policies?ric_id=XXX";
 
 610         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
 
 614     void testPutAndGetService() throws Exception {
 
 616         String serviceName = "ac.dc";
 
 617         putService(serviceName, 0, HttpStatus.CREATED);
 
 618         putService(serviceName, 0, HttpStatus.OK);
 
 621         String url = "/services?service_id=" + serviceName;
 
 622         String rsp = restClient().get(url).block();
 
 623         ServiceStatusList info = gson.fromJson(rsp, ServiceStatusList.class);
 
 624         assertThat(info.statusList).hasSize(1);
 
 625         ServiceStatus status = info.statusList.iterator().next();
 
 626         assertThat(status.keepAliveIntervalSeconds).isZero();
 
 627         assertThat(status.serviceId).isEqualTo(serviceName);
 
 631         rsp = restClient().get(url).block();
 
 632         assertThat(rsp).as("Response contains service name").contains(serviceName);
 
 636         url = "/services/" + serviceName + "/keepalive";
 
 637         ResponseEntity<?> entity = restClient().putForEntity(url).block();
 
 638         assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
 
 641         assertThat(services.size()).isEqualTo(1);
 
 642         url = "/services/" + serviceName;
 
 643         restClient().delete(url).block();
 
 644         assertThat(services.size()).isZero();
 
 646         // Keep alive, no registered service
 
 647         testErrorCode(restClient().put("/services/junk/keepalive", ""), HttpStatus.NOT_FOUND);
 
 649         // PUT service with bad payload
 
 650         testErrorCode(restClient().put("/services", "crap"), HttpStatus.BAD_REQUEST, false);
 
 651         testErrorCode(restClient().put("/services", "{}"), HttpStatus.BAD_REQUEST, false);
 
 652         testErrorCode(restClient().put("/services", createServiceJson(serviceName, -123)), HttpStatus.BAD_REQUEST,
 
 654         testErrorCode(restClient().put("/services", createServiceJson(serviceName, 0, "missing.portandprotocol.com")),
 
 655                 HttpStatus.BAD_REQUEST, false);
 
 657         // GET non existing service
 
 658         testErrorCode(restClient().get("/services?service_id=XXX"), HttpStatus.NOT_FOUND);
 
 662     void testServiceSupervision() throws Exception {
 
 663         putService("service1", 1, HttpStatus.CREATED);
 
 664         addPolicyType("type1", "ric1");
 
 666         String policyBody = putPolicyBody("service1", "ric1", "type1", "instance1");
 
 667         restClient().put("/policies", policyBody).block();
 
 669         assertThat(policies.size()).isEqualTo(1);
 
 670         assertThat(services.size()).isEqualTo(1);
 
 672         // Timeout after ~1 second
 
 673         await().untilAsserted(() -> assertThat(policies.size()).isZero());
 
 674         assertThat(services.size()).isZero();
 
 678     void testGetPolicyStatus() throws Exception {
 
 679         addPolicy("id", "typeName", "service1", "ric1");
 
 680         assertThat(policies.size()).isEqualTo(1);
 
 682         String url = "/policies/id/status";
 
 683         String rsp = restClient().get(url).block();
 
 684         PolicyStatusInfo info = gson.fromJson(rsp, PolicyStatusInfo.class);
 
 685         assertThat(info.status).isEqualTo("OK");
 
 687         // GET non existing policy status
 
 688         url = "/policies/XXX/status";
 
 689         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
 
 691         // GET STATUS, the NearRT RIC returns error
 
 692         MockA1Client a1Client = a1ClientFactory.getOrCreateA1Client("ric1");
 
 693         url = "/policies/id/status";
 
 694         WebClientResponseException a1Exception = new WebClientResponseException(404, "", null, null, null);
 
 695         doReturn(Mono.error(a1Exception)).when(a1Client).getPolicyStatus(any());
 
 696         rsp = restClient().get(url).block();
 
 697         info = gson.fromJson(rsp, PolicyStatusInfo.class);
 
 698         assertThat(info.status).hasToString("{}");
 
 702     void testServiceNotification() throws ServiceException {
 
 703         putService("junkService");
 
 704         Service junkService = this.services.get("junkService");
 
 705         junkService.setCallbackUrl("https://junk");
 
 706         putService("service");
 
 708         Ric ric = addRic("ric1");
 
 709         ric.setState(Ric.RicState.UNAVAILABLE);
 
 710         supervision.checkAllRics();
 
 711         waitForRicState("ric1", RicState.AVAILABLE);
 
 713         RappSimulatorController.TestResults receivedCallbacks = rAppSimulator.getTestResults();
 
 714         assertThat(receivedCallbacks.getReceivedInfo().size()).isEqualTo(1);
 
 715         ServiceCallbackInfo callbackInfo = receivedCallbacks.getReceivedInfo().get(0);
 
 716         assertThat(callbackInfo.ricId).isEqualTo("ric1");
 
 717         assertThat(callbackInfo.eventType).isEqualTo(ServiceCallbackInfo.EventType.AVAILABLE);
 
 720     private Policy addPolicy(String id, String typeName, String service, String ric) throws ServiceException {
 
 722         Policy policy = Policy.builder() //
 
 724                 .json(jsonString()) //
 
 725                 .ownerServiceId(service) //
 
 726                 .ric(rics.getRic(ric)) //
 
 727                 .type(addPolicyType(typeName, ric)) //
 
 728                 .lastModified(Instant.now()) //
 
 729                 .isTransient(false) //
 
 730                 .statusNotificationUri("/policy-status?id=XXX") //
 
 732         policies.put(policy);
 
 736     private Policy addPolicy(String id, String typeName, String service) throws ServiceException {
 
 737         return addPolicy(id, typeName, service, "ric");
 
 740     private String createServiceJson(String name, long keepAliveIntervalSeconds) {
 
 741         String callbackUrl = baseUrl() + RappSimulatorController.SERVICE_CALLBACK_URL;
 
 742         return createServiceJson(name, keepAliveIntervalSeconds, callbackUrl);
 
 745     private String createServiceJson(String name, long keepAliveIntervalSeconds, String url) {
 
 746         ServiceRegistrationInfo service = new ServiceRegistrationInfo(name, keepAliveIntervalSeconds, url);
 
 748         String json = gson.toJson(service);
 
 752     private void putService(String name) {
 
 753         putService(name, 0, null);
 
 756     private void putService(String name, long keepAliveIntervalSeconds, @Nullable HttpStatus expectedStatus) {
 
 757         String url = "/services";
 
 758         String body = createServiceJson(name, keepAliveIntervalSeconds);
 
 759         ResponseEntity<String> resp = restClient().putForEntity(url, body).block();
 
 760         if (expectedStatus != null) {
 
 761             assertEquals(expectedStatus, resp.getStatusCode(), "");
 
 765     private String jsonString() {
 
 766         return "{\"servingCellNrcgi\":\"1\"}";
 
 770     void testConcurrency() throws Exception {
 
 771         logger.info("Concurrency test starting");
 
 772         final Instant startTime = Instant.now();
 
 773         List<Thread> threads = new ArrayList<>();
 
 774         List<ConcurrencyTestRunnable> tests = new ArrayList<>();
 
 775         a1ClientFactory.setResponseDelay(Duration.ofMillis(1));
 
 777         addPolicyType("type1", "ric");
 
 778         addPolicyType("type2", "ric");
 
 780         for (int i = 0; i < 10; ++i) {
 
 781             AsyncRestClient restClient = restClient();
 
 782             ConcurrencyTestRunnable test =
 
 783                     new ConcurrencyTestRunnable(restClient, supervision, a1ClientFactory, rics, policyTypes);
 
 784             Thread thread = new Thread(test, "TestThread_" + i);
 
 789         for (Thread t : threads) {
 
 792         for (ConcurrencyTestRunnable test : tests) {
 
 793             assertThat(test.isFailed()).isFalse();
 
 795         assertThat(policies.size()).isZero();
 
 796         logger.info("Concurrency test took " + Duration.between(startTime, Instant.now()));
 
 799     private AsyncRestClient restClient(String baseUrl, boolean useTrustValidation) {
 
 800         WebClientConfig config = this.applicationConfig.getWebClientConfig();
 
 801         config = ImmutableWebClientConfig.builder() //
 
 802                 .keyStoreType(config.keyStoreType()) //
 
 803                 .keyStorePassword(config.keyStorePassword()) //
 
 804                 .keyStore(config.keyStore()) //
 
 805                 .keyPassword(config.keyPassword()) //
 
 806                 .isTrustStoreUsed(useTrustValidation) //
 
 807                 .trustStore(config.trustStore()) //
 
 808                 .trustStorePassword(config.trustStorePassword()) //
 
 809                 .httpProxyConfig(config.httpProxyConfig()) //
 
 812         AsyncRestClientFactory f = new AsyncRestClientFactory(config);
 
 813         return f.createRestClientNoHttpProxy(baseUrl);
 
 817     private String baseUrl() {
 
 818         return "https://localhost:" + port;
 
 821     private AsyncRestClient restClient(boolean useTrustValidation) {
 
 822         String baseUrl = "https://localhost:" + port + Consts.V2_API_ROOT;
 
 823         return restClient(baseUrl, useTrustValidation);
 
 826     private AsyncRestClient restClient() {
 
 827         return restClient(false);
 
 830     private void testErrorCode(Mono<?> request, HttpStatus expStatus) {
 
 831         testErrorCode(request, expStatus, "", true);
 
 834     private void testErrorCode(Mono<?> request, HttpStatus expStatus, boolean expectApplicationProblemJsonMediaType) {
 
 835         testErrorCode(request, expStatus, "", expectApplicationProblemJsonMediaType);
 
 838     private void testErrorCode(Mono<?> request, HttpStatus expStatus, String responseContains) {
 
 839         testErrorCode(request, expStatus, responseContains, true);
 
 842     private void testErrorCode(Mono<?> request, HttpStatus expStatus, String responseContains,
 
 843             boolean expectApplicationProblemJsonMediaType) {
 
 844         StepVerifier.create(request) //
 
 845                 .expectSubscription() //
 
 847                         t -> checkWebClientError(t, expStatus, responseContains, expectApplicationProblemJsonMediaType)) //
 
 851     private void waitForRicState(String ricId, RicState state) throws ServiceException {
 
 852         Ric ric = rics.getRic(ricId);
 
 853         await().untilAsserted(() -> state.equals(ric.getState()));
 
 856     private boolean checkWebClientError(Throwable throwable, HttpStatus expStatus, String responseContains,
 
 857             boolean expectApplicationProblemJsonMediaType) {
 
 858         assertTrue(throwable instanceof WebClientResponseException);
 
 859         WebClientResponseException responseException = (WebClientResponseException) throwable;
 
 860         assertThat(responseException.getStatusCode()).isEqualTo(expStatus);
 
 861         assertThat(responseException.getResponseBodyAsString()).contains(responseContains);
 
 862         if (expectApplicationProblemJsonMediaType) {
 
 863             assertThat(responseException.getHeaders().getContentType()).isEqualTo(MediaType.APPLICATION_PROBLEM_JSON);
 
 868     private MockA1Client getA1Client(String ricId) throws ServiceException {
 
 869         return a1ClientFactory.getOrCreateA1Client(ricId);
 
 872     private PolicyType createPolicyType(String policyTypeName) {
 
 873         return PolicyType.builder() //
 
 874                 .id(policyTypeName) //
 
 875                 .schema("{\"title\":\"" + policyTypeName + "\"}") //
 
 879     private PolicyType addPolicyType(String policyTypeName, String ricId) {
 
 880         PolicyType type = createPolicyType(policyTypeName);
 
 881         policyTypes.put(type);
 
 882         addRic(ricId).addSupportedPolicyType(type);
 
 886     private Ric addRic(String ricId) {
 
 887         return addRic(ricId, null);
 
 890     private Ric addRic(String ricId, String managedElement) {
 
 891         if (rics.get(ricId) != null) {
 
 892             return rics.get(ricId);
 
 894         List<String> mes = new ArrayList<>();
 
 895         if (managedElement != null) {
 
 896             mes.add(managedElement);
 
 898         RicConfig conf = ImmutableRicConfig.builder() //
 
 901                 .managedElementIds(mes) //
 
 902                 .controllerName("") //
 
 904         Ric ric = new Ric(conf);
 
 905         ric.setState(Ric.RicState.AVAILABLE);