test(server): exclude loader activity from config responsiveness (#8218)

* test(server): trace recurring config responsiveness failures

* test(server): keep loader messages out of config responsiveness probes

---------

Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
This commit is contained in:
paseo-bot[bot]
2026-10-29 21:02:58 +00:00
committed by GitHub
parent 1aef1ec662
commit 9503537041
2 changed files with 52 additions and 16 deletions
@@ -5,7 +5,8 @@ import { fileURLToPath } from "node:url";
import { writeFile, truncate, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { afterEach, beforeEach, describe, expect, it } from "vitest";
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
import { build } from "esbuild";
import { isPlatform } from "../test-utils/platform.js";
import {
readPaseoConfig,
@@ -20,21 +21,40 @@ import {
writePaseoConfigForEdit,
} from "./paseo-config-file.js";
let probeDirectory: string;
let probePath: string;
beforeAll(async () => {
probeDirectory = mkdtempSync(join(tmpdir(), "paseo-config-probe-"));
probePath = join(probeDirectory, "config-responsiveness.mjs");
// Compile before starting the measured process. A runtime TypeScript loader
// owns MessagePorts that the responsiveness probe otherwise charges as work.
await build({
entryPoints: [
fileURLToPath(new URL("./test-fixtures/config-responsiveness.ts", import.meta.url)),
],
outfile: probePath,
bundle: true,
platform: "node",
format: "esm",
});
});
afterAll(() => {
rmSync(probeDirectory, { recursive: true, force: true });
});
async function measureConfigRead(directory: string) {
const fixture = fileURLToPath(
new URL("./test-fixtures/config-responsiveness.ts", import.meta.url),
);
const { stdout, stderr } = await promisify(execFile)(
process.execPath,
["--import", "tsx", fixture, directory],
{ timeout: 15_000 },
);
const { stdout, stderr } = await promisify(execFile)(process.execPath, [probePath, directory], {
timeout: 15_000,
});
process.stderr.write(stderr);
return JSON.parse(stdout) as {
result: Awaited<ReturnType<typeof readPaseoConfigForEdit>>;
blockingMs: number;
filesystemRequests: number;
yields: number;
workerMessages: number;
};
}
@@ -84,7 +104,7 @@ describe("paseo config file substrate", () => {
);
});
it("excludes unrelated test-runner CPU from the config read budget", async () => {
it("excludes test-runner and loader activity from the config read budget", async () => {
await writeFile(join(tempDir, "paseo.json"), JSON.stringify({ worktree: { setup: "npm ci" } }));
const unrelatedWork = new Promise<void>((resolve) => {
setImmediate(() => {
@@ -100,8 +120,10 @@ describe("paseo config file substrate", () => {
resolve();
});
});
const { result, blockingMs, filesystemRequests } = await measureConfigRead(tempDir);
const { result, blockingMs, filesystemRequests, workerMessages } =
await measureConfigRead(tempDir);
await unrelatedWork;
expect(workerMessages).toBe(0);
expect(result).toEqual({
ok: true,
config: { worktree: { setup: "npm ci" } },
@@ -119,11 +141,13 @@ describe("paseo config file substrate", () => {
worktree: { setup: "npm ci" },
};
await writeFile(join(tempDir, "paseo.json"), JSON.stringify(config));
const { result, blockingMs, filesystemRequests, yields } = await measureConfigRead(tempDir);
const { result, blockingMs, filesystemRequests, yields, workerMessages } =
await measureConfigRead(tempDir);
expect(result).toEqual({ ok: true, config, revision: await statPaseoConfigPath(tempDir) });
expect(blockingMs).toBeLessThan(20);
expect(filesystemRequests).toBeGreaterThan(0);
expect(yields).toBeGreaterThan(0);
expect(workerMessages).toBe(0);
});
it("rejects a 32MiB config with main-thread work below 20ms", async () => {
@@ -131,9 +155,11 @@ describe("paseo config file substrate", () => {
join(tempDir, "paseo.json"),
JSON.stringify({ extra: "x".repeat(32 * 1024 * 1024) }),
);
const { result, blockingMs, filesystemRequests } = await measureConfigRead(tempDir);
const { result, blockingMs, filesystemRequests, workerMessages } =
await measureConfigRead(tempDir);
expect(blockingMs).toBeLessThan(20);
expect(filesystemRequests).toBeGreaterThan(0);
expect(workerMessages).toBe(0);
expect(result).toEqual({ ok: false, error: { code: "invalid_project_config" } });
});
@@ -1,14 +1,24 @@
import { createHook, executionAsyncResource } from "node:async_hooks";
import { MessagePort } from "node:worker_threads";
import assert from "node:assert/strict";
import { setImmediate as nextTurn } from "node:timers/promises";
import { measureMainThreadWork } from "../../test-utils/main-thread-work.js";
import { readPaseoConfigForEdit } from "../paseo-config-file.js";
// Keep Vitest transforms, reporting, and unrelated callbacks outside the measured
// thread. Read the real file once, without warming the config snapshot or schema.
// The parent bundles this fixture before launching plain Node, keeping test
// runner callbacks and runtime loader messages outside the measured thread.
// Read the real file once, without warming the config snapshot or schema.
const directory = process.argv[2];
assert.ok(directory, "Config directory is required");
await nextTurn();
let workerMessages = 0;
const activity = createHook({
before() {
if (executionAsyncResource() instanceof MessagePort) workerMessages++;
},
}).enable();
const measured = await measureMainThreadWork("project config main-thread work", () =>
readPaseoConfigForEdit(directory),
);
process.stdout.write(JSON.stringify(measured));
activity.disable();
process.stdout.write(JSON.stringify({ ...measured, workerMessages }));