c689097d328cd29db6574ba46514f40f8cefd839
[ccsdk/oran.git] /
1 /*-
2  * ========================LICENSE_START=================================
3  * ONAP : ccsdk oran
4  * ======================================================================
5  * Copyright (C) 2019-2022 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
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
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===================================
19  */
20
21 package org.onap.ccsdk.oran.a1policymanagementservice.controllers.v2;
22
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;
29
30 import com.google.gson.Gson;
31 import com.google.gson.GsonBuilder;
32
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.Path;
39 import java.nio.file.Paths;
40 import java.time.Duration;
41 import java.time.Instant;
42 import java.util.ArrayList;
43 import java.util.Collections;
44 import java.util.List;
45
46 import org.json.JSONObject;
47 import org.junit.jupiter.api.AfterEach;
48 import org.junit.jupiter.api.Test;
49 import org.junit.jupiter.api.extension.ExtendWith;
50 import org.onap.ccsdk.oran.a1policymanagementservice.clients.A1ClientFactory;
51 import org.onap.ccsdk.oran.a1policymanagementservice.clients.AsyncRestClient;
52 import org.onap.ccsdk.oran.a1policymanagementservice.clients.AsyncRestClientFactory;
53 import org.onap.ccsdk.oran.a1policymanagementservice.clients.SecurityContext;
54 import org.onap.ccsdk.oran.a1policymanagementservice.configuration.ApplicationConfig;
55 import org.onap.ccsdk.oran.a1policymanagementservice.configuration.ApplicationConfig.RicConfigUpdate;
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;
61 import org.onap.ccsdk.oran.a1policymanagementservice.repository.Lock.LockType;
62 import org.onap.ccsdk.oran.a1policymanagementservice.repository.Policies;
63 import org.onap.ccsdk.oran.a1policymanagementservice.repository.Policy;
64 import org.onap.ccsdk.oran.a1policymanagementservice.repository.PolicyType;
65 import org.onap.ccsdk.oran.a1policymanagementservice.repository.PolicyTypes;
66 import org.onap.ccsdk.oran.a1policymanagementservice.repository.Ric;
67 import org.onap.ccsdk.oran.a1policymanagementservice.repository.Ric.RicState;
68 import org.onap.ccsdk.oran.a1policymanagementservice.repository.Rics;
69 import org.onap.ccsdk.oran.a1policymanagementservice.repository.Service;
70 import org.onap.ccsdk.oran.a1policymanagementservice.repository.Services;
71 import org.onap.ccsdk.oran.a1policymanagementservice.tasks.RefreshConfigTask;
72 import org.onap.ccsdk.oran.a1policymanagementservice.tasks.RicSupervision;
73 import org.onap.ccsdk.oran.a1policymanagementservice.tasks.ServiceSupervision;
74 import org.onap.ccsdk.oran.a1policymanagementservice.utils.MockA1Client;
75 import org.onap.ccsdk.oran.a1policymanagementservice.utils.MockA1ClientFactory;
76 import org.slf4j.Logger;
77 import org.slf4j.LoggerFactory;
78 import org.springframework.beans.factory.annotation.Autowired;
79 import org.springframework.boot.test.context.SpringBootTest;
80 import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
81 import org.springframework.boot.test.context.TestConfiguration;
82 import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
83 import org.springframework.boot.web.server.LocalServerPort;
84 import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
85 import org.springframework.context.ApplicationContext;
86 import org.springframework.context.annotation.Bean;
87 import org.springframework.http.HttpStatus;
88 import org.springframework.http.MediaType;
89 import org.springframework.http.ResponseEntity;
90 import org.springframework.test.context.TestPropertySource;
91 import org.springframework.test.context.junit.jupiter.SpringExtension;
92 import org.springframework.web.reactive.function.client.WebClientResponseException;
93
94 import reactor.core.publisher.Mono;
95 import reactor.test.StepVerifier;
96 import reactor.util.annotation.Nullable;
97
98 @ExtendWith(SpringExtension.class)
99 @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
100 @TestPropertySource(properties = { //
101         "server.ssl.key-store=./src/test/resources/keystore.jks", //
102         "app.webclient.trust-store=./src/test/resources/truststore.jks", //
103         "app.webclient.trust-store-used=true", //
104         "app.vardata-directory=./target/testdata", //
105         "app.filepath=" //
106 })
107 class ApplicationTest {
108     private static final Logger logger = LoggerFactory.getLogger(ApplicationTest.class);
109
110     @Autowired
111     ApplicationContext context;
112
113     @Autowired
114     private Rics rics;
115
116     @Autowired
117     private Policies policies;
118
119     @Autowired
120     private PolicyTypes policyTypes;
121
122     @Autowired
123     MockA1ClientFactory a1ClientFactory;
124
125     @Autowired
126     RicSupervision supervision;
127
128     @Autowired
129     ApplicationConfig applicationConfig;
130
131     @Autowired
132     Services services;
133
134     @Autowired
135     RappSimulatorController rAppSimulator;
136
137     @Autowired
138     RefreshConfigTask refreshConfigTask;
139
140     @Autowired
141     SecurityContext securityContext;
142
143     private static Gson gson = new GsonBuilder().create();
144
145     /**
146      * Overrides the BeanFactory.
147      */
148     @TestConfiguration
149     static class TestBeanFactory {
150
151         @Bean
152         A1ClientFactory getA1ClientFactory(@Autowired ApplicationConfig appConfig, @Autowired PolicyTypes types) {
153             return new MockA1ClientFactory(appConfig, types);
154         }
155
156         @Bean
157         public ServiceSupervision getServiceSupervision(@Autowired Services services,
158                 @Autowired A1ClientFactory a1ClientFactory, @Autowired Policies policies) {
159             Duration checkInterval = Duration.ofMillis(1);
160             return new ServiceSupervision(services, policies, a1ClientFactory, checkInterval);
161         }
162
163         @Bean
164         public ServletWebServerFactory servletContainer() {
165             return new TomcatServletWebServerFactory();
166         }
167     }
168
169     @LocalServerPort
170     private int port;
171
172     @AfterEach
173     void reset() {
174         rics.clear();
175         policies.clear();
176         policyTypes.clear();
177         services.clear();
178         a1ClientFactory.reset();
179         this.rAppSimulator.getTestResults().clear();
180         this.a1ClientFactory.setPolicyTypes(policyTypes); // Default same types in RIC and in this app
181         this.securityContext.setAuthTokenFilePath(null);
182     }
183
184     @AfterEach
185     void verifyNoRicLocks() {
186         for (Ric ric : this.rics.getRics()) {
187             Lock.Grant grant = ric.getLock().lockBlocking(LockType.EXCLUSIVE, "");
188             grant.unlockBlocking();
189             assertThat(ric.getLock().getLockCounter()).isZero();
190             assertThat(ric.getState()).isEqualTo(Ric.RicState.AVAILABLE);
191         }
192     }
193
194     @Test
195     void generateApiDoc() throws IOException {
196         String url = "https://localhost:" + this.port + "/v3/api-docs";
197         ResponseEntity<String> resp = restClient("", false).getForEntity(url).block();
198         assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
199         JSONObject jsonObj = new JSONObject(resp.getBody());
200         assertThat(jsonObj.remove("servers")).isNotNull();
201
202         String indented = (jsonObj).toString(4);
203         String docDir = "api/";
204         Files.createDirectories(Paths.get(docDir));
205         try (PrintStream out = new PrintStream(new FileOutputStream(docDir + "pms-api.json"))) {
206             out.print(indented);
207         }
208     }
209
210     @Test
211     void testPersistencyPolicies() throws ServiceException {
212         Ric ric = this.addRic("ric1");
213         PolicyType type = this.addPolicyType("type1", ric.id());
214
215         final int noOfPolicies = 100;
216         for (int i = 0; i < noOfPolicies; ++i) {
217             addPolicy("id" + i, type.getId(), "service", ric.id());
218         }
219
220         {
221             Policies policies = new Policies(this.applicationConfig);
222             policies.restoreFromDatabase(ric, this.policyTypes);
223             assertThat(policies.size()).isEqualTo(noOfPolicies);
224         }
225
226         {
227             restClient().delete("/policies/id2").block();
228             Policies policies = new Policies(this.applicationConfig);
229             policies.restoreFromDatabase(ric, this.policyTypes);
230             assertThat(policies.size()).isEqualTo(noOfPolicies - 1);
231         }
232     }
233
234     @Test
235     void testPersistencyPolicyTypes() throws ServiceException {
236         Ric ric = this.addRic("ric1");
237         this.addPolicyType("type1", ric.id());
238         PolicyTypes types = new PolicyTypes(this.applicationConfig);
239         assertThat(types.size()).isEqualTo(1);
240     }
241
242     @Test
243     void testPersistencyService() throws ServiceException {
244         final String SERVICE = "serviceName";
245         putService(SERVICE, 1234, HttpStatus.CREATED);
246         assertThat(this.services.size()).isEqualTo(1);
247         Service service = this.services.getService(SERVICE);
248
249         Services servicesRestored = new Services(this.applicationConfig);
250         Service serviceRestored = servicesRestored.getService(SERVICE);
251         assertThat(servicesRestored.size()).isEqualTo(1);
252         assertThat(serviceRestored.getCallbackUrl()).isEqualTo(service.getCallbackUrl());
253         assertThat(serviceRestored.getKeepAliveInterval()).isEqualTo(service.getKeepAliveInterval());
254
255         // check that the service can be deleted
256         this.services.remove(SERVICE);
257         servicesRestored = new Services(this.applicationConfig);
258         assertThat(servicesRestored.size()).isZero();
259     }
260
261     @Test
262     void testAddingRicFromConfiguration() throws Exception {
263         // Test adding the RIC from configuration
264
265         final String RIC = "ric1";
266         final String TYPE = "type123";
267         PolicyTypes nearRtRicPolicyTypes = new PolicyTypes(this.applicationConfig);
268         nearRtRicPolicyTypes.put(createPolicyType(TYPE));
269         this.a1ClientFactory.setPolicyTypes(nearRtRicPolicyTypes);
270
271         putService("service");
272
273         refreshConfigTask.handleUpdatedRicConfig( //
274                 new RicConfigUpdate(ricConfig(RIC, "me1"), RicConfigUpdate.Type.ADDED)) //
275                 .block();
276         waitForRicState(RIC, RicState.AVAILABLE);
277
278         // Test that the type has been synched
279         Ric addedRic = this.rics.getRic(RIC);
280         assertThat(addedRic.getSupportedPolicyTypes()).hasSize(1);
281         assertThat(addedRic.getSupportedPolicyTypes().iterator().next().getId()).isEqualTo(TYPE);
282
283         // Check that a service callback for the AVAILABLE RIC is invoked
284         final RappSimulatorController.TestResults receivedCallbacks = rAppSimulator.getTestResults();
285         await().untilAsserted(() -> assertThat(receivedCallbacks.getReceivedInfo()).hasSize(1));
286         ServiceCallbackInfo callbackInfo = receivedCallbacks.getReceivedInfo().get(0);
287         assertThat(callbackInfo.ricId).isEqualTo(RIC);
288         assertThat(callbackInfo.eventType).isEqualTo(ServiceCallbackInfo.EventType.AVAILABLE);
289     }
290
291     @Test
292     void testAddingRicFromConfiguration_nonRespondingRic() throws Exception {
293         putService("service");
294
295         final String RIC = "NonRespondingRic";
296         MockA1Client a1Client = a1ClientFactory.getOrCreateA1Client(RIC);
297         doReturn(MockA1Client.monoError("error", HttpStatus.BAD_GATEWAY)).when(a1Client).getPolicyTypeIdentities();
298
299         refreshConfigTask.handleUpdatedRicConfig( //
300                 new RicConfigUpdate(ricConfig(RIC, "me1"), RicConfigUpdate.Type.ADDED)) //
301                 .block();
302
303         waitForRicState(RIC, RicState.UNAVAILABLE);
304
305         // Check that no service callback for the UNAVAILABLE RIC is invoked
306         final RappSimulatorController.TestResults receivedCallbacks = rAppSimulator.getTestResults();
307         await().untilAsserted(() -> assertThat(receivedCallbacks.getReceivedInfo()).isEmpty());
308
309         // Run a synch and check that the AVAILABLE notification is received
310         a1ClientFactory.reset();
311         supervision.checkAllRics();
312         waitForRicState(RIC, RicState.AVAILABLE);
313
314         await().untilAsserted(() -> assertThat(receivedCallbacks.getReceivedInfo()).hasSize(1));
315     }
316
317     @Test
318     void testTrustValidation() {
319         addRic("ric1");
320         String rsp = restClient(true).get("/rics").block(); // restClient(true) enables trust validation
321         assertThat(rsp).contains("ric1");
322     }
323
324     @Test
325     void testGetRics() throws Exception {
326         addRic("ric1");
327         this.addPolicyType("type1", "ric1");
328         String url = "/rics?policytype_id=type1";
329         String rsp = restClient().get(url).block();
330         assertThat(rsp).contains("ric1");
331
332         // nameless type for ORAN A1 1.1
333         addRic("ric2");
334         this.addPolicyType("", "ric2");
335         url = "/rics?policytype_id=";
336         rsp = restClient().get(url).block();
337         assertThat(rsp).contains("ric2") //
338                 .doesNotContain("ric1") //
339                 .contains("AVAILABLE");
340
341         // All RICs
342         rsp = restClient().get("/rics").block();
343         assertThat(rsp).contains("ric2") //
344                 .contains("ric1");
345
346         // Non existing policy type
347         url = "/rics?policytype_id=XXXX";
348         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
349     }
350
351     @Test
352     void testSynchronization() throws Exception {
353         // Two polictypes will be put in the NearRT RICs
354         PolicyTypes nearRtRicPolicyTypes = new PolicyTypes(this.applicationConfig);
355         nearRtRicPolicyTypes.put(createPolicyType("typeName"));
356         nearRtRicPolicyTypes.put(createPolicyType("typeName2"));
357         this.a1ClientFactory.setPolicyTypes(nearRtRicPolicyTypes);
358
359         // One type and one instance added to the Policy Management Service's storage
360         final String ric1Name = "ric1";
361         Ric ric1 = addRic(ric1Name);
362         Policy policy2 = addPolicy("policyId2", "typeName", "service", ric1Name);
363         Ric ric2 = addRic("ric2");
364
365         getA1Client(ric1Name).putPolicy(policy2); // put it in the RIC (Near-RT RIC)
366         policies.remove(policy2); // Remove it from the repo -> should be deleted in the RIC
367
368         String policyId = "policyId";
369         Policy policy = addPolicy(policyId, "typeName", "service", ric1Name); // This should be created in the RIC
370         supervision.checkAllRics(); // The created policy should be put in the RIC
371
372         // Wait until synch is completed
373         waitForRicState(ric1Name, RicState.SYNCHRONIZING);
374         waitForRicState(ric1Name, RicState.AVAILABLE);
375         waitForRicState("ric2", RicState.AVAILABLE);
376
377         Policies ricPolicies = getA1Client(ric1Name).getPolicies();
378         assertThat(ricPolicies.size()).isEqualTo(1);
379         Policy ricPolicy = ricPolicies.get(policyId);
380         assertThat(ricPolicy.getJson()).isEqualTo(policy.getJson());
381
382         // Both types should be in the Policy Management Service's storage after the
383         // synch
384         assertThat(ric1.getSupportedPolicyTypes()).hasSize(2);
385         assertThat(ric2.getSupportedPolicyTypes()).hasSize(2);
386     }
387
388     @Test
389     void testGetRic() throws Exception {
390         String ricId = "ric1";
391         String managedElementId = "kista_1";
392         addRic(ricId, managedElementId);
393
394         String url = "/rics/ric?managed_element_id=" + managedElementId;
395         String rsp = restClient().get(url).block();
396         RicInfo ricInfo = gson.fromJson(rsp, RicInfo.class);
397         assertThat(ricInfo.ricId).isEqualTo(ricId);
398
399         url = "/rics/ric?ric_id=" + ricId;
400         rsp = restClient().get(url).block();
401         ricInfo = gson.fromJson(rsp, RicInfo.class);
402         assertThat(ricInfo.ricId).isEqualTo(ricId);
403
404         // test GET RIC for ManagedElement that does not exist
405         url = "/rics/ric?managed_element_id=" + "junk";
406         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
407
408         url = "/rics/ric";
409         testErrorCode(restClient().get(url), HttpStatus.BAD_REQUEST);
410     }
411
412     private String putPolicyBody(String serviceName, String ricId, String policyTypeName, String policyInstanceId,
413             boolean isTransient) {
414         PolicyInfo info = new PolicyInfo();
415         info.policyId = policyInstanceId;
416         info.policyTypeId = policyTypeName;
417         info.ricId = ricId;
418         info.serviceId = serviceName;
419         info.policyData = gson.fromJson(jsonString(), Object.class);
420
421         if (isTransient) {
422             info.isTransient = isTransient;
423         }
424         info.statusNotificationUri = "statusNotificationUri";
425         return gson.toJson(info);
426     }
427
428     private String putPolicyBody(String serviceName, String ricId, String policyTypeName, String policyInstanceId) {
429         return putPolicyBody(serviceName, ricId, policyTypeName, policyInstanceId, false);
430     }
431
432     @Test
433     void testPutPolicy() throws Exception {
434         String serviceName = "service.1";
435         String ricId = "ric.1";
436         String policyTypeName = "type1_1.2.3";
437         String policyInstanceId = "instance_1.2.3";
438
439         putService(serviceName);
440         addPolicyType(policyTypeName, ricId);
441
442         // PUT a transient policy
443         String url = "/policies";
444         String policyBody = putPolicyBody(serviceName, ricId, policyTypeName, policyInstanceId, true);
445         this.rics.getRic(ricId).setState(Ric.RicState.AVAILABLE);
446
447         restClient().put(url, policyBody).block();
448
449         Policy policy = policies.getPolicy(policyInstanceId);
450         assertThat(policy).isNotNull();
451         assertThat(policy.getId()).isEqualTo(policyInstanceId);
452         assertThat(policy.getOwnerServiceId()).isEqualTo(serviceName);
453         assertThat(policy.getRic().id()).isEqualTo(ricId);
454         assertThat(policy.isTransient()).isTrue();
455
456         // Put a non transient policy
457         policyBody = putPolicyBody(serviceName, ricId, policyTypeName, policyInstanceId);
458         restClient().put(url, policyBody).block();
459         policy = policies.getPolicy(policyInstanceId);
460         assertThat(policy.isTransient()).isFalse();
461
462         url = "/policy-instances";
463         String rsp = restClient().get(url).block();
464         assertThat(rsp).as("Response contains policy instance ID.").contains(policyInstanceId);
465
466         url = "/policies/" + policyInstanceId;
467         rsp = restClient().get(url).block();
468         assertThat(rsp).contains(policyBody);
469
470         // Test of error codes
471         url = "/policies";
472         policyBody = putPolicyBody(serviceName, ricId + "XX", policyTypeName, policyInstanceId);
473         testErrorCode(restClient().put(url, policyBody), HttpStatus.NOT_FOUND);
474
475         policyBody = putPolicyBody(serviceName, ricId, policyTypeName + "XX", policyInstanceId);
476         addPolicyType(policyTypeName + "XX", "otherRic");
477         testErrorCode(restClient().put(url, policyBody), HttpStatus.NOT_FOUND);
478
479         policyBody = putPolicyBody(serviceName, ricId, policyTypeName, policyInstanceId);
480         this.rics.getRic(ricId).setState(Ric.RicState.SYNCHRONIZING);
481         testErrorCode(restClient().put(url, policyBody), HttpStatus.LOCKED);
482         this.rics.getRic(ricId).setState(Ric.RicState.AVAILABLE);
483     }
484
485     @Test
486     /**
487      * Test that HttpStatus and body from failing REST call to A1 is passed on to
488      * the caller.
489      *
490      * @throws ServiceException
491      */
492     void testErrorFromRic() throws ServiceException {
493         putService("service1");
494         addPolicyType("type1", "ric1");
495
496         MockA1Client a1Client = a1ClientFactory.getOrCreateA1Client("ric1");
497         HttpStatus httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;
498         String responseBody = "Refused";
499         byte[] responseBodyBytes = responseBody.getBytes(StandardCharsets.UTF_8);
500
501         WebClientResponseException a1Exception = new WebClientResponseException(httpStatus.value(), "statusText", null,
502                 responseBodyBytes, StandardCharsets.UTF_8, null);
503         doReturn(Mono.error(a1Exception)).when(a1Client).putPolicy(any());
504
505         // PUT Policy
506         String putBody = putPolicyBody("service1", "ric1", "type1", "id1");
507         String url = "/policies";
508         testErrorCode(restClient().put(url, putBody), httpStatus, responseBody);
509
510         // DELETE POLICY
511         this.addPolicy("instance1", "type1", "service1", "ric1");
512         doReturn(Mono.error(a1Exception)).when(a1Client).deletePolicy(any());
513         testErrorCode(restClient().delete("/policies/instance1"), httpStatus, responseBody);
514
515     }
516
517     @Test
518     void testPutTypelessPolicy() throws Exception {
519         putService("service1");
520         addPolicyType("", "ric1");
521         String body = putPolicyBody("service1", "ric1", "", "id1");
522         restClient().put("/policies", body).block();
523
524         String rsp = restClient().get("/policy-instances").block();
525         PolicyInfoList info = gson.fromJson(rsp, PolicyInfoList.class);
526         assertThat(info.policies).hasSize(1);
527         PolicyInfo policyInfo = info.policies.iterator().next();
528         assertThat(policyInfo.policyId).isEqualTo("id1");
529         assertThat(policyInfo.policyTypeId).isEmpty();
530     }
531
532     @Test
533     void testRefuseToUpdatePolicy() throws Exception {
534         // Test that only the json can be changed for a already created policy
535         // In this case service is attempted to be changed
536         this.addRic("ric1");
537         this.addRic("ricXXX");
538         this.addPolicy("instance1", "type1", "service1", "ric1");
539         this.addPolicy("instance2", "type1", "service1", "ricXXX");
540
541         // Try change ric1 -> ricXXX
542         String bodyWrongRic = putPolicyBody("service1", "ricXXX", "type1", "instance1");
543         testErrorCode(restClient().put("/policies", bodyWrongRic), HttpStatus.CONFLICT);
544     }
545
546     @Test
547     void testGetPolicy() throws Exception {
548         String url = "/policies/id";
549         Policy policy = addPolicy("id", "typeName", "service1", "ric1");
550         {
551             String rsp = restClient().get(url).block();
552             PolicyInfo info = gson.fromJson(rsp, PolicyInfo.class);
553             String policyStr = gson.toJson(info.policyData);
554             assertThat(policyStr).isEqualTo(policy.getJson());
555         }
556         {
557             policies.remove(policy);
558             testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
559         }
560     }
561
562     @Test
563     void testDeletePolicy() throws Exception {
564         String policyId = "id.1";
565         addPolicy(policyId, "typeName", "service1", "ric1");
566         assertThat(policies.size()).isEqualTo(1);
567
568         String url = "/policies/" + policyId;
569         ResponseEntity<String> entity = restClient().deleteForEntity(url).block();
570
571         assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.NO_CONTENT);
572         assertThat(policies.size()).isZero();
573
574         // Delete a non existing policy
575         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
576     }
577
578     @Test
579     void testGetPolicyType() throws Exception {
580         String typeId = "AC.D";
581         addPolicyType(typeId, "ric1");
582
583         waitForRicState("ric1", RicState.AVAILABLE);
584
585         String url = "/policy-types/" + typeId;
586
587         String rsp = this.restClient().get(url).block();
588
589         PolicyTypeInfo info = gson.fromJson(rsp, PolicyTypeInfo.class);
590         assertThat(info.schema).isNotNull();
591
592         // Get non existing schema
593         url = "/policy-types/JUNK";
594         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
595     }
596
597     String createPolicyTypesJson(String... types) {
598         List<String> list = new ArrayList<>();
599         Collections.addAll(list, types);
600         PolicyTypeIdList ids = new PolicyTypeIdList(list);
601         return gson.toJson(ids);
602     }
603
604     @Test
605     void testGetPolicyTypes() throws Exception {
606         String TYPE_ID_1 = "A_type1_1.9.0";
607         String TYPE_ID_2 = "A_type1_2.0.0";
608         String TYPE_ID_3 = "A_type1_1.5.0";
609         String TYPE_ID_4 = "type3_1.9.0";
610         addPolicyType(TYPE_ID_1, "ric1");
611         addPolicyType(TYPE_ID_2, "ric2");
612         addPolicyType(TYPE_ID_3, "ric2");
613         addPolicyType(TYPE_ID_4, "ric2");
614
615         addPolicyType("junk", "ric2");
616         addPolicyType("junk_a.b.c", "ric2");
617
618         String url = "/policy-types";
619         String rsp = restClient().get(url).block();
620         assertThat(rsp).contains(TYPE_ID_1, TYPE_ID_2);
621
622         url = "/policy-types?ric_id=ric1";
623         rsp = restClient().get(url).block();
624         String expResp = createPolicyTypesJson(TYPE_ID_1);
625         assertThat(rsp).isEqualTo(expResp);
626
627         // Get policy types for non existing RIC
628         url = "/policy-types?ric_id=ric1XXX";
629         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
630
631         // All types with a type_name
632         url = "/policy-types?type_name=A_type1";
633         rsp = restClient().get(url).block();
634         assertThat(rsp).contains(TYPE_ID_1, TYPE_ID_2);
635
636         // All types compatible with type1_1.5.0 (which is type1_1.9.0)
637         url = "/policy-types?type_name=A_type1&&compatible_with_version=1.5.0";
638         rsp = restClient().get(url).block();
639         expResp = createPolicyTypesJson(TYPE_ID_3, TYPE_ID_1);
640         assertThat(rsp).isEqualTo(expResp);
641
642         url = "/policy-types?type_name=A_type1&&compatible_with_version=junk";
643         testErrorCode(restClient().get(url), HttpStatus.BAD_REQUEST, "Version must contain major.minor.patch code");
644
645         url = "/policy-types?type_name=A_type1&&compatible_with_version=a.b.c";
646         testErrorCode(restClient().get(url), HttpStatus.BAD_REQUEST, "Syntax error in");
647
648         url = "/policy-types?compatible_with_version=1.5.0";
649         testErrorCode(restClient().get(url), HttpStatus.BAD_REQUEST, "type_name");
650     }
651
652     @Test
653     void testGetPolicyInstances() throws Exception {
654         addPolicy("id1", "type1", "service1");
655
656         String url = "/policy-instances";
657         String rsp = restClient().get(url).block();
658         logger.info(rsp);
659         PolicyInfoList info = gson.fromJson(rsp, PolicyInfoList.class);
660         assertThat(info.policies).hasSize(1);
661         PolicyInfo policyInfo = info.policies.iterator().next();
662         assert (policyInfo.validate());
663         assertThat(policyInfo.policyId).isEqualTo("id1");
664         assertThat(policyInfo.policyTypeId).isEqualTo("type1");
665         assertThat(policyInfo.serviceId).isEqualTo("service1");
666     }
667
668     @Test
669     void testGetPolicyInstancesFilter() throws Exception {
670         addPolicy("id1", "type1", "service1");
671         addPolicy("id2", "type1", "service2");
672         addPolicy("id3", "type2", "service1");
673         addPolicy("id4", "type1_1.0.0", "service1");
674
675         String url = "/policy-instances?policytype_id=type1";
676         String rsp = restClient().get(url).block();
677         logger.info(rsp);
678         assertThat(rsp).contains("id1") //
679                 .contains("id2") //
680                 .doesNotContain("id3");
681
682         url = "/policy-instances?policytype_id=type1&service_id=service2";
683         rsp = restClient().get(url).block();
684         logger.info(rsp);
685         assertThat(rsp).doesNotContain("id1") //
686                 .contains("id2") //
687                 .doesNotContain("id3");
688
689         url = "/policy-instances?type_name=type1";
690         rsp = restClient().get(url).block();
691         assertThat(rsp).contains("id1") //
692                 .contains("id2") //
693                 .doesNotContain("id3") //
694                 .contains("id4");
695
696         // Test get policies for non existing type
697         url = "/policy-instances?policytype_id=type1XXX";
698         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
699
700         // Test get policies for non existing RIC
701         url = "/policy-instances?ric_id=XXX";
702         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
703     }
704
705     @Test
706     void testGetPolicyIdsFilter() throws Exception {
707         addPolicy("id1", "type1", "service1", "ric1");
708         addPolicy("id2", "type1", "service2", "ric1");
709         addPolicy("id3", "type2", "service1", "ric1");
710         addPolicy("id4", "type1_1.0.0", "service1");
711
712         String url = "/policies?policytype_id=type1";
713         String rsp = restClient().get(url).block();
714         logger.info(rsp);
715         assertThat(rsp).contains("id1") //
716                 .contains("id2") //
717                 .doesNotContain("id3");
718
719         url = "/policies?policytype_id=type1&service_id=service1&ric=ric1";
720         rsp = restClient().get(url).block();
721         PolicyIdList respList = gson.fromJson(rsp, PolicyIdList.class);
722         assertThat(respList.policyIds.iterator().next()).isEqualTo("id1");
723
724         url = "/policies?policytype_name=type1&service_id=service1";
725         rsp = restClient().get(url).block();
726         assertThat(rsp).contains("id1") //
727                 .contains("id3") //
728                 .contains("id4");
729         assertThat(gson.fromJson(rsp, PolicyIdList.class).policyIds).hasSize(3);
730
731         // Test get policy ids for non existing type
732         url = "/policies?policytype_id=type1XXX";
733         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
734
735         // Test get policy ids for non existing RIC
736         url = "/policies?ric_id=XXX";
737         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
738     }
739
740     @Test
741     void testPutAndGetService() throws Exception {
742         // PUT
743         String serviceName = "ac.dc";
744         putService(serviceName, 0, HttpStatus.CREATED);
745         putService(serviceName, 0, HttpStatus.OK);
746
747         // GET one service
748         String url = "/services?service_id=" + serviceName;
749         String rsp = restClient().get(url).block();
750         ServiceStatusList info = gson.fromJson(rsp, ServiceStatusList.class);
751         assertThat(info.statusList).hasSize(1);
752         ServiceStatus status = info.statusList.iterator().next();
753         assertThat(status.keepAliveIntervalSeconds).isZero();
754         assertThat(status.serviceId).isEqualTo(serviceName);
755
756         // GET (all)
757         url = "/services";
758         rsp = restClient().get(url).block();
759         assertThat(rsp).as("Response contains service name").contains(serviceName);
760         logger.info(rsp);
761
762         // Keep alive
763         url = "/services/" + serviceName + "/keepalive";
764         ResponseEntity<?> entity = restClient().putForEntity(url).block();
765         assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
766
767         // DELETE service
768         assertThat(services.size()).isEqualTo(1);
769         url = "/services/" + serviceName;
770         restClient().delete(url).block();
771         assertThat(services.size()).isZero();
772
773         // Keep alive, no registered service
774         testErrorCode(restClient().put("/services/junk/keepalive", ""), HttpStatus.NOT_FOUND);
775
776         // PUT service with bad payload
777         testErrorCode(restClient().put("/services", "crap"), HttpStatus.BAD_REQUEST, false);
778         testErrorCode(restClient().put("/services", "{}"), HttpStatus.BAD_REQUEST, false);
779         testErrorCode(restClient().put("/services", createServiceJson(serviceName, -123)), HttpStatus.BAD_REQUEST,
780                 false);
781         testErrorCode(restClient().put("/services", createServiceJson(serviceName, 0, "missing.portandprotocol.com")),
782                 HttpStatus.BAD_REQUEST, false);
783
784         // GET non existing service
785         testErrorCode(restClient().get("/services?service_id=XXX"), HttpStatus.NOT_FOUND);
786     }
787
788     @Test
789     void testServiceSupervision() throws Exception {
790         putService("service1", 1, HttpStatus.CREATED);
791         addPolicyType("type1", "ric1");
792
793         String policyBody = putPolicyBody("service1", "ric1", "type1", "instance1");
794         restClient().put("/policies", policyBody).block();
795
796         assertThat(policies.size()).isEqualTo(1);
797         assertThat(services.size()).isEqualTo(1);
798
799         // Timeout after ~1 second
800         await().untilAsserted(() -> assertThat(policies.size()).isZero());
801         assertThat(services.size()).isZero();
802     }
803
804     @Test
805     void testGetPolicyStatus() throws Exception {
806         addPolicy("id", "typeName", "service1", "ric1");
807         assertThat(policies.size()).isEqualTo(1);
808
809         String url = "/policies/id/status";
810         String rsp = restClient().get(url).block();
811         PolicyStatusInfo info = gson.fromJson(rsp, PolicyStatusInfo.class);
812         assertThat(info.status).isEqualTo("OK");
813
814         // GET non existing policy status
815         url = "/policies/XXX/status";
816         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
817
818         // GET STATUS, the NearRT RIC returns error
819         MockA1Client a1Client = a1ClientFactory.getOrCreateA1Client("ric1");
820         url = "/policies/id/status";
821         WebClientResponseException a1Exception = new WebClientResponseException(404, "", null, null, null);
822         doReturn(Mono.error(a1Exception)).when(a1Client).getPolicyStatus(any());
823         rsp = restClient().get(url).block();
824         info = gson.fromJson(rsp, PolicyStatusInfo.class);
825         assertThat(info.status).hasToString("{}");
826     }
827
828     @Test
829     void testGetServiceStatus() throws Exception {
830         String url = "/status";
831         String rsp = restClient().get(url).block();
832         assertThat(rsp).contains("hunky dory");
833
834         rsp = restClient(baseUrl(), false).get(url).block(); // V1 status is used by a readinessProbe
835         assertThat(rsp).isEqualTo("hunky dory");
836     }
837
838     @Test
839     void testServiceNotification() throws Exception {
840
841         final String AUTH_TOKEN = "testToken";
842         Path authFile = Files.createTempFile("pmsTestAuthToken", ".txt");
843         Files.write(authFile, AUTH_TOKEN.getBytes());
844         this.securityContext.setAuthTokenFilePath(authFile);
845
846         putService("junkService");
847         Service junkService = this.services.get("junkService");
848         junkService.setCallbackUrl("https://junk");
849         putService("service");
850
851         Ric ric = addRic("ric1");
852         ric.setState(Ric.RicState.UNAVAILABLE);
853         supervision.checkAllRics();
854         waitForRicState("ric1", RicState.AVAILABLE);
855
856         final RappSimulatorController.TestResults receivedCallbacks = rAppSimulator.getTestResults();
857         await().untilAsserted(() -> assertThat(receivedCallbacks.getReceivedInfo()).hasSize(1));
858         ServiceCallbackInfo callbackInfo = receivedCallbacks.getReceivedInfo().get(0);
859         assertThat(callbackInfo.ricId).isEqualTo("ric1");
860         assertThat(callbackInfo.eventType).isEqualTo(ServiceCallbackInfo.EventType.AVAILABLE);
861
862         var headers = receivedCallbacks.receivedHeaders.get(0);
863         assertThat(headers).containsEntry("authorization", "Bearer " + AUTH_TOKEN);
864
865         Files.delete(authFile);
866     }
867
868     private Policy addPolicy(String id, String typeName, String service, String ric) throws ServiceException {
869         addRic(ric);
870         Policy policy = Policy.builder() //
871                 .id(id) //
872                 .json(jsonString()) //
873                 .ownerServiceId(service) //
874                 .ric(rics.getRic(ric)) //
875                 .type(addPolicyType(typeName, ric)) //
876                 .lastModified(Instant.now()) //
877                 .isTransient(false) //
878                 .statusNotificationUri("/policy-status?id=XXX") //
879                 .build();
880         policies.put(policy);
881         return policy;
882     }
883
884     private Policy addPolicy(String id, String typeName, String service) throws ServiceException {
885         return addPolicy(id, typeName, service, "ric");
886     }
887
888     private String createServiceJson(String name, long keepAliveIntervalSeconds) {
889         String callbackUrl = baseUrl() + RappSimulatorController.SERVICE_CALLBACK_URL;
890         return createServiceJson(name, keepAliveIntervalSeconds, callbackUrl);
891     }
892
893     private String createServiceJson(String name, long keepAliveIntervalSeconds, String url) {
894         ServiceRegistrationInfo service = new ServiceRegistrationInfo(name, keepAliveIntervalSeconds, url);
895
896         String json = gson.toJson(service);
897         return json;
898     }
899
900     private void putService(String name) {
901         putService(name, 0, null);
902     }
903
904     private void putService(String name, long keepAliveIntervalSeconds, @Nullable HttpStatus expectedStatus) {
905         String url = "/services";
906         String body = createServiceJson(name, keepAliveIntervalSeconds);
907         ResponseEntity<String> resp = restClient().putForEntity(url, body).block();
908         if (expectedStatus != null) {
909             assertEquals(expectedStatus, resp.getStatusCode(), "");
910         }
911     }
912
913     private String jsonString() {
914         return "{\"servingCellNrcgi\":\"1\"}";
915     }
916
917     @Test
918     void testConcurrency() throws Exception {
919         logger.info("Concurrency test starting");
920         final Instant startTime = Instant.now();
921         List<Thread> threads = new ArrayList<>();
922         List<ConcurrencyTestRunnable> tests = new ArrayList<>();
923         a1ClientFactory.setResponseDelay(Duration.ofMillis(2));
924         addRic("ric");
925         addPolicyType("type1", "ric");
926         addPolicyType("type2", "ric");
927
928         final String NON_RESPONDING_RIC = "NonRespondingRic";
929         Ric nonRespondingRic = addRic(NON_RESPONDING_RIC);
930         MockA1Client a1Client = a1ClientFactory.getOrCreateA1Client(NON_RESPONDING_RIC);
931         a1Client.setErrorInject("errorInject");
932
933         for (int i = 0; i < 10; ++i) {
934             AsyncRestClient restClient = restClient();
935             ConcurrencyTestRunnable test =
936                     new ConcurrencyTestRunnable(restClient, supervision, a1ClientFactory, rics, policyTypes);
937             Thread thread = new Thread(test, "TestThread_" + i);
938             thread.start();
939             threads.add(thread);
940             tests.add(test);
941         }
942         for (Thread t : threads) {
943             t.join();
944         }
945         for (ConcurrencyTestRunnable test : tests) {
946             assertThat(test.isFailed()).isFalse();
947         }
948         assertThat(policies.size()).isZero();
949         logger.info("Concurrency test took " + Duration.between(startTime, Instant.now()));
950
951         assertThat(nonRespondingRic.getState()).isEqualTo(RicState.UNAVAILABLE);
952         nonRespondingRic.setState(RicState.AVAILABLE);
953     }
954
955     private AsyncRestClient restClient(String baseUrl, boolean useTrustValidation) {
956         WebClientConfig config = this.applicationConfig.getWebClientConfig();
957         config = WebClientConfig.builder() //
958                 .keyStoreType(config.getKeyStoreType()) //
959                 .keyStorePassword(config.getKeyStorePassword()) //
960                 .keyStore(config.getKeyStore()) //
961                 .keyPassword(config.getKeyPassword()) //
962                 .isTrustStoreUsed(useTrustValidation) //
963                 .trustStore(config.getTrustStore()) //
964                 .trustStorePassword(config.getTrustStorePassword()) //
965                 .httpProxyConfig(config.getHttpProxyConfig()) //
966                 .build();
967
968         AsyncRestClientFactory f = new AsyncRestClientFactory(config, new SecurityContext(""));
969         return f.createRestClientNoHttpProxy(baseUrl);
970
971     }
972
973     private String baseUrl() {
974         return "https://localhost:" + port;
975     }
976
977     private AsyncRestClient restClient(boolean useTrustValidation) {
978         return restClient(baseUrl() + Consts.V2_API_ROOT, useTrustValidation);
979     }
980
981     private AsyncRestClient restClient() {
982         return restClient(false);
983     }
984
985     private void testErrorCode(Mono<?> request, HttpStatus expStatus) {
986         testErrorCode(request, expStatus, "", true);
987     }
988
989     private void testErrorCode(Mono<?> request, HttpStatus expStatus, boolean expectApplicationProblemJsonMediaType) {
990         testErrorCode(request, expStatus, "", expectApplicationProblemJsonMediaType);
991     }
992
993     private void testErrorCode(Mono<?> request, HttpStatus expStatus, String responseContains) {
994         testErrorCode(request, expStatus, responseContains, true);
995     }
996
997     private void testErrorCode(Mono<?> request, HttpStatus expStatus, String responseContains,
998             boolean expectApplicationProblemJsonMediaType) {
999         StepVerifier.create(request) //
1000                 .expectSubscription() //
1001                 .expectErrorMatches(
1002                         t -> checkWebClientError(t, expStatus, responseContains, expectApplicationProblemJsonMediaType)) //
1003                 .verify();
1004     }
1005
1006     private void waitForRicState(String ricId, RicState state) throws ServiceException {
1007         Ric ric = rics.getRic(ricId);
1008         await().untilAsserted(() -> state.equals(ric.getState()));
1009     }
1010
1011     private boolean checkWebClientError(Throwable throwable, HttpStatus expStatus, String responseContains,
1012             boolean expectApplicationProblemJsonMediaType) {
1013         assertTrue(throwable instanceof WebClientResponseException);
1014         WebClientResponseException responseException = (WebClientResponseException) throwable;
1015         assertThat(responseException.getStatusCode()).isEqualTo(expStatus);
1016         assertThat(responseException.getResponseBodyAsString()).contains(responseContains);
1017         if (expectApplicationProblemJsonMediaType) {
1018             assertThat(responseException.getHeaders().getContentType()).isEqualTo(MediaType.APPLICATION_PROBLEM_JSON);
1019         }
1020         return true;
1021     }
1022
1023     private MockA1Client getA1Client(String ricId) throws ServiceException {
1024         return a1ClientFactory.getOrCreateA1Client(ricId);
1025     }
1026
1027     private PolicyType createPolicyType(String policyTypeName) {
1028         return PolicyType.builder() //
1029                 .id(policyTypeName) //
1030                 .schema("{\"title\":\"" + policyTypeName + "\"}") //
1031                 .build();
1032     }
1033
1034     private PolicyType addPolicyType(String policyTypeName, String ricId) {
1035         PolicyType type = createPolicyType(policyTypeName);
1036         policyTypes.put(type);
1037         addRic(ricId).addSupportedPolicyType(type);
1038         return type;
1039     }
1040
1041     private Ric addRic(String ricId) {
1042         return addRic(ricId, null);
1043     }
1044
1045     private RicConfig ricConfig(String ricId, String managedElement) {
1046         List<String> mes = new ArrayList<>();
1047         if (managedElement != null) {
1048             mes.add(managedElement);
1049         }
1050         return RicConfig.builder() //
1051                 .ricId(ricId) //
1052                 .baseUrl(ricId) //
1053                 .managedElementIds(mes) //
1054                 .build();
1055     }
1056
1057     private Ric addRic(String ricId, String managedElement) {
1058         if (rics.get(ricId) != null) {
1059             return rics.get(ricId);
1060         }
1061
1062         RicConfig conf = ricConfig(ricId, managedElement);
1063         Ric ric = new Ric(conf);
1064         ric.setState(Ric.RicState.AVAILABLE);
1065         this.rics.put(ric);
1066         return ric;
1067     }
1068
1069 }