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