reference-instructions.test.ts 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. import { describe, expect } from "bun:test"
  2. import { Effect, Layer } from "effect"
  3. import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
  4. import { AbsolutePath } from "@opencode-ai/core/schema"
  5. import { Reference } from "@opencode-ai/core/reference"
  6. import { ReferenceInstructions } from "@opencode-ai/core/reference/instructions"
  7. import { it } from "./lib/effect"
  8. import { readInitial, readUpdate } from "./lib/instructions"
  9. const instructionsLayer = (referenceLayer: Layer.Layer<Reference.Service>) =>
  10. AppNodeBuilder.build(ReferenceInstructions.node, [[Reference.node, referenceLayer]])
  11. describe("ReferenceInstructions", () => {
  12. it.effect("lists available references in the instructions", () =>
  13. Effect.gen(function* () {
  14. const instructions = yield* ReferenceInstructions.Service
  15. const generation = yield* readInitial(yield* instructions.load())
  16. expect(generation.text).toContain("<available_references>")
  17. expect(generation.text).toContain("<name>docs</name>")
  18. expect(generation.text).toContain("<path>/docs</path>")
  19. expect(generation.text).toContain("<description>Use for product documentation</description>")
  20. }).pipe(
  21. Effect.provide(
  22. instructionsLayer(
  23. Layer.mock(Reference.Service, {
  24. list: () =>
  25. Effect.succeed([
  26. Reference.Info.make({
  27. name: "docs",
  28. path: AbsolutePath.make("/docs"),
  29. description: "Use for product documentation",
  30. source: Reference.LocalSource.make({
  31. type: "local",
  32. path: AbsolutePath.make("/docs"),
  33. description: "Use for product documentation",
  34. }),
  35. }),
  36. ]),
  37. }),
  38. ),
  39. ),
  40. ),
  41. )
  42. it.effect("omits instructions when no references are available", () =>
  43. Effect.gen(function* () {
  44. const instructions = yield* ReferenceInstructions.Service
  45. const generation = yield* readInitial(yield* instructions.load())
  46. expect(generation.text).toBe("")
  47. }).pipe(Effect.provide(instructionsLayer(Layer.mock(Reference.Service, { list: () => Effect.succeed([]) })))),
  48. )
  49. it.effect("omits references without descriptions", () =>
  50. Effect.gen(function* () {
  51. const instructions = yield* ReferenceInstructions.Service
  52. const generation = yield* readInitial(yield* instructions.load())
  53. expect(generation.text).toBe("")
  54. }).pipe(
  55. Effect.provide(
  56. instructionsLayer(
  57. Layer.mock(Reference.Service, {
  58. list: () =>
  59. Effect.succeed([
  60. Reference.Info.make({
  61. name: "docs",
  62. path: AbsolutePath.make("/docs"),
  63. source: Reference.LocalSource.make({ type: "local", path: AbsolutePath.make("/docs") }),
  64. }),
  65. ]),
  66. }),
  67. ),
  68. ),
  69. ),
  70. )
  71. it.effect("announces added and removed references as deltas", () => {
  72. const reference = (name: string, description: string) =>
  73. Reference.Info.make({
  74. name,
  75. path: AbsolutePath.make(`/${name}`),
  76. description,
  77. source: Reference.LocalSource.make({ type: "local", path: AbsolutePath.make(`/${name}`), description }),
  78. })
  79. let references = [reference("docs", "Use for product documentation")]
  80. return Effect.gen(function* () {
  81. const instructions = yield* ReferenceInstructions.Service
  82. const initialized = yield* readInitial(yield* instructions.load())
  83. references = [reference("docs", "Use for product documentation"), reference("examples", "Use for examples")]
  84. const added = yield* readUpdate(yield* instructions.load(), initialized)
  85. expect(added.text).toBe(
  86. [
  87. "New project references are available in addition to those previously listed:",
  88. " <reference>",
  89. " <name>examples</name>",
  90. " <path>/examples</path>",
  91. " <description>Use for examples</description>",
  92. " </reference>",
  93. ].join("\n"),
  94. )
  95. references = [reference("examples", "Use for examples")]
  96. expect((yield* readUpdate(yield* instructions.load(), added)).text).toBe(
  97. "The following project references are no longer available and must not be used: docs.",
  98. )
  99. }).pipe(
  100. Effect.provide(instructionsLayer(Layer.mock(Reference.Service, { list: () => Effect.succeed(references) }))),
  101. )
  102. })
  103. })