@kevinrabun/judges 3.54.0 → 3.56.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/api-versioning-audit.d.ts +6 -0
- package/dist/commands/api-versioning-audit.d.ts.map +1 -0
- package/dist/commands/api-versioning-audit.js +234 -0
- package/dist/commands/api-versioning-audit.js.map +1 -0
- package/dist/commands/boundary-enforce.d.ts +6 -0
- package/dist/commands/boundary-enforce.d.ts.map +1 -0
- package/dist/commands/boundary-enforce.js +256 -0
- package/dist/commands/boundary-enforce.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/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/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/error-taxonomy.d.ts +6 -0
- package/dist/commands/error-taxonomy.d.ts.map +1 -0
- package/dist/commands/error-taxonomy.js +227 -0
- package/dist/commands/error-taxonomy.js.map +1 -0
- package/dist/commands/log-quality.d.ts +6 -0
- package/dist/commands/log-quality.d.ts.map +1 -0
- package/dist/commands/log-quality.js +212 -0
- package/dist/commands/log-quality.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/null-safety-audit.d.ts +6 -0
- package/dist/commands/null-safety-audit.d.ts.map +1 -0
- package/dist/commands/null-safety-audit.js +222 -0
- package/dist/commands/null-safety-audit.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/ownership-map.d.ts +6 -0
- package/dist/commands/ownership-map.d.ts.map +1 -0
- package/dist/commands/ownership-map.js +229 -0
- package/dist/commands/ownership-map.js.map +1 -0
- package/dist/commands/retry-pattern-audit.d.ts +6 -0
- package/dist/commands/retry-pattern-audit.d.ts.map +1 -0
- package/dist/commands/retry-pattern-audit.js +216 -0
- package/dist/commands/retry-pattern-audit.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/test-isolation.d.ts +6 -0
- package/dist/commands/test-isolation.d.ts.map +1 -0
- package/dist/commands/test-isolation.js +235 -0
- package/dist/commands/test-isolation.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,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test isolation — detect shared mutable state, ordering dependencies,
|
|
3
|
+
* and resource leaks between test cases.
|
|
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"]);
|
|
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 analyzeFile(filepath) {
|
|
47
|
+
const issues = [];
|
|
48
|
+
let content;
|
|
49
|
+
try {
|
|
50
|
+
content = readFileSync(filepath, "utf-8");
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return issues;
|
|
54
|
+
}
|
|
55
|
+
const lines = content.split("\n");
|
|
56
|
+
// Track scope context
|
|
57
|
+
let inDescribe = false;
|
|
58
|
+
let describeDepth = 0;
|
|
59
|
+
for (let i = 0; i < lines.length; i++) {
|
|
60
|
+
const line = lines[i];
|
|
61
|
+
if (/\b(?:describe|context|suite)\s*\(/.test(line)) {
|
|
62
|
+
inDescribe = true;
|
|
63
|
+
describeDepth++;
|
|
64
|
+
}
|
|
65
|
+
if (inDescribe && /^\s*\}\s*\)\s*;?\s*$/.test(line)) {
|
|
66
|
+
describeDepth--;
|
|
67
|
+
if (describeDepth <= 0)
|
|
68
|
+
inDescribe = false;
|
|
69
|
+
}
|
|
70
|
+
// Global mutable state
|
|
71
|
+
if (/^\s*(?:let|var)\s+\w+\s*=/.test(line) && !inDescribe) {
|
|
72
|
+
const varName = line.match(/(?:let|var)\s+(\w+)/)?.[1];
|
|
73
|
+
if (varName && /\b(?:state|data|count|result|output|db|conn|client|server)\b/i.test(varName)) {
|
|
74
|
+
issues.push({
|
|
75
|
+
file: filepath,
|
|
76
|
+
line: i + 1,
|
|
77
|
+
issue: "Mutable global test state",
|
|
78
|
+
severity: "high",
|
|
79
|
+
detail: `Global \`${varName}\` — tests sharing mutable state cause order-dependent failures`,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// Module-level side effects in test files
|
|
84
|
+
if (/^\s*(?:fs\.|writeFileSync|mkdirSync|execSync|spawn|createServer)/.test(line) && !inDescribe) {
|
|
85
|
+
issues.push({
|
|
86
|
+
file: filepath,
|
|
87
|
+
line: i + 1,
|
|
88
|
+
issue: "Module-level side effect",
|
|
89
|
+
severity: "high",
|
|
90
|
+
detail: "Side effects outside test blocks affect all tests in the file",
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
// Shared fixtures without reset
|
|
94
|
+
if (/\b(?:beforeAll|before)\s*\(\s*(?:async\s+)?(?:function|\(|=>)/.test(line)) {
|
|
95
|
+
const beforeBlock = lines.slice(i, Math.min(i + 15, lines.length)).join("\n");
|
|
96
|
+
const hasAfter = /\b(?:afterAll|afterEach|after)\s*\(/.test(content);
|
|
97
|
+
if (!hasAfter && /(?:create|setup|init|connect|insert|write)/i.test(beforeBlock)) {
|
|
98
|
+
issues.push({
|
|
99
|
+
file: filepath,
|
|
100
|
+
line: i + 1,
|
|
101
|
+
issue: "Setup without teardown",
|
|
102
|
+
severity: "high",
|
|
103
|
+
detail: "beforeAll creates resources but no afterAll/afterEach cleans them — leaked state",
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
// File system operations without cleanup
|
|
108
|
+
if (/(?:writeFileSync|mkdirSync|writeFile|mkdir)\s*\(/.test(line)) {
|
|
109
|
+
const hasCleanup = /(?:unlinkSync|rmdirSync|rimraf|rm\s*\(|removeSync|afterEach.*unlink|afterAll.*unlink)/i.test(content);
|
|
110
|
+
if (!hasCleanup) {
|
|
111
|
+
issues.push({
|
|
112
|
+
file: filepath,
|
|
113
|
+
line: i + 1,
|
|
114
|
+
issue: "File system write without cleanup",
|
|
115
|
+
severity: "medium",
|
|
116
|
+
detail: "Test creates files but never cleans up — pollutes workspace",
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
// Network listeners without close
|
|
121
|
+
if (/(?:createServer|listen)\s*\(/.test(line)) {
|
|
122
|
+
const hasClose = /(?:\.close\s*\(|server\.close|afterAll.*close|afterEach.*close)/i.test(content);
|
|
123
|
+
if (!hasClose) {
|
|
124
|
+
issues.push({
|
|
125
|
+
file: filepath,
|
|
126
|
+
line: i + 1,
|
|
127
|
+
issue: "Server started without closing",
|
|
128
|
+
severity: "high",
|
|
129
|
+
detail: "Test starts a server but never closes it — port conflicts in parallel runs",
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
// setTimeout in tests (flakiness)
|
|
134
|
+
if (/setTimeout\s*\(\s*(?:resolve|done|callback|cb)\s*,\s*\d+/.test(line)) {
|
|
135
|
+
issues.push({
|
|
136
|
+
file: filepath,
|
|
137
|
+
line: i + 1,
|
|
138
|
+
issue: "setTimeout for async coordination",
|
|
139
|
+
severity: "medium",
|
|
140
|
+
detail: "Timer-based waits are flaky — use event-based or polling patterns",
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
// Order-dependent assertions (relying on previous test state)
|
|
144
|
+
if (/\.toBe\(.*\+\+|\.toEqual\(.*count/i.test(line)) {
|
|
145
|
+
issues.push({
|
|
146
|
+
file: filepath,
|
|
147
|
+
line: i + 1,
|
|
148
|
+
issue: "Assertion relies on accumulated state",
|
|
149
|
+
severity: "medium",
|
|
150
|
+
detail: "Tests should be independently runnable — avoid cross-test counters",
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
// Shared database connections
|
|
154
|
+
if (/(?:mongoose\.connect|createConnection|pg\.Pool|knex|prisma\.\$connect|sequelize)/i.test(line) && !inDescribe) {
|
|
155
|
+
issues.push({
|
|
156
|
+
file: filepath,
|
|
157
|
+
line: i + 1,
|
|
158
|
+
issue: "Shared database connection",
|
|
159
|
+
severity: "medium",
|
|
160
|
+
detail: "Use per-test or per-suite DB connections with transaction rollback",
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
// Environment variable mutation
|
|
164
|
+
if (/process\.env\.\w+\s*=/.test(line)) {
|
|
165
|
+
const hasRestore = /process\.env\.\w+\s*=.*original|delete\s+process\.env|afterEach.*env/i.test(content);
|
|
166
|
+
if (!hasRestore) {
|
|
167
|
+
issues.push({
|
|
168
|
+
file: filepath,
|
|
169
|
+
line: i + 1,
|
|
170
|
+
issue: "Environment mutation without restore",
|
|
171
|
+
severity: "high",
|
|
172
|
+
detail: "Setting process.env leaks to other tests — restore in afterEach",
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return issues;
|
|
178
|
+
}
|
|
179
|
+
// ─── CLI ────────────────────────────────────────────────────────────────────
|
|
180
|
+
export function runTestIsolation(argv) {
|
|
181
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
182
|
+
console.log(`
|
|
183
|
+
judges test-isolation — Detect test isolation violations
|
|
184
|
+
|
|
185
|
+
Usage:
|
|
186
|
+
judges test-isolation [dir]
|
|
187
|
+
judges test-isolation tests/ --format json
|
|
188
|
+
|
|
189
|
+
Options:
|
|
190
|
+
[dir] Directory to scan (default: .)
|
|
191
|
+
--format json JSON output
|
|
192
|
+
--help, -h Show this help
|
|
193
|
+
|
|
194
|
+
Detects: shared mutable state, missing teardown, file system leaks, unclosed servers,
|
|
195
|
+
setTimeout waits, environment mutations, shared DB connections.
|
|
196
|
+
`);
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
|
|
200
|
+
const dir = argv.find((a) => !a.startsWith("-") && argv.indexOf(a) > 0) || ".";
|
|
201
|
+
const files = collectTestFiles(dir);
|
|
202
|
+
const allIssues = [];
|
|
203
|
+
for (const f of files)
|
|
204
|
+
allIssues.push(...analyzeFile(f));
|
|
205
|
+
const highCount = allIssues.filter((i) => i.severity === "high").length;
|
|
206
|
+
const medCount = allIssues.filter((i) => i.severity === "medium").length;
|
|
207
|
+
const score = Math.max(0, 100 - highCount * 10 - medCount * 5);
|
|
208
|
+
if (format === "json") {
|
|
209
|
+
console.log(JSON.stringify({
|
|
210
|
+
issues: allIssues,
|
|
211
|
+
score,
|
|
212
|
+
summary: { files: files.length, high: highCount, medium: medCount, total: allIssues.length },
|
|
213
|
+
timestamp: new Date().toISOString(),
|
|
214
|
+
}, null, 2));
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
const badge = score >= 80 ? "✅ ISOLATED" : score >= 50 ? "⚠️ LEAKY" : "❌ COUPLED";
|
|
218
|
+
console.log(`\n Test Isolation: ${badge} (${score}/100)\n ─────────────────────────`);
|
|
219
|
+
console.log(` Test files: ${files.length}\n`);
|
|
220
|
+
if (allIssues.length === 0) {
|
|
221
|
+
console.log(" No isolation issues detected.\n");
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
for (const issue of allIssues.slice(0, 25)) {
|
|
225
|
+
const icon = issue.severity === "high" ? "🔴" : issue.severity === "medium" ? "🟡" : "🔵";
|
|
226
|
+
console.log(` ${icon} ${issue.issue}`);
|
|
227
|
+
console.log(` ${issue.file}:${issue.line}`);
|
|
228
|
+
console.log(` ${issue.detail}`);
|
|
229
|
+
}
|
|
230
|
+
if (allIssues.length > 25)
|
|
231
|
+
console.log(` ... and ${allIssues.length - 25} more`);
|
|
232
|
+
console.log(`\n Total: ${allIssues.length} | High: ${highCount} | Medium: ${medCount} | Score: ${score}/100\n`);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
//# sourceMappingURL=test-isolation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-isolation.js","sourceRoot":"","sources":["../../src/commands/test-isolation.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.56.0",
|
|
11
11
|
"packages": [
|
|
12
12
|
{
|
|
13
13
|
"registryType": "npm",
|
|
14
14
|
"identifier": "@kevinrabun/judges",
|
|
15
|
-
"version": "3.
|
|
15
|
+
"version": "3.56.0",
|
|
16
16
|
"transport": {
|
|
17
17
|
"type": "stdio"
|
|
18
18
|
}
|