assertloom 0.0.0-stage → 0.1.0

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 (126) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +346 -2
  3. package/dist/ai/index.d.ts +5 -0
  4. package/dist/ai/index.js +14 -0
  5. package/dist/ai/index.js.map +1 -0
  6. package/dist/ai/openai.d.ts +9 -0
  7. package/dist/ai/openai.js +57 -0
  8. package/dist/ai/openai.js.map +1 -0
  9. package/dist/ai/pricing.d.ts +27 -0
  10. package/dist/ai/pricing.js +76 -0
  11. package/dist/ai/pricing.js.map +1 -0
  12. package/dist/ai/prompts.d.ts +27 -0
  13. package/dist/ai/prompts.js +89 -0
  14. package/dist/ai/prompts.js.map +1 -0
  15. package/dist/ai/provider.d.ts +26 -0
  16. package/dist/ai/provider.js +4 -0
  17. package/dist/ai/provider.js.map +1 -0
  18. package/dist/ai/redact.d.ts +5 -0
  19. package/dist/ai/redact.js +31 -0
  20. package/dist/ai/redact.js.map +1 -0
  21. package/dist/analyzer/dependencies.d.ts +16 -0
  22. package/dist/analyzer/dependencies.js +168 -0
  23. package/dist/analyzer/dependencies.js.map +1 -0
  24. package/dist/analyzer/format.d.ts +3 -0
  25. package/dist/analyzer/format.js +14 -0
  26. package/dist/analyzer/format.js.map +1 -0
  27. package/dist/analyzer/functions.d.ts +4 -0
  28. package/dist/analyzer/functions.js +274 -0
  29. package/dist/analyzer/functions.js.map +1 -0
  30. package/dist/analyzer/index.d.ts +5 -0
  31. package/dist/analyzer/index.js +29 -0
  32. package/dist/analyzer/index.js.map +1 -0
  33. package/dist/analyzer/parser.d.ts +11 -0
  34. package/dist/analyzer/parser.js +38 -0
  35. package/dist/analyzer/parser.js.map +1 -0
  36. package/dist/analyzer/usage.d.ts +16 -0
  37. package/dist/analyzer/usage.js +159 -0
  38. package/dist/analyzer/usage.js.map +1 -0
  39. package/dist/cli/commands/analyze.d.ts +2 -0
  40. package/dist/cli/commands/analyze.js +49 -0
  41. package/dist/cli/commands/analyze.js.map +1 -0
  42. package/dist/cli/commands/generate.d.ts +3 -0
  43. package/dist/cli/commands/generate.js +200 -0
  44. package/dist/cli/commands/generate.js.map +1 -0
  45. package/dist/cli/commands/run.d.ts +4 -0
  46. package/dist/cli/commands/run.js +53 -0
  47. package/dist/cli/commands/run.js.map +1 -0
  48. package/dist/cli/index.d.ts +2 -0
  49. package/dist/cli/index.js +16 -0
  50. package/dist/cli/index.js.map +1 -0
  51. package/dist/cli/program.d.ts +2 -0
  52. package/dist/cli/program.js +18 -0
  53. package/dist/cli/program.js.map +1 -0
  54. package/dist/cli/report.d.ts +3 -0
  55. package/dist/cli/report.js +36 -0
  56. package/dist/cli/report.js.map +1 -0
  57. package/dist/cli/validateFile.d.ts +5 -0
  58. package/dist/cli/validateFile.js +43 -0
  59. package/dist/cli/validateFile.js.map +1 -0
  60. package/dist/errors.d.ts +4 -0
  61. package/dist/errors.js +8 -0
  62. package/dist/errors.js.map +1 -0
  63. package/dist/generator/diff.d.ts +5 -0
  64. package/dist/generator/diff.js +56 -0
  65. package/dist/generator/diff.js.map +1 -0
  66. package/dist/generator/importPlan.d.ts +15 -0
  67. package/dist/generator/importPlan.js +62 -0
  68. package/dist/generator/importPlan.js.map +1 -0
  69. package/dist/generator/index.d.ts +5 -0
  70. package/dist/generator/index.js +6 -0
  71. package/dist/generator/index.js.map +1 -0
  72. package/dist/generator/mocks.d.ts +48 -0
  73. package/dist/generator/mocks.js +356 -0
  74. package/dist/generator/mocks.js.map +1 -0
  75. package/dist/generator/schema.d.ts +40 -0
  76. package/dist/generator/schema.js +15 -0
  77. package/dist/generator/schema.js.map +1 -0
  78. package/dist/generator/testGenerator.d.ts +33 -0
  79. package/dist/generator/testGenerator.js +80 -0
  80. package/dist/generator/testGenerator.js.map +1 -0
  81. package/dist/generator/testWriter.d.ts +22 -0
  82. package/dist/generator/testWriter.js +93 -0
  83. package/dist/generator/testWriter.js.map +1 -0
  84. package/dist/generator/validate.d.ts +31 -0
  85. package/dist/generator/validate.js +180 -0
  86. package/dist/generator/validate.js.map +1 -0
  87. package/dist/repair/classifyFailure.d.ts +11 -0
  88. package/dist/repair/classifyFailure.js +26 -0
  89. package/dist/repair/classifyFailure.js.map +1 -0
  90. package/dist/repair/index.d.ts +2 -0
  91. package/dist/repair/index.js +3 -0
  92. package/dist/repair/index.js.map +1 -0
  93. package/dist/repair/repairLoop.d.ts +74 -0
  94. package/dist/repair/repairLoop.js +219 -0
  95. package/dist/repair/repairLoop.js.map +1 -0
  96. package/dist/repair/schema.d.ts +17 -0
  97. package/dist/repair/schema.js +14 -0
  98. package/dist/repair/schema.js.map +1 -0
  99. package/dist/runner/coverage.d.ts +5 -0
  100. package/dist/runner/coverage.js +42 -0
  101. package/dist/runner/coverage.js.map +1 -0
  102. package/dist/runner/index.d.ts +4 -0
  103. package/dist/runner/index.js +4 -0
  104. package/dist/runner/index.js.map +1 -0
  105. package/dist/runner/jestConfig.d.ts +11 -0
  106. package/dist/runner/jestConfig.js +103 -0
  107. package/dist/runner/jestConfig.js.map +1 -0
  108. package/dist/runner/jestRunner.d.ts +14 -0
  109. package/dist/runner/jestRunner.js +140 -0
  110. package/dist/runner/jestRunner.js.map +1 -0
  111. package/dist/runner/networkGuard.d.ts +4 -0
  112. package/dist/runner/networkGuard.js +55 -0
  113. package/dist/runner/networkGuard.js.map +1 -0
  114. package/dist/runner/project.d.ts +30 -0
  115. package/dist/runner/project.js +86 -0
  116. package/dist/runner/project.js.map +1 -0
  117. package/dist/runner/results.d.ts +32 -0
  118. package/dist/runner/results.js +58 -0
  119. package/dist/runner/results.js.map +1 -0
  120. package/dist/runner/types.d.ts +39 -0
  121. package/dist/runner/types.js +2 -0
  122. package/dist/runner/types.js.map +1 -0
  123. package/dist/types/index.d.ts +95 -0
  124. package/dist/types/index.js +2 -0
  125. package/dist/types/index.js.map +1 -0
  126. package/package.json +66 -4
@@ -0,0 +1,80 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { GENERATION_SYSTEM_PROMPT, buildGenerationPrompt } from "../ai/prompts.js";
4
+ import { formatUsd, promptTokens } from "../ai/pricing.js";
5
+ import { redactSecrets } from "../ai/redact.js";
6
+ import { analyzeSource } from "../analyzer/index.js";
7
+ import { UserError } from "../errors.js";
8
+ import { planImports } from "./importPlan.js";
9
+ import { planMocks } from "./mocks.js";
10
+ import { GenerationSchema } from "./schema.js";
11
+ import { previewPathFor, renderTestFile } from "./testWriter.js";
12
+ import { validateGeneration } from "./validate.js";
13
+ export class GenerationError extends UserError {
14
+ }
15
+ const MAX_SOURCE_CHARS = 60_000;
16
+ const MAX_OUTPUT_TOKENS = 32_000;
17
+ export async function generateTests(options) {
18
+ const { source, provider } = options;
19
+ const testPath = options.testPath ?? previewPathFor(source.absolutePath);
20
+ const code = await readFile(source.absolutePath, "utf8");
21
+ const analysis = analyzeSource(code, source);
22
+ const targets = analysis.functions.filter((fn) => fn.exported);
23
+ if (targets.length === 0)
24
+ throw new GenerationError(`No exported functions in ${analysis.file}.`);
25
+ if (code.length > MAX_SOURCE_CHARS) {
26
+ throw new GenerationError(`${analysis.file} is ${code.length} characters; the limit for sending to a model is ${MAX_SOURCE_CHARS}.`);
27
+ }
28
+ const plan = planImports(analysis, targets, source.absolutePath, testPath);
29
+ const mocks = await planMocks({
30
+ analysis,
31
+ sourcePath: source.absolutePath,
32
+ testPath,
33
+ extension: source.extension,
34
+ reserved: plan.bindings.map((b) => b.binding),
35
+ mock: options.mock,
36
+ keepReal: options.keepReal,
37
+ });
38
+ const redacted = redactSecrets(code);
39
+ const prompt = buildGenerationPrompt({
40
+ analysis,
41
+ code: redacted.code,
42
+ bindings: plan.bindings,
43
+ testFileName: path.basename(testPath),
44
+ mockSummary: mocks.summary,
45
+ });
46
+ const { budget } = options;
47
+ if (budget && !budget.allows({ inputTokens: promptTokens(GENERATION_SYSTEM_PROMPT, prompt), outputTokens: MAX_OUTPUT_TOKENS })) {
48
+ throw new GenerationError(`Generating tests for ${analysis.file} could exceed the spending limit of ${formatUsd(budget.maxUsd)}; raise it with --max-cost.`);
49
+ }
50
+ const response = await provider.generateStructured({
51
+ system: GENERATION_SYSTEM_PROMPT,
52
+ prompt,
53
+ schema: GenerationSchema,
54
+ schemaName: "assertloom_tests",
55
+ maxOutputTokens: MAX_OUTPUT_TOKENS,
56
+ });
57
+ budget?.add(response.usage);
58
+ const { suites, rejected } = validateGeneration(response.data, plan.bindings, source.extension);
59
+ if (suites.length === 0) {
60
+ throw new GenerationError(`The model returned no usable test cases (${rejected.length} rejected).`);
61
+ }
62
+ const preamble = [...mocks.imports, ...plan.statements];
63
+ if (mocks.setup.length)
64
+ preamble.push("", mocks.setup.join("\n\n"));
65
+ const content = renderTestFile({ sourceFile: analysis.file, preamble, suites, extension: source.extension });
66
+ return {
67
+ analysis,
68
+ testPath,
69
+ content,
70
+ preamble,
71
+ mocks,
72
+ suites,
73
+ promptSource: redacted.code,
74
+ rejected,
75
+ model: response.model,
76
+ usage: response.usage,
77
+ redactions: redacted.redactions,
78
+ };
79
+ }
80
+ //# sourceMappingURL=testGenerator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testGenerator.js","sourceRoot":"","sources":["../../src/generator/testGenerator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import { UserError } from "../errors.js";
2
+ import type { SupportedExtension } from "../types/index.js";
3
+ import type { ValidatedSuite } from "./validate.js";
4
+ export declare const GENERATED_HEADER = "// Generated by Assertloom.";
5
+ export declare class TestFileExistsError extends UserError {
6
+ }
7
+ /** `src/math.ts` → `src/math.test.preview.ts`. Jest's default pattern ignores it until it has been run. */
8
+ export declare function previewPathFor(sourcePath: string): string;
9
+ /** `src/math.ts` → `src/math.test.ts`. */
10
+ export declare function testPathFor(sourcePath: string): string;
11
+ export declare function renderTestFile(options: {
12
+ sourceFile: string;
13
+ /** Mocks, imports, and setup that precede the describe blocks. */
14
+ preamble: string[];
15
+ suites: ValidatedSuite[];
16
+ extension: SupportedExtension;
17
+ }): string;
18
+ export declare function writeTestFile(filePath: string, content: string, force?: boolean,
19
+ /** Called with the changes before an existing file with different content is overwritten. */
20
+ onOverwrite?: (diff: string[]) => void): Promise<{
21
+ overwritten: boolean;
22
+ }>;
@@ -0,0 +1,93 @@
1
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { parse } from "@babel/parser";
4
+ import traverse from "@babel/traverse";
5
+ import { parseSource, pluginsFor } from "../analyzer/parser.js";
6
+ import { UserError } from "../errors.js";
7
+ import { unifiedDiff } from "./diff.js";
8
+ export const GENERATED_HEADER = "// Generated by Assertloom.";
9
+ export class TestFileExistsError extends UserError {
10
+ }
11
+ /** `src/math.ts` → `src/math.test.preview.ts`. Jest's default pattern ignores it until it has been run. */
12
+ export function previewPathFor(sourcePath) {
13
+ const ext = path.extname(sourcePath);
14
+ return path.join(path.dirname(sourcePath), `${path.basename(sourcePath, ext)}.test.preview${ext}`);
15
+ }
16
+ /** `src/math.ts` → `src/math.test.ts`. */
17
+ export function testPathFor(sourcePath) {
18
+ const ext = path.extname(sourcePath);
19
+ return path.join(path.dirname(sourcePath), `${path.basename(sourcePath, ext)}.test${ext}`);
20
+ }
21
+ /** Offsets inside template literals, where added indentation would change the string's value. */
22
+ function templateRanges(body, extension) {
23
+ const prefix = "async function __assertloomCase() {\n";
24
+ const ranges = [];
25
+ try {
26
+ const ast = parse(`${prefix}${body}\n}`, { sourceType: "module", plugins: pluginsFor(extension) });
27
+ traverse(ast, {
28
+ TemplateLiteral({ node }) {
29
+ if (node.start != null && node.end != null)
30
+ ranges.push([node.start - prefix.length, node.end - prefix.length]);
31
+ },
32
+ });
33
+ }
34
+ catch {
35
+ // Unparseable bodies are rejected later by parseSource; indent them naively.
36
+ }
37
+ return ranges;
38
+ }
39
+ function indent(text, spaces, extension) {
40
+ const pad = " ".repeat(spaces);
41
+ const ranges = templateRanges(text, extension);
42
+ let offset = 0;
43
+ return text
44
+ .split("\n")
45
+ .map((line) => {
46
+ const start = offset;
47
+ offset += line.length + 1;
48
+ if (ranges.some(([from, to]) => start > from && start < to))
49
+ return line;
50
+ const trimmed = line.replace(/\r$/, "");
51
+ return trimmed.trim() ? pad + trimmed : "";
52
+ })
53
+ .join("\n");
54
+ }
55
+ export function renderTestFile(options) {
56
+ const blocks = options.suites.map(({ binding, cases }) => {
57
+ const tests = cases.map((c) => {
58
+ const comment = c.skipReason ? ` // Assertloom skipped this test: ${c.skipReason.replace(/\s+/g, " ")}\n` : "";
59
+ const it = c.skipReason ? "it.skip" : "it";
60
+ return `${comment} ${it}(${JSON.stringify(c.name)}, ${c.async ? "async " : ""}() => {\n${indent(c.body, 4, options.extension)}\n });`;
61
+ });
62
+ return `describe(${JSON.stringify(binding.binding)}, () => {\n${tests.join("\n\n")}\n});`;
63
+ });
64
+ const content = [
65
+ `${GENERATED_HEADER} Source: ${options.sourceFile}. Review before committing.`,
66
+ ...options.preamble,
67
+ "",
68
+ blocks.join("\n\n"),
69
+ "",
70
+ ].join("\n");
71
+ parseSource(content, options.extension);
72
+ return content;
73
+ }
74
+ export async function writeTestFile(filePath, content, force = false,
75
+ /** Called with the changes before an existing file with different content is overwritten. */
76
+ onOverwrite) {
77
+ let existing;
78
+ try {
79
+ existing = await readFile(filePath, "utf8");
80
+ }
81
+ catch {
82
+ // File does not exist yet.
83
+ }
84
+ if (existing !== undefined && !existing.startsWith(GENERATED_HEADER) && !force) {
85
+ throw new TestFileExistsError(`${filePath} already exists and was not generated by Assertloom. Use --force to overwrite it.`);
86
+ }
87
+ if (existing !== undefined && existing !== content)
88
+ onOverwrite?.(unifiedDiff(existing, content));
89
+ await mkdir(path.dirname(filePath), { recursive: true });
90
+ await writeFile(filePath, content, "utf8");
91
+ return { overwritten: existing !== undefined };
92
+ }
93
+ //# sourceMappingURL=testWriter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testWriter.js","sourceRoot":"","sources":["../../src/generator/testWriter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import type { SupportedExtension } from "../types/index.js";
2
+ import type { Binding } from "./importPlan.js";
3
+ import type { GeneratedTestCase, Generation } from "./schema.js";
4
+ export declare const MAX_CASES_PER_FUNCTION = 12;
5
+ export interface AcceptedCase extends GeneratedTestCase {
6
+ async: boolean;
7
+ /** Set when the repair loop gives up on a case; rendered as `it.skip` with this reason. */
8
+ skipReason?: string;
9
+ }
10
+ export interface ValidatedSuite {
11
+ binding: Binding;
12
+ cases: AcceptedCase[];
13
+ }
14
+ export interface RejectedCase {
15
+ functionName: string;
16
+ name: string;
17
+ reason: string;
18
+ }
19
+ export interface ValidationResult {
20
+ suites: ValidatedSuite[];
21
+ rejected: RejectedCase[];
22
+ }
23
+ /** Returns why a test body is unacceptable, or whether it needs an async callback. */
24
+ export declare function checkBody(body: string, extension: SupportedExtension,
25
+ /** Names the target functions are imported as; they are real code, never mocks. */
26
+ targets?: ReadonlySet<string>): {
27
+ error: string;
28
+ } | {
29
+ async: boolean;
30
+ };
31
+ export declare function validateGeneration(generation: Generation, bindings: Binding[], extension: SupportedExtension): ValidationResult;
@@ -0,0 +1,180 @@
1
+ import { parse } from "@babel/parser";
2
+ import traverse from "@babel/traverse";
3
+ import { pluginsFor } from "../analyzer/parser.js";
4
+ export const MAX_CASES_PER_FUNCTION = 12;
5
+ const MAX_BODY_LENGTH = 4000;
6
+ const MAX_NAME_LENGTH = 200;
7
+ /** Module-level mocking is Assertloom's job; these would also load real modules or bypass the mocks. */
8
+ /** Read-only process calls; anything else (exit, kill, chdir, ...) could affect the test run itself. */
9
+ const SAFE_PROCESS_METHODS = new Set(["cwd", "hrtime", "memoryUsage", "uptime", "nextTick", "emitWarning"]);
10
+ const FORBIDDEN_JEST_METHODS = new Set([
11
+ "mock", "doMock", "unmock", "dontMock", "requireActual", "requireMock", "createMockFromModule",
12
+ "enableAutomock", "disableAutomock", "resetModules", "isolateModules", "isolateModulesAsync",
13
+ ]);
14
+ /** Read-only process properties tests may inspect. */
15
+ const SAFE_PROCESS_PROPERTIES = new Set(["env", "platform", "arch", "version", "versions", "argv", "pid", "execPath", "release", "features"]);
16
+ const GLOBAL_OBJECTS = new Set(["globalThis", "global", "window", "self"]);
17
+ function rootIdentifier(node) {
18
+ if (node.type === "Identifier")
19
+ return node.name;
20
+ if (node.type === "MemberExpression" || node.type === "OptionalMemberExpression")
21
+ return rootIdentifier(node.object);
22
+ if (node.type === "CallExpression" || node.type === "OptionalCallExpression")
23
+ return rootIdentifier(node.callee);
24
+ return undefined;
25
+ }
26
+ const isMember = (node) => node?.type === "MemberExpression" || node?.type === "OptionalMemberExpression";
27
+ /** The property name of `a.b` or `a["b"]`, or undefined when it is computed at runtime. */
28
+ function memberName(node) {
29
+ if (!node.computed && node.property.type === "Identifier")
30
+ return node.property.name;
31
+ if (node.property.type === "StringLiteral")
32
+ return node.property.value;
33
+ return undefined;
34
+ }
35
+ const propertyIs = (node, name) => memberName(node) === name;
36
+ /** Returns why a test body is unacceptable, or whether it needs an async callback. */
37
+ export function checkBody(body, extension,
38
+ /** Names the target functions are imported as; they are real code, never mocks. */
39
+ targets = new Set()) {
40
+ if (body.length > MAX_BODY_LENGTH)
41
+ return { error: `body is longer than ${MAX_BODY_LENGTH} characters` };
42
+ let ast;
43
+ try {
44
+ ast = parse(`async function __assertloomCase() {\n${body}\n}`, {
45
+ sourceType: "module",
46
+ plugins: pluginsFor(extension),
47
+ });
48
+ }
49
+ catch (error) {
50
+ return { error: `syntax error: ${error.message.replace(/\s*\(\d+:\d+\)$/, "")}` };
51
+ }
52
+ const [wrapper, ...extra] = ast.program.body;
53
+ if (extra.length || wrapper?.type !== "FunctionDeclaration")
54
+ return { error: "has unbalanced braces" };
55
+ let hasExpect = false;
56
+ let usesAwait = false;
57
+ let problem;
58
+ traverse(ast, {
59
+ "CallExpression|OptionalCallExpression"(path) {
60
+ const node = path.node;
61
+ const callee = node.callee;
62
+ if (rootIdentifier(callee) === "expect")
63
+ hasExpect = true;
64
+ const method = isMember(callee) ? memberName(callee) : undefined;
65
+ if (isMember(callee) && method?.startsWith("mock")) {
66
+ let stubbed = callee.object;
67
+ if ((stubbed.type === "CallExpression" || stubbed.type === "OptionalCallExpression") && rootIdentifier(stubbed.callee) === "jest") {
68
+ stubbed = stubbed.arguments[0];
69
+ }
70
+ if (stubbed?.type === "Identifier" && targets.has(stubbed.name)) {
71
+ problem ??= `calls ${method}() on \`${stubbed.name}\`, a real function under test (only dependencies are mocked)`;
72
+ }
73
+ }
74
+ if (rootIdentifier(callee) === "jest" && isMember(callee) && propertyIs(callee, "spyOn")) {
75
+ const [, spied] = node.arguments;
76
+ if (spied?.type === "StringLiteral" && targets.has(spied.value)) {
77
+ problem ??= `spies on \`${spied.value}\`, a function under test; calls inside the source file bypass spies`;
78
+ }
79
+ }
80
+ },
81
+ ImportExpression() {
82
+ problem ??= "uses import()";
83
+ },
84
+ AwaitExpression(path) {
85
+ if (path.getFunctionParent()?.parentPath?.isProgram())
86
+ usesAwait = true;
87
+ },
88
+ ForOfStatement(path) {
89
+ if (path.node.await && path.getFunctionParent()?.parentPath?.isProgram())
90
+ usesAwait = true;
91
+ },
92
+ Identifier(path) {
93
+ const { name } = path.node;
94
+ const global = path.isReferencedIdentifier() && !path.scope.getBinding(name);
95
+ const parent = path.parent;
96
+ const member = isMember(parent) && parent.object === path.node ? memberName(parent) : null;
97
+ const called = isMember(parent) && path.parentPath.parent.callee === parent;
98
+ if (global && name === "require") {
99
+ const call = path.parent;
100
+ const arg = call.callee === path.node ? call.arguments?.[0] : undefined;
101
+ problem ??= arg?.type === "StringLiteral" ? `uses require("${arg.value}")` : "uses require";
102
+ }
103
+ if (global && (name === "eval" || name === "Function"))
104
+ problem ??= `uses ${name}`;
105
+ if (global && name === "process") {
106
+ if (member === null)
107
+ problem ??= "uses process directly";
108
+ else if (!SAFE_PROCESS_PROPERTIES.has(member ?? "") && !SAFE_PROCESS_METHODS.has(member ?? "")) {
109
+ problem ??= called ? `calls process.${member ?? "[…]"}()` : `uses process.${member ?? "[…]"}`;
110
+ }
111
+ }
112
+ if (global && (name === "jest" || name === "vi")) {
113
+ if (member === null)
114
+ problem ??= `uses ${name} directly`;
115
+ else if (member === undefined || FORBIDDEN_JEST_METHODS.has(member))
116
+ problem ??= `calls jest.${member ?? "[…]"}()`;
117
+ }
118
+ if (global && GLOBAL_OBJECTS.has(name) && (member === null || member === undefined || member === "process" || member === "require")) {
119
+ problem ??= `uses ${name}${member ? `.${member}` : ""}`;
120
+ }
121
+ if ((name === "exports" || (name === "module" && path.parentPath.isMemberExpression({ object: path.node }))) && path.isReferencedIdentifier() && !path.scope.hasBinding(name)) {
122
+ problem ??= `uses ${name === "module" ? "module.exports" : "exports"}, which is the test file's own module`;
123
+ }
124
+ if ((path.node.name === "describe" || path.node.name === "it" || path.node.name === "test") && path.parentPath.isCallExpression({ callee: path.node })) {
125
+ problem ??= `wraps the body in ${path.node.name}()`;
126
+ }
127
+ },
128
+ });
129
+ if (problem)
130
+ return { error: problem };
131
+ if (!hasExpect)
132
+ return { error: "does not call expect()" };
133
+ return { async: usesAwait };
134
+ }
135
+ export function validateGeneration(generation, bindings, extension) {
136
+ const byName = new Map(bindings.map((b) => [b.binding, b]));
137
+ const targets = new Set(byName.keys());
138
+ const suites = new Map();
139
+ const rejected = [];
140
+ for (const suite of generation.suites) {
141
+ const binding = byName.get(suite.functionName);
142
+ if (!binding) {
143
+ for (const c of suite.cases)
144
+ rejected.push({ functionName: suite.functionName, name: c.name, reason: "not a target function" });
145
+ continue;
146
+ }
147
+ const target = suites.get(binding.binding) ?? { binding, cases: [] };
148
+ suites.set(binding.binding, target);
149
+ for (const testCase of suite.cases) {
150
+ const reject = (reason) => rejected.push({ functionName: suite.functionName, name: testCase.name, reason });
151
+ const name = testCase.name.trim();
152
+ if (!name) {
153
+ reject("has an empty name");
154
+ continue;
155
+ }
156
+ if (name.length > MAX_NAME_LENGTH) {
157
+ reject(`name is longer than ${MAX_NAME_LENGTH} characters`);
158
+ continue;
159
+ }
160
+ if (target.cases.length >= MAX_CASES_PER_FUNCTION) {
161
+ reject(`exceeds ${MAX_CASES_PER_FUNCTION} cases for this function`);
162
+ continue;
163
+ }
164
+ const check = checkBody(testCase.body, extension, targets);
165
+ if ("error" in check) {
166
+ reject(check.error);
167
+ continue;
168
+ }
169
+ let uniqueName = name;
170
+ for (let n = 2; target.cases.some((c) => c.name === uniqueName); n++)
171
+ uniqueName = `${name} (${n})`;
172
+ target.cases.push({ ...testCase, name: uniqueName, body: testCase.body.trim(), async: check.async });
173
+ }
174
+ }
175
+ return {
176
+ suites: bindings.map((b) => suites.get(b.binding)).filter((s) => !!s && s.cases.length > 0),
177
+ rejected,
178
+ };
179
+ }
180
+ //# sourceMappingURL=validate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/generator/validate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AACtC,OAAO,QAAQ,MAAM,iBAAiB,CAAC;AAEvC,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAKnD,MAAM,CAAC,MAAM,sBAAsB,GAAG,EAAE,CAAC;AACzC,MAAM,eAAe,GAAG,IAAI,CAAC;AAC7B,MAAM,eAAe,GAAG,GAAG,CAAC;AAE5B,wGAAwG;AACxG,wGAAwG;AACxG,MAAM,oBAAoB,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,QAAQ,EAAE,UAAU,EAAE,aAAa,CAAC,CAAC,CAAC;AAE5G,MAAM,sBAAsB,GAAG,IAAI,GAAG,CAAC;IACrC,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,UAAU,EAAE,eAAe,EAAE,aAAa,EAAE,sBAAsB;IAC9F,gBAAgB,EAAE,iBAAiB,EAAE,cAAc,EAAE,gBAAgB,EAAE,qBAAqB;CAC7F,CAAC,CAAC;AAwBH,sDAAsD;AACtD,MAAM,uBAAuB,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC,CAAC;AAE9I,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,CAAC,YAAY,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC;AAE3E,SAAS,cAAc,CAAC,IAAU;IAChC,IAAI,IAAI,CAAC,IAAI,KAAK,YAAY;QAAE,OAAO,IAAI,CAAC,IAAI,CAAC;IACjD,IAAI,IAAI,CAAC,IAAI,KAAK,kBAAkB,IAAI,IAAI,CAAC,IAAI,KAAK,0BAA0B;QAAE,OAAO,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACrH,IAAI,IAAI,CAAC,IAAI,KAAK,gBAAgB,IAAI,IAAI,CAAC,IAAI,KAAK,wBAAwB;QAAE,OAAO,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACjH,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,QAAQ,GAAG,CAAC,IAA6B,EAAsE,EAAE,CACrH,IAAI,EAAE,IAAI,KAAK,kBAAkB,IAAI,IAAI,EAAE,IAAI,KAAK,0BAA0B,CAAC;AAEjF,2FAA2F;AAC3F,SAAS,UAAU,CAAC,IAA2C;IAC7D,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,KAAK,YAAY;QAAE,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;IACrF,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,KAAK,eAAe;QAAE,OAAO,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;IACvE,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,GAAG,CAAC,IAA2C,EAAE,IAAY,EAAE,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC;AAE5G,sFAAsF;AACtF,MAAM,UAAU,SAAS,CACvB,IAAY,EACZ,SAA6B;AAC7B,mFAAmF;AACnF,OAAO,GAAwB,IAAI,GAAG,EAAE;IAExC,IAAI,IAAI,CAAC,MAAM,GAAG,eAAe;QAAE,OAAO,EAAE,KAAK,EAAE,uBAAuB,eAAe,aAAa,EAAE,CAAC;IAEzG,IAAI,GAAG,CAAC;IACR,IAAI,CAAC;QACH,GAAG,GAAG,KAAK,CAAC,wCAAwC,IAAI,KAAK,EAAE;YAC7D,UAAU,EAAE,QAAQ;YACpB,OAAO,EAAE,UAAU,CAAC,SAAS,CAAC;SAC/B,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,EAAE,KAAK,EAAE,iBAAkB,KAAe,CAAC,OAAO,CAAC,OAAO,CAAC,iBAAiB,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;IAC/F,CAAC;IAED,MAAM,CAAC,OAAO,EAAE,GAAG,KAAK,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC;IAC7C,IAAI,KAAK,CAAC,MAAM,IAAI,OAAO,EAAE,IAAI,KAAK,qBAAqB;QAAE,OAAO,EAAE,KAAK,EAAE,uBAAuB,EAAE,CAAC;IAEvG,IAAI,SAAS,GAAG,KAAK,CAAC;IACtB,IAAI,SAAS,GAAG,KAAK,CAAC;IACtB,IAAI,OAA2B,CAAC;IAEhC,QAAQ,CAAC,GAAG,EAAE;QACZ,uCAAuC,CAAC,IAAI;YAC1C,MAAM,IAAI,GAAG,IAAI,CAAC,IAAkD,CAAC;YACrE,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;YAC3B,IAAI,cAAc,CAAC,MAAM,CAAC,KAAK,QAAQ;gBAAE,SAAS,GAAG,IAAI,CAAC;YAC1D,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YACjE,IAAI,QAAQ,CAAC,MAAM,CAAC,IAAI,MAAM,EAAE,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;gBACnD,IAAI,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC;gBAC5B,IAAI,CAAC,OAAO,CAAC,IAAI,KAAK,gBAAgB,IAAI,OAAO,CAAC,IAAI,KAAK,wBAAwB,CAAC,IAAI,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,MAAM,EAAE,CAAC;oBAClI,OAAO,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,CAAmB,CAAC;gBACnD,CAAC;gBACD,IAAI,OAAO,EAAE,IAAI,KAAK,YAAY,IAAI,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;oBAChE,OAAO,KAAK,SAAS,MAAM,WAAW,OAAO,CAAC,IAAI,+DAA+D,CAAC;gBACpH,CAAC;YACH,CAAC;YACD,IAAI,cAAc,CAAC,MAAM,CAAC,KAAK,MAAM,IAAI,QAAQ,CAAC,MAAM,CAAC,IAAI,UAAU,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;gBACzF,MAAM,CAAC,EAAE,KAAK,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC;gBACjC,IAAI,KAAK,EAAE,IAAI,KAAK,eAAe,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;oBAChE,OAAO,KAAK,cAAc,KAAK,CAAC,KAAK,sEAAsE,CAAC;gBAC9G,CAAC;YACH,CAAC;QACH,CAAC;QACD,gBAAgB;YACd,OAAO,KAAK,eAAe,CAAC;QAC9B,CAAC;QACD,eAAe,CAAC,IAAI;YAClB,IAAI,IAAI,CAAC,iBAAiB,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE;gBAAE,SAAS,GAAG,IAAI,CAAC;QAC1E,CAAC;QACD,cAAc,CAAC,IAAI;YACjB,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,iBAAiB,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE;gBAAE,SAAS,GAAG,IAAI,CAAC;QAC7F,CAAC;QACD,UAAU,CAAC,IAAI;YACb,MAAM,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC;YAC3B,MAAM,MAAM,GAAG,IAAI,CAAC,sBAAsB,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;YAC7E,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;YAC3B,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,KAAK,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;YAC3F,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,IAAK,IAAI,CAAC,UAAU,CAAC,MAA4B,CAAC,MAAM,KAAK,MAAM,CAAC;YAEnG,IAAI,MAAM,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;gBACjC,MAAM,IAAI,GAAG,IAAI,CAAC,MAA+C,CAAC;gBAClE,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;gBACxE,OAAO,KAAK,GAAG,EAAE,IAAI,KAAK,eAAe,CAAC,CAAC,CAAC,iBAAiB,GAAG,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,cAAc,CAAC;YAC9F,CAAC;YACD,IAAI,MAAM,IAAI,CAAC,IAAI,KAAK,MAAM,IAAI,IAAI,KAAK,UAAU,CAAC;gBAAE,OAAO,KAAK,QAAQ,IAAI,EAAE,CAAC;YACnF,IAAI,MAAM,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;gBACjC,IAAI,MAAM,KAAK,IAAI;oBAAE,OAAO,KAAK,uBAAuB,CAAC;qBACpD,IAAI,CAAC,uBAAuB,CAAC,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,EAAE,CAAC;oBAC/F,OAAO,KAAK,MAAM,CAAC,CAAC,CAAC,iBAAiB,MAAM,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,gBAAgB,MAAM,IAAI,KAAK,EAAE,CAAC;gBAChG,CAAC;YACH,CAAC;YACD,IAAI,MAAM,IAAI,CAAC,IAAI,KAAK,MAAM,IAAI,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;gBACjD,IAAI,MAAM,KAAK,IAAI;oBAAE,OAAO,KAAK,QAAQ,IAAI,WAAW,CAAC;qBACpD,IAAI,MAAM,KAAK,SAAS,IAAI,sBAAsB,CAAC,GAAG,CAAC,MAAM,CAAC;oBAAE,OAAO,KAAK,cAAc,MAAM,IAAI,KAAK,IAAI,CAAC;YACrH,CAAC;YACD,IAAI,MAAM,IAAI,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,KAAK,IAAI,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,SAAS,CAAC,EAAE,CAAC;gBACpI,OAAO,KAAK,QAAQ,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;YAC1D,CAAC;YACD,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,CAAC,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,UAAU,CAAC,kBAAkB,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,sBAAsB,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC9K,OAAO,KAAK,QAAQ,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,SAAS,uCAAuC,CAAC;YAC9G,CAAC;YACD,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,UAAU,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;gBACvJ,OAAO,KAAK,qBAAqB,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC;YACtD,CAAC;QACH,CAAC;KACF,CAAC,CAAC;IAEH,IAAI,OAAO;QAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;IACvC,IAAI,CAAC,SAAS;QAAE,OAAO,EAAE,KAAK,EAAE,wBAAwB,EAAE,CAAC;IAC3D,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;AAC9B,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,UAAsB,EAAE,QAAmB,EAAE,SAA6B;IAC3G,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5D,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;IACvC,MAAM,MAAM,GAAG,IAAI,GAAG,EAA0B,CAAC;IACjD,MAAM,QAAQ,GAAmB,EAAE,CAAC;IAEpC,KAAK,MAAM,KAAK,IAAI,UAAU,CAAC,MAAM,EAAE,CAAC;QACtC,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;QAC/C,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,KAAK,MAAM,CAAC,IAAI,KAAK,CAAC,KAAK;gBAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,YAAY,EAAE,KAAK,CAAC,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,uBAAuB,EAAE,CAAC,CAAC;YAChI,SAAS;QACX,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;QACrE,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAEpC,KAAK,MAAM,QAAQ,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;YACnC,MAAM,MAAM,GAAG,CAAC,MAAc,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,YAAY,EAAE,KAAK,CAAC,YAAY,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;YACpH,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;YAElC,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,MAAM,CAAC,mBAAmB,CAAC,CAAC;gBAC5B,SAAS;YACX,CAAC;YACD,IAAI,IAAI,CAAC,MAAM,GAAG,eAAe,EAAE,CAAC;gBAClC,MAAM,CAAC,uBAAuB,eAAe,aAAa,CAAC,CAAC;gBAC5D,SAAS;YACX,CAAC;YACD,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,IAAI,sBAAsB,EAAE,CAAC;gBAClD,MAAM,CAAC,WAAW,sBAAsB,0BAA0B,CAAC,CAAC;gBACpE,SAAS;YACX,CAAC;YAED,MAAM,KAAK,GAAG,SAAS,CAAC,QAAQ,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;YAC3D,IAAI,OAAO,IAAI,KAAK,EAAE,CAAC;gBACrB,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;gBACpB,SAAS;YACX,CAAC;YAED,IAAI,UAAU,GAAG,IAAI,CAAC;YACtB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,UAAU,CAAC,EAAE,CAAC,EAAE;gBAAE,UAAU,GAAG,GAAG,IAAI,KAAK,CAAC,GAAG,CAAC;YACpG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,GAAG,QAAQ,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;QACvG,CAAC;IACH,CAAC;IAED,OAAO;QACL,MAAM,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAuB,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;QAChH,QAAQ;KACT,CAAC;AACJ,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { InfraFailure, TestOutcome } from "../runner/index.js";
2
+ export type FailureCategory = "assertion" | "runtime" | "timeout" | "mock" | "import" | "configuration";
3
+ export interface FailureClassification {
4
+ category: FailureCategory;
5
+ /** Whether rewriting the test body can plausibly fix it. */
6
+ repairable: boolean;
7
+ }
8
+ /** Sorts a failing test into the four repair categories (plus runtime errors and timeouts). */
9
+ export declare function classifyTestFailure(outcome: TestOutcome): FailureClassification;
10
+ /** A failure that stopped the whole file from running. No test-body edit can fix these. */
11
+ export declare function classifyInfraFailure(infra: InfraFailure): FailureClassification;
@@ -0,0 +1,26 @@
1
+ import { NETWORK_BLOCKED_CODE, NETWORK_BLOCKED_MESSAGE } from "../runner/networkGuard.js";
2
+ /**
3
+ * Errors from real sockets: the code reached a dependency that is not mocked. Problems with
4
+ * Assertloom's mocks themselves (unconfigured return values, wrong call arguments) are test bugs.
5
+ */
6
+ const MOCK_SIGNALS = new RegExp(`${NETWORK_BLOCKED_MESSAGE}|${NETWORK_BLOCKED_CODE}|ECONNREFUSED|ENOTFOUND|EAI_AGAIN|ECONNRESET|getaddrinfo|socket hang up`);
7
+ /** Sorts a failing test into the four repair categories (plus runtime errors and timeouts). */
8
+ export function classifyTestFailure(outcome) {
9
+ const failure = outcome.failure;
10
+ if (!failure)
11
+ return { category: "runtime", repairable: true };
12
+ if (MOCK_SIGNALS.test(failure.message))
13
+ return { category: "mock", repairable: false };
14
+ if (/Cannot find module/.test(failure.message))
15
+ return { category: "import", repairable: false };
16
+ if (failure.kind === "assertion")
17
+ return { category: "assertion", repairable: true };
18
+ if (failure.kind === "timeout")
19
+ return { category: "timeout", repairable: true };
20
+ return { category: "runtime", repairable: true };
21
+ }
22
+ /** A failure that stopped the whole file from running. No test-body edit can fix these. */
23
+ export function classifyInfraFailure(infra) {
24
+ return { category: infra.kind === "module-not-found" ? "import" : "configuration", repairable: false };
25
+ }
26
+ //# sourceMappingURL=classifyFailure.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"classifyFailure.js","sourceRoot":"","sources":["../../src/repair/classifyFailure.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AAW1F;;;GAGG;AACH,MAAM,YAAY,GAAG,IAAI,MAAM,CAAC,GAAG,uBAAuB,IAAI,oBAAoB,yEAAyE,CAAC,CAAC;AAE7J,+FAA+F;AAC/F,MAAM,UAAU,mBAAmB,CAAC,OAAoB;IACtD,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAChC,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;IAC/D,IAAI,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC;QAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC;IACvF,IAAI,oBAAoB,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC;QAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC;IACjG,IAAI,OAAO,CAAC,IAAI,KAAK,WAAW;QAAE,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;IACrF,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS;QAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;IACjF,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;AACnD,CAAC;AAED,2FAA2F;AAC3F,MAAM,UAAU,oBAAoB,CAAC,KAAmB;IACtD,OAAO,EAAE,QAAQ,EAAE,KAAK,CAAC,IAAI,KAAK,kBAAkB,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,eAAe,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC;AACzG,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { classifyInfraFailure, classifyTestFailure, type FailureCategory } from "./classifyFailure.js";
2
+ export { MAX_REPAIR_ATTEMPTS, repairTests, summarize, type RepairAttempt, type RepairResult, type StopReason } from "./repairLoop.js";
@@ -0,0 +1,3 @@
1
+ export { classifyInfraFailure, classifyTestFailure } from "./classifyFailure.js";
2
+ export { MAX_REPAIR_ATTEMPTS, repairTests, summarize } from "./repairLoop.js";
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/repair/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAwB,MAAM,sBAAsB,CAAC;AACvG,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,SAAS,EAA0D,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,74 @@
1
+ import { type Budget } from "../ai/pricing.js";
2
+ import type { LLMProvider, TokenUsage } from "../ai/provider.js";
3
+ import { type ValidatedSuite } from "../generator/index.js";
4
+ import { type RunResult } from "../runner/index.js";
5
+ import type { FileAnalysis, SupportedExtension } from "../types/index.js";
6
+ import { type RepairAction } from "./schema.js";
7
+ export declare const MAX_REPAIR_ATTEMPTS = 3;
8
+ export interface RunSummary {
9
+ status: RunResult["status"];
10
+ passed: number;
11
+ failed: number;
12
+ skipped: number;
13
+ coverage?: RunResult["coverage"];
14
+ }
15
+ export interface RepairChange {
16
+ functionName: string;
17
+ name: string;
18
+ action: RepairAction | "set-aside" | "gave-up";
19
+ reason: string;
20
+ }
21
+ export interface RepairAttempt {
22
+ attempt: number;
23
+ before: RunSummary;
24
+ after?: RunSummary;
25
+ requested: number;
26
+ changes: RepairChange[];
27
+ invalid: Array<{
28
+ functionName: string;
29
+ name: string;
30
+ reason: string;
31
+ }>;
32
+ regressed: boolean;
33
+ usage: TokenUsage;
34
+ }
35
+ export type StopReason = "all-passing" | "max-attempts" | "regressed" | "no-valid-repairs" | "unrepairable" | "budget";
36
+ export interface RepairResult {
37
+ suites: ValidatedSuite[];
38
+ content: string;
39
+ run: RunResult;
40
+ initial: RunSummary;
41
+ final: RunSummary;
42
+ attempts: RepairAttempt[];
43
+ /** Cases marked `it.skip` after the loop because they still failed. */
44
+ gaveUp: RepairChange[];
45
+ stopReason: StopReason;
46
+ /** False if a test run modified the source file; Assertloom restores it. */
47
+ sourceUnchanged: boolean;
48
+ usage: TokenUsage;
49
+ }
50
+ export interface RepairOptions {
51
+ provider: LLMProvider;
52
+ analysis: FileAnalysis;
53
+ /** Source as sent to the model (secrets redacted). */
54
+ promptSource: string;
55
+ sourcePath: string;
56
+ testPath: string;
57
+ extension: SupportedExtension;
58
+ preamble: string[];
59
+ /** Description of the mocks, as given to the model when generating. */
60
+ mockSummary?: string;
61
+ suites: ValidatedSuite[];
62
+ initialRun: RunResult;
63
+ maxAttempts?: number;
64
+ runTests?: (testPath: string) => Promise<RunResult>;
65
+ /** Let tests open network connections. Off by default. */
66
+ allowNetwork?: boolean;
67
+ budget?: Budget;
68
+ }
69
+ export declare function summarize(run: RunResult): RunSummary;
70
+ /**
71
+ * Repairs failing generated tests in bounded rounds. Only the test file is ever written;
72
+ * a round that breaks a previously passing test is reverted and ends the loop.
73
+ */
74
+ export declare function repairTests(options: RepairOptions): Promise<RepairResult>;