use packr::parser::parse_interface;
use packr::runtime::InterfaceError;
use packr::Runtime;
const VALID_PROCESS_MODULE: &str = r#"
(module
(memory (export "memory") 1)
;; process: takes (ptr, len), returns packed (out_ptr, out_len)
(func $process (param $in_ptr i32) (param $in_len i32) (result i64)
;; Just return the same pointer/length (echo behavior)
(i64.or
(i64.extend_i32_u (local.get $in_ptr))
(i64.shl
(i64.extend_i32_u (local.get $in_len))
(i64.const 32)))
)
(export "process" (func $process))
)
"#;
const WRONG_SIGNATURE_MODULE: &str = r#"
(module
(memory (export "memory") 1)
(func $process (param $in_ptr i32) (param $in_len i32) (result i32)
(local.get $in_ptr)
)
(export "process" (func $process))
)
"#;
const MISSING_FUNCTION_MODULE: &str = r#"
(module
(memory (export "memory") 1)
(func $other (param $x i32) (result i32)
(local.get $x)
)
(export "other" (func $other))
)
"#;
const NO_MEMORY_MODULE: &str = r#"
(module
(func $process (param $in_ptr i32) (param $in_len i32) (result i64)
(i64.const 0)
)
(export "process" (func $process))
)
"#;
const MULTI_FUNCTION_MODULE: &str = r#"
(module
(memory (export "memory") 1)
(func $parse (param $in_ptr i32) (param $in_len i32) (result i64)
(i64.or
(i64.extend_i32_u (local.get $in_ptr))
(i64.shl (i64.extend_i32_u (local.get $in_len)) (i64.const 32)))
)
(func $eval (param $in_ptr i32) (param $in_len i32) (result i64)
(i64.or
(i64.extend_i32_u (local.get $in_ptr))
(i64.shl (i64.extend_i32_u (local.get $in_len)) (i64.const 32)))
)
(export "parse" (func $parse))
(export "eval" (func $eval))
)
"#;
#[test]
fn valid_module_passes_validation() {
let interface = parse_interface(
r#"
interface api {
process: func(input: string) -> string;
}
"#,
)
.expect("parse interface");
let wasm_bytes = wat::parse_str(VALID_PROCESS_MODULE).expect("parse WAT");
let runtime = Runtime::new();
let module = runtime.load_module(&wasm_bytes).expect("load module");
let mut instance = module.instantiate().expect("instantiate");
let result = instance.validate_interface(&interface);
assert!(result.is_ok(), "Expected Ok, got {:?}", result);
}
#[test]
fn wrong_signature_fails_validation() {
let interface = parse_interface(
r#"
interface api {
process: func(input: string) -> string;
}
"#,
)
.expect("parse interface");
let wasm_bytes = wat::parse_str(WRONG_SIGNATURE_MODULE).expect("parse WAT");
let runtime = Runtime::new();
let module = runtime.load_module(&wasm_bytes).expect("load module");
let mut instance = module.instantiate().expect("instantiate");
let result = instance.validate_interface(&interface);
assert!(matches!(
result,
Err(InterfaceError::SignatureMismatch { name, .. }) if name == "process"
));
}
#[test]
fn missing_function_fails_validation() {
let interface = parse_interface(
r#"
interface api {
process: func(input: string) -> string;
}
"#,
)
.expect("parse interface");
let wasm_bytes = wat::parse_str(MISSING_FUNCTION_MODULE).expect("parse WAT");
let runtime = Runtime::new();
let module = runtime.load_module(&wasm_bytes).expect("load module");
let mut instance = module.instantiate().expect("instantiate");
let result = instance.validate_interface(&interface);
assert!(matches!(
result,
Err(InterfaceError::MissingFunction { name }) if name == "process"
));
}
#[test]
fn missing_memory_fails_validation() {
let interface = parse_interface(
r#"
interface api {
process: func(input: string) -> string;
}
"#,
)
.expect("parse interface");
let wasm_bytes = wat::parse_str(NO_MEMORY_MODULE).expect("parse WAT");
let runtime = Runtime::new();
let module = runtime.load_module(&wasm_bytes).expect("load module");
let mut instance = module.instantiate().expect("instantiate");
let result = instance.validate_interface(&interface);
assert!(matches!(result, Err(InterfaceError::MissingMemory)));
}
#[test]
fn multi_function_interface_validation() {
let interface = parse_interface(
r#"
interface lisp {
variant sexpr {
sym(string),
num(s64),
lst(list<sexpr>),
}
parse: func(input: string) -> result<sexpr, string>;
eval: func(expr: sexpr) -> result<sexpr, string>;
}
"#,
)
.expect("parse interface");
let wasm_bytes = wat::parse_str(MULTI_FUNCTION_MODULE).expect("parse WAT");
let runtime = Runtime::new();
let module = runtime.load_module(&wasm_bytes).expect("load module");
let mut instance = module.instantiate().expect("instantiate");
let result = instance.validate_interface(&interface);
assert!(result.is_ok(), "Expected Ok, got {:?}", result);
}
#[test]
fn partial_implementation_fails_validation() {
let interface = parse_interface(
r#"
interface lisp {
parse: func(input: string) -> string;
eval: func(expr: string) -> string;
compile: func(expr: string) -> string;
}
"#,
)
.expect("parse interface");
let wasm_bytes = wat::parse_str(MULTI_FUNCTION_MODULE).expect("parse WAT");
let runtime = Runtime::new();
let module = runtime.load_module(&wasm_bytes).expect("load module");
let mut instance = module.instantiate().expect("instantiate");
let result = instance.validate_interface(&interface);
assert!(matches!(
result,
Err(InterfaceError::MissingFunction { name }) if name == "compile"
));
}
#[test]
fn export_block_functions_validated() {
let interface = parse_interface(
r#"
interface api {
export api {
process: func(input: string) -> string;
}
}
"#,
)
.expect("parse interface");
let wasm_bytes = wat::parse_str(VALID_PROCESS_MODULE).expect("parse WAT");
let runtime = Runtime::new();
let module = runtime.load_module(&wasm_bytes).expect("load module");
let mut instance = module.instantiate().expect("instantiate");
let result = instance.validate_interface(&interface);
assert!(result.is_ok(), "Expected Ok, got {:?}", result);
}
#[test]
fn export_block_missing_function_fails() {
let interface = parse_interface(
r#"
interface api {
export api {
process: func(input: string) -> string;
transform: func(input: string) -> string;
}
}
"#,
)
.expect("parse interface");
let wasm_bytes = wat::parse_str(VALID_PROCESS_MODULE).expect("parse WAT");
let runtime = Runtime::new();
let module = runtime.load_module(&wasm_bytes).expect("load module");
let mut instance = module.instantiate().expect("instantiate");
let result = instance.validate_interface(&interface);
assert!(matches!(
result,
Err(InterfaceError::MissingFunction { name }) if name == "transform"
));
}
#[test]
fn empty_interface_always_passes() {
let interface = parse_interface(
r#"
interface empty {
}
"#,
)
.expect("parse interface");
let wasm_bytes = wat::parse_str(VALID_PROCESS_MODULE).expect("parse WAT");
let runtime = Runtime::new();
let module = runtime.load_module(&wasm_bytes).expect("load module");
let mut instance = module.instantiate().expect("instantiate");
let result = instance.validate_interface(&interface);
assert!(result.is_ok(), "Expected Ok, got {:?}", result);
}