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.ArrayList;
38 import java.util.Collection;
40 import org.json.JSONObject;
41 import org.junit.jupiter.api.AfterEach;
42 import org.junit.jupiter.api.BeforeEach;
43 import org.junit.jupiter.api.Test;
44 import org.junit.jupiter.api.extension.ExtendWith;
45 import org.oransc.enrichment.clients.AsyncRestClient;
46 import org.oransc.enrichment.clients.AsyncRestClientFactory;
47 import org.oransc.enrichment.configuration.ApplicationConfig;
48 import org.oransc.enrichment.configuration.ImmutableHttpProxyConfig;
49 import org.oransc.enrichment.configuration.ImmutableWebClientConfig;
50 import org.oransc.enrichment.configuration.WebClientConfig;
51 import org.oransc.enrichment.configuration.WebClientConfig.HttpProxyConfig;
52 import org.oransc.enrichment.controller.ConsumerSimulatorController;
53 import org.oransc.enrichment.controller.ProducerSimulatorController;
54 import org.oransc.enrichment.controllers.consumer.ConsumerConsts;
55 import org.oransc.enrichment.controllers.consumer.ConsumerEiJobInfo;
56 import org.oransc.enrichment.controllers.consumer.ConsumerEiJobStatus;
57 import org.oransc.enrichment.controllers.consumer.ConsumerEiTypeInfo;
58 import org.oransc.enrichment.controllers.producer.ProducerCallbacks;
59 import org.oransc.enrichment.controllers.producer.ProducerConsts;
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 testGetEiType() throws Exception {
195 putEiProducerWithOneType(EI_PRODUCER_ID, "test");
196 String url = ConsumerConsts.API_ROOT + "/eitypes/test";
197 String rsp = restClient().get(url).block();
198 ConsumerEiTypeInfo info = gson.fromJson(rsp, ConsumerEiTypeInfo.class);
199 assertThat(info).isNotNull();
203 void testGetEiTypeNotFound() throws Exception {
204 String url = ConsumerConsts.API_ROOT + "/eitypes/junk";
205 testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND, "Could not find EI type: junk");
209 void testGetEiJobsIds() throws Exception {
210 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
211 putEiJob(EI_TYPE_ID, "jobId");
212 final String JOB_ID_JSON = "[\"jobId\"]";
213 String url = ConsumerConsts.API_ROOT + "/eijobs?eiTypeId=typeId";
214 String rsp = restClient().get(url).block();
215 assertThat(rsp).isEqualTo(JOB_ID_JSON);
217 url = ConsumerConsts.API_ROOT + "/eijobs?owner=owner";
218 rsp = restClient().get(url).block();
219 assertThat(rsp).isEqualTo(JOB_ID_JSON);
221 url = ConsumerConsts.API_ROOT + "/eijobs?owner=JUNK";
222 rsp = restClient().get(url).block();
223 assertThat(rsp).isEqualTo("[]");
225 url = ConsumerConsts.API_ROOT + "/eijobs";
226 rsp = restClient().get(url).block();
227 assertThat(rsp).isEqualTo(JOB_ID_JSON);
229 url = ConsumerConsts.API_ROOT + "/eijobs?eiTypeId=typeId&&owner=owner";
230 rsp = restClient().get(url).block();
231 assertThat(rsp).isEqualTo(JOB_ID_JSON);
233 url = ConsumerConsts.API_ROOT + "/eijobs?eiTypeId=JUNK";
234 rsp = restClient().get(url).block();
235 assertThat(rsp).isEqualTo("[]");
239 void testGetEiJob() throws Exception {
240 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
241 putEiJob(EI_TYPE_ID, "jobId");
242 String url = ConsumerConsts.API_ROOT + "/eijobs/jobId";
243 String rsp = restClient().get(url).block();
244 ConsumerEiJobInfo info = gson.fromJson(rsp, ConsumerEiJobInfo.class);
245 assertThat(info.owner).isEqualTo("owner");
246 assertThat(info.eiTypeId).isEqualTo(EI_TYPE_ID);
250 void testGetEiJobNotFound() throws Exception {
251 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
252 String url = ConsumerConsts.API_ROOT + "/eijobs/junk";
253 testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND, "Could not find EI job: junk");
257 void testGetEiJobStatus() throws Exception {
258 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
259 putEiJob(EI_TYPE_ID, "jobId");
261 verifyJobStatus("jobId", "ENABLED");
265 void testDeleteEiJob() throws Exception {
266 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
267 putEiJob(EI_TYPE_ID, "jobId");
268 assertThat(this.eiJobs.size()).isEqualTo(1);
269 String url = ConsumerConsts.API_ROOT + "/eijobs/jobId";
270 restClient().delete(url).block();
271 assertThat(this.eiJobs.size()).isZero();
273 ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
274 await().untilAsserted(() -> assertThat(simulatorResults.jobsStopped.size()).isEqualTo(1));
275 assertThat(simulatorResults.jobsStopped.get(0)).isEqualTo("jobId");
279 void testDeleteEiJobNotFound() throws Exception {
280 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
281 String url = ConsumerConsts.API_ROOT + "/eijobs/junk";
282 testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND, "Could not find EI job: junk");
286 void testPutEiJob() throws Exception {
287 // Test that one producer accepting a job is enough
288 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
289 putEiProducerWithOneTypeRejecting("simulateProducerError", EI_TYPE_ID);
291 String url = ConsumerConsts.API_ROOT + "/eijobs/jobId";
292 String body = gson.toJson(eiJobInfo());
293 ResponseEntity<String> resp = restClient().putForEntity(url, body).block();
294 assertThat(this.eiJobs.size()).isEqualTo(1);
295 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.CREATED);
297 ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
298 await().untilAsserted(() -> assertThat(simulatorResults.jobsStarted.size()).isEqualTo(1));
299 ProducerJobInfo request = simulatorResults.jobsStarted.get(0);
300 assertThat(request.id).isEqualTo("jobId");
302 // One retry --> two calls
303 await().untilAsserted(() -> assertThat(simulatorResults.noOfRejectedCreate).isEqualTo(2));
304 assertThat(simulatorResults.noOfRejectedCreate).isEqualTo(2);
306 resp = restClient().putForEntity(url, body).block();
307 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
308 EiJob job = this.eiJobs.getJob("jobId");
309 assertThat(job.getOwner()).isEqualTo("owner");
313 void putEiProducerWithOneType_rejecting() throws JsonMappingException, JsonProcessingException, ServiceException {
314 putEiProducerWithOneTypeRejecting("simulateProducerError", EI_TYPE_ID);
315 String url = ConsumerConsts.API_ROOT + "/eijobs/jobId";
316 String body = gson.toJson(eiJobInfo());
317 testErrorCode(restClient().put(url, body), HttpStatus.CONFLICT, "Job not accepted by any producers");
319 ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
320 // There is one retry -> 2 calls
321 await().untilAsserted(() -> assertThat(simulatorResults.noOfRejectedCreate).isEqualTo(2));
322 assertThat(simulatorResults.noOfRejectedCreate).isEqualTo(2);
326 void testPutEiJob_jsonSchemavalidationError() throws Exception {
327 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
329 String url = ConsumerConsts.API_ROOT + "/eijobs/jobId";
330 // The element with name "property1" is mandatory in the schema
331 ConsumerEiJobInfo jobInfo = new ConsumerEiJobInfo("typeId", jsonObject("{ \"XXstring\" : \"value\" }"), "owner",
332 "targetUri", "jobStatusUrl");
333 String body = gson.toJson(jobInfo);
335 testErrorCode(restClient().put(url, body), HttpStatus.CONFLICT, "Json validation failure");
339 void testGetEiProducerTypes() throws Exception {
340 final String EI_TYPE_ID_2 = EI_TYPE_ID + "_2";
341 putEiProducerWithOneType("producer1", EI_TYPE_ID);
342 putEiJob(EI_TYPE_ID, "jobId");
343 putEiProducerWithOneType("producer2", EI_TYPE_ID_2);
344 putEiJob(EI_TYPE_ID_2, "jobId2");
345 String url = ProducerConsts.API_ROOT + "/eitypes";
347 ResponseEntity<String> resp = restClient().getForEntity(url).block();
348 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
349 assertThat(resp.getBody()).contains(EI_TYPE_ID);
350 assertThat(resp.getBody()).contains(EI_TYPE_ID_2);
354 void testReplacingEiProducerTypes() throws Exception {
355 final String REPLACED_TYPE_ID = "replaced";
356 putEiProducerWithOneType(EI_PRODUCER_ID, REPLACED_TYPE_ID);
357 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
359 String url = ProducerConsts.API_ROOT + "/eitypes";
361 ResponseEntity<String> resp = restClient().getForEntity(url).block();
362 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
363 assertThat(resp.getBody()).contains(EI_TYPE_ID);
364 assertThat(resp.getBody()).doesNotContain(REPLACED_TYPE_ID);
368 void testChangingEiTypeGetRejected() throws Exception {
369 putEiProducerWithOneType("producer1", "typeId1");
370 putEiProducerWithOneType("producer2", "typeId2");
371 putEiJob("typeId1", "jobId");
373 String url = ConsumerConsts.API_ROOT + "/eijobs/jobId";
374 String body = gson.toJson(eiJobInfo("typeId2", "jobId"));
375 testErrorCode(restClient().put(url, body), HttpStatus.CONFLICT,
376 "Not allowed to change type for existing EI job");
380 void testPutEiProducer() throws Exception {
381 String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId";
382 String body = gson.toJson(producerEiRegistratioInfo(EI_TYPE_ID));
384 ResponseEntity<String> resp = restClient().putForEntity(url, body).block();
385 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.CREATED);
387 assertThat(this.eiTypes.size()).isEqualTo(1);
388 EiType type = this.eiTypes.getType(EI_TYPE_ID);
389 assertThat(this.eiProducers.getProducersForType(EI_TYPE_ID).size()).isEqualTo(1);
390 assertThat(this.eiProducers.size()).isEqualTo(1);
391 assertThat(this.eiProducers.get("eiProducerId").getEiTypes().iterator().next().getId()).isEqualTo(EI_TYPE_ID);
393 resp = restClient().putForEntity(url, body).block();
394 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
396 resp = restClient().getForEntity(url).block();
397 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
398 assertThat(resp.getBody()).isEqualTo(body);
402 void testPutEiProducerExistingJob() throws Exception {
403 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
404 putEiJob(EI_TYPE_ID, "jobId");
405 String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId";
406 String body = gson.toJson(producerEiRegistratioInfo(EI_TYPE_ID));
407 restClient().putForEntity(url, body).block();
409 ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
410 await().untilAsserted(() -> assertThat(simulatorResults.jobsStarted.size()).isEqualTo(2));
411 ProducerJobInfo request = simulatorResults.jobsStarted.get(0);
412 assertThat(request.id).isEqualTo("jobId");
416 void testPutProducerAndEiJob() throws Exception {
417 String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId";
418 String body = gson.toJson(producerEiRegistratioInfo(EI_TYPE_ID));
419 restClient().putForEntity(url, body).block();
420 assertThat(this.eiTypes.size()).isEqualTo(1);
421 this.eiTypes.getType(EI_TYPE_ID);
423 url = ConsumerConsts.API_ROOT + "/eijobs/jobId";
424 body = gson.toJson(eiJobInfo());
425 restClient().putForEntity(url, body).block();
427 ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
428 await().untilAsserted(() -> assertThat(simulatorResults.jobsStarted.size()).isEqualTo(1));
429 ProducerJobInfo request = simulatorResults.jobsStarted.get(0);
430 assertThat(request.id).isEqualTo("jobId");
434 void testGetEiJobsForProducer() throws JsonMappingException, JsonProcessingException, ServiceException {
435 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
436 putEiJob(EI_TYPE_ID, "jobId1");
437 putEiJob(EI_TYPE_ID, "jobId2");
440 String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId";
441 String body = gson.toJson(producerEiRegistratioInfo(EI_TYPE_ID));
442 restClient().putForEntity(url, body).block();
444 url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId/eijobs";
445 ResponseEntity<String> resp = restClient().getForEntity(url).block();
446 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
448 ProducerJobInfo[] parsedResp = gson.fromJson(resp.getBody(), ProducerJobInfo[].class);
449 assertThat(parsedResp[0].typeId).isEqualTo(EI_TYPE_ID);
450 assertThat(parsedResp[1].typeId).isEqualTo(EI_TYPE_ID);
454 void testDeleteEiProducer() throws Exception {
455 putEiProducerWithOneType("eiProducerId", EI_TYPE_ID);
456 putEiProducerWithOneType("eiProducerId2", EI_TYPE_ID);
458 assertThat(this.eiProducers.size()).isEqualTo(2);
459 EiType type = this.eiTypes.getType(EI_TYPE_ID);
460 assertThat(this.eiProducers.getProducerIdsForType(type.getId())).contains("eiProducerId");
461 assertThat(this.eiProducers.getProducerIdsForType(type.getId())).contains("eiProducerId2");
462 putEiJob(EI_TYPE_ID, "jobId");
463 assertThat(this.eiJobs.size()).isEqualTo(1);
465 deleteEiProducer("eiProducerId");
466 assertThat(this.eiProducers.size()).isEqualTo(1);
467 assertThat(this.eiProducers.getProducerIdsForType(EI_TYPE_ID)).doesNotContain("eiProducerId");
468 verifyJobStatus("jobId", "ENABLED");
470 deleteEiProducer("eiProducerId2");
471 assertThat(this.eiProducers.size()).isZero();
472 assertThat(this.eiTypes.size()).isZero();
473 verifyJobStatus("jobId", "DISABLED");
477 void testJobStatusNotifications() throws JsonMappingException, JsonProcessingException, ServiceException {
478 ConsumerSimulatorController.TestResults consumerCalls = this.consumerSimulator.getTestResults();
479 ProducerSimulatorController.TestResults producerCalls = this.producerSimulator.getTestResults();
481 putEiProducerWithOneType("eiProducerId", EI_TYPE_ID);
482 putEiJob(EI_TYPE_ID, "jobId");
483 putEiProducerWithOneType("eiProducerId2", EI_TYPE_ID);
484 await().untilAsserted(() -> assertThat(producerCalls.jobsStarted.size()).isEqualTo(2));
486 deleteEiProducer("eiProducerId2");
487 assertThat(this.eiTypes.size()).isEqualTo(1); // The type remains, one producer left
488 deleteEiProducer("eiProducerId");
489 assertThat(this.eiTypes.size()).isZero(); // The type is gone
490 assertThat(this.eiJobs.size()).isEqualTo(1); // The job remains
491 await().untilAsserted(() -> assertThat(consumerCalls.status.size()).isEqualTo(1));
492 assertThat(consumerCalls.status.get(0).state).isEqualTo(ConsumerEiJobStatus.EiJobStatusValues.DISABLED);
494 putEiProducerWithOneType("eiProducerId", EI_TYPE_ID);
495 await().untilAsserted(() -> assertThat(consumerCalls.status.size()).isEqualTo(2));
496 assertThat(consumerCalls.status.get(1).state).isEqualTo(ConsumerEiJobStatus.EiJobStatusValues.ENABLED);
500 void testJobStatusNotifications2() throws JsonMappingException, JsonProcessingException, ServiceException {
501 // Test replacing a producer with new and removed types
504 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
505 putEiJob(EI_TYPE_ID, EI_JOB_ID);
507 // change the type for the producer, the EI_TYPE_ID is deleted
508 putEiProducerWithOneType(EI_PRODUCER_ID, "junk");
509 verifyJobStatus(EI_JOB_ID, "DISABLED");
510 ConsumerSimulatorController.TestResults consumerCalls = this.consumerSimulator.getTestResults();
511 await().untilAsserted(() -> assertThat(consumerCalls.status.size()).isEqualTo(1));
512 assertThat(consumerCalls.status.get(0).state).isEqualTo(ConsumerEiJobStatus.EiJobStatusValues.DISABLED);
514 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
515 verifyJobStatus(EI_JOB_ID, "ENABLED");
516 await().untilAsserted(() -> assertThat(consumerCalls.status.size()).isEqualTo(2));
517 assertThat(consumerCalls.status.get(1).state).isEqualTo(ConsumerEiJobStatus.EiJobStatusValues.ENABLED);
521 void testGetProducerEiType() throws JsonMappingException, JsonProcessingException, ServiceException {
522 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
523 String url = ProducerConsts.API_ROOT + "/eitypes/" + EI_TYPE_ID;
524 ResponseEntity<String> resp = restClient().getForEntity(url).block();
525 assertThat(resp.getBody()).contains(EI_PRODUCER_ID);
529 void testGetProducerIdentifiers() throws JsonMappingException, JsonProcessingException, ServiceException {
530 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
531 String url = ProducerConsts.API_ROOT + "/eiproducers";
532 ResponseEntity<String> resp = restClient().getForEntity(url).block();
533 assertThat(resp.getBody()).contains(EI_PRODUCER_ID);
537 void testProducerSupervision() throws JsonMappingException, JsonProcessingException, ServiceException {
538 putEiProducerWithOneTypeRejecting("simulateProducerError", EI_TYPE_ID);
542 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
543 putEiJob(EI_TYPE_ID, EI_JOB_ID);
544 deleteEiProducer(EI_PRODUCER_ID);
547 assertThat(this.eiProducers.size()).isEqualTo(1);
548 assertThat(this.eiTypes.size()).isEqualTo(1);
549 assertProducerOpState("simulateProducerError", ProducerStatusInfo.OperationalState.ENABLED);
551 this.producerSupervision.createTask().blockLast();
552 this.producerSupervision.createTask().blockLast();
554 // Now we have one producer that is disabled, but the job will be enabled until
555 // the producer/type is removed
556 assertThat(this.eiProducers.size()).isEqualTo(1);
557 assertProducerOpState("simulateProducerError", ProducerStatusInfo.OperationalState.DISABLED);
558 verifyJobStatus(EI_JOB_ID, "ENABLED");
560 // After 3 failed checks, the producer and the type shall be deregisterred
561 this.producerSupervision.createTask().blockLast();
562 assertThat(this.eiProducers.size()).isEqualTo(0); // The producer is removed
563 assertThat(this.eiTypes.size()).isEqualTo(0); // The type is removed
564 verifyJobStatus(EI_JOB_ID, "DISABLED");
566 // Job disabled status notification shall be received
567 ConsumerSimulatorController.TestResults consumerResults = this.consumerSimulator.getTestResults();
568 await().untilAsserted(() -> assertThat(consumerResults.status.size()).isEqualTo(1));
569 assertThat(consumerResults.status.get(0).state).isEqualTo(ConsumerEiJobStatus.EiJobStatusValues.DISABLED);
573 void testGetStatus() throws JsonMappingException, JsonProcessingException, ServiceException {
574 putEiProducerWithOneTypeRejecting("simulateProducerError", EI_TYPE_ID);
575 putEiProducerWithOneTypeRejecting("simulateProducerError2", EI_TYPE_ID);
577 String url = "/status";
578 ResponseEntity<String> resp = restClient().getForEntity(url).block();
579 assertThat(resp.getBody()).contains("hunky dory");
583 void testEiJobDatabase() throws Exception {
584 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
585 putEiJob(EI_TYPE_ID, "jobId1");
586 putEiJob(EI_TYPE_ID, "jobId2");
588 assertThat(this.eiJobs.size()).isEqualTo(2);
592 EiJobs jobs = new EiJobs(this.applicationConfig, this.producerCallbacks);
593 jobs.restoreJobsFromDatabase();
594 assertThat(jobs.size()).isEqualTo(2);
595 jobs.remove("jobId1", this.eiProducers);
596 jobs.remove("jobId2", this.eiProducers);
599 // Restore the jobs, no jobs in database
600 EiJobs jobs = new EiJobs(this.applicationConfig, this.producerCallbacks);
601 jobs.restoreJobsFromDatabase();
602 assertThat(jobs.size()).isEqualTo(0);
604 logger.warn("Test removing a job when the db file is gone");
605 this.eiJobs.remove("jobId1", this.eiProducers);
606 assertThat(this.eiJobs.size()).isEqualTo(1);
608 ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
609 await().untilAsserted(() -> assertThat(simulatorResults.jobsStopped.size()).isEqualTo(3));
613 void testEiTypesDatabase() throws Exception {
614 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
616 assertThat(this.eiTypes.size()).isEqualTo(1);
620 EiTypes types = new EiTypes(this.applicationConfig);
621 types.restoreTypesFromDatabase();
622 assertThat(types.size()).isEqualTo(1);
626 // Restore the jobs, no jobs in database
627 EiTypes types = new EiTypes(this.applicationConfig);
629 types.restoreTypesFromDatabase();
630 assertThat(types.size()).isEqualTo(0);
632 logger.warn("Test removing a job when the db file is gone");
633 this.eiTypes.remove(this.eiTypes.getType(EI_TYPE_ID));
634 assertThat(this.eiJobs.size()).isEqualTo(0);
637 private void deleteEiProducer(String eiProducerId) {
638 String url = ProducerConsts.API_ROOT + "/eiproducers/" + eiProducerId;
639 restClient().deleteForEntity(url).block();
642 private void verifyJobStatus(String jobId, String expStatus) {
643 String url = ConsumerConsts.API_ROOT + "/eijobs/" + jobId + "/status";
644 String rsp = restClient().get(url).block();
645 assertThat(rsp).contains(expStatus);
648 private void assertProducerOpState(String producerId,
649 ProducerStatusInfo.OperationalState expectedOperationalState) {
650 String statusUrl = ProducerConsts.API_ROOT + "/eiproducers/" + producerId + "/status";
651 ResponseEntity<String> resp = restClient().getForEntity(statusUrl).block();
652 ProducerStatusInfo statusInfo = gson.fromJson(resp.getBody(), ProducerStatusInfo.class);
653 assertThat(statusInfo.opState).isEqualTo(expectedOperationalState);
656 ProducerEiTypeRegistrationInfo producerEiTypeRegistrationInfo(String typeId)
657 throws JsonMappingException, JsonProcessingException {
658 return new ProducerEiTypeRegistrationInfo(jsonSchemaObject(), typeId);
661 ProducerRegistrationInfo producerEiRegistratioInfoRejecting(String typeId)
662 throws JsonMappingException, JsonProcessingException {
663 Collection<ProducerEiTypeRegistrationInfo> types = new ArrayList<>();
664 types.add(producerEiTypeRegistrationInfo(typeId));
665 return new ProducerRegistrationInfo(types, //
666 baseUrl() + ProducerSimulatorController.JOB_ERROR_URL,
667 baseUrl() + ProducerSimulatorController.SUPERVISION_ERROR_URL);
670 ProducerRegistrationInfo producerEiRegistratioInfo(String typeId)
671 throws JsonMappingException, JsonProcessingException {
672 Collection<ProducerEiTypeRegistrationInfo> types = new ArrayList<>();
673 types.add(producerEiTypeRegistrationInfo(typeId));
674 return new ProducerRegistrationInfo(types, //
675 baseUrl() + ProducerSimulatorController.JOB_URL, baseUrl() + ProducerSimulatorController.SUPERVISION_URL);
678 private ConsumerEiJobInfo eiJobInfo() throws JsonMappingException, JsonProcessingException {
679 return eiJobInfo(EI_TYPE_ID, EI_JOB_ID);
682 ConsumerEiJobInfo eiJobInfo(String typeId, String eiJobId) throws JsonMappingException, JsonProcessingException {
683 return new ConsumerEiJobInfo(typeId, jsonObject(), "owner", "targetUri",
684 baseUrl() + ConsumerSimulatorController.getJobStatusUrl(eiJobId));
687 private Object jsonObject(String json) {
689 return JsonParser.parseString(json).getAsJsonObject();
690 } catch (Exception e) {
691 throw new NullPointerException(e.toString());
695 private Object jsonSchemaObject() {
696 // a json schema with one mandatory property named "string"
697 String schemaStr = "{" //
698 + "\"$schema\": \"http://json-schema.org/draft-04/schema#\"," //
699 + "\"type\": \"object\"," //
700 + "\"properties\": {" //
701 + EI_JOB_PROPERTY + " : {" //
702 + " \"type\": \"string\"" //
705 + "\"required\": [" //
709 return jsonObject(schemaStr);
712 private Object jsonObject() {
713 return jsonObject("{ " + EI_JOB_PROPERTY + " : \"value\" }");
716 private EiJob putEiJob(String eiTypeId, String jobId)
717 throws JsonMappingException, JsonProcessingException, ServiceException {
719 String url = ConsumerConsts.API_ROOT + "/eijobs/" + jobId;
720 String body = gson.toJson(eiJobInfo(eiTypeId, jobId));
721 restClient().putForEntity(url, body).block();
723 return this.eiJobs.getJob(jobId);
726 private EiType putEiProducerWithOneTypeRejecting(String producerId, String eiTypeId)
727 throws JsonMappingException, JsonProcessingException, ServiceException {
728 String url = ProducerConsts.API_ROOT + "/eiproducers/" + producerId;
729 String body = gson.toJson(producerEiRegistratioInfoRejecting(eiTypeId));
731 restClient().putForEntity(url, body).block();
732 return this.eiTypes.getType(eiTypeId);
735 private EiType putEiProducerWithOneType(String producerId, String eiTypeId)
736 throws JsonMappingException, JsonProcessingException, ServiceException {
737 String url = ProducerConsts.API_ROOT + "/eiproducers/" + producerId;
738 String body = gson.toJson(producerEiRegistratioInfo(eiTypeId));
740 restClient().putForEntity(url, body).block();
742 return this.eiTypes.getType(eiTypeId);
745 private String baseUrl() {
746 return "https://localhost:" + this.port;
749 private AsyncRestClient restClient(boolean useTrustValidation) {
750 WebClientConfig config = this.applicationConfig.getWebClientConfig();
751 HttpProxyConfig httpProxyConfig = ImmutableHttpProxyConfig.builder() //
752 .httpProxyHost("") //
755 config = ImmutableWebClientConfig.builder() //
756 .keyStoreType(config.keyStoreType()) //
757 .keyStorePassword(config.keyStorePassword()) //
758 .keyStore(config.keyStore()) //
759 .keyPassword(config.keyPassword()) //
760 .isTrustStoreUsed(useTrustValidation) //
761 .trustStore(config.trustStore()) //
762 .trustStorePassword(config.trustStorePassword()) //
763 .httpProxyConfig(httpProxyConfig).build();
765 AsyncRestClientFactory restClientFactory = new AsyncRestClientFactory(config);
766 return restClientFactory.createRestClientNoHttpProxy(baseUrl());
769 private AsyncRestClient restClient() {
770 return restClient(false);
773 private void testErrorCode(Mono<?> request, HttpStatus expStatus, String responseContains) {
774 testErrorCode(request, expStatus, responseContains, true);
777 private void testErrorCode(Mono<?> request, HttpStatus expStatus, String responseContains,
778 boolean expectApplicationProblemJsonMediaType) {
779 StepVerifier.create(request) //
780 .expectSubscription() //
782 t -> checkWebClientError(t, expStatus, responseContains, expectApplicationProblemJsonMediaType)) //
786 private boolean checkWebClientError(Throwable throwable, HttpStatus expStatus, String responseContains,
787 boolean expectApplicationProblemJsonMediaType) {
788 assertTrue(throwable instanceof WebClientResponseException);
789 WebClientResponseException responseException = (WebClientResponseException) throwable;
790 assertThat(responseException.getStatusCode()).isEqualTo(expStatus);
791 assertThat(responseException.getResponseBodyAsString()).contains(responseContains);
792 if (expectApplicationProblemJsonMediaType) {
793 assertThat(responseException.getHeaders().getContentType()).isEqualTo(MediaType.APPLICATION_PROBLEM_JSON);