initial commit
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: Ife1391ed23a1e7f388b1b5eca90b9ea76a6a6964
This commit is contained in:
commit
ef28bdaeb4
63 changed files with 17292 additions and 0 deletions
65
tests/common/mod.rs
Normal file
65
tests/common/mod.rs
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
use std::{env, error::Error, fs, path::PathBuf};
|
||||
|
||||
use git2::{Repository, Signature};
|
||||
|
||||
pub fn pakker_bin_path() -> PathBuf {
|
||||
let manifest = env!("CARGO_MANIFEST_DIR");
|
||||
PathBuf::from(manifest).join("target/debug/pakker")
|
||||
}
|
||||
|
||||
pub fn init_bare_repo(path: &PathBuf) -> Result<Repository, git2::Error> {
|
||||
Repository::init_bare(path)
|
||||
}
|
||||
|
||||
pub fn init_repo_with_commit(
|
||||
path: &PathBuf,
|
||||
file: &str,
|
||||
content: &str,
|
||||
) -> Result<Repository, Box<dyn Error>> {
|
||||
let repo = Repository::init(path)?;
|
||||
let sig = Signature::now("Test User", "test@example.com")?;
|
||||
|
||||
let workdir = repo.workdir().ok_or("no workdir")?;
|
||||
let file_path = workdir.join(file);
|
||||
fs::write(&file_path, content)?;
|
||||
|
||||
let mut index = repo.index()?;
|
||||
index.add_path(std::path::Path::new(file))?;
|
||||
index.write()?;
|
||||
let tree_oid = index.write_tree()?;
|
||||
let tree = repo.find_tree(tree_oid)?;
|
||||
|
||||
// initial commit
|
||||
let commit_oid =
|
||||
repo.commit(Some("HEAD"), &sig, &sig, "initial", &tree, &[])?;
|
||||
// create/ensure master branch points to this commit and set HEAD
|
||||
repo.reference("refs/heads/master", commit_oid, true, "create master")?;
|
||||
repo.set_head("refs/heads/master")?;
|
||||
// drop tree to avoid holding a borrow of `repo` when returning it
|
||||
drop(tree);
|
||||
Ok(repo)
|
||||
}
|
||||
|
||||
pub fn push_to_remote(
|
||||
repo: &Repository,
|
||||
remote_name: &str,
|
||||
remote_url: &str,
|
||||
) -> Result<(), git2::Error> {
|
||||
// Try to create the remote; if it already exists, find it instead
|
||||
let mut remote = match repo.remote(remote_name, remote_url) {
|
||||
Ok(r) => r,
|
||||
Err(_) => repo.find_remote(remote_name)?,
|
||||
};
|
||||
// Push current HEAD to refs/heads/master on remote
|
||||
remote.push(&["HEAD:refs/heads/master"], None)?;
|
||||
|
||||
// If remote is a local filesystem path, ensure its HEAD points to master
|
||||
if remote_url.starts_with('/')
|
||||
&& let Ok(bare) = Repository::open(remote_url)
|
||||
{
|
||||
// Set bare repo HEAD to refs/heads/master
|
||||
let _ = bare.set_head("refs/heads/master");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
196
tests/fork_from_path.rs
Normal file
196
tests/fork_from_path.rs
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
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(())
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue