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.a1e.A1eConsts;
54 import org.oransc.enrichment.controllers.a1e.A1eEiJobInfo;
55 import org.oransc.enrichment.controllers.a1e.A1eEiJobStatus;
56 import org.oransc.enrichment.controllers.a1e.A1eEiTypeInfo;
57 import org.oransc.enrichment.controllers.r1consumer.ConsumerConsts;
58 import org.oransc.enrichment.controllers.r1consumer.ConsumerInfoTypeInfo;
59 import org.oransc.enrichment.controllers.r1consumer.ConsumerJobInfo;
60 import org.oransc.enrichment.controllers.r1producer.ProducerCallbacks;
61 import org.oransc.enrichment.controllers.r1producer.ProducerConsts;
62 import org.oransc.enrichment.controllers.r1producer.ProducerEiTypeInfo;
63 import org.oransc.enrichment.controllers.r1producer.ProducerJobInfo;
64 import org.oransc.enrichment.controllers.r1producer.ProducerRegistrationInfo;
65 import org.oransc.enrichment.controllers.r1producer.ProducerStatusInfo;
66 import org.oransc.enrichment.exceptions.ServiceException;
67 import org.oransc.enrichment.repository.EiJob;
68 import org.oransc.enrichment.repository.EiJobs;
69 import org.oransc.enrichment.repository.EiProducer;
70 import org.oransc.enrichment.repository.EiProducers;
71 import org.oransc.enrichment.repository.EiType;
72 import org.oransc.enrichment.repository.EiTypes;
73 import org.oransc.enrichment.tasks.ProducerSupervision;
74 import org.slf4j.Logger;
75 import org.slf4j.LoggerFactory;
76 import org.springframework.beans.factory.annotation.Autowired;
77 import org.springframework.boot.test.context.SpringBootTest;
78 import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
79 import org.springframework.boot.test.context.TestConfiguration;
80 import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
81 import org.springframework.boot.web.server.LocalServerPort;
82 import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
83 import org.springframework.context.ApplicationContext;
84 import org.springframework.context.annotation.Bean;
85 import org.springframework.http.HttpStatus;
86 import org.springframework.http.MediaType;
87 import org.springframework.http.ResponseEntity;
88 import org.springframework.test.context.TestPropertySource;
89 import org.springframework.test.context.junit.jupiter.SpringExtension;
90 import org.springframework.web.reactive.function.client.WebClientResponseException;
92 import reactor.core.publisher.Mono;
93 import reactor.test.StepVerifier;
95 @ExtendWith(SpringExtension.class)
96 @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
99 "server.ssl.key-store=./config/keystore.jks", //
100 "app.webclient.trust-store=./config/truststore.jks", //
101 "app.vardata-directory=./target"})
102 class ApplicationTest {
103 private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
105 private final String TYPE_ID = "typeId";
106 private final String PRODUCER_ID = "producerId";
107 private final String EI_JOB_PROPERTY = "\"property1\"";
108 private final String EI_JOB_ID = "jobId";
111 ApplicationContext context;
120 EiProducers eiProducers;
123 ApplicationConfig applicationConfig;
126 ProducerSimulatorController producerSimulator;
129 ConsumerSimulatorController consumerSimulator;
132 ProducerSupervision producerSupervision;
135 ProducerCallbacks producerCallbacks;
137 private static Gson gson = new GsonBuilder().create();
140 * Overrides the BeanFactory.
143 static class TestBeanFactory {
145 public ServletWebServerFactory servletContainer() {
146 return new TomcatServletWebServerFactory();
156 this.eiTypes.clear();
157 this.eiProducers.clear();
158 this.producerSimulator.getTestResults().reset();
159 this.consumerSimulator.getTestResults().reset();
164 assertThat(this.producerSimulator.getTestResults().errorFound).isFalse();
168 void generateApiDoc() throws FileNotFoundException {
169 String url = "/v3/api-docs";
170 ResponseEntity<String> resp = restClient().getForEntity(url).block();
171 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
173 JSONObject jsonObj = new JSONObject(resp.getBody());
174 assertThat(jsonObj.remove("servers")).isNotNull();
176 String indented = jsonObj.toString(4);
177 try (PrintStream out = new PrintStream(new FileOutputStream("api/ecs-api.json"))) {
183 void a1eGetEiTypes() throws Exception {
184 putEiProducerWithOneType(PRODUCER_ID, "test");
185 String url = A1eConsts.API_ROOT + "/eitypes";
186 String rsp = restClient().get(url).block();
187 assertThat(rsp).isEqualTo("[\"test\"]");
191 void consumerGetEiTypes() throws Exception {
192 putEiProducerWithOneType(PRODUCER_ID, "test");
193 String url = ConsumerConsts.API_ROOT + "/info-types";
194 String rsp = restClient().get(url).block();
195 assertThat(rsp).isEqualTo("[\"test\"]");
199 void a1eGetEiTypesEmpty() throws Exception {
200 String url = A1eConsts.API_ROOT + "/eitypes";
201 String rsp = restClient().get(url).block();
202 assertThat(rsp).isEqualTo("[]");
206 void consumerGetEiTypesEmpty() throws Exception {
207 String url = ConsumerConsts.API_ROOT + "/info-types";
208 String rsp = restClient().get(url).block();
209 assertThat(rsp).isEqualTo("[]");
213 void a1eGetEiType() throws Exception {
214 putEiProducerWithOneType(PRODUCER_ID, "test");
215 String url = A1eConsts.API_ROOT + "/eitypes/test";
216 String rsp = restClient().get(url).block();
217 A1eEiTypeInfo info = gson.fromJson(rsp, A1eEiTypeInfo.class);
218 assertThat(info).isNotNull();
222 void consumerGetEiType() throws Exception {
223 putEiProducerWithOneType(PRODUCER_ID, "test");
224 String url = ConsumerConsts.API_ROOT + "/info-types/test";
225 String rsp = restClient().get(url).block();
226 ConsumerInfoTypeInfo info = gson.fromJson(rsp, ConsumerInfoTypeInfo.class);
227 assertThat(info).isNotNull();
228 assertThat(info.jobDataSchema).isNotNull();
232 void a1eGetEiTypeNotFound() throws Exception {
233 String url = A1eConsts.API_ROOT + "/eitypes/junk";
234 testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND, "Information type not found: junk");
238 void consumerGetEiTypeNotFound() throws Exception {
239 String url = ConsumerConsts.API_ROOT + "/info-types/junk";
240 testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND, "Information type not found: junk");
244 void a1eGetEiJobsIds() throws Exception {
245 putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
246 putEiJob(TYPE_ID, "jobId");
247 final String JOB_ID_JSON = "[\"jobId\"]";
248 String url = A1eConsts.API_ROOT + "/eijobs?eiTypeId=typeId";
249 String rsp = restClient().get(url).block();
250 assertThat(rsp).isEqualTo(JOB_ID_JSON);
252 url = A1eConsts.API_ROOT + "/eijobs?owner=owner";
253 rsp = restClient().get(url).block();
254 assertThat(rsp).isEqualTo(JOB_ID_JSON);
256 url = A1eConsts.API_ROOT + "/eijobs?owner=JUNK";
257 rsp = restClient().get(url).block();
258 assertThat(rsp).isEqualTo("[]");
260 url = A1eConsts.API_ROOT + "/eijobs";
261 rsp = restClient().get(url).block();
262 assertThat(rsp).isEqualTo(JOB_ID_JSON);
264 url = A1eConsts.API_ROOT + "/eijobs?eiTypeId=typeId&&owner=owner";
265 rsp = restClient().get(url).block();
266 assertThat(rsp).isEqualTo(JOB_ID_JSON);
268 url = A1eConsts.API_ROOT + "/eijobs?eiTypeId=JUNK";
269 rsp = restClient().get(url).block();
270 assertThat(rsp).isEqualTo("[]");
274 void consumerGetInformationJobsIds() throws Exception {
275 putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
276 putEiJob(TYPE_ID, "jobId");
277 final String JOB_ID_JSON = "[\"jobId\"]";
278 String url = ConsumerConsts.API_ROOT + "/info-jobs?infoTypeId=typeId";
279 String rsp = restClient().get(url).block();
280 assertThat(rsp).isEqualTo(JOB_ID_JSON);
282 url = ConsumerConsts.API_ROOT + "/info-jobs?owner=owner";
283 rsp = restClient().get(url).block();
284 assertThat(rsp).isEqualTo(JOB_ID_JSON);
286 url = ConsumerConsts.API_ROOT + "/info-jobs?owner=JUNK";
287 rsp = restClient().get(url).block();
288 assertThat(rsp).isEqualTo("[]");
290 url = ConsumerConsts.API_ROOT + "/info-jobs";
291 rsp = restClient().get(url).block();
292 assertThat(rsp).isEqualTo(JOB_ID_JSON);
294 url = ConsumerConsts.API_ROOT + "/info-jobs?infoTypeId=typeId&&owner=owner";
295 rsp = restClient().get(url).block();
296 assertThat(rsp).isEqualTo(JOB_ID_JSON);
298 url = ConsumerConsts.API_ROOT + "/info-jobs?infoTypeId=JUNK";
299 rsp = restClient().get(url).block();
300 assertThat(rsp).isEqualTo("[]");
304 void a1eGetEiJob() throws Exception {
305 putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
306 putEiJob(TYPE_ID, "jobId");
307 String url = A1eConsts.API_ROOT + "/eijobs/jobId";
308 String rsp = restClient().get(url).block();
309 A1eEiJobInfo info = gson.fromJson(rsp, A1eEiJobInfo.class);
310 assertThat(info.owner).isEqualTo("owner");
311 assertThat(info.eiTypeId).isEqualTo(TYPE_ID);
315 void consumerGetEiJob() throws Exception {
316 putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
317 putEiJob(TYPE_ID, "jobId");
318 String url = ConsumerConsts.API_ROOT + "/info-jobs/jobId";
319 String rsp = restClient().get(url).block();
320 ConsumerJobInfo info = gson.fromJson(rsp, ConsumerJobInfo.class);
321 assertThat(info.owner).isEqualTo("owner");
322 assertThat(info.infoTypeId).isEqualTo(TYPE_ID);
326 void a1eGetEiJobNotFound() throws Exception {
327 putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
328 String url = A1eConsts.API_ROOT + "/eijobs/junk";
329 testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND, "Could not find Information job: junk");
333 void consumerGetInfoJobNotFound() throws Exception {
334 putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
335 String url = ConsumerConsts.API_ROOT + "/info-jobs/junk";
336 testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND, "Could not find Information job: junk");
340 void a1eGetEiJobStatus() throws Exception {
341 putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
342 putEiJob(TYPE_ID, "jobId");
344 verifyJobStatus("jobId", "ENABLED");
348 void consumerGetEiJobStatus() throws Exception {
349 putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
350 putEiJob(TYPE_ID, "jobId");
352 String url = ConsumerConsts.API_ROOT + "/info-jobs/jobId/status";
353 String rsp = restClient().get(url).block();
354 assertThat(rsp).contains("ENABLED");
358 void a1eDeleteEiJob() throws Exception {
359 putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
360 putEiJob(TYPE_ID, "jobId");
361 assertThat(this.eiJobs.size()).isEqualTo(1);
362 String url = A1eConsts.API_ROOT + "/eijobs/jobId";
363 restClient().delete(url).block();
364 assertThat(this.eiJobs.size()).isZero();
366 ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
367 await().untilAsserted(() -> assertThat(simulatorResults.jobsStopped.size()).isEqualTo(1));
368 assertThat(simulatorResults.jobsStopped.get(0)).isEqualTo("jobId");
372 void consumerDeleteEiJob() throws Exception {
373 putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
374 putEiJob(TYPE_ID, "jobId");
375 assertThat(this.eiJobs.size()).isEqualTo(1);
376 String url = ConsumerConsts.API_ROOT + "/info-jobs/jobId";
377 restClient().delete(url).block();
378 assertThat(this.eiJobs.size()).isZero();
380 ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
381 await().untilAsserted(() -> assertThat(simulatorResults.jobsStopped.size()).isEqualTo(1));
382 assertThat(simulatorResults.jobsStopped.get(0)).isEqualTo("jobId");
386 void a1eDeleteEiJobNotFound() throws Exception {
387 putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
388 String url = A1eConsts.API_ROOT + "/eijobs/junk";
389 testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND, "Could not find Information job: junk");
393 void consumerDeleteEiJobNotFound() throws Exception {
394 putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
395 String url = ConsumerConsts.API_ROOT + "/info-jobs/junk";
396 testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND, "Could not find Information job: junk");
400 void a1ePutEiJob() throws Exception {
401 // Test that one producer accepting a job is enough
402 putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
403 putEiProducerWithOneTypeRejecting("simulateProducerError", TYPE_ID);
405 String url = A1eConsts.API_ROOT + "/eijobs/jobId";
406 String body = gson.toJson(eiJobInfo());
407 ResponseEntity<String> resp = restClient().putForEntity(url, body).block();
408 assertThat(this.eiJobs.size()).isEqualTo(1);
409 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.CREATED);
411 ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
412 await().untilAsserted(() -> assertThat(simulatorResults.jobsStarted.size()).isEqualTo(1));
413 ProducerJobInfo request = simulatorResults.jobsStarted.get(0);
414 assertThat(request.id).isEqualTo("jobId");
416 // One retry --> two calls
417 await().untilAsserted(() -> assertThat(simulatorResults.noOfRejectedCreate).isEqualTo(2));
418 assertThat(simulatorResults.noOfRejectedCreate).isEqualTo(2);
420 resp = restClient().putForEntity(url, body).block();
421 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
422 EiJob job = this.eiJobs.getJob("jobId");
423 assertThat(job.getOwner()).isEqualTo("owner");
425 verifyJobStatus(EI_JOB_ID, "ENABLED");
429 void consumerPutInformationJob() throws Exception {
430 // Test that one producer accepting a job is enough
431 putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
433 String url = ConsumerConsts.API_ROOT + "/info-jobs/jobId";
434 String body = gson.toJson(consumerJobInfo());
435 ResponseEntity<String> resp = restClient().putForEntity(url, body).block();
436 assertThat(this.eiJobs.size()).isEqualTo(1);
437 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.CREATED);
439 ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
440 await().untilAsserted(() -> assertThat(simulatorResults.jobsStarted.size()).isEqualTo(1));
441 ProducerJobInfo request = simulatorResults.jobsStarted.get(0);
442 assertThat(request.id).isEqualTo("jobId");
444 resp = restClient().putForEntity(url, body).block();
445 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
446 EiJob job = this.eiJobs.getJob("jobId");
447 assertThat(job.getOwner()).isEqualTo("owner");
449 verifyJobStatus(EI_JOB_ID, "ENABLED");
453 void a1ePutEiJob_jsonSchemavalidationError() throws Exception {
454 putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
456 String url = A1eConsts.API_ROOT + "/eijobs/jobId";
457 // The element with name "property1" is mandatory in the schema
458 A1eEiJobInfo jobInfo = new A1eEiJobInfo("typeId", jsonObject("{ \"XXstring\" : \"value\" }"), "owner",
459 "targetUri", "jobStatusUrl");
460 String body = gson.toJson(jobInfo);
462 testErrorCode(restClient().put(url, body), HttpStatus.CONFLICT, "Json validation failure");
466 void consumerPutJob_jsonSchemavalidationError() throws Exception {
467 putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
469 String url = ConsumerConsts.API_ROOT + "/info-jobs/jobId";
470 // The element with name "property1" is mandatory in the schema
471 ConsumerJobInfo jobInfo = new ConsumerJobInfo("typeId", jsonObject("{ \"XXstring\" : \"value\" }"), "owner",
472 "targetUri", "jobStatusUrl");
473 String body = gson.toJson(jobInfo);
475 testErrorCode(restClient().put(url, body), HttpStatus.CONFLICT, "Json validation failure");
479 void a1eChangingEiTypeGetRejected() throws Exception {
480 putEiProducerWithOneType("producer1", "typeId1");
481 putEiProducerWithOneType("producer2", "typeId2");
482 putEiJob("typeId1", "jobId");
484 String url = A1eConsts.API_ROOT + "/eijobs/jobId";
485 String body = gson.toJson(eiJobInfo("typeId2", "jobId"));
486 testErrorCode(restClient().put(url, body), HttpStatus.CONFLICT,
487 "Not allowed to change type for existing EI job");
491 void consumerChangingInfoTypeGetRejected() throws Exception {
492 putEiProducerWithOneType("producer1", "typeId1");
493 putEiProducerWithOneType("producer2", "typeId2");
494 putEiJob("typeId1", "jobId");
496 String url = ConsumerConsts.API_ROOT + "/info-jobs/jobId";
497 String body = gson.toJson(consumerJobInfo("typeId2", "jobId"));
498 testErrorCode(restClient().put(url, body), HttpStatus.CONFLICT, "Not allowed to change type for existing job");
502 void producerPutEiType() throws JsonMappingException, JsonProcessingException, ServiceException {
503 assertThat(putEiType(TYPE_ID)).isEqualTo(HttpStatus.CREATED);
504 assertThat(putEiType(TYPE_ID)).isEqualTo(HttpStatus.OK);
508 void producerPutEiType_noSchema() {
509 String url = ProducerConsts.API_ROOT + "/eitypes/" + TYPE_ID;
511 testErrorCode(restClient().put(url, body), HttpStatus.BAD_REQUEST, "No schema provided");
515 void producerDeleteEiType() throws Exception {
517 String url = ProducerConsts.API_ROOT + "/eitypes/" + TYPE_ID;
518 restClient().delete(url).block();
519 assertThat(this.eiTypes.size()).isEqualTo(0);
521 testErrorCode(restClient().delete(url), HttpStatus.NOT_FOUND, "Information type not found");
525 void producerDeleteEiTypeExistingProducer() throws Exception {
526 putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
527 String url = ProducerConsts.API_ROOT + "/eitypes/" + TYPE_ID;
528 testErrorCode(restClient().delete(url), HttpStatus.NOT_ACCEPTABLE,
529 "The type has active producers: " + PRODUCER_ID);
530 assertThat(this.eiTypes.size()).isEqualTo(1);
534 void producerPutProducerWithOneType_rejecting()
535 throws JsonMappingException, JsonProcessingException, ServiceException {
536 putEiProducerWithOneTypeRejecting("simulateProducerError", TYPE_ID);
537 String url = A1eConsts.API_ROOT + "/eijobs/" + EI_JOB_ID;
538 String body = gson.toJson(eiJobInfo());
539 restClient().put(url, body).block();
541 ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
542 // There is one retry -> 2 calls
543 await().untilAsserted(() -> assertThat(simulatorResults.noOfRejectedCreate).isEqualTo(2));
544 assertThat(simulatorResults.noOfRejectedCreate).isEqualTo(2);
546 verifyJobStatus(EI_JOB_ID, "DISABLED");
550 void producerGetEiProducerTypes() throws Exception {
551 final String EI_TYPE_ID_2 = TYPE_ID + "_2";
552 putEiProducerWithOneType("producer1", TYPE_ID);
553 putEiJob(TYPE_ID, "jobId");
554 putEiProducerWithOneType("producer2", EI_TYPE_ID_2);
555 putEiJob(EI_TYPE_ID_2, "jobId2");
556 String url = ProducerConsts.API_ROOT + "/eitypes";
558 ResponseEntity<String> resp = restClient().getForEntity(url).block();
559 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
560 assertThat(resp.getBody()).contains(TYPE_ID);
561 assertThat(resp.getBody()).contains(EI_TYPE_ID_2);
565 void producerPutEiProducer() throws Exception {
566 this.putEiType(TYPE_ID);
567 String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId";
568 String body = gson.toJson(producerEiRegistratioInfo(TYPE_ID));
570 ResponseEntity<String> resp = restClient().putForEntity(url, body).block();
571 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.CREATED);
573 assertThat(this.eiTypes.size()).isEqualTo(1);
574 assertThat(this.eiProducers.getProducersForType(TYPE_ID).size()).isEqualTo(1);
575 assertThat(this.eiProducers.size()).isEqualTo(1);
576 assertThat(this.eiProducers.get("eiProducerId").getEiTypes().iterator().next().getId()).isEqualTo(TYPE_ID);
578 resp = restClient().putForEntity(url, body).block();
579 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
581 resp = restClient().getForEntity(url).block();
582 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
583 assertThat(resp.getBody()).isEqualTo(body);
587 void producerPutEiProducerExistingJob() throws Exception {
588 putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
589 putEiJob(TYPE_ID, "jobId");
590 String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId";
591 String body = gson.toJson(producerEiRegistratioInfo(TYPE_ID));
592 restClient().putForEntity(url, body).block();
594 ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
595 await().untilAsserted(() -> assertThat(simulatorResults.jobsStarted.size()).isEqualTo(2));
596 ProducerJobInfo request = simulatorResults.jobsStarted.get(0);
597 assertThat(request.id).isEqualTo("jobId");
601 void testPutEiProducer_noType() throws Exception {
602 String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId";
603 String body = gson.toJson(producerEiRegistratioInfo(TYPE_ID));
604 testErrorCode(restClient().put(url, body), HttpStatus.NOT_FOUND, "Information type not found");
608 void producerPutProducerAndEiJob() throws Exception {
609 this.putEiType(TYPE_ID);
610 String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId";
611 String body = gson.toJson(producerEiRegistratioInfo(TYPE_ID));
612 restClient().putForEntity(url, body).block();
613 assertThat(this.eiTypes.size()).isEqualTo(1);
614 this.eiTypes.getType(TYPE_ID);
616 url = A1eConsts.API_ROOT + "/eijobs/jobId";
617 body = gson.toJson(eiJobInfo());
618 restClient().putForEntity(url, body).block();
620 ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
621 await().untilAsserted(() -> assertThat(simulatorResults.jobsStarted.size()).isEqualTo(1));
622 ProducerJobInfo request = simulatorResults.jobsStarted.get(0);
623 assertThat(request.id).isEqualTo("jobId");
627 void producerGetEiJobsForProducer() throws JsonMappingException, JsonProcessingException, ServiceException {
628 putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
629 putEiJob(TYPE_ID, "jobId1");
630 putEiJob(TYPE_ID, "jobId2");
632 // PUT a consumerRestApiTestBase.java
633 String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId";
634 String body = gson.toJson(producerEiRegistratioInfo(TYPE_ID));
635 restClient().putForEntity(url, body).block();
637 url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId/eijobs";
638 ResponseEntity<String> resp = restClient().getForEntity(url).block();
639 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
641 ProducerJobInfo[] parsedResp = gson.fromJson(resp.getBody(), ProducerJobInfo[].class);
642 assertThat(parsedResp[0].typeId).isEqualTo(TYPE_ID);
643 assertThat(parsedResp[1].typeId).isEqualTo(TYPE_ID);
647 void producerDeleteEiProducer() throws Exception {
648 putEiProducerWithOneType("eiProducerId", TYPE_ID);
649 putEiProducerWithOneType("eiProducerId2", TYPE_ID);
651 assertThat(this.eiProducers.size()).isEqualTo(2);
652 EiType type = this.eiTypes.getType(TYPE_ID);
653 assertThat(this.eiProducers.getProducerIdsForType(type.getId())).contains("eiProducerId");
654 assertThat(this.eiProducers.getProducerIdsForType(type.getId())).contains("eiProducerId2");
655 putEiJob(TYPE_ID, "jobId");
656 assertThat(this.eiJobs.size()).isEqualTo(1);
658 deleteEiProducer("eiProducerId");
659 assertThat(this.eiProducers.size()).isEqualTo(1);
660 assertThat(this.eiProducers.getProducerIdsForType(TYPE_ID)).doesNotContain("eiProducerId");
661 verifyJobStatus("jobId", "ENABLED");
663 deleteEiProducer("eiProducerId2");
664 assertThat(this.eiProducers.size()).isZero();
665 assertThat(this.eiTypes.size()).isEqualTo(1);
666 verifyJobStatus("jobId", "DISABLED");
670 void a1eJobStatusNotifications() throws JsonMappingException, JsonProcessingException, ServiceException {
671 ConsumerSimulatorController.TestResults consumerCalls = this.consumerSimulator.getTestResults();
672 ProducerSimulatorController.TestResults producerCalls = this.producerSimulator.getTestResults();
674 putEiProducerWithOneType("eiProducerId", TYPE_ID);
675 putEiJob(TYPE_ID, "jobId");
676 putEiProducerWithOneType("eiProducerId2", TYPE_ID);
677 await().untilAsserted(() -> assertThat(producerCalls.jobsStarted.size()).isEqualTo(2));
679 deleteEiProducer("eiProducerId2");
680 assertThat(this.eiTypes.size()).isEqualTo(1); // The type remains, one producer left
681 deleteEiProducer("eiProducerId");
682 assertThat(this.eiTypes.size()).isEqualTo(1); // The type remains
683 assertThat(this.eiJobs.size()).isEqualTo(1); // The job remains
684 await().untilAsserted(() -> assertThat(consumerCalls.status.size()).isEqualTo(1));
685 assertThat(consumerCalls.status.get(0).state).isEqualTo(A1eEiJobStatus.EiJobStatusValues.DISABLED);
687 putEiProducerWithOneType("eiProducerId", TYPE_ID);
688 await().untilAsserted(() -> assertThat(consumerCalls.status.size()).isEqualTo(2));
689 assertThat(consumerCalls.status.get(1).state).isEqualTo(A1eEiJobStatus.EiJobStatusValues.ENABLED);
693 void a1eJobStatusNotifications2() throws JsonMappingException, JsonProcessingException, ServiceException {
694 // Test replacing a producer with new and removed types
697 putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
698 putEiJob(TYPE_ID, EI_JOB_ID);
700 // change the type for the producer, the job shall be disabled
701 putEiProducerWithOneType(PRODUCER_ID, "junk");
702 verifyJobStatus(EI_JOB_ID, "DISABLED");
703 ConsumerSimulatorController.TestResults consumerCalls = this.consumerSimulator.getTestResults();
704 await().untilAsserted(() -> assertThat(consumerCalls.status.size()).isEqualTo(1));
705 assertThat(consumerCalls.status.get(0).state).isEqualTo(A1eEiJobStatus.EiJobStatusValues.DISABLED);
707 putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
708 verifyJobStatus(EI_JOB_ID, "ENABLED");
709 await().untilAsserted(() -> assertThat(consumerCalls.status.size()).isEqualTo(2));
710 assertThat(consumerCalls.status.get(1).state).isEqualTo(A1eEiJobStatus.EiJobStatusValues.ENABLED);
714 void producerGetProducerEiType() throws JsonMappingException, JsonProcessingException, ServiceException {
715 putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
716 String url = ProducerConsts.API_ROOT + "/eitypes/" + TYPE_ID;
717 ResponseEntity<String> resp = restClient().getForEntity(url).block();
718 ProducerEiTypeInfo info = gson.fromJson(resp.getBody(), ProducerEiTypeInfo.class);
719 assertThat(info.jobDataSchema).isNotNull();
723 void producerGetProducerIdentifiers() throws JsonMappingException, JsonProcessingException, ServiceException {
724 putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
725 String url = ProducerConsts.API_ROOT + "/eiproducers";
726 ResponseEntity<String> resp = restClient().getForEntity(url).block();
727 assertThat(resp.getBody()).contains(PRODUCER_ID);
729 url = ProducerConsts.API_ROOT + "/eiproducers?ei_type_id=" + TYPE_ID;
730 resp = restClient().getForEntity(url).block();
731 assertThat(resp.getBody()).contains(PRODUCER_ID);
733 url = ProducerConsts.API_ROOT + "/eiproducers?ei_type_id=junk";
734 resp = restClient().getForEntity(url).block();
735 assertThat(resp.getBody()).isEqualTo("[]");
739 void producerSupervision() throws JsonMappingException, JsonProcessingException, ServiceException {
741 ConsumerSimulatorController.TestResults consumerResults = this.consumerSimulator.getTestResults();
742 putEiProducerWithOneTypeRejecting("simulateProducerError", TYPE_ID);
746 putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
747 putEiJob(TYPE_ID, EI_JOB_ID);
748 verifyJobStatus(EI_JOB_ID, "ENABLED");
749 deleteEiProducer(PRODUCER_ID);
750 // A Job disabled status notification shall now be received
751 await().untilAsserted(() -> assertThat(consumerResults.status.size()).isEqualTo(1));
752 assertThat(consumerResults.status.get(0).state).isEqualTo(A1eEiJobStatus.EiJobStatusValues.DISABLED);
753 verifyJobStatus(EI_JOB_ID, "DISABLED");
756 assertThat(this.eiProducers.size()).isEqualTo(1);
757 assertThat(this.eiTypes.size()).isEqualTo(1);
758 assertProducerOpState("simulateProducerError", ProducerStatusInfo.OperationalState.ENABLED);
760 this.producerSupervision.createTask().blockLast();
761 this.producerSupervision.createTask().blockLast();
763 // Now we have one producer that is disabled
764 assertThat(this.eiProducers.size()).isEqualTo(1);
765 assertProducerOpState("simulateProducerError", ProducerStatusInfo.OperationalState.DISABLED);
767 // After 3 failed checks, the producer shall be deregisterred
768 this.producerSupervision.createTask().blockLast();
769 assertThat(this.eiProducers.size()).isEqualTo(0); // The producer is removed
770 assertThat(this.eiTypes.size()).isEqualTo(1); // The type remains
772 // Now we have one disabled job, and no producer.
773 // PUT a producer, then a Job ENABLED status notification shall be received
774 putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
775 await().untilAsserted(() -> assertThat(consumerResults.status.size()).isEqualTo(2));
776 assertThat(consumerResults.status.get(1).state).isEqualTo(A1eEiJobStatus.EiJobStatusValues.ENABLED);
777 verifyJobStatus(EI_JOB_ID, "ENABLED");
781 void producerSupervision2() throws JsonMappingException, JsonProcessingException, ServiceException {
782 // Test that supervision enables not enabled jobs and sends a notification when
785 putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
786 putEiJob(TYPE_ID, EI_JOB_ID);
788 EiProducer producer = this.eiProducers.getProducer(PRODUCER_ID);
789 EiJob job = this.eiJobs.getJob(EI_JOB_ID);
790 // Pretend that the producer did reject the job and the a DISABLED notification
791 // is sent for the job
792 producer.setJobDisabled(job);
793 job.setLastReportedStatus(false);
794 verifyJobStatus(EI_JOB_ID, "DISABLED");
796 // Run the supervision and wait for the job to get started in the producer
797 this.producerSupervision.createTask().blockLast();
798 ConsumerSimulatorController.TestResults consumerResults = this.consumerSimulator.getTestResults();
799 await().untilAsserted(() -> assertThat(consumerResults.status.size()).isEqualTo(1));
800 assertThat(consumerResults.status.get(0).state).isEqualTo(A1eEiJobStatus.EiJobStatusValues.ENABLED);
801 verifyJobStatus(EI_JOB_ID, "ENABLED");
805 void testGetStatus() throws JsonMappingException, JsonProcessingException, ServiceException {
806 putEiProducerWithOneTypeRejecting("simulateProducerError", TYPE_ID);
807 putEiProducerWithOneTypeRejecting("simulateProducerError2", TYPE_ID);
809 String url = "/status";
810 ResponseEntity<String> resp = restClient().getForEntity(url).block();
811 assertThat(resp.getBody()).contains("hunky dory");
815 void testEiJobDatabase() throws Exception {
816 putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
817 putEiJob(TYPE_ID, "jobId1");
818 putEiJob(TYPE_ID, "jobId2");
820 assertThat(this.eiJobs.size()).isEqualTo(2);
823 EiJob savedJob = this.eiJobs.getJob("jobId1");
825 EiJobs jobs = new EiJobs(this.applicationConfig, this.producerCallbacks);
826 jobs.restoreJobsFromDatabase();
827 assertThat(jobs.size()).isEqualTo(2);
828 EiJob restoredJob = jobs.getJob("jobId1");
829 assertThat(restoredJob.getId()).isEqualTo("jobId1");
830 assertThat(restoredJob.getLastUpdated()).isEqualTo(savedJob.getLastUpdated());
832 jobs.remove("jobId1", this.eiProducers);
833 jobs.remove("jobId2", this.eiProducers);
836 // Restore the jobs, no jobs in database
837 EiJobs jobs = new EiJobs(this.applicationConfig, this.producerCallbacks);
838 jobs.restoreJobsFromDatabase();
839 assertThat(jobs.size()).isEqualTo(0);
841 logger.warn("Test removing a job when the db file is gone");
842 this.eiJobs.remove("jobId1", this.eiProducers);
843 assertThat(this.eiJobs.size()).isEqualTo(1);
845 ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
846 await().untilAsserted(() -> assertThat(simulatorResults.jobsStopped.size()).isEqualTo(3));
850 void testEiTypesDatabase() throws Exception {
851 putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
853 assertThat(this.eiTypes.size()).isEqualTo(1);
857 EiTypes types = new EiTypes(this.applicationConfig);
858 types.restoreTypesFromDatabase();
859 assertThat(types.size()).isEqualTo(1);
863 // Restore the jobs, no jobs in database
864 EiTypes types = new EiTypes(this.applicationConfig);
866 types.restoreTypesFromDatabase();
867 assertThat(types.size()).isEqualTo(0);
869 logger.warn("Test removing a job when the db file is gone");
870 this.eiTypes.remove(this.eiTypes.getType(TYPE_ID));
871 assertThat(this.eiJobs.size()).isEqualTo(0);
874 private void deleteEiProducer(String eiProducerId) {
875 String url = ProducerConsts.API_ROOT + "/eiproducers/" + eiProducerId;
876 restClient().deleteForEntity(url).block();
879 private void verifyJobStatus(String jobId, String expStatus) {
880 String url = A1eConsts.API_ROOT + "/eijobs/" + jobId + "/status";
881 String rsp = restClient().get(url).block();
882 assertThat(rsp).contains(expStatus);
885 private void assertProducerOpState(String producerId,
886 ProducerStatusInfo.OperationalState expectedOperationalState) {
887 String statusUrl = ProducerConsts.API_ROOT + "/eiproducers/" + producerId + "/status";
888 ResponseEntity<String> resp = restClient().getForEntity(statusUrl).block();
889 ProducerStatusInfo statusInfo = gson.fromJson(resp.getBody(), ProducerStatusInfo.class);
890 assertThat(statusInfo.opState).isEqualTo(expectedOperationalState);
893 ProducerEiTypeInfo producerEiTypeRegistrationInfo(String typeId)
894 throws JsonMappingException, JsonProcessingException {
895 return new ProducerEiTypeInfo(jsonSchemaObject());
898 ProducerRegistrationInfo producerEiRegistratioInfoRejecting(String typeId)
899 throws JsonMappingException, JsonProcessingException {
900 return new ProducerRegistrationInfo(Arrays.asList(typeId), //
901 baseUrl() + ProducerSimulatorController.JOB_ERROR_URL,
902 baseUrl() + ProducerSimulatorController.SUPERVISION_ERROR_URL);
905 ProducerRegistrationInfo producerEiRegistratioInfo(String typeId)
906 throws JsonMappingException, JsonProcessingException {
907 return new ProducerRegistrationInfo(Arrays.asList(typeId), //
908 baseUrl() + ProducerSimulatorController.JOB_URL, baseUrl() + ProducerSimulatorController.SUPERVISION_URL);
911 private ConsumerJobInfo consumerJobInfo() throws JsonMappingException, JsonProcessingException {
912 return consumerJobInfo(TYPE_ID, EI_JOB_ID);
915 ConsumerJobInfo consumerJobInfo(String typeId, String eiJobId)
916 throws JsonMappingException, JsonProcessingException {
917 return new ConsumerJobInfo(typeId, jsonObject(), "owner", "targetUri",
918 baseUrl() + ConsumerSimulatorController.getJobStatusUrl(eiJobId));
921 private A1eEiJobInfo eiJobInfo() throws JsonMappingException, JsonProcessingException {
922 return eiJobInfo(TYPE_ID, EI_JOB_ID);
925 A1eEiJobInfo eiJobInfo(String typeId, String eiJobId) throws JsonMappingException, JsonProcessingException {
926 return new A1eEiJobInfo(typeId, jsonObject(), "owner", "targetUri",
927 baseUrl() + ConsumerSimulatorController.getJobStatusUrl(eiJobId));
930 private Object jsonObject(String json) {
932 return JsonParser.parseString(json).getAsJsonObject();
933 } catch (Exception e) {
934 throw new NullPointerException(e.toString());
938 private Object jsonSchemaObject() {
939 // a json schema with one mandatory property named "string"
940 String schemaStr = "{" //
941 + "\"$schema\": \"http://json-schema.org/draft-04/schema#\"," //
942 + "\"type\": \"object\"," //
943 + "\"properties\": {" //
944 + EI_JOB_PROPERTY + " : {" //
945 + " \"type\": \"string\"" //
948 + "\"required\": [" //
952 return jsonObject(schemaStr);
955 private Object jsonObject() {
956 return jsonObject("{ " + EI_JOB_PROPERTY + " : \"value\" }");
959 private EiJob putEiJob(String eiTypeId, String jobId)
960 throws JsonMappingException, JsonProcessingException, ServiceException {
962 String url = A1eConsts.API_ROOT + "/eijobs/" + jobId;
963 String body = gson.toJson(eiJobInfo(eiTypeId, jobId));
964 restClient().putForEntity(url, body).block();
966 return this.eiJobs.getJob(jobId);
969 private HttpStatus putEiType(String eiTypeId)
970 throws JsonMappingException, JsonProcessingException, ServiceException {
971 String url = ProducerConsts.API_ROOT + "/eitypes/" + eiTypeId;
972 String body = gson.toJson(producerEiTypeRegistrationInfo(eiTypeId));
974 ResponseEntity<String> resp = restClient().putForEntity(url, body).block();
975 this.eiTypes.getType(eiTypeId);
976 return resp.getStatusCode();
980 private EiType putEiProducerWithOneTypeRejecting(String producerId, String eiTypeId)
981 throws JsonMappingException, JsonProcessingException, ServiceException {
982 this.putEiType(eiTypeId);
983 String url = ProducerConsts.API_ROOT + "/eiproducers/" + producerId;
984 String body = gson.toJson(producerEiRegistratioInfoRejecting(eiTypeId));
985 restClient().putForEntity(url, body).block();
986 return this.eiTypes.getType(eiTypeId);
989 private EiType putEiProducerWithOneType(String producerId, String eiTypeId)
990 throws JsonMappingException, JsonProcessingException, ServiceException {
991 this.putEiType(eiTypeId);
993 String url = ProducerConsts.API_ROOT + "/eiproducers/" + producerId;
994 String body = gson.toJson(producerEiRegistratioInfo(eiTypeId));
996 restClient().putForEntity(url, body).block();
998 return this.eiTypes.getType(eiTypeId);
1001 private String baseUrl() {
1002 return "https://localhost:" + this.port;
1005 private AsyncRestClient restClient(boolean useTrustValidation) {
1006 WebClientConfig config = this.applicationConfig.getWebClientConfig();
1007 HttpProxyConfig httpProxyConfig = ImmutableHttpProxyConfig.builder() //
1008 .httpProxyHost("") //
1009 .httpProxyPort(0) //
1011 config = ImmutableWebClientConfig.builder() //
1012 .keyStoreType(config.keyStoreType()) //
1013 .keyStorePassword(config.keyStorePassword()) //
1014 .keyStore(config.keyStore()) //
1015 .keyPassword(config.keyPassword()) //
1016 .isTrustStoreUsed(useTrustValidation) //
1017 .trustStore(config.trustStore()) //
1018 .trustStorePassword(config.trustStorePassword()) //
1019 .httpProxyConfig(httpProxyConfig).build();
1021 AsyncRestClientFactory restClientFactory = new AsyncRestClientFactory(config);
1022 return restClientFactory.createRestClientNoHttpProxy(baseUrl());
1025 private AsyncRestClient restClient() {
1026 return restClient(false);
1029 private void testErrorCode(Mono<?> request, HttpStatus expStatus, String responseContains) {
1030 testErrorCode(request, expStatus, responseContains, true);
1033 private void testErrorCode(Mono<?> request, HttpStatus expStatus, String responseContains,
1034 boolean expectApplicationProblemJsonMediaType) {
1035 StepVerifier.create(request) //
1036 .expectSubscription() //
1037 .expectErrorMatches(
1038 t -> checkWebClientError(t, expStatus, responseContains, expectApplicationProblemJsonMediaType)) //
1042 private boolean checkWebClientError(Throwable throwable, HttpStatus expStatus, String responseContains,
1043 boolean expectApplicationProblemJsonMediaType) {
1044 assertTrue(throwable instanceof WebClientResponseException);
1045 WebClientResponseException responseException = (WebClientResponseException) throwable;
1046 assertThat(responseException.getStatusCode()).isEqualTo(expStatus);
1047 assertThat(responseException.getResponseBodyAsString()).contains(responseContains);
1048 if (expectApplicationProblemJsonMediaType) {
1049 assertThat(responseException.getHeaders().getContentType()).isEqualTo(MediaType.APPLICATION_PROBLEM_JSON);