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
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
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
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 }