NONRTRIC - Adding information consumer before information type
[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.ProducerEiTypeInfo;
63 import org.oransc.enrichment.controllers.r1producer.ProducerJobInfo;
64 import org.oransc.enrichment.controllers.r1producer.ProducerRegistrationInfo;
65 import org.oransc.enrichment.controllers.r1producer.ProducerStatusInfo;
66 import org.oransc.enrichment.exceptions.ServiceException;
67 import org.oransc.enrichment.repository.EiJob;
68 import org.oransc.enrichment.repository.EiJobs;
69 import org.oransc.enrichment.repository.EiProducer;
70 import org.oransc.enrichment.repository.EiProducers;
71 import org.oransc.enrichment.repository.EiType;
72 import org.oransc.enrichment.repository.EiTypes;
73 import org.oransc.enrichment.tasks.ProducerSupervision;
74 import org.slf4j.Logger;
75 import org.slf4j.LoggerFactory;
76 import org.springframework.beans.factory.annotation.Autowired;
77 import org.springframework.boot.test.context.SpringBootTest;
78 import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
79 import org.springframework.boot.test.context.TestConfiguration;
80 import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
81 import org.springframework.boot.web.server.LocalServerPort;
82 import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
83 import org.springframework.context.ApplicationContext;
84 import org.springframework.context.annotation.Bean;
85 import org.springframework.http.HttpStatus;
86 import org.springframework.http.MediaType;
87 import org.springframework.http.ResponseEntity;
88 import org.springframework.test.context.TestPropertySource;
89 import org.springframework.test.context.junit.jupiter.SpringExtension;
90 import org.springframework.web.reactive.function.client.WebClientResponseException;
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     EiJobs eiJobs;
115
116     @Autowired
117     EiTypes eiTypes;
118
119     @Autowired
120     EiProducers eiProducers;
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.eiJobs.clear();
156         this.eiTypes.clear();
157         this.eiProducers.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         putEiProducerWithOneType(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 consumerGetEiTypes() throws Exception {
192         putEiProducerWithOneType(PRODUCER_ID, "test");
193         String url = ConsumerConsts.API_ROOT + "/info-types";
194         String rsp = restClient().get(url).block();
195         assertThat(rsp).isEqualTo("[\"test\"]");
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         putEiProducerWithOneType(PRODUCER_ID, "test");
215         String url = A1eConsts.API_ROOT + "/eitypes/test";
216         String rsp = restClient().get(url).block();
217         A1eEiTypeInfo info = gson.fromJson(rsp, A1eEiTypeInfo.class);
218         assertThat(info).isNotNull();
219     }
220
221     @Test
222     void consumerGetEiType() throws Exception {
223         putEiProducerWithOneType(PRODUCER_ID, "test");
224         String url = ConsumerConsts.API_ROOT + "/info-types/test";
225         String rsp = restClient().get(url).block();
226         ConsumerInfoTypeInfo info = gson.fromJson(rsp, ConsumerInfoTypeInfo.class);
227         assertThat(info).isNotNull();
228         assertThat(info.jobDataSchema).isNotNull();
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         putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
246         putEiJob(TYPE_ID, "jobId");
247         final String JOB_ID_JSON = "[\"jobId\"]";
248         String url = A1eConsts.API_ROOT + "/eijobs?eiTypeId=typeId";
249         String rsp = restClient().get(url).block();
250         assertThat(rsp).isEqualTo(JOB_ID_JSON);
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         putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
276         putEiJob(TYPE_ID, "jobId");
277         final String JOB_ID_JSON = "[\"jobId\"]";
278         String url = ConsumerConsts.API_ROOT + "/info-jobs?infoTypeId=typeId";
279         String rsp = restClient().get(url).block();
280         assertThat(rsp).isEqualTo(JOB_ID_JSON);
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         putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
306         putEiJob(TYPE_ID, "jobId");
307         String url = A1eConsts.API_ROOT + "/eijobs/jobId";
308         String rsp = restClient().get(url).block();
309         A1eEiJobInfo info = gson.fromJson(rsp, A1eEiJobInfo.class);
310         assertThat(info.owner).isEqualTo("owner");
311         assertThat(info.eiTypeId).isEqualTo(TYPE_ID);
312     }
313
314     @Test
315     void consumerGetEiJob() throws Exception {
316         putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
317         putEiJob(TYPE_ID, "jobId");
318         String url = ConsumerConsts.API_ROOT + "/info-jobs/jobId";
319         String rsp = restClient().get(url).block();
320         ConsumerJobInfo info = gson.fromJson(rsp, ConsumerJobInfo.class);
321         assertThat(info.owner).isEqualTo("owner");
322         assertThat(info.infoTypeId).isEqualTo(TYPE_ID);
323     }
324
325     @Test
326     void a1eGetEiJobNotFound() throws Exception {
327         putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
328         String url = A1eConsts.API_ROOT + "/eijobs/junk";
329         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND, "Could not find Information job: junk");
330     }
331
332     @Test
333     void consumerGetInfoJobNotFound() throws Exception {
334         putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
335         String url = ConsumerConsts.API_ROOT + "/info-jobs/junk";
336         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND, "Could not find Information job: junk");
337     }
338
339     @Test
340     void a1eGetEiJobStatus() throws Exception {
341         putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
342         putEiJob(TYPE_ID, "jobId");
343
344         verifyJobStatus("jobId", "ENABLED");
345     }
346
347     @Test
348     void consumerGetEiJobStatus() throws Exception {
349         putEiProducerWithOneType(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         putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
360         putEiJob(TYPE_ID, "jobId");
361         assertThat(this.eiJobs.size()).isEqualTo(1);
362         String url = A1eConsts.API_ROOT + "/eijobs/jobId";
363         restClient().delete(url).block();
364         assertThat(this.eiJobs.size()).isZero();
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         putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
374         putEiJob(TYPE_ID, "jobId");
375         assertThat(this.eiJobs.size()).isEqualTo(1);
376         String url = ConsumerConsts.API_ROOT + "/info-jobs/jobId";
377         restClient().delete(url).block();
378         assertThat(this.eiJobs.size()).isZero();
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         putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
388         String url = A1eConsts.API_ROOT + "/eijobs/junk";
389         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND, "Could not find Information job: junk");
390     }
391
392     @Test
393     void consumerDeleteEiJobNotFound() throws Exception {
394         putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
395         String url = ConsumerConsts.API_ROOT + "/info-jobs/junk";
396         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND, "Could not find Information job: junk");
397     }
398
399     @Test
400     void a1ePutEiJob() throws Exception {
401         // Test that one producer accepting a job is enough
402         putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
403         putEiProducerWithOneTypeRejecting("simulateProducerError", TYPE_ID);
404
405         String url = A1eConsts.API_ROOT + "/eijobs/jobId";
406         String body = gson.toJson(eiJobInfo());
407         ResponseEntity<String> resp = restClient().putForEntity(url, body).block();
408         assertThat(this.eiJobs.size()).isEqualTo(1);
409         assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.CREATED);
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         EiJob job = this.eiJobs.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         putEiProducerWithOneType(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.eiJobs.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         EiJob job = this.eiJobs.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.eiJobs.size()).isEqualTo(1);
458         assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.CREATED);
459         verifyJobStatus(EI_JOB_ID, "DISABLED");
460
461         putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
462
463         verifyJobStatus(EI_JOB_ID, "ENABLED");
464
465     }
466
467     @Test
468     void a1ePutEiJob_jsonSchemavalidationError() throws Exception {
469         putEiProducerWithOneType(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         putEiProducerWithOneType(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 = new ConsumerJobInfo("typeId", jsonObject("{ \"XXstring\" : \"value\" }"), "owner",
487             "targetUri", "jobStatusUrl");
488         String body = gson.toJson(jobInfo);
489
490         testErrorCode(restClient().put(url, body), HttpStatus.CONFLICT, "Json validation failure");
491     }
492
493     @Test
494     void a1eChangingEiTypeGetRejected() throws Exception {
495         putEiProducerWithOneType("producer1", "typeId1");
496         putEiProducerWithOneType("producer2", "typeId2");
497         putEiJob("typeId1", "jobId");
498
499         String url = A1eConsts.API_ROOT + "/eijobs/jobId";
500         String body = gson.toJson(eiJobInfo("typeId2", "jobId"));
501         testErrorCode(restClient().put(url, body), HttpStatus.CONFLICT,
502             "Not allowed to change type for existing EI job");
503     }
504
505     @Test
506     void consumerChangingInfoTypeGetRejected() throws Exception {
507         putEiProducerWithOneType("producer1", "typeId1");
508         putEiProducerWithOneType("producer2", "typeId2");
509         putEiJob("typeId1", "jobId");
510
511         String url = ConsumerConsts.API_ROOT + "/info-jobs/jobId";
512         String body = gson.toJson(consumerJobInfo("typeId2", "jobId"));
513         testErrorCode(restClient().put(url, body), HttpStatus.CONFLICT, "Not allowed to change type for existing job");
514     }
515
516     @Test
517     void producerPutEiType() throws JsonMappingException, JsonProcessingException, ServiceException {
518         assertThat(putEiType(TYPE_ID)).isEqualTo(HttpStatus.CREATED);
519         assertThat(putEiType(TYPE_ID)).isEqualTo(HttpStatus.OK);
520     }
521
522     @Test
523     void producerPutEiType_noSchema() {
524         String url = ProducerConsts.API_ROOT + "/eitypes/" + TYPE_ID;
525         String body = "{}";
526         testErrorCode(restClient().put(url, body), HttpStatus.BAD_REQUEST, "No schema provided");
527     }
528
529     @Test
530     void producerDeleteEiType() throws Exception {
531         putEiType(TYPE_ID);
532         String url = ProducerConsts.API_ROOT + "/eitypes/" + TYPE_ID;
533         restClient().delete(url).block();
534         assertThat(this.eiTypes.size()).isEqualTo(0);
535
536         testErrorCode(restClient().delete(url), HttpStatus.NOT_FOUND, "Information type not found");
537     }
538
539     @Test
540     void producerDeleteEiTypeExistingProducer() throws Exception {
541         putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
542         String url = ProducerConsts.API_ROOT + "/eitypes/" + TYPE_ID;
543         testErrorCode(restClient().delete(url), HttpStatus.NOT_ACCEPTABLE,
544             "The type has active producers: " + PRODUCER_ID);
545         assertThat(this.eiTypes.size()).isEqualTo(1);
546     }
547
548     @Test
549     void producerPutProducerWithOneType_rejecting()
550         throws JsonMappingException, JsonProcessingException, ServiceException {
551         putEiProducerWithOneTypeRejecting("simulateProducerError", TYPE_ID);
552         String url = A1eConsts.API_ROOT + "/eijobs/" + EI_JOB_ID;
553         String body = gson.toJson(eiJobInfo());
554         restClient().put(url, body).block();
555
556         ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
557         // There is one retry -> 2 calls
558         await().untilAsserted(() -> assertThat(simulatorResults.noOfRejectedCreate).isEqualTo(2));
559         assertThat(simulatorResults.noOfRejectedCreate).isEqualTo(2);
560
561         verifyJobStatus(EI_JOB_ID, "DISABLED");
562     }
563
564     @Test
565     void producerGetEiProducerTypes() throws Exception {
566         final String EI_TYPE_ID_2 = TYPE_ID + "_2";
567         putEiProducerWithOneType("producer1", TYPE_ID);
568         putEiJob(TYPE_ID, "jobId");
569         putEiProducerWithOneType("producer2", EI_TYPE_ID_2);
570         putEiJob(EI_TYPE_ID_2, "jobId2");
571         String url = ProducerConsts.API_ROOT + "/eitypes";
572
573         ResponseEntity<String> resp = restClient().getForEntity(url).block();
574         assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
575         assertThat(resp.getBody()).contains(TYPE_ID);
576         assertThat(resp.getBody()).contains(EI_TYPE_ID_2);
577     }
578
579     @Test
580     void producerPutEiProducer() throws Exception {
581         this.putEiType(TYPE_ID);
582         String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId";
583         String body = gson.toJson(producerEiRegistratioInfo(TYPE_ID));
584
585         ResponseEntity<String> resp = restClient().putForEntity(url, body).block();
586         assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.CREATED);
587
588         assertThat(this.eiTypes.size()).isEqualTo(1);
589         assertThat(this.eiProducers.getProducersForType(TYPE_ID).size()).isEqualTo(1);
590         assertThat(this.eiProducers.size()).isEqualTo(1);
591         assertThat(this.eiProducers.get("eiProducerId").getEiTypes().iterator().next().getId()).isEqualTo(TYPE_ID);
592
593         resp = restClient().putForEntity(url, body).block();
594         assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
595
596         resp = restClient().getForEntity(url).block();
597         assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
598         assertThat(resp.getBody()).isEqualTo(body);
599     }
600
601     @Test
602     void producerPutEiProducerExistingJob() throws Exception {
603         putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
604         putEiJob(TYPE_ID, "jobId");
605         String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId";
606         String body = gson.toJson(producerEiRegistratioInfo(TYPE_ID));
607         restClient().putForEntity(url, body).block();
608
609         ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
610         await().untilAsserted(() -> assertThat(simulatorResults.jobsStarted.size()).isEqualTo(2));
611         ProducerJobInfo request = simulatorResults.jobsStarted.get(0);
612         assertThat(request.id).isEqualTo("jobId");
613     }
614
615     @Test
616     void testPutEiProducer_noType() throws Exception {
617         String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId";
618         String body = gson.toJson(producerEiRegistratioInfo(TYPE_ID));
619         testErrorCode(restClient().put(url, body), HttpStatus.NOT_FOUND, "Information type not found");
620     }
621
622     @Test
623     void producerPutProducerAndEiJob() throws Exception {
624         this.putEiType(TYPE_ID);
625         String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId";
626         String body = gson.toJson(producerEiRegistratioInfo(TYPE_ID));
627         restClient().putForEntity(url, body).block();
628         assertThat(this.eiTypes.size()).isEqualTo(1);
629         this.eiTypes.getType(TYPE_ID);
630
631         url = A1eConsts.API_ROOT + "/eijobs/jobId";
632         body = gson.toJson(eiJobInfo());
633         restClient().putForEntity(url, body).block();
634
635         ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
636         await().untilAsserted(() -> assertThat(simulatorResults.jobsStarted.size()).isEqualTo(1));
637         ProducerJobInfo request = simulatorResults.jobsStarted.get(0);
638         assertThat(request.id).isEqualTo("jobId");
639     }
640
641     @Test
642     void producerGetEiJobsForProducer() throws JsonMappingException, JsonProcessingException, ServiceException {
643         putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
644         putEiJob(TYPE_ID, "jobId1");
645         putEiJob(TYPE_ID, "jobId2");
646
647         // PUT a consumerRestApiTestBase.java
648         String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId";
649         String body = gson.toJson(producerEiRegistratioInfo(TYPE_ID));
650         restClient().putForEntity(url, body).block();
651
652         url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId/eijobs";
653         ResponseEntity<String> resp = restClient().getForEntity(url).block();
654         assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
655
656         ProducerJobInfo[] parsedResp = gson.fromJson(resp.getBody(), ProducerJobInfo[].class);
657         assertThat(parsedResp[0].typeId).isEqualTo(TYPE_ID);
658         assertThat(parsedResp[1].typeId).isEqualTo(TYPE_ID);
659     }
660
661     @Test
662     void producerDeleteEiProducer() throws Exception {
663         putEiProducerWithOneType("eiProducerId", TYPE_ID);
664         putEiProducerWithOneType("eiProducerId2", TYPE_ID);
665
666         assertThat(this.eiProducers.size()).isEqualTo(2);
667         EiType type = this.eiTypes.getType(TYPE_ID);
668         assertThat(this.eiProducers.getProducerIdsForType(type.getId())).contains("eiProducerId");
669         assertThat(this.eiProducers.getProducerIdsForType(type.getId())).contains("eiProducerId2");
670         putEiJob(TYPE_ID, "jobId");
671         assertThat(this.eiJobs.size()).isEqualTo(1);
672
673         deleteEiProducer("eiProducerId");
674         assertThat(this.eiProducers.size()).isEqualTo(1);
675         assertThat(this.eiProducers.getProducerIdsForType(TYPE_ID)).doesNotContain("eiProducerId");
676         verifyJobStatus("jobId", "ENABLED");
677
678         deleteEiProducer("eiProducerId2");
679         assertThat(this.eiProducers.size()).isZero();
680         assertThat(this.eiTypes.size()).isEqualTo(1);
681         verifyJobStatus("jobId", "DISABLED");
682     }
683
684     @Test
685     void a1eJobStatusNotifications() throws JsonMappingException, JsonProcessingException, ServiceException {
686         ConsumerSimulatorController.TestResults consumerCalls = this.consumerSimulator.getTestResults();
687         ProducerSimulatorController.TestResults producerCalls = this.producerSimulator.getTestResults();
688
689         putEiProducerWithOneType("eiProducerId", TYPE_ID);
690         putEiJob(TYPE_ID, "jobId");
691         putEiProducerWithOneType("eiProducerId2", TYPE_ID);
692         await().untilAsserted(() -> assertThat(producerCalls.jobsStarted.size()).isEqualTo(2));
693
694         deleteEiProducer("eiProducerId2");
695         assertThat(this.eiTypes.size()).isEqualTo(1); // The type remains, one producer left
696         deleteEiProducer("eiProducerId");
697         assertThat(this.eiTypes.size()).isEqualTo(1); // The type remains
698         assertThat(this.eiJobs.size()).isEqualTo(1); // The job remains
699         await().untilAsserted(() -> assertThat(consumerCalls.status.size()).isEqualTo(1));
700         assertThat(consumerCalls.status.get(0).state).isEqualTo(A1eEiJobStatus.EiJobStatusValues.DISABLED);
701
702         putEiProducerWithOneType("eiProducerId", TYPE_ID);
703         await().untilAsserted(() -> assertThat(consumerCalls.status.size()).isEqualTo(2));
704         assertThat(consumerCalls.status.get(1).state).isEqualTo(A1eEiJobStatus.EiJobStatusValues.ENABLED);
705     }
706
707     @Test
708     void a1eJobStatusNotifications2() throws JsonMappingException, JsonProcessingException, ServiceException {
709         // Test replacing a producer with new and removed types
710
711         // Create a job
712         putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
713         putEiJob(TYPE_ID, EI_JOB_ID);
714
715         // change the type for the producer, the job shall be disabled
716         putEiProducerWithOneType(PRODUCER_ID, "junk");
717         verifyJobStatus(EI_JOB_ID, "DISABLED");
718         ConsumerSimulatorController.TestResults consumerCalls = this.consumerSimulator.getTestResults();
719         await().untilAsserted(() -> assertThat(consumerCalls.status.size()).isEqualTo(1));
720         assertThat(consumerCalls.status.get(0).state).isEqualTo(A1eEiJobStatus.EiJobStatusValues.DISABLED);
721
722         putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
723         verifyJobStatus(EI_JOB_ID, "ENABLED");
724         await().untilAsserted(() -> assertThat(consumerCalls.status.size()).isEqualTo(2));
725         assertThat(consumerCalls.status.get(1).state).isEqualTo(A1eEiJobStatus.EiJobStatusValues.ENABLED);
726     }
727
728     @Test
729     void producerGetProducerEiType() throws JsonMappingException, JsonProcessingException, ServiceException {
730         putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
731         String url = ProducerConsts.API_ROOT + "/eitypes/" + TYPE_ID;
732         ResponseEntity<String> resp = restClient().getForEntity(url).block();
733         ProducerEiTypeInfo info = gson.fromJson(resp.getBody(), ProducerEiTypeInfo.class);
734         assertThat(info.jobDataSchema).isNotNull();
735     }
736
737     @Test
738     void producerGetProducerIdentifiers() throws JsonMappingException, JsonProcessingException, ServiceException {
739         putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
740         String url = ProducerConsts.API_ROOT + "/eiproducers";
741         ResponseEntity<String> resp = restClient().getForEntity(url).block();
742         assertThat(resp.getBody()).contains(PRODUCER_ID);
743
744         url = ProducerConsts.API_ROOT + "/eiproducers?ei_type_id=" + TYPE_ID;
745         resp = restClient().getForEntity(url).block();
746         assertThat(resp.getBody()).contains(PRODUCER_ID);
747
748         url = ProducerConsts.API_ROOT + "/eiproducers?ei_type_id=junk";
749         resp = restClient().getForEntity(url).block();
750         assertThat(resp.getBody()).isEqualTo("[]");
751     }
752
753     @Test
754     void producerSupervision() throws JsonMappingException, JsonProcessingException, ServiceException {
755
756         ConsumerSimulatorController.TestResults consumerResults = this.consumerSimulator.getTestResults();
757         putEiProducerWithOneTypeRejecting("simulateProducerError", TYPE_ID);
758
759         {
760             // Create a job
761             putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
762             putEiJob(TYPE_ID, EI_JOB_ID);
763             verifyJobStatus(EI_JOB_ID, "ENABLED");
764             deleteEiProducer(PRODUCER_ID);
765             // A Job disabled status notification shall now be received
766             await().untilAsserted(() -> assertThat(consumerResults.status.size()).isEqualTo(1));
767             assertThat(consumerResults.status.get(0).state).isEqualTo(A1eEiJobStatus.EiJobStatusValues.DISABLED);
768             verifyJobStatus(EI_JOB_ID, "DISABLED");
769         }
770
771         assertThat(this.eiProducers.size()).isEqualTo(1);
772         assertThat(this.eiTypes.size()).isEqualTo(1);
773         assertProducerOpState("simulateProducerError", ProducerStatusInfo.OperationalState.ENABLED);
774
775         this.producerSupervision.createTask().blockLast();
776         this.producerSupervision.createTask().blockLast();
777
778         // Now we have one producer that is disabled
779         assertThat(this.eiProducers.size()).isEqualTo(1);
780         assertProducerOpState("simulateProducerError", ProducerStatusInfo.OperationalState.DISABLED);
781
782         // After 3 failed checks, the producer shall be deregisterred
783         this.producerSupervision.createTask().blockLast();
784         assertThat(this.eiProducers.size()).isEqualTo(0); // The producer is removed
785         assertThat(this.eiTypes.size()).isEqualTo(1); // The type remains
786
787         // Now we have one disabled job, and no producer.
788         // PUT a producer, then a Job ENABLED status notification shall be received
789         putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
790         await().untilAsserted(() -> assertThat(consumerResults.status.size()).isEqualTo(2));
791         assertThat(consumerResults.status.get(1).state).isEqualTo(A1eEiJobStatus.EiJobStatusValues.ENABLED);
792         verifyJobStatus(EI_JOB_ID, "ENABLED");
793     }
794
795     @Test
796     void producerSupervision2() throws JsonMappingException, JsonProcessingException, ServiceException {
797         // Test that supervision enables not enabled jobs and sends a notification when
798         // suceeded
799
800         putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
801         putEiJob(TYPE_ID, EI_JOB_ID);
802
803         EiProducer producer = this.eiProducers.getProducer(PRODUCER_ID);
804         EiJob job = this.eiJobs.getJob(EI_JOB_ID);
805         // Pretend that the producer did reject the job and the a DISABLED notification
806         // is sent for the job
807         producer.setJobDisabled(job);
808         job.setLastReportedStatus(false);
809         verifyJobStatus(EI_JOB_ID, "DISABLED");
810
811         // Run the supervision and wait for the job to get started in the producer
812         this.producerSupervision.createTask().blockLast();
813         ConsumerSimulatorController.TestResults consumerResults = this.consumerSimulator.getTestResults();
814         await().untilAsserted(() -> assertThat(consumerResults.status.size()).isEqualTo(1));
815         assertThat(consumerResults.status.get(0).state).isEqualTo(A1eEiJobStatus.EiJobStatusValues.ENABLED);
816         verifyJobStatus(EI_JOB_ID, "ENABLED");
817     }
818
819     @Test
820     void testGetStatus() throws JsonMappingException, JsonProcessingException, ServiceException {
821         putEiProducerWithOneTypeRejecting("simulateProducerError", TYPE_ID);
822         putEiProducerWithOneTypeRejecting("simulateProducerError2", TYPE_ID);
823
824         String url = "/status";
825         ResponseEntity<String> resp = restClient().getForEntity(url).block();
826         assertThat(resp.getBody()).contains("hunky dory");
827     }
828
829     @Test
830     void testEiJobDatabase() throws Exception {
831         putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
832         putEiJob(TYPE_ID, "jobId1");
833         putEiJob(TYPE_ID, "jobId2");
834
835         assertThat(this.eiJobs.size()).isEqualTo(2);
836
837         {
838             EiJob savedJob = this.eiJobs.getJob("jobId1");
839             // Restore the jobs
840             EiJobs jobs = new EiJobs(this.applicationConfig, this.producerCallbacks);
841             jobs.restoreJobsFromDatabase();
842             assertThat(jobs.size()).isEqualTo(2);
843             EiJob restoredJob = jobs.getJob("jobId1");
844             assertThat(restoredJob.getId()).isEqualTo("jobId1");
845             assertThat(restoredJob.getLastUpdated()).isEqualTo(savedJob.getLastUpdated());
846
847             jobs.remove("jobId1", this.eiProducers);
848             jobs.remove("jobId2", this.eiProducers);
849         }
850         {
851             // Restore the jobs, no jobs in database
852             EiJobs jobs = new EiJobs(this.applicationConfig, this.producerCallbacks);
853             jobs.restoreJobsFromDatabase();
854             assertThat(jobs.size()).isEqualTo(0);
855         }
856         logger.warn("Test removing a job when the db file is gone");
857         this.eiJobs.remove("jobId1", this.eiProducers);
858         assertThat(this.eiJobs.size()).isEqualTo(1);
859
860         ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
861         await().untilAsserted(() -> assertThat(simulatorResults.jobsStopped.size()).isEqualTo(3));
862     }
863
864     @Test
865     void testEiTypesDatabase() throws Exception {
866         putEiProducerWithOneType(PRODUCER_ID, TYPE_ID);
867
868         assertThat(this.eiTypes.size()).isEqualTo(1);
869
870         {
871             // Restore the types
872             EiTypes types = new EiTypes(this.applicationConfig);
873             types.restoreTypesFromDatabase();
874             assertThat(types.size()).isEqualTo(1);
875
876         }
877         {
878             // Restore the jobs, no jobs in database
879             EiTypes types = new EiTypes(this.applicationConfig);
880             types.clear();
881             types.restoreTypesFromDatabase();
882             assertThat(types.size()).isEqualTo(0);
883         }
884         logger.warn("Test removing a job when the db file is gone");
885         this.eiTypes.remove(this.eiTypes.getType(TYPE_ID));
886         assertThat(this.eiJobs.size()).isEqualTo(0);
887     }
888
889     private void deleteEiProducer(String eiProducerId) {
890         String url = ProducerConsts.API_ROOT + "/eiproducers/" + eiProducerId;
891         restClient().deleteForEntity(url).block();
892     }
893
894     private void verifyJobStatus(String jobId, String expStatus) {
895         String url = A1eConsts.API_ROOT + "/eijobs/" + jobId + "/status";
896         String rsp = restClient().get(url).block();
897         assertThat(rsp).contains(expStatus);
898     }
899
900     private void assertProducerOpState(String producerId,
901         ProducerStatusInfo.OperationalState expectedOperationalState) {
902         String statusUrl = ProducerConsts.API_ROOT + "/eiproducers/" + producerId + "/status";
903         ResponseEntity<String> resp = restClient().getForEntity(statusUrl).block();
904         ProducerStatusInfo statusInfo = gson.fromJson(resp.getBody(), ProducerStatusInfo.class);
905         assertThat(statusInfo.opState).isEqualTo(expectedOperationalState);
906     }
907
908     ProducerEiTypeInfo producerEiTypeRegistrationInfo(String typeId)
909         throws JsonMappingException, JsonProcessingException {
910         return new ProducerEiTypeInfo(jsonSchemaObject());
911     }
912
913     ProducerRegistrationInfo producerEiRegistratioInfoRejecting(String typeId)
914         throws JsonMappingException, JsonProcessingException {
915         return new ProducerRegistrationInfo(Arrays.asList(typeId), //
916             baseUrl() + ProducerSimulatorController.JOB_ERROR_URL,
917             baseUrl() + ProducerSimulatorController.SUPERVISION_ERROR_URL);
918     }
919
920     ProducerRegistrationInfo producerEiRegistratioInfo(String typeId)
921         throws JsonMappingException, JsonProcessingException {
922         return new ProducerRegistrationInfo(Arrays.asList(typeId), //
923             baseUrl() + ProducerSimulatorController.JOB_URL, baseUrl() + ProducerSimulatorController.SUPERVISION_URL);
924     }
925
926     private ConsumerJobInfo consumerJobInfo() throws JsonMappingException, JsonProcessingException {
927         return consumerJobInfo(TYPE_ID, EI_JOB_ID);
928     }
929
930     ConsumerJobInfo consumerJobInfo(String typeId, String eiJobId)
931         throws JsonMappingException, JsonProcessingException {
932         return new ConsumerJobInfo(typeId, jsonObject(), "owner", "targetUri",
933             baseUrl() + ConsumerSimulatorController.getJobStatusUrl(eiJobId));
934     }
935
936     private A1eEiJobInfo eiJobInfo() throws JsonMappingException, JsonProcessingException {
937         return eiJobInfo(TYPE_ID, EI_JOB_ID);
938     }
939
940     A1eEiJobInfo eiJobInfo(String typeId, String eiJobId) throws JsonMappingException, JsonProcessingException {
941         return new A1eEiJobInfo(typeId, jsonObject(), "owner", "targetUri",
942             baseUrl() + ConsumerSimulatorController.getJobStatusUrl(eiJobId));
943     }
944
945     private Object jsonObject(String json) {
946         try {
947             return JsonParser.parseString(json).getAsJsonObject();
948         } catch (Exception e) {
949             throw new NullPointerException(e.toString());
950         }
951     }
952
953     private Object jsonSchemaObject() {
954         // a json schema with one mandatory property named "string"
955         String schemaStr = "{" //
956             + "\"$schema\": \"http://json-schema.org/draft-04/schema#\"," //
957             + "\"type\": \"object\"," //
958             + "\"properties\": {" //
959             + EI_JOB_PROPERTY + " : {" //
960             + "    \"type\": \"string\"" //
961             + "  }" //
962             + "}," //
963             + "\"required\": [" //
964             + EI_JOB_PROPERTY //
965             + "]" //
966             + "}"; //
967         return jsonObject(schemaStr);
968     }
969
970     private Object jsonObject() {
971         return jsonObject("{ " + EI_JOB_PROPERTY + " : \"value\" }");
972     }
973
974     private EiJob putEiJob(String eiTypeId, String jobId)
975         throws JsonMappingException, JsonProcessingException, ServiceException {
976
977         String url = A1eConsts.API_ROOT + "/eijobs/" + jobId;
978         String body = gson.toJson(eiJobInfo(eiTypeId, jobId));
979         restClient().putForEntity(url, body).block();
980
981         return this.eiJobs.getJob(jobId);
982     }
983
984     private HttpStatus putEiType(String eiTypeId)
985         throws JsonMappingException, JsonProcessingException, ServiceException {
986         String url = ProducerConsts.API_ROOT + "/eitypes/" + eiTypeId;
987         String body = gson.toJson(producerEiTypeRegistrationInfo(eiTypeId));
988
989         ResponseEntity<String> resp = restClient().putForEntity(url, body).block();
990         this.eiTypes.getType(eiTypeId);
991         return resp.getStatusCode();
992
993     }
994
995     private EiType putEiProducerWithOneTypeRejecting(String producerId, String eiTypeId)
996         throws JsonMappingException, JsonProcessingException, ServiceException {
997         this.putEiType(eiTypeId);
998         String url = ProducerConsts.API_ROOT + "/eiproducers/" + producerId;
999         String body = gson.toJson(producerEiRegistratioInfoRejecting(eiTypeId));
1000         restClient().putForEntity(url, body).block();
1001         return this.eiTypes.getType(eiTypeId);
1002     }
1003
1004     private EiType putEiProducerWithOneType(String producerId, String eiTypeId)
1005         throws JsonMappingException, JsonProcessingException, ServiceException {
1006         this.putEiType(eiTypeId);
1007
1008         String url = ProducerConsts.API_ROOT + "/eiproducers/" + producerId;
1009         String body = gson.toJson(producerEiRegistratioInfo(eiTypeId));
1010
1011         restClient().putForEntity(url, body).block();
1012
1013         return this.eiTypes.getType(eiTypeId);
1014     }
1015
1016     private String baseUrl() {
1017         return "https://localhost:" + this.port;
1018     }
1019
1020     private AsyncRestClient restClient(boolean useTrustValidation) {
1021         WebClientConfig config = this.applicationConfig.getWebClientConfig();
1022         HttpProxyConfig httpProxyConfig = ImmutableHttpProxyConfig.builder() //
1023             .httpProxyHost("") //
1024             .httpProxyPort(0) //
1025             .build();
1026         config = ImmutableWebClientConfig.builder() //
1027             .keyStoreType(config.keyStoreType()) //
1028             .keyStorePassword(config.keyStorePassword()) //
1029             .keyStore(config.keyStore()) //
1030             .keyPassword(config.keyPassword()) //
1031             .isTrustStoreUsed(useTrustValidation) //
1032             .trustStore(config.trustStore()) //
1033             .trustStorePassword(config.trustStorePassword()) //
1034             .httpProxyConfig(httpProxyConfig).build();
1035
1036         AsyncRestClientFactory restClientFactory = new AsyncRestClientFactory(config);
1037         return restClientFactory.createRestClientNoHttpProxy(baseUrl());
1038     }
1039
1040     private AsyncRestClient restClient() {
1041         return restClient(false);
1042     }
1043
1044     private void testErrorCode(Mono<?> request, HttpStatus expStatus, String responseContains) {
1045         testErrorCode(request, expStatus, responseContains, true);
1046     }
1047
1048     private void testErrorCode(Mono<?> request, HttpStatus expStatus, String responseContains,
1049         boolean expectApplicationProblemJsonMediaType) {
1050         StepVerifier.create(request) //
1051             .expectSubscription() //
1052             .expectErrorMatches(
1053                 t -> checkWebClientError(t, expStatus, responseContains, expectApplicationProblemJsonMediaType)) //
1054             .verify();
1055     }
1056
1057     private boolean checkWebClientError(Throwable throwable, HttpStatus expStatus, String responseContains,
1058         boolean expectApplicationProblemJsonMediaType) {
1059         assertTrue(throwable instanceof WebClientResponseException);
1060         WebClientResponseException responseException = (WebClientResponseException) throwable;
1061         assertThat(responseException.getStatusCode()).isEqualTo(expStatus);
1062         assertThat(responseException.getResponseBodyAsString()).contains(responseContains);
1063         if (expectApplicationProblemJsonMediaType) {
1064             assertThat(responseException.getHeaders().getContentType()).isEqualTo(MediaType.APPLICATION_PROBLEM_JSON);
1065         }
1066         return true;
1067     }
1068
1069 }