198 lines
7.4 KiB
TypeScript
198 lines
7.4 KiB
TypeScript
import { assert, describe, it } from "@effect/vitest"
|
|
import { Effect, Stream, TxQueue, TxSubscriptionRef } from "effect"
|
|
|
|
describe("TxSubscriptionRef", () => {
|
|
describe("constructors", () => {
|
|
it.effect("make creates ref with initial value", () =>
|
|
Effect.tx(Effect.gen(function*() {
|
|
const ref = yield* TxSubscriptionRef.make(42)
|
|
const value = yield* TxSubscriptionRef.get(ref)
|
|
assert.strictEqual(value, 42)
|
|
})))
|
|
})
|
|
|
|
describe("mutations", () => {
|
|
it.effect("set updates the value", () =>
|
|
Effect.tx(Effect.gen(function*() {
|
|
const ref = yield* TxSubscriptionRef.make(0)
|
|
yield* TxSubscriptionRef.set(ref, 10)
|
|
assert.strictEqual(yield* TxSubscriptionRef.get(ref), 10)
|
|
})))
|
|
|
|
it.effect("update applies a function", () =>
|
|
Effect.tx(Effect.gen(function*() {
|
|
const ref = yield* TxSubscriptionRef.make(5)
|
|
yield* TxSubscriptionRef.update(ref, (n) => n * 2)
|
|
assert.strictEqual(yield* TxSubscriptionRef.get(ref), 10)
|
|
})))
|
|
|
|
it.effect("modify returns a value and updates", () =>
|
|
Effect.tx(Effect.gen(function*() {
|
|
const ref = yield* TxSubscriptionRef.make(10)
|
|
const result = yield* TxSubscriptionRef.modify(ref, (n) => [`was ${n}`, n + 1])
|
|
assert.strictEqual(result, "was 10")
|
|
assert.strictEqual(yield* TxSubscriptionRef.get(ref), 11)
|
|
})))
|
|
|
|
it.effect("getAndSet returns old value and sets new", () =>
|
|
Effect.tx(Effect.gen(function*() {
|
|
const ref = yield* TxSubscriptionRef.make("a")
|
|
const old = yield* TxSubscriptionRef.getAndSet(ref, "b")
|
|
assert.strictEqual(old, "a")
|
|
assert.strictEqual(yield* TxSubscriptionRef.get(ref), "b")
|
|
})))
|
|
|
|
it.effect("getAndUpdate returns old value and applies function", () =>
|
|
Effect.tx(Effect.gen(function*() {
|
|
const ref = yield* TxSubscriptionRef.make(1)
|
|
const old = yield* TxSubscriptionRef.getAndUpdate(ref, (n) => n + 10)
|
|
assert.strictEqual(old, 1)
|
|
assert.strictEqual(yield* TxSubscriptionRef.get(ref), 11)
|
|
})))
|
|
|
|
it.effect("updateAndGet applies function and returns new value", () =>
|
|
Effect.tx(Effect.gen(function*() {
|
|
const ref = yield* TxSubscriptionRef.make(3)
|
|
const result = yield* TxSubscriptionRef.updateAndGet(ref, (n) => n * 3)
|
|
assert.strictEqual(result, 9)
|
|
assert.strictEqual(yield* TxSubscriptionRef.get(ref), 9)
|
|
})))
|
|
})
|
|
|
|
describe("subscriptions", () => {
|
|
it.effect("changes includes current value as first element", () =>
|
|
Effect.gen(function*() {
|
|
const ref = yield* Effect.tx(TxSubscriptionRef.make(100))
|
|
|
|
yield* Effect.scoped(
|
|
Effect.gen(function*() {
|
|
const sub = yield* TxSubscriptionRef.changes(ref)
|
|
const first = yield* Effect.tx(TxQueue.take(sub))
|
|
assert.strictEqual(first, 100)
|
|
})
|
|
)
|
|
}))
|
|
|
|
it.effect("changes receives updates in order", () =>
|
|
Effect.gen(function*() {
|
|
const ref = yield* Effect.tx(TxSubscriptionRef.make(0))
|
|
|
|
yield* Effect.scoped(
|
|
Effect.gen(function*() {
|
|
const sub = yield* TxSubscriptionRef.changes(ref)
|
|
// Take the initial value
|
|
const initial = yield* Effect.tx(TxQueue.take(sub))
|
|
assert.strictEqual(initial, 0)
|
|
|
|
yield* Effect.tx(TxSubscriptionRef.set(ref, 1))
|
|
yield* Effect.tx(TxSubscriptionRef.set(ref, 2))
|
|
yield* Effect.tx(TxSubscriptionRef.set(ref, 3))
|
|
|
|
assert.strictEqual(yield* Effect.tx(TxQueue.take(sub)), 1)
|
|
assert.strictEqual(yield* Effect.tx(TxQueue.take(sub)), 2)
|
|
assert.strictEqual(yield* Effect.tx(TxQueue.take(sub)), 3)
|
|
})
|
|
)
|
|
}))
|
|
|
|
it.effect("multiple subscribers each see all changes", () =>
|
|
Effect.gen(function*() {
|
|
const ref = yield* Effect.tx(TxSubscriptionRef.make(0))
|
|
|
|
yield* Effect.scoped(
|
|
Effect.gen(function*() {
|
|
const sub1 = yield* TxSubscriptionRef.changes(ref)
|
|
const sub2 = yield* TxSubscriptionRef.changes(ref)
|
|
|
|
// Both get the initial value
|
|
assert.strictEqual(yield* Effect.tx(TxQueue.take(sub1)), 0)
|
|
assert.strictEqual(yield* Effect.tx(TxQueue.take(sub2)), 0)
|
|
|
|
yield* Effect.tx(TxSubscriptionRef.set(ref, 42))
|
|
|
|
assert.strictEqual(yield* Effect.tx(TxQueue.take(sub1)), 42)
|
|
assert.strictEqual(yield* Effect.tx(TxQueue.take(sub2)), 42)
|
|
})
|
|
)
|
|
}))
|
|
|
|
it.effect("failed transactions do not publish rolled back values", () =>
|
|
Effect.gen(function*() {
|
|
const ref = yield* Effect.tx(TxSubscriptionRef.make(0))
|
|
|
|
// Collect initial value only (take 1 before any updates)
|
|
const initial = yield* Stream.runCollect(
|
|
TxSubscriptionRef.changesStream(ref).pipe(Stream.take(1))
|
|
)
|
|
assert.deepStrictEqual(initial, [0])
|
|
}))
|
|
|
|
it.effect("changesStream starts from the current value for each subscriber", () =>
|
|
Effect.gen(function*() {
|
|
const ref = yield* Effect.tx(TxSubscriptionRef.make(0))
|
|
|
|
yield* Effect.tx(TxSubscriptionRef.set(ref, 1))
|
|
yield* Effect.tx(TxSubscriptionRef.set(ref, 2))
|
|
|
|
const values = yield* Stream.runCollect(
|
|
TxSubscriptionRef.changesStream(ref).pipe(Stream.take(1))
|
|
)
|
|
// Stream starts fresh, so takes the current value (2)
|
|
assert.deepStrictEqual(values, [2])
|
|
}))
|
|
|
|
it.effect("scope close releases the subscriber queue", () =>
|
|
Effect.gen(function*() {
|
|
const ref = yield* Effect.tx(TxSubscriptionRef.make(0))
|
|
|
|
yield* Effect.scoped(
|
|
Effect.gen(function*() {
|
|
yield* TxSubscriptionRef.changes(ref)
|
|
// Subscriber exists within scope
|
|
})
|
|
)
|
|
|
|
// After scope closes, setting values should not fail
|
|
yield* Effect.tx(TxSubscriptionRef.set(ref, 1))
|
|
assert.strictEqual(yield* Effect.tx(TxSubscriptionRef.get(ref)), 1)
|
|
}))
|
|
})
|
|
|
|
describe("pipe support", () => {
|
|
it.effect("set data-last style", () =>
|
|
Effect.tx(Effect.gen(function*() {
|
|
const ref = yield* TxSubscriptionRef.make(0)
|
|
yield* ref.pipe(TxSubscriptionRef.set(99))
|
|
assert.strictEqual(yield* TxSubscriptionRef.get(ref), 99)
|
|
})))
|
|
|
|
it.effect("update data-last style", () =>
|
|
Effect.tx(Effect.gen(function*() {
|
|
const ref = yield* TxSubscriptionRef.make(5)
|
|
yield* ref.pipe(TxSubscriptionRef.update((n: number) => n + 1))
|
|
assert.strictEqual(yield* TxSubscriptionRef.get(ref), 6)
|
|
})))
|
|
|
|
it.effect("modify data-last style", () =>
|
|
Effect.tx(Effect.gen(function*() {
|
|
const ref = yield* TxSubscriptionRef.make(10)
|
|
const result = yield* ref.pipe(
|
|
TxSubscriptionRef.modify((n: number) => [n * 2, n + 1])
|
|
)
|
|
assert.strictEqual(result, 20)
|
|
assert.strictEqual(yield* TxSubscriptionRef.get(ref), 11)
|
|
})))
|
|
})
|
|
|
|
describe("type guards", () => {
|
|
it.effect("isTxSubscriptionRef identifies instances", () =>
|
|
Effect.tx(Effect.gen(function*() {
|
|
const ref = yield* TxSubscriptionRef.make(0)
|
|
assert.isTrue(TxSubscriptionRef.isTxSubscriptionRef(ref))
|
|
assert.isFalse(TxSubscriptionRef.isTxSubscriptionRef({}))
|
|
assert.isFalse(TxSubscriptionRef.isTxSubscriptionRef(null))
|
|
assert.isFalse(TxSubscriptionRef.isTxSubscriptionRef(undefined))
|
|
})))
|
|
})
|
|
})
|