diff --git a/README.md b/README.md index edc7642..432d969 100644 --- a/README.md +++ b/README.md @@ -335,51 +335,99 @@ ability to run individual or multiple tests using tags. ### Web API Usage -The Commander may be run in automated testing mode for a Web API 1.0.2 Server Certification using a terminal. You do not need to use the Commander JAR file mentioned elsewhere in this step. Instead, you will run the tests using Gradle for automation against a clean copy of the latest Commander code. +The Commander may be run in automated testing mode for a Web API 1.0.2 Server Certification using a terminal. +You do not need to use the Commander JAR file mentioned elsewhere in this step. +Instead, you will run the tests using Gradle for automation against a clean copy of the latest Commander code. -You will need to download the source code so you can run Gradle in the root of the directory. This assumes that you also have Java 8 (1.8.0) or above installed, as mentioned elsewhere in this [`README`](#getting-started). +You will need to download the source code so you can run Gradle in the root of the directory. +This assumes that you also have Java 8 (1.8.0) or above installed, as mentioned elsewhere in this [`README`](#getting-started). -First, change into the directory you want to work in and clone the Commander repository. You will need to have Git installed. Chances are you already do, to check, open a command line and type `git` and if it's present, it will print some info about the app. If not, [there are instructions here](https://git-scm.com/downloads). +First, change into the directory you want to work in and clone the Commander repository. +You will need to have Git installed. +Chances are you already do, to check, open a command line and type `git` and if it's present, +it will print some info about the app. If not, [there are instructions here](https://git-scm.com/downloads). -**MacOS or Linux** +##### MacOS or Linux ``` $ git clone https://github.com/RESOStandards/web-api-commander.git ``` -**Windows** +##### Windows ``` C:\> git clone https://github.com/RESOStandards/web-api-commander.git ``` -This will clone the repository into a directory called web-api-commander, which means you will have a fresh copy of the latest code to execute. To refresh the code after you have downloaded it, issue the command `$ git pull` in the root of the directory that was just created. +This will clone the repository into a directory called web-api-commander relative to whatever directory you're currently in, +which also means you'll have a fresh copy of the latest code to execute. + +To refresh the code after you have downloaded it, issue the command `$ git pull` in the root of the directory that was just created. -The Gradle wrapper provides a convenient way to automatically install Gradle when running tests. After you have cloned the repository, issuing the following command: +#### Running with the Gradle Wrapper +The Gradle wrapper provides a convenient way to automatically install Gradle when running tests. -**MacOS or Linux** +After you have cloned the repository, the task you run will depend on the level of Web API 1.0.2 Server Certification +you're interested in. + +Before you do that, however, you'll want to make sure that you are running the 6.2.2 version of Gradle. + +In the project directory, perform one of the following steps: + +##### MacOS or Linux +```$ ./gradlew wrapper --gradle-version 6.2.2 --distribution-type all``` + +##### Windows +```C:\path\to\web-api-commander> gradlew wrapper --gradle-version 6.2.2 --distribution-type all``` + +You should see a success message. For more information, [see here](https://docs.gradle.org/current/userguide/gradle_wrapper.html). + +### Convenience Methods for Web API 1.0.2 Gold and Platinum Certification (Recommended) +While you may use tags to filter tests as you choose, explained in the next section, it's convenient +to be able to run a predefined set of tests for Gold or Platinum certification. + +These tasks will also produce reports in the local `build` directory, named according to which test you ran. + +#### Gold Certification + +This will run the Gold tests against the Web API 1.0.2 Server provided as `WebAPIURI` in `your.resoscript` file. + +##### MacOS or Linux ``` -$ ./gradlew testWebAPIServer_1_0_2 -DpathToRESOScript=/path/to/your.resoscript +$ ./gradlew testWebAPIServer_1_0_2_Gold -DpathToRESOScript=/path/to/your.resoscript -DshowResponses=true ``` -**Windows** +##### Windows ``` -C:\path\to\web-api-commander> gradlew.bat testWebAPIServer_1_0_2 -DpathToRESOScript=C:\path\to\your.resoscript +C:\path\to\web-api-commander> gradlew testWebAPIServer_1_0_2_Gold -DpathToRESOScript=C:\path\to\your.resoscript -DshowResponses=true ``` -This will run the entirety of the tests against the Web API server provided as `WebAPIURI` in `your.resoscript` file. You can pass tags to filter on in order to run one or more tests matching the given tag. +*Note: the first time you run these tasks, they will take some time as the environment is being configured behind the +scenes and the code is being compiled from the contents of the source directory you downloaded in the previous step. -Note that the first time you run this command, it will take some time to complete as Gradle will download all dependencies and compile the application before running the test suite. *Note: this step will be Dockerized so it can be run with a single command in a Docker container in upcoming versions of the Commander.* +#### Platinum Certification +This will run the Platinum tests against the Web API 1.0.2 Server provided as `WebAPIURI` in `your.resoscript` file. - -To filter by tags, a command similar to the following would be used: - -**MacOS or Linux** +##### MacOS or Linux ``` -$ gradle testWebAPIServer_1_0_2 -DpathToRESOScript=/path/to/your.resoscript -Dcucumber.filter.tags="@core" +$ ./gradlew testWebAPIServer_1_0_2_Platinum -DpathToRESOScript=/path/to/your.resoscript -DshowResponses=true ``` -**Windows** +##### Windows ``` -C:\path\to\web-api-commander> gradlew.bat testWebAPIServer_1_0_2 -DpathToRESOScript=C:\path\to\your.resoscript -Dcucumber.filter.tags="@core" +C:\path\to\web-api-commander> gradlew testWebAPIServer_1_0_2_Platinum -DpathToRESOScript=C:\path\to\your.resoscript -DshowResponses=true +``` + +#### Advanced feature: Tag Filtering +You may also filter by tags. These are the items in the Cucumber .feature files prefixed by an `@` symbol. Expressions +may also be used with tags. See the [Cucumber Documentation](https://cucumber.io/docs/cucumber/api/#tags) for more information. + +##### MacOS or Linux +``` +$ gradle testWebAPIServer_1_0_2_Platinum -DpathToRESOScript=/path/to/your.resoscript -Dcucumber.filter.tags="@core" +``` + +##### Windows +``` +C:\path\to\web-api-commander> gradlew.bat testWebAPIServer_1_0_2_Platinum -DpathToRESOScript=C:\path\to\your.resoscript -Dcucumber.filter.tags="@core" ``` This would run only the tests marked as `@core` in the @@ -403,32 +451,48 @@ Please feel free to suggest additional tags that might be useful. A sample of the runtime terminal output follows: -``` - @REQ-WA103-END3 @core @x.y.z @core-support-endorsement - Scenario: REQ-WA103-END3 - CORE - Request and Validate Server Metadata +```gherkin + > Task :testWebApiServer_1_0_2_Platinum + + @REQ-WA103-END3 @core @x.y.z @core-endorsement @metadata + Scenario: Request and Validate Server Metadata Using RESOScript: /path/to/your.resoscript - Given a RESOScript file was provided - + Given a RESOScript file was provided + RESOScript loaded successfully! - And Client Settings and Parameters were read from the file - + And Client Settings and Parameters were read from the file + Bearer token loaded... first 4 characters: abcd - Service root is: https://api.server.com/serviceRoot - And an OData client was successfully created from the given RESOScript - - Request URI: https://api.server.com/serviceRoot/$metadata?$format=application/xml - Request succeeded...185032 bytes received. - When a GET request is made to the resolved Url in "REQ-WA103-END3" - - Response code is: 200 - Then the server responds with a status code of 200 - - Response is valid XML! - And the response is valid XML - + Service root is: https://api.server.com/ + And an OData client was successfully created from the given RESOScript + + Fetching XMLMetadata with OData Client from: https://api.server.com/$metadata + When a successful metadata request is made to the service root in "ClientSettings_WebAPIURI" + Metadata is valid! - And the metadata returned is valid + And the metadata returned is valid + + Fetching Edm with OData Client from: https://api.server.com/$metadata + Found EntityContainer for the given resource: 'Property' + And the metadata contains the "Parameter_EndpointResource" resource + + Searching metadata for fields in given select list: ListingKey,BedroomsTotal,StreetName,PropertyType,ListingContractDate,ModificationTimestamp,Latitude,Longitude + Found: 'ListingKey' + Found: 'BedroomsTotal' + Found: 'StreetName' + Found: 'PropertyType' + Found: 'ListingContractDate' + Found: 'ModificationTimestamp' + Found: 'Latitude' + Found: 'Longitude' + And resource metadata for "Parameter_EndpointResource" contains the fields in "Parameter_SelectList" + + + 1 Scenarios (1 passed) + 7 Steps (7 passed) + 0m3.244s + ``` This shows configuration parameters, requests, and responses in a lightweight-manner. @@ -456,24 +520,73 @@ These commands should not be necessary for the normal use of the Commander. Ther A [Dockerfile](./Dockerfile) has been provided to dockerize the application. This can be used for CI/CD environments such as Jenkins or TravisCI. The following command will build an image for you: + +### Commander Features Other Than Automated Web API Testing ``` -$ docker build -t darnjo/web-api-command . +$ docker build -t web-api-commander . ``` The usage for the docker container is the same for `web-api-commander.jar` presented above. ``` -$ docker run -it darnjo/web-api-commander --help +$ docker run -it web-api-commander --help ``` If you have input files you may need to mount your filesystem into the docker container ``` -$ docker run -it -v $PWD:/app darnjo/web-api-commander --validateMetadata --inputFile +$ docker run -it -v $PWD:/app web-api-commander --validateMetadata --inputFile +``` + +### Automated Web API Testing + +You may also run the tests in a Docker container locally by issuing one of the following commands. +Docker must be running on your local machine. + +#### MacOS or Linux All-In-One Commands + + +##### Gold +``` +cd ~; \ +rm -rf commander-tmp/; \ +mkdir commander-tmp; \ +cd commander-tmp; \ +git clone https://github.com/RESOStandards/web-api-commander.git; \ +cd web-api-commander; \ +docker run --rm -u gradle -v "$PWD":/home/gradle/project -v /path/to/your/resoscripts:/home/gradle/project/resoscripts -w /home/gradle/project gradle gradle testWebAPIServer_1_0_2_Gold -DpathToRESOScript=/home/gradle/project/resoscripts/your.resoscript -DshowResponses=true +``` + +##### Platinum + +``` +cd ~; \ +rm -rf commander-tmp/; \ +mkdir commander-tmp; \ +cd commander-tmp; \ +git clone https://github.com/RESOStandards/web-api-commander.git; \ +cd web-api-commander; \ +docker run --rm -u gradle -v "$PWD":/home/gradle/project -v /path/to/your/resoscripts:/home/gradle/project/resoscripts -w /home/gradle/project gradle gradle testWebAPIServer_1_0_2_Platinum -DpathToRESOScript=/home/gradle/project/resoscripts/your.resoscript -DshowResponses=true +``` + +Note that this will create a directory in your home directory for the project, and build artifacts and the log will be placed in that directory, +which is also where you will end up after runtime. + + +#### Windows All-In-One WIP + +##### Gold + +``` +cd C:\;mkdir commander-tmp;cd commander-tmp;git clone https://github.com/RESOStandards/web-api-commander.git;cd web-api-commander; docker run --rm -u gradle -v C:\current\path\web-api-commander:/home/gradle/project -v C:\path\to\your\resoscripts:/home/gradle/project/resoscripts -w /home/gradle/project gradle gradle testWebAPIServer_1_0_2_Gold -DpathToRESOScript=/home/gradle/project/resoscripts/your.resoscript -DshowResponses=true +``` + +##### Platinum +``` +cd C:\;mkdir commander-tmp;cd commander-tmp;git clone https://github.com/RESOStandards/web-api-commander.git;cd web-api-commander;docker run --rm -u gradle -v C:\current\path\web-api-commander:/home/gradle/project -v C:\path\to\your\resoscripts:/home/gradle/project/resoscripts -w /home/gradle/project gradle gradle testWebAPIServer_1_0_2_Platinum -DpathToRESOScript=/home/gradle/project/resoscripts/your.resoscript -DshowResponses=true ``` --- - ## Logging In the current version of the Commander, two logs are produced. One is outputted in the terminal at `INFO` level during runtime through `stdout`. A detailed log called `commander.log` will be outputted at runtime and will contain details down to the wire requests. @@ -491,4 +604,4 @@ Please contact [josh@reso.org](mailto:josh@reso.org) with any questions, bug rep * Support for authentication options in addition to Bearer tokens (Client Credentials in beta, please email for more info). * Parallel fetch for replication * Job Scheduling -* Excel export \ No newline at end of file +* Excel export diff --git a/build.gradle b/build.gradle index 118d545..6ce4f92 100644 --- a/build.gradle +++ b/build.gradle @@ -67,26 +67,54 @@ tasks.withType(JavaCompile) { options.deprecation = true } -// task for Web API Server Testing - this is what should be run in the command line -task testWebApiServer_1_0_2() { + +// task for Web API Server 1.0.2 Gold Testing +task testWebApiServer_1_0_2_Gold() { dependsOn jar doLast { javaexec { main = "io.cucumber.core.cli.Main" classpath = configurations.cucumberRuntime + sourceSets.main.output + sourceSets.test.output + systemProperties = System.getProperties() args = [ '--strict', '--plugin', 'pretty', '--plugin', - 'json:build/web-api-server-1.0.2.json', + 'json:build/web-api-server-1.0.2.gold.json', '--plugin', - 'html:build/web-api-server-1.0.2.html', + 'html:build/web-api-server-1.0.2.gold.html', + '--glue', + 'org.reso.certification.stepdefs#WebAPIServer_1_0_2', + 'src/main/java/org/reso/certification/features', + '--tags', + 'not @platinum' + + (systemProperties.get('cucumber.filter.tags') != null ? ' and ' + systemProperties.get('cucumber.filter.tags') : '') + ] + } + } +} + +// task for Web API Server 1.0.2 Platinum Testing - currently equivalent to all, but generates a platinum-named report. +task testWebApiServer_1_0_2_Platinum() { + dependsOn jar + doLast { + javaexec { + main = "io.cucumber.core.cli.Main" + classpath = configurations.cucumberRuntime + sourceSets.main.output + sourceSets.test.output + systemProperties = System.getProperties() + args = [ + '--strict', + '--plugin', + 'pretty', + '--plugin', + 'json:build/web-api-server-1.0.2.platinum.json', + '--plugin', + 'html:build/web-api-server-1.0.2.platinum.html', '--glue', 'org.reso.certification.stepdefs#WebAPIServer_1_0_2', 'src/main/java/org/reso/certification/features' ] - systemProperties = System.getProperties() } } } diff --git a/build/libs/web-api-commander.jar b/build/libs/web-api-commander.jar index 63cdb6d..3c7e34a 100644 Binary files a/build/libs/web-api-commander.jar and b/build/libs/web-api-commander.jar differ diff --git a/settings.gradle b/settings.gradle index c388765..fa27394 100644 --- a/settings.gradle +++ b/settings.gradle @@ -7,5 +7,4 @@ * in the user manual at https://docs.gradle.org/5.2.1/userguide/multi_project_builds.html */ -rootProject.name = 'web-api-commander' - +rootProject.setName('web-api-commander') diff --git a/src/main/java/org/reso/certification/features/web-api-server-1.0.2.feature b/src/main/java/org/reso/certification/features/web-api-server-1.0.2.feature index 69a2ab5..efde159 100644 --- a/src/main/java/org/reso/certification/features/web-api-server-1.0.2.feature +++ b/src/main/java/org/reso/certification/features/web-api-server-1.0.2.feature @@ -6,12 +6,12 @@ Feature: Web API Server 1.0.2 Certification And Client Settings and Parameters were read from the file And an OData client was successfully created from the given RESOScript - @REQ-WA103-END3 @core @x.y.z @core-endorsement + @REQ-WA103-END3 @core @x.y.z @core-endorsement @metadata Scenario: Request and Validate Server Metadata - When a GET request is made to the resolved Url in "REQ-WA103-END3" - Then the server responds with a status code of 200 - And the response is valid XML + When a successful metadata request is made to the service root in "ClientSettings_WebAPIURI" And the metadata returned is valid + And the metadata contains the "Parameter_EndpointResource" resource + And resource metadata for "Parameter_EndpointResource" contains the fields in "Parameter_SelectList" @REQ-WA103-END2 @core @x.y.z @core-endorsement Scenario: Data System Endpoint test diff --git a/src/main/java/org/reso/certification/stepdefs/WebAPIServer_1_0_2.java b/src/main/java/org/reso/certification/stepdefs/WebAPIServer_1_0_2.java index 3c70fad..dfcab02 100644 --- a/src/main/java/org/reso/certification/stepdefs/WebAPIServer_1_0_2.java +++ b/src/main/java/org/reso/certification/stepdefs/WebAPIServer_1_0_2.java @@ -14,8 +14,11 @@ import org.apache.olingo.client.api.communication.ODataClientErrorException; import org.apache.olingo.client.api.communication.request.retrieve.ODataRawRequest; import org.apache.olingo.client.api.communication.response.ODataRawResponse; import org.apache.olingo.client.api.edm.xml.XMLMetadata; +import org.apache.olingo.commons.api.edm.Edm; import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException; -import org.apache.olingo.commons.api.format.ContentType; +import org.apache.olingo.commons.api.edm.provider.CsdlEntityContainer; +import org.apache.olingo.commons.api.edm.provider.CsdlProperty; +import org.apache.olingo.commons.api.edm.provider.CsdlSchema; import org.apache.olingo.commons.core.edm.primitivetype.EdmDate; import org.apache.olingo.commons.core.edm.primitivetype.EdmDateTimeOffset; import org.apache.olingo.commons.core.edm.primitivetype.EdmTimeOfDay; @@ -28,7 +31,10 @@ import java.io.*; import java.net.URI; import java.sql.Time; import java.sql.Timestamp; -import java.time.*; +import java.time.Instant; +import java.time.LocalDate; +import java.time.OffsetDateTime; +import java.time.Year; import java.util.*; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; @@ -116,6 +122,10 @@ public class WebAPIServer_1_0_2 implements En { serverODataHeaderVersion.set(Utils.getHeaderData(HEADER_ODATA_VERSION, cex.getHeaderInfo())); responseCode.set(cex.getStatusLine().getStatusCode()); oDataClientErrorExceptionHandled.set(true); + throw cex; + } catch (Exception ex) { + fail(ex.toString()); + throw ex; } return null; }; @@ -127,12 +137,15 @@ public class WebAPIServer_1_0_2 implements En { if (pathToRESOScript == null) { pathToRESOScript = System.getProperty("pathToRESOScript"); } + + assertNotNull("ERROR: pathToRESOScript must be present in command arguments, see README", pathToRESOScript); LOG.info("Using RESOScript: " + pathToRESOScript); }); And("^Client Settings and Parameters were read from the file$", () -> { if (settings == null) { settings = Settings.loadFromRESOScript(new File(System.getProperty("pathToRESOScript"))); } + assertNotNull("ERROR: Settings could not be loaded.", settings); LOG.info("RESOScript loaded successfully!"); }); Given("^an OData client was successfully created from the given RESOScript$", () -> { @@ -165,6 +178,10 @@ public class WebAPIServer_1_0_2 implements En { .bearerToken(bearerToken) .useEdmEnabledClient(true) .build()); + + assertNotNull(commander.get()); + assertTrue("ERROR: Commander must either have a valid bearer token or Client Credentials configuration.", + commander.get().isTokenClient() || (commander.get().isOAuthClient() && commander.get().getTokenUri() != null)); }); @@ -180,13 +197,17 @@ public class WebAPIServer_1_0_2 implements En { * REQ-WA103-END3 */ And("^the metadata returned is valid$", () -> { - //store the metadata for later comparisons - xmlMetadata.set(commander.get().getClient().getDeserializer(ContentType.APPLICATION_XML) - .toMetadata(new ByteArrayInputStream(responseData.get().getBytes()))); + if (xmlMetadata.get() == null) { + fail("ERROR: No XML Metadata Exists!"); + } - boolean isValid = commander.get().validateMetadata(xmlMetadata.get()); - LOG.info("Metadata is " + (isValid ? "valid" : "invalid") + "!"); - assertTrue(isValid); + try { + boolean isValid = commander.get().validateMetadata(xmlMetadata.get()); + LOG.info("Metadata is " + (isValid ? "valid" : "invalid") + "!"); + assertTrue(isValid); + } catch (Exception ex) { + fail(ex.getMessage()); + } }); @@ -194,24 +215,28 @@ public class WebAPIServer_1_0_2 implements En { * REQ-WA103-QR1 */ And("^the provided \"([^\"]*)\" is returned in \"([^\"]*)\"$", (String parameterUniqueIdValue, String parameterUniqueId) -> { - String expectedValueAsString = Settings.resolveParametersString(parameterUniqueIdValue, settings), resolvedValueAsString = null; - Object resolvedValue = from(responseData.get()).get(Settings.resolveParametersString(parameterUniqueId, settings)); + try { + String expectedValueAsString = Settings.resolveParametersString(parameterUniqueIdValue, settings), resolvedValueAsString = null; + Object resolvedValue = from(responseData.get()).get(Settings.resolveParametersString(parameterUniqueId, settings)); - //both of the inputs should be present - assertNotNull(expectedValueAsString); - assertNotNull(resolvedValue); + //both of the inputs should be present + assertNotNull(expectedValueAsString); + assertNotNull(resolvedValue); - //quotes are passed for strings, let's strip them off - expectedValueAsString = expectedValueAsString - .replace("'", "").replace("\"", ""); + //quotes are passed for strings, let's strip them off + expectedValueAsString = expectedValueAsString + .replace("'", "").replace("\"", ""); - LOG.info("Expected Value is: " + expectedValueAsString); - LOG.info("Resolved value is: " + resolvedValue); + LOG.info("Expected Value is: " + expectedValueAsString); + LOG.info("Resolved value is: " + resolvedValue); - if (resolvedValue.getClass().isInstance(Integer.class)) { - assertEquals(Integer.parseInt(expectedValueAsString), resolvedValue); - } else { - assertEquals(expectedValueAsString, resolvedValue.toString()); + if (resolvedValue.getClass().isInstance(Integer.class)) { + assertEquals(Integer.parseInt(expectedValueAsString), resolvedValue); + } else { + assertEquals(expectedValueAsString, resolvedValue.toString()); + } + } catch (Exception ex) { + fail(ex.getMessage()); } }); @@ -220,32 +245,37 @@ public class WebAPIServer_1_0_2 implements En { * REQ-WA103-QR3 - $select */ And("^data are present in fields contained within \"([^\"]*)\"$", (String parameterSelectList) -> { - AtomicInteger numFieldsWithData = new AtomicInteger(); - List fieldList = new ArrayList<>(Arrays.asList(Settings.resolveParametersString(parameterSelectList, settings).split(","))); + try { + AtomicInteger numFieldsWithData = new AtomicInteger(); + List fieldList = new ArrayList<>(Arrays.asList(Settings.resolveParametersString(parameterSelectList, settings).split(","))); - AtomicInteger numResults = new AtomicInteger(); + AtomicInteger numResults = new AtomicInteger(); - //iterate over the items and count the number of fields with data to determine whether there are data present - from(responseData.get()).getList(JSON_VALUE_PATH, HashMap.class).forEach(item -> { - if (item != null) { - numResults.getAndIncrement(); - fieldList.forEach(field -> { - if (item.get(field) != null) { - numFieldsWithData.getAndIncrement(); - } - }); + + //iterate over the items and count the number of fields with data to determine whether there are data present + from(responseData.get()).getList(JSON_VALUE_PATH, HashMap.class).forEach(item -> { + if (item != null) { + numResults.getAndIncrement(); + fieldList.forEach(field -> { + if (item.get(field) != null) { + numFieldsWithData.getAndIncrement(); + } + }); + } + }); + + LOG.info("Number of Results: " + numResults.get()); + LOG.info("Number of Fields: " + fieldList.size()); + LOG.info("Fields with Data: " + numFieldsWithData.get()); + if (numFieldsWithData.get() > 0) { + LOG.info("Percent Fill: " + ((numResults.get() * fieldList.size()) / (1.0 * numFieldsWithData.get()) * 100) + "%"); + } else { + LOG.info("Percent Fill: 0% - no fields with data found!"); } - }); - - LOG.info("Number of Results: " + numResults.get()); - LOG.info("Number of Fields: " + fieldList.size()); - LOG.info("Fields with Data: " + numFieldsWithData.get()); - if (numFieldsWithData.get() > 0) { - LOG.info("Percent Fill: " + ((numResults.get() * fieldList.size()) / (1.0 * numFieldsWithData.get()) * 100) + "%"); - } else { - LOG.info("Percent Fill: 0% - no fields with data found!"); + assertTrue(numFieldsWithData.get() > 0); + } catch (Exception ex) { + fail(ex.getMessage()); } - assertTrue(numFieldsWithData.get() > 0); }); @@ -254,13 +284,17 @@ public class WebAPIServer_1_0_2 implements En { * $top=*Parameter_TopCount* */ And("^the results contain at most \"([^\"]*)\" records$", (String parameterTopCount) -> { - List items = from(responseData.get()).getList(JSON_VALUE_PATH); - AtomicInteger numResults = new AtomicInteger(items.size()); + try { + List items = from(responseData.get()).getList(JSON_VALUE_PATH); + AtomicInteger numResults = new AtomicInteger(items.size()); - int topCount = Integer.parseInt(Settings.resolveParametersString(parameterTopCount, settings)); - LOG.info("Number of values returned: " + numResults.get() + ", top count is: " + topCount); + int topCount = Integer.parseInt(Settings.resolveParametersString(parameterTopCount, settings)); + LOG.info("Number of values returned: " + numResults.get() + ", top count is: " + topCount); - assertTrue(numResults.get() > 0 && numResults.get() <= topCount); + assertTrue(numResults.get() > 0 && numResults.get() <= topCount); + } catch (Exception ex) { + fail(ex.getMessage()); + } }); @@ -269,29 +303,37 @@ public class WebAPIServer_1_0_2 implements En { * $skip=*Parameter_TopCount* */ And("^a GET request is made to the resolved Url in \"([^\"]*)\" with \\$skip=\"([^\"]*)\"$", (String requirementId, String parameterTopCount) -> { - int skipCount = Integer.parseInt(Settings.resolveParametersString(parameterTopCount, settings)); - LOG.info("Skip count is: " + skipCount); + try { + int skipCount = Integer.parseInt(Settings.resolveParametersString(parameterTopCount, settings)); + LOG.info("Skip count is: " + skipCount); - //preserve initial response data for later comparisons - initialResponseData.set(responseData.get()); + //preserve initial response data for later comparisons + initialResponseData.set(responseData.get()); - //TODO: convert to OData filter factory - URI requestUri = Commander.prepareURI(Settings.resolveParameters(settings.getRequests().get(requirementId), settings).getUrl() + "&$skip=" + skipCount); + //TODO: convert to OData filter factory + URI requestUri = Commander.prepareURI(Settings.resolveParameters(settings.getRequests().get(requirementId), settings).getUrl() + "&$skip=" + skipCount); - executeGetRequest.apply(requestUri); + executeGetRequest.apply(requestUri); + } catch (Exception ex) { + fail(ex.getMessage()); + } }); And("^data in the \"([^\"]*)\" fields are different in the second request than in the first$", (String parameterUniqueId) -> { - List l1 = from(initialResponseData.get()).getJsonObject(JSON_VALUE_PATH); - List l2 = from(responseData.get()).getJsonObject(JSON_VALUE_PATH); + try { + List l1 = from(initialResponseData.get()).getJsonObject(JSON_VALUE_PATH); + List l2 = from(responseData.get()).getJsonObject(JSON_VALUE_PATH); - int combinedCount = l1.size() + l2.size(); - Set combined = new LinkedHashSet<>(l1); - LOG.info("Response Page 1: " + new POJONode(l1)); + int combinedCount = l1.size() + l2.size(); + Set combined = new LinkedHashSet<>(l1); + LOG.info("Response Page 1: " + new POJONode(l1)); - combined.addAll(l2); - LOG.info("Response Page 2: " + new POJONode(l2)); + combined.addAll(l2); + LOG.info("Response Page 2: " + new POJONode(l2)); - assertEquals(combinedCount, combined.size()); + assertEquals(combinedCount, combined.size()); + } catch (Exception ex) { + fail(ex.getMessage()); + } }); //================================================================================================================== @@ -302,65 +344,78 @@ public class WebAPIServer_1_0_2 implements En { * GET request by requirementId (see generic.resoscript) */ When("^a GET request is made to the resolved Url in \"([^\"]*)\"$", (String requirementId) -> { - //reset local state each time a get request is run - resetRequestState.run(); + try { + //reset local state each time a get request is run + resetRequestState.run(); - URI requestUri = Commander.prepareURI(Settings.resolveParameters(settings.getRequests().get(requirementId), settings).getUrl()); - executeGetRequest.apply(requestUri); + URI requestUri = Commander.prepareURI(Settings.resolveParameters(settings.getRequests().get(requirementId), settings).getUrl()); + executeGetRequest.apply(requestUri); + } catch (Exception ex) { + LOG.debug("Exception was thrown in " + this.getClass() + ": " + ex.toString()); + } }); /* * Assert response code */ Then("^the server responds with a status code of (\\d+)$", (Integer assertedResponseCode) -> { - LOG.info("Asserted Response Code: " + assertedResponseCode + ", Server Response Code: " + responseCode); - assertTrue(responseCode.get() > 0 && assertedResponseCode > 0); - assertEquals(assertedResponseCode.intValue(), responseCode.get().intValue()); + try { + LOG.info("Asserted Response Code: " + assertedResponseCode + ", Server Response Code: " + responseCode); + assertTrue(responseCode.get() > 0 && assertedResponseCode > 0); + assertEquals(assertedResponseCode.intValue(), responseCode.get().intValue()); + } catch (Exception ex) { + fail(ex.getMessage()); + } }); /* * validate XML wrapper */ And("^the response is valid XML$", () -> { - assertTrue(Commander.validateXML(responseData.get())); - LOG.info("Response is valid XML!"); + try { + assertTrue(Commander.validateXML(responseData.get())); + LOG.info("Response is valid XML!"); + } catch (Exception ex) { + fail(ex.getMessage()); + } }); /* * validate JSON wrapper */ And("^the response is valid JSON$", () -> { - assertTrue(Utils.isValidJson(responseData.get())); - LOG.info("Response is valid JSON!"); + try { + assertTrue(Utils.isValidJson(responseData.get())); + LOG.info("Response is valid JSON!"); - String showResponses = System.getProperty("showResponses"); - if (Boolean.parseBoolean(showResponses)) { - LOG.info("Response: " + new ObjectMapper().readTree(responseData.get()).toPrettyString()); + String showResponses = System.getProperty("showResponses"); + if (Boolean.parseBoolean(showResponses)) { + LOG.info("Response: " + new ObjectMapper().readTree(responseData.get()).toPrettyString()); + } + } catch (Exception ex) { + fail(ex.getMessage()); } }); - /* - * Assert OData version - */ - And("^the server reports OData version \"([^\"]*)\"$", (String assertODataVersion) -> { - LOG.info("Asserted version: " + assertODataVersion + ", Reported OData Version: " + serverODataHeaderVersion.get()); ; - assertEquals(serverODataHeaderVersion.get(), assertODataVersion); - }); - /* * Assert HTTP Response Code given asserted OData version * * TODO: make a general Header assertion function */ Then("^the server responds with a status code of (\\d+) if the server headers report OData version \"([^\"]*)\"$", (Integer assertedHttpResponseCode, String assertedODataVersion) -> { - boolean versionsMatch = serverODataHeaderVersion.get().equals(assertedODataVersion), - responseCodesMatch = responseCode.get().intValue() == assertedHttpResponseCode.intValue(); + try { + boolean versionsMatch = serverODataHeaderVersion.get().equals(assertedODataVersion), + responseCodesMatch = responseCode.get().intValue() == assertedHttpResponseCode.intValue(); - LOG.info("Asserted OData Version: " + assertedODataVersion + ", Server Version: " + serverODataHeaderVersion.get()); + LOG.info("Asserted OData Version: " + assertedODataVersion + ", Server Version: " + serverODataHeaderVersion.get()); - if (versionsMatch) { - LOG.info("Asserted Response Code: " + assertedHttpResponseCode + ", Response code: " + responseCode.get()); - assertTrue(responseCodesMatch); + if (versionsMatch) { + LOG.info("Asserted Response Code: " + assertedHttpResponseCode + ", Response code: " + responseCode.get()); + assertTrue(responseCodesMatch); + } + } catch (Exception ex) { + //DEBUG only in this case as we are expecting an error and don't want to throw or print it + LOG.debug(ex.toString()); } }); @@ -369,59 +424,76 @@ public class WebAPIServer_1_0_2 implements En { * and is used to select among the supported comparisons. */ And("^Integer data in \"([^\"]*)\" \"([^\"]*)\" \"([^\"]*)\"$", (String parameterFieldName, String op, String parameterAssertedValue) -> { - String fieldName = Settings.resolveParametersString(parameterFieldName, settings); - int assertedValue = Integer.parseInt(Settings.resolveParametersString(parameterAssertedValue, settings)); + try { + String fieldName = Settings.resolveParametersString(parameterFieldName, settings); + int assertedValue = Integer.parseInt(Settings.resolveParametersString(parameterAssertedValue, settings)); - LOG.info("fieldName: " + fieldName + ", op: " + op + ", assertedValue: " + assertedValue); + LOG.info("fieldName: " + fieldName + ", op: " + op + ", assertedValue: " + assertedValue); - //subsequent value comparisons are and-ed together while iterating over the list of items, so init to true - AtomicBoolean result = new AtomicBoolean(true); + //subsequent value comparisons are and-ed together while iterating over the list of items, so init to true + AtomicBoolean result = new AtomicBoolean(true); - AtomicReference fieldValue = new AtomicReference<>(); + AtomicReference fieldValue = new AtomicReference<>(); - //iterate through response data and ensure that with data, the statement fieldName "op" assertValue is true - from(responseData.get()).getList(JSON_VALUE_PATH, HashMap.class).forEach(item -> { - fieldValue.set(Integer.parseInt(item.get(fieldName).toString())); - result.set(result.get() && Utils.compare(fieldValue.get(), op, assertedValue)); - }); + //iterate through response data and ensure that with data, the statement fieldName "op" assertValue is true + from(responseData.get()).getList(JSON_VALUE_PATH, HashMap.class).forEach(item -> { + fieldValue.set(Integer.parseInt(item.get(fieldName).toString())); + result.set(result.get() && Utils.compare(fieldValue.get(), op, assertedValue)); + }); - assertTrue(result.get()); + assertTrue(result.get()); + } catch (Exception ex) { + fail(ex.getMessage()); + } }); /* * True if response has results, meaning value.length > 0 */ And("^the response has results$", () -> { - int count = from(responseData.get()).getList(JSON_VALUE_PATH, HashMap.class).size(); - LOG.info("Results count is: " + count); - assertTrue(count > 0); + try { + int count = from(responseData.get()).getList(JSON_VALUE_PATH, HashMap.class).size(); + LOG.info("Results count is: " + count); + assertTrue(count > 0); + } catch (Exception ex) { + fail(ex.getMessage()); + } }); /* * True if data are present in the response */ And("^the response has singleton results in \"([^\"]*)\"", (String parameterFieldName) -> { - String value = Settings.resolveParametersString(parameterFieldName, settings); - boolean isPresent = from(responseData.get()).get() != null; - LOG.info("Response value is: " + value); - LOG.info("IsPresent: " + isPresent); - assertTrue(isPresent); + try { + String value = Settings.resolveParametersString(parameterFieldName, settings); + boolean isPresent = from(responseData.get()).get() != null; + LOG.info("Response value is: " + value); + LOG.info("IsPresent: " + isPresent); + assertTrue(isPresent); + } catch (Exception ex) { + fail(ex.getMessage()); + } }); /* * True if results count less than or equal to limit */ And("^the number of results is less than or equal to \"([^\"]*)\"$", (String limitField) -> { - int count = from(responseData.get()).getList(JSON_VALUE_PATH, HashMap.class).size(), - limit = Integer.parseInt(Settings.resolveParametersString(limitField, settings)); - LOG.info("Results count is: " + count + ", Limit is: " + limit); - assertTrue(count <= limit); + try { + int count = from(responseData.get()).getList(JSON_VALUE_PATH, HashMap.class).size(), + limit = Integer.parseInt(Settings.resolveParametersString(limitField, settings)); + LOG.info("Results count is: " + count + ", Limit is: " + limit); + assertTrue(count <= limit); + } catch (Exception ex) { + fail(ex.getMessage()); + } }); /* * True if data in the lhs expression and rhs expressions pass the AND or OR condition given in andOrOp */ And("^Integer data in \"([^\"]*)\" \"([^\"]*)\" \"([^\"]*)\" \"([^\"]*)\" \"([^\"]*)\" \"([^\"]*)\"$", (String parameterFieldName, String opLhs, String parameterAssertedLhsValue, String andOrOp, String opRhs, String parameterAssertedRhsValue) -> { + try { String fieldName = Settings.resolveParametersString(parameterFieldName, settings); Integer assertedLhsValue = Integer.parseInt(Settings.resolveParametersString(parameterAssertedLhsValue, settings)), assertedRhsValue = Integer.parseInt(Settings.resolveParametersString(parameterAssertedRhsValue, settings)); @@ -456,88 +528,107 @@ public class WebAPIServer_1_0_2 implements En { assertTrue(itemResult.get()); } }); + } catch (Exception ex) { + fail(ex.getMessage()); + } }); /* * Date Comparison glue */ And("^Date data in \"([^\"]*)\" \"([^\"]*)\" \"([^\"]*)\"$", (String parameterFieldName, String op, String parameterAssertedValue) -> { - String fieldName = Settings.resolveParametersString(parameterFieldName, settings); - AtomicReference fieldValue = new AtomicReference<>(); - AtomicReference assertedValue = new AtomicReference<>(); + try { + String fieldName = Settings.resolveParametersString(parameterFieldName, settings); + AtomicReference fieldValue = new AtomicReference<>(); + AtomicReference assertedValue = new AtomicReference<>(); - assertedValue.set(Utils.parseDateFromEdmDateString(Settings.resolveParametersString(parameterAssertedValue, settings))); - LOG.info("Asserted value is: " + assertedValue.get().toString()); + assertedValue.set(Utils.parseDateFromEdmDateString(Settings.resolveParametersString(parameterAssertedValue, settings))); + LOG.info("Asserted value is: " + assertedValue.get().toString()); - from(responseData.get()).getList(JSON_VALUE_PATH, HashMap.class).forEach(item -> { - try { - fieldValue.set(Utils.parseDateFromEdmDateTimeOffsetString(item.get(fieldName).toString())); - assertTrue(Utils.compare(fieldValue.get(), op, assertedValue.get())); - } catch (Exception ex){ - LOG.error(ex.toString()); - } - }); + from(responseData.get()).getList(JSON_VALUE_PATH, HashMap.class).forEach(item -> { + try { + fieldValue.set(Utils.parseDateFromEdmDateTimeOffsetString(item.get(fieldName).toString())); + assertTrue(Utils.compare(fieldValue.get(), op, assertedValue.get())); + } catch (Exception ex) { + fail(ex.toString()); + + } + }); + } catch (Exception ex) { + fail(ex.getMessage()); + } }); /* * Time comparison glue */ And("^TimeOfDay data in \"([^\"]*)\" \"([^\"]*)\" \"([^\"]*)\"$", (String parameterFieldName, String op, String parameterAssertedValue) -> { - String fieldName = Settings.resolveParametersString(parameterFieldName, settings); - AtomicReference