blob: 279ba6512bb6613cfa5326fc69a3441e38ac67e6 [file] [log] [blame]
/********************************************************************************
* Copyright (c) 2015-2020 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*
********************************************************************************/
package org.eclipse.mdm.api.odsadapter.search;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyCollection;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.text.StringEscapeUtils;
import org.apache.http.HttpHost;
import org.eclipse.mdm.api.base.model.Entity;
import org.eclipse.mdm.api.base.model.Measurement;
import org.eclipse.mdm.api.base.model.TestStep;
import org.eclipse.mdm.api.base.query.DataAccessException;
import org.eclipse.mdm.api.odsadapter.lookup.EntityLoader;
import org.eclipse.mdm.api.odsadapter.lookup.config.EntityConfig.Key;
import org.elasticsearch.client.Request;
import org.elasticsearch.client.RestClient;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Ignore;
import org.junit.Test;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.testcontainers.elasticsearch.ElasticsearchContainer;
import org.testcontainers.shaded.com.fasterxml.jackson.databind.ObjectMapper;
@Ignore
//FIXME 05.02.2020: this test needs a docker to run elasticsearch and is not suitable for continous build in Jenkins.
//Comment this in for local tests only, if docker is available
public class ODSFreeTextSearchTest {
private EntityLoader loader;
private ODSFreeTextSearch fts;
private TestStep ts;
private RestClient client;
@ClassRule
public static ElasticsearchContainer elasticSearch = new ElasticsearchContainer(
"docker.elastic.co/elasticsearch/elasticsearch-oss:7.0.0");
@SuppressWarnings("unchecked")
@Before
public void init() throws DataAccessException {
ts = mock(TestStep.class);
loader = mock(EntityLoader.class);
when(loader.loadAll(any(Key.class), anyCollection())).thenAnswer(new Answer<List<Entity>>() {
@Override
public List<Entity> answer(InvocationOnMock invocation) throws Throwable {
Collection<Long> object = (Collection<Long>) invocation.getArguments()[1];
List<Entity> res = new ArrayList<>();
for (int i = 0; i < object.size(); i++) {
res.add(ts);
}
return res;
}
});
client = RestClient.builder(HttpHost.create(elasticSearch.getHttpHostAddress())).build();
fts = new ODSFreeTextSearch(loader, "mdm", "http://" + elasticSearch.getHttpHostAddress());
}
@Test
public void noIndex_emptyResult() throws DataAccessException {
ODSFreeTextSearch ftsOtherIndex = new ODSFreeTextSearch(loader, "UNKNOWN_INDEX",
"http://" + elasticSearch.getHttpHostAddress());
Map<Class<? extends Entity>, List<Entity>> map = ftsOtherIndex.search("VAG_002");
assertThat(map).isEmpty();
}
@Test
public void exampleIndex_querySuccessfull() throws Exception {
try {
createExampleIndex("mdm", "TestStep", "asdf");
assertThat(fts.search("asdf").get(TestStep.class).get(0)).isEqualTo(ts);
} finally {
deleteIndex("mdm");
}
}
@Test
public void specialCharacters_correctlyEscaped() throws InterruptedException {
try {
createExampleIndex("mdm", "Measurement", "hallo\"!§");
Map<Class<? extends Entity>, List<Entity>> search = fts.search("hallo\"!§");
assertThat(search.get(Measurement.class).get(0)).isEqualTo(ts);
} finally {
deleteIndex("mdm");
}
}
@Test
public void nonMdmResults_ignored() throws InterruptedException {
try {
createExampleIndex("mdm", "NONMDMStuff", "test");
Map<Class<? extends Entity>, List<Entity>> search = fts.search("test");
assertThat(search).isEmpty();
} finally {
deleteIndex("mdm");
}
}
@Test
public void twoResults_twoResultsRetuned() throws InterruptedException {
try {
createExampleIndex("mdm", "Test", "0", "unicorn ASDF");
createExampleIndex("mdm", "Test", "1", "unicorn XYZ");
Map<Class<? extends Entity>, List<Entity>> search = fts.search("unicorn");
assertThat(search.get(org.eclipse.mdm.api.base.model.Test.class)).hasSize(2);
} finally {
deleteIndex("mdm");
}
}
@Test(expected = IllegalStateException.class)
public void illegalLoadRequest_niceExceptionIsThrown() throws DataAccessException, InterruptedException {
try {
loader = mock(EntityLoader.class);
when(loader.loadAll(any(), anyCollection())).thenThrow(new DataAccessException(""));
createExampleIndex("mdm2", "TestStep", "asdf");
ODSFreeTextSearch fts2 = new ODSFreeTextSearch(loader, "mdm2",
"http://" + elasticSearch.getHttpHostAddress());
fts2.search("asdf");
} finally {
deleteIndex("mdm2");
}
}
private void createExampleIndex(String indexName, String type, String value) throws InterruptedException {
createExampleIndex(indexName, type, "0", value);
}
private void createExampleIndex(String indexName, String type, String id, String value)
throws InterruptedException {
ObjectMapper mapper = new ObjectMapper();
Map<String, String> json = new HashMap<>();
json.put("id", StringEscapeUtils.escapeJson(id));
json.put("type", StringEscapeUtils.escapeJson(type));
json.put("attr", StringEscapeUtils.escapeJson(value));
try {
Request a = new Request("PUT", indexName + "/_doc/" + type + "-" + id);
a.setJsonEntity(mapper.writeValueAsString(json));
client.performRequest(a);
} catch (IOException e) {
throw new RuntimeException("Could not index!");
}
Thread.sleep(1000); // let the index some time to populate
}
private void deleteIndex(String indexName) {
try {
client.performRequest(new Request("DELETE", indexName));
} catch (IOException e) {
throw new RuntimeException("Could not delete index!");
}
}
}