diff --git a/README.md b/README.md index f118fc6..833a107 100644 --- a/README.md +++ b/README.md @@ -138,9 +138,7 @@ Current checks focus on patterns that often show up in unreviewed generated code - [generic status envelopes](src/rules/generic-status-envelopes/README.md) - [generic record casts](src/rules/generic-record-casts/README.md) - [stringified unknown errors](src/rules/stringified-unknown-errors/README.md) -- [async wrapper / `return await` noise](src/rules/async-noise/README.md) - [pass-through wrappers](src/rules/pass-through-wrappers/README.md) -- [duplicate helper/function signatures across source files](src/rules/duplicate-function-signatures/README.md) - [duplicated test mock/setup patterns](src/rules/duplicate-mock-setup/README.md) `scan` reports raw + normalized scores, hotspot tables, and grouped findings. Use `--json` when you want the full evidence payload. diff --git a/src/default-registry.ts b/src/default-registry.ts index b7b2fa4..7d3ece3 100644 --- a/src/default-registry.ts +++ b/src/default-registry.ts @@ -12,7 +12,6 @@ import { javascriptLikeLanguage } from "./languages/javascript-like"; import { jsonReporter } from "./reporters/json"; import { lintReporter } from "./reporters/lint"; import { textReporter } from "./reporters/text"; -import { asyncNoiseRule } from "./rules/async-noise"; import { emptyCatchRule } from "./rules/empty-catch"; import { errorObscuringRule } from "./rules/error-obscuring"; import { errorSwallowingRule } from "./rules/error-swallowing"; @@ -20,7 +19,6 @@ import { promiseDefaultFallbacksRule } from "./rules/promise-default-fallbacks"; import { genericStatusEnvelopesRule } from "./rules/generic-status-envelopes"; import { genericRecordCastsRule } from "./rules/generic-record-casts"; import { stringifiedUnknownErrorsRule } from "./rules/stringified-unknown-errors"; -import { duplicateFunctionSignaturesRule } from "./rules/duplicate-function-signatures"; import { passThroughWrappersRule } from "./rules/pass-through-wrappers"; import { duplicateMockSetupRule } from "./rules/duplicate-mock-setup"; @@ -38,7 +36,6 @@ export function createDefaultRegistry(): Registry { registry.registerFactProvider(directoryMetricsFactProvider); registry.registerFactProvider(testDuplicationFactProvider); - registry.registerRule(asyncNoiseRule); registry.registerRule(errorSwallowingRule); registry.registerRule(errorObscuringRule); registry.registerRule(emptyCatchRule); @@ -47,7 +44,6 @@ export function createDefaultRegistry(): Registry { registry.registerRule(genericRecordCastsRule); registry.registerRule(stringifiedUnknownErrorsRule); registry.registerRule(passThroughWrappersRule); - registry.registerRule(duplicateFunctionSignaturesRule); registry.registerRule(duplicateMockSetupRule); registry.registerReporter(textReporter); diff --git a/tests/config.test.ts b/tests/config.test.ts index e7c58f7..32db7b1 100644 --- a/tests/config.test.ts +++ b/tests/config.test.ts @@ -2,10 +2,7 @@ import { afterEach, describe, expect, test } from "bun:test"; import { mkdtemp, mkdir, rm, writeFile } from "node:fs/promises"; import os from "node:os"; import path from "node:path"; -import { analyzeRepository } from "../src/core/engine"; -import type { AnalyzerConfig } from "../src/config"; -import { DEFAULT_CONFIG, loadConfig } from "../src/config"; -import { createDefaultRegistry } from "../src/default-registry"; +import { loadConfig } from "../src/config"; const tempDirs: string[] = []; @@ -24,64 +21,7 @@ async function createTempRepo(): Promise { return rootDir; } -function withRuleConfig( - ruleId: string, - config: { enabled?: boolean; weight?: number }, -): AnalyzerConfig { - return { - ...DEFAULT_CONFIG, - rules: { - ...DEFAULT_CONFIG.rules, - [ruleId]: config, - }, - }; -} - -function withPathOverride( - files: string[], - rules: Record, -): AnalyzerConfig { - return { - ...DEFAULT_CONFIG, - overrides: [{ files, rules }], - }; -} - describe("rule config support", () => { - test("can disable a rule via config", async () => { - const rootDir = await createTempRepo(); - const result = await analyzeRepository( - rootDir, - withRuleConfig("defensive.async-noise", { enabled: false }), - createDefaultRegistry(), - ); - - expect( - result.findings.filter((finding) => finding.ruleId === "defensive.async-noise"), - ).toHaveLength(0); - }); - - test("can weight a rule via config", async () => { - const rootDir = await createTempRepo(); - const baseline = await analyzeRepository(rootDir, DEFAULT_CONFIG, createDefaultRegistry()); - const weighted = await analyzeRepository( - rootDir, - withRuleConfig("defensive.async-noise", { weight: 2 }), - createDefaultRegistry(), - ); - - const baselineAsyncNoise = baseline.findings.find( - (finding) => finding.ruleId === "defensive.async-noise", - ); - const weightedAsyncNoise = weighted.findings.find( - (finding) => finding.ruleId === "defensive.async-noise", - ); - - expect(baselineAsyncNoise).toBeDefined(); - expect(weightedAsyncNoise).toBeDefined(); - expect(weightedAsyncNoise?.score).toBeCloseTo((baselineAsyncNoise?.score ?? 0) * 2, 6); - }); - test("loadConfig reads slop-scan.config.json", async () => { const rootDir = await createTempRepo(); await writeFile( @@ -109,62 +49,6 @@ describe("rule config support", () => { expect(second.ignores).toEqual(["src/nested.ts"]); }); - test("can apply a path-scoped file override", async () => { - const rootDir = await createTempRepo(); - await writeFile( - path.join(rootDir, "src", "nested.ts"), - "function fetchRemote(input: string) {\n return Promise.resolve(input);\n}\n\nexport async function loadValue(id: string) {\n return await fetchRemote(id);\n}\n", - ); - - const result = await analyzeRepository( - rootDir, - withPathOverride(["src/comments.ts"], { - "defensive.async-noise": { enabled: false }, - }), - createDefaultRegistry(), - ); - - const asyncNoiseFindings = result.findings.filter( - (finding) => finding.ruleId === "defensive.async-noise", - ); - - expect(asyncNoiseFindings).toHaveLength(1); - expect(asyncNoiseFindings[0]?.path).toBe("src/nested.ts"); - }); - - test("can apply a path-scoped directory override", async () => { - const rootDir = await createTempRepo(); - - await mkdir(path.join(rootDir, "src/rules/defensive"), { recursive: true }); - await writeFile( - path.join(rootDir, "src/rules/defensive/service.ts"), - "function fetchRule(input: string) {\n return Promise.resolve(input);\n}\n\nexport async function loadRule(id: string) {\n return await fetchRule(id);\n}\n", - ); - - await mkdir(path.join(rootDir, "src/other/defensive"), { recursive: true }); - await writeFile( - path.join(rootDir, "src/other/defensive/service.ts"), - "function fetchOther(input: string) {\n return Promise.resolve(input);\n}\n\nexport async function loadOther(id: string) {\n return await fetchOther(id);\n}\n", - ); - - const result = await analyzeRepository( - rootDir, - withPathOverride(["src/rules/**"], { - "defensive.async-noise": { enabled: false }, - }), - createDefaultRegistry(), - ); - - const asyncNoiseFindings = result.findings.filter( - (finding) => finding.ruleId === "defensive.async-noise", - ); - - expect(asyncNoiseFindings.map((finding) => finding.path).sort()).toEqual([ - "src/comments.ts", - "src/other/defensive/service.ts", - ]); - }); - test("loadConfig reads path-scoped overrides", async () => { const rootDir = await createTempRepo(); await writeFile( @@ -174,7 +58,7 @@ describe("rule config support", () => { { files: ["src/comments.ts"], rules: { - "defensive.async-noise": { enabled: false }, + "defensive.error-obscuring": { enabled: false }, }, }, ], @@ -187,7 +71,7 @@ describe("rule config support", () => { { files: ["src/comments.ts"], rules: { - "defensive.async-noise": { enabled: false }, + "defensive.error-obscuring": { enabled: false }, }, }, ]); diff --git a/tests/fixtures-regression.test.ts b/tests/fixtures-regression.test.ts index 93af6d5..fede53f 100644 --- a/tests/fixtures-regression.test.ts +++ b/tests/fixtures-regression.test.ts @@ -28,10 +28,9 @@ describe("fixture regression suite", () => { createDefaultRegistry(), ); - expect(result.repoScore).toBeCloseTo(7.7, 6); - expect(result.findings).toHaveLength(3); + expect(result.repoScore).toBeCloseTo(6.2, 6); + expect(result.findings).toHaveLength(2); expect([...new Set(result.findings.map((finding) => finding.ruleId))].sort()).toEqual([ - "defensive.async-noise", "defensive.error-obscuring", "structure.pass-through-wrappers", ]); @@ -49,7 +48,7 @@ describe("fixture regression suite", () => { createDefaultRegistry(), ); - expect(result.repoScore).toBeCloseTo(6.7, 6); + expect(result.repoScore).toBeCloseTo(5.2, 6); expect(result.fileScores[0]?.path).toBe("src/slop/service.ts"); expect(result.directoryScores).toHaveLength(0); expect(result.fileScores.every((score) => score.path.startsWith("src/slop/"))).toBe(true); @@ -67,8 +66,8 @@ describe("fixture regression suite", () => { expect(output.status).toBe(0); const report = JSON.parse(output.stdout); - expect(report.summary.repoScore).toBeCloseTo(7.7, 6); - expect(report.summary.findingCount).toBe(3); + expect(report.summary.repoScore).toBeCloseTo(6.2, 6); + expect(report.summary.findingCount).toBe(2); expect(report.directoryScores).toHaveLength(0); expect(report.fileScores[0].path).toBe("src/service.ts"); }); @@ -87,7 +86,7 @@ describe("fixture regression suite", () => { "strong Found 1 error-obscuring catch block defensive.error-obscuring", ); expect(output.stdout).toContain(" at src/error.ts:2:1"); - expect(output.stdout).toContain("3 findings"); + expect(output.stdout).toContain("2 findings"); expect(output.stdout).not.toContain("slop-scan report"); }); diff --git a/tests/heuristics.test.ts b/tests/heuristics.test.ts index 12ba066..4e260d6 100644 --- a/tests/heuristics.test.ts +++ b/tests/heuristics.test.ts @@ -96,7 +96,6 @@ describe("heuristic rule pack", () => { expect(ruleIds.has("api.generic-status-envelopes")).toBe(true); expect(ruleIds.has("types.generic-record-casts")).toBe(true); expect(ruleIds.has("defensive.stringified-unknown-errors")).toBe(true); - expect(ruleIds.has("defensive.async-noise")).toBe(true); expect(ruleIds.has("structure.pass-through-wrappers")).toBe(true); expect(result.fileScores.some((score) => score.path === "src/service.ts")).toBe(true); @@ -537,83 +536,6 @@ describe("heuristic rule pack", () => { ).toBe(false); }); - test("flags duplicated helper signatures across source files", async () => { - const rootDir = await createTempRepo({ - "src/users/normalize.ts": [ - "export function normalizeUserId(input: string) {", - " const trimmed = input.trim().toLowerCase();", - " if (!trimmed.startsWith('usr_')) {", - " return `usr_${trimmed}`;", - " }", - "", - " return trimmed;", - "}", - "", - ].join("\n"), - "src/teams/normalize.ts": [ - "export function normalizeTeamId(value: string) {", - " const cleaned = value.trim().toLowerCase();", - " if (!cleaned.startsWith('team_')) {", - " return `team_${cleaned}`;", - " }", - "", - " return cleaned;", - "}", - "", - ].join("\n"), - "src/accounts/normalize.ts": [ - "export function normalizeAccountId(raw: string) {", - " const normalized = raw.trim().toLowerCase();", - " if (!normalized.startsWith('acct_')) {", - " return `acct_${normalized}`;", - " }", - "", - " return normalized;", - "}", - "", - ].join("\n"), - "tests/helpers.test.ts": [ - "function normalizeTestId(input: string) {", - " const trimmed = input.trim().toLowerCase();", - " if (!trimmed.startsWith('test_')) {", - " return `test_${trimmed}`;", - " }", - "", - " return trimmed;", - "}", - "", - "export { normalizeTestId };", - "", - ].join("\n"), - }); - - const result = await analyzeRepository(rootDir, DEFAULT_CONFIG, createDefaultRegistry()); - const duplicateFindings = result.findings.filter( - (finding) => finding.ruleId === "structure.duplicate-function-signatures", - ); - - expect(duplicateFindings).toHaveLength(3); - expect(duplicateFindings.every((finding) => finding.path?.startsWith("src/"))).toBe(true); - expect( - duplicateFindings[0]?.evidence.some((entry) => entry.includes("repeated in 3 files")), - ).toBe(true); - expect( - duplicateFindings[0]?.locations.some( - (location) => location.path === "src/users/normalize.ts", - ), - ).toBe(true); - expect( - duplicateFindings[0]?.locations.some( - (location) => location.path === "src/teams/normalize.ts", - ), - ).toBe(true); - expect( - duplicateFindings[0]?.locations.some( - (location) => location.path === "src/accounts/normalize.ts", - ), - ).toBe(true); - }); - test("stays quiet on a small clean repo", async () => { const rootDir = await createTempRepo({ "src/index.ts": [