fix: 修复 package协议解析问题
This commit is contained in:
@ -86,13 +86,20 @@ class PackageDiscovery extends BaseDiscovery {
|
||||
*/
|
||||
async _loadStaticRegistry() {
|
||||
const packageRoot = await this._findPackageRoot()
|
||||
const registryPath = path.join(packageRoot, 'src', 'resource.registry.json')
|
||||
|
||||
if (!await fs.pathExists(registryPath)) {
|
||||
throw new Error('Static registry file not found')
|
||||
// 尝试主要路径:src/resource.registry.json
|
||||
const primaryPath = path.join(packageRoot, 'src', 'resource.registry.json')
|
||||
if (await fs.pathExists(primaryPath)) {
|
||||
return await fs.readJSON(primaryPath)
|
||||
}
|
||||
|
||||
return await fs.readJSON(registryPath)
|
||||
// 尝试后备路径:resource.registry.json
|
||||
const alternativePath = path.join(packageRoot, 'resource.registry.json')
|
||||
if (await fs.pathExists(alternativePath)) {
|
||||
return await fs.readJSON(alternativePath)
|
||||
}
|
||||
|
||||
throw new Error('Static registry file not found')
|
||||
}
|
||||
|
||||
/**
|
||||
@ -146,6 +153,86 @@ class PackageDiscovery extends BaseDiscovery {
|
||||
return await this.fileScanner.scanResourceFiles(baseDir, resourceType)
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测执行环境类型
|
||||
* @returns {Promise<string>} 环境类型:development, npx, local, unknown
|
||||
*/
|
||||
async _detectExecutionEnvironment() {
|
||||
// 1. 检查是否在开发环境
|
||||
if (await this._isDevelopmentMode()) {
|
||||
return 'development'
|
||||
}
|
||||
|
||||
// 2. 检查是否通过npx执行
|
||||
if (this._isNpxExecution()) {
|
||||
return 'npx'
|
||||
}
|
||||
|
||||
// 3. 检查是否在node_modules中安装
|
||||
if (this._isLocalInstallation()) {
|
||||
return 'local'
|
||||
}
|
||||
|
||||
return 'unknown'
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否在开发模式
|
||||
* @returns {Promise<boolean>} 是否为开发模式
|
||||
*/
|
||||
async _isDevelopmentMode() {
|
||||
const cwd = process.cwd()
|
||||
const hasCliScript = await fs.pathExists(path.join(cwd, 'src', 'bin', 'promptx.js'))
|
||||
const hasPackageJson = await fs.pathExists(path.join(cwd, 'package.json'))
|
||||
|
||||
if (!hasCliScript || !hasPackageJson) {
|
||||
return false
|
||||
}
|
||||
|
||||
try {
|
||||
const packageJson = await fs.readJSON(path.join(cwd, 'package.json'))
|
||||
return packageJson.name === 'dpml-prompt'
|
||||
} catch (error) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否通过npx执行
|
||||
* @returns {boolean} 是否为npx执行
|
||||
*/
|
||||
_isNpxExecution() {
|
||||
// 检查环境变量
|
||||
if (process.env.npm_execpath && process.env.npm_execpath.includes('npx')) {
|
||||
return true
|
||||
}
|
||||
|
||||
// 检查目录路径(npx缓存目录)
|
||||
const currentDir = this._getCurrentDirectory()
|
||||
if (currentDir.includes('.npm/_npx/') || currentDir.includes('_npx')) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否在本地安装
|
||||
* @returns {boolean} 是否为本地安装
|
||||
*/
|
||||
_isLocalInstallation() {
|
||||
const currentDir = this._getCurrentDirectory()
|
||||
return currentDir.includes('node_modules/dpml-prompt')
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前目录(可以被测试mock)
|
||||
* @returns {string} 当前目录路径
|
||||
*/
|
||||
_getCurrentDirectory() {
|
||||
return __dirname
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找包根目录
|
||||
* @returns {Promise<string>} 包根目录路径
|
||||
@ -157,9 +244,23 @@ class PackageDiscovery extends BaseDiscovery {
|
||||
return cached
|
||||
}
|
||||
|
||||
const packageRoot = await this._findPackageJsonWithPrompt()
|
||||
const environment = await this._detectExecutionEnvironment()
|
||||
let packageRoot = null
|
||||
|
||||
switch (environment) {
|
||||
case 'development':
|
||||
packageRoot = await this._findDevelopmentRoot()
|
||||
break
|
||||
case 'npx':
|
||||
case 'local':
|
||||
packageRoot = await this._findInstalledRoot()
|
||||
break
|
||||
default:
|
||||
packageRoot = await this._findFallbackRoot()
|
||||
}
|
||||
|
||||
if (!packageRoot) {
|
||||
throw new Error('Package root with prompt directory not found')
|
||||
throw new Error('Package root not found')
|
||||
}
|
||||
|
||||
this.setCache(cacheKey, packageRoot)
|
||||
@ -167,40 +268,77 @@ class PackageDiscovery extends BaseDiscovery {
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找包含prompt目录的package.json
|
||||
* 查找开发环境的包根目录
|
||||
* @returns {Promise<string|null>} 包根目录路径或null
|
||||
*/
|
||||
async _findPackageJsonWithPrompt() {
|
||||
let currentDir = __dirname
|
||||
async _findDevelopmentRoot() {
|
||||
const cwd = process.cwd()
|
||||
const hasPackageJson = await fs.pathExists(path.join(cwd, 'package.json'))
|
||||
const hasPromptDir = await fs.pathExists(path.join(cwd, 'prompt'))
|
||||
|
||||
while (currentDir !== path.parse(currentDir).root) {
|
||||
const packageJsonPath = path.join(currentDir, 'package.json')
|
||||
const promptDirPath = path.join(currentDir, 'prompt')
|
||||
if (!hasPackageJson || !hasPromptDir) {
|
||||
return null
|
||||
}
|
||||
|
||||
// 检查是否同时存在package.json和prompt目录
|
||||
const [hasPackageJson, hasPromptDir] = await Promise.all([
|
||||
fs.pathExists(packageJsonPath),
|
||||
fs.pathExists(promptDirPath)
|
||||
])
|
||||
|
||||
if (hasPackageJson && hasPromptDir) {
|
||||
// 验证是否是PromptX包
|
||||
try {
|
||||
const packageJson = await fs.readJSON(packageJsonPath)
|
||||
if (packageJson.name === 'promptx' || packageJson.name === 'dpml-prompt') {
|
||||
return currentDir
|
||||
const packageJson = await fs.readJSON(path.join(cwd, 'package.json'))
|
||||
if (packageJson.name === 'dpml-prompt') {
|
||||
return fs.realpathSync(cwd) // 解析符号链接
|
||||
}
|
||||
} catch (error) {
|
||||
// 忽略package.json读取错误
|
||||
}
|
||||
}
|
||||
|
||||
currentDir = path.dirname(currentDir)
|
||||
// Ignore JSON parsing errors
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找已安装包的根目录
|
||||
* @returns {Promise<string|null>} 包根目录路径或null
|
||||
*/
|
||||
async _findInstalledRoot() {
|
||||
try {
|
||||
const currentDir = this._getCurrentDirectory()
|
||||
let searchDir = currentDir
|
||||
|
||||
// 向上查找package.json
|
||||
while (searchDir !== path.parse(searchDir).root) {
|
||||
const packageJsonPath = path.join(searchDir, 'package.json')
|
||||
|
||||
if (await fs.pathExists(packageJsonPath)) {
|
||||
const packageJson = await fs.readJSON(packageJsonPath)
|
||||
|
||||
if (packageJson.name === 'dpml-prompt') {
|
||||
return searchDir
|
||||
}
|
||||
}
|
||||
|
||||
searchDir = path.dirname(searchDir)
|
||||
}
|
||||
} catch (error) {
|
||||
// Ignore errors
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* 后备方案:使用模块解析查找包根目录
|
||||
* @returns {Promise<string|null>} 包根目录路径或null
|
||||
*/
|
||||
async _findFallbackRoot() {
|
||||
try {
|
||||
const resolve = require('resolve')
|
||||
const packageJsonPath = resolve.sync('dpml-prompt/package.json', {
|
||||
basedir: process.cwd()
|
||||
})
|
||||
return path.dirname(packageJsonPath)
|
||||
} catch (error) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 生成包引用路径
|
||||
* @param {string} filePath - 文件绝对路径
|
||||
|
||||
@ -269,7 +269,7 @@ describe('ResourceDiscovery', () => {
|
||||
const discovered = await discovery.discoverResources(scanPaths.filter(Boolean))
|
||||
|
||||
// Should only call glob for valid paths
|
||||
expect(glob).toHaveBeenCalledTimes(6) // 2 valid paths × 3 resource types
|
||||
expect(glob).toHaveBeenCalledTimes(8) // 2 valid paths × 4 resource types (role, execution, thought, knowledge)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@ -114,6 +114,7 @@ describe('ResourceManager - Unit Tests', () => {
|
||||
const result = await manager.loadResource('java-backend-developer')
|
||||
|
||||
expect(result).toEqual({
|
||||
success: true,
|
||||
content: mockContent,
|
||||
path: '/resolved/path/java.role.md',
|
||||
reference: '@package://prompt/domain/java-backend-developer/java-backend-developer.role.md'
|
||||
@ -144,15 +145,17 @@ describe('ResourceManager - Unit Tests', () => {
|
||||
})
|
||||
|
||||
test('应该处理资源未找到错误', async () => {
|
||||
await expect(manager.loadResource('non-existent-role'))
|
||||
.rejects.toThrow("Resource 'non-existent-role' not found")
|
||||
const result = await manager.loadResource('non-existent-role')
|
||||
expect(result.success).toBe(false)
|
||||
expect(result.message).toBe("Resource 'non-existent-role' not found")
|
||||
})
|
||||
|
||||
test('应该处理协议解析失败', async () => {
|
||||
jest.spyOn(manager.resolver, 'resolve').mockRejectedValue(new Error('Protocol resolution failed'))
|
||||
|
||||
await expect(manager.loadResource('java-backend-developer'))
|
||||
.rejects.toThrow('Protocol resolution failed')
|
||||
const result = await manager.loadResource('java-backend-developer')
|
||||
expect(result.success).toBe(false)
|
||||
expect(result.message).toBe('Protocol resolution failed')
|
||||
})
|
||||
|
||||
test('应该处理文件读取失败', async () => {
|
||||
@ -161,8 +164,9 @@ describe('ResourceManager - Unit Tests', () => {
|
||||
throw new Error('File not found')
|
||||
})
|
||||
|
||||
await expect(manager.loadResource('java-backend-developer'))
|
||||
.rejects.toThrow('File not found')
|
||||
const result = await manager.loadResource('java-backend-developer')
|
||||
expect(result.success).toBe(false)
|
||||
expect(result.message).toBe('File not found')
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@ -0,0 +1,299 @@
|
||||
const PackageDiscovery = require('../../../../lib/core/resource/discovery/PackageDiscovery')
|
||||
const fs = require('fs-extra')
|
||||
const path = require('path')
|
||||
const tmp = require('tmp')
|
||||
|
||||
describe('PackageDiscovery Environment Detection Integration', () => {
|
||||
let discovery
|
||||
let originalCwd
|
||||
let originalEnv
|
||||
let originalExecPath
|
||||
|
||||
beforeEach(() => {
|
||||
discovery = new PackageDiscovery()
|
||||
originalCwd = process.cwd()
|
||||
originalEnv = process.env.NODE_ENV
|
||||
originalExecPath = process.env.npm_execpath
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
process.chdir(originalCwd)
|
||||
process.env.NODE_ENV = originalEnv
|
||||
process.env.npm_execpath = originalExecPath
|
||||
})
|
||||
|
||||
describe('Environment Detection', () => {
|
||||
test('should detect development environment', async () => {
|
||||
// Mock development environment indicators
|
||||
jest.spyOn(fs, 'pathExists')
|
||||
.mockResolvedValueOnce(true) // src/bin/promptx.js exists
|
||||
.mockResolvedValueOnce(true) // package.json exists
|
||||
|
||||
jest.spyOn(fs, 'readJSON').mockResolvedValue({
|
||||
name: 'dpml-prompt'
|
||||
})
|
||||
|
||||
const environment = await discovery._detectExecutionEnvironment()
|
||||
expect(environment).toBe('development')
|
||||
})
|
||||
|
||||
test('should detect npx execution via environment variable', async () => {
|
||||
process.env.npm_execpath = '/usr/local/bin/npx'
|
||||
|
||||
// Mock non-development environment
|
||||
jest.spyOn(fs, 'pathExists').mockResolvedValue(false)
|
||||
|
||||
const environment = await discovery._detectExecutionEnvironment()
|
||||
expect(environment).toBe('npx')
|
||||
})
|
||||
|
||||
test('should detect npx execution via directory path', async () => {
|
||||
// Mock _getCurrentDirectory to simulate npx cache directory
|
||||
jest.spyOn(discovery, '_getCurrentDirectory').mockReturnValue('/home/user/.npm/_npx/abc123/node_modules/dpml-prompt')
|
||||
jest.spyOn(fs, 'pathExists').mockResolvedValue(false)
|
||||
|
||||
const environment = await discovery._detectExecutionEnvironment()
|
||||
expect(environment).toBe('npx')
|
||||
})
|
||||
|
||||
test('should detect local installation', async () => {
|
||||
// Mock _getCurrentDirectory to simulate node_modules installation
|
||||
jest.spyOn(discovery, '_getCurrentDirectory').mockReturnValue('/project/node_modules/dpml-prompt/src/lib/core/resource/discovery')
|
||||
jest.spyOn(fs, 'pathExists').mockResolvedValue(false)
|
||||
|
||||
const environment = await discovery._detectExecutionEnvironment()
|
||||
expect(environment).toBe('local')
|
||||
})
|
||||
|
||||
test('should return unknown for unrecognized environment', async () => {
|
||||
jest.spyOn(fs, 'pathExists').mockResolvedValue(false)
|
||||
|
||||
const environment = await discovery._detectExecutionEnvironment()
|
||||
expect(environment).toBe('unknown')
|
||||
})
|
||||
})
|
||||
|
||||
describe('Package Root Finding - Development Environment', () => {
|
||||
test('should find package root in development mode', async () => {
|
||||
// Setup development environment
|
||||
const tempDir = tmp.dirSync({ unsafeCleanup: true })
|
||||
const projectRoot = tempDir.name
|
||||
|
||||
// Create development structure
|
||||
await fs.ensureDir(path.join(projectRoot, 'src', 'bin'))
|
||||
await fs.ensureDir(path.join(projectRoot, 'prompt'))
|
||||
await fs.writeJSON(path.join(projectRoot, 'package.json'), {
|
||||
name: 'dpml-prompt',
|
||||
version: '1.0.0'
|
||||
})
|
||||
await fs.writeFile(path.join(projectRoot, 'src', 'bin', 'promptx.js'), '// CLI entry')
|
||||
|
||||
process.chdir(projectRoot)
|
||||
|
||||
const packageRoot = await discovery._findDevelopmentRoot()
|
||||
// Use fs.realpathSync to handle symlinks and path resolution consistently
|
||||
expect(fs.realpathSync(packageRoot)).toBe(fs.realpathSync(projectRoot))
|
||||
})
|
||||
|
||||
test('should return null if not dpml-prompt package', async () => {
|
||||
const tempDir = tmp.dirSync({ unsafeCleanup: true })
|
||||
const projectRoot = tempDir.name
|
||||
|
||||
await fs.ensureDir(path.join(projectRoot, 'src', 'bin'))
|
||||
await fs.ensureDir(path.join(projectRoot, 'prompt'))
|
||||
await fs.writeJSON(path.join(projectRoot, 'package.json'), {
|
||||
name: 'other-package',
|
||||
version: '1.0.0'
|
||||
})
|
||||
|
||||
process.chdir(projectRoot)
|
||||
|
||||
const packageRoot = await discovery._findDevelopmentRoot()
|
||||
expect(packageRoot).toBeNull()
|
||||
})
|
||||
|
||||
test('should return null if missing required directories', async () => {
|
||||
const tempDir = tmp.dirSync({ unsafeCleanup: true })
|
||||
process.chdir(tempDir.name)
|
||||
|
||||
await fs.writeJSON(path.join(tempDir.name, 'package.json'), {
|
||||
name: 'dpml-prompt'
|
||||
})
|
||||
// Missing prompt directory
|
||||
|
||||
const packageRoot = await discovery._findDevelopmentRoot()
|
||||
expect(packageRoot).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
describe('Package Root Finding - Installed Environment', () => {
|
||||
test('should find package root by searching upward', async () => {
|
||||
const tempDir = tmp.dirSync({ unsafeCleanup: true })
|
||||
const packagePath = path.join(tempDir.name, 'node_modules', 'dpml-prompt')
|
||||
const searchStartPath = path.join(packagePath, 'src', 'lib', 'core')
|
||||
|
||||
// Create installed package structure
|
||||
await fs.ensureDir(searchStartPath)
|
||||
await fs.writeJSON(path.join(packagePath, 'package.json'), {
|
||||
name: 'dpml-prompt',
|
||||
version: '1.0.0'
|
||||
})
|
||||
|
||||
// Mock _getCurrentDirectory to start search from nested directory
|
||||
jest.spyOn(discovery, '_getCurrentDirectory').mockReturnValue(searchStartPath)
|
||||
|
||||
const packageRoot = await discovery._findInstalledRoot()
|
||||
expect(packageRoot).toBe(packagePath)
|
||||
})
|
||||
|
||||
test('should return null if search finds wrong package', async () => {
|
||||
const tempDir = tmp.dirSync({ unsafeCleanup: true })
|
||||
const packagePath = path.join(tempDir.name, 'node_modules', 'other-package')
|
||||
const searchStartPath = path.join(packagePath, 'src', 'lib')
|
||||
|
||||
await fs.ensureDir(searchStartPath)
|
||||
await fs.writeJSON(path.join(packagePath, 'package.json'), {
|
||||
name: 'other-package',
|
||||
version: '1.0.0'
|
||||
})
|
||||
|
||||
jest.spyOn(discovery, '_getCurrentDirectory').mockReturnValue(searchStartPath)
|
||||
|
||||
const packageRoot = await discovery._findInstalledRoot()
|
||||
expect(packageRoot).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
describe('Package Root Finding - Fallback', () => {
|
||||
test('should find package using module resolution', async () => {
|
||||
const tempDir = tmp.dirSync({ unsafeCleanup: true })
|
||||
const packagePath = path.join(tempDir.name, 'node_modules', 'dpml-prompt')
|
||||
|
||||
// Create package structure
|
||||
await fs.ensureDir(packagePath)
|
||||
await fs.writeJSON(path.join(packagePath, 'package.json'), {
|
||||
name: 'dpml-prompt',
|
||||
version: '1.0.0'
|
||||
})
|
||||
|
||||
// Mock resolve to find our package
|
||||
const resolve = require('resolve')
|
||||
jest.spyOn(resolve, 'sync').mockReturnValue(path.join(packagePath, 'package.json'))
|
||||
|
||||
const packageRoot = await discovery._findFallbackRoot()
|
||||
expect(packageRoot).toBe(packagePath)
|
||||
})
|
||||
|
||||
test('should return null if module resolution fails', async () => {
|
||||
const resolve = require('resolve')
|
||||
jest.spyOn(resolve, 'sync').mockImplementation(() => {
|
||||
throw new Error('Module not found')
|
||||
})
|
||||
|
||||
const packageRoot = await discovery._findFallbackRoot()
|
||||
expect(packageRoot).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
describe('Registry Path Resolution', () => {
|
||||
test('should load registry from src/resource.registry.json in development', async () => {
|
||||
const tempDir = tmp.dirSync({ unsafeCleanup: true })
|
||||
const registryPath = path.join(tempDir.name, 'src', 'resource.registry.json')
|
||||
const testRegistry = { test: 'data' }
|
||||
|
||||
await fs.ensureDir(path.dirname(registryPath))
|
||||
await fs.writeJSON(registryPath, testRegistry)
|
||||
|
||||
jest.spyOn(discovery, '_findPackageRoot').mockResolvedValue(tempDir.name)
|
||||
|
||||
const registry = await discovery._loadStaticRegistry()
|
||||
expect(registry).toEqual(testRegistry)
|
||||
})
|
||||
|
||||
test('should fallback to alternative registry location', async () => {
|
||||
const tempDir = tmp.dirSync({ unsafeCleanup: true })
|
||||
const altRegistryPath = path.join(tempDir.name, 'resource.registry.json')
|
||||
const testRegistry = { test: 'alternative' }
|
||||
|
||||
// No src/resource.registry.json, but alternative exists
|
||||
await fs.writeJSON(altRegistryPath, testRegistry)
|
||||
|
||||
jest.spyOn(discovery, '_findPackageRoot').mockResolvedValue(tempDir.name)
|
||||
|
||||
const registry = await discovery._loadStaticRegistry()
|
||||
expect(registry).toEqual(testRegistry)
|
||||
})
|
||||
|
||||
test('should throw error if no registry found', async () => {
|
||||
const tempDir = tmp.dirSync({ unsafeCleanup: true })
|
||||
jest.spyOn(discovery, '_findPackageRoot').mockResolvedValue(tempDir.name)
|
||||
|
||||
await expect(discovery._loadStaticRegistry()).rejects.toThrow('Static registry file not found')
|
||||
})
|
||||
})
|
||||
|
||||
describe('Integration - Complete Package Discovery Flow', () => {
|
||||
test('should work end-to-end in development environment', async () => {
|
||||
const tempDir = tmp.dirSync({ unsafeCleanup: true })
|
||||
const projectRoot = tempDir.name
|
||||
|
||||
// Setup complete development environment
|
||||
await fs.ensureDir(path.join(projectRoot, 'src', 'bin'))
|
||||
await fs.ensureDir(path.join(projectRoot, 'prompt'))
|
||||
await fs.writeJSON(path.join(projectRoot, 'package.json'), {
|
||||
name: 'dpml-prompt'
|
||||
})
|
||||
await fs.writeFile(path.join(projectRoot, 'src', 'bin', 'promptx.js'), '// CLI')
|
||||
await fs.writeJSON(path.join(projectRoot, 'src', 'resource.registry.json'), {
|
||||
protocols: {
|
||||
role: {
|
||||
registry: {
|
||||
'test-role': '@package://test.md'
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
process.chdir(projectRoot)
|
||||
|
||||
// Test complete discovery flow
|
||||
const resources = await discovery.discover()
|
||||
expect(resources.length).toBeGreaterThan(0)
|
||||
|
||||
// Should find registry resources
|
||||
const roleResources = resources.filter(r => r.id.startsWith('role:'))
|
||||
expect(roleResources.length).toBeGreaterThan(0)
|
||||
})
|
||||
|
||||
test('should work end-to-end in installed environment', async () => {
|
||||
const tempDir = tmp.dirSync({ unsafeCleanup: true })
|
||||
const packagePath = path.join(tempDir.name, 'node_modules', 'dpml-prompt')
|
||||
|
||||
// Setup installed package structure
|
||||
await fs.ensureDir(path.join(packagePath, 'src'))
|
||||
await fs.ensureDir(path.join(packagePath, 'prompt'))
|
||||
await fs.writeJSON(path.join(packagePath, 'package.json'), {
|
||||
name: 'dpml-prompt'
|
||||
})
|
||||
await fs.writeJSON(path.join(packagePath, 'src', 'resource.registry.json'), {
|
||||
protocols: {
|
||||
role: {
|
||||
registry: {
|
||||
'installed-role': '@package://installed.md'
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Mock environment detection to return 'local'
|
||||
jest.spyOn(discovery, '_detectExecutionEnvironment').mockResolvedValue('local')
|
||||
jest.spyOn(discovery, '_findInstalledRoot').mockResolvedValue(packagePath)
|
||||
|
||||
const resources = await discovery.discover()
|
||||
expect(resources.length).toBeGreaterThan(0)
|
||||
|
||||
const roleResources = resources.filter(r => r.id.startsWith('role:'))
|
||||
expect(roleResources.length).toBeGreaterThan(0)
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -113,19 +113,38 @@ describe('PackageDiscovery', () => {
|
||||
})
|
||||
|
||||
describe('_findPackageRoot', () => {
|
||||
test('should find package root containing prompt directory', async () => {
|
||||
// Mock file system check
|
||||
jest.spyOn(discovery, '_findPackageJsonWithPrompt').mockResolvedValue('/mock/package/root')
|
||||
test('should find package root in development environment', async () => {
|
||||
// Mock environment detection and development root finder
|
||||
jest.spyOn(discovery, '_detectExecutionEnvironment').mockResolvedValue('development')
|
||||
jest.spyOn(discovery, '_findDevelopmentRoot').mockResolvedValue('/mock/package/root')
|
||||
|
||||
const root = await discovery._findPackageRoot()
|
||||
|
||||
expect(root).toBe('/mock/package/root')
|
||||
})
|
||||
|
||||
test('should throw error if package root not found', async () => {
|
||||
jest.spyOn(discovery, '_findPackageJsonWithPrompt').mockResolvedValue(null)
|
||||
test('should find package root in installed environment', async () => {
|
||||
// Mock environment detection and installed root finder
|
||||
jest.spyOn(discovery, '_detectExecutionEnvironment').mockResolvedValue('local')
|
||||
jest.spyOn(discovery, '_findInstalledRoot').mockResolvedValue('/mock/node_modules/dpml-prompt')
|
||||
|
||||
await expect(discovery._findPackageRoot()).rejects.toThrow('Package root with prompt directory not found')
|
||||
const root = await discovery._findPackageRoot()
|
||||
expect(root).toBe('/mock/node_modules/dpml-prompt')
|
||||
})
|
||||
|
||||
test('should use fallback method for unknown environment', async () => {
|
||||
// Mock environment detection and fallback finder
|
||||
jest.spyOn(discovery, '_detectExecutionEnvironment').mockResolvedValue('unknown')
|
||||
jest.spyOn(discovery, '_findFallbackRoot').mockResolvedValue('/mock/fallback/root')
|
||||
|
||||
const root = await discovery._findPackageRoot()
|
||||
expect(root).toBe('/mock/fallback/root')
|
||||
})
|
||||
|
||||
test('should throw error if package root not found', async () => {
|
||||
jest.spyOn(discovery, '_detectExecutionEnvironment').mockResolvedValue('development')
|
||||
jest.spyOn(discovery, '_findDevelopmentRoot').mockResolvedValue(null)
|
||||
|
||||
await expect(discovery._findPackageRoot()).rejects.toThrow('Package root not found')
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@ -166,8 +166,9 @@ describe('ResourceManager - Integration Tests', () => {
|
||||
glob.mockResolvedValue([])
|
||||
await manager.initialize()
|
||||
|
||||
await expect(manager.loadResource('non-existent-resource'))
|
||||
.rejects.toThrow("Resource 'non-existent-resource' not found")
|
||||
const result = await manager.loadResource('non-existent-resource')
|
||||
expect(result.success).toBe(false)
|
||||
expect(result.message).toBe("Resource 'non-existent-resource' not found")
|
||||
})
|
||||
|
||||
test('应该处理协议解析失败', async () => {
|
||||
@ -177,8 +178,9 @@ describe('ResourceManager - Integration Tests', () => {
|
||||
|
||||
jest.spyOn(manager.resolver, 'resolve').mockRejectedValue(new Error('Protocol resolution failed'))
|
||||
|
||||
await expect(manager.loadResource('java-backend-developer'))
|
||||
.rejects.toThrow('Protocol resolution failed')
|
||||
const result = await manager.loadResource('java-backend-developer')
|
||||
expect(result.success).toBe(false)
|
||||
expect(result.message).toBe('Protocol resolution failed')
|
||||
})
|
||||
|
||||
test('应该处理文件读取失败', async () => {
|
||||
@ -194,8 +196,9 @@ describe('ResourceManager - Integration Tests', () => {
|
||||
throw new Error('File not found')
|
||||
})
|
||||
|
||||
await expect(manager.loadResource('java-backend-developer'))
|
||||
.rejects.toThrow('File not found')
|
||||
const result = await manager.loadResource('java-backend-developer')
|
||||
expect(result.success).toBe(false)
|
||||
expect(result.message).toBe('File not found')
|
||||
})
|
||||
|
||||
test('应该处理初始化失败', async () => {
|
||||
|
||||
Reference in New Issue
Block a user