View Javadoc
1   package de.dlr.shepard.context.semantic.services;
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.assertThrows;
6   import static org.junit.jupiter.api.Assertions.assertTrue;
7   import static org.mockito.ArgumentMatchers.any;
8   import static org.mockito.Mockito.verify;
9   import static org.mockito.Mockito.when;
10  
11  import de.dlr.shepard.common.exceptions.InvalidBodyException;
12  import de.dlr.shepard.common.exceptions.InvalidPathException;
13  import de.dlr.shepard.common.neo4j.entities.Annotatable;
14  import de.dlr.shepard.common.util.HasNeo4jId;
15  import de.dlr.shepard.context.collection.entities.Collection;
16  import de.dlr.shepard.context.semantic.ISemanticRepositoryConnector;
17  import de.dlr.shepard.context.semantic.SemanticRepositoryConnectorFactory;
18  import de.dlr.shepard.context.semantic.SemanticRepositoryType;
19  import de.dlr.shepard.context.semantic.daos.SemanticAnnotationDAO;
20  import de.dlr.shepard.context.semantic.daos.SemanticRepositoryDAO;
21  import de.dlr.shepard.context.semantic.entities.SemanticAnnotation;
22  import de.dlr.shepard.context.semantic.entities.SemanticRepository;
23  import de.dlr.shepard.context.semantic.io.SemanticAnnotationIO;
24  import de.dlr.shepard.context.version.daos.VersionableEntityConcreteDAO;
25  import de.dlr.shepard.context.version.entities.VersionableEntity;
26  import de.dlr.shepard.data.timeseries.model.Timeseries;
27  import io.quarkus.test.InjectMock;
28  import io.quarkus.test.component.QuarkusComponentTest;
29  import jakarta.inject.Inject;
30  import jakarta.ws.rs.NotFoundException;
31  import java.util.LinkedHashMap;
32  import java.util.List;
33  import java.util.Map;
34  import java.util.Optional;
35  import org.junit.jupiter.api.BeforeEach;
36  import org.junit.jupiter.api.Test;
37  
38  @QuarkusComponentTest
39  public class SemanticAnnotationServiceTest {
40  
41    @InjectMock
42    SemanticAnnotationDAO semanticAnnotationDAO;
43  
44    @InjectMock
45    SemanticRepositoryDAO semanticRepositoryDAO;
46  
47    @InjectMock
48    VersionableEntityConcreteDAO versionableEntityConcreteDao;
49  
50    @InjectMock
51    SemanticRepositoryConnectorFactory semanticRepositoryConnectorFactory;
52  
53    @InjectMock
54    SemanticRepositoryService semanticRepositoryService;
55  
56    @InjectMock
57    ISemanticRepositoryConnector propConnector;
58  
59    @InjectMock
60    ISemanticRepositoryConnector valConnector;
61  
62    @Inject
63    SemanticAnnotationService service;
64  
65    private static final long SEMANTIC_ANNOTATION_ID = 1L;
66    private static final long EXISTING_PROP_REPO_ID = 2L;
67    private static final long EXISTING_VAL_REPO_ID = 3L;
68    private static final long EXISTING_ENTITY_NEO4J_ID = 4L;
69    private static final long EXISTING_VERSIONABLE_SHEPARD_ID = 5L;
70    private static final long NONEXISTING_ID = 7L;
71  
72    private interface AnnotatableEntity extends Annotatable, HasNeo4jId {}
73  
74    private static final AnnotatableEntity EXISTING_NON_VERSIONABLE_ENTITY = new AnnotatableEntity() {
75      @Override
76      public Long getId() {
77        return EXISTING_ENTITY_NEO4J_ID;
78      }
79  
80      @Override
81      public List<SemanticAnnotation> getAnnotations() {
82        return List.of();
83      }
84  
85      @Override
86      public void addAnnotation(SemanticAnnotation annotation) {}
87    };
88  
89    private static final VersionableEntity EXISTING_VERSIONABLE_ENTITY = new Collection(EXISTING_ENTITY_NEO4J_ID);
90  
91    private static final SemanticRepository PROP_REPO = new SemanticRepository() {
92      {
93        setId(EXISTING_PROP_REPO_ID);
94        setType(SemanticRepositoryType.SKOSMOS);
95        setEndpoint("propEndpoint");
96      }
97    };
98    private static final SemanticRepository VAL_REPO = new SemanticRepository() {
99      {
100       setId(EXISTING_VAL_REPO_ID);
101       setType(SemanticRepositoryType.JSKOS);
102       setEndpoint("valEndpoint");
103     }
104   };
105 
106   private static final SemanticAnnotationIO ANNOTATION = new SemanticAnnotationIO() {
107     {
108       setPropertyIRI("propIri");
109       setPropertyRepositoryId(EXISTING_PROP_REPO_ID);
110       setValueIRI("valIri");
111       setValueRepositoryId(EXISTING_VAL_REPO_ID);
112     }
113   };
114 
115   @BeforeEach
116   public void setUpRepositories() {
117     when(semanticRepositoryDAO.findByNeo4jId(EXISTING_PROP_REPO_ID)).thenReturn(PROP_REPO);
118     when(semanticRepositoryDAO.findByNeo4jId(EXISTING_VAL_REPO_ID)).thenReturn(VAL_REPO);
119 
120     when(
121       semanticRepositoryConnectorFactory.getRepositoryService(SemanticRepositoryType.SKOSMOS, "propEndpoint")
122     ).thenReturn(propConnector);
123     when(
124       semanticRepositoryConnectorFactory.getRepositoryService(SemanticRepositoryType.JSKOS, "valEndpoint")
125     ).thenReturn(valConnector);
126 
127     when(propConnector.getTerm("propIri")).thenReturn(Map.of("", "propObject"));
128     when(valConnector.getTerm("valIri")).thenReturn(Map.of("", "valObject"));
129   }
130 
131   @BeforeEach
132   public void setUpMocks() {
133     when(semanticAnnotationDAO.findAnnotatableByNeo4jIdNotDeleted(EXISTING_ENTITY_NEO4J_ID)).thenReturn(
134       Optional.of(EXISTING_NON_VERSIONABLE_ENTITY)
135     );
136     when(versionableEntityConcreteDao.findByShepardId(EXISTING_VERSIONABLE_SHEPARD_ID)).thenReturn(
137       EXISTING_VERSIONABLE_ENTITY
138     );
139     when(semanticRepositoryService.getRepository(EXISTING_PROP_REPO_ID)).thenReturn(PROP_REPO);
140     when(semanticRepositoryService.getRepository(EXISTING_VAL_REPO_ID)).thenReturn(VAL_REPO);
141     when(semanticAnnotationDAO.createOrUpdate(any())).thenAnswer(invocation -> invocation.getArgument(0));
142   }
143 
144   @Test
145   public void getAllAnnotationsTest() {
146     var expected = List.of(new SemanticAnnotation(SEMANTIC_ANNOTATION_ID));
147     when(semanticAnnotationDAO.findAllSemanticAnnotationsByNeo4jId(EXISTING_ENTITY_NEO4J_ID)).thenReturn(expected);
148     var actual = service.getAllAnnotationsByNeo4jId(EXISTING_ENTITY_NEO4J_ID);
149     assertEquals(expected, actual);
150   }
151 
152   @Test
153   public void getAllAnnotationsByShepardIdTest() {
154     var expected = List.of(new SemanticAnnotation(SEMANTIC_ANNOTATION_ID));
155     when(semanticAnnotationDAO.findAllSemanticAnnotationsByShepardId(EXISTING_ENTITY_NEO4J_ID)).thenReturn(expected);
156     var actual = service.getAllAnnotationsByShepardId(EXISTING_ENTITY_NEO4J_ID);
157     assertEquals(expected, actual);
158   }
159 
160   @Test
161   public void getAnnotationTest() {
162     var expected = new SemanticAnnotation(SEMANTIC_ANNOTATION_ID);
163     when(semanticAnnotationDAO.findByNeo4jId(SEMANTIC_ANNOTATION_ID)).thenReturn(expected);
164     var actual = service.getAnnotationByNeo4jId(SEMANTIC_ANNOTATION_ID);
165     assertEquals(expected, actual);
166   }
167 
168   @Test
169   public void getAnnotationTest_Null() {
170     when(semanticAnnotationDAO.findByNeo4jId(SEMANTIC_ANNOTATION_ID)).thenReturn(null);
171     assertThrows(NotFoundException.class, () -> service.getAnnotationByNeo4jId(1L));
172   }
173 
174   @Test
175   public void createAnnotationByShepardIdTest() {
176     var toCreate = new SemanticAnnotation() {
177       {
178         setPropertyName("propObject");
179         setValueName("valObject");
180         setPropertyIRI("propIri");
181         setPropertyRepository(PROP_REPO);
182         setValueIRI("valIri");
183         setValueRepository(VAL_REPO);
184       }
185     };
186     var expected = new SemanticAnnotation() {
187       {
188         setId(SEMANTIC_ANNOTATION_ID);
189         setPropertyName("propObject");
190         setValueName("valObject");
191         setPropertyIRI("propIri");
192         setPropertyRepository(PROP_REPO);
193         setValueIRI("valIri");
194         setValueRepository(VAL_REPO);
195       }
196     };
197     var entity = new Collection(EXISTING_ENTITY_NEO4J_ID);
198     entity.setShepardId(EXISTING_VERSIONABLE_SHEPARD_ID);
199     var entityUpdated = new Collection(EXISTING_ENTITY_NEO4J_ID);
200     entityUpdated.setShepardId(EXISTING_VERSIONABLE_SHEPARD_ID);
201     entityUpdated.setAnnotations(List.of(expected));
202 
203     when(versionableEntityConcreteDao.findByShepardId(entity.getShepardId())).thenReturn(entity);
204     when(semanticAnnotationDAO.createOrUpdate(toCreate)).thenReturn(expected);
205     when(semanticRepositoryService.getRepository(PROP_REPO.getId())).thenReturn(PROP_REPO);
206     when(semanticRepositoryService.getRepository(VAL_REPO.getId())).thenReturn(VAL_REPO);
207 
208     var actual = service.createAnnotationByShepardId(entity.getShepardId(), ANNOTATION);
209     assertEquals(expected, actual);
210     verify(semanticAnnotationDAO).createOrUpdate(toCreate);
211     verify(versionableEntityConcreteDao).createOrUpdate(entity);
212   }
213 
214   @Test
215   public void createAnnotationByShepardIdTest_EntityNull() {
216     when(versionableEntityConcreteDao.findByNeo4jId(EXISTING_ENTITY_NEO4J_ID)).thenReturn(null);
217     assertThrows(InvalidBodyException.class, () ->
218       service.createAnnotationByShepardId(EXISTING_ENTITY_NEO4J_ID, ANNOTATION)
219     );
220   }
221 
222   @Test
223   public void createAnnotationByShepardIdTest_EntityDeleted() {
224     var entity = new Collection(EXISTING_ENTITY_NEO4J_ID);
225     entity.setDeleted(true);
226     when(versionableEntityConcreteDao.findByShepardId(EXISTING_ENTITY_NEO4J_ID)).thenReturn(entity);
227     assertThrows(InvalidBodyException.class, () ->
228       service.createAnnotationByShepardId(EXISTING_ENTITY_NEO4J_ID, ANNOTATION)
229     );
230   }
231 
232   @Test
233   public void createAnnotationByNeo4jIdTest() {
234     var toCreate = new SemanticAnnotation() {
235       {
236         setPropertyName("propObject");
237         setValueName("valObject");
238         setPropertyIRI("propIri");
239         setPropertyRepository(PROP_REPO);
240         setValueIRI("valIri");
241         setValueRepository(VAL_REPO);
242       }
243     };
244     var expected = new SemanticAnnotation() {
245       {
246         setPropertyName("propObject");
247         setValueName("valObject");
248         setPropertyIRI("propIri");
249         setPropertyRepository(PROP_REPO);
250         setValueIRI("valIri");
251         setValueRepository(VAL_REPO);
252       }
253     };
254     var entity = new Timeseries() {
255       {
256         setId(NONEXISTING_ID);
257       }
258     };
259 
260     when(semanticAnnotationDAO.findAnnotatableByNeo4jIdNotDeleted(entity.getId())).thenReturn(Optional.of(entity));
261     when(semanticRepositoryService.getRepository(PROP_REPO.getId())).thenReturn(PROP_REPO);
262     when(semanticRepositoryService.getRepository(VAL_REPO.getId())).thenReturn(VAL_REPO);
263     when(semanticAnnotationDAO.createOrUpdate(toCreate)).thenReturn(expected);
264 
265     var actual = service.createAnnotationByNeo4jId(entity.getId(), ANNOTATION);
266     assertEquals(expected, actual);
267   }
268 
269   @Test
270   public void createAnnotation_RepositoryDoesNotExist() {
271     when(semanticRepositoryService.getRepository(NONEXISTING_ID)).thenThrow(InvalidPathException.class);
272 
273     // Check nonexistent property repo
274     SemanticAnnotationIO annotation = new SemanticAnnotationIO() {
275       {
276         setPropertyIRI("propIri");
277         setPropertyRepositoryId(NONEXISTING_ID);
278         setValueIRI("valIri");
279         setValueRepositoryId(EXISTING_VAL_REPO_ID);
280       }
281     };
282     assertThrows(NotFoundException.class, () -> service.createAnnotationByNeo4jId(EXISTING_ENTITY_NEO4J_ID, annotation)
283     );
284     assertThrows(NotFoundException.class, () ->
285       service.createAnnotationByShepardId(EXISTING_VERSIONABLE_SHEPARD_ID, annotation)
286     );
287 
288     // Check nonexistent value repo
289     annotation.setPropertyRepositoryId(EXISTING_PROP_REPO_ID);
290     annotation.setValueRepositoryId(NONEXISTING_ID);
291     assertThrows(NotFoundException.class, () -> service.createAnnotationByNeo4jId(EXISTING_ENTITY_NEO4J_ID, annotation)
292     );
293     assertThrows(NotFoundException.class, () ->
294       service.createAnnotationByShepardId(EXISTING_VERSIONABLE_SHEPARD_ID, annotation)
295     );
296   }
297 
298   /**
299    * Set up the sparql connector such that the term @term is returned.
300    */
301   private void mockOntologyTerm(Map<String, String> term) {
302     when(semanticRepositoryConnectorFactory.getRepositoryService(any(), any())).thenReturn(
303       new ISemanticRepositoryConnector() {
304         @Override
305         public Map<String, String> getTerm(String termIri) {
306           return term;
307         }
308 
309         @Override
310         public boolean healthCheck() {
311           return false;
312         }
313       }
314     );
315   }
316 
317   @Test
318   public void createAnnotation_TermIsNull() {
319     mockOntologyTerm(null);
320     assertThrows(InvalidBodyException.class, () ->
321       service.createAnnotationByShepardId(EXISTING_VERSIONABLE_SHEPARD_ID, ANNOTATION)
322     );
323   }
324 
325   @Test
326   public void createAnnotation_TermIsEmptyString() {
327     mockOntologyTerm(Map.of());
328     assertThrows(InvalidBodyException.class, () ->
329       service.createAnnotationByShepardId(EXISTING_VERSIONABLE_SHEPARD_ID, ANNOTATION)
330     );
331   }
332 
333   @Test
334   public void createAnnotation_UseDefaultLabel() {
335     mockOntologyTerm(Map.of("", "default"));
336     var annotation1 = service.createAnnotationByNeo4jId(EXISTING_ENTITY_NEO4J_ID, ANNOTATION);
337     var annotation2 = service.createAnnotationByShepardId(EXISTING_VERSIONABLE_SHEPARD_ID, ANNOTATION);
338     assertEquals("default", annotation1.getPropertyName());
339     assertEquals("default", annotation2.getPropertyName());
340   }
341 
342   @Test
343   public void createAnnotation_PreferEnglishLabel() {
344     mockOntologyTerm(Map.of("en", "english"));
345     var annotation1 = service.createAnnotationByNeo4jId(EXISTING_ENTITY_NEO4J_ID, ANNOTATION);
346     var annotation2 = service.createAnnotationByShepardId(EXISTING_VERSIONABLE_SHEPARD_ID, ANNOTATION);
347     assertEquals("english", annotation1.getPropertyName());
348     assertEquals("english", annotation2.getPropertyName());
349   }
350 
351   @Test
352   public void createAnnotation_UseNextLabelInLine() {
353     mockOntologyTerm(
354       new LinkedHashMap<>() {
355         {
356           put("cz", "czech");
357           put("hr", "croatian");
358         }
359       }
360     );
361     var annotation1 = service.createAnnotationByNeo4jId(EXISTING_ENTITY_NEO4J_ID, ANNOTATION);
362     var annotation2 = service.createAnnotationByShepardId(EXISTING_VERSIONABLE_SHEPARD_ID, ANNOTATION);
363     assertEquals("czech", annotation1.getPropertyName());
364     assertEquals("czech", annotation2.getPropertyName());
365   }
366 
367   @Test
368   public void deleteAnnotationTest() {
369     when(semanticAnnotationDAO.deleteByNeo4jId(SEMANTIC_ANNOTATION_ID)).thenReturn(true);
370     when(semanticAnnotationDAO.findByNeo4jId(SEMANTIC_ANNOTATION_ID)).thenReturn(
371       new SemanticAnnotation(SEMANTIC_ANNOTATION_ID)
372     );
373 
374     var actual = service.deleteAnnotationByNeo4jId(SEMANTIC_ANNOTATION_ID);
375     assertTrue(actual);
376   }
377 
378   @Test
379   public void deleteAnnotationTest_isNull() {
380     when(semanticAnnotationDAO.deleteByNeo4jId(SEMANTIC_ANNOTATION_ID)).thenReturn(false);
381     when(semanticAnnotationDAO.findByNeo4jId(SEMANTIC_ANNOTATION_ID)).thenReturn(
382       new SemanticAnnotation(SEMANTIC_ANNOTATION_ID)
383     );
384 
385     var actual = service.deleteAnnotationByNeo4jId(SEMANTIC_ANNOTATION_ID);
386     assertFalse(actual);
387   }
388 }