From 73b695d8933711805574d84c2442e8086e201417 Mon Sep 17 00:00:00 2001 From: Davis Plumlee <56367316+dplumlee@users.noreply.github.com> Date: Tue, 2 Jul 2024 08:28:09 -0400 Subject: [PATCH] [Security Solution] Adds diff algorithm and unit tests for scalar array values (#186323) ## Summary Related ticket: https://github.com/elastic/kibana/issues/180162 Adds the diff algorithm for arrays of scalar values (right now we only have fields of strings) and unit tests to cover all the base cases of what the algorithm can return. ### 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 --------- Co-authored-by: Elastic Machine --- .../three_way_diff/three_way_diff_outcome.ts | 46 ++- .../diff/calculation/algorithms/index.ts | 1 + .../algorithms/number_diff_algorithm.test.ts | 14 +- .../scalar_array_diff_algorithm.test.ts | 333 ++++++++++++++++++ .../algorithms/scalar_array_diff_algorithm.ts | 123 +++++++ .../single_line_string_diff_algorithm.test.ts | 14 +- 6 files changed, 516 insertions(+), 15 deletions(-) create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/scalar_array_diff_algorithm.test.ts create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/scalar_array_diff_algorithm.ts diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/model/diff/three_way_diff/three_way_diff_outcome.ts b/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/model/diff/three_way_diff/three_way_diff_outcome.ts index fd36a69dbde3c..afa63c01744e1 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/model/diff/three_way_diff/three_way_diff_outcome.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/model/diff/three_way_diff/three_way_diff_outcome.ts @@ -38,7 +38,51 @@ export const determineDiffOutcome = ( const baseEqlTarget = isEqual(baseVersion, targetVersion); const currentEqlTarget = isEqual(currentVersion, targetVersion); - if (baseVersion === MissingVersion) { + return getThreeWayDiffOutcome({ + baseEqlCurrent, + baseEqlTarget, + currentEqlTarget, + hasBaseVersion: baseVersion !== MissingVersion, + }); +}; + +/** + * Determines diff outcomes of array fields that do not care about order (e.g. `[1, 2 , 3] === [3, 2, 1]`) + */ +export const determineOrderAgnosticDiffOutcome = ( + baseVersion: TValue[] | MissingVersion, + currentVersion: TValue[], + targetVersion: TValue[] +): ThreeWayDiffOutcome => { + const baseSet = baseVersion === MissingVersion ? MissingVersion : new Set(baseVersion); + const currentSet = new Set(currentVersion); + const targetSet = new Set(targetVersion); + const baseEqlCurrent = isEqual(baseSet, currentSet); + const baseEqlTarget = isEqual(baseSet, targetSet); + const currentEqlTarget = isEqual(currentSet, targetSet); + + return getThreeWayDiffOutcome({ + baseEqlCurrent, + baseEqlTarget, + currentEqlTarget, + hasBaseVersion: baseVersion !== MissingVersion, + }); +}; + +interface DetermineDiffOutcomeProps { + baseEqlCurrent: boolean; + baseEqlTarget: boolean; + currentEqlTarget: boolean; + hasBaseVersion: boolean; +} + +const getThreeWayDiffOutcome = ({ + baseEqlCurrent, + baseEqlTarget, + currentEqlTarget, + hasBaseVersion, +}: DetermineDiffOutcomeProps): ThreeWayDiffOutcome => { + if (!hasBaseVersion) { /** * We couldn't find the base version of the rule in the package so further * version comparison is not possible. We assume that the rule is not diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/index.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/index.ts index 57dd3553cb4c7..f2ffb98ad5432 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/index.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/index.ts @@ -7,4 +7,5 @@ export { numberDiffAlgorithm } from './number_diff_algorithm'; export { singleLineStringDiffAlgorithm } from './single_line_string_diff_algorithm'; +export { scalarArrayDiffAlgorithm } from './scalar_array_diff_algorithm'; export { simpleDiffAlgorithm } from './simple_diff_algorithm'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/number_diff_algorithm.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/number_diff_algorithm.test.ts index 43f6c9ed97e9d..ddefb27a397da 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/number_diff_algorithm.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/number_diff_algorithm.test.ts @@ -14,7 +14,7 @@ import { import { numberDiffAlgorithm } from './number_diff_algorithm'; describe('numberDiffAlgorithm', () => { - it('returns current_version as merged output if there is no update', () => { + it('returns current_version as merged output if there is no update - scenario AAA', () => { const mockVersions: ThreeVersionsOf = { base_version: 1, current_version: 1, @@ -33,7 +33,7 @@ describe('numberDiffAlgorithm', () => { ); }); - it('returns current_version as merged output if current_version is different and there is no update', () => { + it('returns current_version as merged output if current_version is different and there is no update - scenario ABA', () => { const mockVersions: ThreeVersionsOf = { base_version: 1, current_version: 2, @@ -52,7 +52,7 @@ describe('numberDiffAlgorithm', () => { ); }); - it('returns target_version as merged output if current_version is the same and there is an update', () => { + it('returns target_version as merged output if current_version is the same and there is an update - scenario AAB', () => { const mockVersions: ThreeVersionsOf = { base_version: 1, current_version: 1, @@ -71,7 +71,7 @@ describe('numberDiffAlgorithm', () => { ); }); - it('returns current_version as merged output if current version is different but it matches the update', () => { + it('returns current_version as merged output if current version is different but it matches the update - scenario ABB', () => { const mockVersions: ThreeVersionsOf = { base_version: 1, current_version: 2, @@ -90,7 +90,7 @@ describe('numberDiffAlgorithm', () => { ); }); - it('returns current_version as merged output if all three versions are different', () => { + it('returns current_version as merged output if all three versions are different - scenario ABC', () => { const mockVersions: ThreeVersionsOf = { base_version: 1, current_version: 2, @@ -110,7 +110,7 @@ describe('numberDiffAlgorithm', () => { }); describe('if base_version is missing', () => { - it('returns current_version as merged output if current_version and target_version are the same', () => { + it('returns current_version as merged output if current_version and target_version are the same - scenario -AA', () => { const mockVersions: ThreeVersionsOf = { base_version: MissingVersion, current_version: 1, @@ -129,7 +129,7 @@ describe('numberDiffAlgorithm', () => { ); }); - it('returns target_version as merged output if current_version and target_version are different', () => { + it('returns target_version as merged output if current_version and target_version are different - scenario -AB', () => { const mockVersions: ThreeVersionsOf = { base_version: MissingVersion, current_version: 1, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/scalar_array_diff_algorithm.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/scalar_array_diff_algorithm.test.ts new file mode 100644 index 0000000000000..81f3c0272ac6e --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/scalar_array_diff_algorithm.test.ts @@ -0,0 +1,333 @@ +/* + * 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 { scalarArrayDiffAlgorithm } from './scalar_array_diff_algorithm'; + +describe('scalarArrayDiffAlgorithm', () => { + it('returns current_version as merged output if there is no update - scenario AAA', () => { + const mockVersions: ThreeVersionsOf = { + base_version: ['one', 'two', 'three'], + current_version: ['one', 'two', 'three'], + target_version: ['one', 'two', 'three'], + }; + + const result = scalarArrayDiffAlgorithm(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 - scenario ABA', () => { + const mockVersions: ThreeVersionsOf = { + base_version: ['one', 'two', 'three'], + current_version: ['one', 'three', 'four'], + target_version: ['one', 'two', 'three'], + }; + + const result = scalarArrayDiffAlgorithm(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 - scenario AAB', () => { + const mockVersions: ThreeVersionsOf = { + base_version: ['one', 'two', 'three'], + current_version: ['one', 'two', 'three'], + target_version: ['one', 'four', 'three'], + }; + + const result = scalarArrayDiffAlgorithm(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 - scenario ABB', () => { + const mockVersions: ThreeVersionsOf = { + base_version: ['one', 'two', 'three'], + current_version: ['one', 'three', 'four'], + target_version: ['one', 'four', 'three'], + }; + + const result = scalarArrayDiffAlgorithm(mockVersions); + + expect(result).toEqual( + expect.objectContaining({ + merged_version: mockVersions.current_version, + diff_outcome: ThreeWayDiffOutcome.CustomizedValueSameUpdate, + merge_outcome: ThreeWayMergeOutcome.Current, + has_conflict: false, + }) + ); + }); + + it('returns custom merged version as merged output if all three versions are different - scenario ABC', () => { + const mockVersions: ThreeVersionsOf = { + base_version: ['one', 'two', 'three'], + current_version: ['two', 'three', 'four', 'five'], + target_version: ['one', 'three', 'four', 'six'], + }; + const expectedMergedVersion = ['three', 'four', 'five', 'six']; + + const result = scalarArrayDiffAlgorithm(mockVersions); + + expect(result).toEqual( + expect.objectContaining({ + merged_version: expectedMergedVersion, + diff_outcome: ThreeWayDiffOutcome.CustomizedValueCanUpdate, + merge_outcome: ThreeWayMergeOutcome.Merged, + has_conflict: false, + }) + ); + }); + + describe('if base_version is missing', () => { + it('returns current_version as merged output if current_version and target_version are the same - scenario -AA', () => { + const mockVersions: ThreeVersionsOf = { + base_version: MissingVersion, + current_version: ['one', 'two', 'three'], + target_version: ['one', 'two', 'three'], + }; + + const result = scalarArrayDiffAlgorithm(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 - scenario -AB', () => { + const mockVersions: ThreeVersionsOf = { + base_version: MissingVersion, + current_version: ['one', 'two', 'three'], + target_version: ['one', 'four', 'three'], + }; + + const result = scalarArrayDiffAlgorithm(mockVersions); + + expect(result).toEqual( + expect.objectContaining({ + merged_version: mockVersions.target_version, + diff_outcome: ThreeWayDiffOutcome.StockValueCanUpdate, + merge_outcome: ThreeWayMergeOutcome.Target, + has_conflict: false, + }) + ); + }); + }); + + describe('edge cases', () => { + it('compares arrays agnostic of order', () => { + const mockVersions: ThreeVersionsOf = { + base_version: ['one', 'two', 'three'], + current_version: ['one', 'three', 'two'], + target_version: ['three', 'one', 'two'], + }; + + const result = scalarArrayDiffAlgorithm(mockVersions); + + expect(result).toEqual( + expect.objectContaining({ + merged_version: mockVersions.current_version, + diff_outcome: ThreeWayDiffOutcome.StockValueNoUpdate, + merge_outcome: ThreeWayMergeOutcome.Current, + has_conflict: false, + }) + ); + }); + + describe('compares arrays deduplicated', () => { + it('when values duplicated in base version', () => { + const mockVersions: ThreeVersionsOf = { + base_version: ['one', 'two', 'two'], + current_version: ['one', 'two'], + target_version: ['one', 'two'], + }; + const expectedMergedVersion = ['one', 'two']; + + const result = scalarArrayDiffAlgorithm(mockVersions); + + expect(result).toEqual( + expect.objectContaining({ + merged_version: expectedMergedVersion, + diff_outcome: ThreeWayDiffOutcome.StockValueNoUpdate, + merge_outcome: ThreeWayMergeOutcome.Current, + has_conflict: false, + }) + ); + }); + + it('when values are duplicated in current version', () => { + const mockVersions: ThreeVersionsOf = { + base_version: ['one', 'two'], + current_version: ['one', 'two', 'two'], + target_version: ['one', 'two'], + }; + const expectedMergedVersion = ['one', 'two']; + + const result = scalarArrayDiffAlgorithm(mockVersions); + + expect(result).toEqual( + expect.objectContaining({ + merged_version: expectedMergedVersion, + diff_outcome: ThreeWayDiffOutcome.StockValueNoUpdate, + merge_outcome: ThreeWayMergeOutcome.Current, + has_conflict: false, + }) + ); + }); + + it('when values are duplicated in target version', () => { + const mockVersions: ThreeVersionsOf = { + base_version: ['one', 'two'], + current_version: ['one', 'two'], + target_version: ['one', 'two', 'two'], + }; + const expectedMergedVersion = ['one', 'two']; + + const result = scalarArrayDiffAlgorithm(mockVersions); + + expect(result).toEqual( + expect.objectContaining({ + merged_version: expectedMergedVersion, + diff_outcome: ThreeWayDiffOutcome.StockValueNoUpdate, + merge_outcome: ThreeWayMergeOutcome.Current, + has_conflict: false, + }) + ); + }); + + it('when values are duplicated in all versions', () => { + const mockVersions: ThreeVersionsOf = { + base_version: ['one', 'two', 'two'], + current_version: ['two', 'two', 'three'], + target_version: ['one', 'one', 'three', 'three'], + }; + const expectedMergedVersion = ['three']; + + const result = scalarArrayDiffAlgorithm(mockVersions); + + expect(result).toEqual( + expect.objectContaining({ + merged_version: expectedMergedVersion, + diff_outcome: ThreeWayDiffOutcome.CustomizedValueCanUpdate, + merge_outcome: ThreeWayMergeOutcome.Merged, + has_conflict: false, + }) + ); + }); + }); + + describe('compares empty arrays', () => { + it('when base version is empty', () => { + const mockVersions: ThreeVersionsOf = { + base_version: [], + current_version: ['one', 'two'], + target_version: ['one', 'two'], + }; + + const result = scalarArrayDiffAlgorithm(mockVersions); + + expect(result).toEqual( + expect.objectContaining({ + merged_version: mockVersions.current_version, + diff_outcome: ThreeWayDiffOutcome.CustomizedValueSameUpdate, + merge_outcome: ThreeWayMergeOutcome.Current, + has_conflict: false, + }) + ); + }); + + it('when current version is empty', () => { + const mockVersions: ThreeVersionsOf = { + base_version: ['one', 'two'], + current_version: [], + target_version: ['one', 'two'], + }; + + const result = scalarArrayDiffAlgorithm(mockVersions); + + expect(result).toEqual( + expect.objectContaining({ + merged_version: mockVersions.current_version, + diff_outcome: ThreeWayDiffOutcome.CustomizedValueNoUpdate, + merge_outcome: ThreeWayMergeOutcome.Current, + has_conflict: false, + }) + ); + }); + + it('when target version is empty', () => { + const mockVersions: ThreeVersionsOf = { + base_version: ['one', 'two'], + current_version: ['one', 'two'], + target_version: [], + }; + + const result = scalarArrayDiffAlgorithm(mockVersions); + + expect(result).toEqual( + expect.objectContaining({ + merged_version: mockVersions.target_version, + diff_outcome: ThreeWayDiffOutcome.StockValueCanUpdate, + merge_outcome: ThreeWayMergeOutcome.Target, + has_conflict: false, + }) + ); + }); + + it('when all versions are empty', () => { + const mockVersions: ThreeVersionsOf = { + base_version: [], + current_version: [], + target_version: [], + }; + + const result = scalarArrayDiffAlgorithm(mockVersions); + + expect(result).toEqual( + expect.objectContaining({ + merged_version: [], + diff_outcome: ThreeWayDiffOutcome.StockValueNoUpdate, + merge_outcome: ThreeWayMergeOutcome.Current, + has_conflict: false, + }) + ); + }); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/scalar_array_diff_algorithm.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/scalar_array_diff_algorithm.ts new file mode 100644 index 0000000000000..18cf7f4f8b2cd --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/scalar_array_diff_algorithm.ts @@ -0,0 +1,123 @@ +/* + * 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 { difference, union, uniq } from 'lodash'; +import { assertUnreachable } from '../../../../../../../../common/utility_types'; +import type { + ThreeVersionsOf, + ThreeWayDiff, +} from '../../../../../../../../common/api/detection_engine/prebuilt_rules'; +import { + determineOrderAgnosticDiffOutcome, + determineIfValueCanUpdate, + ThreeWayDiffOutcome, + ThreeWayMergeOutcome, + MissingVersion, +} from '../../../../../../../../common/api/detection_engine/prebuilt_rules'; + +/** + * Diff algorithm used for arrays of scalar values (eg. numbers, strings, booleans, etc.) + * + * NOTE: Diffing logic will be agnostic to array order + */ +export const scalarArrayDiffAlgorithm = ( + versions: ThreeVersionsOf +): ThreeWayDiff => { + const { + base_version: baseVersion, + current_version: currentVersion, + target_version: targetVersion, + } = versions; + + const diffOutcome = determineOrderAgnosticDiffOutcome(baseVersion, currentVersion, targetVersion); + const valueCanUpdate = determineIfValueCanUpdate(diffOutcome); + + const { mergeOutcome, mergedVersion } = mergeVersions({ + baseVersion, + currentVersion, + targetVersion, + diffOutcome, + }); + + return { + base_version: baseVersion, + current_version: currentVersion, + target_version: targetVersion, + merged_version: mergedVersion, + + diff_outcome: diffOutcome, + merge_outcome: mergeOutcome, + has_update: valueCanUpdate, + has_conflict: mergeOutcome === ThreeWayMergeOutcome.Conflict, + }; +}; + +interface MergeResult { + mergeOutcome: ThreeWayMergeOutcome; + mergedVersion: TValue[]; +} + +interface MergeArgs { + baseVersion: TValue[] | MissingVersion; + currentVersion: TValue[]; + targetVersion: TValue[]; + diffOutcome: ThreeWayDiffOutcome; +} + +const mergeVersions = ({ + baseVersion, + currentVersion, + targetVersion, + diffOutcome, +}: MergeArgs): MergeResult => { + const dedupedBaseVersion = baseVersion !== MissingVersion ? uniq(baseVersion) : MissingVersion; + const dedupedCurrentVersion = uniq(currentVersion); + const dedupedTargetVersion = uniq(targetVersion); + + switch (diffOutcome) { + case ThreeWayDiffOutcome.StockValueNoUpdate: + case ThreeWayDiffOutcome.CustomizedValueNoUpdate: + case ThreeWayDiffOutcome.CustomizedValueSameUpdate: { + return { + mergeOutcome: ThreeWayMergeOutcome.Current, + mergedVersion: dedupedCurrentVersion, + }; + } + case ThreeWayDiffOutcome.StockValueCanUpdate: { + return { + mergeOutcome: ThreeWayMergeOutcome.Target, + mergedVersion: dedupedTargetVersion, + }; + } + case ThreeWayDiffOutcome.CustomizedValueCanUpdate: { + if (dedupedBaseVersion === MissingVersion) { + return { + mergeOutcome: ThreeWayMergeOutcome.Merged, + mergedVersion: union(currentVersion, targetVersion), + }; + } + + const addedCurrent = difference(dedupedCurrentVersion, dedupedBaseVersion); + const removedCurrent = difference(dedupedBaseVersion, dedupedCurrentVersion); + + const addedTarget = difference(dedupedTargetVersion, dedupedBaseVersion); + const removedTarget = difference(dedupedBaseVersion, dedupedTargetVersion); + + const bothAdded = union(addedCurrent, addedTarget); + const bothRemoved = union(removedCurrent, removedTarget); + + const merged = difference(union(dedupedBaseVersion, bothAdded), bothRemoved); + + return { + mergeOutcome: ThreeWayMergeOutcome.Merged, + mergedVersion: merged, + }; + } + default: + return assertUnreachable(diffOutcome); + } +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/single_line_string_diff_algorithm.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/single_line_string_diff_algorithm.test.ts index a4f5197979db4..427b592985e07 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/single_line_string_diff_algorithm.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/single_line_string_diff_algorithm.test.ts @@ -14,7 +14,7 @@ import { import { singleLineStringDiffAlgorithm } from './single_line_string_diff_algorithm'; describe('singleLineStringDiffAlgorithm', () => { - it('returns current_version as merged output if there is no update', () => { + it('returns current_version as merged output if there is no update - scenario AAA', () => { const mockVersions: ThreeVersionsOf = { base_version: 'A', current_version: 'A', @@ -33,7 +33,7 @@ describe('singleLineStringDiffAlgorithm', () => { ); }); - it('returns current_version as merged output if current_version is different and there is no update', () => { + it('returns current_version as merged output if current_version is different and there is no update - scenario ABA', () => { const mockVersions: ThreeVersionsOf = { base_version: 'A', current_version: 'B', @@ -52,7 +52,7 @@ describe('singleLineStringDiffAlgorithm', () => { ); }); - it('returns target_version as merged output if current_version is the same and there is an update', () => { + it('returns target_version as merged output if current_version is the same and there is an update - scenario AAB', () => { const mockVersions: ThreeVersionsOf = { base_version: 'A', current_version: 'A', @@ -71,7 +71,7 @@ describe('singleLineStringDiffAlgorithm', () => { ); }); - it('returns current_version as merged output if current version is different but it matches the update', () => { + it('returns current_version as merged output if current version is different but it matches the update - scenario ABB', () => { const mockVersions: ThreeVersionsOf = { base_version: 'A', current_version: 'B', @@ -90,7 +90,7 @@ describe('singleLineStringDiffAlgorithm', () => { ); }); - it('returns current_version as merged output if all three versions are different', () => { + it('returns current_version as merged output if all three versions are different - scenario ABC', () => { const mockVersions: ThreeVersionsOf = { base_version: 'A', current_version: 'B', @@ -110,7 +110,7 @@ describe('singleLineStringDiffAlgorithm', () => { }); describe('if base_version is missing', () => { - it('returns current_version as merged output if current_version and target_version are the same', () => { + it('returns current_version as merged output if current_version and target_version are the same - scenario -AA', () => { const mockVersions: ThreeVersionsOf = { base_version: MissingVersion, current_version: 'A', @@ -129,7 +129,7 @@ describe('singleLineStringDiffAlgorithm', () => { ); }); - it('returns target_version as merged output if current_version and target_version are different', () => { + it('returns target_version as merged output if current_version and target_version are different - scenario -AB', () => { const mockVersions: ThreeVersionsOf = { base_version: MissingVersion, current_version: 'A',