log.test.ts 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. import { expect } from "bun:test"
  2. import { Effect } from "effect"
  3. import fs from "fs/promises"
  4. import path from "path"
  5. import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
  6. import { Global } from "@opencode-ai/core/global"
  7. import * as Log from "@opencode-ai/core/util/log"
  8. import { tmpdirScoped } from "../fixture/fixture"
  9. import { testEffect } from "../lib/effect"
  10. const it = testEffect(CrossSpawnSpawner.defaultLayer)
  11. function files(dir: string) {
  12. return Effect.gen(function* () {
  13. let last = ""
  14. let same = 0
  15. for (let i = 0; i < 50; i++) {
  16. const list = yield* Effect.promise(() => fs.readdir(dir).then((files) => files.sort()))
  17. const next = JSON.stringify(list)
  18. same = next === last ? same + 1 : 0
  19. if (same >= 2 && list.length === 11) return list
  20. last = next
  21. yield* Effect.sleep("10 millis")
  22. }
  23. return yield* Effect.promise(() => fs.readdir(dir).then((files) => files.sort()))
  24. })
  25. }
  26. it.live("init cleanup keeps the newest timestamped logs", () =>
  27. Effect.gen(function* () {
  28. const log = Global.Path.log
  29. yield* Effect.addFinalizer(() => Effect.sync(() => (Global.Path.log = log)))
  30. const dir = yield* tmpdirScoped()
  31. Global.Path.log = dir
  32. const list = Array.from({ length: 12 }, (_, i) => `2000-01-${String(i + 1).padStart(2, "0")}T000000.log`)
  33. yield* Effect.all(list.map((file) => Effect.promise(() => fs.writeFile(path.join(dir, file), file))))
  34. yield* Effect.promise(() => Log.init({ print: false, dev: false }))
  35. const next = yield* files(dir)
  36. expect(next).not.toContain(list[0]!)
  37. expect(next).toContain(list.at(-1)!)
  38. }),
  39. )
  40. it.live("local dev log is not truncated twice for the same run", () =>
  41. Effect.gen(function* () {
  42. const log = Global.Path.log
  43. const runID = process.env.OPENCODE_RUN_ID
  44. const initialized = process.env.OPENCODE_LOG_INITIALIZED_RUN_ID
  45. yield* Effect.addFinalizer(() =>
  46. Effect.sync(() => {
  47. Global.Path.log = log
  48. if (runID === undefined) delete process.env.OPENCODE_RUN_ID
  49. else process.env.OPENCODE_RUN_ID = runID
  50. if (initialized === undefined) delete process.env.OPENCODE_LOG_INITIALIZED_RUN_ID
  51. else process.env.OPENCODE_LOG_INITIALIZED_RUN_ID = initialized
  52. }),
  53. )
  54. const dir = yield* tmpdirScoped()
  55. Global.Path.log = dir
  56. process.env.OPENCODE_RUN_ID = "run-1"
  57. delete process.env.OPENCODE_LOG_INITIALIZED_RUN_ID
  58. yield* Effect.promise(() => Log.init({ print: false, dev: true }))
  59. yield* Effect.promise(() => fs.writeFile(path.join(dir, "dev.log"), "main startup\n"))
  60. yield* Effect.promise(() => Log.init({ print: false, dev: true }))
  61. expect(yield* Effect.promise(() => fs.readFile(path.join(dir, "dev.log"), "utf8"))).toContain("main startup")
  62. }),
  63. )