Skip to content

Commit

Permalink
Merge pull request #21 from xzhavilla/fix/unknown-error
Browse files Browse the repository at this point in the history
  • Loading branch information
xzhayon authored Apr 24, 2024
2 parents 851b6f7 + 2598392 commit 781340e
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 27 deletions.
76 changes: 61 additions & 15 deletions src/Error.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import * as $Cause from './Cause'
import * as $Error from './Error'
import * as $Exit from './Exit'
import * as $Layer from './Layer'
import { Result } from './Result'
import * as $Runtime from './Runtime'
Expand All @@ -15,49 +17,54 @@ describe('Error', () => {

const tag = $Tag.tag<Divide>()
const divide = $Proxy.function(tag)
const layer = $Layer.layer().with(tag, function* (a, b) {
if (b === 0) {
yield* $Exception.raise(new Error('Cannot divide by zero'))
}

describe('tryCatch', () => {
const layer = $Layer.layer().with(tag, function* (a, b) {
if (b === 0) {
yield* $Exception.raise(new Error('Cannot divide by zero'))
}

return a / b
})
return a / b
})

test('forwarding error', async () => {
describe('tryCatch', () => {
test.failing('forwarding error', async () => {
await expect(
$Runtime.runPromise(
$Runtime.runExit(
$Error.tryCatch(divide(42, 0), function* (error) {
throw error
}),
layer,
),
).rejects.toThrow('Cannot divide by zero')
).resolves.toStrictEqual(
$Exit.failure($Cause.die(new Error('Cannot divide by zero'))),
)
})

test('throwing new error', async () => {
await expect(
$Runtime.runPromise(
$Runtime.runExit(
$Error.tryCatch(divide(42, 0), function* () {
throw new Error('Cannot recover from exception')
}),
layer,
),
).rejects.toThrow('Cannot recover from exception')
).resolves.toStrictEqual(
$Exit.failure($Cause.die(new Error('Cannot recover from exception'))),
)
})

test('raising new error', async () => {
await expect(
$Runtime.runPromise(
$Runtime.runExit(
$Error.tryCatch(divide(42, 0), function* () {
return yield* $Exception.raise(
new Error('Cannot recover from exception'),
)
}),
layer,
),
).rejects.toThrow('Cannot recover from exception')
).resolves.toStrictEqual(
$Exit.failure($Cause.fail(new Error('Cannot recover from exception'))),
)
})

test('returning value', async () => {
Expand Down Expand Up @@ -89,5 +96,44 @@ describe('Error', () => {
),
).resolves.toStrictEqual(42)
})

test('handling unexpected errors', async () => {
class FooError extends Error {
readonly [uri]!: 'Foo'
}

class BarError extends Error {
readonly [uri]!: 'Bar'
}

await expect(
$Runtime.runExit(function* () {
// @ts-expect-error
return (yield* $Error.tryCatch(
function* () {
if (false) {
return yield* $Exception.raise(new FooError())
}

throw new BarError()
},
function* (error) {
switch (error[uri]) {
case 'Foo':
return 'foo'
}
},
)).length
}, $Layer.layer()),
).resolves.toStrictEqual(
$Exit.failure(
$Cause.die(
new TypeError(
"Cannot read properties of undefined (reading 'length')",
),
),
),
)
})
})
})
6 changes: 5 additions & 1 deletion src/Error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ export interface NullError {
readonly [uri]?: unique symbol
}

export interface UnknownError {
readonly [uri]?: unique symbol
}

export function is(u: unknown): u is Error {
return u instanceof Error
}
Expand All @@ -15,7 +19,7 @@ export function isAggregate(error: Error): error is AggregateError {

export function* tryCatch<A extends Generator, B extends Generator>(
effector: OrLazy<A>,
onError: (error: $Generator.TOf<A>) => B,
onError: (error: $Generator.TOf<A> | UnknownError) => B,
): Generator<
$Generator.YOf<A> | $Generator.YOf<B>,
$Generator.ROf<A> | $Generator.ROf<B>,
Expand Down
57 changes: 46 additions & 11 deletions src/effect/Exception.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import * as $Cause from '../Cause'
import * as $Exit from '../Exit'
import * as $Layer from '../Layer'
import { Result } from '../Result'
import * as $Runtime from '../Runtime'
Expand Down Expand Up @@ -26,7 +28,7 @@ describe('Exception', () => {
const random = $Proxy.function(tagRandom)

await expect(
$Runtime.runPromise(
$Runtime.runExit(
random,
// @ts-expect-error
$Layer.layer().with(tagRandom, function* () {
Expand All @@ -35,12 +37,14 @@ describe('Exception', () => {
)
}),
),
).rejects.toThrow('Cannot return random number')
).resolves.toStrictEqual(
$Exit.failure($Cause.fail(new Error('Cannot return random number'))),
)
})

test('raising error', async () => {
await expect(
$Runtime.runPromise(
$Runtime.runExit(
divide(42, 0),
$Layer.layer().with(tag, function* (a, b) {
if (b === 0) {
Expand All @@ -50,14 +54,16 @@ describe('Exception', () => {
return a / b
}),
),
).rejects.toThrow('Cannot divide by zero')
).resolves.toStrictEqual(
$Exit.failure($Cause.fail(new Error('Cannot divide by zero'))),
)
})

test('raising error subclass', async () => {
class FooError extends Error {}

await expect(
$Runtime.runPromise(
$Runtime.runExit(
divide(42, 0),
$Layer.layer().with(tag, function* (a, b) {
if (b === 0) {
Expand All @@ -67,16 +73,18 @@ describe('Exception', () => {
return a / b
}),
),
).rejects.toThrow('Cannot divide by zero')
).resolves.toStrictEqual(
$Exit.failure($Cause.fail(new FooError('Cannot divide by zero'))),
)
})

test('raising different error', async () => {
class BarError extends Error {
readonly [uri]!: 'BarError'
readonly [uri]!: 'Bar'
}

await expect(
$Runtime.runPromise(
$Runtime.runExit(
divide(42, 0),
// @ts-expect-error
$Layer.layer().with(tag, function* (a, b) {
Expand All @@ -87,7 +95,32 @@ describe('Exception', () => {
return a / b
}),
),
).rejects.toThrow('Cannot divide by zero')
).resolves.toStrictEqual(
$Exit.failure($Cause.fail(new BarError('Cannot divide by zero'))),
)
})

test('rethrowing error', async () => {
await expect(
$Runtime.runExit(
function* () {
try {
return yield* divide(42, 0)
} catch (error) {
throw error
}
},
$Layer.layer().with(tag, function* (a, b) {
if (b === 0) {
yield* $Exception.raise(new Error('Cannot divide by zero'))
}

return a / b
}),
),
).resolves.toStrictEqual(
$Exit.failure($Cause.fail(new Error('Cannot divide by zero'))),
)
})

test('raising error and performing effect', async () => {
Expand All @@ -100,7 +133,7 @@ describe('Exception', () => {
const random = $Proxy.function(tagRandom)

await expect(
$Runtime.runPromise(
$Runtime.runExit(
divide(42, 0),
$Layer
.layer()
Expand All @@ -113,7 +146,9 @@ describe('Exception', () => {
})
.with(tagRandom, () => Math.random()),
),
).rejects.toThrow('Cannot divide by zero')
).resolves.toStrictEqual(
$Exit.failure($Cause.fail(new Error('Cannot divide by zero'))),
)
})
})
})

0 comments on commit 781340e

Please sign in to comment.