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