9cfe222fcac693854cfe358e134d9b0e27adf476
[nonrtric.git] / enrichment-coordinator-service / src / test / java / org / oransc / enrichment / ApplicationTest.java
1 /*-
2  * ========================LICENSE_START=================================
3  * ONAP : ccsdk oran
4  * ======================================================================
5  * Copyright (C) 2019-2020 Nordix Foundation. All rights reserved.
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.util.ArrayList;
34 import java.util.Collection;
35
36 import org.junit.jupiter.api.AfterEach;
37 import org.junit.jupiter.api.BeforeEach;
38 import org.junit.jupiter.api.Test;
39 import org.junit.jupiter.api.extension.ExtendWith;
40 import org.oransc.enrichment.clients.AsyncRestClient;
41 import org.oransc.enrichment.clients.ProducerJobInfo;
42 import org.oransc.enrichment.configuration.ApplicationConfig;
43 import org.oransc.enrichment.configuration.ImmutableWebClientConfig;
44 import org.oransc.enrichment.configuration.WebClientConfig;
45 import org.oransc.enrichment.controller.ProducerSimulatorController;
46 import org.oransc.enrichment.controllers.consumer.ConsumerConsts;
47 import org.oransc.enrichment.controllers.consumer.ConsumerEiJobInfo;
48 import org.oransc.enrichment.controllers.consumer.ConsumerEiTypeInfo;
49 import org.oransc.enrichment.controllers.producer.ProducerConsts;
50 import org.oransc.enrichment.controllers.producer.ProducerRegistrationInfo;
51 import org.oransc.enrichment.controllers.producer.ProducerRegistrationInfo.ProducerEiTypeRegistrationInfo;
52 import org.oransc.enrichment.controllers.producer.ProducerStatusInfo;
53 import org.oransc.enrichment.exceptions.ServiceException;
54 import org.oransc.enrichment.repository.EiJob;
55 import org.oransc.enrichment.repository.EiJobs;
56 import org.oransc.enrichment.repository.EiProducers;
57 import org.oransc.enrichment.repository.EiType;
58 import org.oransc.enrichment.repository.EiTypes;
59 import org.oransc.enrichment.tasks.ProducerSupervision;
60 import org.springframework.beans.factory.annotation.Autowired;
61 import org.springframework.boot.test.context.SpringBootTest;
62 import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
63 import org.springframework.boot.test.context.TestConfiguration;
64 import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
65 import org.springframework.boot.web.server.LocalServerPort;
66 import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
67 import org.springframework.context.ApplicationContext;
68 import org.springframework.context.annotation.Bean;
69 import org.springframework.http.HttpStatus;
70 import org.springframework.http.MediaType;
71 import org.springframework.http.ResponseEntity;
72 import org.springframework.test.context.TestPropertySource;
73 import org.springframework.test.context.junit.jupiter.SpringExtension;
74 import org.springframework.web.reactive.function.client.WebClientResponseException;
75
76 import reactor.core.publisher.Mono;
77 import reactor.test.StepVerifier;
78
79 @ExtendWith(SpringExtension.class)
80 @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
81 @TestPropertySource(
82     properties = { //
83         "server.ssl.key-store=./config/keystore.jks", //
84         "app.webclient.trust-store=./config/truststore.jks"})
85 class ApplicationTest {
86     private final String EI_TYPE_ID = "typeId";
87     private final String EI_PRODUCER_ID = "producerId";
88     private final String EI_JOB_PROPERTY = "\"property1\"";
89
90     @Autowired
91     ApplicationContext context;
92
93     @Autowired
94     EiJobs eiJobs;
95
96     @Autowired
97     EiTypes eiTypes;
98
99     @Autowired
100     EiProducers eiProducers;
101
102     @Autowired
103     ApplicationConfig applicationConfig;
104
105     @Autowired
106     ProducerSimulatorController producerSimulator;
107
108     @Autowired
109     ProducerSupervision producerSupervision;
110
111     private static Gson gson = new GsonBuilder() //
112         .serializeNulls() //
113         .create(); //
114
115     /**
116      * Overrides the BeanFactory.
117      */
118     @TestConfiguration
119     static class TestBeanFactory {
120         @Bean
121         public ServletWebServerFactory servletContainer() {
122             return new TomcatServletWebServerFactory();
123         }
124     }
125
126     @LocalServerPort
127     private int port;
128
129     @BeforeEach
130     void reset() {
131         this.eiJobs.clear();
132         this.eiTypes.clear();
133         this.eiProducers.clear();
134         this.producerSimulator.getTestResults().reset();
135     }
136
137     @AfterEach
138     void check() {
139         assertThat(this.producerSimulator.getTestResults().errorFound).isFalse();
140     }
141
142     @Test
143     void testGetEiTypes() throws Exception {
144         putEiProducerWithOneType(EI_PRODUCER_ID, "test");
145         String url = ConsumerConsts.API_ROOT + "/eitypes";
146         String rsp = restClient().get(url).block();
147         assertThat(rsp).isEqualTo("[\"test\"]");
148     }
149
150     @Test
151     void testGetEiType() throws Exception {
152         putEiProducerWithOneType(EI_PRODUCER_ID, "test");
153         String url = ConsumerConsts.API_ROOT + "/eitypes/test";
154         String rsp = restClient().get(url).block();
155         ConsumerEiTypeInfo info = gson.fromJson(rsp, ConsumerEiTypeInfo.class);
156         assertThat(info.jobParametersSchema).isNotNull();
157     }
158
159     @Test
160     void testGetEiTypeNotFound() throws Exception {
161         String url = ConsumerConsts.API_ROOT + "/eitypes/junk";
162         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND, "Could not find EI type: junk");
163     }
164
165     @Test
166     void testGetEiJobsIds() throws Exception {
167         putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
168         putEiJob(EI_TYPE_ID, "jobId");
169         String url = ConsumerConsts.API_ROOT + "/eitypes/typeId/eijobs";
170         String rsp = restClient().get(url).block();
171         assertThat(rsp).isEqualTo("[\"jobId\"]");
172     }
173
174     @Test
175     void testGetEiJobTypeNotFound() throws Exception {
176         String url = ConsumerConsts.API_ROOT + "/eitypes/junk/eijobs";
177         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND, "Could not find EI type: junk");
178     }
179
180     @Test
181     void testGetEiJob() throws Exception {
182         putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
183         putEiJob(EI_TYPE_ID, "jobId");
184         String url = ConsumerConsts.API_ROOT + "/eitypes/typeId/eijobs/jobId";
185         String rsp = restClient().get(url).block();
186         ConsumerEiJobInfo info = gson.fromJson(rsp, ConsumerEiJobInfo.class);
187         assertThat(info.owner).isEqualTo("owner");
188     }
189
190     @Test
191     void testGetEiJobNotFound() throws Exception {
192         putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
193         String url = ConsumerConsts.API_ROOT + "/eitypes/typeId/eijobs/junk";
194         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND, "Could not find EI job: junk");
195     }
196
197     @Test
198     void testGetEiJobStatus() throws Exception {
199         putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
200         putEiJob(EI_TYPE_ID, "jobId");
201         String url = ConsumerConsts.API_ROOT + "/eitypes/typeId/eijobs/jobId/status";
202         String rsp = restClient().get(url).block();
203         assertThat(rsp).contains("ENABLED");
204     }
205
206     // Status TBD
207
208     @Test
209     void testDeleteEiJob() throws Exception {
210         putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
211         putEiJob(EI_TYPE_ID, "jobId");
212         assertThat(this.eiJobs.size()).isEqualTo(1);
213         String url = ConsumerConsts.API_ROOT + "/eitypes/typeId/eijobs/jobId";
214         restClient().delete(url).block();
215         assertThat(this.eiJobs.size()).isZero();
216
217         ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
218         await().untilAsserted(() -> assertThat(simulatorResults.jobsStopped.size()).isEqualTo(1));
219         assertThat(simulatorResults.jobsStopped.get(0).id).isEqualTo("jobId");
220     }
221
222     @Test
223     void testDeleteEiJobNotFound() throws Exception {
224         putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
225         String url = ConsumerConsts.API_ROOT + "/eitypes/typeId/eijobs/junk";
226         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND, "Could not find EI job: junk");
227     }
228
229     @Test
230     void testPutEiJob() throws Exception {
231         // Test that one producer accepting a job is enough
232         putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
233         putEiProducerWithOneTypeRejecting("simulateProducerError", EI_TYPE_ID);
234
235         String url = ConsumerConsts.API_ROOT + "/eitypes/typeId/eijobs/jobId";
236         String body = gson.toJson(eiJobInfo());
237         ResponseEntity<String> resp = restClient().putForEntity(url, body).block();
238         assertThat(this.eiJobs.size()).isEqualTo(1);
239         assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.CREATED);
240
241         ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
242         await().untilAsserted(() -> assertThat(simulatorResults.jobsStarted.size()).isEqualTo(1));
243         ProducerJobInfo request = simulatorResults.jobsStarted.get(0);
244         assertThat(request.id).isEqualTo("jobId");
245
246         assertThat(simulatorResults.noOfRejectedCreate).isEqualTo(1);
247
248         resp = restClient().putForEntity(url, body).block();
249         assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
250         EiJob job = this.eiJobs.getJob("jobId");
251         assertThat(job.owner()).isEqualTo("owner");
252     }
253
254     @Test
255     void putEiProducerWithOneType_rejecting() throws JsonMappingException, JsonProcessingException, ServiceException {
256         putEiProducerWithOneTypeRejecting("simulateProducerError", EI_TYPE_ID);
257         String url = ConsumerConsts.API_ROOT + "/eitypes/typeId/eijobs/jobId";
258         String body = gson.toJson(eiJobInfo());
259         testErrorCode(restClient().put(url, body), HttpStatus.CONFLICT, "Job not accepted by any producers");
260
261         ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
262         assertThat(simulatorResults.noOfRejectedCreate).isEqualTo(1);
263     }
264
265     @Test
266     void testPutEiJob_jsonSchemavalidationError() throws Exception {
267         putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
268
269         String url = ConsumerConsts.API_ROOT + "/eitypes/typeId/eijobs/jobId";
270         // The element with name "property1" is mandatory in the schema
271         ConsumerEiJobInfo jobInfo =
272             new ConsumerEiJobInfo(jsonObject("{ \"XXstring\" : \"value\" }"), "owner", "targetUri");
273         String body = gson.toJson(jobInfo);
274
275         testErrorCode(restClient().put(url, body), HttpStatus.CONFLICT, "Json validation failure");
276     }
277
278     @Test
279     void testGetEiProducerTypes() throws Exception {
280         final String EI_TYPE_ID_2 = EI_TYPE_ID + "_2";
281         putEiProducerWithOneType("producer1", EI_TYPE_ID);
282         putEiJob(EI_TYPE_ID, "jobId");
283         putEiProducerWithOneType("producer2", EI_TYPE_ID_2);
284         putEiJob(EI_TYPE_ID_2, "jobId2");
285         String url = ProducerConsts.API_ROOT + "/eitypes";
286
287         ResponseEntity<String> resp = restClient().getForEntity(url).block();
288         assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
289         assertThat(resp.getBody()).contains(EI_TYPE_ID);
290         assertThat(resp.getBody()).contains(EI_TYPE_ID_2);
291     }
292
293     @Test
294     void testReplacingEiProducerTypes() throws Exception {
295         final String REPLACED_TYPE_ID = "replaced";
296         putEiProducerWithOneType(EI_PRODUCER_ID, REPLACED_TYPE_ID);
297         putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
298
299         String url = ProducerConsts.API_ROOT + "/eitypes";
300
301         ResponseEntity<String> resp = restClient().getForEntity(url).block();
302         assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
303         assertThat(resp.getBody()).contains(EI_TYPE_ID);
304         assertThat(resp.getBody()).doesNotContain(REPLACED_TYPE_ID);
305     }
306
307     @Test
308     void testChangingEiTypeGetRejected() throws Exception {
309         putEiProducerWithOneType("producer1", "typeId1");
310         putEiProducerWithOneType("producer2", "typeId2");
311         putEiJob("typeId1", "jobId");
312
313         String url = ConsumerConsts.API_ROOT + "/eitypes/typeId2/eijobs/jobId";
314         String body = gson.toJson(eiJobInfo());
315         testErrorCode(restClient().put(url, body), HttpStatus.CONFLICT,
316             "Not allowed to change type for existing EI job");
317     }
318
319     @Test
320     void testPutEiProducer() throws Exception {
321         String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId";
322         String body = gson.toJson(producerEiRegistratioInfo(EI_TYPE_ID));
323
324         ResponseEntity<String> resp = restClient().putForEntity(url, body).block();
325         assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.CREATED);
326
327         assertThat(this.eiTypes.size()).isEqualTo(1);
328         EiType type = this.eiTypes.getType(EI_TYPE_ID);
329         assertThat(type.getProducerIds()).contains("eiProducerId");
330         assertThat(this.eiProducers.size()).isEqualTo(1);
331         assertThat(this.eiProducers.get("eiProducerId").getEiTypes().iterator().next().getId()).isEqualTo(EI_TYPE_ID);
332
333         resp = restClient().putForEntity(url, body).block();
334         assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
335
336         resp = restClient().getForEntity(url).block();
337         assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
338         assertThat(resp.getBody()).isEqualTo(body);
339     }
340
341     @Test
342     void testPutEiProducerExistingJob() throws Exception {
343         putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
344         putEiJob(EI_TYPE_ID, "jobId");
345         String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId";
346         String body = gson.toJson(producerEiRegistratioInfo(EI_TYPE_ID));
347         restClient().putForEntity(url, body).block();
348
349         ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
350         await().untilAsserted(() -> assertThat(simulatorResults.jobsStarted.size()).isEqualTo(2));
351         ProducerJobInfo request = simulatorResults.jobsStarted.get(0);
352         assertThat(request.id).isEqualTo("jobId");
353     }
354
355     @Test
356     void testPutProducerAndEiJob() throws Exception {
357         String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId";
358         String body = gson.toJson(producerEiRegistratioInfo(EI_TYPE_ID));
359         restClient().putForEntity(url, body).block();
360         assertThat(this.eiTypes.size()).isEqualTo(1);
361         this.eiTypes.getType(EI_TYPE_ID);
362
363         url = ConsumerConsts.API_ROOT + "/eitypes/typeId/eijobs/jobId";
364         body = gson.toJson(eiJobInfo());
365         restClient().putForEntity(url, body).block();
366
367         ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
368         await().untilAsserted(() -> assertThat(simulatorResults.jobsStarted.size()).isEqualTo(1));
369         ProducerJobInfo request = simulatorResults.jobsStarted.get(0);
370         assertThat(request.id).isEqualTo("jobId");
371     }
372
373     @Test
374     void testGetEiJobsForProducer() throws JsonMappingException, JsonProcessingException, ServiceException {
375         putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
376         putEiJob(EI_TYPE_ID, "jobId1");
377         putEiJob(EI_TYPE_ID, "jobId2");
378
379         // PUT a consumer
380         String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId";
381         String body = gson.toJson(producerEiRegistratioInfo(EI_TYPE_ID));
382         restClient().putForEntity(url, body).block();
383
384         url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId/eijobs";
385         ResponseEntity<String> resp = restClient().getForEntity(url).block();
386         assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
387
388         ProducerJobInfo[] parsedResp = gson.fromJson(resp.getBody(), ProducerJobInfo[].class);
389         assertThat(parsedResp[0].typeId).isEqualTo(EI_TYPE_ID);
390         assertThat(parsedResp[1].typeId).isEqualTo(EI_TYPE_ID);
391     }
392
393     @Test
394     void testDeleteEiProducer() throws Exception {
395         putEiProducerWithOneType("eiProducerId", EI_TYPE_ID);
396         putEiProducerWithOneType("eiProducerId2", EI_TYPE_ID);
397
398         assertThat(this.eiProducers.size()).isEqualTo(2);
399         EiType type = this.eiTypes.getType(EI_TYPE_ID);
400         assertThat(type.getProducerIds()).contains("eiProducerId");
401         assertThat(type.getProducerIds()).contains("eiProducerId2");
402         putEiJob(EI_TYPE_ID, "jobId");
403         assertThat(this.eiJobs.size()).isEqualTo(1);
404
405         String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId";
406         restClient().deleteForEntity(url).block();
407         assertThat(this.eiProducers.size()).isEqualTo(1);
408         assertThat(this.eiTypes.getType(EI_TYPE_ID).getProducerIds()).doesNotContain("eiProducerId");
409         assertThat(this.eiJobs.size()).isEqualTo(1);
410
411         String url2 = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId2";
412         restClient().deleteForEntity(url2).block();
413         assertThat(this.eiProducers.size()).isZero();
414         assertThat(this.eiTypes.size()).isZero();
415         assertThat(this.eiJobs.size()).isZero();
416     }
417
418     @Test
419     void testGetProducerEiType() throws JsonMappingException, JsonProcessingException, ServiceException {
420         putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
421         String url = ProducerConsts.API_ROOT + "/eitypes/" + EI_TYPE_ID;
422         ResponseEntity<String> resp = restClient().getForEntity(url).block();
423         assertThat(resp.getBody()).contains(EI_PRODUCER_ID);
424     }
425
426     @Test
427     void testGetProducerIdentifiers() throws JsonMappingException, JsonProcessingException, ServiceException {
428         putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
429         String url = ProducerConsts.API_ROOT + "/eiproducers";
430         ResponseEntity<String> resp = restClient().getForEntity(url).block();
431         assertThat(resp.getBody()).contains(EI_PRODUCER_ID);
432     }
433
434     private void assertProducerOpState(String producerId,
435         ProducerStatusInfo.OperationalState expectedOperationalState) {
436         String statusUrl = ProducerConsts.API_ROOT + "/eiproducers/" + producerId + "/status";
437         ResponseEntity<String> resp = restClient().getForEntity(statusUrl).block();
438         ProducerStatusInfo statusInfo = gson.fromJson(resp.getBody(), ProducerStatusInfo.class);
439         assertThat(statusInfo.opState).isEqualTo(expectedOperationalState);
440     }
441
442     @Test
443     void testProducerSupervision() throws JsonMappingException, JsonProcessingException, ServiceException {
444         putEiProducerWithOneTypeRejecting("simulateProducerError", EI_TYPE_ID);
445
446         assertThat(this.eiProducers.size()).isEqualTo(1);
447         assertThat(this.eiTypes.size()).isEqualTo(1);
448         assertProducerOpState("simulateProducerError", ProducerStatusInfo.OperationalState.ENABLED);
449
450         this.producerSupervision.createTask().blockLast();
451         this.producerSupervision.createTask().blockLast();
452         assertThat(this.eiProducers.size()).isEqualTo(1);
453         assertProducerOpState("simulateProducerError", ProducerStatusInfo.OperationalState.DISABLED);
454
455         // After 3 failed checks, the producer shall be deregisterred
456         this.producerSupervision.createTask().blockLast();
457         assertThat(this.eiProducers.size()).isEqualTo(0);
458         assertThat(this.eiTypes.size()).isEqualTo(0);
459     }
460
461     ProducerEiTypeRegistrationInfo producerEiTypeRegistrationInfo(String typeId)
462         throws JsonMappingException, JsonProcessingException {
463         return new ProducerEiTypeRegistrationInfo(jsonSchemaObject(), typeId);
464     }
465
466     ProducerRegistrationInfo producerEiRegistratioInfoRejecting(String typeId)
467         throws JsonMappingException, JsonProcessingException {
468         Collection<ProducerEiTypeRegistrationInfo> types = new ArrayList<>();
469         types.add(producerEiTypeRegistrationInfo(typeId));
470         return new ProducerRegistrationInfo(types, //
471             baseUrl() + ProducerSimulatorController.JOB_CREATED_ERROR_URL,
472             baseUrl() + ProducerSimulatorController.JOB_DELETED_ERROR_URL,
473             baseUrl() + ProducerSimulatorController.SUPERVISION_ERROR_URL);
474     }
475
476     ProducerRegistrationInfo producerEiRegistratioInfo(String typeId)
477         throws JsonMappingException, JsonProcessingException {
478         Collection<ProducerEiTypeRegistrationInfo> types = new ArrayList<>();
479         types.add(producerEiTypeRegistrationInfo(typeId));
480         return new ProducerRegistrationInfo(types, //
481             baseUrl() + ProducerSimulatorController.JOB_CREATED_URL,
482             baseUrl() + ProducerSimulatorController.JOB_DELETED_URL,
483             baseUrl() + ProducerSimulatorController.SUPERVISION_URL);
484     }
485
486     ConsumerEiJobInfo eiJobInfo() throws JsonMappingException, JsonProcessingException {
487         return new ConsumerEiJobInfo(jsonObject(), "owner", "targetUri");
488     }
489
490     Object jsonObject(String json) {
491         try {
492             return JsonParser.parseString(json).getAsJsonObject();
493         } catch (Exception e) {
494             throw new NullPointerException(e.toString());
495         }
496     }
497
498     Object jsonSchemaObject() {
499         // a json schema with one mandatory property named "string"
500         String schemaStr = "{" //
501             + "\"$schema\": \"http://json-schema.org/draft-04/schema#\"," //
502             + "\"type\": \"object\"," //
503             + "\"properties\": {" //
504             + EI_JOB_PROPERTY + " : {" //
505             + "    \"type\": \"string\"" //
506             + "  }" //
507             + "}," //
508             + "\"required\": [" //
509             + EI_JOB_PROPERTY //
510             + "]" //
511             + "}"; //
512         return jsonObject(schemaStr);
513     }
514
515     Object jsonObject() {
516         return jsonObject("{ " + EI_JOB_PROPERTY + " : \"value\" }");
517     }
518
519     private EiJob putEiJob(String eiTypeId, String jobId)
520         throws JsonMappingException, JsonProcessingException, ServiceException {
521
522         String url = ConsumerConsts.API_ROOT + "/eitypes/" + eiTypeId + "/eijobs/" + jobId;
523         String body = gson.toJson(eiJobInfo());
524         restClient().putForEntity(url, body).block();
525
526         return this.eiJobs.getJob(jobId);
527     }
528
529     private EiType putEiProducerWithOneTypeRejecting(String producerId, String eiTypeId)
530         throws JsonMappingException, JsonProcessingException, ServiceException {
531         String url = ProducerConsts.API_ROOT + "/eiproducers/" + producerId;
532         String body = gson.toJson(producerEiRegistratioInfoRejecting(eiTypeId));
533
534         restClient().putForEntity(url, body).block();
535         return this.eiTypes.getType(eiTypeId);
536     }
537
538     private EiType putEiProducerWithOneType(String producerId, String eiTypeId)
539         throws JsonMappingException, JsonProcessingException, ServiceException {
540         String url = ProducerConsts.API_ROOT + "/eiproducers/" + producerId;
541         String body = gson.toJson(producerEiRegistratioInfo(eiTypeId));
542
543         restClient().putForEntity(url, body).block();
544         return this.eiTypes.getType(eiTypeId);
545     }
546
547     private String baseUrl() {
548         return "https://localhost:" + this.port;
549     }
550
551     private AsyncRestClient restClient(boolean useTrustValidation) {
552         WebClientConfig config = this.applicationConfig.getWebClientConfig();
553         config = ImmutableWebClientConfig.builder() //
554             .keyStoreType(config.keyStoreType()) //
555             .keyStorePassword(config.keyStorePassword()) //
556             .keyStore(config.keyStore()) //
557             .keyPassword(config.keyPassword()) //
558             .isTrustStoreUsed(useTrustValidation) //
559             .trustStore(config.trustStore()) //
560             .trustStorePassword(config.trustStorePassword()) //
561             .build();
562
563         return new AsyncRestClient(baseUrl(), config);
564     }
565
566     private AsyncRestClient restClient() {
567         return restClient(false);
568     }
569
570     private void testErrorCode(Mono<?> request, HttpStatus expStatus, String responseContains) {
571         testErrorCode(request, expStatus, responseContains, true);
572     }
573
574     private void testErrorCode(Mono<?> request, HttpStatus expStatus, String responseContains,
575         boolean expectApplicationProblemJsonMediaType) {
576         StepVerifier.create(request) //
577             .expectSubscription() //
578             .expectErrorMatches(
579                 t -> checkWebClientError(t, expStatus, responseContains, expectApplicationProblemJsonMediaType)) //
580             .verify();
581     }
582
583     private boolean checkWebClientError(Throwable throwable, HttpStatus expStatus, String responseContains,
584         boolean expectApplicationProblemJsonMediaType) {
585         assertTrue(throwable instanceof WebClientResponseException);
586         WebClientResponseException responseException = (WebClientResponseException) throwable;
587         assertThat(responseException.getStatusCode()).isEqualTo(expStatus);
588         assertThat(responseException.getResponseBodyAsString()).contains(responseContains);
589         if (expectApplicationProblemJsonMediaType) {
590             assertThat(responseException.getHeaders().getContentType()).isEqualTo(MediaType.APPLICATION_PROBLEM_JSON);
591         }
592         return true;
593     }
594
595 }