Skip to content

Commit 458a118

Browse files
Merge pull request #23 from quantori/feature/create-cqp-storage-elasticsearch
create cqp-storage-elasticsearch
2 parents 430a05c + ca7c05f commit 458a118

31 files changed

+3801
-5
lines changed

cqp-api/build.gradle.kts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,6 @@ group = "com.quantori"
88
description = "Chem query platform. Storage API"
99
version = "0.0.11"
1010

11-
repositories {
12-
mavenLocal()
13-
mavenCentral()
14-
}
15-
1611
dependencies {
1712
implementation("commons-codec:commons-codec:1.15")
1813

@@ -26,6 +21,8 @@ dependencies {
2621

2722
testImplementation("org.mockito:mockito-core:4.11.0")
2823
testImplementation("org.mockito:mockito-inline:4.11.0")
24+
testImplementation("org.mockito:mockito-junit-jupiter:5.12.0")
25+
testImplementation(libs.jackson)
2926
testImplementation(platform("org.junit:junit-bom:5.10.3"))
3027
testImplementation("org.junit.jupiter:junit-jupiter-api")
3128
testImplementation("org.junit.jupiter:junit-jupiter-params")
Lines changed: 305 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,305 @@
1+
package com.quantori.cqp.api.service;
2+
3+
import static org.junit.jupiter.api.Assertions.assertAll;
4+
import static org.junit.jupiter.api.Assertions.assertEquals;
5+
import static org.junit.jupiter.api.Assertions.assertFalse;
6+
import static org.junit.jupiter.api.Assertions.assertThrows;
7+
import static org.junit.jupiter.api.Assertions.assertTrue;
8+
import static org.junit.jupiter.api.Assertions.fail;
9+
import static org.mockito.ArgumentMatchers.any;
10+
import static org.mockito.ArgumentMatchers.anyString;
11+
import static org.mockito.ArgumentMatchers.eq;
12+
import static org.mockito.Mockito.doNothing;
13+
import static org.mockito.Mockito.doThrow;
14+
import static org.mockito.Mockito.when;
15+
16+
import com.quantori.cqp.api.StorageException;
17+
import com.quantori.cqp.api.model.Library;
18+
import com.quantori.cqp.api.model.LibraryType;
19+
import com.quantori.cqp.api.model.Property;
20+
import com.quantori.cqp.api.model.upload.Molecule;
21+
import org.junit.jupiter.api.Test;
22+
import org.junit.jupiter.api.extension.ExtendWith;
23+
import org.junit.jupiter.api.function.Executable;
24+
import org.junit.jupiter.params.ParameterizedTest;
25+
import org.junit.jupiter.params.provider.EnumSource;
26+
import org.mockito.Mock;
27+
import org.mockito.junit.jupiter.MockitoExtension;
28+
29+
import java.time.Instant;
30+
import java.time.temporal.ChronoUnit;
31+
import java.util.LinkedHashMap;
32+
import java.util.Map;
33+
import java.util.Optional;
34+
import java.util.UUID;
35+
import java.util.concurrent.ConcurrentHashMap;
36+
import java.util.concurrent.CountDownLatch;
37+
import java.util.stream.Collectors;
38+
39+
40+
@ExtendWith(MockitoExtension.class)
41+
class StorageLibraryTest {
42+
43+
@Mock
44+
private StorageLibrary storageLibrary;
45+
46+
@Mock
47+
private StorageMolecules storageMolecules;
48+
49+
@Mock
50+
private ItemWriter<Molecule> itemWriter;
51+
52+
private final String LIBRARY_ID = UUID.randomUUID().toString();
53+
54+
@ParameterizedTest
55+
@EnumSource(value = LibraryType.class, names = {"MOLECULES", "REACTIONS"})
56+
void testCreateLibrary(LibraryType libraryType) {
57+
Instant now = Instant.now().truncatedTo(ChronoUnit.MICROS);
58+
String libraryName = "test_create_library_" + libraryType;
59+
60+
Library library = new Library();
61+
library.setName(libraryName);
62+
library.setCreatedStamp(now.plusSeconds(1));
63+
library.setUpdatedStamp(now.plusSeconds(1));
64+
65+
when(storageLibrary.createLibrary(eq(libraryName), eq(libraryType), any())).thenReturn(library);
66+
67+
Library result = storageLibrary.createLibrary(libraryName, libraryType, null);
68+
assertEquals(libraryName, result.getName());
69+
assertEquals(result.getCreatedStamp(), result.getUpdatedStamp());
70+
}
71+
72+
@Test
73+
void testCreateLibrary_withPropertiesMapping() {
74+
Instant now = Instant.now().truncatedTo(ChronoUnit.MICROS);
75+
Map<String, Property> propertiesMapping = Map.of(
76+
"test1", new Property("test 1", Property.PropertyType.STRING),
77+
"test2", new Property("test 2", Property.PropertyType.DATE),
78+
"test3", new Property("test 3", Property.PropertyType.DECIMAL)
79+
);
80+
81+
Library library = new Library();
82+
library.setName("test_create_library_properties_mapping");
83+
library.setCreatedStamp(now.plusSeconds(1));
84+
library.setUpdatedStamp(now.plusSeconds(1));
85+
library.setId(LIBRARY_ID);
86+
87+
when(storageLibrary.createLibrary(anyString(), eq(LibraryType.MOLECULES), eq(propertiesMapping), any()))
88+
.thenReturn(library);
89+
when(storageLibrary.getPropertiesMapping(LIBRARY_ID)).thenReturn(propertiesMapping);
90+
91+
Library result = storageLibrary.createLibrary("test_create_library_properties_mapping",
92+
LibraryType.MOLECULES, propertiesMapping, null);
93+
94+
assertEquals("test_create_library_properties_mapping", result.getName());
95+
assertEquals(result.getCreatedStamp(), result.getUpdatedStamp());
96+
assertEquals(propertiesMapping, storageLibrary.getPropertiesMapping(result.getId()));
97+
98+
Map<String, Property> newProperties = Map.of(
99+
"test4", new Property("test 4", Property.PropertyType.STRING),
100+
"test5", new Property("test 5", Property.PropertyType.DATE),
101+
"test6", new Property("test 6", Property.PropertyType.DECIMAL)
102+
);
103+
Map<String, Property> all = new LinkedHashMap<>(propertiesMapping);
104+
all.putAll(newProperties);
105+
106+
when(storageLibrary.addPropertiesMapping(LIBRARY_ID, newProperties)).thenReturn(true);
107+
when(storageLibrary.getPropertiesMapping(LIBRARY_ID)).thenReturn(all);
108+
109+
boolean status = storageLibrary.addPropertiesMapping(LIBRARY_ID, newProperties);
110+
assertTrue(status);
111+
assertEquals(all, storageLibrary.getPropertiesMapping(LIBRARY_ID));
112+
}
113+
114+
@Test
115+
void testCreateLibrary_addExistingPropertyMapping() {
116+
Map<String, Property> initial = Map.of(
117+
"test1", new Property("test 1", Property.PropertyType.STRING)
118+
);
119+
Map<String, Property> duplicate = Map.of(
120+
"test1", new Property("test 1", Property.PropertyType.STRING)
121+
);
122+
123+
Library library = new Library();
124+
library.setId(LIBRARY_ID);
125+
126+
when(storageLibrary.createLibrary(any(), any(), any(), any())).thenReturn(library);
127+
doThrow(new StorageException("A library %s already contains the following properties test1".formatted(LIBRARY_ID)))
128+
.when(storageLibrary).addPropertiesMapping(eq(LIBRARY_ID), eq(duplicate));
129+
130+
Library result = storageLibrary.createLibrary("lib", LibraryType.MOLECULES, initial, null);
131+
132+
Exception exception = assertThrows(StorageException.class, () ->
133+
storageLibrary.addPropertiesMapping(result.getId(), duplicate)
134+
);
135+
assertEquals("A library %s already contains the following properties test1".formatted(LIBRARY_ID), exception.getMessage());
136+
}
137+
138+
@Test
139+
void testCreateLibrary_updatePropertyMapping() {
140+
Library library = new Library();
141+
library.setId(LIBRARY_ID);
142+
143+
Map<String, Property> newMapping = Map.of(
144+
"test1", new Property("new test 1", Property.PropertyType.DATE, 1, true, false)
145+
);
146+
147+
when(storageLibrary.createLibrary(any(), any(), any(), any())).thenReturn(library);
148+
when(storageLibrary.updatePropertiesMapping(eq(LIBRARY_ID), eq(newMapping))).thenReturn(true);
149+
when(storageLibrary.getPropertiesMapping(eq(LIBRARY_ID))).thenReturn(
150+
Map.of("test1", new Property("new test 1", Property.PropertyType.STRING, 1, true, false))
151+
);
152+
153+
Library result = storageLibrary.createLibrary("lib", LibraryType.MOLECULES, Map.of(), null);
154+
boolean status = storageLibrary.updatePropertiesMapping(result.getId(), newMapping);
155+
assertTrue(status);
156+
157+
Map<String, Property> actual = storageLibrary.getPropertiesMapping(result.getId());
158+
assertEquals("new test 1", actual.get("test1").getName());
159+
assertEquals(Property.PropertyType.STRING, actual.get("test1").getType());
160+
}
161+
162+
@Test
163+
void testCreateLibrary_updateNonExistingPropertyMapping() {
164+
Library library = new Library();
165+
library.setId(LIBRARY_ID);
166+
167+
Map<String, Property> newMapping = Map.of(
168+
"test4", new Property("test 4", Property.PropertyType.STRING)
169+
);
170+
171+
when(storageLibrary.createLibrary(any(), any(), any(), any())).thenReturn(library);
172+
doThrow(new StorageException("A library %s does not contain the following properties test4".formatted(LIBRARY_ID)))
173+
.when(storageLibrary).updatePropertiesMapping(eq(LIBRARY_ID), eq(newMapping));
174+
175+
Library result = storageLibrary.createLibrary("lib", LibraryType.MOLECULES, Map.of(), null);
176+
Exception exception = assertThrows(StorageException.class, () ->
177+
storageLibrary.updatePropertiesMapping(result.getId(), newMapping)
178+
);
179+
assertEquals("A library %s does not contain the following properties test4".formatted(LIBRARY_ID), exception.getMessage());
180+
}
181+
182+
@Test
183+
void testCreateLibrary_updateGeneratedPropertyMapping() {
184+
Library library = new Library();
185+
library.setId(LIBRARY_ID);
186+
187+
Molecule m = new Molecule();
188+
m.setMolProperties(Map.of("test1", "1", "test2", "1.0", "test3", "some value"));
189+
m.setSmiles("");
190+
m.setStructure(new byte[0]);
191+
192+
doNothing().when(itemWriter).write(any());
193+
itemWriter.write(m);
194+
195+
Map<String, Property> properties = Map.of(
196+
"test1", new Property("test1", Property.PropertyType.DECIMAL),
197+
"test2", new Property("test2", Property.PropertyType.DECIMAL),
198+
"test3", new Property("test3", Property.PropertyType.STRING)
199+
);
200+
201+
when(storageLibrary.getPropertiesMapping(LIBRARY_ID)).thenReturn(properties);
202+
when(storageLibrary.updatePropertiesMapping(LIBRARY_ID, properties)).thenReturn(true);
203+
204+
boolean status = storageLibrary.updatePropertiesMapping(LIBRARY_ID, properties);
205+
assertTrue(status);
206+
assertEquals(properties, storageLibrary.getPropertiesMapping(LIBRARY_ID));
207+
}
208+
209+
@ParameterizedTest
210+
@EnumSource(value = LibraryType.class, names = {"MOLECULES", "REACTIONS"})
211+
void testUpdateLibrary(LibraryType libraryType) {
212+
Library library = new Library();
213+
library.setId(LIBRARY_ID);
214+
library.setName("test_update_library_" + libraryType);
215+
library.setStructuresCount(0);
216+
Instant now = Instant.now();
217+
library.setCreatedStamp(now);
218+
library.setUpdatedStamp(now);
219+
220+
when(storageLibrary.createLibrary(any(), any(), any())).thenReturn(library);
221+
when(storageLibrary.updateLibrary(any())).thenReturn(library);
222+
when(storageLibrary.getLibraryById(LIBRARY_ID)).thenReturn(Optional.of(library));
223+
224+
Library created = storageLibrary.createLibrary("lib", libraryType, null);
225+
created.setName("test_update_library_" + libraryType + "_new_name");
226+
227+
Library updated = storageLibrary.updateLibrary(created);
228+
assertEquals("test_update_library_" + libraryType + "_new_name", updated.getName());
229+
assertEquals(created, storageLibrary.getLibraryById(LIBRARY_ID).orElseThrow());
230+
}
231+
232+
@Test
233+
void testUpdateLibrary_parallel() throws InterruptedException {
234+
String libraryName = "test_update_library_parallel";
235+
Library baseLibrary = new Library();
236+
baseLibrary.setId(LIBRARY_ID);
237+
baseLibrary.setName(libraryName);
238+
239+
when(storageLibrary.createLibrary(any(), any(), any())).thenReturn(baseLibrary);
240+
when(storageLibrary.updateLibrary(any())).thenAnswer(invocation -> invocation.getArgument(0));
241+
242+
Library library = storageLibrary.createLibrary(libraryName, LibraryType.MOLECULES, null);
243+
244+
int threadCounts = 10;
245+
CountDownLatch latch = new CountDownLatch(threadCounts);
246+
Thread[] threads = new Thread[threadCounts];
247+
Map<String, String> resultMap = new ConcurrentHashMap<>();
248+
249+
for (int i = 0; i < threadCounts; i++) {
250+
threads[i] = new Thread(() -> {
251+
Library update = new Library();
252+
String newName = library.getName() + "_" + UUID.randomUUID();
253+
update.setId(library.getId());
254+
update.setName(newName);
255+
latch.countDown();
256+
try {
257+
latch.await();
258+
Library updated = storageLibrary.updateLibrary(update);
259+
resultMap.put(newName, updated.getName());
260+
} catch (InterruptedException e) {
261+
fail(e);
262+
}
263+
});
264+
threads[i].start();
265+
}
266+
267+
for (Thread thread : threads) {
268+
thread.join();
269+
}
270+
271+
assertAll(resultMap.entrySet().stream()
272+
.map(entry -> (Executable) () -> assertEquals(entry.getKey(), entry.getValue()))
273+
.collect(Collectors.toList()));
274+
}
275+
276+
@ParameterizedTest
277+
@EnumSource(value = LibraryType.class, names = {"MOLECULES", "REACTIONS"})
278+
void testDeleteLibrary(LibraryType libraryType) {
279+
Library library = new Library();
280+
library.setId(LIBRARY_ID);
281+
library.setName("lib");
282+
library.setType(libraryType);
283+
284+
when(storageLibrary.createLibrary(any(), any(), any())).thenReturn(library);
285+
when(storageLibrary.deleteLibrary(library)).thenReturn(true).thenReturn(false);
286+
287+
Library result = storageLibrary.createLibrary("lib", libraryType, null);
288+
assertTrue(storageLibrary.deleteLibrary(result));
289+
assertFalse(storageLibrary.deleteLibrary(result));
290+
}
291+
292+
@ParameterizedTest
293+
@EnumSource(value = LibraryType.class, names = {"METRICS", "ANY"})
294+
void testDeleteLibraryWrongType(LibraryType libraryType) {
295+
Library library = new Library();
296+
library.setId("1");
297+
library.setName("name");
298+
library.setType(libraryType);
299+
300+
doThrow(new StorageException("A library type must be molecule or reaction"))
301+
.when(storageLibrary).deleteLibrary(library);
302+
303+
assertThrows(StorageException.class, () -> storageLibrary.deleteLibrary(library));
304+
}
305+
}

0 commit comments

Comments
 (0)