fix(coding-agent): resolve captured commands on close closes #3027
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
import { EventEmitter } from "node:events";
|
||||
import { mkdirSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join, relative } from "node:path";
|
||||
import { PassThrough } from "node:stream";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { DefaultPackageManager, type ProgressEvent, type ResolvedResource } from "../src/core/package-manager.js";
|
||||
import { SettingsManager } from "../src/core/settings-manager.js";
|
||||
@@ -13,6 +15,16 @@ function pathEndsWith(actualPath: string, suffix: string): boolean {
|
||||
return normalizeForMatch(actualPath).endsWith(normalizeForMatch(suffix));
|
||||
}
|
||||
|
||||
class MockSpawnedProcess extends EventEmitter {
|
||||
stdout = new PassThrough();
|
||||
stderr = new PassThrough();
|
||||
|
||||
kill(): boolean {
|
||||
this.emit("close", null, "SIGTERM");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper to check if a resource is enabled
|
||||
const isEnabled = (r: ResolvedResource, pathMatch: string, matchFn: "endsWith" | "includes" = "endsWith") => {
|
||||
const normalizedPath = normalizeForMatch(r.path);
|
||||
@@ -1553,5 +1565,38 @@ export default function(api) { api.registerTool({ name: "test", description: "te
|
||||
expect.objectContaining({ cwd: tempDir }),
|
||||
);
|
||||
});
|
||||
|
||||
it("should wait for close before resolving captured stdout", async () => {
|
||||
const managerWithInternals = packageManager as unknown as {
|
||||
spawnCaptureCommand(
|
||||
command: string,
|
||||
args: string[],
|
||||
options?: { cwd?: string; env?: Record<string, string> },
|
||||
): MockSpawnedProcess;
|
||||
runCommandCapture(
|
||||
command: string,
|
||||
args: string[],
|
||||
options?: { cwd?: string; timeoutMs?: number; env?: Record<string, string> },
|
||||
): Promise<string>;
|
||||
};
|
||||
const child = new MockSpawnedProcess();
|
||||
vi.spyOn(managerWithInternals, "spawnCaptureCommand").mockReturnValue(child);
|
||||
|
||||
let settled = false;
|
||||
const capturePromise = managerWithInternals.runCommandCapture("git", ["rev-parse", "HEAD"]).then((value) => {
|
||||
settled = true;
|
||||
return value;
|
||||
});
|
||||
|
||||
child.emit("exit", 0, null);
|
||||
await Promise.resolve();
|
||||
expect(settled).toBe(false);
|
||||
|
||||
child.stdout.write("abc123\n");
|
||||
child.stdout.end();
|
||||
child.emit("close", 0, null);
|
||||
|
||||
await expect(capturePromise).resolves.toBe("abc123");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user