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

663 lines
27 KiB
Rust
Raw Normal View History

2024-06-09 10:21:09 +01:00
//
use crate::{
git::{
self,
repository::{
factory::RepositoryFactory as _,
open::otest::{OnFetch, OnPush},
Direction, RepositoryLike as _,
},
tests::{given, then},
validation::positions::validate,
},
GitDir, StoragePathType,
};
2024-06-09 10:21:09 +01:00
use assert2::let_assert;
mod repos {
2024-06-09 10:21:09 +01:00
use super::*;
#[test]
fn where_repo_has_no_push_remote() {
let_assert!(Ok(fs) = kxio::fs::temp(), "temp fs");
let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal);
let repo_details = given::repo_details(&fs).with_gitdir(gitdir);
let mut mock_open_repository = git::repository::open::mock();
mock_open_repository
.expect_find_default_remote()
.with(mockall::predicate::eq(Direction::Push))
.return_once(|_| None);
let mut repository_factory = git::repository::factory::mock();
repository_factory
.expect_open()
.return_once(move |_| Ok(mock_open_repository));
let_assert!(
Ok(open_repository) = repository_factory.open(&repo_details),
"open repo"
);
let result =
git::validation::remotes::validate_default_remotes(&*open_repository, &repo_details);
print!("{result:?}");
let_assert!(Err(err) = result);
2024-06-09 10:21:09 +01:00
assert!(matches!(
err,
git::validation::remotes::Error::NoDefaultPushRemote
2024-06-09 10:21:09 +01:00
));
}
}
2024-06-09 10:21:09 +01:00
mod positions {
use super::*;
mod validate {
2024-06-09 10:21:09 +01:00
use super::*;
#[test]
fn where_fetch_fails_should_error() {
let fs = given::a_filesystem();
let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal);
let repo_details = given::repo_details(&fs).with_gitdir(gitdir);
let mut mock_open_repository = git::repository::open::mock();
mock_open_repository
.expect_fetch()
.return_once(|_| Err(git::fetch::Error::TestFailureExpected));
let mut repository_factory = git::repository::factory::mock();
repository_factory
.expect_open()
.return_once(move |_| Ok(mock_open_repository));
2024-06-09 10:21:09 +01:00
let_assert!(
Ok(repository) = repository_factory.open(&repo_details),
"open repo"
2024-06-09 10:21:09 +01:00
);
let repo_config = given::a_repo_config();
let result = validate(&*repository, &repo_details, &repo_config);
println!("{result:?}");
let_assert!(Err(err) = result, "validate");
2024-06-09 10:21:09 +01:00
assert!(matches!(
err,
git::validation::positions::Error::Retryable(_)
2024-06-09 10:21:09 +01:00
));
}
#[test]
fn where_main_branch_is_missing_or_commit_log_is_empty_should_error() {
let fs = given::a_filesystem();
let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal);
let repo_details = given::repo_details(&fs).with_gitdir(gitdir);
2024-06-09 10:21:09 +01:00
let repo_config = given::a_repo_config();
let main_branch = repo_config.branches().main();
let mut mock_open_repository = git::repository::open::mock();
mock_open_repository.expect_fetch().return_once(|_| Ok(()));
mock_open_repository
.expect_commit_log()
.returning(move |branch_name, _| {
if branch_name == &main_branch {
Err(git::commit::log::Error::Gix {
branch: branch_name.clone(),
error: "foo".to_string(),
})
} else {
Ok(vec![])
}
});
let mut repository_factory = git::repository::factory::mock();
repository_factory
.expect_open()
.return_once(move |_| Ok(mock_open_repository));
2024-06-09 10:21:09 +01:00
let_assert!(
Ok(open_repository) = repository_factory.open(&repo_details),
"open repo"
2024-06-09 10:21:09 +01:00
);
let result = validate(&*open_repository, &repo_details, &repo_config);
println!("{result:?}");
2024-06-09 10:21:09 +01:00
assert!(matches!(
result,
Err(git::validation::positions::Error::Retryable(_))
2024-06-09 10:21:09 +01:00
));
}
#[test]
fn where_next_branch_is_missing_or_commit_log_is_empty_should_error() {
let fs = given::a_filesystem();
let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal);
let repo_details = given::repo_details(&fs).with_gitdir(gitdir);
2024-06-09 10:21:09 +01:00
let repo_config = given::a_repo_config();
let next_branch = repo_config.branches().next();
let mut mock_open_repository = git::repository::open::mock();
mock_open_repository.expect_fetch().return_once(|_| Ok(()));
mock_open_repository
.expect_commit_log()
.returning(move |branch_name, _| {
if branch_name == &next_branch {
Err(git::commit::log::Error::Gix {
branch: branch_name.clone(),
error: "foo".to_string(),
})
} else {
Ok(vec![given::a_commit()])
}
});
let mut repository_factory = git::repository::factory::mock();
repository_factory
.expect_open()
.return_once(move |_| Ok(mock_open_repository));
2024-06-09 10:21:09 +01:00
let_assert!(
Ok(open_repository) = repository_factory.open(&repo_details),
"open repo"
2024-06-09 10:21:09 +01:00
);
let result = validate(&*open_repository, &repo_details, &repo_config);
println!("{result:?}");
2024-06-09 10:21:09 +01:00
assert!(matches!(
result,
Err(git::validation::positions::Error::Retryable(_))
2024-06-09 10:21:09 +01:00
));
}
#[test]
fn where_dev_branch_is_missing_or_commit_log_is_empty_should_error() {
let fs = given::a_filesystem();
let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal);
let repo_details = given::repo_details(&fs).with_gitdir(gitdir);
2024-06-09 10:21:09 +01:00
let repo_config = given::a_repo_config();
let dev_branch = repo_config.branches().dev();
let mut mock_open_repository = git::repository::open::mock();
mock_open_repository.expect_fetch().return_once(|_| Ok(()));
mock_open_repository
.expect_commit_log()
.returning(move |branch_name, _| {
if branch_name == &dev_branch {
Err(git::commit::log::Error::Gix {
branch: branch_name.clone(),
error: "foo".to_string(),
})
} else {
Ok(vec![given::a_commit()])
}
});
let mut repository_factory = git::repository::factory::mock();
repository_factory
.expect_open()
.return_once(move |_| Ok(mock_open_repository));
2024-06-09 10:21:09 +01:00
let_assert!(
Ok(open_repository) = repository_factory.open(&repo_details),
"open repo"
2024-06-09 10:21:09 +01:00
);
let result = validate(&*open_repository, &repo_details, &repo_config);
println!("{result:?}");
2024-06-09 10:21:09 +01:00
assert!(matches!(
result,
Err(git::validation::positions::Error::Retryable(_))
2024-06-09 10:21:09 +01:00
));
}
#[test]
fn where_dev_branch_is_not_based_on_main_should_error() {
//given
let fs = given::a_filesystem();
let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal);
let forge_details = given::forge_details();
let mut test_repository = git::repository::test(fs.clone(), forge_details);
2024-06-09 10:21:09 +01:00
let repo_config = given::a_repo_config();
test_repository.on_fetch(OnFetch::new(
2024-06-09 10:21:09 +01:00
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(open_repository) = test_repository.open(&gitdir),
"open repo"
);
2024-06-09 10:21:09 +01:00
let repo_details = given::repo_details(&fs).with_gitdir(gitdir);
//when
let_assert!(
Err(err) = validate(&*open_repository, &repo_details, &repo_config),
2024-06-09 10:21:09 +01:00
"validate"
);
//then
assert!(matches!(
err,
git::validation::positions::Error::UserIntervention(_)
2024-06-09 10:21:09 +01:00
));
}
#[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 = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal);
let forge_details = given::forge_details();
let mut test_repository = git::repository::test(fs.clone(), forge_details);
2024-06-09 10:21:09 +01:00
let repo_config = given::a_repo_config();
test_repository.on_fetch(OnFetch::new(
2024-06-09 10:21:09 +01:00
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(OnFetch::new(
2024-06-09 10:21:09 +01:00
repo_config.branches().clone(),
gitdir.clone(),
fs.clone(),
|_branches, _gitdir, _fs| {
// don't change anything
git::fetch::Result::Ok(())
},
));
test_repository.on_push(OnPush::new(
2024-06-09 10:21:09 +01:00
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(open_repository) = test_repository.open(&gitdir),
"open repo"
);
2024-06-09 10:21:09 +01:00
let repo_details = given::repo_details(&fs).with_gitdir(gitdir);
//when
let_assert!(
Err(err) = validate(&*open_repository, &repo_details, &repo_config),
2024-06-09 10:21:09 +01:00
"validate"
);
//then
println!("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::Retryable(_)
2024-06-09 10:21:09 +01:00
));
}
#[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 = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal);
let forge_details = given::forge_details();
let mut test_repository = git::repository::test(fs.clone(), forge_details);
2024-06-09 10:21:09 +01:00
let repo_config = given::a_repo_config();
test_repository.on_fetch(OnFetch::new(
2024-06-09 10:21:09 +01:00
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(OnFetch::new(
2024-06-09 10:21:09 +01:00
repo_config.branches().clone(),
gitdir.clone(),
fs.clone(),
|_branches, _gitdir, _fs| {
// don't change anything
git::fetch::Result::Ok(())
},
));
test_repository.on_push(OnPush::new(
2024-06-09 10:21:09 +01:00
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(open_repository) = test_repository.open(&gitdir),
"open repo"
);
2024-06-09 10:21:09 +01:00
let repo_details = given::repo_details(&fs).with_gitdir(gitdir.clone());
//when
let_assert!(
Err(err) = validate(&*open_repository, &repo_details, &repo_config),
2024-06-09 10:21:09 +01:00
"validate"
);
//then
println!("Got: {err:?}");
2024-06-09 10:21:09 +01:00
let_assert!(
Ok(_) = then::get_sha_for_branch(&fs, &gitdir, &repo_config.branches().next()),
2024-06-09 10:21:09 +01:00
"load next branch sha"
);
assert!(matches!(
err,
git::validation::positions::Error::NonRetryable(_)
2024-06-09 10:21:09 +01:00
));
}
#[test]
#[allow(clippy::expect_used)]
fn where_dev_branch_is_on_main_and_next_is_not_should_reset_next_branch_to_main_and_retryable_error(
) {
2024-06-09 10:21:09 +01:00
//given
let_assert!(Ok(fs) = kxio::fs::temp(), "temp fs");
let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal);
let forge_details = given::forge_details();
let mut test_repository = git::repository::test(fs.clone(), forge_details);
2024-06-09 10:21:09 +01:00
let repo_config = given::a_repo_config();
test_repository.on_fetch(OnFetch::new(
2024-06-09 10:21:09 +01:00
repo_config.branches().clone(),
gitdir.clone(),
fs.clone(),
|branches, gitdir, fs| {
// /--- 3 next
// 0 --- 1 main & dev
2024-06-09 10:21:09 +01:00
// add a commit to main (0 -> 1)
then::create_a_commit_on_branch(fs, gitdir, &branches.main())?;
// create dev branch on main (1)
then::git_switch(&branches.dev(), gitdir)?;
2024-06-09 10:21:09 +01:00
// 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(OnFetch::new(
2024-06-09 10:21:09 +01:00
repo_config.branches().clone(),
gitdir.clone(),
fs.clone(),
|_branches, _gitdir, _fs| {
// don't change anything
git::fetch::Result::Ok(())
},
));
test_repository.on_push(OnPush::new(
2024-06-09 10:21:09 +01:00
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(open_repository) = test_repository.open(&gitdir),
"open repo"
);
2024-06-09 10:21:09 +01:00
let repo_details = given::repo_details(&fs).with_gitdir(gitdir);
//when
let_assert!(
Err(err) = validate(&*open_repository, &repo_details, &repo_config),
2024-06-09 10:21:09 +01:00
"validate"
);
//then
println!("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::Retryable(_)
2024-06-09 10:21:09 +01:00
));
}
#[test]
#[allow(clippy::expect_used)]
fn where_dev_branch_is_not_based_on_next_should_reset_next_branch_to_next_commit_on_dev_and_retryable_error(
) {
2024-06-09 10:21:09 +01:00
//given
let_assert!(Ok(fs) = kxio::fs::temp(), "temp fs");
let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal);
let forge_details = given::forge_details();
let mut test_repository = git::repository::test(fs.clone(), forge_details);
2024-06-09 10:21:09 +01:00
let repo_config = given::a_repo_config();
test_repository.on_fetch(OnFetch::new(
2024-06-09 10:21:09 +01:00
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(OnFetch::new(
2024-06-09 10:21:09 +01:00
repo_config.branches().clone(),
gitdir.clone(),
fs.clone(),
|_branches, _gitdir, _fs| {
// don't change anything
git::fetch::Result::Ok(())
},
));
test_repository.on_push(OnPush::new(
2024-06-09 10:21:09 +01:00
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_dev = then::get_sha_for_branch(fs, gitdir, &repo_branches.dev())?;
assert_eq!(
gitref,
&git::GitRef::from(sha_dev),
"should reset to the sha of the next commit on 'dev' 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(())
2024-06-09 10:21:09 +01:00
},
));
let_assert!(
Ok(open_repository) = test_repository.open(&gitdir),
"open repo"
);
2024-06-09 10:21:09 +01:00
let repo_details = given::repo_details(&fs).with_gitdir(gitdir.clone());
//when
let_assert!(
Ok((positions, _git_log)) =
validate(&*open_repository, &repo_details, &repo_config),
2024-06-09 10:21:09 +01:00
"validate"
);
//then
println!("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())
2024-06-09 10:21:09 +01:00
);
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);
2024-06-09 10:21:09 +01:00
}
#[test]
fn where_branches_are_all_valid_should_return_positions() {
//given
let_assert!(Ok(fs) = kxio::fs::temp(), "temp fs");
let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal);
let forge_details = given::forge_details();
let mut test_repository = git::repository::test(fs.clone(), forge_details);
2024-06-09 10:21:09 +01:00
let repo_config = given::a_repo_config();
test_repository.on_fetch(OnFetch::new(
2024-06-09 10:21:09 +01:00
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(open_repository) = test_repository.open(&gitdir),
"open repo"
);
2024-06-09 10:21:09 +01:00
let repo_details = given::repo_details(&fs).with_gitdir(gitdir.clone());
//when
let_assert!(
Ok((positions, _git_log)) =
validate(&*open_repository, &repo_details, &repo_config),
2024-06-09 10:21:09 +01:00
"validate"
);
//then
println!("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);
}
}
}