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