config.test.ts 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. import { NodeFileSystem } from "@effect/platform-node"
  2. import { Global } from "@opencode-ai/core/global"
  3. import { Effect } from "effect"
  4. import { expect, test } from "bun:test"
  5. import path from "path"
  6. import { Config } from "../src/config"
  7. function run<A, E>(directory: string, effect: Effect.Effect<A, E, Config.Service>) {
  8. return Effect.runPromise(
  9. effect.pipe(
  10. Effect.provide(Config.layer),
  11. Effect.provide(Global.layerWith({ config: directory, state: directory })),
  12. Effect.provide(NodeFileSystem.layer),
  13. ),
  14. )
  15. }
  16. test("migrates tui and kv config into cli.json", async () => {
  17. const directory = await Bun.$`mktemp -d`.text().then((value) => value.trim())
  18. await Bun.write(
  19. path.join(directory, "tui.json"),
  20. JSON.stringify({
  21. theme: "legacy",
  22. keybinds: { leader: "ctrl+o" },
  23. plugin: [["example", { mode: "safe" }]],
  24. plugin_enabled: { disabled: false },
  25. leader_timeout: 500,
  26. scroll_speed: 2,
  27. scroll_acceleration: { enabled: true },
  28. diff_style: "stacked",
  29. mouse: false,
  30. }),
  31. )
  32. await Bun.write(
  33. path.join(directory, "kv.json"),
  34. JSON.stringify({
  35. theme_mode_lock: "light",
  36. paste_summary_enabled: false,
  37. exploration_grouping: false,
  38. tips_hidden: true,
  39. }),
  40. )
  41. try {
  42. const config = await run(
  43. directory,
  44. Effect.gen(function* () {
  45. const service = yield* Config.Service
  46. return yield* service.get()
  47. }),
  48. )
  49. expect(config).toMatchObject({
  50. theme: { name: "legacy", mode: "light" },
  51. keybinds: { leader: "ctrl+o" },
  52. plugins: [{ package: "example", options: { mode: "safe" } }, "-disabled"],
  53. leader: { timeout: 500 },
  54. scroll: { speed: 2, acceleration: true },
  55. diffs: { view: "unified" },
  56. prompt: { paste: "full" },
  57. session: { grouping: "none" },
  58. hints: { tips: false },
  59. mouse: false,
  60. })
  61. expect((await Bun.file(path.join(directory, "cli.json")).json()).keybinds).toEqual({ leader: "ctrl+o" })
  62. expect(await Bun.file(path.join(directory, "cli.json")).exists()).toBe(true)
  63. expect(await Bun.file(path.join(directory, "tui.json")).exists()).toBe(true)
  64. expect(await Bun.file(path.join(directory, "kv.json")).exists()).toBe(true)
  65. } finally {
  66. await Bun.$`rm -rf ${directory}`
  67. }
  68. })
  69. test("migrates before the first update and does not remigrate afterward", async () => {
  70. const directory = await Bun.$`mktemp -d`.text().then((value) => value.trim())
  71. await Bun.write(path.join(directory, "tui.json"), JSON.stringify({ theme: "legacy" }))
  72. try {
  73. const config = await run(
  74. directory,
  75. Effect.gen(function* () {
  76. const service = yield* Config.Service
  77. yield* service.update((draft) => {
  78. draft.animations = false
  79. draft.mouse = false
  80. })
  81. yield* Effect.promise(() =>
  82. Bun.write(path.join(directory, "tui.json"), JSON.stringify({ theme: "changed" })),
  83. )
  84. return yield* service.get()
  85. }),
  86. )
  87. expect(config).toEqual({ theme: { name: "legacy" }, animations: false, mouse: false })
  88. expect(await Bun.file(path.join(directory, "cli.json")).json()).toEqual({
  89. theme: { name: "legacy" },
  90. animations: false,
  91. mouse: false,
  92. })
  93. } finally {
  94. await Bun.$`rm -rf ${directory}`
  95. }
  96. })
  97. test("updates a config draft while preserving JSONC comments", async () => {
  98. const directory = await Bun.$`mktemp -d`.text().then((value) => value.trim())
  99. await Bun.write(path.join(directory, "cli.json"), "{\n // Keep this comment\n \"animations\": true\n}\n")
  100. try {
  101. const config = await run(
  102. directory,
  103. Effect.gen(function* () {
  104. const service = yield* Config.Service
  105. return yield* service.update((draft) => {
  106. draft.prompt = { paste: "compact" }
  107. })
  108. }),
  109. )
  110. expect(config).toEqual({ animations: true, prompt: { paste: "compact" } })
  111. expect(await Bun.file(path.join(directory, "cli.json")).text()).toContain("// Keep this comment")
  112. } finally {
  113. await Bun.$`rm -rf ${directory}`
  114. }
  115. })