Skip to content

Commit 3fcbbc6

Browse files
authored
fix(aggregate): also make ids unique (#1673)
Make testIds and mutantIds unique accross all modules in `aggregateResultsByModule` Fixes #1672
1 parent c17b945 commit 3fcbbc6

File tree

2 files changed

+113
-13
lines changed

2 files changed

+113
-13
lines changed

packages/metrics/src/aggregate.ts

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,40 @@ export function aggregateResultsByModule(resultsByModule: Record<string, Mutatio
2121

2222
return Object.entries(resultsByModule).reduce((acc, [moduleName, report]) => {
2323
Object.entries(normalizeFileNames(report.files)).forEach(([fileName, fileResult]) => {
24-
aggregatedResult.files[`${moduleName}/${fileName}`] = fileResult;
24+
aggregatedResult.files[`${moduleName}/${fileName}`] = {
25+
...fileResult,
26+
mutants: [
27+
...fileResult.mutants.map(({ id, coveredBy, killedBy, ...mutantData }) => ({
28+
...mutantData,
29+
id: toUniqueId(moduleName, id),
30+
killedBy: toUniqueIds(moduleName, killedBy),
31+
coveredBy: toUniqueIds(moduleName, coveredBy),
32+
})),
33+
],
34+
};
2535
});
2636
if (report.testFiles) {
2737
const aggregatedTestFiles: TestFileDefinitionDictionary = aggregatedResult.testFiles ?? (aggregatedResult.testFiles = Object.create(null));
2838
Object.entries(normalizeFileNames(report.testFiles)).forEach(([fileName, testFileResult]) => {
29-
aggregatedTestFiles[`${moduleName}/${fileName}`] = testFileResult;
39+
aggregatedTestFiles[`${moduleName}/${fileName}`] = {
40+
...testFileResult,
41+
tests: testFileResult.tests.map(({ id, ...testData }) => ({ ...testData, id: toUniqueId(moduleName, id) })),
42+
};
3043
});
3144
}
3245

3346
return acc;
3447
}, aggregatedResult);
3548
}
49+
50+
function toUniqueIds(moduleName: string, localIds: string[] | undefined): string[] | undefined {
51+
if (localIds) {
52+
const toUniqueIdForModule = toUniqueId.bind(undefined, moduleName);
53+
return localIds.map(toUniqueIdForModule);
54+
}
55+
return;
56+
}
57+
58+
function toUniqueId(moduleName: string, localId: string) {
59+
return `${moduleName}_${localId}`;
60+
}

packages/metrics/test/unit/aggregate.spec.ts

Lines changed: 86 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { expect } from 'chai';
22
import { MutationTestResult } from 'mutation-testing-report-schema/api';
33
import { aggregateResultsByModule } from '../../src';
4-
import { createFileResult, createMutationTestResult, createTestFile } from '../helpers/factories';
4+
import { createFileResult, createMutantResult, createMutationTestResult, createTestDefinition, createTestFile } from '../helpers/factories';
55

66
describe(aggregateResultsByModule.name, () => {
77
it('should result in an empty report when an empty object is provided', () => {
@@ -38,15 +38,15 @@ describe(aggregateResultsByModule.name, () => {
3838

3939
describe('files', () => {
4040
it('should prefix files with the module name', () => {
41-
const file = createFileResult();
41+
const file = createFileResult({ mutants: [] });
4242
const module = createMutationTestResult({ files: { a: file } });
4343
const result = aggregateResultsByModule({ module });
4444
expect(result.files).deep.eq({ 'module/a': file });
4545
});
4646
it('should merge file results together', () => {
47-
const fileA = createFileResult();
48-
const fileB = createFileResult();
49-
const fileC = createFileResult();
47+
const fileA = createFileResult({ source: 'core/a', mutants: [] });
48+
const fileB = createFileResult({ source: 'core/b', mutants: [] });
49+
const fileC = createFileResult({ source: 'api/c', mutants: [] });
5050
const core = createMutationTestResult({ files: { a: fileA, b: fileB } });
5151
const api = createMutationTestResult({ files: { a: fileC } });
5252
const result = aggregateResultsByModule({ core, api });
@@ -56,27 +56,102 @@ describe(aggregateResultsByModule.name, () => {
5656

5757
describe('testFiles', () => {
5858
it('should prefix test files with the module name', () => {
59-
const file = createTestFile();
59+
const file = createTestFile({ tests: [] });
6060
const module = createMutationTestResult({ testFiles: { a: file } });
6161
const result = aggregateResultsByModule({ module });
6262
expect(result.testFiles).deep.eq({ 'module/a': file });
6363
});
6464
it('should merge test file results together', () => {
65-
const fileA = createTestFile();
66-
const fileB = createTestFile();
67-
const fileC = createTestFile();
65+
const fileA = createTestFile({ source: 'fileA', tests: [] });
66+
const fileB = createTestFile({ source: 'fileB', tests: [] });
67+
const fileC = createTestFile({ source: 'fileC', tests: [] });
6868
const core = createMutationTestResult({ testFiles: { a: fileA, b: fileB } });
6969
const api = createMutationTestResult({ testFiles: { a: fileC } });
7070
const result = aggregateResultsByModule({ core, api });
7171
expect(result.testFiles).deep.eq({ 'core/a': fileA, 'core/b': fileB, 'api/a': fileC });
7272
});
7373
it('should allow undefined test files', () => {
74-
const fileA = createTestFile();
75-
const fileB = createTestFile();
74+
const fileA = createTestFile({ source: 'fileA', tests: [] });
75+
const fileB = createTestFile({ source: 'fileB', tests: [] });
7676
const core = createMutationTestResult({ testFiles: { a: fileA, b: fileB } });
7777
const api = createMutationTestResult({ testFiles: undefined });
7878
const result = aggregateResultsByModule({ core, api });
7979
expect(result.testFiles).deep.eq({ 'core/a': fileA, 'core/b': fileB });
8080
});
8181
});
82+
83+
describe('mutants', () => {
84+
it('should prefix mutant ids with moduleName', () => {
85+
// Arrange
86+
const fileA = createFileResult({ mutants: [createMutantResult({ id: '1' })] });
87+
const fileB = createFileResult({ mutants: [createMutantResult({ id: '1' })] });
88+
const core = createMutationTestResult({ files: { a: fileA } });
89+
const api = createMutationTestResult({ files: { b: fileB } });
90+
91+
// Act
92+
const result = aggregateResultsByModule({ core, api });
93+
94+
// Assert
95+
const mutantsCore = result.files['core/a'].mutants;
96+
const mutantsApi = result.files['api/b'].mutants;
97+
expect(mutantsCore).lengthOf(1);
98+
expect(mutantsApi).lengthOf(1);
99+
expect(mutantsCore[0].id).eq('core_1');
100+
expect(mutantsApi[0].id).eq('api_1');
101+
});
102+
103+
it('should prefix coveredBy with moduleName', () => {
104+
// Arrange
105+
const fileA = createFileResult({ mutants: [createMutantResult({ coveredBy: ['1'] })] });
106+
const fileB = createFileResult({ mutants: [createMutantResult({ coveredBy: ['1', '2'] })] });
107+
const core = createMutationTestResult({ files: { a: fileA } });
108+
const api = createMutationTestResult({ files: { b: fileB } });
109+
110+
// Act
111+
const result = aggregateResultsByModule({ core, api });
112+
113+
// Assert
114+
const [mutantCore] = result.files['core/a'].mutants;
115+
const [mutantApi] = result.files['api/b'].mutants;
116+
expect(mutantCore.coveredBy).deep.eq(['core_1']);
117+
expect(mutantApi.coveredBy).deep.eq(['api_1', 'api_2']);
118+
});
119+
120+
it('should prefix killedBy with moduleName', () => {
121+
// Arrange
122+
const fileA = createFileResult({ mutants: [createMutantResult({ killedBy: ['1'] })] });
123+
const fileB = createFileResult({ mutants: [createMutantResult({ killedBy: ['1', '2'] })] });
124+
const core = createMutationTestResult({ files: { a: fileA } });
125+
const api = createMutationTestResult({ files: { b: fileB } });
126+
127+
// Act
128+
const result = aggregateResultsByModule({ core, api });
129+
130+
// Assert
131+
const [mutantCore] = result.files['core/a'].mutants;
132+
const [mutantApi] = result.files['api/b'].mutants;
133+
expect(mutantCore.killedBy).deep.eq(['core_1']);
134+
expect(mutantApi.killedBy).deep.eq(['api_1', 'api_2']);
135+
});
136+
});
137+
describe('tests', () => {
138+
it('should prefix test ids with moduleName', () => {
139+
// Arrange
140+
const fileA = createTestFile({ tests: [createTestDefinition({ id: '1' })] });
141+
const fileB = createTestFile({ tests: [createTestDefinition({ id: '1' })] });
142+
const core = createMutationTestResult({ testFiles: { a: fileA } });
143+
const api = createMutationTestResult({ testFiles: { b: fileB } });
144+
145+
// Act
146+
const result = aggregateResultsByModule({ core, api });
147+
148+
// Assert
149+
const testsCore = result.testFiles!['core/a'].tests;
150+
const testsApi = result.testFiles!['api/b'].tests;
151+
expect(testsCore).lengthOf(1);
152+
expect(testsApi).lengthOf(1);
153+
expect(testsCore[0].id).eq('core_1');
154+
expect(testsApi[0].id).eq('api_1');
155+
});
156+
});
82157
});

0 commit comments

Comments
 (0)