plugin-discovery.test.ts 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172
  1. import { mkdir, writeFile } from "node:fs/promises"
  2. import path from "node:path"
  3. import { expect, test } from "bun:test"
  4. import { discoverTuiPlugins, tuiPluginDirectories } from "../src/plugin/discovery"
  5. import { localProjectDirectory } from "../src/util/config-directories"
  6. import { tmpdir } from "./fixture/fixture"
  7. test("discovers project TUI plugin files in stable order", async () => {
  8. await using tmp = await tmpdir()
  9. const directory = path.join(tmp.path, ".opencode", "plugins", "tui")
  10. await mkdir(path.join(directory, "nested"), { recursive: true })
  11. await Promise.all([
  12. writeFile(path.join(directory, "second.tsx"), "export default {}"),
  13. writeFile(path.join(directory, "first.js"), "export default {}"),
  14. writeFile(path.join(directory, "ignored.json"), "{}"),
  15. writeFile(path.join(directory, "nested", "ignored.ts"), "export default {}"),
  16. ])
  17. expect(
  18. await discoverTuiPlugins(await tuiPluginDirectories(tmp.path, path.join(tmp.path, "config"))),
  19. ).toEqual([path.join(directory, "first.js"), path.join(directory, "second.tsx")])
  20. })
  21. test("returns no project TUI plugins when the directory is absent", async () => {
  22. await using tmp = await tmpdir()
  23. const roots = await tuiPluginDirectories(tmp.path, path.join(tmp.path, "config"))
  24. expect(await discoverTuiPlugins(roots)).toEqual([])
  25. expect(roots).toContain(path.join(tmp.path, ".opencode", "plugins", "tui"))
  26. })
  27. test("discovers global and ancestor plugin roots in precedence order", async () => {
  28. await using tmp = await tmpdir()
  29. const cwd = path.join(tmp.path, "repo", "packages", "app")
  30. const project = path.join(tmp.path, "repo")
  31. const config = path.join(tmp.path, "config")
  32. const directories = [
  33. path.join(config, "plugins", "tui"),
  34. path.join(tmp.path, "repo", ".opencode", "plugins", "tui"),
  35. path.join(tmp.path, "repo", "packages", ".opencode", "plugins", "tui"),
  36. ]
  37. const outside = path.join(tmp.path, ".opencode", "plugins", "tui")
  38. await mkdir(path.join(project, ".git"), { recursive: true })
  39. await Promise.all([...directories, outside].map((directory) => mkdir(directory, { recursive: true })))
  40. await Promise.all(
  41. directories.map((directory, index) => writeFile(path.join(directory, `${index}.ts`), "export default {}")),
  42. )
  43. await writeFile(path.join(outside, "outside.ts"), "export default {}")
  44. const roots = await tuiPluginDirectories(cwd, config)
  45. expect(await discoverTuiPlugins(roots)).toEqual(
  46. directories.map((directory, index) => path.join(directory, `${index}.ts`)),
  47. )
  48. expect(roots).not.toContain(path.join(cwd, ".opencode", "plugins", "tui"))
  49. expect(roots).not.toContain(outside)
  50. })
  51. test("uses an Hg root for a missing project plugin directory", async () => {
  52. await using tmp = await tmpdir()
  53. const project = path.join(tmp.path, "repo")
  54. const cwd = path.join(project, "package")
  55. await mkdir(path.join(project, ".hg"), { recursive: true })
  56. await mkdir(cwd, { recursive: true })
  57. expect(await tuiPluginDirectories(cwd, path.join(tmp.path, "config"))).toContain(
  58. path.join(project, ".opencode", "plugins", "tui"),
  59. )
  60. })
  61. test("propagates non-missing filesystem errors", async () => {
  62. await expect(localProjectDirectory("\0")).rejects.toBeInstanceOf(Error)
  63. await expect(discoverTuiPlugins(["\0"])).rejects.toBeInstanceOf(Error)
  64. })