runtime.stdin.test.ts 1.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071
  1. import { describe, expect, test } from "bun:test"
  2. import { Readable } from "node:stream"
  3. import { INTERACTIVE_INPUT_ERROR, resolveInteractiveStdin } from "@/cli/cmd/run/runtime.stdin"
  4. function stream(isTTY: boolean) {
  5. return Object.assign(new Readable({ read() {} }), { isTTY }) as NodeJS.ReadStream
  6. }
  7. describe("run interactive stdin", () => {
  8. test("reuses stdin when it is already a tty", () => {
  9. const stdin = stream(true)
  10. const seen: string[] = []
  11. const result = resolveInteractiveStdin(
  12. stdin,
  13. (path) => {
  14. seen.push(path)
  15. return stream(true)
  16. },
  17. "linux",
  18. )
  19. expect(result.stdin).toBe(stdin)
  20. expect(result.cleanup).toBeUndefined()
  21. expect(seen).toEqual([])
  22. })
  23. test("opens the controlling terminal when stdin is piped", () => {
  24. const tty = stream(true)
  25. const seen: string[] = []
  26. const result = resolveInteractiveStdin(
  27. stream(false),
  28. (path) => {
  29. seen.push(path)
  30. return tty
  31. },
  32. "linux",
  33. )
  34. expect(result.stdin).toBe(tty)
  35. expect(seen).toEqual(["/dev/tty"])
  36. result.cleanup?.()
  37. expect(tty.destroyed).toBe(true)
  38. })
  39. test("uses CONIN$ on windows", () => {
  40. const seen: string[] = []
  41. resolveInteractiveStdin(
  42. stream(false),
  43. (path) => {
  44. seen.push(path)
  45. return stream(true)
  46. },
  47. "win32",
  48. )
  49. expect(seen).toEqual(["CONIN$"])
  50. })
  51. test("throws a clear error when no controlling terminal is available", () => {
  52. expect(() =>
  53. resolveInteractiveStdin(
  54. stream(false),
  55. () => {
  56. throw new Error("open failed")
  57. },
  58. "linux",
  59. ),
  60. ).toThrow(INTERACTIVE_INPUT_ERROR)
  61. })
  62. })