106 lines
4.0 KiB
TypeScript
106 lines
4.0 KiB
TypeScript
import { describe, it } from "@effect/vitest"
|
|
import { deepStrictEqual, strictEqual } from "@effect/vitest/utils"
|
|
import { Deferred, Effect, Exit, Fiber, pipe, SynchronizedRef } from "effect"
|
|
|
|
const current = "value"
|
|
const update = "new value"
|
|
const failure = "failure"
|
|
|
|
type State = Active | Changed | Closed
|
|
|
|
interface Active {
|
|
readonly _tag: "Active"
|
|
}
|
|
|
|
interface Changed {
|
|
readonly _tag: "Changed"
|
|
}
|
|
|
|
interface Closed {
|
|
readonly _tag: "Closed"
|
|
}
|
|
|
|
export const Active: State = { _tag: "Active" }
|
|
export const Changed: State = { _tag: "Changed" }
|
|
export const Closed: State = { _tag: "Closed" }
|
|
|
|
const isActive = (self: State): boolean => self._tag === "Active"
|
|
const isChanged = (self: State): boolean => self._tag === "Changed"
|
|
const isClosed = (self: State): boolean => self._tag === "Closed"
|
|
|
|
describe("SynchronizedRef", () => {
|
|
it.effect("get", () =>
|
|
Effect.gen(function*() {
|
|
const result = yield* pipe(SynchronizedRef.make(current), Effect.flatMap(SynchronizedRef.get))
|
|
strictEqual(result, current)
|
|
}))
|
|
it.effect("getAndUpdateEffect - happy path", () =>
|
|
Effect.gen(function*() {
|
|
const ref = yield* SynchronizedRef.make(current)
|
|
const result1 = yield* SynchronizedRef.getAndUpdateEffect(ref, () => Effect.succeed(update))
|
|
const result2 = yield* SynchronizedRef.get(ref)
|
|
strictEqual(result1, current)
|
|
strictEqual(result2, update)
|
|
}))
|
|
it.effect("getAndUpdateEffect - with failure", () =>
|
|
Effect.gen(function*() {
|
|
const ref = yield* SynchronizedRef.make(current)
|
|
const result = yield* pipe(SynchronizedRef.getAndUpdateEffect(ref, (_) => Effect.fail(failure)), Effect.exit)
|
|
deepStrictEqual(result, Exit.fail(failure))
|
|
}))
|
|
it.effect("getAndUpdateSomeEffect - happy path", () =>
|
|
Effect.gen(function*() {
|
|
const ref = yield* SynchronizedRef.make<State>(Active)
|
|
const result1 = yield* (SynchronizedRef.getAndUpdateSomeEffect(ref, (state) =>
|
|
isClosed(state) ?
|
|
Effect.succeedSome(Changed) :
|
|
Effect.succeedNone))
|
|
const result2 = yield* SynchronizedRef.get(ref)
|
|
deepStrictEqual(result1, Active)
|
|
deepStrictEqual(result2, Active)
|
|
}))
|
|
it.effect("getAndUpdateSomeEffect - twice", () =>
|
|
Effect.gen(function*() {
|
|
const ref = yield* SynchronizedRef.make<State>(Active)
|
|
const result1 = yield* SynchronizedRef.getAndUpdateSomeEffect(ref, (state) =>
|
|
isActive(state) ?
|
|
Effect.succeedSome(Changed) :
|
|
Effect.succeedNone)
|
|
const result2 = yield* SynchronizedRef.getAndUpdateSomeEffect(ref, (state) =>
|
|
isClosed(state)
|
|
? Effect.succeedSome(Active)
|
|
: isChanged(state)
|
|
? Effect.succeedSome(Closed)
|
|
: Effect.succeedNone)
|
|
const result3 = yield* SynchronizedRef.get(ref)
|
|
deepStrictEqual(result1, Active)
|
|
deepStrictEqual(result2, Changed)
|
|
deepStrictEqual(result3, Closed)
|
|
}))
|
|
it.effect("getAndUpdateSomeEffect - with failure", () =>
|
|
Effect.gen(function*() {
|
|
const ref = yield* SynchronizedRef.make<State>(Active)
|
|
const result = yield* pipe(
|
|
SynchronizedRef.getAndUpdateSomeEffect(ref, (state) =>
|
|
isActive(state) ?
|
|
Effect.fail(failure) :
|
|
Effect.succeedNone),
|
|
Effect.exit
|
|
)
|
|
deepStrictEqual(result, Exit.fail(failure))
|
|
}))
|
|
it.effect("getAndUpdateSomeEffect - interrupt parent fiber and update", () =>
|
|
Effect.gen(function*() {
|
|
const deferred = yield* Deferred.make<SynchronizedRef.SynchronizedRef<State>>()
|
|
const latch = yield* Deferred.make<void>()
|
|
const makeAndWait = Deferred.complete(deferred, SynchronizedRef.make<State>(Active)).pipe(
|
|
Effect.andThen(Deferred.await(latch))
|
|
)
|
|
const fiber = yield* Effect.forkChild(makeAndWait)
|
|
const ref = yield* Deferred.await(deferred)
|
|
yield* Fiber.interrupt(fiber)
|
|
const result = yield* SynchronizedRef.updateAndGetEffect(ref, (_) => Effect.succeed(Closed))
|
|
deepStrictEqual(result, Closed)
|
|
}))
|
|
})
|