From 78b880919829e2ef7585b9aadbd81fbfa4f33600 Mon Sep 17 00:00:00 2001 From: glorydavid03023 Date: Wed, 1 Jul 2026 04:47:37 -0500 Subject: [PATCH] fix(mcp): count cypress/e2e and snapshot paths as test files The MCP client copy of isTestFile is a hand-copied duplicate of the canonical isTestPath in src/signals/test-evidence.ts, but it dropped the last two branches: `*.cy.*` / `*.e2e.*` (Cypress/Playwright e2e) and `__snapshots__/` (Jest/Vitest snapshots). The server-side analysis delegates to isTestPath, so the client and server classify the same diff and must agree. Because isCodeFile is `codeExt && !isTestFile`, a changed `Button.cy.ts` or `__snapshots__/x.snap.ts` was misclassified as source: dropped from collectLocalDiff().testFiles, counted in codeFiles, inflating the source-line/token estimate and under-reporting test evidence in the local packet. Add the two missing branches so the client mirrors isTestPath, export isTestFile/isCodeFile, and add a regression test. No issue because issue creation is restricted on this repo; this is a small, self-evident consistency fix in a pure helper with no schema or API change. --- packages/gittensory-mcp/lib/local-branch.js | 13 ++++++++++--- test/unit/local-branch.test.ts | 18 ++++++++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/packages/gittensory-mcp/lib/local-branch.js b/packages/gittensory-mcp/lib/local-branch.js index 95bfb25e1e..8c356ed8d8 100644 --- a/packages/gittensory-mcp/lib/local-branch.js +++ b/packages/gittensory-mcp/lib/local-branch.js @@ -587,17 +587,24 @@ function firstCommitTitle(messages) { return messages.find((message) => message.trim().length > 0)?.split("\n")[0]?.trim(); } -function isTestFile(file) { +// Must mirror the canonical server-side matcher in src/signals/test-evidence.ts (isTestPath); the +// server local-branch analysis delegates to it, so this client copy classifies the same diff and must +// agree. The last two branches (Cypress/e2e `*.cy.*`/`*.e2e.*` and `__snapshots__/`) were missing here, +// so a changed `Button.cy.ts` / snapshot file was misclassified as source (isCodeFile) and dropped from +// testFiles, inflating source-line/token counts and under-reporting test evidence in the local packet. +export function isTestFile(file) { return ( /(^|\/)(test|tests|spec|__tests__)\//i.test(file) || /(^|\/)src\/test\//i.test(file) || /(^|\/)[^/]+_test\.(go|py|rb)$/i.test(file) || /(^|\/)[^/]+_spec\.rb$/i.test(file) || - /\.(test|spec)\.(ts|tsx|js|jsx|py|rb|rs)$/i.test(file) + /\.(test|spec)\.(ts|tsx|js|jsx|py|rb|rs)$/i.test(file) || + /(^|\/)[^/]+\.(cy|e2e)\.(ts|tsx|js|jsx)$/i.test(file) || + /(^|\/)__snapshots__\//i.test(file) ); } -function isCodeFile(file) { +export function isCodeFile(file) { return /\.(ts|tsx|js|jsx|py|rb|rs|kt|scala|java|go|sql)$/i.test(file) && !isTestFile(file); } diff --git a/test/unit/local-branch.test.ts b/test/unit/local-branch.test.ts index 16ad62dff5..f11a5b53c4 100644 --- a/test/unit/local-branch.test.ts +++ b/test/unit/local-branch.test.ts @@ -1737,6 +1737,24 @@ describe("local MCP git metadata collection", () => { ); }); + it("classifies Cypress/e2e and snapshot paths as test files, mirroring the server isTestPath", async () => { + // @ts-expect-error package helper is plain JS because the local wrapper ships as a Node bin package. + const { isTestFile, isCodeFile } = await import("../../packages/gittensory-mcp/lib/local-branch.js"); + // Existing forms still classify as tests. + for (const file of ["test/foo.ts", "src/app.test.ts", "pkg/foo_test.go", "spec/foo_spec.rb", "src/__tests__/x.ts"]) { + expect(isTestFile(file)).toBe(true); + } + // Regression: Cypress/e2e and snapshot files must count as tests; before this they fell through to + // isCodeFile and were wrongly counted as source in the local packet. + for (const file of ["components/Button.cy.ts", "e2e/login.e2e.tsx", "src/__snapshots__/Button.snap.ts"]) { + expect(isTestFile(file)).toBe(true); + expect(isCodeFile(file)).toBe(false); + } + // Plain source stays source. + expect(isTestFile("src/app.ts")).toBe(false); + expect(isCodeFile("src/app.ts")).toBe(true); + }); + it("extracts linked issues only from standalone closing keywords, not keyword substrings", async () => { // @ts-expect-error package helper is plain JS because the local wrapper ships as a Node bin package. const { extractLinkedIssues } = await import("../../packages/gittensory-mcp/lib/local-branch.js");