@elench/testkit 0.1.149 → 0.1.151
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 +29 -9
- package/lib/cli/assistant/view-model.mjs +1 -1
- package/lib/cli/components/blocks/run-tree.mjs +1 -1
- package/lib/cli/renderers/run/events.mjs +4 -3
- package/lib/cli/renderers/run/failure.mjs +2 -2
- package/lib/cli/renderers/run/inline-detail.mjs +2 -2
- package/lib/cli/renderers/run/interactive.mjs +2 -2
- package/lib/cli/renderers/run/text-reporter.mjs +9 -9
- package/lib/cli/state/run/model.mjs +7 -7
- package/lib/cli/state/run/state.mjs +3 -3
- package/lib/cli/terminal/colors.mjs +1 -1
- package/lib/config/database-materialization.mjs +25 -0
- package/lib/config/database.mjs +30 -0
- package/lib/config/index.mjs +47 -1
- package/lib/config/runtime.mjs +130 -0
- package/lib/config-api/index.d.ts +28 -0
- package/lib/config-api/index.mjs +6 -0
- package/lib/database/cleanup.mjs +76 -1
- package/lib/database/constants.mjs +3 -0
- package/lib/database/index.mjs +6 -0
- package/lib/database/local-postgres.mjs +123 -4
- package/lib/database/naming.mjs +7 -0
- package/lib/database/resource-postgres.mjs +13 -0
- package/lib/database/state-files.mjs +17 -0
- package/lib/docker-compat/matrix.mjs +5 -3
- package/lib/kiln/client.mjs +8 -0
- package/lib/local/kiln-driver.mjs +96 -68
- package/lib/ownership/docker.mjs +67 -1
- package/lib/regressions/github-transport.mjs +178 -4
- package/lib/regressions/github.mjs +52 -16
- package/lib/regressions/index.d.ts +58 -29
- package/lib/regressions/index.mjs +171 -58
- package/lib/regressions/workflow.mjs +266 -0
- package/lib/results/artifacts.mjs +8 -7
- package/lib/runner/formatting.mjs +17 -16
- package/lib/runner/orchestrator.mjs +6 -5
- package/lib/runner/planning.mjs +40 -0
- package/lib/runner/regressions.mjs +183 -33
- package/lib/runner/reporting.mjs +1 -1
- package/lib/runner/run-finalization.mjs +34 -4
- package/lib/runner/runtime-manager.mjs +91 -10
- package/lib/runner/scheduler/index.mjs +30 -1
- package/lib/runtime/index.d.ts +5 -5
- package/lib/runtime-src/k6/http.js +11 -11
- 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/node_modules/es-toolkit/CHANGELOG.md +801 -0
- package/node_modules/es-toolkit/src/compat/_internal/Equals.d.ts +1 -0
- package/node_modules/es-toolkit/src/compat/_internal/IsWritable.d.ts +3 -0
- package/node_modules/es-toolkit/src/compat/_internal/MutableList.d.ts +4 -0
- package/node_modules/es-toolkit/src/compat/_internal/RejectReadonly.d.ts +4 -0
- package/node_modules/esprima/ChangeLog +235 -0
- package/package.json +6 -5
- package/packages/testkit-bridge/node_modules/@elench/testkit-protocol/dist/index.d.ts +0 -188
- package/packages/testkit-bridge/node_modules/@elench/testkit-protocol/dist/index.d.ts.map +0 -1
- package/packages/testkit-bridge/node_modules/@elench/testkit-protocol/dist/index.js +0 -293
- package/packages/testkit-bridge/node_modules/@elench/testkit-protocol/dist/index.js.map +0 -1
- package/packages/testkit-bridge/node_modules/@elench/testkit-protocol/package.json +0 -25
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { findMatchingRegressionCases } from "./index.mjs";
|
|
2
2
|
import {
|
|
3
3
|
applyStaleCacheFallback,
|
|
4
4
|
loadIssueCache,
|
|
@@ -40,11 +40,13 @@ export function normalizeRegressionSyncConfig(value) {
|
|
|
40
40
|
value.cacheTtlSeconds == null
|
|
41
41
|
? DEFAULT_CACHE_TTL_SECONDS
|
|
42
42
|
: normalizePositiveInteger(value.cacheTtlSeconds, "testkit.config.ts regressions.sync.cacheTtlSeconds");
|
|
43
|
+
const apiBaseUrl = normalizeOptionalString(value.apiBaseUrl);
|
|
43
44
|
|
|
44
45
|
return {
|
|
45
46
|
provider,
|
|
46
47
|
mode,
|
|
47
48
|
cacheTtlSeconds,
|
|
49
|
+
apiBaseUrl,
|
|
48
50
|
};
|
|
49
51
|
}
|
|
50
52
|
|
|
@@ -62,7 +64,8 @@ export async function validateRegressionIssues({
|
|
|
62
64
|
if (!document || !normalizedConfig || normalizedConfig.mode === "off") return null;
|
|
63
65
|
|
|
64
66
|
const checks = collectObservedRegressionEntries(document, runArtifact, statusArtifact);
|
|
65
|
-
const
|
|
67
|
+
const cases = document.cases || [];
|
|
68
|
+
const issueNumbersByRepo = groupIssueNumbersByRepo(cases);
|
|
66
69
|
const repoSlug = gitMetadata?.repoSlug || null;
|
|
67
70
|
const remoteUrl = gitMetadata?.remoteUrl || null;
|
|
68
71
|
const cache = loadIssueCache(productDir, CACHE_PATH, CACHE_SCHEMA_VERSION);
|
|
@@ -71,7 +74,9 @@ export async function validateRegressionIssues({
|
|
|
71
74
|
let issuesByRepo = cacheResolution.issuesByRepo;
|
|
72
75
|
|
|
73
76
|
if (cacheResolution.missingByRepo.size > 0) {
|
|
74
|
-
const client = transport || (await createDefaultGitHubIssueTransport(
|
|
77
|
+
const client = transport || (await createDefaultGitHubIssueTransport(process.env, {
|
|
78
|
+
apiBaseUrl: normalizedConfig.apiBaseUrl,
|
|
79
|
+
}));
|
|
75
80
|
if (client) {
|
|
76
81
|
try {
|
|
77
82
|
const fetchedIssues = await fetchIssuesByRepo(client, cacheResolution.missingByRepo);
|
|
@@ -105,9 +110,11 @@ export async function validateRegressionIssues({
|
|
|
105
110
|
}
|
|
106
111
|
}
|
|
107
112
|
|
|
108
|
-
const
|
|
113
|
+
const caseResults = cases.map((entry) => {
|
|
109
114
|
const observed = checks.get(entry.id) || createObservedCheck(entry);
|
|
110
|
-
const issueData =
|
|
115
|
+
const issueData = entry.issue
|
|
116
|
+
? issuesByRepo.get(entry.issue.repo)?.get(entry.issue.number) || null
|
|
117
|
+
: null;
|
|
111
118
|
return buildIssueValidationEntry({ entry, observed, issueData });
|
|
112
119
|
});
|
|
113
120
|
|
|
@@ -117,12 +124,12 @@ export async function validateRegressionIssues({
|
|
|
117
124
|
code: "detected_repo_mismatch",
|
|
118
125
|
severity: normalizedConfig.mode === "error" ? "error" : "warning",
|
|
119
126
|
message:
|
|
120
|
-
`Regression
|
|
127
|
+
`Regression case store issueRepo ${document.issueRepo} does not match detected repo ${repoSlug}` +
|
|
121
128
|
(remoteUrl ? ` (${remoteUrl})` : ""),
|
|
122
129
|
});
|
|
123
130
|
}
|
|
124
131
|
|
|
125
|
-
const summary = buildIssueValidationSummary(
|
|
132
|
+
const summary = buildIssueValidationSummary(caseResults, globalFindings);
|
|
126
133
|
return {
|
|
127
134
|
schemaVersion: 2,
|
|
128
135
|
provider: "github",
|
|
@@ -139,7 +146,7 @@ export async function validateRegressionIssues({
|
|
|
139
146
|
},
|
|
140
147
|
findings: globalFindings,
|
|
141
148
|
summary,
|
|
142
|
-
|
|
149
|
+
cases: caseResults,
|
|
143
150
|
};
|
|
144
151
|
}
|
|
145
152
|
|
|
@@ -182,17 +189,18 @@ export function buildRegressionSyncSummaryLines(result) {
|
|
|
182
189
|
if (parts.length === 0) return [];
|
|
183
190
|
return [
|
|
184
191
|
"",
|
|
185
|
-
"
|
|
192
|
+
"Case store issues:",
|
|
186
193
|
...parts.map((part) => ` - ${part}`),
|
|
187
194
|
];
|
|
188
195
|
}
|
|
189
196
|
|
|
190
197
|
function collectObservedRegressionEntries(document, runArtifact, statusArtifact) {
|
|
191
198
|
const observedTests = collectObservedTests(runArtifact, statusArtifact);
|
|
192
|
-
const
|
|
199
|
+
const cases = document.cases || [];
|
|
200
|
+
const checks = new Map(cases.map((entry) => [entry.id, createObservedCheck(entry)]));
|
|
193
201
|
|
|
194
202
|
for (const test of observedTests) {
|
|
195
|
-
const matchedEntries =
|
|
203
|
+
const matchedEntries = findMatchingRegressionCases(document, test);
|
|
196
204
|
for (const entry of matchedEntries) {
|
|
197
205
|
const observed = checks.get(entry.id) || createObservedCheck(entry);
|
|
198
206
|
observed.matchedTests += 1;
|
|
@@ -252,6 +260,7 @@ function createObservedCheck(entry) {
|
|
|
252
260
|
function groupIssueNumbersByRepo(entries) {
|
|
253
261
|
const map = new Map();
|
|
254
262
|
for (const entry of entries) {
|
|
263
|
+
if (!entry.issue) continue;
|
|
255
264
|
if (!map.has(entry.issue.repo)) {
|
|
256
265
|
map.set(entry.issue.repo, []);
|
|
257
266
|
}
|
|
@@ -271,11 +280,38 @@ function buildIssueValidationEntry({ entry, observed, issueData }) {
|
|
|
271
280
|
const findings = [];
|
|
272
281
|
const normalizedIssueState = normalizeIssueState(issueData?.state);
|
|
273
282
|
|
|
283
|
+
if (!entry.issue) {
|
|
284
|
+
return {
|
|
285
|
+
id: entry.id,
|
|
286
|
+
summary: entry.summary,
|
|
287
|
+
issue: null,
|
|
288
|
+
observed: {
|
|
289
|
+
matchedTests: observed.matchedTests,
|
|
290
|
+
executedTests: observed.executedTests,
|
|
291
|
+
passedTests: observed.passedTests,
|
|
292
|
+
failedTests: observed.failedTests,
|
|
293
|
+
skippedTests: observed.skippedTests,
|
|
294
|
+
notRunTests: observed.notRunTests,
|
|
295
|
+
reproduced: observed.failedTests > 0,
|
|
296
|
+
},
|
|
297
|
+
github: {
|
|
298
|
+
exists: null,
|
|
299
|
+
title: null,
|
|
300
|
+
state: null,
|
|
301
|
+
url: null,
|
|
302
|
+
checkedAt: null,
|
|
303
|
+
cached: false,
|
|
304
|
+
},
|
|
305
|
+
findings,
|
|
306
|
+
status: observed.matchedTests > 0 ? "unlinked" : "not_observed",
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
|
|
274
310
|
if (issueData && issueData.exists === false) {
|
|
275
311
|
findings.push({
|
|
276
312
|
code: "issue_missing",
|
|
277
313
|
severity: "error",
|
|
278
|
-
message: `Regression
|
|
314
|
+
message: `Regression case ${entry.id} references missing issue #${entry.issue.number} in ${entry.issue.repo}`,
|
|
279
315
|
});
|
|
280
316
|
}
|
|
281
317
|
|
|
@@ -369,7 +405,7 @@ function resolveIssueValidationStatus(issueData, observed) {
|
|
|
369
405
|
return "not_observed";
|
|
370
406
|
}
|
|
371
407
|
|
|
372
|
-
function buildIssueValidationSummary(
|
|
408
|
+
function buildIssueValidationSummary(caseResults, globalFindings) {
|
|
373
409
|
const byCode = {};
|
|
374
410
|
const byStatus = {};
|
|
375
411
|
let errors = 0;
|
|
@@ -381,7 +417,7 @@ function buildIssueValidationSummary(entries, globalFindings) {
|
|
|
381
417
|
if (finding.severity === "warning") warnings += 1;
|
|
382
418
|
}
|
|
383
419
|
|
|
384
|
-
for (const entry of
|
|
420
|
+
for (const entry of caseResults) {
|
|
385
421
|
byStatus[entry.status] = (byStatus[entry.status] || 0) + 1;
|
|
386
422
|
for (const finding of entry.findings) {
|
|
387
423
|
byCode[finding.code] = (byCode[finding.code] || 0) + 1;
|
|
@@ -391,8 +427,8 @@ function buildIssueValidationSummary(entries, globalFindings) {
|
|
|
391
427
|
}
|
|
392
428
|
|
|
393
429
|
return {
|
|
394
|
-
|
|
395
|
-
|
|
430
|
+
cases: caseResults.length,
|
|
431
|
+
observedCases: caseResults.filter((entry) => entry.observed.matchedTests > 0).length,
|
|
396
432
|
errors,
|
|
397
433
|
warnings,
|
|
398
434
|
byCode,
|
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
export type RegressionClassification =
|
|
2
2
|
| "expected_failure"
|
|
3
|
+
| "flaky"
|
|
4
|
+
| "harness_bug"
|
|
3
5
|
| "infra"
|
|
4
6
|
| "product_bug"
|
|
5
7
|
| "stale_test"
|
|
6
8
|
| "test_bug";
|
|
7
9
|
|
|
10
|
+
export type RegressionCaseState =
|
|
11
|
+
| "active"
|
|
12
|
+
| "coverage_missing"
|
|
13
|
+
| "fixed_pending_verification"
|
|
14
|
+
| "fix_claimed"
|
|
15
|
+
| "resolved"
|
|
16
|
+
| "untriaged";
|
|
17
|
+
|
|
8
18
|
export interface RegressionIssueRef {
|
|
9
19
|
repo: string;
|
|
10
20
|
number: number;
|
|
@@ -13,32 +23,37 @@ export interface RegressionIssueRef {
|
|
|
13
23
|
export interface RegressionFingerprint {
|
|
14
24
|
service?: string;
|
|
15
25
|
type?: string;
|
|
16
|
-
path
|
|
26
|
+
path?: string;
|
|
27
|
+
pathGlob?: string;
|
|
17
28
|
failureKey?: string;
|
|
18
29
|
errorIncludes?: string;
|
|
19
30
|
}
|
|
20
31
|
|
|
21
|
-
export interface
|
|
32
|
+
export interface RegressionCase {
|
|
22
33
|
id: string;
|
|
34
|
+
state: RegressionCaseState;
|
|
23
35
|
classification: RegressionClassification;
|
|
24
|
-
|
|
36
|
+
owner?: string;
|
|
37
|
+
issue: RegressionIssueRef | null;
|
|
25
38
|
summary: string;
|
|
26
39
|
cause: string;
|
|
27
40
|
lastReviewedAt: string;
|
|
28
41
|
fingerprints: RegressionFingerprint[];
|
|
42
|
+
lifecycle: Record<string, unknown>;
|
|
43
|
+
coverage: Record<string, unknown>;
|
|
29
44
|
}
|
|
30
45
|
|
|
31
|
-
export interface
|
|
32
|
-
schemaVersion:
|
|
46
|
+
export interface RegressionCaseStoreDocument {
|
|
47
|
+
schemaVersion: 2;
|
|
33
48
|
issueRepo?: string | null;
|
|
34
|
-
|
|
49
|
+
cases: RegressionCase[];
|
|
35
50
|
}
|
|
36
51
|
|
|
37
|
-
export interface
|
|
52
|
+
export interface RegressionCaseStoreValidationResult {
|
|
38
53
|
errors: string[];
|
|
39
54
|
warnings: string[];
|
|
40
55
|
stats: {
|
|
41
|
-
|
|
56
|
+
cases: number;
|
|
42
57
|
fingerprints: number;
|
|
43
58
|
failedTests: number;
|
|
44
59
|
diagnosedFailedTests: number;
|
|
@@ -50,6 +65,7 @@ export interface RegressionSyncConfig {
|
|
|
50
65
|
provider?: "github";
|
|
51
66
|
mode?: "off" | "warn" | "error";
|
|
52
67
|
cacheTtlSeconds?: number;
|
|
68
|
+
apiBaseUrl?: string;
|
|
53
69
|
}
|
|
54
70
|
|
|
55
71
|
export interface RegressionSyncFinding {
|
|
@@ -61,7 +77,7 @@ export interface RegressionSyncFinding {
|
|
|
61
77
|
export interface RegressionSyncEntry {
|
|
62
78
|
id: string;
|
|
63
79
|
summary: string;
|
|
64
|
-
issue: RegressionIssueRef;
|
|
80
|
+
issue: RegressionIssueRef | null;
|
|
65
81
|
observed: {
|
|
66
82
|
matchedTests: number;
|
|
67
83
|
executedTests: number;
|
|
@@ -99,41 +115,52 @@ export interface RegressionSyncResult {
|
|
|
99
115
|
};
|
|
100
116
|
findings: RegressionSyncFinding[];
|
|
101
117
|
summary: {
|
|
102
|
-
|
|
103
|
-
|
|
118
|
+
cases: number;
|
|
119
|
+
observedCases: number;
|
|
104
120
|
errors: number;
|
|
105
121
|
warnings: number;
|
|
106
122
|
byCode: Record<string, number>;
|
|
107
123
|
byStatus: Record<string, number>;
|
|
108
124
|
};
|
|
109
|
-
|
|
125
|
+
cases: RegressionSyncEntry[];
|
|
110
126
|
}
|
|
111
127
|
|
|
112
|
-
export declare function
|
|
128
|
+
export declare function loadRegressionCaseStoreConfig(
|
|
113
129
|
productDir: string,
|
|
114
130
|
config: { file?: string | null } | null
|
|
115
|
-
):
|
|
116
|
-
export declare function
|
|
131
|
+
): RegressionCaseStoreDocument | null;
|
|
132
|
+
export declare function loadRegressionCaseStoreDocument(
|
|
117
133
|
filePath: string,
|
|
118
134
|
relativePath?: string
|
|
119
|
-
):
|
|
120
|
-
export declare function
|
|
135
|
+
): RegressionCaseStoreDocument;
|
|
136
|
+
export declare function writeRegressionCaseStoreDocument(
|
|
137
|
+
filePath: string,
|
|
138
|
+
document: RegressionCaseStoreDocument
|
|
139
|
+
): void;
|
|
140
|
+
export declare function serializeRegressionCaseStore(
|
|
141
|
+
document: RegressionCaseStoreDocument
|
|
142
|
+
): {
|
|
143
|
+
schemaVersion: 2;
|
|
144
|
+
issueRepo?: string | null;
|
|
145
|
+
cases: RegressionCase[];
|
|
146
|
+
};
|
|
147
|
+
export declare function normalizeRegressionCaseStoreDocument(
|
|
121
148
|
document: unknown,
|
|
122
149
|
relativePath?: string
|
|
123
|
-
):
|
|
124
|
-
export declare function
|
|
125
|
-
document:
|
|
150
|
+
): RegressionCaseStoreDocument;
|
|
151
|
+
export declare function validateRegressionCaseStoreDocument(
|
|
152
|
+
document: RegressionCaseStoreDocument,
|
|
126
153
|
options?: {
|
|
127
154
|
productDir?: string;
|
|
128
155
|
statusArtifactPath?: string;
|
|
129
156
|
statusArtifact?: unknown;
|
|
130
157
|
}
|
|
131
|
-
):
|
|
132
|
-
export declare function
|
|
133
|
-
document:
|
|
158
|
+
): RegressionCaseStoreValidationResult;
|
|
159
|
+
export declare function renderRegressionCaseStoreMarkdown(
|
|
160
|
+
document: RegressionCaseStoreDocument
|
|
134
161
|
): string;
|
|
135
|
-
export declare function
|
|
136
|
-
document:
|
|
162
|
+
export declare function findMatchingRegressionCases(
|
|
163
|
+
document: RegressionCaseStoreDocument,
|
|
137
164
|
fileSummary: {
|
|
138
165
|
service?: string;
|
|
139
166
|
type?: string;
|
|
@@ -142,9 +169,9 @@ export declare function findMatchingRegressionEntries(
|
|
|
142
169
|
error?: string | null;
|
|
143
170
|
failureDetails?: Array<{ key?: string; title?: string }>;
|
|
144
171
|
}
|
|
145
|
-
):
|
|
146
|
-
export declare function
|
|
147
|
-
entry:
|
|
172
|
+
): RegressionCase[];
|
|
173
|
+
export declare function matchesRegressionCase(
|
|
174
|
+
entry: RegressionCase,
|
|
148
175
|
fileSummary: {
|
|
149
176
|
service?: string;
|
|
150
177
|
type?: string;
|
|
@@ -158,6 +185,7 @@ export declare function buildRegressionFileIdentity(
|
|
|
158
185
|
type: string,
|
|
159
186
|
filePath: string
|
|
160
187
|
): string;
|
|
188
|
+
export declare function issueRefToString(issue: RegressionIssueRef | null): string | null;
|
|
161
189
|
|
|
162
190
|
export declare function normalizeRegressionSyncConfig(
|
|
163
191
|
value: unknown
|
|
@@ -165,10 +193,11 @@ export declare function normalizeRegressionSyncConfig(
|
|
|
165
193
|
provider: "github";
|
|
166
194
|
mode: "off" | "warn" | "error";
|
|
167
195
|
cacheTtlSeconds: number;
|
|
196
|
+
apiBaseUrl: string | null;
|
|
168
197
|
} | null;
|
|
169
198
|
export declare function validateRegressionIssues(options: {
|
|
170
199
|
productDir: string;
|
|
171
|
-
document:
|
|
200
|
+
document: RegressionCaseStoreDocument | null;
|
|
172
201
|
runArtifact?: unknown;
|
|
173
202
|
statusArtifact?: unknown;
|
|
174
203
|
config?: RegressionSyncConfig | null;
|