@elench/testkit 0.1.79 → 0.1.81
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +50 -35
- package/lib/cli/args.mjs +2 -14
- package/lib/cli/args.test.mjs +1 -17
- package/lib/cli/command-helpers.mjs +1 -20
- package/lib/cli/entrypoint.mjs +0 -4
- package/lib/cli/presentation/colors.mjs +1 -1
- package/lib/cli/presentation/failure-presentation.mjs +31 -0
- package/lib/cli/presentation/run-reporter.mjs +63 -93
- package/lib/cli/presentation/run-reporter.test.mjs +137 -26
- package/lib/cli/presentation/summary-box.mjs +45 -0
- package/lib/cli/presentation/summary-box.test.mjs +43 -0
- package/lib/cli/presentation/terminal-layout.mjs +43 -0
- package/lib/cli/presentation/terminal-layout.test.mjs +23 -0
- package/lib/cli/viewer.mjs +18 -19
- package/lib/config/index.mjs +6 -6
- package/lib/config/runtime.mjs +8 -8
- package/lib/config-api/index.d.ts +4 -4
- package/lib/package.test.mjs +4 -4
- package/lib/{known-failures → regressions}/github.mjs +39 -77
- package/lib/regressions/github.test.mjs +324 -0
- package/lib/regressions/index.d.ts +189 -0
- package/lib/{known-failures → regressions}/index.mjs +90 -93
- package/lib/{known-failures → regressions}/index.test.mjs +37 -48
- package/lib/runner/formatting.mjs +105 -103
- package/lib/runner/formatting.test.mjs +94 -131
- package/lib/runner/metadata.mjs +1 -1
- package/lib/runner/orchestrator.mjs +7 -8
- package/lib/runner/regressions.mjs +304 -0
- package/lib/runner/{triage.test.mjs → regressions.test.mjs} +50 -36
- package/lib/runner/reporting.mjs +2 -2
- package/lib/runner/reporting.test.mjs +2 -2
- package/lib/runner/run-finalization.mjs +18 -30
- package/lib/runner/template-steps.mjs +2 -2
- package/lib/runner/worker-loop.mjs +1 -0
- package/node_modules/@elench/next-analysis/package.json +1 -1
- package/node_modules/@elench/testkit-bridge/package.json +2 -2
- package/node_modules/@elench/testkit-protocol/package.json +1 -1
- package/node_modules/@elench/ts-analysis/package.json +1 -1
- package/package.json +12 -9
- package/lib/cli/commands/known-failures/render.mjs +0 -19
- package/lib/cli/commands/known-failures/validate.mjs +0 -20
- package/lib/cli/known-failures.mjs +0 -164
- package/lib/known-failures/github.test.mjs +0 -512
- package/lib/known-failures/index.d.ts +0 -192
- package/lib/runner/triage.mjs +0 -221
- /package/lib/{known-failures → regressions}/github-cache.mjs +0 -0
- /package/lib/{known-failures → regressions}/github-transport.mjs +0 -0
|
@@ -1,512 +0,0 @@
|
|
|
1
|
-
import fs from "fs";
|
|
2
|
-
import os from "os";
|
|
3
|
-
import path from "path";
|
|
4
|
-
import { afterEach, describe, expect, it } from "vitest";
|
|
5
|
-
import { normalizeKnownFailuresDocument } from "./index.mjs";
|
|
6
|
-
import {
|
|
7
|
-
parseGitHubRepoSlug,
|
|
8
|
-
shouldFailKnownFailureIssueValidation,
|
|
9
|
-
validateKnownFailureIssues,
|
|
10
|
-
} from "./github.mjs";
|
|
11
|
-
|
|
12
|
-
const tempDirs = [];
|
|
13
|
-
|
|
14
|
-
afterEach(() => {
|
|
15
|
-
for (const tempDir of tempDirs.splice(0)) {
|
|
16
|
-
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
17
|
-
}
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
describe("known failures GitHub validation", () => {
|
|
21
|
-
it("parses GitHub repo slugs from common origin URLs", () => {
|
|
22
|
-
expect(parseGitHubRepoSlug("https://github.com/acme/repo.git")).toBe("acme/repo");
|
|
23
|
-
expect(parseGitHubRepoSlug("git@github.com:acme/repo.git")).toBe("acme/repo");
|
|
24
|
-
expect(parseGitHubRepoSlug("ssh://git@github.com/acme/repo.git")).toBe("acme/repo");
|
|
25
|
-
expect(parseGitHubRepoSlug("https://gitlab.com/acme/repo.git")).toBe(null);
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
it("fails validation for title/state drift and closed issues that still reproduce", async () => {
|
|
29
|
-
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "testkit-gh-issues-"));
|
|
30
|
-
tempDirs.push(tempDir);
|
|
31
|
-
const document = normalizeKnownFailuresDocument({
|
|
32
|
-
schemaVersion: 1,
|
|
33
|
-
issueRepo: "acme/repo",
|
|
34
|
-
entries: [
|
|
35
|
-
{
|
|
36
|
-
id: "bad-message",
|
|
37
|
-
title: "Bad message bug",
|
|
38
|
-
classification: "product_bug",
|
|
39
|
-
state: "open",
|
|
40
|
-
issue: {
|
|
41
|
-
repo: "acme/repo",
|
|
42
|
-
number: 12,
|
|
43
|
-
url: "https://github.com/acme/repo/issues/12",
|
|
44
|
-
},
|
|
45
|
-
description: "Wrong message",
|
|
46
|
-
whyFailing: "Payload is wrong",
|
|
47
|
-
lastReviewedAt: "2026-04-27",
|
|
48
|
-
matches: [
|
|
49
|
-
{
|
|
50
|
-
service: "api",
|
|
51
|
-
type: "int",
|
|
52
|
-
path: "src/api/routes/__testkit__/failing.int.testkit.ts",
|
|
53
|
-
},
|
|
54
|
-
],
|
|
55
|
-
},
|
|
56
|
-
],
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
const result = await validateKnownFailureIssues({
|
|
60
|
-
productDir: tempDir,
|
|
61
|
-
document,
|
|
62
|
-
statusArtifact: {
|
|
63
|
-
tests: [
|
|
64
|
-
{
|
|
65
|
-
service: "api",
|
|
66
|
-
type: "int",
|
|
67
|
-
path: "src/api/routes/__testkit__/failing.int.testkit.ts",
|
|
68
|
-
status: "failed",
|
|
69
|
-
},
|
|
70
|
-
],
|
|
71
|
-
},
|
|
72
|
-
config: {
|
|
73
|
-
provider: "github",
|
|
74
|
-
mode: "error",
|
|
75
|
-
cacheTtlSeconds: 60,
|
|
76
|
-
},
|
|
77
|
-
gitMetadata: {
|
|
78
|
-
repoSlug: "acme/repo",
|
|
79
|
-
remoteUrl: "https://github.com/acme/repo.git",
|
|
80
|
-
},
|
|
81
|
-
transport: {
|
|
82
|
-
async fetchRepoIssues(repo, numbers) {
|
|
83
|
-
const map = new Map();
|
|
84
|
-
map.set(numbers[0], {
|
|
85
|
-
repo,
|
|
86
|
-
number: numbers[0],
|
|
87
|
-
exists: true,
|
|
88
|
-
title: "Different title",
|
|
89
|
-
state: "CLOSED",
|
|
90
|
-
url: `https://github.com/${repo}/issues/${numbers[0]}`,
|
|
91
|
-
checkedAt: "2026-04-27T00:00:00.000Z",
|
|
92
|
-
source: "github",
|
|
93
|
-
});
|
|
94
|
-
return map;
|
|
95
|
-
},
|
|
96
|
-
},
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
expect(result.summary.byCode.closed_but_failing).toBe(1);
|
|
100
|
-
expect(result.summary.byCode.title_mismatch).toBe(1);
|
|
101
|
-
expect(result.summary.byCode.state_mismatch).toBe(1);
|
|
102
|
-
expect(result.summary.errors).toBeGreaterThan(0);
|
|
103
|
-
expect(result.entries[0].status).toBe("closed_but_failing");
|
|
104
|
-
expect(shouldFailKnownFailureIssueValidation(result)).toBe(true);
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it("allows multiple local entries to share one issue title while keeping distinct descriptions", async () => {
|
|
108
|
-
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "testkit-gh-shared-title-"));
|
|
109
|
-
tempDirs.push(tempDir);
|
|
110
|
-
const document = normalizeKnownFailuresDocument({
|
|
111
|
-
schemaVersion: 1,
|
|
112
|
-
issueRepo: "acme/repo",
|
|
113
|
-
entries: [
|
|
114
|
-
{
|
|
115
|
-
id: "route-a-invalid-uuid",
|
|
116
|
-
title: "UUID path params reach Postgres and return 500 instead of 400",
|
|
117
|
-
classification: "product_bug",
|
|
118
|
-
state: "open",
|
|
119
|
-
issue: {
|
|
120
|
-
repo: "acme/repo",
|
|
121
|
-
number: 77,
|
|
122
|
-
url: "https://github.com/acme/repo/issues/77",
|
|
123
|
-
},
|
|
124
|
-
description: "Route A leaks Postgres UUID parse errors on malformed ids.",
|
|
125
|
-
whyFailing: "Route A is missing UUID param validation.",
|
|
126
|
-
lastReviewedAt: "2026-04-27",
|
|
127
|
-
matches: [
|
|
128
|
-
{
|
|
129
|
-
service: "api",
|
|
130
|
-
type: "int",
|
|
131
|
-
path: "src/api/routes/__testkit__/route-a.int.testkit.ts",
|
|
132
|
-
},
|
|
133
|
-
],
|
|
134
|
-
},
|
|
135
|
-
{
|
|
136
|
-
id: "route-b-invalid-uuid",
|
|
137
|
-
title: "UUID path params reach Postgres and return 500 instead of 400",
|
|
138
|
-
classification: "product_bug",
|
|
139
|
-
state: "open",
|
|
140
|
-
issue: {
|
|
141
|
-
repo: "acme/repo",
|
|
142
|
-
number: 77,
|
|
143
|
-
url: "https://github.com/acme/repo/issues/77",
|
|
144
|
-
},
|
|
145
|
-
description: "Route B leaks Postgres UUID parse errors on malformed ids.",
|
|
146
|
-
whyFailing: "Route B is missing UUID param validation.",
|
|
147
|
-
lastReviewedAt: "2026-04-27",
|
|
148
|
-
matches: [
|
|
149
|
-
{
|
|
150
|
-
service: "api",
|
|
151
|
-
type: "int",
|
|
152
|
-
path: "src/api/routes/__testkit__/route-b.int.testkit.ts",
|
|
153
|
-
},
|
|
154
|
-
],
|
|
155
|
-
},
|
|
156
|
-
],
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
const result = await validateKnownFailureIssues({
|
|
160
|
-
productDir: tempDir,
|
|
161
|
-
document,
|
|
162
|
-
statusArtifact: {
|
|
163
|
-
tests: [
|
|
164
|
-
{
|
|
165
|
-
service: "api",
|
|
166
|
-
type: "int",
|
|
167
|
-
path: "src/api/routes/__testkit__/route-a.int.testkit.ts",
|
|
168
|
-
status: "failed",
|
|
169
|
-
},
|
|
170
|
-
{
|
|
171
|
-
service: "api",
|
|
172
|
-
type: "int",
|
|
173
|
-
path: "src/api/routes/__testkit__/route-b.int.testkit.ts",
|
|
174
|
-
status: "failed",
|
|
175
|
-
},
|
|
176
|
-
],
|
|
177
|
-
},
|
|
178
|
-
config: {
|
|
179
|
-
provider: "github",
|
|
180
|
-
mode: "error",
|
|
181
|
-
},
|
|
182
|
-
transport: {
|
|
183
|
-
async fetchRepoIssues(repo, numbers) {
|
|
184
|
-
const map = new Map();
|
|
185
|
-
map.set(numbers[0], {
|
|
186
|
-
repo,
|
|
187
|
-
number: numbers[0],
|
|
188
|
-
exists: true,
|
|
189
|
-
title: "UUID path params reach Postgres and return 500 instead of 400",
|
|
190
|
-
state: "OPEN",
|
|
191
|
-
url: `https://github.com/${repo}/issues/${numbers[0]}`,
|
|
192
|
-
checkedAt: "2026-04-27T00:00:00.000Z",
|
|
193
|
-
source: "github",
|
|
194
|
-
});
|
|
195
|
-
return map;
|
|
196
|
-
},
|
|
197
|
-
},
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
expect(result.summary.byCode.title_mismatch).toBeUndefined();
|
|
201
|
-
expect(result.summary.errors).toBe(0);
|
|
202
|
-
expect(result.entries).toHaveLength(2);
|
|
203
|
-
expect(result.entries[0].findings).toEqual([]);
|
|
204
|
-
expect(result.entries[1].findings).toEqual([]);
|
|
205
|
-
expect(result.entries[0].status).toBe("open_and_failing");
|
|
206
|
-
expect(result.entries[1].status).toBe("open_and_failing");
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
it("warns when an open issue is not reproduced", async () => {
|
|
210
|
-
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "testkit-gh-open-"));
|
|
211
|
-
tempDirs.push(tempDir);
|
|
212
|
-
const document = normalizeKnownFailuresDocument({
|
|
213
|
-
schemaVersion: 1,
|
|
214
|
-
issueRepo: "acme/repo",
|
|
215
|
-
entries: [
|
|
216
|
-
{
|
|
217
|
-
id: "bad-message",
|
|
218
|
-
title: "Bad message bug",
|
|
219
|
-
classification: "product_bug",
|
|
220
|
-
state: "open",
|
|
221
|
-
issue: {
|
|
222
|
-
repo: "acme/repo",
|
|
223
|
-
number: 12,
|
|
224
|
-
url: "https://github.com/acme/repo/issues/12",
|
|
225
|
-
},
|
|
226
|
-
description: "Wrong message",
|
|
227
|
-
whyFailing: "Payload is wrong",
|
|
228
|
-
lastReviewedAt: "2026-04-27",
|
|
229
|
-
matches: [
|
|
230
|
-
{
|
|
231
|
-
service: "api",
|
|
232
|
-
type: "int",
|
|
233
|
-
path: "src/api/routes/__testkit__/failing.int.testkit.ts",
|
|
234
|
-
},
|
|
235
|
-
],
|
|
236
|
-
},
|
|
237
|
-
],
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
const result = await validateKnownFailureIssues({
|
|
241
|
-
productDir: tempDir,
|
|
242
|
-
document,
|
|
243
|
-
statusArtifact: {
|
|
244
|
-
tests: [
|
|
245
|
-
{
|
|
246
|
-
service: "api",
|
|
247
|
-
type: "int",
|
|
248
|
-
path: "src/api/routes/__testkit__/failing.int.testkit.ts",
|
|
249
|
-
status: "passed",
|
|
250
|
-
},
|
|
251
|
-
],
|
|
252
|
-
},
|
|
253
|
-
config: {
|
|
254
|
-
provider: "github",
|
|
255
|
-
mode: "warn",
|
|
256
|
-
},
|
|
257
|
-
gitMetadata: {
|
|
258
|
-
repoSlug: "acme/repo",
|
|
259
|
-
},
|
|
260
|
-
transport: {
|
|
261
|
-
async fetchRepoIssues(repo, numbers) {
|
|
262
|
-
const map = new Map();
|
|
263
|
-
map.set(numbers[0], {
|
|
264
|
-
repo,
|
|
265
|
-
number: numbers[0],
|
|
266
|
-
exists: true,
|
|
267
|
-
title: "Bad message bug",
|
|
268
|
-
state: "OPEN",
|
|
269
|
-
url: `https://github.com/${repo}/issues/${numbers[0]}`,
|
|
270
|
-
checkedAt: "2026-04-27T00:00:00.000Z",
|
|
271
|
-
source: "github",
|
|
272
|
-
});
|
|
273
|
-
return map;
|
|
274
|
-
},
|
|
275
|
-
},
|
|
276
|
-
});
|
|
277
|
-
|
|
278
|
-
expect(result.summary.byCode.open_not_reproduced).toBe(1);
|
|
279
|
-
expect(result.summary.errors).toBe(0);
|
|
280
|
-
expect(result.summary.warnings).toBeGreaterThan(0);
|
|
281
|
-
expect(shouldFailKnownFailureIssueValidation(result)).toBe(false);
|
|
282
|
-
expect(result.entries[0].observed).toMatchObject({
|
|
283
|
-
matchedTests: 1,
|
|
284
|
-
executedTests: 1,
|
|
285
|
-
passedTests: 1,
|
|
286
|
-
failedTests: 0,
|
|
287
|
-
skippedTests: 0,
|
|
288
|
-
notRunTests: 0,
|
|
289
|
-
reproduced: false,
|
|
290
|
-
});
|
|
291
|
-
});
|
|
292
|
-
|
|
293
|
-
it("does not treat skipped or not_run files as not reproduced", async () => {
|
|
294
|
-
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "testkit-gh-not-executed-"));
|
|
295
|
-
tempDirs.push(tempDir);
|
|
296
|
-
const document = normalizeKnownFailuresDocument({
|
|
297
|
-
schemaVersion: 1,
|
|
298
|
-
issueRepo: "acme/repo",
|
|
299
|
-
entries: [
|
|
300
|
-
{
|
|
301
|
-
id: "bad-message",
|
|
302
|
-
title: "Bad message bug",
|
|
303
|
-
classification: "product_bug",
|
|
304
|
-
state: "open",
|
|
305
|
-
issue: {
|
|
306
|
-
repo: "acme/repo",
|
|
307
|
-
number: 12,
|
|
308
|
-
url: "https://github.com/acme/repo/issues/12",
|
|
309
|
-
},
|
|
310
|
-
description: "Wrong message",
|
|
311
|
-
whyFailing: "Payload is wrong",
|
|
312
|
-
lastReviewedAt: "2026-04-27",
|
|
313
|
-
matches: [
|
|
314
|
-
{
|
|
315
|
-
service: "api",
|
|
316
|
-
type: "int",
|
|
317
|
-
path: "src/api/routes/__testkit__/failing.int.testkit.ts",
|
|
318
|
-
},
|
|
319
|
-
{
|
|
320
|
-
service: "api",
|
|
321
|
-
type: "int",
|
|
322
|
-
path: "src/api/routes/__testkit__/blocked.int.testkit.ts",
|
|
323
|
-
},
|
|
324
|
-
],
|
|
325
|
-
},
|
|
326
|
-
],
|
|
327
|
-
});
|
|
328
|
-
|
|
329
|
-
const result = await validateKnownFailureIssues({
|
|
330
|
-
productDir: tempDir,
|
|
331
|
-
document,
|
|
332
|
-
statusArtifact: {
|
|
333
|
-
tests: [
|
|
334
|
-
{
|
|
335
|
-
service: "api",
|
|
336
|
-
type: "int",
|
|
337
|
-
path: "src/api/routes/__testkit__/failing.int.testkit.ts",
|
|
338
|
-
status: "skipped",
|
|
339
|
-
},
|
|
340
|
-
{
|
|
341
|
-
service: "api",
|
|
342
|
-
type: "int",
|
|
343
|
-
path: "src/api/routes/__testkit__/blocked.int.testkit.ts",
|
|
344
|
-
status: "not_run",
|
|
345
|
-
},
|
|
346
|
-
],
|
|
347
|
-
},
|
|
348
|
-
config: {
|
|
349
|
-
provider: "github",
|
|
350
|
-
mode: "warn",
|
|
351
|
-
},
|
|
352
|
-
transport: {
|
|
353
|
-
async fetchRepoIssues(repo, numbers) {
|
|
354
|
-
const map = new Map();
|
|
355
|
-
map.set(numbers[0], {
|
|
356
|
-
repo,
|
|
357
|
-
number: numbers[0],
|
|
358
|
-
exists: true,
|
|
359
|
-
title: "Bad message bug",
|
|
360
|
-
state: "OPEN",
|
|
361
|
-
url: `https://github.com/${repo}/issues/${numbers[0]}`,
|
|
362
|
-
checkedAt: "2026-04-27T00:00:00.000Z",
|
|
363
|
-
source: "github",
|
|
364
|
-
});
|
|
365
|
-
return map;
|
|
366
|
-
},
|
|
367
|
-
},
|
|
368
|
-
});
|
|
369
|
-
|
|
370
|
-
expect(result.summary.byCode.open_not_reproduced).toBeUndefined();
|
|
371
|
-
expect(result.entries[0].findings).toEqual([]);
|
|
372
|
-
expect(result.entries[0].status).toBe("not_executed");
|
|
373
|
-
expect(result.entries[0].observed).toMatchObject({
|
|
374
|
-
matchedTests: 2,
|
|
375
|
-
executedTests: 0,
|
|
376
|
-
passedTests: 0,
|
|
377
|
-
failedTests: 0,
|
|
378
|
-
skippedTests: 1,
|
|
379
|
-
notRunTests: 1,
|
|
380
|
-
reproduced: false,
|
|
381
|
-
});
|
|
382
|
-
});
|
|
383
|
-
|
|
384
|
-
it("still treats mixed passed and skipped execution as not reproduced", async () => {
|
|
385
|
-
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "testkit-gh-mixed-execution-"));
|
|
386
|
-
tempDirs.push(tempDir);
|
|
387
|
-
const document = normalizeKnownFailuresDocument({
|
|
388
|
-
schemaVersion: 1,
|
|
389
|
-
issueRepo: "acme/repo",
|
|
390
|
-
entries: [
|
|
391
|
-
{
|
|
392
|
-
id: "bad-message",
|
|
393
|
-
title: "Bad message bug",
|
|
394
|
-
classification: "product_bug",
|
|
395
|
-
state: "open",
|
|
396
|
-
issue: {
|
|
397
|
-
repo: "acme/repo",
|
|
398
|
-
number: 12,
|
|
399
|
-
url: "https://github.com/acme/repo/issues/12",
|
|
400
|
-
},
|
|
401
|
-
description: "Wrong message",
|
|
402
|
-
whyFailing: "Payload is wrong",
|
|
403
|
-
lastReviewedAt: "2026-04-27",
|
|
404
|
-
matches: [
|
|
405
|
-
{
|
|
406
|
-
service: "api",
|
|
407
|
-
type: "int",
|
|
408
|
-
path: "src/api/routes/__testkit__/passed.int.testkit.ts",
|
|
409
|
-
},
|
|
410
|
-
{
|
|
411
|
-
service: "api",
|
|
412
|
-
type: "int",
|
|
413
|
-
path: "src/api/routes/__testkit__/skipped.int.testkit.ts",
|
|
414
|
-
},
|
|
415
|
-
],
|
|
416
|
-
},
|
|
417
|
-
],
|
|
418
|
-
});
|
|
419
|
-
|
|
420
|
-
const result = await validateKnownFailureIssues({
|
|
421
|
-
productDir: tempDir,
|
|
422
|
-
document,
|
|
423
|
-
statusArtifact: {
|
|
424
|
-
tests: [
|
|
425
|
-
{
|
|
426
|
-
service: "api",
|
|
427
|
-
type: "int",
|
|
428
|
-
path: "src/api/routes/__testkit__/passed.int.testkit.ts",
|
|
429
|
-
status: "passed",
|
|
430
|
-
},
|
|
431
|
-
{
|
|
432
|
-
service: "api",
|
|
433
|
-
type: "int",
|
|
434
|
-
path: "src/api/routes/__testkit__/skipped.int.testkit.ts",
|
|
435
|
-
status: "skipped",
|
|
436
|
-
},
|
|
437
|
-
],
|
|
438
|
-
},
|
|
439
|
-
config: {
|
|
440
|
-
provider: "github",
|
|
441
|
-
mode: "warn",
|
|
442
|
-
},
|
|
443
|
-
transport: {
|
|
444
|
-
async fetchRepoIssues(repo, numbers) {
|
|
445
|
-
const map = new Map();
|
|
446
|
-
map.set(numbers[0], {
|
|
447
|
-
repo,
|
|
448
|
-
number: numbers[0],
|
|
449
|
-
exists: true,
|
|
450
|
-
title: "Bad message bug",
|
|
451
|
-
state: "OPEN",
|
|
452
|
-
url: `https://github.com/${repo}/issues/${numbers[0]}`,
|
|
453
|
-
checkedAt: "2026-04-27T00:00:00.000Z",
|
|
454
|
-
source: "github",
|
|
455
|
-
});
|
|
456
|
-
return map;
|
|
457
|
-
},
|
|
458
|
-
},
|
|
459
|
-
});
|
|
460
|
-
|
|
461
|
-
expect(result.summary.byCode.open_not_reproduced).toBe(1);
|
|
462
|
-
expect(result.entries[0].status).toBe("open_not_reproduced");
|
|
463
|
-
expect(result.entries[0].observed).toMatchObject({
|
|
464
|
-
matchedTests: 2,
|
|
465
|
-
executedTests: 1,
|
|
466
|
-
passedTests: 1,
|
|
467
|
-
failedTests: 0,
|
|
468
|
-
skippedTests: 1,
|
|
469
|
-
notRunTests: 0,
|
|
470
|
-
reproduced: false,
|
|
471
|
-
});
|
|
472
|
-
});
|
|
473
|
-
|
|
474
|
-
it("falls back to warning when validation is unavailable", async () => {
|
|
475
|
-
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "testkit-gh-unavailable-"));
|
|
476
|
-
tempDirs.push(tempDir);
|
|
477
|
-
const document = normalizeKnownFailuresDocument({
|
|
478
|
-
schemaVersion: 1,
|
|
479
|
-
entries: [
|
|
480
|
-
{
|
|
481
|
-
id: "bad-message",
|
|
482
|
-
title: "Bad message bug",
|
|
483
|
-
classification: "product_bug",
|
|
484
|
-
state: "open",
|
|
485
|
-
issue: {
|
|
486
|
-
repo: "acme/repo",
|
|
487
|
-
number: 12,
|
|
488
|
-
url: "https://github.com/acme/repo/issues/12",
|
|
489
|
-
},
|
|
490
|
-
description: "Wrong message",
|
|
491
|
-
whyFailing: "Payload is wrong",
|
|
492
|
-
lastReviewedAt: "2026-04-27",
|
|
493
|
-
matches: [{ path: "src/api/routes/__testkit__/failing.int.testkit.ts" }],
|
|
494
|
-
},
|
|
495
|
-
],
|
|
496
|
-
});
|
|
497
|
-
|
|
498
|
-
const result = await validateKnownFailureIssues({
|
|
499
|
-
productDir: tempDir,
|
|
500
|
-
document,
|
|
501
|
-
config: {
|
|
502
|
-
provider: "github",
|
|
503
|
-
mode: "warn",
|
|
504
|
-
},
|
|
505
|
-
transport: null,
|
|
506
|
-
});
|
|
507
|
-
|
|
508
|
-
expect(result.summary.byCode.validation_unavailable).toBe(1);
|
|
509
|
-
expect(result.summary.errors).toBe(0);
|
|
510
|
-
expect(result.summary.warnings).toBeGreaterThan(0);
|
|
511
|
-
});
|
|
512
|
-
});
|
|
@@ -1,192 +0,0 @@
|
|
|
1
|
-
export type KnownFailureClassification =
|
|
2
|
-
| "expected_failure"
|
|
3
|
-
| "infra"
|
|
4
|
-
| "product_bug"
|
|
5
|
-
| "stale_test"
|
|
6
|
-
| "test_bug";
|
|
7
|
-
|
|
8
|
-
export type KnownFailureState = "closed" | "open";
|
|
9
|
-
|
|
10
|
-
export interface KnownFailureIssueRef {
|
|
11
|
-
repo: string;
|
|
12
|
-
number: number;
|
|
13
|
-
url: string;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export interface KnownFailureMatch {
|
|
17
|
-
service?: string;
|
|
18
|
-
type?: string;
|
|
19
|
-
path: string;
|
|
20
|
-
failureKey?: string;
|
|
21
|
-
errorIncludes?: string;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export interface KnownFailureEntry {
|
|
25
|
-
id: string;
|
|
26
|
-
/** Exact issue-tracker title for the linked issue. */
|
|
27
|
-
title: string;
|
|
28
|
-
classification: KnownFailureClassification;
|
|
29
|
-
state: KnownFailureState;
|
|
30
|
-
issue: KnownFailureIssueRef;
|
|
31
|
-
/** Product-local bug slice or route-family summary. */
|
|
32
|
-
description: string;
|
|
33
|
-
/** Underlying technical cause of the failure. */
|
|
34
|
-
whyFailing: string;
|
|
35
|
-
lastReviewedAt: string;
|
|
36
|
-
matches: KnownFailureMatch[];
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export interface KnownFailuresDocument {
|
|
40
|
-
schemaVersion: 1;
|
|
41
|
-
issueRepo?: string | null;
|
|
42
|
-
entries: KnownFailureEntry[];
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export interface KnownFailuresValidationResult {
|
|
46
|
-
errors: string[];
|
|
47
|
-
warnings: string[];
|
|
48
|
-
stats: {
|
|
49
|
-
entries: number;
|
|
50
|
-
matches: number;
|
|
51
|
-
failedTests: number;
|
|
52
|
-
triagedFailedTests: number;
|
|
53
|
-
untriagedFailedTests: number;
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export interface KnownFailureIssueValidationConfig {
|
|
58
|
-
provider?: "github";
|
|
59
|
-
mode?: "off" | "warn" | "error";
|
|
60
|
-
cacheTtlSeconds?: number;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export interface KnownFailureIssueValidationFinding {
|
|
64
|
-
code: string;
|
|
65
|
-
severity: "warning" | "error";
|
|
66
|
-
message: string;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export interface KnownFailureIssueValidationEntry {
|
|
70
|
-
id: string;
|
|
71
|
-
title: string;
|
|
72
|
-
issue: KnownFailureIssueRef;
|
|
73
|
-
observed: {
|
|
74
|
-
matchedTests: number;
|
|
75
|
-
executedTests: number;
|
|
76
|
-
passedTests: number;
|
|
77
|
-
failedTests: number;
|
|
78
|
-
skippedTests: number;
|
|
79
|
-
notRunTests: number;
|
|
80
|
-
reproduced: boolean;
|
|
81
|
-
};
|
|
82
|
-
github: {
|
|
83
|
-
exists: boolean | null;
|
|
84
|
-
title: string | null;
|
|
85
|
-
state: string | null;
|
|
86
|
-
url: string | null;
|
|
87
|
-
checkedAt: string | null;
|
|
88
|
-
cached: boolean;
|
|
89
|
-
};
|
|
90
|
-
findings: KnownFailureIssueValidationFinding[];
|
|
91
|
-
status: string;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
export interface KnownFailureIssueValidationResult {
|
|
95
|
-
schemaVersion: 2;
|
|
96
|
-
provider: "github";
|
|
97
|
-
mode: "off" | "warn" | "error";
|
|
98
|
-
checkedAt: string;
|
|
99
|
-
repo: {
|
|
100
|
-
detected: string | null;
|
|
101
|
-
remoteUrl: string | null;
|
|
102
|
-
configured: string | null;
|
|
103
|
-
};
|
|
104
|
-
availability: {
|
|
105
|
-
hasFreshData: boolean;
|
|
106
|
-
usedCachedFallback: boolean;
|
|
107
|
-
};
|
|
108
|
-
findings: KnownFailureIssueValidationFinding[];
|
|
109
|
-
summary: {
|
|
110
|
-
entries: number;
|
|
111
|
-
observedEntries: number;
|
|
112
|
-
errors: number;
|
|
113
|
-
warnings: number;
|
|
114
|
-
byCode: Record<string, number>;
|
|
115
|
-
byStatus: Record<string, number>;
|
|
116
|
-
};
|
|
117
|
-
entries: KnownFailureIssueValidationEntry[];
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
export declare function loadKnownFailuresConfig(
|
|
121
|
-
productDir: string,
|
|
122
|
-
config: { knownFailuresFile?: string | null } | null
|
|
123
|
-
): KnownFailuresDocument | null;
|
|
124
|
-
export declare function loadKnownFailuresDocument(
|
|
125
|
-
filePath: string,
|
|
126
|
-
relativePath?: string
|
|
127
|
-
): KnownFailuresDocument;
|
|
128
|
-
export declare function normalizeKnownFailuresDocument(
|
|
129
|
-
document: unknown,
|
|
130
|
-
relativePath?: string
|
|
131
|
-
): KnownFailuresDocument;
|
|
132
|
-
export declare function validateKnownFailuresDocument(
|
|
133
|
-
document: KnownFailuresDocument,
|
|
134
|
-
options?: {
|
|
135
|
-
productDir?: string;
|
|
136
|
-
statusArtifactPath?: string;
|
|
137
|
-
statusArtifact?: unknown;
|
|
138
|
-
}
|
|
139
|
-
): KnownFailuresValidationResult;
|
|
140
|
-
export declare function renderKnownFailuresMarkdown(document: KnownFailuresDocument): string;
|
|
141
|
-
export declare function findMatchingKnownFailureEntries(
|
|
142
|
-
document: KnownFailuresDocument,
|
|
143
|
-
fileSummary: {
|
|
144
|
-
service?: string;
|
|
145
|
-
type?: string;
|
|
146
|
-
path: string;
|
|
147
|
-
status?: string;
|
|
148
|
-
error?: string | null;
|
|
149
|
-
failureDetails?: Array<{ key?: string }>;
|
|
150
|
-
}
|
|
151
|
-
): KnownFailureEntry[];
|
|
152
|
-
export declare function matchesKnownFailureEntry(
|
|
153
|
-
entry: KnownFailureEntry,
|
|
154
|
-
fileSummary: {
|
|
155
|
-
service?: string;
|
|
156
|
-
type?: string;
|
|
157
|
-
path: string;
|
|
158
|
-
error?: string | null;
|
|
159
|
-
failureDetails?: Array<{ key?: string }>;
|
|
160
|
-
}
|
|
161
|
-
): boolean;
|
|
162
|
-
export declare function buildKnownFailureFileIdentity(
|
|
163
|
-
service: string,
|
|
164
|
-
type: string,
|
|
165
|
-
filePath: string
|
|
166
|
-
): string;
|
|
167
|
-
|
|
168
|
-
export declare function normalizeKnownFailureIssueValidationConfig(
|
|
169
|
-
value: unknown
|
|
170
|
-
): {
|
|
171
|
-
provider: "github";
|
|
172
|
-
mode: "off" | "warn" | "error";
|
|
173
|
-
cacheTtlSeconds: number;
|
|
174
|
-
} | null;
|
|
175
|
-
export declare function validateKnownFailureIssues(options: {
|
|
176
|
-
productDir: string;
|
|
177
|
-
document: KnownFailuresDocument | null;
|
|
178
|
-
runArtifact?: unknown;
|
|
179
|
-
statusArtifact?: unknown;
|
|
180
|
-
config?: KnownFailureIssueValidationConfig | null;
|
|
181
|
-
gitMetadata?: {
|
|
182
|
-
repoSlug?: string | null;
|
|
183
|
-
remoteUrl?: string | null;
|
|
184
|
-
} | null;
|
|
185
|
-
}): Promise<KnownFailureIssueValidationResult | null>;
|
|
186
|
-
export declare function shouldFailKnownFailureIssueValidation(
|
|
187
|
-
result: KnownFailureIssueValidationResult | null
|
|
188
|
-
): boolean;
|
|
189
|
-
export declare function buildKnownFailureIssueValidationSummaryLines(
|
|
190
|
-
result: KnownFailureIssueValidationResult | null
|
|
191
|
-
): string[];
|
|
192
|
-
export declare function parseGitHubRepoSlug(remoteUrl: string | null | undefined): string | null;
|