537 lines
16 KiB
Rust
537 lines
16 KiB
Rust
#![allow(dead_code)]
|
|
|
|
use crate as git;
|
|
use git_next_config as config;
|
|
|
|
mod commit {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn should_return_sha() {
|
|
let sha = given::a_commit_sha();
|
|
let commit = given::a_commit_with_sha(&sha);
|
|
|
|
assert_eq!(commit.sha(), &sha);
|
|
}
|
|
#[test]
|
|
fn should_return_message() {
|
|
let message = given::a_commit_message();
|
|
let commit = given::a_commit_with_message(&message);
|
|
|
|
assert_eq!(commit.message(), &message);
|
|
}
|
|
#[test]
|
|
fn should_convert_from_push() {
|
|
let sha = given::a_commit_sha();
|
|
let message = given::a_commit_message();
|
|
let push = given::a_webhook_push(&sha, &message);
|
|
let commit = git::Commit::from(push);
|
|
|
|
let expected = git::Commit::new(
|
|
git::commit::Sha::new(sha.to_string()),
|
|
git::commit::Message::new(message.to_string()),
|
|
);
|
|
|
|
assert_eq!(commit, expected);
|
|
}
|
|
}
|
|
mod generation {
|
|
use crate::Generation;
|
|
|
|
#[test]
|
|
fn should_increment() {
|
|
let mut g = Generation::new();
|
|
assert_eq!(g.to_string(), "0");
|
|
|
|
g.inc();
|
|
|
|
assert_eq!(g.to_string(), "1");
|
|
}
|
|
}
|
|
mod gitref {
|
|
use crate::{commit, Commit, GitRef};
|
|
#[test]
|
|
fn should_convert_from_commit() {
|
|
let commit = Commit::new(
|
|
commit::Sha::new("sha".to_string()),
|
|
commit::Message::new("message".to_string()),
|
|
);
|
|
let gitref = GitRef::from(commit);
|
|
|
|
assert_eq!(gitref.to_string(), "sha");
|
|
}
|
|
}
|
|
mod gitremote {
|
|
use git_next_config::{Hostname, RepoPath};
|
|
|
|
use crate::GitRemote;
|
|
|
|
#[test]
|
|
fn should_return_hostname() {
|
|
let host = Hostname::new("localhost".to_string());
|
|
let repo_path = RepoPath::new("kemitix/git-next".to_string());
|
|
let gr = GitRemote::new(host.clone(), repo_path);
|
|
|
|
assert_eq!(gr.host(), &host);
|
|
}
|
|
#[test]
|
|
fn should_return_repo_path() {
|
|
let host = Hostname::new("localhost".to_string());
|
|
let repo_path = RepoPath::new("kemitix/git-next".to_string());
|
|
let gr = GitRemote::new(host, repo_path.clone());
|
|
|
|
assert_eq!(gr.repo_path(), &repo_path);
|
|
}
|
|
}
|
|
mod push {
|
|
use super::*;
|
|
use crate::GitRef;
|
|
|
|
#[test]
|
|
fn force_no_should_display() {
|
|
assert_eq!(git::push::Force::No.to_string(), "fast-forward")
|
|
}
|
|
|
|
#[test]
|
|
fn force_from_should_display() {
|
|
let sha = given::a_name();
|
|
let commit = given::a_commit_with_sha(&git::commit::Sha::new(sha.clone()));
|
|
assert_eq!(
|
|
git::push::Force::From(GitRef::from(commit)).to_string(),
|
|
format!("force-if-from:{sha}")
|
|
)
|
|
}
|
|
|
|
mod reset {
|
|
use super::*;
|
|
use crate::tests::given;
|
|
use assert2::let_assert;
|
|
|
|
#[test]
|
|
fn should_perform_a_fetch_then_push() {
|
|
let mut open_repository = git::repository::open::mock();
|
|
let mut seq = mockall::Sequence::new();
|
|
open_repository
|
|
.expect_fetch()
|
|
.times(1)
|
|
.in_sequence(&mut seq)
|
|
.returning(|| Ok(()));
|
|
open_repository
|
|
.expect_push()
|
|
.times(1)
|
|
.in_sequence(&mut seq)
|
|
.returning(|_repo_details, _branch_name, _gitref, _force| Ok(()));
|
|
|
|
let fs = given::a_filesystem();
|
|
let repo_details = given::repo_details(&fs);
|
|
let branch_name = &repo_details.branch;
|
|
let commit = given::a_commit();
|
|
let gitref = GitRef::from(commit);
|
|
let_assert!(
|
|
Ok(_) = git::push::reset(
|
|
&*open_repository,
|
|
&repo_details,
|
|
branch_name,
|
|
&gitref,
|
|
&git::push::Force::No
|
|
)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
mod repo_details {
|
|
|
|
use std::{collections::BTreeMap, path::PathBuf};
|
|
|
|
use git_next_config::{
|
|
ForgeAlias, ForgeConfig, ForgeType, GitDir, Hostname, RepoAlias, RepoPath, ServerRepoConfig,
|
|
};
|
|
use secrecy::ExposeSecret;
|
|
|
|
use crate::{Generation, GitRemote, RepoDetails};
|
|
|
|
#[test]
|
|
fn should_return_origin() {
|
|
let rd = RepoDetails::new(
|
|
Generation::new(),
|
|
&RepoAlias::new("foo"),
|
|
&ServerRepoConfig::new(
|
|
"repo".to_string(),
|
|
"branch".to_string(),
|
|
None,
|
|
None,
|
|
None,
|
|
None,
|
|
),
|
|
&ForgeAlias::new("default".to_string()),
|
|
&ForgeConfig::new(
|
|
ForgeType::MockForge,
|
|
"host".to_string(),
|
|
"user".to_string(),
|
|
"token".to_string(),
|
|
BTreeMap::new(),
|
|
),
|
|
GitDir::from(PathBuf::default().join("foo")),
|
|
);
|
|
|
|
assert_eq!(
|
|
rd.origin().expose_secret(),
|
|
"https://user:token@host/repo.git"
|
|
);
|
|
}
|
|
#[test]
|
|
fn should_return_git_remote() {
|
|
let rd = RepoDetails::new(
|
|
Generation::new(),
|
|
&RepoAlias::new("foo"),
|
|
&ServerRepoConfig::new(
|
|
"user/repo".to_string(),
|
|
"branch".to_string(),
|
|
None,
|
|
None,
|
|
None,
|
|
None,
|
|
),
|
|
&ForgeAlias::new("default".to_string()),
|
|
&ForgeConfig::new(
|
|
ForgeType::MockForge,
|
|
"host".to_string(),
|
|
"user".to_string(),
|
|
"token".to_string(),
|
|
BTreeMap::new(),
|
|
),
|
|
GitDir::from(PathBuf::default().join("foo")),
|
|
);
|
|
|
|
assert_eq!(
|
|
rd.git_remote(),
|
|
GitRemote::new(
|
|
Hostname::new("host".to_string()),
|
|
RepoPath::new("user/repo".to_string())
|
|
)
|
|
);
|
|
}
|
|
}
|
|
pub mod given {
|
|
use std::path::PathBuf;
|
|
|
|
//
|
|
use crate::{
|
|
self as git,
|
|
repository::{FakeOpenRepository, FakeRepository},
|
|
tests::given,
|
|
};
|
|
use config::{
|
|
BranchName, ForgeAlias, ForgeConfig, ForgeType, GitDir, RepoAlias, RepoBranches,
|
|
RepoConfig, ServerRepoConfig,
|
|
};
|
|
use git_next_config as config;
|
|
|
|
use crate::RepoDetails;
|
|
|
|
pub fn repo_branches() -> RepoBranches {
|
|
RepoBranches::new(
|
|
format!("main-{}", a_name()),
|
|
format!("next-{}", a_name()),
|
|
format!("dev-{}", a_name()),
|
|
)
|
|
}
|
|
|
|
pub fn a_forge_alias() -> ForgeAlias {
|
|
ForgeAlias::new(a_name())
|
|
}
|
|
|
|
pub fn a_repo_alias() -> RepoAlias {
|
|
RepoAlias::new(a_name())
|
|
}
|
|
|
|
pub fn a_pathbuf() -> PathBuf {
|
|
PathBuf::from(given::a_name())
|
|
}
|
|
|
|
pub fn a_name() -> String {
|
|
use rand::Rng;
|
|
use std::iter;
|
|
|
|
fn generate(len: usize) -> String {
|
|
const CHARSET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
|
let mut rng = rand::thread_rng();
|
|
let one_char = || CHARSET[rng.gen_range(0..CHARSET.len())] as char;
|
|
iter::repeat_with(one_char).take(len).collect()
|
|
}
|
|
generate(5)
|
|
}
|
|
|
|
pub fn a_branch_name() -> BranchName {
|
|
BranchName::new(a_name())
|
|
}
|
|
|
|
pub fn a_git_dir(fs: &kxio::fs::FileSystem) -> GitDir {
|
|
let dir_name = a_name();
|
|
let dir = fs.base().join(dir_name);
|
|
GitDir::new(dir)
|
|
}
|
|
|
|
pub fn a_forge_config() -> ForgeConfig {
|
|
ForgeConfig::new(
|
|
ForgeType::MockForge,
|
|
a_name(),
|
|
a_name(),
|
|
a_name(),
|
|
Default::default(), // no repos
|
|
)
|
|
}
|
|
|
|
pub fn a_server_repo_config() -> ServerRepoConfig {
|
|
let main = a_branch_name().unwrap();
|
|
let next = a_branch_name().unwrap();
|
|
let dev = a_branch_name().unwrap();
|
|
ServerRepoConfig::new(
|
|
format!("{}/{}", a_name(), a_name()),
|
|
main.clone(),
|
|
None,
|
|
Some(main),
|
|
Some(next),
|
|
Some(dev),
|
|
)
|
|
}
|
|
|
|
pub fn a_repo_config() -> RepoConfig {
|
|
RepoConfig::new(given::repo_branches(), config::RepoConfigSource::Repo)
|
|
}
|
|
|
|
pub fn a_commit() -> git::Commit {
|
|
git::Commit::new(a_commit_sha(), a_commit_message())
|
|
}
|
|
|
|
pub fn a_commit_with_message(message: &git::commit::Message) -> git::Commit {
|
|
git::Commit::new(a_commit_sha(), message.to_owned())
|
|
}
|
|
|
|
pub fn a_commit_with_sha(sha: &git::commit::Sha) -> git::Commit {
|
|
git::Commit::new(sha.to_owned(), a_commit_message())
|
|
}
|
|
|
|
pub fn a_commit_message() -> git::commit::Message {
|
|
git::commit::Message::new(a_name())
|
|
}
|
|
|
|
pub fn a_commit_sha() -> git::commit::Sha {
|
|
git::commit::Sha::new(a_name())
|
|
}
|
|
|
|
pub fn a_webhook_push(
|
|
sha: &git::commit::Sha,
|
|
message: &git::commit::Message,
|
|
) -> config::webhook::Push {
|
|
let branch = a_branch_name();
|
|
config::webhook::Push::new(branch, sha.to_string(), message.to_string())
|
|
}
|
|
|
|
pub fn a_filesystem() -> kxio::fs::FileSystem {
|
|
kxio::fs::temp().unwrap_or_else(|e| panic!("{}", e))
|
|
}
|
|
|
|
pub fn repo_details(fs: &kxio::fs::FileSystem) -> git::RepoDetails {
|
|
let generation = git::Generation::new();
|
|
let repo_alias = a_repo_alias();
|
|
let server_repo_config = a_server_repo_config();
|
|
let forge_alias = a_forge_alias();
|
|
let forge_config = a_forge_config();
|
|
let gitdir = a_git_dir(fs);
|
|
RepoDetails::new(
|
|
generation,
|
|
&repo_alias,
|
|
&server_repo_config,
|
|
&forge_alias,
|
|
&forge_config,
|
|
gitdir,
|
|
)
|
|
}
|
|
|
|
pub fn an_open_repository(
|
|
fs: &kxio::fs::FileSystem,
|
|
) -> (FakeOpenRepository, GitDir, FakeRepository) {
|
|
let mut mock = git::repository::fake();
|
|
let gitdir = a_git_dir(fs);
|
|
let or = mock.given_can_be_opened(&gitdir);
|
|
(or, gitdir, mock)
|
|
}
|
|
|
|
pub fn a_git_remote() -> git::GitRemote {
|
|
git::GitRemote::new(
|
|
config::Hostname::new(given::a_name()),
|
|
config::RepoPath::new(given::a_name()),
|
|
)
|
|
}
|
|
}
|
|
pub mod then {
|
|
use std::path::{Path, PathBuf};
|
|
|
|
type TestResult = Result<(), Box<dyn std::error::Error>>;
|
|
|
|
use super::*;
|
|
|
|
pub fn commit_named_file_to_branch(
|
|
file_name: &Path,
|
|
contents: &str,
|
|
fs: &kxio::fs::FileSystem,
|
|
gitdir: &config::GitDir,
|
|
branch_name: &config::BranchName,
|
|
) -> TestResult {
|
|
// git checkout ${branch_name}
|
|
git_checkout_new_branch(branch_name, gitdir)?;
|
|
// echo ${word} > file-${word}
|
|
let pathbuf = PathBuf::from(gitdir);
|
|
let file = fs.base().join(pathbuf).join(file_name);
|
|
#[allow(clippy::expect_used)]
|
|
fs.file_write(&file, contents)?;
|
|
// git add ${file}
|
|
git_add_file(gitdir, &file)?;
|
|
// git commit -m"Added ${file}"
|
|
git_commit(gitdir, &file)?;
|
|
|
|
then::push_branch(fs, gitdir, branch_name)?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub fn create_a_commit_on_branch(
|
|
fs: &kxio::fs::FileSystem,
|
|
gitdir: &config::GitDir,
|
|
branch_name: &config::BranchName,
|
|
) -> TestResult {
|
|
// git checkout ${branch_name}
|
|
git_checkout_new_branch(branch_name, gitdir)?;
|
|
// echo ${word} > file-${word}
|
|
let word = given::a_name();
|
|
let pathbuf = PathBuf::from(gitdir);
|
|
let file = fs.base().join(pathbuf).join(&word);
|
|
fs.file_write(&file, &word)?;
|
|
// git add ${file}
|
|
git_add_file(gitdir, &file)?;
|
|
// git commit -m"Added ${file}"
|
|
git_commit(gitdir, &file)?;
|
|
|
|
then::push_branch(fs, gitdir, branch_name)?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn push_branch(
|
|
fs: &kxio::fs::FileSystem,
|
|
gitdir: &config::GitDir,
|
|
branch_name: &config::BranchName,
|
|
) -> TestResult {
|
|
let gitrefs = fs
|
|
.base()
|
|
.join(gitdir.to_path_buf())
|
|
.join(".git")
|
|
.join("refs");
|
|
let local_branch = gitrefs.join("heads").join(branch_name.to_string().as_str());
|
|
let origin_heads = gitrefs.join("remotes").join("origin");
|
|
let remote_branch = origin_heads.join(branch_name.to_string().as_str());
|
|
let contents = fs.file_read_to_string(&local_branch)?;
|
|
fs.dir_create_all(&origin_heads)?;
|
|
fs.file_write(&remote_branch, &contents)?;
|
|
Ok(())
|
|
}
|
|
|
|
pub fn git_checkout_new_branch(
|
|
branch_name: &git_next_config::BranchName,
|
|
gitdir: &git_next_config::GitDir,
|
|
) -> TestResult {
|
|
exec(
|
|
format!("git checkout -b {}", branch_name),
|
|
std::process::Command::new("/usr/bin/git")
|
|
.current_dir(gitdir.to_path_buf())
|
|
.args(["checkout", "-b", branch_name.to_string().as_str()])
|
|
.output(),
|
|
)?;
|
|
Ok(())
|
|
}
|
|
|
|
pub fn git_switch(
|
|
branch_name: &git_next_config::BranchName,
|
|
gitdir: &git_next_config::GitDir,
|
|
) -> TestResult {
|
|
exec(
|
|
format!("git switch {}", branch_name),
|
|
std::process::Command::new("/usr/bin/git")
|
|
.current_dir(gitdir.to_path_buf())
|
|
.args(["switch", branch_name.to_string().as_str()])
|
|
.output(),
|
|
)
|
|
}
|
|
|
|
fn exec(label: String, output: Result<std::process::Output, std::io::Error>) -> TestResult {
|
|
eprintln!("== {label}");
|
|
match output {
|
|
Ok(output) => {
|
|
eprintln!(
|
|
"\nstdout:\n{}",
|
|
String::from_utf8_lossy(output.stdout.as_slice())
|
|
);
|
|
eprintln!(
|
|
"\nstderr:\n{}",
|
|
String::from_utf8_lossy(output.stderr.as_slice())
|
|
);
|
|
eprintln!("=============================");
|
|
Ok(())
|
|
}
|
|
Err(err) => {
|
|
eprintln!("ERROR: {err:#?}");
|
|
Ok(Err(err)?)
|
|
}
|
|
}
|
|
}
|
|
|
|
fn git_add_file(gitdir: &git_next_config::GitDir, file: &Path) -> TestResult {
|
|
exec(
|
|
format!("git add {file:?}"),
|
|
std::process::Command::new("/usr/bin/git")
|
|
.current_dir(gitdir.to_path_buf())
|
|
.args(["add", file.display().to_string().as_str()])
|
|
.output(),
|
|
)
|
|
}
|
|
|
|
fn git_commit(gitdir: &git_next_config::GitDir, file: &Path) -> TestResult {
|
|
exec(
|
|
format!(r#"git commit -m"Added {file:?}""#),
|
|
std::process::Command::new("/usr/bin/git")
|
|
.current_dir(gitdir.to_path_buf())
|
|
.args([
|
|
"commit",
|
|
format!(r#"-m"Added {}"#, file.display().to_string().as_str()).as_str(),
|
|
])
|
|
.output(),
|
|
)
|
|
}
|
|
|
|
pub fn git_log_all(gitdir: &config::GitDir) -> TestResult {
|
|
exec(
|
|
"git log --all --oneline --decorate --graph".to_string(),
|
|
std::process::Command::new("/usr/bin/git")
|
|
.current_dir(gitdir.to_path_buf())
|
|
.args(["log", "--all", "--oneline", "--decorate", "--graph"])
|
|
.output(),
|
|
)
|
|
}
|
|
|
|
pub fn get_sha_for_branch(
|
|
fs: &kxio::fs::FileSystem,
|
|
gitdir: &git_next_config::GitDir,
|
|
branch_name: &git_next_config::BranchName,
|
|
) -> Result<git::commit::Sha, Box<dyn std::error::Error>> {
|
|
let main_ref = fs
|
|
.base()
|
|
.join(gitdir.to_path_buf())
|
|
.join(".git")
|
|
.join("refs")
|
|
.join("heads")
|
|
.join(branch_name.to_string().as_str());
|
|
let sha = fs.file_read_to_string(&main_ref)?;
|
|
Ok(git::commit::Sha::new(sha.trim().to_string()))
|
|
}
|
|
}
|