From 0f7f71bbfa0caed543f9a6062deaebe370081499 Mon Sep 17 00:00:00 2001 From: John Dorlus Date: Thu, 17 Oct 2019 19:36:54 -0700 Subject: [PATCH] Hybrid index pattern test (#43498) * Added 5 second sleep to let thte page load before checking for title. * Made changes to Rollups page object, added data test subjects to some elements, added function to get Indices to indexmanagement page. * Removed 5 second wait from old commit, * Updated home page for a more simplified create rollups method. Simplified data for the rollups to use a simple data structure rather than using makelogs. * Added stop to the previous rollup job that ran. Added some timeouts to wait for the rollup to run. Added future data to be rolled into hybrid index pattern. Added some custom handling for creating different types of index patterns. * Added forking condition for if the create index pattern button has the dropdown due to rollup data being present and to handle that case. Changed assertion for rollup job test to ensure that there is one index created with the proper name from the rollup job. Made adjustment to cron expression to run every 10 seconds. Added deletion clauses for all rollup jobs, index patterns and indices to ensure proper teardown. * Added navigate to the discover page and verify the number of hits. * Fixed assertion: Not supposed to contain hits. * Fixing the button that's being searched for. * Made adjustment to setings page due to test failures. * Made adjustment to setings page due to test failures. * Made a change to the order of the parameters for createIndexPattern which was causing non rollup index patterns to break. Also added new conditional and function to click on rollup index pattern button. * Changed ciGroup and also added logging statements to rollup jobs test. * Trying an additional wait to see if there is a race condition. * Changed timing of when rollup job is stopped. It was getting stopped before having triggered. * Added waitlogic for condition rather than hardcoded skeep. Used cheerio methods to parse table instead of bluebird's map async. Updated tests assertiuons to ensure the rollup has triggered and produced the right number of documents. Tested many times with Throttling and headless mode enabled. * Returned the method reloadIndicesButton back to the page object instead of modifying it. * Changed CI job to run test repeatedly. * Forgot some script omissions. * Reverting the script changes. Did not work. * Readded config to run tests repeatedly per Dima. * Added additional config for running CI tests. * Fixed config error. * Removed extra rollup cration step. * Made edits per review and added some comments to clarify functions purposes. * Added explanation of assertion and change that the rollup_info file was renamed. * Adjusted assertion to compensate for CI's slow network. * Split the hybrid index pattern test out and rollup job to be in separate files. Mocked out rollups and cleaned up log statements. * Added load empty kibana to reset kibana to an empty state. * Removed duplicate file and move method over from it. Updated references. * Renamed test suite to hybrid index pattern as that's what the test entails * Fixed missed reference rename. * Fixed mistaken async keyword causing unhandled rejection error. Also deleted rollup job in hybrid index pattern test in after hook. Renoved log statements. * Reverted changes to build scripts to normal. * Fixed jobs file to match upstream due to a missed fix in a merge conflict. * Added remainder of CI groups to job. * Removed whitespace error. * Made fixes per CJs review. Fixed clashing variable names. Reverted rollup job test to assert using rollups table. Changed group back to ciGroup1. Removed duplicate teardown index deletion calls. * Created more concrete assertion for checking if hybrid index pattern was created. Some refactorings. Changed Math.floor() to Date.now() for avoidance of name collisions. * Removed the data.json file. * Addressed nits. --- test/functional/page_objects/settings_page.js | 20 +++- .../index_list/index_table/index_table.js | 4 +- .../apps/rollup_job/hybrid_index_helper.js | 32 ++++++ .../apps/rollup_job/hybrid_index_pattern.js | 103 ++++++++++++++++++ .../test/functional/apps/rollup_job/index.js | 5 +- .../functional/apps/rollup_job/rollup_jobs.js | 88 +++++++-------- .../page_objects/index_management_page.ts | 40 +++++++ .../functional/page_objects/rollup_page.js | 58 +++++++++- 8 files changed, 295 insertions(+), 55 deletions(-) create mode 100644 x-pack/test/functional/apps/rollup_job/hybrid_index_helper.js create mode 100644 x-pack/test/functional/apps/rollup_job/hybrid_index_pattern.js diff --git a/test/functional/page_objects/settings_page.js b/test/functional/page_objects/settings_page.js index 3f2e3b923d1f2..60f3d3a7e9eab 100644 --- a/test/functional/page_objects/settings_page.js +++ b/test/functional/page_objects/settings_page.js @@ -239,8 +239,8 @@ export function SettingsPageProvider({ getService, getPageObjects }) { async setScriptedFieldLanguageFilter(language) { await find.clickByCssSelector( 'select[data-test-subj="scriptedFieldLanguageFilterDropdown"] > option[label="' + - language + - '"]' + language + + '"]' ); } @@ -287,9 +287,14 @@ export function SettingsPageProvider({ getService, getPageObjects }) { await indexLink.click(); } + async getIndexPatternList() { + await testSubjects.existOrFail('indexPatternTable', { timeout: 5000 }); + return await find.allByCssSelector('[data-test-subj="indexPatternTable"] .euiTable a'); + } + async isIndexPatternListEmpty() { await testSubjects.existOrFail('indexPatternTable', { timeout: 5000 }); - const indexPatternList = await find.allByCssSelector('[data-test-subj="indexPatternTable"] .euiTable a'); + const indexPatternList = await this.getIndexPatternList(); return indexPatternList.length === 0; } @@ -300,13 +305,16 @@ export function SettingsPageProvider({ getService, getPageObjects }) { } } - async createIndexPattern(indexPatternName, timefield = '@timestamp') { + async createIndexPattern(indexPatternName, timefield = '@timestamp', isStandardIndexPattern = true) { await retry.try(async () => { await this.navigateTo(); await PageObjects.header.waitUntilLoadingHasFinished(); await this.clickKibanaIndexPatterns(); await PageObjects.header.waitUntilLoadingHasFinished(); await this.clickOptionalAddNewButton(); + if (!isStandardIndexPattern) { + await this.clickCreateNewRollupButton(); + } await PageObjects.header.waitUntilLoadingHasFinished(); await retry.try(async () => { await this.setIndexPatternField({ indexPatternName }); @@ -340,6 +348,10 @@ export function SettingsPageProvider({ getService, getPageObjects }) { } } + async clickCreateNewRollupButton() { + await testSubjects.click('createRollupIndexPatternButton'); + } + async getIndexPatternIdFromUrl() { const currentUrl = await browser.getCurrentUrl(); const indexPatternId = currentUrl.match(/.*\/(.*)/)[1]; diff --git a/x-pack/legacy/plugins/index_management/public/sections/home/index_list/index_table/index_table.js b/x-pack/legacy/plugins/index_management/public/sections/home/index_list/index_table/index_table.js index f0f7a3d010f7f..0c3d998942b2b 100644 --- a/x-pack/legacy/plugins/index_management/public/sections/home/index_list/index_table/index_table.js +++ b/x-pack/legacy/plugins/index_management/public/sections/home/index_list/index_table/index_table.js @@ -337,6 +337,7 @@ export class IndexTable extends Component { const { name } = index; return ( toggleChanged(name, event.target.checked)} label={label} diff --git a/x-pack/test/functional/apps/rollup_job/hybrid_index_helper.js b/x-pack/test/functional/apps/rollup_job/hybrid_index_helper.js new file mode 100644 index 0000000000000..fc0c55213fe89 --- /dev/null +++ b/x-pack/test/functional/apps/rollup_job/hybrid_index_helper.js @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + + +export default function mockRolledUpData(jobName, targetIndexName, day) { + return { + index: `${targetIndexName}`, + body: { + '_rollup.version': 2, + '@timestamp.date_histogram.time_zone': 'UTC', + '@timestamp.date_histogram.timestamp': day.toISOString(), + '@timestamp.date_histogram.interval': '1000ms', + '@timestamp.date_histogram._count': 1, + '_rollup.id': jobName + } + }; +} + +//This function just adds some stub indices that includes a timestamp and an arbritary metric. This is fine since we are not actually testing +//rollup functionality. +export function mockIndices(day, prepend) { + return { + index: `${prepend}-${day.format('MM-DD-YYYY')}`, + body: { + '@timestamp': day.toISOString(), + foo_metric: 1 + } + }; +} diff --git a/x-pack/test/functional/apps/rollup_job/hybrid_index_pattern.js b/x-pack/test/functional/apps/rollup_job/hybrid_index_pattern.js new file mode 100644 index 0000000000000..dca5534fb68b3 --- /dev/null +++ b/x-pack/test/functional/apps/rollup_job/hybrid_index_pattern.js @@ -0,0 +1,103 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + + +import datemath from '@elastic/datemath'; +import expect from '@kbn/expect'; +import mockRolledUpData, { mockIndices } from './hybrid_index_helper'; + +export default function ({ getService, getPageObjects }) { + + const es = getService('es'); + const esArchiver = getService('esArchiver'); + const retry = getService('retry'); + const PageObjects = getPageObjects(['common', 'settings']); + + describe('hybrid index pattern', function () { + //Since rollups can only be created once with the same name (even if you delete it), + //we add the Date.now() to avoid name collision if you run the tests locally back to back. + const rollupJobName = `hybrid-index-pattern-test-rollup-job-${Date.now()}`; + const rollupTargetIndexName = `rollup-target-data`; + const regularIndexPrefix = `regular-index`; + const rollupSourceIndexPrefix = `rollup-source-data`; + const rollupIndexPatternName = `${regularIndexPrefix}*,${rollupTargetIndexName}`; + const now = new Date(); + const pastDates = [ + datemath.parse('now-1d', { forceNow: now }), + datemath.parse('now-2d', { forceNow: now }), + datemath.parse('now-3d', { forceNow: now }), + ]; + + it('create hybrid index pattern', async () => { + //Create data for rollup job to recognize. + //Index past data to be used in the test. + await pastDates.map(async (day) => { + await es.index(mockIndices(day, rollupSourceIndexPrefix)); + }); + + await retry.waitForWithTimeout('waiting for 3 records to be loaded into elasticsearch.', 10000, async () => { + const response = await es.indices.get({ + index: `${rollupSourceIndexPrefix}*`, + allow_no_indices: false + }); + return Object.keys(response).length === 3; + }); + + await retry.try(async () => { + //Create a rollup for kibana to recognize + await es.transport.request({ + path: `/_rollup/job/${rollupJobName}`, + method: 'PUT', + body: { + 'index_pattern': `${rollupSourceIndexPrefix}*`, + 'rollup_index': rollupTargetIndexName, + 'cron': '*/10 * * * * ?', + 'groups': { + 'date_histogram': { + 'fixed_interval': '1000ms', + 'field': '@timestamp', + 'time_zone': 'UTC' + } + }, + 'timeout': '20s', + 'page_size': 1000 + } + }); + }); + + + await pastDates.map(async (day) => { + await es.index(mockRolledUpData(rollupJobName, rollupTargetIndexName, day)); + }); + + //Index live data to be used in the test. + await es.index(mockIndices(datemath.parse('now', { forceNow: now }), regularIndexPrefix)); + + await PageObjects.common.navigateToApp('settings'); + await PageObjects.settings.createIndexPattern(rollupIndexPatternName, '@timestamp', false); + + + await PageObjects.settings.clickKibanaIndexPatterns(); + const indexPattern = (await PageObjects.settings.getIndexPatternList()).pop(); + const indexPatternText = await indexPattern.getVisibleText(); + expect(indexPatternText).to.contain(rollupIndexPatternName); + expect(indexPatternText).to.contain('Rollup'); + }); + + after(async () => { + // Delete the rollup job. + await es.transport.request({ + path: `/_rollup/job/${rollupJobName}`, + method: 'DELETE', + }); + + await es.indices.delete({ index: rollupTargetIndexName }); + await es.indices.delete({ index: `${regularIndexPrefix}*` }); + await es.indices.delete({ index: `${rollupSourceIndexPrefix}*` }); + await esArchiver.load('empty_kibana'); + }); + }); +} diff --git a/x-pack/test/functional/apps/rollup_job/index.js b/x-pack/test/functional/apps/rollup_job/index.js index cc5c6feacbdd2..43cb3eaff0e79 100644 --- a/x-pack/test/functional/apps/rollup_job/index.js +++ b/x-pack/test/functional/apps/rollup_job/index.js @@ -5,10 +5,11 @@ */ export default function ({ loadTestFile }) { - // FLAKY: https://github.com/elastic/kibana/issues/43559 - describe.skip('rollup job', function () { + + describe('rollup app', function () { this.tags('ciGroup1'); loadTestFile(require.resolve('./rollup_jobs')); + loadTestFile(require.resolve('./hybrid_index_pattern')); }); } diff --git a/x-pack/test/functional/apps/rollup_job/rollup_jobs.js b/x-pack/test/functional/apps/rollup_job/rollup_jobs.js index 85acc047be396..eeff20105aed2 100644 --- a/x-pack/test/functional/apps/rollup_job/rollup_jobs.js +++ b/x-pack/test/functional/apps/rollup_job/rollup_jobs.js @@ -4,64 +4,64 @@ * you may not use this file except in compliance with the Elastic License. */ + +import datemath from '@elastic/datemath'; import expect from '@kbn/expect'; -import { indexBy } from 'lodash'; +import { mockIndices } from './hybrid_index_helper'; export default function ({ getService, getPageObjects }) { + + const es = getService('es'); const esArchiver = getService('esArchiver'); - const log = getService('log'); - const PageObjects = getPageObjects(['security', 'rollup', 'common', 'header']); + const PageObjects = getPageObjects(['rollup', 'common']); describe('rollup job', function () { - this.tags('smoke'); - before(async () => { - // init data - await esArchiver.loadIfNeeded('logstash_functional'); - await esArchiver.load('canvas/default'); - await PageObjects.common.navigateToApp('rollupJob'); - }); + //Since rollups can only be created once with the same name (even if you delete it), + //we add the Date.now() to avoid name collision. + const rollupJobName = 'rollup-to-be-' + Date.now(); + const targetIndexName = 'rollup-to-be'; + const rollupSourceIndexPattern = 'to-be*'; + const rollupSourceDataPrepend = 'to-be'; + //make sure all dates have the same concept of "now" + const now = new Date(); + const pastDates = [ + datemath.parse('now-1d', { forceNow: now }), + datemath.parse('now-2d', { forceNow: now }), + datemath.parse('now-3d', { forceNow: now }), + ]; - after(async () => await esArchiver.unload('logstash_functional')); - - it('create and save a new job', async () => { - const jobName = 'Testjob1'; - const indexPattern = '.kibana*'; - const indexName = 'rollup_index'; + it('create new rollup job', async () => { const interval = '1000ms'; - await PageObjects.rollup.createNewRollUpJob(); - await PageObjects.rollup.verifyStepIsActive(1); - await PageObjects.rollup.addRoleNameandIndexPattern(jobName, indexPattern); - await PageObjects.rollup.verifyIndexPatternAccepted(); - await PageObjects.rollup.setIndexName(indexName); - await PageObjects.rollup.moveToNextStep(); - - //now navigate to histogram - await PageObjects.rollup.verifyStepIsActive(2); - await PageObjects.rollup.setJobInterval(interval); - await PageObjects.rollup.moveToNextStep(); + pastDates.map(async (day) => { + await es.index(mockIndices(day, rollupSourceDataPrepend)); + }); - //Terms (optional) - await PageObjects.rollup.verifyStepIsActive(3); - await PageObjects.rollup.moveToNextStep(); + await PageObjects.common.navigateToApp('rollupJob'); + await PageObjects.rollup.createNewRollUpJob(rollupJobName, rollupSourceIndexPattern, targetIndexName, + interval, ' ', true, { time: '*/10 * * * * ?', cron: true }); - //Histogram(optional) - await PageObjects.rollup.verifyStepIsActive(4); - await PageObjects.rollup.moveToNextStep(); + const jobList = await PageObjects.rollup.getJobList(); + expect(jobList.length).to.be(1); - //Metrics(optional) - await PageObjects.rollup.verifyStepIsActive(5); - await PageObjects.rollup.moveToNextStep(); + }); - //saveJob and verify the name in the list - await PageObjects.rollup.verifyStepIsActive(6); - await PageObjects.rollup.saveJob(); + after(async () => { + //Stop the running rollup job. + await es.transport.request({ + path: `/_rollup/job/${rollupJobName}/_stop?wait_for_completion=true`, + method: 'POST', + }); + // Delete the rollup job. + await es.transport.request({ + path: `/_rollup/job/${rollupJobName}`, + method: 'DELETE', + }); - // verify jobListTitle - const jobList = indexBy(await PageObjects.rollup.getJobList(), 'jobName'); - log.debug(JSON.stringify(jobList)); - log.debug(Object.keys(jobList)); - expect(Object.keys(jobList)).to.have.length(1); + //Delete all data indices that were created. + await es.indices.delete({ index: targetIndexName }); + await es.indices.delete({ index: rollupSourceIndexPattern }); + await esArchiver.load('empty_kibana'); }); }); } diff --git a/x-pack/test/functional/page_objects/index_management_page.ts b/x-pack/test/functional/page_objects/index_management_page.ts index 39a672b163eb3..1ae23b24156d0 100644 --- a/x-pack/test/functional/page_objects/index_management_page.ts +++ b/x-pack/test/functional/page_objects/index_management_page.ts @@ -7,15 +7,55 @@ import { FtrProviderContext } from '../ftr_provider_context'; export function IndexManagementPageProvider({ getService }: FtrProviderContext) { + const find = getService('find'); const testSubjects = getService('testSubjects'); return { async sectionHeadingText() { return await testSubjects.getVisibleText('appTitle'); }, + async reloadIndices() { + await testSubjects.click('reloadIndicesButton'); + }, async reloadIndicesButton() { return await testSubjects.find('reloadIndicesButton'); }, + async toggleRollupIndices() { + await testSubjects.click('checkboxToggles-rollupToggle'); + }, + + async getIndexList() { + const table = await find.byCssSelector('table'); + const $ = await table.parseDomContent(); + return $.findTestSubjects('indexTableRow') + .toArray() + .map(row => { + return { + indexName: $(row) + .findTestSubject('indexTableIndexNameLink') + .text(), + indexHealth: $(row) + .findTestSubject('indexTableCell-health') + .text(), + indexStatus: $(row) + .findTestSubject('indexTableCell-status') + .text(), + indexPrimary: $(row) + .findTestSubject('indexTableCell-primary') + .text(), + indexReplicas: $(row) + .findTestSubject('indexTableCell-replica') + .text(), + indexDocuments: $(row) + .findTestSubject('indexTableCell-documents') + .text() + .replace('documents', ''), + indexSize: $(row) + .findTestSubject('indexTableCell-size') + .text(), + }; + }); + }, async changeTabs(tab: 'indicesTab' | 'templatesTab') { return await testSubjects.click(tab); }, diff --git a/x-pack/test/functional/page_objects/rollup_page.js b/x-pack/test/functional/page_objects/rollup_page.js index 44eb762fb7082..b0e848fa1a1e8 100644 --- a/x-pack/test/functional/page_objects/rollup_page.js +++ b/x-pack/test/functional/page_objects/rollup_page.js @@ -10,24 +10,69 @@ import { map as mapAsync } from 'bluebird'; export function RollupPageProvider({ getService, getPageObjects }) { const testSubjects = getService('testSubjects'); const log = getService('log'); + const find = getService('find'); const PageObjects = getPageObjects(['header', 'common']); class RollupJobPage { - async createNewRollUpJob() { + async createNewRollUpJob(jobName, indexPattern, indexName, interval, delay = '1d', + startImmediately = false, scheduledTime = { time: 'minute', cron: true }) { + let stepNum = 1; + //Step 1 await testSubjects.click('createRollupJobButton'); + await this.verifyStepIsActive(stepNum); + await this.addRollupNameandIndexPattern(jobName, indexPattern); + await this.verifyIndexPatternAccepted(); + await this.setIndexName(indexName); + await this.setScheduleTime(scheduledTime.time, scheduledTime.cron); + await this.setRollupDelay(delay); + stepNum = await this.moveToNextStep(stepNum); + + //Step 2: Histogram + await this.verifyStepIsActive(stepNum); + await this.setJobInterval(interval); + stepNum = await this.moveToNextStep(stepNum); + + //Step 3: Terms (optional) + await this.verifyStepIsActive(stepNum); + stepNum = await this.moveToNextStep(); + + //Step 4: Histogram(optional) + await this.verifyStepIsActive(stepNum); + stepNum = await this.moveToNextStep(); + + //Step 5: Metrics(optional) + await this.verifyStepIsActive(stepNum); + stepNum = await this.moveToNextStep(); + + //Step 6: saveJob and verify the name in the list + await this.verifyStepIsActive(stepNum); + await this.saveJob(startImmediately); } async verifyStepIsActive(stepNumber) { await testSubjects.exists(`createRollupStep${stepNumber}--active`); } - async addRoleNameandIndexPattern(name, indexPattern) { + async setScheduleTime(time, cron) { + if (cron) { + await testSubjects.click('rollupShowAdvancedCronLink'); + await testSubjects.setValue('rollupAdvancedCron', time); + } + // TODO: Add handling for if Cron is false to go through clicking options. + } + + async addRollupNameandIndexPattern(name, indexPattern) { log.debug(`Adding name ${name} to form`); await testSubjects.setValue('rollupJobName', name); await testSubjects.setValue('rollupIndexPattern', indexPattern); } + async setRollupDelay(time) { + log.debug(`Setting rollup delay to "${time}"`); + await testSubjects.setValue('rollupDelay', time); + } + async verifyIndexPatternAccepted() { const span = await testSubjects.find('fieldIndexPatternSuccessMessage'); const message = await span.findByCssSelector('p'); @@ -39,15 +84,20 @@ export function RollupPageProvider({ getService, getPageObjects }) { await testSubjects.setValue('rollupIndexName', name); } - async moveToNextStep() { + async moveToNextStep(stepNum) { await testSubjects.click('rollupJobNextButton'); + return stepNum + 1; } async setJobInterval(time) { await testSubjects.setValue('rollupJobInterval', time); } - async saveJob() { + async saveJob(startImmediately) { + if (startImmediately) { + const checkbox = await find.byCssSelector('.euiCheckbox'); + await checkbox.click(); + } await testSubjects.click('rollupJobSaveButton'); await PageObjects.header.waitUntilLoadingHasFinished(); }