Pact provider test returning 400 response on Windows but passing on OSX - pact

I am trying out sample code here https://github.com/DiUS/pact-workshop-dotnet-core-v3 with PactNet - 4.3.0. The only change I have made to the sample code is that i am not working with provider states in the test and instead have the required state set as part of controller only.
While it works fine on OSX when i try it on windows (Windows Server 2019 Standard) I get the following error -
PactNet.Exceptions.PactFailureException: Pact verification failed
PactNet.Exceptions.PactFailureException
Pact verification failed
at PactNet.Verifier.InteropVerifierProvider.Execute()
at PactNet.Verifier.PactVerifierSource.Verify()
at tests.ProductApiTestsWithoutBroker.EnsureProviderApiHonoursPactWithConsumer() in C:\repos\ProofOfConcepts\Pact-Product-api\ProviderTests\ProductApiTestsWithoutBroker.cs:line 47
at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
at System.Reflection.MethodInvoker.Invoke(Object obj, IntPtr* args, BindingFlags invokeAttr)
Getting to pact verification
Starting verification...
Pact verification failed
Verifier Output
---------------
Verifying a pact between ApiClient and ProductService
A valid request for all products
returns a response which
has status code 200 (FAILED)
includes headers
"Content-Type" with value "application/json; charset=utf-8" (FAILED)
has a matching body (FAILED)
Failures:
1) Verifying a pact between ApiClient and ProductService - A valid request for all products
1.1) has a matching body
expected 'application/json;charset=utf-8' body but was 'text/html'
1.2) has status code 200
expected 200 but was 400
1.3) includes header 'Content-Type' with value 'application/json; charset=utf-8'
Expected header 'Content-Type' to have value 'application/json; charset=utf-8' but was 'text/html'
There were 1 pact failures
Verifier Logs
-------------
2023-02-14T20:36:14.433512Z INFO ThreadId(33) pact_verifier: Running provider verification for 'A valid request for all products'
2023-02-14T20:36:14.433752Z INFO ThreadId(33) pact_verifier::provider_client: Sending request to provider at http://localhost:9001/
2023-02-14T20:36:14.433771Z INFO ThreadId(33) pact_verifier::provider_client: Sending request HTTP Request ( method: GET, path: /api/products, query: None, headers: None, body: Missing )
2023-02-14T20:36:14.456312Z INFO ThreadId(33) pact_verifier::provider_client: Received response: HTTP Response ( status: 400, headers: Some({"cache-control": ["no-store"], "via": ["1.1 ForcepointCGCluster"], "date": ["Tue", "14 Feb 2023 20:36:32 GMT"], "content-type": ["text/html"], "connection": ["close"], "content-language": ["en"], "content-length": ["666"]}), body: Present(666 bytes, text/html) )
2023-02-14T20:36:14.456421Z INFO ThreadId(33) pact_matching: comparing to expected response: HTTP Response ( status: 200, headers: Some({"Content-Type": ["application/json; charset=utf-8"]}), body: Present(130 bytes) )
2023-02-14T20:36:14.457552Z WARN ThreadId(33) pact_matching::metrics:
Please note:
We are tracking events anonymously to gather important usage statistics like Pact version and operating system. To disable tracking, set the 'PACT_DO_NOT_TRACK' environment variable to 'true'.
2023-02-14T20:36:14.495312Z WARN ThreadId(33) rustls::conn: Sending fatal alert DecodeError
I am using .NET 7 and i tried including PactNet.Windows nuget package but it did not help. Also does not look like long Windows path issue to me here. Any pointers would be very helpful here. Thanks in advance,

Solution:
Update PactNet package to the latest version.
Check the configuration of the environment in which you are running the tests. Ensure that the Content-Type and Content-Encoding headers are set correctly in the response.
Check the code of the API to make sure it is returning the correct response headers and body.

Related

HttpError 400 when requesting on Youtube commentThreads: Check the structure of the <code>commentThread</code> resource in the request body

I am tying to access my comments on other videos. But I didn't understand that what is wrong with the request. I put youtube.force-ssl scope.
(Pdb) request = self.service.commentThreads().list(part="id,replies,snippet", channelId="UCxz_B5DVTdZV5c0JLctjqUA")
INFO __init__.py line:49 2022-09-19 21:23:55,044 - file_cache is only supported with oauth2client<4.0.0
(Pdb) request.execute()
*** googleapiclient.errors.HttpError: <HttpError 400 when requesting https://youtube.googleapis.com/youtube/v3/commentThreads?part=id%2Creplies%2Csnippet&channelId=UCxz_B5DVTdZV5c0JLctjqUA&alt=json returned "The API server failed to successfully process the request. While this can be a transient error, it usually indicates that the request's input is invalid. Check the structure of the <code>commentThread</code> resource in the request body to ensure that it is valid.". Details: "[{'message': "The API server failed to successfully process the request. While this can be a transient error, it usually indicates that the request's input is invalid. Check the structure of the <code>commentThread</code> resource in the request body to ensure that it is valid.", 'domain': 'youtube.commentThread', 'reason': 'processingFailure', 'location': 'body', 'locationType': 'other'}]">
(Pdb) request.headers
{'accept': 'application/json', 'accept-encoding': 'gzip, deflate', 'user-agent': '(gzip)', 'x-goog-api-client': 'gdcl/2.52.0 gl-python/3.8.10', 'content-length': '0'}
(Pdb) request.uri
'https://youtube.googleapis.com/youtube/v3/commentThreads?part=id%2Creplies%2Csnippet&channelId=UCxz_B5DVTdZV5c0JLctjqUA&alt=json'
(Pdb) request.body
(Pdb) request.to_json()
'{"uri": "https://youtube.googleapis.com/youtube/v3/commentThreads?part=id%2Creplies%2Csnippet&channelId=UCxz_B5DVTdZV5c0JLctjqUA&alt=json", "method": "GET", "body": null, "headers": {"accept": "application/json", "accept-encoding": "gzip, deflate", "user-agent": "(gzip)", "x-goog-api-client": "gdcl/2.52.0 gl-python/3.8.10", "content-length": "0"}, "methodId": "youtube.commentThreads.list", "resumable": null, "response_callbacks": [], "_in_error_state": false, "body_size": 0, "resumable_uri": null, "resumable_progress": 0}'
(Pdb) request.body
On the other hand lines below return a valid 200 response.
(Pdb) request=self.service.commentThreads().list(part="id,replies,snippet", allThreadsRelatedToChannelId="UCxz_B5DVTdZV5c0JLctjqUA")
INFO __init__.py line:49 2022-09-19 22:06:17,504 - file_cache is only supported with oauth2client<4.0.0
(Pdb) request.execute()
I have enabled Youtube Data API and give youtube.force-ssl scope again on Google Cloud console but I am not sure that am I have to give any other permissions. According to the doc. channelId should be a valid argument but I didn't understand why it returns 400 error if I gave channelId.
Weirdest thing is that even the same example on Google's documentation does not work.
However if I give my channel's id with allThreadsRelatedToChannelId argument it returns a HTTP 200 responseas expected.
They had removed channelId parameter :( https://issuetracker.google.com/issues/247966553

Access token request results in 302 in Angular HttpClient

I'm trying to authenticate requests for WordPress rest-api using grant type password. OAuth2 authentication in WordPress is provided by WP OAuth Server plugin.
When I request access token using Postman Chrome app the server responds with expected access token object but the similar request doesn't work in Angular. It gives status 302 and due to xhr redirect to login page, I'm not able to get access token object. I'm using Angular 5.
Here's how I request access token in Angular:
/* Example token url
AuthProvider.TOKEN_URL:
https://www.example-wordpress.com/oauth/token
*/
const body = {
grant_type: 'password',
username: username,
password: password,
};
const headers = new HttpHeaders()
.set('Authorization', 'Basic ' + btoa(AuthProvider.CLIENT_ID + ':' + AuthProvider.CLIENT_SECRET));
this.http.post(AuthProvider.TOKEN_URL, body, { headers: headers });
The above request produces 302 with location header set to:
https://www.example-wordpress.com/login/?redirect_to=https%3A%2F%2Fwww.example-wordpress.com%2Foauth%2Ftoken
And then a xhr GET request is made to above location which responds with HTML of login page and hence no access token is obtained.
The similar POST request for access token in Postman works fine and results in expected access token object but I can't get it to work in Angular.
EDIT
While debugging I generated JavaScript code for access token request from Postman and pasted in console of Chrome after importing jQuery.
The request works as expected in console as well and no redirection occurs. The response is JSON with access token.
Here's the code Postman generated for the POST request:
var settings = {
"async": true,
"crossDomain": true,
"url": "https://example-wordpress.com/oauth/token",
"method": "POST",
"headers": {
"content-type": "application/x-www-form-urlencoded",
"authorization": "Basic M0wzakE3d080VmxxbXB0UUF1dUI5RkxicWxmeE8yR25Zdk4xQmxvbTp4TktTYnJ1Mno5cEp2VDFMbTNGNFhEQm10eDZzUGsya1FqZDg3VmQ2",
"cache-control": "no-cache",
"postman-token": "46339abe-2d1a-1032-f5d8-36e3193d9a81"
},
"data": {
"grant_type": "password",
"username": "my-username",
"password": "my-password",
"client_id": "3L3jA7wO4VlqmptQAuuB9FLbqlfxO2GnYvN1Blom",
"client_secret": "xNKSbru2z9pJvT1Lm3F4XDBmtx6sPk2kQjd87Vd6"
}
}
$.ajax(settings).done(function (response) {
console.log(response);
});
And here's the response logged from above code:
{
access_token: "rksen3p351fj0povsrpfv2eeuahrciglc3ilphhy",
expires_in: 3600,
token_type: "Bearer",
scope: "basic",
refresh_token: "fudju8tecbnwly2e1xgfv92tykvpsniwkfpvrd7d"
}
I'm unable to figure out why redirection occurs when we request through Angular and not responds with access token JSON.
Any help is appreciated.
access_token (which I imagine is what you expect to have) isn't part of the few headers that Angular is able to read without setting up your server.
Angular only read "basic" headers such as Content-type. This is because of the default CORS configuration that only reads Cache-Control, Content-Language, Content-Type, Expires, Last-Modified and Pragma. When it comes to custom headers, you have to tell your server to expose the headers.
This is done through the Access-Control-Expose-Headers header.
There was no problem at all. It was a very very silly mistake. I apologize.
I was testing with two websites simultaneously and both had similar configuration. The only difference was that one had OAuth plugin installed and other not. So when I tried to authorize the request from Angular with the website which hadn't had OAuth2 plugin installed and so redirected to the login page. The constant set for the AuthProvider.TOKEN_URL was incorrectly set, while when I was testing with other tools I was using correct url.
Anyway, this was all my mistake. It happens sometimes, when you don't take break. :)

.NET Core Patch JsonPatchDocument works locally but not on server

I have a very simple ASP.NET Core app which works perfectlly on my local machine.
When I publish my project everything works (GET, POST, DELETE) but not partially update (PATCH). I use JsonPatchDocument<T>. This is the code:
[HttpPatch("{id}")]
public IActionResult PartiallyUpdateNote(int id, [FromBody] JsonPatchDocument<NoteForUpdateDto> patches)
{
_log.LogInformation("Partial update of #{0}. Patches: {1}", id, patches);
if (patches == null) // <------ THIS IS ALWAYS TRUE
{
_log.LogError("Invalid patch format");
return BadRequest("Invalid patch format?");
}
This is json I try to send from Postman:
PATCH http://tomasz.look24.net/api/notes/1
headers:
Content-Type=application/json
Raw: [{ "op": "replace", "path":
"/title", "value": "ne555e" }]
This is log from server (single patch hit):
2017-02-27T22:46:46.1647691+01:00 0HL2VB6EI3TIB [INF] Connection id ""0HL2VB6EGP0A9"" bad request data: ""Unexpected end of request content"" (86f1a409)
Microsoft.AspNetCore.Server.Kestrel.BadHttpRequestException: Unexpected end of request content
at Microsoft.AspNetCore.Server.Kestrel.Internal.Http.MessageBody.ForContentLength.<ReadAsyncAwaited>d__4.MoveNext()
2017-02-27T22:46:46.1647691+01:00 0HL2VB6EI3TIB [INF] Executing action method "notes.api.Controllers.NotesController.PartiallyUpdateNote (notes.api)" with arguments (["1", ""]) - ModelState is Invalid (ba7f4ac2)
2017-02-27T22:46:46.1647691+01:00 0HL2VB6EI3TIB [INF] Partial update of #1. Patches: null (30b5833a)
2017-02-27T22:46:46.1647691+01:00 0HL2VB6EI3TIB [ERR] Invalid patch format (b8690943)
2017-02-27T22:46:46.1647691+01:00 0HL2VB6EI3TIB [INF] Executing ObjectResult, writing value "Microsoft.AspNetCore.Mvc.ControllerContext". (4e968210)
2017-02-27T22:46:46.1647691+01:00 0HL2VB6EI3TIB [INF] Executed action "notes.api.Controllers.NotesController.PartiallyUpdateNote (notes.api)" in 119502.6086ms (afa2e885)
2017-02-27T22:46:46.1647691+01:00 0HL2VB6EI3TIB [INF] Request finished in 119503.2131ms 400 text/plain; charset=utf-8 (15c52c40)
2017-02-27T22:46:46.1647691+01:00 [INF] Connection id ""0HL2VB6EGP0A9"" bad request data: ""Unexpected end of request content"" (86f1a409)
Microsoft.AspNetCore.Server.Kestrel.BadHttpRequestException: Unexpected end of request content
at Microsoft.AspNetCore.Server.Kestrel.Internal.Http.MessageBody.ForContentLength.ReadAsyncImplementation(ArraySegment`1 buffer, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Server.Kestrel.Internal.Http.MessageBody.Consume(CancellationToken cancellationToken)
at Microsoft.AspNetCore.Server.Kestrel.Internal.Http.Frame`1.<RequestProcessingAsync>d__2.MoveNext()
2017-02-27T22:46:46.1647691+01:00 [INF] Connection id ""0HL2VB6EGP0A9"" communication error (46f6d175)
Microsoft.AspNetCore.Server.Kestrel.Internal.Networking.UvException: Error -4077 ECONNRESET connection reset by peer
I got this log after 2 minutes with message:
Server Error
502 - Web server received an invalid response while acting as a
gateway or proxy server. There is a problem with the page you are
looking for, and it cannot be displayed. When the Web server (while
acting as a gateway or proxy) contacted the upstream content server,
it received an invalid response from the content server.
What can be wrong? Is there something missing in server configuration?
When I try to get data as a string like that:
public IActionResult PartiallyUpdateNote(int id, [FromBody] string patches)
Everything is fine on the server side. There must be something with PartiallyUpdateNote (I believe) which always return null.
I found the solution.
It works when I change PATCH to POST
[HttpPost("{id}")]
I wonder why?

How do I inject new request header with json data in proxy request flow?

I am trying to inject a new request header in the proxy request flow using JS policy to be sent to the backend server. When I look at the debug trace, I see that the json data in the request header is distorted.
I am trying to inject some string like
{"scope":"","time_till":2264,"id_1":"hUXLXVqpA1J4vA9sayk2UttWNdM","custom_data":{"c_id":"test_data"}}
But when I look at the trace window I see this
{"scope":"","time_till":2264,id_1":"hUXLXVqpA1J4vA9sayk2UttWNdM,"custom_data":{"c_id":"test_data"}}
what am I doing wrong?
var obj = {"scope":"","time_till":2264,"id_1":"hUXLXVqpA1J4vA9sayk2UttWNdM","custom_data":{"c_id":"test_data"}};
var header_str = JSON.stringify(obj);
context.setVariable('json-header',header_str);
request.headers['x-json-hedar']= header_str;
I tested your code and it seems to work. Here's an example response where I set the header string as a response:
HTTP/1.1 200 OK
User-Agent: curl/7.30.0
Accept: */*
x-json-header: {"scope":"","time_till":2264,"id_1":"hUXLXVqpA1J4vA9sayk2UttWNdM","custom_data":{"c_id":"test_data"}}
Content-Length: 0
It appears this is only an issue with the Apigee debug session / trace tool as the header value was set correctly. Here was the JSON download of the debug session showing this header value:
{
"name": "x-json-header",
"value": "{\"scope\":\"\",\"time_till\":2264,id_1\":\"hUXLXVqpA1J4vA9sayk2UttWNdM,\"custom_data\":{\"c_id\":\"test_data\"}}"
}
You can see that the value passed to the UI for displaying the debug info has the malformed json:
id_1\":\"hUXLXVqpA1J4vA9sayk2UttWNdM,
This does not appear to be a problem with the Apigee debug/trace UI. I see the malformed JSON trickle down to my backend service.
Here is the header I'm trying to send -
{"timeStamp":"2349218349381274","latitude":"34.589","longitude":"-37.343","clientIp":"127.0.0.0","deviceId":"MOBILE_TEST_DEVICE_AGAIN","macAddress":"23:45:345:345","deviceType":"phone","deviceOS":"iOS","deviceModel":"iPhone 5S","connection":"5G","carrier":"Vodafone","refererURL":"http://www.google.com","xforwardedFor":"129.0.0.0","sessionId":"kfkls498327ksdjf","application":"mobile-app","appVersion":"7.6.5","serviceVersion":"1.0","userAgent":"Gecko"}
But Apigee reads the header as below. Note the missing start quotes from some fields.
{"timeStamp":"2349218349381274",latitude":"34.589,longitude":"-37.343,clientIp":"127.0.0.0,deviceId":"MOBILE_TEST_DEVICE_AGAIN,macAddress":"23:45:345:345,deviceType":"phone,deviceOS":"iOS,deviceModel":"iPhone 5S,connection":"5G,carrier":"Vodafone,refererURL":"http://www.google.com,xforwardedFor":"129.0.0.0,sessionId":"kfkls498327ksdjf,application":"mobile-app,appVersion":"7.6.5,serviceVersion":"1.0,"userAgent":"Gecko"}
The header is used in a service callout to a backend service which parses it. And rightly so, I get the below error -
com.fasterxml.jackson.core.JsonParseException: Unexpected character ('l' (code 108)): was expecting double-quote to start field name
at [Source: java.io.StringReader#22549cdc; line: 1, column: 35]
at com.fasterxml.jackson.core.JsonParser._constructError(JsonParser.java:1378)
at com.fasterxml.jackson.core.base.ParserMinimalBase._reportError(ParserMinimalBase.java:599)
at com.fasterxml.jackson.core.base.ParserMinimalBase._reportUnexpectedChar(ParserMinimalBase.java:520)
at com.fasterxml.jackson.core.json.ReaderBasedJsonParser._handleUnusualFieldName(ReaderBasedJsonParser.java:1275)
at com.fasterxml.jackson.core.json.ReaderBasedJsonParser._parseFieldName(ReaderBasedJsonParser.java:1170)
at com.fasterxml.jackson.core.json.ReaderBasedJsonParser.nextToken(ReaderBasedJsonParser.java:611)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:301)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:121)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:2796)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:1942)
I encounter strange behaviour when adding JSON to a context variable for example like the following:
var header_str = JSON.stringify(obj);
context.setVariable('json-header',header_str);
I appreciate this is an example so you may not have included the full extent of the problem but this normally works (now it is not added to a variable first):
request.headers['x-json-header'] = JSON.stringify(obj);
Code like this also works if you can send the request from JavaScript
var headers = {"Accept": "application/json", "Accept-Language": "en"};
var sessionRequest = new Request(url, 'POST', headers, body);
var exchange = httpClient.send(sessionRequest);
exchange.waitForComplete()
if (exchange.isSuccess()){
var responseObj = exchange.getResponse().content.asJSON;
if (responseObj.error){
request.content += JSON.stringify(responseObj);
}
}
Also, I have had success with using an AssignMessage policy to build a request, followed by a Callout policy to read the stored request and then make that request and store the result in a response object which can then be read by an Extract Variables policy.

why is my grails integration testing of my web api failing?

I am developing an app that appends all the values of the form to a FormData() object e.g. "msgBody" and sends it to the grails server-side where it is consumed by jax-rs api. I have used something like:
GrailsWebRequest request = WebUtils.retrieveGrailsWebRequest()
def params = request.getParams()
if (!(params.msgBody.length() > 0)) {
log.error("Empty message body")
}
The problem I am having is with the integration testing of that api where I have written:
def headers = ['Content-Type': 'multipart/form-data; boundary=----WebKitFormBoundaryZ7dAiucrA3eTbjzI', 'Accept': 'application/json']
def content='{"msgBody":"Hello World"}'
sendRequest("/api/v1/message", 'POST', headers, content.bytes)
I keep on getting the "500 Internal Server Error" and error message:
"Caused by: java.lang.NullPointerException: Cannot invoke method length() on null object".
Why is this happening?
I have found out that you don't need to set the Content-Type in the headers and you also have to set all the fields of the FormData() object even if they are null. I did so but still I am getting the same NullPointerException message.

Resources