Search code examples
javajsonhttpcitrus-framework

Citrus Framework - Attempt to use validator in JSON causes error with header ACCEPT


I have written an integration test using Citrus Framework for a Spring Boot service that communicates via HTTP REST.

I was able to embed some of the Citrus validation "methods" in the JSON to handle situations like @isNumber()@ for timestamps that are always changing. I ran into a problem, however, when I attempted some others.

There is another field (commandID) in the JSON which contains a UUID and is likely to change from one invocation to another. I first decided to use @match("<regex>")@ to match a UUID pattern, and when that produced a problem, switched to trying to use @ignore@, but that also produced the same problem.

Here is the error from the console:

13:29:21.962 [main] ERROR com.consol.citrus.report.LoggingReporter - TEST FAILED MissionPlannerIT.testPlanMission <edu.mit.ll.mission_services.service.mission_planner> Nested exception is:
com.consol.citrus.exceptions.TestCaseFailedException: Validation failed: Values not equal for header element 'Accept', expected 'application/json' but was 'application/json,application/*+json'

I don't know what the issue is here, nor how to fix it.

gen-route-command.json:

{
    "header": {
        "timestamp": "@isNumber()@",
        "typeID": "edu.mit.ll.mission_services.messages.GenerateRouteCommand",
        "transaction": {
            "id": 1,
            "startTime": "@isNumber()@"
        },
        "signature": {
            "algorithm": null,
            "keySize": 0,
            "keyValue": null,
            "sender": null
        }
    },
    "commandID": "@ignore@",
    "commandType": "GENERATE_ROUTE"
}

The @isNumber()@ functions work and allow the matching of those elements to pass validation. @ignore@, however does not. I have tried with both variations: @ignore@ and @ignore()@, neither works. As mentioned earlier, @match(...)@ also does not work.

UPDATE:

I modified things so that I try to do the validation in the test case code. I changed the value in the JSON back to a UUID.

runner.http(builder -> builder
    .server(routeGeneratorServer)
    .receive()
    .post("/v1/missionServices/missionPlanning/generateRoute")
    .accept(ContentType.APPLICATION_JSON.getMimeType())
    .payload(new ClassPathResource("templates/gen-route-command.json"))
    .validate("$.commandID", "@ignore@"));

gen-route-command.json:

{
    "header": {
        "timestamp": "@isNumber()@",
        "typeID": "edu.mit.ll.mission_services.messages.GenerateRouteCommand",
        "transaction": {
            "id": 1,
            "startTime": "@isNumber()@"
        },
        "signature": {
            "algorithm": null,
            "keySize": 0,
            "keyValue": null,
            "sender": null
        }
    },
    "commandID": "0710d523-43da-4f68-90c7-a2b4544a955d",
    "commandType": "GENERATE_ROUTE"
}

Unfortunately, this does not work. I get the error that I originally was trying to mitigate with @ignore@ or @match(...)@.

14:12:57.299 [main] ERROR com.consol.citrus.report.LoggingReporter - TEST FAILED MissionPlannerIT.testPlanMission <edu.mit.ll.mission_services.service.mission_planner> Nested exception is:
com.consol.citrus.exceptions.TestCaseFailedException: Failed to validate JSON text:
{"header":{"timestamp":1581016372132,"typeID":"edu.mit.ll.mission_services.messages.GenerateRouteCommand","transaction":{"id":1,"startTime":1581016372130},"signature":{"algorithm":null,"keySize":0,"keyValue":null,"sender":null}},"commandID":"9302ebde-894b-43a3-b7a3-92a158c7170e","commandType":"GENERATE_ROUTE"} Values not equal for entry: 'commandID', expected '0710d523-43da-4f68-90c7-a2b4544a955d' but was '9302ebde-894b-43a3-b7a3-92a158c7170e'

The commandID values are different because it generates a new UUID each run.

It appears that the validation in the test does not "ignore" the value and instead the JSON validation flags the UUID differences.


Solution

  • It would seem that the Citrus "functions" embedded in the JSON were not the problem. The problem was that I was not setting the Accept and Content-Type headers in the message the service was POSTing for the Citrus receive call. Since the receive call was set up to look for message type application/json, it was failing because my message had no headers at all (this is my impression).

    The Citrus call:

    // Set route generator to receive and validate generate route command.
    runner.http(builder -> builder
        .server(routeGeneratorServer)
        .receive()
        .post("/v1/missionServices/missionPlanning/generateRoute")
        .accept(ContentType.APPLICATION_JSON.getMimeType())
        .payload(new ClassPathResource("templates/gen-route-command.json")));
    

    The "generate route command" message it received had no headers defined. Once I set the Accept and Content-Type headers to application/json, the test was able to proceed.