142 lines
4.9 KiB
Rust
142 lines
4.9 KiB
Rust
|
|
use std::path::Path;
|
||
|
|
use std::process::Command;
|
||
|
|
|
||
|
|
const IMAGE: &str = "swactor-mvp-local-e2e-cluster:latest";
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn local_e2e_cluster_docker_cpu_pipeline_prompt() {
|
||
|
|
if std::env::var_os("MVP_SYSTEM_LOCAL_E2E_CLUSTER").is_none() {
|
||
|
|
eprintln!("skipping; set MVP_SYSTEM_LOCAL_E2E_CLUSTER=1 to run Docker CPU cluster e2e");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
build_docker_fixture();
|
||
|
|
|
||
|
|
let output = Command::new(env!("CARGO_BIN_EXE_mvp-local-e2e-cluster"))
|
||
|
|
.arg("--prompt")
|
||
|
|
.arg("ping")
|
||
|
|
.env("MVP_LOCAL_E2E_CLUSTER_IMAGE", IMAGE)
|
||
|
|
.output()
|
||
|
|
.expect("run mvp-local-e2e-cluster");
|
||
|
|
|
||
|
|
assert!(
|
||
|
|
output.status.success(),
|
||
|
|
"mvp-local-e2e-cluster failed\nstdout:\n{}\nstderr:\n{}",
|
||
|
|
String::from_utf8_lossy(&output.stdout),
|
||
|
|
String::from_utf8_lossy(&output.stderr)
|
||
|
|
);
|
||
|
|
|
||
|
|
let value: serde_json::Value = serde_json::from_slice(&output.stdout).expect("json stdout");
|
||
|
|
assert_eq!(value["ok"], true);
|
||
|
|
assert_eq!(value["actor_plane"], "iroh-swactor");
|
||
|
|
assert_eq!(value["data_plane"], "iroh-quic-persistent-edge-streams");
|
||
|
|
assert_eq!(
|
||
|
|
value["edge_protocol"],
|
||
|
|
"edge-id-preamble-mo01-object-records"
|
||
|
|
);
|
||
|
|
assert_eq!(
|
||
|
|
value["node_local_data_plane"],
|
||
|
|
"arena-backed-rings-json-metadata-only"
|
||
|
|
);
|
||
|
|
assert_eq!(
|
||
|
|
value["worker_processes"],
|
||
|
|
"docker-tinygrad-cpu-worker-per-node"
|
||
|
|
);
|
||
|
|
assert_eq!(value["tinygrad_device"], "CPU");
|
||
|
|
assert_eq!(value["prompt_text"], "ping");
|
||
|
|
assert_eq!(value["response_text"], "pong");
|
||
|
|
assert_eq!(value["response_tokens"].as_array().map(Vec::len), Some(1));
|
||
|
|
assert_eq!(
|
||
|
|
value["engine_builder_pattern"],
|
||
|
|
"host-coordinator-static-topology-docker-workers"
|
||
|
|
);
|
||
|
|
assert_eq!(value["engine_builder_node_count"], 3);
|
||
|
|
assert_eq!(value["engine_builder_stage_assignments"], 2);
|
||
|
|
assert_eq!(value["injected_prompt_observed"], true);
|
||
|
|
assert_eq!(value["token_received_observed"], true);
|
||
|
|
assert_eq!(value["run_completed_observed"], true);
|
||
|
|
assert_eq!(value["run_torn_down_observed"], true);
|
||
|
|
assert_eq!(value["stop_sent_to_all_nodes"], true);
|
||
|
|
assert_eq!(value["stage_ready_stdout_count"], 2);
|
||
|
|
assert!(
|
||
|
|
value["node0_endpoint"]["addrs"]
|
||
|
|
.as_array()
|
||
|
|
.is_some_and(|addrs| !addrs.is_empty()),
|
||
|
|
"{value}"
|
||
|
|
);
|
||
|
|
assert!(
|
||
|
|
value["node1_endpoint"]["addrs"]
|
||
|
|
.as_array()
|
||
|
|
.is_some_and(|addrs| !addrs.is_empty()),
|
||
|
|
"{value}"
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
fn build_docker_fixture() {
|
||
|
|
let crate_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
|
||
|
|
let workspace = crate_dir
|
||
|
|
.parent()
|
||
|
|
.and_then(Path::parent)
|
||
|
|
.expect("workspace root")
|
||
|
|
.canonicalize()
|
||
|
|
.expect("canonical workspace root");
|
||
|
|
let context = std::env::temp_dir().join(format!(
|
||
|
|
"mvp-system-local-e2e-cluster-context-{}",
|
||
|
|
std::process::id()
|
||
|
|
));
|
||
|
|
let _ = std::fs::remove_dir_all(&context);
|
||
|
|
copy_workspace_context(&workspace, &context);
|
||
|
|
let dockerfile = context.join("crates/mvp-system/tests/local_e2e_cluster/Dockerfile");
|
||
|
|
|
||
|
|
phase("building Docker CPU cluster fixture image");
|
||
|
|
let build = Command::new("docker")
|
||
|
|
.args(["build", "-f"])
|
||
|
|
.arg(&dockerfile)
|
||
|
|
.args(["-t", IMAGE])
|
||
|
|
.arg(&context)
|
||
|
|
.status()
|
||
|
|
.expect("run docker build");
|
||
|
|
assert!(build.success(), "docker build failed with status {build}");
|
||
|
|
}
|
||
|
|
|
||
|
|
fn copy_workspace_context(source: &Path, dest: &Path) {
|
||
|
|
std::fs::create_dir_all(dest).expect("create docker context");
|
||
|
|
for entry in std::fs::read_dir(source).expect("read workspace") {
|
||
|
|
let entry = entry.expect("read workspace entry");
|
||
|
|
let name = entry.file_name();
|
||
|
|
let name = name.to_string_lossy();
|
||
|
|
if matches!(name.as_ref(), ".git" | "target" | ".dockerignore") {
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
copy_context_entry(&entry.path(), &dest.join(name.as_ref()));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
fn copy_context_entry(source: &Path, dest: &Path) {
|
||
|
|
let metadata = std::fs::symlink_metadata(source).expect("context metadata");
|
||
|
|
if metadata.file_type().is_symlink() {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if metadata.is_dir() {
|
||
|
|
std::fs::create_dir_all(dest).expect("create context dir");
|
||
|
|
for entry in std::fs::read_dir(source).expect("read context dir") {
|
||
|
|
let entry = entry.expect("read context entry");
|
||
|
|
let name = entry.file_name();
|
||
|
|
let name = name.to_string_lossy();
|
||
|
|
if matches!(name.as_ref(), ".git" | "target" | ".dockerignore") {
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
copy_context_entry(&entry.path(), &dest.join(name.as_ref()));
|
||
|
|
}
|
||
|
|
} else if metadata.is_file() {
|
||
|
|
if let Some(parent) = dest.parent() {
|
||
|
|
std::fs::create_dir_all(parent).expect("create context parent");
|
||
|
|
}
|
||
|
|
std::fs::copy(source, dest).expect("copy context file");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
fn phase(message: &str) {
|
||
|
|
eprintln!("local-e2e-cluster: {message}");
|
||
|
|
}
|