claude-harness/dsh/tests/role.test.mjs

124 lines
4.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 { join } from 'node:path'
import test from 'node:test'
import { resolveRolesDir } from '../bundle/index.js'
import { isLegacyClaudePath } from '../bundle/lib/legacy.js'
import { buildRoleText, listRoles, parseRole } from '../bundle/lib/role.js'
import { cleanup, tempDir, write } from './helpers.mjs'
const names = ['dev', 'admin', 'analyst', 'qa']
test('parseRole distinguishes list, role, and invalid input', () => {
assert.deepEqual(parseRole('', names), { kind: 'list' })
assert.deepEqual(parseRole('dev', names), { kind: 'role', role: 'dev' })
assert.deepEqual(parseRole(' admin ', names), { kind: 'role', role: 'admin' })
assert.deepEqual(parseRole('nope', names), { kind: 'invalid', role: 'nope' })
})
test('buildRoleText reads the global layer and notes the missing project layer', () => {
const root = tempDir()
try {
const rolesDir = join(root, 'roles')
const project = join(root, 'project')
write(join(rolesDir, 'dev.md'), '# Роль: dev\n\n## Процесс\n- TDD\n\n## Не делать\n- не писать в main\n')
const built = buildRoleText({
role: 'dev',
cwd: project,
rolesDir,
projectRolesDir: join(project, '.claude', 'roles'),
roleNames: names,
})
assert.match(built.text, /Роль сессии: dev/)
assert.match(built.text, /# Роль: dev/)
assert.match(built.text, /TDD/)
assert.match(built.text, /Проектного файла роли dev нет/)
assert.equal(built.projectMissing, true)
} finally {
cleanup(root)
}
})
test('buildRoleText includes the project layer when it exists', () => {
const root = tempDir()
try {
const rolesDir = join(root, 'roles')
const project = join(root, 'project')
write(join(rolesDir, 'dev.md'), '# Роль: dev\n- общий\n')
write(join(project, '.claude', 'roles', 'dev.md'), '# Роль: dev (проект)\n- проектный\n')
const built = buildRoleText({
role: 'dev',
cwd: project,
rolesDir,
projectRolesDir: join(project, '.claude', 'roles'),
roleNames: names,
})
assert.match(built.text, /- общий/)
assert.match(built.text, /- проектный/)
assert.equal(built.projectMissing, false)
} finally {
cleanup(root)
}
})
test('buildRoleText fails loudly without the global layer', () => {
const root = tempDir()
try {
assert.throws(
() => buildRoleText({ role: 'dev', cwd: root, rolesDir: join(root, 'roles'), roleNames: names }),
/Нет общего файла роли/,
)
} finally {
cleanup(root)
}
})
test('resolveRolesDir prefers ~/.dsh/roles over ~/.claude/roles', () => {
const home = tempDir()
try {
const dsh = join(home, '.dsh', 'roles')
const claude = join(home, '.claude', 'roles')
write(join(dsh, 'dev.md'), '# dsh\n')
write(join(claude, 'dev.md'), '# claude\n')
assert.equal(resolveRolesDir(join(home, 'bundle'), '', home), dsh)
} finally {
cleanup(home)
}
})
test('listRoles reports both layers', () => {
const root = tempDir()
try {
const rolesDir = join(root, 'roles')
const project = join(root, 'project')
write(join(rolesDir, 'dev.md'), '# dev\n')
write(join(project, '.claude', 'roles', 'qa.md'), '# qa\n')
const text = listRoles({
cwd: project,
rolesDir,
projectRolesDir: join(project, '.claude', 'roles'),
roleNames: names,
})
assert.match(text, /dev: общий — есть; проектный — нет/)
assert.match(text, /qa: общий — нет; проектный — есть/)
} finally {
cleanup(root)
}
})
test('isLegacyClaudePath detects ~/.claude paths only', () => {
const home = tempDir()
try {
assert.equal(isLegacyClaudePath(join(home, '.claude', 'roles', 'dev.md'), home), true)
assert.equal(isLegacyClaudePath(join(home, '.dsh', 'roles', 'dev.md'), home), false)
assert.equal(isLegacyClaudePath(join(home, '.claude-other', 'x'), home), false)
assert.equal(isLegacyClaudePath('', home), false)
} finally {
cleanup(home)
}
})