@kevinrabun/judges 3.53.0 → 3.55.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +112 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/approve-chain.d.ts +8 -0
- package/dist/commands/approve-chain.d.ts.map +1 -0
- package/dist/commands/approve-chain.js +235 -0
- package/dist/commands/approve-chain.js.map +1 -0
- package/dist/commands/build-optimize.d.ts +7 -0
- package/dist/commands/build-optimize.d.ts.map +1 -0
- package/dist/commands/build-optimize.js +257 -0
- package/dist/commands/build-optimize.js.map +1 -0
- package/dist/commands/coach-mode.d.ts +8 -0
- package/dist/commands/coach-mode.d.ts.map +1 -0
- package/dist/commands/coach-mode.js +230 -0
- package/dist/commands/coach-mode.js.map +1 -0
- package/dist/commands/commit-hygiene.d.ts +6 -0
- package/dist/commands/commit-hygiene.d.ts.map +1 -0
- package/dist/commands/commit-hygiene.js +176 -0
- package/dist/commands/commit-hygiene.js.map +1 -0
- package/dist/commands/context-inject.d.ts +9 -0
- package/dist/commands/context-inject.d.ts.map +1 -0
- package/dist/commands/context-inject.js +212 -0
- package/dist/commands/context-inject.js.map +1 -0
- package/dist/commands/deploy-readiness.d.ts +6 -0
- package/dist/commands/deploy-readiness.d.ts.map +1 -0
- package/dist/commands/deploy-readiness.js +212 -0
- package/dist/commands/deploy-readiness.js.map +1 -0
- package/dist/commands/finding-contest.d.ts +8 -0
- package/dist/commands/finding-contest.d.ts.map +1 -0
- package/dist/commands/finding-contest.js +193 -0
- package/dist/commands/finding-contest.js.map +1 -0
- package/dist/commands/habit-tracker.d.ts +8 -0
- package/dist/commands/habit-tracker.d.ts.map +1 -0
- package/dist/commands/habit-tracker.js +195 -0
- package/dist/commands/habit-tracker.js.map +1 -0
- package/dist/commands/migration-safety.d.ts +6 -0
- package/dist/commands/migration-safety.d.ts.map +1 -0
- package/dist/commands/migration-safety.js +257 -0
- package/dist/commands/migration-safety.js.map +1 -0
- package/dist/commands/observability-gap.d.ts +6 -0
- package/dist/commands/observability-gap.d.ts.map +1 -0
- package/dist/commands/observability-gap.js +212 -0
- package/dist/commands/observability-gap.js.map +1 -0
- package/dist/commands/prompt-replay.d.ts +8 -0
- package/dist/commands/prompt-replay.d.ts.map +1 -0
- package/dist/commands/prompt-replay.js +177 -0
- package/dist/commands/prompt-replay.js.map +1 -0
- package/dist/commands/review-replay.d.ts +9 -0
- package/dist/commands/review-replay.d.ts.map +1 -0
- package/dist/commands/review-replay.js +265 -0
- package/dist/commands/review-replay.js.map +1 -0
- package/dist/commands/rollback-safety.d.ts +5 -0
- package/dist/commands/rollback-safety.d.ts.map +1 -0
- package/dist/commands/rollback-safety.js +192 -0
- package/dist/commands/rollback-safety.js.map +1 -0
- package/dist/commands/secret-age.d.ts +6 -0
- package/dist/commands/secret-age.d.ts.map +1 -0
- package/dist/commands/secret-age.js +215 -0
- package/dist/commands/secret-age.js.map +1 -0
- package/dist/commands/snippet-eval.d.ts +8 -0
- package/dist/commands/snippet-eval.d.ts.map +1 -0
- package/dist/commands/snippet-eval.js +224 -0
- package/dist/commands/snippet-eval.js.map +1 -0
- package/dist/commands/test-quality.d.ts +6 -0
- package/dist/commands/test-quality.d.ts.map +1 -0
- package/dist/commands/test-quality.js +161 -0
- package/dist/commands/test-quality.js.map +1 -0
- package/package.json +1 -1
- package/server.json +2 -2
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Snippet eval — evaluate a code snippet from stdin or a string
|
|
3
|
+
* argument without needing a file, with instant formatted output.
|
|
4
|
+
*
|
|
5
|
+
* Zero-friction entry point for evaluating AI-generated code snippets.
|
|
6
|
+
*/
|
|
7
|
+
import { readFileSync } from "fs";
|
|
8
|
+
const SNIPPET_RULES = [
|
|
9
|
+
{
|
|
10
|
+
id: "SNIP-001",
|
|
11
|
+
title: "SQL injection risk",
|
|
12
|
+
pattern: /(?:SELECT|INSERT|UPDATE|DELETE).*\+\s*\w|`\$\{.*(?:SELECT|INSERT|UPDATE|DELETE)/i,
|
|
13
|
+
severity: "high",
|
|
14
|
+
recommendation: "Use parameterized queries",
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
id: "SNIP-002",
|
|
18
|
+
title: "XSS vulnerability",
|
|
19
|
+
pattern: /innerHTML|dangerouslySetInnerHTML|document\.write/i,
|
|
20
|
+
severity: "high",
|
|
21
|
+
recommendation: "Sanitize user input before DOM insertion",
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
id: "SNIP-003",
|
|
25
|
+
title: "Hardcoded secret",
|
|
26
|
+
pattern: /(?:password|secret|api.?key|token)\s*[:=]\s*['"][^'"]{8,}/i,
|
|
27
|
+
severity: "critical",
|
|
28
|
+
recommendation: "Use environment variables or secrets manager",
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
id: "SNIP-004",
|
|
32
|
+
title: "eval() usage",
|
|
33
|
+
pattern: /\beval\s*\(|new\s+Function\s*\(/i,
|
|
34
|
+
severity: "high",
|
|
35
|
+
recommendation: "Use safe expression parsers instead of eval",
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
id: "SNIP-005",
|
|
39
|
+
title: "Empty catch block",
|
|
40
|
+
pattern: /catch\s*\([^)]*\)\s*\{\s*\}/,
|
|
41
|
+
severity: "medium",
|
|
42
|
+
recommendation: "Log the error or re-throw it",
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
id: "SNIP-006",
|
|
46
|
+
title: "Console.log in production code",
|
|
47
|
+
pattern: /console\.(log|debug)\s*\(/,
|
|
48
|
+
severity: "low",
|
|
49
|
+
recommendation: "Use a proper logging framework",
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
id: "SNIP-007",
|
|
53
|
+
title: "Insecure HTTP",
|
|
54
|
+
pattern: /['"]http:\/\/(?!localhost|127\.0\.0\.1)/i,
|
|
55
|
+
severity: "medium",
|
|
56
|
+
recommendation: "Use HTTPS for all external connections",
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
id: "SNIP-008",
|
|
60
|
+
title: "Weak crypto",
|
|
61
|
+
pattern: /Math\.random\s*\(\)|createHash\s*\(\s*['"]md5['"]\)/i,
|
|
62
|
+
severity: "high",
|
|
63
|
+
recommendation: "Use crypto.randomUUID() or SHA-256+",
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
id: "SNIP-009",
|
|
67
|
+
title: "Command injection risk",
|
|
68
|
+
pattern: /execSync\s*\(.*\+|spawn\s*\(.*\$\{/i,
|
|
69
|
+
severity: "critical",
|
|
70
|
+
recommendation: "Validate/sanitize inputs, use array args with spawn",
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
id: "SNIP-010",
|
|
74
|
+
title: "Permissive CORS",
|
|
75
|
+
pattern: /cors\(\s*\)|Allow-Origin.*\*/i,
|
|
76
|
+
severity: "medium",
|
|
77
|
+
recommendation: "Specify allowed origins explicitly",
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
id: "SNIP-011",
|
|
81
|
+
title: "Missing error handling",
|
|
82
|
+
pattern: /\.then\s*\([^)]*\)\s*(?!\.catch)/i,
|
|
83
|
+
severity: "medium",
|
|
84
|
+
recommendation: "Add .catch() or use async/await with try/catch",
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
id: "SNIP-012",
|
|
88
|
+
title: "SELECT * usage",
|
|
89
|
+
pattern: /SELECT\s+\*\s+FROM/i,
|
|
90
|
+
severity: "low",
|
|
91
|
+
recommendation: "Select only needed columns",
|
|
92
|
+
},
|
|
93
|
+
];
|
|
94
|
+
function scanSnippet(code, _lang) {
|
|
95
|
+
const findings = [];
|
|
96
|
+
const lines = code.split("\n");
|
|
97
|
+
for (let i = 0; i < lines.length; i++) {
|
|
98
|
+
for (const rule of SNIPPET_RULES) {
|
|
99
|
+
if (rule.pattern.test(lines[i])) {
|
|
100
|
+
if (!findings.some((f) => f.ruleId === rule.id && Math.abs(f.line - (i + 1)) < 3)) {
|
|
101
|
+
findings.push({
|
|
102
|
+
ruleId: rule.id,
|
|
103
|
+
title: rule.title,
|
|
104
|
+
severity: rule.severity,
|
|
105
|
+
line: i + 1,
|
|
106
|
+
recommendation: rule.recommendation,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return findings;
|
|
113
|
+
}
|
|
114
|
+
// ─── Language Detection ────────────────────────────────────────────────────
|
|
115
|
+
function detectLanguage(code) {
|
|
116
|
+
if (/\bimport\s+\{.*\}\s+from\s+['"]|:\s*(string|number|boolean|void)\b|interface\s+\w+/.test(code))
|
|
117
|
+
return "typescript";
|
|
118
|
+
if (/\bdef\s+\w+\s*\(|import\s+\w+\s*$|from\s+\w+\s+import/m.test(code))
|
|
119
|
+
return "python";
|
|
120
|
+
if (/\bfunc\s+\w+\s*\(|package\s+\w+|:=\s*/.test(code))
|
|
121
|
+
return "go";
|
|
122
|
+
if (/\bfn\s+\w+\s*\(|let\s+mut\s|impl\s+\w+/.test(code))
|
|
123
|
+
return "rust";
|
|
124
|
+
if (/\bpublic\s+class\s|System\.out\.|@Override/.test(code))
|
|
125
|
+
return "java";
|
|
126
|
+
if (/\bnamespace\s+\w+|using\s+System|public\s+async\s+Task/.test(code))
|
|
127
|
+
return "csharp";
|
|
128
|
+
if (/\bfunction\s+\w+|const\s+\w+\s*=|require\s*\(/.test(code))
|
|
129
|
+
return "javascript";
|
|
130
|
+
return "unknown";
|
|
131
|
+
}
|
|
132
|
+
// ─── CLI ────────────────────────────────────────────────────────────────────
|
|
133
|
+
export function runSnippetEval(argv) {
|
|
134
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
135
|
+
console.log(`
|
|
136
|
+
judges snippet-eval — Evaluate code snippets instantly
|
|
137
|
+
|
|
138
|
+
Usage:
|
|
139
|
+
echo "const key = '12345'" | judges snippet-eval
|
|
140
|
+
judges snippet-eval --code "eval(userInput)"
|
|
141
|
+
judges snippet-eval --code "SELECT * FROM users WHERE id=" --lang sql
|
|
142
|
+
judges snippet-eval --demo
|
|
143
|
+
|
|
144
|
+
Options:
|
|
145
|
+
--code <snippet> Code snippet to evaluate (or pipe via stdin)
|
|
146
|
+
--lang <language> Language hint (auto-detected if omitted)
|
|
147
|
+
--demo Run with demo vulnerable code
|
|
148
|
+
--format json JSON output
|
|
149
|
+
--help, -h Show this help
|
|
150
|
+
|
|
151
|
+
Zero-friction evaluation — no project setup needed.
|
|
152
|
+
`);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
|
|
156
|
+
const codeArg = argv.find((_a, i) => argv[i - 1] === "--code") || "";
|
|
157
|
+
const langArg = argv.find((_a, i) => argv[i - 1] === "--lang") || "";
|
|
158
|
+
const isDemo = argv.includes("--demo");
|
|
159
|
+
let code;
|
|
160
|
+
if (isDemo) {
|
|
161
|
+
code = `// AI-generated API handler
|
|
162
|
+
const query = "SELECT * FROM users WHERE id=" + req.params.id;
|
|
163
|
+
const apiKey = "sk-proj-1234567890abcdef1234567890";
|
|
164
|
+
document.innerHTML = userInput;
|
|
165
|
+
try { await riskyOperation(); } catch (e) {}
|
|
166
|
+
console.log("Debug: user token = " + token);
|
|
167
|
+
fetch("http://api.example.com/data");
|
|
168
|
+
const sessionId = Math.random().toString(36);`;
|
|
169
|
+
}
|
|
170
|
+
else if (codeArg) {
|
|
171
|
+
code = codeArg;
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
// Try reading from stdin
|
|
175
|
+
try {
|
|
176
|
+
code = readFileSync(0, "utf-8");
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
console.error(" Provide code via --code, --demo, or pipe to stdin");
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
if (!code.trim()) {
|
|
184
|
+
console.error(" No code provided");
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
const lang = langArg || detectLanguage(code);
|
|
188
|
+
const findings = scanSnippet(code, lang);
|
|
189
|
+
// Determine verdict
|
|
190
|
+
const hasCritical = findings.some((f) => f.severity === "critical");
|
|
191
|
+
const hasHigh = findings.some((f) => f.severity === "high");
|
|
192
|
+
const verdict = hasCritical || findings.length > 5
|
|
193
|
+
? "FAIL"
|
|
194
|
+
: hasHigh || findings.length > 2
|
|
195
|
+
? "WARN"
|
|
196
|
+
: findings.length === 0
|
|
197
|
+
? "SAFE"
|
|
198
|
+
: "WARN";
|
|
199
|
+
if (format === "json") {
|
|
200
|
+
console.log(JSON.stringify({
|
|
201
|
+
verdict,
|
|
202
|
+
language: lang,
|
|
203
|
+
findings,
|
|
204
|
+
lines: code.split("\n").length,
|
|
205
|
+
timestamp: new Date().toISOString(),
|
|
206
|
+
}, null, 2));
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
const badge = verdict === "SAFE" ? "✅ SAFE" : verdict === "WARN" ? "⚠️ WARN" : "❌ FAIL";
|
|
210
|
+
console.log(`\n Snippet Eval: ${badge} (${lang}, ${code.split("\n").length} lines)\n ──────────────────────────`);
|
|
211
|
+
if (findings.length === 0) {
|
|
212
|
+
console.log(" No issues detected in snippet.");
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
for (const f of findings) {
|
|
216
|
+
const icon = f.severity === "critical" ? "🔴" : f.severity === "high" ? "🟠" : f.severity === "medium" ? "🟡" : "⚪";
|
|
217
|
+
console.log(` ${icon} L${f.line} [${f.severity}] ${f.ruleId}: ${f.title}`);
|
|
218
|
+
console.log(` 💡 ${f.recommendation}`);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
console.log(`\n ${findings.length} finding(s) | Verdict: ${verdict}\n`);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
//# sourceMappingURL=snippet-eval.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snippet-eval.js","sourceRoot":"","sources":["../../src/commands/snippet-eval.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-quality.d.ts","sourceRoot":"","sources":["../../src/commands/test-quality.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAuHH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAwEnD"}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test quality — score test suites for assertion density, boundary coverage,
|
|
3
|
+
* flakiness patterns, and mutation-testing readiness.
|
|
4
|
+
*/
|
|
5
|
+
import { readFileSync, readdirSync, statSync } from "fs";
|
|
6
|
+
import { join, extname, basename } from "path";
|
|
7
|
+
// ─── File Collection ────────────────────────────────────────────────────────
|
|
8
|
+
const TEST_EXTS = new Set([".ts", ".tsx", ".js", ".jsx", ".py", ".java", ".go", ".rs"]);
|
|
9
|
+
const TEST_PATTERNS = [/\.test\./i, /\.spec\./i, /test_/i, /_test\./i];
|
|
10
|
+
function isTestFile(name) {
|
|
11
|
+
return TEST_PATTERNS.some((p) => p.test(name));
|
|
12
|
+
}
|
|
13
|
+
function collectTestFiles(dir, max = 300) {
|
|
14
|
+
const files = [];
|
|
15
|
+
function walk(d) {
|
|
16
|
+
if (files.length >= max)
|
|
17
|
+
return;
|
|
18
|
+
let entries;
|
|
19
|
+
try {
|
|
20
|
+
entries = readdirSync(d);
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
for (const e of entries) {
|
|
26
|
+
if (files.length >= max)
|
|
27
|
+
return;
|
|
28
|
+
if (e.startsWith(".") || e === "node_modules" || e === "dist" || e === "build")
|
|
29
|
+
continue;
|
|
30
|
+
const full = join(d, e);
|
|
31
|
+
try {
|
|
32
|
+
if (statSync(full).isDirectory())
|
|
33
|
+
walk(full);
|
|
34
|
+
else if (TEST_EXTS.has(extname(full)) && isTestFile(basename(full)))
|
|
35
|
+
files.push(full);
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
/* skip */
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
walk(dir);
|
|
43
|
+
return files;
|
|
44
|
+
}
|
|
45
|
+
// ─── Analysis ───────────────────────────────────────────────────────────────
|
|
46
|
+
function analyzeTestFile(filepath) {
|
|
47
|
+
const issues = [];
|
|
48
|
+
let content;
|
|
49
|
+
try {
|
|
50
|
+
content = readFileSync(filepath, "utf-8");
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return { file: filepath, tests: 0, assertions: 0, assertionDensity: 0, issues: ["Could not read file"] };
|
|
54
|
+
}
|
|
55
|
+
// Count tests
|
|
56
|
+
const testMatches = content.match(/(?:it|test|specify)\s*\(|def\s+test_|@Test|func\s+Test/g) || [];
|
|
57
|
+
const tests = testMatches.length;
|
|
58
|
+
// Count assertions
|
|
59
|
+
const assertionPatterns = /assert|expect|should|toBe|toEqual|toMatch|toThrow|toContain|toHaveLength|toHaveBeenCalled|assertEqual|assertIn|assertRaises|assert_eq!|require\./g;
|
|
60
|
+
const assertions = (content.match(assertionPatterns) || []).length;
|
|
61
|
+
const assertionDensity = tests > 0 ? assertions / tests : 0;
|
|
62
|
+
// Zero-assertion tests
|
|
63
|
+
if (tests > 0 && assertions === 0) {
|
|
64
|
+
issues.push("No assertions found — tests that never assert prove nothing");
|
|
65
|
+
}
|
|
66
|
+
else if (assertionDensity < 1 && tests > 0) {
|
|
67
|
+
issues.push(`Low assertion density (${assertionDensity.toFixed(1)}/test) — some tests may lack assertions`);
|
|
68
|
+
}
|
|
69
|
+
// setTimeout / sleep (flakiness)
|
|
70
|
+
if (/setTimeout|sleep|time\.sleep|Thread\.sleep|time\.After/i.test(content)) {
|
|
71
|
+
issues.push("Timer-based waits detected — flakiness risk");
|
|
72
|
+
}
|
|
73
|
+
// Snapshot auto-update
|
|
74
|
+
if (/toMatchSnapshot|toMatchInlineSnapshot/i.test(content)) {
|
|
75
|
+
issues.push("Snapshot tests found — check for auto-update masking regressions");
|
|
76
|
+
}
|
|
77
|
+
// Mock overuse
|
|
78
|
+
const mockCount = (content.match(/mock|jest\.fn|sinon\.stub|patch\(|@Mock|gomock/gi) || []).length;
|
|
79
|
+
if (mockCount > 10) {
|
|
80
|
+
issues.push(`Heavy mocking (${mockCount} mocks) — test may not reflect real behavior`);
|
|
81
|
+
}
|
|
82
|
+
// Missing edge cases
|
|
83
|
+
const hasNullCheck = /null|undefined|nil|None|empty|boundary|edge/i.test(content);
|
|
84
|
+
const hasErrorCheck = /error|exception|throw|reject|fail|panic/i.test(content);
|
|
85
|
+
if (!hasNullCheck && !hasErrorCheck && tests > 0) {
|
|
86
|
+
issues.push("No null/error/boundary test cases — missing edge coverage");
|
|
87
|
+
}
|
|
88
|
+
// Hardcoded values only
|
|
89
|
+
const hasParameterized = /\.each|@parameterized|@pytest\.mark\.parametrize|testCases|table.?driven/i.test(content);
|
|
90
|
+
if (tests >= 5 && !hasParameterized) {
|
|
91
|
+
issues.push("No parameterized tests — consider table-driven testing for broader coverage");
|
|
92
|
+
}
|
|
93
|
+
// Commented-out tests
|
|
94
|
+
const commentedTests = (content.match(/\/\/\s*(it|test|describe)\s*\(|#\s*def\s+test_/g) || []).length;
|
|
95
|
+
if (commentedTests > 0) {
|
|
96
|
+
issues.push(`${commentedTests} commented-out test(s) — remove or restore`);
|
|
97
|
+
}
|
|
98
|
+
return { file: filepath, tests, assertions, assertionDensity, issues };
|
|
99
|
+
}
|
|
100
|
+
// ─── CLI ────────────────────────────────────────────────────────────────────
|
|
101
|
+
export function runTestQuality(argv) {
|
|
102
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
103
|
+
console.log(`
|
|
104
|
+
judges test-quality — Score test suites beyond coverage percentage
|
|
105
|
+
|
|
106
|
+
Usage:
|
|
107
|
+
judges test-quality [dir]
|
|
108
|
+
judges test-quality tests/ --format json
|
|
109
|
+
|
|
110
|
+
Options:
|
|
111
|
+
[dir] Directory to scan (default: .)
|
|
112
|
+
--format json JSON output
|
|
113
|
+
--help, -h Show this help
|
|
114
|
+
|
|
115
|
+
Checks: assertion density, zero-assertion tests, timer-based waits, snapshot abuse,
|
|
116
|
+
mock overuse, missing edge cases, parameterized tests, commented-out tests.
|
|
117
|
+
`);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
|
|
121
|
+
const dir = argv.find((a) => !a.startsWith("-") && argv.indexOf(a) > 0) || ".";
|
|
122
|
+
const files = collectTestFiles(dir);
|
|
123
|
+
const reports = files.map(analyzeTestFile);
|
|
124
|
+
const totalTests = reports.reduce((s, r) => s + r.tests, 0);
|
|
125
|
+
const totalAssertions = reports.reduce((s, r) => s + r.assertions, 0);
|
|
126
|
+
const totalIssues = reports.reduce((s, r) => s + r.issues.length, 0);
|
|
127
|
+
const avgDensity = totalTests > 0 ? totalAssertions / totalTests : 0;
|
|
128
|
+
const score = Math.max(0, Math.min(100, Math.round(100 - totalIssues * 5)));
|
|
129
|
+
if (format === "json") {
|
|
130
|
+
console.log(JSON.stringify({
|
|
131
|
+
reports,
|
|
132
|
+
score,
|
|
133
|
+
summary: {
|
|
134
|
+
files: files.length,
|
|
135
|
+
tests: totalTests,
|
|
136
|
+
assertions: totalAssertions,
|
|
137
|
+
avgDensity: +avgDensity.toFixed(2),
|
|
138
|
+
issues: totalIssues,
|
|
139
|
+
},
|
|
140
|
+
timestamp: new Date().toISOString(),
|
|
141
|
+
}, null, 2));
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
const badge = score >= 80 ? "✅ GOOD" : score >= 50 ? "⚠️ FAIR" : "❌ POOR";
|
|
145
|
+
console.log(`\n Test Quality: ${badge} (${score}/100)\n ──────────────────────────`);
|
|
146
|
+
console.log(` Files: ${files.length} | Tests: ${totalTests} | Assertions: ${totalAssertions} | Avg density: ${avgDensity.toFixed(1)}/test\n`);
|
|
147
|
+
const problematic = reports.filter((r) => r.issues.length > 0);
|
|
148
|
+
if (problematic.length === 0) {
|
|
149
|
+
console.log(" No quality issues detected.\n");
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
for (const r of problematic) {
|
|
153
|
+
console.log(` 📄 ${r.file} (${r.tests} tests, ${r.assertions} assertions)`);
|
|
154
|
+
for (const issue of r.issues) {
|
|
155
|
+
console.log(` ⚠️ ${issue}`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
console.log(`\n Total issues: ${totalIssues} | Score: ${score}/100\n`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=test-quality.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-quality.js","sourceRoot":"","sources":["../../src/commands/test-quality.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/server.json
CHANGED
|
@@ -7,12 +7,12 @@
|
|
|
7
7
|
"url": "https://github.com/kevinrabun/judges",
|
|
8
8
|
"source": "github"
|
|
9
9
|
},
|
|
10
|
-
"version": "3.
|
|
10
|
+
"version": "3.55.0",
|
|
11
11
|
"packages": [
|
|
12
12
|
{
|
|
13
13
|
"registryType": "npm",
|
|
14
14
|
"identifier": "@kevinrabun/judges",
|
|
15
|
-
"version": "3.
|
|
15
|
+
"version": "3.55.0",
|
|
16
16
|
"transport": {
|
|
17
17
|
"type": "stdio"
|
|
18
18
|
}
|