import * as fs from 'fs'; import * as path from 'path'; import * as os from 'os'; import { analyzeDependencies } from '../src/analyzers/dependencyAnalyzer'; describe('DependencyAnalyzer', () => { let tempDir: string; beforeEach(() => { tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ai-context-dep-test-')); }); afterEach(() => { fs.rmSync(tempDir, { recursive: true, force: true }); }); describe('Node.js dependencies', () => { it('should parse package.json dependencies', async () => { const packageJson = { name: 'test-project', version: '1.0.0', dependencies: { express: '^4.18.2', lodash: '^4.17.21' }, devDependencies: { jest: '^29.7.0', typescript: '^5.3.3' } }; fs.writeFileSync( path.join(tempDir, 'package.json'), JSON.stringify(packageJson) ); const result = await analyzeDependencies(tempDir, 'node'); expect(result.dependencies).toHaveLength(4); expect(result.dependencies.find(d => d.name === 'express')).toEqual({ name: 'express', version: '^4.18.2', type: 'prod' }); expect(result.dependencies.find(d => d.name === 'jest')).toEqual({ name: 'jest', version: '^29.7.0', type: 'dev' }); expect(result.packageManager).toBe('npm'); }); it('should detect yarn package manager', async () => { const packageJson = { name: 'test-project', dependencies: { express: '^4.18.0' } }; fs.writeFileSync( path.join(tempDir, 'package.json'), JSON.stringify(packageJson) ); fs.writeFileSync(path.join(tempDir, 'yarn.lock'), '# yarn lock file'); const result = await analyzeDependencies(tempDir, 'node'); expect(result.packageManager).toBe('yarn'); expect(result.lockFile).toBe('yarn.lock'); }); it('should detect pnpm package manager', async () => { const packageJson = { name: 'test-project', dependencies: { express: '^4.18.0' } }; fs.writeFileSync( path.join(tempDir, 'package.json'), JSON.stringify(packageJson) ); fs.writeFileSync(path.join(tempDir, 'pnpm-lock.yaml'), '# pnpm lock file'); const result = await analyzeDependencies(tempDir, 'node'); expect(result.packageManager).toBe('pnpm'); }); }); describe('Python dependencies', () => { it('should parse requirements.txt', async () => { fs.writeFileSync( path.join(tempDir, 'requirements.txt'), `flask>=2.0.0 requests==2.28.0 numpy~=1.24.0 pandas # pandas for data analysis django<4.0 black; python_version >= "3.6" ` ); const result = await analyzeDependencies(tempDir, 'python'); expect(result.dependencies.length).toBeGreaterThan(0); const flaskDep = result.dependencies.find(d => d.name === 'flask'); expect(flaskDep).toBeDefined(); expect(flaskDep?.type).toBe('prod'); expect(result.packageManager).toBe('pip'); }); it('should detect poetry package manager', async () => { fs.writeFileSync( path.join(tempDir, 'pyproject.toml'), `[tool.poetry] name = "test-project" version = "0.1.0" [tool.poetry.dependencies] python = "^3.8" fastapi = "^0.100.0" ` ); fs.writeFileSync(path.join(tempDir, 'poetry.lock'), '# poetry lock'); const result = await analyzeDependencies(tempDir, 'python'); expect(result.packageManager).toBe('poetry'); }); }); describe('Go dependencies', () => { it('should parse go.mod', async () => { const goMod = `module github.com/test/project go 1.20 require ( github.com/gin-gonic/gin v1.9.1 github.com/gofiber/fiber v2.52.0 ) `; fs.writeFileSync(path.join(tempDir, 'go.mod'), goMod); const result = await analyzeDependencies(tempDir, 'go'); expect(result.dependencies.length).toBe(2); expect(result.packageManager).toBe('go'); const ginDep = result.dependencies.find(d => d.name.includes('gin-gonic/gin')); expect(ginDep).toBeDefined(); }); }); describe('Rust dependencies', () => { it('should parse Cargo.toml', async () => { const cargoToml = `[package] name = "test" version = "0.1.0" edition = "2021" [dependencies] serde = "1.0" tokio = "1.0" [dev.dependencies] tempfile = "3.0" `; fs.writeFileSync(path.join(tempDir, 'Cargo.toml'), cargoToml); const result = await analyzeDependencies(tempDir, 'rust'); expect(result.dependencies.length).toBe(3); expect(result.packageManager).toBe('cargo'); const serdeDep = result.dependencies.find(d => d.name === 'serde'); expect(serdeDep).toBeDefined(); expect(serdeDep?.type).toBe('prod'); const tempfileDep = result.dependencies.find(d => d.name === 'tempfile'); expect(tempfileDep?.type).toBe('dev'); }); }); describe('Java dependencies', () => { it('should parse pom.xml', async () => { const pomXml = ` 4.0.0 com.test test 1.0.0 org.springframework.boot spring-boot-starter 3.1.0 com.fasterxml.jackson.core jackson-databind 2.15.0 `; fs.writeFileSync(path.join(tempDir, 'pom.xml'), pomXml); const result = await analyzeDependencies(tempDir, 'java'); expect(result.dependencies.length).toBe(2); expect(result.packageManager).toBe('maven'); const springDep = result.dependencies.find(d => d.name.includes('spring-boot-starter')); expect(springDep).toBeDefined(); }); }); describe('Empty project', () => { it('should return empty dependencies for unknown project type', async () => { const result = await analyzeDependencies(tempDir, 'unknown'); expect(result.dependencies).toHaveLength(0); expect(result.packageManager).toBeNull(); }); }); });