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.assertArrayEquals;
6   import static org.junit.jupiter.api.Assertions.assertEquals;
7   import static org.junit.jupiter.api.Assertions.assertTrue;
8   
9   import de.dlr.shepard.ErrorResponse;
10  import de.dlr.shepard.RandomGenerator;
11  import de.dlr.shepard.auth.permission.io.PermissionsIO;
12  import de.dlr.shepard.auth.permission.model.Roles;
13  import de.dlr.shepard.common.util.Constants;
14  import de.dlr.shepard.common.util.JsonConverter;
15  import de.dlr.shepard.common.util.PermissionType;
16  import de.dlr.shepard.data.spatialdata.io.FilterCondition;
17  import de.dlr.shepard.data.spatialdata.io.Operator;
18  import de.dlr.shepard.data.spatialdata.io.SpatialDataContainerIO;
19  import de.dlr.shepard.data.spatialdata.io.SpatialDataPointIO;
20  import de.dlr.shepard.data.spatialdata.model.geometryFilter.AxisAlignedBoundingBox;
21  import de.dlr.shepard.data.spatialdata.model.geometryFilter.BoundingSphere;
22  import de.dlr.shepard.data.spatialdata.model.geometryFilter.KNearestNeighbor;
23  import io.quarkus.test.junit.QuarkusIntegrationTest;
24  import java.util.ArrayList;
25  import java.util.List;
26  import java.util.Map;
27  import java.util.stream.Collectors;
28  import java.util.stream.IntStream;
29  import org.junit.jupiter.api.AfterAll;
30  import org.junit.jupiter.api.BeforeAll;
31  import org.junit.jupiter.api.MethodOrderer;
32  import org.junit.jupiter.api.Order;
33  import org.junit.jupiter.api.Test;
34  import org.junit.jupiter.api.TestMethodOrder;
35  
36  @QuarkusIntegrationTest
37  @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
38  public class SpatialDataIT extends BaseTestCaseIT {
39  
40    private static String containerURL;
41    private static SpatialDataContainerIO container;
42    private static ArrayList<SpatialDataContainerIO> existingContainers = new ArrayList<>();
43    private static List<SpatialDataPointIO> createdDataPoints;
44  
45    @BeforeAll
46    public static void setUp() {
47      containerURL = Constants.SPATIAL_DATA_CONTAINERS;
48  
49      IntStream.range(0, 50).forEach(index -> {
50        var containerName = "container_" + index + RandomGenerator.generateString(20);
51        var toCreate = new SpatialDataContainerIO();
52        toCreate.setName(containerName);
53  
54        var actual = given()
55          .spec(requestSpecOfDefaultUser)
56          .body(toCreate)
57          .when()
58          .post(containerURL)
59          .then()
60          .statusCode(201)
61          .extract()
62          .as(SpatialDataContainerIO.class);
63        existingContainers.add(actual);
64      });
65    }
66  
67    @AfterAll
68    public static void tearDown() {
69      existingContainers.forEach(container ->
70        given().spec(requestSpecOfDefaultUser).when().delete(containerURL + "/" + container.getId())
71      );
72    }
73  
74    @Test
75    @Order(1)
76    public void createSpatialDataContainer() {
77      var containerName = "SpatialContainer";
78      var toCreate = new SpatialDataContainerIO();
79      toCreate.setName(containerName);
80  
81      var actual = given()
82        .spec(requestSpecOfDefaultUser)
83        .body(toCreate)
84        .when()
85        .post(containerURL)
86        .then()
87        .statusCode(201)
88        .extract()
89        .as(SpatialDataContainerIO.class);
90      container = actual;
91  
92      assertThat(actual.getId()).isNotNull();
93      assertThat(actual.getCreatedAt()).isNotNull();
94      assertThat(actual.getCreatedBy()).isEqualTo(nameOfDefaultUser);
95      assertThat(actual.getName()).isEqualTo(containerName);
96      assertThat(actual.getUpdatedAt()).isNull();
97      assertThat(actual.getUpdatedBy()).isNull();
98    }
99  
100   @Test
101   @Order(2)
102   public void getAllSpatialDataContainers_success() {
103     var actual = given()
104       .spec(requestSpecOfDefaultUser)
105       .when()
106       .get(containerURL)
107       .then()
108       .statusCode(200)
109       .extract()
110       .as(SpatialDataContainerIO[].class);
111 
112     assertThat(actual).contains(container);
113   }
114 
115   @Test
116   @Order(2)
117   public void getAllSpatialDataContainers_filterByName_success() {
118     var actual = given()
119       .spec(requestSpecOfDefaultUser)
120       .queryParam("name", container.getName())
121       .when()
122       .get(containerURL)
123       .then()
124       .statusCode(200)
125       .extract()
126       .as(SpatialDataContainerIO[].class);
127 
128     assertThat(actual).contains(container);
129 
130     actual = given()
131       .spec(requestSpecOfDefaultUser)
132       .queryParam("name", RandomGenerator.generateString(20))
133       .when()
134       .get(containerURL)
135       .then()
136       .statusCode(200)
137       .extract()
138       .as(SpatialDataContainerIO[].class);
139 
140     assertThat(actual).doesNotContain(container);
141   }
142 
143   @Test
144   @Order(2)
145   public void getAllSpatialDataContainers_requestPage_success() {
146     var allContainers = given()
147       .spec(requestSpecOfDefaultUser)
148       .when()
149       .get(containerURL)
150       .then()
151       .statusCode(200)
152       .extract()
153       .as(SpatialDataContainerIO[].class);
154     var firstPage = given()
155       .spec(requestSpecOfDefaultUser)
156       .queryParam("page", 0)
157       .queryParam("size", 3)
158       .when()
159       .get(containerURL)
160       .then()
161       .statusCode(200)
162       .extract()
163       .as(SpatialDataContainerIO[].class);
164 
165     var thirdPage = given()
166       .spec(requestSpecOfDefaultUser)
167       .queryParam("page", 2)
168       .queryParam("size", 3)
169       .when()
170       .get(containerURL)
171       .then()
172       .statusCode(200)
173       .extract()
174       .as(SpatialDataContainerIO[].class);
175 
176     assertTrue(firstPage.length == 3);
177     assertTrue(thirdPage.length == 3);
178 
179     SpatialDataContainerIO[] expectedFirstPge = { allContainers[0], allContainers[1], allContainers[2] };
180     SpatialDataContainerIO[] expectedThirdPge = { allContainers[6], allContainers[7], allContainers[8] };
181     assertArrayEquals(expectedFirstPge, firstPage);
182     assertArrayEquals(expectedThirdPge, thirdPage);
183   }
184 
185   @Test
186   @Order(2)
187   public void getAllSpatialDataContainers_orderBy_success() {
188     var allContainersDesc = given()
189       .spec(requestSpecOfDefaultUser)
190       .queryParam("orderBy", "createdAt")
191       .queryParam("orderDesc", true)
192       .when()
193       .get(containerURL)
194       .then()
195       .statusCode(200)
196       .extract()
197       .as(SpatialDataContainerIO[].class);
198 
199     var allContainersAsc = given()
200       .spec(requestSpecOfDefaultUser)
201       .queryParam("orderBy", "createdAt")
202       .queryParam("orderDesc", false)
203       .when()
204       .get(containerURL)
205       .then()
206       .statusCode(200)
207       .extract()
208       .as(SpatialDataContainerIO[].class);
209     // Check for reverse order
210     IntStream.range(0, allContainersAsc.length).forEach(index ->
211       assertTrue(
212         allContainersAsc[index].getCreatedAt()
213           .before(allContainersDesc[allContainersAsc.length - 1 - index].getCreatedAt()) ||
214         allContainersAsc[index].getCreatedAt()
215           .equals(allContainersDesc[allContainersAsc.length - 1 - index].getCreatedAt())
216       )
217     );
218   }
219 
220   @Test
221   @Order(4)
222   public void getSpatialDataContainer_success() {
223     var actual = given()
224       .spec(requestSpecOfDefaultUser)
225       .when()
226       .get(containerURL + "/" + container.getId())
227       .then()
228       .statusCode(200)
229       .extract()
230       .as(SpatialDataContainerIO.class);
231 
232     assertThat(actual).isEqualTo(container);
233   }
234 
235   @Test
236   @Order(4)
237   public void getSpatialDataContainer_wrongPermissions_forbid() {
238     // different user tries to access container
239     given().spec(requestSpecOfOtherUser).when().get(containerURL + "/" + container.getId()).then().statusCode(403);
240   }
241 
242   @Test
243   @Order(5)
244   public void getSpatialDataContainer_doesNotExist_notFound() {
245     ErrorResponse actual = given()
246       .spec(requestSpecOfDefaultUser)
247       .when()
248       .get(containerURL + "/999999")
249       .then()
250       .statusCode(404)
251       .extract()
252       .as(ErrorResponse.class);
253     assertThat(actual.getMessage()).isEqualTo("ID ERROR - Spatial data container with id 999999 is null or deleted");
254   }
255 
256   @Test
257   @Order(6)
258   public void addSpatialDataPoints_success() {
259     createdDataPoints = IntStream.range(0, 10)
260       .mapToObj(index ->
261         new SpatialDataPointIO(
262           Long.valueOf(index),
263           index * 10.0,
264           index * 10.0,
265           index * 10.0,
266           Map.of("a_meta_data", "metadata_%s".formatted(index)),
267           Map.of("a_measurement", index)
268         )
269       )
270       .collect(Collectors.toList());
271     given()
272       .spec(requestSpecOfDefaultUser)
273       .body(createdDataPoints)
274       .when()
275       .post(String.format("%s/%d/%s", containerURL, container.getId(), Constants.PAYLOAD))
276       .then()
277       .statusCode(204);
278   }
279 
280   @Test
281   @Order(6)
282   public void addSpatialDataPoints_wrongPermissions_forbid() {
283     createdDataPoints = IntStream.range(0, 10)
284       .mapToObj(index ->
285         new SpatialDataPointIO(
286           Long.valueOf(index),
287           index * 10.0,
288           index * 10.0,
289           index * 10.0,
290           Map.of("a_meta_data", "metadata_%s".formatted(index)),
291           Map.of("a_measurement", index)
292         )
293       )
294       .collect(Collectors.toList());
295     given()
296       .spec(requestSpecOfOtherUser)
297       .body(createdDataPoints)
298       .when()
299       .post(String.format("%s/%d/%s", containerURL, container.getId(), Constants.PAYLOAD))
300       .then()
301       .statusCode(403);
302   }
303 
304   @Test
305   @Order(7)
306   public void getSpatialData_noFilters_retrievesAllPoints_Success() {
307     var dataPoints = given()
308       .spec(requestSpecOfDefaultUser)
309       .when()
310       .get(String.format("%s/%d/%s", containerURL, container.getId(), Constants.PAYLOAD))
311       .then()
312       .extract()
313       .as(SpatialDataPointIO[].class);
314     assertArrayEquals(createdDataPoints.toArray(), dataPoints);
315   }
316 
317   @Test
318   @Order(7)
319   public void getSpatialData_geometryFilterBoundingSphere_success() {
320     BoundingSphere boundingSphere = new BoundingSphere(49, 0, 0, 0);
321 
322     String geometryFilter = JsonConverter.convertToString(boundingSphere);
323     var dataPoints = given()
324       .spec(requestSpecOfDefaultUser)
325       .queryParam("geometryFilter", geometryFilter)
326       .when()
327       .get(String.format("%s/%d/%s", containerURL, container.getId(), Constants.PAYLOAD))
328       .then()
329       .extract()
330       .as(SpatialDataPointIO[].class);
331     // Sphere of radius 49 will include the points [0, 0, 0] [10, 10, 10] and [20, 20, 20]
332     SpatialDataPointIO[] expectedDataPoints = {
333       createdDataPoints.get(0),
334       createdDataPoints.get(1),
335       createdDataPoints.get(2),
336     };
337     assertArrayEquals(expectedDataPoints, dataPoints);
338   }
339 
340   @Test
341   @Order(7)
342   public void getSpatialData_geometryFilterAxisAlignedBoundingBox_success() {
343     AxisAlignedBoundingBox axisAlignedBoundingBox = new AxisAlignedBoundingBox(-1, -1, -1, 49, 49, 49);
344 
345     String geometryFilter = JsonConverter.convertToString(axisAlignedBoundingBox);
346     var dataPoints = given()
347       .spec(requestSpecOfDefaultUser)
348       .queryParam("geometryFilter", geometryFilter)
349       .when()
350       .get(String.format("%s/%d/%s", containerURL, container.getId(), Constants.PAYLOAD))
351       .then()
352       .extract()
353       .as(SpatialDataPointIO[].class);
354     assertEquals(dataPoints.length, 5);
355   }
356 
357   @Test
358   @Order(7)
359   public void getSpatialData_geometryFilterKNN_success() {
360     KNearestNeighbor kNearestNeighbor = new KNearestNeighbor(3, 1, 1, 1);
361 
362     String geometryFilter = JsonConverter.convertToString(kNearestNeighbor);
363 
364     var dataPoints = given()
365       .spec(requestSpecOfDefaultUser)
366       .queryParam("geometryFilter", geometryFilter)
367       .when()
368       .get(String.format("%s/%d/%s", containerURL, container.getId(), Constants.PAYLOAD))
369       .then()
370       .extract()
371       .as(SpatialDataPointIO[].class);
372     assertEquals(dataPoints.length, 3);
373   }
374 
375   @Test
376   @Order(7)
377   public void getSpatialData_geometryFilterKNNWithMetadataFilter_success() {
378     KNearestNeighbor kNearestNeighbor = new KNearestNeighbor(3, 1, 1, 1);
379 
380     String geometryFilter = JsonConverter.convertToString(kNearestNeighbor);
381     String metadataFilter = JsonConverter.convertToString(Map.of("a_meta_data", "metadata_1"));
382     var dataPoints = given()
383       .spec(requestSpecOfDefaultUser)
384       .queryParam("geometryFilter", geometryFilter)
385       .queryParam("metadataFilter", metadataFilter)
386       .when()
387       .get(String.format("%s/%d/%s", containerURL, container.getId(), Constants.PAYLOAD))
388       .then()
389       .extract()
390       .as(SpatialDataPointIO[].class);
391     assertEquals(dataPoints.length, 1);
392     assertEquals(dataPoints[0].getMetadata().get("a_meta_data"), "metadata_1");
393   }
394 
395   @Test
396   @Order(7)
397   public void getSpatialData_geometryFilterKNNWithMeasurementsFilter_success() {
398     KNearestNeighbor kNearestNeighbor = new KNearestNeighbor(3, 1, 1, 1);
399 
400     String geometryFilter = JsonConverter.convertToString(kNearestNeighbor);
401     List<FilterCondition> measurementsFilters = List.of(new FilterCondition("a_measurement", Operator.EQUALS, 1));
402     String measurementsFilter = JsonConverter.convertToString(measurementsFilters);
403     var dataPoints = given()
404       .spec(requestSpecOfDefaultUser)
405       .queryParam("geometryFilter", geometryFilter)
406       .queryParam("measurementsFilter", measurementsFilter)
407       .when()
408       .get(String.format("%s/%d/%s", containerURL, container.getId(), Constants.PAYLOAD))
409       .then()
410       .extract()
411       .as(SpatialDataPointIO[].class);
412     assertEquals(dataPoints.length, 1);
413     assertEquals(dataPoints[0].getMeasurements().get("a_measurement"), 1);
414   }
415 
416   @Test
417   @Order(7)
418   public void getSpatialData_geometryFilterKNNWithTimeDuration_success() {
419     KNearestNeighbor kNearestNeighbor = new KNearestNeighbor(50, 1, 1, 1);
420 
421     String geometryFilter = JsonConverter.convertToString(kNearestNeighbor);
422     var dataPoints = given()
423       .spec(requestSpecOfDefaultUser)
424       .queryParam("geometryFilter", geometryFilter)
425       .queryParam("startTime", 0)
426       .queryParam("endTime", 4)
427       .when()
428       .get(String.format("%s/%d/%s", containerURL, container.getId(), Constants.PAYLOAD))
429       .then()
430       .extract()
431       .as(SpatialDataPointIO[].class);
432     assertEquals(dataPoints.length, 5);
433   }
434 
435   @Test
436   @Order(7)
437   public void getSpatialData_geometryFilterBoundingSphereWithLimit_success() {
438     BoundingSphere boundingSphere = new BoundingSphere(110, 0, 0, 0);
439 
440     String geometryFilter = JsonConverter.convertToString(boundingSphere);
441     var dataPoints = given()
442       .spec(requestSpecOfDefaultUser)
443       .queryParam("geometryFilter", geometryFilter)
444       .queryParam("limit", 5)
445       .when()
446       .get(String.format("%s/%d/%s", containerURL, container.getId(), Constants.PAYLOAD))
447       .then()
448       .extract()
449       .as(SpatialDataPointIO[].class);
450     assertEquals(dataPoints.length, 5);
451   }
452 
453   @Test
454   @Order(7)
455   public void getSpatialData_geometryFilterKNNWithSkip_success() {
456     BoundingSphere boundingSphere = new BoundingSphere(200, 0, 0, 0);
457 
458     String geometryFilter = JsonConverter.convertToString(boundingSphere);
459     var dataPoints = given()
460       .spec(requestSpecOfDefaultUser)
461       .queryParam("geometryFilter", geometryFilter)
462       .queryParam("skip", 2)
463       .when()
464       .get(String.format("%s/%d/%s", containerURL, container.getId(), Constants.PAYLOAD))
465       .then()
466       .extract()
467       .as(SpatialDataPointIO[].class);
468     assertEquals(dataPoints.length, 5);
469   }
470 
471   @Test
472   @Order(7)
473   public void getSpatialData_wrongPermissions_forbid() {
474     BoundingSphere boundingSphere = new BoundingSphere(200, 0, 0, 0);
475     String geometryFilter = JsonConverter.convertToString(boundingSphere);
476     given()
477       .spec(requestSpecOfOtherUser)
478       .queryParam("geometryFilter", geometryFilter)
479       .queryParam("skip", 2)
480       .when()
481       .get(String.format("%s/%d/%s", containerURL, container.getId(), Constants.PAYLOAD))
482       .then()
483       .statusCode(403);
484   }
485 
486   @Test
487   @Order(8)
488   public void getPermissions_success() {
489     PermissionsIO expectedPermissions = new PermissionsIO();
490     expectedPermissions.setEntityId(container.getId());
491     expectedPermissions.setOwner(defaultUser.getUser().getUsername());
492     expectedPermissions.setPermissionType(PermissionType.Private);
493     expectedPermissions.setManager(new String[] {});
494     expectedPermissions.setReader(new String[] {});
495     expectedPermissions.setWriter(new String[] {});
496 
497     PermissionsIO actualPermissions = given()
498       .spec(requestSpecOfDefaultUser)
499       .when()
500       .get(String.format("%s/%d/%s", containerURL, container.getId(), Constants.PERMISSIONS))
501       .then()
502       .statusCode(200)
503       .extract()
504       .as(PermissionsIO.class);
505 
506     assertEquals(expectedPermissions, actualPermissions);
507   }
508 
509   @Test
510   @Order(8)
511   public void getPermissions_wrongPermissions_forbid() {
512     given()
513       .spec(requestSpecOfOtherUser)
514       .when()
515       .get(String.format("%s/%d/%s", containerURL, container.getId(), Constants.PERMISSIONS))
516       .then()
517       .statusCode(403);
518   }
519 
520   @Test
521   @Order(8)
522   public void getRoles_success() {
523     Roles expectedRolesOwner = new Roles();
524     expectedRolesOwner.setOwner(true);
525     expectedRolesOwner.setManager(false);
526     expectedRolesOwner.setReader(false);
527     expectedRolesOwner.setWriter(false);
528 
529     Roles actualRolesOwner = given()
530       .spec(requestSpecOfDefaultUser)
531       .when()
532       .get(String.format("%s/%d/%s", containerURL, container.getId(), Constants.ROLES))
533       .then()
534       .statusCode(200)
535       .extract()
536       .as(Roles.class);
537 
538     assertEquals(expectedRolesOwner, actualRolesOwner);
539   }
540 
541   @Test
542   @Order(9)
543   public void updatePermissions_setPublicReadableAndWritePermissionsToOtherUser_success() {
544     // make container public readable, and set write permissions to OtherUser to upload data
545     PermissionsIO updatedPermissions = new PermissionsIO();
546     updatedPermissions.setEntityId(container.getId());
547     updatedPermissions.setOwner(defaultUser.getUser().getUsername());
548     updatedPermissions.setPermissionType(PermissionType.PublicReadable);
549     updatedPermissions.setManager(new String[] {});
550     updatedPermissions.setReader(new String[] {});
551     updatedPermissions.setWriter(new String[] { otherUser.getUser().getUsername() });
552 
553     PermissionsIO actualPermissions = given()
554       .spec(requestSpecOfDefaultUser)
555       .body(updatedPermissions)
556       .when()
557       .put(String.format("%s/%d/%s", containerURL, container.getId(), Constants.PERMISSIONS))
558       .then()
559       .statusCode(200)
560       .extract()
561       .as(PermissionsIO.class);
562 
563     assertEquals(updatedPermissions, actualPermissions);
564 
565     // OtherUser can now read data from container
566     BoundingSphere boundingSphere = new BoundingSphere(200, 0, 0, 0);
567     String geometryFilter = JsonConverter.convertToString(boundingSphere);
568     given()
569       .spec(requestSpecOfOtherUser)
570       .queryParam("geometryFilter", geometryFilter)
571       .queryParam("skip", 2)
572       .when()
573       .get(String.format("%s/%d/%s", containerURL, container.getId(), Constants.PAYLOAD))
574       .then()
575       .statusCode(200);
576 
577     // OtherUser can now also write data to container
578     createdDataPoints = IntStream.range(0, 10)
579       .mapToObj(index ->
580         new SpatialDataPointIO(
581           Long.valueOf(index),
582           index * 10.0,
583           index * 10.0,
584           index * 10.0,
585           Map.of("a_meta_data", "metadata_%s".formatted(index)),
586           Map.of("a_measurement", index)
587         )
588       )
589       .collect(Collectors.toList());
590     given()
591       .spec(requestSpecOfOtherUser)
592       .body(createdDataPoints)
593       .when()
594       .post(String.format("%s/%d/%s", containerURL, container.getId(), Constants.PAYLOAD))
595       .then()
596       .statusCode(204);
597   }
598 
599   @Test
600   @Order(10)
601   public void updatePermissions_OtherUserIsNotManager_forbid() {
602     PermissionsIO updatedPermissions = new PermissionsIO();
603     updatedPermissions.setEntityId(container.getId());
604     updatedPermissions.setOwner(defaultUser.getUser().getUsername());
605     updatedPermissions.setPermissionType(PermissionType.PublicReadable);
606     updatedPermissions.setManager(new String[] {});
607     updatedPermissions.setReader(new String[] {});
608     updatedPermissions.setWriter(new String[] { otherUser.getUser().getUsername() });
609 
610     given()
611       .spec(requestSpecOfOtherUser)
612       .body(updatedPermissions)
613       .when()
614       .put(String.format("%s/%d/%s", containerURL, container.getId(), Constants.PERMISSIONS))
615       .then()
616       .statusCode(403);
617   }
618 
619   @Test
620   @Order(11)
621   public void deleteContainer() {
622     // non-owner tries to delete container -> 403 forbid
623     given().spec(requestSpecOfOtherUser).when().delete(containerURL + "/" + container.getId()).then().statusCode(403);
624     // owner tries to delete container -> 200 success
625     given().spec(requestSpecOfDefaultUser).when().delete(containerURL + "/" + container.getId()).then().statusCode(200);
626     // owner tries to delete container again -> 404 not found
627     given().spec(requestSpecOfDefaultUser).when().get(containerURL + "/" + container.getId()).then().statusCode(404);
628   }
629 }