/*
* 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.distributed;
import static org.apache.geode.cache.lucene.test.LuceneTestUtilities.DEFAULT_FIELD;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.execute.FunctionException;
import org.apache.geode.cache.execute.ResultSender;
import org.apache.geode.cache.lucene.LuceneQueryException;
import org.apache.geode.cache.lucene.LuceneQueryFactory;
import org.apache.geode.cache.lucene.LuceneQueryProvider;
import org.apache.geode.cache.lucene.internal.InternalLuceneService;
import org.apache.geode.cache.lucene.internal.LuceneIndexImpl;
import org.apache.geode.cache.lucene.internal.LuceneIndexStats;
import org.apache.geode.cache.lucene.internal.StringQueryProvider;
import org.apache.geode.cache.lucene.internal.repository.IndexRepository;
import org.apache.geode.cache.lucene.internal.repository.IndexResultCollector;
import org.apache.geode.cache.lucene.internal.repository.RepositoryManager;
import org.apache.geode.internal.cache.BucketNotFoundException;
import org.apache.geode.internal.cache.InternalCache;
import org.apache.geode.internal.cache.execute.InternalRegionFunctionContext;
import org.apache.geode.test.junit.categories.UnitTest;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.search.Query;
import org.junit.Before;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
@Category(UnitTest.class)
public class LuceneFunctionJUnitTest {
String regionPath = "/region";
String indexName = "index";
final EntryScore<String> r1_1 = new EntryScore<String>("key-1-1", .5f);
final EntryScore<String> r1_2 = new EntryScore<String>("key-1-2", .4f);
final EntryScore<String> r1_3 = new EntryScore<String>("key-1-3", .3f);
final EntryScore<String> r2_1 = new EntryScore<String>("key-2-1", .45f);
final EntryScore<String> r2_2 = new EntryScore<String>("key-2-2", .35f);
InternalRegionFunctionContext mockContext;
ResultSender<TopEntriesCollector> mockResultSender;
Region<Object, Object> mockRegion;
RepositoryManager mockRepoManager;
IndexRepository mockRepository1;
IndexRepository mockRepository2;
IndexResultCollector mockCollector;
InternalLuceneService mockService;
LuceneIndexImpl mockIndex;
LuceneIndexStats mockStats;
ArrayList<IndexRepository> repos;
LuceneFunctionContext<IndexResultCollector> searchArgs;
LuceneQueryProvider queryProvider;
Query query;
private InternalCache mockCache;
@Test
public void testRepoQueryAndMerge() throws Exception {
when(mockContext.getDataSet()).thenReturn(mockRegion);
when(mockContext.getArguments()).thenReturn(searchArgs);
when(mockContext.<TopEntriesCollector>getResultSender()).thenReturn(mockResultSender);
when(mockRepoManager.getRepositories(eq(mockContext))).thenReturn(repos);
doAnswer(invocation -> {
IndexResultCollector collector = invocation.getArgumentAt(2, IndexResultCollector.class);
collector.collect(r1_1.getKey(), r1_1.getScore());
collector.collect(r1_2.getKey(), r1_2.getScore());
collector.collect(r1_3.getKey(), r1_3.getScore());
return null;
}).when(mockRepository1).query(eq(query), eq(LuceneQueryFactory.DEFAULT_LIMIT),
any(IndexResultCollector.class));
doAnswer(invocation -> {
IndexResultCollector collector = invocation.getArgumentAt(2, IndexResultCollector.class);
collector.collect(r2_1.getKey(), r2_1.getScore());
collector.collect(r2_2.getKey(), r2_2.getScore());
return null;
}).when(mockRepository2).query(eq(query), eq(LuceneQueryFactory.DEFAULT_LIMIT),
any(IndexResultCollector.class));
LuceneFunction function = new LuceneFunction();
function.execute(mockContext);
ArgumentCaptor<TopEntriesCollector> resultCaptor =
ArgumentCaptor.forClass(TopEntriesCollector.class);
verify(mockResultSender).lastResult(resultCaptor.capture());
TopEntriesCollector result = resultCaptor.getValue();
List<EntryScore> hits = result.getEntries().getHits();
assertEquals(5, hits.size());
TopEntriesJUnitTest.verifyResultOrder(result.getEntries().getHits(), r1_1, r2_1, r1_2, r2_2,
r1_3);
}
@Test
public void testResultLimitClause() throws Exception {
searchArgs =
new LuceneFunctionContext<IndexResultCollector>(queryProvider, "indexName", null, 3);
when(mockContext.getDataSet()).thenReturn(mockRegion);
when(mockContext.getArguments()).thenReturn(searchArgs);
when(mockContext.<TopEntriesCollector>getResultSender()).thenReturn(mockResultSender);
when(mockRepoManager.getRepositories(eq(mockContext))).thenReturn(repos);
doAnswer(invocation -> {
IndexResultCollector collector = invocation.getArgumentAt(2, IndexResultCollector.class);
collector.collect(r1_1.getKey(), r1_1.getScore());
collector.collect(r1_2.getKey(), r1_2.getScore());
collector.collect(r1_3.getKey(), r1_3.getScore());
return null;
}).when(mockRepository1).query(eq(query), eq(3), any(IndexResultCollector.class));
doAnswer(invocation -> {
IndexResultCollector collector = invocation.getArgumentAt(2, IndexResultCollector.class);
collector.collect(r2_1.getKey(), r2_1.getScore());
collector.collect(r2_2.getKey(), r2_2.getScore());
return null;
}).when(mockRepository2).query(eq(query), eq(3), any(IndexResultCollector.class));
LuceneFunction function = new LuceneFunction();
function.execute(mockContext);
ArgumentCaptor<TopEntriesCollector> resultCaptor =
ArgumentCaptor.forClass(TopEntriesCollector.class);
verify(mockResultSender).lastResult(resultCaptor.capture());
TopEntriesCollector result = resultCaptor.getValue();
List<EntryScore> hits = result.getEntries().getHits();
assertEquals(3, hits.size());
TopEntriesJUnitTest.verifyResultOrder(result.getEntries().getHits(), r1_1, r2_1, r1_2);
}
@Test
public void injectCustomCollectorManager() throws Exception {
final CollectorManager mockManager = mock(CollectorManager.class);
searchArgs =
new LuceneFunctionContext<IndexResultCollector>(queryProvider, "indexName", mockManager);
when(mockContext.getDataSet()).thenReturn(mockRegion);
when(mockContext.getArguments()).thenReturn(searchArgs);
when(mockContext.<TopEntriesCollector>getResultSender()).thenReturn(mockResultSender);
repos.remove(0);
when(mockRepoManager.getRepositories(eq(mockContext))).thenReturn(repos);
when(mockManager.newCollector(eq("repo2"))).thenReturn(mockCollector);
when(mockManager.reduce(any(Collection.class))).thenAnswer(invocation -> {
Collection<IndexResultCollector> collectors = invocation.getArgumentAt(0, Collection.class);
assertEquals(1, collectors.size());
assertEquals(mockCollector, collectors.iterator().next());
return new TopEntriesCollector(null);
});
doAnswer(invocation -> {
IndexResultCollector collector = invocation.getArgumentAt(2, IndexResultCollector.class);
collector.collect(r2_1.getKey(), r2_1.getScore());
return null;
}).when(mockRepository2).query(eq(query), eq(LuceneQueryFactory.DEFAULT_LIMIT),
any(IndexResultCollector.class));
LuceneFunction function = new LuceneFunction();
function.execute(mockContext);
verify(mockCollector).collect(eq("key-2-1"), eq(.45f));
verify(mockResultSender).lastResult(any(TopEntriesCollector.class));
}
@Test(expected = FunctionException.class)
public void testIndexRepoQueryFails() throws Exception {
when(mockContext.getDataSet()).thenReturn(mockRegion);
when(mockContext.getArguments()).thenReturn(searchArgs);
when(mockContext.<TopEntriesCollector>getResultSender()).thenReturn(mockResultSender);
when(mockRepoManager.getRepositories(eq(mockContext))).thenReturn(repos);
doThrow(IOException.class).when(mockRepository1).query(eq(query),
eq(LuceneQueryFactory.DEFAULT_LIMIT), any(IndexResultCollector.class));
LuceneFunction function = new LuceneFunction();
function.execute(mockContext);
}
@Test(expected = FunctionException.class)
public void testBucketNotFound() throws Exception {
when(mockContext.getDataSet()).thenReturn(mockRegion);
when(mockContext.getArguments()).thenReturn(searchArgs);
when(mockContext.<TopEntriesCollector>getResultSender()).thenReturn(mockResultSender);
when(mockRepoManager.getRepositories(eq(mockContext)))
.thenThrow(new BucketNotFoundException(""));
LuceneFunction function = new LuceneFunction();
function.execute(mockContext);
verify(mockResultSender).sendException(any(BucketNotFoundException.class));
}
@Test(expected = FunctionException.class)
public void testReduceError() throws Exception {
final CollectorManager mockManager = mock(CollectorManager.class);
searchArgs =
new LuceneFunctionContext<IndexResultCollector>(queryProvider, "indexName", mockManager);
when(mockContext.getDataSet()).thenReturn(mockRegion);
when(mockContext.getArguments()).thenReturn(searchArgs);
when(mockContext.<TopEntriesCollector>getResultSender()).thenReturn(mockResultSender);
repos.remove(1);
when(mockRepoManager.getRepositories(eq(mockContext))).thenReturn(repos);
when(mockManager.newCollector(eq("repo1"))).thenReturn(mockCollector);
when(mockManager.reduce(any(Collection.class))).thenThrow(IOException.class);
LuceneFunction function = new LuceneFunction();
function.execute(mockContext);
}
@Test(expected = FunctionException.class)
public void queryProviderErrorIsHandled() throws Exception {
queryProvider = mock(LuceneQueryProvider.class);
searchArgs = new LuceneFunctionContext<IndexResultCollector>(queryProvider, "indexName");
when(mockContext.getDataSet()).thenReturn(mockRegion);
when(mockContext.getArguments()).thenReturn(searchArgs);
when(mockContext.<TopEntriesCollector>getResultSender()).thenReturn(mockResultSender);
when(queryProvider.getQuery(eq(mockIndex))).thenThrow(LuceneQueryException.class);
LuceneFunction function = new LuceneFunction();
function.execute(mockContext);
}
@Test
public void testQueryFunctionId() {
String id = new LuceneFunction().getId();
assertEquals(LuceneFunction.class.getName(), id);
}
@Before
public void createMocksAndCommonObjects() throws Exception {
mockContext = mock(InternalRegionFunctionContext.class);
mockResultSender = mock(ResultSender.class);
mockRegion = mock(Region.class);
mockRepoManager = mock(RepositoryManager.class);
mockRepository1 = mock(IndexRepository.class, "repo1");
mockRepository2 = mock(IndexRepository.class, "repo2");
mockCollector = mock(IndexResultCollector.class);
mockStats = mock(LuceneIndexStats.class);
repos = new ArrayList<IndexRepository>();
repos.add(mockRepository1);
repos.add(mockRepository2);
mockIndex = mock(LuceneIndexImpl.class);
mockService = mock(InternalLuceneService.class);
mockCache = mock(InternalCache.class);
Analyzer analyzer = new StandardAnalyzer();
Mockito.doReturn(analyzer).when(mockIndex).getAnalyzer();
queryProvider = new StringQueryProvider("gemfire:lucene", DEFAULT_FIELD);
searchArgs = new LuceneFunctionContext<IndexResultCollector>(queryProvider, "indexName");
when(mockRegion.getCache()).thenReturn(mockCache);
when(mockRegion.getFullPath()).thenReturn(regionPath);
when(mockCache.getService(any())).thenReturn(mockService);
when(mockService.getIndex(eq("indexName"), eq(regionPath))).thenReturn(mockIndex);
when(mockIndex.getRepositoryManager()).thenReturn(mockRepoManager);
when(mockIndex.getFieldNames()).thenReturn(new String[] {"gemfire"});
when(mockIndex.getIndexStats()).thenReturn(mockStats);
query = queryProvider.getQuery(mockIndex);
}
}