From 7be865bac850d05e11b22680efe367fd6fe9a944 Mon Sep 17 00:00:00 2001 From: DanConwayDev Date: Sat, 1 Aug 2026 16:16:26 +0000 Subject: [PATCH] test: route integration-test git subprocesses through hermetic helper Adopt grasp_audit::git_command() at every git call site in tests/ so the integration suite sees only configuration it supplies itself, matching the hermeticity grasp-audit's fixtures now enforce. Ambient settings such as a failing hook from core.hooksPath/init.templateDir or an init.defaultBranch override previously reached these subprocesses directly; the nip09 helpers even depended on init.defaultBranch=main to make their swallowed branch-setup errors harmless. This commit is the mechanical conversion only: arguments and error handling are unchanged (tokio call sites wrap the helper with tokio::process::Command::from). Load-bearing swallowed exit statuses are fixed in a follow-up so each change stays independently reviewable. Validated: cargo clippy --workspace --all-targets -D warnings; cargo fmt --all -- --check; full suite runs in the final validation pass. --- tests/archive_grasp_services.rs | 6 +-- tests/common/git_server.rs | 42 ++++++++++---------- tests/common/nip09_helpers.rs | 16 ++++---- tests/common/purgatory_helpers.rs | 22 +++++----- tests/common/sync_helpers.rs | 4 +- tests/lifecycle/nip09_cascade_event_types.rs | 4 +- tests/lifecycle/nip09_recovery.rs | 8 ++-- tests/lifecycle/nip09_state_cascade.rs | 8 ++-- tests/sync/maintainer_reprocessing.rs | 10 ++--- tests/test_filter_support.rs | 10 ++--- 10 files changed, 65 insertions(+), 65 deletions(-) diff --git a/tests/archive_grasp_services.rs b/tests/archive_grasp_services.rs index 2ac34bc..8e36324 100644 --- a/tests/archive_grasp_services.rs +++ b/tests/archive_grasp_services.rs @@ -572,7 +572,7 @@ async fn test_archive_read_only_creates_bare_repo() { .await .expect("Failed to write new file"); - let output = tokio::process::Command::new("git") + let output = tokio::process::Command::from(grasp_audit::git_command()) .args(["add", "."]) .current_dir(temp_dir.path()) .output() @@ -580,7 +580,7 @@ async fn test_archive_read_only_creates_bare_repo() { .expect("Failed to git add"); assert!(output.status.success()); - let output = tokio::process::Command::new("git") + let output = tokio::process::Command::from(grasp_audit::git_command()) .args(["commit", "-m", "New commit for push test"]) .current_dir(temp_dir.path()) .output() @@ -590,7 +590,7 @@ async fn test_archive_read_only_creates_bare_repo() { // Try to push to archive relay (should fail in read-only mode) let push_url = format!("http://{}/{}/{}.git", archive_domain, npub, identifier); - let output = tokio::process::Command::new("git") + let output = tokio::process::Command::from(grasp_audit::git_command()) .args(["push", &push_url, "main"]) .current_dir(temp_dir.path()) .output() diff --git a/tests/common/git_server.rs b/tests/common/git_server.rs index 7634968..f4a5e50 100644 --- a/tests/common/git_server.rs +++ b/tests/common/git_server.rs @@ -27,7 +27,7 @@ //! let server = SmartGitServer::start(temp_dir.path()).await; //! //! // Git operations work against server.url() -//! let output = Command::new("git") +//! let output = git_command() //! .args(["clone", "--depth=1", server.url(), "/tmp/clone"]) //! .output() //! .unwrap(); @@ -46,9 +46,9 @@ //! The purgatory sync system uses `git fetch --depth=1`, so tests involving purgatory //! sync should use `SmartGitServer`. +use grasp_audit::git_command; use std::net::SocketAddr; use std::path::{Path, PathBuf}; -use std::process::Command; use std::sync::Arc; use http_body_util::Full; @@ -100,7 +100,7 @@ impl SimpleGitServer { let bare_repo_path = temp_dir.path().join("repo.git"); // 2. Create bare clone - let output = Command::new("git") + let output = git_command() .args(["clone", "--bare"]) .arg(source_repo) .arg(&bare_repo_path) @@ -115,7 +115,7 @@ impl SimpleGitServer { } // 3. Run git update-server-info to generate info/refs and objects/info/packs - let output = Command::new("git") + let output = git_command() .args(["update-server-info"]) .current_dir(&bare_repo_path) .output() @@ -395,7 +395,7 @@ mod tests { let server = SimpleGitServer::start(temp_dir.path()).await; // Run git ls-remote against the server (using tokio::process::Command) - let output = tokio::process::Command::new("git") + let output = tokio::process::Command::from(grasp_audit::git_command()) .args(["ls-remote", server.url()]) .output() .await @@ -439,7 +439,7 @@ mod tests { let dest_dir = tempfile::tempdir().expect("Failed to create dest dir"); // Initialize empty repo (using tokio::process::Command) - let output = tokio::process::Command::new("git") + let output = tokio::process::Command::from(grasp_audit::git_command()) .args(["init"]) .current_dir(dest_dir.path()) .output() @@ -448,7 +448,7 @@ mod tests { assert!(output.status.success()); // Add the server as a remote - let output = tokio::process::Command::new("git") + let output = tokio::process::Command::from(grasp_audit::git_command()) .args(["remote", "add", "origin", server.url()]) .current_dir(dest_dir.path()) .output() @@ -457,7 +457,7 @@ mod tests { assert!(output.status.success()); // Fetch from the server - let output = tokio::process::Command::new("git") + let output = tokio::process::Command::from(grasp_audit::git_command()) .args(["fetch", "origin"]) .current_dir(dest_dir.path()) .output() @@ -471,7 +471,7 @@ mod tests { ); // Verify the commit was fetched - let output = tokio::process::Command::new("git") + let output = tokio::process::Command::from(grasp_audit::git_command()) .args(["rev-parse", "origin/main"]) .current_dir(dest_dir.path()) .output() @@ -561,7 +561,7 @@ impl SmartGitServer { let bare_repo_path = temp_dir.path().join("repo.git"); // 2. Create bare clone - let output = Command::new("git") + let output = git_command() .args(["clone", "--bare"]) .arg(source_repo) .arg(&bare_repo_path) @@ -759,7 +759,7 @@ async fn handle_info_refs_upload_pack( use tokio::process::Command as TokioCommand; // Spawn git upload-pack --advertise-refs - let mut cmd = TokioCommand::new("git"); + let mut cmd = TokioCommand::from(grasp_audit::git_command()); cmd.arg("-c") .arg("uploadpack.allowReachableSHA1InWant=true") .arg("-c") @@ -851,7 +851,7 @@ async fn handle_upload_pack( let body_bytes = req.collect().await?.to_bytes(); // Spawn git upload-pack - let mut cmd = TokioCommand::new("git"); + let mut cmd = TokioCommand::from(grasp_audit::git_command()); cmd.arg("-c") .arg("uploadpack.allowReachableSHA1InWant=true") .arg("-c") @@ -1004,7 +1004,7 @@ mod smart_git_server_tests { let server = SmartGitServer::start(temp_dir.path()).await; // Run git ls-remote against the server (using tokio::process::Command) - let output = tokio::process::Command::new("git") + let output = tokio::process::Command::from(grasp_audit::git_command()) .args(["ls-remote", server.url()]) .output() .await @@ -1048,7 +1048,7 @@ mod smart_git_server_tests { let dest_dir = tempfile::tempdir().expect("Failed to create dest dir"); // Initialize empty repo - let output = tokio::process::Command::new("git") + let output = tokio::process::Command::from(grasp_audit::git_command()) .args(["init"]) .current_dir(dest_dir.path()) .output() @@ -1057,7 +1057,7 @@ mod smart_git_server_tests { assert!(output.status.success()); // Add the server as a remote - let output = tokio::process::Command::new("git") + let output = tokio::process::Command::from(grasp_audit::git_command()) .args(["remote", "add", "origin", server.url()]) .current_dir(dest_dir.path()) .output() @@ -1066,7 +1066,7 @@ mod smart_git_server_tests { assert!(output.status.success()); // Fetch from the server - let output = tokio::process::Command::new("git") + let output = tokio::process::Command::from(grasp_audit::git_command()) .args(["fetch", "origin"]) .current_dir(dest_dir.path()) .output() @@ -1080,7 +1080,7 @@ mod smart_git_server_tests { ); // Verify the commit was fetched - let output = tokio::process::Command::new("git") + let output = tokio::process::Command::from(grasp_audit::git_command()) .args(["rev-parse", "origin/main"]) .current_dir(dest_dir.path()) .output() @@ -1113,7 +1113,7 @@ mod smart_git_server_tests { let dest_dir = tempfile::tempdir().expect("Failed to create dest dir"); // Initialize empty repo - let output = tokio::process::Command::new("git") + let output = tokio::process::Command::from(grasp_audit::git_command()) .args(["init"]) .current_dir(dest_dir.path()) .output() @@ -1122,7 +1122,7 @@ mod smart_git_server_tests { assert!(output.status.success()); // Add the server as a remote - let output = tokio::process::Command::new("git") + let output = tokio::process::Command::from(grasp_audit::git_command()) .args(["remote", "add", "origin", server.url()]) .current_dir(dest_dir.path()) .output() @@ -1131,7 +1131,7 @@ mod smart_git_server_tests { assert!(output.status.success()); // Shallow fetch from the server - THIS IS WHAT PURGATORY SYNC USES - let output = tokio::process::Command::new("git") + let output = tokio::process::Command::from(grasp_audit::git_command()) .args(["fetch", "--depth=1", "origin", &commit_hash]) .current_dir(dest_dir.path()) .output() @@ -1145,7 +1145,7 @@ mod smart_git_server_tests { ); // Verify the commit was fetched - let output = tokio::process::Command::new("git") + let output = tokio::process::Command::from(grasp_audit::git_command()) .args(["cat-file", "-t", &commit_hash]) .current_dir(dest_dir.path()) .output() diff --git a/tests/common/nip09_helpers.rs b/tests/common/nip09_helpers.rs index 5d5457f..84a5751 100644 --- a/tests/common/nip09_helpers.rs +++ b/tests/common/nip09_helpers.rs @@ -106,11 +106,11 @@ pub async fn publish_served_repo(client: &AuditClient, test_name: &str) -> (Even "deterministic commit hash mismatch" ); - let _ = std::process::Command::new("git") + let _ = grasp_audit::git_command() .args(["branch", "main"]) .current_dir(&clone_path) .output(); - let _ = std::process::Command::new("git") + let _ = grasp_audit::git_command() .args(["checkout", "main"]) .current_dir(&clone_path) .output(); @@ -222,11 +222,11 @@ pub async fn publish_served_repo_with_maintainers( "deterministic commit hash mismatch" ); - let _ = std::process::Command::new("git") + let _ = grasp_audit::git_command() .args(["branch", "main"]) .current_dir(&clone_path) .output(); - let _ = std::process::Command::new("git") + let _ = grasp_audit::git_command() .args(["checkout", "main"]) .current_dir(&clone_path) .output(); @@ -498,11 +498,11 @@ pub async fn publish_served_announcement_for_identifier( "deterministic commit hash mismatch" ); - let _ = std::process::Command::new("git") + let _ = grasp_audit::git_command() .args(["branch", "main"]) .current_dir(&clone_path) .output(); - let _ = std::process::Command::new("git") + let _ = grasp_audit::git_command() .args(["checkout", "main"]) .current_dir(&clone_path) .output(); @@ -611,11 +611,11 @@ pub async fn publish_served_announcement_with_state_for_identifier( "deterministic commit hash mismatch" ); - let _ = std::process::Command::new("git") + let _ = grasp_audit::git_command() .args(["branch", "main"]) .current_dir(&clone_path) .output(); - let _ = std::process::Command::new("git") + let _ = grasp_audit::git_command() .args(["checkout", "main"]) .current_dir(&clone_path) .output(); diff --git a/tests/common/purgatory_helpers.rs b/tests/common/purgatory_helpers.rs index 987428a..66db09f 100644 --- a/tests/common/purgatory_helpers.rs +++ b/tests/common/purgatory_helpers.rs @@ -11,9 +11,9 @@ //! - Use `Tag::custom("name", vec![...])` syntax //! - Use `EventBuilder::new(kind, content).tags(tags)` syntax +use grasp_audit::git_command; use nostr_sdk::prelude::*; use std::path::Path; -use std::process::Command; use std::time::Duration; // NOTE: Using rust-nostr Kind variants: @@ -131,7 +131,7 @@ pub fn create_branch( /// Get the HEAD commit hash. fn get_head_commit(path: &Path) -> Result { - let output = Command::new("git") + let output = git_command() .args(["rev-parse", "HEAD"]) .current_dir(path) .output() @@ -149,7 +149,7 @@ fn get_head_commit(path: &Path) -> Result { /// Run a git command in the specified directory. fn run_git(path: &Path, args: &[&str]) -> Result<(), String> { - let output = Command::new("git") + let output = git_command() .args(args) .current_dir(path) .output() @@ -543,7 +543,7 @@ pub async fn check_ref_at_commit( ) -> Result { let remote_url = format!("http://{}/{}/{}.git", relay_domain, npub, repo_id); - let output = Command::new("git") + let output = git_command() .args(["ls-remote", &remote_url, ref_name]) .output() .map_err(|e| format!("Failed to run git ls-remote: {}", e))?; @@ -591,7 +591,7 @@ pub fn push_to_relay( let remote_url = format!("http://{}/{}/{}.git", relay_domain, npub, repo_id); // Check if origin already exists - let check_output = Command::new("git") + let check_output = git_command() .args(["remote", "get-url", "origin"]) .current_dir(local_path) .output() @@ -606,7 +606,7 @@ pub fn push_to_relay( } // Push all refs - let output = Command::new("git") + let output = git_command() .args(["push", "-u", "origin", "--all"]) .current_dir(local_path) .output() @@ -650,7 +650,7 @@ pub fn push_ref_to_relay( let remote_url = format!("http://{}/{}/{}.git", relay_domain, npub, repo_id); // Check if origin already exists - let check_output = Command::new("git") + let check_output = git_command() .args(["remote", "get-url", "origin"]) .current_dir(local_path) .output() @@ -658,13 +658,13 @@ pub fn push_ref_to_relay( if check_output.status.success() { // Remote exists, update it - let _ = Command::new("git") + let _ = git_command() .args(["remote", "set-url", "origin", &remote_url]) .current_dir(local_path) .output(); } else { // Add new remote - let _ = Command::new("git") + let _ = git_command() .args(["remote", "add", "origin", &remote_url]) .current_dir(local_path) .output(); @@ -673,7 +673,7 @@ pub fn push_ref_to_relay( // Push specific commit to specific ref // Format: git push origin : let refspec = format!("{}:{}", commit_hash, ref_name); - let output = Command::new("git") + let output = git_command() .args(["push", "origin", &refspec]) .current_dir(local_path) .output() @@ -823,7 +823,7 @@ mod tests { create_branch(temp_dir.path(), "feature", None).expect("Failed to create branch"); // Verify branch exists - let output = Command::new("git") + let output = git_command() .args(["rev-parse", "feature"]) .current_dir(temp_dir.path()) .output() diff --git a/tests/common/sync_helpers.rs b/tests/common/sync_helpers.rs index f7eccba..cb26bb5 100644 --- a/tests/common/sync_helpers.rs +++ b/tests/common/sync_helpers.rs @@ -1234,7 +1234,7 @@ pub async fn push_unique_git_data_to_relay( let path = git_temp_dir.path(); fn git(path: &std::path::Path, args: &[&str]) { - let status = std::process::Command::new("git") + let status = grasp_audit::git_command() .args(args) .current_dir(path) .env("GIT_AUTHOR_NAME", "Test User") @@ -1269,7 +1269,7 @@ pub async fn push_unique_git_data_to_relay( git(path, &["commit", "-m", "State test commit"]); let commit_hash = { - let out = std::process::Command::new("git") + let out = grasp_audit::git_command() .args(["rev-parse", "HEAD"]) .current_dir(path) .output() diff --git a/tests/lifecycle/nip09_cascade_event_types.rs b/tests/lifecycle/nip09_cascade_event_types.rs index cc0567c..6ecb4b0 100644 --- a/tests/lifecycle/nip09_cascade_event_types.rs +++ b/tests/lifecycle/nip09_cascade_event_types.rs @@ -12,15 +12,15 @@ use common::{ publish_served_repo, push_ref_to_relay, TestRelay, }; +use grasp_audit::git_command; use grasp_audit::{clone_repo, AuditClient, AuditConfig, DETERMINISTIC_COMMIT_HASH}; use nostr_sdk::prelude::*; use std::fs; use std::path::Path; -use std::process::Command; use std::time::Duration; fn repo_has_ref(repo_path: &Path, ref_name: &str) -> bool { - let output = Command::new("git") + let output = git_command() .args([ "--git-dir", repo_path.to_str().expect("repo path utf8"), diff --git a/tests/lifecycle/nip09_recovery.rs b/tests/lifecycle/nip09_recovery.rs index 474033d..8be5d0a 100644 --- a/tests/lifecycle/nip09_recovery.rs +++ b/tests/lifecycle/nip09_recovery.rs @@ -9,13 +9,13 @@ use common::{ publish_served_repo_with_maintainers, publish_served_repo_with_state_event, push_to_relay, CommitVariant, TestRelay, }; +use grasp_audit::git_command; use grasp_audit::{AuditClient, AuditConfig}; use ngit_grasp::nostr::lifecycle::{ HoldingStore, DEFAULT_RETENTION, HOLDING_ARCHIVE_PATH_TAG, HOLDING_METADATA_KIND, }; use nostr_sdk::prelude::*; use std::path::{Path, PathBuf}; -use std::process::Command; use std::time::Duration; async fn open_holding(relay: &TestRelay) -> HoldingStore { @@ -230,7 +230,7 @@ async fn wait_for_event_presence( } fn run_git_in_repo(repo_path: &Path, args: &[&str]) -> std::process::Output { - Command::new("git") + git_command() .args(args) .current_dir(repo_path) .output() @@ -238,7 +238,7 @@ fn run_git_in_repo(repo_path: &Path, args: &[&str]) -> std::process::Output { } fn run_git_bare(repo_path: &Path, args: &[&str]) -> std::process::Output { - Command::new("git") + git_command() .args(["--git-dir", repo_path.to_str().expect("repo path utf8")]) .args(args) .output() @@ -992,7 +992,7 @@ async fn reannouncement_restores_events_and_git_archive_happy_path() { .expect("query restored state")); assert!(repo_path.is_dir(), "git repo must be restored"); - let show_ref = Command::new("git") + let show_ref = git_command() .args(["--git-dir", repo_path.to_str().unwrap(), "show-ref"]) .output() .expect("run git show-ref on restored repo"); diff --git a/tests/lifecycle/nip09_state_cascade.rs b/tests/lifecycle/nip09_state_cascade.rs index de84e10..43d8712 100644 --- a/tests/lifecycle/nip09_state_cascade.rs +++ b/tests/lifecycle/nip09_state_cascade.rs @@ -13,11 +13,11 @@ use common::{ publish_served_repo_with_state_event_and_maintainers, push_to_relay, CommitVariant, TestRelay, }; +use grasp_audit::git_command; use grasp_audit::{AuditClient, AuditConfig, DETERMINISTIC_COMMIT_HASH}; use ngit_grasp::nostr::lifecycle::HoldingStore; use nostr_sdk::prelude::*; use std::collections::HashSet; -use std::process::Command; use std::time::Duration; fn build_state_with_branches( @@ -53,7 +53,7 @@ fn repo_path(relay: &TestRelay, client: &AuditClient, repo_id: &str) -> std::pat } fn list_repo_refs(repo_path: &std::path::Path) -> HashSet { - let output = Command::new("git") + let output = git_command() .args([ "--git-dir", repo_path.to_str().expect("repo path"), @@ -80,7 +80,7 @@ fn assert_valid_empty_bare_repo(repo_path: &std::path::Path) { repo_path.display() ); - let bare = Command::new("git") + let bare = git_command() .args([ "--git-dir", repo_path.to_str().expect("repo path"), @@ -100,7 +100,7 @@ fn assert_valid_empty_bare_repo(repo_path: &std::path::Path) { "replacement repo must report itself as bare" ); - let refs = Command::new("git") + let refs = git_command() .args([ "--git-dir", repo_path.to_str().expect("repo path"), diff --git a/tests/sync/maintainer_reprocessing.rs b/tests/sync/maintainer_reprocessing.rs index 9e17be5..d0f3643 100644 --- a/tests/sync/maintainer_reprocessing.rs +++ b/tests/sync/maintainer_reprocessing.rs @@ -29,9 +29,9 @@ //! announcements are in relay_a's DB), wait briefly for the sync round-trip, then //! send the owner announcement + git push. +use grasp_audit::git_command; use std::collections::BTreeMap; use std::path::Path; -use std::process::Command; use std::time::Duration; use nostr_sdk::prelude::*; @@ -146,7 +146,7 @@ async fn push_counts(relay: &TestRelay) -> (u64, u64) { } fn list_remote_refs(clone_url: &str) -> Result, String> { - let output = Command::new("git") + let output = git_command() .args(["ls-remote", "--refs", clone_url]) .output() .map_err(|error| format!("Failed to list refs from {clone_url}: {error}"))?; @@ -203,7 +203,7 @@ async fn assert_remote_refs( } fn rename_default_branch(repository: &Path, branch: &str) { - let output = Command::new("git") + let output = git_command() .args(["branch", "-m", branch]) .current_dir(repository) .output() @@ -216,7 +216,7 @@ fn rename_default_branch(repository: &Path, branch: &str) { } fn remote_default_branch(clone_url: &str) -> Result { - let output = Command::new("git") + let output = git_command() .args(["ls-remote", "--symref", clone_url, "HEAD"]) .output() .map_err(|error| format!("Failed to inspect HEAD from {clone_url}: {error}"))?; @@ -1080,7 +1080,7 @@ async fn test_purgatory_owner_uses_rejected_maintainer_clone_to_sync_git() { &[&source_relay.domain()], ) .await; - let commit_output = Command::new("git") + let commit_output = git_command() .args(["rev-parse", "HEAD"]) .current_dir(maintainer_git.path()) .output() diff --git a/tests/test_filter_support.rs b/tests/test_filter_support.rs index 58c6352..732a1fd 100644 --- a/tests/test_filter_support.rs +++ b/tests/test_filter_support.rs @@ -19,7 +19,7 @@ async fn test_filter_capability_advertised() { let server = SmartGitServer::start(temp_dir.path()).await; // Run git ls-remote to see advertised capabilities - let output = Command::new("git") + let output = Command::from(grasp_audit::git_command()) .env("GIT_TRACE_PACKET", "1") .args(["ls-remote", server.url()]) .output() @@ -63,7 +63,7 @@ async fn test_filtered_clone_succeeds() { let clone_path = clone_dir.path().join("cloned-repo"); // Attempt a filtered clone - let output = Command::new("git") + let output = Command::from(grasp_audit::git_command()) .args([ "clone", "--filter=blob:none", @@ -90,7 +90,7 @@ async fn test_filtered_clone_succeeds() { ); // In a filtered clone, we should be able to list files - let ls_output = Command::new("git") + let ls_output = Command::from(grasp_audit::git_command()) .current_dir(&clone_path) .args(["ls-files"]) .output() @@ -126,7 +126,7 @@ async fn test_filtered_fetch_succeeds() { let clone_dir = TempDir::new().expect("Failed to create clone dir"); let clone_path = clone_dir.path().join("repo"); - let clone_output = Command::new("git") + let clone_output = Command::from(grasp_audit::git_command()) .args(["clone", server.url(), clone_path.to_str().unwrap()]) .output() .await @@ -139,7 +139,7 @@ async fn test_filtered_fetch_succeeds() { ); // Now try a filtered fetch - let fetch_output = Command::new("git") + let fetch_output = Command::from(grasp_audit::git_command()) .current_dir(&clone_path) .args(["fetch", "--filter=blob:none", "origin"]) .output()