use super::*;
use std::{fs, path::PathBuf, time::SystemTime};
#[cfg(unix)]
use std::os::unix::fs::PermissionsExt;
#[test]
fn repository_ic_wasm_authority_matches_every_install_projection() {
assert_eq!(
IC_WASM_VERSION,
crate::test_support::ic_tool_pin("ic-wasm", "linux-x86_64", 1)
);
let expected_projections = [
("macos", "aarch64", "aarch64-apple-darwin", "DARWIN_ARM64"),
("macos", "x86_64", "x86_64-apple-darwin", "DARWIN_X64"),
("linux", "x86_64", "x86_64-unknown-linux-gnu", "LINUX_X64"),
];
for (os, arch, archive_platform, pin_suffix) in expected_projections {
let authority = ic_wasm_authority_for(os, arch).expect("install-capable projection");
assert_eq!(authority.archive_platform(), archive_platform);
assert_eq!(
authority.archive_sha256(),
crate::test_support::ic_tool_pin(
"ic-wasm",
match pin_suffix {
"DARWIN_ARM64" => "darwin-arm64",
"DARWIN_X64" => "darwin-x86_64",
_ => "linux-x86_64",
},
3
)
);
}
let standalone = ic_wasm_authority_for("linux", "aarch64").unwrap();
assert_eq!(standalone.archive_platform(), "aarch64-unknown-linux-gnu");
let standalone_pins: Vec<_> = include_str!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/../../tool-versions.env"
))
.lines()
.filter_map(|line| line.strip_prefix("export CANIC_IC_WASM_SHA256_LINUX_ARM64="))
.collect();
assert_eq!(standalone_pins, [standalone.archive_sha256()]);
assert_eq!(
SUPPORTED_IC_WASM_AUTHORITIES.len(),
expected_projections.len() + 1
);
}
#[cfg(unix)]
#[test]
fn incompatible_path_selected_ic_wasm_is_rejected_with_its_exact_path() {
let root = temp_root("wrong-version");
fs::create_dir_all(&root).expect("create test root");
let executable = root.join(IC_WASM_TOOL);
write_executable(&executable, "#!/bin/sh\nprintf 'ic-wasm 0.9.11\\n'\n");
let error = admit_ic_wasm_executable(&executable).expect_err("wrong version must reject");
assert!(matches!(
error,
IcWasmToolError::VersionMismatch { path, actual, expected }
if path == executable
&& actual == "ic-wasm 0.9.11"
&& expected == IC_WASM_VERSION_IDENTITY
));
fs::remove_dir_all(root).expect("remove test root");
}
#[cfg(unix)]
#[test]
fn admitted_executable_records_absolute_path_and_exact_version() {
let root = temp_root("admitted");
fs::create_dir_all(&root).expect("create test root");
let executable = root.join(IC_WASM_TOOL);
write_executable(
&executable,
&crate::test_support::tool_script("#!/bin/sh\nprintf '@IC_WASM_IDENTITY@\\n'\n"),
);
let admitted = admit_ic_wasm_executable(&executable).expect("admit exact tool");
assert_eq!(admitted.path(), executable);
assert_eq!(admitted.version_identity(), IC_WASM_VERSION_IDENTITY);
fs::remove_dir_all(root).expect("remove test root");
}
#[cfg(unix)]
#[test]
fn canonical_install_precedes_a_path_wrapper() {
const CHILD_ENV: &str = "CANIC_TEST_IC_WASM_CANONICAL_PRECEDENCE_CHILD";
if std::env::var_os(CHILD_ENV).is_some() {
let admitted = resolve_required_ic_wasm().expect("resolve canonical installed ic-wasm");
assert!(admitted.path().ends_with(".local/bin/ic-wasm"));
assert_eq!(admitted.version_identity(), IC_WASM_VERSION_IDENTITY);
return;
}
let root = temp_root("canonical-precedence");
let canonical = root.join(".local/bin/ic-wasm");
let path_directory = root.join("path-bin");
let wrapper = path_directory.join(IC_WASM_TOOL);
fs::create_dir_all(canonical.parent().expect("canonical parent"))
.expect("create canonical bin");
fs::create_dir_all(&path_directory).expect("create PATH bin");
write_executable(
&canonical,
&crate::test_support::tool_script("#!/bin/sh\nprintf '@IC_WASM_IDENTITY@\\n'\n"),
);
write_executable(&wrapper, "#!/bin/sh\nprintf 'ic-wasm 0.9.11\\n'\n");
let output = std::process::Command::new(std::env::current_exe().expect("current test binary"))
.args([
"--exact",
"ic_wasm::tests::canonical_install_precedes_a_path_wrapper",
])
.env("HOME", &root)
.env("PATH", &path_directory)
.env(CHILD_ENV, "1")
.output()
.expect("run isolated precedence test");
assert!(
output.status.success(),
"isolated precedence test failed: {}",
String::from_utf8_lossy(&output.stderr)
);
fs::remove_dir_all(root).expect("remove test root");
}
#[cfg(target_os = "linux")]
#[test]
fn staged_installer_closes_its_writer_before_executable_admission() {
let root = temp_root("staged-admission");
fs::create_dir_all(&root).expect("create test root");
let candidate = root.join("candidate-ic-wasm");
write_executable(
&candidate,
&crate::test_support::tool_script("#!/bin/sh\nprintf '@IC_WASM_IDENTITY@\\n'\n"),
);
let destination = root.join("bin/ic-wasm");
tool_install::publish_executable(IC_WASM_TOOL, &candidate, &destination, |path| {
admit_ic_wasm_executable(path).map(|_| ())
})
.expect("publish and admit closed staged executable");
let admitted = admit_ic_wasm_executable(&destination).expect("admit published executable");
assert_eq!(admitted.path(), destination);
assert_eq!(admitted.version_identity(), IC_WASM_VERSION_IDENTITY);
fs::remove_dir_all(root).expect("remove test root");
}
fn temp_root(label: &str) -> PathBuf {
let nanos = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.expect("system time after epoch")
.as_nanos();
std::env::temp_dir().join(format!(
"canic-ic-wasm-{label}-{}-{nanos}",
std::process::id()
))
}
#[cfg(unix)]
fn write_executable(path: &Path, contents: &str) {
fs::write(path, contents).expect("write fake executable");
fs::set_permissions(path, fs::Permissions::from_mode(0o755))
.expect("make fake executable executable");
}
#[test]
fn npm_distribution_binds_exact_platform_and_preserves_launcher_search_order() {
let paths = npm_binary_candidates(
Path::new("/tools/node_modules/@icp-sdk/ic-wasm/bin/ic-wasm.js"),
Path::new("/workspace"),
"linux",
"x86_64",
)
.unwrap();
assert_eq!(
paths,
[
PathBuf::from(
"/tools/node_modules/@icp-sdk/ic-wasm/bin/../../../@icp-sdk/ic-wasm-linux-x64/bin/ic-wasm"
),
PathBuf::from(
"/tools/node_modules/@icp-sdk/ic-wasm/bin/../node_modules/@icp-sdk/ic-wasm-linux-x64/bin/ic-wasm"
),
PathBuf::from("/workspace/node_modules/@icp-sdk/ic-wasm-linux-x64/bin/ic-wasm"),
]
);
assert!(matches!(
npm_binary_candidates(
Path::new("/tool.js"),
Path::new("/workspace"),
"unknown",
"unknown"
),
Err(IcWasmToolError::UnsupportedPlatform { .. })
));
}