Rule Outputs

Groovy rules can produce documents or text, and place them in the running execution for somesequent assertions and operations to act upon them.

Here is an example of a testscript rule that produces rule outputs:

../../_images/rule-output-rule-a4.png

In the example above, the rule-ouput oauth2GetTokenResponse1-id-token-header is defined to produce a document of type JSON while the rule oauth2GetTokenResponse1-id-token-payload-iss produces the default text/string type.

Within the Groovy rule (below), a JSON document is constructed and placed in the output binding under the oauth2GetTokenResponse1-id-token-header key (or Rule Output Id). This id can then be used as the value of sourceId in downstream operations and assertions. The output oauth2GetTokenResponse1-id-token-payload-iss, on the other hand, is populated as a text/string value and can be acted upon using string operators like notEmpty and equals.

../../_images/rule-output-groovy-a3.png

Assertions on one Rule Output

As mentioned in the earlier section, rule outputs can become the sourceId or variable of common testscript assertions. Alternatively, a rule output can be grabbed by its rule output id:

def idTokenHeaderDoc = ruleOutputs.get('oauth2GetTokenResponse1-id-token-header')

def idTokenHeaderIssValue = ruleOutputs.get('oauth2GetTokenResponse1-id-token-payload-iss')

A series of assertions could then be performed using the Rule API:

idTokenHeaderDoc.assertBodyNotEmpty()
idTokenHeaderDoc.assertJsonPathContains("iss", "theValue")

assert !idTokenHeaderIssValue.is(null): "Expected oauth2GetTokenResponse1-id-token-payload-iss to be set"

If we wanted to perform assertions on multiple rule outputs and wanted the assertion failures to be reported on all of the rule outputs, then the above approach would not work as rule-execution would stop on the first rule outputs assertion failure.


Assertions on multiple Rule Outputs

If multiple assertions are performed in a rule (e.g. on all rule outputs) and one of the assertions fail, then subsequent assertions are not performed. To perform all the assertions, the rule author must catch the assertion failure and register the error with the rules-engine before proceeding with the other assertions. Touchstone will fail the overall rule assertion if one or more assertions fail and will consolidate all the assertion failure messages into one message.

Here’s an example that demonstrates how to perform assertions on multple rule outputs without short-circuiting on any given rule output assertion failure:

ruleOutputs.each() { ruleOutputEntry ->
   def ruleOutputId = ruleOutputEntry.key
   if (ruleOutputId.startsWith("oauth2GetToken")) { // The prefix can be passed as a rule parameter from the test script
      try {
         def idTokenHeaderDoc = ruleOutputEntry.value

         logger.info("Validating "+ruleOutputId)
         idTokenHeaderDoc.assertBodyNotEmpty()
         idTokenHeaderDoc.assertJsonPathContains("iss", "theValue")
      } catch (Throwable e) {
         errorsAndWarnings.registerAndContinue(e);
      }
   }
}

Notice how we’re iterating through the ruleOutputs and registering any assertion errors with the Rules Engine using errorsAndWarnings.registerAndContinue(e);, and then proceeding with assertions on the next rule output.


Rule Output when outputting a Fixture

First, the Rule output when outputting a fixture and not a variable needs to set up the ‘output’ extension like so:

<action>
        <assert>
                <extension url="http://touchstone.aegis.net/touchstone/fhir/testing/StructureDefinition/testscript-assert-rule">
                        <extension url="ruleId">
                                <valueId value="{rule Id}"/>
                        </extension>

                        <extension url="output">
                                <extension url="name">
                                        <valueString value="{name of the output fixture - Example - ruleOutputFile}"/>
                                </extension>
                                <extension url="type">
                                        <valueString value="document"/>
                                </extension>
                                <extension url="contentType">
                                        <valueString value="{format of the fixture, i.e. json or xml}"/>
                                </extension>
                        </extension>

                </extension>
                <description value="Full Description here" />
                <sourceId value="none"/>
                <warningOnly value="false" />
        </assert>
</action>

The output fixture can then be accessed in a assert by using assert.sourceId like so:

<action>
        <assert>
                <extension url="http://touchstone.aegis.net/touchstone/fhir/testing/StructureDefinition/testscript-assert-stopTestOnFail">
                        <valueBoolean value="false" />
                </extension>
                <description value="Full Description here"/>
                <expression value="Bundle.type = 'batch'"/>
                <sourceId value="ruleOutputFile" />
                <warningOnly value="false"/>
        </assert>
</action>

Any Rule Output Fixture, once populated, can be referenced by the ‘name’ extension value like any other static/dynamic fixture id.

And can be used in a Operation like normal:

<action>
        <operation>
                <type>
                        <system value="http://touchstone.com/fhir/testscript-operation-codes-extended"/>
                        <code value="process-message"/>
                </type>
                <description value="Full Description here"/>
                <accept value="json"/>
                <contentType value="json"/>
                <encodeRequestUrl value="true"/>
                <params value="/$validate"/>
                <sourceId value="ruleOutputFile"/>
        </operation>
</action>

Note: you may find this rule helpful.