From 645f991f0ec3fefd382bec4eca6ca8487171eb63 Mon Sep 17 00:00:00 2001 From: JSONbored <49853598+JSONbored@users.noreply.github.com> Date: Fri, 10 Jul 2026 03:51:17 -0700 Subject: [PATCH] chore(cleanup): remove 12 confirmed-dead exports (#4619) Dead-code sweep from the review-stack architecture audit (#4619): each item was grep/git-blame-verified as having zero real call sites outside its own declaration and test files, re-verified fresh against main before deletion. Removed: countRecentSubmissionsByAuthor (db/repositories.ts), ANALYZER_REGISTRY (review-enrichment registry.ts, a redundant alias of ANALYZERS), scanDependencies (review-enrichment dependency-scan.ts, superseded by the descriptor-based scanDependencyChanges), ISSUE_SUBMISSION_LABELS + AI_REVIEW_VERDICTS (review/content-lane/registry-logic.ts), REPO_DOC_TEMPLATE_VERSION (review/repo-doc-render.ts), githubRateLimitAdmissionRemainingFloor (selfhost/queue-common.ts), __linkedIssueSatisfactionRunInternals (services/linked-issue-satisfaction-run.ts), ALL_TYPE_LABELS (both the server and gittensory-engine copies of settings/pr-type-label.ts), WorkerEntrypoint (selfhost/cf-workers-shim.ts), and detectTestConvention/TestConvention (gittensory-engine signals/test-evidence.ts, plus the two internal marker tables that only ever fed it). Also removes scanDependencies's two now-orphaned tests from review-enrichment/test/enrichment.test.ts -- the equivalent behavior stays covered by extractDependencyChanges's and scanDependencyChanges's own direct tests -- and detectTestConvention's describe block from test-evidence.test.ts. RpcTarget in src/selfhost/cf-workers-shim.ts is removed too (same shim-parity reasoning as WorkerEntrypoint), but its copy in test/stubs/cloudflare-workers.ts is kept: vitest aliases the cloudflare:workers specifier to that file for every module resolved under Node, and the agents package (a dependency of src/mcp/server.ts's agents/mcp import) extends RpcTarget from it -- a grep-only pass over this repo's own source would have missed that, since the only real caller lives in node_modules. The issue_quality_reports/registry_drift_events tables and their upsertIssueQualityReport/persistRegistryDriftEvents functions are deliberately left untouched pending explicit sign-off on production data before a DROP TABLE migration ships. --- .../src/settings/pr-type-label.ts | 5 -- .../src/signals/test-evidence.ts | 64 +------------------ .../src/analyzers/dependency-scan.ts | 13 ---- review-enrichment/src/analyzers/registry.ts | 3 - review-enrichment/test/enrichment.test.ts | 37 ----------- src/db/repositories.ts | 13 ---- src/review/content-lane/registry-logic.ts | 9 --- src/review/repo-doc-render.ts | 4 -- src/selfhost/cf-workers-shim.ts | 7 -- src/selfhost/queue-common.ts | 4 -- src/services/linked-issue-satisfaction-run.ts | 2 - src/settings/pr-type-label.ts | 5 -- test/stubs/cloudflare-workers.ts | 5 ++ test/unit/test-evidence.test.ts | 61 +----------------- 14 files changed, 9 insertions(+), 223 deletions(-) diff --git a/packages/gittensory-engine/src/settings/pr-type-label.ts b/packages/gittensory-engine/src/settings/pr-type-label.ts index 3a80c7fe13..bbcc5ffdc1 100644 --- a/packages/gittensory-engine/src/settings/pr-type-label.ts +++ b/packages/gittensory-engine/src/settings/pr-type-label.ts @@ -28,11 +28,6 @@ export const DEFAULT_TYPE_LABELS: PrTypeLabelSet = { priority: "gittensor:priority", }; -/** Every label name in the built-in default set, generic over however many categories - * `DEFAULT_TYPE_LABELS` carries (#label-modularity) -- never hardcode `.bug`/`.feature`/`.priority` - * property access here, a configured set can carry more or fewer categories than the default. */ -export const ALL_TYPE_LABELS: readonly string[] = Object.values(DEFAULT_TYPE_LABELS); - export const MAX_TYPE_LABEL_CATEGORIES = 32; export const MAX_TYPE_LABEL_NAME_LENGTH = 50; diff --git a/packages/gittensory-engine/src/signals/test-evidence.ts b/packages/gittensory-engine/src/signals/test-evidence.ts index 2cc9d498df..1e504f6f71 100644 --- a/packages/gittensory-engine/src/signals/test-evidence.ts +++ b/packages/gittensory-engine/src/signals/test-evidence.ts @@ -120,66 +120,8 @@ export function classifyTestCoverage(changedPaths: string[]): TestCoverageClassi return "weak"; } -// #2187 (foundational slice of #1972 — boundary-safe test generation): a small, precise framework list, each -// tied to an unambiguous marker file/pattern and an existing isTestPath naming family. Deliberately narrow — -// a longer list of guessable-but-ambiguous frameworks would make detectTestConvention's output less trustworthy -// as a test-gen input than returning null (see the "unknown => null" fail-safe below). +// #2187 (foundational slice of #1972 — boundary-safe test generation): a small, precise framework list. +// Consumed by the MCP test-gen tool's enum (src/mcp/server.ts testGenShape) so a caller cannot request a +// spec for a framework this engine doesn't recognize. export const TEST_FRAMEWORKS = ["vitest", "jest", "pytest", "go-test", "rspec", "cargo-test"] as const; export type TestFramework = (typeof TEST_FRAMEWORKS)[number]; - -/** Deterministic detection result: which framework, where tests live, and the file-naming convention to - * follow when scaffolding a new one. `testDir` is `null` for a co-located convention (e.g. Go/Rust/Dart's - * `_test`/`#[cfg(test)]` siblings), matching how those ecosystems actually lay out tests. */ -export type TestConvention = { - framework: TestFramework; - testDir: string | null; - namingPattern: string; -}; - -// One marker file per framework, checked against the basename of each changed/known path. Ordered by -// specificity where two frameworks could share an ecosystem (vitest before jest: a repo migrating from Jest to -// Vitest keeps `jest.config.js` around far more often than the reverse, so vitest's own config marker — when -// present — must win). -const FRAMEWORK_MARKERS: ReadonlyArray<{ framework: TestFramework; pattern: RegExp; testDir: string | null; namingPattern: string }> = [ - { framework: "vitest", pattern: /(^|\/)vitest\.config\.(ts|mts|cts|js|mjs|cjs)$/i, testDir: "test/", namingPattern: "*.test.ts" }, - { framework: "vitest", pattern: /(^|\/)vitest\.workspace\.(ts|mts|cts|js|mjs|cjs)$/i, testDir: "test/", namingPattern: "*.test.ts" }, - { framework: "jest", pattern: /(^|\/)jest\.config\.(ts|js|mjs|cjs|json)$/i, testDir: "__tests__/", namingPattern: "*.test.js" }, - { framework: "pytest", pattern: /(^|\/)pytest\.ini$/i, testDir: null, namingPattern: "test_*.py" }, - { framework: "pytest", pattern: /(^|\/)pyproject\.toml$/i, testDir: null, namingPattern: "test_*.py" }, - { framework: "go-test", pattern: /(^|\/)go\.mod$/i, testDir: null, namingPattern: "*_test.go" }, - { framework: "rspec", pattern: /(^|\/)\.rspec$/i, testDir: "spec/", namingPattern: "*_spec.rb" }, - { framework: "cargo-test", pattern: /(^|\/)Cargo\.toml$/i, testDir: null, namingPattern: "#[cfg(test)] mod tests" }, -]; - -// Fallback inference from an EXISTING test file's own naming, when no marker is present (e.g. a marker file -// wasn't part of the changed/known set passed in, but the repo already has real test files to imitate). -const CONVENTION_FROM_EXISTING_TEST: ReadonlyArray<{ framework: TestFramework; pattern: RegExp; testDir: string | null; namingPattern: string }> = [ - { framework: "vitest", pattern: /\.(test|spec)\.(ts|tsx|mts|cts)$/i, testDir: "test/", namingPattern: "*.test.ts" }, - { framework: "jest", pattern: /\.(test|spec)\.(js|jsx|mjs|cjs)$/i, testDir: "__tests__/", namingPattern: "*.test.js" }, - { framework: "pytest", pattern: /(^|\/)test_[^/]*\.py$|[^/]+_test\.py$/i, testDir: null, namingPattern: "test_*.py" }, - { framework: "go-test", pattern: /[^/]+_test\.go$/i, testDir: null, namingPattern: "*_test.go" }, - { framework: "rspec", pattern: /[^/]+_spec\.rb$/i, testDir: "spec/", namingPattern: "*_spec.rb" }, -]; - -/** - * Detect a repo's test framework + convention from a bounded set of changed paths and known marker filenames - * (e.g. `package.json`, `pyproject.toml`, `go.mod` — paths the caller already has, never fetched by this - * function). Deterministic and pure: markers win over inferring from existing test-file naming (a config file - * is a stronger, unambiguous signal than a naming guess), and the marker list is checked in a fixed order so a - * repo with more than one marker present always resolves to the same framework. Returns `null` when nothing in - * `paths`/`markers` matches any known convention — an unrecognized layout is left alone (fail-safe) rather than - * guessing, since a wrong framework guess would make the downstream test-gen spec actively misleading. - */ -export function detectTestConvention(paths: string[], markers: string[]): TestConvention | null { - for (const marker of FRAMEWORK_MARKERS) { - if (markers.some((path) => marker.pattern.test(path)) || paths.some((path) => marker.pattern.test(path))) { - return { framework: marker.framework, testDir: marker.testDir, namingPattern: marker.namingPattern }; - } - } - for (const convention of CONVENTION_FROM_EXISTING_TEST) { - if (paths.some((path) => isTestPath(path) && convention.pattern.test(path))) { - return { framework: convention.framework, testDir: convention.testDir, namingPattern: convention.namingPattern }; - } - } - return null; -} diff --git a/review-enrichment/src/analyzers/dependency-scan.ts b/review-enrichment/src/analyzers/dependency-scan.ts index 0d43eee901..5f99fc13be 100644 --- a/review-enrichment/src/analyzers/dependency-scan.ts +++ b/review-enrichment/src/analyzers/dependency-scan.ts @@ -432,16 +432,3 @@ export async function scanDependencyChanges( } return findings; } - -/** Analyzer entrypoint: changed deps → OSV → only the deps that carry vulnerabilities. */ -export async function scanDependencies( - req: EnrichRequest, - fetchImpl: typeof fetch = fetch, - options: ScanOptions = {}, -): Promise { - return scanDependencyChanges( - extractDependencyChanges(req.files ?? [], options.limits), - fetchImpl, - options, - ); -} diff --git a/review-enrichment/src/analyzers/registry.ts b/review-enrichment/src/analyzers/registry.ts index 7edb5f46c9..b70f10e9ac 100644 --- a/review-enrichment/src/analyzers/registry.ts +++ b/review-enrichment/src/analyzers/registry.ts @@ -57,7 +57,6 @@ import type { AnalyzerDescriptor, AnalyzerFn, AnalyzerName, - AnalyzerRegistry, AnyAnalyzerDescriptor, } from "./types.js"; import { DEFAULT_MAX_FINDINGS, DEFAULT_MAX_LINE_CHARS } from "./limits.js"; @@ -1632,8 +1631,6 @@ export const ANALYZERS = Object.fromEntries( ANALYZER_DESCRIPTORS.map((analyzer) => [analyzer.name, analyzer.run]), ) as Record; -export const ANALYZER_REGISTRY: AnalyzerRegistry = ANALYZERS; - export const ANALYZER_DESCRIPTORS_BY_NAME = Object.fromEntries( ANALYZER_DESCRIPTORS.map((analyzer) => [analyzer.name, analyzer]), ) as Partial>; diff --git a/review-enrichment/test/enrichment.test.ts b/review-enrichment/test/enrichment.test.ts index 1df8b14105..18215a96b3 100644 --- a/review-enrichment/test/enrichment.test.ts +++ b/review-enrichment/test/enrichment.test.ts @@ -3,7 +3,6 @@ import assert from "node:assert/strict"; import { extractDependencyChanges, queryOsv, - scanDependencies, } from "../dist/analyzers/dependency-scan.js"; import { extractLockfileChanges, @@ -193,20 +192,6 @@ test("queryOsv: CVSS numeric score bucketed when no database_specific", async () assert.equal(cves[0].severity, "critical"); }); -test("scanDependencies: only deps with vulns are returned", async () => { - const findings = await scanDependencies( - { - repoFullName: "o/r", - prNumber: 1, - files: [{ path: "package.json", patch: '+ "lodash": "4.17.20",' }], - }, - okBatchFetch([{ id: "GHSA-x", database_specific: { severity: "CRITICAL" } }]), - ); - assert.equal(findings.length, 1); - assert.equal(findings[0].direction, "add"); - assert.equal(findings[0].cves[0].severity, "critical"); -}); - test("extractLockfileChanges: package-lock version drift with file line, skipping direct manifest deps", () => { const changes = extractLockfileChanges([ { @@ -1668,28 +1653,6 @@ test("extractDependencyChanges: caps manifest files and patch lines", () => { ); }); -test("scanDependencies: caps OSV queries and forwards abort signals", async () => { - const seenSignals = []; - const files = Array.from({ length: 3 }, (_, index) => ({ - path: "package.json", - patch: `+ "pkg-${index}": "1.0.0",`, - })); - - const controller = new AbortController(); - const findings = await scanDependencies( - { repoFullName: "o/r", prNumber: 1, files }, - async (_url, init) => { - seenSignals.push(init.signal); - return { ok: true, json: async () => ({ results: [{ vulns: [] }, { vulns: [] }] }) }; - }, - { signal: controller.signal, limits: { maxDependencyQueries: 2 } }, - ); - - assert.equal(findings.length, 0); - assert.equal(seenSignals.length, 1); - assert.ok(seenSignals.every((signal) => signal instanceof AbortSignal)); -}); - test("buildBrief: timeout aborts dependency scan so OSV work stops", async () => { const realFetch = globalThis.fetch; const signals = []; diff --git a/src/db/repositories.ts b/src/db/repositories.ts index 842b94203b..c803ae5200 100644 --- a/src/db/repositories.ts +++ b/src/db/repositories.ts @@ -4070,19 +4070,6 @@ export async function listOpenItemsForAuthorAcrossInstall(env: Env, installation ]; } -// Anti-farming (#anti-gaming-flood): how many PRs this author has SUBMITTED to this repo since `sinceIso` (ANY -// state — open/merged/closed), so a flood that merges fast is still caught. createdAt is the row-insert time -// (≈ when gittensory first saw the PR), a good proxy for submission time on live webhook-driven PRs. -export async function countRecentSubmissionsByAuthor(env: Env, fullName: string, authorLogin: string, sinceIso: string): Promise { - const db = getDb(env.DB); - const [row] = await db - .select({ count: sql`count(*)` }) - .from(pullRequests) - .where(and(eq(pullRequests.repoFullName, fullName), eq(pullRequests.authorLogin, authorLogin), gte(pullRequests.createdAt, sinceIso))); - /* v8 ignore next -- SQL aggregate count always returns one row; fallback protects D1 driver anomalies. */ - return Number(row?.count ?? 0); -} - export async function markUnseenOpenPullRequestsClosed(env: Env, fullName: string, seenOpenAt: string): Promise { const db = getDb(env.DB); const result = await db diff --git a/src/review/content-lane/registry-logic.ts b/src/review/content-lane/registry-logic.ts index 81032e55f8..cc02cdc521 100644 --- a/src/review/content-lane/registry-logic.ts +++ b/src/review/content-lane/registry-logic.ts @@ -31,13 +31,6 @@ export function isInternalAutomationBranch(ref: string | undefined): boolean { export const ARTIFACT_PATTERN = /^public\/metagraph\/[a-z0-9/_-]+\.json$/i; export const DEFAULT_PUBLIC_API_BASE = "https://api.metagraph.sh/api/v1"; -export const ISSUE_SUBMISSION_LABELS = new Set([ - "interface-submission", - "endpoint-submission", - "provider-submission", - "status-report", -]); - const REVIEWER_CLOSE_REASONS = new Set([ "malformed-json", "unsafe-url", @@ -81,8 +74,6 @@ const REVIEWER_SAFE_KINDS = new Set([ "docs", "data-artifact", ]); -export const AI_REVIEW_VERDICTS = new Set(["merged", "closed", "manual-review"]); - /** Live verdict vocabulary → core verdict. */ export type MetaVerdict = "merged" | "closed" | "manual-review"; export function toCoreVerdict(v: MetaVerdict): Verdict { diff --git a/src/review/repo-doc-render.ts b/src/review/repo-doc-render.ts index e47d368e26..f0e13f862d 100644 --- a/src/review/repo-doc-render.ts +++ b/src/review/repo-doc-render.ts @@ -11,10 +11,6 @@ import type { RepoProfile, RepoProfileCommands, RepoProfileFileNamingStyle, RepoProfileTestFileConvention } from "./repo-profile"; import type { GeneratedDocMarkers } from "./generated-doc-refresh"; -/** Bumped whenever the RENDERED CONTENT's structure changes in a way #3004's diff-aware refresh needs to know - * about (new section, reordered section, changed marker) -- not on copy-only wording tweaks. */ -export const REPO_DOC_TEMPLATE_VERSION = 2; - /** HTML-comment marker pair bracketing the machine-generated section of every AGENTS.md this engine writes. * Content outside this pair (added by a maintainer before the start marker or after the end marker) is treated * as permanently manual and is never touched by a refresh (#3004) -- see generated-doc-refresh.ts. */ diff --git a/src/selfhost/cf-workers-shim.ts b/src/selfhost/cf-workers-shim.ts index b59af6f08a..4307219150 100644 --- a/src/selfhost/cf-workers-shim.ts +++ b/src/selfhost/cf-workers-shim.ts @@ -9,10 +9,3 @@ export class DurableObject { protected env?: E, ) {} } -export class WorkerEntrypoint { - constructor( - protected ctx?: unknown, - protected env?: E, - ) {} -} -export class RpcTarget {} diff --git a/src/selfhost/queue-common.ts b/src/selfhost/queue-common.ts index 40b7fcb200..f6c6a24959 100644 --- a/src/selfhost/queue-common.ts +++ b/src/selfhost/queue-common.ts @@ -598,10 +598,6 @@ export function githubWebhookRateLimitDelayMs( return githubObservedRateLimitDelayMs(observation, LOW_REST_RATE_LIMIT_REMAINING, nowMs); } -export function githubRateLimitAdmissionRemainingFloor(kind: "background" | "webhook"): number { - return kind === "webhook" ? LOW_REST_RATE_LIMIT_REMAINING : MAINTENANCE_RESERVED_HEADROOM; -} - function githubWebhookPriority(payload: string): number { try { const message = JSON.parse(payload) as { diff --git a/src/services/linked-issue-satisfaction-run.ts b/src/services/linked-issue-satisfaction-run.ts index 62879f3270..f73a267bdc 100644 --- a/src/services/linked-issue-satisfaction-run.ts +++ b/src/services/linked-issue-satisfaction-run.ts @@ -177,5 +177,3 @@ async function record( metadata: { repoFullName: input.repoFullName, pullNumber: input.prNumber, ...(metadata ?? {}) }, }); } - -export const __linkedIssueSatisfactionRunInternals = { runWorkersSatisfactionOpinion }; diff --git a/src/settings/pr-type-label.ts b/src/settings/pr-type-label.ts index 43a0b015ee..454ca208ec 100644 --- a/src/settings/pr-type-label.ts +++ b/src/settings/pr-type-label.ts @@ -28,11 +28,6 @@ export const DEFAULT_TYPE_LABELS: PrTypeLabelSet = { priority: "gittensor:priority", }; -/** Every label name in the built-in default set, generic over however many categories - * `DEFAULT_TYPE_LABELS` carries (#label-modularity) -- never hardcode `.bug`/`.feature`/`.priority` - * property access here, a configured set can carry more or fewer categories than the default. */ -export const ALL_TYPE_LABELS: readonly string[] = Object.values(DEFAULT_TYPE_LABELS); - export const MAX_TYPE_LABEL_CATEGORIES = 32; export const MAX_TYPE_LABEL_NAME_LENGTH = 50; diff --git a/test/stubs/cloudflare-workers.ts b/test/stubs/cloudflare-workers.ts index 5eaf552b32..e7aa48ac2e 100644 --- a/test/stubs/cloudflare-workers.ts +++ b/test/stubs/cloudflare-workers.ts @@ -18,6 +18,11 @@ export class WorkflowEntrypoint { } } +// NOT dead despite having no first-party caller: vitest.config.ts aliases the `cloudflare:workers` +// specifier to this file for EVERY module resolved under Node, including node_modules -- the `agents` +// package (a dependency of src/mcp/server.ts's `agents/mcp` import) does `import { RpcTarget } from +// "cloudflare:workers"` and extends it, so removing this breaks that whole dependency graph at test +// runtime (confirmed by `npm run test:changed`, not by static grep -- there is no in-repo caller). export class RpcTarget {} export const exports = {}; diff --git a/test/unit/test-evidence.test.ts b/test/unit/test-evidence.test.ts index 83bf93ce89..4584371116 100644 --- a/test/unit/test-evidence.test.ts +++ b/test/unit/test-evidence.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import { classifyTestCoverage, detectTestConvention, hasLocalTestEvidence, hasValidationNote, isSourcePath, isTestPath } from "../../src/signals/test-evidence"; +import { classifyTestCoverage, hasLocalTestEvidence, hasValidationNote, isSourcePath, isTestPath } from "../../src/signals/test-evidence"; describe("test evidence helpers", () => { it("detects common test path conventions", () => { @@ -240,62 +240,3 @@ describe("classifyTestCoverage", () => { expect(classifyTestCoverage([...sources, "test/single.test.ts"])).toBe("weak"); }); }); - -// #2187 (foundational slice of #1972): a bounded, deterministic framework/convention detector feeding the -// boundary-safe test-gen action spec (#2188). -describe("detectTestConvention", () => { - it("detects vitest from its config marker, taking precedence over a stale jest config", () => { - expect(detectTestConvention([], ["vitest.config.ts"])).toEqual({ framework: "vitest", testDir: "test/", namingPattern: "*.test.ts" }); - expect(detectTestConvention([], ["vitest.workspace.mts"])).toEqual({ framework: "vitest", testDir: "test/", namingPattern: "*.test.ts" }); - // A repo migrating off Jest keeps the old config around; vitest's own marker must still win (checked first). - expect(detectTestConvention([], ["vitest.config.js", "jest.config.js"])).toEqual({ framework: "vitest", testDir: "test/", namingPattern: "*.test.ts" }); - }); - - it("detects jest from its config marker", () => { - expect(detectTestConvention([], ["jest.config.ts"])).toEqual({ framework: "jest", testDir: "__tests__/", namingPattern: "*.test.js" }); - expect(detectTestConvention([], ["jest.config.json"])).toEqual({ framework: "jest", testDir: "__tests__/", namingPattern: "*.test.js" }); - }); - - it("detects pytest from pytest.ini or pyproject.toml", () => { - expect(detectTestConvention([], ["pytest.ini"])).toEqual({ framework: "pytest", testDir: null, namingPattern: "test_*.py" }); - expect(detectTestConvention([], ["pyproject.toml"])).toEqual({ framework: "pytest", testDir: null, namingPattern: "test_*.py" }); - }); - - it("detects go test from go.mod", () => { - expect(detectTestConvention([], ["go.mod"])).toEqual({ framework: "go-test", testDir: null, namingPattern: "*_test.go" }); - }); - - it("detects rspec from .rspec", () => { - expect(detectTestConvention([], [".rspec"])).toEqual({ framework: "rspec", testDir: "spec/", namingPattern: "*_spec.rb" }); - }); - - it("detects cargo test from Cargo.toml", () => { - expect(detectTestConvention([], ["Cargo.toml"])).toEqual({ framework: "cargo-test", testDir: null, namingPattern: "#[cfg(test)] mod tests" }); - }); - - it("matches a marker found in the changed paths, not only the markers list", () => { - expect(detectTestConvention(["backend/go.mod", "backend/main.go"], [])).toEqual({ framework: "go-test", testDir: null, namingPattern: "*_test.go" }); - }); - - it("falls back to inferring from an existing test file's naming when no marker is present", () => { - expect(detectTestConvention(["test/unit/widget.test.ts"], [])).toEqual({ framework: "vitest", testDir: "test/", namingPattern: "*.test.ts" }); - expect(detectTestConvention(["__tests__/widget.test.js"], [])).toEqual({ framework: "jest", testDir: "__tests__/", namingPattern: "*.test.js" }); - expect(detectTestConvention(["mypackage/test_utils.py"], [])).toEqual({ framework: "pytest", testDir: null, namingPattern: "test_*.py" }); - expect(detectTestConvention(["pkg/foo_test.go"], [])).toEqual({ framework: "go-test", testDir: null, namingPattern: "*_test.go" }); - expect(detectTestConvention(["spec/models/widget_spec.rb"], [])).toEqual({ framework: "rspec", testDir: "spec/", namingPattern: "*_spec.rb" }); - }); - - it("prefers a marker over an existing test file's naming when both are present", () => { - // go.mod marker present alongside a Ruby-looking spec path (an unusual but possible polyglot repo) — the - // marker is checked first and wins deterministically. - expect(detectTestConvention(["spec/widget_spec.rb"], ["go.mod"])).toEqual({ framework: "go-test", testDir: null, namingPattern: "*_test.go" }); - }); - - it("returns null for an unknown/empty layout (fail-safe)", () => { - expect(detectTestConvention([], [])).toBeNull(); - expect(detectTestConvention(["src/widget.rs"], ["Makefile"])).toBeNull(); - // A path that merely LOOKS like a test file per isTestPath's directory rule, but whose extension isn't in - // any known convention pattern (e.g. a bare snapshot), does not resolve to a framework. - expect(detectTestConvention(["components/__snapshots__/Card.tsx.snap"], [])).toBeNull(); - }); -});