From e34c12c83e7453cac919ea1b92ab640442439a10 Mon Sep 17 00:00:00 2001 From: Andrew Cholakian Date: Thu, 4 Jun 2020 13:32:31 -0500 Subject: [PATCH] Add unit tests --- .../refine_potential_matches.test.ts | 112 ++++++++++++++++++ .../server/lib/requests/search/fetch_chunk.ts | 4 +- .../requests/search/find_potential_matches.ts | 44 +------ .../search/refine_potential_matches.ts | 24 ++-- 4 files changed, 122 insertions(+), 62 deletions(-) create mode 100644 x-pack/plugins/uptime/server/lib/requests/search/__tests__/refine_potential_matches.test.ts diff --git a/x-pack/plugins/uptime/server/lib/requests/search/__tests__/refine_potential_matches.test.ts b/x-pack/plugins/uptime/server/lib/requests/search/__tests__/refine_potential_matches.test.ts new file mode 100644 index 00000000000000..fe89139e7a0589 --- /dev/null +++ b/x-pack/plugins/uptime/server/lib/requests/search/__tests__/refine_potential_matches.test.ts @@ -0,0 +1,112 @@ +/* + * 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 { fullyMatchingIds } from '../refine_potential_matches'; +import { MonitorLocCheckGroup } from '..'; + +const mockQueryResult = (opts: { latestSummary: any; latestMatching: any }) => { + return { + aggregations: { + monitor: { + buckets: [ + { + key: 'my-monitor', + location: { + buckets: [ + { + key: 'my-location', + summaries: { + latest: { + hits: { + hits: [ + { + _source: opts.latestSummary, + }, + ], + }, + }, + }, + latest_matching: { + top: { + hits: { + hits: [ + { + _source: opts.latestMatching, + }, + ], + }, + }, + }, + }, + ], + }, + }, + ], + }, + }, + }; +}; + +describe('fully matching IDs', () => { + it('should exclude items whose latest result does not match', () => { + const queryRes = mockQueryResult({ + latestSummary: { + '@timestamp': '2020-06-04T12:39:54.698-0500', + monitor: { + check_group: 'latest-summary-check-group', + }, + summary: { + up: 1, + down: 0, + }, + }, + latestMatching: { + '@timestamp': '2019-06-04T12:39:54.698-0500', + summary: { + up: 1, + down: 0, + }, + }, + }); + const res = fullyMatchingIds(queryRes, undefined); + const expected = new Map(); + expect(res).toEqual(expected); + }); + + it('should include items whose latest result does match', () => { + const queryRes = mockQueryResult({ + latestSummary: { + '@timestamp': '2020-06-04T12:39:54.698-0500', + monitor: { + check_group: 'latest-summary-check-group', + }, + summary: { + up: 1, + down: 0, + }, + }, + latestMatching: { + '@timestamp': '2020-06-04T12:39:54.698-0500', + summary: { + up: 1, + down: 0, + }, + }, + }); + const res = fullyMatchingIds(queryRes, undefined); + const expected = new Map(); + expected.set('my-monitor', [ + { + checkGroup: 'latest-summary-check-group', + location: 'my-location', + monitorId: 'my-monitor', + status: 'up', + summaryTimestamp: '2020-06-04T12:39:54.698-0500', + }, + ]); + expect(res).toEqual(expected); + }); +}); diff --git a/x-pack/plugins/uptime/server/lib/requests/search/fetch_chunk.ts b/x-pack/plugins/uptime/server/lib/requests/search/fetch_chunk.ts index 77676ac9a63731..2a5f1f1261cb37 100644 --- a/x-pack/plugins/uptime/server/lib/requests/search/fetch_chunk.ts +++ b/x-pack/plugins/uptime/server/lib/requests/search/fetch_chunk.ts @@ -26,12 +26,12 @@ export const fetchChunk: ChunkFetcher = async ( searchAfter: any, size: number ): Promise => { - const { monitorIds, checkGroups, searchAfter: foundSearchAfter } = await findPotentialMatches( + const { monitorIds, searchAfter: foundSearchAfter } = await findPotentialMatches( queryContext, searchAfter, size ); - const matching = await refinePotentialMatches(queryContext, monitorIds, checkGroups); + const matching = await refinePotentialMatches(queryContext, monitorIds); return { monitorGroups: matching, diff --git a/x-pack/plugins/uptime/server/lib/requests/search/find_potential_matches.ts b/x-pack/plugins/uptime/server/lib/requests/search/find_potential_matches.ts index a3e7324086073f..ac4ff91230b959 100644 --- a/x-pack/plugins/uptime/server/lib/requests/search/find_potential_matches.ts +++ b/x-pack/plugins/uptime/server/lib/requests/search/find_potential_matches.ts @@ -8,12 +8,8 @@ import { get, set } from 'lodash'; import { CursorDirection } from '../../../../common/runtime_types'; import { QueryContext } from './query_context'; -// This is the first phase of the query. In it, we find the most recent check groups that matched the given query. -// Note that these check groups may not be the most recent groups for the matching monitor ID! We'll filter those /** - * This is the first phase of the query. In it, we find the most recent check groups that matched the given query. - * Note that these check groups may not be the most recent groups for the matching monitor ID. They'll be filtered - * out in the next phase. + * This is the first phase of the query. In it, we find all monitor IDs that have ever matched the given filters. * @param queryContext the data and resources needed to perform the query * @param searchAfter indicates where Elasticsearch should continue querying on subsequent requests, if at all * @param size the minimum size of the matches to chunk @@ -24,29 +20,14 @@ export const findPotentialMatches = async ( size: number ) => { const queryResult = await query(queryContext, searchAfter, size); - const checkGroups = new Set(); const monitorIds: string[] = []; get(queryResult, 'aggregations.monitors.buckets', []).forEach((b: any) => { const monitorId = b.key.monitor_id; monitorIds.push(monitorId); - - // Doc count can be zero if status filter optimization does not match - if (b.doc_count > 0) { - // Here we grab the most recent 2 check groups per location and add them to the list. - // Why 2? Because the most recent one may be a partial result from mode: all, and hence not match a summary doc. - b.locations.buckets.forEach((lb: any) => { - lb.ips.buckets.forEach((ib: any) => { - ib.top.hits.hits.forEach((h: any) => { - checkGroups.add(h._source.monitor.check_group); - }); - }); - }); - } }); return { monitorIds, - checkGroups, searchAfter: queryResult.aggregations?.monitors?.after_key, }; }; @@ -89,29 +70,6 @@ const queryBody = async (queryContext: QueryContext, searchAfter: any, size: num }, ], }, - aggs: { - // Here we grab the most recent 2 check groups per location. - // Why 2? Because the most recent one may not be for a summary, it may be incomplete. - locations: { - terms: { field: 'observer.geo.name', missing: '__missing__' }, - aggs: { - ips: { - terms: { field: 'monitor.ip', missing: '0.0.0.0' }, - aggs: { - top: { - top_hits: { - sort: [{ '@timestamp': 'desc' }], - _source: { - includes: ['monitor.check_group', '@timestamp'], - }, - size: 2, - }, - }, - }, - }, - }, - }, - }, }, }, }; diff --git a/x-pack/plugins/uptime/server/lib/requests/search/refine_potential_matches.ts b/x-pack/plugins/uptime/server/lib/requests/search/refine_potential_matches.ts index 21629f200d0184..57b07c5fb5edd3 100644 --- a/x-pack/plugins/uptime/server/lib/requests/search/refine_potential_matches.ts +++ b/x-pack/plugins/uptime/server/lib/requests/search/refine_potential_matches.ts @@ -18,18 +18,14 @@ import { MonitorGroups, MonitorLocCheckGroup } from './fetch_page'; // check groups for their associated monitor IDs. If not, it discards the result. export const refinePotentialMatches = async ( queryContext: QueryContext, - potentialMatchMonitorIDs: string[], - potentialMatchCheckGroups: Set + potentialMatchMonitorIDs: string[] ): Promise => { if (potentialMatchMonitorIDs.length === 0) { return []; } - const recentGroupsMatchingStatus = await fullyMatchingIds( - queryContext, - potentialMatchMonitorIDs, - potentialMatchCheckGroups - ); + const queryResult = await query(queryContext, potentialMatchMonitorIDs); + const recentGroupsMatchingStatus = await fullyMatchingIds(queryResult, queryContext.statusFilter); // Return the monitor groups filtering out potential matches that weren't current const matches: MonitorGroups[] = potentialMatchMonitorIDs @@ -49,15 +45,9 @@ export const refinePotentialMatches = async ( return matches; }; -const fullyMatchingIds = async ( - queryContext: QueryContext, - potentialMatchMonitorIDs: string[], - potentialMatchCheckGroups: Set -) => { - const mostRecentQueryResult = await mostRecentCheckGroups(queryContext, potentialMatchMonitorIDs); - +export const fullyMatchingIds = (queryResult: any, statusFilter?: string) => { const matching = new Map(); - MonitorLoop: for (const monBucket of mostRecentQueryResult.aggregations.monitor.buckets) { + MonitorLoop: for (const monBucket of queryResult.aggregations.monitor.buckets) { const monitorId: string = monBucket.key; const groups: MonitorLocCheckGroup[] = []; @@ -80,7 +70,7 @@ const fullyMatchingIds = async ( // This monitor doesn't match, so just skip ahead and don't add it to the output // Only skip in case of up statusFilter, for a monitor to be up, all checks should be up - if (queryContext?.statusFilter === 'up' && queryContext.statusFilter !== status) { + if (statusFilter === 'up' && statusFilter !== status) { continue MonitorLoop; } @@ -102,7 +92,7 @@ const fullyMatchingIds = async ( return matching; }; -export const mostRecentCheckGroups = async ( +export const query = async ( queryContext: QueryContext, potentialMatchMonitorIDs: string[] ): Promise => {