swactor/crates/mvp-system/src/tests/edge_establisher_guarantees.rs

443 lines
15 KiB
Rust
Raw Normal View History

//! Black-box contract tests for MVP edge establishment.
//!
//! These tests intentionally know only the public EdgeEstablisher surface:
//!
//! - `ProvisionTx`, `ProvisionRx`, lease, ring-install, driver, stop, and fault
//! events in
//! - arena, worker/token, driver, ready, fault, and release commands out
//!
//! They assert the guarantees in
//! `specs/mvp_system/edge_establisher_contract.md`.
use mvp_system::transport::edge_establisher as edge;
// A send provision carries the consumer node id because the driver must know
// where to send. It deliberately carries no remote actor address.
fn tx_provision() -> edge::ProvisionTx {
edge::ProvisionTx {
run_id: edge::RunId(7),
edge_id: edge::EdgeId(7001),
local_node_id: edge::NodeId(10),
consumer_node_id: edge::NodeId(11),
object_spec: edge::ObjectSpec::test_activation(),
ring_spec: edge::RingSpec::test_activation(),
}
}
// A receive provision needs the shared edge id and local layout information
// after leasing; it does not need producer actor addressing for data flow.
fn rx_provision() -> edge::ProvisionRx {
edge::ProvisionRx {
run_id: edge::RunId(7),
edge_id: edge::EdgeId(7001),
local_node_id: edge::NodeId(11),
object_spec: edge::ObjectSpec::test_activation(),
ring_spec: edge::RingSpec::test_activation(),
}
}
// The harness records only public establishment outputs. Tests never inspect a
// private edge record; they infer it from commands and lifecycle events.
fn new_establisher() -> edge::EdgeEstablisherHarness {
edge::EdgeEstablisherHarness::new(edge::NodeId(10))
}
// This helper drives a successful lease and ring install for the tx edge. It is
// used by driver and stop tests to stay on the public establishment path.
fn leased_and_installed_tx() -> edge::EdgeEstablisherHarness {
let mut harness = new_establisher();
harness.observe(edge::EdgeEvent::ProvisionTx(tx_provision()));
harness.observe(edge::EdgeEvent::RingLeased {
request_id: edge::LeaseRequestId(1),
ring_id: edge::RingId(8001),
layout: edge::RingLayout::test_layout(0),
});
harness.observe(edge::EdgeEvent::RingInstalled {
edge_id: edge::EdgeId(7001),
ring_id: edge::RingId(8001),
});
harness
}
// This proves ProvisionTx and ProvisionRx create local edge records with the
// required addressing facts and without remote actor addresses.
#[test]
fn provisioning_creates_local_edge_records_without_remote_actor_addresses() {
// Provision both sides through public messages.
let mut tx = new_establisher();
tx.observe(edge::EdgeEvent::ProvisionTx(tx_provision()));
let mut rx = edge::EdgeEstablisherHarness::new(edge::NodeId(11));
rx.observe(edge::EdgeEvent::ProvisionRx(rx_provision()));
// Tx must request a local lease and remember the consumer node id for the
// later driver command.
assert!(tx.commands().iter().any(|command| {
matches!(
command,
edge::EdgeCommand::LeaseRing {
edge_id: edge::EdgeId(7001),
..
}
)
}));
assert_eq!(
tx.local_record(edge::EdgeId(7001)).unwrap().peer_node_id,
Some(edge::NodeId(11))
);
// Rx must create a receive record keyed by edge id.
assert!(rx.local_record(edge::EdgeId(7001)).is_some());
// Data-flow provisioning must not require remote actor addresses.
for record in [
tx.local_record(edge::EdgeId(7001)).unwrap(),
rx.local_record(edge::EdgeId(7001)).unwrap(),
] {
assert!(record.remote_actor_address.is_none());
}
}
// This proves lease events advance or fail only matching records, stale lease
// events for stopped records do not install state, and unused fresh leases after
// cancellation are released.
#[test]
fn lease_flow_matches_records_and_suppresses_stale_or_cancelled_leases() {
// Start one tx record and send a mismatched lease event.
let mut harness = new_establisher();
harness.observe(edge::EdgeEvent::ProvisionTx(tx_provision()));
harness.observe(edge::EdgeEvent::RingLeased {
request_id: edge::LeaseRequestId(99),
ring_id: edge::RingId(8999),
layout: edge::RingLayout::test_layout(0),
});
// Mismatched lease must not install worker or pump state.
assert!(
!harness
.commands()
.iter()
.any(|command| { matches!(command, edge::EdgeCommand::InstallWorkerRing { .. }) })
);
// A matching rejection faults the record.
harness.observe(edge::EdgeEvent::RingLeaseRejected {
request_id: edge::LeaseRequestId(1),
reason: edge::RingLeaseRejection::CannotFit,
});
assert!(harness.events().iter().any(|event| {
matches!(
event,
edge::EdgeLifecycleEvent::EdgeFaulted {
edge_id: edge::EdgeId(7001),
..
}
)
}));
// Stop and then deliver a fresh lease; it must be released, not installed.
harness.observe(edge::EdgeEvent::StopEdge {
edge_id: edge::EdgeId(7001),
});
harness.observe(edge::EdgeEvent::RingLeased {
request_id: edge::LeaseRequestId(1),
ring_id: edge::RingId(8001),
layout: edge::RingLayout::test_layout(0),
});
assert!(harness.commands().iter().any(|command| {
matches!(
command,
edge::EdgeCommand::ReleaseArenaLease {
ring_id: edge::RingId(8001),
..
}
)
}));
}
// This proves driver state is established only after worker or token endpoint
// ring installation succeeds, using the ObjectSpec and RingSpec from
// provisioning.
#[test]
fn worker_ring_install_precedes_driver_establishment_and_uses_provision_specs() {
// Provision and lease a tx edge.
let mut harness = new_establisher();
let provision = tx_provision();
harness.observe(edge::EdgeEvent::ProvisionTx(provision.clone()));
harness.observe(edge::EdgeEvent::RingLeased {
request_id: edge::LeaseRequestId(1),
ring_id: edge::RingId(8001),
layout: edge::RingLayout::test_layout(0),
});
// Before ring installation, no driver command may be issued.
assert!(
!harness
.commands()
.iter()
.any(|command| { matches!(command, edge::EdgeCommand::EstablishSend { .. }) })
);
// Matching ring installation advances establishment.
harness.observe(edge::EdgeEvent::RingInstalled {
edge_id: edge::EdgeId(7001),
ring_id: edge::RingId(8001),
});
// The worker install command must carry the provisioned object/ring specs.
assert!(harness.commands().iter().any(|command| {
matches!(
command,
edge::EdgeCommand::InstallWorkerRing {
object_spec,
ring_spec,
..
} if *object_spec == provision.object_spec && *ring_spec == provision.ring_spec
)
}));
// Now the driver may be established.
assert!(harness.commands().iter().any(|command| {
matches!(
command,
edge::EdgeCommand::EstablishSend {
edge_id: edge::EdgeId(7001),
..
}
)
}));
}
// This proves send and receive driver establishment uses the correct public
// arguments, and DriverEdgeReady is the readiness boundary for the local actor.
#[test]
fn driver_ready_marks_local_edge_actor_ready() {
// Drive tx through lease and ring install.
let mut tx = leased_and_installed_tx();
// EstablishSend must include edge id, consumer node id, and local layout.
assert!(tx.commands().iter().any(|command| {
matches!(
command,
edge::EdgeCommand::EstablishSend {
edge_id: edge::EdgeId(7001),
consumer_node_id: edge::NodeId(11),
layout,
} if *layout == edge::RingLayout::test_layout(0)
)
}));
// The edge is not ready until DriverEdgeReady arrives.
assert!(
!tx.events()
.iter()
.any(|event| { matches!(event, edge::EdgeLifecycleEvent::EdgeReady { .. }) })
);
tx.observe(edge::EdgeEvent::DriverEdgeReady {
edge_id: edge::EdgeId(7001),
});
assert!(tx.events().iter().any(|event| {
matches!(
event,
edge::EdgeLifecycleEvent::EdgeReady {
edge_id: edge::EdgeId(7001),
..
}
)
}));
// After readiness, stream and pump behavior belongs to the driver; the
// public command enum has no hot-path byte variant.
}
// This proves StopEdge cancels queued leases, stops pumps, uninstalls worker
// rings, releases arena lease only after pump stop, worker-ring quiescence, and
// quiescence proof, and makes Stopped terminal.
#[test]
fn stop_edge_tears_down_local_state_and_terminal_stopped_ignores_late_events() {
// Drive an edge to ready.
let mut harness = leased_and_installed_tx();
harness.observe(edge::EdgeEvent::DriverEdgeReady {
edge_id: edge::EdgeId(7001),
});
// Pre-stop proof-like events are stale for teardown and must not satisfy
// the later release gate.
harness.observe(edge::EdgeEvent::QuiescenceProven {
ring_id: edge::RingId(8001),
});
harness.observe(edge::EdgeEvent::RingQuiesced {
ring_id: edge::RingId(8001),
});
harness.observe(edge::EdgeEvent::PumpStopped {
edge_id: edge::EdgeId(7001),
ring_id: edge::RingId(8001),
});
// Stop the edge.
harness.observe(edge::EdgeEvent::StopEdge {
edge_id: edge::EdgeId(7001),
});
// Stop commands must cover queued lease, pump, and worker ring cleanup.
assert!(
harness
.commands()
.iter()
.any(|command| { matches!(command, edge::EdgeCommand::CancelQueuedLease { .. }) })
);
assert!(
harness
.commands()
.iter()
.any(|command| { matches!(command, edge::EdgeCommand::StopPump { .. }) })
);
assert!(
harness
.commands()
.iter()
.any(|command| { matches!(command, edge::EdgeCommand::UninstallWorkerRing { .. }) })
);
// Quiescence proof alone must not release the arena while the pump may
// still write and the worker still owns the ring.
assert!(
!harness
.commands()
.iter()
.any(|command| { matches!(command, edge::EdgeCommand::ReleaseArenaLease { .. }) })
);
harness.observe(edge::EdgeEvent::QuiescenceProven {
ring_id: edge::RingId(8001),
});
assert!(
!harness
.commands()
.iter()
.any(|command| { matches!(command, edge::EdgeCommand::ReleaseArenaLease { .. }) })
);
// Pump stop plus proof is still insufficient until the worker ring is
// quiesced.
harness.observe(edge::EdgeEvent::PumpStopped {
edge_id: edge::EdgeId(7001),
ring_id: edge::RingId(8001),
});
assert!(
!harness
.commands()
.iter()
.any(|command| { matches!(command, edge::EdgeCommand::ReleaseArenaLease { .. }) })
);
harness.observe(edge::EdgeEvent::RingQuiesced {
ring_id: edge::RingId(8001),
});
assert!(harness.commands().iter().any(|command| {
matches!(
command,
edge::EdgeCommand::ReleaseArenaLease {
ring_id: edge::RingId(8001),
..
}
)
}));
// Ring quiescence must not independently release either, and pump stop plus
// ring quiescence is still insufficient until the aggregate proof arrives.
let mut ring_first = leased_and_installed_tx();
ring_first.observe(edge::EdgeEvent::DriverEdgeReady {
edge_id: edge::EdgeId(7001),
});
ring_first.observe(edge::EdgeEvent::StopEdge {
edge_id: edge::EdgeId(7001),
});
ring_first.observe(edge::EdgeEvent::RingQuiesced {
ring_id: edge::RingId(8001),
});
assert!(
!ring_first
.commands()
.iter()
.any(|command| { matches!(command, edge::EdgeCommand::ReleaseArenaLease { .. }) })
);
ring_first.observe(edge::EdgeEvent::PumpStopped {
edge_id: edge::EdgeId(7001),
ring_id: edge::RingId(8001),
});
assert!(
!ring_first
.commands()
.iter()
.any(|command| { matches!(command, edge::EdgeCommand::ReleaseArenaLease { .. }) })
);
ring_first.observe(edge::EdgeEvent::QuiescenceProven {
ring_id: edge::RingId(8001),
});
assert!(ring_first.commands().iter().any(|command| {
matches!(
command,
edge::EdgeCommand::ReleaseArenaLease {
ring_id: edge::RingId(8001),
..
}
)
}));
// If StopEdge races with worker installation before RingInstalled, the
// lease is still withheld until worker cleanup and proof complete.
let mut install_in_flight = new_establisher();
install_in_flight.observe(edge::EdgeEvent::ProvisionTx(tx_provision()));
install_in_flight.observe(edge::EdgeEvent::RingLeased {
request_id: edge::LeaseRequestId(1),
ring_id: edge::RingId(8001),
layout: edge::RingLayout::test_layout(0),
});
install_in_flight.observe(edge::EdgeEvent::StopEdge {
edge_id: edge::EdgeId(7001),
});
assert!(
install_in_flight
.commands()
.iter()
.any(|command| { matches!(command, edge::EdgeCommand::UninstallWorkerRing { .. }) })
);
assert!(
!install_in_flight
.commands()
.iter()
.any(|command| { matches!(command, edge::EdgeCommand::ReleaseArenaLease { .. }) })
);
install_in_flight.observe(edge::EdgeEvent::RingQuiesced {
ring_id: edge::RingId(8001),
});
install_in_flight.observe(edge::EdgeEvent::QuiescenceProven {
ring_id: edge::RingId(8001),
});
assert!(install_in_flight.commands().iter().any(|command| {
matches!(
command,
edge::EdgeCommand::ReleaseArenaLease {
ring_id: edge::RingId(8001),
..
}
)
}));
// Stopped is terminal: later stale events cannot revive readiness.
harness.observe(edge::EdgeEvent::Stopped {
edge_id: edge::EdgeId(7001),
});
let ready_before = harness
.events()
.iter()
.filter(|event| matches!(event, edge::EdgeLifecycleEvent::EdgeReady { .. }))
.count();
harness.observe(edge::EdgeEvent::DriverEdgeReady {
edge_id: edge::EdgeId(7001),
});
let ready_after = harness
.events()
.iter()
.filter(|event| matches!(event, edge::EdgeLifecycleEvent::EdgeReady { .. }))
.count();
assert_eq!(ready_after, ready_before);
}