/*
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license
* agreements. See the NOTICE file distributed with this work for additional information regarding
* copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License. You may obtain a
* copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package org.apache.geode.cache.lucene.internal.cli;
import static org.junit.Assert.*;
import static org.mockito.Matchers.isA;
import static org.mockito.Mockito.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.core.KeywordAnalyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.junit.Assert;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.mockito.ArgumentCaptor;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.execute.Execution;
import org.apache.geode.cache.execute.ResultCollector;
import org.apache.geode.cache.lucene.internal.LuceneIndexStats;
import org.apache.geode.cache.lucene.internal.cli.functions.LuceneCreateIndexFunction;
import org.apache.geode.cache.lucene.internal.cli.functions.LuceneDescribeIndexFunction;
import org.apache.geode.cache.lucene.internal.cli.functions.LuceneListIndexFunction;
import org.apache.geode.distributed.DistributedMember;
import org.apache.geode.internal.cache.execute.AbstractExecution;
import org.apache.geode.internal.util.CollectionUtils;
import org.apache.geode.management.cli.Result.Status;
import org.apache.geode.management.internal.cli.functions.CliFunctionResult;
import org.apache.geode.management.internal.cli.result.CommandResult;
import org.apache.geode.management.internal.cli.result.ResultBuilder;
import org.apache.geode.management.internal.cli.result.TabularResultData;
import org.apache.geode.management.internal.cli.shell.Gfsh;
import org.apache.geode.test.junit.categories.UnitTest;
/**
* The LuceneIndexCommandsJUnitTest class is a test suite of test cases testing the contract and
* functionality of the LuceneIndexCommands class.
* </p>
*
* @see LuceneIndexCommands
* @see LuceneIndexDetails
* @see org.apache.geode.cache.lucene.internal.cli.functions.LuceneListIndexFunction
* @see org.jmock.Expectations
* @see org.jmock.Mockery
* @see org.jmock.lib.legacy.ClassImposteriser
* @see org.junit.Assert
* @see org.junit.Test
* @since GemFire 7.0
*/
@Category(UnitTest.class)
public class LuceneIndexCommandsJUnitTest {
@Test
public void testListIndexWithoutStats() {
final Cache mockCache = mock(Cache.class, "Cache");
final String serverName = "mockServer";
final AbstractExecution mockFunctionExecutor =
mock(AbstractExecution.class, "Function Executor");
final ResultCollector mockResultCollector = mock(ResultCollector.class, "ResultCollector");
String[] searchableFields = {"field1", "field2", "field3"};
Map<String, Analyzer> fieldAnalyzers = new HashMap<>();
fieldAnalyzers.put("field1", new StandardAnalyzer());
fieldAnalyzers.put("field2", new KeywordAnalyzer());
fieldAnalyzers.put("field3", null);
final LuceneIndexDetails indexDetails1 = createIndexDetails("memberFive", "/Employees",
searchableFields, fieldAnalyzers, true, serverName);
final LuceneIndexDetails indexDetails2 = createIndexDetails("memberSix", "/Employees",
searchableFields, fieldAnalyzers, false, serverName);
final LuceneIndexDetails indexDetails3 = createIndexDetails("memberTen", "/Employees",
searchableFields, fieldAnalyzers, true, serverName);
final List<Set<LuceneIndexDetails>> results = new ArrayList<>();
results.add(CollectionUtils.asSet(indexDetails2, indexDetails1, indexDetails3));
when(mockFunctionExecutor.execute(isA(LuceneListIndexFunction.class)))
.thenReturn(mockResultCollector);
when(mockResultCollector.getResult()).thenReturn(results);
final LuceneIndexCommands commands = createIndexCommands(mockCache, mockFunctionExecutor);
CommandResult result = (CommandResult) commands.listIndex(false);
TabularResultData data = (TabularResultData) result.getResultData();
assertEquals(Arrays.asList("memberFive", "memberSix", "memberTen"),
data.retrieveAllValues("Index Name"));
assertEquals(Arrays.asList("/Employees", "/Employees", "/Employees"),
data.retrieveAllValues("Region Path"));
assertEquals(Arrays.asList("[field1, field2, field3]", "[field1, field2, field3]",
"[field1, field2, field3]"), data.retrieveAllValues("Indexed Fields"));
assertEquals(
Arrays.asList("{field1=StandardAnalyzer, field2=KeywordAnalyzer}",
"{field1=StandardAnalyzer, field2=KeywordAnalyzer}",
"{field1=StandardAnalyzer, field2=KeywordAnalyzer}"),
data.retrieveAllValues("Field Analyzer"));
assertEquals(Arrays.asList("Initialized", "Defined", "Initialized"),
data.retrieveAllValues("Status"));
}
@Test
public void testListIndexWithStats() {
final Cache mockCache = mock(Cache.class, "Cache");
final String serverName = "mockServer";
final AbstractExecution mockFunctionExecutor =
mock(AbstractExecution.class, "Function Executor");
final ResultCollector mockResultCollector = mock(ResultCollector.class, "ResultCollector");
final LuceneIndexStats mockIndexStats1 = getMockIndexStats(1, 10, 5, 1);
final LuceneIndexStats mockIndexStats2 = getMockIndexStats(2, 20, 10, 2);
final LuceneIndexStats mockIndexStats3 = getMockIndexStats(3, 30, 15, 3);
String[] searchableFields = {"field1", "field2", "field3"};
Map<String, Analyzer> fieldAnalyzers = new HashMap<>();
fieldAnalyzers.put("field1", new StandardAnalyzer());
fieldAnalyzers.put("field2", new KeywordAnalyzer());
fieldAnalyzers.put("field3", null);
final LuceneIndexDetails indexDetails1 = createIndexDetails("memberFive", "/Employees",
searchableFields, fieldAnalyzers, mockIndexStats1, true, serverName);
final LuceneIndexDetails indexDetails2 = createIndexDetails("memberSix", "/Employees",
searchableFields, fieldAnalyzers, mockIndexStats2, true, serverName);
final LuceneIndexDetails indexDetails3 = createIndexDetails("memberTen", "/Employees",
searchableFields, fieldAnalyzers, mockIndexStats3, true, serverName);
final List<Set<LuceneIndexDetails>> results = new ArrayList<>();
results.add(CollectionUtils.asSet(indexDetails2, indexDetails1, indexDetails3));
when(mockFunctionExecutor.execute(isA(LuceneListIndexFunction.class)))
.thenReturn(mockResultCollector);
when(mockResultCollector.getResult()).thenReturn(results);
final LuceneIndexCommands commands = createIndexCommands(mockCache, mockFunctionExecutor);
CommandResult result = (CommandResult) commands.listIndex(true);
TabularResultData data = (TabularResultData) result.getResultData();
assertEquals(Arrays.asList("memberFive", "memberSix", "memberTen"),
data.retrieveAllValues("Index Name"));
assertEquals(Arrays.asList("/Employees", "/Employees", "/Employees"),
data.retrieveAllValues("Region Path"));
assertEquals(Arrays.asList("[field1, field2, field3]", "[field1, field2, field3]",
"[field1, field2, field3]"), data.retrieveAllValues("Indexed Fields"));
assertEquals(
Arrays.asList("{field1=StandardAnalyzer, field2=KeywordAnalyzer}",
"{field1=StandardAnalyzer, field2=KeywordAnalyzer}",
"{field1=StandardAnalyzer, field2=KeywordAnalyzer}"),
data.retrieveAllValues("Field Analyzer"));
assertEquals(Arrays.asList("1", "2", "3"), data.retrieveAllValues("Query Executions"));
assertEquals(Arrays.asList("10", "20", "30"), data.retrieveAllValues("Commits"));
assertEquals(Arrays.asList("5", "10", "15"), data.retrieveAllValues("Updates"));
assertEquals(Arrays.asList("1", "2", "3"), data.retrieveAllValues("Documents"));
}
@Test
public void testCreateIndex() throws Exception {
final Cache mockCache = mock(Cache.class);
final ResultCollector mockResultCollector = mock(ResultCollector.class);
final LuceneIndexCommands commands = spy(createIndexCommands(mockCache, null));
final List<CliFunctionResult> cliFunctionResults = new ArrayList<>();
cliFunctionResults.add(new CliFunctionResult("member1", true, "Index Created"));
cliFunctionResults.add(new CliFunctionResult("member2", false, "Index creation failed"));
cliFunctionResults.add(new CliFunctionResult("member3", true, "Index Created"));
doReturn(mockResultCollector).when(commands).executeFunctionOnGroups(
isA(LuceneCreateIndexFunction.class), any(), any(LuceneIndexInfo.class));
doReturn(cliFunctionResults).when(mockResultCollector).getResult();
String indexName = "index";
String regionPath = "regionPath";
String[] searchableFields = {"field1", "field2", "field3"};
String[] fieldAnalyzers = {StandardAnalyzer.class.getCanonicalName(),
KeywordAnalyzer.class.getCanonicalName(), StandardAnalyzer.class.getCanonicalName()};
CommandResult result = (CommandResult) commands.createIndex(indexName, regionPath,
searchableFields, fieldAnalyzers, null);
assertEquals(Status.OK, result.getStatus());
TabularResultData data = (TabularResultData) result.getResultData();
assertEquals(Arrays.asList("member1", "member2", "member3"), data.retrieveAllValues("Member"));
assertEquals(Arrays.asList("Successfully created lucene index", "Failed: Index creation failed",
"Successfully created lucene index"), data.retrieveAllValues("Status"));
}
@Test
public void testDescribeIndex() throws Exception {
final Cache mockCache = mock(Cache.class, "Cache");
final String serverName = "mockServer";
final ResultCollector mockResultCollector = mock(ResultCollector.class, "ResultCollector");
final LuceneIndexCommands commands = spy(createIndexCommands(mockCache, null));
String[] searchableFields = {"field1", "field2", "field3"};
Map<String, Analyzer> fieldAnalyzers = new HashMap<>();
fieldAnalyzers.put("field1", new StandardAnalyzer());
fieldAnalyzers.put("field2", new KeywordAnalyzer());
fieldAnalyzers.put("field3", null);
final LuceneIndexStats mockIndexStats = getMockIndexStats(1, 10, 5, 1);
final List<LuceneIndexDetails> indexDetails = new ArrayList<>();
indexDetails.add(createIndexDetails("memberFive", "/Employees", searchableFields,
fieldAnalyzers, mockIndexStats, true, serverName));
doReturn(mockResultCollector).when(commands).executeFunctionOnGroups(
isA(LuceneDescribeIndexFunction.class), any(), any(LuceneIndexInfo.class));
doReturn(indexDetails).when(mockResultCollector).getResult();
CommandResult result = (CommandResult) commands.describeIndex("memberFive", "/Employees");
TabularResultData data = (TabularResultData) result.getResultData();
assertEquals(Collections.singletonList("memberFive"), data.retrieveAllValues("Index Name"));
assertEquals(Collections.singletonList("/Employees"), data.retrieveAllValues("Region Path"));
assertEquals(Collections.singletonList("[field1, field2, field3]"),
data.retrieveAllValues("Indexed Fields"));
assertEquals(Collections.singletonList("{field1=StandardAnalyzer, field2=KeywordAnalyzer}"),
data.retrieveAllValues("Field Analyzer"));
assertEquals(Collections.singletonList("Initialized"), data.retrieveAllValues("Status"));
assertEquals(Collections.singletonList("1"), data.retrieveAllValues("Query Executions"));
assertEquals(Collections.singletonList("10"), data.retrieveAllValues("Commits"));
assertEquals(Collections.singletonList("5"), data.retrieveAllValues("Updates"));
assertEquals(Collections.singletonList("1"), data.retrieveAllValues("Documents"));
}
@Test
public void testSearchIndex() throws Exception {
final Cache mockCache = mock(Cache.class, "Cache");
final ResultCollector mockResultCollector = mock(ResultCollector.class, "ResultCollector");
final LuceneIndexCommands commands = spy(createIndexCommands(mockCache, null));
final List<Set<LuceneSearchResults>> queryResultsList = new ArrayList<>();
HashSet<LuceneSearchResults> queryResults = new HashSet<>();
queryResults.add(createQueryResults("A", "Result1", Float.valueOf("1.3")));
queryResults.add(createQueryResults("B", "Result1", Float.valueOf("1.2")));
queryResults.add(createQueryResults("C", "Result1", Float.valueOf("1.1")));
queryResultsList.add(queryResults);
doReturn(mockResultCollector).when(commands).executeSearch(isA(LuceneQueryInfo.class));
doReturn(queryResultsList).when(mockResultCollector).getResult();
CommandResult result =
(CommandResult) commands.searchIndex("index", "region", "Result1", "field1", -1, -1, false);
TabularResultData data = (TabularResultData) result.getResultData();
assertEquals(Arrays.asList("C", "B", "A"), data.retrieveAllValues("key"));
assertEquals(Arrays.asList("Result1", "Result1", "Result1"), data.retrieveAllValues("value"));
assertEquals(Arrays.asList("1.1", "1.2", "1.3"), data.retrieveAllValues("score"));
}
@Test
public void testSearchIndexWithPaging() throws Exception {
final Cache mockCache = mock(Cache.class, "Cache");
final Gfsh mockGfsh = mock(Gfsh.class);
final ResultCollector mockResultCollector = mock(ResultCollector.class, "ResultCollector");
final LuceneIndexCommands commands = spy(createIndexCommands(mockCache, null));
ArgumentCaptor<String> resultCaptor = ArgumentCaptor.forClass(String.class);
LuceneSearchResults result1 = createQueryResults("A", "Result1", Float.valueOf("1.7"));
LuceneSearchResults result2 = createQueryResults("B", "Result1", Float.valueOf("1.6"));
LuceneSearchResults result3 = createQueryResults("C", "Result1", Float.valueOf("1.5"));
LuceneSearchResults result4 = createQueryResults("D", "Result1", Float.valueOf("1.4"));
LuceneSearchResults result5 = createQueryResults("E", "Result1", Float.valueOf("1.3"));
LuceneSearchResults result6 = createQueryResults("F", "Result1", Float.valueOf("1.2"));
LuceneSearchResults result7 = createQueryResults("G", "Result1", Float.valueOf("1.1"));
final List<Set<LuceneSearchResults>> queryResultsList =
getSearchResults(result1, result2, result3, result4, result5, result6, result7);
doReturn(mockResultCollector).when(commands).executeSearch(any(LuceneQueryInfo.class));
doReturn(queryResultsList).when(mockResultCollector).getResult();
doReturn(mockGfsh).when(commands).initGfsh();
when(mockGfsh.interact(anyString())).thenReturn("n").thenReturn("n").thenReturn("n")
.thenReturn("n").thenReturn("p").thenReturn("p").thenReturn("p").thenReturn("p")
.thenReturn("p").thenReturn("n").thenReturn("q");
LuceneSearchResults[] expectedResults =
new LuceneSearchResults[] {result7, result6, result5, result4, result3, result2, result1};
String expectedPage1 = getPage(expectedResults, new int[] {0, 1});
String expectedPage2 = getPage(expectedResults, new int[] {2, 3});
String expectedPage3 = getPage(expectedResults, new int[] {4, 5});
String expectedPage4 = getPage(expectedResults, new int[] {6});
commands.searchIndex("index", "region", "Result1", "field1", -1, 2, false);
verify(mockGfsh, times(20)).printAsInfo(resultCaptor.capture());
List<String> actualPageResults = resultCaptor.getAllValues();
assertEquals(expectedPage1, actualPageResults.get(0));
assertEquals("\t\tPage 1 of 4", actualPageResults.get(1));
assertEquals(expectedPage2, actualPageResults.get(2));
assertEquals("\t\tPage 2 of 4", actualPageResults.get(3));
assertEquals(expectedPage3, actualPageResults.get(4));
assertEquals("\t\tPage 3 of 4", actualPageResults.get(5));
assertEquals(expectedPage4, actualPageResults.get(6));
assertEquals("\t\tPage 4 of 4", actualPageResults.get(7));
assertEquals("No more results to display.", actualPageResults.get(8));
assertEquals(expectedPage4, actualPageResults.get(9));
assertEquals("\t\tPage 4 of 4", actualPageResults.get(10));
assertEquals(expectedPage3, actualPageResults.get(11));
assertEquals("\t\tPage 3 of 4", actualPageResults.get(12));
assertEquals(expectedPage2, actualPageResults.get(13));
assertEquals("\t\tPage 2 of 4", actualPageResults.get(14));
assertEquals(expectedPage1, actualPageResults.get(15));
assertEquals("\t\tPage 1 of 4", actualPageResults.get(16));
assertEquals("At the top of the search results.", actualPageResults.get(17));
assertEquals(expectedPage1, actualPageResults.get(18));
assertEquals("\t\tPage 1 of 4", actualPageResults.get(19));
}
@Test
public void testSearchIndexWithKeysOnly() throws Exception {
final Cache mockCache = mock(Cache.class, "Cache");
final ResultCollector mockResultCollector = mock(ResultCollector.class, "ResultCollector");
final LuceneIndexCommands commands = spy(createIndexCommands(mockCache, null));
final List<Set<LuceneSearchResults>> queryResultsList = new ArrayList<>();
HashSet<LuceneSearchResults> queryResults = new HashSet<>();
queryResults.add(createQueryResults("A", "Result1", Float.valueOf("1.3")));
queryResults.add(createQueryResults("B", "Result1", Float.valueOf("1.2")));
queryResults.add(createQueryResults("C", "Result1", Float.valueOf("1.1")));
queryResultsList.add(queryResults);
doReturn(mockResultCollector).when(commands).executeSearch(isA(LuceneQueryInfo.class));
doReturn(queryResultsList).when(mockResultCollector).getResult();
CommandResult result =
(CommandResult) commands.searchIndex("index", "region", "Result1", "field1", -1, -1, true);
TabularResultData data = (TabularResultData) result.getResultData();
assertEquals(Arrays.asList("C", "B", "A"), data.retrieveAllValues("key"));
}
@Test
public void testSearchIndexWhenSearchResultsHaveSameScore() throws Exception {
final Cache mockCache = mock(Cache.class, "Cache");
final ResultCollector mockResultCollector = mock(ResultCollector.class, "ResultCollector");
final LuceneIndexCommands commands = spy(createIndexCommands(mockCache, null));
final List<Set<LuceneSearchResults>> queryResultsList = new ArrayList<>();
HashSet<LuceneSearchResults> queryResults = new HashSet<>();
queryResults.add(createQueryResults("A", "Result1", 1));
queryResults.add(createQueryResults("B", "Result1", 1));
queryResults.add(createQueryResults("C", "Result1", 1));
queryResults.add(createQueryResults("D", "Result1", 1));
queryResults.add(createQueryResults("E", "Result1", 1));
queryResults.add(createQueryResults("F", "Result1", 1));
queryResults.add(createQueryResults("G", "Result1", 1));
queryResults.add(createQueryResults("H", "Result1", 1));
queryResults.add(createQueryResults("I", "Result1", 1));
queryResults.add(createQueryResults("J", "Result1", 1));
queryResults.add(createQueryResults("K", "Result1", 1));
queryResults.add(createQueryResults("L", "Result1", 1));
queryResults.add(createQueryResults("M", "Result1", 1));
queryResults.add(createQueryResults("N", "Result1", 1));
queryResults.add(createQueryResults("P", "Result1", 1));
queryResults.add(createQueryResults("Q", "Result1", 1));
queryResults.add(createQueryResults("R", "Result1", 1));
queryResults.add(createQueryResults("S", "Result1", 1));
queryResults.add(createQueryResults("T", "Result1", 1));
queryResultsList.add(queryResults);
doReturn(mockResultCollector).when(commands).executeSearch(isA(LuceneQueryInfo.class));
doReturn(queryResultsList).when(mockResultCollector).getResult();
CommandResult result =
(CommandResult) commands.searchIndex("index", "region", "Result1", "field1", -1, -1, true);
TabularResultData data = (TabularResultData) result.getResultData();
assertEquals(queryResults.size(), data.retrieveAllValues("key").size());
}
private String getPage(final LuceneSearchResults[] expectedResults, int[] indexList) {
final TabularResultData data = ResultBuilder.createTabularResultData();
for (int i : indexList) {
data.accumulate("key", expectedResults[i].getKey());
data.accumulate("value", expectedResults[i].getValue());
data.accumulate("score", expectedResults[i].getScore());
}
CommandResult commandResult = (CommandResult) ResultBuilder.buildResult(data);
StringBuffer buffer = new StringBuffer();
while (commandResult.hasNextLine())
buffer.append(commandResult.nextLine());
return buffer.toString();
}
private List<Set<LuceneSearchResults>> getSearchResults(LuceneSearchResults... results) {
final List<Set<LuceneSearchResults>> queryResultsList = new ArrayList<>();
HashSet<LuceneSearchResults> queryResults = new HashSet<>();
for (LuceneSearchResults result : results)
queryResults.add(result);
queryResultsList.add(queryResults);
return queryResultsList;
}
private LuceneIndexStats getMockIndexStats(int queries, int commits, int updates, int docs) {
LuceneIndexStats mockIndexStats = mock(LuceneIndexStats.class);
when(mockIndexStats.getQueryExecutions()).thenReturn(queries);
when(mockIndexStats.getCommits()).thenReturn(commits);
when(mockIndexStats.getUpdates()).thenReturn(updates);
when(mockIndexStats.getDocuments()).thenReturn(docs);
return mockIndexStats;
}
private LuceneIndexCommands createIndexCommands(final Cache cache,
final Execution functionExecutor) {
return new LuceneTestIndexCommands(cache, functionExecutor);
}
private LuceneIndexDetails createIndexDetails(final String indexName, final String regionPath,
final String[] searchableFields, final Map<String, Analyzer> fieldAnalyzers,
LuceneIndexStats indexStats, boolean status, final String serverName) {
return new LuceneIndexDetails(indexName, regionPath, searchableFields, fieldAnalyzers,
indexStats, status, serverName);
}
private LuceneIndexDetails createIndexDetails(final String indexName, final String regionPath,
final String[] searchableFields, final Map<String, Analyzer> fieldAnalyzers, boolean status,
final String serverName) {
return new LuceneIndexDetails(indexName, regionPath, searchableFields, fieldAnalyzers, null,
status, serverName);
}
private LuceneSearchResults createQueryResults(final String key, final String value,
final float score) {
return new LuceneSearchResults(key, value, score);
}
private static class LuceneTestIndexCommands extends LuceneIndexCommands {
private final Cache cache;
private final Execution functionExecutor;
protected LuceneTestIndexCommands(final Cache cache, final Execution functionExecutor) {
assert cache != null : "The Cache cannot be null!";
this.cache = cache;
this.functionExecutor = functionExecutor;
}
@Override
protected Cache getCache() {
return this.cache;
}
@Override
protected Set<DistributedMember> getMembers(final Cache cache) {
assertSame(getCache(), cache);
return Collections.emptySet();
}
@Override
protected Execution getMembersFunctionExecutor(final Set<DistributedMember> members) {
Assert.assertNotNull(members);
return functionExecutor;
}
}
}