import * as fs from 'fs'; import * as path from 'path'; import { DependencyAnalyzer } from '../src/analyzers/dependencyAnalyzer'; describe('DependencyAnalyzer', () => { let analyzer: DependencyAnalyzer; let testDir: string; beforeEach(async () => { analyzer = new DependencyAnalyzer(); testDir = path.join('/tmp', `ai-context-test-${Date.now()}`); await fs.promises.mkdir(testDir, { recursive: true }); }); afterEach(async () => { if (await fs.promises.stat(testDir).catch(() => null)) { await fs.promises.rm(testDir, { recursive: true }); } }); describe('analyze', () => { it('should parse package.json dependencies correctly', async () => { const packageJson = { dependencies: { express: '^4.18.0', lodash: '^4.17.21', }, devDependencies: { jest: '^29.0.0', typescript: '^5.0.0', }, }; await fs.promises.writeFile( path.join(testDir, 'package.json'), JSON.stringify(packageJson) ); const result = await analyzer.analyze(testDir, false); expect(result.total).toBe(2); expect(result.direct.length).toBe(2); expect(result.direct.find(d => d.name === 'express')?.version).toBe('^4.18.0'); expect(result.dev.length).toBe(0); }); it('should include dev dependencies when requested', async () => { const packageJson = { dependencies: { express: '^4.18.0' }, devDependencies: { jest: '^29.0.0' }, }; await fs.promises.writeFile( path.join(testDir, 'package.json'), JSON.stringify(packageJson) ); const result = await analyzer.analyze(testDir, true); expect(result.total).toBe(2); expect(result.dev.length).toBe(1); expect(result.dev[0].name).toBe('jest'); }); it('should parse requirements.txt dependencies', async () => { await fs.promises.writeFile( path.join(testDir, 'requirements.txt'), `django>=4.0.0 flask>=2.0.0 requests==2.28.0 numpy~=1.24.0` ); const result = await analyzer.analyze(testDir, false); expect(result.total).toBeGreaterThan(0); const deps = result.direct.map(d => d.name); expect(deps).toContain('django'); expect(deps).toContain('flask'); expect(deps).toContain('requests'); }); it('should parse go.mod dependencies', async () => { await fs.promises.writeFile( path.join(testDir, 'go.mod'), `module example.com/mymodule go 1.21 require ( github.com/gin-gonic/gin v1.9.0 github.com/stretchr/testify v1.8.0 )` ); const result = await analyzer.analyze(testDir, false); expect(result.total).toBeGreaterThan(0); const deps = result.direct.map(d => d.name); expect(deps).toContain('github.com/gin-gonic/gin'); }); it('should parse Cargo.toml dependencies', async () => { await fs.promises.writeFile( path.join(testDir, 'Cargo.toml'), `[package] name = "myproject" version = "0.1.0" [dependencies] serde = { version = "1.0", features = ["derive"] } tokio = { version = "1.0", features = ["full"] } [dev-dependencies] assertions = "0.3"` ); const result = await analyzer.analyze(testDir, true); expect(result.total).toBe(2); expect(result.direct.length).toBe(2); expect(result.dev.length).toBe(1); }); it('should parse pyproject.toml dependencies', async () => { await fs.promises.writeFile( path.join(testDir, 'pyproject.toml'), `[project] name = "myproject" version = "0.1.0" dependencies = [ "requests>=2.28.0", "flask>=2.0.0", ] [project.optional-dependencies] dev = [ "pytest>=7.0.0", "black>=23.0.0", ]` ); const result = await analyzer.analyze(testDir, false); expect(result.total).toBe(2); expect(result.direct.length).toBe(2); }); it('should parse composer.json dependencies', async () => { await fs.promises.writeFile( path.join(testDir, 'composer.json'), JSON.stringify({ require: { php: '^8.0', 'laravel/framework': '^10.0', }, 'require-dev': { 'phpunit/phpunit': '^10.0', }, }) ); const result = await analyzer.analyze(testDir, true); expect(result.total).toBe(2); expect(result.dev.length).toBe(1); }); it('should parse Gemfile dependencies', async () => { await fs.promises.writeFile( path.join(testDir, 'Gemfile'), `source 'https://rubygems.org' gem 'rails', '~> 7.0.0' gem 'nokogiri', '>= 1.13' group :development, :test do gem 'rspec-rails', '~> 6.0' end` ); const result = await analyzer.analyze(testDir, true); expect(result.total).toBe(2); expect(result.dev.length).toBe(1); }); it('should handle empty project gracefully', async () => { const result = await analyzer.analyze(testDir, false); expect(result.total).toBe(0); expect(result.direct).toHaveLength(0); expect(result.dev).toHaveLength(0); }); it('should handle invalid JSON gracefully', async () => { await fs.promises.writeFile( path.join(testDir, 'package.json'), 'invalid json {' ); const result = await analyzer.analyze(testDir, false); expect(result.total).toBe(0); }); it('should mark local dependencies correctly', async () => { const packageJson = { dependencies: { './local-module': '*', '@local/package': '*', express: '^4.18.0', }, }; await fs.promises.writeFile( path.join(testDir, 'package.json'), JSON.stringify(packageJson) ); const result = await analyzer.analyze(testDir, false); expect(result.direct.find(d => d.name === './local-module')?.isLocal).toBe(true); expect(result.direct.find(d => d.name === '@local/package')?.isLocal).toBe(true); expect(result.direct.find(d => d.name === 'express')?.isLocal).toBe(false); }); }); });