claudecode-omc 4.7.4 → 4.8.1
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/.claude-plugin/plugin.json +1 -1
- package/README.md +50 -0
- package/agents/test-engineer.md +74 -0
- package/bridge/cli.cjs +1960 -108
- package/dist/cli/index.js +181 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/hud/usage-api.d.ts.map +1 -1
- package/dist/hud/usage-api.js +14 -0
- package/dist/hud/usage-api.js.map +1 -1
- package/dist/testing/analyzers/complexity.d.ts +18 -0
- package/dist/testing/analyzers/complexity.d.ts.map +1 -0
- package/dist/testing/analyzers/complexity.js +121 -0
- package/dist/testing/analyzers/complexity.js.map +1 -0
- package/dist/testing/analyzers/coverage.d.ts +13 -0
- package/dist/testing/analyzers/coverage.d.ts.map +1 -0
- package/dist/testing/analyzers/coverage.js +99 -0
- package/dist/testing/analyzers/coverage.js.map +1 -0
- package/dist/testing/analyzers/quality-scorer.d.ts +8 -0
- package/dist/testing/analyzers/quality-scorer.d.ts.map +1 -0
- package/dist/testing/analyzers/quality-scorer.js +128 -0
- package/dist/testing/analyzers/quality-scorer.js.map +1 -0
- package/dist/testing/analyzers/types.d.ts +56 -0
- package/dist/testing/analyzers/types.d.ts.map +1 -0
- package/dist/testing/analyzers/types.js +2 -0
- package/dist/testing/analyzers/types.js.map +1 -0
- package/dist/testing/cli/agent-integration.d.ts +20 -0
- package/dist/testing/cli/agent-integration.d.ts.map +1 -0
- package/dist/testing/cli/agent-integration.js +60 -0
- package/dist/testing/cli/agent-integration.js.map +1 -0
- package/dist/testing/cli/commands.d.ts +89 -0
- package/dist/testing/cli/commands.d.ts.map +1 -0
- package/dist/testing/cli/commands.js +228 -0
- package/dist/testing/cli/commands.js.map +1 -0
- package/dist/testing/cli/ultraqa-integration.d.ts +13 -0
- package/dist/testing/cli/ultraqa-integration.d.ts.map +1 -0
- package/dist/testing/cli/ultraqa-integration.js +68 -0
- package/dist/testing/cli/ultraqa-integration.js.map +1 -0
- package/dist/testing/detectors/go.d.ts +3 -0
- package/dist/testing/detectors/go.d.ts.map +1 -0
- package/dist/testing/detectors/go.js +38 -0
- package/dist/testing/detectors/go.js.map +1 -0
- package/dist/testing/detectors/index.d.ts +8 -0
- package/dist/testing/detectors/index.d.ts.map +1 -0
- package/dist/testing/detectors/index.js +46 -0
- package/dist/testing/detectors/index.js.map +1 -0
- package/dist/testing/detectors/package-json.d.ts +3 -0
- package/dist/testing/detectors/package-json.d.ts.map +1 -0
- package/dist/testing/detectors/package-json.js +52 -0
- package/dist/testing/detectors/package-json.js.map +1 -0
- package/dist/testing/detectors/python.d.ts +3 -0
- package/dist/testing/detectors/python.d.ts.map +1 -0
- package/dist/testing/detectors/python.js +37 -0
- package/dist/testing/detectors/python.js.map +1 -0
- package/dist/testing/detectors/rust.d.ts +3 -0
- package/dist/testing/detectors/rust.d.ts.map +1 -0
- package/dist/testing/detectors/rust.js +39 -0
- package/dist/testing/detectors/rust.js.map +1 -0
- package/dist/testing/generators/contract.d.ts +14 -0
- package/dist/testing/generators/contract.d.ts.map +1 -0
- package/dist/testing/generators/contract.js +163 -0
- package/dist/testing/generators/contract.js.map +1 -0
- package/dist/testing/generators/e2e.d.ts +34 -0
- package/dist/testing/generators/e2e.d.ts.map +1 -0
- package/dist/testing/generators/e2e.js +74 -0
- package/dist/testing/generators/e2e.js.map +1 -0
- package/dist/testing/generators/go.d.ts +12 -0
- package/dist/testing/generators/go.d.ts.map +1 -0
- package/dist/testing/generators/go.js +144 -0
- package/dist/testing/generators/go.js.map +1 -0
- package/dist/testing/generators/nodejs.d.ts +12 -0
- package/dist/testing/generators/nodejs.d.ts.map +1 -0
- package/dist/testing/generators/nodejs.js +37 -0
- package/dist/testing/generators/nodejs.js.map +1 -0
- package/dist/testing/generators/python.d.ts +12 -0
- package/dist/testing/generators/python.d.ts.map +1 -0
- package/dist/testing/generators/python.js +163 -0
- package/dist/testing/generators/python.js.map +1 -0
- package/dist/testing/generators/react.d.ts +12 -0
- package/dist/testing/generators/react.d.ts.map +1 -0
- package/dist/testing/generators/react.js +31 -0
- package/dist/testing/generators/react.js.map +1 -0
- package/dist/testing/generators/rust.d.ts +11 -0
- package/dist/testing/generators/rust.d.ts.map +1 -0
- package/dist/testing/generators/rust.js +138 -0
- package/dist/testing/generators/rust.js.map +1 -0
- package/dist/testing/index.d.ts +6 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +11 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/integrations/autopilot.d.ts +42 -0
- package/dist/testing/integrations/autopilot.d.ts.map +1 -0
- package/dist/testing/integrations/autopilot.js +55 -0
- package/dist/testing/integrations/autopilot.js.map +1 -0
- package/dist/testing/integrations/cicd.d.ts +26 -0
- package/dist/testing/integrations/cicd.d.ts.map +1 -0
- package/dist/testing/integrations/cicd.js +162 -0
- package/dist/testing/integrations/cicd.js.map +1 -0
- package/dist/testing/integrations/giskard/behavioral-tests.d.ts +4 -0
- package/dist/testing/integrations/giskard/behavioral-tests.d.ts.map +1 -0
- package/dist/testing/integrations/giskard/behavioral-tests.js +66 -0
- package/dist/testing/integrations/giskard/behavioral-tests.js.map +1 -0
- package/dist/testing/integrations/giskard/types.d.ts +35 -0
- package/dist/testing/integrations/giskard/types.d.ts.map +1 -0
- package/dist/testing/integrations/giskard/types.js +2 -0
- package/dist/testing/integrations/giskard/types.js.map +1 -0
- package/dist/testing/integrations/ralph.d.ts +65 -0
- package/dist/testing/integrations/ralph.d.ts.map +1 -0
- package/dist/testing/integrations/ralph.js +69 -0
- package/dist/testing/integrations/ralph.js.map +1 -0
- package/dist/testing/performance/cache-manager.d.ts +16 -0
- package/dist/testing/performance/cache-manager.d.ts.map +1 -0
- package/dist/testing/performance/cache-manager.js +39 -0
- package/dist/testing/performance/cache-manager.js.map +1 -0
- package/dist/testing/performance/parallel-generator.d.ts +23 -0
- package/dist/testing/performance/parallel-generator.d.ts.map +1 -0
- package/dist/testing/performance/parallel-generator.js +31 -0
- package/dist/testing/performance/parallel-generator.js.map +1 -0
- package/dist/testing/types.d.ts +23 -0
- package/dist/testing/types.d.ts.map +1 -0
- package/dist/testing/types.js +2 -0
- package/dist/testing/types.js.map +1 -0
- package/docs/2026-03-06-llm-testing-system-phase1.md +0 -0
- package/docs/plans/2026-03-06-llm-testing-system-design.md +311 -0
- package/docs/plans/2026-03-06-llm-testing-system-phase1.md +1268 -0
- package/docs/plans/2026-03-06-llm-testing-system-phase2.md +3053 -0
- package/docs/plans/2026-03-06-llm-testing-system-phase3.md +1830 -0
- package/docs/testing/IMPLEMENTATION.md +804 -0
- package/docs/testing/PHASE2.md +266 -0
- package/docs/testing/PHASE3.md +601 -0
- package/docs/testing/README.md +634 -0
- package/package.json +1 -1
- package/skills/test-gen/skill.md +531 -0
- package/skills/ultraqa.md +58 -0
package/bridge/cli.cjs
CHANGED
|
@@ -967,8 +967,8 @@ var require_command = __commonJS({
|
|
|
967
967
|
"node_modules/commander/lib/command.js"(exports2) {
|
|
968
968
|
var EventEmitter = require("node:events").EventEmitter;
|
|
969
969
|
var childProcess = require("node:child_process");
|
|
970
|
-
var
|
|
971
|
-
var
|
|
970
|
+
var path26 = require("node:path");
|
|
971
|
+
var fs25 = require("node:fs");
|
|
972
972
|
var process3 = require("node:process");
|
|
973
973
|
var { Argument: Argument2, humanReadableArgName } = require_argument();
|
|
974
974
|
var { CommanderError: CommanderError2 } = require_error();
|
|
@@ -1900,11 +1900,11 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1900
1900
|
let launchWithNode = false;
|
|
1901
1901
|
const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
|
|
1902
1902
|
function findFile(baseDir, baseName) {
|
|
1903
|
-
const localBin =
|
|
1904
|
-
if (
|
|
1905
|
-
if (sourceExt.includes(
|
|
1903
|
+
const localBin = path26.resolve(baseDir, baseName);
|
|
1904
|
+
if (fs25.existsSync(localBin)) return localBin;
|
|
1905
|
+
if (sourceExt.includes(path26.extname(baseName))) return void 0;
|
|
1906
1906
|
const foundExt = sourceExt.find(
|
|
1907
|
-
(ext) =>
|
|
1907
|
+
(ext) => fs25.existsSync(`${localBin}${ext}`)
|
|
1908
1908
|
);
|
|
1909
1909
|
if (foundExt) return `${localBin}${foundExt}`;
|
|
1910
1910
|
return void 0;
|
|
@@ -1916,21 +1916,21 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1916
1916
|
if (this._scriptPath) {
|
|
1917
1917
|
let resolvedScriptPath;
|
|
1918
1918
|
try {
|
|
1919
|
-
resolvedScriptPath =
|
|
1919
|
+
resolvedScriptPath = fs25.realpathSync(this._scriptPath);
|
|
1920
1920
|
} catch (err) {
|
|
1921
1921
|
resolvedScriptPath = this._scriptPath;
|
|
1922
1922
|
}
|
|
1923
|
-
executableDir =
|
|
1924
|
-
|
|
1923
|
+
executableDir = path26.resolve(
|
|
1924
|
+
path26.dirname(resolvedScriptPath),
|
|
1925
1925
|
executableDir
|
|
1926
1926
|
);
|
|
1927
1927
|
}
|
|
1928
1928
|
if (executableDir) {
|
|
1929
1929
|
let localFile = findFile(executableDir, executableFile);
|
|
1930
1930
|
if (!localFile && !subcommand._executableFile && this._scriptPath) {
|
|
1931
|
-
const legacyName =
|
|
1931
|
+
const legacyName = path26.basename(
|
|
1932
1932
|
this._scriptPath,
|
|
1933
|
-
|
|
1933
|
+
path26.extname(this._scriptPath)
|
|
1934
1934
|
);
|
|
1935
1935
|
if (legacyName !== this._name) {
|
|
1936
1936
|
localFile = findFile(
|
|
@@ -1941,7 +1941,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1941
1941
|
}
|
|
1942
1942
|
executableFile = localFile || executableFile;
|
|
1943
1943
|
}
|
|
1944
|
-
launchWithNode = sourceExt.includes(
|
|
1944
|
+
launchWithNode = sourceExt.includes(path26.extname(executableFile));
|
|
1945
1945
|
let proc;
|
|
1946
1946
|
if (process3.platform !== "win32") {
|
|
1947
1947
|
if (launchWithNode) {
|
|
@@ -2781,7 +2781,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2781
2781
|
* @return {Command}
|
|
2782
2782
|
*/
|
|
2783
2783
|
nameFromFilename(filename) {
|
|
2784
|
-
this._name =
|
|
2784
|
+
this._name = path26.basename(filename, path26.extname(filename));
|
|
2785
2785
|
return this;
|
|
2786
2786
|
}
|
|
2787
2787
|
/**
|
|
@@ -2795,9 +2795,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2795
2795
|
* @param {string} [path]
|
|
2796
2796
|
* @return {(string|null|Command)}
|
|
2797
2797
|
*/
|
|
2798
|
-
executableDir(
|
|
2799
|
-
if (
|
|
2800
|
-
this._executableDir =
|
|
2798
|
+
executableDir(path27) {
|
|
2799
|
+
if (path27 === void 0) return this._executableDir;
|
|
2800
|
+
this._executableDir = path27;
|
|
2801
2801
|
return this;
|
|
2802
2802
|
}
|
|
2803
2803
|
/**
|
|
@@ -3041,8 +3041,8 @@ var init_config_dir = __esm({
|
|
|
3041
3041
|
});
|
|
3042
3042
|
|
|
3043
3043
|
// src/utils/paths.ts
|
|
3044
|
-
function toForwardSlash(
|
|
3045
|
-
return
|
|
3044
|
+
function toForwardSlash(path26) {
|
|
3045
|
+
return path26.replace(/\\/g, "/");
|
|
3046
3046
|
}
|
|
3047
3047
|
function getClaudeConfigDir() {
|
|
3048
3048
|
return getConfigDir();
|
|
@@ -3382,16 +3382,16 @@ function getConfigPaths() {
|
|
|
3382
3382
|
project: (0, import_path2.join)(process.cwd(), ".claude", "omc.jsonc")
|
|
3383
3383
|
};
|
|
3384
3384
|
}
|
|
3385
|
-
function loadJsoncFile(
|
|
3386
|
-
if (!(0, import_fs2.existsSync)(
|
|
3385
|
+
function loadJsoncFile(path26) {
|
|
3386
|
+
if (!(0, import_fs2.existsSync)(path26)) {
|
|
3387
3387
|
return null;
|
|
3388
3388
|
}
|
|
3389
3389
|
try {
|
|
3390
|
-
const content = (0, import_fs2.readFileSync)(
|
|
3390
|
+
const content = (0, import_fs2.readFileSync)(path26, "utf-8");
|
|
3391
3391
|
const result = parseJsonc(content);
|
|
3392
3392
|
return result;
|
|
3393
3393
|
} catch (error2) {
|
|
3394
|
-
console.error(`Error loading config from ${
|
|
3394
|
+
console.error(`Error loading config from ${path26}:`, error2);
|
|
3395
3395
|
return null;
|
|
3396
3396
|
}
|
|
3397
3397
|
}
|
|
@@ -17617,12 +17617,12 @@ async function pollTelegram(config2, state, rateLimiter) {
|
|
|
17617
17617
|
}
|
|
17618
17618
|
try {
|
|
17619
17619
|
const offset = state.telegramLastUpdateId ? state.telegramLastUpdateId + 1 : 0;
|
|
17620
|
-
const
|
|
17620
|
+
const path26 = `/bot${config2.telegramBotToken}/getUpdates?offset=${offset}&timeout=0`;
|
|
17621
17621
|
const updates = await new Promise((resolve12, reject) => {
|
|
17622
17622
|
const req = (0, import_https2.request)(
|
|
17623
17623
|
{
|
|
17624
17624
|
hostname: "api.telegram.org",
|
|
17625
|
-
path:
|
|
17625
|
+
path: path26,
|
|
17626
17626
|
method: "GET",
|
|
17627
17627
|
family: 4,
|
|
17628
17628
|
// Force IPv4
|
|
@@ -19132,8 +19132,8 @@ async function exportWisdomToNotepad(directory) {
|
|
|
19132
19132
|
let hasWisdom = false;
|
|
19133
19133
|
try {
|
|
19134
19134
|
const planDirs = (0, import_fs53.readdirSync)(notepadsDir).filter((name) => {
|
|
19135
|
-
const
|
|
19136
|
-
return (0, import_fs53.statSync)(
|
|
19135
|
+
const path26 = (0, import_path62.join)(notepadsDir, name);
|
|
19136
|
+
return (0, import_fs53.statSync)(path26).isDirectory();
|
|
19137
19137
|
});
|
|
19138
19138
|
for (const planDir of planDirs) {
|
|
19139
19139
|
const planPath = (0, import_path62.join)(notepadsDir, planDir);
|
|
@@ -19192,9 +19192,9 @@ async function saveModeSummary(directory) {
|
|
|
19192
19192
|
}
|
|
19193
19193
|
];
|
|
19194
19194
|
const reads = stateFiles.map(async (config2) => {
|
|
19195
|
-
const
|
|
19195
|
+
const path26 = (0, import_path62.join)(stateDir, config2.file);
|
|
19196
19196
|
try {
|
|
19197
|
-
const content = await import_fs54.promises.readFile(
|
|
19197
|
+
const content = await import_fs54.promises.readFile(path26, "utf-8");
|
|
19198
19198
|
const state = JSON.parse(content);
|
|
19199
19199
|
const extracted = config2.extract(state);
|
|
19200
19200
|
return extracted ? { key: config2.key, value: extracted } : null;
|
|
@@ -21572,6 +21572,17 @@ async function getUsage() {
|
|
|
21572
21572
|
const authToken = process.env.ANTHROPIC_AUTH_TOKEN;
|
|
21573
21573
|
const isZai = baseUrl != null && isZaiHost(baseUrl);
|
|
21574
21574
|
const currentSource = isZai && authToken ? "zai" : "anthropic";
|
|
21575
|
+
if (!isZai || !authToken) {
|
|
21576
|
+
const creds2 = getCredentials();
|
|
21577
|
+
if (!creds2) {
|
|
21578
|
+
const cache2 = readCache();
|
|
21579
|
+
if (cache2 && isCacheValid(cache2) && cache2.errorReason === "no_credentials") {
|
|
21580
|
+
return { rateLimits: null, error: "no_credentials" };
|
|
21581
|
+
}
|
|
21582
|
+
writeCache(null, true, "anthropic", false, 0, "no_credentials");
|
|
21583
|
+
return { rateLimits: null, error: "no_credentials" };
|
|
21584
|
+
}
|
|
21585
|
+
}
|
|
21575
21586
|
const cache = readCache();
|
|
21576
21587
|
if (cache && isCacheValid(cache) && cache.source === currentSource) {
|
|
21577
21588
|
if (cache.rateLimited) {
|
|
@@ -23234,7 +23245,7 @@ async function requeueDeadWorkerTasks(teamName, deadWorkerNames, cwd2) {
|
|
|
23234
23245
|
await writeFile5(sidecarPath, JSON.stringify(sidecar, null, 2), "utf-8");
|
|
23235
23246
|
const taskPath2 = absPath(cwd2, TeamPaths.taskFile(sanitized, task.id));
|
|
23236
23247
|
try {
|
|
23237
|
-
const raw = await import("fs/promises").then((
|
|
23248
|
+
const raw = await import("fs/promises").then((fs25) => fs25.readFile(taskPath2, "utf-8"));
|
|
23238
23249
|
const taskData = JSON.parse(raw);
|
|
23239
23250
|
taskData.status = "pending";
|
|
23240
23251
|
taskData.owner = void 0;
|
|
@@ -26820,6 +26831,1718 @@ var init_hud = __esm({
|
|
|
26820
26831
|
}
|
|
26821
26832
|
});
|
|
26822
26833
|
|
|
26834
|
+
// src/testing/detectors/package-json.ts
|
|
26835
|
+
async function detectFromPackageJson(packageJson) {
|
|
26836
|
+
const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
|
|
26837
|
+
const stack = {};
|
|
26838
|
+
if (deps.react) {
|
|
26839
|
+
stack.frontend = {
|
|
26840
|
+
framework: "react",
|
|
26841
|
+
testFramework: deps.vitest ? "vitest" : deps.jest ? "jest" : "none"
|
|
26842
|
+
};
|
|
26843
|
+
} else if (deps.vue) {
|
|
26844
|
+
stack.frontend = {
|
|
26845
|
+
framework: "vue",
|
|
26846
|
+
testFramework: deps.vitest ? "vitest" : "none"
|
|
26847
|
+
};
|
|
26848
|
+
} else if (deps.svelte) {
|
|
26849
|
+
stack.frontend = {
|
|
26850
|
+
framework: "svelte",
|
|
26851
|
+
testFramework: deps.vitest ? "vitest" : "none"
|
|
26852
|
+
};
|
|
26853
|
+
}
|
|
26854
|
+
if (deps.express || deps.fastify || deps.koa) {
|
|
26855
|
+
stack.backend = {
|
|
26856
|
+
language: "nodejs",
|
|
26857
|
+
testFramework: deps.vitest ? "vitest" : deps.jest ? "jest" : void 0
|
|
26858
|
+
};
|
|
26859
|
+
}
|
|
26860
|
+
const databases = [];
|
|
26861
|
+
if (deps.pg || deps.postgres) databases.push("postgresql");
|
|
26862
|
+
if (deps.mysql || deps.mysql2) databases.push("mysql");
|
|
26863
|
+
if (deps.mongodb || deps.mongoose) databases.push("mongodb");
|
|
26864
|
+
if (databases.length > 0) stack.databases = databases;
|
|
26865
|
+
const apis = [];
|
|
26866
|
+
if (deps.express || deps.fastify) apis.push("rest");
|
|
26867
|
+
if (deps.graphql || deps["@apollo/server"]) apis.push("graphql");
|
|
26868
|
+
if (deps["@grpc/grpc-js"]) apis.push("grpc");
|
|
26869
|
+
if (apis.length > 0) stack.apis = apis;
|
|
26870
|
+
return stack;
|
|
26871
|
+
}
|
|
26872
|
+
var init_package_json = __esm({
|
|
26873
|
+
"src/testing/detectors/package-json.ts"() {
|
|
26874
|
+
"use strict";
|
|
26875
|
+
}
|
|
26876
|
+
});
|
|
26877
|
+
|
|
26878
|
+
// src/testing/detectors/python.ts
|
|
26879
|
+
async function detectPythonStack(projectRoot) {
|
|
26880
|
+
const stack = {};
|
|
26881
|
+
try {
|
|
26882
|
+
const requirementsPath = import_path107.default.join(projectRoot, "requirements.txt");
|
|
26883
|
+
const requirements = await import_promises16.default.readFile(requirementsPath, "utf-8");
|
|
26884
|
+
stack.backend = {
|
|
26885
|
+
language: "python",
|
|
26886
|
+
testFramework: requirements.includes("pytest") ? "pytest" : requirements.includes("unittest") ? "unittest" : void 0
|
|
26887
|
+
};
|
|
26888
|
+
const databases = [];
|
|
26889
|
+
if (requirements.includes("psycopg2") || requirements.includes("psycopg3")) databases.push("postgresql");
|
|
26890
|
+
if (requirements.includes("pymysql") || requirements.includes("mysql-connector")) databases.push("mysql");
|
|
26891
|
+
if (requirements.includes("pymongo")) databases.push("mongodb");
|
|
26892
|
+
if (databases.length > 0) stack.databases = databases;
|
|
26893
|
+
const apis = [];
|
|
26894
|
+
if (requirements.includes("flask") || requirements.includes("fastapi") || requirements.includes("django")) apis.push("rest");
|
|
26895
|
+
if (requirements.includes("graphene") || requirements.includes("strawberry")) apis.push("graphql");
|
|
26896
|
+
if (apis.length > 0) stack.apis = apis;
|
|
26897
|
+
} catch (error2) {
|
|
26898
|
+
}
|
|
26899
|
+
return stack;
|
|
26900
|
+
}
|
|
26901
|
+
var import_promises16, import_path107;
|
|
26902
|
+
var init_python = __esm({
|
|
26903
|
+
"src/testing/detectors/python.ts"() {
|
|
26904
|
+
"use strict";
|
|
26905
|
+
import_promises16 = __toESM(require("fs/promises"), 1);
|
|
26906
|
+
import_path107 = __toESM(require("path"), 1);
|
|
26907
|
+
}
|
|
26908
|
+
});
|
|
26909
|
+
|
|
26910
|
+
// src/testing/detectors/go.ts
|
|
26911
|
+
async function detectGoStack(projectRoot) {
|
|
26912
|
+
const stack = {};
|
|
26913
|
+
try {
|
|
26914
|
+
const goModPath = import_path108.default.join(projectRoot, "go.mod");
|
|
26915
|
+
const goMod = await import_promises17.default.readFile(goModPath, "utf-8");
|
|
26916
|
+
stack.backend = {
|
|
26917
|
+
language: "go",
|
|
26918
|
+
testFramework: "testing"
|
|
26919
|
+
};
|
|
26920
|
+
const databases = [];
|
|
26921
|
+
if (goMod.includes("github.com/lib/pq") || goMod.includes("github.com/jackc/pgx")) databases.push("postgresql");
|
|
26922
|
+
if (goMod.includes("github.com/go-sql-driver/mysql")) databases.push("mysql");
|
|
26923
|
+
if (goMod.includes("go.mongodb.org/mongo-driver")) databases.push("mongodb");
|
|
26924
|
+
if (databases.length > 0) stack.databases = databases;
|
|
26925
|
+
const apis = [];
|
|
26926
|
+
if (goMod.includes("github.com/gin-gonic/gin") || goMod.includes("github.com/gorilla/mux")) apis.push("rest");
|
|
26927
|
+
if (goMod.includes("github.com/graphql-go/graphql")) apis.push("graphql");
|
|
26928
|
+
if (goMod.includes("google.golang.org/grpc")) apis.push("grpc");
|
|
26929
|
+
if (apis.length > 0) stack.apis = apis;
|
|
26930
|
+
} catch (error2) {
|
|
26931
|
+
}
|
|
26932
|
+
return stack;
|
|
26933
|
+
}
|
|
26934
|
+
var import_promises17, import_path108;
|
|
26935
|
+
var init_go = __esm({
|
|
26936
|
+
"src/testing/detectors/go.ts"() {
|
|
26937
|
+
"use strict";
|
|
26938
|
+
import_promises17 = __toESM(require("fs/promises"), 1);
|
|
26939
|
+
import_path108 = __toESM(require("path"), 1);
|
|
26940
|
+
}
|
|
26941
|
+
});
|
|
26942
|
+
|
|
26943
|
+
// src/testing/detectors/rust.ts
|
|
26944
|
+
async function detectRustStack(projectRoot) {
|
|
26945
|
+
const stack = {};
|
|
26946
|
+
try {
|
|
26947
|
+
const cargoTomlPath = import_path109.default.join(projectRoot, "Cargo.toml");
|
|
26948
|
+
const cargoToml = await import_promises18.default.readFile(cargoTomlPath, "utf-8");
|
|
26949
|
+
stack.backend = {
|
|
26950
|
+
language: "rust",
|
|
26951
|
+
testFramework: "cargo test"
|
|
26952
|
+
// Rust's built-in testing
|
|
26953
|
+
};
|
|
26954
|
+
const databases = [];
|
|
26955
|
+
if (cargoToml.includes("tokio-postgres") || cargoToml.includes("sqlx")) databases.push("postgresql");
|
|
26956
|
+
if (cargoToml.includes("mysql_async")) databases.push("mysql");
|
|
26957
|
+
if (cargoToml.includes("mongodb")) databases.push("mongodb");
|
|
26958
|
+
if (databases.length > 0) stack.databases = databases;
|
|
26959
|
+
const apis = [];
|
|
26960
|
+
if (cargoToml.includes("actix-web") || cargoToml.includes("rocket") || cargoToml.includes("axum")) apis.push("rest");
|
|
26961
|
+
if (cargoToml.includes("async-graphql") || cargoToml.includes("juniper")) apis.push("graphql");
|
|
26962
|
+
if (cargoToml.includes("tonic")) apis.push("grpc");
|
|
26963
|
+
if (apis.length > 0) stack.apis = apis;
|
|
26964
|
+
} catch (error2) {
|
|
26965
|
+
}
|
|
26966
|
+
return stack;
|
|
26967
|
+
}
|
|
26968
|
+
var import_promises18, import_path109;
|
|
26969
|
+
var init_rust = __esm({
|
|
26970
|
+
"src/testing/detectors/rust.ts"() {
|
|
26971
|
+
"use strict";
|
|
26972
|
+
import_promises18 = __toESM(require("fs/promises"), 1);
|
|
26973
|
+
import_path109 = __toESM(require("path"), 1);
|
|
26974
|
+
}
|
|
26975
|
+
});
|
|
26976
|
+
|
|
26977
|
+
// src/testing/detectors/index.ts
|
|
26978
|
+
async function detectTechStack(projectRoot) {
|
|
26979
|
+
let stack = {};
|
|
26980
|
+
try {
|
|
26981
|
+
const packageJsonPath = import_path110.default.join(projectRoot, "package.json");
|
|
26982
|
+
const content = await import_promises19.default.readFile(packageJsonPath, "utf-8");
|
|
26983
|
+
const packageJson = JSON.parse(content);
|
|
26984
|
+
stack = await detectFromPackageJson(packageJson);
|
|
26985
|
+
} catch (error2) {
|
|
26986
|
+
}
|
|
26987
|
+
try {
|
|
26988
|
+
const pythonStack = await detectPythonStack(projectRoot);
|
|
26989
|
+
stack = { ...stack, ...pythonStack };
|
|
26990
|
+
} catch (error2) {
|
|
26991
|
+
}
|
|
26992
|
+
try {
|
|
26993
|
+
const goStack = await detectGoStack(projectRoot);
|
|
26994
|
+
stack = { ...stack, ...goStack };
|
|
26995
|
+
} catch (error2) {
|
|
26996
|
+
}
|
|
26997
|
+
try {
|
|
26998
|
+
const rustStack = await detectRustStack(projectRoot);
|
|
26999
|
+
stack = { ...stack, ...rustStack };
|
|
27000
|
+
} catch (error2) {
|
|
27001
|
+
}
|
|
27002
|
+
return stack;
|
|
27003
|
+
}
|
|
27004
|
+
var import_promises19, import_path110;
|
|
27005
|
+
var init_detectors = __esm({
|
|
27006
|
+
"src/testing/detectors/index.ts"() {
|
|
27007
|
+
"use strict";
|
|
27008
|
+
import_promises19 = __toESM(require("fs/promises"), 1);
|
|
27009
|
+
import_path110 = __toESM(require("path"), 1);
|
|
27010
|
+
init_package_json();
|
|
27011
|
+
init_python();
|
|
27012
|
+
init_go();
|
|
27013
|
+
init_rust();
|
|
27014
|
+
}
|
|
27015
|
+
});
|
|
27016
|
+
|
|
27017
|
+
// src/testing/generators/react.ts
|
|
27018
|
+
async function generateReactTest(options) {
|
|
27019
|
+
const { filePath, code, testFramework } = options;
|
|
27020
|
+
const fileName = filePath.split("/").pop()?.replace(/\.(tsx?|jsx?)$/, "") || "Component";
|
|
27021
|
+
const testFilePath = filePath.replace(/\.(tsx?|jsx?)$/, ".test.$1");
|
|
27022
|
+
const hasOnClick = code.includes("onClick");
|
|
27023
|
+
const hasChildren = code.includes("children");
|
|
27024
|
+
const testCode = `import { describe, it, expect${testFramework === "vitest" ? ", vi" : ""} } from '${testFramework}';
|
|
27025
|
+
import { render, screen${hasOnClick ? ", fireEvent" : ""} } from '@testing-library/react';
|
|
27026
|
+
import { ${fileName} } from './${fileName}';
|
|
27027
|
+
|
|
27028
|
+
describe('${fileName}', () => {
|
|
27029
|
+
it('renders children', () => {
|
|
27030
|
+
render(<${fileName}>Click me</${fileName}>);
|
|
27031
|
+
expect(screen.getByText('Click me')).toBeInTheDocument();
|
|
27032
|
+
});
|
|
27033
|
+
${hasOnClick ? `
|
|
27034
|
+
it('calls onClick when clicked', () => {
|
|
27035
|
+
const handleClick = ${testFramework === "vitest" ? "vi.fn()" : "jest.fn()"};
|
|
27036
|
+
render(<${fileName} onClick={handleClick}>Click me</${fileName}>);
|
|
27037
|
+
fireEvent.click(screen.getByText('Click me'));
|
|
27038
|
+
expect(handleClick).toHaveBeenCalledTimes(1);
|
|
27039
|
+
});
|
|
27040
|
+
` : ""}});
|
|
27041
|
+
`;
|
|
27042
|
+
return { testFilePath, testCode };
|
|
27043
|
+
}
|
|
27044
|
+
var init_react = __esm({
|
|
27045
|
+
"src/testing/generators/react.ts"() {
|
|
27046
|
+
"use strict";
|
|
27047
|
+
}
|
|
27048
|
+
});
|
|
27049
|
+
|
|
27050
|
+
// src/testing/generators/nodejs.ts
|
|
27051
|
+
async function generateNodeJsTest(options) {
|
|
27052
|
+
const { filePath, code, testFramework } = options;
|
|
27053
|
+
const functionMatches = code.matchAll(/export\s+(?:async\s+)?function\s+(\w+)/g);
|
|
27054
|
+
const functions = Array.from(functionMatches).map((match) => match[1]);
|
|
27055
|
+
const testFilePath = filePath.replace(/\.ts$/, ".test.ts");
|
|
27056
|
+
const fileName = filePath.split("/").pop()?.replace(/\.ts$/, "") || "module";
|
|
27057
|
+
const testCases = functions.map((funcName) => {
|
|
27058
|
+
if (funcName === "add") {
|
|
27059
|
+
return ` it('adds two numbers', () => {
|
|
27060
|
+
expect(add(2, 3)).toBe(5);
|
|
27061
|
+
expect(add(-1, 1)).toBe(0);
|
|
27062
|
+
expect(add(0, 0)).toBe(0);
|
|
27063
|
+
});`;
|
|
27064
|
+
}
|
|
27065
|
+
return ` it('${funcName} works correctly', () => {
|
|
27066
|
+
// TODO: Add specific test cases for ${funcName}
|
|
27067
|
+
expect(${funcName}).toBeDefined();
|
|
27068
|
+
});`;
|
|
27069
|
+
}).join("\n\n");
|
|
27070
|
+
const describeName = functions.length === 1 ? functions[0] : fileName;
|
|
27071
|
+
const testCode = `import { describe, it, expect } from '${testFramework}';
|
|
27072
|
+
import { ${functions.join(", ")} } from './${fileName}';
|
|
27073
|
+
|
|
27074
|
+
describe('${describeName}', () => {
|
|
27075
|
+
${testCases}
|
|
27076
|
+
});
|
|
27077
|
+
`;
|
|
27078
|
+
return { testFilePath, testCode };
|
|
27079
|
+
}
|
|
27080
|
+
var init_nodejs = __esm({
|
|
27081
|
+
"src/testing/generators/nodejs.ts"() {
|
|
27082
|
+
"use strict";
|
|
27083
|
+
}
|
|
27084
|
+
});
|
|
27085
|
+
|
|
27086
|
+
// src/testing/generators/python.ts
|
|
27087
|
+
async function generatePythonTest(options) {
|
|
27088
|
+
const { filePath, code, testFramework } = options;
|
|
27089
|
+
const fileName = filePath.split("/").pop()?.replace(/\.py$/, "") || "module";
|
|
27090
|
+
const testFilePath = `tests/test_${fileName}.py`;
|
|
27091
|
+
const functions = extractPythonFunctions(code);
|
|
27092
|
+
const classes = extractPythonClasses(code);
|
|
27093
|
+
let testCode = "";
|
|
27094
|
+
if (testFramework === "pytest") {
|
|
27095
|
+
testCode = generatePytestCode(fileName, functions, classes);
|
|
27096
|
+
} else {
|
|
27097
|
+
testCode = generateUnittestCode(fileName, functions, classes);
|
|
27098
|
+
}
|
|
27099
|
+
return { testFilePath, testCode };
|
|
27100
|
+
}
|
|
27101
|
+
function extractPythonFunctions(code) {
|
|
27102
|
+
const functions = [];
|
|
27103
|
+
const functionRegex = /^(async\s+)?def\s+(\w+)\s*\((.*?)\)/gm;
|
|
27104
|
+
let match;
|
|
27105
|
+
while ((match = functionRegex.exec(code)) !== null) {
|
|
27106
|
+
const isAsync3 = !!match[1];
|
|
27107
|
+
const name = match[2];
|
|
27108
|
+
const paramsStr = match[3];
|
|
27109
|
+
const beforeDef = code.substring(0, match.index);
|
|
27110
|
+
const lastClassMatch = beforeDef.lastIndexOf("class ");
|
|
27111
|
+
const lastFunctionMatch = beforeDef.lastIndexOf("\ndef ");
|
|
27112
|
+
if (lastClassMatch > lastFunctionMatch) {
|
|
27113
|
+
continue;
|
|
27114
|
+
}
|
|
27115
|
+
const params = paramsStr.split(",").map((p) => p.trim().split(":")[0].trim()).filter((p) => p && p !== "self");
|
|
27116
|
+
functions.push({ name, params, isAsync: isAsync3 });
|
|
27117
|
+
}
|
|
27118
|
+
return functions;
|
|
27119
|
+
}
|
|
27120
|
+
function extractPythonClasses(code) {
|
|
27121
|
+
const classes = [];
|
|
27122
|
+
const classRegex = /class\s+(\w+).*?:/g;
|
|
27123
|
+
let match;
|
|
27124
|
+
while ((match = classRegex.exec(code)) !== null) {
|
|
27125
|
+
const className = match[1];
|
|
27126
|
+
const classStart = match.index;
|
|
27127
|
+
const methods = [];
|
|
27128
|
+
const methodRegex = /^\s+(async\s+)?def\s+(\w+)\s*\((.*?)\)/gm;
|
|
27129
|
+
methodRegex.lastIndex = classStart;
|
|
27130
|
+
let methodMatch;
|
|
27131
|
+
while ((methodMatch = methodRegex.exec(code)) !== null) {
|
|
27132
|
+
const nextClass = code.indexOf("\nclass ", classStart + 1);
|
|
27133
|
+
if (nextClass !== -1 && methodMatch.index > nextClass) {
|
|
27134
|
+
break;
|
|
27135
|
+
}
|
|
27136
|
+
const isAsync3 = !!methodMatch[1];
|
|
27137
|
+
const methodName = methodMatch[2];
|
|
27138
|
+
const paramsStr = methodMatch[3];
|
|
27139
|
+
const params = paramsStr.split(",").map((p) => p.trim().split(":")[0].trim()).filter((p) => p && p !== "self");
|
|
27140
|
+
methods.push({ name: methodName, params, isAsync: isAsync3 });
|
|
27141
|
+
}
|
|
27142
|
+
classes.push({ name: className, methods });
|
|
27143
|
+
}
|
|
27144
|
+
return classes;
|
|
27145
|
+
}
|
|
27146
|
+
function generatePytestCode(moduleName, functions, classes) {
|
|
27147
|
+
let code = `import pytest
|
|
27148
|
+
from src.${moduleName} import ${[...functions.map((f) => f.name), ...classes.map((c) => c.name)].join(", ")}
|
|
27149
|
+
|
|
27150
|
+
`;
|
|
27151
|
+
for (const func of functions) {
|
|
27152
|
+
code += generatePytestFunction(func);
|
|
27153
|
+
}
|
|
27154
|
+
for (const cls of classes) {
|
|
27155
|
+
code += `class Test${cls.name}:
|
|
27156
|
+
`;
|
|
27157
|
+
for (const method of cls.methods) {
|
|
27158
|
+
code += generatePytestMethod(cls.name, method);
|
|
27159
|
+
}
|
|
27160
|
+
code += "\n";
|
|
27161
|
+
}
|
|
27162
|
+
return code;
|
|
27163
|
+
}
|
|
27164
|
+
function generatePytestFunction(func) {
|
|
27165
|
+
const testName = `test_${func.name}`;
|
|
27166
|
+
const asyncPrefix = func.isAsync ? "@pytest.mark.asyncio\nasync " : "";
|
|
27167
|
+
let testBody = "";
|
|
27168
|
+
if (func.name === "add") {
|
|
27169
|
+
testBody = ` assert add(2, 3) == 5
|
|
27170
|
+
assert add(-1, 1) == 0
|
|
27171
|
+
assert add(0, 0) == 0`;
|
|
27172
|
+
} else {
|
|
27173
|
+
testBody = ` # TODO: Add test cases for ${func.name}
|
|
27174
|
+
assert ${func.name} is not None`;
|
|
27175
|
+
}
|
|
27176
|
+
return `${asyncPrefix}def ${testName}():
|
|
27177
|
+
${testBody}
|
|
27178
|
+
|
|
27179
|
+
`;
|
|
27180
|
+
}
|
|
27181
|
+
function generatePytestMethod(className, method) {
|
|
27182
|
+
const testName = `test_${method.name}`;
|
|
27183
|
+
const asyncPrefix = method.isAsync ? " @pytest.mark.asyncio\n async " : " ";
|
|
27184
|
+
let testBody = "";
|
|
27185
|
+
if (method.name === "add") {
|
|
27186
|
+
testBody = ` instance = ${className}()
|
|
27187
|
+
assert instance.add(2, 3) == 5
|
|
27188
|
+
assert instance.add(-1, 1) == 0`;
|
|
27189
|
+
} else if (method.name === "subtract") {
|
|
27190
|
+
testBody = ` instance = ${className}()
|
|
27191
|
+
assert instance.subtract(5, 3) == 2
|
|
27192
|
+
assert instance.subtract(0, 0) == 0`;
|
|
27193
|
+
} else {
|
|
27194
|
+
testBody = ` instance = ${className}()
|
|
27195
|
+
# TODO: Add test cases for ${method.name}
|
|
27196
|
+
assert instance.${method.name} is not None`;
|
|
27197
|
+
}
|
|
27198
|
+
return `${asyncPrefix}def ${testName}(self):
|
|
27199
|
+
${testBody}
|
|
27200
|
+
|
|
27201
|
+
`;
|
|
27202
|
+
}
|
|
27203
|
+
function generateUnittestCode(moduleName, functions, classes) {
|
|
27204
|
+
let code = `import unittest
|
|
27205
|
+
from src.${moduleName} import ${[...functions.map((f) => f.name), ...classes.map((c) => c.name)].join(", ")}
|
|
27206
|
+
|
|
27207
|
+
`;
|
|
27208
|
+
if (functions.length > 0) {
|
|
27209
|
+
code += `class TestFunctions(unittest.TestCase):
|
|
27210
|
+
`;
|
|
27211
|
+
for (const func of functions) {
|
|
27212
|
+
code += generateUnittestFunction(func);
|
|
27213
|
+
}
|
|
27214
|
+
code += "\n";
|
|
27215
|
+
}
|
|
27216
|
+
for (const cls of classes) {
|
|
27217
|
+
code += `class Test${cls.name}(unittest.TestCase):
|
|
27218
|
+
`;
|
|
27219
|
+
for (const method of cls.methods) {
|
|
27220
|
+
code += generateUnittestMethod(cls.name, method);
|
|
27221
|
+
}
|
|
27222
|
+
code += "\n";
|
|
27223
|
+
}
|
|
27224
|
+
code += `
|
|
27225
|
+
if __name__ == '__main__':
|
|
27226
|
+
unittest.main()
|
|
27227
|
+
`;
|
|
27228
|
+
return code;
|
|
27229
|
+
}
|
|
27230
|
+
function generateUnittestFunction(func) {
|
|
27231
|
+
const testName = `test_${func.name}`;
|
|
27232
|
+
let testBody = "";
|
|
27233
|
+
if (func.name === "add") {
|
|
27234
|
+
testBody = ` self.assertEqual(add(2, 3), 5)
|
|
27235
|
+
self.assertEqual(add(-1, 1), 0)
|
|
27236
|
+
self.assertEqual(add(0, 0), 0)`;
|
|
27237
|
+
} else {
|
|
27238
|
+
testBody = ` # TODO: Add test cases for ${func.name}
|
|
27239
|
+
self.assertIsNotNone(${func.name})`;
|
|
27240
|
+
}
|
|
27241
|
+
return ` def ${testName}(self):
|
|
27242
|
+
${testBody}
|
|
27243
|
+
|
|
27244
|
+
`;
|
|
27245
|
+
}
|
|
27246
|
+
function generateUnittestMethod(className, method) {
|
|
27247
|
+
const testName = `test_${method.name}`;
|
|
27248
|
+
let testBody = "";
|
|
27249
|
+
if (method.name === "add") {
|
|
27250
|
+
testBody = ` instance = ${className}()
|
|
27251
|
+
self.assertEqual(instance.add(2, 3), 5)
|
|
27252
|
+
self.assertEqual(instance.add(-1, 1), 0)`;
|
|
27253
|
+
} else if (method.name === "subtract") {
|
|
27254
|
+
testBody = ` instance = ${className}()
|
|
27255
|
+
self.assertEqual(instance.subtract(5, 3), 2)
|
|
27256
|
+
self.assertEqual(instance.subtract(0, 0), 0)`;
|
|
27257
|
+
} else {
|
|
27258
|
+
testBody = ` instance = ${className}()
|
|
27259
|
+
# TODO: Add test cases for ${method.name}
|
|
27260
|
+
self.assertIsNotNone(instance.${method.name})`;
|
|
27261
|
+
}
|
|
27262
|
+
return ` def ${testName}(self):
|
|
27263
|
+
${testBody}
|
|
27264
|
+
|
|
27265
|
+
`;
|
|
27266
|
+
}
|
|
27267
|
+
var init_python2 = __esm({
|
|
27268
|
+
"src/testing/generators/python.ts"() {
|
|
27269
|
+
"use strict";
|
|
27270
|
+
}
|
|
27271
|
+
});
|
|
27272
|
+
|
|
27273
|
+
// src/testing/generators/go.ts
|
|
27274
|
+
async function generateGoTest(options) {
|
|
27275
|
+
const { filePath, code, packageName } = options;
|
|
27276
|
+
const testFilePath = filePath.replace(/\.go$/, "_test.go");
|
|
27277
|
+
const functions = extractGoFunctions(code);
|
|
27278
|
+
const testCode = generateTestCode(packageName, functions);
|
|
27279
|
+
return { testFilePath, testCode };
|
|
27280
|
+
}
|
|
27281
|
+
function extractGoFunctions(code) {
|
|
27282
|
+
const functions = [];
|
|
27283
|
+
const funcRegex = /func\s+([A-Z]\w*)\s*\((.*?)\)\s*([\w.*[\]]*)?/g;
|
|
27284
|
+
let match;
|
|
27285
|
+
while ((match = funcRegex.exec(code)) !== null) {
|
|
27286
|
+
const name = match[1];
|
|
27287
|
+
const paramsStr = match[2];
|
|
27288
|
+
const returnType = (match[3] || "").trim();
|
|
27289
|
+
const params = parseGoParams(paramsStr);
|
|
27290
|
+
functions.push({ name, params, returnType });
|
|
27291
|
+
}
|
|
27292
|
+
return functions;
|
|
27293
|
+
}
|
|
27294
|
+
function parseGoParams(paramsStr) {
|
|
27295
|
+
if (!paramsStr.trim()) return [];
|
|
27296
|
+
const params = [];
|
|
27297
|
+
const parts = paramsStr.split(",").map((p) => p.trim());
|
|
27298
|
+
let lastType = "";
|
|
27299
|
+
const parsed = [];
|
|
27300
|
+
for (const part of parts) {
|
|
27301
|
+
const tokens = part.split(/\s+/);
|
|
27302
|
+
if (tokens.length >= 2) {
|
|
27303
|
+
const type = tokens[tokens.length - 1];
|
|
27304
|
+
const names = tokens.slice(0, -1);
|
|
27305
|
+
parsed.push({ names, type });
|
|
27306
|
+
lastType = type;
|
|
27307
|
+
} else {
|
|
27308
|
+
parsed.push({ names: [tokens[0]], type: "" });
|
|
27309
|
+
}
|
|
27310
|
+
}
|
|
27311
|
+
for (let i = parsed.length - 1; i >= 0; i--) {
|
|
27312
|
+
if (parsed[i].type) {
|
|
27313
|
+
lastType = parsed[i].type;
|
|
27314
|
+
} else {
|
|
27315
|
+
parsed[i].type = lastType;
|
|
27316
|
+
}
|
|
27317
|
+
}
|
|
27318
|
+
for (const p of parsed) {
|
|
27319
|
+
for (const name of p.names) {
|
|
27320
|
+
params.push({ name, type: p.type });
|
|
27321
|
+
}
|
|
27322
|
+
}
|
|
27323
|
+
return params;
|
|
27324
|
+
}
|
|
27325
|
+
function generateTestCode(packageName, functions) {
|
|
27326
|
+
let code = `package ${packageName}
|
|
27327
|
+
|
|
27328
|
+
import "testing"
|
|
27329
|
+
|
|
27330
|
+
`;
|
|
27331
|
+
for (const func of functions) {
|
|
27332
|
+
code += generateTableDrivenTest(func);
|
|
27333
|
+
}
|
|
27334
|
+
return code;
|
|
27335
|
+
}
|
|
27336
|
+
function generateTableDrivenTest(func) {
|
|
27337
|
+
const testName = `Test${func.name}`;
|
|
27338
|
+
const structFields = func.params.map((p) => ` ${p.name} ${p.type}`).join("\n");
|
|
27339
|
+
const expectedField = func.returnType ? ` expected ${func.returnType}` : "";
|
|
27340
|
+
const allFields = [structFields, expectedField].filter(Boolean).join("\n");
|
|
27341
|
+
const argsList = func.params.map((p) => `tt.${p.name}`).join(", ");
|
|
27342
|
+
const sampleArgs = func.params.map((p) => getZeroValue(p.type)).join(", ");
|
|
27343
|
+
const sampleExpected = func.returnType ? getZeroValue(func.returnType) : "";
|
|
27344
|
+
let testCase = func.params.map((p) => `${getZeroValue(p.type)}`).join(", ");
|
|
27345
|
+
if (func.returnType) {
|
|
27346
|
+
testCase += `, ${sampleExpected}`;
|
|
27347
|
+
}
|
|
27348
|
+
let code = `func ${testName}(t *testing.T) {
|
|
27349
|
+
`;
|
|
27350
|
+
code += ` tests := []struct {
|
|
27351
|
+
`;
|
|
27352
|
+
code += ` name string
|
|
27353
|
+
`;
|
|
27354
|
+
code += `${allFields}
|
|
27355
|
+
`;
|
|
27356
|
+
code += ` }{
|
|
27357
|
+
`;
|
|
27358
|
+
code += ` {
|
|
27359
|
+
`;
|
|
27360
|
+
code += ` name: "basic case",
|
|
27361
|
+
`;
|
|
27362
|
+
for (const p of func.params) {
|
|
27363
|
+
code += ` ${p.name}: ${getZeroValue(p.type)},
|
|
27364
|
+
`;
|
|
27365
|
+
}
|
|
27366
|
+
if (func.returnType) {
|
|
27367
|
+
code += ` expected: ${getZeroValue(func.returnType)},
|
|
27368
|
+
`;
|
|
27369
|
+
}
|
|
27370
|
+
code += ` },
|
|
27371
|
+
`;
|
|
27372
|
+
code += ` }
|
|
27373
|
+
|
|
27374
|
+
`;
|
|
27375
|
+
code += ` for _, tt := range tests {
|
|
27376
|
+
`;
|
|
27377
|
+
code += ` t.Run(tt.name, func(t *testing.T) {
|
|
27378
|
+
`;
|
|
27379
|
+
if (func.returnType) {
|
|
27380
|
+
code += ` got := ${func.name}(${argsList})
|
|
27381
|
+
`;
|
|
27382
|
+
code += ` if got != tt.expected {
|
|
27383
|
+
`;
|
|
27384
|
+
code += ` t.Errorf("${func.name}() = %v, want %v", got, tt.expected)
|
|
27385
|
+
`;
|
|
27386
|
+
code += ` }
|
|
27387
|
+
`;
|
|
27388
|
+
} else {
|
|
27389
|
+
code += ` ${func.name}(${argsList})
|
|
27390
|
+
`;
|
|
27391
|
+
}
|
|
27392
|
+
code += ` })
|
|
27393
|
+
`;
|
|
27394
|
+
code += ` }
|
|
27395
|
+
`;
|
|
27396
|
+
code += `}
|
|
27397
|
+
|
|
27398
|
+
`;
|
|
27399
|
+
return code;
|
|
27400
|
+
}
|
|
27401
|
+
function getZeroValue(goType) {
|
|
27402
|
+
switch (goType) {
|
|
27403
|
+
case "int":
|
|
27404
|
+
case "int8":
|
|
27405
|
+
case "int16":
|
|
27406
|
+
case "int32":
|
|
27407
|
+
case "int64":
|
|
27408
|
+
case "uint":
|
|
27409
|
+
case "uint8":
|
|
27410
|
+
case "uint16":
|
|
27411
|
+
case "uint32":
|
|
27412
|
+
case "uint64":
|
|
27413
|
+
case "float32":
|
|
27414
|
+
case "float64":
|
|
27415
|
+
return "0";
|
|
27416
|
+
case "string":
|
|
27417
|
+
return '""';
|
|
27418
|
+
case "bool":
|
|
27419
|
+
return "false";
|
|
27420
|
+
default:
|
|
27421
|
+
return "nil";
|
|
27422
|
+
}
|
|
27423
|
+
}
|
|
27424
|
+
var init_go2 = __esm({
|
|
27425
|
+
"src/testing/generators/go.ts"() {
|
|
27426
|
+
"use strict";
|
|
27427
|
+
}
|
|
27428
|
+
});
|
|
27429
|
+
|
|
27430
|
+
// src/testing/generators/rust.ts
|
|
27431
|
+
async function generateRustTest(options) {
|
|
27432
|
+
const { filePath, code } = options;
|
|
27433
|
+
const testFilePath = filePath;
|
|
27434
|
+
const functions = extractRustFunctions(code);
|
|
27435
|
+
const structs = extractRustStructs(code);
|
|
27436
|
+
const testCode = generateRustTestModule(functions, structs);
|
|
27437
|
+
return { testCode, testFilePath };
|
|
27438
|
+
}
|
|
27439
|
+
function extractRustFunctions(code) {
|
|
27440
|
+
const functions = [];
|
|
27441
|
+
const functionRegex = /(pub\s+)?fn\s+(\w+)\s*\((.*?)\)\s*(?:->\s*(.*?))?\s*{/g;
|
|
27442
|
+
let match;
|
|
27443
|
+
while ((match = functionRegex.exec(code)) !== null) {
|
|
27444
|
+
const isPublic = !!match[1];
|
|
27445
|
+
const name = match[2];
|
|
27446
|
+
const paramsStr = match[3];
|
|
27447
|
+
const returnType = match[4]?.trim() || "()";
|
|
27448
|
+
const beforeFn = code.substring(0, match.index);
|
|
27449
|
+
const lastImpl = beforeFn.lastIndexOf("impl ");
|
|
27450
|
+
const lastCloseBrace = beforeFn.lastIndexOf("}");
|
|
27451
|
+
if (lastImpl > lastCloseBrace) {
|
|
27452
|
+
continue;
|
|
27453
|
+
}
|
|
27454
|
+
const params = parseRustParams(paramsStr);
|
|
27455
|
+
functions.push({ name, params, returnType, isPublic });
|
|
27456
|
+
}
|
|
27457
|
+
return functions;
|
|
27458
|
+
}
|
|
27459
|
+
function extractRustStructs(code) {
|
|
27460
|
+
const structs = [];
|
|
27461
|
+
const structRegex = /struct\s+(\w+)/g;
|
|
27462
|
+
let match;
|
|
27463
|
+
while ((match = structRegex.exec(code)) !== null) {
|
|
27464
|
+
const structName = match[1];
|
|
27465
|
+
const implRegex = new RegExp(`impl\\s+${structName}\\s*{([^}]+)}`, "s");
|
|
27466
|
+
const implMatch = implRegex.exec(code);
|
|
27467
|
+
if (implMatch) {
|
|
27468
|
+
const implBody = implMatch[1];
|
|
27469
|
+
const methods = extractRustMethods(implBody);
|
|
27470
|
+
structs.push({ name: structName, methods });
|
|
27471
|
+
}
|
|
27472
|
+
}
|
|
27473
|
+
return structs;
|
|
27474
|
+
}
|
|
27475
|
+
function extractRustMethods(implBody) {
|
|
27476
|
+
const methods = [];
|
|
27477
|
+
const methodRegex = /(pub\s+)?fn\s+(\w+)\s*\((.*?)\)\s*(?:->\s*(.*?))?\s*{/g;
|
|
27478
|
+
let match;
|
|
27479
|
+
while ((match = methodRegex.exec(implBody)) !== null) {
|
|
27480
|
+
const isPublic = !!match[1];
|
|
27481
|
+
const name = match[2];
|
|
27482
|
+
const paramsStr = match[3];
|
|
27483
|
+
const returnType = match[4]?.trim() || "()";
|
|
27484
|
+
const params = parseRustParams(paramsStr);
|
|
27485
|
+
methods.push({ name, params, returnType, isPublic });
|
|
27486
|
+
}
|
|
27487
|
+
return methods;
|
|
27488
|
+
}
|
|
27489
|
+
function parseRustParams(paramsStr) {
|
|
27490
|
+
if (!paramsStr.trim()) return [];
|
|
27491
|
+
const params = [];
|
|
27492
|
+
const parts = paramsStr.split(",").map((p) => p.trim());
|
|
27493
|
+
for (const part of parts) {
|
|
27494
|
+
const colonIndex = part.indexOf(":");
|
|
27495
|
+
if (colonIndex !== -1) {
|
|
27496
|
+
const name = part.substring(0, colonIndex).trim();
|
|
27497
|
+
const type = part.substring(colonIndex + 1).trim();
|
|
27498
|
+
params.push({ name, type });
|
|
27499
|
+
}
|
|
27500
|
+
}
|
|
27501
|
+
return params;
|
|
27502
|
+
}
|
|
27503
|
+
function generateRustTestModule(functions, structs) {
|
|
27504
|
+
let code = `
|
|
27505
|
+
#[cfg(test)]
|
|
27506
|
+
mod tests {
|
|
27507
|
+
use super::*;
|
|
27508
|
+
|
|
27509
|
+
`;
|
|
27510
|
+
for (const func of functions) {
|
|
27511
|
+
if (func.isPublic) {
|
|
27512
|
+
code += generateRustTestFunction(func);
|
|
27513
|
+
}
|
|
27514
|
+
}
|
|
27515
|
+
for (const struct of structs) {
|
|
27516
|
+
for (const method of struct.methods) {
|
|
27517
|
+
if (method.isPublic) {
|
|
27518
|
+
code += generateRustTestMethod(struct.name, method);
|
|
27519
|
+
}
|
|
27520
|
+
}
|
|
27521
|
+
}
|
|
27522
|
+
code += `}
|
|
27523
|
+
`;
|
|
27524
|
+
return code;
|
|
27525
|
+
}
|
|
27526
|
+
function generateRustTestFunction(func) {
|
|
27527
|
+
const testName = `test_${func.name}`;
|
|
27528
|
+
let testBody = "";
|
|
27529
|
+
if (func.name === "add") {
|
|
27530
|
+
testBody = ` assert_eq!(add(2, 3), 5);
|
|
27531
|
+
assert_eq!(add(-1, 1), 0);
|
|
27532
|
+
assert_eq!(add(0, 0), 0);`;
|
|
27533
|
+
} else {
|
|
27534
|
+
testBody = ` // TODO: Add test implementation for ${func.name}`;
|
|
27535
|
+
}
|
|
27536
|
+
return ` #[test]
|
|
27537
|
+
fn ${testName}() {
|
|
27538
|
+
${testBody}
|
|
27539
|
+
}
|
|
27540
|
+
|
|
27541
|
+
`;
|
|
27542
|
+
}
|
|
27543
|
+
function generateRustTestMethod(structName, method) {
|
|
27544
|
+
const testName = `test_${method.name}`;
|
|
27545
|
+
let testBody = "";
|
|
27546
|
+
if (method.name === "new") {
|
|
27547
|
+
testBody = ` let instance = ${structName}::new();
|
|
27548
|
+
// TODO: Add assertions`;
|
|
27549
|
+
} else if (method.name === "add") {
|
|
27550
|
+
testBody = ` let mut instance = ${structName}::new();
|
|
27551
|
+
instance.add(5);
|
|
27552
|
+
// TODO: Add assertions`;
|
|
27553
|
+
} else {
|
|
27554
|
+
testBody = ` // TODO: Add test implementation for ${method.name}`;
|
|
27555
|
+
}
|
|
27556
|
+
return ` #[test]
|
|
27557
|
+
fn ${testName}() {
|
|
27558
|
+
${testBody}
|
|
27559
|
+
}
|
|
27560
|
+
|
|
27561
|
+
`;
|
|
27562
|
+
}
|
|
27563
|
+
var init_rust2 = __esm({
|
|
27564
|
+
"src/testing/generators/rust.ts"() {
|
|
27565
|
+
"use strict";
|
|
27566
|
+
}
|
|
27567
|
+
});
|
|
27568
|
+
|
|
27569
|
+
// src/testing/generators/contract.ts
|
|
27570
|
+
async function generateContractTest(options) {
|
|
27571
|
+
const { spec, apiDefinition, framework, consumer, provider } = options;
|
|
27572
|
+
let testCode = "";
|
|
27573
|
+
let testFilePath = "";
|
|
27574
|
+
if (framework === "pact" && spec) {
|
|
27575
|
+
testCode = generatePactTest(spec, consumer || "consumer", provider || "provider");
|
|
27576
|
+
testFilePath = `tests/contract/${consumer}-${provider}.pact.test.ts`;
|
|
27577
|
+
} else if (framework === "supertest" && apiDefinition) {
|
|
27578
|
+
testCode = generateSupertestContract(apiDefinition);
|
|
27579
|
+
testFilePath = `tests/contract/api.contract.test.ts`;
|
|
27580
|
+
} else if (framework === "msw" && spec) {
|
|
27581
|
+
testCode = generateMSWHandlers(spec);
|
|
27582
|
+
testFilePath = `tests/mocks/handlers.ts`;
|
|
27583
|
+
}
|
|
27584
|
+
return { testCode, testFilePath };
|
|
27585
|
+
}
|
|
27586
|
+
function generatePactTest(spec, consumer, provider) {
|
|
27587
|
+
const paths = spec.paths || {};
|
|
27588
|
+
const interactions = [];
|
|
27589
|
+
for (const [path26, methods] of Object.entries(paths)) {
|
|
27590
|
+
for (const [method, details] of Object.entries(methods)) {
|
|
27591
|
+
const interaction = generatePactInteraction(path26, method.toUpperCase(), details);
|
|
27592
|
+
interactions.push(interaction);
|
|
27593
|
+
}
|
|
27594
|
+
}
|
|
27595
|
+
return `import { Pact } from '@pact-foundation/pact';
|
|
27596
|
+
import { like, eachLike } from '@pact-foundation/pact/dsl/matchers';
|
|
27597
|
+
|
|
27598
|
+
describe('${consumer} <-> ${provider} Contract', () => {
|
|
27599
|
+
const provider = new Pact({
|
|
27600
|
+
consumer: '${consumer}',
|
|
27601
|
+
provider: '${provider}',
|
|
27602
|
+
port: 1234,
|
|
27603
|
+
log: './logs/pact.log',
|
|
27604
|
+
dir: './pacts',
|
|
27605
|
+
});
|
|
27606
|
+
|
|
27607
|
+
beforeAll(() => provider.setup());
|
|
27608
|
+
afterEach(() => provider.verify());
|
|
27609
|
+
afterAll(() => provider.finalize());
|
|
27610
|
+
|
|
27611
|
+
${interactions.join("\n\n")}
|
|
27612
|
+
});
|
|
27613
|
+
`;
|
|
27614
|
+
}
|
|
27615
|
+
function generatePactInteraction(path26, method, details) {
|
|
27616
|
+
const response = details.responses?.["200"] || details.responses?.["201"];
|
|
27617
|
+
const responseSchema = response?.content?.["application/json"]?.schema;
|
|
27618
|
+
const responseBody = responseSchema ? generateMatcherFromSchema(responseSchema) : "{}";
|
|
27619
|
+
return ` it('${method} ${path26}', async () => {
|
|
27620
|
+
await provider.addInteraction({
|
|
27621
|
+
state: 'resource exists',
|
|
27622
|
+
uponReceiving: '${method} request to ${path26}',
|
|
27623
|
+
withRequest: {
|
|
27624
|
+
method: '${method}',
|
|
27625
|
+
path: '${path26}',
|
|
27626
|
+
},
|
|
27627
|
+
willRespondWith: {
|
|
27628
|
+
status: 200,
|
|
27629
|
+
headers: { 'Content-Type': 'application/json' },
|
|
27630
|
+
body: ${responseBody},
|
|
27631
|
+
},
|
|
27632
|
+
});
|
|
27633
|
+
|
|
27634
|
+
// Make actual request and verify
|
|
27635
|
+
const response = await fetch(\`http://localhost:1234${path26}\`);
|
|
27636
|
+
expect(response.status).toBe(200);
|
|
27637
|
+
});`;
|
|
27638
|
+
}
|
|
27639
|
+
function generateMatcherFromSchema(schema) {
|
|
27640
|
+
if (schema.type === "object") {
|
|
27641
|
+
const properties = schema.properties || {};
|
|
27642
|
+
const matchers = [];
|
|
27643
|
+
for (const [key, prop] of Object.entries(properties)) {
|
|
27644
|
+
if (prop.type === "string") {
|
|
27645
|
+
matchers.push(`${key}: like('example')`);
|
|
27646
|
+
} else if (prop.type === "number") {
|
|
27647
|
+
matchers.push(`${key}: like(123)`);
|
|
27648
|
+
} else if (prop.type === "boolean") {
|
|
27649
|
+
matchers.push(`${key}: like(true)`);
|
|
27650
|
+
} else if (prop.type === "array") {
|
|
27651
|
+
matchers.push(`${key}: eachLike({ id: like('1') })`);
|
|
27652
|
+
}
|
|
27653
|
+
}
|
|
27654
|
+
return `{ ${matchers.join(", ")} }`;
|
|
27655
|
+
}
|
|
27656
|
+
return "{}";
|
|
27657
|
+
}
|
|
27658
|
+
function generateSupertestContract(apiDefinition) {
|
|
27659
|
+
const { endpoint, method, requestBody, responseBody } = apiDefinition;
|
|
27660
|
+
const requestExample = generateExampleFromSchema(requestBody);
|
|
27661
|
+
const responseExample = generateExampleFromSchema(responseBody);
|
|
27662
|
+
return `import request from 'supertest';
|
|
27663
|
+
import app from '../src/app';
|
|
27664
|
+
|
|
27665
|
+
describe('API Contract Tests', () => {
|
|
27666
|
+
it('${method} ${endpoint} should match contract', async () => {
|
|
27667
|
+
const response = await request(app)
|
|
27668
|
+
.${method.toLowerCase()}('${endpoint}')
|
|
27669
|
+
.send(${JSON.stringify(requestExample, null, 2)})
|
|
27670
|
+
.expect(200)
|
|
27671
|
+
.expect('Content-Type', /json/);
|
|
27672
|
+
|
|
27673
|
+
// Verify response structure
|
|
27674
|
+
expect(response.body).toMatchObject(${JSON.stringify(responseExample, null, 2)});
|
|
27675
|
+
});
|
|
27676
|
+
});
|
|
27677
|
+
`;
|
|
27678
|
+
}
|
|
27679
|
+
function generateMSWHandlers(spec) {
|
|
27680
|
+
const paths = spec.paths || {};
|
|
27681
|
+
const handlers = [];
|
|
27682
|
+
for (const [path26, methods] of Object.entries(paths)) {
|
|
27683
|
+
for (const [method, details] of Object.entries(methods)) {
|
|
27684
|
+
const handler = generateMSWHandler(path26, method, details);
|
|
27685
|
+
handlers.push(handler);
|
|
27686
|
+
}
|
|
27687
|
+
}
|
|
27688
|
+
return `import { rest } from 'msw';
|
|
27689
|
+
|
|
27690
|
+
export const handlers = [
|
|
27691
|
+
${handlers.join(",\n\n")}
|
|
27692
|
+
];
|
|
27693
|
+
`;
|
|
27694
|
+
}
|
|
27695
|
+
function generateMSWHandler(path26, method, details) {
|
|
27696
|
+
const response = details.responses?.["200"] || details.responses?.["201"];
|
|
27697
|
+
const responseSchema = response?.content?.["application/json"]?.schema;
|
|
27698
|
+
const responseExample = responseSchema ? generateExampleFromSchema(responseSchema.properties || {}) : {};
|
|
27699
|
+
return ` rest.${method.toLowerCase()}('${path26}', (req, res, ctx) => {
|
|
27700
|
+
return res(
|
|
27701
|
+
ctx.status(200),
|
|
27702
|
+
ctx.json(${JSON.stringify(responseExample, null, 2)})
|
|
27703
|
+
);
|
|
27704
|
+
})`;
|
|
27705
|
+
}
|
|
27706
|
+
function generateExampleFromSchema(schema) {
|
|
27707
|
+
if (typeof schema === "string") {
|
|
27708
|
+
return schema === "string" ? "example" : schema === "number" ? 123 : schema === "boolean" ? true : [];
|
|
27709
|
+
}
|
|
27710
|
+
const example = {};
|
|
27711
|
+
for (const [key, type] of Object.entries(schema)) {
|
|
27712
|
+
if (type === "string") {
|
|
27713
|
+
example[key] = "example";
|
|
27714
|
+
} else if (type === "number") {
|
|
27715
|
+
example[key] = 123;
|
|
27716
|
+
} else if (type === "boolean") {
|
|
27717
|
+
example[key] = true;
|
|
27718
|
+
} else if (type === "array") {
|
|
27719
|
+
example[key] = [];
|
|
27720
|
+
}
|
|
27721
|
+
}
|
|
27722
|
+
return example;
|
|
27723
|
+
}
|
|
27724
|
+
var init_contract = __esm({
|
|
27725
|
+
"src/testing/generators/contract.ts"() {
|
|
27726
|
+
"use strict";
|
|
27727
|
+
}
|
|
27728
|
+
});
|
|
27729
|
+
|
|
27730
|
+
// src/testing/analyzers/coverage.ts
|
|
27731
|
+
async function analyzeCoverage(options) {
|
|
27732
|
+
const { projectRoot, coverageData } = options;
|
|
27733
|
+
let coverage = coverageData;
|
|
27734
|
+
if (!coverage) {
|
|
27735
|
+
try {
|
|
27736
|
+
(0, import_child_process33.execSync)("pnpm test --coverage --reporter=json", {
|
|
27737
|
+
cwd: projectRoot,
|
|
27738
|
+
stdio: "pipe"
|
|
27739
|
+
});
|
|
27740
|
+
const coveragePath = import_path111.default.join(projectRoot, "coverage", "coverage-summary.json");
|
|
27741
|
+
const coverageContent = await import_promises20.default.readFile(coveragePath, "utf-8");
|
|
27742
|
+
coverage = JSON.parse(coverageContent);
|
|
27743
|
+
} catch (error2) {
|
|
27744
|
+
throw new Error(`Failed to generate coverage: ${error2}`);
|
|
27745
|
+
}
|
|
27746
|
+
}
|
|
27747
|
+
const total = coverage.total;
|
|
27748
|
+
return {
|
|
27749
|
+
totalCoverage: total.lines.pct,
|
|
27750
|
+
lineCoverage: total.lines.pct,
|
|
27751
|
+
functionCoverage: total.functions.pct,
|
|
27752
|
+
branchCoverage: total.branches.pct,
|
|
27753
|
+
statementCoverage: total.statements.pct
|
|
27754
|
+
};
|
|
27755
|
+
}
|
|
27756
|
+
var import_promises20, import_path111, import_child_process33;
|
|
27757
|
+
var init_coverage = __esm({
|
|
27758
|
+
"src/testing/analyzers/coverage.ts"() {
|
|
27759
|
+
"use strict";
|
|
27760
|
+
import_promises20 = __toESM(require("fs/promises"), 1);
|
|
27761
|
+
import_path111 = __toESM(require("path"), 1);
|
|
27762
|
+
import_child_process33 = require("child_process");
|
|
27763
|
+
}
|
|
27764
|
+
});
|
|
27765
|
+
|
|
27766
|
+
// src/testing/analyzers/complexity.ts
|
|
27767
|
+
async function analyzeComplexity(options) {
|
|
27768
|
+
const { code } = options;
|
|
27769
|
+
const metrics = calculateMetrics(code);
|
|
27770
|
+
const reasons = [];
|
|
27771
|
+
let isComplex = false;
|
|
27772
|
+
if (metrics.lines >= 50) {
|
|
27773
|
+
reasons.push("Function exceeds 50 lines");
|
|
27774
|
+
isComplex = true;
|
|
27775
|
+
}
|
|
27776
|
+
if (metrics.cyclomaticComplexity >= 10) {
|
|
27777
|
+
reasons.push("High cyclomatic complexity");
|
|
27778
|
+
isComplex = true;
|
|
27779
|
+
}
|
|
27780
|
+
if (metrics.nestingLevel >= 3) {
|
|
27781
|
+
reasons.push("Deep nesting level");
|
|
27782
|
+
isComplex = true;
|
|
27783
|
+
}
|
|
27784
|
+
if (metrics.externalDependencies > 0) {
|
|
27785
|
+
reasons.push("External API calls");
|
|
27786
|
+
isComplex = true;
|
|
27787
|
+
}
|
|
27788
|
+
if (code.includes("stripe") || code.includes("paypal") || code.includes("payment")) {
|
|
27789
|
+
reasons.push("Payment processing logic");
|
|
27790
|
+
isComplex = true;
|
|
27791
|
+
}
|
|
27792
|
+
if (code.includes("auth") || code.includes("jwt") || code.includes("session")) {
|
|
27793
|
+
reasons.push("Authentication logic");
|
|
27794
|
+
isComplex = true;
|
|
27795
|
+
}
|
|
27796
|
+
if (code.includes("db.transaction") || code.includes("BEGIN") || code.includes("COMMIT")) {
|
|
27797
|
+
reasons.push("Database transactions");
|
|
27798
|
+
isComplex = true;
|
|
27799
|
+
}
|
|
27800
|
+
if (code.includes("async") && code.includes("await")) {
|
|
27801
|
+
const awaitCount = (code.match(/await/g) || []).length;
|
|
27802
|
+
if (awaitCount > 3) {
|
|
27803
|
+
reasons.push("Multiple async operations");
|
|
27804
|
+
isComplex = true;
|
|
27805
|
+
}
|
|
27806
|
+
}
|
|
27807
|
+
return {
|
|
27808
|
+
complexity: isComplex ? "complex" : "simple",
|
|
27809
|
+
metrics,
|
|
27810
|
+
reasons
|
|
27811
|
+
};
|
|
27812
|
+
}
|
|
27813
|
+
function calculateMetrics(code) {
|
|
27814
|
+
const lines = code.split("\n").filter((line) => line.trim() !== "").length;
|
|
27815
|
+
const cyclomaticComplexity = calculateCyclomaticComplexity(code);
|
|
27816
|
+
const nestingLevel = calculateNestingLevel(code);
|
|
27817
|
+
const externalDependencies = countExternalDependencies(code);
|
|
27818
|
+
return {
|
|
27819
|
+
lines,
|
|
27820
|
+
cyclomaticComplexity,
|
|
27821
|
+
nestingLevel,
|
|
27822
|
+
externalDependencies
|
|
27823
|
+
};
|
|
27824
|
+
}
|
|
27825
|
+
function calculateCyclomaticComplexity(code) {
|
|
27826
|
+
let complexity = 1;
|
|
27827
|
+
const decisionPoints = [
|
|
27828
|
+
/\bif\b/g,
|
|
27829
|
+
/\belse\s+if\b/g,
|
|
27830
|
+
/\bfor\b/g,
|
|
27831
|
+
/\bwhile\b/g,
|
|
27832
|
+
/\bcase\b/g,
|
|
27833
|
+
/\bcatch\b/g,
|
|
27834
|
+
/\b\?\s*.*\s*:/g,
|
|
27835
|
+
// Ternary operator
|
|
27836
|
+
/\b&&\b/g,
|
|
27837
|
+
/\b\|\|\b/g
|
|
27838
|
+
];
|
|
27839
|
+
for (const pattern of decisionPoints) {
|
|
27840
|
+
const matches = code.match(pattern);
|
|
27841
|
+
if (matches) {
|
|
27842
|
+
complexity += matches.length;
|
|
27843
|
+
}
|
|
27844
|
+
}
|
|
27845
|
+
return complexity;
|
|
27846
|
+
}
|
|
27847
|
+
function calculateNestingLevel(code) {
|
|
27848
|
+
let maxNesting = 0;
|
|
27849
|
+
let currentNesting = 0;
|
|
27850
|
+
for (const char of code) {
|
|
27851
|
+
if (char === "{") {
|
|
27852
|
+
currentNesting++;
|
|
27853
|
+
maxNesting = Math.max(maxNesting, currentNesting);
|
|
27854
|
+
} else if (char === "}") {
|
|
27855
|
+
currentNesting--;
|
|
27856
|
+
}
|
|
27857
|
+
}
|
|
27858
|
+
return maxNesting;
|
|
27859
|
+
}
|
|
27860
|
+
function countExternalDependencies(code) {
|
|
27861
|
+
let count = 0;
|
|
27862
|
+
if (code.includes("fetch(") || code.includes("axios.") || code.includes("http.")) {
|
|
27863
|
+
count++;
|
|
27864
|
+
}
|
|
27865
|
+
const externalServices = ["stripe", "aws", "firebase", "sendgrid", "twilio"];
|
|
27866
|
+
for (const service of externalServices) {
|
|
27867
|
+
if (code.includes(service)) {
|
|
27868
|
+
count++;
|
|
27869
|
+
}
|
|
27870
|
+
}
|
|
27871
|
+
return count;
|
|
27872
|
+
}
|
|
27873
|
+
var init_complexity = __esm({
|
|
27874
|
+
"src/testing/analyzers/complexity.ts"() {
|
|
27875
|
+
"use strict";
|
|
27876
|
+
}
|
|
27877
|
+
});
|
|
27878
|
+
|
|
27879
|
+
// src/testing/generators/e2e.ts
|
|
27880
|
+
var e2e_exports = {};
|
|
27881
|
+
__export(e2e_exports, {
|
|
27882
|
+
generateFromUserFlow: () => generateFromUserFlow,
|
|
27883
|
+
generatePlaywrightTest: () => generatePlaywrightTest
|
|
27884
|
+
});
|
|
27885
|
+
function renderTemplate(template, vars) {
|
|
27886
|
+
return template.replace(/\{\{(\w+)\}\}/g, (_, key) => vars[key] || "");
|
|
27887
|
+
}
|
|
27888
|
+
async function generatePlaywrightTest(options) {
|
|
27889
|
+
const { steps, testName, testSuite = "E2E Tests" } = options;
|
|
27890
|
+
const testSteps = steps.map((step) => {
|
|
27891
|
+
const template = STEP_TEMPLATES[step.action];
|
|
27892
|
+
return renderTemplate(template, {
|
|
27893
|
+
target: step.target,
|
|
27894
|
+
value: step.value || "",
|
|
27895
|
+
assertion: step.assertion || ""
|
|
27896
|
+
});
|
|
27897
|
+
}).join("\n ");
|
|
27898
|
+
return renderTemplate(PLAYWRIGHT_TEST_TEMPLATE, {
|
|
27899
|
+
testSuite,
|
|
27900
|
+
testName,
|
|
27901
|
+
testSteps
|
|
27902
|
+
});
|
|
27903
|
+
}
|
|
27904
|
+
async function generateFromUserFlow(options) {
|
|
27905
|
+
const { flowDescription, baseUrl, testName } = options;
|
|
27906
|
+
const steps = [];
|
|
27907
|
+
const lowerFlow = flowDescription.toLowerCase();
|
|
27908
|
+
const actions = flowDescription.split(/,\s*/).map((s) => s.trim().toLowerCase());
|
|
27909
|
+
for (const action of actions) {
|
|
27910
|
+
if (action.includes("logs in") || action.includes("login")) {
|
|
27911
|
+
steps.push({ action: "goto", target: `${baseUrl}/login` });
|
|
27912
|
+
} else if (action.includes("navigates to") || action.includes("dashboard")) {
|
|
27913
|
+
steps.push({ action: "goto", target: `${baseUrl}/dashboard` });
|
|
27914
|
+
} else if (action.includes("clicks on") || action.includes("settings")) {
|
|
27915
|
+
steps.push({ action: "click", target: 'a[href="/settings"]' });
|
|
27916
|
+
}
|
|
27917
|
+
}
|
|
27918
|
+
const testCode = await generatePlaywrightTest({ steps, testName });
|
|
27919
|
+
return {
|
|
27920
|
+
testCode,
|
|
27921
|
+
steps
|
|
27922
|
+
};
|
|
27923
|
+
}
|
|
27924
|
+
var PLAYWRIGHT_TEST_TEMPLATE, STEP_TEMPLATES;
|
|
27925
|
+
var init_e2e = __esm({
|
|
27926
|
+
"src/testing/generators/e2e.ts"() {
|
|
27927
|
+
"use strict";
|
|
27928
|
+
PLAYWRIGHT_TEST_TEMPLATE = `import { test, expect } from '@playwright/test';
|
|
27929
|
+
|
|
27930
|
+
test.describe('{{testSuite}}', () => {
|
|
27931
|
+
test('{{testName}}', async ({ page }) => {
|
|
27932
|
+
{{testSteps}}
|
|
27933
|
+
});
|
|
27934
|
+
});
|
|
27935
|
+
`;
|
|
27936
|
+
STEP_TEMPLATES = {
|
|
27937
|
+
goto: "await page.goto('{{target}}');",
|
|
27938
|
+
click: "await page.click('{{target}}');",
|
|
27939
|
+
fill: "await page.fill('{{target}}', '{{value}}');",
|
|
27940
|
+
expect: "await expect(page.locator('{{target}}')).{{assertion}}();",
|
|
27941
|
+
waitFor: "await page.waitForSelector('{{target}}');",
|
|
27942
|
+
screenshot: "await page.screenshot({ path: '{{target}}' });"
|
|
27943
|
+
};
|
|
27944
|
+
}
|
|
27945
|
+
});
|
|
27946
|
+
|
|
27947
|
+
// src/testing/integrations/giskard/behavioral-tests.ts
|
|
27948
|
+
var behavioral_tests_exports = {};
|
|
27949
|
+
__export(behavioral_tests_exports, {
|
|
27950
|
+
generatePerturbationTests: () => generatePerturbationTests,
|
|
27951
|
+
generateRobustnessTests: () => generateRobustnessTests
|
|
27952
|
+
});
|
|
27953
|
+
async function generatePerturbationTests(options) {
|
|
27954
|
+
const { testCases, perturbations } = options;
|
|
27955
|
+
const tests = [];
|
|
27956
|
+
for (const testCase of testCases) {
|
|
27957
|
+
for (const perturbationType of perturbations) {
|
|
27958
|
+
const perturbed = applyPerturbation(testCase.input, perturbationType);
|
|
27959
|
+
tests.push({
|
|
27960
|
+
original: testCase.input,
|
|
27961
|
+
perturbed,
|
|
27962
|
+
perturbationType,
|
|
27963
|
+
expectedBehavior: `should still classify as ${testCase.expectedOutput}`,
|
|
27964
|
+
expectedOutput: testCase.expectedOutput
|
|
27965
|
+
});
|
|
27966
|
+
}
|
|
27967
|
+
}
|
|
27968
|
+
return {
|
|
27969
|
+
tests,
|
|
27970
|
+
totalTests: tests.length
|
|
27971
|
+
};
|
|
27972
|
+
}
|
|
27973
|
+
function applyPerturbation(text, type) {
|
|
27974
|
+
switch (type) {
|
|
27975
|
+
case "typo":
|
|
27976
|
+
const pos = Math.floor(Math.random() * (text.length - 1));
|
|
27977
|
+
return text.slice(0, pos) + text[pos + 1] + text[pos] + text.slice(pos + 2);
|
|
27978
|
+
case "negation":
|
|
27979
|
+
return `not ${text}`;
|
|
27980
|
+
case "synonym":
|
|
27981
|
+
return text.replace(/hello/gi, "hi").replace(/goodbye/gi, "bye");
|
|
27982
|
+
default:
|
|
27983
|
+
return text;
|
|
27984
|
+
}
|
|
27985
|
+
}
|
|
27986
|
+
async function generateRobustnessTests(options) {
|
|
27987
|
+
const { modelEndpoint, testInputs, robustnessChecks } = options;
|
|
27988
|
+
const checks = [];
|
|
27989
|
+
for (const checkType of robustnessChecks) {
|
|
27990
|
+
const testCases = testInputs.map((input) => ({
|
|
27991
|
+
input: applyRobustnessTransform(input, checkType),
|
|
27992
|
+
expected: input
|
|
27993
|
+
// Expected to behave same as original
|
|
27994
|
+
}));
|
|
27995
|
+
checks.push({
|
|
27996
|
+
type: checkType,
|
|
27997
|
+
testCases
|
|
27998
|
+
});
|
|
27999
|
+
}
|
|
28000
|
+
return {
|
|
28001
|
+
checks,
|
|
28002
|
+
totalChecks: checks.length
|
|
28003
|
+
};
|
|
28004
|
+
}
|
|
28005
|
+
function applyRobustnessTransform(text, type) {
|
|
28006
|
+
switch (type) {
|
|
28007
|
+
case "case-sensitivity":
|
|
28008
|
+
return text.toUpperCase();
|
|
28009
|
+
case "whitespace":
|
|
28010
|
+
return ` ${text} `;
|
|
28011
|
+
case "special-chars":
|
|
28012
|
+
return `${text}!!!`;
|
|
28013
|
+
default:
|
|
28014
|
+
return text;
|
|
28015
|
+
}
|
|
28016
|
+
}
|
|
28017
|
+
var init_behavioral_tests = __esm({
|
|
28018
|
+
"src/testing/integrations/giskard/behavioral-tests.ts"() {
|
|
28019
|
+
"use strict";
|
|
28020
|
+
}
|
|
28021
|
+
});
|
|
28022
|
+
|
|
28023
|
+
// src/testing/integrations/cicd.ts
|
|
28024
|
+
var cicd_exports = {};
|
|
28025
|
+
__export(cicd_exports, {
|
|
28026
|
+
generateGitHubActionsWorkflow: () => generateGitHubActionsWorkflow
|
|
28027
|
+
});
|
|
28028
|
+
async function generateGitHubActionsWorkflow(config2) {
|
|
28029
|
+
const {
|
|
28030
|
+
language,
|
|
28031
|
+
languages,
|
|
28032
|
+
testCommand: testCommand2,
|
|
28033
|
+
testCommands,
|
|
28034
|
+
nodeVersion = "20",
|
|
28035
|
+
pythonVersion = "3.11",
|
|
28036
|
+
goVersion = "1.21",
|
|
28037
|
+
rustVersion = "stable",
|
|
28038
|
+
coverage = false,
|
|
28039
|
+
coverageProvider = "codecov",
|
|
28040
|
+
artifacts = false,
|
|
28041
|
+
artifactPath = "test-results/",
|
|
28042
|
+
matrix
|
|
28043
|
+
} = config2;
|
|
28044
|
+
const runsOn = matrix?.os ? "${{ matrix.os }}" : "ubuntu-latest";
|
|
28045
|
+
let workflow = `name: Test
|
|
28046
|
+
|
|
28047
|
+
on:
|
|
28048
|
+
push:
|
|
28049
|
+
branches: [main, dev]
|
|
28050
|
+
pull_request:
|
|
28051
|
+
branches: [main, dev]
|
|
28052
|
+
|
|
28053
|
+
jobs:
|
|
28054
|
+
test:
|
|
28055
|
+
runs-on: ${runsOn}
|
|
28056
|
+
`;
|
|
28057
|
+
if (matrix) {
|
|
28058
|
+
workflow += ` strategy:
|
|
28059
|
+
matrix:
|
|
28060
|
+
`;
|
|
28061
|
+
if (matrix.nodeVersion) {
|
|
28062
|
+
workflow += ` node-version: [${matrix.nodeVersion.join(", ")}]
|
|
28063
|
+
`;
|
|
28064
|
+
}
|
|
28065
|
+
if (matrix.pythonVersion) {
|
|
28066
|
+
workflow += ` python-version: [${matrix.pythonVersion.join(", ")}]
|
|
28067
|
+
`;
|
|
28068
|
+
}
|
|
28069
|
+
if (matrix.goVersion) {
|
|
28070
|
+
workflow += ` go-version: [${matrix.goVersion.join(", ")}]
|
|
28071
|
+
`;
|
|
28072
|
+
}
|
|
28073
|
+
if (matrix.os) {
|
|
28074
|
+
workflow += ` os: [${matrix.os.join(", ")}]
|
|
28075
|
+
`;
|
|
28076
|
+
}
|
|
28077
|
+
if (matrix.language) {
|
|
28078
|
+
workflow += ` language: [${matrix.language.join(", ")}]
|
|
28079
|
+
`;
|
|
28080
|
+
}
|
|
28081
|
+
}
|
|
28082
|
+
workflow += ` steps:
|
|
28083
|
+
- uses: actions/checkout@v4
|
|
28084
|
+
|
|
28085
|
+
`;
|
|
28086
|
+
if (languages && matrix?.language) {
|
|
28087
|
+
const matrixLang = "${{ matrix.language }}";
|
|
28088
|
+
workflow += ` - name: Setup Node.js
|
|
28089
|
+
if: matrix.language == 'nodejs'
|
|
28090
|
+
uses: actions/setup-node@v4
|
|
28091
|
+
with:
|
|
28092
|
+
node-version: ${nodeVersion}
|
|
28093
|
+
cache: 'pnpm'
|
|
28094
|
+
|
|
28095
|
+
- name: Setup Python
|
|
28096
|
+
if: matrix.language == 'python'
|
|
28097
|
+
uses: actions/setup-python@v5
|
|
28098
|
+
with:
|
|
28099
|
+
python-version: ${pythonVersion}
|
|
28100
|
+
|
|
28101
|
+
- name: Setup Go
|
|
28102
|
+
if: matrix.language == 'go'
|
|
28103
|
+
uses: actions/setup-go@v5
|
|
28104
|
+
with:
|
|
28105
|
+
go-version: ${goVersion}
|
|
28106
|
+
|
|
28107
|
+
- name: Install Node.js dependencies
|
|
28108
|
+
if: matrix.language == 'nodejs'
|
|
28109
|
+
run: pnpm install
|
|
28110
|
+
|
|
28111
|
+
- name: Install Python dependencies
|
|
28112
|
+
if: matrix.language == 'python'
|
|
28113
|
+
run: pip install -r requirements.txt
|
|
28114
|
+
|
|
28115
|
+
- name: Run tests
|
|
28116
|
+
run: |
|
|
28117
|
+
if [ "${matrixLang}" == "nodejs" ]; then
|
|
28118
|
+
${testCommands?.nodejs || "pnpm test"}
|
|
28119
|
+
elif [ "${matrixLang}" == "python" ]; then
|
|
28120
|
+
${testCommands?.python || "pytest"}
|
|
28121
|
+
elif [ "${matrixLang}" == "go" ]; then
|
|
28122
|
+
${testCommands?.go || "go test ./..."}
|
|
28123
|
+
fi
|
|
28124
|
+
`;
|
|
28125
|
+
} else {
|
|
28126
|
+
if (language === "nodejs" || !language && testCommand2?.includes("pnpm")) {
|
|
28127
|
+
const nodeVer = matrix?.nodeVersion ? "${{ matrix.node-version }}" : nodeVersion;
|
|
28128
|
+
workflow += ` - name: Setup Node.js
|
|
28129
|
+
uses: actions/setup-node@v4
|
|
28130
|
+
with:
|
|
28131
|
+
node-version: ${nodeVer}
|
|
28132
|
+
cache: 'pnpm'
|
|
28133
|
+
|
|
28134
|
+
- name: Install dependencies
|
|
28135
|
+
run: pnpm install
|
|
28136
|
+
|
|
28137
|
+
- name: Run tests
|
|
28138
|
+
run: ${testCommand2 || "pnpm test"}
|
|
28139
|
+
`;
|
|
28140
|
+
} else if (language === "python") {
|
|
28141
|
+
const pyVer = matrix?.pythonVersion ? "${{ matrix.python-version }}" : pythonVersion;
|
|
28142
|
+
workflow += ` - name: Setup Python
|
|
28143
|
+
uses: actions/setup-python@v5
|
|
28144
|
+
with:
|
|
28145
|
+
python-version: ${pyVer}
|
|
28146
|
+
|
|
28147
|
+
- name: Install dependencies
|
|
28148
|
+
run: pip install -r requirements.txt
|
|
28149
|
+
|
|
28150
|
+
- name: Run tests
|
|
28151
|
+
run: ${testCommand2 || "pytest"}
|
|
28152
|
+
`;
|
|
28153
|
+
} else if (language === "go") {
|
|
28154
|
+
const goVer = matrix?.goVersion ? "${{ matrix.go-version }}" : goVersion;
|
|
28155
|
+
workflow += ` - name: Setup Go
|
|
28156
|
+
uses: actions/setup-go@v5
|
|
28157
|
+
with:
|
|
28158
|
+
go-version: ${goVer}
|
|
28159
|
+
|
|
28160
|
+
- name: Run tests
|
|
28161
|
+
run: ${testCommand2 || "go test ./..."}
|
|
28162
|
+
`;
|
|
28163
|
+
} else if (language === "rust") {
|
|
28164
|
+
workflow += ` - name: Setup Rust
|
|
28165
|
+
uses: actions-rust-lang/setup-rust-toolchain@v1
|
|
28166
|
+
with:
|
|
28167
|
+
toolchain: ${rustVersion}
|
|
28168
|
+
|
|
28169
|
+
- name: Run tests
|
|
28170
|
+
run: ${testCommand2 || "cargo test"}
|
|
28171
|
+
`;
|
|
28172
|
+
}
|
|
28173
|
+
}
|
|
28174
|
+
if (coverage) {
|
|
28175
|
+
const token = "${{ secrets.CODECOV_TOKEN }}";
|
|
28176
|
+
workflow += `
|
|
28177
|
+
- name: Upload coverage
|
|
28178
|
+
uses: ${coverageProvider}/codecov-action@v4
|
|
28179
|
+
with:
|
|
28180
|
+
token: ${token}
|
|
28181
|
+
`;
|
|
28182
|
+
}
|
|
28183
|
+
if (artifacts) {
|
|
28184
|
+
workflow += `
|
|
28185
|
+
- name: Upload test results
|
|
28186
|
+
if: always()
|
|
28187
|
+
uses: actions/upload-artifact@v4
|
|
28188
|
+
with:
|
|
28189
|
+
name: test-results
|
|
28190
|
+
path: ${artifactPath}
|
|
28191
|
+
`;
|
|
28192
|
+
}
|
|
28193
|
+
return workflow;
|
|
28194
|
+
}
|
|
28195
|
+
var init_cicd = __esm({
|
|
28196
|
+
"src/testing/integrations/cicd.ts"() {
|
|
28197
|
+
"use strict";
|
|
28198
|
+
}
|
|
28199
|
+
});
|
|
28200
|
+
|
|
28201
|
+
// src/testing/analyzers/quality-scorer.ts
|
|
28202
|
+
var quality_scorer_exports = {};
|
|
28203
|
+
__export(quality_scorer_exports, {
|
|
28204
|
+
scoreTestQuality: () => scoreTestQuality
|
|
28205
|
+
});
|
|
28206
|
+
async function scoreTestQuality(options) {
|
|
28207
|
+
const { testCode } = options;
|
|
28208
|
+
const assertionCount = (testCode.match(/expect\(/g) || []).length;
|
|
28209
|
+
const hasAssertions = assertionCount > 0;
|
|
28210
|
+
const hasEdgeCases = testCode.includes("null") || testCode.includes("undefined") || testCode.includes("0") || testCode.includes("empty") || testCode.includes("-1") || testCode.includes("''") || testCode.includes('""');
|
|
28211
|
+
const specificAssertionPatterns = [
|
|
28212
|
+
/\.toBe\(/g,
|
|
28213
|
+
/\.toEqual\(/g,
|
|
28214
|
+
/\.toStrictEqual\(/g,
|
|
28215
|
+
/\.toMatch\(/g,
|
|
28216
|
+
/\.toContain\(/g,
|
|
28217
|
+
/\.toHaveLength\(/g
|
|
28218
|
+
];
|
|
28219
|
+
const specificAssertionCount = specificAssertionPatterns.reduce(
|
|
28220
|
+
(count, pattern) => count + (testCode.match(pattern) || []).length,
|
|
28221
|
+
0
|
|
28222
|
+
);
|
|
28223
|
+
const hasSpecificAssertions = specificAssertionCount > 0;
|
|
28224
|
+
const hasSharedState = testCode.includes("let ") && !testCode.match(/test\([^)]*\)\s*=>\s*\{[^}]*let /s) || testCode.includes("var ") && !testCode.match(/test\([^)]*\)\s*=>\s*\{[^}]*var /s);
|
|
28225
|
+
const testNames = testCode.match(/test\(['"]([^'"]+)['"]/g) || [];
|
|
28226
|
+
const hasDescriptiveNames = testNames.every((name) => {
|
|
28227
|
+
const testName = name.match(/test\(['"]([^'"]+)['"]/)?.[1] || "";
|
|
28228
|
+
return testName.length > 15 && (testName.includes("should") || testName.includes("when"));
|
|
28229
|
+
});
|
|
28230
|
+
const hasMocks = testCode.includes("mock") || testCode.includes("spy") || testCode.includes("stub");
|
|
28231
|
+
const hasSetup = testCode.includes("beforeEach") || testCode.includes("beforeAll");
|
|
28232
|
+
const hasTeardown = testCode.includes("afterEach") || testCode.includes("afterAll");
|
|
28233
|
+
const metrics = {
|
|
28234
|
+
assertionCount,
|
|
28235
|
+
hasEdgeCases,
|
|
28236
|
+
hasAssertions,
|
|
28237
|
+
hasSpecificAssertions,
|
|
28238
|
+
hasSharedState,
|
|
28239
|
+
hasDescriptiveNames,
|
|
28240
|
+
hasMocks,
|
|
28241
|
+
hasSetup,
|
|
28242
|
+
hasTeardown
|
|
28243
|
+
};
|
|
28244
|
+
let completenessScore = 0;
|
|
28245
|
+
if (hasAssertions) completenessScore += 40;
|
|
28246
|
+
completenessScore += Math.min(assertionCount * 10, 30);
|
|
28247
|
+
if (hasEdgeCases) completenessScore += 15;
|
|
28248
|
+
if (hasMocks) completenessScore += 10;
|
|
28249
|
+
if (hasSetup || hasTeardown) completenessScore += 5;
|
|
28250
|
+
let assertionQuality = 0;
|
|
28251
|
+
if (hasAssertions) {
|
|
28252
|
+
assertionQuality += 50;
|
|
28253
|
+
if (hasSpecificAssertions) {
|
|
28254
|
+
const specificRatio = specificAssertionCount / assertionCount;
|
|
28255
|
+
assertionQuality += specificRatio * 50;
|
|
28256
|
+
}
|
|
28257
|
+
}
|
|
28258
|
+
let independenceScore = 100;
|
|
28259
|
+
if (hasSharedState) independenceScore -= 40;
|
|
28260
|
+
if (!hasSetup && testCode.includes("const ") && testCode.split("const ").length > 3) {
|
|
28261
|
+
independenceScore -= 10;
|
|
28262
|
+
}
|
|
28263
|
+
let namingScore = 0;
|
|
28264
|
+
if (testNames.length > 0) {
|
|
28265
|
+
namingScore = hasDescriptiveNames ? 90 : 50;
|
|
28266
|
+
if (testNames.some((name) => name.includes("should"))) namingScore += 10;
|
|
28267
|
+
}
|
|
28268
|
+
const overallScore = Math.round(
|
|
28269
|
+
completenessScore * 0.35 + assertionQuality * 0.25 + independenceScore * 0.2 + namingScore * 0.2
|
|
28270
|
+
);
|
|
28271
|
+
const recommendations = [];
|
|
28272
|
+
if (!hasAssertions) {
|
|
28273
|
+
recommendations.push("Add assertions to verify behavior");
|
|
28274
|
+
}
|
|
28275
|
+
if (assertionCount < 2) {
|
|
28276
|
+
recommendations.push("Add more test cases to cover edge cases");
|
|
28277
|
+
}
|
|
28278
|
+
if (!hasEdgeCases) {
|
|
28279
|
+
recommendations.push("Test edge cases like null, undefined, empty values, and boundary conditions");
|
|
28280
|
+
}
|
|
28281
|
+
if (!hasSpecificAssertions) {
|
|
28282
|
+
recommendations.push("Use specific assertions (toBe, toEqual) instead of generic truthy checks");
|
|
28283
|
+
}
|
|
28284
|
+
if (hasSharedState) {
|
|
28285
|
+
recommendations.push("Avoid shared state between tests - use beforeEach for setup");
|
|
28286
|
+
}
|
|
28287
|
+
if (!hasDescriptiveNames) {
|
|
28288
|
+
recommendations.push("Use descriptive test names that explain what is being tested");
|
|
28289
|
+
}
|
|
28290
|
+
if (assertionQuality < 60) {
|
|
28291
|
+
recommendations.push("Improve assertion quality with more specific matchers");
|
|
28292
|
+
}
|
|
28293
|
+
return {
|
|
28294
|
+
completenessScore: Math.min(completenessScore, 100),
|
|
28295
|
+
assertionQuality: Math.min(assertionQuality, 100),
|
|
28296
|
+
independenceScore: Math.min(independenceScore, 100),
|
|
28297
|
+
namingScore: Math.min(namingScore, 100),
|
|
28298
|
+
overallScore: Math.min(overallScore, 100),
|
|
28299
|
+
assertionCount,
|
|
28300
|
+
hasEdgeCases,
|
|
28301
|
+
hasAssertions,
|
|
28302
|
+
hasSpecificAssertions,
|
|
28303
|
+
hasSharedState,
|
|
28304
|
+
hasDescriptiveNames,
|
|
28305
|
+
metrics,
|
|
28306
|
+
recommendations
|
|
28307
|
+
};
|
|
28308
|
+
}
|
|
28309
|
+
var init_quality_scorer = __esm({
|
|
28310
|
+
"src/testing/analyzers/quality-scorer.ts"() {
|
|
28311
|
+
"use strict";
|
|
28312
|
+
}
|
|
28313
|
+
});
|
|
28314
|
+
|
|
28315
|
+
// src/testing/cli/commands.ts
|
|
28316
|
+
var commands_exports = {};
|
|
28317
|
+
__export(commands_exports, {
|
|
28318
|
+
testAnalyzeCoverageCommand: () => testAnalyzeCoverageCommand,
|
|
28319
|
+
testCICDCommand: () => testCICDCommand,
|
|
28320
|
+
testComplexityCommand: () => testComplexityCommand,
|
|
28321
|
+
testContractCommand: () => testContractCommand,
|
|
28322
|
+
testDetectStackCommand: () => testDetectStackCommand,
|
|
28323
|
+
testE2ECommand: () => testE2ECommand,
|
|
28324
|
+
testGenCommand: () => testGenCommand,
|
|
28325
|
+
testGiskardCommand: () => testGiskardCommand,
|
|
28326
|
+
testQualityCommand: () => testQualityCommand
|
|
28327
|
+
});
|
|
28328
|
+
function detectLanguage(filePath) {
|
|
28329
|
+
if (filePath.match(/\.(tsx|jsx)$/)) return "react";
|
|
28330
|
+
if (filePath.match(/\.ts$/)) return "typescript";
|
|
28331
|
+
if (filePath.match(/\.py$/)) return "python";
|
|
28332
|
+
if (filePath.match(/\.go$/)) return "go";
|
|
28333
|
+
if (filePath.match(/\.rs$/)) return "rust";
|
|
28334
|
+
return null;
|
|
28335
|
+
}
|
|
28336
|
+
async function testGenCommand(options) {
|
|
28337
|
+
try {
|
|
28338
|
+
const { filePath, output } = options;
|
|
28339
|
+
const language = options.language || detectLanguage(filePath);
|
|
28340
|
+
if (!language) {
|
|
28341
|
+
return {
|
|
28342
|
+
success: false,
|
|
28343
|
+
error: "Unsupported file type"
|
|
28344
|
+
};
|
|
28345
|
+
}
|
|
28346
|
+
const code = await import_promises21.default.readFile(filePath, "utf-8");
|
|
28347
|
+
const projectRoot = process.cwd();
|
|
28348
|
+
const stack = await detectTechStack(projectRoot);
|
|
28349
|
+
let result;
|
|
28350
|
+
switch (language) {
|
|
28351
|
+
case "react": {
|
|
28352
|
+
const detectedFramework = stack.frontend?.testFramework;
|
|
28353
|
+
const testFramework = detectedFramework === "jest" ? "jest" : "vitest";
|
|
28354
|
+
result = await generateReactTest({ filePath, code, testFramework });
|
|
28355
|
+
break;
|
|
28356
|
+
}
|
|
28357
|
+
case "typescript": {
|
|
28358
|
+
const detectedFramework = stack.backend?.testFramework;
|
|
28359
|
+
const testFramework = detectedFramework === "jest" ? "jest" : "vitest";
|
|
28360
|
+
result = await generateNodeJsTest({ filePath, code, testFramework });
|
|
28361
|
+
break;
|
|
28362
|
+
}
|
|
28363
|
+
case "python": {
|
|
28364
|
+
const testFramework = stack.backend?.testFramework === "unittest" ? "unittest" : "pytest";
|
|
28365
|
+
result = await generatePythonTest({ filePath, code, testFramework });
|
|
28366
|
+
break;
|
|
28367
|
+
}
|
|
28368
|
+
case "go": {
|
|
28369
|
+
const packageName = import_path112.default.basename(import_path112.default.dirname(filePath));
|
|
28370
|
+
result = await generateGoTest({ filePath, code, packageName });
|
|
28371
|
+
break;
|
|
28372
|
+
}
|
|
28373
|
+
case "rust": {
|
|
28374
|
+
result = await generateRustTest({ filePath, code });
|
|
28375
|
+
break;
|
|
28376
|
+
}
|
|
28377
|
+
default:
|
|
28378
|
+
return {
|
|
28379
|
+
success: false,
|
|
28380
|
+
error: "Unsupported file type"
|
|
28381
|
+
};
|
|
28382
|
+
}
|
|
28383
|
+
const testFilePath = output || result.testFilePath;
|
|
28384
|
+
await import_promises21.default.writeFile(testFilePath, result.testCode, "utf-8");
|
|
28385
|
+
return {
|
|
28386
|
+
success: true,
|
|
28387
|
+
testFilePath
|
|
28388
|
+
};
|
|
28389
|
+
} catch (error2) {
|
|
28390
|
+
return {
|
|
28391
|
+
success: false,
|
|
28392
|
+
error: error2 instanceof Error ? error2.message : "Unknown error"
|
|
28393
|
+
};
|
|
28394
|
+
}
|
|
28395
|
+
}
|
|
28396
|
+
async function testDetectStackCommand(options) {
|
|
28397
|
+
const stack = await detectTechStack(options.projectRoot);
|
|
28398
|
+
return { stack };
|
|
28399
|
+
}
|
|
28400
|
+
async function testAnalyzeCoverageCommand(options) {
|
|
28401
|
+
return analyzeCoverage({
|
|
28402
|
+
projectRoot: options.projectRoot,
|
|
28403
|
+
coverageData: options.coverageData
|
|
28404
|
+
});
|
|
28405
|
+
}
|
|
28406
|
+
async function testContractCommand(options) {
|
|
28407
|
+
const { spec, specPath, framework, consumer, provider } = options;
|
|
28408
|
+
let specData = spec;
|
|
28409
|
+
if (!specData) {
|
|
28410
|
+
const content = await import_promises21.default.readFile(specPath, "utf-8");
|
|
28411
|
+
specData = JSON.parse(content);
|
|
28412
|
+
}
|
|
28413
|
+
const result = await generateContractTest({
|
|
28414
|
+
spec: specData,
|
|
28415
|
+
framework,
|
|
28416
|
+
consumer,
|
|
28417
|
+
provider
|
|
28418
|
+
});
|
|
28419
|
+
return {
|
|
28420
|
+
success: true,
|
|
28421
|
+
testCode: result.testCode,
|
|
28422
|
+
testFilePath: result.testFilePath
|
|
28423
|
+
};
|
|
28424
|
+
}
|
|
28425
|
+
async function testComplexityCommand(options) {
|
|
28426
|
+
const code = await import_promises21.default.readFile(options.filePath, "utf-8");
|
|
28427
|
+
return analyzeComplexity({ code, filePath: options.filePath });
|
|
28428
|
+
}
|
|
28429
|
+
async function testE2ECommand(options) {
|
|
28430
|
+
try {
|
|
28431
|
+
const { generateFromUserFlow: generateFromUserFlow2 } = await Promise.resolve().then(() => (init_e2e(), e2e_exports));
|
|
28432
|
+
const result = await generateFromUserFlow2({
|
|
28433
|
+
flowDescription: options.flowDescription,
|
|
28434
|
+
baseUrl: options.baseUrl || "http://localhost:3000",
|
|
28435
|
+
testName: options.testName || "User flow test"
|
|
28436
|
+
});
|
|
28437
|
+
const testFilePath = options.output || "./tests/e2e/user-flow.spec.ts";
|
|
28438
|
+
await import_promises21.default.writeFile(testFilePath, result.testCode, "utf-8");
|
|
28439
|
+
return {
|
|
28440
|
+
success: true,
|
|
28441
|
+
testFilePath
|
|
28442
|
+
};
|
|
28443
|
+
} catch (error2) {
|
|
28444
|
+
return {
|
|
28445
|
+
success: false,
|
|
28446
|
+
error: error2 instanceof Error ? error2.message : "Unknown error"
|
|
28447
|
+
};
|
|
28448
|
+
}
|
|
28449
|
+
}
|
|
28450
|
+
async function testGiskardCommand(options) {
|
|
28451
|
+
try {
|
|
28452
|
+
const { generatePerturbationTests: generatePerturbationTests2 } = await Promise.resolve().then(() => (init_behavioral_tests(), behavioral_tests_exports));
|
|
28453
|
+
const code = await import_promises21.default.readFile(options.filePath, "utf-8");
|
|
28454
|
+
const result = await generatePerturbationTests2({
|
|
28455
|
+
testCases: [
|
|
28456
|
+
{ input: "sample input", expectedOutput: "expected" }
|
|
28457
|
+
],
|
|
28458
|
+
perturbations: ["typo", "negation", "synonym"]
|
|
28459
|
+
});
|
|
28460
|
+
const testFilePath = options.output || "./tests/behavioral/perturbation.test.ts";
|
|
28461
|
+
const testCode = `// Generated Giskard behavioral tests
|
|
28462
|
+
import { describe, it, expect } from 'vitest';
|
|
28463
|
+
|
|
28464
|
+
describe('Behavioral Tests', () => {
|
|
28465
|
+
${result.tests.map((test) => `
|
|
28466
|
+
it('${test.expectedBehavior}', async () => {
|
|
28467
|
+
// Original: ${test.original}
|
|
28468
|
+
// Perturbed (${test.perturbationType}): ${test.perturbed}
|
|
28469
|
+
// TODO: Add test implementation
|
|
28470
|
+
});
|
|
28471
|
+
`).join("")}
|
|
28472
|
+
});
|
|
28473
|
+
`;
|
|
28474
|
+
await import_promises21.default.writeFile(testFilePath, testCode, "utf-8");
|
|
28475
|
+
return {
|
|
28476
|
+
success: true,
|
|
28477
|
+
testFilePath
|
|
28478
|
+
};
|
|
28479
|
+
} catch (error2) {
|
|
28480
|
+
return {
|
|
28481
|
+
success: false,
|
|
28482
|
+
error: error2 instanceof Error ? error2.message : "Unknown error"
|
|
28483
|
+
};
|
|
28484
|
+
}
|
|
28485
|
+
}
|
|
28486
|
+
async function testCICDCommand(options) {
|
|
28487
|
+
try {
|
|
28488
|
+
const { generateGitHubActionsWorkflow: generateGitHubActionsWorkflow2 } = await Promise.resolve().then(() => (init_cicd(), cicd_exports));
|
|
28489
|
+
const workflow = await generateGitHubActionsWorkflow2({
|
|
28490
|
+
language: options.language || "nodejs",
|
|
28491
|
+
coverage: true,
|
|
28492
|
+
artifacts: true
|
|
28493
|
+
});
|
|
28494
|
+
const workflowPath = options.output || "./.github/workflows/test.yml";
|
|
28495
|
+
const dir = import_path112.default.dirname(workflowPath);
|
|
28496
|
+
await import_promises21.default.mkdir(dir, { recursive: true });
|
|
28497
|
+
await import_promises21.default.writeFile(workflowPath, workflow, "utf-8");
|
|
28498
|
+
return {
|
|
28499
|
+
success: true,
|
|
28500
|
+
workflowPath
|
|
28501
|
+
};
|
|
28502
|
+
} catch (error2) {
|
|
28503
|
+
return {
|
|
28504
|
+
success: false,
|
|
28505
|
+
error: error2 instanceof Error ? error2.message : "Unknown error"
|
|
28506
|
+
};
|
|
28507
|
+
}
|
|
28508
|
+
}
|
|
28509
|
+
async function testQualityCommand(options) {
|
|
28510
|
+
try {
|
|
28511
|
+
const { scoreTestQuality: scoreTestQuality2 } = await Promise.resolve().then(() => (init_quality_scorer(), quality_scorer_exports));
|
|
28512
|
+
const testCode = await import_promises21.default.readFile(options.testFilePath, "utf-8");
|
|
28513
|
+
const score = await scoreTestQuality2({
|
|
28514
|
+
testCode,
|
|
28515
|
+
testType: options.testType || "unit"
|
|
28516
|
+
});
|
|
28517
|
+
return {
|
|
28518
|
+
success: true,
|
|
28519
|
+
score
|
|
28520
|
+
};
|
|
28521
|
+
} catch (error2) {
|
|
28522
|
+
return {
|
|
28523
|
+
success: false,
|
|
28524
|
+
error: error2 instanceof Error ? error2.message : "Unknown error"
|
|
28525
|
+
};
|
|
28526
|
+
}
|
|
28527
|
+
}
|
|
28528
|
+
var import_promises21, import_path112;
|
|
28529
|
+
var init_commands = __esm({
|
|
28530
|
+
"src/testing/cli/commands.ts"() {
|
|
28531
|
+
"use strict";
|
|
28532
|
+
import_promises21 = __toESM(require("fs/promises"), 1);
|
|
28533
|
+
import_path112 = __toESM(require("path"), 1);
|
|
28534
|
+
init_detectors();
|
|
28535
|
+
init_react();
|
|
28536
|
+
init_nodejs();
|
|
28537
|
+
init_python2();
|
|
28538
|
+
init_go2();
|
|
28539
|
+
init_rust2();
|
|
28540
|
+
init_contract();
|
|
28541
|
+
init_coverage();
|
|
28542
|
+
init_complexity();
|
|
28543
|
+
}
|
|
28544
|
+
});
|
|
28545
|
+
|
|
26823
28546
|
// node_modules/commander/esm.mjs
|
|
26824
28547
|
var import_index = __toESM(require_commander(), 1);
|
|
26825
28548
|
var {
|
|
@@ -27334,7 +29057,7 @@ var source_default = chalk;
|
|
|
27334
29057
|
|
|
27335
29058
|
// src/cli/index.ts
|
|
27336
29059
|
var import_fs92 = require("fs");
|
|
27337
|
-
var
|
|
29060
|
+
var import_path113 = require("path");
|
|
27338
29061
|
var import_url15 = require("url");
|
|
27339
29062
|
init_loader();
|
|
27340
29063
|
init_models();
|
|
@@ -31354,8 +33077,8 @@ var require_schemes = __commonJS2((exports2, module2) => {
|
|
|
31354
33077
|
wsComponents.secure = void 0;
|
|
31355
33078
|
}
|
|
31356
33079
|
if (wsComponents.resourceName) {
|
|
31357
|
-
const [
|
|
31358
|
-
wsComponents.path =
|
|
33080
|
+
const [path26, query] = wsComponents.resourceName.split("?");
|
|
33081
|
+
wsComponents.path = path26 && path26 !== "/" ? path26 : void 0;
|
|
31359
33082
|
wsComponents.query = query;
|
|
31360
33083
|
wsComponents.resourceName = void 0;
|
|
31361
33084
|
}
|
|
@@ -34390,12 +36113,12 @@ var require_dist = __commonJS2((exports2, module2) => {
|
|
|
34390
36113
|
throw new Error(`Unknown format "${name}"`);
|
|
34391
36114
|
return f;
|
|
34392
36115
|
};
|
|
34393
|
-
function addFormats(ajv, list,
|
|
36116
|
+
function addFormats(ajv, list, fs25, exportName) {
|
|
34394
36117
|
var _a;
|
|
34395
36118
|
var _b;
|
|
34396
36119
|
(_a = (_b = ajv.opts.code).formats) !== null && _a !== void 0 || (_b.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`);
|
|
34397
36120
|
for (const f of list)
|
|
34398
|
-
ajv.addFormat(f,
|
|
36121
|
+
ajv.addFormat(f, fs25[f]);
|
|
34399
36122
|
}
|
|
34400
36123
|
module2.exports = exports2 = formatsPlugin;
|
|
34401
36124
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
@@ -35057,11 +36780,11 @@ function getDebugWriter() {
|
|
|
35057
36780
|
if (!debugWriter) {
|
|
35058
36781
|
debugWriter = createBufferedWriter({
|
|
35059
36782
|
writeFn: (content) => {
|
|
35060
|
-
const
|
|
35061
|
-
if (!getFsImplementation().existsSync((0, import_path6.dirname)(
|
|
35062
|
-
getFsImplementation().mkdirSync((0, import_path6.dirname)(
|
|
36783
|
+
const path26 = getDebugLogPath();
|
|
36784
|
+
if (!getFsImplementation().existsSync((0, import_path6.dirname)(path26))) {
|
|
36785
|
+
getFsImplementation().mkdirSync((0, import_path6.dirname)(path26));
|
|
35063
36786
|
}
|
|
35064
|
-
getFsImplementation().appendFileSync(
|
|
36787
|
+
getFsImplementation().appendFileSync(path26, content);
|
|
35065
36788
|
updateLatestDebugLogSymlink();
|
|
35066
36789
|
},
|
|
35067
36790
|
flushIntervalMs: 1e3,
|
|
@@ -35163,40 +36886,40 @@ var NodeFsOperations = {
|
|
|
35163
36886
|
}
|
|
35164
36887
|
});
|
|
35165
36888
|
},
|
|
35166
|
-
appendFileSync(
|
|
35167
|
-
return withSlowLogging2(`appendFileSync(${
|
|
35168
|
-
if (!fs.existsSync(
|
|
35169
|
-
const fd = fs.openSync(
|
|
36889
|
+
appendFileSync(path26, data, options) {
|
|
36890
|
+
return withSlowLogging2(`appendFileSync(${path26}, ${data.length} chars)`, () => {
|
|
36891
|
+
if (!fs.existsSync(path26) && options?.mode !== void 0) {
|
|
36892
|
+
const fd = fs.openSync(path26, "a", options.mode);
|
|
35170
36893
|
try {
|
|
35171
36894
|
fs.appendFileSync(fd, data);
|
|
35172
36895
|
} finally {
|
|
35173
36896
|
fs.closeSync(fd);
|
|
35174
36897
|
}
|
|
35175
36898
|
} else {
|
|
35176
|
-
fs.appendFileSync(
|
|
36899
|
+
fs.appendFileSync(path26, data);
|
|
35177
36900
|
}
|
|
35178
36901
|
});
|
|
35179
36902
|
},
|
|
35180
36903
|
copyFileSync(src, dest) {
|
|
35181
36904
|
return withSlowLogging2(`copyFileSync(${src} \u2192 ${dest})`, () => fs.copyFileSync(src, dest));
|
|
35182
36905
|
},
|
|
35183
|
-
unlinkSync(
|
|
35184
|
-
return withSlowLogging2(`unlinkSync(${
|
|
36906
|
+
unlinkSync(path26) {
|
|
36907
|
+
return withSlowLogging2(`unlinkSync(${path26})`, () => fs.unlinkSync(path26));
|
|
35185
36908
|
},
|
|
35186
36909
|
renameSync(oldPath, newPath) {
|
|
35187
36910
|
return withSlowLogging2(`renameSync(${oldPath} \u2192 ${newPath})`, () => fs.renameSync(oldPath, newPath));
|
|
35188
36911
|
},
|
|
35189
|
-
linkSync(target,
|
|
35190
|
-
return withSlowLogging2(`linkSync(${target} \u2192 ${
|
|
36912
|
+
linkSync(target, path26) {
|
|
36913
|
+
return withSlowLogging2(`linkSync(${target} \u2192 ${path26})`, () => fs.linkSync(target, path26));
|
|
35191
36914
|
},
|
|
35192
|
-
symlinkSync(target,
|
|
35193
|
-
return withSlowLogging2(`symlinkSync(${target} \u2192 ${
|
|
36915
|
+
symlinkSync(target, path26) {
|
|
36916
|
+
return withSlowLogging2(`symlinkSync(${target} \u2192 ${path26})`, () => fs.symlinkSync(target, path26));
|
|
35194
36917
|
},
|
|
35195
|
-
readlinkSync(
|
|
35196
|
-
return withSlowLogging2(`readlinkSync(${
|
|
36918
|
+
readlinkSync(path26) {
|
|
36919
|
+
return withSlowLogging2(`readlinkSync(${path26})`, () => fs.readlinkSync(path26));
|
|
35197
36920
|
},
|
|
35198
|
-
realpathSync(
|
|
35199
|
-
return withSlowLogging2(`realpathSync(${
|
|
36921
|
+
realpathSync(path26) {
|
|
36922
|
+
return withSlowLogging2(`realpathSync(${path26})`, () => fs.realpathSync(path26));
|
|
35200
36923
|
},
|
|
35201
36924
|
mkdirSync(dirPath, options) {
|
|
35202
36925
|
return withSlowLogging2(`mkdirSync(${dirPath})`, () => {
|
|
@@ -35226,11 +36949,11 @@ var NodeFsOperations = {
|
|
|
35226
36949
|
rmdirSync(dirPath) {
|
|
35227
36950
|
return withSlowLogging2(`rmdirSync(${dirPath})`, () => fs.rmdirSync(dirPath));
|
|
35228
36951
|
},
|
|
35229
|
-
rmSync(
|
|
35230
|
-
return withSlowLogging2(`rmSync(${
|
|
36952
|
+
rmSync(path26, options) {
|
|
36953
|
+
return withSlowLogging2(`rmSync(${path26})`, () => fs.rmSync(path26, options));
|
|
35231
36954
|
},
|
|
35232
|
-
createWriteStream(
|
|
35233
|
-
return fs.createWriteStream(
|
|
36955
|
+
createWriteStream(path26) {
|
|
36956
|
+
return fs.createWriteStream(path26);
|
|
35234
36957
|
}
|
|
35235
36958
|
};
|
|
35236
36959
|
var activeFs = NodeFsOperations;
|
|
@@ -35586,8 +37309,8 @@ function getErrorMap() {
|
|
|
35586
37309
|
return overrideErrorMap;
|
|
35587
37310
|
}
|
|
35588
37311
|
var makeIssue = (params) => {
|
|
35589
|
-
const { data, path:
|
|
35590
|
-
const fullPath = [...
|
|
37312
|
+
const { data, path: path26, errorMaps, issueData } = params;
|
|
37313
|
+
const fullPath = [...path26, ...issueData.path || []];
|
|
35591
37314
|
const fullIssue = {
|
|
35592
37315
|
...issueData,
|
|
35593
37316
|
path: fullPath
|
|
@@ -35694,11 +37417,11 @@ var errorUtil;
|
|
|
35694
37417
|
errorUtil22.toString = (message) => typeof message === "string" ? message : message?.message;
|
|
35695
37418
|
})(errorUtil || (errorUtil = {}));
|
|
35696
37419
|
var ParseInputLazyPath = class {
|
|
35697
|
-
constructor(parent, value,
|
|
37420
|
+
constructor(parent, value, path26, key) {
|
|
35698
37421
|
this._cachedPath = [];
|
|
35699
37422
|
this.parent = parent;
|
|
35700
37423
|
this.data = value;
|
|
35701
|
-
this._path =
|
|
37424
|
+
this._path = path26;
|
|
35702
37425
|
this._key = key;
|
|
35703
37426
|
}
|
|
35704
37427
|
get path() {
|
|
@@ -39189,10 +40912,10 @@ function assignProp(target, prop, value) {
|
|
|
39189
40912
|
configurable: true
|
|
39190
40913
|
});
|
|
39191
40914
|
}
|
|
39192
|
-
function getElementAtPath(obj,
|
|
39193
|
-
if (!
|
|
40915
|
+
function getElementAtPath(obj, path26) {
|
|
40916
|
+
if (!path26)
|
|
39194
40917
|
return obj;
|
|
39195
|
-
return
|
|
40918
|
+
return path26.reduce((acc, key) => acc?.[key], obj);
|
|
39196
40919
|
}
|
|
39197
40920
|
function promiseAllObject(promisesObj) {
|
|
39198
40921
|
const keys = Object.keys(promisesObj);
|
|
@@ -39509,11 +41232,11 @@ function aborted(x, startIndex = 0) {
|
|
|
39509
41232
|
}
|
|
39510
41233
|
return false;
|
|
39511
41234
|
}
|
|
39512
|
-
function prefixIssues(
|
|
41235
|
+
function prefixIssues(path26, issues) {
|
|
39513
41236
|
return issues.map((iss) => {
|
|
39514
41237
|
var _a;
|
|
39515
41238
|
(_a = iss).path ?? (_a.path = []);
|
|
39516
|
-
iss.path.unshift(
|
|
41239
|
+
iss.path.unshift(path26);
|
|
39517
41240
|
return iss;
|
|
39518
41241
|
});
|
|
39519
41242
|
}
|
|
@@ -48181,8 +49904,8 @@ function getErrorMap2() {
|
|
|
48181
49904
|
|
|
48182
49905
|
// node_modules/zod/v3/helpers/parseUtil.js
|
|
48183
49906
|
var makeIssue2 = (params) => {
|
|
48184
|
-
const { data, path:
|
|
48185
|
-
const fullPath = [...
|
|
49907
|
+
const { data, path: path26, errorMaps, issueData } = params;
|
|
49908
|
+
const fullPath = [...path26, ...issueData.path || []];
|
|
48186
49909
|
const fullIssue = {
|
|
48187
49910
|
...issueData,
|
|
48188
49911
|
path: fullPath
|
|
@@ -48298,11 +50021,11 @@ var errorUtil2;
|
|
|
48298
50021
|
|
|
48299
50022
|
// node_modules/zod/v3/types.js
|
|
48300
50023
|
var ParseInputLazyPath2 = class {
|
|
48301
|
-
constructor(parent, value,
|
|
50024
|
+
constructor(parent, value, path26, key) {
|
|
48302
50025
|
this._cachedPath = [];
|
|
48303
50026
|
this.parent = parent;
|
|
48304
50027
|
this.data = value;
|
|
48305
|
-
this._path =
|
|
50028
|
+
this._path = path26;
|
|
48306
50029
|
this._key = key;
|
|
48307
50030
|
}
|
|
48308
50031
|
get path() {
|
|
@@ -52562,11 +54285,11 @@ function formatRange(range) {
|
|
|
52562
54285
|
function formatLocation(location) {
|
|
52563
54286
|
const uri = location.uri || location.targetUri;
|
|
52564
54287
|
if (!uri) return "Unknown location";
|
|
52565
|
-
const
|
|
54288
|
+
const path26 = uriToPath(uri);
|
|
52566
54289
|
const locationRange = location.range || location.targetRange || location.targetSelectionRange;
|
|
52567
|
-
if (!locationRange) return
|
|
54290
|
+
if (!locationRange) return path26;
|
|
52568
54291
|
const range = formatRange(locationRange);
|
|
52569
|
-
return `${
|
|
54292
|
+
return `${path26}:${range}`;
|
|
52570
54293
|
}
|
|
52571
54294
|
function formatHover(hover) {
|
|
52572
54295
|
if (!hover) return "No hover information available";
|
|
@@ -52652,8 +54375,8 @@ function formatWorkspaceEdit(edit) {
|
|
|
52652
54375
|
const lines = [];
|
|
52653
54376
|
if (edit.changes) {
|
|
52654
54377
|
for (const [uri, changes] of Object.entries(edit.changes)) {
|
|
52655
|
-
const
|
|
52656
|
-
lines.push(`File: ${
|
|
54378
|
+
const path26 = uriToPath(uri);
|
|
54379
|
+
lines.push(`File: ${path26}`);
|
|
52657
54380
|
for (const change of changes) {
|
|
52658
54381
|
const range = formatRange(change.range);
|
|
52659
54382
|
const preview = change.newText.length > 50 ? change.newText.slice(0, 50) + "..." : change.newText;
|
|
@@ -52663,8 +54386,8 @@ function formatWorkspaceEdit(edit) {
|
|
|
52663
54386
|
}
|
|
52664
54387
|
if (edit.documentChanges) {
|
|
52665
54388
|
for (const docChange of edit.documentChanges) {
|
|
52666
|
-
const
|
|
52667
|
-
lines.push(`File: ${
|
|
54389
|
+
const path26 = uriToPath(docChange.textDocument.uri);
|
|
54390
|
+
lines.push(`File: ${path26}`);
|
|
52668
54391
|
for (const change of docChange.edits) {
|
|
52669
54392
|
const range = formatRange(change.range);
|
|
52670
54393
|
const preview = change.newText.length > 50 ? change.newText.slice(0, 50) + "..." : change.newText;
|
|
@@ -53456,7 +55179,7 @@ Note: Patterns must be valid AST nodes for the language.`,
|
|
|
53456
55179
|
const {
|
|
53457
55180
|
pattern,
|
|
53458
55181
|
language,
|
|
53459
|
-
path:
|
|
55182
|
+
path: path26 = ".",
|
|
53460
55183
|
context = 2,
|
|
53461
55184
|
maxResults = 20
|
|
53462
55185
|
} = args;
|
|
@@ -53473,13 +55196,13 @@ Error: ${sgLoadError}`
|
|
|
53473
55196
|
]
|
|
53474
55197
|
};
|
|
53475
55198
|
}
|
|
53476
|
-
const files = getFilesForLanguage(
|
|
55199
|
+
const files = getFilesForLanguage(path26, language);
|
|
53477
55200
|
if (files.length === 0) {
|
|
53478
55201
|
return {
|
|
53479
55202
|
content: [
|
|
53480
55203
|
{
|
|
53481
55204
|
type: "text",
|
|
53482
|
-
text: `No ${language} files found in ${
|
|
55205
|
+
text: `No ${language} files found in ${path26}`
|
|
53483
55206
|
}
|
|
53484
55207
|
]
|
|
53485
55208
|
};
|
|
@@ -53519,7 +55242,7 @@ Error: ${sgLoadError}`
|
|
|
53519
55242
|
type: "text",
|
|
53520
55243
|
text: `No matches found for pattern: ${pattern}
|
|
53521
55244
|
|
|
53522
|
-
Searched ${files.length} ${language} file(s) in ${
|
|
55245
|
+
Searched ${files.length} ${language} file(s) in ${path26}
|
|
53523
55246
|
|
|
53524
55247
|
Tip: Ensure the pattern is a valid AST node. For example:
|
|
53525
55248
|
- Use "function $NAME" not just "$NAME"
|
|
@@ -53579,7 +55302,7 @@ IMPORTANT: dryRun=true (default) only previews changes. Set dryRun=false to appl
|
|
|
53579
55302
|
dryRun: external_exports.boolean().optional().describe("Preview only, don't apply changes (default: true)")
|
|
53580
55303
|
},
|
|
53581
55304
|
handler: async (args) => {
|
|
53582
|
-
const { pattern, replacement, language, path:
|
|
55305
|
+
const { pattern, replacement, language, path: path26 = ".", dryRun = true } = args;
|
|
53583
55306
|
try {
|
|
53584
55307
|
const sg = await getSgModule();
|
|
53585
55308
|
if (!sg) {
|
|
@@ -53593,13 +55316,13 @@ Error: ${sgLoadError}`
|
|
|
53593
55316
|
]
|
|
53594
55317
|
};
|
|
53595
55318
|
}
|
|
53596
|
-
const files = getFilesForLanguage(
|
|
55319
|
+
const files = getFilesForLanguage(path26, language);
|
|
53597
55320
|
if (files.length === 0) {
|
|
53598
55321
|
return {
|
|
53599
55322
|
content: [
|
|
53600
55323
|
{
|
|
53601
55324
|
type: "text",
|
|
53602
|
-
text: `No ${language} files found in ${
|
|
55325
|
+
text: `No ${language} files found in ${path26}`
|
|
53603
55326
|
}
|
|
53604
55327
|
]
|
|
53605
55328
|
};
|
|
@@ -53667,7 +55390,7 @@ Error: ${sgLoadError}`
|
|
|
53667
55390
|
type: "text",
|
|
53668
55391
|
text: `No matches found for pattern: ${pattern}
|
|
53669
55392
|
|
|
53670
|
-
Searched ${files.length} ${language} file(s) in ${
|
|
55393
|
+
Searched ${files.length} ${language} file(s) in ${path26}`
|
|
53671
55394
|
}
|
|
53672
55395
|
]
|
|
53673
55396
|
};
|
|
@@ -59655,14 +61378,14 @@ function getGitDiffStats(directory) {
|
|
|
59655
61378
|
for (const line of output.split("\n")) {
|
|
59656
61379
|
const parts = line.split(" ");
|
|
59657
61380
|
if (parts.length < 3) continue;
|
|
59658
|
-
const [addedStr, removedStr,
|
|
61381
|
+
const [addedStr, removedStr, path26] = parts;
|
|
59659
61382
|
const added = addedStr === "-" ? 0 : parseInt(addedStr, 10);
|
|
59660
61383
|
const removed = removedStr === "-" ? 0 : parseInt(removedStr, 10);
|
|
59661
61384
|
stats.push({
|
|
59662
|
-
path:
|
|
61385
|
+
path: path26,
|
|
59663
61386
|
added,
|
|
59664
61387
|
removed,
|
|
59665
|
-
status: statusMap.get(
|
|
61388
|
+
status: statusMap.get(path26) ?? "modified"
|
|
59666
61389
|
});
|
|
59667
61390
|
}
|
|
59668
61391
|
return stats;
|
|
@@ -60296,11 +62019,11 @@ Running directly without heavy agent stacking. Prefix with \`quick:\`, \`simple:
|
|
|
60296
62019
|
const existingPrd = findPrd(directory);
|
|
60297
62020
|
if (!noPrd && !existingPrd) {
|
|
60298
62021
|
const { basename: basename21 } = await import("path");
|
|
60299
|
-
const { execSync:
|
|
62022
|
+
const { execSync: execSync19 } = await import("child_process");
|
|
60300
62023
|
const projectName = basename21(directory);
|
|
60301
62024
|
let branchName = "ralph/task";
|
|
60302
62025
|
try {
|
|
60303
|
-
branchName =
|
|
62026
|
+
branchName = execSync19("git rev-parse --abbrev-ref HEAD", { cwd: directory, encoding: "utf-8", timeout: 5e3 }).trim();
|
|
60304
62027
|
} catch {
|
|
60305
62028
|
}
|
|
60306
62029
|
initPrdFn(directory, projectName, branchName, cleanPrompt);
|
|
@@ -60832,11 +62555,11 @@ async function processPostToolUse(input) {
|
|
|
60832
62555
|
const existingPrd = findPrd(directory);
|
|
60833
62556
|
if (!noPrd && !existingPrd) {
|
|
60834
62557
|
const { basename: basename21 } = await import("path");
|
|
60835
|
-
const { execSync:
|
|
62558
|
+
const { execSync: execSync19 } = await import("child_process");
|
|
60836
62559
|
const projectName = basename21(directory);
|
|
60837
62560
|
let branchName = "ralph/task";
|
|
60838
62561
|
try {
|
|
60839
|
-
branchName =
|
|
62562
|
+
branchName = execSync19("git rev-parse --abbrev-ref HEAD", { cwd: directory, encoding: "utf-8", timeout: 5e3 }).trim();
|
|
60840
62563
|
} catch {
|
|
60841
62564
|
}
|
|
60842
62565
|
initPrdFn(directory, projectName, branchName, cleanPrompt);
|
|
@@ -63067,17 +64790,17 @@ function taskFileCandidates(teamName, taskId, cwd2) {
|
|
|
63067
64790
|
const legacy = legacyTaskFilePath(teamName, taskId, cwd2);
|
|
63068
64791
|
return canonical === legacy ? [canonical] : [canonical, legacy];
|
|
63069
64792
|
}
|
|
63070
|
-
async function writeAtomic(
|
|
63071
|
-
const tmp = `${
|
|
63072
|
-
await (0, import_promises7.mkdir)((0, import_node_path5.dirname)(
|
|
64793
|
+
async function writeAtomic(path26, data) {
|
|
64794
|
+
const tmp = `${path26}.${process.pid}.tmp`;
|
|
64795
|
+
await (0, import_promises7.mkdir)((0, import_node_path5.dirname)(path26), { recursive: true });
|
|
63073
64796
|
await (0, import_promises7.writeFile)(tmp, data, "utf8");
|
|
63074
64797
|
const { rename: rename3 } = await import("node:fs/promises");
|
|
63075
|
-
await rename3(tmp,
|
|
64798
|
+
await rename3(tmp, path26);
|
|
63076
64799
|
}
|
|
63077
|
-
async function readJsonSafe(
|
|
64800
|
+
async function readJsonSafe(path26) {
|
|
63078
64801
|
try {
|
|
63079
|
-
if (!(0, import_node_fs2.existsSync)(
|
|
63080
|
-
const raw = await (0, import_promises7.readFile)(
|
|
64802
|
+
if (!(0, import_node_fs2.existsSync)(path26)) return null;
|
|
64803
|
+
const raw = await (0, import_promises7.readFile)(path26, "utf8");
|
|
63081
64804
|
return JSON.parse(raw);
|
|
63082
64805
|
} catch {
|
|
63083
64806
|
return null;
|
|
@@ -63574,10 +65297,10 @@ function readTeamStateRootFromEnv(env2 = process.env) {
|
|
|
63574
65297
|
const candidate = typeof env2.OMC_TEAM_STATE_ROOT === "string" && env2.OMC_TEAM_STATE_ROOT.trim() !== "" ? env2.OMC_TEAM_STATE_ROOT.trim() : typeof env2.OMX_TEAM_STATE_ROOT === "string" && env2.OMX_TEAM_STATE_ROOT.trim() !== "" ? env2.OMX_TEAM_STATE_ROOT.trim() : "";
|
|
63575
65298
|
return candidate || null;
|
|
63576
65299
|
}
|
|
63577
|
-
function readTeamStateRootFromFile(
|
|
63578
|
-
if (!(0, import_node_fs3.existsSync)(
|
|
65300
|
+
function readTeamStateRootFromFile(path26) {
|
|
65301
|
+
if (!(0, import_node_fs3.existsSync)(path26)) return null;
|
|
63579
65302
|
try {
|
|
63580
|
-
const parsed = JSON.parse((0, import_node_fs3.readFileSync)(
|
|
65303
|
+
const parsed = JSON.parse((0, import_node_fs3.readFileSync)(path26, "utf8"));
|
|
63581
65304
|
return typeof parsed.team_state_root === "string" && parsed.team_state_root.trim() !== "" ? parsed.team_state_root.trim() : null;
|
|
63582
65305
|
} catch {
|
|
63583
65306
|
return null;
|
|
@@ -66196,7 +67919,7 @@ function warnIfWin32() {
|
|
|
66196
67919
|
}
|
|
66197
67920
|
|
|
66198
67921
|
// src/cli/index.ts
|
|
66199
|
-
var __dirname3 = (0,
|
|
67922
|
+
var __dirname3 = (0, import_path113.dirname)((0, import_url15.fileURLToPath)(importMetaUrl));
|
|
66200
67923
|
var version2 = getRuntimePackageVersion();
|
|
66201
67924
|
var program2 = new Command();
|
|
66202
67925
|
warnIfWin32();
|
|
@@ -66244,7 +67967,7 @@ Examples:
|
|
|
66244
67967
|
console.log(source_default.gray("Configuration is now managed automatically. Use /oh-my-claudecode:omc-setup instead.\n"));
|
|
66245
67968
|
const paths = getConfigPaths();
|
|
66246
67969
|
const targetPath = options.global ? paths.user : paths.project;
|
|
66247
|
-
const targetDir = (0,
|
|
67970
|
+
const targetDir = (0, import_path113.dirname)(targetPath);
|
|
66248
67971
|
console.log(source_default.blue("Oh-My-ClaudeCode Configuration Setup\n"));
|
|
66249
67972
|
if ((0, import_fs92.existsSync)(targetPath) && !options.force) {
|
|
66250
67973
|
console.log(source_default.yellow(`Configuration already exists at ${targetPath}`));
|
|
@@ -66350,12 +68073,12 @@ Examples:
|
|
|
66350
68073
|
`;
|
|
66351
68074
|
(0, import_fs92.writeFileSync)(targetPath, configContent);
|
|
66352
68075
|
console.log(source_default.green(`Created configuration: ${targetPath}`));
|
|
66353
|
-
const schemaPath = (0,
|
|
68076
|
+
const schemaPath = (0, import_path113.join)(targetDir, "omc-schema.json");
|
|
66354
68077
|
(0, import_fs92.writeFileSync)(schemaPath, JSON.stringify(generateConfigSchema(), null, 2));
|
|
66355
68078
|
console.log(source_default.green(`Created JSON schema: ${schemaPath}`));
|
|
66356
68079
|
console.log(source_default.blue("\nSetup complete!"));
|
|
66357
68080
|
console.log(source_default.gray("Edit the configuration file to customize your setup."));
|
|
66358
|
-
const agentsMdPath = (0,
|
|
68081
|
+
const agentsMdPath = (0, import_path113.join)(process.cwd(), "AGENTS.md");
|
|
66359
68082
|
if (!(0, import_fs92.existsSync)(agentsMdPath) && !options.global) {
|
|
66360
68083
|
const agentsMdContent = `# Project Agents Configuration
|
|
66361
68084
|
|
|
@@ -67080,8 +68803,8 @@ Note:
|
|
|
67080
68803
|
teleportCmd.command("list").description("List existing worktrees in ~/Workspace/omc-worktrees/").option("--json", "Output as JSON").action(async (options) => {
|
|
67081
68804
|
await teleportListCommand(options);
|
|
67082
68805
|
});
|
|
67083
|
-
teleportCmd.command("remove <path>").alias("rm").description("Remove a worktree").option("-f, --force", "Force removal even with uncommitted changes").option("--json", "Output as JSON").action(async (
|
|
67084
|
-
const exitCode = await teleportRemoveCommand(
|
|
68806
|
+
teleportCmd.command("remove <path>").alias("rm").description("Remove a worktree").option("-f, --force", "Force removal even with uncommitted changes").option("--json", "Output as JSON").action(async (path26, options) => {
|
|
68807
|
+
const exitCode = await teleportRemoveCommand(path26, options);
|
|
67085
68808
|
if (exitCode !== 0) process.exit(exitCode);
|
|
67086
68809
|
});
|
|
67087
68810
|
var doctorCmd = program2.command("doctor").description("Diagnostic tools for troubleshooting OMC installation").addHelpText("after", `
|
|
@@ -67175,6 +68898,135 @@ program2.command("hud").description("Run the OMC HUD statusline renderer").optio
|
|
|
67175
68898
|
await hudMain();
|
|
67176
68899
|
}
|
|
67177
68900
|
});
|
|
68901
|
+
var testCommand = program2.command("test").description("Testing utilities");
|
|
68902
|
+
testCommand.command("gen <file>").description("Generate tests for a file (.ts, .tsx, .jsx, .py, .go, .rs)").option("-o, --output <path>", "Output test file path").option("-l, --language <lang>", "Language override (typescript, react, python, go, rust)").action(async (file, options) => {
|
|
68903
|
+
const { testGenCommand: testGenCommand2 } = await Promise.resolve().then(() => (init_commands(), commands_exports));
|
|
68904
|
+
const result = await testGenCommand2({
|
|
68905
|
+
filePath: file,
|
|
68906
|
+
output: options.output,
|
|
68907
|
+
language: options.language
|
|
68908
|
+
});
|
|
68909
|
+
if (result.success) {
|
|
68910
|
+
console.log(source_default.green(`\u2705 Test generated: ${result.testFilePath}`));
|
|
68911
|
+
} else {
|
|
68912
|
+
console.error(source_default.red(`\u274C Error: ${result.error}`));
|
|
68913
|
+
process.exit(1);
|
|
68914
|
+
}
|
|
68915
|
+
});
|
|
68916
|
+
testCommand.command("detect-stack").description("Detect project tech stack").action(async () => {
|
|
68917
|
+
const { testDetectStackCommand: testDetectStackCommand2 } = await Promise.resolve().then(() => (init_commands(), commands_exports));
|
|
68918
|
+
const result = await testDetectStackCommand2({
|
|
68919
|
+
projectRoot: process.cwd()
|
|
68920
|
+
});
|
|
68921
|
+
console.log(source_default.blue("\u{1F4CA} Detected Tech Stack:"));
|
|
68922
|
+
console.log(JSON.stringify(result.stack, null, 2));
|
|
68923
|
+
});
|
|
68924
|
+
testCommand.command("analyze").description("Analyze test coverage for the project").action(async () => {
|
|
68925
|
+
const { testAnalyzeCoverageCommand: testAnalyzeCoverageCommand2 } = await Promise.resolve().then(() => (init_commands(), commands_exports));
|
|
68926
|
+
const result = await testAnalyzeCoverageCommand2({
|
|
68927
|
+
projectRoot: process.cwd()
|
|
68928
|
+
});
|
|
68929
|
+
console.log(source_default.blue("\u{1F4CA} Coverage Analysis:"));
|
|
68930
|
+
console.log(` Total: ${result.totalCoverage}%`);
|
|
68931
|
+
console.log(` Lines: ${result.lineCoverage}%`);
|
|
68932
|
+
console.log(` Functions: ${result.functionCoverage}%`);
|
|
68933
|
+
console.log(` Branches: ${result.branchCoverage}%`);
|
|
68934
|
+
console.log(` Statements: ${result.statementCoverage}%`);
|
|
68935
|
+
});
|
|
68936
|
+
testCommand.command("contract <openapi-spec>").description("Generate contract tests from an OpenAPI spec").option("-f, --framework <framework>", "Test framework (pact, supertest, msw)", "pact").option("-c, --consumer <name>", "Consumer name").option("-p, --provider <name>", "Provider name").action(async (specPath, options) => {
|
|
68937
|
+
const { testContractCommand: testContractCommand2 } = await Promise.resolve().then(() => (init_commands(), commands_exports));
|
|
68938
|
+
const result = await testContractCommand2({
|
|
68939
|
+
specPath,
|
|
68940
|
+
framework: options.framework,
|
|
68941
|
+
consumer: options.consumer,
|
|
68942
|
+
provider: options.provider
|
|
68943
|
+
});
|
|
68944
|
+
if (result.success) {
|
|
68945
|
+
console.log(source_default.green(`\u2705 Contract test generated: ${result.testFilePath}`));
|
|
68946
|
+
} else {
|
|
68947
|
+
console.error(source_default.red("\u274C Failed to generate contract test"));
|
|
68948
|
+
process.exit(1);
|
|
68949
|
+
}
|
|
68950
|
+
});
|
|
68951
|
+
testCommand.command("complexity <file>").description("Analyze code complexity for a file").action(async (file) => {
|
|
68952
|
+
const { testComplexityCommand: testComplexityCommand2 } = await Promise.resolve().then(() => (init_commands(), commands_exports));
|
|
68953
|
+
const result = await testComplexityCommand2({ filePath: file });
|
|
68954
|
+
console.log(source_default.blue(`\u{1F4CA} Complexity Analysis: ${result.complexity}`));
|
|
68955
|
+
console.log(` Lines: ${result.metrics.lines}`);
|
|
68956
|
+
console.log(` Cyclomatic Complexity: ${result.metrics.cyclomaticComplexity}`);
|
|
68957
|
+
console.log(` Nesting Level: ${result.metrics.nestingLevel}`);
|
|
68958
|
+
console.log(` External Deps: ${result.metrics.externalDependencies}`);
|
|
68959
|
+
if (result.reasons.length > 0) {
|
|
68960
|
+
console.log(` Reasons: ${result.reasons.join(", ")}`);
|
|
68961
|
+
}
|
|
68962
|
+
});
|
|
68963
|
+
testCommand.command("e2e <flow-description>").description("Generate Playwright E2E tests from user flow description").option("-b, --base-url <url>", "Base URL for the application", "http://localhost:3000").option("-n, --test-name <name>", "Test name", "User flow test").option("-o, --output <path>", "Output test file path", "./tests/e2e/user-flow.spec.ts").action(async (flowDescription, options) => {
|
|
68964
|
+
const { testE2ECommand: testE2ECommand2 } = await Promise.resolve().then(() => (init_commands(), commands_exports));
|
|
68965
|
+
const result = await testE2ECommand2({
|
|
68966
|
+
flowDescription,
|
|
68967
|
+
baseUrl: options.baseUrl,
|
|
68968
|
+
testName: options.testName,
|
|
68969
|
+
output: options.output
|
|
68970
|
+
});
|
|
68971
|
+
if (result.success) {
|
|
68972
|
+
console.log(source_default.green(`\u2705 E2E test generated: ${result.testFilePath}`));
|
|
68973
|
+
} else {
|
|
68974
|
+
console.error(source_default.red(`\u274C Error: ${result.error}`));
|
|
68975
|
+
process.exit(1);
|
|
68976
|
+
}
|
|
68977
|
+
});
|
|
68978
|
+
testCommand.command("giskard <file>").description("Generate Giskard behavioral tests for robustness testing").option("-t, --test-type <type>", "Test type (perturbation, robustness)", "perturbation").option("-o, --output <path>", "Output test file path", "./tests/behavioral/perturbation.test.ts").action(async (file, options) => {
|
|
68979
|
+
const { testGiskardCommand: testGiskardCommand2 } = await Promise.resolve().then(() => (init_commands(), commands_exports));
|
|
68980
|
+
const result = await testGiskardCommand2({
|
|
68981
|
+
filePath: file,
|
|
68982
|
+
testType: options.testType,
|
|
68983
|
+
output: options.output
|
|
68984
|
+
});
|
|
68985
|
+
if (result.success) {
|
|
68986
|
+
console.log(source_default.green(`\u2705 Giskard behavioral tests generated: ${result.testFilePath}`));
|
|
68987
|
+
} else {
|
|
68988
|
+
console.error(source_default.red(`\u274C Error: ${result.error}`));
|
|
68989
|
+
process.exit(1);
|
|
68990
|
+
}
|
|
68991
|
+
});
|
|
68992
|
+
testCommand.command("cicd").description("Generate GitHub Actions CI/CD workflow for testing").option("-l, --language <lang>", "Primary language (nodejs, python, go, rust)", "nodejs").option("-o, --output <path>", "Output workflow file path", "./.github/workflows/test.yml").action(async (options) => {
|
|
68993
|
+
const { testCICDCommand: testCICDCommand2 } = await Promise.resolve().then(() => (init_commands(), commands_exports));
|
|
68994
|
+
const result = await testCICDCommand2({
|
|
68995
|
+
language: options.language,
|
|
68996
|
+
output: options.output
|
|
68997
|
+
});
|
|
68998
|
+
if (result.success) {
|
|
68999
|
+
console.log(source_default.green(`\u2705 CI/CD workflow generated: ${result.workflowPath}`));
|
|
69000
|
+
} else {
|
|
69001
|
+
console.error(source_default.red(`\u274C Error: ${result.error}`));
|
|
69002
|
+
process.exit(1);
|
|
69003
|
+
}
|
|
69004
|
+
});
|
|
69005
|
+
testCommand.command("quality <test-file>").description("Score test quality and get improvement recommendations").option("-t, --test-type <type>", "Test type (unit, integration, e2e)", "unit").action(async (testFile, options) => {
|
|
69006
|
+
const { testQualityCommand: testQualityCommand2 } = await Promise.resolve().then(() => (init_commands(), commands_exports));
|
|
69007
|
+
const result = await testQualityCommand2({
|
|
69008
|
+
testFilePath: testFile,
|
|
69009
|
+
testType: options.testType
|
|
69010
|
+
});
|
|
69011
|
+
if (result.success && result.score) {
|
|
69012
|
+
console.log(source_default.blue("\u{1F4CA} Test Quality Score:"));
|
|
69013
|
+
console.log(` Overall: ${result.score.overallScore}/100`);
|
|
69014
|
+
console.log(` Completeness: ${result.score.completenessScore}/100`);
|
|
69015
|
+
console.log(` Assertions: ${result.score.assertionQuality}/100`);
|
|
69016
|
+
console.log(` Independence: ${result.score.independenceScore}/100`);
|
|
69017
|
+
console.log(` Naming: ${result.score.namingScore}/100`);
|
|
69018
|
+
console.log(` Assertion Count: ${result.score.assertionCount}`);
|
|
69019
|
+
if (result.score.recommendations.length > 0) {
|
|
69020
|
+
console.log(source_default.yellow("\n\u{1F4A1} Recommendations:"));
|
|
69021
|
+
result.score.recommendations.forEach((rec) => {
|
|
69022
|
+
console.log(` - ${rec}`);
|
|
69023
|
+
});
|
|
69024
|
+
}
|
|
69025
|
+
} else {
|
|
69026
|
+
console.error(source_default.red(`\u274C Error: ${result.error}`));
|
|
69027
|
+
process.exit(1);
|
|
69028
|
+
}
|
|
69029
|
+
});
|
|
67178
69030
|
program2.command("team").description("Team CLI API for worker lifecycle operations").helpOption(false).allowUnknownOption(true).allowExcessArguments(true).argument("[args...]", "team subcommand arguments").action(async (args) => {
|
|
67179
69031
|
await teamCommand(args);
|
|
67180
69032
|
});
|