import { describe, expect, it, vi } from "vitest"; import { Capsule } from "../src/capsule.js"; describe("PtyManager", () => { it("starts a PTY, sends controls, and yields events", async () => { const capsule = new Capsule("cap_1", { baseUrl: "https://api.example.com", }); const sent: unknown[] = []; let onMessage: ((message: unknown) => void) | undefined; const close = vi.fn(); vi.spyOn(capsule.client.capsules, "ptySession").mockImplementation( async (_id, opts) => { onMessage = opts.onMessage; return { close, get isClosed() { return false; }, send: (message: unknown) => sent.push(message), } as never; }, ); const session = await capsule.pty.start({ cmd: "/bin/sh", cols: 100, rows: 30, }); expect(sent).toEqual([ { cmd: "/bin/sh", cols: 100, rows: 30, type: "start" }, ]); session.input("ls\n"); session.resize(120, 40); session.kill(); expect(sent.slice(1)).toEqual([ { data: Buffer.from("ls\n").toString("base64"), type: "input" }, { cols: 120, rows: 40, type: "resize" }, { type: "kill" }, ]); const event = session.events.next(); onMessage?.({ data: Buffer.from("ok").toString("base64"), type: "output" }); await expect(event).resolves.toEqual({ done: false, value: { data: Buffer.from("ok").toString("base64"), type: "output", }, }); await session[Symbol.asyncDispose](); expect(close).toHaveBeenCalledOnce(); }); it("connects to an existing PTY tag", async () => { const capsule = new Capsule("cap_1", { baseUrl: "https://api.example.com", }); const sent: unknown[] = []; vi.spyOn(capsule.client.capsules, "ptySession").mockResolvedValue({ close: vi.fn(), get isClosed() { return false; }, send: (message: unknown) => sent.push(message), } as never); await capsule.pty.connect("pty-tag"); expect(sent).toEqual([{ tag: "pty-tag", type: "connect" }]); }); it("closes the connection when close() is called directly", async () => { const capsule = new Capsule("cap_1", { baseUrl: "https://api.example.com", }); const close = vi.fn(); vi.spyOn(capsule.client.capsules, "ptySession").mockResolvedValue({ close, get isClosed() { return false; }, send: vi.fn(), } as never); const session = await capsule.pty.start(); session.close(); expect(close).toHaveBeenCalledOnce(); }); it("sends all PtyStartOptions fields in the start message", async () => { const capsule = new Capsule("cap_1", { baseUrl: "https://api.example.com", }); const sent: unknown[] = []; vi.spyOn(capsule.client.capsules, "ptySession").mockResolvedValue({ close: vi.fn(), get isClosed() { return false; }, send: (message: unknown) => sent.push(message), } as never); await capsule.pty.start({ cmd: "/bin/bash", args: ["--login"], cols: 120, rows: 40, envs: { TERM: "xterm-256color" }, cwd: "/home/user", user: "user", }); expect(sent).toEqual([ { type: "start", cmd: "/bin/bash", args: ["--login"], cols: 120, rows: 40, envs: { TERM: "xterm-256color" }, cwd: "/home/user", user: "user", }, ]); }); });