fix(skills): address QA round 23

QA23-1: Resolve custom Gradle version-catalog accessor names by
matching settings.gradle(.kts) create(name) declarations to catalog
filenames by basename, so ./gradle/foo.versions.toml and similar valid
path spellings are recognized.

QA23-2: Exclude Poetry group dependencies from FastAPI framework
classification to avoid dev/test-only FastAPI deps implying application
usage.

Add regression tests for:
- settings-defined catalog accessors that differ from TOML basename
- Poetry group FastAPI false positives
This commit is contained in:
Derek Pearson 2026-03-22 09:07:32 -04:00
parent ea765166b1
commit 6cfd1c385c
2 changed files with 44 additions and 5 deletions

View file

@ -1026,8 +1026,7 @@ function extractPyprojectDependencySections(content: string): string {
if (
section === "project.optional-dependencies" ||
section === "tool.poetry.dependencies" ||
/^tool\.poetry\.group\.[^.]+\.dependencies$/.test(section)
section === "tool.poetry.dependencies"
) {
if (section === "project.optional-dependencies") {
const equalsIndex = line.indexOf("=");
@ -1078,12 +1077,15 @@ function resolveVersionCatalogAccessors(
try {
const raw = readBounded(join(basePath, settingsFile), 64 * 1024);
const content = stripDependencyComments(settingsFile, raw);
const createRe = /create\(\s*["']([A-Za-z0-9_]+)["']\s*\)\s*\{[\s\S]*?from\(files\(\s*["']([^"']+\.versions\.toml)["']\s*\)\s*\)/g;
const createRe = /create\(\s*["']([A-Za-z0-9_]+)["']\s*\)\s*\{[\s\S]*?([A-Za-z0-9_.-]+\.versions\.toml)["']?\s*\)\s*\)/g;
let match: RegExpExecArray | null;
while ((match = createRe.exec(content)) !== null) {
const accessor = match[1].toLowerCase();
const catalogPath = match[2].replaceAll("\\", "/");
if (versionCatalogFiles.some((file) => file.replaceAll("\\", "/").endsWith(catalogPath))) {
const catalogBasename = match[2].replaceAll("\\", "/").split("/").pop()!;
if (versionCatalogFiles.some((file) => {
const normalized = file.replaceAll("\\", "/");
return normalized === catalogBasename || normalized.endsWith(`/${catalogBasename}`);
})) {
accessors.add(accessor);
}
}

View file

@ -823,6 +823,21 @@ test("detectProjectSignals: pyproject dependency table extras do not trigger dep
}
});
test("detectProjectSignals: Poetry group FastAPI dependency does not imply app framework usage", () => {
const dir = makeTempDir("signals-fastapi-poetry-group");
try {
writeFileSync(
join(dir, "pyproject.toml"),
'[tool.poetry.dependencies]\npython = "^3.12"\nflask = "^3.0"\n\n[tool.poetry.group.dev.dependencies]\nfastapi = "^0.115"\n',
"utf-8",
);
const signals = detectProjectSignals(dir);
assert.ok(!signals.detectedFiles.includes("dep:fastapi"), "Poetry dev-group dependencies should not imply FastAPI app usage");
} finally {
cleanup(dir);
}
});
test("detectProjectSignals: pyproject optional-dependency group name does not trigger dep:fastapi", () => {
const dir = makeTempDir("signals-fastapi-pyproject-extra-name");
try {
@ -1197,3 +1212,25 @@ test("detectProjectSignals: Spring Boot custom version-catalog accessor emits de
cleanup(dir);
}
});
test("detectProjectSignals: Spring Boot settings-defined catalog accessor emits dep:spring-boot", () => {
const dir = makeTempDir("signals-spring-version-catalog-settings-accessor");
try {
mkdirSync(join(dir, "gradle"), { recursive: true });
writeFileSync(
join(dir, "settings.gradle.kts"),
'dependencyResolutionManagement { versionCatalogs { create("backendLibs") { from(files("./gradle/backend.versions.toml")) } } }',
"utf-8",
);
writeFileSync(join(dir, "build.gradle.kts"), "plugins { alias(backendLibs.plugins.web) }", "utf-8");
writeFileSync(
join(dir, "gradle", "backend.versions.toml"),
"[plugins]\nweb = { id = 'org.springframework.boot', version = '3.2.0' }\n",
"utf-8",
);
const signals = detectProjectSignals(dir);
assert.ok(signals.detectedFiles.includes("dep:spring-boot"), "settings-defined catalog accessors should trigger Spring Boot detection");
} finally {
cleanup(dir);
}
});