/**
* Copyright 2008 The University of North Carolina at Chapel Hill
*
* Licensed 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 edu.unc.lib.dl.data.ingest.solr.action;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.isNull;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.MockitoAnnotations.initMocks;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import edu.unc.lib.dl.data.ingest.solr.SolrUpdateRequest;
import edu.unc.lib.dl.data.ingest.solr.exception.IndexingException;
import edu.unc.lib.dl.data.ingest.solr.indexing.DocumentIndexingPackage;
import edu.unc.lib.dl.data.ingest.solr.indexing.DocumentIndexingPipeline;
import edu.unc.lib.dl.data.ingest.solr.indexing.SolrUpdateDriver;
import edu.unc.lib.dl.fedora.PID;
import edu.unc.lib.dl.search.solr.model.IndexDocumentBean;
public class RecursiveTreeIndexerTest extends Assert {
private RecursiveTreeIndexer indexer;
@Mock
private SolrUpdateDriver driver;
@Mock
private UpdateTreeAction action;
@Mock
private DocumentIndexingPipeline pipeline;
@Mock
private DocumentIndexingPackage parentDip;
@Mock
private SolrUpdateRequest request;
@Before
public void setup() {
initMocks(this);
when(action.getSolrUpdateDriver()).thenReturn(driver);
when(action.getPipeline()).thenReturn(pipeline);
indexer = new RecursiveTreeIndexer(request, action);
}
@Test
public void indexNoDip() throws Exception {
when(action.getDocumentIndexingPackage(any(PID.class), any(DocumentIndexingPackage.class))).thenReturn(null);
indexer.index(new PID("pid"), parentDip);
verify(pipeline, never()).process(any(DocumentIndexingPackage.class));
verify(driver, never()).addDocument(any(IndexDocumentBean.class));
verify(request, never()).incrementChildrenProcessed();
}
@Test
public void indexGetDipException() throws Exception {
when(action.getDocumentIndexingPackage(any(PID.class), any(DocumentIndexingPackage.class)))
.thenThrow(new IndexingException(""));
indexer.index(new PID("pid"), parentDip);
verify(pipeline, never()).process(any(DocumentIndexingPackage.class));
verify(driver, never()).addDocument(any(IndexDocumentBean.class));
verify(request, never()).incrementChildrenProcessed();
}
@Test
public void indexNoChildren() throws Exception {
DocumentIndexingPackage dip = mock(DocumentIndexingPackage.class);
when(dip.getChildren()).thenReturn(null);
when(action.getDocumentIndexingPackage(any(PID.class), any(DocumentIndexingPackage.class))).thenReturn(dip);
indexer.index(new PID("pid"), parentDip);
verify(pipeline).process(eq(dip));
verify(driver).addDocument(any(IndexDocumentBean.class));
verify(request).incrementChildrenProcessed();
}
@Test
public void indexUpdate() throws Exception {
indexer = new RecursiveTreeIndexer(request, action, false);
DocumentIndexingPackage dip = mock(DocumentIndexingPackage.class);
when(action.getDocumentIndexingPackage(any(PID.class), any(DocumentIndexingPackage.class))).thenReturn(dip);
indexer.index(new PID("pid"), parentDip);
verify(pipeline).process(eq(dip));
verify(driver, never()).addDocument(any(IndexDocumentBean.class));
verify(driver).updateDocument(eq("set"), any(IndexDocumentBean.class));
verify(request).incrementChildrenProcessed();
}
@Test
public void indexVerifyOrder() throws Exception {
final Map<String, DocumentIndexingPackage> tree = new HashMap<String, DocumentIndexingPackage>();
DocumentIndexingPackage dipRoot = mock(DocumentIndexingPackage.class);
tree.put("c0", dipRoot);
List<PID> children1 = Arrays.asList(new PID("c1"), new PID("c2"));
when(dipRoot.getChildren()).thenReturn(children1);
DocumentIndexingPackage dipC1 = mock(DocumentIndexingPackage.class);
tree.put("c1", dipC1);
List<PID> children2 = Arrays.asList(new PID("c3"));
when(dipC1.getChildren()).thenReturn(children2);
tree.put("c2", mock(DocumentIndexingPackage.class));
tree.put("c3", mock(DocumentIndexingPackage.class));
ArgumentCaptor<PID> pidArgs = ArgumentCaptor.forClass(PID.class);
when(action.getDocumentIndexingPackage(any(PID.class), any(DocumentIndexingPackage.class))).thenAnswer(
new Answer<DocumentIndexingPackage>() {
@Override
public DocumentIndexingPackage answer(InvocationOnMock invocation) throws Throwable {
Object[] args = invocation.getArguments();
PID pid = (PID) args[0];
return tree.get(pid.getPid());
}
});
indexer.index(new PID("c0"), parentDip);
verify(action, times(4)).getDocumentIndexingPackage(pidArgs.capture(), any(DocumentIndexingPackage.class));
verify(pipeline, times(4)).process(any(DocumentIndexingPackage.class));
verify(driver, times(4)).addDocument(any(IndexDocumentBean.class));
verify(request, times(4)).incrementChildrenProcessed();
List<PID> pidOrder = pidArgs.getAllValues();
assertEquals("Incorrect number of pids used to retrieved indexing packages", 4, pidOrder.size());
assertEquals("Processing order incorrect", "c0", pidOrder.get(0).getPid());
assertEquals("Processing order incorrect", "c1", pidOrder.get(1).getPid());
assertEquals("Processing order incorrect", "c3", pidOrder.get(2).getPid());
assertEquals("Processing order incorrect", "c2", pidOrder.get(3).getPid());
}
@Test
public void testIndexingExceptionThrown() throws Exception {
DocumentIndexingPackage dipRoot = mock(DocumentIndexingPackage.class);
List<PID> children1 = Arrays.asList(new PID("c1"), new PID("c2"), new PID("c3"));
when(dipRoot.getChildren()).thenReturn(children1);
DocumentIndexingPackage dipC1 = mock(DocumentIndexingPackage.class);
DocumentIndexingPackage dipC2 = mock(DocumentIndexingPackage.class);
List<PID> children2 = Arrays.asList(new PID("c4"));
when(dipC2.getChildren()).thenReturn(children2);
DocumentIndexingPackage dipC3 = mock(DocumentIndexingPackage.class);
DocumentIndexingPackage dipC4 = mock(DocumentIndexingPackage.class);
when(action.getDocumentIndexingPackage(any(PID.class), any(DocumentIndexingPackage.class)))
.thenReturn(dipRoot, dipC1, dipC2, dipC4, dipC3);
doThrow(new IndexingException("")).when(pipeline).process(eq(dipC2));
indexer.index(new PID("c0"), parentDip);
// All objects, including the children of c2 should have been retrieved
verify(action, times(5)).getDocumentIndexingPackage(any(PID.class),
any(DocumentIndexingPackage.class));
verify(pipeline, times(5)).process(any(DocumentIndexingPackage.class));
// All objects except c2 should have been added to driver
verify(driver, times(4)).addDocument(any(IndexDocumentBean.class));
// Count should only be increment for objects that successfully indexed
verify(request, times(4)).incrementChildrenProcessed();
verify(dipC3).setParentDocument(isNull(DocumentIndexingPackage.class));
// Ensure that parent reference cleared on object that throws exception
verify(dipC2).setParentDocument(isNull(DocumentIndexingPackage.class));
}
/**
* This test seems a bit contrived, but it is to ensure that cleanup happens and the children of the object that
* threw the exception are not indexed.
*/
@Test
public void indexUnexpectedException() throws Exception {
when(action.getSolrUpdateDriver()).thenReturn(driver, (SolrUpdateDriver) null, driver);
DocumentIndexingPackage dipRoot = mock(DocumentIndexingPackage.class);
List<PID> children1 = Arrays.asList(new PID("c1"), new PID("c2"));
when(dipRoot.getChildren()).thenReturn(children1);
DocumentIndexingPackage dipC1 = mock(DocumentIndexingPackage.class);
IndexDocumentBean idbC1 = mock(IndexDocumentBean.class);
when(dipC1.getDocument()).thenReturn(idbC1);
List<PID> children2 = Arrays.asList(new PID("c3"));
when(dipC1.getChildren()).thenReturn(children2);
DocumentIndexingPackage dipC2 = mock(DocumentIndexingPackage.class);
when(action.getDocumentIndexingPackage(any(PID.class), any(DocumentIndexingPackage.class)))
.thenReturn(dipRoot, dipC1, dipC2);
try {
indexer.index(new PID("c0"), parentDip);
} finally {
// Everything except the children of c1 and its children should have been indexed
verify(action, times(3)).getDocumentIndexingPackage(any(PID.class), any(DocumentIndexingPackage.class));
verify(driver, times(2)).addDocument(any(IndexDocumentBean.class));
verify(driver, never()).addDocument(eq(idbC1));
verify(dipC1).setParentDocument(isNull(DocumentIndexingPackage.class));
}
}
}