/**
* Copyright (C) 2014 Cohesive Integrations, LLC (info@cohesiveintegrations.com)
*
* 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 net.di2e.ecdr.source.rest;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
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 java.io.Serializable;
import java.net.URI;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import javax.ws.rs.core.Response;
import net.di2e.ecdr.commons.constants.SearchConstants;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.cxf.jaxrs.client.WebClient;
import org.geotools.filter.text.cql2.CQL;
import org.junit.Before;
import org.junit.Test;
import org.opengis.filter.sort.SortBy;
import org.opengis.filter.sort.SortOrder;
import ddf.catalog.data.Result;
import ddf.catalog.filter.impl.SortByImpl;
import ddf.catalog.filter.proxy.adapter.GeotoolsFilterAdapterImpl;
import ddf.catalog.operation.QueryRequest;
import ddf.catalog.operation.SourceResponse;
import ddf.catalog.operation.impl.QueryImpl;
import ddf.catalog.operation.impl.QueryRequestImpl;
import ddf.catalog.source.SourceMonitor;
import ddf.catalog.source.UnsupportedQueryException;
public abstract class CDRAbstractSourceTest {
private static final String SERVICE_URL = "http://localhost:8181/services/cdr/test";
private static final String PING_URL = "http://localhost:8181/services/cdr/ping";
private static final String RESOURCE_URL = "http://localhost:8181/services/catalog/sources/SELF/553b8ce5718c40ac9e093823817bbb98?transform=resource";
private static final String RESPONSE_FILE = "/exampleResponse.xml";
protected static final GeotoolsFilterAdapterImpl FILTER_ADAPTER = new GeotoolsFilterAdapterImpl();
private WebClient client;
@Before
public void setUp() {
client = mock( WebClient.class );
}
/**
* Tests that a site comes back as available using a GET request when the response is OK.
*/
@Test
public void testGetPing() {
WebClient pingClient = mock( WebClient.class );
CDROpenSearchSource source = configureSource();
source.setPingClient( pingClient );
Response response = mock( Response.class );
when( response.getStatus() ).thenReturn( Response.Status.OK.getStatusCode() );
when( pingClient.get() ).thenReturn( response );
assertTrue( source.isAvailable() );
// test cache
assertTrue( source.isAvailable() );
// verify get was only called once
verify( pingClient ).get();
}
/**
* Tests that a site comes back as available using a HEAD request when the response is OK.
*/
@Test
public void testHeadPing() {
SourceMonitor monitor = mock( SourceMonitor.class );
WebClient pingClient = mock( WebClient.class );
CDROpenSearchSource source = configureSource();
source.setPingClient( pingClient );
Response response = mock( Response.class );
when( response.getStatus() ).thenReturn( Response.Status.OK.getStatusCode() );
when( pingClient.head() ).thenReturn( response );
source.setPingMethodString( CDRSourceConfiguration.PingMethod.HEAD.toString() );
assertTrue( source.isAvailable( monitor ) );
// test cache
assertTrue( source.isAvailable() );
// verify head was only called once
verify( pingClient ).head();
// verify monitor was called available and not unavailable
verify( monitor, times( 2 ) ).setAvailable();
verify( monitor, never() ).setUnavailable();
}
/**
* Tests that a site comes back correctly as not available if the ping returns a non-OK response
*/
@Test
public void testPingNotAvailable() {
SourceMonitor monitor = mock( SourceMonitor.class );
WebClient pingClient = mock( WebClient.class );
CDROpenSearchSource source = configureSource();
source.setPingClient( pingClient );
Response response = mock( Response.class );
when( response.getStatus() ).thenReturn( Response.Status.NOT_FOUND.getStatusCode() );
when( pingClient.head() ).thenReturn( response );
source.setPingMethodString( CDRSourceConfiguration.PingMethod.HEAD.toString() );
assertFalse( source.isAvailable( monitor ) );
verify( pingClient ).head();
// verify monitor was called unavailable and not available
verify( monitor ).setUnavailable();
verify( monitor, never() ).setAvailable();
}
/**
* Tests that a site comes back as available using a HEAD request when the response is OK.
*/
@Test
public void testNoPingAvailable() {
SourceMonitor monitor = mock( SourceMonitor.class );
WebClient pingClient = mock( WebClient.class );
CDROpenSearchSource source = configureSource();
source.setPingClient( pingClient );
Response response = mock( Response.class );
when( response.getStatus() ).thenReturn( Response.Status.OK.getStatusCode() );
when( pingClient.head() ).thenReturn( response );
source.setPingMethodString( CDRSourceConfiguration.PingMethod.NONE.toString() );
assertTrue( source.isAvailable( monitor ) );
// test cache
assertTrue( source.isAvailable() );
// verify ping url was never called
verify( pingClient, never() ).head();
// verify monitor was called available and not unavailable
verify( monitor, times( 2 ) ).setAvailable();
verify( monitor, never() ).setUnavailable();
}
@Test( expected = UnsupportedQueryException.class )
public void testBadQueryResponse() throws Exception {
CDROpenSearchSource source = configureSource();
QueryRequest request = new QueryRequestImpl( new QueryImpl( CQL.toFilter( "id = '12345678910'" ) ) );
when( client.getCurrentURI() ).thenReturn( new URI( SERVICE_URL ) );
Response webResponse = mock( Response.class );
when( webResponse.getStatus() ).thenReturn( Response.Status.NOT_FOUND.getStatusCode() );
when( webResponse.getEntity() ).thenReturn( IOUtils.toInputStream( "NOT FOUND" ) );
when( client.get() ).thenReturn( webResponse );
source.query( request );
fail( "Should have thrown an error during the query." );
}
@Test
public void testUidQuery() throws Exception {
performQuery( "id = '12345678910'" );
}
@Test
public void testKeywordQuery() throws Exception {
performQuery( "metadata like 'example'" );
}
@Test
public void testTemporalQuery() throws Exception {
performQuery( "created before 2014-05-05T00:00:00" );
}
@Test( expected = UnsupportedQueryException.class )
public void testInvalidResponseTransformer() throws Exception {
performQuery( "created before 2014-05-05T00:00:00", "blah" );
}
@Test
public void testdoRetrieval() throws Exception {
CDROpenSearchSource source = configureSource();
WebClient retrievalClient = mock( WebClient.class );
Response retrievalResponse = mock( Response.class );
when( retrievalResponse.getStatus() ).thenReturn( Response.Status.OK.getStatusCode() );
when( retrievalClient.get() ).thenReturn( retrievalResponse );
when( client.getCurrentURI() ).thenReturn( new URI( RESOURCE_URL ) );
when( retrievalResponse.getEntity() ).thenReturn( getClass().getResourceAsStream( RESPONSE_FILE ) );
source.doRetrieval( retrievalClient, Collections.<String, Serializable>emptyMap() );
}
@Test
public void testdoRetrievalSkipBytes() throws Exception {
CDROpenSearchSource source = configureSource();
WebClient retrievalClient = mock( WebClient.class );
Response retrievalResponse = mock( Response.class );
when( retrievalResponse.getStatus() ).thenReturn( Response.Status.OK.getStatusCode() );
when( retrievalClient.get() ).thenReturn( retrievalResponse );
when( client.getCurrentURI() ).thenReturn( new URI( RESOURCE_URL ) );
when( retrievalResponse.getEntity() ).thenReturn( getClass().getResourceAsStream( RESPONSE_FILE ) );
Map<String, Serializable> requestProperties = new HashMap<>();
requestProperties.put( "BytesToSkip", new Long( 1000 ) );
// no range header
source.doRetrieval( retrievalClient, requestProperties );
// with range header and disposition
when( retrievalResponse.getHeaderString( "Content-Disposition" ) ).thenReturn( "attachment;filename=test.example" );
when( retrievalResponse.getHeaderString( "Accept-Ranges" ) ).thenReturn( "bytes" );
source.doRetrieval( retrievalClient, requestProperties );
}
private void performQuery( String CQLStr ) throws Exception {
this.performQuery( CQLStr, null );
}
private void performQuery( String CQLStr, String responseTransformer ) throws Exception {
CDROpenSearchSource source = configureSource();
if ( StringUtils.isNotBlank( responseTransformer ) ) {
source.setResponseTransformer( responseTransformer );
}
SortBy sort = new SortByImpl( Result.RELEVANCE, SortOrder.DESCENDING );
QueryRequestImpl request = new QueryRequestImpl( new QueryImpl( CQL.toFilter( CQLStr ), 0, 20, sort, true, 10000 ) );
Map<String, Serializable> properties = new HashMap<String, Serializable>();
properties.put( SearchConstants.STRICTMODE_PARAMETER, Boolean.TRUE );
properties.put( "EMID", "value" );
properties.put( "NoHeaderMatch", "value" );
request.setProperties( properties );
when( client.getCurrentURI() ).thenReturn( new URI( SERVICE_URL ) );
Response webResponse = mock( Response.class );
when( webResponse.getStatus() ).thenReturn( Response.Status.OK.getStatusCode() );
when( webResponse.getEntity() ).thenReturn( getClass().getResourceAsStream( RESPONSE_FILE ) );
when( client.get() ).thenReturn( webResponse );
SourceResponse response = source.query( request );
assertEquals( 19, response.getHits() );
assertFalse( response.getResults().isEmpty() );
assertEquals( 19, response.getResults().size() );
}
private CDROpenSearchSource configureSource() {
CDROpenSearchSource source = createSource();
source.setId( "example_site" );
source.setPingUrl( PING_URL );
source.setPingMethodString( CDRSourceConfiguration.PingMethod.GET.toString() );
source.setAvailableCheckCacheTime( 60 );
source.setMaxResultCount( 10 );
source.setUrl( SERVICE_URL );
source.setReceiveTimeoutSeconds( 10 );
source.setConnectionTimeoutSeconds( 1 );
source.setRestClient( client );
return source;
}
abstract CDROpenSearchSource createSource();
}