diff --git a/scripts/lib/project-detect.js b/scripts/lib/project-detect.js index 9f1566ada..8535946f2 100644 --- a/scripts/lib/project-detect.js +++ b/scripts/lib/project-detect.js @@ -198,10 +198,13 @@ function getPythonDeps(projectDir) { const trimmed = line.trim(); if (trimmed && !trimmed.startsWith('#') && !trimmed.startsWith('-')) { const name = trimmed - .split(/[>== { const name = m .replace(/"/g, '') - .split(/[>== { + const dir = createTempDir(); + try { + writeTestFile(dir, 'requirements.txt', 'fastapi~=0.110\nflask~=3.0\nuvicorn'); + const result = detectProjectType(dir); + assert.ok(result.frameworks.includes('fastapi'), `fastapi not detected: ${JSON.stringify(result.frameworks)}`); + assert.ok(result.frameworks.includes('flask'), `flask not detected: ${JSON.stringify(result.frameworks)}`); + } finally { + cleanupDir(dir); + } + })) passed++; else failed++; + + if (test('detects fastapi from ~= pin in pyproject.toml dependencies', () => { + const dir = createTempDir(); + try { + writeTestFile(dir, 'pyproject.toml', '[project]\nname = "test"\ndependencies = [\n "fastapi~=0.110",\n "uvicorn"\n]'); + const result = detectProjectType(dir); + assert.ok(result.frameworks.includes('fastapi'), `fastapi not detected: ${JSON.stringify(result.frameworks)}`); + } finally { + cleanupDir(dir); + } + })) passed++; else failed++; + // TypeScript/JavaScript detection console.log('\nTypeScript/JavaScript Detection:'); @@ -388,6 +411,35 @@ function runTests() { } })) passed++; else failed++; + if (test('getPythonDeps strips ~= compatible-release pins', () => { + const dir = createTempDir(); + try { + writeTestFile(dir, 'requirements.txt', 'fastapi~=0.110\nflask~=3.0'); + const deps = getPythonDeps(dir); + assert.ok(deps.includes('fastapi'), `fastapi missing from deps: ${JSON.stringify(deps)}`); + assert.ok(deps.includes('flask'), `flask missing from deps: ${JSON.stringify(deps)}`); + assert.ok(!deps.includes('flask~')); + } finally { + cleanupDir(dir); + } + })) passed++; else failed++; + + if (test('getPythonDeps strips @ direct-reference URLs and git+ VCS forms', () => { + const dir = createTempDir(); + try { + writeTestFile(dir, 'requirements.txt', 'pkg @ git+https://github.com/user/repo.git\nother @ https://example.com/pkg.tar.gz\ngit-dep @ git+https://github.com/user/dep.git@v2.0\ngit+https://github.com/user/repo.git#egg=pkg'); + const deps = getPythonDeps(dir); + assert.ok(deps.includes('pkg'), `pkg missing from deps: ${JSON.stringify(deps)}`); + assert.ok(deps.includes('other'), `other missing from deps: ${JSON.stringify(deps)}`); + assert.ok(deps.includes('git-dep'), `git-dep missing from deps: ${JSON.stringify(deps)}`); + assert.ok(!deps.some(d => d.includes('git+')), `VCS URL leaked into deps: ${JSON.stringify(deps)}`); + assert.ok(!deps.some(d => d.includes('://')), `URL leaked into deps: ${JSON.stringify(deps)}`); + assert.ok(!deps.some(d => d.includes('@')), `@ delimiter leaked into deps: ${JSON.stringify(deps)}`); + } finally { + cleanupDir(dir); + } + })) passed++; else failed++; + if (test('getGoDeps reads go.mod require block', () => { const dir = createTempDir(); try {