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 consumerPutInformationJob_noType() throws JsonMappingException, JsonProcessingException, ServiceException {
454 String url = ConsumerConsts.API_ROOT + "/info-jobs/jobId?typeCheck=false";
455 String body = gson.toJson(consumerJobInfo());
456 ResponseEntity<String> resp = restClient().putForEntity(url, body).block();
457 assertThat(this.eiJobs.size()).isEqualTo(1);
458 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.CREATED);
459 verifyJobStatus(EI_JOB_ID, "DISABLED");
461 putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
463 verifyJobStatus(EI_JOB_ID, "ENABLED");
468 void a1ePutEiJob_jsonSchemavalidationError() throws Exception {
469 putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
471 String url = A1eConsts.API_ROOT + "/eijobs/jobId";
472 // The element with name "property1" is mandatory in the schema
473 A1eEiJobInfo jobInfo = new A1eEiJobInfo("typeId", jsonObject("{ \"XXstring\" : \"value\" }"), "owner",
474 "targetUri", "jobStatusUrl");
475 String body = gson.toJson(jobInfo);
477 testErrorCode(restClient().put(url, body), HttpStatus.CONFLICT, "Json validation failure");
481 void consumerPutJob_jsonSchemavalidationError() throws Exception {
482 putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
484 String url = ConsumerConsts.API_ROOT + "/info-jobs/jobId?typeCheck=true";
485 // The element with name "property1" is mandatory in the schema
486 ConsumerJobInfo jobInfo = new ConsumerJobInfo("typeId", jsonObject("{ \"XXstring\" : \"value\" }"), "owner",
487 "targetUri", "jobStatusUrl");
488 String body = gson.toJson(jobInfo);
490 testErrorCode(restClient().put(url, body), HttpStatus.CONFLICT, "Json validation failure");
494 void a1eChangingEiTypeGetRejected() throws Exception {
495 putEiProducerWithOneType("producer1", "typeId1");
496 putEiProducerWithOneType("producer2", "typeId2");
497 putEiJob("typeId1", "jobId");
499 String url = A1eConsts.API_ROOT + "/eijobs/jobId";
500 String body = gson.toJson(eiJobInfo("typeId2", "jobId"));
501 testErrorCode(restClient().put(url, body), HttpStatus.CONFLICT,
502 "Not allowed to change type for existing EI job");
506 void consumerChangingInfoTypeGetRejected() throws Exception {
507 putEiProducerWithOneType("producer1", "typeId1");
508 putEiProducerWithOneType("producer2", "typeId2");
509 putEiJob("typeId1", "jobId");
511 String url = ConsumerConsts.API_ROOT + "/info-jobs/jobId";
512 String body = gson.toJson(consumerJobInfo("typeId2", "jobId"));
513 testErrorCode(restClient().put(url, body), HttpStatus.CONFLICT, "Not allowed to change type for existing job");
517 void producerPutEiType() throws JsonMappingException, JsonProcessingException, ServiceException {
518 assertThat(putEiType(TYPE_ID)).isEqualTo(HttpStatus.CREATED);
519 assertThat(putEiType(TYPE_ID)).isEqualTo(HttpStatus.OK);
523 void producerPutEiType_noSchema() {
524 String url = ProducerConsts.API_ROOT + "/eitypes/" + TYPE_ID;
526 testErrorCode(restClient().put(url, body), HttpStatus.BAD_REQUEST, "No schema provided");
530 void producerDeleteEiType() throws Exception {
532 String url = ProducerConsts.API_ROOT + "/eitypes/" + TYPE_ID;
533 restClient().delete(url).block();
534 assertThat(this.eiTypes.size()).isEqualTo(0);
536 testErrorCode(restClient().delete(url), HttpStatus.NOT_FOUND, "Information type not found");
540 void producerDeleteEiTypeExistingProducer() throws Exception {
541 putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
542 String url = ProducerConsts.API_ROOT + "/eitypes/" + TYPE_ID;
543 testErrorCode(restClient().delete(url), HttpStatus.NOT_ACCEPTABLE,
544 "The type has active producers: " + PRODUCER_ID);
545 assertThat(this.eiTypes.size()).isEqualTo(1);
549 void producerPutProducerWithOneType_rejecting()
550 throws JsonMappingException, JsonProcessingException, ServiceException {
551 putEiProducerWithOneTypeRejecting("simulateProducerError", TYPE_ID);
552 String url = A1eConsts.API_ROOT + "/eijobs/" + EI_JOB_ID;
553 String body = gson.toJson(eiJobInfo());
554 restClient().put(url, body).block();
556 ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
557 // There is one retry -> 2 calls
558 await().untilAsserted(() -> assertThat(simulatorResults.noOfRejectedCreate).isEqualTo(2));
559 assertThat(simulatorResults.noOfRejectedCreate).isEqualTo(2);
561 verifyJobStatus(EI_JOB_ID, "DISABLED");
565 void producerGetEiProducerTypes() throws Exception {
566 final String EI_TYPE_ID_2 = TYPE_ID + "_2";
567 putEiProducerWithOneType("producer1", TYPE_ID);
568 putEiJob(TYPE_ID, "jobId");
569 putEiProducerWithOneType("producer2", EI_TYPE_ID_2);
570 putEiJob(EI_TYPE_ID_2, "jobId2");
571 String url = ProducerConsts.API_ROOT + "/eitypes";
573 ResponseEntity<String> resp = restClient().getForEntity(url).block();
574 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
575 assertThat(resp.getBody()).contains(TYPE_ID);
576 assertThat(resp.getBody()).contains(EI_TYPE_ID_2);
580 void producerPutEiProducer() throws Exception {
581 this.putEiType(TYPE_ID);
582 String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId";
583 String body = gson.toJson(producerEiRegistratioInfo(TYPE_ID));
585 ResponseEntity<String> resp = restClient().putForEntity(url, body).block();
586 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.CREATED);
588 assertThat(this.eiTypes.size()).isEqualTo(1);
589 assertThat(this.eiProducers.getProducersForType(TYPE_ID).size()).isEqualTo(1);
590 assertThat(this.eiProducers.size()).isEqualTo(1);
591 assertThat(this.eiProducers.get("eiProducerId").getEiTypes().iterator().next().getId()).isEqualTo(TYPE_ID);
593 resp = restClient().putForEntity(url, body).block();
594 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
596 resp = restClient().getForEntity(url).block();
597 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
598 assertThat(resp.getBody()).isEqualTo(body);
602 void producerPutEiProducerExistingJob() throws Exception {
603 putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
604 putEiJob(TYPE_ID, "jobId");
605 String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId";
606 String body = gson.toJson(producerEiRegistratioInfo(TYPE_ID));
607 restClient().putForEntity(url, body).block();
609 ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
610 await().untilAsserted(() -> assertThat(simulatorResults.jobsStarted.size()).isEqualTo(2));
611 ProducerJobInfo request = simulatorResults.jobsStarted.get(0);
612 assertThat(request.id).isEqualTo("jobId");
616 void testPutEiProducer_noType() throws Exception {
617 String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId";
618 String body = gson.toJson(producerEiRegistratioInfo(TYPE_ID));
619 testErrorCode(restClient().put(url, body), HttpStatus.NOT_FOUND, "Information type not found");
623 void producerPutProducerAndEiJob() throws Exception {
624 this.putEiType(TYPE_ID);
625 String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId";
626 String body = gson.toJson(producerEiRegistratioInfo(TYPE_ID));
627 restClient().putForEntity(url, body).block();
628 assertThat(this.eiTypes.size()).isEqualTo(1);
629 this.eiTypes.getType(TYPE_ID);
631 url = A1eConsts.API_ROOT + "/eijobs/jobId";
632 body = gson.toJson(eiJobInfo());
633 restClient().putForEntity(url, body).block();
635 ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
636 await().untilAsserted(() -> assertThat(simulatorResults.jobsStarted.size()).isEqualTo(1));
637 ProducerJobInfo request = simulatorResults.jobsStarted.get(0);
638 assertThat(request.id).isEqualTo("jobId");
642 void producerGetEiJobsForProducer() throws JsonMappingException, JsonProcessingException, ServiceException {
643 putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
644 putEiJob(TYPE_ID, "jobId1");
645 putEiJob(TYPE_ID, "jobId2");
647 // PUT a consumerRestApiTestBase.java
648 String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId";
649 String body = gson.toJson(producerEiRegistratioInfo(TYPE_ID));
650 restClient().putForEntity(url, body).block();
652 url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId/eijobs";
653 ResponseEntity<String> resp = restClient().getForEntity(url).block();
654 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
656 ProducerJobInfo[] parsedResp = gson.fromJson(resp.getBody(), ProducerJobInfo[].class);
657 assertThat(parsedResp[0].typeId).isEqualTo(TYPE_ID);
658 assertThat(parsedResp[1].typeId).isEqualTo(TYPE_ID);
662 void producerDeleteEiProducer() throws Exception {
663 putEiProducerWithOneType("eiProducerId", TYPE_ID);
664 putEiProducerWithOneType("eiProducerId2", TYPE_ID);
666 assertThat(this.eiProducers.size()).isEqualTo(2);
667 EiType type = this.eiTypes.getType(TYPE_ID);
668 assertThat(this.eiProducers.getProducerIdsForType(type.getId())).contains("eiProducerId");
669 assertThat(this.eiProducers.getProducerIdsForType(type.getId())).contains("eiProducerId2");
670 putEiJob(TYPE_ID, "jobId");
671 assertThat(this.eiJobs.size()).isEqualTo(1);
673 deleteEiProducer("eiProducerId");
674 assertThat(this.eiProducers.size()).isEqualTo(1);
675 assertThat(this.eiProducers.getProducerIdsForType(TYPE_ID)).doesNotContain("eiProducerId");
676 verifyJobStatus("jobId", "ENABLED");
678 deleteEiProducer("eiProducerId2");
679 assertThat(this.eiProducers.size()).isZero();
680 assertThat(this.eiTypes.size()).isEqualTo(1);
681 verifyJobStatus("jobId", "DISABLED");
685 void a1eJobStatusNotifications() throws JsonMappingException, JsonProcessingException, ServiceException {
686 ConsumerSimulatorController.TestResults consumerCalls = this.consumerSimulator.getTestResults();
687 ProducerSimulatorController.TestResults producerCalls = this.producerSimulator.getTestResults();
689 putEiProducerWithOneType("eiProducerId", TYPE_ID);
690 putEiJob(TYPE_ID, "jobId");
691 putEiProducerWithOneType("eiProducerId2", TYPE_ID);
692 await().untilAsserted(() -> assertThat(producerCalls.jobsStarted.size()).isEqualTo(2));
694 deleteEiProducer("eiProducerId2");
695 assertThat(this.eiTypes.size()).isEqualTo(1); // The type remains, one producer left
696 deleteEiProducer("eiProducerId");
697 assertThat(this.eiTypes.size()).isEqualTo(1); // The type remains
698 assertThat(this.eiJobs.size()).isEqualTo(1); // The job remains
699 await().untilAsserted(() -> assertThat(consumerCalls.status.size()).isEqualTo(1));
700 assertThat(consumerCalls.status.get(0).state).isEqualTo(A1eEiJobStatus.EiJobStatusValues.DISABLED);
702 putEiProducerWithOneType("eiProducerId", TYPE_ID);
703 await().untilAsserted(() -> assertThat(consumerCalls.status.size()).isEqualTo(2));
704 assertThat(consumerCalls.status.get(1).state).isEqualTo(A1eEiJobStatus.EiJobStatusValues.ENABLED);
708 void a1eJobStatusNotifications2() throws JsonMappingException, JsonProcessingException, ServiceException {
709 // Test replacing a producer with new and removed types
712 putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
713 putEiJob(TYPE_ID, EI_JOB_ID);
715 // change the type for the producer, the job shall be disabled
716 putEiProducerWithOneType(PRODUCER_ID, "junk");
717 verifyJobStatus(EI_JOB_ID, "DISABLED");
718 ConsumerSimulatorController.TestResults consumerCalls = this.consumerSimulator.getTestResults();
719 await().untilAsserted(() -> assertThat(consumerCalls.status.size()).isEqualTo(1));
720 assertThat(consumerCalls.status.get(0).state).isEqualTo(A1eEiJobStatus.EiJobStatusValues.DISABLED);
722 putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
723 verifyJobStatus(EI_JOB_ID, "ENABLED");
724 await().untilAsserted(() -> assertThat(consumerCalls.status.size()).isEqualTo(2));
725 assertThat(consumerCalls.status.get(1).state).isEqualTo(A1eEiJobStatus.EiJobStatusValues.ENABLED);
729 void producerGetProducerEiType() throws JsonMappingException, JsonProcessingException, ServiceException {
730 putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
731 String url = ProducerConsts.API_ROOT + "/eitypes/" + TYPE_ID;
732 ResponseEntity<String> resp = restClient().getForEntity(url).block();
733 ProducerEiTypeInfo info = gson.fromJson(resp.getBody(), ProducerEiTypeInfo.class);
734 assertThat(info.jobDataSchema).isNotNull();
738 void producerGetProducerIdentifiers() throws JsonMappingException, JsonProcessingException, ServiceException {
739 putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
740 String url = ProducerConsts.API_ROOT + "/eiproducers";
741 ResponseEntity<String> resp = restClient().getForEntity(url).block();
742 assertThat(resp.getBody()).contains(PRODUCER_ID);
744 url = ProducerConsts.API_ROOT + "/eiproducers?ei_type_id=" + TYPE_ID;
745 resp = restClient().getForEntity(url).block();
746 assertThat(resp.getBody()).contains(PRODUCER_ID);
748 url = ProducerConsts.API_ROOT + "/eiproducers?ei_type_id=junk";
749 resp = restClient().getForEntity(url).block();
750 assertThat(resp.getBody()).isEqualTo("[]");
754 void producerSupervision() throws JsonMappingException, JsonProcessingException, ServiceException {
756 ConsumerSimulatorController.TestResults consumerResults = this.consumerSimulator.getTestResults();
757 putEiProducerWithOneTypeRejecting("simulateProducerError", TYPE_ID);
761 putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
762 putEiJob(TYPE_ID, EI_JOB_ID);
763 verifyJobStatus(EI_JOB_ID, "ENABLED");
764 deleteEiProducer(PRODUCER_ID);
765 // A Job disabled status notification shall now be received
766 await().untilAsserted(() -> assertThat(consumerResults.status.size()).isEqualTo(1));
767 assertThat(consumerResults.status.get(0).state).isEqualTo(A1eEiJobStatus.EiJobStatusValues.DISABLED);
768 verifyJobStatus(EI_JOB_ID, "DISABLED");
771 assertThat(this.eiProducers.size()).isEqualTo(1);
772 assertThat(this.eiTypes.size()).isEqualTo(1);
773 assertProducerOpState("simulateProducerError", ProducerStatusInfo.OperationalState.ENABLED);
775 this.producerSupervision.createTask().blockLast();
776 this.producerSupervision.createTask().blockLast();
778 // Now we have one producer that is disabled
779 assertThat(this.eiProducers.size()).isEqualTo(1);
780 assertProducerOpState("simulateProducerError", ProducerStatusInfo.OperationalState.DISABLED);
782 // After 3 failed checks, the producer shall be deregisterred
783 this.producerSupervision.createTask().blockLast();
784 assertThat(this.eiProducers.size()).isEqualTo(0); // The producer is removed
785 assertThat(this.eiTypes.size()).isEqualTo(1); // The type remains
787 // Now we have one disabled job, and no producer.
788 // PUT a producer, then a Job ENABLED status notification shall be received
789 putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
790 await().untilAsserted(() -> assertThat(consumerResults.status.size()).isEqualTo(2));
791 assertThat(consumerResults.status.get(1).state).isEqualTo(A1eEiJobStatus.EiJobStatusValues.ENABLED);
792 verifyJobStatus(EI_JOB_ID, "ENABLED");
796 void producerSupervision2() throws JsonMappingException, JsonProcessingException, ServiceException {
797 // Test that supervision enables not enabled jobs and sends a notification when
800 putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
801 putEiJob(TYPE_ID, EI_JOB_ID);
803 EiProducer producer = this.eiProducers.getProducer(PRODUCER_ID);
804 EiJob job = this.eiJobs.getJob(EI_JOB_ID);
805 // Pretend that the producer did reject the job and the a DISABLED notification
806 // is sent for the job
807 producer.setJobDisabled(job);
808 job.setLastReportedStatus(false);
809 verifyJobStatus(EI_JOB_ID, "DISABLED");
811 // Run the supervision and wait for the job to get started in the producer
812 this.producerSupervision.createTask().blockLast();
813 ConsumerSimulatorController.TestResults consumerResults = this.consumerSimulator.getTestResults();
814 await().untilAsserted(() -> assertThat(consumerResults.status.size()).isEqualTo(1));
815 assertThat(consumerResults.status.get(0).state).isEqualTo(A1eEiJobStatus.EiJobStatusValues.ENABLED);
816 verifyJobStatus(EI_JOB_ID, "ENABLED");
820 void testGetStatus() throws JsonMappingException, JsonProcessingException, ServiceException {
821 putEiProducerWithOneTypeRejecting("simulateProducerError", TYPE_ID);
822 putEiProducerWithOneTypeRejecting("simulateProducerError2", TYPE_ID);
824 String url = "/status";
825 ResponseEntity<String> resp = restClient().getForEntity(url).block();
826 assertThat(resp.getBody()).contains("hunky dory");
830 void testEiJobDatabase() throws Exception {
831 putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
832 putEiJob(TYPE_ID, "jobId1");
833 putEiJob(TYPE_ID, "jobId2");
835 assertThat(this.eiJobs.size()).isEqualTo(2);
838 EiJob savedJob = this.eiJobs.getJob("jobId1");
840 EiJobs jobs = new EiJobs(this.applicationConfig, this.producerCallbacks);
841 jobs.restoreJobsFromDatabase();
842 assertThat(jobs.size()).isEqualTo(2);
843 EiJob restoredJob = jobs.getJob("jobId1");
844 assertThat(restoredJob.getId()).isEqualTo("jobId1");
845 assertThat(restoredJob.getLastUpdated()).isEqualTo(savedJob.getLastUpdated());
847 jobs.remove("jobId1", this.eiProducers);
848 jobs.remove("jobId2", this.eiProducers);
851 // Restore the jobs, no jobs in database
852 EiJobs jobs = new EiJobs(this.applicationConfig, this.producerCallbacks);
853 jobs.restoreJobsFromDatabase();
854 assertThat(jobs.size()).isEqualTo(0);
856 logger.warn("Test removing a job when the db file is gone");
857 this.eiJobs.remove("jobId1", this.eiProducers);
858 assertThat(this.eiJobs.size()).isEqualTo(1);
860 ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
861 await().untilAsserted(() -> assertThat(simulatorResults.jobsStopped.size()).isEqualTo(3));
865 void testEiTypesDatabase() throws Exception {
866 putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
868 assertThat(this.eiTypes.size()).isEqualTo(1);
872 EiTypes types = new EiTypes(this.applicationConfig);
873 types.restoreTypesFromDatabase();
874 assertThat(types.size()).isEqualTo(1);
878 // Restore the jobs, no jobs in database
879 EiTypes types = new EiTypes(this.applicationConfig);
881 types.restoreTypesFromDatabase();
882 assertThat(types.size()).isEqualTo(0);
884 logger.warn("Test removing a job when the db file is gone");
885 this.eiTypes.remove(this.eiTypes.getType(TYPE_ID));
886 assertThat(this.eiJobs.size()).isEqualTo(0);
889 private void deleteEiProducer(String eiProducerId) {
890 String url = ProducerConsts.API_ROOT + "/eiproducers/" + eiProducerId;
891 restClient().deleteForEntity(url).block();
894 private void verifyJobStatus(String jobId, String expStatus) {
895 String url = A1eConsts.API_ROOT + "/eijobs/" + jobId + "/status";
896 String rsp = restClient().get(url).block();
897 assertThat(rsp).contains(expStatus);
900 private void assertProducerOpState(String producerId,
901 ProducerStatusInfo.OperationalState expectedOperationalState) {
902 String statusUrl = ProducerConsts.API_ROOT + "/eiproducers/" + producerId + "/status";
903 ResponseEntity<String> resp = restClient().getForEntity(statusUrl).block();
904 ProducerStatusInfo statusInfo = gson.fromJson(resp.getBody(), ProducerStatusInfo.class);
905 assertThat(statusInfo.opState).isEqualTo(expectedOperationalState);
908 ProducerEiTypeInfo producerEiTypeRegistrationInfo(String typeId)
909 throws JsonMappingException, JsonProcessingException {
910 return new ProducerEiTypeInfo(jsonSchemaObject());
913 ProducerRegistrationInfo producerEiRegistratioInfoRejecting(String typeId)
914 throws JsonMappingException, JsonProcessingException {
915 return new ProducerRegistrationInfo(Arrays.asList(typeId), //
916 baseUrl() + ProducerSimulatorController.JOB_ERROR_URL,
917 baseUrl() + ProducerSimulatorController.SUPERVISION_ERROR_URL);
920 ProducerRegistrationInfo producerEiRegistratioInfo(String typeId)
921 throws JsonMappingException, JsonProcessingException {
922 return new ProducerRegistrationInfo(Arrays.asList(typeId), //
923 baseUrl() + ProducerSimulatorController.JOB_URL, baseUrl() + ProducerSimulatorController.SUPERVISION_URL);
926 private ConsumerJobInfo consumerJobInfo() throws JsonMappingException, JsonProcessingException {
927 return consumerJobInfo(TYPE_ID, EI_JOB_ID);
930 ConsumerJobInfo consumerJobInfo(String typeId, String eiJobId)
931 throws JsonMappingException, JsonProcessingException {
932 return new ConsumerJobInfo(typeId, jsonObject(), "owner", "targetUri",
933 baseUrl() + ConsumerSimulatorController.getJobStatusUrl(eiJobId));
936 private A1eEiJobInfo eiJobInfo() throws JsonMappingException, JsonProcessingException {
937 return eiJobInfo(TYPE_ID, EI_JOB_ID);
940 A1eEiJobInfo eiJobInfo(String typeId, String eiJobId) throws JsonMappingException, JsonProcessingException {
941 return new A1eEiJobInfo(typeId, jsonObject(), "owner", "targetUri",
942 baseUrl() + ConsumerSimulatorController.getJobStatusUrl(eiJobId));
945 private Object jsonObject(String json) {
947 return JsonParser.parseString(json).getAsJsonObject();
948 } catch (Exception e) {
949 throw new NullPointerException(e.toString());
953 private Object jsonSchemaObject() {
954 // a json schema with one mandatory property named "string"
955 String schemaStr = "{" //
956 + "\"$schema\": \"http://json-schema.org/draft-04/schema#\"," //
957 + "\"type\": \"object\"," //
958 + "\"properties\": {" //
959 + EI_JOB_PROPERTY + " : {" //
960 + " \"type\": \"string\"" //
963 + "\"required\": [" //
967 return jsonObject(schemaStr);
970 private Object jsonObject() {
971 return jsonObject("{ " + EI_JOB_PROPERTY + " : \"value\" }");
974 private EiJob putEiJob(String eiTypeId, String jobId)
975 throws JsonMappingException, JsonProcessingException, ServiceException {
977 String url = A1eConsts.API_ROOT + "/eijobs/" + jobId;
978 String body = gson.toJson(eiJobInfo(eiTypeId, jobId));
979 restClient().putForEntity(url, body).block();
981 return this.eiJobs.getJob(jobId);
984 private HttpStatus putEiType(String eiTypeId)
985 throws JsonMappingException, JsonProcessingException, ServiceException {
986 String url = ProducerConsts.API_ROOT + "/eitypes/" + eiTypeId;
987 String body = gson.toJson(producerEiTypeRegistrationInfo(eiTypeId));
989 ResponseEntity<String> resp = restClient().putForEntity(url, body).block();
990 this.eiTypes.getType(eiTypeId);
991 return resp.getStatusCode();
995 private EiType putEiProducerWithOneTypeRejecting(String producerId, String eiTypeId)
996 throws JsonMappingException, JsonProcessingException, ServiceException {
997 this.putEiType(eiTypeId);
998 String url = ProducerConsts.API_ROOT + "/eiproducers/" + producerId;
999 String body = gson.toJson(producerEiRegistratioInfoRejecting(eiTypeId));
1000 restClient().putForEntity(url, body).block();
1001 return this.eiTypes.getType(eiTypeId);
1004 private EiType putEiProducerWithOneType(String producerId, String eiTypeId)
1005 throws JsonMappingException, JsonProcessingException, ServiceException {
1006 this.putEiType(eiTypeId);
1008 String url = ProducerConsts.API_ROOT + "/eiproducers/" + producerId;
1009 String body = gson.toJson(producerEiRegistratioInfo(eiTypeId));
1011 restClient().putForEntity(url, body).block();
1013 return this.eiTypes.getType(eiTypeId);
1016 private String baseUrl() {
1017 return "https://localhost:" + this.port;
1020 private AsyncRestClient restClient(boolean useTrustValidation) {
1021 WebClientConfig config = this.applicationConfig.getWebClientConfig();
1022 HttpProxyConfig httpProxyConfig = ImmutableHttpProxyConfig.builder() //
1023 .httpProxyHost("") //
1024 .httpProxyPort(0) //
1026 config = ImmutableWebClientConfig.builder() //
1027 .keyStoreType(config.keyStoreType()) //
1028 .keyStorePassword(config.keyStorePassword()) //
1029 .keyStore(config.keyStore()) //
1030 .keyPassword(config.keyPassword()) //
1031 .isTrustStoreUsed(useTrustValidation) //
1032 .trustStore(config.trustStore()) //
1033 .trustStorePassword(config.trustStorePassword()) //
1034 .httpProxyConfig(httpProxyConfig).build();
1036 AsyncRestClientFactory restClientFactory = new AsyncRestClientFactory(config);
1037 return restClientFactory.createRestClientNoHttpProxy(baseUrl());
1040 private AsyncRestClient restClient() {
1041 return restClient(false);
1044 private void testErrorCode(Mono<?> request, HttpStatus expStatus, String responseContains) {
1045 testErrorCode(request, expStatus, responseContains, true);
1048 private void testErrorCode(Mono<?> request, HttpStatus expStatus, String responseContains,
1049 boolean expectApplicationProblemJsonMediaType) {
1050 StepVerifier.create(request) //
1051 .expectSubscription() //
1052 .expectErrorMatches(
1053 t -> checkWebClientError(t, expStatus, responseContains, expectApplicationProblemJsonMediaType)) //
1057 private boolean checkWebClientError(Throwable throwable, HttpStatus expStatus, String responseContains,
1058 boolean expectApplicationProblemJsonMediaType) {
1059 assertTrue(throwable instanceof WebClientResponseException);
1060 WebClientResponseException responseException = (WebClientResponseException) throwable;
1061 assertThat(responseException.getStatusCode()).isEqualTo(expStatus);
1062 assertThat(responseException.getResponseBodyAsString()).contains(responseContains);
1063 if (expectApplicationProblemJsonMediaType) {
1064 assertThat(responseException.getHeaders().getContentType()).isEqualTo(MediaType.APPLICATION_PROBLEM_JSON);