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.ImmutableWebClientConfig;
49 import org.oransc.enrichment.configuration.WebClientConfig;
50 import org.oransc.enrichment.controller.ConsumerSimulatorController;
51 import org.oransc.enrichment.controller.ProducerSimulatorController;
52 import org.oransc.enrichment.controllers.consumer.ConsumerConsts;
53 import org.oransc.enrichment.controllers.consumer.ConsumerEiJobInfo;
54 import org.oransc.enrichment.controllers.consumer.ConsumerEiJobStatus;
55 import org.oransc.enrichment.controllers.consumer.ConsumerEiTypeInfo;
56 import org.oransc.enrichment.controllers.producer.ProducerConsts;
57 import org.oransc.enrichment.controllers.producer.ProducerJobInfo;
58 import org.oransc.enrichment.controllers.producer.ProducerRegistrationInfo;
59 import org.oransc.enrichment.controllers.producer.ProducerRegistrationInfo.ProducerEiTypeRegistrationInfo;
60 import org.oransc.enrichment.controllers.producer.ProducerStatusInfo;
61 import org.oransc.enrichment.exceptions.ServiceException;
62 import org.oransc.enrichment.repository.EiJob;
63 import org.oransc.enrichment.repository.EiJobs;
64 import org.oransc.enrichment.repository.EiProducers;
65 import org.oransc.enrichment.repository.EiType;
66 import org.oransc.enrichment.repository.EiTypes;
67 import org.oransc.enrichment.tasks.ProducerSupervision;
68 import org.slf4j.Logger;
69 import org.slf4j.LoggerFactory;
70 import org.springframework.beans.factory.annotation.Autowired;
71 import org.springframework.boot.test.context.SpringBootTest;
72 import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
73 import org.springframework.boot.test.context.TestConfiguration;
74 import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
75 import org.springframework.boot.web.server.LocalServerPort;
76 import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
77 import org.springframework.context.ApplicationContext;
78 import org.springframework.context.annotation.Bean;
79 import org.springframework.http.HttpStatus;
80 import org.springframework.http.MediaType;
81 import org.springframework.http.ResponseEntity;
82 import org.springframework.test.context.TestPropertySource;
83 import org.springframework.test.context.junit.jupiter.SpringExtension;
84 import org.springframework.web.reactive.function.client.WebClientResponseException;
86 import reactor.core.publisher.Mono;
87 import reactor.test.StepVerifier;
89 @ExtendWith(SpringExtension.class)
90 @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
93 "server.ssl.key-store=./config/keystore.jks", //
94 "app.webclient.trust-store=./config/truststore.jks", //
95 "app.vardata-directory=./target"})
96 class ApplicationTest {
97 private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
99 private final String EI_TYPE_ID = "typeId";
100 private final String EI_PRODUCER_ID = "producerId";
101 private final String EI_JOB_PROPERTY = "\"property1\"";
102 private final String EI_JOB_ID = "jobId";
105 ApplicationContext context;
114 EiProducers eiProducers;
117 ApplicationConfig applicationConfig;
120 ProducerSimulatorController producerSimulator;
123 ConsumerSimulatorController consumerSimulator;
126 ProducerSupervision producerSupervision;
128 private static Gson gson = new GsonBuilder().create();
131 * Overrides the BeanFactory.
134 static class TestBeanFactory {
136 public ServletWebServerFactory servletContainer() {
137 return new TomcatServletWebServerFactory();
147 this.eiTypes.clear();
148 this.eiProducers.clear();
149 this.producerSimulator.getTestResults().reset();
150 this.consumerSimulator.getTestResults().reset();
155 assertThat(this.producerSimulator.getTestResults().errorFound).isFalse();
159 void createApiDoc() throws FileNotFoundException {
160 String url = "/v2/api-docs";
161 ResponseEntity<String> resp = restClient().getForEntity(url).block();
162 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
164 String indented = (new JSONObject(resp.getBody())).toString(4);
165 try (PrintStream out = new PrintStream(new FileOutputStream("../docs/offeredapis/swagger/ecs-api.json"))) {
171 void testGetEiTypes() throws Exception {
172 putEiProducerWithOneType(EI_PRODUCER_ID, "test");
173 String url = ConsumerConsts.API_ROOT + "/eitypes";
174 String rsp = restClient().get(url).block();
175 assertThat(rsp).isEqualTo("[\"test\"]");
179 void testGetEiTypesEmpty() throws Exception {
180 String url = ConsumerConsts.API_ROOT + "/eitypes";
181 String rsp = restClient().get(url).block();
182 assertThat(rsp).isEqualTo("[]");
186 void testGetEiType() throws Exception {
187 putEiProducerWithOneType(EI_PRODUCER_ID, "test");
188 String url = ConsumerConsts.API_ROOT + "/eitypes/test";
189 String rsp = restClient().get(url).block();
190 ConsumerEiTypeInfo info = gson.fromJson(rsp, ConsumerEiTypeInfo.class);
191 assertThat(info).isNotNull();
195 void testGetEiTypeNotFound() throws Exception {
196 String url = ConsumerConsts.API_ROOT + "/eitypes/junk";
197 testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND, "Could not find EI type: junk");
201 void testGetEiJobsIds() throws Exception {
202 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
203 putEiJob(EI_TYPE_ID, "jobId");
204 final String JOB_ID_JSON = "[\"jobId\"]";
205 String url = ConsumerConsts.API_ROOT + "/eijobs?eiTypeId=typeId";
206 String rsp = restClient().get(url).block();
207 assertThat(rsp).isEqualTo(JOB_ID_JSON);
209 url = ConsumerConsts.API_ROOT + "/eijobs?owner=owner";
210 rsp = restClient().get(url).block();
211 assertThat(rsp).isEqualTo(JOB_ID_JSON);
213 url = ConsumerConsts.API_ROOT + "/eijobs?owner=JUNK";
214 rsp = restClient().get(url).block();
215 assertThat(rsp).isEqualTo("[]");
217 url = ConsumerConsts.API_ROOT + "/eijobs";
218 rsp = restClient().get(url).block();
219 assertThat(rsp).isEqualTo(JOB_ID_JSON);
221 url = ConsumerConsts.API_ROOT + "/eijobs?eiTypeId=typeId&&owner=owner";
222 rsp = restClient().get(url).block();
223 assertThat(rsp).isEqualTo(JOB_ID_JSON);
225 url = ConsumerConsts.API_ROOT + "/eijobs?eiTypeId=JUNK";
226 rsp = restClient().get(url).block();
227 assertThat(rsp).isEqualTo("[]");
231 void testGetEiJob() throws Exception {
232 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
233 putEiJob(EI_TYPE_ID, "jobId");
234 String url = ConsumerConsts.API_ROOT + "/eijobs/jobId";
235 String rsp = restClient().get(url).block();
236 ConsumerEiJobInfo info = gson.fromJson(rsp, ConsumerEiJobInfo.class);
237 assertThat(info.owner).isEqualTo("owner");
238 assertThat(info.eiTypeId).isEqualTo(EI_TYPE_ID);
242 void testGetEiJobNotFound() throws Exception {
243 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
244 String url = ConsumerConsts.API_ROOT + "/eijobs/junk";
245 testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND, "Could not find EI job: junk");
249 void testGetEiJobStatus() throws Exception {
250 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
251 putEiJob(EI_TYPE_ID, "jobId");
253 verifyJobStatus("jobId", "ENABLED");
257 void testDeleteEiJob() throws Exception {
258 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
259 putEiJob(EI_TYPE_ID, "jobId");
260 assertThat(this.eiJobs.size()).isEqualTo(1);
261 String url = ConsumerConsts.API_ROOT + "/eijobs/jobId";
262 restClient().delete(url).block();
263 assertThat(this.eiJobs.size()).isZero();
265 ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
266 await().untilAsserted(() -> assertThat(simulatorResults.jobsStopped.size()).isEqualTo(1));
267 assertThat(simulatorResults.jobsStopped.get(0)).isEqualTo("jobId");
271 void testDeleteEiJobNotFound() throws Exception {
272 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
273 String url = ConsumerConsts.API_ROOT + "/eijobs/junk";
274 testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND, "Could not find EI job: junk");
278 void testPutEiJob() throws Exception {
279 // Test that one producer accepting a job is enough
280 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
281 putEiProducerWithOneTypeRejecting("simulateProducerError", EI_TYPE_ID);
283 String url = ConsumerConsts.API_ROOT + "/eijobs/jobId";
284 String body = gson.toJson(eiJobInfo());
285 ResponseEntity<String> resp = restClient().putForEntity(url, body).block();
286 assertThat(this.eiJobs.size()).isEqualTo(1);
287 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.CREATED);
289 ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
290 await().untilAsserted(() -> assertThat(simulatorResults.jobsStarted.size()).isEqualTo(1));
291 ProducerJobInfo request = simulatorResults.jobsStarted.get(0);
292 assertThat(request.id).isEqualTo("jobId");
294 // One retry --> two calls
295 await().untilAsserted(() -> assertThat(simulatorResults.noOfRejectedCreate).isEqualTo(2));
296 assertThat(simulatorResults.noOfRejectedCreate).isEqualTo(2);
298 resp = restClient().putForEntity(url, body).block();
299 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
300 EiJob job = this.eiJobs.getJob("jobId");
301 assertThat(job.getOwner()).isEqualTo("owner");
305 void putEiProducerWithOneType_rejecting() throws JsonMappingException, JsonProcessingException, ServiceException {
306 putEiProducerWithOneTypeRejecting("simulateProducerError", EI_TYPE_ID);
307 String url = ConsumerConsts.API_ROOT + "/eijobs/jobId";
308 String body = gson.toJson(eiJobInfo());
309 testErrorCode(restClient().put(url, body), HttpStatus.CONFLICT, "Job not accepted by any producers");
311 ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
312 // There is one retry -> 2 calls
313 await().untilAsserted(() -> assertThat(simulatorResults.noOfRejectedCreate).isEqualTo(2));
314 assertThat(simulatorResults.noOfRejectedCreate).isEqualTo(2);
318 void testPutEiJob_jsonSchemavalidationError() throws Exception {
319 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
321 String url = ConsumerConsts.API_ROOT + "/eijobs/jobId";
322 // The element with name "property1" is mandatory in the schema
323 ConsumerEiJobInfo jobInfo = new ConsumerEiJobInfo("typeId", jsonObject("{ \"XXstring\" : \"value\" }"), "owner",
324 "targetUri", "jobStatusUrl");
325 String body = gson.toJson(jobInfo);
327 testErrorCode(restClient().put(url, body), HttpStatus.CONFLICT, "Json validation failure");
331 void testGetEiProducerTypes() throws Exception {
332 final String EI_TYPE_ID_2 = EI_TYPE_ID + "_2";
333 putEiProducerWithOneType("producer1", EI_TYPE_ID);
334 putEiJob(EI_TYPE_ID, "jobId");
335 putEiProducerWithOneType("producer2", EI_TYPE_ID_2);
336 putEiJob(EI_TYPE_ID_2, "jobId2");
337 String url = ProducerConsts.API_ROOT + "/eitypes";
339 ResponseEntity<String> resp = restClient().getForEntity(url).block();
340 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
341 assertThat(resp.getBody()).contains(EI_TYPE_ID);
342 assertThat(resp.getBody()).contains(EI_TYPE_ID_2);
346 void testReplacingEiProducerTypes() throws Exception {
347 final String REPLACED_TYPE_ID = "replaced";
348 putEiProducerWithOneType(EI_PRODUCER_ID, REPLACED_TYPE_ID);
349 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
351 String url = ProducerConsts.API_ROOT + "/eitypes";
353 ResponseEntity<String> resp = restClient().getForEntity(url).block();
354 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
355 assertThat(resp.getBody()).contains(EI_TYPE_ID);
356 assertThat(resp.getBody()).doesNotContain(REPLACED_TYPE_ID);
360 void testChangingEiTypeGetRejected() throws Exception {
361 putEiProducerWithOneType("producer1", "typeId1");
362 putEiProducerWithOneType("producer2", "typeId2");
363 putEiJob("typeId1", "jobId");
365 String url = ConsumerConsts.API_ROOT + "/eijobs/jobId";
366 String body = gson.toJson(eiJobInfo("typeId2", "jobId"));
367 testErrorCode(restClient().put(url, body), HttpStatus.CONFLICT,
368 "Not allowed to change type for existing EI job");
372 void testPutEiProducer() throws Exception {
373 String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId";
374 String body = gson.toJson(producerEiRegistratioInfo(EI_TYPE_ID));
376 ResponseEntity<String> resp = restClient().putForEntity(url, body).block();
377 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.CREATED);
379 assertThat(this.eiTypes.size()).isEqualTo(1);
380 EiType type = this.eiTypes.getType(EI_TYPE_ID);
381 assertThat(type.getProducerIds()).contains("eiProducerId");
382 assertThat(this.eiProducers.size()).isEqualTo(1);
383 assertThat(this.eiProducers.get("eiProducerId").getEiTypes().iterator().next().getId()).isEqualTo(EI_TYPE_ID);
385 resp = restClient().putForEntity(url, body).block();
386 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
388 resp = restClient().getForEntity(url).block();
389 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
390 assertThat(resp.getBody()).isEqualTo(body);
394 void testPutEiProducerExistingJob() throws Exception {
395 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
396 putEiJob(EI_TYPE_ID, "jobId");
397 String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId";
398 String body = gson.toJson(producerEiRegistratioInfo(EI_TYPE_ID));
399 restClient().putForEntity(url, body).block();
401 ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
402 await().untilAsserted(() -> assertThat(simulatorResults.jobsStarted.size()).isEqualTo(2));
403 ProducerJobInfo request = simulatorResults.jobsStarted.get(0);
404 assertThat(request.id).isEqualTo("jobId");
408 void testPutProducerAndEiJob() throws Exception {
409 String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId";
410 String body = gson.toJson(producerEiRegistratioInfo(EI_TYPE_ID));
411 restClient().putForEntity(url, body).block();
412 assertThat(this.eiTypes.size()).isEqualTo(1);
413 this.eiTypes.getType(EI_TYPE_ID);
415 url = ConsumerConsts.API_ROOT + "/eijobs/jobId";
416 body = gson.toJson(eiJobInfo());
417 restClient().putForEntity(url, body).block();
419 ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
420 await().untilAsserted(() -> assertThat(simulatorResults.jobsStarted.size()).isEqualTo(1));
421 ProducerJobInfo request = simulatorResults.jobsStarted.get(0);
422 assertThat(request.id).isEqualTo("jobId");
426 void testGetEiJobsForProducer() throws JsonMappingException, JsonProcessingException, ServiceException {
427 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
428 putEiJob(EI_TYPE_ID, "jobId1");
429 putEiJob(EI_TYPE_ID, "jobId2");
432 String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId";
433 String body = gson.toJson(producerEiRegistratioInfo(EI_TYPE_ID));
434 restClient().putForEntity(url, body).block();
436 url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId/eijobs";
437 ResponseEntity<String> resp = restClient().getForEntity(url).block();
438 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
440 ProducerJobInfo[] parsedResp = gson.fromJson(resp.getBody(), ProducerJobInfo[].class);
441 assertThat(parsedResp[0].typeId).isEqualTo(EI_TYPE_ID);
442 assertThat(parsedResp[1].typeId).isEqualTo(EI_TYPE_ID);
446 void testDeleteEiProducer() throws Exception {
447 putEiProducerWithOneType("eiProducerId", EI_TYPE_ID);
448 putEiProducerWithOneType("eiProducerId2", EI_TYPE_ID);
450 assertThat(this.eiProducers.size()).isEqualTo(2);
451 EiType type = this.eiTypes.getType(EI_TYPE_ID);
452 assertThat(type.getProducerIds()).contains("eiProducerId");
453 assertThat(type.getProducerIds()).contains("eiProducerId2");
454 putEiJob(EI_TYPE_ID, "jobId");
455 assertThat(this.eiJobs.size()).isEqualTo(1);
457 deleteEiProducer("eiProducerId");
458 assertThat(this.eiProducers.size()).isEqualTo(1);
459 assertThat(this.eiTypes.getType(EI_TYPE_ID).getProducerIds()).doesNotContain("eiProducerId");
460 verifyJobStatus("jobId", "ENABLED");
462 deleteEiProducer("eiProducerId2");
463 assertThat(this.eiProducers.size()).isZero();
464 assertThat(this.eiTypes.size()).isZero();
465 verifyJobStatus("jobId", "DISABLED");
469 void testJobStatusNotifications() throws JsonMappingException, JsonProcessingException, ServiceException {
470 ConsumerSimulatorController.TestResults consumerCalls = this.consumerSimulator.getTestResults();
471 ProducerSimulatorController.TestResults producerCalls = this.producerSimulator.getTestResults();
473 putEiProducerWithOneType("eiProducerId", EI_TYPE_ID);
474 putEiJob(EI_TYPE_ID, "jobId");
475 putEiProducerWithOneType("eiProducerId2", EI_TYPE_ID);
476 await().untilAsserted(() -> assertThat(producerCalls.jobsStarted.size()).isEqualTo(2));
478 deleteEiProducer("eiProducerId2");
479 assertThat(this.eiTypes.size()).isEqualTo(1); // The type remains, one producer left
480 deleteEiProducer("eiProducerId");
481 assertThat(this.eiTypes.size()).isZero(); // The type is gone
482 assertThat(this.eiJobs.size()).isEqualTo(1); // The job remains
483 await().untilAsserted(() -> assertThat(consumerCalls.status.size()).isEqualTo(1));
484 assertThat(consumerCalls.status.get(0).state).isEqualTo(ConsumerEiJobStatus.EiJobStatusValues.DISABLED);
486 putEiProducerWithOneType("eiProducerId", EI_TYPE_ID);
487 await().untilAsserted(() -> assertThat(consumerCalls.status.size()).isEqualTo(2));
488 assertThat(consumerCalls.status.get(1).state).isEqualTo(ConsumerEiJobStatus.EiJobStatusValues.ENABLED);
492 void testJobStatusNotifications2() throws JsonMappingException, JsonProcessingException, ServiceException {
493 // Test replacing a producer with new and removed types
496 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
497 putEiJob(EI_TYPE_ID, EI_JOB_ID);
499 // change the type for the producer, the EI_TYPE_ID is deleted
500 putEiProducerWithOneType(EI_PRODUCER_ID, "junk");
501 verifyJobStatus(EI_JOB_ID, "DISABLED");
502 ConsumerSimulatorController.TestResults consumerCalls = this.consumerSimulator.getTestResults();
503 await().untilAsserted(() -> assertThat(consumerCalls.status.size()).isEqualTo(1));
504 assertThat(consumerCalls.status.get(0).state).isEqualTo(ConsumerEiJobStatus.EiJobStatusValues.DISABLED);
506 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
507 verifyJobStatus(EI_JOB_ID, "ENABLED");
508 await().untilAsserted(() -> assertThat(consumerCalls.status.size()).isEqualTo(2));
509 assertThat(consumerCalls.status.get(1).state).isEqualTo(ConsumerEiJobStatus.EiJobStatusValues.ENABLED);
513 void testGetProducerEiType() throws JsonMappingException, JsonProcessingException, ServiceException {
514 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
515 String url = ProducerConsts.API_ROOT + "/eitypes/" + EI_TYPE_ID;
516 ResponseEntity<String> resp = restClient().getForEntity(url).block();
517 assertThat(resp.getBody()).contains(EI_PRODUCER_ID);
521 void testGetProducerIdentifiers() throws JsonMappingException, JsonProcessingException, ServiceException {
522 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
523 String url = ProducerConsts.API_ROOT + "/eiproducers";
524 ResponseEntity<String> resp = restClient().getForEntity(url).block();
525 assertThat(resp.getBody()).contains(EI_PRODUCER_ID);
529 void testProducerSupervision() throws JsonMappingException, JsonProcessingException, ServiceException {
530 putEiProducerWithOneTypeRejecting("simulateProducerError", EI_TYPE_ID);
534 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
535 putEiJob(EI_TYPE_ID, EI_JOB_ID);
536 deleteEiProducer(EI_PRODUCER_ID);
539 assertThat(this.eiProducers.size()).isEqualTo(1);
540 assertThat(this.eiTypes.size()).isEqualTo(1);
541 assertProducerOpState("simulateProducerError", ProducerStatusInfo.OperationalState.ENABLED);
543 this.producerSupervision.createTask().blockLast();
544 this.producerSupervision.createTask().blockLast();
546 // Now we have one producer that is disabled, but the job will be enabled until
547 // the producer/type is removed
548 assertThat(this.eiProducers.size()).isEqualTo(1);
549 assertProducerOpState("simulateProducerError", ProducerStatusInfo.OperationalState.DISABLED);
550 verifyJobStatus(EI_JOB_ID, "ENABLED");
552 // After 3 failed checks, the producer and the type shall be deregisterred
553 this.producerSupervision.createTask().blockLast();
554 assertThat(this.eiProducers.size()).isEqualTo(0);
555 assertThat(this.eiTypes.size()).isEqualTo(0);
556 verifyJobStatus(EI_JOB_ID, "DISABLED");
558 // Job disabled status notification shall be received
559 ConsumerSimulatorController.TestResults consumerResults = this.consumerSimulator.getTestResults();
560 await().untilAsserted(() -> assertThat(consumerResults.status.size()).isEqualTo(1));
561 assertThat(consumerResults.status.get(0).state).isEqualTo(ConsumerEiJobStatus.EiJobStatusValues.DISABLED);
565 void testGetStatus() throws JsonMappingException, JsonProcessingException, ServiceException {
566 putEiProducerWithOneTypeRejecting("simulateProducerError", EI_TYPE_ID);
567 putEiProducerWithOneTypeRejecting("simulateProducerError2", EI_TYPE_ID);
569 String url = "/status";
570 ResponseEntity<String> resp = restClient().getForEntity(url).block();
571 assertThat(resp.getBody()).contains("hunky dory");
575 void testEiJobDatabase() throws Exception {
576 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
577 putEiJob(EI_TYPE_ID, "jobId1");
578 putEiJob(EI_TYPE_ID, "jobId2");
580 assertThat(this.eiJobs.size()).isEqualTo(2);
584 EiJobs jobs = new EiJobs(this.applicationConfig);
585 jobs.restoreJobsFromDatabase();
586 assertThat(jobs.size()).isEqualTo(2);
587 jobs.remove("jobId1");
588 jobs.remove("jobId2");
591 // Restore the jobs, no jobs in database
592 EiJobs jobs = new EiJobs(this.applicationConfig);
593 jobs.restoreJobsFromDatabase();
594 assertThat(jobs.size()).isEqualTo(0);
596 logger.warn("Test removing a job when the db file is gone");
597 this.eiJobs.remove("jobId1");
598 assertThat(this.eiJobs.size()).isEqualTo(1);
601 private void deleteEiProducer(String eiProducerId) {
602 String url = ProducerConsts.API_ROOT + "/eiproducers/" + eiProducerId;
603 restClient().deleteForEntity(url).block();
606 private void verifyJobStatus(String jobId, String expStatus) {
607 String url = ConsumerConsts.API_ROOT + "/eijobs/" + jobId + "/status";
608 String rsp = restClient().get(url).block();
609 assertThat(rsp).contains(expStatus);
612 private void assertProducerOpState(String producerId,
613 ProducerStatusInfo.OperationalState expectedOperationalState) {
614 String statusUrl = ProducerConsts.API_ROOT + "/eiproducers/" + producerId + "/status";
615 ResponseEntity<String> resp = restClient().getForEntity(statusUrl).block();
616 ProducerStatusInfo statusInfo = gson.fromJson(resp.getBody(), ProducerStatusInfo.class);
617 assertThat(statusInfo.opState).isEqualTo(expectedOperationalState);
620 ProducerEiTypeRegistrationInfo producerEiTypeRegistrationInfo(String typeId)
621 throws JsonMappingException, JsonProcessingException {
622 return new ProducerEiTypeRegistrationInfo(jsonSchemaObject(), typeId);
625 ProducerRegistrationInfo producerEiRegistratioInfoRejecting(String typeId)
626 throws JsonMappingException, JsonProcessingException {
627 Collection<ProducerEiTypeRegistrationInfo> types = new ArrayList<>();
628 types.add(producerEiTypeRegistrationInfo(typeId));
629 return new ProducerRegistrationInfo(types, //
630 baseUrl() + ProducerSimulatorController.JOB_ERROR_URL,
631 baseUrl() + ProducerSimulatorController.SUPERVISION_ERROR_URL);
634 ProducerRegistrationInfo producerEiRegistratioInfo(String typeId)
635 throws JsonMappingException, JsonProcessingException {
636 Collection<ProducerEiTypeRegistrationInfo> types = new ArrayList<>();
637 types.add(producerEiTypeRegistrationInfo(typeId));
638 return new ProducerRegistrationInfo(types, //
639 baseUrl() + ProducerSimulatorController.JOB_URL, baseUrl() + ProducerSimulatorController.SUPERVISION_URL);
642 private ConsumerEiJobInfo eiJobInfo() throws JsonMappingException, JsonProcessingException {
643 return eiJobInfo(EI_TYPE_ID, EI_JOB_ID);
646 ConsumerEiJobInfo eiJobInfo(String typeId, String eiJobId) throws JsonMappingException, JsonProcessingException {
647 return new ConsumerEiJobInfo(typeId, jsonObject(), "owner", "targetUri",
648 baseUrl() + ConsumerSimulatorController.getJobStatusUrl(eiJobId));
651 private Object jsonObject(String json) {
653 return JsonParser.parseString(json).getAsJsonObject();
654 } catch (Exception e) {
655 throw new NullPointerException(e.toString());
659 private Object jsonSchemaObject() {
660 // a json schema with one mandatory property named "string"
661 String schemaStr = "{" //
662 + "\"$schema\": \"http://json-schema.org/draft-04/schema#\"," //
663 + "\"type\": \"object\"," //
664 + "\"properties\": {" //
665 + EI_JOB_PROPERTY + " : {" //
666 + " \"type\": \"string\"" //
669 + "\"required\": [" //
673 return jsonObject(schemaStr);
676 private Object jsonObject() {
677 return jsonObject("{ " + EI_JOB_PROPERTY + " : \"value\" }");
680 private EiJob putEiJob(String eiTypeId, String jobId)
681 throws JsonMappingException, JsonProcessingException, ServiceException {
683 String url = ConsumerConsts.API_ROOT + "/eijobs/" + jobId;
684 String body = gson.toJson(eiJobInfo(eiTypeId, jobId));
685 restClient().putForEntity(url, body).block();
687 return this.eiJobs.getJob(jobId);
690 private EiType putEiProducerWithOneTypeRejecting(String producerId, String eiTypeId)
691 throws JsonMappingException, JsonProcessingException, ServiceException {
692 String url = ProducerConsts.API_ROOT + "/eiproducers/" + producerId;
693 String body = gson.toJson(producerEiRegistratioInfoRejecting(eiTypeId));
695 restClient().putForEntity(url, body).block();
696 return this.eiTypes.getType(eiTypeId);
699 private EiType putEiProducerWithOneType(String producerId, String eiTypeId)
700 throws JsonMappingException, JsonProcessingException, ServiceException {
701 String url = ProducerConsts.API_ROOT + "/eiproducers/" + producerId;
702 String body = gson.toJson(producerEiRegistratioInfo(eiTypeId));
704 restClient().putForEntity(url, body).block();
705 return this.eiTypes.getType(eiTypeId);
708 private String baseUrl() {
709 return "https://localhost:" + this.port;
712 private AsyncRestClient restClient(boolean useTrustValidation) {
713 WebClientConfig config = this.applicationConfig.getWebClientConfig();
714 config = ImmutableWebClientConfig.builder() //
715 .keyStoreType(config.keyStoreType()) //
716 .keyStorePassword(config.keyStorePassword()) //
717 .keyStore(config.keyStore()) //
718 .keyPassword(config.keyPassword()) //
719 .isTrustStoreUsed(useTrustValidation) //
720 .trustStore(config.trustStore()) //
721 .trustStorePassword(config.trustStorePassword()) //
724 AsyncRestClientFactory restClientFactory = new AsyncRestClientFactory(config);
725 return restClientFactory.createRestClient(baseUrl());
728 private AsyncRestClient restClient() {
729 return restClient(false);
732 private void testErrorCode(Mono<?> request, HttpStatus expStatus, String responseContains) {
733 testErrorCode(request, expStatus, responseContains, true);
736 private void testErrorCode(Mono<?> request, HttpStatus expStatus, String responseContains,
737 boolean expectApplicationProblemJsonMediaType) {
738 StepVerifier.create(request) //
739 .expectSubscription() //
741 t -> checkWebClientError(t, expStatus, responseContains, expectApplicationProblemJsonMediaType)) //
745 private boolean checkWebClientError(Throwable throwable, HttpStatus expStatus, String responseContains,
746 boolean expectApplicationProblemJsonMediaType) {
747 assertTrue(throwable instanceof WebClientResponseException);
748 WebClientResponseException responseException = (WebClientResponseException) throwable;
749 assertThat(responseException.getStatusCode()).isEqualTo(expStatus);
750 assertThat(responseException.getResponseBodyAsString()).contains(responseContains);
751 if (expectApplicationProblemJsonMediaType) {
752 assertThat(responseException.getHeaders().getContentType()).isEqualTo(MediaType.APPLICATION_PROBLEM_JSON);