[6.7] [ML] Adds jest tests for annotation_service. (#30193) (#30205)

This adds tests to `server/models/annotation_service`. The tests include a check if the `.key` attribute of an annotation is properly removed from an annotation before indexing it.
This commit is contained in:
Walter Rafelsberger 2019-02-07 09:41:54 +01:00 committed by GitHub
parent 06d9def36c
commit c139d4ecab
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 337 additions and 14 deletions

View file

@ -0,0 +1,85 @@
{
"index": ".ml-annotations-read",
"size": 500,
"body": {
"query": {
"bool": {
"filter": [
{
"query_string": {
"query": "type:annotation",
"analyze_wildcard": false
}
},
{
"bool": {
"must": [
{
"bool": {
"must_not": [
{
"bool": {
"filter": [
{
"range": {
"timestamp": {
"lte": 1454804100000,
"format": "epoch_millis"
}
}
},
{
"range": {
"end_timestamp": {
"lte": 1454804100000,
"format": "epoch_millis"
}
}
}
]
}
},
{
"bool": {
"filter": [
{
"range": {
"timestamp": {
"gte": 1455233399999,
"format": "epoch_millis"
}
}
},
{
"range": {
"end_timestamp": {
"gte": 1455233399999,
"format": "epoch_millis"
}
}
}
]
}
}
]
}
},
{
"exists": {
"field": "annotation"
}
},
{
"query_string": {
"analyze_wildcard": false,
"query": "job_id:jobIdMock"
}
}
]
}
}
]
}
}
}
}

View file

@ -0,0 +1,53 @@
{
"took": 1,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 2,
"relation": "eq"
},
"max_score": 0,
"hits": [
{
"_index": ".ml-annotations-6",
"_type": "doc",
"_id": "T-CNvmgBQUJYQVn7TCPA",
"_score": 0,
"_source": {
"timestamp": 1454928364298,
"end_timestamp": 1455000642848,
"annotation": "Annotation 1",
"job_id": "jobIdMock",
"type": "annotation",
"create_time": 1549385157823,
"create_username": "usernameMock",
"modified_time": 1549448867514,
"modified_username": "usernameMock"
}
},
{
"_index": ".ml-annotations-6",
"_type": "doc",
"_id": "3lVpvmgB5xYzd3PM-MSe",
"_score": 0,
"_source": {
"timestamp": 1455024146013,
"end_timestamp": 1455048912790,
"annotation": "Annotation 2",
"job_id": "jobIdMock",
"type": "annotation",
"create_time": 1549382842522,
"create_username": "usernameMock",
"modified_time": 1549448876306,
"modified_username": "usernameMock"
}
}
]
}
}

View file

@ -0,0 +1,179 @@
/*
* 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 getAnnotationsRequestMock from './__mocks__/get_annotations_request.json';
import getAnnotationsResponseMock from './__mocks__/get_annotations_response.json';
import { ANNOTATION_TYPE } from '../../../common/constants/annotations';
import { ML_ANNOTATIONS_INDEX_ALIAS_WRITE } from '../../../common/constants/index_patterns';
import { Annotation, isAnnotations } from '../../../common/types/annotations';
import { DeleteParams, GetResponse, IndexAnnotationArgs } from './annotation';
import { annotationServiceProvider } from './index';
const acknowledgedResponseMock = { acknowledged: true };
describe('annotation_service', () => {
let callWithRequestSpy: jest.Mock;
beforeEach(() => {
callWithRequestSpy = jest.fn((action: string) => {
switch (action) {
case 'delete':
case 'index':
return Promise.resolve(acknowledgedResponseMock);
case 'search':
return Promise.resolve(getAnnotationsResponseMock);
}
});
});
describe('deleteAnnotation()', () => {
it('should delete annotation', async done => {
const { deleteAnnotation } = annotationServiceProvider(callWithRequestSpy);
const annotationMockId = 'mockId';
const deleteParamsMock: DeleteParams = {
index: ML_ANNOTATIONS_INDEX_ALIAS_WRITE,
id: annotationMockId,
refresh: 'wait_for',
type: 'doc',
};
const response = await deleteAnnotation(annotationMockId);
expect(callWithRequestSpy.mock.calls[0][0]).toBe('delete');
expect(callWithRequestSpy.mock.calls[0][1]).toEqual(deleteParamsMock);
expect(response).toBe(acknowledgedResponseMock);
done();
});
});
describe('getAnnotation()', () => {
it('should get annotations for specific job', async done => {
const { getAnnotations } = annotationServiceProvider(callWithRequestSpy);
const jobIdMock = 'jobIdMock';
const indexAnnotationArgsMock: IndexAnnotationArgs = {
jobIds: [jobIdMock],
earliestMs: 1454804100000,
latestMs: 1455233399999,
maxAnnotations: 500,
};
const response: GetResponse = await getAnnotations(indexAnnotationArgsMock);
expect(callWithRequestSpy.mock.calls[0][0]).toBe('search');
expect(callWithRequestSpy.mock.calls[0][1]).toEqual(getAnnotationsRequestMock);
expect(Object.keys(response.annotations)).toHaveLength(1);
expect(response.annotations[jobIdMock]).toHaveLength(2);
expect(isAnnotations(response.annotations[jobIdMock])).toBeTruthy();
done();
});
});
describe('indexAnnotation()', () => {
it('should index annotation', async done => {
const { indexAnnotation } = annotationServiceProvider(callWithRequestSpy);
const jobIdMock = 'jobIdMock';
const annotationMock: Annotation = {
annotation: 'Annotation text',
job_id: jobIdMock,
timestamp: 1454804100000,
type: ANNOTATION_TYPE.ANNOTATION,
};
const usernameMock = 'usernameMock';
const response = await indexAnnotation(annotationMock, usernameMock);
expect(callWithRequestSpy.mock.calls[0][0]).toBe('index');
// test if the annotation has been correctly augmented
const indexParamsCheck = callWithRequestSpy.mock.calls[0][1];
const annotation = indexParamsCheck.body;
expect(annotation.create_username).toBe(usernameMock);
expect(annotation.modified_username).toBe(usernameMock);
expect(typeof annotation.create_time).toBe('number');
expect(typeof annotation.modified_time).toBe('number');
expect(response).toBe(acknowledgedResponseMock);
done();
});
it('should remove ._id and .key before updating annotation', async done => {
const { indexAnnotation } = annotationServiceProvider(callWithRequestSpy);
const jobIdMock = 'jobIdMock';
const annotationMock: Annotation = {
_id: 'mockId',
annotation: 'Updated annotation text',
job_id: jobIdMock,
key: 'A',
timestamp: 1454804100000,
type: ANNOTATION_TYPE.ANNOTATION,
};
const usernameMock = 'usernameMock';
const response = await indexAnnotation(annotationMock, usernameMock);
expect(callWithRequestSpy.mock.calls[0][0]).toBe('index');
// test if the annotation has been correctly augmented
const indexParamsCheck = callWithRequestSpy.mock.calls[0][1];
const annotation = indexParamsCheck.body;
expect(annotation.create_username).toBe(usernameMock);
expect(annotation.modified_username).toBe(usernameMock);
expect(typeof annotation.create_time).toBe('number');
expect(typeof annotation.modified_time).toBe('number');
expect(typeof annotation._id).toBe('undefined');
expect(typeof annotation.key).toBe('undefined');
expect(response).toBe(acknowledgedResponseMock);
done();
});
it('should update annotation text and the username for modified_username', async done => {
const { getAnnotations, indexAnnotation } = annotationServiceProvider(callWithRequestSpy);
const jobIdMock = 'jobIdMock';
const indexAnnotationArgsMock: IndexAnnotationArgs = {
jobIds: [jobIdMock],
earliestMs: 1454804100000,
latestMs: 1455233399999,
maxAnnotations: 500,
};
const response: GetResponse = await getAnnotations(indexAnnotationArgsMock);
const annotation: Annotation = response.annotations[jobIdMock][0];
const originalUsernameMock = 'usernameMock';
expect(annotation.create_username).toBe(originalUsernameMock);
expect(annotation.modified_username).toBe(originalUsernameMock);
const modifiedAnnotationText = 'Modified Annotation 1';
annotation.annotation = modifiedAnnotationText;
const modifiedUsernameMock = 'modifiedUsernameMock';
await indexAnnotation(annotation, modifiedUsernameMock);
expect(callWithRequestSpy.mock.calls[1][0]).toBe('index');
// test if the annotation has been correctly updated
const indexParamsCheck = callWithRequestSpy.mock.calls[1][1];
const modifiedAnnotation = indexParamsCheck.body;
expect(modifiedAnnotation.annotation).toBe(modifiedAnnotationText);
expect(modifiedAnnotation.create_username).toBe(originalUsernameMock);
expect(modifiedAnnotation.modified_username).toBe(modifiedUsernameMock);
expect(typeof modifiedAnnotation.create_time).toBe('number');
expect(typeof modifiedAnnotation.modified_time).toBe('number');
done();
});
});
});

View file

@ -20,32 +20,34 @@ import {
isAnnotations,
} from '../../../common/types/annotations';
// TODO All of the following interface/type definitions should
// eventually be replaced by the proper upstream definitions
interface EsResult {
_source: object;
_id: string;
}
interface IndexAnnotationArgs {
export interface IndexAnnotationArgs {
jobIds: string[];
earliestMs: Date;
latestMs: Date;
earliestMs: number;
latestMs: number;
maxAnnotations: number;
}
interface GetParams {
export interface GetParams {
index: string;
size: number;
body: object;
}
interface GetResponse {
export interface GetResponse {
success: true;
annotations: {
[key: string]: Annotations;
};
}
interface IndexParams {
export interface IndexParams {
index: string;
type: string;
body: Annotation;
@ -53,16 +55,21 @@ interface IndexParams {
id?: string;
}
interface DeleteParams {
export interface DeleteParams {
index: string;
type: string;
refresh?: string;
id: string;
}
export function annotationProvider(
callWithRequest: (action: string, params: IndexParams | DeleteParams | GetParams) => Promise<any>
) {
type annotationProviderParams = DeleteParams | GetParams | IndexParams;
export type callWithRequestType = (
action: string,
params: annotationProviderParams
) => Promise<any>;
export function annotationProvider(callWithRequest: callWithRequestType) {
async function indexAnnotation(annotation: Annotation, username: string) {
if (isAnnotation(annotation) === false) {
return Promise.reject(new Error('invalid annotation format'));

View file

@ -4,11 +4,10 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { annotationProvider, callWithRequestType } from './annotation';
import { annotationProvider } from './annotation';
export function annotationServiceProvider(callWithRequest) {
export function annotationServiceProvider(callWithRequest: callWithRequestType) {
return {
...annotationProvider(callWithRequest)
...annotationProvider(callWithRequest),
};
}