e92705e9d8aa6d34d77660c39a25328f14e71e0d
[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 = new ConsumerEiJobInfo(jsonObject("{ \"XXstring\" : \"value\" }"), "owner");
237         String body = gson.toJson(jobInfo);
238
239         testErrorCode(restClient().put(url, body), HttpStatus.NOT_FOUND, "Json validation failure");
240     }
241
242     @Test
243     void testGetEiProducerTypes() throws Exception {
244         putEiProducerWithOneType(EI_TYPE_ID);
245         putEiJob(EI_TYPE_ID, "jobId");
246         String url = ProducerConsts.API_ROOT + "/eitypes";
247
248         ResponseEntity<String> resp = restClient().getForEntity(url).block();
249         assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
250     }
251
252     @Test
253     void testPutEiProducer() throws Exception {
254         String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId";
255         String body = gson.toJson(producerEiRegistratioInfo(EI_TYPE_ID));
256
257         ResponseEntity<String> resp = restClient().putForEntity(url, body).block();
258         assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.CREATED);
259
260         assertThat(this.eiTypes.size()).isEqualTo(1);
261         EiType type = this.eiTypes.getType(EI_TYPE_ID);
262         assertThat(type.getProducerIds().contains("eiProducerId")).isTrue();
263         assertThat(this.eiProducers.size()).isEqualTo(1);
264         assertThat(this.eiProducers.get("eiProducerId").eiTypes().iterator().next().getId().equals(EI_TYPE_ID))
265             .isTrue();
266
267         resp = restClient().putForEntity(url, body).block();
268         assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
269
270         resp = restClient().getForEntity(url).block();
271         assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
272         assertThat(resp.getBody()).isEqualTo(body);
273     }
274
275     @Test
276     void testPutEiProducerExistingJob() throws Exception {
277         putEiProducerWithOneType(EI_TYPE_ID);
278         putEiJob(EI_TYPE_ID, "jobId");
279         String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId";
280         String body = gson.toJson(producerEiRegistratioInfo(EI_TYPE_ID));
281         restClient().putForEntity(url, body).block();
282
283         ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
284         await().untilAsserted(() -> assertThat(simulatorResults.jobsStarted.size()).isEqualTo(2));
285         ProducerJobInfo request = simulatorResults.jobsStarted.get(0);
286         assertThat(request.id).isEqualTo("jobId");
287     }
288
289     @Test
290     void testPutProducerAndEiJob() throws Exception {
291         String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId";
292         String body = gson.toJson(producerEiRegistratioInfo(EI_TYPE_ID));
293         restClient().putForEntity(url, body).block();
294         assertThat(this.eiTypes.size()).isEqualTo(1);
295         this.eiTypes.getType(EI_TYPE_ID);
296
297         url = ConsumerConsts.API_ROOT + "/eitypes/typeId/eijobs/jobId";
298         body = gson.toJson(eiJobInfo());
299         restClient().putForEntity(url, body).block();
300
301         ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
302         await().untilAsserted(() -> assertThat(simulatorResults.jobsStarted.size()).isEqualTo(1));
303         ProducerJobInfo request = simulatorResults.jobsStarted.get(0);
304         assertThat(request.id).isEqualTo("jobId");
305     }
306
307     @Test
308     void testGetEiJobsForProducer() throws JsonMappingException, JsonProcessingException, ServiceException {
309         putEiProducerWithOneType(EI_TYPE_ID);
310         putEiJob(EI_TYPE_ID, "jobId1");
311         putEiJob(EI_TYPE_ID, "jobId2");
312
313         // PUT a consumer
314         String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId";
315         String body = gson.toJson(producerEiRegistratioInfo(EI_TYPE_ID));
316         restClient().putForEntity(url, body).block();
317
318         url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId/eijobs";
319         ResponseEntity<String> resp = restClient().getForEntity(url).block();
320         assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
321
322         ProducerJobInfo[] parsedResp = gson.fromJson(resp.getBody(), ProducerJobInfo[].class);
323         assertThat(parsedResp[0].typeId).isEqualTo(EI_TYPE_ID);
324         assertThat(parsedResp[1].typeId).isEqualTo(EI_TYPE_ID);
325     }
326
327     @Test
328     void testDeleteEiProducer() throws Exception {
329         String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId";
330         String url2 = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId2";
331         String body = gson.toJson(producerEiRegistratioInfo(EI_TYPE_ID));
332         restClient().putForEntity(url, body).block();
333         restClient().putForEntity(url2, body).block();
334         assertThat(this.eiProducers.size()).isEqualTo(2);
335         EiType type = this.eiTypes.getType(EI_TYPE_ID);
336         assertThat(type.getProducerIds().contains("eiProducerId")).isTrue();
337         assertThat(type.getProducerIds().contains("eiProducerId2")).isTrue();
338
339         restClient().deleteForEntity(url).block();
340         assertThat(this.eiProducers.size()).isEqualTo(1);
341         assertThat(this.eiTypes.getType(EI_TYPE_ID).getProducerIds().contains("eiProducerId")).isFalse();
342
343         restClient().deleteForEntity(url2).block();
344         assertThat(this.eiProducers.size()).isEqualTo(0);
345         assertThat(this.eiTypes.size()).isEqualTo(0);
346     }
347
348     ProducerEiTypeRegistrationInfo producerEiTypeRegistrationInfo(String typeId)
349         throws JsonMappingException, JsonProcessingException {
350         return new ProducerEiTypeRegistrationInfo(jsonSchemaObject(), typeId);
351     }
352
353     ProducerRegistrationInfo producerEiRegistratioInfo(String typeId)
354         throws JsonMappingException, JsonProcessingException {
355         Collection<ProducerEiTypeRegistrationInfo> types = new ArrayList<>();
356         types.add(producerEiTypeRegistrationInfo(typeId));
357         return new ProducerRegistrationInfo(types, baseUrl() + ProducerSimulatorController.JOB_CREATED_URL,
358             baseUrl() + ProducerSimulatorController.JOB_DELETED_URL);
359     }
360
361     ConsumerEiJobInfo eiJobInfo() throws JsonMappingException, JsonProcessingException {
362         return new ConsumerEiJobInfo(jsonObject(), "owner");
363     }
364
365     Object jsonObject(String json) {
366         try {
367             return JsonParser.parseString(json).getAsJsonObject();
368         } catch (Exception e) {
369             throw new NullPointerException(e.toString());
370         }
371     }
372
373     Object jsonSchemaObject() {
374         // a json schema with one mandatory property named "string"
375         String schemaStr = "{" //
376             + "\"$schema\": \"http://json-schema.org/draft-04/schema#\"," //
377             + "\"type\": \"object\"," //
378             + "\"properties\": {" //
379             + EI_JOB_PROPERTY + " : {" //
380             + "    \"type\": \"string\"" //
381             + "  }" //
382             + "}," //
383             + "\"required\": [" //
384             + EI_JOB_PROPERTY //
385             + "]" //
386             + "}"; //
387         return jsonObject(schemaStr);
388     }
389
390     Object jsonObject() {
391         return jsonObject("{ " + EI_JOB_PROPERTY + " : \"value\" }");
392     }
393
394     private EiJob putEiJob(String eiTypeId, String jobId)
395         throws JsonMappingException, JsonProcessingException, ServiceException {
396
397         String url = ConsumerConsts.API_ROOT + "/eitypes/typeId/eijobs/" + jobId;
398         String body = gson.toJson(eiJobInfo());
399         restClient().putForEntity(url, body).block();
400
401         return this.eiJobs.getJob(jobId);
402     }
403
404     private EiType putEiProducerWithOneType(String eiTypeId)
405         throws JsonMappingException, JsonProcessingException, ServiceException {
406         String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId";
407         String body = gson.toJson(producerEiRegistratioInfo(eiTypeId));
408
409         restClient().putForEntity(url, body).block();
410         assertThat(this.eiTypes.size()).isEqualTo(1);
411         return this.eiTypes.getType(eiTypeId);
412     }
413
414     private String baseUrl() {
415         return "https://localhost:" + this.port;
416     }
417
418     private AsyncRestClient restClient(boolean useTrustValidation) {
419         WebClientConfig config = this.applicationConfig.getWebClientConfig();
420         config = ImmutableWebClientConfig.builder() //
421             .keyStoreType(config.keyStoreType()) //
422             .keyStorePassword(config.keyStorePassword()) //
423             .keyStore(config.keyStore()) //
424             .keyPassword(config.keyPassword()) //
425             .isTrustStoreUsed(useTrustValidation) //
426             .trustStore(config.trustStore()) //
427             .trustStorePassword(config.trustStorePassword()) //
428             .build();
429
430         return new AsyncRestClient(baseUrl(), config);
431     }
432
433     private AsyncRestClient restClient() {
434         return restClient(false);
435     }
436
437     private void testErrorCode(Mono<?> request, HttpStatus expStatus, String responseContains) {
438         testErrorCode(request, expStatus, responseContains, true);
439     }
440
441     private void testErrorCode(Mono<?> request, HttpStatus expStatus, String responseContains,
442         boolean expectApplicationProblemJsonMediaType) {
443         StepVerifier.create(request) //
444             .expectSubscription() //
445             .expectErrorMatches(
446                 t -> checkWebClientError(t, expStatus, responseContains, expectApplicationProblemJsonMediaType)) //
447             .verify();
448     }
449
450     private boolean checkWebClientError(Throwable throwable, HttpStatus expStatus, String responseContains,
451         boolean expectApplicationProblemJsonMediaType) {
452         assertTrue(throwable instanceof WebClientResponseException);
453         WebClientResponseException responseException = (WebClientResponseException) throwable;
454         assertThat(responseException.getStatusCode()).isEqualTo(expStatus);
455         assertThat(responseException.getResponseBodyAsString()).contains(responseContains);
456         if (expectApplicationProblemJsonMediaType) {
457             assertThat(responseException.getHeaders().getContentType()).isEqualTo(MediaType.APPLICATION_PROBLEM_JSON);
458         }
459         return true;
460     }
461
462 }