2 * ========================LICENSE_START=================================
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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===================================
21 package org.oransc.enrichment;
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;
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;
33 import java.util.ArrayList;
34 import java.util.Collection;
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;
75 import reactor.core.publisher.Mono;
76 import reactor.test.StepVerifier;
78 @ExtendWith(SpringExtension.class)
79 @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
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\"";
90 ApplicationContext context;
99 EiProducers eiProducers;
102 ApplicationConfig applicationConfig;
105 ProducerSimulatorController producerSimulator;
107 private static Gson gson = new GsonBuilder() //
112 * Overrides the BeanFactory.
115 static class TestBeanFactory {
117 public ServletWebServerFactory servletContainer() {
118 return new TomcatServletWebServerFactory();
128 this.eiTypes.clear();
129 this.eiProducers.clear();
130 this.producerSimulator.getTestResults().reset();
135 assertThat(this.producerSimulator.getTestResults().errorFound).isFalse();
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\"]");
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");
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");
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\"]");
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");
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");
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");
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);
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");
210 void testPutEiJob() throws Exception {
211 putEiProducerWithOneType(EI_TYPE_ID);
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);
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");
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");
231 void testPutEiJob_jsonSchemavalidationError() throws Exception {
232 putEiProducerWithOneType(EI_TYPE_ID);
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);
239 testErrorCode(restClient().put(url, body), HttpStatus.NOT_FOUND, "Json validation failure");
243 void testGetEiProducerTypes() throws Exception {
244 putEiProducerWithOneType(EI_TYPE_ID);
245 putEiJob(EI_TYPE_ID, "jobId");
246 String url = ProducerConsts.API_ROOT + "/eitypes";
248 ResponseEntity<String> resp = restClient().getForEntity(url).block();
249 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
253 void testPutEiProducer() throws Exception {
254 String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId";
255 String body = gson.toJson(producerEiRegistratioInfo(EI_TYPE_ID));
257 ResponseEntity<String> resp = restClient().putForEntity(url, body).block();
258 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.CREATED);
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))
267 resp = restClient().putForEntity(url, body).block();
268 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
270 resp = restClient().getForEntity(url).block();
271 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
272 assertThat(resp.getBody()).isEqualTo(body);
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();
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");
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);
297 url = ConsumerConsts.API_ROOT + "/eitypes/typeId/eijobs/jobId";
298 body = gson.toJson(eiJobInfo());
299 restClient().putForEntity(url, body).block();
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");
308 void testGetEiJobsForProducer() throws JsonMappingException, JsonProcessingException, ServiceException {
309 putEiProducerWithOneType(EI_TYPE_ID);
310 putEiJob(EI_TYPE_ID, "jobId1");
311 putEiJob(EI_TYPE_ID, "jobId2");
314 String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId";
315 String body = gson.toJson(producerEiRegistratioInfo(EI_TYPE_ID));
316 restClient().putForEntity(url, body).block();
318 url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId/eijobs";
319 ResponseEntity<String> resp = restClient().getForEntity(url).block();
320 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
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);
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();
339 restClient().deleteForEntity(url).block();
340 assertThat(this.eiProducers.size()).isEqualTo(1);
341 assertThat(this.eiTypes.getType(EI_TYPE_ID).getProducerIds().contains("eiProducerId")).isFalse();
343 restClient().deleteForEntity(url2).block();
344 assertThat(this.eiProducers.size()).isEqualTo(0);
345 assertThat(this.eiTypes.size()).isEqualTo(0);
348 ProducerEiTypeRegistrationInfo producerEiTypeRegistrationInfo(String typeId)
349 throws JsonMappingException, JsonProcessingException {
350 return new ProducerEiTypeRegistrationInfo(jsonSchemaObject(), typeId);
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);
361 ConsumerEiJobInfo eiJobInfo() throws JsonMappingException, JsonProcessingException {
362 return new ConsumerEiJobInfo(jsonObject(), "owner");
365 Object jsonObject(String json) {
367 return JsonParser.parseString(json).getAsJsonObject();
368 } catch (Exception e) {
369 throw new NullPointerException(e.toString());
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\"" //
383 + "\"required\": [" //
387 return jsonObject(schemaStr);
390 Object jsonObject() {
391 return jsonObject("{ " + EI_JOB_PROPERTY + " : \"value\" }");
394 private EiJob putEiJob(String eiTypeId, String jobId)
395 throws JsonMappingException, JsonProcessingException, ServiceException {
397 String url = ConsumerConsts.API_ROOT + "/eitypes/typeId/eijobs/" + jobId;
398 String body = gson.toJson(eiJobInfo());
399 restClient().putForEntity(url, body).block();
401 return this.eiJobs.getJob(jobId);
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));
409 restClient().putForEntity(url, body).block();
410 assertThat(this.eiTypes.size()).isEqualTo(1);
411 return this.eiTypes.getType(eiTypeId);
414 private String baseUrl() {
415 return "https://localhost:" + this.port;
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()) //
430 return new AsyncRestClient(baseUrl(), config);
433 private AsyncRestClient restClient() {
434 return restClient(false);
437 private void testErrorCode(Mono<?> request, HttpStatus expStatus, String responseContains) {
438 testErrorCode(request, expStatus, responseContains, true);
441 private void testErrorCode(Mono<?> request, HttpStatus expStatus, String responseContains,
442 boolean expectApplicationProblemJsonMediaType) {
443 StepVerifier.create(request) //
444 .expectSubscription() //
446 t -> checkWebClientError(t, expStatus, responseContains, expectApplicationProblemJsonMediaType)) //
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);