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.nio.file.Files;
38 import java.nio.file.Path;
39 import java.util.Arrays;
42 import org.json.JSONObject;
43 import org.junit.jupiter.api.AfterEach;
44 import org.junit.jupiter.api.BeforeEach;
45 import org.junit.jupiter.api.Test;
46 import org.junit.jupiter.api.extension.ExtendWith;
47 import org.oransc.ics.clients.AsyncRestClient;
48 import org.oransc.ics.clients.AsyncRestClientFactory;
49 import org.oransc.ics.clients.SecurityContext;
50 import org.oransc.ics.configuration.ApplicationConfig;
51 import org.oransc.ics.configuration.ImmutableHttpProxyConfig;
52 import org.oransc.ics.configuration.ImmutableWebClientConfig;
53 import org.oransc.ics.configuration.WebClientConfig;
54 import org.oransc.ics.configuration.WebClientConfig.HttpProxyConfig;
55 import org.oransc.ics.controller.A1eCallbacksSimulatorController;
56 import org.oransc.ics.controller.ConsumerSimulatorController;
57 import org.oransc.ics.controller.ProducerSimulatorController;
58 import org.oransc.ics.controllers.a1e.A1eConsts;
59 import org.oransc.ics.controllers.a1e.A1eEiJobInfo;
60 import org.oransc.ics.controllers.a1e.A1eEiJobStatus;
61 import org.oransc.ics.controllers.a1e.A1eEiTypeInfo;
62 import org.oransc.ics.controllers.r1consumer.ConsumerConsts;
63 import org.oransc.ics.controllers.r1consumer.ConsumerInfoTypeInfo;
64 import org.oransc.ics.controllers.r1consumer.ConsumerJobInfo;
65 import org.oransc.ics.controllers.r1consumer.ConsumerJobStatus;
66 import org.oransc.ics.controllers.r1consumer.ConsumerTypeRegistrationInfo;
67 import org.oransc.ics.controllers.r1consumer.ConsumerTypeSubscriptionInfo;
68 import org.oransc.ics.controllers.r1producer.ProducerCallbacks;
69 import org.oransc.ics.controllers.r1producer.ProducerConsts;
70 import org.oransc.ics.controllers.r1producer.ProducerInfoTypeInfo;
71 import org.oransc.ics.controllers.r1producer.ProducerJobInfo;
72 import org.oransc.ics.controllers.r1producer.ProducerRegistrationInfo;
73 import org.oransc.ics.controllers.r1producer.ProducerStatusInfo;
74 import org.oransc.ics.exceptions.ServiceException;
75 import org.oransc.ics.repository.InfoJob;
76 import org.oransc.ics.repository.InfoJobs;
77 import org.oransc.ics.repository.InfoProducer;
78 import org.oransc.ics.repository.InfoProducers;
79 import org.oransc.ics.repository.InfoType;
80 import org.oransc.ics.repository.InfoTypeSubscriptions;
81 import org.oransc.ics.repository.InfoTypes;
82 import org.oransc.ics.tasks.ProducerSupervision;
83 import org.slf4j.Logger;
84 import org.slf4j.LoggerFactory;
85 import org.springframework.beans.factory.annotation.Autowired;
86 import org.springframework.boot.test.context.SpringBootTest;
87 import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
88 import org.springframework.boot.test.context.TestConfiguration;
89 import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
90 import org.springframework.boot.web.server.LocalServerPort;
91 import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
92 import org.springframework.context.ApplicationContext;
93 import org.springframework.context.annotation.Bean;
94 import org.springframework.http.HttpStatus;
95 import org.springframework.http.MediaType;
96 import org.springframework.http.ResponseEntity;
97 import org.springframework.test.context.TestPropertySource;
98 import org.springframework.test.context.junit.jupiter.SpringExtension;
99 import org.springframework.web.reactive.function.client.WebClientResponseException;
101 import reactor.core.publisher.Mono;
102 import reactor.test.StepVerifier;
104 @ExtendWith(SpringExtension.class)
105 @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
108 "server.ssl.key-store=./config/keystore.jks", //
109 "app.webclient.trust-store=./config/truststore.jks", //
110 "app.vardata-directory=./target"})
111 class ApplicationTest {
112 private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
114 private final String TYPE_ID = "typeId";
115 private final String PRODUCER_ID = "producerId";
116 private final String EI_JOB_PROPERTY = "\"property1\"";
117 private final String EI_JOB_ID = "jobId";
120 ApplicationContext context;
129 InfoProducers infoProducers;
132 ApplicationConfig applicationConfig;
135 ProducerSimulatorController producerSimulator;
138 ConsumerSimulatorController consumerSimulator;
141 A1eCallbacksSimulatorController a1eCallbacksSimulator;
144 ProducerSupervision producerSupervision;
147 ProducerCallbacks producerCallbacks;
150 InfoTypeSubscriptions infoTypeSubscriptions;
153 SecurityContext securityContext;
155 private static Gson gson = new GsonBuilder().create();
158 * Overrides the BeanFactory.
161 static class TestBeanFactory {
163 public ServletWebServerFactory servletContainer() {
164 return new TomcatServletWebServerFactory();
173 this.infoJobs.clear();
174 this.infoTypes.clear();
175 this.infoProducers.clear();
176 this.infoTypeSubscriptions.clear();
177 this.producerSimulator.getTestResults().reset();
178 this.consumerSimulator.getTestResults().reset();
179 this.a1eCallbacksSimulator.getTestResults().reset();
180 this.securityContext.setAuthTokenFilePath(null);
185 assertThat(this.producerSimulator.getTestResults().errorFound).isFalse();
189 void generateApiDoc() throws FileNotFoundException {
190 String url = "/v3/api-docs";
191 ResponseEntity<String> resp = restClient().getForEntity(url).block();
192 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
194 JSONObject jsonObj = new JSONObject(resp.getBody());
195 assertThat(jsonObj.remove("servers")).isNotNull();
197 String indented = jsonObj.toString(4);
198 try (PrintStream out = new PrintStream(new FileOutputStream("api/ics-api.json"))) {
204 void a1eGetEiTypes() throws Exception {
205 putInfoProducerWithOneType(PRODUCER_ID, "test");
206 String url = A1eConsts.API_ROOT + "/eitypes";
207 String rsp = restClient().get(url).block();
208 assertThat(rsp).isEqualTo("[\"test\"]");
212 void consumerGetInfoTypes() throws Exception {
213 putInfoProducerWithOneType(PRODUCER_ID, "test");
214 String url = ConsumerConsts.API_ROOT + "/info-types";
215 String rsp = restClient().get(url).block();
216 assertThat(rsp).isEqualTo("[\"test\"]");
220 void a1eGetEiTypesEmpty() throws Exception {
221 String url = A1eConsts.API_ROOT + "/eitypes";
222 String rsp = restClient().get(url).block();
223 assertThat(rsp).isEqualTo("[]");
227 void consumerGetEiTypesEmpty() throws Exception {
228 String url = ConsumerConsts.API_ROOT + "/info-types";
229 String rsp = restClient().get(url).block();
230 assertThat(rsp).isEqualTo("[]");
234 void a1eGetEiType() throws Exception {
235 putInfoProducerWithOneType(PRODUCER_ID, "test");
236 String url = A1eConsts.API_ROOT + "/eitypes/test";
237 String rsp = restClient().get(url).block();
238 A1eEiTypeInfo info = gson.fromJson(rsp, A1eEiTypeInfo.class);
239 assertThat(info).isNotNull();
243 void consumerGetEiType() throws Exception {
244 putInfoProducerWithOneType(PRODUCER_ID, "test");
245 String url = ConsumerConsts.API_ROOT + "/info-types/test";
246 String rsp = restClient().get(url).block();
247 ConsumerInfoTypeInfo info = gson.fromJson(rsp, ConsumerInfoTypeInfo.class);
248 assertThat(info).isNotNull();
249 assertThat(info.jobDataSchema).isNotNull();
250 assertThat(info.state).isEqualTo(ConsumerInfoTypeInfo.ConsumerTypeStatusValues.ENABLED);
251 assertThat(info.noOfProducers).isEqualTo(1);
255 void a1eGetEiTypeNotFound() throws Exception {
256 String url = A1eConsts.API_ROOT + "/eitypes/junk";
257 testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND, "Information type not found: junk");
261 void consumerGetEiTypeNotFound() throws Exception {
262 String url = ConsumerConsts.API_ROOT + "/info-types/junk";
263 testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND, "Information type not found: junk");
267 void a1eGetEiJobsIds() throws Exception {
268 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
269 putInfoJob(TYPE_ID, "jobId");
270 final String JOB_ID_JSON = "[\"jobId\"]";
271 String url = A1eConsts.API_ROOT + "/eijobs?infoTypeId=typeId";
272 String rsp = restClient().get(url).block();
273 assertThat(rsp).isEqualTo(JOB_ID_JSON);
275 url = A1eConsts.API_ROOT + "/eijobs?owner=owner";
276 rsp = restClient().get(url).block();
277 assertThat(rsp).isEqualTo(JOB_ID_JSON);
279 url = A1eConsts.API_ROOT + "/eijobs?owner=JUNK";
280 rsp = restClient().get(url).block();
281 assertThat(rsp).isEqualTo("[]");
283 url = A1eConsts.API_ROOT + "/eijobs";
284 rsp = restClient().get(url).block();
285 assertThat(rsp).isEqualTo(JOB_ID_JSON);
287 url = A1eConsts.API_ROOT + "/eijobs?eiTypeId=typeId&&owner=owner";
288 rsp = restClient().get(url).block();
289 assertThat(rsp).isEqualTo(JOB_ID_JSON);
291 url = A1eConsts.API_ROOT + "/eijobs?eiTypeId=JUNK";
292 rsp = restClient().get(url).block();
293 assertThat(rsp).isEqualTo("[]");
297 void consumerGetInformationJobsIds() throws Exception {
298 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
299 putInfoJob(TYPE_ID, "jobId");
300 final String JOB_ID_JSON = "[\"jobId\"]";
301 String url = ConsumerConsts.API_ROOT + "/info-jobs?infoTypeId=typeId";
302 String rsp = restClient().get(url).block();
303 assertThat(rsp).isEqualTo(JOB_ID_JSON);
305 url = ConsumerConsts.API_ROOT + "/info-jobs?owner=owner";
306 rsp = restClient().get(url).block();
307 assertThat(rsp).isEqualTo(JOB_ID_JSON);
309 url = ConsumerConsts.API_ROOT + "/info-jobs?owner=JUNK";
310 rsp = restClient().get(url).block();
311 assertThat(rsp).isEqualTo("[]");
313 url = ConsumerConsts.API_ROOT + "/info-jobs";
314 rsp = restClient().get(url).block();
315 assertThat(rsp).isEqualTo(JOB_ID_JSON);
317 url = ConsumerConsts.API_ROOT + "/info-jobs?infoTypeId=typeId&&owner=owner";
318 rsp = restClient().get(url).block();
319 assertThat(rsp).isEqualTo(JOB_ID_JSON);
321 url = ConsumerConsts.API_ROOT + "/info-jobs?infoTypeId=JUNK";
322 rsp = restClient().get(url).block();
323 assertThat(rsp).isEqualTo("[]");
327 void a1eGetEiJob() throws Exception {
328 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
329 putInfoJob(TYPE_ID, "jobId");
330 String url = A1eConsts.API_ROOT + "/eijobs/jobId";
331 String rsp = restClient().get(url).block();
332 A1eEiJobInfo info = gson.fromJson(rsp, A1eEiJobInfo.class);
333 assertThat(info.owner).isEqualTo("owner");
334 assertThat(info.eiTypeId).isEqualTo(TYPE_ID);
338 void consumerGetEiJob() throws Exception {
339 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
340 putInfoJob(TYPE_ID, "jobId");
341 String url = ConsumerConsts.API_ROOT + "/info-jobs/jobId";
342 String rsp = restClient().get(url).block();
343 ConsumerJobInfo info = gson.fromJson(rsp, ConsumerJobInfo.class);
344 assertThat(info.owner).isEqualTo("owner");
345 assertThat(info.infoTypeId).isEqualTo(TYPE_ID);
349 void a1eGetEiJobNotFound() throws Exception {
350 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
351 String url = A1eConsts.API_ROOT + "/eijobs/junk";
352 testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND, "Could not find Information job: junk");
356 void consumerGetInfoJobNotFound() throws Exception {
357 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
358 String url = ConsumerConsts.API_ROOT + "/info-jobs/junk";
359 testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND, "Could not find Information job: junk");
363 void a1eGetEiJobStatus() throws Exception {
364 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
365 putInfoJob(TYPE_ID, "jobId");
367 verifyJobStatus("jobId", "ENABLED");
371 void consumerGetInfoJobStatus() throws Exception {
372 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
373 putInfoJob(TYPE_ID, "jobId");
375 String url = ConsumerConsts.API_ROOT + "/info-jobs/jobId/status";
376 String rsp = restClient().get(url).block();
378 .contains("ENABLED") //
379 .contains(PRODUCER_ID);
381 ConsumerJobStatus status = gson.fromJson(rsp, ConsumerJobStatus.class);
382 assertThat(status.producers).contains(PRODUCER_ID);
386 void a1eDeleteEiJob() throws Exception {
387 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
388 putInfoJob(TYPE_ID, "jobId");
389 assertThat(this.infoJobs.size()).isEqualTo(1);
390 String url = A1eConsts.API_ROOT + "/eijobs/jobId";
391 restClient().delete(url).block();
392 assertThat(this.infoJobs.size()).isZero();
394 ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
395 await().untilAsserted(() -> assertThat(simulatorResults.jobsStopped).hasSize(1));
396 assertThat(simulatorResults.jobsStopped.get(0)).isEqualTo("jobId");
401 void consumerDeleteEiJob() throws Exception {
402 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
403 putInfoJob(TYPE_ID, "jobId");
404 assertThat(this.infoJobs.size()).isEqualTo(1);
405 String url = ConsumerConsts.API_ROOT + "/info-jobs/jobId";
406 restClient().delete(url).block();
407 assertThat(this.infoJobs.size()).isZero();
409 ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
410 await().untilAsserted(() -> assertThat(simulatorResults.jobsStopped).hasSize(1));
411 assertThat(simulatorResults.jobsStopped.get(0)).isEqualTo("jobId");
413 testErrorCode(restClient().delete(url), HttpStatus.NOT_FOUND, "Could not find Information job: jobId");
417 void a1eDeleteEiJobNotFound() throws Exception {
418 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
419 String url = A1eConsts.API_ROOT + "/eijobs/junk";
420 testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND, "Could not find Information job: junk");
424 void consumerDeleteEiJobNotFound() throws Exception {
425 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
426 String url = ConsumerConsts.API_ROOT + "/info-jobs/junk";
427 testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND, "Could not find Information job: junk");
431 void a1ePutEiJob() throws Exception {
432 // Test that one producer accepting a job is enough
433 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
434 putInfoProducerWithOneTypeRejecting("simulateProducerError", TYPE_ID);
436 String url = A1eConsts.API_ROOT + "/eijobs/jobId";
437 String body = gson.toJson(infoJobInfo());
438 ResponseEntity<String> resp = restClient().putForEntity(url, body).block();
439 assertThat(this.infoJobs.size()).isEqualTo(1);
440 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.CREATED);
442 ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
443 await().untilAsserted(() -> assertThat(simulatorResults.jobsStarted).hasSize(1));
444 ProducerJobInfo request = simulatorResults.jobsStarted.get(0);
445 assertThat(request.id).isEqualTo("jobId");
447 // One retry --> two calls
448 await().untilAsserted(() -> assertThat(simulatorResults.noOfRejectedCreate).isEqualTo(2));
449 assertThat(simulatorResults.noOfRejectedCreate).isEqualTo(2);
451 resp = restClient().putForEntity(url, body).block();
452 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
453 InfoJob job = this.infoJobs.getJob("jobId");
454 assertThat(job.getOwner()).isEqualTo("owner");
456 verifyJobStatus(EI_JOB_ID, "ENABLED");
460 void consumerPutInformationJob() throws Exception {
461 // Test that one producer accepting a job is enough
462 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
464 String url = ConsumerConsts.API_ROOT + "/info-jobs/jobId";
465 String body = gson.toJson(consumerJobInfo());
466 ResponseEntity<String> resp = restClient().putForEntity(url, body).block();
467 assertThat(this.infoJobs.size()).isEqualTo(1);
468 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.CREATED);
470 ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
471 await().untilAsserted(() -> assertThat(simulatorResults.jobsStarted).hasSize(1));
472 ProducerJobInfo request = simulatorResults.jobsStarted.get(0);
473 assertThat(request.id).isEqualTo("jobId");
475 resp = restClient().putForEntity(url, body).block();
476 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
477 InfoJob job = this.infoJobs.getJob("jobId");
478 assertThat(job.getOwner()).isEqualTo("owner");
480 verifyJobStatus(EI_JOB_ID, "ENABLED");
484 void consumerPutInformationJob_noType() throws JsonMappingException, JsonProcessingException, ServiceException {
485 String url = ConsumerConsts.API_ROOT + "/info-jobs/jobId?typeCheck=false";
486 String body = gson.toJson(consumerJobInfo());
487 ResponseEntity<String> resp = restClient().putForEntity(url, body).block();
488 assertThat(this.infoJobs.size()).isEqualTo(1);
489 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.CREATED);
490 verifyJobStatus(EI_JOB_ID, "DISABLED");
492 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
494 verifyJobStatus(EI_JOB_ID, "ENABLED");
498 void a1ePutEiJob_jsonSchemavalidationError() throws Exception {
499 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
501 String url = A1eConsts.API_ROOT + "/eijobs/jobId";
502 // The element with name "property1" is mandatory in the schema
503 A1eEiJobInfo jobInfo = new A1eEiJobInfo("typeId", jsonObject("{ \"XXstring\" : \"value\" }"), "owner",
504 "targetUri", "jobStatusUrl");
505 String body = gson.toJson(jobInfo);
507 testErrorCode(restClient().put(url, body), HttpStatus.BAD_REQUEST, "Json validation failure");
509 testErrorCode(restClient().put(url, "{jojo}"), HttpStatus.BAD_REQUEST, "", false);
514 void consumerPutJob_jsonSchemavalidationError() throws Exception {
515 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
517 String url = ConsumerConsts.API_ROOT + "/info-jobs/jobId?typeCheck=true";
518 // The element with name "property1" is mandatory in the schema
519 ConsumerJobInfo jobInfo =
520 new ConsumerJobInfo("typeId", jsonObject("{ \"XXstring\" : \"value\" }"), "owner", "targetUri", null);
521 String body = gson.toJson(jobInfo);
523 testErrorCode(restClient().put(url, body), HttpStatus.BAD_REQUEST, "Json validation failure");
527 void consumerPutJob_uriError() throws Exception {
528 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
530 String url = ConsumerConsts.API_ROOT + "/info-jobs/jobId?typeCheck=true";
532 ConsumerJobInfo jobInfo = new ConsumerJobInfo(TYPE_ID, jsonObject(), "owner", "junk", null);
533 String body = gson.toJson(jobInfo);
535 testErrorCode(restClient().put(url, body), HttpStatus.BAD_REQUEST, "URI: junk is not absolute");
539 void a1eChangingEiTypeGetRejected() throws Exception {
540 putInfoProducerWithOneType("producer1", "typeId1");
541 putInfoProducerWithOneType("producer2", "typeId2");
542 putInfoJob("typeId1", "jobId");
544 String url = A1eConsts.API_ROOT + "/eijobs/jobId";
545 String body = gson.toJson(infoJobInfo("typeId2", "jobId"));
546 testErrorCode(restClient().put(url, body), HttpStatus.CONFLICT,
547 "Not allowed to change type for existing EI job");
551 void consumerChangingInfoTypeGetRejected() throws Exception {
552 putInfoProducerWithOneType("producer1", "typeId1");
553 putInfoProducerWithOneType("producer2", "typeId2");
554 putInfoJob("typeId1", "jobId");
556 String url = ConsumerConsts.API_ROOT + "/info-jobs/jobId";
557 String body = gson.toJson(consumerJobInfo("typeId2", "jobId"));
558 testErrorCode(restClient().put(url, body), HttpStatus.CONFLICT, "Not allowed to change type for existing job");
562 void producerPutEiType() throws JsonMappingException, JsonProcessingException, ServiceException {
563 assertThat(putInfoType(TYPE_ID)).isEqualTo(HttpStatus.CREATED);
564 assertThat(putInfoType(TYPE_ID)).isEqualTo(HttpStatus.OK);
568 void producerPutEiType_noSchema() {
569 String url = ProducerConsts.API_ROOT + "/info-types/" + TYPE_ID;
571 testErrorCode(restClient().put(url, body), HttpStatus.BAD_REQUEST, "No schema provided");
573 testErrorCode(restClient().post(url, body), HttpStatus.METHOD_NOT_ALLOWED, "", false);
577 void producerDeleteEiType() throws Exception {
578 putInfoType(TYPE_ID);
579 this.putInfoJob(TYPE_ID, "job1");
580 this.putInfoJob(TYPE_ID, "job2");
581 deleteInfoType(TYPE_ID);
583 assertThat(this.infoTypes.size()).isZero();
584 assertThat(this.infoJobs.size()).isZero(); // Test that also the job is deleted
586 testErrorCode(restClient().delete(deleteInfoTypeUrl(TYPE_ID)), HttpStatus.NOT_FOUND,
587 "Information type not found");
591 void producerDeleteEiTypeExistingProducer() throws Exception {
592 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
593 String url = ProducerConsts.API_ROOT + "/info-types/" + TYPE_ID;
594 testErrorCode(restClient().delete(url), HttpStatus.CONFLICT, "The type has active producers: " + PRODUCER_ID);
595 assertThat(this.infoTypes.size()).isEqualTo(1);
599 void producerPutProducerWithOneType_rejecting()
600 throws JsonMappingException, JsonProcessingException, ServiceException {
601 putInfoProducerWithOneTypeRejecting("simulateProducerError", TYPE_ID);
602 String url = A1eConsts.API_ROOT + "/eijobs/" + EI_JOB_ID;
603 String body = gson.toJson(infoJobInfo());
604 restClient().put(url, body).block();
606 ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
607 // There is one retry -> 2 calls
608 await().untilAsserted(() -> assertThat(simulatorResults.noOfRejectedCreate).isEqualTo(2));
609 assertThat(simulatorResults.noOfRejectedCreate).isEqualTo(2);
611 verifyJobStatus(EI_JOB_ID, "DISABLED");
615 void producerGetInfoProducerTypes() throws Exception {
616 final String EI_TYPE_ID_2 = TYPE_ID + "_2";
617 putInfoProducerWithOneType("producer1", TYPE_ID);
618 putInfoJob(TYPE_ID, "jobId");
619 putInfoProducerWithOneType("producer2", EI_TYPE_ID_2);
620 putInfoJob(EI_TYPE_ID_2, "jobId2");
621 String url = ProducerConsts.API_ROOT + "/info-types";
623 ResponseEntity<String> resp = restClient().getForEntity(url).block();
624 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
625 assertThat(resp.getBody()).contains(TYPE_ID);
626 assertThat(resp.getBody()).contains(EI_TYPE_ID_2);
630 void producerPutInfoProducer() throws Exception {
631 this.putInfoType(TYPE_ID);
632 String url = ProducerConsts.API_ROOT + "/info-producers/infoProducerId";
633 String body = gson.toJson(producerInfoRegistratioInfo(TYPE_ID));
635 ResponseEntity<String> resp = restClient().putForEntity(url, body).block();
636 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.CREATED);
638 assertThat(this.infoTypes.size()).isEqualTo(1);
639 assertThat(this.infoProducers.getProducersForType(TYPE_ID)).hasSize(1);
640 assertThat(this.infoProducers.size()).isEqualTo(1);
641 assertThat(this.infoProducers.get("infoProducerId").getInfoTypes().iterator().next().getId())
644 resp = restClient().putForEntity(url, body).block();
645 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
648 resp = restClient().getForEntity(url).block();
649 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
650 assertThat(resp.getBody()).isEqualTo(body);
652 testErrorCode(restClient().get(url + "junk"), HttpStatus.NOT_FOUND, "Could not find Information Producer");
656 void producerPutInfoProducerExistingJob() throws Exception {
657 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
658 putInfoJob(TYPE_ID, "jobId");
659 String url = ProducerConsts.API_ROOT + "/info-producers/infoProducerId";
660 String body = gson.toJson(producerInfoRegistratioInfo(TYPE_ID));
661 restClient().putForEntity(url, body).block();
663 ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
664 await().untilAsserted(() -> assertThat(simulatorResults.jobsStarted).hasSize(2));
665 ProducerJobInfo request = simulatorResults.jobsStarted.get(0);
666 assertThat(request.id).isEqualTo("jobId");
670 void testPutInfoProducer_noType() throws Exception {
671 String url = ProducerConsts.API_ROOT + "/info-producers/infoProducerId";
672 String body = gson.toJson(producerInfoRegistratioInfo(TYPE_ID));
673 testErrorCode(restClient().put(url, body), HttpStatus.NOT_FOUND, "Information type not found");
677 void producerPutProducerAndInfoJob() throws Exception {
678 this.putInfoType(TYPE_ID);
679 String url = ProducerConsts.API_ROOT + "/info-producers/infoProducerId";
680 String body = gson.toJson(producerInfoRegistratioInfo(TYPE_ID));
681 restClient().putForEntity(url, body).block();
682 assertThat(this.infoTypes.size()).isEqualTo(1);
683 this.infoTypes.getType(TYPE_ID);
685 url = A1eConsts.API_ROOT + "/eijobs/jobId";
686 body = gson.toJson(infoJobInfo());
687 restClient().putForEntity(url, body).block();
689 ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
690 await().untilAsserted(() -> assertThat(simulatorResults.jobsStarted).hasSize(1));
691 ProducerJobInfo request = simulatorResults.jobsStarted.get(0);
692 assertThat(request.id).isEqualTo("jobId");
696 void producerGetInfoJobsForProducer() throws JsonMappingException, JsonProcessingException, ServiceException {
697 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
698 putInfoJob(TYPE_ID, "jobId1");
699 putInfoJob(TYPE_ID, "jobId2");
701 // PUT a consumerRestApiTestBase.java
702 String url = ProducerConsts.API_ROOT + "/info-producers/infoProducerId";
703 String body = gson.toJson(producerInfoRegistratioInfo(TYPE_ID));
704 restClient().putForEntity(url, body).block();
706 url = ProducerConsts.API_ROOT + "/info-producers/infoProducerId/info-jobs";
707 ResponseEntity<String> resp = restClient().getForEntity(url).block();
708 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
710 ProducerJobInfo[] parsedResp = gson.fromJson(resp.getBody(), ProducerJobInfo[].class);
711 assertThat(parsedResp[0].typeId).isEqualTo(TYPE_ID);
712 assertThat(parsedResp[1].typeId).isEqualTo(TYPE_ID);
716 void producerDeleteInfoProducer() throws Exception {
717 putInfoProducerWithOneType("infoProducerId", TYPE_ID);
718 putInfoProducerWithOneType("infoProducerId2", TYPE_ID);
720 assertThat(this.infoProducers.size()).isEqualTo(2);
721 InfoType type = this.infoTypes.getType(TYPE_ID);
722 assertThat(this.infoProducers.getProducerIdsForType(type.getId())).contains("infoProducerId");
723 assertThat(this.infoProducers.getProducerIdsForType(type.getId())).contains("infoProducerId2");
724 putInfoJob(TYPE_ID, "jobId");
725 assertThat(this.infoJobs.size()).isEqualTo(1);
727 deleteInfoProducer("infoProducerId");
728 assertThat(this.infoProducers.size()).isEqualTo(1);
729 assertThat(this.infoProducers.getProducerIdsForType(TYPE_ID)).doesNotContain("infoProducerId");
730 verifyJobStatus("jobId", "ENABLED");
732 deleteInfoProducer("infoProducerId2");
733 assertThat(this.infoProducers.size()).isZero();
734 assertThat(this.infoTypes.size()).isEqualTo(1);
735 verifyJobStatus("jobId", "DISABLED");
737 String url = ProducerConsts.API_ROOT + "/info-producers/" + "junk";
738 testErrorCode(restClient().delete(url), HttpStatus.NOT_FOUND, "Could not find Information Producer");
742 void a1eJobStatusNotifications() throws JsonMappingException, JsonProcessingException, ServiceException {
743 A1eCallbacksSimulatorController.TestResults consumerCalls = this.a1eCallbacksSimulator.getTestResults();
744 ProducerSimulatorController.TestResults producerCalls = this.producerSimulator.getTestResults();
746 putInfoProducerWithOneType("infoProducerId", TYPE_ID);
747 putInfoJob(TYPE_ID, "jobId");
748 putInfoProducerWithOneType("infoProducerId2", TYPE_ID);
749 await().untilAsserted(() -> assertThat(producerCalls.jobsStarted).hasSize(2));
751 deleteInfoProducer("infoProducerId2");
752 assertThat(this.infoTypes.size()).isEqualTo(1); // The type remains, one producer left
753 deleteInfoProducer("infoProducerId");
754 assertThat(this.infoTypes.size()).isEqualTo(1); // The type remains
755 assertThat(this.infoJobs.size()).isEqualTo(1); // The job remains
756 await().untilAsserted(() -> assertThat(consumerCalls.eiJobStatusCallbacks).hasSize(1));
757 assertThat(consumerCalls.eiJobStatusCallbacks.get(0).state)
758 .isEqualTo(A1eEiJobStatus.EiJobStatusValues.DISABLED);
760 putInfoProducerWithOneType("infoProducerId", TYPE_ID);
761 await().untilAsserted(() -> assertThat(consumerCalls.eiJobStatusCallbacks).hasSize(2));
762 assertThat(consumerCalls.eiJobStatusCallbacks.get(1).state).isEqualTo(A1eEiJobStatus.EiJobStatusValues.ENABLED);
766 void a1eJobStatusNotifications2() throws JsonMappingException, JsonProcessingException, ServiceException {
767 // Test replacing a producer with new and removed types
770 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
771 putInfoJob(TYPE_ID, EI_JOB_ID);
773 // change the type for the producer, the job shall be disabled
774 putInfoProducerWithOneType(PRODUCER_ID, "junk");
775 verifyJobStatus(EI_JOB_ID, "DISABLED");
776 A1eCallbacksSimulatorController.TestResults consumerCalls = this.a1eCallbacksSimulator.getTestResults();
777 await().untilAsserted(() -> assertThat(consumerCalls.eiJobStatusCallbacks).hasSize(1));
778 assertThat(consumerCalls.eiJobStatusCallbacks.get(0).state)
779 .isEqualTo(A1eEiJobStatus.EiJobStatusValues.DISABLED);
781 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
782 verifyJobStatus(EI_JOB_ID, "ENABLED");
783 await().untilAsserted(() -> assertThat(consumerCalls.eiJobStatusCallbacks).hasSize(2));
784 assertThat(consumerCalls.eiJobStatusCallbacks.get(1).state).isEqualTo(A1eEiJobStatus.EiJobStatusValues.ENABLED);
788 void producerGetProducerInfoType() throws JsonMappingException, JsonProcessingException, ServiceException {
789 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
790 String url = ProducerConsts.API_ROOT + "/info-types/" + TYPE_ID;
791 ResponseEntity<String> resp = restClient().getForEntity(url).block();
792 ProducerInfoTypeInfo info = gson.fromJson(resp.getBody(), ProducerInfoTypeInfo.class);
793 assertThat(info.jobDataSchema).isNotNull();
794 assertThat(info.typeSpecificInformation).isNotNull();
796 testErrorCode(restClient().get(url + "junk"), HttpStatus.NOT_FOUND, "Information type not found");
800 void producerGetProducerIdentifiers() throws JsonMappingException, JsonProcessingException, ServiceException {
801 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
802 String url = ProducerConsts.API_ROOT + "/info-producers";
803 ResponseEntity<String> resp = restClient().getForEntity(url).block();
804 assertThat(resp.getBody()).contains(PRODUCER_ID);
806 url = ProducerConsts.API_ROOT + "/info-producers?infoTypeId=" + TYPE_ID;
807 resp = restClient().getForEntity(url).block();
808 assertThat(resp.getBody()).contains(PRODUCER_ID);
810 url = ProducerConsts.API_ROOT + "/info-producers?infoTypeId=junk";
811 resp = restClient().getForEntity(url).block();
812 assertThat(resp.getBody()).isEqualTo("[]");
816 void producerSupervision() throws JsonMappingException, JsonProcessingException, ServiceException {
818 A1eCallbacksSimulatorController.TestResults consumerResults = this.a1eCallbacksSimulator.getTestResults();
819 putInfoProducerWithOneTypeRejecting("simulateProducerError", TYPE_ID);
823 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
824 putInfoJob(TYPE_ID, EI_JOB_ID);
825 verifyJobStatus(EI_JOB_ID, "ENABLED");
826 deleteInfoProducer(PRODUCER_ID);
827 // A Job disabled status notification shall now be received
828 await().untilAsserted(() -> assertThat(consumerResults.eiJobStatusCallbacks).hasSize(1));
829 assertThat(consumerResults.eiJobStatusCallbacks.get(0).state)
830 .isEqualTo(A1eEiJobStatus.EiJobStatusValues.DISABLED);
831 verifyJobStatus(EI_JOB_ID, "DISABLED");
834 assertThat(this.infoProducers.size()).isEqualTo(1);
835 assertThat(this.infoTypes.size()).isEqualTo(1);
836 assertProducerOpState("simulateProducerError", ProducerStatusInfo.OperationalState.ENABLED);
838 this.producerSupervision.createTask().blockLast();
839 this.producerSupervision.createTask().blockLast();
841 // Now we have one producer that is disabled
842 assertThat(this.infoProducers.size()).isEqualTo(1);
843 assertProducerOpState("simulateProducerError", ProducerStatusInfo.OperationalState.DISABLED);
845 // After 3 failed checks, the producer shall be deregistered
846 this.producerSupervision.createTask().blockLast();
847 assertThat(this.infoProducers.size()).isZero(); // The producer is removed
848 assertThat(this.infoTypes.size()).isEqualTo(1); // The type remains
850 // Now we have one disabled job, and no producer.
851 // PUT a producer, then a Job ENABLED status notification shall be received
852 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
853 await().untilAsserted(() -> assertThat(consumerResults.eiJobStatusCallbacks).hasSize(2));
854 assertThat(consumerResults.eiJobStatusCallbacks.get(1).state)
855 .isEqualTo(A1eEiJobStatus.EiJobStatusValues.ENABLED);
856 verifyJobStatus(EI_JOB_ID, "ENABLED");
860 void producerSupervision2() throws JsonMappingException, JsonProcessingException, ServiceException {
861 // Test that supervision enables not enabled jobs and sends a notification when
864 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
865 putInfoJob(TYPE_ID, EI_JOB_ID);
867 InfoProducer producer = this.infoProducers.getProducer(PRODUCER_ID);
868 InfoJob job = this.infoJobs.getJob(EI_JOB_ID);
869 // Pretend that the producer did reject the job and the a DISABLED notification
870 // is sent for the job
871 producer.setJobDisabled(job);
872 job.setLastReportedStatus(false);
873 verifyJobStatus(EI_JOB_ID, "DISABLED");
875 // Run the supervision and wait for the job to get started in the producer
876 this.producerSupervision.createTask().blockLast();
877 A1eCallbacksSimulatorController.TestResults consumerResults = this.a1eCallbacksSimulator.getTestResults();
878 await().untilAsserted(() -> assertThat(consumerResults.eiJobStatusCallbacks).hasSize(1));
879 assertThat(consumerResults.eiJobStatusCallbacks.get(0).state)
880 .isEqualTo(A1eEiJobStatus.EiJobStatusValues.ENABLED);
881 verifyJobStatus(EI_JOB_ID, "ENABLED");
885 void testGetStatus() throws JsonMappingException, JsonProcessingException, ServiceException {
886 putInfoProducerWithOneTypeRejecting("simulateProducerError", TYPE_ID);
887 putInfoProducerWithOneTypeRejecting("simulateProducerError2", TYPE_ID);
889 String url = "/status";
890 ResponseEntity<String> resp = restClient().getForEntity(url).block();
891 assertThat(resp.getBody()).contains("hunky dory");
895 void testEiJobDatabase() throws Exception {
896 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
897 putInfoJob(TYPE_ID, "jobId1");
898 putInfoJob(TYPE_ID, "jobId2");
900 assertThat(this.infoJobs.size()).isEqualTo(2);
903 InfoJob savedJob = this.infoJobs.getJob("jobId1");
905 InfoJobs jobs = new InfoJobs(this.applicationConfig, this.producerCallbacks);
906 jobs.restoreJobsFromDatabase();
907 assertThat(jobs.size()).isEqualTo(2);
908 InfoJob restoredJob = jobs.getJob("jobId1");
909 assertThat(restoredJob.getId()).isEqualTo("jobId1");
910 assertThat(restoredJob.getLastUpdated()).isEqualTo(savedJob.getLastUpdated());
912 jobs.remove("jobId1", this.infoProducers);
913 jobs.remove("jobId2", this.infoProducers);
916 // Restore the jobs, no jobs in database
917 InfoJobs jobs = new InfoJobs(this.applicationConfig, this.producerCallbacks);
918 jobs.restoreJobsFromDatabase();
919 assertThat(jobs.size()).isZero();
921 logger.warn("Test removing a job when the db file is gone");
922 this.infoJobs.remove("jobId1", this.infoProducers);
923 assertThat(this.infoJobs.size()).isEqualTo(1);
925 ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
926 await().untilAsserted(() -> assertThat(simulatorResults.jobsStopped).hasSize(3));
930 void testEiTypesDatabase() throws Exception {
931 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
933 assertThat(this.infoTypes.size()).isEqualTo(1);
937 InfoTypes types = new InfoTypes(this.applicationConfig);
938 types.restoreTypesFromDatabase();
939 assertThat(types.size()).isEqualTo(1);
942 // Restore the jobs, no jobs in database
943 InfoTypes types = new InfoTypes(this.applicationConfig);
945 types.restoreTypesFromDatabase();
946 assertThat(types.size()).isZero();
948 logger.warn("Test removing a job when the db file is gone");
949 this.infoTypes.remove(this.infoTypes.getType(TYPE_ID));
950 assertThat(this.infoJobs.size()).isZero();
954 void testConsumerTypeSubscriptionDatabase() {
955 final String callbackUrl = baseUrl() + ConsumerSimulatorController.getTypeStatusCallbackUrl();
956 final ConsumerTypeSubscriptionInfo info = new ConsumerTypeSubscriptionInfo(callbackUrl, "owner");
958 // PUT a subscription
959 String body = gson.toJson(info);
960 restClient().putForEntity(typeSubscriptionUrl() + "/subscriptionId", body).block();
961 assertThat(this.infoTypeSubscriptions.size()).isEqualTo(1);
963 InfoTypeSubscriptions restoredSubscriptions = new InfoTypeSubscriptions(this.applicationConfig);
964 assertThat(restoredSubscriptions.size()).isEqualTo(1);
965 assertThat(restoredSubscriptions.getSubscriptionsForOwner("owner")).hasSize(1);
967 // Delete the subscription
968 restClient().deleteForEntity(typeSubscriptionUrl() + "/subscriptionId").block();
969 restoredSubscriptions = new InfoTypeSubscriptions(this.applicationConfig);
970 assertThat(restoredSubscriptions.size()).isZero();
974 void testConsumerTypeSubscription() throws Exception {
976 final String callbackUrl = baseUrl() + ConsumerSimulatorController.getTypeStatusCallbackUrl();
977 final ConsumerTypeSubscriptionInfo info = new ConsumerTypeSubscriptionInfo(callbackUrl, "owner");
979 testErrorCode(restClient().get(typeSubscriptionUrl() + "/junk"), HttpStatus.NOT_FOUND,
980 "Could not find Information subscription: junk");
982 testErrorCode(restClient().delete(typeSubscriptionUrl() + "/junk"), HttpStatus.NOT_FOUND,
983 "Could not find Information subscription: junk");
986 // PUT a subscription
987 String body = gson.toJson(info);
988 ResponseEntity<String> resp =
989 restClient().putForEntity(typeSubscriptionUrl() + "/subscriptionId", body).block();
990 assertThat(this.infoTypeSubscriptions.size()).isEqualTo(1);
991 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.CREATED);
992 resp = restClient().putForEntity(typeSubscriptionUrl() + "/subscriptionId", body).block();
993 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
997 ResponseEntity<String> resp = restClient().getForEntity(typeSubscriptionUrl()).block();
998 assertThat(resp.getBody()).isEqualTo("[\"subscriptionId\"]");
999 resp = restClient().getForEntity(typeSubscriptionUrl() + "?owner=owner").block();
1000 assertThat(resp.getBody()).isEqualTo("[\"subscriptionId\"]");
1001 resp = restClient().getForEntity(typeSubscriptionUrl() + "?owner=junk").block();
1002 assertThat(resp.getBody()).isEqualTo("[]");
1006 // GET the individual subscription
1007 ResponseEntity<String> resp = restClient().getForEntity(typeSubscriptionUrl() + "/subscriptionId").block();
1008 ConsumerTypeSubscriptionInfo respInfo = gson.fromJson(resp.getBody(), ConsumerTypeSubscriptionInfo.class);
1009 assertThat(respInfo).isEqualTo(info);
1013 // Test the callbacks
1014 final ConsumerSimulatorController.TestResults consumerCalls = this.consumerSimulator.getTestResults();
1016 // Test callback for PUT type
1017 this.putInfoType(TYPE_ID);
1018 await().untilAsserted(() -> assertThat(consumerCalls.typeRegistrationInfoCallbacks).hasSize(1));
1019 assertThat(consumerCalls.typeRegistrationInfoCallbacks.get(0).state)
1020 .isEqualTo(ConsumerTypeRegistrationInfo.ConsumerTypeStatusValues.REGISTERED);
1022 // Test callback for DELETE type
1023 this.deleteInfoType(TYPE_ID);
1024 await().untilAsserted(() -> assertThat(consumerCalls.typeRegistrationInfoCallbacks).hasSize(2));
1025 assertThat(consumerCalls.typeRegistrationInfoCallbacks.get(1).state)
1026 .isEqualTo(ConsumerTypeRegistrationInfo.ConsumerTypeStatusValues.DEREGISTERED);
1030 // DELETE the subscription
1031 ResponseEntity<String> resp =
1032 restClient().deleteForEntity(typeSubscriptionUrl() + "/subscriptionId").block();
1033 assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.NO_CONTENT);
1034 assertThat(this.infoTypeSubscriptions.size()).isZero();
1035 resp = restClient().getForEntity(typeSubscriptionUrl()).block();
1036 assertThat(resp.getBody()).isEqualTo("[]");
1041 void testRemovingNonWorkingSubscription() throws Exception {
1042 // Test that subscriptions are removed for a unresponsive consumer
1044 // PUT a subscription with a junk callback
1045 final ConsumerTypeSubscriptionInfo info = new ConsumerTypeSubscriptionInfo(baseUrl() + "/JUNK", "owner");
1046 String body = gson.toJson(info);
1047 restClient().putForEntity(typeSubscriptionUrl() + "/subscriptionId", body).block();
1048 assertThat(this.infoTypeSubscriptions.size()).isEqualTo(1);
1050 this.putInfoType(TYPE_ID);
1051 // The callback will fail and the subscription will be removed
1052 await().untilAsserted(() -> assertThat(this.infoTypeSubscriptions.size()).isZero());
1056 void testTypeSubscriptionErrorCodes() throws Exception {
1058 testErrorCode(restClient().get(typeSubscriptionUrl() + "/junk"), HttpStatus.NOT_FOUND,
1059 "Could not find Information subscription: junk");
1061 testErrorCode(restClient().delete(typeSubscriptionUrl() + "/junk"), HttpStatus.NOT_FOUND,
1062 "Could not find Information subscription: junk");
1066 void testAuthHeader() throws Exception {
1067 final String AUTH_TOKEN = "testToken";
1068 Path authFile = Files.createTempFile("icsTestAuthToken", ".txt");
1069 Files.write(authFile, AUTH_TOKEN.getBytes());
1070 this.securityContext.setAuthTokenFilePath(authFile);
1071 putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
1072 putInfoJob(TYPE_ID, "jobId");
1074 // Test that authorization header is sent to the producer.
1075 await().untilAsserted(() -> assertThat(this.producerSimulator.getTestResults().receivedHeaders).hasSize(1));
1076 Map<String, String> headers = this.producerSimulator.getTestResults().receivedHeaders.get(0);
1077 assertThat(headers).containsEntry("authorization", "Bearer " + AUTH_TOKEN);
1079 Files.delete(authFile);
1081 // Test that it works. The cached header is used
1082 putInfoJob(TYPE_ID, "jobId2");
1083 await().untilAsserted(() -> assertThat(this.infoJobs.size()).isEqualByComparingTo(2));
1084 headers = this.producerSimulator.getTestResults().receivedHeaders.get(1);
1085 assertThat(headers).containsEntry("authorization", "Bearer " + AUTH_TOKEN);
1089 private String typeSubscriptionUrl() {
1090 return ConsumerConsts.API_ROOT + "/info-type-subscription";
1093 private void deleteInfoProducer(String infoProducerId) {
1094 String url = ProducerConsts.API_ROOT + "/info-producers/" + infoProducerId;
1095 restClient().deleteForEntity(url).block();
1098 private void verifyJobStatus(String jobId, String expStatus) {
1099 String url = A1eConsts.API_ROOT + "/eijobs/" + jobId + "/status";
1100 String rsp = restClient().get(url).block();
1101 assertThat(rsp).contains(expStatus);
1104 private void assertProducerOpState(String producerId,
1105 ProducerStatusInfo.OperationalState expectedOperationalState) {
1106 String statusUrl = ProducerConsts.API_ROOT + "/info-producers/" + producerId + "/status";
1107 ResponseEntity<String> resp = restClient().getForEntity(statusUrl).block();
1108 ProducerStatusInfo statusInfo = gson.fromJson(resp.getBody(), ProducerStatusInfo.class);
1109 assertThat(statusInfo.opState).isEqualTo(expectedOperationalState);
1112 ProducerInfoTypeInfo ProducerInfoTypeRegistrationInfo(String typeId)
1113 throws JsonMappingException, JsonProcessingException {
1114 return new ProducerInfoTypeInfo(jsonSchemaObject(), typeSpecifcInfoObject());
1117 ProducerRegistrationInfo producerEiRegistratioInfoRejecting(String typeId)
1118 throws JsonMappingException, JsonProcessingException {
1119 return new ProducerRegistrationInfo(Arrays.asList(typeId), //
1120 baseUrl() + ProducerSimulatorController.JOB_ERROR_URL,
1121 baseUrl() + ProducerSimulatorController.SUPERVISION_ERROR_URL);
1124 ProducerRegistrationInfo producerInfoRegistratioInfo(String typeId)
1125 throws JsonMappingException, JsonProcessingException {
1126 return new ProducerRegistrationInfo(Arrays.asList(typeId), //
1127 baseUrl() + ProducerSimulatorController.JOB_URL, baseUrl() + ProducerSimulatorController.SUPERVISION_URL);
1130 private ConsumerJobInfo consumerJobInfo() throws JsonMappingException, JsonProcessingException {
1131 return consumerJobInfo(TYPE_ID, EI_JOB_ID);
1134 ConsumerJobInfo consumerJobInfo(String typeId, String infoJobId)
1135 throws JsonMappingException, JsonProcessingException {
1136 return new ConsumerJobInfo(typeId, jsonObject(), "owner", "https://junk.com",
1137 baseUrl() + A1eCallbacksSimulatorController.getJobStatusUrl(infoJobId));
1140 private A1eEiJobInfo infoJobInfo() throws JsonMappingException, JsonProcessingException {
1141 return infoJobInfo(TYPE_ID, EI_JOB_ID);
1144 A1eEiJobInfo infoJobInfo(String typeId, String infoJobId) throws JsonMappingException, JsonProcessingException {
1145 return new A1eEiJobInfo(typeId, jsonObject(), "owner", "https://junk.com",
1146 baseUrl() + A1eCallbacksSimulatorController.getJobStatusUrl(infoJobId));
1149 private Object jsonObject(String json) {
1151 return JsonParser.parseString(json).getAsJsonObject();
1152 } catch (Exception e) {
1153 throw new NullPointerException(e.toString());
1157 private Object typeSpecifcInfoObject() {
1158 return jsonObject("{ \"propertyName\" : \"value\" }");
1161 private Object jsonSchemaObject() {
1162 // a json schema with one mandatory property named "string"
1163 String schemaStr = "{" //
1164 + "\"$schema\": \"http://json-schema.org/draft-04/schema#\"," //
1165 + "\"type\": \"object\"," //
1166 + "\"properties\": {" //
1167 + EI_JOB_PROPERTY + " : {" //
1168 + " \"type\": \"string\"" //
1171 + "\"required\": [" //
1172 + EI_JOB_PROPERTY //
1175 return jsonObject(schemaStr);
1178 private Object jsonObject() {
1179 return jsonObject("{ " + EI_JOB_PROPERTY + " : \"value\" }");
1182 private InfoJob putInfoJob(String infoTypeId, String jobId)
1183 throws JsonMappingException, JsonProcessingException, ServiceException {
1185 String url = A1eConsts.API_ROOT + "/eijobs/" + jobId;
1186 String body = gson.toJson(infoJobInfo(infoTypeId, jobId));
1187 restClient().putForEntity(url, body).block();
1189 return this.infoJobs.getJob(jobId);
1192 private HttpStatus putInfoType(String infoTypeId)
1193 throws JsonMappingException, JsonProcessingException, ServiceException {
1194 String url = ProducerConsts.API_ROOT + "/info-types/" + infoTypeId;
1195 String body = gson.toJson(ProducerInfoTypeRegistrationInfo(infoTypeId));
1197 ResponseEntity<String> resp = restClient().putForEntity(url, body).block();
1198 this.infoTypes.getType(infoTypeId);
1199 return resp.getStatusCode();
1202 private String deleteInfoTypeUrl(String typeId) {
1203 return ProducerConsts.API_ROOT + "/info-types/" + typeId;
1206 private void deleteInfoType(String typeId) {
1207 restClient().delete(deleteInfoTypeUrl(typeId)).block();
1210 private InfoType putInfoProducerWithOneTypeRejecting(String producerId, String infoTypeId)
1211 throws JsonMappingException, JsonProcessingException, ServiceException {
1212 this.putInfoType(infoTypeId);
1213 String url = ProducerConsts.API_ROOT + "/info-producers/" + producerId;
1214 String body = gson.toJson(producerEiRegistratioInfoRejecting(infoTypeId));
1215 restClient().putForEntity(url, body).block();
1216 return this.infoTypes.getType(infoTypeId);
1219 private InfoType putInfoProducerWithOneType(String producerId, String infoTypeId)
1220 throws JsonMappingException, JsonProcessingException, ServiceException {
1221 this.putInfoType(infoTypeId);
1223 String url = ProducerConsts.API_ROOT + "/info-producers/" + producerId;
1224 String body = gson.toJson(producerInfoRegistratioInfo(infoTypeId));
1226 restClient().putForEntity(url, body).block();
1228 return this.infoTypes.getType(infoTypeId);
1231 private String baseUrl() {
1232 return "https://localhost:" + this.port;
1235 private AsyncRestClient restClient(boolean useTrustValidation) {
1236 WebClientConfig config = this.applicationConfig.getWebClientConfig();
1237 HttpProxyConfig httpProxyConfig = ImmutableHttpProxyConfig.builder() //
1238 .httpProxyHost("") //
1239 .httpProxyPort(0) //
1241 config = ImmutableWebClientConfig.builder() //
1242 .keyStoreType(config.keyStoreType()) //
1243 .keyStorePassword(config.keyStorePassword()) //
1244 .keyStore(config.keyStore()) //
1245 .keyPassword(config.keyPassword()) //
1246 .isTrustStoreUsed(useTrustValidation) //
1247 .trustStore(config.trustStore()) //
1248 .trustStorePassword(config.trustStorePassword()) //
1249 .httpProxyConfig(httpProxyConfig).build();
1251 AsyncRestClientFactory restClientFactory = new AsyncRestClientFactory(config, securityContext);
1252 return restClientFactory.createRestClientNoHttpProxy(baseUrl());
1255 private AsyncRestClient restClient() {
1256 return restClient(false);
1259 private void testErrorCode(Mono<?> request, HttpStatus expStatus, String responseContains) {
1260 testErrorCode(request, expStatus, responseContains, true);
1263 private void testErrorCode(Mono<?> request, HttpStatus expStatus, String responseContains,
1264 boolean expectApplicationProblemJsonMediaType) {
1265 StepVerifier.create(request) //
1266 .expectSubscription() //
1267 .expectErrorMatches(
1268 t -> checkWebClientError(t, expStatus, responseContains, expectApplicationProblemJsonMediaType)) //
1272 private boolean checkWebClientError(Throwable throwable, HttpStatus expStatus, String responseContains,
1273 boolean expectApplicationProblemJsonMediaType) {
1274 assertTrue(throwable instanceof WebClientResponseException);
1275 WebClientResponseException responseException = (WebClientResponseException) throwable;
1276 assertThat(responseException.getStatusCode()).isEqualTo(expStatus);
1277 assertThat(responseException.getResponseBodyAsString()).contains(responseContains);
1278 if (expectApplicationProblemJsonMediaType) {
1279 assertThat(responseException.getHeaders().getContentType()).isEqualTo(MediaType.APPLICATION_PROBLEM_JSON);