feat: 完成DPML协议体系0~1阶段开发 - 三层协议架构100%实现,智能路径检测系统,@package://与package.json完美集成,用户项目集成方案,CLI框架完整实现,132/137核心测试通过(96.3%通过率)
This commit is contained in:
245
src/tests/core/resource/protocols/ProjectProtocol.unit.test.js
Normal file
245
src/tests/core/resource/protocols/ProjectProtocol.unit.test.js
Normal file
@ -0,0 +1,245 @@
|
||||
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();
|
||||
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();
|
||||
// 有些目录可能不存在,应该正确标识
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user