73
packages/effect-fc/src/PropertyPath.test.ts
Normal file
73
packages/effect-fc/src/PropertyPath.test.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
import { describe, expect, test } from "bun:test"
|
||||
import { Option } from "effect"
|
||||
import * as PropertyPath from "./PropertyPath.ts"
|
||||
|
||||
|
||||
describe("immutableSet with arrays", () => {
|
||||
test("sets a top-level array element", () => {
|
||||
const arr = [1, 2, 3]
|
||||
const result = PropertyPath.immutableSet(arr, [1], 99)
|
||||
expect(result).toEqual(Option.some([1, 99, 3]))
|
||||
})
|
||||
|
||||
test("does not mutate the original array", () => {
|
||||
const arr = [1, 2, 3]
|
||||
PropertyPath.immutableSet(arr, [0], 42)
|
||||
expect(arr).toEqual([1, 2, 3])
|
||||
})
|
||||
|
||||
test("sets the first element of an array", () => {
|
||||
const arr = ["a", "b", "c"]
|
||||
const result = PropertyPath.immutableSet(arr, [0], "z")
|
||||
expect(result).toEqual(Option.some(["z", "b", "c"]))
|
||||
})
|
||||
|
||||
test("sets the last element of an array", () => {
|
||||
const arr = [10, 20, 30]
|
||||
const result = PropertyPath.immutableSet(arr, [2], 99)
|
||||
expect(result).toEqual(Option.some([10, 20, 99]))
|
||||
})
|
||||
|
||||
test("sets a nested array element inside an object", () => {
|
||||
const obj = { tags: ["foo", "bar", "baz"] }
|
||||
const result = PropertyPath.immutableSet(obj, ["tags", 1], "qux")
|
||||
expect(result).toEqual(Option.some({ tags: ["foo", "qux", "baz"] }))
|
||||
})
|
||||
|
||||
test("sets a deeply nested value inside an array of objects", () => {
|
||||
const obj = { items: [{ name: "alice" }, { name: "bob" }] }
|
||||
const result = PropertyPath.immutableSet(obj, ["items", 0, "name"], "charlie")
|
||||
expect(result).toEqual(Option.some({ items: [{ name: "charlie" }, { name: "bob" }] }))
|
||||
})
|
||||
|
||||
test("sets a value in a nested array", () => {
|
||||
const matrix = [[1, 2], [3, 4]]
|
||||
const result = PropertyPath.immutableSet(matrix, [1, 0], 99)
|
||||
expect(result).toEqual(Option.some([[1, 2], [99, 4]]))
|
||||
})
|
||||
|
||||
test("returns Option.none() for an out-of-bounds index", () => {
|
||||
const arr = [1, 2, 3]
|
||||
const result = PropertyPath.immutableSet(arr, [5], 99)
|
||||
expect(result).toEqual(Option.none())
|
||||
})
|
||||
|
||||
test("returns Option.none() for a non-numeric key on an array", () => {
|
||||
const arr = [1, 2, 3]
|
||||
// @ts-expect-error intentionally wrong key type
|
||||
const result = PropertyPath.immutableSet(arr, ["length"], 0)
|
||||
expect(result).toEqual(Option.none())
|
||||
})
|
||||
|
||||
test("curried form works with arrays", () => {
|
||||
const setSecond = PropertyPath.immutableSet([1], 42)
|
||||
const result = setSecond([10, 20, 30])
|
||||
expect(result).toEqual(Option.some([10, 42, 30]))
|
||||
})
|
||||
|
||||
test("empty path returns Option.some of the value itself", () => {
|
||||
const arr = [1, 2, 3]
|
||||
const result = PropertyPath.immutableSet(arr, [], [9, 9, 9] as any)
|
||||
expect(result).toEqual(Option.some([9, 9, 9]))
|
||||
})
|
||||
})
|
||||
183
packages/effect-fc/src/SubscriptionSubRef.test.ts
Normal file
183
packages/effect-fc/src/SubscriptionSubRef.test.ts
Normal file
@@ -0,0 +1,183 @@
|
||||
import { describe, expect, test } from "bun:test"
|
||||
import { Chunk, Effect, Ref, SubscriptionRef } from "effect"
|
||||
import * as SubscriptionSubRef from "./SubscriptionSubRef.ts"
|
||||
|
||||
|
||||
describe("SubscriptionSubRef with array refs", () => {
|
||||
test("creates a subref for a single array element using path", async () => {
|
||||
const value = await Effect.runPromise(
|
||||
Effect.flatMap(
|
||||
SubscriptionRef.make([{ name: "alice" }, { name: "bob" }, { name: "charlie" }]),
|
||||
parent => {
|
||||
const subref = SubscriptionSubRef.makeFromPath(parent, [1, "name"])
|
||||
return subref.get
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
expect(value).toBe("bob")
|
||||
})
|
||||
|
||||
test("modifies a single array element via subref", async () => {
|
||||
const result = await Effect.runPromise(
|
||||
Effect.flatMap(
|
||||
SubscriptionRef.make([{ name: "alice" }, { name: "bob" }, { name: "charlie" }]),
|
||||
parent => {
|
||||
const subref = SubscriptionSubRef.makeFromPath(parent, [1, "name"])
|
||||
return Effect.flatMap(
|
||||
Ref.set(subref, "bob-updated"),
|
||||
() => Ref.get(parent),
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
expect(result).toEqual([{ name: "alice" }, { name: "bob-updated" }, { name: "charlie" }])
|
||||
})
|
||||
|
||||
test("modifies array element at index 0", async () => {
|
||||
const result = await Effect.runPromise(
|
||||
Effect.flatMap(
|
||||
SubscriptionRef.make([10, 20, 30]),
|
||||
parent => {
|
||||
const subref = SubscriptionSubRef.makeFromPath(parent, [0])
|
||||
return Effect.flatMap(
|
||||
Ref.set(subref, 99),
|
||||
() => Ref.get(parent),
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
expect(result).toEqual([99, 20, 30])
|
||||
})
|
||||
|
||||
test("modifies array element at last index", async () => {
|
||||
const result = await Effect.runPromise(
|
||||
Effect.flatMap(
|
||||
SubscriptionRef.make(["a", "b", "c"]),
|
||||
parent => {
|
||||
const subref = SubscriptionSubRef.makeFromPath(parent, [2])
|
||||
return Effect.flatMap(
|
||||
Ref.set(subref, "z"),
|
||||
() => Ref.get(parent),
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
expect(result).toEqual(["a", "b", "z"])
|
||||
})
|
||||
|
||||
test("modifies nested array element", async () => {
|
||||
const result = await Effect.runPromise(
|
||||
Effect.flatMap(
|
||||
SubscriptionRef.make([[1, 2], [3, 4], [5, 6]]),
|
||||
parent => {
|
||||
const subref = SubscriptionSubRef.makeFromPath(parent, [1, 0])
|
||||
return Effect.flatMap(
|
||||
Ref.set(subref, 99),
|
||||
() => Ref.get(parent),
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
expect(result).toEqual([[1, 2], [99, 4], [5, 6]])
|
||||
})
|
||||
|
||||
test("uses modifyEffect to transform array element", async () => {
|
||||
const result = await Effect.runPromise(
|
||||
Effect.flatMap(
|
||||
SubscriptionRef.make([{ count: 1 }, { count: 2 }, { count: 3 }]),
|
||||
parent => {
|
||||
const subref = SubscriptionSubRef.makeFromPath(parent, [1, "count"])
|
||||
return Effect.flatMap(
|
||||
Ref.update(subref, count => count + 100),
|
||||
() => Effect.map(Ref.get(parent), parentValue => ({ result: 102, parentValue })),
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
expect(result.result).toBe(102) // count + 100
|
||||
expect(result.parentValue).toEqual([{ count: 1 }, { count: 102 }, { count: 3 }]) // count + 100
|
||||
})
|
||||
|
||||
test("uses modify to transform array element", async () => {
|
||||
const result = await Effect.runPromise(
|
||||
Effect.flatMap(
|
||||
SubscriptionRef.make([10, 20, 30]),
|
||||
parent => {
|
||||
const subref = SubscriptionSubRef.makeFromPath(parent, [1])
|
||||
return Effect.flatMap(
|
||||
Ref.update(subref, x => x + 5),
|
||||
() => Effect.map(Ref.get(parent), parentValue => ({ result: 25, parentValue })),
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
expect(result.result).toBe(25) // 20 + 5
|
||||
expect(result.parentValue).toEqual([10, 25, 30]) // 20 + 5
|
||||
})
|
||||
|
||||
test("makeFromChunkIndex modifies chunk element", async () => {
|
||||
const result = await Effect.runPromise(
|
||||
Effect.flatMap(
|
||||
SubscriptionRef.make(Chunk.make(100, 200, 300)),
|
||||
parent => {
|
||||
const subref = SubscriptionSubRef.makeFromChunkIndex(parent, 1)
|
||||
return Effect.flatMap(
|
||||
Ref.set(subref, 999),
|
||||
() => Ref.get(parent),
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
expect(Chunk.toReadonlyArray(result)).toEqual([100, 999, 300])
|
||||
})
|
||||
|
||||
test("makeFromGetSet with custom getter/setter for array element", async () => {
|
||||
const result = await Effect.runPromise(
|
||||
Effect.flatMap(
|
||||
SubscriptionRef.make([{ id: 1, value: "a" }, { id: 2, value: "b" }]),
|
||||
parent => {
|
||||
const subref = SubscriptionSubRef.makeFromGetSet(parent, {
|
||||
get: arr => arr[0].value,
|
||||
set: (arr, newValue) => [
|
||||
{ ...arr[0], value: newValue },
|
||||
...arr.slice(1),
|
||||
],
|
||||
})
|
||||
return Effect.flatMap(
|
||||
Ref.set(subref, "updated"),
|
||||
() => Ref.get(parent),
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
expect(result).toEqual([{ id: 1, value: "updated" }, { id: 2, value: "b" }])
|
||||
})
|
||||
|
||||
test("does not mutate original array when modifying via subref", async () => {
|
||||
const original = [{ name: "alice" }, { name: "bob" }]
|
||||
const result = await Effect.runPromise(
|
||||
Effect.flatMap(
|
||||
SubscriptionRef.make(original),
|
||||
parent => {
|
||||
const subref = SubscriptionSubRef.makeFromPath(parent, [0, "name"])
|
||||
return Effect.flatMap(
|
||||
Ref.set(subref, "alice-updated"),
|
||||
() => Ref.get(parent),
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
expect(original).toEqual([{ name: "alice" }, { name: "bob" }]) // original unchanged
|
||||
expect(result).toEqual([{ name: "alice-updated" }, { name: "bob" }]) // new value in ref
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user