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.ics;
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.ics.clients.AsyncRestClient;
45 import org.oransc.ics.clients.AsyncRestClientFactory;
46 import org.oransc.ics.configuration.ApplicationConfig;
47 import org.oransc.ics.configuration.ImmutableHttpProxyConfig;
48 import org.oransc.ics.configuration.ImmutableWebClientConfig;
49 import org.oransc.ics.configuration.WebClientConfig;
50 import org.oransc.ics.configuration.WebClientConfig.HttpProxyConfig;
51 import org.oransc.ics.controller.A1eCallbacksSimulatorController;
52 import org.oransc.ics.controller.ConsumerSimulatorController;
53 import org.oransc.ics.controller.ProducerSimulatorController;
54 import org.oransc.ics.controllers.a1e.A1eConsts;
55 import org.oransc.ics.controllers.a1e.A1eEiJobInfo;
56 import org.oransc.ics.controllers.a1e.A1eEiJobStatus;
57 import org.oransc.ics.controllers.a1e.A1eEiTypeInfo;
58 import org.oransc.ics.controllers.r1consumer.ConsumerConsts;
59 import org.oransc.ics.controllers.r1consumer.ConsumerInfoTypeInfo;
60 import org.oransc.ics.controllers.r1consumer.ConsumerJobInfo;
61 import org.oransc.ics.controllers.r1consumer.ConsumerJobStatus;
62 import org.oransc.ics.controllers.r1consumer.ConsumerTypeRegistrationInfo;
63 import org.oransc.ics.controllers.r1consumer.ConsumerTypeSubscriptionInfo;
64 import org.oransc.ics.controllers.r1producer.ProducerCallbacks;
65 import org.oransc.ics.controllers.r1producer.ProducerConsts;
66 import org.oransc.ics.controllers.r1producer.ProducerInfoTypeInfo;
67 import org.oransc.ics.controllers.r1producer.ProducerJobInfo;
68 import org.oransc.ics.controllers.r1producer.ProducerRegistrationInfo;
69 import org.oransc.ics.controllers.r1producer.ProducerStatusInfo;
70 import org.oransc.ics.exceptions.ServiceException;
71 import org.oransc.ics.repository.InfoJob;
72 import org.oransc.ics.repository.InfoJobs;
73 import org.oransc.ics.repository.InfoProducer;
74 import org.oransc.ics.repository.InfoProducers;
75 import org.oransc.ics.repository.InfoType;
76 import org.oransc.ics.repository.InfoTypeSubscriptions;
77 import org.oransc.ics.repository.InfoTypes;
78 import org.oransc.ics.tasks.ProducerSupervision;
79 import org.slf4j.Logger;
80 import org.slf4j.LoggerFactory;
81 import org.springframework.beans.factory.annotation.Autowired;
82 import org.springframework.boot.test.context.SpringBootTest;
83 import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
84 import org.springframework.boot.test.context.TestConfiguration;
85 import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
86 import org.springframework.boot.web.server.LocalServerPort;
87 import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
88 import org.springframework.context.ApplicationContext;
89 import org.springframework.context.annotation.Bean;
90 import org.springframework.http.HttpStatus;
91 import org.springframework.http.MediaType;
92 import org.springframework.http.ResponseEntity;
93 import org.springframework.test.context.TestPropertySource;
94 import org.springframework.test.context.junit.jupiter.SpringExtension;
95 import org.springframework.web.reactive.function.client.WebClientResponseException;
97 import reactor.core.publisher.Mono;
98 import reactor.test.StepVerifier;
100 @ExtendWith(SpringExtension.class)
101 @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
104 "server.ssl.key-store=./config/keystore.jks", //
105 "app.webclient.trust-store=./config/truststore.jks", //
106 "app.vardata-directory=./target"})
107 class ApplicationTest {
108 private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
110 private final String TYPE_ID = "typeId";
111 private final String PRODUCER_ID = "producerId";
112 private final String EI_JOB_PROPERTY = "\"property1\"";
113 private final String EI_JOB_ID = "jobId";
116 ApplicationContext context;
125 InfoProducers infoProducers;
128 ApplicationConfig applicationConfig;
131 ProducerSimulatorController producerSimulator;
134 ConsumerSimulatorController consumerSimulator;
137 A1eCallbacksSimulatorController a1eCallbacksSimulator;
140 ProducerSupervision producerSupervision;
143 ProducerCallbacks producerCallbacks;
146 InfoTypeSubscriptions infoTypeSubscriptions;
148 private static Gson gson = new GsonBuilder().create();
151 * Overrides the BeanFactory.
154 static class TestBeanFactory {
156 public ServletWebServerFactory servletContainer() {
157 return new TomcatServletWebServerFactory();
166 this.infoJobs.clear();
167 this.infoTypes.clear();
168 this.infoProducers.clear();
169 this.infoTypeSubscriptions.clear();
170 this.producerSimulator.getTestResults().reset();
171 this.consumerSimulator.getTestResults().reset();
172 this.a1eCallbacksSimulator.getTestResults().reset();
177 assertThat(this.producerSimulator.getTestResults().errorFound).isFalse();
181 void generateApiDoc() throws FileNotFoundException {
182 String url = "/v3/api-docs";
183 ResponseEntity<String> resp = restClient().getForEntity(url).block();
184 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
186 JSONObject jsonObj = new JSONObject(resp.getBody());
187 assertThat(jsonObj.remove("servers")).isNotNull();
189 String indented = jsonObj.toString(4);
190 try (PrintStream out = new PrintStream(new FileOutputStream("api/ics-api.json"))) {
196 void a1eGetEiTypes() throws Exception {
197 putInfoProducerWithOneType(PRODUCER_ID, "test");
198 String url = A1eConsts.API_ROOT + "/eitypes";
199 String rsp = restClient().get(url).block();
200 assertThat(rsp).isEqualTo("[\"test\"]");
204 void consumerGetInfoTypes() throws Exception {
205 putInfoProducerWithOneType(PRODUCER_ID, "test");
206 String url = ConsumerConsts.API_ROOT + "/info-types";
207 String rsp = restClient().get(url).block();
208 assertThat(rsp).isEqualTo("[\"test\"]");
212 void a1eGetEiTypesEmpty() throws Exception {
213 String url = A1eConsts.API_ROOT + "/eitypes";
214 String rsp = restClient().get(url).block();
215 assertThat(rsp).isEqualTo("[]");
219 void consumerGetEiTypesEmpty() throws Exception {
220 String url = ConsumerConsts.API_ROOT + "/info-types";
221 String rsp = restClient().get(url).block();
222 assertThat(rsp).isEqualTo("[]");
226 void a1eGetEiType() throws Exception {
227 putInfoProducerWithOneType(PRODUCER_ID, "test");
228 String url = A1eConsts.API_ROOT + "/eitypes/test";
229 String rsp = restClient().get(url).block();
230 A1eEiTypeInfo info = gson.fromJson(rsp, A1eEiTypeInfo.class);
231 assertThat(info).isNotNull();
235 void consumerGetEiType() throws Exception {
236 putInfoProducerWithOneType(PRODUCER_ID, "test");
237 String url = ConsumerConsts.API_ROOT + "/info-types/test";
238 String rsp = restClient().get(url).block();
239 ConsumerInfoTypeInfo info = gson.fromJson(rsp, ConsumerInfoTypeInfo.class);
240 assertThat(info).isNotNull();
241 assertThat(info.jobDataSchema).isNotNull();
242 assertThat(info.state).isEqualTo(ConsumerInfoTypeInfo.ConsumerTypeStatusValues.ENABLED);
243 assertThat(info.noOfProducers).isEqualTo(1);
247 void a1eGetEiTypeNotFound() throws Exception {
248 String url = A1eConsts.API_ROOT + "/eitypes/junk";
249 testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND, "Information type not found: junk");
253 void consumerGetEiTypeNotFound() throws Exception {
254 String url = ConsumerConsts.API_ROOT + "/info-types/junk";
255 testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND, "Information type not found: junk");
259 void a1eGetEiJobsIds() throws Exception {
260 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
261 putInfoJob(TYPE_ID, "jobId");
262 final String JOB_ID_JSON = "[\"jobId\"]";
263 String url = A1eConsts.API_ROOT + "/eijobs?infoTypeId=typeId";
264 String rsp = restClient().get(url).block();
265 assertThat(rsp).isEqualTo(JOB_ID_JSON);
267 url = A1eConsts.API_ROOT + "/eijobs?owner=owner";
268 rsp = restClient().get(url).block();
269 assertThat(rsp).isEqualTo(JOB_ID_JSON);
271 url = A1eConsts.API_ROOT + "/eijobs?owner=JUNK";
272 rsp = restClient().get(url).block();
273 assertThat(rsp).isEqualTo("[]");
275 url = A1eConsts.API_ROOT + "/eijobs";
276 rsp = restClient().get(url).block();
277 assertThat(rsp).isEqualTo(JOB_ID_JSON);
279 url = A1eConsts.API_ROOT + "/eijobs?eiTypeId=typeId&&owner=owner";
280 rsp = restClient().get(url).block();
281 assertThat(rsp).isEqualTo(JOB_ID_JSON);
283 url = A1eConsts.API_ROOT + "/eijobs?eiTypeId=JUNK";
284 rsp = restClient().get(url).block();
285 assertThat(rsp).isEqualTo("[]");
289 void consumerGetInformationJobsIds() throws Exception {
290 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
291 putInfoJob(TYPE_ID, "jobId");
292 final String JOB_ID_JSON = "[\"jobId\"]";
293 String url = ConsumerConsts.API_ROOT + "/info-jobs?infoTypeId=typeId";
294 String rsp = restClient().get(url).block();
295 assertThat(rsp).isEqualTo(JOB_ID_JSON);
297 url = ConsumerConsts.API_ROOT + "/info-jobs?owner=owner";
298 rsp = restClient().get(url).block();
299 assertThat(rsp).isEqualTo(JOB_ID_JSON);
301 url = ConsumerConsts.API_ROOT + "/info-jobs?owner=JUNK";
302 rsp = restClient().get(url).block();
303 assertThat(rsp).isEqualTo("[]");
305 url = ConsumerConsts.API_ROOT + "/info-jobs";
306 rsp = restClient().get(url).block();
307 assertThat(rsp).isEqualTo(JOB_ID_JSON);
309 url = ConsumerConsts.API_ROOT + "/info-jobs?infoTypeId=typeId&&owner=owner";
310 rsp = restClient().get(url).block();
311 assertThat(rsp).isEqualTo(JOB_ID_JSON);
313 url = ConsumerConsts.API_ROOT + "/info-jobs?infoTypeId=JUNK";
314 rsp = restClient().get(url).block();
315 assertThat(rsp).isEqualTo("[]");
319 void a1eGetEiJob() throws Exception {
320 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
321 putInfoJob(TYPE_ID, "jobId");
322 String url = A1eConsts.API_ROOT + "/eijobs/jobId";
323 String rsp = restClient().get(url).block();
324 A1eEiJobInfo info = gson.fromJson(rsp, A1eEiJobInfo.class);
325 assertThat(info.owner).isEqualTo("owner");
326 assertThat(info.eiTypeId).isEqualTo(TYPE_ID);
330 void consumerGetEiJob() throws Exception {
331 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
332 putInfoJob(TYPE_ID, "jobId");
333 String url = ConsumerConsts.API_ROOT + "/info-jobs/jobId";
334 String rsp = restClient().get(url).block();
335 ConsumerJobInfo info = gson.fromJson(rsp, ConsumerJobInfo.class);
336 assertThat(info.owner).isEqualTo("owner");
337 assertThat(info.infoTypeId).isEqualTo(TYPE_ID);
341 void a1eGetEiJobNotFound() throws Exception {
342 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
343 String url = A1eConsts.API_ROOT + "/eijobs/junk";
344 testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND, "Could not find Information job: junk");
348 void consumerGetInfoJobNotFound() throws Exception {
349 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
350 String url = ConsumerConsts.API_ROOT + "/info-jobs/junk";
351 testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND, "Could not find Information job: junk");
355 void a1eGetEiJobStatus() throws Exception {
356 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
357 putInfoJob(TYPE_ID, "jobId");
359 verifyJobStatus("jobId", "ENABLED");
363 void consumerGetInfoJobStatus() throws Exception {
364 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
365 putInfoJob(TYPE_ID, "jobId");
367 String url = ConsumerConsts.API_ROOT + "/info-jobs/jobId/status";
368 String rsp = restClient().get(url).block();
370 .contains("ENABLED") //
371 .contains(PRODUCER_ID);
373 ConsumerJobStatus status = gson.fromJson(rsp, ConsumerJobStatus.class);
374 assertThat(status.producers).contains(PRODUCER_ID);
378 void a1eDeleteEiJob() throws Exception {
379 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
380 putInfoJob(TYPE_ID, "jobId");
381 assertThat(this.infoJobs.size()).isEqualTo(1);
382 String url = A1eConsts.API_ROOT + "/eijobs/jobId";
383 restClient().delete(url).block();
384 assertThat(this.infoJobs.size()).isZero();
386 ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
387 await().untilAsserted(() -> assertThat(simulatorResults.jobsStopped).hasSize(1));
388 assertThat(simulatorResults.jobsStopped.get(0)).isEqualTo("jobId");
392 void consumerDeleteEiJob() throws Exception {
393 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
394 putInfoJob(TYPE_ID, "jobId");
395 assertThat(this.infoJobs.size()).isEqualTo(1);
396 String url = ConsumerConsts.API_ROOT + "/info-jobs/jobId";
397 restClient().delete(url).block();
398 assertThat(this.infoJobs.size()).isZero();
400 ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
401 await().untilAsserted(() -> assertThat(simulatorResults.jobsStopped).hasSize(1));
402 assertThat(simulatorResults.jobsStopped.get(0)).isEqualTo("jobId");
404 testErrorCode(restClient().delete(url), HttpStatus.NOT_FOUND, "Could not find Information job: jobId");
408 void a1eDeleteEiJobNotFound() throws Exception {
409 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
410 String url = A1eConsts.API_ROOT + "/eijobs/junk";
411 testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND, "Could not find Information job: junk");
415 void consumerDeleteEiJobNotFound() throws Exception {
416 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
417 String url = ConsumerConsts.API_ROOT + "/info-jobs/junk";
418 testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND, "Could not find Information job: junk");
422 void a1ePutEiJob() throws Exception {
423 // Test that one producer accepting a job is enough
424 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
425 putInfoProducerWithOneTypeRejecting("simulateProducerError", TYPE_ID);
427 String url = A1eConsts.API_ROOT + "/eijobs/jobId";
428 String body = gson.toJson(infoJobInfo());
429 ResponseEntity<String> resp = restClient().putForEntity(url, body).block();
430 assertThat(this.infoJobs.size()).isEqualTo(1);
431 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.CREATED);
433 ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
434 await().untilAsserted(() -> assertThat(simulatorResults.jobsStarted).hasSize(1));
435 ProducerJobInfo request = simulatorResults.jobsStarted.get(0);
436 assertThat(request.id).isEqualTo("jobId");
438 // One retry --> two calls
439 await().untilAsserted(() -> assertThat(simulatorResults.noOfRejectedCreate).isEqualTo(2));
440 assertThat(simulatorResults.noOfRejectedCreate).isEqualTo(2);
442 resp = restClient().putForEntity(url, body).block();
443 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
444 InfoJob job = this.infoJobs.getJob("jobId");
445 assertThat(job.getOwner()).isEqualTo("owner");
447 verifyJobStatus(EI_JOB_ID, "ENABLED");
451 void consumerPutInformationJob() throws Exception {
452 // Test that one producer accepting a job is enough
453 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
455 String url = ConsumerConsts.API_ROOT + "/info-jobs/jobId";
456 String body = gson.toJson(consumerJobInfo());
457 ResponseEntity<String> resp = restClient().putForEntity(url, body).block();
458 assertThat(this.infoJobs.size()).isEqualTo(1);
459 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.CREATED);
461 ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
462 await().untilAsserted(() -> assertThat(simulatorResults.jobsStarted).hasSize(1));
463 ProducerJobInfo request = simulatorResults.jobsStarted.get(0);
464 assertThat(request.id).isEqualTo("jobId");
466 resp = restClient().putForEntity(url, body).block();
467 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
468 InfoJob job = this.infoJobs.getJob("jobId");
469 assertThat(job.getOwner()).isEqualTo("owner");
471 verifyJobStatus(EI_JOB_ID, "ENABLED");
475 void consumerPutInformationJob_noType() throws JsonMappingException, JsonProcessingException, ServiceException {
476 String url = ConsumerConsts.API_ROOT + "/info-jobs/jobId?typeCheck=false";
477 String body = gson.toJson(consumerJobInfo());
478 ResponseEntity<String> resp = restClient().putForEntity(url, body).block();
479 assertThat(this.infoJobs.size()).isEqualTo(1);
480 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.CREATED);
481 verifyJobStatus(EI_JOB_ID, "DISABLED");
483 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
485 verifyJobStatus(EI_JOB_ID, "ENABLED");
489 void a1ePutEiJob_jsonSchemavalidationError() throws Exception {
490 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
492 String url = A1eConsts.API_ROOT + "/eijobs/jobId";
493 // The element with name "property1" is mandatory in the schema
494 A1eEiJobInfo jobInfo = new A1eEiJobInfo("typeId", jsonObject("{ \"XXstring\" : \"value\" }"), "owner",
495 "targetUri", "jobStatusUrl");
496 String body = gson.toJson(jobInfo);
498 testErrorCode(restClient().put(url, body), HttpStatus.BAD_REQUEST, "Json validation failure");
500 testErrorCode(restClient().put(url, "{jojo}"), HttpStatus.BAD_REQUEST, "", false);
505 void consumerPutJob_jsonSchemavalidationError() throws Exception {
506 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
508 String url = ConsumerConsts.API_ROOT + "/info-jobs/jobId?typeCheck=true";
509 // The element with name "property1" is mandatory in the schema
510 ConsumerJobInfo jobInfo =
511 new ConsumerJobInfo("typeId", jsonObject("{ \"XXstring\" : \"value\" }"), "owner", "targetUri", null);
512 String body = gson.toJson(jobInfo);
514 testErrorCode(restClient().put(url, body), HttpStatus.BAD_REQUEST, "Json validation failure");
518 void consumerPutJob_uriError() throws Exception {
519 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
521 String url = ConsumerConsts.API_ROOT + "/info-jobs/jobId?typeCheck=true";
523 ConsumerJobInfo jobInfo = new ConsumerJobInfo(TYPE_ID, jsonObject(), "owner", "junk", null);
524 String body = gson.toJson(jobInfo);
526 testErrorCode(restClient().put(url, body), HttpStatus.BAD_REQUEST, "URI: junk is not absolute");
530 void a1eChangingEiTypeGetRejected() throws Exception {
531 putInfoProducerWithOneType("producer1", "typeId1");
532 putInfoProducerWithOneType("producer2", "typeId2");
533 putInfoJob("typeId1", "jobId");
535 String url = A1eConsts.API_ROOT + "/eijobs/jobId";
536 String body = gson.toJson(infoJobInfo("typeId2", "jobId"));
537 testErrorCode(restClient().put(url, body), HttpStatus.CONFLICT,
538 "Not allowed to change type for existing EI job");
542 void consumerChangingInfoTypeGetRejected() throws Exception {
543 putInfoProducerWithOneType("producer1", "typeId1");
544 putInfoProducerWithOneType("producer2", "typeId2");
545 putInfoJob("typeId1", "jobId");
547 String url = ConsumerConsts.API_ROOT + "/info-jobs/jobId";
548 String body = gson.toJson(consumerJobInfo("typeId2", "jobId"));
549 testErrorCode(restClient().put(url, body), HttpStatus.CONFLICT, "Not allowed to change type for existing job");
553 void producerPutEiType() throws JsonMappingException, JsonProcessingException, ServiceException {
554 assertThat(putInfoType(TYPE_ID)).isEqualTo(HttpStatus.CREATED);
555 assertThat(putInfoType(TYPE_ID)).isEqualTo(HttpStatus.OK);
559 void producerPutEiType_noSchema() {
560 String url = ProducerConsts.API_ROOT + "/info-types/" + TYPE_ID;
562 testErrorCode(restClient().put(url, body), HttpStatus.BAD_REQUEST, "No schema provided");
564 testErrorCode(restClient().post(url, body), HttpStatus.METHOD_NOT_ALLOWED, "", false);
568 void producerDeleteEiType() throws Exception {
569 putInfoType(TYPE_ID);
570 this.putInfoJob(TYPE_ID, "job1");
571 this.putInfoJob(TYPE_ID, "job2");
572 deleteInfoType(TYPE_ID);
574 assertThat(this.infoTypes.size()).isZero();
575 assertThat(this.infoJobs.size()).isZero(); // Test that also the job is deleted
577 testErrorCode(restClient().delete(deleteInfoTypeUrl(TYPE_ID)), HttpStatus.NOT_FOUND,
578 "Information type not found");
582 void producerDeleteEiTypeExistingProducer() throws Exception {
583 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
584 String url = ProducerConsts.API_ROOT + "/info-types/" + TYPE_ID;
585 testErrorCode(restClient().delete(url), HttpStatus.CONFLICT, "The type has active producers: " + PRODUCER_ID);
586 assertThat(this.infoTypes.size()).isEqualTo(1);
590 void producerPutProducerWithOneType_rejecting()
591 throws JsonMappingException, JsonProcessingException, ServiceException {
592 putInfoProducerWithOneTypeRejecting("simulateProducerError", TYPE_ID);
593 String url = A1eConsts.API_ROOT + "/eijobs/" + EI_JOB_ID;
594 String body = gson.toJson(infoJobInfo());
595 restClient().put(url, body).block();
597 ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
598 // There is one retry -> 2 calls
599 await().untilAsserted(() -> assertThat(simulatorResults.noOfRejectedCreate).isEqualTo(2));
600 assertThat(simulatorResults.noOfRejectedCreate).isEqualTo(2);
602 verifyJobStatus(EI_JOB_ID, "DISABLED");
606 void producerGetInfoProducerTypes() throws Exception {
607 final String EI_TYPE_ID_2 = TYPE_ID + "_2";
608 putInfoProducerWithOneType("producer1", TYPE_ID);
609 putInfoJob(TYPE_ID, "jobId");
610 putInfoProducerWithOneType("producer2", EI_TYPE_ID_2);
611 putInfoJob(EI_TYPE_ID_2, "jobId2");
612 String url = ProducerConsts.API_ROOT + "/info-types";
614 ResponseEntity<String> resp = restClient().getForEntity(url).block();
615 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
616 assertThat(resp.getBody()).contains(TYPE_ID);
617 assertThat(resp.getBody()).contains(EI_TYPE_ID_2);
621 void producerPutInfoProducer() throws Exception {
622 this.putInfoType(TYPE_ID);
623 String url = ProducerConsts.API_ROOT + "/info-producers/infoProducerId";
624 String body = gson.toJson(producerInfoRegistratioInfo(TYPE_ID));
626 ResponseEntity<String> resp = restClient().putForEntity(url, body).block();
627 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.CREATED);
629 assertThat(this.infoTypes.size()).isEqualTo(1);
630 assertThat(this.infoProducers.getProducersForType(TYPE_ID)).hasSize(1);
631 assertThat(this.infoProducers.size()).isEqualTo(1);
632 assertThat(this.infoProducers.get("infoProducerId").getInfoTypes().iterator().next().getId())
635 resp = restClient().putForEntity(url, body).block();
636 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
639 resp = restClient().getForEntity(url).block();
640 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
641 assertThat(resp.getBody()).isEqualTo(body);
643 testErrorCode(restClient().get(url + "junk"), HttpStatus.NOT_FOUND, "Could not find Information Producer");
647 void producerPutInfoProducerExistingJob() throws Exception {
648 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
649 putInfoJob(TYPE_ID, "jobId");
650 String url = ProducerConsts.API_ROOT + "/info-producers/infoProducerId";
651 String body = gson.toJson(producerInfoRegistratioInfo(TYPE_ID));
652 restClient().putForEntity(url, body).block();
654 ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
655 await().untilAsserted(() -> assertThat(simulatorResults.jobsStarted).hasSize(2));
656 ProducerJobInfo request = simulatorResults.jobsStarted.get(0);
657 assertThat(request.id).isEqualTo("jobId");
661 void testPutInfoProducer_noType() throws Exception {
662 String url = ProducerConsts.API_ROOT + "/info-producers/infoProducerId";
663 String body = gson.toJson(producerInfoRegistratioInfo(TYPE_ID));
664 testErrorCode(restClient().put(url, body), HttpStatus.NOT_FOUND, "Information type not found");
668 void producerPutProducerAndInfoJob() throws Exception {
669 this.putInfoType(TYPE_ID);
670 String url = ProducerConsts.API_ROOT + "/info-producers/infoProducerId";
671 String body = gson.toJson(producerInfoRegistratioInfo(TYPE_ID));
672 restClient().putForEntity(url, body).block();
673 assertThat(this.infoTypes.size()).isEqualTo(1);
674 this.infoTypes.getType(TYPE_ID);
676 url = A1eConsts.API_ROOT + "/eijobs/jobId";
677 body = gson.toJson(infoJobInfo());
678 restClient().putForEntity(url, body).block();
680 ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
681 await().untilAsserted(() -> assertThat(simulatorResults.jobsStarted).hasSize(1));
682 ProducerJobInfo request = simulatorResults.jobsStarted.get(0);
683 assertThat(request.id).isEqualTo("jobId");
687 void producerGetInfoJobsForProducer() throws JsonMappingException, JsonProcessingException, ServiceException {
688 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
689 putInfoJob(TYPE_ID, "jobId1");
690 putInfoJob(TYPE_ID, "jobId2");
692 // PUT a consumerRestApiTestBase.java
693 String url = ProducerConsts.API_ROOT + "/info-producers/infoProducerId";
694 String body = gson.toJson(producerInfoRegistratioInfo(TYPE_ID));
695 restClient().putForEntity(url, body).block();
697 url = ProducerConsts.API_ROOT + "/info-producers/infoProducerId/info-jobs";
698 ResponseEntity<String> resp = restClient().getForEntity(url).block();
699 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
701 ProducerJobInfo[] parsedResp = gson.fromJson(resp.getBody(), ProducerJobInfo[].class);
702 assertThat(parsedResp[0].typeId).isEqualTo(TYPE_ID);
703 assertThat(parsedResp[1].typeId).isEqualTo(TYPE_ID);
707 void producerDeleteInfoProducer() throws Exception {
708 putInfoProducerWithOneType("infoProducerId", TYPE_ID);
709 putInfoProducerWithOneType("infoProducerId2", TYPE_ID);
711 assertThat(this.infoProducers.size()).isEqualTo(2);
712 InfoType type = this.infoTypes.getType(TYPE_ID);
713 assertThat(this.infoProducers.getProducerIdsForType(type.getId())).contains("infoProducerId");
714 assertThat(this.infoProducers.getProducerIdsForType(type.getId())).contains("infoProducerId2");
715 putInfoJob(TYPE_ID, "jobId");
716 assertThat(this.infoJobs.size()).isEqualTo(1);
718 deleteInfoProducer("infoProducerId");
719 assertThat(this.infoProducers.size()).isEqualTo(1);
720 assertThat(this.infoProducers.getProducerIdsForType(TYPE_ID)).doesNotContain("infoProducerId");
721 verifyJobStatus("jobId", "ENABLED");
723 deleteInfoProducer("infoProducerId2");
724 assertThat(this.infoProducers.size()).isZero();
725 assertThat(this.infoTypes.size()).isEqualTo(1);
726 verifyJobStatus("jobId", "DISABLED");
728 String url = ProducerConsts.API_ROOT + "/info-producers/" + "junk";
729 testErrorCode(restClient().delete(url), HttpStatus.NOT_FOUND, "Could not find Information Producer");
733 void a1eJobStatusNotifications() throws JsonMappingException, JsonProcessingException, ServiceException {
734 A1eCallbacksSimulatorController.TestResults consumerCalls = this.a1eCallbacksSimulator.getTestResults();
735 ProducerSimulatorController.TestResults producerCalls = this.producerSimulator.getTestResults();
737 putInfoProducerWithOneType("infoProducerId", TYPE_ID);
738 putInfoJob(TYPE_ID, "jobId");
739 putInfoProducerWithOneType("infoProducerId2", TYPE_ID);
740 await().untilAsserted(() -> assertThat(producerCalls.jobsStarted).hasSize(2));
742 deleteInfoProducer("infoProducerId2");
743 assertThat(this.infoTypes.size()).isEqualTo(1); // The type remains, one producer left
744 deleteInfoProducer("infoProducerId");
745 assertThat(this.infoTypes.size()).isEqualTo(1); // The type remains
746 assertThat(this.infoJobs.size()).isEqualTo(1); // The job remains
747 await().untilAsserted(() -> assertThat(consumerCalls.eiJobStatusCallbacks).hasSize(1));
748 assertThat(consumerCalls.eiJobStatusCallbacks.get(0).state)
749 .isEqualTo(A1eEiJobStatus.EiJobStatusValues.DISABLED);
751 putInfoProducerWithOneType("infoProducerId", TYPE_ID);
752 await().untilAsserted(() -> assertThat(consumerCalls.eiJobStatusCallbacks).hasSize(2));
753 assertThat(consumerCalls.eiJobStatusCallbacks.get(1).state).isEqualTo(A1eEiJobStatus.EiJobStatusValues.ENABLED);
757 void a1eJobStatusNotifications2() throws JsonMappingException, JsonProcessingException, ServiceException {
758 // Test replacing a producer with new and removed types
761 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
762 putInfoJob(TYPE_ID, EI_JOB_ID);
764 // change the type for the producer, the job shall be disabled
765 putInfoProducerWithOneType(PRODUCER_ID, "junk");
766 verifyJobStatus(EI_JOB_ID, "DISABLED");
767 A1eCallbacksSimulatorController.TestResults consumerCalls = this.a1eCallbacksSimulator.getTestResults();
768 await().untilAsserted(() -> assertThat(consumerCalls.eiJobStatusCallbacks).hasSize(1));
769 assertThat(consumerCalls.eiJobStatusCallbacks.get(0).state)
770 .isEqualTo(A1eEiJobStatus.EiJobStatusValues.DISABLED);
772 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
773 verifyJobStatus(EI_JOB_ID, "ENABLED");
774 await().untilAsserted(() -> assertThat(consumerCalls.eiJobStatusCallbacks).hasSize(2));
775 assertThat(consumerCalls.eiJobStatusCallbacks.get(1).state).isEqualTo(A1eEiJobStatus.EiJobStatusValues.ENABLED);
779 void producerGetProducerInfoType() throws JsonMappingException, JsonProcessingException, ServiceException {
780 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
781 String url = ProducerConsts.API_ROOT + "/info-types/" + TYPE_ID;
782 ResponseEntity<String> resp = restClient().getForEntity(url).block();
783 ProducerInfoTypeInfo info = gson.fromJson(resp.getBody(), ProducerInfoTypeInfo.class);
784 assertThat(info.jobDataSchema).isNotNull();
785 assertThat(info.typeSpecificInformation).isNotNull();
787 testErrorCode(restClient().get(url + "junk"), HttpStatus.NOT_FOUND, "Information type not found");
791 void producerGetProducerIdentifiers() throws JsonMappingException, JsonProcessingException, ServiceException {
792 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
793 String url = ProducerConsts.API_ROOT + "/info-producers";
794 ResponseEntity<String> resp = restClient().getForEntity(url).block();
795 assertThat(resp.getBody()).contains(PRODUCER_ID);
797 url = ProducerConsts.API_ROOT + "/info-producers?infoTypeId=" + TYPE_ID;
798 resp = restClient().getForEntity(url).block();
799 assertThat(resp.getBody()).contains(PRODUCER_ID);
801 url = ProducerConsts.API_ROOT + "/info-producers?infoTypeId=junk";
802 resp = restClient().getForEntity(url).block();
803 assertThat(resp.getBody()).isEqualTo("[]");
807 void producerSupervision() throws JsonMappingException, JsonProcessingException, ServiceException {
809 A1eCallbacksSimulatorController.TestResults consumerResults = this.a1eCallbacksSimulator.getTestResults();
810 putInfoProducerWithOneTypeRejecting("simulateProducerError", TYPE_ID);
814 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
815 putInfoJob(TYPE_ID, EI_JOB_ID);
816 verifyJobStatus(EI_JOB_ID, "ENABLED");
817 deleteInfoProducer(PRODUCER_ID);
818 // A Job disabled status notification shall now be received
819 await().untilAsserted(() -> assertThat(consumerResults.eiJobStatusCallbacks).hasSize(1));
820 assertThat(consumerResults.eiJobStatusCallbacks.get(0).state)
821 .isEqualTo(A1eEiJobStatus.EiJobStatusValues.DISABLED);
822 verifyJobStatus(EI_JOB_ID, "DISABLED");
825 assertThat(this.infoProducers.size()).isEqualTo(1);
826 assertThat(this.infoTypes.size()).isEqualTo(1);
827 assertProducerOpState("simulateProducerError", ProducerStatusInfo.OperationalState.ENABLED);
829 this.producerSupervision.createTask().blockLast();
830 this.producerSupervision.createTask().blockLast();
832 // Now we have one producer that is disabled
833 assertThat(this.infoProducers.size()).isEqualTo(1);
834 assertProducerOpState("simulateProducerError", ProducerStatusInfo.OperationalState.DISABLED);
836 // After 3 failed checks, the producer shall be deregistered
837 this.producerSupervision.createTask().blockLast();
838 assertThat(this.infoProducers.size()).isZero(); // The producer is removed
839 assertThat(this.infoTypes.size()).isEqualTo(1); // The type remains
841 // Now we have one disabled job, and no producer.
842 // PUT a producer, then a Job ENABLED status notification shall be received
843 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
844 await().untilAsserted(() -> assertThat(consumerResults.eiJobStatusCallbacks).hasSize(2));
845 assertThat(consumerResults.eiJobStatusCallbacks.get(1).state)
846 .isEqualTo(A1eEiJobStatus.EiJobStatusValues.ENABLED);
847 verifyJobStatus(EI_JOB_ID, "ENABLED");
851 void producerSupervision2() throws JsonMappingException, JsonProcessingException, ServiceException {
852 // Test that supervision enables not enabled jobs and sends a notification when
855 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
856 putInfoJob(TYPE_ID, EI_JOB_ID);
858 InfoProducer producer = this.infoProducers.getProducer(PRODUCER_ID);
859 InfoJob job = this.infoJobs.getJob(EI_JOB_ID);
860 // Pretend that the producer did reject the job and the a DISABLED notification
861 // is sent for the job
862 producer.setJobDisabled(job);
863 job.setLastReportedStatus(false);
864 verifyJobStatus(EI_JOB_ID, "DISABLED");
866 // Run the supervision and wait for the job to get started in the producer
867 this.producerSupervision.createTask().blockLast();
868 A1eCallbacksSimulatorController.TestResults consumerResults = this.a1eCallbacksSimulator.getTestResults();
869 await().untilAsserted(() -> assertThat(consumerResults.eiJobStatusCallbacks).hasSize(1));
870 assertThat(consumerResults.eiJobStatusCallbacks.get(0).state)
871 .isEqualTo(A1eEiJobStatus.EiJobStatusValues.ENABLED);
872 verifyJobStatus(EI_JOB_ID, "ENABLED");
876 void testGetStatus() throws JsonMappingException, JsonProcessingException, ServiceException {
877 putInfoProducerWithOneTypeRejecting("simulateProducerError", TYPE_ID);
878 putInfoProducerWithOneTypeRejecting("simulateProducerError2", TYPE_ID);
880 String url = "/status";
881 ResponseEntity<String> resp = restClient().getForEntity(url).block();
882 assertThat(resp.getBody()).contains("hunky dory");
886 void testEiJobDatabase() throws Exception {
887 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
888 putInfoJob(TYPE_ID, "jobId1");
889 putInfoJob(TYPE_ID, "jobId2");
891 assertThat(this.infoJobs.size()).isEqualTo(2);
894 InfoJob savedJob = this.infoJobs.getJob("jobId1");
896 InfoJobs jobs = new InfoJobs(this.applicationConfig, this.producerCallbacks);
897 jobs.restoreJobsFromDatabase();
898 assertThat(jobs.size()).isEqualTo(2);
899 InfoJob restoredJob = jobs.getJob("jobId1");
900 assertThat(restoredJob.getId()).isEqualTo("jobId1");
901 assertThat(restoredJob.getLastUpdated()).isEqualTo(savedJob.getLastUpdated());
903 jobs.remove("jobId1", this.infoProducers);
904 jobs.remove("jobId2", this.infoProducers);
907 // Restore the jobs, no jobs in database
908 InfoJobs jobs = new InfoJobs(this.applicationConfig, this.producerCallbacks);
909 jobs.restoreJobsFromDatabase();
910 assertThat(jobs.size()).isZero();
912 logger.warn("Test removing a job when the db file is gone");
913 this.infoJobs.remove("jobId1", this.infoProducers);
914 assertThat(this.infoJobs.size()).isEqualTo(1);
916 ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
917 await().untilAsserted(() -> assertThat(simulatorResults.jobsStopped).hasSize(3));
921 void testEiTypesDatabase() throws Exception {
922 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
924 assertThat(this.infoTypes.size()).isEqualTo(1);
928 InfoTypes types = new InfoTypes(this.applicationConfig);
929 types.restoreTypesFromDatabase();
930 assertThat(types.size()).isEqualTo(1);
933 // Restore the jobs, no jobs in database
934 InfoTypes types = new InfoTypes(this.applicationConfig);
936 types.restoreTypesFromDatabase();
937 assertThat(types.size()).isZero();
939 logger.warn("Test removing a job when the db file is gone");
940 this.infoTypes.remove(this.infoTypes.getType(TYPE_ID));
941 assertThat(this.infoJobs.size()).isZero();
945 void testConsumerTypeSubscriptionDatabase() {
946 final String callbackUrl = baseUrl() + ConsumerSimulatorController.getTypeStatusCallbackUrl();
947 final ConsumerTypeSubscriptionInfo info = new ConsumerTypeSubscriptionInfo(callbackUrl, "owner");
949 // PUT a subscription
950 String body = gson.toJson(info);
951 restClient().putForEntity(typeSubscriptionUrl() + "/subscriptionId", body).block();
952 assertThat(this.infoTypeSubscriptions.size()).isEqualTo(1);
954 InfoTypeSubscriptions restoredSubscriptions = new InfoTypeSubscriptions(this.applicationConfig);
955 assertThat(restoredSubscriptions.size()).isEqualTo(1);
956 assertThat(restoredSubscriptions.getSubscriptionsForOwner("owner")).hasSize(1);
958 // Delete the subscription
959 restClient().deleteForEntity(typeSubscriptionUrl() + "/subscriptionId").block();
960 restoredSubscriptions = new InfoTypeSubscriptions(this.applicationConfig);
961 assertThat(restoredSubscriptions.size()).isZero();
965 void testConsumerTypeSubscription() throws Exception {
967 final String callbackUrl = baseUrl() + ConsumerSimulatorController.getTypeStatusCallbackUrl();
968 final ConsumerTypeSubscriptionInfo info = new ConsumerTypeSubscriptionInfo(callbackUrl, "owner");
970 testErrorCode(restClient().get(typeSubscriptionUrl() + "/junk"), HttpStatus.NOT_FOUND,
971 "Could not find Information subscription: junk");
973 testErrorCode(restClient().delete(typeSubscriptionUrl() + "/junk"), HttpStatus.NOT_FOUND,
974 "Could not find Information subscription: junk");
977 // PUT a subscription
978 String body = gson.toJson(info);
979 ResponseEntity<String> resp =
980 restClient().putForEntity(typeSubscriptionUrl() + "/subscriptionId", body).block();
981 assertThat(this.infoTypeSubscriptions.size()).isEqualTo(1);
982 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.CREATED);
983 resp = restClient().putForEntity(typeSubscriptionUrl() + "/subscriptionId", body).block();
984 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
988 ResponseEntity<String> resp = restClient().getForEntity(typeSubscriptionUrl()).block();
989 assertThat(resp.getBody()).isEqualTo("[\"subscriptionId\"]");
990 resp = restClient().getForEntity(typeSubscriptionUrl() + "?owner=owner").block();
991 assertThat(resp.getBody()).isEqualTo("[\"subscriptionId\"]");
992 resp = restClient().getForEntity(typeSubscriptionUrl() + "?owner=junk").block();
993 assertThat(resp.getBody()).isEqualTo("[]");
997 // GET the individual subscription
998 ResponseEntity<String> resp = restClient().getForEntity(typeSubscriptionUrl() + "/subscriptionId").block();
999 ConsumerTypeSubscriptionInfo respInfo = gson.fromJson(resp.getBody(), ConsumerTypeSubscriptionInfo.class);
1000 assertThat(respInfo).isEqualTo(info);
1004 // Test the callbacks
1005 final ConsumerSimulatorController.TestResults consumerCalls = this.consumerSimulator.getTestResults();
1007 // Test callback for PUT type
1008 this.putInfoType(TYPE_ID);
1009 await().untilAsserted(() -> assertThat(consumerCalls.typeRegistrationInfoCallbacks).hasSize(1));
1010 assertThat(consumerCalls.typeRegistrationInfoCallbacks.get(0).state)
1011 .isEqualTo(ConsumerTypeRegistrationInfo.ConsumerTypeStatusValues.REGISTERED);
1013 // Test callback for DELETE type
1014 this.deleteInfoType(TYPE_ID);
1015 await().untilAsserted(() -> assertThat(consumerCalls.typeRegistrationInfoCallbacks).hasSize(2));
1016 assertThat(consumerCalls.typeRegistrationInfoCallbacks.get(1).state)
1017 .isEqualTo(ConsumerTypeRegistrationInfo.ConsumerTypeStatusValues.DEREGISTERED);
1021 // DELETE the subscription
1022 ResponseEntity<String> resp =
1023 restClient().deleteForEntity(typeSubscriptionUrl() + "/subscriptionId").block();
1024 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.NO_CONTENT);
1025 assertThat(this.infoTypeSubscriptions.size()).isZero();
1026 resp = restClient().getForEntity(typeSubscriptionUrl()).block();
1027 assertThat(resp.getBody()).isEqualTo("[]");
1032 void testRemovingNonWorkingSubscription() throws Exception {
1033 // Test that subscriptions are removed for a unresponsive consumer
1035 // PUT a subscription with a junk callback
1036 final ConsumerTypeSubscriptionInfo info = new ConsumerTypeSubscriptionInfo(baseUrl() + "/JUNK", "owner");
1037 String body = gson.toJson(info);
1038 restClient().putForEntity(typeSubscriptionUrl() + "/subscriptionId", body).block();
1039 assertThat(this.infoTypeSubscriptions.size()).isEqualTo(1);
1041 this.putInfoType(TYPE_ID);
1042 // The callback will fail and the subscription will be removed
1043 await().untilAsserted(() -> assertThat(this.infoTypeSubscriptions.size()).isZero());
1047 void testTypeSubscriptionErrorCodes() throws Exception {
1049 testErrorCode(restClient().get(typeSubscriptionUrl() + "/junk"), HttpStatus.NOT_FOUND,
1050 "Could not find Information subscription: junk");
1052 testErrorCode(restClient().delete(typeSubscriptionUrl() + "/junk"), HttpStatus.NOT_FOUND,
1053 "Could not find Information subscription: junk");
1056 private String typeSubscriptionUrl() {
1057 return ConsumerConsts.API_ROOT + "/info-type-subscription";
1060 private void deleteInfoProducer(String infoProducerId) {
1061 String url = ProducerConsts.API_ROOT + "/info-producers/" + infoProducerId;
1062 restClient().deleteForEntity(url).block();
1065 private void verifyJobStatus(String jobId, String expStatus) {
1066 String url = A1eConsts.API_ROOT + "/eijobs/" + jobId + "/status";
1067 String rsp = restClient().get(url).block();
1068 assertThat(rsp).contains(expStatus);
1071 private void assertProducerOpState(String producerId,
1072 ProducerStatusInfo.OperationalState expectedOperationalState) {
1073 String statusUrl = ProducerConsts.API_ROOT + "/info-producers/" + producerId + "/status";
1074 ResponseEntity<String> resp = restClient().getForEntity(statusUrl).block();
1075 ProducerStatusInfo statusInfo = gson.fromJson(resp.getBody(), ProducerStatusInfo.class);
1076 assertThat(statusInfo.opState).isEqualTo(expectedOperationalState);
1079 ProducerInfoTypeInfo ProducerInfoTypeRegistrationInfo(String typeId)
1080 throws JsonMappingException, JsonProcessingException {
1081 return new ProducerInfoTypeInfo(jsonSchemaObject(), typeSpecifcInfoObject());
1084 ProducerRegistrationInfo producerEiRegistratioInfoRejecting(String typeId)
1085 throws JsonMappingException, JsonProcessingException {
1086 return new ProducerRegistrationInfo(Arrays.asList(typeId), //
1087 baseUrl() + ProducerSimulatorController.JOB_ERROR_URL,
1088 baseUrl() + ProducerSimulatorController.SUPERVISION_ERROR_URL);
1091 ProducerRegistrationInfo producerInfoRegistratioInfo(String typeId)
1092 throws JsonMappingException, JsonProcessingException {
1093 return new ProducerRegistrationInfo(Arrays.asList(typeId), //
1094 baseUrl() + ProducerSimulatorController.JOB_URL, baseUrl() + ProducerSimulatorController.SUPERVISION_URL);
1097 private ConsumerJobInfo consumerJobInfo() throws JsonMappingException, JsonProcessingException {
1098 return consumerJobInfo(TYPE_ID, EI_JOB_ID);
1101 ConsumerJobInfo consumerJobInfo(String typeId, String infoJobId)
1102 throws JsonMappingException, JsonProcessingException {
1103 return new ConsumerJobInfo(typeId, jsonObject(), "owner", "https://junk.com",
1104 baseUrl() + A1eCallbacksSimulatorController.getJobStatusUrl(infoJobId));
1107 private A1eEiJobInfo infoJobInfo() throws JsonMappingException, JsonProcessingException {
1108 return infoJobInfo(TYPE_ID, EI_JOB_ID);
1111 A1eEiJobInfo infoJobInfo(String typeId, String infoJobId) throws JsonMappingException, JsonProcessingException {
1112 return new A1eEiJobInfo(typeId, jsonObject(), "owner", "https://junk.com",
1113 baseUrl() + A1eCallbacksSimulatorController.getJobStatusUrl(infoJobId));
1116 private Object jsonObject(String json) {
1118 return JsonParser.parseString(json).getAsJsonObject();
1119 } catch (Exception e) {
1120 throw new NullPointerException(e.toString());
1124 private Object typeSpecifcInfoObject() {
1125 return jsonObject("{ \"propertyName\" : \"value\" }");
1128 private Object jsonSchemaObject() {
1129 // a json schema with one mandatory property named "string"
1130 String schemaStr = "{" //
1131 + "\"$schema\": \"http://json-schema.org/draft-04/schema#\"," //
1132 + "\"type\": \"object\"," //
1133 + "\"properties\": {" //
1134 + EI_JOB_PROPERTY + " : {" //
1135 + " \"type\": \"string\"" //
1138 + "\"required\": [" //
1139 + EI_JOB_PROPERTY //
1142 return jsonObject(schemaStr);
1145 private Object jsonObject() {
1146 return jsonObject("{ " + EI_JOB_PROPERTY + " : \"value\" }");
1149 private InfoJob putInfoJob(String infoTypeId, String jobId)
1150 throws JsonMappingException, JsonProcessingException, ServiceException {
1152 String url = A1eConsts.API_ROOT + "/eijobs/" + jobId;
1153 String body = gson.toJson(infoJobInfo(infoTypeId, jobId));
1154 restClient().putForEntity(url, body).block();
1156 return this.infoJobs.getJob(jobId);
1159 private HttpStatus putInfoType(String infoTypeId)
1160 throws JsonMappingException, JsonProcessingException, ServiceException {
1161 String url = ProducerConsts.API_ROOT + "/info-types/" + infoTypeId;
1162 String body = gson.toJson(ProducerInfoTypeRegistrationInfo(infoTypeId));
1164 ResponseEntity<String> resp = restClient().putForEntity(url, body).block();
1165 this.infoTypes.getType(infoTypeId);
1166 return resp.getStatusCode();
1169 private String deleteInfoTypeUrl(String typeId) {
1170 return ProducerConsts.API_ROOT + "/info-types/" + typeId;
1173 private void deleteInfoType(String typeId) {
1174 restClient().delete(deleteInfoTypeUrl(typeId)).block();
1177 private InfoType putInfoProducerWithOneTypeRejecting(String producerId, String infoTypeId)
1178 throws JsonMappingException, JsonProcessingException, ServiceException {
1179 this.putInfoType(infoTypeId);
1180 String url = ProducerConsts.API_ROOT + "/info-producers/" + producerId;
1181 String body = gson.toJson(producerEiRegistratioInfoRejecting(infoTypeId));
1182 restClient().putForEntity(url, body).block();
1183 return this.infoTypes.getType(infoTypeId);
1186 private InfoType putInfoProducerWithOneType(String producerId, String infoTypeId)
1187 throws JsonMappingException, JsonProcessingException, ServiceException {
1188 this.putInfoType(infoTypeId);
1190 String url = ProducerConsts.API_ROOT + "/info-producers/" + producerId;
1191 String body = gson.toJson(producerInfoRegistratioInfo(infoTypeId));
1193 restClient().putForEntity(url, body).block();
1195 return this.infoTypes.getType(infoTypeId);
1198 private String baseUrl() {
1199 return "https://localhost:" + this.port;
1202 private AsyncRestClient restClient(boolean useTrustValidation) {
1203 WebClientConfig config = this.applicationConfig.getWebClientConfig();
1204 HttpProxyConfig httpProxyConfig = ImmutableHttpProxyConfig.builder() //
1205 .httpProxyHost("") //
1206 .httpProxyPort(0) //
1208 config = ImmutableWebClientConfig.builder() //
1209 .keyStoreType(config.keyStoreType()) //
1210 .keyStorePassword(config.keyStorePassword()) //
1211 .keyStore(config.keyStore()) //
1212 .keyPassword(config.keyPassword()) //
1213 .isTrustStoreUsed(useTrustValidation) //
1214 .trustStore(config.trustStore()) //
1215 .trustStorePassword(config.trustStorePassword()) //
1216 .httpProxyConfig(httpProxyConfig).build();
1218 AsyncRestClientFactory restClientFactory = new AsyncRestClientFactory(config);
1219 return restClientFactory.createRestClientNoHttpProxy(baseUrl());
1222 private AsyncRestClient restClient() {
1223 return restClient(false);
1226 private void testErrorCode(Mono<?> request, HttpStatus expStatus, String responseContains) {
1227 testErrorCode(request, expStatus, responseContains, true);
1230 private void testErrorCode(Mono<?> request, HttpStatus expStatus, String responseContains,
1231 boolean expectApplicationProblemJsonMediaType) {
1232 StepVerifier.create(request) //
1233 .expectSubscription() //
1234 .expectErrorMatches(
1235 t -> checkWebClientError(t, expStatus, responseContains, expectApplicationProblemJsonMediaType)) //
1239 private boolean checkWebClientError(Throwable throwable, HttpStatus expStatus, String responseContains,
1240 boolean expectApplicationProblemJsonMediaType) {
1241 assertTrue(throwable instanceof WebClientResponseException);
1242 WebClientResponseException responseException = (WebClientResponseException) throwable;
1243 assertThat(responseException.getStatusCode()).isEqualTo(expStatus);
1244 assertThat(responseException.getResponseBodyAsString()).contains(responseContains);
1245 if (expectApplicationProblemJsonMediaType) {
1246 assertThat(responseException.getHeaders().getContentType()).isEqualTo(MediaType.APPLICATION_PROBLEM_JSON);