mirror of
https://github.com/Jeuners/ECC.git
synced 2026-09-09 15:02:30 +02:00
* fix(install): reference all curated skills in modules + add reverse-coverage guard (#2431) Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * test(install): normalize path separators in delivery-gate dry-run assertion (#2431) Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --------- Co-authored-by: affaan <affaan@itomarkets.com> Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
67537ea480
commit
38a7ebbe32
5 changed files with 349 additions and 14 deletions
|
|
@ -52,6 +52,29 @@ function writeInstallComponentsManifest(testDir, components) {
|
|||
});
|
||||
}
|
||||
|
||||
function writeInstallModulesManifest(testDir, modules) {
|
||||
writeJson(path.join(testDir, 'manifests', 'install-modules.json'), {
|
||||
version: 1,
|
||||
modules,
|
||||
});
|
||||
}
|
||||
|
||||
function writeInstallProfilesManifest(testDir, profiles) {
|
||||
writeJson(path.join(testDir, 'manifests', 'install-profiles.json'), {
|
||||
version: 1,
|
||||
profiles,
|
||||
});
|
||||
}
|
||||
|
||||
function writeSkillFixture(testDir, skillId, description) {
|
||||
const skillDir = path.join(testDir, 'skills', skillId);
|
||||
fs.mkdirSync(skillDir, { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(skillDir, 'SKILL.md'),
|
||||
`---\nname: ${skillId}\ndescription: ${description}\n---\n# ${skillId}\n`
|
||||
);
|
||||
}
|
||||
|
||||
function stripShebang(source) {
|
||||
let s = source;
|
||||
if (s.charCodeAt(0) === 0xFEFF) s = s.slice(1);
|
||||
|
|
@ -2793,6 +2816,99 @@ function runTests() {
|
|||
assert.ok(result.stdout.includes('Validated'), 'Should output validation count');
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('fails when a curated skill is not referenced by any install module', () => {
|
||||
const testDir = createTestDir();
|
||||
try {
|
||||
writeInstallModulesManifest(testDir, [
|
||||
{
|
||||
id: 'skill-alpha',
|
||||
kind: 'skills',
|
||||
description: 'Alpha skill',
|
||||
paths: ['skills/alpha'],
|
||||
targets: ['claude'],
|
||||
dependencies: [],
|
||||
defaultInstall: false,
|
||||
cost: 'light',
|
||||
stability: 'stable',
|
||||
},
|
||||
{
|
||||
id: 'skill-beta',
|
||||
kind: 'skills',
|
||||
description: 'Beta skill',
|
||||
paths: ['skills/beta'],
|
||||
targets: ['claude'],
|
||||
dependencies: [],
|
||||
defaultInstall: false,
|
||||
cost: 'light',
|
||||
stability: 'stable',
|
||||
},
|
||||
]);
|
||||
writeInstallProfilesManifest(testDir, {
|
||||
core: { description: 'Core', modules: ['skill-alpha', 'skill-beta'] },
|
||||
developer: { description: 'Developer', modules: ['skill-alpha', 'skill-beta'] },
|
||||
security: { description: 'Security', modules: ['skill-alpha', 'skill-beta'] },
|
||||
research: { description: 'Research', modules: ['skill-alpha', 'skill-beta'] },
|
||||
full: { description: 'Full', modules: ['skill-alpha', 'skill-beta'] },
|
||||
});
|
||||
writeSkillFixture(testDir, 'alpha', 'Alpha skill');
|
||||
writeSkillFixture(testDir, 'beta', 'Beta skill');
|
||||
|
||||
let result = runValidatorWithDirs('validate-install-manifests', {
|
||||
REPO_ROOT: testDir,
|
||||
MODULES_MANIFEST_PATH: path.join(testDir, 'manifests', 'install-modules.json'),
|
||||
PROFILES_MANIFEST_PATH: path.join(testDir, 'manifests', 'install-profiles.json'),
|
||||
COMPONENTS_MANIFEST_PATH: path.join(testDir, 'manifests', 'install-components.json'),
|
||||
MODULES_SCHEMA_PATH: modulesSchemaPath,
|
||||
PROFILES_SCHEMA_PATH: profilesSchemaPath,
|
||||
COMPONENTS_SCHEMA_PATH: componentsSchemaPath,
|
||||
});
|
||||
assert.strictEqual(result.code, 0, `Should pass with both skills referenced, got stderr: ${result.stderr}`);
|
||||
|
||||
writeInstallModulesManifest(testDir, [
|
||||
{
|
||||
id: 'skill-alpha',
|
||||
kind: 'skills',
|
||||
description: 'Alpha skill',
|
||||
paths: ['skills/alpha'],
|
||||
targets: ['claude'],
|
||||
dependencies: [],
|
||||
defaultInstall: false,
|
||||
cost: 'light',
|
||||
stability: 'stable',
|
||||
},
|
||||
{
|
||||
id: 'skill-beta',
|
||||
kind: 'skills',
|
||||
description: 'Beta skill',
|
||||
paths: ['skills/beta-restored'],
|
||||
targets: ['claude'],
|
||||
dependencies: [],
|
||||
defaultInstall: false,
|
||||
cost: 'light',
|
||||
stability: 'stable',
|
||||
},
|
||||
]);
|
||||
writeSkillFixture(testDir, 'beta-restored', 'Beta skill restored');
|
||||
|
||||
result = runValidatorWithDirs('validate-install-manifests', {
|
||||
REPO_ROOT: testDir,
|
||||
MODULES_MANIFEST_PATH: path.join(testDir, 'manifests', 'install-modules.json'),
|
||||
PROFILES_MANIFEST_PATH: path.join(testDir, 'manifests', 'install-profiles.json'),
|
||||
COMPONENTS_MANIFEST_PATH: path.join(testDir, 'manifests', 'install-components.json'),
|
||||
MODULES_SCHEMA_PATH: modulesSchemaPath,
|
||||
PROFILES_SCHEMA_PATH: profilesSchemaPath,
|
||||
COMPONENTS_SCHEMA_PATH: componentsSchemaPath,
|
||||
});
|
||||
assert.strictEqual(result.code, 1, 'Should fail when beta is no longer referenced');
|
||||
assert.ok(
|
||||
result.stderr.includes('curated skill skills/beta is not referenced by any install module'),
|
||||
`Should report unreferenced skill, got: ${result.stderr}`
|
||||
);
|
||||
} finally {
|
||||
cleanupTestDir(testDir);
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('exits 0 when install manifests do not exist', () => {
|
||||
const testDir = createTestDir();
|
||||
const result = runValidatorWithDirs('validate-install-manifests', {
|
||||
|
|
|
|||
|
|
@ -372,6 +372,28 @@ function runTests() {
|
|||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('full profile dry-runs include delivery-gate in the install plan', () => {
|
||||
const homeDir = createTempDir('install-apply-home-');
|
||||
const projectDir = createTempDir('install-apply-project-');
|
||||
|
||||
try {
|
||||
const result = run(['--profile', 'full', '--dry-run', '--json'], { cwd: projectDir, homeDir });
|
||||
assert.strictEqual(result.code, 0, result.stderr);
|
||||
const parsed = JSON.parse(result.stdout);
|
||||
assert.strictEqual(parsed.dryRun, true);
|
||||
assert.ok(parsed.plan.selectedModuleIds.includes('workflow-quality'));
|
||||
assert.ok(
|
||||
parsed.plan.operations.some(operation => (
|
||||
String(operation.sourceRelativePath || '').replace(/\\/g, '/').startsWith('skills/delivery-gate/')
|
||||
)),
|
||||
'Full profile dry-run should include the delivery-gate skill'
|
||||
);
|
||||
} finally {
|
||||
cleanup(homeDir);
|
||||
cleanup(projectDir);
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('supports minimal profile dry-runs without hooks through the installer', () => {
|
||||
const homeDir = createTempDir('install-apply-home-');
|
||||
const projectDir = createTempDir('install-apply-project-');
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue