use insta::glob;
use insta_cmd::assert_cmd_snapshot;
use mockito::Server;
use moonup::constant;
use serial_test::serial;
use std::{env, fs};
use crate::util::{self, TestWorkspace};
#[test]
#[serial]
fn test_basic_flow() {
util::apply_common_filters!();
let ws = TestWorkspace::new();
assert_cmd_snapshot!("moonup_completions", ws.cli().arg("completions").arg("zsh"));
assert_cmd_snapshot!("moonup_show", ws.cli().arg("show"));
assert_cmd_snapshot!("moonup_default", ws.cli().arg("default").arg("latest"));
assert_cmd_snapshot!("moonup_default_2", ws.cli().arg("default"));
let project_path = ws.project_path();
fs::create_dir_all(project_path).expect("should create project directory");
env::set_current_dir(project_path).expect("should set current directory");
assert_cmd_snapshot!("moonup_pin", ws.cli().arg("pin").arg("nightly"));
assert!(project_path.join(constant::TOOLCHAIN_FILE).exists());
assert_cmd_snapshot!("moonup_pin_2", ws.cli().arg("pin"));
assert_cmd_snapshot!(
"moonup_run_not_installed",
ws.cli().arg("run").arg("nightly").arg("moon").arg("--help")
);
env::set_current_dir(ws.tempdir()).expect("should restore current directory");
}
#[test]
fn test_flow_with_network_mock() {
util::apply_common_filters!();
let ws = TestWorkspace::new();
let mut s = Server::new();
glob!("../fixtures/dist_server", "**/*.json", |path: &Path| {
let path = path.display().to_string();
#[cfg(target_os = "windows")]
let path = path.replace("\\", "/");
let pathname = path.rsplit_once("dist_server").unwrap().1;
s.mock("GET", pathname)
.with_body_from_file(path)
.with_header("content-type", "application/json")
.create();
});
assert_cmd_snapshot!(
"moonup_install_list_available_mock",
ws.cli()
.env(constant::ENVNAME_MOONUP_DIST_SERVER, s.url())
.arg("install")
.arg("--list-available")
);
assert_cmd_snapshot!(
"moonup_install_list_available_mock_2",
ws.cli()
.env(constant::ENVNAME_MOONUP_DIST_SERVER, s.url())
.arg("install")
.arg("--list-available")
.arg("-vvv")
);
}
#[cfg(feature = "test-extra")]
mod liveinstall {
use super::*;
use serial_test::serial;
#[cfg(feature = "test-liveinstall")]
#[test]
#[serial]
fn test_flow_with_network() {
util::apply_common_filters!();
let ws = TestWorkspace::new();
let test_install_version = "0.1.20241231+ba15a9a4e";
let moon_exe_name = std::ffi::OsStr::new("moon");
let build_moonup_path = ws.moonup();
let build_output_path = build_moonup_path.parent().expect("should have parent");
assert_cmd_snapshot!("moonup_selfupdate", ws.cli().arg("self-update"));
assert_cmd_snapshot!(
"moonup_install",
ws.cli().arg("install").arg(test_install_version)
);
assert_cmd_snapshot!("moonup_list_1", ws.cli().arg("list"));
let project_path = ws.project_path();
fs::create_dir_all(project_path).expect("should create project directory");
env::set_current_dir(project_path).expect("should set current directory");
let pin_file = project_path.join(constant::TOOLCHAIN_FILE);
assert_cmd_snapshot!("moonup_pin", ws.cli().arg("pin").arg(test_install_version));
assert!(pin_file.exists());
assert_cmd_snapshot!("moonup_which", ws.cli().arg("which").arg(moon_exe_name));
assert_cmd_snapshot!(
"moonup_run",
ws.cli()
.arg("run")
.arg(test_install_version)
.arg(moon_exe_name)
.arg("version")
);
let moon_path = ws.moon_home().join("bin").display().to_string();
let current_path = env::var("PATH").unwrap_or_else(|_| String::new());
let updated_path = if current_path.is_empty() {
moon_path
} else {
let env_separator = if cfg!(windows) { ";" } else { ":" };
format!(
"{}{}{}{}{}",
moon_path,
env_separator,
build_output_path.display(),
env_separator,
current_path
)
};
temp_env::with_var("PATH", Some(updated_path.clone()), || {
let mut cmd_moon = ws.cmd(moon_exe_name);
assert_cmd_snapshot!(
"moon_use_pinned_version",
cmd_moon.arg("version").arg("--all")
);
let mut cmd_moon = ws.cmd(moon_exe_name);
assert_cmd_snapshot!("moon_upgrade_intercept", cmd_moon.arg("upgrade"));
});
std::fs::remove_file(pin_file).expect("should remove pinned toolchain file");
temp_env::with_var("PATH", Some(updated_path.clone()), || {
let mut cmd_moon = ws.cmd(moon_exe_name);
assert_cmd_snapshot!(
"moon_use_version_from_arg",
cmd_moon
.arg(format!("+{}", test_install_version))
.arg("version")
);
});
assert_cmd_snapshot!(
"moonup_uninstall_keep_cache",
ws.cli()
.arg("uninstall")
.arg(test_install_version)
.arg("--keep-cache")
);
let cache_path = ws
.moonup_home()
.join("downloads")
.join("latest")
.join(test_install_version);
assert!(cache_path.exists());
assert_cmd_snapshot!(
"moonup_default",
ws.cli().arg("default").arg(test_install_version)
);
temp_env::with_var("PATH", Some(updated_path.clone()), || {
let mut cmd_moon = ws.cmd(moon_exe_name);
assert_cmd_snapshot!("moon_use_default_version", cmd_moon.arg("version"));
});
assert_cmd_snapshot!("moonup_list_2", ws.cli().arg("list"));
assert_cmd_snapshot!(
"moonup_uninstall_clear",
ws.cli().arg("uninstall").arg("--clear")
);
assert!(!cache_path.exists());
assert_cmd_snapshot!(
"moonup_uninstall",
ws.cli().arg("uninstall").arg(test_install_version)
);
let custom_bin = ws.moon_home().join("bin").join(if cfg!(windows) {
"moonup-e2e-user-bin.cmd"
} else {
"moonup-e2e-user-bin"
});
fs::write(&custom_bin, "@echo off\n").expect("should create custom bin");
let install_path = ws
.moonup_home()
.join("toolchains")
.join(test_install_version);
assert!(!install_path.exists());
assert_cmd_snapshot!("moonup_list_3", ws.cli().arg("list"));
assert_cmd_snapshot!(
"moonup_install_neverexists",
ws.cli().arg("install").arg("neverexists")
);
assert_cmd_snapshot!(
"moonup_install_latest",
ws.cli().arg("install").arg("latest")
);
assert_cmd_snapshot!(
"moonup_install_nightly",
ws.cli().arg("install").arg("nightly")
);
let test_nightly_version = "nightly-2026-01-31";
assert_cmd_snapshot!(
"moonup_install_nightly_2",
ws.cli().arg("install").arg(test_nightly_version)
);
temp_env::with_var("PATH", Some(updated_path.clone()), || {
let mut cmd_moonlsp = ws.cmd("moon-lsp");
assert_cmd_snapshot!(
"moon_lsp_nodebased_lsp_version",
cmd_moonlsp
.arg(format!("+{}", test_nightly_version))
.arg("-v")
);
});
let path = ws.moon_home().join("bin").display().to_string();
temp_env::with_var("PATH", Some(path), || {
let mut cmd_moonlsp = ws.cmd("moon-lsp");
assert_cmd_snapshot!(
"moon_lsp_nodebased_lsp_help",
cmd_moonlsp
.arg(format!("+{}", test_nightly_version))
.arg("--help")
);
});
assert_cmd_snapshot!("moonup_update", ws.cli().arg("update"));
assert!(custom_bin.exists());
temp_env::with_var("MOONUP_TEST_FORCE_SELFUPDATE", Some("1"), || {
assert_cmd_snapshot!("moonup_selfupdate_forced", ws.cli().arg("self-update"));
});
env::set_current_dir(ws.tempdir()).expect("should restore current directory");
}
#[cfg(feature = "test-interactive")]
#[cfg(not(windows))]
#[test]
#[serial]
fn test_flow_interactive() {
use expectrl::{ControlCode, Eof, Expect, Session};
use insta::assert_snapshot;
util::apply_common_filters!();
let ws = TestWorkspace::new();
let project_path = ws.project_path();
fs::create_dir_all(project_path).expect("should create project directory");
env::set_current_dir(project_path).expect("should set current directory");
let mut cmd = ws.cli();
cmd.arg("install").arg("latest").arg("--list-available");
let mut p = Session::spawn(cmd).expect("should spawn moonup process");
let duration = std::time::Duration::from_secs(60);
p.set_expect_timeout(Some(duration));
p.expect("Pick a version from latest channel")
.expect("should prompt for selecting version");
p.send_line("").expect("should send new line");
let output =
String::from_utf8_lossy(p.expect(Eof).expect("should finish installing").as_bytes())
.to_string();
let re = regex::Regex::new(r"(?s)Installing toolchain.*?Installed").unwrap();
let output = re.replace_all(&output, "Installing toolchain...\n...\nInstalled");
assert_snapshot!("moonup_install_interactive", output);
let mut cmd = ws.cli();
cmd.arg("pin");
let mut p = Session::spawn(cmd).expect("should spawn moonup process");
p.expect("Pick a installed version")
.expect("should prompt for selecting installed version");
p.send_line("").expect("should send new line");
let output =
String::from_utf8_lossy(p.expect(Eof).expect("should finish pinning").as_bytes())
.to_string();
assert_snapshot!("moonup_pin_interactive", output);
let mut cmd = ws.cli();
cmd.arg("default");
let mut p = Session::spawn(cmd).expect("should spawn moonup process");
p.expect("Pick a installed version")
.expect("should prompt for selecting installed version");
p.send_line("").expect("should send new line");
let output =
String::from_utf8_lossy(p.expect(Eof).expect("should finish defaulting").as_bytes())
.to_string();
assert_snapshot!("moonup_default_interactive", output);
let mut cmd = ws.cli();
cmd.arg("uninstall");
let mut p = Session::spawn(cmd).expect("should spawn moonup process");
p.expect("Select toolchains to uninstall")
.expect("should prompt for selecting installed version");
p.send_line(ControlCode::Space) .expect("should send space");
p.send_line("").expect("should send new line");
let output = String::from_utf8_lossy(
p.expect(Eof)
.expect("should finish uninstalling")
.as_bytes(),
)
.to_string();
assert_snapshot!("moonup_uninstall_interactive", output);
env::set_current_dir(ws.tempdir()).expect("should restore current directory");
}
}