Skip to content
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

Merged
merged 16 commits into from
Jan 19, 2021

Conversation

jakelandis
Copy link
Contributor

@jakelandis jakelandis commented Jan 11, 2021

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.

@jakelandis jakelandis added the WIP label Jan 11, 2021
@jakelandis
Copy link
Contributor Author

@elasticmachine update branch

@jakelandis jakelandis changed the title WIP Introduce REST test transformations Jan 12, 2021
@jakelandis jakelandis added :Delivery/Build Build or test infrastructure >enhancement v8.0.0 >non-issue and removed WIP labels Jan 12, 2021
@jakelandis jakelandis marked this pull request as ready for review January 12, 2021 16:35
@elasticmachine elasticmachine added the Team:Delivery Meta label for Delivery team label Jan 12, 2021
@elasticmachine
Copy link
Collaborator

Pinging @elastic/es-delivery (Team:Delivery)

@nik9000
Copy link
Member

nik9000 commented Jan 12, 2021

This seems like a very reasonable way to do it to me. I'd love to be able to cat the transformed yml files and this seems like a very reasonable way to get that.

Copy link
Member

@nik9000 nik9000 left a 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.

Copy link
Contributor

@mark-vieira mark-vieira left a 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) {
Copy link
Contributor

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.

Copy link
Contributor Author

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.

Copy link
Contributor

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?

Copy link
Contributor Author

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 {
Copy link
Contributor

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.

@mark-vieira
Copy link
Contributor

@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.

Copy link
Contributor

@pgomulka pgomulka left a 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()
Copy link
Contributor

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?

Copy link
Contributor Author

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) {
Copy link
Contributor

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 ?

Copy link
Contributor Author

@jakelandis jakelandis Jan 15, 2021

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

@jakelandis
Copy link
Contributor Author

@elasticmachine update branch

@jakelandis
Copy link
Contributor Author

Thanks everyone for the reviews !

@jakelandis jakelandis merged commit e3dc69b into elastic:master Jan 19, 2021
@jakelandis jakelandis deleted the compat_header_inject branch January 19, 2021 15:48
jakelandis added a commit that referenced this pull request Jan 19, 2021
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants