Skip to content

Commit

Permalink
[ML] Add integration tests for trained_models API (elastic#104819)
Browse files Browse the repository at this point in the history
* [ML] api integration tests for get trained models endpoint

* [ML] delete ingest pipelines after tests execution

* [ML] deleteIngestPipeline method

* [ML] test for unauthorized user

* [ML] tests for model stats

* [ML] delete trained model tests

* [ML] fix typo

* [ML] fix expect package path

* [ML] get model pipelines tests

* [ML] test for aliases

* [ML] add tests for a 404 response

* [ML] fix typo

* [ML] fix typo
  • Loading branch information
darnautov authored and kibanamachine committed Jul 9, 2021
1 parent a037e19 commit 590883f
Show file tree
Hide file tree
Showing 8 changed files with 371 additions and 35 deletions.
1 change: 1 addition & 0 deletions x-pack/test/api_integration/apis/ml/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,5 +82,6 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) {
loadTestFile(require.resolve('./results'));
loadTestFile(require.resolve('./saved_objects'));
loadTestFile(require.resolve('./system'));
loadTestFile(require.resolve('./trained_models'));
});
}
67 changes: 67 additions & 0 deletions x-pack/test/api_integration/apis/ml/trained_models/delete_model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* 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 expect from '@kbn/expect';
import { FtrProviderContext } from '../../../ftr_provider_context';
import { USER } from '../../../../functional/services/ml/security_common';
import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common_api';

export default ({ getService }: FtrProviderContext) => {
const supertest = getService('supertestWithoutAuth');
const ml = getService('ml');

describe('DELETE trained_models', () => {
before(async () => {
await ml.testResources.setKibanaTimeZoneToUTC();
await ml.api.createdTestTrainedModels('regression', 2);
});

after(async () => {
await ml.api.cleanMlIndices();
});

it('deletes trained model by id', async () => {
const { body: deleteResponseBody } = await supertest
.delete(`/api/ml/trained_models/dfa_regression_model_n_0`)
.auth(USER.ML_POWERUSER, ml.securityCommon.getPasswordForUser(USER.ML_POWERUSER))
.set(COMMON_REQUEST_HEADERS)
.expect(200);

expect(deleteResponseBody).to.eql({ acknowledged: true });

// verify that model is actually deleted
await supertest
.get(`/api/ml/trained_models/dfa_regression_model_n_0`)
.auth(USER.ML_POWERUSER, ml.securityCommon.getPasswordForUser(USER.ML_POWERUSER))
.set(COMMON_REQUEST_HEADERS)
.expect(404);
});

it('returns 404 if requested trained model does not exist', async () => {
await supertest
.delete(`/api/ml/trained_models/not_existing_model`)
.auth(USER.ML_POWERUSER, ml.securityCommon.getPasswordForUser(USER.ML_POWERUSER))
.set(COMMON_REQUEST_HEADERS)
.expect(404);
});

it('does not allow to delete trained model if the user does not have required permissions', async () => {
await supertest
.delete(`/api/ml/trained_models/dfa_regression_model_n_1`)
.auth(USER.ML_VIEWER, ml.securityCommon.getPasswordForUser(USER.ML_VIEWER))
.set(COMMON_REQUEST_HEADERS)
.expect(403);

// verify that model has not been deleted
await supertest
.get(`/api/ml/trained_models/dfa_regression_model_n_1`)
.auth(USER.ML_POWERUSER, ml.securityCommon.getPasswordForUser(USER.ML_POWERUSER))
.set(COMMON_REQUEST_HEADERS)
.expect(200);
});
});
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* 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 expect from '@kbn/expect';
import { FtrProviderContext } from '../../../ftr_provider_context';
import { USER } from '../../../../functional/services/ml/security_common';
import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common_api';

export default ({ getService }: FtrProviderContext) => {
const supertest = getService('supertestWithoutAuth');
const ml = getService('ml');

describe('GET trained_models/pipelines', () => {
let testModelIds: string[] = [];

before(async () => {
await ml.testResources.setKibanaTimeZoneToUTC();
testModelIds = await ml.api.createdTestTrainedModels('regression', 2, true);
});

after(async () => {
// delete all created ingest pipelines
await Promise.all(testModelIds.map((modelId) => ml.api.deleteIngestPipeline(modelId)));
await ml.api.cleanMlIndices();
});

it('returns trained model pipelines by id', async () => {
const { body } = await supertest
.get(`/api/ml/trained_models/dfa_regression_model_n_0/pipelines`)
.auth(USER.ML_POWERUSER, ml.securityCommon.getPasswordForUser(USER.ML_POWERUSER))
.set(COMMON_REQUEST_HEADERS)
.expect(200);

expect(body.length).to.eql(1);
expect(body[0].model_id).to.eql('dfa_regression_model_n_0');
expect(Object.keys(body[0].pipelines).length).to.eql(1);
});

it('returns an error in case user does not have required permission', async () => {
await supertest
.get(`/api/ml/trained_models/dfa_regression_model_n_0/pipelines`)
.auth(USER.ML_VIEWER, ml.securityCommon.getPasswordForUser(USER.ML_VIEWER))
.set(COMMON_REQUEST_HEADERS)
.expect(403);
});
});
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* 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 expect from '@kbn/expect';
import { FtrProviderContext } from '../../../ftr_provider_context';
import { USER } from '../../../../functional/services/ml/security_common';
import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common_api';

export default ({ getService }: FtrProviderContext) => {
const supertest = getService('supertestWithoutAuth');
const ml = getService('ml');

describe('GET trained_models/_stats', () => {
before(async () => {
await ml.testResources.setKibanaTimeZoneToUTC();
await ml.api.createdTestTrainedModels('regression', 2);
});

after(async () => {
await ml.api.cleanMlIndices();
});

it('returns trained model stats by id', async () => {
const { body } = await supertest
.get(`/api/ml/trained_models/dfa_regression_model_n_0/_stats`)
.auth(USER.ML_VIEWER, ml.securityCommon.getPasswordForUser(USER.ML_VIEWER))
.set(COMMON_REQUEST_HEADERS)
.expect(200);

expect(body.count).to.eql(1);
expect(body.trained_model_stats[0].model_id).to.eql('dfa_regression_model_n_0');
});

it('returns 404 if requested trained model does not exist', async () => {
await supertest
.get(`/api/ml/trained_models/not_existing_model/_stats`)
.auth(USER.ML_POWERUSER, ml.securityCommon.getPasswordForUser(USER.ML_POWERUSER))
.set(COMMON_REQUEST_HEADERS)
.expect(404);
});

it('returns an error for unauthorized user', async () => {
await supertest
.get(`/api/ml/trained_models/dfa_regression_model_n_0/_stats`)
.auth(USER.ML_UNAUTHORIZED, ml.securityCommon.getPasswordForUser(USER.ML_UNAUTHORIZED))
.set(COMMON_REQUEST_HEADERS)
.expect(403);
});
});
};
88 changes: 88 additions & 0 deletions x-pack/test/api_integration/apis/ml/trained_models/get_models.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* 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 expect from '@kbn/expect';
import { FtrProviderContext } from '../../../ftr_provider_context';
import { USER } from '../../../../functional/services/ml/security_common';
import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common_api';

export default ({ getService }: FtrProviderContext) => {
const supertest = getService('supertestWithoutAuth');
const ml = getService('ml');

describe('GET trained_models', () => {
let testModelIds: string[] = [];

before(async () => {
await ml.testResources.setKibanaTimeZoneToUTC();
testModelIds = await ml.api.createdTestTrainedModels('regression', 5, true);
await ml.api.createModelAlias('dfa_regression_model_n_0', 'dfa_regression_model_alias');
await ml.api.createIngestPipeline('dfa_regression_model_alias');
});

after(async () => {
// delete created ingest pipelines
await Promise.all(
['dfa_regression_model_alias', ...testModelIds].map((modelId) =>
ml.api.deleteIngestPipeline(modelId)
)
);
await ml.api.cleanMlIndices();
});

it('returns all trained models with associated pipelines including aliases', async () => {
const { body } = await supertest
.get(`/api/ml/trained_models?with_pipelines=true`)
.auth(USER.ML_POWERUSER, ml.securityCommon.getPasswordForUser(USER.ML_POWERUSER))
.set(COMMON_REQUEST_HEADERS)
.expect(200);
// Created models + system model
expect(body.length).to.eql(6);

const sampleModel = body.find((v: any) => v.model_id === 'dfa_regression_model_n_0');
expect(Object.keys(sampleModel.pipelines).length).to.eql(2);
});

it('returns models without pipeline in case user does not have required permission', async () => {
const { body } = await supertest
.get(`/api/ml/trained_models?with_pipelines=true`)
.auth(USER.ML_VIEWER, ml.securityCommon.getPasswordForUser(USER.ML_VIEWER))
.set(COMMON_REQUEST_HEADERS)
.expect(200);
// Created models + system model
expect(body.length).to.eql(6);
const sampleModel = body.find((v: any) => v.model_id === 'dfa_regression_model_n_0');
expect(sampleModel.pipelines).to.eql(undefined);
});

it('returns trained model by id', async () => {
const { body } = await supertest
.get(`/api/ml/trained_models/dfa_regression_model_n_1`)
.auth(USER.ML_VIEWER, ml.securityCommon.getPasswordForUser(USER.ML_VIEWER))
.set(COMMON_REQUEST_HEADERS)
.expect(200);
expect(body.length).to.eql(1);
expect(body[0].model_id).to.eql('dfa_regression_model_n_1');
});

it('returns 404 if requested trained model does not exist', async () => {
await supertest
.get(`/api/ml/trained_models/not_existing_model`)
.auth(USER.ML_POWERUSER, ml.securityCommon.getPasswordForUser(USER.ML_POWERUSER))
.set(COMMON_REQUEST_HEADERS)
.expect(404);
});

it('returns an error for unauthorized user', async () => {
await supertest
.get(`/api/ml/trained_models/dfa_regression_model_n_1`)
.auth(USER.ML_UNAUTHORIZED, ml.securityCommon.getPasswordForUser(USER.ML_UNAUTHORIZED))
.set(COMMON_REQUEST_HEADERS)
.expect(403);
});
});
};
17 changes: 17 additions & 0 deletions x-pack/test/api_integration/apis/ml/trained_models/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* 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 { FtrProviderContext } from '../../../ftr_provider_context';

export default function ({ loadTestFile }: FtrProviderContext) {
describe('trained models', function () {
loadTestFile(require.resolve('./get_models'));
loadTestFile(require.resolve('./get_model_stats'));
loadTestFile(require.resolve('./get_model_pipelines'));
loadTestFile(require.resolve('./delete_model'));
});
}
Loading

0 comments on commit 590883f

Please sign in to comment.