app-runtime-logger.test.ts 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. import { expect } from "bun:test"
  2. import { LayerNode } from "@kirincode-ai/core/effect/layer-node"
  3. import { Context, Deferred, Effect, Fiber, Layer, Logger } from "effect"
  4. import { CrossSpawnSpawner } from "@kirincode-ai/core/cross-spawn-spawner"
  5. import { AppLayer } from "../../src/effect/app-runtime"
  6. import { EffectBridge } from "@/effect/bridge"
  7. import { InstanceRef } from "../../src/effect/instance-ref"
  8. import * as Observability from "@kirincode-ai/core/observability"
  9. import { attach } from "../../src/effect/run-service"
  10. import { TestInstance } from "../fixture/fixture"
  11. import { testEffect } from "../lib/effect"
  12. const observabilityLayer = LayerNode.compile(Observability.node)
  13. const it = testEffect(LayerNode.compile(CrossSpawnSpawner.node))
  14. function check(loggers: ReadonlySet<Logger.Logger<unknown, any>>) {
  15. return {
  16. tracerLogger: loggers.has(Logger.tracerLogger),
  17. size: loggers.size,
  18. }
  19. }
  20. it.live("makeRuntime installs the observability logger", () =>
  21. Effect.gen(function* () {
  22. class Dummy extends Context.Service<Dummy, { readonly current: () => Effect.Effect<ReturnType<typeof check>> }>()(
  23. "@test/Dummy",
  24. ) {}
  25. const layer = Layer.effect(
  26. Dummy,
  27. Effect.gen(function* () {
  28. return Dummy.of({
  29. current: () => Effect.map(Effect.service(Logger.CurrentLoggers), check),
  30. })
  31. }),
  32. )
  33. const current = yield* Dummy.use((svc) => svc.current()).pipe(
  34. Effect.provide(Layer.provideMerge(layer, observabilityLayer)),
  35. )
  36. expect(current.size).toBeGreaterThan(0)
  37. }),
  38. )
  39. it.live("AppLayer also installs the observability logger", () =>
  40. Effect.gen(function* () {
  41. const current = yield* Effect.map(Effect.service(Logger.CurrentLoggers), check).pipe(Effect.provide(AppLayer))
  42. expect(current.size).toBeGreaterThan(0)
  43. }),
  44. )
  45. it.instance(
  46. "attach preserves InstanceRef from the current fiber context",
  47. () =>
  48. Effect.gen(function* () {
  49. const test = yield* TestInstance
  50. const current = yield* attach(
  51. Effect.gen(function* () {
  52. return (yield* InstanceRef)?.directory
  53. }),
  54. )
  55. expect(current).toBe(test.directory)
  56. }),
  57. { git: true },
  58. )
  59. it.instance(
  60. "EffectBridge preserves logger and instance context across async boundaries",
  61. () =>
  62. Effect.gen(function* () {
  63. const test = yield* TestInstance
  64. const bridge = yield* EffectBridge.make()
  65. const started = yield* Deferred.make<void>()
  66. const fiber = yield* Effect.gen(function* () {
  67. yield* Deferred.succeed(started, undefined)
  68. return yield* Effect.promise(() =>
  69. Promise.resolve().then(() =>
  70. bridge.promise(
  71. Effect.gen(function* () {
  72. return {
  73. directory: (yield* InstanceRef)?.directory,
  74. ...check(yield* Effect.service(Logger.CurrentLoggers)),
  75. }
  76. }),
  77. ),
  78. ),
  79. )
  80. }).pipe(Effect.forkScoped)
  81. yield* Deferred.await(started)
  82. const result = yield* Fiber.join(fiber)
  83. expect(result.directory).toBe(test.directory)
  84. expect(result.size).toBeGreaterThan(0)
  85. }).pipe(Effect.provide(observabilityLayer)),
  86. { git: true },
  87. )