summaryrefslogtreecommitdiff
path: root/tests/integration/git_helpers.rs
diff options
context:
space:
mode:
Diffstat (limited to 'tests/integration/git_helpers.rs')
-rw-r--r--tests/integration/git_helpers.rs275
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;
+}