From 507e2b7b09d6e279faa77df83f5881897b45ac9e Mon Sep 17 00:00:00 2001 From: pkupczyk <pkupczyk> Date: Thu, 4 Sep 2014 15:02:03 +0000 Subject: [PATCH] SSDM-781 : OaipmhServlet automated tests SVN: 32414 --- .../1/dss/services/oaipmh/handler.py | 95 +++++++ .../1/dss/services/oaipmh/plugin.properties | 5 + .../systemtests/OaipmhServletTest.java | 269 ++++++++++++++++++ 3 files changed, 369 insertions(+) create mode 100644 datastore_server/sourceTest/core-plugins/generic-test/1/dss/services/oaipmh/handler.py create mode 100644 datastore_server/sourceTest/core-plugins/generic-test/1/dss/services/oaipmh/plugin.properties create mode 100644 datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/datastoreserver/systemtests/OaipmhServletTest.java diff --git a/datastore_server/sourceTest/core-plugins/generic-test/1/dss/services/oaipmh/handler.py b/datastore_server/sourceTest/core-plugins/generic-test/1/dss/services/oaipmh/handler.py new file mode 100644 index 00000000000..ea8f5446ef5 --- /dev/null +++ b/datastore_server/sourceTest/core-plugins/generic-test/1/dss/services/oaipmh/handler.py @@ -0,0 +1,95 @@ +#! /usr/bin/env python +from java.util import Date +from java.text import SimpleDateFormat +from xml.etree import ElementTree +from xml.etree.ElementTree import Element, SubElement +from com.lyncode.xoai.dataprovider import DataProvider +from com.lyncode.xoai.dataprovider.model import Context, MetadataFormat, Item +from com.lyncode.xoai.dataprovider.repository import Repository, RepositoryConfiguration +from com.lyncode.xoai.dataprovider.parameters import OAIRequest +from com.lyncode.xoai.dataprovider.handlers.results import ListItemIdentifiersResult, ListItemsResults +from com.lyncode.xoai.model.oaipmh import OAIPMH, DeletedRecord, Granularity, Metadata +from com.lyncode.xoai.xml import XmlWriter +from ch.systemsx.cisd.openbis.dss.generic.server.oaipmh.xoai import SimpleItemIdentifier, SimpleItem, SimpleItemRepository, SimpleSetRepository +from ch.systemsx.cisd.openbis.generic.shared.api.v1.dto import SearchCriteria +from ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchCriteria import MatchClause, MatchClauseAttribute, MatchClauseTimeAttribute, CompareMode +DATE_FORMAT = SimpleDateFormat("yyyy-MM-dd") +TIME_ZONE = "0" + +def handle(req, resp): + context = Context(); + context.withMetadataFormat(MetadataFormat().withPrefix("testPrefix").withTransformer(MetadataFormat.identity())); + configuration = RepositoryConfiguration(); + configuration.withMaxListSets(10); + configuration.withMaxListIdentifiers(10); + configuration.withMaxListRecords(10); + configuration.withAdminEmail("test@test"); + configuration.withBaseUrl("http://localhost"); + configuration.withDeleteMethod(DeletedRecord.NO); + configuration.withEarliestDate(Date(0)); + configuration.withRepositoryName("TEST"); + configuration.withGranularity(Granularity.Day); + repository = Repository(); + repository.withConfiguration(configuration); + repository.withItemRepository(ItemRepository()); + repository.withSetRepository(SimpleSetRepository()); + provider = DataProvider(context, repository); + params = {} + for param in req.getParameterNames(): + values = [] + for value in req.getParameterValues(param): + values.append(value) + params[param] = values + request = OAIRequest(params); + response = provider.handle(request); + writer = XmlWriter(resp.getOutputStream()); + response.write(writer); + writer.flush(); + +class ItemRepository(SimpleItemRepository): + + def doGetItem(self, identifier): + criteria = SearchCriteria() + criteria.addMatchClause(MatchClause.createAttributeMatch(MatchClauseAttribute.CODE, identifier)) + dataSets = searchService.searchForDataSets(criteria) + + if dataSets: + return createItem(dataSets[0]) + else: + return None + def doGetItemIdentifiers(self, filters, offset, length, setSpec, fromDate, untilDate): + results = self.doGetItems(filters, offset, length, setSpec, fromDate, untilDate) + return ListItemIdentifiersResult(results.hasMore(), results.getResults(), results.getTotal()) + + def doGetItems(self, filters, offset, length, setSpec, fromDate, untilDate): + criteria = SearchCriteria() + if fromDate: + criteria.addMatchClause(MatchClause.createTimeAttributeMatch(MatchClauseTimeAttribute.REGISTRATION_DATE, CompareMode.GREATER_THAN_OR_EQUAL, DATE_FORMAT.format(fromDate), TIME_ZONE)) + if untilDate: + criteria.addMatchClause(MatchClause.createTimeAttributeMatch(MatchClauseTimeAttribute.REGISTRATION_DATE, CompareMode.LESS_THAN_OR_EQUAL, DATE_FORMAT.format(untilDate), TIME_ZONE)) + dataSets = searchService.searchForDataSets(criteria) + if dataSets: + hasMoreResults = (offset + length) < len(dataSets) + results = [createItem(dataSet) for dataSet in dataSets[offset:(offset + length)]] + total = len(dataSets) + return ListItemsResults(hasMoreResults, results, total) + else: + return ListItemsResults(False, [], 0) + + +def createItemMetadata(dataSet): + properties = Element("properties") + + for propertyCode in dataSet.getAllPropertyCodes(): + property = SubElement(properties, "property") + property.set("code", propertyCode) + property.text = dataSet.getPropertyValue(propertyCode) + + return Metadata(ElementTree.tostring(properties)) + +def createItem(dataSet): + item = SimpleItem() + item.setIdentifier(dataSet.getDataSetCode()) + item.setDatestamp(Date()) + item.setMetadata(createItemMetadata(dataSet)) + return item \ No newline at end of file diff --git a/datastore_server/sourceTest/core-plugins/generic-test/1/dss/services/oaipmh/plugin.properties b/datastore_server/sourceTest/core-plugins/generic-test/1/dss/services/oaipmh/plugin.properties new file mode 100644 index 00000000000..2acdff1936d --- /dev/null +++ b/datastore_server/sourceTest/core-plugins/generic-test/1/dss/services/oaipmh/plugin.properties @@ -0,0 +1,5 @@ +class = ch.systemsx.cisd.openbis.dss.generic.server.oaipmh.OaipmhServlet +path = /oaipmh/* +request-handler = ch.systemsx.cisd.openbis.dss.generic.server.oaipmh.JythonBasedRequestHandler +request-handler.script-path = handler.py +authentication-handler = ch.systemsx.cisd.openbis.dss.generic.server.oaipmh.BasicHttpAuthenticationHandler diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/datastoreserver/systemtests/OaipmhServletTest.java b/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/datastoreserver/systemtests/OaipmhServletTest.java new file mode 100644 index 00000000000..80dda4b7199 --- /dev/null +++ b/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/datastoreserver/systemtests/OaipmhServletTest.java @@ -0,0 +1,269 @@ +/* + * Copyright 2014 ETH Zuerich, Scientific IT Services + * + * 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 ch.systemsx.cisd.openbis.datastoreserver.systemtests; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.List; + +import javax.xml.namespace.QName; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; + +import junit.framework.Assert; + +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.httpclient.HttpClient; +import org.apache.commons.httpclient.HttpException; +import org.apache.commons.httpclient.methods.GetMethod; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; +import org.w3c.dom.Document; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; + +import ch.systemsx.cisd.base.exceptions.CheckedExceptionTunnel; +import ch.systemsx.cisd.common.spring.HttpInvokerUtils; +import ch.systemsx.cisd.openbis.generic.shared.api.v1.IGeneralInformationService; +import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.DataSet; +import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchCriteria; +import ch.systemsx.cisd.openbis.generic.shared.util.TestInstanceHostUtils; + +/** + * @author pkupczyk + */ +@Test(groups = +{ "slow" }) +public class OaipmhServletTest extends SystemTestCase +{ + + private static final String GENERAL_INFORMATION_SERVICE_URL = TestInstanceHostUtils.getOpenBISUrl() + IGeneralInformationService.SERVICE_URL; + + private static final String OAIPMH_SERVLET_URL = TestInstanceHostUtils.getDSSUrl() + "/oaipmh/"; + + private static final String USER_ID = "test"; + + private static final String USER_PASSWORD = "password"; + + private IGeneralInformationService generalInformationService; + + private DocumentBuilder xmlBuilder; + + private XPath xPath; + + @BeforeClass + public void beforeClass() throws ParserConfigurationException + { + generalInformationService = HttpInvokerUtils.createServiceStub(IGeneralInformationService.class, GENERAL_INFORMATION_SERVICE_URL, 5000); + xmlBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + xPath = XPathFactory.newInstance().newXPath(); + } + + @Test + public void testWithoutAuthorizationHeader() + { + GetMethod method = sendRequest(null, OAIPMH_SERVLET_URL + "?verb=Identify"); + Assert.assertEquals(401, method.getStatusCode()); + } + + @Test + public void testWithIncorrectAuthorizationHeader() + { + GetMethod method = sendRequest("This is an invalid header", OAIPMH_SERVLET_URL + "?verb=Identify"); + Assert.assertEquals(500, method.getStatusCode()); + } + + @Test + public void testWithIncorrectCredentials() + { + GetMethod method = sendRequest("incorrect", USER_PASSWORD, OAIPMH_SERVLET_URL + "?verb=Identify"); + Assert.assertEquals(401, method.getStatusCode()); + } + + @Test + public void testIdentify() throws InterruptedException + { + GetMethod method = sendRequest(USER_ID, USER_PASSWORD, OAIPMH_SERVLET_URL + "?verb=Identify"); + Assert.assertEquals(200, method.getStatusCode()); + Document document = parseResponse(method); + Assert.assertEquals("TEST", evaluateToString(document, "/OAI-PMH/Identify/repositoryName")); + } + + @Test + public void testListMetadataformats() + { + GetMethod method = sendRequest(USER_ID, USER_PASSWORD, OAIPMH_SERVLET_URL + "?verb=ListMetadataFormats"); + Assert.assertEquals(200, method.getStatusCode()); + Document document = parseResponse(method); + Assert.assertEquals("testPrefix", evaluateToString(document, "/OAI-PMH/ListMetadataFormats/metadataFormat/metadataPrefix")); + } + + @Test + public void testListSets() + { + GetMethod method = sendRequest(USER_ID, USER_PASSWORD, OAIPMH_SERVLET_URL + "?verb=ListSets"); + Assert.assertEquals(200, method.getStatusCode()); + Document document = parseResponse(method); + Assert.assertEquals("This repository does not support sets", evaluateToString(document, "/OAI-PMH/error")); + } + + @Test + public void testListIdentifiers() + { + String sessionToken = generalInformationService.tryToAuthenticateForAllServices(USER_ID, USER_PASSWORD); + List<DataSet> dataSets = generalInformationService.searchForDataSets(sessionToken, new SearchCriteria()); + + String resumptionToken = null; + int dataSetCount = 0; + + do + { + GetMethod method = null; + if (resumptionToken == null) + { + method = sendRequest(USER_ID, USER_PASSWORD, OAIPMH_SERVLET_URL + "?verb=ListIdentifiers&metadataPrefix=testPrefix"); + } else + { + method = sendRequest(USER_ID, USER_PASSWORD, OAIPMH_SERVLET_URL + "?verb=ListIdentifiers&resumptionToken=" + resumptionToken); + } + Assert.assertEquals(200, method.getStatusCode()); + + Document document = parseResponse(method); + dataSetCount += evaluateToNodeList(document, "/OAI-PMH/ListIdentifiers/header").getLength(); + resumptionToken = evaluateToString(document, "/OAI-PMH/ListIdentifiers/resumptionToken"); + + } while (resumptionToken != null && !resumptionToken.isEmpty()); + + Assert.assertEquals(dataSets.size(), dataSetCount); + } + + @Test + public void testListRecords() + { + String sessionToken = generalInformationService.tryToAuthenticateForAllServices(USER_ID, USER_PASSWORD); + List<DataSet> dataSets = generalInformationService.searchForDataSets(sessionToken, new SearchCriteria()); + + String resumptionToken = null; + int dataSetCount = 0; + + do + { + GetMethod method = null; + if (resumptionToken == null) + { + method = sendRequest(USER_ID, USER_PASSWORD, OAIPMH_SERVLET_URL + "?verb=ListRecords&metadataPrefix=testPrefix"); + } else + { + method = sendRequest(USER_ID, USER_PASSWORD, OAIPMH_SERVLET_URL + "?verb=ListRecords&resumptionToken=" + resumptionToken); + } + Assert.assertEquals(200, method.getStatusCode()); + + Document document = parseResponse(method); + dataSetCount += evaluateToNodeList(document, "/OAI-PMH/ListRecords/record").getLength(); + resumptionToken = evaluateToString(document, "/OAI-PMH/ListRecords/resumptionToken"); + + } while (resumptionToken != null && !resumptionToken.isEmpty()); + + Assert.assertEquals(dataSets.size(), dataSetCount); + } + + @Test + public void testGetRecord() + { + GetMethod method = + sendRequest(USER_ID, USER_PASSWORD, OAIPMH_SERVLET_URL + "?verb=GetRecord&metadataPrefix=testPrefix&identifier=20081105092159111-1"); + Assert.assertEquals(200, method.getStatusCode()); + + Document document = parseResponse(method); + Assert.assertEquals("20081105092159111-1", evaluateToString(document, "/OAI-PMH/GetRecord/record/header/identifier")); + Assert.assertEquals("FEMALE", evaluateToString(document, "/OAI-PMH/GetRecord/record/metadata/properties/property[@code='GENDER']")); + } + + private GetMethod sendRequest(String user, String password, String url) + { + String authorizationHeader = "Basic " + new String(Base64.encodeBase64(new String(user + ":" + password).getBytes())); + return sendRequest(authorizationHeader, url); + } + + private GetMethod sendRequest(String authorizationHeader, String url) + { + try + { + operationLog.info("Sending OAI-PMH request: " + url); + + HttpClient httpClient = new HttpClient(); + GetMethod method = new GetMethod(url); + if (authorizationHeader != null) + { + method.setRequestHeader("Authorization", authorizationHeader); + } + httpClient.executeMethod(method); + + operationLog.info("Received OAI-PMH response: " + method.getResponseBodyAsString()); + + return method; + } catch (HttpException ex) + { + throw CheckedExceptionTunnel.wrapIfNecessary(ex); + } catch (IOException ex) + { + throw CheckedExceptionTunnel.wrapIfNecessary(ex); + } + } + + private Document parseResponse(GetMethod method) + { + try + { + String body = method.getResponseBodyAsString(); + return xmlBuilder.parse(new ByteArrayInputStream(body.getBytes())); + } catch (IOException ex) + { + throw CheckedExceptionTunnel.wrapIfNecessary(ex); + } catch (SAXException ex) + { + throw CheckedExceptionTunnel.wrapIfNecessary(ex); + } + } + + private String evaluateToString(Document document, String xpath) + { + return (String) evaluate(document, xpath, XPathConstants.STRING); + } + + private NodeList evaluateToNodeList(Document document, String xpath) + { + return (NodeList) evaluate(document, xpath, XPathConstants.NODESET); + } + + private Object evaluate(Document document, String xpath, QName returnType) + { + try + { + return xPath.compile(xpath).evaluate(document, returnType); + } catch (XPathExpressionException ex) + { + throw CheckedExceptionTunnel.wrapIfNecessary(ex); + } + } + +} -- GitLab