diff options
Diffstat (limited to 'tests/integration/git_helpers.rs')
| -rw-r--r-- | tests/integration/git_helpers.rs | 275 |
1 files changed, 275 insertions, 0 deletions
diff --git a/tests/integration/git_helpers.rs b/tests/integration/git_helpers.rs new file mode 100644 index 0000000..578806a --- /dev/null +++ b/tests/integration/git_helpers.rs @@ -0,0 +1,275 @@ +use std::path::Path; +use tokio::process::Command; + +/// Create a git Command isolated from parent git environment. +/// Prevents interference when tests run inside git hooks +/// (e.g., pre-commit hook running `cargo test`). +fn git_cmd() -> Command { + let mut cmd = Command::new("git"); + cmd.env_remove("GIT_DIR") + .env_remove("GIT_WORK_TREE") + .env_remove("GIT_INDEX_FILE"); + cmd +} + +/// Check if git is available on this system. +pub fn is_git_available() -> bool { + std::process::Command::new("git") + .arg("--version") + .stdout(std::process::Stdio::null()) + .stderr(std::process::Stdio::null()) + .status() + .map(|s| s.success()) + .unwrap_or(false) +} + +/// Create a local bare git repository with an initial commit. +/// Returns a `file://` URL usable by `git clone --depth 1`. +pub async fn create_local_repo(parent_dir: &Path, branch: &str) -> String { + let bare_repo = parent_dir.join("origin.git"); + tokio::fs::create_dir_all(&bare_repo).await.unwrap(); + + // Init bare repo + let output = git_cmd() + .args(["init", "--bare", "--initial-branch", branch]) + .current_dir(&bare_repo) + .output() + .await + .unwrap(); + assert!(output.status.success(), "git init --bare failed"); + + // Create working copy for initial commit + let work_dir = parent_dir.join("work"); + let output = git_cmd() + .args([ + "clone", + bare_repo.to_str().unwrap(), + work_dir.to_str().unwrap(), + ]) + .output() + .await + .unwrap(); + assert!( + output.status.success(), + "git clone failed: {}", + String::from_utf8_lossy(&output.stderr) + ); + + // Configure git user + for args in [ + &["config", "user.email", "test@test.local"][..], + &["config", "user.name", "Test"], + ] { + let out = git_cmd() + .args(args) + .current_dir(&work_dir) + .output() + .await + .unwrap(); + assert!(out.status.success()); + } + + // Checkout target branch + let output = git_cmd() + .args(["checkout", "-B", branch]) + .current_dir(&work_dir) + .output() + .await + .unwrap(); + assert!(output.status.success(), "git checkout failed"); + + // Create witryna.yaml + initial content + tokio::fs::write( + work_dir.join("witryna.yaml"), + "image: alpine:latest\ncommand: \"mkdir -p out && echo '<h1>test</h1>' > out/index.html\"\npublic: out\n", + ) + .await + .unwrap(); + + tokio::fs::create_dir_all(work_dir.join("out")) + .await + .unwrap(); + tokio::fs::write(work_dir.join("out/index.html"), "<h1>initial</h1>") + .await + .unwrap(); + + // Stage and commit + let output = git_cmd() + .args(["add", "-A"]) + .current_dir(&work_dir) + .output() + .await + .unwrap(); + assert!(output.status.success(), "git add failed"); + + let output = git_cmd() + .args(["commit", "-m", "Initial commit"]) + .current_dir(&work_dir) + .output() + .await + .unwrap(); + assert!( + output.status.success(), + "git commit failed: {}", + String::from_utf8_lossy(&output.stderr) + ); + + // Push + let output = git_cmd() + .args(["push", "-u", "origin", branch]) + .current_dir(&work_dir) + .output() + .await + .unwrap(); + assert!( + output.status.success(), + "git push failed: {}", + String::from_utf8_lossy(&output.stderr) + ); + + // Cleanup working copy + let _ = tokio::fs::remove_dir_all(&work_dir).await; + + format!("file://{}", bare_repo.to_str().unwrap()) +} + +/// Create a local bare repo without a witryna.yaml (for override-only tests). +pub async fn create_bare_repo(parent_dir: &Path, branch: &str) -> String { + let bare_repo = parent_dir.join("bare-origin.git"); + tokio::fs::create_dir_all(&bare_repo).await.unwrap(); + + let output = git_cmd() + .args(["init", "--bare", "--initial-branch", branch]) + .current_dir(&bare_repo) + .output() + .await + .unwrap(); + assert!(output.status.success()); + + let work_dir = parent_dir.join("bare-work"); + let output = git_cmd() + .args([ + "clone", + bare_repo.to_str().unwrap(), + work_dir.to_str().unwrap(), + ]) + .output() + .await + .unwrap(); + assert!(output.status.success()); + + for args in [ + &["config", "user.email", "test@test.local"][..], + &["config", "user.name", "Test"], + ] { + git_cmd() + .args(args) + .current_dir(&work_dir) + .output() + .await + .unwrap(); + } + + let output = git_cmd() + .args(["checkout", "-B", branch]) + .current_dir(&work_dir) + .output() + .await + .unwrap(); + assert!(output.status.success()); + + tokio::fs::write(work_dir.join("README.md"), "# Test\n") + .await + .unwrap(); + + git_cmd() + .args(["add", "-A"]) + .current_dir(&work_dir) + .output() + .await + .unwrap(); + + let output = git_cmd() + .args(["commit", "-m", "Initial commit"]) + .current_dir(&work_dir) + .output() + .await + .unwrap(); + assert!(output.status.success()); + + let output = git_cmd() + .args(["push", "-u", "origin", branch]) + .current_dir(&work_dir) + .output() + .await + .unwrap(); + assert!(output.status.success()); + + let _ = tokio::fs::remove_dir_all(&work_dir).await; + + format!("file://{}", bare_repo.to_str().unwrap()) +} + +/// Push a new commit to a bare repo (clone, commit, push). +pub async fn push_new_commit(bare_repo_url: &str, parent_dir: &Path, branch: &str) { + let work_dir = parent_dir.join("push-work"); + let _ = tokio::fs::remove_dir_all(&work_dir).await; + + let output = git_cmd() + .args([ + "clone", + "--branch", + branch, + bare_repo_url, + work_dir.to_str().unwrap(), + ]) + .output() + .await + .unwrap(); + assert!(output.status.success(), "clone for push failed"); + + for args in [ + &["config", "user.email", "test@test.local"][..], + &["config", "user.name", "Test"], + ] { + git_cmd() + .args(args) + .current_dir(&work_dir) + .output() + .await + .unwrap(); + } + + let timestamp = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_nanos(); + tokio::fs::write(work_dir.join("update.txt"), format!("update-{timestamp}")) + .await + .unwrap(); + + git_cmd() + .args(["add", "-A"]) + .current_dir(&work_dir) + .output() + .await + .unwrap(); + + let output = git_cmd() + .args(["commit", "-m", "Test update"]) + .current_dir(&work_dir) + .output() + .await + .unwrap(); + assert!(output.status.success(), "commit failed"); + + let output = git_cmd() + .args(["push", "origin", branch]) + .current_dir(&work_dir) + .output() + .await + .unwrap(); + assert!(output.status.success(), "push failed"); + + let _ = tokio::fs::remove_dir_all(&work_dir).await; +} |
