Closed sushantchoudhary closed 4 years ago
The JSON parser is failing to parse the pact file. Looks like it might be either a corrupt file or the file has the wrong content type.
In what environment are the tests running? (Linux/Windows etc.)
Cool, I will verify the content-type, is there a way to validate corrupt pact file?
Open it in a text editor.
cool, on it. thanks @uglyog
Hey @uglyog , I looked at content-type values and also verified the pact json file but cant zero in on anything potentially causing this error. Any other element which might be causing it? Btw this surfaces only while running the test with gradle test
, running it from IDE doesnt complain. This is my complete pact json file for reference,
{
"provider": {
"name": "mobile-rest-plugin"
},
"consumer": {
"name": "jira-android"
},
"interactions": [
{
"description": "GET agile board",
"request": {
"method": "GET",
"path": "/boards/1.0/board/1",
"query": "markAsViewed=true&moduleKey=agile-mobile-board-service"
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/json"
},
"body": {
"subscribed": "false",
"name": "DEMO board",
"id": 1,
"type": "SCRUM"
},
"matchingRules": {
"$.body.type": {
"regex": "SCRUM",
"match": "regex"
},
"$.body.id": {
"match": "integer"
},
"$.body.subscribed": {
"regex": "false",
"match": "regex"
},
"$.body.name": {
"regex": "DEMO board",
"match": "regex"
}
}
},
"providerState": "JIRA project with agile board"
},
{
"description": "GET all boards",
"request": {
"method": "GET",
"path": "/boards/1.0/recent",
"query": "maxResults=10"
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/json"
},
"body": {
"values": [
{
"name": "Business",
"id": 7243125415,
"type": "CORE",
"moduleKey": "core-mobile-board-service"
},
{
"name": "DEMO board",
"id": 5636926176,
"type": "SCRUM",
"moduleKey": "agile-mobile-board-service"
}
]
},
"matchingRules": {
"$.body.values[*].type": {
"regex": "SCRUM",
"match": "regex"
},
"$.body.values[*].id": {
"match": "type"
},
"$.body.values[*].name": {
"regex": "DEMO board",
"match": "regex"
},
"$.body.values[*].moduleKey": {
"regex": "agile-mobile-board-service",
"match": "regex"
},
"$.body.values": {
"min": 1,
"match": "type"
}
}
},
"providerState": "JIRA project with boards"
},
{
"description": "GET subscription setting",
"request": {
"method": "GET",
"path": "/cards/1.0/events/subscription"
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/json"
},
"body": {
"watcher": "false",
"boards": [
],
"assignee": "false",
"mentioned": "false",
"reporter ": "false"
}
},
"providerState": "User can fetch subscription setting"
},
{
"description": "GET board search",
"request": {
"method": "GET",
"path": "/boards/1.0/search",
"query": "q=board"
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/json"
},
"body": [
{
"name": "EL Board",
"id": 4622810293,
"type": "SCRUM",
"moduleKey": "agile-mobile-board-service"
},
{
"name": "DEMO board",
"id": 8283190000,
"type": "SCRUM",
"moduleKey": "agile-mobile-board-service"
}
],
"matchingRules": {
"$.body[*].type": {
"regex": "SCRUM",
"match": "regex"
},
"$.body": {
"min": 0,
"match": "type"
},
"$.body[*].name": {
"regex": "DEMO board",
"match": "regex"
},
"$.body[*].id": {
"match": "type"
},
"$.body[*].moduleKey": {
"regex": "agile-mobile-board-service",
"match": "regex"
}
}
},
"providerState": "User can search for a board"
}
],
"metadata": {
"pact-specification": {
"version": "2.0.0"
},
"pact-jvm": {
"version": "3.4.1"
}
}
}
Also , the tests are running on mac (local) and Linux (CI).
Assuming that a previous test was generating a Pact file that the current test could not read I started commenting things out and eventually discovered that when we replace the matchPath method call:
return builder
.given("User can fetch subscription setting")
.uponReceiving("GET subscription setting")
.matchPath("(/!?rest)?/cards/1.0/events/subscription", "rest/cards/1.0/events/subscription")
.method("GET")
.willRespondWith()
.status(HttpStatus.SC_OK).headers(ImmutableMap.of("Content-Type", "application/json"))
.body(GSON.toJson(response))
.toPact();
with the path method call:
return builder
.given("User can fetch subscription setting")
.uponReceiving("GET subscription setting")
.path("rest/cards/1.0/events/subscription")
.method("GET")
.willRespondWith()
.status(HttpStatus.SC_OK).headers(ImmutableMap.of("Content-Type", "application/json"))
.body(GSON.toJson(response))
.toPact();
it fixes the problem.
This pact mock configuration is part of a test that runs and passes prior to the test that fails. I've just tried to reproduce the problem in a simpler example and was unable to get it to fail so I'm not too sure what exactly is tripping up the parser.
I hope this helps you guys narrow in on the root cause problem.
Hey @uglyog , I tried using pact DSL directly for the consumer tests in hope that it will perhaps fix how pact file is read/written and also applied Ben's approach for path
matcher but realized it doesn't makes a difference and test still fails with the same JSON parsing error. Please suggest if there is any other data point we can work against?
From the stack trace, it looks like the failure happens when the test is successful, and pact-jvm is trying to merge the generated pact file with the one that already exists on disk. It is the one on disk that is causing the issue. Could you run a clean before the tests?
yeah I did run gradle clean
before every test run so that there is no pact file on disk.
Another observation, I have 3 classes and 4 unit tests and occasionally one test is not executed and the gradle test
succeeds without any error. However, on the subsequent run all the 4 tests run and one of them fails(with reported error) while writing to the existing pact file (generated from previous tests) . Not sure why 😕
On a hunch that since gradle test
run all the test-*
tasks in my Android project, test classes were included in more than one task and causing the dirty file state, hence I excluded the consumer test package from all but one test-*
task but unfortunately that didn't resolve the issue either 😞 .
Btw the test runs and generates pact file successfully if we run the test from IDE. Its the gradle test
where it trips.
Ok, we're getting closer. The different between running things in IntelliJ and Gradle is that Gradle could run tests concurrently in different threads. I'm assuming you're using the Android Gradle plugin? Are you're tests running as unit tests or instrumented tests (under androidTest)?
Thats correct, they are unit tests ( not the instrumented tests) . And yes we are using Android gradle plugin to run the tests.
Also, after making all the above changes, I think it has come to a point where all the tests are running successfully but the pact file doesn't capture all the interactions (2/4). Guess its just that I am not running into concurrency scenario now. Any hints?
Can you check that the four interactions have a unique description. They may be overwriting the older ones (the provider state and description should be unique).
I'm going to add a file system lock to the pact writing code to protect against this type of issue.
Thats right they are not always unique. This issue is surfacing because we were forking 4 test worker process for running our unit test using Junit runner. Gradle Test
task provides a system property maxParallelForks
to control the no of test process to execute in parallel.As a workaround, I have excluded the Pact test classes using the testOptions
and running them as a test task with single test process(default is maxParallelForks=1) .
In module gradle,
testOptions {
unitTests {
returnDefaultValues = true
all {
if (it.name != 'testNightlyDebugUnitTest') {
exclude 'com/atlassian/android/jira/core/contracts/**'
}
}
}
}
In root gradle,
tasks.withType(Test).whenTaskAdded { testTask ->
if (!testTask.name == 'testNightlyDebugUnitTest') {
testTask.maxParallelForks config.testForks // 4
testTask.testLogging config.testOptions
}
}
Looks a bit clunky though 😞
I definitely need to put that synchronisation check in before writing the pact file.
Version 3.5.2 has been released with synchronisation and file locking on the pact file.
Hey guys,
We have a pact-jvm(pact-jvm-consumer-junit_2.11:3.4.1) setup running the junit tests to generate the pact file.Recently we have started getting this error while running the tests. Stacktrace doesnt give a lot of info but looks like some discrepancy in reading the Pact file. Not sure if its how I am generating the response causing the issue.
This is one of the interaction setup where we are getting this error,
And this is how the json from pact file looks like,