/** * 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.hasEntry; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.argThat; import static org.mockito.Mockito.doReturn; 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 static com.google.common.collect.Lists.newArrayList; import static com.google.common.collect.Sets.newHashSet; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.function.Consumer; import java.util.function.Predicate; import org.geotools.filter.text.cql2.CQL; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.ArgumentMatcher; import ddf.catalog.CatalogFramework; import ddf.catalog.data.Metacard; import ddf.catalog.data.Result; import ddf.catalog.data.impl.MetacardImpl; import ddf.catalog.filter.proxy.builder.GeotoolsFilterBuilder; import ddf.catalog.operation.Query; import ddf.catalog.operation.QueryRequest; import ddf.catalog.operation.QueryResponse; import ddf.catalog.operation.ResourceRequest; import ddf.catalog.operation.ResourceResponse; import ddf.catalog.resource.Resource; public class SeedCommandTest extends CommandCatalogFrameworkCommon { private SeedCommand seedCommand; private CatalogFramework catalogFramework; @Before public void setUp() throws Exception { catalogFramework = mock(CatalogFramework.class); doReturn(mockResourceResponse()).when(catalogFramework) .getResource(any(ResourceRequest.class), anyString()); seedCommand = new SeedCommand(); seedCommand.catalogFramework = catalogFramework; seedCommand.filterBuilder = new GeotoolsFilterBuilder(); } @Test public void testBadResourceLimit() throws Exception { seedCommand.resourceLimit = 0; seedCommand.executeWithSubject(); assertThat(consoleOutput.getOutput(), containsString("The limit must be greater than 0.")); } @Test public void testEnterprise() throws Exception { mockQueryResponse(1, new String[0], new boolean[0]); runCommandAndVerifyQueryRequest(request -> assertThat(request.isEnterprise(), is(true))); } @Test public void testSources() throws Exception { final String source1 = "source1"; final String source2 = "source2"; seedCommand.sources = newArrayList(source1, source2); mockQueryResponse(1, new String[0], new boolean[0]); runCommandAndVerifyQueryRequest(request -> { assertThat(request.isEnterprise(), is(false)); assertThat(request.getSourceIds(), is(newHashSet(source1, source2))); }); } @Test public void testCql() throws Exception { final String cql = "modified AFTER 2016-07-21T00:00:00Z"; seedCommand.cqlFilter = cql; mockQueryResponse(1, new String[0], new boolean[0]); runCommandAndVerifyQueryRequest(request -> { Query query = request.getQuery(); assertThat(CQL.toCQL(query), is(cql)); }); } @Test public void testNoCql() throws Exception { mockQueryResponse(1, new String[0], new boolean[0]); runCommandAndVerifyQueryRequest(request -> { Query query = request.getQuery(); assertThat(CQL.toCQL(query), is("anyText ILIKE '*'")); }); } @Test public void testCacheMetacards() throws Exception { mockQueryResponse(1, new String[0], new boolean[0]); runCommandAndVerifyQueryRequest(request -> assertThat(request.getProperties(), hasEntry("mode", "update"))); } private void runCommandAndVerifyQueryRequest(Consumer<QueryRequest> queryRequestAssertions) throws Exception { seedCommand.executeWithSubject(); ArgumentCaptor<QueryRequest> queryCaptor = ArgumentCaptor.forClass(QueryRequest.class); verify(catalogFramework).query(queryCaptor.capture()); QueryRequest request = queryCaptor.getValue(); queryRequestAssertions.accept(request); } @Test public void testResourceLimit() throws Exception { final int limit = 2; seedCommand.resourceLimit = limit; final String id1 = "1"; final String id2 = "2"; mockQueryResponse(limit * 2 + 1, new String[] {id1, id2}, new boolean[] {false, false}); runCommandAndVerifyResourceRequests(limit, resourceRequests -> { assertThat(resourceRequests, hasSize(limit)); verifyResourceRequest(resourceRequests.get(0), id1); verifyResourceRequest(resourceRequests.get(1), id2); }, siteNames -> assertThat(siteNames, is(newArrayList(id1, id2)))); verify(catalogFramework, times(1)).query(any(QueryRequest.class)); } @Test public void testFewerResourcesThanLimit() throws Exception { final int limit = 10; seedCommand.resourceLimit = limit; final String id1 = "1"; final String id2 = "2"; final String id3 = "3"; mockQueryResponse(limit + 1, new String[] {id1, id2, id3}, new boolean[] {false, false, false}); final int expectedResourceRequests = 3; runCommandAndVerifyResourceRequests(expectedResourceRequests, resourceRequests -> { assertThat(resourceRequests, hasSize(expectedResourceRequests)); verifyResourceRequest(resourceRequests.get(0), id1); verifyResourceRequest(resourceRequests.get(1), id2); verifyResourceRequest(resourceRequests.get(2), id3); }, siteNames -> assertThat(siteNames, is(newArrayList(id1, id2, id3)))); verify(catalogFramework, times(2)).query(any(QueryRequest.class)); } @Test public void testDoesNotDownloadCachedResource() throws Exception { final int limit = 3; seedCommand.resourceLimit = limit; final String id1 = "1"; final String id2 = "2"; mockQueryResponse(limit * 2 + 1, new String[] {id1, id2, "3"}, new boolean[] {false, false, true}); final int expectedResourceRequests = 3; runCommandAndVerifyResourceRequests(expectedResourceRequests, resourceRequests -> { assertThat(resourceRequests, hasSize(expectedResourceRequests)); verifyResourceRequest(resourceRequests.get(0), id1); verifyResourceRequest(resourceRequests.get(1), id2); verifyResourceRequest(resourceRequests.get(2), id1); }, siteNames -> assertThat(siteNames, is(newArrayList(id1, id2, id1)))); verify(catalogFramework, times(2)).query(any(QueryRequest.class)); } private void runCommandAndVerifyResourceRequests(int expectedResourceRequests, Consumer<List<ResourceRequest>> requestAssertions, Consumer<List<String>> siteNameAssertions) throws Exception { seedCommand.executeWithSubject(); ArgumentCaptor<ResourceRequest> resourceRequestCaptor = ArgumentCaptor.forClass( ResourceRequest.class); ArgumentCaptor<String> siteNameCaptor = ArgumentCaptor.forClass(String.class); verify(catalogFramework, times(expectedResourceRequests)).getResource(resourceRequestCaptor.capture(), siteNameCaptor.capture()); List<ResourceRequest> resourceRequests = resourceRequestCaptor.getAllValues(); requestAssertions.accept(resourceRequests); List<String> siteNames = siteNameCaptor.getAllValues(); siteNameAssertions.accept(siteNames); } private void mockQueryResponse(int stopReturningResultsAtIndex, String[] ids, boolean[] cached) throws Exception { List<Result> results = new ArrayList<>(); for (int i = 0; i < ids.length; ++i) { String id = ids[i]; Result mockResult = mock(Result.class); MetacardImpl metacard = new MetacardImpl(); metacard.setId(id); metacard.setSourceId(id); metacard.setAttribute("internal.local-resource", cached[i]); when(mockResult.getMetacard()).thenReturn(metacard); results.add(mockResult); } QueryResponse response = mock(QueryResponse.class); when(response.getResults()).thenReturn(results); doReturn(response).when(catalogFramework) .query(argThat(is(queryWithStartIndex(request -> request.getQuery() .getStartIndex() < stopReturningResultsAtIndex)))); QueryResponse noResults = mock(QueryResponse.class); when(noResults.getResults()).thenReturn(Collections.emptyList()); doReturn(noResults).when(catalogFramework) .query(argThat(is(queryWithStartIndex(request -> request.getQuery() .getStartIndex() >= stopReturningResultsAtIndex)))); } private QueryWithStartIndex queryWithStartIndex(Predicate<QueryRequest> test) { return new QueryWithStartIndex(test); } private void verifyResourceRequest(ResourceRequest request, String expectedAttributeValue) { assertThat(request.getAttributeName(), is(Metacard.ID)); assertThat(request.getAttributeValue(), is(expectedAttributeValue)); } private ResourceResponse mockResourceResponse() throws IOException { InputStream mockInputStream = mock(InputStream.class); doReturn(-1).when(mockInputStream) .read(any(byte[].class), anyInt(), anyInt()); Resource mockResource = mock(Resource.class); when(mockResource.getInputStream()).thenReturn(mockInputStream); ResourceResponse mockResponse = mock(ResourceResponse.class); when(mockResponse.getResource()).thenReturn(mockResource); return mockResponse; } private class QueryWithStartIndex extends ArgumentMatcher<QueryRequest> { private final Predicate<QueryRequest> test; private QueryWithStartIndex(Predicate<QueryRequest> test) { this.test = test; } @Override public boolean matches(Object o) { return test.test((QueryRequest) o); } } }