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.auth.permission.io.PermissionsIO;
8   import de.dlr.shepard.auth.users.io.UserGroupIO;
9   import de.dlr.shepard.common.neo4j.io.BasicEntityIO;
10  import de.dlr.shepard.common.search.io.QueryType;
11  import de.dlr.shepard.common.search.io.ResponseBody;
12  import de.dlr.shepard.common.search.io.ResultTriple;
13  import de.dlr.shepard.common.search.io.SearchBody;
14  import de.dlr.shepard.common.search.io.SearchParams;
15  import de.dlr.shepard.common.search.io.SearchScope;
16  import de.dlr.shepard.common.util.Constants;
17  import de.dlr.shepard.common.util.TraversalRules;
18  import de.dlr.shepard.context.collection.io.CollectionIO;
19  import de.dlr.shepard.context.collection.io.DataObjectIO;
20  import de.dlr.shepard.context.references.structureddata.io.StructuredDataReferenceIO;
21  import de.dlr.shepard.data.structureddata.entities.StructuredData;
22  import de.dlr.shepard.data.structureddata.entities.StructuredDataPayload;
23  import de.dlr.shepard.data.structureddata.io.StructuredDataContainerIO;
24  import io.quarkus.test.junit.QuarkusIntegrationTest;
25  import io.restassured.builder.RequestSpecBuilder;
26  import io.restassured.http.ContentType;
27  import io.restassured.specification.RequestSpecification;
28  import java.util.Arrays;
29  import java.util.HashSet;
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 StructuredDataSearcherIT extends BaseTestCaseIT {
39  
40    private static String containerURL;
41    private static CollectionIO collection;
42    private static DataObjectIO rootObject;
43    private static DataObjectIO firstChild;
44    private static DataObjectIO secondChild;
45    private static DataObjectIO secondChildFirstGrandchild;
46    private static DataObjectIO cyclicSuccessor;
47    private static DataObjectIO firstSuccessor;
48    private static StructuredDataContainerIO structuredDataContainer;
49    private static StructuredDataContainerIO structuredDataContainerSuccessor;
50    private static StructuredData structuredData;
51    private static StructuredData structuredData1;
52    private static StructuredData structuredDataSuccessor;
53    private static StructuredDataPayload payload;
54    private static StructuredDataPayload payload1;
55    private static StructuredDataPayload payloadSuccessor;
56    private static String structuredDataOID;
57    private static String structuredDataOID1;
58    private static String structuredDataOIDSuccessor;
59    private static StructuredDataReferenceIO reference;
60    private static StructuredDataReferenceIO reference1;
61    private static StructuredDataReferenceIO referenceSuccessor;
62    private static String searchURL;
63    private static UserWithApiKey user1;
64    private static String jws1;
65    private static RequestSpecification searchRequestSpec1;
66    private static UserWithApiKey user2;
67    private static String jws2;
68    private static RequestSpecification searchRequestSpec2;
69  
70    @BeforeAll
71    public static void setUp() {
72      collection = createCollection("SearchTestCollection");
73      rootObject = createDataObject("RootDataObject", collection.getId());
74      firstChild = createDataObjectWithParent("firstChild", collection.getId(), rootObject.getId());
75      secondChild = createDataObjectWithParent("secondChild", collection.getId(), rootObject.getId());
76      secondChildFirstGrandchild = createDataObjectWithParent(
77        "secondChildFirstGrandchild",
78        collection.getId(),
79        secondChild.getId()
80      );
81      long[] firstPredecessorIDs = { firstChild.getId(), secondChild.getId() };
82      firstSuccessor = createDataObjectWithPredecessors("firstSuccessor", collection.getId(), firstPredecessorIDs);
83      structuredDataContainer = createDataContainer("DataContainer");
84      structuredDataContainerSuccessor = createDataContainer("DataContainerSuccessor");
85      cyclicSuccessor = createDataObjectWithPredecessors(
86        "cyclicSuccessor",
87        collection.getId(),
88        new long[] { firstSuccessor.getId() }
89      );
90      long[] newPredecessorIDs = new long[firstSuccessor.getPredecessorIds().length + 1];
91      for (int i = 0; i < firstSuccessor.getPredecessorIds().length; i++) newPredecessorIDs[i] =
92        firstSuccessor.getPredecessorIds()[i];
93      newPredecessorIDs[newPredecessorIDs.length - 1] = cyclicSuccessor.getId();
94      firstSuccessor.setPredecessorIds(newPredecessorIDs);
95      firstSuccessor.setSuccessorIds(null);
96      putDataObject(firstSuccessor.getId(), collection.getId(), firstSuccessor);
97      // create and store first payload
98      var structuredDataToCreate = new StructuredData();
99      structuredDataToCreate.setName("StructuredData");
100     payload = new StructuredDataPayload(structuredDataToCreate, "{\"number1\":3,\"number2\":456}");
101     structuredData = createStructuredData(structuredDataToCreate, structuredDataContainer.getId(), payload);
102     payload.setStructuredData(structuredData);
103     structuredDataOID = structuredData.getOid();
104     String[] dataOIDs = { structuredDataOID };
105     reference = createStructuredDataReference("reference", dataOIDs, structuredDataContainer, secondChild);
106     // create and store another payload
107     var structuredDataToCreate1 = new StructuredData();
108     structuredDataToCreate1.setName("StructuredData1");
109     payload1 = new StructuredDataPayload(structuredDataToCreate, "{\"number1\":0,\"number2\":123}");
110     structuredData1 = createStructuredData(structuredDataToCreate1, structuredDataContainer.getId(), payload1);
111     payload1.setStructuredData(structuredData1);
112     structuredDataOID1 = structuredData1.getOid();
113     String[] dataOIDs1 = { structuredDataOID1 };
114     reference1 = createStructuredDataReference("reference1", dataOIDs1, structuredDataContainer, firstChild);
115     // create and store successor payload
116     var structuredDataToCreateSuccessor = new StructuredData();
117     structuredDataToCreateSuccessor.setName("StructuredDataSuccessor");
118     payloadSuccessor = new StructuredDataPayload(structuredDataToCreateSuccessor, "{\"success\":0,\"number2\":123}");
119     structuredDataSuccessor = createStructuredData(
120       structuredDataToCreateSuccessor,
121       structuredDataContainerSuccessor.getId(),
122       payloadSuccessor
123     );
124     payloadSuccessor.setStructuredData(structuredDataSuccessor);
125     structuredDataOIDSuccessor = structuredDataSuccessor.getOid();
126     String[] dataOIDsSuccessor = { structuredDataOIDSuccessor };
127     referenceSuccessor = createStructuredDataReference(
128       "referenceSucessor",
129       dataOIDsSuccessor,
130       structuredDataContainerSuccessor,
131       firstSuccessor
132     );
133     // prepare search API calls
134     searchURL = "/search";
135     user1 = getNewUserWithApiKey("user1" + System.currentTimeMillis());
136     jws1 = user1.getApiKey().getJws();
137     searchRequestSpec1 = new RequestSpecBuilder().setContentType(ContentType.JSON).addHeader("X-API-KEY", jws1).build();
138     user2 = getNewUserWithApiKey("user2" + System.currentTimeMillis());
139     jws2 = user2.getApiKey().getJws();
140     searchRequestSpec2 = new RequestSpecBuilder().setContentType(ContentType.JSON).addHeader("X-API-KEY", jws2).build();
141   }
142 
143   @Test
144   @Order(1)
145   public void testFindViaChildren() {
146     SearchBody searchBody = new SearchBody();
147     SearchScope searchScope = new SearchScope();
148     searchScope.setCollectionId(collection.getId());
149     searchScope.setDataObjectId(rootObject.getId());
150     TraversalRules[] traversalRules = { TraversalRules.children };
151     searchScope.setTraversalRules(traversalRules);
152     SearchScope[] scopes = { searchScope };
153     searchBody.setScopes(scopes);
154     SearchParams searchParams = new SearchParams();
155     searchParams.setQueryType(QueryType.StructuredData);
156     String query = "number1: {$gt: 1}";
157     searchParams.setQuery(query);
158     searchBody.setSearchParams(searchParams);
159     var result = given()
160       .spec(requestSpecOfDefaultUser)
161       .body(searchBody)
162       .when()
163       .post(searchURL)
164       .then()
165       .statusCode(200)
166       .extract()
167       .as(ResponseBody.class);
168     ResultTriple triple = new ResultTriple(collection.getId(), secondChild.getId(), reference.getId());
169     assertThat(result.getResultSet()).containsExactly(triple);
170     assertThat(result.getSearchParams()).isEqualTo(searchParams);
171     assertThat(Arrays.asList(result.getResults()).stream().map(BasicEntityIO::getId)).containsExactlyInAnyOrder(
172       reference.getId()
173     );
174   }
175 
176   @Test
177   @Order(2)
178   public void testFindWithoutDataObjectId() {
179     SearchBody searchBody = new SearchBody();
180     SearchScope searchScope = new SearchScope();
181     searchScope.setCollectionId(collection.getId());
182     TraversalRules[] traversalRules = {};
183     searchScope.setTraversalRules(traversalRules);
184     SearchScope[] scopes = { searchScope };
185     searchBody.setScopes(scopes);
186     SearchParams searchParams = new SearchParams();
187     searchParams.setQueryType(QueryType.StructuredData);
188     String query = "number1: {$gt: 1}";
189     searchParams.setQuery(query);
190     searchBody.setSearchParams(searchParams);
191     var result = given()
192       .spec(requestSpecOfDefaultUser)
193       .body(searchBody)
194       .when()
195       .post(searchURL)
196       .then()
197       .statusCode(200)
198       .extract()
199       .as(ResponseBody.class);
200     ResultTriple triple = new ResultTriple(collection.getId(), secondChild.getId(), reference.getId());
201     assertThat(result.getResultSet()).containsExactly(triple);
202     assertThat(result.getSearchParams()).isEqualTo(searchParams);
203     assertThat(Arrays.asList(result.getResults()).stream().map(BasicEntityIO::getId)).containsExactlyInAnyOrder(
204       reference.getId()
205     );
206   }
207 
208   @Test
209   @Order(3)
210   public void testFindViaChildrenUniversalSyntaxAND() {
211     SearchBody searchBody = new SearchBody();
212     SearchScope searchScope = new SearchScope();
213     searchScope.setCollectionId(collection.getId());
214     searchScope.setDataObjectId(rootObject.getId());
215     TraversalRules[] traversalRules = { TraversalRules.children };
216     searchScope.setTraversalRules(traversalRules);
217     SearchScope[] scopes = { searchScope };
218     searchBody.setScopes(scopes);
219     SearchParams searchParams = new SearchParams();
220     searchParams.setQueryType(QueryType.StructuredData);
221     String query =
222       "{\"AND\": [{\"property\": \"number1\", \"value\": 3, \"operator\": \"ge\"},{\"property\": \"number1\",\"value\": 3, \"operator\": \"le\"}]}";
223     searchParams.setQuery(query);
224     searchBody.setSearchParams(searchParams);
225     var result = given()
226       .spec(requestSpecOfDefaultUser)
227       .body(searchBody)
228       .when()
229       .post(searchURL)
230       .then()
231       .statusCode(200)
232       .extract()
233       .as(ResponseBody.class);
234     ResultTriple triple = new ResultTriple(collection.getId(), secondChild.getId(), reference.getId());
235     assertThat(result.getResultSet()).containsExactly(triple);
236     assertEquals(query, result.getSearchParams().getQuery());
237     assertThat(Arrays.asList(result.getResults()).stream().map(BasicEntityIO::getId)).containsExactlyInAnyOrder(
238       reference.getId()
239     );
240   }
241 
242   @Test
243   @Order(4)
244   public void testDoNotFindViaChildren() {
245     SearchBody searchBody = new SearchBody();
246     SearchScope searchScope = new SearchScope();
247     searchScope.setCollectionId(collection.getId());
248     searchScope.setDataObjectId(rootObject.getId());
249     TraversalRules[] traversalRules = { TraversalRules.children };
250     searchScope.setTraversalRules(traversalRules);
251     SearchScope[] scopes = { searchScope };
252     searchBody.setScopes(scopes);
253     SearchParams searchParams = new SearchParams();
254     searchParams.setQueryType(QueryType.StructuredData);
255     String query = "number1: {$lt: 0}";
256     searchParams.setQuery(query);
257     searchBody.setSearchParams(searchParams);
258     var result = given()
259       .spec(requestSpecOfDefaultUser)
260       .body(searchBody)
261       .when()
262       .post(searchURL)
263       .then()
264       .statusCode(200)
265       .extract()
266       .as(ResponseBody.class);
267     assertEquals(0, result.getResultSet().length);
268     assertEquals(0, result.getResults().length);
269     assertThat(result.getSearchParams()).isEqualTo(searchParams);
270   }
271 
272   @Test
273   @Order(5)
274   public void testFindViaChildrenUniversalSyntaxOR() {
275     SearchBody searchBody = new SearchBody();
276     SearchScope searchScope = new SearchScope();
277     searchScope.setCollectionId(collection.getId());
278     searchScope.setDataObjectId(rootObject.getId());
279     TraversalRules[] traversalRules = { TraversalRules.children };
280     searchScope.setTraversalRules(traversalRules);
281     SearchScope[] scopes = { searchScope };
282     searchBody.setScopes(scopes);
283     SearchParams searchParams = new SearchParams();
284     searchParams.setQueryType(QueryType.StructuredData);
285     String query =
286       "{\"OR\": [{\"property\": \"number1\", \"value\": 3, \"operator\": \"ge\"}," +
287       "{\"property\": \"number1\",\"value\": 3, \"operator\": \"le\"}]}";
288     searchParams.setQuery(query);
289     searchBody.setSearchParams(searchParams);
290     var result = given()
291       .spec(requestSpecOfDefaultUser)
292       .body(searchBody)
293       .when()
294       .post(searchURL)
295       .then()
296       .statusCode(200)
297       .extract()
298       .as(ResponseBody.class);
299     assertEquals(2, result.getResultSet().length);
300     ResultTriple triple = new ResultTriple(collection.getId(), secondChild.getId(), reference.getId());
301     ResultTriple triple1 = new ResultTriple(collection.getId(), firstChild.getId(), reference1.getId());
302     assertThat(result.getResultSet()).contains(triple);
303     assertThat(result.getResultSet()).contains(triple1);
304     assertEquals(query, result.getSearchParams().getQuery());
305     assertThat(Arrays.asList(result.getResults()).stream().map(BasicEntityIO::getId)).containsExactlyInAnyOrder(
306       reference.getId(),
307       reference1.getId()
308     );
309   }
310 
311   @Test
312   @Order(6)
313   public void testFindViaPredecessor() {
314     SearchBody searchBody = new SearchBody();
315     SearchScope searchScope = new SearchScope();
316     searchScope.setCollectionId(collection.getId());
317     searchScope.setDataObjectId(firstSuccessor.getId());
318     TraversalRules[] traversalRules = { TraversalRules.predecessors };
319     searchScope.setTraversalRules(traversalRules);
320     SearchScope[] scopes = { searchScope };
321     searchBody.setScopes(scopes);
322     SearchParams searchParams = new SearchParams();
323     searchParams.setQueryType(QueryType.StructuredData);
324     String query = "number1: {$gt: 1}";
325     searchParams.setQuery(query);
326     searchBody.setSearchParams(searchParams);
327     var result = given()
328       .spec(requestSpecOfDefaultUser)
329       .body(searchBody)
330       .when()
331       .post(searchURL)
332       .then()
333       .statusCode(200)
334       .extract()
335       .as(ResponseBody.class);
336     ResultTriple triple = new ResultTriple(collection.getId(), secondChild.getId(), reference.getId());
337     assertThat(result.getResultSet()).containsExactly(triple);
338     assertThat(Arrays.asList(result.getResults()).stream().map(BasicEntityIO::getId)).containsExactlyInAnyOrder(
339       reference.getId()
340     );
341     assertThat(result.getSearchParams()).isEqualTo(searchParams);
342   }
343 
344   @Test
345   @Order(7)
346   public void testFindViaChildrenUniversalSyntaxNOT() {
347     SearchBody searchBody = new SearchBody();
348     SearchScope searchScope = new SearchScope();
349     searchScope.setCollectionId(collection.getId());
350     searchScope.setDataObjectId(rootObject.getId());
351     TraversalRules[] traversalRules = { TraversalRules.children };
352     searchScope.setTraversalRules(traversalRules);
353     SearchScope[] scopes = { searchScope };
354     searchBody.setScopes(scopes);
355     SearchParams searchParams = new SearchParams();
356     searchParams.setQueryType(QueryType.StructuredData);
357     String query = "{\"NOT\": {\"property\": \"number1\", \"value\": 3, \"operator\": \"eq\"}}";
358     searchParams.setQuery(query);
359     searchBody.setSearchParams(searchParams);
360     var result = given()
361       .spec(requestSpecOfDefaultUser)
362       .body(searchBody)
363       .when()
364       .post(searchURL)
365       .then()
366       .statusCode(200)
367       .extract()
368       .as(ResponseBody.class);
369     assertEquals(1, result.getResultSet().length);
370     ResultTriple triple1 = new ResultTriple(collection.getId(), firstChild.getId(), reference1.getId());
371     assertThat(result.getResultSet()).contains(triple1);
372     assertEquals(query, result.getSearchParams().getQuery());
373     assertThat(Arrays.asList(result.getResults()).stream().map(BasicEntityIO::getId)).contains(reference1.getId());
374   }
375 
376   @Test
377   @Order(8)
378   public void testDoNotFindViaPredecessor() {
379     SearchBody searchBody = new SearchBody();
380     SearchScope searchScope = new SearchScope();
381     searchScope.setCollectionId(collection.getId());
382     searchScope.setDataObjectId(firstSuccessor.getId());
383     TraversalRules[] traversalRules = { TraversalRules.predecessors };
384     searchScope.setTraversalRules(traversalRules);
385     SearchScope[] scopes = { searchScope };
386     searchBody.setScopes(scopes);
387     SearchParams searchParams = new SearchParams();
388     searchParams.setQueryType(QueryType.StructuredData);
389     String query = "number1: {$lt: 0}";
390     searchParams.setQuery(query);
391     searchBody.setSearchParams(searchParams);
392     var result = given()
393       .spec(requestSpecOfDefaultUser)
394       .body(searchBody)
395       .when()
396       .post(searchURL)
397       .then()
398       .statusCode(200)
399       .extract()
400       .as(ResponseBody.class);
401     assertEquals(0, result.getResultSet().length);
402     assertEquals(0, result.getResults().length);
403     assertThat(result.getSearchParams()).isEqualTo(searchParams);
404   }
405 
406   @Test
407   @Order(9)
408   public void testFindViaChildrenUniversalSyntaxDeMorgan() {
409     SearchBody searchBody = new SearchBody();
410     SearchScope searchScope = new SearchScope();
411     searchScope.setCollectionId(collection.getId());
412     searchScope.setDataObjectId(rootObject.getId());
413     TraversalRules[] traversalRules = { TraversalRules.children };
414     searchScope.setTraversalRules(traversalRules);
415     SearchScope[] scopes = { searchScope };
416     searchBody.setScopes(scopes);
417     SearchParams searchParams = new SearchParams();
418     searchParams.setQueryType(QueryType.StructuredData);
419     String query =
420       "{\"NOT\": {\"OR\": [{\"property\": \"number1\", \"value\": 4, \"operator\": \"gt\"}," +
421       " {\"property\": \"number1\", \"value\": 1, \"operator\": \"lt\"}]}}";
422     searchParams.setQuery(query);
423     searchBody.setSearchParams(searchParams);
424     var result = given()
425       .spec(requestSpecOfDefaultUser)
426       .body(searchBody)
427       .when()
428       .post(searchURL)
429       .then()
430       .statusCode(200)
431       .extract()
432       .as(ResponseBody.class);
433     assertEquals(1, result.getResultSet().length);
434     ResultTriple triple = new ResultTriple(collection.getId(), secondChild.getId(), reference.getId());
435     assertThat(result.getResultSet()).contains(triple);
436     assertEquals(query, result.getSearchParams().getQuery());
437     assertThat(Arrays.asList(result.getResults()).stream().map(BasicEntityIO::getId)).contains(reference.getId());
438   }
439 
440   @Test
441   @Order(10)
442   public void testFindViaParent() {
443     SearchBody searchBody = new SearchBody();
444     SearchScope searchScope = new SearchScope();
445     searchScope.setCollectionId(collection.getId());
446     searchScope.setDataObjectId(secondChildFirstGrandchild.getId());
447     TraversalRules[] traversalRules = { TraversalRules.parents };
448     searchScope.setTraversalRules(traversalRules);
449     SearchScope[] scopes = { searchScope };
450     searchBody.setScopes(scopes);
451     SearchParams searchParams = new SearchParams();
452     searchParams.setQueryType(QueryType.StructuredData);
453     String query = "number1: {$gt: 1}";
454     searchParams.setQuery(query);
455     searchBody.setSearchParams(searchParams);
456     var result = given()
457       .spec(requestSpecOfDefaultUser)
458       .body(searchBody)
459       .when()
460       .post(searchURL)
461       .then()
462       .statusCode(200)
463       .extract()
464       .as(ResponseBody.class);
465     ResultTriple triple = new ResultTriple(collection.getId(), secondChild.getId(), reference.getId());
466     assertThat(result.getResultSet()).containsExactly(triple);
467     assertThat(Arrays.asList(result.getResults()).stream().map(BasicEntityIO::getId)).containsExactlyInAnyOrder(
468       reference.getId()
469     );
470     assertThat(result.getSearchParams()).isEqualTo(searchParams);
471   }
472 
473   @Test
474   @Order(11)
475   public void testFindViaSuccessor() {
476     SearchBody searchBody = new SearchBody();
477     SearchScope searchScope = new SearchScope();
478     searchScope.setCollectionId(collection.getId());
479     searchScope.setDataObjectId(secondChild.getId());
480     TraversalRules[] traversalRules = { TraversalRules.successors };
481     searchScope.setTraversalRules(traversalRules);
482     SearchScope[] scopes = { searchScope };
483     searchBody.setScopes(scopes);
484     SearchParams searchParams = new SearchParams();
485     searchParams.setQueryType(QueryType.StructuredData);
486     String query = "success: {$eq: 0}";
487     searchParams.setQuery(query);
488     searchBody.setSearchParams(searchParams);
489     var result = given()
490       .spec(requestSpecOfDefaultUser)
491       .body(searchBody)
492       .when()
493       .post(searchURL)
494       .then()
495       .statusCode(200)
496       .extract()
497       .as(ResponseBody.class);
498     ResultTriple triple = new ResultTriple(collection.getId(), firstSuccessor.getId(), referenceSuccessor.getId());
499     assertThat(result.getResultSet()).containsExactly(triple);
500     assertThat(Arrays.asList(result.getResults()).stream().map(BasicEntityIO::getId)).containsExactlyInAnyOrder(
501       referenceSuccessor.getId()
502     );
503     assertThat(result.getSearchParams()).isEqualTo(searchParams);
504   }
505 
506   @Test
507   @Order(12)
508   public void testFindMultipleResults() {
509     SearchBody searchBody = new SearchBody();
510     SearchScope searchScope = new SearchScope();
511     searchScope.setCollectionId(collection.getId());
512     searchScope.setDataObjectId(rootObject.getId());
513     TraversalRules[] traversalRules = { TraversalRules.children };
514     searchScope.setTraversalRules(traversalRules);
515     SearchScope[] scopes = { searchScope };
516     searchBody.setScopes(scopes);
517     SearchParams searchParams = new SearchParams();
518     searchParams.setQueryType(QueryType.StructuredData);
519     String query = "number2: {$gt: 0}";
520     searchParams.setQuery(query);
521     searchBody.setSearchParams(searchParams);
522     var result = given()
523       .spec(requestSpecOfDefaultUser)
524       .body(searchBody)
525       .when()
526       .post(searchURL)
527       .then()
528       .statusCode(200)
529       .extract()
530       .as(ResponseBody.class);
531     assertEquals(2, result.getResultSet().length);
532     HashSet<ResultTriple> resultTriples = new HashSet<>();
533     resultTriples.add(result.getResultSet()[0]);
534     resultTriples.add(result.getResultSet()[1]);
535     ResultTriple expectedResult0 = new ResultTriple(collection.getId(), secondChild.getId(), reference.getId());
536     ResultTriple expectedResult1 = new ResultTriple(collection.getId(), firstChild.getId(), reference1.getId());
537     assertThat(resultTriples).containsExactlyInAnyOrder(expectedResult0, expectedResult1);
538     assertThat(Arrays.asList(result.getResults()).stream().map(BasicEntityIO::getId)).containsExactlyInAnyOrder(
539       reference.getId(),
540       reference1.getId()
541     );
542     assertThat(result.getSearchParams()).isEqualTo(searchParams);
543   }
544 
545   @Test
546   @Order(13)
547   public void testFindViaPredecessorCycle() {
548     SearchBody searchBody = new SearchBody();
549     SearchScope searchScope = new SearchScope();
550     searchScope.setCollectionId(collection.getId());
551     searchScope.setDataObjectId(cyclicSuccessor.getId());
552     TraversalRules[] traversalRules = { TraversalRules.predecessors };
553     searchScope.setTraversalRules(traversalRules);
554     SearchScope[] scopes = { searchScope };
555     searchBody.setScopes(scopes);
556     SearchParams searchParams = new SearchParams();
557     searchParams.setQueryType(QueryType.StructuredData);
558     String query = "success: {$eq: 0}";
559     searchParams.setQuery(query);
560     searchBody.setSearchParams(searchParams);
561     var result = given()
562       .spec(requestSpecOfDefaultUser)
563       .body(searchBody)
564       .when()
565       .post(searchURL)
566       .then()
567       .statusCode(200)
568       .extract()
569       .as(ResponseBody.class);
570     assertEquals(1, result.getResultSet().length);
571     assertThat(result.getSearchParams()).isEqualTo(searchParams);
572   }
573 
574   @Test
575   @Order(14)
576   public void testFindViaPredecessorCycleUnauthorized() {
577     SearchBody searchBody = new SearchBody();
578     SearchScope searchScope = new SearchScope();
579     searchScope.setCollectionId(collection.getId());
580     searchScope.setDataObjectId(cyclicSuccessor.getId());
581     TraversalRules[] traversalRules = { TraversalRules.predecessors };
582     searchScope.setTraversalRules(traversalRules);
583     SearchScope[] scopes = { searchScope };
584     searchBody.setScopes(scopes);
585     SearchParams searchParams = new SearchParams();
586     searchParams.setQueryType(QueryType.StructuredData);
587     String query = "success: {$eq: 0}";
588     searchParams.setQuery(query);
589     searchBody.setSearchParams(searchParams);
590     var result = given()
591       .spec(searchRequestSpec1)
592       .body(searchBody)
593       .when()
594       .post(searchURL)
595       .then()
596       .statusCode(200)
597       .extract()
598       .as(ResponseBody.class);
599     assertEquals(0, result.getResultSet().length);
600     assertEquals(0, result.getResults().length);
601     assertThat(result.getSearchParams()).isEqualTo(searchParams);
602   }
603 
604   @Test
605   @Order(15)
606   public void testFindViaPredecessorCyclePermissionsReader() {
607     String permissionsURL = "/collections/" + collection.getId() + "/permissions";
608 
609     PermissionsIO permissions = given()
610       .spec(requestSpecOfDefaultUser)
611       .when()
612       .get(permissionsURL)
613       .then()
614       .statusCode(200)
615       .extract()
616       .as(PermissionsIO.class);
617     String[] reader = { user1.getUser().getUsername() };
618     permissions.setReader(reader);
619     given()
620       .spec(requestSpecOfDefaultUser)
621       .body(permissions)
622       .when()
623       .put(permissionsURL)
624       .then()
625       .statusCode(200)
626       .extract()
627       .as(PermissionsIO.class);
628     SearchBody searchBody = new SearchBody();
629     SearchScope searchScope = new SearchScope();
630     searchScope.setCollectionId(collection.getId());
631     searchScope.setDataObjectId(cyclicSuccessor.getId());
632     TraversalRules[] traversalRules = { TraversalRules.predecessors };
633     searchScope.setTraversalRules(traversalRules);
634     SearchScope[] scopes = { searchScope };
635     searchBody.setScopes(scopes);
636     SearchParams searchParams = new SearchParams();
637     searchParams.setQueryType(QueryType.StructuredData);
638     String query = "success: {$eq: 0}";
639     searchParams.setQuery(query);
640     searchBody.setSearchParams(searchParams);
641     var result = given()
642       .spec(searchRequestSpec1)
643       .body(searchBody)
644       .when()
645       .post(searchURL)
646       .then()
647       .statusCode(200)
648       .extract()
649       .as(ResponseBody.class);
650     assertEquals(1, result.getResultSet().length);
651     assertThat(result.getSearchParams()).isEqualTo(searchParams);
652   }
653 
654   @Test
655   @Order(16)
656   public void testFindViaPredecessorCycleReaderGroup() {
657     String userGroupURL = "/" + Constants.USERGROUPS;
658     UserGroupIO userGroup = new UserGroupIO();
659     userGroup.setName("userGroup");
660     userGroup.setUsernames(new String[] { user2.getUser().getUsername() });
661     UserGroupIO userGroupCreated = given()
662       .spec(requestSpecOfDefaultUser)
663       .body(userGroup)
664       .when()
665       .post(userGroupURL)
666       .then()
667       .statusCode(201)
668       .extract()
669       .as(UserGroupIO.class);
670 
671     String permissionsURL = "/" + Constants.COLLECTIONS + "/" + collection.getId() + "/" + Constants.PERMISSIONS;
672     PermissionsIO permissions = given()
673       .spec(requestSpecOfDefaultUser)
674       .when()
675       .get(permissionsURL)
676       .then()
677       .statusCode(200)
678       .extract()
679       .as(PermissionsIO.class);
680     long[] readerGroupsIds = { userGroupCreated.getId() };
681     permissions.setReaderGroupIds(readerGroupsIds);
682     given()
683       .spec(requestSpecOfDefaultUser)
684       .body(permissions)
685       .when()
686       .put(permissionsURL)
687       .then()
688       .statusCode(200)
689       .extract()
690       .as(PermissionsIO.class);
691     SearchBody searchBody = new SearchBody();
692     SearchScope searchScope = new SearchScope();
693     searchScope.setCollectionId(collection.getId());
694     searchScope.setDataObjectId(cyclicSuccessor.getId());
695     TraversalRules[] traversalRules = { TraversalRules.predecessors };
696     searchScope.setTraversalRules(traversalRules);
697     SearchScope[] scopes = { searchScope };
698     searchBody.setScopes(scopes);
699     SearchParams searchParams = new SearchParams();
700     searchParams.setQueryType(QueryType.StructuredData);
701     String query = "success: {$eq: 0}";
702     searchParams.setQuery(query);
703     searchBody.setSearchParams(searchParams);
704     var result = given()
705       .spec(searchRequestSpec2)
706       .body(searchBody)
707       .when()
708       .post(searchURL)
709       .then()
710       .statusCode(200)
711       .extract()
712       .as(ResponseBody.class);
713     assertEquals(1, result.getResultSet().length);
714     assertThat(result.getSearchParams()).isEqualTo(searchParams);
715   }
716 
717   @Test
718   @Order(17)
719   public void testDoNotFindViaDeletedNode() {
720     deleteDataObject(secondChild);
721     SearchBody searchBody = new SearchBody();
722     SearchScope searchScope = new SearchScope();
723     searchScope.setCollectionId(collection.getId());
724     searchScope.setDataObjectId(rootObject.getId());
725     TraversalRules[] traversalRules = { TraversalRules.children };
726     searchScope.setTraversalRules(traversalRules);
727     SearchScope[] scopes = { searchScope };
728     searchBody.setScopes(scopes);
729     SearchParams searchParams = new SearchParams();
730     searchParams.setQueryType(QueryType.StructuredData);
731     String query = "number1: {$gt: 1}";
732     searchParams.setQuery(query);
733     searchBody.setSearchParams(searchParams);
734     var result = given()
735       .spec(requestSpecOfDefaultUser)
736       .body(searchBody)
737       .when()
738       .post(searchURL)
739       .then()
740       .statusCode(200)
741       .extract()
742       .as(ResponseBody.class);
743     assertEquals(0, result.getResultSet().length);
744     assertThat(result.getSearchParams()).isEqualTo(searchParams);
745   }
746 
747   private static DataObjectIO createDataObjectWithParent(String name, long collectionId, long parentID) {
748     var dataObjectsURL = "/%s/%d/%s/".formatted(Constants.COLLECTIONS, collectionId, Constants.DATA_OBJECTS);
749     DataObjectIO dataObjectIO = new DataObjectIO();
750     dataObjectIO.setName(name);
751     dataObjectIO.setParentId(parentID);
752     var dataObject = given()
753       .spec(requestSpecOfDefaultUser)
754       .body(dataObjectIO)
755       .when()
756       .post(dataObjectsURL)
757       .then()
758       .statusCode(201)
759       .extract()
760       .as(DataObjectIO.class);
761     return dataObject;
762   }
763 
764   private static DataObjectIO createDataObjectWithPredecessors(String name, long collectionId, long[] predecessorsIDs) {
765     var dataObjectsURL = "/%s/%d/%s/".formatted(Constants.COLLECTIONS, collectionId, Constants.DATA_OBJECTS);
766     DataObjectIO dataObjectIO = new DataObjectIO();
767     dataObjectIO.setName(name);
768     dataObjectIO.setPredecessorIds(predecessorsIDs);
769     var dataObject = given()
770       .spec(requestSpecOfDefaultUser)
771       .body(dataObjectIO)
772       .when()
773       .post(dataObjectsURL)
774       .then()
775       .statusCode(201)
776       .extract()
777       .as(DataObjectIO.class);
778     return dataObject;
779   }
780 
781   private static StructuredDataContainerIO createDataContainer(String name) {
782     containerURL = "/" + Constants.STRUCTURED_DATA_CONTAINERS;
783     StructuredDataContainerIO containerToCreate = new StructuredDataContainerIO();
784     containerToCreate.setName(name);
785     return given()
786       .spec(requestSpecOfDefaultUser)
787       .body(containerToCreate)
788       .when()
789       .post(containerURL)
790       .then()
791       .statusCode(201)
792       .extract()
793       .as(StructuredDataContainerIO.class);
794   }
795 
796   private static StructuredData createStructuredData(
797     StructuredData structuredDataToCreate,
798     long containerID,
799     StructuredDataPayload payload
800   ) {
801     containerURL = "/" + Constants.STRUCTURED_DATA_CONTAINERS;
802     return given()
803       .spec(requestSpecOfDefaultUser)
804       .body(payload)
805       .when()
806       .post("%s/%d/%s".formatted(containerURL, containerID, Constants.PAYLOAD))
807       .then()
808       .statusCode(201)
809       .extract()
810       .as(StructuredData.class);
811   }
812 
813   private static StructuredDataReferenceIO createStructuredDataReference(
814     String name,
815     String[] structuredDataOIDs,
816     StructuredDataContainerIO container,
817     DataObjectIO dataObject
818   ) {
819     StructuredDataReferenceIO toCreate = new StructuredDataReferenceIO();
820     toCreate.setName(name);
821     toCreate.setStructuredDataOids(structuredDataOIDs);
822     toCreate.setStructuredDataContainerId(container.getId());
823     String referencesURL =
824       "/%s/%d/%s/%d/%s".formatted(
825           Constants.COLLECTIONS,
826           collection.getId(),
827           Constants.DATA_OBJECTS,
828           dataObject.getId(),
829           Constants.STRUCTURED_DATA_REFERENCES
830         );
831     return given()
832       .spec(requestSpecOfDefaultUser)
833       .body(toCreate)
834       .when()
835       .post(referencesURL)
836       .then()
837       .statusCode(201)
838       .extract()
839       .as(StructuredDataReferenceIO.class);
840   }
841 
842   private static void deleteDataObject(DataObjectIO dataObject) {
843     String dataObjectsURL = "/%s/%d/%s".formatted(Constants.COLLECTIONS, collection.getId(), Constants.DATA_OBJECTS);
844     given()
845       .spec(requestSpecOfDefaultUser)
846       .when()
847       .delete(dataObjectsURL + "/" + dataObject.getId())
848       .then()
849       .statusCode(204);
850   }
851 
852   private static void putDataObject(Long dataObjectToChangeID, Long collectionID, DataObjectIO changedDataObject) {
853     String putURL = "/%s/%d/%s".formatted(Constants.COLLECTIONS, collection.getId(), Constants.DATA_OBJECTS);
854     putURL = putURL + "/" + dataObjectToChangeID;
855     given().spec(requestSpecOfDefaultUser).body(changedDataObject).when().put(putURL).then().statusCode(200);
856   }
857 }