pty-session.test.ts 3.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. import { describe, expect, test } from "bun:test"
  2. import { Bus } from "../../src/bus"
  3. import { Instance } from "../../src/project/instance"
  4. import { Pty } from "../../src/pty"
  5. import type { PtyID } from "../../src/pty/schema"
  6. import { tmpdir } from "../fixture/fixture"
  7. import { setTimeout as sleep } from "node:timers/promises"
  8. const wait = async (fn: () => boolean, ms = 5000) => {
  9. const end = Date.now() + ms
  10. while (Date.now() < end) {
  11. if (fn()) return
  12. await sleep(25)
  13. }
  14. throw new Error("timeout waiting for pty events")
  15. }
  16. const pick = (log: Array<{ type: "created" | "exited" | "deleted"; id: PtyID }>, id: PtyID) => {
  17. return log.filter((evt) => evt.id === id).map((evt) => evt.type)
  18. }
  19. describe("pty", () => {
  20. test("publishes created, exited, deleted in order for a short-lived process", async () => {
  21. if (process.platform === "win32") return
  22. await using dir = await tmpdir({ git: true })
  23. await Instance.provide({
  24. directory: dir.path,
  25. fn: async () => {
  26. const log: Array<{ type: "created" | "exited" | "deleted"; id: PtyID }> = []
  27. const off = [
  28. Bus.subscribe(Pty.Event.Created, (evt) => log.push({ type: "created", id: evt.properties.info.id })),
  29. Bus.subscribe(Pty.Event.Exited, (evt) => log.push({ type: "exited", id: evt.properties.id })),
  30. Bus.subscribe(Pty.Event.Deleted, (evt) => log.push({ type: "deleted", id: evt.properties.id })),
  31. ]
  32. let id: PtyID | undefined
  33. try {
  34. const info = await Pty.create({
  35. command: "/usr/bin/env",
  36. args: ["sh", "-c", "sleep 0.1"],
  37. title: "sleep",
  38. })
  39. id = info.id
  40. await wait(() => pick(log, id!).includes("exited"))
  41. await Pty.remove(id)
  42. await wait(() => pick(log, id!).length >= 3)
  43. expect(pick(log, id!)).toEqual(["created", "exited", "deleted"])
  44. } finally {
  45. off.forEach((x) => x())
  46. if (id) await Pty.remove(id)
  47. }
  48. },
  49. })
  50. })
  51. test("publishes created, exited, deleted in order for /bin/sh + remove", async () => {
  52. if (process.platform === "win32") return
  53. await using dir = await tmpdir({ git: true })
  54. await Instance.provide({
  55. directory: dir.path,
  56. fn: async () => {
  57. const log: Array<{ type: "created" | "exited" | "deleted"; id: PtyID }> = []
  58. const off = [
  59. Bus.subscribe(Pty.Event.Created, (evt) => log.push({ type: "created", id: evt.properties.info.id })),
  60. Bus.subscribe(Pty.Event.Exited, (evt) => log.push({ type: "exited", id: evt.properties.id })),
  61. Bus.subscribe(Pty.Event.Deleted, (evt) => log.push({ type: "deleted", id: evt.properties.id })),
  62. ]
  63. let id: PtyID | undefined
  64. try {
  65. const info = await Pty.create({ command: "/bin/sh", title: "sh" })
  66. id = info.id
  67. await sleep(100)
  68. await Pty.remove(id)
  69. await wait(() => pick(log, id!).length >= 3)
  70. expect(pick(log, id!)).toEqual(["created", "exited", "deleted"])
  71. } finally {
  72. off.forEach((x) => x())
  73. if (id) await Pty.remove(id)
  74. }
  75. },
  76. })
  77. })
  78. })