682559534cc55d733e5838592dbaee4235f2e461
[ccsdk/oran.git] /
1 /*-
2  * ========================LICENSE_START=================================
3  * ONAP : ccsdk oran
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
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.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;
44
45 import org.json.JSONObject;
46 import org.junit.jupiter.api.AfterEach;
47 import org.junit.jupiter.api.BeforeEach;
48 import org.junit.jupiter.api.Test;
49 import org.junit.jupiter.api.extension.ExtendWith;
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.ImmutablePolicy;
60 import org.onap.ccsdk.oran.a1policymanagementservice.repository.ImmutablePolicyType;
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.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;
92
93 import reactor.core.publisher.Mono;
94 import reactor.test.StepVerifier;
95 import reactor.util.annotation.Nullable;
96
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 class ApplicationTest {
103     private static final Logger logger = LoggerFactory.getLogger(ApplicationTest.class);
104
105     @Autowired
106     ApplicationContext context;
107
108     @Autowired
109     private Rics rics;
110
111     @Autowired
112     private Policies policies;
113
114     @Autowired
115     private PolicyTypes policyTypes;
116
117     @Autowired
118     MockA1ClientFactory a1ClientFactory;
119
120     @Autowired
121     RicSupervision supervision;
122
123     @Autowired
124     ApplicationConfig applicationConfig;
125
126     @Autowired
127     Services services;
128
129     @Autowired
130     RappSimulatorController rAppSimulator;
131
132     private static Gson gson = new GsonBuilder().create();
133
134     public static class MockApplicationConfig extends ApplicationConfig {
135         @Override
136         public String getLocalConfigurationFilePath() {
137             return ""; // No config file loaded for the test
138         }
139     }
140
141     /**
142      * Overrides the BeanFactory.
143      */
144     @TestConfiguration
145     static class TestBeanFactory {
146         private final PolicyTypes policyTypes = new PolicyTypes();
147         private final Services services = new Services();
148         private final Policies policies = new Policies();
149         MockA1ClientFactory a1ClientFactory = null;
150
151         @Bean
152         public ApplicationConfig getApplicationConfig() {
153             return new MockApplicationConfig();
154         }
155
156         @Bean
157         MockA1ClientFactory getA1ClientFactory() {
158             if (a1ClientFactory == null) {
159                 this.a1ClientFactory = new MockA1ClientFactory(this.policyTypes);
160             }
161             return this.a1ClientFactory;
162         }
163
164         @Bean
165         public PolicyTypes getPolicyTypes() {
166             return this.policyTypes;
167         }
168
169         @Bean
170         Policies getPolicies() {
171             return this.policies;
172         }
173
174         @Bean
175         Services getServices() {
176             return this.services;
177         }
178
179         @Bean
180         public ServiceSupervision getServiceSupervision() {
181             Duration checkInterval = Duration.ofMillis(1);
182             return new ServiceSupervision(this.services, this.policies, this.getA1ClientFactory(), checkInterval);
183         }
184
185         @Bean
186         public ServletWebServerFactory servletContainer() {
187             return new TomcatServletWebServerFactory();
188         }
189
190     }
191
192     @LocalServerPort
193     private int port;
194
195     @BeforeEach
196     void reset() {
197         rics.clear();
198         policies.clear();
199         policyTypes.clear();
200         services.clear();
201         a1ClientFactory.reset();
202         this.rAppSimulator.getTestResults().clear();
203     }
204
205     @AfterEach
206     void verifyNoRicLocks() {
207         for (Ric ric : this.rics.getRics()) {
208             ric.getLock().lockBlocking(LockType.EXCLUSIVE);
209             ric.getLock().unlockBlocking();
210             assertThat(ric.getLock().getLockCounter()).isZero();
211             assertThat(ric.getState()).isEqualTo(Ric.RicState.AVAILABLE);
212         }
213     }
214
215     @Test
216     void generateApiDoc() throws IOException {
217         String url = "https://localhost:" + this.port + "/v3/api-docs";
218         ResponseEntity<String> resp = restClient("", false).getForEntity(url).block();
219         assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
220         JSONObject jsonObj = new JSONObject(resp.getBody());
221         assertThat(jsonObj.remove("servers")).isNotNull();
222
223         String indented = (jsonObj).toString(4);
224         String docDir = "api/";
225         Files.createDirectories(Paths.get(docDir));
226         try (PrintStream out = new PrintStream(new FileOutputStream(docDir + "pms-api.json"))) {
227             out.print(indented);
228         }
229     }
230
231     @Test
232     void testGetRics() throws Exception {
233         addRic("ric1");
234         this.addPolicyType("type1", "ric1");
235         String url = "/rics?policytype_id=type1";
236         String rsp = restClient().get(url).block();
237         assertThat(rsp).contains("ric1");
238
239         // nameless type for ORAN A1 1.1
240         addRic("ric2");
241         this.addPolicyType("", "ric2");
242         url = "/rics?policytype_id=";
243
244         // This tests also validation of trusted certs restClient(true)
245         rsp = restClient(true).get(url).block();
246         assertThat(rsp).contains("ric2") //
247                 .doesNotContain("ric1") //
248                 .contains("AVAILABLE");
249
250         // All RICs
251         rsp = restClient().get("/rics").block();
252         assertThat(rsp).contains("ric2") //
253                 .contains("ric1");
254
255         // Non existing policy type
256         url = "/rics?policytype_id=XXXX";
257         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
258     }
259
260     @Test
261     void testSynchronization() throws Exception {
262         // Two polictypes will be put in the NearRT RICs
263         PolicyTypes nearRtRicPolicyTypes = new PolicyTypes();
264         nearRtRicPolicyTypes.put(createPolicyType("typeName"));
265         nearRtRicPolicyTypes.put(createPolicyType("typeName2"));
266         this.a1ClientFactory.setPolicyTypes(nearRtRicPolicyTypes);
267
268         // One type and one instance added to the Policy Management Service's storage
269         final String ric1Name = "ric1";
270         Ric ric1 = addRic(ric1Name);
271         Policy policy2 = addPolicy("policyId2", "typeName", "service", ric1Name);
272         Ric ric2 = addRic("ric2");
273
274         getA1Client(ric1Name).putPolicy(policy2); // put it in the RIC (Near-RT RIC)
275         policies.remove(policy2); // Remove it from the repo -> should be deleted in the RIC
276
277         String policyId = "policyId";
278         Policy policy = addPolicy(policyId, "typeName", "service", ric1Name); // This should be created in the RIC
279         supervision.checkAllRics(); // The created policy should be put in the RIC
280
281         // Wait until synch is completed
282         waitForRicState(ric1Name, RicState.SYNCHRONIZING);
283         waitForRicState(ric1Name, RicState.AVAILABLE);
284         waitForRicState("ric2", RicState.AVAILABLE);
285
286         Policies ricPolicies = getA1Client(ric1Name).getPolicies();
287         assertThat(ricPolicies.size()).isEqualTo(1);
288         Policy ricPolicy = ricPolicies.get(policyId);
289         assertThat(ricPolicy.json()).isEqualTo(policy.json());
290
291         // Both types should be in the Policy Management Service's storage after the
292         // synch
293         assertThat(ric1.getSupportedPolicyTypes()).hasSize(2);
294         assertThat(ric2.getSupportedPolicyTypes()).hasSize(2);
295     }
296
297     @Test
298     void testGetRic() throws Exception {
299         String ricId = "ric1";
300         String managedElementId = "kista_1";
301         addRic(ricId, managedElementId);
302
303         String url = "/rics/ric?managed_element_id=" + managedElementId;
304         String rsp = restClient().get(url).block();
305         RicInfo ricInfo = gson.fromJson(rsp, RicInfo.class);
306         assertThat(ricInfo.ricId).isEqualTo(ricId);
307
308         url = "/rics/ric?ric_id=" + ricId;
309         rsp = restClient().get(url).block();
310         ricInfo = gson.fromJson(rsp, RicInfo.class);
311         assertThat(ricInfo.ricId).isEqualTo(ricId);
312
313         // test GET RIC for ManagedElement that does not exist
314         url = "/rics/ric?managed_element_id=" + "junk";
315         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
316
317         url = "/rics/ric";
318         testErrorCode(restClient().get(url), HttpStatus.BAD_REQUEST);
319     }
320
321     private String putPolicyBody(String serviceName, String ricId, String policyTypeName, String policyInstanceId,
322             boolean isTransient) {
323         PolicyInfo info = new PolicyInfo();
324         info.policyId = policyInstanceId;
325         info.policyTypeId = policyTypeName;
326         info.ricId = ricId;
327         info.serviceId = serviceName;
328         info.policyData = gson.fromJson(jsonString(), Object.class);
329
330         if (isTransient) {
331             info.isTransient = isTransient;
332         }
333         info.statusNotificationUri = "statusNotificationUri";
334         return gson.toJson(info);
335     }
336
337     private String putPolicyBody(String serviceName, String ricId, String policyTypeName, String policyInstanceId) {
338         return putPolicyBody(serviceName, ricId, policyTypeName, policyInstanceId, false);
339     }
340
341     @Test
342     void testPutPolicy() throws Exception {
343         String serviceName = "service.1";
344         String ricId = "ric.1";
345         String policyTypeName = "type1_1.2.3";
346         String policyInstanceId = "instance_1.2.3";
347
348         putService(serviceName);
349         addPolicyType(policyTypeName, ricId);
350
351         // PUT a transient policy
352         String url = "/policies";
353         String policyBody = putPolicyBody(serviceName, ricId, policyTypeName, policyInstanceId, true);
354         this.rics.getRic(ricId).setState(Ric.RicState.AVAILABLE);
355
356         restClient().put(url, policyBody).block();
357
358         Policy policy = policies.getPolicy(policyInstanceId);
359         assertThat(policy).isNotNull();
360         assertThat(policy.id()).isEqualTo(policyInstanceId);
361         assertThat(policy.ownerServiceId()).isEqualTo(serviceName);
362         assertThat(policy.ric().id()).isEqualTo(ricId);
363         assertThat(policy.isTransient()).isTrue();
364
365         // Put a non transient policy
366         policyBody = putPolicyBody(serviceName, ricId, policyTypeName, policyInstanceId);
367         restClient().put(url, policyBody).block();
368         policy = policies.getPolicy(policyInstanceId);
369         assertThat(policy.isTransient()).isFalse();
370
371         url = "/policy-instances";
372         String rsp = restClient().get(url).block();
373         assertThat(rsp).as("Response contains policy instance ID.").contains(policyInstanceId);
374
375         url = "/policies/" + policyInstanceId;
376         rsp = restClient().get(url).block();
377         assertThat(rsp).contains(policyBody);
378
379         // Test of error codes
380         url = "/policies";
381         policyBody = putPolicyBody(serviceName, ricId + "XX", policyTypeName, policyInstanceId);
382         testErrorCode(restClient().put(url, policyBody), HttpStatus.NOT_FOUND);
383
384         policyBody = putPolicyBody(serviceName, ricId, policyTypeName + "XX", policyInstanceId);
385         addPolicyType(policyTypeName + "XX", "otherRic");
386         testErrorCode(restClient().put(url, policyBody), HttpStatus.NOT_FOUND);
387
388         policyBody = putPolicyBody(serviceName, ricId, policyTypeName, policyInstanceId);
389         this.rics.getRic(ricId).setState(Ric.RicState.SYNCHRONIZING);
390         testErrorCode(restClient().put(url, policyBody), HttpStatus.LOCKED);
391         this.rics.getRic(ricId).setState(Ric.RicState.AVAILABLE);
392     }
393
394     @Test
395     /**
396      * Test that HttpStatus and body from failing REST call to A1 is passed on to
397      * the caller.
398      *
399      * @throws ServiceException
400      */
401     void testErrorFromRic() throws ServiceException {
402         putService("service1");
403         addPolicyType("type1", "ric1");
404
405         MockA1Client a1Client = a1ClientFactory.getOrCreateA1Client("ric1");
406         HttpStatus httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;
407         String responseBody = "Refused";
408         byte[] responseBodyBytes = responseBody.getBytes(StandardCharsets.UTF_8);
409
410         WebClientResponseException a1Exception = new WebClientResponseException(httpStatus.value(), "statusText", null,
411                 responseBodyBytes, StandardCharsets.UTF_8, null);
412         doReturn(Mono.error(a1Exception)).when(a1Client).putPolicy(any());
413
414         // PUT Policy
415         String putBody = putPolicyBody("service1", "ric1", "type1", "id1");
416         String url = "/policies";
417         testErrorCode(restClient().put(url, putBody), httpStatus, responseBody);
418
419         // DELETE POLICY
420         this.addPolicy("instance1", "type1", "service1", "ric1");
421         doReturn(Mono.error(a1Exception)).when(a1Client).deletePolicy(any());
422         testErrorCode(restClient().delete("/policies/instance1"), httpStatus, responseBody);
423
424     }
425
426     @Test
427     void testPutTypelessPolicy() throws Exception {
428         putService("service1");
429         addPolicyType("", "ric1");
430         String body = putPolicyBody("service1", "ric1", "", "id1");
431         restClient().put("/policies", body).block();
432
433         String rsp = restClient().get("/policy-instances").block();
434         PolicyInfoList info = gson.fromJson(rsp, PolicyInfoList.class);
435         assertThat(info.policies).hasSize(1);
436         PolicyInfo policyInfo = info.policies.iterator().next();
437         assertThat(policyInfo.policyId).isEqualTo("id1");
438         assertThat(policyInfo.policyTypeId).isEmpty();
439     }
440
441     @Test
442     void testRefuseToUpdatePolicy() throws Exception {
443         // Test that only the json can be changed for a already created policy
444         // In this case service is attempted to be changed
445         this.addRic("ric1");
446         this.addRic("ricXXX");
447         this.addPolicy("instance1", "type1", "service1", "ric1");
448         this.addPolicy("instance2", "type1", "service1", "ricXXX");
449
450         // Try change ric1 -> ricXXX
451         String bodyWrongRic = putPolicyBody("service1", "ricXXX", "type1", "instance1");
452         testErrorCode(restClient().put("/policies", bodyWrongRic), HttpStatus.CONFLICT);
453     }
454
455     @Test
456     void testGetPolicy() throws Exception {
457         String url = "/policies/id";
458         Policy policy = addPolicy("id", "typeName", "service1", "ric1");
459         {
460             String rsp = restClient().get(url).block();
461             PolicyInfo info = gson.fromJson(rsp, PolicyInfo.class);
462             String policyStr = gson.toJson(info.policyData);
463             assertThat(policyStr).isEqualTo(policy.json());
464         }
465         {
466             policies.remove(policy);
467             testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
468         }
469     }
470
471     @Test
472     void testDeletePolicy() throws Exception {
473         String policyId = "id.1";
474         addPolicy(policyId, "typeName", "service1", "ric1");
475         assertThat(policies.size()).isEqualTo(1);
476
477         String url = "/policies/" + policyId;
478         ResponseEntity<String> entity = restClient().deleteForEntity(url).block();
479
480         assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.NO_CONTENT);
481         assertThat(policies.size()).isZero();
482
483         // Delete a non existing policy
484         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
485     }
486
487     @Test
488     void testGetPolicyType() throws Exception {
489         String typeId = "AC.D";
490         addPolicyType(typeId, "ric1");
491
492         waitForRicState("ric1", RicState.AVAILABLE);
493
494         String url = "/policy-types/" + typeId;
495
496         String rsp = this.restClient().get(url).block();
497
498         PolicyTypeInfo info = gson.fromJson(rsp, PolicyTypeInfo.class);
499         assertThat(info.schema).isNotNull();
500
501         // Get non existing schema
502         url = "/policy-types/JUNK";
503         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
504     }
505
506     String createPolicyTypesJson(String... types) {
507         List<String> list = new ArrayList<>();
508         Collections.addAll(list, types);
509         PolicyTypeIdList ids = new PolicyTypeIdList(list);
510         return gson.toJson(ids);
511     }
512
513     @Test
514     void testGetPolicyTypes() throws Exception {
515         addPolicyType("type1", "ric1");
516         addPolicyType("type2", "ric2");
517
518         String url = "/policy-types";
519         String rsp = restClient().get(url).block();
520         String expResp = createPolicyTypesJson("type2", "type1");
521         assertThat(rsp).isEqualTo(expResp);
522
523         url = "/policy-types?ric_id=ric1";
524         rsp = restClient().get(url).block();
525         expResp = createPolicyTypesJson("type1");
526         assertThat(rsp).isEqualTo(expResp);
527
528         // Get policy types for non existing RIC
529         url = "/policy-types?ric_id=ric1XXX";
530         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
531     }
532
533     @Test
534     void testGetPolicyInstances() throws Exception {
535         addPolicy("id1", "type1", "service1");
536
537         String url = "/policy-instances";
538         String rsp = restClient().get(url).block();
539         logger.info(rsp);
540         PolicyInfoList info = gson.fromJson(rsp, PolicyInfoList.class);
541         assertThat(info.policies).hasSize(1);
542         PolicyInfo policyInfo = info.policies.iterator().next();
543         assert (policyInfo.validate());
544         assertThat(policyInfo.policyId).isEqualTo("id1");
545         assertThat(policyInfo.policyTypeId).isEqualTo("type1");
546         assertThat(policyInfo.serviceId).isEqualTo("service1");
547     }
548
549     @Test
550     void testGetPolicyInstancesFilter() throws Exception {
551         addPolicy("id1", "type1", "service1");
552         addPolicy("id2", "type1", "service2");
553         addPolicy("id3", "type2", "service1");
554
555         String url = "/policy-instances?policytype_id=type1";
556         String rsp = restClient().get(url).block();
557         logger.info(rsp);
558         assertThat(rsp).contains("id1") //
559                 .contains("id2") //
560                 .doesNotContain("id3");
561
562         url = "/policy-instances?policytype_id=type1&service_id=service2";
563         rsp = restClient().get(url).block();
564         logger.info(rsp);
565         assertThat(rsp).doesNotContain("id1") //
566                 .contains("id2") //
567                 .doesNotContain("id3");
568
569         // Test get policies for non existing type
570         url = "/policy-instances?policytype_id=type1XXX";
571         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
572
573         // Test get policies for non existing RIC
574         url = "/policy-instances?ric_id=XXX";
575         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
576     }
577
578     @Test
579     void testGetPolicyIdsFilter() throws Exception {
580         addPolicy("id1", "type1", "service1", "ric1");
581         addPolicy("id2", "type1", "service2", "ric1");
582         addPolicy("id3", "type2", "service1", "ric1");
583
584         String url = "/policies?policytype_id=type1";
585         String rsp = restClient().get(url).block();
586         logger.info(rsp);
587         assertThat(rsp).contains("id1") //
588                 .contains("id2") //
589                 .doesNotContain("id3");
590
591         url = "/policies?policytype_id=type1&service_id=service1&ric=ric1";
592         rsp = restClient().get(url).block();
593         PolicyIdList respList = gson.fromJson(rsp, PolicyIdList.class);
594         assertThat(respList.policyIds.iterator().next()).isEqualTo("id1");
595
596         // Test get policy ids for non existing type
597         url = "/policies?policytype_id=type1XXX";
598         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
599
600         // Test get policy ids for non existing RIC
601         url = "/policies?ric_id=XXX";
602         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
603     }
604
605     @Test
606     void testPutAndGetService() throws Exception {
607         // PUT
608         String serviceName = "ac.dc";
609         putService(serviceName, 0, HttpStatus.CREATED);
610         putService(serviceName, 0, HttpStatus.OK);
611
612         // GET one service
613         String url = "/services?service_id=" + serviceName;
614         String rsp = restClient().get(url).block();
615         ServiceStatusList info = gson.fromJson(rsp, ServiceStatusList.class);
616         assertThat(info.statusList).hasSize(1);
617         ServiceStatus status = info.statusList.iterator().next();
618         assertThat(status.keepAliveIntervalSeconds).isZero();
619         assertThat(status.serviceId).isEqualTo(serviceName);
620
621         // GET (all)
622         url = "/services";
623         rsp = restClient().get(url).block();
624         assertThat(rsp).as("Response contains service name").contains(serviceName);
625         logger.info(rsp);
626
627         // Keep alive
628         url = "/services/" + serviceName + "/keepalive";
629         ResponseEntity<?> entity = restClient().putForEntity(url).block();
630         assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
631
632         // DELETE service
633         assertThat(services.size()).isEqualTo(1);
634         url = "/services/" + serviceName;
635         restClient().delete(url).block();
636         assertThat(services.size()).isZero();
637
638         // Keep alive, no registered service
639         testErrorCode(restClient().put("/services/junk/keepalive", ""), HttpStatus.NOT_FOUND);
640
641         // PUT service with bad payload
642         testErrorCode(restClient().put("/services", "crap"), HttpStatus.BAD_REQUEST, false);
643         testErrorCode(restClient().put("/services", "{}"), HttpStatus.BAD_REQUEST, false);
644         testErrorCode(restClient().put("/services", createServiceJson(serviceName, -123)), HttpStatus.BAD_REQUEST,
645                 false);
646         testErrorCode(restClient().put("/services", createServiceJson(serviceName, 0, "missing.portandprotocol.com")),
647                 HttpStatus.BAD_REQUEST, false);
648
649         // GET non existing service
650         testErrorCode(restClient().get("/services?service_id=XXX"), HttpStatus.NOT_FOUND);
651     }
652
653     @Test
654     void testServiceSupervision() throws Exception {
655         putService("service1", 1, HttpStatus.CREATED);
656         addPolicyType("type1", "ric1");
657
658         String policyBody = putPolicyBody("service1", "ric1", "type1", "instance1");
659         restClient().put("/policies", policyBody).block();
660
661         assertThat(policies.size()).isEqualTo(1);
662         assertThat(services.size()).isEqualTo(1);
663
664         // Timeout after ~1 second
665         await().untilAsserted(() -> assertThat(policies.size()).isZero());
666         assertThat(services.size()).isZero();
667     }
668
669     @Test
670     void testGetPolicyStatus() throws Exception {
671         addPolicy("id", "typeName", "service1", "ric1");
672         assertThat(policies.size()).isEqualTo(1);
673
674         String url = "/policies/id/status";
675         String rsp = restClient().get(url).block();
676         PolicyStatusInfo info = gson.fromJson(rsp, PolicyStatusInfo.class);
677         assertThat(info.status).isEqualTo("OK");
678
679         // GET non existing policy status
680         url = "/policies/XXX/status";
681         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
682
683         // GET STATUS, the NearRT RIC returns error
684         MockA1Client a1Client = a1ClientFactory.getOrCreateA1Client("ric1");
685         url = "/policies/id/status";
686         WebClientResponseException a1Exception = new WebClientResponseException(404, "", null, null, null);
687         doReturn(Mono.error(a1Exception)).when(a1Client).getPolicyStatus(any());
688         rsp = restClient().get(url).block();
689         info = gson.fromJson(rsp, PolicyStatusInfo.class);
690         assertThat(info.status).hasToString("{}");
691     }
692
693     @Test
694     void testServiceNotification() throws ServiceException {
695         putService("junkService");
696         Service junkService = this.services.get("junkService");
697         junkService.setCallbackUrl("https://junk");
698         putService("service");
699
700         Ric ric = addRic("ric1");
701         ric.setState(Ric.RicState.UNAVAILABLE);
702         supervision.checkAllRics();
703         waitForRicState("ric1", RicState.AVAILABLE);
704
705         RappSimulatorController.TestResults receivedCallbacks = rAppSimulator.getTestResults();
706         assertThat(receivedCallbacks.getReceivedInfo().size()).isEqualTo(1);
707         ServiceCallbackInfo callbackInfo = receivedCallbacks.getReceivedInfo().get(0);
708         assertThat(callbackInfo.ricId).isEqualTo("ric1");
709         assertThat(callbackInfo.eventType).isEqualTo(ServiceCallbackInfo.EventType.AVAILABLE);
710     }
711
712     private Policy addPolicy(String id, String typeName, String service, String ric) throws ServiceException {
713         addRic(ric);
714         Policy policy = ImmutablePolicy.builder() //
715                 .id(id) //
716                 .json(jsonString()) //
717                 .ownerServiceId(service) //
718                 .ric(rics.getRic(ric)) //
719                 .type(addPolicyType(typeName, ric)) //
720                 .lastModified(Instant.now()) //
721                 .isTransient(false) //
722                 .statusNotificationUri("/policy-status?id=XXX") //
723                 .build();
724         policies.put(policy);
725         return policy;
726     }
727
728     private Policy addPolicy(String id, String typeName, String service) throws ServiceException {
729         return addPolicy(id, typeName, service, "ric");
730     }
731
732     private String createServiceJson(String name, long keepAliveIntervalSeconds) {
733         String callbackUrl = baseUrl() + RappSimulatorController.SERVICE_CALLBACK_URL;
734         return createServiceJson(name, keepAliveIntervalSeconds, callbackUrl);
735     }
736
737     private String createServiceJson(String name, long keepAliveIntervalSeconds, String url) {
738         ServiceRegistrationInfo service = new ServiceRegistrationInfo(name, keepAliveIntervalSeconds, url);
739
740         String json = gson.toJson(service);
741         return json;
742     }
743
744     private void putService(String name) {
745         putService(name, 0, null);
746     }
747
748     private void putService(String name, long keepAliveIntervalSeconds, @Nullable HttpStatus expectedStatus) {
749         String url = "/services";
750         String body = createServiceJson(name, keepAliveIntervalSeconds);
751         ResponseEntity<String> resp = restClient().putForEntity(url, body).block();
752         if (expectedStatus != null) {
753             assertEquals(expectedStatus, resp.getStatusCode(), "");
754         }
755     }
756
757     private String jsonString() {
758         return "{\"servingCellNrcgi\":\"1\"}";
759     }
760
761     @Test
762     void testConcurrency() throws Exception {
763         final Instant startTime = Instant.now();
764         List<Thread> threads = new ArrayList<>();
765         List<ConcurrencyTestRunnable> tests = new ArrayList<>();
766         a1ClientFactory.setResponseDelay(Duration.ofMillis(1));
767         addRic("ric");
768         addPolicyType("type1", "ric");
769         addPolicyType("type2", "ric");
770
771         for (int i = 0; i < 10; ++i) {
772             AsyncRestClient restClient = restClient();
773             ConcurrencyTestRunnable test =
774                     new ConcurrencyTestRunnable(restClient, supervision, a1ClientFactory, rics, policyTypes);
775             Thread thread = new Thread(test, "TestThread_" + i);
776             thread.start();
777             threads.add(thread);
778             tests.add(test);
779         }
780         for (Thread t : threads) {
781             t.join();
782         }
783         for (ConcurrencyTestRunnable test : tests) {
784             assertThat(test.isFailed()).isFalse();
785         }
786         assertThat(policies.size()).isZero();
787         logger.info("Concurrency test took " + Duration.between(startTime, Instant.now()));
788     }
789
790     private AsyncRestClient restClient(String baseUrl, boolean useTrustValidation) {
791         WebClientConfig config = this.applicationConfig.getWebClientConfig();
792         config = ImmutableWebClientConfig.builder() //
793                 .keyStoreType(config.keyStoreType()) //
794                 .keyStorePassword(config.keyStorePassword()) //
795                 .keyStore(config.keyStore()) //
796                 .keyPassword(config.keyPassword()) //
797                 .isTrustStoreUsed(useTrustValidation) //
798                 .trustStore(config.trustStore()) //
799                 .trustStorePassword(config.trustStorePassword()) //
800                 .httpProxyConfig(config.httpProxyConfig()) //
801                 .build();
802
803         AsyncRestClientFactory f = new AsyncRestClientFactory(config);
804         return f.createRestClientNoHttpProxy(baseUrl);
805
806     }
807
808     private String baseUrl() {
809         return "https://localhost:" + port;
810     }
811
812     private AsyncRestClient restClient(boolean useTrustValidation) {
813         String baseUrl = "https://localhost:" + port + Consts.V2_API_ROOT;
814         return restClient(baseUrl, useTrustValidation);
815     }
816
817     private AsyncRestClient restClient() {
818         return restClient(false);
819     }
820
821     private void testErrorCode(Mono<?> request, HttpStatus expStatus) {
822         testErrorCode(request, expStatus, "", true);
823     }
824
825     private void testErrorCode(Mono<?> request, HttpStatus expStatus, boolean expectApplicationProblemJsonMediaType) {
826         testErrorCode(request, expStatus, "", expectApplicationProblemJsonMediaType);
827     }
828
829     private void testErrorCode(Mono<?> request, HttpStatus expStatus, String responseContains) {
830         testErrorCode(request, expStatus, responseContains, true);
831     }
832
833     private void testErrorCode(Mono<?> request, HttpStatus expStatus, String responseContains,
834             boolean expectApplicationProblemJsonMediaType) {
835         StepVerifier.create(request) //
836                 .expectSubscription() //
837                 .expectErrorMatches(
838                         t -> checkWebClientError(t, expStatus, responseContains, expectApplicationProblemJsonMediaType)) //
839                 .verify();
840     }
841
842     private void waitForRicState(String ricId, RicState state) throws ServiceException {
843         Ric ric = rics.getRic(ricId);
844         await().untilAsserted(() -> state.equals(ric.getState()));
845     }
846
847     private boolean checkWebClientError(Throwable throwable, HttpStatus expStatus, String responseContains,
848             boolean expectApplicationProblemJsonMediaType) {
849         assertTrue(throwable instanceof WebClientResponseException);
850         WebClientResponseException responseException = (WebClientResponseException) throwable;
851         assertThat(responseException.getStatusCode()).isEqualTo(expStatus);
852         assertThat(responseException.getResponseBodyAsString()).contains(responseContains);
853         if (expectApplicationProblemJsonMediaType) {
854             assertThat(responseException.getHeaders().getContentType()).isEqualTo(MediaType.APPLICATION_PROBLEM_JSON);
855         }
856         return true;
857     }
858
859     private MockA1Client getA1Client(String ricId) throws ServiceException {
860         return a1ClientFactory.getOrCreateA1Client(ricId);
861     }
862
863     private PolicyType createPolicyType(String policyTypeName) {
864         return ImmutablePolicyType.builder() //
865                 .id(policyTypeName) //
866                 .schema("{\"title\":\"" + policyTypeName + "\"}") //
867                 .build();
868     }
869
870     private PolicyType addPolicyType(String policyTypeName, String ricId) {
871         PolicyType type = createPolicyType(policyTypeName);
872         policyTypes.put(type);
873         addRic(ricId).addSupportedPolicyType(type);
874         return type;
875     }
876
877     private Ric addRic(String ricId) {
878         return addRic(ricId, null);
879     }
880
881     private Ric addRic(String ricId, String managedElement) {
882         if (rics.get(ricId) != null) {
883             return rics.get(ricId);
884         }
885         List<String> mes = new ArrayList<>();
886         if (managedElement != null) {
887             mes.add(managedElement);
888         }
889         RicConfig conf = ImmutableRicConfig.builder() //
890                 .ricId(ricId) //
891                 .baseUrl(ricId) //
892                 .managedElementIds(mes) //
893                 .controllerName("") //
894                 .build();
895         Ric ric = new Ric(conf);
896         ric.setState(Ric.RicState.AVAILABLE);
897         this.rics.put(ric);
898         return ric;
899     }
900
901 }