312 lines
5.6 KiB
TypeScript
312 lines
5.6 KiB
TypeScript
import { describe } from 'bun:test'
|
|
import { expect, test } from 'bun:test'
|
|
|
|
describe('exception handling', () => {
|
|
test('try with catch - no error thrown', () => {
|
|
expect(`
|
|
try:
|
|
42
|
|
catch err:
|
|
99
|
|
end
|
|
`).toEvaluateTo(42)
|
|
})
|
|
|
|
test('try with catch - error thrown', () => {
|
|
expect(`
|
|
try:
|
|
throw 'something went wrong'
|
|
99
|
|
catch err:
|
|
err
|
|
end
|
|
`).toEvaluateTo('something went wrong')
|
|
})
|
|
|
|
test('try with catch - catch variable binding', () => {
|
|
expect(`
|
|
try:
|
|
throw 100
|
|
catch my-error:
|
|
my-error + 50
|
|
end
|
|
`).toEvaluateTo(150)
|
|
})
|
|
|
|
test('try with finally - no error', () => {
|
|
expect(`
|
|
x = 0
|
|
result = try:
|
|
x = 10
|
|
42
|
|
finally:
|
|
x = x + 5
|
|
end
|
|
x
|
|
`).toEvaluateTo(15)
|
|
})
|
|
|
|
test('try with finally - return value from try', () => {
|
|
expect(`
|
|
x = 0
|
|
result = try:
|
|
x = 10
|
|
42
|
|
finally:
|
|
x = x + 5
|
|
999
|
|
end
|
|
result
|
|
`).toEvaluateTo(42)
|
|
})
|
|
|
|
test('try with catch and finally - no error', () => {
|
|
expect(`
|
|
x = 0
|
|
try:
|
|
x = 10
|
|
42
|
|
catch err:
|
|
x = 999
|
|
0
|
|
finally:
|
|
x = x + 5
|
|
end
|
|
x
|
|
`).toEvaluateTo(15)
|
|
})
|
|
|
|
test('try with catch and finally - error thrown', () => {
|
|
expect(`
|
|
x = 0
|
|
result = try:
|
|
x = 10
|
|
throw 'error'
|
|
99
|
|
catch err:
|
|
x = 20
|
|
err
|
|
finally:
|
|
x = x + 5
|
|
end
|
|
x
|
|
`).toEvaluateTo(25)
|
|
})
|
|
|
|
test('try with catch and finally - return value from catch', () => {
|
|
expect(`
|
|
result = try:
|
|
throw 'oops'
|
|
catch err:
|
|
'caught'
|
|
finally:
|
|
'finally'
|
|
end
|
|
result
|
|
`).toEvaluateTo('caught')
|
|
})
|
|
|
|
test('throw statement with string', () => {
|
|
expect(`
|
|
try:
|
|
throw 'error message'
|
|
catch err:
|
|
err
|
|
end
|
|
`).toEvaluateTo('error message')
|
|
})
|
|
|
|
test('throw statement with number', () => {
|
|
expect(`
|
|
try:
|
|
throw 404
|
|
catch err:
|
|
err
|
|
end
|
|
`).toEvaluateTo(404)
|
|
})
|
|
|
|
test('throw statement with dict', () => {
|
|
expect(`
|
|
try:
|
|
throw [code=500 message=failed]
|
|
catch e:
|
|
e
|
|
end
|
|
`).toEvaluateTo({ code: 500, message: 'failed' })
|
|
})
|
|
|
|
test('uncaught exception fails', () => {
|
|
expect(`throw 'uncaught error'`).toFailEvaluation()
|
|
})
|
|
|
|
test('single-line try catch', () => {
|
|
expect(`result = try: throw 'err' catch e: 'handled' end; result`).toEvaluateTo('handled')
|
|
})
|
|
|
|
test('nested try blocks - inner catches', () => {
|
|
expect(`
|
|
try:
|
|
result = try:
|
|
throw 'inner error'
|
|
catch err:
|
|
err
|
|
end
|
|
result
|
|
catch outer:
|
|
'outer'
|
|
end
|
|
`).toEvaluateTo('inner error')
|
|
})
|
|
|
|
test('nested try blocks - outer catches', () => {
|
|
expect(`
|
|
try:
|
|
try:
|
|
throw 'inner error'
|
|
catch err:
|
|
throw 'outer error'
|
|
end
|
|
catch outer:
|
|
outer
|
|
end
|
|
`).toEvaluateTo('outer error')
|
|
})
|
|
|
|
test('try as expression', () => {
|
|
expect(`
|
|
x = try: 10 catch err: 0 end
|
|
y = try: throw 'err' catch err: 20 end
|
|
x + y
|
|
`).toEvaluateTo(30)
|
|
})
|
|
})
|
|
|
|
describe('function-level exception handling', () => {
|
|
test('function with catch - no error', () => {
|
|
expect(`
|
|
read-file = do path:
|
|
path
|
|
catch e:
|
|
'default'
|
|
end
|
|
|
|
read-file test.txt
|
|
`).toEvaluateTo('test.txt')
|
|
})
|
|
|
|
test('function with catch - error thrown', () => {
|
|
expect(`
|
|
read-file = do path:
|
|
throw 'file not found'
|
|
catch e:
|
|
'default'
|
|
end
|
|
|
|
read-file test.txt
|
|
`).toEvaluateTo('default')
|
|
})
|
|
|
|
test('function with catch - error variable binding', () => {
|
|
expect(`
|
|
safe-call = do:
|
|
throw 'operation failed'
|
|
catch err:
|
|
err
|
|
end
|
|
|
|
safe-call
|
|
`).toEvaluateTo('operation failed')
|
|
})
|
|
|
|
test('function with finally - always runs', () => {
|
|
expect(`
|
|
counter = 0
|
|
increment-task = do:
|
|
result = 42
|
|
result
|
|
finally:
|
|
counter = counter + 1
|
|
end
|
|
|
|
x = increment-task
|
|
y = increment-task
|
|
counter
|
|
`).toEvaluateTo(2)
|
|
})
|
|
|
|
test('function with finally - return value from body', () => {
|
|
expect(`
|
|
get-value = do:
|
|
100
|
|
finally:
|
|
999
|
|
end
|
|
|
|
get-value
|
|
`).toEvaluateTo(100)
|
|
})
|
|
|
|
test('function with catch and finally', () => {
|
|
expect(`
|
|
cleanup-count = 0
|
|
safe-op = do should-fail:
|
|
if should-fail:
|
|
throw 'failed'
|
|
end
|
|
'success'
|
|
catch e:
|
|
'caught'
|
|
finally:
|
|
cleanup-count = cleanup-count + 1
|
|
end
|
|
|
|
result1 = safe-op false
|
|
result2 = safe-op true
|
|
cleanup-count
|
|
`).toEvaluateTo(2)
|
|
})
|
|
|
|
test('function with catch and finally - catch return value', () => {
|
|
expect(`
|
|
safe-fail = do:
|
|
throw 'always fails'
|
|
catch e:
|
|
'error handled'
|
|
finally:
|
|
noop = 1
|
|
end
|
|
|
|
safe-fail
|
|
`).toEvaluateTo('error handled')
|
|
})
|
|
|
|
test('function without catch/finally still works', () => {
|
|
expect(`
|
|
regular = do x:
|
|
x + 10
|
|
end
|
|
|
|
regular 5
|
|
`).toEvaluateTo(15)
|
|
})
|
|
|
|
test('nested functions with catch', () => {
|
|
expect(`
|
|
inner = do:
|
|
throw 'inner error'
|
|
catch e:
|
|
'inner caught'
|
|
end
|
|
|
|
outer = do:
|
|
inner
|
|
catch e:
|
|
'outer caught'
|
|
end
|
|
|
|
outer
|
|
`).toEvaluateTo('inner caught')
|
|
})
|
|
})
|