instance-state.test.ts 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. import { afterEach, expect, test } from "bun:test"
  2. import { Effect } from "effect"
  3. import { Instance } from "../../src/project/instance"
  4. import { InstanceState } from "../../src/util/instance-state"
  5. import { tmpdir } from "../fixture/fixture"
  6. async function access<A, E>(state: InstanceState.State<A, E>, dir: string) {
  7. return Instance.provide({
  8. directory: dir,
  9. fn: () => Effect.runPromise(InstanceState.get(state)),
  10. })
  11. }
  12. afterEach(async () => {
  13. await Instance.disposeAll()
  14. })
  15. test("InstanceState caches values for the same instance", async () => {
  16. await using tmp = await tmpdir()
  17. let n = 0
  18. await Effect.runPromise(
  19. Effect.scoped(
  20. Effect.gen(function* () {
  21. const state = yield* InstanceState.make({
  22. lookup: () => Effect.sync(() => ({ n: ++n })),
  23. })
  24. const a = yield* Effect.promise(() => access(state, tmp.path))
  25. const b = yield* Effect.promise(() => access(state, tmp.path))
  26. expect(a).toBe(b)
  27. expect(n).toBe(1)
  28. }),
  29. ),
  30. )
  31. })
  32. test("InstanceState isolates values by directory", async () => {
  33. await using a = await tmpdir()
  34. await using b = await tmpdir()
  35. let n = 0
  36. await Effect.runPromise(
  37. Effect.scoped(
  38. Effect.gen(function* () {
  39. const state = yield* InstanceState.make({
  40. lookup: (dir) => Effect.sync(() => ({ dir, n: ++n })),
  41. })
  42. const x = yield* Effect.promise(() => access(state, a.path))
  43. const y = yield* Effect.promise(() => access(state, b.path))
  44. const z = yield* Effect.promise(() => access(state, a.path))
  45. expect(x).toBe(z)
  46. expect(x).not.toBe(y)
  47. expect(n).toBe(2)
  48. }),
  49. ),
  50. )
  51. })
  52. test("InstanceState is disposed on instance reload", async () => {
  53. await using tmp = await tmpdir()
  54. const seen: string[] = []
  55. let n = 0
  56. await Effect.runPromise(
  57. Effect.scoped(
  58. Effect.gen(function* () {
  59. const state = yield* InstanceState.make({
  60. lookup: () => Effect.sync(() => ({ n: ++n })),
  61. release: (value) =>
  62. Effect.sync(() => {
  63. seen.push(String(value.n))
  64. }),
  65. })
  66. const a = yield* Effect.promise(() => access(state, tmp.path))
  67. yield* Effect.promise(() => Instance.reload({ directory: tmp.path }))
  68. const b = yield* Effect.promise(() => access(state, tmp.path))
  69. expect(a).not.toBe(b)
  70. expect(seen).toEqual(["1"])
  71. }),
  72. ),
  73. )
  74. })
  75. test("InstanceState is disposed on disposeAll", async () => {
  76. await using a = await tmpdir()
  77. await using b = await tmpdir()
  78. const seen: string[] = []
  79. await Effect.runPromise(
  80. Effect.scoped(
  81. Effect.gen(function* () {
  82. const state = yield* InstanceState.make({
  83. lookup: (dir) => Effect.sync(() => ({ dir })),
  84. release: (value) =>
  85. Effect.sync(() => {
  86. seen.push(value.dir)
  87. }),
  88. })
  89. yield* Effect.promise(() => access(state, a.path))
  90. yield* Effect.promise(() => access(state, b.path))
  91. yield* Effect.promise(() => Instance.disposeAll())
  92. expect(seen.sort()).toEqual([a.path, b.path].sort())
  93. }),
  94. ),
  95. )
  96. })
  97. test("InstanceState dedupes concurrent lookups for the same directory", async () => {
  98. await using tmp = await tmpdir()
  99. let n = 0
  100. await Effect.runPromise(
  101. Effect.scoped(
  102. Effect.gen(function* () {
  103. const state = yield* InstanceState.make({
  104. lookup: () =>
  105. Effect.promise(async () => {
  106. n += 1
  107. await Bun.sleep(10)
  108. return { n }
  109. }),
  110. })
  111. const [a, b] = yield* Effect.promise(() => Promise.all([access(state, tmp.path), access(state, tmp.path)]))
  112. expect(a).toBe(b)
  113. expect(n).toBe(1)
  114. }),
  115. ),
  116. )
  117. })