diff --git a/x-pack/plugins/security_solution/public/resolver/store/data/selectors.test.ts b/x-pack/plugins/security_solution/public/resolver/store/data/selectors.test.ts index 0826391a106881..6786a93f1d9cac 100644 --- a/x-pack/plugins/security_solution/public/resolver/store/data/selectors.test.ts +++ b/x-pack/plugins/security_solution/public/resolver/store/data/selectors.test.ts @@ -14,6 +14,7 @@ import { mockTreeWith2AncestorsAndNoChildren, mockTreeWith1AncestorAnd2ChildrenAndAllNodesHave2GraphableEvents, mockTreeWithAllProcessesTerminated, + mockTreeWithNoProcessEvents, } from '../mocks/resolver_tree'; import { uniquePidForProcess } from '../../models/process_event'; import { EndpointEvent } from '../../../../common/endpoint/types'; @@ -408,4 +409,26 @@ describe('data state', () => { expect(selectors.graphableProcesses(state()).length).toBe(4); }); }); + describe('with a tree with no process events', () => { + beforeEach(() => { + const tree = mockTreeWithNoProcessEvents(); + actions.push({ + type: 'serverReturnedResolverData', + payload: { + result: tree, + // this value doesn't matter + databaseDocumentID: '', + }, + }); + }); + it('should return an empty layout', () => { + expect(selectors.layout(state())).toMatchInlineSnapshot(` + Object { + "ariaLevels": Map {}, + "edgeLineSegments": Array [], + "processNodePositions": Map {}, + } + `); + }); + }); }); diff --git a/x-pack/plugins/security_solution/public/resolver/store/data/selectors.ts b/x-pack/plugins/security_solution/public/resolver/store/data/selectors.ts index ea0cb8663d11d0..10ace895b32671 100644 --- a/x-pack/plugins/security_solution/public/resolver/store/data/selectors.ts +++ b/x-pack/plugins/security_solution/public/resolver/store/data/selectors.ts @@ -374,9 +374,9 @@ export const layout = createSelector( // find the origin node const originNode = indexedProcessTreeModel.processEvent(indexedProcessTree, originID); - if (!originNode) { - // this should only happen if the `ResolverTree` from the server has an entity ID with no matching lifecycle events. - throw new Error('Origin node not found in ResolverTree'); + if (originNode === null) { + // If a tree is returned that has no process events for the origin, this can happen. + return taxiLayout; } // Find the position of the origin, we'll center the map on it intrinsically diff --git a/x-pack/plugins/security_solution/public/resolver/store/mocks/resolver_tree.ts b/x-pack/plugins/security_solution/public/resolver/store/mocks/resolver_tree.ts index ae43955f4c47c7..6a8ab61ccf9b64 100644 --- a/x-pack/plugins/security_solution/public/resolver/store/mocks/resolver_tree.ts +++ b/x-pack/plugins/security_solution/public/resolver/store/mocks/resolver_tree.ts @@ -226,3 +226,33 @@ export function mockTreeWith1AncestorAnd2ChildrenAndAllNodesHave2GraphableEvents lifecycle: [origin, originClone], } as unknown) as ResolverTree; } + +export function mockTreeWithNoProcessEvents(): ResolverTree { + return { + entityID: 'entityID', + children: { + childNodes: [], + nextChild: null, + }, + relatedEvents: { + events: [], + nextEvent: null, + }, + relatedAlerts: { + alerts: [], + nextAlert: null, + }, + lifecycle: [], + ancestry: { + ancestors: [], + nextAncestor: null, + }, + stats: { + totalAlerts: 0, + events: { + total: 0, + byCategory: {}, + }, + }, + }; +}