use axon::flow_plan::compile_source_to_ir;
use axon::runner::execute_server_flow;
fn run(source: &str, flow: &str) -> axon::runner::ServerRunnerMetrics {
let (_prog, ir) = compile_source_to_ir(source, "<v2.46.0-test>").expect("compile");
let empty = std::collections::HashMap::new();
execute_server_flow(
&ir, flow, "stub", "", "<v2.46.0-test>", None, None, &empty, &empty, None, None, None,
None, None, None, None, None, None, None, None, )
.expect("execute")
}
const STEP_NOW: &str = "flow Plan() -> Unit {\n\
step Triage {\n\
now: \"America/Bogota\"\n\
ask: \"Propose three visit slots this week.\"\n\
}\n\
}\n";
#[test]
fn step_now_yields_a_temporal_record() {
let m = run(STEP_NOW, "Plan");
assert!(m.success, "stub flow runs");
let rec = m.temporal_context.expect("temporal record present");
assert_eq!(rec.zones, vec!["America/Bogota".to_string()]);
assert_eq!(rec.tzdb_version, axon::window::tz_db_version());
assert!(rec.captured_utc.ends_with('Z'), "UTC instant: {}", rec.captured_utc);
}
#[test]
fn frame_level_now_covers_steps_without_their_own() {
let src = "context Scheduling { now: \"UTC\" }\n\
flow Plan() -> Unit { step S { ask: \"hi\" } }\n";
let m = run(src, "Plan");
assert!(m.success);
let rec = m.temporal_context.expect("frame-level record present");
assert_eq!(rec.zones, vec!["UTC".to_string()]);
}
#[test]
fn step_zone_overrides_frame_zone_and_both_record_once() {
let src = "context Scheduling { now: \"UTC\" }\n\
flow Plan() -> Unit {\n\
step A { now: \"America/Bogota\" ask: \"a\" }\n\
step B { ask: \"b\" }\n\
step C { ask: \"c\" }\n\
}\n";
let m = run(src, "Plan");
assert!(m.success);
let rec = m.temporal_context.expect("record present");
assert_eq!(
rec.zones,
vec!["America/Bogota".to_string(), "UTC".to_string()]
);
}
#[test]
fn now_less_flow_has_no_record_and_no_wire_key() {
let src = "flow Plan() -> Unit { step S { ask: \"hi\" } }\n";
let m = run(src, "Plan");
assert!(m.success);
assert!(m.temporal_context.is_none(), "no `now:` ⇒ no record");
}
#[test]
fn flow_envelope_elides_temporal_context_when_absent() {
#[derive(serde::Serialize)]
struct Probe {
#[serde(default, skip_serializing_if = "Option::is_none")]
temporal_context: Option<axon::temporal_context::TemporalRecord>,
}
let json = serde_json::to_string(&Probe { temporal_context: None }).unwrap();
assert_eq!(json, "{}");
let m = run(STEP_NOW, "Plan");
let json = serde_json::to_string(&Probe { temporal_context: m.temporal_context }).unwrap();
assert!(json.contains("\"captured_utc\""), "{json}");
assert!(json.contains("\"tzdb_version\""), "{json}");
assert!(json.contains("\"zones\":[\"America/Bogota\"]"), "{json}");
}
#[test]
fn unknown_zone_fails_closed() {
let src = "flow Plan() -> Unit { step S { now: \"Fake/Zone\" ask: \"hi\" } }\n";
let m = run(src, "Plan");
assert!(!m.success, "unresolvable declared zone must fail the flow");
let err = m.error.expect("honest failure detail");
assert!(
err.contains("not a known IANA timezone"),
"names the cause: {err}"
);
assert!(m.temporal_context.is_none(), "no record for a failed render");
}