2 * ========================LICENSE_START=================================
5 * Copyright (C) 2020 Nordix Foundation
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.io.FileNotFoundException;
34 import java.io.FileOutputStream;
35 import java.io.PrintStream;
36 import java.lang.invoke.MethodHandles;
37 import java.util.Arrays;
39 import org.json.JSONObject;
40 import org.junit.jupiter.api.AfterEach;
41 import org.junit.jupiter.api.BeforeEach;
42 import org.junit.jupiter.api.Test;
43 import org.junit.jupiter.api.extension.ExtendWith;
44 import org.oransc.enrichment.clients.AsyncRestClient;
45 import org.oransc.enrichment.clients.AsyncRestClientFactory;
46 import org.oransc.enrichment.configuration.ApplicationConfig;
47 import org.oransc.enrichment.configuration.ImmutableHttpProxyConfig;
48 import org.oransc.enrichment.configuration.ImmutableWebClientConfig;
49 import org.oransc.enrichment.configuration.WebClientConfig;
50 import org.oransc.enrichment.configuration.WebClientConfig.HttpProxyConfig;
51 import org.oransc.enrichment.controller.ConsumerSimulatorController;
52 import org.oransc.enrichment.controller.ProducerSimulatorController;
53 import org.oransc.enrichment.controllers.consumer.ConsumerConsts;
54 import org.oransc.enrichment.controllers.consumer.ConsumerEiJobInfo;
55 import org.oransc.enrichment.controllers.consumer.ConsumerEiJobStatus;
56 import org.oransc.enrichment.controllers.consumer.ConsumerEiTypeInfo;
57 import org.oransc.enrichment.controllers.producer.ProducerCallbacks;
58 import org.oransc.enrichment.controllers.producer.ProducerConsts;
59 import org.oransc.enrichment.controllers.producer.ProducerEiTypeInfo;
60 import org.oransc.enrichment.controllers.producer.ProducerJobInfo;
61 import org.oransc.enrichment.controllers.producer.ProducerRegistrationInfo;
62 import org.oransc.enrichment.controllers.producer.ProducerRegistrationInfo.ProducerEiTypeRegistrationInfo;
63 import org.oransc.enrichment.controllers.producer.ProducerStatusInfo;
64 import org.oransc.enrichment.exceptions.ServiceException;
65 import org.oransc.enrichment.repository.EiJob;
66 import org.oransc.enrichment.repository.EiJobs;
67 import org.oransc.enrichment.repository.EiProducers;
68 import org.oransc.enrichment.repository.EiType;
69 import org.oransc.enrichment.repository.EiTypes;
70 import org.oransc.enrichment.tasks.ProducerSupervision;
71 import org.slf4j.Logger;
72 import org.slf4j.LoggerFactory;
73 import org.springframework.beans.factory.annotation.Autowired;
74 import org.springframework.boot.test.context.SpringBootTest;
75 import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
76 import org.springframework.boot.test.context.TestConfiguration;
77 import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
78 import org.springframework.boot.web.server.LocalServerPort;
79 import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
80 import org.springframework.context.ApplicationContext;
81 import org.springframework.context.annotation.Bean;
82 import org.springframework.http.HttpStatus;
83 import org.springframework.http.MediaType;
84 import org.springframework.http.ResponseEntity;
85 import org.springframework.test.context.TestPropertySource;
86 import org.springframework.test.context.junit.jupiter.SpringExtension;
87 import org.springframework.web.reactive.function.client.WebClientResponseException;
89 import reactor.core.publisher.Mono;
90 import reactor.test.StepVerifier;
92 @ExtendWith(SpringExtension.class)
93 @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
96 "server.ssl.key-store=./config/keystore.jks", //
97 "app.webclient.trust-store=./config/truststore.jks", //
98 "app.vardata-directory=./target"})
99 class ApplicationTest {
100 private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
102 private final String EI_TYPE_ID = "typeId";
103 private final String EI_PRODUCER_ID = "producerId";
104 private final String EI_JOB_PROPERTY = "\"property1\"";
105 private final String EI_JOB_ID = "jobId";
108 ApplicationContext context;
117 EiProducers eiProducers;
120 ApplicationConfig applicationConfig;
123 ProducerSimulatorController producerSimulator;
126 ConsumerSimulatorController consumerSimulator;
129 ProducerSupervision producerSupervision;
132 ProducerCallbacks producerCallbacks;
134 private static Gson gson = new GsonBuilder().create();
137 * Overrides the BeanFactory.
140 static class TestBeanFactory {
142 public ServletWebServerFactory servletContainer() {
143 return new TomcatServletWebServerFactory();
153 this.eiTypes.clear();
154 this.eiProducers.clear();
155 this.producerSimulator.getTestResults().reset();
156 this.consumerSimulator.getTestResults().reset();
161 assertThat(this.producerSimulator.getTestResults().errorFound).isFalse();
165 void createApiDoc() throws FileNotFoundException {
166 String url = "/v2/api-docs";
167 ResponseEntity<String> resp = restClient().getForEntity(url).block();
168 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
170 JSONObject jsonObj = new JSONObject(resp.getBody());
171 jsonObj.remove("host");
172 String indented = jsonObj.toString(4);
173 try (PrintStream out = new PrintStream(new FileOutputStream("api/ecs-api.json"))) {
179 void testGetEiTypes() throws Exception {
180 putEiProducerWithOneType(EI_PRODUCER_ID, "test");
181 String url = ConsumerConsts.API_ROOT + "/eitypes";
182 String rsp = restClient().get(url).block();
183 assertThat(rsp).isEqualTo("[\"test\"]");
187 void testGetEiTypesEmpty() throws Exception {
188 String url = ConsumerConsts.API_ROOT + "/eitypes";
189 String rsp = restClient().get(url).block();
190 assertThat(rsp).isEqualTo("[]");
194 void testPutEiType() throws JsonMappingException, JsonProcessingException, ServiceException {
195 assertThat(putEiType(EI_TYPE_ID)).isEqualTo(HttpStatus.CREATED);
196 assertThat(putEiType(EI_TYPE_ID)).isEqualTo(HttpStatus.OK);
200 void testGetEiType() throws Exception {
201 putEiProducerWithOneType(EI_PRODUCER_ID, "test");
202 String url = ConsumerConsts.API_ROOT + "/eitypes/test";
203 String rsp = restClient().get(url).block();
204 ConsumerEiTypeInfo info = gson.fromJson(rsp, ConsumerEiTypeInfo.class);
205 assertThat(info).isNotNull();
209 void testDeleteEiType() throws Exception {
210 putEiType(EI_TYPE_ID);
211 String url = ProducerConsts.API_ROOT + "/eitypes/" + EI_TYPE_ID;
212 restClient().delete(url).block();
213 assertThat(this.eiTypes.size()).isEqualTo(0);
215 testErrorCode(restClient().delete(url), HttpStatus.NOT_FOUND, "EI type not found");
219 void testDeleteEiTypeExistingProducer() throws Exception {
220 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
221 String url = ProducerConsts.API_ROOT + "/eitypes/" + EI_TYPE_ID;
222 testErrorCode(restClient().delete(url), HttpStatus.NOT_ACCEPTABLE,
223 "The type has active producers: " + EI_PRODUCER_ID);
224 assertThat(this.eiTypes.size()).isEqualTo(1);
228 void testGetEiTypeNotFound() throws Exception {
229 String url = ConsumerConsts.API_ROOT + "/eitypes/junk";
230 testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND, "EI type not found: junk");
234 void testGetEiJobsIds() throws Exception {
235 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
236 putEiJob(EI_TYPE_ID, "jobId");
237 final String JOB_ID_JSON = "[\"jobId\"]";
238 String url = ConsumerConsts.API_ROOT + "/eijobs?eiTypeId=typeId";
239 String rsp = restClient().get(url).block();
240 assertThat(rsp).isEqualTo(JOB_ID_JSON);
242 url = ConsumerConsts.API_ROOT + "/eijobs?owner=owner";
243 rsp = restClient().get(url).block();
244 assertThat(rsp).isEqualTo(JOB_ID_JSON);
246 url = ConsumerConsts.API_ROOT + "/eijobs?owner=JUNK";
247 rsp = restClient().get(url).block();
248 assertThat(rsp).isEqualTo("[]");
250 url = ConsumerConsts.API_ROOT + "/eijobs";
251 rsp = restClient().get(url).block();
252 assertThat(rsp).isEqualTo(JOB_ID_JSON);
254 url = ConsumerConsts.API_ROOT + "/eijobs?eiTypeId=typeId&&owner=owner";
255 rsp = restClient().get(url).block();
256 assertThat(rsp).isEqualTo(JOB_ID_JSON);
258 url = ConsumerConsts.API_ROOT + "/eijobs?eiTypeId=JUNK";
259 rsp = restClient().get(url).block();
260 assertThat(rsp).isEqualTo("[]");
264 void testGetEiJob() throws Exception {
265 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
266 putEiJob(EI_TYPE_ID, "jobId");
267 String url = ConsumerConsts.API_ROOT + "/eijobs/jobId";
268 String rsp = restClient().get(url).block();
269 ConsumerEiJobInfo info = gson.fromJson(rsp, ConsumerEiJobInfo.class);
270 assertThat(info.owner).isEqualTo("owner");
271 assertThat(info.eiTypeId).isEqualTo(EI_TYPE_ID);
275 void testGetEiJobNotFound() throws Exception {
276 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
277 String url = ConsumerConsts.API_ROOT + "/eijobs/junk";
278 testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND, "Could not find EI job: junk");
282 void testGetEiJobStatus() throws Exception {
283 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
284 putEiJob(EI_TYPE_ID, "jobId");
286 verifyJobStatus("jobId", "ENABLED");
290 void testDeleteEiJob() throws Exception {
291 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
292 putEiJob(EI_TYPE_ID, "jobId");
293 assertThat(this.eiJobs.size()).isEqualTo(1);
294 String url = ConsumerConsts.API_ROOT + "/eijobs/jobId";
295 restClient().delete(url).block();
296 assertThat(this.eiJobs.size()).isZero();
298 ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
299 await().untilAsserted(() -> assertThat(simulatorResults.jobsStopped.size()).isEqualTo(1));
300 assertThat(simulatorResults.jobsStopped.get(0)).isEqualTo("jobId");
304 void testDeleteEiJobNotFound() throws Exception {
305 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
306 String url = ConsumerConsts.API_ROOT + "/eijobs/junk";
307 testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND, "Could not find EI job: junk");
311 void testPutEiJob() throws Exception {
312 // Test that one producer accepting a job is enough
313 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
314 putEiProducerWithOneTypeRejecting("simulateProducerError", EI_TYPE_ID);
316 String url = ConsumerConsts.API_ROOT + "/eijobs/jobId";
317 String body = gson.toJson(eiJobInfo());
318 ResponseEntity<String> resp = restClient().putForEntity(url, body).block();
319 assertThat(this.eiJobs.size()).isEqualTo(1);
320 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.CREATED);
322 ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
323 await().untilAsserted(() -> assertThat(simulatorResults.jobsStarted.size()).isEqualTo(1));
324 ProducerJobInfo request = simulatorResults.jobsStarted.get(0);
325 assertThat(request.id).isEqualTo("jobId");
327 // One retry --> two calls
328 await().untilAsserted(() -> assertThat(simulatorResults.noOfRejectedCreate).isEqualTo(2));
329 assertThat(simulatorResults.noOfRejectedCreate).isEqualTo(2);
331 resp = restClient().putForEntity(url, body).block();
332 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
333 EiJob job = this.eiJobs.getJob("jobId");
334 assertThat(job.getOwner()).isEqualTo("owner");
336 verifyJobStatus(EI_JOB_ID, "ENABLED");
340 void putEiProducerWithOneType_rejecting() throws JsonMappingException, JsonProcessingException, ServiceException {
341 putEiProducerWithOneTypeRejecting("simulateProducerError", EI_TYPE_ID);
342 String url = ConsumerConsts.API_ROOT + "/eijobs/" + EI_JOB_ID;
343 String body = gson.toJson(eiJobInfo());
344 restClient().put(url, body).block();
346 ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
347 // There is one retry -> 2 calls
348 await().untilAsserted(() -> assertThat(simulatorResults.noOfRejectedCreate).isEqualTo(2));
349 assertThat(simulatorResults.noOfRejectedCreate).isEqualTo(2);
351 verifyJobStatus(EI_JOB_ID, "DISABLED");
356 void testPutEiJob_jsonSchemavalidationError() throws Exception {
357 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
359 String url = ConsumerConsts.API_ROOT + "/eijobs/jobId";
360 // The element with name "property1" is mandatory in the schema
361 ConsumerEiJobInfo jobInfo = new ConsumerEiJobInfo("typeId", jsonObject("{ \"XXstring\" : \"value\" }"), "owner",
362 "targetUri", "jobStatusUrl");
363 String body = gson.toJson(jobInfo);
365 testErrorCode(restClient().put(url, body), HttpStatus.CONFLICT, "Json validation failure");
369 void testGetEiProducerTypes() throws Exception {
370 final String EI_TYPE_ID_2 = EI_TYPE_ID + "_2";
371 putEiProducerWithOneType("producer1", EI_TYPE_ID);
372 putEiJob(EI_TYPE_ID, "jobId");
373 putEiProducerWithOneType("producer2", EI_TYPE_ID_2);
374 putEiJob(EI_TYPE_ID_2, "jobId2");
375 String url = ProducerConsts.API_ROOT + "/eitypes";
377 ResponseEntity<String> resp = restClient().getForEntity(url).block();
378 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
379 assertThat(resp.getBody()).contains(EI_TYPE_ID);
380 assertThat(resp.getBody()).contains(EI_TYPE_ID_2);
384 void testChangingEiTypeGetRejected() throws Exception {
385 putEiProducerWithOneType("producer1", "typeId1");
386 putEiProducerWithOneType("producer2", "typeId2");
387 putEiJob("typeId1", "jobId");
389 String url = ConsumerConsts.API_ROOT + "/eijobs/jobId";
390 String body = gson.toJson(eiJobInfo("typeId2", "jobId"));
391 testErrorCode(restClient().put(url, body), HttpStatus.CONFLICT,
392 "Not allowed to change type for existing EI job");
396 void testPutEiProducer() throws Exception {
397 this.putEiType(EI_TYPE_ID);
398 String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId";
399 String body = gson.toJson(producerEiRegistratioInfo(EI_TYPE_ID));
401 ResponseEntity<String> resp = restClient().putForEntity(url, body).block();
402 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.CREATED);
404 assertThat(this.eiTypes.size()).isEqualTo(1);
405 assertThat(this.eiProducers.getProducersForType(EI_TYPE_ID).size()).isEqualTo(1);
406 assertThat(this.eiProducers.size()).isEqualTo(1);
407 assertThat(this.eiProducers.get("eiProducerId").getEiTypes().iterator().next().getId()).isEqualTo(EI_TYPE_ID);
409 resp = restClient().putForEntity(url, body).block();
410 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
412 resp = restClient().getForEntity(url).block();
413 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
414 assertThat(resp.getBody()).isEqualTo(body);
418 void testPutEiProducerExistingJob() throws Exception {
419 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
420 putEiJob(EI_TYPE_ID, "jobId");
421 String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId";
422 String body = gson.toJson(producerEiRegistratioInfo(EI_TYPE_ID));
423 restClient().putForEntity(url, body).block();
425 ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
426 await().untilAsserted(() -> assertThat(simulatorResults.jobsStarted.size()).isEqualTo(2));
427 ProducerJobInfo request = simulatorResults.jobsStarted.get(0);
428 assertThat(request.id).isEqualTo("jobId");
432 void testPutProducerAndEiJob() throws Exception {
433 this.putEiType(EI_TYPE_ID);
434 String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId";
435 String body = gson.toJson(producerEiRegistratioInfo(EI_TYPE_ID));
436 restClient().putForEntity(url, body).block();
437 assertThat(this.eiTypes.size()).isEqualTo(1);
438 this.eiTypes.getType(EI_TYPE_ID);
440 url = ConsumerConsts.API_ROOT + "/eijobs/jobId";
441 body = gson.toJson(eiJobInfo());
442 restClient().putForEntity(url, body).block();
444 ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
445 await().untilAsserted(() -> assertThat(simulatorResults.jobsStarted.size()).isEqualTo(1));
446 ProducerJobInfo request = simulatorResults.jobsStarted.get(0);
447 assertThat(request.id).isEqualTo("jobId");
451 void testGetEiJobsForProducer() throws JsonMappingException, JsonProcessingException, ServiceException {
452 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
453 putEiJob(EI_TYPE_ID, "jobId1");
454 putEiJob(EI_TYPE_ID, "jobId2");
457 String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId";
458 String body = gson.toJson(producerEiRegistratioInfo(EI_TYPE_ID));
459 restClient().putForEntity(url, body).block();
461 url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId/eijobs";
462 ResponseEntity<String> resp = restClient().getForEntity(url).block();
463 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
465 ProducerJobInfo[] parsedResp = gson.fromJson(resp.getBody(), ProducerJobInfo[].class);
466 assertThat(parsedResp[0].typeId).isEqualTo(EI_TYPE_ID);
467 assertThat(parsedResp[1].typeId).isEqualTo(EI_TYPE_ID);
471 void testDeleteEiProducer() throws Exception {
472 putEiProducerWithOneType("eiProducerId", EI_TYPE_ID);
473 putEiProducerWithOneType("eiProducerId2", EI_TYPE_ID);
475 assertThat(this.eiProducers.size()).isEqualTo(2);
476 EiType type = this.eiTypes.getType(EI_TYPE_ID);
477 assertThat(this.eiProducers.getProducerIdsForType(type.getId())).contains("eiProducerId");
478 assertThat(this.eiProducers.getProducerIdsForType(type.getId())).contains("eiProducerId2");
479 putEiJob(EI_TYPE_ID, "jobId");
480 assertThat(this.eiJobs.size()).isEqualTo(1);
482 deleteEiProducer("eiProducerId");
483 assertThat(this.eiProducers.size()).isEqualTo(1);
484 assertThat(this.eiProducers.getProducerIdsForType(EI_TYPE_ID)).doesNotContain("eiProducerId");
485 verifyJobStatus("jobId", "ENABLED");
487 deleteEiProducer("eiProducerId2");
488 assertThat(this.eiProducers.size()).isZero();
489 assertThat(this.eiTypes.size()).isEqualTo(1);
490 verifyJobStatus("jobId", "DISABLED");
494 void testJobStatusNotifications() throws JsonMappingException, JsonProcessingException, ServiceException {
495 ConsumerSimulatorController.TestResults consumerCalls = this.consumerSimulator.getTestResults();
496 ProducerSimulatorController.TestResults producerCalls = this.producerSimulator.getTestResults();
498 putEiProducerWithOneType("eiProducerId", EI_TYPE_ID);
499 putEiJob(EI_TYPE_ID, "jobId");
500 putEiProducerWithOneType("eiProducerId2", EI_TYPE_ID);
501 await().untilAsserted(() -> assertThat(producerCalls.jobsStarted.size()).isEqualTo(2));
503 deleteEiProducer("eiProducerId2");
504 assertThat(this.eiTypes.size()).isEqualTo(1); // The type remains, one producer left
505 deleteEiProducer("eiProducerId");
506 assertThat(this.eiTypes.size()).isEqualTo(1); // The type remains
507 assertThat(this.eiJobs.size()).isEqualTo(1); // The job remains
508 await().untilAsserted(() -> assertThat(consumerCalls.status.size()).isEqualTo(1));
509 assertThat(consumerCalls.status.get(0).state).isEqualTo(ConsumerEiJobStatus.EiJobStatusValues.DISABLED);
511 putEiProducerWithOneType("eiProducerId", EI_TYPE_ID);
512 await().untilAsserted(() -> assertThat(consumerCalls.status.size()).isEqualTo(2));
513 assertThat(consumerCalls.status.get(1).state).isEqualTo(ConsumerEiJobStatus.EiJobStatusValues.ENABLED);
517 void testJobStatusNotifications2() throws JsonMappingException, JsonProcessingException, ServiceException {
518 // Test replacing a producer with new and removed types
521 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
522 putEiJob(EI_TYPE_ID, EI_JOB_ID);
524 // change the type for the producer, the job shall be disabled
525 putEiProducerWithOneType(EI_PRODUCER_ID, "junk");
526 verifyJobStatus(EI_JOB_ID, "DISABLED");
527 ConsumerSimulatorController.TestResults consumerCalls = this.consumerSimulator.getTestResults();
528 await().untilAsserted(() -> assertThat(consumerCalls.status.size()).isEqualTo(1));
529 assertThat(consumerCalls.status.get(0).state).isEqualTo(ConsumerEiJobStatus.EiJobStatusValues.DISABLED);
531 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
532 verifyJobStatus(EI_JOB_ID, "ENABLED");
533 await().untilAsserted(() -> assertThat(consumerCalls.status.size()).isEqualTo(2));
534 assertThat(consumerCalls.status.get(1).state).isEqualTo(ConsumerEiJobStatus.EiJobStatusValues.ENABLED);
538 void testGetProducerEiType() throws JsonMappingException, JsonProcessingException, ServiceException {
539 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
540 String url = ProducerConsts.API_ROOT + "/eitypes/" + EI_TYPE_ID;
541 ResponseEntity<String> resp = restClient().getForEntity(url).block();
542 ProducerEiTypeInfo info = gson.fromJson(resp.getBody(), ProducerEiTypeInfo.class);
543 assertThat(info.jobDataSchema).isNotNull();
547 void testGetProducerIdentifiers() throws JsonMappingException, JsonProcessingException, ServiceException {
548 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
549 String url = ProducerConsts.API_ROOT + "/eiproducers";
550 ResponseEntity<String> resp = restClient().getForEntity(url).block();
551 assertThat(resp.getBody()).contains(EI_PRODUCER_ID);
553 url = ProducerConsts.API_ROOT + "/eiproducers?ei_type_id=" + EI_TYPE_ID;
554 resp = restClient().getForEntity(url).block();
555 assertThat(resp.getBody()).contains(EI_PRODUCER_ID);
557 url = ProducerConsts.API_ROOT + "/eiproducers?ei_type_id=junk";
558 resp = restClient().getForEntity(url).block();
559 assertThat(resp.getBody()).isEqualTo("[]");
563 void testProducerSupervision() throws JsonMappingException, JsonProcessingException, ServiceException {
564 putEiProducerWithOneTypeRejecting("simulateProducerError", EI_TYPE_ID);
568 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
569 putEiJob(EI_TYPE_ID, EI_JOB_ID);
570 verifyJobStatus(EI_JOB_ID, "ENABLED");
571 deleteEiProducer(EI_PRODUCER_ID);
572 verifyJobStatus(EI_JOB_ID, "DISABLED");
575 // Job disabled status notification shall be received
576 ConsumerSimulatorController.TestResults consumerResults = this.consumerSimulator.getTestResults();
577 await().untilAsserted(() -> assertThat(consumerResults.status.size()).isEqualTo(1));
578 assertThat(consumerResults.status.get(0).state).isEqualTo(ConsumerEiJobStatus.EiJobStatusValues.DISABLED);
580 assertThat(this.eiProducers.size()).isEqualTo(1);
581 assertThat(this.eiTypes.size()).isEqualTo(1);
582 assertProducerOpState("simulateProducerError", ProducerStatusInfo.OperationalState.ENABLED);
584 this.producerSupervision.createTask().blockLast();
585 this.producerSupervision.createTask().blockLast();
587 // Now we have one producer that is disabled
588 assertThat(this.eiProducers.size()).isEqualTo(1);
589 assertProducerOpState("simulateProducerError", ProducerStatusInfo.OperationalState.DISABLED);
591 // After 3 failed checks, the producer and the type shall be deregisterred
592 this.producerSupervision.createTask().blockLast();
593 assertThat(this.eiProducers.size()).isEqualTo(0); // The producer is removed
594 assertThat(this.eiTypes.size()).isEqualTo(1); // The type remains
599 void testGetStatus() throws JsonMappingException, JsonProcessingException, ServiceException {
600 putEiProducerWithOneTypeRejecting("simulateProducerError", EI_TYPE_ID);
601 putEiProducerWithOneTypeRejecting("simulateProducerError2", EI_TYPE_ID);
603 String url = "/status";
604 ResponseEntity<String> resp = restClient().getForEntity(url).block();
605 assertThat(resp.getBody()).contains("hunky dory");
609 void testEiJobDatabase() throws Exception {
610 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
611 putEiJob(EI_TYPE_ID, "jobId1");
612 putEiJob(EI_TYPE_ID, "jobId2");
614 assertThat(this.eiJobs.size()).isEqualTo(2);
618 EiJobs jobs = new EiJobs(this.applicationConfig, this.producerCallbacks);
619 jobs.restoreJobsFromDatabase();
620 assertThat(jobs.size()).isEqualTo(2);
621 jobs.remove("jobId1", this.eiProducers);
622 jobs.remove("jobId2", this.eiProducers);
625 // Restore the jobs, no jobs in database
626 EiJobs jobs = new EiJobs(this.applicationConfig, this.producerCallbacks);
627 jobs.restoreJobsFromDatabase();
628 assertThat(jobs.size()).isEqualTo(0);
630 logger.warn("Test removing a job when the db file is gone");
631 this.eiJobs.remove("jobId1", this.eiProducers);
632 assertThat(this.eiJobs.size()).isEqualTo(1);
634 ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
635 await().untilAsserted(() -> assertThat(simulatorResults.jobsStopped.size()).isEqualTo(3));
639 void testEiTypesDatabase() throws Exception {
640 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
642 assertThat(this.eiTypes.size()).isEqualTo(1);
646 EiTypes types = new EiTypes(this.applicationConfig);
647 types.restoreTypesFromDatabase();
648 assertThat(types.size()).isEqualTo(1);
652 // Restore the jobs, no jobs in database
653 EiTypes types = new EiTypes(this.applicationConfig);
655 types.restoreTypesFromDatabase();
656 assertThat(types.size()).isEqualTo(0);
658 logger.warn("Test removing a job when the db file is gone");
659 this.eiTypes.remove(this.eiTypes.getType(EI_TYPE_ID));
660 assertThat(this.eiJobs.size()).isEqualTo(0);
663 private void deleteEiProducer(String eiProducerId) {
664 String url = ProducerConsts.API_ROOT + "/eiproducers/" + eiProducerId;
665 restClient().deleteForEntity(url).block();
668 private void verifyJobStatus(String jobId, String expStatus) {
669 String url = ConsumerConsts.API_ROOT + "/eijobs/" + jobId + "/status";
670 String rsp = restClient().get(url).block();
671 assertThat(rsp).contains(expStatus);
674 private void assertProducerOpState(String producerId,
675 ProducerStatusInfo.OperationalState expectedOperationalState) {
676 String statusUrl = ProducerConsts.API_ROOT + "/eiproducers/" + producerId + "/status";
677 ResponseEntity<String> resp = restClient().getForEntity(statusUrl).block();
678 ProducerStatusInfo statusInfo = gson.fromJson(resp.getBody(), ProducerStatusInfo.class);
679 assertThat(statusInfo.opState).isEqualTo(expectedOperationalState);
682 ProducerEiTypeRegistrationInfo producerEiTypeRegistrationInfo(String typeId)
683 throws JsonMappingException, JsonProcessingException {
684 return new ProducerEiTypeRegistrationInfo(jsonSchemaObject(), typeId);
687 ProducerRegistrationInfo producerEiRegistratioInfoRejecting(String typeId)
688 throws JsonMappingException, JsonProcessingException {
689 return new ProducerRegistrationInfo(Arrays.asList(typeId), //
690 baseUrl() + ProducerSimulatorController.JOB_ERROR_URL,
691 baseUrl() + ProducerSimulatorController.SUPERVISION_ERROR_URL);
694 ProducerRegistrationInfo producerEiRegistratioInfo(String typeId)
695 throws JsonMappingException, JsonProcessingException {
696 return new ProducerRegistrationInfo(Arrays.asList(typeId), //
697 baseUrl() + ProducerSimulatorController.JOB_URL, baseUrl() + ProducerSimulatorController.SUPERVISION_URL);
700 private ConsumerEiJobInfo eiJobInfo() throws JsonMappingException, JsonProcessingException {
701 return eiJobInfo(EI_TYPE_ID, EI_JOB_ID);
704 ConsumerEiJobInfo eiJobInfo(String typeId, String eiJobId) throws JsonMappingException, JsonProcessingException {
705 return new ConsumerEiJobInfo(typeId, jsonObject(), "owner", "targetUri",
706 baseUrl() + ConsumerSimulatorController.getJobStatusUrl(eiJobId));
709 private Object jsonObject(String json) {
711 return JsonParser.parseString(json).getAsJsonObject();
712 } catch (Exception e) {
713 throw new NullPointerException(e.toString());
717 private Object jsonSchemaObject() {
718 // a json schema with one mandatory property named "string"
719 String schemaStr = "{" //
720 + "\"$schema\": \"http://json-schema.org/draft-04/schema#\"," //
721 + "\"type\": \"object\"," //
722 + "\"properties\": {" //
723 + EI_JOB_PROPERTY + " : {" //
724 + " \"type\": \"string\"" //
727 + "\"required\": [" //
731 return jsonObject(schemaStr);
734 private Object jsonObject() {
735 return jsonObject("{ " + EI_JOB_PROPERTY + " : \"value\" }");
738 private EiJob putEiJob(String eiTypeId, String jobId)
739 throws JsonMappingException, JsonProcessingException, ServiceException {
741 String url = ConsumerConsts.API_ROOT + "/eijobs/" + jobId;
742 String body = gson.toJson(eiJobInfo(eiTypeId, jobId));
743 restClient().putForEntity(url, body).block();
745 return this.eiJobs.getJob(jobId);
748 private HttpStatus putEiType(String eiTypeId)
749 throws JsonMappingException, JsonProcessingException, ServiceException {
750 String url = ProducerConsts.API_ROOT + "/eitypes/" + eiTypeId;
751 String body = gson.toJson(producerEiTypeRegistrationInfo(eiTypeId));
752 ResponseEntity<String> resp = restClient().putForEntity(url, body).block();
753 this.eiTypes.getType(eiTypeId);
754 return resp.getStatusCode();
758 private EiType putEiProducerWithOneTypeRejecting(String producerId, String eiTypeId)
759 throws JsonMappingException, JsonProcessingException, ServiceException {
760 this.putEiType(eiTypeId);
761 String url = ProducerConsts.API_ROOT + "/eiproducers/" + producerId;
762 String body = gson.toJson(producerEiRegistratioInfoRejecting(eiTypeId));
763 restClient().putForEntity(url, body).block();
764 return this.eiTypes.getType(eiTypeId);
767 private EiType putEiProducerWithOneType(String producerId, String eiTypeId)
768 throws JsonMappingException, JsonProcessingException, ServiceException {
769 this.putEiType(eiTypeId);
771 String url = ProducerConsts.API_ROOT + "/eiproducers/" + producerId;
772 String body = gson.toJson(producerEiRegistratioInfo(eiTypeId));
774 restClient().putForEntity(url, body).block();
776 return this.eiTypes.getType(eiTypeId);
779 private String baseUrl() {
780 return "https://localhost:" + this.port;
783 private AsyncRestClient restClient(boolean useTrustValidation) {
784 WebClientConfig config = this.applicationConfig.getWebClientConfig();
785 HttpProxyConfig httpProxyConfig = ImmutableHttpProxyConfig.builder() //
786 .httpProxyHost("") //
789 config = ImmutableWebClientConfig.builder() //
790 .keyStoreType(config.keyStoreType()) //
791 .keyStorePassword(config.keyStorePassword()) //
792 .keyStore(config.keyStore()) //
793 .keyPassword(config.keyPassword()) //
794 .isTrustStoreUsed(useTrustValidation) //
795 .trustStore(config.trustStore()) //
796 .trustStorePassword(config.trustStorePassword()) //
797 .httpProxyConfig(httpProxyConfig).build();
799 AsyncRestClientFactory restClientFactory = new AsyncRestClientFactory(config);
800 return restClientFactory.createRestClientNoHttpProxy(baseUrl());
803 private AsyncRestClient restClient() {
804 return restClient(false);
807 private void testErrorCode(Mono<?> request, HttpStatus expStatus, String responseContains) {
808 testErrorCode(request, expStatus, responseContains, true);
811 private void testErrorCode(Mono<?> request, HttpStatus expStatus, String responseContains,
812 boolean expectApplicationProblemJsonMediaType) {
813 StepVerifier.create(request) //
814 .expectSubscription() //
816 t -> checkWebClientError(t, expStatus, responseContains, expectApplicationProblemJsonMediaType)) //
820 private boolean checkWebClientError(Throwable throwable, HttpStatus expStatus, String responseContains,
821 boolean expectApplicationProblemJsonMediaType) {
822 assertTrue(throwable instanceof WebClientResponseException);
823 WebClientResponseException responseException = (WebClientResponseException) throwable;
824 assertThat(responseException.getStatusCode()).isEqualTo(expStatus);
825 assertThat(responseException.getResponseBodyAsString()).contains(responseContains);
826 if (expectApplicationProblemJsonMediaType) {
827 assertThat(responseException.getHeaders().getContentType()).isEqualTo(MediaType.APPLICATION_PROBLEM_JSON);