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