pact-foundation / pact-mock_service Goto Github PK
View Code? Open in Web Editor NEWProvides a mock service for use with Pact
Home Page: https://pact.io
License: MIT License
Provides a mock service for use with Pact
Home Page: https://pact.io
License: MIT License
I tried to run the latest Pact Mock Service (version 0.5.1) on my Mac as instructed on the release page, namely:
curl -LO https://github.com/bethesque/pact-mock_service/releases/download/v0.5.1/pact-mock-service-0.5.1-1-osx.tar.gz
tar xzf pact-mock-service-0.5.1-1-osx.tar.gz
cd pact-mock-service-0.5.1-1-osx
./pact-mock-service -p 1234
First of all, there's no pact-mock-service
file in the root of the unpacked directory. There's a bin
directory though, but when I try to start that one I get:
$ ./pact-mock-service -p 1234
Could not find awesome_print-1.6.1 in any of the sources
Run `bundle install` to install missing gems.
Here's the detailed output running with bash -x
:
$ bash -x ./pact-mock-service
+ set -e
++ dirname ./pact-mock-service
+ SELFDIR=.
++ cd .
++ cd ..
++ pwd
+ SELFDIR=/Users/treimann/Tasks/pact_mock_service/bin/pact-mock-service-0.5.1-1-osx
+ export BUNDLE_GEMFILE=/Users/treimann/Tasks/pact_mock_service/bin/pact-mock-service-0.5.1-1-osx/lib/vendor/Gemfile
+ BUNDLE_GEMFILE=/Users/treimann/Tasks/pact_mock_service/bin/pact-mock-service-0.5.1-1-osx/lib/vendor/Gemfile
+ unset BUNDLE_IGNORE_CONFIG
+ exec /Users/treimann/Tasks/pact_mock_service/bin/pact-mock-service-0.5.1-1-osx/lib/ruby/bin/ruby -rbundler/setup -I/Users/treimann/Tasks/pact_mock_service/bin/pact-mock-service-0.5.1-1-osx/lib/app/lib /Users/treimann/Tasks/pact_mock_service/bin/pact-mock-service-0.5.1-1-osx/lib/app/pact-mock-service.rb
Could not find awesome_print-1.6.1 in any of the sources
Run `bundle install` to install missing gems.
I realize that I am missing a dependency. However, my understanding of the stand-alone server was that I do not need to install Ruby or any Ruby dependencies. Am I missing something?
FWIW, when I execute the start command directly from the root directory without the bash wrapper, I get a bit further:
$ ls -1d *
bin
lib
$ ( export BUNDLE_GEMFILE=$(pwd)/lib/vendor/Gemfile && unset BUNDLE_IGNORE_CONFIG && lib/ruby/bin/ruby -rbundler/setup -I$(pwd)/lib/app/lib -I$(pwd)/lib/app/pact-mock-service.rb -p 1234 )
/Users/treimann/Tasks/pact_mock_service/bin/pact-mock-service-0.5.1-1-osx/lib/ruby/bin.real/ruby: No such file or directory -- 1234 (LoadError)
The error is a bit weird to me because the file does exist:
$ test -f /Users/treimann/Tasks/pact_mock_service/bin/pact-mock-service-0.5.1-1-osx/lib/ruby/bin.real/ruby && echo "ruby binary exists"
ruby binary exists
Side note: Version 0.5.1 seems to be fairly behind the latest code version (0.7.1). Would be great to see an update in this regards as well.
Hi!
I have 2 unit test written in js using ( pact-consumer-js-dsl ) that create interactions like in the README for that library e.g. alligators/Mary, alligators/Pete They are separated in two different files. In each file I set up the same logic:
describe("Client", function() {
var client, helloProvider;
beforeEach(function() {
//ProviderClient is the class you have written to make the HTTP calls to the provider
client = new ProviderClient('http://localhost:1234');
helloProvider = Pact.mockService({
consumer: 'Hello Consumer',
provider: 'Hello Provider',
port: 1234,
done: function (error) {
expect(error).toBe(null);
}
});
});
it("should say hello", function(done) {
helloProvider
.given("an alligator with the name Mary exists")
.uponReceiving("a request for an alligator")
.withRequest("get", "/alligators/Mary", {
"Accept": "application/json"
}).willRespondWith(200, {
"Content-Type": "application/json"
}, {
"name": "Mary"
});
helloProvider.run(done, function(runComplete) {
expect(client.getAlligatorByName("Mary")).toEqual(new Alligator("Mary"));
runComplete();
});
});
});
When I trigger them the pact json files are generated. But after they are generated the pact-mock-server writes in logs that:
ERROR Errno:ECONNRESET: An existing connection was forcibly closed by the remote host. @ io-Fillbuf - fd:5
I don't know if this is the correct way of behavior so want to know if those errors that I get are
`
After this error occurs, the server ( started on localhost:1234 ) as response to request localhost:1234/alligators/Mary (specified in the js code above) will response with:
Is this a BUG in pact-mock-server and how this can by solved?
P.S. These are errors of WebRick server, so in the internet people say that switching to thin server resolves this.
If one does not pass the --pact_dir
parameter the mock service, it only fails when the first interaction has succeeded, i.e., when the service wants to persist the generated Pact file.
IMHO, the service should already check whether the parameter has been passed on startup so that users receive an indication early enough. This is especially important in high-volume CI environments where extensive test suites should not even need to start if the mock service hasn't started successfully.
Since we do not want to setup each restful path and then test every single one for the same interactions, the ability to specify a generic path that works for all the same paths would be the best way to move forward.
This and the ability to test data schema would solve 90% of our use cases: #10
A new thread for a conversation started here: DiUS/pact-consumer-js-dsl#27 (comment)
I'm starting the pact mock service using this command:
pact-mock-service --port 1234 --log build-output/pact-mock-service-logs/pact.log --cors --pact-dir build-output/pact
Despite the fact I am passing a specific log file the file log/pact.log
is created with the following contents:
# Logfile created on 2015-03-03 00:22:16 +1100 by logger.rb/31641
If I'm doing it wrong please let me know. Otherwise I think this is a bug.
I am using pact-mock_service 0.4.1, Ruby 1.9.3p551 and OS X 10.10.2.
When setting a provider state like:
some_service.given('some scenario')
.upon_receiving('a request for questions')
.with(method: :get, path: '/questions', query: { question_ids: [1,2] })
and sending in query params hash is like this:
query_params_hash = { question_ids: [1,2] }
I encounter the following error:
Missing requests:
GET /questions?question_ids=1&question_ids=2
Unexpected requests:
GET /questions?question_ids[]=1&question_ids[]=2
The square brackets are missing from the expected request
Hi Beth,
I had so many discussions in my company about the same problem, that I'd like to expose it.
Just so we can talk about it, as I feel I'm missing some kind of capability.
We are "pact verifying" a lot of end points and there are always some properties on a JSON output that are not always there. Properties or even whole objects.
We kind of come to conclusion where we should either:
Here is a simple example of a list of categories for books:
{
"_embedded": {
"categories": [
{
"name": "Children",
"_links": {
"doc:books": {
"href": "http://example.com/list-of-books?category=children"
}
}
},
{
"name": "Factual",
"_links": {}
}
]
}
}
In this list of categories, the _links
object may or may not contain a url that point to a list of books.
This is how my response interaction looks like:
{
"_embedded": {
"categories": Pact.Matchers.eachLike({
"name": Pact.Matchers.somethingLike("Children"),
"_links": {
"doc:books": {
"href": Pact.Matchers.term({
matcher: "^(?=.*list-of-books)(?=.*category=)",
generate: "http://example.com/list-of-books?category=children"
})
}
}
})
}
}
When I come to verifying mocks, which may or may not have the links in each categories, the verification would fail as the pact interaction is expecting to always have a doc:books
links.
What are my solutions?
{
"_embedded": {
"categories": Pact.Matchers.eachLike({
"name": Pact.Matchers.somethingLike("Children")
})
}
}
Problem: it doesn't represent the reality, url are not tested
Benefit: can verify any mock files
{
"_embedded": {
"categories": [
{
"name": "Children",
"_links": {
"doc:books": {
"href": Pact.Matchers.term({
matcher: "^(?=.*list-of-books)(?=.*category=)",
generate: "http://example.com/list-of-books?category=children"
})
}
}
},
{
"name": "Factual",
"_links": {}
}
]
}
}
Problem: it still doesn't represent the reality, can't verify mock files that are different from this response
Benefit: can test both case of having and not having the links
How I would like to write the interaction
I don't know if that's feasible, but if I had some kind of an "optional matcher", that would be applied or not applied depending of the existence of a node, it would help me write the interaction. I would also be able to verify any mock files and it would also represent the reality.
Something like (see the <Pact.Matchers.optional> in the node property):
{
"_embedded": {
"categories": Pact.Matchers.eachLike({
"name": Pact.Matchers.somethingLike("Children"),
"_links": {
"<Pact.Matchers.optional>doc:books": {
"href": Pact.Matchers.term({
matcher: "^(?=.*list-of-books)(?=.*category=)",
generate: "http://example.com/list-of-books?category=children"
})
}
}
})
}
}
Any thought of how to approach this problem?
Hi,
I have followed the instructions mentioned here https://github.com/pact-foundation/pact-ruby-standalone/releases for the Linux x64 platform.
When I run the this command ./pact-mock-service start
I get the following message
INFO WEBrick 1.3.1
INFO ruby 2.2.2 (2015-04-13) [x86_64-linux]
WARN TCPServer Error: Cannot assign requested address - bind(2) for "::1" port 9222
INFO WEBrick::HTTPServer#start: pid=671 port=9222
I see this warning "TCPServer Error" only on the Linux Platform, I have tried Windows and OSX and both are working fine.
In the message it says it is a warning but in reality the server is not up or not accessible.
Steps to reproduce
docker run -it -p 9222:9222 microsoft/aspnetcore-build /bin/bash
curl -LO https://github.com/pact-foundation/pact-ruby-standalone/releases/download/v1.18.0/pact-1.18.0-linux-x86_64.tar.gz
tar xzf pact-1.18.0-linux-x86_64.tar.gz
cd pact/bin
./pact-mock-service start
Can you please shed some light on this issue.
Thanks
Hi,
We have clients in different languages so I want to use pact-mock_service via HTTP directly. I tried to create an interaction via curl but does not seem to be working. Am I missing something here? I tried to trace what pact ruby client passes in and it seems to match what I am doing below.
here is my request -
curl -H "Content-Type: application/json" -X POST -d "{\"description\":\"a request for an account\",\"provider_state\":\"an account exists\",\"request\":{\"method\":\"get\",\"path\":\"/accounts/1\",\"query\":\"include_deleted=false&include_locked=false\"},\"response\":{\"status\":200,\"headers\":{\"Content-Type\":\"application/json\"},\"body\":{\"id\":1,\"is_active\":true,\"name\":\"Test account\",\"subdomain\":\"supercool\",\"time_zone\":\"UTC\",\"created_at\":null,\"owner_id\":null,\"host_mapping\":null,\"updated_at\":null,\"locale_id\":5,\"sandbox_master_id\":null,\"is_serviceable\":true,\"help_desk_size\":\"1\",\"shard_id\":1,\"lock_state\":0,\"route_id\":null,\"deleted_at\":null,\"account_group_id\":1,\"dnsttl\":null}}}" localhost:5000/interactions
and the response -
{"message":"No interaction found for POST /interactions","interaction_diffs":[]}
I'm not sure what is the right way to fix this:
https://travis-ci.org/bethesque/pact-mock_service/builds/149614097
https://travis-ci.org/bethesque/pact-mock_service/builds/149669454
We have a custom header which has a key such as "APP_AUTH" and I'd like to keep this key in the pact contract. However, I believe pact-mock_serivce helps me reformat the custom header to be "App-Auth" and breaks the contract on the provider side.
After look around the source code with my bad ruby knowledge. I believe the source code is under pact-mock_service/lib/pact/consumer/mock_service/rack_request_helper.rb
line 47:
def standardise_header header
header.gsub(/^HTTP_/, '').split("_").collect{|word| word[0] + word[1..-1].downcase}.join("-")
end
Would you please tell me whether I can still keep the original contract format rather than to standardise it? I know "APP_AUTH" maybe is not a good name, however it's have been used by a lot of services which we can not easily modify.
Thanks for your help and looking forward to your reply. :)
Otherwise the error does not bubble up to the client (eg the javascript client) in a helpful way, as the response may not be logged.
I, [2014-12-17T16:16:36.687916 #52188] INFO -- : Received request POST /thing?lastName=Smith&firstName=Mary
D, [2014-12-17T16:16:36.688114 #52188] DEBUG -- : {
"path": "/thing",
"query": "lastName=Smith&firstName=Mary",
"method": "post",
"headers": {
"Content-Type": "application/x-www-form-urlencoded",
"Origin": "http://localhost:9876",
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X) AppleWebKit/534.34 (KHTML, like Gecko) PhantomJS/1.9.8 Safari/534.34",
"Referer": "http://localhost:9876/context.html",
"Accept": "*/*",
"Connection": "Keep-Alive",
"Accept-Encoding": "gzip",
"Accept-Language": "en-AU,*",
"Host": "localhost:1234",
"Version": "HTTP/1.1"
}
}
E, [2014-12-17T16:16:36.688534 #52188] ERROR -- : Error ocurred in mock service:
E, [2014-12-17T16:16:36.688849 #52188] ERROR -- : #<RuntimeError: Interaction with same description (another request for hello) and provider state () already exists>
D, [2014-12-17T16:16:36.689348 #52188] DEBUG -- : [
"/usr/local/var/rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/pact-mock_service-0.2.2/lib/pact/consumer/interactions_filter.rb:39:in `<<'",
"/usr/local/var/rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/pact-mock_service-0.2.2/lib/pact/consumer/mock_service/interaction_replay.rb:36:in `add_verified_interaction'",
"/usr/local/var/rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/pact-mock_service-0.2.2/lib/pact/consumer/mock_service/interaction_replay.rb:63:in `handle_matched_interaction'",
"/usr/local/var/rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/pact-mock_service-0.2.2/lib/pact/consumer/mock_service/interaction_replay.rb:48:in `find_response'",
"/usr/local/var/rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/pact-mock_service-0.2.2/lib/pact/consumer/mock_service/interaction_replay.rb:30:in `respond'",
"/usr/local/var/rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/pact-mock_service-0.2.2/lib/pact/consumer/mock_service/app.rb:67:in `call'",
"/usr/local/var/rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/rack-1.5.2/lib/rack/handler/webrick.rb:60:in `service'",
"/usr/local/var/rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/webrick-1.3.1/lib/webrick/httpserver.rb:138:in `service'",
"/usr/local/var/rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/webrick-1.3.1/lib/webrick/httpserver.rb:94:in `run'",
"/usr/local/var/rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/webrick-1.3.1/lib/webrick/server.rb:191:in `block in start_thread'"
After installing ruby and gems, we run a simple gem i pact-mock_service
which seems to download just fine and it's able to use the command, but it crashes since it's missing the json dependency.
It doesn't seem to download it automatically or it simply fails silently since the json dependency needs the dev kit to be installed as well.
I just upgrade pact-node to the latest version of the pact-mock-service, where it was using 0.7.2 before. It seems that the exit signal has changed since I used a to a SIGKILL on the process to close it and it worked, but now it has to be SIGINT. Personally, I think both should be supported by pact-mock_service since one is a graceful exit and the other is ungraceful, which should still work.
'Pact::ArrayLike' doesn't seem to work, is there support for flexible array matching?
An example:
{
'json_class': 'Pact::ArrayLike',
'contents': {colour: 'red'},
'min': 2
}
I am (indirectly) using pact-mock_service to test against a REST service that I do not control that requires HTTP header names of the form /[a-z]+(_[a-z]+)/, e.g., 'my_header'. These tests fail, and I find that somewhere between the client sending the request and the pact mock receiving it, header names get rewritten to the form /[A_Z][a-z](-[A-Z][a-z])/, e.g., 'My-Header'.
While the latter form is most common in the wild, the former seems still to be according to the following definitions from RFC 7230 (https://tools.ietf.org/html/rfc7230#page-22):
3.2. Header Fields
Each header field consists of a case-insensitive field name followed
by a colon (":"), optional leading whitespace, the field value, and
optional trailing whitespace.
header-field = field-name ":" OWS field-value OWS
field-name = token
...
token = 1*tchar
tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*"
/ "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
/ DIGIT / ALPHA
; any VCHAR, except delimiters
def standardise_header header
header.gsub(/^HTTP_/, '').split("_").collect{|word| word[0] + word[1..-1].downcase}.join("-")
end
All is well if I change the function to read,
def standardise_header header
header.gsub(/^HTTP_/, '').downcase
end
I do not completely understand what is happening. What is the purpose of the original transformation? Can we do with my version?
Thank you!
My Pact mock server is listening in different port from my tests and my pact mock server is running with following configuration:
const provider = pact({
consumer: 'myconsumer',
provider: 'myprovider',
port: 8989,
cors: true,
log: path.resolve(process.cwd(), 'logs', 'pact.log'),
dir: path.resolve(process.cwd(), 'pacts'),
logLevel: 'DEBUG',
spec: 1
});
I request the above mock server using 'axios' and the request is matched with one of the interaction i have configured on the provider (as per pact.log). But response always throws error with CORS.
My pact log has following information:
Received OPTIONS request for mock service administration endpoint POST /interactions. Returning CORS headers: {"Access-Control-Allow-Origin":"null","Access-Control-Allow-Headers":"x-pact-mock-service","Access-Control-Allow-Methods":"DELETE, POST, GET, HEAD, PUT, TRACE, CONNECT, PATCH"}.
why is Access-Control-Allow-Origin is null instead of '*'. ?
How can i disable CORS security completely with tests as the final resorts when i use webpack, npm and mocha based tests.?
Found a use case that doesn't work for us. IE 8, by default, will send the header Content-Type
with application/json; charset=utf-8;
while Chrome will sent it as application/json; charset=UTF-8;
. I'm certain that this is in fact browser specific.
The problem is that we need application/json to create the interaction needed on a put/post, but I can't specify it 'globally' because I need to specify the charset as well and there's no way to make it generic using Pact::Term. I've tried it, but it crashes. If I use it for all headers, it's giving me the error Error ocurred in mock service: NoMethodError - undefined method '[]' for #<Pact::Term:0x00000001c52e60>
, if I put it specifically for the Content-Type header, it's giving me Error ocurred in mock service: TypeError - can't convert Pact::Term to String
.
I imagine that there should be a way to say "Use this request as long as application/json is in the content-type" or any of the other headers, like accept.
In our case, we need to be able to make sure that the json schema returned back by an API is current (right properties, right data types), but the data itself is not really important.
Using a path matcher seems to work correctly from the consumer point of view, but the matcher details are missing from the outputted pact file.
The interaction sent to the mock server:
{
"description": "a request for an alligator with path matcher",
"provider_state": "an alligator exists",
"request": {
"method": "get",
"path": {
"json_class": "Pact::Term",
"data": {
"generate": "/alligators/1234",
"matcher": {"json_class":"Regexp","o":0,"s":"^\\/alligators\\/[0-9]{4}"}
}
}
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/json"
},
"body": {
"type": "alligator",
"name": "Mary"
}
}
}
And the resulting pact file:
{
"description": "a request for an alligator with path matcher",
"provider_state": "an alligator exists",
"request": {
"method": "get",
"path": "/alligators/1234"
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/json"
},
"body": {
"type": "alligator",
"name": "Mary"
}
}
}
This is probably related to #11
The configuration options and the post body are merged on that line to configure the ConsumerContractWriter
. As I found when investigating a problem integrating pact-js with pack-mock_service, this means that the fields sent in the request depend on the launch configuration: a client may not know how the server was launched and therefore whether to supply pact_dir
as part of the request body.
Further, the use of Hash#merge here means that the client can always override the configured pact_dir
, controlling the output folder. That means the client can force the server to e.g. fill up a different volume than the intended pact_dir
location, or write pacts to a folder not expected when the service was configured.
It seems that it should be an error not to provide a pact_dir
on launch, or a reasonable default should be chosen, and that the mock service should not read it from the post request on writing a pact.
My tests are working with chrome but failing with PhantomJs
It looks like the verification is done before the request with phantomjs.
The output I get from the pact-mock_service is:
I, [2016-05-16T15:41:49.828460 #79772] INFO -- : Registered expected interaction POST /api/v1/campaigns
D, [2016-05-16T15:41:49.828915 #79772] DEBUG -- : {
"description": "a campaign will be created",
"provider_state": "we submit a new campaign",
"request": {
"method": "post",
"path": "/api/v1/campaigns",
...
W, [2016-05-16T15:41:49.928726 #79772] WARN -- : Verifying - actual interactions do not match expected interactions for example "".
Missing requests:
POST /api/v1/campaigns
W, [2016-05-16T15:41:49.928768 #79772] WARN -- : Missing requests:
POST /api/v1/campaigns
I, [2016-05-16T15:41:49.930866 #79772] INFO -- : Received request POST /api/v1/campaigns
D, [2016-05-16T15:41:49.931029 #79772] DEBUG -- : {
I, [2016-05-16T16:26:14.649965 #81885] INFO -- : Found matching response for POST /api/v1/campaigns
on my test console:
โ 0 tests completed
โ 1 test failed
FAILED TESTS:
PactCampaignCreate
handleSubmit
โ should send the correct request
PhantomJS 2.1.1 (Mac OS X 0.0.0)
AssertionError: expected [Error:
pact-consumer-js-dsl: Pact verification failed
Actual interactions do not match expected interactions for mock MockService.
Missing requests:
POST /api/v1/campaigns
See standard out/err for details.
] to equal null (/Users/pierrecaserta/workspace/jupiter-admin/tests.webpack.js:24570 <- webpack:///~/chai/lib/chai/assertion.js:111:0)
Here is my test code:
let pactMockService = null;
let renderer = null;
let form = null;
beforeEach(() => {
pactMockService = Pact.mockService({
consumer: 'campaign create consumer',
provider: 'campaign create provider',
port: 1234,
done: (error) => {
expect(error).to.equal(null);
}
});
// pactMockService.resetSession(done);
});
describe('handleSubmit', () => {
beforeEach( () => {
const spy = chai.spy((d) => {
data = preprocessBeforeSend(d);
});
renderer = rendererValidation(createStore(browserHistory, client, mockStore), spy);
form = ReactTestUtils.findRenderedDOMComponentWithClass(renderer, "form-horizontal");
});
afterEach( () => {
ReactDOM.unmountComponentAtNode(ReactDOM.findDOMNode(renderer).parentNode);
});
it("should send the correct request", (done) => {
const responseBody = {"id": Pact.Match.somethingLike(1097),
"name":"492699",
"start_date":"2016-06-04T00:00:00Z",
"end_date":"2016-07-01T00:00:00Z",
"internal_code": Pact.Match.somethingLike("8bbddbe518ed43368fc932a1092e0983"),
"timezone":"Sydney",
"cpc":0.5,
"total_clicks":0,
"max_clicks":186,
"total_daily_clicks":0,
"max_daily_clicks":76,
"total_impressions":0,
"budget":197.0,
"advertiser_id":333555,
"active":true,
"priority_id":3,
"frequency_capped":true,
"freq_cap_time_unit":"day",
"freq_cap_period":10,
"freq_cap_count":5,
"freq_cap_type":"impression",
"created_at": Pact.Match.somethingLike("2016-05-09T04:36:33Z"),
"updated_at":Pact.Match.somethingLike("2016-05-09T04:36:33Z"),
"deleted_at":null,
"disable_daily_cap":false,
"target_platform":"both",
"target_category_ids":[6263, 6076, 6317],
"target_keywords":null,
"target_sites":null,
"target_location_ids":[6047981],
"target_subcategory_ids":[]
};
pactMockService
.given("we submit a new campaign")
.uponReceiving("a campaign will be created")
.withRequest({
method: "post",
path: "/api/v1/campaigns"
}).willRespondWith({
status: 201,
headers: {"Content-Type": "application/json; charset=utf-8"},
body: responseBody
});
pactMockService.run(done, (runComplete) => {
ReactTestUtils.Simulate.submit(form);
const headers = { "Accept": "*/*",
"Accept-Encoding":"gzip, deflate",
"Accept-Language":"en-US,en;q=0.8",
"Connection":"keep-alive",
"Content-Length":"491",
"Content-Type":"application/json",
"Host":"127.0.0.1:1234",
"X-APIKEY": "49b4102ec4aad5a025fd906b4867bf11" };
client.post('campaigns', {data: data, headers: headers}).then((r) => {
// I do not care about the fake response
expect(r.body.name).to.equal(responseBody.name);
}).catch((e) => {
console.log(e);
});
runComplete();
});
});
});
Hi,
I can't seem to be able to use somethingLike
matchers in a POST request (body and headers).
Is that normal? Am I missing something?
See this gist:
https://gist.github.com/soundstep/4991f67b98d274efbe18ef4eb0c53baa
The only matchingRules
created are for the body of the response.
FYI: I originally reported this issue on the js implementation: pact-foundation/pact-js#16
Any help appreciated.
Cheers.
Setting up an interaction using a withRequest
component as in
myProvider.withRequest({
method: "get",
path: "/resource?query=param"
});
leads to a No matching interaction found for GET /resource?query=param error on the mock service end.
However, if I split out the query parameter into a dedicated query
property as in
myProvider.withRequest({
method: "get",
path: "/resource",
query: { query: "param" }
});
the interaction is matched successfully.
While it should probably be good practice to separate the path from the query parameter, I think that the mock service should still consider the interaction as matching in this case in order to avoid confusion on the user's end. This seems especially important when employing the more concise syntax
myProvider.withRequest("get", "/resource?query=param")
where a separate object is not required to specify the request.
If making the mock service more flexible in this regards is not an option, then I'd suggest to at least improve the error message because the current one makes it fairly hard to see what the problem is.
We have a request which has an array at the top level of the body which I am unable to perform a type match on.
For example:
.withRequest({
method: 'post',
path: '/xxx/en',
body: {
"json_class": "Pact::SomethingLike",
"contents": ["REVISION_HISTORY"]
}
})
This request only matches request with ["REVISION_HISTORY"] in the body and not all arrays.
As per the conversation in pact-foundation/pact-net#103, it would be great if we can document how a user should be setting a request and/or response body that should explicitly be blank and have a Content-Length: 0.
Hi there
recently a bug was opened on Pact JS relating to having two interactions with the same endpoint but with one of the interactions differing only by a query string on the request.
Here's the issue: pact-foundation/pact-js#12
I've tested locally and got the same results. Here's the test I created as part of the Pact JS suite: https://github.com/pact-foundation/pact-js/blob/test-query-string/test/dsl/integration.spec.js#L137
Edit:
I suspect the issue is on Pact Mock Server, hence the bug. Below is the output of my log file. The Pact file gets created with only one interaction:
I, [2016-09-24T16:03:37.783385 #9663] INFO -- : Registered expected interaction GET /projects
D, [2016-09-24T16:03:37.783615 #9663] DEBUG -- : {
"description": "a request for projects",
"provider_state": "i have a list of projects",
"request": {
"method": "GET",
"path": "/projects",
"headers": {
"Accept": "application/json"
}
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/json"
},
"body": [
{
"id": 1,
"name": "Project 1",
"due": "2016-02-11T09:46:56.023Z",
"tasks": [
{
"id": 1,
"name": "Do the laundry",
"done": true
},
{
"id": 2,
"name": "Do the dishes",
"done": false
},
{
"id": 3,
"name": "Do the backyard",
"done": false
},
{
"id": 4,
"name": "Do nothing",
"done": false
}
]
}
]
}
}
I, [2016-09-24T16:03:37.785313 #9663] INFO -- : Registered expected interaction GET /projects?from=today
D, [2016-09-24T16:03:37.785473 #9663] DEBUG -- : {
"description": "a request for projects starting today",
"provider_state": "i have a list of projects starting today",
"request": {
"method": "GET",
"path": "/projects",
"query": {
"from": [
"today"
]
},
"headers": {
"Accept": "application/json"
}
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/json"
},
"body": [
{
"id": 1,
"name": "Project 1",
"due": "2016-02-11T09:46:56.023Z",
"tasks": [
{
"id": 1,
"name": "Do the laundry",
"done": true
},
{
"id": 2,
"name": "Do the dishes",
"done": false
},
{
"id": 3,
"name": "Do the backyard",
"done": false
},
{
"id": 4,
"name": "Do nothing",
"done": false
}
]
}
]
}
}
I, [2016-09-24T16:03:37.794977 #9663] INFO -- : Received request GET /projects
D, [2016-09-24T16:03:37.795086 #9663] DEBUG -- : {
"path": "/projects",
"query": "",
"method": "get",
"headers": {
"Host": "localhost:1234",
"Accept-Encoding": "gzip, deflate",
"User-Agent": "node-superagent/2.3.0",
"Accept": "application/json",
"Connection": "close",
"Version": "HTTP/1.1"
}
}
I, [2016-09-24T16:03:37.795500 #9663] INFO -- : Found matching response for GET /projects
D, [2016-09-24T16:03:37.795785 #9663] DEBUG -- : {
"status": 200,
"headers": {
"Content-Type": "application/json"
},
"body": [
{
"id": 1,
"name": "Project 1",
"due": "2016-02-11T09:46:56.023Z",
"tasks": [
{
"id": 1,
"name": "Do the laundry",
"done": true
},
{
"id": 2,
"name": "Do the dishes",
"done": false
},
{
"id": 3,
"name": "Do the backyard",
"done": false
},
{
"id": 4,
"name": "Do nothing",
"done": false
}
]
}
]
}
I, [2016-09-24T16:03:37.801958 #9663] INFO -- : Received request GET /projects?from=today
D, [2016-09-24T16:03:37.802037 #9663] DEBUG -- : {
"path": "/projects",
"query": "from=today",
"method": "get",
"headers": {
"Host": "localhost:1234",
"Accept-Encoding": "gzip, deflate",
"User-Agent": "node-superagent/2.3.0",
"Accept": "application/json",
"Connection": "close",
"Version": "HTTP/1.1"
}
}
E, [2016-09-24T16:03:37.802210 #9663] ERROR -- : Multiple interactions found for GET /projects?from=today:
D, [2016-09-24T16:03:37.802344 #9663] DEBUG -- : {
"description": "a request for projects",
"provider_state": "i have a list of projects",
"request": {
"method": "GET",
"path": "/projects",
"headers": {
"Accept": "application/json"
}
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/json"
},
"body": [
{
"id": 1,
"name": "Project 1",
"due": "2016-02-11T09:46:56.023Z",
"tasks": [
{
"id": 1,
"name": "Do the laundry",
"done": true
},
{
"id": 2,
"name": "Do the dishes",
"done": false
},
{
"id": 3,
"name": "Do the backyard",
"done": false
},
{
"id": 4,
"name": "Do nothing",
"done": false
}
]
}
]
}
}
D, [2016-09-24T16:03:37.802467 #9663] DEBUG -- : {
"description": "a request for projects starting today",
"provider_state": "i have a list of projects starting today",
"request": {
"method": "GET",
"path": "/projects",
"query": {
"from": [
"today"
]
},
"headers": {
"Accept": "application/json"
}
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/json"
},
"body": [
{
"id": 1,
"name": "Project 1",
"due": "2016-02-11T09:46:56.023Z",
"tasks": [
{
"id": 1,
"name": "Do the laundry",
"done": true
},
{
"id": 2,
"name": "Do the dishes",
"done": false
},
{
"id": 3,
"name": "Do the backyard",
"done": false
},
{
"id": 4,
"name": "Do nothing",
"done": false
}
]
}
]
}
}
I, [2016-09-24T16:03:37.806536 #9663] INFO -- : Writing pact with details {:consumer=>{:name=>"Consumer 1"}, :provider=>{:name=>"Provider 1"}}
I, [2016-09-24T16:03:37.806612 #9663] INFO -- : Writing pact for Provider 1 to /home/tarcio/workspace/pact/pact-js/pacts/consumer_1-provider_1.json
I, [2016-09-24T16:03:37.808968 #9663] INFO -- : Cleared interactions before example ""
I'm not sure where to post it - I encountered it when used pact + pact-mock_service, so, I've decided to post it here.
When you accidentaly forget to specify status
response option you can get strange result with Net::HTTP
:
Net::HTTPBadResponse:
wrong status line: "HTTP/1.1 0 "
I see such possible solutions:
0
200
- it's the most common expected response code IMO;I'm running into a strange issue in my pact tests which I believe I have isolated to the pact-mock_service. My service is .NET (C#) and in one of my interactions I setup an interaction with a DateTimeOffset string in the body the timezone of that string is changed (and the time correctly adjusted) when the pact file is written. Because developer machines may run on a different timezone than build machines, this causes issues.
To reproduce this, and eliminate the I called the admin REST endpoints directly. I set the timezone on my machine to Pacific Time (UTC-08:00). After starting the mock service, I made two calls:
First:
POST /interactions
{
provider_state: 'test',
description: 'test',
request: {
method: 'get',
path: '/test'
},
response: {
status: 200,
headers: {
'Content-Type': 'application/json; charset=utf-8'
},
body: {
datetimeoffset: '2012-12-12T01:11:11-05:00'
}
}
}
This returned:
200
Added interaction
Then:
POST /pact
{
provider: {
name: 'test-participant'
},
consumer: {
name: 'test-consumer'
}
}
This returned:
200
{
"provider": {
"name": "test-participant"
},
"consumer": {
"name": "test-consumer"
},
"interactions": [
{
"description": "test",
"provider_state": "test",
"request": {
"method": "get",
"path": "/test"
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/json; charset=utf-8"
},
"body": {
"datetimeoffset": "2012-12-11T22:11:11-08:00"
}
}
}
],
"metadata": {
"pactSpecificationVersion": "1.1.0"
}
}
Note that the timezone has been adjusted, although it still refers to the same point in time.
With the mock server still running, I can set my timezone to Eastern Time (UCT-05:00) and call POST /pact
again and get the same result (timezone adjusted). This suggests that something is happening to the date when the interaction is added.
I'm not familiar with Ruby so I wasn't able to debug into the server itself. Does this sound like an issue with the mock service, or am I just doing something wrong?
So all the interactions can be set up in one go.
Hey there!
I am thinking of using pact-mock_service with a microservice that accepts Avro JSON and binary data. Looks like currently pact-mock_service only support JSON data. I am curious about your thoughts on extending it to support different content types?
Best!
Saroj
See pact-foundation/pact-js#58 (comment) for some background.
If you start the mock service as follows:
pact-mock-service --cors true --port 1234
And then cURL it, you can see, the Access-Control-Allow-Origin: *
header is coming back, but nothing to do with methods it supports (e.g. Access-Control-Allow-Methods
or the other headers) . Further more, a cURL
with OPTIONS
is returning a 500 which means pre-flight requests won't work.
GET
and POST
(and HEAD
) requests don't require a pre-flight, which probably explains why they have worked to date, however PUT
and others do.
Looks like we don't support the OPTIONS pre-flight check, and probably explicitly allow all methods to be used.
The problem is about /pact
DELETE
. I have few tests within same consumer for multiple providers. When interactions for Provider A
are finished I call DELETE
on /pact
and I would like to go with next Provider B
. But when I do this I inherit Provider A
interactions in pact file of Provider B
, so for real they are not deleted.
Is there possibility to create pacts for multiple providers within one instance of pact-mock-service
?
Hey,
The links on the release page incorrectly prefix the version with a 'v', which causes a 404.
curl -LO https://github.com/bethesque/pact-mock_service/releases/download/v0.7.1/pact-mock-service-0.7.1-1-osx.tar.gz
should be
curl -LO https://github.com/bethesque/pact-mock_service/releases/download/0.7.1/pact-mock-service-0.7.1-1-osx.tar.gz
Note, there is no v in download/0.7.1/...
gp
This will work:
provider.given('initial state')
.uponReceiving('a request for all the data')
.withRequest('get', '/')
.willRespondWith({
status: 200,
headers: { 'Content-Type': 'application/json' },
body: 'weee'
});
This will not:
provider.given('initial state')
.uponReceiving('a request for all the data')
.withRequest('GET', '/')
.willRespondWith({
status: 200,
headers: { 'Content-Type': 'application/json' },
body: 'weee'
});
Spent a good hour trying to figure that tidbit out... Please fix.
Currently, I need the pact-mock_service to run as a service, and I am doing (in ruby)
system "pact-mock-service start -p #{PACT_PORT} -l pact/pact.log"
sleep 2
# `curl -XDELETE -H "X-Pact-Mock-Service: true" localhost:#{PACT_PORT}/interactions`
pact = JSON.parse(File.read('pactfile.json'))
pact['interactions'].each do |interaction|
`curl -XPOST -H "X-Pact-Mock-Service: true" -H "Content-Type: application/json" http://localhost:#{PACT_PORT}/interactions -d '#{interaction.to_json}'`
end
Is there a way to avoid splitting the interactions, and just send the mock service the entire pactfile and let it parse the interactions from it? That way it won't require ruby to parse the interactions
I'll investigate this further later, reporting so not to lose track of this bug.
When setting an expected interaction response with the following body payload, and posting to PACT, the server hangs until HTTP timeout.
Removing the regular expressions from the payload avoids the problem.
{
"title":"Application Form",
"questions":[
{
"label":"What is your name?",
"fields":[
{
"type":"text",
"placeholder":"Given name",
"identifier":"given-name"
},
{
"type":"text",
"placeholder":"Family name",
"identifier":"last-name"
}
]
},
{
"label":"What is your address?",
"fields":[
{
"type":"address",
"identifier":"address"
}
]
},
{
"label":"What is your tax file number?",
"fields":[
{
"type":"text",
"identifier":"tfn",
"placeholder":"TFN",
"validations":[
{
"type":"regexp",
"expression":"([\\d]{3}[^\\d]*){3}",
"message":"TFN requires 9 digits."
}
],
"processings":[
{
"type":"regexpReplace",
"expression":"[^\\d]",
"replace":""
}
]
}
]
},
{
"label":"Employment history:",
"fields":[
{
"type":"text",
"placeholder":"Employer name",
"identifier":"employer-name"
},
{
"type":"city",
"placeholder":"Employer city",
"identifier":"employer-city"
}
],
"questions":[
{
"repeatable":{
"min":1
},
"label":"Role in the company:",
"fields":[
{
"type":"date",
"placeholder":"Started",
"identifier":"start-date"
},
{
"type":"date",
"placeholder":"Ended",
"identifier":"end-date"
},
{
"type":"text",
"placeholder":"Title",
"identifier":"role-title"
},
{
"type":"text-area",
"placeholder":"Description",
"identifier":"role-desc"
}
]
}
]
}
]
}
When an interaction described in the test suite differs from the interaction sent by the client, the summary message is something like:
1) SomeProvider service View can add a new view :
No interaction found for PATCH /view/
It's easy to read this as implying that the problem is with the POST
or the /view/
not matching in the request, when it might be because the request fails to match for other reasons (such as a different body content or headers).
Further up, the detailed output says:
Pact verification failed!
Actual interactions do not match expected interactions for mock MockService.
Incorrect requests:
PATCH /view/0 (request body did not match)
See /Users/tjones/office/jobsearch-malcolm-ui/logs/pact.log for details.
2) "after each" hook for ""
I think the incorrect requests line there: PATCH /view/0 (request body did not match)
would be a better summary than the No interaction found
message.
If there's no easy way to pull a good summary for all the cases that cause No interaction
, then an easy improvement would be:
No interaction found for PATCH /view/ (see detailed output above)
Happy to contribute PRs if it would be helpful.
Adding this here to track the issue.
I'm working on a branch of the mock service here to implement pact-mock-service start using spawn instead of fork. I can get it to start up ok, but I can't work out how to kill the process gracefully (SIGTERM). It always needs to be killed with a SIGKILL, which means that the shutdown hook on the mock service won't execute.
Can I use the pact-mock-service as data-provider for my integration and unit test?
If it possible to implement the following scenario:
Thus populate the pack-mock-server with interactions and query it for responses from those interactions.
So we will use the interaction definitions as definition of mock data for our tests.
I have come across an issue when using a query parameter on the pact.with_request()
The issue is when your path has a trailing slash and you are including a query in your request
The resulting contract splits the path and query into separate keys under request but does not include the trailing slash as a part of the path.
I have included a link to a gist of an example test that generates a contract as well as the contract it generated in the comments
https://gist.github.com/nickshoust-wf/5850df10ec556632e8eee467f6819199
calling
bin\pact-mock-service.bat help
results in
ERROR: "pact-mock-service.rb service" was called with arguments ["help"]
Usage: "pact-mock-service.rb service"
Can you please remove the default --pact-dir
argument from pact-mock-service.bat
Are there any plans to add a possibility to use services that are not HTTP (ie. REST API) based?
I'm currently looking into adding messaging support to pact-js which ultimately uses pact-mock_service.
The most neat way for this would be to implement a service for message broker mocks here (RabbitMQ, NATS, Kafka...).
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.