From f41550ea1898be2ec6c4be205e4cad0085400313 Mon Sep 17 00:00:00 2001 From: DanConwayDev Date: Fri, 28 Nov 2025 03:38:50 +0000 Subject: audit: stop checking git_data_directory --- grasp-audit/src/bin/grasp-audit.rs | 43 +---- grasp-audit/src/specs/grasp01/cors.rs | 1 - grasp-audit/src/specs/grasp01/git_clone.rs | 45 +---- grasp-audit/src/specs/grasp01/mod.rs | 2 +- .../src/specs/grasp01/push_authorization.rs | 64 +------ .../src/specs/grasp01/repository_creation.rs | 208 +++++++-------------- 6 files changed, 88 insertions(+), 275 deletions(-) (limited to 'grasp-audit/src') diff --git a/grasp-audit/src/bin/grasp-audit.rs b/grasp-audit/src/bin/grasp-audit.rs index c0a9273..29f9c9a 100644 --- a/grasp-audit/src/bin/grasp-audit.rs +++ b/grasp-audit/src/bin/grasp-audit.rs @@ -48,21 +48,6 @@ async fn main() -> Result<()> { match cli.command { Commands::Audit { relay, mode, spec, git_data_dir } => { - // Early validation: check if --git-data-dir is required for the spec - let specs_requiring_git_dir = ["all", "git-clone", "push-auth", "repo-creation"]; - if specs_requiring_git_dir.contains(&spec.as_str()) && git_data_dir.is_none() { - return Err(anyhow!( - "The '{}' spec requires --git-data-dir to be specified.\n\ - \n\ - This directory should point to the relay's git data storage.\n\ - Example: --git-data-dir /path/to/relay/repos\n\ - \n\ - If using Docker, mount a volume and pass that path:\n\ - docker run -v /tmp/repos:/srv/ngit-relay/repos ...\n\ - cargo run -- audit --relay ws://localhost:8080 --git-data-dir /tmp/repos", - spec - )); - } let mut config = match mode.as_str() { "ci" => AuditConfig::ci(), @@ -103,16 +88,6 @@ async fn main() -> Result<()> { println!("✓ Connected\n"); - // Helper to check if git_data_dir is required for individual specs - let require_git_data_dir = |spec_name: &str| -> Result { - git_data_dir.clone().ok_or_else(|| { - anyhow!( - "The '{}' spec requires --git-data-dir to be specified", - spec_name - ) - }) - }; - let results = match spec.as_str() { "nip01-smoke" => { println!("Running NIP-01 smoke tests...\n"); @@ -131,24 +106,18 @@ async fn main() -> Result<()> { specs::CorsTests::run_all(&client, &relay_domain).await } "git-clone" => { - let dir = require_git_data_dir("git-clone")?; println!("Running Git clone tests...\n"); - specs::GitCloneTests::run_all(&client, &dir, &relay_domain).await + specs::GitCloneTests::run_all(&client, &relay_domain).await } "push-auth" => { - let dir = require_git_data_dir("push-auth")?; println!("Running push authorization tests...\n"); - specs::PushAuthorizationTests::run_all(&client, &dir, &relay_domain).await + specs::PushAuthorizationTests::run_all(&client, &relay_domain).await } "repo-creation" => { - let dir = require_git_data_dir("repo-creation")?; println!("Running repository creation tests...\n"); - specs::RepositoryCreationTests::run_all(&client, &dir).await + specs::RepositoryCreationTests::run_all(&client, &relay_domain).await } "all" => { - // git_data_dir is guaranteed by early validation - let dir = git_data_dir.clone().expect("git_data_dir validated earlier"); - println!("Running all tests...\n"); let mut all_results = AuditResult::new("All GRASP-01 Tests"); @@ -174,17 +143,17 @@ async fn main() -> Result<()> { // Git clone tests println!(" → Git clone tests..."); - let clone_results = specs::GitCloneTests::run_all(&client, &dir, &relay_domain).await; + let clone_results = specs::GitCloneTests::run_all(&client, &relay_domain).await; all_results.merge(clone_results); // Push authorization tests println!(" → Push authorization tests..."); - let push_results = specs::PushAuthorizationTests::run_all(&client, &dir, &relay_domain).await; + let push_results = specs::PushAuthorizationTests::run_all(&client, &relay_domain).await; all_results.merge(push_results); // Repository creation tests println!(" → Repository creation tests..."); - let repo_results = specs::RepositoryCreationTests::run_all(&client, &dir).await; + let repo_results = specs::RepositoryCreationTests::run_all(&client, &relay_domain).await; all_results.merge(repo_results); println!(); diff --git a/grasp-audit/src/specs/grasp01/cors.rs b/grasp-audit/src/specs/grasp01/cors.rs index 4e0513e..08c5ab6 100644 --- a/grasp-audit/src/specs/grasp01/cors.rs +++ b/grasp-audit/src/specs/grasp01/cors.rs @@ -257,7 +257,6 @@ impl CorsTests { /// For integration tests that want to test against real repositories pub async fn test_cors_on_real_repo( client: &AuditClient, - _git_data_dir: &Path, relay_domain: &str, ) -> TestResult { let test_name = "test_cors_on_real_repo"; diff --git a/grasp-audit/src/specs/grasp01/git_clone.rs b/grasp-audit/src/specs/grasp01/git_clone.rs index 4666a40..9ee6ed7 100644 --- a/grasp-audit/src/specs/grasp01/git_clone.rs +++ b/grasp-audit/src/specs/grasp01/git_clone.rs @@ -18,7 +18,6 @@ use crate::{AuditClient, FixtureKind, TestContext, TestResult}; use nostr_sdk::prelude::*; use std::fs; -use std::path::Path; use std::process::Command; /// Test suite for Git clone operations @@ -28,14 +27,13 @@ impl GitCloneTests { /// Run all Git clone tests pub async fn run_all( client: &AuditClient, - git_data_dir: &Path, relay_domain: &str, ) -> crate::AuditResult { let mut results = crate::AuditResult::new("GRASP-01 Git Clone Tests"); - results.add(Self::test_basic_git_clone(client, git_data_dir, relay_domain).await); - results.add(Self::test_clone_url_format(client, git_data_dir, relay_domain).await); - results.add(Self::test_sha1_capabilities_advertised(client, git_data_dir, relay_domain).await); + results.add(Self::test_basic_git_clone(client, relay_domain).await); + results.add(Self::test_clone_url_format(client, relay_domain).await); + results.add(Self::test_sha1_capabilities_advertised(client, relay_domain).await); results } @@ -49,7 +47,6 @@ impl GitCloneTests { /// 4. Verifies the clone succeeded pub async fn test_basic_git_clone( client: &AuditClient, - git_data_dir: &Path, relay_domain: &str, ) -> TestResult { let test_name = "test_basic_git_clone"; @@ -101,20 +98,6 @@ impl GitCloneTests { } }; - // Verify repository exists - let repo_path = git_data_dir.join(&npub).join(format!("{}.git", repo_id)); - if !repo_path.exists() { - return TestResult::new( - test_name, - "GRASP-01", - "Repository must be cloneable via Git HTTP backend", - ) - .fail(&format!( - "Repository not found at: {}", - repo_path.display() - )); - } - // Create a test clone directory using standard library let temp_base = std::env::temp_dir(); let clone_dir_name = format!("grasp-test-clone-{}", uuid::Uuid::new_v4()); @@ -128,7 +111,7 @@ impl GitCloneTests { // Attempt to clone the repository let output = Command::new("git") - .args(&["clone", &clone_url, clone_path.to_str().unwrap()]) + .args(["clone", &clone_url, clone_path.to_str().unwrap()]) .env("GIT_TERMINAL_PROMPT", "0") // Disable password prompts .output(); @@ -188,7 +171,6 @@ impl GitCloneTests { /// 2. Invalid URLs are rejected properly pub async fn test_clone_url_format( client: &AuditClient, - _git_data_dir: &Path, relay_domain: &str, ) -> TestResult { let test_name = "test_clone_url_format"; @@ -254,7 +236,7 @@ impl GitCloneTests { let invalid_url = format!("http://{}/invalid/path", relay_domain); let output = Command::new("git") - .args(&["clone", &invalid_url, clone_path.to_str().unwrap()]) + .args(["clone", &invalid_url, clone_path.to_str().unwrap()]) .env("GIT_TERMINAL_PROMPT", "0") .output() .unwrap(); @@ -291,7 +273,6 @@ impl GitCloneTests { /// 2. Both allow-reachable-sha1-in-want and allow-tip-sha1-in-want are present pub async fn test_sha1_capabilities_advertised( client: &AuditClient, - git_data_dir: &Path, relay_domain: &str, ) -> TestResult { let test_name = "test_sha1_capabilities_advertised"; @@ -343,20 +324,6 @@ impl GitCloneTests { } }; - // Verify repository exists - let repo_path = git_data_dir.join(&npub).join(format!("{}.git", repo_id)); - if !repo_path.exists() { - return TestResult::new( - test_name, - "GRASP-01", - "MUST include allow-reachable-sha1-in-want and allow-tip-sha1-in-want in advertisement", - ) - .fail(&format!( - "Repository not found at: {}", - repo_path.display() - )); - } - // Build info/refs URL for git-upload-pack service let info_refs_url = format!( "http://{}/{}/{}.git/info/refs?service=git-upload-pack", @@ -435,8 +402,6 @@ impl GitCloneTests { #[cfg(test)] mod tests { - - #[test] fn test_module_exists() { // Simple compilation test diff --git a/grasp-audit/src/specs/grasp01/mod.rs b/grasp-audit/src/specs/grasp01/mod.rs index b5471d1..6f58b96 100644 --- a/grasp-audit/src/specs/grasp01/mod.rs +++ b/grasp-audit/src/specs/grasp01/mod.rs @@ -26,4 +26,4 @@ pub use git_clone::GitCloneTests; pub use nip01_smoke::Nip01SmokeTests; pub use nip11_document::Nip11DocumentTests; pub use push_authorization::PushAuthorizationTests; -pub use repository_creation::{is_bare_repository, RepositoryCreationTests}; +pub use repository_creation::{RepositoryCreationTests}; diff --git a/grasp-audit/src/specs/grasp01/push_authorization.rs b/grasp-audit/src/specs/grasp01/push_authorization.rs index 4599ea5..69664d6 100644 --- a/grasp-audit/src/specs/grasp01/push_authorization.rs +++ b/grasp-audit/src/specs/grasp01/push_authorization.rs @@ -33,15 +33,14 @@ impl PushAuthorizationTests { /// Run all push authorization tests pub async fn run_all( client: &AuditClient, - git_data_dir: &Path, relay_domain: &str, ) -> crate::AuditResult { let mut results = crate::AuditResult::new("GRASP-01 Push Authorization Tests"); - results.add(Self::test_push_authorized_by_owner_state(client, git_data_dir, relay_domain).await); - results.add(Self::test_push_rejected_without_state_event(client, git_data_dir, relay_domain).await); - results.add(Self::test_push_rejected_wrong_commit(client, git_data_dir, relay_domain).await); - results.add(Self::test_push_authorized_by_maintainer_state_only(client, git_data_dir, relay_domain).await); + results.add(Self::test_push_authorized_by_owner_state(client, relay_domain).await); + results.add(Self::test_push_rejected_without_state_event(client, relay_domain).await); + results.add(Self::test_push_rejected_wrong_commit(client, relay_domain).await); + results.add(Self::test_push_authorized_by_maintainer_state_only(client, relay_domain).await); results } @@ -59,7 +58,6 @@ impl PushAuthorizationTests { /// 3. **Verify**: Push should succeed because state event authorizes this commit pub async fn test_push_authorized_by_owner_state( client: &AuditClient, - git_data_dir: &Path, relay_domain: &str, ) -> TestResult { use std::process::Command; @@ -103,13 +101,6 @@ impl PushAuthorizationTests { } }; - // Verify repo exists on disk - let repo_path = git_data_dir.join(&npub).join(format!("{}.git", repo_id)); - if !repo_path.exists() { - return TestResult::new(test_name, "GRASP-01", "Push authorized with matching state") - .fail(&format!("Repo not found: {}", repo_path.display())); - } - // ============================================================ // Step 2: SEND - Clone repo, create deterministic commit, push // ============================================================ @@ -222,7 +213,6 @@ impl PushAuthorizationTests { /// Test that push is rejected when no state event exists pub async fn test_push_rejected_without_state_event( client: &AuditClient, - git_data_dir: &Path, relay_domain: &str, ) -> TestResult { let test_name = "test_push_rejected_without_state_event"; @@ -243,12 +233,6 @@ impl PushAuthorizationTests { .and_then(|t| t.content()).unwrap().to_string(); let npub = repo.pubkey.to_bech32().unwrap(); - let repo_path = git_data_dir.join(&npub).join(format!("{}.git", repo_id)); - if !repo_path.exists() { - return TestResult::new(test_name, "GRASP-01", "Push rejected without state event") - .fail(&format!("Repo not found: {}", repo_path.display())); - } - // Clone and create commit let clone_path = match clone_repo(relay_domain, &npub, &repo_id) { Ok(p) => p, @@ -286,7 +270,6 @@ impl PushAuthorizationTests { /// 4. **Verify**: Push should be rejected because new commit doesn't match state event pub async fn test_push_rejected_wrong_commit( client: &AuditClient, - git_data_dir: &Path, relay_domain: &str, ) -> TestResult { use std::process::Command; @@ -330,13 +313,6 @@ impl PushAuthorizationTests { } }; - // Verify repo exists on disk - let repo_path = git_data_dir.join(&npub).join(format!("{}.git", repo_id)); - if !repo_path.exists() { - return TestResult::new(test_name, "GRASP-01", "Push rejected when commit not in state event") - .fail(&format!("Repo not found: {}", repo_path.display())); - } - // ============================================================ // Step 2: SEND - Clone repo, create deterministic commit, push // (establishes the state on the relay) @@ -495,7 +471,6 @@ impl PushAuthorizationTests { /// 4. The push should be ACCEPTED because maintainer's state event authorizes it pub async fn test_push_authorized_by_maintainer_state_only( client: &AuditClient, - git_data_dir: &Path, relay_domain: &str, ) -> TestResult { use std::process::Command; @@ -566,17 +541,6 @@ impl PushAuthorizationTests { } }; - // Verify repo exists on disk - let repo_path = git_data_dir.join(&npub).join(format!("{}.git", repo_id)); - if !repo_path.exists() { - return TestResult::new( - test_name, - "GRASP-01", - "Push authorized by maintainer state event only (no announcement)", - ) - .fail(&format!("Repo not found: {}", repo_path.display())); - } - // ============================================================ // Step 2: SEND - Clone, create maintainer commit, push // ============================================================ @@ -741,7 +705,6 @@ impl PushAuthorizationTests { /// Each level publishes announcements that authorize the next level. pub async fn test_push_authorized_by_recursive_maintainer_state( client: &AuditClient, - git_data_dir: &Path, relay_domain: &str, ) -> TestResult { use std::process::Command; @@ -837,17 +800,6 @@ impl PushAuthorizationTests { } }; - // Verify repo exists on disk - let repo_path = git_data_dir.join(&npub).join(format!("{}.git", repo_id)); - if !repo_path.exists() { - return TestResult::new( - test_name, - "GRASP-01", - "Push authorized by recursive maintainer state event", - ) - .fail(&format!("Repo not found: {}", repo_path.display())); - } - // ============================================================ // Step 2: SEND - Clone, create recursive maintainer commit, push // ============================================================ @@ -1007,7 +959,6 @@ impl PushAuthorizationTests { /// 5. **Verify**: Push should be rejected because rogue state event is ignored pub async fn test_non_maintainer_state_rejected( client: &AuditClient, - git_data_dir: &Path, relay_domain: &str, ) -> TestResult { use std::process::Command; @@ -1051,13 +1002,6 @@ impl PushAuthorizationTests { } }; - // Verify repo exists on disk - let repo_path = git_data_dir.join(&npub).join(format!("{}.git", repo_id)); - if !repo_path.exists() { - return TestResult::new(test_name, "GRASP-01", "Non-maintainer state events ignored") - .fail(&format!("Repo not found: {}", repo_path.display())); - } - // ============================================================ // Step 2: SEND - Clone repo, create deterministic commit, push // (establishes the state on the relay) diff --git a/grasp-audit/src/specs/grasp01/repository_creation.rs b/grasp-audit/src/specs/grasp01/repository_creation.rs index 2eaf32f..588187b 100644 --- a/grasp-audit/src/specs/grasp01/repository_creation.rs +++ b/grasp-audit/src/specs/grasp01/repository_creation.rs @@ -5,10 +5,9 @@ //! //! ## Test Coverage //! -//! - Repository creation on valid announcement +//! - Repository creation on valid announcement (verified via HTTP) //! - Idempotent creation (no error if repo already exists) -//! - Proper directory structure (/.git) -//! - Bare repository validation (has HEAD, config, objects, refs) +//! - Repository accessibility via Smart HTTP service //! //! ## Running Tests //! @@ -18,7 +17,6 @@ use crate::{AuditClient, FixtureKind, TestContext, TestResult}; use nostr_sdk::prelude::*; -use std::path::Path; /// Test suite for repository creation pub struct RepositoryCreationTests; @@ -27,13 +25,13 @@ impl RepositoryCreationTests { /// Run all repository creation tests pub async fn run_all( client: &AuditClient, - git_data_dir: &Path, + relay_domain: &str, ) -> crate::AuditResult { let mut results = crate::AuditResult::new("GRASP-01 Repository Creation Tests"); - results.add(Self::test_bare_repo_created_on_announcement(client, git_data_dir).await); - results.add(Self::test_repo_creation_idempotent(client, git_data_dir).await); - results.add(Self::test_bare_repo_structure(client, git_data_dir).await); + results.add(Self::test_bare_repo_created_on_announcement(client, relay_domain).await); + results.add(Self::test_repo_creation_idempotent(client, relay_domain).await); + results.add(Self::test_repo_accessible_via_http(client, relay_domain).await); results } @@ -43,10 +41,10 @@ impl RepositoryCreationTests { /// This test: /// 1. Sends a valid repository announcement via TestContext /// 2. Verifies the announcement was accepted - /// 3. Checks that a bare git repository was created at the expected path + /// 3. Verifies the repository is accessible via Smart HTTP service pub async fn test_bare_repo_created_on_announcement( client: &AuditClient, - git_data_dir: &Path, + relay_domain: &str, ) -> TestResult { let test_name = "test_bare_repo_created_on_announcement"; let ctx = TestContext::new(client); @@ -97,19 +95,14 @@ impl RepositoryCreationTests { } }; - // Check if repository was created - let repo_path = git_data_dir.join(&npub).join(format!("{}.git", repo_id)); - - if !is_bare_repository(&repo_path) { + // Verify repository exists via HTTP (info/refs endpoint) + if let Err(e) = check_repo_accessible_via_http(relay_domain, &npub, &repo_id).await { return TestResult::new( test_name, "GRASP-01", "Bare repository must be created when announcement is accepted", ) - .fail(&format!( - "Bare repository not found at: {}", - repo_path.display() - )); + .fail(&format!("Repository not accessible via HTTP: {}", e)); } TestResult::new( @@ -128,7 +121,7 @@ impl RepositoryCreationTests { /// 3. Verifies no error occurs and repo still exists pub async fn test_repo_creation_idempotent( client: &AuditClient, - git_data_dir: &Path, + relay_domain: &str, ) -> TestResult { let test_name = "test_repo_creation_idempotent"; let ctx = TestContext::new(client); @@ -162,7 +155,7 @@ impl RepositoryCreationTests { // Wait again tokio::time::sleep(std::time::Duration::from_millis(200)).await; - // Verify repository still exists and is valid + // Verify repository still exists and is accessible via HTTP let repo_id = repo .tags .iter() @@ -173,15 +166,14 @@ impl RepositoryCreationTests { .to_string(); let npub = repo.pubkey.to_bech32().unwrap(); - let repo_path = git_data_dir.join(&npub).join(format!("{}.git", repo_id)); - if !is_bare_repository(&repo_path) { + if let Err(e) = check_repo_accessible_via_http(relay_domain, &npub, &repo_id).await { return TestResult::new( test_name, "GRASP-01", "Repository creation must be idempotent", ) - .fail("Repository not found after second send"); + .fail(&format!("Repository not accessible after second send: {}", e)); } TestResult::new( @@ -192,14 +184,16 @@ impl RepositoryCreationTests { .pass() } - /// Test that the repository has the correct structure + /// Test that the repository is accessible via Smart HTTP service /// /// This test verifies: - /// 1. Repository is at //.git - /// 2. Repository is bare (no working directory) - /// 3. Repository has required git structure (HEAD, config, objects/, refs/) - pub async fn test_bare_repo_structure(client: &AuditClient, git_data_dir: &Path) -> TestResult { - let test_name = "test_bare_repo_structure"; + /// 1. Repository responds to git-upload-pack service discovery + /// 2. URL format follows http://domain/npub/identifier.git pattern + pub async fn test_repo_accessible_via_http( + client: &AuditClient, + relay_domain: &str, + ) -> TestResult { + let test_name = "test_repo_accessible_via_http"; let ctx = TestContext::new(client); // Create and send repository announcement via TestContext @@ -209,7 +203,7 @@ impl RepositoryCreationTests { return TestResult::new( test_name, "GRASP-01", - "Bare repository must have correct structure", + "Repository must be accessible via Smart HTTP service", ) .fail(&format!("Failed to create repo fixture: {}", e)) } @@ -230,134 +224,76 @@ impl RepositoryCreationTests { let npub = repo.pubkey.to_bech32().unwrap(); - // Verify correct path structure: //.git - let expected_path = git_data_dir.join(&npub).join(format!("{}.git", repo_id)); - - if !expected_path.exists() { - return TestResult::new( - test_name, - "GRASP-01", - "Bare repository must have correct structure", - ) - .fail(&format!( - "Repository not at expected path: {}", - expected_path.display() - )); - } - - // Verify it's a bare repository with correct structure - if !expected_path.join("HEAD").is_file() { - return TestResult::new( - test_name, - "GRASP-01", - "Bare repository must have correct structure", - ) - .fail("Missing HEAD file"); - } - - if !expected_path.join("config").is_file() { - return TestResult::new( - test_name, - "GRASP-01", - "Bare repository must have correct structure", - ) - .fail("Missing config file"); - } - - if !expected_path.join("objects").is_dir() { + // Verify repository is accessible via HTTP + if let Err(e) = check_repo_accessible_via_http(relay_domain, &npub, &repo_id).await { return TestResult::new( test_name, "GRASP-01", - "Bare repository must have correct structure", + "Repository must be accessible via Smart HTTP service", ) - .fail("Missing objects/ directory"); - } - - if !expected_path.join("refs").is_dir() { - return TestResult::new( - test_name, - "GRASP-01", - "Bare repository must have correct structure", - ) - .fail("Missing refs/ directory"); - } - - // Verify the helper function agrees - if !is_bare_repository(&expected_path) { - return TestResult::new( - test_name, - "GRASP-01", - "Bare repository must have correct structure", - ) - .fail("Helper function does not recognize repository as bare"); + .fail(&e); } TestResult::new( test_name, "GRASP-01", - "Bare repository must have correct structure", + "Repository must be accessible via Smart HTTP service", ) .pass() } } -/// Helper function to check if a path is a valid bare git repository +/// Helper function to check if a repository is accessible via Smart HTTP service /// -/// A bare repository must have: -/// - HEAD file -/// - config file -/// - objects/ directory -/// - refs/ directory -pub fn is_bare_repository(path: &Path) -> bool { - if !path.exists() { - return false; +/// Verifies that the repository responds correctly to git-upload-pack service discovery +/// at the URL: http://domain/npub/identifier.git/info/refs?service=git-upload-pack +async fn check_repo_accessible_via_http( + relay_domain: &str, + npub: &str, + repo_id: &str, +) -> Result<(), String> { + let info_refs_url = format!( + "http://{}/{}/{}.git/info/refs?service=git-upload-pack", + relay_domain, npub, repo_id + ); + + let http_client = reqwest::Client::new(); + let response = http_client + .get(&info_refs_url) + .send() + .await + .map_err(|e| format!("HTTP request failed: {}", e))?; + + if !response.status().is_success() { + return Err(format!( + "info/refs returned status {} for URL: {}", + response.status(), + info_refs_url + )); } - // Check for required bare repository components - let has_head = path.join("HEAD").is_file(); - let has_config = path.join("config").is_file(); - let has_objects = path.join("objects").is_dir(); - let has_refs = path.join("refs").is_dir(); + // Verify Content-Type indicates git-upload-pack service + let content_type = response + .headers() + .get("content-type") + .and_then(|v| v.to_str().ok()) + .unwrap_or(""); + + if !content_type.contains("application/x-git-upload-pack-advertisement") { + return Err(format!( + "Expected Content-Type: application/x-git-upload-pack-advertisement, got: {}", + content_type + )); + } - has_head && has_config && has_objects && has_refs + Ok(()) } #[cfg(test)] mod tests { - use super::*; - use std::process::Command; - - #[test] - fn test_is_bare_repository_detects_valid_repo() { - // Create a temporary bare repository for testing - let temp_dir = tempfile::tempdir().unwrap(); - let repo_path = temp_dir.path().join("test.git"); - - // Initialize a bare repository - Command::new("git") - .args(&["init", "--bare", repo_path.to_str().unwrap()]) - .output() - .expect("Failed to create test repository"); - - // Verify our helper function detects it - assert!( - is_bare_repository(&repo_path), - "Should detect valid bare repository" - ); - } - - #[test] - fn test_is_bare_repository_rejects_non_repo() { - let temp_dir = tempfile::tempdir().unwrap(); - assert!( - !is_bare_repository(temp_dir.path()), - "Should reject non-repository directory" - ); - } - #[test] - fn test_is_bare_repository_rejects_nonexistent() { - let path = Path::new("/nonexistent/path/to/repo.git"); - assert!(!is_bare_repository(path), "Should reject nonexistent path"); + fn test_module_exists() { + // Simple compilation test + assert!(true); } } -- cgit v1.2.3