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
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 }