diff --git a/tests/packaging.rs b/tests/packaging.rs index 5e0352172..00e227d1b 100644 --- a/tests/packaging.rs +++ b/tests/packaging.rs @@ -1,8 +1,10 @@ //! The crates.io package: it carries the sources and the Factory skills the //! binary embeds, and none of the repository's own agent tooling, docs, site -//! or tests. And the binary's dependencies: HTTPS through rustls, never -//! OpenSSL (ADR 0003). +//! or tests. And the binary's dependencies, as the lockfile names them: HTTPS +//! through rustls, never OpenSSL (ADR 0003). +use std::fs; +use std::path::Path; use std::process::Command; /// The paths `cargo package` would put in the crate. @@ -24,6 +26,29 @@ fn package_list() -> Vec { .collect() } +/// The packages in a lockfile that would link OpenSSL, as `name version`. +/// +/// The lockfile names every package for every target without touching the +/// registry, where `cargo tree --target all --offline` fails unless every +/// platform's crates are already in the cargo cache. It also names the +/// dev-dependencies, so this is stricter than the binary's own normal and +/// build dependencies, which is all ADR 0003 needs: a test-only OpenSSL is +/// caught too. +fn openssl_packages(lockfile: &str) -> Vec { + let lockfile: toml::Table = lockfile.parse().expect("the lockfile is not TOML"); + lockfile["package"] + .as_array() + .expect("the lockfile has no packages") + .iter() + .filter_map(|package| { + let name = package["name"].as_str().unwrap(); + ["openssl-sys", "openssl", "native-tls"] + .contains(&name) + .then(|| format!("{name} {}", package["version"].as_str().unwrap())) + }) + .collect() +} + #[test] fn package_leaves_out_the_repository_tooling() { for path in package_list() { @@ -60,30 +85,48 @@ fn package_carries_the_factory_skills_with_their_license_and_credits() { #[test] fn the_binary_never_links_openssl() { - let output = Command::new(env!("CARGO")) - .args([ - "tree", - "--edges", - "normal,build", - "--prefix", - "none", - "--format", - "{p}", - "--target", - "all", - "--offline", - ]) - .current_dir(env!("CARGO_MANIFEST_DIR")) - .output() - .expect("could not run cargo tree"); - assert!( - output.status.success(), - "stderr: {}", - String::from_utf8_lossy(&output.stderr) + let lockfile = fs::read_to_string(Path::new(env!("CARGO_MANIFEST_DIR")).join("Cargo.lock")) + .expect("could not read Cargo.lock"); + let linked = openssl_packages(&lockfile); + assert!(linked.is_empty(), "{linked:?} are dependencies"); +} + +#[test] +fn a_lockfile_with_openssl_or_native_tls_in_it_is_caught() { + let lockfile = r#" +version = 4 + +[[package]] +name = "native-tls" +version = "0.2.14" +dependencies = [ + "openssl", + "openssl-probe", + "openssl-sys", +] + +[[package]] +name = "openssl" +version = "0.10.73" + +[[package]] +name = "openssl-probe" +version = "0.2.1" + +[[package]] +name = "openssl-sys" +version = "0.9.109" + +[[package]] +name = "rustls" +version = "0.23.31" +"#; + assert_eq!( + openssl_packages(lockfile), + [ + "native-tls 0.2.14", + "openssl 0.10.73", + "openssl-sys 0.9.109" + ] ); - for package in String::from_utf8(output.stdout).unwrap().lines() { - for linked in ["openssl-sys ", "openssl ", "native-tls "] { - assert!(!package.starts_with(linked), "{package} is a dependency"); - } - } }