I'm using the Pact gem (and loving it!) for my contract test suite. The API service I'm testing requires an authorization token for all requests.
I know how to generate an API token for my user, but I don't know where to place the token in the Pact workflow. I searched the Pact documentation and repo for examples but didn't have any luck.
I tried sending a POST in the consumer specs to generate a token, but the Pact mock server doesn't know what to do with the request and errors out (as I would expect).
I found this example and it seems promising, particularly the ability to assign predefined headers to all requests with a requestFilter
and the addHeader
method.
How can I use such a request filter with the Pact gem?
If that's not a current feature, what alternatives do I have?
UPDATE:
J_A_X's answer works great for creating the pacts with the mock server but it doesn't satisfy the API service provider's expectation of a valid auth token. More specifically, I need to dynamically insert valid auth tokens into the pacts upon running pact:verify. So, one step closer but still need to figure out the latter part.
Matthew's answer contains hints for what appear to be two possible solutions for the latter part (pact:verify). I hesitate to introduce another dependency so I'd love to get the ProxyApp class example working. I don't understand what exactly I'd pass into ProxyApp.new() though. Suggestions?
You actually don't have to use a real token for each pact interaction unless you really want/need to.
Normally for that kind of stuff, I just create a regex to be used on the header to validate certain rules while keeping it 'open'. In my node project (which uses the Ruby binary in the back), I created these 2 utilities functions to create objects with a pattern and another for a object minimum equal:
function term(matcher, generate) {
if ((typeof matcher === 'undefined') || (typeof generate === 'undefined')) {
throw 'Matcher and Generate arguments must be specified to use Term';
}
return {
"json_class": "Pact::Term",
"data": {
"generate": generate,
"matcher": {
"json_class": "Regexp",
"o": 0,
"s": matcher
}
}
};
}
function somethingLike(value) {
return {
"json_class": "Pact::SomethingLike",
"contents": value
};
}
You can then use it in your DSL definition like so:
mockService
.given('a form')
.uponReceiving('a GET request with a valid auth')
.withRequest('get', '/', term('^Bearer (?!null$).+$', 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ'))
.willRespondWith({
status: 200,
headers: {'Content-Type': 'application/json;charset=utf-8'},
body: {worked:true}
});
The 'term' utility has a regex as the first parameter and then an example (that should match the first) of what to use during the test.
I know that this needs to be expanded better within Pact itself to make it simpler to use. I hope this helps.
The Ruby implementation of Pact doesn't support this directly as per the JVM implementation.
If you're using the Pact Provider Proxy gem, you could take a look at some of the options discussed at https://github.com/realestate-com-au/pact/issues/49#issuecomment-65346357 and https://groups.google.com/forum/#!topic/pact-support/tSyKZMxsECk.
An example might look something like:
class ProxyApp
def initialize real_app
@real_app = real_app
end
def call env
@real_app.call(env.merge('HTTP_AUTHORIZATION' => '12345'))
end
end
Pact.service_provider "Some Provider" do
app do
ProxyApp.new(RealApp)
end
honours_pact_with "Some Consumer" do
#...
end
end