From c710ae3514a2df94b5e0932607176fa628c517f6 Mon Sep 17 00:00:00 2001 From: glorydavid03023 Date: Fri, 3 Jul 2026 20:32:44 +0900 Subject: [PATCH] fix(mcp): re-sync isTestFile with the server isTestPath (pytest prefix + JVM/C#/Swift) The MCP local-branch isTestFile is a standalone inlined copy of the server's canonical isTestPath (packages/gittensory-mcp ships as a Node bin package, so it cannot import from src/). Two conventions added to isTestPath were never mirrored here: the pytest `test_*.py` prefix (#2666) and the JVM/C#/Swift `SomethingTest(s)`/`Spec` class-suffix (#2743). So the local pre-submit predictor counted Java/Kotlin/Scala/C#/Swift test classes and pytest-prefixed files as SOURCE, disagreeing with the server gate's own test-coverage classification and misleading contributors in those ecosystems about readiness. Add both rules verbatim from isTestPath (same order, same case-sensitive JVM suffix so `Latest.java`/`Contest.cs`/`manifest.scala` stay source). Adds regressions to the MCP-copy classifier test. --- packages/gittensory-mcp/lib/local-branch.js | 5 +++++ test/unit/local-branch.test.ts | 12 ++++++++++++ 2 files changed, 17 insertions(+) diff --git a/packages/gittensory-mcp/lib/local-branch.js b/packages/gittensory-mcp/lib/local-branch.js index 949e07a181..5a4a034145 100644 --- a/packages/gittensory-mcp/lib/local-branch.js +++ b/packages/gittensory-mcp/lib/local-branch.js @@ -597,9 +597,14 @@ export function isTestFile(file) { /(^|\/)(test|tests|spec|__tests__)\//i.test(file) || /(^|\/)src\/test\//i.test(file) || /(^|\/)[^/]+_test\.(go|py|rb)$/i.test(file) || + /(^|\/)test_[^/]*\.py$/i.test(file) || // pytest's default `test_*.py` prefix convention (the suffix rule above only catches `*_test.py`) /(^|\/)[^/]+_spec\.rb$/i.test(file) || /\.(test|spec)\.(ts|tsx|mts|cts|js|jsx|mjs|cjs|py|rb|rs)$/i.test(file) || /(^|\/)[^/]+\.(cy|e2e)\.(ts|tsx|mts|cts|js|jsx|mjs|cjs)$/i.test(file) || + // JVM / C# / Swift `SomethingTest(s)`/`SomethingSpec` class-suffix convention (JUnit, Kotlin/ScalaTest, + // Spock, xUnit/NUnit, XCTest). Case-sensitive on the PascalCase suffix so it can't false-positive on words + // that merely end in "test"/"spec" (Latest.java, Contest.cs, manifest.scala). + /(^|\/)\w*(Tests?|Spec)\.(java|kt|kts|scala|cs|swift|groovy)$/.test(file) || /(^|\/)__snapshots__\//i.test(file) ); } diff --git a/test/unit/local-branch.test.ts b/test/unit/local-branch.test.ts index 5f62a051b8..e538e334ac 100644 --- a/test/unit/local-branch.test.ts +++ b/test/unit/local-branch.test.ts @@ -1762,6 +1762,18 @@ describe("local MCP git metadata collection", () => { expect(isTestFile(file)).toBe(true); expect(isCodeFile(file)).toBe(false); } + // #2666 + #2743 parity: the pytest `test_*.py` prefix and the JVM/C#/Swift `SomethingTest(s)`/`Spec` + // class-suffix conventions were added to the server isTestPath but not this MCP copy — so the local + // predictor wrongly counted Java/Kotlin/Scala/C#/Swift tests and pytest-prefixed files as SOURCE. + for (const file of ["tests/test_utils.py", "test_api.py", "app/FooTests.java", "src/BarSpec.kt", "core/BazTest.scala", "svc/QuuxTests.cs", "ios/CorgeSpec.swift", "build/GraultTest.groovy"]) { + expect(isTestFile(file)).toBe(true); + expect(isCodeFile(file)).toBe(false); + } + // Case-sensitive on the PascalCase suffix: a JVM source merely ENDING in "test"/"spec" stays source. + for (const file of ["src/Latest.java", "core/manifest.scala", "app/MyService.kt"]) { + expect(isTestFile(file)).toBe(false); + expect(isCodeFile(file)).toBe(true); + } }); it("extracts linked issues only from standalone closing keywords, not keyword substrings", async () => {