fix(tui): prevented test env from suppressing interactive launch
Scoped test-runtime detection to explicit runner markers and Bun test entrypoints, so application NODE_ENV/BUN_ENV values no longer make ProcessTerminal headless. Added subprocess regression coverage and propagated the private marker to test children. Fixes #7261
This commit is contained in:
@@ -2,6 +2,10 @@
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed interactive launches hanging silently when a host project or its `.env` sets `NODE_ENV=test` or `BUN_ENV=test` ([#7261](https://github.com/can1357/oh-my-pi/issues/7261)).
|
||||
|
||||
## [17.2.3] - 2026-08-01
|
||||
|
||||
### Changed
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed interactive terminals suppressing all output and input when the host project sets `NODE_ENV=test` or `BUN_ENV=test` ([#7261](https://github.com/can1357/oh-my-pi/issues/7261)).
|
||||
|
||||
## [17.2.2] - 2026-07-31
|
||||
|
||||
### Added
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed Bun test-runtime detection treating application-owned `NODE_ENV=test` and `BUN_ENV=test` values as test-runner signals ([#7261](https://github.com/can1357/oh-my-pi/issues/7261)).
|
||||
|
||||
## [17.2.1] - 2026-07-30
|
||||
|
||||
### Added
|
||||
|
||||
@@ -258,9 +258,13 @@ export function $envpos(name: string, defaultValue: number): number {
|
||||
return parsed;
|
||||
}
|
||||
|
||||
/** True when `BUN_ENV` or `NODE_ENV` is the string `test`. */
|
||||
const BUN_TEST_ENTRY_PATTERN = /\.(?:test|spec)\.[cm]?[jt]sx?$/;
|
||||
|
||||
/** True when the process is an explicitly marked test child or Bun is running a test entrypoint. */
|
||||
export function isBunTestRuntime(): boolean {
|
||||
return Bun.env.BUN_ENV === "test" || Bun.env.NODE_ENV === "test";
|
||||
if (Bun.env.PI_TEST_RUNTIME === "1") return true;
|
||||
const hasTestEnvironment = Bun.env.BUN_ENV === "test" || Bun.env.NODE_ENV === "test";
|
||||
return hasTestEnvironment && BUN_TEST_ENTRY_PATTERN.test(Bun.main);
|
||||
}
|
||||
|
||||
let terminalHeadless = isBunTestRuntime();
|
||||
|
||||
@@ -5,6 +5,7 @@ import * as path from "node:path";
|
||||
import { filterProcessEnv, parseEnvFile } from "@oh-my-pi/pi-utils/env";
|
||||
|
||||
const tempDirs: string[] = [];
|
||||
const runtimeProbePath = path.join(import.meta.dir, "fixtures", "test-runtime-probe.ts");
|
||||
|
||||
afterEach(() => {
|
||||
for (const dir of tempDirs.splice(0)) {
|
||||
@@ -20,6 +21,23 @@ function writeTempEnv(content: string): string {
|
||||
return filePath;
|
||||
}
|
||||
|
||||
async function runRuntimeProbe(env: Record<string, string | undefined>): Promise<boolean> {
|
||||
const cwd = path.dirname(writeTempEnv(""));
|
||||
const proc = Bun.spawn([process.execPath, runtimeProbePath], {
|
||||
cwd,
|
||||
env: { ...process.env, ...env },
|
||||
stdout: "pipe",
|
||||
stderr: "pipe",
|
||||
});
|
||||
const [stdout, stderr, exitCode] = await Promise.all([
|
||||
new Response(proc.stdout).text(),
|
||||
new Response(proc.stderr).text(),
|
||||
proc.exited,
|
||||
]);
|
||||
expect(exitCode, stderr).toBe(0);
|
||||
return JSON.parse(stdout) as boolean;
|
||||
}
|
||||
|
||||
describe("parseEnvFile", () => {
|
||||
it("ignores malformed names and nul-containing values", () => {
|
||||
const filePath = writeTempEnv(
|
||||
@@ -121,3 +139,14 @@ describe("filterProcessEnv", () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("isBunTestRuntime", () => {
|
||||
it("does not treat shared application env names as a test runner signal", async () => {
|
||||
expect(await runRuntimeProbe({ NODE_ENV: "test", BUN_ENV: undefined, PI_TEST_RUNTIME: undefined })).toBe(false);
|
||||
expect(await runRuntimeProbe({ NODE_ENV: undefined, BUN_ENV: "test", PI_TEST_RUNTIME: undefined })).toBe(false);
|
||||
});
|
||||
|
||||
it("honors the private test runner signal", async () => {
|
||||
expect(await runRuntimeProbe({ NODE_ENV: undefined, BUN_ENV: undefined, PI_TEST_RUNTIME: "1" })).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
import { isBunTestRuntime } from "@oh-my-pi/pi-utils/env";
|
||||
|
||||
process.stdout.write(JSON.stringify(isBunTestRuntime()));
|
||||
@@ -467,9 +467,9 @@ async function runTestCommand(testCommand: TestCommand): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
// Child env shared by every spawned test process: the parent env with all CI
|
||||
// credential / cloud-config variables scrubbed (see SCRUBBED_ENV_* above) and
|
||||
// GITHUB_ACTIONS cleared so suites resolve only against their own fixtures.
|
||||
// Child env shared by every spawned test process: the parent env with the
|
||||
// private test-runtime marker set, all CI credential / cloud-config variables
|
||||
// scrubbed (see SCRUBBED_ENV_* above), and GITHUB_ACTIONS cleared.
|
||||
//
|
||||
// GC knobs (both needed — they gate different JSC mechanisms):
|
||||
// - `BUN_JSC_useConcurrentGC=0` stops the collector from marking concurrently
|
||||
@@ -493,6 +493,7 @@ function buildChildEnv(): Record<string, string | undefined> {
|
||||
const env: Record<string, string | undefined> = {
|
||||
...Bun.env,
|
||||
GITHUB_ACTIONS: "",
|
||||
PI_TEST_RUNTIME: "1",
|
||||
BUN_JSC_useConcurrentGC: "0",
|
||||
BUN_JSC_numberOfGCMarkers: "1",
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user