git-next/crates/git/src/validation/tests.rs

595 lines
25 KiB
Rust
Raw Normal View History

2024-06-09 10:21:09 +01:00
//
use crate as git;
use git::tests::given;
use git_next_config as config;
use assert2::let_assert;
mod repos {
use crate::repository::RepositoryLike as _;
use super::*;
#[test]
fn where_repo_has_no_push_remote() {
let_assert!(Ok(fs) = kxio::fs::temp(), "temp fs");
let gitdir: config::GitDir = fs.base().to_path_buf().into();
let test_repository = git::repository::test(fs.clone());
// default has no push or fetch remotes
let repo_details = given::repo_details(&fs);
let_assert!(Ok(repository) = test_repository.open(&gitdir), "open repo");
let_assert!(Err(err) = git::validation::repo::validate_repo(&repository, &repo_details));
assert!(matches!(
err,
git::validation::repo::Error::NoDefaultPushRemote
));
}
}
mod positions {
use super::*;
use git::repository::RepositoryLike as _;
mod validate_positions {
use git::validation::positions::validate_positions;
use git::tests::then;
use super::*;
#[test]
fn where_fetch_fails_should_error() {
let_assert!(Ok(fs) = kxio::fs::temp(), "temp fs");
let gitdir: config::GitDir = fs.base().to_path_buf().into();
let mut test_repository = git::repository::test(fs.clone());
test_repository.on_fetch(git::repository::OnFetch::new(
given::repo_branches(),
gitdir.clone(),
fs.clone(),
|_, _, _| git::fetch::Result::Err(git::fetch::Error::TestFailureExpected),
));
let_assert!(Ok(repository) = test_repository.open(&gitdir), "open repo");
let repo_details = given::repo_details(&fs).with_gitdir(gitdir);
let repo_config = given::a_repo_config();
let_assert!(
Err(err) = validate_positions(&repository, &repo_details, repo_config),
"validate"
);
assert!(matches!(
err,
git::validation::positions::Error::Fetch(git::fetch::Error::TestFailureExpected)
));
}
#[test]
fn where_main_branch_is_missing_or_commit_log_is_empty_should_error() {
let_assert!(Ok(fs) = kxio::fs::temp(), "temp fs");
let gitdir: config::GitDir = fs.base().to_path_buf().into();
let mut test_repository = git::repository::test(fs.clone());
test_repository.on_fetch(git::repository::OnFetch::new(
given::repo_branches(),
gitdir.clone(),
fs.clone(),
|_, _, _| git::fetch::Result::Ok(()),
));
// test repo is a new bare repo with no commits
let_assert!(Ok(repository) = test_repository.open(&gitdir), "open repo");
let repo_details = given::repo_details(&fs).with_gitdir(gitdir);
let repo_config = given::a_repo_config();
let_assert!(
Err(err) = validate_positions(&repository, &repo_details, repo_config.clone()),
"validate"
);
let branch_name = repo_config.branches().main();
let error_message = format!(
r#"The ref partially named "remotes/origin/{branch_name}" could not be found"#
);
assert!(matches!(
err,
git::validation::positions::Error::CommitLog(git::commit::log::Error::Gix {
branch,
error
}) if branch == branch_name && error == error_message
));
}
#[test]
fn where_next_branch_is_missing_or_commit_log_is_empty_should_error() {
//given
let_assert!(Ok(fs) = kxio::fs::temp(), "temp fs");
let gitdir: config::GitDir = fs.base().to_path_buf().into();
let mut test_repository = git::repository::test(fs.clone());
let repo_config = given::a_repo_config();
test_repository.on_fetch(git::repository::OnFetch::new(
repo_config.branches().clone(),
gitdir.clone(),
fs.clone(),
|branches, gitdir, fs| {
// add a commit to main
then::create_a_commit_on_branch(fs, gitdir, &branches.main())?;
git::fetch::Result::Ok(())
},
));
let_assert!(Ok(repository) = test_repository.open(&gitdir), "open repo");
let repo_details = given::repo_details(&fs).with_gitdir(gitdir);
//when
let_assert!(
Err(err) = validate_positions(&repository, &repo_details, repo_config.clone()),
"validate"
);
//then
let branch_name = repo_config.branches().next();
let error_message = format!(
r#"The ref partially named "remotes/origin/{branch_name}" could not be found"#
);
assert!(matches!(
err,
git::validation::positions::Error::CommitLog(git::commit::log::Error::Gix {
branch,
error
}) if branch == branch_name && error == error_message
));
}
#[test]
fn where_dev_branch_is_missing_or_commit_log_is_empty_should_error() {
//given
let_assert!(Ok(fs) = kxio::fs::temp(), "temp fs");
let gitdir: config::GitDir = fs.base().to_path_buf().into();
let mut test_repository = git::repository::test(fs.clone());
let repo_config = given::a_repo_config();
test_repository.on_fetch(git::repository::OnFetch::new(
repo_config.branches().clone(),
gitdir.clone(),
fs.clone(),
|branches, gitdir, fs| {
// add a commit to main
then::create_a_commit_on_branch(fs, gitdir, &branches.main())?;
// add a commit to next
then::create_a_commit_on_branch(fs, gitdir, &branches.next())?;
git::fetch::Result::Ok(())
},
));
let_assert!(Ok(repository) = test_repository.open(&gitdir), "open repo");
let repo_details = given::repo_details(&fs).with_gitdir(gitdir);
//when
let_assert!(
Err(err) = validate_positions(&repository, &repo_details, repo_config.clone()),
"validate"
);
//then
let branch_name = repo_config.branches().dev();
let error_message = format!(
r#"The ref partially named "remotes/origin/{branch_name}" could not be found"#
);
assert!(matches!(
err,
git::validation::positions::Error::CommitLog(git::commit::log::Error::Gix {
branch,
error
}) if branch == branch_name && error == error_message
));
}
#[test]
fn where_dev_branch_is_not_based_on_main_should_error() {
//given
let_assert!(Ok(fs) = kxio::fs::temp(), "temp fs");
let gitdir: config::GitDir = fs.base().to_path_buf().into();
let mut test_repository = git::repository::test(fs.clone());
let repo_config = given::a_repo_config();
test_repository.on_fetch(git::repository::OnFetch::new(
repo_config.branches().clone(),
gitdir.clone(),
fs.clone(),
|branches, gitdir, fs| {
// /--- 4 next
// 0 --- 1 --- 3 main
// \--- 2 dev
// add a commit to main (0 -> 1)
then::create_a_commit_on_branch(fs, gitdir, &branches.main())?;
// add a commit to dev ( 1 -> 2)
then::create_a_commit_on_branch(fs, gitdir, &branches.dev())?;
// switch back to main
then::git_switch(&branches.main(), gitdir)?;
// add a second commit to main (1 -> 3)
then::git_log_all(gitdir)?;
then::create_a_commit_on_branch(fs, gitdir, &branches.main())?;
// add a commit to next 1 -> 4
then::create_a_commit_on_branch(fs, gitdir, &branches.next())?;
then::git_log_all(gitdir)?;
git::fetch::Result::Ok(())
},
));
let_assert!(Ok(repository) = test_repository.open(&gitdir), "open repo");
let repo_details = given::repo_details(&fs).with_gitdir(gitdir);
//when
let_assert!(
Err(err) = validate_positions(&repository, &repo_details, repo_config.clone()),
"validate"
);
//then
assert!(matches!(
err,
git::validation::positions::Error::DevBranchNotBasedOn { dev, other }
if dev == repo_config.branches().dev() && other == repo_config.branches().main()
));
}
#[test]
fn where_next_branch_is_not_based_on_main_should_reset_next_branch_to_main_and_then_error()
{
//given
let_assert!(Ok(fs) = kxio::fs::temp(), "temp fs");
let gitdir: config::GitDir = fs.base().to_path_buf().into();
let mut test_repository = git::repository::test(fs.clone());
let repo_config = given::a_repo_config();
test_repository.on_fetch(git::repository::OnFetch::new(
repo_config.branches().clone(),
gitdir.clone(),
fs.clone(),
|branches, gitdir, fs| {
// /--- 4 dev
// 0 --- 1 --- 3 main
// \--- 2 next
// add a commit to main (0 -> 1)
then::create_a_commit_on_branch(fs, gitdir, &branches.main())?;
// add a commit to next (1 -> 2)
then::create_a_commit_on_branch(fs, gitdir, &branches.next())?;
// switch back to main (1)
then::git_switch(&branches.main(), gitdir)?;
// add a second commit to main (1 -> 3)
then::create_a_commit_on_branch(fs, gitdir, &branches.main())?;
// add a commit to dev (3 -> 4)
then::create_a_commit_on_branch(fs, gitdir, &branches.dev())?;
then::git_log_all(gitdir)?;
git::fetch::Result::Ok(())
},
));
// second fetch as prep to push
test_repository.on_fetch(git::repository::OnFetch::new(
repo_config.branches().clone(),
gitdir.clone(),
fs.clone(),
|_branches, _gitdir, _fs| {
// don't change anything
git::fetch::Result::Ok(())
},
));
test_repository.on_push(git::repository::OnPush::new(
repo_config.branches().clone(),
gitdir.clone(),
fs.clone(),
|_repo_details, branch_name, gitref, force, repo_branches, gitdir, fs| {
assert_eq!(
branch_name,
&repo_branches.next(),
"branch to reset should be 'next'"
);
let sha_main = then::get_sha_for_branch(fs, gitdir, &repo_branches.main())?;
assert_eq!(
gitref,
&git::GitRef::from(sha_main),
"should reset to the sha of the 'main' branch"
);
let sha_next = then::get_sha_for_branch(fs, gitdir, &repo_branches.next())?;
assert_eq!(
force,
&git::push::Force::From(sha_next.into()),
"should force push only if next is on expected sha"
);
git::push::Result::Ok(())
},
));
let_assert!(Ok(repository) = test_repository.open(&gitdir), "open repo");
let repo_details = given::repo_details(&fs).with_gitdir(gitdir);
//when
let_assert!(
Err(err) = validate_positions(&repository, &repo_details, repo_config.clone()),
"validate"
);
//then
2024-06-13 20:00:04 +01:00
eprintln!("Got: {err:?}");
2024-06-09 10:21:09 +01:00
// NOTE: assertions for correct push are in on_push above
assert!(matches!(
err,
git::validation::positions::Error::NextBranchResetRequired(branch)
if branch == repo_config.branches().next()
));
}
#[test]
fn where_next_branch_is_not_based_on_main_and_reset_of_next_fails_should_error() {
//given
let_assert!(Ok(fs) = kxio::fs::temp(), "temp fs");
let gitdir: config::GitDir = fs.base().to_path_buf().into();
let mut test_repository = git::repository::test(fs.clone());
let repo_config = given::a_repo_config();
test_repository.on_fetch(git::repository::OnFetch::new(
repo_config.branches().clone(),
gitdir.clone(),
fs.clone(),
|branches, gitdir, fs| {
// /--- 4 dev
// 0 --- 1 --- 3 main
// \--- 2 next
// add a commit to main (0 -> 1)
then::create_a_commit_on_branch(fs, gitdir, &branches.main())?;
// add a commit to next (1 -> 2)
then::create_a_commit_on_branch(fs, gitdir, &branches.next())?;
// switch back to main (1)
then::git_switch(&branches.main(), gitdir)?;
// add a second commit to main (1 -> 3)
then::create_a_commit_on_branch(fs, gitdir, &branches.main())?;
// add a commit to dev (3 -> 4)
then::create_a_commit_on_branch(fs, gitdir, &branches.dev())?;
then::git_log_all(gitdir)?;
git::fetch::Result::Ok(())
},
));
// second fetch as prep to push
test_repository.on_fetch(git::repository::OnFetch::new(
repo_config.branches().clone(),
gitdir.clone(),
fs.clone(),
|_branches, _gitdir, _fs| {
// don't change anything
git::fetch::Result::Ok(())
},
));
test_repository.on_push(git::repository::OnPush::new(
repo_config.branches().clone(),
gitdir.clone(),
fs.clone(),
|_repo_details, _branch_name, _gitref, _force, _repo_branches, _gitdir, _fs| {
git::push::Result::Err(git::push::Error::Lock)
},
));
let_assert!(Ok(repository) = test_repository.open(&gitdir), "open repo");
let repo_details = given::repo_details(&fs).with_gitdir(gitdir.clone());
//when
let_assert!(
Err(err) = validate_positions(&repository, &repo_details, repo_config.clone()),
"validate"
);
//then
2024-06-13 20:00:04 +01:00
eprintln!("Got: {err:?}");
2024-06-09 10:21:09 +01:00
let_assert!(
Ok(sha_next) =
then::get_sha_for_branch(&fs, &gitdir, &repo_config.branches().next()),
"load next branch sha"
);
assert!(matches!(
err,
git::validation::positions::Error::FailedToResetBranch{branch, commit}
if branch == repo_config.branches().next() && commit.sha() == &sha_next
));
}
#[test]
fn where_dev_branch_is_not_based_on_next_should_reset_next_branch_to_main_and_then_error() {
//given
let_assert!(Ok(fs) = kxio::fs::temp(), "temp fs");
let gitdir: config::GitDir = fs.base().to_path_buf().into();
let mut test_repository = git::repository::test(fs.clone());
let repo_config = given::a_repo_config();
test_repository.on_fetch(git::repository::OnFetch::new(
repo_config.branches().clone(),
gitdir.clone(),
fs.clone(),
|branches, gitdir, fs| {
// /--- 3 next
// 0 --- 1 main
// \--- 2 dev
// add a commit to main (0 -> 1)
then::create_a_commit_on_branch(fs, gitdir, &branches.main())?;
// add a commit to dev (1 -> 2)
then::create_a_commit_on_branch(fs, gitdir, &branches.dev())?;
// switch back to main (1)
then::git_switch(&branches.main(), gitdir)?;
// add a commit to next (1 -> 3)
then::create_a_commit_on_branch(fs, gitdir, &branches.next())?;
then::git_log_all(gitdir)?;
git::fetch::Result::Ok(())
},
));
// second fetch as prep to push
test_repository.on_fetch(git::repository::OnFetch::new(
repo_config.branches().clone(),
gitdir.clone(),
fs.clone(),
|_branches, _gitdir, _fs| {
// don't change anything
git::fetch::Result::Ok(())
},
));
test_repository.on_push(git::repository::OnPush::new(
repo_config.branches().clone(),
gitdir.clone(),
fs.clone(),
|_repo_details, branch_name, gitref, force, repo_branches, gitdir, fs| {
assert_eq!(
branch_name,
&repo_branches.next(),
"branch to reset should be 'next'"
);
let sha_main = then::get_sha_for_branch(fs, gitdir, &repo_branches.main())?;
assert_eq!(
gitref,
&git::GitRef::from(sha_main),
"should reset to the sha of the 'main' branch"
);
let sha_next = then::get_sha_for_branch(fs, gitdir, &repo_branches.next())?;
assert_eq!(
force,
&git::push::Force::From(sha_next.into()),
"should force push only if next is on expected sha"
);
git::push::Result::Ok(())
},
));
let_assert!(Ok(repository) = test_repository.open(&gitdir), "open repo");
let repo_details = given::repo_details(&fs).with_gitdir(gitdir);
//when
let_assert!(
Err(err) = validate_positions(&repository, &repo_details, repo_config.clone()),
"validate"
);
//then
2024-06-13 20:00:04 +01:00
eprintln!("Got: {err:?}");
2024-06-09 10:21:09 +01:00
// NOTE: assertions for correct push are in on_push above
assert!(matches!(
err,
git::validation::positions::Error::NextBranchResetRequired(branch)
if branch == repo_config.branches().next()
));
}
#[test]
fn where_dev_branch_is_not_based_on_next_and_reset_of_next_fails_should_error() {
//given
let_assert!(Ok(fs) = kxio::fs::temp(), "temp fs");
let gitdir: config::GitDir = fs.base().to_path_buf().into();
let mut test_repository = git::repository::test(fs.clone());
let repo_config = given::a_repo_config();
test_repository.on_fetch(git::repository::OnFetch::new(
repo_config.branches().clone(),
gitdir.clone(),
fs.clone(),
|branches, gitdir, fs| {
// /--- 3 next
// 0 --- 1 main
// \--- 2 dev
// add a commit to main (0 -> 1)
then::create_a_commit_on_branch(fs, gitdir, &branches.main())?;
// add a commit to dev (1 -> 2)
then::create_a_commit_on_branch(fs, gitdir, &branches.dev())?;
// switch back to main (1)
then::git_switch(&branches.main(), gitdir)?;
// add a commit to next (1 -> 3)
then::create_a_commit_on_branch(fs, gitdir, &branches.next())?;
then::git_log_all(gitdir)?;
git::fetch::Result::Ok(())
},
));
// second fetch as prep to push
test_repository.on_fetch(git::repository::OnFetch::new(
repo_config.branches().clone(),
gitdir.clone(),
fs.clone(),
|_branches, _gitdir, _fs| {
// don't change anything
git::fetch::Result::Ok(())
},
));
test_repository.on_push(git::repository::OnPush::new(
repo_config.branches().clone(),
gitdir.clone(),
fs.clone(),
|_repo_details, _branch_name, _gitref, _force, _repo_branches, _gitdir, _fs| {
git::push::Result::Err(git::push::Error::Lock)
},
));
let_assert!(Ok(repository) = test_repository.open(&gitdir), "open repo");
let repo_details = given::repo_details(&fs).with_gitdir(gitdir.clone());
//when
let_assert!(
Err(err) = validate_positions(&repository, &repo_details, repo_config.clone()),
"validate"
);
//then
2024-06-13 20:00:04 +01:00
eprintln!("Got: {err:?}");
2024-06-09 10:21:09 +01:00
let_assert!(
Ok(sha_next) =
then::get_sha_for_branch(&fs, &gitdir, &repo_config.branches().next()),
"load next branch sha"
);
assert!(matches!(
err,
git::validation::positions::Error::FailedToResetBranch{branch, commit}
if branch == repo_config.branches().next() && commit.sha() == &sha_next
));
}
#[test]
fn where_branches_are_all_valid_should_return_positions() {
//given
let_assert!(Ok(fs) = kxio::fs::temp(), "temp fs");
let gitdir: config::GitDir = fs.base().to_path_buf().into();
let mut test_repository = git::repository::test(fs.clone());
let repo_config = given::a_repo_config();
test_repository.on_fetch(git::repository::OnFetch::new(
repo_config.branches().clone(),
gitdir.clone(),
fs.clone(),
|branches, gitdir, fs| {
// 0 --- 1 main
// \--- 2 next
// \--- dev
// add a commit to main (0 -> 1)
then::create_a_commit_on_branch(fs, gitdir, &branches.main())?;
// add a commit to next (1 -> 2)
then::create_a_commit_on_branch(fs, gitdir, &branches.next())?;
// add a commit to dev (2 -> 3)
then::create_a_commit_on_branch(fs, gitdir, &branches.dev())?;
then::git_log_all(gitdir)?;
git::fetch::Result::Ok(())
},
));
let_assert!(Ok(repository) = test_repository.open(&gitdir), "open repo");
let repo_details = given::repo_details(&fs).with_gitdir(gitdir.clone());
//when
let_assert!(
Ok(positions) = validate_positions(&repository, &repo_details, repo_config.clone()),
"validate"
);
//then
2024-06-13 20:00:04 +01:00
eprintln!("positions: {positions:#?}");
2024-06-09 10:21:09 +01:00
let_assert!(
Ok(main_sha) =
then::get_sha_for_branch(&fs, &gitdir, &repo_config.branches().main())
);
assert_eq!(positions.main.sha(), &main_sha);
let_assert!(
Ok(next_sha) =
then::get_sha_for_branch(&fs, &gitdir, &repo_config.branches().next())
);
assert_eq!(positions.next.sha(), &next_sha);
let_assert!(
Ok(dev_sha) = then::get_sha_for_branch(&fs, &gitdir, &repo_config.branches().dev())
);
assert_eq!(positions.dev.sha(), &dev_sha);
}
}
}