diff --git a/build.gradle b/build.gradle index 46e4e76..d33fbae 100644 --- a/build.gradle +++ b/build.gradle @@ -42,6 +42,8 @@ dependencies { compile 'com.networknt:json-schema-validator:1.0.35' + implementation 'org.mockito:mockito-core:3.3.3' + } configurations { @@ -91,7 +93,8 @@ task testWebApiServer_1_0_2_Gold() { 'org.reso.certification.stepdefs#WebAPIServer_1_0_2', 'src/main/java/org/reso/certification/features/web-api', '--tags', - (systemProperties.get('cucumber.filter.tags', '').toString().trim().length() > 0 ? 'not @platinum and ' + systemProperties.get('cucumber.filter.tags') : 'not @platinum') + (systemProperties.get('cucumber.filter.tags', '').toString().trim().length() > 0 + ? 'not @platinum and ' + systemProperties.get('cucumber.filter.tags') : 'not @platinum') ] } } @@ -141,7 +144,8 @@ task testDataDictionary_1_5() { 'org.reso.certification.stepdefs#DataDictionary', 'src/main/java/org/reso/certification/features/data-dictionary', '--tags', - (systemProperties.get('cucumber.filter.tags', '').toString().trim().length() > 0 ? '@DD1.5 and ' + systemProperties.get('cucumber.filter.tags') : '@DD1.5') + (systemProperties.get('cucumber.filter.tags', '').toString().trim().length() > 0 + ? '@DD1.5 and ' + systemProperties.get('cucumber.filter.tags') : '@DD1.5') ] } } @@ -167,7 +171,27 @@ task testDataDictionary_1_6() { 'org.reso.certification.stepdefs#DataDictionary', 'src/main/java/org/reso/certification/features/data-dictionary', '--tags', - (systemProperties.get('cucumber.filter.tags', '').toString().trim().length() > 0 ? '@DD1.6 and ' + systemProperties.get('cucumber.filter.tags') : '@DD1.6') + (systemProperties.get('cucumber.filter.tags', '').toString().trim().length() > 0 + ? '@DD1.6 and ' + systemProperties.get('cucumber.filter.tags') : '@DD1.6') + ] + } + } +} + +//used for internal Commander testing +test { + dependsOn assemble, compileTestJava + doLast { + javaexec { + main = "io.cucumber.core.cli.Main" + systemProperties = System.getProperties() + classpath = configurations.cucumberRuntime + sourceSets.main.output + sourceSets.test.output + args = ['--strict', + '--plugin', + 'pretty', + '--glue', + 'org.reso.commander.test.stepdefs', + 'src/test/java/org/reso/commander/test/features' ] } } diff --git a/build/libs/web-api-commander.jar b/build/libs/web-api-commander.jar index 29b509f..7b33bf9 100644 Binary files a/build/libs/web-api-commander.jar and b/build/libs/web-api-commander.jar differ diff --git a/src/main/java/org/reso/commander/Commander.java b/src/main/java/org/reso/commander/Commander.java index 481a6be..43fe92d 100644 --- a/src/main/java/org/reso/commander/Commander.java +++ b/src/main/java/org/reso/commander/Commander.java @@ -357,6 +357,16 @@ public class Commander { * @return true if the metadata is valid, meaning that it's also a valid OData 4 Service Document */ public boolean validateMetadata(XMLMetadata metadata) { + return validateMetadata(metadata, client); + } + + /** + * Static version of the metadata validator that can work with a given client + * @param metadata the XML Metadata to validate + * @param client the OData client to use for validation + * @return true if the given XML metadata is valid, false otherwise + */ + public static boolean validateMetadata(XMLMetadata metadata, ODataClient client) { try { // call the probably-useless metadata validator. can't hurt though // SEE: https://github.com/apache/olingo-odata4/blob/master/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/ODataMetadataValidationImpl.java#L77-L116 @@ -383,10 +393,20 @@ public class Commander { * @return true if the metadata is valid, meaning that it's also a valid OData 4 Service Document */ public boolean validateMetadata(Edm metadata) { + return validateMetadata(metadata, client); + } + + /** + * Static version of the metadata validator that can work with a given client + * @param edm the Edm to validate + * @param client the OData client to use for validation + * @return true if the given XML metadata is valid, false otherwise + */ + public static boolean validateMetadata(Edm edm, ODataClient client) { try { // call the probably-useless metadata validator. can't hurt though // SEE: https://github.com/apache/olingo-odata4/blob/master/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/ODataMetadataValidationImpl.java#L77-L116 - client.metadataValidation().validateMetadata(metadata); + client.metadataValidation().validateMetadata(edm); //if Edm metadata are invalid, the previous line will throw an exception and this line won't be reached. return true; } catch (NullPointerException nex) { @@ -406,7 +426,7 @@ public class Commander { * @param inputStream the input stream containing the metadata to validate. * @return true if the given input stream contains valid XML Metadata, false otherwise. */ - public boolean validateMetadata(InputStream inputStream) { + public boolean validateXMLAndXMLMetadata(InputStream inputStream) { try { String xmlString = TestUtils.convertInputStreamToString(inputStream); @@ -429,6 +449,31 @@ public class Commander { return false; } + /** + * Deserializes XML Metadata from a string + * @param xmlMetadataString a string containing XML Metadata + * @param client an instance of an OData Client + * @return the XML Metadata contained within the string + */ + public static XMLMetadata deserializeXMLMetadata(String xmlMetadataString, ODataClient client) { + //deserialize response into XML Metadata - will throw an exception if metadata are invalid + return client.getDeserializer(ContentType.APPLICATION_XML) + .toMetadata(new ByteArrayInputStream(xmlMetadataString.getBytes(StandardCharsets.UTF_8))); + } + + /** + * Deserializes Edm from XML Metadata + * @param xmlMetadataString a string containing XML metadata + * @param client an instance of an OData Client + * @return the Edm contained within the xmlMetadataString + * + * TODO: rewrite the separate Edm request in the Web API server test code to only make one request and convert + * to Edm from the XML Metadata that was received. + */ + public static Edm deserializeEdm(String xmlMetadataString, ODataClient client) { + return client.getReader().readMetadata(new ByteArrayInputStream(xmlMetadataString.getBytes(StandardCharsets.UTF_8))); + } + /** * Validates the given metadata contained in the given file path. * @@ -437,7 +482,7 @@ public class Commander { */ public boolean validateMetadata(String pathToEdmx) { try { - return validateMetadata(new FileInputStream(pathToEdmx)); + return validateXMLAndXMLMetadata(new FileInputStream(pathToEdmx)); } catch (Exception ex) { LOG.error("ERROR: could not validate metadata.\nPath was:" + pathToEdmx); LOG.error(ex.getMessage()); diff --git a/src/main/java/org/reso/commander/certfication/containers/WebApiTestContainer.java b/src/main/java/org/reso/commander/certfication/containers/WebApiTestContainer.java index 1622894..092a8fe 100644 --- a/src/main/java/org/reso/commander/certfication/containers/WebApiTestContainer.java +++ b/src/main/java/org/reso/commander/certfication/containers/WebApiTestContainer.java @@ -22,14 +22,13 @@ import org.reso.models.Parameters; import org.reso.models.Request; import org.reso.models.Settings; -import java.io.ByteArrayInputStream; import java.net.URI; -import java.nio.charset.StandardCharsets; import java.util.*; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; -import static org.junit.Assert.*; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; import static org.reso.commander.Commander.AMPERSAND; import static org.reso.commander.Commander.EQUALS; import static org.reso.commander.common.ErrorMsg.getDefaultErrorMessage; @@ -300,12 +299,7 @@ public final class WebApiTestContainer implements TestContainer { setServerODataHeaderVersion(TestUtils.getHeaderData(HEADER_ODATA_VERSION, response)); xmlResponseData.set(TestUtils.convertInputStreamToString(response.getRawResponse())); - - //deserialize response into XML Metadata - will throw an exception if metadata are in valid - XMLMetadata metadata = getCommander().getClient().getDeserializer(ContentType.APPLICATION_XML) - .toMetadata(new ByteArrayInputStream(xmlResponseData.get().getBytes(StandardCharsets.UTF_8))); - - xmlMetadata.set(metadata); + xmlMetadata.set(Commander.deserializeXMLMetadata(xmlResponseData.get(), getCommander().getClient())); } catch (Exception ex) { processODataRequestException(ex); } finally { diff --git a/src/test/java/org/reso/commander/AppTest.java b/src/test/java/org/reso/commander/AppTest.java deleted file mode 100644 index 086d567..0000000 --- a/src/test/java/org/reso/commander/AppTest.java +++ /dev/null @@ -1,12 +0,0 @@ -/* - * This Java source file was generated by the Gradle 'init' task. - */ -package org.reso.commander; - -import org.junit.Test; - -public class AppTest { - @Test public void testParameterDeserialization() { - //TODO - } -} diff --git a/src/test/java/org/reso/commander/test/features/xml-and-metadata-validation.feature b/src/test/java/org/reso/commander/test/features/xml-and-metadata-validation.feature new file mode 100644 index 0000000..4273da8 --- /dev/null +++ b/src/test/java/org/reso/commander/test/features/xml-and-metadata-validation.feature @@ -0,0 +1,46 @@ +Feature: Web API 1.0.2 Server - XML and Metadata Validation + + Background: + Given an OData test client has been created + + + ####################################### + # XML Validation Tests + ####################################### + Scenario: XML Validation using OASIS reference XSDs + Given data were loaded from the sample resource "good.edmx-and-edm.xml" + When XML validation is performed on the resource data + Then XML validation succeeds + + Scenario: XML Validation fails when XML are malformed + Given data were loaded from the sample resource "bad.edmx-unparsable-xml.xml" + When XML validation is performed on the resource data + Then XML validation fails + + + ####################################### + # XML Metadata Validation Tests + ####################################### + Scenario: XML Metadata validation succeeds when XML Metadata are valid + Given data were loaded from the sample resource "good.edmx-and-edm.xml" + When XML Metadata validation is performed on the resource data + Then XML Metadata validation succeeds + + Scenario: XML Validation fails when XML Metadata are missing Key element in EntityType definition + Given data were loaded from the sample resource "bad.edmx-no-keyfield.xml" + When XML Metadata validation is performed on the resource data + Then XML Metadata validation fails + + + ####################################### + # Edm Validation Tests + ####################################### + Scenario: Edm validation succeeds when XML Metadata contain a valid Edm + Given data were loaded from the sample resource "good.edmx-and-edm.xml" + When Edm validation is performed on the resource data + Then Edm Metadata validation succeeds + + Scenario: Edm validation fails when XML Metadata don't contain a valid service document + Given data were loaded from the sample resource "bad.edmx-wrong-edm-binding-target.xml" + When Edm validation is performed on the resource data + Then Edm Metadata validation fails \ No newline at end of file diff --git a/src/test/java/org/reso/commander/test/stepdefs/XMLAndMetadataValidation.java b/src/test/java/org/reso/commander/test/stepdefs/XMLAndMetadataValidation.java new file mode 100644 index 0000000..5b8d922 --- /dev/null +++ b/src/test/java/org/reso/commander/test/stepdefs/XMLAndMetadataValidation.java @@ -0,0 +1,113 @@ +package org.reso.commander.test.stepdefs; + +import io.cucumber.java8.En; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.olingo.client.api.edm.xml.XMLMetadata; +import org.apache.olingo.commons.api.edm.Edm; +import org.reso.commander.Commander; +import org.reso.commander.common.TestUtils; + +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + +import static org.junit.Assert.*; +import static org.reso.commander.common.ErrorMsg.getDefaultErrorMessage; + +public class XMLAndMetadataValidation implements En { + private static final Logger LOG = LogManager.getLogger(XMLAndMetadataValidation.class); + AtomicReference xmlMetadataString = new AtomicReference<>(); + AtomicReference xmlMetadata = new AtomicReference<>(); + AtomicReference edm = new AtomicReference<>(); + AtomicReference commander = new AtomicReference<>(); + + //state variables + AtomicBoolean isXMLValid = new AtomicBoolean(false); + AtomicBoolean isXMLMetadataValid = new AtomicBoolean(false); + AtomicBoolean isEdmValid = new AtomicBoolean(false); + + public XMLAndMetadataValidation() { + //background + runBackground(); + + /* + * loads a test resource to a local string object by name + */ + Given("^data were loaded from the sample resource \"([^\"]*)\"$", (String resourceName) -> { + assertNotNull(getDefaultErrorMessage("ERROR: resource name cannot be null!", resourceName)); + + try { + xmlMetadataString.set(TestUtils.convertInputStreamToString(getClass().getClassLoader().getResourceAsStream(resourceName))); + assertNotNull("ERROR: no string data was loaded from the given resource!", xmlMetadataString.get()); + } catch (Exception ex) { + LOG.error("An exception was thrown trying to load the given resource name: " + resourceName); + LOG.error(ex); + } + }); + + + /* + * XML validation + */ + When("^XML validation is performed on the resource data$", () -> { + assertNotNull(getDefaultErrorMessage("resource data is null!", xmlMetadataString.get())); + isXMLValid.set(Commander.validateXML(xmlMetadataString.get())); + }); + Then("^XML validation succeeds$", () -> { + assertTrue(getDefaultErrorMessage("expected XML validation to succeed but it failed!"), isXMLValid.get()); + }); + Then("^XML validation fails$", () -> { + assertFalse(getDefaultErrorMessage("expected XML validation to succeed but it failed!"), isXMLValid.get()); + }); + + + /* + * XML Metadata validation + */ + When("^XML Metadata validation is performed on the resource data$", () -> { + assertNotNull(getDefaultErrorMessage("resource data is null!", xmlMetadataString.get())); + + try { + xmlMetadata.set(Commander.deserializeXMLMetadata(xmlMetadataString.get(), commander.get().getClient())); + assertNotNull(getDefaultErrorMessage("XML Metadata cannot be null!"), xmlMetadata.get()); + isXMLMetadataValid.set(Commander.validateMetadata(xmlMetadata.get(), commander.get().getClient())); + } catch (Exception ex) { + fail(getDefaultErrorMessage("could not deserialize XML Metadata!")); + } + }); + Then("^XML Metadata validation succeeds$", () -> { + assertTrue(getDefaultErrorMessage("expected XML validation to succeed but it failed!"), isXMLMetadataValid.get()); + }); + Then("^XML Metadata validation fails$", () -> { + assertFalse(getDefaultErrorMessage("expected XML validation to fail but it succeeded!"), isXMLMetadataValid.get()); + }); + + + /* + * Edm validation + */ + When("^Edm validation is performed on the resource data$", () -> { + try { + assertNotNull(getDefaultErrorMessage("resource data is null!", xmlMetadataString.get())); + edm.set(Commander.deserializeEdm(xmlMetadataString.get(), commander.get().getClient())); + isEdmValid.set(Commander.validateMetadata(edm.get(), commander.get().getClient())); + } catch (Exception ex) { + fail(getDefaultErrorMessage(ex)); + } + + }); + Then("^Edm Metadata validation succeeds$", () -> { + assertTrue(getDefaultErrorMessage("expected Entity Data Model (Edm) to succeed but it failed!"), isEdmValid.get()); + }); + Then("^Edm Metadata validation fails$", () -> { + assertFalse(getDefaultErrorMessage("expected Entity Data Model (Edm) to fail but it succeeded!"), isEdmValid.get()); + }); + + } + + private void runBackground() { + Given("^an OData test client has been created$", () -> { + commander.set(Commander.Builder.class.newInstance().build()); + }); + } +} diff --git a/src/test/resources/bad.edmx-no-keyfield.xml b/src/test/resources/bad.edmx-no-keyfield.xml new file mode 100644 index 0000000..11b94a0 --- /dev/null +++ b/src/test/resources/bad.edmx-no-keyfield.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/bad.edmx-unparsable-xml.xml b/src/test/resources/bad.edmx-unparsable-xml.xml new file mode 100644 index 0000000..6a706f9 --- /dev/null +++ b/src/test/resources/bad.edmx-unparsable-xml.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + +' + diff --git a/src/test/resources/bad.edmx-wrong-edm-binding-target.xml b/src/test/resources/bad.edmx-wrong-edm-binding-target.xml new file mode 100644 index 0000000..607563b --- /dev/null +++ b/src/test/resources/bad.edmx-wrong-edm-binding-target.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/good.edmx-and-edm.xml b/src/test/resources/good.edmx-and-edm.xml new file mode 100644 index 0000000..04f8beb --- /dev/null +++ b/src/test/resources/good.edmx-and-edm.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/mock.platinum.resoscript b/src/test/resources/mock.platinum.resoscript new file mode 100644 index 0000000..4869340 --- /dev/null +++ b/src/test/resources/mock.platinum.resoscript @@ -0,0 +1,617 @@ + + + + + + + + + + + + + + + + + + + + + ]> + + + + + + + 2.0.2 + + + + + + + + + + + + + authorization_code + + + + client_credentials + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +