diff --git a/pom.xml b/pom.xml index 12072b4..d0877c0 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,5 @@ - + org.opengis.cite @@ -16,17 +17,17 @@ "OGC Catalogue Services 3.0 Specification -- HTTP Protocol Binding" (OGC 12-176r6) and related specifications. - http://opengeospatial.github.io/ets-cat30/ + https://opengeospatial.github.io/ets-cat30/ Apache License, Version 2.0 - http://opensource.org/licenses/Apache-2.0 + https://opensource.org/licenses/Apache-2.0 Open Geospatial Consortium - http://www.opengeospatial.org/ + https://www.ogc.org/ scm:git:https://github.com/opengeospatial/ets-cat30.git @@ -44,6 +45,11 @@ https://github.com/rjmartell Canada/Pacific + + D. Stenger + https://github.com/dstenger + Europe/Berlin + @@ -60,13 +66,6 @@ 6.0.0-RC1 - - - smartbear-sweden-plugin-repository - https://www.soapui.org/repository/maven2/ - - - org.opengis.cite @@ -114,8 +113,8 @@ jersey-media-jaxb - org.glassfish.jersey.inject - jersey-hk2 + org.glassfish.jersey.inject + jersey-hk2 junit @@ -147,10 +146,10 @@ xerces xercesImpl - - xml-apis - xml-apis - + + xml-apis + xml-apis + @@ -163,81 +162,27 @@ maven-assembly-plugin - 3.6.0 org.opengis.cite.cat30.TestNGController - - ${basedir}/src/assembly/deps.xml - ${basedir}/src/assembly/ctl-scripts.xml - ${basedir}/src/assembly/aio.xml - - - - - package - - single - - - - - - maven-surefire-plugin - - - maven-compiler-plugin - - 17 - 17 - maven-jar-plugin - - - org.apache.maven.plugins - maven-release-plugin - - true - @{project.version} - release - - - - org.codehaus.mojo - buildnumber-maven-plugin - - - org.apache.maven.plugins maven-scm-publish-plugin - 3.2.1 - - gh-pages - + com.smartbear.soapui soapui-maven-plugin - 5.7.2 - - - com.jgoodies - forms - 1.2.1 - - src/test/resources/soapui/ets-cat30-soapui-project.xml src/test/resources/soapui/ets-cat30-soapui-settings.xml - ${project.build.directory}/soapui - true ${soapui.test.fail.ignore} teamengine.endpoint=${soapui.teamengine.endpoint} @@ -253,31 +198,13 @@ io.fabric8 docker-maven-plugin - 0.43.4 - ogccite/${project.artifactId} - ${project.basedir}/src/docker ${project.version}-teamengine-${docker.teamengine.version} - - - - - ${project.build.directory} - . - - dependency/*teamengine-*.war - dependency/*teamengine-*.zip - *ets-*.zip - - - - - @@ -296,7 +223,6 @@ maven-dependency-plugin - 3.6.1 @@ -327,34 +253,6 @@ - - release - - - - org.apache.maven.plugins - maven-gpg-plugin - 3.1.0 - - - sign-artifacts - verify - - sign - - - - - - - - - - - docker @@ -443,6 +341,13 @@ + + + smartbear-sweden-plugin-repository + https://www.soapui.org/repository/maven2/ + + + @@ -454,10 +359,6 @@ - - sonatype-nexus-staging - https://oss.sonatype.org/service/local/staging/deploy/maven2/ - site scm:git:git@github.com:opengeospatial/ets-cat30.git diff --git a/src/main/java/org/opengis/cite/cat30/CAT3.java b/src/main/java/org/opengis/cite/cat30/CAT3.java index 0a57589..712e97a 100644 --- a/src/main/java/org/opengis/cite/cat30/CAT3.java +++ b/src/main/java/org/opengis/cite/cat30/CAT3.java @@ -1,74 +1,108 @@ package org.opengis.cite.cat30; /** - * Contains various constants pertaining to Catalogue 3.0 (HTTP) service - * interfaces as specified in OGC 12-176r5 and related standards. + * Contains various constants pertaining to Catalogue 3.0 (HTTP) service interfaces as + * specified in OGC 12-176r5 and related standards. * - * @see "OGC Catalogue Services 3.0 Specification -- HTTP Protocol Binding, - * Version 3.0" + * @see "OGC Catalogue Services 3.0 Specification -- HTTP Protocol Binding, Version 3.0" */ public class CAT3 { - private CAT3() { - } - - public static final String SCHEMA_URI - = "http://schemas.opengis.net/cat/csw/3.0/cswAll.xsd"; - public static final String SERVICE_TYPE_CODE = "CSW"; - public static final String VERSION_3_0_0 = "3.0.0"; - public static final String GET_CAPABILITIES = "GetCapabilities"; - public static final String GET_RECORD_BY_ID = "GetRecordById"; - public static final String GET_RECORDS = "GetRecords"; - public static final String GET_RECORDS_RSP = "GetRecordsResponse"; - public static final String SEARCH_RESULTS = "SearchResults"; - - // request parameters - public static final String REQUEST = "request"; - public static final String SERVICE = "service"; - public static final String VERSION = "version"; - public static final String ACCEPT_VERSIONS = "acceptVersions"; - public static final String ACCEPT_FORMATS = "acceptFormats"; - public static final String SECTIONS = "sections"; - public static final String ID = "id"; - public static final String MAX_RECORDS = "maxRecords"; - public static final String ELEMENT_SET = "elementSetName"; - public static final String ELEMENT_NAME = "elementName"; - public static final String TYPE_NAMES = "typeNames"; - public static final String NAMESPACE = "namespace"; - public static final String OUTPUT_FORMAT = "outputFormat"; - public static final String OUTPUT_SCHEMA = "outputSchema"; - public static final String START_POS = "startPosition"; - public static final String BBOX = "bbox"; - public static final String Q = "q"; - public static final String REC_ID_LIST = "recordIds"; - - // response properties - public static final String NUM_REC_RETURNED = "numberOfRecordsReturned"; - public static final String NUM_REC_MATCHED = "numberOfRecordsMatched"; - public static final String NEXT_REC = "nextRecord"; - - // query parameter (code list) values - public static final String ELEMENT_SET_FULL = "full"; - public static final String ELEMENT_SET_SUMMARY = "summary"; - public static final String ELEMENT_SET_BRIEF = "brief"; - - // exception codes: see OGC 06-121r9, Table 27; - public static final String VER_NEGOTIATION_FAILED = "VersionNegotiationFailed"; - public static final String MISSING_PARAM_VAL = "MissingParameterValue"; - public static final String INVALID_PARAM_VAL = "InvalidParameterValue"; - public static final String INVALID_UPDATE_SEQ = "InvalidUpdateSequence"; - public static final String OPER_NOT_SUPPORTED = "OperationNotSupported"; - public static final String OPT_NOT_SUPPORTED = "OptionNotSupported"; - public static final String NO_CODE = "NoApplicableCode"; - public static final String PROCESSING_FAILED = "OperationProcessingFailed"; - public static final String PARSING_FAILED = "OperationParsingFailed"; - - // media types - public static final String APP_OPENSEARCH_XML - = "application/opensearchdescription+xml"; - public static final String APP_VND_OPENSEARCH_XML - = "application/vnd.a9.opensearchdescription+xml"; - - // conformance classes - public static final String CC_OPEN_SEARCH = "OpenSearch"; + private CAT3() { + } + + public static final String SCHEMA_URI = "http://schemas.opengis.net/cat/csw/3.0/cswAll.xsd"; + + public static final String SERVICE_TYPE_CODE = "CSW"; + + public static final String VERSION_3_0_0 = "3.0.0"; + + public static final String GET_CAPABILITIES = "GetCapabilities"; + + public static final String GET_RECORD_BY_ID = "GetRecordById"; + + public static final String GET_RECORDS = "GetRecords"; + + public static final String GET_RECORDS_RSP = "GetRecordsResponse"; + + public static final String SEARCH_RESULTS = "SearchResults"; + + // request parameters + public static final String REQUEST = "request"; + + public static final String SERVICE = "service"; + + public static final String VERSION = "version"; + + public static final String ACCEPT_VERSIONS = "acceptVersions"; + + public static final String ACCEPT_FORMATS = "acceptFormats"; + + public static final String SECTIONS = "sections"; + + public static final String ID = "id"; + + public static final String MAX_RECORDS = "maxRecords"; + + public static final String ELEMENT_SET = "elementSetName"; + + public static final String ELEMENT_NAME = "elementName"; + + public static final String TYPE_NAMES = "typeNames"; + + public static final String NAMESPACE = "namespace"; + + public static final String OUTPUT_FORMAT = "outputFormat"; + + public static final String OUTPUT_SCHEMA = "outputSchema"; + + public static final String START_POS = "startPosition"; + + public static final String BBOX = "bbox"; + + public static final String Q = "q"; + + public static final String REC_ID_LIST = "recordIds"; + + // response properties + public static final String NUM_REC_RETURNED = "numberOfRecordsReturned"; + + public static final String NUM_REC_MATCHED = "numberOfRecordsMatched"; + + public static final String NEXT_REC = "nextRecord"; + + // query parameter (code list) values + public static final String ELEMENT_SET_FULL = "full"; + + public static final String ELEMENT_SET_SUMMARY = "summary"; + + public static final String ELEMENT_SET_BRIEF = "brief"; + + // exception codes: see OGC 06-121r9, Table 27; + public static final String VER_NEGOTIATION_FAILED = "VersionNegotiationFailed"; + + public static final String MISSING_PARAM_VAL = "MissingParameterValue"; + + public static final String INVALID_PARAM_VAL = "InvalidParameterValue"; + + public static final String INVALID_UPDATE_SEQ = "InvalidUpdateSequence"; + + public static final String OPER_NOT_SUPPORTED = "OperationNotSupported"; + + public static final String OPT_NOT_SUPPORTED = "OptionNotSupported"; + + public static final String NO_CODE = "NoApplicableCode"; + + public static final String PROCESSING_FAILED = "OperationProcessingFailed"; + + public static final String PARSING_FAILED = "OperationParsingFailed"; + + // media types + public static final String APP_OPENSEARCH_XML = "application/opensearchdescription+xml"; + + public static final String APP_VND_OPENSEARCH_XML = "application/vnd.a9.opensearchdescription+xml"; + + // conformance classes + public static final String CC_OPEN_SEARCH = "OpenSearch"; + } diff --git a/src/main/java/org/opengis/cite/cat30/CommonFixture.java b/src/main/java/org/opengis/cite/cat30/CommonFixture.java index 1cdcb35..292880d 100644 --- a/src/main/java/org/opengis/cite/cat30/CommonFixture.java +++ b/src/main/java/org/opengis/cite/cat30/CommonFixture.java @@ -18,130 +18,127 @@ import jakarta.ws.rs.core.Response; /** - * A supporting base class that sets up a common test fixture. These - * configuration methods are invoked before those defined in a subclass. + * A supporting base class that sets up a common test fixture. These configuration methods + * are invoked before those defined in a subclass. */ public class CommonFixture { - /** - * Root test suite package (absolute path). - */ - public static final String ROOT_PKG_PATH = "/org/opengis/cite/cat30/"; - /** - * Classpath reference for Schematron schema (Atom feed). - */ - public static final String SCHEMATRON_ATOM = "/org/opengis/cite/cat30/sch/atom-feed.sch"; - /** - * HTTP client component (JAX-RS Client API). - */ - protected Client client; - /** - * Service capabilities document (csw:Capabilities). - */ - protected Document cswCapabilities; - /** - * An immutable Schema object for validating all CSW 3.0 messages - * (cswAll.xsd). - */ - protected Schema cswSchema; - /** - * An immutable Schema object for validating Atom feeds/entries (RFC 4287, - * Appendix B). - */ - protected Schema atomSchema; - /** - * An HTTP request message. - */ - protected ClientRequest request; - /** - * An HTTP response message. - */ - protected Response response; - - /** - * Initializes the common test fixture with the following objects: - * - *
    - *
  • a client component for interacting with HTTP endpoints
  • - *
  • the CSW message schema (obtained from the suite attribute - * {@link org.opengis.cite.cat30.SuiteAttribute#CSW_SCHEMA}, a thread-safe - * Schema object).
  • - *
  • the Atom schema (obtained from the suite attribute - * {@link org.opengis.cite.cat30.SuiteAttribute#ATOM_SCHEMA}, a thread-safe - * Schema object).
  • - *
  • the service capabilities document (obtained from the suite attribute - * {@link org.opengis.cite.cat30.SuiteAttribute#TEST_SUBJECT}, which should - * evaluate to a DOM Document node).
  • - *
- * - * @param testContext The test context that contains all the information for - * a test run, including suite attributes. - */ - @BeforeClass - public void initCommonFixture(ITestContext testContext) { - Object obj = testContext.getSuite().getAttribute(SuiteAttribute.CLIENT.getName()); - if (null != obj) { - this.client = Client.class.cast(obj); - } - obj = testContext.getSuite().getAttribute(SuiteAttribute.TEST_SUBJECT.getName()); - if (null == obj) { - throw new SkipException("Capabilities document not found in ITestContext."); - } - this.cswCapabilities = Document.class.cast(obj); - obj = testContext.getSuite().getAttribute(SuiteAttribute.CSW_SCHEMA.getName()); - if (null == obj) { - throw new SkipException("CSW schema not found in ITestContext."); - } - this.cswSchema = Schema.class.cast(obj); - obj = testContext.getSuite().getAttribute(SuiteAttribute.ATOM_SCHEMA.getName()); - if (null == obj) { - throw new SkipException("Atom schema not found in ITestContext."); - } - this.atomSchema = Schema.class.cast(obj); - } - - @BeforeMethod - public void clearMessages() { - this.request = null; - this.response = null; - } - - /** - * Obtains the (XML) response entity as a DOM Document. This convenience - * method wraps a static method call to facilitate unit testing (Mockito - * workaround). - * - * @param response A representation of an HTTP response message. - * @param targetURI The target URI from which the entity was retrieved (may - * be null). - * @return A Document representing the entity. - * - * @see - * ClientUtils#getResponseEntityAsDocument(com.sun.jersey.api.client.ClientResponse, - * java.lang.String) - */ - public Document getResponseEntityAsDocument(Response response, - String targetURI) { - return ClientUtils.getResponseEntityAsDocument(response, targetURI); - } - - /** - * Builds an HTTP request message that uses the GET method. This convenience - * method wraps a static method call to facilitate unit testing (Mockito - * workaround). - * - * @param endpoint A URI indicating the target resource. - * @param qryParams A Map containing query parameters (may be null); - * @param mediaTypes A list of acceptable media types; if not specified, - * generic XML ("application/xml") is preferred. - * @return A ClientRequest object. - * - * @see ClientUtils#buildGetRequest(java.net.URI, java.util.Map, - * javax.ws.rs.core.MediaType...) - */ - public Response buildGetRequest(URI endpoint, - Map qryParams, MediaType... mediaTypes) { - return ClientUtils.buildGetRequest(endpoint, qryParams, mediaTypes); - } + /** + * Root test suite package (absolute path). + */ + public static final String ROOT_PKG_PATH = "/org/opengis/cite/cat30/"; + + /** + * Classpath reference for Schematron schema (Atom feed). + */ + public static final String SCHEMATRON_ATOM = "/org/opengis/cite/cat30/sch/atom-feed.sch"; + + /** + * HTTP client component (JAX-RS Client API). + */ + protected Client client; + + /** + * Service capabilities document (csw:Capabilities). + */ + protected Document cswCapabilities; + + /** + * An immutable Schema object for validating all CSW 3.0 messages (cswAll.xsd). + */ + protected Schema cswSchema; + + /** + * An immutable Schema object for validating Atom feeds/entries (RFC 4287, Appendix + * B). + */ + protected Schema atomSchema; + + /** + * An HTTP request message. + */ + protected ClientRequest request; + + /** + * An HTTP response message. + */ + protected Response response; + + /** + * Initializes the common test fixture with the following objects: + * + *
    + *
  • a client component for interacting with HTTP endpoints
  • + *
  • the CSW message schema (obtained from the suite attribute + * {@link org.opengis.cite.cat30.SuiteAttribute#CSW_SCHEMA}, a thread-safe Schema + * object).
  • + *
  • the Atom schema (obtained from the suite attribute + * {@link org.opengis.cite.cat30.SuiteAttribute#ATOM_SCHEMA}, a thread-safe Schema + * object).
  • + *
  • the service capabilities document (obtained from the suite attribute + * {@link org.opengis.cite.cat30.SuiteAttribute#TEST_SUBJECT}, which should evaluate + * to a DOM Document node).
  • + *
+ * @param testContext The test context that contains all the information for a test + * run, including suite attributes. + */ + @BeforeClass + public void initCommonFixture(ITestContext testContext) { + Object obj = testContext.getSuite().getAttribute(SuiteAttribute.CLIENT.getName()); + if (null != obj) { + this.client = Client.class.cast(obj); + } + obj = testContext.getSuite().getAttribute(SuiteAttribute.TEST_SUBJECT.getName()); + if (null == obj) { + throw new SkipException("Capabilities document not found in ITestContext."); + } + this.cswCapabilities = Document.class.cast(obj); + obj = testContext.getSuite().getAttribute(SuiteAttribute.CSW_SCHEMA.getName()); + if (null == obj) { + throw new SkipException("CSW schema not found in ITestContext."); + } + this.cswSchema = Schema.class.cast(obj); + obj = testContext.getSuite().getAttribute(SuiteAttribute.ATOM_SCHEMA.getName()); + if (null == obj) { + throw new SkipException("Atom schema not found in ITestContext."); + } + this.atomSchema = Schema.class.cast(obj); + } + + @BeforeMethod + public void clearMessages() { + this.request = null; + this.response = null; + } + + /** + * Obtains the (XML) response entity as a DOM Document. This convenience method wraps + * a static method call to facilitate unit testing (Mockito workaround). + * @param response A representation of an HTTP response message. + * @param targetURI The target URI from which the entity was retrieved (may be null). + * @return A Document representing the entity. + * + * @see ClientUtils#getResponseEntityAsDocument(com.sun.jersey.api.client.ClientResponse, + * java.lang.String) + */ + public Document getResponseEntityAsDocument(Response response, String targetURI) { + return ClientUtils.getResponseEntityAsDocument(response, targetURI); + } + + /** + * Builds an HTTP request message that uses the GET method. This convenience method + * wraps a static method call to facilitate unit testing (Mockito workaround). + * @param endpoint A URI indicating the target resource. + * @param qryParams A Map containing query parameters (may be null); + * @param mediaTypes A list of acceptable media types; if not specified, generic XML + * ("application/xml") is preferred. + * @return A ClientRequest object. + * + * @see ClientUtils#buildGetRequest(java.net.URI, java.util.Map, + * javax.ws.rs.core.MediaType...) + */ + public Response buildGetRequest(URI endpoint, Map qryParams, MediaType... mediaTypes) { + return ClientUtils.buildGetRequest(endpoint, qryParams, mediaTypes); + } } diff --git a/src/main/java/org/opengis/cite/cat30/ETSAssert.java b/src/main/java/org/opengis/cite/cat30/ETSAssert.java index e2dcf97..3fb0603 100644 --- a/src/main/java/org/opengis/cite/cat30/ETSAssert.java +++ b/src/main/java/org/opengis/cite/cat30/ETSAssert.java @@ -47,307 +47,272 @@ */ public class ETSAssert { - private static final Logger LOGR = Logger.getLogger( - ETSAssert.class.getPackage().getName()); + private static final Logger LOGR = Logger.getLogger(ETSAssert.class.getPackage().getName()); - private ETSAssert() { - } + private ETSAssert() { + } - /** - * Asserts that the qualified name of a DOM Node matches the expected value. - * - * @param node The Node to check. - * @param qName A QName object containing a namespace name (URI) and a local - * part. - */ - public static void assertQualifiedName(Node node, QName qName) { - Assert.assertEquals(node.getLocalName(), qName.getLocalPart(), - ErrorMessage.get(ErrorMessageKeys.LOCAL_NAME)); - String nsName = (null != node.getNamespaceURI()) - ? node.getNamespaceURI() - : XMLConstants.NULL_NS_URI; - Assert.assertEquals(nsName, qName.getNamespaceURI(), - ErrorMessage.get(ErrorMessageKeys.NAMESPACE_NAME)); - } + /** + * Asserts that the qualified name of a DOM Node matches the expected value. + * @param node The Node to check. + * @param qName A QName object containing a namespace name (URI) and a local part. + */ + public static void assertQualifiedName(Node node, QName qName) { + Assert.assertEquals(node.getLocalName(), qName.getLocalPart(), ErrorMessage.get(ErrorMessageKeys.LOCAL_NAME)); + String nsName = (null != node.getNamespaceURI()) ? node.getNamespaceURI() : XMLConstants.NULL_NS_URI; + Assert.assertEquals(nsName, qName.getNamespaceURI(), ErrorMessage.get(ErrorMessageKeys.NAMESPACE_NAME)); + } - /** - * Asserts that an XPath 1.0 expression holds true for the given evaluation - * context. The following standard namespace bindings do not need to be - * explicitly declared: - * - *
    - *
  • ows: {@value org.opengis.cite.cat30.Namespaces#OWS}
  • - *
  • ows11: {@value org.opengis.cite.cat30.Namespaces#OWS11}
  • - *
  • xlink: {@value org.opengis.cite.cat30.Namespaces#XLINK}
  • - *
  • gml: {@value org.opengis.cite.cat30.Namespaces#GML}
  • - *
  • csw: {@value org.opengis.cite.cat30.Namespaces#CSW}
  • - *
  • dc: {@value org.opengis.cite.cat30.Namespaces#DCMES}
  • - *
- * - * @param expr A valid XPath 1.0 expression. - * @param context The context node (Document or Element). - * @param namespaceBindings A collection of namespace bindings for the XPath - * expression, where each entry maps a namespace URI (key) to a prefix - * (value). It may be {@code null}. - */ - public static void assertXPath(String expr, Node context, - Map namespaceBindings) { - if (null == context) { - throw new NullPointerException("Context node is null."); - } - NamespaceBindings bindings = NamespaceBindings.withStandardBindings(); - bindings.addAllBindings(namespaceBindings); - XPath xpath = XPathFactory.newInstance().newXPath(); - xpath.setNamespaceContext(bindings); - Boolean result; - try { - result = (Boolean) xpath.evaluate(expr, context, - XPathConstants.BOOLEAN); - } catch (XPathExpressionException xpe) { - String msg = ErrorMessage - .format(ErrorMessageKeys.XPATH_ERROR, expr); - LOGR.log(Level.WARNING, msg, xpe); - throw new AssertionError(msg); - } - Element elemNode; - if (Document.class.isInstance(context)) { - elemNode = Document.class.cast(context).getDocumentElement(); - } else { - elemNode = (Element) context; - } - Assert.assertTrue( - result, - ErrorMessage.format(ErrorMessageKeys.XPATH_RESULT, - elemNode.getNodeName(), expr)); - } + /** + * Asserts that an XPath 1.0 expression holds true for the given evaluation context. + * The following standard namespace bindings do not need to be explicitly declared: + * + *
    + *
  • ows: {@value org.opengis.cite.cat30.Namespaces#OWS}
  • + *
  • ows11: {@value org.opengis.cite.cat30.Namespaces#OWS11}
  • + *
  • xlink: {@value org.opengis.cite.cat30.Namespaces#XLINK}
  • + *
  • gml: {@value org.opengis.cite.cat30.Namespaces#GML}
  • + *
  • csw: {@value org.opengis.cite.cat30.Namespaces#CSW}
  • + *
  • dc: {@value org.opengis.cite.cat30.Namespaces#DCMES}
  • + *
+ * @param expr A valid XPath 1.0 expression. + * @param context The context node (Document or Element). + * @param namespaceBindings A collection of namespace bindings for the XPath + * expression, where each entry maps a namespace URI (key) to a prefix (value). It may + * be {@code null}. + */ + public static void assertXPath(String expr, Node context, Map namespaceBindings) { + if (null == context) { + throw new NullPointerException("Context node is null."); + } + NamespaceBindings bindings = NamespaceBindings.withStandardBindings(); + bindings.addAllBindings(namespaceBindings); + XPath xpath = XPathFactory.newInstance().newXPath(); + xpath.setNamespaceContext(bindings); + Boolean result; + try { + result = (Boolean) xpath.evaluate(expr, context, XPathConstants.BOOLEAN); + } + catch (XPathExpressionException xpe) { + String msg = ErrorMessage.format(ErrorMessageKeys.XPATH_ERROR, expr); + LOGR.log(Level.WARNING, msg, xpe); + throw new AssertionError(msg); + } + Element elemNode; + if (Document.class.isInstance(context)) { + elemNode = Document.class.cast(context).getDocumentElement(); + } + else { + elemNode = (Element) context; + } + Assert.assertTrue(result, ErrorMessage.format(ErrorMessageKeys.XPATH_RESULT, elemNode.getNodeName(), expr)); + } - /** - * Asserts that an XML resource is schema-valid. - * - * @param validator The Validator to use. - * @param source The XML Source to be validated. - */ - public static void assertSchemaValid(Validator validator, Source source) { - ValidationErrorHandler errHandler = new ValidationErrorHandler(); - validator.setErrorHandler(errHandler); - try { - validator.validate(source); - } catch (SAXException | IOException e) { - throw new AssertionError(ErrorMessage.format( - ErrorMessageKeys.XML_ERROR, e.getMessage())); - } - Assert.assertFalse(errHandler.errorsDetected(), ErrorMessage.format( - ErrorMessageKeys.NOT_SCHEMA_VALID, errHandler.getErrorCount(), - errHandler.toString())); - } + /** + * Asserts that an XML resource is schema-valid. + * @param validator The Validator to use. + * @param source The XML Source to be validated. + */ + public static void assertSchemaValid(Validator validator, Source source) { + ValidationErrorHandler errHandler = new ValidationErrorHandler(); + validator.setErrorHandler(errHandler); + try { + validator.validate(source); + } + catch (SAXException | IOException e) { + throw new AssertionError(ErrorMessage.format(ErrorMessageKeys.XML_ERROR, e.getMessage())); + } + Assert.assertFalse(errHandler.errorsDetected(), ErrorMessage.format(ErrorMessageKeys.NOT_SCHEMA_VALID, + errHandler.getErrorCount(), errHandler.toString())); + } - /** - * Asserts that an XML resource satisfies all applicable constraints - * specified in a Schematron (ISO 19757-3) schema. The "xslt2" query - * language binding is supported. All patterns are checked. - * - * @param schemaRef A URL that denotes the location of a Schematron schema. - * @param xmlSource The XML Source to be validated. - */ - public static void assertSchematronValid(URL schemaRef, Source xmlSource) { - SchematronValidator validator; - try { - validator = new SchematronValidator(new StreamSource( - schemaRef.toString()), "#ALL"); - } catch (Exception e) { - StringBuilder msg = new StringBuilder( - "Failed to process Schematron schema at "); - msg.append(schemaRef).append('\n'); - msg.append(e.getMessage()); - throw new AssertionError(msg); - } - DOMResult result = (DOMResult) validator.validate(xmlSource); - Assert.assertFalse(validator.ruleViolationsDetected(), ErrorMessage - .format(ErrorMessageKeys.NOT_SCHEMA_VALID, - validator.getRuleViolationCount(), - XMLUtils.writeNodeToString(result.getNode()))); - } + /** + * Asserts that an XML resource satisfies all applicable constraints specified in a + * Schematron (ISO 19757-3) schema. The "xslt2" query language binding is supported. + * All patterns are checked. + * @param schemaRef A URL that denotes the location of a Schematron schema. + * @param xmlSource The XML Source to be validated. + */ + public static void assertSchematronValid(URL schemaRef, Source xmlSource) { + SchematronValidator validator; + try { + validator = new SchematronValidator(new StreamSource(schemaRef.toString()), "#ALL"); + } + catch (Exception e) { + StringBuilder msg = new StringBuilder("Failed to process Schematron schema at "); + msg.append(schemaRef).append('\n'); + msg.append(e.getMessage()); + throw new AssertionError(msg); + } + DOMResult result = (DOMResult) validator.validate(xmlSource); + Assert.assertFalse(validator.ruleViolationsDetected(), ErrorMessage.format(ErrorMessageKeys.NOT_SCHEMA_VALID, + validator.getRuleViolationCount(), XMLUtils.writeNodeToString(result.getNode()))); + } - /** - * Asserts that the given XML entity contains the expected number of - * descendant elements having the specified name. - * - * @param xmlEntity A Document representing an XML entity. - * @param elementName The qualified name of the element. - * @param expectedCount The expected number of occurrences. - */ - public static void assertDescendantElementCount(Document xmlEntity, - QName elementName, int expectedCount) { - NodeList features = xmlEntity.getElementsByTagNameNS( - elementName.getNamespaceURI(), elementName.getLocalPart()); - Assert.assertEquals(features.getLength(), expectedCount, String.format( - "Unexpected number of %s descendant elements.", elementName)); - } + /** + * Asserts that the given XML entity contains the expected number of descendant + * elements having the specified name. + * @param xmlEntity A Document representing an XML entity. + * @param elementName The qualified name of the element. + * @param expectedCount The expected number of occurrences. + */ + public static void assertDescendantElementCount(Document xmlEntity, QName elementName, int expectedCount) { + NodeList features = xmlEntity.getElementsByTagNameNS(elementName.getNamespaceURI(), elementName.getLocalPart()); + Assert.assertEquals(features.getLength(), expectedCount, + String.format("Unexpected number of %s descendant elements.", elementName)); + } - /** - * Asserts that the given response message contains an OGC exception report. - * The message body must contain an XML document that has a document element - * with the following properties: - * - *
    - *
  • [local name] = "ExceptionReport"
  • - *
  • [namespace name] = "http://www.opengis.net/ows/2.0"
  • - *
- * - * @param rsp A ClientResponse object representing an HTTP response message. - * @param exceptionCode The expected OGC exception code. - * @param locator A case-insensitive string value expected to occur in the - * locator attribute (e.g. a parameter name); the attribute value will be - * ignored if the argument is null or empty. - */ - public static void assertExceptionReport(Response rsp, - String exceptionCode, String locator) { - Assert.assertEquals(rsp.getStatus(), - Response.Status.BAD_REQUEST.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - Document doc = null; - try { - doc = rsp.readEntity(Document.class); - } catch (RuntimeException ex) { - StringBuilder msg = new StringBuilder(); - msg.append("Failed to parse response entity. "); - msg.append(ex.getMessage()).append('\n'); - byte[] body = rsp.readEntity(byte[].class); - msg.append(new String(body, StandardCharsets.US_ASCII)); - throw new AssertionError(msg); - } - String expr = String.format("//ows:Exception[@exceptionCode = '%s']", - exceptionCode); - NodeList nodeList = null; - try { - nodeList = XMLUtils.evaluateXPath(doc, expr, null); - } catch (XPathExpressionException xpe) {// won't happen - } - Assert.assertTrue(nodeList.getLength() > 0, - "Expected exception not found in response: " + expr); - if (null != locator && !locator.isEmpty()) { - Element exception = (Element) nodeList.item(0); - String locatorValue = exception.getAttribute("locator").toLowerCase(); - Assert.assertTrue(locatorValue.contains(locator.toLowerCase()), - String.format("Expected locator attribute to contain '%s']", - locator)); - } - } + /** + * Asserts that the given response message contains an OGC exception report. The + * message body must contain an XML document that has a document element with the + * following properties: + * + *
    + *
  • [local name] = "ExceptionReport"
  • + *
  • [namespace name] = "http://www.opengis.net/ows/2.0"
  • + *
+ * @param rsp A ClientResponse object representing an HTTP response message. + * @param exceptionCode The expected OGC exception code. + * @param locator A case-insensitive string value expected to occur in the locator + * attribute (e.g. a parameter name); the attribute value will be ignored if the + * argument is null or empty. + */ + public static void assertExceptionReport(Response rsp, String exceptionCode, String locator) { + Assert.assertEquals(rsp.getStatus(), Response.Status.BAD_REQUEST.getStatusCode(), + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + Document doc = null; + try { + doc = rsp.readEntity(Document.class); + } + catch (RuntimeException ex) { + StringBuilder msg = new StringBuilder(); + msg.append("Failed to parse response entity. "); + msg.append(ex.getMessage()).append('\n'); + byte[] body = rsp.readEntity(byte[].class); + msg.append(new String(body, StandardCharsets.US_ASCII)); + throw new AssertionError(msg); + } + String expr = String.format("//ows:Exception[@exceptionCode = '%s']", exceptionCode); + NodeList nodeList = null; + try { + nodeList = XMLUtils.evaluateXPath(doc, expr, null); + } + catch (XPathExpressionException xpe) {// won't happen + } + Assert.assertTrue(nodeList.getLength() > 0, "Expected exception not found in response: " + expr); + if (null != locator && !locator.isEmpty()) { + Element exception = (Element) nodeList.item(0); + String locatorValue = exception.getAttribute("locator").toLowerCase(); + Assert.assertTrue(locatorValue.contains(locator.toLowerCase()), + String.format("Expected locator attribute to contain '%s']", locator)); + } + } - /** - * Asserts that all bounding boxes appearing in the search results intersect - * the given envelope. The following bounding box representations are - * recognized: - *
    - *
  • ows:BoundingBox
  • - *
  • ows:WGS84BoundingBox
  • - *
  • georss:box (EPSG 4326)
  • - *
  • georss:where/{http://www.opengis.net/gml}Envelope
  • - *
- * - * @param bbox An envelope specifying a spatial extent in some CRS. - * @param results A Source object for reading the query results (the - * document element is typically csw:GetRecordsResponse or atom:feed). - */ - public static void assertEnvelopeIntersectsBoundingBoxes(final Envelope bbox, - final Source results) { - NodeList boxNodeList = null; - Map nsBindings = new HashMap<>(); - nsBindings.put(Namespaces.GEORSS, "georss"); - nsBindings.put(Namespaces.GML31, "gml31"); - try { - boxNodeList = (NodeList) XMLUtils.evaluateXPath(results, - "//ows:BoundingBox | //ows:WGS84BoundingBox | //georss:box | //gml31:Envelope", - nsBindings, XPathConstants.NODESET); - } catch (XPathExpressionException xpe) { // ignore--expression is ok - } - Assert.assertTrue(boxNodeList.getLength() > 0, - "No bounding box representations (ows:BoundingBox, ows:WGS84BoundingBox, georss:box, gml31:Envelope) found in results."); - for (int i = 0; i < boxNodeList.getLength(); i++) { - Node bboxNode = boxNodeList.item(i); - if (bboxNode.getNamespaceURI().equals(Namespaces.GML31)) { - bboxNode = SpatialUtils.createGML32Envelope(bboxNode); - } - try { - Envelope envelope; - if (bboxNode.getNamespaceURI().equals(Namespaces.GEORSS)) { - envelope = SpatialUtils.envelopeFromSimpleGeoRSSBox(bboxNode); - } else { - envelope = Extents.createEnvelope(bboxNode); - } - SpatialAssert.assertIntersects(bbox, envelope); - } catch (FactoryException fex) { - StringBuilder msg = new StringBuilder( - "Failed to create envelope from bounding box in result set:\n"); - msg.append(XMLUtils.writeNodeToString(bboxNode)); - throw new AssertionError(msg.toString(), fex); - } - } - } + /** + * Asserts that all bounding boxes appearing in the search results intersect the given + * envelope. The following bounding box representations are recognized: + *
    + *
  • ows:BoundingBox
  • + *
  • ows:WGS84BoundingBox
  • + *
  • georss:box (EPSG 4326)
  • + *
  • georss:where/{http://www.opengis.net/gml}Envelope
  • + *
+ * @param bbox An envelope specifying a spatial extent in some CRS. + * @param results A Source object for reading the query results (the document element + * is typically csw:GetRecordsResponse or atom:feed). + */ + public static void assertEnvelopeIntersectsBoundingBoxes(final Envelope bbox, final Source results) { + NodeList boxNodeList = null; + Map nsBindings = new HashMap<>(); + nsBindings.put(Namespaces.GEORSS, "georss"); + nsBindings.put(Namespaces.GML31, "gml31"); + try { + boxNodeList = (NodeList) XMLUtils.evaluateXPath(results, + "//ows:BoundingBox | //ows:WGS84BoundingBox | //georss:box | //gml31:Envelope", nsBindings, + XPathConstants.NODESET); + } + catch (XPathExpressionException xpe) { // ignore--expression is ok + } + Assert.assertTrue(boxNodeList.getLength() > 0, + "No bounding box representations (ows:BoundingBox, ows:WGS84BoundingBox, georss:box, gml31:Envelope) found in results."); + for (int i = 0; i < boxNodeList.getLength(); i++) { + Node bboxNode = boxNodeList.item(i); + if (bboxNode.getNamespaceURI().equals(Namespaces.GML31)) { + bboxNode = SpatialUtils.createGML32Envelope(bboxNode); + } + try { + Envelope envelope; + if (bboxNode.getNamespaceURI().equals(Namespaces.GEORSS)) { + envelope = SpatialUtils.envelopeFromSimpleGeoRSSBox(bboxNode); + } + else { + envelope = Extents.createEnvelope(bboxNode); + } + SpatialAssert.assertIntersects(bbox, envelope); + } + catch (FactoryException fex) { + StringBuilder msg = new StringBuilder("Failed to create envelope from bounding box in result set:\n"); + msg.append(XMLUtils.writeNodeToString(bboxNode)); + throw new AssertionError(msg.toString(), fex); + } + } + } - /** - * Asserts that the given response entity includes no search results. - * - * @param entity A Document representing a search response (atom:feed, - * csw:GetRecordsResponse, or rss). - */ - public static void assertEmptyResultSet(Document entity) { - String xpath; - Element docElem = entity.getDocumentElement(); - switch (docElem.getLocalName()) { - case "feed": - xpath = "os:totalResults = 0 and count(atom:entry) = 0"; - break; - case "GetRecordsResponse": - xpath = "csw:SearchResults/@numberOfRecordsMatched = 0 and count(csw:SearchResults/*) = 0"; - break; - case "rss": - xpath = "channel/os:totalResults = 0 and count(channel/item) = 0"; - break; - default: - throw new AssertionError("Unknown content: " + docElem.getNodeName()); - } - Map nsBindings = new HashMap<>(); - nsBindings.put(Namespaces.ATOM, "atom"); - nsBindings.put(Namespaces.OSD11, "os"); - assertXPath(xpath, docElem, nsBindings); - } + /** + * Asserts that the given response entity includes no search results. + * @param entity A Document representing a search response (atom:feed, + * csw:GetRecordsResponse, or rss). + */ + public static void assertEmptyResultSet(Document entity) { + String xpath; + Element docElem = entity.getDocumentElement(); + switch (docElem.getLocalName()) { + case "feed": + xpath = "os:totalResults = 0 and count(atom:entry) = 0"; + break; + case "GetRecordsResponse": + xpath = "csw:SearchResults/@numberOfRecordsMatched = 0 and count(csw:SearchResults/*) = 0"; + break; + case "rss": + xpath = "channel/os:totalResults = 0 and count(channel/item) = 0"; + break; + default: + throw new AssertionError("Unknown content: " + docElem.getNodeName()); + } + Map nsBindings = new HashMap<>(); + nsBindings.put(Namespaces.ATOM, "atom"); + nsBindings.put(Namespaces.OSD11, "os"); + assertXPath(xpath, docElem, nsBindings); + } + + /** + * Asserts that the given search terms all occur in the content of each record in the + * given collection. The context includes the text content of all child elements and + * their attributes. The comparison is not case-sensitive. + * @param recordList A list of nodes representing catalog records (csw:Record or + * atom:entry). + * @param searchTerms A list of search terms. + */ + public static void assertAllTermsOccur(NodeList recordList, String... searchTerms) { + for (String term : searchTerms) { + for (int i = 0; i < recordList.getLength(); i++) { + Element record = (Element) recordList.item(i); + String expr = String.format("child::*[(text() | attribute::*)[matches(., '%s', 'i')]]", term); + try { + XdmValue result = XMLUtils.evaluateXPath2(new DOMSource(record), expr, null); + Assert.assertTrue(result.size() > 0, + ErrorMessage.format(ErrorMessageKeys.XPATH_RESULT, Records.getRecordId(record), + // search term may contain non-ASCII char + expr.replace(term, URIUtils.getPercentEncodedString(term)))); + LOGR.log(Level.FINE, "In {0} found {1} matching fields for ''{2}'':\n{3}", new Object[] { + Records.getRecordId(record), result.size(), term, XMLUtils.writeXdmValueToString(result) }); + } + catch (SaxonApiException sae) { + throw new AssertionError(String.format("Failed to evaluate XPath expression: %s \nReason: %s", expr, + sae.getMessage())); + } + } + } + } - /** - * Asserts that the given search terms all occur in the content of each - * record in the given collection. The context includes the text content of - * all child elements and their attributes. The comparison is not - * case-sensitive. - * - * @param recordList A list of nodes representing catalog records - * (csw:Record or atom:entry). - * @param searchTerms A list of search terms. - */ - public static void assertAllTermsOccur(NodeList recordList, String... searchTerms) { - for (String term : searchTerms) { - for (int i = 0; i < recordList.getLength(); i++) { - Element record = (Element) recordList.item(i); - String expr = String.format( - "child::*[(text() | attribute::*)[matches(., '%s', 'i')]]", - term); - try { - XdmValue result = XMLUtils.evaluateXPath2( - new DOMSource(record), expr, null); - Assert.assertTrue(result.size() > 0, - ErrorMessage.format(ErrorMessageKeys.XPATH_RESULT, - Records.getRecordId(record), - // search term may contain non-ASCII char - expr.replace(term, URIUtils.getPercentEncodedString(term)))); - LOGR.log(Level.FINE, "In {0} found {1} matching fields for ''{2}'':\n{3}", - new Object[]{Records.getRecordId(record), result.size(), - term, XMLUtils.writeXdmValueToString(result)}); - } catch (SaxonApiException sae) { - throw new AssertionError(String.format( - "Failed to evaluate XPath expression: %s \nReason: %s", - expr, - sae.getMessage())); - } - } - } - } } diff --git a/src/main/java/org/opengis/cite/cat30/ErrorMessage.java b/src/main/java/org/opengis/cite/cat30/ErrorMessage.java index 76b1ca0..f565955 100644 --- a/src/main/java/org/opengis/cite/cat30/ErrorMessage.java +++ b/src/main/java/org/opengis/cite/cat30/ErrorMessage.java @@ -4,46 +4,39 @@ import java.util.ResourceBundle; /** - * Utility class for retrieving and formatting localized error messages that - * describe failed assertions. + * Utility class for retrieving and formatting localized error messages that describe + * failed assertions. */ public class ErrorMessage { - private static final String BASE_NAME = - "org.opengis.cite.cat30.MessageBundle"; - private static ResourceBundle msgResources = - ResourceBundle.getBundle(BASE_NAME); + private static final String BASE_NAME = "org.opengis.cite.cat30.MessageBundle"; - /** - * Produces a formatted error message using the supplied substitution - * arguments and the current locale. The arguments should reflect the order - * of the placeholders in the message template. - * - * @param msgKey - * The key identifying the message template; it should be a - * member of {@code ErrorMessageKeys}. - * @param args - * An array of arguments to be formatted and substituted in the - * content of the message. - * @return A String containing the message content. If no message is found - * for the given key, a {@link java.util.MissingResourceException} - * is thrown. - */ - public static String format(String msgKey, Object... args) { - return MessageFormat.format(msgResources.getString(msgKey), args); - } + private static ResourceBundle msgResources = ResourceBundle.getBundle(BASE_NAME); + + /** + * Produces a formatted error message using the supplied substitution arguments and + * the current locale. The arguments should reflect the order of the placeholders in + * the message template. + * @param msgKey The key identifying the message template; it should be a member of + * {@code ErrorMessageKeys}. + * @param args An array of arguments to be formatted and substituted in the content of + * the message. + * @return A String containing the message content. If no message is found for the + * given key, a {@link java.util.MissingResourceException} is thrown. + */ + public static String format(String msgKey, Object... args) { + return MessageFormat.format(msgResources.getString(msgKey), args); + } + + /** + * Retrieves a simple message according to the current locale. + * @param msgKey The key identifying the message; it should be a member of + * {@code ErrorMessageKeys}. + * @return A String containing the message content. If no message is found for the + * given key, a {@link java.util.MissingResourceException} is thrown. + */ + public static String get(String msgKey) { + return msgResources.getString(msgKey); + } - /** - * Retrieves a simple message according to the current locale. - * - * @param msgKey - * The key identifying the message; it should be a member of - * {@code ErrorMessageKeys}. - * @return A String containing the message content. If no message is found - * for the given key, a {@link java.util.MissingResourceException} - * is thrown. - */ - public static String get(String msgKey) { - return msgResources.getString(msgKey); - } } diff --git a/src/main/java/org/opengis/cite/cat30/ErrorMessageKeys.java b/src/main/java/org/opengis/cite/cat30/ErrorMessageKeys.java index 61f5f5d..946bb0c 100644 --- a/src/main/java/org/opengis/cite/cat30/ErrorMessageKeys.java +++ b/src/main/java/org/opengis/cite/cat30/ErrorMessageKeys.java @@ -1,30 +1,49 @@ package org.opengis.cite.cat30; /** - * Defines keys used to access localized messages for assertion errors. The - * messages are stored in Properties files that are encoded in ISO-8859-1 - * (Latin-1). For some languages the {@code native2ascii} tool must be used to - * process the files and produce escaped Unicode characters. + * Defines keys used to access localized messages for assertion errors. The messages are + * stored in Properties files that are encoded in ISO-8859-1 (Latin-1). For some languages + * the {@code native2ascii} tool must be used to process the files and produce escaped + * Unicode characters. */ public class ErrorMessageKeys { - public static final String NOT_SCHEMA_VALID = "NotSchemaValid"; - public static final String EMPTY_STRING = "EmptyString"; - public static final String XPATH_RESULT = "XPathResult"; - public static final String NAMESPACE_NAME = "NamespaceName"; - public static final String LOCAL_NAME = "LocalName"; - public static final String XML_ERROR = "XMLError"; - public static final String XPATH_ERROR = "XPathError"; - public static final String MISSING_INFOSET_ITEM = "MissingInfosetItem"; - public static final String UNEXPECTED_STATUS = "UnexpectedStatus"; - public static final String UNEXPECTED_MEDIA_TYPE = "UnexpectedMediaType"; - public static final String MISSING_ENTITY = "MissingEntity"; - public static final String EMPTY_RESULT_SET = "EmptyResultSet"; - public static final String RESULT_SET_SIZE = "ResultSetSize"; - public static final String ID_NOT_FOUND = "IdNotFound"; - public static final String NOT_XML = "NotXML"; - public static final String OPENSEARCH_UNAVAIL = "OpenSearchUnavailable"; - public static final String NAMED_ITEM_NOT_FOUND = "NamedItemNotFound"; - public static final String INFOSET_ITEM_VALUE = "InfosetItemValue"; - public static final String CONSTRAINT_VIOLATION = "ConstraintViolation"; + public static final String NOT_SCHEMA_VALID = "NotSchemaValid"; + + public static final String EMPTY_STRING = "EmptyString"; + + public static final String XPATH_RESULT = "XPathResult"; + + public static final String NAMESPACE_NAME = "NamespaceName"; + + public static final String LOCAL_NAME = "LocalName"; + + public static final String XML_ERROR = "XMLError"; + + public static final String XPATH_ERROR = "XPathError"; + + public static final String MISSING_INFOSET_ITEM = "MissingInfosetItem"; + + public static final String UNEXPECTED_STATUS = "UnexpectedStatus"; + + public static final String UNEXPECTED_MEDIA_TYPE = "UnexpectedMediaType"; + + public static final String MISSING_ENTITY = "MissingEntity"; + + public static final String EMPTY_RESULT_SET = "EmptyResultSet"; + + public static final String RESULT_SET_SIZE = "ResultSetSize"; + + public static final String ID_NOT_FOUND = "IdNotFound"; + + public static final String NOT_XML = "NotXML"; + + public static final String OPENSEARCH_UNAVAIL = "OpenSearchUnavailable"; + + public static final String NAMED_ITEM_NOT_FOUND = "NamedItemNotFound"; + + public static final String INFOSET_ITEM_VALUE = "InfosetItemValue"; + + public static final String CONSTRAINT_VIOLATION = "ConstraintViolation"; + } diff --git a/src/main/java/org/opengis/cite/cat30/Namespaces.java b/src/main/java/org/opengis/cite/cat30/Namespaces.java index 37c0ea9..450f978 100644 --- a/src/main/java/org/opengis/cite/cat30/Namespaces.java +++ b/src/main/java/org/opengis/cite/cat30/Namespaces.java @@ -10,74 +10,87 @@ */ public class Namespaces { - private Namespaces() { - } - - /** - * SOAP 1.2 message envelopes. - */ - public static final String SOAP_ENV = "http://www.w3.org/2003/05/soap-envelope"; - /** - * W3C XLink - */ - public static final String XLINK = "http://www.w3.org/1999/xlink"; - /** - * OGC 06-121r9 (OWS 2.0) - */ - public static final String OWS = "http://www.opengis.net/ows/2.0"; - /** - * OGC 06-121r3 (OWS 1.1) - */ - public static final String OWS11 = "http://www.opengis.net/ows/1.1"; - /** - * GML 3.2 (ISO 19136) - */ - public static final String GML = "http://www.opengis.net/gml/3.2"; - /** - * GML 3.1 (03-105r1) - */ - public static final String GML31 = "http://www.opengis.net/gml"; - /** - * ISO 19143:2010 (FES 2.0) - */ - public static final String FES = "http://www.opengis.net/fes/2.0"; - /** - * OGC 12-176r4 (CSW 3.0) - */ - public static final String CSW = "http://www.opengis.net/cat/csw/3.0"; - /** - * DCMI Metadata Terms - */ - public static final String DCMI = "http://purl.org/dc/terms/"; - /** - * Dublin Core Metadata Element Set, Version 1.1 (legacy) - */ - public static final String DCMES = "http://purl.org/dc/elements/1.1/"; - /** - * Atom Syndication Format (RFC 4287) - */ - public static final String ATOM = "http://www.w3.org/2005/Atom"; - /** - * OpenSearch 1.1 description - */ - public static final String OSD11 = "http://a9.com/-/spec/opensearch/1.1/"; - /** - * OpenSearch Geo extension namespace (see OGC 10-032r8). - */ - public static final String OS_GEO = "http://a9.com/-/opensearch/extensions/geo/1.0/"; - /** - * GeoRSS namespace. - */ - public static final String GEORSS = "http://www.georss.org/georss/10"; - /** - * W3C XML Schema namespace - */ - public static final URI XSD = URI - .create("http://www.w3.org/2001/XMLSchema"); - /** - * Schematron (ISO 19757-3) namespace - */ - public static final URI SCH = URI - .create("http://purl.oclc.org/dsdl/schematron"); + private Namespaces() { + } + + /** + * SOAP 1.2 message envelopes. + */ + public static final String SOAP_ENV = "http://www.w3.org/2003/05/soap-envelope"; + + /** + * W3C XLink + */ + public static final String XLINK = "http://www.w3.org/1999/xlink"; + + /** + * OGC 06-121r9 (OWS 2.0) + */ + public static final String OWS = "http://www.opengis.net/ows/2.0"; + + /** + * OGC 06-121r3 (OWS 1.1) + */ + public static final String OWS11 = "http://www.opengis.net/ows/1.1"; + + /** + * GML 3.2 (ISO 19136) + */ + public static final String GML = "http://www.opengis.net/gml/3.2"; + + /** + * GML 3.1 (03-105r1) + */ + public static final String GML31 = "http://www.opengis.net/gml"; + + /** + * ISO 19143:2010 (FES 2.0) + */ + public static final String FES = "http://www.opengis.net/fes/2.0"; + + /** + * OGC 12-176r4 (CSW 3.0) + */ + public static final String CSW = "http://www.opengis.net/cat/csw/3.0"; + + /** + * DCMI Metadata Terms + */ + public static final String DCMI = "http://purl.org/dc/terms/"; + + /** + * Dublin Core Metadata Element Set, Version 1.1 (legacy) + */ + public static final String DCMES = "http://purl.org/dc/elements/1.1/"; + + /** + * Atom Syndication Format (RFC 4287) + */ + public static final String ATOM = "http://www.w3.org/2005/Atom"; + + /** + * OpenSearch 1.1 description + */ + public static final String OSD11 = "http://a9.com/-/spec/opensearch/1.1/"; + + /** + * OpenSearch Geo extension namespace (see OGC 10-032r8). + */ + public static final String OS_GEO = "http://a9.com/-/opensearch/extensions/geo/1.0/"; + + /** + * GeoRSS namespace. + */ + public static final String GEORSS = "http://www.georss.org/georss/10"; + + /** + * W3C XML Schema namespace + */ + public static final URI XSD = URI.create("http://www.w3.org/2001/XMLSchema"); + + /** + * Schematron (ISO 19757-3) namespace + */ + public static final URI SCH = URI.create("http://purl.oclc.org/dsdl/schematron"); } diff --git a/src/main/java/org/opengis/cite/cat30/ReusableEntityFilter.java b/src/main/java/org/opengis/cite/cat30/ReusableEntityFilter.java index 457dd39..f72c603 100644 --- a/src/main/java/org/opengis/cite/cat30/ReusableEntityFilter.java +++ b/src/main/java/org/opengis/cite/cat30/ReusableEntityFilter.java @@ -12,17 +12,16 @@ * Buffers the (response) entity so it can be read multiple times. * *

- * WARNING: The entity InputStream must be reset after each - * read attempt.

+ * WARNING: The entity InputStream must be reset after each read attempt. + *

*/ public class ReusableEntityFilter implements ClientResponseFilter { - @Override - public void filter(ClientRequestContext requestContext, - ClientResponseContext responseContext) throws IOException { - if(responseContext instanceof ClientResponse) { - ((ClientResponse)responseContext).bufferEntity(); - } - } + @Override + public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext) throws IOException { + if (responseContext instanceof ClientResponse) { + ((ClientResponse) responseContext).bufferEntity(); + } + } } diff --git a/src/main/java/org/opengis/cite/cat30/SuiteAttribute.java b/src/main/java/org/opengis/cite/cat30/SuiteAttribute.java index 36488eb..f760725 100644 --- a/src/main/java/org/opengis/cite/cat30/SuiteAttribute.java +++ b/src/main/java/org/opengis/cite/cat30/SuiteAttribute.java @@ -10,61 +10,63 @@ import jakarta.ws.rs.client.Client; /** - * An enumerated type defining ISuite attributes that may be set to constitute a - * shared test fixture. + * An enumerated type defining ISuite attributes that may be set to constitute a shared + * test fixture. */ @SuppressWarnings("rawtypes") public enum SuiteAttribute { - /** - * A client component for interacting with HTTP endpoints. - */ - CLIENT("httpClient", Client.class), - /** - * An immutable Schema object representing the complete CSW 3.0 schema. - */ - CSW_SCHEMA("cswSchema", Schema.class), - /** - * An immutable Schema object for Atom (RFC 4287). - */ - ATOM_SCHEMA("atomSchema", Schema.class), - /** - * Sample data obtained from the IUT. - */ - DATASET("dataset", DatasetInfo.class), - /** - * A DOM Document representing the test subject or a description of it. - */ - TEST_SUBJECT("testSubject", Document.class), - /** - * A File containing the test subject or a description of it. - */ - TEST_SUBJ_FILE("testSubjectFile", File.class), - /** - * A DOM Document representing an OpenSearch 1.1 description. - */ - OPENSEARCH_DESCR("openSearchDescr", Document.class); + /** + * A client component for interacting with HTTP endpoints. + */ + CLIENT("httpClient", Client.class), + /** + * An immutable Schema object representing the complete CSW 3.0 schema. + */ + CSW_SCHEMA("cswSchema", Schema.class), + /** + * An immutable Schema object for Atom (RFC 4287). + */ + ATOM_SCHEMA("atomSchema", Schema.class), + /** + * Sample data obtained from the IUT. + */ + DATASET("dataset", DatasetInfo.class), + /** + * A DOM Document representing the test subject or a description of it. + */ + TEST_SUBJECT("testSubject", Document.class), + /** + * A File containing the test subject or a description of it. + */ + TEST_SUBJ_FILE("testSubjectFile", File.class), + /** + * A DOM Document representing an OpenSearch 1.1 description. + */ + OPENSEARCH_DESCR("openSearchDescr", Document.class); - private final Class attrType; - private final String attrName; + private final Class attrType; - private SuiteAttribute(String attrName, Class attrType) { - this.attrName = attrName; - this.attrType = attrType; - } + private final String attrName; - public Class getType() { - return attrType; - } + private SuiteAttribute(String attrName, Class attrType) { + this.attrName = attrName; + this.attrType = attrType; + } - public String getName() { - return attrName; - } + public Class getType() { + return attrType; + } + + public String getName() { + return attrName; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(attrName); + sb.append('(').append(attrType.getName()).append(')'); + return sb.toString(); + } - @Override - public String toString() { - StringBuilder sb = new StringBuilder(attrName); - sb.append('(').append(attrType.getName()).append(')'); - return sb.toString(); - } } diff --git a/src/main/java/org/opengis/cite/cat30/SuiteFixtureListener.java b/src/main/java/org/opengis/cite/cat30/SuiteFixtureListener.java index 0cc3e35..9f21939 100644 --- a/src/main/java/org/opengis/cite/cat30/SuiteFixtureListener.java +++ b/src/main/java/org/opengis/cite/cat30/SuiteFixtureListener.java @@ -22,159 +22,143 @@ import jakarta.ws.rs.client.Client; /** - * A listener that performs various tasks before and after a test suite is run, - * usually concerned with maintaining a shared test suite fixture. Since this - * listener is loaded using the ServiceLoader mechanism, its methods will be - * called before those of other suite listeners listed in the test suite - * definition and before any annotated configuration methods. + * A listener that performs various tasks before and after a test suite is run, usually + * concerned with maintaining a shared test suite fixture. Since this listener is loaded + * using the ServiceLoader mechanism, its methods will be called before those of other + * suite listeners listed in the test suite definition and before any annotated + * configuration methods. * - * Attributes set on an ISuite instance are not inherited by constituent test - * group contexts (ITestContext). However, suite attributes are still accessible - * from lower contexts. + * Attributes set on an ISuite instance are not inherited by constituent test group + * contexts (ITestContext). However, suite attributes are still accessible from lower + * contexts. * * @see org.testng.ISuite ISuite interface */ public class SuiteFixtureListener implements ISuiteListener { - @Override - public void onStart(ISuite suite) { - processSuiteParameters(suite); - registerSchemas(suite); - registerClientComponent(suite); - } + @Override + public void onStart(ISuite suite) { + processSuiteParameters(suite); + registerSchemas(suite); + registerClientComponent(suite); + } - /** - * Performs various cleanup tasks when the test run is completed. Any - * temporary files created during the test run are deleted if - * TestSuiteLogger is enabled at the INFO level or higher. - * - * @param suite The test suite. - */ - @Override - public void onFinish(ISuite suite) { - deleteTempFiles(suite); - } + /** + * Performs various cleanup tasks when the test run is completed. Any temporary files + * created during the test run are deleted if TestSuiteLogger is enabled at the INFO + * level or higher. + * @param suite The test suite. + */ + @Override + public void onFinish(ISuite suite) { + deleteTempFiles(suite); + } - /** - * Processes test suite arguments and sets suite attributes accordingly. The - * entity referenced by the {@link TestRunArg#IUT iut} argument--expected to - * be an OGC service capabilities document--is parsed and the resulting - * Document is set as the value of the - * {@link SuiteAttribute#TEST_SUBJECT testSubject} attribute. - * - * @param suite An ISuite object representing a TestNG test suite. - */ - void processSuiteParameters(ISuite suite) { - Map params = suite.getXmlSuite().getParameters(); - TestSuiteLogger.log(Level.CONFIG, - "Suite parameters\n" + params.toString()); - String iutParam = params.get(TestRunArg.IUT.toString()); - if ((null == iutParam) || iutParam.isEmpty()) { - throw new IllegalArgumentException( - "Required test run parameter not found: " - + TestRunArg.IUT.toString()); - } - URI iutRef = URI.create(iutParam.trim()); - File entityFile = null; - try { - entityFile = URIUtils.dereferenceURI(iutRef); - } catch (IOException iox) { - throw new RuntimeException("Failed to dereference resource located at " - + iutRef, iox); - } - suite.setAttribute(SuiteAttribute.TEST_SUBJ_FILE.getName(), entityFile); - Document iutDoc = null; - try { - iutDoc = URIUtils.parseURI(entityFile.toURI()); - } catch (SAXException | IOException x) { - throw new RuntimeException("Failed to parse resource retrieved from " - + iutRef, x); - } - suite.setAttribute(SuiteAttribute.TEST_SUBJECT.getName(), iutDoc); - if (TestSuiteLogger.isLoggable(Level.FINE)) { - StringBuilder logMsg = new StringBuilder( - "Parsed resource retrieved from "); - logMsg.append(iutRef).append("\n"); - logMsg.append(XMLUtils.writeNodeToString(iutDoc)); - TestSuiteLogger.log(Level.FINE, logMsg.toString()); - } - } + /** + * Processes test suite arguments and sets suite attributes accordingly. The entity + * referenced by the {@link TestRunArg#IUT iut} argument--expected to be an OGC + * service capabilities document--is parsed and the resulting Document is set as the + * value of the {@link SuiteAttribute#TEST_SUBJECT testSubject} attribute. + * @param suite An ISuite object representing a TestNG test suite. + */ + void processSuiteParameters(ISuite suite) { + Map params = suite.getXmlSuite().getParameters(); + TestSuiteLogger.log(Level.CONFIG, "Suite parameters\n" + params.toString()); + String iutParam = params.get(TestRunArg.IUT.toString()); + if ((null == iutParam) || iutParam.isEmpty()) { + throw new IllegalArgumentException("Required test run parameter not found: " + TestRunArg.IUT.toString()); + } + URI iutRef = URI.create(iutParam.trim()); + File entityFile = null; + try { + entityFile = URIUtils.dereferenceURI(iutRef); + } + catch (IOException iox) { + throw new RuntimeException("Failed to dereference resource located at " + iutRef, iox); + } + suite.setAttribute(SuiteAttribute.TEST_SUBJ_FILE.getName(), entityFile); + Document iutDoc = null; + try { + iutDoc = URIUtils.parseURI(entityFile.toURI()); + } + catch (SAXException | IOException x) { + throw new RuntimeException("Failed to parse resource retrieved from " + iutRef, x); + } + suite.setAttribute(SuiteAttribute.TEST_SUBJECT.getName(), iutDoc); + if (TestSuiteLogger.isLoggable(Level.FINE)) { + StringBuilder logMsg = new StringBuilder("Parsed resource retrieved from "); + logMsg.append(iutRef).append("\n"); + logMsg.append(XMLUtils.writeNodeToString(iutDoc)); + TestSuiteLogger.log(Level.FINE, logMsg.toString()); + } + } - /** - * A client component is added to the suite fixture as the value of the - * {@link SuiteAttribute#CLIENT} attribute; it may be subsequently accessed - * via the {@link org.testng.ITestContext#getSuite()} method. - * - * @param suite The test suite instance. - */ - void registerClientComponent(ISuite suite) { - Client client = ClientUtils.buildClient(); - if (null != client) { - suite.setAttribute(SuiteAttribute.CLIENT.getName(), client); - } - } + /** + * A client component is added to the suite fixture as the value of the + * {@link SuiteAttribute#CLIENT} attribute; it may be subsequently accessed via the + * {@link org.testng.ITestContext#getSuite()} method. + * @param suite The test suite instance. + */ + void registerClientComponent(ISuite suite) { + Client client = ClientUtils.buildClient(); + if (null != client) { + suite.setAttribute(SuiteAttribute.CLIENT.getName(), client); + } + } - /** - * Builds immutable {@link Schema Schema} objects suitable for validating - * the content of CSW 3.0 response entities. The schemas are added to the - * suite fixture as the value of the attributes identified in the following - * table. - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
Application schemas
SuiteAttributeSchema
SuiteAttribute.CSW_SCHEMAOGC 12-176r6, 7.9(a): cswAll.xsd
SuiteAttribute.ATOM_SCHEMARFC 4287, Appendix B: RELAX NG Compact Schema
- * - * @param suite The test suite to be run. - */ - void registerSchemas(ISuite suite) { - Schema csw3Schema = ValidationUtils.createCSWSchema(); - if (null != csw3Schema) { - suite.setAttribute(SuiteAttribute.CSW_SCHEMA.getName(), csw3Schema); - } - Schema atomSchema = ValidationUtils.createAtomSchema(); - if (null != atomSchema) { - suite.setAttribute(SuiteAttribute.ATOM_SCHEMA.getName(), atomSchema); - } - } + /** + * Builds immutable {@link Schema Schema} objects suitable for validating the content + * of CSW 3.0 response entities. The schemas are added to the suite fixture as the + * value of the attributes identified in the following table. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Application schemas
SuiteAttributeSchema
SuiteAttribute.CSW_SCHEMAOGC 12-176r6, 7.9(a): cswAll.xsd
SuiteAttribute.ATOM_SCHEMARFC 4287, Appendix B: RELAX NG Compact Schema
+ * @param suite The test suite to be run. + */ + void registerSchemas(ISuite suite) { + Schema csw3Schema = ValidationUtils.createCSWSchema(); + if (null != csw3Schema) { + suite.setAttribute(SuiteAttribute.CSW_SCHEMA.getName(), csw3Schema); + } + Schema atomSchema = ValidationUtils.createAtomSchema(); + if (null != atomSchema) { + suite.setAttribute(SuiteAttribute.ATOM_SCHEMA.getName(), atomSchema); + } + } - /** - * Deletes temporary files created during the test run if TestSuiteLogger is - * enabled at the INFO level or higher (they are left intact at the CONFIG - * level or lower). - * - * @param suite The test suite. - */ - void deleteTempFiles(ISuite suite) { - if (TestSuiteLogger.isLoggable(Level.CONFIG)) { - return; - } - File testSubjFile = (File) suite.getAttribute( - SuiteAttribute.TEST_SUBJ_FILE.getName()); - if (testSubjFile.exists()) { - testSubjFile.delete(); - } - DatasetInfo dataset = (DatasetInfo) suite.getAttribute( - SuiteAttribute.DATASET.getName()); - if (null != dataset) { - File dataFile = dataset.getDataFile(); - dataFile.delete(); - } - } + /** + * Deletes temporary files created during the test run if TestSuiteLogger is enabled + * at the INFO level or higher (they are left intact at the CONFIG level or lower). + * @param suite The test suite. + */ + void deleteTempFiles(ISuite suite) { + if (TestSuiteLogger.isLoggable(Level.CONFIG)) { + return; + } + File testSubjFile = (File) suite.getAttribute(SuiteAttribute.TEST_SUBJ_FILE.getName()); + if (testSubjFile.exists()) { + testSubjFile.delete(); + } + DatasetInfo dataset = (DatasetInfo) suite.getAttribute(SuiteAttribute.DATASET.getName()); + if (null != dataset) { + File dataFile = dataset.getDataFile(); + dataFile.delete(); + } + } } diff --git a/src/main/java/org/opengis/cite/cat30/TestFailureListener.java b/src/main/java/org/opengis/cite/cat30/TestFailureListener.java index a0caafc..ad8a3b3 100644 --- a/src/main/java/org/opengis/cite/cat30/TestFailureListener.java +++ b/src/main/java/org/opengis/cite/cat30/TestFailureListener.java @@ -12,91 +12,89 @@ import jakarta.ws.rs.core.Response; /** - * A listener that augments a test result with diagnostic information in the - * event that a test method failed. This information will appear in the XML - * report when the test run is completed. + * A listener that augments a test result with diagnostic information in the event that a + * test method failed. This information will appear in the XML report when the test run is + * completed. */ public class TestFailureListener extends TestListenerAdapter { - /** - * Sets the "request" and "response" attributes of a test result. The value - * of these attributes is a string that contains information about the - * content of an outgoing or incoming message: target resource, status code, - * headers, entity (if present). The entity is represented as a String with - * UTF-8 character encoding. - * - * @param result A description of a test result (with a fail verdict). - */ - @Override - public void onTestFailure(ITestResult result) { - super.onTestFailure(result); - Object instance = result.getInstance(); - if (CommonFixture.class.isInstance(instance)) { - CommonFixture fixture = CommonFixture.class.cast(instance); - result.setAttribute("request", getRequestMessageInfo(fixture.request)); - result.setAttribute("response", getResponseMessageInfo(fixture.response)); - } - } + /** + * Sets the "request" and "response" attributes of a test result. The value of these + * attributes is a string that contains information about the content of an outgoing + * or incoming message: target resource, status code, headers, entity (if present). + * The entity is represented as a String with UTF-8 character encoding. + * @param result A description of a test result (with a fail verdict). + */ + @Override + public void onTestFailure(ITestResult result) { + super.onTestFailure(result); + Object instance = result.getInstance(); + if (CommonFixture.class.isInstance(instance)) { + CommonFixture fixture = CommonFixture.class.cast(instance); + result.setAttribute("request", getRequestMessageInfo(fixture.request)); + result.setAttribute("response", getResponseMessageInfo(fixture.response)); + } + } - /** - * Gets diagnostic information about a request message. If the request - * contains a message body, it should be represented as a DOM Document node - * or as an object having a meaningful toString() implementation. - * - * @param req An object representing an HTTP request message. - * @return A string containing information gleaned from the request message. - */ - String getRequestMessageInfo(ClientRequest req) { - if (null == req) { - return "No request message."; - } - StringBuilder msgInfo = new StringBuilder(); - msgInfo.append("Method: ").append(req.getMethod()).append('\n'); - msgInfo.append("Target URI: ").append(req.getUri()).append('\n'); - msgInfo.append("Headers: ").append(req.getHeaders()).append('\n'); - if (null != req.getEntity()) { - Object entity = req.getEntity(); - String body; - if (Document.class.isInstance(entity)) { - Document doc = Document.class.cast(entity); - body = XMLUtils.writeNodeToString(doc); - } else { - body = entity.toString(); - } - msgInfo.append(body).append('\n'); - } - return msgInfo.toString(); - } + /** + * Gets diagnostic information about a request message. If the request contains a + * message body, it should be represented as a DOM Document node or as an object + * having a meaningful toString() implementation. + * @param req An object representing an HTTP request message. + * @return A string containing information gleaned from the request message. + */ + String getRequestMessageInfo(ClientRequest req) { + if (null == req) { + return "No request message."; + } + StringBuilder msgInfo = new StringBuilder(); + msgInfo.append("Method: ").append(req.getMethod()).append('\n'); + msgInfo.append("Target URI: ").append(req.getUri()).append('\n'); + msgInfo.append("Headers: ").append(req.getHeaders()).append('\n'); + if (null != req.getEntity()) { + Object entity = req.getEntity(); + String body; + if (Document.class.isInstance(entity)) { + Document doc = Document.class.cast(entity); + body = XMLUtils.writeNodeToString(doc); + } + else { + body = entity.toString(); + } + msgInfo.append(body).append('\n'); + } + return msgInfo.toString(); + } - /** - * Gets diagnostic information about a response message. - * - * @param rsp An object representing an HTTP response message. - * @return A string containing information gleaned from the response - * message. - */ - String getResponseMessageInfo(Response rsp) { - if (null == rsp) { - return "No response message."; - } - StringBuilder msgInfo = new StringBuilder(); - msgInfo.append("Status: ").append(rsp.getStatus()).append('\n'); - msgInfo.append("Headers: ").append(rsp.getHeaders()).append('\n'); - if (rsp.hasEntity()) { - if (XMLUtils.isXML(rsp.getMediaType())) { - Document doc = ClientUtils.getResponseEntityAsDocument(rsp, null); - if (null == doc) { - msgInfo.append("Failed to parse XML response entity."); - } else { - msgInfo.append(XMLUtils.writeNodeToString(doc)); - } - } else { - byte[] body = rsp.readEntity(byte[].class); - msgInfo.append(new String(body, StandardCharsets.US_ASCII)); - } - msgInfo.append('\n'); - } - return msgInfo.toString(); - } + /** + * Gets diagnostic information about a response message. + * @param rsp An object representing an HTTP response message. + * @return A string containing information gleaned from the response message. + */ + String getResponseMessageInfo(Response rsp) { + if (null == rsp) { + return "No response message."; + } + StringBuilder msgInfo = new StringBuilder(); + msgInfo.append("Status: ").append(rsp.getStatus()).append('\n'); + msgInfo.append("Headers: ").append(rsp.getHeaders()).append('\n'); + if (rsp.hasEntity()) { + if (XMLUtils.isXML(rsp.getMediaType())) { + Document doc = ClientUtils.getResponseEntityAsDocument(rsp, null); + if (null == doc) { + msgInfo.append("Failed to parse XML response entity."); + } + else { + msgInfo.append(XMLUtils.writeNodeToString(doc)); + } + } + else { + byte[] body = rsp.readEntity(byte[].class); + msgInfo.append(new String(body, StandardCharsets.US_ASCII)); + } + msgInfo.append('\n'); + } + return msgInfo.toString(); + } } diff --git a/src/main/java/org/opengis/cite/cat30/TestNGController.java b/src/main/java/org/opengis/cite/cat30/TestNGController.java index 42c6a2e..97f6bb2 100644 --- a/src/main/java/org/opengis/cite/cat30/TestNGController.java +++ b/src/main/java/org/opengis/cite/cat30/TestNGController.java @@ -31,135 +31,129 @@ */ public class TestNGController implements TestSuiteController { - private TestRunExecutor executor; - private Properties etsProperties = new Properties(); - - /** - * A convenience method for running the test suite using a command-line - * interface. The default values of the test run arguments are as follows: - *
    - *
  • XML properties file: ${user.home}/test-run-props.xml
  • - *
  • outputDir: ${user.home}
  • - *
- *

- * Synopsis - *

- *
-     * ets-cat30-${version}-aio.jar [-o|--outputDir $TMPDIR] [test-run-props.xml]
-     * 
- * - * @param args Test run arguments (optional). - * @throws Exception If the test run cannot be executed (usually due to - * unsatisfied pre-conditions). - */ - public static void main(String[] args) throws Exception { - TestRunArguments testRunArgs = new TestRunArguments(); - JCommander cmd = new JCommander(testRunArgs); - try { - cmd.parse(args); - } catch (ParameterException px) { - System.out.println(px.getMessage()); - cmd.usage(); - } - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - DocumentBuilder db = dbf.newDocumentBuilder(); - File xmlArgs = testRunArgs.getPropertiesFile(); - Document testRunProps = db.parse(xmlArgs); - TestNGController controller = new TestNGController(testRunArgs.getOutputDir()); - Source testResults = controller.doTestRun(testRunProps); - System.out.println("Test results: " + testResults.getSystemId()); - } - - /** - * Default constructor uses the location given by the "user.home" system - * property as the root output directory. - */ - public TestNGController() { - this(new File(System.getProperty("user.home")).toURI().toString()); - } - - /** - * Construct a controller that writes results to the given output directory. - * - * @param outputDir The location of the directory in which test results will - * be written. It will be created if it does not exist. - */ - public TestNGController(String outputDir) { - InputStream is = getClass().getResourceAsStream("ets.properties"); - try { - this.etsProperties.load(is); - } catch (IOException ex) { - TestSuiteLogger.log(Level.WARNING, - "Unable to load ets.properties. " + ex.getMessage()); - } - URL tngSuite = TestNGController.class.getResource("testng.xml"); - File resultsDir; - if (null == outputDir || outputDir.isEmpty()) { - resultsDir = new File(FilenameUtils.normalize(System.getProperty("user.home"))); - } else if (outputDir.startsWith("file:")) { - resultsDir = new File(URI.create(outputDir)); - } else { - resultsDir = new File(outputDir); - } - TestSuiteLogger.log(Level.CONFIG, "Using TestNG config: " + tngSuite); - TestSuiteLogger.log(Level.CONFIG, - "Using outputDirPath: " + resultsDir.getAbsolutePath()); - // NOTE: setting third argument to 'true' enables the default listeners - this.executor = new TestNGExecutor(tngSuite.toString(), - resultsDir.getAbsolutePath(), false); - } - - @Override - public String getCode() { - return etsProperties.getProperty("ets-code"); - } - - @Override - public String getVersion() { - return etsProperties.getProperty("ets-version"); - } - - @Override - public String getTitle() { - return etsProperties.getProperty("ets-title"); - } - - @Override - public Source doTestRun(Document testRunArgs) throws Exception { - validateTestRunArgs(testRunArgs); - return executor.execute(testRunArgs); - } - - /** - * Validates the test run arguments. The test run is aborted if any of these - * checks fail. - * - * @param testRunArgs A DOM Document containing a set of XML properties - * (key-value pairs). - * @throws IllegalArgumentException If any test run arguments are missing or - * invalid. - */ - void validateTestRunArgs(Document testRunArgs) throws IllegalArgumentException { - if (null == testRunArgs - || !testRunArgs.getDocumentElement().getNodeName() - .equals("properties")) { - throw new IllegalArgumentException( - "Input is not an XML properties document."); - } - NodeList entries = testRunArgs.getDocumentElement() - .getElementsByTagName("entry"); - if (entries.getLength() == 0) { - throw new IllegalArgumentException("No test run arguments found."); - } - Map args = new HashMap<>(); - for (int i = 0; i < entries.getLength(); i++) { - Element entry = (Element) entries.item(i); - args.put(entry.getAttribute("key"), entry.getTextContent()); - } - if (!args.containsKey(TestRunArg.IUT.toString())) { - throw new IllegalArgumentException(String.format( - "Missing argument: '%s' must be present.", - TestRunArg.IUT)); - } - } + private TestRunExecutor executor; + + private Properties etsProperties = new Properties(); + + /** + * A convenience method for running the test suite using a command-line interface. The + * default values of the test run arguments are as follows: + *
    + *
  • XML properties file: ${user.home}/test-run-props.xml
  • + *
  • outputDir: ${user.home}
  • + *
+ *

+ * Synopsis + *

+ *
+	 * ets-cat30-${version}-aio.jar [-o|--outputDir $TMPDIR] [test-run-props.xml]
+	 * 
+ * @param args Test run arguments (optional). + * @throws Exception If the test run cannot be executed (usually due to unsatisfied + * pre-conditions). + */ + public static void main(String[] args) throws Exception { + TestRunArguments testRunArgs = new TestRunArguments(); + JCommander cmd = new JCommander(testRunArgs); + try { + cmd.parse(args); + } + catch (ParameterException px) { + System.out.println(px.getMessage()); + cmd.usage(); + } + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + DocumentBuilder db = dbf.newDocumentBuilder(); + File xmlArgs = testRunArgs.getPropertiesFile(); + Document testRunProps = db.parse(xmlArgs); + TestNGController controller = new TestNGController(testRunArgs.getOutputDir()); + Source testResults = controller.doTestRun(testRunProps); + System.out.println("Test results: " + testResults.getSystemId()); + } + + /** + * Default constructor uses the location given by the "user.home" system property as + * the root output directory. + */ + public TestNGController() { + this(new File(System.getProperty("user.home")).toURI().toString()); + } + + /** + * Construct a controller that writes results to the given output directory. + * @param outputDir The location of the directory in which test results will be + * written. It will be created if it does not exist. + */ + public TestNGController(String outputDir) { + InputStream is = getClass().getResourceAsStream("ets.properties"); + try { + this.etsProperties.load(is); + } + catch (IOException ex) { + TestSuiteLogger.log(Level.WARNING, "Unable to load ets.properties. " + ex.getMessage()); + } + URL tngSuite = TestNGController.class.getResource("testng.xml"); + File resultsDir; + if (null == outputDir || outputDir.isEmpty()) { + resultsDir = new File(FilenameUtils.normalize(System.getProperty("user.home"))); + } + else if (outputDir.startsWith("file:")) { + resultsDir = new File(URI.create(outputDir)); + } + else { + resultsDir = new File(outputDir); + } + TestSuiteLogger.log(Level.CONFIG, "Using TestNG config: " + tngSuite); + TestSuiteLogger.log(Level.CONFIG, "Using outputDirPath: " + resultsDir.getAbsolutePath()); + // NOTE: setting third argument to 'true' enables the default listeners + this.executor = new TestNGExecutor(tngSuite.toString(), resultsDir.getAbsolutePath(), false); + } + + @Override + public String getCode() { + return etsProperties.getProperty("ets-code"); + } + + @Override + public String getVersion() { + return etsProperties.getProperty("ets-version"); + } + + @Override + public String getTitle() { + return etsProperties.getProperty("ets-title"); + } + + @Override + public Source doTestRun(Document testRunArgs) throws Exception { + validateTestRunArgs(testRunArgs); + return executor.execute(testRunArgs); + } + + /** + * Validates the test run arguments. The test run is aborted if any of these checks + * fail. + * @param testRunArgs A DOM Document containing a set of XML properties (key-value + * pairs). + * @throws IllegalArgumentException If any test run arguments are missing or invalid. + */ + void validateTestRunArgs(Document testRunArgs) throws IllegalArgumentException { + if (null == testRunArgs || !testRunArgs.getDocumentElement().getNodeName().equals("properties")) { + throw new IllegalArgumentException("Input is not an XML properties document."); + } + NodeList entries = testRunArgs.getDocumentElement().getElementsByTagName("entry"); + if (entries.getLength() == 0) { + throw new IllegalArgumentException("No test run arguments found."); + } + Map args = new HashMap<>(); + for (int i = 0; i < entries.getLength(); i++) { + Element entry = (Element) entries.item(i); + args.put(entry.getAttribute("key"), entry.getTextContent()); + } + if (!args.containsKey(TestRunArg.IUT.toString())) { + throw new IllegalArgumentException( + String.format("Missing argument: '%s' must be present.", TestRunArg.IUT)); + } + } + } diff --git a/src/main/java/org/opengis/cite/cat30/TestRunArg.java b/src/main/java/org/opengis/cite/cat30/TestRunArg.java index d6965a9..0b38a6e 100644 --- a/src/main/java/org/opengis/cite/cat30/TestRunArg.java +++ b/src/main/java/org/opengis/cite/cat30/TestRunArg.java @@ -5,14 +5,15 @@ */ public enum TestRunArg { - /** - * An absolute URI that refers to a representation of the test subject or - * metadata about it. - */ - IUT; + /** + * An absolute URI that refers to a representation of the test subject or metadata + * about it. + */ + IUT; + + @Override + public String toString() { + return name().toLowerCase(); + } - @Override - public String toString() { - return name().toLowerCase(); - } } diff --git a/src/main/java/org/opengis/cite/cat30/TestRunArguments.java b/src/main/java/org/opengis/cite/cat30/TestRunArguments.java index 97eba8d..7bf1311 100644 --- a/src/main/java/org/opengis/cite/cat30/TestRunArguments.java +++ b/src/main/java/org/opengis/cite/cat30/TestRunArguments.java @@ -7,9 +7,8 @@ import java.util.List; /** - * Declares supported command line arguments that are parsed using the - * JCommander library. All arguments are optional. The default values are as - * follows: + * Declares supported command line arguments that are parsed using the JCommander library. + * All arguments are optional. The default values are as follows: *
    *
  • XML properties file: ${user.home}/test-run-props.xml
  • *
  • outputDir: ${user.home}
  • @@ -23,30 +22,30 @@ */ public class TestRunArguments { - @Parameter(description = "Properties file") - private List xmlProps; - - @Parameter(names = {"-o", "--outputDir"}, description = "Output directory") - private String outputDir; - - public TestRunArguments() { - this.xmlProps = new ArrayList<>(); - } - - public File getPropertiesFile() { - File fileRef; - if (xmlProps.isEmpty()) { - fileRef = new File(System.getProperty("user.home"), - "test-run-props.xml"); - } else { - String propsFile = xmlProps.get(0); - fileRef = (propsFile.startsWith("file:")) ? new File( - URI.create(propsFile)) : new File(propsFile); - } - return fileRef; - } - - public String getOutputDir() { - return (null != outputDir) ? outputDir : System.getProperty("user.home"); - } + @Parameter(description = "Properties file") + private List xmlProps; + + @Parameter(names = { "-o", "--outputDir" }, description = "Output directory") + private String outputDir; + + public TestRunArguments() { + this.xmlProps = new ArrayList<>(); + } + + public File getPropertiesFile() { + File fileRef; + if (xmlProps.isEmpty()) { + fileRef = new File(System.getProperty("user.home"), "test-run-props.xml"); + } + else { + String propsFile = xmlProps.get(0); + fileRef = (propsFile.startsWith("file:")) ? new File(URI.create(propsFile)) : new File(propsFile); + } + return fileRef; + } + + public String getOutputDir() { + return (null != outputDir) ? outputDir : System.getProperty("user.home"); + } + } diff --git a/src/main/java/org/opengis/cite/cat30/basic/BasicGetRecordsTests.java b/src/main/java/org/opengis/cite/cat30/basic/BasicGetRecordsTests.java index 05e121c..03da4ba 100644 --- a/src/main/java/org/opengis/cite/cat30/basic/BasicGetRecordsTests.java +++ b/src/main/java/org/opengis/cite/cat30/basic/BasicGetRecordsTests.java @@ -39,433 +39,390 @@ import jakarta.ws.rs.core.Response; /** - * Provides tests that apply to the GetRecords request using the - * KVP syntax. + * Provides tests that apply to the GetRecords request using the KVP syntax. * *

    - * The KVP syntax must be supported; this encoding is generally used with the - * GET method but may also be used with the POST method; however a POST method - * binding is very unusual and will be advertised in the capabilities document - * as an operational constraint as indicated below. The media type of a KVP - * request entity is "application/x-www-form-urlencoded". + * The KVP syntax must be supported; this encoding is generally used with the GET method + * but may also be used with the POST method; however a POST method binding is very + * unusual and will be advertised in the capabilities document as an operational + * constraint as indicated below. The media type of a KVP request entity is + * "application/x-www-form-urlencoded". *

    * * @see "OGC 12-176r6, 7.3: GetRecords operation" */ public class BasicGetRecordsTests extends CommonFixture { - /** - * Service endpoint for GetRecords using the GET method. - */ - private URI getURI; - /** - * Service endpoint for GetRecords using the POST method. - */ - private URI postURI; - /** - * Information about the sample data retrieved from the IUT. - */ - private DatasetInfo datasetInfo; + /** + * Service endpoint for GetRecords using the GET method. + */ + private URI getURI; - void setGetEndpoint(URI uri) { - this.getURI = uri; - } + /** + * Service endpoint for GetRecords using the POST method. + */ + private URI postURI; - /** - * Finds the GET and POST method endpoints for the GetRecords request in the - * capabilities document. - * - * @param testContext The test context containing various suite attributes. - */ - @BeforeClass - public void findRequestEndpoints(ITestContext testContext) { - this.getURI = ServiceMetadataUtils.getOperationEndpoint( - this.cswCapabilities, CAT3.GET_RECORDS, HttpMethod.GET); - if (null == this.getURI.getScheme()) { - throw new SkipException("GET endpoint for GetRecords request not found."); - } - this.postURI = ServiceMetadataUtils.getOperationEndpoint( - this.cswCapabilities, CAT3.GET_RECORDS, HttpMethod.POST); - } + /** + * Information about the sample data retrieved from the IUT. + */ + private DatasetInfo datasetInfo; - /** - * Gets information about the sample data obtained from the IUT, including: - * its total geographic extent; a collection of record titles; and a set of - * record identifiers. Each csw:Record element may contain at least one - * ows:BoundingBox (or ows:WGS84BoundingBox) element that describes the - * spatial coverage of a catalogued resource. - * - * @param testContext The test context containing various suite attributes. - */ - @BeforeClass - public void getDatasetInfo(ITestContext testContext) { - DatasetInfo dataset = (DatasetInfo) testContext.getSuite().getAttribute( - SuiteAttribute.DATASET.getName()); - if (null == dataset) { - throw new SkipException("Dataset info not found in test context."); - } - this.datasetInfo = dataset; - } + void setGetEndpoint(URI uri) { + this.getURI = uri; + } - /** - * [Test] Submits a GetRecords request with no search criteria. The - * namespace parameter declares a namespace binding for the - * common type name (tns:Record). The results must contain one or more - * csw:BriefRecord elements. - * - *

    Sources

    - *
      - *
    • OGC 12-176r6, Table 19: KVP encoding for GetRecords operation - * request
    • - *
    • OGC 12-176r6, 7.3.4.1: NAMESPACE parameter
    • - *
    - */ - @Test(description = "Requirements: 063") - public void getBriefRecordsWithNamespaceBinding() { - Map qryParams = new HashMap<>(); - qryParams.put(CAT3.REQUEST, CAT3.GET_RECORDS); - qryParams.put(CAT3.SERVICE, CAT3.SERVICE_TYPE_CODE); - qryParams.put(CAT3.VERSION, CAT3.VERSION_3_0_0); - qryParams.put(CAT3.NAMESPACE, String.format("xmlns(tns=%s)", Namespaces.CSW)); - qryParams.put(CAT3.TYPE_NAMES, "tns:Record"); - qryParams.put(CAT3.ELEMENT_SET, CAT3.ELEMENT_SET_BRIEF); - response = ClientUtils.buildGetRequest(this.getURI, qryParams, - MediaType.APPLICATION_XML_TYPE); - Assert.assertEquals(response.getStatus(), - Response.Status.OK.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - Document entity = getResponseEntityAsDocument(response, null); - String expr = "count(//csw:SearchResults/csw:BriefRecord) > 0"; - ETSAssert.assertXPath(expr, entity, null); - } + /** + * Finds the GET and POST method endpoints for the GetRecords request in the + * capabilities document. + * @param testContext The test context containing various suite attributes. + */ + @BeforeClass + public void findRequestEndpoints(ITestContext testContext) { + this.getURI = ServiceMetadataUtils.getOperationEndpoint(this.cswCapabilities, CAT3.GET_RECORDS, HttpMethod.GET); + if (null == this.getURI.getScheme()) { + throw new SkipException("GET endpoint for GetRecords request not found."); + } + this.postURI = ServiceMetadataUtils.getOperationEndpoint(this.cswCapabilities, CAT3.GET_RECORDS, + HttpMethod.POST); + } - /** - * [Test] Submits a GetRecords request with no search criteria. The - * requested record representation is csw:SummaryRecord; the - * csw:SearchResults element in the response cannot be empty. - */ - @Test(description = "Requirements: 101") - public void getSummaryRecordsInDefaultRepresentation() { - Map qryParams = new HashMap<>(); - qryParams.put(CAT3.REQUEST, CAT3.GET_RECORDS); - qryParams.put(CAT3.SERVICE, CAT3.SERVICE_TYPE_CODE); - qryParams.put(CAT3.VERSION, CAT3.VERSION_3_0_0); - qryParams.put(CAT3.TYPE_NAMES, "Record"); - qryParams.put(CAT3.ELEMENT_SET, CAT3.ELEMENT_SET_SUMMARY); - response = ClientUtils.buildGetRequest(this.getURI, qryParams, - MediaType.APPLICATION_XML_TYPE); - Assert.assertEquals(response.getStatus(), - Response.Status.OK.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - Document entity = getResponseEntityAsDocument(response, null); - String expr = "count(//csw:SearchResults/csw:SummaryRecord) > 0"; - ETSAssert.assertXPath(expr, entity, null); - } + /** + * Gets information about the sample data obtained from the IUT, including: its total + * geographic extent; a collection of record titles; and a set of record identifiers. + * Each csw:Record element may contain at least one ows:BoundingBox (or + * ows:WGS84BoundingBox) element that describes the spatial coverage of a catalogued + * resource. + * @param testContext The test context containing various suite attributes. + */ + @BeforeClass + public void getDatasetInfo(ITestContext testContext) { + DatasetInfo dataset = (DatasetInfo) testContext.getSuite().getAttribute(SuiteAttribute.DATASET.getName()); + if (null == dataset) { + throw new SkipException("Dataset info not found in test context."); + } + this.datasetInfo = dataset; + } - /** - * [Test] Submits a GetRecords request with no search criteria. The Accept - * header and the outputFormat parameter express a preference for an Atom - * feed in the response. The resulting atom:feed must be valid according to - * RFC 4287. - * - *

    Sources

    - *
      - *
    • OGC 12-176r6, 7.3.6: Atom response
    • - *
    • OGC 10-032r8, Table 6: Elements of Search operation response in the - * atom:feed element describing the search service
    • - *
    • RFC - * 4287: The Atom Syndication Format
    • - *
    - */ - @Test(description = "Requirements: 80,122") - public void getSummaryRecordsInAtomFeed() { - Map qryParams = new HashMap<>(); - qryParams.put(CAT3.REQUEST, CAT3.GET_RECORDS); - qryParams.put(CAT3.SERVICE, CAT3.SERVICE_TYPE_CODE); - qryParams.put(CAT3.VERSION, CAT3.VERSION_3_0_0); - qryParams.put(CAT3.TYPE_NAMES, "Record"); - qryParams.put(CAT3.ELEMENT_SET, CAT3.ELEMENT_SET_SUMMARY); - qryParams.put(CAT3.OUTPUT_FORMAT, MediaType.APPLICATION_ATOM_XML); - response = ClientUtils.buildGetRequest(this.getURI, qryParams, - MediaType.APPLICATION_ATOM_XML_TYPE); - Assert.assertEquals(response.getStatus(), - Response.Status.OK.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - Assert.assertEquals(ClientUtils.removeParameters(response.getMediaType()), - MediaType.APPLICATION_ATOM_XML_TYPE, - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_MEDIA_TYPE)); - Document entity = getResponseEntityAsDocument(response, null); - Map nsBindings = Collections.singletonMap(Namespaces.ATOM, "atom"); - String expr = "count(/atom:feed/atom:entry) > 0"; - ETSAssert.assertXPath(expr, entity, nsBindings); - Validator atomValidator = this.atomSchema.newValidator(); - ValidationErrorHandler err = new ValidationErrorHandler(); - atomValidator.setErrorHandler(err); - try { - Source src = XMLUtils.toStreamSource(new DOMSource(entity)); - atomValidator.validate(src); - } catch (SAXException | IOException ex) { - Logger.getLogger(GetRecordByIdTests.class.getName()).log( - Level.WARNING, "Error attempting to validate Atom feed.", ex); - } - Assert.assertFalse(err.errorsDetected(), - ErrorMessage.format(ErrorMessageKeys.NOT_SCHEMA_VALID, - err.getErrorCount(), err.toString())); - } + /** + * [Test] Submits a GetRecords request with no search criteria. The + * namespace parameter declares a namespace binding for the common type + * name (tns:Record). The results must contain one or more csw:BriefRecord elements. + * + *

    + * Sources + *

    + *
      + *
    • OGC 12-176r6, Table 19: KVP encoding for GetRecords operation request
    • + *
    • OGC 12-176r6, 7.3.4.1: NAMESPACE parameter
    • + *
    + */ + @Test(description = "Requirements: 063") + public void getBriefRecordsWithNamespaceBinding() { + Map qryParams = new HashMap<>(); + qryParams.put(CAT3.REQUEST, CAT3.GET_RECORDS); + qryParams.put(CAT3.SERVICE, CAT3.SERVICE_TYPE_CODE); + qryParams.put(CAT3.VERSION, CAT3.VERSION_3_0_0); + qryParams.put(CAT3.NAMESPACE, String.format("xmlns(tns=%s)", Namespaces.CSW)); + qryParams.put(CAT3.TYPE_NAMES, "tns:Record"); + qryParams.put(CAT3.ELEMENT_SET, CAT3.ELEMENT_SET_BRIEF); + response = ClientUtils.buildGetRequest(this.getURI, qryParams, MediaType.APPLICATION_XML_TYPE); + Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode(), + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + Document entity = getResponseEntityAsDocument(response, null); + String expr = "count(//csw:SearchResults/csw:BriefRecord) > 0"; + ETSAssert.assertXPath(expr, entity, null); + } - /** - * [Test] Submits a GetRecords request that specifies an unsupported output - * format (text/example) as the value of the outputFormat - * parameter. An exception report is expected in response with HTTP status - * code 400 and exception code - * "{@value org.opengis.cite.cat30.CAT3#INVALID_PARAM_VAL}". - */ - @Test(description = "Requirements: 035,037,042") - public void getRecordsInUnsupportedOutputFormat() { - Map qryParams = new HashMap<>(); - qryParams.put(CAT3.REQUEST, CAT3.GET_RECORDS); - qryParams.put(CAT3.SERVICE, CAT3.SERVICE_TYPE_CODE); - qryParams.put(CAT3.VERSION, CAT3.VERSION_3_0_0); - qryParams.put(CAT3.TYPE_NAMES, "Record"); - qryParams.put(CAT3.ELEMENT_SET, CAT3.ELEMENT_SET_FULL); - qryParams.put(CAT3.OUTPUT_FORMAT, "text/example"); - response = ClientUtils.buildGetRequest(this.getURI, qryParams, - MediaType.WILDCARD_TYPE); - ETSAssert.assertExceptionReport(response, CAT3.INVALID_PARAM_VAL, - CAT3.OUTPUT_FORMAT); - } + /** + * [Test] Submits a GetRecords request with no search criteria. The requested record + * representation is csw:SummaryRecord; the csw:SearchResults element in the response + * cannot be empty. + */ + @Test(description = "Requirements: 101") + public void getSummaryRecordsInDefaultRepresentation() { + Map qryParams = new HashMap<>(); + qryParams.put(CAT3.REQUEST, CAT3.GET_RECORDS); + qryParams.put(CAT3.SERVICE, CAT3.SERVICE_TYPE_CODE); + qryParams.put(CAT3.VERSION, CAT3.VERSION_3_0_0); + qryParams.put(CAT3.TYPE_NAMES, "Record"); + qryParams.put(CAT3.ELEMENT_SET, CAT3.ELEMENT_SET_SUMMARY); + response = ClientUtils.buildGetRequest(this.getURI, qryParams, MediaType.APPLICATION_XML_TYPE); + Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode(), + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + Document entity = getResponseEntityAsDocument(response, null); + String expr = "count(//csw:SearchResults/csw:SummaryRecord) > 0"; + ETSAssert.assertXPath(expr, entity, null); + } - /** - * [Test] Submits a GetRecords request that specifies an unknown - * typeName parameter value. Since the type does not belong to - * any supported information model, an exception report is expected in - * response with HTTP status code 400 and exception code - * "{@value org.opengis.cite.cat30.CAT3#INVALID_PARAM_VAL}". - * - * @see "OGC Catalogue Services 3.0 Specification - HTTP Protocol Binding, - * 7.3.4.7: typeNames parameter" - */ - @Test(description = "Requirements: 088") - public void getRecordsWithUnknownTypeName() { - Map qryParams = new HashMap<>(); - qryParams.put(CAT3.REQUEST, CAT3.GET_RECORDS); - qryParams.put(CAT3.SERVICE, CAT3.SERVICE_TYPE_CODE); - qryParams.put(CAT3.VERSION, CAT3.VERSION_3_0_0); - qryParams.put(CAT3.TYPE_NAMES, "UnknownType"); - response = ClientUtils.buildGetRequest(this.getURI, qryParams, - MediaType.APPLICATION_XML_TYPE); - ETSAssert.assertExceptionReport(response, CAT3.INVALID_PARAM_VAL, - CAT3.TYPE_NAMES); - } + /** + * [Test] Submits a GetRecords request with no search criteria. The Accept header and + * the outputFormat parameter express a preference for an Atom feed in the response. + * The resulting atom:feed must be valid according to RFC 4287. + * + *

    + * Sources + *

    + *
      + *
    • OGC 12-176r6, 7.3.6: Atom response
    • + *
    • OGC 10-032r8, Table 6: Elements of Search operation response in the atom:feed + * element describing the search service
    • + *
    • RFC 4287: The + * Atom Syndication Format
    • + *
    + */ + @Test(description = "Requirements: 80,122") + public void getSummaryRecordsInAtomFeed() { + Map qryParams = new HashMap<>(); + qryParams.put(CAT3.REQUEST, CAT3.GET_RECORDS); + qryParams.put(CAT3.SERVICE, CAT3.SERVICE_TYPE_CODE); + qryParams.put(CAT3.VERSION, CAT3.VERSION_3_0_0); + qryParams.put(CAT3.TYPE_NAMES, "Record"); + qryParams.put(CAT3.ELEMENT_SET, CAT3.ELEMENT_SET_SUMMARY); + qryParams.put(CAT3.OUTPUT_FORMAT, MediaType.APPLICATION_ATOM_XML); + response = ClientUtils.buildGetRequest(this.getURI, qryParams, MediaType.APPLICATION_ATOM_XML_TYPE); + Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode(), + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + Assert.assertEquals(ClientUtils.removeParameters(response.getMediaType()), MediaType.APPLICATION_ATOM_XML_TYPE, + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_MEDIA_TYPE)); + Document entity = getResponseEntityAsDocument(response, null); + Map nsBindings = Collections.singletonMap(Namespaces.ATOM, "atom"); + String expr = "count(/atom:feed/atom:entry) > 0"; + ETSAssert.assertXPath(expr, entity, nsBindings); + Validator atomValidator = this.atomSchema.newValidator(); + ValidationErrorHandler err = new ValidationErrorHandler(); + atomValidator.setErrorHandler(err); + try { + Source src = XMLUtils.toStreamSource(new DOMSource(entity)); + atomValidator.validate(src); + } + catch (SAXException | IOException ex) { + Logger.getLogger(GetRecordByIdTests.class.getName()) + .log(Level.WARNING, "Error attempting to validate Atom feed.", ex); + } + Assert.assertFalse(err.errorsDetected(), + ErrorMessage.format(ErrorMessageKeys.NOT_SCHEMA_VALID, err.getErrorCount(), err.toString())); + } - /** - * [Test] Submits a GetRecords request that specifies an unknown - * elementSetName parameter value. An exception report is - * expected in response with HTTP status code 400 and exception code - * "{@value org.opengis.cite.cat30.CAT3#INVALID_PARAM_VAL}". - * - * @see "OGC Catalogue Services 3.0 Specification - HTTP Protocol Binding, - * 7.3.4.8: ElementName or ElementSetName parameter" - */ - @Test(description = "Requirements: 102") - public void unknownElementSetName() { - Map qryParams = new HashMap<>(); - qryParams.put(CAT3.REQUEST, CAT3.GET_RECORDS); - qryParams.put(CAT3.SERVICE, CAT3.SERVICE_TYPE_CODE); - qryParams.put(CAT3.VERSION, CAT3.VERSION_3_0_0); - qryParams.put(CAT3.TYPE_NAMES, "Record"); - qryParams.put(CAT3.ELEMENT_SET, "undefined-view"); - response = ClientUtils.buildGetRequest(this.getURI, qryParams, - MediaType.APPLICATION_XML_TYPE); - ETSAssert.assertExceptionReport(response, CAT3.INVALID_PARAM_VAL, - CAT3.ELEMENT_SET); - } + /** + * [Test] Submits a GetRecords request that specifies an unsupported output format + * (text/example) as the value of the outputFormat parameter. An + * exception report is expected in response with HTTP status code 400 and exception + * code "{@value org.opengis.cite.cat30.CAT3#INVALID_PARAM_VAL}". + */ + @Test(description = "Requirements: 035,037,042") + public void getRecordsInUnsupportedOutputFormat() { + Map qryParams = new HashMap<>(); + qryParams.put(CAT3.REQUEST, CAT3.GET_RECORDS); + qryParams.put(CAT3.SERVICE, CAT3.SERVICE_TYPE_CODE); + qryParams.put(CAT3.VERSION, CAT3.VERSION_3_0_0); + qryParams.put(CAT3.TYPE_NAMES, "Record"); + qryParams.put(CAT3.ELEMENT_SET, CAT3.ELEMENT_SET_FULL); + qryParams.put(CAT3.OUTPUT_FORMAT, "text/example"); + response = ClientUtils.buildGetRequest(this.getURI, qryParams, MediaType.WILDCARD_TYPE); + ETSAssert.assertExceptionReport(response, CAT3.INVALID_PARAM_VAL, CAT3.OUTPUT_FORMAT); + } - /** - * [Test] Submits a GetRecords request that specifies an unsupported output - * schema ("urn:uuid:6a29d2a8-9651-47a6-9b14-f05d2b5644f0"). An exception - * report is expected in response with HTTP status code 400 and exception - * code "{@value org.opengis.cite.cat30.CAT3#INVALID_PARAM_VAL}". - */ - @Test(description = "Requirements: 035,037,042") - public void getRecordsWithUnsupportedSchema() { - Map qryParams = new HashMap<>(); - qryParams.put(CAT3.REQUEST, CAT3.GET_RECORDS); - qryParams.put(CAT3.SERVICE, CAT3.SERVICE_TYPE_CODE); - qryParams.put(CAT3.VERSION, CAT3.VERSION_3_0_0); - qryParams.put(CAT3.TYPE_NAMES, "Record"); - qryParams.put(CAT3.ELEMENT_SET, CAT3.ELEMENT_SET_BRIEF); - qryParams.put(CAT3.OUTPUT_SCHEMA, "urn:uuid:6a29d2a8-9651-47a6-9b14-f05d2b5644f0"); - response = ClientUtils.buildGetRequest(this.getURI, qryParams, - MediaType.APPLICATION_XML_TYPE); - ETSAssert.assertExceptionReport(response, CAT3.INVALID_PARAM_VAL, - CAT3.OUTPUT_SCHEMA); - } + /** + * [Test] Submits a GetRecords request that specifies an unknown typeName + * parameter value. Since the type does not belong to any supported information model, + * an exception report is expected in response with HTTP status code 400 and exception + * code "{@value org.opengis.cite.cat30.CAT3#INVALID_PARAM_VAL}". + * + * @see "OGC Catalogue Services 3.0 Specification - HTTP Protocol Binding, 7.3.4.7: + * typeNames parameter" + */ + @Test(description = "Requirements: 088") + public void getRecordsWithUnknownTypeName() { + Map qryParams = new HashMap<>(); + qryParams.put(CAT3.REQUEST, CAT3.GET_RECORDS); + qryParams.put(CAT3.SERVICE, CAT3.SERVICE_TYPE_CODE); + qryParams.put(CAT3.VERSION, CAT3.VERSION_3_0_0); + qryParams.put(CAT3.TYPE_NAMES, "UnknownType"); + response = ClientUtils.buildGetRequest(this.getURI, qryParams, MediaType.APPLICATION_XML_TYPE); + ETSAssert.assertExceptionReport(response, CAT3.INVALID_PARAM_VAL, CAT3.TYPE_NAMES); + } - /** - * [Test] Submits a GetRecords request where the 'elementName' parameter - * value identifies a single element from the output schema (dc:title). The - * response must be augmented with additional elements so as to be schema - * valid. Furthermore, every record in the result set must contain one or - * more (required) dc:title elements; no other optional elements must appear. - */ - @Test(description = "Requirements: 093") - public void presentTitleProperty() { - Map qryParams = new HashMap<>(); - qryParams.put(CAT3.REQUEST, CAT3.GET_RECORDS); - qryParams.put(CAT3.SERVICE, CAT3.SERVICE_TYPE_CODE); - qryParams.put(CAT3.VERSION, CAT3.VERSION_3_0_0); - qryParams.put(CAT3.TYPE_NAMES, "Record"); - qryParams.put(CAT3.ELEMENT_NAME, "tns:title"); - qryParams.put(CAT3.NAMESPACE, - String.format("xmlns(tns=%s)", Namespaces.DCMES)); - response = ClientUtils.buildGetRequest(this.getURI, qryParams, - MediaType.APPLICATION_XML_TYPE); - Assert.assertEquals(response.getStatus(), - Response.Status.OK.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - Document entity = getResponseEntityAsDocument(response, null); - Validator validator = this.cswSchema.newValidator(); - ETSAssert.assertSchemaValid(validator, - new DOMSource(entity, entity.getDocumentURI())); - Element results = (Element) entity.getElementsByTagNameNS( - Namespaces.CSW, CAT3.SEARCH_RESULTS).item(0); - ETSAssert.assertXPath( - "not(csw:SummaryRecord[dc:type or dc:subject or dc:format or ows:BoundingBox])", - results, null); - } + /** + * [Test] Submits a GetRecords request that specifies an unknown + * elementSetName parameter value. An exception report is expected in + * response with HTTP status code 400 and exception code + * "{@value org.opengis.cite.cat30.CAT3#INVALID_PARAM_VAL}". + * + * @see "OGC Catalogue Services 3.0 Specification - HTTP Protocol Binding, 7.3.4.8: + * ElementName or ElementSetName parameter" + */ + @Test(description = "Requirements: 102") + public void unknownElementSetName() { + Map qryParams = new HashMap<>(); + qryParams.put(CAT3.REQUEST, CAT3.GET_RECORDS); + qryParams.put(CAT3.SERVICE, CAT3.SERVICE_TYPE_CODE); + qryParams.put(CAT3.VERSION, CAT3.VERSION_3_0_0); + qryParams.put(CAT3.TYPE_NAMES, "Record"); + qryParams.put(CAT3.ELEMENT_SET, "undefined-view"); + response = ClientUtils.buildGetRequest(this.getURI, qryParams, MediaType.APPLICATION_XML_TYPE); + ETSAssert.assertExceptionReport(response, CAT3.INVALID_PARAM_VAL, CAT3.ELEMENT_SET); + } - /** - * [Test] Submits a GetRecords request that specifies an element name not - * declared in the output schema. An exception report is expected in - * response with HTTP status code 400 and exception code - * "{@value org.opengis.cite.cat30.CAT3#INVALID_PARAM_VAL}". - */ - @Test(description = "Requirements: 091") - public void presentUnknownRecordProperty() { - Map qryParams = new HashMap<>(); - qryParams.put(CAT3.REQUEST, CAT3.GET_RECORDS); - qryParams.put(CAT3.SERVICE, CAT3.SERVICE_TYPE_CODE); - qryParams.put(CAT3.VERSION, CAT3.VERSION_3_0_0); - qryParams.put(CAT3.TYPE_NAMES, "Record"); - qryParams.put(CAT3.ELEMENT_NAME, "undefined"); - response = ClientUtils.buildGetRequest(this.getURI, qryParams, - MediaType.APPLICATION_XML_TYPE); - ETSAssert.assertExceptionReport(response, CAT3.INVALID_PARAM_VAL, - CAT3.ELEMENT_NAME); - } + /** + * [Test] Submits a GetRecords request that specifies an unsupported output schema + * ("urn:uuid:6a29d2a8-9651-47a6-9b14-f05d2b5644f0"). An exception report is expected + * in response with HTTP status code 400 and exception code + * "{@value org.opengis.cite.cat30.CAT3#INVALID_PARAM_VAL}". + */ + @Test(description = "Requirements: 035,037,042") + public void getRecordsWithUnsupportedSchema() { + Map qryParams = new HashMap<>(); + qryParams.put(CAT3.REQUEST, CAT3.GET_RECORDS); + qryParams.put(CAT3.SERVICE, CAT3.SERVICE_TYPE_CODE); + qryParams.put(CAT3.VERSION, CAT3.VERSION_3_0_0); + qryParams.put(CAT3.TYPE_NAMES, "Record"); + qryParams.put(CAT3.ELEMENT_SET, CAT3.ELEMENT_SET_BRIEF); + qryParams.put(CAT3.OUTPUT_SCHEMA, "urn:uuid:6a29d2a8-9651-47a6-9b14-f05d2b5644f0"); + response = ClientUtils.buildGetRequest(this.getURI, qryParams, MediaType.APPLICATION_XML_TYPE); + ETSAssert.assertExceptionReport(response, CAT3.INVALID_PARAM_VAL, CAT3.OUTPUT_SCHEMA); + } - /** - * [Test] Submits a GetRecords request that contains both the - * ElementName and ElementSetName parameters. - * Since these parameters are mutually exclusive, an exception report is - * expected in response with HTTP status code 400 and exception code - * "{@value org.opengis.cite.cat30.CAT3#NO_CODE}". - */ - @Test(description = "Requirements: 099") - public void elementSetAndElementName() { - Map qryParams = new HashMap<>(); - qryParams.put(CAT3.REQUEST, CAT3.GET_RECORDS); - qryParams.put(CAT3.SERVICE, CAT3.SERVICE_TYPE_CODE); - qryParams.put(CAT3.VERSION, CAT3.VERSION_3_0_0); - qryParams.put(CAT3.TYPE_NAMES, "Record"); - qryParams.put(CAT3.ELEMENT_SET, CAT3.ELEMENT_SET_BRIEF); - qryParams.put(CAT3.ELEMENT_NAME, "ns1:subject"); - qryParams.put(CAT3.NAMESPACE, String.format("xmlns(ns1=%s)", Namespaces.DCMES)); - response = ClientUtils.buildGetRequest(this.getURI, qryParams, - MediaType.APPLICATION_XML_TYPE); - ETSAssert.assertExceptionReport(response, CAT3.NO_CODE, null); - } + /** + * [Test] Submits a GetRecords request where the 'elementName' parameter value + * identifies a single element from the output schema (dc:title). The response must be + * augmented with additional elements so as to be schema valid. Furthermore, every + * record in the result set must contain one or more (required) dc:title elements; no + * other optional elements must appear. + */ + @Test(description = "Requirements: 093") + public void presentTitleProperty() { + Map qryParams = new HashMap<>(); + qryParams.put(CAT3.REQUEST, CAT3.GET_RECORDS); + qryParams.put(CAT3.SERVICE, CAT3.SERVICE_TYPE_CODE); + qryParams.put(CAT3.VERSION, CAT3.VERSION_3_0_0); + qryParams.put(CAT3.TYPE_NAMES, "Record"); + qryParams.put(CAT3.ELEMENT_NAME, "tns:title"); + qryParams.put(CAT3.NAMESPACE, String.format("xmlns(tns=%s)", Namespaces.DCMES)); + response = ClientUtils.buildGetRequest(this.getURI, qryParams, MediaType.APPLICATION_XML_TYPE); + Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode(), + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + Document entity = getResponseEntityAsDocument(response, null); + Validator validator = this.cswSchema.newValidator(); + ETSAssert.assertSchemaValid(validator, new DOMSource(entity, entity.getDocumentURI())); + Element results = (Element) entity.getElementsByTagNameNS(Namespaces.CSW, CAT3.SEARCH_RESULTS).item(0); + ETSAssert.assertXPath("not(csw:SummaryRecord[dc:type or dc:subject or dc:format or ows:BoundingBox])", results, + null); + } - /** - * [Test] Submits a GetRecords request where the startPosition - * and maxRecords parameters have non-default values. The - * csw:SearchResults element in the response entity must present the correct - * "slice" of the result set. - */ - @Test(description = "Requirements: 082,084") - public void getPartialResults() { - Map qryParams = new HashMap<>(); - qryParams.put(CAT3.REQUEST, CAT3.GET_RECORDS); - qryParams.put(CAT3.SERVICE, CAT3.SERVICE_TYPE_CODE); - qryParams.put(CAT3.VERSION, CAT3.VERSION_3_0_0); - qryParams.put(CAT3.NAMESPACE, String.format("xmlns(csw3=%s)", Namespaces.CSW)); - qryParams.put(CAT3.TYPE_NAMES, "csw3:Record"); - qryParams.put(CAT3.ELEMENT_SET, CAT3.ELEMENT_SET_SUMMARY); - int startPosition = 3; - qryParams.put(CAT3.START_POS, Integer.toString(startPosition)); - int maxRecords = 2; - qryParams.put(CAT3.MAX_RECORDS, Integer.toString(maxRecords)); - response = ClientUtils.buildGetRequest(this.getURI, qryParams, - MediaType.APPLICATION_XML_TYPE); - Assert.assertEquals(response.getStatus(), - Response.Status.OK.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - Document entity = getResponseEntityAsDocument(response, null); - Node resultsNode = entity.getElementsByTagNameNS( - Namespaces.CSW, "SearchResults").item(0); - Assert.assertNotNull(resultsNode, - ErrorMessage.format(ErrorMessageKeys.MISSING_INFOSET_ITEM, "csw:SearchResults")); - Element results = (Element) resultsNode; - Assert.assertEquals(results.getElementsByTagNameNS(Namespaces.CSW, "SummaryRecord").getLength(), - maxRecords, - ErrorMessage.format(ErrorMessageKeys.RESULT_SET_SIZE, "csw:SummaryRecord")); - Assert.assertEquals(Integer.parseInt(results.getAttribute(CAT3.NUM_REC_RETURNED)), - maxRecords, - ErrorMessage.format(ErrorMessageKeys.INFOSET_ITEM_VALUE, "@numberOfRecordsReturned")); - Assert.assertEquals(Integer.parseInt(results.getAttribute(CAT3.NEXT_REC)), - startPosition + maxRecords, - ErrorMessage.format(ErrorMessageKeys.INFOSET_ITEM_VALUE, "@nextRecord")); - } + /** + * [Test] Submits a GetRecords request that specifies an element name not declared in + * the output schema. An exception report is expected in response with HTTP status + * code 400 and exception code + * "{@value org.opengis.cite.cat30.CAT3#INVALID_PARAM_VAL}". + */ + @Test(description = "Requirements: 091") + public void presentUnknownRecordProperty() { + Map qryParams = new HashMap<>(); + qryParams.put(CAT3.REQUEST, CAT3.GET_RECORDS); + qryParams.put(CAT3.SERVICE, CAT3.SERVICE_TYPE_CODE); + qryParams.put(CAT3.VERSION, CAT3.VERSION_3_0_0); + qryParams.put(CAT3.TYPE_NAMES, "Record"); + qryParams.put(CAT3.ELEMENT_NAME, "undefined"); + response = ClientUtils.buildGetRequest(this.getURI, qryParams, MediaType.APPLICATION_XML_TYPE); + ETSAssert.assertExceptionReport(response, CAT3.INVALID_PARAM_VAL, CAT3.ELEMENT_NAME); + } + + /** + * [Test] Submits a GetRecords request that contains both the ElementName + * and ElementSetName parameters. Since these parameters are mutually + * exclusive, an exception report is expected in response with HTTP status code 400 + * and exception code "{@value org.opengis.cite.cat30.CAT3#NO_CODE}". + */ + @Test(description = "Requirements: 099") + public void elementSetAndElementName() { + Map qryParams = new HashMap<>(); + qryParams.put(CAT3.REQUEST, CAT3.GET_RECORDS); + qryParams.put(CAT3.SERVICE, CAT3.SERVICE_TYPE_CODE); + qryParams.put(CAT3.VERSION, CAT3.VERSION_3_0_0); + qryParams.put(CAT3.TYPE_NAMES, "Record"); + qryParams.put(CAT3.ELEMENT_SET, CAT3.ELEMENT_SET_BRIEF); + qryParams.put(CAT3.ELEMENT_NAME, "ns1:subject"); + qryParams.put(CAT3.NAMESPACE, String.format("xmlns(ns1=%s)", Namespaces.DCMES)); + response = ClientUtils.buildGetRequest(this.getURI, qryParams, MediaType.APPLICATION_XML_TYPE); + ETSAssert.assertExceptionReport(response, CAT3.NO_CODE, null); + } + + /** + * [Test] Submits a GetRecords request where the startPosition and + * maxRecords parameters have non-default values. The csw:SearchResults + * element in the response entity must present the correct "slice" of the result set. + */ + @Test(description = "Requirements: 082,084") + public void getPartialResults() { + Map qryParams = new HashMap<>(); + qryParams.put(CAT3.REQUEST, CAT3.GET_RECORDS); + qryParams.put(CAT3.SERVICE, CAT3.SERVICE_TYPE_CODE); + qryParams.put(CAT3.VERSION, CAT3.VERSION_3_0_0); + qryParams.put(CAT3.NAMESPACE, String.format("xmlns(csw3=%s)", Namespaces.CSW)); + qryParams.put(CAT3.TYPE_NAMES, "csw3:Record"); + qryParams.put(CAT3.ELEMENT_SET, CAT3.ELEMENT_SET_SUMMARY); + int startPosition = 3; + qryParams.put(CAT3.START_POS, Integer.toString(startPosition)); + int maxRecords = 2; + qryParams.put(CAT3.MAX_RECORDS, Integer.toString(maxRecords)); + response = ClientUtils.buildGetRequest(this.getURI, qryParams, MediaType.APPLICATION_XML_TYPE); + Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode(), + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + Document entity = getResponseEntityAsDocument(response, null); + Node resultsNode = entity.getElementsByTagNameNS(Namespaces.CSW, "SearchResults").item(0); + Assert.assertNotNull(resultsNode, + ErrorMessage.format(ErrorMessageKeys.MISSING_INFOSET_ITEM, "csw:SearchResults")); + Element results = (Element) resultsNode; + Assert.assertEquals(results.getElementsByTagNameNS(Namespaces.CSW, "SummaryRecord").getLength(), maxRecords, + ErrorMessage.format(ErrorMessageKeys.RESULT_SET_SIZE, "csw:SummaryRecord")); + Assert.assertEquals(Integer.parseInt(results.getAttribute(CAT3.NUM_REC_RETURNED)), maxRecords, + ErrorMessage.format(ErrorMessageKeys.INFOSET_ITEM_VALUE, "@numberOfRecordsReturned")); + Assert.assertEquals(Integer.parseInt(results.getAttribute(CAT3.NEXT_REC)), startPosition + maxRecords, + ErrorMessage.format(ErrorMessageKeys.INFOSET_ITEM_VALUE, "@nextRecord")); + } + + /** + * [Test] Submits a GetRecords request where the startPosition and + * maxRecords parameters have non-default values. The atom:feed element + * in the response entity must present the correct "slice" of the result set. + * + * @see "OGC Catalogue Services 3.0 Specification - HTTP Protocol Binding, 7.3.6: Atom + * response" + * @see "OGC OpenSearch Geo and Time Extensions, Table 6" + */ + @Test(description = "Requirements: 082,084,122") + public void getPartialResultsAsAtomFeed() { + Map qryParams = new HashMap<>(); + qryParams.put(CAT3.REQUEST, CAT3.GET_RECORDS); + qryParams.put(CAT3.SERVICE, CAT3.SERVICE_TYPE_CODE); + qryParams.put(CAT3.VERSION, CAT3.VERSION_3_0_0); + qryParams.put(CAT3.NAMESPACE, String.format("xmlns(csw3=%s)", Namespaces.CSW)); + qryParams.put(CAT3.TYPE_NAMES, "csw3:Record"); + qryParams.put(CAT3.ELEMENT_SET, CAT3.ELEMENT_SET_SUMMARY); + qryParams.put(CAT3.OUTPUT_FORMAT, MediaType.APPLICATION_ATOM_XML); + int startPosition = 3; + qryParams.put(CAT3.START_POS, Integer.toString(startPosition)); + int maxRecords = 2; + qryParams.put(CAT3.MAX_RECORDS, Integer.toString(maxRecords)); + response = ClientUtils.buildGetRequest(this.getURI, qryParams, MediaType.APPLICATION_ATOM_XML_TYPE); + Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode(), + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + Document entity = getResponseEntityAsDocument(response, null); + Node feedNode = entity.getElementsByTagNameNS(Namespaces.ATOM, "feed").item(0); + Assert.assertNotNull(feedNode, ErrorMessage.format(ErrorMessageKeys.MISSING_INFOSET_ITEM, "atom:feed")); + Element feed = (Element) feedNode; + Assert.assertEquals(feed.getElementsByTagNameNS(Namespaces.ATOM, "entry").getLength(), maxRecords, + ErrorMessage.format(ErrorMessageKeys.RESULT_SET_SIZE, "atom:entry")); + Node startIndex = feed.getElementsByTagNameNS(Namespaces.OSD11, "startIndex").item(0); + Assert.assertNotNull(startIndex, ErrorMessage.format(ErrorMessageKeys.MISSING_INFOSET_ITEM, "os:startIndex")); + Assert.assertEquals(Integer.parseInt(startIndex.getTextContent()), startPosition, + ErrorMessage.format(ErrorMessageKeys.INFOSET_ITEM_VALUE, "os:startIndex")); + Node itemsPerPage = feed.getElementsByTagNameNS(Namespaces.OSD11, "itemsPerPage").item(0); + Assert.assertNotNull(itemsPerPage, + ErrorMessage.format(ErrorMessageKeys.MISSING_INFOSET_ITEM, "os:itemsPerPage")); + Assert.assertEquals(Integer.parseInt(itemsPerPage.getTextContent()), maxRecords, + ErrorMessage.format(ErrorMessageKeys.INFOSET_ITEM_VALUE, "os:itemsPerPage")); + } - /** - * [Test] Submits a GetRecords request where the startPosition - * and maxRecords parameters have non-default values. The - * atom:feed element in the response entity must present the correct "slice" - * of the result set. - * - * @see "OGC Catalogue Services 3.0 Specification - HTTP Protocol Binding, - * 7.3.6: Atom response" - * @see "OGC OpenSearch Geo and Time Extensions, Table 6" - */ - @Test(description = "Requirements: 082,084,122") - public void getPartialResultsAsAtomFeed() { - Map qryParams = new HashMap<>(); - qryParams.put(CAT3.REQUEST, CAT3.GET_RECORDS); - qryParams.put(CAT3.SERVICE, CAT3.SERVICE_TYPE_CODE); - qryParams.put(CAT3.VERSION, CAT3.VERSION_3_0_0); - qryParams.put(CAT3.NAMESPACE, String.format("xmlns(csw3=%s)", Namespaces.CSW)); - qryParams.put(CAT3.TYPE_NAMES, "csw3:Record"); - qryParams.put(CAT3.ELEMENT_SET, CAT3.ELEMENT_SET_SUMMARY); - qryParams.put(CAT3.OUTPUT_FORMAT, MediaType.APPLICATION_ATOM_XML); - int startPosition = 3; - qryParams.put(CAT3.START_POS, Integer.toString(startPosition)); - int maxRecords = 2; - qryParams.put(CAT3.MAX_RECORDS, Integer.toString(maxRecords)); - response = ClientUtils.buildGetRequest(this.getURI, qryParams, - MediaType.APPLICATION_ATOM_XML_TYPE); - Assert.assertEquals(response.getStatus(), - Response.Status.OK.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - Document entity = getResponseEntityAsDocument(response, null); - Node feedNode = entity.getElementsByTagNameNS(Namespaces.ATOM, "feed").item(0); - Assert.assertNotNull(feedNode, - ErrorMessage.format(ErrorMessageKeys.MISSING_INFOSET_ITEM, "atom:feed")); - Element feed = (Element) feedNode; - Assert.assertEquals(feed.getElementsByTagNameNS(Namespaces.ATOM, "entry").getLength(), - maxRecords, - ErrorMessage.format(ErrorMessageKeys.RESULT_SET_SIZE, "atom:entry")); - Node startIndex = feed.getElementsByTagNameNS(Namespaces.OSD11, "startIndex").item(0); - Assert.assertNotNull(startIndex, - ErrorMessage.format(ErrorMessageKeys.MISSING_INFOSET_ITEM, "os:startIndex")); - Assert.assertEquals(Integer.parseInt(startIndex.getTextContent()), - startPosition, - ErrorMessage.format(ErrorMessageKeys.INFOSET_ITEM_VALUE, "os:startIndex")); - Node itemsPerPage = feed.getElementsByTagNameNS(Namespaces.OSD11, "itemsPerPage").item(0); - Assert.assertNotNull(itemsPerPage, - ErrorMessage.format(ErrorMessageKeys.MISSING_INFOSET_ITEM, "os:itemsPerPage")); - Assert.assertEquals(Integer.parseInt(itemsPerPage.getTextContent()), - maxRecords, - ErrorMessage.format(ErrorMessageKeys.INFOSET_ITEM_VALUE, "os:itemsPerPage")); - } } diff --git a/src/main/java/org/opengis/cite/cat30/basic/BasicSearchTests.java b/src/main/java/org/opengis/cite/cat30/basic/BasicSearchTests.java index 0a632e3..950ad78 100644 --- a/src/main/java/org/opengis/cite/cat30/basic/BasicSearchTests.java +++ b/src/main/java/org/opengis/cite/cat30/basic/BasicSearchTests.java @@ -49,9 +49,8 @@ import net.sf.saxon.s9api.XdmValue; /** - * Includes GetRecords tests pertaining to the Filter-FES-KVP - * conformance class. The following basic search capabilities must be - * implemented: + * Includes GetRecords tests pertaining to the Filter-FES-KVP conformance + * class. The following basic search capabilities must be implemented: *
      *
    • Text search using the 'q' query parameter;
    • *
    • Record search using the 'recordIds' query parameter;
    • @@ -67,22 +66,27 @@ public class BasicSearchTests extends CommonFixture { * Service endpoint for GetRecords using the GET method. */ private URI getURI; + /** * An Envelope defining the total geographic extent of the sample data. */ private Envelope geoExtent; + /** * Information about the sample data retrieved from the IUT. */ private DatasetInfo datasetInfo; + /** * A list of record titles retrieved from the IUT. */ private List recordTitles; + /** * A list of record identifiers retrieved from the IUT. */ private List recordIdentifiers; + /** * A list of record topics retrieved from the IUT. */ @@ -97,36 +101,29 @@ void setGetEndpoint(URI uri) { } /** - * Finds the GET method endpoint for the GetRecords request in the - * capabilities document. - * - * @param testContext - * The test context containing various suite attributes. + * Finds the GET method endpoint for the GetRecords request in the capabilities + * document. + * @param testContext The test context containing various suite attributes. */ @BeforeClass public void findRequestEndpoints(ITestContext testContext) { - this.getURI = ServiceMetadataUtils.getOperationEndpoint( - this.cswCapabilities, CAT3.GET_RECORDS, HttpMethod.GET); + this.getURI = ServiceMetadataUtils.getOperationEndpoint(this.cswCapabilities, CAT3.GET_RECORDS, HttpMethod.GET); if (null == this.getURI.getScheme()) { - throw new SkipException( - "GET endpoint for GetRecords request not found."); + throw new SkipException("GET endpoint for GetRecords request not found."); } } /** - * Gets information about the sample data obtained from the IUT, including: - * its total geographic extent; a collection of record titles; and a set of - * record identifiers. Each csw:Record element may contain at least one - * ows:BoundingBox (or ows:WGS84BoundingBox) element that describes the - * spatial coverage of a catalogued resource. - * - * @param testContext - * The test context containing various suite attributes. + * Gets information about the sample data obtained from the IUT, including: its total + * geographic extent; a collection of record titles; and a set of record identifiers. + * Each csw:Record element may contain at least one ows:BoundingBox (or + * ows:WGS84BoundingBox) element that describes the spatial coverage of a catalogued + * resource. + * @param testContext The test context containing various suite attributes. */ @BeforeClass public void getDatasetInfo(ITestContext testContext) { - DatasetInfo dataset = (DatasetInfo) testContext.getSuite() - .getAttribute(SuiteAttribute.DATASET.getName()); + DatasetInfo dataset = (DatasetInfo) testContext.getSuite().getAttribute(SuiteAttribute.DATASET.getName()); if (null == dataset) { throw new SkipException("Dataset info not found in test context."); } @@ -139,10 +136,10 @@ public void getDatasetInfo(ITestContext testContext) { } /** - * [Test] Submits a GetRecords request with a BBOX parameter that includes - * an invalid CRS reference ("urn:ogc:def:crs:EPSG::0000"). An exception - * report is expected in response with HTTP status code 400 and exception - * code "{@value org.opengis.cite.cat30.CAT3#INVALID_PARAM_VAL}". + * [Test] Submits a GetRecords request with a BBOX parameter that includes an invalid + * CRS reference ("urn:ogc:def:crs:EPSG::0000"). An exception report is expected in + * response with HTTP status code 400 and exception code + * "{@value org.opengis.cite.cat30.CAT3#INVALID_PARAM_VAL}". * *

      * Sources @@ -160,19 +157,16 @@ public void getRecordsByBBOXWithUnsupportedCRS() { qryParams.put(CAT3.VERSION, CAT3.VERSION_3_0_0); qryParams.put(CAT3.TYPE_NAMES, "Record"); qryParams.put(CAT3.ELEMENT_SET, CAT3.ELEMENT_SET_BRIEF); - qryParams.put(CAT3.BBOX, - "472944,5363287,492722,5455253,urn:ogc:def:crs:EPSG::0000"); - response = ClientUtils.buildGetRequest(this.getURI, qryParams, - MediaType.APPLICATION_XML_TYPE); - ETSAssert.assertExceptionReport(response, CAT3.INVALID_PARAM_VAL, - CAT3.BBOX); + qryParams.put(CAT3.BBOX, "472944,5363287,492722,5455253,urn:ogc:def:crs:EPSG::0000"); + response = ClientUtils.buildGetRequest(this.getURI, qryParams, MediaType.APPLICATION_XML_TYPE); + ETSAssert.assertExceptionReport(response, CAT3.INVALID_PARAM_VAL, CAT3.BBOX); } /** - * [Test] Submits a GetRecords request with a BBOX parameter that includes a - * supported CRS reference. The brief records in the response must all - * contain an ows:BoundingBox (or ows:WGS84BoundingBox) element that - * intersects the specified bounding box. + * [Test] Submits a GetRecords request with a BBOX parameter that includes a supported + * CRS reference. The brief records in the response must all contain an + * ows:BoundingBox (or ows:WGS84BoundingBox) element that intersects the specified + * bounding box. * *

      * Sources @@ -187,8 +181,7 @@ public void getRecordsByBBOXWithUnsupportedCRS() { @Test(description = "Requirements: 017; Tests: 017") public void getBriefRecordsByBBOX() { if (null == this.geoExtent) { - throw new SkipException( - "Could not determine extent of sample data."); + throw new SkipException("Could not determine extent of sample data."); } Map qryParams = new HashMap<>(); qryParams.put(CAT3.REQUEST, CAT3.GET_RECORDS); @@ -198,10 +191,8 @@ public void getBriefRecordsByBBOX() { qryParams.put(CAT3.ELEMENT_SET, CAT3.ELEMENT_SET_BRIEF); Envelope bbox = this.geoExtent; qryParams.put(CAT3.BBOX, Extents.envelopeToString(bbox)); - response = ClientUtils.buildGetRequest(this.getURI, qryParams, - MediaType.APPLICATION_XML_TYPE); - Assert.assertEquals(response.getStatus(), - Response.Status.OK.getStatusCode(), + response = ClientUtils.buildGetRequest(this.getURI, qryParams, MediaType.APPLICATION_XML_TYPE); + Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode(), ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); Document entity = getResponseEntityAsDocument(response, null); Source results = new DOMSource(entity); @@ -209,10 +200,10 @@ public void getBriefRecordsByBBOX() { } /** - * [Test] Submits a GetRecords request with a 'bbox' parameter that uses the - * default CRS ("urn:ogc:def:crs:OGC:1.3:CRS84"). The summary records in the - * response must all contain an ows:BoundingBox (or ows:WGS84BoundingBox) - * element that intersects the specified bounding box. + * [Test] Submits a GetRecords request with a 'bbox' parameter that uses the default + * CRS ("urn:ogc:def:crs:OGC:1.3:CRS84"). The summary records in the response must all + * contain an ows:BoundingBox (or ows:WGS84BoundingBox) element that intersects the + * specified bounding box. * *

      * Sources @@ -227,8 +218,7 @@ public void getBriefRecordsByBBOX() { @Test(description = "Requirements: 017; Tests: 017") public void getSummaryRecordsByWGS84BBOX() { if (null == this.geoExtent) { - throw new SkipException( - "Could not determine extent of sample data."); + throw new SkipException("Could not determine extent of sample data."); } Map qryParams = new HashMap<>(); qryParams.put(CAT3.REQUEST, CAT3.GET_RECORDS); @@ -238,19 +228,16 @@ public void getSummaryRecordsByWGS84BBOX() { qryParams.put(CAT3.ELEMENT_SET, CAT3.ELEMENT_SET_SUMMARY); Envelope bbox = this.geoExtent; try { - if (!bbox.getCoordinateReferenceSystem().equals( - CommonCRS.WGS84.normalizedGeographic())) { - bbox = new GeneralEnvelope(Envelopes.transform(bbox, - CommonCRS.WGS84.normalizedGeographic())); + if (!bbox.getCoordinateReferenceSystem().equals(CommonCRS.WGS84.normalizedGeographic())) { + bbox = new GeneralEnvelope(Envelopes.transform(bbox, CommonCRS.WGS84.normalizedGeographic())); } - } catch (TransformException ex) { + } + catch (TransformException ex) { throw new RuntimeException("Failed to create WGS84 envelope.", ex); } qryParams.put(CAT3.BBOX, Extents.envelopeToString(bbox)); - response = ClientUtils.buildGetRequest(this.getURI, qryParams, - MediaType.APPLICATION_XML_TYPE); - Assert.assertEquals(response.getStatus(), - Response.Status.OK.getStatusCode(), + response = ClientUtils.buildGetRequest(this.getURI, qryParams, MediaType.APPLICATION_XML_TYPE); + Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode(), ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); Document entity = getResponseEntityAsDocument(response, null); Source results = new DOMSource(entity); @@ -258,9 +245,9 @@ public void getSummaryRecordsByWGS84BBOX() { } /** - * [Test] Submits a GetRecords request with a 'recordIds' parameter that - * contains a (comma-separated) list of two record identifiers. Two matching - * records (csw:SummaryRecord) are expected in the response. + * [Test] Submits a GetRecords request with a 'recordIds' parameter that contains a + * (comma-separated) list of two record identifiers. Two matching records + * (csw:SummaryRecord) are expected in the response. */ @Test(description = "OGC 12-176, Table 6 - Record search") public void getMultipleRecordsById() { @@ -278,40 +265,34 @@ public void getMultipleRecordsById() { idList.add(this.recordIdentifiers.get(this.recordIdentifiers.size() - 1)); paramValue.append(idList.get(1)); qryParams.put(CAT3.REC_ID_LIST, paramValue.toString()); - response = ClientUtils.buildGetRequest(this.getURI, qryParams, - MediaType.APPLICATION_XML_TYPE); - Assert.assertEquals(response.getStatus(), - Response.Status.OK.getStatusCode(), + response = ClientUtils.buildGetRequest(this.getURI, qryParams, MediaType.APPLICATION_XML_TYPE); + Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode(), ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); Document entity = getResponseEntityAsDocument(response, null); QName recordName = new QName(Namespaces.CSW, "SummaryRecord"); - NodeList recordList = entity.getElementsByTagNameNS( - recordName.getNamespaceURI(), recordName.getLocalPart()); - Assert.assertEquals(recordList.getLength(), 2, ErrorMessage.format( - ErrorMessageKeys.RESULT_SET_SIZE, recordName)); + NodeList recordList = entity.getElementsByTagNameNS(recordName.getNamespaceURI(), recordName.getLocalPart()); + Assert.assertEquals(recordList.getLength(), 2, + ErrorMessage.format(ErrorMessageKeys.RESULT_SET_SIZE, recordName)); for (int i = 0; i < recordList.getLength(); i++) { Element record = (Element) recordList.item(i); - NodeList identifiers = record.getElementsByTagNameNS( - Namespaces.DCMES, "identifier"); + NodeList identifiers = record.getElementsByTagNameNS(Namespaces.DCMES, "identifier"); List recIdList = XMLUtils.getNodeValues(identifiers); // retain common elements (intersection) recIdList.retainAll(idList); - Assert.assertFalse( - recIdList.isEmpty(), - ErrorMessage.format(ErrorMessageKeys.ID_NOT_FOUND, - Records.getRecordId(record))); + Assert.assertFalse(recIdList.isEmpty(), + ErrorMessage.format(ErrorMessageKeys.ID_NOT_FOUND, Records.getRecordId(record))); } } /** - * [Test] Submits a GetRecords request where the 'q' parameter value is a - * single URL-encoded term that occurs in at least one catalog record. The - * result set must not be empty. + * [Test] Submits a GetRecords request where the 'q' parameter value is a single + * URL-encoded term that occurs in at least one catalog record. The result set must + * not be empty. * *

      - * Note: According to Table 4 in OGC OpenSearch Geo and - * Time Extensions (OGC 10-032r8), the domain of keyword matching - * should include the record elements indicated below. + * Note: According to Table 4 in OGC OpenSearch Geo and Time + * Extensions (OGC 10-032r8), the domain of keyword matching should include the + * record elements indicated below. *

      * * @@ -367,32 +348,27 @@ public void singleTermTextSearch() { // remove any chars that may give rise to invalid XPath expression keyword = keyword.replaceAll("[()]", ""); qryParams.put(CAT3.Q, URIUtils.getPercentEncodedString(keyword)); - response = ClientUtils.buildGetRequest(this.getURI, qryParams, - MediaType.APPLICATION_XML_TYPE); - Assert.assertEquals(response.getStatus(), - Response.Status.OK.getStatusCode(), + response = ClientUtils.buildGetRequest(this.getURI, qryParams, MediaType.APPLICATION_XML_TYPE); + Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode(), ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); Document entity = getResponseEntityAsDocument(response, null); QName recordName = new QName(Namespaces.CSW, "Record"); - NodeList recordList = entity.getElementsByTagNameNS( - recordName.getNamespaceURI(), recordName.getLocalPart()); - Assert.assertTrue(recordList.getLength() > 0, ErrorMessage.format( - ErrorMessageKeys.EMPTY_RESULT_SET, recordName)); + NodeList recordList = entity.getElementsByTagNameNS(recordName.getNamespaceURI(), recordName.getLocalPart()); + Assert.assertTrue(recordList.getLength() > 0, + ErrorMessage.format(ErrorMessageKeys.EMPTY_RESULT_SET, recordName)); // NOTE: Spec does not indicate how records are matched // ETSAssert.assertAllTermsOccur(recordList, keyword); } /** - * [Test] Submits a GetRecords request where the q parameter - * contains a subject word and the maxRecords parameter value - * has the value "0" (zero). The csw:SearchResults element in the response - * must be empty. + * [Test] Submits a GetRecords request where the q parameter contains a + * subject word and the maxRecords parameter value has the value "0" + * (zero). The csw:SearchResults element in the response must be empty. */ @Test(description = "Requirements: 086") public void getRecordsBySubjectWithoutResults() { if (this.recordTopics.isEmpty()) { - throw new SkipException( - "No dc:subject elements found in sample records."); + throw new SkipException("No dc:subject elements found in sample records."); } Map qryParams = new HashMap<>(); qryParams.put(CAT3.REQUEST, CAT3.GET_RECORDS); @@ -401,38 +377,27 @@ public void getRecordsBySubjectWithoutResults() { qryParams.put(CAT3.TYPE_NAMES, "Record"); qryParams.put(CAT3.MAX_RECORDS, "0"); qryParams.put(CAT3.ELEMENT_SET, CAT3.ELEMENT_SET_SUMMARY); - int randomIndex = ThreadLocalRandom.current().nextInt( - this.recordTopics.size()); - String[] subjectWords = this.recordTopics.get(randomIndex) - .split("\\s+"); + int randomIndex = ThreadLocalRandom.current().nextInt(this.recordTopics.size()); + String[] subjectWords = this.recordTopics.get(randomIndex).split("\\s+"); String subject = subjectWords[subjectWords.length - 1]; qryParams.put(CAT3.Q, subject); - response = ClientUtils.buildGetRequest(this.getURI, qryParams, - MediaType.APPLICATION_XML_TYPE); - Assert.assertEquals(response.getStatus(), - Response.Status.OK.getStatusCode(), + response = ClientUtils.buildGetRequest(this.getURI, qryParams, MediaType.APPLICATION_XML_TYPE); + Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode(), ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); Document entity = getResponseEntityAsDocument(response, null); - Element results = (Element) entity.getElementsByTagNameNS( - Namespaces.CSW, "SearchResults").item(0); - Assert.assertNotNull(results, ErrorMessage.format( - ErrorMessageKeys.MISSING_INFOSET_ITEM, "csw:SearchResults")); + Element results = (Element) entity.getElementsByTagNameNS(Namespaces.CSW, "SearchResults").item(0); + Assert.assertNotNull(results, ErrorMessage.format(ErrorMessageKeys.MISSING_INFOSET_ITEM, "csw:SearchResults")); Assert.assertEquals(results.getChildNodes().getLength(), 0, - ErrorMessage.format(ErrorMessageKeys.RESULT_SET_SIZE, - "csw:SummaryRecord")); - Assert.assertEquals(Integer.parseInt(results - .getAttribute(CAT3.NUM_REC_RETURNED)), 0, ErrorMessage - .format(ErrorMessageKeys.INFOSET_ITEM_VALUE, - "@numberOfRecordsReturned")); - Assert.assertTrue(Integer.parseInt(results - .getAttribute(CAT3.NUM_REC_MATCHED)) > 0, ErrorMessage.format( - ErrorMessageKeys.CONSTRAINT_VIOLATION, - "numberOfRecordsMatched > 0")); + ErrorMessage.format(ErrorMessageKeys.RESULT_SET_SIZE, "csw:SummaryRecord")); + Assert.assertEquals(Integer.parseInt(results.getAttribute(CAT3.NUM_REC_RETURNED)), 0, + ErrorMessage.format(ErrorMessageKeys.INFOSET_ITEM_VALUE, "@numberOfRecordsReturned")); + Assert.assertTrue(Integer.parseInt(results.getAttribute(CAT3.NUM_REC_MATCHED)) > 0, + ErrorMessage.format(ErrorMessageKeys.CONSTRAINT_VIOLATION, "numberOfRecordsMatched > 0")); } /** - * [Test] Submits a GetRecords request where the 'q' parameter value is - * randomly generated text. The result set is expected to be empty. + * [Test] Submits a GetRecords request where the 'q' parameter value is randomly + * generated text. The result set is expected to be empty. */ @Test(description = "OGC 12-176, Table 6 - Text search") public void textSearchProducesEmptyResultSet() { @@ -443,20 +408,17 @@ public void textSearchProducesEmptyResultSet() { qryParams.put(CAT3.TYPE_NAMES, "Record"); qryParams.put(CAT3.ELEMENT_SET, CAT3.ELEMENT_SET_FULL); qryParams.put(CAT3.Q, Records.generateRandomText()); - response = ClientUtils.buildGetRequest(this.getURI, qryParams, - MediaType.APPLICATION_XML_TYPE); - Assert.assertEquals(response.getStatus(), - Response.Status.OK.getStatusCode(), + response = ClientUtils.buildGetRequest(this.getURI, qryParams, MediaType.APPLICATION_XML_TYPE); + Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode(), ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); Document entity = getResponseEntityAsDocument(response, null); ETSAssert.assertEmptyResultSet(entity); } /** - * [Test] This test submits a GetRecords request where the 'q' parameter - * value contains two terms. The terms are separated by a space character, - * and the parameter value as a whole is percent-encoded. The result set - * must not be empty. + * [Test] This test submits a GetRecords request where the 'q' parameter value + * contains two terms. The terms are separated by a space character, and the parameter + * value as a whole is percent-encoded. The result set must not be empty. */ @Test(description = "OGC 12-176, Table 6 - Text search") public void multipleTermTextSearch() { @@ -468,20 +430,16 @@ public void multipleTermTextSearch() { qryParams.put(CAT3.ELEMENT_SET, CAT3.ELEMENT_SET_SUMMARY); QName titleName = new QName(Namespaces.DCMES, "title"); QName subjectName = new QName(Namespaces.DCMES, "subject"); - String searchTerms = Records.findMatchingSearchTerms( - this.datasetInfo.getDataFile(), titleName, subjectName); + String searchTerms = Records.findMatchingSearchTerms(this.datasetInfo.getDataFile(), titleName, subjectName); qryParams.put(CAT3.Q, URIUtils.getPercentEncodedString(searchTerms)); - response = ClientUtils.buildGetRequest(this.getURI, qryParams, - MediaType.APPLICATION_XML_TYPE); - Assert.assertEquals(response.getStatus(), - Response.Status.OK.getStatusCode(), + response = ClientUtils.buildGetRequest(this.getURI, qryParams, MediaType.APPLICATION_XML_TYPE); + Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode(), ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); Document entity = getResponseEntityAsDocument(response, null); QName recordName = new QName(Namespaces.CSW, "SummaryRecord"); - NodeList recordList = entity.getElementsByTagNameNS( - recordName.getNamespaceURI(), recordName.getLocalPart()); - Assert.assertTrue(recordList.getLength() > 0, ErrorMessage.format( - ErrorMessageKeys.EMPTY_RESULT_SET, recordName)); + NodeList recordList = entity.getElementsByTagNameNS(recordName.getNamespaceURI(), recordName.getLocalPart()); + Assert.assertTrue(recordList.getLength() > 0, + ErrorMessage.format(ErrorMessageKeys.EMPTY_RESULT_SET, recordName)); // NOTE: Spec does not indicate how multiple terms are interpreted or // how records are matched // ETSAssert.assertAllTermsOccur(recordList, searchTerms.split("\\s+")); @@ -489,18 +447,16 @@ public void multipleTermTextSearch() { /** * [Test] Submits a GetRecords request containing the bbox and - * q parameters, where the latter matches one or more record - * titles. The response must include all records that satisfy both search - * criteria. + * q parameters, where the latter matches one or more record titles. The + * response must include all records that satisfy both search criteria. * - * @see "OGC Catalogue Services 3.0 Specification - HTTP Protocol Binding, - * 6.5.5.3: KVP encoding" + * @see "OGC Catalogue Services 3.0 Specification - HTTP Protocol Binding, 6.5.5.3: + * KVP encoding" */ @Test(description = "Requirements: Table 6") public void getRecordsByBBOXAndTitle() { if (null == this.geoExtent) { - throw new SkipException( - "Could not determine extent of sample data."); + throw new SkipException("Could not determine extent of sample data."); } int maxRecords = 15; Map qryParams = new HashMap<>(); @@ -512,56 +468,47 @@ public void getRecordsByBBOXAndTitle() { qryParams.put(CAT3.MAX_RECORDS, Integer.toString(maxRecords)); Envelope bbox = this.geoExtent; try { - if (!bbox.getCoordinateReferenceSystem().equals( - CommonCRS.WGS84.normalizedGeographic())) { - bbox = new GeneralEnvelope(Envelopes.transform(bbox, - CommonCRS.WGS84.normalizedGeographic())); + if (!bbox.getCoordinateReferenceSystem().equals(CommonCRS.WGS84.normalizedGeographic())) { + bbox = new GeneralEnvelope(Envelopes.transform(bbox, CommonCRS.WGS84.normalizedGeographic())); } - } catch (TransformException ex) { + } + catch (TransformException ex) { throw new RuntimeException("Failed to create WGS84 envelope.", ex); } qryParams.put(CAT3.BBOX, Extents.envelopeToString(bbox)); String titleWord = null; try { // get titles for records with bbox XdmValue titles = this.datasetInfo - .findItems( - "//csw:Record[ows:BoundingBox or ows:WGS84BoundingBox]/dc:title", - null); + .findItems("//csw:Record[ows:BoundingBox or ows:WGS84BoundingBox]/dc:title", null); for (XdmItem title : titles) { if (!title.getStringValue().isEmpty()) { - String[] titleWords = title.getStringValue().trim() - .split("\\s+"); + String[] titleWords = title.getStringValue().trim().split("\\s+"); titleWord = titleWords[0]; break; } } - } catch (SaxonApiException ex) { + } + catch (SaxonApiException ex) { throw new RuntimeException(ex.getMessage()); } // remove any chars that may give rise to invalid XPath expression titleWord = titleWord.replaceAll("[()]", ""); qryParams.put(CAT3.Q, titleWord); - response = ClientUtils.buildGetRequest(this.getURI, qryParams, - MediaType.APPLICATION_XML_TYPE); - Assert.assertEquals(response.getStatus(), - Response.Status.OK.getStatusCode(), + response = ClientUtils.buildGetRequest(this.getURI, qryParams, MediaType.APPLICATION_XML_TYPE); + Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode(), ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); Document entity = getResponseEntityAsDocument(response, null); - Element results = (Element) entity.getElementsByTagNameNS( - Namespaces.CSW, "SearchResults").item(0); - Assert.assertNotNull(results, ErrorMessage.format( - ErrorMessageKeys.MISSING_INFOSET_ITEM, "csw:SearchResults")); + Element results = (Element) entity.getElementsByTagNameNS(Namespaces.CSW, "SearchResults").item(0); + Assert.assertNotNull(results, ErrorMessage.format(ErrorMessageKeys.MISSING_INFOSET_ITEM, "csw:SearchResults")); String numReturned = results.getAttribute(CAT3.NUM_REC_RETURNED); Assert.assertTrue(Integer.parseInt(numReturned) <= maxRecords, - ErrorMessage.format(ErrorMessageKeys.CONSTRAINT_VIOLATION, - "numberOfRecordsReturned <= maxRecords")); - ETSAssert.assertEnvelopeIntersectsBoundingBoxes(bbox, new DOMSource( - results)); + ErrorMessage.format(ErrorMessageKeys.CONSTRAINT_VIOLATION, "numberOfRecordsReturned <= maxRecords")); + ETSAssert.assertEnvelopeIntersectsBoundingBoxes(bbox, new DOMSource(results)); QName recordName = new QName(Namespaces.CSW, "SummaryRecord"); - NodeList recordList = results.getElementsByTagNameNS( - recordName.getNamespaceURI(), recordName.getLocalPart()); - Assert.assertTrue(recordList.getLength() > 0, ErrorMessage.format( - ErrorMessageKeys.EMPTY_RESULT_SET, recordName)); + NodeList recordList = results.getElementsByTagNameNS(recordName.getNamespaceURI(), recordName.getLocalPart()); + Assert.assertTrue(recordList.getLength() > 0, + ErrorMessage.format(ErrorMessageKeys.EMPTY_RESULT_SET, recordName)); ETSAssert.assertAllTermsOccur(recordList, titleWord); } + } diff --git a/src/main/java/org/opengis/cite/cat30/basic/GetCapabilitiesTests.java b/src/main/java/org/opengis/cite/cat30/basic/GetCapabilitiesTests.java index 0d7cee2..74b9cff 100644 --- a/src/main/java/org/opengis/cite/cat30/basic/GetCapabilitiesTests.java +++ b/src/main/java/org/opengis/cite/cat30/basic/GetCapabilitiesTests.java @@ -33,17 +33,19 @@ import jakarta.ws.rs.core.Response; /** - * Provides tests pertaining to the GetCapabilities request. This - * request implements the abstract getCapabilities operation defined in - * the OGCWebService interface (OGC 06-121r9, Figure C.2). + * Provides tests pertaining to the GetCapabilities request. This request + * implements the abstract getCapabilities operation defined in the OGCWebService + * interface (OGC 06-121r9, Figure C.2). * *

      - * The KVP syntax must be supported; this encoding is generally used with the - * GET method but may also be used with the POST method. The media type of a KVP - * request entity is "application/x-www-form-urlencoded". + * The KVP syntax must be supported; this encoding is generally used with the GET method + * but may also be used with the POST method. The media type of a KVP request entity is + * "application/x-www-form-urlencoded". *

      * - *

      Sources

      + *

      + * Sources + *

      *
        *
      • OGC 06-121r9, 7: GetCapabilities operation
      • *
      • OGC 12-176r6, 7.1: GetCapabilities operation
      • @@ -51,304 +53,279 @@ */ public class GetCapabilitiesTests extends CommonFixture { - private static final String SCHEMATRON_CSW_CAPABILITIES - = ROOT_PKG_PATH + "sch/csw-capabilities-3.0.sch"; - /** - * Service endpoint for GetCapabilities using the GET method. - */ - private URI getCapabilitiesURI; + private static final String SCHEMATRON_CSW_CAPABILITIES = ROOT_PKG_PATH + "sch/csw-capabilities-3.0.sch"; + + /** + * Service endpoint for GetCapabilities using the GET method. + */ + private URI getCapabilitiesURI; + + /** + * Finds the GET method endpoint for the GetCapabilities request in the capabilities + * document. + */ + @BeforeClass + public void findServiceEndpoint() { + this.getCapabilitiesURI = ServiceMetadataUtils.getOperationEndpoint(this.cswCapabilities, CAT3.GET_CAPABILITIES, + HttpMethod.GET); + } - /** - * Finds the GET method endpoint for the GetCapabilities request in the - * capabilities document. - */ - @BeforeClass - public void findServiceEndpoint() { - this.getCapabilitiesURI = ServiceMetadataUtils.getOperationEndpoint( - this.cswCapabilities, CAT3.GET_CAPABILITIES, HttpMethod.GET); - } + /** + * Sets the service capabilities document. This method is intended to facilitate unit + * testing. + * @param cswCapabilities A Document node representing a service description + * (csw:Capabilities). + */ + public void setServiceCapabilities(Document cswCapabilities) { + this.cswCapabilities = cswCapabilities; + } - /** - * Sets the service capabilities document. This method is intended to - * facilitate unit testing. - * - * @param cswCapabilities A Document node representing a service description - * (csw:Capabilities). - */ - public void setServiceCapabilities(Document cswCapabilities) { - this.cswCapabilities = cswCapabilities; - } + /** + * [Test] Verifies that the content of a complete service capabilities document is + * schema-valid. All implementations must support the GET method for a GetCapabilities + * request. + *

        + * The Accept request header expresses a preference for a representation + * of type {@value javax.ws.rs.core.MediaType#APPLICATION_XML}. + *

        + * + * @see "OGC 06-121r9, 7.2.1: GetCapabilities request parameters" + */ + @Test(description = "Requirements: 043,045") + public void getFullCapabilitiesAcceptVersion3() { + Map qryParams = new HashMap<>(); + qryParams.put(CAT3.REQUEST, CAT3.GET_CAPABILITIES); + qryParams.put(CAT3.SERVICE, CAT3.SERVICE_TYPE_CODE); + qryParams.put(CAT3.ACCEPT_VERSIONS, CAT3.VERSION_3_0_0); + response = ClientUtils.buildGetRequest(this.getCapabilitiesURI, qryParams, MediaType.APPLICATION_XML_TYPE); + Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode(), + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + Source source = ClientUtils.getResponseEntityAsSource(response, null); + Validator validator = this.cswSchema.newValidator(); + ETSAssert.assertSchemaValid(validator, source); + URL schemaUrl = getClass().getResource(SCHEMATRON_CSW_CAPABILITIES); + ETSAssert.assertSchematronValid(schemaUrl, source); + } - /** - * [Test] Verifies that the content of a complete service capabilities - * document is schema-valid. All implementations must support the GET method - * for a GetCapabilities request. - *

        - * The Accept request header expresses a preference for a - * representation of type - * {@value javax.ws.rs.core.MediaType#APPLICATION_XML}. - *

        - * - * @see "OGC 06-121r9, 7.2.1: GetCapabilities request parameters" - */ - @Test(description = "Requirements: 043,045") - public void getFullCapabilitiesAcceptVersion3() { - Map qryParams = new HashMap<>(); - qryParams.put(CAT3.REQUEST, CAT3.GET_CAPABILITIES); - qryParams.put(CAT3.SERVICE, CAT3.SERVICE_TYPE_CODE); - qryParams.put(CAT3.ACCEPT_VERSIONS, CAT3.VERSION_3_0_0); - response = ClientUtils.buildGetRequest(this.getCapabilitiesURI, - qryParams, MediaType.APPLICATION_XML_TYPE); - Assert.assertEquals(response.getStatus(), - Response.Status.OK.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - Source source = ClientUtils.getResponseEntityAsSource(response, null); - Validator validator = this.cswSchema.newValidator(); - ETSAssert.assertSchemaValid(validator, source); - URL schemaUrl = getClass().getResource(SCHEMATRON_CSW_CAPABILITIES); - ETSAssert.assertSchematronValid(schemaUrl, source); - } + /** + * [Test] Attempts to retrieve the capabilities document from the base URL (endpoint + * for GetCapabilities via GET). The Accept header indicates that any media type is + * acceptable ("*/*"); this is equivalent to omitting the Accept header. The + * response shall include a complete XML representation. + * + * @see "OGC 12-176r6, 6.4: Obtaining service metadata" + */ + @Test(description = "Requirements: 006") + public void getCapabilitiesFromBaseURL() { + response = ClientUtils.buildGetRequest(this.getCapabilitiesURI, null, MediaType.WILDCARD_TYPE); + Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode(), + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + Source source = ClientUtils.getResponseEntityAsSource(response, null); + URL schURL = getClass().getResource(SCHEMATRON_CSW_CAPABILITIES); + ETSAssert.assertSchematronValid(schURL, source); + } - /** - * [Test] Attempts to retrieve the capabilities document from the base URL - * (endpoint for GetCapabilities via GET). The Accept header indicates that - * any media type is acceptable ("*/*"); this is equivalent to - * omitting the Accept header. The response shall include a complete XML - * representation. - * - * @see "OGC 12-176r6, 6.4: Obtaining service metadata" - */ - @Test(description = "Requirements: 006") - public void getCapabilitiesFromBaseURL() { - response = ClientUtils.buildGetRequest(this.getCapabilitiesURI, null, - MediaType.WILDCARD_TYPE); - Assert.assertEquals(response.getStatus(), - Response.Status.OK.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - Source source = ClientUtils.getResponseEntityAsSource(response, null); - URL schURL = getClass().getResource(SCHEMATRON_CSW_CAPABILITIES); - ETSAssert.assertSchematronValid(schURL, source); - } + /** + * [Test] Attempts to retrieve the capabilities document from the base URL (endpoint + * for GetCapabilities via GET). The Accept header indicates XML as the preferred + * media type: + * + *
        Accept: text/html; q=0.5, application/xml
        + * + * The response shall include a complete XML representation. + * + * @see "OGC 12-176r6, 6.4: Obtaining service metadata" + */ + @Test(description = "Requirements: 007") + public void getCapabilitiesFromBaseURLAsXML() { + response = ClientUtils.buildGetRequest(this.getCapabilitiesURI, null, + MediaType.valueOf(MediaType.TEXT_HTML + "; q=0.5"), MediaType.APPLICATION_XML_TYPE); + Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode(), + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + Source source = ClientUtils.getResponseEntityAsSource(response, null); + URL schURL = getClass().getResource(SCHEMATRON_CSW_CAPABILITIES); + ETSAssert.assertSchematronValid(schURL, source); + } - /** - * [Test] Attempts to retrieve the capabilities document from the base URL - * (endpoint for GetCapabilities via GET). The Accept header indicates XML - * as the preferred media type: - * - *
        Accept: text/html; q=0.5, application/xml
        - * - * The response shall include a complete XML representation. - * - * @see "OGC 12-176r6, 6.4: Obtaining service metadata" - */ - @Test(description = "Requirements: 007") - public void getCapabilitiesFromBaseURLAsXML() { - response = ClientUtils.buildGetRequest(this.getCapabilitiesURI, null, - MediaType.valueOf(MediaType.TEXT_HTML + "; q=0.5"), - MediaType.APPLICATION_XML_TYPE); - Assert.assertEquals(response.getStatus(), - Response.Status.OK.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - Source source = ClientUtils.getResponseEntityAsSource(response, null); - URL schURL = getClass().getResource(SCHEMATRON_CSW_CAPABILITIES); - ETSAssert.assertSchematronValid(schURL, source); - } + /** + * [Test] Query parameter names must be handled in a case-insensitive manner. The + * parameter names are all presented in mixed case; a complete capabilities document + * is expected in response. + * + * @see "OGC 12-176r5, 6.5.4: KVP encoding rules" + */ + @Test(description = "Requirements: 011") + public void getCapabilitiesWithMixedCaseParamNames() { + Map qryParams = new HashMap<>(); + qryParams.put("Request", CAT3.GET_CAPABILITIES); + qryParams.put("SERVICE", CAT3.SERVICE_TYPE_CODE); + qryParams.put("acceptversions", CAT3.VERSION_3_0_0); + response = ClientUtils.buildGetRequest(this.getCapabilitiesURI, qryParams, MediaType.APPLICATION_XML_TYPE); + Document doc = ClientUtils.getResponseEntityAsDocument(response, null); + QName qName = new QName(Namespaces.CSW, "Capabilities"); + ETSAssert.assertQualifiedName(doc.getDocumentElement(), qName); + } - /** - * [Test] Query parameter names must be handled in a case-insensitive - * manner. The parameter names are all presented in mixed case; a complete - * capabilities document is expected in response. - * - * @see "OGC 12-176r5, 6.5.4: KVP encoding rules" - */ - @Test(description = "Requirements: 011") - public void getCapabilitiesWithMixedCaseParamNames() { - Map qryParams = new HashMap<>(); - qryParams.put("Request", CAT3.GET_CAPABILITIES); - qryParams.put("SERVICE", CAT3.SERVICE_TYPE_CODE); - qryParams.put("acceptversions", CAT3.VERSION_3_0_0); - response = ClientUtils.buildGetRequest(this.getCapabilitiesURI, - qryParams, MediaType.APPLICATION_XML_TYPE); - Document doc = ClientUtils.getResponseEntityAsDocument(response, null); - QName qName = new QName(Namespaces.CSW, "Capabilities"); - ETSAssert.assertQualifiedName(doc.getDocumentElement(), qName); - } + /** + * [Test] Query parameter values must be handled in a case-sensitive manner. The + * request specifies request=getCapabilities; an exception report is + * expected in response with OGC exception code + * {@value org.opengis.cite.cat30.CAT3#INVALID_PARAM_VAL} and status code 400. + * + * @see "OGC 12-176r5, 6.5.4: KVP encoding rules" + * @see "OGC 06-121r9, Table 28: Standard exception codes and meanings" + */ + @Test(description = "Requirements: 012") + public void getCapabilitiesWithInvalidParamValue() { + Map qryParams = new HashMap<>(); + qryParams.put(CAT3.REQUEST, "getCapabilities"); + qryParams.put(CAT3.SERVICE, CAT3.SERVICE_TYPE_CODE); + qryParams.put(CAT3.ACCEPT_VERSIONS, CAT3.VERSION_3_0_0); + response = ClientUtils.buildGetRequest(this.getCapabilitiesURI, qryParams, MediaType.APPLICATION_XML_TYPE); + ETSAssert.assertExceptionReport(response, CAT3.INVALID_PARAM_VAL, CAT3.REQUEST); + } - /** - * [Test] Query parameter values must be handled in a case-sensitive manner. - * The request specifies request=getCapabilities; an exception - * report is expected in response with OGC exception code - * {@value org.opengis.cite.cat30.CAT3#INVALID_PARAM_VAL} and status code - * 400. - * - * @see "OGC 12-176r5, 6.5.4: KVP encoding rules" - * @see "OGC 06-121r9, Table 28: Standard exception codes and meanings" - */ - @Test(description = "Requirements: 012") - public void getCapabilitiesWithInvalidParamValue() { - Map qryParams = new HashMap<>(); - qryParams.put(CAT3.REQUEST, "getCapabilities"); - qryParams.put(CAT3.SERVICE, CAT3.SERVICE_TYPE_CODE); - qryParams.put(CAT3.ACCEPT_VERSIONS, CAT3.VERSION_3_0_0); - response = ClientUtils.buildGetRequest(this.getCapabilitiesURI, - qryParams, MediaType.APPLICATION_XML_TYPE); - ETSAssert.assertExceptionReport(response, CAT3.INVALID_PARAM_VAL, CAT3.REQUEST); - } + /** + * [Test] If the required "service" parameter is missing, an exception report with + * status code 400 must be produced. The expected OGC exception code is + * {@value org.opengis.cite.cat30.CAT3#MISSING_PARAM_VAL}. + * + * @see "OGC 12-176r5, Table 5: KVP encoding of common operation request parameters" + */ + @Test(description = "Requirements: 010") + public void getCapabilitiesIsMissingServiceParam() { + Map qryParams = new HashMap<>(); + qryParams.put(CAT3.REQUEST, CAT3.GET_CAPABILITIES); + qryParams.put(CAT3.ACCEPT_VERSIONS, CAT3.VERSION_3_0_0); + response = ClientUtils.buildGetRequest(this.getCapabilitiesURI, qryParams, MediaType.APPLICATION_XML_TYPE); + ETSAssert.assertExceptionReport(response, CAT3.MISSING_PARAM_VAL, CAT3.SERVICE); + } - /** - * [Test] If the required "service" parameter is missing, an exception - * report with status code 400 must be produced. The expected OGC exception - * code is {@value org.opengis.cite.cat30.CAT3#MISSING_PARAM_VAL}. - * - * @see "OGC 12-176r5, Table 5: KVP encoding of common operation request - * parameters" - */ - @Test(description = "Requirements: 010") - public void getCapabilitiesIsMissingServiceParam() { - Map qryParams = new HashMap<>(); - qryParams.put(CAT3.REQUEST, CAT3.GET_CAPABILITIES); - qryParams.put(CAT3.ACCEPT_VERSIONS, CAT3.VERSION_3_0_0); - response = ClientUtils.buildGetRequest(this.getCapabilitiesURI, - qryParams, MediaType.APPLICATION_XML_TYPE); - ETSAssert.assertExceptionReport(response, CAT3.MISSING_PARAM_VAL, CAT3.SERVICE); - } + /** + * [Test] Verifies that a request for an unsupported version of a capabilities + * document produces an exception report containing the exception code + * "VersionNegotiationFailed". + * + * The status code must be 400 (Bad Request) and the response entity must be an XML + * document having {http://www.opengis.net/ows/2.0}ExceptionReport as the document + * element. + * + * @see "OGC 06-121r9, 7.3.2: Version negotiation" + */ + @Test(description = "Requirements: 036,037,042") + public void getCapabilitiesWithUnsupportedVersion() { + Map qryParams = new HashMap<>(); + qryParams.put(CAT3.REQUEST, CAT3.GET_CAPABILITIES); + qryParams.put(CAT3.SERVICE, CAT3.SERVICE_TYPE_CODE); + qryParams.put(CAT3.ACCEPT_VERSIONS, "9999.12.31"); + response = ClientUtils.buildGetRequest(this.getCapabilitiesURI, qryParams, MediaType.APPLICATION_XML_TYPE); + ETSAssert.assertExceptionReport(response, CAT3.VER_NEGOTIATION_FAILED, CAT3.ACCEPT_VERSIONS); + } - /** - * [Test] Verifies that a request for an unsupported version of a - * capabilities document produces an exception report containing the - * exception code "VersionNegotiationFailed". - * - * The status code must be 400 (Bad Request) and the response entity must be - * an XML document having {http://www.opengis.net/ows/2.0}ExceptionReport as - * the document element. - * - * @see "OGC 06-121r9, 7.3.2: Version negotiation" - */ - @Test(description = "Requirements: 036,037,042") - public void getCapabilitiesWithUnsupportedVersion() { - Map qryParams = new HashMap<>(); - qryParams.put(CAT3.REQUEST, CAT3.GET_CAPABILITIES); - qryParams.put(CAT3.SERVICE, CAT3.SERVICE_TYPE_CODE); - qryParams.put(CAT3.ACCEPT_VERSIONS, "9999.12.31"); - response = ClientUtils.buildGetRequest(this.getCapabilitiesURI, - qryParams, MediaType.APPLICATION_XML_TYPE); - ETSAssert.assertExceptionReport(response, CAT3.VER_NEGOTIATION_FAILED, - CAT3.ACCEPT_VERSIONS); - } + /** + * [Test] Verifies that a request for a known but unsupported representation (format) + * of a capabilities document produces an exception report containing the exception + * code "InvalidParameterValue". + * + * The status code must be 400 (Bad Request) and the response entity must be an XML + * document having {http://www.opengis.net/ows/2.0}ExceptionReport as the document + * element. + * + * @see "OGC 06-121r9, Table 5: GetCapabilities operation request URL parameters" + */ + @Test(description = "Requirements: 036,037,042") + public void getCapabilitiesInUnsupportedFormat() { + Map qryParams = new HashMap<>(); + qryParams.put(CAT3.REQUEST, CAT3.GET_CAPABILITIES); + qryParams.put(CAT3.SERVICE, CAT3.SERVICE_TYPE_CODE); + qryParams.put(CAT3.ACCEPT_VERSIONS, CAT3.VERSION_3_0_0); + qryParams.put(CAT3.ACCEPT_FORMATS, "model/x3d+xml"); + response = ClientUtils.buildGetRequest(this.getCapabilitiesURI, qryParams, MediaType.WILDCARD_TYPE); + ETSAssert.assertExceptionReport(response, CAT3.INVALID_PARAM_VAL, CAT3.ACCEPT_FORMATS); + } - /** - * [Test] Verifies that a request for a known but unsupported representation - * (format) of a capabilities document produces an exception report - * containing the exception code "InvalidParameterValue". - * - * The status code must be 400 (Bad Request) and the response entity must be - * an XML document having {http://www.opengis.net/ows/2.0}ExceptionReport as - * the document element. - * - * @see "OGC 06-121r9, Table 5: GetCapabilities operation request URL - * parameters" - */ - @Test(description = "Requirements: 036,037,042") - public void getCapabilitiesInUnsupportedFormat() { - Map qryParams = new HashMap<>(); - qryParams.put(CAT3.REQUEST, CAT3.GET_CAPABILITIES); - qryParams.put(CAT3.SERVICE, CAT3.SERVICE_TYPE_CODE); - qryParams.put(CAT3.ACCEPT_VERSIONS, CAT3.VERSION_3_0_0); - qryParams.put(CAT3.ACCEPT_FORMATS, "model/x3d+xml"); - response = ClientUtils.buildGetRequest(this.getCapabilitiesURI, - qryParams, MediaType.WILDCARD_TYPE); - ETSAssert.assertExceptionReport(response, CAT3.INVALID_PARAM_VAL, - CAT3.ACCEPT_FORMATS); - } + /** + * [Test] Verifies that a request for a supported representation (format) of a + * capabilities document produces the expected response entity. All supported formats + * must be listed in the capabilities document as values of the "AcceptFormats" + * parameter. The media type "text/xml" must be supported, but it need not be + * explicitly listed. + * + * @see "OGC 06-121r9, 7.3.5: AcceptFormats parameter" + * @see "OGC 12-176, Table 15" + */ + @Test(description = "Requirements: 036,037,042") + public void getCapabilitiesInSupportedFormat() { + Map qryParams = new HashMap<>(); + qryParams.put(CAT3.REQUEST, CAT3.GET_CAPABILITIES); + qryParams.put(CAT3.SERVICE, CAT3.SERVICE_TYPE_CODE); + qryParams.put(CAT3.ACCEPT_VERSIONS, CAT3.VERSION_3_0_0); + Set allowedFormats = ServiceMetadataUtils.getParameterValues(cswCapabilities, CAT3.GET_CAPABILITIES, + CAT3.ACCEPT_FORMATS); + allowedFormats.add(MediaType.TEXT_XML); + for (String format : allowedFormats) { + qryParams.put(CAT3.ACCEPT_FORMATS, format); + response = ClientUtils.buildGetRequest(this.getCapabilitiesURI, qryParams, MediaType.WILDCARD_TYPE); + Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode(), + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + // ignore media type parameters + MediaType mediaType = new MediaType(response.getMediaType().getType(), + response.getMediaType().getSubtype()); + Assert.assertEquals(mediaType, MediaType.valueOf(format), + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_MEDIA_TYPE)); + } + } - /** - * [Test] Verifies that a request for a supported representation (format) of - * a capabilities document produces the expected response entity. All - * supported formats must be listed in the capabilities document as values - * of the "AcceptFormats" parameter. The media type "text/xml" must be - * supported, but it need not be explicitly listed. - * - * @see "OGC 06-121r9, 7.3.5: AcceptFormats parameter" - * @see "OGC 12-176, Table 15" - */ - @Test(description = "Requirements: 036,037,042") - public void getCapabilitiesInSupportedFormat() { - Map qryParams = new HashMap<>(); - qryParams.put(CAT3.REQUEST, CAT3.GET_CAPABILITIES); - qryParams.put(CAT3.SERVICE, CAT3.SERVICE_TYPE_CODE); - qryParams.put(CAT3.ACCEPT_VERSIONS, CAT3.VERSION_3_0_0); - Set allowedFormats = ServiceMetadataUtils.getParameterValues( - cswCapabilities, CAT3.GET_CAPABILITIES, CAT3.ACCEPT_FORMATS); - allowedFormats.add(MediaType.TEXT_XML); - for (String format : allowedFormats) { - qryParams.put(CAT3.ACCEPT_FORMATS, format); - response = ClientUtils.buildGetRequest(this.getCapabilitiesURI, - qryParams, MediaType.WILDCARD_TYPE); - Assert.assertEquals(response.getStatus(), - Response.Status.OK.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - // ignore media type parameters - MediaType mediaType = new MediaType(response.getMediaType().getType(), - response.getMediaType().getSubtype()); - Assert.assertEquals(mediaType, - MediaType.valueOf(format), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_MEDIA_TYPE)); - } - } + /** + * [Test] Verifies that a request for a part of a capabilities document produces the + * expected response entity. All recognized section names must be listed in the + * capabilities document as values of the "Sections" parameter; the value "All" + * indicates that a complete document is requested. + * + *

        + * The possible section names are listed below. + *

        + *
          + *
        • All
        • + *
        • ServiceIdentification
        • + *
        • ServiceProvider
        • + *
        • OperationsMetadata
        • + *
        • Filter_Capabilities
        • + *
        + * + * @see "OGC 06-121r9, 7.3.3: Sections parameter" + * @see "OGC 12-176, Table 12" + */ + @Test(description = "Requirements: 044") + public void getCapabilitiesBySection() { + Map qryParams = new HashMap<>(); + qryParams.put(CAT3.REQUEST, CAT3.GET_CAPABILITIES); + qryParams.put(CAT3.SERVICE, CAT3.SERVICE_TYPE_CODE); + qryParams.put(CAT3.ACCEPT_VERSIONS, CAT3.VERSION_3_0_0); + Set sections = ServiceMetadataUtils.getParameterValues(cswCapabilities, CAT3.GET_CAPABILITIES, + CAT3.SECTIONS); + sections.add("All"); + for (String section : sections) { + qryParams.put(CAT3.SECTIONS, section); + response = ClientUtils.buildGetRequest(this.getCapabilitiesURI, qryParams, MediaType.APPLICATION_XML_TYPE); + Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode(), + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + Source source = ClientUtils.getResponseEntityAsSource(response, null); + if (section.equals("All")) { + URL schemaUrl = getClass().getResource(SCHEMATRON_CSW_CAPABILITIES); + ETSAssert.assertSchematronValid(schemaUrl, source); + } + else { // check that only requested section appears + String xpath = String.format("count(/csw:Capabilities/*) = count(//*[local-name()='%s'])", section); + try { + Boolean result = (Boolean) XMLUtils.evaluateXPath(source, xpath, null, XPathConstants.BOOLEAN); + Assert.assertTrue(result, ErrorMessage.format(ErrorMessageKeys.XPATH_ERROR, xpath)); + } + catch (XPathExpressionException ex) { + TestSuiteLogger.log(Level.WARNING, ex.getMessage()); + } + } + } + } - /** - * [Test] Verifies that a request for a part of a capabilities document - * produces the expected response entity. All recognized section names must - * be listed in the capabilities document as values of the "Sections" - * parameter; the value "All" indicates that a complete document is - * requested. - * - *

        - * The possible section names are listed below.

        - *
          - *
        • All
        • - *
        • ServiceIdentification
        • - *
        • ServiceProvider
        • - *
        • OperationsMetadata
        • - *
        • Filter_Capabilities
        • - *
        - * - * @see "OGC 06-121r9, 7.3.3: Sections parameter" - * @see "OGC 12-176, Table 12" - */ - @Test(description = "Requirements: 044") - public void getCapabilitiesBySection() { - Map qryParams = new HashMap<>(); - qryParams.put(CAT3.REQUEST, CAT3.GET_CAPABILITIES); - qryParams.put(CAT3.SERVICE, CAT3.SERVICE_TYPE_CODE); - qryParams.put(CAT3.ACCEPT_VERSIONS, CAT3.VERSION_3_0_0); - Set sections = ServiceMetadataUtils.getParameterValues( - cswCapabilities, CAT3.GET_CAPABILITIES, CAT3.SECTIONS); - sections.add("All"); - for (String section : sections) { - qryParams.put(CAT3.SECTIONS, section); - response = ClientUtils.buildGetRequest(this.getCapabilitiesURI, - qryParams, MediaType.APPLICATION_XML_TYPE); - Assert.assertEquals(response.getStatus(), - Response.Status.OK.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - Source source = ClientUtils.getResponseEntityAsSource(response, null); - if (section.equals("All")) { - URL schemaUrl = getClass().getResource(SCHEMATRON_CSW_CAPABILITIES); - ETSAssert.assertSchematronValid(schemaUrl, source); - } else { // check that only requested section appears - String xpath = String.format( - "count(/csw:Capabilities/*) = count(//*[local-name()='%s'])", - section); - try { - Boolean result = (Boolean) XMLUtils.evaluateXPath( - source, xpath, null, XPathConstants.BOOLEAN); - Assert.assertTrue(result, ErrorMessage.format( - ErrorMessageKeys.XPATH_ERROR, xpath)); - } catch (XPathExpressionException ex) { - TestSuiteLogger.log(Level.WARNING, ex.getMessage()); - } - } - } - } } diff --git a/src/main/java/org/opengis/cite/cat30/basic/GetRecordByIdTests.java b/src/main/java/org/opengis/cite/cat30/basic/GetRecordByIdTests.java index c2f0258..4f704b3 100644 --- a/src/main/java/org/opengis/cite/cat30/basic/GetRecordByIdTests.java +++ b/src/main/java/org/opengis/cite/cat30/basic/GetRecordByIdTests.java @@ -39,16 +39,15 @@ import jakarta.ws.rs.core.Response; /** - * Provides tests that apply to the GetRecordById request. This - * request implements the abstract GetResourceByID operation defined in - * the OGCWebService interface (OGC 06-121r9, Figure C.2). + * Provides tests that apply to the GetRecordById request. This request + * implements the abstract GetResourceByID operation defined in the OGCWebService + * interface (OGC 06-121r9, Figure C.2). * *

        - * The KVP syntax must be supported; this encoding is generally used with the - * GET method but may also be used with the POST method; this latter capability - * will be advertised in the capabilities document as an operational constraint - * as indicated below. The media type of a KVP request entity is - * "application/x-www-form-urlencoded". + * The KVP syntax must be supported; this encoding is generally used with the GET method + * but may also be used with the POST method; this latter capability will be advertised in + * the capabilities document as an operational constraint as indicated below. The media + * type of a KVP request entity is "application/x-www-form-urlencoded". *

        * *
        {@literal
        @@ -64,7 +63,9 @@
          *}
          * 
        * - *

        Sources

        + *

        + * Sources + *

        *
          *
        • OGC 12-176r6, 7.4: GetRecordById operation
        • *
        • OGC 12-176r6, Table 16: Operation constraints
        • @@ -73,328 +74,299 @@ */ public class GetRecordByIdTests extends CommonFixture { - /** - * Service endpoint for GetRecordById using the GET method. - */ - private URI getURI; - /** - * Service endpoint for GetRecordById using the POST method. - */ - private URI postURI; - /** - * A list of record identifiers retrieved from the SUT. - */ - private List idList; + /** + * Service endpoint for GetRecordById using the GET method. + */ + private URI getURI; + + /** + * Service endpoint for GetRecordById using the POST method. + */ + private URI postURI; + + /** + * A list of record identifiers retrieved from the SUT. + */ + private List idList; + + void setIdList(List idList) { + this.idList = idList; + } - void setIdList(List idList) { - this.idList = idList; - } + /** + * Finds the GET and POST method endpoints for the GetCapabilities request in the + * capabilities document. + */ + @BeforeClass + public void findRequestEndpoints() { + this.getURI = ServiceMetadataUtils.getOperationEndpoint(this.cswCapabilities, CAT3.GET_RECORD_BY_ID, + HttpMethod.GET); + this.postURI = ServiceMetadataUtils.getOperationEndpoint(this.cswCapabilities, CAT3.GET_RECORD_BY_ID, + HttpMethod.POST); + } - /** - * Finds the GET and POST method endpoints for the GetCapabilities request - * in the capabilities document. - */ - @BeforeClass - public void findRequestEndpoints() { - this.getURI = ServiceMetadataUtils.getOperationEndpoint( - this.cswCapabilities, CAT3.GET_RECORD_BY_ID, HttpMethod.GET); - this.postURI = ServiceMetadataUtils.getOperationEndpoint( - this.cswCapabilities, CAT3.GET_RECORD_BY_ID, HttpMethod.POST); - } + /** + * Gets the record identifiers that occur in the sample data obtained from the SUT. + * Each csw:Record element must contain at least one dc:identifier element. + * @param testContext The test context containing various suite attributes. + */ + @BeforeClass + public void getRecordIdentifiers(ITestContext testContext) { + DatasetInfo dataset = (DatasetInfo) testContext.getSuite().getAttribute(SuiteAttribute.DATASET.getName()); + if (null == dataset) { + throw new SkipException("Dataset info not found in test context."); + } + List identifiers = dataset.getRecordIdentifiers(); + if (identifiers.isEmpty()) { + throw new SkipException("No dc:identifier elements found in sample data."); + } + setIdList(identifiers); + } - /** - * Gets the record identifiers that occur in the sample data obtained from - * the SUT. Each csw:Record element must contain at least one dc:identifier - * element. - * - * @param testContext The test context containing various suite attributes. - */ - @BeforeClass - public void getRecordIdentifiers(ITestContext testContext) { - DatasetInfo dataset = (DatasetInfo) testContext.getSuite().getAttribute( - SuiteAttribute.DATASET.getName()); - if (null == dataset) { - throw new SkipException("Dataset info not found in test context."); - } - List identifiers = dataset.getRecordIdentifiers(); - if (identifiers.isEmpty()) { - throw new SkipException("No dc:identifier elements found in sample data."); - } - setIdList(identifiers); - } + /** + * [Test] Verifies that a request for a record by identifier produces a response with + * status code 404 (Not Found) if no matching resource representation is found. A + * response entity (an exception report) is optional; if present, the exception code + * shall be "InvalidParameterValue". + * + * @see "OGC 12-176r6, 7.4.4.2: 7.4.4.2 Id parameter" + */ + @Test(description = "Requirements: 127,141") + public void getRecordById_noMatch() { + Map qryParams = new HashMap<>(); + qryParams.put(CAT3.REQUEST, CAT3.GET_RECORD_BY_ID); + qryParams.put(CAT3.SERVICE, CAT3.SERVICE_TYPE_CODE); + qryParams.put(CAT3.VERSION, CAT3.VERSION_3_0_0); + qryParams.put(CAT3.ID, "urn:example:" + System.currentTimeMillis()); + response = ClientUtils.buildGetRequest(this.getURI, qryParams, MediaType.APPLICATION_XML_TYPE); + Assert.assertEquals(response.getStatus(), Response.Status.NOT_FOUND.getStatusCode(), + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + } - /** - * [Test] Verifies that a request for a record by identifier produces a - * response with status code 404 (Not Found) if no matching resource - * representation is found. A response entity (an exception report) is - * optional; if present, the exception code shall be - * "InvalidParameterValue". - * - * @see "OGC 12-176r6, 7.4.4.2: 7.4.4.2 Id parameter" - */ - @Test(description = "Requirements: 127,141") - public void getRecordById_noMatch() { - Map qryParams = new HashMap<>(); - qryParams.put(CAT3.REQUEST, CAT3.GET_RECORD_BY_ID); - qryParams.put(CAT3.SERVICE, CAT3.SERVICE_TYPE_CODE); - qryParams.put(CAT3.VERSION, CAT3.VERSION_3_0_0); - qryParams.put(CAT3.ID, "urn:example:" + System.currentTimeMillis()); - response = ClientUtils.buildGetRequest(this.getURI, - qryParams, MediaType.APPLICATION_XML_TYPE); - Assert.assertEquals(response.getStatus(), - Response.Status.NOT_FOUND.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - } + /** + * [Test] Verifies that a request for a record by identifier produces a matching + * csw:SummaryRecord in the response entity. The default view (element set) is + * "summary". The default output schema is identified by the namespace name + * {@value org.opengis.cite.cat30.Namespaces#CSW}. + * + * @see "OGC 12-176r6, 7.4.4.2: Id parameter" + * @see "OGC 12-176r6, 7.4.5: Response" + */ + @Test(description = "Requirements: 124,134") + public void getSummaryRecordById() { + Map qryParams = new HashMap<>(); + qryParams.put(CAT3.REQUEST, CAT3.GET_RECORD_BY_ID); + qryParams.put(CAT3.SERVICE, CAT3.SERVICE_TYPE_CODE); + qryParams.put(CAT3.VERSION, CAT3.VERSION_3_0_0); + int randomIndex = ThreadLocalRandom.current().nextInt(this.idList.size()); + String id = this.idList.get(randomIndex); + qryParams.put(CAT3.ID, id); + response = ClientUtils.buildGetRequest(this.getURI, qryParams, MediaType.APPLICATION_XML_TYPE); + Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode(), + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + Document entity = ClientUtils.getResponseEntityAsDocument(response, null); + String expr = String.format("/csw:SummaryRecord/dc:identifier = '%s'", id); + ETSAssert.assertXPath(expr, entity, null); + } - /** - * [Test] Verifies that a request for a record by identifier produces a - * matching csw:SummaryRecord in the response entity. The default view - * (element set) is "summary". The default output schema is identified by - * the namespace name {@value org.opengis.cite.cat30.Namespaces#CSW}. - * - * @see "OGC 12-176r6, 7.4.4.2: Id parameter" - * @see "OGC 12-176r6, 7.4.5: Response" - */ - @Test(description = "Requirements: 124,134") - public void getSummaryRecordById() { - Map qryParams = new HashMap<>(); - qryParams.put(CAT3.REQUEST, CAT3.GET_RECORD_BY_ID); - qryParams.put(CAT3.SERVICE, CAT3.SERVICE_TYPE_CODE); - qryParams.put(CAT3.VERSION, CAT3.VERSION_3_0_0); - int randomIndex = ThreadLocalRandom.current().nextInt(this.idList.size()); - String id = this.idList.get(randomIndex); - qryParams.put(CAT3.ID, id); - response = ClientUtils.buildGetRequest(this.getURI, - qryParams, MediaType.APPLICATION_XML_TYPE); - Assert.assertEquals(response.getStatus(), - Response.Status.OK.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - Document entity = ClientUtils.getResponseEntityAsDocument(response, null); - String expr = String.format("/csw:SummaryRecord/dc:identifier = '%s'", - id); - ETSAssert.assertXPath(expr, entity, null); - } + /** + * [Test] Verifies that a request for a brief record by identifier produces a matching + * csw:BriefRecord in the response entity. The entity must be schema-valid. + * + * @see "OGC 12-176r6, 7.4.4.1: ElementSetName parameter" + */ + @Test(description = "Requirements: 123") + public void getBriefRecordById() { + Map qryParams = new HashMap<>(); + qryParams.put(CAT3.REQUEST, CAT3.GET_RECORD_BY_ID); + qryParams.put(CAT3.SERVICE, CAT3.SERVICE_TYPE_CODE); + qryParams.put(CAT3.VERSION, CAT3.VERSION_3_0_0); + qryParams.put(CAT3.ELEMENT_SET, CAT3.ELEMENT_SET_BRIEF); + int randomIndex = ThreadLocalRandom.current().nextInt(this.idList.size()); + String id = this.idList.get(randomIndex); + qryParams.put(CAT3.ID, id); + response = ClientUtils.buildGetRequest(this.getURI, qryParams, MediaType.APPLICATION_XML_TYPE); + Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode(), + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + Document entity = ClientUtils.getResponseEntityAsDocument(response, null); + String expr = String.format("/csw:BriefRecord/dc:identifier = '%s'", id); + ETSAssert.assertXPath(expr, entity, null); + Validator validator = this.cswSchema.newValidator(); + ETSAssert.assertSchemaValid(validator, new DOMSource(entity)); + } - /** - * [Test] Verifies that a request for a brief record by identifier produces - * a matching csw:BriefRecord in the response entity. The entity must be - * schema-valid. - * - * @see "OGC 12-176r6, 7.4.4.1: ElementSetName parameter" - */ - @Test(description = "Requirements: 123") - public void getBriefRecordById() { - Map qryParams = new HashMap<>(); - qryParams.put(CAT3.REQUEST, CAT3.GET_RECORD_BY_ID); - qryParams.put(CAT3.SERVICE, CAT3.SERVICE_TYPE_CODE); - qryParams.put(CAT3.VERSION, CAT3.VERSION_3_0_0); - qryParams.put(CAT3.ELEMENT_SET, CAT3.ELEMENT_SET_BRIEF); - int randomIndex = ThreadLocalRandom.current().nextInt(this.idList.size()); - String id = this.idList.get(randomIndex); - qryParams.put(CAT3.ID, id); - response = ClientUtils.buildGetRequest(this.getURI, - qryParams, MediaType.APPLICATION_XML_TYPE); - Assert.assertEquals(response.getStatus(), - Response.Status.OK.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - Document entity = ClientUtils.getResponseEntityAsDocument(response, null); - String expr = String.format("/csw:BriefRecord/dc:identifier = '%s'", - id); - ETSAssert.assertXPath(expr, entity, null); - Validator validator = this.cswSchema.newValidator(); - ETSAssert.assertSchemaValid(validator, new DOMSource(entity)); - } + /** + * [Test] Verifies that a request for a full record by identifier produces a matching + * csw:Record in the response entity. The entity must be schema-valid. + * + * @see "OGC 12-176r6, 7.4.4.1: ElementSetName parameter" + */ + @Test(description = "Requirements: 123") + public void getFullRecordById() { + Map qryParams = new HashMap<>(); + qryParams.put(CAT3.REQUEST, CAT3.GET_RECORD_BY_ID); + qryParams.put(CAT3.SERVICE, CAT3.SERVICE_TYPE_CODE); + qryParams.put(CAT3.VERSION, CAT3.VERSION_3_0_0); + qryParams.put(CAT3.ELEMENT_SET, CAT3.ELEMENT_SET_FULL); + int randomIndex = ThreadLocalRandom.current().nextInt(this.idList.size()); + String id = this.idList.get(randomIndex); + qryParams.put(CAT3.ID, id); + response = ClientUtils.buildGetRequest(this.getURI, qryParams, MediaType.APPLICATION_XML_TYPE); + Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode(), + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + Document entity = ClientUtils.getResponseEntityAsDocument(response, null); + String expr = String.format("/csw:Record/dc:identifier = '%s'", id); + ETSAssert.assertXPath(expr, entity, null); + Validator validator = this.cswSchema.newValidator(); + ETSAssert.assertSchemaValid(validator, new DOMSource(entity)); + } - /** - * [Test] Verifies that a request for a full record by identifier produces a - * matching csw:Record in the response entity. The entity must be - * schema-valid. - * - * @see "OGC 12-176r6, 7.4.4.1: ElementSetName parameter" - */ - @Test(description = "Requirements: 123") - public void getFullRecordById() { - Map qryParams = new HashMap<>(); - qryParams.put(CAT3.REQUEST, CAT3.GET_RECORD_BY_ID); - qryParams.put(CAT3.SERVICE, CAT3.SERVICE_TYPE_CODE); - qryParams.put(CAT3.VERSION, CAT3.VERSION_3_0_0); - qryParams.put(CAT3.ELEMENT_SET, CAT3.ELEMENT_SET_FULL); - int randomIndex = ThreadLocalRandom.current().nextInt(this.idList.size()); - String id = this.idList.get(randomIndex); - qryParams.put(CAT3.ID, id); - response = ClientUtils.buildGetRequest(this.getURI, - qryParams, MediaType.APPLICATION_XML_TYPE); - Assert.assertEquals(response.getStatus(), - Response.Status.OK.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - Document entity = ClientUtils.getResponseEntityAsDocument(response, null); - String expr = String.format("/csw:Record/dc:identifier = '%s'", - id); - ETSAssert.assertXPath(expr, entity, null); - Validator validator = this.cswSchema.newValidator(); - ETSAssert.assertSchemaValid(validator, new DOMSource(entity)); - } + /** + * [Test] Verifies that a request for an Atom representation of a record produces a + * matching atom:entry in the response entity. The Accept request header + * indicates a preference for Atom content; the outputFormat parameter is omitted + * (thus the header value applies). The content of the entry must conform to RFC 4287. + * + *

          + * The atom:entry element is expected to include a dc:identifier element in accord + * with the mappings given in OGC 10-032r8, Table 7. + *

          + * + *
          {@literal
          +	 *
          +	 *  http://csw.example.org/record/ff711198-b30f-11e4-a71e-12e3f512a338
          +	 *  Title
          +	 *  2015-02-12T23:46:57Z
          +	 *  ff711198-b30f-11e4-a71e-12e3f512a338
          +	 *
          +	 *}
          +	 * 
          + * + *

          + * Sources + *

          + *
            + *
          • OGC 12-176r6, 7.4.4.4: outputSchema parameter
          • + *
          • OGC 10-032r8, 9.3.2: Normal response XML encoding
          • + *
          • RFC 4287: The + * Atom Syndication Format
          • + *
          + */ + @Test(description = "Requirements: 003,139,140") + public void getRecordByIdAsAtomEntryUsingAcceptHeader() { + Map qryParams = new HashMap<>(); + qryParams.put(CAT3.REQUEST, CAT3.GET_RECORD_BY_ID); + qryParams.put(CAT3.SERVICE, CAT3.SERVICE_TYPE_CODE); + qryParams.put(CAT3.VERSION, CAT3.VERSION_3_0_0); + int randomIndex = ThreadLocalRandom.current().nextInt(this.idList.size()); + String id = this.idList.get(randomIndex); + qryParams.put(CAT3.ID, id); + response = buildGetRequest(this.getURI, qryParams, MediaType.APPLICATION_ATOM_XML_TYPE); + Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode(), + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + Document entity = getResponseEntityAsDocument(response, null); + Map nsBindings = Collections.singletonMap(Namespaces.ATOM, "atom"); + String expr = String.format("/atom:entry/dc:identifier = '%s'", id); + ETSAssert.assertXPath(expr, entity, nsBindings); + Validator atomValidator = this.atomSchema.newValidator(); + ValidationErrorHandler err = new ValidationErrorHandler(); + atomValidator.setErrorHandler(err); + try { + // Jing Validator implementation rejects DOMSource as input + Source src = XMLUtils.toStreamSource(new DOMSource(entity)); + atomValidator.validate(src); + } + catch (SAXException | IOException ex) { + Logger.getLogger(GetRecordByIdTests.class.getName()) + .log(Level.WARNING, "Error attempting to validate Atom entry.", ex); + } + Assert.assertFalse(err.errorsDetected(), + ErrorMessage.format(ErrorMessageKeys.NOT_SCHEMA_VALID, err.getErrorCount(), err.toString())); + } - /** - * [Test] Verifies that a request for an Atom representation of a record - * produces a matching atom:entry in the response entity. The - * Accept request header indicates a preference for Atom - * content; the outputFormat parameter is omitted (thus the header value - * applies). The content of the entry must conform to RFC 4287. - * - *

          - * The atom:entry element is expected to include a dc:identifier element in - * accord with the mappings given in OGC 10-032r8, Table 7. - *

          - * - *
          {@literal
          -     *
          -     *  http://csw.example.org/record/ff711198-b30f-11e4-a71e-12e3f512a338
          -     *  Title
          -     *  2015-02-12T23:46:57Z
          -     *  ff711198-b30f-11e4-a71e-12e3f512a338
          -     *
          -     *}
          -     * 
          - * - *

          Sources

          - *
            - *
          • OGC 12-176r6, 7.4.4.4: outputSchema parameter
          • - *
          • OGC 10-032r8, 9.3.2: Normal response XML encoding
          • - *
          • RFC - * 4287: The Atom Syndication Format
          • - *
          - */ - @Test(description = "Requirements: 003,139,140") - public void getRecordByIdAsAtomEntryUsingAcceptHeader() { - Map qryParams = new HashMap<>(); - qryParams.put(CAT3.REQUEST, CAT3.GET_RECORD_BY_ID); - qryParams.put(CAT3.SERVICE, CAT3.SERVICE_TYPE_CODE); - qryParams.put(CAT3.VERSION, CAT3.VERSION_3_0_0); - int randomIndex = ThreadLocalRandom.current().nextInt(this.idList.size()); - String id = this.idList.get(randomIndex); - qryParams.put(CAT3.ID, id); - response = buildGetRequest(this.getURI, qryParams, - MediaType.APPLICATION_ATOM_XML_TYPE); - Assert.assertEquals(response.getStatus(), - Response.Status.OK.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - Document entity = getResponseEntityAsDocument(response, null); - Map nsBindings = Collections.singletonMap(Namespaces.ATOM, "atom"); - String expr = String.format("/atom:entry/dc:identifier = '%s'", - id); - ETSAssert.assertXPath(expr, entity, nsBindings); - Validator atomValidator = this.atomSchema.newValidator(); - ValidationErrorHandler err = new ValidationErrorHandler(); - atomValidator.setErrorHandler(err); - try { - // Jing Validator implementation rejects DOMSource as input - Source src = XMLUtils.toStreamSource(new DOMSource(entity)); - atomValidator.validate(src); - } catch (SAXException | IOException ex) { - Logger.getLogger(GetRecordByIdTests.class.getName()).log( - Level.WARNING, "Error attempting to validate Atom entry.", ex); - } - Assert.assertFalse(err.errorsDetected(), - ErrorMessage.format(ErrorMessageKeys.NOT_SCHEMA_VALID, - err.getErrorCount(), err.toString())); - } + /** + * [Test] Verifies that a request for an Atom representation of a record produces a + * matching atom:entry in the response entity. The outputFormat query parameter value + * ("application/atom+xml") overrides the Accept request header ("application/xml"). + */ + @Test(description = "Requirements: 002,135,140") + public void getRecordByIdAsAtomEntryUsingOutputFormat() { + Map qryParams = new HashMap<>(); + qryParams.put(CAT3.REQUEST, CAT3.GET_RECORD_BY_ID); + qryParams.put(CAT3.SERVICE, CAT3.SERVICE_TYPE_CODE); + qryParams.put(CAT3.VERSION, CAT3.VERSION_3_0_0); + qryParams.put(CAT3.OUTPUT_FORMAT, MediaType.APPLICATION_ATOM_XML); + int randomIndex = ThreadLocalRandom.current().nextInt(this.idList.size()); + String id = this.idList.get(randomIndex); + qryParams.put(CAT3.ID, id); + response = ClientUtils.buildGetRequest(this.getURI, qryParams, MediaType.APPLICATION_XML_TYPE); + Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode(), + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + Document entity = ClientUtils.getResponseEntityAsDocument(response, null); + Map nsBindings = Collections.singletonMap(Namespaces.ATOM, "atom"); + String expr = String.format("/atom:entry/dc:identifier = '%s'", id); + ETSAssert.assertXPath(expr, entity, nsBindings); + } - /** - * [Test] Verifies that a request for an Atom representation of a record - * produces a matching atom:entry in the response entity. The outputFormat - * query parameter value ("application/atom+xml") overrides the Accept - * request header ("application/xml"). - */ - @Test(description = "Requirements: 002,135,140") - public void getRecordByIdAsAtomEntryUsingOutputFormat() { - Map qryParams = new HashMap<>(); - qryParams.put(CAT3.REQUEST, CAT3.GET_RECORD_BY_ID); - qryParams.put(CAT3.SERVICE, CAT3.SERVICE_TYPE_CODE); - qryParams.put(CAT3.VERSION, CAT3.VERSION_3_0_0); - qryParams.put(CAT3.OUTPUT_FORMAT, MediaType.APPLICATION_ATOM_XML); - int randomIndex = ThreadLocalRandom.current().nextInt(this.idList.size()); - String id = this.idList.get(randomIndex); - qryParams.put(CAT3.ID, id); - response = ClientUtils.buildGetRequest(this.getURI, qryParams, - MediaType.APPLICATION_XML_TYPE); - Assert.assertEquals(response.getStatus(), - Response.Status.OK.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - Document entity = ClientUtils.getResponseEntityAsDocument(response, null); - Map nsBindings = Collections.singletonMap(Namespaces.ATOM, "atom"); - String expr = String.format("/atom:entry/dc:identifier = '%s'", - id); - ETSAssert.assertXPath(expr, entity, nsBindings); - } + /** + * [Test] Verifies that a request for a record representation in an unsupported format + * (media type) produces an exception report containing the exception code + * "InvalidParameterValue". The {@value org.opengis.cite.cat30.CAT3#OUTPUT_FORMAT} + * parameter has the value "model/vnd.collada+xml". + * + * @see "OGC 12-176r6, 7.4.4.3: outputFormat parameter" + */ + @Test(description = "Requirements: 002,035,128") + public void getRecordByIdWithUnsupportedFormat() { + Map qryParams = new HashMap<>(); + qryParams.put(CAT3.REQUEST, CAT3.GET_RECORD_BY_ID); + qryParams.put(CAT3.SERVICE, CAT3.SERVICE_TYPE_CODE); + qryParams.put(CAT3.VERSION, CAT3.VERSION_3_0_0); + qryParams.put(CAT3.OUTPUT_FORMAT, "model/vnd.collada+xml"); + int randomIndex = ThreadLocalRandom.current().nextInt(this.idList.size()); + String id = this.idList.get(randomIndex); + qryParams.put(CAT3.ID, id); + response = ClientUtils.buildGetRequest(this.getURI, qryParams, MediaType.APPLICATION_XML_TYPE); + ETSAssert.assertExceptionReport(response, CAT3.INVALID_PARAM_VAL, CAT3.OUTPUT_FORMAT); + } - /** - * [Test] Verifies that a request for a record representation in an - * unsupported format (media type) produces an exception report containing - * the exception code "InvalidParameterValue". The - * {@value org.opengis.cite.cat30.CAT3#OUTPUT_FORMAT} parameter has the - * value "model/vnd.collada+xml". - * - * @see "OGC 12-176r6, 7.4.4.3: outputFormat parameter" - */ - @Test(description = "Requirements: 002,035,128") - public void getRecordByIdWithUnsupportedFormat() { - Map qryParams = new HashMap<>(); - qryParams.put(CAT3.REQUEST, CAT3.GET_RECORD_BY_ID); - qryParams.put(CAT3.SERVICE, CAT3.SERVICE_TYPE_CODE); - qryParams.put(CAT3.VERSION, CAT3.VERSION_3_0_0); - qryParams.put(CAT3.OUTPUT_FORMAT, "model/vnd.collada+xml"); - int randomIndex = ThreadLocalRandom.current().nextInt(this.idList.size()); - String id = this.idList.get(randomIndex); - qryParams.put(CAT3.ID, id); - response = ClientUtils.buildGetRequest(this.getURI, - qryParams, MediaType.APPLICATION_XML_TYPE); - ETSAssert.assertExceptionReport(response, CAT3.INVALID_PARAM_VAL, - CAT3.OUTPUT_FORMAT); - } + /** + * [Test] Verifies that a request for a record that conforms to an unsupported output + * schema produces an exception report containing the exception code + * "InvalidParameterValue". The {@value org.opengis.cite.cat30.CAT3#OUTPUT_SCHEMA} + * parameter has the value "http://www.example.org/ns/alpha". + * + * @see "OGC 12-176r6, 7.4.4.4: outputSchema parameter" + */ + @Test(description = "Requirements: 132,136") + public void getRecordByIdWithUnsupportedSchema() { + Map qryParams = new HashMap<>(); + qryParams.put(CAT3.REQUEST, CAT3.GET_RECORD_BY_ID); + qryParams.put(CAT3.SERVICE, CAT3.SERVICE_TYPE_CODE); + qryParams.put(CAT3.VERSION, CAT3.VERSION_3_0_0); + qryParams.put(CAT3.OUTPUT_SCHEMA, "http://www.example.org/ns/alpha"); + int randomIndex = ThreadLocalRandom.current().nextInt(this.idList.size()); + String id = this.idList.get(randomIndex); + qryParams.put(CAT3.ID, id); + response = ClientUtils.buildGetRequest(this.getURI, qryParams, MediaType.APPLICATION_XML_TYPE); + ETSAssert.assertExceptionReport(response, CAT3.INVALID_PARAM_VAL, CAT3.OUTPUT_SCHEMA); + } - /** - * [Test] Verifies that a request for a record that conforms to an - * unsupported output schema produces an exception report containing the - * exception code "InvalidParameterValue". The - * {@value org.opengis.cite.cat30.CAT3#OUTPUT_SCHEMA} parameter has the - * value "http://www.example.org/ns/alpha". - * - * @see "OGC 12-176r6, 7.4.4.4: outputSchema parameter" - */ - @Test(description = "Requirements: 132,136") - public void getRecordByIdWithUnsupportedSchema() { - Map qryParams = new HashMap<>(); - qryParams.put(CAT3.REQUEST, CAT3.GET_RECORD_BY_ID); - qryParams.put(CAT3.SERVICE, CAT3.SERVICE_TYPE_CODE); - qryParams.put(CAT3.VERSION, CAT3.VERSION_3_0_0); - qryParams.put(CAT3.OUTPUT_SCHEMA, "http://www.example.org/ns/alpha"); - int randomIndex = ThreadLocalRandom.current().nextInt(this.idList.size()); - String id = this.idList.get(randomIndex); - qryParams.put(CAT3.ID, id); - response = ClientUtils.buildGetRequest(this.getURI, - qryParams, MediaType.APPLICATION_XML_TYPE); - ETSAssert.assertExceptionReport(response, CAT3.INVALID_PARAM_VAL, - CAT3.OUTPUT_SCHEMA); - } + /** + * [Test] Verifies that a request for a record that omits the required 'id' parameter + * produces an exception report containing the exception code "MissingParameterValue". + * + * @see "OGC 12-176r6, Table 21: KVP encoding for GetRecordById operation request" + */ + @Test(description = "Requirements: 037") + public void getRecordByIdWithMissingId() { + Map qryParams = new HashMap<>(); + qryParams.put(CAT3.REQUEST, CAT3.GET_RECORD_BY_ID); + qryParams.put(CAT3.SERVICE, CAT3.SERVICE_TYPE_CODE); + qryParams.put(CAT3.VERSION, CAT3.VERSION_3_0_0); + response = ClientUtils.buildGetRequest(this.getURI, qryParams, MediaType.APPLICATION_XML_TYPE); + ETSAssert.assertExceptionReport(response, CAT3.MISSING_PARAM_VAL, CAT3.ID); + } - /** - * [Test] Verifies that a request for a record that omits the required 'id' - * parameter produces an exception report containing the exception code - * "MissingParameterValue". - * - * @see "OGC 12-176r6, Table 21: KVP encoding for GetRecordById operation - * request" - */ - @Test(description = "Requirements: 037") - public void getRecordByIdWithMissingId() { - Map qryParams = new HashMap<>(); - qryParams.put(CAT3.REQUEST, CAT3.GET_RECORD_BY_ID); - qryParams.put(CAT3.SERVICE, CAT3.SERVICE_TYPE_CODE); - qryParams.put(CAT3.VERSION, CAT3.VERSION_3_0_0); - response = ClientUtils.buildGetRequest(this.getURI, - qryParams, MediaType.APPLICATION_XML_TYPE); - ETSAssert.assertExceptionReport(response, CAT3.MISSING_PARAM_VAL, CAT3.ID); - } } diff --git a/src/main/java/org/opengis/cite/cat30/basic/SuitePreconditions.java b/src/main/java/org/opengis/cite/cat30/basic/SuitePreconditions.java index ce1299d..14f8f25 100644 --- a/src/main/java/org/opengis/cite/cat30/basic/SuitePreconditions.java +++ b/src/main/java/org/opengis/cite/cat30/basic/SuitePreconditions.java @@ -29,89 +29,82 @@ import jakarta.ws.rs.core.MediaType; /** - * Checks that various preconditions are satisfied before the test suite is run. - * If any of these (BeforeSuite) methods fail, all tests are skipped. + * Checks that various preconditions are satisfied before the test suite is run. If any of + * these (BeforeSuite) methods fail, all tests are skipped. */ public class SuitePreconditions { - /** - * Verifies that a service capabilities document was supplied as a test run - * argument and that the implementation it describes is available. - * - * @param testContext Information about the test run. - */ - @BeforeSuite - public void verifyTestSubject() { - ITestContext testContext = Reporter.getCurrentTestResult().getTestContext(); - Object sutObj = testContext.getSuite().getAttribute( - SuiteAttribute.TEST_SUBJECT.getName()); - if (null != sutObj && Document.class.isInstance(sutObj)) { - Document capabilitiesDoc = (Document) sutObj; - String docElemNamespace = capabilitiesDoc.getDocumentElement().getNamespaceURI(); - Assert.assertEquals(docElemNamespace, Namespaces.CSW, - "Document element in unexpected namespace;"); - URI getCapabilitiesGET = ServiceMetadataUtils.getOperationEndpoint( - capabilitiesDoc, CAT3.GET_CAPABILITIES, HttpMethod.GET); - try { - URL url = getCapabilitiesGET.toURL(); - URLConnection connection = url.openConnection(); - connection.connect(); - } catch (IOException iox) { - throw new AssertionError("Service not available at " - + getCapabilitiesGET, iox); - } - } else { - String msg = String.format( - "Value of test suite attribute %s is missing or is not a DOM Document.", - SuiteAttribute.TEST_SUBJECT.getName()); - TestSuiteLogger.log(Level.SEVERE, msg); - throw new AssertionError(msg); - } - } + /** + * Verifies that a service capabilities document was supplied as a test run argument + * and that the implementation it describes is available. + * @param testContext Information about the test run. + */ + @BeforeSuite + public void verifyTestSubject() { + ITestContext testContext = Reporter.getCurrentTestResult().getTestContext(); + Object sutObj = testContext.getSuite().getAttribute(SuiteAttribute.TEST_SUBJECT.getName()); + if (null != sutObj && Document.class.isInstance(sutObj)) { + Document capabilitiesDoc = (Document) sutObj; + String docElemNamespace = capabilitiesDoc.getDocumentElement().getNamespaceURI(); + Assert.assertEquals(docElemNamespace, Namespaces.CSW, "Document element in unexpected namespace;"); + URI getCapabilitiesGET = ServiceMetadataUtils.getOperationEndpoint(capabilitiesDoc, CAT3.GET_CAPABILITIES, + HttpMethod.GET); + try { + URL url = getCapabilitiesGET.toURL(); + URLConnection connection = url.openConnection(); + connection.connect(); + } + catch (IOException iox) { + throw new AssertionError("Service not available at " + getCapabilitiesGET, iox); + } + } + else { + String msg = String.format("Value of test suite attribute %s is missing or is not a DOM Document.", + SuiteAttribute.TEST_SUBJECT.getName()); + TestSuiteLogger.log(Level.SEVERE, msg); + throw new AssertionError(msg); + } + } + + /** + * Fetches records from the IUT using a simple GetRecords request (with no filter + * criteria) and saves the response entity to a temporary file. The resulting + * {@link DatasetInfo DatasetInfo} object is stored as the value of the suite + * attribute {@link SuiteAttribute#DATASET dataset}. + * + *

          + * The resulting csw:Record (full) representations are inspected in order to construct + * successful service requests (e.g. a GetRecordById request that produces a matching + * record, GetRecords request with a spatial filter). + *

          + * @param testContext Information about the test run. + */ + @BeforeSuite + public void fetchSampleData() { + ITestContext testContext = Reporter.getCurrentTestResult().getTestContext(); + Document capabilitiesDoc = (Document) testContext.getSuite() + .getAttribute(SuiteAttribute.TEST_SUBJECT.getName()); + CSWClient cswClient = new CSWClient(); + cswClient.setServiceDescription(capabilitiesDoc); + File dataFile = cswClient.saveFullRecords(20, MediaType.APPLICATION_XML_TYPE); + if (!dataFile.isFile()) { + throw new AssertionError("Failed to save GetRecords response to temp file."); + } + Boolean hasResults = null; + try { + hasResults = (Boolean) XMLUtils.evaluateXPath(new StreamSource(dataFile), "//csw:Record", null, + XPathConstants.BOOLEAN); + } + catch (XPathExpressionException ex) { + // not possible + } + if (!hasResults) { + throw new AssertionError("fetchSampleData: No csw:Record elements found in GetRecords response."); + } + TestSuiteLogger.log(Level.INFO, + "fetchSampleData: Saved GetRecords response to file: " + dataFile.getAbsolutePath()); + DatasetInfo dataset = new DatasetInfo(dataFile); + testContext.getSuite().setAttribute(SuiteAttribute.DATASET.getName(), dataset); + } - /** - * Fetches records from the IUT using a simple GetRecords request (with no - * filter criteria) and saves the response entity to a temporary file. The - * resulting {@link DatasetInfo DatasetInfo} object is stored as the value - * of the suite attribute {@link SuiteAttribute#DATASET dataset}. - * - *

          - * The resulting csw:Record (full) representations are inspected in order to - * construct successful service requests (e.g. a GetRecordById request that - * produces a matching record, GetRecords request with a spatial filter). - *

          - * - * @param testContext Information about the test run. - */ - @BeforeSuite - public void fetchSampleData() { - ITestContext testContext = Reporter.getCurrentTestResult().getTestContext(); - Document capabilitiesDoc = (Document) testContext.getSuite().getAttribute( - SuiteAttribute.TEST_SUBJECT.getName()); - CSWClient cswClient = new CSWClient(); - cswClient.setServiceDescription(capabilitiesDoc); - File dataFile = cswClient.saveFullRecords(20, - MediaType.APPLICATION_XML_TYPE); - if (!dataFile.isFile()) { - throw new AssertionError( - "Failed to save GetRecords response to temp file."); - } - Boolean hasResults = null; - try { - hasResults = (Boolean) XMLUtils.evaluateXPath( - new StreamSource(dataFile), "//csw:Record", null, XPathConstants.BOOLEAN); - } catch (XPathExpressionException ex) { - // not possible - } - if (!hasResults) { - throw new AssertionError( - "fetchSampleData: No csw:Record elements found in GetRecords response."); - } - TestSuiteLogger.log(Level.INFO, - "fetchSampleData: Saved GetRecords response to file: " - + dataFile.getAbsolutePath()); - DatasetInfo dataset = new DatasetInfo(dataFile); - testContext.getSuite().setAttribute( - SuiteAttribute.DATASET.getName(), dataset); - } } diff --git a/src/main/java/org/opengis/cite/cat30/basic/package-info.java b/src/main/java/org/opengis/cite/cat30/basic/package-info.java index 86aa9e0..4375e0c 100644 --- a/src/main/java/org/opengis/cite/cat30/basic/package-info.java +++ b/src/main/java/org/opengis/cite/cat30/basic/package-info.java @@ -1,29 +1,35 @@ /** - *

          Includes tests covering capabilities required for Basic-Catalogue - * conformance. The service requests listed below fall into this group; they may - * be submitted using either the GET method or the POST method with the content - * type "application/x-www-form-urlencoded" (key-value pair syntax).

          - * + *

          + * Includes tests covering capabilities required for Basic-Catalogue + * conformance. The service requests listed below fall into this group; they may be + * submitted using either the GET method or the POST method with the content type + * "application/x-www-form-urlencoded" (key-value pair syntax). + *

          + * *
            - *
          • GetCapabilities: Obtain information (service-related - * metadata) about the capabilities of the service
          • - *
          • GetRecordById: Retrieve a representation of a catalog - * record by identifier.
          • - *
          • GetRecords: Search catalog content and retrieve results - * using basic filter criteria.
          • + *
          • GetCapabilities: Obtain information (service-related metadata) about + * the capabilities of the service
          • + *
          • GetRecordById: Retrieve a representation of a catalog record by + * identifier.
          • + *
          • GetRecords: Search catalog content and retrieve results using basic + * filter criteria.
          • *
          - * - *

          The following presentation formats must be supported:

          - *
            - *
          • CSW record schema (mostly consisting of DCMI metadata terms);
          • - *
          • ATOM syndication format (RFC 4287).
          • + * + *

            + * The following presentation formats must be supported: + *

            + *
              + *
            • CSW record schema (mostly consisting of DCMI metadata terms);
            • + *
            • ATOM syndication format (RFC 4287).
            • *
            * - *

            Sources

            + *

            + * Sources + *

            *
              - *
            • [OGC-12-176r5] OGC Catalogue Services 3.0 Specification – HTTP Protocol + *
            • [OGC-12-176r5] OGC Catalogue Services 3.0 Specification – HTTP Protocol * Binding, Table 2: Requirement classes and abstract tests
            • - *
            • [OGC-14-014r3] OGC Catalogue Services 3.0 Specification – HTTP Protocol + *
            • [OGC-14-014r3] OGC Catalogue Services 3.0 Specification – HTTP Protocol * Binding – Abstract Test Suite, cl. 2: Basic-Catalogue conformance class
            • *
            */ diff --git a/src/main/java/org/opengis/cite/cat30/opensearch/OpenSearchCoreTests.java b/src/main/java/org/opengis/cite/cat30/opensearch/OpenSearchCoreTests.java index aba5477..b0b1210 100644 --- a/src/main/java/org/opengis/cite/cat30/opensearch/OpenSearchCoreTests.java +++ b/src/main/java/org/opengis/cite/cat30/opensearch/OpenSearchCoreTests.java @@ -39,9 +39,9 @@ import jakarta.ws.rs.core.Response; /** - * Verifies behavior of the SUT when processing queries that contain one or more - * core OpenSearch parameters. The relevant query parameter bindings are shown - * in the following table. + * Verifies behavior of the SUT when processing queries that contain one or more core + * OpenSearch parameters. The relevant query parameter bindings are shown in the following + * table. * *
      * @@ -54,8 +54,8 @@ * * * - * + * * * * @@ -65,8 +65,8 @@ * * * - * + * * * *
      Binding OpenSearch query parameters
      q{searchTerms}A comma-separated list of terms used to search across all text fields. - * The value must be URL-encoded.A comma-separated list of terms used to search across all text fields. The value + * must be URL-encoded.
      startPosition
      maxRecords{count?}Non-negative integer value specifying the number of search results (per - * page) desired by the client.Non-negative integer value specifying the number of search results (per page) + * desired by the client.
      @@ -75,25 +75,30 @@ * Sources *

      *
        - *
      • [OGC-12-176r5] OGC Catalogue Services 3.0 Specification – HTTP - * Protocol Binding, Table 6: KVP encoding for query constraints
      • - *
      • OpenSearch 1.1 Draft 5
      • + *
      • [OGC-12-176r5] OGC Catalogue Services 3.0 Specification – HTTP Protocol + * Binding, Table 6: KVP encoding for query constraints
      • + *
      • OpenSearch + * 1.1 + * Draft 5
      • *
      */ public class OpenSearchCoreTests extends CommonFixture { private String searchTerm; - private static final QName SEARCH_TERMS_PARAM = new QName(Namespaces.OSD11, - "searchTerms"); + + private static final QName SEARCH_TERMS_PARAM = new QName(Namespaces.OSD11, "searchTerms"); + private Document openSearchDescr; + private List templates; + private List searchTermsTemplates; + /** * Information about the sample data retrieved from the IUT. */ private DatasetInfo datasetInfo; + /** * A list of record titles retrieved from the IUT. */ @@ -104,28 +109,22 @@ void setSearchTerm(String searchTerm) { } /** - * Initializes the test fixture. A Document representing an OpenSearch - * description document is obtained from the test context and the URL - * templates it contains are extracted. - * - * @param testContext - * The test context containing various suite attributes. + * Initializes the test fixture. A Document representing an OpenSearch description + * document is obtained from the test context and the URL templates it contains are + * extracted. + * @param testContext The test context containing various suite attributes. */ @BeforeClass public void initOpenSearchCoreTestsFixture(ITestContext testContext) { - this.openSearchDescr = (Document) testContext.getSuite().getAttribute( - SuiteAttribute.OPENSEARCH_DESCR.getName()); + this.openSearchDescr = (Document) testContext.getSuite() + .getAttribute(SuiteAttribute.OPENSEARCH_DESCR.getName()); if (null == this.openSearchDescr) { - throw new SkipException( - "OpenSearch description not found in test context."); + throw new SkipException("OpenSearch description not found in test context."); } - this.templates = ServiceMetadataUtils - .getOpenSearchURLTemplates(this.openSearchDescr); + this.templates = ServiceMetadataUtils.getOpenSearchURLTemplates(this.openSearchDescr); QName searchTermsParam = new QName(Namespaces.OSD11, "searchTerms"); - this.searchTermsTemplates = OpenSearchTemplateUtils - .filterURLTemplatesByParam(templates, searchTermsParam); - DatasetInfo dataset = (DatasetInfo) testContext.getSuite() - .getAttribute(SuiteAttribute.DATASET.getName()); + this.searchTermsTemplates = OpenSearchTemplateUtils.filterURLTemplatesByParam(templates, searchTermsParam); + DatasetInfo dataset = (DatasetInfo) testContext.getSuite().getAttribute(SuiteAttribute.DATASET.getName()); if (null == dataset) { throw new SkipException("Dataset info not found in test context."); } @@ -134,16 +133,14 @@ public void initOpenSearchCoreTestsFixture(ITestContext testContext) { } /** - * [Test] Submits a keyword search where the searchTerms value is a randomly - * generated sequence of 5-14 characters in the range [a-z]. The result set - * is expected to be empty; that is, there are no matching entries - * (os:totalResults = 0). + * [Test] Submits a keyword search where the searchTerms value is a randomly generated + * sequence of 5-14 characters in the range [a-z]. The result set is expected to be + * empty; that is, there are no matching entries (os:totalResults = 0). */ @Test(description = "OGC 12-176, Table 6: Text search") public void keywordSearch_emptyResultSet() { if (this.searchTermsTemplates.isEmpty()) { - throw new AssertionError( - "No URL templates containing {searchTerms} parameter."); + throw new AssertionError("No URL templates containing {searchTerms} parameter."); } Map values = new HashMap<>(); values.put(SEARCH_TERMS_PARAM, Records.generateRandomText()); @@ -153,12 +150,9 @@ public void keywordSearch_emptyResultSet() { if (!mediaType.contains("xml")) { continue; // ignore non-XML media types } - URI targetURI = OpenSearchTemplateUtils.buildRequestURI(urlElem, - values); - response = ClientUtils.buildGetRequest(targetURI, null, - MediaType.valueOf(mediaType)); - Assert.assertEquals(response.getStatus(), - Response.Status.OK.getStatusCode(), + URI targetURI = OpenSearchTemplateUtils.buildRequestURI(urlElem, values); + response = ClientUtils.buildGetRequest(targetURI, null, MediaType.valueOf(mediaType)); + Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode(), ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); Document entity = getResponseEntityAsDocument(response, null); ETSAssert.assertEmptyResultSet(entity); @@ -166,78 +160,69 @@ public void keywordSearch_emptyResultSet() { } /** - * [Test] Submits a keyword search where the {searchTerms} value is a title - * word (URL-encoded) that occurs in at least one catalog record. The result - * set must not be empty. + * [Test] Submits a keyword search where the {searchTerms} value is a title word + * (URL-encoded) that occurs in at least one catalog record. The result set must not + * be empty. */ @Test(description = "OGC 12-176, Table 6: Text search") public void singleKeywordSearch() { if (this.searchTermsTemplates.isEmpty()) { - throw new AssertionError( - "No URL templates containing {searchTerms} parameter."); + throw new AssertionError("No URL templates containing {searchTerms} parameter."); } if (null == searchTerm || searchTerm.isEmpty()) { this.searchTerm = randomlySelectTitleWord(this.recordTitles); } Map values = new HashMap<>(); - values.put(SEARCH_TERMS_PARAM, - URIUtils.getPercentEncodedString(searchTerm)); + values.put(SEARCH_TERMS_PARAM, URIUtils.getPercentEncodedString(searchTerm)); for (Node template : this.searchTermsTemplates) { Element urlElem = (Element) template; NodeList records; try { records = invokeQuery(urlElem, values); - } catch (UnsupportedOperationException e) { + } + catch (UnsupportedOperationException e) { continue; // skip query if it produces non-XML results } - Assert.assertEquals(response.getStatus(), - Response.Status.OK.getStatusCode(), + Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode(), ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - Assert.assertTrue(records.getLength() > 0, ErrorMessage.format( - ErrorMessageKeys.EMPTY_RESULT_SET, + Assert.assertTrue(records.getLength() > 0, ErrorMessage.format(ErrorMessageKeys.EMPTY_RESULT_SET, Records.getRecordName(urlElem.getAttribute("type")))); // NOTE: Spec does not indicate how records are matched // ETSAssert.assertAllTermsOccur(records, searchTerm); Document entity = getResponseEntityAsDocument(response, null); - if (entity.getDocumentElement().getNamespaceURI() - .equals(Namespaces.ATOM)) { + if (entity.getDocumentElement().getNamespaceURI().equals(Namespaces.ATOM)) { URL schemaUrl = getClass().getResource(SCHEMATRON_ATOM); - ETSAssert.assertSchematronValid(schemaUrl, - new DOMSource(entity)); + ETSAssert.assertSchematronValid(schemaUrl, new DOMSource(entity)); } } } /** - * [Test] Submits a keyword search request where the {searchTerms} value - * contains two terms (URL-encoded). The result set must not be empty. + * [Test] Submits a keyword search request where the {searchTerms} value contains two + * terms (URL-encoded). The result set must not be empty. */ @Test(description = "OGC 12-176, Table 6: Text search") public void multipleKeywordSearch() { if (this.searchTermsTemplates.isEmpty()) { - throw new AssertionError( - "No URL templates containing {searchTerms} parameter."); + throw new AssertionError("No URL templates containing {searchTerms} parameter."); } QName titleName = new QName(Namespaces.DCMES, "title"); QName subjectName = new QName(Namespaces.DCMES, "subject"); - String searchTerms = Records.findMatchingSearchTerms( - this.datasetInfo.getDataFile(), titleName, subjectName); + String searchTerms = Records.findMatchingSearchTerms(this.datasetInfo.getDataFile(), titleName, subjectName); Map params = new HashMap<>(); - params.put(SEARCH_TERMS_PARAM, - URIUtils.getPercentEncodedString(searchTerms)); + params.put(SEARCH_TERMS_PARAM, URIUtils.getPercentEncodedString(searchTerms)); for (Node template : this.searchTermsTemplates) { Element urlElem = (Element) template; NodeList records; try { records = invokeQuery(urlElem, params); - } catch (UnsupportedOperationException e) { + } + catch (UnsupportedOperationException e) { continue; // skip query if it produces non-XML results } - Assert.assertEquals(response.getStatus(), - Response.Status.OK.getStatusCode(), + Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode(), ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - Assert.assertTrue(records.getLength() > 0, ErrorMessage.format( - ErrorMessageKeys.EMPTY_RESULT_SET, + Assert.assertTrue(records.getLength() > 0, ErrorMessage.format(ErrorMessageKeys.EMPTY_RESULT_SET, Records.getRecordName(urlElem.getAttribute("type")))); // ETSAssert.assertQualifiedName(template, titleName); // NOTE: Spec does not indicate how multiple terms are interpreted @@ -249,8 +234,8 @@ public void multipleKeywordSearch() { /** * [Test] Submits a query that contains the count and - * startIndex parameters. The response entity must contain the - * requested 'slice' of the result set in accord with its content model: + * startIndex parameters. The response entity must contain the requested + * 'slice' of the result set in accord with its content model: *
        *
      • atom:feed (with OpenSearch response elements)
      • *
      • csw:GetRecordsResponse/csw:SearchResults
      • @@ -259,11 +244,9 @@ public void multipleKeywordSearch() { @Test(description = "Requirements: 022,023") public void sliceResults() { QName countParam = new QName(Namespaces.OSD11, "count"); - List templatesWithCountParam = OpenSearchTemplateUtils - .filterURLTemplatesByParam(templates, countParam); + List templatesWithCountParam = OpenSearchTemplateUtils.filterURLTemplatesByParam(templates, countParam); if (templatesWithCountParam.isEmpty()) { - throw new AssertionError( - "No URL templates containing {count} parameter."); + throw new AssertionError("No URL templates containing {count} parameter."); } int count = 4; Map params = new HashMap<>(); @@ -276,143 +259,111 @@ public void sliceResults() { NodeList records; try { records = invokeQuery(urlTemplate, params); - } catch (UnsupportedOperationException e) { + } + catch (UnsupportedOperationException e) { continue; // skip if query produces non-XML results } - Assert.assertEquals(response.getStatus(), - Response.Status.OK.getStatusCode(), + Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode(), ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - Assert.assertTrue(records.getLength() > 0, - ErrorMessage.get(ErrorMessageKeys.EMPTY_RESULT_SET)); + Assert.assertTrue(records.getLength() > 0, ErrorMessage.get(ErrorMessageKeys.EMPTY_RESULT_SET)); Document entity = getResponseEntityAsDocument(response, null); String namespaceURI = entity.getDocumentElement().getNamespaceURI(); switch (namespaceURI) { - case Namespaces.ATOM: - Node itemsPerPage = entity.getElementsByTagNameNS( - Namespaces.OSD11, "itemsPerPage").item(0); - Assert.assertNotNull(itemsPerPage, ErrorMessage.format( - ErrorMessageKeys.MISSING_INFOSET_ITEM, - "os:itemsPerPage")); - Assert.assertEquals(Integer.parseInt(itemsPerPage - .getTextContent()), count, ErrorMessage.format( - ErrorMessageKeys.INFOSET_ITEM_VALUE, "os:itemsPerPage")); - Node startIndexNode = entity.getElementsByTagNameNS( - Namespaces.OSD11, "startIndex").item(0); - Assert.assertNotNull(startIndexNode, ErrorMessage.format( - ErrorMessageKeys.MISSING_INFOSET_ITEM, "os:startIndex")); - Assert.assertEquals(Integer.parseInt(startIndexNode - .getTextContent()), startIndex, ErrorMessage.format( - ErrorMessageKeys.INFOSET_ITEM_VALUE, "os:startIndex")); - break; - case Namespaces.CSW: - Node resultsNode = entity.getElementsByTagNameNS( - Namespaces.CSW, "SearchResults").item(0); - Assert.assertNotNull(resultsNode, ErrorMessage.format( - ErrorMessageKeys.MISSING_INFOSET_ITEM, - "csw:SearchResults")); - Element results = (Element) resultsNode; - int nRecordsReturned = Integer.parseInt(results - .getAttribute(CAT3.NUM_REC_RETURNED)); - Assert.assertEquals(nRecordsReturned, count, ErrorMessage - .format(ErrorMessageKeys.INFOSET_ITEM_VALUE, - "@numberOfRecordsReturned")); - int nextRecord = Integer.parseInt(results - .getAttribute(CAT3.NEXT_REC)); - Assert.assertEquals(nextRecord, startIndex + count, - ErrorMessage.format( - ErrorMessageKeys.INFOSET_ITEM_VALUE, - "@nextRecord")); - break; - default: - throw new SkipException("Unrecognized namespace: " - + namespaceURI); + case Namespaces.ATOM: + Node itemsPerPage = entity.getElementsByTagNameNS(Namespaces.OSD11, "itemsPerPage").item(0); + Assert.assertNotNull(itemsPerPage, + ErrorMessage.format(ErrorMessageKeys.MISSING_INFOSET_ITEM, "os:itemsPerPage")); + Assert.assertEquals(Integer.parseInt(itemsPerPage.getTextContent()), count, + ErrorMessage.format(ErrorMessageKeys.INFOSET_ITEM_VALUE, "os:itemsPerPage")); + Node startIndexNode = entity.getElementsByTagNameNS(Namespaces.OSD11, "startIndex").item(0); + Assert.assertNotNull(startIndexNode, + ErrorMessage.format(ErrorMessageKeys.MISSING_INFOSET_ITEM, "os:startIndex")); + Assert.assertEquals(Integer.parseInt(startIndexNode.getTextContent()), startIndex, + ErrorMessage.format(ErrorMessageKeys.INFOSET_ITEM_VALUE, "os:startIndex")); + break; + case Namespaces.CSW: + Node resultsNode = entity.getElementsByTagNameNS(Namespaces.CSW, "SearchResults").item(0); + Assert.assertNotNull(resultsNode, + ErrorMessage.format(ErrorMessageKeys.MISSING_INFOSET_ITEM, "csw:SearchResults")); + Element results = (Element) resultsNode; + int nRecordsReturned = Integer.parseInt(results.getAttribute(CAT3.NUM_REC_RETURNED)); + Assert.assertEquals(nRecordsReturned, count, + ErrorMessage.format(ErrorMessageKeys.INFOSET_ITEM_VALUE, "@numberOfRecordsReturned")); + int nextRecord = Integer.parseInt(results.getAttribute(CAT3.NEXT_REC)); + Assert.assertEquals(nextRecord, startIndex + count, + ErrorMessage.format(ErrorMessageKeys.INFOSET_ITEM_VALUE, "@nextRecord")); + break; + default: + throw new SkipException("Unrecognized namespace: " + namespaceURI); } } } /** - * Executes example queries specified in the OpenSearch description - * document. It is recommended that the document contains at least one query - * having role="example" in order to allow testing or demonstration of the - * search service. + * Executes example queries specified in the OpenSearch description document. It is + * recommended that the document contains at least one query having role="example" in + * order to allow testing or demonstration of the search service. * * @see OpenSearch Query element + * "http://www.opensearch.org/Specifications/OpenSearch/1.1#OpenSearch_Query_element" + * >OpenSearch Query element * @see Developer best practices guide + * "http://www.opensearch.org/Documentation/Developer_best_practices_guide" >Developer + * best practices guide */ @Test(description = "OpenSearchDescription: Query element") public void executeExampleQueries() { - List exampleQueryList = ServiceMetadataUtils - .getOpenSearchQueriesByRole(this.openSearchDescr, new QName( - Namespaces.OSD11, "example")); + List exampleQueryList = ServiceMetadataUtils.getOpenSearchQueriesByRole(this.openSearchDescr, + new QName(Namespaces.OSD11, "example")); if (exampleQueryList.isEmpty()) { - throw new SkipException( - "No example queries found in OpenSearch description."); + throw new SkipException("No example queries found in OpenSearch description."); } for (Node query : exampleQueryList) { - Map params = OpenSearchTemplateUtils - .getQueryParameters(query); + Map params = OpenSearchTemplateUtils.getQueryParameters(query); // Assume all params are allowed in template - List qryTemplates = OpenSearchTemplateUtils - .filterURLTemplatesByParam(this.templates, params.keySet() - .iterator().next()); + List qryTemplates = OpenSearchTemplateUtils.filterURLTemplatesByParam(this.templates, + params.keySet().iterator().next()); for (Node template : qryTemplates) { Element qryTemplate = (Element) template; NodeList records; try { records = invokeQuery(qryTemplate, params); - } catch (UnsupportedOperationException e) { + } + catch (UnsupportedOperationException e) { continue; // skip query if it produces non-XML results } - Assert.assertEquals(response.getStatus(), - Response.Status.OK.getStatusCode(), + Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode(), ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - Assert.assertTrue(records.getLength() > 0, - ErrorMessage.format(ErrorMessageKeys.EMPTY_RESULT_SET, - Records.getRecordName(qryTemplate - .getAttribute("type")))); + Assert.assertTrue(records.getLength() > 0, ErrorMessage.format(ErrorMessageKeys.EMPTY_RESULT_SET, + Records.getRecordName(qryTemplate.getAttribute("type")))); } } } /** - * Invokes the query defined by the given OpenSearch template. The supplied - * parameters replace the corresponding substitution variables in the - * template. - * - * @param qryTemplate - * An Element representing an OpenSearch query template - * (osd:Url). - * @param parameters - * A Map containing the actual query parameters. + * Invokes the query defined by the given OpenSearch template. The supplied parameters + * replace the corresponding substitution variables in the template. + * @param qryTemplate An Element representing an OpenSearch query template (osd:Url). + * @param parameters A Map containing the actual query parameters. * @return A NodeList containing the records extracted from the response. */ NodeList invokeQuery(Element qryTemplate, Map parameters) { String mediaType = qryTemplate.getAttribute("type"); if (!mediaType.contains("xml")) { - throw new UnsupportedOperationException( - "URL template does not produce XML results: " + mediaType); + throw new UnsupportedOperationException("URL template does not produce XML results: " + mediaType); } - URI targetURI = OpenSearchTemplateUtils.buildRequestURI(qryTemplate, - parameters); + URI targetURI = OpenSearchTemplateUtils.buildRequestURI(qryTemplate, parameters); TestSuiteLogger.log(Level.FINE, "invokeQuery target URI: " + targetURI); - response = ClientUtils.buildGetRequest(targetURI, null, - MediaType.valueOf(mediaType)); + response = ClientUtils.buildGetRequest(targetURI, null, MediaType.valueOf(mediaType)); Document entity = getResponseEntityAsDocument(response, null); QName recordName = Records.getRecordName(mediaType); - NodeList records = entity.getElementsByTagNameNS( - recordName.getNamespaceURI(), recordName.getLocalPart()); + NodeList records = entity.getElementsByTagNameNS(recordName.getNamespaceURI(), recordName.getLocalPart()); return records; } /** * Returns a word from a randomly selected title in the given list. - * - * @param titles - * A list of record titles. + * @param titles A list of record titles. * @return A word (the last) occurring in some title. */ String randomlySelectTitleWord(List titles) { @@ -427,4 +378,5 @@ String randomlySelectTitleWord(List titles) { word = word.replaceAll("[()]", ""); return word; } + } diff --git a/src/main/java/org/opengis/cite/cat30/opensearch/OpenSearchDescriptionTests.java b/src/main/java/org/opengis/cite/cat30/opensearch/OpenSearchDescriptionTests.java index 3424a35..b0f5d8e 100644 --- a/src/main/java/org/opengis/cite/cat30/opensearch/OpenSearchDescriptionTests.java +++ b/src/main/java/org/opengis/cite/cat30/opensearch/OpenSearchDescriptionTests.java @@ -34,10 +34,10 @@ import jakarta.ws.rs.core.Response; /** - * Verifies the structure and content of the OpenSearch description document - * obtained from the SUT. The document is obtained in response to a GET request - * submitted to the base service endpoint where the Accept request - * header expresses a preference for any of the following media types: + * Verifies the structure and content of the OpenSearch description document obtained from + * the SUT. The document is obtained in response to a GET request submitted to the base + * service endpoint where the Accept request header expresses a preference + * for any of the following media types: * *
          *
        • application/vnd.a9.opensearchdescription+xml
        • @@ -45,144 +45,135 @@ *
        * *

        - * Note: None of the media types listed above appear in the - * IANA media type registry. Registrations in the standards tree - * must be approved by the IESG or originate from a recognized standards-related - * organization (see RFC 6838); third-party registrations are allowed in the - * vendor tree. + * Note: None of the media types listed above appear in the IANA + * media type registry. Registrations in the standards tree must be approved + * by the IESG or originate from a recognized standards-related organization (see + * RFC 6838); + * third-party registrations are allowed in the vendor tree. *

        * - *

        Sources

        + *

        + * Sources + *

        * * */ public class OpenSearchDescriptionTests extends CommonFixture { - private RelaxNGValidator osdValidator; - private URI baseUri; - private static final String SCHEMATRON_OPENSEARCH_DESCR - = CommonFixture.ROOT_PKG_PATH + "sch/opensearch-1.1.sch"; - public static final String OPENSEARCH_CONSTRAINT - = "OpenSearchDescriptionDocument"; + private RelaxNGValidator osdValidator; + + private URI baseUri; + + private static final String SCHEMATRON_OPENSEARCH_DESCR = CommonFixture.ROOT_PKG_PATH + "sch/opensearch-1.1.sch"; + + public static final String OPENSEARCH_CONSTRAINT = "OpenSearchDescriptionDocument"; + + /** + * Initializes the test fixture by: + *
          + *
        • building a Relax NG schema validator for an OpenSearch description document; + * the schema resource is located on the classpath at this location: + * /org/opengis/cite/cat30/rnc/osd-1.1-draft5.rnc
        • + *
        • extracting the base GetCapabilities URL (for the GET method binding) from the + * capabilities document
        • + *
        + * @param testContext The test context containing various suite attributes. + * + */ + @BeforeClass + public void initFixture(ITestContext testContext) { + URL rncSchema = getClass().getResource(CommonFixture.ROOT_PKG_PATH + "rnc/osd-1.1-draft5.rnc"); + try { + this.osdValidator = new RelaxNGValidator(rncSchema); + } + catch (SAXException | IOException ex) { + TestSuiteLogger.log(Level.WARNING, getClass().getName(), ex); + } + this.baseUri = ServiceMetadataUtils.getOperationEndpoint(this.cswCapabilities, CAT3.GET_CAPABILITIES, + HttpMethod.GET); + } - /** - * Initializes the test fixture by: - *
          - *
        • building a Relax NG schema validator for an OpenSearch description - * document; the schema resource is located on the classpath at this - * location: - * /org/opengis/cite/cat30/rnc/osd-1.1-draft5.rnc
        • - *
        • extracting the base GetCapabilities URL (for the GET method binding) - * from the capabilities document
        • - *
        - * - * @param testContext The test context containing various suite attributes. - * - */ - @BeforeClass - public void initFixture(ITestContext testContext) { - URL rncSchema = getClass().getResource(CommonFixture.ROOT_PKG_PATH - + "rnc/osd-1.1-draft5.rnc"); - try { - this.osdValidator = new RelaxNGValidator(rncSchema); - } catch (SAXException | IOException ex) { - TestSuiteLogger.log(Level.WARNING, getClass().getName(), ex); - } - this.baseUri = ServiceMetadataUtils.getOperationEndpoint( - this.cswCapabilities, CAT3.GET_CAPABILITIES, HttpMethod.GET); - } + /** + * [Test] Requests an OpenSearch description document as the most preferred media + * type. The generic XML media type is included in the Accept header with a q + * parameter value < 1: + * + *
        Accept: application/xml; q=0.5, application/opensearchdescription+xml
        + */ + @Test(description = "Requirements: 008; Tests: 008") + public void preferOpenSearchDescription() { + String xmlNotPreferred = MediaType.APPLICATION_XML + "; q=0.5"; + response = ClientUtils.buildGetRequest(this.baseUri, null, MediaType.valueOf(xmlNotPreferred), + MediaType.valueOf(CAT3.APP_OPENSEARCH_XML)); + Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode(), + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + Assert.assertTrue(XMLUtils.isXML(response.getMediaType()), + ErrorMessage.format(ErrorMessageKeys.NOT_XML, response.getMediaType())); + Document entity = ClientUtils.getResponseEntityAsDocument(response, null); + QName osdDocElemName = new QName(Namespaces.OSD11, "OpenSearchDescription"); + ETSAssert.assertQualifiedName(entity.getDocumentElement(), osdDocElemName); + } - /** - * [Test] Requests an OpenSearch description document as the most preferred - * media type. The generic XML media type is included in the Accept header - * with a q parameter value < 1: - * - *
        Accept: application/xml; q=0.5, application/opensearchdescription+xml
        - */ - @Test(description = "Requirements: 008; Tests: 008") - public void preferOpenSearchDescription() { - String xmlNotPreferred = MediaType.APPLICATION_XML + "; q=0.5"; - response = ClientUtils.buildGetRequest(this.baseUri, null, - MediaType.valueOf(xmlNotPreferred), - MediaType.valueOf(CAT3.APP_OPENSEARCH_XML)); - Assert.assertEquals(response.getStatus(), - Response.Status.OK.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - Assert.assertTrue(XMLUtils.isXML(response.getMediaType()), - ErrorMessage.format(ErrorMessageKeys.NOT_XML, response.getMediaType())); - Document entity = ClientUtils.getResponseEntityAsDocument(response, null); - QName osdDocElemName = new QName(Namespaces.OSD11, "OpenSearchDescription"); - ETSAssert.assertQualifiedName(entity.getDocumentElement(), osdDocElemName); - } + /** + * [Test] Validates the OpenSearch description document obtained from the IUT. The + * document is checked against the constraints in the OpenSearch 1.1 draft 5 + * specification. + * @throws SAXException If the document cannot be read. + * @throws IOException If an I/O error occurs while trying to access the document. + * + * @see "[CAT-HTTP], 6.5.6.5: Requirements for an OpenSearch enabled CSW" + */ + @Test(description = "Requirements: 021; Tests: 021") + public void validOpenSearchDescription() throws SAXException, IOException { + response = ClientUtils.buildGetRequest(this.baseUri, null, MediaType.valueOf(CAT3.APP_VND_OPENSEARCH_XML), + MediaType.valueOf(CAT3.APP_OPENSEARCH_XML)); + Assert.assertTrue(XMLUtils.isXML(response.getMediaType()), + ErrorMessage.format(ErrorMessageKeys.NOT_XML, response.getMediaType())); + Source entity = ClientUtils.getResponseEntityAsSource(response, null); + this.osdValidator.validate(entity); + ValidationErrorHandler err = osdValidator.getErrorHandler(); + Assert.assertFalse(err.errorsDetected(), + ErrorMessage.format(ErrorMessageKeys.NOT_SCHEMA_VALID, err.getErrorCount(), err.toString())); + URL schemaUrl = getClass().getResource(SCHEMATRON_OPENSEARCH_DESCR); + ETSAssert.assertSchematronValid(schemaUrl, entity); + } - /** - * [Test] Validates the OpenSearch description document obtained from the - * IUT. The document is checked against the constraints in the OpenSearch - * 1.1 draft 5 specification. - * - * @throws SAXException If the document cannot be read. - * @throws IOException If an I/O error occurs while trying to access the - * document. - * - * @see "[CAT-HTTP], 6.5.6.5: Requirements for an OpenSearch enabled CSW" - */ - @Test(description = "Requirements: 021; Tests: 021") - public void validOpenSearchDescription() throws SAXException, IOException { - response = ClientUtils.buildGetRequest(this.baseUri, null, - MediaType.valueOf(CAT3.APP_VND_OPENSEARCH_XML), - MediaType.valueOf(CAT3.APP_OPENSEARCH_XML)); - Assert.assertTrue(XMLUtils.isXML(response.getMediaType()), - ErrorMessage.format(ErrorMessageKeys.NOT_XML, response.getMediaType())); - Source entity = ClientUtils.getResponseEntityAsSource(response, null); - this.osdValidator.validate(entity); - ValidationErrorHandler err = osdValidator.getErrorHandler(); - Assert.assertFalse(err.errorsDetected(), - ErrorMessage.format(ErrorMessageKeys.NOT_SCHEMA_VALID, - err.getErrorCount(), err.toString())); - URL schemaUrl = getClass().getResource(SCHEMATRON_OPENSEARCH_DESCR); - ETSAssert.assertSchematronValid(schemaUrl, entity); - } + /** + * [Test] Attempts to retrieve an OpenSearch description document using the URI + * presented in the capabilities document as the value of the + * {@value #OPENSEARCH_CONSTRAINT} constraint. + * + * @see "[CAT-HTTP], 6.5.6.2, Table 16" + */ + @Test(description = "[CAT-HTTP]: 6.5.6.2, Table 16") + public void getOpenSearchDescriptionFromCapabilities() { + URI getCapabilitiesEndpoint = ServiceMetadataUtils.getOperationEndpoint(this.cswCapabilities, + CAT3.GET_CAPABILITIES, HttpMethod.GET); + CSWClient cswClient = new CSWClient(); + Document capabilitiesDoc = cswClient.getCapabilities(getCapabilitiesEndpoint); + Assert.assertNotNull(capabilitiesDoc, + "Failed to retrieve capabilities document as 'application/xml' from " + getCapabilitiesEndpoint); + Set values = ServiceMetadataUtils.getConstraintValues(capabilitiesDoc, OPENSEARCH_CONSTRAINT); + if (null == values || values.isEmpty()) { + throw new AssertionError(ErrorMessage.format(ErrorMessageKeys.NAMED_ITEM_NOT_FOUND, OPENSEARCH_CONSTRAINT)); + } + URI uri = URI.create(values.iterator().next()); + response = ClientUtils.buildGetRequest(uri, null, MediaType.valueOf(CAT3.APP_VND_OPENSEARCH_XML), + MediaType.valueOf(CAT3.APP_OPENSEARCH_XML)); + Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode(), + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + Assert.assertTrue(XMLUtils.isXML(response.getMediaType()), + ErrorMessage.format(ErrorMessageKeys.NOT_XML, response.getMediaType())); + Document entity = ClientUtils.getResponseEntityAsDocument(response, null); + QName osdDocElemName = new QName(Namespaces.OSD11, "OpenSearchDescription"); + ETSAssert.assertQualifiedName(entity.getDocumentElement(), osdDocElemName); + } - /** - * [Test] Attempts to retrieve an OpenSearch description document using the - * URI presented in the capabilities document as the value of the - * {@value #OPENSEARCH_CONSTRAINT} constraint. - * - * @see "[CAT-HTTP], 6.5.6.2, Table 16" - */ - @Test(description = "[CAT-HTTP]: 6.5.6.2, Table 16") - public void getOpenSearchDescriptionFromCapabilities() { - URI getCapabilitiesEndpoint = ServiceMetadataUtils.getOperationEndpoint( - this.cswCapabilities, CAT3.GET_CAPABILITIES, HttpMethod.GET); - CSWClient cswClient = new CSWClient(); - Document capabilitiesDoc = cswClient.getCapabilities(getCapabilitiesEndpoint); - Assert.assertNotNull(capabilitiesDoc, - "Failed to retrieve capabilities document as 'application/xml' from " - + getCapabilitiesEndpoint); - Set values = ServiceMetadataUtils.getConstraintValues( - capabilitiesDoc, OPENSEARCH_CONSTRAINT); - if (null == values || values.isEmpty()) { - throw new AssertionError(ErrorMessage.format( - ErrorMessageKeys.NAMED_ITEM_NOT_FOUND, OPENSEARCH_CONSTRAINT)); - } - URI uri = URI.create(values.iterator().next()); - response = ClientUtils.buildGetRequest(uri, null, - MediaType.valueOf(CAT3.APP_VND_OPENSEARCH_XML), - MediaType.valueOf(CAT3.APP_OPENSEARCH_XML)); - Assert.assertEquals(response.getStatus(), - Response.Status.OK.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - Assert.assertTrue(XMLUtils.isXML(response.getMediaType()), - ErrorMessage.format(ErrorMessageKeys.NOT_XML, response.getMediaType())); - Document entity = ClientUtils.getResponseEntityAsDocument(response, null); - QName osdDocElemName = new QName(Namespaces.OSD11, "OpenSearchDescription"); - ETSAssert.assertQualifiedName(entity.getDocumentElement(), osdDocElemName); - } } diff --git a/src/main/java/org/opengis/cite/cat30/opensearch/OpenSearchGeoTests.java b/src/main/java/org/opengis/cite/cat30/opensearch/OpenSearchGeoTests.java index a2e30da..e267ca7 100644 --- a/src/main/java/org/opengis/cite/cat30/opensearch/OpenSearchGeoTests.java +++ b/src/main/java/org/opengis/cite/cat30/opensearch/OpenSearchGeoTests.java @@ -45,14 +45,14 @@ /** * Verifies behavior of the SUT when processing OpenSearch requests that contain - * geographic extensions defined in OGC OpenSearch Geo and Time - * Extensions (OGC 10-032r8). The relevant namespace URI is + * geographic extensions defined in OGC OpenSearch Geo and Time Extensions (OGC + * 10-032r8). The relevant namespace URI is * http://a9.com/-/opensearch/extensions/geo/1.0/. * *

        - * All implementations must satisfy the requirements of the - * Core conformance class (see OGC 10-032r8, Table 1). A - * conforming service must:

        + * All implementations must satisfy the requirements of the Core + * conformance class (see OGC 10-032r8, Table 1). A conforming service must: + *

        *
          *
        • present a valid OpenSearch description document;
        • *
        • define a URL template for the Atom response type;
        • @@ -60,234 +60,218 @@ *
        * *

        - * In an Atom feed or entry, a bounding box is represented using a GeoRSS - * element. Either the simple or GML variant is acceptable:

        + * In an Atom feed or entry, a bounding box is represented using a GeoRSS element. Either + * the simple or GML variant is acceptable: + *

        *
          *
        • georss:box (EPSG 4326)
        • - *
        • georss:where/{http://www.opengis.net/gml}Envelope (any - * CRS)
        • + *
        • georss:where/{http://www.opengis.net/gml}Envelope (any CRS)
        • *
        * *

        - * In addition to the service capabilities listed above, a Catalog v3 service - * must also implement the Get record by id conformance class - * that allows a client to retrieve a record by identifier - * (geo:uid). This requirement is a consequence of applying the - * Filter-FES-KVP conformance class to OpenSearch queries (see - * Table 1).

        + * In addition to the service capabilities listed above, a Catalog v3 service must also + * implement the Get record by id conformance class that allows a client + * to retrieve a record by identifier (geo:uid). This requirement is a + * consequence of applying the Filter-FES-KVP conformance class to OpenSearch + * queries (see Table 1). + *

        * - *

        Sources

        + *

        + * Sources + *

        *
          *
        • OGC 10-032r8: OGC OpenSearch Geo and Time Extensions, - * Version 1.0.0
        • + * target="_blank">OGC 10-032r8: OGC OpenSearch Geo and Time Extensions, Version + * 1.0.0 *
        • GeoRSS
        • *
        */ public class OpenSearchGeoTests extends CommonFixture { - static final QName UID_PARAM = new QName(Namespaces.OS_GEO, "uid"); - private Document openSearchDescr; - private List urlTemplates; - /** - * A list of record identifiers retrieved from the SUT. - */ - private List idList; - /** - * An Envelope defining the total geographic extent of the sample data. - */ - private Envelope geoExtent; + static final QName UID_PARAM = new QName(Namespaces.OS_GEO, "uid"); - /** - * Initializes the test fixture. A Document representing an OpenSearch - * description document is obtained from the test context and the URL - * templates it contains are extracted. - * - * @param testContext The test context containing various suite attributes. - */ - @BeforeClass - public void initOpenSearchGeoTestsFixture(ITestContext testContext) { - this.openSearchDescr = (Document) testContext.getSuite().getAttribute( - SuiteAttribute.OPENSEARCH_DESCR.getName()); - if (null == this.openSearchDescr) { - throw new SkipException("OpenSearch description not found in test context."); - } - this.urlTemplates = ServiceMetadataUtils.getOpenSearchURLTemplates( - this.openSearchDescr); - DatasetInfo dataset = (DatasetInfo) testContext.getSuite().getAttribute( - SuiteAttribute.DATASET.getName()); - if (null == dataset) { - throw new SkipException("Dataset info not found in test context."); - } - this.idList = dataset.getRecordIdentifiers(); - this.geoExtent = dataset.getGeographicExtent(); - } + private Document openSearchDescr; - /** - * [Test] Submits an OpenSearch request that includes a (randomly generated) - * identifier for which there is no matching record. Status code 404 (Not - * Found) is expected in response. An error message may be conveyed in the - * response entity. - */ - @Test(description = "Requirement-141") - public void getResourceById_notFound() { - QName uidParam = new QName(Namespaces.OS_GEO, "uid"); - List uidTemplates = OpenSearchTemplateUtils.filterURLTemplatesByParam( - this.urlTemplates, uidParam); - Assert.assertFalse(uidTemplates.isEmpty(), - "No URL templates containing {geo:uid} parameter."); - Map values = new HashMap<>(); - values.put(uidParam, "uid-" + UUID.randomUUID().toString()); - for (Node urlTemplate : uidTemplates) { - Element urlElem = (Element) urlTemplate; - String mediaType = urlElem.getAttribute("type"); - if (!mediaType.contains("xml")) { - continue; // ignore non-XML media types - } - URI uri = OpenSearchTemplateUtils.buildRequestURI(urlElem, values); - response = ClientUtils.buildGetRequest(uri, null, - MediaType.valueOf(mediaType)); - Assert.assertEquals(response.getStatus(), - Response.Status.NOT_FOUND.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - } - } + private List urlTemplates; - /** - * [Test] Submits an OpenSearch request that includes a resource identifier. - * A matching entry is expected in the response, along with the os:Query - * element that describes the search request. The request URI is constructed - * in accord with a URL template containing the {geo:uid} - * parameter. Any other template parameters are set to their default values. - * - * @see "OGC OpenSearch Geo and Time Extensions (OGC 10-032r8), 9.3.2: - * Normal response XML encoding" - */ - @Test(description = "OGC 12-176r6, Table 6") - public void getResourceById() { - List uidTemplates = OpenSearchTemplateUtils.filterURLTemplatesByParam( - this.urlTemplates, UID_PARAM); - Assert.assertFalse(uidTemplates.isEmpty(), - "No URL templates containing {geo:uid} parameter."); - Map values = new HashMap<>(); - int randomIndex = ThreadLocalRandom.current().nextInt(this.idList.size()); - String id = this.idList.get(randomIndex); - values.put(UID_PARAM, URIUtils.getPercentEncodedString(id)); - for (Node urlTemplate : uidTemplates) { - Element url = (Element) urlTemplate; - String mediaType = url.getAttribute("type"); - if (!mediaType.startsWith(MediaType.APPLICATION_ATOM_XML)) { - continue; - } - URI uri = OpenSearchTemplateUtils.buildRequestURI(url, values); - response = ClientUtils.buildGetRequest(uri, null, - MediaType.valueOf(mediaType)); - Assert.assertEquals(response.getStatus(), - Response.Status.OK.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - Document entity = getResponseEntityAsDocument(response, null); - String recordPath = "atom:feed/atom:entry"; - String expr = String.format("/%s/dc:identifier = '%s'", - recordPath, id); - ETSAssert.assertXPath(expr, entity, - Collections.singletonMap(Namespaces.ATOM, "atom")); - Source source = ClientUtils.getResponseEntityAsSource(response, null); - URL schemaUrl = getClass().getResource(SCHEMATRON_ATOM); - ETSAssert.assertSchematronValid(schemaUrl, source); - } - } + /** + * A list of record identifiers retrieved from the SUT. + */ + private List idList; - /** - * [Test] Submits an OpenSearch request that includes a bounding box with - * invalid (UTM) coordinates. Status code 400 (Bad Request) is expected in - * response. An error message may be conveyed in the response entity. - */ - @Test(description = "OGC 10-032r8: 9.3,A.3") - public void invalidBoundingBoxCoords() { - QName boxParam = new QName(Namespaces.OS_GEO, "box"); - List boxTemplates = OpenSearchTemplateUtils.filterURLTemplatesByParam( - this.urlTemplates, boxParam); - Assert.assertFalse(boxTemplates.isEmpty(), - "No URL templates containing {geo:box} parameter."); - Map values = new HashMap<>(); - values.put(boxParam, "514432,5429689,529130,5451619"); - for (Node urlTemplate : boxTemplates) { - Element url = (Element) urlTemplate; - String mediaType = url.getAttribute("type"); - if (!mediaType.contains("xml")) { - continue; // ignore non-XML media types - } - URI uri = OpenSearchTemplateUtils.buildRequestURI(url, values); - response = ClientUtils.buildGetRequest(uri, null, - MediaType.valueOf(mediaType)); - Assert.assertEquals(response.getStatus(), - Response.Status.BAD_REQUEST.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - } - } + /** + * An Envelope defining the total geographic extent of the sample data. + */ + private Envelope geoExtent; + + /** + * Initializes the test fixture. A Document representing an OpenSearch description + * document is obtained from the test context and the URL templates it contains are + * extracted. + * @param testContext The test context containing various suite attributes. + */ + @BeforeClass + public void initOpenSearchGeoTestsFixture(ITestContext testContext) { + this.openSearchDescr = (Document) testContext.getSuite() + .getAttribute(SuiteAttribute.OPENSEARCH_DESCR.getName()); + if (null == this.openSearchDescr) { + throw new SkipException("OpenSearch description not found in test context."); + } + this.urlTemplates = ServiceMetadataUtils.getOpenSearchURLTemplates(this.openSearchDescr); + DatasetInfo dataset = (DatasetInfo) testContext.getSuite().getAttribute(SuiteAttribute.DATASET.getName()); + if (null == dataset) { + throw new SkipException("Dataset info not found in test context."); + } + this.idList = dataset.getRecordIdentifiers(); + this.geoExtent = dataset.getGeographicExtent(); + } + + /** + * [Test] Submits an OpenSearch request that includes a (randomly generated) + * identifier for which there is no matching record. Status code 404 (Not Found) is + * expected in response. An error message may be conveyed in the response entity. + */ + @Test(description = "Requirement-141") + public void getResourceById_notFound() { + QName uidParam = new QName(Namespaces.OS_GEO, "uid"); + List uidTemplates = OpenSearchTemplateUtils.filterURLTemplatesByParam(this.urlTemplates, uidParam); + Assert.assertFalse(uidTemplates.isEmpty(), "No URL templates containing {geo:uid} parameter."); + Map values = new HashMap<>(); + values.put(uidParam, "uid-" + UUID.randomUUID().toString()); + for (Node urlTemplate : uidTemplates) { + Element urlElem = (Element) urlTemplate; + String mediaType = urlElem.getAttribute("type"); + if (!mediaType.contains("xml")) { + continue; // ignore non-XML media types + } + URI uri = OpenSearchTemplateUtils.buildRequestURI(urlElem, values); + response = ClientUtils.buildGetRequest(uri, null, MediaType.valueOf(mediaType)); + Assert.assertEquals(response.getStatus(), Response.Status.NOT_FOUND.getStatusCode(), + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + } + } + + /** + * [Test] Submits an OpenSearch request that includes a resource identifier. A + * matching entry is expected in the response, along with the os:Query element that + * describes the search request. The request URI is constructed in accord with a URL + * template containing the {geo:uid} parameter. Any other template + * parameters are set to their default values. + * + * @see "OGC OpenSearch Geo and Time Extensions (OGC 10-032r8), 9.3.2: Normal response + * XML encoding" + */ + @Test(description = "OGC 12-176r6, Table 6") + public void getResourceById() { + List uidTemplates = OpenSearchTemplateUtils.filterURLTemplatesByParam(this.urlTemplates, UID_PARAM); + Assert.assertFalse(uidTemplates.isEmpty(), "No URL templates containing {geo:uid} parameter."); + Map values = new HashMap<>(); + int randomIndex = ThreadLocalRandom.current().nextInt(this.idList.size()); + String id = this.idList.get(randomIndex); + values.put(UID_PARAM, URIUtils.getPercentEncodedString(id)); + for (Node urlTemplate : uidTemplates) { + Element url = (Element) urlTemplate; + String mediaType = url.getAttribute("type"); + if (!mediaType.startsWith(MediaType.APPLICATION_ATOM_XML)) { + continue; + } + URI uri = OpenSearchTemplateUtils.buildRequestURI(url, values); + response = ClientUtils.buildGetRequest(uri, null, MediaType.valueOf(mediaType)); + Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode(), + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + Document entity = getResponseEntityAsDocument(response, null); + String recordPath = "atom:feed/atom:entry"; + String expr = String.format("/%s/dc:identifier = '%s'", recordPath, id); + ETSAssert.assertXPath(expr, entity, Collections.singletonMap(Namespaces.ATOM, "atom")); + Source source = ClientUtils.getResponseEntityAsSource(response, null); + URL schemaUrl = getClass().getResource(SCHEMATRON_ATOM); + ETSAssert.assertSchematronValid(schemaUrl, source); + } + } + + /** + * [Test] Submits an OpenSearch request that includes a bounding box with invalid + * (UTM) coordinates. Status code 400 (Bad Request) is expected in response. An error + * message may be conveyed in the response entity. + */ + @Test(description = "OGC 10-032r8: 9.3,A.3") + public void invalidBoundingBoxCoords() { + QName boxParam = new QName(Namespaces.OS_GEO, "box"); + List boxTemplates = OpenSearchTemplateUtils.filterURLTemplatesByParam(this.urlTemplates, boxParam); + Assert.assertFalse(boxTemplates.isEmpty(), "No URL templates containing {geo:box} parameter."); + Map values = new HashMap<>(); + values.put(boxParam, "514432,5429689,529130,5451619"); + for (Node urlTemplate : boxTemplates) { + Element url = (Element) urlTemplate; + String mediaType = url.getAttribute("type"); + if (!mediaType.contains("xml")) { + continue; // ignore non-XML media types + } + URI uri = OpenSearchTemplateUtils.buildRequestURI(url, values); + response = ClientUtils.buildGetRequest(uri, null, MediaType.valueOf(mediaType)); + Assert.assertEquals(response.getStatus(), Response.Status.BAD_REQUEST.getStatusCode(), + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + } + } + + /** + * [Test] Submits an OpenSearch request that includes a bounding box in the normative + * CRS ("urn:ogc:def:crs:OGC:1.3:CRS84"). The request URI is constructed in accord + * with a URL template containing the {geo:box} parameter. Any other + * template parameters are set to their default values. + * + *

        + * All matching record representations in the response entity must satisfy the + * bounding box constraint. If the entity is an Atom feed, it must also contain the + * os:Query element that describes the search request. + *

        + * + * @see "OGC OpenSearch Geo and Time Extensions (OGC 10-032r8), 9.3.2: Normal response + * XML encoding" + */ + @Test(description = "Requirements: 022,023; OGC 10-032r8, A.3") + public void boundingBoxQuery() { + QName boxParam = new QName(Namespaces.OS_GEO, "box"); + List boxTemplates = OpenSearchTemplateUtils.filterURLTemplatesByParam(this.urlTemplates, boxParam); + Assert.assertFalse(boxTemplates.isEmpty(), "No URL templates containing {geo:box} parameter."); + Map values = new HashMap<>(); + Envelope bbox = this.geoExtent; + try { + if (!bbox.getCoordinateReferenceSystem().equals(CommonCRS.WGS84.normalizedGeographic())) { + bbox = new GeneralEnvelope(Envelopes.transform(bbox, CommonCRS.WGS84.normalizedGeographic())); + } + } + catch (TransformException ex) { + throw new AssertionError("Failed to create CRS84 box from envelope in source CRS: " + + bbox.getCoordinateReferenceSystem().getName(), ex); + } + values.put(boxParam, Extents.envelopeToString(bbox)); + for (Node urlTemplate : boxTemplates) { + Element url = (Element) urlTemplate; + String mediaType = url.getAttribute("type"); + if (!mediaType.contains("xml")) { + continue; // ignore non-XML media types + } + URI uri = OpenSearchTemplateUtils.buildRequestURI(url, values); + response = ClientUtils.buildGetRequest(uri, null, MediaType.valueOf(mediaType)); + Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode(), + ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); + Document entity = getResponseEntityAsDocument(response, null); + ETSAssert.assertEnvelopeIntersectsBoundingBoxes(bbox, new DOMSource(entity)); + if (mediaType.startsWith(MediaType.APPLICATION_ATOM_XML)) { + Source source = ClientUtils.getResponseEntityAsSource(response, null); + URL schemaUrl = getClass().getResource(SCHEMATRON_ATOM); + ETSAssert.assertSchematronValid(schemaUrl, source); + } + else if (mediaType.startsWith("application/rss+xml")) { + QName docElem = new QName(XMLConstants.NULL_NS_URI, "rss"); + ETSAssert.assertQualifiedName(entity.getDocumentElement(), docElem); + } + else { + QName docElem = new QName(Namespaces.CSW, CAT3.GET_RECORDS_RSP); + ETSAssert.assertQualifiedName(entity.getDocumentElement(), docElem); + } + } + } - /** - * [Test] Submits an OpenSearch request that includes a bounding box in the - * normative CRS ("urn:ogc:def:crs:OGC:1.3:CRS84"). The request URI is - * constructed in accord with a URL template containing the - * {geo:box} parameter. Any other template parameters are set - * to their default values. - * - *

        - * All matching record representations in the response entity must satisfy - * the bounding box constraint. If the entity is an Atom feed, it must also - * contain the os:Query element that describes the search request. - *

        - * - * @see "OGC OpenSearch Geo and Time Extensions (OGC 10-032r8), 9.3.2: - * Normal response XML encoding" - */ - @Test(description = "Requirements: 022,023; OGC 10-032r8, A.3") - public void boundingBoxQuery() { - QName boxParam = new QName(Namespaces.OS_GEO, "box"); - List boxTemplates = OpenSearchTemplateUtils.filterURLTemplatesByParam( - this.urlTemplates, boxParam); - Assert.assertFalse(boxTemplates.isEmpty(), - "No URL templates containing {geo:box} parameter."); - Map values = new HashMap<>(); - Envelope bbox = this.geoExtent; - try { - if (!bbox.getCoordinateReferenceSystem().equals( - CommonCRS.WGS84.normalizedGeographic())) { - bbox = new GeneralEnvelope(Envelopes.transform(bbox, - CommonCRS.WGS84.normalizedGeographic())); - } - } catch (TransformException ex) { - throw new AssertionError("Failed to create CRS84 box from envelope in source CRS: " - + bbox.getCoordinateReferenceSystem().getName(), ex); - } - values.put(boxParam, Extents.envelopeToString(bbox)); - for (Node urlTemplate : boxTemplates) { - Element url = (Element) urlTemplate; - String mediaType = url.getAttribute("type"); - if (!mediaType.contains("xml")) { - continue; // ignore non-XML media types - } - URI uri = OpenSearchTemplateUtils.buildRequestURI(url, values); - response = ClientUtils.buildGetRequest(uri, null, - MediaType.valueOf(mediaType)); - Assert.assertEquals(response.getStatus(), - Response.Status.OK.getStatusCode(), - ErrorMessage.get(ErrorMessageKeys.UNEXPECTED_STATUS)); - Document entity = getResponseEntityAsDocument(response, null); - ETSAssert.assertEnvelopeIntersectsBoundingBoxes(bbox, - new DOMSource(entity)); - if (mediaType.startsWith(MediaType.APPLICATION_ATOM_XML)) { - Source source = ClientUtils.getResponseEntityAsSource(response, null); - URL schemaUrl = getClass().getResource(SCHEMATRON_ATOM); - ETSAssert.assertSchematronValid(schemaUrl, source); - } else if (mediaType.startsWith("application/rss+xml")) { - QName docElem = new QName(XMLConstants.NULL_NS_URI, "rss"); - ETSAssert.assertQualifiedName(entity.getDocumentElement(), docElem); - } else { - QName docElem = new QName(Namespaces.CSW, CAT3.GET_RECORDS_RSP); - ETSAssert.assertQualifiedName(entity.getDocumentElement(), docElem); - } - } - } } diff --git a/src/main/java/org/opengis/cite/cat30/opensearch/OpenSearchPreconditions.java b/src/main/java/org/opengis/cite/cat30/opensearch/OpenSearchPreconditions.java index 582e90e..630c7bc 100644 --- a/src/main/java/org/opengis/cite/cat30/opensearch/OpenSearchPreconditions.java +++ b/src/main/java/org/opengis/cite/cat30/opensearch/OpenSearchPreconditions.java @@ -19,87 +19,79 @@ import org.testng.annotations.BeforeTest; /** - * Checks that various preconditions are satisfied before the tests for - * OpenSearch conformance are run. If any of these checks fail, all applicable - * tests are skipped. + * Checks that various preconditions are satisfied before the tests for OpenSearch + * conformance are run. If any of these checks fail, all applicable tests are skipped. */ public class OpenSearchPreconditions { - private CSWClient cswClient; + private CSWClient cswClient; - void setClient(CSWClient client) { - this.cswClient = client; - } + void setClient(CSWClient client) { + this.cswClient = client; + } - public OpenSearchPreconditions() { - this.cswClient = new CSWClient(); - } + public OpenSearchPreconditions() { + this.cswClient = new CSWClient(); + } + + /** + * Checks that the service capabilities document advertises OpenSearch support. The + * implementation status of the corresponding conformance class must be set to "TRUE". + * The ows:DefaultValue element takes precedence; if it does not appear then the value + * of the first occurrence of the ows:Value element will be checked. + * + *
        {@literal
        +	 *
        +	 *  
        +	 *  
        +	 *    
        +	 *      TRUE
        +	 *      FALSE
        +	 *    
        +	 *    TRUE
        +	 *    Conformance class
        +	 *  
        +	 *
        +	 *}
        +	 * 
        + * + *

        + * An attempt will be made to retrieve an OpenSearch description document from the + * IUT. A representation is expected to be obtained by dereferencing the URI + * corresponding to the GetCapabilities (GET) endpoint. The resulting Document object + * is stored as the value of the suite attribute + * {@link SuiteAttribute#OPENSEARCH_DESCR}. + *

        + * @param testContext Information about the current test run. + * + * @see "OGC 12-176r5, Table 17: Service constraints" + */ + @BeforeTest + public void checkOpenSearchImplementationStatus(ITestContext testContext) { + Document cswCapabilities = (Document) testContext.getSuite() + .getAttribute(SuiteAttribute.TEST_SUBJECT.getName()); + String xpath = String.format("//ows:Constraint[ends-with(@name,'%s')]/ows:DefaultValue", CAT3.CC_OPEN_SEARCH); + XdmValue xdmValue = null; + Map nsMap = Collections.singletonMap(Namespaces.OWS, "ows"); + Source source = new DOMSource(cswCapabilities, cswCapabilities.getDocumentURI()); + try { + xdmValue = XMLUtils.evaluateXPath2(source, xpath, nsMap); + if (xdmValue.size() == 0) { + xpath = String.format("//ows:Constraint[ends-with(@name,'%s')]//ows:Value[1]", CAT3.CC_OPEN_SEARCH); + xdmValue = XMLUtils.evaluateXPath2(source, xpath, nsMap); + } + } + catch (SaxonApiException ex) { // ignore--expressions ok + } + if (xdmValue.size() == 0 || !xdmValue.itemAt(0).getStringValue().trim().equalsIgnoreCase("TRUE")) { + throw new AssertionError("OpenSearch not a supported capability."); + } + this.cswClient.setServiceDescription(cswCapabilities); + Document openSearchDescr = this.cswClient.getOpenSearchDescription(null); + if (null == openSearchDescr) { + throw new AssertionError(ErrorMessage.get(ErrorMessageKeys.OPENSEARCH_UNAVAIL)); + } + testContext.getSuite().setAttribute(SuiteAttribute.OPENSEARCH_DESCR.getName(), openSearchDescr); + } - /** - * Checks that the service capabilities document advertises OpenSearch - * support. The implementation status of the corresponding conformance class - * must be set to "TRUE". The ows:DefaultValue element takes precedence; if - * it does not appear then the value of the first occurrence of the - * ows:Value element will be checked. - * - *
        {@literal
        -     *
        -     *  
        -     *  
        -     *    
        -     *      TRUE
        -     *      FALSE
        -     *    
        -     *    TRUE
        -     *    Conformance class
        -     *  
        -     *
        -     *}
        -     * 
        - * - *

        - * An attempt will be made to retrieve an OpenSearch description document - * from the IUT. A representation is expected to be obtained by - * dereferencing the URI corresponding to the GetCapabilities (GET) - * endpoint. The resulting Document object is stored as the value of the - * suite attribute {@link SuiteAttribute#OPENSEARCH_DESCR}. - *

        - * - * @param testContext Information about the current test run. - * - * @see "OGC 12-176r5, Table 17: Service constraints" - */ - @BeforeTest - public void checkOpenSearchImplementationStatus(ITestContext testContext) { - Document cswCapabilities = (Document) testContext.getSuite().getAttribute( - SuiteAttribute.TEST_SUBJECT.getName()); - String xpath = String.format( - "//ows:Constraint[ends-with(@name,'%s')]/ows:DefaultValue", - CAT3.CC_OPEN_SEARCH); - XdmValue xdmValue = null; - Map nsMap = Collections.singletonMap(Namespaces.OWS, "ows"); - Source source = new DOMSource(cswCapabilities, cswCapabilities.getDocumentURI()); - try { - xdmValue = XMLUtils.evaluateXPath2(source, xpath, nsMap); - if (xdmValue.size() == 0) { - xpath = String.format( - "//ows:Constraint[ends-with(@name,'%s')]//ows:Value[1]", - CAT3.CC_OPEN_SEARCH); - xdmValue = XMLUtils.evaluateXPath2(source, xpath, nsMap); - } - } catch (SaxonApiException ex) { // ignore--expressions ok - } - if (xdmValue.size() == 0 - || !xdmValue.itemAt(0).getStringValue().trim().equalsIgnoreCase("TRUE")) { - throw new AssertionError("OpenSearch not a supported capability."); - } - this.cswClient.setServiceDescription(cswCapabilities); - Document openSearchDescr = this.cswClient.getOpenSearchDescription(null); - if (null == openSearchDescr) { - throw new AssertionError(ErrorMessage.get( - ErrorMessageKeys.OPENSEARCH_UNAVAIL)); - } - testContext.getSuite().setAttribute( - SuiteAttribute.OPENSEARCH_DESCR.getName(), openSearchDescr); - } } diff --git a/src/main/java/org/opengis/cite/cat30/opensearch/TemplateParamInfo.java b/src/main/java/org/opengis/cite/cat30/opensearch/TemplateParamInfo.java index 3cd28e1..402b55c 100644 --- a/src/main/java/org/opengis/cite/cat30/opensearch/TemplateParamInfo.java +++ b/src/main/java/org/opengis/cite/cat30/opensearch/TemplateParamInfo.java @@ -5,139 +5,132 @@ import javax.xml.namespace.QName; /** - * Provides information about a URL template parameter appearing in an - * OpenSearch description document. + * Provides information about a URL template parameter appearing in an OpenSearch + * description document. * - * @see - * OpenSearch + * @see OpenSearch * URL template syntax */ public class TemplateParamInfo { - private QName name; - private boolean isRequired; - private Class type; - private Object defaultValue; - - /** - * Default constructor sets the parameter type to be String with a - * zero-length string as the default value. - */ - public TemplateParamInfo() { - this.type = String.class; - this.defaultValue = new String(); - } - - /** - * Constructs a new TemplateParamInfo with the given qualified name. - * - * @param name A QName representing the name of the parameter. - * @param isRequired A boolean value indicating whether or not the parameter - * is required. - */ - public TemplateParamInfo(QName name, boolean isRequired) { - this(); - this.name = name; - this.isRequired = isRequired; - } - - @Override - public int hashCode() { - int hash = 7; - hash = 67 * hash + Objects.hashCode(this.name); - hash = 67 * hash + (this.isRequired ? 1 : 0); - return hash; - } - - @Override - public boolean equals(Object that) { - boolean isEqual; - if (that == null || this.getClass() != that.getClass()) { - isEqual = false; - } else { - final TemplateParamInfo other = (TemplateParamInfo) that; - isEqual = this.name.equals(other.name) - && this.isRequired == other.isRequired; - } - return isEqual; - } - - /** - * Get the default value of the parameter. - * - * @return A instance of the parameter's type, or a zero-length string if it - * has not been set. - */ - public Object getDefaultValue() { - return defaultValue; - } - - /** - * Set the default value of the parameter. - * - * @param defaultValue A type-compatible value. - */ - public void setDefaultValue(Object defaultValue) { - if (!this.type.isInstance(defaultValue)) { - throw new IllegalArgumentException( - "Default value must be an instance of " + this.type.getName()); - } - this.defaultValue = defaultValue; - } - - /** - * Get the data type of the parameter. - * - * @return The parameter type. - */ - public Class getType() { - return type; - } - - /** - * Set the data type of the parameter (java.lang.String by default). - * - * @param type The new data type. - * - */ - public void setType(Class type) { - this.type = type; - } - - /** - * Get the value of isRequired - * - * @return the value of isRequired - */ - public boolean isRequired() { - return isRequired; - } - - /** - * Set the value of isRequired - * - * @param isRequired new value of isRequired - */ - public void setIsRequired(boolean isRequired) { - this.isRequired = isRequired; - } - - /** - * Get the qualified name of the parameter. - * - * @return A QName object specifying a name. - */ - public QName getName() { - return name; - } - - /** - * Set the (qualified) name of the parameter. - * - * @param name A QName object. - */ - public void setName(QName name) { - this.name = name; - } + private QName name; + + private boolean isRequired; + + private Class type; + + private Object defaultValue; + + /** + * Default constructor sets the parameter type to be String with a zero-length string + * as the default value. + */ + public TemplateParamInfo() { + this.type = String.class; + this.defaultValue = new String(); + } + + /** + * Constructs a new TemplateParamInfo with the given qualified name. + * @param name A QName representing the name of the parameter. + * @param isRequired A boolean value indicating whether or not the parameter is + * required. + */ + public TemplateParamInfo(QName name, boolean isRequired) { + this(); + this.name = name; + this.isRequired = isRequired; + } + + @Override + public int hashCode() { + int hash = 7; + hash = 67 * hash + Objects.hashCode(this.name); + hash = 67 * hash + (this.isRequired ? 1 : 0); + return hash; + } + + @Override + public boolean equals(Object that) { + boolean isEqual; + if (that == null || this.getClass() != that.getClass()) { + isEqual = false; + } + else { + final TemplateParamInfo other = (TemplateParamInfo) that; + isEqual = this.name.equals(other.name) && this.isRequired == other.isRequired; + } + return isEqual; + } + + /** + * Get the default value of the parameter. + * @return A instance of the parameter's type, or a zero-length string if it has not + * been set. + */ + public Object getDefaultValue() { + return defaultValue; + } + + /** + * Set the default value of the parameter. + * @param defaultValue A type-compatible value. + */ + public void setDefaultValue(Object defaultValue) { + if (!this.type.isInstance(defaultValue)) { + throw new IllegalArgumentException("Default value must be an instance of " + this.type.getName()); + } + this.defaultValue = defaultValue; + } + + /** + * Get the data type of the parameter. + * @return The parameter type. + */ + public Class getType() { + return type; + } + + /** + * Set the data type of the parameter (java.lang.String by default). + * @param type The new data type. + * + */ + public void setType(Class type) { + this.type = type; + } + + /** + * Get the value of isRequired + * @return the value of isRequired + */ + public boolean isRequired() { + return isRequired; + } + + /** + * Set the value of isRequired + * @param isRequired new value of isRequired + */ + public void setIsRequired(boolean isRequired) { + this.isRequired = isRequired; + } + + /** + * Get the qualified name of the parameter. + * @return A QName object specifying a name. + */ + public QName getName() { + return name; + } + + /** + * Set the (qualified) name of the parameter. + * @param name A QName object. + */ + public void setName(QName name) { + this.name = name; + } } diff --git a/src/main/java/org/opengis/cite/cat30/opensearch/package-info.java b/src/main/java/org/opengis/cite/cat30/opensearch/package-info.java index 5b0e526..d7cb5d5 100644 --- a/src/main/java/org/opengis/cite/cat30/opensearch/package-info.java +++ b/src/main/java/org/opengis/cite/cat30/opensearch/package-info.java @@ -1,22 +1,27 @@ /** - *

        Includes tests covering capabilities required for OpenSearch - * conformance. The query template parameters listed below must be supported:

        + *

        + * Includes tests covering capabilities required for OpenSearch + * conformance. The query template parameters listed below must be supported: + *

        * *
          - *
        • q={searchTerms}
        • - *
        • startPosition={startIndex?}
        • - *
        • maxRecords={count?}
        • - *
        • bbox={geo:box}
        • - *
        • recordIds={geo:uid}
        • + *
        • q={searchTerms}
        • + *
        • startPosition={startIndex?}
        • + *
        • maxRecords={count?}
        • + *
        • bbox={geo:box}
        • + *
        • recordIds={geo:uid}
        • *
        - * - *

        Sources

        + * + *

        + * Sources + *

        *
          - *
        • [OGC-12-176r5] OGC Catalogue Services 3.0 Specification – HTTP Protocol + *
        • [OGC-12-176r5] OGC Catalogue Services 3.0 Specification – HTTP Protocol * Binding, cl. 6.5.6: Enabling OpenSearch
        • *
        • [OGC-10-032r8] OGC OpenSearch Geo and Time Extensions, Version 1.0
        • - *
        • OpenSearch 1.1 Draft 5
        • + *
        • OpenSearch + * 1.1 + * Draft 5
        • *
        */ package org.opengis.cite.cat30.opensearch; \ No newline at end of file diff --git a/src/main/java/org/opengis/cite/cat30/package-info.java b/src/main/java/org/opengis/cite/cat30/package-info.java index 53fdc1e..3ac141f 100644 --- a/src/main/java/org/opengis/cite/cat30/package-info.java +++ b/src/main/java/org/opengis/cite/cat30/package-info.java @@ -1,8 +1,9 @@ /** - * The root package includes supporting classes of general utility such as the - * main controller, listeners, and reporters. + * The root package includes supporting classes of general utility such as the main + * controller, listeners, and reporters. * - *

        Subsidiary packages correspond to distinct test groups such as conformance - * classes.

        + *

        + * Subsidiary packages correspond to distinct test groups such as conformance classes. + *

        */ -package org.opengis.cite.cat30; \ No newline at end of file +package org.opengis.cite.cat30; \ No newline at end of file diff --git a/src/main/java/org/opengis/cite/cat30/util/CSWClient.java b/src/main/java/org/opengis/cite/cat30/util/CSWClient.java index 770a4a3..c670f4e 100644 --- a/src/main/java/org/opengis/cite/cat30/util/CSWClient.java +++ b/src/main/java/org/opengis/cite/cat30/util/CSWClient.java @@ -23,140 +23,132 @@ */ public class CSWClient { - private static final Logger LOGR = Logger.getLogger(CSWClient.class.getName()); - /** - * A Document that describes the service under test (csw:Capabilities). - */ - private Document cswCapabilities; + private static final Logger LOGR = Logger.getLogger(CSWClient.class.getName()); - public Document getServiceDescription() { - return cswCapabilities; - } + /** + * A Document that describes the service under test (csw:Capabilities). + */ + private Document cswCapabilities; - public void setServiceDescription(Document capabilities) { - if (!capabilities.getDocumentElement().getNamespaceURI().equals(Namespaces.CSW)) { - throw new IllegalArgumentException("Expected a CSW v3 capabilities document."); - } - this.cswCapabilities = capabilities; - } + public Document getServiceDescription() { + return cswCapabilities; + } - /** - * Submits a GetRecords request and saves the response entity to a - * (temporary) file. The {@value org.opengis.cite.cat30.CAT3#ELEMENT_SET} - * parameter is set to "full". - * - * @param maxRecords The maximum number of records to retrieve. - * @param mediaType The preferred response media type; this will constitute - * the value of the Accept header field (generic XML or Atom content). - * - * @return A File containing the response entity (csw:GetRecordsResponse); - * it will be located in the default temporary file directory. - */ - public File saveFullRecords(final int maxRecords, final MediaType mediaType) { - URI getRecordsURI = ServiceMetadataUtils.getOperationEndpoint( - this.cswCapabilities, CAT3.GET_RECORDS, HttpMethod.GET); - Map qryParams = new HashMap<>(); - qryParams.put(CAT3.REQUEST, CAT3.GET_RECORDS); - qryParams.put(CAT3.SERVICE, CAT3.SERVICE_TYPE_CODE); - qryParams.put(CAT3.VERSION, CAT3.VERSION_3_0_0); - qryParams.put(CAT3.MAX_RECORDS, Integer.toString(maxRecords)); - qryParams.put(CAT3.ELEMENT_SET, CAT3.ELEMENT_SET_FULL); - if (mediaType.equals(MediaType.APPLICATION_XML_TYPE)) { - // default namespace is target namespace of default output schema - qryParams.put(CAT3.TYPE_NAMES, "Record"); - } - Response rsp = ClientUtils.buildGetRequest(getRecordsURI, qryParams, - mediaType); - if (rsp.getStatus() != Response.Status.OK.getStatusCode()) { - return null; - } - Document entityDoc = rsp.readEntity(Document.class); - File outputFile = null; - try { - outputFile = File.createTempFile("records-", ".xml"); - FileOutputStream fos = new FileOutputStream(outputFile); - XMLUtils.writeNode(entityDoc, fos); - fos.close(); - } catch (IOException ex) { - LOGR.log(Level.WARNING, - "Failed to save GetRecords response entity to file.", ex); - } - return outputFile; - } + public void setServiceDescription(Document capabilities) { + if (!capabilities.getDocumentElement().getNamespaceURI().equals(Namespaces.CSW)) { + throw new IllegalArgumentException("Expected a CSW v3 capabilities document."); + } + this.cswCapabilities = capabilities; + } - /** - * Retrieves a complete capabilities document from the specified endpoint. - * - * @param uri An absolute URI from which the capabilities can be retrieved; - * if null, the endpoint from a known capabilities document (which may - * differ from the one presented by the IUT) is used. - * @return A Document representing a capabilities document, or null if one - * is not available. - */ - public Document getCapabilities(URI uri) { - if (null == uri || !uri.isAbsolute()) { - uri = ServiceMetadataUtils.getOperationEndpoint( - this.cswCapabilities, CAT3.GET_CAPABILITIES, HttpMethod.GET); - } - Response rsp = ClientUtils.buildGetRequest(uri, null, - MediaType.APPLICATION_XML_TYPE); - Document capabilitiesDoc = null; - if (rsp.getStatus() == Response.Status.OK.getStatusCode() - && XMLUtils.isXML(rsp.getMediaType())) { - capabilitiesDoc = rsp.readEntity(Document.class); - } - return capabilitiesDoc; - } + /** + * Submits a GetRecords request and saves the response entity to a (temporary) file. + * The {@value org.opengis.cite.cat30.CAT3#ELEMENT_SET} parameter is set to "full". + * @param maxRecords The maximum number of records to retrieve. + * @param mediaType The preferred response media type; this will constitute the value + * of the Accept header field (generic XML or Atom content). + * @return A File containing the response entity (csw:GetRecordsResponse); it will be + * located in the default temporary file directory. + */ + public File saveFullRecords(final int maxRecords, final MediaType mediaType) { + URI getRecordsURI = ServiceMetadataUtils.getOperationEndpoint(this.cswCapabilities, CAT3.GET_RECORDS, + HttpMethod.GET); + Map qryParams = new HashMap<>(); + qryParams.put(CAT3.REQUEST, CAT3.GET_RECORDS); + qryParams.put(CAT3.SERVICE, CAT3.SERVICE_TYPE_CODE); + qryParams.put(CAT3.VERSION, CAT3.VERSION_3_0_0); + qryParams.put(CAT3.MAX_RECORDS, Integer.toString(maxRecords)); + qryParams.put(CAT3.ELEMENT_SET, CAT3.ELEMENT_SET_FULL); + if (mediaType.equals(MediaType.APPLICATION_XML_TYPE)) { + // default namespace is target namespace of default output schema + qryParams.put(CAT3.TYPE_NAMES, "Record"); + } + Response rsp = ClientUtils.buildGetRequest(getRecordsURI, qryParams, mediaType); + if (rsp.getStatus() != Response.Status.OK.getStatusCode()) { + return null; + } + Document entityDoc = rsp.readEntity(Document.class); + File outputFile = null; + try { + outputFile = File.createTempFile("records-", ".xml"); + FileOutputStream fos = new FileOutputStream(outputFile); + XMLUtils.writeNode(entityDoc, fos); + fos.close(); + } + catch (IOException ex) { + LOGR.log(Level.WARNING, "Failed to save GetRecords response entity to file.", ex); + } + return outputFile; + } + + /** + * Retrieves a complete capabilities document from the specified endpoint. + * @param uri An absolute URI from which the capabilities can be retrieved; if null, + * the endpoint from a known capabilities document (which may differ from the one + * presented by the IUT) is used. + * @return A Document representing a capabilities document, or null if one is not + * available. + */ + public Document getCapabilities(URI uri) { + if (null == uri || !uri.isAbsolute()) { + uri = ServiceMetadataUtils.getOperationEndpoint(this.cswCapabilities, CAT3.GET_CAPABILITIES, + HttpMethod.GET); + } + Response rsp = ClientUtils.buildGetRequest(uri, null, MediaType.APPLICATION_XML_TYPE); + Document capabilitiesDoc = null; + if (rsp.getStatus() == Response.Status.OK.getStatusCode() && XMLUtils.isXML(rsp.getMediaType())) { + capabilitiesDoc = rsp.readEntity(Document.class); + } + return capabilitiesDoc; + } + + /** + * Retrieves an OpenSearch description document from the IUT. The default endpoint is + * the one corresponding to the (mandatory) GET method binding for the GetCapabilities + * request. The Accept header indicates a preference for the following + * media types: + *
          + *
        • {@value org.opengis.cite.cat30.CAT3#APP_VND_OPENSEARCH_XML}
        • + *
        • {@value org.opengis.cite.cat30.CAT3#APP_OPENSEARCH_XML}
        • + *
        + *

        + * An alternative endpoint may be presented in the capabilities document using the + * "OpenSearchDescriptionDocument" constraint. + *

        + * @param uri An absolute URI from which the OpenSearch description can be retrieved; + * if null, the default endpoint is used. + * @return A Document representing an OpenSearch description document, or null if one + * is not available. + * + * @see OpenSearch description document + */ + public Document getOpenSearchDescription(URI uri) { + if (null == uri || !uri.isAbsolute()) { + uri = ServiceMetadataUtils.getOperationEndpoint(this.cswCapabilities, CAT3.GET_CAPABILITIES, + HttpMethod.GET); + } + Response rsp = ClientUtils.buildGetRequest(uri, null, MediaType.valueOf(CAT3.APP_VND_OPENSEARCH_XML), + MediaType.valueOf(CAT3.APP_OPENSEARCH_XML)); + if (rsp.getStatus() != Response.Status.OK.getStatusCode() || !XMLUtils.isXML(rsp.getMediaType())) { + Set values = ServiceMetadataUtils.getConstraintValues(cswCapabilities, + "OpenSearchDescriptionDocument"); + if (null != values && !values.isEmpty()) { + URI endpoint = URI.create(values.iterator().next()); + if (!endpoint.equals(uri)) { // only attempt once + return getOpenSearchDescription(endpoint); + } + } + LOGR.config(rsp.toString()); + return null; + } + Document entityDoc = rsp.readEntity(Document.class); + if (!entityDoc.getDocumentElement().getNamespaceURI().equals(Namespaces.OSD11)) { + LOGR.config(entityDoc.getDocumentElement().getNodeName()); + return null; + } + return entityDoc; + } - /** - * Retrieves an OpenSearch description document from the IUT. The default - * endpoint is the one corresponding to the (mandatory) GET method binding - * for the GetCapabilities request. The Accept header indicates - * a preference for the following media types: - *
          - *
        • {@value org.opengis.cite.cat30.CAT3#APP_VND_OPENSEARCH_XML}
        • - *
        • {@value org.opengis.cite.cat30.CAT3#APP_OPENSEARCH_XML}
        • - *
        - *

        - * An alternative endpoint may be presented in the capabilities document - * using the "OpenSearchDescriptionDocument" constraint. - *

        - * - * @param uri An absolute URI from which the OpenSearch description can be - * retrieved; if null, the default endpoint is used. - * @return A Document representing an OpenSearch description document, or - * null if one is not available. - * - * @see - * OpenSearch description document - */ - public Document getOpenSearchDescription(URI uri) { - if (null == uri || !uri.isAbsolute()) { - uri = ServiceMetadataUtils.getOperationEndpoint( - this.cswCapabilities, CAT3.GET_CAPABILITIES, HttpMethod.GET); - } - Response rsp = ClientUtils.buildGetRequest(uri, null, - MediaType.valueOf(CAT3.APP_VND_OPENSEARCH_XML), - MediaType.valueOf(CAT3.APP_OPENSEARCH_XML)); - if (rsp.getStatus() != Response.Status.OK.getStatusCode() - || !XMLUtils.isXML(rsp.getMediaType())) { - Set values = ServiceMetadataUtils.getConstraintValues( - cswCapabilities, "OpenSearchDescriptionDocument"); - if (null != values && !values.isEmpty()) { - URI endpoint = URI.create(values.iterator().next()); - if (!endpoint.equals(uri)) { // only attempt once - return getOpenSearchDescription(endpoint); - } - } - LOGR.config(rsp.toString()); - return null; - } - Document entityDoc = rsp.readEntity(Document.class); - if (!entityDoc.getDocumentElement().getNamespaceURI().equals(Namespaces.OSD11)) { - LOGR.config(entityDoc.getDocumentElement().getNodeName()); - return null; - } - return entityDoc; - } } diff --git a/src/main/java/org/opengis/cite/cat30/util/ClientUtils.java b/src/main/java/org/opengis/cite/cat30/util/ClientUtils.java index 5f064d6..73a33a9 100644 --- a/src/main/java/org/opengis/cite/cat30/util/ClientUtils.java +++ b/src/main/java/org/opengis/cite/cat30/util/ClientUtils.java @@ -28,133 +28,117 @@ import jakarta.ws.rs.core.UriBuilder; /** - * Provides various utility methods for creating and configuring HTTP client - * components. + * Provides various utility methods for creating and configuring HTTP client components. */ public class ClientUtils { - private static final Logger LOGGER = Logger.getLogger(ClientUtils.class.getName()); - - /** - * Builds a client component for interacting with HTTP endpoints. The client - * will automatically redirect to the URI declared in 3xx responses. The - * connection timeout is 10 s. Request and response messages may be logged - * to a JDK logger (in the namespace "com.sun.jersey.api.client"). - * - * @return A Client component. - */ - public static Client buildClient() { - ClientConfig config = new ClientConfig(); - config.property(ClientProperties.FOLLOW_REDIRECTS, true); - config.property(ClientProperties.CONNECT_TIMEOUT, 10000); - config.register(new LoggingFeature(LOGGER, Level.ALL, LoggingFeature.Verbosity.PAYLOAD_ANY, 5000)); - Client client = ClientBuilder.newClient(config); - client.register(new ReusableEntityFilter()); - return client; - } + private static final Logger LOGGER = Logger.getLogger(ClientUtils.class.getName()); - /** - * Constructs a client component that uses a specified web proxy. Proxy - * authentication is not supported. Configuring the client to use an - * intercepting proxy can be useful when debugging a test. - * - * @param proxyHost The host name or IP address of the proxy server. - * @param proxyPort The port number of the proxy listener. - * - * @return A Client component that submits requests through a web proxy. - */ - public static Client buildClientWithProxy(final String proxyHost, - final int proxyPort) { - ClientConfig config = new ClientConfig(); - config.connectorProvider(new ApacheConnectorProvider()); - config.register(new LoggingFeature(LOGGER, Level.ALL, LoggingFeature.Verbosity.PAYLOAD_ANY, 5000)); - SocketAddress addr = new InetSocketAddress(proxyHost, proxyPort); - Proxy proxy = new Proxy(Proxy.Type.HTTP, addr); - config.property(ClientProperties.PROXY_URI, proxy); - config.property(ClientProperties.FOLLOW_REDIRECTS, true); - config.property(LoggingFeature.LOGGING_FEATURE_VERBOSITY_CLIENT, LoggingFeature.Verbosity.PAYLOAD_ANY); - config.property(LoggingFeature.LOGGING_FEATURE_LOGGER_LEVEL_CLIENT, Level.ALL); - Client client = ClientBuilder.newClient(config); - client.register(new ReusableEntityFilter()); - return client; - } + /** + * Builds a client component for interacting with HTTP endpoints. The client will + * automatically redirect to the URI declared in 3xx responses. The connection timeout + * is 10 s. Request and response messages may be logged to a JDK logger (in the + * namespace "com.sun.jersey.api.client"). + * @return A Client component. + */ + public static Client buildClient() { + ClientConfig config = new ClientConfig(); + config.property(ClientProperties.FOLLOW_REDIRECTS, true); + config.property(ClientProperties.CONNECT_TIMEOUT, 10000); + config.register(new LoggingFeature(LOGGER, Level.ALL, LoggingFeature.Verbosity.PAYLOAD_ANY, 5000)); + Client client = ClientBuilder.newClient(config); + client.register(new ReusableEntityFilter()); + return client; + } - /** - * Builds an HTTP request message that uses the GET method. - * - * @param endpoint A URI indicating the target resource. - * @param qryParams A Map containing query parameters (may be null); - * @param mediaTypes A list of acceptable media types; if not specified, the - * Accept header is omitted. - * - * @return A ClientRequest object. - */ - public static Response buildGetRequest(URI endpoint, - Map qryParams, MediaType... mediaTypes) { - UriBuilder uriBuilder = UriBuilder.fromUri(endpoint); - if (null != qryParams) { - for (Map.Entry param : qryParams.entrySet()) { - uriBuilder.queryParam(param.getKey(), param.getValue()); - } - } - URI uri = uriBuilder.build(); - WebTarget target = buildClient().target(uri); - Builder reqBuilder = target.request(); - if (null != mediaTypes && mediaTypes.length > 0) { - reqBuilder = reqBuilder.accept(mediaTypes); - } - Invocation req = reqBuilder.buildGet(); - return req.invoke(); - } + /** + * Constructs a client component that uses a specified web proxy. Proxy authentication + * is not supported. Configuring the client to use an intercepting proxy can be useful + * when debugging a test. + * @param proxyHost The host name or IP address of the proxy server. + * @param proxyPort The port number of the proxy listener. + * @return A Client component that submits requests through a web proxy. + */ + public static Client buildClientWithProxy(final String proxyHost, final int proxyPort) { + ClientConfig config = new ClientConfig(); + config.connectorProvider(new ApacheConnectorProvider()); + config.register(new LoggingFeature(LOGGER, Level.ALL, LoggingFeature.Verbosity.PAYLOAD_ANY, 5000)); + SocketAddress addr = new InetSocketAddress(proxyHost, proxyPort); + Proxy proxy = new Proxy(Proxy.Type.HTTP, addr); + config.property(ClientProperties.PROXY_URI, proxy); + config.property(ClientProperties.FOLLOW_REDIRECTS, true); + config.property(LoggingFeature.LOGGING_FEATURE_VERBOSITY_CLIENT, LoggingFeature.Verbosity.PAYLOAD_ANY); + config.property(LoggingFeature.LOGGING_FEATURE_LOGGER_LEVEL_CLIENT, Level.ALL); + Client client = ClientBuilder.newClient(config); + client.register(new ReusableEntityFilter()); + return client; + } - /** - * Creates a copy of the given MediaType object but without any parameters. - * - * @param mediaType A MediaType descriptor. - * @return A new (immutable) MediaType object having the same type and - * subtype. - */ - public static MediaType removeParameters(MediaType mediaType) { - return new MediaType(mediaType.getType(), mediaType.getSubtype()); - } + /** + * Builds an HTTP request message that uses the GET method. + * @param endpoint A URI indicating the target resource. + * @param qryParams A Map containing query parameters (may be null); + * @param mediaTypes A list of acceptable media types; if not specified, the Accept + * header is omitted. + * @return A ClientRequest object. + */ + public static Response buildGetRequest(URI endpoint, Map qryParams, MediaType... mediaTypes) { + UriBuilder uriBuilder = UriBuilder.fromUri(endpoint); + if (null != qryParams) { + for (Map.Entry param : qryParams.entrySet()) { + uriBuilder.queryParam(param.getKey(), param.getValue()); + } + } + URI uri = uriBuilder.build(); + WebTarget target = buildClient().target(uri); + Builder reqBuilder = target.request(); + if (null != mediaTypes && mediaTypes.length > 0) { + reqBuilder = reqBuilder.accept(mediaTypes); + } + Invocation req = reqBuilder.buildGet(); + return req.invoke(); + } - /** - * Obtains the (XML) response entity as a JAXP Source object and resets the - * entity input stream for subsequent reads. - * - * @param response A representation of an HTTP response message. - * @param targetURI The target URI from which the entity was retrieved (may - * be null). - * @return A Source to read the entity from; its system identifier is set - * using the given targetURI value (this may be used to resolve any relative - * URIs found in the source). - */ - public static Source getResponseEntityAsSource(Response response, - String targetURI) { - Source source = response.readEntity(DOMSource.class); - if (null != targetURI && !targetURI.isEmpty()) { - source.setSystemId(targetURI); - } - return source; - } + /** + * Creates a copy of the given MediaType object but without any parameters. + * @param mediaType A MediaType descriptor. + * @return A new (immutable) MediaType object having the same type and subtype. + */ + public static MediaType removeParameters(MediaType mediaType) { + return new MediaType(mediaType.getType(), mediaType.getSubtype()); + } + + /** + * Obtains the (XML) response entity as a JAXP Source object and resets the entity + * input stream for subsequent reads. + * @param response A representation of an HTTP response message. + * @param targetURI The target URI from which the entity was retrieved (may be null). + * @return A Source to read the entity from; its system identifier is set using the + * given targetURI value (this may be used to resolve any relative URIs found in the + * source). + */ + public static Source getResponseEntityAsSource(Response response, String targetURI) { + Source source = response.readEntity(DOMSource.class); + if (null != targetURI && !targetURI.isEmpty()) { + source.setSystemId(targetURI); + } + return source; + } + + /** + * Obtains the (XML) response entity as a DOM Document and resets the entity input + * stream for subsequent reads. + * @param response A representation of an HTTP response message. + * @param targetURI The target URI from which the entity was retrieved (may be null). + * @return A Document representing the entity; its base URI is set using the given + * targetURI value (this may be used to resolve any relative URIs found in the + * document). + */ + public static Document getResponseEntityAsDocument(Response response, String targetURI) { + DOMSource domSource = (DOMSource) getResponseEntityAsSource(response, targetURI); + Document entityDoc = (Document) domSource.getNode(); + entityDoc.setDocumentURI(domSource.getSystemId()); + return entityDoc; + } - /** - * Obtains the (XML) response entity as a DOM Document and resets the entity - * input stream for subsequent reads. - * - * @param response A representation of an HTTP response message. - * @param targetURI The target URI from which the entity was retrieved (may - * be null). - * @return A Document representing the entity; its base URI is set using the - * given targetURI value (this may be used to resolve any relative URIs - * found in the document). - */ - public static Document getResponseEntityAsDocument(Response response, - String targetURI) { - DOMSource domSource = (DOMSource) getResponseEntityAsSource(response, - targetURI); - Document entityDoc = (Document) domSource.getNode(); - entityDoc.setDocumentURI(domSource.getSystemId()); - return entityDoc; - } } diff --git a/src/main/java/org/opengis/cite/cat30/util/DatasetInfo.java b/src/main/java/org/opengis/cite/cat30/util/DatasetInfo.java index 77e8da0..48be2ff 100644 --- a/src/main/java/org/opengis/cite/cat30/util/DatasetInfo.java +++ b/src/main/java/org/opengis/cite/cat30/util/DatasetInfo.java @@ -27,151 +27,142 @@ */ public class DatasetInfo { - private final File dataFile; - private Envelope geographicExtent; - private List recordIdentifiers; - private List recordTitles; - private List topics; - - public DatasetInfo(File dataFile) { - if (!dataFile.isFile()) { - throw new IllegalArgumentException("Data file does not exist at " - + dataFile.getAbsolutePath()); - } - QName docElemName = XMLUtils.nameOfDocumentElement(new StreamSource(dataFile)); - if (!docElemName.getLocalPart().equals("GetRecordsResponse")) { - Logger.getLogger(DatasetInfo.class.getName()).log(Level.WARNING, - "File does not contain a GetRecords response: {0}", docElemName); - } - this.dataFile = dataFile; - } - - /** - * Returns the file containing the sample data. - * - * @return A File object representing a normal file. - */ - public File getDataFile() { - return dataFile; - } - - /** - * Returns an Envelope representing the total geographic extent of the - * sample data. The bounding boxes (ows:BoundingBox or ows:WGS84BoundingBox) - * for each record are coalesced to determine the overall extent. - * - * @return An Envelope in some supported CRS. - */ - public Envelope getGeographicExtent() { - if (null == this.geographicExtent) { - this.geographicExtent = calculateTotalExtent(dataFile); - } - return geographicExtent; - } - - /** - * Returns a sequence of record identifiers (dc:identifier) found in the - * sample data. - * - * @return A List containing all element values. - */ - public List getRecordIdentifiers() { - if (null == this.recordIdentifiers) { - this.recordIdentifiers = Records.findPropertyValues(dataFile, - "//dc:identifier"); - } - return recordIdentifiers; - } - - /** - * Returns a sequence of record titles (dc:title) found in the sample data. - * At least one such element must appear in every record representation. - * - * @return A List containing all element values. - */ - public List getRecordTitles() { - if (null == this.recordTitles) { - this.recordTitles = Records.findPropertyValues(dataFile, "//dc:title"); - } - return recordTitles; - } - - /** - * Returns a sequence of topic (dc:subject) values found in the sample data. - * A record may contain zero or more subject elements that convey a set of - * topics (e.g. keywords, key phrases, classification codes) that apply to - * it. - * - * @return A List containing all topic values. - */ - public List getRecordTopics() { - if (null == this.topics) { - this.topics = Records.findPropertyValues(dataFile, "//dc:subject"); - } - return topics; - } - - /** - * Finds the (infoset) items in the sample data that satisfy the given XPath - * (2.0) expression. - * - * @param xpath The XPath expression to be evaluated. - * @param nsBindings A collection of namespace bindings required to evaluate - * the XPath expression, where each entry maps a namespace URI (key) to a - * prefix (value); bindings for the standard namespaces are not required. - * @return A sequence of zero or more items, where each item is either an - * atomic value or a node. - * @throws SaxonApiException If the expression cannot be evaluated (this - * always wraps some other underlying exception). - */ - public XdmValue findItems(String xpath, Map nsBindings) - throws SaxonApiException { - if (null == nsBindings) { - nsBindings = new HashMap<>(); - nsBindings.put(Namespaces.CSW, "csw"); - nsBindings.put(Namespaces.OWS, "ows"); - nsBindings.put(Namespaces.DCMES, "dc"); - nsBindings.put(Namespaces.DCMI, "dct"); - } - XdmValue results = XMLUtils.evaluateXPath2(new StreamSource(dataFile), - xpath, nsBindings); - return results; - } - - /** - * Calculates the total extent of the records in the sample data. Each - * csw:Record element may contain at least one ows:BoundingBox (or - * ows:WGS84BoundingBox) element that describes the spatial coverage of a - * catalogued resource. - * - * @param file A File containing catalog data (csw:GetRecordsResponse). - * @return An Envelope representing the total geographic extent of the - * sample data, or null if no bounding boxes exist in the data. - */ - Envelope calculateTotalExtent(File file) { - Source src = new StreamSource(dataFile); - NodeList nodeList = null; - try { - nodeList = (NodeList) XMLUtils.evaluateXPath(src, - "//csw:Record/ows:BoundingBox[1] | //csw:Record/ows:WGS84BoundingBox[1]", - null, XPathConstants.NODESET); - } catch (XPathExpressionException xpe) { - TestSuiteLogger.log(Level.WARNING, "getBoundingBoxes: ", xpe); - } - if (nodeList.getLength() == 0) { - return null; - } - List boxNodes = XMLUtils.getNodeListAsList(nodeList); - Envelope extent; - try { - extent = Extents.coalesceBoundingBoxes(boxNodes); - } catch (FactoryException | TransformException ex) { - StringBuilder msg = new StringBuilder("Failed to coalesce bounding boxes. "); - msg.append(new String(ex.getMessage().getBytes(), - StandardCharsets.US_ASCII)); - throw new RuntimeException(msg.toString()); - } - return extent; - } + private final File dataFile; + + private Envelope geographicExtent; + + private List recordIdentifiers; + + private List recordTitles; + + private List topics; + + public DatasetInfo(File dataFile) { + if (!dataFile.isFile()) { + throw new IllegalArgumentException("Data file does not exist at " + dataFile.getAbsolutePath()); + } + QName docElemName = XMLUtils.nameOfDocumentElement(new StreamSource(dataFile)); + if (!docElemName.getLocalPart().equals("GetRecordsResponse")) { + Logger.getLogger(DatasetInfo.class.getName()) + .log(Level.WARNING, "File does not contain a GetRecords response: {0}", docElemName); + } + this.dataFile = dataFile; + } + + /** + * Returns the file containing the sample data. + * @return A File object representing a normal file. + */ + public File getDataFile() { + return dataFile; + } + + /** + * Returns an Envelope representing the total geographic extent of the sample data. + * The bounding boxes (ows:BoundingBox or ows:WGS84BoundingBox) for each record are + * coalesced to determine the overall extent. + * @return An Envelope in some supported CRS. + */ + public Envelope getGeographicExtent() { + if (null == this.geographicExtent) { + this.geographicExtent = calculateTotalExtent(dataFile); + } + return geographicExtent; + } + + /** + * Returns a sequence of record identifiers (dc:identifier) found in the sample data. + * @return A List containing all element values. + */ + public List getRecordIdentifiers() { + if (null == this.recordIdentifiers) { + this.recordIdentifiers = Records.findPropertyValues(dataFile, "//dc:identifier"); + } + return recordIdentifiers; + } + + /** + * Returns a sequence of record titles (dc:title) found in the sample data. At least + * one such element must appear in every record representation. + * @return A List containing all element values. + */ + public List getRecordTitles() { + if (null == this.recordTitles) { + this.recordTitles = Records.findPropertyValues(dataFile, "//dc:title"); + } + return recordTitles; + } + + /** + * Returns a sequence of topic (dc:subject) values found in the sample data. A record + * may contain zero or more subject elements that convey a set of topics (e.g. + * keywords, key phrases, classification codes) that apply to it. + * @return A List containing all topic values. + */ + public List getRecordTopics() { + if (null == this.topics) { + this.topics = Records.findPropertyValues(dataFile, "//dc:subject"); + } + return topics; + } + + /** + * Finds the (infoset) items in the sample data that satisfy the given XPath (2.0) + * expression. + * @param xpath The XPath expression to be evaluated. + * @param nsBindings A collection of namespace bindings required to evaluate the XPath + * expression, where each entry maps a namespace URI (key) to a prefix (value); + * bindings for the standard namespaces are not required. + * @return A sequence of zero or more items, where each item is either an atomic value + * or a node. + * @throws SaxonApiException If the expression cannot be evaluated (this always wraps + * some other underlying exception). + */ + public XdmValue findItems(String xpath, Map nsBindings) throws SaxonApiException { + if (null == nsBindings) { + nsBindings = new HashMap<>(); + nsBindings.put(Namespaces.CSW, "csw"); + nsBindings.put(Namespaces.OWS, "ows"); + nsBindings.put(Namespaces.DCMES, "dc"); + nsBindings.put(Namespaces.DCMI, "dct"); + } + XdmValue results = XMLUtils.evaluateXPath2(new StreamSource(dataFile), xpath, nsBindings); + return results; + } + + /** + * Calculates the total extent of the records in the sample data. Each csw:Record + * element may contain at least one ows:BoundingBox (or ows:WGS84BoundingBox) element + * that describes the spatial coverage of a catalogued resource. + * @param file A File containing catalog data (csw:GetRecordsResponse). + * @return An Envelope representing the total geographic extent of the sample data, or + * null if no bounding boxes exist in the data. + */ + Envelope calculateTotalExtent(File file) { + Source src = new StreamSource(dataFile); + NodeList nodeList = null; + try { + nodeList = (NodeList) XMLUtils.evaluateXPath(src, + "//csw:Record/ows:BoundingBox[1] | //csw:Record/ows:WGS84BoundingBox[1]", null, + XPathConstants.NODESET); + } + catch (XPathExpressionException xpe) { + TestSuiteLogger.log(Level.WARNING, "getBoundingBoxes: ", xpe); + } + if (nodeList.getLength() == 0) { + return null; + } + List boxNodes = XMLUtils.getNodeListAsList(nodeList); + Envelope extent; + try { + extent = Extents.coalesceBoundingBoxes(boxNodes); + } + catch (FactoryException | TransformException ex) { + StringBuilder msg = new StringBuilder("Failed to coalesce bounding boxes. "); + msg.append(new String(ex.getMessage().getBytes(), StandardCharsets.US_ASCII)); + throw new RuntimeException(msg.toString()); + } + return extent; + } } diff --git a/src/main/java/org/opengis/cite/cat30/util/LoggingFilter.java b/src/main/java/org/opengis/cite/cat30/util/LoggingFilter.java index 6b2df63..6eb6807 100644 --- a/src/main/java/org/opengis/cite/cat30/util/LoggingFilter.java +++ b/src/main/java/org/opengis/cite/cat30/util/LoggingFilter.java @@ -9,15 +9,16 @@ import jakarta.ws.rs.client.ClientRequestFilter; public class LoggingFilter implements ClientRequestFilter { - - private static final Logger LOG = LoggerFactory.getLogger(LoggingFilter.class); - @Override - public void filter(ClientRequestContext requestContext) throws IOException { - Object entity = requestContext.getEntity(); - if(entity == null) { - return; - } - LOG.info(entity.toString()); - } + private static final Logger LOG = LoggerFactory.getLogger(LoggingFilter.class); + + @Override + public void filter(ClientRequestContext requestContext) throws IOException { + Object entity = requestContext.getEntity(); + if (entity == null) { + return; + } + LOG.info(entity.toString()); + } + } \ No newline at end of file diff --git a/src/main/java/org/opengis/cite/cat30/util/NamespaceBindings.java b/src/main/java/org/opengis/cite/cat30/util/NamespaceBindings.java index ff2d456..30bc425 100644 --- a/src/main/java/org/opengis/cite/cat30/util/NamespaceBindings.java +++ b/src/main/java/org/opengis/cite/cat30/util/NamespaceBindings.java @@ -9,102 +9,96 @@ import org.opengis.cite.cat30.Namespaces; /** - * Provides namespace bindings for evaluating XPath 1.0 expressions using the - * JAXP XPath API. A namespace name (URI) may be bound to only one prefix. + * Provides namespace bindings for evaluating XPath 1.0 expressions using the JAXP XPath + * API. A namespace name (URI) may be bound to only one prefix. */ public class NamespaceBindings implements NamespaceContext { - private Map bindings = new HashMap(); + private Map bindings = new HashMap(); - @Override - public String getNamespaceURI(String prefix) { - String nsName = null; - for (Map.Entry binding : bindings.entrySet()) { - if (binding.getValue().equals(prefix)) { - nsName = binding.getKey(); - break; - } - } - return nsName; - } + @Override + public String getNamespaceURI(String prefix) { + String nsName = null; + for (Map.Entry binding : bindings.entrySet()) { + if (binding.getValue().equals(prefix)) { + nsName = binding.getKey(); + break; + } + } + return nsName; + } - @Override - public String getPrefix(String namespaceURI) { - return bindings.get(namespaceURI); - } + @Override + public String getPrefix(String namespaceURI) { + return bindings.get(namespaceURI); + } - @Override - public Iterator getPrefixes(String namespaceURI) { - return Arrays.asList(getPrefix(namespaceURI)).iterator(); - } + @Override + public Iterator getPrefixes(String namespaceURI) { + return Arrays.asList(getPrefix(namespaceURI)).iterator(); + } - /** - * Adds a namespace binding that associates a namespace name with a prefix. - * If a binding for a given namespace name already exists it will be - * replaced. - * - * @param namespaceURI A String denoting a namespace name (an absolute URI - * value). - * @param prefix A prefix associated with the namespace name. - */ - public void addNamespaceBinding(String namespaceURI, String prefix) { - bindings.put(namespaceURI, prefix); - } + /** + * Adds a namespace binding that associates a namespace name with a prefix. If a + * binding for a given namespace name already exists it will be replaced. + * @param namespaceURI A String denoting a namespace name (an absolute URI value). + * @param prefix A prefix associated with the namespace name. + */ + public void addNamespaceBinding(String namespaceURI, String prefix) { + bindings.put(namespaceURI, prefix); + } - /** - * Adds all of the supplied namespace bindings to the existing set of - * entries. - * - * @param nsBindings A Map containing a collection of namespace bindings - * where the key is an absolute URI specifying the namespace name and the - * value denotes the associated prefix. - */ - public void addAllBindings(Map nsBindings) { - if (null != nsBindings) { - bindings.putAll(nsBindings); - } - } + /** + * Adds all of the supplied namespace bindings to the existing set of entries. + * @param nsBindings A Map containing a collection of namespace bindings where the key + * is an absolute URI specifying the namespace name and the value denotes the + * associated prefix. + */ + public void addAllBindings(Map nsBindings) { + if (null != nsBindings) { + bindings.putAll(nsBindings); + } + } - /** - * Returns an unmodifiable view of the declared namespace bindings. - * - * @return An immutable Map containing zero or more namespace bindings where - * the key is an absolute URI specifying the namespace name and the value is - * the associated prefix. - */ - public Map getAllBindings() { - return Collections.unmodifiableMap(this.bindings); - } + /** + * Returns an unmodifiable view of the declared namespace bindings. + * @return An immutable Map containing zero or more namespace bindings where the key + * is an absolute URI specifying the namespace name and the value is the associated + * prefix. + */ + public Map getAllBindings() { + return Collections.unmodifiableMap(this.bindings); + } - /** - * Creates a NamespaceBindings object that is configured with the following - * namespace bindings: - * - *
          - *
        • ows: {@value org.opengis.cite.cat30.Namespaces#OWS}
        • - *
        • ows11: {@value org.opengis.cite.cat30.Namespaces#OWS11}
        • - *
        • xlink: {@value org.opengis.cite.cat30.Namespaces#XLINK}
        • - *
        • gml: {@value org.opengis.cite.cat30.Namespaces#GML}
        • - *
        • csw: {@value org.opengis.cite.cat30.Namespaces#CSW}
        • - *
        • dc: {@value org.opengis.cite.cat30.Namespaces#DCMES}
        • - *
        - * - * @return A NamespaceBindings object. - */ - public static NamespaceBindings withStandardBindings() { - NamespaceBindings nsBindings = new NamespaceBindings(); - nsBindings.addNamespaceBinding(Namespaces.OWS, "ows"); - nsBindings.addNamespaceBinding(Namespaces.OWS11, "ows11"); - nsBindings.addNamespaceBinding(Namespaces.XLINK, "xlink"); - nsBindings.addNamespaceBinding(Namespaces.GML, "gml"); - nsBindings.addNamespaceBinding(Namespaces.CSW, "csw"); - nsBindings.addNamespaceBinding(Namespaces.DCMES, "dc"); - nsBindings.addNamespaceBinding(Namespaces.DCMI, "dct"); - return nsBindings; - } + /** + * Creates a NamespaceBindings object that is configured with the following namespace + * bindings: + * + *
          + *
        • ows: {@value org.opengis.cite.cat30.Namespaces#OWS}
        • + *
        • ows11: {@value org.opengis.cite.cat30.Namespaces#OWS11}
        • + *
        • xlink: {@value org.opengis.cite.cat30.Namespaces#XLINK}
        • + *
        • gml: {@value org.opengis.cite.cat30.Namespaces#GML}
        • + *
        • csw: {@value org.opengis.cite.cat30.Namespaces#CSW}
        • + *
        • dc: {@value org.opengis.cite.cat30.Namespaces#DCMES}
        • + *
        + * @return A NamespaceBindings object. + */ + public static NamespaceBindings withStandardBindings() { + NamespaceBindings nsBindings = new NamespaceBindings(); + nsBindings.addNamespaceBinding(Namespaces.OWS, "ows"); + nsBindings.addNamespaceBinding(Namespaces.OWS11, "ows11"); + nsBindings.addNamespaceBinding(Namespaces.XLINK, "xlink"); + nsBindings.addNamespaceBinding(Namespaces.GML, "gml"); + nsBindings.addNamespaceBinding(Namespaces.CSW, "csw"); + nsBindings.addNamespaceBinding(Namespaces.DCMES, "dc"); + nsBindings.addNamespaceBinding(Namespaces.DCMI, "dct"); + return nsBindings; + } + + @Override + public String toString() { + return "NamespaceBindings:\n" + bindings; + } - @Override - public String toString() { - return "NamespaceBindings:\n" + bindings; - } } diff --git a/src/main/java/org/opengis/cite/cat30/util/OpenSearchTemplateUtils.java b/src/main/java/org/opengis/cite/cat30/util/OpenSearchTemplateUtils.java index dd5ca89..f2cd85a 100644 --- a/src/main/java/org/opengis/cite/cat30/util/OpenSearchTemplateUtils.java +++ b/src/main/java/org/opengis/cite/cat30/util/OpenSearchTemplateUtils.java @@ -23,172 +23,159 @@ */ public class OpenSearchTemplateUtils { - /** - * Returns the qualified name of the given OpenSearch URL template - * parameter. - * - * @param param A parameter in a URL template (e.g. "searchTerms", - * "geo:box?"). - * @param contextNode The context node; this is used to perform a namespace - * lookup if necessary. - * @return A QName representing the name of the template parameter. - */ - public static QName getTemplateParameterName(String param, Node contextNode) { - QName paramName; - String[] qName = param.split(":"); - if (qName.length > 1) { - String nsPrefix = qName[0]; - String nsURI = contextNode.lookupNamespaceURI(nsPrefix); - paramName = new QName(nsURI, qName[1].replace("?", ""), nsPrefix); - } else { - paramName = new QName(Namespaces.OSD11, qName[0].replace("?", "")); - } - return paramName; - } + /** + * Returns the qualified name of the given OpenSearch URL template parameter. + * @param param A parameter in a URL template (e.g. "searchTerms", "geo:box?"). + * @param contextNode The context node; this is used to perform a namespace lookup if + * necessary. + * @return A QName representing the name of the template parameter. + */ + public static QName getTemplateParameterName(String param, Node contextNode) { + QName paramName; + String[] qName = param.split(":"); + if (qName.length > 1) { + String nsPrefix = qName[0]; + String nsURI = contextNode.lookupNamespaceURI(nsPrefix); + paramName = new QName(nsURI, qName[1].replace("?", ""), nsPrefix); + } + else { + paramName = new QName(Namespaces.OSD11, qName[0].replace("?", "")); + } + return paramName; + } - /** - * Filters a list of OpenSearch URL templates to include only those that - * contain the specified parameter. - * - * @param urlTemplates A list of (Element) nodes representing URL templates. - * @param paramName The qualified name of a template parameter. - * @return A new list containing templates with the specified parameter. - */ - public static List filterURLTemplatesByParam(List urlTemplates, QName paramName) { - List templates = new ArrayList<>(); - for (Node urlTemplate : urlTemplates) { - List paramList = (List) urlTemplate.getUserData( - ServiceMetadataUtils.URL_TEMPLATE_PARAMS); - if (null != paramList && !paramList.isEmpty()) { - for (TemplateParamInfo paramInfo : paramList) { - if (paramInfo.getName().equals(paramName)) { - templates.add(urlTemplate); - break; - } - } - } - } - return templates; - } + /** + * Filters a list of OpenSearch URL templates to include only those that contain the + * specified parameter. + * @param urlTemplates A list of (Element) nodes representing URL templates. + * @param paramName The qualified name of a template parameter. + * @return A new list containing templates with the specified parameter. + */ + public static List filterURLTemplatesByParam(List urlTemplates, QName paramName) { + List templates = new ArrayList<>(); + for (Node urlTemplate : urlTemplates) { + List paramList = (List) urlTemplate + .getUserData(ServiceMetadataUtils.URL_TEMPLATE_PARAMS); + if (null != paramList && !paramList.isEmpty()) { + for (TemplateParamInfo paramInfo : paramList) { + if (paramInfo.getName().equals(paramName)) { + templates.add(urlTemplate); + break; + } + } + } + } + return templates; + } - /** - * Builds a request URI by processing a URL template according to the given - * parameter substitution values. The default value is used if there is no - * replacement value for a parameter. - * - * @param urlElem An Element node that represents a query endpoint - * (osd:Url). - * @param values A Map containing replacement values for template - * parameters. - * @return A URI representing the resulting request URI. - */ - public static URI buildRequestURI(Element urlElem, Map values) { - List paramList = (List) urlElem.getUserData( - ServiceMetadataUtils.URL_TEMPLATE_PARAMS); - Pattern paramPattern = Pattern.compile("\\{([^}]+)\\}"); - String template = urlElem.getAttribute("template"); - Matcher matcher = paramPattern.matcher(template); - while (matcher.find()) { - String param = matcher.group(0); // entire expression with braces - QName qName = getTemplateParameterName(matcher.group(1), urlElem); - if (values.containsKey(qName)) { - template = template.replace(param, values.get(qName)); - } else { - template = template.replace(param, - getDefaultParamValue(paramList, qName)); - } - } - return URI.create(template); - } + /** + * Builds a request URI by processing a URL template according to the given parameter + * substitution values. The default value is used if there is no replacement value for + * a parameter. + * @param urlElem An Element node that represents a query endpoint (osd:Url). + * @param values A Map containing replacement values for template parameters. + * @return A URI representing the resulting request URI. + */ + public static URI buildRequestURI(Element urlElem, Map values) { + List paramList = (List) urlElem + .getUserData(ServiceMetadataUtils.URL_TEMPLATE_PARAMS); + Pattern paramPattern = Pattern.compile("\\{([^}]+)\\}"); + String template = urlElem.getAttribute("template"); + Matcher matcher = paramPattern.matcher(template); + while (matcher.find()) { + String param = matcher.group(0); // entire expression with braces + QName qName = getTemplateParameterName(matcher.group(1), urlElem); + if (values.containsKey(qName)) { + template = template.replace(param, values.get(qName)); + } + else { + template = template.replace(param, getDefaultParamValue(paramList, qName)); + } + } + return URI.create(template); + } - /** - * Returns the default value of the specified template parameter. - * - * @param paramList A list of TemplateParamInfo objects describing the - * declared template parameters. - * @param paramName The qualified name of a template parameter. - * @return A String representing the default value (possibly an empty - * string). - */ - public static String getDefaultParamValue(List paramList, - QName paramName) { - String value = null; - for (TemplateParamInfo paramInfo : paramList) { - if (paramInfo.getName().equals(paramName)) { - value = paramInfo.getDefaultValue().toString(); - break; - } - } - return value; - } + /** + * Returns the default value of the specified template parameter. + * @param paramList A list of TemplateParamInfo objects describing the declared + * template parameters. + * @param paramName The qualified name of a template parameter. + * @return A String representing the default value (possibly an empty string). + */ + public static String getDefaultParamValue(List paramList, QName paramName) { + String value = null; + for (TemplateParamInfo paramInfo : paramList) { + if (paramInfo.getName().equals(paramName)) { + value = paramInfo.getDefaultValue().toString(); + break; + } + } + return value; + } - /** - * Updates the type and default value of a standard OpenSearch parameter. - * - * @param paramInfo A TemplateParamInfo object describing a standard - * OpenSearch parameter. - * @param urlElem An Element node (osd:Url) that defines an OpenSearch - * request containing the parameter. - * - * @see - * OpenSearch - * 1.1 parameters - */ - public static void updateOpenSearchParameter(TemplateParamInfo paramInfo, - Element urlElem) { - String paramName = paramInfo.getName().getLocalPart(); - switch (paramName) { - case "count": - paramInfo.setType(Integer.class); - break; - case "startIndex": - paramInfo.setType(Integer.class); - String indexOffset = urlElem.getAttribute("indexOffset"); - Integer defaultValue = (indexOffset.isEmpty()) ? 1 : Integer.decode(indexOffset); - paramInfo.setDefaultValue(defaultValue); - break; - case "startPage": - paramInfo.setType(Integer.class); - String pageOffset = urlElem.getAttribute("pageOffset"); - defaultValue = (pageOffset.isEmpty()) ? 1 : Integer.decode(pageOffset); - paramInfo.setDefaultValue(defaultValue); - break; - case "language": - paramInfo.setType(String.class); - paramInfo.setDefaultValue("*"); - break; - case "inputEncoding": - case "outputEncoding": - paramInfo.setType(Charset.class); - paramInfo.setDefaultValue(StandardCharsets.UTF_8); - break; - default: - paramInfo.setType(String.class); - } - } + /** + * Updates the type and default value of a standard OpenSearch parameter. + * @param paramInfo A TemplateParamInfo object describing a standard OpenSearch + * parameter. + * @param urlElem An Element node (osd:Url) that defines an OpenSearch request + * containing the parameter. + * + * @see OpenSearch + * 1.1 parameters + */ + public static void updateOpenSearchParameter(TemplateParamInfo paramInfo, Element urlElem) { + String paramName = paramInfo.getName().getLocalPart(); + switch (paramName) { + case "count": + paramInfo.setType(Integer.class); + break; + case "startIndex": + paramInfo.setType(Integer.class); + String indexOffset = urlElem.getAttribute("indexOffset"); + Integer defaultValue = (indexOffset.isEmpty()) ? 1 : Integer.decode(indexOffset); + paramInfo.setDefaultValue(defaultValue); + break; + case "startPage": + paramInfo.setType(Integer.class); + String pageOffset = urlElem.getAttribute("pageOffset"); + defaultValue = (pageOffset.isEmpty()) ? 1 : Integer.decode(pageOffset); + paramInfo.setDefaultValue(defaultValue); + break; + case "language": + paramInfo.setType(String.class); + paramInfo.setDefaultValue("*"); + break; + case "inputEncoding": + case "outputEncoding": + paramInfo.setType(Charset.class); + paramInfo.setDefaultValue(StandardCharsets.UTF_8); + break; + default: + paramInfo.setType(String.class); + } + } - /** - * Extracts the actual parameters from an OpenSearch query specification. - * - * @param query A Node representing an osd:Query element. - * @return A Map containing the query parameters, where the key is the - * (qualified) parameter name. - */ - public static Map getQueryParameters(Node query) { - Map params = new HashMap<>(); - NamedNodeMap attribs = query.getAttributes(); - List metadataAttribs = Arrays.asList( - new String[]{"role", "title", "totalResults"}); - for (int i = 0; i < attribs.getLength(); i++) { - Node attr = attribs.item(i); - String attrName = attr.getNodeName(); - if (metadataAttribs.contains(attrName)) { - continue; //ignore metadata attributes - } - QName paramName = getTemplateParameterName(attrName, query); - params.put(paramName, attr.getNodeValue()); - } - return params; - } + /** + * Extracts the actual parameters from an OpenSearch query specification. + * @param query A Node representing an osd:Query element. + * @return A Map containing the query parameters, where the key is the (qualified) + * parameter name. + */ + public static Map getQueryParameters(Node query) { + Map params = new HashMap<>(); + NamedNodeMap attribs = query.getAttributes(); + List metadataAttribs = Arrays.asList(new String[] { "role", "title", "totalResults" }); + for (int i = 0; i < attribs.getLength(); i++) { + Node attr = attribs.item(i); + String attrName = attr.getNodeName(); + if (metadataAttribs.contains(attrName)) { + continue; // ignore metadata attributes + } + QName paramName = getTemplateParameterName(attrName, query); + params.put(paramName, attr.getNodeValue()); + } + return params; + } } diff --git a/src/main/java/org/opengis/cite/cat30/util/Records.java b/src/main/java/org/opengis/cite/cat30/util/Records.java index 044d750..7d6a752 100644 --- a/src/main/java/org/opengis/cite/cat30/util/Records.java +++ b/src/main/java/org/opengis/cite/cat30/util/Records.java @@ -24,178 +24,172 @@ import org.w3c.dom.NodeList; /** - * This class contains various utility methods for querying or reading - * representations of catalog records. + * This class contains various utility methods for querying or reading representations of + * catalog records. */ public class Records { - /** - * Generates a random sequence of 5-14 characters in the range [a-z]. Such - * text is very unlikely to match the content of any record fields. - * - * @return A String containing lower case letters (Latin alphabet). - */ - public static String generateRandomText() { - Random r = new Random(); - int length = r.nextInt(10) + 5; - StringBuilder str = new StringBuilder(); - for (int i = 0; i < length; i++) { - char letter = (char) (r.nextInt(26) + 'a'); - str.append(letter); - } - return str.toString(); - } + /** + * Generates a random sequence of 5-14 characters in the range [a-z]. Such text is + * very unlikely to match the content of any record fields. + * @return A String containing lower case letters (Latin alphabet). + */ + public static String generateRandomText() { + Random r = new Random(); + int length = r.nextInt(10) + 5; + StringBuilder str = new StringBuilder(); + for (int i = 0; i < length; i++) { + char letter = (char) (r.nextInt(26) + 'a'); + str.append(letter); + } + return str.toString(); + } - /** - * Gets the identifier of the given record representation. - * - * @param record An Element node representing an entry in a result set - * (csw:Record or atom:entry). - * @return The element name with a record identifier appended (the first - * identifier if there is more than one). - */ - public static String getRecordId(Element record) { - StringBuilder str = new StringBuilder(record.getNodeName()); - NodeList idList = record.getElementsByTagNameNS(Namespaces.DCMES, "identifier"); - if (idList.getLength() == 0) { - idList = record.getElementsByTagNameNS(Namespaces.ATOM, "id"); - } - if (idList.getLength() > 0) { - str.append("[").append(idList.item(0).getTextContent()).append("]"); - } - return str.toString(); - } + /** + * Gets the identifier of the given record representation. + * @param record An Element node representing an entry in a result set (csw:Record or + * atom:entry). + * @return The element name with a record identifier appended (the first identifier if + * there is more than one). + */ + public static String getRecordId(Element record) { + StringBuilder str = new StringBuilder(record.getNodeName()); + NodeList idList = record.getElementsByTagNameNS(Namespaces.DCMES, "identifier"); + if (idList.getLength() == 0) { + idList = record.getElementsByTagNameNS(Namespaces.ATOM, "id"); + } + if (idList.getLength() > 0) { + str.append("[").append(idList.item(0).getTextContent()).append("]"); + } + return str.toString(); + } - /** - * Evaluates an XPath (2.0) expression against the sample data and returns - * the results as a list of string values. - * - * @param file A File containing catalog data (csw:GetRecordsResponse). - * @param xpath An XPath expression that is expected to denote a simple - * record property; the namespace prefixes "dc" and "dct" may be used (e.g. - * //dc:title). - * @return A list of property values, which may be empty if none are found - * in the sample data. - */ - public static List findPropertyValues(File file, String xpath) { - List idList = new ArrayList<>(); - Source src = new StreamSource(file); - Map nsBindings = new HashMap<>(); - nsBindings.put(Namespaces.DCMES, "dc"); - nsBindings.put(Namespaces.DCMI, "dct"); - XdmValue value = null; - try { - value = XMLUtils.evaluateXPath2(src, xpath, nsBindings); - } catch (SaxonApiException ex) { - Logger.getLogger(DatasetInfo.class.getName()).log(Level.WARNING, - "Failed to evaluate XPath expression: " + xpath, ex); - } - for (XdmItem item : value) { - idList.add(item.getStringValue()); - } - return idList; - } + /** + * Evaluates an XPath (2.0) expression against the sample data and returns the results + * as a list of string values. + * @param file A File containing catalog data (csw:GetRecordsResponse). + * @param xpath An XPath expression that is expected to denote a simple record + * property; the namespace prefixes "dc" and "dct" may be used (e.g. //dc:title). + * @return A list of property values, which may be empty if none are found in the + * sample data. + */ + public static List findPropertyValues(File file, String xpath) { + List idList = new ArrayList<>(); + Source src = new StreamSource(file); + Map nsBindings = new HashMap<>(); + nsBindings.put(Namespaces.DCMES, "dc"); + nsBindings.put(Namespaces.DCMI, "dct"); + XdmValue value = null; + try { + value = XMLUtils.evaluateXPath2(src, xpath, nsBindings); + } + catch (SaxonApiException ex) { + Logger.getLogger(DatasetInfo.class.getName()) + .log(Level.WARNING, "Failed to evaluate XPath expression: " + xpath, ex); + } + for (XdmItem item : value) { + idList.add(item.getStringValue()); + } + return idList; + } - /** - * Finds matching search terms corresponding to the given record elements. - * One term is selected for each element. If there are no records that - * contain values for all elements, two randomly selected title words are - * returned instead. - * - * @param dataFile A file containing sample data (csw:Record - * representations). - * @param elemNames A list of QName objects denoting the qualified names of - * record elements. - * @return A string containing two or more terms (separated by a space - * character). - */ - public static String findMatchingSearchTerms(File dataFile, QName... elemNames) { - StringBuilder searchTerms = new StringBuilder(); - XdmValue results = findRecordsInSampleData(dataFile, elemNames); - if (results.size() > 0) { - XdmNode lastNode = (XdmNode) results.itemAt(results.size() - 1); - for (QName elemName : elemNames) { - String value = lastNode.axisIterator(Axis.CHILD, - new net.sf.saxon.s9api.QName(elemName)).next().getStringValue(); - // element content may contain multiple words - searchTerms.append(value.trim().split("\\s+")[0]).append(" "); - } - } else { // fallback: use randomly selected title words - String[] titleWords; - ThreadLocalRandom random = ThreadLocalRandom.current(); - List titles = findPropertyValues(dataFile, "//dc:title"); - do { - int randomIndex = random.nextInt(titles.size()); - titleWords = titles.get(randomIndex).trim().split("\\s+"); - } while (titleWords.length < 2); - searchTerms.append(titleWords[titleWords.length - 1]).append(" "); - searchTerms.append(titleWords[0]); - } - return searchTerms.toString().trim(); - } + /** + * Finds matching search terms corresponding to the given record elements. One term is + * selected for each element. If there are no records that contain values for all + * elements, two randomly selected title words are returned instead. + * @param dataFile A file containing sample data (csw:Record representations). + * @param elemNames A list of QName objects denoting the qualified names of record + * elements. + * @return A string containing two or more terms (separated by a space character). + */ + public static String findMatchingSearchTerms(File dataFile, QName... elemNames) { + StringBuilder searchTerms = new StringBuilder(); + XdmValue results = findRecordsInSampleData(dataFile, elemNames); + if (results.size() > 0) { + XdmNode lastNode = (XdmNode) results.itemAt(results.size() - 1); + for (QName elemName : elemNames) { + String value = lastNode.axisIterator(Axis.CHILD, new net.sf.saxon.s9api.QName(elemName)) + .next() + .getStringValue(); + // element content may contain multiple words + searchTerms.append(value.trim().split("\\s+")[0]).append(" "); + } + } + else { // fallback: use randomly selected title words + String[] titleWords; + ThreadLocalRandom random = ThreadLocalRandom.current(); + List titles = findPropertyValues(dataFile, "//dc:title"); + do { + int randomIndex = random.nextInt(titles.size()); + titleWords = titles.get(randomIndex).trim().split("\\s+"); + } + while (titleWords.length < 2); + searchTerms.append(titleWords[titleWords.length - 1]).append(" "); + searchTerms.append(titleWords[0]); + } + return searchTerms.toString().trim(); + } - /** - * Finds records in the sample data that contain all of the specified child - * elements (some of which may occur more than once). - * - * @param dataFile A file containing sample data (csw:Record - * representations). - * @param properties A list of QName objects denoting the qualified names of - * record elements. - * @return An immutable XdmValue object containing zero or more matching - * nodes (XdmItem). - */ - public static XdmValue findRecordsInSampleData(File dataFile, - QName... properties) { - StringBuilder xpath = new StringBuilder("//csw:Record["); - Map nsBindings = new HashMap<>(); - nsBindings.put(Namespaces.CSW, "csw"); - NamespaceBindings stdBindings = NamespaceBindings.withStandardBindings(); - for (int i = 0; i < properties.length; i++) { - QName elemName = properties[i]; - String localName = elemName.getLocalPart(); - String nsName = elemName.getNamespaceURI(); - String nsPrefix = stdBindings.getPrefix(nsName); - nsBindings.put(nsName, nsPrefix); - xpath.append(nsPrefix).append(':').append(localName); - if (i < (properties.length - 1)) { - xpath.append(" and "); - } - } - xpath.append("]"); - XdmValue value = null; - try { - value = XMLUtils.evaluateXPath2(new StreamSource(dataFile), - xpath.toString(), nsBindings); - } catch (SaxonApiException x) { - Logger.getLogger(Records.class.getName()).log(Level.WARNING, - String.format( - "Failed to evaluate XPath expression against sample data file at %s:\n %s ", - dataFile.getAbsolutePath(), xpath), x); - } - return value; - } + /** + * Finds records in the sample data that contain all of the specified child elements + * (some of which may occur more than once). + * @param dataFile A file containing sample data (csw:Record representations). + * @param properties A list of QName objects denoting the qualified names of record + * elements. + * @return An immutable XdmValue object containing zero or more matching nodes + * (XdmItem). + */ + public static XdmValue findRecordsInSampleData(File dataFile, QName... properties) { + StringBuilder xpath = new StringBuilder("//csw:Record["); + Map nsBindings = new HashMap<>(); + nsBindings.put(Namespaces.CSW, "csw"); + NamespaceBindings stdBindings = NamespaceBindings.withStandardBindings(); + for (int i = 0; i < properties.length; i++) { + QName elemName = properties[i]; + String localName = elemName.getLocalPart(); + String nsName = elemName.getNamespaceURI(); + String nsPrefix = stdBindings.getPrefix(nsName); + nsBindings.put(nsName, nsPrefix); + xpath.append(nsPrefix).append(':').append(localName); + if (i < (properties.length - 1)) { + xpath.append(" and "); + } + } + xpath.append("]"); + XdmValue value = null; + try { + value = XMLUtils.evaluateXPath2(new StreamSource(dataFile), xpath.toString(), nsBindings); + } + catch (SaxonApiException x) { + Logger.getLogger(Records.class.getName()) + .log(Level.WARNING, + String.format("Failed to evaluate XPath expression against sample data file at %s:\n %s ", + dataFile.getAbsolutePath(), xpath), + x); + } + return value; + } + + /** + * Returns the name of the catalog record corresponding to the specified media type. + * @param mediaType A string identifying a media type. + * @return The qualified name of the element representing a catalog record. + * + * @see IANA + * Media Types Register + */ + public static QName getRecordName(String mediaType) { + QName recordName; + if (mediaType.startsWith(MediaType.APPLICATION_ATOM_XML)) { + recordName = new QName(Namespaces.ATOM, "entry"); + } + else if (mediaType.startsWith("application/rss+xml")) { + recordName = new QName(XMLConstants.NULL_NS_URI, "item"); + } + else { + recordName = new QName(Namespaces.CSW, "Record"); + } + return recordName; + } - /** - * Returns the name of the catalog record corresponding to the specified - * media type. - * - * @param mediaType A string identifying a media type. - * @return The qualified name of the element representing a catalog record. - * - * @see - * IANA - * Media Types Register - */ - public static QName getRecordName(String mediaType) { - QName recordName; - if (mediaType.startsWith(MediaType.APPLICATION_ATOM_XML)) { - recordName = new QName(Namespaces.ATOM, "entry"); - } else if (mediaType.startsWith("application/rss+xml")) { - recordName = new QName(XMLConstants.NULL_NS_URI, "item"); - } else { - recordName = new QName(Namespaces.CSW, "Record"); - } - return recordName; - } } diff --git a/src/main/java/org/opengis/cite/cat30/util/ServiceMetadataUtils.java b/src/main/java/org/opengis/cite/cat30/util/ServiceMetadataUtils.java index b2cc023..bcbff44 100644 --- a/src/main/java/org/opengis/cite/cat30/util/ServiceMetadataUtils.java +++ b/src/main/java/org/opengis/cite/cat30/util/ServiceMetadataUtils.java @@ -31,226 +31,198 @@ import org.w3c.dom.NodeList; /** - * Provides various utility methods for accessing service-related metadata - * resources such as an OGC service description or an OpenSearch description - * document. + * Provides various utility methods for accessing service-related metadata resources such + * as an OGC service description or an OpenSearch description document. */ public class ServiceMetadataUtils { - /** - * Key value that associates an Element node (osd:Url in an OpenSearch - * description document) with the collection of URL template parameters it - * declares. - */ - public static final String URL_TEMPLATE_PARAMS = "url.template.params"; + /** + * Key value that associates an Element node (osd:Url in an OpenSearch description + * document) with the collection of URL template parameters it declares. + */ + public static final String URL_TEMPLATE_PARAMS = "url.template.params"; - /** - * Extracts a request endpoint from a service capabilities document. If the - * request URI contains a query component it is ignored. - * - * @param cswMetadata A DOM Document node containing service metadata (OGC - * capabilities document). - * @param opName The operation (request) name. - * @param httpMethod The HTTP method to use (if {@code null} or empty the - * first method listed will be used). - * - * @return A URI denoting a service endpoint; the URI is empty if no - * matching endpoint was found. - */ - public static URI getOperationEndpoint(final Document cswMetadata, - String opName, String httpMethod) { - String expr; - if (null == httpMethod || httpMethod.isEmpty()) { - // use first supported method - expr = String.format( - "//ows:Operation[@name='%s']//ows:HTTP/*[1]/@xlink:href", - opName); - } else { - // method name in OWS content model is "Get" or "Post" - StringBuilder method = new StringBuilder(httpMethod); - method.replace(1, method.length(), method.substring(1) - .toLowerCase()); - expr = String.format( - "//ows:Operation[@name='%s']//ows:%s/@xlink:href", opName, - method.toString()); - } - NamespaceBindings nsBindings = new NamespaceBindings(); - nsBindings.addNamespaceBinding(Namespaces.OWS, "ows"); - nsBindings.addNamespaceBinding(Namespaces.XLINK, "xlink"); - XPath xpath = XPathFactory.newInstance().newXPath(); - xpath.setNamespaceContext(nsBindings); - URI endpoint = null; - try { - String href = xpath.evaluate(expr, cswMetadata); - endpoint = URI.create(href); - } catch (XPathExpressionException ex) { - // XPath expression is correct - TestSuiteLogger.log(Level.INFO, ex.getMessage()); - } - if (null != endpoint && null != endpoint.getQuery()) { - // prune query component if present - String uriRef = endpoint.toString(); - endpoint = URI.create(uriRef.substring(0, uriRef.indexOf('?'))); - } - return endpoint; - } + /** + * Extracts a request endpoint from a service capabilities document. If the request + * URI contains a query component it is ignored. + * @param cswMetadata A DOM Document node containing service metadata (OGC + * capabilities document). + * @param opName The operation (request) name. + * @param httpMethod The HTTP method to use (if {@code null} or empty the first method + * listed will be used). + * @return A URI denoting a service endpoint; the URI is empty if no matching endpoint + * was found. + */ + public static URI getOperationEndpoint(final Document cswMetadata, String opName, String httpMethod) { + String expr; + if (null == httpMethod || httpMethod.isEmpty()) { + // use first supported method + expr = String.format("//ows:Operation[@name='%s']//ows:HTTP/*[1]/@xlink:href", opName); + } + else { + // method name in OWS content model is "Get" or "Post" + StringBuilder method = new StringBuilder(httpMethod); + method.replace(1, method.length(), method.substring(1).toLowerCase()); + expr = String.format("//ows:Operation[@name='%s']//ows:%s/@xlink:href", opName, method.toString()); + } + NamespaceBindings nsBindings = new NamespaceBindings(); + nsBindings.addNamespaceBinding(Namespaces.OWS, "ows"); + nsBindings.addNamespaceBinding(Namespaces.XLINK, "xlink"); + XPath xpath = XPathFactory.newInstance().newXPath(); + xpath.setNamespaceContext(nsBindings); + URI endpoint = null; + try { + String href = xpath.evaluate(expr, cswMetadata); + endpoint = URI.create(href); + } + catch (XPathExpressionException ex) { + // XPath expression is correct + TestSuiteLogger.log(Level.INFO, ex.getMessage()); + } + if (null != endpoint && null != endpoint.getQuery()) { + // prune query component if present + String uriRef = endpoint.toString(); + endpoint = URI.create(uriRef.substring(0, uriRef.indexOf('?'))); + } + return endpoint; + } - /** - * Returns a list of nodes representing the URL templates defined in an - * OpenSearch description document. Each node in the resulting list will be - * associated with an unmodifiable {@code List} - * containing information about the declared template parameters; it can be - * accessed via {@link Node#getUserData(java.lang.String) getUserData} using - * the key value {@link #URL_TEMPLATE_PARAMS}. - * - * @param osDescr An OpenSearchDescription document - * (osd:OpenSearchDescription). - * @return A sequence of Element nodes (os:Url) containing URL templates. - */ - public static List getOpenSearchURLTemplates(final Document osDescr) { - List urlList = new ArrayList<>(); - NodeList nodes = osDescr.getElementsByTagNameNS(Namespaces.OSD11, "Url"); - Pattern queryParam = Pattern.compile("\\{([^}]+)\\}"); - for (int i = 0; i < nodes.getLength(); i++) { - Element urlElem = (Element) nodes.item(i); - String template = urlElem.getAttribute("template"); - Matcher matcher = queryParam.matcher(template); - List templateParamList = new ArrayList<>(); - while (matcher.find()) { - TemplateParamInfo paramInfo = new TemplateParamInfo(); - String param = matcher.group(1); // first capturing group - paramInfo.setIsRequired(!param.endsWith("?")); - QName paramQName = OpenSearchTemplateUtils.getTemplateParameterName(param, urlElem); - paramInfo.setName(paramQName); - templateParamList.add(paramInfo); - if (paramQName.getNamespaceURI().equals(Namespaces.OSD11)) { - OpenSearchTemplateUtils.updateOpenSearchParameter(paramInfo, urlElem); - } - } - urlElem.setUserData(URL_TEMPLATE_PARAMS, - Collections.unmodifiableList(templateParamList), null); - urlList.add(urlElem); - } - return urlList; - } + /** + * Returns a list of nodes representing the URL templates defined in an OpenSearch + * description document. Each node in the resulting list will be associated with an + * unmodifiable {@code List} containing information about the + * declared template parameters; it can be accessed via + * {@link Node#getUserData(java.lang.String) getUserData} using the key value + * {@link #URL_TEMPLATE_PARAMS}. + * @param osDescr An OpenSearchDescription document (osd:OpenSearchDescription). + * @return A sequence of Element nodes (os:Url) containing URL templates. + */ + public static List getOpenSearchURLTemplates(final Document osDescr) { + List urlList = new ArrayList<>(); + NodeList nodes = osDescr.getElementsByTagNameNS(Namespaces.OSD11, "Url"); + Pattern queryParam = Pattern.compile("\\{([^}]+)\\}"); + for (int i = 0; i < nodes.getLength(); i++) { + Element urlElem = (Element) nodes.item(i); + String template = urlElem.getAttribute("template"); + Matcher matcher = queryParam.matcher(template); + List templateParamList = new ArrayList<>(); + while (matcher.find()) { + TemplateParamInfo paramInfo = new TemplateParamInfo(); + String param = matcher.group(1); // first capturing group + paramInfo.setIsRequired(!param.endsWith("?")); + QName paramQName = OpenSearchTemplateUtils.getTemplateParameterName(param, urlElem); + paramInfo.setName(paramQName); + templateParamList.add(paramInfo); + if (paramQName.getNamespaceURI().equals(Namespaces.OSD11)) { + OpenSearchTemplateUtils.updateOpenSearchParameter(paramInfo, urlElem); + } + } + urlElem.setUserData(URL_TEMPLATE_PARAMS, Collections.unmodifiableList(templateParamList), null); + urlList.add(urlElem); + } + return urlList; + } - /** - * Returns a list of nodes representing queries defined in an OpenSearch - * description document. - * - * @param osDescr An OpenSearchDescription document. - * @param role The (qualified) name of a query role. - * @return A sequence of Element nodes (os:Query) that define specific - * search requests; the list may be empty if no matching queries are found. - */ - public static List getOpenSearchQueriesByRole(final Document osDescr, - QName role) { - List queryList = new ArrayList<>(); - NodeList nodes = osDescr.getElementsByTagNameNS(Namespaces.OSD11, "Query"); - for (int i = 0; i < nodes.getLength(); i++) { - Element query = (Element) nodes.item(i); - QName roleName = OpenSearchTemplateUtils.getTemplateParameterName( - query.getAttribute("role"), query); - if (roleName.equals(role)) { - queryList.add(query); - } - } - return queryList; - } + /** + * Returns a list of nodes representing queries defined in an OpenSearch description + * document. + * @param osDescr An OpenSearchDescription document. + * @param role The (qualified) name of a query role. + * @return A sequence of Element nodes (os:Query) that define specific search + * requests; the list may be empty if no matching queries are found. + */ + public static List getOpenSearchQueriesByRole(final Document osDescr, QName role) { + List queryList = new ArrayList<>(); + NodeList nodes = osDescr.getElementsByTagNameNS(Namespaces.OSD11, "Query"); + for (int i = 0; i < nodes.getLength(); i++) { + Element query = (Element) nodes.item(i); + QName roleName = OpenSearchTemplateUtils.getTemplateParameterName(query.getAttribute("role"), query); + if (roleName.equals(role)) { + queryList.add(query); + } + } + return queryList; + } - /** - * Searches a CSW capabilities document for the specified constraint and - * returns its set of allowed values. The default value is returned if - * present; otherwise the complete list of allowed values. - * - * @param cswMetadata A CSW capabilities document. - * @param name The name of the constraint (not case-sensitive). - * @return A set containing the allowed values of the constraint; it will be - * empty if no such constraint exists or it has no value. - */ - public static Set getConstraintValues(final Document cswMetadata, - String name) { - Set valueList = new HashSet<>(); - String xpath = String.format("//ows:Constraint[matches(@name,'%s','i')]", - name); - try { - XdmValue result = XMLUtils.evaluateXPath2( - new DOMSource(cswMetadata), xpath, - Collections.singletonMap(Namespaces.OWS, "ows")); - addDomainValues(valueList, result); - } catch (SaxonApiException ex) { - // expression is ok - } - return valueList; - } + /** + * Searches a CSW capabilities document for the specified constraint and returns its + * set of allowed values. The default value is returned if present; otherwise the + * complete list of allowed values. + * @param cswMetadata A CSW capabilities document. + * @param name The name of the constraint (not case-sensitive). + * @return A set containing the allowed values of the constraint; it will be empty if + * no such constraint exists or it has no value. + */ + public static Set getConstraintValues(final Document cswMetadata, String name) { + Set valueList = new HashSet<>(); + String xpath = String.format("//ows:Constraint[matches(@name,'%s','i')]", name); + try { + XdmValue result = XMLUtils.evaluateXPath2(new DOMSource(cswMetadata), xpath, + Collections.singletonMap(Namespaces.OWS, "ows")); + addDomainValues(valueList, result); + } + catch (SaxonApiException ex) { + // expression is ok + } + return valueList; + } - /** - * Searches a CSW capabilities document for the specified request parameter - * and returns its set of allowed values. A service-level parameter value - * may be overridden or supplemented by an operation-specific parameter - * value. - * - * @param cswMetadata A CSW capabilities document. - * @param reqName The name of a service request; if omitted, the search is - * restricted to service-level parameters. - * @param paramName The name of the request parameter (not case-sensitive). - * @return A set containing the allowed parameter values; it will be empty - * if no such parameter exists or it has no value. - */ - public static Set getParameterValues(final Document cswMetadata, - String reqName, String paramName) { - Set valueSet = new HashSet<>(); - String xpath = String.format( - "//ows:OperationsMetadata/ows:Parameter[matches(@name,'%s','i')]", - paramName); - Map nsBindings - = Collections.singletonMap(Namespaces.OWS, "ows"); - try { - XdmValue result = XMLUtils.evaluateXPath2( - new DOMSource(cswMetadata), xpath, nsBindings); - addDomainValues(valueSet, result); - if (null != reqName) { - xpath = String.format( - "//ows:Operation[@name='%s']/ows:Parameter[matches(@name,'%s','i')]", - reqName, paramName); - result = XMLUtils.evaluateXPath2( - new DOMSource(cswMetadata), xpath, nsBindings); - addDomainValues(valueSet, result); - } - } catch (SaxonApiException ex) { - // expressions are ok - } - return valueSet; - } + /** + * Searches a CSW capabilities document for the specified request parameter and + * returns its set of allowed values. A service-level parameter value may be + * overridden or supplemented by an operation-specific parameter value. + * @param cswMetadata A CSW capabilities document. + * @param reqName The name of a service request; if omitted, the search is restricted + * to service-level parameters. + * @param paramName The name of the request parameter (not case-sensitive). + * @return A set containing the allowed parameter values; it will be empty if no such + * parameter exists or it has no value. + */ + public static Set getParameterValues(final Document cswMetadata, String reqName, String paramName) { + Set valueSet = new HashSet<>(); + String xpath = String.format("//ows:OperationsMetadata/ows:Parameter[matches(@name,'%s','i')]", paramName); + Map nsBindings = Collections.singletonMap(Namespaces.OWS, "ows"); + try { + XdmValue result = XMLUtils.evaluateXPath2(new DOMSource(cswMetadata), xpath, nsBindings); + addDomainValues(valueSet, result); + if (null != reqName) { + xpath = String.format("//ows:Operation[@name='%s']/ows:Parameter[matches(@name,'%s','i')]", reqName, + paramName); + result = XMLUtils.evaluateXPath2(new DOMSource(cswMetadata), xpath, nsBindings); + addDomainValues(valueSet, result); + } + } + catch (SaxonApiException ex) { + // expressions are ok + } + return valueSet; + } - /** - * Adds the allowed values of operation metadata elements to the given set. - * - * @param valueSet The set to which the values are added. - * @param opMetadata A sequence of operation metadata elements - * (ows:Parameter or ows:Constraint, of type ows:DomainType). - */ - static void addDomainValues(Set valueSet, XdmValue opMetadata) { - if (null == opMetadata) { - return; - } - if (null == valueSet) { - valueSet = new HashSet<>(); - } - QName defaultValue = new QName(Namespaces.OWS, "DefaultValue"); - QName value = new QName(Namespaces.OWS, "Value"); - for (XdmItem item : opMetadata) { - XdmNode node = (XdmNode) item; - XdmSequenceIterator nodes = node.axisIterator(Axis.DESCENDANT, - new net.sf.saxon.s9api.QName(defaultValue)); - if (!nodes.hasNext()) { - nodes = node.axisIterator(Axis.DESCENDANT, - new net.sf.saxon.s9api.QName(value)); - } - while (nodes.hasNext()) { - valueSet.add(nodes.next().getStringValue().trim()); - } - } - } + /** + * Adds the allowed values of operation metadata elements to the given set. + * @param valueSet The set to which the values are added. + * @param opMetadata A sequence of operation metadata elements (ows:Parameter or + * ows:Constraint, of type ows:DomainType). + */ + static void addDomainValues(Set valueSet, XdmValue opMetadata) { + if (null == opMetadata) { + return; + } + if (null == valueSet) { + valueSet = new HashSet<>(); + } + QName defaultValue = new QName(Namespaces.OWS, "DefaultValue"); + QName value = new QName(Namespaces.OWS, "Value"); + for (XdmItem item : opMetadata) { + XdmNode node = (XdmNode) item; + XdmSequenceIterator nodes = node.axisIterator(Axis.DESCENDANT, new net.sf.saxon.s9api.QName(defaultValue)); + if (!nodes.hasNext()) { + nodes = node.axisIterator(Axis.DESCENDANT, new net.sf.saxon.s9api.QName(value)); + } + while (nodes.hasNext()) { + valueSet.add(nodes.next().getStringValue().trim()); + } + } + } } diff --git a/src/main/java/org/opengis/cite/cat30/util/SpatialUtils.java b/src/main/java/org/opengis/cite/cat30/util/SpatialUtils.java index 0ece320..0c687f5 100644 --- a/src/main/java/org/opengis/cite/cat30/util/SpatialUtils.java +++ b/src/main/java/org/opengis/cite/cat30/util/SpatialUtils.java @@ -20,66 +20,64 @@ */ public class SpatialUtils { - /** - * Creates an Envelope from a simple georss:box element. The coordinate - * reference system is EPSG 4326 (lat,lon axis order). - * - * @param boxNode An Element node (georss:box) containing the coordinates of - * the lower and upper corners. - * @return An Envelope object representing the given spatial extent, or null - * if it cannot be constructed (a runtime exception may have occurred). - * - * @see GeoRSS - * Simple - */ - public static Envelope envelopeFromSimpleGeoRSSBox(Node boxNode) { - if (!boxNode.getNamespaceURI().equals(Namespaces.GEORSS)) { - throw new IllegalArgumentException("Not a GeoRSS element."); - } - CoordinateReferenceSystem crs = CommonCRS.WGS84.geographic(); - GeneralEnvelope env = new GeneralEnvelope(crs); - String[] coords = boxNode.getTextContent().trim().split("\\s+"); - if (coords.length != 4) { - throw new IllegalArgumentException( - "Expected two coordinate tuples (lower and upper corners)."); - } - double[] coordArray = new double[coords.length]; - for (int i = 0; i < coords.length; i++) { - coordArray[i] = Double.parseDouble(coords[i]); - } - env.setEnvelope(coordArray); - return env; - } + /** + * Creates an Envelope from a simple georss:box element. The coordinate reference + * system is EPSG 4326 (lat,lon axis order). + * @param boxNode An Element node (georss:box) containing the coordinates of the lower + * and upper corners. + * @return An Envelope object representing the given spatial extent, or null if it + * cannot be constructed (a runtime exception may have occurred). + * + * @see GeoRSS Simple + */ + public static Envelope envelopeFromSimpleGeoRSSBox(Node boxNode) { + if (!boxNode.getNamespaceURI().equals(Namespaces.GEORSS)) { + throw new IllegalArgumentException("Not a GeoRSS element."); + } + CoordinateReferenceSystem crs = CommonCRS.WGS84.geographic(); + GeneralEnvelope env = new GeneralEnvelope(crs); + String[] coords = boxNode.getTextContent().trim().split("\\s+"); + if (coords.length != 4) { + throw new IllegalArgumentException("Expected two coordinate tuples (lower and upper corners)."); + } + double[] coordArray = new double[coords.length]; + for (int i = 0; i < coords.length; i++) { + coordArray[i] = Double.parseDouble(coords[i]); + } + env.setEnvelope(coordArray); + return env; + } + + /** + * Creates a GML 3.2 envelope from a legacy GML 3.1 representation. + * @param oldEnvNode An element node representing a GML 3.1 envelope. + * @return A new gml:Envelope element, or null if the source node is not a GML 3.1 + * envelope. + */ + public static Element createGML32Envelope(Node oldEnvNode) { + if (!oldEnvNode.getNamespaceURI().equals(Namespaces.GML31)) { + return null; + } + Element oldEnvelope = (Element) oldEnvNode; + DocumentBuilder builder = null; + try { + builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + } + catch (ParserConfigurationException ex) { + Logger.getLogger(SpatialUtils.class.getName()).log(Level.WARNING, null, ex); + } + Document doc = builder.newDocument(); + Element envelope = doc.createElementNS(Namespaces.GML, "Envelope"); + envelope.setAttribute("srsName", oldEnvelope.getAttribute("srsName")); + Element lowerCorner = doc.createElementNS(Namespaces.GML, "lowerCorner"); + lowerCorner.setTextContent( + oldEnvelope.getElementsByTagNameNS(Namespaces.GML31, "lowerCorner").item(0).getTextContent()); + envelope.appendChild(lowerCorner); + Element upperCorner = doc.createElementNS(Namespaces.GML, "upperCorner"); + upperCorner.setTextContent( + oldEnvelope.getElementsByTagNameNS(Namespaces.GML31, "upperCorner").item(0).getTextContent()); + envelope.appendChild(upperCorner); + return envelope; + } - /** - * Creates a GML 3.2 envelope from a legacy GML 3.1 representation. - * - * @param oldEnvNode An element node representing a GML 3.1 envelope. - * @return A new gml:Envelope element, or null if the source node is not a - * GML 3.1 envelope. - */ - public static Element createGML32Envelope(Node oldEnvNode) { - if (!oldEnvNode.getNamespaceURI().equals(Namespaces.GML31)) { - return null; - } - Element oldEnvelope = (Element) oldEnvNode; - DocumentBuilder builder = null; - try { - builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); - } catch (ParserConfigurationException ex) { - Logger.getLogger(SpatialUtils.class.getName()).log(Level.WARNING, null, ex); - } - Document doc = builder.newDocument(); - Element envelope = doc.createElementNS(Namespaces.GML, "Envelope"); - envelope.setAttribute("srsName", oldEnvelope.getAttribute("srsName")); - Element lowerCorner = doc.createElementNS(Namespaces.GML, "lowerCorner"); - lowerCorner.setTextContent(oldEnvelope.getElementsByTagNameNS( - Namespaces.GML31, "lowerCorner").item(0).getTextContent()); - envelope.appendChild(lowerCorner); - Element upperCorner = doc.createElementNS(Namespaces.GML, "upperCorner"); - upperCorner.setTextContent(oldEnvelope.getElementsByTagNameNS( - Namespaces.GML31, "upperCorner").item(0).getTextContent()); - envelope.appendChild(upperCorner); - return envelope; - } } diff --git a/src/main/java/org/opengis/cite/cat30/util/TestSuiteLogger.java b/src/main/java/org/opengis/cite/cat30/util/TestSuiteLogger.java index d8237a5..f5ee097 100644 --- a/src/main/java/org/opengis/cite/cat30/util/TestSuiteLogger.java +++ b/src/main/java/org/opengis/cite/cat30/util/TestSuiteLogger.java @@ -4,70 +4,65 @@ import java.util.logging.Logger; /** - * Logging utility class that provides simple access to the JDK Logging API. Set - * the "java.util.logging.config.file" system property to specify the location - * of the desired logging configuration file. A sample configuration file is - * available at {@code src/main/config/logging.properties}. + * Logging utility class that provides simple access to the JDK Logging API. Set the + * "java.util.logging.config.file" system property to specify the location of the desired + * logging configuration file. A sample configuration file is available at + * {@code src/main/config/logging.properties}. * * @see java.util.logging.LogManager LogManager */ public class TestSuiteLogger { - private static final Logger LOGR = - Logger.getLogger(TestSuiteLogger.class.getPackage().getName()); + private static final Logger LOGR = Logger.getLogger(TestSuiteLogger.class.getPackage().getName()); - /** - * Logs a message at the specified logging level with the given message - * parameters. - * - * @param level The logging {@link Level level}. - * @param message A String representing the content of the log message. - * @param params An array of message parameters. - */ - public static void log(Level level, String message, Object[] params) { - if (LOGR.isLoggable(level)) { - LOGR.log(level, message, params); - } - } + /** + * Logs a message at the specified logging level with the given message parameters. + * @param level The logging {@link Level level}. + * @param message A String representing the content of the log message. + * @param params An array of message parameters. + */ + public static void log(Level level, String message, Object[] params) { + if (LOGR.isLoggable(level)) { + LOGR.log(level, message, params); + } + } - /** - * Logs a message at the specified logging level with the given Exception - * object that represents a noteworthy error condition. - * - * @param level The logging {@link Level level}. - * @param message A String representing the content of the log message. - * @param except An object that indicates an exceptional situation. - */ - public static void log(Level level, String message, Exception except) { - if (LOGR.isLoggable(level)) { - LOGR.log(level, message, except); - } - } + /** + * Logs a message at the specified logging level with the given Exception object that + * represents a noteworthy error condition. + * @param level The logging {@link Level level}. + * @param message A String representing the content of the log message. + * @param except An object that indicates an exceptional situation. + */ + public static void log(Level level, String message, Exception except) { + if (LOGR.isLoggable(level)) { + LOGR.log(level, message, except); + } + } - /** - * Logs a simple message at the specified logging level. - * - * @param level The logging {@link Level level}. - * @param message A String representing the content of the log message. - */ - public static void log(Level level, String message) { - if (LOGR.isLoggable(level)) { - LOGR.log(level, message); - } - } + /** + * Logs a simple message at the specified logging level. + * @param level The logging {@link Level level}. + * @param message A String representing the content of the log message. + */ + public static void log(Level level, String message) { + if (LOGR.isLoggable(level)) { + LOGR.log(level, message); + } + } - /** - * Indicates if the logger is enabled at a given logging level. Message - * levels lower than this value will be discarded. - * - * @param level The logging {@link Level level}. - * @return true if the logger is currently enabled for this logging level; - * false otherwise. - */ - public static boolean isLoggable(Level level) { - return LOGR.isLoggable(level); - } + /** + * Indicates if the logger is enabled at a given logging level. Message levels lower + * than this value will be discarded. + * @param level The logging {@link Level level}. + * @return true if the logger is currently enabled for this logging level; false + * otherwise. + */ + public static boolean isLoggable(Level level) { + return LOGR.isLoggable(level); + } + + private TestSuiteLogger() { + } - private TestSuiteLogger() { - } } diff --git a/src/main/java/org/opengis/cite/cat30/util/URIUtils.java b/src/main/java/org/opengis/cite/cat30/util/URIUtils.java index 215b115..9dd8a41 100644 --- a/src/main/java/org/opengis/cite/cat30/util/URIUtils.java +++ b/src/main/java/org/opengis/cite/cat30/util/URIUtils.java @@ -24,145 +24,134 @@ import jakarta.ws.rs.core.Response; /** - * Provides a collection of utility methods for manipulating or resolving URI - * references. + * Provides a collection of utility methods for manipulating or resolving URI references. */ public class URIUtils { - private static final String FIXUP_BASE_URI = "http://apache.org/xml/features/xinclude/fixup-base-uris"; + private static final String FIXUP_BASE_URI = "http://apache.org/xml/features/xinclude/fixup-base-uris"; - /** - * Parses the content of the given URI as an XML document and returns a new - * DOM Document object. Entity reference nodes will not be expanded. XML - * inclusions (xi:include elements) will be processed if present. - * - * @param uriRef An absolute URI specifying the location of an XML resource. - * @return A DOM Document node representing an XML resource. - * @throws SAXException If the resource cannot be parsed. - * @throws IOException If the resource is not accessible. - */ - public static Document parseURI(URI uriRef) throws SAXException, - IOException { - if ((null == uriRef) || !uriRef.isAbsolute()) { - throw new IllegalArgumentException( - "Absolute URI is required, but received " + uriRef); - } - DocumentBuilderFactory docFactory = DocumentBuilderFactory - .newInstance(); - docFactory.setNamespaceAware(true); - docFactory.setExpandEntityReferences(false); - docFactory.setXIncludeAware(true); - Document doc = null; - try { - // XInclude processor will not add xml:base attributes - docFactory.setFeature(FIXUP_BASE_URI, false); - DocumentBuilder docBuilder = docFactory.newDocumentBuilder(); - doc = docBuilder.parse(uriRef.toString()); - } catch (ParserConfigurationException x) { - TestSuiteLogger.log(Level.WARNING, - "Failed to create DocumentBuilder." + x); - } - if (null != doc) { - doc.setDocumentURI(uriRef.toString()); - } - return doc; - } + /** + * Parses the content of the given URI as an XML document and returns a new DOM + * Document object. Entity reference nodes will not be expanded. XML inclusions + * (xi:include elements) will be processed if present. + * @param uriRef An absolute URI specifying the location of an XML resource. + * @return A DOM Document node representing an XML resource. + * @throws SAXException If the resource cannot be parsed. + * @throws IOException If the resource is not accessible. + */ + public static Document parseURI(URI uriRef) throws SAXException, IOException { + if ((null == uriRef) || !uriRef.isAbsolute()) { + throw new IllegalArgumentException("Absolute URI is required, but received " + uriRef); + } + DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); + docFactory.setNamespaceAware(true); + docFactory.setExpandEntityReferences(false); + docFactory.setXIncludeAware(true); + Document doc = null; + try { + // XInclude processor will not add xml:base attributes + docFactory.setFeature(FIXUP_BASE_URI, false); + DocumentBuilder docBuilder = docFactory.newDocumentBuilder(); + doc = docBuilder.parse(uriRef.toString()); + } + catch (ParserConfigurationException x) { + TestSuiteLogger.log(Level.WARNING, "Failed to create DocumentBuilder." + x); + } + if (null != doc) { + doc.setDocumentURI(uriRef.toString()); + } + return doc; + } - /** - * Dereferences the given URI and stores the resulting resource - * representation in a local file. The file will be located in the default - * temporary file directory. - * - * @param uriRef An absolute URI specifying the location of some resource. - * @return A File containing the content of the resource; it may be empty if - * resolution failed for any reason. - * @throws IOException If an IO error occurred. - */ - public static File dereferenceURI(URI uriRef) throws IOException { - if ((null == uriRef) || !uriRef.isAbsolute()) { - throw new IllegalArgumentException( - "Absolute URI is required, but received " + uriRef); - } - if (uriRef.getScheme().equalsIgnoreCase("file")) { - return new File(uriRef); - } - Client client = ClientUtils.buildClient(); - WebTarget target = client.target(uriRef); - Builder builder = target.request(); - Response rsp = builder.buildGet().invoke(); - String suffix = null; - if (rsp.getHeaders().getFirst(HttpHeaders.CONTENT_TYPE).toString().endsWith("xml")) { - suffix = ".xml"; - } - File destFile = File.createTempFile("entity-", suffix); - if (rsp.hasEntity()) { - Object entity = rsp.getEntity(); - if(!(entity instanceof InputStream)) { - return null; - } - InputStream is = (InputStream)entity; - OutputStream os = new FileOutputStream(destFile); - byte[] buffer = new byte[8 * 1024]; - int bytesRead; - while ((bytesRead = is.read(buffer)) != -1) { - os.write(buffer, 0, bytesRead); - } - is.close(); - os.flush(); - os.close(); - } - TestSuiteLogger.log(Level.FINE, "Wrote " + destFile.length() - + " bytes to file at " + destFile.getAbsolutePath()); - return destFile; - } + /** + * Dereferences the given URI and stores the resulting resource representation in a + * local file. The file will be located in the default temporary file directory. + * @param uriRef An absolute URI specifying the location of some resource. + * @return A File containing the content of the resource; it may be empty if + * resolution failed for any reason. + * @throws IOException If an IO error occurred. + */ + public static File dereferenceURI(URI uriRef) throws IOException { + if ((null == uriRef) || !uriRef.isAbsolute()) { + throw new IllegalArgumentException("Absolute URI is required, but received " + uriRef); + } + if (uriRef.getScheme().equalsIgnoreCase("file")) { + return new File(uriRef); + } + Client client = ClientUtils.buildClient(); + WebTarget target = client.target(uriRef); + Builder builder = target.request(); + Response rsp = builder.buildGet().invoke(); + String suffix = null; + if (rsp.getHeaders().getFirst(HttpHeaders.CONTENT_TYPE).toString().endsWith("xml")) { + suffix = ".xml"; + } + File destFile = File.createTempFile("entity-", suffix); + if (rsp.hasEntity()) { + Object entity = rsp.getEntity(); + if (!(entity instanceof InputStream)) { + return null; + } + InputStream is = (InputStream) entity; + OutputStream os = new FileOutputStream(destFile); + byte[] buffer = new byte[8 * 1024]; + int bytesRead; + while ((bytesRead = is.read(buffer)) != -1) { + os.write(buffer, 0, bytesRead); + } + is.close(); + os.flush(); + os.close(); + } + TestSuiteLogger.log(Level.FINE, + "Wrote " + destFile.length() + " bytes to file at " + destFile.getAbsolutePath()); + return destFile; + } - /** - * Constructs an absolute URI from the given URI reference and a base URI. - * - * @see RFC 3986, - * 5.2 - * - * @param baseURI The base URI; if present, it must be an absolute URI. - * @param uriRef A URI reference that may be relative to the given base URI. - * @return The resulting URI. - * - */ - public static URI resolveRelativeURI(String baseURI, String uriRef) { - URI uri = (null != baseURI) ? URI.create(baseURI) : URI.create(""); - if (null != baseURI && null == uri.getScheme()) { - throw new IllegalArgumentException( - "Base URI has no scheme component: " + baseURI); - } - return uri.resolve(uriRef); - } + /** + * Constructs an absolute URI from the given URI reference and a base URI. + * + * @see RFC 3986, 5.2 + * @param baseURI The base URI; if present, it must be an absolute URI. + * @param uriRef A URI reference that may be relative to the given base URI. + * @return The resulting URI. + * + */ + public static URI resolveRelativeURI(String baseURI, String uriRef) { + URI uri = (null != baseURI) ? URI.create(baseURI) : URI.create(""); + if (null != baseURI && null == uri.getScheme()) { + throw new IllegalArgumentException("Base URI has no scheme component: " + baseURI); + } + return uri.resolve(uriRef); + } + + /** + * Replaces characters not allowed in the query component of a URI with their + * equivalent percent-encoded values (UTF-8). While this may encode more "unsafe" + * characters than is strictly necessary according to RFC 3986 (see ABNF fragment + * below), it should not affect query processing. + * + *
        +	 * query = *( pchar / "/" / "?" )
        +	 * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
        +	 * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
        +	 * 
        + * @param str The sequence of characters to be encoded. + * @return A percent-encoded string. + * + * @see RFC + * 3986, 2.1 + */ + public static String getPercentEncodedString(String str) { + String encoded = null; + try { + // URLEncoder is intended for HTML form encoding + encoded = URLEncoder.encode(str, "UTF-8").replaceAll("\\+", "%20"); + } + catch (UnsupportedEncodingException ex) { + // UTF-8 is supported + } + return encoded; + } - /** - * Replaces characters not allowed in the query component of a URI with - * their equivalent percent-encoded values (UTF-8). While this may encode - * more "unsafe" characters than is strictly necessary according to RFC 3986 - * (see ABNF fragment below), it should not affect query processing. - * - *
        -     * query = *( pchar / "/" / "?" )
        -     * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
        -     * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
        -     * 
        - * - * @param str The sequence of characters to be encoded. - * @return A percent-encoded string. - * - * @see - * RFC - * 3986, 2.1 - */ - public static String getPercentEncodedString(String str) { - String encoded = null; - try { - // URLEncoder is intended for HTML form encoding - encoded = URLEncoder.encode(str, "UTF-8").replaceAll("\\+", "%20"); - } catch (UnsupportedEncodingException ex) { - // UTF-8 is supported - } - return encoded; - } } diff --git a/src/main/java/org/opengis/cite/cat30/util/ValidationUtils.java b/src/main/java/org/opengis/cite/cat30/util/ValidationUtils.java index e22b5c9..97d4298 100644 --- a/src/main/java/org/opengis/cite/cat30/util/ValidationUtils.java +++ b/src/main/java/org/opengis/cite/cat30/util/ValidationUtils.java @@ -31,214 +31,189 @@ import org.xml.sax.SAXException; /** - * A utility class that provides convenience methods to support schema - * validation. + * A utility class that provides convenience methods to support schema validation. */ public class ValidationUtils { - static final String ROOT_PKG = "/org/opengis/cite/cat30/"; - static final String FACTORY_RELAXNG_COMPACT - = "com.thaiopensource.relaxng.jaxp.CompactSyntaxSchemaFactory"; - private static final XMLCatalogResolver SCH_RESOLVER = initCatalogResolver(); + static final String ROOT_PKG = "/org/opengis/cite/cat30/"; + static final String FACTORY_RELAXNG_COMPACT = "com.thaiopensource.relaxng.jaxp.CompactSyntaxSchemaFactory"; - private static XMLCatalogResolver initCatalogResolver() { - return (XMLCatalogResolver) createSchemaResolver(Namespaces.SCH); - } + private static final XMLCatalogResolver SCH_RESOLVER = initCatalogResolver(); - /** - * Creates a resource resolver suitable for locating schemas using an entity - * catalog. In effect, local copies of standard schemas are returned instead - * of retrieving them from external repositories. - * - * @param schemaLanguage A URI that identifies a schema language by - * namespace name. - * @return A {@code LSResourceResolver} object that is configured to use an - * OASIS entity catalog. - */ - public static LSResourceResolver createSchemaResolver(URI schemaLanguage) { - String catalogFileName; - if (schemaLanguage.equals(Namespaces.XSD)) { - catalogFileName = "schema-catalog.xml"; - } else { - catalogFileName = "schematron-catalog.xml"; - } - URL catalogURL = ValidationUtils.class.getResource(ROOT_PKG - + catalogFileName); - XMLCatalogResolver resolver = new XMLCatalogResolver(); - resolver.setCatalogList(new String[]{catalogURL.toString()}); - return resolver; - } + private static XMLCatalogResolver initCatalogResolver() { + return (XMLCatalogResolver) createSchemaResolver(Namespaces.SCH); + } - /** - * Constructs a SchematronValidator that will check an XML resource against - * the rules defined in a Schematron schema. An attempt is made to resolve - * the schema reference using an entity catalog; if this fails the reference - * is used as given. - * - * @param schemaRef A reference to a Schematron schema; this is expected to - * be a relative or absolute URI value, possibly matching the system - * identifier for some entry in an entity catalog. - * @param phase The name of the phase to invoke. - * @return A SchematronValidator instance, or {@code null} if the validator - * cannot be constructed (e.g. invalid schema reference or phase name). - */ - public static SchematronValidator buildSchematronValidator( - String schemaRef, String phase) { - Source source = null; - try { - String catalogRef = SCH_RESOLVER - .resolveSystem(schemaRef); - if (null != catalogRef) { - source = new StreamSource(URI.create(catalogRef).toString()); - } else { - source = new StreamSource(schemaRef); - } - } catch (IOException x) { - TestSuiteLogger.log(Level.WARNING, - "Error reading Schematron schema catalog.", x); - } - SchematronValidator validator = null; - try { - validator = new SchematronValidator(source, phase); - } catch (Exception e) { - TestSuiteLogger.log(Level.WARNING, - "Error creating Schematron validator.", e); - } - return validator; - } + /** + * Creates a resource resolver suitable for locating schemas using an entity catalog. + * In effect, local copies of standard schemas are returned instead of retrieving them + * from external repositories. + * @param schemaLanguage A URI that identifies a schema language by namespace name. + * @return A {@code LSResourceResolver} object that is configured to use an OASIS + * entity catalog. + */ + public static LSResourceResolver createSchemaResolver(URI schemaLanguage) { + String catalogFileName; + if (schemaLanguage.equals(Namespaces.XSD)) { + catalogFileName = "schema-catalog.xml"; + } + else { + catalogFileName = "schematron-catalog.xml"; + } + URL catalogURL = ValidationUtils.class.getResource(ROOT_PKG + catalogFileName); + XMLCatalogResolver resolver = new XMLCatalogResolver(); + resolver.setCatalogList(new String[] { catalogURL.toString() }); + return resolver; + } - /** - * Extracts a set of XML Schema references from a source XML document. The - * document element is expected to include the standard xsi:schemaLocation - * attribute. - * - * @param source The source instance to read from; its base URI (systemId) - * should be set. - * @param baseURI An alternative base URI to use if the source does not have - * a system identifier set or if its system id is a {@code file} URI. This - * will usually be the URI used to retrieve the resource; it may be null. - * @return A Set containing absolute URI references that specify the - * locations of XML Schema resources. - * @throws XMLStreamException If an error occurs while reading the source - * instance. - */ - public static Set extractSchemaReferences(Source source, String baseURI) - throws XMLStreamException { - XMLInputFactory factory = XMLInputFactory.newInstance(); - XMLEventReader reader = factory.createXMLEventReader(source); - // advance to document element - StartElement docElem = reader.nextTag().asStartElement(); - Attribute schemaLoc = docElem.getAttributeByName(new QName( - XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "schemaLocation")); - if (null == schemaLoc) { - throw new RuntimeException( - "No xsi:schemaLocation attribute found. See ISO 19136, A.3.1."); - } - String[] uriValues = schemaLoc.getValue().split("\\s+"); - if (uriValues.length % 2 != 0) { - throw new RuntimeException( - "xsi:schemaLocation attribute contains an odd number of URI values:\n" - + Arrays.toString(uriValues)); - } - Set schemaURIs = new HashSet(); - // one or more pairs of [namespace name] [schema location] - for (int i = 0; i < uriValues.length; i += 2) { - URI schemaURI = null; - if (!URI.create(uriValues[i + 1]).isAbsolute() - && (null != source.getSystemId())) { - String schemaRef = URIUtils.resolveRelativeURI( - source.getSystemId(), uriValues[i + 1]).toString(); - if (schemaRef.startsWith("file") - && !new File(schemaRef).exists() && (null != baseURI)) { - schemaRef = URIUtils.resolveRelativeURI(baseURI, - uriValues[i + 1]).toString(); - } - schemaURI = URI.create(schemaRef); - } else { - schemaURI = URI.create(uriValues[i + 1]); - } - schemaURIs.add(schemaURI); - } - return schemaURIs; - } + /** + * Constructs a SchematronValidator that will check an XML resource against the rules + * defined in a Schematron schema. An attempt is made to resolve the schema reference + * using an entity catalog; if this fails the reference is used as given. + * @param schemaRef A reference to a Schematron schema; this is expected to be a + * relative or absolute URI value, possibly matching the system identifier for some + * entry in an entity catalog. + * @param phase The name of the phase to invoke. + * @return A SchematronValidator instance, or {@code null} if the validator cannot be + * constructed (e.g. invalid schema reference or phase name). + */ + public static SchematronValidator buildSchematronValidator(String schemaRef, String phase) { + Source source = null; + try { + String catalogRef = SCH_RESOLVER.resolveSystem(schemaRef); + if (null != catalogRef) { + source = new StreamSource(URI.create(catalogRef).toString()); + } + else { + source = new StreamSource(schemaRef); + } + } + catch (IOException x) { + TestSuiteLogger.log(Level.WARNING, "Error reading Schematron schema catalog.", x); + } + SchematronValidator validator = null; + try { + validator = new SchematronValidator(source, phase); + } + catch (Exception e) { + TestSuiteLogger.log(Level.WARNING, "Error creating Schematron validator.", e); + } + return validator; + } - /** - * Creates a Schema object representing the complete set of constraints - * defined in the CSW 3.0 schema. It incorporates schema components from all - * relevant namespaces. - * - * @return An immutable Schema object, or null if it cannot be - * constructed. - */ - public static Schema createCSWSchema() { - URL entityCatalog = ValidationUtils.class.getResource(ROOT_PKG - + "schema-catalog.xml"); - XmlSchemaCompiler xsdCompiler = new XmlSchemaCompiler(entityCatalog); - Schema appSchema = null; - try { - URL schemaRef = ValidationUtils.class.getResource(ROOT_PKG - + "xsd/opengis/cat/csw/3.0/csw-3.0.xsd"); - Source xsdSource = new StreamSource(schemaRef.toString()); - appSchema = xsdCompiler - .compileXmlSchema(new Source[]{xsdSource}); - } catch (SAXException e) { - TestSuiteLogger.log(Level.WARNING, - "Failed to create CSW Schema object.", e); - } - return appSchema; - } + /** + * Extracts a set of XML Schema references from a source XML document. The document + * element is expected to include the standard xsi:schemaLocation attribute. + * @param source The source instance to read from; its base URI (systemId) should be + * set. + * @param baseURI An alternative base URI to use if the source does not have a system + * identifier set or if its system id is a {@code file} URI. This will usually be the + * URI used to retrieve the resource; it may be null. + * @return A Set containing absolute URI references that specify the locations of XML + * Schema resources. + * @throws XMLStreamException If an error occurs while reading the source instance. + */ + public static Set extractSchemaReferences(Source source, String baseURI) throws XMLStreamException { + XMLInputFactory factory = XMLInputFactory.newInstance(); + XMLEventReader reader = factory.createXMLEventReader(source); + // advance to document element + StartElement docElem = reader.nextTag().asStartElement(); + Attribute schemaLoc = docElem + .getAttributeByName(new QName(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "schemaLocation")); + if (null == schemaLoc) { + throw new RuntimeException("No xsi:schemaLocation attribute found. See ISO 19136, A.3.1."); + } + String[] uriValues = schemaLoc.getValue().split("\\s+"); + if (uriValues.length % 2 != 0) { + throw new RuntimeException("xsi:schemaLocation attribute contains an odd number of URI values:\n" + + Arrays.toString(uriValues)); + } + Set schemaURIs = new HashSet(); + // one or more pairs of [namespace name] [schema location] + for (int i = 0; i < uriValues.length; i += 2) { + URI schemaURI = null; + if (!URI.create(uriValues[i + 1]).isAbsolute() && (null != source.getSystemId())) { + String schemaRef = URIUtils.resolveRelativeURI(source.getSystemId(), uriValues[i + 1]).toString(); + if (schemaRef.startsWith("file") && !new File(schemaRef).exists() && (null != baseURI)) { + schemaRef = URIUtils.resolveRelativeURI(baseURI, uriValues[i + 1]).toString(); + } + schemaURI = URI.create(schemaRef); + } + else { + schemaURI = URI.create(uriValues[i + 1]); + } + schemaURIs.add(schemaURI); + } + return schemaURIs; + } - /** - * Creates a Schema object representing the constraints defined in RFC 4287 - * ("The Atom Syndication Format"). Appendix B provides an informative RELAX - * NG grammar (compact syntax); it can be used to validate either a feed or - * a stand-alone entry element. - * - * @return An immutable Schema object, or null if it cannot be - * constructed. - * - * @see - * - * RFC 4287, Appendix B - */ - public static Schema createAtomSchema() { - SchemaFactory factory = SchemaFactory.newInstance( - Constants.RELAXNG_COMPACT_URI, FACTORY_RELAXNG_COMPACT, null); - URL schemaRef = ValidationUtils.class.getResource(ROOT_PKG - + "rnc/atom.rnc"); - Schema schema = null; - try { - schema = factory.newSchema(schemaRef); - } catch (SAXException e) { - TestSuiteLogger.log(Level.WARNING, - "Failed to create Atom Schema object from RELAX NG (compact) grammar", e); - } - return schema; - } + /** + * Creates a Schema object representing the complete set of constraints defined in the + * CSW 3.0 schema. It incorporates schema components from all relevant namespaces. + * @return An immutable Schema object, or null if it cannot be + * constructed. + */ + public static Schema createCSWSchema() { + URL entityCatalog = ValidationUtils.class.getResource(ROOT_PKG + "schema-catalog.xml"); + XmlSchemaCompiler xsdCompiler = new XmlSchemaCompiler(entityCatalog); + Schema appSchema = null; + try { + URL schemaRef = ValidationUtils.class.getResource(ROOT_PKG + "xsd/opengis/cat/csw/3.0/csw-3.0.xsd"); + Source xsdSource = new StreamSource(schemaRef.toString()); + appSchema = xsdCompiler.compileXmlSchema(new Source[] { xsdSource }); + } + catch (SAXException e) { + TestSuiteLogger.log(Level.WARNING, "Failed to create CSW Schema object.", e); + } + return appSchema; + } + + /** + * Creates a Schema object representing the constraints defined in RFC 4287 ("The Atom + * Syndication Format"). Appendix B provides an informative RELAX NG grammar (compact + * syntax); it can be used to validate either a feed or a stand-alone entry element. + * @return An immutable Schema object, or null if it cannot be + * constructed. + * + * @see RFC + * 4287, Appendix B + */ + public static Schema createAtomSchema() { + SchemaFactory factory = SchemaFactory.newInstance(Constants.RELAXNG_COMPACT_URI, FACTORY_RELAXNG_COMPACT, null); + URL schemaRef = ValidationUtils.class.getResource(ROOT_PKG + "rnc/atom.rnc"); + Schema schema = null; + try { + schema = factory.newSchema(schemaRef); + } + catch (SAXException e) { + TestSuiteLogger.log(Level.WARNING, "Failed to create Atom Schema object from RELAX NG (compact) grammar", + e); + } + return schema; + } + + /** + * Creates a Schema object representing the constraints defined for an OpenSearch + * description document (1.1 Draft 5). + * @return An immutable Schema object, or null if it cannot be + * constructed. + * + * @see OpenSearch 1.1 + * Draft 5 + */ + public static Schema createOpenSearchSchema() { + SchemaFactory factory = SchemaFactory.newInstance(Constants.RELAXNG_COMPACT_URI, FACTORY_RELAXNG_COMPACT, null); + URL schemaRef = ValidationUtils.class.getResource(ROOT_PKG + "rnc/osd-1.1-draft5.rnc"); + Schema schema = null; + try { + schema = factory.newSchema(schemaRef); + } + catch (SAXException e) { + TestSuiteLogger.log(Level.WARNING, + "Failed to create OpenSearch Schema object from RELAX NG (compact) grammar", e); + } + return schema; + } - /** - * Creates a Schema object representing the constraints defined for an - * OpenSearch description document (1.1 Draft 5). - * - * @return An immutable Schema object, or null if it cannot be - * constructed. - * - * @see - * - * OpenSearch 1.1 Draft 5 - */ - public static Schema createOpenSearchSchema() { - SchemaFactory factory = SchemaFactory.newInstance( - Constants.RELAXNG_COMPACT_URI, FACTORY_RELAXNG_COMPACT, null); - URL schemaRef = ValidationUtils.class.getResource(ROOT_PKG - + "rnc/osd-1.1-draft5.rnc"); - Schema schema = null; - try { - schema = factory.newSchema(schemaRef); - } catch (SAXException e) { - TestSuiteLogger.log(Level.WARNING, - "Failed to create OpenSearch Schema object from RELAX NG (compact) grammar", e); - } - return schema; - } } diff --git a/src/main/java/org/opengis/cite/cat30/util/XMLUtils.java b/src/main/java/org/opengis/cite/cat30/util/XMLUtils.java index ad73285..9e46c5c 100644 --- a/src/main/java/org/opengis/cite/cat30/util/XMLUtils.java +++ b/src/main/java/org/opengis/cite/cat30/util/XMLUtils.java @@ -59,469 +59,435 @@ import net.sf.saxon.s9api.XsltTransformer; /** - * Provides various utility methods for accessing or manipulating XML - * representations. + * Provides various utility methods for accessing or manipulating XML representations. */ public class XMLUtils { - private static final Logger LOGR = Logger.getLogger(XMLUtils.class - .getPackage().getName()); - private static final XMLInputFactory STAX_FACTORY = initXMLInputFactory(); - private static final XPathFactory XPATH_FACTORY = initXPathFactory(); + private static final Logger LOGR = Logger.getLogger(XMLUtils.class.getPackage().getName()); - private static XPathFactory initXPathFactory() { - XPathFactory factory = XPathFactory.newInstance(); - return factory; - } + private static final XMLInputFactory STAX_FACTORY = initXMLInputFactory(); - private static XMLInputFactory initXMLInputFactory() { - XMLInputFactory factory = XMLInputFactory.newInstance(); - factory.setProperty(XMLInputFactory.IS_COALESCING, Boolean.TRUE); - return factory; - } + private static final XPathFactory XPATH_FACTORY = initXPathFactory(); - /** - * Writes the content of a DOM Node to a string. The XML declaration is - * omitted and the character encoding is set to "US-ASCII" (any character - * outside of this set is serialized as a numeric character reference). - * - * @param node The DOM Node to be serialized. - * @return A String representing the content of the given node. - */ - public static String writeNodeToString(Node node) { - if (null == node) { - return ""; - } - Writer writer = null; - try { - Transformer idTransformer = TransformerFactory.newInstance() - .newTransformer(); - Properties outProps = new Properties(); - outProps.setProperty(OutputKeys.ENCODING, "US-ASCII"); - outProps.setProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); - outProps.setProperty(OutputKeys.INDENT, "yes"); - idTransformer.setOutputProperties(outProps); - writer = new StringWriter(); - idTransformer.transform(new DOMSource(node), new StreamResult( - writer)); - } catch (TransformerException ex) { - TestSuiteLogger.log(Level.WARNING, "Failed to serialize node " - + node.getNodeName(), ex); - } - return writer.toString(); - } + private static XPathFactory initXPathFactory() { + XPathFactory factory = XPathFactory.newInstance(); + return factory; + } - /** - * Writes the content of a DOM Node to a byte stream. An XML declaration is - * always omitted. - * - * @param node The DOM Node to be serialized. - * @param outputStream The destination OutputStream reference. - */ - public static void writeNode(Node node, OutputStream outputStream) { - try { - Transformer idTransformer = TransformerFactory.newInstance() - .newTransformer(); - Properties outProps = new Properties(); - outProps.setProperty(OutputKeys.METHOD, "xml"); - outProps.setProperty(OutputKeys.ENCODING, "UTF-8"); - outProps.setProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); - outProps.setProperty(OutputKeys.INDENT, "yes"); - idTransformer.setOutputProperties(outProps); - idTransformer.transform(new DOMSource(node), new StreamResult( - outputStream)); - } catch (TransformerException ex) { - String nodeName = (node.getNodeType() == Node.DOCUMENT_NODE) ? Document.class - .cast(node).getDocumentElement().getNodeName() - : node.getNodeName(); - TestSuiteLogger.log(Level.WARNING, "Failed to serialize DOM node: " - + nodeName, ex); - } - } + private static XMLInputFactory initXMLInputFactory() { + XMLInputFactory factory = XMLInputFactory.newInstance(); + factory.setProperty(XMLInputFactory.IS_COALESCING, Boolean.TRUE); + return factory; + } - /** - * Evaluates an XPath 1.0 expression using the given context and returns the - * result as a node set. - * - * @param context The context node. - * @param expr An XPath expression. - * @param namespaceBindings A collection of namespace bindings for the XPath - * expression, where each entry maps a namespace URI (key) to a prefix - * (value). Standard bindings do not need to be declared (see - * {@link NamespaceBindings#withStandardBindings()}. - * @return A NodeList containing nodes that satisfy the expression (it may - * be empty). - * @throws XPathExpressionException If the expression cannot be evaluated - * for any reason. - */ - public static NodeList evaluateXPath(Node context, String expr, - Map namespaceBindings) - throws XPathExpressionException { - Object result = evaluateXPath(new DOMSource(context), expr, - namespaceBindings, XPathConstants.NODESET); - if (!NodeList.class.isInstance(result)) { - throw new XPathExpressionException( - "Expression does not evaluate to a NodeList: " + expr); - } - return (NodeList) result; - } + /** + * Writes the content of a DOM Node to a string. The XML declaration is omitted and + * the character encoding is set to "US-ASCII" (any character outside of this set is + * serialized as a numeric character reference). + * @param node The DOM Node to be serialized. + * @return A String representing the content of the given node. + */ + public static String writeNodeToString(Node node) { + if (null == node) { + return ""; + } + Writer writer = null; + try { + Transformer idTransformer = TransformerFactory.newInstance().newTransformer(); + Properties outProps = new Properties(); + outProps.setProperty(OutputKeys.ENCODING, "US-ASCII"); + outProps.setProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); + outProps.setProperty(OutputKeys.INDENT, "yes"); + idTransformer.setOutputProperties(outProps); + writer = new StringWriter(); + idTransformer.transform(new DOMSource(node), new StreamResult(writer)); + } + catch (TransformerException ex) { + TestSuiteLogger.log(Level.WARNING, "Failed to serialize node " + node.getNodeName(), ex); + } + return writer.toString(); + } - /** - * Evaluates an XPath expression using the given context item and returns - * the result as the specified type. - * - *

        - * Note: The Saxon implementation supports XPath 2.0 - * expressions when using the JAXP XPath APIs (the default implementation - * will throw an exception). - *

        - * - * @param context The context item. - * @param expr An XPath expression. - * @param namespaceBindings A collection of namespace bindings for the XPath - * expression, where each entry maps a namespace URI (key) to a prefix - * (value). Standard bindings do not need to be declared (see - * {@link NamespaceBindings#withStandardBindings()}. - * @param returnType The desired return type (as declared in - * {@link XPathConstants} ). - * - * @return The result converted to the desired returnType. - * - * @throws XPathExpressionException If the expression cannot be evaluated - * for any reason. - */ - public static Object evaluateXPath(Source context, String expr, - Map namespaceBindings, QName returnType) - throws XPathExpressionException { - Node contextNode = null; - if (DOMSource.class.isInstance(context)) { - contextNode = DOMSource.class.cast(context).getNode(); - } else { - try { - contextNode = parse(context); - } catch (TransformerException ex) { - TestSuiteLogger.log(Level.WARNING, - "Failed to read context item. ", ex); - } - } - NamespaceBindings bindings = NamespaceBindings.withStandardBindings(); - bindings.addAllBindings(namespaceBindings); - XPathFactory factory = XPATH_FACTORY; - // WARNING: If context node is Saxon NodeOverNodeInfo, the factory must - // use the same Configuration object to avoid IllegalArgumentException - XPath xpath = factory.newXPath(); - xpath.setNamespaceContext(bindings); - Object result = xpath.evaluate(expr, contextNode, returnType); - return result; - } + /** + * Writes the content of a DOM Node to a byte stream. An XML declaration is always + * omitted. + * @param node The DOM Node to be serialized. + * @param outputStream The destination OutputStream reference. + */ + public static void writeNode(Node node, OutputStream outputStream) { + try { + Transformer idTransformer = TransformerFactory.newInstance().newTransformer(); + Properties outProps = new Properties(); + outProps.setProperty(OutputKeys.METHOD, "xml"); + outProps.setProperty(OutputKeys.ENCODING, "UTF-8"); + outProps.setProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); + outProps.setProperty(OutputKeys.INDENT, "yes"); + idTransformer.setOutputProperties(outProps); + idTransformer.transform(new DOMSource(node), new StreamResult(outputStream)); + } + catch (TransformerException ex) { + String nodeName = (node.getNodeType() == Node.DOCUMENT_NODE) + ? Document.class.cast(node).getDocumentElement().getNodeName() : node.getNodeName(); + TestSuiteLogger.log(Level.WARNING, "Failed to serialize DOM node: " + nodeName, ex); + } + } - /** - * Evaluates an XPath 2.0 expression using the Saxon s9api interfaces. - * - * @param xmlSource The XML Source. - * @param expr The XPath expression to be evaluated. - * @param nsBindings A collection of namespace bindings required to evaluate - * the XPath expression, where each entry maps a namespace URI (key) to a - * prefix (value); this may be {@code null} if not needed. - * @return An XdmValue object representing a value in the XDM data model; - * this is a sequence of zero or more items, where each item is either an - * atomic value or a node. - * @throws SaxonApiException If an error occurs while evaluating the - * expression; this always wraps some other underlying exception. - */ - public static XdmValue evaluateXPath2(Source xmlSource, String expr, - Map nsBindings) throws SaxonApiException { - Processor proc = new Processor(false); - XPathCompiler compiler = proc.newXPathCompiler(); - if (null != nsBindings) { - for (String nsURI : nsBindings.keySet()) { - compiler.declareNamespace(nsBindings.get(nsURI), nsURI); - } - } - XPathSelector xpath = compiler.compile(expr).load(); - DocumentBuilder builder = proc.newDocumentBuilder(); - XdmNode node = null; - if (DOMSource.class.isInstance(xmlSource)) { - DOMSource domSource = (DOMSource) xmlSource; - node = builder.wrap(domSource.getNode()); - } else { - node = builder.build(xmlSource); - } - xpath.setContextItem(node); - return xpath.evaluate(); - } + /** + * Evaluates an XPath 1.0 expression using the given context and returns the result as + * a node set. + * @param context The context node. + * @param expr An XPath expression. + * @param namespaceBindings A collection of namespace bindings for the XPath + * expression, where each entry maps a namespace URI (key) to a prefix (value). + * Standard bindings do not need to be declared (see + * {@link NamespaceBindings#withStandardBindings()}. + * @return A NodeList containing nodes that satisfy the expression (it may be empty). + * @throws XPathExpressionException If the expression cannot be evaluated for any + * reason. + */ + public static NodeList evaluateXPath(Node context, String expr, Map namespaceBindings) + throws XPathExpressionException { + Object result = evaluateXPath(new DOMSource(context), expr, namespaceBindings, XPathConstants.NODESET); + if (!NodeList.class.isInstance(result)) { + throw new XPathExpressionException("Expression does not evaluate to a NodeList: " + expr); + } + return (NodeList) result; + } - /** - * Evaluates an XQuery 1.0 expression using the Saxon s9api interfaces. - * - * @param source The XML Source. - * @param query The query expression. - * @param nsBindings A collection of namespace bindings required to evaluate - * the query, where each entry maps a namespace URI (key) to a prefix - * (value). - * @return An XdmValue object representing a value in the XDM data model. - * @throws SaxonApiException If an error occurs while evaluating the query - * (this always wraps some other underlying exception). - */ - public static XdmValue evaluateXQuery(Source source, String query, - Map nsBindings) throws SaxonApiException { - Processor proc = new Processor(false); - XQueryCompiler xqCompiler = proc.newXQueryCompiler(); - if (null != nsBindings) { - for (String nsURI : nsBindings.keySet()) { - xqCompiler.declareNamespace(nsBindings.get(nsURI), nsURI); - } - } - XQueryExecutable xqExec = xqCompiler.compile(query); - XQueryEvaluator xqEval = xqExec.load(); - xqEval.setSource(source); - return xqEval.evaluate(); - } + /** + * Evaluates an XPath expression using the given context item and returns the result + * as the specified type. + * + *

        + * Note: The Saxon implementation supports XPath 2.0 expressions when + * using the JAXP XPath APIs (the default implementation will throw an exception). + *

        + * @param context The context item. + * @param expr An XPath expression. + * @param namespaceBindings A collection of namespace bindings for the XPath + * expression, where each entry maps a namespace URI (key) to a prefix (value). + * Standard bindings do not need to be declared (see + * {@link NamespaceBindings#withStandardBindings()}. + * @param returnType The desired return type (as declared in {@link XPathConstants} ). + * @return The result converted to the desired returnType. + * @throws XPathExpressionException If the expression cannot be evaluated for any + * reason. + */ + public static Object evaluateXPath(Source context, String expr, Map namespaceBindings, + QName returnType) throws XPathExpressionException { + Node contextNode = null; + if (DOMSource.class.isInstance(context)) { + contextNode = DOMSource.class.cast(context).getNode(); + } + else { + try { + contextNode = parse(context); + } + catch (TransformerException ex) { + TestSuiteLogger.log(Level.WARNING, "Failed to read context item. ", ex); + } + } + NamespaceBindings bindings = NamespaceBindings.withStandardBindings(); + bindings.addAllBindings(namespaceBindings); + XPathFactory factory = XPATH_FACTORY; + // WARNING: If context node is Saxon NodeOverNodeInfo, the factory must + // use the same Configuration object to avoid IllegalArgumentException + XPath xpath = factory.newXPath(); + xpath.setNamespaceContext(bindings); + Object result = xpath.evaluate(expr, contextNode, returnType); + return result; + } - /** - * Creates a new Element having the specified qualified name. The element - * must be {@link Document#adoptNode(Node) adopted} when inserted into - * another Document. - * - * @param qName A QName object. - * @return An Element node (with a Document owner but no parent). - */ - public static Element createElement(QName qName) { - Document doc = null; - try { - doc = DocumentBuilderFactory.newInstance().newDocumentBuilder() - .newDocument(); - } catch (ParserConfigurationException e) { - throw new RuntimeException(e); - } - Element elem = doc.createElementNS(qName.getNamespaceURI(), - qName.getLocalPart()); - return elem; - } + /** + * Evaluates an XPath 2.0 expression using the Saxon s9api interfaces. + * @param xmlSource The XML Source. + * @param expr The XPath expression to be evaluated. + * @param nsBindings A collection of namespace bindings required to evaluate the XPath + * expression, where each entry maps a namespace URI (key) to a prefix (value); this + * may be {@code null} if not needed. + * @return An XdmValue object representing a value in the XDM data model; this is a + * sequence of zero or more items, where each item is either an atomic value or a + * node. + * @throws SaxonApiException If an error occurs while evaluating the expression; this + * always wraps some other underlying exception. + */ + public static XdmValue evaluateXPath2(Source xmlSource, String expr, Map nsBindings) + throws SaxonApiException { + Processor proc = new Processor(false); + XPathCompiler compiler = proc.newXPathCompiler(); + if (null != nsBindings) { + for (String nsURI : nsBindings.keySet()) { + compiler.declareNamespace(nsBindings.get(nsURI), nsURI); + } + } + XPathSelector xpath = compiler.compile(expr).load(); + DocumentBuilder builder = proc.newDocumentBuilder(); + XdmNode node = null; + if (DOMSource.class.isInstance(xmlSource)) { + DOMSource domSource = (DOMSource) xmlSource; + node = builder.wrap(domSource.getNode()); + } + else { + node = builder.build(xmlSource); + } + xpath.setContextItem(node); + return xpath.evaluate(); + } - /** - * Returns a List of all descendant Element nodes having the specified - * [namespace name] property. The elements are listed in document order. - * - * @param node The node to search from. - * @param namespaceURI An absolute URI denoting a namespace name. - * @return A List containing elements in the specified namespace; the list - * is empty if there are no elements in the namespace. - */ - public static List getElementsByNamespaceURI(Node node, - String namespaceURI) { - List list = new ArrayList(); - NodeList children = node.getChildNodes(); - for (int i = 0; i < children.getLength(); i++) { - Node child = children.item(i); - if (child.getNodeType() != Node.ELEMENT_NODE) { - continue; - } - if (child.getNamespaceURI().equals(namespaceURI)) { - list.add((Element) child); - } - } - return list; - } + /** + * Evaluates an XQuery 1.0 expression using the Saxon s9api interfaces. + * @param source The XML Source. + * @param query The query expression. + * @param nsBindings A collection of namespace bindings required to evaluate the + * query, where each entry maps a namespace URI (key) to a prefix (value). + * @return An XdmValue object representing a value in the XDM data model. + * @throws SaxonApiException If an error occurs while evaluating the query (this + * always wraps some other underlying exception). + */ + public static XdmValue evaluateXQuery(Source source, String query, Map nsBindings) + throws SaxonApiException { + Processor proc = new Processor(false); + XQueryCompiler xqCompiler = proc.newXQueryCompiler(); + if (null != nsBindings) { + for (String nsURI : nsBindings.keySet()) { + xqCompiler.declareNamespace(nsBindings.get(nsURI), nsURI); + } + } + XQueryExecutable xqExec = xqCompiler.compile(query); + XQueryEvaluator xqEval = xqExec.load(); + xqEval.setSource(source); + return xqEval.evaluate(); + } - /** - * Transforms the content of a DOM Node using a specified XSLT stylesheet. - * - * @param xslt A Source object representing a stylesheet (XSLT 1.0 or 2.0). - * @param source A Node representing the XML source. If it is an Element - * node it will be imported into a new DOM Document. - * @return A DOM Document containing the result of the transformation. - */ - public static Document transform(Source xslt, Node source) { - Document sourceDoc = null; - Document resultDoc = null; - try { - resultDoc = DocumentBuilderFactory.newInstance() - .newDocumentBuilder().newDocument(); - if (source.getNodeType() == Node.DOCUMENT_NODE) { - sourceDoc = (Document) source; - } else { - sourceDoc = DocumentBuilderFactory.newInstance() - .newDocumentBuilder().newDocument(); - sourceDoc.appendChild(sourceDoc.importNode(source, true)); - } - } catch (ParserConfigurationException pce) { - throw new RuntimeException(pce); - } - Processor processor = new Processor(false); - XsltCompiler compiler = processor.newXsltCompiler(); - try { - XsltExecutable exec = compiler.compile(xslt); - XsltTransformer transformer = exec.load(); - transformer.setSource(new DOMSource(sourceDoc)); - transformer.setDestination(new DOMDestination(resultDoc)); - transformer.transform(); - } catch (SaxonApiException e) { - throw new RuntimeException(e); - } - return resultDoc; - } + /** + * Creates a new Element having the specified qualified name. The element must be + * {@link Document#adoptNode(Node) adopted} when inserted into another Document. + * @param qName A QName object. + * @return An Element node (with a Document owner but no parent). + */ + public static Element createElement(QName qName) { + Document doc = null; + try { + doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); + } + catch (ParserConfigurationException e) { + throw new RuntimeException(e); + } + Element elem = doc.createElementNS(qName.getNamespaceURI(), qName.getLocalPart()); + return elem; + } - /** - * Expands character entity ({@literal &name;}) and numeric references - * ({@literal &#xhhhh;} or {@literal &dddd;}) that occur within a given - * string value. It may be necessary to do this before processing an XPath - * expression. - * - * @param value A string representing text content. - * @return A string with all included references expanded. - */ - public static String expandReferencesInText(String value) { - StringBuilder wrapper = new StringBuilder(""); - wrapper.append(value).append(""); - Reader reader = new StringReader(wrapper.toString()); - String str = null; - try { - XMLStreamReader xsr = STAX_FACTORY.createXMLStreamReader(reader); - xsr.nextTag(); // document element - str = xsr.getElementText(); - } catch (XMLStreamException xse) { - LOGR.log(Level.WARNING, xse.getMessage(), xse); - } - return str; - } + /** + * Returns a List of all descendant Element nodes having the specified [namespace + * name] property. The elements are listed in document order. + * @param node The node to search from. + * @param namespaceURI An absolute URI denoting a namespace name. + * @return A List containing elements in the specified namespace; the list is empty if + * there are no elements in the namespace. + */ + public static List getElementsByNamespaceURI(Node node, String namespaceURI) { + List list = new ArrayList(); + NodeList children = node.getChildNodes(); + for (int i = 0; i < children.getLength(); i++) { + Node child = children.item(i); + if (child.getNodeType() != Node.ELEMENT_NODE) { + continue; + } + if (child.getNamespaceURI().equals(namespaceURI)) { + list.add((Element) child); + } + } + return list; + } - /** - * Converts a DOMSource object to a StreamSource representing an XML data - * source. The system ID is preserved, allowing relative URIs to be - * processed. - * - * @param domSource A DOMSource instance. - * - * @return A StreamSource object for reading the content represented by the - * original DOM tree. - */ - public static StreamSource toStreamSource(DOMSource domSource) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - StreamResult result = new StreamResult(baos); - try { - // use identity transformer - Transformer idt = TransformerFactory.newInstance().newTransformer(); - idt.transform(domSource, result); - } catch (TransformerException tex) { - LOGR.log(Level.WARNING, "Error serializing DOMSource " - + domSource.getSystemId(), tex); - } - StreamSource streamSrc = new StreamSource(new ByteArrayInputStream( - baos.toByteArray()), domSource.getSystemId()); - return streamSrc; - } + /** + * Transforms the content of a DOM Node using a specified XSLT stylesheet. + * @param xslt A Source object representing a stylesheet (XSLT 1.0 or 2.0). + * @param source A Node representing the XML source. If it is an Element node it will + * be imported into a new DOM Document. + * @return A DOM Document containing the result of the transformation. + */ + public static Document transform(Source xslt, Node source) { + Document sourceDoc = null; + Document resultDoc = null; + try { + resultDoc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); + if (source.getNodeType() == Node.DOCUMENT_NODE) { + sourceDoc = (Document) source; + } + else { + sourceDoc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); + sourceDoc.appendChild(sourceDoc.importNode(source, true)); + } + } + catch (ParserConfigurationException pce) { + throw new RuntimeException(pce); + } + Processor processor = new Processor(false); + XsltCompiler compiler = processor.newXsltCompiler(); + try { + XsltExecutable exec = compiler.compile(xslt); + XsltTransformer transformer = exec.load(); + transformer.setSource(new DOMSource(sourceDoc)); + transformer.setDestination(new DOMDestination(resultDoc)); + transformer.transform(); + } + catch (SaxonApiException e) { + throw new RuntimeException(e); + } + return resultDoc; + } - /** - * Returns the name of the document element in the given XML resource. - * - * @param source The Source to read the document from. - * @return The qualified name of the document element, or null - * if the source is not an XML document or it cannot be read for some - * reason. - */ - public static QName nameOfDocumentElement(Source source) { - XMLInputFactory factory = XMLInputFactory.newInstance(); - QName qName = null; - try { - XMLEventReader reader = factory.createXMLEventReader(source); - // advance to document element - StartElement docElem = reader.nextTag().asStartElement(); - qName = docElem.getName(); - } catch (XMLStreamException xse) { - LOGR.log(Level.WARNING, "Failed to read Source.", xse); - } - return qName; - } + /** + * Expands character entity ({@literal &name;}) and numeric references + * ({@literal &#xhhhh;} or {@literal &dddd;}) that occur within a given string value. + * It may be necessary to do this before processing an XPath expression. + * @param value A string representing text content. + * @return A string with all included references expanded. + */ + public static String expandReferencesInText(String value) { + StringBuilder wrapper = new StringBuilder(""); + wrapper.append(value).append(""); + Reader reader = new StringReader(wrapper.toString()); + String str = null; + try { + XMLStreamReader xsr = STAX_FACTORY.createXMLStreamReader(reader); + xsr.nextTag(); // document element + str = xsr.getElementText(); + } + catch (XMLStreamException xse) { + LOGR.log(Level.WARNING, xse.getMessage(), xse); + } + return str; + } - /** - * Parses the content of the given Source and returns a DOM Document node. - * - * @param source The Source to read the XML content from. - * @return A Document node representing the XML content. - * - * @throws javax.xml.transform.TransformerException If the source cannot be - * parsed for any reason. - */ - public static Document parse(Source source) throws TransformerException { - Transformer idt = TransformerFactory.newInstance().newTransformer(); - DOMResult result = new DOMResult(); - idt.transform(source, result); - Document doc = (Document) result.getNode(); - if (null != doc) { - doc.setDocumentURI(source.getSystemId()); - } - return doc; - } + /** + * Converts a DOMSource object to a StreamSource representing an XML data source. The + * system ID is preserved, allowing relative URIs to be processed. + * @param domSource A DOMSource instance. + * @return A StreamSource object for reading the content represented by the original + * DOM tree. + */ + public static StreamSource toStreamSource(DOMSource domSource) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + StreamResult result = new StreamResult(baos); + try { + // use identity transformer + Transformer idt = TransformerFactory.newInstance().newTransformer(); + idt.transform(domSource, result); + } + catch (TransformerException tex) { + LOGR.log(Level.WARNING, "Error serializing DOMSource " + domSource.getSystemId(), tex); + } + StreamSource streamSrc = new StreamSource(new ByteArrayInputStream(baos.toByteArray()), + domSource.getSystemId()); + return streamSrc; + } - /** - * Returns a List view of the specified NodeList collection. - * - * @param nodeList An ordered collection of DOM nodes. - * @return A List containing the original sequence of Node objects. - */ - public static List getNodeListAsList(NodeList nodeList) { - List nodes = new ArrayList<>(); - for (int i = 0; i < nodeList.getLength(); i++) { - nodes.add(nodeList.item(i)); - } - return nodes; - } + /** + * Returns the name of the document element in the given XML resource. + * @param source The Source to read the document from. + * @return The qualified name of the document element, or null if the + * source is not an XML document or it cannot be read for some reason. + */ + public static QName nameOfDocumentElement(Source source) { + XMLInputFactory factory = XMLInputFactory.newInstance(); + QName qName = null; + try { + XMLEventReader reader = factory.createXMLEventReader(source); + // advance to document element + StartElement docElem = reader.nextTag().asStartElement(); + qName = docElem.getName(); + } + catch (XMLStreamException xse) { + LOGR.log(Level.WARNING, "Failed to read Source.", xse); + } + return qName; + } - /** - * Writes the content of an XdmValue sequence to a string. Each item in the - * sequence is either an atomic value or a node. - * - * @param value A value in the XDM data model. - * @return A String representing the content of the sequence. - * - * @see - * Saxon - * API: XdmValue - * @see - * XQuery - * 1.0 and XPath 2.0 Data Model (XDM) (Second Edition) - */ - public static String writeXdmValueToString(XdmValue value) { - StringBuilder str = new StringBuilder(); - for (XdmItem item : value) { - if (item.isAtomicValue()) { - str.append(item.getStringValue()); - } else { - XdmNode node = (XdmNode) item; - str.append(node.getNodeName()).append(": "); - str.append(node.getStringValue()); - } - str.append('\n'); - } - return str.toString(); - } + /** + * Parses the content of the given Source and returns a DOM Document node. + * @param source The Source to read the XML content from. + * @return A Document node representing the XML content. + * @throws javax.xml.transform.TransformerException If the source cannot be parsed for + * any reason. + */ + public static Document parse(Source source) throws TransformerException { + Transformer idt = TransformerFactory.newInstance().newTransformer(); + DOMResult result = new DOMResult(); + idt.transform(source, result); + Document doc = (Document) result.getNode(); + if (null != doc) { + doc.setDocumentURI(source.getSystemId()); + } + return doc; + } - /** - * Determines if the given media type is an XML-based media type. - * - * @param mediaType A MediaType object. - * @return true if the type corresponds to an XML entity; false otherwise. - * - * @see RFC - * 7303: XML Media Types - */ - public static boolean isXML(final MediaType mediaType) { - return mediaType.getSubtype().endsWith("xml"); - } + /** + * Returns a List view of the specified NodeList collection. + * @param nodeList An ordered collection of DOM nodes. + * @return A List containing the original sequence of Node objects. + */ + public static List getNodeListAsList(NodeList nodeList) { + List nodes = new ArrayList<>(); + for (int i = 0; i < nodeList.getLength(); i++) { + nodes.add(nodeList.item(i)); + } + return nodes; + } + + /** + * Writes the content of an XdmValue sequence to a string. Each item in the sequence + * is either an atomic value or a node. + * @param value A value in the XDM data model. + * @return A String representing the content of the sequence. + * + * @see Saxon + * API: XdmValue + * @see XQuery 1.0 and + * XPath 2.0 Data Model (XDM) (Second Edition) + */ + public static String writeXdmValueToString(XdmValue value) { + StringBuilder str = new StringBuilder(); + for (XdmItem item : value) { + if (item.isAtomicValue()) { + str.append(item.getStringValue()); + } + else { + XdmNode node = (XdmNode) item; + str.append(node.getNodeName()).append(": "); + str.append(node.getStringValue()); + } + str.append('\n'); + } + return str.toString(); + } + + /** + * Determines if the given media type is an XML-based media type. + * @param mediaType A MediaType object. + * @return true if the type corresponds to an XML entity; false otherwise. + * + * @see RFC 7303: XML + * Media Types + */ + public static boolean isXML(final MediaType mediaType) { + return mediaType.getSubtype().endsWith("xml"); + } + + /** + * Returns the text content of the nodes in the given list. + * @param nodeList A sequence of DOM nodes. + * @return A list of String values, each of which represents the content of a node + * (and its descendants, if any). + */ + public static List getNodeValues(NodeList nodeList) { + List valueList = new ArrayList<>(); + for (int i = 0; i < nodeList.getLength(); i++) { + valueList.add(nodeList.item(i).getTextContent()); + } + return valueList; + } - /** - * Returns the text content of the nodes in the given list. - * - * @param nodeList A sequence of DOM nodes. - * @return A list of String values, each of which represents the content of - * a node (and its descendants, if any). - */ - public static List getNodeValues(NodeList nodeList) { - List valueList = new ArrayList<>(); - for (int i = 0; i < nodeList.getLength(); i++) { - valueList.add(nodeList.item(i).getTextContent()); - } - return valueList; - } } diff --git a/src/test/java/org/opengis/cite/cat30/TestCommon.java b/src/test/java/org/opengis/cite/cat30/TestCommon.java index fe3184d..5b80f7a 100644 --- a/src/test/java/org/opengis/cite/cat30/TestCommon.java +++ b/src/test/java/org/opengis/cite/cat30/TestCommon.java @@ -12,19 +12,23 @@ import jakarta.ws.rs.core.Response; public abstract class TestCommon { - - protected final Response rsp = mock( Response.class ); - protected final WebTarget mockWebTarget = mock(WebTarget.class); - protected final Invocation.Builder mockBuilder = mock(Invocation.Builder.class); - protected final Client client = mock( Client.class ); - protected static ISuite suite = mock(ISuite.class); - - protected void mockResponse() { - when(suite.getAttribute(SuiteAttribute.CLIENT.getName())) - .thenReturn(client); - when(client.target(anyString())).thenReturn(mockWebTarget); - when(mockWebTarget.request()).thenReturn(mockBuilder); - when(mockBuilder.get()).thenReturn(rsp); - when(rsp.getStatus()).thenReturn(Response.Status.OK.getStatusCode()); - } + + protected final Response rsp = mock(Response.class); + + protected final WebTarget mockWebTarget = mock(WebTarget.class); + + protected final Invocation.Builder mockBuilder = mock(Invocation.Builder.class); + + protected final Client client = mock(Client.class); + + protected static ISuite suite = mock(ISuite.class); + + protected void mockResponse() { + when(suite.getAttribute(SuiteAttribute.CLIENT.getName())).thenReturn(client); + when(client.target(anyString())).thenReturn(mockWebTarget); + when(mockWebTarget.request()).thenReturn(mockBuilder); + when(mockBuilder.get()).thenReturn(rsp); + when(rsp.getStatus()).thenReturn(Response.Status.OK.getStatusCode()); + } + } diff --git a/src/test/java/org/opengis/cite/cat30/VerifyETSAssert.java b/src/test/java/org/opengis/cite/cat30/VerifyETSAssert.java index 8f0c530..a337c3c 100644 --- a/src/test/java/org/opengis/cite/cat30/VerifyETSAssert.java +++ b/src/test/java/org/opengis/cite/cat30/VerifyETSAssert.java @@ -18,85 +18,79 @@ public class VerifyETSAssert { - private static final String WADL_NS = "http://wadl.dev.java.net/2009/02"; - private static DocumentBuilder docBuilder; - private static SchemaFactory factory; - @Rule - public ExpectedException thrown = ExpectedException.none(); - - public VerifyETSAssert() { - } - - @BeforeClass - public static void setUpClass() throws ParserConfigurationException { - factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - dbf.setNamespaceAware(true); - docBuilder = dbf.newDocumentBuilder(); - } - - @Test - public void assertXPathWithNamespaceBindings() throws SAXException, - IOException { - Document doc = docBuilder.parse(this.getClass().getResourceAsStream( - "/capabilities/basic.xml")); - Map nsBindings = new HashMap<>(); - nsBindings.put(WADL_NS, "ns1"); - String xpath = "//ns1:resources"; - ETSAssert.assertXPath(xpath, doc, nsBindings); - } - - @Test - public void assertXPath_expectFalse() throws SAXException, IOException { - thrown.expect(AssertionError.class); - thrown.expectMessage("Unexpected result evaluating XPath expression"); - Document doc = docBuilder.parse(this.getClass().getResourceAsStream( - "/capabilities/basic.xml")); - // using built-in namespace bindings - String xpath = "//ows:OperationsMetadata/ows:Constraint[@name='GetCapabilities-XML']/ows:DefaultValue = 'TRUE'"; - ETSAssert.assertXPath(xpath, doc, null); - } - - @Test - public void emptyAtomFeed() throws SAXException, IOException { - Document doc = docBuilder.parse(this.getClass().getResourceAsStream( - "/atom/feed-empty.xml")); - ETSAssert.assertEmptyResultSet(doc); - } - - @Test - public void searchResponseNotEmpty() throws SAXException, IOException { - thrown.expect(AssertionError.class); - thrown.expectMessage("csw:SearchResults/@numberOfRecordsMatched = 0"); - Document doc = docBuilder.parse(this.getClass().getResourceAsStream( - "/rsp/GetRecordsResponse-1.xml")); - ETSAssert.assertEmptyResultSet(doc); - } - - @Test - public void emptySearchResponse() throws SAXException, IOException { - Document doc = docBuilder.parse(this.getClass().getResourceAsStream( - "/rsp/GetRecordsResponse-empty.xml")); - ETSAssert.assertEmptyResultSet(doc); - } - - @Test - public void searchTermOccursInAttribute() - throws SAXException, IOException { - Document doc = docBuilder.parse(this.getClass().getResourceAsStream( - "/atom/feed.xml")); - NodeList records = doc.getElementsByTagNameNS(Namespaces.ATOM, "entry"); - ETSAssert.assertAllTermsOccur(records, "robotics"); - } - - @Test - public void searchTermWithNonASCIICharDoesNotOccur() - throws SAXException, IOException { - thrown.expect(AssertionError.class); - thrown.expectMessage("Unexpected result evaluating XPath expression"); - Document doc = docBuilder.parse(this.getClass().getResourceAsStream( - "/atom/feed.xml")); - NodeList records = doc.getElementsByTagNameNS(Namespaces.ATOM, "entry"); - ETSAssert.assertAllTermsOccur(records, "données"); - } + private static final String WADL_NS = "http://wadl.dev.java.net/2009/02"; + + private static DocumentBuilder docBuilder; + + private static SchemaFactory factory; + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + public VerifyETSAssert() { + } + + @BeforeClass + public static void setUpClass() throws ParserConfigurationException { + factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setNamespaceAware(true); + docBuilder = dbf.newDocumentBuilder(); + } + + @Test + public void assertXPathWithNamespaceBindings() throws SAXException, IOException { + Document doc = docBuilder.parse(this.getClass().getResourceAsStream("/capabilities/basic.xml")); + Map nsBindings = new HashMap<>(); + nsBindings.put(WADL_NS, "ns1"); + String xpath = "//ns1:resources"; + ETSAssert.assertXPath(xpath, doc, nsBindings); + } + + @Test + public void assertXPath_expectFalse() throws SAXException, IOException { + thrown.expect(AssertionError.class); + thrown.expectMessage("Unexpected result evaluating XPath expression"); + Document doc = docBuilder.parse(this.getClass().getResourceAsStream("/capabilities/basic.xml")); + // using built-in namespace bindings + String xpath = "//ows:OperationsMetadata/ows:Constraint[@name='GetCapabilities-XML']/ows:DefaultValue = 'TRUE'"; + ETSAssert.assertXPath(xpath, doc, null); + } + + @Test + public void emptyAtomFeed() throws SAXException, IOException { + Document doc = docBuilder.parse(this.getClass().getResourceAsStream("/atom/feed-empty.xml")); + ETSAssert.assertEmptyResultSet(doc); + } + + @Test + public void searchResponseNotEmpty() throws SAXException, IOException { + thrown.expect(AssertionError.class); + thrown.expectMessage("csw:SearchResults/@numberOfRecordsMatched = 0"); + Document doc = docBuilder.parse(this.getClass().getResourceAsStream("/rsp/GetRecordsResponse-1.xml")); + ETSAssert.assertEmptyResultSet(doc); + } + + @Test + public void emptySearchResponse() throws SAXException, IOException { + Document doc = docBuilder.parse(this.getClass().getResourceAsStream("/rsp/GetRecordsResponse-empty.xml")); + ETSAssert.assertEmptyResultSet(doc); + } + + @Test + public void searchTermOccursInAttribute() throws SAXException, IOException { + Document doc = docBuilder.parse(this.getClass().getResourceAsStream("/atom/feed.xml")); + NodeList records = doc.getElementsByTagNameNS(Namespaces.ATOM, "entry"); + ETSAssert.assertAllTermsOccur(records, "robotics"); + } + + @Test + public void searchTermWithNonASCIICharDoesNotOccur() throws SAXException, IOException { + thrown.expect(AssertionError.class); + thrown.expectMessage("Unexpected result evaluating XPath expression"); + Document doc = docBuilder.parse(this.getClass().getResourceAsStream("/atom/feed.xml")); + NodeList records = doc.getElementsByTagNameNS(Namespaces.ATOM, "entry"); + ETSAssert.assertAllTermsOccur(records, "données"); + } + } diff --git a/src/test/java/org/opengis/cite/cat30/VerifySuiteFixtureListener.java b/src/test/java/org/opengis/cite/cat30/VerifySuiteFixtureListener.java index 107e929..75ebbc2 100644 --- a/src/test/java/org/opengis/cite/cat30/VerifySuiteFixtureListener.java +++ b/src/test/java/org/opengis/cite/cat30/VerifySuiteFixtureListener.java @@ -20,50 +20,50 @@ public class VerifySuiteFixtureListener { - private static XmlSuite xmlSuite; - private static ISuite suite; + private static XmlSuite xmlSuite; - public VerifySuiteFixtureListener() { - } + private static ISuite suite; - @BeforeClass - public static void setUpClass() { - xmlSuite = mock(XmlSuite.class); - suite = mock(ISuite.class); - when(suite.getXmlSuite()).thenReturn(xmlSuite); - } + public VerifySuiteFixtureListener() { + } - @AfterClass - public static void tearDownClass() { - } + @BeforeClass + public static void setUpClass() { + xmlSuite = mock(XmlSuite.class); + suite = mock(ISuite.class); + when(suite.getXmlSuite()).thenReturn(xmlSuite); + } - @Before - public void setUp() { - } + @AfterClass + public static void tearDownClass() { + } - @After - public void tearDown() { - } + @Before + public void setUp() { + } - @Test(expected = IllegalArgumentException.class) - public void noSuiteParameters() { - Map params = new HashMap(); - when(xmlSuite.getParameters()).thenReturn(params); - SuiteFixtureListener iut = new SuiteFixtureListener(); - iut.onStart(suite); - } + @After + public void tearDown() { + } - @Test - public void processIUTParameter() throws URISyntaxException { - URL url = this.getClass().getResource("/capabilities/basic.xml"); - Map params = new HashMap(); - params.put(TestRunArg.IUT.toString(), url.toURI().toString()); - when(xmlSuite.getParameters()).thenReturn(params); - SuiteFixtureListener iut = new SuiteFixtureListener(); - iut.onStart(suite); - verify(suite).setAttribute( - ArgumentMatchers.eq(SuiteAttribute.TEST_SUBJECT.getName()), - ArgumentMatchers.isA(Document.class)); - } + @Test(expected = IllegalArgumentException.class) + public void noSuiteParameters() { + Map params = new HashMap(); + when(xmlSuite.getParameters()).thenReturn(params); + SuiteFixtureListener iut = new SuiteFixtureListener(); + iut.onStart(suite); + } + + @Test + public void processIUTParameter() throws URISyntaxException { + URL url = this.getClass().getResource("/capabilities/basic.xml"); + Map params = new HashMap(); + params.put(TestRunArg.IUT.toString(), url.toURI().toString()); + when(xmlSuite.getParameters()).thenReturn(params); + SuiteFixtureListener iut = new SuiteFixtureListener(); + iut.onStart(suite); + verify(suite).setAttribute(ArgumentMatchers.eq(SuiteAttribute.TEST_SUBJECT.getName()), + ArgumentMatchers.isA(Document.class)); + } } diff --git a/src/test/java/org/opengis/cite/cat30/VerifyTestNGController.java b/src/test/java/org/opengis/cite/cat30/VerifyTestNGController.java index f510108..4f38713 100644 --- a/src/test/java/org/opengis/cite/cat30/VerifyTestNGController.java +++ b/src/test/java/org/opengis/cite/cat30/VerifyTestNGController.java @@ -28,43 +28,39 @@ */ public class VerifyTestNGController { - private static DocumentBuilder docBuilder; - private Properties testRunProps; + private static DocumentBuilder docBuilder; - @BeforeClass - public static void initParser() throws ParserConfigurationException { - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - dbf.setNamespaceAware(true); - dbf.setValidating(false); - dbf.setFeature( - "http://apache.org/xml/features/nonvalidating/load-external-dtd", - false); - docBuilder = dbf.newDocumentBuilder(); - } + private Properties testRunProps; - @Before - public void loadDefaultTestRunProperties() - throws InvalidPropertiesFormatException, IOException { - this.testRunProps = new Properties(); - this.testRunProps.loadFromXML(getClass().getResourceAsStream( - "/test-run-props.xml")); - } + @BeforeClass + public static void initParser() throws ParserConfigurationException { + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setNamespaceAware(true); + dbf.setValidating(false); + dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); + docBuilder = dbf.newDocumentBuilder(); + } + + @Before + public void loadDefaultTestRunProperties() throws InvalidPropertiesFormatException, IOException { + this.testRunProps = new Properties(); + this.testRunProps.loadFromXML(getClass().getResourceAsStream("/test-run-props.xml")); + } + + @Test + public void skipAllTests_sutIsUnavailable() throws Exception { + URL testSubject = this.getClass().getResource("basic-unavailable.xml"); + this.testRunProps.setProperty(TestRunArg.IUT.toString(), testSubject.toURI().toString()); + ByteArrayOutputStream outStream = new ByteArrayOutputStream(1024); + this.testRunProps.storeToXML(outStream, "Integration test"); + Document testRunArgs = docBuilder.parse(new ByteArrayInputStream(outStream.toByteArray())); + TestNGController controller = new TestNGController(); + Source source = controller.doTestRun(testRunArgs); + String xpathSkipped = "/testng-results/@skipped"; + XdmValue skipped = XMLUtils.evaluateXPath2(source, xpathSkipped, null); + // all tests should have been skipped + int numSkipped = Integer.parseInt(skipped.getUnderlyingValue().getStringValue()); + assertEquals("Unexpected number of fail verdicts.", 52, numSkipped); + } - @Test - public void skipAllTests_sutIsUnavailable() throws Exception { - URL testSubject = this.getClass().getResource("basic-unavailable.xml"); - this.testRunProps.setProperty(TestRunArg.IUT.toString(), testSubject - .toURI().toString()); - ByteArrayOutputStream outStream = new ByteArrayOutputStream(1024); - this.testRunProps.storeToXML(outStream, "Integration test"); - Document testRunArgs = docBuilder.parse(new ByteArrayInputStream( - outStream.toByteArray())); - TestNGController controller = new TestNGController(); - Source source = controller.doTestRun(testRunArgs); - String xpathSkipped = "/testng-results/@skipped"; - XdmValue skipped = XMLUtils.evaluateXPath2(source, xpathSkipped, null); - // all tests should have been skipped - int numSkipped = Integer.parseInt(skipped.getUnderlyingValue().getStringValue()); - assertEquals("Unexpected number of fail verdicts.", 52, numSkipped); - } } diff --git a/src/test/java/org/opengis/cite/cat30/basic/VerifyBasicGetRecordsTests.java b/src/test/java/org/opengis/cite/cat30/basic/VerifyBasicGetRecordsTests.java index 0de5fbd..cbac32a 100644 --- a/src/test/java/org/opengis/cite/cat30/basic/VerifyBasicGetRecordsTests.java +++ b/src/test/java/org/opengis/cite/cat30/basic/VerifyBasicGetRecordsTests.java @@ -36,66 +36,67 @@ public class VerifyBasicGetRecordsTests extends TestCommon { - @Rule - public ExpectedException thrown = ExpectedException.none(); + @Rule + public ExpectedException thrown = ExpectedException.none(); - private static DocumentBuilder docBuilder; + private static DocumentBuilder docBuilder; - private static ITestContext testContext; + private static ITestContext testContext; - private static Schema cswSchema; + private static Schema cswSchema; - private static Schema atomSchema; - - @BeforeClass - public static void initTestFixture() - throws Exception { - testContext = mock( ITestContext.class ); - when( testContext.getSuite() ).thenReturn( suite ); - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - dbf.setNamespaceAware( true ); - docBuilder = dbf.newDocumentBuilder(); - cswSchema = ValidationUtils.createCSWSchema(); - when( suite.getAttribute( SuiteAttribute.CSW_SCHEMA.getName() ) ).thenReturn( cswSchema ); - atomSchema = ValidationUtils.createAtomSchema(); - when( suite.getAttribute( SuiteAttribute.ATOM_SCHEMA.getName() ) ).thenReturn( atomSchema ); - } + private static Schema atomSchema; - @Test - public void testPresentTitleProperty() throws Exception { - Document doc = docBuilder.parse(getClass().getResourceAsStream("/capabilities/basic.xml")); - when( suite.getAttribute( SuiteAttribute.TEST_SUBJECT.getName() ) ).thenReturn( doc ); - mockResponse(); - Document entity = - docBuilder.parse(this.getClass().getResourceAsStream("/getrecords/GetRecords-Summary-Response.xml")); - BasicGetRecordsTests spy = Mockito.spy(new BasicGetRecordsTests()); - spy.setGetEndpoint(new URI("http://test")); - try (MockedStatic clientUtils = Mockito.mockStatic(ClientUtils.class)) { - clientUtils.when(() -> ClientUtils.buildGetRequest(any(URI.class), ArgumentMatchers.anyMap(), any(MediaType.class))) - .thenReturn(rsp); - Mockito.doReturn(entity).when(spy).getResponseEntityAsDocument(any(Response.class), nullable(String.class)); - spy.initCommonFixture(testContext); - spy.presentTitleProperty(); - } - } + @BeforeClass + public static void initTestFixture() throws Exception { + testContext = mock(ITestContext.class); + when(testContext.getSuite()).thenReturn(suite); + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setNamespaceAware(true); + docBuilder = dbf.newDocumentBuilder(); + cswSchema = ValidationUtils.createCSWSchema(); + when(suite.getAttribute(SuiteAttribute.CSW_SCHEMA.getName())).thenReturn(cswSchema); + atomSchema = ValidationUtils.createAtomSchema(); + when(suite.getAttribute(SuiteAttribute.ATOM_SCHEMA.getName())).thenReturn(atomSchema); + } - @Test - public void testPresentTitleProperty_invalid() - throws Exception { - thrown.expect(AssertionError.class); - Document doc = docBuilder.parse( getClass().getResourceAsStream( "/capabilities/basic.xml" ) ); - when( suite.getAttribute( SuiteAttribute.TEST_SUBJECT.getName() ) ).thenReturn( doc ); - mockResponse(); - Document entity = docBuilder.parse( this.getClass().getResourceAsStream( "/getrecords/GetRecords-Summary-Response-invalid.xml" ) ); - BasicGetRecordsTests spy = Mockito.spy( new BasicGetRecordsTests() ); - spy.setGetEndpoint( new URI( "http://test" ) ); - try (MockedStatic clientUtils = Mockito.mockStatic(ClientUtils.class)) { - clientUtils.when(() -> ClientUtils.buildGetRequest(any(URI.class), any(Map.class), any(MediaType.class))) - .thenReturn(rsp); - Mockito.doReturn(entity).when(spy).getResponseEntityAsDocument(any(Response.class), nullable(String.class)); - spy.initCommonFixture(testContext); - spy.presentTitleProperty(); - } - } + @Test + public void testPresentTitleProperty() throws Exception { + Document doc = docBuilder.parse(getClass().getResourceAsStream("/capabilities/basic.xml")); + when(suite.getAttribute(SuiteAttribute.TEST_SUBJECT.getName())).thenReturn(doc); + mockResponse(); + Document entity = docBuilder + .parse(this.getClass().getResourceAsStream("/getrecords/GetRecords-Summary-Response.xml")); + BasicGetRecordsTests spy = Mockito.spy(new BasicGetRecordsTests()); + spy.setGetEndpoint(new URI("http://test")); + try (MockedStatic clientUtils = Mockito.mockStatic(ClientUtils.class)) { + clientUtils + .when(() -> ClientUtils.buildGetRequest(any(URI.class), ArgumentMatchers.anyMap(), + any(MediaType.class))) + .thenReturn(rsp); + Mockito.doReturn(entity).when(spy).getResponseEntityAsDocument(any(Response.class), nullable(String.class)); + spy.initCommonFixture(testContext); + spy.presentTitleProperty(); + } + } + + @Test + public void testPresentTitleProperty_invalid() throws Exception { + thrown.expect(AssertionError.class); + Document doc = docBuilder.parse(getClass().getResourceAsStream("/capabilities/basic.xml")); + when(suite.getAttribute(SuiteAttribute.TEST_SUBJECT.getName())).thenReturn(doc); + mockResponse(); + Document entity = docBuilder + .parse(this.getClass().getResourceAsStream("/getrecords/GetRecords-Summary-Response-invalid.xml")); + BasicGetRecordsTests spy = Mockito.spy(new BasicGetRecordsTests()); + spy.setGetEndpoint(new URI("http://test")); + try (MockedStatic clientUtils = Mockito.mockStatic(ClientUtils.class)) { + clientUtils.when(() -> ClientUtils.buildGetRequest(any(URI.class), any(Map.class), any(MediaType.class))) + .thenReturn(rsp); + Mockito.doReturn(entity).when(spy).getResponseEntityAsDocument(any(Response.class), nullable(String.class)); + spy.initCommonFixture(testContext); + spy.presentTitleProperty(); + } + } } diff --git a/src/test/java/org/opengis/cite/cat30/basic/VerifyBasicSearchTests.java b/src/test/java/org/opengis/cite/cat30/basic/VerifyBasicSearchTests.java index 9ae4a4e..b8c4e29 100644 --- a/src/test/java/org/opengis/cite/cat30/basic/VerifyBasicSearchTests.java +++ b/src/test/java/org/opengis/cite/cat30/basic/VerifyBasicSearchTests.java @@ -41,103 +41,107 @@ public class VerifyBasicSearchTests extends TestCommon { - @Rule - public ExpectedException thrown = ExpectedException.none(); - private static DocumentBuilder docBuilder; - private static ITestContext testContext; - private static Schema cswSchema; - private static Schema atomSchema; - - @BeforeClass - public static void initTestFixture() throws Exception { - testContext = mock(ITestContext.class); - when(testContext.getSuite()).thenReturn(suite); - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - dbf.setNamespaceAware(true); - docBuilder = dbf.newDocumentBuilder(); - Document doc = docBuilder.parse(VerifyBasicSearchTests.class.getResourceAsStream( - "/capabilities/basic.xml")); - when(suite.getAttribute(SuiteAttribute.TEST_SUBJECT.getName())).thenReturn(doc); - cswSchema = ValidationUtils.createCSWSchema(); - when(suite.getAttribute(SuiteAttribute.CSW_SCHEMA.getName())) - .thenReturn(cswSchema); - atomSchema = ValidationUtils.createAtomSchema(); - when(suite.getAttribute(SuiteAttribute.ATOM_SCHEMA.getName())) - .thenReturn(atomSchema); - } - - @Test - public void getBriefRecordsByBBOX_allIntersect() - throws SAXException, IOException, FactoryException, TransformException { - mockResponse(); - Document rspEntity = docBuilder.parse(this.getClass().getResourceAsStream( - "/rsp/GetRecordsResponse-full.xml")); - BasicSearchTests spy = Mockito.spy(new BasicSearchTests()); - try (MockedStatic clientUtils = Mockito.mockStatic(ClientUtils.class)) { - clientUtils.when(() -> ClientUtils.buildGetRequest(any(URI.class), any(Map.class), any(MediaType.class))) - .thenReturn(rsp); - Mockito.doReturn(rspEntity).when(spy).getResponseEntityAsDocument(any(Response.class), nullable(String.class)); - spy.initCommonFixture(testContext); - // BOX2D(32.5 -117.6, 34 -115) with CRS EPSG:4326 - spy.setExtent(buildEnvelope(1)); - spy.setGetEndpoint(URI.create("http://localhost/csw/v3")); - spy.getBriefRecordsByBBOX(); - } - } - - @Test - public void getBriefRecordsByBBOX_noneIntersect() - throws SAXException, IOException, FactoryException, TransformException { - thrown.expect(AssertionError.class); - thrown.expectMessage("The envelopes do not intersect"); - mockResponse(); - Document rspEntity = docBuilder.parse(this.getClass().getResourceAsStream( - "/rsp/GetRecordsResponse-full.xml")); - BasicSearchTests spy = Mockito.spy(new BasicSearchTests()); - try (MockedStatic clientUtils = Mockito.mockStatic(ClientUtils.class)) { - clientUtils.when(() -> ClientUtils.buildGetRequest(any(URI.class), any(Map.class), any(MediaType.class))) - .thenReturn(rsp); - Mockito.doReturn(rspEntity).when(spy).getResponseEntityAsDocument(any(Response.class), nullable(String.class)); - spy.initCommonFixture(testContext); - // BOX2D(472944 5363287, 516011 5456383) with CRS EPSG:32610 - spy.setExtent(buildEnvelope(2)); - spy.setGetEndpoint(URI.create("http://localhost/csw/v3")); - spy.getBriefRecordsByBBOX(); - } - } - - @Test - public void getSummaryRecordsByWGS84BBOX_noneIntersect() - throws SAXException, IOException, FactoryException, TransformException { - thrown.expect(AssertionError.class); - thrown.expectMessage("The envelopes do not intersect"); - mockResponse(); - Document rspEntity = docBuilder.parse(this.getClass().getResourceAsStream( - "/rsp/GetRecordsResponse-full.xml")); - BasicSearchTests spy = Mockito.spy(new BasicSearchTests()); - try (MockedStatic clientUtils = Mockito.mockStatic(ClientUtils.class)) { - clientUtils.when(() -> ClientUtils.buildGetRequest(any(URI.class), any(Map.class), any(MediaType.class))) - .thenReturn(rsp); - Mockito.doReturn(rspEntity).when(spy).getResponseEntityAsDocument(any(Response.class), nullable(String.class)); - spy.initCommonFixture(testContext); - // BOX2D(472944 5363287, 516011 5456383) with CRS EPSG:32610 - spy.setExtent(buildEnvelope(2)); - spy.setGetEndpoint(URI.create("http://localhost/csw/v3")); - spy.getSummaryRecordsByWGS84BBOX(); - } - } - - private Envelope buildEnvelope(int id) - throws SAXException, IOException, FactoryException, TransformException { - String path = String.format("/rsp/GetRecordsResponse-%d.xml", id); - Document doc = docBuilder.parse(getClass().getResourceAsStream(path)); - NodeList boxNodes = null; - try { - boxNodes = XMLUtils.evaluateXPath(doc, - "//csw:Record/ows:BoundingBox[1] | //csw:Record/ows:WGS84BoundingBox[1]", null); - } catch (XPathExpressionException ex) { // ignore--expression ok - } - return Extents.coalesceBoundingBoxes(XMLUtils.getNodeListAsList(boxNodes)); - } + @Rule + public ExpectedException thrown = ExpectedException.none(); + + private static DocumentBuilder docBuilder; + + private static ITestContext testContext; + + private static Schema cswSchema; + + private static Schema atomSchema; + + @BeforeClass + public static void initTestFixture() throws Exception { + testContext = mock(ITestContext.class); + when(testContext.getSuite()).thenReturn(suite); + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setNamespaceAware(true); + docBuilder = dbf.newDocumentBuilder(); + Document doc = docBuilder.parse(VerifyBasicSearchTests.class.getResourceAsStream("/capabilities/basic.xml")); + when(suite.getAttribute(SuiteAttribute.TEST_SUBJECT.getName())).thenReturn(doc); + cswSchema = ValidationUtils.createCSWSchema(); + when(suite.getAttribute(SuiteAttribute.CSW_SCHEMA.getName())).thenReturn(cswSchema); + atomSchema = ValidationUtils.createAtomSchema(); + when(suite.getAttribute(SuiteAttribute.ATOM_SCHEMA.getName())).thenReturn(atomSchema); + } + + @Test + public void getBriefRecordsByBBOX_allIntersect() + throws SAXException, IOException, FactoryException, TransformException { + mockResponse(); + Document rspEntity = docBuilder.parse(this.getClass().getResourceAsStream("/rsp/GetRecordsResponse-full.xml")); + BasicSearchTests spy = Mockito.spy(new BasicSearchTests()); + try (MockedStatic clientUtils = Mockito.mockStatic(ClientUtils.class)) { + clientUtils.when(() -> ClientUtils.buildGetRequest(any(URI.class), any(Map.class), any(MediaType.class))) + .thenReturn(rsp); + Mockito.doReturn(rspEntity) + .when(spy) + .getResponseEntityAsDocument(any(Response.class), nullable(String.class)); + spy.initCommonFixture(testContext); + // BOX2D(32.5 -117.6, 34 -115) with CRS EPSG:4326 + spy.setExtent(buildEnvelope(1)); + spy.setGetEndpoint(URI.create("http://localhost/csw/v3")); + spy.getBriefRecordsByBBOX(); + } + } + + @Test + public void getBriefRecordsByBBOX_noneIntersect() + throws SAXException, IOException, FactoryException, TransformException { + thrown.expect(AssertionError.class); + thrown.expectMessage("The envelopes do not intersect"); + mockResponse(); + Document rspEntity = docBuilder.parse(this.getClass().getResourceAsStream("/rsp/GetRecordsResponse-full.xml")); + BasicSearchTests spy = Mockito.spy(new BasicSearchTests()); + try (MockedStatic clientUtils = Mockito.mockStatic(ClientUtils.class)) { + clientUtils.when(() -> ClientUtils.buildGetRequest(any(URI.class), any(Map.class), any(MediaType.class))) + .thenReturn(rsp); + Mockito.doReturn(rspEntity) + .when(spy) + .getResponseEntityAsDocument(any(Response.class), nullable(String.class)); + spy.initCommonFixture(testContext); + // BOX2D(472944 5363287, 516011 5456383) with CRS EPSG:32610 + spy.setExtent(buildEnvelope(2)); + spy.setGetEndpoint(URI.create("http://localhost/csw/v3")); + spy.getBriefRecordsByBBOX(); + } + } + + @Test + public void getSummaryRecordsByWGS84BBOX_noneIntersect() + throws SAXException, IOException, FactoryException, TransformException { + thrown.expect(AssertionError.class); + thrown.expectMessage("The envelopes do not intersect"); + mockResponse(); + Document rspEntity = docBuilder.parse(this.getClass().getResourceAsStream("/rsp/GetRecordsResponse-full.xml")); + BasicSearchTests spy = Mockito.spy(new BasicSearchTests()); + try (MockedStatic clientUtils = Mockito.mockStatic(ClientUtils.class)) { + clientUtils.when(() -> ClientUtils.buildGetRequest(any(URI.class), any(Map.class), any(MediaType.class))) + .thenReturn(rsp); + Mockito.doReturn(rspEntity) + .when(spy) + .getResponseEntityAsDocument(any(Response.class), nullable(String.class)); + spy.initCommonFixture(testContext); + // BOX2D(472944 5363287, 516011 5456383) with CRS EPSG:32610 + spy.setExtent(buildEnvelope(2)); + spy.setGetEndpoint(URI.create("http://localhost/csw/v3")); + spy.getSummaryRecordsByWGS84BBOX(); + } + } + + private Envelope buildEnvelope(int id) throws SAXException, IOException, FactoryException, TransformException { + String path = String.format("/rsp/GetRecordsResponse-%d.xml", id); + Document doc = docBuilder.parse(getClass().getResourceAsStream(path)); + NodeList boxNodes = null; + try { + boxNodes = XMLUtils.evaluateXPath(doc, + "//csw:Record/ows:BoundingBox[1] | //csw:Record/ows:WGS84BoundingBox[1]", null); + } + catch (XPathExpressionException ex) { // ignore--expression ok + } + return Extents.coalesceBoundingBoxes(XMLUtils.getNodeListAsList(boxNodes)); + } } diff --git a/src/test/java/org/opengis/cite/cat30/basic/VerifyGetCapabilitiesTests.java b/src/test/java/org/opengis/cite/cat30/basic/VerifyGetCapabilitiesTests.java index 4599e18..fa1f83c 100644 --- a/src/test/java/org/opengis/cite/cat30/basic/VerifyGetCapabilitiesTests.java +++ b/src/test/java/org/opengis/cite/cat30/basic/VerifyGetCapabilitiesTests.java @@ -23,42 +23,42 @@ public class VerifyGetCapabilitiesTests extends TestCommon { - @Rule - public ExpectedException thrown = ExpectedException.none(); - private static final String SUBJ = SuiteAttribute.TEST_SUBJECT.getName(); - private static DocumentBuilder docBuilder; - private static ITestContext testContext; - private static Schema cswSchema; - private static Schema atomSchema; + @Rule + public ExpectedException thrown = ExpectedException.none(); - @BeforeClass - public static void initCommonFixture() throws Exception { - testContext = mock(ITestContext.class); - when(testContext.getSuite()).thenReturn(suite); - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - dbf.setNamespaceAware(true); - docBuilder = dbf.newDocumentBuilder(); - cswSchema = ValidationUtils.createCSWSchema(); - atomSchema = ValidationUtils.createAtomSchema(); - } + private static final String SUBJ = SuiteAttribute.TEST_SUBJECT.getName(); - @Test(expected = ProcessingException.class) - public void getFullCapabilities_noService() throws SAXException, - IOException { - when(suite.getAttribute(SuiteAttribute.CLIENT.getName())) - .thenReturn(client); - Document doc = docBuilder.parse(this.getClass().getResourceAsStream( - "/capabilities/basic.xml")); - when(suite.getAttribute(SUBJ)).thenReturn(doc); - when(suite.getAttribute(SuiteAttribute.CSW_SCHEMA.getName())) - .thenReturn(cswSchema); - when(suite.getAttribute(SuiteAttribute.ATOM_SCHEMA.getName())) - .thenReturn(atomSchema); - GetCapabilitiesTests iut = new GetCapabilitiesTests(); - iut.initCommonFixture(testContext); - iut.clearMessages(); - iut.findServiceEndpoint(); - iut.getFullCapabilitiesAcceptVersion3(); - } + private static DocumentBuilder docBuilder; + + private static ITestContext testContext; + + private static Schema cswSchema; + + private static Schema atomSchema; + + @BeforeClass + public static void initCommonFixture() throws Exception { + testContext = mock(ITestContext.class); + when(testContext.getSuite()).thenReturn(suite); + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setNamespaceAware(true); + docBuilder = dbf.newDocumentBuilder(); + cswSchema = ValidationUtils.createCSWSchema(); + atomSchema = ValidationUtils.createAtomSchema(); + } + + @Test(expected = ProcessingException.class) + public void getFullCapabilities_noService() throws SAXException, IOException { + when(suite.getAttribute(SuiteAttribute.CLIENT.getName())).thenReturn(client); + Document doc = docBuilder.parse(this.getClass().getResourceAsStream("/capabilities/basic.xml")); + when(suite.getAttribute(SUBJ)).thenReturn(doc); + when(suite.getAttribute(SuiteAttribute.CSW_SCHEMA.getName())).thenReturn(cswSchema); + when(suite.getAttribute(SuiteAttribute.ATOM_SCHEMA.getName())).thenReturn(atomSchema); + GetCapabilitiesTests iut = new GetCapabilitiesTests(); + iut.initCommonFixture(testContext); + iut.clearMessages(); + iut.findServiceEndpoint(); + iut.getFullCapabilitiesAcceptVersion3(); + } } diff --git a/src/test/java/org/opengis/cite/cat30/basic/VerifyGetRecordByIdTests.java b/src/test/java/org/opengis/cite/cat30/basic/VerifyGetRecordByIdTests.java index fce6435..95d0d6f 100644 --- a/src/test/java/org/opengis/cite/cat30/basic/VerifyGetRecordByIdTests.java +++ b/src/test/java/org/opengis/cite/cat30/basic/VerifyGetRecordByIdTests.java @@ -39,60 +39,63 @@ public class VerifyGetRecordByIdTests extends TestCommon { - @Rule - public ExpectedException thrown = ExpectedException.none(); - private static DocumentBuilder docBuilder; - private static ITestContext testContext; - private static Schema cswSchema; - private static Schema atomSchema; + @Rule + public ExpectedException thrown = ExpectedException.none(); - @BeforeClass - public static void initTestFixture() throws Exception { - testContext = mock(ITestContext.class); - when(testContext.getSuite()).thenReturn(suite); - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - dbf.setNamespaceAware(true); - docBuilder = dbf.newDocumentBuilder(); - cswSchema = ValidationUtils.createCSWSchema(); - when(suite.getAttribute(SuiteAttribute.CSW_SCHEMA.getName())) - .thenReturn(cswSchema); - atomSchema = ValidationUtils.createAtomSchema(); - when(suite.getAttribute(SuiteAttribute.ATOM_SCHEMA.getName())) - .thenReturn(atomSchema); - } + private static DocumentBuilder docBuilder; - @Test - public void getRecordByIdReturnsInvalidAtomEntry() throws SAXException, - IOException, URISyntaxException { - thrown.expect(AssertionError.class); - thrown.expectMessage("schema validation error(s) detected"); - Document doc = docBuilder.parse(getClass().getResourceAsStream("/capabilities/basic.xml")); - when(suite.getAttribute(SuiteAttribute.TEST_SUBJECT.getName())).thenReturn(doc); - mockResponse(); - Document entity = docBuilder.parse(this.getClass().getResourceAsStream( - "/atom/entry-invalid.xml")); - try (MockedStatic clientUtils = Mockito.mockStatic(ClientUtils.class); -// MockedStatic serviceMetadataUtils = Mockito.mockStatic(ServiceMetadataUtils.class) - ) { -// serviceMetadataUtils.when(() -> ServiceMetadataUtils.getOperationEndpoint( -// nullable(Document.class), anyString(), anyString())) -// .thenReturn(new URI("http://test")); - GetRecordByIdTests spy = Mockito.spy(new GetRecordByIdTests()); - clientUtils.when(() -> ClientUtils.buildGetRequest(nullable(URI.class), any(Map.class), any(MediaType.class))) - .thenReturn(rsp); - Mockito.doReturn(entity).when(spy).getResponseEntityAsDocument(any(Response.class), nullable(String.class)); - spy.initCommonFixture(testContext); - List idList = new ArrayList<>(); - idList.add("id-01"); - spy.setIdList(idList); - spy.getRecordByIdAsAtomEntryUsingAcceptHeader(); - } -// Mockito.doReturn(entity).when(spy).getResponseEntityAsDocument(any(Response.class), anyString()); -// spy.initCommonFixture(testContext); -// List idList = new ArrayList<>(); -// idList.add("id-01"); -// spy.setIdList(idList); -// spy.getRecordByIdAsAtomEntryUsingAcceptHeader(); - } + private static ITestContext testContext; + + private static Schema cswSchema; + + private static Schema atomSchema; + + @BeforeClass + public static void initTestFixture() throws Exception { + testContext = mock(ITestContext.class); + when(testContext.getSuite()).thenReturn(suite); + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setNamespaceAware(true); + docBuilder = dbf.newDocumentBuilder(); + cswSchema = ValidationUtils.createCSWSchema(); + when(suite.getAttribute(SuiteAttribute.CSW_SCHEMA.getName())).thenReturn(cswSchema); + atomSchema = ValidationUtils.createAtomSchema(); + when(suite.getAttribute(SuiteAttribute.ATOM_SCHEMA.getName())).thenReturn(atomSchema); + } + + @Test + public void getRecordByIdReturnsInvalidAtomEntry() throws SAXException, IOException, URISyntaxException { + thrown.expect(AssertionError.class); + thrown.expectMessage("schema validation error(s) detected"); + Document doc = docBuilder.parse(getClass().getResourceAsStream("/capabilities/basic.xml")); + when(suite.getAttribute(SuiteAttribute.TEST_SUBJECT.getName())).thenReturn(doc); + mockResponse(); + Document entity = docBuilder.parse(this.getClass().getResourceAsStream("/atom/entry-invalid.xml")); + try (MockedStatic clientUtils = Mockito.mockStatic(ClientUtils.class); + // MockedStatic serviceMetadataUtils = + // Mockito.mockStatic(ServiceMetadataUtils.class) + ) { + // serviceMetadataUtils.when(() -> ServiceMetadataUtils.getOperationEndpoint( + // nullable(Document.class), anyString(), anyString())) + // .thenReturn(new URI("http://test")); + GetRecordByIdTests spy = Mockito.spy(new GetRecordByIdTests()); + clientUtils + .when(() -> ClientUtils.buildGetRequest(nullable(URI.class), any(Map.class), any(MediaType.class))) + .thenReturn(rsp); + Mockito.doReturn(entity).when(spy).getResponseEntityAsDocument(any(Response.class), nullable(String.class)); + spy.initCommonFixture(testContext); + List idList = new ArrayList<>(); + idList.add("id-01"); + spy.setIdList(idList); + spy.getRecordByIdAsAtomEntryUsingAcceptHeader(); + } + // Mockito.doReturn(entity).when(spy).getResponseEntityAsDocument(any(Response.class), + // anyString()); + // spy.initCommonFixture(testContext); + // List idList = new ArrayList<>(); + // idList.add("id-01"); + // spy.setIdList(idList); + // spy.getRecordByIdAsAtomEntryUsingAcceptHeader(); + } } diff --git a/src/test/java/org/opengis/cite/cat30/basic/VerifyPreconditions.java b/src/test/java/org/opengis/cite/cat30/basic/VerifyPreconditions.java index f94b6c0..fe412f4 100644 --- a/src/test/java/org/opengis/cite/cat30/basic/VerifyPreconditions.java +++ b/src/test/java/org/opengis/cite/cat30/basic/VerifyPreconditions.java @@ -29,39 +29,40 @@ public class VerifyPreconditions { - @Rule - public ExpectedException thrown = ExpectedException.none(); - private static final String SUBJ = SuiteAttribute.TEST_SUBJECT.getName(); - private static DocumentBuilder docBuilder; - private static ITestContext testContext; - private static ISuite suite; + @Rule + public ExpectedException thrown = ExpectedException.none(); - @BeforeClass - public static void initCommonFixture() throws Exception { - testContext = mock(ITestContext.class); - suite = mock(ISuite.class); - when(testContext.getSuite()).thenReturn(suite); - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - dbf.setNamespaceAware(true); - docBuilder = dbf.newDocumentBuilder(); - } + private static final String SUBJ = SuiteAttribute.TEST_SUBJECT.getName(); - @Test - public void verifyTestSubjectIsNotCapabilitiesDoc() throws SAXException, - IOException { - thrown.expect(AssertionError.class); - thrown.expectMessage("Document element in unexpected namespace"); - Document doc = docBuilder.parse(this.getClass().getResourceAsStream( - "/atom/feed.xml")); - when(suite.getAttribute(SUBJ)).thenReturn(doc); - try (MockedStatic reporter = Mockito.mockStatic(Reporter.class)) { - ITestResult testResult = mock(ITestResult.class); - reporter.when(() -> Reporter.getCurrentTestResult()) - .thenReturn(testResult); - when(testResult.getTestContext()).thenReturn(testContext); - SuitePreconditions iut = new SuitePreconditions(); - iut.verifyTestSubject(); - } - } + private static DocumentBuilder docBuilder; + + private static ITestContext testContext; + + private static ISuite suite; + + @BeforeClass + public static void initCommonFixture() throws Exception { + testContext = mock(ITestContext.class); + suite = mock(ISuite.class); + when(testContext.getSuite()).thenReturn(suite); + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setNamespaceAware(true); + docBuilder = dbf.newDocumentBuilder(); + } + + @Test + public void verifyTestSubjectIsNotCapabilitiesDoc() throws SAXException, IOException { + thrown.expect(AssertionError.class); + thrown.expectMessage("Document element in unexpected namespace"); + Document doc = docBuilder.parse(this.getClass().getResourceAsStream("/atom/feed.xml")); + when(suite.getAttribute(SUBJ)).thenReturn(doc); + try (MockedStatic reporter = Mockito.mockStatic(Reporter.class)) { + ITestResult testResult = mock(ITestResult.class); + reporter.when(() -> Reporter.getCurrentTestResult()).thenReturn(testResult); + when(testResult.getTestContext()).thenReturn(testContext); + SuitePreconditions iut = new SuitePreconditions(); + iut.verifyTestSubject(); + } + } } diff --git a/src/test/java/org/opengis/cite/cat30/opensearch/VerifyOpenSearchCoreTests.java b/src/test/java/org/opengis/cite/cat30/opensearch/VerifyOpenSearchCoreTests.java index be80c34..fe55d60 100644 --- a/src/test/java/org/opengis/cite/cat30/opensearch/VerifyOpenSearchCoreTests.java +++ b/src/test/java/org/opengis/cite/cat30/opensearch/VerifyOpenSearchCoreTests.java @@ -35,99 +35,105 @@ import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; -public class VerifyOpenSearchCoreTests extends TestCommon{ - - @Rule - public ExpectedException thrown = ExpectedException.none(); - private static DocumentBuilder docBuilder; - private static ITestContext testContext; - private static Document emptyResponse; - - @BeforeClass - public static void initTestFixture() throws Exception { - testContext = mock(ITestContext.class); - when(testContext.getSuite()).thenReturn(suite); - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - dbf.setNamespaceAware(true); - docBuilder = dbf.newDocumentBuilder(); - Document osd = docBuilder.parse(VerifyOpenSearchCoreTests.class.getResourceAsStream( - "/opensearch/OpenSearchDescription-valid.xml")); - when(suite.getAttribute(SuiteAttribute.OPENSEARCH_DESCR.getName())).thenReturn(osd); - URL dataURL = VerifyOpenSearchCoreTests.class.getResource("/rsp/GetRecordsResponse-full.xml"); - File dataFile = new File(dataURL.toURI()); - DatasetInfo dataInfo = new DatasetInfo(dataFile); - when(suite.getAttribute(SuiteAttribute.DATASET.getName())) - .thenReturn(dataInfo); - Document doc = docBuilder.parse(VerifyOpenSearchCoreTests.class.getResourceAsStream( - "/capabilities/basic.xml")); - when(suite.getAttribute(SuiteAttribute.TEST_SUBJECT.getName())).thenReturn(doc); - Schema cswSchema = ValidationUtils.createCSWSchema(); - when(suite.getAttribute(SuiteAttribute.CSW_SCHEMA.getName())) - .thenReturn(cswSchema); - Schema atomSchema = ValidationUtils.createAtomSchema(); - when(suite.getAttribute(SuiteAttribute.ATOM_SCHEMA.getName())) - .thenReturn(atomSchema); - emptyResponse = docBuilder.parse(VerifyOpenSearchCoreTests.class.getResourceAsStream( - "/rsp/GetRecordsResponse-empty.xml")); - } - - @Test - public void verifyKeywordSearch() throws SAXException, IOException { - mockResponse(); - Document rspEntity = docBuilder.parse(this.getClass().getResourceAsStream( - "/rsp/feed-1.xml")); - OpenSearchCoreTests spy = Mockito.spy(new OpenSearchCoreTests()); - try (MockedStatic clientUtils = Mockito.mockStatic(ClientUtils.class)) { - clientUtils.when(() -> ClientUtils.buildGetRequest(nullable(URI.class), nullable(Map.class), any(MediaType.class))) - .thenReturn(rsp); - spy.initCommonFixture(testContext); - spy.initOpenSearchCoreTestsFixture(testContext); - spy.setSearchTerm("Mona"); - prepareTest(spy); - Mockito.doReturn(rspEntity).when(spy).getResponseEntityAsDocument(any(Response.class), nullable(String.class)); - spy.singleKeywordSearch(); - } - } - - @Test - @Ignore("Call to ETSAssert.assertAllTermsOccur is disabled") - public void verifyKeywordSearchFails() throws SAXException, IOException { - thrown.expect(AssertionError.class); - thrown.expectMessage("rec-1001"); - mockResponse(); - Document rspEntity = docBuilder.parse(this.getClass().getResourceAsStream( - "/rsp/feed-1.xml")); - OpenSearchCoreTests spy = Mockito.spy(new OpenSearchCoreTests()); - try (MockedStatic clientUtils = Mockito.mockStatic(ClientUtils.class)) { - clientUtils.when(() -> ClientUtils.buildGetRequest(nullable(URI.class), nullable(Map.class), any(MediaType.class))) - .thenReturn(rsp); - spy.initCommonFixture(testContext); - spy.initOpenSearchCoreTestsFixture(testContext); - prepareTest(spy); - Mockito.doReturn(rspEntity).when(spy).getResponseEntityAsDocument(any(Response.class), anyString()); - spy.singleKeywordSearch(); - } - } - - @Test - public void executeSampleQuery() throws SAXException, IOException { - mockResponse(); - Document rspEntity = docBuilder.parse(this.getClass().getResourceAsStream( - "/rsp/feed-1.xml")); - OpenSearchCoreTests spy = Mockito.spy(new OpenSearchCoreTests()); - try (MockedStatic clientUtils = Mockito.mockStatic(ClientUtils.class)) { - clientUtils.when(() -> ClientUtils.buildGetRequest(nullable(URI.class), nullable(Map.class), any(MediaType.class))) - .thenReturn(rsp); - spy.initCommonFixture(testContext); - spy.initOpenSearchCoreTestsFixture(testContext); - prepareTest(spy); - Mockito.doReturn(rspEntity).when(spy).getResponseEntityAsDocument(any(Response.class), nullable(String.class)); - spy.executeExampleQueries(); - } - } - - private void prepareTest(OpenSearchCoreTests spy) throws SAXException, IOException { - Mockito.doReturn(emptyResponse).when(spy).getResponseEntityAsDocument(any(Response.class), nullable(String.class)); - spy.keywordSearch_emptyResultSet(); - } +public class VerifyOpenSearchCoreTests extends TestCommon { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + private static DocumentBuilder docBuilder; + + private static ITestContext testContext; + + private static Document emptyResponse; + + @BeforeClass + public static void initTestFixture() throws Exception { + testContext = mock(ITestContext.class); + when(testContext.getSuite()).thenReturn(suite); + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setNamespaceAware(true); + docBuilder = dbf.newDocumentBuilder(); + Document osd = docBuilder + .parse(VerifyOpenSearchCoreTests.class.getResourceAsStream("/opensearch/OpenSearchDescription-valid.xml")); + when(suite.getAttribute(SuiteAttribute.OPENSEARCH_DESCR.getName())).thenReturn(osd); + URL dataURL = VerifyOpenSearchCoreTests.class.getResource("/rsp/GetRecordsResponse-full.xml"); + File dataFile = new File(dataURL.toURI()); + DatasetInfo dataInfo = new DatasetInfo(dataFile); + when(suite.getAttribute(SuiteAttribute.DATASET.getName())).thenReturn(dataInfo); + Document doc = docBuilder.parse(VerifyOpenSearchCoreTests.class.getResourceAsStream("/capabilities/basic.xml")); + when(suite.getAttribute(SuiteAttribute.TEST_SUBJECT.getName())).thenReturn(doc); + Schema cswSchema = ValidationUtils.createCSWSchema(); + when(suite.getAttribute(SuiteAttribute.CSW_SCHEMA.getName())).thenReturn(cswSchema); + Schema atomSchema = ValidationUtils.createAtomSchema(); + when(suite.getAttribute(SuiteAttribute.ATOM_SCHEMA.getName())).thenReturn(atomSchema); + emptyResponse = docBuilder + .parse(VerifyOpenSearchCoreTests.class.getResourceAsStream("/rsp/GetRecordsResponse-empty.xml")); + } + + @Test + public void verifyKeywordSearch() throws SAXException, IOException { + mockResponse(); + Document rspEntity = docBuilder.parse(this.getClass().getResourceAsStream("/rsp/feed-1.xml")); + OpenSearchCoreTests spy = Mockito.spy(new OpenSearchCoreTests()); + try (MockedStatic clientUtils = Mockito.mockStatic(ClientUtils.class)) { + clientUtils + .when(() -> ClientUtils.buildGetRequest(nullable(URI.class), nullable(Map.class), any(MediaType.class))) + .thenReturn(rsp); + spy.initCommonFixture(testContext); + spy.initOpenSearchCoreTestsFixture(testContext); + spy.setSearchTerm("Mona"); + prepareTest(spy); + Mockito.doReturn(rspEntity) + .when(spy) + .getResponseEntityAsDocument(any(Response.class), nullable(String.class)); + spy.singleKeywordSearch(); + } + } + + @Test + @Ignore("Call to ETSAssert.assertAllTermsOccur is disabled") + public void verifyKeywordSearchFails() throws SAXException, IOException { + thrown.expect(AssertionError.class); + thrown.expectMessage("rec-1001"); + mockResponse(); + Document rspEntity = docBuilder.parse(this.getClass().getResourceAsStream("/rsp/feed-1.xml")); + OpenSearchCoreTests spy = Mockito.spy(new OpenSearchCoreTests()); + try (MockedStatic clientUtils = Mockito.mockStatic(ClientUtils.class)) { + clientUtils + .when(() -> ClientUtils.buildGetRequest(nullable(URI.class), nullable(Map.class), any(MediaType.class))) + .thenReturn(rsp); + spy.initCommonFixture(testContext); + spy.initOpenSearchCoreTestsFixture(testContext); + prepareTest(spy); + Mockito.doReturn(rspEntity).when(spy).getResponseEntityAsDocument(any(Response.class), anyString()); + spy.singleKeywordSearch(); + } + } + + @Test + public void executeSampleQuery() throws SAXException, IOException { + mockResponse(); + Document rspEntity = docBuilder.parse(this.getClass().getResourceAsStream("/rsp/feed-1.xml")); + OpenSearchCoreTests spy = Mockito.spy(new OpenSearchCoreTests()); + try (MockedStatic clientUtils = Mockito.mockStatic(ClientUtils.class)) { + clientUtils + .when(() -> ClientUtils.buildGetRequest(nullable(URI.class), nullable(Map.class), any(MediaType.class))) + .thenReturn(rsp); + spy.initCommonFixture(testContext); + spy.initOpenSearchCoreTestsFixture(testContext); + prepareTest(spy); + Mockito.doReturn(rspEntity) + .when(spy) + .getResponseEntityAsDocument(any(Response.class), nullable(String.class)); + spy.executeExampleQueries(); + } + } + + private void prepareTest(OpenSearchCoreTests spy) throws SAXException, IOException { + Mockito.doReturn(emptyResponse) + .when(spy) + .getResponseEntityAsDocument(any(Response.class), nullable(String.class)); + spy.keywordSearch_emptyResultSet(); + } + } diff --git a/src/test/java/org/opengis/cite/cat30/opensearch/VerifyOpenSearchGeoTests.java b/src/test/java/org/opengis/cite/cat30/opensearch/VerifyOpenSearchGeoTests.java index ec57e68..2f61828 100644 --- a/src/test/java/org/opengis/cite/cat30/opensearch/VerifyOpenSearchGeoTests.java +++ b/src/test/java/org/opengis/cite/cat30/opensearch/VerifyOpenSearchGeoTests.java @@ -35,71 +35,74 @@ public class VerifyOpenSearchGeoTests extends TestCommon { - @Rule - public ExpectedException thrown = ExpectedException.none(); - private static DocumentBuilder docBuilder; - private static ITestContext testContext; + @Rule + public ExpectedException thrown = ExpectedException.none(); - @BeforeClass - public static void initTestFixture() throws Exception { - testContext = mock(ITestContext.class); - when(testContext.getSuite()).thenReturn(suite); - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - dbf.setNamespaceAware(true); - docBuilder = dbf.newDocumentBuilder(); - URL dataURL = VerifyOpenSearchGeoTests.class.getResource("/rsp/GetRecordsResponse-full.xml"); - File dataFile = new File(dataURL.toURI()); - DatasetInfo dataInfo = new DatasetInfo(dataFile); - when(suite.getAttribute(SuiteAttribute.DATASET.getName())) - .thenReturn(dataInfo); - Document doc = docBuilder.parse(VerifyOpenSearchGeoTests.class.getResourceAsStream( - "/capabilities/basic.xml")); - when(suite.getAttribute(SuiteAttribute.TEST_SUBJECT.getName())).thenReturn(doc); - Schema cswSchema = ValidationUtils.createCSWSchema(); - when(suite.getAttribute(SuiteAttribute.CSW_SCHEMA.getName())) - .thenReturn(cswSchema); - Schema atomSchema = ValidationUtils.createAtomSchema(); - when(suite.getAttribute(SuiteAttribute.ATOM_SCHEMA.getName())) - .thenReturn(atomSchema); - } + private static DocumentBuilder docBuilder; - @Test - public void boundingBoxQuery_disjoint() throws SAXException, IOException { - thrown.expect(AssertionError.class); - thrown.expectMessage("The envelopes do not intersect"); - Document osd = docBuilder.parse(VerifyOpenSearchGeoTests.class.getResourceAsStream( - "/opensearch/OpenSearchDescription-valid.xml")); - when(suite.getAttribute(SuiteAttribute.OPENSEARCH_DESCR.getName())).thenReturn(osd); - mockResponse(); - Document rspEntity = docBuilder.parse(this.getClass().getResourceAsStream( - "/rsp/feed-1.xml")); - OpenSearchGeoTests spy = Mockito.spy(new OpenSearchGeoTests()); - try (MockedStatic clientUtils = Mockito.mockStatic(ClientUtils.class)) { - clientUtils.when(() -> ClientUtils.buildGetRequest(nullable(URI.class), nullable(Map.class), any(MediaType.class))) - .thenReturn(rsp); - Mockito.doReturn(rspEntity).when(spy).getResponseEntityAsDocument(any(Response.class), nullable(String.class)); - spy.initCommonFixture(testContext); - spy.initOpenSearchGeoTestsFixture(testContext); - spy.boundingBoxQuery(); - } - } + private static ITestContext testContext; + + @BeforeClass + public static void initTestFixture() throws Exception { + testContext = mock(ITestContext.class); + when(testContext.getSuite()).thenReturn(suite); + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setNamespaceAware(true); + docBuilder = dbf.newDocumentBuilder(); + URL dataURL = VerifyOpenSearchGeoTests.class.getResource("/rsp/GetRecordsResponse-full.xml"); + File dataFile = new File(dataURL.toURI()); + DatasetInfo dataInfo = new DatasetInfo(dataFile); + when(suite.getAttribute(SuiteAttribute.DATASET.getName())).thenReturn(dataInfo); + Document doc = docBuilder.parse(VerifyOpenSearchGeoTests.class.getResourceAsStream("/capabilities/basic.xml")); + when(suite.getAttribute(SuiteAttribute.TEST_SUBJECT.getName())).thenReturn(doc); + Schema cswSchema = ValidationUtils.createCSWSchema(); + when(suite.getAttribute(SuiteAttribute.CSW_SCHEMA.getName())).thenReturn(cswSchema); + Schema atomSchema = ValidationUtils.createAtomSchema(); + when(suite.getAttribute(SuiteAttribute.ATOM_SCHEMA.getName())).thenReturn(atomSchema); + } + + @Test + public void boundingBoxQuery_disjoint() throws SAXException, IOException { + thrown.expect(AssertionError.class); + thrown.expectMessage("The envelopes do not intersect"); + Document osd = docBuilder + .parse(VerifyOpenSearchGeoTests.class.getResourceAsStream("/opensearch/OpenSearchDescription-valid.xml")); + when(suite.getAttribute(SuiteAttribute.OPENSEARCH_DESCR.getName())).thenReturn(osd); + mockResponse(); + Document rspEntity = docBuilder.parse(this.getClass().getResourceAsStream("/rsp/feed-1.xml")); + OpenSearchGeoTests spy = Mockito.spy(new OpenSearchGeoTests()); + try (MockedStatic clientUtils = Mockito.mockStatic(ClientUtils.class)) { + clientUtils + .when(() -> ClientUtils.buildGetRequest(nullable(URI.class), nullable(Map.class), any(MediaType.class))) + .thenReturn(rsp); + Mockito.doReturn(rspEntity) + .when(spy) + .getResponseEntityAsDocument(any(Response.class), nullable(String.class)); + spy.initCommonFixture(testContext); + spy.initOpenSearchGeoTestsFixture(testContext); + spy.boundingBoxQuery(); + } + } + + @Test + public void boundingBoxQuery_RSSResponse() throws SAXException, IOException { + Document osd = docBuilder + .parse(VerifyOpenSearchGeoTests.class.getResourceAsStream("/opensearch/OpenSearchDescription-rss.xml")); + when(suite.getAttribute(SuiteAttribute.OPENSEARCH_DESCR.getName())).thenReturn(osd); + mockResponse(); + Document rspEntity = docBuilder.parse(this.getClass().getResourceAsStream("/rsp/rss-1.xml")); + OpenSearchGeoTests spy = Mockito.spy(new OpenSearchGeoTests()); + try (MockedStatic clientUtils = Mockito.mockStatic(ClientUtils.class)) { + clientUtils + .when(() -> ClientUtils.buildGetRequest(nullable(URI.class), nullable(Map.class), any(MediaType.class))) + .thenReturn(rsp); + Mockito.doReturn(rspEntity) + .when(spy) + .getResponseEntityAsDocument(any(Response.class), nullable(String.class)); + spy.initCommonFixture(testContext); + spy.initOpenSearchGeoTestsFixture(testContext); + spy.boundingBoxQuery(); + } + } - @Test - public void boundingBoxQuery_RSSResponse() throws SAXException, IOException { - Document osd = docBuilder.parse(VerifyOpenSearchGeoTests.class.getResourceAsStream( - "/opensearch/OpenSearchDescription-rss.xml")); - when(suite.getAttribute(SuiteAttribute.OPENSEARCH_DESCR.getName())).thenReturn(osd); - mockResponse(); - Document rspEntity = docBuilder.parse(this.getClass().getResourceAsStream( - "/rsp/rss-1.xml")); - OpenSearchGeoTests spy = Mockito.spy(new OpenSearchGeoTests()); - try (MockedStatic clientUtils = Mockito.mockStatic(ClientUtils.class)) { - clientUtils.when(() -> ClientUtils.buildGetRequest(nullable(URI.class), nullable(Map.class), any(MediaType.class))) - .thenReturn(rsp); - Mockito.doReturn(rspEntity).when(spy).getResponseEntityAsDocument(any(Response.class), nullable(String.class)); - spy.initCommonFixture(testContext); - spy.initOpenSearchGeoTestsFixture(testContext); - spy.boundingBoxQuery(); - } - } } diff --git a/src/test/java/org/opengis/cite/cat30/opensearch/VerifyOpenSearchPreconditions.java b/src/test/java/org/opengis/cite/cat30/opensearch/VerifyOpenSearchPreconditions.java index 217fb0b..34f46cf 100644 --- a/src/test/java/org/opengis/cite/cat30/opensearch/VerifyOpenSearchPreconditions.java +++ b/src/test/java/org/opengis/cite/cat30/opensearch/VerifyOpenSearchPreconditions.java @@ -21,45 +21,45 @@ public class VerifyOpenSearchPreconditions { - @Rule - public ExpectedException thrown = ExpectedException.none(); - private static DocumentBuilder docBuilder; - private static ITestContext testContext; - private static ISuite suite; + @Rule + public ExpectedException thrown = ExpectedException.none(); - @BeforeClass - public static void initTestFixture() throws Exception { - testContext = mock(ITestContext.class); - suite = mock(ISuite.class); - when(testContext.getSuite()).thenReturn(suite); - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - dbf.setNamespaceAware(true); - docBuilder = dbf.newDocumentBuilder(); - } + private static DocumentBuilder docBuilder; - @Test - public void declareOpenSearchUsingDefaultValue() throws SAXException, IOException { - CSWClient client = mock(CSWClient.class); - when(client.getOpenSearchDescription(nullable(URI.class))).thenReturn( - docBuilder.newDocument()); - Document doc = docBuilder.parse(this.getClass().getResourceAsStream( - "/capabilities/basic.xml")); - when(suite.getAttribute(SuiteAttribute.TEST_SUBJECT.getName())).thenReturn(doc); - OpenSearchPreconditions iut = new OpenSearchPreconditions(); - iut.setClient(client); - iut.checkOpenSearchImplementationStatus(testContext); - } + private static ITestContext testContext; + + private static ISuite suite; + + @BeforeClass + public static void initTestFixture() throws Exception { + testContext = mock(ITestContext.class); + suite = mock(ISuite.class); + when(testContext.getSuite()).thenReturn(suite); + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setNamespaceAware(true); + docBuilder = dbf.newDocumentBuilder(); + } + + @Test + public void declareOpenSearchUsingDefaultValue() throws SAXException, IOException { + CSWClient client = mock(CSWClient.class); + when(client.getOpenSearchDescription(nullable(URI.class))).thenReturn(docBuilder.newDocument()); + Document doc = docBuilder.parse(this.getClass().getResourceAsStream("/capabilities/basic.xml")); + when(suite.getAttribute(SuiteAttribute.TEST_SUBJECT.getName())).thenReturn(doc); + OpenSearchPreconditions iut = new OpenSearchPreconditions(); + iut.setClient(client); + iut.checkOpenSearchImplementationStatus(testContext); + } + + @Test + public void declareOpenSearchUsingAllowedValue() throws SAXException, IOException { + CSWClient client = mock(CSWClient.class); + when(client.getOpenSearchDescription(nullable(URI.class))).thenReturn(docBuilder.newDocument()); + Document doc = docBuilder.parse(this.getClass().getResourceAsStream("/capabilities/pycsw-cite.xml")); + when(suite.getAttribute(SuiteAttribute.TEST_SUBJECT.getName())).thenReturn(doc); + OpenSearchPreconditions iut = new OpenSearchPreconditions(); + iut.setClient(client); + iut.checkOpenSearchImplementationStatus(testContext); + } - @Test - public void declareOpenSearchUsingAllowedValue() throws SAXException, IOException { - CSWClient client = mock(CSWClient.class); - when(client.getOpenSearchDescription(nullable(URI.class))).thenReturn( - docBuilder.newDocument()); - Document doc = docBuilder.parse(this.getClass().getResourceAsStream( - "/capabilities/pycsw-cite.xml")); - when(suite.getAttribute(SuiteAttribute.TEST_SUBJECT.getName())).thenReturn(doc); - OpenSearchPreconditions iut = new OpenSearchPreconditions(); - iut.setClient(client); - iut.checkOpenSearchImplementationStatus(testContext); - } } diff --git a/src/test/java/org/opengis/cite/cat30/util/VerifyCSWClient.java b/src/test/java/org/opengis/cite/cat30/util/VerifyCSWClient.java index 0add3f9..4d7e935 100644 --- a/src/test/java/org/opengis/cite/cat30/util/VerifyCSWClient.java +++ b/src/test/java/org/opengis/cite/cat30/util/VerifyCSWClient.java @@ -22,24 +22,23 @@ */ public class VerifyCSWClient { - private static DocumentBuilder docBuilder; - - @BeforeClass - public static void initFixture() throws ParserConfigurationException { - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - dbf.setNamespaceAware(true); - docBuilder = dbf.newDocumentBuilder(); - } - - @Test - @Ignore - public void saveFullRecordsAsCsw() throws SAXException, IOException { - CSWClient iut = new CSWClient(); - Document doc = docBuilder.parse(this.getClass().getResourceAsStream( - "/capabilities-pycsw-cite.xml")); - iut.setServiceDescription(doc); - File file = iut.saveFullRecords(10, MediaType.APPLICATION_XML_TYPE); - assertTrue("Response file does not exist: " + file.getAbsolutePath(), - file.exists()); - } + private static DocumentBuilder docBuilder; + + @BeforeClass + public static void initFixture() throws ParserConfigurationException { + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setNamespaceAware(true); + docBuilder = dbf.newDocumentBuilder(); + } + + @Test + @Ignore + public void saveFullRecordsAsCsw() throws SAXException, IOException { + CSWClient iut = new CSWClient(); + Document doc = docBuilder.parse(this.getClass().getResourceAsStream("/capabilities-pycsw-cite.xml")); + iut.setServiceDescription(doc); + File file = iut.saveFullRecords(10, MediaType.APPLICATION_XML_TYPE); + assertTrue("Response file does not exist: " + file.getAbsolutePath(), file.exists()); + } + } diff --git a/src/test/java/org/opengis/cite/cat30/util/VerifyDatasetInfo.java b/src/test/java/org/opengis/cite/cat30/util/VerifyDatasetInfo.java index 6bbe126..3aa952d 100644 --- a/src/test/java/org/opengis/cite/cat30/util/VerifyDatasetInfo.java +++ b/src/test/java/org/opengis/cite/cat30/util/VerifyDatasetInfo.java @@ -19,54 +19,50 @@ */ public class VerifyDatasetInfo { - private static DocumentBuilder docBuilder; + private static DocumentBuilder docBuilder; - public VerifyDatasetInfo() { - } + public VerifyDatasetInfo() { + } - @BeforeClass - public static void setUpClass() throws Exception { - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - dbf.setNamespaceAware(true); - docBuilder = dbf.newDocumentBuilder(); - } + @BeforeClass + public static void setUpClass() throws Exception { + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setNamespaceAware(true); + docBuilder = dbf.newDocumentBuilder(); + } - @Test - public void getGeographicExtent() throws URISyntaxException { - URL url = getClass().getResource("/rsp/GetRecordsResponse-full.xml"); - File dataFile = new File(url.toURI()); - DatasetInfo dataset = new DatasetInfo(dataFile); - Envelope env = dataset.getGeographicExtent(); - assertEquals("Unexpected CRS code", "WGS 84", env.getCoordinateReferenceSystem().getName().getCode()); - assertArrayEquals("Unexpected coords for upper corner.", - new double[]{33.63, -116.00}, - env.getUpperCorner().getCoordinate(), - 0.005); - } + @Test + public void getGeographicExtent() throws URISyntaxException { + URL url = getClass().getResource("/rsp/GetRecordsResponse-full.xml"); + File dataFile = new File(url.toURI()); + DatasetInfo dataset = new DatasetInfo(dataFile); + Envelope env = dataset.getGeographicExtent(); + assertEquals("Unexpected CRS code", "WGS 84", env.getCoordinateReferenceSystem().getName().getCode()); + assertArrayEquals("Unexpected coords for upper corner.", new double[] { 33.63, -116.00 }, + env.getUpperCorner().getCoordinate(), 0.005); + } - @Test - public void getRecordIdentifiers() throws URISyntaxException { - URL url = getClass().getResource("/rsp/GetRecordsResponse-full.xml"); - File dataFile = new File(url.toURI()); - DatasetInfo dataset = new DatasetInfo(dataFile); - List idList = dataset.getRecordIdentifiers(); - assertEquals("Unexpected number of identifiers", 10, idList.size()); - ListIterator listItr = idList.listIterator(idList.size()); - String lastId = listItr.previous(); - assertEquals("Unexpected value for last id.", - "urn:uuid:b1254954-f765-11e1-bf69-aa0000ae6bfc", lastId); - } + @Test + public void getRecordIdentifiers() throws URISyntaxException { + URL url = getClass().getResource("/rsp/GetRecordsResponse-full.xml"); + File dataFile = new File(url.toURI()); + DatasetInfo dataset = new DatasetInfo(dataFile); + List idList = dataset.getRecordIdentifiers(); + assertEquals("Unexpected number of identifiers", 10, idList.size()); + ListIterator listItr = idList.listIterator(idList.size()); + String lastId = listItr.previous(); + assertEquals("Unexpected value for last id.", "urn:uuid:b1254954-f765-11e1-bf69-aa0000ae6bfc", lastId); + } + + @Test + public void calculateExtentUsingHttpCRSReferences() throws URISyntaxException { + URL url = getClass().getResource("/rsp/GetRecordsResponse-3.xml"); + File dataFile = new File(url.toURI()); + DatasetInfo dataset = new DatasetInfo(dataFile); + Envelope env = dataset.getGeographicExtent(); + assertEquals("Unexpected CRS code", "WGS 84", env.getCoordinateReferenceSystem().getName().getCode()); + assertArrayEquals("Unexpected coords for upper corner.", new double[] { 68.41, 17.92 }, + env.getUpperCorner().getCoordinate(), 0.005); + } - @Test - public void calculateExtentUsingHttpCRSReferences() throws URISyntaxException { - URL url = getClass().getResource("/rsp/GetRecordsResponse-3.xml"); - File dataFile = new File(url.toURI()); - DatasetInfo dataset = new DatasetInfo(dataFile); - Envelope env = dataset.getGeographicExtent(); - assertEquals("Unexpected CRS code", "WGS 84", env.getCoordinateReferenceSystem().getName().getCode()); - assertArrayEquals("Unexpected coords for upper corner.", - new double[]{68.41, 17.92}, - env.getUpperCorner().getCoordinate(), - 0.005); - } } diff --git a/src/test/java/org/opengis/cite/cat30/util/VerifyOpenSearchTemplateUtils.java b/src/test/java/org/opengis/cite/cat30/util/VerifyOpenSearchTemplateUtils.java index 9d92667..47b4224 100644 --- a/src/test/java/org/opengis/cite/cat30/util/VerifyOpenSearchTemplateUtils.java +++ b/src/test/java/org/opengis/cite/cat30/util/VerifyOpenSearchTemplateUtils.java @@ -25,43 +25,43 @@ */ public class VerifyOpenSearchTemplateUtils { - private static DocumentBuilder docBuilder; + private static DocumentBuilder docBuilder; - public VerifyOpenSearchTemplateUtils() { - } + public VerifyOpenSearchTemplateUtils() { + } - @BeforeClass - public static void setUpClass() throws Exception { - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - dbf.setNamespaceAware(true); - docBuilder = dbf.newDocumentBuilder(); - } + @BeforeClass + public static void setUpClass() throws Exception { + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setNamespaceAware(true); + docBuilder = dbf.newDocumentBuilder(); + } - @Test - public void buildRequestURIWithBox() throws SAXException, IOException { - Document doc = docBuilder.parse(this.getClass().getResourceAsStream( - "/opensearch/OpenSearchDescription-valid.xml")); - List urlTemplates = ServiceMetadataUtils.getOpenSearchURLTemplates(doc); - Element url1 = (Element) urlTemplates.get(0); - Map values = new HashMap<>(); - values.put(new QName(Namespaces.OSD11, "searchTerms"), "alpha"); - values.put(new QName(Namespaces.OS_GEO, "box"), "-123.45,48.99,-122.45,49.49"); - URI uri = OpenSearchTemplateUtils.buildRequestURI(url1, values); - String query = uri.getQuery(); - assertEquals("q=alpha&pw=1&box=-123.45,48.99,-122.45,49.49&format=atom", query); - } + @Test + public void buildRequestURIWithBox() throws SAXException, IOException { + Document doc = docBuilder + .parse(this.getClass().getResourceAsStream("/opensearch/OpenSearchDescription-valid.xml")); + List urlTemplates = ServiceMetadataUtils.getOpenSearchURLTemplates(doc); + Element url1 = (Element) urlTemplates.get(0); + Map values = new HashMap<>(); + values.put(new QName(Namespaces.OSD11, "searchTerms"), "alpha"); + values.put(new QName(Namespaces.OS_GEO, "box"), "-123.45,48.99,-122.45,49.49"); + URI uri = OpenSearchTemplateUtils.buildRequestURI(url1, values); + String query = uri.getQuery(); + assertEquals("q=alpha&pw=1&box=-123.45,48.99,-122.45,49.49&format=atom", query); + } - @Test - public void buildRequestURIWithIllegalChars() throws SAXException, IOException { - Document doc = docBuilder.parse(this.getClass().getResourceAsStream( - "/opensearch/OpenSearchDescription-id.xml")); - List urlTemplates = ServiceMetadataUtils.getOpenSearchURLTemplates(doc); - Element url1 = (Element) urlTemplates.get(0); - Map values = new HashMap<>(); - String id = "{5d0060fe-d5c4-4307-acc4-e0810c21b6aa}"; - values.put(new QName(Namespaces.OS_GEO, "uid"), URIUtils.getPercentEncodedString(id)); - URI uri = OpenSearchTemplateUtils.buildRequestURI(url1, values); - assertEquals("q=&pw=1&id={5d0060fe-d5c4-4307-acc4-e0810c21b6aa}", uri.getQuery()); - } + @Test + public void buildRequestURIWithIllegalChars() throws SAXException, IOException { + Document doc = docBuilder + .parse(this.getClass().getResourceAsStream("/opensearch/OpenSearchDescription-id.xml")); + List urlTemplates = ServiceMetadataUtils.getOpenSearchURLTemplates(doc); + Element url1 = (Element) urlTemplates.get(0); + Map values = new HashMap<>(); + String id = "{5d0060fe-d5c4-4307-acc4-e0810c21b6aa}"; + values.put(new QName(Namespaces.OS_GEO, "uid"), URIUtils.getPercentEncodedString(id)); + URI uri = OpenSearchTemplateUtils.buildRequestURI(url1, values); + assertEquals("q=&pw=1&id={5d0060fe-d5c4-4307-acc4-e0810c21b6aa}", uri.getQuery()); + } } diff --git a/src/test/java/org/opengis/cite/cat30/util/VerifyRecords.java b/src/test/java/org/opengis/cite/cat30/util/VerifyRecords.java index a000dc0..23dcb58 100644 --- a/src/test/java/org/opengis/cite/cat30/util/VerifyRecords.java +++ b/src/test/java/org/opengis/cite/cat30/util/VerifyRecords.java @@ -17,25 +17,26 @@ */ public class VerifyRecords { - public VerifyRecords() { - } - - @Test - public void findRecordsWithSubject() throws URISyntaxException { - URL url = getClass().getResource("/rsp/GetRecordsResponse-full.xml"); - File dataFile = new File(url.toURI()); - QName subject = new QName(Namespaces.DCMES, "subject"); - XdmValue results = Records.findRecordsInSampleData(dataFile, subject); - assertEquals("Unexpected number of results", 5, results.size()); - } - - @Test - public void findRecordsWithTitleAndLanguage() throws URISyntaxException { - URL url = getClass().getResource("/rsp/GetRecordsResponse-full.xml"); - File dataFile = new File(url.toURI()); - QName title = new QName(Namespaces.DCMES, "title"); - QName lang = new QName(Namespaces.DCMES, "language"); - XdmValue results = Records.findRecordsInSampleData(dataFile, title, lang); - assertEquals("Unexpected number of results", 10, results.size()); - } + public VerifyRecords() { + } + + @Test + public void findRecordsWithSubject() throws URISyntaxException { + URL url = getClass().getResource("/rsp/GetRecordsResponse-full.xml"); + File dataFile = new File(url.toURI()); + QName subject = new QName(Namespaces.DCMES, "subject"); + XdmValue results = Records.findRecordsInSampleData(dataFile, subject); + assertEquals("Unexpected number of results", 5, results.size()); + } + + @Test + public void findRecordsWithTitleAndLanguage() throws URISyntaxException { + URL url = getClass().getResource("/rsp/GetRecordsResponse-full.xml"); + File dataFile = new File(url.toURI()); + QName title = new QName(Namespaces.DCMES, "title"); + QName lang = new QName(Namespaces.DCMES, "language"); + XdmValue results = Records.findRecordsInSampleData(dataFile, title, lang); + assertEquals("Unexpected number of results", 10, results.size()); + } + } diff --git a/src/test/java/org/opengis/cite/cat30/util/VerifyServiceMetadataUtils.java b/src/test/java/org/opengis/cite/cat30/util/VerifyServiceMetadataUtils.java index 9e88cf7..b7235b6 100644 --- a/src/test/java/org/opengis/cite/cat30/util/VerifyServiceMetadataUtils.java +++ b/src/test/java/org/opengis/cite/cat30/util/VerifyServiceMetadataUtils.java @@ -24,66 +24,58 @@ */ public class VerifyServiceMetadataUtils { - private static DocumentBuilder docBuilder; + private static DocumentBuilder docBuilder; - public VerifyServiceMetadataUtils() { - } + public VerifyServiceMetadataUtils() { + } - @BeforeClass - public static void setUpClass() throws Exception { - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - dbf.setNamespaceAware(true); - docBuilder = dbf.newDocumentBuilder(); - } + @BeforeClass + public static void setUpClass() throws Exception { + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setNamespaceAware(true); + docBuilder = dbf.newDocumentBuilder(); + } - @Test - public void getOpenSearchURLTemplates() throws SAXException, IOException { - Document doc = docBuilder.parse(this.getClass().getResourceAsStream( - "/opensearch/OpenSearchDescription-valid.xml")); - List urlTemplates = ServiceMetadataUtils.getOpenSearchURLTemplates(doc); - assertEquals("Unexpected number of URL templates found.", 2, urlTemplates.size()); - Node url1 = urlTemplates.get(0); - Object userData = url1.getUserData(ServiceMetadataUtils.URL_TEMPLATE_PARAMS); - assertNotNull("No user data.", userData); - List templateParams = (List) userData; - TemplateParamInfo param1 = templateParams.get(0); - assertEquals(param1.getName(), new QName(Namespaces.OSD11, "searchTerms")); - assertTrue("searchTerms is required.", param1.isRequired()); - TemplateParamInfo param3 = templateParams.get(2); - assertEquals(param3.getName(), new QName(Namespaces.OS_GEO, "box")); - } + @Test + public void getOpenSearchURLTemplates() throws SAXException, IOException { + Document doc = docBuilder + .parse(this.getClass().getResourceAsStream("/opensearch/OpenSearchDescription-valid.xml")); + List urlTemplates = ServiceMetadataUtils.getOpenSearchURLTemplates(doc); + assertEquals("Unexpected number of URL templates found.", 2, urlTemplates.size()); + Node url1 = urlTemplates.get(0); + Object userData = url1.getUserData(ServiceMetadataUtils.URL_TEMPLATE_PARAMS); + assertNotNull("No user data.", userData); + List templateParams = (List) userData; + TemplateParamInfo param1 = templateParams.get(0); + assertEquals(param1.getName(), new QName(Namespaces.OSD11, "searchTerms")); + assertTrue("searchTerms is required.", param1.isRequired()); + TemplateParamInfo param3 = templateParams.get(2); + assertEquals(param3.getName(), new QName(Namespaces.OS_GEO, "box")); + } - @Test - public void getOpenSearchDescriptionConstraint() throws SAXException, IOException { - Document doc = docBuilder.parse(this.getClass().getResourceAsStream( - "/capabilities/basic.xml")); - Set values = ServiceMetadataUtils.getConstraintValues( - doc, "OpenSearchDescriptionDocument"); - assertEquals("Unexpected number of values.", 1, values.size()); - assertEquals(values.iterator().next(), - "http://www.sdisuite.de/terraCatalog/opensearch"); - } + @Test + public void getOpenSearchDescriptionConstraint() throws SAXException, IOException { + Document doc = docBuilder.parse(this.getClass().getResourceAsStream("/capabilities/basic.xml")); + Set values = ServiceMetadataUtils.getConstraintValues(doc, "OpenSearchDescriptionDocument"); + assertEquals("Unexpected number of values.", 1, values.size()); + assertEquals(values.iterator().next(), "http://www.sdisuite.de/terraCatalog/opensearch"); + } - @Test - public void getAcceptVersionsParamValues() throws SAXException, IOException { - Document doc = docBuilder.parse(this.getClass().getResourceAsStream( - "/capabilities/basic.xml")); - Set versions = ServiceMetadataUtils.getParameterValues( - doc, CAT3.GET_CAPABILITIES, CAT3.ACCEPT_VERSIONS); - assertEquals("Unexpected number of supported versions.", 2, versions.size()); - assertTrue("Expected '3.0.0' as supported version.", - versions.contains("3.0.0")); - } + @Test + public void getAcceptVersionsParamValues() throws SAXException, IOException { + Document doc = docBuilder.parse(this.getClass().getResourceAsStream("/capabilities/basic.xml")); + Set versions = ServiceMetadataUtils.getParameterValues(doc, CAT3.GET_CAPABILITIES, + CAT3.ACCEPT_VERSIONS); + assertEquals("Unexpected number of supported versions.", 2, versions.size()); + assertTrue("Expected '3.0.0' as supported version.", versions.contains("3.0.0")); + } + + @Test + public void getOutputSchemaValuesForGetRecordById() throws SAXException, IOException { + Document doc = docBuilder.parse(this.getClass().getResourceAsStream("/capabilities/basic.xml")); + Set schemas = ServiceMetadataUtils.getParameterValues(doc, CAT3.GET_RECORD_BY_ID, CAT3.OUTPUT_SCHEMA); + assertEquals("Unexpected number of supported versions.", 2, schemas.size()); + assertTrue("Expected Atom as supported outputSchema.", schemas.contains(Namespaces.ATOM)); + } - @Test - public void getOutputSchemaValuesForGetRecordById() - throws SAXException, IOException { - Document doc = docBuilder.parse(this.getClass().getResourceAsStream( - "/capabilities/basic.xml")); - Set schemas = ServiceMetadataUtils.getParameterValues( - doc, CAT3.GET_RECORD_BY_ID, CAT3.OUTPUT_SCHEMA); - assertEquals("Unexpected number of supported versions.", 2, schemas.size()); - assertTrue("Expected Atom as supported outputSchema.", - schemas.contains(Namespaces.ATOM)); - } } diff --git a/src/test/java/org/opengis/cite/cat30/util/VerifySpatialUtils.java b/src/test/java/org/opengis/cite/cat30/util/VerifySpatialUtils.java index e12d14f..6ec9742 100644 --- a/src/test/java/org/opengis/cite/cat30/util/VerifySpatialUtils.java +++ b/src/test/java/org/opengis/cite/cat30/util/VerifySpatialUtils.java @@ -19,57 +19,50 @@ */ public class VerifySpatialUtils { - private static DocumentBuilder docBuilder; + private static DocumentBuilder docBuilder; - public VerifySpatialUtils() { - } + public VerifySpatialUtils() { + } - @BeforeClass - public static void setUpClass() throws Exception { - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - dbf.setNamespaceAware(true); - docBuilder = dbf.newDocumentBuilder(); - } + @BeforeClass + public static void setUpClass() throws Exception { + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setNamespaceAware(true); + docBuilder = dbf.newDocumentBuilder(); + } - @Test - public void createEnvelopeFromGeoRSSBox() throws SAXException, IOException { - Document doc = docBuilder.parse(this.getClass().getResourceAsStream( - "/georss/box.xml")); - Envelope env = SpatialUtils.envelopeFromSimpleGeoRSSBox(doc.getDocumentElement()); - assertEquals("Unexpected CRS code", "WGS 84", env.getCoordinateReferenceSystem().getName().getCode()); - assertArrayEquals("Unexpected coords for upper corner.", - new double[]{33.5, -116.2}, - env.getUpperCorner().getCoordinate(), - 0.05); - } + @Test + public void createEnvelopeFromGeoRSSBox() throws SAXException, IOException { + Document doc = docBuilder.parse(this.getClass().getResourceAsStream("/georss/box.xml")); + Envelope env = SpatialUtils.envelopeFromSimpleGeoRSSBox(doc.getDocumentElement()); + assertEquals("Unexpected CRS code", "WGS 84", env.getCoordinateReferenceSystem().getName().getCode()); + assertArrayEquals("Unexpected coords for upper corner.", new double[] { 33.5, -116.2 }, + env.getUpperCorner().getCoordinate(), 0.05); + } - @Test(expected = IllegalArgumentException.class) - public void createEnvelopeFromBoxWithMissingCorner() throws SAXException, IOException { - Document doc = docBuilder.parse(this.getClass().getResourceAsStream( - "/georss/box-missingCorner.xml")); - Envelope env = SpatialUtils.envelopeFromSimpleGeoRSSBox(doc.getDocumentElement()); - System.out.println(env.toString()); - assertNull(env); - } + @Test(expected = IllegalArgumentException.class) + public void createEnvelopeFromBoxWithMissingCorner() throws SAXException, IOException { + Document doc = docBuilder.parse(this.getClass().getResourceAsStream("/georss/box-missingCorner.xml")); + Envelope env = SpatialUtils.envelopeFromSimpleGeoRSSBox(doc.getDocumentElement()); + System.out.println(env.toString()); + assertNull(env); + } - @Test(expected = NumberFormatException.class) - public void createEnvelopeFromBoxWithNonNumericValue() throws SAXException, IOException { - Document doc = docBuilder.parse(this.getClass().getResourceAsStream( - "/georss/box-NaN.xml")); - Envelope env = SpatialUtils.envelopeFromSimpleGeoRSSBox(doc.getDocumentElement()); - assertNull(env); - } + @Test(expected = NumberFormatException.class) + public void createEnvelopeFromBoxWithNonNumericValue() throws SAXException, IOException { + Document doc = docBuilder.parse(this.getClass().getResourceAsStream("/georss/box-NaN.xml")); + Envelope env = SpatialUtils.envelopeFromSimpleGeoRSSBox(doc.getDocumentElement()); + assertNull(env); + } + + @Test + public void createEnvelopeFromGML31Envelope() throws SAXException, IOException { + Document doc = docBuilder.parse(this.getClass().getResourceAsStream("/Envelope-GML31.xml")); + Element env = SpatialUtils.createGML32Envelope(doc.getDocumentElement()); + assertEquals("Unexpected namespace", Namespaces.GML, env.getNamespaceURI()); + assertEquals("Unexpected srsName", "urn:ogc:def:crs:EPSG::32610", env.getAttribute("srsName")); + String upperCorner = env.getElementsByTagNameNS(Namespaces.GML, "upperCorner").item(0).getTextContent(); + assertTrue("Expected upperCorner ends with 5451619", upperCorner.endsWith("5451619")); + } - @Test - public void createEnvelopeFromGML31Envelope() throws SAXException, IOException { - Document doc = docBuilder.parse(this.getClass().getResourceAsStream( - "/Envelope-GML31.xml")); - Element env = SpatialUtils.createGML32Envelope(doc.getDocumentElement()); - assertEquals("Unexpected namespace", Namespaces.GML, env.getNamespaceURI()); - assertEquals("Unexpected srsName", - "urn:ogc:def:crs:EPSG::32610", env.getAttribute("srsName")); - String upperCorner = env.getElementsByTagNameNS( - Namespaces.GML, "upperCorner").item(0).getTextContent(); - assertTrue("Expected upperCorner ends with 5451619", upperCorner.endsWith("5451619")); - } } diff --git a/src/test/java/org/opengis/cite/cat30/util/VerifyURIUtils.java b/src/test/java/org/opengis/cite/cat30/util/VerifyURIUtils.java index 83dc1e6..90c0509 100644 --- a/src/test/java/org/opengis/cite/cat30/util/VerifyURIUtils.java +++ b/src/test/java/org/opengis/cite/cat30/util/VerifyURIUtils.java @@ -19,56 +19,53 @@ */ public class VerifyURIUtils { - public VerifyURIUtils() { - } + public VerifyURIUtils() { + } - @BeforeClass - public static void setUpClass() { - } + @BeforeClass + public static void setUpClass() { + } - @Ignore - @Test - // comment out @Ignore to run test (requires network connection) - public void resolveHttpUriAsDocument() throws SAXException, IOException { - URI uriRef = URI.create("http://www.w3schools.com/xml/note.xml"); - Document doc = URIUtils.parseURI(uriRef); - assertNotNull(doc); - assertEquals("Document element has unexpected [local name].", - "note", doc.getDocumentElement().getLocalName()); - } + @Ignore + @Test + // comment out @Ignore to run test (requires network connection) + public void resolveHttpUriAsDocument() throws SAXException, IOException { + URI uriRef = URI.create("http://www.w3schools.com/xml/note.xml"); + Document doc = URIUtils.parseURI(uriRef); + assertNotNull(doc); + assertEquals("Document element has unexpected [local name].", "note", doc.getDocumentElement().getLocalName()); + } - @Ignore - @Test - // comment out @Ignore to run test (requires network connection) - public void resolveHttpUriAsFile() throws SAXException, IOException { - URI uriRef = URI.create("http://www.w3schools.com/xml/note.xml"); - File file = URIUtils.dereferenceURI(uriRef); - assertNotNull(file); - assertTrue("File should not be empty", file.length() > 0); - } + @Ignore + @Test + // comment out @Ignore to run test (requires network connection) + public void resolveHttpUriAsFile() throws SAXException, IOException { + URI uriRef = URI.create("http://www.w3schools.com/xml/note.xml"); + File file = URIUtils.dereferenceURI(uriRef); + assertNotNull(file); + assertTrue("File should not be empty", file.length() > 0); + } - @Test - public void resolveClasspathResource() throws SAXException, IOException, - URISyntaxException { - URL url = this.getClass().getResource("/atom/feed.xml"); - Document doc = URIUtils.parseURI(url.toURI()); - assertNotNull(doc); - assertEquals("Document element has unexpected [local name].", - "feed", doc.getDocumentElement().getLocalName()); - } + @Test + public void resolveClasspathResource() throws SAXException, IOException, URISyntaxException { + URL url = this.getClass().getResource("/atom/feed.xml"); + Document doc = URIUtils.parseURI(url.toURI()); + assertNotNull(doc); + assertEquals("Document element has unexpected [local name].", "feed", doc.getDocumentElement().getLocalName()); + } - @Test(expected = IllegalArgumentException.class) - public void resolveMissingClasspathResource() throws SAXException, - URISyntaxException, IOException { - URL url = this.getClass().getResource("/alpha.xml"); - URI uri = (null != url) ? url.toURI() : null; - Document doc = URIUtils.parseURI(uri); - assertNull(doc); - } + @Test(expected = IllegalArgumentException.class) + public void resolveMissingClasspathResource() throws SAXException, URISyntaxException, IOException { + URL url = this.getClass().getResource("/alpha.xml"); + URI uri = (null != url) ? url.toURI() : null; + Document doc = URIUtils.parseURI(uri); + assertNull(doc); + } + + @Test + public void percentEncodeStringWithNonASCIIChar() { + String result = URIUtils.getPercentEncodedString("Ville Montréal"); + assertEquals("Unexpected encoding.", "Ville%20Montr%C3%A9al", result); + } - @Test - public void percentEncodeStringWithNonASCIIChar() { - String result = URIUtils.getPercentEncodedString("Ville Montréal"); - assertEquals("Unexpected encoding.", "Ville%20Montr%C3%A9al", result); - } } diff --git a/src/test/java/org/opengis/cite/cat30/util/VerifyValidationUtils.java b/src/test/java/org/opengis/cite/cat30/util/VerifyValidationUtils.java index 168f2e5..4ab7b17 100644 --- a/src/test/java/org/opengis/cite/cat30/util/VerifyValidationUtils.java +++ b/src/test/java/org/opengis/cite/cat30/util/VerifyValidationUtils.java @@ -23,42 +23,38 @@ */ public class VerifyValidationUtils { - public VerifyValidationUtils() { - } + public VerifyValidationUtils() { + } + + @Test + public void testBuildSchematronValidator() { + String schemaRef = "http://schemas.opengis.net/gml/3.2.1/SchematronConstraints.xml"; + String phase = ""; + SchematronValidator result = ValidationUtils.buildSchematronValidator(schemaRef, phase); + assertNotNull(result); + } + + @Test + public void extractRelativeSchemaReference() throws FileNotFoundException, XMLStreamException { + File xmlFile = new File("src/test/resources/Alpha-1.xml"); + Set xsdSet = ValidationUtils.extractSchemaReferences(new StreamSource(xmlFile), null); + URI schemaURI = xsdSet.iterator().next(); + assertTrue("Expected schema reference */xsd/alpha.xsd", schemaURI.toString().endsWith("/xsd/alpha.xsd")); + } + + @Test + public void buildAtomSchema() { + Schema schema = ValidationUtils.createAtomSchema(); + assertNotNull("Failed to construct Atom Schema", schema); + } + + @Test + public void validateOpenSearchDescription() throws SAXException, IOException { + Schema schema = ValidationUtils.createOpenSearchSchema(); + assertNotNull("Failed to build OpenSearch Schema", schema); + Validator validator = schema.newValidator(); + InputStream inStream = getClass().getResourceAsStream("/opensearch/OpenSearchDescription-valid.xml"); + validator.validate(new StreamSource(inStream)); + } - @Test - public void testBuildSchematronValidator() { - String schemaRef = "http://schemas.opengis.net/gml/3.2.1/SchematronConstraints.xml"; - String phase = ""; - SchematronValidator result = ValidationUtils.buildSchematronValidator( - schemaRef, phase); - assertNotNull(result); - } - - @Test - public void extractRelativeSchemaReference() throws FileNotFoundException, - XMLStreamException { - File xmlFile = new File("src/test/resources/Alpha-1.xml"); - Set xsdSet = ValidationUtils.extractSchemaReferences( - new StreamSource(xmlFile), null); - URI schemaURI = xsdSet.iterator().next(); - assertTrue("Expected schema reference */xsd/alpha.xsd", schemaURI - .toString().endsWith("/xsd/alpha.xsd")); - } - - @Test - public void buildAtomSchema() { - Schema schema = ValidationUtils.createAtomSchema(); - assertNotNull("Failed to construct Atom Schema", schema); - } - - @Test - public void validateOpenSearchDescription() throws SAXException, IOException { - Schema schema = ValidationUtils.createOpenSearchSchema(); - assertNotNull("Failed to build OpenSearch Schema", schema); - Validator validator = schema.newValidator(); - InputStream inStream = getClass().getResourceAsStream( - "/opensearch/OpenSearchDescription-valid.xml"); - validator.validate(new StreamSource(inStream)); - } } diff --git a/src/test/java/org/opengis/cite/cat30/util/VerifyXMLUtils.java b/src/test/java/org/opengis/cite/cat30/util/VerifyXMLUtils.java index b7262b8..7733a59 100644 --- a/src/test/java/org/opengis/cite/cat30/util/VerifyXMLUtils.java +++ b/src/test/java/org/opengis/cite/cat30/util/VerifyXMLUtils.java @@ -28,135 +28,120 @@ */ public class VerifyXMLUtils { - private static final String ATOM_NS = "http://www.w3.org/2005/Atom"; - private static final String EX_NS = "http://example.org/ns1"; - private static DocumentBuilder docBuilder; - - public VerifyXMLUtils() { - } - - @BeforeClass - public static void setUpClass() throws Exception { - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - dbf.setNamespaceAware(true); - docBuilder = dbf.newDocumentBuilder(); - } - - @Test - public void writeDocToString() throws SAXException, IOException { - Document doc = docBuilder.parse(this.getClass().getResourceAsStream( - "/atom/feed.xml")); - String content = XMLUtils.writeNodeToString(doc); - assertTrue("String should start with ' nsBindings = new HashMap(); - nsBindings.put(ATOM_NS, "tns"); - nsBindings.put(EX_NS, "ns1"); - NodeList results = XMLUtils.evaluateXPath(doc, expr, nsBindings); - assertTrue("Expected 1 node in results.", - results.getLength() == 1); - assertEquals("author", results.item(0).getLocalName()); - } - - @Test - public void evaluateXPathExpression_noMatch() - throws XPathExpressionException, SAXException, IOException { - Document doc = docBuilder.parse(this.getClass().getResourceAsStream( - "/atom/feed.xml")); - String expr = "/tns:feed/tns:author[ns1:blog]"; - Map nsBindings = new HashMap(); - nsBindings.put(ATOM_NS, "tns"); - nsBindings.put(EX_NS, "ns1"); - NodeList results = XMLUtils.evaluateXPath(doc, expr, nsBindings); - assertTrue("Expected empty results.", results.getLength() == 0); - } - - @Test(expected = XPathExpressionException.class) - public void evaluateXPathExpression_booleanResult() - throws XPathExpressionException, SAXException, IOException { - Document doc = docBuilder.parse(this.getClass().getResourceAsStream( - "/atom/feed.xml")); - String expr = "count(//tns:entry) > 0"; - Map nsBindings = new HashMap(); - nsBindings.put(ATOM_NS, "tns"); - NodeList results = XMLUtils.evaluateXPath(doc, expr, nsBindings); - assertNull(results); - } - - @Test - public void createElement_Alpha() { - QName qName = new QName("http://example.org", "Alpha"); - Element elem = XMLUtils.createElement(qName); - assertEquals("Alpha", elem.getLocalName()); - assertNull(elem.getParentNode()); - assertNotNull(elem.getOwnerDocument()); - } - - @Test - public void evaluateXPath2ExpressionAgainstDocument() throws SAXException, - IOException, SaxonApiException, XPathException { - Document doc = docBuilder.parse(this.getClass().getResourceAsStream( - "/atom/feed.xml")); - String expr = "matches(//tns:entry/tns:title, '.*Robots')"; - Map nsBindings = new HashMap(); - nsBindings.put(ATOM_NS, "tns"); - XdmValue result = XMLUtils.evaluateXPath2(new DOMSource(doc), expr, - nsBindings); - assertTrue("Expected non-empty result.", result.size() > 0); - assertEquals("Result has unexpected string value.", "true", - result.getUnderlyingValue().getStringValue()); - } - - @Test - public void evaluateXPath2ExpressionAgainstElement() throws SAXException, - IOException, SaxonApiException, XPathException { - Document doc = docBuilder.parse(this.getClass().getResourceAsStream( - "/atom/feed.xml")); - Node entry = doc.getElementsByTagNameNS(ATOM_NS, "entry").item(0); - String expr = "matches(tns:title, '.*Robots')"; - Map nsBindings = new HashMap(); - nsBindings.put(ATOM_NS, "tns"); - XdmValue result = XMLUtils.evaluateXPath2(new DOMSource(entry), expr, - nsBindings); - assertTrue("Expected non-empty result.", result.size() > 0); - assertEquals("Result has unexpected string value.", "true", - result.getUnderlyingValue().getStringValue()); - } - - @Test - public void expandCharacterEntity() { - String text = "Ce n'est pas"; - String result = XMLUtils.expandReferencesInText(text); - assertTrue("Expected result to contain an apostrophe (')", - result.contains("'")); - } - - @Test - public void expandNumericCharacterReference() { - String text = "Montréal"; - String result = XMLUtils.expandReferencesInText(text); - assertEquals("Expected result to contain character é (U+00E9)", - "Montréal", result); - } - - @Test - public void evaluateXQuery() throws SAXException, IOException, SaxonApiException { - Document doc = docBuilder.parse(this.getClass().getResourceAsStream( - "/atom/feed.xml")); - String query = "for $e in //atom:entry/child::* where $e[@* = 'robotics'] return $e"; - Map nsBindings = new HashMap<>(); - nsBindings.put(ATOM_NS, "atom"); - XdmValue results = XMLUtils.evaluateXQuery(new DOMSource(doc), query, nsBindings); - assertTrue("Expected non-empty results.", results.size() > 0); - XdmNode node = (XdmNode) results.iterator().next(); - assertEquals("Unexpected local name", "category", node.getNodeName().getLocalName()); - } + private static final String ATOM_NS = "http://www.w3.org/2005/Atom"; + + private static final String EX_NS = "http://example.org/ns1"; + + private static DocumentBuilder docBuilder; + + public VerifyXMLUtils() { + } + + @BeforeClass + public static void setUpClass() throws Exception { + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setNamespaceAware(true); + docBuilder = dbf.newDocumentBuilder(); + } + + @Test + public void writeDocToString() throws SAXException, IOException { + Document doc = docBuilder.parse(this.getClass().getResourceAsStream("/atom/feed.xml")); + String content = XMLUtils.writeNodeToString(doc); + assertTrue("String should start with ' nsBindings = new HashMap(); + nsBindings.put(ATOM_NS, "tns"); + nsBindings.put(EX_NS, "ns1"); + NodeList results = XMLUtils.evaluateXPath(doc, expr, nsBindings); + assertTrue("Expected 1 node in results.", results.getLength() == 1); + assertEquals("author", results.item(0).getLocalName()); + } + + @Test + public void evaluateXPathExpression_noMatch() throws XPathExpressionException, SAXException, IOException { + Document doc = docBuilder.parse(this.getClass().getResourceAsStream("/atom/feed.xml")); + String expr = "/tns:feed/tns:author[ns1:blog]"; + Map nsBindings = new HashMap(); + nsBindings.put(ATOM_NS, "tns"); + nsBindings.put(EX_NS, "ns1"); + NodeList results = XMLUtils.evaluateXPath(doc, expr, nsBindings); + assertTrue("Expected empty results.", results.getLength() == 0); + } + + @Test(expected = XPathExpressionException.class) + public void evaluateXPathExpression_booleanResult() throws XPathExpressionException, SAXException, IOException { + Document doc = docBuilder.parse(this.getClass().getResourceAsStream("/atom/feed.xml")); + String expr = "count(//tns:entry) > 0"; + Map nsBindings = new HashMap(); + nsBindings.put(ATOM_NS, "tns"); + NodeList results = XMLUtils.evaluateXPath(doc, expr, nsBindings); + assertNull(results); + } + + @Test + public void createElement_Alpha() { + QName qName = new QName("http://example.org", "Alpha"); + Element elem = XMLUtils.createElement(qName); + assertEquals("Alpha", elem.getLocalName()); + assertNull(elem.getParentNode()); + assertNotNull(elem.getOwnerDocument()); + } + + @Test + public void evaluateXPath2ExpressionAgainstDocument() + throws SAXException, IOException, SaxonApiException, XPathException { + Document doc = docBuilder.parse(this.getClass().getResourceAsStream("/atom/feed.xml")); + String expr = "matches(//tns:entry/tns:title, '.*Robots')"; + Map nsBindings = new HashMap(); + nsBindings.put(ATOM_NS, "tns"); + XdmValue result = XMLUtils.evaluateXPath2(new DOMSource(doc), expr, nsBindings); + assertTrue("Expected non-empty result.", result.size() > 0); + assertEquals("Result has unexpected string value.", "true", result.getUnderlyingValue().getStringValue()); + } + + @Test + public void evaluateXPath2ExpressionAgainstElement() + throws SAXException, IOException, SaxonApiException, XPathException { + Document doc = docBuilder.parse(this.getClass().getResourceAsStream("/atom/feed.xml")); + Node entry = doc.getElementsByTagNameNS(ATOM_NS, "entry").item(0); + String expr = "matches(tns:title, '.*Robots')"; + Map nsBindings = new HashMap(); + nsBindings.put(ATOM_NS, "tns"); + XdmValue result = XMLUtils.evaluateXPath2(new DOMSource(entry), expr, nsBindings); + assertTrue("Expected non-empty result.", result.size() > 0); + assertEquals("Result has unexpected string value.", "true", result.getUnderlyingValue().getStringValue()); + } + + @Test + public void expandCharacterEntity() { + String text = "Ce n'est pas"; + String result = XMLUtils.expandReferencesInText(text); + assertTrue("Expected result to contain an apostrophe (')", result.contains("'")); + } + + @Test + public void expandNumericCharacterReference() { + String text = "Montréal"; + String result = XMLUtils.expandReferencesInText(text); + assertEquals("Expected result to contain character é (U+00E9)", "Montréal", result); + } + + @Test + public void evaluateXQuery() throws SAXException, IOException, SaxonApiException { + Document doc = docBuilder.parse(this.getClass().getResourceAsStream("/atom/feed.xml")); + String query = "for $e in //atom:entry/child::* where $e[@* = 'robotics'] return $e"; + Map nsBindings = new HashMap<>(); + nsBindings.put(ATOM_NS, "atom"); + XdmValue results = XMLUtils.evaluateXQuery(new DOMSource(doc), query, nsBindings); + assertTrue("Expected non-empty results.", results.size() > 0); + XdmNode node = (XdmNode) results.iterator().next(); + assertEquals("Unexpected local name", "category", node.getNodeName().getLocalName()); + } + }