system-context.test.ts 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. import { describe, expect, test } from "bun:test"
  2. import { Effect, Schema } from "effect"
  3. import { SystemContext } from "@opencode-ai/core/system-context"
  4. import { Hash } from "@opencode-ai/core/util/hash"
  5. const key = SystemContext.Key.make
  6. describe("SystemContext", () => {
  7. test("loads one coherent sample and initializes a deterministic baseline", async () => {
  8. let loads = 0
  9. const context = SystemContext.struct({
  10. date: SystemContext.value({
  11. key: key("core/date"),
  12. load: Effect.sync(() => {
  13. loads++
  14. return { baseline: "Today's date is 2026-06-03.", update: "The current date is 2026-06-03." }
  15. }),
  16. }),
  17. location: SystemContext.value({
  18. key: key("core/location"),
  19. load: Effect.succeed({ baseline: "Working directory: /repo", update: "The working directory is /repo." }),
  20. }),
  21. })
  22. const initialized = SystemContext.initialize(await Effect.runPromise(SystemContext.load(context)))
  23. expect(loads).toBe(1)
  24. expect(initialized).toEqual({
  25. baseline: [
  26. { key: key("core/date"), text: "Today's date is 2026-06-03." },
  27. { key: key("core/location"), text: "Working directory: /repo" },
  28. ],
  29. checkpoint: {
  30. "core/date": Hash.sha256("The current date is 2026-06-03."),
  31. "core/location": Hash.sha256("The working directory is /repo."),
  32. },
  33. })
  34. })
  35. test("emits changed and newly registered components in declaration order", async () => {
  36. const context = SystemContext.struct({
  37. date: SystemContext.value({
  38. key: key("core/date"),
  39. load: Effect.succeed({ baseline: "Today's date is 2026-06-04.", update: "The current date is 2026-06-04." }),
  40. }),
  41. location: SystemContext.value({
  42. key: key("core/location"),
  43. load: Effect.succeed({ baseline: "Working directory: /repo", update: "The working directory is /repo." }),
  44. }),
  45. skills: SystemContext.value({
  46. key: key("core/skills"),
  47. load: Effect.succeed({ baseline: "Available skills: effect", update: "Available skills: effect" }),
  48. }),
  49. })
  50. const refreshed = SystemContext.refresh(await Effect.runPromise(SystemContext.load(context)), {
  51. "core/date": Hash.sha256("The current date is 2026-06-03."),
  52. "core/location": Hash.sha256("The working directory is /repo."),
  53. })
  54. expect(refreshed).toEqual({
  55. changes: [
  56. { key: key("core/date"), text: "The current date is 2026-06-04." },
  57. { key: key("core/skills"), text: "Available skills: effect" },
  58. ],
  59. checkpoint: {
  60. "core/date": Hash.sha256("The current date is 2026-06-04."),
  61. "core/location": Hash.sha256("The working directory is /repo."),
  62. "core/skills": Hash.sha256("Available skills: effect"),
  63. },
  64. })
  65. expect(SystemContext.render(refreshed.changes)).toBe("The current date is 2026-06-04.\n\nAvailable skills: effect")
  66. })
  67. test("omits unavailable initial context and admits it after its first successful load", async () => {
  68. let available = false
  69. const context = SystemContext.struct({
  70. remote: SystemContext.value({
  71. key: key("core/remote-instructions"),
  72. load: Effect.sync(() =>
  73. available
  74. ? { baseline: "Remote instructions: available", update: "Remote instructions are now available." }
  75. : SystemContext.unavailable,
  76. ),
  77. }),
  78. })
  79. const initialized = SystemContext.initialize(await Effect.runPromise(SystemContext.load(context)))
  80. available = true
  81. const refreshed = SystemContext.refresh(
  82. await Effect.runPromise(SystemContext.load(context)),
  83. initialized.checkpoint,
  84. )
  85. expect(initialized).toEqual({ baseline: [], checkpoint: {} })
  86. expect(refreshed.changes).toEqual([
  87. { key: key("core/remote-instructions"), text: "Remote instructions are now available." },
  88. ])
  89. })
  90. test("retains an existing checkpoint while context is unavailable", async () => {
  91. const previous = { "core/remote-instructions": Hash.sha256("Remote instructions: old") }
  92. const context = SystemContext.struct({
  93. remote: SystemContext.value({
  94. key: key("core/remote-instructions"),
  95. load: Effect.succeed(SystemContext.unavailable),
  96. }),
  97. })
  98. const refreshed = SystemContext.refresh(await Effect.runPromise(SystemContext.load(context)), previous)
  99. expect(refreshed).toEqual({ changes: [], checkpoint: previous })
  100. })
  101. test("drops checkpoints for removed components", async () => {
  102. const context = SystemContext.struct({
  103. date: SystemContext.value({
  104. key: key("core/date"),
  105. load: Effect.succeed({ baseline: "Today's date is 2026-06-03.", update: "The current date is 2026-06-03." }),
  106. }),
  107. })
  108. const refreshed = SystemContext.refresh(await Effect.runPromise(SystemContext.load(context)), {
  109. "core/date": Hash.sha256("The current date is 2026-06-03."),
  110. "plugin/removed": Hash.sha256("Removed plugin context"),
  111. })
  112. expect(refreshed).toEqual({
  113. changes: [],
  114. checkpoint: { "core/date": Hash.sha256("The current date is 2026-06-03.") },
  115. })
  116. })
  117. test("ignores inherited checkpoint properties", async () => {
  118. const context = SystemContext.struct({
  119. date: SystemContext.value({
  120. key: key("core/date"),
  121. load: Effect.succeed({ baseline: "Today's date is 2026-06-03.", update: "The current date is 2026-06-03." }),
  122. }),
  123. })
  124. const previous = Object.create({
  125. "core/date": Hash.sha256("The current date is 2026-06-03."),
  126. }) as SystemContext.Checkpoint
  127. const refreshed = SystemContext.refresh(await Effect.runPromise(SystemContext.load(context)), previous)
  128. expect(refreshed.changes).toEqual([{ key: key("core/date"), text: "The current date is 2026-06-03." }])
  129. expect(Object.hasOwn(refreshed.checkpoint, "core/date")).toBe(true)
  130. })
  131. test("preserves unexpected loader failures", async () => {
  132. const context = SystemContext.struct({
  133. broken: SystemContext.value({
  134. key: key("plugin/broken"),
  135. load: Effect.fail("broken loader"),
  136. }),
  137. })
  138. await expect(Effect.runPromise(SystemContext.load(context))).rejects.toBe("broken loader")
  139. })
  140. test("rejects duplicate component keys", () => {
  141. expect(() =>
  142. SystemContext.struct({
  143. one: SystemContext.value({ key: key("core/date"), load: Effect.succeed({ baseline: "one", update: "one" }) }),
  144. two: SystemContext.value({ key: key("core/date"), load: Effect.succeed({ baseline: "two", update: "two" }) }),
  145. }),
  146. ).toThrow(new SystemContext.DuplicateKeyError({ key: key("core/date") }))
  147. })
  148. test("rejects duplicate component keys at the interpreter boundary", async () => {
  149. const component = SystemContext.value({
  150. key: key("core/date"),
  151. load: Effect.succeed({ baseline: "date", update: "date" }),
  152. })
  153. const context: SystemContext.SystemContext = { components: [component, component] }
  154. await expect(Effect.runPromise(SystemContext.load(context))).rejects.toBeInstanceOf(SystemContext.DuplicateKeyError)
  155. })
  156. test("requires namespaced component keys", () => {
  157. const decode = Schema.decodeUnknownSync(SystemContext.Key)
  158. expect(decode("core/date")).toBe(key("core/date"))
  159. expect(() => decode("date")).toThrow()
  160. expect(() => decode("core/")).toThrow()
  161. })
  162. })