use determa_state::value::Value;
use determa_state::{load_machines, resolve_definitions, Engine};
use std::collections::BTreeMap;
const TYPES: &str = "id: types
events:
go: {}
top:
esvs:
i: { type: int, init: 0 }
f: { type: float, init: 0.0 }
b: { type: bool, init: false }
s: { type: string, init: \"\" }
m: { type: map, init: {} }
l: { type: list, init: [] }
initial: { transition_to: a }
states:
a:
on_events:
go:
action:
- { assign: { i: \"1 + 2\" } }
- { assign: { f: \"1.5 + 0.5\" } }
- { assign: { b: \"true\" } }
- { assign: { s: \"'a' + 'b'\" } }
- { assign: { m: \"{'k': 2}\" } }
- { assign: { l: \"[1, 2, 3]\" } }
";
fn run() -> Engine {
let docs = load_machines(TYPES).expect("parses");
let machines = resolve_definitions(&docs).expect("builds");
let mut engine = Engine::new();
for m in machines {
engine.register(m);
}
engine
.create_root("r", "types", None, &BTreeMap::new())
.expect("create_root");
engine.send("r", "go", Value::Null).expect("send");
engine
}
#[test]
fn cel_assignments_are_canonical_values() {
let engine = run();
let esvs = engine.state_view("r").expect("state").esvs;
assert!(matches!(esvs.get("i"), Some(Value::Int(3))), "i = {:?}", esvs.get("i"));
assert!(matches!(esvs.get("f"), Some(Value::Float(_))), "f = {:?}", esvs.get("f"));
assert_eq!(esvs.get("f"), Some(&Value::Float(2.0)));
assert!(matches!(esvs.get("b"), Some(Value::Bool(true))), "b = {:?}", esvs.get("b"));
assert!(matches!(esvs.get("s"), Some(Value::Str(_))), "s = {:?}", esvs.get("s"));
assert_eq!(esvs.get("s"), Some(&Value::Str("ab".into())));
assert!(matches!(esvs.get("m"), Some(Value::Map(_))), "m = {:?}", esvs.get("m"));
assert!(matches!(esvs.get("l"), Some(Value::List(_))), "l = {:?}", esvs.get("l"));
if let Some(Value::Map(m)) = esvs.get("m") {
assert_eq!(m.get("k"), Some(&Value::Int(2)));
}
if let Some(Value::List(l)) = esvs.get("l") {
assert!(l.iter().all(|x| matches!(x, Value::Int(_))));
assert_eq!(l, &vec![Value::Int(1), Value::Int(2), Value::Int(3)]);
}
}
#[test]
fn snapshot_contains_only_json_representable_values() {
let engine = run();
let snap = engine.snapshot("r").expect("snapshot");
let json = serde_json::to_string(&snap).expect("snapshot is JSON-serializable");
assert!(json.contains("\"top::i\":3"));
assert!(json.contains("\"top::s\":\"ab\""));
assert_eq!(snap.esvs.get("top::i"), Some(&Value::Int(3)));
}
#[test]
fn assign_coerces_int_to_declared_float() {
let src = "id: coerce
events: { go: {} }
top:
esvs:
f: { type: float, init: 0.0 }
initial: { transition_to: a }
states:
a:
on_events:
go:
action: [ { assign: { f: \"3\" } } ]
";
let docs = load_machines(src).expect("parses");
let machines = resolve_definitions(&docs).expect("builds");
let mut engine = Engine::new();
for m in machines {
engine.register(m);
}
engine.create_root("r", "coerce", None, &BTreeMap::new()).unwrap();
engine.send("r", "go", Value::Null).unwrap();
let esvs = engine.state_view("r").unwrap().esvs;
assert_eq!(esvs.get("f"), Some(&Value::Float(3.0)));
}