Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/platform/plugins/shared/workflows_management/moon.yml
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ dependsOn:
- '@kbn/logging'
- '@kbn/core-saved-objects-api-server'
- '@kbn/management-settings-ids'
- '@kbn/rule-data-utils'
tags:
- plugin
- prod
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,14 @@ export type WorkflowsConfig = Record<string, unknown>;

export type WorkflowsSecrets = Record<string, unknown>;

export type AlertStateId = 'new' | 'ongoing' | 'recovered';
export type AlertStates = Record<AlertStateId, boolean>;

export interface WorkflowsActionParams {
subAction: string;
subActionParams: {
workflowId: string;
summaryMode?: boolean;
alertStates?: AlertStates;
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,11 @@ describe('WorkflowsParamsFields', () => {
expect(mockEditAction).toHaveBeenCalledWith('subAction', 'run', 0);
expect(mockEditAction).toHaveBeenCalledWith(
'subActionParams',
{ workflowId: '', summaryMode: true },
{
workflowId: '',
summaryMode: true,
alertStates: { new: true, ongoing: false, recovered: false },
},
0
);
});
Expand Down Expand Up @@ -822,6 +826,212 @@ describe('WorkflowsParamsFields', () => {
});
});

describe('Alert state checkboxes (alertStates parameter)', () => {
test('should render alert state checkboxes', async () => {
await act(async () => {
renderWithIntl(<WorkflowsParamsFields {...defaultProps} />);
});

await waitFor(() => {
expect(screen.getAllByText('Run workflow for').length).toBeGreaterThan(0);
expect(screen.getByLabelText('New alerts')).toBeInTheDocument();
expect(screen.getByLabelText('Ongoing alerts')).toBeInTheDocument();
expect(screen.getByLabelText('Recovered alerts')).toBeInTheDocument();
});
});

test('should default to new=checked, ongoing=unchecked, recovered=unchecked', async () => {
await act(async () => {
renderWithIntl(<WorkflowsParamsFields {...defaultProps} />);
});

await waitFor(() => {
expect(screen.getByLabelText('New alerts')).toBeChecked();
expect(screen.getByLabelText('Ongoing alerts')).not.toBeChecked();
expect(screen.getByLabelText('Recovered alerts')).not.toBeChecked();
});
});

test('should reflect alertStates from action params', async () => {
const props = {
...defaultProps,
actionParams: {
subAction: 'run',
subActionParams: {
workflowId: 'test-workflow',
alertStates: { new: true, ongoing: true, recovered: false },
},
} as WorkflowsActionParams,
};

await act(async () => {
renderWithIntl(<WorkflowsParamsFields {...props} />);
});

await waitFor(() => {
expect(screen.getByLabelText('New alerts')).toBeChecked();
expect(screen.getByLabelText('Ongoing alerts')).toBeChecked();
expect(screen.getByLabelText('Recovered alerts')).not.toBeChecked();
});
});

test('should toggle alert state on checkbox click', async () => {
const props = {
...defaultProps,
actionParams: {
subAction: 'run',
subActionParams: {
workflowId: 'test-workflow',
summaryMode: true,
alertStates: { new: true, ongoing: false, recovered: false },
},
} as WorkflowsActionParams,
};

await act(async () => {
renderWithIntl(<WorkflowsParamsFields {...props} />);
});

const recoveredCheckbox = screen.getByLabelText('Recovered alerts');
await act(async () => {
fireEvent.click(recoveredCheckbox);
});

await waitFor(() => {
expect(mockEditAction).toHaveBeenCalledWith(
'subActionParams',
expect.objectContaining({
alertStates: { new: true, ongoing: false, recovered: true },
}),
0
);
});
});

test('should initialize alertStates when missing from subActionParams', async () => {
const props = {
...defaultProps,
actionParams: {
subAction: 'run',
subActionParams: {
workflowId: 'test-workflow',
summaryMode: true,
},
} as WorkflowsActionParams,
};

await act(async () => {
renderWithIntl(<WorkflowsParamsFields {...props} />);
});

await waitFor(() => {
expect(mockEditAction).toHaveBeenCalledWith(
'subActionParams',
expect.objectContaining({
alertStates: { new: true, ongoing: false, recovered: false },
}),
0
);
});
});

test('should initialize alertStates together with summaryMode in a single editAction call', async () => {
const props = {
...defaultProps,
actionParams: {
subAction: 'run',
subActionParams: {
workflowId: 'test-workflow',
},
} as any,
};

await act(async () => {
renderWithIntl(<WorkflowsParamsFields {...props} />);
});

await waitFor(() => {
expect(mockEditAction).toHaveBeenCalledWith(
'subActionParams',
{
workflowId: 'test-workflow',
summaryMode: true,
alertStates: { new: true, ongoing: false, recovered: false },
},
0
);
});
});

test('should hide alert state checkboxes for Security detection rules', async () => {
const props = {
...defaultProps,
ruleTypeId: 'siem.queryRule',
};

await act(async () => {
renderWithIntl(<WorkflowsParamsFields {...props} />);
});

await waitFor(() => {
expect(screen.queryByLabelText('New alerts')).not.toBeInTheDocument();
expect(screen.queryByLabelText('Ongoing alerts')).not.toBeInTheDocument();
expect(screen.queryByLabelText('Recovered alerts')).not.toBeInTheDocument();
});
});

test('should hide alert state checkboxes for attack-discovery rules', async () => {
const props = {
...defaultProps,
ruleTypeId: 'attack-discovery',
};

await act(async () => {
renderWithIntl(<WorkflowsParamsFields {...props} />);
});

await waitFor(() => {
expect(screen.queryByLabelText('New alerts')).not.toBeInTheDocument();
expect(screen.queryByLabelText('Ongoing alerts')).not.toBeInTheDocument();
expect(screen.queryByLabelText('Recovered alerts')).not.toBeInTheDocument();
});
});

test('should show alert state checkboxes for non-SIEM rules', async () => {
const props = {
...defaultProps,
ruleTypeId: 'metrics.alert.threshold',
};

await act(async () => {
renderWithIntl(<WorkflowsParamsFields {...props} />);
});

await waitFor(() => {
expect(screen.getByLabelText('New alerts')).toBeInTheDocument();
expect(screen.getByLabelText('Ongoing alerts')).toBeInTheDocument();
expect(screen.getByLabelText('Recovered alerts')).toBeInTheDocument();
});
});

test('should show alert state checkboxes when ruleTypeId is undefined', async () => {
const props = {
...defaultProps,
ruleTypeId: undefined,
};

await act(async () => {
renderWithIntl(<WorkflowsParamsFields {...props} />);
});

await waitFor(() => {
expect(screen.getByLabelText('New alerts')).toBeInTheDocument();
expect(screen.getByLabelText('Ongoing alerts')).toBeInTheDocument();
expect(screen.getByLabelText('Recovered alerts')).toBeInTheDocument();
});
});
});

describe('Action frequency (summaryMode parameter)', () => {
test('should render Action frequency section with switch', async () => {
await act(async () => {
Expand Down Expand Up @@ -854,7 +1064,7 @@ describe('WorkflowsParamsFields', () => {
await waitFor(() => {
expect(mockEditAction).toHaveBeenCalledWith(
'subActionParams',
{ workflowId: 'test-workflow', summaryMode: true },
expect.objectContaining({ workflowId: 'test-workflow', summaryMode: true }),
0
);
});
Expand All @@ -876,7 +1086,11 @@ describe('WorkflowsParamsFields', () => {
await waitFor(() => {
expect(mockEditAction).toHaveBeenCalledWith(
'subActionParams',
{ workflowId: '', summaryMode: true },
{
workflowId: '',
summaryMode: true,
alertStates: { new: true, ongoing: false, recovered: false },
},
0
);
});
Expand Down
Loading
Loading