46 lines
2.2 KiB
TypeScript
46 lines
2.2 KiB
TypeScript
|
|
import { describe, it, expect } from 'vitest';
|
||
|
|
import { readFileSync } from 'node:fs';
|
||
|
|
import { join } from 'node:path';
|
||
|
|
import { PI_EXTENSION_FILES, PI_EXTENSION_FILENAMES } from '../../src/config/pi-extension.js';
|
||
|
|
|
||
|
|
/**
|
||
|
|
* `mcpctl config pi` installs the *embedded* copy of the extension, not the
|
||
|
|
* files in src/pi-ext/. Editing the sources without re-running the generator
|
||
|
|
* therefore ships stale code to users while the repo looks correct — and the
|
||
|
|
* embedded copy is the one thing no typecheck covers. Same guarantee the
|
||
|
|
* completions check gives.
|
||
|
|
*/
|
||
|
|
const repoRoot = join(import.meta.dirname, '..', '..', '..', '..');
|
||
|
|
const piExtDir = join(repoRoot, 'src', 'pi-ext');
|
||
|
|
|
||
|
|
describe('embedded pi extension', () => {
|
||
|
|
it('matches the sources in src/pi-ext (re-run scripts/generate-pi-extension.ts)', () => {
|
||
|
|
for (const name of PI_EXTENSION_FILENAMES) {
|
||
|
|
const onDisk = readFileSync(join(piExtDir, name), 'utf-8');
|
||
|
|
expect(PI_EXTENSION_FILES[name], `${name} is embedded`).toBeDefined();
|
||
|
|
expect(PI_EXTENSION_FILES[name], `${name} is stale — regenerate the embed`).toBe(onDisk);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
it('embeds every file the extension needs to load', () => {
|
||
|
|
// mcpctl-pi.ts imports ./mcp-http.js — installing one without the other
|
||
|
|
// yields an extension pi cannot load.
|
||
|
|
expect(Object.keys(PI_EXTENSION_FILES).sort()).toEqual(['mcp-http.ts', 'mcpctl-pi.ts']);
|
||
|
|
expect(PI_EXTENSION_FILES['mcpctl-pi.ts']).toContain('./mcp-http.js');
|
||
|
|
});
|
||
|
|
|
||
|
|
it('carries the fixes the pi API requires', () => {
|
||
|
|
const main = PI_EXTENSION_FILES['mcpctl-pi.ts'] ?? '';
|
||
|
|
// ctx.ui.select takes string[] and returns the chosen string.
|
||
|
|
expect(main).not.toMatch(/select\([^)]*\[\s*\{\s*value:/);
|
||
|
|
// Skills must land in pi's tree, never ~/.claude: the sync passes
|
||
|
|
// --agent pi and no longer tells the user it wrote to Claude's tree.
|
||
|
|
expect(main).toContain('"--agent", "pi"');
|
||
|
|
expect(main).not.toContain('(into ~/.claude/skills)');
|
||
|
|
// Spawned without a shell, so the project name is never interpolated into
|
||
|
|
// a command string. Matches the call, not prose mentioning it.
|
||
|
|
expect(main).toContain('execFile("mcpctl", args');
|
||
|
|
expect(main).not.toMatch(/execSync\s*\(/);
|
||
|
|
});
|
||
|
|
});
|