dsh 0.2.0-rc.2: in-container update (experimental channel), path-agnostic compose, upstream patches adapted to 0.1.x/0.2.x
This commit is contained in:
commit
e6dcef9bf5
302 files changed
+75178
No files matched your search
@@ -0,0 +1,113 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { lstat, mkdir, readFile, readlink, writeFile } from 'node:fs/promises'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import { mkdtemp } from 'node:fs/promises'
|
||||
import test from 'node:test'
|
||||
import {
|
||||
readSystemSkillCatalog,
|
||||
SystemSkillManager,
|
||||
} from '../../control-plane/modules/skill-manager/index.mjs'
|
||||
|
||||
async function fixture() {
|
||||
const root = await mkdtemp(join(tmpdir(), 'dsh-system-skills-'))
|
||||
const sourceRoot = join(root, 'source')
|
||||
const bundle = join(sourceRoot, 'operations')
|
||||
await mkdir(join(bundle, 'references'), { recursive: true })
|
||||
await writeFile(join(sourceRoot, 'catalog.json'), JSON.stringify({
|
||||
schema: 1,
|
||||
skills: [{
|
||||
id: 'operations',
|
||||
source: 'operations',
|
||||
description: { en: 'Operate DSH.', zh: '操作 DSH。' },
|
||||
}],
|
||||
}))
|
||||
await writeFile(join(bundle, 'SKILL.md'), '---\nname: operations\ndescription: Operate DSH.\n---\n\n# Operations\n')
|
||||
await writeFile(join(bundle, 'references', 'guide.md'), '# Guide\n')
|
||||
return {
|
||||
root,
|
||||
sourceRoot,
|
||||
viewRoot: join(root, 'run', 'skills'),
|
||||
statePath: join(root, 'state', 'skills.json'),
|
||||
}
|
||||
}
|
||||
|
||||
test('System Skills are identified by signed catalog ID and content hash', async () => {
|
||||
const { sourceRoot } = await fixture()
|
||||
const catalog = await readSystemSkillCatalog(sourceRoot)
|
||||
assert.equal(catalog.length, 1)
|
||||
assert.equal(catalog[0].id, 'operations')
|
||||
assert.match(catalog[0].sha256, /^[0-9a-f]{64}$/)
|
||||
assert.deepEqual(catalog[0].description, { en: 'Operate DSH.', zh: '操作 DSH。' })
|
||||
})
|
||||
|
||||
test('System Skill selection hot-updates one stable view and persists install state', async () => {
|
||||
const paths = await fixture()
|
||||
const manager = new SystemSkillManager(paths)
|
||||
assert.deepEqual(await manager.initialize(), [{
|
||||
id: 'operations',
|
||||
sha256: (await manager.list())[0].sha256,
|
||||
description: { en: 'Operate DSH.', zh: '操作 DSH。' },
|
||||
installed: true,
|
||||
enabled: true,
|
||||
}])
|
||||
assert.equal(await readlink(join(paths.viewRoot, 'operations')), join(paths.sourceRoot, 'operations'))
|
||||
|
||||
assert.equal((await manager.configure('operations', 'disable'))[0].enabled, false)
|
||||
await assert.rejects(lstat(join(paths.viewRoot, 'operations')), { code: 'ENOENT' })
|
||||
assert.equal((await manager.configure('operations', 'enable'))[0].enabled, true)
|
||||
assert.equal(await readlink(join(paths.viewRoot, 'operations')), join(paths.sourceRoot, 'operations'))
|
||||
|
||||
const uninstalled = (await manager.configure('operations', 'uninstall'))[0]
|
||||
assert.deepEqual({ installed: uninstalled.installed, enabled: uninstalled.enabled }, { installed: false, enabled: false })
|
||||
await assert.rejects(manager.configure('operations', 'enable'), /must be installed/)
|
||||
assert.equal((await new SystemSkillManager(paths).initialize())[0].installed, false)
|
||||
assert.equal((await manager.configure('operations', 'install'))[0].enabled, true)
|
||||
})
|
||||
|
||||
test('System Skill state ignores removed entries and defaults newly signed skills on', async () => {
|
||||
const paths = await fixture()
|
||||
const manager = new SystemSkillManager(paths)
|
||||
await manager.initialize()
|
||||
await manager.configure('operations', 'uninstall')
|
||||
const second = join(paths.sourceRoot, 'second')
|
||||
await mkdir(second)
|
||||
await writeFile(join(second, 'SKILL.md'), '---\nname: second\ndescription: Second.\n---\n\n# Second\n')
|
||||
await writeFile(join(paths.sourceRoot, 'catalog.json'), JSON.stringify({
|
||||
schema: 1,
|
||||
skills: [{ id: 'second', source: 'second', description: { en: 'Second.', zh: '第二个。' } }],
|
||||
}))
|
||||
const listed = await manager.initialize()
|
||||
assert.deepEqual(listed.map(skill => ({ id: skill.id, installed: skill.installed, enabled: skill.enabled })), [
|
||||
{ id: 'second', installed: true, enabled: true },
|
||||
])
|
||||
const saved = JSON.parse(await readFile(paths.statePath, 'utf8'))
|
||||
assert.deepEqual(Object.keys(saved.skills), ['second'])
|
||||
})
|
||||
|
||||
test('System Skill manager switches to a newly selected Environment catalog', async () => {
|
||||
const paths = await fixture()
|
||||
const manager = new SystemSkillManager(paths)
|
||||
await manager.initialize()
|
||||
const replacementRoot = join(paths.root, 'replacement')
|
||||
const replacement = join(replacementRoot, 'replacement')
|
||||
await mkdir(replacement, { recursive: true })
|
||||
await writeFile(join(replacement, 'SKILL.md'), '---\nname: replacement\ndescription: Replacement.\n---\n')
|
||||
await writeFile(join(replacementRoot, 'catalog.json'), JSON.stringify({
|
||||
schema: 1,
|
||||
skills: [{ id: 'replacement', source: 'replacement', description: { en: 'Replacement.', zh: '替代。' } }],
|
||||
}))
|
||||
|
||||
assert.deepEqual((await manager.useSourceRoot(replacementRoot)).map(skill => skill.id), ['replacement'])
|
||||
await assert.rejects(lstat(join(paths.viewRoot, 'operations')), { code: 'ENOENT' })
|
||||
assert.equal(await readlink(join(paths.viewRoot, 'replacement')), replacement)
|
||||
await assert.rejects(manager.configure('operations', 'enable'), /current Environment/)
|
||||
})
|
||||
|
||||
test('System Skill catalog rejects source aliases and mismatched frontmatter', async () => {
|
||||
const paths = await fixture()
|
||||
const document = JSON.parse(await readFile(join(paths.sourceRoot, 'catalog.json'), 'utf8'))
|
||||
document.skills[0].source = '../operations'
|
||||
await writeFile(join(paths.sourceRoot, 'catalog.json'), JSON.stringify(document))
|
||||
await assert.rejects(readSystemSkillCatalog(paths.sourceRoot), /source is invalid/)
|
||||
})
|
||||
Reference in new issue
Block a user