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

120 lines
3.7 KiB
Rust
Raw Normal View History

2026-07-01 08:44:25 +00:00
use std::sync::Arc;
use datastream::{DatastreamEndpoint, Record};
use iroh::{EndpointAddr, SecretKey};
use mvp_system::bootstrap_datastream::{BootstrapDatastreamBridge, node_stream_id};
use mvp_system::provisioning::{
NodeProvisionSpec, PluginObservation, PluginObservationSink, PluginSink, ProvisionLogStream,
};
use mvp_system::telemetry::MvpProvisionLogRecord;
use parking_lot::Mutex;
use serde_json::json;
use swactor::actor::ActorAddress;
#[derive(Default)]
struct RecordingSink {
observations: Mutex<Vec<PluginObservation>>,
}
impl RecordingSink {
fn observations(&self) -> Vec<PluginObservation> {
self.observations.lock().clone()
}
}
impl PluginObservationSink for RecordingSink {
fn observe(&self, observation: PluginObservation) {
self.observations.lock().push(observation);
}
}
fn recording_sink() -> (Arc<RecordingSink>, PluginSink) {
let recording = Arc::new(RecordingSink::default());
(recording.clone(), PluginSink::new(recording))
}
fn spec() -> NodeProvisionSpec {
NodeProvisionSpec {
run_id: 7,
node_id: 42,
stage_index: Some(3),
image: "worker:latest".to_owned(),
env: Vec::new(),
args: Vec::new(),
2026-07-05 09:59:51 +00:00
mounts: Vec::new(),
2026-07-01 08:44:25 +00:00
}
}
#[test]
fn bootstrap_bridge_writes_node_stream_and_forwards_plugin_observations() {
let endpoint = DatastreamEndpoint::new(node_stream_id(7, 42));
let subscription = endpoint.subscribe_all("test");
let (recording, sink) = recording_sink();
let bridge = BootstrapDatastreamBridge::new(spec(), sink, Some(endpoint.producer()));
2026-07-07 10:40:02 +00:00
bridge.observe_stdout_line("ssh stdout diagnostic");
bridge.observe_stderr_line("debug1: ssh stderr diagnostic");
2026-07-01 08:44:25 +00:00
endpoint.tick();
let observations = recording.observations();
assert_eq!(
observations,
vec![
PluginObservation::StdoutLine {
run_id: 7,
node_id: 42,
2026-07-07 10:40:02 +00:00
line: "ssh stdout diagnostic".to_owned(),
2026-07-01 08:44:25 +00:00
},
PluginObservation::StderrLine {
run_id: 7,
node_id: 42,
2026-07-07 10:40:02 +00:00
line: "debug1: ssh stderr diagnostic".to_owned(),
2026-07-01 08:44:25 +00:00
},
]
);
let deliveries = subscription.drain_available();
2026-07-07 10:40:02 +00:00
let logs = deliveries
.iter()
.filter(|delivery| delivery.stream == node_stream_id(7, 42))
.filter_map(|delivery| MvpProvisionLogRecord::decode(&delivery.frame.payload).ok())
.collect::<Vec<_>>();
assert_eq!(logs.len(), 2, "{deliveries:?}");
2026-07-01 08:44:25 +00:00
2026-07-07 10:40:02 +00:00
let stdout = &logs[0];
let stderr = &logs[1];
2026-07-01 08:44:25 +00:00
assert_eq!(stdout.line.stream, ProvisionLogStream::Stdout);
2026-07-07 10:40:02 +00:00
assert_eq!(stdout.line.line, "ssh stdout diagnostic");
2026-07-01 08:44:25 +00:00
assert_eq!(stderr.line.stream, ProvisionLogStream::Stderr);
2026-07-07 10:40:02 +00:00
assert_eq!(stderr.line.line, "debug1: ssh stderr diagnostic");
2026-07-01 08:44:25 +00:00
}
#[test]
fn ready_json_on_stdout_emits_runtime_ready_through_plugin_sink() {
let (recording, sink) = recording_sink();
let bridge = BootstrapDatastreamBridge::new(spec(), sink, None);
let endpoint = EndpointAddr::new(SecretKey::from_bytes(&[7; 32]).public());
let node_actor = ActorAddress::new_random();
let line = serde_json::to_string(&json!({
"type": "ready",
"endpoint": endpoint,
"node_actor": node_actor,
"logical_node_id": 42,
"stage_index": 3,
}))
.unwrap();
bridge.observe_stdout_line(line);
assert!(recording.observations().iter().any(|observation| matches!(
observation,
PluginObservation::RuntimeReady {
run_id: 7,
node_id: 42,
stage_index: Some(3),
endpoint: observed_endpoint,
node_actor: observed_actor,
} if observed_endpoint == &endpoint && observed_actor == &node_actor
)));
}