package edu.kit.aifb.cumulus.webapp;
import static edu.kit.aifb.cumulus.WebTestUtils.EMPTY_STRINGS;
import static edu.kit.aifb.cumulus.WebTestUtils.clean;
import static edu.kit.aifb.cumulus.WebTestUtils.newTripleStore;
import static edu.kit.aifb.cumulus.WebTestUtils.tmpFile;
import static edu.kit.aifb.cumulus.util.Util.*;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.io.File;
import java.io.FileInputStream;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.openrdf.query.TupleQueryResult;
import org.openrdf.query.resultio.BooleanQueryResultFormat;
import org.openrdf.query.resultio.QueryResultIO;
import org.openrdf.query.resultio.TupleQueryResultFormat;
import org.openrdf.repository.sail.SailRepository;
import org.openrdf.rio.RDFFormat;
import edu.kit.aifb.cumulus.AbstractCumulusWebTest;
import edu.kit.aifb.cumulus.StubServletOutputStream;
import edu.kit.aifb.cumulus.framework.Environment.ConfigParams;
import edu.kit.aifb.cumulus.framework.Environment.ConfigValues;
import edu.kit.aifb.cumulus.store.sesame.CumulusRDFSail;
import edu.kit.aifb.cumulus.webapp.HttpProtocol.Methods;
import edu.kit.aifb.cumulus.webapp.HttpProtocol.MimeTypes;
import edu.kit.aifb.cumulus.webapp.HttpProtocol.Parameters;
/**
* Test cases for the {@link SPARQLServlet}.
*
* @author Andreas Wagner
* @author Andrea Gazzarini
* @since 1.0
*/
public class SPARQLServletTest extends AbstractCumulusWebTest {
/**
* Setup fixture for all tests.
*
* @throws Exception never otherwise tests fail.
*/
@BeforeClass
public static void setUp() throws Exception {
TRIPLE_STORE = newTripleStore();
SAIL = new CumulusRDFSail(TRIPLE_STORE);
REPOSITORY = new SailRepository(SAIL);
REPOSITORY.initialize();
}
protected SPARQLServlet _classUnderTest;
private final String[] _invalid_queries = {
"ASK { ?d <http://www.w3.org/2000/01/rdf-schema#",
"CONSTRUCT ASK { ?d <http://www.w3.org/2000/01/rdf-schema#",
"SELECT * WHERE {"
};
private final String[] _accept_ask_header = {
BooleanQueryResultFormat.TEXT.getDefaultMIMEType(),
BooleanQueryResultFormat.JSON.getDefaultMIMEType(),
BooleanQueryResultFormat.SPARQL.getDefaultMIMEType()
};
private final String[] _accept_select_header = {
TupleQueryResultFormat.BINARY.getDefaultMIMEType(),
TupleQueryResultFormat.TSV.getDefaultMIMEType(),
TupleQueryResultFormat.JSON.getDefaultMIMEType(),
TupleQueryResultFormat.SPARQL.getDefaultMIMEType(),
TupleQueryResultFormat.CSV.getDefaultMIMEType()
};
protected ServletContext _context;
/**
* Teardown fixture for each test.
* Basically deletes all data in the store.
*
* @throws Exception hopefully never, otherwise the corresponding test fails.
*/
@After
public void afterEachTest() throws Exception {
clean(TRIPLE_STORE);
}
/**
* Setup fixture for each test.
* Initializes mock stuff and load sample data.
*
* @throws Exception hopefully never, otherwise the corresponding test fails.
*/
@Before
public void beforeEachTest() throws Exception {
_context = mock(ServletContext.class);
when(_context.getAttribute(ConfigParams.SESAME_REPO)).thenReturn(REPOSITORY);
when(_context.getAttribute(ConfigParams.STORE)).thenReturn(TRIPLE_STORE);
when(_context.getAttribute(ConfigParams.LAYOUT)).thenReturn(ConfigValues.STORE_LAYOUT_TRIPLE);
_classUnderTest = spy(new SPARQLServlet());
doReturn(_context).when(_classUnderTest).getServletContext();
when(_context.getNamedDispatcher(anyString())).thenReturn(mock(RequestDispatcher.class));
TRIPLE_STORE.bulkLoad(DATA_NT, RDFFormat.NTRIPLES);
}
/**
* Creates a mock {@link HttpServletRequest}.
*
* @param query the SPARQL query.
* @param update the SPARQL update query.
* @param acceptParameter the accept value as request parameter.
* @param acceptHeader the accept value as request header.
* @return a mock {@link HttpServletRequest}.
*/
protected HttpServletRequest createMockHttpRequest(
final String query,
final String acceptParameter,
final String acceptHeader,
final String update) {
final HttpServletRequest request = mock(HttpServletRequest.class);
when(request.getParameter(Parameters.QUERY)).thenReturn(query);
when(request.getParameter(Parameters.ACCEPT)).thenReturn(acceptParameter);
when(request.getHeader(Parameters.ACCEPT)).thenReturn(acceptHeader);
when(request.getParameter(Parameters.UPDATE)).thenReturn(update);
when(request.getRequestDispatcher(anyString())).thenReturn(mock(RequestDispatcher.class));
when(request.getMethod()).thenReturn(Methods.POST);
return request;
}
/**
* ASK queries with several supported formats.
*
* @param query the ASK queries.
* @param acceptAsParameter accept as request parameter.
* @param acceptAsHeader accept as request header.
* @throws Exception never, otherwise test fails.
*/
private void internalValidAskQueries(
final String query,
final String acceptAsParameter,
final String acceptAsHeader) throws Exception {
final String accept = acceptAsParameter != null ? acceptAsParameter : acceptAsHeader;
final File tmp = tmpFile();
final ServletOutputStream servletOutputStream = new StubServletOutputStream(tmp);
final HttpServletRequest request = createMockHttpRequest(query, acceptAsParameter, acceptAsHeader, null);
final HttpServletResponse response = mock(HttpServletResponse.class);
when(response.getOutputStream()).thenReturn(servletOutputStream);
_classUnderTest.service(request, response);
servletOutputStream.close();
FileInputStream inputStream = null;
try {
inputStream = new FileInputStream(tmp);
assertTrue(
"query: " + query + ", accept: " + accept,
QueryResultIO.parse(inputStream, BooleanQueryResultFormat.forMIMEType(accept)));
} finally {
// CHECKSTYLE:OFF
// @formatter:off
if (inputStream != null) { try { inputStream.close();} catch (final Exception ignore) {}};
// @formatter:on
// CHECKSTYLE:ON
}
}
/**
* If an invalid SPARQL query is received, then a 400 (BAD_REQUEST) should be returned.
*
* @throws Exception never, otherwise the test fails.
*/
@Test
public void invalidQueries() throws Exception {
for (final String query : _invalid_queries) {
final HttpServletRequest request = createMockHttpRequest(
query,
RDFFormat.RDFXML.getDefaultFileExtension(),
null,
null);
final HttpServletResponse response = mock(HttpServletResponse.class);
_classUnderTest.service(request, response);
verify(response).setStatus(HttpServletResponse.SC_BAD_REQUEST);
}
}
/**
* If an input params are null, then a 400 (BAD_REQUEST) should be returned.
*
* @throws Exception never, otherwise the test fails.
*/
@Test
public void nullInputParameters() throws Exception {
for (final String invalidQuery : EMPTY_STRINGS) {
nullInputParameters(invalidQuery, RDFFormat.RDFXML.getDefaultFileExtension(), null, null);
nullInputParameters(null, RDFFormat.RDFXML.getDefaultFileExtension(), null, invalidQuery);
}
}
/**
* Internal method for testing several invalid input scenarios.
*
* @param query the (invalid) query.
* @param update the (invalid) update query.
* @param acceptAsParam the (invalid) accept request parameter.
* @param acceptAsHeader the (invalid) accept header.
* @throws Exception never, otherwise the test fails.
*/
private void nullInputParameters(
final String query,
final String acceptAsParam,
final String acceptAsHeader,
final String update) throws Exception {
final HttpServletRequest request = createMockHttpRequest(query, acceptAsParam, acceptAsHeader, update);
final HttpServletResponse response = mock(HttpServletResponse.class);
_classUnderTest.service(request, response);
verify(response).setStatus(HttpServletResponse.SC_BAD_REQUEST);
}
/**
* ASK queries with several supported formats (sending accept as request header).
*
* @throws Exception never, otherwise test fails.
*/
@Test
public void validAskQueriesWithAcceptHeader() throws Exception {
for (final String query : _sparql_ask) {
for (final String accept : _accept_ask_header) {
internalValidAskQueries(query, null, accept);
}
}
}
/**
* ASK queries with several supported formats (sending accept as request parameter).
*
* @throws Exception never, otherwise test fails.
*/
@Test
public void validAskQueriesWithAcceptParam() throws Exception {
for (final String query : _sparql_ask) {
for (final String accept : _accept_ask_header) {
internalValidAskQueries(query, accept, null);
}
}
}
/**
* CONSTRUCT queries with several supported formats.
*
* @throws Exception never, otherwise test fails.
*/
@Test
public void validConstructQueriesWithAcceptParam() throws Exception {
for (final String query : _sparql_construct) {
for (final String accept : MimeTypes.RDF_SERIALIZATIONS) {
final File tmp = tmpFile();
final ServletOutputStream servletOutputStream = new StubServletOutputStream(tmp);
final HttpServletRequest request = createMockHttpRequest(query, null, accept, null);
final HttpServletResponse response = mock(HttpServletResponse.class);
when(response.getOutputStream()).thenReturn(servletOutputStream);
_classUnderTest.service(request, response);
servletOutputStream.close();
assertTrue("query: " + query + ", accept: " + accept, parseAsIterator(new FileInputStream(tmp), RDFFormat.forMIMEType(accept)).hasNext());
}
}
}
/**
* SELECT queries with several supported formats.
*
* @throws Exception never, otherwise test fails.
*/
@Test
public void validSelectQueriesWithAcceptParam() throws Exception {
for (final String query : _sparql_select) {
for (final String accept : _accept_select_header) {
final File tmp = tmpFile();
final ServletOutputStream servletOutputStream = new StubServletOutputStream(tmp);
final HttpServletRequest request = mock(HttpServletRequest.class);
when(request.getParameter(Parameters.QUERY)).thenReturn(query);
when(request.getParameter(Parameters.ACCEPT)).thenReturn(accept);
when(request.getMethod()).thenReturn(Methods.POST);
final HttpServletResponse response = mock(HttpServletResponse.class);
when(response.getOutputStream()).thenReturn(servletOutputStream);
_classUnderTest.service(request, response);
servletOutputStream.close();
final TupleQueryResult res = QueryResultIO.parse(new FileInputStream(tmp), TupleQueryResultFormat.forMIMEType(accept));
assertTrue("query: " + query + ", accept: " + accept, _queries2count.get(query) > 0 ? res.hasNext() : !res.hasNext());
}
}
}
}