1import { describe, it } from "@effect/vitest"
2import { deepStrictEqual, strictEqual } from "@effect/vitest/utils"
3import { Deferred, Effect, Exit, Fiber, Option, pipe, SynchronizedRef } from "effect"
4
5const current = "value"
6const update = "new value"
7const failure = "failure"
8
9type State = Active | Changed | Closed
10
11interface Active {
12 readonly _tag: "Active"
13}
14
15interface Changed {
16 readonly _tag: "Changed"
17}
18
19interface Closed {
20 readonly _tag: "Closed"
21}
22
23export const Active: State = { _tag: "Active" }
24export const Changed: State = { _tag: "Changed" }
25export const Closed: State = { _tag: "Closed" }
26
27const isActive = (self: State): boolean => self._tag === "Active"
28const isChanged = (self: State): boolean => self._tag === "Changed"
29const isClosed = (self: State): boolean => self._tag === "Closed"
30
31describe("SynchronizedRef", () => {
32 it.effect("get", () =>
33 Effect.gen(function*() {
34 const result = yield* pipe(SynchronizedRef.make(current), Effect.flatMap(SynchronizedRef.get))
35 strictEqual(result, current)
36 }))
37 it.effect("getAndUpdateEffect - happy path", () =>
38 Effect.gen(function*() {
39 const ref = yield* SynchronizedRef.make(current)
40 const result1 = yield* SynchronizedRef.getAndUpdateEffect(ref, () => Effect.succeed(update))
41 const result2 = yield* SynchronizedRef.get(ref)
42 strictEqual(result1, current)
43 strictEqual(result2, update)
44 }))
45 it.effect("getAndUpdateEffect - with failure", () =>
46 Effect.gen(function*() {
47 const ref = yield* SynchronizedRef.make(current)
48 const result = yield* pipe(SynchronizedRef.getAndUpdateEffect(ref, (_) => Effect.fail(failure)), Effect.exit)
49 deepStrictEqual(result, Exit.fail(failure))
50 }))
51 it.effect("getAndUpdateSomeEffect - happy path", () =>
52 Effect.gen(function*() {
53 const ref = yield* SynchronizedRef.make<State>(Active)
54 const result1 = yield* (SynchronizedRef.getAndUpdateSomeEffect(ref, (state) =>
55 isClosed(state) ?
56 Effect.succeedSome(Changed) :
57 Effect.succeedNone))
58 const result2 = yield* SynchronizedRef.get(ref)
59 deepStrictEqual(result1, Active)
60 deepStrictEqual(result2, Active)
61 }))
62 it.effect("getAndUpdateSomeEffect - twice", () =>
63 Effect.gen(function*() {
64 const ref = yield* SynchronizedRef.make<State>(Active)
65 const result1 = yield* SynchronizedRef.getAndUpdateSomeEffect(ref, (state) =>
66 isActive(state) ?
67 Effect.succeedSome(Changed) :
68 Effect.succeedNone)
69 const result2 = yield* SynchronizedRef.getAndUpdateSomeEffect(ref, (state) =>
70 isClosed(state)
71 ? Effect.succeedSome(Active)
72 : isChanged(state)
73 ? Effect.succeedSome(Closed)
74 : Effect.succeedNone)
75 const result3 = yield* SynchronizedRef.get(ref)
76 deepStrictEqual(result1, Active)
77 deepStrictEqual(result2, Changed)
78 deepStrictEqual(result3, Closed)
79 }))
80 it.effect("getAndUpdateSomeEffect - with failure", () =>
81 Effect.gen(function*() {
82 const ref = yield* SynchronizedRef.make<State>(Active)
83 const result = yield* pipe(
84 SynchronizedRef.getAndUpdateSomeEffect(ref, (state) =>
85 isActive(state) ?
86 Effect.fail(failure) :
87 Effect.succeedNone),
88 Effect.exit
89 )
90 deepStrictEqual(result, Exit.fail(failure))
91 }))
92 it.effect("getAndUpdateSomeEffect - interrupt parent fiber and update", () =>
93 Effect.gen(function*() {
94 const deferred = yield* Deferred.make<SynchronizedRef.SynchronizedRef<State>>()
95 const latch = yield* Deferred.make<void>()
96 const makeAndWait = Deferred.complete(deferred, SynchronizedRef.make<State>(Active)).pipe(
97 Effect.andThen(Deferred.await(latch))
98 )
99 const fiber = yield* Effect.forkChild(makeAndWait)
100 const ref = yield* Deferred.await(deferred)
101 yield* Fiber.interrupt(fiber)
102 const result = yield* SynchronizedRef.updateAndGetEffect(ref, (_) => Effect.succeed(Closed))
103 deepStrictEqual(result, Closed)
104 }))
105 it.effect("getAndUpdateSome updates the backing ref", () =>
106 Effect.gen(function*() {
107 const ref = yield* SynchronizedRef.make(1)
108 const previous = yield* SynchronizedRef.getAndUpdateSome(ref, (n) => Option.some(n + 1))
109 strictEqual(previous, 1)
110 strictEqual(yield* SynchronizedRef.get(ref), 2)
111 }))
112})