Skip to content

Commit

Permalink
[ML] Added jest and API integration tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
walterra committed Jul 9, 2020
1 parent 5daa854 commit 3f45214
Show file tree
Hide file tree
Showing 4 changed files with 157 additions and 15 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* 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 React from 'react';

import { getFieldType } from './use_column_chart';

describe('getFieldType', () => {
it('should return the Kibana field type for a given EUI data grid schema', () => {
expect(getFieldType('text')).toBe('string');
expect(getFieldType('datetime')).toBe('date');
expect(getFieldType('numeric')).toBe('number');
expect(getFieldType('boolean')).toBe('boolean');
expect(getFieldType('json')).toBe('object');
expect(getFieldType('non-aggregatable')).toBe(undefined);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export interface Field {
cardinality: number;
}

interface HistogramField {
export interface HistogramField {
fieldName: string;
type: string;
}
Expand Down Expand Up @@ -308,7 +308,8 @@ export class DataVisualizer {
});

const aggsPath = getSamplerAggregationsResponsePath(samplerShardSize);
const aggregations = _.get(respStats.aggregations, aggsPath);
const aggregations =
aggsPath.length > 0 ? _.get(respStats.aggregations, aggsPath) : respStats.aggregations;

return Object.keys(aggregations).reduce((p, aggName) => {
const stats = [aggregations[aggName].min, aggregations[aggName].max];
Expand All @@ -317,12 +318,8 @@ export class DataVisualizer {

let aggInterval = 1;

if (delta > MAX_CHART_COLUMNS) {
aggInterval = Math.round(delta / MAX_CHART_COLUMNS);
}

if (delta <= 1) {
aggInterval = delta / MAX_CHART_COLUMNS;
if (delta > MAX_CHART_COLUMNS || delta <= 1) {
aggInterval = delta / (MAX_CHART_COLUMNS - 1);
}

p[aggName] = { interval: aggInterval, min: stats[0], max: stats[1] };
Expand Down Expand Up @@ -394,7 +391,10 @@ export class DataVisualizer {
});

const aggsPath = getSamplerAggregationsResponsePath(samplerShardSize);
const aggregations = _.get(respChartsData.aggregations, aggsPath);
const aggregations =
aggsPath.length > 0
? _.get(respChartsData.aggregations, aggsPath)
: respChartsData.aggregations;

const chartsData: ChartData[] = fields.map(
(field): ChartData => {
Expand Down
12 changes: 6 additions & 6 deletions x-pack/plugins/ml/server/routes/data_visualizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import { RequestHandlerContext } from 'kibana/server';
import { wrapError } from '../client/error_wrapper';
import { DataVisualizer } from '../models/data_visualizer';
import { Field } from '../models/data_visualizer/data_visualizer';
import { Field, HistogramField } from '../models/data_visualizer/data_visualizer';
import {
dataVisualizerFieldHistogramsSchema,
dataVisualizerFieldStatsSchema,
Expand Down Expand Up @@ -70,7 +70,7 @@ function getHistogramsForFields(
context: RequestHandlerContext,
indexPatternTitle: string,
query: any,
fields: Field[],
fields: HistogramField[],
samplerShardSize: number
) {
const dv = new DataVisualizer(context.ml!.mlClient.callAsCurrentUser);
Expand All @@ -84,14 +84,14 @@ export function dataVisualizerRoutes({ router, mlLicense }: RouteInitialization)
/**
* @apiGroup DataVisualizer
*
* @api {post} /api/ml/data_visualizer/get_field_stats/:indexPatternTitle Get stats for fields
* @apiName GetStatsForFields
* @apiDescription Returns the stats on individual fields in the specified index pattern.
* @api {post} /api/ml/data_visualizer/get_field_stats/:indexPatternTitle Get histograms for fields
* @apiName GetHistogramsForFields
* @apiDescription Returns the histograms on a list fields in the specified index pattern.
*
* @apiSchema (params) indexPatternTitleSchema
* @apiSchema (body) dataVisualizerFieldHistogramsSchema
*
* @apiSuccess {Object} fieldName stats by field, keyed on the name of the field.
* @apiSuccess {Object} fieldName histograms by field, keyed on the name of the field.
*/
router.post(
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/*
* 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 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';

// eslint-disable-next-line import/no-default-export
export default ({ getService }: FtrProviderContext) => {
const esArchiver = getService('esArchiver');
const supertest = getService('supertestWithoutAuth');
const ml = getService('ml');

const fieldHistogramsTestData = {
testTitle: 'returns histogram data for fields',
index: 'ft_farequote',
user: USER.ML_POWERUSER,
requestBody: {
query: { bool: { should: [{ match_phrase: { airline: 'JZA' } }], minimum_should_match: 1 } },
fields: [
{ fieldName: '@timestamp', type: 'date' },
{ fieldName: 'airline', type: 'string' },
{ fieldName: 'responsetime', type: 'number' },
],
samplerShardSize: -1, // No sampling, as otherwise counts could vary on each run.
},
expected: {
responseCode: 200,
responseBody: [
{
dataLength: 20,
type: 'numeric',
id: '@timestamp',
},
{ type: 'ordinal', dataLength: 1, id: 'airline' },
{
dataLength: 20,
type: 'numeric',
id: 'responsetime',
},
],
},
};

const errorTestData = {
testTitle: 'returns error for index which does not exist',
index: 'ft_farequote_not_exists',
user: USER.ML_POWERUSER,
requestBody: {
query: { bool: { must: [{ match_all: {} }] } },
fields: [{ fieldName: 'responsetime', type: 'number' }],
samplerShardSize: -1,
},
expected: {
responseCode: 404,
responseBody: {
statusCode: 404,
error: 'Not Found',
message:
'[index_not_found_exception] no such index [ft_farequote_not_exists], with { resource.type="index_or_alias" & resource.id="ft_farequote_not_exists" & index_uuid="_na_" & index="ft_farequote_not_exists" }',
},
},
};

async function runGetFieldHistogramsRequest(
index: string,
user: USER,
requestBody: object,
expectedResponsecode: number
): Promise<any> {
const { body } = await supertest
.post(`/api/ml/data_visualizer/get_field_histograms/${index}`)
.auth(user, ml.securityCommon.getPasswordForUser(user))
.set(COMMON_REQUEST_HEADERS)
.send(requestBody)
.expect(expectedResponsecode);

return body;
}

describe('get_field_histograms', function () {
before(async () => {
await esArchiver.loadIfNeeded('ml/farequote');
await ml.testResources.setKibanaTimeZoneToUTC();
});

it(`${fieldHistogramsTestData.testTitle}`, async () => {
const body = await runGetFieldHistogramsRequest(
fieldHistogramsTestData.index,
fieldHistogramsTestData.user,
fieldHistogramsTestData.requestBody,
fieldHistogramsTestData.expected.responseCode
);

const expected = fieldHistogramsTestData.expected;

const actual = body.map((b: any) => ({
dataLength: b.data.length,
type: b.type,
id: b.id,
}));
expect(actual).to.eql(expected.responseBody);
});

it(`${errorTestData.testTitle}`, async () => {
const body = await runGetFieldHistogramsRequest(
errorTestData.index,
errorTestData.user,
errorTestData.requestBody,
errorTestData.expected.responseCode
);

expect(body.error).to.eql(errorTestData.expected.responseBody.error);
expect(body.message).to.eql(errorTestData.expected.responseBody.message);
});
});
};

0 comments on commit 3f45214

Please sign in to comment.