679 lines
23 KiB
TypeScript
679 lines
23 KiB
TypeScript
import { assert, describe, it } from "@effect/vitest"
|
|
import { Effect, Exit, Fiber } from "effect"
|
|
import { TestClock } from "effect/testing"
|
|
|
|
describe("Effect Eager Operations", () => {
|
|
describe("fnUntracedEager", () => {
|
|
describe("eager computation", () => {
|
|
it("computes sync effects immediately", () => {
|
|
let execCount = 0
|
|
const fn = Effect.fnUntracedEager(function*() {
|
|
execCount++
|
|
yield* Effect.succeed(1)
|
|
yield* Effect.succeed(2)
|
|
return execCount
|
|
})
|
|
|
|
const effect = fn()
|
|
|
|
assert.strictEqual(execCount, 1)
|
|
assert.strictEqual((effect as any)._tag, "Success")
|
|
assert.strictEqual((effect as any).value, 1)
|
|
})
|
|
|
|
it("computes failures immediately", () => {
|
|
const fn = Effect.fnUntracedEager(function*() {
|
|
yield* Effect.succeed(1)
|
|
// @effect-diagnostics-next-line missingReturnYieldStar:off
|
|
yield* Effect.fail("error")
|
|
return "unreachable"
|
|
})
|
|
|
|
const effect = fn()
|
|
assert.strictEqual((effect as any)._tag, "Failure")
|
|
})
|
|
|
|
it("stops at first async effect", () => {
|
|
let syncSteps = 0
|
|
let asyncReached = false
|
|
|
|
const fn = Effect.fnUntracedEager(function*() {
|
|
syncSteps++
|
|
yield* Effect.succeed(1)
|
|
syncSteps++
|
|
yield* Effect.promise(() => {
|
|
asyncReached = true
|
|
return Promise.resolve(2)
|
|
})
|
|
syncSteps++
|
|
return "done"
|
|
})
|
|
|
|
const effect = fn()
|
|
|
|
assert.strictEqual(syncSteps, 2)
|
|
assert.strictEqual(asyncReached, false)
|
|
assert.notStrictEqual((effect as any)._tag, "Success")
|
|
})
|
|
|
|
it("handles exceptions", () => {
|
|
const fn = Effect.fnUntracedEager(function*() {
|
|
yield* Effect.succeed(1)
|
|
throw new Error("sync error")
|
|
})
|
|
|
|
const effect = fn()
|
|
assert.strictEqual((effect as any)._tag, "Failure")
|
|
})
|
|
})
|
|
|
|
it.effect("executes sync generators", () =>
|
|
Effect.gen(function*() {
|
|
const fn = Effect.fnUntracedEager(function*() {
|
|
yield* Effect.succeed(1)
|
|
yield* Effect.succeed(2)
|
|
return "done"
|
|
})
|
|
|
|
const result = yield* fn()
|
|
assert.strictEqual(result, "done")
|
|
}))
|
|
|
|
it.effect("handles mixed sync/async effects", () =>
|
|
Effect.gen(function*() {
|
|
const fn = Effect.fnUntracedEager(function*() {
|
|
const a = yield* Effect.succeed(1)
|
|
const b = yield* Effect.promise(() => Promise.resolve(2))
|
|
return a + b
|
|
})
|
|
|
|
const result = yield* fn()
|
|
assert.strictEqual(result, 3)
|
|
}))
|
|
|
|
it.effect("handles failures", () =>
|
|
Effect.gen(function*() {
|
|
const fn = Effect.fnUntracedEager(function*() {
|
|
// @effect-diagnostics-next-line missingReturnYieldStar:off
|
|
yield* Effect.fail("error")
|
|
return "unreachable"
|
|
})
|
|
|
|
const exit = yield* Effect.exit(fn())
|
|
assert.isTrue(Exit.isFailure(exit))
|
|
}))
|
|
|
|
it.effect("works with arguments and this context", () =>
|
|
Effect.gen(function*() {
|
|
const obj = {
|
|
value: 10,
|
|
fn: Effect.fnUntracedEager(function*(this: { value: number }, x: number) {
|
|
const a = yield* Effect.succeed(x)
|
|
return a + this.value
|
|
})
|
|
}
|
|
|
|
const result = yield* obj.fn(5)
|
|
assert.strictEqual(result, 15)
|
|
}))
|
|
|
|
it.effect("works with pipeable operators", () =>
|
|
Effect.gen(function*() {
|
|
const fn = Effect.fnUntracedEager(
|
|
function*() {
|
|
yield* Effect.succeed(1)
|
|
return 10
|
|
},
|
|
Effect.map((x: number) => x * 2)
|
|
)
|
|
|
|
const result = yield* fn()
|
|
assert.strictEqual(result, 20)
|
|
}))
|
|
|
|
it("proxies body length", () => {
|
|
const fn = Effect.fnUntracedEager(function*(a: string, b: number, c: boolean) {
|
|
return c ? a.length + b : b
|
|
})
|
|
|
|
assert.strictEqual(fn.length, 3)
|
|
})
|
|
|
|
it.effect("re-execution creates fresh iterators", () =>
|
|
Effect.gen(function*() {
|
|
let execCount = 0
|
|
const fn = Effect.fnUntracedEager(function*() {
|
|
execCount++
|
|
yield* Effect.succeed(1)
|
|
yield* Effect.promise(() => Promise.resolve(2))
|
|
return execCount
|
|
})
|
|
|
|
const effect = fn()
|
|
|
|
const result1 = yield* effect
|
|
assert.strictEqual(result1, 1)
|
|
|
|
const result2 = yield* effect
|
|
assert.strictEqual(result2, 2)
|
|
assert.strictEqual(execCount, 2)
|
|
}))
|
|
|
|
it.effect("handles long sync chains efficiently", () =>
|
|
Effect.gen(function*() {
|
|
const fn = Effect.fnUntracedEager(function*() {
|
|
let sum = 0
|
|
for (let i = 0; i < 100; i++) {
|
|
const val = yield* Effect.succeed(i)
|
|
sum += val
|
|
}
|
|
return sum
|
|
})
|
|
|
|
const result = yield* fn()
|
|
assert.strictEqual(result, 4950) // sum of 0 to 99
|
|
}))
|
|
})
|
|
|
|
describe("mapEager", () => {
|
|
it.effect("successful effect", () =>
|
|
Effect.gen(function*() {
|
|
const result = yield* Effect.mapEager(Effect.succeed(5), (n) => n * 2)
|
|
assert.strictEqual(result, 10)
|
|
}))
|
|
|
|
it.effect("failed effect preserves failure", () =>
|
|
Effect.gen(function*() {
|
|
const effect = Effect.fail("error")
|
|
const mapped = Effect.mapEager(effect, (n) => n * 2)
|
|
const exit = yield* Effect.exit(mapped)
|
|
|
|
assert.strictEqual(exit._tag, "Failure", "Expected effect to fail")
|
|
assert.ok(exit._tag === "Failure", "Type guard for exit failure")
|
|
|
|
const failure = exit.cause.reasons.find((failure: any) => failure._tag === "Fail")
|
|
assert.ok(failure, "Expected to find a Fail cause")
|
|
assert.strictEqual(failure._tag, "Fail", "Expected failure to be a Fail type")
|
|
assert.strictEqual((failure as any).error, "error", "Expected error to be preserved")
|
|
}))
|
|
|
|
it.effect("complex effect falls back to regular map", () =>
|
|
Effect.gen(function*() {
|
|
const effect = Effect.mapEager(Effect.delay(Effect.succeed(10), 1), (n) => n + 5)
|
|
const fiber = yield* Effect.forkChild(effect)
|
|
yield* TestClock.adjust(1)
|
|
const result = yield* Fiber.join(fiber)
|
|
assert.strictEqual(result, 15)
|
|
}))
|
|
})
|
|
|
|
describe("flatMapEager", () => {
|
|
it.effect("applies transformation eagerly for success effects", () =>
|
|
Effect.gen(function*() {
|
|
let executions = 0
|
|
const effect = Effect.succeed(5)
|
|
const flatMapped = Effect.flatMapEager(effect, (n) => {
|
|
executions++
|
|
return Effect.succeed(n * 2)
|
|
})
|
|
assert.strictEqual(yield* flatMapped, 10)
|
|
assert.strictEqual(yield* flatMapped, 10)
|
|
assert.strictEqual(yield* flatMapped, 10)
|
|
assert.strictEqual(executions, 1)
|
|
}))
|
|
|
|
it.effect("preserves failure for failed effects", () =>
|
|
Effect.gen(function*() {
|
|
const error = new Error("test error")
|
|
const effect = Effect.fail(error)
|
|
const flatMapped = Effect.flatMapEager(effect, (n) => Effect.succeed(n * 2))
|
|
const exit = yield* Effect.exit(flatMapped)
|
|
|
|
assert.strictEqual(exit._tag, "Failure", "Expected effect to fail")
|
|
assert.ok(exit._tag === "Failure", "Type guard for exit failure")
|
|
|
|
const failure = exit.cause.reasons.find((failure: any) => failure._tag === "Fail")
|
|
assert.ok(failure, "Expected to find a Fail cause")
|
|
assert.strictEqual(failure._tag, "Fail", "Expected failure to be a Fail type")
|
|
assert.strictEqual((failure as any).error, error, "Expected error to be preserved")
|
|
}))
|
|
|
|
it.effect("fallback to regular flatMap for complex effects", () =>
|
|
Effect.gen(function*() {
|
|
const effect = Effect.delay(Effect.succeed(10), "1 millis")
|
|
const flatMapped = Effect.flatMapEager(effect, (n) => Effect.succeed(n * 2))
|
|
|
|
const fiber = yield* Effect.forkChild(flatMapped)
|
|
yield* TestClock.adjust("1 millis")
|
|
const result = yield* Fiber.join(fiber)
|
|
|
|
assert.strictEqual(result, 20)
|
|
}))
|
|
})
|
|
|
|
describe("mapErrorEager", () => {
|
|
it.effect("preserves success for successful effects", () =>
|
|
Effect.gen(function*() {
|
|
const effect = Effect.succeed(5)
|
|
const mapped = Effect.mapErrorEager(effect, (err) => `mapped: ${err}`)
|
|
const result = yield* mapped
|
|
assert.strictEqual(result, 5)
|
|
}))
|
|
|
|
it.effect("applies transformation eagerly for failure effects", () =>
|
|
Effect.gen(function*() {
|
|
const effect = Effect.fail("original error")
|
|
const mapped = Effect.mapErrorEager(effect, (err) => `mapped: ${err}`)
|
|
const exit = yield* Effect.exit(mapped)
|
|
|
|
assert.strictEqual(exit._tag, "Failure", "Expected effect to fail")
|
|
assert.ok(exit._tag === "Failure", "Type guard for exit failure")
|
|
|
|
const failure = exit.cause.reasons.find((failure: any) => failure._tag === "Fail")
|
|
assert.ok(failure, "Expected to find a Fail cause")
|
|
assert.strictEqual(failure._tag, "Fail", "Expected failure to be a Fail type")
|
|
assert.strictEqual((failure as any).error, "mapped: original error", "Expected error to be transformed")
|
|
}))
|
|
|
|
it.effect("fallback to regular mapError for complex effects", () =>
|
|
Effect.gen(function*() {
|
|
const effect = Effect.delay(Effect.fail("error"), "1 millis")
|
|
const mapped = Effect.mapErrorEager(effect, (err) => `mapped: ${err}`)
|
|
|
|
const fiber = yield* Effect.forkChild(mapped)
|
|
yield* TestClock.adjust("1 millis")
|
|
const exit = yield* Fiber.await(fiber)
|
|
|
|
assert.strictEqual(exit._tag, "Failure", "Expected effect to fail")
|
|
assert.ok(exit._tag === "Failure", "Type guard for exit failure")
|
|
|
|
const failure = exit.cause.reasons.find((failure: any) => failure._tag === "Fail")
|
|
assert.ok(failure, "Expected to find a Fail cause")
|
|
assert.strictEqual(failure._tag, "Fail", "Expected failure to be a Fail type")
|
|
assert.strictEqual((failure as any).error, "mapped: error", "Expected error to be transformed")
|
|
}))
|
|
})
|
|
|
|
describe("mapBothEager", () => {
|
|
it.effect("applies onSuccess eagerly for success effects", () =>
|
|
Effect.gen(function*() {
|
|
const effect = Effect.succeed(5)
|
|
const mapped = Effect.mapBothEager(effect, {
|
|
onFailure: (err) => `Failed: ${err}`,
|
|
onSuccess: (n) => n * 2
|
|
})
|
|
const result = yield* mapped
|
|
assert.strictEqual(result, 10)
|
|
}))
|
|
|
|
it.effect("applies onFailure eagerly for failure effects", () =>
|
|
Effect.gen(function*() {
|
|
const effect = Effect.fail("original error")
|
|
const mapped = Effect.mapBothEager(effect, {
|
|
onFailure: (err) => `Failed: ${err}`,
|
|
onSuccess: (n) => n * 2
|
|
})
|
|
const exit = yield* Effect.exit(mapped)
|
|
|
|
assert.strictEqual(exit._tag, "Failure", "Expected effect to fail")
|
|
assert.ok(exit._tag === "Failure", "Type guard for exit failure")
|
|
|
|
const failure = exit.cause.reasons.find((failure: any) => failure._tag === "Fail")
|
|
assert.ok(failure, "Expected to find a Fail cause")
|
|
assert.strictEqual(failure._tag, "Fail", "Expected failure to be a Fail type")
|
|
assert.strictEqual((failure as any).error, "Failed: original error", "Expected error to be transformed")
|
|
}))
|
|
|
|
it.effect("fallback to regular mapBoth for complex effects", () =>
|
|
Effect.gen(function*() {
|
|
const effect = Effect.delay(Effect.succeed(10), "1 millis")
|
|
const mapped = Effect.mapBothEager(effect, {
|
|
onFailure: (err) => `Failed: ${err}`,
|
|
onSuccess: (n) => n * 2
|
|
})
|
|
|
|
const fiber = yield* Effect.forkChild(mapped)
|
|
yield* TestClock.adjust("1 millis")
|
|
const result = yield* Fiber.join(fiber)
|
|
|
|
assert.strictEqual(result, 20)
|
|
}))
|
|
})
|
|
|
|
describe("catchEager", () => {
|
|
it.effect("preserves success effects without applying catch", () =>
|
|
Effect.gen(function*() {
|
|
const effect = Effect.succeed(42)
|
|
const caught = Effect.catchEager(effect, (err) => Effect.succeed(`recovered: ${err}`))
|
|
const result = yield* caught
|
|
assert.strictEqual(result, 42)
|
|
}))
|
|
|
|
it.effect("applies catch function eagerly for failure effects", () =>
|
|
Effect.gen(function*() {
|
|
const effect = Effect.fail("original error")
|
|
const caught = Effect.catchEager(effect, (err) => Effect.succeed(`recovered: ${err}`))
|
|
const result = yield* caught
|
|
assert.strictEqual(result, "recovered: original error")
|
|
}))
|
|
|
|
it.effect("fallback to regular catch for complex effects", () =>
|
|
Effect.gen(function*() {
|
|
const effect = Effect.delay(Effect.fail("error"), "1 millis")
|
|
const caught = Effect.catchEager(effect, (err) => Effect.succeed(`recovered: ${err}`))
|
|
|
|
const fiber = yield* Effect.forkChild(caught)
|
|
yield* TestClock.adjust("1 millis")
|
|
const result = yield* Fiber.join(fiber)
|
|
|
|
assert.strictEqual(result, "recovered: error")
|
|
}))
|
|
})
|
|
|
|
describe("matchEager", () => {
|
|
it.effect("applies onSuccess eagerly for success effects", () =>
|
|
Effect.gen(function*() {
|
|
const effect = Effect.succeed(42)
|
|
const matched = Effect.matchEager(effect, {
|
|
onFailure: (err) => `Failed: ${err}`,
|
|
onSuccess: (n) => `Success: ${n}`
|
|
})
|
|
const result = yield* matched
|
|
assert.strictEqual(result, "Success: 42")
|
|
}))
|
|
|
|
it.effect("applies onFailure eagerly for failure effects", () =>
|
|
Effect.gen(function*() {
|
|
const effect = Effect.fail("original error")
|
|
const matched = Effect.matchEager(effect, {
|
|
onFailure: (err) => `Failed: ${err}`,
|
|
onSuccess: (n) => `Success: ${n}`
|
|
})
|
|
const result = yield* matched
|
|
assert.strictEqual(result, "Failed: original error")
|
|
}))
|
|
|
|
it.effect("fallback to regular match for complex effects", () =>
|
|
Effect.gen(function*() {
|
|
const effect = Effect.delay(Effect.succeed(10), "1 millis")
|
|
const matched = Effect.matchEager(effect, {
|
|
onFailure: (err) => `Failed: ${err}`,
|
|
onSuccess: (n) => `Success: ${n}`
|
|
})
|
|
|
|
const fiber = yield* Effect.forkChild(matched)
|
|
yield* TestClock.adjust("1 millis")
|
|
const result = yield* Fiber.join(fiber)
|
|
|
|
assert.strictEqual(result, "Success: 10")
|
|
}))
|
|
|
|
it.effect("eagerly handles complex failure scenarios", () =>
|
|
Effect.gen(function*() {
|
|
const effect = Effect.fail(new Error("test error"))
|
|
const matched = Effect.matchEager(effect, {
|
|
onFailure: (err) => `Recovered from: ${err.message}`,
|
|
onSuccess: (n) => `Success: ${n}`
|
|
})
|
|
const result = yield* matched
|
|
assert.strictEqual(result, "Recovered from: test error")
|
|
}))
|
|
|
|
it.effect("supports curried usage", () =>
|
|
Effect.gen(function*() {
|
|
const matcher = Effect.matchEager({
|
|
onFailure: (err: string) => `Failed: ${err}`,
|
|
onSuccess: (n: number) => `Success: ${n}`
|
|
})
|
|
|
|
const successResult = yield* matcher(Effect.succeed(100))
|
|
assert.strictEqual(successResult, "Success: 100")
|
|
|
|
const failureResult = yield* matcher(Effect.fail("error"))
|
|
assert.strictEqual(failureResult, "Failed: error")
|
|
}))
|
|
|
|
it.effect("never fails - always produces a result", () =>
|
|
Effect.gen(function*() {
|
|
const effect = Effect.fail("test error")
|
|
const matched = Effect.matchEager(effect, {
|
|
onFailure: (err) => `Handled: ${err}`,
|
|
onSuccess: (n) => `Value: ${n}`
|
|
})
|
|
|
|
// Should not throw, should produce a result
|
|
const result = yield* matched
|
|
assert.strictEqual(result, "Handled: test error")
|
|
|
|
// Test with success case
|
|
const successEffect = Effect.succeed(42)
|
|
const successMatched = Effect.matchEager(successEffect, {
|
|
onFailure: (err) => `Handled: ${err}`,
|
|
onSuccess: (n) => `Value: ${n}`
|
|
})
|
|
|
|
const successResult = yield* successMatched
|
|
assert.strictEqual(successResult, "Value: 42")
|
|
}))
|
|
|
|
it.effect("performance: applies transformations immediately for resolved effects", () =>
|
|
Effect.gen(function*() {
|
|
let onSuccessCount = 0
|
|
let onFailureCount = 0
|
|
|
|
const successEffect = Effect.succeed(10)
|
|
const failureEffect = Effect.fail("error")
|
|
|
|
const matcher = {
|
|
onSuccess: (n: number) => {
|
|
onSuccessCount++
|
|
return n * 2
|
|
},
|
|
onFailure: (err: string) => {
|
|
onFailureCount++
|
|
return err.toUpperCase()
|
|
}
|
|
}
|
|
|
|
// Multiple invocations of the same resolved effect should be efficient
|
|
const successMatched = Effect.matchEager(successEffect, matcher)
|
|
const failureMatched = Effect.matchEager(failureEffect, matcher)
|
|
|
|
const result1 = yield* successMatched
|
|
const result2 = yield* successMatched
|
|
const result3 = yield* failureMatched
|
|
const result4 = yield* failureMatched
|
|
|
|
assert.strictEqual(result1, 20)
|
|
assert.strictEqual(result2, 20)
|
|
assert.strictEqual(result3, "ERROR")
|
|
assert.strictEqual(result4, "ERROR")
|
|
|
|
// Handlers should be called only once per effect execution due to eager evaluation
|
|
assert.strictEqual(onSuccessCount, 1)
|
|
assert.strictEqual(onFailureCount, 1)
|
|
}))
|
|
})
|
|
|
|
describe("matchCauseEager", () => {
|
|
it("eagerly matches success effects", () => {
|
|
let onSuccessCount = 0
|
|
let onFailureCount = 0
|
|
|
|
const successEffect = Effect.succeed(42)
|
|
const matcher = {
|
|
onSuccess: (value: number) => {
|
|
onSuccessCount++
|
|
return value * 2
|
|
},
|
|
onFailure: (_cause: any) => {
|
|
onFailureCount++
|
|
return "ERROR"
|
|
}
|
|
}
|
|
|
|
const result = Effect.matchCauseEager(successEffect, matcher)
|
|
|
|
// Should be resolved immediately
|
|
assert.strictEqual((result as any)._tag, "Success")
|
|
assert.strictEqual((result as any).value, 84)
|
|
assert.strictEqual(onSuccessCount, 1)
|
|
assert.strictEqual(onFailureCount, 0)
|
|
})
|
|
|
|
it("eagerly matches failure effects", () => {
|
|
let onSuccessCount = 0
|
|
let onFailureCount = 0
|
|
|
|
const failureEffect = Effect.fail("test error")
|
|
const matcher = {
|
|
onSuccess: (value: any) => {
|
|
onSuccessCount++
|
|
return value * 2
|
|
},
|
|
onFailure: (_cause: any) => {
|
|
onFailureCount++
|
|
return "HANDLED"
|
|
}
|
|
}
|
|
|
|
const result = Effect.matchCauseEager(failureEffect, matcher)
|
|
|
|
// Should be resolved immediately
|
|
assert.strictEqual((result as any)._tag, "Success")
|
|
assert.strictEqual((result as any).value, "HANDLED")
|
|
assert.strictEqual(onSuccessCount, 0)
|
|
assert.strictEqual(onFailureCount, 1)
|
|
})
|
|
|
|
it.effect("falls back to regular matchCause for complex effects", () =>
|
|
Effect.gen(function*() {
|
|
let onSuccessCount = 0
|
|
let onFailureCount = 0
|
|
|
|
const delayedEffect = Effect.delay(Effect.succeed(10), "10 millis")
|
|
const matcher = {
|
|
onSuccess: (value: number) => {
|
|
onSuccessCount++
|
|
return value * 2
|
|
},
|
|
onFailure: (_cause: any) => {
|
|
onFailureCount++
|
|
return "ERROR"
|
|
}
|
|
}
|
|
|
|
const result = Effect.matchCauseEager(delayedEffect, matcher)
|
|
|
|
// Should not be resolved immediately - should be a complex effect
|
|
assert.notStrictEqual((result as any)._tag, "Success")
|
|
assert.notStrictEqual((result as any)._tag, "Failure")
|
|
|
|
// Verify handlers haven't been called yet
|
|
assert.strictEqual(onSuccessCount, 0)
|
|
assert.strictEqual(onFailureCount, 0)
|
|
|
|
// Fork the effect and advance time
|
|
const fiber = yield* Effect.forkChild(result)
|
|
yield* TestClock.adjust("10 millis")
|
|
const finalResult = yield* Fiber.join(fiber)
|
|
|
|
// Now handlers should have been called
|
|
assert.strictEqual(finalResult, 20)
|
|
assert.strictEqual(onSuccessCount, 1)
|
|
assert.strictEqual(onFailureCount, 0)
|
|
}))
|
|
|
|
it.effect("falls back to regular matchCause for delayed failure effects", () =>
|
|
Effect.gen(function*() {
|
|
let onSuccessCount = 0
|
|
let onFailureCount = 0
|
|
|
|
const delayedFailure = Effect.delay(Effect.fail("delayed error"), "5 millis")
|
|
const matcher = {
|
|
onSuccess: (value: any) => {
|
|
onSuccessCount++
|
|
return value * 2
|
|
},
|
|
onFailure: (_cause: any) => {
|
|
onFailureCount++
|
|
return "HANDLED_DELAYED"
|
|
}
|
|
}
|
|
|
|
const result = Effect.matchCauseEager(delayedFailure, matcher)
|
|
|
|
// Should not be resolved immediately
|
|
assert.notStrictEqual((result as any)._tag, "Success")
|
|
assert.notStrictEqual((result as any)._tag, "Failure")
|
|
|
|
// Verify handlers haven't been called yet
|
|
assert.strictEqual(onSuccessCount, 0)
|
|
assert.strictEqual(onFailureCount, 0)
|
|
|
|
// Fork the effect and advance time
|
|
const fiber = yield* Effect.forkChild(result)
|
|
yield* TestClock.adjust("5 millis")
|
|
const finalResult = yield* Fiber.join(fiber)
|
|
|
|
// Now handlers should have been called
|
|
assert.strictEqual(finalResult, "HANDLED_DELAYED")
|
|
assert.strictEqual(onSuccessCount, 0)
|
|
assert.strictEqual(onFailureCount, 1)
|
|
}))
|
|
|
|
it("handles cause information correctly", () => {
|
|
let capturedCause: any = null
|
|
|
|
const failureEffect = Effect.fail("test error")
|
|
const matcher = {
|
|
onSuccess: (value: any) => value,
|
|
onFailure: (cause: any) => {
|
|
capturedCause = cause
|
|
return "handled"
|
|
}
|
|
}
|
|
|
|
const result = Effect.matchCauseEager(failureEffect, matcher)
|
|
|
|
// Should be resolved immediately
|
|
assert.strictEqual((result as any)._tag, "Success")
|
|
assert.strictEqual((result as any).value, "handled")
|
|
assert.isNotNull(capturedCause)
|
|
})
|
|
|
|
it.effect("preserves effect execution behavior for non-eager paths", () =>
|
|
Effect.gen(function*() {
|
|
let executionCount = 0
|
|
|
|
const complexEffect = Effect.flatMap(
|
|
Effect.delay(Effect.succeed(42), "1 millis"),
|
|
(value) => {
|
|
executionCount++
|
|
return Effect.succeed(value * 2)
|
|
}
|
|
)
|
|
|
|
const matcher = {
|
|
onSuccess: (value: number) => value + 100,
|
|
onFailure: (_cause: any) => "ERROR"
|
|
}
|
|
|
|
const result = Effect.matchCauseEager(complexEffect, matcher)
|
|
|
|
// Should not be resolved immediately
|
|
assert.notStrictEqual((result as any)._tag, "Success")
|
|
assert.strictEqual(executionCount, 0)
|
|
|
|
// Fork the effect and advance time
|
|
const fiber = yield* Effect.forkChild(result)
|
|
yield* TestClock.adjust("1 millis")
|
|
const finalResult = yield* Fiber.join(fiber)
|
|
|
|
// Should have executed the effect chain properly
|
|
assert.strictEqual(finalResult, 184) // (42 * 2) + 100
|
|
assert.strictEqual(executionCount, 1)
|
|
}))
|
|
})
|
|
})
|