tui-command.test.ts 3.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
  2. import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
  3. import { join } from "node:path";
  4. import { tmpdir } from "node:os";
  5. import { realpathSync } from "node:fs";
  6. import { createProgram } from "../program.js";
  7. describe("tui command", () => {
  8. const originalArgv = process.argv;
  9. const originalCwd = process.cwd();
  10. let tempDir: string;
  11. beforeEach(async () => {
  12. tempDir = await mkdtemp(join(tmpdir(), "inkos-default-studio-"));
  13. process.chdir(tempDir);
  14. });
  15. afterEach(async () => {
  16. process.argv = originalArgv;
  17. process.chdir(originalCwd);
  18. vi.restoreAllMocks();
  19. await rm(tempDir, { recursive: true, force: true });
  20. });
  21. it("launches Studio when no subcommand is provided", async () => {
  22. const launchStudio = vi.fn(async () => {});
  23. const program = createProgram({ launchStudio });
  24. await program.parseAsync([], { from: "user" });
  25. expect(launchStudio).toHaveBeenCalledTimes(1);
  26. expect(launchStudio).toHaveBeenCalledWith(process.cwd(), "4567");
  27. });
  28. it("auto-initializes a minimal project before launching Studio by default", async () => {
  29. const launchStudio = vi.fn(async () => {});
  30. const program = createProgram({ launchStudio });
  31. await program.parseAsync([], { from: "user" });
  32. const config = JSON.parse(await readFile(join(tempDir, "inkos.json"), "utf-8"));
  33. expect(config.name).toMatch(/^inkos-default-studio-/);
  34. expect(config.llm.configSource).toBe("studio");
  35. expect(config.llm.model).toBe("");
  36. expect(config.llm.baseUrl).toBe("");
  37. expect(launchStudio).toHaveBeenCalledTimes(1);
  38. const [calledRoot, calledPort] = launchStudio.mock.calls[0] as unknown as [string, string];
  39. expect(realpathSync(calledRoot)).toBe(realpathSync(tempDir));
  40. expect(calledPort).toBe("4567");
  41. });
  42. it("does not overwrite an existing .env during automatic Studio init", async () => {
  43. await writeFile(join(tempDir, ".env"), "EXISTING_ENV=1\n", "utf-8");
  44. const launchStudio = vi.fn(async () => {});
  45. const program = createProgram({ launchStudio });
  46. await program.parseAsync([], { from: "user" });
  47. await expect(readFile(join(tempDir, ".env"), "utf-8")).resolves.toBe("EXISTING_ENV=1\n");
  48. });
  49. it("launches the TUI when the explicit tui command is used", async () => {
  50. const launchTui = vi.fn(async () => {});
  51. const launchStudio = vi.fn(async () => {});
  52. const program = createProgram({ launchTui, launchStudio });
  53. await program.parseAsync(["tui"], { from: "user" });
  54. expect(launchTui).toHaveBeenCalledTimes(1);
  55. expect(launchStudio).not.toHaveBeenCalled();
  56. });
  57. it("auto-initializes a minimal project before launching the explicit studio command", async () => {
  58. const launchStudio = vi.fn(async () => {});
  59. const program = createProgram({ launchStudio });
  60. await program.parseAsync(["studio"], { from: "user" });
  61. const config = JSON.parse(await readFile(join(tempDir, "inkos.json"), "utf-8"));
  62. expect(config.llm.configSource).toBe("studio");
  63. expect(launchStudio).toHaveBeenCalledTimes(1);
  64. const [calledRoot, calledPort] = launchStudio.mock.calls[0] as unknown as [string, string];
  65. expect(realpathSync(calledRoot)).toBe(realpathSync(tempDir));
  66. expect(calledPort).toBe("4567");
  67. });
  68. });