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