fix: harden runtime config helper protocol and lifecycle
This commit is contained in:
@@ -4,7 +4,7 @@ import { tmpdir } from "node:os";
|
||||
import { dirname, join } from "node:path";
|
||||
import { execFileSync } from "node:child_process";
|
||||
import { createHash } from "node:crypto";
|
||||
import { WorkspaceRuntimeConfigLeaseFactory } from "../src/workspaces/runtime-config-lease.js";
|
||||
import { runBoundedHelper, WorkspaceRuntimeConfigLeaseFactory } from "../src/workspaces/runtime-config-lease.js";
|
||||
import { parse } from "yaml";
|
||||
import { parseWorkspaceYaml, serializeWorkspaceYaml } from "../src/workspaces/schema.js";
|
||||
|
||||
@@ -72,6 +72,7 @@ function fixture(extraEnv: Record<string, string> = {}) {
|
||||
const factoryInput = {
|
||||
dataRoot, runtimeSnapshotRoot: snapshots, harnessDir: harness, configPath,
|
||||
env: {
|
||||
NODE_ENV: "test",
|
||||
THT_WS_ABC_DWH_TRANSPORT: "postgres_direct", THT_WS_ABC_DWH_HOST: "dwh",
|
||||
THT_WS_ABC_DWH_PORT: "5432", THT_WS_ABC_DWH_USER: "reader",
|
||||
THT_WS_ABC_DWH_PASSWORD_FILE: secret, ...extraEnv,
|
||||
@@ -258,10 +259,12 @@ test("runtime config symlink replacement is refused", async () => {
|
||||
function realHarnessBinding(config: string): Record<string, string> {
|
||||
const helper = join(process.cwd(), "..", "harness", "tht", "runtime_config_lease_io.py");
|
||||
const python = join(process.cwd(), "..", "harness", ".venv", "bin", "python");
|
||||
return JSON.parse(execFileSync(python, [helper], {
|
||||
const result = JSON.parse(execFileSync(python, [helper], {
|
||||
cwd: join(process.cwd(), "..", "harness"), encoding: "utf8",
|
||||
input: JSON.stringify({ protocol_version: 1, action: "binding", config_hex: Buffer.from(config).toString("hex") }),
|
||||
}));
|
||||
const { protocol_version: _protocol, kind: _kind, ...binding } = result;
|
||||
return binding;
|
||||
}
|
||||
|
||||
test("explicit installation overlay is canonical and has one real harness binding", async () => {
|
||||
@@ -609,3 +612,18 @@ else:
|
||||
expect(output.trim()).toBe("rejected");
|
||||
} finally { rmSync(f.root, { recursive: true, force: true }); }
|
||||
});
|
||||
|
||||
|
||||
test("bounded helper settles early stdin close and a never-reading child without crashing", async () => {
|
||||
const options = (action: string, payload: string, timeoutMs = 200) => ({
|
||||
cwd: tmpdir(), env: process.env, action, payload, timeoutMs,
|
||||
});
|
||||
await expect(runBoundedHelper(process.execPath, ["-e", "process.stdin.destroy(); setTimeout(() => {}, 1000)"],
|
||||
options("early-close", "x".repeat(16 * 1024 * 1024)))).rejects.toThrow();
|
||||
const started = Date.now();
|
||||
const timer = new Promise<void>((resolve) => setTimeout(resolve, 20));
|
||||
await expect(runBoundedHelper(process.execPath, ["-e", "setTimeout(() => {}, 10000)"],
|
||||
options("never-read", "x".repeat(16 * 1024 * 1024), 80))).rejects.toThrow(/timed out|pipe|closed/i);
|
||||
await timer;
|
||||
expect(Date.now() - started).toBeLessThan(2_000);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user