@elench/testkit 0.1.43 → 0.1.44

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.
@@ -0,0 +1,219 @@
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("flags closed issues that still reproduce and title mismatches", 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.errors).toBeGreaterThan(0);
102
+ expect(result.entries[0].status).toBe("closed_but_failing");
103
+ expect(shouldFailKnownFailureIssueValidation(result)).toBe(true);
104
+ });
105
+
106
+ it("warns when an open issue is not reproduced", async () => {
107
+ const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "testkit-gh-open-"));
108
+ tempDirs.push(tempDir);
109
+ const document = normalizeKnownFailuresDocument({
110
+ schemaVersion: 1,
111
+ issueRepo: "acme/repo",
112
+ entries: [
113
+ {
114
+ id: "bad-message",
115
+ title: "Bad message bug",
116
+ classification: "product_bug",
117
+ state: "open",
118
+ issue: {
119
+ repo: "acme/repo",
120
+ number: 12,
121
+ url: "https://github.com/acme/repo/issues/12",
122
+ },
123
+ description: "Wrong message",
124
+ whyFailing: "Payload is wrong",
125
+ lastReviewedAt: "2026-04-27",
126
+ matches: [
127
+ {
128
+ service: "api",
129
+ type: "int",
130
+ path: "src/api/routes/__testkit__/failing.int.testkit.ts",
131
+ },
132
+ ],
133
+ },
134
+ ],
135
+ });
136
+
137
+ const result = await validateKnownFailureIssues({
138
+ productDir: tempDir,
139
+ document,
140
+ statusArtifact: {
141
+ tests: [
142
+ {
143
+ service: "api",
144
+ type: "int",
145
+ path: "src/api/routes/__testkit__/failing.int.testkit.ts",
146
+ status: "passed",
147
+ },
148
+ ],
149
+ },
150
+ config: {
151
+ provider: "github",
152
+ mode: "warn",
153
+ },
154
+ gitMetadata: {
155
+ repoSlug: "acme/repo",
156
+ },
157
+ transport: {
158
+ async fetchRepoIssues(repo, numbers) {
159
+ const map = new Map();
160
+ map.set(numbers[0], {
161
+ repo,
162
+ number: numbers[0],
163
+ exists: true,
164
+ title: "Bad message bug",
165
+ state: "OPEN",
166
+ url: `https://github.com/${repo}/issues/${numbers[0]}`,
167
+ checkedAt: "2026-04-27T00:00:00.000Z",
168
+ source: "github",
169
+ });
170
+ return map;
171
+ },
172
+ },
173
+ });
174
+
175
+ expect(result.summary.byCode.open_not_reproduced).toBe(1);
176
+ expect(result.summary.errors).toBe(0);
177
+ expect(result.summary.warnings).toBeGreaterThan(0);
178
+ expect(shouldFailKnownFailureIssueValidation(result)).toBe(false);
179
+ });
180
+
181
+ it("falls back to warning when validation is unavailable", async () => {
182
+ const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "testkit-gh-unavailable-"));
183
+ tempDirs.push(tempDir);
184
+ const document = normalizeKnownFailuresDocument({
185
+ schemaVersion: 1,
186
+ entries: [
187
+ {
188
+ id: "bad-message",
189
+ title: "Bad message bug",
190
+ classification: "product_bug",
191
+ state: "open",
192
+ issue: {
193
+ repo: "acme/repo",
194
+ number: 12,
195
+ url: "https://github.com/acme/repo/issues/12",
196
+ },
197
+ description: "Wrong message",
198
+ whyFailing: "Payload is wrong",
199
+ lastReviewedAt: "2026-04-27",
200
+ matches: [{ path: "src/api/routes/__testkit__/failing.int.testkit.ts" }],
201
+ },
202
+ ],
203
+ });
204
+
205
+ const result = await validateKnownFailureIssues({
206
+ productDir: tempDir,
207
+ document,
208
+ config: {
209
+ provider: "github",
210
+ mode: "warn",
211
+ },
212
+ transport: null,
213
+ });
214
+
215
+ expect(result.summary.byCode.validation_unavailable).toBe(1);
216
+ expect(result.summary.errors).toBe(0);
217
+ expect(result.summary.warnings).toBeGreaterThan(0);
218
+ });
219
+ });
@@ -0,0 +1,185 @@
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
+ title: string;
27
+ classification: KnownFailureClassification;
28
+ state: KnownFailureState;
29
+ issue: KnownFailureIssueRef;
30
+ description: string;
31
+ whyFailing: string;
32
+ lastReviewedAt: string;
33
+ matches: KnownFailureMatch[];
34
+ }
35
+
36
+ export interface KnownFailuresDocument {
37
+ schemaVersion: 1;
38
+ issueRepo?: string | null;
39
+ entries: KnownFailureEntry[];
40
+ }
41
+
42
+ export interface KnownFailuresValidationResult {
43
+ errors: string[];
44
+ warnings: string[];
45
+ stats: {
46
+ entries: number;
47
+ matches: number;
48
+ failedTests: number;
49
+ triagedFailedTests: number;
50
+ untriagedFailedTests: number;
51
+ };
52
+ }
53
+
54
+ export interface KnownFailureIssueValidationConfig {
55
+ provider?: "github";
56
+ mode?: "off" | "warn" | "error";
57
+ cacheTtlSeconds?: number;
58
+ }
59
+
60
+ export interface KnownFailureIssueValidationFinding {
61
+ code: string;
62
+ severity: "warning" | "error";
63
+ message: string;
64
+ }
65
+
66
+ export interface KnownFailureIssueValidationEntry {
67
+ id: string;
68
+ title: string;
69
+ issue: KnownFailureIssueRef;
70
+ observed: {
71
+ matchedTests: number;
72
+ failedTests: number;
73
+ reproduced: boolean;
74
+ };
75
+ github: {
76
+ exists: boolean | null;
77
+ title: string | null;
78
+ state: string | null;
79
+ url: string | null;
80
+ checkedAt: string | null;
81
+ cached: boolean;
82
+ };
83
+ findings: KnownFailureIssueValidationFinding[];
84
+ status: string;
85
+ }
86
+
87
+ export interface KnownFailureIssueValidationResult {
88
+ schemaVersion: 1;
89
+ provider: "github";
90
+ mode: "off" | "warn" | "error";
91
+ checkedAt: string;
92
+ repo: {
93
+ detected: string | null;
94
+ remoteUrl: string | null;
95
+ configured: string | null;
96
+ };
97
+ availability: {
98
+ hasFreshData: boolean;
99
+ usedCachedFallback: boolean;
100
+ };
101
+ findings: KnownFailureIssueValidationFinding[];
102
+ summary: {
103
+ entries: number;
104
+ observedEntries: number;
105
+ errors: number;
106
+ warnings: number;
107
+ byCode: Record<string, number>;
108
+ byStatus: Record<string, number>;
109
+ };
110
+ entries: KnownFailureIssueValidationEntry[];
111
+ }
112
+
113
+ export declare function loadKnownFailuresConfig(
114
+ productDir: string,
115
+ config: { knownFailuresFile?: string | null } | null
116
+ ): KnownFailuresDocument | null;
117
+ export declare function loadKnownFailuresDocument(
118
+ filePath: string,
119
+ relativePath?: string
120
+ ): KnownFailuresDocument;
121
+ export declare function normalizeKnownFailuresDocument(
122
+ document: unknown,
123
+ relativePath?: string
124
+ ): KnownFailuresDocument;
125
+ export declare function validateKnownFailuresDocument(
126
+ document: KnownFailuresDocument,
127
+ options?: {
128
+ productDir?: string;
129
+ statusArtifactPath?: string;
130
+ statusArtifact?: unknown;
131
+ }
132
+ ): KnownFailuresValidationResult;
133
+ export declare function renderKnownFailuresMarkdown(document: KnownFailuresDocument): string;
134
+ export declare function findMatchingKnownFailureEntries(
135
+ document: KnownFailuresDocument,
136
+ fileSummary: {
137
+ service?: string;
138
+ type?: string;
139
+ path: string;
140
+ status?: string;
141
+ error?: string | null;
142
+ failureDetails?: Array<{ key?: string }>;
143
+ }
144
+ ): KnownFailureEntry[];
145
+ export declare function matchesKnownFailureEntry(
146
+ entry: KnownFailureEntry,
147
+ fileSummary: {
148
+ service?: string;
149
+ type?: string;
150
+ path: string;
151
+ error?: string | null;
152
+ failureDetails?: Array<{ key?: string }>;
153
+ }
154
+ ): boolean;
155
+ export declare function buildKnownFailureFileIdentity(
156
+ service: string,
157
+ type: string,
158
+ filePath: string
159
+ ): string;
160
+
161
+ export declare function normalizeKnownFailureIssueValidationConfig(
162
+ value: unknown
163
+ ): {
164
+ provider: "github";
165
+ mode: "off" | "warn" | "error";
166
+ cacheTtlSeconds: number;
167
+ } | null;
168
+ export declare function validateKnownFailureIssues(options: {
169
+ productDir: string;
170
+ document: KnownFailuresDocument | null;
171
+ runArtifact?: unknown;
172
+ statusArtifact?: unknown;
173
+ config?: KnownFailureIssueValidationConfig | null;
174
+ gitMetadata?: {
175
+ repoSlug?: string | null;
176
+ remoteUrl?: string | null;
177
+ } | null;
178
+ }): Promise<KnownFailureIssueValidationResult | null>;
179
+ export declare function shouldFailKnownFailureIssueValidation(
180
+ result: KnownFailureIssueValidationResult | null
181
+ ): boolean;
182
+ export declare function buildKnownFailureIssueValidationSummaryLines(
183
+ result: KnownFailureIssueValidationResult | null
184
+ ): string[];
185
+ export declare function parseGitHubRepoSlug(remoteUrl: string | null | undefined): string | null;