187 lines Β· 5.3 KB Β· TypeScript
RawDownload
import { describe, expect, it } from "vite-plus/test"; import { EnvironmentId, TerminalSessionSnapshot, ThreadId } from "@t3tools/contracts"; import {  applyTerminalAttachStreamEvent,  applyTerminalMetadataStreamEvent,  combineTerminalSessionState,  EMPTY_TERMINAL_BUFFER_STATE,  selectRunningSubprocessTerminalIds,} from "./terminalSession.ts"; const TARGET = {  environmentId: EnvironmentId.make("env-local"),  threadId: ThreadId.make("thread-1"),  terminalId: "term-1",} as const; const BASE_SNAPSHOT: TerminalSessionSnapshot = {  threadId: TARGET.threadId,  terminalId: TARGET.terminalId,  cwd: "/repo",  worktreePath: null,  status: "running",  pid: 123,  history: "hello",  exitCode: null,  exitSignal: null,  label: "Terminal 1",  updatedAt: "2026-04-01T00:00:00.000Z",}; describe("terminal session reducers", () => {  it("prefers live attach status over stale metadata after the attach stream starts", () => {    const summary = applyTerminalMetadataStreamEvent([], {      type: "snapshot",      terminals: [        {          threadId: BASE_SNAPSHOT.threadId,          terminalId: BASE_SNAPSHOT.terminalId,          cwd: BASE_SNAPSHOT.cwd,          worktreePath: BASE_SNAPSHOT.worktreePath,          status: "running",          pid: BASE_SNAPSHOT.pid,          exitCode: BASE_SNAPSHOT.exitCode,          exitSignal: BASE_SNAPSHOT.exitSignal,          updatedAt: BASE_SNAPSHOT.updatedAt,          hasRunningSubprocess: false,          label: BASE_SNAPSHOT.label,        },      ],    })[0]!;    const attached = applyTerminalAttachStreamEvent(EMPTY_TERMINAL_BUFFER_STATE, {      type: "error",      threadId: TARGET.threadId,      terminalId: TARGET.terminalId,      message: "Terminal disconnected.",    });     expect(combineTerminalSessionState(summary, attached)).toMatchObject({      status: "error",      error: "Terminal disconnected.",      version: 1,    });  });   it("uses metadata status before an attach stream has emitted", () => {    const summary = applyTerminalMetadataStreamEvent([], {      type: "snapshot",      terminals: [        {          threadId: BASE_SNAPSHOT.threadId,          terminalId: BASE_SNAPSHOT.terminalId,          cwd: BASE_SNAPSHOT.cwd,          worktreePath: BASE_SNAPSHOT.worktreePath,          status: "running",          pid: BASE_SNAPSHOT.pid,          exitCode: BASE_SNAPSHOT.exitCode,          exitSignal: BASE_SNAPSHOT.exitSignal,          updatedAt: BASE_SNAPSHOT.updatedAt,          hasRunningSubprocess: false,          label: BASE_SNAPSHOT.label,        },      ],    })[0]!;     expect(combineTerminalSessionState(summary, EMPTY_TERMINAL_BUFFER_STATE).status).toBe(      "running",    );  });   it("does not treat an idle running shell as a running subprocess", () => {    const idleSession = {      target: TARGET,      state: {        ...combineTerminalSessionState(null, EMPTY_TERMINAL_BUFFER_STATE),        status: "running" as const,        hasRunningSubprocess: false,      },    };    const activeSession = {      target: { ...TARGET, terminalId: "term-2" },      state: {        ...idleSession.state,        hasRunningSubprocess: true,      },    };     expect(selectRunningSubprocessTerminalIds([idleSession, activeSession])).toEqual(["term-2"]);  });   it("reduces attach snapshots and output without an imperative session manager", () => {    const snapshot = applyTerminalAttachStreamEvent(EMPTY_TERMINAL_BUFFER_STATE, {      type: "snapshot",      snapshot: BASE_SNAPSHOT,    });    const output = applyTerminalAttachStreamEvent(      snapshot,      {        type: "output",        threadId: TARGET.threadId,        terminalId: TARGET.terminalId,        data: " world",      },      8,    );     expect(output).toMatchObject({      buffer: "lo world",      status: "running",      error: null,      version: 2,    });  });   it("reduces terminal metadata snapshots, upserts, and removals", () => {    const initial = applyTerminalMetadataStreamEvent([], {      type: "snapshot",      terminals: [        {          threadId: BASE_SNAPSHOT.threadId,          terminalId: BASE_SNAPSHOT.terminalId,          cwd: BASE_SNAPSHOT.cwd,          worktreePath: BASE_SNAPSHOT.worktreePath,          status: BASE_SNAPSHOT.status,          pid: BASE_SNAPSHOT.pid,          exitCode: BASE_SNAPSHOT.exitCode,          exitSignal: BASE_SNAPSHOT.exitSignal,          updatedAt: BASE_SNAPSHOT.updatedAt,          hasRunningSubprocess: false,          label: BASE_SNAPSHOT.label,        },      ],    });    const updated = applyTerminalMetadataStreamEvent(initial, {      type: "upsert",      terminal: {        ...initial[0]!,        hasRunningSubprocess: true,      },    });    const removed = applyTerminalMetadataStreamEvent(updated, {      type: "remove",      threadId: TARGET.threadId,      terminalId: TARGET.terminalId,    });     expect(updated).toHaveLength(1);    expect(updated[0]?.hasRunningSubprocess).toBe(true);    expect(removed).toEqual([]);  });   it("caps retained output by UTF-8 byte length", () => {    const state = applyTerminalAttachStreamEvent(      EMPTY_TERMINAL_BUFFER_STATE,      {        type: "output",        threadId: TARGET.threadId,        terminalId: TARGET.terminalId,        data: "πŸ™‚πŸ™‚",      },      4,    );     expect(state.buffer).toBe("πŸ™‚");  });});