Remove new code smells
[nonrtric.git] / policy-agent / src / test / java / org / oransc / policyagent / ApplicationTest.java
1 /*-
2  * ========================LICENSE_START=================================
3  * O-RAN-SC
4  * %%
5  * Copyright (C) 2019 Nordix Foundation
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.oransc.policyagent;
22
23 import static org.assertj.core.api.Assertions.assertThat;
24 import static org.awaitility.Awaitility.await;
25
26 import com.google.gson.Gson;
27 import com.google.gson.GsonBuilder;
28 import com.google.gson.JsonArray;
29 import com.google.gson.JsonElement;
30 import com.google.gson.JsonParser;
31
32 import java.io.IOException;
33 import java.time.Duration;
34 import java.time.Instant;
35 import java.util.ArrayList;
36 import java.util.List;
37 import java.util.concurrent.atomic.AtomicInteger;
38
39 import org.junit.jupiter.api.AfterEach;
40 import org.junit.jupiter.api.BeforeEach;
41 import org.junit.jupiter.api.Test;
42 import org.junit.jupiter.api.extension.ExtendWith;
43 import org.oransc.policyagent.configuration.ApplicationConfig;
44 import org.oransc.policyagent.configuration.ImmutableRicConfig;
45 import org.oransc.policyagent.configuration.RicConfig;
46 import org.oransc.policyagent.controllers.PolicyInfo;
47 import org.oransc.policyagent.controllers.ServiceRegistrationInfo;
48 import org.oransc.policyagent.controllers.ServiceStatus;
49 import org.oransc.policyagent.exceptions.ServiceException;
50 import org.oransc.policyagent.repository.ImmutablePolicy;
51 import org.oransc.policyagent.repository.ImmutablePolicyType;
52 import org.oransc.policyagent.repository.Lock.LockType;
53 import org.oransc.policyagent.repository.Policies;
54 import org.oransc.policyagent.repository.Policy;
55 import org.oransc.policyagent.repository.PolicyType;
56 import org.oransc.policyagent.repository.PolicyTypes;
57 import org.oransc.policyagent.repository.Ric;
58 import org.oransc.policyagent.repository.Ric.RicState;
59 import org.oransc.policyagent.repository.Rics;
60 import org.oransc.policyagent.repository.Services;
61 import org.oransc.policyagent.tasks.RepositorySupervision;
62 import org.oransc.policyagent.utils.MockA1Client;
63 import org.oransc.policyagent.utils.MockA1ClientFactory;
64 import org.springframework.beans.factory.annotation.Autowired;
65 import org.springframework.boot.test.context.SpringBootTest;
66 import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
67 import org.springframework.boot.test.context.TestConfiguration;
68 import org.springframework.boot.web.server.LocalServerPort;
69 import org.springframework.context.ApplicationContext;
70 import org.springframework.context.annotation.Bean;
71 import org.springframework.http.HttpEntity;
72 import org.springframework.http.HttpHeaders;
73 import org.springframework.http.HttpMethod;
74 import org.springframework.http.HttpStatus;
75 import org.springframework.http.HttpStatus.Series;
76 import org.springframework.http.MediaType;
77 import org.springframework.http.ResponseEntity;
78 import org.springframework.http.client.ClientHttpResponse;
79 import org.springframework.test.context.junit.jupiter.SpringExtension;
80 import org.springframework.web.client.ResponseErrorHandler;
81 import org.springframework.web.client.RestTemplate;
82
83 @ExtendWith(SpringExtension.class)
84 @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
85 public class ApplicationTest {
86     @Autowired
87     ApplicationContext context;
88
89     @Autowired
90     private Rics rics;
91
92     @Autowired
93     private Policies policies;
94
95     @Autowired
96     private PolicyTypes policyTypes;
97
98     @Autowired
99     MockA1ClientFactory a1ClientFactory;
100
101     @Autowired
102     RepositorySupervision supervision;
103
104     @Autowired
105     Services services;
106
107     private static Gson gson = new GsonBuilder() //
108         .serializeNulls() //
109         .create(); //
110
111     public static class MockApplicationConfig extends ApplicationConfig {
112         @Override
113         public String getLocalConfigurationFilePath() {
114             return ""; // No config file loaded for the test
115         }
116     }
117
118     /**
119      * Overrides the BeanFactory.
120      */
121     @TestConfiguration
122     static class TestBeanFactory {
123         private final PolicyTypes policyTypes = new PolicyTypes();
124
125         @Bean
126         public ApplicationConfig getApplicationConfig() {
127             return new MockApplicationConfig();
128         }
129
130         @Bean
131         MockA1ClientFactory getA1ClientFactory() {
132             return new MockA1ClientFactory(this.policyTypes);
133         }
134
135         @Bean
136         public PolicyTypes getPolicyTypes() {
137             return this.policyTypes;
138         }
139     }
140
141     @LocalServerPort
142     private int port;
143
144     private final RestTemplate restTemplate = new RestTemplate();
145
146     public class RestTemplateResponseErrorHandler implements ResponseErrorHandler {
147
148         @Override
149         public boolean hasError(ClientHttpResponse httpResponse) throws IOException {
150             return (httpResponse.getStatusCode().series() == Series.CLIENT_ERROR
151                 || httpResponse.getStatusCode().series() == Series.SERVER_ERROR);
152         }
153
154         @Override
155         public void handleError(ClientHttpResponse httpResponse) throws IOException {
156             System.out.println("Error " + httpResponse.toString());
157         }
158     }
159
160     private void setRestErrorhandler() {
161         restTemplate.setErrorHandler(new RestTemplateResponseErrorHandler());
162     }
163
164     @BeforeEach
165     public void reset() {
166         rics.clear();
167         policies.clear();
168         policyTypes.clear();
169         services.clear();
170     }
171
172     @AfterEach
173     public void verifyNoRicLocks() {
174         for (Ric ric : this.rics.getRics()) {
175             ric.getLock().lockBlocking(LockType.EXCLUSIVE);
176             ric.getLock().unlockBlocking();
177             assertThat(ric.getLock().getLockCounter()).isEqualTo(0);
178             assertThat(ric.getState()).isEqualTo(Ric.RicState.IDLE);
179         }
180     }
181
182     @Test
183     public void testGetRics() throws Exception {
184         addRic("kista_1");
185         String url = baseUrl() + "/rics";
186         String rsp = this.restTemplate.getForObject(url, String.class);
187         System.out.println(rsp);
188         assertThat(rsp).contains("kista_1");
189
190         url = baseUrl() + "/rics?policyType=STD_PolicyModelUnconstrained_0.2.0";
191         rsp = this.restTemplate.getForObject(url, String.class);
192         assertThat(rsp).isEqualTo("[]");
193     }
194
195     @Test
196     public void testRecovery() throws Exception {
197         addRic("ric").setState(Ric.RicState.UNDEFINED);
198         String ricName = "ric";
199         Policy policy2 = addPolicy("policyId2", "typeName", "service", ricName);
200
201         getA1Client(ricName).putPolicy(policy2); // put it in the RIC
202         policies.remove(policy2); // Remove it from the repo -> should be deleted in the RIC
203
204         String policyId = "policyId";
205         Policy policy = addPolicy(policyId, "typeName", "service", ricName); // This should be created in the RIC
206         supervision.checkAllRics(); // The created policy should be put in the RIC
207         await().untilAsserted(() -> RicState.SYNCHRONIZING.equals(rics.getRic(ricName).getState()));
208         await().untilAsserted(() -> RicState.IDLE.equals(rics.getRic(ricName).getState()));
209
210         Policies ricPolicies = getA1Client(ricName).getPolicies();
211         assertThat(ricPolicies.size()).isEqualTo(1);
212         Policy ricPolicy = ricPolicies.get(policyId);
213         assertThat(ricPolicy.json()).isEqualTo(policy.json());
214     }
215
216     @Test
217     public void testGetRicForManagedElement_thenReturnCorrectRic() throws Exception {
218         String ricName = "ric1";
219         String managedElementId = "kista_1";
220         addRic(ricName, managedElementId);
221
222         String url = baseUrl() + "/ric?managedElementId=" + managedElementId;
223         String rsp = this.restTemplate.getForObject(url, String.class);
224
225         assertThat(rsp).isEqualTo(ricName);
226     }
227
228     @Test
229     public void testGetRicForManagedElementThatDoesNotExist() throws Exception {
230         this.setRestErrorhandler();
231         String url = baseUrl() + "/ric?managedElementId=kista_1";
232         ResponseEntity<String> entity = this.restTemplate.getForEntity(url, String.class);
233         assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
234     }
235
236     @Test
237     public void testPutPolicy() throws Exception {
238         String serviceName = "service1";
239         String ricName = "ric1";
240         String policyTypeName = "type1";
241         String policyInstanceId = "instance1";
242
243         putService(serviceName);
244         addPolicyType(policyTypeName, ricName);
245
246         String url = baseUrl() + "/policy?type=" + policyTypeName + "&instance=" + policyInstanceId + "&ric=" + ricName
247             + "&service=" + serviceName;
248         final String json = jsonString();
249         this.rics.getRic(ricName).setState(Ric.RicState.IDLE);
250
251         this.restTemplate.put(url, createJsonHttpEntity(json));
252
253         Policy policy = policies.getPolicy(policyInstanceId);
254         assertThat(policy).isNotNull();
255         assertThat(policy.id()).isEqualTo(policyInstanceId);
256         assertThat(policy.ownerServiceName()).isEqualTo(serviceName);
257         assertThat(policy.ric().name()).isEqualTo("ric1");
258
259         url = baseUrl() + "/policies";
260         String rsp = this.restTemplate.getForObject(url, String.class);
261         assertThat(rsp.contains(policyInstanceId)).isTrue();
262
263     }
264
265     @Test
266     public void testRefuseToUpdatePolicy() throws Exception {
267         // Test that only the json can be changed for a already created policy
268         // In this case service is attempted to be changed
269         this.addRic("ric1");
270         this.addRic("ricXXX");
271
272         this.addPolicy("instance1", "type1", "service1", "ric1");
273         this.setRestErrorhandler();
274         String urlWrongRic = baseUrl() + "/policy?type=type1&instance=instance1&ric=ricXXX&service=service1";
275         ResponseEntity<String> entity = this.putForEntity(urlWrongRic, jsonString());
276         assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.METHOD_NOT_ALLOWED);
277
278         Policy policy = policies.getPolicy("instance1");
279         assertThat(policy.ric().name()).isEqualTo("ric1"); // Not changed
280     }
281
282     @Test
283     public void testGetPolicy() throws Exception {
284         String url = baseUrl() + "/policy?instance=id";
285         Policy policy = addPolicy("id", "typeName", "service1", "ric1");
286         {
287             String rsp = this.restTemplate.getForObject(url, String.class);
288             assertThat(rsp).isEqualTo(policy.json());
289         }
290         {
291             policies.remove(policy);
292             ResponseEntity<String> rsp = this.restTemplate.getForEntity(url, String.class);
293             assertThat(rsp.getStatusCodeValue()).isEqualTo(HttpStatus.NO_CONTENT.value());
294         }
295     }
296
297     @Test
298     public void testDeletePolicy() throws Exception {
299         String url = baseUrl() + "/policy?instance=id";
300         addPolicy("id", "typeName", "service1", "ric1");
301         assertThat(policies.size()).isEqualTo(1);
302
303         this.restTemplate.delete(url);
304
305         assertThat(policies.size()).isEqualTo(0);
306     }
307
308     @Test
309     public void testGetPolicySchemas() throws Exception {
310         addPolicyType("type1", "ric1");
311         addPolicyType("type2", "ric2");
312
313         String url = baseUrl() + "/policy_schemas";
314         String rsp = this.restTemplate.getForObject(url, String.class);
315         System.out.println("*** " + rsp);
316         assertThat(rsp).contains("type1");
317         assertThat(rsp).contains("[{\"title\":\"type2\"}");
318
319         List<String> info = parseSchemas(rsp);
320         assertThat(info.size()).isEqualTo(2);
321
322         url = baseUrl() + "/policy_schemas?ric=ric1";
323         rsp = this.restTemplate.getForObject(url, String.class);
324         assertThat(rsp).contains("type1");
325         info = parseSchemas(rsp);
326         assertThat(info.size()).isEqualTo(1);
327     }
328
329     @Test
330     public void testGetPolicySchema() throws Exception {
331         addPolicyType("type1", "ric1");
332         addPolicyType("type2", "ric2");
333
334         String url = baseUrl() + "/policy_schema?id=type1";
335         String rsp = this.restTemplate.getForObject(url, String.class);
336         System.out.println(rsp);
337         assertThat(rsp).contains("type1");
338         assertThat(rsp).contains("title");
339     }
340
341     @Test
342     public void testGetPolicyTypes() throws Exception {
343         addPolicyType("type1", "ric1");
344         addPolicyType("type2", "ric2");
345
346         String url = baseUrl() + "/policy_types";
347         String rsp = this.restTemplate.getForObject(url, String.class);
348         assertThat(rsp).isEqualTo("[\"type2\",\"type1\"]");
349
350         url = baseUrl() + "/policy_types?ric=ric1";
351         rsp = this.restTemplate.getForObject(url, String.class);
352         assertThat(rsp).isEqualTo("[\"type1\"]");
353     }
354
355     @Test
356     public void testGetPolicies() throws Exception {
357         reset();
358         String url = baseUrl() + "/policies";
359         addPolicy("id1", "type1", "service1");
360
361         String rsp = this.restTemplate.getForObject(url, String.class);
362         System.out.println(rsp);
363         List<PolicyInfo> info = parseList(rsp, PolicyInfo.class);
364         assertThat(info).size().isEqualTo(1);
365         PolicyInfo policyInfo = info.get(0);
366         assert (policyInfo.validate());
367         assertThat(policyInfo.id).isEqualTo("id1");
368         assertThat(policyInfo.type).isEqualTo("type1");
369         assertThat(policyInfo.service).isEqualTo("service1");
370     }
371
372     @Test
373     public void testGetPoliciesFilter() throws Exception {
374         addPolicy("id1", "type1", "service1");
375         addPolicy("id2", "type1", "service2");
376         addPolicy("id3", "type2", "service1");
377
378         String url = baseUrl() + "/policies?type=type1";
379         String rsp = this.restTemplate.getForObject(url, String.class);
380         System.out.println(rsp);
381         assertThat(rsp).contains("id1");
382         assertThat(rsp).contains("id2");
383         assertThat(rsp.contains("id3")).isFalse();
384
385         url = baseUrl() + "/policies?type=type1&service=service2";
386         rsp = this.restTemplate.getForObject(url, String.class);
387         System.out.println(rsp);
388         assertThat(rsp.contains("id1")).isFalse();
389         assertThat(rsp).contains("id2");
390         assertThat(rsp.contains("id3")).isFalse();
391     }
392
393     @Test
394     public void testPutAndGetService() throws Exception {
395         // PUT
396         putService("name");
397
398         // GET
399         String url = baseUrl() + "/services?serviceName=name";
400         String rsp = this.restTemplate.getForObject(url, String.class);
401         List<ServiceStatus> info = parseList(rsp, ServiceStatus.class);
402         assertThat(info.size()).isEqualTo(1);
403         ServiceStatus status = info.iterator().next();
404         assertThat(status.keepAliveIntervalSeconds).isEqualTo(1);
405         assertThat(status.serviceName).isEqualTo("name");
406
407         // GET (all)
408         url = baseUrl() + "/services";
409         rsp = this.restTemplate.getForObject(url, String.class);
410         assertThat(rsp.contains("name")).isTrue();
411         System.out.println(rsp);
412
413         // Keep alive
414         url = baseUrl() + "/services/keepalive?name=name";
415         ResponseEntity<String> entity = this.restTemplate.postForEntity(url, null, String.class);
416         assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
417
418         // DELETE
419         assertThat(services.size()).isEqualTo(1);
420         url = baseUrl() + "/services?name=name";
421         this.restTemplate.delete(url);
422         assertThat(services.size()).isEqualTo(0);
423
424         // Keep alive, no registerred service
425         url = baseUrl() + "/services/keepalive?name=name";
426         setRestErrorhandler();
427         entity = this.restTemplate.postForEntity(url, null, String.class);
428         assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
429     }
430
431     @Test
432     public void testGetPolicyStatus() throws Exception {
433         addPolicy("id", "typeName", "service1", "ric1");
434         assertThat(policies.size()).isEqualTo(1);
435
436         String url = baseUrl() + "/policy_status?instance=id";
437         String rsp = this.restTemplate.getForObject(url, String.class);
438         assertThat(rsp.equals("OK")).isTrue();
439     }
440
441     private Policy addPolicy(String id, String typeName, String service, String ric) throws ServiceException {
442         addRic(ric);
443         Policy p = ImmutablePolicy.builder().id(id) //
444             .json(jsonString()) //
445             .ownerServiceName(service) //
446             .ric(rics.getRic(ric)) //
447             .type(addPolicyType(typeName, ric)) //
448             .lastModified("lastModified").build();
449         policies.put(p);
450         return p;
451     }
452
453     private Policy addPolicy(String id, String typeName, String service) throws ServiceException {
454         return addPolicy(id, typeName, service, "ric");
455     }
456
457     private String createServiceJson(String name) {
458         ServiceRegistrationInfo service = new ServiceRegistrationInfo(name, 1, "callbackUrl");
459
460         String json = gson.toJson(service);
461         return json;
462     }
463
464     private void putService(String name) {
465         String url = baseUrl() + "/service";
466         HttpEntity<String> entity = createJsonHttpEntity(createServiceJson(name));
467         this.restTemplate.put(url, entity);
468     }
469
470     private String baseUrl() {
471         return "http://localhost:" + port;
472     }
473
474     private String jsonString() {
475         return "{\n  \"servingCellNrcgi\": \"1\"\n }";
476     }
477
478     private static class ConcurrencyTestRunnable implements Runnable {
479         private final RestTemplate restTemplate = new RestTemplate();
480         private final String baseUrl;
481         static AtomicInteger nextCount = new AtomicInteger(0);
482         private final int count;
483         private final RepositorySupervision supervision;
484
485         ConcurrencyTestRunnable(String baseUrl, RepositorySupervision supervision) {
486             this.baseUrl = baseUrl;
487             this.count = nextCount.incrementAndGet();
488             this.supervision = supervision;
489         }
490
491         @Override
492         public void run() {
493             for (int i = 0; i < 100; ++i) {
494                 if (i % 10 == 0) {
495                     this.supervision.checkAllRics();
496                 }
497                 String name = "policy:" + count + ":" + i;
498                 putPolicy(name);
499                 deletePolicy(name);
500             }
501         }
502
503         private void putPolicy(String name) {
504             String putUrl = baseUrl + "/policy?type=type1&instance=" + name + "&ric=ric1&service=service1";
505             this.restTemplate.put(putUrl, createJsonHttpEntity("{}"));
506         }
507
508         private void deletePolicy(String name) {
509             String deleteUrl = baseUrl + "/policy?instance=" + name;
510             this.restTemplate.delete(deleteUrl);
511         }
512     }
513
514     @Test
515     public void testConcurrency() throws Exception {
516         final Instant startTime = Instant.now();
517         List<Thread> threads = new ArrayList<>();
518         addRic("ric1");
519         addPolicyType("type1", "ric1");
520
521         for (int i = 0; i < 100; ++i) {
522             Thread t = new Thread(new ConcurrencyTestRunnable(baseUrl(), this.supervision), "TestThread_" + i);
523             t.start();
524             threads.add(t);
525         }
526         for (Thread t : threads) {
527             t.join();
528         }
529         assertThat(policies.size()).isEqualTo(0);
530         System.out.println("Concurrency test took " + Duration.between(startTime, Instant.now()));
531     }
532
533     private MockA1Client getA1Client(String ricName) throws ServiceException {
534         return a1ClientFactory.getOrCreateA1Client(ricName);
535     }
536
537     private PolicyType addPolicyType(String policyTypeName, String ricName) {
538         PolicyType type = ImmutablePolicyType.builder() //
539             .name(policyTypeName) //
540             .schema("{\"title\":\"" + policyTypeName + "\"}") //
541             .build();
542
543         policyTypes.put(type);
544         addRic(ricName).addSupportedPolicyType(type);
545         return type;
546     }
547
548     private Ric addRic(String ricName) {
549         return addRic(ricName, null);
550     }
551
552     private Ric addRic(String ricName, String managedElement) {
553         if (rics.get(ricName) != null) {
554             return rics.get(ricName);
555         }
556         List<String> mes = new ArrayList<>();
557         if (managedElement != null) {
558             mes.add(managedElement);
559         }
560         RicConfig conf = ImmutableRicConfig.builder() //
561             .name(ricName) //
562             .baseUrl(ricName) //
563             .managedElementIds(mes) //
564             .build();
565         Ric ric = new Ric(conf);
566         ric.setState(Ric.RicState.IDLE);
567         this.rics.put(ric);
568         return ric;
569     }
570
571     private static HttpEntity<String> createJsonHttpEntity(String content) {
572         HttpHeaders headers = new HttpHeaders();
573         headers.setContentType(MediaType.APPLICATION_JSON);
574         return new HttpEntity<String>(content, headers);
575     }
576
577     private ResponseEntity<String> putForEntity(String url, String jsonBody) {
578         return restTemplate.exchange(url, HttpMethod.PUT, createJsonHttpEntity(jsonBody), String.class);
579     }
580
581     private static <T> List<T> parseList(String jsonString, Class<T> clazz) {
582         List<T> result = new ArrayList<>();
583         JsonArray jsonArr = JsonParser.parseString(jsonString).getAsJsonArray();
584         for (JsonElement jsonElement : jsonArr) {
585             T o = gson.fromJson(jsonElement.toString(), clazz);
586             result.add(o);
587         }
588         return result;
589     }
590
591     private static List<String> parseSchemas(String jsonString) {
592         JsonArray arrayOfSchema = JsonParser.parseString(jsonString).getAsJsonArray();
593         List<String> result = new ArrayList<>();
594         for (JsonElement schemaObject : arrayOfSchema) {
595             result.add(schemaObject.toString());
596         }
597         return result;
598     }
599 }