PACT jvm matching rules are being ignored when running test - pact

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.

Related

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

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)

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

In swagger-editor can I control whether to send an empty parameter or not send the parameter

Swagger-editor sends optional array parameters even when they are empty.
Is there a way to specify in Swagger 2.0 to not send the optional parameter if it is an empty array?
The target of the request is intended to be a list of Cats and/or a list of Dogs - neither of which are required.
I was hoping that if the request contains only Cats, the caller would only provide a list of Cats, and not have to also send an empty list of Dogs.
The swagger-editor enforces the empty list.
POST http://example:9090/api/testpath HTTP/1.1
Content-Type: application/json
{
"Dogs": [
1
],
"Cats": []
}
I am not sure whether this would be a valid request.
POST http://example:9090/api/testpath HTTP/1.1
Content-Type: application/json
{
"Dogs": [
1
]
}
This is an example swagger specification
{
"swagger": "2.0",
"info": {
"title": "demo",
"version": "Draft 0.1.0"
},
"host": "example:9090",
"schemes": [
"http",
"https"
],
"basePath": "/api",
"produces": [
"application/json"
],
"paths": {
"/testpath": {
"post": {
"summary": "Request with a complex target\n",
"parameters": [
{
"name": "target",
"in": "body",
"required": true,
"description": "Array of collars and/or mobs to target with this request.",
"schema": {
"$ref": "#/definitions/TargetObjects"
}
}
],
"responses": {
"200": {
"description": "The request was accepted."
}
}
}
}
},
"definitions": {
"TargetObjects": {
"type": "object",
"description": "Target of a request may be one or more objects or different types.\n",
"properties": {
"Dogs": {
"description": "A list of Dogs.",
"type": "array",
"items": {
"type": "number",
"format": "int64"
}
},
"Cats": {
"description": "A list of Pets.",
"type": "array",
"items": {
"type": "number",
"format": "int64"
}
}
}
}
}
}

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