lsp.test.ts 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. import { describe, expect, test } from "bun:test"
  2. import { Schema } from "effect"
  3. import { ConfigLSPV1 } from "@kirincode-ai/core/v1/config/lsp"
  4. // The LSP config refinement enforces: any custom (non-builtin) LSP server
  5. // entry must declare an `extensions` array so the client knows which files
  6. // the server should attach to. Builtin server IDs and explicitly disabled
  7. // entries are exempt.
  8. //
  9. // `typescript` is a builtin server id (see src/lsp/server.ts).
  10. describe("ConfigLSPV1.Info refinement", () => {
  11. const decodeEffect = Schema.decodeUnknownSync(ConfigLSPV1.Info)
  12. describe("accepted inputs", () => {
  13. test("true and false pass (top-level toggle)", () => {
  14. expect(decodeEffect(true)).toBe(true)
  15. expect(decodeEffect(false)).toBe(false)
  16. })
  17. test("builtin server with no extensions passes", () => {
  18. const input = { typescript: { command: ["typescript-language-server", "--stdio"] } }
  19. expect(decodeEffect(input)).toEqual(input)
  20. })
  21. test("custom server WITH extensions passes", () => {
  22. const input = {
  23. "my-lsp": { command: ["my-lsp-bin"], extensions: [".ml"] },
  24. }
  25. expect(decodeEffect(input)).toEqual(input)
  26. })
  27. test("disabled custom server passes (no extensions needed)", () => {
  28. const input = { "my-lsp": { disabled: true as const } }
  29. expect(decodeEffect(input)).toEqual(input)
  30. })
  31. test("mix of builtin and custom with extensions passes", () => {
  32. const input = {
  33. typescript: { command: ["typescript-language-server", "--stdio"] },
  34. "my-lsp": { command: ["my-lsp-bin"], extensions: [".ml"] },
  35. }
  36. expect(decodeEffect(input)).toEqual(input)
  37. })
  38. })
  39. describe("rejected inputs", () => {
  40. const expectedMessage = "For custom LSP servers, 'extensions' array is required."
  41. test("custom server WITHOUT extensions fails via Effect decode", () => {
  42. expect(() => decodeEffect({ "my-lsp": { command: ["my-lsp-bin"] } })).toThrow(expectedMessage)
  43. })
  44. test("custom server with empty extensions array fails (extensions must be non-empty-truthy)", () => {
  45. // Boolean(['']) is true, so a non-empty array of strings is fine.
  46. // Boolean([]) is also true in JS, so empty arrays are accepted by the
  47. // refinement. This test documents current behavior.
  48. const input = { "my-lsp": { command: ["my-lsp-bin"], extensions: [] } }
  49. expect(decodeEffect(input)).toEqual(input)
  50. })
  51. test("custom server without extensions mixed with a valid builtin still fails", () => {
  52. const input = {
  53. typescript: { command: ["typescript-language-server", "--stdio"] },
  54. "my-lsp": { command: ["my-lsp-bin"] },
  55. }
  56. expect(() => decodeEffect(input)).toThrow(expectedMessage)
  57. })
  58. })
  59. })