Skip to content

Commit 6b612b0

Browse files
committed
feat: introduce history hash in place of history id
1 parent 5cfb9bd commit 6b612b0

38 files changed

Lines changed: 283 additions & 128 deletions

File tree

packages/core-api/src/categories.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,9 @@ export type CategoryNodeItem = {
141141
name: string;
142142
key?: string;
143143
value?: string;
144+
/** @deprecated Prefer `historyHash` for copy/group identity. */
144145
historyId?: string;
146+
historyHash?: string;
145147
retriesCount?: number;
146148
transition?: TestStatusTransition;
147149
tooltips?: Record<string, string>;

packages/core-api/src/history.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@ export interface HistoryTestResult {
2222

2323
url: string;
2424

25-
historyId?: string; // TODO: double check the necessity to have historyId in the history test result
25+
/** @deprecated Legacy map key / field without environment in the hash. Prefer `historyHash`. */
26+
historyId?: string;
27+
/** Primary key in history `testResults` maps for new history entries. */
28+
historyHash?: string;
2629
reportLinks?: any[]; // TODO: add the correct type for previously missing report links
2730
}
2831

packages/core-api/src/known.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ import type { TestLink } from "./metadata.js";
22
import type { TestError } from "./model.js";
33

44
export interface KnownTestFailure {
5-
historyId: string;
5+
/** @deprecated Prefer `historyHash`. */
6+
historyId?: string;
7+
historyHash?: string;
68
issues?: TestLink[];
79
comment?: string;
810
error?: TestError;

packages/core-api/src/model.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,12 @@ export interface TestResult {
3131
environment?: string;
3232

3333
fullName?: string;
34+
/** @deprecated Use `historyHash`. Legacy key without environment in the hash. */
3435
historyId?: string;
36+
/**
37+
* Stable key for history storage and lookup: md5(`${testCaseId}:${parametersHash}:${environmentId ?? "default"}`).
38+
*/
39+
historyHash?: string;
3540

3641
description?: string;
3742
descriptionHtml?: string;

packages/core-api/src/utils/history.ts

Lines changed: 52 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,31 @@ export const getFallbackHistoryId = (tr: Pick<TestResult, "labels" | "parameters
3030
return `${fallbackTestCaseId}.${md5(stringifyHistoryParams(tr.parameters ?? []))}`;
3131
};
3232

33-
export const getHistoryIdCandidates = (tr: Pick<TestResult, "historyId" | "labels" | "parameters">): string[] => {
33+
/**
34+
* Same formula as retry grouping: md5(`${testCaseId}:${parametersHash}:${environmentId ?? "default"}`).
35+
*/
36+
export const calculateHistoryHash = (
37+
testCaseId: string | undefined,
38+
parametersHash: string,
39+
environmentId: string | undefined,
40+
): string | undefined => {
41+
if (!testCaseId) {
42+
return undefined;
43+
}
44+
45+
return md5(`${testCaseId}:${parametersHash}:${environmentId ?? "default"}`);
46+
};
47+
48+
export const getHistoryHashCandidates = (
49+
tr: Pick<TestResult, "historyHash" | "historyId" | "labels" | "parameters">,
50+
): string[] => {
3451
const result: string[] = [];
3552

36-
if (tr.historyId) {
53+
if (tr.historyHash) {
54+
result.push(tr.historyHash);
55+
}
56+
57+
if (tr.historyId && !result.includes(tr.historyId)) {
3758
result.push(tr.historyId);
3859
}
3960

@@ -46,18 +67,37 @@ export const getHistoryIdCandidates = (tr: Pick<TestResult, "historyId" | "label
4667
return result;
4768
};
4869

70+
/**
71+
* @deprecated Use {@link getHistoryHashCandidates}.
72+
*/
73+
export const getHistoryIdCandidates = getHistoryHashCandidates;
74+
75+
const getHistoryTestResultKeyCandidates = (htr: HistoryTestResult): string[] => {
76+
const result: string[] = [];
77+
78+
if (htr.historyHash) {
79+
result.push(htr.historyHash);
80+
}
81+
82+
if (htr.historyId && !result.includes(htr.historyId)) {
83+
result.push(htr.historyId);
84+
}
85+
86+
return result;
87+
};
88+
4989
export const filterUnknownByKnownIssues = (
5090
trs: TestResult[],
5191
knownIssueHistoryIds: ReadonlySet<string>,
5292
): TestResult[] => {
5393
return trs.filter((tr) => {
54-
const historyIdCandidates = getHistoryIdCandidates(tr);
94+
const historyKeyCandidates = getHistoryHashCandidates(tr);
5595

56-
if (historyIdCandidates.length === 0) {
96+
if (historyKeyCandidates.length === 0) {
5797
return true;
5898
}
5999

60-
return historyIdCandidates.every((historyId) => !knownIssueHistoryIds.has(historyId));
100+
return historyKeyCandidates.every((historyKey) => !knownIssueHistoryIds.has(historyKey));
61101
});
62102
};
63103

@@ -97,15 +137,15 @@ export const normalizeHistoryDataPointUrls = (historyDataPoint: HistoryDataPoint
97137

98138
export const selectHistoryTestResults = (
99139
historyDataPoints: HistoryDataPoint[],
100-
historyIdCandidates: readonly string[],
140+
historyKeyCandidates: readonly string[],
101141
): HistoryTestResult[] => {
102-
if (historyIdCandidates.length === 0) {
142+
if (historyKeyCandidates.length === 0) {
103143
return [];
104144
}
105145

106146
return historyDataPoints.reduce((acc, historyDataPoint) => {
107-
for (const historyId of historyIdCandidates) {
108-
const historyTestResult = historyDataPoint.testResults[historyId];
147+
for (const historyKey of historyKeyCandidates) {
148+
const historyTestResult = historyDataPoint.testResults[historyKey];
109149

110150
if (!historyTestResult) {
111151
continue;
@@ -126,9 +166,8 @@ export const selectHistoryTestResults = (
126166
* @returns The history test results array.
127167
*/
128168
export const htrsByTr = (hdps: HistoryDataPoint[], tr: TestResult | HistoryTestResult): HistoryTestResult[] => {
129-
if (!tr?.historyId) {
130-
return [];
131-
}
169+
const candidates =
170+
"parameters" in tr ? getHistoryHashCandidates(tr) : getHistoryTestResultKeyCandidates(tr as HistoryTestResult);
132171

133-
return selectHistoryTestResults(hdps, [tr.historyId]);
172+
return selectHistoryTestResults(hdps, candidates);
134173
};

packages/core/src/history.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,14 @@ import { isFileNotFoundError } from "./utils/misc.js";
1717

1818
const createHistoryItems = (testResults: TestResult[], remoteUrl: string) => {
1919
return testResults
20-
.filter((tr) => tr.historyId)
20+
.filter((tr) => tr.historyHash || tr.historyId)
2121
.map(
2222
({
2323
id,
2424
name,
2525
fullName,
2626
environment,
27-
historyId,
27+
historyHash,
2828
status,
2929
error: { message, trace } = {},
3030
start,
@@ -45,14 +45,20 @@ const createHistoryItems = (testResults: TestResult[], remoteUrl: string) => {
4545
duration,
4646
labels,
4747
url: remoteUrl,
48-
historyId: historyId!,
48+
historyHash,
4949
reportLinks: [],
5050
} as HistoryTestResult;
5151
},
5252
)
5353
.reduce(
5454
(acc, item) => {
55-
acc[item.historyId!] = item;
55+
const historyKey = item.historyHash;
56+
57+
if (!historyKey) {
58+
return acc;
59+
}
60+
61+
acc[historyKey] = item;
5662

5763
return acc;
5864
},

packages/core/src/known.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@ export const writeKnownIssues = async (store: AllureStore, knownIssuesPath?: str
3232
const testResults = await store.allTestResults();
3333
const knownIssues: KnownTestFailure[] = testResults
3434
.filter((tr) => failedStatuses.has(tr.status))
35-
.filter((tr) => tr.historyId)
36-
.map(({ historyId, links }) => ({
37-
historyId: historyId!,
35+
.filter((tr) => tr.historyHash)
36+
.map(({ historyHash, links }) => ({
37+
historyHash,
3838
issues: links.filter((l) => l.type === "issue"),
3939
comment: "automatically generated from failure by allure known-issue command",
4040
}));

packages/core/src/qualityGate/rules.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ export const maxFailuresRule: QualityGateRule<number> = {
77
message: ({ actual, expected }) =>
88
`The number of failed tests ${bold(String(actual))} exceeds the allowed threshold value ${bold(String(expected))}`,
99
validate: async ({ trs, knownIssues, expected, state }) => {
10-
const knownIssuesHistoryIds = new Set(knownIssues.map(({ historyId }) => historyId));
10+
const knownIssuesHistoryIds = new Set(
11+
knownIssues.map(({ historyHash, historyId }) => historyHash ?? historyId).filter((k): k is string => Boolean(k)),
12+
);
1113
const unknown = filterUnknownByKnownIssues(trs, knownIssuesHistoryIds);
1214
const failedTrs = unknown.filter(filterUnsuccessful);
1315
const actual = failedTrs.length + (state.getResult() ?? 0);
@@ -42,7 +44,9 @@ export const successRateRule: QualityGateRule<number> = {
4244
message: ({ actual, expected }) =>
4345
`Success rate ${bold(String(actual))} is less, than expected ${bold(String(expected))}`,
4446
validate: async ({ trs, knownIssues, expected }) => {
45-
const knownIssuesHistoryIds = new Set(knownIssues.map(({ historyId }) => historyId));
47+
const knownIssuesHistoryIds = new Set(
48+
knownIssues.map(({ historyHash, historyId }) => historyHash ?? historyId).filter((k): k is string => Boolean(k)),
49+
);
4650
const unknown = filterUnknownByKnownIssues(trs, knownIssuesHistoryIds);
4751
const passedTrs = unknown.filter(filterSuccessful);
4852
const rate = passedTrs.length === 0 ? 0 : passedTrs.length / unknown.length;

packages/core/src/report.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -335,12 +335,12 @@ export class AllureReport {
335335
globalAttachmentIds = [],
336336
globalErrors = [],
337337
indexAttachmentByTestResult = {},
338-
indexTestResultByHistoryId = {},
338+
indexTestResultByHistoryHash = {},
339339
indexTestResultByTestCase = {},
340340
indexLatestEnvTestResultByHistoryId = {},
341341
indexAttachmentByFixture = {},
342342
indexFixturesByTestResult = {},
343-
indexKnownByHistoryId = {},
343+
indexKnownByHistoryHash = {},
344344
qualityGateResults = [],
345345
} = this.#store.dumpState();
346346
const allAttachments = await this.#store.allAttachments();
@@ -384,7 +384,7 @@ export class AllureReport {
384384
await addEntry(Buffer.from(JSON.stringify(indexAttachmentByTestResult)), {
385385
name: AllureStoreDumpFiles.IndexAttachmentsByTestResults,
386386
});
387-
await addEntry(Buffer.from(JSON.stringify(indexTestResultByHistoryId)), {
387+
await addEntry(Buffer.from(JSON.stringify(indexTestResultByHistoryHash)), {
388388
name: AllureStoreDumpFiles.IndexTestResultsByHistoryId,
389389
});
390390
await addEntry(Buffer.from(JSON.stringify(indexTestResultByTestCase)), {
@@ -399,7 +399,7 @@ export class AllureReport {
399399
await addEntry(Buffer.from(JSON.stringify(indexFixturesByTestResult)), {
400400
name: AllureStoreDumpFiles.IndexFixturesByTestResult,
401401
});
402-
await addEntry(Buffer.from(JSON.stringify(indexKnownByHistoryId)), {
402+
await addEntry(Buffer.from(JSON.stringify(indexKnownByHistoryHash)), {
403403
name: AllureStoreDumpFiles.IndexKnownByHistoryId,
404404
});
405405
await addEntry(Buffer.from(JSON.stringify(qualityGateResults)), {
@@ -498,12 +498,12 @@ export class AllureReport {
498498
globalAttachmentIds: JSON.parse(globalAttachmentsEntry.toString("utf8")),
499499
globalErrors: JSON.parse(globalErrorsEntry.toString("utf8")),
500500
indexAttachmentByTestResult: JSON.parse(indexAttachmentsEntry.toString("utf8")),
501-
indexTestResultByHistoryId: JSON.parse(indexTestResultsByHistoryId.toString("utf8")),
501+
indexTestResultByHistoryHash: JSON.parse(indexTestResultsByHistoryId.toString("utf8")),
502502
indexTestResultByTestCase: JSON.parse(indexTestResultsByTestCaseEntry.toString("utf8")),
503503
indexLatestEnvTestResultByHistoryId: JSON.parse(indexLatestEnvTestResultsByHistoryIdEntry.toString("utf8")),
504504
indexAttachmentByFixture: JSON.parse(indexAttachmentsByFixtureEntry.toString("utf8")),
505505
indexFixturesByTestResult: JSON.parse(indexFixturesByTestResultEntry.toString("utf8")),
506-
indexKnownByHistoryId: JSON.parse(indexKnownByHistoryIdEntry.toString("utf8")),
506+
indexKnownByHistoryHash: JSON.parse(indexKnownByHistoryIdEntry.toString("utf8")),
507507
qualityGateResults: JSON.parse(qualityGateResultsEntry.toString("utf8")),
508508
};
509509
const dumpTempDir = await mkdtemp(join(tmpdir(), basename(dump, ".zip")));

0 commit comments

Comments
 (0)