finish engram-lang: protocols, decorators, imports, Result, closures, stdlib, integration tests

This commit is contained in:
Will Anderson
2026-04-27 20:22:23 -05:00
parent 316c0a85ce
commit c427a0adc0
32 changed files with 2878 additions and 32 deletions
@@ -0,0 +1,153 @@
//! Tests that `activate` expressions parse and type-check correctly.
//!
//! `activate TypeName where "semantic query"` is the Engram graph query primitive.
//! It returns `[TypeName]` — an array of matching nodes. The type checker
//! requires `TypeName` to be a defined type in the type environment.
use crate::{parse_ok, pipeline_ok};
// ── Parse tests ───────────────────────────────────────────────────────────────
#[test]
fn test_parse_activate_basic() {
let src = r#"let results = activate User where "recent users""#;
assert!(parse_ok(src).is_ok(), "basic activate expression should parse");
}
#[test]
fn test_parse_activate_in_let_binding() {
let src = r#"let users = activate User where "all active users""#;
assert!(parse_ok(src).is_ok(), "activate in let binding should parse");
}
#[test]
fn test_parse_activate_in_fn_body() {
let src = r#"
fn find_users(query: String) -> [User] {
let results = activate User where "active users"
return results
}
"#;
assert!(parse_ok(src).is_ok(), "activate in function body should parse");
}
#[test]
fn test_parse_activate_multiple_in_program() {
let src = r#"
let users = activate User where "all users"
let docs = activate Document where "recent documents"
"#;
assert!(parse_ok(src).is_ok(), "multiple activates should parse");
}
#[test]
fn test_parse_activate_with_complex_query() {
let src = r#"let items = activate Product where "top-selling electronics under $500""#;
assert!(parse_ok(src).is_ok(), "activate with complex query string should parse");
}
#[test]
fn test_parse_activate_used_in_test_block() {
let src = r#"
test "activate in test" target: unit {
let results = activate User where "test users"
assert true
}
"#;
assert!(parse_ok(src).is_ok(), "activate inside test block should parse");
}
// ── Activate AST structure tests ──────────────────────────────────────────────
#[test]
fn test_activate_parses_to_correct_ast_node() {
use el_parser::{Expr, Stmt};
let src = r#"let u = activate User where "query""#;
let prog = parse_ok(src).unwrap();
assert_eq!(prog.stmts.len(), 1);
if let Stmt::Let { value, .. } = &prog.stmts[0] {
assert!(
matches!(value, Expr::Activate { type_name, query }
if type_name == "User" && query == "query"),
"expected Activate node"
);
} else {
panic!("expected Let statement");
}
}
#[test]
fn test_activate_type_name_is_preserved() {
use el_parser::{Expr, Stmt};
let src = r#"let n = activate NeuralPattern where "dense clusters""#;
let prog = parse_ok(src).unwrap();
if let Stmt::Let { value, .. } = &prog.stmts[0] {
if let Expr::Activate { type_name, .. } = value {
assert_eq!(type_name, "NeuralPattern");
} else {
panic!("expected Activate expr");
}
} else {
panic!("expected Let stmt");
}
}
// ── Pipeline tests ────────────────────────────────────────────────────────────
// The type checker requires the activated type to be defined in the type env.
// These tests define the type before activating it.
#[test]
fn test_pipeline_activate_defined_type_typechecks() {
let src = r#"
type User {
id: Int
name: String
}
let results = activate User where "all users"
"#;
assert!(pipeline_ok(src).is_ok(), "activate on defined type should pass type checking");
}
#[test]
fn test_pipeline_activate_result_used_in_stdlib_call() {
let src = r#"
type User {
id: Int
name: String
}
let results = activate User where "active users"
let count: Int = array_length(results)
"#;
assert!(pipeline_ok(src).is_ok(), "activate result used in stdlib call should type-check");
}
#[test]
fn test_pipeline_engram_search_typechecks() {
let src = r#"let items = engram_search("neural patterns", 10)"#;
assert!(pipeline_ok(src).is_ok(), "engram_search should type-check");
}
#[test]
fn test_pipeline_engram_node_count_typechecks() {
let src = r#"let n: Int = engram_node_count()"#;
assert!(pipeline_ok(src).is_ok(), "engram_node_count should type-check");
}
#[test]
fn test_pipeline_activate_in_fn_with_defined_type() {
let src = r#"
type Document {
id: Int
title: String
content: String
}
fn find_docs(query: String) -> [Document] {
let results = activate Document where "recent documents"
return results
}
"#;
assert!(pipeline_ok(src).is_ok(), "activate in fn with defined type should type-check");
}
@@ -0,0 +1,194 @@
//! Full pipeline tests: source → lex → parse → type-check → compile.
use crate::{compile_ok, parse_ok, pipeline_ok};
// ── Parse-only smoke tests ────────────────────────────────────────────────────
#[test]
fn test_parse_let_int() {
assert!(parse_ok("let x: Int = 42").is_ok());
}
#[test]
fn test_parse_let_string() {
assert!(parse_ok(r#"let name: String = "hello""#).is_ok());
}
#[test]
fn test_parse_let_bool() {
assert!(parse_ok("let flag: Bool = true").is_ok());
}
#[test]
fn test_parse_fn_def() {
let src = r#"
fn add(a: Int, b: Int) -> Int {
return a + b
}
"#;
assert!(parse_ok(src).is_ok());
}
#[test]
fn test_parse_if_else() {
let src = r#"
fn abs(x: Int) -> Int {
if x < 0 {
return 0 - x
} else {
return x
}
}
"#;
assert!(parse_ok(src).is_ok());
}
#[test]
fn test_parse_type_def() {
let src = r#"
type User {
id: Int
name: String
email: String
}
"#;
assert!(parse_ok(src).is_ok());
}
#[test]
fn test_parse_enum_def() {
let src = r#"
enum Status {
Active
Inactive
Pending
}
"#;
assert!(parse_ok(src).is_ok());
}
#[test]
fn test_parse_match_expr() {
let src = r#"
fn describe(s: Status) -> String {
match s {
Status::Active => "active"
Status::Inactive => "inactive"
_ => "unknown"
}
}
"#;
assert!(parse_ok(src).is_ok());
}
#[test]
fn test_parse_array_literal() {
assert!(parse_ok("let xs: [Int] = [1, 2, 3]").is_ok());
}
#[test]
fn test_parse_nested_fn_calls() {
let src = r#"
fn double(x: Int) -> Int {
return x * 2
}
fn quad(x: Int) -> Int {
return double(double(x))
}
"#;
assert!(parse_ok(src).is_ok());
}
// ── Pipeline (lex + parse + type-check) tests ─────────────────────────────────
#[test]
fn test_pipeline_hello_world() {
assert!(pipeline_ok(r#"let msg: String = "Hello, World!""#).is_ok());
}
#[test]
fn test_pipeline_arithmetic() {
assert!(pipeline_ok("let result: Int = 3 + 4 * 2").is_ok());
}
#[test]
fn test_pipeline_fn_def_and_call() {
let src = r#"
fn square(n: Int) -> Int {
return n * n
}
let s: Int = square(5)
"#;
assert!(pipeline_ok(src).is_ok());
}
#[test]
fn test_pipeline_bool_logic() {
assert!(pipeline_ok("let ok: Bool = true && false").is_ok());
assert!(pipeline_ok("let ok: Bool = true || false").is_ok());
}
#[test]
fn test_pipeline_float_literal() {
assert!(pipeline_ok("let pi: Float = 3.14").is_ok());
}
#[test]
fn test_pipeline_string_trim_via_call() {
// string_trim is registered as a stdlib builtin
let src = r#"let s: String = string_trim(" hello ")"#;
assert!(pipeline_ok(src).is_ok());
}
// ── Compile tests (full artifact generation) ──────────────────────────────────
#[test]
fn test_compile_integer_literal() {
let artifact = compile_ok("let x: Int = 99").unwrap();
assert!(!artifact.is_empty());
}
#[test]
fn test_compile_fn_def() {
let src = r#"
fn greet(name: String) -> String {
return name
}
"#;
let artifact = compile_ok(src).unwrap();
assert!(!artifact.is_empty());
}
#[test]
fn test_compile_if_else() {
let src = r#"
fn max_val(a: Int, b: Int) -> Int {
if a > b {
return a
} else {
return b
}
}
"#;
let artifact = compile_ok(src).unwrap();
assert!(!artifact.is_empty());
}
#[test]
fn test_compile_produces_valid_json_artifact() {
let artifact = compile_ok("let x: Int = 1").unwrap();
// Debug target produces JSON-serialized bytecode
let parsed: serde_json::Value = serde_json::from_slice(&artifact).unwrap();
assert!(parsed.is_array());
}
#[test]
fn test_compile_multiple_stmts() {
let src = r#"
let a: Int = 1
let b: Int = 2
let c: Int = a + b
"#;
let artifact = compile_ok(src).unwrap();
assert!(!artifact.is_empty());
}
@@ -0,0 +1,139 @@
//! Tests that decorator-annotated functions parse and compile correctly.
//!
//! Decorators are metadata — they do not change compilation semantics in
//! the current implementation. The compiler emits identical bytecode whether
//! or not a function is decorated.
use crate::{compile_ok, parse_ok, pipeline_ok};
// ── Parse tests ───────────────────────────────────────────────────────────────
#[test]
fn test_parse_authenticate_decorator() {
let src = r#"
@authenticate
fn get_user(id: Int) -> String {
return "user"
}
"#;
assert!(parse_ok(src).is_ok(), "@authenticate decorator should parse");
}
#[test]
fn test_parse_public_decorator() {
let src = r#"
@public
fn health_check() -> Bool {
return true
}
"#;
assert!(parse_ok(src).is_ok(), "@public decorator should parse");
}
#[test]
fn test_parse_cache_decorator_with_args() {
let src = r#"
@cache(300)
fn get_config(key: String) -> String {
return key
}
"#;
assert!(parse_ok(src).is_ok(), "@cache(ttl) decorator should parse");
}
#[test]
fn test_parse_multiple_decorators() {
let src = r#"
@authenticate
@public
fn list_items() -> [String] {
return ["a", "b"]
}
"#;
assert!(parse_ok(src).is_ok(), "multiple decorators should parse");
}
#[test]
fn test_parse_decorator_with_string_arg() {
let src = r#"
@route("/api/users")
fn list_users() -> [String] {
return ["alice", "bob"]
}
"#;
assert!(parse_ok(src).is_ok(), "@route decorator with string arg should parse");
}
#[test]
fn test_parse_decorator_preserves_fn_body() {
let src = r#"
@authenticate
fn add(a: Int, b: Int) -> Int {
return a + b
}
"#;
let prog = parse_ok(src).unwrap();
// There is exactly one top-level statement (the fn def)
assert_eq!(prog.stmts.len(), 1);
}
// ── Pipeline tests ────────────────────────────────────────────────────────────
#[test]
fn test_pipeline_decorated_fn_typechecks() {
let src = r#"
@authenticate
fn secure_op(id: Int) -> Bool {
return true
}
"#;
assert!(pipeline_ok(src).is_ok(), "decorated fn should type-check");
}
#[test]
fn test_pipeline_multiple_decorators_typechecks() {
let src = r#"
@authenticate
@cache(60)
fn get_profile(id: Int) -> String {
return "profile"
}
"#;
assert!(pipeline_ok(src).is_ok(), "multiply-decorated fn should type-check");
}
// ── Compile tests ─────────────────────────────────────────────────────────────
#[test]
fn test_compile_decorated_fn_produces_artifact() {
let src = r#"
@authenticate
fn whoami() -> String {
return "me"
}
"#;
let artifact = compile_ok(src).unwrap();
assert!(!artifact.is_empty(), "decorated fn should produce bytecode artifact");
}
#[test]
fn test_compile_decorator_does_not_change_bytecode_semantics() {
// A decorated function and an identical undecorated function should both
// compile without errors and produce non-empty artifacts.
let decorated = r#"
@public
fn value() -> Int {
return 42
}
"#;
let plain = r#"
fn value() -> Int {
return 42
}
"#;
let art_dec = compile_ok(decorated).unwrap();
let art_plain = compile_ok(plain).unwrap();
// Both should compile to non-empty artifacts
assert!(!art_dec.is_empty());
assert!(!art_plain.is_empty());
}
@@ -0,0 +1,220 @@
//! Tests for Result<T, E> type annotation, the `?` try operator, and closures.
use crate::{parse_ok, pipeline_ok};
// ── Result<T, E> type annotation parsing ─────────────────────────────────────
#[test]
fn test_parse_result_return_type() {
let src = r#"
fn divide(a: Int, b: Int) -> Result<Int, String> {
return a
}
"#;
assert!(parse_ok(src).is_ok(), "Result<T, E> return type should parse");
}
#[test]
fn test_parse_result_in_let_binding() {
let src = r#"
fn parse_int(s: String) -> Result<Int, String> {
return 0
}
"#;
assert!(parse_ok(src).is_ok(), "Result<T, E> in function signature should parse");
}
#[test]
fn test_parse_result_with_complex_types() {
let src = r#"
fn fetch_user(id: Int) -> Result<String, String> {
return "user"
}
"#;
assert!(parse_ok(src).is_ok(), "Result<String, String> should parse");
}
#[test]
fn test_parse_nested_result() {
let src = r#"
fn complex_op() -> Result<Result<Int, String>, String> {
return 0
}
"#;
assert!(parse_ok(src).is_ok(), "nested Result types should parse");
}
// ── Try operator (`?`) parsing ────────────────────────────────────────────────
#[test]
fn test_parse_try_operator_on_call() {
let src = r#"
fn safe_div(a: Int, b: Int) -> Result<Int, String> {
return a
}
fn compute() -> Result<Int, String> {
let x: Int = safe_div(10, 2)?
return x
}
"#;
assert!(parse_ok(src).is_ok(), "? operator on function call should parse");
}
#[test]
fn test_parse_try_operator_on_variable() {
let src = r#"
fn process(result: Result<Int, String>) -> Result<Int, String> {
let value: Int = result?
return value
}
"#;
assert!(parse_ok(src).is_ok(), "? operator on variable should parse");
}
#[test]
fn test_parse_chained_try_operators() {
let src = r#"
fn step1() -> Result<Int, String> { return 1 }
fn step2(n: Int) -> Result<String, String> { return "ok" }
fn pipeline() -> Result<String, String> {
let n: Int = step1()?
let s: String = step2(n)?
return s
}
"#;
assert!(parse_ok(src).is_ok(), "chained ? operators should parse");
}
// ── Optional type (`T?`) parsing ──────────────────────────────────────────────
#[test]
fn test_parse_optional_return_type() {
let src = r#"
fn find(id: Int) -> String? {
return "user"
}
"#;
assert!(parse_ok(src).is_ok(), "Optional return type T? should parse");
}
#[test]
fn test_parse_optional_parameter() {
let src = r#"
fn greet(name: String?) -> String {
return "hello"
}
"#;
assert!(parse_ok(src).is_ok(), "Optional parameter type T? should parse");
}
#[test]
fn test_parse_optional_in_let_binding() {
let src = r#"
fn maybe_val() -> Int? {
return 42
}
"#;
assert!(parse_ok(src).is_ok(), "Optional in let binding should parse");
}
// ── Closure parsing ───────────────────────────────────────────────────────────
#[test]
fn test_parse_simple_closure() {
let src = r#"let double = |x: Int| x * 2"#;
assert!(parse_ok(src).is_ok(), "simple closure should parse");
}
#[test]
fn test_parse_closure_with_return_type() {
let src = r#"let double = |x: Int| -> Int { return x * 2 }"#;
assert!(parse_ok(src).is_ok(), "closure with explicit return type should parse");
}
#[test]
fn test_parse_closure_with_multiple_params() {
let src = r#"let add = |a: Int, b: Int| a + b"#;
assert!(parse_ok(src).is_ok(), "multi-param closure should parse");
}
#[test]
fn test_parse_closure_single_param_no_body_type() {
// Closure with one param and inferred return type
let src = r#"let inc = |n: Int| n + 1"#;
assert!(parse_ok(src).is_ok(), "single-param closure with inferred return should parse");
}
#[test]
fn test_parse_closure_with_block_body() {
let src = r#"
let compute = |x: Int| -> Int {
let y: Int = x * 2
return y + 1
}
"#;
assert!(parse_ok(src).is_ok(), "closure with block body should parse");
}
// ── Pipeline tests ────────────────────────────────────────────────────────────
#[test]
fn test_pipeline_result_return_type_typechecks() {
let src = r#"
fn safe_op(x: Int) -> Result<Int, String> {
return x
}
"#;
assert!(pipeline_ok(src).is_ok(), "Result<T,E> return type should type-check");
}
#[test]
fn test_pipeline_optional_return_type_typechecks() {
let src = r#"
fn maybe(x: Int) -> Int? {
return x
}
"#;
assert!(pipeline_ok(src).is_ok(), "Optional return type should type-check");
}
#[test]
fn test_pipeline_closure_typechecks() {
let src = r#"let inc = |n: Int| n + 1"#;
assert!(pipeline_ok(src).is_ok(), "closure expression should type-check");
}
#[test]
fn test_pipeline_try_operator_typechecks() {
let src = r#"
fn maybe_int() -> Result<Int, String> {
return 1
}
fn compute() -> Result<Int, String> {
let x: Int = maybe_int()?
return x
}
"#;
assert!(pipeline_ok(src).is_ok(), "? operator should type-check");
}
#[test]
fn test_pipeline_result_stdlib_unwrap_or() {
let src = r#"
fn safe_op(x: Int) -> Result<Int, String> {
return x
}
let val: Int = result_unwrap_or(safe_op(5), 0)
"#;
assert!(pipeline_ok(src).is_ok(), "result_unwrap_or should type-check");
}
#[test]
fn test_pipeline_optional_stdlib_is_some() {
let src = r#"
fn maybe(x: Int) -> Int? {
return x
}
let val: Bool = optional_is_some(maybe(3))
"#;
assert!(pipeline_ok(src).is_ok(), "optional_is_some should type-check");
}
+9
View File
@@ -0,0 +1,9 @@
//! Integration test modules — full pipeline end-to-end.
mod compiler_pipeline;
mod stdlib_usage;
mod protocol_conformance;
mod decorator_codegen;
mod test_framework;
mod activate_typing;
mod error_propagation;
@@ -0,0 +1,147 @@
//! Tests that verify protocol definitions and impl blocks parse correctly
//! and pass through the type-checking pipeline.
use crate::{parse_ok, pipeline_ok};
// ── Protocol definition parsing ───────────────────────────────────────────────
#[test]
fn test_parse_protocol_definition() {
let src = r#"
protocol Serializable {
fn serialize(self: Serializable) -> String
fn deserialize(data: String) -> Serializable
}
"#;
assert!(parse_ok(src).is_ok(), "protocol definition should parse");
}
#[test]
fn test_parse_protocol_with_multiple_methods() {
let src = r#"
protocol Comparable {
fn compare(a: Comparable, b: Comparable) -> Int
fn equals(a: Comparable, b: Comparable) -> Bool
fn less_than(a: Comparable, b: Comparable) -> Bool
}
"#;
assert!(parse_ok(src).is_ok(), "multi-method protocol should parse");
}
#[test]
fn test_parse_impl_for_type() {
let src = r#"
protocol Printable {
fn print(self: Printable) -> String
}
type Point {
x: Float
y: Float
}
impl Printable for Point {
fn print(self: Point) -> String {
return "point"
}
}
"#;
assert!(parse_ok(src).is_ok(), "impl block should parse");
}
#[test]
fn test_parse_impl_with_multiple_methods() {
let src = r#"
protocol Codec {
fn encode(data: String) -> String
fn decode(data: String) -> String
}
type Base64Codec {
padding: Bool
}
impl Codec for Base64Codec {
fn encode(data: String) -> String {
return data
}
fn decode(data: String) -> String {
return data
}
}
"#;
assert!(parse_ok(src).is_ok(), "impl with multiple methods should parse");
}
// ── Protocol pipeline tests ───────────────────────────────────────────────────
#[test]
fn test_pipeline_protocol_definition_ok() {
let src = r#"
protocol Runnable {
fn run(self: Runnable) -> Int
}
"#;
assert!(pipeline_ok(src).is_ok(), "protocol definition should type-check");
}
#[test]
fn test_pipeline_impl_for_builtin_type() {
let src = r#"
protocol Describable {
fn describe(self: Describable) -> String
}
type Tag {
label: String
value: Int
}
impl Describable for Tag {
fn describe(self: Tag) -> String {
return self.label
}
}
"#;
assert!(pipeline_ok(src).is_ok(), "impl block should type-check");
}
#[test]
fn test_pipeline_multiple_impls_for_same_protocol() {
let src = r#"
protocol Shape {
fn area(self: Shape) -> Float
}
type Circle {
radius: Float
}
type Square {
side: Float
}
impl Shape for Circle {
fn area(self: Circle) -> Float {
return self.radius * self.radius
}
}
impl Shape for Square {
fn area(self: Square) -> Float {
return self.side * self.side
}
}
"#;
assert!(pipeline_ok(src).is_ok(), "multiple impls for same protocol should type-check");
}
#[test]
fn test_pipeline_protocol_with_result_return() {
let src = r#"
protocol Validatable {
fn validate(self: Validatable) -> Result<Bool, String>
}
"#;
assert!(pipeline_ok(src).is_ok(), "protocol with Result return type should type-check");
}
#[test]
fn test_pipeline_protocol_with_optional_return() {
let src = r#"
protocol Repository {
fn find_by_id(id: Int) -> String?
}
"#;
assert!(pipeline_ok(src).is_ok(), "protocol with optional return type should type-check");
}
@@ -0,0 +1,187 @@
//! Integration tests for programs that use stdlib functions.
//!
//! The stdlib functions are registered as builtins, so el programs can call
//! them without an import statement.
use crate::pipeline_ok;
// ── Array stdlib ──────────────────────────────────────────────────────────────
#[test]
fn test_array_length_call_typechecks() {
let src = r#"
let xs: [Int] = [1, 2, 3]
let n: Int = array_length(xs)
"#;
assert!(pipeline_ok(src).is_ok(), "array_length should be in scope");
}
#[test]
fn test_array_push_call_typechecks() {
let src = r#"
let xs: [Int] = [1, 2, 3]
let ys: [Int] = array_push(xs, 4)
"#;
assert!(pipeline_ok(src).is_ok(), "array_push should be in scope");
}
#[test]
fn test_array_pop_call_typechecks() {
// array_pop returns T? (Optional), not [T]
let src = r#"
let xs: [Int] = [1, 2, 3]
let head = array_pop(xs)
"#;
assert!(pipeline_ok(src).is_ok(), "array_pop should be in scope");
}
#[test]
fn test_array_reverse_call_typechecks() {
let src = r#"
let xs: [Int] = [3, 2, 1]
let ys: [Int] = array_reverse(xs)
"#;
assert!(pipeline_ok(src).is_ok(), "array_reverse should be in scope");
}
#[test]
fn test_array_contains_returns_bool() {
// array_contains takes [String] and String
let src = r#"
let xs: [String] = ["a", "b", "c"]
let found: Bool = array_contains(xs, "b")
"#;
assert!(pipeline_ok(src).is_ok(), "array_contains should be in scope");
}
// ── String stdlib ─────────────────────────────────────────────────────────────
#[test]
fn test_string_len_call_typechecks() {
let src = r#"let n: Int = string_len("hello")"#;
assert!(pipeline_ok(src).is_ok(), "string_len should be in scope");
}
#[test]
fn test_string_trim_call_typechecks() {
let src = r#"let s: String = string_trim(" hi ")"#;
assert!(pipeline_ok(src).is_ok(), "string_trim should be in scope");
}
#[test]
fn test_string_to_upper_call_typechecks() {
let src = r#"let s: String = string_to_upper("hello")"#;
assert!(pipeline_ok(src).is_ok(), "string_to_upper should be in scope");
}
#[test]
fn test_string_to_lower_call_typechecks() {
let src = r#"let s: String = string_to_lower("HELLO")"#;
assert!(pipeline_ok(src).is_ok(), "string_to_lower should be in scope");
}
#[test]
fn test_string_contains_returns_bool() {
let src = r#"let ok: Bool = string_contains("hello world", "world")"#;
assert!(pipeline_ok(src).is_ok(), "string_contains should be in scope");
}
#[test]
fn test_string_concat_call_typechecks() {
let src = r#"let s: String = string_concat("hello", " world")"#;
assert!(pipeline_ok(src).is_ok(), "string_concat should be in scope");
}
// ── Math stdlib ───────────────────────────────────────────────────────────────
#[test]
fn test_math_abs_call_typechecks() {
// math_abs takes Float -> Float
let src = r#"let n: Float = math_abs(0.0 - 5.0)"#;
assert!(pipeline_ok(src).is_ok(), "math_abs should be in scope");
}
#[test]
fn test_math_max_call_typechecks() {
// math_max takes (Float, Float) -> Float
let src = r#"let n: Float = math_max(3.0, 7.0)"#;
assert!(pipeline_ok(src).is_ok(), "math_max should be in scope");
}
#[test]
fn test_math_min_call_typechecks() {
// math_min takes (Float, Float) -> Float
let src = r#"let n: Float = math_min(3.0, 7.0)"#;
assert!(pipeline_ok(src).is_ok(), "math_min should be in scope");
}
#[test]
fn test_math_pow_call_typechecks() {
let src = r#"let n: Float = math_pow(2.0, 10.0)"#;
assert!(pipeline_ok(src).is_ok(), "math_pow should be in scope");
}
#[test]
fn test_math_abs_int_call_typechecks() {
// math_abs_int takes Int -> Int (integer variant)
let src = r#"let n: Int = math_abs_int(0 - 5)"#;
assert!(pipeline_ok(src).is_ok(), "math_abs_int should be in scope");
}
#[test]
fn test_math_max_int_call_typechecks() {
let src = r#"let n: Int = math_max_int(3, 7)"#;
assert!(pipeline_ok(src).is_ok(), "math_max_int should be in scope");
}
// ── Map stdlib ────────────────────────────────────────────────────────────────
#[test]
fn test_map_new_call_typechecks() {
let src = r#"let m: Map<String, Int> = map_new()"#;
assert!(pipeline_ok(src).is_ok(), "map_new should be in scope");
}
#[test]
fn test_map_size_call_typechecks() {
let src = r#"
let m: Map<String, Int> = map_new()
let n: Int = map_size(m)
"#;
assert!(pipeline_ok(src).is_ok(), "map_size should be in scope");
}
#[test]
fn test_map_is_empty_call_typechecks() {
let src = r#"
let m: Map<String, Int> = map_new()
let empty: Bool = map_is_empty(m)
"#;
assert!(pipeline_ok(src).is_ok(), "map_is_empty should be in scope");
}
// ── Engram graph stdlib ───────────────────────────────────────────────────────
#[test]
fn test_engram_node_count_call_typechecks() {
let src = r#"let n: Int = engram_node_count()"#;
assert!(pipeline_ok(src).is_ok(), "engram_node_count should be in scope");
}
#[test]
fn test_engram_search_call_typechecks() {
let src = r#"let results = engram_search("neural patterns", 10)"#;
assert!(pipeline_ok(src).is_ok(), "engram_search should be in scope");
}
#[test]
fn test_engram_edge_between_returns_bool() {
// engram_edge_between takes (Uuid, Uuid) -> Bool
// Uuid literals are just strings assigned to Uuid type
let src = r#"
fn check_edge(a: Uuid, b: Uuid) -> Bool {
return engram_edge_between(a, b)
}
"#;
assert!(pipeline_ok(src).is_ok(), "engram_edge_between should be in scope");
}
@@ -0,0 +1,131 @@
//! Tests that el `test { ... }` blocks parse and type-check correctly.
use crate::{parse_ok, pipeline_ok};
// ── Parse tests ───────────────────────────────────────────────────────────────
#[test]
fn test_parse_simple_test_block() {
let src = r#"
test "addition works" {
assert 1 + 1 == 2
}
"#;
assert!(parse_ok(src).is_ok(), "simple test block should parse");
}
#[test]
fn test_parse_test_with_unit_target() {
let src = r#"
test "unit test" target: unit {
assert true
}
"#;
assert!(parse_ok(src).is_ok(), "test with unit target should parse");
}
#[test]
fn test_parse_test_with_e2e_target() {
let src = r#"
test "e2e test" target: e2e {
assert true
}
"#;
assert!(parse_ok(src).is_ok(), "test with e2e target should parse");
}
#[test]
fn test_parse_test_with_both_target() {
let src = r#"
test "both targets" target: both {
assert true
}
"#;
assert!(parse_ok(src).is_ok(), "test with both target should parse");
}
#[test]
fn test_parse_test_with_let_binding() {
let src = r#"
test "arithmetic" {
let x: Int = 3 + 4
assert x == 7
}
"#;
assert!(parse_ok(src).is_ok(), "test with let binding should parse");
}
#[test]
fn test_parse_test_with_fn_call() {
let src = r#"
fn double(n: Int) -> Int {
return n * 2
}
test "double function" {
let result: Int = double(5)
assert result == 10
}
"#;
assert!(parse_ok(src).is_ok(), "test calling fn should parse");
}
#[test]
fn test_parse_multiple_asserts() {
let src = r#"
test "multiple assertions" {
assert 1 < 2
assert 2 < 3
assert 3 > 0
}
"#;
assert!(parse_ok(src).is_ok(), "test with multiple asserts should parse");
}
#[test]
fn test_parse_test_with_seed_node() {
let src = r#"
test "with seed data" target: unit {
seed Node { node_type: "User", content: "Alice", importance: 0.9 }
assert true
}
"#;
assert!(parse_ok(src).is_ok(), "test with seed node should parse");
}
// ── Pipeline tests ────────────────────────────────────────────────────────────
#[test]
fn test_pipeline_test_block_typechecks() {
let src = r#"
test "type-checks ok" {
let x: Int = 42
assert x > 0
}
"#;
assert!(pipeline_ok(src).is_ok(), "test block should type-check");
}
#[test]
fn test_pipeline_test_with_string_typechecks() {
let src = r#"
test "string test" {
let s: String = "hello"
let n: Int = string_len(s)
assert n > 0
}
"#;
assert!(pipeline_ok(src).is_ok(), "test using stdlib should type-check");
}
#[test]
fn test_pipeline_multiple_test_blocks() {
let src = r#"
test "first" {
assert true
}
test "second" {
assert 1 == 1
}
"#;
assert!(pipeline_ok(src).is_ok(), "multiple test blocks should type-check");
}