test: share process platform spy helper

This commit is contained in:
Vincent Koc
2026-05-17 16:36:35 +08:00
parent 3c1c850c02
commit 46061442e7
8 changed files with 31 additions and 91 deletions

View File

@@ -4,6 +4,7 @@ import { afterEach, describe, expect, it, vi } from "vitest";
import { clearConfigCache, clearRuntimeConfigSnapshot } from "../config/config.js";
import { makeTempWorkspace } from "../test-helpers/workspace.js";
import { captureEnv } from "../test-utils/env.js";
import { mockProcessPlatform } from "../test-utils/vitest-spies.js";
import type { GatewayService } from "./service.js";
import {
describeGatewayServiceRestart,
@@ -14,24 +15,12 @@ import {
} from "./service.js";
import { createMockGatewayService } from "./service.test-helpers.js";
const originalPlatformDescriptor = Object.getOwnPropertyDescriptor(process, "platform");
function setPlatform(value: NodeJS.Platform | "aix") {
if (!originalPlatformDescriptor) {
throw new Error("missing process.platform descriptor");
}
Object.defineProperty(process, "platform", {
configurable: true,
enumerable: originalPlatformDescriptor.enumerable ?? false,
value,
});
function setPlatform(value: NodeJS.Platform) {
mockProcessPlatform(value);
}
afterEach(() => {
if (!originalPlatformDescriptor) {
return;
}
Object.defineProperty(process, "platform", originalPlatformDescriptor);
vi.restoreAllMocks();
});
function createService(overrides: Partial<GatewayService> = {}): GatewayService {

View File

@@ -1,4 +1,5 @@
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { mockProcessPlatform } from "../test-utils/vitest-spies.js";
const spawnSyncMock = vi.hoisted(() => vi.fn());
const readFileSyncMock = vi.hoisted(() => vi.fn());
@@ -63,13 +64,8 @@ const {
signalVerifiedGatewayPidSync,
} = await import("./gateway-processes.js");
const originalPlatformDescriptor = Object.getOwnPropertyDescriptor(process, "platform");
function setPlatform(platform: NodeJS.Platform): void {
Object.defineProperty(process, "platform", {
value: platform,
configurable: true,
});
mockProcessPlatform(platform);
}
describe("gateway-processes", () => {
@@ -84,9 +80,6 @@ describe("gateway-processes", () => {
afterEach(() => {
vi.restoreAllMocks();
if (originalPlatformDescriptor) {
Object.defineProperty(process, "platform", originalPlatformDescriptor);
}
});
it("reads linux process args from /proc and parses cmdlines", () => {

View File

@@ -1,4 +1,5 @@
import { afterEach, describe, expect, it } from "vitest";
import { afterEach, describe, expect, it, vi } from "vitest";
import { mockProcessPlatform } from "../test-utils/vitest-spies.js";
import {
hasNodeErrorCode,
isNodeError,
@@ -8,19 +9,12 @@ import {
normalizeWindowsPathForComparison,
} from "./path-guards.js";
const originalPlatformDescriptor = Object.getOwnPropertyDescriptor(process, "platform");
function setPlatform(platform: NodeJS.Platform): void {
Object.defineProperty(process, "platform", {
value: platform,
configurable: true,
});
mockProcessPlatform(platform);
}
afterEach(() => {
if (originalPlatformDescriptor) {
Object.defineProperty(process, "platform", originalPlatformDescriptor);
}
vi.restoreAllMocks();
});
describe("normalizeWindowsPathForComparison", () => {

View File

@@ -1,5 +1,6 @@
import net from "node:net";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import { mockProcessPlatform } from "../test-utils/vitest-spies.js";
import { stripAnsi } from "../terminal/ansi.js";
const runCommandWithTimeoutMock = vi.hoisted(() => vi.fn());
@@ -15,13 +16,9 @@ let handlePortError: typeof import("./ports.js").handlePortError;
let PortInUseError: typeof import("./ports.js").PortInUseError;
const describeUnix = process.platform === "win32" ? describe.skip : describe;
const originalPlatformDescriptor = Object.getOwnPropertyDescriptor(process, "platform");
function setPlatform(platform: NodeJS.Platform): void {
Object.defineProperty(process, "platform", {
value: platform,
configurable: true,
});
mockProcessPlatform(platform);
}
async function listenServer(
@@ -63,9 +60,7 @@ beforeEach(() => {
});
afterEach(() => {
if (originalPlatformDescriptor) {
Object.defineProperty(process, "platform", originalPlatformDescriptor);
}
vi.restoreAllMocks();
});
describe("ports helpers", () => {

View File

@@ -1,6 +1,7 @@
import fs from "node:fs";
import path from "node:path";
import { afterEach, describe, expect, it, vi } from "vitest";
import { mockProcessPlatform } from "../test-utils/vitest-spies.js";
import {
evaluateRuntimeEligibility,
evaluateRuntimeRequires,
@@ -11,15 +12,11 @@ import {
resolveRuntimePlatform,
} from "./config-eval.js";
const originalPlatformDescriptor = Object.getOwnPropertyDescriptor(process, "platform");
const originalPath = process.env.PATH;
const originalPathExt = process.env.PATHEXT;
function setPlatform(platform: NodeJS.Platform): void {
Object.defineProperty(process, "platform", {
value: platform,
configurable: true,
});
mockProcessPlatform(platform);
}
afterEach(() => {
@@ -30,9 +27,6 @@ afterEach(() => {
} else {
process.env.PATHEXT = originalPathExt;
}
if (originalPlatformDescriptor) {
Object.defineProperty(process, "platform", originalPlatformDescriptor);
}
});
describe("config-eval helpers", () => {

View File

@@ -1,23 +1,17 @@
import { afterEach, describe, expect, it } from "vitest";
import { afterEach, describe, expect, it, vi } from "vitest";
import { mockProcessPlatform } from "../test-utils/vitest-spies.js";
import {
evaluateEntryMetadataRequirements,
evaluateEntryMetadataRequirementsForCurrentPlatform,
evaluateEntryRequirementsForCurrentPlatform,
} from "./entry-status.js";
const originalPlatformDescriptor = Object.getOwnPropertyDescriptor(process, "platform");
function setPlatform(platform: NodeJS.Platform): void {
Object.defineProperty(process, "platform", {
value: platform,
configurable: true,
});
mockProcessPlatform(platform);
}
afterEach(() => {
if (originalPlatformDescriptor) {
Object.defineProperty(process, "platform", originalPlatformDescriptor);
}
vi.restoreAllMocks();
});
describe("shared/entry-status", () => {

View File

@@ -1,5 +1,6 @@
import fsSync from "node:fs";
import { afterEach, describe, expect, it, vi } from "vitest";
import { withMockedPlatform } from "../test-utils/vitest-spies.js";
import { getProcessStartTime, isPidAlive, isPidDefinitelyDead } from "./pid-alive.js";
afterEach(() => {
@@ -17,30 +18,6 @@ function mockProcReads(entries: Record<string, string>) {
});
}
async function withLinuxProcessPlatform<T>(run: () => Promise<T>): Promise<T> {
return withProcessPlatform("linux", run);
}
async function withProcessPlatform<T>(
platform: NodeJS.Platform,
run: () => Promise<T>,
): Promise<T> {
const originalPlatformDescriptor = Object.getOwnPropertyDescriptor(process, "platform");
if (!originalPlatformDescriptor) {
throw new Error("missing process.platform descriptor");
}
Object.defineProperty(process, "platform", {
...originalPlatformDescriptor,
value: platform,
});
try {
return await run();
} finally {
Object.defineProperty(process, "platform", originalPlatformDescriptor);
vi.restoreAllMocks();
}
}
describe("isPidAlive", () => {
it("returns true for the current running process", () => {
expect(isPidAlive(process.pid)).toBe(true);
@@ -64,7 +41,7 @@ describe("isPidAlive", () => {
mockProcReads({
[`/proc/${zombiePid}/status`]: `Name:\tnode\nUmask:\t0022\nState:\tZ (zombie)\nTgid:\t${zombiePid}\nPid:\t${zombiePid}\n`,
});
await withLinuxProcessPlatform(async () => {
await withMockedPlatform("linux", async () => {
expect(isPidAlive(zombiePid)).toBe(false);
});
});
@@ -75,7 +52,7 @@ describe("isPidAlive", () => {
});
const killSpy = vi.spyOn(process, "kill").mockImplementation(() => true);
await withLinuxProcessPlatform(async () => {
await withMockedPlatform("linux", async () => {
expect(isPidAlive(42)).toBe(true);
});
@@ -120,7 +97,7 @@ describe("isPidDefinitelyDead", () => {
[`/proc/${zombiePid}/status`]: `Name:\tnode\nUmask:\t0022\nState:\tZ (zombie)\nTgid:\t${zombiePid}\nPid:\t${zombiePid}\n`,
});
await withLinuxProcessPlatform(async () => {
await withMockedPlatform("linux", async () => {
expect(isPidDefinitelyDead(zombiePid)).toBe(true);
});
});
@@ -132,7 +109,7 @@ describe("isPidDefinitelyDead", () => {
[`/proc/${livePid}/status`]: `Name:\tnode\nUmask:\t0022\nState:\tS (sleeping)\nTgid:\t${livePid}\nPid:\t${livePid}\n`,
});
await withLinuxProcessPlatform(async () => {
await withMockedPlatform("linux", async () => {
expect(isPidDefinitelyDead(livePid)).toBe(false);
});
});
@@ -152,7 +129,7 @@ describe("getProcessStartTime", () => {
"/proc/46/stat": `${fakeStatPrefix}1.5${fakeStatSuffix}`,
});
await withLinuxProcessPlatform(async () => {
await withMockedPlatform("linux", async () => {
expect(getProcessStartTime(process.pid)).toBe(98765);
expect(getProcessStartTime(42)).toBe(55555);
expect(getProcessStartTime(43)).toBeNull();
@@ -163,7 +140,7 @@ describe("getProcessStartTime", () => {
});
it("returns null on non-Linux platforms", () => {
return withProcessPlatform("darwin", async () => {
return withMockedPlatform("darwin", async () => {
expect(getProcessStartTime(process.pid)).toBeNull();
});
});

View File

@@ -40,13 +40,17 @@ export function withRestoredMocks<T>(
}
}
export function mockProcessPlatform(platform: NodeJS.Platform): RestorableMock {
return vi.spyOn(process, "platform", "get").mockReturnValue(platform);
}
export function withMockedPlatform<T>(platform: NodeJS.Platform, run: () => Promise<T>): Promise<T>;
export function withMockedPlatform<T>(platform: NodeJS.Platform, run: () => T): T;
export function withMockedPlatform<T>(
platform: NodeJS.Platform,
run: () => T | Promise<T>,
): T | Promise<T> {
return withRestoredMocks([vi.spyOn(process, "platform", "get").mockReturnValue(platform)], run);
return withRestoredMocks([mockProcessPlatform(platform)], run);
}
export function withMockedWindowsPlatform<T>(run: () => Promise<T>): Promise<T>;