diff --git a/test/capy-skill-wrapper.test.ts b/test/capy-skill-wrapper.test.ts index 711973ad0..21210ddb8 100644 --- a/test/capy-skill-wrapper.test.ts +++ b/test/capy-skill-wrapper.test.ts @@ -17,6 +17,7 @@ import { afterEach, describe, expect, test } from 'bun:test'; import { spawn, spawnSync } from 'child_process'; import * as fs from 'fs'; import * as path from 'path'; +import { capyBootstrapCommand } from '../lib/doctor-components'; import { RESULT_CODES } from '../lib/result-codes'; import { capyHost, type CapyHost } from './helpers/capy-host'; @@ -222,7 +223,7 @@ describe('capy skill bootstrapper (capy/skill/gstack/scripts/install.sh)', () => fs.rmSync(f.root, { recursive: true, force: true }); const check = f.run(['--check', '--for', 'autoplan']); expect(check.status).toBe(1); - expect(check.last).toBe(`gstack: fail not-installed — no gstack checkout at ${f.root}; fix: bash ${BOOT} --for autoplan (installs)`); + expect(check.last).toBe(`gstack: fail not-installed — no gstack checkout at ${f.root}; fix: bash ${BOOT} --for autoplan (installs; on a machine without this skill: ${capyBootstrapCommand('autoplan', f.root)})`); const install = f.run(['--for', 'autoplan']); expect(install.status, install.stdout + install.stderr).toBe(0); expect(f.git(['rev-parse', 'HEAD'])).toBe(f.b); diff --git a/test/doctor-components.test.ts b/test/doctor-components.test.ts index 16d5c6185..3bcb854a0 100644 --- a/test/doctor-components.test.ts +++ b/test/doctor-components.test.ts @@ -10,7 +10,7 @@ import { spawnSync } from 'child_process'; import * as fs from 'fs'; import * as path from 'path'; import { - COMPONENT_IDS, CORE_IDS, DOCTOR_COMPONENTS, SKILL_REQUIREMENTS, renderDoctorComponentsSh, + COMPONENT_IDS, CORE_IDS, DOCTOR_COMPONENTS, INSTALL_PROFILES, SKILL_REQUIREMENTS, renderDoctorComponentsSh, } from '../lib/doctor-components'; import { RESULT_CODES } from '../lib/result-codes'; import { DOCTOR_COMPONENTS_RELPATH } from '../scripts/gen-doctor-components'; @@ -33,10 +33,15 @@ describe('doctor component table', () => { } }); - test('every skill directory in the repo is classified, and no classified skill is missing', () => { + test('every skill directory in the repo is classified, and no classified skill is missing (install profiles are the only other rows)', () => { const skills = fs.readdirSync(ROOT).filter(d => fs.existsSync(path.join(ROOT, d, 'SKILL.md.tmpl'))).sort(); expect(skills.length).toBeGreaterThan(40); - expect(Object.keys(SKILL_REQUIREMENTS).sort()).toEqual(skills); + expect(Object.keys(SKILL_REQUIREMENTS).filter(s => !INSTALL_PROFILES.includes(s)).sort()).toEqual(skills); + expect(INSTALL_PROFILES).toEqual(['review-voice']); + for (const profile of INSTALL_PROFILES) { + expect(skills, `${profile} is an install profile, not a skill`).not.toContain(profile); + expect(SKILL_REQUIREMENTS[profile]?.requires).toEqual(['helpers']); + } }); test('the Bash render answers the same questions as the table, and refuses unknown names', () => { diff --git a/test/gstack-capy-install.test.ts b/test/gstack-capy-install.test.ts index a8db03fa4..10aea1948 100644 --- a/test/gstack-capy-install.test.ts +++ b/test/gstack-capy-install.test.ts @@ -296,9 +296,9 @@ describe('gstack-capy-install', () => { const r = run(f, ['--root', clone], { GSTACK_CAPY_REPO: f.root }); expect(r.status, r.stdout + r.stderr).toBe(0); expect(r.stdout).toContain(`cloning ${f.root} into ${clone}`); - expect(r.stdout).toMatch(new RegExp(`^gstack-capy-install: cloned ${f.root} at [0-9a-f]{12} \\(1\\.2\\.3\\)$`, 'm')); + expect(r.stdout).toMatch(new RegExp(`^gstack-capy-install: cloned ${f.root} into ${clone} at [0-9a-f]{12} \\(1\\.2\\.3\\)$`, 'm')); const handed = run(f, [], { GSTACK_CAPY_CLONED: 'https://github.com/garrytan/gstack.git' }); - expect(handed.stdout).toMatch(/^gstack-capy-install: cloned https:\/\/github\.com\/garrytan\/gstack\.git at [0-9a-f]{12} \(1\.2\.3\)$/m); + expect(handed.stdout).toMatch(new RegExp(`^gstack-capy-install: cloned https://github\\.com/garrytan/gstack\\.git into ${f.root} at [0-9a-f]{12} \\(1\\.2\\.3\\)$`, 'm')); expect(handed.stdout).not.toContain('reusing'); expect(fs.existsSync(path.join(clone, 'setup'))).toBe(true); const occupied = path.join(f.base, 'occupied'); diff --git a/test/gstack-doctor.test.ts b/test/gstack-doctor.test.ts index 3dddefea5..f0ac3309b 100644 --- a/test/gstack-doctor.test.ts +++ b/test/gstack-doctor.test.ts @@ -697,7 +697,7 @@ describe('gstack-doctor --check', () => { expect(json.ok).toBe(false); expect(json.failed).toEqual(['runtime']); expect(json.required).toEqual(['claude', 'codex', 'patch', 'runtime', 'pins', 'state-root', 'privacy', 'cores', 'revision', 'wrapper']); - expect(json.rows.map((x: { id: string }) => x.id)).toEqual(['claude', 'codex', 'patch', 'codex-cli', 'browse-bundle', 'browser', 'cso', 'runtime', 'pins', 'state-root', 'privacy', 'cores', 'revision', 'wrapper']); + expect(json.rows.map((x: { id: string }) => x.id)).toEqual(['claude', 'codex', 'patch', 'codex-cli', 'browse-bundle', 'browser', 'cso', 'helpers', 'runtime', 'pins', 'state-root', 'privacy', 'cores', 'revision', 'wrapper']); const runtime = json.rows.find((x: { id: string }) => x.id === 'runtime'); expect(runtime.status).toBe('FAIL'); expect(runtime.code).toBe('RUNTIME_BELOW_MINIMUM');