-
Notifications
You must be signed in to change notification settings - Fork 4
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
Support matching on arrays nested in the body #48
Conversation
324fa3e
to
86a58fa
Compare
57f7fe9
to
b59a60b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM.
Posted some comments, but none of those is critical, IMO.
internal/app/pactproxy/constraint.go
Outdated
@@ -15,3 +18,34 @@ type interactionConstraint struct { | |||
func (i interactionConstraint) Key() string { | |||
return strings.Join([]string{i.Interaction, i.Path}, "_") | |||
} | |||
|
|||
func (i interactionConstraint) Check(expectedValues []interface{}, actualValue interface{}) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it need to be exported? 🤔
func (i interactionConstraint) Check(expectedValues []interface{}, actualValue interface{}) error { | |
func (i interactionConstraint) check(expectedValues []interface{}, actualValue interface{}) error { |
expected := fmt.Sprintf(constraint.Format, values...) | ||
if actual != expected { | ||
violations = append(violations, fmt.Sprintf("value '%s' at path '%s' does not match constraint '%s'", actual, constraint.Path, expected)) | ||
if err := constraint.Check(values, val); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: I slightly prefer original variable naming, tbh (i.e. actual
vs. expected
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
expected
& actual
got moved inside Check
. So values
& val
here are the raw data that was previously used to create those. They are very confusingly named though, and now expected
& actual
are freed up they work well here 👍
case []interface{}: | ||
// Create constraints for each element in the array. This allows matching rules to override them. | ||
for j := range val { | ||
i.addJSONConstraintsFromPact(fmt.Sprintf("%v[%v]", path, j), matchingRules, val[j]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just my curiosity - any reason not to use a bit more specific format (%s[%d]
) as path
and j
are known to be string
and int
respectively?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No reason, and it makes sense to use the more specific ones so that the arguments can't get accidentally swapped. Changed 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. Thanks for addressing the comments. 🙏
If there is an array nested within a JSON body on the pact, when a request was received and the interaction had its constraints evaluated, the constraint for the value of the array got skipped. This meant that if pact-proxy had interactions that differed only in the content of arrays, when requests were received it would match all of those interactions rather than the specific one that would have been matched had the contents of the array been considered. This was causing flaky tests in a project that used pact-proxy to wait for N of a specific interaction. The interaction was the same as another apart from the content of an array in the body. Since pact-proxy could not tell these apart, it would continue after N of either of these interactions, not N of the one the test needed to wait for. That randomly caused the next test to fail because interactions from the previous test were still ongoing. Now when constraints are added from the Pact, we generate constraints for each element in the array, as well as a length check. When evaluating constraints, we can then run these like we do for any other type. This allows for two interactions to be created that differ only in the contents of an array and pact-proxy is able to tell them apart. Generating constraints on a per-element basis is done to also allow for matching rules that apply to an individual array element. When matching rules are specified, pact-proxy does not enforce these so the constraint must not be generated for that element. As with non-array matching rules, if a request is received and the remaining constraints are met, the request is considered a match and proxied to Pact server, which will check matching rules.
Previously, a JSON request body being an array was a special-case. Now that arrays are handled nested within the JSON body, treat them as any other JSON. The constraints will now get correctly added as with any other value.
95acf44
to
c36a434
Compare
Closing & re-opening as CI hasn't kicked off. Github had an outage earlier, so expect it was due to that. |
If there is an array nested within a JSON body on the pact, when a request was received and the interaction had its constraints evaluated, the constraint for the value of the array got skipped. This meant that if pact-proxy had interactions that differed only in the content of arrays, when requests were received it would match all of those interactions rather than the specific one that would have been matched had the contents of the array been considered.
This was causing flaky tests in a project that used pact-proxy to wait for N of a specific interaction. The interaction was the same as another apart from the content of an array in the body. Since pact-proxy could not tell these apart, it would continue after N of either of these interactions, not N of the one the test needed to wait for. That randomly caused the next test to fail because interactions from the previous test were still ongoing.
Now when constraints are added from the Pact, we generate constraints for each element in the array, as well as a length check. When evaluating constraints, we can then run these like we do for any other type. This allows for two interactions to be created that differ only in the contents of an array and pact-proxy is able to tell them apart.
Generating constraints on a per-element basis is done to also allow for matching rules that apply to an individual array element. When matching rules are specified, pact-proxy does not enforce these so the constraint must not be generated for that element. As with non-array matching rules, if a request is received and the remaining constraints are met, the request is considered a match and proxied to Pact server, which will check matching rules.
Additionally (separate commit), constraints are now allowed when the JSON body is an array. Previously, a JSON request body being an array was a special-case. Now that arrays are handled nested within the JSON body, treat them as any other JSON. The constraints will now get correctly added as with any other value.