Skip to content

Commit 9f38613

Browse files
committed
feat: use history hash for history
1 parent 132d73f commit 9f38613

43 files changed

Lines changed: 317 additions & 123 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

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
@@ -43,7 +43,12 @@ export interface TestResult {
4343
environment?: string;
4444

4545
fullName?: string;
46+
/** @deprecated Use `historyHash`. Legacy key without environment in the hash. */
4647
historyId?: string;
48+
/**
49+
* Stable key for history storage and lookup: md5(`${testCaseId}:${parametersHash}:${environmentId ?? "default"}`).
50+
*/
51+
historyHash?: string;
4752

4853
description?: string;
4954
descriptionHtml?: string;

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

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

47-
export const getHistoryIdCandidates = (tr: Pick<TestResult, "historyId" | "labels" | "parameters">): string[] => {
47+
/**
48+
* Same formula as retry grouping: md5(`${testCaseId}:${parametersHash}:${environmentId ?? "default"}`).
49+
*/
50+
export const calculateHistoryHash = (
51+
testCaseId: string | undefined,
52+
parametersHash: string,
53+
environmentId: string | undefined,
54+
): string | undefined => {
55+
if (!testCaseId) {
56+
return undefined;
57+
}
58+
59+
return md5(`${testCaseId}:${parametersHash}:${environmentId ?? "default"}`);
60+
};
61+
62+
export const getHistoryHashCandidates = (
63+
tr: Pick<TestResult, "historyHash" | "historyId" | "labels" | "parameters">,
64+
): string[] => {
4865
const result: string[] = [];
4966

50-
if (tr.historyId) {
67+
if (tr.historyHash) {
68+
result.push(tr.historyHash);
69+
}
70+
71+
if (tr.historyId && !result.includes(tr.historyId)) {
5172
result.push(tr.historyId);
5273
}
5374

@@ -60,18 +81,37 @@ export const getHistoryIdCandidates = (tr: Pick<TestResult, "historyId" | "label
6081
return result;
6182
};
6283

84+
/**
85+
* @deprecated Use {@link getHistoryHashCandidates}.
86+
*/
87+
export const getHistoryIdCandidates = getHistoryHashCandidates;
88+
89+
const getHistoryTestResultKeyCandidates = (htr: HistoryTestResult): string[] => {
90+
const result: string[] = [];
91+
92+
if (htr.historyHash) {
93+
result.push(htr.historyHash);
94+
}
95+
96+
if (htr.historyId && !result.includes(htr.historyId)) {
97+
result.push(htr.historyId);
98+
}
99+
100+
return result;
101+
};
102+
63103
export const filterUnknownByKnownIssues = (
64104
trs: TestResult[],
65105
knownIssueHistoryIds: ReadonlySet<string>,
66106
): TestResult[] => {
67107
return trs.filter((tr) => {
68-
const historyIdCandidates = getHistoryIdCandidates(tr);
108+
const historyKeyCandidates = getHistoryHashCandidates(tr);
69109

70-
if (historyIdCandidates.length === 0) {
110+
if (historyKeyCandidates.length === 0) {
71111
return true;
72112
}
73113

74-
return historyIdCandidates.every((historyId) => !knownIssueHistoryIds.has(historyId));
114+
return historyKeyCandidates.every((historyKey) => !knownIssueHistoryIds.has(historyKey));
75115
});
76116
};
77117

@@ -120,15 +160,19 @@ export const normalizeHistoryDataPointUrls = (historyDataPoint: HistoryDataPoint
120160

121161
export const selectHistoryTestResults = (
122162
historyDataPoints: HistoryDataPoint[],
123-
historyIdCandidates: readonly string[],
163+
historyKeyCandidates: readonly string[],
124164
): HistoryTestResult[] => {
125-
if (historyIdCandidates.length === 0) {
165+
if (historyKeyCandidates.length === 0) {
126166
return [];
127167
}
128168

129169
return historyDataPoints.reduce((acc, historyDataPoint) => {
130-
for (const historyId of historyIdCandidates) {
131-
const historyTestResult = historyDataPoint.testResults?.[historyId];
170+
if (!historyDataPoint.testResults) {
171+
return acc;
172+
}
173+
174+
for (const historyKey of historyKeyCandidates) {
175+
const historyTestResult = historyDataPoint.testResults[historyKey];
132176

133177
if (!historyTestResult) {
134178
continue;
@@ -149,9 +193,8 @@ export const selectHistoryTestResults = (
149193
* @returns The history test results array.
150194
*/
151195
export const htrsByTr = (hdps: HistoryDataPoint[], tr: TestResult | HistoryTestResult): HistoryTestResult[] => {
152-
if (!tr?.historyId) {
153-
return [];
154-
}
196+
const candidates =
197+
"parameters" in tr ? getHistoryHashCandidates(tr) : getHistoryTestResultKeyCandidates(tr as HistoryTestResult);
155198

156-
return selectHistoryTestResults(hdps, [tr.historyId]);
199+
return selectHistoryTestResults(hdps, candidates);
157200
};

packages/core/src/history.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,15 @@ 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,
2727
historyId,
28+
historyHash,
2829
status,
2930
error: { message, trace } = {},
3031
start,
@@ -45,14 +46,21 @@ const createHistoryItems = (testResults: TestResult[], remoteUrl: string) => {
4546
duration,
4647
labels,
4748
url: remoteUrl,
48-
historyId: historyId!,
49+
historyId,
50+
historyHash,
4951
reportLinks: [],
5052
} as HistoryTestResult;
5153
},
5254
)
5355
.reduce(
5456
(acc, item) => {
55-
acc[item.historyId!] = item;
57+
const historyKey = item.historyHash ?? item.historyId;
58+
59+
if (!historyKey) {
60+
return acc;
61+
}
62+
63+
acc[historyKey] = item;
5664

5765
return acc;
5866
},

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: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -594,11 +594,11 @@ export class AllureReport {
594594
globalAttachmentIds = [],
595595
globalErrors = [],
596596
indexAttachmentByTestResult = {},
597-
indexTestResultByHistoryId = {},
597+
indexTestResultByHistoryHash = {},
598598
indexTestResultByTestCase = {},
599599
indexAttachmentByFixture = {},
600600
indexFixturesByTestResult = {},
601-
indexKnownByHistoryId = {},
601+
indexKnownByHistoryHash = {},
602602
qualityGateResults = [],
603603
testResultIdsIngestOrder = [],
604604
}: AllureStoreDump): [AllureStoreDumpFiles, unknown][] => [
@@ -612,11 +612,11 @@ export class AllureReport {
612612
[AllureStoreDumpFiles.GlobalAttachments, globalAttachmentIds],
613613
[AllureStoreDumpFiles.GlobalErrors, globalErrors],
614614
[AllureStoreDumpFiles.IndexAttachmentsByTestResults, indexAttachmentByTestResult],
615-
[AllureStoreDumpFiles.IndexTestResultsByHistoryId, indexTestResultByHistoryId],
615+
[AllureStoreDumpFiles.IndexTestResultsByHistoryId, indexTestResultByHistoryHash],
616616
[AllureStoreDumpFiles.IndexTestResultsByTestCase, indexTestResultByTestCase],
617617
[AllureStoreDumpFiles.IndexAttachmentsByFixture, indexAttachmentByFixture],
618618
[AllureStoreDumpFiles.IndexFixturesByTestResult, indexFixturesByTestResult],
619-
[AllureStoreDumpFiles.IndexKnownByHistoryId, indexKnownByHistoryId],
619+
[AllureStoreDumpFiles.IndexKnownByHistoryId, indexKnownByHistoryHash],
620620
[AllureStoreDumpFiles.QualityGateResults, qualityGateResults],
621621
[AllureStoreDumpFiles.TestResultIngestOrder, testResultIdsIngestOrder],
622622
];
@@ -812,6 +812,8 @@ export class AllureReport {
812812
});
813813
}
814814
}, {});
815+
const indexTestResultByHistoryHash = JSON.parse(indexTestResultsByHistoryId.toString("utf8"));
816+
const indexKnownByHistoryHash = JSON.parse(indexKnownByHistoryIdEntry.toString("utf8"));
815817
const dumpState: AllureStoreDump = {
816818
testResults: JSON.parse(testResultsEntry.toString("utf8")),
817819
testCases: JSON.parse(testCasesEntry.toString("utf8")),
@@ -823,11 +825,13 @@ export class AllureReport {
823825
globalAttachmentIds: JSON.parse(globalAttachmentsEntry.toString("utf8")),
824826
globalErrors: JSON.parse(globalErrorsEntry.toString("utf8")),
825827
indexAttachmentByTestResult: JSON.parse(indexAttachmentsEntry.toString("utf8")),
826-
indexTestResultByHistoryId: JSON.parse(indexTestResultsByHistoryId.toString("utf8")),
828+
indexTestResultByHistoryHash,
829+
indexTestResultByHistoryId: indexTestResultByHistoryHash,
827830
indexTestResultByTestCase: JSON.parse(indexTestResultsByTestCaseEntry.toString("utf8")),
828831
indexAttachmentByFixture: JSON.parse(indexAttachmentsByFixtureEntry.toString("utf8")),
829832
indexFixturesByTestResult: JSON.parse(indexFixturesByTestResultEntry.toString("utf8")),
830-
indexKnownByHistoryId: JSON.parse(indexKnownByHistoryIdEntry.toString("utf8")),
833+
indexKnownByHistoryHash,
834+
indexKnownByHistoryId: indexKnownByHistoryHash,
831835
qualityGateResults: JSON.parse(qualityGateResultsEntry.toString("utf8")),
832836
testResultIdsIngestOrder: testResultIngestOrderEntry
833837
? JSON.parse(testResultIngestOrderEntry.toString("utf8"))

0 commit comments

Comments
 (0)