9a6aaca563
Replace execSync with spawnSync in findReferences() to prevent shell injection through malicious symbol names in PR diffs. Use -F (fixed-string) and -e flags for safe argument passing to ripgrep. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
87 lines
2.8 KiB
TypeScript
87 lines
2.8 KiB
TypeScript
// tests/context-gatherer/reference-collector.test.ts
|
|
import { describe, it, expect } from 'vitest'
|
|
import { extractSymbolsFromDiff, findReferences, formatCallChainForReviewer } from '../../src/context-gatherer/collectors/reference-collector.js'
|
|
import type { RawReference } from '../../src/context-gatherer/types.js'
|
|
|
|
describe('extractSymbolsFromDiff', () => {
|
|
it('should extract function names', () => {
|
|
const diff = `
|
|
+function createOrder(data) {
|
|
+ return data
|
|
+}
|
|
`
|
|
const symbols = extractSymbolsFromDiff(diff)
|
|
expect(symbols).toContain('createOrder')
|
|
})
|
|
|
|
it('should extract class names', () => {
|
|
const diff = `
|
|
+class OrderService {
|
|
+ constructor() {}
|
|
+}
|
|
`
|
|
const symbols = extractSymbolsFromDiff(diff)
|
|
expect(symbols).toContain('OrderService')
|
|
})
|
|
|
|
it('should extract arrow functions', () => {
|
|
const diff = `
|
|
+const processOrder = async (order) => {
|
|
+ return order
|
|
+}
|
|
`
|
|
const symbols = extractSymbolsFromDiff(diff)
|
|
expect(symbols).toContain('processOrder')
|
|
})
|
|
|
|
it('should filter out short names and keywords', () => {
|
|
const diff = `
|
|
+function do() {}
|
|
+const x = 1
|
|
`
|
|
const symbols = extractSymbolsFromDiff(diff)
|
|
expect(symbols).not.toContain('do')
|
|
expect(symbols).not.toContain('x')
|
|
})
|
|
})
|
|
|
|
describe('findReferences', () => {
|
|
it('should safely handle symbols with shell metacharacters', () => {
|
|
const result = findReferences(['$(whoami)', 'test;rm', '`id`'], '/tmp')
|
|
expect(Array.isArray(result)).toBe(true)
|
|
})
|
|
})
|
|
|
|
describe('formatCallChainForReviewer', () => {
|
|
it('should format references as structured call chain text', () => {
|
|
const references: RawReference[] = [{
|
|
symbol: 'parseAuth',
|
|
foundInFiles: [
|
|
{ file: 'src/api/login.ts', line: 34, content: 'const user = parseAuth(req.token, opts)' },
|
|
{ file: 'src/middleware/auth.ts', line: 12, content: 'const u = parseAuth(header, {})' }
|
|
]
|
|
}]
|
|
const result = formatCallChainForReviewer(references)
|
|
expect(result).toContain('parseAuth')
|
|
expect(result).toContain('src/api/login.ts:34')
|
|
expect(result).toContain('src/middleware/auth.ts:12')
|
|
})
|
|
|
|
it('should return empty string for no references', () => {
|
|
const result = formatCallChainForReviewer([])
|
|
expect(result).toBe('')
|
|
})
|
|
|
|
it('should limit to 10 callers per symbol', () => {
|
|
const manyFiles = Array.from({ length: 15 }, (_, i) => ({
|
|
file: `src/file${i}.ts`, line: i + 1, content: `call${i}()`
|
|
}))
|
|
const references: RawReference[] = [{ symbol: 'myFunc', foundInFiles: manyFiles }]
|
|
const result = formatCallChainForReviewer(references)
|
|
// Should only contain first 10, not all 15
|
|
expect(result).toContain('src/file0.ts:1')
|
|
expect(result).toContain('src/file9.ts:10')
|
|
expect(result).not.toContain('src/file10.ts:11')
|
|
})
|
|
})
|