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 =
237 new ConsumerEiJobInfo(jsonObject("{ \"XXstring\" : \"value\" }"), "owner", "targetUri");
238 String body = gson.toJson(jobInfo);
240 testErrorCode(restClient().put(url, body), HttpStatus.NOT_FOUND, "Json validation failure");
244 void testGetEiProducerTypes() throws Exception {
245 putEiProducerWithOneType(EI_TYPE_ID);
246 putEiJob(EI_TYPE_ID, "jobId");
247 String url = ProducerConsts.API_ROOT + "/eitypes";
249 ResponseEntity<String> resp = restClient().getForEntity(url).block();
250 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
254 void testPutEiProducer() throws Exception {
255 String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId";
256 String body = gson.toJson(producerEiRegistratioInfo(EI_TYPE_ID));
258 ResponseEntity<String> resp = restClient().putForEntity(url, body).block();
259 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.CREATED);
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))
268 resp = restClient().putForEntity(url, body).block();
269 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
271 resp = restClient().getForEntity(url).block();
272 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
273 assertThat(resp.getBody()).isEqualTo(body);
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();
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");
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);
298 url = ConsumerConsts.API_ROOT + "/eitypes/typeId/eijobs/jobId";
299 body = gson.toJson(eiJobInfo());
300 restClient().putForEntity(url, body).block();
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");
309 void testGetEiJobsForProducer() throws JsonMappingException, JsonProcessingException, ServiceException {
310 putEiProducerWithOneType(EI_TYPE_ID);
311 putEiJob(EI_TYPE_ID, "jobId1");
312 putEiJob(EI_TYPE_ID, "jobId2");
315 String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId";
316 String body = gson.toJson(producerEiRegistratioInfo(EI_TYPE_ID));
317 restClient().putForEntity(url, body).block();
319 url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId/eijobs";
320 ResponseEntity<String> resp = restClient().getForEntity(url).block();
321 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
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);
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();
340 restClient().deleteForEntity(url).block();
341 assertThat(this.eiProducers.size()).isEqualTo(1);
342 assertThat(this.eiTypes.getType(EI_TYPE_ID).getProducerIds().contains("eiProducerId")).isFalse();
344 restClient().deleteForEntity(url2).block();
345 assertThat(this.eiProducers.size()).isEqualTo(0);
346 assertThat(this.eiTypes.size()).isEqualTo(0);
349 ProducerEiTypeRegistrationInfo producerEiTypeRegistrationInfo(String typeId)
350 throws JsonMappingException, JsonProcessingException {
351 return new ProducerEiTypeRegistrationInfo(jsonSchemaObject(), typeId);
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);
362 ConsumerEiJobInfo eiJobInfo() throws JsonMappingException, JsonProcessingException {
363 return new ConsumerEiJobInfo(jsonObject(), "owner", "targetUri");
366 Object jsonObject(String json) {
368 return JsonParser.parseString(json).getAsJsonObject();
369 } catch (Exception e) {
370 throw new NullPointerException(e.toString());
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\"" //
384 + "\"required\": [" //
388 return jsonObject(schemaStr);
391 Object jsonObject() {
392 return jsonObject("{ " + EI_JOB_PROPERTY + " : \"value\" }");
395 private EiJob putEiJob(String eiTypeId, String jobId)
396 throws JsonMappingException, JsonProcessingException, ServiceException {
398 String url = ConsumerConsts.API_ROOT + "/eitypes/typeId/eijobs/" + jobId;
399 String body = gson.toJson(eiJobInfo());
400 restClient().putForEntity(url, body).block();
402 return this.eiJobs.getJob(jobId);
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));
410 restClient().putForEntity(url, body).block();
411 assertThat(this.eiTypes.size()).isEqualTo(1);
412 return this.eiTypes.getType(eiTypeId);
415 private String baseUrl() {
416 return "https://localhost:" + this.port;
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()) //
431 return new AsyncRestClient(baseUrl(), config);
434 private AsyncRestClient restClient() {
435 return restClient(false);
438 private void testErrorCode(Mono<?> request, HttpStatus expStatus, String responseContains) {
439 testErrorCode(request, expStatus, responseContains, true);
442 private void testErrorCode(Mono<?> request, HttpStatus expStatus, String responseContains,
443 boolean expectApplicationProblemJsonMediaType) {
444 StepVerifier.create(request) //
445 .expectSubscription() //
447 t -> checkWebClientError(t, expStatus, responseContains, expectApplicationProblemJsonMediaType)) //
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);