View Javadoc
1   package de.dlr.shepard.common.neo4j.daos;
2   
3   import static org.junit.jupiter.api.Assertions.assertEquals;
4   import static org.junit.jupiter.api.Assertions.assertFalse;
5   import static org.junit.jupiter.api.Assertions.assertTrue;
6   import static org.mockito.ArgumentMatchers.any;
7   import static org.mockito.ArgumentMatchers.eq;
8   import static org.mockito.Mockito.doNothing;
9   import static org.mockito.Mockito.mock;
10  import static org.mockito.Mockito.verify;
11  import static org.mockito.Mockito.when;
12  
13  import de.dlr.shepard.BaseTestCase;
14  import de.dlr.shepard.common.util.TraversalRules;
15  import java.util.List;
16  import java.util.Map;
17  import java.util.stream.Stream;
18  import lombok.Data;
19  import org.junit.jupiter.api.Test;
20  import org.junit.jupiter.params.ParameterizedTest;
21  import org.junit.jupiter.params.provider.Arguments;
22  import org.junit.jupiter.params.provider.MethodSource;
23  import org.mockito.ArgumentCaptor;
24  import org.mockito.Captor;
25  import org.mockito.InjectMocks;
26  import org.mockito.Mock;
27  import org.neo4j.ogm.cypher.ComparisonOperator;
28  import org.neo4j.ogm.cypher.Filter;
29  import org.neo4j.ogm.cypher.query.Pagination;
30  import org.neo4j.ogm.model.QueryStatistics;
31  import org.neo4j.ogm.model.Result;
32  import org.neo4j.ogm.session.Session;
33  
34  public class GenericDAOTest extends BaseTestCase {
35  
36    @Data
37    private static class TestObject {
38  
39      private final int a;
40    }
41  
42    private static class TestDAO extends GenericDAO<TestObject> {
43  
44      @Override
45      public Class<TestObject> getEntityType() {
46        return TestObject.class;
47      }
48    }
49  
50    @Mock
51    private Session session;
52  
53    @InjectMocks
54    private TestDAO dao = new TestDAO();
55  
56    @Captor
57    ArgumentCaptor<Pagination> paginationCaptor;
58  
59    @Test
60    public void findAllTest() {
61      var a = new TestObject(1);
62      var b = new TestObject(2);
63  
64      when(session.loadAll(TestObject.class, 1)).thenReturn(List.of(a, b));
65      var actual = dao.findAll();
66      assertEquals(List.of(a, b), actual);
67    }
68  
69    @Test
70    public void findMatchingTest() {
71      var a = new TestObject(1);
72      var filter = new Filter("a", ComparisonOperator.EQUALS, 1);
73  
74      when(session.loadAll(TestObject.class, filter, 1)).thenReturn(List.of(a));
75      var actual = dao.findMatching(filter);
76      assertEquals(List.of(a), actual);
77    }
78  
79    @Test
80    public void findTest() {
81      var a = new TestObject(1);
82  
83      when(session.load(TestObject.class, 1L, 1)).thenReturn(a);
84      var actual = dao.findByNeo4jId(1L);
85      assertEquals(a, actual);
86    }
87  
88    @Test
89    public void findLightTest() {
90      var a = new TestObject(1);
91  
92      when(session.load(TestObject.class, 1L, 0)).thenReturn(a);
93      var actual = dao.findLightByNeo4jId(1L);
94      assertEquals(a, actual);
95    }
96  
97    @Test
98    public void deleteTest_Successful() {
99      var a = new TestObject(1);
100 
101     when(session.load(TestObject.class, 1L)).thenReturn(a);
102     doNothing().when(session).delete(a);
103     var actual = dao.deleteByNeo4jId(1L);
104     assertTrue(actual);
105   }
106 
107   @Test
108   public void deleteTest_Error() {
109     var a = new TestObject(1);
110 
111     when(session.load(TestObject.class, 1L)).thenReturn(null);
112     doNothing().when(session).delete(a);
113     var actual = dao.deleteByNeo4jId(1L);
114     assertFalse(actual);
115   }
116 
117   @Test
118   public void createOrUpdateTest() {
119     var a = new TestObject(1);
120 
121     doNothing().when(session).save(a, 1);
122     var actual = dao.createOrUpdate(a);
123     assertEquals(a, actual);
124   }
125 
126   @Test
127   public void findByQueryTest() {
128     var a = new TestObject(1);
129     var query = "MATCH (n {a: 1}) RETURN n";
130     Map<String, Object> params = Map.of("a", "b", "c", "d");
131 
132     when(session.query(TestObject.class, query, params)).thenReturn(List.of(a));
133     var actual = dao.findByQuery(query, params);
134     assertEquals(List.of(a), actual);
135   }
136 
137   @Test
138   public void runQueryTest() {
139     var query = "MATCH (n {a: 1}) RETURN n";
140     Map<String, Object> params = Map.of("a", "b", "c", "d");
141     Result result = mock(Result.class);
142     QueryStatistics stat = mock(QueryStatistics.class);
143 
144     when(session.query(query, params)).thenReturn(result);
145     when(result.queryStatistics()).thenReturn(stat);
146     when(stat.containsUpdates()).thenReturn(true);
147 
148     var actual = dao.runQuery(query, params);
149     assertTrue(actual);
150   }
151 
152   @Test
153   public void getSearchForReachableReferencesQueryStartIdTest() {
154     long startId = 1L;
155     long collectionId = 2L;
156     String userName = "user";
157     String startIdQuery =
158       "MATCH path = (col:Collection)-[:has_dataobject]->(d:DataObject)-[hr:has_reference]->(r:TestObject) WITH nodes(path) as ns, r as ret WHERE id(d) = 1 AND id(col) = 2 AND NONE(node IN ns WHERE (node.deleted = TRUE)) AND (NOT exists((col)-[:has_permissions]->(:Permissions)) OR exists((col)-[:has_permissions]->(:Permissions)-[:readable_by|owned_by]->(:User { username: \"user\" })) OR exists((col)-[:has_permissions]->(:Permissions {permissionType: \"Public\"})) OR exists((col)-[:has_permissions]->(:Permissions {permissionType: \"PublicReadable\"})) OR exists((col)-[:has_permissions]->(:Permissions)-[:readable_by_group]->(:UserGroup)<-[:is_in_group]-(:User { username: \"user\"}))) MATCH path=(ret)-[*0..1]-(n) WHERE n.deleted = FALSE OR n.deleted IS NULL RETURN ret, nodes(path), relationships(path)";
159     var actual = dao.getSearchForReachableReferencesQuery(collectionId, startId, userName);
160     assertEquals(startIdQuery, actual);
161   }
162 
163   @Test
164   public void getSearchForReachableReferencesByShepardIdQueryStartIdTest() {
165     long startShepardId = 11L;
166     long collectionShepardId = 21L;
167     String userName = "user";
168     String startIdQuery =
169       "MATCH path = (col:Collection)-[:has_dataobject]->(d:DataObject)-[hr:has_reference]->(r:TestObject) WITH nodes(path) as ns, r as ret WHERE d.shepardId = 11 AND col.shepardId = 21 AND NONE(node IN ns WHERE (node.deleted = TRUE)) AND (NOT exists((col)-[:has_permissions]->(:Permissions)) OR exists((col)-[:has_permissions]->(:Permissions)-[:readable_by|owned_by]->(:User { username: \"user\" })) OR exists((col)-[:has_permissions]->(:Permissions {permissionType: \"Public\"})) OR exists((col)-[:has_permissions]->(:Permissions {permissionType: \"PublicReadable\"})) OR exists((col)-[:has_permissions]->(:Permissions)-[:readable_by_group]->(:UserGroup)<-[:is_in_group]-(:User { username: \"user\"}))) MATCH path=(ret)-[*0..1]-(n) WHERE n.deleted = FALSE OR n.deleted IS NULL RETURN ret, nodes(path), relationships(path)";
170     var actual = dao.getSearchForReachableReferencesByShepardIdQuery(collectionShepardId, startShepardId, userName);
171     assertEquals(startIdQuery, actual);
172   }
173 
174   @Test
175   public void getSearchForReachableReferencesByShepardIdWithoutStartIdQueryStartIdTest() {
176     long collectionShepardId = 21L;
177     String username = "Leonard Bernstein";
178     String startIdQuery =
179       "MATCH path = (col:Collection)-[:has_dataobject]->(do:DataObject)-[hr:has_reference]->(r:TestObject) WITH nodes(path) as ns, r as ret WHERE col.shepardId = 21 AND NONE(node IN ns WHERE (node.deleted = TRUE)) AND (NOT exists((col)-[:has_permissions]->(:Permissions)) OR exists((col)-[:has_permissions]->(:Permissions)-[:readable_by|owned_by]->(:User { username: \"Leonard Bernstein\" })) OR exists((col)-[:has_permissions]->(:Permissions {permissionType: \"Public\"})) OR exists((col)-[:has_permissions]->(:Permissions {permissionType: \"PublicReadable\"})) OR exists((col)-[:has_permissions]->(:Permissions)-[:readable_by_group]->(:UserGroup)<-[:is_in_group]-(:User { username: \"Leonard Bernstein\"}))) MATCH path=(ret)-[*0..1]-(n) WHERE n.deleted = FALSE OR n.deleted IS NULL RETURN ret, nodes(path), relationships(path)";
180     var actual = dao.getSearchForReachableReferencesByShepardIdQuery(collectionShepardId, username);
181     assertEquals(startIdQuery, actual);
182   }
183 
184   private static Stream<Arguments> getSearchForReachableReferencesByShepardIdQueryTest() {
185     String childrenQuery =
186       "MATCH path = (col:Collection)-[:has_dataobject]->(d:DataObject)-[:has_child*0..]->(e:DataObject)-[hr:has_reference]->(r:TestObject) WITH nodes(path) as ns, r as ret WHERE d.shepardId = 11 AND col.shepardId = 21 AND NONE(node IN ns WHERE (node.deleted = TRUE)) AND (NOT exists((col)-[:has_permissions]->(:Permissions)) OR exists((col)-[:has_permissions]->(:Permissions)-[:readable_by|owned_by]->(:User { username: \"user\" })) OR exists((col)-[:has_permissions]->(:Permissions {permissionType: \"Public\"})) OR exists((col)-[:has_permissions]->(:Permissions {permissionType: \"PublicReadable\"})) OR exists((col)-[:has_permissions]->(:Permissions)-[:readable_by_group]->(:UserGroup)<-[:is_in_group]-(:User { username: \"user\"}))) MATCH path=(ret)-[*0..1]-(n) WHERE n.deleted = FALSE OR n.deleted IS NULL RETURN ret, nodes(path), relationships(path)";
187     String parentsQuery =
188       "MATCH path = (col:Collection)-[:has_dataobject]->(d:DataObject)<-[:has_child*0..]-(e:DataObject)-[hr:has_reference]->(r:TestObject) WITH nodes(path) as ns, r as ret WHERE d.shepardId = 11 AND col.shepardId = 21 AND NONE(node IN ns WHERE (node.deleted = TRUE)) AND (NOT exists((col)-[:has_permissions]->(:Permissions)) OR exists((col)-[:has_permissions]->(:Permissions)-[:readable_by|owned_by]->(:User { username: \"user\" })) OR exists((col)-[:has_permissions]->(:Permissions {permissionType: \"Public\"})) OR exists((col)-[:has_permissions]->(:Permissions {permissionType: \"PublicReadable\"})) OR exists((col)-[:has_permissions]->(:Permissions)-[:readable_by_group]->(:UserGroup)<-[:is_in_group]-(:User { username: \"user\"}))) MATCH path=(ret)-[*0..1]-(n) WHERE n.deleted = FALSE OR n.deleted IS NULL RETURN ret, nodes(path), relationships(path)";
189     String predecessorsQuery =
190       "MATCH path = (col:Collection)-[:has_dataobject]->(d:DataObject)<-[:has_successor*0..]-(e:DataObject)-[hr:has_reference]->(r:TestObject) WITH nodes(path) as ns, r as ret WHERE d.shepardId = 11 AND col.shepardId = 21 AND NONE(node IN ns WHERE (node.deleted = TRUE)) AND (NOT exists((col)-[:has_permissions]->(:Permissions)) OR exists((col)-[:has_permissions]->(:Permissions)-[:readable_by|owned_by]->(:User { username: \"user\" })) OR exists((col)-[:has_permissions]->(:Permissions {permissionType: \"Public\"})) OR exists((col)-[:has_permissions]->(:Permissions {permissionType: \"PublicReadable\"})) OR exists((col)-[:has_permissions]->(:Permissions)-[:readable_by_group]->(:UserGroup)<-[:is_in_group]-(:User { username: \"user\"}))) MATCH path=(ret)-[*0..1]-(n) WHERE n.deleted = FALSE OR n.deleted IS NULL RETURN ret, nodes(path), relationships(path)";
191     String successorsQuery =
192       "MATCH path = (col:Collection)-[:has_dataobject]->(d:DataObject)-[:has_successor*0..]->(e:DataObject)-[hr:has_reference]->(r:TestObject) WITH nodes(path) as ns, r as ret WHERE d.shepardId = 11 AND col.shepardId = 21 AND NONE(node IN ns WHERE (node.deleted = TRUE)) AND (NOT exists((col)-[:has_permissions]->(:Permissions)) OR exists((col)-[:has_permissions]->(:Permissions)-[:readable_by|owned_by]->(:User { username: \"user\" })) OR exists((col)-[:has_permissions]->(:Permissions {permissionType: \"Public\"})) OR exists((col)-[:has_permissions]->(:Permissions {permissionType: \"PublicReadable\"})) OR exists((col)-[:has_permissions]->(:Permissions)-[:readable_by_group]->(:UserGroup)<-[:is_in_group]-(:User { username: \"user\"}))) MATCH path=(ret)-[*0..1]-(n) WHERE n.deleted = FALSE OR n.deleted IS NULL RETURN ret, nodes(path), relationships(path)";
193     // @formatter:off
194 	    return Stream.of(
195 	    		Arguments.of(TraversalRules.children, childrenQuery),
196 	    		Arguments.of(TraversalRules.parents, parentsQuery),
197 	    		Arguments.of(TraversalRules.predecessors, predecessorsQuery),
198 	    		Arguments.of(TraversalRules.successors, successorsQuery)
199 	    	    );
200     // @formatter:on
201   }
202 
203   @ParameterizedTest
204   @MethodSource
205   public void getSearchForReachableReferencesByShepardIdQueryTest(TraversalRules traversalRules, String expected) {
206     long startShepardId = 11L;
207     long collectionShepardId = 21L;
208     String userName = "user";
209     var actual = dao.getSearchForReachableReferencesByShepardIdQuery(
210       traversalRules,
211       collectionShepardId,
212       startShepardId,
213       userName
214     );
215     assertEquals(expected, actual);
216   }
217 
218   @Test
219   public void getSearchForReachableReferencesQueryCollectionIdOnly() {
220     long collectionId = 1L;
221     String userName = "user";
222     String expected =
223       "MATCH path = (col:Collection)-[:has_dataobject]->(do:DataObject)-[hr:has_reference]->(r:TestObject) WITH nodes(path) as ns, r as ret WHERE id(col) = 1 AND NONE(node IN ns WHERE (node.deleted = TRUE)) AND (NOT exists((col)-[:has_permissions]->(:Permissions)) OR exists((col)-[:has_permissions]->(:Permissions)-[:readable_by|owned_by]->(:User { username: \"user\" })) OR exists((col)-[:has_permissions]->(:Permissions {permissionType: \"Public\"})) OR exists((col)-[:has_permissions]->(:Permissions {permissionType: \"PublicReadable\"})) OR exists((col)-[:has_permissions]->(:Permissions)-[:readable_by_group]->(:UserGroup)<-[:is_in_group]-(:User { username: \"user\"}))) MATCH path=(ret)-[*0..1]-(n) WHERE n.deleted = FALSE OR n.deleted IS NULL RETURN ret, nodes(path), relationships(path)";
224     String actual = dao.getSearchForReachableReferencesQuery(collectionId, userName);
225     assertEquals(expected, actual);
226   }
227 
228   //new test
229   @Test
230   public void deleteRelationTest() {
231     long fromId = 1L;
232     long toId = 2L;
233     String fromType = "fromType";
234     String toType = "toType";
235     String relationName = "relationName";
236     dao.deleteRelation(fromId, toId, fromType, toType, relationName);
237     String expected = "MATCH (a:fromType {shepardId: 1})-[r:relationName]->(b:toType {shepardId: 2}) DELETE r;";
238     verify(session).query(eq(expected), any());
239   }
240 }