Files
ycode/scripts/verify-project-storage.cjs
T

59 lines
3.7 KiB
JavaScript

const assert = require('node:assert/strict')
const fs = require('node:fs/promises')
const path = require('node:path')
const os = require('node:os')
const { build } = require('esbuild')
async function main() {
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'ycode-projects-'))
try {
const bundle = path.join(root, 'projects.cjs')
await build({ entryPoints: [path.resolve('src/main/projects.ts')], bundle: true, platform: 'node', format: 'cjs', outfile: bundle, logLevel: 'silent' })
const { ProjectRepository, buildProjectPrompt } = require(bundle)
const filename = path.join(root, 'data', 'projects.json')
const repository = new ProjectRepository(filename)
const input = { name: ' 项目测试 ', instructions: ' 项目约束 ', experts: [{ id: 'expert', source: 'local' }], skills: [{ id: 'skill', source: 'market', provider: 'skillhub' }], connectors: ['mcp', 'mcp'], templateId: 'custom' }
assert.deepEqual(await repository.list(), [])
const created = await Promise.all(Array.from({ length: 12 }, (_, i) => repository.create({ ...input, name: `项目${i}` })))
assert.equal(new Set(created.map(item => item.projectId)).size, 12)
const reloaded = await new ProjectRepository(filename).list()
assert.equal(reloaded.length, 12)
assert.deepEqual(new Set(reloaded.map(item => item.projectId)), new Set(created.map(item => item.projectId)))
assert.equal(reloaded[0].instructions, '项目约束')
assert.deepEqual(reloaded[0].connectors, ['mcp'])
assert.deepEqual(reloaded[0].experts, input.experts)
assert.deepEqual(reloaded[0].skills, input.skills)
assert.equal((await repository.get(created[0].projectId)).projectId, created[0].projectId)
await assert.rejects(repository.get('missing-project'))
assert.equal(buildProjectPrompt(undefined), '')
assert.equal(buildProjectPrompt({ ...created[0], instructions: ' ' }), '')
assert.equal(buildProjectPrompt(created[0]), '项目:项目0\n项目指令:\n项目约束')
assert.ok(!buildProjectPrompt(created[0]).includes('skillhub'))
await Promise.all([
repository.update(created[0].projectId, { name: '改名项目' }),
repository.update(created[0].projectId, { instructions: '更新后的项目指令' })
])
const edited = await new ProjectRepository(filename).get(created[0].projectId)
assert.equal(edited.name, '改名项目')
assert.equal(edited.instructions, '更新后的项目指令')
assert.equal(edited.createdAt, created[0].createdAt)
assert.deepEqual(edited.skills, input.skills)
await assert.rejects(repository.update(created[0].projectId, { projectId: 'forged' }))
await assert.rejects(repository.update('missing-project', { name: '不存在' }))
await assert.rejects(repository.update(created[0].projectId, { name: '' }))
assert.deepEqual(await repository.get(created[0].projectId), edited)
await assert.rejects(repository.create({ ...input, name: ' ' }))
await assert.rejects(repository.create({ ...input, name: '1234567890123456' }))
await assert.rejects(repository.create({ ...input, experts: [{ id: '../x', source: 'invalid' }] }))
assert.equal((await repository.list()).length, 12)
await repository.create({ ...input, name: '恢复写入' })
assert.equal((await repository.list()).length, 13)
await fs.writeFile(filename, '{broken')
await assert.rejects(repository.list())
await assert.rejects(repository.create(input))
assert.equal(await fs.readFile(filename, 'utf8'), '{broken')
console.log('PASS project persistence: concurrent creates, reload, references, validation, queue recovery, corruption preservation')
} finally { await fs.rm(root, { recursive: true, force: true }) }
}
main().catch(error => { console.error(error); process.exitCode = 1 })