pakker/tests/fork_from_path.rs
NotAShelf ef28bdaeb4
initial commit
Signed-off-by: NotAShelf <raf@notashelf.dev>
Change-Id: Ife1391ed23a1e7f388b1b5eca90b9ea76a6a6964
2026-02-13 00:14:46 +03:00

196 lines
5.4 KiB
Rust

use std::{error::Error, fs, process::Command};
use git2::{Repository, Signature};
use tempfile::TempDir;
// shared test helpers
mod common;
use common::{
init_bare_repo,
init_repo_with_commit,
pakker_bin_path,
push_to_remote,
};
#[test]
fn happy_path_from_path() -> Result<(), Box<dyn Error>> {
let tmp = TempDir::new()?;
let tmp_path = tmp.path().to_path_buf();
let upstream = tmp_path.join("upstream.git");
init_bare_repo(&upstream)?;
let work = tmp_path.join("work_repo");
fs::create_dir_all(&work)?;
let work_repo = init_repo_with_commit(&work, "README.md", "hello")?;
push_to_remote(&work_repo, "origin", upstream.to_str().unwrap())?;
// Clone upstream to local path
let local = tmp_path.join("local_clone");
Repository::clone(upstream.to_str().unwrap(), &local)?;
// Now create a new project dir where pakker will be initialized
let project = tmp_path.join("project_dir");
fs::create_dir_all(&project)?;
let pakker = pakker_bin_path();
let status = Command::new(pakker)
.args([
"fork",
"init",
"--from-path",
local.to_str().unwrap(),
"--ref-name",
"master",
])
.current_dir(&project)
.status()?;
assert!(status.success());
let parent = project.join(".pakku").join("parent");
assert!(parent.exists());
Ok(())
}
#[test]
fn fails_with_uncommitted_changes() -> Result<(), Box<dyn Error>> {
let tmp = TempDir::new()?;
let tmp_path = tmp.path().to_path_buf();
let upstream = tmp_path.join("upstream2.git");
init_bare_repo(&upstream)?;
let work = tmp_path.join("work2");
fs::create_dir_all(&work)?;
let work_repo = init_repo_with_commit(&work, "file.txt", "a")?;
work_repo.remote("origin", upstream.to_str().unwrap())?;
push_to_remote(&work_repo, "origin", upstream.to_str().unwrap())?;
let local = tmp_path.join("local2");
Repository::clone(upstream.to_str().unwrap(), &local)?;
// Make an uncommitted change
fs::write(local.join("UNCOMMITTED.md"), "oops")?;
let project = tmp_path.join("project2");
fs::create_dir_all(&project)?;
let pakker = pakker_bin_path();
let output = Command::new(pakker)
.args([
"fork",
"init",
"--from-path",
local.to_str().unwrap(),
"--ref-name",
"master",
])
.current_dir(&project)
.output()?;
assert!(!output.status.success());
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
stderr.to_lowercase().contains("uncommitted")
|| stderr.to_lowercase().contains("dirty")
);
Ok(())
}
#[test]
fn fails_when_local_ahead() -> Result<(), Box<dyn Error>> {
let tmp = TempDir::new()?;
let tmp_path = tmp.path().to_path_buf();
let upstream = tmp_path.join("upstream3.git");
init_bare_repo(&upstream)?;
let work = tmp_path.join("work3");
fs::create_dir_all(&work)?;
let work_repo = init_repo_with_commit(&work, "f.txt", "1")?;
work_repo.remote("origin", upstream.to_str().unwrap())?;
push_to_remote(&work_repo, "origin", upstream.to_str().unwrap())?;
let local = tmp_path.join("local3");
Repository::clone(upstream.to_str().unwrap(), &local)?;
// Create a new commit locally that is not pushed
{
let repo = Repository::open(&local)?;
let workdir = repo.workdir().ok_or("no workdir")?;
fs::write(workdir.join("f.txt"), "2")?;
let mut index = repo.index()?;
index.add_path(std::path::Path::new("f.txt"))?;
index.write()?;
let tree_oid = index.write_tree()?;
let tree = repo.find_tree(tree_oid)?;
let sig = Signature::now("Test User", "test@example.com")?;
let head = repo.head()?;
let parent = head.peel_to_commit()?;
repo.commit(Some("HEAD"), &sig, &sig, "local change", &tree, &[&parent])?;
}
let project = tmp_path.join("project3");
fs::create_dir_all(&project)?;
let pakker = pakker_bin_path();
let output = Command::new(pakker)
.args([
"fork",
"init",
"--from-path",
local.to_str().unwrap(),
"--ref-name",
"master",
])
.current_dir(&project)
.output()?;
assert!(!output.status.success());
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
stderr.to_lowercase().contains("commits not present")
|| stderr.to_lowercase().contains("not present on upstream")
);
Ok(())
}
#[test]
fn warns_on_fetch_failure_and_proceeds() -> Result<(), Box<dyn Error>> {
let tmp = TempDir::new()?;
let tmp_path = tmp.path().to_path_buf();
let upstream = tmp_path.join("upstream4.git");
init_bare_repo(&upstream)?;
let work = tmp_path.join("work4");
fs::create_dir_all(&work)?;
let work_repo = init_repo_with_commit(&work, "a.txt", "1")?;
work_repo.remote("origin", upstream.to_str().unwrap())?;
push_to_remote(&work_repo, "origin", upstream.to_str().unwrap())?;
let local = tmp_path.join("local4");
Repository::clone(upstream.to_str().unwrap(), &local)?;
// Set an invalid URL for origin to force fetch failure
let repo = Repository::open(&local)?;
repo.remote_set_url("origin", "git@invalid:nonexistent/repo.git")?;
let project = tmp_path.join("project4");
fs::create_dir_all(&project)?;
let pakker = pakker_bin_path();
let status = Command::new(pakker)
.args([
"fork",
"init",
"--from-path",
local.to_str().unwrap(),
"--ref-name",
"master",
])
.current_dir(&project)
.status()?;
assert!(status.success());
let parent = project.join(".pakku").join("parent");
assert!(parent.exists());
Ok(())
}