Pact provider verification fails with : For input string: "\null" - pact

I am trying to validate on the provider side but getting error -
Verifying a pact between DataConsumer and DataProvider
[Using File pact/DataConsumer-DataProvider.json]
Given some state
a request for json data
Request Failed - For input string: "\null"
Not sure what did I miss here.
My Pojo -
#EqualsAndHashCode
#RequiredArgsConstructor
#Builder(toBuilder = true)
#JsonDeserialize(builder = DataModel.DataModelBuilder.class)
public class DataModel {
#JsonProperty("name")
private final String name;
#JsonProperty("price")
private final double price;
}
Pact -
{
"provider": {
"name": "DataProvider"
},
"consumer": {
"name": "DataConsumer"
},
"interactions": [
{
"description": "a request for json data",
"request": {
"method": "GET",
"path": "/get/ice/2.0"
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/json; charset\u003dUTF-8"
},
"body": {
"price": 10,
"name": "some name"
},
"matchingRules": {
"header": {
"Content-Type": {
"matchers": [
{
"match": "regex",
"regex": "application/json(;\\s?charset\u003d[\\w\\-]+)?"
}
],
"combine": "AND"
}
}
},
"generators": {
"body": {
"$.name": {
"type": "ProviderState",
"expression": "\\${name}",
"dataType": "STRING"
},
"$.price": {
"type": "ProviderState",
"expression": "\\${price}",
"dataType": "FLOAT"
}
}
}
},
"providerStates": [
{
"name": "some state"
}
]
}
],
"metadata": {
"pactSpecification": {
"version": "3.0.0"
},
"pact-jvm": {
"version": "3.6.15"
}
}
}
Test -
#ExtendWith(SpringExtension.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#Provider("DataProvider")
#PactFolder(value = "pact")
public class ContractVerificationTest {
#TestTemplate
#ExtendWith(PactVerificationSpringProvider.class)
void pactVerificationTestTemplate(PactVerificationContext context) {
context.verifyInteraction();
}
#State("some state")
void testPact() {
}
}
Code -
https://github.com/nrworld4/pact-consumer-demo
https://github.com/nrworld4/pact-demo-provider

You aren't returning the values (name, price) from your provider state annotation in your provider test (it's currently doing nothing) so when Pact tries to replace the values dynamically in the request they are null.
Do you actually need them to be generated by the provider in the first place?
See
https://pactflow.io/blog/injecting-values-from-provider-states/ for a detailed example of how to use and fix.
Update
Could it be that you're double escaping the parameters?
In the example:
.queryParameterFromProviderState("accountNumber", "\${accountNumber}", "100")
In your code:
.valueFromProviderState("price", "\\${price}", 10.0)

Related

PACT jvm matching rules are being ignored when running test

I'm using PACT JVM https://github.com/DiUS/pact-jvm/tree/master/provider/pact-jvm-provider-junit
I don't know why the matching rules in my contact are being ignored.
My HTTP test
#RunWith(SpringRestPactRunner.class) // Say JUnit to run tests with custom Runner
#Provider("provDataTest")// Set up name of tested provider
#PactBroker(host="pact-broker.services", port = "80")
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT,
properties = {"spring.profiles.active=dev"},
classes = Application.class)
public class ContractTesting {
private static String token;
#BeforeClass //Method will be run once: before whole contract test suite
public static void setUpService() {
//Run DB, create schema
//Run service
//...
System.out.println("Now service in default state");
}
#TargetRequestFilter
public void exampleRequestFilter(HttpRequest request) {
}
#State("Check correct data json structure in case code: 401")
public void checkDataInCaseUnauthorized() {
}
#TestTarget // Annotation denotes Target that will be used for tests
public final Target target = new HttpTarget(8082);
And my Contract file
{
"provider": {
"name": "provDataTest"
},
"consumer": {
"name": "test"
},
"interactions": [
{
"description": "Read all links_401",
"request": {
"method": "GET",
"path": "/v1/consumer/me/link_social_account",
"headers": {
"invalidAuth": "401"
}
},
"response": {
"status": 401,
"headers": {
"Content-Type": "text/json;charset=utf-8"
},
"body": {
"error": {
"code": 401,
"message": "session incorrect",
"errors": []
}
},
"matchingRules": {
"body": {
"$.error": {
"matchers": [
{
"match": "type"
}
],
"combine": "AND"
},
"$.error.code": {
"matchers": [
{
"match": "integer"
}
],
"combine": "AND"
},
"$.error.message": {
"matchers": [
{
"match": "type"
}
],
"combine": "AND"
},
"$.error.errors[*].message": {
"matchers": [
{
"match": "type"
}
],
"combine": "AND"
},
"$.error.errors[*].field": {
"matchers": [
{
"match": "type"
}
],
"combine": "AND"
}
},
"header": {
"Content-Type": {
"matchers": [
{
"match": "text/json;charset=utf-8",
"regex": "regex"
}
],
"combine": "AND"
}
}
}
},
"providerStates": [
{
"name": "Check correct data json structure in case code: 401"
}
]
}
],
"metadata": {
"pactSpecification": {
"version": "3.0.0"
},
"pact-jvm": {
"version": "3.2.11"
}
}
}
And show error after run
Read all links_401 returns a response which has a matching body
Read all links_401 returns a response which has a matching
body=BodyComparisonResult(mismatches={/=[BodyMismatch(expected=[B#480e8a7a, actual=[B#10378c35, mismatch=Actual body '[B#10378c35' is not equal to the expected body '[B#480e8a7a', path=/, diff=null)]},
diff=[-{"error":{"code":401,"message":"session incorrect","errors":[]}},
+{"error":{"code":401,"message":"session incorrect, please login again (CR_A1004)","errors":[]}}])
I have tried to change regex but the problem is still happening.
It is only correct when message data correct.
Please help to give my point incorrect.
Thanks,
The problem is with your content-type header: "Content-Type": "text/json;charset=utf-8". The proper content type for JSON payloads should be application/json. That is why the matchers are not being applied, because the body is being treated as a text payload. Just change the content type and then the body will be parsed as a JSON document and the matchers will be applied to the fields.

ServiceStack: OpenApi import in Azure Api Management Gateway

We are running a Dotnet Core 2.2 service using ServiceStack 5.7, and need to throttle it. So we want to put it behind a Azure Api Management Gateway (apim) - it runs in a Azure App Service.
We have enabled OpenApi feature using
self.Plugins.Add(new OpenApiFeature());
When we export our OpenApi definition we get the following:
"paths": {
...
"/api/search": {
"post": {
"tags": [
"api"
],
"operationId": "SearchRequestsearch_Post",
"consumes": [
"application/x-www-form-urlencoded"
],
"produces": [
"application/json"
],
"parameters": [
{
"name": "Filters",
"in": "formData",
"type": "array",
"items": {
"$ref": "#/definitions/FilterDto"
},
"collectionFormat": "multi",
"required": false
}
],
"responses": {
"200": {
"description": "Success",
"schema": {
"$ref": "#/definitions/SearchResponse"
}
}
},
"deprecated": false,
"security": [
{
"Bearer": []
}
]
},
"parameters": [
{
"$ref": "#/parameters/Accept"
}
]
}
}
...
"definitions": {
"FilterDto": {
"title": "FilterDto",
"properties": {
"Field": {
"description": "The field to filter on",
"type": "string",
"enum": [
"None",
"DestinationName",
"DocumentId"
]
},
"Values": {
type": "array",
"items": {
"type": "string"
}
},
"Type": {
"type": "string",
"enum": [
"Equals",
"NotEquals",
"RangeNumeric",
"RangeDate"
]
}
},
"description": "FilterDto",
"type": "object"
}
...
}
The problem is that it is not supported to have a parameter with an array of a type (defined in #/definitions/FilterDto). And it fails with:
Parsing error(s): JSON is valid against no schemas from 'oneOf'. Path 'paths['/api/search'].post.parameters[1]', line 1, position 666.
Parsing error(s): The input OpenAPI file is not valid for the OpenAPI specificate https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md (schema https://github.com/OAI/OpenAPI-Specification/blob/master/schemas/v2.0/schema.json).
In the Azure portal.
In c# (ServiceStack) we have defined the following:
public class SearchRequest : SearchRequestBase, IReturn<SearchResponse>
{
public SearchRequest()
{
Filters = new List<FilterDto>();
}
[ApiMember(Name = "Filters"]
public List<FilterDto> Filters { get; set; }
}
public class FilterDto
{
[ApiMember(Name = "Field"]
[ApiAllowableValues("Field", typeof(FilterAndFacetField))]
public FilterAndFacetField Field { get; set; }
[ApiMember(Name = "Values")]
public List<string> Values { get; set; }
[ApiMember(Name = "Type")]
[ApiAllowableValues("Type", typeof(FilterType))]
public FilterType Type { get; set; }
public FilterDto()
{
Values = new List<string>();
}
}
Have anyone successfully managed to import a OpenApi using array of $ref in the parameters from ServiceStack into a Api Management?

Is there any way we can only validate field name not value in Pact json

The example below is the response. In this I just want to validate field not field value from the body.
When I do pact verify:
{
"provider": {
"name": "provider"
},
"consumer": {
"name": "consumer"
},
"interactions": [
{
"description": "A valid data read request",
"request": {
"method": "GET",
"path": "/v1/users",
"query": {
"user": [
"1"
]
}
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/json"
},
"body": {
"name": "xyz",
"work": "gen",
"age": "29",
}
},
"providerStates": [
{
"name": "user exists state"
}
]
}
],
"metadata": {
"pactSpecification": {
"version": "3.0.0"
},
"pact-jvm": {
"version": "4.0.4"
}
}
}
Only i need to validate field such as name, age and work not field value.
got solution we can achieve this using
final DslPart body = new PactDslJsonBody()
.stringType("name", "xyz")
.stringType("work", "gen")
.integerType("age", 29)
or for only optional
DslPart body = new PactDslJsonBody()
.eachKeyLike("name", PactDslJsonRootValue.stringType("xyz"))
.eachKeyLike("work", PactDslJsonRootValue.stringType("gen"))
.eachKeyLike("age", PactDslJsonRootValue.integerType(29))
.asBody()
and set this body to PactDslWithProvider body and we can have matching rule generated in the json so that when we verify only the field type and field name

How to generate mock server for pact consumer from contract json file?

I want to use contract file from provider to run tests against consumer.
I have
{
"provider": {
"name": "Provider"
},
"consumer": {
"name": "Consumer"
},
"interactions": [
{
"description": "Get data",
"request": {
"method": "Get",
"path": "/data/1"
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/json"
},
"body": {
"message": ""
}
},
"providerState": "state"
}
],
"metadata": {
"pact-specification": {
"version": "2.0.0"
},
"pact-jvm": {
"version": "3.5.6"
}
}
And want to use it to generate pact mock server like:
RequestResponsePact pact = new RequestResponsePact(mockServerDescriptionString);
Is it possible to do this?
No. But you can use the pact-stub-server or the pact-stub-service CLI

Using Pact.eachLike() when array contents vary for each item

Hi I have a Consumer test produced using Pact NPM https://www.npmjs.com/package/pact
I use the following code to generate a pact.json:
provider
.addInteraction({
state: 'test',
uponReceiving: 'a test,
withRequest: {
method: 'GET',
path: '/test'
},
willRespondWith: {
status: 200,
headers: { 'Content-Type': 'application/json' }
body: {
"company": like("My big company"),
"factories": eachLike({
"location": like("Sydney"),
"capacity": like(5)
},{min:1})
}
}
})
.then(function(){ done(); });
It generates the following testconsumer-testprovider.json file:
{
"consumer": {
"name": "TestConsumer"
},
"provider": {
"name": "TestProvider"
},
"interactions": [
{
"description": "a request for loans",
"providerState": "broker is logged in, list all loans",
"request": {
"method": "GET",
"path": "/test"
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/vnd.hal+json"
},
"body": {
"company": "My big company",
"factories": [
{
"location": "Sydney",
"capacity": 5
}
]
},
"matchingRules": {
"$.headers.Content-Type": {
"match": "regex",
"regex": "application\\/.*json.*"
},
"$.body.company": {
"match": "type"
},
"$.body.factories": {
"min": 1
},
"$.body.factories[*].*": {
"match": "type"
},
"$.body.factories[*].location": {
"match": "type"
},
"$.body.factories[*].capacity": {
"match": "type"
}
}
}
}
],
"metadata": {
"pactSpecification": {
"version": "3.0.0"
}
}
}
However when we test against the following provided output we get an error with the geographicCoords because it's an unexpected key/value:
{
"company": "My Company",
"factories": [
{
"location": "Sydney",
"capacity": 5
},
{
"location": "Sydney",
"geographicCoords": "-0.145,1.4445",
"capacity": 5,
}
]
}
So is there a was to allow unexpected key/values in arrays because we're only test for required key/values and we don't want out pact tests to fail in future when new values are added to our providers.
The scenario you are describing is supported, see https://github.com/pact-foundation/pact-js/tree/master/examples/e2e for an example.
If you were to remove, say the eligibility object and run the tests everything still works.
If you are still having troubles, please raise a defect on the pact-js repository and we'll get to the bottom of it.

Resources