import test from 'node:test'; import assert from 'node:assert/strict'; import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { installWorkshopSkill } from '../scripts/install-workshop-skill.mjs'; const source = fileURLToPath(new URL('../skills/ksf-workshop-runtime', import.meta.url)); function fixture(t) { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'ksf-skill-install-')); t.after(() => { assert.equal(path.dirname(root), fs.realpathSync(os.tmpdir())); assert.ok(path.basename(root).startsWith('ksf-skill-install-')); fs.rmSync(root, { recursive: true }); }); const destination = path.join(root, 'skills', 'ksf-workshop-runtime'); fs.cpSync(source, destination, { recursive: true }); fs.writeFileSync(path.join(destination, 'credential-binding.json'), 'fixture-private-binding'); const state = path.join(root, 'ksf-workshop-state', 'participants'); fs.mkdirSync(state, { recursive: true }); fs.writeFileSync(path.join(state, 'fixture.json'), 'fixture-private-state'); return { root, destination, state }; } test('Skill只读预检与重复安装不改变绑定、状态和文件,也不创建备份', t => { const f = fixture(t); const check = installWorkshopSkill(source, f.destination, { apply: false }); assert.equal(check.readOnly, true); assert.equal(check.filesChecked, 11); assert.deepEqual(check.changed, []); const result = installWorkshopSkill(source, f.destination); assert.deepEqual(result.changed, []); assert.equal(result.backup, null); assert.equal(fs.readFileSync(path.join(f.destination, 'credential-binding.json'), 'utf8'), 'fixture-private-binding'); assert.equal(fs.readFileSync(path.join(f.state, 'fixture.json'), 'utf8'), 'fixture-private-state'); assert.ok(!JSON.stringify(result).includes('fixture-private')); }); test('未知目标修改在升级前停止,不覆盖维护人的Skill或清单', t => { for (const name of ['SKILL.md', 'FILES.sha256']) { const f = fixture(t), target = path.join(f.destination, name); fs.writeFileSync(target, 'maintainer-concurrent-edit'); assert.throws(() => installWorkshopSkill(source, f.destination), { code: name === 'SKILL.md' ? 'TARGET_HAS_UNEXPECTED_CONTENT' : 'TARGET_MANIFEST_HAS_UNEXPECTED_CONTENT' }); assert.equal(fs.readFileSync(target, 'utf8'), 'maintainer-concurrent-edit'); assert.deepEqual(fs.readdirSync(f.root).sort(), ['ksf-workshop-state', 'skills']); } }); test('包内脚本被篡改时拒绝升级且保留已有文件', t => { const f = fixture(t), altered = path.join(f.root, 'altered-package'); fs.cpSync(source, altered, { recursive: true }); fs.appendFileSync(path.join(altered, 'scripts', 'ksf-workshop.mjs'), '\n// tampered\n'); const before = fs.readFileSync(path.join(f.destination, 'SKILL.md')); assert.throws(() => installWorkshopSkill(altered, f.destination), { code: 'PACKAGE_HASH_MISMATCH' }); assert.deepEqual(fs.readFileSync(path.join(f.destination, 'SKILL.md')), before); });