From 9503537041002d63d9078dced49257c8d8a903d2 Mon Sep 17 00:00:00 2001 From: "paseo-bot[bot]" <266920839+paseo-bot[bot]@users.noreply.github.com> Date: Thu, 29 Oct 2026 21:02:58 +0000 Subject: [PATCH] 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> --- .../src/utils/paseo-config-file.test.ts | 52 ++++++++++++++----- .../test-fixtures/config-responsiveness.ts | 16 ++++-- 2 files changed, 52 insertions(+), 16 deletions(-) diff --git a/packages/server/src/utils/paseo-config-file.test.ts b/packages/server/src/utils/paseo-config-file.test.ts index 186a5259ed..6a7e43c757 100644 --- a/packages/server/src/utils/paseo-config-file.test.ts +++ b/packages/server/src/utils/paseo-config-file.test.ts @@ -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>; 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((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" } }); }); diff --git a/packages/server/src/utils/test-fixtures/config-responsiveness.ts b/packages/server/src/utils/test-fixtures/config-responsiveness.ts index 656c5a2864..9e151c805a 100644 --- a/packages/server/src/utils/test-fixtures/config-responsiveness.ts +++ b/packages/server/src/utils/test-fixtures/config-responsiveness.ts @@ -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 }));