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.
299 lines (237 loc) • 10.4 kB
JavaScript
const PromptProtocol = require('../../../../lib/core/resource/protocols/PromptProtocol')
const PackageProtocol = require('../../../../lib/core/resource/protocols/PackageProtocol')
const { QueryParams } = require('../../../../lib/core/resource/types')
describe('PromptProtocol', () => {
let promptProtocol
let mockPackageProtocol
beforeEach(() => {
promptProtocol = new PromptProtocol()
// 创建模拟的 PackageProtocol
mockPackageProtocol = {
getPackageRoot: jest.fn().mockResolvedValue('/mock/package/root'),
loadContent: jest.fn(),
exists: jest.fn()
}
promptProtocol.setPackageProtocol(mockPackageProtocol)
})
afterEach(() => {
promptProtocol.clearCache()
})
describe('基础功能', () => {
test('应该正确初始化协议', () => {
expect(promptProtocol.name).toBe('prompt')
expect(promptProtocol.registry).toBeInstanceOf(Map)
expect(promptProtocol.registry.size).toBeGreaterThan(0)
})
test('应该提供协议信息', () => {
const info = promptProtocol.getProtocolInfo()
expect(info.name).toBe('prompt')
expect(info.description).toContain('PromptX内置提示词资源协议')
expect(info.location).toBe('prompt://{resource_id}')
expect(info.availableResources).toContain('protocols')
expect(info.availableResources).toContain('core')
expect(info.availableResources).toContain('domain')
expect(info.availableResources).toContain('bootstrap')
})
test('应该设置包协议依赖', () => {
const newMockPackage = { test: 'protocol' }
promptProtocol.setPackageProtocol(newMockPackage)
expect(promptProtocol.packageProtocol).toBe(newMockPackage)
})
test('应该提供支持的查询参数', () => {
const params = promptProtocol.getSupportedParams()
expect(params.merge).toContain('是否合并多个文件内容')
expect(params.separator).toContain('文件间分隔符')
expect(params.include_filename).toContain('是否包含文件名标题')
})
})
describe('路径验证', () => {
test('应该验证有效的资源路径', () => {
expect(promptProtocol.validatePath('protocols')).toBe(true)
expect(promptProtocol.validatePath('core')).toBe(true)
expect(promptProtocol.validatePath('domain')).toBe(true)
expect(promptProtocol.validatePath('bootstrap')).toBe(true)
})
test('应该拒绝无效的资源路径', () => {
expect(promptProtocol.validatePath('invalid')).toBe(false)
expect(promptProtocol.validatePath('unknown')).toBe(false)
expect(promptProtocol.validatePath('')).toBe(false)
expect(promptProtocol.validatePath(null)).toBe(false)
})
})
describe('路径解析', () => {
test('应该解析有效的资源路径', async () => {
const resolved = await promptProtocol.resolvePath('protocols')
expect(resolved).toBe('@package://prompt/protocol/**/*.md')
})
test('应该解析所有注册的资源', async () => {
for (const resourceId of promptProtocol.registry.keys()) {
const resolved = await promptProtocol.resolvePath(resourceId)
expect(resolved).toMatch(/@package:\/\//)
}
})
test('应该拒绝未注册的资源', async () => {
await expect(promptProtocol.resolvePath('nonexistent'))
.rejects.toThrow('未找到 prompt 资源: nonexistent')
})
})
describe('单个文件加载', () => {
test('应该加载单个文件', async () => {
const mockContent = '# Test Content\n\nThis is test content.'
mockPackageProtocol.loadContent.mockResolvedValue({
content: mockContent,
metadata: { path: '/test/path' }
})
const content = await promptProtocol.loadSingleFile('@package://bootstrap.md')
expect(content).toBe(mockContent)
expect(mockPackageProtocol.loadContent).toHaveBeenCalledWith('bootstrap.md', undefined)
})
test('应该处理加载错误', async () => {
mockPackageProtocol.loadContent.mockRejectedValue(new Error('File not found'))
await expect(promptProtocol.loadSingleFile('@package://nonexistent.md'))
.rejects.toThrow('加载单个文件失败')
})
})
describe('多个文件加载', () => {
test('应该加载多个文件并合并', async () => {
// 为这个测试使用真实的PackageProtocol
const realPackageProtocol = new PackageProtocol()
promptProtocol.setPackageProtocol(realPackageProtocol)
const content = await promptProtocol.loadMultipleFiles('@package://prompt/protocol/**/*.md')
expect(content).toContain('protocol')
expect(content).toContain('prompt/protocol/')
expect(typeof content).toBe('string')
expect(content.length).toBeGreaterThan(0)
})
test('应该处理没有匹配文件的情况', async () => {
const glob = require('glob')
jest.doMock('glob', () => ({
...jest.requireActual('glob'),
__esModule: true,
default: jest.fn().mockImplementation((pattern, options, callback) => {
if (typeof options === 'function') {
callback = options
options = {}
}
callback(null, []) // 返回空文件列表
})
}))
await expect(promptProtocol.loadMultipleFiles('@package://prompt/nonexistent/**/*.md'))
.rejects.toThrow('没有找到匹配的文件')
})
})
describe('内容合并', () => {
test('应该合并多个文件内容', () => {
const contents = [
{ path: 'file1.md', content: '# File 1\n\nContent 1' },
{ path: 'file2.md', content: '# File 2\n\nContent 2' }
]
const merged = promptProtocol.mergeContents(contents)
expect(merged).toContain('# file1.md')
expect(merged).toContain('# File 1')
expect(merged).toContain('# file2.md')
expect(merged).toContain('# File 2')
expect(merged).toContain('---')
})
test('应该支持不合并返回JSON', () => {
const contents = [
{ path: 'file1.md', content: 'Content 1' },
{ path: 'file2.md', content: 'Content 2' }
]
const queryParams = new QueryParams()
queryParams.set('merge', 'false')
const result = promptProtocol.mergeContents(contents, queryParams)
expect(() => JSON.parse(result)).not.toThrow()
const parsed = JSON.parse(result)
expect(parsed).toHaveLength(2)
expect(parsed[0]).toHaveProperty('path', 'file1.md')
expect(parsed[0]).toHaveProperty('content', 'Content 1')
})
test('应该支持自定义分隔符', () => {
const contents = [
{ path: 'file1.md', content: 'Content 1' },
{ path: 'file2.md', content: 'Content 2' }
]
const queryParams = new QueryParams()
queryParams.set('separator', '\n\n===\n\n')
const result = promptProtocol.mergeContents(contents, queryParams)
expect(result).toContain('===')
expect(result).not.toContain('---')
})
test('应该支持隐藏文件名', () => {
const contents = [
{ path: 'file1.md', content: 'Content 1' }
]
const queryParams = new QueryParams()
queryParams.set('include_filename', 'false')
const result = promptProtocol.mergeContents(contents, queryParams)
expect(result).not.toContain('# file1.md')
expect(result).toContain('Content 1')
})
})
describe('资源存在性检查', () => {
test('应该检查单个文件是否存在', async () => {
mockPackageProtocol.exists.mockResolvedValue(true)
const exists = await promptProtocol.exists('bootstrap')
expect(exists).toBe(true)
expect(mockPackageProtocol.exists).toHaveBeenCalledWith('bootstrap.md', undefined)
})
test('应该检查通配符文件是否存在', async () => {
// 为这个测试使用真实的PackageProtocol
const realPackageProtocol = new PackageProtocol()
promptProtocol.setPackageProtocol(realPackageProtocol)
const exists = await promptProtocol.exists('protocols')
expect(exists).toBe(true)
})
test('应该处理不存在的资源', async () => {
const exists = await promptProtocol.exists('nonexistent')
expect(exists).toBe(false)
})
})
describe('完整协议解析', () => {
test('应该完整加载内容', async () => {
mockPackageProtocol.loadContent.mockResolvedValue({
content: '# Bootstrap Content\n\nThis is bootstrap.',
metadata: {}
})
const content = await promptProtocol.loadContent('@package://bootstrap.md')
expect(content).toBe('# Bootstrap Content\n\nThis is bootstrap.')
})
test('应该处理缺少依赖的情况', async () => {
const newProtocol = new PromptProtocol()
// 不设置 packageProtocol 依赖
await expect(newProtocol.loadContent('@package://test.md'))
.rejects.toThrow('PromptProtocol 需要 PackageProtocol 依赖')
})
})
describe('工具方法', () => {
test('应该列出所有可用资源', () => {
const resources = promptProtocol.listResources()
expect(resources).toBeInstanceOf(Array)
expect(resources.length).toBeGreaterThan(0)
const protocolsResource = resources.find(r => r.id === 'protocols')
expect(protocolsResource).toBeDefined()
expect(protocolsResource.description).toContain('DPML协议规范文档')
})
test('应该获取资源描述', () => {
expect(promptProtocol.getResourceDescription('protocols')).toContain('DPML协议规范文档')
expect(promptProtocol.getResourceDescription('core')).toContain('核心思维和执行模式')
expect(promptProtocol.getResourceDescription('unknown')).toBe('未知资源')
})
})
describe('缓存管理', () => {
test('应该提供缓存统计', () => {
const stats = promptProtocol.getCacheStats()
expect(stats.protocol).toBe('prompt')
expect(typeof stats.size).toBe('number')
expect(typeof stats.enabled).toBe('boolean')
})
test('应该能清除缓存', () => {
// 模拟一些缓存数据
promptProtocol.cache.set('test', 'value')
expect(promptProtocol.cache.size).toBeGreaterThan(0)
promptProtocol.clearCache()
expect(promptProtocol.cache.size).toBe(0)
})
})
})