builtins.test.ts 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. import { describe, expect } from "bun:test"
  2. import { Effect, Layer } from "effect"
  3. import * as TestClock from "effect/testing/TestClock"
  4. import { Location } from "@opencode-ai/core/location"
  5. import { FSUtil } from "@opencode-ai/core/fs-util"
  6. import { Global } from "@opencode-ai/core/global"
  7. import { AbsolutePath } from "@opencode-ai/core/schema"
  8. import { SystemContext } from "@opencode-ai/core/system-context"
  9. import { SystemContextBuiltIns } from "@opencode-ai/core/system-context/builtins"
  10. import { SystemContextRegistry } from "@opencode-ai/core/system-context/registry"
  11. import { location } from "../fixture/location"
  12. import { testEffect } from "../lib/effect"
  13. const directory = AbsolutePath.make(FSUtil.resolve("/repo/packages/core"))
  14. const projectDirectory = AbsolutePath.make(FSUtil.resolve("/repo"))
  15. const instructionFile = FSUtil.resolve("/repo/AGENTS.md")
  16. const timestamp = Date.parse("2026-06-03T12:00:00.000Z")
  17. const localDate = (time: number) => new Date(time).toDateString()
  18. const locationLayer = Layer.succeed(
  19. Location.Service,
  20. Location.Service.of(
  21. location(
  22. { directory },
  23. { projectDirectory, vcs: { type: "git", store: AbsolutePath.make(FSUtil.resolve("/repo/.git")) } },
  24. ),
  25. ),
  26. )
  27. const it = testEffect(
  28. SystemContextBuiltIns.locationLayer.pipe(
  29. Layer.provide(FSUtil.defaultLayer),
  30. Layer.provide(Global.layerWith({ config: "/global" })),
  31. Layer.provide(locationLayer),
  32. ),
  33. )
  34. const instructionFS = Layer.effect(
  35. FSUtil.Service,
  36. FSUtil.Service.pipe(
  37. Effect.map((fs) =>
  38. FSUtil.Service.of({
  39. ...fs,
  40. up: () => Effect.succeed([instructionFile]),
  41. readFileStringSafe: (path) => Effect.succeed(path === instructionFile ? "Be precise." : undefined),
  42. }),
  43. ),
  44. ),
  45. ).pipe(Layer.provide(FSUtil.defaultLayer))
  46. const itWithInstructions = testEffect(
  47. SystemContextBuiltIns.locationLayer.pipe(
  48. Layer.provide(instructionFS),
  49. Layer.provide(Global.layerWith({ config: "/global" })),
  50. Layer.provide(locationLayer),
  51. ),
  52. )
  53. describe("SystemContextBuiltIns", () => {
  54. it.effect("loads location-scoped environment and host-local date context", () =>
  55. Effect.gen(function* () {
  56. yield* TestClock.setTime(timestamp)
  57. const context = yield* SystemContextRegistry.Service
  58. const initialized = yield* SystemContext.initialize(yield* context.load())
  59. expect(initialized.baseline).toBe(
  60. [
  61. "Here is some useful information about the environment you are running in:",
  62. "<env>",
  63. ` Working directory: ${directory}`,
  64. ` Workspace root folder: ${projectDirectory}`,
  65. " Is directory a git repo: yes",
  66. ` Platform: ${process.platform}`,
  67. "</env>",
  68. "",
  69. `Today's date: ${localDate(timestamp)}`,
  70. ].join("\n"),
  71. )
  72. }),
  73. )
  74. it.effect("reconciles the date without repeating unchanged environment context", () =>
  75. Effect.gen(function* () {
  76. yield* TestClock.setTime(timestamp)
  77. const context = yield* SystemContextRegistry.Service
  78. const initialized = yield* SystemContext.initialize(yield* context.load())
  79. yield* TestClock.setTime(timestamp + 24 * 60 * 60 * 1000)
  80. const refreshed = yield* SystemContext.reconcile(yield* context.load(), initialized.snapshot)
  81. expect(refreshed).toMatchObject({
  82. _tag: "Updated",
  83. text: `Today's date is now: ${localDate(timestamp + 24 * 60 * 60 * 1000)}`,
  84. })
  85. }),
  86. )
  87. it.effect("does not update again within the same local calendar day", () =>
  88. Effect.gen(function* () {
  89. yield* TestClock.setTime(timestamp)
  90. const context = yield* SystemContextRegistry.Service
  91. const initialized = yield* SystemContext.initialize(yield* context.load())
  92. yield* TestClock.setTime(timestamp + 60 * 60 * 1000)
  93. expect(yield* SystemContext.reconcile(yield* context.load(), initialized.snapshot)).toEqual({ _tag: "Unchanged" })
  94. }),
  95. )
  96. itWithInstructions.effect("composes ambient instructions after built-in context", () =>
  97. Effect.gen(function* () {
  98. yield* TestClock.setTime(timestamp)
  99. const context = yield* SystemContextRegistry.Service
  100. expect((yield* SystemContext.initialize(yield* context.load())).baseline).toBe(
  101. [
  102. "Here is some useful information about the environment you are running in:",
  103. "<env>",
  104. ` Working directory: ${directory}`,
  105. ` Workspace root folder: ${projectDirectory}`,
  106. " Is directory a git repo: yes",
  107. ` Platform: ${process.platform}`,
  108. "</env>",
  109. "",
  110. `Today's date: ${localDate(timestamp)}`,
  111. "",
  112. `Instructions from: ${instructionFile}\nBe precise.`,
  113. ].join("\n"),
  114. )
  115. }),
  116. )
  117. })