Skip to content

Commit

Permalink
refactor(metadata-search): mock elasticsearch for metadata search tes…
Browse files Browse the repository at this point in the history
…ts (#31)

closes #30
  • Loading branch information
bathienle authored Aug 21, 2024
1 parent f9d6699 commit ec0e85d
Show file tree
Hide file tree
Showing 2 changed files with 128 additions and 105 deletions.
149 changes: 44 additions & 105 deletions src/test/java/be/cytomine/service/search/MetadataSearchServiceTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,67 +15,64 @@
* limitations under the License.
*/

import be.cytomine.BasicInstanceBuilder;
import be.cytomine.CytomineCoreApplication;
import be.cytomine.domain.image.AbstractImage;
import be.cytomine.domain.meta.Property;
import be.cytomine.utils.JsonObject;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import javax.transaction.Transactional;

import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.client.WireMock;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;

import javax.transaction.Transactional;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import be.cytomine.BasicInstanceBuilder;
import be.cytomine.CytomineCoreApplication;
import be.cytomine.domain.image.AbstractImage;
import be.cytomine.utils.JsonObject;
import be.cytomine.utils.WireMockHelper;

import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;

@SpringBootTest(classes = CytomineCoreApplication.class)
@Transactional
public class MetadataSearchServiceTests {

private final IndexCoordinates index = IndexCoordinates.of("properties");

@Autowired
BasicInstanceBuilder builder;

@Autowired
ElasticsearchOperations operations;

@Autowired
MetadataSearchService metadataSearchService;

private WireMockServer wireMockServer;

private WireMockHelper wireMockHelper;

@BeforeEach
public void setup() {
operations.indexOps(index).create();
wireMockServer = new WireMockServer(9200); // Use port 9200 as it is the default Elasticsearch port
wireMockServer.start();
wireMockHelper = new WireMockHelper(wireMockServer);

WireMock.configureFor("localhost", wireMockServer.port());
}

@AfterEach
public void clean() {
operations.indexOps(index).delete();
wireMockServer.stop();
}

@Test
void list_all_images_by_string_filters() {
AbstractImage ai1 = builder.given_an_abstract_image();
AbstractImage ai2 = builder.given_an_abstract_image();
Property p1 = builder.given_a_property(ai1, "key1", "value");
Property p2 = builder.given_a_property(ai1, "key2", "2000");
Property p3 = builder.given_a_property(ai2, "key3", "7000");

for (Property p : Arrays.asList(p1, p2, p3)) {
operations.save(p, index);
}
/* Let elasticsearch refresh the indices */
operations.indexOps(index).refresh();

HashMap<String, Object> query = new HashMap<>();
query.put("key1", "val");
query.put("test-key", "test-value");

wireMockHelper.stubElasticSearchApi(ai1.getId(), query.size());

HashMap<String, Object> parameters = new HashMap<>();
parameters.put("imageIds", Arrays.asList(ai1.getId().intValue(), ai2.getId().intValue()));
Expand All @@ -93,17 +90,11 @@ void list_all_images_by_string_filters() {
void list_all_images_by_number_filters() {
AbstractImage ai1 = builder.given_an_abstract_image();
AbstractImage ai2 = builder.given_an_abstract_image();
Property p1 = builder.given_a_property(ai1, "key1", "value");
Property p2 = builder.given_a_property(ai1, "key2", "2000");

for (Property p : Arrays.asList(p1, p2)) {
operations.save(p, index);
}
/* Let elasticsearch refresh the indices */
operations.indexOps(index).refresh();

HashMap<String, Object> query = new HashMap<>();
query.put("key2", Arrays.asList(1000, 2000));
query.put("test-key", Arrays.asList(0, 2000));

wireMockHelper.stubElasticSearchApi(ai1.getId(), query.size());

HashMap<String, Object> parameters = new HashMap<>();
parameters.put("imageIds", Arrays.asList(ai1.getId().intValue(), ai2.getId().intValue()));
Expand All @@ -121,18 +112,12 @@ void list_all_images_by_number_filters() {
void list_all_images_by_mixed_filters() {
AbstractImage ai1 = builder.given_an_abstract_image();
AbstractImage ai2 = builder.given_an_abstract_image();
Property p1 = builder.given_a_property(ai1, "key1", "value");
Property p2 = builder.given_a_property(ai1, "key2", "2000");

for (Property p : Arrays.asList(p1, p2)) {
operations.save(p, index);
}
/* Let elasticsearch refresh the indices */
operations.indexOps(index).refresh();

HashMap<String, Object> query = new HashMap<>();
query.put("key1", "val");
query.put("key2", Arrays.asList(1000, 2000));
query.put("test-key", Arrays.asList(0, 2000));

wireMockHelper.stubElasticSearchApi(ai1.getId(), query.size());

HashMap<String, Object> parameters = new HashMap<>();
parameters.put("imageIds", Arrays.asList(ai1.getId().intValue(), ai2.getId().intValue()));
Expand All @@ -148,35 +133,17 @@ void list_all_images_by_mixed_filters() {

@Test
void list_no_suggestions_for_wrong_value() {
AbstractImage ai = builder.given_an_abstract_image();
String key = "key";
Property p1 = builder.given_a_property(ai, key, "value1");
Property p2 = builder.given_a_property(ai, key, "value2");

for (Property p : Arrays.asList(p1, p2)) {
operations.save(p, index);
}
/* Let elasticsearch refresh the indices */
operations.indexOps(index).refresh();

List<String> actualSuggestions = metadataSearchService.searchAutoCompletion(key, "wrong");
wireMockHelper.stubElasticAutoCompleteApiReturnEmpty();

List<String> actualSuggestions = metadataSearchService.searchAutoCompletion("key", "wrong");
List<String> expectedSuggestions = List.of();

assertThat(actualSuggestions).isEqualTo(expectedSuggestions);
}

@Test
void list_no_suggestions_for_wrong_key() {
AbstractImage ai = builder.given_an_abstract_image();
String key = "key";
Property p1 = builder.given_a_property(ai, key, "value1");
Property p2 = builder.given_a_property(ai, key, "value2");

for (Property p : Arrays.asList(p1, p2)) {
operations.save(p, index);
}
/* Let elasticsearch refresh the indices */
operations.indexOps(index).refresh();
wireMockHelper.stubElasticAutoCompleteApiReturnEmpty();

List<String> actualSuggestions = metadataSearchService.searchAutoCompletion("wrong", "");
List<String> expectedSuggestions = List.of();
Expand All @@ -186,57 +153,29 @@ void list_no_suggestions_for_wrong_key() {

@Test
void list_all_suggestions_for_partial_value() {
AbstractImage ai = builder.given_an_abstract_image();
String key = "key";
Property p1 = builder.given_a_property(ai, key, "value1");
Property p2 = builder.given_a_property(ai, key, "value2");

for (Property p : Arrays.asList(p1, p2)) {
operations.save(p, index);
}
/* Let elasticsearch refresh the indices */
operations.indexOps(index).refresh();

List<String> actualSuggestions = metadataSearchService.searchAutoCompletion(key, "val");
wireMockHelper.stubElasticAutoCompleteApiReturnResults(List.of("value1", "value2"));

List<String> actualSuggestions = metadataSearchService.searchAutoCompletion("key", "val");
List<String> expectedSuggestions = List.of("value1", "value2");

assertThat(actualSuggestions).isEqualTo(expectedSuggestions);
}

@Test
void list_exact_suggestion_for_value() {
AbstractImage ai = builder.given_an_abstract_image();
String key = "key";
Property p1 = builder.given_a_property(ai, key, "value1");
Property p2 = builder.given_a_property(ai, key, "value2");

for (Property p : Arrays.asList(p1, p2)) {
operations.save(p, index);
}
/* Let elasticsearch refresh the indices */
operations.indexOps(index).refresh();

List<String> actualSuggestions = metadataSearchService.searchAutoCompletion(key, "value1");
wireMockHelper.stubElasticAutoCompleteApiReturnResults(List.of("value1"));

List<String> actualSuggestions = metadataSearchService.searchAutoCompletion("key", "value1");
List<String> expectedSuggestions = List.of("value1");

assertThat(actualSuggestions).isEqualTo(expectedSuggestions);
}

@Test
void list_all_suggestions_for_empty_value() {
AbstractImage ai = builder.given_an_abstract_image();
String key = "key";
Property p1 = builder.given_a_property(ai, key, "value1");
Property p2 = builder.given_a_property(ai, key, "value2");
Property p3 = builder.given_a_property(ai, key, "value3");

for (Property p : Arrays.asList(p1, p2, p3)) {
operations.save(p, index);
}
/* Let elasticsearch refresh the indices */
operations.indexOps(index).refresh();

List<String> actualSuggestions = metadataSearchService.searchAutoCompletion(key, "");
wireMockHelper.stubElasticAutoCompleteApiReturnResults(List.of("value1", "value2", "value3"));

List<String> actualSuggestions = metadataSearchService.searchAutoCompletion("key", "");
List<String> expectedSuggestions = List.of("value1", "value2", "value3");

assertThat(actualSuggestions).isEqualTo(expectedSuggestions);
Expand Down
84 changes: 84 additions & 0 deletions src/test/java/be/cytomine/utils/WireMockHelper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package be.cytomine.utils;

import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;

import com.github.tomakehurst.wiremock.WireMockServer;
import org.springframework.http.HttpStatus;

import static com.github.tomakehurst.wiremock.client.WireMock.*;

public class WireMockHelper {

private final WireMockServer wireMockServer;

public WireMockHelper(WireMockServer wireMockServer) {
this.wireMockServer = wireMockServer;
}

private String generateHitResult(String key, String value) {
return "{\"_index\":\"properties\",\"_id\":\"1041\",\"_score\":1,\"_version\":1," +
"\"_source\":{\"value\":\"" + value + "\",\"@timestamp\":\"2024-08-21T09:04:00.694446626Z\"," +
"\"domain_class_name\":\"be.cytomine.domain.image.AbstractImage\",\"domain_ident\":201," +
"\"key\":\"" + key + "\",\"id\":1041,\"@version\":\"1\"}}";
}

public void stubElasticSearchApi(long id, int filterSize) {
wireMockServer.stubFor(post(urlPathMatching("/properties/_search"))
.willReturn(aResponse()
.withHeader("Content-Type", "application/json")
.withHeader("X-Elastic-Product", "Elasticsearch")
.withBody("{\"took\":911," +
"\"timed_out\":false," +
"\"_shards\":{\"total\":1,\"successful\":1,\"skipped\":0,\"failed\":0}," +
"\"hits\":{\"total\":{\"value\":1,\"relation\":\"eq\"}," +
"\"max_score\":1,\"hits\":[{\"_index\":\"properties\",\"_id\":\"1041\",\"_score\":1," +
"\"_version\":1," +
"\"_source\":{\"value\":\"test-value\",\"@timestamp\":\"2024-08-21T09:04:00.694446626Z\"," +
"\"domain_class_name\":\"be.cytomine.domain.image.AbstractImage\",\"domain_ident\":201," +
"\"key\":\"test-key\",\"id\":1041,\"@version\":\"1\"}}]}," +
"\"aggregations\":{\"lterms#domain_ident_count\":{\"buckets\":" +
"[{\"key\":" + id + ",\"doc_count\":" + filterSize + "}]}}}"
)
.withStatus(HttpStatus.OK.value())
)
);
}

public void stubElasticAutoCompleteApiReturnEmpty() {
wireMockServer.stubFor(post(urlPathMatching("/properties/_search"))
.willReturn(aResponse()
.withHeader("Content-Type", "application/json")
.withHeader("X-Elastic-Product", "Elasticsearch")
.withBody("{\"took\":911,\"timed_out\":false," +
"\"_shards\":{\"total\":1,\"successful\":1,\"skipped\":0,\"failed\":0}," +
"\"hits\":{\"total\":{\"value\":0,\"relation\":\"eq\"}," +
"\"max_score\":0,\"hits\":[]}}"
)
.withStatus(HttpStatus.OK.value())
)
);
}

public void stubElasticAutoCompleteApiReturnResults(List<String> values) {
String hits = values
.stream()
.map(value -> generateHitResult(UUID.randomUUID().toString(), value))
.collect(Collectors.joining(","));

wireMockServer.stubFor(post(urlPathMatching("/properties/_search"))
.willReturn(aResponse()
.withHeader("Content-Type", "application/json")
.withHeader("X-Elastic-Product", "Elasticsearch")
.withBody("{\"took\":911," +
"\"timed_out\":false," +
"\"_shards\":{\"total\":1,\"successful\":1,\"skipped\":0,\"failed\":0}," +
"\"hits\":{\"total\":{\"value\":" + values.size() + ",\"relation\":\"eq\"}," +
"\"max_score\":1,\"hits\":[" + hits + "]}}"
)
.withStatus(HttpStatus.OK.value())
)
);
}
}

0 comments on commit ec0e85d

Please sign in to comment.