diff --git a/src/server/terminals/terminalService.test.ts b/src/server/terminals/terminalService.test.ts index 3b314cf..ca3aded 100644 --- a/src/server/terminals/terminalService.test.ts +++ b/src/server/terminals/terminalService.test.ts @@ -1,4 +1,4 @@ -import { describe, expect, it } from "vitest"; +import { afterEach, beforeEach, describe, expect, it } from "vitest"; import type { RealtimeEvent, TerminalInfo } from "../../shared/apiTypes.js"; import type { WorkspaceActivityService } from "../activity/workspaceActivityService.js"; import { SessionEventHub } from "../realtime/sessionEventHub.js"; @@ -22,22 +22,71 @@ describe.skipIf(process.platform === "win32")("TerminalService command runs", () } }); - it("sets IS_PIWEB for terminal commands", async () => { - const service = new TerminalService(); - try { - const run = service.runCommand({ - origin: "core", - projectId: "p1", - workspaceId: "w1", - cwd: process.cwd(), - title: "Environment check", - command: "printf '%s' \"$IS_PIWEB\"", - }); + describe("IS_PIWEB propagation", () => { + let originalIsPiWeb: string | undefined; - expect(await terminalExit(service, run.terminalId)).toContain("1"); - } finally { - service.dispose(); - } + beforeEach(() => { + originalIsPiWeb = process.env["IS_PIWEB"]; + process.env["IS_PIWEB"] = "conflicting-parent-value"; + }); + + afterEach(() => { + if (originalIsPiWeb === undefined) { + delete process.env["IS_PIWEB"]; + } else { + process.env["IS_PIWEB"] = originalIsPiWeb; + } + }); + + it("sets IS_PIWEB for terminal commands", async () => { + const service = new TerminalService(); + try { + const frame = "__PI_WEB_RUN_ENV_7F3A9C__"; + const run = service.runCommand({ + origin: "core", + projectId: "p1", + workspaceId: "w1", + cwd: process.cwd(), + title: "Environment check", + command: `printf '${frame}%s${frame}\\n' "$IS_PIWEB"`, + }); + + expect(await terminalExit(service, run.terminalId)).toContain(`${frame}1${frame}`); + } finally { + service.dispose(); + } + }); + + it("sets IS_PIWEB in a continued interactive shell", async () => { + const service = new TerminalService(); + try { + const run = service.runCommand({ + origin: "core", + projectId: "p1", + workspaceId: "w1", + cwd: process.cwd(), + title: "Done command", + command: "true", + }); + await terminalExit(service, run.terminalId); + + const continued = service.continue(run.terminalId); + + expect(continued).toMatchObject({ id: run.terminalId, exited: false }); + expect(continued.commandRunId).toBeUndefined(); + expect(service.get(run.terminalId)?.commandRunId).toBeUndefined(); + + const frame = "__PI_WEB_CONTINUE_ENV_42D8B1__"; + const exit = terminalExit(service, run.terminalId); + service.write(run.terminalId, `printf '${frame}%s${frame}\\n' "$IS_PIWEB"\nexit\n`); + + const output = await exit; + expect(output).toContain("[continued in interactive shell]"); + expect(output).toContain(`${frame}1${frame}`); + } finally { + service.dispose(); + } + }); }); it("tracks dedicated terminal command runs through completion", async () => { @@ -68,30 +117,6 @@ describe.skipIf(process.platform === "win32")("TerminalService command runs", () } }); - it("continues an exited command-run terminal as an interactive shell", async () => { - const service = new TerminalService(); - try { - const run = service.runCommand({ - origin: "core", - projectId: "p1", - workspaceId: "w1", - cwd: process.cwd(), - title: "Done command", - command: "true", - }); - await terminalExit(service, run.terminalId); - - const continued = service.continue(run.terminalId); - - expect(continued).toMatchObject({ id: run.terminalId, exited: false }); - expect(continued.commandRunId).toBeUndefined(); - expect(service.get(run.terminalId)?.commandRunId).toBeUndefined(); - expect(await terminalReplay(service, run.terminalId)).toContain("[continued in interactive shell]"); - } finally { - service.dispose(); - } - }); - it("marks failed command runs when the command exits non-zero", async () => { const service = new TerminalService(); try { @@ -198,16 +223,6 @@ function requireTerminal(service: TerminalService, terminalId: string): Terminal return terminal; } -function terminalReplay(service: TerminalService, terminalId: string): Promise { - let output = ""; - const detach = service.attach(terminalId, { - output: (data) => { output += data; }, - exit: () => undefined, - }); - detach(); - return Promise.resolve(output); -} - function terminalExit(service: TerminalService, terminalId: string): Promise { const output: string[] = []; return new Promise((resolve, reject) => {