/** * Copyright (c) Codice Foundation * <p/> * This is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser * General Public License as published by the Free Software Foundation, either version 3 of the * License, or any later version. * <p/> * 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 * Lesser General Public License for more details. A copy of the GNU Lesser General Public License * is distributed along with this program and can be found at * <http://www.gnu.org/licenses/lgpl.html>. */ package org.codice.ddf.commands.catalog; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; import static org.junit.Assert.assertThat; import static org.mockito.Matchers.isA; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.io.IOException; import java.io.InputStream; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; import java.util.stream.Stream; import org.apache.commons.io.IOUtils; import org.apache.felix.service.command.CommandSession; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; import ddf.catalog.CatalogFramework; import ddf.catalog.data.Metacard; import ddf.catalog.data.Result; import ddf.catalog.data.impl.MetacardImpl; import ddf.catalog.data.impl.ResultImpl; import ddf.catalog.data.types.Core; import ddf.catalog.federation.FederationException; import ddf.catalog.filter.proxy.builder.GeotoolsFilterBuilder; import ddf.catalog.operation.CreateRequest; import ddf.catalog.operation.CreateResponse; import ddf.catalog.operation.Query; import ddf.catalog.operation.QueryRequest; import ddf.catalog.operation.QueryResponse; import ddf.catalog.source.IngestException; import ddf.catalog.source.SourceUnavailableException; import ddf.catalog.source.UnsupportedQueryException; public class ReplicateCommandTest extends ConsoleOutputCommon { private static final Set<String> SOURCE_IDS = new HashSet<>(Arrays.asList("sourceId1", "sourceId2")); private static final int HITS = 1000; private CatalogFramework catalogFramework; private CreateResponse mockCreateResponse = mock(CreateResponse.class); private CommandSession mockSession = mock(CommandSession.class); private InputStream mockIS = IOUtils.toInputStream("sourceId1"); private ReplicateCommand replicateCommand; @Before public void setUp() throws UnsupportedQueryException, SourceUnavailableException, FederationException, IngestException { catalogFramework = mock(CatalogFramework.class); replicateCommand = new ReplicateCommand() { @Override public String getInput(String message) throws IOException { return "sourceId1"; } }; replicateCommand.catalogFramework = catalogFramework; replicateCommand.filterBuilder = new GeotoolsFilterBuilder(); when(mockSession.getKeyboard()).thenReturn(mockIS); when(catalogFramework.getSourceIds()).thenReturn(SOURCE_IDS); when(catalogFramework.query(isA(QueryRequest.class))).thenAnswer(invocation -> { Object[] args = invocation.getArguments(); QueryRequest request = (QueryRequest) args[0]; QueryResponse mockQueryResponse = mock(QueryResponse.class); when(mockQueryResponse.getHits()).thenReturn(Long.valueOf(HITS)); when(mockQueryResponse.getResults()).thenReturn(getResultList(Math.min(replicateCommand.batchSize, HITS - request.getQuery() .getStartIndex() + 1))); return mockQueryResponse; }); when(catalogFramework.create(isA(CreateRequest.class))).thenAnswer(invocation -> { Object[] args = invocation.getArguments(); CreateRequest request = (CreateRequest) args[0]; when(mockCreateResponse.getCreatedMetacards()).thenReturn(request.getMetacards()); return mockCreateResponse; }); } @Test public void testBadBatchSize() throws Exception { replicateCommand.isUseTemporal = false; replicateCommand.sourceId = "sourceId1"; replicateCommand.temporalProperty = Metacard.EFFECTIVE; replicateCommand.batchSize = -1; replicateCommand.executeWithSubject(); verifyConsoleOutput("Batch Size must be between 1 and 1000."); } @Test public void testInvalidTemporalProperty() { replicateCommand.temporalProperty = "invalidTemporalProperty"; assertThat(replicateCommand.getTemporalProperty(), is(Core.CREATED)); } @Test public void testPrintSourceIds() throws Exception { replicateCommand.isUseTemporal = false; replicateCommand.sourceId = ""; replicateCommand.temporalProperty = Metacard.EFFECTIVE; replicateCommand.executeWithSubject(); verifyConsoleOutput("Please enter the Source ID you would like to replicate:", "sourceId1", "sourceId2"); } @Test public void testDefaultQuery() throws Exception { replicateCommand.isUseTemporal = false; replicateCommand.sourceId = "sourceId1"; replicateCommand.temporalProperty = Metacard.EFFECTIVE; replicateCommand.executeWithSubject(); verifyReplicate(HITS, Metacard.EFFECTIVE); verifyConsoleOutput(HITS + " record(s) replicated; " + 0 + " record(s) failed;"); } @Test public void testSmallBatchSize() throws Exception { replicateCommand.isUseTemporal = false; replicateCommand.sourceId = "sourceId1"; replicateCommand.batchSize = 10; replicateCommand.temporalProperty = Metacard.EFFECTIVE; replicateCommand.executeWithSubject(); verifyReplicate(HITS, Metacard.EFFECTIVE); verifyConsoleOutput(HITS + " record(s) replicated; " + 0 + " record(s) failed;"); } @Test public void testMaxMetacard() throws Exception { replicateCommand.isUseTemporal = false; replicateCommand.sourceId = "sourceId1"; replicateCommand.batchSize = 10; replicateCommand.maxMetacards = 20; replicateCommand.temporalProperty = Metacard.EFFECTIVE; replicateCommand.executeWithSubject(); verifyReplicate(Math.min(HITS, replicateCommand.maxMetacards), Metacard.EFFECTIVE); verifyConsoleOutput( Math.min(HITS, replicateCommand.maxMetacards) + " record(s) replicated; " + 0 + " record(s) failed;"); } @Test public void testMultithreaded() throws Exception { replicateCommand.isUseTemporal = false; replicateCommand.sourceId = "sourceId1"; replicateCommand.batchSize = 10; replicateCommand.multithreaded = 4; replicateCommand.temporalProperty = Metacard.EFFECTIVE; replicateCommand.executeWithSubject(); verifyReplicate(HITS, Metacard.EFFECTIVE); verifyConsoleOutput(HITS + " record(s) replicated; " + 0 + " record(s) failed;"); } @Test public void testTemporalFlag() throws Exception { replicateCommand.isUseTemporal = true; replicateCommand.sourceId = "sourceId1"; replicateCommand.temporalProperty = Core.CREATED; replicateCommand.lastMinutes = 30; replicateCommand.executeWithSubject(); verifyReplicate(HITS, Metacard.EFFECTIVE); verifyConsoleOutput(HITS + " record(s) replicated; " + 0 + " record(s) failed;"); // TODO - How do I validate the actual filter } @Test public void testFailedtoIngestHalf() throws Exception { when(catalogFramework.create(isA(CreateRequest.class))).thenAnswer(invocation -> { Object[] args = invocation.getArguments(); CreateRequest request = (CreateRequest) args[0]; when(mockCreateResponse.getCreatedMetacards()).thenReturn(request.getMetacards() .subList(0, request.getMetacards() .size() / 2)); return mockCreateResponse; }); replicateCommand.isUseTemporal = false; replicateCommand.sourceId = "sourceId1"; replicateCommand.temporalProperty = Metacard.EFFECTIVE; replicateCommand.executeWithSubject(); verifyReplicate(HITS, Metacard.EFFECTIVE); verifyConsoleOutput( (int) Math.floor(HITS / 2) + " record(s) replicated; " + (int) (HITS - Math.floor( HITS / 2)) + " record(s) failed;"); } private List<Result> getResultList(int size) { return Stream.generate(() -> { MetacardImpl metacard = new MetacardImpl(); metacard.setId(UUID.randomUUID() .toString()); return new ResultImpl(metacard); }) .limit(size) .collect(Collectors.toList()); } private void verifyReplicate(int actualMaxMetacards, String sortBy) throws Exception { ArgumentCaptor<QueryRequest> argument = ArgumentCaptor.forClass(QueryRequest.class); verify(catalogFramework, times((int) Math.ceil( (double) actualMaxMetacards / (double) replicateCommand.batchSize))).query( argument.capture()); QueryRequest request = argument.getValue(); assertThat(request, notNullValue()); Query query = request.getQuery(); assertThat(query, notNullValue()); assertThat(query.getPageSize(), is(replicateCommand.batchSize)); if (replicateCommand.multithreaded == 1) { assertThat(query.getStartIndex(), is((((int) ((double) (actualMaxMetacards - 1) / (double) replicateCommand.batchSize)) * replicateCommand.batchSize + 1))); } assertThat(query.getSortBy() .getPropertyName() .getPropertyName(), is(sortBy)); } private void verifyConsoleOutput(String... message) { Arrays.stream(message) .forEach(s -> assertThat(consoleOutput.getOutput(), containsString(s))); } }