Skip to main content
Glama

AutoDev Codebase MCP Server

by anrgct
index.test.ts12.6 kB
import * as fs from "fs/promises" import { parseSourceCodeForDefinitionsTopLevel } from "../index" import { listFiles } from "../../glob/list-files" import { loadRequiredLanguageParsers } from "../languageParser" import { fileExistsAtPath } from "../../../utils/fs" // Mock dependencies jest.mock("../../glob/list-files") jest.mock("../languageParser") jest.mock("../../../utils/fs") jest.mock("fs/promises") describe("Tree-sitter Service", () => { beforeEach(() => { jest.clearAllMocks() ;(fileExistsAtPath as jest.Mock).mockResolvedValue(true) }) describe("parseSourceCodeForDefinitionsTopLevel", () => { it("should handle non-existent directory", async () => { ;(fileExistsAtPath as jest.Mock).mockResolvedValue(false) const result = await parseSourceCodeForDefinitionsTopLevel("/non/existent/path") expect(result).toBe("This directory does not exist or you do not have permission to access it.") }) it("should handle empty directory", async () => { ;(listFiles as jest.Mock).mockResolvedValue([[], new Set()]) const result = await parseSourceCodeForDefinitionsTopLevel("/test/path") expect(result).toBe("No source code definitions found.") }) it("should parse TypeScript files correctly", async () => { const mockFiles = ["/test/path/file1.ts", "/test/path/file2.tsx", "/test/path/readme.md"] ;(listFiles as jest.Mock).mockResolvedValue([mockFiles, new Set()]) const mockParser = { parse: jest.fn().mockReturnValue({ rootNode: "mockNode", }), } const mockQuery = { captures: jest.fn().mockReturnValue([ { // Must span 4 lines to meet MIN_COMPONENT_LINES node: { startPosition: { row: 0 }, endPosition: { row: 3 }, parent: { startPosition: { row: 0 }, endPosition: { row: 3 }, }, text: () => "export class TestClass", }, name: "name.definition", }, ]), } ;(loadRequiredLanguageParsers as jest.Mock).mockResolvedValue({ ts: { parser: mockParser, query: mockQuery }, tsx: { parser: mockParser, query: mockQuery }, }) ;(fs.readFile as jest.Mock).mockResolvedValue("export class TestClass {\n constructor() {}\n}") const result = await parseSourceCodeForDefinitionsTopLevel("/test/path") expect(result).toContain("file1.ts") expect(result).toContain("file2.tsx") expect(result).not.toContain("readme.md") expect(result).toContain("export class TestClass") }) it("should handle multiple definition types", async () => { const mockFiles = ["/test/path/file.ts"] ;(listFiles as jest.Mock).mockResolvedValue([mockFiles, new Set()]) const mockParser = { parse: jest.fn().mockReturnValue({ rootNode: "mockNode", }), } const mockQuery = { captures: jest.fn().mockReturnValue([ { node: { startPosition: { row: 0 }, endPosition: { row: 3 }, parent: { startPosition: { row: 0 }, endPosition: { row: 3 }, }, text: () => "class TestClass", }, name: "name.definition.class", }, { node: { startPosition: { row: 2 }, endPosition: { row: 5 }, parent: { startPosition: { row: 2 }, endPosition: { row: 5 }, }, text: () => "testMethod()", }, name: "name.definition.function", }, ]), } ;(loadRequiredLanguageParsers as jest.Mock).mockResolvedValue({ ts: { parser: mockParser, query: mockQuery }, }) const fileContent = "class TestClass {\n" + " constructor() {}\n" + " testMethod() {}\n" + "}" ;(fs.readFile as jest.Mock).mockResolvedValue(fileContent) const result = await parseSourceCodeForDefinitionsTopLevel("/test/path") expect(result).toContain("class TestClass") expect(result).toContain("testMethod()") }) it("should handle parsing errors gracefully", async () => { const mockFiles = ["/test/path/file.ts"] ;(listFiles as jest.Mock).mockResolvedValue([mockFiles, new Set()]) const mockParser = { parse: jest.fn().mockImplementation(() => { throw new Error("Parsing error") }), } const mockQuery = { captures: jest.fn(), } ;(loadRequiredLanguageParsers as jest.Mock).mockResolvedValue({ ts: { parser: mockParser, query: mockQuery }, }) ;(fs.readFile as jest.Mock).mockResolvedValue("invalid code") const result = await parseSourceCodeForDefinitionsTopLevel("/test/path") expect(result).toBe("No source code definitions found.") }) it("should capture arrow functions in JSX attributes with 4+ lines", async () => { const mockFiles = ["/test/path/jsx-arrow.tsx"] ;(listFiles as jest.Mock).mockResolvedValue([mockFiles, new Set()]) // Embed the fixture content directly const fixtureContent = `import React from 'react'; export const CheckboxExample = () => ( <VSCodeCheckbox checked={isCustomTemperature} onChange={(e: any) => { const isChecked = e.target.checked setIsCustomTemperature(isChecked) if (!isChecked) { setInputValue(null) // Unset the temperature } else { setInputValue(value ?? 0) // Use value from config } }}> <label className="block font-medium mb-1"> {t("settings:temperature.useCustom")} </label> </VSCodeCheckbox> );` ;(fs.readFile as jest.Mock).mockResolvedValue(fixtureContent) const lines = fixtureContent.split("\n") // Define the node type for proper TypeScript support interface TreeNode { type?: string toString?: () => string text?: () => string startPosition?: { row: number } endPosition?: { row: number } children?: TreeNode[] fields?: () => Record<string, any> printTree?: (depth?: number) => string } // Create a more detailed mock rootNode for debugging Tree-sitter structure // Helper function to print tree nodes const printTree = (node: TreeNode, depth = 0): string => { let result = "" const indent = " ".repeat(depth) // Print node details result += `${indent}Type: ${node.type || "ROOT"}\n` result += `${indent}Text: "${node.text ? node.text() : "root"}"` // Print fields if available if (node.fields) { result += "\n" + indent + "Fields: " + JSON.stringify(node.fields(), null, 2) } // Print children recursively if (node.children && node.children.length > 0) { result += "\n" + indent + "Children:" for (const child of node.children) { result += "\n" + printTree(child, depth + 1) } } return result } const mockRootNode: TreeNode = { toString: () => fixtureContent, text: () => fixtureContent, printTree: function (depth = 0) { return printTree(this, depth) }, children: [ { type: "class_declaration", text: () => "class TestComponent extends React.Component", startPosition: { row: 0 }, endPosition: { row: 20 }, printTree: function (depth = 0) { return printTree(this, depth) }, children: [ { type: "type_identifier", text: () => "TestComponent", printTree: function (depth = 0) { return printTree(this, depth) }, }, { type: "extends_clause", text: () => "extends React.Component", printTree: function (depth = 0) { return printTree(this, depth) }, children: [ { type: "generic_type", text: () => "React.Component", children: [{ type: "member_expression", text: () => "React.Component" }], }, ], }, ], // Debug output to see field names fields: () => { return { name: [{ type: "type_identifier", text: () => "TestComponent" }], class_heritage: [{ type: "extends_clause", text: () => "extends React.Component" }], } }, }, ], } const mockParser = { parse: jest.fn().mockReturnValue({ rootNode: mockRootNode, }), } const mockQuery = { captures: jest.fn().mockImplementation(() => { // Log tree structure for debugging console.log("TREE STRUCTURE:") if (mockRootNode.printTree) { console.log(mockRootNode.printTree()) } else { console.log("Tree structure:", JSON.stringify(mockRootNode, null, 2)) } return [ { node: { startPosition: { row: 4 }, endPosition: { row: 14 }, text: () => lines[4], parent: { startPosition: { row: 4 }, endPosition: { row: 14 }, text: () => lines[4], }, }, name: "definition.lambda", }, ] }), } ;(loadRequiredLanguageParsers as jest.Mock).mockResolvedValue({ tsx: { parser: mockParser, query: mockQuery }, }) const result = await parseSourceCodeForDefinitionsTopLevel("/test/path") // Verify function found and correctly parsed expect(result).toContain("jsx-arrow.tsx") expect(result).toContain("5--15 |") // Verify line count const capture = mockQuery.captures.mock.results[0].value[0] expect(capture.node.endPosition.row - capture.node.startPosition.row).toBeGreaterThanOrEqual(4) }) it("should respect file limit", async () => { const mockFiles = Array(100) .fill(0) .map((_, i) => `/test/path/file${i}.ts`) ;(listFiles as jest.Mock).mockResolvedValue([mockFiles, new Set()]) const mockParser = { parse: jest.fn().mockReturnValue({ rootNode: "mockNode", }), } const mockQuery = { captures: jest.fn().mockReturnValue([]), } ;(loadRequiredLanguageParsers as jest.Mock).mockResolvedValue({ ts: { parser: mockParser, query: mockQuery }, }) await parseSourceCodeForDefinitionsTopLevel("/test/path") // Should only process first 50 files expect(mockParser.parse).toHaveBeenCalledTimes(50) }) it("should handle various supported file extensions", async () => { const mockFiles = [ "/test/path/script.js", "/test/path/app.py", "/test/path/main.rs", "/test/path/program.cpp", "/test/path/code.go", "/test/path/app.kt", "/test/path/script.kts", ] ;(listFiles as jest.Mock).mockResolvedValue([mockFiles, new Set()]) const mockParser = { parse: jest.fn().mockReturnValue({ rootNode: "mockNode", }), } const mockQuery = { captures: jest.fn().mockReturnValue([ { node: { startPosition: { row: 0 }, endPosition: { row: 3 }, parent: { startPosition: { row: 0 }, endPosition: { row: 3 }, }, text: () => "function test() {}", }, name: "name", }, ]), } ;(loadRequiredLanguageParsers as jest.Mock).mockResolvedValue({ js: { parser: mockParser, query: mockQuery }, py: { parser: mockParser, query: mockQuery }, rs: { parser: mockParser, query: mockQuery }, cpp: { parser: mockParser, query: mockQuery }, go: { parser: mockParser, query: mockQuery }, kt: { parser: mockParser, query: mockQuery }, kts: { parser: mockParser, query: mockQuery }, }) ;(fs.readFile as jest.Mock).mockResolvedValue("function test() {}") const result = await parseSourceCodeForDefinitionsTopLevel("/test/path") expect(result).toContain("script.js") expect(result).toContain("app.py") expect(result).toContain("main.rs") expect(result).toContain("program.cpp") expect(result).toContain("code.go") expect(result).toContain("app.kt") expect(result).toContain("script.kts") }) it("should normalize paths in output", async () => { const mockFiles = ["/test/path/dir\\file.ts"] ;(listFiles as jest.Mock).mockResolvedValue([mockFiles, new Set()]) const mockParser = { parse: jest.fn().mockReturnValue({ rootNode: "mockNode", }), } const mockQuery = { captures: jest.fn().mockReturnValue([ { node: { startPosition: { row: 0 }, endPosition: { row: 3 }, parent: { startPosition: { row: 0 }, endPosition: { row: 3 }, }, text: () => "class Test {}", }, name: "name", }, ]), } ;(loadRequiredLanguageParsers as jest.Mock).mockResolvedValue({ ts: { parser: mockParser, query: mockQuery }, }) ;(fs.readFile as jest.Mock).mockResolvedValue("class Test {}") const result = await parseSourceCodeForDefinitionsTopLevel("/test/path") // Should use forward slashes regardless of platform expect(result).toContain("dir/file.ts") expect(result).not.toContain("dir\\file.ts") }) }) })

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/anrgct/autodev-codebase'

If you have feedback or need assistance with the MCP directory API, please join our Discord server