claude-harness/dsh/tests/install.test.mjs
Andrey Epifancev 768bc46339 fix(dsh): install.sh --roles дописывает DSH-заметку в dev.md (#53)
Установленная копия роли теперь воспроизводима: после копирования roles/
install.sh вставляет в ~/.dsh/roles/dev.md пункт «в DSH исполнение плана —
skill sdd» (после «Режим по умолчанию», с откатом в конец файла, если якоря
нет). Вставка идемпотентна: каждая установка ролей перекопирует файл и
добавляет ровно одну заметку. Общий roles/dev.md не меняется — в Claude Code
строка не действует.

--check сообщает ok/WARN про заметку; тест проверяет наличие, отсутствие
дубля после второго запуска и строку в --check. PORT.md §6 объясняет.

Co-Authored-By: DeepSeek Harness <noreply@deepseek.com>
2026-10-24 18:13:28 +04:00

79 lines
3.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import assert from 'node:assert/strict'
import { spawnSync } from 'node:child_process'
import { existsSync, lstatSync, readFileSync } from 'node:fs'
import { dirname, join } from 'node:path'
import test from 'node:test'
import { fileURLToPath } from 'node:url'
import { cleanup, tempDir, write } from './helpers.mjs'
const here = dirname(fileURLToPath(import.meta.url))
const script = join(here, '..', 'install.sh')
test('install.sh copies skills and roles instead of linking into ~/.claude', () => {
const home = tempDir()
try {
const dshHome = join(home, '.dsh')
const cache = join(
home,
'.claude',
'plugins',
'cache',
'claude-plugins-official',
'superpowers',
'7.0.0',
'skills',
)
write(join(cache, 'using-superpowers', 'SKILL.md'), '---\nname: using-superpowers\n---\nbody\n')
write(join(cache, 'brainstorming', 'SKILL.md'), '---\nname: brainstorming\n---\nbody\n')
const env = { ...process.env, HOME: home, DSH_HOME: dshHome }
const run = () => spawnSync('bash', [script, '--skills', '--roles'], { env, encoding: 'utf8' })
const first = run()
assert.equal(first.status, 0, first.stderr)
assert.match(first.stdout, /copied 2 superpowers skills/)
assert.match(first.stdout, /copied 2 local skills/)
assert.match(first.stdout, /installed 4 roles/)
const skill = join(dshHome, 'skills', 'using-superpowers')
assert.equal(lstatSync(skill).isSymbolicLink(), false)
assert.equal(lstatSync(skill).isDirectory(), true)
assert.match(readFileSync(join(skill, 'SKILL.md'), 'utf8'), /body/)
const roleSkill = join(dshHome, 'skills', 'role', 'SKILL.md')
assert.ok(existsSync(roleSkill), 'role skill was not installed')
assert.match(readFileSync(roleSkill, 'utf8'), /name: role/)
const sddSkill = join(dshHome, 'skills', 'sdd', 'SKILL.md')
assert.ok(existsSync(sddSkill), 'sdd skill was not installed')
assert.match(readFileSync(sddSkill, 'utf8'), /name: sdd/)
for (const role of ['dev', 'admin', 'analyst', 'qa']) {
assert.ok(existsSync(join(dshHome, 'roles', `${role}.md`)), `missing ${role}.md`)
}
const devRole = join(dshHome, 'roles', 'dev.md')
const devText = readFileSync(devRole, 'utf8')
assert.match(devText, /В DSH-сессии исполнение плана идёт по skill `sdd`/)
assert.match(devText, /`maxDepth: 2`/)
assert.match(first.stdout, /role dsh notes/)
assert.equal(countNotes(devText), 1, 'note must be inserted once')
const second = run()
assert.equal(second.status, 0, second.stderr)
assert.equal(lstatSync(skill).isSymbolicLink(), false)
assert.ok(existsSync(roleSkill))
assert.match(second.stdout, /role dsh notes/)
assert.equal(countNotes(readFileSync(devRole, 'utf8')), 1, 'note must not duplicate')
const check = spawnSync('bash', [script, '--check'], { env, encoding: 'utf8' })
assert.match(check.stdout, /ok: role dsh notes/)
} finally {
cleanup(home)
}
})
function countNotes(text) {
return (text.match(/В DSH-сессии исполнение плана/g) ?? []).length
}