070959e133
landing-page-staging / Deploy landing page to staging (push) Has been skipped
landing-page-ci / Validate landing page (push) Failing after 4s
visual-baseline / Capture visual baselines (push) Has been cancelled
bake-plugin-previews / Bake plugin previews (push) Has been cancelled
52 lines
1.8 KiB
TypeScript
52 lines
1.8 KiB
TypeScript
import fs from 'node:fs';
|
|
|
|
import { afterEach, describe, expect, it } from 'vitest';
|
|
|
|
import {
|
|
createTerminalService,
|
|
ensureSpawnHelperExecutable,
|
|
spawnHelperCandidatePaths,
|
|
} from '../src/terminals.js';
|
|
|
|
/**
|
|
* Regression for the "无法启动终端会话" / "Could not start the terminal session"
|
|
* report: pnpm unpacks node-pty's prebuilt `spawn-helper` without the execute
|
|
* bit, so the first `pty.spawn()` dies with "posix_spawnp failed." and the
|
|
* launcher toast fires. `terminals.create()` must self-heal that bit so a fresh
|
|
* PTY actually spawns.
|
|
*
|
|
* macOS/Linux only — on win32 ConPTY has no spawn-helper and there is nothing to
|
|
* repair (`spawnHelperCandidatePaths()` is empty there).
|
|
*/
|
|
const helperPath = spawnHelperCandidatePaths().find((p) => fs.existsSync(p)) ?? null;
|
|
const describeIfHelper = helperPath ? describe : describe.skip;
|
|
|
|
describeIfHelper('terminal spawn-helper executable repair', () => {
|
|
afterEach(() => {
|
|
// Leave the shared node_modules artifact executable regardless of outcome,
|
|
// so a failure here can't cascade into every other terminal test.
|
|
if (helperPath) ensureSpawnHelperExecutable();
|
|
});
|
|
|
|
it('spawns a PTY even when the prebuilt spawn-helper lost its +x bit', async () => {
|
|
// Reproduce the post-`pnpm install` state: helper present but mode 0644.
|
|
fs.chmodSync(helperPath!, 0o644);
|
|
expect(fs.statSync(helperPath!).mode & 0o111).toBe(0);
|
|
|
|
const terminals = createTerminalService();
|
|
const session = await terminals.create({
|
|
projectId: 'p-spawn-helper',
|
|
cwd: process.cwd(),
|
|
});
|
|
|
|
try {
|
|
expect(session.status).toBe('running');
|
|
expect(typeof session.pty.pid).toBe('number');
|
|
// The repair ran as part of create() → loadPty().
|
|
expect(fs.statSync(helperPath!).mode & 0o100).not.toBe(0);
|
|
} finally {
|
|
terminals.kill(session, 'SIGTERM');
|
|
}
|
|
});
|
|
});
|