Skip to content

[CCR] Client tests auto-follow pattern & follower index forms#34449

Merged
sebelga merged 19 commits intoelastic:masterfrom
sebelga:test/ccr_forms
Apr 9, 2019
Merged

[CCR] Client tests auto-follow pattern & follower index forms#34449
sebelga merged 19 commits intoelastic:masterfrom
sebelga:test/ccr_forms

Conversation

@sebelga
Copy link
Copy Markdown
Contributor

@sebelga sebelga commented Apr 3, 2019

This PR adds the client integration tests for the

  • Add auto-follow pattern
  • Edit auto-follow pattern
  • Add follower index
  • Edit follower index

I also fixed a small bug in the client side validation of the auto-follow pattern input. It is better to have errors as objects with a "message" property. This way, we can add other properties to the error object (like "alwaysVisible") and give our validation UI all the flexibility needed.

@sebelga sebelga requested review from cjcenizal and jen-huang April 3, 2019 14:54
@sebelga sebelga added Feature:CCR and Remote Clusters non-issue Indicates to automation that a pull request should not appear in the release notes test-jest-integration v7.2.0 v8.0.0 labels Apr 3, 2019
@elasticmachine
Copy link
Copy Markdown
Contributor

💔 Build Failed

sebelga added 5 commits April 3, 2019 17:34
There was a bug introduced in the client side validation of the auto-follow patterns by mixing the type of error (object vs JSX element). The correct way to handle errors is to use an object with a "message" property. This way, we can add other properties to the error object (like "alwaysVisible") and give our validation UI all the flexibility needed.
@elasticmachine
Copy link
Copy Markdown
Contributor

💔 Build Failed

Copy link
Copy Markdown
Contributor

@cjcenizal cjcenizal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome work! I have some feedback in sebelga#9 and will re-review once that's resolved.

@elasticmachine
Copy link
Copy Markdown
Contributor

💚 Build Succeeded

@sebelga sebelga added the Team:Kibana Management Dev Tools, Index Management, Upgrade Assistant, ILM, Ingest Node Pipelines, and more t// label Apr 4, 2019
@elasticmachine
Copy link
Copy Markdown
Contributor

Pinging @elastic/es-ui

@elasticmachine
Copy link
Copy Markdown
Contributor

💚 Build Succeeded

@sebelga
Copy link
Copy Markdown
Contributor Author

sebelga commented Apr 6, 2019

@cjcenizal I updated the PR with your proposal. Can you have another look at it? I also added the tests for the follower index add & edit. cheers!

@elasticmachine
Copy link
Copy Markdown
Contributor

💔 Build Failed

@sebelga sebelga changed the title [CCR] Client integration test Add/Edit auto-follow pattern [CCR] Client integration test auto-follow pattern & follower index forms Apr 6, 2019
@sebelga sebelga changed the title [CCR] Client integration test auto-follow pattern & follower index forms [CCR] Client tests auto-follow pattern & follower index forms Apr 6, 2019
@elasticmachine
Copy link
Copy Markdown
Contributor

💚 Build Succeeded

Copy link
Copy Markdown
Contributor

@cjcenizal cjcenizal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great!

this.setState({
isLoading: false,
remoteClusters: sortClusterByName(remoteClusters)
remoteClusters
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you mind helping me understand why you made this change? The reason I ask is because this change adds a little complexity by creating an additional Promise (via the added then) and passes remoteClusters to sortClusterByName implicitly instead of explicitly, so the original code seems both simpler and more expressive to me.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is directly related to the video you shared to me: "first-class functions". The same way we don't need to be explicit (and use imperative programming) with map, and forEach. I must say that I'm a bit confused and don't understand why here you think being declarative adds complexity:

loadRemoteClusters()
  .then(sortClusterByName) 
  .then((remoteClusters) => {
    ...
  })

but here no

await Promise.all(names.map(deleteCluster));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I can explain. I'm measuring complexity by how many "moving parts" are involved in an expression. In the example of piping through .then we've created an additional promise, which adds another moving part. By removing the .then we've removed that moving part which would be a reduction in complexity according to how I'm measuring it.

In this regard, I think your example of using Promise.all is as simple as you can get. It has the minimum number of moving parts required.

In terms of expressiveness, loadRemoteClusters().then(sortClusterByName) doesn't contain all of the information that names.map(deleteCluster) has. The latter example communicate that deleteCluster will accept name as its argument because that's what map does, but the first example implies that remoteClusters will be the argument passed to sortClusterByName because we assume that's what loadRemoteClusters resolves to.

Anyway, the bottom line is that I don't see the imperative programming style or functional programming style as inherently good or bad, I think they're both useful tools in various situations and I just wanted to share my perspective in this example -- just another way of thinking about code. :)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for explaining your thoughts, I get it. Not saying I share your opinion on complexity but I understand better your idea. I don't have any problem of reverting the change if you prefer it the other way.
This is, in the end, the result of doing so much testing. From time to time you bump on some code and think: "oh, this would be more elegant written this way" (purely at the preference level 😊 )

breadcrumbs: { set: () => {} },
}));

jest.mock('ui/index_patterns', () => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about adding some newlines so we don't need to disable the max-len rule? This will also make the code easier to read when the code editor is narrow. This comment applies to the other test files, too.

jest.mock('ui/index_patterns', () => {
  const { INDEX_PATTERN_ILLEGAL_CHARACTERS_VISIBLE } =
    jest.requireActual('../../../../../src/legacy/ui/public/index_patterns/constants');

  const { validateIndexPattern, ILLEGAL_CHARACTERS, CONTAINS_SPACES } =
    jest.requireActual('../../../../../src/legacy/ui/public/index_patterns/validate/validate_index_pattern');
    
  return {
    INDEX_PATTERN_ILLEGAL_CHARACTERS_VISIBLE,
    ILLEGAL_CHARACTERS,
    CONTAINS_SPACES,
    validateIndexPattern,
  };
});

setLoadRemoteClusteresResponse();

// Mock all HTTP Requests that have not been handled previously
server.respondWith([200, {}, '']);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same with this mocked response. I think we can remove it?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nooooooooooo 😄 This is our safeguard against any not handled HTTP requests (that we don't care in our test but could break the CI at any point).

} = registerHttpRequestMockHelpers(server));

// Set "default" mock responses by not providing any arguments
setLoadRemoteClusteresResponse();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this needed? I removed it and the tests seemed to run fine without it.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also I just noticed that this method name contains a typo -- "Clusters" is spelled "Clusteres", so it should be setLoadRemoteClustersResponse.

Copy link
Copy Markdown
Contributor Author

@sebelga sebelga Apr 9, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😄 I must say that you make me smile here! So when I am explicit, you ask me not to be, when I am not, you ask me to be.
So this line is indeed not needed as we have a safeguard for HTTP requests not handled by any mock. To be even more explicit it should have been setLoadRemoteClusteresResponse([]); (with empty Array), but the default response is an empty array.
So even though it is not necessary, I decided to be explicit and make it clear that there is a request to load the remote clusters.

I am not sure though to what extent we should add comments to the test code to explain that the form depends on the remote clusters being loaded before displaying the component.

expect(getFormErrorsMessages()).toContain(`Name can't begin with an underscore.`);
});

test('should not allow a "," (coma)', () => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small typo: "coma" -> "comma"

setLoadRemoteClusteresResponse();

// Mock all HTTP Requests that have not been handled previously
server.respondWith([200, {}, '']);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like both lines 56 and 59 are unnecessary, since the tests seem to execute normally when they're removed. This comment applies to follower_index_add.test.js, too.

// The implementation of the remote cluster "Select" + validation is
// done inside the <RemoteClustersFormField /> component. The same component that we use in the <AutoFollowPatternAdd /> section.
// To avoid copy/pasting the same tests here, we simply make sure that both sections use the <RemoteClustersFormField />
test('should use the same <RemoteClustersFormField /> component that in the <AutoFollowPatternAdd /> section', async () => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think "that in the" would read a bit more naturally if it was changed to "as the".

await nextTick(550); // we need to wait as there is a debounce of 500ms on the http validation

expect(server.requests.length).toBe(totalRequests + 1);
expect(server.requests[totalRequests].url).toBe('/api/index_management/indices');
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor nit: I think this would be clearer if changed to:

expect(server.requests[server.requests.length - 1].url).toBe('/api/index_management/indices');

Using server.requests.length - 1 more clearly communicates to me that we're looking at the most recent request, whereas referencing totalRequests is a bit more of a circuitous way of saying the same thing.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point 👍

* the "create" follower index, we won't test it again but simply make sure that
* the form component is indeed shared between the 2 app sections.
*/
test('should use the same Form component that the "<FollowerIndexAdd />" component', async () => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"that the" -> "as the"?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CR + English class = ❤️ 😊

expect(formAdd.length).toBe(1);
});

test('should populate the form fields with the values from the follower index loaded', () => {
Copy link
Copy Markdown
Contributor

@cjcenizal cjcenizal Apr 8, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think of using a data structure to dedupe some of these assertions and make it easier to compare inputs to expected values?

    test('should populate the form fields with the values from the follower index loaded', () => {
      const {
        remoteCluster,
        leaderIndex,
        name,
        maxReadRequestOperationCount,
        maxOutstandingReadRequests,
        maxReadRequestSize,
        maxWriteRequestOperationCount,
        maxWriteRequestSize,
        maxOutstandingWriteRequests,
        maxWriteBufferCount,
        maxWriteBufferSize,
        maxRetryDelay,
        readPollTimeout,
      } = FOLLOWER_INDEX;

      const inputToValueMap = {
        ccrRemoteClusterInput: remoteCluster,
        ccrFollowerIndexFormLeaderIndexInput: leaderIndex,
        ccrFollowerIndexFormFollowerIndexInput: name,
        ccrFollowerIndexFormMaxReadRequestOperationCountInput: maxReadRequestOperationCount,
        ccrFollowerIndexFormMaxOutstandingReadRequestsInput: maxOutstandingReadRequests,
        ccrFollowerIndexFormMaxReadRequestSizeInput: maxReadRequestSize,
        ccrFollowerIndexFormMaxWriteRequestOperationCountInput: maxWriteRequestOperationCount,
        ccrFollowerIndexFormMaxWriteRequestSizeInput: maxWriteRequestSize,
        ccrFollowerIndexFormMaxOutstandingWriteRequestsInput: maxOutstandingWriteRequests,
        ccrFollowerIndexFormMaxWriteBufferCountInput: maxWriteBufferCount,
        ccrFollowerIndexFormMaxWriteBufferSizeInput: maxWriteBufferSize,
        ccrFollowerIndexFormMaxRetryDelayInput: maxRetryDelay,
        ccrFollowerIndexFormReadPollTimeoutInput: readPollTimeout,
      };

      Object.keys(inputToValueMap).forEach(input => {
        expect(find(input).props().value).toBe(inputToValueMap[input]);
      });
    });

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea!

@elasticmachine
Copy link
Copy Markdown
Contributor

💔 Build Failed

@elasticmachine
Copy link
Copy Markdown
Contributor

💚 Build Succeeded

@sebelga
Copy link
Copy Markdown
Contributor Author

sebelga commented Apr 9, 2019

Thanks for the review @cjcenizal ! I added your suggested changes. We can talk offline about the comments I made if you want.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Feature:CCR and Remote Clusters non-issue Indicates to automation that a pull request should not appear in the release notes Team:Kibana Management Dev Tools, Index Management, Upgrade Assistant, ILM, Ingest Node Pipelines, and more t// test-jest-integration v7.2.0 v8.0.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants