Added information in the GET consumer job-status
[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.lang.invoke.MethodHandles;
37 import java.util.Arrays;
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.ImmutableHttpProxyConfig;
48 import org.oransc.enrichment.configuration.ImmutableWebClientConfig;
49 import org.oransc.enrichment.configuration.WebClientConfig;
50 import org.oransc.enrichment.configuration.WebClientConfig.HttpProxyConfig;
51 import org.oransc.enrichment.controller.ConsumerSimulatorController;
52 import org.oransc.enrichment.controller.ProducerSimulatorController;
53 import org.oransc.enrichment.controllers.a1e.A1eConsts;
54 import org.oransc.enrichment.controllers.a1e.A1eEiJobInfo;
55 import org.oransc.enrichment.controllers.a1e.A1eEiJobStatus;
56 import org.oransc.enrichment.controllers.a1e.A1eEiTypeInfo;
57 import org.oransc.enrichment.controllers.r1consumer.ConsumerConsts;
58 import org.oransc.enrichment.controllers.r1consumer.ConsumerInfoTypeInfo;
59 import org.oransc.enrichment.controllers.r1consumer.ConsumerJobInfo;
60 import org.oransc.enrichment.controllers.r1consumer.ConsumerJobStatus;
61 import org.oransc.enrichment.controllers.r1producer.ProducerCallbacks;
62 import org.oransc.enrichment.controllers.r1producer.ProducerConsts;
63 import org.oransc.enrichment.controllers.r1producer.ProducerInfoTypeInfo;
64 import org.oransc.enrichment.controllers.r1producer.ProducerJobInfo;
65 import org.oransc.enrichment.controllers.r1producer.ProducerRegistrationInfo;
66 import org.oransc.enrichment.controllers.r1producer.ProducerStatusInfo;
67 import org.oransc.enrichment.exceptions.ServiceException;
68 import org.oransc.enrichment.repository.InfoJob;
69 import org.oransc.enrichment.repository.InfoJobs;
70 import org.oransc.enrichment.repository.InfoProducer;
71 import org.oransc.enrichment.repository.InfoProducers;
72 import org.oransc.enrichment.repository.InfoType;
73 import org.oransc.enrichment.repository.InfoTypes;
74 import org.oransc.enrichment.tasks.ProducerSupervision;
75 import org.slf4j.Logger;
76 import org.slf4j.LoggerFactory;
77 import org.springframework.beans.factory.annotation.Autowired;
78 import org.springframework.boot.test.context.SpringBootTest;
79 import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
80 import org.springframework.boot.test.context.TestConfiguration;
81 import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
82 import org.springframework.boot.web.server.LocalServerPort;
83 import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
84 import org.springframework.context.ApplicationContext;
85 import org.springframework.context.annotation.Bean;
86 import org.springframework.http.HttpStatus;
87 import org.springframework.http.MediaType;
88 import org.springframework.http.ResponseEntity;
89 import org.springframework.test.context.TestPropertySource;
90 import org.springframework.test.context.junit.jupiter.SpringExtension;
91 import org.springframework.web.reactive.function.client.WebClientResponseException;
92
93 import reactor.core.publisher.Mono;
94 import reactor.test.StepVerifier;
95
96 @ExtendWith(SpringExtension.class)
97 @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
98 @TestPropertySource(
99     properties = { //
100         "server.ssl.key-store=./config/keystore.jks", //
101         "app.webclient.trust-store=./config/truststore.jks", //
102         "app.vardata-directory=./target"})
103 class ApplicationTest {
104     private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
105
106     private final String TYPE_ID = "typeId";
107     private final String PRODUCER_ID = "producerId";
108     private final String EI_JOB_PROPERTY = "\"property1\"";
109     private final String EI_JOB_ID = "jobId";
110
111     @Autowired
112     ApplicationContext context;
113
114     @Autowired
115     InfoJobs infoJobs;
116
117     @Autowired
118     InfoTypes infoTypes;
119
120     @Autowired
121     InfoProducers infoProducers;
122
123     @Autowired
124     ApplicationConfig applicationConfig;
125
126     @Autowired
127     ProducerSimulatorController producerSimulator;
128
129     @Autowired
130     ConsumerSimulatorController consumerSimulator;
131
132     @Autowired
133     ProducerSupervision producerSupervision;
134
135     @Autowired
136     ProducerCallbacks producerCallbacks;
137
138     private static Gson gson = new GsonBuilder().create();
139
140     /**
141      * Overrides the BeanFactory.
142      */
143     @TestConfiguration
144     static class TestBeanFactory {
145         @Bean
146         public ServletWebServerFactory servletContainer() {
147             return new TomcatServletWebServerFactory();
148         }
149     }
150
151     @LocalServerPort
152     private int port;
153
154     @BeforeEach
155     void reset() {
156         this.infoJobs.clear();
157         this.infoTypes.clear();
158         this.infoProducers.clear();
159         this.producerSimulator.getTestResults().reset();
160         this.consumerSimulator.getTestResults().reset();
161     }
162
163     @AfterEach
164     void check() {
165         assertThat(this.producerSimulator.getTestResults().errorFound).isFalse();
166     }
167
168     @Test
169     void generateApiDoc() throws FileNotFoundException {
170         String url = "/v3/api-docs";
171         ResponseEntity<String> resp = restClient().getForEntity(url).block();
172         assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
173
174         JSONObject jsonObj = new JSONObject(resp.getBody());
175         assertThat(jsonObj.remove("servers")).isNotNull();
176
177         String indented = jsonObj.toString(4);
178         try (PrintStream out = new PrintStream(new FileOutputStream("api/ecs-api.json"))) {
179             out.print(indented);
180         }
181     }
182
183     @Test
184     void a1eGetEiTypes() throws Exception {
185         putInfoProducerWithOneType(PRODUCER_ID, "test");
186         String url = A1eConsts.API_ROOT + "/eitypes";
187         String rsp = restClient().get(url).block();
188         assertThat(rsp).isEqualTo("[\"test\"]");
189     }
190
191     @Test
192     void consumerGetInfoTypes() throws Exception {
193         putInfoProducerWithOneType(PRODUCER_ID, "test");
194         String url = ConsumerConsts.API_ROOT + "/info-types";
195         String rsp = restClient().get(url).block();
196         assertThat(rsp).isEqualTo("[\"test\"]");
197     }
198
199     @Test
200     void a1eGetEiTypesEmpty() throws Exception {
201         String url = A1eConsts.API_ROOT + "/eitypes";
202         String rsp = restClient().get(url).block();
203         assertThat(rsp).isEqualTo("[]");
204     }
205
206     @Test
207     void consumerGetEiTypesEmpty() throws Exception {
208         String url = ConsumerConsts.API_ROOT + "/info-types";
209         String rsp = restClient().get(url).block();
210         assertThat(rsp).isEqualTo("[]");
211     }
212
213     @Test
214     void a1eGetEiType() throws Exception {
215         putInfoProducerWithOneType(PRODUCER_ID, "test");
216         String url = A1eConsts.API_ROOT + "/eitypes/test";
217         String rsp = restClient().get(url).block();
218         A1eEiTypeInfo info = gson.fromJson(rsp, A1eEiTypeInfo.class);
219         assertThat(info).isNotNull();
220     }
221
222     @Test
223     void consumerGetEiType() throws Exception {
224         putInfoProducerWithOneType(PRODUCER_ID, "test");
225         String url = ConsumerConsts.API_ROOT + "/info-types/test";
226         String rsp = restClient().get(url).block();
227         ConsumerInfoTypeInfo info = gson.fromJson(rsp, ConsumerInfoTypeInfo.class);
228         assertThat(info).isNotNull();
229         assertThat(info.jobDataSchema).isNotNull();
230     }
231
232     @Test
233     void a1eGetEiTypeNotFound() throws Exception {
234         String url = A1eConsts.API_ROOT + "/eitypes/junk";
235         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND, "Information type not found: junk");
236     }
237
238     @Test
239     void consumerGetEiTypeNotFound() throws Exception {
240         String url = ConsumerConsts.API_ROOT + "/info-types/junk";
241         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND, "Information type not found: junk");
242     }
243
244     @Test
245     void a1eGetEiJobsIds() throws Exception {
246         putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
247         putEiJob(TYPE_ID, "jobId");
248         final String JOB_ID_JSON = "[\"jobId\"]";
249         String url = A1eConsts.API_ROOT + "/eijobs?infoTypeId=typeId";
250         String rsp = restClient().get(url).block();
251         assertThat(rsp).isEqualTo(JOB_ID_JSON);
252
253         url = A1eConsts.API_ROOT + "/eijobs?owner=owner";
254         rsp = restClient().get(url).block();
255         assertThat(rsp).isEqualTo(JOB_ID_JSON);
256
257         url = A1eConsts.API_ROOT + "/eijobs?owner=JUNK";
258         rsp = restClient().get(url).block();
259         assertThat(rsp).isEqualTo("[]");
260
261         url = A1eConsts.API_ROOT + "/eijobs";
262         rsp = restClient().get(url).block();
263         assertThat(rsp).isEqualTo(JOB_ID_JSON);
264
265         url = A1eConsts.API_ROOT + "/eijobs?eiTypeId=typeId&&owner=owner";
266         rsp = restClient().get(url).block();
267         assertThat(rsp).isEqualTo(JOB_ID_JSON);
268
269         url = A1eConsts.API_ROOT + "/eijobs?eiTypeId=JUNK";
270         rsp = restClient().get(url).block();
271         assertThat(rsp).isEqualTo("[]");
272     }
273
274     @Test
275     void consumerGetInformationJobsIds() throws Exception {
276         putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
277         putEiJob(TYPE_ID, "jobId");
278         final String JOB_ID_JSON = "[\"jobId\"]";
279         String url = ConsumerConsts.API_ROOT + "/info-jobs?infoTypeId=typeId";
280         String rsp = restClient().get(url).block();
281         assertThat(rsp).isEqualTo(JOB_ID_JSON);
282
283         url = ConsumerConsts.API_ROOT + "/info-jobs?owner=owner";
284         rsp = restClient().get(url).block();
285         assertThat(rsp).isEqualTo(JOB_ID_JSON);
286
287         url = ConsumerConsts.API_ROOT + "/info-jobs?owner=JUNK";
288         rsp = restClient().get(url).block();
289         assertThat(rsp).isEqualTo("[]");
290
291         url = ConsumerConsts.API_ROOT + "/info-jobs";
292         rsp = restClient().get(url).block();
293         assertThat(rsp).isEqualTo(JOB_ID_JSON);
294
295         url = ConsumerConsts.API_ROOT + "/info-jobs?infoTypeId=typeId&&owner=owner";
296         rsp = restClient().get(url).block();
297         assertThat(rsp).isEqualTo(JOB_ID_JSON);
298
299         url = ConsumerConsts.API_ROOT + "/info-jobs?infoTypeId=JUNK";
300         rsp = restClient().get(url).block();
301         assertThat(rsp).isEqualTo("[]");
302     }
303
304     @Test
305     void a1eGetEiJob() throws Exception {
306         putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
307         putEiJob(TYPE_ID, "jobId");
308         String url = A1eConsts.API_ROOT + "/eijobs/jobId";
309         String rsp = restClient().get(url).block();
310         A1eEiJobInfo info = gson.fromJson(rsp, A1eEiJobInfo.class);
311         assertThat(info.owner).isEqualTo("owner");
312         assertThat(info.eiTypeId).isEqualTo(TYPE_ID);
313     }
314
315     @Test
316     void consumerGetEiJob() throws Exception {
317         putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
318         putEiJob(TYPE_ID, "jobId");
319         String url = ConsumerConsts.API_ROOT + "/info-jobs/jobId";
320         String rsp = restClient().get(url).block();
321         ConsumerJobInfo info = gson.fromJson(rsp, ConsumerJobInfo.class);
322         assertThat(info.owner).isEqualTo("owner");
323         assertThat(info.infoTypeId).isEqualTo(TYPE_ID);
324     }
325
326     @Test
327     void a1eGetEiJobNotFound() throws Exception {
328         putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
329         String url = A1eConsts.API_ROOT + "/eijobs/junk";
330         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND, "Could not find Information job: junk");
331     }
332
333     @Test
334     void consumerGetInfoJobNotFound() throws Exception {
335         putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
336         String url = ConsumerConsts.API_ROOT + "/info-jobs/junk";
337         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND, "Could not find Information job: junk");
338     }
339
340     @Test
341     void a1eGetEiJobStatus() throws Exception {
342         putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
343         putEiJob(TYPE_ID, "jobId");
344
345         verifyJobStatus("jobId", "ENABLED");
346     }
347
348     @Test
349     void consumerGetEiJobStatus() throws Exception {
350         putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
351         putEiJob(TYPE_ID, "jobId");
352
353         String url = ConsumerConsts.API_ROOT + "/info-jobs/jobId/status";
354         String rsp = restClient().get(url).block();
355         assertThat(rsp).contains("ENABLED");
356         assertThat(rsp).contains(PRODUCER_ID);
357
358         ConsumerJobStatus status = gson.fromJson(rsp, ConsumerJobStatus.class);
359         assertThat(status.producers).contains(PRODUCER_ID);
360     }
361
362     @Test
363     void a1eDeleteEiJob() throws Exception {
364         putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
365         putEiJob(TYPE_ID, "jobId");
366         assertThat(this.infoJobs.size()).isEqualTo(1);
367         String url = A1eConsts.API_ROOT + "/eijobs/jobId";
368         restClient().delete(url).block();
369         assertThat(this.infoJobs.size()).isZero();
370
371         ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
372         await().untilAsserted(() -> assertThat(simulatorResults.jobsStopped.size()).isEqualTo(1));
373         assertThat(simulatorResults.jobsStopped.get(0)).isEqualTo("jobId");
374     }
375
376     @Test
377     void consumerDeleteEiJob() throws Exception {
378         putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
379         putEiJob(TYPE_ID, "jobId");
380         assertThat(this.infoJobs.size()).isEqualTo(1);
381         String url = ConsumerConsts.API_ROOT + "/info-jobs/jobId";
382         restClient().delete(url).block();
383         assertThat(this.infoJobs.size()).isZero();
384
385         ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
386         await().untilAsserted(() -> assertThat(simulatorResults.jobsStopped.size()).isEqualTo(1));
387         assertThat(simulatorResults.jobsStopped.get(0)).isEqualTo("jobId");
388     }
389
390     @Test
391     void a1eDeleteEiJobNotFound() throws Exception {
392         putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
393         String url = A1eConsts.API_ROOT + "/eijobs/junk";
394         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND, "Could not find Information job: junk");
395     }
396
397     @Test
398     void consumerDeleteEiJobNotFound() throws Exception {
399         putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
400         String url = ConsumerConsts.API_ROOT + "/info-jobs/junk";
401         testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND, "Could not find Information job: junk");
402     }
403
404     @Test
405     void a1ePutEiJob() throws Exception {
406         // Test that one producer accepting a job is enough
407         putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
408         putEiProducerWithOneTypeRejecting("simulateProducerError", TYPE_ID);
409
410         String url = A1eConsts.API_ROOT + "/eijobs/jobId";
411         String body = gson.toJson(infoJobInfo());
412         ResponseEntity<String> resp = restClient().putForEntity(url, body).block();
413         assertThat(this.infoJobs.size()).isEqualTo(1);
414         assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.CREATED);
415
416         ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
417         await().untilAsserted(() -> assertThat(simulatorResults.jobsStarted.size()).isEqualTo(1));
418         ProducerJobInfo request = simulatorResults.jobsStarted.get(0);
419         assertThat(request.id).isEqualTo("jobId");
420
421         // One retry --> two calls
422         await().untilAsserted(() -> assertThat(simulatorResults.noOfRejectedCreate).isEqualTo(2));
423         assertThat(simulatorResults.noOfRejectedCreate).isEqualTo(2);
424
425         resp = restClient().putForEntity(url, body).block();
426         assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
427         InfoJob job = this.infoJobs.getJob("jobId");
428         assertThat(job.getOwner()).isEqualTo("owner");
429
430         verifyJobStatus(EI_JOB_ID, "ENABLED");
431     }
432
433     @Test
434     void consumerPutInformationJob() throws Exception {
435         // Test that one producer accepting a job is enough
436         putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
437
438         String url = ConsumerConsts.API_ROOT + "/info-jobs/jobId";
439         String body = gson.toJson(consumerJobInfo());
440         ResponseEntity<String> resp = restClient().putForEntity(url, body).block();
441         assertThat(this.infoJobs.size()).isEqualTo(1);
442         assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.CREATED);
443
444         ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
445         await().untilAsserted(() -> assertThat(simulatorResults.jobsStarted.size()).isEqualTo(1));
446         ProducerJobInfo request = simulatorResults.jobsStarted.get(0);
447         assertThat(request.id).isEqualTo("jobId");
448
449         resp = restClient().putForEntity(url, body).block();
450         assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
451         InfoJob job = this.infoJobs.getJob("jobId");
452         assertThat(job.getOwner()).isEqualTo("owner");
453
454         verifyJobStatus(EI_JOB_ID, "ENABLED");
455     }
456
457     @Test
458     void consumerPutInformationJob_noType() throws JsonMappingException, JsonProcessingException, ServiceException {
459         String url = ConsumerConsts.API_ROOT + "/info-jobs/jobId?typeCheck=false";
460         String body = gson.toJson(consumerJobInfo());
461         ResponseEntity<String> resp = restClient().putForEntity(url, body).block();
462         assertThat(this.infoJobs.size()).isEqualTo(1);
463         assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.CREATED);
464         verifyJobStatus(EI_JOB_ID, "DISABLED");
465
466         putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
467
468         verifyJobStatus(EI_JOB_ID, "ENABLED");
469
470     }
471
472     @Test
473     void a1ePutEiJob_jsonSchemavalidationError() throws Exception {
474         putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
475
476         String url = A1eConsts.API_ROOT + "/eijobs/jobId";
477         // The element with name "property1" is mandatory in the schema
478         A1eEiJobInfo jobInfo = new A1eEiJobInfo("typeId", jsonObject("{ \"XXstring\" : \"value\" }"), "owner",
479             "targetUri", "jobStatusUrl");
480         String body = gson.toJson(jobInfo);
481
482         testErrorCode(restClient().put(url, body), HttpStatus.CONFLICT, "Json validation failure");
483     }
484
485     @Test
486     void consumerPutJob_jsonSchemavalidationError() throws Exception {
487         putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
488
489         String url = ConsumerConsts.API_ROOT + "/info-jobs/jobId?typeCheck=true";
490         // The element with name "property1" is mandatory in the schema
491         ConsumerJobInfo jobInfo =
492             new ConsumerJobInfo("typeId", jsonObject("{ \"XXstring\" : \"value\" }"), "owner", "targetUri", null);
493         String body = gson.toJson(jobInfo);
494
495         testErrorCode(restClient().put(url, body), HttpStatus.CONFLICT, "Json validation failure");
496     }
497
498     @Test
499     void consumerPutJob_uriError() throws Exception {
500         putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
501
502         String url = ConsumerConsts.API_ROOT + "/info-jobs/jobId?typeCheck=true";
503
504         ConsumerJobInfo jobInfo = new ConsumerJobInfo(TYPE_ID, jsonObject(), "owner", "junk", null);
505         String body = gson.toJson(jobInfo);
506
507         testErrorCode(restClient().put(url, body), HttpStatus.CONFLICT, "URI: junk is not absolute");
508     }
509
510     @Test
511     void a1eChangingEiTypeGetRejected() throws Exception {
512         putInfoProducerWithOneType("producer1", "typeId1");
513         putInfoProducerWithOneType("producer2", "typeId2");
514         putEiJob("typeId1", "jobId");
515
516         String url = A1eConsts.API_ROOT + "/eijobs/jobId";
517         String body = gson.toJson(infoJobInfo("typeId2", "jobId"));
518         testErrorCode(restClient().put(url, body), HttpStatus.CONFLICT,
519             "Not allowed to change type for existing EI job");
520     }
521
522     @Test
523     void consumerChangingInfoTypeGetRejected() throws Exception {
524         putInfoProducerWithOneType("producer1", "typeId1");
525         putInfoProducerWithOneType("producer2", "typeId2");
526         putEiJob("typeId1", "jobId");
527
528         String url = ConsumerConsts.API_ROOT + "/info-jobs/jobId";
529         String body = gson.toJson(consumerJobInfo("typeId2", "jobId"));
530         testErrorCode(restClient().put(url, body), HttpStatus.CONFLICT, "Not allowed to change type for existing job");
531     }
532
533     @Test
534     void producerPutEiType() throws JsonMappingException, JsonProcessingException, ServiceException {
535         assertThat(putInfoType(TYPE_ID)).isEqualTo(HttpStatus.CREATED);
536         assertThat(putInfoType(TYPE_ID)).isEqualTo(HttpStatus.OK);
537     }
538
539     @Test
540     void producerPutEiType_noSchema() {
541         String url = ProducerConsts.API_ROOT + "/info-types/" + TYPE_ID;
542         String body = "{}";
543         testErrorCode(restClient().put(url, body), HttpStatus.BAD_REQUEST, "No schema provided");
544     }
545
546     @Test
547     void producerDeleteEiType() throws Exception {
548         putInfoType(TYPE_ID);
549         String url = ProducerConsts.API_ROOT + "/info-types/" + TYPE_ID;
550         restClient().delete(url).block();
551         assertThat(this.infoTypes.size()).isZero();
552
553         testErrorCode(restClient().delete(url), HttpStatus.NOT_FOUND, "Information type not found");
554     }
555
556     @Test
557     void producerDeleteEiTypeExistingProducer() throws Exception {
558         putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
559         String url = ProducerConsts.API_ROOT + "/info-types/" + TYPE_ID;
560         testErrorCode(restClient().delete(url), HttpStatus.NOT_ACCEPTABLE,
561             "The type has active producers: " + PRODUCER_ID);
562         assertThat(this.infoTypes.size()).isEqualTo(1);
563     }
564
565     @Test
566     void producerPutProducerWithOneType_rejecting()
567         throws JsonMappingException, JsonProcessingException, ServiceException {
568         putEiProducerWithOneTypeRejecting("simulateProducerError", TYPE_ID);
569         String url = A1eConsts.API_ROOT + "/eijobs/" + EI_JOB_ID;
570         String body = gson.toJson(infoJobInfo());
571         restClient().put(url, body).block();
572
573         ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
574         // There is one retry -> 2 calls
575         await().untilAsserted(() -> assertThat(simulatorResults.noOfRejectedCreate).isEqualTo(2));
576         assertThat(simulatorResults.noOfRejectedCreate).isEqualTo(2);
577
578         verifyJobStatus(EI_JOB_ID, "DISABLED");
579     }
580
581     @Test
582     void producerGetEiProducerTypes() throws Exception {
583         final String EI_TYPE_ID_2 = TYPE_ID + "_2";
584         putInfoProducerWithOneType("producer1", TYPE_ID);
585         putEiJob(TYPE_ID, "jobId");
586         putInfoProducerWithOneType("producer2", EI_TYPE_ID_2);
587         putEiJob(EI_TYPE_ID_2, "jobId2");
588         String url = ProducerConsts.API_ROOT + "/info-types";
589
590         ResponseEntity<String> resp = restClient().getForEntity(url).block();
591         assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
592         assertThat(resp.getBody()).contains(TYPE_ID);
593         assertThat(resp.getBody()).contains(EI_TYPE_ID_2);
594     }
595
596     @Test
597     void producerPutEiProducer() throws Exception {
598         this.putInfoType(TYPE_ID);
599         String url = ProducerConsts.API_ROOT + "/info-producers/infoProducerId";
600         String body = gson.toJson(producerInfoRegistratioInfo(TYPE_ID));
601
602         ResponseEntity<String> resp = restClient().putForEntity(url, body).block();
603         assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.CREATED);
604
605         assertThat(this.infoTypes.size()).isEqualTo(1);
606         assertThat(this.infoProducers.getProducersForType(TYPE_ID)).hasSize(1);
607         assertThat(this.infoProducers.size()).isEqualTo(1);
608         assertThat(this.infoProducers.get("infoProducerId").getInfoTypes().iterator().next().getId())
609             .isEqualTo(TYPE_ID);
610
611         resp = restClient().putForEntity(url, body).block();
612         assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
613
614         resp = restClient().getForEntity(url).block();
615         assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
616         assertThat(resp.getBody()).isEqualTo(body);
617     }
618
619     @Test
620     void producerPutEiProducerExistingJob() throws Exception {
621         putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
622         putEiJob(TYPE_ID, "jobId");
623         String url = ProducerConsts.API_ROOT + "/info-producers/infoProducerId";
624         String body = gson.toJson(producerInfoRegistratioInfo(TYPE_ID));
625         restClient().putForEntity(url, body).block();
626
627         ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
628         await().untilAsserted(() -> assertThat(simulatorResults.jobsStarted.size()).isEqualTo(2));
629         ProducerJobInfo request = simulatorResults.jobsStarted.get(0);
630         assertThat(request.id).isEqualTo("jobId");
631     }
632
633     @Test
634     void testPutEiProducer_noType() throws Exception {
635         String url = ProducerConsts.API_ROOT + "/info-producers/infoProducerId";
636         String body = gson.toJson(producerInfoRegistratioInfo(TYPE_ID));
637         testErrorCode(restClient().put(url, body), HttpStatus.NOT_FOUND, "Information type not found");
638     }
639
640     @Test
641     void producerPutProducerAndEiJob() throws Exception {
642         this.putInfoType(TYPE_ID);
643         String url = ProducerConsts.API_ROOT + "/info-producers/infoProducerId";
644         String body = gson.toJson(producerInfoRegistratioInfo(TYPE_ID));
645         restClient().putForEntity(url, body).block();
646         assertThat(this.infoTypes.size()).isEqualTo(1);
647         this.infoTypes.getType(TYPE_ID);
648
649         url = A1eConsts.API_ROOT + "/eijobs/jobId";
650         body = gson.toJson(infoJobInfo());
651         restClient().putForEntity(url, body).block();
652
653         ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
654         await().untilAsserted(() -> assertThat(simulatorResults.jobsStarted.size()).isEqualTo(1));
655         ProducerJobInfo request = simulatorResults.jobsStarted.get(0);
656         assertThat(request.id).isEqualTo("jobId");
657     }
658
659     @Test
660     void producerGetEiJobsForProducer() throws JsonMappingException, JsonProcessingException, ServiceException {
661         putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
662         putEiJob(TYPE_ID, "jobId1");
663         putEiJob(TYPE_ID, "jobId2");
664
665         // PUT a consumerRestApiTestBase.java
666         String url = ProducerConsts.API_ROOT + "/info-producers/infoProducerId";
667         String body = gson.toJson(producerInfoRegistratioInfo(TYPE_ID));
668         restClient().putForEntity(url, body).block();
669
670         url = ProducerConsts.API_ROOT + "/info-producers/infoProducerId/info-jobs";
671         ResponseEntity<String> resp = restClient().getForEntity(url).block();
672         assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
673
674         ProducerJobInfo[] parsedResp = gson.fromJson(resp.getBody(), ProducerJobInfo[].class);
675         assertThat(parsedResp[0].typeId).isEqualTo(TYPE_ID);
676         assertThat(parsedResp[1].typeId).isEqualTo(TYPE_ID);
677     }
678
679     @Test
680     void producerDeleteEiProducer() throws Exception {
681         putInfoProducerWithOneType("infoProducerId", TYPE_ID);
682         putInfoProducerWithOneType("infoProducerId2", TYPE_ID);
683
684         assertThat(this.infoProducers.size()).isEqualTo(2);
685         InfoType type = this.infoTypes.getType(TYPE_ID);
686         assertThat(this.infoProducers.getProducerIdsForType(type.getId())).contains("infoProducerId");
687         assertThat(this.infoProducers.getProducerIdsForType(type.getId())).contains("infoProducerId2");
688         putEiJob(TYPE_ID, "jobId");
689         assertThat(this.infoJobs.size()).isEqualTo(1);
690
691         deleteEiProducer("infoProducerId");
692         assertThat(this.infoProducers.size()).isEqualTo(1);
693         assertThat(this.infoProducers.getProducerIdsForType(TYPE_ID)).doesNotContain("infoProducerId");
694         verifyJobStatus("jobId", "ENABLED");
695
696         deleteEiProducer("infoProducerId2");
697         assertThat(this.infoProducers.size()).isZero();
698         assertThat(this.infoTypes.size()).isEqualTo(1);
699         verifyJobStatus("jobId", "DISABLED");
700     }
701
702     @Test
703     void a1eJobStatusNotifications() throws JsonMappingException, JsonProcessingException, ServiceException {
704         ConsumerSimulatorController.TestResults consumerCalls = this.consumerSimulator.getTestResults();
705         ProducerSimulatorController.TestResults producerCalls = this.producerSimulator.getTestResults();
706
707         putInfoProducerWithOneType("infoProducerId", TYPE_ID);
708         putEiJob(TYPE_ID, "jobId");
709         putInfoProducerWithOneType("infoProducerId2", TYPE_ID);
710         await().untilAsserted(() -> assertThat(producerCalls.jobsStarted.size()).isEqualTo(2));
711
712         deleteEiProducer("infoProducerId2");
713         assertThat(this.infoTypes.size()).isEqualTo(1); // The type remains, one producer left
714         deleteEiProducer("infoProducerId");
715         assertThat(this.infoTypes.size()).isEqualTo(1); // The type remains
716         assertThat(this.infoJobs.size()).isEqualTo(1); // The job remains
717         await().untilAsserted(() -> assertThat(consumerCalls.status.size()).isEqualTo(1));
718         assertThat(consumerCalls.status.get(0).state).isEqualTo(A1eEiJobStatus.EiJobStatusValues.DISABLED);
719
720         putInfoProducerWithOneType("infoProducerId", TYPE_ID);
721         await().untilAsserted(() -> assertThat(consumerCalls.status.size()).isEqualTo(2));
722         assertThat(consumerCalls.status.get(1).state).isEqualTo(A1eEiJobStatus.EiJobStatusValues.ENABLED);
723     }
724
725     @Test
726     void a1eJobStatusNotifications2() throws JsonMappingException, JsonProcessingException, ServiceException {
727         // Test replacing a producer with new and removed types
728
729         // Create a job
730         putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
731         putEiJob(TYPE_ID, EI_JOB_ID);
732
733         // change the type for the producer, the job shall be disabled
734         putInfoProducerWithOneType(PRODUCER_ID, "junk");
735         verifyJobStatus(EI_JOB_ID, "DISABLED");
736         ConsumerSimulatorController.TestResults consumerCalls = this.consumerSimulator.getTestResults();
737         await().untilAsserted(() -> assertThat(consumerCalls.status.size()).isEqualTo(1));
738         assertThat(consumerCalls.status.get(0).state).isEqualTo(A1eEiJobStatus.EiJobStatusValues.DISABLED);
739
740         putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
741         verifyJobStatus(EI_JOB_ID, "ENABLED");
742         await().untilAsserted(() -> assertThat(consumerCalls.status.size()).isEqualTo(2));
743         assertThat(consumerCalls.status.get(1).state).isEqualTo(A1eEiJobStatus.EiJobStatusValues.ENABLED);
744     }
745
746     @Test
747     void producerGetProducerEiType() throws JsonMappingException, JsonProcessingException, ServiceException {
748         putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
749         String url = ProducerConsts.API_ROOT + "/info-types/" + TYPE_ID;
750         ResponseEntity<String> resp = restClient().getForEntity(url).block();
751         ProducerInfoTypeInfo info = gson.fromJson(resp.getBody(), ProducerInfoTypeInfo.class);
752         assertThat(info.jobDataSchema).isNotNull();
753     }
754
755     @Test
756     void producerGetProducerIdentifiers() throws JsonMappingException, JsonProcessingException, ServiceException {
757         putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
758         String url = ProducerConsts.API_ROOT + "/info-producers";
759         ResponseEntity<String> resp = restClient().getForEntity(url).block();
760         assertThat(resp.getBody()).contains(PRODUCER_ID);
761
762         url = ProducerConsts.API_ROOT + "/info-producers?info_type_id=" + TYPE_ID;
763         resp = restClient().getForEntity(url).block();
764         assertThat(resp.getBody()).contains(PRODUCER_ID);
765
766         url = ProducerConsts.API_ROOT + "/info-producers?info_type_id=junk";
767         resp = restClient().getForEntity(url).block();
768         assertThat(resp.getBody()).isEqualTo("[]");
769     }
770
771     @Test
772     void producerSupervision() throws JsonMappingException, JsonProcessingException, ServiceException {
773
774         ConsumerSimulatorController.TestResults consumerResults = this.consumerSimulator.getTestResults();
775         putEiProducerWithOneTypeRejecting("simulateProducerError", TYPE_ID);
776
777         {
778             // Create a job
779             putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
780             putEiJob(TYPE_ID, EI_JOB_ID);
781             verifyJobStatus(EI_JOB_ID, "ENABLED");
782             deleteEiProducer(PRODUCER_ID);
783             // A Job disabled status notification shall now be received
784             await().untilAsserted(() -> assertThat(consumerResults.status.size()).isEqualTo(1));
785             assertThat(consumerResults.status.get(0).state).isEqualTo(A1eEiJobStatus.EiJobStatusValues.DISABLED);
786             verifyJobStatus(EI_JOB_ID, "DISABLED");
787         }
788
789         assertThat(this.infoProducers.size()).isEqualTo(1);
790         assertThat(this.infoTypes.size()).isEqualTo(1);
791         assertProducerOpState("simulateProducerError", ProducerStatusInfo.OperationalState.ENABLED);
792
793         this.producerSupervision.createTask().blockLast();
794         this.producerSupervision.createTask().blockLast();
795
796         // Now we have one producer that is disabled
797         assertThat(this.infoProducers.size()).isEqualTo(1);
798         assertProducerOpState("simulateProducerError", ProducerStatusInfo.OperationalState.DISABLED);
799
800         // After 3 failed checks, the producer shall be deregisterred
801         this.producerSupervision.createTask().blockLast();
802         assertThat(this.infoProducers.size()).isZero(); // The producer is removed
803         assertThat(this.infoTypes.size()).isEqualTo(1); // The type remains
804
805         // Now we have one disabled job, and no producer.
806         // PUT a producer, then a Job ENABLED status notification shall be received
807         putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
808         await().untilAsserted(() -> assertThat(consumerResults.status.size()).isEqualTo(2));
809         assertThat(consumerResults.status.get(1).state).isEqualTo(A1eEiJobStatus.EiJobStatusValues.ENABLED);
810         verifyJobStatus(EI_JOB_ID, "ENABLED");
811     }
812
813     @Test
814     void producerSupervision2() throws JsonMappingException, JsonProcessingException, ServiceException {
815         // Test that supervision enables not enabled jobs and sends a notification when
816         // suceeded
817
818         putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
819         putEiJob(TYPE_ID, EI_JOB_ID);
820
821         InfoProducer producer = this.infoProducers.getProducer(PRODUCER_ID);
822         InfoJob job = this.infoJobs.getJob(EI_JOB_ID);
823         // Pretend that the producer did reject the job and the a DISABLED notification
824         // is sent for the job
825         producer.setJobDisabled(job);
826         job.setLastReportedStatus(false);
827         verifyJobStatus(EI_JOB_ID, "DISABLED");
828
829         // Run the supervision and wait for the job to get started in the producer
830         this.producerSupervision.createTask().blockLast();
831         ConsumerSimulatorController.TestResults consumerResults = this.consumerSimulator.getTestResults();
832         await().untilAsserted(() -> assertThat(consumerResults.status.size()).isEqualTo(1));
833         assertThat(consumerResults.status.get(0).state).isEqualTo(A1eEiJobStatus.EiJobStatusValues.ENABLED);
834         verifyJobStatus(EI_JOB_ID, "ENABLED");
835     }
836
837     @Test
838     void testGetStatus() throws JsonMappingException, JsonProcessingException, ServiceException {
839         putEiProducerWithOneTypeRejecting("simulateProducerError", TYPE_ID);
840         putEiProducerWithOneTypeRejecting("simulateProducerError2", TYPE_ID);
841
842         String url = "/status";
843         ResponseEntity<String> resp = restClient().getForEntity(url).block();
844         assertThat(resp.getBody()).contains("hunky dory");
845     }
846
847     @Test
848     void testEiJobDatabase() throws Exception {
849         putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
850         putEiJob(TYPE_ID, "jobId1");
851         putEiJob(TYPE_ID, "jobId2");
852
853         assertThat(this.infoJobs.size()).isEqualTo(2);
854
855         {
856             InfoJob savedJob = this.infoJobs.getJob("jobId1");
857             // Restore the jobs
858             InfoJobs jobs = new InfoJobs(this.applicationConfig, this.producerCallbacks);
859             jobs.restoreJobsFromDatabase();
860             assertThat(jobs.size()).isEqualTo(2);
861             InfoJob restoredJob = jobs.getJob("jobId1");
862             assertThat(restoredJob.getId()).isEqualTo("jobId1");
863             assertThat(restoredJob.getLastUpdated()).isEqualTo(savedJob.getLastUpdated());
864
865             jobs.remove("jobId1", this.infoProducers);
866             jobs.remove("jobId2", this.infoProducers);
867         }
868         {
869             // Restore the jobs, no jobs in database
870             InfoJobs jobs = new InfoJobs(this.applicationConfig, this.producerCallbacks);
871             jobs.restoreJobsFromDatabase();
872             assertThat(jobs.size()).isZero();
873         }
874         logger.warn("Test removing a job when the db file is gone");
875         this.infoJobs.remove("jobId1", this.infoProducers);
876         assertThat(this.infoJobs.size()).isEqualTo(1);
877
878         ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
879         await().untilAsserted(() -> assertThat(simulatorResults.jobsStopped.size()).isEqualTo(3));
880     }
881
882     @Test
883     void testEiTypesDatabase() throws Exception {
884         putInfoProducerWithOneType(PRODUCER_ID, TYPE_ID);
885
886         assertThat(this.infoTypes.size()).isEqualTo(1);
887
888         {
889             // Restore the types
890             InfoTypes types = new InfoTypes(this.applicationConfig);
891             types.restoreTypesFromDatabase();
892             assertThat(types.size()).isEqualTo(1);
893
894         }
895         {
896             // Restore the jobs, no jobs in database
897             InfoTypes types = new InfoTypes(this.applicationConfig);
898             types.clear();
899             types.restoreTypesFromDatabase();
900             assertThat(types.size()).isZero();
901         }
902         logger.warn("Test removing a job when the db file is gone");
903         this.infoTypes.remove(this.infoTypes.getType(TYPE_ID));
904         assertThat(this.infoJobs.size()).isZero();
905     }
906
907     private void deleteEiProducer(String infoProducerId) {
908         String url = ProducerConsts.API_ROOT + "/info-producers/" + infoProducerId;
909         restClient().deleteForEntity(url).block();
910     }
911
912     private void verifyJobStatus(String jobId, String expStatus) {
913         String url = A1eConsts.API_ROOT + "/eijobs/" + jobId + "/status";
914         String rsp = restClient().get(url).block();
915         assertThat(rsp).contains(expStatus);
916     }
917
918     private void assertProducerOpState(String producerId,
919         ProducerStatusInfo.OperationalState expectedOperationalState) {
920         String statusUrl = ProducerConsts.API_ROOT + "/info-producers/" + producerId + "/status";
921         ResponseEntity<String> resp = restClient().getForEntity(statusUrl).block();
922         ProducerStatusInfo statusInfo = gson.fromJson(resp.getBody(), ProducerStatusInfo.class);
923         assertThat(statusInfo.opState).isEqualTo(expectedOperationalState);
924     }
925
926     ProducerInfoTypeInfo producerEiTypeRegistrationInfo(String typeId)
927         throws JsonMappingException, JsonProcessingException {
928         return new ProducerInfoTypeInfo(jsonSchemaObject());
929     }
930
931     ProducerRegistrationInfo producerEiRegistratioInfoRejecting(String typeId)
932         throws JsonMappingException, JsonProcessingException {
933         return new ProducerRegistrationInfo(Arrays.asList(typeId), //
934             baseUrl() + ProducerSimulatorController.JOB_ERROR_URL,
935             baseUrl() + ProducerSimulatorController.SUPERVISION_ERROR_URL);
936     }
937
938     ProducerRegistrationInfo producerInfoRegistratioInfo(String typeId)
939         throws JsonMappingException, JsonProcessingException {
940         return new ProducerRegistrationInfo(Arrays.asList(typeId), //
941             baseUrl() + ProducerSimulatorController.JOB_URL, baseUrl() + ProducerSimulatorController.SUPERVISION_URL);
942     }
943
944     private ConsumerJobInfo consumerJobInfo() throws JsonMappingException, JsonProcessingException {
945         return consumerJobInfo(TYPE_ID, EI_JOB_ID);
946     }
947
948     ConsumerJobInfo consumerJobInfo(String typeId, String infoJobId)
949         throws JsonMappingException, JsonProcessingException {
950         return new ConsumerJobInfo(typeId, jsonObject(), "owner", "https://junk.com",
951             baseUrl() + ConsumerSimulatorController.getJobStatusUrl(infoJobId));
952     }
953
954     private A1eEiJobInfo infoJobInfo() throws JsonMappingException, JsonProcessingException {
955         return infoJobInfo(TYPE_ID, EI_JOB_ID);
956     }
957
958     A1eEiJobInfo infoJobInfo(String typeId, String infoJobId) throws JsonMappingException, JsonProcessingException {
959         return new A1eEiJobInfo(typeId, jsonObject(), "owner", "https://junk.com",
960             baseUrl() + ConsumerSimulatorController.getJobStatusUrl(infoJobId));
961     }
962
963     private Object jsonObject(String json) {
964         try {
965             return JsonParser.parseString(json).getAsJsonObject();
966         } catch (Exception e) {
967             throw new NullPointerException(e.toString());
968         }
969     }
970
971     private Object jsonSchemaObject() {
972         // a json schema with one mandatory property named "string"
973         String schemaStr = "{" //
974             + "\"$schema\": \"http://json-schema.org/draft-04/schema#\"," //
975             + "\"type\": \"object\"," //
976             + "\"properties\": {" //
977             + EI_JOB_PROPERTY + " : {" //
978             + "    \"type\": \"string\"" //
979             + "  }" //
980             + "}," //
981             + "\"required\": [" //
982             + EI_JOB_PROPERTY //
983             + "]" //
984             + "}"; //
985         return jsonObject(schemaStr);
986     }
987
988     private Object jsonObject() {
989         return jsonObject("{ " + EI_JOB_PROPERTY + " : \"value\" }");
990     }
991
992     private InfoJob putEiJob(String infoTypeId, String jobId)
993         throws JsonMappingException, JsonProcessingException, ServiceException {
994
995         String url = A1eConsts.API_ROOT + "/eijobs/" + jobId;
996         String body = gson.toJson(infoJobInfo(infoTypeId, jobId));
997         restClient().putForEntity(url, body).block();
998
999         return this.infoJobs.getJob(jobId);
1000     }
1001
1002     private HttpStatus putInfoType(String infoTypeId)
1003         throws JsonMappingException, JsonProcessingException, ServiceException {
1004         String url = ProducerConsts.API_ROOT + "/info-types/" + infoTypeId;
1005         String body = gson.toJson(producerEiTypeRegistrationInfo(infoTypeId));
1006
1007         ResponseEntity<String> resp = restClient().putForEntity(url, body).block();
1008         this.infoTypes.getType(infoTypeId);
1009         return resp.getStatusCode();
1010
1011     }
1012
1013     private InfoType putEiProducerWithOneTypeRejecting(String producerId, String infoTypeId)
1014         throws JsonMappingException, JsonProcessingException, ServiceException {
1015         this.putInfoType(infoTypeId);
1016         String url = ProducerConsts.API_ROOT + "/info-producers/" + producerId;
1017         String body = gson.toJson(producerEiRegistratioInfoRejecting(infoTypeId));
1018         restClient().putForEntity(url, body).block();
1019         return this.infoTypes.getType(infoTypeId);
1020     }
1021
1022     private InfoType putInfoProducerWithOneType(String producerId, String infoTypeId)
1023         throws JsonMappingException, JsonProcessingException, ServiceException {
1024         this.putInfoType(infoTypeId);
1025
1026         String url = ProducerConsts.API_ROOT + "/info-producers/" + producerId;
1027         String body = gson.toJson(producerInfoRegistratioInfo(infoTypeId));
1028
1029         restClient().putForEntity(url, body).block();
1030
1031         return this.infoTypes.getType(infoTypeId);
1032     }
1033
1034     private String baseUrl() {
1035         return "https://localhost:" + this.port;
1036     }
1037
1038     private AsyncRestClient restClient(boolean useTrustValidation) {
1039         WebClientConfig config = this.applicationConfig.getWebClientConfig();
1040         HttpProxyConfig httpProxyConfig = ImmutableHttpProxyConfig.builder() //
1041             .httpProxyHost("") //
1042             .httpProxyPort(0) //
1043             .build();
1044         config = ImmutableWebClientConfig.builder() //
1045             .keyStoreType(config.keyStoreType()) //
1046             .keyStorePassword(config.keyStorePassword()) //
1047             .keyStore(config.keyStore()) //
1048             .keyPassword(config.keyPassword()) //
1049             .isTrustStoreUsed(useTrustValidation) //
1050             .trustStore(config.trustStore()) //
1051             .trustStorePassword(config.trustStorePassword()) //
1052             .httpProxyConfig(httpProxyConfig).build();
1053
1054         AsyncRestClientFactory restClientFactory = new AsyncRestClientFactory(config);
1055         return restClientFactory.createRestClientNoHttpProxy(baseUrl());
1056     }
1057
1058     private AsyncRestClient restClient() {
1059         return restClient(false);
1060     }
1061
1062     private void testErrorCode(Mono<?> request, HttpStatus expStatus, String responseContains) {
1063         testErrorCode(request, expStatus, responseContains, true);
1064     }
1065
1066     private void testErrorCode(Mono<?> request, HttpStatus expStatus, String responseContains,
1067         boolean expectApplicationProblemJsonMediaType) {
1068         StepVerifier.create(request) //
1069             .expectSubscription() //
1070             .expectErrorMatches(
1071                 t -> checkWebClientError(t, expStatus, responseContains, expectApplicationProblemJsonMediaType)) //
1072             .verify();
1073     }
1074
1075     private boolean checkWebClientError(Throwable throwable, HttpStatus expStatus, String responseContains,
1076         boolean expectApplicationProblemJsonMediaType) {
1077         assertTrue(throwable instanceof WebClientResponseException);
1078         WebClientResponseException responseException = (WebClientResponseException) throwable;
1079         assertThat(responseException.getStatusCode()).isEqualTo(expStatus);
1080         assertThat(responseException.getResponseBodyAsString()).contains(responseContains);
1081         if (expectApplicationProblemJsonMediaType) {
1082             assertThat(responseException.getHeaders().getContentType()).isEqualTo(MediaType.APPLICATION_PROBLEM_JSON);
1083         }
1084         return true;
1085     }
1086
1087 }