f8c15b6a8298f002f6b47884fcfd596fae9c8293
[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.v1;
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 import com.google.gson.JsonArray;
33 import com.google.gson.JsonElement;
34 import com.google.gson.JsonParser;
35
36 import java.nio.charset.StandardCharsets;
37 import java.time.Duration;
38 import java.time.Instant;
39 import java.util.ArrayList;
40 import java.util.List;
41
42 import org.junit.jupiter.api.AfterEach;
43 import org.junit.jupiter.api.BeforeEach;
44 import org.junit.jupiter.api.Test;
45 import org.junit.jupiter.api.extension.ExtendWith;
46 import org.onap.ccsdk.oran.a1policymanagementservice.clients.AsyncRestClient;
47 import org.onap.ccsdk.oran.a1policymanagementservice.configuration.ApplicationConfig;
48 import org.onap.ccsdk.oran.a1policymanagementservice.configuration.ImmutableRicConfig;
49 import org.onap.ccsdk.oran.a1policymanagementservice.configuration.ImmutableWebClientConfig;
50 import org.onap.ccsdk.oran.a1policymanagementservice.configuration.RicConfig;
51 import org.onap.ccsdk.oran.a1policymanagementservice.configuration.WebClientConfig;
52 import org.onap.ccsdk.oran.a1policymanagementservice.exceptions.ServiceException;
53 import org.onap.ccsdk.oran.a1policymanagementservice.repository.ImmutablePolicy;
54 import org.onap.ccsdk.oran.a1policymanagementservice.repository.ImmutablePolicyType;
55 import org.onap.ccsdk.oran.a1policymanagementservice.repository.Lock.LockType;
56 import org.onap.ccsdk.oran.a1policymanagementservice.repository.Policies;
57 import org.onap.ccsdk.oran.a1policymanagementservice.repository.Policy;
58 import org.onap.ccsdk.oran.a1policymanagementservice.repository.PolicyType;
59 import org.onap.ccsdk.oran.a1policymanagementservice.repository.PolicyTypes;
60 import org.onap.ccsdk.oran.a1policymanagementservice.repository.Ric;
61 import org.onap.ccsdk.oran.a1policymanagementservice.repository.Ric.RicState;
62 import org.onap.ccsdk.oran.a1policymanagementservice.repository.Rics;
63 import org.onap.ccsdk.oran.a1policymanagementservice.repository.Services;
64 import org.onap.ccsdk.oran.a1policymanagementservice.tasks.RicSupervision;
65 import org.onap.ccsdk.oran.a1policymanagementservice.tasks.ServiceSupervision;
66 import org.onap.ccsdk.oran.a1policymanagementservice.utils.MockA1Client;
67 import org.onap.ccsdk.oran.a1policymanagementservice.utils.MockA1ClientFactory;
68 import org.slf4j.Logger;
69 import org.slf4j.LoggerFactory;
70 import org.springframework.beans.factory.annotation.Autowired;
71 import org.springframework.boot.test.context.SpringBootTest;
72 import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
73 import org.springframework.boot.test.context.TestConfiguration;
74 import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
75 import org.springframework.boot.web.server.LocalServerPort;
76 import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
77 import org.springframework.context.ApplicationContext;
78 import org.springframework.context.annotation.Bean;
79 import org.springframework.http.HttpStatus;
80 import org.springframework.http.ResponseEntity;
81 import org.springframework.test.context.TestPropertySource;
82 import org.springframework.test.context.junit.jupiter.SpringExtension;
83 import org.springframework.web.reactive.function.client.WebClientResponseException;
84
85 import reactor.core.publisher.Mono;
86 import reactor.test.StepVerifier;
87 import reactor.util.annotation.Nullable;
88
89 @ExtendWith(SpringExtension.class)
90 @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
91 @TestPropertySource(properties = { //
92         "server.ssl.key-store=./config/keystore.jks", //
93         "app.webclient.trust-store=./config/truststore.jks"})
94 class ApplicationTest {
95     private static final Logger logger = LoggerFactory.getLogger(ApplicationTest.class);
96
97     @Autowired
98     ApplicationContext context;
99
100     @Autowired
101     private Rics rics;
102
103     @Autowired
104     private Policies policies;
105
106     @Autowired
107     private PolicyTypes policyTypes;
108
109     @Autowired
110     MockA1ClientFactory a1ClientFactory;
111
112     @Autowired
113     RicSupervision supervision;
114
115     @Autowired
116     ApplicationConfig applicationConfig;
117
118     @Autowired
119     Services services;
120
121     private static Gson gson = new GsonBuilder() //
122             .serializeNulls() //
123             .create(); //
124
125     public static class MockApplicationConfig extends ApplicationConfig {
126         @Override
127         public String getLocalConfigurationFilePath() {
128             return ""; // No config file loaded for the test
129         }
130     }
131
132     /**
133      * Overrides the BeanFactory.
134      */
135     @TestConfiguration
136     static class TestBeanFactory {
137         private final PolicyTypes policyTypes = new PolicyTypes();
138         private final Services services = new Services();
139         private final Policies policies = new Policies();
140         MockA1ClientFactory a1ClientFactory = null;
141
142         @Bean
143         public ApplicationConfig getApplicationConfig() {
144             return new MockApplicationConfig();
145         }
146
147         @Bean
148         MockA1ClientFactory getA1ClientFactory() {
149             if (a1ClientFactory == null) {
150                 this.a1ClientFactory = new MockA1ClientFactory(this.policyTypes);
151             }
152             return this.a1ClientFactory;
153         }
154
155         @Bean
156         public PolicyTypes getPolicyTypes() {
157             return this.policyTypes;
158         }
159
160         @Bean
161         Policies getPolicies() {
162             return this.policies;
163         }
164
165         @Bean
166         Services getServices() {
167             return this.services;
168         }
169
170         @Bean
171         public ServiceSupervision getServiceSupervision() {
172             Duration checkInterval = Duration.ofMillis(1);
173             return new ServiceSupervision(this.services, this.policies, this.getA1ClientFactory(), checkInterval);
174         }
175
176         @Bean
177         public ServletWebServerFactory servletContainer() {
178             return new TomcatServletWebServerFactory();
179         }
180
181     }
182
183     @LocalServerPort
184     private int port;
185
186     @BeforeEach
187     void reset() {
188         rics.clear();
189         policies.clear();
190         policyTypes.clear();
191         services.clear();
192         a1ClientFactory.reset();
193     }
194
195     @AfterEach
196     void verifyNoRicLocks() {
197         for (Ric ric : this.rics.getRics()) {
198             ric.getLock().lockBlocking(LockType.EXCLUSIVE);
199             ric.getLock().unlockBlocking();
200             assertThat(ric.getLock().getLockCounter()).isZero();
201             assertThat(ric.getState()).isEqualTo(Ric.RicState.AVAILABLE);
202         }
203     }
204
205     @Test
206     void testGetRics() throws Exception {
207         addRic("ric1");
208         this.addPolicyType("type1", "ric1");
209         String url = "/rics?policyType=type1";
210         String rsp = restClient().get(url).block();
211         assertThat(rsp).contains("ric1");
212
213         // nameless type for ORAN A1 1.1
214         addRic("ric2");
215         this.addPolicyType("", "ric2");
216         url = "/rics?policyType=";
217
218         // This tests also validation of trusted certs restClient(true)
219         rsp = restClient(true).get(url).block();
220         assertThat(rsp).contains("ric2") //
221                 .doesNotContain("ric1") //
222                 .contains("AVAILABLE");
223
224         // All RICs
225         rsp = restClient().get("/rics").block();
226         assertThat(rsp).contains("ric2") //
227                 .contains("ric1");
228
229         // Non existing policy type
230         url = "/rics?policyType=XXXX";
231         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
232     }
233
234     @Test
235     void testSynchronization() throws Exception {
236         // Two polictypes will be put in the NearRT RICs
237         PolicyTypes nearRtRicPolicyTypes = new PolicyTypes();
238         nearRtRicPolicyTypes.put(createPolicyType("typeName"));
239         nearRtRicPolicyTypes.put(createPolicyType("typeName2"));
240         this.a1ClientFactory.setPolicyTypes(nearRtRicPolicyTypes);
241
242         // One type and one instance added to the Policy Management Service's storage
243         final String ric1Name = "ric1";
244         Ric ric1 = addRic(ric1Name);
245         Policy policy2 = addPolicy("policyId2", "typeName", "service", ric1Name);
246         Ric ric2 = addRic("ric2");
247
248         getA1Client(ric1Name).putPolicy(policy2); // put it in the RIC
249         policies.remove(policy2); // Remove it from the repo -> should be deleted in the RIC
250
251         String policyId = "policyId";
252         Policy policy = addPolicy(policyId, "typeName", "service", ric1Name); // This should be created in the RIC
253         supervision.checkAllRics(); // The created policy should be put in the RIC
254
255         // Wait until synch is completed
256         await().untilAsserted(() -> RicState.SYNCHRONIZING.equals(rics.getRic(ric1Name).getState()));
257         await().untilAsserted(() -> RicState.AVAILABLE.equals(rics.getRic(ric1Name).getState()));
258         await().untilAsserted(() -> RicState.AVAILABLE.equals(rics.getRic("ric2").getState()));
259
260         Policies ricPolicies = getA1Client(ric1Name).getPolicies();
261         assertThat(ricPolicies.size()).isEqualTo(1);
262         Policy ricPolicy = ricPolicies.get(policyId);
263         assertThat(ricPolicy.json()).isEqualTo(policy.json());
264
265         // Both types should be in the Policy Management Service's storage after the
266         // synch
267         assertThat(ric1.getSupportedPolicyTypes()).hasSize(2);
268         assertThat(ric2.getSupportedPolicyTypes()).hasSize(2);
269     }
270
271     @Test
272     void testGetRicForManagedElement_thenReturnCorrectRic() throws Exception {
273         String ricName = "ric1";
274         String managedElementId = "kista_1";
275         addRic(ricName, managedElementId);
276
277         String url = "/ric?managedElementId=" + managedElementId;
278         String rsp = restClient().get(url).block();
279         assertThat(rsp).isEqualTo(ricName);
280
281         // test GET RIC for ManagedElement that does not exist
282         url = "/ric?managedElementId=" + "junk";
283         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
284     }
285
286     private String putPolicyUrl(String serviceName, String ricName, String policyTypeName, String policyInstanceId,
287             boolean isTransient) {
288         String url;
289         if (policyTypeName.isEmpty()) {
290             url = "/policy?id=" + policyInstanceId + "&ric=" + ricName + "&service=" + serviceName;
291         } else {
292             url = "/policy?id=" + policyInstanceId + "&ric=" + ricName + "&service=" + serviceName + "&type="
293                     + policyTypeName;
294         }
295         if (isTransient) {
296             url += "&transient=true";
297         }
298         return url;
299     }
300
301     private String putPolicyUrl(String serviceName, String ricName, String policyTypeName, String policyInstanceId) {
302         return putPolicyUrl(serviceName, ricName, policyTypeName, policyInstanceId, false);
303     }
304
305     @Test
306     void testPutPolicy() throws Exception {
307         String serviceName = "service1";
308         String ricName = "ric1";
309         String policyTypeName = "type1";
310         String policyInstanceId = "instance1";
311
312         putService(serviceName);
313         addPolicyType(policyTypeName, ricName);
314
315         // PUT a transient policy
316         String url = putPolicyUrl(serviceName, ricName, policyTypeName, policyInstanceId, true);
317         final String policyBody = jsonString();
318         this.rics.getRic(ricName).setState(Ric.RicState.AVAILABLE);
319
320         restClient().put(url, policyBody).block();
321
322         Policy policy = policies.getPolicy(policyInstanceId);
323         assertThat(policy).isNotNull();
324         assertThat(policy.id()).isEqualTo(policyInstanceId);
325         assertThat(policy.ownerServiceId()).isEqualTo(serviceName);
326         assertThat(policy.ric().id()).isEqualTo("ric1");
327         assertThat(policy.isTransient()).isTrue();
328
329         // Put a non transient policy
330         url = putPolicyUrl(serviceName, ricName, policyTypeName, policyInstanceId);
331         restClient().put(url, policyBody).block();
332         policy = policies.getPolicy(policyInstanceId);
333         assertThat(policy.isTransient()).isFalse();
334
335         url = "/policies";
336         String rsp = restClient().get(url).block();
337         assertThat(rsp).as("Response contains policy instance ID.").contains(policyInstanceId);
338
339         url = "/policy?id=" + policyInstanceId;
340         rsp = restClient().get(url).block();
341         assertThat(rsp).isEqualTo(policyBody);
342
343         // Test of error codes
344         url = putPolicyUrl(serviceName, ricName + "XX", policyTypeName, policyInstanceId);
345         testErrorCode(restClient().put(url, policyBody), HttpStatus.NOT_FOUND);
346
347         url = putPolicyUrl(serviceName, ricName, policyTypeName + "XX", policyInstanceId);
348         addPolicyType(policyTypeName + "XX", "otherRic");
349         testErrorCode(restClient().put(url, policyBody), HttpStatus.NOT_FOUND);
350
351         url = putPolicyUrl(serviceName, ricName, policyTypeName, policyInstanceId);
352         this.rics.getRic(ricName).setState(Ric.RicState.SYNCHRONIZING);
353         testErrorCode(restClient().put(url, policyBody), HttpStatus.LOCKED);
354         this.rics.getRic(ricName).setState(Ric.RicState.AVAILABLE);
355     }
356
357     @Test
358     /**
359      * Test that HttpStatus and body from failing REST call to A1 is passed on to
360      * the caller.
361      *
362      * @throws ServiceException
363      */
364     void testErrorFromRic() throws ServiceException {
365         putService("service1");
366         addPolicyType("type1", "ric1");
367
368         String url = putPolicyUrl("service1", "ric1", "type1", "id1");
369         MockA1Client a1Client = a1ClientFactory.getOrCreateA1Client("ric1");
370         HttpStatus httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;
371         String responseBody = "Refused";
372         byte[] responseBodyBytes = responseBody.getBytes(StandardCharsets.UTF_8);
373
374         WebClientResponseException a1Exception = new WebClientResponseException(httpStatus.value(), "statusText", null,
375                 responseBodyBytes, StandardCharsets.UTF_8, null);
376         doReturn(Mono.error(a1Exception)).when(a1Client).putPolicy(any());
377
378         // PUT Policy
379         testErrorCode(restClient().put(url, "{}"), httpStatus, responseBody);
380
381         // DELETE POLICY
382         this.addPolicy("instance1", "type1", "service1", "ric1");
383         doReturn(Mono.error(a1Exception)).when(a1Client).deletePolicy(any());
384         testErrorCode(restClient().delete("/policy?id=instance1"), httpStatus, responseBody);
385
386         // GET STATUS
387         this.addPolicy("instance1", "type1", "service1", "ric1");
388         doReturn(Mono.error(a1Exception)).when(a1Client).getPolicyStatus(any());
389         testErrorCode(restClient().get("/policy_status?id=instance1"), httpStatus, responseBody);
390
391         // Check that empty response body is OK
392         a1Exception = new WebClientResponseException(httpStatus.value(), "", null, null, null, null);
393         doReturn(Mono.error(a1Exception)).when(a1Client).getPolicyStatus(any());
394         testErrorCode(restClient().get("/policy_status?id=instance1"), httpStatus);
395     }
396
397     @Test
398     void testPutTypelessPolicy() throws Exception {
399         putService("service1");
400         addPolicyType("", "ric1");
401         String url = putPolicyUrl("service1", "ric1", "", "id1");
402         restClient().put(url, jsonString()).block();
403
404         String rsp = restClient().get("/policies").block();
405         List<PolicyInfo> info = parseList(rsp, PolicyInfo.class);
406         assertThat(info).hasSize(1);
407         PolicyInfo policyInfo = info.get(0);
408         assertThat(policyInfo.id).isEqualTo("id1");
409         assertThat(policyInfo.type).isEmpty();
410     }
411
412     @Test
413     void testRefuseToUpdatePolicy() throws Exception {
414         // Test that only the json can be changed for a already created policy
415         // In this case service is attempted to be changed
416         this.addRic("ric1");
417         this.addRic("ricXXX");
418         this.addPolicy("instance1", "type1", "service1", "ric1");
419         this.addPolicy("instance2", "type1", "service1", "ricXXX");
420
421         // Try change ric1 -> ricXXX
422         String urlWrongRic = putPolicyUrl("service1", "ricXXX", "type1", "instance1");
423         testErrorCode(restClient().put(urlWrongRic, jsonString()), HttpStatus.CONFLICT);
424     }
425
426     @Test
427     void testGetPolicy() throws Exception {
428         String url = "/policy?id=id";
429         Policy policy = addPolicy("id", "typeName", "service1", "ric1");
430         {
431             String rsp = restClient().get(url).block();
432             assertThat(rsp).isEqualTo(policy.json());
433         }
434         {
435             policies.remove(policy);
436             testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
437         }
438     }
439
440     @Test
441     void testDeletePolicy() throws Exception {
442         addPolicy("id", "typeName", "service1", "ric1");
443         assertThat(policies.size()).isEqualTo(1);
444
445         String url = "/policy?id=id";
446         ResponseEntity<String> entity = restClient().deleteForEntity(url).block();
447
448         assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.NO_CONTENT);
449         assertThat(policies.size()).isZero();
450
451         // Delete a non existing policy
452         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
453     }
454
455     @Test
456     void testGetPolicySchemas() throws Exception {
457         addPolicyType("type1", "ric1");
458         addPolicyType("type2", "ric2");
459
460         String url = "/policy_schemas";
461         String rsp = this.restClient().get(url).block();
462         assertThat(rsp).contains("type1") //
463                 .contains("{\"title\":\"type2\"}");
464
465         List<String> info = parseSchemas(rsp);
466         assertThat(info).hasSize(2);
467
468         url = "/policy_schemas?ric=ric1";
469         rsp = restClient().get(url).block();
470         assertThat(rsp).contains("type1");
471         info = parseSchemas(rsp);
472         assertThat(info).hasSize(1);
473
474         // Get schema for non existing RIC
475         url = "/policy_schemas?ric=ric1XXX";
476         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
477     }
478
479     @Test
480     void testGetPolicySchema() throws Exception {
481         addPolicyType("type1", "ric1");
482         addPolicyType("type2", "ric2");
483
484         String url = "/policy_schema?id=type1";
485         String rsp = restClient().get(url).block();
486         logger.info(rsp);
487         assertThat(rsp).contains("type1") //
488                 .contains("title");
489
490         // Get non existing schema
491         url = "/policy_schema?id=type1XX";
492         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
493     }
494
495     @Test
496     void testGetPolicyTypes() throws Exception {
497         addPolicyType("type1", "ric1");
498         addPolicyType("type2", "ric2");
499
500         String url = "/policy_types";
501         String rsp = restClient().get(url).block();
502         assertThat(rsp).isEqualTo("[\"type2\",\"type1\"]");
503
504         url = "/policy_types?ric=ric1";
505         rsp = restClient().get(url).block();
506         assertThat(rsp).isEqualTo("[\"type1\"]");
507
508         // Get policy types for non existing RIC
509         url = "/policy_types?ric=ric1XXX";
510         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
511     }
512
513     @Test
514     void testGetPolicies() throws Exception {
515         addPolicy("id1", "type1", "service1");
516
517         String url = "/policies";
518         String rsp = restClient().get(url).block();
519         logger.info(rsp);
520         List<PolicyInfo> info = parseList(rsp, PolicyInfo.class);
521         assertThat(info).hasSize(1);
522         PolicyInfo policyInfo = info.get(0);
523         assert (policyInfo.validate());
524         assertThat(policyInfo.id).isEqualTo("id1");
525         assertThat(policyInfo.type).isEqualTo("type1");
526         assertThat(policyInfo.service).isEqualTo("service1");
527     }
528
529     @Test
530     void testGetPoliciesFilter() throws Exception {
531         addPolicy("id1", "type1", "service1");
532         addPolicy("id2", "type1", "service2");
533         addPolicy("id3", "type2", "service1");
534
535         String url = "/policies?type=type1";
536         String rsp = restClient().get(url).block();
537         logger.info(rsp);
538         assertThat(rsp).contains("id1") //
539                 .contains("id2") //
540                 .doesNotContain("id3");
541
542         url = "/policies?type=type1&service=service2";
543         rsp = restClient().get(url).block();
544         logger.info(rsp);
545         assertThat(rsp).doesNotContain("id1") //
546                 .contains("id2") //
547                 .doesNotContain("id3");
548
549         // Test get policies for non existing type
550         url = "/policies?type=type1XXX";
551         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
552
553         // Test get policies for non existing RIC
554         url = "/policies?ric=XXX";
555         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
556     }
557
558     @Test
559     void testGetPolicyIdsFilter() throws Exception {
560         addPolicy("id1", "type1", "service1", "ric1");
561         addPolicy("id2", "type1", "service2", "ric1");
562         addPolicy("id3", "type2", "service1", "ric1");
563
564         String url = "/policy_ids?type=type1";
565         String rsp = restClient().get(url).block();
566         logger.info(rsp);
567         assertThat(rsp).contains("id1") //
568                 .contains("id2") //
569                 .doesNotContain("id3");
570
571         url = "/policy_ids?type=type1&service=service1&ric=ric1";
572         rsp = restClient().get(url).block();
573         assertThat(rsp).isEqualTo("[\"id1\"]");
574
575         // Test get policy ids for non existing type
576         url = "/policy_ids?type=type1XXX";
577         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
578
579         // Test get policy ids for non existing RIC
580         url = "/policy_ids?ric=XXX";
581         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
582     }
583
584     @Test
585     void testPutAndGetService() throws Exception {
586         // PUT
587         String serviceName = "name";
588         putService(serviceName, 0, HttpStatus.CREATED);
589         putService(serviceName, 0, HttpStatus.OK);
590
591         // GET one service
592         String url = "/services?name=name";
593         String rsp = restClient().get(url).block();
594         List<ServiceStatus> info = parseList(rsp, ServiceStatus.class);
595         assertThat(info).hasSize(1);
596         ServiceStatus status = info.iterator().next();
597         assertThat(status.keepAliveIntervalSeconds).isZero();
598         assertThat(status.serviceName).isEqualTo(serviceName);
599
600         // GET (all)
601         url = "/services";
602         rsp = restClient().get(url).block();
603         assertThat(rsp).as("Response contains service name").contains(serviceName);
604         logger.info(rsp);
605
606         // Keep alive
607         url = "/services/keepalive?name=name";
608         ResponseEntity<String> entity = restClient().putForEntity(url).block();
609         assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
610
611         // DELETE service
612         assertThat(services.size()).isEqualTo(1);
613         url = "/services?name=name";
614         restClient().delete(url).block();
615         assertThat(services.size()).isZero();
616
617         // Keep alive, no registered service
618         testErrorCode(restClient().put("/services/keepalive?name=name", ""), HttpStatus.NOT_FOUND);
619
620         // PUT servive with bad payload
621         testErrorCode(restClient().put("/service", "crap"), HttpStatus.BAD_REQUEST);
622         testErrorCode(restClient().put("/service", "{}"), HttpStatus.BAD_REQUEST);
623         testErrorCode(restClient().put("/service", createServiceJson(serviceName, -123)), HttpStatus.BAD_REQUEST);
624         testErrorCode(restClient().put("/service", createServiceJson(serviceName, 0, "missing.portandprotocol.com")),
625                 HttpStatus.BAD_REQUEST);
626
627         // GET non existing service
628         testErrorCode(restClient().get("/services?name=XXX"), HttpStatus.NOT_FOUND);
629     }
630
631     @Test
632     void testServiceSupervision() throws Exception {
633         putService("service1", 1, HttpStatus.CREATED);
634         addPolicyType("type1", "ric1");
635
636         String url = putPolicyUrl("service1", "ric1", "type1", "instance1");
637         final String policyBody = jsonString();
638         restClient().put(url, policyBody).block();
639
640         assertThat(policies.size()).isEqualTo(1);
641         assertThat(services.size()).isEqualTo(1);
642
643         // Timeout after ~1 second
644         await().untilAsserted(() -> assertThat(policies.size()).isZero());
645         assertThat(services.size()).isZero();
646     }
647
648     @Test
649     void testGetPolicyStatus() throws Exception {
650         addPolicy("id", "typeName", "service1", "ric1");
651         assertThat(policies.size()).isEqualTo(1);
652
653         String url = "/policy_status?id=id";
654         String rsp = restClient().get(url).block();
655         assertThat(rsp).isEqualTo("OK");
656
657         // GET non existing policy status
658         url = "/policy_status?id=XXX";
659         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
660     }
661
662     private Policy addPolicy(String id, String typeName, String service, String ric) throws ServiceException {
663         addRic(ric);
664         Policy policy = ImmutablePolicy.builder() //
665                 .id(id) //
666                 .json(jsonString()) //
667                 .ownerServiceId(service) //
668                 .ric(rics.getRic(ric)) //
669                 .type(addPolicyType(typeName, ric)) //
670                 .lastModified(Instant.now()) //
671                 .isTransient(false) //
672                 .build();
673         policies.put(policy);
674         return policy;
675     }
676
677     private Policy addPolicy(String id, String typeName, String service) throws ServiceException {
678         return addPolicy(id, typeName, service, "ric");
679     }
680
681     private String createServiceJson(String name, long keepAliveIntervalSeconds) {
682         return createServiceJson(name, keepAliveIntervalSeconds, "https://examples.javacodegeeks.com/core-java/");
683     }
684
685     private String createServiceJson(String name, long keepAliveIntervalSeconds, String url) {
686         ServiceRegistrationInfo service = new ServiceRegistrationInfo(name, keepAliveIntervalSeconds, url);
687
688         String json = gson.toJson(service);
689         return json;
690     }
691
692     private void putService(String name) {
693         putService(name, 0, null);
694     }
695
696     private void putService(String name, long keepAliveIntervalSeconds, @Nullable HttpStatus expectedStatus) {
697         String url = "/service";
698         String body = createServiceJson(name, keepAliveIntervalSeconds);
699         ResponseEntity<String> resp = restClient().putForEntity(url, body).block();
700         if (expectedStatus != null) {
701             assertEquals(expectedStatus, resp.getStatusCode(), "");
702         }
703     }
704
705     private String baseUrl() {
706         return "https://localhost:" + port;
707     }
708
709     private String jsonString() {
710         return "{\"servingCellNrcgi\":\"1\"}";
711     }
712
713     private AsyncRestClient restClient(boolean useTrustValidation) {
714         WebClientConfig config = this.applicationConfig.getWebClientConfig();
715         config = ImmutableWebClientConfig.builder() //
716                 .keyStoreType(config.keyStoreType()) //
717                 .keyStorePassword(config.keyStorePassword()) //
718                 .keyStore(config.keyStore()) //
719                 .keyPassword(config.keyPassword()) //
720                 .isTrustStoreUsed(useTrustValidation) //
721                 .trustStore(config.trustStore()) //
722                 .trustStorePassword(config.trustStorePassword()) //
723                 .build();
724
725         return new AsyncRestClient(baseUrl(), config);
726     }
727
728     private AsyncRestClient restClient() {
729         return restClient(false);
730     }
731
732     private void testErrorCode(Mono<?> request, HttpStatus expStatus) {
733         testErrorCode(request, expStatus, "");
734     }
735
736     private void testErrorCode(Mono<?> request, HttpStatus expStatus, String responseContains) {
737         StepVerifier.create(request) //
738                 .expectSubscription() //
739                 .expectErrorMatches(t -> checkWebClientError(t, expStatus, responseContains)) //
740                 .verify();
741     }
742
743     private boolean checkWebClientError(Throwable throwable, HttpStatus expStatus, String responseContains) {
744         assertTrue(throwable instanceof WebClientResponseException);
745         WebClientResponseException responseException = (WebClientResponseException) throwable;
746         assertThat(responseException.getStatusCode()).isEqualTo(expStatus);
747         assertThat(responseException.getResponseBodyAsString()).contains(responseContains);
748         return true;
749     }
750
751     private MockA1Client getA1Client(String ricName) throws ServiceException {
752         return a1ClientFactory.getOrCreateA1Client(ricName);
753     }
754
755     private PolicyType createPolicyType(String policyTypeName) {
756         return ImmutablePolicyType.builder() //
757                 .id(policyTypeName) //
758                 .schema("{\"title\":\"" + policyTypeName + "\"}") //
759                 .build();
760     }
761
762     private PolicyType addPolicyType(String policyTypeName, String ricName) {
763         PolicyType type = createPolicyType(policyTypeName);
764         policyTypes.put(type);
765         addRic(ricName).addSupportedPolicyType(type);
766         return type;
767     }
768
769     private Ric addRic(String ricName) {
770         return addRic(ricName, null);
771     }
772
773     private Ric addRic(String ricName, String managedElement) {
774         if (rics.get(ricName) != null) {
775             return rics.get(ricName);
776         }
777         List<String> mes = new ArrayList<>();
778         if (managedElement != null) {
779             mes.add(managedElement);
780         }
781         RicConfig conf = ImmutableRicConfig.builder() //
782                 .ricId(ricName) //
783                 .baseUrl(ricName) //
784                 .managedElementIds(mes) //
785                 .controllerName("") //
786                 .build();
787         Ric ric = new Ric(conf);
788         ric.setState(Ric.RicState.AVAILABLE);
789         this.rics.put(ric);
790         return ric;
791     }
792
793     private static <T> List<T> parseList(String jsonString, Class<T> clazz) {
794         List<T> result = new ArrayList<>();
795         JsonArray jsonArr = JsonParser.parseString(jsonString).getAsJsonArray();
796         for (JsonElement jsonElement : jsonArr) {
797             T json = gson.fromJson(jsonElement.toString(), clazz);
798             result.add(json);
799         }
800         return result;
801     }
802
803     private static List<String> parseSchemas(String jsonString) {
804         JsonArray arrayOfSchema = JsonParser.parseString(jsonString).getAsJsonArray();
805         List<String> result = new ArrayList<>();
806         for (JsonElement schemaObject : arrayOfSchema) {
807             result.add(schemaObject.toString());
808         }
809         return result;
810     }
811 }