-
Notifications
You must be signed in to change notification settings - Fork 24.7k
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
Introduce REST test transformations #67268
Conversation
@elasticmachine update branch |
Pinging @elastic/es-delivery (Team:Delivery) |
This seems like a very reasonable way to do it to me. I'd love to be able to |
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.
Given that @mark-vieira is going to be pretty busy I think we can get this in now and beg forgiveness later if we find something untoward. It makes sense to me. We'll get a closer review on the actual build changes required when we build the files and such.
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.
I left a few comments, otherwise I'll defer to someone that's more familiar with the YAML test stuff to review.
We'll definitely want to chat about how to do the build integration stuff. Effectively, the transforms themselves will be an input to these tasks so we need to track those implementations as inputs, which can be tricky depending on where those implementations live.
* @param currentNode The current node that is being evaluated. | ||
* @param objectKeyFinders A Map of the key to find to the | ||
*/ | ||
private void transformByObjectKeyName(JsonNode currentNode, Map<String, RestTestTransformByObjectKey> objectKeyFinders) { |
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.
This seems like something that should exist in the transform, as again, we are now coupled to the corresponding interface via an instanceof
check. Perhaps put this in an abstract implementation of RestTestTransform
transformTest
method and have InjectHeaders
extend that.
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.
The RestTestTransformer
operates against the whole YAML test, and the implementations of RestTestTransform
operate only on the specific node passed (from one or multiple tests) that it passed. This is to help minimize the number of times we need to traverse the JSON tree. By keeping the JSON tree traversal here (as opposed to the individual transform) allows for multiple transforms with a single traversal for all transforms that are initiated by a named object key. Currently it is only looks for "do" to inject headers, but future transforms will will be able to implement that interface with a different object key name and do something different like replace the value of object. Further, this method will likely get extended and renamed in future iterations to allow for more traits that kick off a transform (i.e. replacing one text value with another) while keeping the JSON traversals needed at a minimum.
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.
Understood. Does JsonNode
not contain a reference to it's parent such that the transform itself could make the deterinmation it's "inside" a do:
block?
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.
It does not maintain a parent reference, however, with the recursive approach here it is pretty simple to carry forward the parent (which is only ever an object or an array which kicks off the recursion).
Not sure that would help however, the goal here is to find all objects with the key "do" once we find that then we can transform it. In this specific case we only need the object itself, in other (future) cases we will need to pass the parent not the thing found. For example if we need an AddSyblingObject then we would need to pass in the parent of found object.
Perhaps I could change the name of the method to avoid confusion... the method should probably be called traverseTest
.
import java.util.concurrent.atomic.LongAdder; | ||
import java.util.stream.Collectors; | ||
|
||
public class InjectHeaderTests extends GradleUnitTestCase { |
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.
Boy, I wish we could start moving some of these unit tests to spock as well but that's outside the scope of this PR.
@breskeby heads up on this as well. This is very reminiscent of what I did for the Gradle documentation stuff 😉. I remember making cacheability work properly on that was a non-trivial effort and we'll want to ensure the same thing here. |
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.
I left two minor comments
LGTM
if ("teardown".equals(testName)) { | ||
teardownSection = test; | ||
} | ||
Map<String, RestTestTransformByObjectKey> objectKeyFinders = transformations.stream() |
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.
this could be done before the tests
for loop?
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.
indeed it can, nice catch.
int i = 0; | ||
for (RestTestTransformGlobalSetup setupTransform : setupTransforms) { | ||
ObjectNode result = setupTransform.transformSetup(setupSection); | ||
if (result != null && setupSection == null && i++ == 0) { |
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.
can you put a comment about this expression?
Is the intention that if there is no setupSection, but there is a transformation for a setup, then add a setup?
but it also looks like only one can be added? What if I have two setup transformations ?
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.
can you put a comment about this expression?
will do.
Is the intention that if there is no setupSection, but there is a transformation for a setup, then add a setup?
yes, that is the intention (create one if missing)
but it also looks like only one can be added? What if I have two setup transformations ?
good catch . There is a bug as-is with multiple setup/teardown transforms which will get fixed ... however since there is only at most 1 with this PR it is not tested yet , but will likely run into that sooner then later once i introduce more transforms.
edit: changed method name 8af24f0
@elasticmachine update branch |
Thanks everyone for the reviews ! |
This commit introduces the ability to programmatically transform YAML based REST tests. Specifically this initial commit introduces the ability to inject HTTP headers into the YAML REST tests. Additional capabilities (transforms) will be introduced in future commits. Transforming REST tests is a key component to test REST API compatibility. Eventually (not included here) the ability to transform tests will be integrated with Gradle such that the tests that are transformed prior to being executed. This commit does not have any REST API compatibility specific changes, and only introduces the code that will be used for that testing. Transforming REST tests at the YAML level was chosen over updating the test runners to ensure that these tests may (at a future time) be used by the various clients that re-use the YAML based REST tests. By transforming the tests themselves, there is a consistent view across all runners for exactly how and what to test when running N-1 tests against a N cluster with REST compatibility.
This commit introduces the ability to programmatically transform YAML based REST tests. Specifically this initial commit introduces the ability to inject HTTP headers into the YAML REST tests. Additional capabilities (transforms) will be introduced in future commits.
Transforming REST tests is a key component to test REST API compatibility. Eventually (not included here) the ability to transform tests will be integrated with Gradle such that the tests that are transformed prior to being executed. This commit does not have any REST API compatibility specific changes, and only introduces the code that will be used for that testing.
Transforming REST tests at the YAML level was chosen over updating the test runners to ensure that these tests may (at a future time) be used by the various clients that re-use the YAML based REST tests. By transforming the tests themselves, there is a consistent view across all runners for exactly how and what to test when running N-1 tests against a N cluster with REST compatibility.
To get an idea of how this will be used:
https://github.com/jakelandis/elasticsearch/blob/compat_header_inject_with_gradle/modules/ingest-common/build.gradle#L20
https://github.com/jakelandis/elasticsearch/blob/compat_header_inject_with_gradle/buildSrc/src/main/java/org/elasticsearch/gradle/test/rest/YamlRestCompatTestPlugin.java#L148
https://github.com/jakelandis/elasticsearch/blob/compat_header_inject_with_gradle/buildSrc/src/main/java/org/elasticsearch/gradle/test/rest/RestCompatTestTransformTask.java
I also added "humanDebug" flag to the tests that will output the transformed tests to stdout in case you want to run the tests and see the output directly.