Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions packages/gittensory-mcp/lib/local-branch.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
18 changes: 18 additions & 0 deletions test/unit/local-branch.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down