fix(coding-runtime): close Pi extension lifecycle races

This commit is contained in:
2026-08-23 10:13:10 +08:00
parent 3861c3286a
commit 31de325cd8
9 changed files with 197 additions and 14 deletions

View File

@@ -5,6 +5,7 @@ import { tmpdir } from 'node:os';
import path from 'node:path';
import { afterEach, describe, expect, it } from 'vitest';
import { PiManagedExtensionHost } from '../../electron/coding-runtime/pi/extension-host';
import { PiProjectWriteLeaseCoordinator } from '../../electron/coding-runtime/pi/write-lease';
const roots: string[] = [];
const hosts: PiManagedExtensionHost[] = [];
@@ -29,6 +30,40 @@ async function post(
}
describe('managed Pi extension bridge', () => {
it('finishes a queued HTTP request while closing a holder and waiter', async () => {
const root = await mkdtemp(path.join(tmpdir(), 'makelore-pi-extension-close-'));
roots.push(root);
const leases = new PiProjectWriteLeaseCoordinator();
const host = new PiManagedExtensionHost(leases);
hosts.push(host);
const holder = await host.registerWorker({
conversationId: 'conversation-holder', generation: 1, projectId: 'project-a', extensionsDir: root,
});
const waiter = await host.registerWorker({
conversationId: 'conversation-waiter', generation: 1, projectId: 'project-a', extensionsDir: root,
});
await Promise.all([
host.bindRun('conversation-holder', 1, 'run-holder'),
host.bindRun('conversation-waiter', 1, 'run-waiter'),
]);
const held = await post(holder, {
action: 'lease.acquire', conversationId: 'conversation-holder', workerGeneration: 1,
runId: 'run-holder', resourceId: 'held-tool',
});
expect(held.status).toBe(200);
const waiting = post(waiter, {
action: 'lease.acquire', conversationId: 'conversation-waiter', workerGeneration: 1,
runId: 'run-waiter', resourceId: 'waiting-tool',
});
await expect.poll(() => leases.waitingCount('project-a')).toBe(1);
await expect(Promise.race([
host.close().then(() => 'closed'),
new Promise<string>((resolve) => setTimeout(() => resolve('timeout'), 500)),
])).resolves.toBe('closed');
await expect(waiting).resolves.toMatchObject({ status: 409 });
});
it('materializes the active run into a replacement generation before spawn', async () => {
const root = await mkdtemp(path.join(tmpdir(), 'makelore-pi-extension-rebuild-'));
roots.push(root);