@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.
Files changed (60) hide show
  1. package/README.md +29 -9
  2. package/lib/cli/assistant/view-model.mjs +1 -1
  3. package/lib/cli/components/blocks/run-tree.mjs +1 -1
  4. package/lib/cli/renderers/run/events.mjs +4 -3
  5. package/lib/cli/renderers/run/failure.mjs +2 -2
  6. package/lib/cli/renderers/run/inline-detail.mjs +2 -2
  7. package/lib/cli/renderers/run/interactive.mjs +2 -2
  8. package/lib/cli/renderers/run/text-reporter.mjs +9 -9
  9. package/lib/cli/state/run/model.mjs +7 -7
  10. package/lib/cli/state/run/state.mjs +3 -3
  11. package/lib/cli/terminal/colors.mjs +1 -1
  12. package/lib/config/database-materialization.mjs +25 -0
  13. package/lib/config/database.mjs +30 -0
  14. package/lib/config/index.mjs +47 -1
  15. package/lib/config/runtime.mjs +130 -0
  16. package/lib/config-api/index.d.ts +28 -0
  17. package/lib/config-api/index.mjs +6 -0
  18. package/lib/database/cleanup.mjs +76 -1
  19. package/lib/database/constants.mjs +3 -0
  20. package/lib/database/index.mjs +6 -0
  21. package/lib/database/local-postgres.mjs +123 -4
  22. package/lib/database/naming.mjs +7 -0
  23. package/lib/database/resource-postgres.mjs +13 -0
  24. package/lib/database/state-files.mjs +17 -0
  25. package/lib/docker-compat/matrix.mjs +5 -3
  26. package/lib/kiln/client.mjs +8 -0
  27. package/lib/local/kiln-driver.mjs +96 -68
  28. package/lib/ownership/docker.mjs +67 -1
  29. package/lib/regressions/github-transport.mjs +178 -4
  30. package/lib/regressions/github.mjs +52 -16
  31. package/lib/regressions/index.d.ts +58 -29
  32. package/lib/regressions/index.mjs +171 -58
  33. package/lib/regressions/workflow.mjs +266 -0
  34. package/lib/results/artifacts.mjs +8 -7
  35. package/lib/runner/formatting.mjs +17 -16
  36. package/lib/runner/orchestrator.mjs +6 -5
  37. package/lib/runner/planning.mjs +40 -0
  38. package/lib/runner/regressions.mjs +183 -33
  39. package/lib/runner/reporting.mjs +1 -1
  40. package/lib/runner/run-finalization.mjs +34 -4
  41. package/lib/runner/runtime-manager.mjs +91 -10
  42. package/lib/runner/scheduler/index.mjs +30 -1
  43. package/lib/runtime/index.d.ts +5 -5
  44. package/lib/runtime-src/k6/http.js +11 -11
  45. package/node_modules/@elench/next-analysis/package.json +1 -1
  46. package/node_modules/@elench/testkit-bridge/package.json +2 -2
  47. package/node_modules/@elench/testkit-protocol/package.json +1 -1
  48. package/node_modules/@elench/ts-analysis/package.json +1 -1
  49. package/node_modules/es-toolkit/CHANGELOG.md +801 -0
  50. package/node_modules/es-toolkit/src/compat/_internal/Equals.d.ts +1 -0
  51. package/node_modules/es-toolkit/src/compat/_internal/IsWritable.d.ts +3 -0
  52. package/node_modules/es-toolkit/src/compat/_internal/MutableList.d.ts +4 -0
  53. package/node_modules/es-toolkit/src/compat/_internal/RejectReadonly.d.ts +4 -0
  54. package/node_modules/esprima/ChangeLog +235 -0
  55. package/package.json +6 -5
  56. package/packages/testkit-bridge/node_modules/@elench/testkit-protocol/dist/index.d.ts +0 -188
  57. package/packages/testkit-bridge/node_modules/@elench/testkit-protocol/dist/index.d.ts.map +0 -1
  58. package/packages/testkit-bridge/node_modules/@elench/testkit-protocol/dist/index.js +0 -293
  59. package/packages/testkit-bridge/node_modules/@elench/testkit-protocol/dist/index.js.map +0 -1
  60. package/packages/testkit-bridge/node_modules/@elench/testkit-protocol/package.json +0 -25
@@ -1,4 +1,4 @@
1
- import { findMatchingRegressionEntries } from "./index.mjs";
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 issueNumbersByRepo = groupIssueNumbersByRepo(document.entries);
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 entries = document.entries.map((entry) => {
113
+ const caseResults = cases.map((entry) => {
109
114
  const observed = checks.get(entry.id) || createObservedCheck(entry);
110
- const issueData = issuesByRepo.get(entry.issue.repo)?.get(entry.issue.number) || null;
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 catalog issueRepo ${document.issueRepo} does not match detected repo ${repoSlug}` +
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(entries, globalFindings);
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
- entries,
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
- "Catalog issues:",
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 checks = new Map(document.entries.map((entry) => [entry.id, createObservedCheck(entry)]));
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 = findMatchingRegressionEntries(document, test);
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 catalog entry ${entry.id} references missing issue #${entry.issue.number} in ${entry.issue.repo}`,
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(entries, globalFindings) {
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 entries) {
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
- entries: entries.length,
395
- observedEntries: entries.filter((entry) => entry.observed.matchedTests > 0).length,
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: string;
26
+ path?: string;
27
+ pathGlob?: string;
17
28
  failureKey?: string;
18
29
  errorIncludes?: string;
19
30
  }
20
31
 
21
- export interface RegressionCatalogEntry {
32
+ export interface RegressionCase {
22
33
  id: string;
34
+ state: RegressionCaseState;
23
35
  classification: RegressionClassification;
24
- issue: RegressionIssueRef;
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 RegressionCatalogDocument {
32
- schemaVersion: 1;
46
+ export interface RegressionCaseStoreDocument {
47
+ schemaVersion: 2;
33
48
  issueRepo?: string | null;
34
- entries: RegressionCatalogEntry[];
49
+ cases: RegressionCase[];
35
50
  }
36
51
 
37
- export interface RegressionCatalogValidationResult {
52
+ export interface RegressionCaseStoreValidationResult {
38
53
  errors: string[];
39
54
  warnings: string[];
40
55
  stats: {
41
- entries: number;
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
- entries: number;
103
- observedEntries: number;
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
- entries: RegressionSyncEntry[];
125
+ cases: RegressionSyncEntry[];
110
126
  }
111
127
 
112
- export declare function loadRegressionCatalogConfig(
128
+ export declare function loadRegressionCaseStoreConfig(
113
129
  productDir: string,
114
130
  config: { file?: string | null } | null
115
- ): RegressionCatalogDocument | null;
116
- export declare function loadRegressionCatalogDocument(
131
+ ): RegressionCaseStoreDocument | null;
132
+ export declare function loadRegressionCaseStoreDocument(
117
133
  filePath: string,
118
134
  relativePath?: string
119
- ): RegressionCatalogDocument;
120
- export declare function normalizeRegressionCatalogDocument(
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
- ): RegressionCatalogDocument;
124
- export declare function validateRegressionCatalogDocument(
125
- document: RegressionCatalogDocument,
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
- ): RegressionCatalogValidationResult;
132
- export declare function renderRegressionCatalogMarkdown(
133
- document: RegressionCatalogDocument
158
+ ): RegressionCaseStoreValidationResult;
159
+ export declare function renderRegressionCaseStoreMarkdown(
160
+ document: RegressionCaseStoreDocument
134
161
  ): string;
135
- export declare function findMatchingRegressionEntries(
136
- document: RegressionCatalogDocument,
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
- ): RegressionCatalogEntry[];
146
- export declare function matchesRegressionEntry(
147
- entry: RegressionCatalogEntry,
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: RegressionCatalogDocument | null;
200
+ document: RegressionCaseStoreDocument | null;
172
201
  runArtifact?: unknown;
173
202
  statusArtifact?: unknown;
174
203
  config?: RegressionSyncConfig | null;