-
-
Notifications
You must be signed in to change notification settings - Fork 349
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
MatchersV3.date
and MatchersV3.time
do not use example value on the provider side
#1076
Comments
No, it isn't. Matchers are for loosening the need for the provider to expect the exact same data used in the consumer tests. Have a look at the documentation on matchers |
I'd say the purpose of
This issue is about the second case. I've created an example to illustrate the use-case. The important interaction is "A sample request with Matcher on request query string": it uses $ http GET 'localhost:8080/test-query?t=10:11:12'
HTTP/1.1 200 OK
access-control-allow-origin: *
content-length: 33
content-type: application/json
date: Sun, 19 Mar 2023 08:11:10 GMT
{
"answer": "42",
"time": "10:11:12"
}
$ http GET 'localhost:8080/test-query?t=11:11:12'
HTTP/1.1 200 OK
access-control-allow-origin: *
content-length: 33
content-type: application/json
date: Sun, 19 Mar 2023 08:11:13 GMT
{
"answer": "42",
"time": "10:11:12"
} The provider will be verified with a value of {
"consumer": { ... },
"interactions": [
{
"description": "A request where Matcher is used on request",
"request": {
"generators": {
"query": {
"$.t[0]": {
"format": "HH:mm:ss",
"type": "Time"
}
}
},
"method": "GET",
"path": "/test-query",
"query": {
"t": [
"10:11:12"
]
}
},
... By default that is the value of "now". While running the provider verification with debug logging:
See that the generated value is now ( Example Pact: import { pactOptions } from "@/pact/pact-options";
import { pactWith } from "jest-pact/dist/v3";
import { MatchersV3 } from "@pact-foundation/pact";
import "isomorphic-fetch";
global.setImmediate = jest.useFakeTimers as any;
pactWith(pactOptions, (interaction) => {
interaction(
"A sample request with exact response data",
({ provider, execute }) => {
beforeEach(() => {
return provider
.uponReceiving("A request where exact data needs to be returned")
.withRequest({
method: "GET",
path: "/test-exact",
})
.willRespondWith({
status: 200,
body: {
answer: "42",
time: "10:11:12",
},
});
});
execute("responds with exact data", async (mockserver) => {
await fetch(mockserver.url + "/test-exact");
});
}
);
interaction(
"A sample request with typed response data",
({ provider, execute }) => {
beforeEach(() => {
return provider
.uponReceiving("A request where typed data needs to be returned")
.withRequest({
method: "GET",
path: "/test-typed",
})
.willRespondWith({
status: 200,
body: {
answer: MatchersV3.like("42"),
time: MatchersV3.time("HH:mm:ss", "10:11:12"),
},
});
});
execute("responds with generated time", async (mockserver) => {
await fetch(mockserver.url + "/test-typed");
});
}
);
interaction(
"A sample request with Matcher on request query string",
({ provider, execute }) => {
beforeEach(() => {
return provider
.uponReceiving("A request where Matcher is used on request")
.withRequest({
method: "GET",
path: "/test-query",
query: {
t: MatchersV3.time("HH:mm:ss", "10:11:12"),
},
})
.willRespondWith({
status: 200,
body: {
answer: MatchersV3.like("42"),
time: "10:11:12",
},
});
});
execute("responds with time from request", async (mockserver) => {
await fetch(mockserver.url + "/test-query?t=10:11:12");
});
}
);
}); |
Interesting, this bit in the the JSON will compute now + 2 hours for provider verification: "generators": {
"query": {
"$.t[0]": {
"format": "HH:mm:ss",
"expression": "+ 2 hours",
"type": "Time"
}
}
} |
I think there’s a misunderstanding here- either yours or mine. As I understand it, one way you can think of matchers is “this test covers every case that passes this matcher”. It’s not a description of the API- if you need an exact match, you just use the exact value. I’m not completely across generators, as they’re after my time as a pact maintainer - but my understanding is that they’re just a way of building default values for matchers. Following this, it would be valid for a pact implementation to use the generator OR the example during verification. If you need an exact value during verification, it should be written into the pact as an exact value. |
The problem especially with date and time values is that sometimes the domain requires them to be in the future, but maybe also not too much in the distant future. In these cases, static values won't cut it. Consider a hotel reservation system where reservations might be acceptable from today until today + 1 year, but not beyond. You could use generators for this, as the docs for date/time expressions state:
The consumer could specify a reservation request for Perhaps the naming of the API is a bit misleading. But... It appears that the On the other hand, I can see why both concepts are included in a single API. As far as I understand it generating values is also useful when you run the stub server, where maybe you want to return stubbed responses including timestamps that always reflect a future date. |
I think normally you would handle that case with provider state- ie, “the time is {some specific time}” |
Docs on generators do exist:
Despite all of the above, this issue exists because No matchers appear to handle expressions for date and time generation currently. |
Looks like @agross has the right point here. If other Otherwise, while they do behave differently at the moment, it is confusing and causes issues. |
Yes, I agree that it would be better to be consistent - even if the behaviour is an implementation detail rather than an expectation, it would be an improvement to keep that implementation detail consistent across all matchers. I also agree that there's a naming issue with matchers in general - they're more like My point was that I don't think it's expected behaviour that the provider verification will use the example (although it might). I think appropriate uses of matchers would mean that the test would be valid with any example that passes the matcher. If you need a specific, exact value, then that specific value should be used instead of a matcher. If it is the case that there needs to be some constraints on the value (like the example of "within |
You're right, the definitions are slightly different:
vs
The My thinking here is that you should be opting in to generators explicitly, and that by having generators mixed into a matcher makes the behaviour confusing and unexpected. I could see a few ways to do it:
@uglyog is the expression language linked above designed for use outside of plugins? |
These predate plugins, and are supported with V3 generators |
Software versions
v16.19.0
Issue Checklist
Please confirm the following:
Expected behaviour
We use
MatchersV3.date
andMatchersV3.time
. The docs state:The expected behavior is that the provider will receive the passed example value of
10:11:12
:Actual behaviour
The provider receives a generated date/time value, despite examples being given.
The Pact JSON file also contains
generators
for such date or time matchers.Seems to be related to #620 which fixed it for
MatchersV3.datetime
.While researching this I came across this document which seems to indicate that date/time values on the provider side could be dynamically generated. How can such expressions be included when generated values are used (i.e. no concrete examples are given, but a recipe describing the values to be generated). It would be very useful to use because "the current date/time" is not enough for most of our use-cases.
Steps to reproduce
The text was updated successfully, but these errors were encountered: