Fix Sonar issues
[nonrtric.git] / enrichment-coordinator-service / src / test / java / org / oransc / enrichment / ApplicationTest.java
1 /*-
2  * ========================LICENSE_START=================================
3  * O-RAN-SC
4  * %%
5  * Copyright (C) 2020 Nordix Foundation
6  * %%
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
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
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===================================
19  */
20
21 package org.oransc.enrichment;
22
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;
26
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;
32
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;
38
39 import org.json.JSONObject;
40 import org.junit.jupiter.api.AfterEach;
41 import org.junit.jupiter.api.BeforeEach;
42 import org.junit.jupiter.api.Test;
43 import org.junit.jupiter.api.extension.ExtendWith;
44 import org.oransc.enrichment.clients.AsyncRestClient;
45 import org.oransc.enrichment.clients.AsyncRestClientFactory;
46 import org.oransc.enrichment.configuration.ApplicationConfig;
47 import org.oransc.enrichment.configuration.ImmutableHttpProxyConfig;
48 import org.oransc.enrichment.configuration.ImmutableWebClientConfig;
49 import org.oransc.enrichment.configuration.WebClientConfig;
50 import org.oransc.enrichment.configuration.WebClientConfig.HttpProxyConfig;
51 import org.oransc.enrichment.controller.ConsumerSimulatorController;
52 import org.oransc.enrichment.controller.ProducerSimulatorController;
53 import org.oransc.enrichment.controllers.a1e.A1eConsts;
54 import org.oransc.enrichment.controllers.a1e.A1eEiJobInfo;
55 import org.oransc.enrichment.controllers.a1e.A1eEiJobStatus;
56 import org.oransc.enrichment.controllers.a1e.A1eEiTypeInfo;
57 import org.oransc.enrichment.controllers.r1consumer.ConsumerConsts;
58 import org.oransc.enrichment.controllers.r1consumer.ConsumerInfoTypeInfo;
59 import org.oransc.enrichment.controllers.r1consumer.ConsumerJobInfo;
60 import org.oransc.enrichment.controllers.r1producer.ProducerCallbacks;
61 import org.oransc.enrichment.controllers.r1producer.ProducerConsts;
62 import org.oransc.enrichment.controllers.r1producer.ProducerInfoTypeInfo;
63 import org.oransc.enrichment.controllers.r1producer.ProducerJobInfo;
64 import org.oransc.enrichment.controllers.r1producer.ProducerRegistrationInfo;
65 import org.oransc.enrichment.controllers.r1producer.ProducerStatusInfo;
66 import org.oransc.enrichment.exceptions.ServiceException;
67 import org.oransc.enrichment.repository.InfoJob;
68 import org.oransc.enrichment.repository.InfoJobs;
69 import org.oransc.enrichment.repository.InfoProducer;
70 import org.oransc.enrichment.repository.InfoProducers;
71 import org.oransc.enrichment.repository.InfoType;
72 import org.oransc.enrichment.repository.InfoTypes;
73 import org.oransc.enrichment.tasks.ProducerSupervision;
74 import org.slf4j.Logger;
75 import org.slf4j.LoggerFactory;
76 import org.springframework.beans.factory.annotation.Autowired;
77 import org.springframework.boot.test.context.SpringBootTest;
78 import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
79 import org.springframework.boot.test.context.TestConfiguration;
80 import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
81 import org.springframework.boot.web.server.LocalServerPort;
82 import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
83 import org.springframework.context.ApplicationContext;
84 import org.springframework.context.annotation.Bean;
85 import org.springframework.http.HttpStatus;
86 import org.springframework.http.MediaType;
87 import org.springframework.http.ResponseEntity;
88 import org.springframework.test.context.TestPropertySource;
89 import org.springframework.test.context.junit.jupiter.SpringExtension;
90 import org.springframework.web.reactive.function.client.WebClientResponseException;
91
92 import reactor.core.publisher.Mono;
93 import reactor.test.StepVerifier;
94
95 @ExtendWith(SpringExtension.class)
96 @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
97 @TestPropertySource(
98     properties = { //
99         "server.ssl.key-store=./config/keystore.jks", //
100         "app.webclient.trust-store=./config/truststore.jks", //
101         "app.vardata-directory=./target"})
102 class ApplicationTest {
103     private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
104
105     private final String TYPE_ID = "typeId";
106     private final String PRODUCER_ID = "producerId";
107     private final String EI_JOB_PROPERTY = "\"property1\"";
108     private final String EI_JOB_ID = "jobId";
109
110     @Autowired
111     ApplicationContext context;
112
113     @Autowired
114     InfoJobs infoJobs;
115
116     @Autowired
117     InfoTypes infoTypes;
118
119     @Autowired
120     InfoProducers infoProducers;
121
122     @Autowired
123     ApplicationConfig applicationConfig;
124
125     @Autowired
126     ProducerSimulatorController producerSimulator;
127
128     @Autowired
129     ConsumerSimulatorController consumerSimulator;
130
131     @Autowired
132     ProducerSupervision producerSupervision;
133
134     @Autowired
135     ProducerCallbacks producerCallbacks;
136
137     private static Gson gson = new GsonBuilder().create();
138
139     /**
140      * Overrides the BeanFactory.
141      */
142     @TestConfiguration
143     static class TestBeanFactory {
144         @Bean
145         public ServletWebServerFactory servletContainer() {
146             return new TomcatServletWebServerFactory();
147         }
148     }
149
150     @LocalServerPort
151     private int port;
152
153     @BeforeEach
154     void reset() {
155         this.infoJobs.clear();
156         this.infoTypes.clear();
157         this.infoProducers.clear();
158         this.producerSimulator.getTestResults().reset();
159         this.consumerSimulator.getTestResults().reset();
160     }
161
162     @AfterEach
163     void check() {
164         assertThat(this.producerSimulator.getTestResults().errorFound).isFalse();
165     }
166
167     @Test
168     void generateApiDoc() throws FileNotFoundException {
169         String url = "/v3/api-docs";
170         ResponseEntity<String> resp = restClient().getForEntity(url).block();
171         assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
172
173         JSONObject jsonObj = new JSONObject(resp.getBody());
174         assertThat(jsonObj.remove("servers")).isNotNull();
175
176         String indented = jsonObj.toString(4);
177         try (PrintStream out = new PrintStream(new FileOutputStream("api/ecs-api.json"))) {
178             out.print(indented);
179         }
180     }
181
182     @Test
183     void a1eGetEiTypes() throws Exception {
184         putInfoProducerWithOneType(PRODUCER_ID, "test");
185         String url = A1eConsts.API_ROOT + "/eitypes";
186         String rsp = restClient().get(url).block();
187         assertThat(rsp).isEqualTo("[\"test\"]");
188     }
189
190     @Test
191     void consumerGetInfoTypes() throws Exception {
192         putInfoProducerWithOneType(PRODUCER_ID, "test");
193         String url = ConsumerConsts.API_ROOT + "/info-types";
194         String rsp = restClient().get(url).block();
195         assertThat(rsp).isEqualTo("[\"test\"]");
196     }
197
198     @Test
199     void a1eGetEiTypesEmpty() throws Exception {
200         String url = A1eConsts.API_ROOT + "/eitypes";
201         String rsp = restClient().get(url).block();
202         assertThat(rsp).isEqualTo("[]");
203     }
204
205     @Test
206     void consumerGetEiTypesEmpty() throws Exception {
207         String url = ConsumerConsts.API_ROOT + "/info-types";
208         String rsp = restClient().get(url).block();
209         assertThat(rsp).isEqualTo("[]");
210     }
211
212     @Test
213     void a1eGetEiType() throws Exception {
214         putInfoProducerWithOneType(PRODUCER_ID, "test");
215         String url = A1eConsts.API_ROOT + "/eitypes/test";
216         String rsp = restClient().get(url).block();
217         A1eEiTypeInfo info = gson.fromJson(rsp, A1eEiTypeInfo.class);
218         assertThat(info).isNotNull();
219     }
220
221     @Test
222     void consumerGetEiType() throws Exception {
223         putInfoProducerWithOneType(PRODUCER_ID, "test");
224         String url = ConsumerConsts.API_ROOT + "/info-types/test";
225         String rsp = restClient().get(url).block();
226         ConsumerInfoTypeInfo info = gson.fromJson(rsp, ConsumerInfoTypeInfo.class);
227         assertThat(info).isNotNull();
228         assertThat(info.jobDataSchema).isNotNull();
229     }
230
231     @Test
232     void a1eGetEiTypeNotFound() throws Exception {
233         String url = A1eConsts.API_ROOT + "/eitypes/junk";
234         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND, "Information type not found: junk");
235     }
236
237     @Test
238     void consumerGetEiTypeNotFound() throws Exception {
239         String url = ConsumerConsts.API_ROOT + "/info-types/junk";
240         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND, "Information type not found: junk");
241     }
242
243     @Test
244     void a1eGetEiJobsIds() throws Exception {
245         putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
246         putEiJob(TYPE_ID, "jobId");
247         final String JOB_ID_JSON = "[\"jobId\"]";
248         String url = A1eConsts.API_ROOT + "/eijobs?infoTypeId=typeId";
249         String rsp = restClient().get(url).block();
250         assertThat(rsp).isEqualTo(JOB_ID_JSON);
251
252         url = A1eConsts.API_ROOT + "/eijobs?owner=owner";
253         rsp = restClient().get(url).block();
254         assertThat(rsp).isEqualTo(JOB_ID_JSON);
255
256         url = A1eConsts.API_ROOT + "/eijobs?owner=JUNK";
257         rsp = restClient().get(url).block();
258         assertThat(rsp).isEqualTo("[]");
259
260         url = A1eConsts.API_ROOT + "/eijobs";
261         rsp = restClient().get(url).block();
262         assertThat(rsp).isEqualTo(JOB_ID_JSON);
263
264         url = A1eConsts.API_ROOT + "/eijobs?eiTypeId=typeId&&owner=owner";
265         rsp = restClient().get(url).block();
266         assertThat(rsp).isEqualTo(JOB_ID_JSON);
267
268         url = A1eConsts.API_ROOT + "/eijobs?eiTypeId=JUNK";
269         rsp = restClient().get(url).block();
270         assertThat(rsp).isEqualTo("[]");
271     }
272
273     @Test
274     void consumerGetInformationJobsIds() throws Exception {
275         putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
276         putEiJob(TYPE_ID, "jobId");
277         final String JOB_ID_JSON = "[\"jobId\"]";
278         String url = ConsumerConsts.API_ROOT + "/info-jobs?infoTypeId=typeId";
279         String rsp = restClient().get(url).block();
280         assertThat(rsp).isEqualTo(JOB_ID_JSON);
281
282         url = ConsumerConsts.API_ROOT + "/info-jobs?owner=owner";
283         rsp = restClient().get(url).block();
284         assertThat(rsp).isEqualTo(JOB_ID_JSON);
285
286         url = ConsumerConsts.API_ROOT + "/info-jobs?owner=JUNK";
287         rsp = restClient().get(url).block();
288         assertThat(rsp).isEqualTo("[]");
289
290         url = ConsumerConsts.API_ROOT + "/info-jobs";
291         rsp = restClient().get(url).block();
292         assertThat(rsp).isEqualTo(JOB_ID_JSON);
293
294         url = ConsumerConsts.API_ROOT + "/info-jobs?infoTypeId=typeId&&owner=owner";
295         rsp = restClient().get(url).block();
296         assertThat(rsp).isEqualTo(JOB_ID_JSON);
297
298         url = ConsumerConsts.API_ROOT + "/info-jobs?infoTypeId=JUNK";
299         rsp = restClient().get(url).block();
300         assertThat(rsp).isEqualTo("[]");
301     }
302
303     @Test
304     void a1eGetEiJob() throws Exception {
305         putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
306         putEiJob(TYPE_ID, "jobId");
307         String url = A1eConsts.API_ROOT + "/eijobs/jobId";
308         String rsp = restClient().get(url).block();
309         A1eEiJobInfo info = gson.fromJson(rsp, A1eEiJobInfo.class);
310         assertThat(info.owner).isEqualTo("owner");
311         assertThat(info.eiTypeId).isEqualTo(TYPE_ID);
312     }
313
314     @Test
315     void consumerGetEiJob() throws Exception {
316         putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
317         putEiJob(TYPE_ID, "jobId");
318         String url = ConsumerConsts.API_ROOT + "/info-jobs/jobId";
319         String rsp = restClient().get(url).block();
320         ConsumerJobInfo info = gson.fromJson(rsp, ConsumerJobInfo.class);
321         assertThat(info.owner).isEqualTo("owner");
322         assertThat(info.infoTypeId).isEqualTo(TYPE_ID);
323     }
324
325     @Test
326     void a1eGetEiJobNotFound() throws Exception {
327         putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
328         String url = A1eConsts.API_ROOT + "/eijobs/junk";
329         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND, "Could not find Information job: junk");
330     }
331
332     @Test
333     void consumerGetInfoJobNotFound() throws Exception {
334         putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
335         String url = ConsumerConsts.API_ROOT + "/info-jobs/junk";
336         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND, "Could not find Information job: junk");
337     }
338
339     @Test
340     void a1eGetEiJobStatus() throws Exception {
341         putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
342         putEiJob(TYPE_ID, "jobId");
343
344         verifyJobStatus("jobId", "ENABLED");
345     }
346
347     @Test
348     void consumerGetEiJobStatus() throws Exception {
349         putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
350         putEiJob(TYPE_ID, "jobId");
351
352         String url = ConsumerConsts.API_ROOT + "/info-jobs/jobId/status";
353         String rsp = restClient().get(url).block();
354         assertThat(rsp).contains("ENABLED");
355     }
356
357     @Test
358     void a1eDeleteEiJob() throws Exception {
359         putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
360         putEiJob(TYPE_ID, "jobId");
361         assertThat(this.infoJobs.size()).isEqualTo(1);
362         String url = A1eConsts.API_ROOT + "/eijobs/jobId";
363         restClient().delete(url).block();
364         assertThat(this.infoJobs.size()).isZero();
365
366         ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
367         await().untilAsserted(() -> assertThat(simulatorResults.jobsStopped.size()).isEqualTo(1));
368         assertThat(simulatorResults.jobsStopped.get(0)).isEqualTo("jobId");
369     }
370
371     @Test
372     void consumerDeleteEiJob() throws Exception {
373         putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
374         putEiJob(TYPE_ID, "jobId");
375         assertThat(this.infoJobs.size()).isEqualTo(1);
376         String url = ConsumerConsts.API_ROOT + "/info-jobs/jobId";
377         restClient().delete(url).block();
378         assertThat(this.infoJobs.size()).isZero();
379
380         ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
381         await().untilAsserted(() -> assertThat(simulatorResults.jobsStopped.size()).isEqualTo(1));
382         assertThat(simulatorResults.jobsStopped.get(0)).isEqualTo("jobId");
383     }
384
385     @Test
386     void a1eDeleteEiJobNotFound() throws Exception {
387         putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
388         String url = A1eConsts.API_ROOT + "/eijobs/junk";
389         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND, "Could not find Information job: junk");
390     }
391
392     @Test
393     void consumerDeleteEiJobNotFound() throws Exception {
394         putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
395         String url = ConsumerConsts.API_ROOT + "/info-jobs/junk";
396         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND, "Could not find Information job: junk");
397     }
398
399     @Test
400     void a1ePutEiJob() throws Exception {
401         // Test that one producer accepting a job is enough
402         putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
403         putEiProducerWithOneTypeRejecting("simulateProducerError", TYPE_ID);
404
405         String url = A1eConsts.API_ROOT + "/eijobs/jobId";
406         String body = gson.toJson(infoJobInfo());
407         ResponseEntity<String> resp = restClient().putForEntity(url, body).block();
408         assertThat(this.infoJobs.size()).isEqualTo(1);
409         assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.CREATED);
410
411         ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
412         await().untilAsserted(() -> assertThat(simulatorResults.jobsStarted.size()).isEqualTo(1));
413         ProducerJobInfo request = simulatorResults.jobsStarted.get(0);
414         assertThat(request.id).isEqualTo("jobId");
415
416         // One retry --> two calls
417         await().untilAsserted(() -> assertThat(simulatorResults.noOfRejectedCreate).isEqualTo(2));
418         assertThat(simulatorResults.noOfRejectedCreate).isEqualTo(2);
419
420         resp = restClient().putForEntity(url, body).block();
421         assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
422         InfoJob job = this.infoJobs.getJob("jobId");
423         assertThat(job.getOwner()).isEqualTo("owner");
424
425         verifyJobStatus(EI_JOB_ID, "ENABLED");
426     }
427
428     @Test
429     void consumerPutInformationJob() throws Exception {
430         // Test that one producer accepting a job is enough
431         putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
432
433         String url = ConsumerConsts.API_ROOT + "/info-jobs/jobId";
434         String body = gson.toJson(consumerJobInfo());
435         ResponseEntity<String> resp = restClient().putForEntity(url, body).block();
436         assertThat(this.infoJobs.size()).isEqualTo(1);
437         assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.CREATED);
438
439         ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
440         await().untilAsserted(() -> assertThat(simulatorResults.jobsStarted.size()).isEqualTo(1));
441         ProducerJobInfo request = simulatorResults.jobsStarted.get(0);
442         assertThat(request.id).isEqualTo("jobId");
443
444         resp = restClient().putForEntity(url, body).block();
445         assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
446         InfoJob job = this.infoJobs.getJob("jobId");
447         assertThat(job.getOwner()).isEqualTo("owner");
448
449         verifyJobStatus(EI_JOB_ID, "ENABLED");
450     }
451
452     @Test
453     void consumerPutInformationJob_noType() throws JsonMappingException, JsonProcessingException, ServiceException {
454         String url = ConsumerConsts.API_ROOT + "/info-jobs/jobId?typeCheck=false";
455         String body = gson.toJson(consumerJobInfo());
456         ResponseEntity<String> resp = restClient().putForEntity(url, body).block();
457         assertThat(this.infoJobs.size()).isEqualTo(1);
458         assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.CREATED);
459         verifyJobStatus(EI_JOB_ID, "DISABLED");
460
461         putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
462
463         verifyJobStatus(EI_JOB_ID, "ENABLED");
464
465     }
466
467     @Test
468     void a1ePutEiJob_jsonSchemavalidationError() throws Exception {
469         putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
470
471         String url = A1eConsts.API_ROOT + "/eijobs/jobId";
472         // The element with name "property1" is mandatory in the schema
473         A1eEiJobInfo jobInfo = new A1eEiJobInfo("typeId", jsonObject("{ \"XXstring\" : \"value\" }"), "owner",
474             "targetUri", "jobStatusUrl");
475         String body = gson.toJson(jobInfo);
476
477         testErrorCode(restClient().put(url, body), HttpStatus.CONFLICT, "Json validation failure");
478     }
479
480     @Test
481     void consumerPutJob_jsonSchemavalidationError() throws Exception {
482         putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
483
484         String url = ConsumerConsts.API_ROOT + "/info-jobs/jobId?typeCheck=true";
485         // The element with name "property1" is mandatory in the schema
486         ConsumerJobInfo jobInfo =
487             new ConsumerJobInfo("typeId", jsonObject("{ \"XXstring\" : \"value\" }"), "owner", "targetUri", null);
488         String body = gson.toJson(jobInfo);
489
490         testErrorCode(restClient().put(url, body), HttpStatus.CONFLICT, "Json validation failure");
491     }
492
493     @Test
494     void consumerPutJob_uriError() throws Exception {
495         putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
496
497         String url = ConsumerConsts.API_ROOT + "/info-jobs/jobId?typeCheck=true";
498
499         ConsumerJobInfo jobInfo = new ConsumerJobInfo(TYPE_ID, jsonObject(), "owner", "junk", null);
500         String body = gson.toJson(jobInfo);
501
502         testErrorCode(restClient().put(url, body), HttpStatus.CONFLICT, "URI: junk is not absolute");
503     }
504
505     @Test
506     void a1eChangingEiTypeGetRejected() throws Exception {
507         putInfoProducerWithOneType("producer1", "typeId1");
508         putInfoProducerWithOneType("producer2", "typeId2");
509         putEiJob("typeId1", "jobId");
510
511         String url = A1eConsts.API_ROOT + "/eijobs/jobId";
512         String body = gson.toJson(infoJobInfo("typeId2", "jobId"));
513         testErrorCode(restClient().put(url, body), HttpStatus.CONFLICT,
514             "Not allowed to change type for existing EI job");
515     }
516
517     @Test
518     void consumerChangingInfoTypeGetRejected() throws Exception {
519         putInfoProducerWithOneType("producer1", "typeId1");
520         putInfoProducerWithOneType("producer2", "typeId2");
521         putEiJob("typeId1", "jobId");
522
523         String url = ConsumerConsts.API_ROOT + "/info-jobs/jobId";
524         String body = gson.toJson(consumerJobInfo("typeId2", "jobId"));
525         testErrorCode(restClient().put(url, body), HttpStatus.CONFLICT, "Not allowed to change type for existing job");
526     }
527
528     @Test
529     void producerPutEiType() throws JsonMappingException, JsonProcessingException, ServiceException {
530         assertThat(putInfoType(TYPE_ID)).isEqualTo(HttpStatus.CREATED);
531         assertThat(putInfoType(TYPE_ID)).isEqualTo(HttpStatus.OK);
532     }
533
534     @Test
535     void producerPutEiType_noSchema() {
536         String url = ProducerConsts.API_ROOT + "/info-types/" + TYPE_ID;
537         String body = "{}";
538         testErrorCode(restClient().put(url, body), HttpStatus.BAD_REQUEST, "No schema provided");
539     }
540
541     @Test
542     void producerDeleteEiType() throws Exception {
543         putInfoType(TYPE_ID);
544         String url = ProducerConsts.API_ROOT + "/info-types/" + TYPE_ID;
545         restClient().delete(url).block();
546         assertThat(this.infoTypes.size()).isZero();
547
548         testErrorCode(restClient().delete(url), HttpStatus.NOT_FOUND, "Information type not found");
549     }
550
551     @Test
552     void producerDeleteEiTypeExistingProducer() throws Exception {
553         putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
554         String url = ProducerConsts.API_ROOT + "/info-types/" + TYPE_ID;
555         testErrorCode(restClient().delete(url), HttpStatus.NOT_ACCEPTABLE,
556             "The type has active producers: " + PRODUCER_ID);
557         assertThat(this.infoTypes.size()).isEqualTo(1);
558     }
559
560     @Test
561     void producerPutProducerWithOneType_rejecting()
562         throws JsonMappingException, JsonProcessingException, ServiceException {
563         putEiProducerWithOneTypeRejecting("simulateProducerError", TYPE_ID);
564         String url = A1eConsts.API_ROOT + "/eijobs/" + EI_JOB_ID;
565         String body = gson.toJson(infoJobInfo());
566         restClient().put(url, body).block();
567
568         ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
569         // There is one retry -> 2 calls
570         await().untilAsserted(() -> assertThat(simulatorResults.noOfRejectedCreate).isEqualTo(2));
571         assertThat(simulatorResults.noOfRejectedCreate).isEqualTo(2);
572
573         verifyJobStatus(EI_JOB_ID, "DISABLED");
574     }
575
576     @Test
577     void producerGetEiProducerTypes() throws Exception {
578         final String EI_TYPE_ID_2 = TYPE_ID + "_2";
579         putInfoProducerWithOneType("producer1", TYPE_ID);
580         putEiJob(TYPE_ID, "jobId");
581         putInfoProducerWithOneType("producer2", EI_TYPE_ID_2);
582         putEiJob(EI_TYPE_ID_2, "jobId2");
583         String url = ProducerConsts.API_ROOT + "/info-types";
584
585         ResponseEntity<String> resp = restClient().getForEntity(url).block();
586         assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
587         assertThat(resp.getBody()).contains(TYPE_ID);
588         assertThat(resp.getBody()).contains(EI_TYPE_ID_2);
589     }
590
591     @Test
592     void producerPutEiProducer() throws Exception {
593         this.putInfoType(TYPE_ID);
594         String url = ProducerConsts.API_ROOT + "/info-producers/infoProducerId";
595         String body = gson.toJson(producerInfoRegistratioInfo(TYPE_ID));
596
597         ResponseEntity<String> resp = restClient().putForEntity(url, body).block();
598         assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.CREATED);
599
600         assertThat(this.infoTypes.size()).isEqualTo(1);
601         assertThat(this.infoProducers.getProducersForType(TYPE_ID)).hasSize(1);
602         assertThat(this.infoProducers.size()).isEqualTo(1);
603         assertThat(this.infoProducers.get("infoProducerId").getInfoTypes().iterator().next().getId())
604             .isEqualTo(TYPE_ID);
605
606         resp = restClient().putForEntity(url, body).block();
607         assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
608
609         resp = restClient().getForEntity(url).block();
610         assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
611         assertThat(resp.getBody()).isEqualTo(body);
612     }
613
614     @Test
615     void producerPutEiProducerExistingJob() throws Exception {
616         putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
617         putEiJob(TYPE_ID, "jobId");
618         String url = ProducerConsts.API_ROOT + "/info-producers/infoProducerId";
619         String body = gson.toJson(producerInfoRegistratioInfo(TYPE_ID));
620         restClient().putForEntity(url, body).block();
621
622         ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
623         await().untilAsserted(() -> assertThat(simulatorResults.jobsStarted.size()).isEqualTo(2));
624         ProducerJobInfo request = simulatorResults.jobsStarted.get(0);
625         assertThat(request.id).isEqualTo("jobId");
626     }
627
628     @Test
629     void testPutEiProducer_noType() throws Exception {
630         String url = ProducerConsts.API_ROOT + "/info-producers/infoProducerId";
631         String body = gson.toJson(producerInfoRegistratioInfo(TYPE_ID));
632         testErrorCode(restClient().put(url, body), HttpStatus.NOT_FOUND, "Information type not found");
633     }
634
635     @Test
636     void producerPutProducerAndEiJob() throws Exception {
637         this.putInfoType(TYPE_ID);
638         String url = ProducerConsts.API_ROOT + "/info-producers/infoProducerId";
639         String body = gson.toJson(producerInfoRegistratioInfo(TYPE_ID));
640         restClient().putForEntity(url, body).block();
641         assertThat(this.infoTypes.size()).isEqualTo(1);
642         this.infoTypes.getType(TYPE_ID);
643
644         url = A1eConsts.API_ROOT + "/eijobs/jobId";
645         body = gson.toJson(infoJobInfo());
646         restClient().putForEntity(url, body).block();
647
648         ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
649         await().untilAsserted(() -> assertThat(simulatorResults.jobsStarted.size()).isEqualTo(1));
650         ProducerJobInfo request = simulatorResults.jobsStarted.get(0);
651         assertThat(request.id).isEqualTo("jobId");
652     }
653
654     @Test
655     void producerGetEiJobsForProducer() throws JsonMappingException, JsonProcessingException, ServiceException {
656         putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
657         putEiJob(TYPE_ID, "jobId1");
658         putEiJob(TYPE_ID, "jobId2");
659
660         // PUT a consumerRestApiTestBase.java
661         String url = ProducerConsts.API_ROOT + "/info-producers/infoProducerId";
662         String body = gson.toJson(producerInfoRegistratioInfo(TYPE_ID));
663         restClient().putForEntity(url, body).block();
664
665         url = ProducerConsts.API_ROOT + "/info-producers/infoProducerId/info-jobs";
666         ResponseEntity<String> resp = restClient().getForEntity(url).block();
667         assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
668
669         ProducerJobInfo[] parsedResp = gson.fromJson(resp.getBody(), ProducerJobInfo[].class);
670         assertThat(parsedResp[0].typeId).isEqualTo(TYPE_ID);
671         assertThat(parsedResp[1].typeId).isEqualTo(TYPE_ID);
672     }
673
674     @Test
675     void producerDeleteEiProducer() throws Exception {
676         putInfoProducerWithOneType("infoProducerId", TYPE_ID);
677         putInfoProducerWithOneType("infoProducerId2", TYPE_ID);
678
679         assertThat(this.infoProducers.size()).isEqualTo(2);
680         InfoType type = this.infoTypes.getType(TYPE_ID);
681         assertThat(this.infoProducers.getProducerIdsForType(type.getId())).contains("infoProducerId");
682         assertThat(this.infoProducers.getProducerIdsForType(type.getId())).contains("infoProducerId2");
683         putEiJob(TYPE_ID, "jobId");
684         assertThat(this.infoJobs.size()).isEqualTo(1);
685
686         deleteEiProducer("infoProducerId");
687         assertThat(this.infoProducers.size()).isEqualTo(1);
688         assertThat(this.infoProducers.getProducerIdsForType(TYPE_ID)).doesNotContain("infoProducerId");
689         verifyJobStatus("jobId", "ENABLED");
690
691         deleteEiProducer("infoProducerId2");
692         assertThat(this.infoProducers.size()).isZero();
693         assertThat(this.infoTypes.size()).isEqualTo(1);
694         verifyJobStatus("jobId", "DISABLED");
695     }
696
697     @Test
698     void a1eJobStatusNotifications() throws JsonMappingException, JsonProcessingException, ServiceException {
699         ConsumerSimulatorController.TestResults consumerCalls = this.consumerSimulator.getTestResults();
700         ProducerSimulatorController.TestResults producerCalls = this.producerSimulator.getTestResults();
701
702         putInfoProducerWithOneType("infoProducerId", TYPE_ID);
703         putEiJob(TYPE_ID, "jobId");
704         putInfoProducerWithOneType("infoProducerId2", TYPE_ID);
705         await().untilAsserted(() -> assertThat(producerCalls.jobsStarted.size()).isEqualTo(2));
706
707         deleteEiProducer("infoProducerId2");
708         assertThat(this.infoTypes.size()).isEqualTo(1); // The type remains, one producer left
709         deleteEiProducer("infoProducerId");
710         assertThat(this.infoTypes.size()).isEqualTo(1); // The type remains
711         assertThat(this.infoJobs.size()).isEqualTo(1); // The job remains
712         await().untilAsserted(() -> assertThat(consumerCalls.status.size()).isEqualTo(1));
713         assertThat(consumerCalls.status.get(0).state).isEqualTo(A1eEiJobStatus.EiJobStatusValues.DISABLED);
714
715         putInfoProducerWithOneType("infoProducerId", TYPE_ID);
716         await().untilAsserted(() -> assertThat(consumerCalls.status.size()).isEqualTo(2));
717         assertThat(consumerCalls.status.get(1).state).isEqualTo(A1eEiJobStatus.EiJobStatusValues.ENABLED);
718     }
719
720     @Test
721     void a1eJobStatusNotifications2() throws JsonMappingException, JsonProcessingException, ServiceException {
722         // Test replacing a producer with new and removed types
723
724         // Create a job
725         putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
726         putEiJob(TYPE_ID, EI_JOB_ID);
727
728         // change the type for the producer, the job shall be disabled
729         putInfoProducerWithOneType(PRODUCER_ID, "junk");
730         verifyJobStatus(EI_JOB_ID, "DISABLED");
731         ConsumerSimulatorController.TestResults consumerCalls = this.consumerSimulator.getTestResults();
732         await().untilAsserted(() -> assertThat(consumerCalls.status.size()).isEqualTo(1));
733         assertThat(consumerCalls.status.get(0).state).isEqualTo(A1eEiJobStatus.EiJobStatusValues.DISABLED);
734
735         putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
736         verifyJobStatus(EI_JOB_ID, "ENABLED");
737         await().untilAsserted(() -> assertThat(consumerCalls.status.size()).isEqualTo(2));
738         assertThat(consumerCalls.status.get(1).state).isEqualTo(A1eEiJobStatus.EiJobStatusValues.ENABLED);
739     }
740
741     @Test
742     void producerGetProducerEiType() throws JsonMappingException, JsonProcessingException, ServiceException {
743         putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
744         String url = ProducerConsts.API_ROOT + "/info-types/" + TYPE_ID;
745         ResponseEntity<String> resp = restClient().getForEntity(url).block();
746         ProducerInfoTypeInfo info = gson.fromJson(resp.getBody(), ProducerInfoTypeInfo.class);
747         assertThat(info.jobDataSchema).isNotNull();
748     }
749
750     @Test
751     void producerGetProducerIdentifiers() throws JsonMappingException, JsonProcessingException, ServiceException {
752         putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
753         String url = ProducerConsts.API_ROOT + "/info-producers";
754         ResponseEntity<String> resp = restClient().getForEntity(url).block();
755         assertThat(resp.getBody()).contains(PRODUCER_ID);
756
757         url = ProducerConsts.API_ROOT + "/info-producers?info_type_id=" + TYPE_ID;
758         resp = restClient().getForEntity(url).block();
759         assertThat(resp.getBody()).contains(PRODUCER_ID);
760
761         url = ProducerConsts.API_ROOT + "/info-producers?info_type_id=junk";
762         resp = restClient().getForEntity(url).block();
763         assertThat(resp.getBody()).isEqualTo("[]");
764     }
765
766     @Test
767     void producerSupervision() throws JsonMappingException, JsonProcessingException, ServiceException {
768
769         ConsumerSimulatorController.TestResults consumerResults = this.consumerSimulator.getTestResults();
770         putEiProducerWithOneTypeRejecting("simulateProducerError", TYPE_ID);
771
772         {
773             // Create a job
774             putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
775             putEiJob(TYPE_ID, EI_JOB_ID);
776             verifyJobStatus(EI_JOB_ID, "ENABLED");
777             deleteEiProducer(PRODUCER_ID);
778             // A Job disabled status notification shall now be received
779             await().untilAsserted(() -> assertThat(consumerResults.status.size()).isEqualTo(1));
780             assertThat(consumerResults.status.get(0).state).isEqualTo(A1eEiJobStatus.EiJobStatusValues.DISABLED);
781             verifyJobStatus(EI_JOB_ID, "DISABLED");
782         }
783
784         assertThat(this.infoProducers.size()).isEqualTo(1);
785         assertThat(this.infoTypes.size()).isEqualTo(1);
786         assertProducerOpState("simulateProducerError", ProducerStatusInfo.OperationalState.ENABLED);
787
788         this.producerSupervision.createTask().blockLast();
789         this.producerSupervision.createTask().blockLast();
790
791         // Now we have one producer that is disabled
792         assertThat(this.infoProducers.size()).isEqualTo(1);
793         assertProducerOpState("simulateProducerError", ProducerStatusInfo.OperationalState.DISABLED);
794
795         // After 3 failed checks, the producer shall be deregisterred
796         this.producerSupervision.createTask().blockLast();
797         assertThat(this.infoProducers.size()).isZero(); // The producer is removed
798         assertThat(this.infoTypes.size()).isEqualTo(1); // The type remains
799
800         // Now we have one disabled job, and no producer.
801         // PUT a producer, then a Job ENABLED status notification shall be received
802         putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
803         await().untilAsserted(() -> assertThat(consumerResults.status.size()).isEqualTo(2));
804         assertThat(consumerResults.status.get(1).state).isEqualTo(A1eEiJobStatus.EiJobStatusValues.ENABLED);
805         verifyJobStatus(EI_JOB_ID, "ENABLED");
806     }
807
808     @Test
809     void producerSupervision2() throws JsonMappingException, JsonProcessingException, ServiceException {
810         // Test that supervision enables not enabled jobs and sends a notification when
811         // suceeded
812
813         putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
814         putEiJob(TYPE_ID, EI_JOB_ID);
815
816         InfoProducer producer = this.infoProducers.getProducer(PRODUCER_ID);
817         InfoJob job = this.infoJobs.getJob(EI_JOB_ID);
818         // Pretend that the producer did reject the job and the a DISABLED notification
819         // is sent for the job
820         producer.setJobDisabled(job);
821         job.setLastReportedStatus(false);
822         verifyJobStatus(EI_JOB_ID, "DISABLED");
823
824         // Run the supervision and wait for the job to get started in the producer
825         this.producerSupervision.createTask().blockLast();
826         ConsumerSimulatorController.TestResults consumerResults = this.consumerSimulator.getTestResults();
827         await().untilAsserted(() -> assertThat(consumerResults.status.size()).isEqualTo(1));
828         assertThat(consumerResults.status.get(0).state).isEqualTo(A1eEiJobStatus.EiJobStatusValues.ENABLED);
829         verifyJobStatus(EI_JOB_ID, "ENABLED");
830     }
831
832     @Test
833     void testGetStatus() throws JsonMappingException, JsonProcessingException, ServiceException {
834         putEiProducerWithOneTypeRejecting("simulateProducerError", TYPE_ID);
835         putEiProducerWithOneTypeRejecting("simulateProducerError2", TYPE_ID);
836
837         String url = "/status";
838         ResponseEntity<String> resp = restClient().getForEntity(url).block();
839         assertThat(resp.getBody()).contains("hunky dory");
840     }
841
842     @Test
843     void testEiJobDatabase() throws Exception {
844         putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
845         putEiJob(TYPE_ID, "jobId1");
846         putEiJob(TYPE_ID, "jobId2");
847
848         assertThat(this.infoJobs.size()).isEqualTo(2);
849
850         {
851             InfoJob savedJob = this.infoJobs.getJob("jobId1");
852             // Restore the jobs
853             InfoJobs jobs = new InfoJobs(this.applicationConfig, this.producerCallbacks);
854             jobs.restoreJobsFromDatabase();
855             assertThat(jobs.size()).isEqualTo(2);
856             InfoJob restoredJob = jobs.getJob("jobId1");
857             assertThat(restoredJob.getId()).isEqualTo("jobId1");
858             assertThat(restoredJob.getLastUpdated()).isEqualTo(savedJob.getLastUpdated());
859
860             jobs.remove("jobId1", this.infoProducers);
861             jobs.remove("jobId2", this.infoProducers);
862         }
863         {
864             // Restore the jobs, no jobs in database
865             InfoJobs jobs = new InfoJobs(this.applicationConfig, this.producerCallbacks);
866             jobs.restoreJobsFromDatabase();
867             assertThat(jobs.size()).isZero();
868         }
869         logger.warn("Test removing a job when the db file is gone");
870         this.infoJobs.remove("jobId1", this.infoProducers);
871         assertThat(this.infoJobs.size()).isEqualTo(1);
872
873         ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
874         await().untilAsserted(() -> assertThat(simulatorResults.jobsStopped.size()).isEqualTo(3));
875     }
876
877     @Test
878     void testEiTypesDatabase() throws Exception {
879         putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
880
881         assertThat(this.infoTypes.size()).isEqualTo(1);
882
883         {
884             // Restore the types
885             InfoTypes types = new InfoTypes(this.applicationConfig);
886             types.restoreTypesFromDatabase();
887             assertThat(types.size()).isEqualTo(1);
888
889         }
890         {
891             // Restore the jobs, no jobs in database
892             InfoTypes types = new InfoTypes(this.applicationConfig);
893             types.clear();
894             types.restoreTypesFromDatabase();
895             assertThat(types.size()).isZero();
896         }
897         logger.warn("Test removing a job when the db file is gone");
898         this.infoTypes.remove(this.infoTypes.getType(TYPE_ID));
899         assertThat(this.infoJobs.size()).isZero();
900     }
901
902     private void deleteEiProducer(String infoProducerId) {
903         String url = ProducerConsts.API_ROOT + "/info-producers/" + infoProducerId;
904         restClient().deleteForEntity(url).block();
905     }
906
907     private void verifyJobStatus(String jobId, String expStatus) {
908         String url = A1eConsts.API_ROOT + "/eijobs/" + jobId + "/status";
909         String rsp = restClient().get(url).block();
910         assertThat(rsp).contains(expStatus);
911     }
912
913     private void assertProducerOpState(String producerId,
914         ProducerStatusInfo.OperationalState expectedOperationalState) {
915         String statusUrl = ProducerConsts.API_ROOT + "/info-producers/" + producerId + "/status";
916         ResponseEntity<String> resp = restClient().getForEntity(statusUrl).block();
917         ProducerStatusInfo statusInfo = gson.fromJson(resp.getBody(), ProducerStatusInfo.class);
918         assertThat(statusInfo.opState).isEqualTo(expectedOperationalState);
919     }
920
921     ProducerInfoTypeInfo producerEiTypeRegistrationInfo(String typeId)
922         throws JsonMappingException, JsonProcessingException {
923         return new ProducerInfoTypeInfo(jsonSchemaObject());
924     }
925
926     ProducerRegistrationInfo producerEiRegistratioInfoRejecting(String typeId)
927         throws JsonMappingException, JsonProcessingException {
928         return new ProducerRegistrationInfo(Arrays.asList(typeId), //
929             baseUrl() + ProducerSimulatorController.JOB_ERROR_URL,
930             baseUrl() + ProducerSimulatorController.SUPERVISION_ERROR_URL);
931     }
932
933     ProducerRegistrationInfo producerInfoRegistratioInfo(String typeId)
934         throws JsonMappingException, JsonProcessingException {
935         return new ProducerRegistrationInfo(Arrays.asList(typeId), //
936             baseUrl() + ProducerSimulatorController.JOB_URL, baseUrl() + ProducerSimulatorController.SUPERVISION_URL);
937     }
938
939     private ConsumerJobInfo consumerJobInfo() throws JsonMappingException, JsonProcessingException {
940         return consumerJobInfo(TYPE_ID, EI_JOB_ID);
941     }
942
943     ConsumerJobInfo consumerJobInfo(String typeId, String infoJobId)
944         throws JsonMappingException, JsonProcessingException {
945         return new ConsumerJobInfo(typeId, jsonObject(), "owner", "https://junk.com",
946             baseUrl() + ConsumerSimulatorController.getJobStatusUrl(infoJobId));
947     }
948
949     private A1eEiJobInfo infoJobInfo() throws JsonMappingException, JsonProcessingException {
950         return infoJobInfo(TYPE_ID, EI_JOB_ID);
951     }
952
953     A1eEiJobInfo infoJobInfo(String typeId, String infoJobId) throws JsonMappingException, JsonProcessingException {
954         return new A1eEiJobInfo(typeId, jsonObject(), "owner", "https://junk.com",
955             baseUrl() + ConsumerSimulatorController.getJobStatusUrl(infoJobId));
956     }
957
958     private Object jsonObject(String json) {
959         try {
960             return JsonParser.parseString(json).getAsJsonObject();
961         } catch (Exception e) {
962             throw new NullPointerException(e.toString());
963         }
964     }
965
966     private Object jsonSchemaObject() {
967         // a json schema with one mandatory property named "string"
968         String schemaStr = "{" //
969             + "\"$schema\": \"http://json-schema.org/draft-04/schema#\"," //
970             + "\"type\": \"object\"," //
971             + "\"properties\": {" //
972             + EI_JOB_PROPERTY + " : {" //
973             + "    \"type\": \"string\"" //
974             + "  }" //
975             + "}," //
976             + "\"required\": [" //
977             + EI_JOB_PROPERTY //
978             + "]" //
979             + "}"; //
980         return jsonObject(schemaStr);
981     }
982
983     private Object jsonObject() {
984         return jsonObject("{ " + EI_JOB_PROPERTY + " : \"value\" }");
985     }
986
987     private InfoJob putEiJob(String infoTypeId, String jobId)
988         throws JsonMappingException, JsonProcessingException, ServiceException {
989
990         String url = A1eConsts.API_ROOT + "/eijobs/" + jobId;
991         String body = gson.toJson(infoJobInfo(infoTypeId, jobId));
992         restClient().putForEntity(url, body).block();
993
994         return this.infoJobs.getJob(jobId);
995     }
996
997     private HttpStatus putInfoType(String infoTypeId)
998         throws JsonMappingException, JsonProcessingException, ServiceException {
999         String url = ProducerConsts.API_ROOT + "/info-types/" + infoTypeId;
1000         String body = gson.toJson(producerEiTypeRegistrationInfo(infoTypeId));
1001
1002         ResponseEntity<String> resp = restClient().putForEntity(url, body).block();
1003         this.infoTypes.getType(infoTypeId);
1004         return resp.getStatusCode();
1005
1006     }
1007
1008     private InfoType putEiProducerWithOneTypeRejecting(String producerId, String infoTypeId)
1009         throws JsonMappingException, JsonProcessingException, ServiceException {
1010         this.putInfoType(infoTypeId);
1011         String url = ProducerConsts.API_ROOT + "/info-producers/" + producerId;
1012         String body = gson.toJson(producerEiRegistratioInfoRejecting(infoTypeId));
1013         restClient().putForEntity(url, body).block();
1014         return this.infoTypes.getType(infoTypeId);
1015     }
1016
1017     private InfoType putInfoProducerWithOneType(String producerId, String infoTypeId)
1018         throws JsonMappingException, JsonProcessingException, ServiceException {
1019         this.putInfoType(infoTypeId);
1020
1021         String url = ProducerConsts.API_ROOT + "/info-producers/" + producerId;
1022         String body = gson.toJson(producerInfoRegistratioInfo(infoTypeId));
1023
1024         restClient().putForEntity(url, body).block();
1025
1026         return this.infoTypes.getType(infoTypeId);
1027     }
1028
1029     private String baseUrl() {
1030         return "https://localhost:" + this.port;
1031     }
1032
1033     private AsyncRestClient restClient(boolean useTrustValidation) {
1034         WebClientConfig config = this.applicationConfig.getWebClientConfig();
1035         HttpProxyConfig httpProxyConfig = ImmutableHttpProxyConfig.builder() //
1036             .httpProxyHost("") //
1037             .httpProxyPort(0) //
1038             .build();
1039         config = ImmutableWebClientConfig.builder() //
1040             .keyStoreType(config.keyStoreType()) //
1041             .keyStorePassword(config.keyStorePassword()) //
1042             .keyStore(config.keyStore()) //
1043             .keyPassword(config.keyPassword()) //
1044             .isTrustStoreUsed(useTrustValidation) //
1045             .trustStore(config.trustStore()) //
1046             .trustStorePassword(config.trustStorePassword()) //
1047             .httpProxyConfig(httpProxyConfig).build();
1048
1049         AsyncRestClientFactory restClientFactory = new AsyncRestClientFactory(config);
1050         return restClientFactory.createRestClientNoHttpProxy(baseUrl());
1051     }
1052
1053     private AsyncRestClient restClient() {
1054         return restClient(false);
1055     }
1056
1057     private void testErrorCode(Mono<?> request, HttpStatus expStatus, String responseContains) {
1058         testErrorCode(request, expStatus, responseContains, true);
1059     }
1060
1061     private void testErrorCode(Mono<?> request, HttpStatus expStatus, String responseContains,
1062         boolean expectApplicationProblemJsonMediaType) {
1063         StepVerifier.create(request) //
1064             .expectSubscription() //
1065             .expectErrorMatches(
1066                 t -> checkWebClientError(t, expStatus, responseContains, expectApplicationProblemJsonMediaType)) //
1067             .verify();
1068     }
1069
1070     private boolean checkWebClientError(Throwable throwable, HttpStatus expStatus, String responseContains,
1071         boolean expectApplicationProblemJsonMediaType) {
1072         assertTrue(throwable instanceof WebClientResponseException);
1073         WebClientResponseException responseException = (WebClientResponseException) throwable;
1074         assertThat(responseException.getStatusCode()).isEqualTo(expStatus);
1075         assertThat(responseException.getResponseBodyAsString()).contains(responseContains);
1076         if (expectApplicationProblemJsonMediaType) {
1077             assertThat(responseException.getHeaders().getContentType()).isEqualTo(MediaType.APPLICATION_PROBLEM_JSON);
1078         }
1079         return true;
1080     }
1081
1082 }