tree-ast-grep-mcp
Version:
Simple, direct ast-grep wrapper for AI coding agents. Zero abstractions, maximum performance.
327 lines (276 loc) • 7.74 kB
JavaScript
/**
* Integration tests for ScanTool
*/
import { ScanTool } from '../../build/tools/scan.js';
import { AstGrepBinaryManager } from '../../build/core/binary-manager.js';
import { WorkspaceManager } from '../../build/core/workspace-manager.js';
import { TestSuite, TestAssert, withTimeout } from '../utils/test-helpers.js';
import fs from 'fs/promises';
import path from 'path';
import { tmpdir } from 'os';
export default async function runScanToolIntegrationTests() {
const suite = new TestSuite('ScanTool Integration Tests');
let binaryManager;
let workspaceManager;
let scanTool;
let tempDir;
suite.beforeAll(async () => {
binaryManager = new AstGrepBinaryManager({ useSystem: true });
workspaceManager = new WorkspaceManager();
scanTool = new ScanTool(binaryManager, workspaceManager);
try {
await binaryManager.initialize();
} catch (error) {
console.log(' ⚠️ Binary manager initialization failed:', error.message);
}
// Create temp directory for test files
tempDir = await fs.mkdtemp(path.join(tmpdir(), 'ast-grep-scan-test-'));
});
suite.afterAll(async () => {
// Clean up temp directory
try {
await fs.rm(tempDir, { recursive: true, force: true });
} catch (error) {
console.log(' ⚠️ Failed to clean up temp directory');
}
});
suite.test('should scan with YAML rule string', async () => {
const ruleYaml = `
rule:
pattern: console.log($ARG)
language: javascript
message: "Found console.log usage"
severity: warning
`;
const params = {
rule: ruleYaml,
paths: ['../test-files']
};
try {
const result = await withTimeout(
scanTool.execute(params),
10000,
'YAML rule scan timed out'
);
TestAssert.assertTrue(typeof result === 'object');
TestAssert.assertTrue('matches' in result || 'findings' in result || 'results' in result);
} catch (error) {
if (error.message.includes('ast-grep not found')) {
console.log(' ⚠️ Skipping YAML rule test - ast-grep not available');
return;
}
throw error;
}
});
suite.test('should scan with rule file', async () => {
// Create a test rule file
const ruleFile = path.join(tempDir, 'test-rule.yml');
const ruleContent = `
rule:
pattern: function $NAME($ARGS) { $$$BODY }
language: javascript
message: "Found function declaration"
severity: info
`;
await fs.writeFile(ruleFile, ruleContent);
const params = {
ruleFile: ruleFile,
paths: ['../test-files']
};
try {
const result = await withTimeout(
scanTool.execute(params),
10000,
'Rule file scan timed out'
);
TestAssert.assertTrue(typeof result === 'object');
} catch (error) {
if (error.message.includes('ast-grep not found')) {
console.log(' ⚠️ Skipping rule file test - ast-grep not available');
return;
}
throw error;
}
});
suite.test('should scan inline code with rule', async () => {
const ruleYaml = `
rule:
pattern: if ($CONDITION) { $$$BODY }
language: javascript
message: "Found if statement"
severity: info
`;
const params = {
rule: ruleYaml,
code: `
function test(user) {
if (user.isActive) {
console.log("User is active");
return true;
}
if (user.age > 18) {
console.log("User is adult");
}
}
`
};
try {
const result = await withTimeout(
scanTool.execute(params),
5000,
'Inline code scan timed out'
);
TestAssert.assertTrue(typeof result === 'object');
} catch (error) {
if (error.message.includes('ast-grep not found')) {
console.log(' ⚠️ Skipping inline code scan test - ast-grep not available');
return;
}
throw error;
}
});
suite.test('should handle multiple rule format', async () => {
const multiRuleYaml = `
rules:
- rule:
pattern: console.log($ARG)
language: javascript
message: "Found console.log"
severity: warning
- rule:
pattern: console.error($ARG)
language: javascript
message: "Found console.error"
severity: error
`;
const params = {
rule: multiRuleYaml,
code: `
console.log("debug message");
console.error("error message");
console.warn("warning message");
`
};
try {
const result = await withTimeout(
scanTool.execute(params),
5000,
'Multi-rule scan timed out'
);
TestAssert.assertTrue(typeof result === 'object');
} catch (error) {
if (error.message.includes('ast-grep not found')) {
console.log(' ⚠️ Skipping multi-rule test - ast-grep not available');
return;
}
throw error;
}
});
suite.test('should validate required parameters', async () => {
const invalidParams1 = {
paths: ['test']
// Missing rule and ruleFile
};
const invalidParams2 = {
rule: '',
paths: ['test']
};
const invalidParams3 = {
rule: 'invalid yaml content {{{}',
code: 'test code'
};
await TestAssert.assertThrowsAsync(
() => scanTool.execute(invalidParams1),
'rule'
);
await TestAssert.assertThrowsAsync(
() => scanTool.execute(invalidParams2),
'rule'
);
// Invalid YAML might be caught by ast-grep, not our validation
try {
await scanTool.execute(invalidParams3);
} catch (error) {
TestAssert.assertTrue(error instanceof Error);
}
});
suite.test('should handle TypeScript rules', async () => {
const tsRuleYaml = `
rule:
pattern: interface $NAME { $$$PROPS }
language: typescript
message: "Found interface declaration"
severity: info
`;
const params = {
rule: tsRuleYaml,
code: `
interface User {
name: string;
age: number;
}
interface Config {
debug: boolean;
version: string;
}
type Status = 'active' | 'inactive';
`
};
try {
const result = await withTimeout(
scanTool.execute(params),
5000,
'TypeScript rule scan timed out'
);
TestAssert.assertTrue(typeof result === 'object');
} catch (error) {
if (error.message.includes('ast-grep not found')) {
console.log(' ⚠️ Skipping TypeScript rule test - ast-grep not available');
return;
}
throw error;
}
});
suite.test('should handle complex scanning rules', async () => {
const complexRuleYaml = `
rule:
any:
- pattern: function $NAME($ARGS) { $$$BODY }
- pattern: const $NAME = ($ARGS) => { $$$BODY }
- pattern: ($ARGS) => { $$$BODY }
language: javascript
message: "Found function definition"
severity: info
constraints:
NAME:
regex: "^[a-z][a-zA-Z0-9]*$"
`;
const params = {
rule: complexRuleYaml,
code: `
function regularFunction(param) {
return param * 2;
}
const arrowFunction = (x, y) => {
return x + y;
}
const lambda = (n) => { return n * n; }
`
};
try {
const result = await withTimeout(
scanTool.execute(params),
5000,
'Complex rule scan timed out'
);
TestAssert.assertTrue(typeof result === 'object');
} catch (error) {
if (error.message.includes('ast-grep not found')) {
console.log(' ⚠️ Skipping complex rule test - ast-grep not available');
return;
}
throw error;
}
});
return suite.run();
}