/**
* Integration tests for all local explorer MCP tools
* Tests all 4 tools on actual node_modules to verify they return smart research data
*/
import { describe, it, expect } from 'vitest';
import { searchContentRipgrep } from '../../src/tools/local_ripgrep.js';
import { viewStructure } from '../../src/tools/local_view_structure.js';
import { findFiles } from '../../src/tools/local_find_files.js';
import { fetchContent } from '../../src/tools/local_fetch_content.js';
import path from 'path';
const NODE_MODULES_PATH = path.resolve(process.cwd(), 'node_modules');
function verifySmartData(
result: { status: string; [key: string]: unknown },
toolName: string
) {
expect(result, `${toolName} should return a result object`).toBeDefined();
expect(result.status, `${toolName} should have status field`).toBeDefined();
expect(['hasResults', 'empty', 'error']).toContain(result.status);
if (result.status === 'hasResults') {
// Different tools return data in different fields
const hasData =
result.files ||
result.entries ||
result.content !== undefined ||
result.treeStructure ||
result.fileType ||
result.magicBytes !== undefined || // Allow empty string
result.hexDump ||
result.strings ||
result.archiveEntries ||
result.data;
expect(hasData, `${toolName} should have data when status is hasResults`).toBeTruthy();
}
if (result.hints) {
expect(Array.isArray(result.hints), `${toolName} hints should be an array`).toBe(true);
}
return result;
}
describe('Integration Tests: All Tools on node_modules', () => {
describe('local_ripgrep - Pattern Search', () => {
it('should find patterns in JavaScript files', async () => {
const result = await searchContentRipgrep({
pattern: 'export',
path: NODE_MODULES_PATH,
include: ['*.js'],
maxMatches: 5,
researchGoal: 'Find exported functions in JavaScript files',
reasoning: 'Testing pattern search on node_modules',
});
verifySmartData(result, 'local_ripgrep');
if (result.status === 'hasResults') {
expect(result.files).toBeDefined();
expect(Array.isArray(result.files)).toBe(true);
}
});
it('should find files only mode', async () => {
const result = await searchContentRipgrep({
pattern: 'package.json',
path: NODE_MODULES_PATH,
filesOnly: true,
maxResults: 10,
researchGoal: 'Find package.json files',
reasoning: 'Testing filesOnly mode',
});
verifySmartData(result, 'local_ripgrep');
if (result.status === 'hasResults') {
expect(result.files).toBeDefined();
expect(Array.isArray(result.files)).toBe(true);
}
});
});
describe('local_view_structure - Directory Listing', () => {
it('should list directory contents', async () => {
const result = await viewStructure({
path: NODE_MODULES_PATH,
details: false,
maxEntries: 20,
researchGoal: 'List top-level node_modules contents',
reasoning: 'Testing basic directory listing',
});
verifySmartData(result, 'local_view_structure');
if (result.status === 'hasResults') {
expect(result.entries || result.treeStructure).toBeDefined();
}
});
it('should provide detailed file information', async () => {
const result = await viewStructure({
path: NODE_MODULES_PATH,
details: true,
maxEntries: 10,
sortBy: 'size',
researchGoal: 'Get detailed file information sorted by size',
reasoning: 'Testing detailed listing with sorting',
});
verifySmartData(result, 'local_view_structure');
if (result.status === 'hasResults') {
expect(result.entries || result.treeStructure).toBeDefined();
}
});
it('should generate tree view', async () => {
const result = await viewStructure({
path: NODE_MODULES_PATH,
treeView: true,
depth: 2,
researchGoal: 'Get tree structure view',
reasoning: 'Testing tree view mode',
});
verifySmartData(result, 'local_view_structure');
if (result.status === 'hasResults') {
expect(result.treeStructure).toBeDefined();
}
});
});
describe('local_find_files - File Discovery', () => {
it('should find files by name', async () => {
const result = await findFiles({
path: NODE_MODULES_PATH,
name: 'package.json',
maxDepth: 2,
maxResults: 20,
researchGoal: 'Find package.json files',
reasoning: 'Testing name-based file discovery',
});
verifySmartData(result, 'local_find_files');
if (result.status === 'hasResults') {
expect(result.files).toBeDefined();
expect(Array.isArray(result.files)).toBe(true);
}
});
it('should find files by extension', async () => {
const result = await findFiles({
path: NODE_MODULES_PATH,
type: 'f',
extension: '.md',
maxResults: 10,
researchGoal: 'Find markdown documentation files',
reasoning: 'Testing extension-based discovery',
});
verifySmartData(result, 'local_find_files');
if (result.status === 'hasResults') {
expect(result.files).toBeDefined();
}
});
it('should find directories', async () => {
const result = await findFiles({
path: NODE_MODULES_PATH,
type: 'd',
maxDepth: 1,
maxResults: 15,
researchGoal: 'Find top-level directories',
reasoning: 'Testing directory discovery',
});
verifySmartData(result, 'local_find_files');
if (result.status === 'hasResults') {
expect(result.files).toBeDefined();
}
});
});
describe('local_fetch_content - File Content Reading', () => {
let testFile: string | null = null;
it('should find a test file first', async () => {
const findResult = await findFiles({
path: NODE_MODULES_PATH,
name: 'package.json',
maxDepth: 2,
maxResults: 5,
researchGoal: 'Find package.json files',
reasoning: 'Testing file discovery for fetch_content tests',
});
if (findResult.status === 'hasResults' && findResult.files && findResult.files.length > 0) {
testFile = typeof findResult.files[0] === 'string' ? findResult.files[0] : findResult.files[0].path;
} else {
// Fallback: try to find any JavaScript file
const jsFileResult = await findFiles({
path: NODE_MODULES_PATH,
extension: '.js',
maxResults: 1,
});
if (jsFileResult.status === 'hasResults' && jsFileResult.files && jsFileResult.files.length > 0) {
testFile = typeof jsFileResult.files[0] === 'string' ? jsFileResult.files[0] : jsFileResult.files[0].path;
}
}
// Don't fail if no file found - subsequent tests will skip
if (!testFile) {
console.warn('Could not find a test file for fetch_content tests');
}
});
it('should read full file content', async () => {
if (!testFile) {
// Skip if no test file found
return;
}
const result = await fetchContent({
path: testFile,
fullContent: true,
minified: false,
researchGoal: 'Read full package.json content',
reasoning: 'Testing full content fetch',
});
verifySmartData(result, 'local_fetch_content');
if (result.status === 'hasResults') {
expect(result.content).toBeDefined();
expect(typeof result.content).toBe('string');
}
});
it('should read line range', async () => {
if (!testFile) {
return;
}
const result = await fetchContent({
path: testFile,
startLine: 1,
endLine: 20,
researchGoal: 'Read first 20 lines',
reasoning: 'Testing line range fetch',
});
verifySmartData(result, 'local_fetch_content');
if (result.status === 'hasResults') {
expect(result.content).toBeDefined();
}
});
it('should extract pattern-based content', async () => {
if (!testFile) {
return;
}
const result = await fetchContent({
path: testFile,
matchString: 'dependencies',
matchStringContextLines: 5,
researchGoal: 'Extract dependencies section',
reasoning: 'Testing pattern-based extraction',
});
verifySmartData(result, 'local_fetch_content');
if (result.status === 'hasResults') {
expect(result.content).toBeDefined();
}
});
});
});