diff --git a/crates/socket-patch-cli/tests/e2e_npm.rs b/crates/socket-patch-cli/tests/e2e_npm.rs index 74051c673..3a7dce516 100644 --- a/crates/socket-patch-cli/tests/e2e_npm.rs +++ b/crates/socket-patch-cli/tests/e2e_npm.rs @@ -15,7 +15,7 @@ #[path = "common/mod.rs"] mod common; -use common::{binary, git_sha256}; +use common::{binary, git_sha256, hermetic}; use std::path::Path; use std::process::{Command, Output}; @@ -58,24 +58,12 @@ fn git_sha256_file(path: &Path) -> String { /// Run the CLI binary with the given args, setting `cwd` as the working dir. /// Returns `(exit_code, stdout, stderr)`. fn run(cwd: &Path, args: &[&str]) -> (i32, String, String) { - let mut cmd = Command::new(binary()); + // The hermetic `SOCKET_*` environment: no ambient value (SOCKET_DRY_RUN, + // SOCKET_GLOBAL_PREFIX, ...) changes what these tests exercise, no token + // (so the public proxy), and the config layer, update notifier and + // telemetry forced off. + let mut cmd = hermetic::command(&binary()); cmd.args(args).current_dir(cwd); - // The binary binds a wide `SOCKET_*` env surface (SOCKET_CWD, - // SOCKET_DRY_RUN, SOCKET_STRICT, SOCKET_ECOSYSTEMS, SOCKET_GLOBAL_PREFIX, - // ...). An ambient value silently changes what these tests exercise — - // SOCKET_DRY_RUN=true turns every real apply into a no-op, and - // SOCKET_GLOBAL_PREFIX flips commands into global mode, aiming mutations - // at the host's *real* global node_modules. Scrub the whole prefix so - // only the flags each test passes are in effect; removing - // SOCKET_API_TOKEN also forces the public proxy (free-tier). Telemetry - // opt-outs are deliberately kept so an opted-out dev stays opted out. - for (key, _) in std::env::vars_os() { - let name = key.to_string_lossy(); - if name.starts_with("SOCKET_") && !name.contains("TELEMETRY") && name != "SOCKET_NO_CONFIG" - { - cmd.env_remove(&key); - } - } // Partial cache isolation only — the global auto-discovery test below // needs the binary's `npm root -g` / `yarn global dir` / `pnpm root -g` // probes to resolve the REAL prefixes, which come out of $HOME, so a full @@ -108,6 +96,7 @@ fn assert_run_ok(cwd: &Path, args: &[&str], context: &str) -> (String, String) { fn npm_run(cwd: &Path, args: &[&str]) { let mut cmd = Command::new("npm"); cmd.args(args).current_dir(cwd); + hermetic::scrub_socket_vars(&mut cmd); cache_env::isolate(&mut cmd); let out = cmd.output().expect("failed to run npm"); assert!( @@ -357,6 +346,7 @@ fn test_npm_global_lifecycle() { ]); // `--prefix` is a flag, so it still decides where the package lands; the // sandbox only moves the download cache off the caller's home. + hermetic::scrub_socket_vars(&mut cmd); cache_env::isolate(&mut cmd); let out = cmd.output().expect("failed to run npm install -g"); assert!( diff --git a/crates/socket-patch-cli/tests/e2e_redirect_bun_build.rs b/crates/socket-patch-cli/tests/e2e_redirect_bun_build.rs index 6dc2cc1a3..80e42bff0 100644 --- a/crates/socket-patch-cli/tests/e2e_redirect_bun_build.rs +++ b/crates/socket-patch-cli/tests/e2e_redirect_bun_build.rs @@ -92,7 +92,7 @@ #[path = "common/mod.rs"] mod common; -use common::binary; +use common::{binary, hermetic}; use std::path::{Path, PathBuf}; use std::process::{Command, Output, Stdio}; @@ -300,17 +300,23 @@ fn bun_env(cwd: &Path, args: &[&str], cache_dir: &Path, env: &[(String, String)] cmd.output().expect("failed to run bun") } -/// The real binary with `--no-telemetry` appended: nothing in this suite -/// should ever post a telemetry event, mocked API or not. +/// The real binary. `hermetic::command` forces telemetry off: nothing in +/// this suite should ever post a telemetry event, mocked API or not. fn run_socket(cwd: &Path, args: &[&str]) -> (i32, String, String) { run_socket_env(cwd, args, &[]) } -/// [`run_socket`] with extra env applied after the scrub. +/// [`run_socket`] with extra env applied after the scrub: the hermetic +/// `SOCKET_*` environment (`hermetic::command`), the bun suites' shared +/// scrub of every other ambient bun / npm registry setting, and a stand-in +/// `HOME` (`jvm_env::isolate_cli`). The `bun` fixture children run under the +/// cache sandbox's home, so the CLI must not read the developer's real +/// `~/.npmrc` either: a registry or scope there would make the two disagree. fn run_socket_env(cwd: &Path, args: &[&str], env: &[(String, String)]) -> (i32, String, String) { - let mut cmd = Command::new(binary()); - cmd.args(args).arg("--no-telemetry").current_dir(cwd); - cache_env::scrub_ambient_bun_env(&mut cmd); + let mut cmd = hermetic::command(&binary()); + cmd.args(args).current_dir(cwd); + cache_env::scrub_ambient_bun_tool_env(&mut cmd); + common::jvm_env::isolate_cli(&mut cmd); for (k, v) in env { cmd.env(k, v); } diff --git a/crates/socket-patch-cli/tests/spawn_env_hygiene.rs b/crates/socket-patch-cli/tests/spawn_env_hygiene.rs index d2a76309c..23ac28316 100644 --- a/crates/socket-patch-cli/tests/spawn_env_hygiene.rs +++ b/crates/socket-patch-cli/tests/spawn_env_hygiene.rs @@ -32,15 +32,12 @@ const PENDING_RAW_SPAWNS: &[&str] = &[ "apply/apply_network.rs", "e2e_hosted_production.rs", "e2e_maven.rs", - "e2e_npm.rs", - "e2e_redirect_bun_build.rs", "e2e_redirect_cargo_shapes.rs", "e2e_scan.rs", "e2e_socket_yml_policy.rs", "e2e_vendored_production.rs", "e2e_vex_vendor.rs", "rollback/rollback_multicopy_blob_gate.rs", - "vendor/in_process_vendor_bun.rs", ]; /// The environment `cmd`'s child would see: the parent environment with diff --git a/crates/socket-patch-cli/tests/vendor/in_process_vendor_bun.rs b/crates/socket-patch-cli/tests/vendor/in_process_vendor_bun.rs index 3e983f1a4..81f14e2d0 100644 --- a/crates/socket-patch-cli/tests/vendor/in_process_vendor_bun.rs +++ b/crates/socket-patch-cli/tests/vendor/in_process_vendor_bun.rs @@ -1619,21 +1619,10 @@ async fn scan_vendored_from_workspace_member_cwd_fetches_then_engine_refuses_loc fn run_with_deadline(root: &Path, argv: &[&str], deadline: Duration) -> (i32, String, String) { let out_path = root.join("child.stdout"); let err_path = root.join("child.stderr"); - let mut cmd = Command::new(common::binary()); - cmd.args(argv).current_dir(root); - for (key, _) in std::env::vars_os() { - let name = key.to_string_lossy(); - if name.starts_with("SOCKET_") - && !name.contains("TELEMETRY") - && name != "SOCKET_NO_CONFIG" - && name != "SOCKET_NO_UPDATE_CHECK" - { - cmd.env_remove(&key); - } - } - cmd.env("SOCKET_NO_CONFIG", "1") - .env("SOCKET_NO_UPDATE_CHECK", "1") - .env("SOCKET_TELEMETRY_DISABLED", "1") + let mut cmd = common::hermetic_command(&common::binary()); + common::jvm_env::isolate_cli(&mut cmd); + cmd.args(argv) + .current_dir(root) .stdin(Stdio::null()) .stdout(std::fs::File::create(&out_path).unwrap()) .stderr(std::fs::File::create(&err_path).unwrap());