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.ProducerConsts;
59 import org.oransc.enrichment.controllers.producer.ProducerJobInfo;
60 import org.oransc.enrichment.controllers.producer.ProducerRegistrationInfo;
61 import org.oransc.enrichment.controllers.producer.ProducerRegistrationInfo.ProducerEiTypeRegistrationInfo;
62 import org.oransc.enrichment.controllers.producer.ProducerStatusInfo;
63 import org.oransc.enrichment.exceptions.ServiceException;
64 import org.oransc.enrichment.repository.EiJob;
65 import org.oransc.enrichment.repository.EiJobs;
66 import org.oransc.enrichment.repository.EiProducers;
67 import org.oransc.enrichment.repository.EiType;
68 import org.oransc.enrichment.repository.EiTypes;
69 import org.oransc.enrichment.tasks.ProducerSupervision;
70 import org.slf4j.Logger;
71 import org.slf4j.LoggerFactory;
72 import org.springframework.beans.factory.annotation.Autowired;
73 import org.springframework.boot.test.context.SpringBootTest;
74 import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
75 import org.springframework.boot.test.context.TestConfiguration;
76 import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
77 import org.springframework.boot.web.server.LocalServerPort;
78 import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
79 import org.springframework.context.ApplicationContext;
80 import org.springframework.context.annotation.Bean;
81 import org.springframework.http.HttpStatus;
82 import org.springframework.http.MediaType;
83 import org.springframework.http.ResponseEntity;
84 import org.springframework.test.context.TestPropertySource;
85 import org.springframework.test.context.junit.jupiter.SpringExtension;
86 import org.springframework.web.reactive.function.client.WebClientResponseException;
88 import reactor.core.publisher.Mono;
89 import reactor.test.StepVerifier;
91 @ExtendWith(SpringExtension.class)
92 @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
95 "server.ssl.key-store=./config/keystore.jks", //
96 "app.webclient.trust-store=./config/truststore.jks", //
97 "app.vardata-directory=./target"})
98 class ApplicationTest {
99 private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
101 private final String EI_TYPE_ID = "typeId";
102 private final String EI_PRODUCER_ID = "producerId";
103 private final String EI_JOB_PROPERTY = "\"property1\"";
104 private final String EI_JOB_ID = "jobId";
107 ApplicationContext context;
116 EiProducers eiProducers;
119 ApplicationConfig applicationConfig;
122 ProducerSimulatorController producerSimulator;
125 ConsumerSimulatorController consumerSimulator;
128 ProducerSupervision producerSupervision;
130 private static Gson gson = new GsonBuilder().create();
133 * Overrides the BeanFactory.
136 static class TestBeanFactory {
138 public ServletWebServerFactory servletContainer() {
139 return new TomcatServletWebServerFactory();
149 this.eiTypes.clear();
150 this.eiProducers.clear();
151 this.producerSimulator.getTestResults().reset();
152 this.consumerSimulator.getTestResults().reset();
157 assertThat(this.producerSimulator.getTestResults().errorFound).isFalse();
161 void createApiDoc() throws FileNotFoundException {
162 String url = "/v2/api-docs";
163 ResponseEntity<String> resp = restClient().getForEntity(url).block();
164 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
166 JSONObject jsonObj = new JSONObject(resp.getBody());
167 jsonObj.remove("host");
168 String indented = jsonObj.toString(4);
169 try (PrintStream out = new PrintStream(new FileOutputStream("api/ecs-api.json"))) {
175 void testGetEiTypes() throws Exception {
176 putEiProducerWithOneType(EI_PRODUCER_ID, "test");
177 String url = ConsumerConsts.API_ROOT + "/eitypes";
178 String rsp = restClient().get(url).block();
179 assertThat(rsp).isEqualTo("[\"test\"]");
183 void testGetEiTypesEmpty() throws Exception {
184 String url = ConsumerConsts.API_ROOT + "/eitypes";
185 String rsp = restClient().get(url).block();
186 assertThat(rsp).isEqualTo("[]");
190 void testGetEiType() throws Exception {
191 putEiProducerWithOneType(EI_PRODUCER_ID, "test");
192 String url = ConsumerConsts.API_ROOT + "/eitypes/test";
193 String rsp = restClient().get(url).block();
194 ConsumerEiTypeInfo info = gson.fromJson(rsp, ConsumerEiTypeInfo.class);
195 assertThat(info).isNotNull();
199 void testGetEiTypeNotFound() throws Exception {
200 String url = ConsumerConsts.API_ROOT + "/eitypes/junk";
201 testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND, "Could not find EI type: junk");
205 void testGetEiJobsIds() throws Exception {
206 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
207 putEiJob(EI_TYPE_ID, "jobId");
208 final String JOB_ID_JSON = "[\"jobId\"]";
209 String url = ConsumerConsts.API_ROOT + "/eijobs?eiTypeId=typeId";
210 String rsp = restClient().get(url).block();
211 assertThat(rsp).isEqualTo(JOB_ID_JSON);
213 url = ConsumerConsts.API_ROOT + "/eijobs?owner=owner";
214 rsp = restClient().get(url).block();
215 assertThat(rsp).isEqualTo(JOB_ID_JSON);
217 url = ConsumerConsts.API_ROOT + "/eijobs?owner=JUNK";
218 rsp = restClient().get(url).block();
219 assertThat(rsp).isEqualTo("[]");
221 url = ConsumerConsts.API_ROOT + "/eijobs";
222 rsp = restClient().get(url).block();
223 assertThat(rsp).isEqualTo(JOB_ID_JSON);
225 url = ConsumerConsts.API_ROOT + "/eijobs?eiTypeId=typeId&&owner=owner";
226 rsp = restClient().get(url).block();
227 assertThat(rsp).isEqualTo(JOB_ID_JSON);
229 url = ConsumerConsts.API_ROOT + "/eijobs?eiTypeId=JUNK";
230 rsp = restClient().get(url).block();
231 assertThat(rsp).isEqualTo("[]");
235 void testGetEiJob() throws Exception {
236 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
237 putEiJob(EI_TYPE_ID, "jobId");
238 String url = ConsumerConsts.API_ROOT + "/eijobs/jobId";
239 String rsp = restClient().get(url).block();
240 ConsumerEiJobInfo info = gson.fromJson(rsp, ConsumerEiJobInfo.class);
241 assertThat(info.owner).isEqualTo("owner");
242 assertThat(info.eiTypeId).isEqualTo(EI_TYPE_ID);
246 void testGetEiJobNotFound() throws Exception {
247 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
248 String url = ConsumerConsts.API_ROOT + "/eijobs/junk";
249 testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND, "Could not find EI job: junk");
253 void testGetEiJobStatus() throws Exception {
254 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
255 putEiJob(EI_TYPE_ID, "jobId");
257 verifyJobStatus("jobId", "ENABLED");
261 void testDeleteEiJob() throws Exception {
262 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
263 putEiJob(EI_TYPE_ID, "jobId");
264 assertThat(this.eiJobs.size()).isEqualTo(1);
265 String url = ConsumerConsts.API_ROOT + "/eijobs/jobId";
266 restClient().delete(url).block();
267 assertThat(this.eiJobs.size()).isZero();
269 ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
270 await().untilAsserted(() -> assertThat(simulatorResults.jobsStopped.size()).isEqualTo(1));
271 assertThat(simulatorResults.jobsStopped.get(0)).isEqualTo("jobId");
275 void testDeleteEiJobNotFound() 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 testPutEiJob() throws Exception {
283 // Test that one producer accepting a job is enough
284 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
285 putEiProducerWithOneTypeRejecting("simulateProducerError", EI_TYPE_ID);
287 String url = ConsumerConsts.API_ROOT + "/eijobs/jobId";
288 String body = gson.toJson(eiJobInfo());
289 ResponseEntity<String> resp = restClient().putForEntity(url, body).block();
290 assertThat(this.eiJobs.size()).isEqualTo(1);
291 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.CREATED);
293 ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
294 await().untilAsserted(() -> assertThat(simulatorResults.jobsStarted.size()).isEqualTo(1));
295 ProducerJobInfo request = simulatorResults.jobsStarted.get(0);
296 assertThat(request.id).isEqualTo("jobId");
298 // One retry --> two calls
299 await().untilAsserted(() -> assertThat(simulatorResults.noOfRejectedCreate).isEqualTo(2));
300 assertThat(simulatorResults.noOfRejectedCreate).isEqualTo(2);
302 resp = restClient().putForEntity(url, body).block();
303 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
304 EiJob job = this.eiJobs.getJob("jobId");
305 assertThat(job.getOwner()).isEqualTo("owner");
309 void putEiProducerWithOneType_rejecting() throws JsonMappingException, JsonProcessingException, ServiceException {
310 putEiProducerWithOneTypeRejecting("simulateProducerError", EI_TYPE_ID);
311 String url = ConsumerConsts.API_ROOT + "/eijobs/jobId";
312 String body = gson.toJson(eiJobInfo());
313 testErrorCode(restClient().put(url, body), HttpStatus.CONFLICT, "Job not accepted by any producers");
315 ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
316 // There is one retry -> 2 calls
317 await().untilAsserted(() -> assertThat(simulatorResults.noOfRejectedCreate).isEqualTo(2));
318 assertThat(simulatorResults.noOfRejectedCreate).isEqualTo(2);
322 void testPutEiJob_jsonSchemavalidationError() throws Exception {
323 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
325 String url = ConsumerConsts.API_ROOT + "/eijobs/jobId";
326 // The element with name "property1" is mandatory in the schema
327 ConsumerEiJobInfo jobInfo = new ConsumerEiJobInfo("typeId", jsonObject("{ \"XXstring\" : \"value\" }"), "owner",
328 "targetUri", "jobStatusUrl");
329 String body = gson.toJson(jobInfo);
331 testErrorCode(restClient().put(url, body), HttpStatus.CONFLICT, "Json validation failure");
335 void testGetEiProducerTypes() throws Exception {
336 final String EI_TYPE_ID_2 = EI_TYPE_ID + "_2";
337 putEiProducerWithOneType("producer1", EI_TYPE_ID);
338 putEiJob(EI_TYPE_ID, "jobId");
339 putEiProducerWithOneType("producer2", EI_TYPE_ID_2);
340 putEiJob(EI_TYPE_ID_2, "jobId2");
341 String url = ProducerConsts.API_ROOT + "/eitypes";
343 ResponseEntity<String> resp = restClient().getForEntity(url).block();
344 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
345 assertThat(resp.getBody()).contains(EI_TYPE_ID);
346 assertThat(resp.getBody()).contains(EI_TYPE_ID_2);
350 void testReplacingEiProducerTypes() throws Exception {
351 final String REPLACED_TYPE_ID = "replaced";
352 putEiProducerWithOneType(EI_PRODUCER_ID, REPLACED_TYPE_ID);
353 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
355 String url = ProducerConsts.API_ROOT + "/eitypes";
357 ResponseEntity<String> resp = restClient().getForEntity(url).block();
358 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
359 assertThat(resp.getBody()).contains(EI_TYPE_ID);
360 assertThat(resp.getBody()).doesNotContain(REPLACED_TYPE_ID);
364 void testChangingEiTypeGetRejected() throws Exception {
365 putEiProducerWithOneType("producer1", "typeId1");
366 putEiProducerWithOneType("producer2", "typeId2");
367 putEiJob("typeId1", "jobId");
369 String url = ConsumerConsts.API_ROOT + "/eijobs/jobId";
370 String body = gson.toJson(eiJobInfo("typeId2", "jobId"));
371 testErrorCode(restClient().put(url, body), HttpStatus.CONFLICT,
372 "Not allowed to change type for existing EI job");
376 void testPutEiProducer() throws Exception {
377 String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId";
378 String body = gson.toJson(producerEiRegistratioInfo(EI_TYPE_ID));
380 ResponseEntity<String> resp = restClient().putForEntity(url, body).block();
381 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.CREATED);
383 assertThat(this.eiTypes.size()).isEqualTo(1);
384 EiType type = this.eiTypes.getType(EI_TYPE_ID);
385 assertThat(type.getProducerIds()).contains("eiProducerId");
386 assertThat(this.eiProducers.size()).isEqualTo(1);
387 assertThat(this.eiProducers.get("eiProducerId").getEiTypes().iterator().next().getId()).isEqualTo(EI_TYPE_ID);
389 resp = restClient().putForEntity(url, body).block();
390 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
392 resp = restClient().getForEntity(url).block();
393 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
394 assertThat(resp.getBody()).isEqualTo(body);
398 void testPutEiProducerExistingJob() throws Exception {
399 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
400 putEiJob(EI_TYPE_ID, "jobId");
401 String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId";
402 String body = gson.toJson(producerEiRegistratioInfo(EI_TYPE_ID));
403 restClient().putForEntity(url, body).block();
405 ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
406 await().untilAsserted(() -> assertThat(simulatorResults.jobsStarted.size()).isEqualTo(2));
407 ProducerJobInfo request = simulatorResults.jobsStarted.get(0);
408 assertThat(request.id).isEqualTo("jobId");
412 void testPutProducerAndEiJob() throws Exception {
413 String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId";
414 String body = gson.toJson(producerEiRegistratioInfo(EI_TYPE_ID));
415 restClient().putForEntity(url, body).block();
416 assertThat(this.eiTypes.size()).isEqualTo(1);
417 this.eiTypes.getType(EI_TYPE_ID);
419 url = ConsumerConsts.API_ROOT + "/eijobs/jobId";
420 body = gson.toJson(eiJobInfo());
421 restClient().putForEntity(url, body).block();
423 ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
424 await().untilAsserted(() -> assertThat(simulatorResults.jobsStarted.size()).isEqualTo(1));
425 ProducerJobInfo request = simulatorResults.jobsStarted.get(0);
426 assertThat(request.id).isEqualTo("jobId");
430 void testGetEiJobsForProducer() throws JsonMappingException, JsonProcessingException, ServiceException {
431 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
432 putEiJob(EI_TYPE_ID, "jobId1");
433 putEiJob(EI_TYPE_ID, "jobId2");
436 String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId";
437 String body = gson.toJson(producerEiRegistratioInfo(EI_TYPE_ID));
438 restClient().putForEntity(url, body).block();
440 url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId/eijobs";
441 ResponseEntity<String> resp = restClient().getForEntity(url).block();
442 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
444 ProducerJobInfo[] parsedResp = gson.fromJson(resp.getBody(), ProducerJobInfo[].class);
445 assertThat(parsedResp[0].typeId).isEqualTo(EI_TYPE_ID);
446 assertThat(parsedResp[1].typeId).isEqualTo(EI_TYPE_ID);
450 void testDeleteEiProducer() throws Exception {
451 putEiProducerWithOneType("eiProducerId", EI_TYPE_ID);
452 putEiProducerWithOneType("eiProducerId2", EI_TYPE_ID);
454 assertThat(this.eiProducers.size()).isEqualTo(2);
455 EiType type = this.eiTypes.getType(EI_TYPE_ID);
456 assertThat(type.getProducerIds()).contains("eiProducerId");
457 assertThat(type.getProducerIds()).contains("eiProducerId2");
458 putEiJob(EI_TYPE_ID, "jobId");
459 assertThat(this.eiJobs.size()).isEqualTo(1);
461 deleteEiProducer("eiProducerId");
462 assertThat(this.eiProducers.size()).isEqualTo(1);
463 assertThat(this.eiTypes.getType(EI_TYPE_ID).getProducerIds()).doesNotContain("eiProducerId");
464 verifyJobStatus("jobId", "ENABLED");
466 deleteEiProducer("eiProducerId2");
467 assertThat(this.eiProducers.size()).isZero();
468 assertThat(this.eiTypes.size()).isZero();
469 verifyJobStatus("jobId", "DISABLED");
473 void testJobStatusNotifications() throws JsonMappingException, JsonProcessingException, ServiceException {
474 ConsumerSimulatorController.TestResults consumerCalls = this.consumerSimulator.getTestResults();
475 ProducerSimulatorController.TestResults producerCalls = this.producerSimulator.getTestResults();
477 putEiProducerWithOneType("eiProducerId", EI_TYPE_ID);
478 putEiJob(EI_TYPE_ID, "jobId");
479 putEiProducerWithOneType("eiProducerId2", EI_TYPE_ID);
480 await().untilAsserted(() -> assertThat(producerCalls.jobsStarted.size()).isEqualTo(2));
482 deleteEiProducer("eiProducerId2");
483 assertThat(this.eiTypes.size()).isEqualTo(1); // The type remains, one producer left
484 deleteEiProducer("eiProducerId");
485 assertThat(this.eiTypes.size()).isZero(); // The type is gone
486 assertThat(this.eiJobs.size()).isEqualTo(1); // The job remains
487 await().untilAsserted(() -> assertThat(consumerCalls.status.size()).isEqualTo(1));
488 assertThat(consumerCalls.status.get(0).state).isEqualTo(ConsumerEiJobStatus.EiJobStatusValues.DISABLED);
490 putEiProducerWithOneType("eiProducerId", EI_TYPE_ID);
491 await().untilAsserted(() -> assertThat(consumerCalls.status.size()).isEqualTo(2));
492 assertThat(consumerCalls.status.get(1).state).isEqualTo(ConsumerEiJobStatus.EiJobStatusValues.ENABLED);
496 void testJobStatusNotifications2() throws JsonMappingException, JsonProcessingException, ServiceException {
497 // Test replacing a producer with new and removed types
500 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
501 putEiJob(EI_TYPE_ID, EI_JOB_ID);
503 // change the type for the producer, the EI_TYPE_ID is deleted
504 putEiProducerWithOneType(EI_PRODUCER_ID, "junk");
505 verifyJobStatus(EI_JOB_ID, "DISABLED");
506 ConsumerSimulatorController.TestResults consumerCalls = this.consumerSimulator.getTestResults();
507 await().untilAsserted(() -> assertThat(consumerCalls.status.size()).isEqualTo(1));
508 assertThat(consumerCalls.status.get(0).state).isEqualTo(ConsumerEiJobStatus.EiJobStatusValues.DISABLED);
510 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
511 verifyJobStatus(EI_JOB_ID, "ENABLED");
512 await().untilAsserted(() -> assertThat(consumerCalls.status.size()).isEqualTo(2));
513 assertThat(consumerCalls.status.get(1).state).isEqualTo(ConsumerEiJobStatus.EiJobStatusValues.ENABLED);
517 void testGetProducerEiType() throws JsonMappingException, JsonProcessingException, ServiceException {
518 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
519 String url = ProducerConsts.API_ROOT + "/eitypes/" + EI_TYPE_ID;
520 ResponseEntity<String> resp = restClient().getForEntity(url).block();
521 assertThat(resp.getBody()).contains(EI_PRODUCER_ID);
525 void testGetProducerIdentifiers() throws JsonMappingException, JsonProcessingException, ServiceException {
526 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
527 String url = ProducerConsts.API_ROOT + "/eiproducers";
528 ResponseEntity<String> resp = restClient().getForEntity(url).block();
529 assertThat(resp.getBody()).contains(EI_PRODUCER_ID);
533 void testProducerSupervision() throws JsonMappingException, JsonProcessingException, ServiceException {
534 putEiProducerWithOneTypeRejecting("simulateProducerError", EI_TYPE_ID);
538 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
539 putEiJob(EI_TYPE_ID, EI_JOB_ID);
540 deleteEiProducer(EI_PRODUCER_ID);
543 assertThat(this.eiProducers.size()).isEqualTo(1);
544 assertThat(this.eiTypes.size()).isEqualTo(1);
545 assertProducerOpState("simulateProducerError", ProducerStatusInfo.OperationalState.ENABLED);
547 this.producerSupervision.createTask().blockLast();
548 this.producerSupervision.createTask().blockLast();
550 // Now we have one producer that is disabled, but the job will be enabled until
551 // the producer/type is removed
552 assertThat(this.eiProducers.size()).isEqualTo(1);
553 assertProducerOpState("simulateProducerError", ProducerStatusInfo.OperationalState.DISABLED);
554 verifyJobStatus(EI_JOB_ID, "ENABLED");
556 // After 3 failed checks, the producer and the type shall be deregisterred
557 this.producerSupervision.createTask().blockLast();
558 assertThat(this.eiProducers.size()).isEqualTo(0);
559 assertThat(this.eiTypes.size()).isEqualTo(0);
560 verifyJobStatus(EI_JOB_ID, "DISABLED");
562 // Job disabled status notification shall be received
563 ConsumerSimulatorController.TestResults consumerResults = this.consumerSimulator.getTestResults();
564 await().untilAsserted(() -> assertThat(consumerResults.status.size()).isEqualTo(1));
565 assertThat(consumerResults.status.get(0).state).isEqualTo(ConsumerEiJobStatus.EiJobStatusValues.DISABLED);
569 void testGetStatus() throws JsonMappingException, JsonProcessingException, ServiceException {
570 putEiProducerWithOneTypeRejecting("simulateProducerError", EI_TYPE_ID);
571 putEiProducerWithOneTypeRejecting("simulateProducerError2", EI_TYPE_ID);
573 String url = "/status";
574 ResponseEntity<String> resp = restClient().getForEntity(url).block();
575 assertThat(resp.getBody()).contains("hunky dory");
579 void testEiJobDatabase() throws Exception {
580 putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
581 putEiJob(EI_TYPE_ID, "jobId1");
582 putEiJob(EI_TYPE_ID, "jobId2");
584 assertThat(this.eiJobs.size()).isEqualTo(2);
588 EiJobs jobs = new EiJobs(this.applicationConfig);
589 jobs.restoreJobsFromDatabase();
590 assertThat(jobs.size()).isEqualTo(2);
591 jobs.remove("jobId1");
592 jobs.remove("jobId2");
595 // Restore the jobs, no jobs in database
596 EiJobs jobs = new EiJobs(this.applicationConfig);
597 jobs.restoreJobsFromDatabase();
598 assertThat(jobs.size()).isEqualTo(0);
600 logger.warn("Test removing a job when the db file is gone");
601 this.eiJobs.remove("jobId1");
602 assertThat(this.eiJobs.size()).isEqualTo(1);
605 private void deleteEiProducer(String eiProducerId) {
606 String url = ProducerConsts.API_ROOT + "/eiproducers/" + eiProducerId;
607 restClient().deleteForEntity(url).block();
610 private void verifyJobStatus(String jobId, String expStatus) {
611 String url = ConsumerConsts.API_ROOT + "/eijobs/" + jobId + "/status";
612 String rsp = restClient().get(url).block();
613 assertThat(rsp).contains(expStatus);
616 private void assertProducerOpState(String producerId,
617 ProducerStatusInfo.OperationalState expectedOperationalState) {
618 String statusUrl = ProducerConsts.API_ROOT + "/eiproducers/" + producerId + "/status";
619 ResponseEntity<String> resp = restClient().getForEntity(statusUrl).block();
620 ProducerStatusInfo statusInfo = gson.fromJson(resp.getBody(), ProducerStatusInfo.class);
621 assertThat(statusInfo.opState).isEqualTo(expectedOperationalState);
624 ProducerEiTypeRegistrationInfo producerEiTypeRegistrationInfo(String typeId)
625 throws JsonMappingException, JsonProcessingException {
626 return new ProducerEiTypeRegistrationInfo(jsonSchemaObject(), typeId);
629 ProducerRegistrationInfo producerEiRegistratioInfoRejecting(String typeId)
630 throws JsonMappingException, JsonProcessingException {
631 Collection<ProducerEiTypeRegistrationInfo> types = new ArrayList<>();
632 types.add(producerEiTypeRegistrationInfo(typeId));
633 return new ProducerRegistrationInfo(types, //
634 baseUrl() + ProducerSimulatorController.JOB_ERROR_URL,
635 baseUrl() + ProducerSimulatorController.SUPERVISION_ERROR_URL);
638 ProducerRegistrationInfo producerEiRegistratioInfo(String typeId)
639 throws JsonMappingException, JsonProcessingException {
640 Collection<ProducerEiTypeRegistrationInfo> types = new ArrayList<>();
641 types.add(producerEiTypeRegistrationInfo(typeId));
642 return new ProducerRegistrationInfo(types, //
643 baseUrl() + ProducerSimulatorController.JOB_URL, baseUrl() + ProducerSimulatorController.SUPERVISION_URL);
646 private ConsumerEiJobInfo eiJobInfo() throws JsonMappingException, JsonProcessingException {
647 return eiJobInfo(EI_TYPE_ID, EI_JOB_ID);
650 ConsumerEiJobInfo eiJobInfo(String typeId, String eiJobId) throws JsonMappingException, JsonProcessingException {
651 return new ConsumerEiJobInfo(typeId, jsonObject(), "owner", "targetUri",
652 baseUrl() + ConsumerSimulatorController.getJobStatusUrl(eiJobId));
655 private Object jsonObject(String json) {
657 return JsonParser.parseString(json).getAsJsonObject();
658 } catch (Exception e) {
659 throw new NullPointerException(e.toString());
663 private Object jsonSchemaObject() {
664 // a json schema with one mandatory property named "string"
665 String schemaStr = "{" //
666 + "\"$schema\": \"http://json-schema.org/draft-04/schema#\"," //
667 + "\"type\": \"object\"," //
668 + "\"properties\": {" //
669 + EI_JOB_PROPERTY + " : {" //
670 + " \"type\": \"string\"" //
673 + "\"required\": [" //
677 return jsonObject(schemaStr);
680 private Object jsonObject() {
681 return jsonObject("{ " + EI_JOB_PROPERTY + " : \"value\" }");
684 private EiJob putEiJob(String eiTypeId, String jobId)
685 throws JsonMappingException, JsonProcessingException, ServiceException {
687 String url = ConsumerConsts.API_ROOT + "/eijobs/" + jobId;
688 String body = gson.toJson(eiJobInfo(eiTypeId, jobId));
689 restClient().putForEntity(url, body).block();
691 return this.eiJobs.getJob(jobId);
694 private EiType putEiProducerWithOneTypeRejecting(String producerId, String eiTypeId)
695 throws JsonMappingException, JsonProcessingException, ServiceException {
696 String url = ProducerConsts.API_ROOT + "/eiproducers/" + producerId;
697 String body = gson.toJson(producerEiRegistratioInfoRejecting(eiTypeId));
699 restClient().putForEntity(url, body).block();
700 return this.eiTypes.getType(eiTypeId);
703 private EiType putEiProducerWithOneType(String producerId, String eiTypeId)
704 throws JsonMappingException, JsonProcessingException, ServiceException {
705 String url = ProducerConsts.API_ROOT + "/eiproducers/" + producerId;
706 String body = gson.toJson(producerEiRegistratioInfo(eiTypeId));
708 restClient().putForEntity(url, body).block();
709 return this.eiTypes.getType(eiTypeId);
712 private String baseUrl() {
713 return "https://localhost:" + this.port;
716 private AsyncRestClient restClient(boolean useTrustValidation) {
717 WebClientConfig config = this.applicationConfig.getWebClientConfig();
718 HttpProxyConfig httpProxyConfig = ImmutableHttpProxyConfig.builder() //
719 .httpProxyHost("") //
722 config = ImmutableWebClientConfig.builder() //
723 .keyStoreType(config.keyStoreType()) //
724 .keyStorePassword(config.keyStorePassword()) //
725 .keyStore(config.keyStore()) //
726 .keyPassword(config.keyPassword()) //
727 .isTrustStoreUsed(useTrustValidation) //
728 .trustStore(config.trustStore()) //
729 .trustStorePassword(config.trustStorePassword()) //
730 .httpProxyConfig(httpProxyConfig).build();
732 AsyncRestClientFactory restClientFactory = new AsyncRestClientFactory(config);
733 return restClientFactory.createRestClientNoHttpProxy(baseUrl());
736 private AsyncRestClient restClient() {
737 return restClient(false);
740 private void testErrorCode(Mono<?> request, HttpStatus expStatus, String responseContains) {
741 testErrorCode(request, expStatus, responseContains, true);
744 private void testErrorCode(Mono<?> request, HttpStatus expStatus, String responseContains,
745 boolean expectApplicationProblemJsonMediaType) {
746 StepVerifier.create(request) //
747 .expectSubscription() //
749 t -> checkWebClientError(t, expStatus, responseContains, expectApplicationProblemJsonMediaType)) //
753 private boolean checkWebClientError(Throwable throwable, HttpStatus expStatus, String responseContains,
754 boolean expectApplicationProblemJsonMediaType) {
755 assertTrue(throwable instanceof WebClientResponseException);
756 WebClientResponseException responseException = (WebClientResponseException) throwable;
757 assertThat(responseException.getStatusCode()).isEqualTo(expStatus);
758 assertThat(responseException.getResponseBodyAsString()).contains(responseContains);
759 if (expectApplicationProblemJsonMediaType) {
760 assertThat(responseException.getHeaders().getContentType()).isEqualTo(MediaType.APPLICATION_PROBLEM_JSON);