package com.temenos.interaction.commands.solr;
/*
* Unit tests for the SOLR Select command. These are self contained. They start a local embedded SOLR server. This is contained
* in, and only accessible by, the test's local (jUnit) JVM. No external server is required. The working directory must be set
* to the location of the SOLR configuration files. Currently this is:
*
* Hothouse\Jenkins
*
* Due to mocking of the server connection there are some features (e.g. failure on bad core names) that cannot be tested here.
* These should be tested by HotHouse integration tests,
*/
/*
* #%L
* interaction-commands-solr
* %%
* Copyright (C) 2012 - 2013 Temenos Holdings N.V.
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.*;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.UriInfo;
import org.apache.solr.client.solrj.SolrServer;
import org.apache.solr.client.solrj.impl.HttpSolrServer;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import com.temenos.interaction.commands.solr.data.SolrConstants;
import com.temenos.interaction.core.MultivaluedMapImpl;
import com.temenos.interaction.core.command.InteractionCommand;
import com.temenos.interaction.core.command.InteractionCommand.Result;
import com.temenos.interaction.core.command.InteractionContext;
import com.temenos.interaction.core.command.InteractionException;
import com.temenos.interaction.core.entity.Entity;
import com.temenos.interaction.core.entity.Metadata;
import com.temenos.interaction.core.hypermedia.ResourceState;
import com.temenos.interaction.core.resource.CollectionResource;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.net.URL;
/**
* The Class SolrSearchCommandTest.
*/
public class SolrSearchCommandTest extends AbstractSolrTest {
private @Mock ResourceState currentState;
private SolrSearchCommand command;
private InteractionContext ctx;
private MultivaluedMap<String, String> queryParams;
private MultivaluedMap<String, String> pathParams;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
command = new SolrSearchCommand();
queryParams = new MultivaluedMapImpl<String>();
pathParams = new MultivaluedMapImpl<String>();
ctx = spy(new InteractionContext(
mock(UriInfo.class),
mock(HttpHeaders.class),
pathParams,
queryParams,
mock(ResourceState.class),
mock(Metadata.class)));
when(ctx.getCurrentState()).thenReturn(currentState);
}
/**
* Test select for single entities. Search on all fields.
*/
@SuppressWarnings("unchecked")
@Test
public void testEntity1SelectAllFields() {
when(currentState.getEntityName()).thenReturn(ENTITY1_TYPE);
queryParams.add("q", "A Jones");
pathParams.add("companyid", COMPANY_NAME);
try {
InteractionCommand.Result result = command.execute(ctx, entity1SolrServer);
assertEquals(Result.SUCCESS, result);
} catch (InteractionException e) {
fail("InteractionException : " + e.getHttpStatus().toString() + " - " + e.getMessage());
}
CollectionResource<Entity> cr = (CollectionResource<Entity>) ctx.getResource();
assertEquals(1, cr.getEntities().size());
}
/**
* Test select for duplicate entities. Search on all fields.
*/
@SuppressWarnings("unchecked")
@Test
public void testDuplicateEntity1SelectAllFields() {
when(currentState.getEntityName()).thenReturn(ENTITY1_TYPE);
queryParams.add("q", "Ima Twin");
pathParams.add("companyid", COMPANY_NAME);
try {
InteractionCommand.Result result = command.execute(ctx, entity1SolrServer);
assertEquals(Result.SUCCESS, result);
} catch (InteractionException e) {
fail("InteractionException : " + e.getHttpStatus().toString() + " - " + e.getMessage());
}
CollectionResource<Entity> cr = (CollectionResource<Entity>) ctx.getResource();
assertEquals(2, cr.getEntities().size());
}
/**
* Test select for single Entity1 search on a named field.
*/
@SuppressWarnings("unchecked")
@Test
public void testEntity1SelectByName() {
when(currentState.getEntityName()).thenReturn(ENTITY1_TYPE);
queryParams.add("q", "name:Alan Jones");
pathParams.add("companyid", COMPANY_NAME);
try {
InteractionCommand.Result result = command.execute(ctx, entity1SolrServer);
assertEquals(Result.SUCCESS, result);
} catch (InteractionException e) {
fail("InteractionException : " + e.getHttpStatus().toString() + " - " + e.getMessage());
}
CollectionResource<Entity> cr = (CollectionResource<Entity>) ctx.getResource();
assertEquals(1, cr.getEntities().size());
}
/**
* Test select for single Entity2 search on a named field.
*/
@SuppressWarnings("unchecked")
@Test
public void testEntity2SelectByName() {
when(currentState.getEntityName()).thenReturn(ENTITY2_TYPE);
queryParams.add("q", "name:Alan Jones");
pathParams.add("companyid", COMPANY_NAME);
try {
InteractionCommand.Result result = command.execute(ctx, entity2SolrServer);
assertEquals(Result.SUCCESS, result);
} catch (InteractionException e) {
fail("InteractionException : " + e.getHttpStatus().toString() + " - " + e.getMessage());
}
CollectionResource<Entity> cr = (CollectionResource<Entity>) ctx.getResource();
assertEquals(1, cr.getEntities().size());
}
/**
* Test select for partial match with wildcard at start. Should work.
*/
@SuppressWarnings("unchecked")
@Test
public void testWildcardStart() {
when(currentState.getEntityName()).thenReturn(ENTITY2_TYPE);
// Add part of "Alan Jones"
queryParams.add("q", "name:*lan Jones");
pathParams.add("companyid", COMPANY_NAME);
try {
InteractionCommand.Result result = command.execute(ctx, entity2SolrServer);
assertEquals(Result.SUCCESS, result);
} catch (InteractionException e) {
fail("InteractionException : " + e.getHttpStatus().toString() + " - " + e.getMessage());
}
CollectionResource<Entity> cr = (CollectionResource<Entity>) ctx.getResource();
assertEquals(1, cr.getEntities().size());
}
/**
* Test select for partial match with wildcard at end. Should work.
*/
@SuppressWarnings("unchecked")
@Test
public void testWildcardEnd() {
when(currentState.getEntityName()).thenReturn(ENTITY1_TYPE);
// Add part of "Alan Jones"
queryParams.add("q", "name:A Jon*");
pathParams.add("companyid", COMPANY_NAME);
try {
InteractionCommand.Result result = command.execute(ctx, entity1SolrServer);
assertEquals(Result.SUCCESS, result);
} catch (InteractionException e) {
fail("InteractionException : " + e.getHttpStatus().toString() + " - " + e.getMessage());
}
CollectionResource<Entity> cr = (CollectionResource<Entity>) ctx.getResource();
assertEquals(1, cr.getEntities().size());
}
/**
* Test select for partial match with wildcard at start and end . Should
* work.
*/
@SuppressWarnings("unchecked")
@Test
public void testWildcardBoth() {
when(currentState.getEntityName()).thenReturn(ENTITY1_TYPE);
// Add part of "Alan Jones"
queryParams.add("q", "name:*lan Jon*");
pathParams.add("companyid", COMPANY_NAME);
try {
InteractionCommand.Result result = command.execute(ctx, entity1SolrServer);
assertEquals(Result.SUCCESS, result);
} catch (InteractionException e) {
fail("InteractionException : " + e.getHttpStatus().toString() + " - " + e.getMessage());
}
CollectionResource<Entity> cr = (CollectionResource<Entity>) ctx.getResource();
assertEquals(1, cr.getEntities().size());
}
/**
* Test select for partial match with no wildcard. Some wildcarding is done
* automatically so use quotes to force an exact match. Should not work.
*/
@SuppressWarnings("unchecked")
@Test
public void testWildcardNeither() {
when(currentState.getEntityName()).thenReturn(ENTITY1_TYPE);
// Add part of "Alan Jones"
queryParams.add("q", "name:\"lan Jones\"");
pathParams.add("companyid", COMPANY_NAME);
try {
InteractionCommand.Result result = command.execute(ctx, entity1SolrServer);
assertEquals(Result.SUCCESS, result);
} catch (InteractionException e) {
fail("InteractionException : " + e.getHttpStatus().toString() + " - " + e.getMessage());
}
CollectionResource<Entity> cr = (CollectionResource<Entity>) ctx.getResource();
assertEquals(0, cr.getEntities().size());
}
/**
* Test that a specific core can be selected.
*/
@Test
public void testSpecificCoreName() {
when(currentState.getEntityName()).thenReturn(ENTITY1_TYPE);
queryParams.add("q", "John");
pathParams.add("companyid", COMPANY_NAME);
// Specify a different core for the query
queryParams.add("core", ENTITY2_TYPE);
try {
InteractionCommand.Result result = command.execute(ctx, entity2SolrServer);
assertEquals(Result.SUCCESS, result);
} catch (InteractionException e) {
fail("InteractionException : " + e.getHttpStatus().toString() + " - " + e.getMessage());
}
CollectionResource<Entity> cr = (CollectionResource<Entity>) ctx.getResource();
assertEquals(1, cr.getEntities().size());
}
/**
* May be called with 'q=' missing ... filtering on '$filter='
*/
@Test
public void testMissingQuery() {
when(currentState.getEntityName()).thenReturn(ENTITY1_TYPE);
pathParams.add("companyid", COMPANY_NAME);
try {
InteractionCommand.Result result = command.execute(ctx, entity1SolrServer);
assertEquals("Did failed on mission 'q='", Result.SUCCESS, result);
} catch (InteractionException e) {
fail("InteractionException : " + e.getHttpStatus().toString() + " - " + e.getMessage());
}
}
/**
* Test fails on unknown field name.
*/
@Test
public void testFailsOnBadFieldName() {
when(currentState.getEntityName()).thenReturn(ENTITY1_TYPE);
queryParams.add("q", "rubbish:A Jones");
pathParams.add("companyid", COMPANY_NAME);
try {
InteractionCommand.Result result = command.execute(ctx, entity1SolrServer);
assertEquals("Did not fail on bad fieldname", Result.FAILURE, result);
} catch (InteractionException e) {
fail("InteractionException : " + e.getHttpStatus().toString() + " - " + e.getMessage());
}
}
/**
* Test fails if company not present
*/
@Ignore("Invalid test, it should not be here as its T24 specific test")
@Test
public void testFailsOnMissingComapny() {
when(currentState.getEntityName()).thenReturn(ENTITY1_TYPE);
queryParams.add("q", "A Jones");
try {
InteractionCommand.Result result = command.execute(ctx, entity1SolrServer);
assertEquals("Did not fail on missing company", Result.FAILURE, result);
} catch (InteractionException e) {
fail("InteractionException : " + e.getHttpStatus().toString() + " - " + e.getMessage());
}
}
/**
* Check that select still works if there are additional (ignored)
* parameters.
*/
@SuppressWarnings("unchecked")
@Test
public void testWorksWithAdditionalParams() {
when(currentState.getEntityName()).thenReturn(ENTITY1_TYPE);
queryParams.add("q", "A Jones");
pathParams.add("companyid", COMPANY_NAME);
// Extra params
queryParams.add("filter", "rubbish");
queryParams.add("rubbish", "rubbish");
try {
InteractionCommand.Result result = command.execute(ctx, entity1SolrServer);
assertEquals(Result.SUCCESS, result);
} catch (InteractionException e) {
fail("InteractionException : " + e.getHttpStatus().toString() + " - " + e.getMessage());
}
CollectionResource<Entity> cr = (CollectionResource<Entity>) ctx.getResource();
assertEquals(1, cr.getEntities().size());
}
/**
* Test for a large number of entries
*/
@SuppressWarnings("unchecked")
@Test
public void testEntity1SelectLarge() {
try {
// Add twice as many test entities as the max number returned.
addEntity1TestData(SolrSearchCommand.MAX_ENTITIES_RETURNED * 2);
} catch (Exception e) {
fail("Adding extra entities threw. " + e);
}
when(currentState.getEntityName()).thenReturn(ENTITY1_TYPE);
// Get all the entries
queryParams.add("q", "*");
pathParams.add("companyid", COMPANY_NAME);
try {
InteractionCommand.Result result = command.execute(ctx, entity1SolrServer);
assertEquals(Result.SUCCESS, result);
} catch (InteractionException e) {
fail("InteractionException : " + e.getHttpStatus().toString() + " - " + e.getMessage());
}
CollectionResource<Entity> cr = (CollectionResource<Entity>) ctx.getResource();
assertEquals(SolrSearchCommand.MAX_ENTITIES_RETURNED, cr.getEntities().size());
}
@Ignore ("This test is currently ignored because distributed search is not supported by Embedded Solr. See https://issues.apache.org/jira/browse/SOLR-1858 for more details")
@Test
public void testSolrSharding() {
when(currentState.getEntityName()).thenReturn(SHARD_ENTITY);
// Search for a string Jillian, according to the test data we should get data from both cores
queryParams.add(SolrConstants.SOLR_QUERY_KEY, "Jillian");
pathParams.add(SolrConstants.SOLR_COMPANY_NAME_KEY, COMPANY_NAME);
// Add the core and shards param
queryParams.add(SolrConstants.SOLR_CORE_KEY, SOLR_CORE_FOR_SHARD);
queryParams.add(SolrConstants.SOLR_SHARDS_KEY, ENTITY1_TYPE + "," + ENTITY2_TYPE);
queryParams.add(SolrConstants.SOLR_SHARDS_TOLERANT_KEY, "true");
// Extra params
queryParams.add("filter", "rubbish");
queryParams.add("rubbish", "rubbish");
try {
InteractionCommand.Result result = command.execute(ctx, shardCoreSolrServer);
assertEquals(Result.SUCCESS, result);
} catch (InteractionException e) {
fail("InteractionException : " + e.getHttpStatus().toString() + " - " + e.getMessage());
}
CollectionResource<Entity> cr = (CollectionResource<Entity>) ctx.getResource();
assertEquals(2, cr.getEntities().size());
}
}