use operonx::{model, op, Operon};
use serde_json::{json, Map, Value};
#[model]
struct TripleOut {
result: i64,
}
#[op(name = "__macros_smoke_triple")]
fn triple(x: i64) -> TripleOut {
TripleOut { result: x * 3 }
}
#[op(name = "__macros_smoke_shout")]
fn shout(inputs: &Value) -> Value {
let msg = inputs
.get("msg")
.and_then(|v| v.as_str())
.unwrap_or_default();
json!({"cry": format!("{}!", msg.to_uppercase())})
}
fn graph_json(op_name: &str, input_key: &str, output_key: &str) -> String {
format!(
r#"{{
"schema_version": "1.0",
"type": "graph",
"name": "main",
"full_name": "main",
"entries": ["step"],
"exits": ["step"],
"initial_ready_count": {{"step": 0}},
"compiled_adj": {{"step": []}},
"inputs": {{"{input_key}": {{"required": true}}}},
"outputs": {{"{output_key}": {{}}}},
"ops": {{
"step": {{
"type": "code",
"name": "step",
"full_name": "main.step",
"func_name": "{op_name}",
"bound": "sync",
"inputs": {{
"{input_key}": {{
"required": true,
"ref": {{"source": "__PARENT__", "var": "{input_key}"}}
}}
}},
"outputs": {{
"{output_key}": {{
"ref": {{
"source": "__PARENT__",
"var": "{output_key}",
"is_output": true
}}
}}
}}
}}
}}
}}"#
)
}
#[tokio::test]
async fn auto_register_dispatches_typed_op() {
let json_str = graph_json("__macros_smoke_triple", "x", "result");
let engine = Operon::builder(&json_str)
.no_resources()
.install_global_hub(false)
.auto_register()
.build()
.expect("engine builds cleanly");
let mut inputs = Map::new();
inputs.insert("x".into(), Value::from(7));
let out = engine
.run_json_async(inputs, None, None, None)
.await
.unwrap();
assert_eq!(out.get("result"), Some(&Value::from(21)));
}
#[tokio::test]
async fn auto_register_dispatches_untyped_op() {
let json_str = graph_json("__macros_smoke_shout", "msg", "cry");
let engine = Operon::builder(&json_str)
.no_resources()
.install_global_hub(false)
.auto_register()
.build()
.expect("engine builds cleanly");
let mut inputs = Map::new();
inputs.insert("msg".into(), Value::from("hi"));
let out = engine
.run_json_async(inputs, None, None, None)
.await
.unwrap();
assert_eq!(out.get("cry"), Some(&Value::from("HI!")));
}
#[tokio::test]
async fn typed_op_surfaces_deserialize_error_as_output() {
let json_str = graph_json("__macros_smoke_triple", "x", "result");
let engine = Operon::builder(&json_str)
.no_resources()
.install_global_hub(false)
.auto_register()
.build()
.unwrap();
let mut inputs = Map::new();
inputs.insert("x".into(), Value::from("not-a-number"));
let out = engine
.run_json_async(inputs, None, None, None)
.await
.unwrap();
assert!(out.is_object());
}