Files
galaxy/crates/local_control/src/discovery_tests.rs
T
rkw6086 dbfa8bcd48 Complete agent monitoring and Galaxy Control integration
- expose command-monitor conversations and preserve visible agent transcripts
- add bounded polling and a dedicated shell interrupt tool
- improve direct-provider images, skills, tool history, and usage handling
- package and brand Galaxy Control across releases, installers, persistence, and docs
2026-07-29 15:04:58 -05:00

336 lines
11 KiB
Rust

use std::fs;
use std::path::Path;
#[cfg(unix)]
use command::blocking::Command;
use super::*;
#[test]
fn control_endpoint_composes_loopback_control_route() {
assert_eq!(
ControlEndpoint::localhost(4000).url(),
"http://127.0.0.1:4000/v1/control"
);
}
#[test]
fn broker_socket_reference_is_bound_to_instance_identity() {
let record = InstanceRecord::for_current_process(
Some(ControlEndpoint::localhost(4000)),
"local",
"dev.galaxy.GalaxyLocal",
Some("test".to_owned()),
crate::protocol::ActionKind::implemented_metadata(),
);
assert_eq!(
record
.credential_broker
.expect("credential broker")
.socket_path,
PathBuf::from(format!("{}.broker.sock", record.instance_id.0))
);
}
#[test]
fn registered_instance_round_trips_discovery_record() {
let dir = tempfile::tempdir().expect("temp dir");
let record = InstanceRecord::for_current_process(
Some(ControlEndpoint::localhost(4000)),
"local",
"dev.galaxy.GalaxyLocal",
Some("test".to_owned()),
crate::protocol::ActionKind::implemented_metadata(),
);
let _registered = RegisteredInstance::register_in_dir_for_test(record.clone(), dir.path())
.expect("registered");
let records = list_instances_from_dir(dir.path(), "local");
assert_eq!(records, vec![record]);
}
#[test]
fn incompatible_protocol_record_is_ignored() {
let dir = tempfile::tempdir().expect("temp dir");
let mut record = InstanceRecord::for_current_process(
Some(ControlEndpoint::localhost(4000)),
"local",
"dev.galaxy.GalaxyLocal",
Some("test".to_owned()),
crate::protocol::ActionKind::implemented_metadata(),
);
record.protocol_version = PROTOCOL_VERSION + 1;
let _registered =
RegisteredInstance::register_in_dir_for_test(record, dir.path()).expect("registered");
assert!(list_instances_from_dir(dir.path(), "local").is_empty());
}
#[test]
fn malformed_record_and_matching_broker_socket_are_pruned() {
let dir = tempfile::tempdir().expect("temp dir");
let record_path = dir.path().join("inst_malformed.json");
let socket_path = dir.path().join("inst_malformed.broker.sock");
fs::write(&record_path, "not json").expect("write malformed record");
fs::write(&socket_path, "").expect("write broker socket");
assert!(list_instances_from_dir(dir.path(), "local").is_empty());
assert!(!record_path.exists());
assert!(!socket_path.exists());
}
#[test]
fn orphan_broker_sockets_are_pruned_after_grace_period() {
let dir = tempfile::tempdir().expect("temp dir");
let orphan_path = dir.path().join("inst_orphan.broker.sock");
let retained_filename = PathBuf::from("inst_retained.broker.sock");
let retained_path = dir.path().join(&retained_filename);
fs::write(&orphan_path, "").expect("write orphan socket");
fs::write(&retained_path, "").expect("write retained socket");
sweep_orphan_broker_sockets(
dir.path(),
&HashSet::from([retained_filename]),
Duration::ZERO,
);
assert!(!orphan_path.exists());
assert!(retained_path.exists());
}
#[test]
fn abandoned_temp_records_are_pruned_after_grace_period() {
let dir = tempfile::tempdir().expect("temp dir");
let temp_path = dir.path().join("inst_abandoned.json.tmp");
fs::write(&temp_path, "").expect("write temporary record");
sweep_abandoned_temp_records(dir.path(), Duration::ZERO);
assert!(!temp_path.exists());
}
#[cfg(unix)]
#[test]
fn stale_process_record_is_pruned() {
let dir = tempfile::tempdir().expect("temp dir");
let mut child = Command::new("true")
.spawn()
.expect("short-lived process starts");
let pid = child.id();
child.wait().expect("short-lived process exits");
let mut record = InstanceRecord::for_current_process(
Some(ControlEndpoint::localhost(4000)),
"local",
"dev.galaxy.GalaxyLocal",
Some("test".to_owned()),
crate::protocol::ActionKind::implemented_metadata(),
);
record.pid = pid;
let socket_path = dir.path().join(broker_socket_filename(&record.instance_id));
fs::write(&socket_path, "").expect("write broker socket");
let registered =
RegisteredInstance::register_in_dir_for_test(record, dir.path()).expect("registered");
assert!(list_instances_from_dir(dir.path(), "local").is_empty());
assert!(!registered.path.exists());
assert!(!socket_path.exists());
}
#[cfg(unix)]
#[test]
fn multiple_live_process_records_are_discovered() {
let dir = tempfile::tempdir().expect("temp dir");
let mut first_process = Command::new("sleep")
.arg("10")
.spawn()
.expect("first process starts");
let mut second_process = Command::new("sleep")
.arg("10")
.spawn()
.expect("second process starts");
let mut first_record = InstanceRecord::for_current_process(
Some(ControlEndpoint::localhost(4000)),
"local",
"dev.galaxy.GalaxyLocal",
Some("test".to_owned()),
crate::protocol::ActionKind::implemented_metadata(),
);
first_record.pid = first_process.id();
let mut second_record = InstanceRecord::for_current_process(
Some(ControlEndpoint::localhost(4001)),
"local",
"dev.galaxy.GalaxyLocal",
Some("test".to_owned()),
crate::protocol::ActionKind::implemented_metadata(),
);
second_record.pid = second_process.id();
let first_id = first_record.instance_id.clone();
let second_id = second_record.instance_id.clone();
let _first = RegisteredInstance::register_in_dir_for_test(first_record, dir.path())
.expect("first registered");
let _second = RegisteredInstance::register_in_dir_for_test(second_record, dir.path())
.expect("second registered");
let ids = list_instances_from_dir(dir.path(), "local")
.into_iter()
.map(|record| record.instance_id)
.collect::<Vec<_>>();
assert_eq!(ids.len(), 2);
assert!(ids.contains(&first_id));
assert!(ids.contains(&second_id));
first_process.kill().expect("first process stops");
first_process.wait().expect("first process reaped");
second_process.kill().expect("second process stops");
second_process.wait().expect("second process reaped");
}
#[test]
fn records_from_other_channels_are_ignored() {
let dir = tempfile::tempdir().expect("temp dir");
let record = InstanceRecord::for_current_process(
Some(ControlEndpoint::localhost(4000)),
"dev",
"dev.galaxy.Galaxy-Dev",
Some("test".to_owned()),
crate::protocol::ActionKind::implemented_metadata(),
);
let socket_path = dir.path().join(broker_socket_filename(&record.instance_id));
fs::write(&socket_path, "").expect("write broker socket");
let _registered =
RegisteredInstance::register_in_dir_for_test(record, dir.path()).expect("registered");
assert!(list_instances_from_dir(dir.path(), "local").is_empty());
assert!(socket_path.exists());
}
#[test]
fn serialized_discovery_record_does_not_contain_raw_credential_material() {
let raw_secret = "raw-secret-token-material";
let record = InstanceRecord::for_current_process(
Some(ControlEndpoint::localhost(4000)),
"local",
"dev.galaxy.GalaxyLocal",
Some("test".to_owned()),
crate::protocol::ActionKind::implemented_metadata(),
);
let serialized = serde_json::to_string_pretty(&record).expect("serialize");
assert!(!serialized.contains(raw_secret));
assert!(!serialized.contains("auth_token"));
assert!(!serialized.contains("bearer_token"));
}
#[test]
fn disabled_record_does_not_expose_actionable_authority() {
let record = InstanceRecord::for_current_process(
None,
"local",
"dev.galaxy.GalaxyLocal",
Some("test".to_owned()),
crate::protocol::ActionKind::implemented_metadata(),
);
assert!(record.endpoint.is_none());
assert!(record.credential_broker.is_none());
}
#[test]
fn rejects_unsafe_or_divergent_discovery_authority() {
let mut record = InstanceRecord::for_current_process(
Some(ControlEndpoint::localhost(4000)),
"local",
"dev.galaxy.GalaxyLocal",
Some("test".to_owned()),
crate::protocol::ActionKind::implemented_metadata(),
);
record
.validate_local_control_authority()
.expect("matching 127.0.0.1 endpoints are accepted");
record.endpoint.as_mut().expect("endpoint").host = "localhost".to_owned();
let err = record
.validate_local_control_authority()
.expect_err("localhost alias is rejected");
assert_eq!(err.code, ErrorCode::UnauthorizedLocalClient);
record.endpoint = Some(ControlEndpoint::localhost(4000));
record
.credential_broker
.as_mut()
.expect("credential broker")
.socket_path = "different.broker.sock".into();
let err = record
.validate_local_control_authority()
.expect_err("divergent broker socket is rejected");
assert_eq!(err.code, ErrorCode::UnauthorizedLocalClient);
}
#[cfg(unix)]
#[test]
fn discovery_directory_is_owner_only_on_unix() {
use std::os::unix::fs::PermissionsExt as _;
let dir = tempfile::tempdir().expect("temp dir");
let record = InstanceRecord::for_current_process(
Some(ControlEndpoint::localhost(4000)),
"local",
"dev.galaxy.GalaxyLocal",
Some("test".to_owned()),
crate::protocol::ActionKind::implemented_metadata(),
);
let _registered =
RegisteredInstance::register_in_dir_for_test(record, dir.path()).expect("registered");
let mode = fs::metadata(dir.path())
.expect("metadata")
.permissions()
.mode()
& 0o777;
assert_eq!(mode, 0o700);
}
#[cfg(unix)]
#[test]
fn discovery_record_is_owner_only_on_unix() {
let dir = tempfile::tempdir().expect("temp dir");
let record = InstanceRecord::for_current_process(
Some(ControlEndpoint::localhost(4000)),
"local",
"dev.galaxy.GalaxyLocal",
Some("test".to_owned()),
crate::protocol::ActionKind::implemented_metadata(),
);
let registered =
RegisteredInstance::register_in_dir_for_test(record, dir.path()).expect("registered");
let mode = fs::metadata(&registered.path)
.expect("metadata")
.permissions()
.mode()
& 0o777;
assert_eq!(mode, 0o600);
}
impl RegisteredInstance {
fn register_in_dir_for_test(record: InstanceRecord, dir: &Path) -> Result<Self, ControlError> {
fs::create_dir_all(dir).expect("create dir");
#[cfg(unix)]
set_private_dir_permissions(dir)?;
let path = record_path(dir, &record.instance_id);
let bytes = serde_json::to_vec_pretty(&record).map_err(|err| {
ControlError::with_details(
ErrorCode::Internal,
"failed to serialize local-control discovery test record",
err.to_string(),
)
})?;
fs::write(&path, bytes).map_err(|err| {
ControlError::with_details(
ErrorCode::Internal,
"failed to write local-control discovery test record",
err.to_string(),
)
})?;
#[cfg(unix)]
set_private_permissions(&path)?;
Ok(Self {
record,
path,
broker_socket_path: None,
})
}
}