View Javadoc
1   package de.dlr.shepard.integrationtests;
2   
3   import static io.restassured.RestAssured.given;
4   import static org.assertj.core.api.Assertions.assertThat;
5   import static org.junit.jupiter.api.Assertions.assertEquals;
6   
7   import de.dlr.shepard.ErrorResponse;
8   import de.dlr.shepard.common.util.Constants;
9   import de.dlr.shepard.context.semantic.SemanticRepositoryType;
10  import de.dlr.shepard.context.semantic.io.SemanticAnnotationIO;
11  import de.dlr.shepard.context.semantic.io.SemanticRepositoryIO;
12  import de.dlr.shepard.data.timeseries.TimeseriesTestDataGenerator;
13  import de.dlr.shepard.data.timeseries.io.TimeseriesContainerIO;
14  import de.dlr.shepard.data.timeseries.io.TimeseriesIO;
15  import de.dlr.shepard.data.timeseries.io.TimeseriesWithDataPoints;
16  import de.dlr.shepard.data.timeseries.model.TimeseriesDataPoint;
17  import de.dlr.shepard.data.timeseries.model.TimeseriesTuple;
18  import de.dlr.shepard.data.timeseries.services.InstantHelper;
19  import io.quarkus.test.common.WithTestResource;
20  import io.quarkus.test.junit.QuarkusIntegrationTest;
21  import io.restassured.response.ExtractableResponse;
22  import jakarta.ws.rs.core.Response;
23  import java.util.ArrayList;
24  import java.util.List;
25  import java.util.Map;
26  import org.junit.jupiter.api.BeforeAll;
27  import org.junit.jupiter.api.MethodOrderer;
28  import org.junit.jupiter.api.Order;
29  import org.junit.jupiter.api.Test;
30  import org.junit.jupiter.api.TestMethodOrder;
31  
32  @QuarkusIntegrationTest
33  @WithTestResource(WireMockResource.class)
34  @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
35  public class TimeseriesIT extends BaseTestCaseIT {
36  
37    private static String containerURL;
38    private static String repositoryURL;
39    private static String annotationURL;
40    private static SemanticRepositoryIO repository;
41    private static SemanticAnnotationIO annotation;
42  
43    private static TimeseriesContainerIO container;
44    private static TimeseriesWithDataPoints payload;
45    private static TimeseriesIO timeseries;
46    private static long start;
47    private static long end;
48  
49    @BeforeAll
50    public static void setUp() {
51      containerURL = Constants.TIMESERIES_CONTAINERS;
52      repositoryURL = "/" + Constants.SEMANTIC_REPOSITORIES;
53      SemanticRepositoryIO repoToCreate = new SemanticRepositoryIO();
54      repoToCreate.setName("SemanticRepository");
55      repoToCreate.setType(SemanticRepositoryType.SPARQL);
56      repoToCreate.setEndpoint(WireMockResource.getWireMockServerURlWithPath("/sparql"));
57      repository = given()
58        .spec(requestSpecOfDefaultUser)
59        .body(repoToCreate)
60        .when()
61        .post(repositoryURL)
62        .then()
63        .statusCode(Response.Status.CREATED.getStatusCode())
64        .extract()
65        .as(SemanticRepositoryIO.class);
66    }
67  
68    @Test
69    @Order(1)
70    public void createTimeseriesContainer() {
71      String containerName = "TimeseriesContainer";
72      TimeseriesContainerIO containerToCreate = new TimeseriesContainerIO();
73      containerToCreate.setName(containerName);
74  
75      TimeseriesContainerIO containerCreated = given()
76        .spec(requestSpecOfDefaultUser)
77        .body(containerToCreate)
78        .when()
79        .post(containerURL)
80        .then()
81        .statusCode(Response.Status.CREATED.getStatusCode())
82        .extract()
83        .as(TimeseriesContainerIO.class);
84      container = containerCreated;
85      assertThat(containerCreated.getId()).isNotNull();
86      assertThat(containerCreated.getCreatedAt()).isNotNull();
87      assertThat(containerCreated.getCreatedBy()).isEqualTo(nameOfDefaultUser);
88      assertThat(containerCreated.getName()).isEqualTo(containerName);
89      assertThat(containerCreated.getUpdatedAt()).isNull();
90      assertThat(containerCreated.getUpdatedBy()).isNull();
91    }
92  
93    @Test
94    @Order(2)
95    public void getAllTimeseriesContainers() {
96      TimeseriesContainerIO[] containersFound = given()
97        .spec(requestSpecOfDefaultUser)
98        .when()
99        .get(containerURL)
100       .then()
101       .statusCode(Response.Status.OK.getStatusCode())
102       .extract()
103       .as(TimeseriesContainerIO[].class);
104     assertThat(containersFound).contains(container);
105   }
106 
107   @Test
108   @Order(3)
109   public void getTimeseriesContainer() {
110     TimeseriesContainerIO containerFound = given()
111       .spec(requestSpecOfDefaultUser)
112       .when()
113       .get(containerURL + "/" + container.getId())
114       .then()
115       .statusCode(Response.Status.OK.getStatusCode())
116       .extract()
117       .as(TimeseriesContainerIO.class);
118     assertThat(containerFound).isEqualTo(container);
119   }
120 
121   @Test
122   @Order(4)
123   public void getTimeseriesContainer_doesNotExist_notFound() {
124     ErrorResponse containerNotExistent = given()
125       .spec(requestSpecOfDefaultUser)
126       .when()
127       .get(containerURL + "/99999")
128       .then()
129       .statusCode(Response.Status.NOT_FOUND.getStatusCode())
130       .extract()
131       .as(ErrorResponse.class);
132     assertThat(containerNotExistent.getMessage()).isEqualTo(
133       "ID ERROR - Timeseries Container with id 99999 is null or deleted"
134     );
135   }
136 
137   @Test
138   @Order(5)
139   public void getTimeseriesContainer_doesNotExistNegativeId_badRequest() {
140     int respNegativeId = given().spec(requestSpecOfDefaultUser).when().get(containerURL + "/-1").statusCode();
141     assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), respNegativeId);
142   }
143 
144   @Test
145   @Order(6)
146   public void createTimeseries() {
147     TimeseriesTuple timeseries = TimeseriesTestDataGenerator.generateTimeseries("temperature");
148     InstantHelper instantHelper = InstantHelper.fromGermanDate("01.01.2024");
149     start = instantHelper.toNano();
150     List<TimeseriesDataPoint> dataPointsIO = new ArrayList<>(
151       List.of(
152         TimeseriesTestDataGenerator.generateDataPointDouble(instantHelper.toNano(), 22.1),
153         TimeseriesTestDataGenerator.generateDataPointDouble(instantHelper.addSeconds(1).toNano(), 22.3),
154         TimeseriesTestDataGenerator.generateDataPointDouble(instantHelper.addSeconds(1).toNano(), 22.2)
155       )
156     );
157     end = instantHelper.addSeconds(2).toNano();
158 
159     payload = new TimeseriesWithDataPoints(timeseries, dataPointsIO);
160 
161     timeseries = given()
162       .spec(requestSpecOfDefaultUser)
163       .body(payload)
164       .when()
165       .post("%s/%d/%s".formatted(containerURL, container.getId(), Constants.PAYLOAD))
166       .then()
167       .statusCode(Response.Status.CREATED.getStatusCode())
168       .extract()
169       .as(TimeseriesTuple.class);
170 
171     assertThat(timeseries).isEqualTo(payload.getTimeseries());
172   }
173 
174   @Test
175   @Order(6)
176   public void createAnotherTimeseries() {
177     TimeseriesTuple timeseries = TimeseriesTestDataGenerator.generateTimeseries("temperature2");
178     InstantHelper instantHelper = InstantHelper.fromGermanDate("01.01.2024");
179     List<TimeseriesDataPoint> dataPointsIO = new ArrayList<>(
180       List.of(
181         TimeseriesTestDataGenerator.generateDataPointDouble(instantHelper.toNano(), 22.1),
182         TimeseriesTestDataGenerator.generateDataPointDouble(instantHelper.addSeconds(1).toNano(), 22.3),
183         TimeseriesTestDataGenerator.generateDataPointDouble(instantHelper.addSeconds(1).toNano(), 22.2)
184       )
185     );
186 
187     TimeseriesWithDataPoints payload = new TimeseriesWithDataPoints(timeseries, dataPointsIO);
188 
189     timeseries = given()
190       .spec(requestSpecOfDefaultUser)
191       .body(payload)
192       .when()
193       .post("%s/%d/%s".formatted(containerURL, container.getId(), Constants.PAYLOAD))
194       .then()
195       .statusCode(Response.Status.CREATED.getStatusCode())
196       .extract()
197       .as(TimeseriesTuple.class);
198 
199     assertThat(timeseries).isEqualTo(payload.getTimeseries());
200   }
201 
202   @Test
203   @Order(7)
204   public void addToTimeseries_DifferentValueType_BadRequest() {
205     TimeseriesTuple timeseries = TimeseriesTestDataGenerator.generateTimeseries("temperature");
206     InstantHelper instantHelper = InstantHelper.fromGermanDate("01.01.2026");
207     List<TimeseriesDataPoint> dataPointsIO = new ArrayList<>(
208       List.of(
209         TimeseriesTestDataGenerator.generateDataPointString(instantHelper.toNano(), "22.1"),
210         TimeseriesTestDataGenerator.generateDataPointString(instantHelper.addSeconds(1).toNano(), "22.3"),
211         TimeseriesTestDataGenerator.generateDataPointString(instantHelper.addSeconds(1).toNano(), "22.2")
212       )
213     );
214 
215     TimeseriesWithDataPoints payload = new TimeseriesWithDataPoints(timeseries, dataPointsIO);
216 
217     io.restassured.response.Response response = given()
218       .spec(requestSpecOfDefaultUser)
219       .body(payload)
220       .when()
221       .post("%s/%d/%s".formatted(containerURL, container.getId(), Constants.PAYLOAD));
222 
223     assertThat(response.statusCode()).isEqualTo(Response.Status.BAD_REQUEST.getStatusCode());
224   }
225 
226   @Test
227   @Order(8)
228   public void getTimeseriesAvailable_NoParameters_ReturnAllTimeseries() {
229     TimeseriesTuple[] timeseriesFound = given()
230       .spec(requestSpecOfDefaultUser)
231       .when()
232       .get(containerURL + "/" + container.getId() + "/" + Constants.AVAILABLE)
233       .then()
234       .statusCode(Response.Status.OK.getStatusCode())
235       .extract()
236       .as(TimeseriesTuple[].class);
237 
238     TimeseriesTuple expectedTimeseriesIO = new TimeseriesTuple(
239       "temperature",
240       "device",
241       "location",
242       "symbolicName",
243       "field"
244     );
245 
246     assertThat(timeseriesFound).contains(expectedTimeseriesIO);
247     assertThat(timeseriesFound.length).isEqualTo(2);
248   }
249 
250   @Test
251   @Order(9)
252   public void getTimeseriesPayload() {
253     TimeseriesWithDataPoints timeseriesWithDataPointsFound = given()
254       .spec(requestSpecOfDefaultUser)
255       .when()
256       .queryParams(
257         Map.of(
258           Constants.MEASUREMENT,
259           "temperature",
260           Constants.LOCATION,
261           "location",
262           Constants.DEVICE,
263           "device",
264           Constants.SYMBOLICNAME,
265           "symbolicName",
266           Constants.FIELD,
267           "field",
268           Constants.START,
269           start,
270           Constants.END,
271           end
272         )
273       )
274       .get(containerURL + "/" + container.getId() + "/" + Constants.PAYLOAD)
275       .then()
276       .statusCode(Response.Status.OK.getStatusCode())
277       .extract()
278       .as(TimeseriesWithDataPoints.class);
279 
280     assertThat(timeseriesWithDataPointsFound).isEqualTo(payload);
281   }
282 
283   @Test
284   @Order(10)
285   public void getTimeseries_startNotSet_badRequest() {
286     int respNoStartSet = given()
287       .spec(requestSpecOfDefaultUser)
288       .when()
289       .queryParams(
290         Map.of(
291           Constants.MEASUREMENT,
292           "temperature",
293           Constants.LOCATION,
294           "location",
295           Constants.DEVICE,
296           "device",
297           Constants.SYMBOLICNAME,
298           "symbolicName",
299           Constants.FIELD,
300           "field",
301           Constants.END,
302           end
303         )
304       )
305       .get(containerURL + "/" + container.getId() + "/" + Constants.PAYLOAD)
306       .getStatusCode();
307     assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), respNoStartSet);
308   }
309 
310   @Test
311   @Order(11)
312   public void getTimeseries_measurementIsBlank_badRequest() {
313     int respNoMeasurement = given()
314       .spec(requestSpecOfDefaultUser)
315       .when()
316       .queryParams(
317         Map.of(
318           Constants.MEASUREMENT,
319           "   ",
320           Constants.LOCATION,
321           "location",
322           Constants.DEVICE,
323           "device",
324           Constants.SYMBOLICNAME,
325           "symbolicName",
326           Constants.FIELD,
327           "field",
328           Constants.START,
329           start,
330           Constants.END,
331           end
332         )
333       )
334       .get(containerURL + "/" + container.getId() + "/" + Constants.PAYLOAD)
335       .getStatusCode();
336     assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), respNoMeasurement);
337   }
338 
339   @Test
340   @Order(12)
341   public void getTimeseriesByQuintuple_ParametersFilled_ReturnSubset() {
342     TimeseriesIO[] timeseriesEntitiesFoundByQuintuple = given()
343       .spec(requestSpecOfDefaultUser)
344       .when()
345       .queryParams(
346         Map.of(
347           Constants.MEASUREMENT,
348           "temperature",
349           Constants.LOCATION,
350           "location",
351           Constants.DEVICE,
352           "device",
353           Constants.SYMBOLICNAME,
354           "symbolicName",
355           Constants.FIELD,
356           "field"
357         )
358       )
359       .get(containerURL + "/" + container.getId() + "/" + Constants.TIMESERIES)
360       .then()
361       .statusCode(Response.Status.OK.getStatusCode())
362       .extract()
363       .as(TimeseriesIO[].class);
364 
365     assertEquals(1, timeseriesEntitiesFoundByQuintuple.length);
366     timeseries = timeseriesEntitiesFoundByQuintuple[0];
367   }
368 
369   @Test
370   @Order(13)
371   public void getTimeseriesByQuintuple_ParametersEmpty_ReturnAllTimeseries() {
372     TimeseriesIO[] timeseriesFound = given()
373       .spec(requestSpecOfDefaultUser)
374       .when()
375       .queryParams(
376         Map.of(
377           Constants.MEASUREMENT,
378           "",
379           Constants.LOCATION,
380           "",
381           Constants.DEVICE,
382           "",
383           Constants.SYMBOLICNAME,
384           "",
385           Constants.FIELD,
386           ""
387         )
388       )
389       .get(containerURL + "/" + container.getId() + "/" + Constants.TIMESERIES)
390       .then()
391       .statusCode(Response.Status.OK.getStatusCode())
392       .extract()
393       .as(TimeseriesIO[].class);
394 
395     assertThat(timeseriesFound.length).isEqualTo(2);
396   }
397 
398   @Test
399   @Order(14)
400   public void getTimeseriesByQuintuple_ParametersNull_ReturnAllTimeseries() {
401     TimeseriesIO[] timeseriesFound = given()
402       .spec(requestSpecOfDefaultUser)
403       .when()
404       .get(containerURL + "/" + container.getId() + "/" + Constants.TIMESERIES)
405       .then()
406       .statusCode(Response.Status.OK.getStatusCode())
407       .extract()
408       .as(TimeseriesIO[].class);
409 
410     assertThat(timeseriesFound.length).isEqualTo(2);
411   }
412 
413   @Test
414   @Order(15)
415   public void getTimeseriesById() {
416     TimeseriesIO timeseriesEntityFoundById = given()
417       .spec(requestSpecOfDefaultUser)
418       .when()
419       .get(containerURL + "/" + container.getId() + "/" + Constants.TIMESERIES + "/" + timeseries.getId())
420       .then()
421       .statusCode(Response.Status.OK.getStatusCode())
422       .extract()
423       .as(TimeseriesIO.class);
424     assertEquals(timeseries.getField(), timeseriesEntityFoundById.getField());
425     assertEquals(timeseries.getMeasurement(), timeseriesEntityFoundById.getMeasurement());
426     assertEquals(timeseries.getDevice(), timeseriesEntityFoundById.getDevice());
427     assertEquals(timeseries.getContainerId(), timeseriesEntityFoundById.getContainerId());
428     assertEquals(timeseries.getLocation(), timeseriesEntityFoundById.getLocation());
429     assertEquals(timeseries.getSymbolicName(), timeseriesEntityFoundById.getSymbolicName());
430     assertEquals(timeseries.getId(), timeseriesEntityFoundById.getId());
431   }
432 
433   /**
434    * Test for bug #717
435    */
436   @Test
437   @Order(16)
438   public void getTimeseries_ContainerNotAccessible() {
439     String containerName = "AccessibleContainer";
440     TimeseriesContainerIO toCreate = new TimeseriesContainerIO();
441     toCreate.setName(containerName);
442 
443     TimeseriesContainerIO accessibleContainer = given()
444       .spec(requestSpecOfOtherUser)
445       .body(toCreate)
446       .when()
447       .post(containerURL)
448       .then()
449       .statusCode(Response.Status.CREATED.getStatusCode())
450       .extract()
451       .as(TimeseriesContainerIO.class);
452 
453     ExtractableResponse response = given()
454       .spec(requestSpecOfOtherUser)
455       .when()
456       .get(containerURL + "/" + accessibleContainer.getId() + "/timeseries/" + timeseries.getId())
457       .then()
458       .extract();
459 
460     assertThat(response.statusCode()).isEqualTo(Response.Status.FORBIDDEN.getStatusCode());
461   }
462 
463   @Test
464   @Order(17)
465   public void annotateTimeseries() {
466     annotationURL =
467       Constants.TIMESERIES_CONTAINERS +
468       "/" +
469       container.getId() +
470       "/" +
471       Constants.TIMESERIES +
472       "/" +
473       timeseries.getId() +
474       "/" +
475       Constants.SEMANTIC_ANNOTATIONS;
476     SemanticAnnotationIO annotationToCreate = new SemanticAnnotationIO();
477     annotationToCreate.setPropertyIRI("http://dbpedia.org/ontology/ingredient");
478     annotationToCreate.setPropertyRepositoryId(repository.getId());
479     annotationToCreate.setValueIRI("http://dbpedia.org/resource/Almond_milk");
480     annotationToCreate.setValueRepositoryId(repository.getId());
481 
482     annotation = given()
483       .spec(requestSpecOfDefaultUser)
484       .body(annotationToCreate)
485       .when()
486       .post(annotationURL)
487       .then()
488       .statusCode(Response.Status.CREATED.getStatusCode())
489       .extract()
490       .as(SemanticAnnotationIO.class);
491 
492     assertThat(annotation.getId()).isNotNull();
493     assertThat(annotation.getName()).isEqualTo("ingredient::Almond milk");
494     assertThat(annotation.getPropertyIRI()).isEqualTo("http://dbpedia.org/ontology/ingredient");
495     assertThat(annotation.getPropertyRepositoryId()).isEqualTo(repository.getId());
496     assertThat(annotation.getValueIRI()).isEqualTo("http://dbpedia.org/resource/Almond_milk");
497     assertThat(annotation.getValueRepositoryId()).isEqualTo(repository.getId());
498   }
499 
500   @Test
501   @Order(18)
502   public void getAllAnnotations() {
503     SemanticAnnotationIO[] annotationsFound = given()
504       .spec(requestSpecOfDefaultUser)
505       .when()
506       .get(annotationURL)
507       .then()
508       .statusCode(Response.Status.OK.getStatusCode())
509       .extract()
510       .as(SemanticAnnotationIO[].class);
511     assertEquals(1, annotationsFound.length);
512     assertThat(annotationsFound[0].equals(annotation));
513   }
514 
515   @Test
516   @Order(18)
517   public void getAnnotationById() {
518     SemanticAnnotationIO annotationFound = given()
519       .spec(requestSpecOfDefaultUser)
520       .when()
521       .get(annotationURL + "/" + annotation.getId())
522       .then()
523       .statusCode(Response.Status.OK.getStatusCode())
524       .extract()
525       .as(SemanticAnnotationIO.class);
526     assertThat(annotationFound.equals(annotation));
527   }
528 
529   @Test
530   @Order(18)
531   public void findAnnotationInTimeseries() {
532     TimeseriesIO timeseriesEntityWithAnnotation = given()
533       .spec(requestSpecOfDefaultUser)
534       .when()
535       .get(containerURL + "/" + container.getId() + "/" + Constants.TIMESERIES + "/" + timeseries.getId())
536       .then()
537       .statusCode(Response.Status.OK.getStatusCode())
538       .extract()
539       .as(TimeseriesIO.class);
540     assertEquals(1, timeseriesEntityWithAnnotation.getSemanticAnnotationIds().length);
541     assertEquals(annotation.getId(), timeseriesEntityWithAnnotation.getSemanticAnnotationIds()[0]);
542   }
543 
544   @Test
545   @Order(19)
546   public void deleteAnnotation() {
547     int respDelete = given()
548       .spec(requestSpecOfDefaultUser)
549       .when()
550       .delete(annotationURL + "/" + annotation.getId())
551       .getStatusCode();
552     int respSearch = given()
553       .spec(requestSpecOfDefaultUser)
554       .when()
555       .get(annotationURL + "/" + annotation.getId())
556       .statusCode();
557     assertEquals(Response.Status.NO_CONTENT.getStatusCode(), respDelete);
558     assertEquals(Response.Status.NOT_FOUND.getStatusCode(), respSearch);
559   }
560 
561   @Test
562   @Order(20)
563   public void deleteContainer() {
564     int respDelete = given()
565       .spec(requestSpecOfDefaultUser)
566       .when()
567       .delete(containerURL + "/" + container.getId())
568       .getStatusCode();
569     int respSearch = given()
570       .spec(requestSpecOfDefaultUser)
571       .when()
572       .get(containerURL + "/" + container.getId())
573       .getStatusCode();
574     assertEquals(Response.Status.NO_CONTENT.getStatusCode(), respDelete);
575     assertEquals(Response.Status.NOT_FOUND.getStatusCode(), respSearch);
576   }
577 }