4c8448849fb2ddd17919fec19aa5e6e28fa08ab2
[nonrtric.git] / enrichment-coordinator-service / src / test / java / org / oransc / enrichment / ApplicationTest.java
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.oransc.enrichment;
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.assertTrue;
26
27 import com.fasterxml.jackson.core.JsonProcessingException;
28 import com.fasterxml.jackson.databind.JsonMappingException;
29 import com.google.gson.Gson;
30 import com.google.gson.GsonBuilder;
31 import com.google.gson.JsonParser;
32
33 import java.util.ArrayList;
34 import java.util.Collection;
35
36 import org.junit.jupiter.api.AfterEach;
37 import org.junit.jupiter.api.BeforeEach;
38 import org.junit.jupiter.api.Test;
39 import org.junit.jupiter.api.extension.ExtendWith;
40 import org.oransc.enrichment.clients.AsyncRestClient;
41 import org.oransc.enrichment.clients.ProducerJobInfo;
42 import org.oransc.enrichment.configuration.ApplicationConfig;
43 import org.oransc.enrichment.configuration.ImmutableWebClientConfig;
44 import org.oransc.enrichment.configuration.WebClientConfig;
45 import org.oransc.enrichment.controller.ProducerSimulatorController;
46 import org.oransc.enrichment.controllers.consumer.ConsumerConsts;
47 import org.oransc.enrichment.controllers.consumer.ConsumerEiJobInfo;
48 import org.oransc.enrichment.controllers.producer.ProducerConsts;
49 import org.oransc.enrichment.controllers.producer.ProducerRegistrationInfo;
50 import org.oransc.enrichment.controllers.producer.ProducerRegistrationInfo.ProducerEiTypeRegistrationInfo;
51 import org.oransc.enrichment.exceptions.ServiceException;
52 import org.oransc.enrichment.repository.EiJob;
53 import org.oransc.enrichment.repository.EiJobs;
54 import org.oransc.enrichment.repository.EiProducers;
55 import org.oransc.enrichment.repository.EiType;
56 import org.oransc.enrichment.repository.EiTypes;
57 import org.slf4j.Logger;
58 import org.slf4j.LoggerFactory;
59 import org.springframework.beans.factory.annotation.Autowired;
60 import org.springframework.boot.test.context.SpringBootTest;
61 import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
62 import org.springframework.boot.test.context.TestConfiguration;
63 import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
64 import org.springframework.boot.web.server.LocalServerPort;
65 import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
66 import org.springframework.context.ApplicationContext;
67 import org.springframework.context.annotation.Bean;
68 import org.springframework.http.HttpStatus;
69 import org.springframework.http.MediaType;
70 import org.springframework.http.ResponseEntity;
71 import org.springframework.test.context.TestPropertySource;
72 import org.springframework.test.context.junit.jupiter.SpringExtension;
73 import org.springframework.web.reactive.function.client.WebClientResponseException;
74
75 import reactor.core.publisher.Mono;
76 import reactor.test.StepVerifier;
77
78 @ExtendWith(SpringExtension.class)
79 @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
80 @TestPropertySource(
81     properties = { //
82         "server.ssl.key-store=./config/keystore.jks", //
83         "app.webclient.trust-store=./config/truststore.jks"})
84 class ApplicationTest {
85     private static final Logger logger = LoggerFactory.getLogger(ApplicationTest.class);
86     private final String EI_TYPE_ID = "typeId";
87     private final String EI_JOB_PROPERTY = "\"property1\"";
88
89     @Autowired
90     ApplicationContext context;
91
92     @Autowired
93     EiJobs eiJobs;
94
95     @Autowired
96     EiTypes eiTypes;
97
98     @Autowired
99     EiProducers eiProducers;
100
101     @Autowired
102     ApplicationConfig applicationConfig;
103
104     @Autowired
105     ProducerSimulatorController producerSimulator;
106
107     private static Gson gson = new GsonBuilder() //
108         .serializeNulls() //
109         .create(); //
110
111     /**
112      * Overrides the BeanFactory.
113      */
114     @TestConfiguration
115     static class TestBeanFactory {
116         @Bean
117         public ServletWebServerFactory servletContainer() {
118             return new TomcatServletWebServerFactory();
119         }
120     }
121
122     @LocalServerPort
123     private int port;
124
125     @BeforeEach
126     void reset() {
127         this.eiJobs.clear();
128         this.eiTypes.clear();
129         this.eiProducers.clear();
130         this.producerSimulator.getTestResults().reset();
131     }
132
133     @AfterEach
134     void check() {
135         assertThat(this.producerSimulator.getTestResults().errorFound).isFalse();
136     }
137
138     @Test
139     void testGetEiTypes() throws Exception {
140         putEiProducerWithOneType("test");
141         String url = ConsumerConsts.API_ROOT + "/eitypes";
142         String rsp = restClient().get(url).block();
143         assertThat(rsp).isEqualTo("[\"test\"]");
144     }
145
146     @Test
147     void testGetEiType() throws Exception {
148         putEiProducerWithOneType("test");
149         String url = ConsumerConsts.API_ROOT + "/eitypes/test";
150         String rsp = restClient().get(url).block();
151         assertThat(rsp).contains("job_data_schema");
152     }
153
154     @Test
155     void testGetEiTypeNotFound() throws Exception {
156         String url = ConsumerConsts.API_ROOT + "/eitypes/junk";
157         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND, "Could not find EI type: junk");
158     }
159
160     @Test
161     void testGetEiJobsIds() throws Exception {
162         putEiProducerWithOneType(EI_TYPE_ID);
163         putEiJob(EI_TYPE_ID, "jobId");
164         String url = ConsumerConsts.API_ROOT + "/eitypes/typeId/eijobs";
165         String rsp = restClient().get(url).block();
166         assertThat(rsp).isEqualTo("[\"jobId\"]");
167     }
168
169     @Test
170     void testGetEiJobTypeNotFound() throws Exception {
171         String url = ConsumerConsts.API_ROOT + "/eitypes/junk/eijobs";
172         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND, "Could not find EI type: junk");
173     }
174
175     @Test
176     void testGetEiJob() throws Exception {
177         putEiProducerWithOneType(EI_TYPE_ID);
178         putEiJob(EI_TYPE_ID, "jobId");
179         String url = ConsumerConsts.API_ROOT + "/eitypes/typeId/eijobs/jobId";
180         String rsp = restClient().get(url).block();
181         assertThat(rsp).contains("job_data");
182     }
183
184     @Test
185     void testGetEiJobStatus() throws Exception {
186         putEiProducerWithOneType(EI_TYPE_ID);
187         putEiJob(EI_TYPE_ID, "jobId");
188         String url = ConsumerConsts.API_ROOT + "/eitypes/typeId/eijobs/jobId/status";
189         String rsp = restClient().get(url).block();
190         assertThat(rsp).contains("ENABLED");
191     }
192
193     // Status TBD
194
195     @Test
196     void testDeleteEiJob() throws Exception {
197         putEiProducerWithOneType(EI_TYPE_ID);
198         putEiJob(EI_TYPE_ID, "jobId");
199         assertThat(this.eiJobs.size()).isEqualTo(1);
200         String url = ConsumerConsts.API_ROOT + "/eitypes/typeId/eijobs/jobId";
201         restClient().delete(url).block();
202         assertThat(this.eiJobs.size()).isEqualTo(0);
203
204         ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
205         await().untilAsserted(() -> assertThat(simulatorResults.jobsStopped.size()).isEqualTo(1));
206         assertThat(simulatorResults.jobsStopped.get(0).id).isEqualTo("jobId");
207     }
208
209     @Test
210     void testPutEiJob() throws Exception {
211         putEiProducerWithOneType(EI_TYPE_ID);
212
213         String url = ConsumerConsts.API_ROOT + "/eitypes/typeId/eijobs/jobId";
214         String body = gson.toJson(eiJobInfo());
215         ResponseEntity<String> resp = restClient().putForEntity(url, body).block();
216         assertThat(this.eiJobs.size()).isEqualTo(1);
217         assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.CREATED);
218
219         ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
220         await().untilAsserted(() -> assertThat(simulatorResults.jobsStarted.size()).isEqualTo(1));
221         ProducerJobInfo request = simulatorResults.jobsStarted.get(0);
222         assertThat(request.id).isEqualTo("jobId");
223
224         resp = restClient().putForEntity(url, body).block();
225         assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
226         EiJob job = this.eiJobs.getJob("jobId");
227         assertThat(job.owner()).isEqualTo("owner");
228     }
229
230     @Test
231     void testPutEiJob_jsonSchemavalidationError() throws Exception {
232         putEiProducerWithOneType(EI_TYPE_ID);
233
234         String url = ConsumerConsts.API_ROOT + "/eitypes/typeId/eijobs/jobId";
235         // The element with name "property1" is mandatory in the schema
236         ConsumerEiJobInfo jobInfo =
237             new ConsumerEiJobInfo(jsonObject("{ \"XXstring\" : \"value\" }"), "owner", "targetUri");
238         String body = gson.toJson(jobInfo);
239
240         testErrorCode(restClient().put(url, body), HttpStatus.NOT_FOUND, "Json validation failure");
241     }
242
243     @Test
244     void testGetEiProducerTypes() throws Exception {
245         putEiProducerWithOneType(EI_TYPE_ID);
246         putEiJob(EI_TYPE_ID, "jobId");
247         String url = ProducerConsts.API_ROOT + "/eitypes";
248
249         ResponseEntity<String> resp = restClient().getForEntity(url).block();
250         assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
251     }
252
253     @Test
254     void testPutEiProducer() throws Exception {
255         String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId";
256         String body = gson.toJson(producerEiRegistratioInfo(EI_TYPE_ID));
257
258         ResponseEntity<String> resp = restClient().putForEntity(url, body).block();
259         assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.CREATED);
260
261         assertThat(this.eiTypes.size()).isEqualTo(1);
262         EiType type = this.eiTypes.getType(EI_TYPE_ID);
263         assertThat(type.getProducerIds().contains("eiProducerId")).isTrue();
264         assertThat(this.eiProducers.size()).isEqualTo(1);
265         assertThat(this.eiProducers.get("eiProducerId").eiTypes().iterator().next().getId().equals(EI_TYPE_ID))
266             .isTrue();
267
268         resp = restClient().putForEntity(url, body).block();
269         assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
270
271         resp = restClient().getForEntity(url).block();
272         assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
273         assertThat(resp.getBody()).isEqualTo(body);
274     }
275
276     @Test
277     void testPutEiProducerExistingJob() throws Exception {
278         putEiProducerWithOneType(EI_TYPE_ID);
279         putEiJob(EI_TYPE_ID, "jobId");
280         String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId";
281         String body = gson.toJson(producerEiRegistratioInfo(EI_TYPE_ID));
282         restClient().putForEntity(url, body).block();
283
284         ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
285         await().untilAsserted(() -> assertThat(simulatorResults.jobsStarted.size()).isEqualTo(2));
286         ProducerJobInfo request = simulatorResults.jobsStarted.get(0);
287         assertThat(request.id).isEqualTo("jobId");
288     }
289
290     @Test
291     void testPutProducerAndEiJob() throws Exception {
292         String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId";
293         String body = gson.toJson(producerEiRegistratioInfo(EI_TYPE_ID));
294         restClient().putForEntity(url, body).block();
295         assertThat(this.eiTypes.size()).isEqualTo(1);
296         this.eiTypes.getType(EI_TYPE_ID);
297
298         url = ConsumerConsts.API_ROOT + "/eitypes/typeId/eijobs/jobId";
299         body = gson.toJson(eiJobInfo());
300         restClient().putForEntity(url, body).block();
301
302         ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
303         await().untilAsserted(() -> assertThat(simulatorResults.jobsStarted.size()).isEqualTo(1));
304         ProducerJobInfo request = simulatorResults.jobsStarted.get(0);
305         assertThat(request.id).isEqualTo("jobId");
306     }
307
308     @Test
309     void testGetEiJobsForProducer() throws JsonMappingException, JsonProcessingException, ServiceException {
310         putEiProducerWithOneType(EI_TYPE_ID);
311         putEiJob(EI_TYPE_ID, "jobId1");
312         putEiJob(EI_TYPE_ID, "jobId2");
313
314         // PUT a consumer
315         String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId";
316         String body = gson.toJson(producerEiRegistratioInfo(EI_TYPE_ID));
317         restClient().putForEntity(url, body).block();
318
319         url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId/eijobs";
320         ResponseEntity<String> resp = restClient().getForEntity(url).block();
321         assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
322
323         ProducerJobInfo[] parsedResp = gson.fromJson(resp.getBody(), ProducerJobInfo[].class);
324         assertThat(parsedResp[0].typeId).isEqualTo(EI_TYPE_ID);
325         assertThat(parsedResp[1].typeId).isEqualTo(EI_TYPE_ID);
326     }
327
328     @Test
329     void testDeleteEiProducer() throws Exception {
330         String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId";
331         String url2 = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId2";
332         String body = gson.toJson(producerEiRegistratioInfo(EI_TYPE_ID));
333         restClient().putForEntity(url, body).block();
334         restClient().putForEntity(url2, body).block();
335         assertThat(this.eiProducers.size()).isEqualTo(2);
336         EiType type = this.eiTypes.getType(EI_TYPE_ID);
337         assertThat(type.getProducerIds().contains("eiProducerId")).isTrue();
338         assertThat(type.getProducerIds().contains("eiProducerId2")).isTrue();
339
340         restClient().deleteForEntity(url).block();
341         assertThat(this.eiProducers.size()).isEqualTo(1);
342         assertThat(this.eiTypes.getType(EI_TYPE_ID).getProducerIds().contains("eiProducerId")).isFalse();
343
344         restClient().deleteForEntity(url2).block();
345         assertThat(this.eiProducers.size()).isEqualTo(0);
346         assertThat(this.eiTypes.size()).isEqualTo(0);
347     }
348
349     ProducerEiTypeRegistrationInfo producerEiTypeRegistrationInfo(String typeId)
350         throws JsonMappingException, JsonProcessingException {
351         return new ProducerEiTypeRegistrationInfo(jsonSchemaObject(), typeId);
352     }
353
354     ProducerRegistrationInfo producerEiRegistratioInfo(String typeId)
355         throws JsonMappingException, JsonProcessingException {
356         Collection<ProducerEiTypeRegistrationInfo> types = new ArrayList<>();
357         types.add(producerEiTypeRegistrationInfo(typeId));
358         return new ProducerRegistrationInfo(types, baseUrl() + ProducerSimulatorController.JOB_CREATED_URL,
359             baseUrl() + ProducerSimulatorController.JOB_DELETED_URL);
360     }
361
362     ConsumerEiJobInfo eiJobInfo() throws JsonMappingException, JsonProcessingException {
363         return new ConsumerEiJobInfo(jsonObject(), "owner", "targetUri");
364     }
365
366     Object jsonObject(String json) {
367         try {
368             return JsonParser.parseString(json).getAsJsonObject();
369         } catch (Exception e) {
370             throw new NullPointerException(e.toString());
371         }
372     }
373
374     Object jsonSchemaObject() {
375         // a json schema with one mandatory property named "string"
376         String schemaStr = "{" //
377             + "\"$schema\": \"http://json-schema.org/draft-04/schema#\"," //
378             + "\"type\": \"object\"," //
379             + "\"properties\": {" //
380             + EI_JOB_PROPERTY + " : {" //
381             + "    \"type\": \"string\"" //
382             + "  }" //
383             + "}," //
384             + "\"required\": [" //
385             + EI_JOB_PROPERTY //
386             + "]" //
387             + "}"; //
388         return jsonObject(schemaStr);
389     }
390
391     Object jsonObject() {
392         return jsonObject("{ " + EI_JOB_PROPERTY + " : \"value\" }");
393     }
394
395     private EiJob putEiJob(String eiTypeId, String jobId)
396         throws JsonMappingException, JsonProcessingException, ServiceException {
397
398         String url = ConsumerConsts.API_ROOT + "/eitypes/typeId/eijobs/" + jobId;
399         String body = gson.toJson(eiJobInfo());
400         restClient().putForEntity(url, body).block();
401
402         return this.eiJobs.getJob(jobId);
403     }
404
405     private EiType putEiProducerWithOneType(String eiTypeId)
406         throws JsonMappingException, JsonProcessingException, ServiceException {
407         String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId";
408         String body = gson.toJson(producerEiRegistratioInfo(eiTypeId));
409
410         restClient().putForEntity(url, body).block();
411         assertThat(this.eiTypes.size()).isEqualTo(1);
412         return this.eiTypes.getType(eiTypeId);
413     }
414
415     private String baseUrl() {
416         return "https://localhost:" + this.port;
417     }
418
419     private AsyncRestClient restClient(boolean useTrustValidation) {
420         WebClientConfig config = this.applicationConfig.getWebClientConfig();
421         config = ImmutableWebClientConfig.builder() //
422             .keyStoreType(config.keyStoreType()) //
423             .keyStorePassword(config.keyStorePassword()) //
424             .keyStore(config.keyStore()) //
425             .keyPassword(config.keyPassword()) //
426             .isTrustStoreUsed(useTrustValidation) //
427             .trustStore(config.trustStore()) //
428             .trustStorePassword(config.trustStorePassword()) //
429             .build();
430
431         return new AsyncRestClient(baseUrl(), config);
432     }
433
434     private AsyncRestClient restClient() {
435         return restClient(false);
436     }
437
438     private void testErrorCode(Mono<?> request, HttpStatus expStatus, String responseContains) {
439         testErrorCode(request, expStatus, responseContains, true);
440     }
441
442     private void testErrorCode(Mono<?> request, HttpStatus expStatus, String responseContains,
443         boolean expectApplicationProblemJsonMediaType) {
444         StepVerifier.create(request) //
445             .expectSubscription() //
446             .expectErrorMatches(
447                 t -> checkWebClientError(t, expStatus, responseContains, expectApplicationProblemJsonMediaType)) //
448             .verify();
449     }
450
451     private boolean checkWebClientError(Throwable throwable, HttpStatus expStatus, String responseContains,
452         boolean expectApplicationProblemJsonMediaType) {
453         assertTrue(throwable instanceof WebClientResponseException);
454         WebClientResponseException responseException = (WebClientResponseException) throwable;
455         assertThat(responseException.getStatusCode()).isEqualTo(expStatus);
456         assertThat(responseException.getResponseBodyAsString()).contains(responseContains);
457         if (expectApplicationProblemJsonMediaType) {
458             assertThat(responseException.getHeaders().getContentType()).isEqualTo(MediaType.APPLICATION_PROBLEM_JSON);
459         }
460         return true;
461     }
462
463 }