Skip to content

Commit

Permalink
[Security Solution] Adds unit tests for simple diff algorithm (#184483)
Browse files Browse the repository at this point in the history
## Summary

Adds unit tests in accordance to
#180158

Abstracts the `simpleDiffAlgorithm` function used in the prebuilt rule
upgrade workflow into `singleLineStringDiffAlgorithm` and
`numberDiffAlgorithm` and adds unit tests for both cases

Addresses the following test cases defined in the [related RFC
section](https://github.com/elastic/kibana/blob/4c9ab711b2a59ebec60ce5f1de18122d7405f9a0/x-pack/plugins/security_solution/docs/rfcs/detection_response/prebuilt_rules_customization.md#single-line-string-fields)
table

- [x] AAA
- [x] ABA
- [x] AAB
- [x] ABB
- [x] ABC
- [x] -AA
- [x] -AB


### Checklist

Delete any items that are not applicable to this PR.

- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [ ] [Flaky Test
Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was
used on any tests changed


### For maintainers

- [ ] This was checked for breaking API changes and was [labeled
appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)
  • Loading branch information
dplumlee authored May 31, 2024
1 parent 464f797 commit 06fc22a
Show file tree
Hide file tree
Showing 5 changed files with 328 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import type { ThreeVersionsOf } from '../../../../../../../../common/api/detection_engine';
import {
ThreeWayDiffOutcome,
ThreeWayMergeOutcome,
MissingVersion,
} from '../../../../../../../../common/api/detection_engine';
import { numberDiffAlgorithm } from './number_diff_algorithm';

describe('numberDiffAlgorithm', () => {
it('returns current_version as merged output if there is no update', () => {
const mockVersions: ThreeVersionsOf<number> = {
base_version: 1,
current_version: 1,
target_version: 1,
};

const result = numberDiffAlgorithm(mockVersions);

expect(result).toEqual(
expect.objectContaining({
merged_version: mockVersions.current_version,
diff_outcome: ThreeWayDiffOutcome.StockValueNoUpdate,
merge_outcome: ThreeWayMergeOutcome.Current,
has_conflict: false,
})
);
});

it('returns current_version as merged output if current_version is different and there is no update', () => {
const mockVersions: ThreeVersionsOf<number> = {
base_version: 1,
current_version: 2,
target_version: 1,
};

const result = numberDiffAlgorithm(mockVersions);

expect(result).toEqual(
expect.objectContaining({
merged_version: mockVersions.current_version,
diff_outcome: ThreeWayDiffOutcome.CustomizedValueNoUpdate,
merge_outcome: ThreeWayMergeOutcome.Current,
has_conflict: false,
})
);
});

it('returns target_version as merged output if current_version is the same and there is an update', () => {
const mockVersions: ThreeVersionsOf<number> = {
base_version: 1,
current_version: 1,
target_version: 2,
};

const result = numberDiffAlgorithm(mockVersions);

expect(result).toEqual(
expect.objectContaining({
merged_version: mockVersions.target_version,
diff_outcome: ThreeWayDiffOutcome.StockValueCanUpdate,
merge_outcome: ThreeWayMergeOutcome.Target,
has_conflict: false,
})
);
});

it('returns current_version as merged output if current version is different but it matches the update', () => {
const mockVersions: ThreeVersionsOf<number> = {
base_version: 1,
current_version: 2,
target_version: 2,
};

const result = numberDiffAlgorithm(mockVersions);

expect(result).toEqual(
expect.objectContaining({
merged_version: mockVersions.current_version,
diff_outcome: ThreeWayDiffOutcome.CustomizedValueSameUpdate,
merge_outcome: ThreeWayMergeOutcome.Current,
has_conflict: false,
})
);
});

it('returns current_version as merged output if all three versions are different', () => {
const mockVersions: ThreeVersionsOf<number> = {
base_version: 1,
current_version: 2,
target_version: 3,
};

const result = numberDiffAlgorithm(mockVersions);

expect(result).toEqual(
expect.objectContaining({
merged_version: mockVersions.current_version,
diff_outcome: ThreeWayDiffOutcome.CustomizedValueCanUpdate,
merge_outcome: ThreeWayMergeOutcome.Conflict,
has_conflict: true,
})
);
});

describe('if base_version is missing', () => {
it('returns current_version as merged output if current_version and target_version are the same', () => {
const mockVersions: ThreeVersionsOf<number> = {
base_version: MissingVersion,
current_version: 1,
target_version: 1,
};

const result = numberDiffAlgorithm(mockVersions);

expect(result).toEqual(
expect.objectContaining({
merged_version: mockVersions.current_version,
diff_outcome: ThreeWayDiffOutcome.StockValueNoUpdate,
merge_outcome: ThreeWayMergeOutcome.Current,
has_conflict: false,
})
);
});

it('returns target_version as merged output if current_version and target_version are different', () => {
const mockVersions: ThreeVersionsOf<number> = {
base_version: MissingVersion,
current_version: 1,
target_version: 2,
};

const result = numberDiffAlgorithm(mockVersions);

expect(result).toEqual(
expect.objectContaining({
merged_version: mockVersions.target_version,
diff_outcome: ThreeWayDiffOutcome.StockValueCanUpdate,
merge_outcome: ThreeWayMergeOutcome.Target,
has_conflict: false,
})
);
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { simpleDiffAlgorithm } from './simple_diff_algorithm';

export const numberDiffAlgorithm = simpleDiffAlgorithm<number>;
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ import {
ThreeWayMergeOutcome,
} from '../../../../../../../../common/api/detection_engine/prebuilt_rules';

/**
* The default diff algorithm, diffs versions passed using a simple lodash `isEqual` comparison
*
* Meant to be used with primitive types (strings, numbers, booleans), NOT Arrays or Objects
*/
export const simpleDiffAlgorithm = <TValue>(
versions: ThreeVersionsOf<TValue>
): ThreeWayDiff<TValue> => {
Expand Down Expand Up @@ -82,7 +87,7 @@ const mergeVersions = <TValue>({
case ThreeWayDiffOutcome.CustomizedValueCanUpdate: {
return {
mergeOutcome: ThreeWayMergeOutcome.Conflict,
mergedVersion: targetVersion,
mergedVersion: currentVersion,
};
}
default:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import type { ThreeVersionsOf } from '../../../../../../../../common/api/detection_engine';
import {
ThreeWayDiffOutcome,
ThreeWayMergeOutcome,
MissingVersion,
} from '../../../../../../../../common/api/detection_engine';
import { singleLineStringDiffAlgorithm } from './single_line_string_diff_algorithm';

describe('singleLineStringDiffAlgorithm', () => {
it('returns current_version as merged output if there is no update', () => {
const mockVersions: ThreeVersionsOf<string> = {
base_version: 'A',
current_version: 'A',
target_version: 'A',
};

const result = singleLineStringDiffAlgorithm(mockVersions);

expect(result).toEqual(
expect.objectContaining({
merged_version: mockVersions.current_version,
diff_outcome: ThreeWayDiffOutcome.StockValueNoUpdate,
merge_outcome: ThreeWayMergeOutcome.Current,
has_conflict: false,
})
);
});

it('returns current_version as merged output if current_version is different and there is no update', () => {
const mockVersions: ThreeVersionsOf<string> = {
base_version: 'A',
current_version: 'B',
target_version: 'A',
};

const result = singleLineStringDiffAlgorithm(mockVersions);

expect(result).toEqual(
expect.objectContaining({
merged_version: mockVersions.current_version,
diff_outcome: ThreeWayDiffOutcome.CustomizedValueNoUpdate,
merge_outcome: ThreeWayMergeOutcome.Current,
has_conflict: false,
})
);
});

it('returns target_version as merged output if current_version is the same and there is an update', () => {
const mockVersions: ThreeVersionsOf<string> = {
base_version: 'A',
current_version: 'A',
target_version: 'B',
};

const result = singleLineStringDiffAlgorithm(mockVersions);

expect(result).toEqual(
expect.objectContaining({
merged_version: mockVersions.target_version,
diff_outcome: ThreeWayDiffOutcome.StockValueCanUpdate,
merge_outcome: ThreeWayMergeOutcome.Target,
has_conflict: false,
})
);
});

it('returns current_version as merged output if current version is different but it matches the update', () => {
const mockVersions: ThreeVersionsOf<string> = {
base_version: 'A',
current_version: 'B',
target_version: 'B',
};

const result = singleLineStringDiffAlgorithm(mockVersions);

expect(result).toEqual(
expect.objectContaining({
merged_version: mockVersions.current_version,
diff_outcome: ThreeWayDiffOutcome.CustomizedValueSameUpdate,
merge_outcome: ThreeWayMergeOutcome.Current,
has_conflict: false,
})
);
});

it('returns current_version as merged output if all three versions are different', () => {
const mockVersions: ThreeVersionsOf<string> = {
base_version: 'A',
current_version: 'B',
target_version: 'C',
};

const result = singleLineStringDiffAlgorithm(mockVersions);

expect(result).toEqual(
expect.objectContaining({
merged_version: mockVersions.current_version,
diff_outcome: ThreeWayDiffOutcome.CustomizedValueCanUpdate,
merge_outcome: ThreeWayMergeOutcome.Conflict,
has_conflict: true,
})
);
});

describe('if base_version is missing', () => {
it('returns current_version as merged output if current_version and target_version are the same', () => {
const mockVersions: ThreeVersionsOf<string> = {
base_version: MissingVersion,
current_version: 'A',
target_version: 'A',
};

const result = singleLineStringDiffAlgorithm(mockVersions);

expect(result).toEqual(
expect.objectContaining({
merged_version: mockVersions.current_version,
diff_outcome: ThreeWayDiffOutcome.StockValueNoUpdate,
merge_outcome: ThreeWayMergeOutcome.Current,
has_conflict: false,
})
);
});

it('returns target_version as merged output if current_version and target_version are different', () => {
const mockVersions: ThreeVersionsOf<string> = {
base_version: MissingVersion,
current_version: 'A',
target_version: 'B',
};

const result = singleLineStringDiffAlgorithm(mockVersions);

expect(result).toEqual(
expect.objectContaining({
merged_version: mockVersions.target_version,
diff_outcome: ThreeWayDiffOutcome.StockValueCanUpdate,
merge_outcome: ThreeWayMergeOutcome.Target,
has_conflict: false,
})
);
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { simpleDiffAlgorithm } from './simple_diff_algorithm';

export const singleLineStringDiffAlgorithm = simpleDiffAlgorithm<string>;

0 comments on commit 06fc22a

Please sign in to comment.