Files
makelore/tests/unit/pi-worker-process-real.test.ts

126 lines
4.6 KiB
TypeScript

// @vitest-environment node
import { afterEach, describe, expect, it } from 'vitest';
import { realpathSync } from 'node:fs';
import { mkdir, mkdtemp, readFile, rm } from 'node:fs/promises';
import { createRequire } from 'node:module';
import { tmpdir } from 'node:os';
import { join, resolve } from 'node:path';
import { PiWorkerProcess } from '../../electron/coding-runtime/pi/worker-process';
import { PiManagedExtensionHost } from '../../electron/coding-runtime/pi/extension-host';
const scratchRoots: string[] = [];
afterEach(async () => {
await Promise.all(scratchRoots.splice(0).map((root) => rm(root, {
recursive: true,
force: true,
maxRetries: 3,
})));
});
describe('locked Pi worker process smoke', () => {
it('loads the real Pi 0.84.2 entry through Electron Node and exits by closing stdin', async () => {
const requireFromProject = createRequire(resolve('package.json'));
const electronExecutable = requireFromProject('electron') as string;
const packageRoot = realpathSync(resolve(
'node_modules',
'@earendil-works',
'pi-coding-agent',
));
const packageJson = JSON.parse(await readFile(join(packageRoot, 'package.json'), 'utf8')) as {
version: string;
};
expect(packageJson.version).toBe('0.84.2');
const root = await mkdtemp(join(tmpdir(), 'makelore-pi-real-worker-'));
scratchRoots.push(root);
const configDir = join(root, 'config');
const sessionDir = join(root, 'sessions');
const cwd = join(root, 'project');
await Promise.all([mkdir(configDir), mkdir(sessionDir), mkdir(cwd)]);
const extensionHost = new PiManagedExtensionHost();
const extension = await extensionHost.registerWorker({
conversationId: 'real-conversation',
generation: 1,
projectId: 'real-project',
extensionsDir: join(root, 'extensions'),
});
await extensionHost.bindRun('real-conversation', 1, 'real-run');
const worker = await new PiWorkerProcess({
executablePath: electronExecutable,
cliPath: join(packageRoot, 'dist', 'cli.js'),
cwd,
configDir,
sessionDir,
additionalArgs: ['--extension', extension.extensionPath],
env: extension.env,
sensitiveValues: extension.sensitiveValues,
commandTimeoutMs: 5_000,
}).start();
try {
await expect(worker.request({ type: 'get_state' })).resolves.toMatchObject({
type: 'response',
command: 'get_state',
success: true,
});
expect(worker.stderrDiagnostic).not.toContain('Failed to load extension');
await expect(worker.stop()).resolves.toMatchObject({ mode: 'stdin-close', code: 0 });
} finally {
await worker.stop().catch(() => undefined);
await extension.dispose();
await extensionHost.close();
}
}, 15_000);
it('starts a real ephemeral read-only child with the child extension role', async () => {
const requireFromProject = createRequire(resolve('package.json'));
const electronExecutable = requireFromProject('electron') as string;
const packageRoot = realpathSync(resolve(
'node_modules',
'@earendil-works',
'pi-coding-agent',
));
const root = await mkdtemp(join(tmpdir(), 'makelore-pi-real-child-'));
scratchRoots.push(root);
const configDir = join(root, 'config');
const sessionDir = join(root, 'sessions');
const cwd = join(root, 'project');
await Promise.all([mkdir(configDir), mkdir(sessionDir), mkdir(cwd)]);
const extensionHost = new PiManagedExtensionHost();
const extension = await extensionHost.registerWorker({
conversationId: 'real-parent',
generation: 1,
projectId: 'real-project',
extensionsDir: join(root, 'extensions'),
role: 'child',
runId: 'real-parent-run',
});
const worker = await new PiWorkerProcess({
executablePath: electronExecutable,
cliPath: join(packageRoot, 'dist', 'cli.js'),
cwd,
configDir,
sessionDir,
tools: ['read', 'grep', 'find', 'ls'],
additionalArgs: ['--extension', extension.extensionPath, '--no-session'],
env: extension.env,
sensitiveValues: extension.sensitiveValues,
commandTimeoutMs: 5_000,
}).start();
try {
await expect(worker.request({ type: 'get_state' })).resolves.toMatchObject({
type: 'response', command: 'get_state', success: true,
});
expect(worker.stderrDiagnostic).not.toContain('Failed to load extension');
await expect(worker.stop()).resolves.toMatchObject({ mode: 'stdin-close', code: 0 });
} finally {
await worker.stop().catch(() => undefined);
await extension.dispose();
await extensionHost.close();
}
}, 15_000);
});