use aethershell::env::Env;
use aethershell::eval::eval_program;
use aethershell::parser::parse_program;
use aethershell::value::Value;
fn eval(src: &str) -> Value {
let stmts = parse_program(src).expect("parse failed");
let mut env = Env::new();
eval_program(&stmts, &mut env).expect("eval failed")
}
fn eval_result(src: &str) -> Result<Value, String> {
let stmts = parse_program(src).map_err(|e| e.to_string())?;
let mut env = Env::new();
eval_program(&stmts, &mut env).map_err(|e| e.to_string())
}
#[test]
fn throw_creates_error_value() {
let v = eval(r#"throw "something went wrong""#);
assert!(matches!(v, Value::Error(_)));
if let Value::Error(msg) = v {
assert_eq!(msg, "something went wrong");
}
}
#[test]
fn throw_with_expression() {
let v = eval(r#"throw 42"#);
assert!(matches!(v, Value::Error(_)));
}
#[test]
fn throw_type_of() {
let v = eval(r#"type_of(throw "error")"#);
assert_eq!(v, Value::Str("Error".to_string()));
}
#[test]
fn try_catch_success_returns_value() {
let v = eval(r#"try { 42 } catch { 0 }"#);
assert_eq!(v, Value::Int(42));
}
#[test]
fn try_catch_catches_thrown_error() {
let v = eval(r#"try { throw "error" } catch { "caught" }"#);
assert_eq!(v, Value::Str("caught".to_string()));
}
#[test]
fn try_catch_with_error_binding() {
let v = eval(r#"try { throw "the error message" } catch e { e }"#);
assert_eq!(v, Value::Str("the error message".to_string()));
}
#[test]
fn try_catch_catches_runtime_error() {
let v = eval(r#"try { 1 / 0 } catch { "caught division error" }"#);
assert!(v == Value::Str("caught division error".to_string()) || matches!(v, Value::Float(_)));
}
#[test]
fn try_catch_nested() {
let v = eval(
r#"
try {
try {
throw "inner"
} catch inner_err {
throw ("outer: " + inner_err)
}
} catch outer_err {
outer_err
}
"#,
);
assert_eq!(v, Value::Str("outer: inner".to_string()));
}
#[test]
fn try_catch_in_pipeline() {
let v = eval(
r#"
let safe_divide = fn(a, b) => try { a / b } catch { 0 }
safe_divide(10, 2)
"#,
);
assert_eq!(v, Value::Float(5.0));
}
#[test]
fn is_error_on_error_value() {
let v = eval(r#"is_error(throw "test")"#);
assert_eq!(v, Value::Bool(true));
}
#[test]
fn is_error_on_non_error() {
let v = eval(r#"is_error(42)"#);
assert_eq!(v, Value::Bool(false));
}
#[test]
fn is_error_on_string() {
let v = eval(r#"is_error("not an error")"#);
assert_eq!(v, Value::Bool(false));
}
#[test]
fn is_error_with_pipeline() {
let v = eval(r#"throw "test" | is_error"#);
assert_eq!(v, Value::Bool(true));
}
#[test]
fn error_value_is_falsy() {
let v = eval(
r#"
let e = throw "error"
match e {
_ if !is_error(e) => "not error"
_ => "is error"
}
"#,
);
assert_eq!(v, Value::Str("is error".to_string()));
}
#[test]
fn error_in_array() {
let v = eval(r#"[1, throw "err", 3]"#);
if let Value::Array(arr) = v {
assert_eq!(arr.len(), 3);
assert!(matches!(arr[1], Value::Error(_)));
} else {
panic!("expected array");
}
}
#[test]
fn error_in_record() {
let v = eval(r#"{ a: 1, b: throw "err", c: 3 }"#);
if let Value::Record(rec) = v {
assert!(matches!(rec.get("b"), Some(Value::Error(_))));
} else {
panic!("expected record");
}
}