use std::{path::Path, process::Command};
mod common;
use common::*;
fn spawn_miden(
miden: &Path,
project: &Path,
env: &TestEnvironment,
manifest_uri: &str,
) -> std::process::Child {
midenup_command_via_xdg(miden, env, manifest_uri)
.args(["help", "vm"])
.current_dir(project)
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped())
.spawn()
.expect("failed to spawn miden")
}
fn write_toolchain_file(project: &Path, components: &[&str]) {
std::fs::create_dir_all(project).unwrap();
let components = components
.iter()
.map(|name| format!("\"{name}\""))
.collect::<Vec<_>>()
.join(", ");
std::fs::write(
project.join("miden-toolchain.toml"),
format!("[toolchain]\nchannel = \"0.15.0\"\ncomponents = [{components}]\n"),
)
.unwrap();
}
#[test]
fn integration_concurrent_activations_converge_on_a_superset() {
let _guard = common::harness::mutating_test_guard();
let env = environment_setup("concurrent_activations");
let fixture = common::harness::OfflineFixture::create(env.tmp_dir.path(), "0.15.0");
let bin = env.tmp_dir.path().join("bin");
std::fs::create_dir_all(&bin).unwrap();
let miden = bin.join("miden");
std::os::unix::fs::symlink(env!("CARGO_BIN_EXE_midenup"), &miden).unwrap();
let plain = env.tmp_dir.path().join("project-plain");
let with_assets = env.tmp_dir.path().join("project-assets");
write_toolchain_file(&plain, &[]);
write_toolchain_file(&with_assets, &["assets"]);
let first = spawn_miden(&miden, &plain, &env, &fixture.manifest_uri);
let second = spawn_miden(&miden, &with_assets, &env, &fixture.manifest_uri);
let outputs = [first, second].map(|child| child.wait_with_output().expect("failed to wait"));
for output in &outputs {
assert!(
output.status.success(),
"both invocations must succeed.\nstdout:\n{}\nstderr:\n{}\ntree:\n{}",
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr),
String::from_utf8_lossy(
&Command::new("find")
.arg(env.tmp_dir.path())
.output()
.map(|o| o.stdout)
.unwrap_or_default()
)
);
}
let channel = semver::Version::new(0, 15, 0);
let state =
midenup::state::LocalState::load(&midenup::paths::state_path(&env.midenup_home)).unwrap();
let installation = state.get(&channel).expect("the channel must be recorded as installed");
assert!(
installation.intent.roots.contains("assets"),
"the second project's request must survive the first's write: {:?}",
installation.intent
);
let names: Vec<_> = installation.components.iter().map(|c| c.name.to_string()).collect();
assert!(
names.contains(&"vm".to_string()) && names.contains(&"assets".to_string()),
"both activations' components must be installed; got {names:?}"
);
let toolchain = midenup::paths::toolchain_link(&env.midenup_home, &channel);
assert!(toolchain.join("bin").join("miden-vm").exists());
assert!(toolchain.join("etc").join("assets").join("config.yml").exists());
let midenup::state::PublicationRef::Managed { id, .. } = &installation.publication else {
panic!("expected a managed publication");
};
assert!(
midenup::paths::publication_dir(&env.midenup_home, &channel, id).is_dir(),
"the recorded publication must exist"
);
}
#[test]
fn integration_dispatch_against_an_installed_toolchain_makes_no_network_request() {
let _guard = common::harness::mutating_test_guard();
let env = environment_setup("offline_dispatch");
let fixture = common::harness::OfflineFixture::create(env.tmp_dir.path(), "0.15.0");
let install = midenup_command(env!("CARGO_BIN_EXE_midenup"), &env, &fixture.manifest_uri)
.args(["install", "0.15.0"])
.output()
.expect("failed to run midenup");
assert!(install.status.success(), "{}", String::from_utf8_lossy(&install.stderr));
assert!(
env.midenup_home.join("toolchains").join("0.15.0").exists(),
"the toolchain must be installed before dispatch is tested"
);
std::fs::remove_file(midenup::paths::manifest_cache(&env.midenup_home)).unwrap();
let bin = env.tmp_dir.path().join("bin");
std::fs::create_dir_all(&bin).unwrap();
let miden = bin.join("miden");
std::os::unix::fs::symlink(env!("CARGO_BIN_EXE_midenup"), &miden).unwrap();
let project = env.tmp_dir.path().join("project");
write_toolchain_file(&project, &[]);
let output = midenup_command_via_xdg(&miden, &env, "https://127.0.0.1:1/nope.json")
.args(["help", "vm"])
.current_dir(&project)
.output()
.expect("failed to run miden");
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
output.status.success(),
"dispatch must not need upstream.\nstdout:\n{}\nstderr:\n{stderr}",
String::from_utf8_lossy(&output.stdout),
);
assert!(
!stderr.contains("could not reach"),
"dispatch must not even *attempt* a fetch: {stderr}"
);
}
#[test]
fn integration_an_operation_needing_upstream_falls_back_to_the_cached_manifest() {
let _guard = common::harness::mutating_test_guard();
let env = environment_setup("cached_upstream");
let fixture = common::harness::OfflineFixture::create(env.tmp_dir.path(), "0.15.0");
let midenup = |manifest_uri: &str, args: &[&str]| {
midenup_command(env!("CARGO_BIN_EXE_midenup"), &env, manifest_uri)
.args(args)
.output()
.expect("failed to run midenup")
};
let installed = midenup(&fixture.manifest_uri, &["install", "0.15.0"]);
assert!(installed.status.success(), "{}", String::from_utf8_lossy(&installed.stderr));
assert!(
midenup::paths::manifest_cache(&env.midenup_home).exists(),
"a successful fetch must be cached"
);
let output = midenup("https://127.0.0.1:1/nope.json", &["list"]);
assert!(
output.status.success(),
"the cache must let it proceed: {}",
String::from_utf8_lossy(&output.stderr)
);
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(stderr.contains("cached"), "staleness must be reported: {stderr}");
assert!(
String::from_utf8_lossy(&output.stdout).contains("0.15.0"),
"and the cached content must actually be used"
);
}