UNPKG

dpml-prompt

Version:

DPML-powered AI prompt framework - Revolutionary AI-First CLI system based on Deepractice Prompt Markup Language. Build sophisticated AI agents with structured prompts, memory systems, and execution frameworks.

256 lines (215 loc) 9.63 kB
const ProjectProtocol = require('../../../../lib/core/resource/protocols/ProjectProtocol') const { QueryParams } = require('../../../../lib/core/resource/types') const path = require('path') const fs = require('fs').promises describe('ProjectProtocol', () => { let projectProtocol const projectRoot = process.cwd() // PromptX项目根目录 const promptxPath = path.join(projectRoot, '.promptx') beforeEach(() => { projectProtocol = new ProjectProtocol() }) afterEach(() => { projectProtocol.clearCache() }) describe('基础功能', () => { test('应该正确初始化协议', () => { expect(projectProtocol.name).toBe('project') expect(projectProtocol.projectDirs).toBeDefined() expect(Object.keys(projectProtocol.projectDirs)).toContain('root') expect(Object.keys(projectProtocol.projectDirs)).toContain('src') expect(Object.keys(projectProtocol.projectDirs)).toContain('lib') }) test('应该提供协议信息', () => { const info = projectProtocol.getProtocolInfo() expect(info.name).toBe('project') expect(info.description).toContain('项目协议') expect(info.projectMarker).toBe('.promptx') expect(info.supportedDirectories).toContain('src') expect(info.examples).toEqual(expect.arrayContaining([ expect.stringContaining('project://src/') ])) }) test('应该提供支持的查询参数', () => { const params = projectProtocol.getSupportedParams() expect(params.from).toContain('指定搜索起始目录') expect(params.create).toContain('如果目录不存在是否创建') expect(params.line).toContain('行范围') }) }) describe('路径验证', () => { test('应该验证有效的项目路径', () => { expect(projectProtocol.validatePath('src/index.js')).toBe(true) expect(projectProtocol.validatePath('lib/utils')).toBe(true) expect(projectProtocol.validatePath('docs')).toBe(true) expect(projectProtocol.validatePath('root/package.json')).toBe(true) }) test('应该拒绝无效的项目路径', () => { expect(projectProtocol.validatePath('invalid/path')).toBe(false) expect(projectProtocol.validatePath('unknown')).toBe(false) expect(projectProtocol.validatePath('')).toBe(false) expect(projectProtocol.validatePath(null)).toBe(false) }) test('应该验证项目目录类型', () => { const supportedDirs = Object.keys(projectProtocol.projectDirs) supportedDirs.forEach(dir => { expect(projectProtocol.validatePath(`${dir}/test.js`)).toBe(true) expect(projectProtocol.validatePath(dir)).toBe(true) }) }) }) describe('项目根目录查找', () => { test('应该找到当前项目的根目录', async () => { const root = await projectProtocol.findProjectRoot() expect(root).toBe(projectRoot) }) test('应该从子目录找到项目根目录', async () => { const subDir = path.join(projectRoot, 'src', 'lib') const root = await projectProtocol.findProjectRoot(subDir) expect(root).toBe(projectRoot) }) test('应该缓存项目根目录结果', async () => { const root1 = await projectProtocol.findProjectRoot() const root2 = await projectProtocol.findProjectRoot() expect(root1).toBe(root2) expect(root1).toBe(projectRoot) }) test('应该处理未找到项目根目录的情况', async () => { // 使用系统临时目录测试 const tempDir = '/tmp' const root = await projectProtocol.findProjectRoot(tempDir) expect(root).toBeNull() }) }) describe('路径解析', () => { test('应该解析src目录路径', async () => { const resolvedPath = await projectProtocol.resolvePath('src/index.js') expect(resolvedPath).toBe(path.join(projectRoot, 'src', 'index.js')) }) test('应该解析lib目录路径', async () => { const resolvedPath = await projectProtocol.resolvePath('lib/core/resource') expect(resolvedPath).toBe(path.join(projectRoot, 'lib', 'core', 'resource')) }) test('应该解析根目录路径', async () => { const resolvedPath = await projectProtocol.resolvePath('root/package.json') expect(resolvedPath).toBe(path.join(projectRoot, 'package.json')) }) test('应该解析目录路径(无文件名)', async () => { const resolvedPath = await projectProtocol.resolvePath('src') expect(resolvedPath).toBe(path.join(projectRoot, 'src')) }) test('应该拒绝不支持的目录类型', async () => { await expect(projectProtocol.resolvePath('invalid/path')).rejects.toThrow('不支持的项目目录类型') }) test('应该处理安全路径检查', async () => { await expect(projectProtocol.resolvePath('src/../../../etc/passwd')).rejects.toThrow('安全错误') }) test('应该支持from参数指定起始目录', async () => { const queryParams = new QueryParams() queryParams.set('from', projectRoot) const resolvedPath = await projectProtocol.resolvePath('src/test.js', queryParams) expect(resolvedPath).toBe(path.join(projectRoot, 'src', 'test.js')) }) }) describe('内容加载', () => { test('应该加载存在的文件内容', async () => { const packageJsonPath = path.join(projectRoot, 'package.json') const content = await projectProtocol.loadFileContent(packageJsonPath) expect(content).toContain('promptx') }) test('应该加载目录内容', async () => { const srcPath = path.join(projectRoot, 'src') const content = await projectProtocol.loadDirectoryContent(srcPath) expect(content).toContain('[DIR]') }) test('应该支持JSON格式的目录列表', async () => { const srcPath = path.join(projectRoot, 'src') const queryParams = new QueryParams() queryParams.set('format', 'json') const content = await projectProtocol.loadDirectoryContent(srcPath, queryParams) const parsed = JSON.parse(content) expect(Array.isArray(parsed)).toBe(true) }) test('应该支持类型过滤', async () => { const rootPath = projectRoot const queryParams = new QueryParams() queryParams.set('type', 'file') const content = await projectProtocol.loadDirectoryContent(rootPath, queryParams) expect(content).toContain('[FILE]') expect(content).not.toContain('[DIR]') }) test('应该处理不存在的文件', async () => { const nonExistentPath = path.join(projectRoot, 'nonexistent.txt') await expect(projectProtocol.loadContent(nonExistentPath)).rejects.toThrow('文件或目录不存在') }) test('应该支持exists=false参数', async () => { const nonExistentPath = path.join(projectRoot, 'nonexistent.txt') const queryParams = new QueryParams() queryParams.set('exists', 'false') const content = await projectProtocol.loadContent(nonExistentPath, queryParams) expect(content).toBe('') }) }) describe('完整协议解析', () => { test('应该完整解析project://协议', async () => { const content = await projectProtocol.resolve('root/package.json') expect(content).toContain('promptx') }) test('应该处理带查询参数的协议', async () => { const queryParams = new QueryParams() queryParams.set('format', 'json') const content = await projectProtocol.resolve('src', queryParams) const parsed = JSON.parse(content) expect(Array.isArray(parsed)).toBe(true) }) test('应该应用行过滤', async () => { const queryParams = new QueryParams() queryParams.set('line', '1-3') const content = await projectProtocol.resolve('root/package.json', queryParams) const lines = content.split('\n') expect(lines.length).toBe(3) }) }) describe('项目信息', () => { test('应该获取项目信息', async () => { const info = await projectProtocol.getProjectInfo(projectRoot) if (info.error) { // 如果找不到项目根目录,跳过测试 console.warn('Skipping test - project root not found:', info.error) return } expect(info.projectRoot).toBe(projectRoot) expect(info.promptxPath).toBe(promptxPath) expect(info.directories).toBeDefined() expect(info.directories.root.exists).toBe(true) expect(info.directories.src.exists).toBe(true) }) test('应该标识不存在的目录', async () => { const info = await projectProtocol.getProjectInfo(projectRoot) if (info.error) { // 如果找不到项目根目录,跳过测试 console.warn('Skipping test - project root not found:', info.error) return } // 有些目录可能不存在,应该正确标识 Object.values(info.directories).forEach(dir => { expect(dir).toHaveProperty('exists') expect(dir).toHaveProperty('path') }) }) }) describe('缓存管理', () => { test('应该提供缓存统计', () => { const stats = projectProtocol.getCacheStats() expect(stats.protocol).toBe('project') expect(typeof stats.size).toBe('number') expect(typeof stats.enabled).toBe('boolean') }) test('应该能清除缓存', async () => { await projectProtocol.findProjectRoot() // 填充缓存 expect(projectProtocol.projectRootCache.size).toBeGreaterThan(0) projectProtocol.clearCache() expect(projectProtocol.projectRootCache.size).toBe(0) }) }) })