@elench/testkit 0.1.82 → 0.1.84

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.
Files changed (100) hide show
  1. package/README.md +37 -7
  2. package/lib/cli/agents/index.mjs +64 -0
  3. package/lib/cli/agents/investigate.mjs +75 -0
  4. package/lib/cli/agents/investigation-context.mjs +102 -0
  5. package/lib/cli/agents/prompt-builder.mjs +25 -0
  6. package/lib/cli/agents/providers/claude.mjs +74 -0
  7. package/lib/cli/agents/providers/codex.mjs +83 -0
  8. package/lib/cli/agents/providers/shared.mjs +134 -0
  9. package/lib/cli/command-helpers.mjs +53 -25
  10. package/lib/cli/commands/investigate.mjs +87 -0
  11. package/lib/cli/entrypoint.mjs +3 -0
  12. package/lib/cli/presentation/colors.mjs +12 -0
  13. package/lib/cli/presentation/events-reporter.mjs +135 -0
  14. package/lib/cli/presentation/summary-box.mjs +11 -11
  15. package/lib/cli/presentation/tree-reporter.mjs +159 -0
  16. package/lib/cli/tui/run-app.mjs +1 -0
  17. package/lib/cli/tui/run-session-app.mjs +370 -0
  18. package/lib/cli/tui/run-session-state.mjs +481 -0
  19. package/lib/cli/tui/run-tree-state.mjs +1 -0
  20. package/lib/config-api/auth-fixtures.mjs +15 -10
  21. package/lib/discovery/index.mjs +1 -1
  22. package/lib/index.d.ts +5 -1
  23. package/lib/runner/orchestrator.mjs +1 -0
  24. package/lib/runtime/index.d.ts +138 -5
  25. package/lib/runtime/index.mjs +68 -2
  26. package/lib/runtime-src/k6/http-assertions.js +31 -1
  27. package/lib/runtime-src/k6/http-checks.js +120 -0
  28. package/lib/runtime-src/k6/http-suite-runtime.js +5 -1
  29. package/lib/runtime-src/k6/http.js +213 -23
  30. package/lib/runtime-src/shared/error-body.mjs +42 -0
  31. package/lib/runtime-src/shared/http-parsing.mjs +68 -0
  32. package/node_modules/@elench/next-analysis/package.json +1 -1
  33. package/node_modules/@elench/testkit-bridge/package.json +2 -2
  34. package/node_modules/@elench/testkit-protocol/package.json +1 -1
  35. package/node_modules/@elench/ts-analysis/package.json +1 -1
  36. package/package.json +7 -6
  37. package/lib/app/configs.test.mjs +0 -34
  38. package/lib/app/typecheck.test.mjs +0 -24
  39. package/lib/bundler/index.test.mjs +0 -164
  40. package/lib/cli/args.test.mjs +0 -110
  41. package/lib/cli/presentation/code-frames.test.mjs +0 -71
  42. package/lib/cli/presentation/run-reporter.test.mjs +0 -192
  43. package/lib/cli/presentation/summary-box.test.mjs +0 -43
  44. package/lib/cli/presentation/terminal-layout.test.mjs +0 -23
  45. package/lib/config/database.test.mjs +0 -29
  46. package/lib/config/discovery.test.mjs +0 -276
  47. package/lib/config/env.test.mjs +0 -40
  48. package/lib/config/index.test.mjs +0 -44
  49. package/lib/config/paths.test.mjs +0 -27
  50. package/lib/config/runtime.test.mjs +0 -82
  51. package/lib/config/skip-config.test.mjs +0 -63
  52. package/lib/config-api/index.test.mjs +0 -344
  53. package/lib/config-api/next-runtime-tsconfig.test.mjs +0 -58
  54. package/lib/coverage/backend-discovery.test.mjs +0 -61
  55. package/lib/coverage/evidence.test.mjs +0 -87
  56. package/lib/coverage/index.test.mjs +0 -715
  57. package/lib/coverage/routing.test.mjs +0 -36
  58. package/lib/coverage/shared.test.mjs +0 -72
  59. package/lib/database/fingerprint.test.mjs +0 -99
  60. package/lib/database/index.test.mjs +0 -95
  61. package/lib/database/naming.test.mjs +0 -39
  62. package/lib/database/state.test.mjs +0 -66
  63. package/lib/database/template-steps.test.mjs +0 -43
  64. package/lib/discovery/file-metadata.test.mjs +0 -51
  65. package/lib/discovery/index.test.mjs +0 -182
  66. package/lib/discovery/path-policy.test.mjs +0 -65
  67. package/lib/drizzle/index.test.mjs +0 -33
  68. package/lib/env/index.test.mjs +0 -82
  69. package/lib/history/index.test.mjs +0 -115
  70. package/lib/package.test.mjs +0 -59
  71. package/lib/playwright/index.test.mjs +0 -43
  72. package/lib/regressions/github.test.mjs +0 -324
  73. package/lib/regressions/index.test.mjs +0 -187
  74. package/lib/reporters/playwright.test.mjs +0 -167
  75. package/lib/runner/default-runtime-errors.test.mjs +0 -49
  76. package/lib/runner/execution-config.test.mjs +0 -67
  77. package/lib/runner/failure-details.test.mjs +0 -114
  78. package/lib/runner/formatting.test.mjs +0 -205
  79. package/lib/runner/metadata.test.mjs +0 -52
  80. package/lib/runner/planning.test.mjs +0 -371
  81. package/lib/runner/playwright-config.test.mjs +0 -78
  82. package/lib/runner/processes.test.mjs +0 -21
  83. package/lib/runner/regressions.test.mjs +0 -168
  84. package/lib/runner/reporting.test.mjs +0 -310
  85. package/lib/runner/results.test.mjs +0 -376
  86. package/lib/runner/runtime-manager.test.mjs +0 -252
  87. package/lib/runner/runtime-preparation.test.mjs +0 -141
  88. package/lib/runner/selection.test.mjs +0 -24
  89. package/lib/runner/setup-operations.test.mjs +0 -94
  90. package/lib/runner/state.test.mjs +0 -62
  91. package/lib/runner/suite-selection.test.mjs +0 -49
  92. package/lib/runner/template.test.mjs +0 -272
  93. package/lib/shared/build-config.test.mjs +0 -132
  94. package/lib/shared/configured-steps.test.mjs +0 -102
  95. package/lib/shared/execution-schema.test.mjs +0 -26
  96. package/lib/shared/file-timeout.test.mjs +0 -64
  97. package/lib/shared/test-context.test.mjs +0 -43
  98. package/lib/timing/index.test.mjs +0 -64
  99. package/lib/toolchains/index.test.mjs +0 -168
  100. package/lib/vitest/index.test.mjs +0 -20
@@ -1,371 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import {
3
- applyShard,
4
- buildGraphDirName,
5
- buildRuntimeGraphs,
6
- buildTaskQueue,
7
- claimNextTask,
8
- collectSuites,
9
- resolveRuntimeConfigs,
10
- } from "./planning.mjs";
11
-
12
- function makeConfig(name, extras = {}) {
13
- const providedTestkit = extras.testkit || {};
14
- return {
15
- name,
16
- suites: extras.suites || {},
17
- testkit: {
18
- dependsOn: extras.dependsOn || providedTestkit.dependsOn || [],
19
- execution: providedTestkit.execution || {
20
- workers: 1,
21
- fileTimeoutSeconds: 60,
22
- },
23
- runtime: providedTestkit.runtime || {
24
- instances: 1,
25
- maxConcurrentTasks: Number.POSITIVE_INFINITY,
26
- },
27
- requirements: providedTestkit.requirements || {
28
- suites: [],
29
- files: [],
30
- fileLocksByPath: new Map(),
31
- },
32
- ...providedTestkit,
33
- },
34
- ...extras,
35
- };
36
- }
37
-
38
- describe("runner-planning", () => {
39
- it("orders runtime configs by dependency and detects cycles", () => {
40
- const api = makeConfig("api");
41
- const frontend = makeConfig("frontend", { dependsOn: ["api"] });
42
- const configMap = new Map([
43
- ["api", api],
44
- ["frontend", frontend],
45
- ]);
46
-
47
- expect(resolveRuntimeConfigs(frontend, configMap).map((config) => config.name)).toEqual([
48
- "api",
49
- "frontend",
50
- ]);
51
-
52
- api.testkit.dependsOn = ["frontend"];
53
- expect(() => resolveRuntimeConfigs(frontend, configMap)).toThrow("Dependency cycle");
54
- });
55
-
56
- it("collects suites with user-facing type selection", () => {
57
- const config = makeConfig("api", {
58
- suites: {
59
- integration: [
60
- { name: "health", files: ["tests/api/integration/health.int.testkit.ts"] },
61
- ],
62
- e2e: [
63
- {
64
- name: "auth",
65
- framework: "playwright",
66
- files: [
67
- "tests/frontend/e2e/auth.pw.testkit.ts",
68
- "tests/frontend/e2e/signup.pw.testkit.ts",
69
- ],
70
- },
71
- ],
72
- },
73
- });
74
-
75
- expect(collectSuites(config, ["int"], [], [])[0]).toMatchObject({
76
- name: "health",
77
- type: "integration",
78
- displayType: "int",
79
- framework: "k6",
80
- weight: 1,
81
- });
82
-
83
- expect(collectSuites(config, ["pw"], [], [])[0]).toMatchObject({
84
- name: "auth",
85
- displayType: "pw",
86
- framework: "playwright",
87
- weight: 2,
88
- });
89
-
90
- expect(
91
- collectSuites(
92
- config,
93
- ["all"],
94
- [],
95
- ["tests/frontend/e2e/signup.pw.testkit.ts"]
96
- )[0]
97
- ).toMatchObject({
98
- name: "auth",
99
- files: ["tests/frontend/e2e/signup.pw.testkit.ts"],
100
- });
101
- });
102
-
103
- it("keeps skipped files visible while removing them from runnable work", () => {
104
- const config = makeConfig("api", {
105
- suites: {
106
- integration: [
107
- {
108
- name: "billing",
109
- files: [
110
- "__testkit__/billing/a.int.testkit.ts",
111
- "__testkit__/billing/b.int.testkit.ts",
112
- ],
113
- },
114
- ],
115
- },
116
- testkit: {
117
- skip: {
118
- fileReasonByPath: new Map([
119
- ["__testkit__/billing/a.int.testkit.ts", "Billing is stubbed"],
120
- ]),
121
- suites: [],
122
- },
123
- },
124
- });
125
-
126
- expect(collectSuites(config, ["int"], [], [])).toEqual([
127
- expect.objectContaining({
128
- name: "billing",
129
- files: ["__testkit__/billing/b.int.testkit.ts"],
130
- skippedFiles: [
131
- {
132
- path: "__testkit__/billing/a.int.testkit.ts",
133
- reason: "Billing is stubbed",
134
- },
135
- ],
136
- totalFileCount: 2,
137
- }),
138
- ]);
139
- });
140
-
141
- it("does not require repo-level skip config when file metadata is present", () => {
142
- const config = makeConfig("api", {
143
- suites: {
144
- integration: [
145
- {
146
- name: "health",
147
- files: ["__testkit__/health/health.int.testkit.ts"],
148
- },
149
- ],
150
- },
151
- testkit: {
152
- fileMetadataByPath: new Map([
153
- ["__testkit__/health/health.int.testkit.ts", { skipReason: null, locks: [] }],
154
- ]),
155
- },
156
- });
157
-
158
- expect(collectSuites(config, ["int"], [], [])).toEqual([
159
- expect.objectContaining({
160
- name: "health",
161
- files: ["__testkit__/health/health.int.testkit.ts"],
162
- skippedFiles: [],
163
- }),
164
- ]);
165
- });
166
-
167
- it("applies lock requirements to matching suites and files", () => {
168
- const api = makeConfig("api", {
169
- suites: {
170
- integration: [
171
- {
172
- name: "routes",
173
- framework: "k6",
174
- files: [
175
- "src/api/routes/__testkit__/health.int.testkit.ts",
176
- "src/api/routes/__testkit__/crawls-worker-concurrency.int.testkit.ts",
177
- ],
178
- orderIndex: 0,
179
- weight: 2,
180
- },
181
- ],
182
- },
183
- testkit: {
184
- runtime: {
185
- instances: 3,
186
- maxConcurrentTasks: 2,
187
- },
188
- requirements: {
189
- suites: [
190
- {
191
- selector: { kind: "typed", type: "int", name: "routes", raw: "int:routes" },
192
- locks: ["suite-lock"],
193
- },
194
- ],
195
- files: [
196
- {
197
- path: "src/api/routes/__testkit__/crawls-worker-concurrency.int.testkit.ts",
198
- locks: ["worker-loop"],
199
- },
200
- ],
201
- fileLocksByPath: new Map([
202
- [
203
- "src/api/routes/__testkit__/crawls-worker-concurrency.int.testkit.ts",
204
- ["worker-loop"],
205
- ],
206
- ]),
207
- },
208
- },
209
- });
210
-
211
- const graphs = buildRuntimeGraphs([
212
- {
213
- config: api,
214
- skipped: false,
215
- runtimeConfigs: [api],
216
- runtimeNames: ["api"],
217
- runtimeKey: "api",
218
- suites: collectSuites(api, ["int"], [], []),
219
- },
220
- ]);
221
-
222
- const queue = buildTaskQueue(
223
- [
224
- {
225
- config: api,
226
- skipped: false,
227
- runtimeConfigs: [api],
228
- runtimeNames: ["api"],
229
- runtimeKey: "api",
230
- assignedGraphKey: "api",
231
- suites: collectSuites(api, ["int"], [], []),
232
- },
233
- ],
234
- graphs,
235
- { files: {} }
236
- );
237
-
238
- expect(queue).toEqual(
239
- expect.arrayContaining([
240
- expect.objectContaining({
241
- file: "src/api/routes/__testkit__/health.int.testkit.ts",
242
- locks: ["suite-lock"],
243
- }),
244
- expect.objectContaining({
245
- file: "src/api/routes/__testkit__/crawls-worker-concurrency.int.testkit.ts",
246
- locks: ["suite-lock", "worker-loop"],
247
- }),
248
- ])
249
- );
250
- });
251
-
252
- it("builds exact runtime graphs and claims single file tasks", () => {
253
- const api = makeConfig("api", {
254
- testkit: {
255
- runtime: {
256
- instances: 2,
257
- maxConcurrentTasks: 4,
258
- },
259
- },
260
- });
261
- const frontend = makeConfig("frontend", {
262
- dependsOn: ["api"],
263
- testkit: {
264
- runtime: {
265
- instances: 1,
266
- maxConcurrentTasks: 2,
267
- },
268
- },
269
- });
270
-
271
- const plans = [
272
- {
273
- config: api,
274
- skipped: false,
275
- runtimeConfigs: [api],
276
- runtimeNames: ["api"],
277
- runtimeKey: "api",
278
- suites: [
279
- {
280
- name: "health",
281
- type: "integration",
282
- framework: "k6",
283
- files: ["a.js", "b.js"],
284
- orderIndex: 0,
285
- weight: 2,
286
- },
287
- ],
288
- },
289
- {
290
- config: frontend,
291
- skipped: false,
292
- runtimeConfigs: [api, frontend],
293
- runtimeNames: ["api", "frontend"],
294
- runtimeKey: "api|frontend",
295
- suites: [
296
- {
297
- name: "auth",
298
- type: "e2e",
299
- framework: "playwright",
300
- files: ["auth.spec.js"],
301
- orderIndex: 0,
302
- weight: 1,
303
- },
304
- ],
305
- },
306
- ];
307
-
308
- expect(applyShard(["a", "b", "c", "d"], { index: 2, total: 2 })).toEqual(["b", "d"]);
309
-
310
- const graphs = buildRuntimeGraphs(plans);
311
- expect(graphs).toEqual([
312
- expect.objectContaining({
313
- key: "api",
314
- instanceCount: 2,
315
- maxConcurrentTasks: 4,
316
- targetNames: ["api"],
317
- }),
318
- expect.objectContaining({
319
- key: "api|frontend",
320
- instanceCount: 1,
321
- maxConcurrentTasks: 2,
322
- targetNames: ["frontend"],
323
- }),
324
- ]);
325
- expect(buildGraphDirName(["api", "frontend"])).toBe("api__frontend");
326
-
327
- const queue = buildTaskQueue(plans, graphs, { files: {} });
328
- expect(queue).toHaveLength(3);
329
- expect(claimNextTask(queue, "api|frontend")).toMatchObject({
330
- framework: "playwright",
331
- graphKey: "api|frontend",
332
- file: "auth.spec.js",
333
- });
334
- expect(claimNextTask(queue, "api")).toMatchObject({
335
- framework: "k6",
336
- graphKey: "api",
337
- });
338
- });
339
-
340
- it("skips blocked preferred-graph work and claims the next runnable task", () => {
341
- const queue = [
342
- {
343
- id: 1,
344
- graphKey: "api",
345
- file: "blocked.js",
346
- },
347
- {
348
- id: 2,
349
- graphKey: "api|frontend",
350
- file: "runnable.js",
351
- },
352
- ];
353
-
354
- const claimed = claimNextTask(
355
- queue,
356
- "api",
357
- (task) => task.file === "runnable.js"
358
- );
359
-
360
- expect(claimed).toMatchObject({
361
- id: 2,
362
- file: "runnable.js",
363
- });
364
- expect(queue).toEqual([
365
- expect.objectContaining({
366
- id: 1,
367
- file: "blocked.js",
368
- }),
369
- ]);
370
- });
371
- });
@@ -1,78 +0,0 @@
1
- import fs from "fs";
2
- import os from "os";
3
- import path from "path";
4
- import { pathToFileURL } from "url";
5
- import { afterEach, describe, expect, it } from "vitest";
6
- import {
7
- ensurePlaywrightTestConfig,
8
- findPlaywrightConfig,
9
- resolvePlaywrightOutputDir,
10
- } from "./playwright-config.mjs";
11
-
12
- const cleanups = [];
13
-
14
- afterEach(() => {
15
- while (cleanups.length > 0) {
16
- cleanups.pop()();
17
- }
18
- });
19
-
20
- function makeTempDir(prefix) {
21
- const dir = fs.mkdtempSync(path.join(os.tmpdir(), prefix));
22
- cleanups.push(() => fs.rmSync(dir, { recursive: true, force: true }));
23
- return dir;
24
- }
25
-
26
- describe("runner-playwright-config", () => {
27
- it("uses a lease-local output directory under the lease dir", async () => {
28
- const productDir = makeTempDir("testkit-playwright-product-");
29
- const leaseDir = path.join(productDir, ".testkit", "leases", "lease-12");
30
- const cwd = path.join(productDir, "frontend");
31
- fs.mkdirSync(cwd, { recursive: true });
32
- fs.writeFileSync(
33
- path.join(cwd, "playwright.config.mjs"),
34
- "export default { outputDir: 'shared-test-results', workers: 8, fullyParallel: true, webServer: { command: 'npm run dev', url: 'http://127.0.0.1:3000' } };\n"
35
- );
36
-
37
- const configPath = ensurePlaywrightTestConfig(
38
- { name: "frontend", productDir, stateDir: path.join(productDir, ".testkit", "frontend") },
39
- cwd,
40
- ["frontend/__testkit__/homepage/homepage.pw.testkit.ts"],
41
- { leaseDir }
42
- );
43
- const generated = await import(pathToFileURL(configPath).href + `?t=${Date.now()}`);
44
-
45
- const expectedOutputDir = resolvePlaywrightOutputDir(leaseDir);
46
- expect(generated.default.outputDir).toBe(expectedOutputDir);
47
- expect(generated.default.workers).toBe(1);
48
- expect(generated.default.fullyParallel).toBe(false);
49
- expect(generated.default.webServer).toBeUndefined();
50
- expect(fs.existsSync(expectedOutputDir)).toBe(true);
51
- expect(fs.readFileSync(configPath, "utf8")).toContain(
52
- `outputDir: ${JSON.stringify(expectedOutputDir)}`
53
- );
54
- expect(fs.readFileSync(configPath, "utf8")).toContain("workers: 1");
55
- expect(fs.readFileSync(configPath, "utf8")).toContain("fullyParallel: false");
56
- });
57
-
58
- it("requires a lease-scoped directory", () => {
59
- const productDir = makeTempDir("testkit-playwright-product-");
60
- const cwd = path.join(productDir, "frontend");
61
- fs.mkdirSync(cwd, { recursive: true });
62
-
63
- expect(() =>
64
- ensurePlaywrightTestConfig(
65
- { name: "frontend", productDir, stateDir: path.join(productDir, ".testkit", "frontend") },
66
- cwd,
67
- ["frontend/__testkit__/homepage/homepage.pw.testkit.ts"]
68
- )
69
- ).toThrow('Playwright task for service "frontend" requires a lease-scoped directory');
70
- });
71
-
72
- it("finds a supported playwright config file", () => {
73
- const cwd = makeTempDir("testkit-playwright-cwd-");
74
- fs.writeFileSync(path.join(cwd, "playwright.config.ts"), "export default {};\n");
75
-
76
- expect(findPlaywrightConfig(cwd)).toBe(path.join(cwd, "playwright.config.ts"));
77
- });
78
- });
@@ -1,21 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { normalizeServiceStartCommand } from "./processes.mjs";
3
-
4
- describe("runner processes", () => {
5
- it("strips a single leading exec prefix for backward compatibility", () => {
6
- expect(normalizeServiceStartCommand("exec node server.mjs")).toBe("node server.mjs");
7
- expect(normalizeServiceStartCommand(" exec ./node_modules/.bin/next dev -p {port} ")).toBe(
8
- "./node_modules/.bin/next dev -p {port}"
9
- );
10
- });
11
-
12
- it("leaves plain commands unchanged", () => {
13
- expect(normalizeServiceStartCommand("node server.mjs")).toBe("node server.mjs");
14
- });
15
-
16
- it("rejects empty commands", () => {
17
- expect(() => normalizeServiceStartCommand(" ")).toThrow(
18
- /Service start command must not be empty/
19
- );
20
- });
21
- });
@@ -1,168 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { normalizeRegressionCatalogDocument } from "../regressions/index.mjs";
3
- import { applyRegressionAnalysisToArtifacts } from "./regressions.mjs";
4
-
5
- describe("runner regressions", () => {
6
- it("classifies known failed regressions and enriches both artifacts", () => {
7
- const regressionCatalog = normalizeRegressionCatalogDocument({
8
- schemaVersion: 1,
9
- issueRepo: "acme/repo",
10
- entries: [
11
- {
12
- id: "bad-message",
13
- classification: "product_bug",
14
- issue: {
15
- repo: "acme/repo",
16
- number: 12,
17
- },
18
- summary: "API returns the wrong message",
19
- cause: "The endpoint payload is wrong.",
20
- lastReviewedAt: "2026-05-04",
21
- fingerprints: [
22
- {
23
- service: "api",
24
- type: "int",
25
- path: "__testkit__/http/failing.int.testkit.ts",
26
- failureKey: "returns the wrong message",
27
- },
28
- ],
29
- },
30
- ],
31
- });
32
-
33
- const runArtifact = {
34
- services: [
35
- {
36
- name: "api",
37
- suites: [
38
- {
39
- type: "int",
40
- files: [
41
- {
42
- path: "__testkit__/http/failing.int.testkit.ts",
43
- status: "failed",
44
- error: "Default runtime thresholds failed: checks(rate==1.0)",
45
- failureDetails: [
46
- {
47
- kind: "k6-check",
48
- key: "returns the wrong message",
49
- title: "returns the wrong message",
50
- count: 1,
51
- },
52
- ],
53
- },
54
- ],
55
- },
56
- ],
57
- },
58
- ],
59
- };
60
- const statusArtifact = {
61
- tests: [
62
- {
63
- service: "api",
64
- type: "int",
65
- path: "__testkit__/http/failing.int.testkit.ts",
66
- status: "failed",
67
- error: "Default runtime thresholds failed: checks(rate==1.0)",
68
- failureDetails: [
69
- {
70
- kind: "k6-check",
71
- key: "returns the wrong message",
72
- title: "returns the wrong message",
73
- count: 1,
74
- },
75
- ],
76
- },
77
- ],
78
- };
79
-
80
- const enriched = applyRegressionAnalysisToArtifacts(
81
- runArtifact,
82
- statusArtifact,
83
- regressionCatalog,
84
- {
85
- entries: [
86
- {
87
- id: "bad-message",
88
- status: "open_and_failing",
89
- github: {
90
- state: "OPEN",
91
- url: "https://github.com/acme/repo/issues/12",
92
- cached: false,
93
- },
94
- findings: [],
95
- },
96
- ],
97
- findings: [],
98
- }
99
- );
100
-
101
- expect(enriched.statusArtifact.tests[0].diagnosis).toMatchObject({
102
- status: "known_regression",
103
- classifications: ["product_bug"],
104
- });
105
- expect(enriched.runArtifact.services[0].suites[0].files[0].diagnosis.entries[0]).toMatchObject({
106
- id: "bad-message",
107
- issue: {
108
- number: 12,
109
- },
110
- });
111
- expect(enriched.statusArtifact.regressions.summary).toEqual({
112
- newRegressions: 0,
113
- knownRegressions: 1,
114
- fixedKnownRegressions: 0,
115
- catalogStale: 0,
116
- catalogSyncUnavailable: false,
117
- usedStaleCache: false,
118
- });
119
- });
120
-
121
- it("marks unmatched failed tests as new regressions and emits drafts", () => {
122
- const enriched = applyRegressionAnalysisToArtifacts(
123
- {
124
- services: [
125
- {
126
- name: "api",
127
- suites: [
128
- {
129
- type: "int",
130
- files: [
131
- {
132
- path: "__testkit__/http/failing.int.testkit.ts",
133
- status: "failed",
134
- error: "boom",
135
- },
136
- ],
137
- },
138
- ],
139
- },
140
- ],
141
- },
142
- {
143
- tests: [
144
- {
145
- service: "api",
146
- type: "int",
147
- path: "__testkit__/http/failing.int.testkit.ts",
148
- status: "failed",
149
- error: "boom",
150
- },
151
- ],
152
- },
153
- normalizeRegressionCatalogDocument({
154
- schemaVersion: 1,
155
- entries: [],
156
- }),
157
- null
158
- );
159
-
160
- expect(enriched.statusArtifact.tests[0].diagnosis).toEqual({
161
- status: "new_regression",
162
- classifications: [],
163
- entries: [],
164
- });
165
- expect(enriched.statusArtifact.regressions.summary.newRegressions).toBe(1);
166
- expect(enriched.statusArtifact.regressions.drafts.newRegressions).toHaveLength(1);
167
- });
168
- });