import * as Schema from "effect/Schema"; import { describe, expect, it } from "vite-plus/test"; import { DEFAULT_TERMINAL_ID, TerminalAttachInput, TerminalClearInput, TerminalCloseInput, TerminalEvent, TerminalOpenInput, TerminalResizeInput, TerminalSessionSnapshot, TerminalThreadInput, TerminalWriteInput, } from "./terminal.ts"; function decodeSync(schema: S, input: unknown): Schema.Schema.Type { return Schema.decodeUnknownSync(schema as never)(input) as Schema.Schema.Type; } function decodes(schema: S, input: unknown): boolean { try { Schema.decodeUnknownSync(schema as never)(input); return true; } catch { return false; } } describe("TerminalOpenInput", () => { it("accepts valid open input", () => { expect( decodes(TerminalOpenInput, { threadId: "thread-1", terminalId: DEFAULT_TERMINAL_ID, cwd: "/tmp/project", cols: 120, rows: 40, }), ).toBe(true); }); it("accepts ultrawide terminal dimensions from xterm fit", () => { expect( decodes(TerminalOpenInput, { threadId: "thread-1", terminalId: DEFAULT_TERMINAL_ID, cwd: "/tmp/project", cols: 423, rows: 40, }), ).toBe(true); }); it("rejects invalid bounds", () => { expect( decodes(TerminalOpenInput, { threadId: "thread-1", terminalId: DEFAULT_TERMINAL_ID, cwd: "/tmp/project", cols: 10, rows: 0, }), ).toBe(false); }); it("requires terminalId — the client must always pick an id", () => { expect( decodes(TerminalOpenInput, { threadId: "thread-1", cwd: "/tmp/project", cols: 100, rows: 24, }), ).toBe(false); }); it("accepts optional env overrides", () => { const parsed = decodeSync(TerminalOpenInput, { threadId: "thread-1", terminalId: DEFAULT_TERMINAL_ID, cwd: "/tmp/project", worktreePath: "/tmp/project/.t3/worktrees/feature-a", cols: 100, rows: 24, env: { T3CODE_PROJECT_ROOT: "/tmp/project", CUSTOM_FLAG: "1", }, }); expect(parsed.env).toMatchObject({ T3CODE_PROJECT_ROOT: "/tmp/project", CUSTOM_FLAG: "1", }); expect(parsed.worktreePath).toBe("/tmp/project/.t3/worktrees/feature-a"); }); it("rejects invalid env keys", () => { expect( decodes(TerminalOpenInput, { threadId: "thread-1", cwd: "/tmp/project", cols: 100, rows: 24, env: { "bad-key": "1", }, }), ).toBe(false); }); }); describe("TerminalAttachInput", () => { it("accepts explicit inactive-session restart intent", () => { const parsed = decodeSync(TerminalAttachInput, { threadId: "thread-1", terminalId: DEFAULT_TERMINAL_ID, cwd: "/tmp/project", restartIfNotRunning: true, }); expect(parsed.restartIfNotRunning).toBe(true); }); }); describe("TerminalWriteInput", () => { it("accepts non-empty data", () => { expect( decodes(TerminalWriteInput, { threadId: "thread-1", terminalId: DEFAULT_TERMINAL_ID, data: "echo hello\n", }), ).toBe(true); }); it("rejects empty data", () => { expect( decodes(TerminalWriteInput, { threadId: "thread-1", terminalId: DEFAULT_TERMINAL_ID, data: "", }), ).toBe(false); }); it("rejects missing terminalId", () => { expect( decodes(TerminalWriteInput, { threadId: "thread-1", data: "echo hello\n", }), ).toBe(false); }); }); describe("TerminalThreadInput", () => { it("trims thread ids", () => { const parsed = decodeSync(TerminalThreadInput, { threadId: " thread-1 " }); expect(parsed.threadId).toBe("thread-1"); }); }); describe("TerminalResizeInput", () => { it("accepts valid size", () => { expect( decodes(TerminalResizeInput, { threadId: "thread-1", terminalId: DEFAULT_TERMINAL_ID, cols: 80, rows: 24, }), ).toBe(true); }); it("rejects missing terminalId", () => { expect( decodes(TerminalResizeInput, { threadId: "thread-1", cols: 80, rows: 24, }), ).toBe(false); }); }); describe("TerminalClearInput", () => { it("requires terminalId", () => { expect(decodes(TerminalClearInput, { threadId: "thread-1" })).toBe(false); }); it("accepts an explicit terminalId", () => { const parsed = decodeSync(TerminalClearInput, { threadId: "thread-1", terminalId: DEFAULT_TERMINAL_ID, }); expect(parsed.terminalId).toBe(DEFAULT_TERMINAL_ID); }); }); describe("TerminalCloseInput", () => { it("accepts optional deleteHistory", () => { expect( decodes(TerminalCloseInput, { threadId: "thread-1", deleteHistory: true, }), ).toBe(true); }); }); describe("TerminalSessionSnapshot", () => { const isoTimestamp = "2026-01-01T00:00:00.000Z"; it("accepts running snapshots", () => { expect( decodes(TerminalSessionSnapshot, { threadId: "thread-1", terminalId: DEFAULT_TERMINAL_ID, cwd: "/tmp/project", worktreePath: null, status: "running", pid: 1234, history: "hello\n", exitCode: null, exitSignal: null, label: "Primary", updatedAt: isoTimestamp, }), ).toBe(true); }); }); describe("TerminalEvent", () => { const isoTimestamp = "2026-01-01T00:00:00.000Z"; it("accepts output events", () => { expect( decodes(TerminalEvent, { type: "output", threadId: "thread-1", terminalId: DEFAULT_TERMINAL_ID, data: "line\n", }), ).toBe(true); }); it("accepts exited events", () => { expect( decodes(TerminalEvent, { type: "exited", threadId: "thread-1", terminalId: DEFAULT_TERMINAL_ID, exitCode: 0, exitSignal: null, }), ).toBe(true); }); it("accepts closed events", () => { expect( decodes(TerminalEvent, { type: "closed", threadId: "thread-1", terminalId: DEFAULT_TERMINAL_ID, }), ).toBe(true); }); it("accepts activity events", () => { expect( decodes(TerminalEvent, { type: "activity", threadId: "thread-1", terminalId: DEFAULT_TERMINAL_ID, hasRunningSubprocess: true, label: "vim", }), ).toBe(true); }); it("accepts started events with snapshot worktree metadata", () => { expect( decodes(TerminalEvent, { type: "started", threadId: "thread-1", terminalId: DEFAULT_TERMINAL_ID, snapshot: { threadId: "thread-1", terminalId: DEFAULT_TERMINAL_ID, cwd: "/tmp/project/.t3/worktrees/feature-a", worktreePath: "/tmp/project/.t3/worktrees/feature-a", status: "running", pid: 1234, history: "", exitCode: null, exitSignal: null, label: "Primary", updatedAt: isoTimestamp, }, }), ).toBe(true); }); });