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