UNPKG

loqatevars

Version:

Locate JavaScript files with 'const' or 'process.env' usage in LLM-generated codebases

196 lines (164 loc) 6.52 kB
const { analyzeConstUsage, findMatchingFiles, findMatchingFilesDetailed, validateDirectory } = require('../lib/utils'); const fs = require('fs-extra'); const path = require('path'); const os = require('os'); const globby = require('globby').default; jest.mock('globby', () => { // Create the main globby function mock const mockGlobby = jest.fn(); // Attach stream method directly to the function mockGlobby.stream = jest.fn(); return { __esModule: true, default: mockGlobby, globby: mockGlobby, stream: mockGlobby.stream, }; }); describe('analyzeConstUsage', () => { test('should detect const variable declarations', () => { const code = `const x = 10;`; const result = analyzeConstUsage(code, 'test.js'); expect(result.variableConst).toBe(1); expect(result.shouldFlag).toBe(true); }); test('should ignore require imports', () => { const code = `const fs = require('fs');`; const result = analyzeConstUsage(code, 'test.js'); expect(result.importConst).toBe(1); expect(result.variableConst).toBe(0); expect(result.shouldFlag).toBe(false); }); test('should detect process.env usage', () => { const code = `console.log(process.env.USER);`; const result = analyzeConstUsage(code, 'test.js'); expect(result.hasProcessEnv).toBe(true); expect(result.shouldFlag).toBe(true); }); test('detects env alias usage', () => { const code = `const env = process.env; console.log(env.MY_VAR);`; const result = analyzeConstUsage(code, 'test.js'); expect(result.hasProcessEnv).toBe(true); }); test('should correctly parse files with ES6 import statements', () => { const importPath = path.join(__dirname, '..', 'directory_test', 'importModule.js'); const code = fs.readFileSync(importPath, 'utf8'); const result = analyzeConstUsage(code, 'importModule.js'); expect(result.importConst).toBe(2); }); }); describe('findMatchingFiles', () => { let testDir; beforeAll(() => { testDir = fs.mkdtempSync(path.join(os.tmpdir(), 'find-match-')); fs.writeFileSync(path.join(testDir, 'file1.js'), 'const x = 1;'); fs.writeFileSync(path.join(testDir, 'file2.js'), 'process.env.USER;'); fs.writeFileSync(path.join(testDir, 'ignore.js'), '// ignored'); }); afterAll(() => { fs.removeSync(testDir); }); test('should find files with const or process.env', async () => { // Mock globby.stream to return test files const mockStream = async function* () { yield path.join(testDir, 'file1.js'); yield path.join(testDir, 'file2.js'); }; globby.stream.mockReturnValue(mockStream()); const result = await findMatchingFiles(testDir, ['ignore.js']); expect(result).toEqual(expect.arrayContaining(['file1.js', 'file2.js'])); }); test('should return an empty array when no matching files', async () => { const noMatchDir = path.join(__dirname, 'no-match-data'); fs.ensureDirSync(noMatchDir); fs.writeFileSync(path.join(noMatchDir, 'a.js'), 'let a = 1;'); fs.writeFileSync(path.join(noMatchDir, 'b.js'), 'var b = 2;'); // Mock globby.stream to return test files const mockStream = async function* () { yield path.join(noMatchDir, 'a.js'); yield path.join(noMatchDir, 'b.js'); }; globby.stream.mockReturnValue(mockStream()); const result = await findMatchingFiles(noMatchDir, []); expect(result).toEqual([]); fs.removeSync(noMatchDir); }); }); describe('findMatchingFilesDetailed', () => { let testDir; beforeAll(() => { testDir = fs.mkdtempSync(path.join(os.tmpdir(), 'detailed-match-')); fs.writeFileSync(path.join(testDir, 'file1.js'), 'const x = 1;'); fs.writeFileSync(path.join(testDir, 'file2.js'), 'process.env.USER;'); fs.writeFileSync(path.join(testDir, 'ignore.js'), '// ignored'); }); afterAll(() => { fs.removeSync(testDir); }); test('should return detailed results', async () => { // Mock globby.stream to return test files const mockStream = async function* () { yield path.join(testDir, 'file1.js'); yield path.join(testDir, 'file2.js'); }; globby.stream.mockReturnValue(mockStream()); const result = await findMatchingFilesDetailed(testDir, ['ignore.js']); expect(result.summary.scannedFiles).toBe(2); expect(result.summary.matchingFiles).toBe(2); const paths = result.matches.map(m => m.relativePath); expect(paths).toContain('file1.js'); expect(paths).toContain('file2.js'); }); test('should report zero matches when none found', async () => { const emptyDir = path.join(__dirname, 'no-match-detailed'); fs.ensureDirSync(emptyDir); fs.writeFileSync(path.join(emptyDir, 'a.js'), 'function t() { return 1; }'); fs.writeFileSync(path.join(emptyDir, 'b.js'), 'var x = 3;'); // Mock globby.stream to return test files const mockStream = async function* () { yield path.join(emptyDir, 'a.js'); yield path.join(emptyDir, 'b.js'); }; globby.stream.mockReturnValue(mockStream()); const result = await findMatchingFilesDetailed(emptyDir, []); expect(result.matches.length).toBe(0); expect(result.summary.matchingFiles).toBe(0); expect(result.summary.scannedFiles).toBe(2); fs.removeSync(emptyDir); }); }); describe('validateDirectory additional cases', () => { test('should reject when path is a file', async () => { const filePath = path.join(__dirname, 'tempfile'); fs.writeFileSync(filePath, 'data'); await expect(validateDirectory(filePath)).rejects.toThrow('PATH_NOT_DIRECTORY'); fs.removeSync(filePath); }); test('should reject for non-string path', async () => { await expect(validateDirectory(42)).rejects.toThrow('INVALID_DIRECTORY'); }); }); describe('analyzeConstUsage error handling', () => { test('should throw AppError for invalid JavaScript', () => { const badCode = 'const ='; expect(() => analyzeConstUsage(badCode, 'bad.js')).toThrow( "Failed to parse bad.js: Both module and script parsing failed. Module error: Unexpected token (1:6), Script error: Unexpected token (1:6)" ); }); }); describe('concurrency detection', () => { test('defaults to 1 when os.cpus returns undefined', () => { jest.resetModules(); const os = require('os'); const original = os.cpus; os.cpus = () => undefined; const { concurrency } = require('../lib/utils'); os.cpus = original; expect(concurrency).toBe(1); }); });