const path = require('path')
const fs = require('fs-extra')
const os = require('os')
// Import the new SimplifiedRoleDiscovery for testing
const SimplifiedRoleDiscovery = require('../../lib/core/resource/SimplifiedRoleDiscovery')
describe('跨平台角色发现兼容性测试 - 优化版', () => {
let tempDir
let projectDir
beforeEach(async () => {
tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'cross-platform-test-'))
projectDir = path.join(tempDir, 'test-project')
await fs.ensureDir(path.join(projectDir, 'prompt', 'domain'))
await fs.ensureDir(path.join(projectDir, '.promptx', 'resource', 'domain'))
await fs.writeFile(
path.join(projectDir, 'package.json'),
JSON.stringify({ name: 'test-project', version: '1.0.0' })
)
// Mock process.cwd to point to our test project
jest.spyOn(process, 'cwd').mockReturnValue(projectDir)
})
afterEach(async () => {
if (tempDir) {
await fs.remove(tempDir)
}
jest.restoreAllMocks()
})
describe('SimplifiedRoleDiscovery 跨平台兼容性', () => {
test('应该使用原生API替代glob发现用户角色', async () => {
// 创建用户角色文件
const roleDir = path.join(projectDir, '.promptx', 'resource', 'domain', 'test-user-role')
await fs.ensureDir(roleDir)
await fs.writeFile(
path.join(roleDir, 'test-user-role.role.md'),
'用户测试角色'
)
const discovery = new SimplifiedRoleDiscovery()
const userRoles = await discovery.discoverUserRoles()
expect(userRoles).toHaveProperty('test-user-role')
expect(userRoles['test-user-role'].source).toBe('user-generated')
})
test('应该正确合并系统角色和用户角色', async () => {
// 创建用户角色(与系统角色同名,应该覆盖)
const roleDir = path.join(projectDir, '.promptx', 'resource', 'domain', 'assistant')
await fs.ensureDir(roleDir)
await fs.writeFile(
path.join(roleDir, 'assistant.role.md'),
`# 自定义助手
> 用户自定义的助手角色
自定义助手`
)
const discovery = new SimplifiedRoleDiscovery()
const allRoles = await discovery.discoverAllRoles()
// 应该包含系统角色和用户角色
expect(allRoles).toHaveProperty('assistant')
// 用户角色应该覆盖系统角色
expect(allRoles.assistant.source).toBe('user-generated')
expect(allRoles.assistant.name).toBe('自定义助手')
})
test('应该能处理不同平台的路径分隔符', () => {
const unixPath = 'prompt/domain/role/role.role.md'
const windowsPath = 'prompt\\domain\\role\\role.role.md'
// 使用path.join确保跨平台兼容性
const normalizedPath = path.join('prompt', 'domain', 'role', 'role.role.md')
// 在当前平台上验证路径处理
if (process.platform === 'win32') {
expect(normalizedPath).toContain('\\')
} else {
expect(normalizedPath).toContain('/')
}
// path.relative应该也能正常工作
const relativePath = path.relative(projectDir, path.join(projectDir, normalizedPath))
expect(relativePath).toBe(normalizedPath)
})
test('应该处理路径中的特殊字符', async () => {
// 创建包含特殊字符的角色名(但符合文件系统要求)
const specialRoleName = 'role-with_special.chars'
const roleDir = path.join(projectDir, 'prompt', 'domain', specialRoleName)
await fs.ensureDir(roleDir)
const roleFile = path.join(roleDir, `${specialRoleName}.role.md`)
await fs.writeFile(roleFile, '特殊角色')
// 验证能正确处理特殊字符的文件名
expect(await fs.pathExists(roleFile)).toBe(true)
const content = await fs.readFile(roleFile, 'utf-8')
expect(content).toContain('特殊角色')
})
})
describe('文件系统权限处理', () => {
test('应该优雅处理无权限访问的目录', async () => {
if (process.platform === 'win32') {
// Windows权限测试较为复杂,跳过
expect(true).toBe(true)
return
}
const restrictedDir = path.join(projectDir, 'restricted')
await fs.ensureDir(restrictedDir)
// 移除读权限
await fs.chmod(restrictedDir, 0o000)
// 角色发现应该不会因为权限问题而崩溃
async function safeDiscoverRoles(scanPath) {
try {
if (await fs.pathExists(scanPath)) {
const domains = await fs.readdir(scanPath)
return domains
}
return []
} catch (error) {
// 应该优雅处理权限错误
console.warn('权限不足,跳过目录:', scanPath)
return []
}
}
const result = await safeDiscoverRoles(restrictedDir)
expect(Array.isArray(result)).toBe(true)
// 恢复权限以便清理
await fs.chmod(restrictedDir, 0o755)
})
})
describe('错误恢复机制', () => {
test('应该在部分文件失败时继续处理其他文件', async () => {
// 创建多个角色,其中一个有问题
const goodRoleDir = path.join(projectDir, 'prompt', 'domain', 'good-role')
await fs.ensureDir(goodRoleDir)
await fs.writeFile(
path.join(goodRoleDir, 'good-role.role.md'),
'正常角色'
)
const badRoleDir = path.join(projectDir, 'prompt', 'domain', 'bad-role')
await fs.ensureDir(badRoleDir)
await fs.writeFile(
path.join(badRoleDir, 'bad-role.role.md'),
'无效内容'
)
// 模拟容错的角色发现实现
async function resilientDiscoverRoles(scanPath) {
const discoveredRoles = {}
const errors = []
try {
if (await fs.pathExists(scanPath)) {
const domains = await fs.readdir(scanPath)
for (const domain of domains) {
try {
const domainDir = path.join(scanPath, domain)
const stat = await fs.stat(domainDir)
if (stat.isDirectory()) {
const roleFile = path.join(domainDir, `${domain}.role.md`)
if (await fs.pathExists(roleFile)) {
const content = await fs.readFile(roleFile, 'utf-8')
// 简单验证内容
if (content.includes('')) {
discoveredRoles[domain] = {
file: roleFile,
name: `🎭 ${domain}`,
description: '容错发现的角色',
source: 'resilient-discovery'
}
} else {
throw new Error('无效的角色文件格式')
}
}
}
} catch (error) {
// 记录错误但继续处理其他文件
errors.push({ domain, error: error.message })
console.warn(`跳过无效角色 ${domain}:`, error.message)
}
}
}
} catch (error) {
console.warn('角色发现过程中出错:', error.message)
}
return { discoveredRoles, errors }
}
const domainPath = path.join(projectDir, 'prompt', 'domain')
const result = await resilientDiscoverRoles(domainPath)
// 应该发现正常角色,跳过问题角色
expect(result.discoveredRoles).toHaveProperty('good-role')
expect(result.discoveredRoles).not.toHaveProperty('bad-role')
expect(result.errors).toHaveLength(1)
expect(result.errors[0].domain).toBe('bad-role')
})
})
})