[ML] Add functional tests for reauthorizing transforms (#156699)

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Quynh Nguyen (Quinn) 2023-05-18 13:24:00 -05:00 committed by GitHub
parent 8e2b5ae796
commit 3766cd66ab
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 282 additions and 15 deletions

View file

@ -225,7 +225,7 @@ enabled:
- x-pack/test/functional_basic/apps/ml/data_visualizer/group2/config.ts
- x-pack/test/functional_basic/apps/ml/data_visualizer/group3/config.ts
- x-pack/test/functional_basic/apps/transform/creation/index_pattern/config.ts
- x-pack/test/functional_basic/apps/transform/start_reset_delete/config.ts
- x-pack/test/functional_basic/apps/transform/actions/config.ts
- x-pack/test/functional_basic/apps/transform/edit_clone/config.ts
- x-pack/test/functional_basic/apps/transform/creation/runtime_mappings_saved_search/config.ts
- x-pack/test/functional_basic/apps/transform/permissions/config.ts
@ -291,7 +291,7 @@ enabled:
- x-pack/test/functional/apps/status_page/config.ts
- x-pack/test/functional/apps/transform/creation/index_pattern/config.ts
- x-pack/test/functional/apps/transform/creation/runtime_mappings_saved_search/config.ts
- x-pack/test/functional/apps/transform/start_reset_delete/config.ts
- x-pack/test/functional/apps/transform/actions/config.ts
- x-pack/test/functional/apps/transform/edit_clone/config.ts
- x-pack/test/functional/apps/transform/permissions/config.ts
- x-pack/test/functional/apps/transform/feature_controls/config.ts

View file

@ -114,6 +114,7 @@ export const TransformManagement: FC = () => {
<EuiCallOut
iconType="alert"
color="warning"
data-test-subj="transformPageReauthorizeCallout"
title={`${insufficientPermissionsMsg} ${actionMsg}`}
/>
<EuiSpacer size="s" />

View file

@ -122,7 +122,7 @@ With PATH_TO_CONFIG and other options as follows.
edit, clone | `test/functional/apps/transform/edit_clone/config.ts`
feature controls | `test/functional/apps/transform/feature_controls/config.ts`
permissions | `test/functional/apps/transform/permissions/config.ts`
start, reset, delete | `test/functional/apps/transform/start_reset_delete/config.ts`
actions | `test/functional/apps/transform/actions/config.ts`
1. Functional UI tests with `Basic` license:
@ -133,7 +133,7 @@ With PATH_TO_CONFIG and other options as follows.
edit, clone | `test/functional_basic/apps/transform/edit_clone/config.ts`
feature controls | `test/functional_basic/apps/transform/feature_controls/config.ts`
permissions | `test/functional_basic/apps/transform/permissions/config.ts`
start, reset, delete | `test/functional_basic/apps/transform/start_reset_delete/config.ts`
actions | `test/functional_basic/apps/transform/actions/config.ts`
1. API integration tests with `Trial` license:

View file

@ -21,9 +21,6 @@ export default ({ getService }: FtrProviderContext) => {
const supertest = getService('supertestWithoutAuth');
const transform = getService('transform');
// If transform was created with sufficient -> should still authorize and start
// If transform was created with insufficient -> should still authorize and start
function getTransformIdByUser(username: USER) {
return `transform-by-${username}`;
}
@ -51,6 +48,8 @@ export default ({ getService }: FtrProviderContext) => {
await transform.api.deleteIndices(destinationIndex);
}
// If transform was created with sufficient permissions -> should create and start
// If transform was created with insufficient permissions -> should create but not start
describe('/api/transform/reauthorize_transforms', function () {
const apiKeysForTransformUsers = new Map<USER, SecurityCreateApiKeyResponse>();

View file

@ -14,7 +14,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
...functionalConfig.getAll(),
testFiles: [require.resolve('.')],
junit: {
reportName: 'Chrome X-Pack UI Functional Tests - transform - start reset & delete',
reportName: 'Chrome X-Pack UI Functional Tests - transform - actions',
},
};
}

View file

@ -11,7 +11,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) {
const esArchiver = getService('esArchiver');
const transform = getService('transform');
describe('transform - start reset & delete', function () {
describe('transform - actions', function () {
this.tags('transform');
before(async () => {
@ -33,6 +33,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) {
});
loadTestFile(require.resolve('./deleting'));
loadTestFile(require.resolve('./reauthorizing'));
loadTestFile(require.resolve('./resetting'));
loadTestFile(require.resolve('./starting'));
});

View file

@ -0,0 +1,221 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { TRANSFORM_HEALTH_LABEL, TRANSFORM_STATE } from '@kbn/transform-plugin/common/constants';
import type {
TransformLatestConfig,
TransformPivotConfig,
} from '@kbn/transform-plugin/common/types/transform';
import type { SecurityCreateApiKeyResponse } from '@elastic/elasticsearch/lib/api/types';
import type { FtrProviderContext } from '../../../ftr_provider_context';
import { getLatestTransformConfig, getPivotTransformConfig } from '../helpers';
import { USER } from '../../../services/transform/security_common';
import { COMMON_REQUEST_HEADERS } from '../../../services/ml/common_api';
interface TestDataPivot {
suiteTitle: string;
originalConfig: TransformPivotConfig;
mode: 'batch' | 'continuous';
type: 'pivot';
expected: {
originalState: object;
reauthorizeEnabled: boolean;
reauthorizedState: object;
};
created_by_user: USER;
current_user: USER;
}
interface TestDataLatest {
suiteTitle: string;
originalConfig: TransformLatestConfig;
mode: 'batch' | 'continuous';
type: 'latest';
expected: {
originalState: object;
reauthorizeEnabled: boolean;
reauthorizedState: object;
};
created_by_user: USER;
current_user: USER;
}
type TestData = TestDataPivot | TestDataLatest;
function generateHeaders(apiKey: SecurityCreateApiKeyResponse) {
return {
...COMMON_REQUEST_HEADERS,
'es-secondary-authorization': `ApiKey ${apiKey.encoded}`,
};
}
export default function ({ getService }: FtrProviderContext) {
const esArchiver = getService('esArchiver');
const transform = getService('transform');
const apiKeysForTransformUsers = new Map<USER, SecurityCreateApiKeyResponse>();
describe('reauthorizing', function () {
const PREFIX = 'reauthorizing';
const testDataList: TestData[] = [
{
suiteTitle: 'continuous pivot transform (created by viewer, viewed by viewer)',
originalConfig: getPivotTransformConfig(`${PREFIX}-${USER.TRANSFORM_VIEWER}-1`, true),
mode: 'continuous',
type: 'pivot',
expected: {
originalState: { status: TRANSFORM_STATE.STOPPED, health: TRANSFORM_HEALTH_LABEL.red },
reauthorizeEnabled: false,
reauthorizedState: {
status: TRANSFORM_STATE.STARTED,
health: TRANSFORM_HEALTH_LABEL.green,
},
},
created_by_user: USER.TRANSFORM_VIEWER,
current_user: USER.TRANSFORM_VIEWER,
},
{
suiteTitle: 'batch pivot transform (created by viewer, viewed by poweruser)',
originalConfig: getPivotTransformConfig(PREFIX, false),
mode: 'batch',
type: 'pivot',
expected: {
originalState: { status: TRANSFORM_STATE.STOPPED, health: TRANSFORM_HEALTH_LABEL.red },
reauthorizeEnabled: true,
reauthorizedState: {
status: TRANSFORM_STATE.STOPPED,
progress: '100',
health: TRANSFORM_HEALTH_LABEL.green,
},
},
created_by_user: USER.TRANSFORM_VIEWER,
current_user: USER.TRANSFORM_POWERUSER,
},
{
suiteTitle: 'continuous pivot transform (created by viewer, authorized by poweruser)',
originalConfig: getPivotTransformConfig(`${PREFIX}-${USER.TRANSFORM_VIEWER}-2`, true),
mode: 'continuous',
type: 'pivot',
expected: {
originalState: { status: TRANSFORM_STATE.STOPPED, health: TRANSFORM_HEALTH_LABEL.red },
reauthorizeEnabled: true,
reauthorizedState: {
status: TRANSFORM_STATE.STARTED,
health: TRANSFORM_HEALTH_LABEL.green,
},
},
created_by_user: USER.TRANSFORM_VIEWER,
current_user: USER.TRANSFORM_POWERUSER,
},
{
suiteTitle: 'continuous latest transform (created by poweruser, viewed by viewer)',
originalConfig: getLatestTransformConfig(`${PREFIX}-${USER.TRANSFORM_POWERUSER}-1`, true),
mode: 'continuous',
type: 'latest',
expected: {
originalState: { status: TRANSFORM_STATE.STARTED, health: TRANSFORM_HEALTH_LABEL.green },
reauthorizeEnabled: false,
reauthorizedState: {
status: TRANSFORM_STATE.STARTED,
health: TRANSFORM_HEALTH_LABEL.green,
},
},
created_by_user: USER.TRANSFORM_POWERUSER,
current_user: USER.TRANSFORM_VIEWER,
},
];
before(async () => {
const apiKeyForTransformUsers =
await transform.securityCommon.createApiKeyForTransformUsers();
apiKeyForTransformUsers.forEach(({ user, apiKey }) =>
apiKeysForTransformUsers.set(user.name as USER, apiKey)
);
await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/ecommerce');
await transform.testResources.createIndexPatternIfNeeded('ft_ecommerce', 'order_date');
for (const testData of testDataList) {
await transform.api.createTransform(testData.originalConfig.id, testData.originalConfig, {
deferValidation: true,
// Create transforms with secondary authorization headers
headers: generateHeaders(apiKeysForTransformUsers.get(testData.created_by_user)!),
});
// For transforms created with insufficient permissions, they can be created but not started
// so we should not assert that the api call is successful here
await transform.api.startTransform(testData.originalConfig.id, false);
}
await transform.testResources.setKibanaTimeZoneToUTC();
});
after(async () => {
await transform.securityCommon.clearAllTransformApiKeys();
for (const testData of testDataList) {
await transform.testResources.deleteIndexPatternByTitle(testData.originalConfig.dest.index);
await transform.api.deleteIndices(testData.originalConfig.dest.index);
}
await transform.api.cleanTransformIndices();
await transform.testResources.deleteIndexPatternByTitle('ft_ecommerce');
});
for (const testData of testDataList) {
const transformId = testData.originalConfig.id;
describe(`${testData.suiteTitle}`, function () {
it('reauthorize transform', async () => {
await transform.securityUI.loginAs(testData.current_user);
await transform.testExecution.logTestStep('should load the home page');
await transform.navigation.navigateTo();
await transform.management.assertTransformListPageExists();
await transform.testExecution.logTestStep('should display the transforms table');
await transform.management.assertTransformsTableExists();
await transform.testExecution.logTestStep(
'should display the transforms reauthorize callout'
);
await transform.management.assertTransformsReauthorizeCalloutExists();
await transform.testExecution.logTestStep(
'should display the original transform in the transform list'
);
await transform.table.filterWithSearchString(transformId, 1);
await transform.table.assertTransformRowFields(
transformId,
testData.expected.originalState
);
if (testData.expected.reauthorizeEnabled) {
await transform.testExecution.logTestStep('should reauthorize the transform');
await transform.table.assertTransformRowActionEnabled(
transformId,
'Reauthorize',
testData.expected.reauthorizeEnabled
);
await transform.table.clickTransformRowAction(transformId, 'Reauthorize');
await transform.table.confirmReauthorizeTransform();
await transform.table.assertTransformRowFields(
transformId,
testData.expected.reauthorizedState
);
await transform.testExecution.logTestStep('should not show Reauthorize action anymore');
await transform.table.assertTransformRowActionMissing(transformId, 'Reauthorize');
} else {
await transform.testExecution.logTestStep('should show disabled action menu button');
await transform.table.assertTransformRowActionsButtonEnabled(transformId, false);
}
await transform.table.clearSearchString(testDataList.length);
});
});
}
});
}

View file

@ -272,10 +272,13 @@ export function TransformAPIProvider({ getService }: FtrProviderContext) {
});
},
async startTransform(transformId: string) {
async startTransform(transformId: string, assertSuccess = true) {
log.debug(`Starting transform '${transformId}' ...`);
const { body, status } = await esSupertest.post(`/_transform/${transformId}/_start`);
this.assertResponseStatusCode(200, status, body);
if (assertSuccess) {
this.assertResponseStatusCode(200, status, body);
}
},
async stopTransform(transformId: string) {

View file

@ -32,6 +32,10 @@ export function TransformManagementProvider({ getService }: FtrProviderContext)
await testSubjects.existOrFail('transformCreateFirstButton');
},
async assertTransformsReauthorizeCalloutExists() {
await testSubjects.existOrFail('transformPageReauthorizeCallout');
},
async assertCreateFirstTransformButtonEnabled(expectedValue: boolean) {
const isEnabled = await testSubjects.isEnabled('transformCreateFirstButton');
expect(isEnabled).to.eql(

View file

@ -9,7 +9,15 @@ import expect from '@kbn/expect';
import { FtrProviderContext } from '../../ftr_provider_context';
type TransformRowActionName = 'Clone' | 'Delete' | 'Discover' | 'Edit' | 'Reset' | 'Start' | 'Stop';
type TransformRowActionName =
| 'Clone'
| 'Delete'
| 'Discover'
| 'Edit'
| 'Reset'
| 'Start'
| 'Stop'
| 'Reauthorize';
export function TransformTableProvider({ getService }: FtrProviderContext) {
const find = getService('find');
@ -464,6 +472,21 @@ export function TransformTableProvider({ getService }: FtrProviderContext) {
});
}
public async assertTransformRowActionMissing(
transformId: string,
action: TransformRowActionName
) {
const selector = `transformAction${action}`;
await retry.tryForTime(60 * 1000, async () => {
await this.refreshTransformList();
await this.ensureTransformActionsMenuOpen(transformId);
await testSubjects.missingOrFail(selector, { timeout: 1000 });
await this.ensureTransformActionsMenuClosed();
});
}
public async assertTransformRowActionEnabled(
transformId: string,
action: TransformRowActionName,
@ -516,6 +539,14 @@ export function TransformTableProvider({ getService }: FtrProviderContext) {
await testSubjects.missingOrFail('transformDeleteModal', { timeout: 60 * 1000 });
}
public async assertTransformReauthorizeModalExists() {
await testSubjects.existOrFail('transformReauthorizeModal', { timeout: 60 * 1000 });
}
public async assertTransformReauthorizeModalNotExists() {
await testSubjects.missingOrFail('transformReauthorizeModal', { timeout: 60 * 1000 });
}
public async assertTransformResetModalExists() {
await testSubjects.existOrFail('transformResetModal', { timeout: 60 * 1000 });
}
@ -564,6 +595,14 @@ export function TransformTableProvider({ getService }: FtrProviderContext) {
});
}
public async confirmReauthorizeTransform() {
await retry.tryForTime(30 * 1000, async () => {
await this.assertTransformReauthorizeModalExists();
await testSubjects.click('transformReauthorizeModal > confirmModalConfirmButton');
await this.assertTransformReauthorizeModalNotExists();
});
}
public async confirmStartTransform() {
await retry.tryForTime(30 * 1000, async () => {
await this.assertTransformStartModalExists();

View file

@ -16,8 +16,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
testFiles: [require.resolve('.')],
junit: {
...transformConfig.get('junit'),
reportName:
'Chrome X-Pack UI Functional Tests Basic License - transform - start reset & delete',
reportName: 'Chrome X-Pack UI Functional Tests Basic License - transform - actions',
},
};
}

View file

@ -12,6 +12,6 @@ export default function ({ loadTestFile }: FtrProviderContext) {
this.tags(['skipFirefox', 'transform']);
// The transform UI should work the same as with a trial license
loadTestFile(require.resolve('../../../../functional/apps/transform/start_reset_delete'));
loadTestFile(require.resolve('../../../../functional/apps/transform/actions'));
});
}