add treetest protocol simulator and ui

This commit is contained in:
Michael Mikovsky
2026-04-24 16:19:42 -06:00
parent 555663bd3d
commit 2b633ce019
31 changed files with 2760 additions and 4254 deletions
+80
View File
@@ -0,0 +1,80 @@
use treetest::{model::NodeId, scenarios::built_in_scenarios, sim::Simulation};
use unshell::protocol::ProtocolFault;
#[test]
fn unknown_leaf_and_unknown_procedure_fault_to_root() {
let scenarios = built_in_scenarios();
let mut simulation = Simulation::new(scenarios[4].clone()).expect("scenario should build");
simulation
.call_unchecked(
NodeId(1),
Some("missing_leaf"),
"demo.leaf.v1.echo.invoke",
Vec::new(),
)
.expect("unknown leaf call should start");
simulation.drain().expect("network should drain");
assert_eq!(
simulation
.latest_root_fault()
.expect("root should observe unknown-leaf fault")
.fault,
ProtocolFault::UNKNOWN_LEAF
);
simulation
.call_unchecked(
NodeId(1),
None,
"demo.endpoint.v1.control.missing",
Vec::new(),
)
.expect("unknown procedure call should start");
simulation.drain().expect("network should drain");
assert_eq!(
simulation
.latest_root_fault()
.expect("root should observe unknown-procedure fault")
.fault,
ProtocolFault::UNKNOWN_PROCEDURE
);
}
#[test]
fn invalid_hook_peer_faults_an_active_chat_hook() {
let scenarios = built_in_scenarios();
let mut simulation = Simulation::new(scenarios[5].clone()).expect("scenario should build");
simulation
.call_endpoint_procedure(NodeId(3), "demo.endpoint.v1.chat.session", b"open".to_vec())
.expect("chat call should start");
simulation.drain().expect("network should drain");
let hook_id = *simulation.hook_ids().last().expect("hook should exist");
simulation
.inject_invalid_peer_data(
NodeId(1),
NodeId(0),
hook_id,
"demo.endpoint.v1.chat.session",
"spoof",
)
.expect("invalid peer injection should enqueue");
simulation.drain().expect("network should drain");
assert_eq!(
simulation
.latest_root_fault()
.expect("root should observe a fault")
.fault,
ProtocolFault::INVALID_HOOK_PEER
);
assert!(
simulation
.hooks
.get(&hook_id)
.expect("hook snapshot should exist")
.closed
);
}
+50
View File
@@ -0,0 +1,50 @@
use treetest::{model::NodeId, scenarios::built_in_scenarios, sim::Simulation};
#[test]
fn bidirectional_chat_remains_active_until_bye() {
let scenarios = built_in_scenarios();
let mut simulation = Simulation::new(scenarios[3].clone()).expect("scenario should build");
simulation
.call_endpoint_procedure(NodeId(1), "demo.endpoint.v1.chat.session", b"open".to_vec())
.expect("chat call should start");
simulation.drain().expect("network should drain");
let hook_id = *simulation.hook_ids().last().expect("hook should exist");
assert!(
!simulation
.hooks
.get(&hook_id)
.expect("hook snapshot should exist")
.closed
);
assert_eq!(
simulation.latest_root_data_text().as_deref(),
Some("chat ready")
);
simulation
.send_root_hook_data(hook_id, "hello there", false)
.expect("chat data should send");
simulation.drain().expect("network should drain");
assert_eq!(
simulation.latest_root_data_text().as_deref(),
Some("chat ack: HELLO THERE")
);
simulation
.send_root_hook_data(hook_id, "bye", true)
.expect("chat close should send");
simulation.drain().expect("network should drain");
assert!(
simulation
.hooks
.get(&hook_id)
.expect("hook snapshot should exist")
.closed
);
assert_eq!(
simulation.latest_root_data_text().as_deref(),
Some("chat session closed")
);
}
+43
View File
@@ -0,0 +1,43 @@
use treetest::{scenarios::built_in_scenarios, sim::Simulation};
#[test]
fn endpoint_and_leaf_introspection_complete_successfully() {
let scenarios = built_in_scenarios();
let mut simulation = Simulation::new(scenarios[0].clone()).expect("scenario should build");
simulation
.call_endpoint_introspection(treetest::model::NodeId(1))
.expect("endpoint introspection should start");
simulation.drain().expect("network should drain");
assert!(simulation.latest_root_data_text().is_some());
simulation
.call_leaf_introspection(treetest::model::NodeId(1), "echo")
.expect("leaf introspection should start");
simulation.drain().expect("network should drain");
assert!(simulation.latest_root_data_text().is_some());
}
#[test]
fn echo_leaf_round_trips_user_payload() {
let scenarios = built_in_scenarios();
let mut simulation = Simulation::new(scenarios[1].clone()).expect("scenario should build");
simulation
.call_echo_leaf(treetest::model::NodeId(1), "echo", "hello from test")
.expect("echo call should start");
simulation.drain().expect("network should drain");
assert_eq!(
simulation.latest_root_data_text().as_deref(),
Some("hello from test")
);
let hook_id = *simulation.hook_ids().last().expect("hook should exist");
assert!(
simulation
.hooks
.get(&hook_id)
.expect("hook snapshot should exist")
.closed
);
}
+51
View File
@@ -0,0 +1,51 @@
use treetest::{model::NodeId, scenarios::built_in_scenarios, sim::Simulation};
#[test]
fn nested_route_uses_longest_prefix_path() {
let scenarios = built_in_scenarios();
let mut simulation = Simulation::new(scenarios[2].clone()).expect("scenario should build");
simulation
.call_echo_leaf(NodeId(2), "echo", "nested")
.expect("echo call should start");
simulation.drain().expect("network should drain");
let trace_text = simulation
.trace
.iter()
.map(|event| event.summary.clone())
.collect::<Vec<_>>()
.join("\n");
assert!(trace_text.contains("forwarded frame to child /alpha"));
assert!(trace_text.contains("forwarded frame to child /alpha/beta"));
assert_eq!(
simulation.latest_root_data_text().as_deref(),
Some("nested")
);
}
#[test]
fn chunked_endpoint_procedure_returns_multiple_packets() {
let scenarios = built_in_scenarios();
let mut simulation = Simulation::new(scenarios[1].clone()).expect("scenario should build");
simulation
.call_endpoint_procedure(
NodeId(1),
"demo.endpoint.v1.stream.chunked_greeting",
b"go".to_vec(),
)
.expect("procedure call should start");
simulation.drain().expect("network should drain");
let root_data_count = simulation
.recorded_events
.iter()
.filter(|event| matches!(event, treetest::sim::RecordedEvent::Data { node_path, .. } if node_path == "/"))
.count();
assert!(root_data_count >= 3);
assert_eq!(
simulation.latest_root_data_text().as_deref(),
Some("chunk 3: hook complete")
);
}