Compare commits

...

7 commits

Author SHA1 Message Date
df352443b7 feat: GitDir tracks when repo is cloned by git-next
All checks were successful
ci/woodpecker/cron/cron-docker-builder Pipeline was successful
ci/woodpecker/cron/push-next Pipeline was successful
ci/woodpecker/cron/tag-created Pipeline was successful
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/push-next Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
Rust / build (push) Successful in 1m21s
2024-07-06 15:08:13 +01:00
425241196d chore: local dev used debug logging
All checks were successful
Rust / build (push) Successful in 1m19s
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/push-next Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
2024-07-06 14:23:04 +01:00
4e60be61f7 refactor: extract git::repository::factory module
All checks were successful
Rust / build (push) Successful in 1m29s
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/push-next Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
2024-07-05 20:31:16 +01:00
5ab075c181 refactor: split git::repository::tests module
All checks were successful
Rust / build (push) Successful in 1m19s
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/push-next Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
2024-07-05 20:12:17 +01:00
56756cab70 chore: bacon treats clippy warnings as errors
All checks were successful
Rust / build (push) Successful in 1m22s
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/push-next Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
2024-07-05 20:12:17 +01:00
d9feaeaa7b chore: remove unused FakeOpenRepository
All checks were successful
Rust / build (push) Successful in 1m25s
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/push-next Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
2024-07-05 20:12:17 +01:00
2e374d317a refactor: split git::repository::open::tests module
All checks were successful
Rust / build (push) Successful in 1m12s
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/push-next Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
2024-07-05 20:12:17 +01:00
35 changed files with 761 additions and 810 deletions

View file

@ -13,5 +13,5 @@ debug = 0
strip = "debuginfo" strip = "debuginfo"
[env] [env]
RUST_LOG = "hyper=warn,info" RUST_LOG = "hyper=warn,debug"
RUSTFLAGS = "--cfg tokio_unstable" RUSTFLAGS = "--cfg tokio_unstable"

View file

@ -24,7 +24,15 @@ command = ["cargo", "check", "--all-targets", "--color", "always"]
need_stdout = false need_stdout = false
[jobs.clippy] [jobs.clippy]
command = ["cargo", "clippy", "--all-targets", "--color", "always"] command = [
"cargo",
"clippy",
"--all-targets",
"--color",
"always",
"--",
"-Dwarnings",
]
need_stdout = false need_stdout = false
[jobs.test] [jobs.test]

View file

@ -1,20 +1,43 @@
// //
use std::path::PathBuf; use std::path::PathBuf;
crate::newtype!(GitDir: PathBuf, Default: "The path to the directory containing the git repository."); use derive_more::Deref;
/// The path to the directory containing the git repository.
#[derive(
Clone,
Debug,
derive_more::From,
PartialEq,
Eq,
PartialOrd,
Ord,
derive_more::AsRef,
derive_more::Constructor,
)]
pub struct GitDir {
pathbuf: PathBuf,
/// Whether the directory is under the control of git-next (Internal) or not (External).
storage_path_type: StoragePathType,
}
impl GitDir { impl GitDir {
pub const fn pathbuf(&self) -> &PathBuf { pub const fn pathbuf(&self) -> &PathBuf {
&self.0 &self.pathbuf
}
pub const fn storage_path_type(&self) -> &StoragePathType {
&self.storage_path_type
} }
} }
impl std::fmt::Display for GitDir { impl std::fmt::Display for GitDir {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", &self.0.display()) write!(f, "{}", &self.pathbuf.display())
} }
} }
impl From<&str> for GitDir { impl Deref for GitDir {
fn from(value: &str) -> Self { type Target = PathBuf;
Self(value.into())
fn deref(&self) -> &Self::Target {
&self.pathbuf
} }
} }
impl From<&GitDir> for PathBuf { impl From<&GitDir> for PathBuf {
@ -22,3 +45,9 @@ impl From<&GitDir> for PathBuf {
value.to_path_buf() value.to_path_buf()
} }
} }
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum StoragePathType {
Internal,
External,
}

View file

@ -30,6 +30,7 @@ pub use forge_config::ForgeConfig;
pub use forge_details::ForgeDetails; pub use forge_details::ForgeDetails;
pub use forge_type::ForgeType; pub use forge_type::ForgeType;
pub use git_dir::GitDir; pub use git_dir::GitDir;
pub use git_dir::StoragePathType;
pub use host_name::Hostname; pub use host_name::Hostname;
pub use registered_webhook::RegisteredWebhook; pub use registered_webhook::RegisteredWebhook;
pub use repo_alias::RepoAlias; pub use repo_alias::RepoAlias;

View file

@ -35,7 +35,10 @@ impl ServerRepoConfig {
} }
pub fn gitdir(&self) -> Option<GitDir> { pub fn gitdir(&self) -> Option<GitDir> {
self.gitdir.clone().map(GitDir::from) self.gitdir
.clone()
// Provenance is external as the gitdir is only used to specify non-internal paths
.map(|dir| GitDir::new(dir, crate::git_dir::StoragePathType::External))
} }
/// Returns a RepoConfig from the server configuration if ALL THREE branches were provided /// Returns a RepoConfig from the server configuration if ALL THREE branches were provided

View file

@ -90,7 +90,10 @@ mod server_repo_config {
assert_eq!( assert_eq!(
src.gitdir(), src.gitdir(),
Some(GitDir::new(PathBuf::default().join(gitdir))) Some(GitDir::new(
PathBuf::default().join(gitdir),
git_dir::StoragePathType::External
))
); );
} }
} }
@ -382,47 +385,29 @@ mod gitdir {
#[test] #[test]
fn should_return_pathbuf() { fn should_return_pathbuf() {
let pathbuf = PathBuf::default().join(given::a_name()); let pathbuf = PathBuf::default().join(given::a_name());
let gitdir = GitDir::new(&pathbuf); let gitdir = GitDir::new(pathbuf.clone(), git_dir::StoragePathType::Internal);
let result = gitdir.unwrap(); let result = gitdir.to_path_buf();
assert_eq!(result, pathbuf); assert_eq!(result, pathbuf);
} }
#[test] #[test]
fn should_display() { fn should_display() {
let pathbuf = PathBuf::default().join("foo"); let pathbuf = PathBuf::default().join("foo");
let gitdir = GitDir::new(pathbuf); let gitdir = GitDir::new(pathbuf, git_dir::StoragePathType::Internal);
let result = gitdir.to_string(); let result = gitdir.to_string();
assert_eq!(result, "foo"); assert_eq!(result, "foo");
} }
#[test] #[test]
fn should_convert_from_str() {
let path = given::a_name();
let pathbuf = PathBuf::default().join(path.clone());
let gitdir: GitDir = path.as_str().into();
assert_eq!(gitdir, GitDir::new(pathbuf));
}
#[test]
fn should_convert_to_pathbuf_from_ref() { fn should_convert_to_pathbuf_from_ref() {
let path = given::a_name(); let path = given::a_name();
let pathbuf = PathBuf::default().join(path.clone()); let pathbuf = PathBuf::default().join(path);
let gitdir: GitDir = path.as_str().into(); let gitdir: GitDir = GitDir::new(pathbuf.clone(), git_dir::StoragePathType::Internal);
let result: PathBuf = (&gitdir).into(); let result: PathBuf = (&gitdir).into();
assert_eq!(result, pathbuf);
}
#[test]
fn should_convert_to_pathbuf_from_inst() {
let path = given::a_name();
let pathbuf = PathBuf::default().join(path.clone());
let gitdir: GitDir = path.as_str().into();
let result: PathBuf = gitdir.into();
assert_eq!(result, pathbuf); assert_eq!(result, pathbuf);
} }
} }
@ -503,7 +488,7 @@ mod server {
let branch = server_repo_config.branch(); let branch = server_repo_config.branch();
let gitdir = server_repo_config let gitdir = server_repo_config
.gitdir() .gitdir()
.map(|gitdir| gitdir.unwrap()) .map(|gitdir| gitdir.to_path_buf())
.map(|pathbuf| pathbuf.display().to_string()) .map(|pathbuf| pathbuf.display().to_string())
.map(|gitdir| format!(r#", gitdir = "{gitdir}" "#)) .map(|gitdir| format!(r#", gitdir = "{gitdir}" "#))
.unwrap_or_default(); .unwrap_or_default();

View file

@ -734,7 +734,7 @@ mod forgejo {
pub fn a_git_dir(fs: &kxio::fs::FileSystem) -> config::GitDir { pub fn a_git_dir(fs: &kxio::fs::FileSystem) -> config::GitDir {
let dir_name = a_name(); let dir_name = a_name();
let dir = fs.base().join(dir_name); let dir = fs.base().join(dir_name);
config::GitDir::new(dir) config::GitDir::new(dir, config::git_dir::StoragePathType::Internal)
} }
pub fn a_forge_config() -> config::ForgeConfig { pub fn a_forge_config() -> config::ForgeConfig {

View file

@ -508,6 +508,8 @@ mod github {
mod given { mod given {
use std::path::PathBuf;
use git_next_config::{server::Webhook, WebhookId}; use git_next_config::{server::Webhook, WebhookId};
use kxio::network::{MockNetwork, StatusCode}; use kxio::network::{MockNetwork, StatusCode};
use rand::RngCore; use rand::RngCore;
@ -624,7 +626,10 @@ mod github {
a_name(), a_name(),
BTreeMap::default(), BTreeMap::default(),
), ),
GitDir::default(), GitDir::new(
PathBuf::default(),
config::git_dir::StoragePathType::External,
),
) )
} }

View file

@ -37,6 +37,9 @@ fn given_repo_details(forge_type: config::ForgeType) -> RepoDetails {
1, 1,
config::RepoConfigSource::Repo, config::RepoConfigSource::Repo,
)), )),
config::GitDir::new(fs.base()), config::GitDir::new(
fs.base().to_path_buf(),
config::git_dir::StoragePathType::Internal,
),
) )
} }

View file

@ -7,7 +7,7 @@ use git_next_config as config;
use super::{Generation, GitRemote}; use super::{Generation, GitRemote};
/// The derived information about a repo, used to interact with it /// The derived information about a repo, used to interact with it
#[derive(Clone, Default, Debug, derive_more::Display, derive_with::With)] #[derive(Clone, Debug, derive_more::Display, derive_with::With)]
#[display("gen-{}:{}:{}/{}", generation, forge.forge_type(), forge.forge_alias(), repo_alias )] #[display("gen-{}:{}:{}/{}", generation, forge.forge_type(), forge.forge_alias(), repo_alias )]
pub struct RepoDetails { pub struct RepoDetails {
pub generation: Generation, pub generation: Generation,

View file

@ -0,0 +1,51 @@
use super::RepoDetails;
use super::Result;
pub use crate::repository::open::OpenRepositoryLike;
use crate::repository::RealOpenRepository;
use derive_more::Deref as _;
use git_next_config::GitDir;
use std::sync::{atomic::AtomicBool, Arc, RwLock};
#[mockall::automock]
pub trait RepositoryFactory: std::fmt::Debug + Sync + Send {
fn duplicate(&self) -> Box<dyn RepositoryFactory>;
fn open(&self, gitdir: &GitDir) -> Result<Box<dyn OpenRepositoryLike>>;
fn git_clone(&self, repo_details: &RepoDetails) -> Result<Box<dyn OpenRepositoryLike>>;
}
pub fn real() -> Box<dyn RepositoryFactory> {
Box::new(RealRepositoryFactory)
}
pub fn mock() -> Box<MockRepositoryFactory> {
Box::new(MockRepositoryFactory::new())
}
#[derive(Debug, Clone)]
struct RealRepositoryFactory;
#[cfg(not(tarpaulin_include))] // requires network access to either clone new and/or fetch.
impl RepositoryFactory for RealRepositoryFactory {
fn open(&self, gitdir: &GitDir) -> Result<Box<dyn OpenRepositoryLike>> {
let gix_repo = gix::ThreadSafeRepository::open(gitdir.to_path_buf())?.to_thread_local();
let repo = RealOpenRepository::new(Arc::new(RwLock::new(gix_repo.into())));
Ok(Box::new(repo))
}
fn git_clone(&self, repo_details: &RepoDetails) -> Result<Box<dyn OpenRepositoryLike>> {
tracing::info!("creating");
use secrecy::ExposeSecret;
let (gix_repo, _outcome) = gix::prepare_clone_bare(
repo_details.origin().expose_secret().as_str(),
repo_details.gitdir.deref(),
)?
.fetch_only(gix::progress::Discard, &AtomicBool::new(false))?;
tracing::info!("created");
let repo = RealOpenRepository::new(Arc::new(RwLock::new(gix_repo.into())));
Ok(Box::new(repo))
}
fn duplicate(&self) -> Box<dyn RepositoryFactory> {
Box::new(self.clone())
}
}

View file

@ -1,30 +1,23 @@
// //
use std::sync::{atomic::AtomicBool, Arc, RwLock}; use super::RepoDetails;
use crate::repository::test::TestRepository;
use derive_more::Deref as _; use crate::validation::remotes::validate_default_remotes;
pub use factory::RepositoryFactory;
pub mod open;
// mod real;
mod test;
#[cfg(test)]
mod tests;
use git_next_config as config; use git_next_config as config;
use git_next_config::GitDir; use git_next_config::GitDir;
pub use open::otest::OnFetch; pub use open::otest::OnFetch;
pub use open::otest::OnPush; pub use open::otest::OnPush;
pub use open::OpenRepository; pub use open::OpenRepository;
pub use open::OpenRepositoryLike; pub use open::OpenRepositoryLike;
pub use open::RealOpenRepository; pub use open::RealOpenRepository;
use tracing::info; use tracing::info;
use crate::repository::test::TestRepository; pub mod factory;
use crate::validation::remotes::validate_default_remotes; pub mod open;
mod test;
use super::RepoDetails; #[cfg(test)]
mod tests;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
#[allow(clippy::large_enum_variant)] #[allow(clippy::large_enum_variant)]
@ -41,7 +34,7 @@ pub const fn test(fs: kxio::fs::FileSystem) -> TestRepository {
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
#[cfg(not(tarpaulin_include))] // requires network access to either clone new and/or fetch. #[cfg(not(tarpaulin_include))] // requires network access to either clone new and/or fetch.
pub fn open( pub fn open(
repository_factory: &dyn RepositoryFactory, repository_factory: &dyn factory::RepositoryFactory,
repo_details: &RepoDetails, repo_details: &RepoDetails,
gitdir: config::GitDir, gitdir: config::GitDir,
) -> Result<Box<dyn OpenRepositoryLike>> { ) -> Result<Box<dyn OpenRepositoryLike>> {
@ -58,50 +51,6 @@ pub fn open(
Ok(open_repository) Ok(open_repository)
} }
#[mockall::automock]
pub trait RepositoryFactory: std::fmt::Debug + Sync + Send {
fn duplicate(&self) -> Box<dyn RepositoryFactory>;
fn open(&self, gitdir: &GitDir) -> Result<Box<dyn OpenRepositoryLike>>;
fn git_clone(&self, repo_details: &RepoDetails) -> Result<Box<dyn OpenRepositoryLike>>;
}
pub fn real() -> Box<dyn RepositoryFactory> {
Box::new(RealRepositoryFactory)
}
pub fn mock() -> Box<MockRepositoryFactory> {
Box::new(MockRepositoryFactory::new())
}
#[derive(Debug, Clone)]
struct RealRepositoryFactory;
#[cfg(not(tarpaulin_include))] // requires network access to either clone new and/or fetch.
impl RepositoryFactory for RealRepositoryFactory {
fn open(&self, gitdir: &GitDir) -> Result<Box<dyn OpenRepositoryLike>> {
let gix_repo = gix::ThreadSafeRepository::open(gitdir.to_path_buf())?.to_thread_local();
let repo = RealOpenRepository::new(Arc::new(RwLock::new(gix_repo.into())));
Ok(Box::new(repo))
}
fn git_clone(&self, repo_details: &RepoDetails) -> Result<Box<dyn OpenRepositoryLike>> {
tracing::info!("creating");
use secrecy::ExposeSecret;
let (gix_repo, _outcome) = gix::prepare_clone_bare(
repo_details.origin().expose_secret().as_str(),
repo_details.gitdir.deref(),
)?
.fetch_only(gix::progress::Discard, &AtomicBool::new(false))?;
tracing::info!("created");
let repo = RealOpenRepository::new(Arc::new(RwLock::new(gix_repo.into())));
Ok(Box::new(repo))
}
fn duplicate(&self) -> Box<dyn RepositoryFactory> {
Box::new(self.clone())
}
}
pub trait RepositoryLike { pub trait RepositoryLike {
fn open(&self, gitdir: &GitDir) -> Result<OpenRepository>; fn open(&self, gitdir: &GitDir) -> Result<OpenRepository>;
fn git_clone(&self, repo_details: &RepoDetails) -> Result<OpenRepository>; fn git_clone(&self, repo_details: &RepoDetails) -> Result<OpenRepository>;

View file

@ -5,9 +5,6 @@ mod tests;
pub mod oreal; pub mod oreal;
pub mod otest; pub mod otest;
#[cfg(test)]
pub mod ofake;
use std::{ use std::{
path::Path, path::Path,
sync::{Arc, RwLock}, sync::{Arc, RwLock},
@ -16,8 +13,6 @@ use std::{
use crate as git; use crate as git;
use git::repository::Direction; use git::repository::Direction;
use git_next_config as config; use git_next_config as config;
#[cfg(test)]
pub use ofake::FakeOpenRepository;
pub use oreal::RealOpenRepository; pub use oreal::RealOpenRepository;
pub use otest::TestOpenRepository; pub use otest::TestOpenRepository;

View file

@ -1,121 +0,0 @@
//
use crate::{self as git, repository::OpenRepositoryLike};
use git_next_config as config;
use std::{
path::Path,
sync::{Arc, RwLock},
};
#[derive(Clone, Debug, Default)]
pub struct FakeOpenRepository {
default_push_remote: Arc<RwLock<Option<git::GitRemote>>>,
default_fetch_remote: Arc<RwLock<Option<git::GitRemote>>>,
operations: Arc<RwLock<Vec<String>>>,
}
impl FakeOpenRepository {
pub fn new() -> Self {
Self::default()
}
pub fn given_has_default_remote(
&mut self,
direction: git::repository::Direction,
remote: Option<git::GitRemote>,
) {
#[allow(clippy::unwrap_used)]
match direction {
git::repository::Direction::Push => self
.default_push_remote
.write()
.map(|mut o| match remote {
Some(gr) => o.replace(gr),
None => o.take(),
})
.unwrap(),
git::repository::Direction::Fetch => self
.default_fetch_remote
.write()
.map(|mut o| match remote {
Some(gr) => o.replace(gr),
None => o.take(),
})
.unwrap(),
};
}
pub fn operations(&self) -> Vec<String> {
self.operations
.read()
.map(|operations| operations.clone())
.unwrap_or_default()
}
}
#[allow(clippy::unwrap_used)]
impl git::repository::OpenRepositoryLike for FakeOpenRepository {
fn remote_branches(&self) -> git::push::Result<Vec<config::BranchName>> {
todo!("MockOpenRepository::remote_branched")
}
fn find_default_remote(&self, direction: git::repository::Direction) -> Option<git::GitRemote> {
match direction {
git::repository::Direction::Push => self
.default_push_remote
.read()
.map(|r| r.clone())
.unwrap_or(None),
git::repository::Direction::Fetch => self
.default_fetch_remote
.read()
.map(|r| r.clone())
.unwrap_or(None),
}
}
fn fetch(&self) -> core::result::Result<(), crate::fetch::Error> {
self.operations
.write()
.map_err(|_| crate::fetch::Error::Lock)
.map(|mut operations| operations.push("fetch".to_string()))?;
Ok(())
}
fn push(
&self,
repo_details: &git::RepoDetails,
branch_name: &git_next_config::BranchName,
to_commit: &crate::GitRef,
force: &crate::push::Force,
) -> core::result::Result<(), crate::push::Error> {
let forge_alias = repo_details.forge.forge_alias();
let repo_alias = &repo_details.repo_alias;
self.operations
.write()
.map_err(|_| crate::fetch::Error::Lock)
.map(|mut operations| {
operations.push(format!(
"push fa:{forge_alias} ra:{repo_alias} bn:{branch_name} tc:{to_commit} f:{force}"
))
})?;
Ok(())
}
fn commit_log(
&self,
_branch_name: &git_next_config::BranchName,
_find_commits: &[git::Commit],
) -> core::result::Result<Vec<crate::Commit>, git::commit::log::Error> {
todo!("MockOpenRepository::commit_log")
}
fn read_file(
&self,
_branch_name: &git_next_config::BranchName,
_file_name: &Path,
) -> git::file::Result<String> {
todo!("MockOpenRepository::read_file")
}
fn duplicate(&self) -> Box<dyn OpenRepositoryLike> {
Box::new(self.clone())
}
}

View file

@ -1,454 +0,0 @@
//
use crate as git;
use crate::repository::RepositoryLike as _;
use git::tests::given;
use git_next_config as config;
use assert2::let_assert;
type TestResult = Result<(), Box<dyn std::error::Error>>;
mod server_repo_config {
use super::*;
use std::path::PathBuf;
use assert2::let_assert;
use crate::tests::given;
#[test]
fn should_not_return_repo_config_when_no_branches() {
let main = None;
let next = None;
let dev = None;
let src =
config::ServerRepoConfig::new(given::a_name(), given::a_name(), None, main, next, dev);
let_assert!(None = src.repo_config());
}
#[test]
fn should_return_repo_config_when_branches() {
let main = given::a_name();
let next = given::a_name();
let dev = given::a_name();
let src = config::ServerRepoConfig::new(
given::a_name(),
given::a_name(),
None,
Some(main.clone()),
Some(next.clone()),
Some(dev.clone()),
);
let_assert!(Some(rc) = src.repo_config());
assert_eq!(
rc,
config::RepoConfig::new(
config::RepoBranches::new(main, next, dev),
config::RepoConfigSource::Server
)
);
}
#[test]
fn should_return_repo() {
let repo_path = given::a_name();
let src = config::ServerRepoConfig::new(
repo_path.clone(),
given::a_name(),
None,
Some(given::a_name()),
Some(given::a_name()),
Some(given::a_name()),
);
assert_eq!(src.repo(), config::RepoPath::new(repo_path));
}
#[test]
fn should_return_branch() {
let branch = given::a_name();
let src = config::ServerRepoConfig::new(
given::a_name(),
branch.clone(),
None,
Some(given::a_name()),
Some(given::a_name()),
Some(given::a_name()),
);
assert_eq!(src.branch(), config::BranchName::new(branch));
}
#[test]
fn should_return_gitdir() {
let gitdir = given::a_name();
let src = config::ServerRepoConfig::new(
given::a_name(),
given::a_name(),
Some(gitdir.clone().into()),
Some(given::a_name()),
Some(given::a_name()),
Some(given::a_name()),
);
assert_eq!(
src.gitdir(),
Some(config::GitDir::new(PathBuf::default().join(gitdir)))
);
}
}
mod repo_config {
use super::*;
#[test]
fn should_parse_toml() -> TestResult {
let main = given::a_name();
let next = given::a_name();
let dev = given::a_name();
let toml = format!(
r#"
[branches]
main = "{main}"
next = "{next}"
dev = "{dev}"
"#
);
let rc = config::RepoConfig::parse(toml.as_str())?;
assert_eq!(
rc,
config::RepoConfig::new(
config::RepoBranches::new(main, next, dev),
config::RepoConfigSource::Repo // reading from repo is the default
)
);
Ok(())
}
#[test]
fn should_return_branches() {
let main = given::a_name();
let next = given::a_name();
let dev = given::a_name();
let branches = config::RepoBranches::new(main, next, dev);
let repo_config = config::RepoConfig::new(branches.clone(), config::RepoConfigSource::Repo);
assert_eq!(repo_config.branches(), &branches);
}
#[test]
fn should_return_source() {
let main = given::a_name();
let next = given::a_name();
let dev = given::a_name();
let repo_config = config::RepoConfig::new(
config::RepoBranches::new(main, next, dev),
config::RepoConfigSource::Repo,
);
assert_eq!(repo_config.source(), config::RepoConfigSource::Repo);
}
}
mod forge_config {
use super::*;
use std::collections::BTreeMap;
use secrecy::ExposeSecret;
use crate::tests::given;
#[test]
fn should_return_repos() {
let forge_type = config::ForgeType::MockForge;
let hostname = given::a_name();
let user = given::a_name();
let token = given::a_name();
// alphabetical order by key
let red_name = format!("a-{}", given::a_name());
let blue_name = format!("b-{}", given::a_name());
let red = config::ServerRepoConfig::new(
red_name.clone(),
given::a_name(),
None,
None,
None,
None,
);
let blue = config::ServerRepoConfig::new(
blue_name.clone(),
given::a_name(),
None,
None,
None,
None,
);
let mut repos = BTreeMap::new();
repos.insert(red_name.clone(), red.clone());
repos.insert(blue_name.clone(), blue.clone());
let fc = config::ForgeConfig::new(forge_type, hostname, user, token, repos);
let returned_repos = fc.repos().collect::<Vec<_>>();
assert_eq!(
returned_repos,
vec![
// alphabetical order by key
(config::RepoAlias::new(red_name.as_str()), &red),
(config::RepoAlias::new(blue_name.as_str()), &blue),
]
);
}
#[test]
fn should_return_forge_type() {
let forge_type = config::ForgeType::MockForge;
let hostname = given::a_name();
let user = given::a_name();
let token = given::a_name();
let repos = BTreeMap::new();
let fc = config::ForgeConfig::new(forge_type, hostname, user, token, repos);
assert_eq!(fc.forge_type(), config::ForgeType::MockForge);
}
#[test]
fn should_return_hostname() {
let forge_type = config::ForgeType::MockForge;
let hostname = given::a_name();
let user = given::a_name();
let token = given::a_name();
let repos = BTreeMap::new();
let fc = config::ForgeConfig::new(forge_type, hostname.clone(), user, token, repos);
assert_eq!(fc.hostname(), config::Hostname::new(hostname));
}
#[test]
fn should_return_user() {
let forge_type = config::ForgeType::MockForge;
let hostname = given::a_name();
let user = given::a_name();
let token = given::a_name();
let repos = BTreeMap::new();
let fc = config::ForgeConfig::new(forge_type, hostname, user.clone(), token, repos);
assert_eq!(fc.user(), config::User::new(user));
}
#[test]
fn should_return_token() {
let forge_type = config::ForgeType::MockForge;
let hostname = given::a_name();
let user = given::a_name();
let token = given::a_name();
let repos = BTreeMap::new();
let fc = config::ForgeConfig::new(forge_type, hostname, user, token.clone(), repos);
assert_eq!(fc.token().expose_secret(), token.as_str());
}
#[test]
fn should_return_repo() {
let forge_type = config::ForgeType::MockForge;
let hostname = given::a_name();
let user = given::a_name();
let token = given::a_name();
let red_name = given::a_name();
let blue_name = given::a_name();
let red = config::ServerRepoConfig::new(
red_name.clone(),
given::a_name(),
None,
None,
None,
None,
);
let blue = config::ServerRepoConfig::new(
blue_name.clone(),
given::a_name(),
None,
None,
None,
None,
);
let mut repos = BTreeMap::new();
repos.insert(red_name.clone(), red.clone());
repos.insert(blue_name, blue);
let fc = config::ForgeConfig::new(forge_type, hostname, user, token, repos);
let returned_repo = fc.get_repo(red_name.as_str());
assert_eq!(returned_repo, Some(&red),);
}
}
mod find_default_remote {
use super::*;
use assert2::let_assert;
#[test]
fn should_find_default_push_remote() {
// uses the current repo
let_assert!(Ok(cwd) = std::env::current_dir());
let gitdir = config::GitDir::from(cwd.join("../..")); // from ./crate/git directory to the project rook
let_assert!(Ok(repo) = crate::repository::real().open(&gitdir));
let_assert!(Some(remote) = repo.find_default_remote(crate::repository::Direction::Push));
assert_eq!(
remote,
git::GitRemote::new(
config::Hostname::new("git.kemitix.net"),
config::RepoPath::new("kemitix/git-next".to_string())
)
)
}
}
mod fetch {
use assert2::let_assert;
use git_next_config::GitDir;
#[test]
#[ignore] // requires authentication to the server - which the CI doesn't have
fn should_fetch_from_repo() {
// uses the current repo and fetches from the remote server
let_assert!(Ok(cwd) = std::env::current_dir());
let gitdir = GitDir::from(cwd.join("../.."));
let_assert!(Ok(repo) = crate::repository::real().open(&gitdir));
let_assert!(Ok(_) = repo.fetch());
}
}
mod commit_log {
use git::tests::given;
use crate::tests::then;
use super::*;
#[test]
// assumes running in the git-next repo which should have main, next and dev as remote branches
fn should_return_single_item_in_commit_log_when_not_searching() -> TestResult {
let_assert!(Ok(fs) = kxio::fs::temp());
let gitdir: config::GitDir = fs.base().to_path_buf().into();
let test_repository = git::repository::test(fs.clone());
let_assert!(Ok(open_repository) = test_repository.open(&gitdir));
let repo_config = &given::a_repo_config();
let branches = repo_config.branches();
then::create_a_commit_on_branch(&fs, &gitdir, &branches.main())?;
then::create_a_commit_on_branch(&fs, &gitdir, &branches.main())?;
let_assert!(Ok(result) = open_repository.commit_log(&branches.main(), &[]));
assert_eq!(result.len(), 1);
Ok(())
}
#[test]
// assumes running in the git-next repo which should have main, next and dev as remote branches
fn should_return_capacity_50_in_commit_log_when_searching_for_garbage() -> TestResult {
let_assert!(Ok(fs) = kxio::fs::temp());
let branch_name = given::a_branch_name();
let gitdir: config::GitDir = fs.base().to_path_buf().into();
let test_repository = git::repository::test(fs.clone());
let_assert!(Ok(open_repository) = test_repository.open(&gitdir));
for _ in [0; 60] {
// create 60 commits
then::create_a_commit_on_branch(&fs, &gitdir, &branch_name)?;
}
let_assert!(Ok(result) = open_repository.commit_log(&branch_name, &[given::a_commit()]));
assert_eq!(result.len(), 50);
Ok(())
}
#[test]
// assumes running in the git-next repo which should have main, next and dev as remote branches
fn should_return_5_in_commit_log_when_searching_for_5th_item() -> TestResult {
let_assert!(Ok(fs) = kxio::fs::temp(), "create temp directory");
let branch_name = given::a_branch_name();
let gitdir: config::GitDir = fs.base().to_path_buf().into();
let test_repository = git::repository::test(fs.clone());
let_assert!(
Ok(open_repository) = test_repository.open(&gitdir),
"open repository"
);
for _ in [0; 10] {
then::create_a_commit_on_branch(&fs, &gitdir, &branch_name)?;
}
// search to garbage to get all 10 items
let_assert!(
Ok(long_list) = open_repository.commit_log(&branch_name, &[given::a_commit()]),
"get commit_log"
);
// pick the 5th item
let search = &long_list[4]; // zero-based
// search for the 25th item
let_assert!(
Ok(result) = open_repository.commit_log(&branch_name, &[search.clone()]),
"get commit log"
);
// returns
assert_eq!(result.len(), 5);
Ok(())
}
}
mod read_file {
use git::tests::given;
use git::tests::then;
type TestResult = Result<(), Box<dyn std::error::Error>>;
use super::*;
#[test]
// assumes running in the git-next repo which should have main, next and dev as remote branches
fn should_return_file() -> TestResult {
let_assert!(Ok(fs) = kxio::fs::temp());
let repo_config = given::a_repo_config();
let file_name = given::a_pathbuf();
let contents = given::a_name();
let gitdir: config::GitDir = fs.base().to_path_buf().into();
let test_repository = git::repository::test(fs.clone());
let_assert!(Ok(open_repository) = test_repository.open(&gitdir));
then::commit_named_file_to_branch(
&file_name,
&contents,
&fs,
&gitdir,
&repo_config.branches().main(),
)?;
// then::create_a_commit_on_branch(&fs, &gitdir, &repo_config.branches().main())?;
let_assert!(
Ok(result) = open_repository.read_file(&repo_config.branches().main(), &file_name),
"read file"
);
assert_eq!(result, contents);
Ok(())
}
#[test]
// assumes running in the git-next repo which should have main, next and dev as remote branches
fn should_error_on_missing_file() -> TestResult {
let_assert!(Ok(fs) = kxio::fs::temp());
let gitdir: config::GitDir = fs.base().to_path_buf().into();
let test_repository = git::repository::test(fs.clone());
let_assert!(Ok(open_repository) = test_repository.open(&gitdir));
let repo_config = &given::a_repo_config();
let branches = repo_config.branches();
then::create_a_commit_on_branch(&fs, &gitdir, &branches.dev())?;
let_assert!(
Err(err) = open_repository.read_file(&branches.dev(), &given::a_pathbuf()),
"read file"
);
eprintln!("err: {err:#?}");
assert!(matches!(err, git::file::Error::FileNotFound));
Ok(())
}
}

View file

@ -0,0 +1,75 @@
use super::*;
#[test]
// assumes running in the git-next repo which should have main, next and dev as remote branches
fn should_return_single_item_in_commit_log_when_not_searching() -> TestResult {
let_assert!(Ok(fs) = kxio::fs::temp());
let gitdir = GitDir::new(
fs.base().to_path_buf(),
config::git_dir::StoragePathType::Internal,
);
let test_repository = git::repository::test(fs.clone());
let_assert!(Ok(open_repository) = test_repository.open(&gitdir));
let repo_config = &given::a_repo_config();
let branches = repo_config.branches();
then::create_a_commit_on_branch(&fs, &gitdir, &branches.main())?;
then::create_a_commit_on_branch(&fs, &gitdir, &branches.main())?;
let_assert!(Ok(result) = open_repository.commit_log(&branches.main(), &[]));
assert_eq!(result.len(), 1);
Ok(())
}
#[test]
// assumes running in the git-next repo which should have main, next and dev as remote branches
fn should_return_capacity_50_in_commit_log_when_searching_for_garbage() -> TestResult {
let_assert!(Ok(fs) = kxio::fs::temp());
let branch_name = given::a_branch_name();
let gitdir = GitDir::new(
fs.base().to_path_buf(),
config::git_dir::StoragePathType::Internal,
);
let test_repository = git::repository::test(fs.clone());
let_assert!(Ok(open_repository) = test_repository.open(&gitdir));
for _ in [0; 60] {
// create 60 commits
then::create_a_commit_on_branch(&fs, &gitdir, &branch_name)?;
}
let_assert!(Ok(result) = open_repository.commit_log(&branch_name, &[given::a_commit()]));
assert_eq!(result.len(), 50);
Ok(())
}
#[test]
// assumes running in the git-next repo which should have main, next and dev as remote branches
fn should_return_5_in_commit_log_when_searching_for_5th_item() -> TestResult {
let_assert!(Ok(fs) = kxio::fs::temp(), "create temp directory");
let branch_name = given::a_branch_name();
let gitdir = GitDir::new(
fs.base().to_path_buf(),
config::git_dir::StoragePathType::Internal,
);
let test_repository = git::repository::test(fs.clone());
let_assert!(
Ok(open_repository) = test_repository.open(&gitdir),
"open repository"
);
for _ in [0; 10] {
then::create_a_commit_on_branch(&fs, &gitdir, &branch_name)?;
}
// search to garbage to get all 10 items
let_assert!(
Ok(long_list) = open_repository.commit_log(&branch_name, &[given::a_commit()]),
"get commit_log"
);
// pick the 5th item
let search = &long_list[4]; // zero-based
// search for the 25th item
let_assert!(
Ok(result) = open_repository.commit_log(&branch_name, &[search.clone()]),
"get commit log"
);
// returns
assert_eq!(result.len(), 5);
Ok(())
}

View file

@ -0,0 +1,14 @@
use super::*;
#[test]
#[ignore] // requires authentication to the server - which the CI doesn't have
fn should_fetch_from_repo() {
// uses the current repo and fetches from the remote server
let_assert!(Ok(cwd) = std::env::current_dir());
let gitdir = GitDir::new(
cwd.join("../.."),
config::git_dir::StoragePathType::External,
);
let_assert!(Ok(repo) = crate::repository::factory::real().open(&gitdir));
let_assert!(Ok(_) = repo.fetch());
}

View file

@ -0,0 +1,20 @@
use super::*;
#[test]
fn should_find_default_push_remote() {
// uses the current repo
let_assert!(Ok(cwd) = std::env::current_dir());
let gitdir = config::GitDir::new(
cwd.join("../.."),
config::git_dir::StoragePathType::External,
); // from ./crate/git directory to the project root
let_assert!(Ok(repo) = git::repository::factory::real().open(&gitdir));
let_assert!(Some(remote) = repo.find_default_remote(crate::repository::Direction::Push));
assert_eq!(
remote,
git::GitRemote::new(
config::Hostname::new("git.kemitix.net"),
config::RepoPath::new("kemitix/git-next".to_string())
)
)
}

View file

@ -0,0 +1,102 @@
use super::*;
#[test]
fn should_return_repos() {
let forge_type = config::ForgeType::MockForge;
let hostname = given::a_name();
let user = given::a_name();
let token = given::a_name();
// alphabetical order by key
let red_name = format!("a-{}", given::a_name());
let blue_name = format!("b-{}", given::a_name());
let red =
config::ServerRepoConfig::new(red_name.clone(), given::a_name(), None, None, None, None);
let blue =
config::ServerRepoConfig::new(blue_name.clone(), given::a_name(), None, None, None, None);
let mut repos = BTreeMap::new();
repos.insert(red_name.clone(), red.clone());
repos.insert(blue_name.clone(), blue.clone());
let fc = config::ForgeConfig::new(forge_type, hostname, user, token, repos);
let returned_repos = fc.repos().collect::<Vec<_>>();
assert_eq!(
returned_repos,
vec![
// alphabetical order by key
(config::RepoAlias::new(red_name.as_str()), &red),
(config::RepoAlias::new(blue_name.as_str()), &blue),
]
);
}
#[test]
fn should_return_forge_type() {
let forge_type = config::ForgeType::MockForge;
let hostname = given::a_name();
let user = given::a_name();
let token = given::a_name();
let repos = BTreeMap::new();
let fc = config::ForgeConfig::new(forge_type, hostname, user, token, repos);
assert_eq!(fc.forge_type(), config::ForgeType::MockForge);
}
#[test]
fn should_return_hostname() {
let forge_type = config::ForgeType::MockForge;
let hostname = given::a_name();
let user = given::a_name();
let token = given::a_name();
let repos = BTreeMap::new();
let fc = config::ForgeConfig::new(forge_type, hostname.clone(), user, token, repos);
assert_eq!(fc.hostname(), config::Hostname::new(hostname));
}
#[test]
fn should_return_user() {
let forge_type = config::ForgeType::MockForge;
let hostname = given::a_name();
let user = given::a_name();
let token = given::a_name();
let repos = BTreeMap::new();
let fc = config::ForgeConfig::new(forge_type, hostname, user.clone(), token, repos);
assert_eq!(fc.user(), config::User::new(user));
}
#[test]
fn should_return_token() {
let forge_type = config::ForgeType::MockForge;
let hostname = given::a_name();
let user = given::a_name();
let token = given::a_name();
let repos = BTreeMap::new();
let fc = config::ForgeConfig::new(forge_type, hostname, user, token.clone(), repos);
assert_eq!(fc.token().expose_secret(), token.as_str());
}
#[test]
fn should_return_repo() {
let forge_type = config::ForgeType::MockForge;
let hostname = given::a_name();
let user = given::a_name();
let token = given::a_name();
let red_name = given::a_name();
let blue_name = given::a_name();
let red =
config::ServerRepoConfig::new(red_name.clone(), given::a_name(), None, None, None, None);
let blue =
config::ServerRepoConfig::new(blue_name.clone(), given::a_name(), None, None, None, None);
let mut repos = BTreeMap::new();
repos.insert(red_name.clone(), red.clone());
repos.insert(blue_name, blue);
let fc = config::ForgeConfig::new(forge_type, hostname, user, token, repos);
let returned_repo = fc.get_repo(red_name.as_str());
assert_eq!(returned_repo, Some(&red),);
}

View file

@ -0,0 +1,21 @@
//
use crate as git;
use assert2::let_assert;
use config::GitDir;
use git::repository::RepositoryLike as _;
use git::tests::given;
use git::tests::then;
use git_next_config as config;
use secrecy::ExposeSecret;
use std::collections::BTreeMap;
use std::path::PathBuf;
type TestResult = Result<(), Box<dyn std::error::Error>>;
mod commit_log;
mod fetch;
mod find_default_remote;
mod forge_config;
mod read_file;
mod repo_config;
mod server_repo_config;

View file

@ -0,0 +1,53 @@
use super::*;
#[test]
// assumes running in the git-next repo which should have main, next and dev as remote branches
fn should_return_file() -> TestResult {
let_assert!(Ok(fs) = kxio::fs::temp());
let repo_config = given::a_repo_config();
let file_name = given::a_pathbuf();
let contents = given::a_name();
let gitdir = GitDir::new(
fs.base().to_path_buf(),
config::git_dir::StoragePathType::Internal,
);
let test_repository = git::repository::test(fs.clone());
let_assert!(Ok(open_repository) = test_repository.open(&gitdir));
then::commit_named_file_to_branch(
&file_name,
&contents,
&fs,
&gitdir,
&repo_config.branches().main(),
)?;
// then::create_a_commit_on_branch(&fs, &gitdir, &repo_config.branches().main())?;
let_assert!(
Ok(result) = open_repository.read_file(&repo_config.branches().main(), &file_name),
"read file"
);
assert_eq!(result, contents);
Ok(())
}
#[test]
// assumes running in the git-next repo which should have main, next and dev as remote branches
fn should_error_on_missing_file() -> TestResult {
let_assert!(Ok(fs) = kxio::fs::temp());
let gitdir = GitDir::new(
fs.base().to_path_buf(),
config::git_dir::StoragePathType::Internal,
);
let test_repository = git::repository::test(fs.clone());
let_assert!(Ok(open_repository) = test_repository.open(&gitdir));
let repo_config = &given::a_repo_config();
let branches = repo_config.branches();
then::create_a_commit_on_branch(&fs, &gitdir, &branches.dev())?;
let_assert!(
Err(err) = open_repository.read_file(&branches.dev(), &given::a_pathbuf()),
"read file"
);
eprintln!("err: {err:#?}");
assert!(matches!(err, git::file::Error::FileNotFound));
Ok(())
}

View file

@ -0,0 +1,50 @@
use super::*;
#[test]
fn should_parse_toml() -> TestResult {
let main = given::a_name();
let next = given::a_name();
let dev = given::a_name();
let toml = format!(
r#"
[branches]
main = "{main}"
next = "{next}"
dev = "{dev}"
"#
);
let rc = config::RepoConfig::parse(toml.as_str())?;
assert_eq!(
rc,
config::RepoConfig::new(
config::RepoBranches::new(main, next, dev),
config::RepoConfigSource::Repo // reading from repo is the default
)
);
Ok(())
}
#[test]
fn should_return_branches() {
let main = given::a_name();
let next = given::a_name();
let dev = given::a_name();
let branches = config::RepoBranches::new(main, next, dev);
let repo_config = config::RepoConfig::new(branches.clone(), config::RepoConfigSource::Repo);
assert_eq!(repo_config.branches(), &branches);
}
#[test]
fn should_return_source() {
let main = given::a_name();
let next = given::a_name();
let dev = given::a_name();
let repo_config = config::RepoConfig::new(
config::RepoBranches::new(main, next, dev),
config::RepoConfigSource::Repo,
);
assert_eq!(repo_config.source(), config::RepoConfigSource::Repo);
}

View file

@ -0,0 +1,87 @@
use super::*;
#[test]
fn should_not_return_repo_config_when_no_branches() {
let main = None;
let next = None;
let dev = None;
let src =
config::ServerRepoConfig::new(given::a_name(), given::a_name(), None, main, next, dev);
let_assert!(None = src.repo_config());
}
#[test]
fn should_return_repo_config_when_branches() {
let main = given::a_name();
let next = given::a_name();
let dev = given::a_name();
let src = config::ServerRepoConfig::new(
given::a_name(),
given::a_name(),
None,
Some(main.clone()),
Some(next.clone()),
Some(dev.clone()),
);
let_assert!(Some(rc) = src.repo_config());
assert_eq!(
rc,
config::RepoConfig::new(
config::RepoBranches::new(main, next, dev),
config::RepoConfigSource::Server
)
);
}
#[test]
fn should_return_repo() {
let repo_path = given::a_name();
let src = config::ServerRepoConfig::new(
repo_path.clone(),
given::a_name(),
None,
Some(given::a_name()),
Some(given::a_name()),
Some(given::a_name()),
);
assert_eq!(src.repo(), config::RepoPath::new(repo_path));
}
#[test]
fn should_return_branch() {
let branch = given::a_name();
let src = config::ServerRepoConfig::new(
given::a_name(),
branch.clone(),
None,
Some(given::a_name()),
Some(given::a_name()),
Some(given::a_name()),
);
assert_eq!(src.branch(), config::BranchName::new(branch));
}
#[test]
fn should_return_gitdir() {
let gitdir = given::a_name();
let src = config::ServerRepoConfig::new(
given::a_name(),
given::a_name(),
Some(gitdir.clone().into()),
Some(given::a_name()),
Some(given::a_name()),
Some(given::a_name()),
);
assert_eq!(
src.gitdir(),
Some(GitDir::new(
PathBuf::default().join(gitdir),
config::StoragePathType::External,
))
);
}

View file

@ -1,100 +0,0 @@
use crate as git;
mod validate {
use crate::{tests::given, validation::remotes::validate_default_remotes};
use super::*;
use git::repository::Direction;
#[test]
fn should_ok_a_valid_repo() {
let fs = given::a_filesystem();
let repo_details = given::repo_details(&fs);
let repo_details_mock = repo_details.clone();
let mut open_repository = git::repository::open::mock();
open_repository
.expect_find_default_remote()
.returning(move |_direction| Some(repo_details_mock.git_remote()));
let result = validate_default_remotes(&*open_repository, &repo_details);
println!("{result:?}");
assert!(result.is_ok());
}
#[test]
fn should_fail_where_no_default_push_remote() {
let fs = given::a_filesystem();
let repo_details = given::repo_details(&fs);
let repo_details_mock = repo_details.clone();
let mut open_repository = git::repository::open::mock();
open_repository
.expect_find_default_remote()
.returning(move |direction| match direction {
Direction::Push => None,
Direction::Fetch => Some(repo_details_mock.git_remote()),
});
let result = validate_default_remotes(&*open_repository, &repo_details);
println!("{result:?}");
assert!(result.is_err());
}
#[test]
fn should_fail_where_no_default_fetch_remote() {
let fs = given::a_filesystem();
let repo_details = given::repo_details(&fs);
let repo_details_mock = repo_details.clone();
let mut open_repository = git::repository::open::mock();
open_repository
.expect_find_default_remote()
.returning(move |direction| match direction {
Direction::Push => Some(repo_details_mock.git_remote()),
Direction::Fetch => None,
});
let result = validate_default_remotes(&*open_repository, &repo_details);
println!("{result:?}");
assert!(result.is_err());
}
#[test]
fn should_fail_where_invalid_default_push_remote() {
let fs = given::a_filesystem();
let repo_details = given::repo_details(&fs);
let repo_details_mock = repo_details.clone();
let mut open_repository = git::repository::open::mock();
open_repository
.expect_find_default_remote()
.returning(move |direction| match direction {
Direction::Push => Some(given::a_git_remote()),
Direction::Fetch => Some(repo_details_mock.git_remote()),
});
let result = validate_default_remotes(&*open_repository, &repo_details);
println!("{result:?}");
assert!(result.is_err());
}
#[test]
fn should_fail_where_invalid_default_fetch_remote() {
let fs = given::a_filesystem();
let repo_details = given::repo_details(&fs);
let repo_details_mock = repo_details.clone();
let mut open_repository = git::repository::open::mock();
open_repository
.expect_find_default_remote()
.returning(move |direction| match direction {
Direction::Push => Some(repo_details_mock.git_remote()),
Direction::Fetch => Some(given::a_git_remote()),
});
let result = validate_default_remotes(&*open_repository, &repo_details);
println!("{result:?}");
assert!(result.is_err());
}
}

View file

@ -0,0 +1,52 @@
use git_next_config::GitDir;
use super::*;
use crate::tests::given;
// clone - can't test are this required a remote server (git_clone only works with https origins)
// open
// - outside storage path
#[test_log::test]
fn open_where_storage_external() -> TestResult {
//given
let fs = given::a_filesystem();
// create a bare repo
let _x = gix::prepare_clone_bare("https://user:auth@git.kemitix.net/user/repo.git", fs.base())?;
let gitdir = GitDir::new(
fs.base().to_path_buf(),
git_next_config::git_dir::Provenance::Internal,
);
let factory = crate::repository::factory::real();
//when
let result = factory.open(&gitdir);
//then
tracing::debug!(?result, "open");
assert!(result.is_ok());
Ok(())
}
// - in storage path
#[test_log::test]
fn open_where_storage_internal() -> TestResult {
//given
let fs = given::a_filesystem();
// create a bare repo
let _x = gix::prepare_clone_bare("https://user:auth@git.kemitix.net/user/repo.git", fs.base())?;
let gitdir = GitDir::new(
fs.base().to_path_buf(),
git_next_config::git_dir::Provenance::Internal,
);
let factory = crate::repository::factory::real();
//when
let result = factory.open(&gitdir);
//then
tracing::debug!(?result, "open");
assert!(result.is_ok());
Ok(())
}
// - - auth matches
// - - NEW: auth does not match

View file

@ -0,0 +1,3 @@
use crate as git;
mod validate;

View file

@ -0,0 +1,94 @@
use super::*;
use git::{repository::Direction, tests::given, validation::remotes::validate_default_remotes};
#[test]
fn should_ok_a_valid_repo() {
let fs = given::a_filesystem();
let repo_details = given::repo_details(&fs);
let repo_details_mock = repo_details.clone();
let mut open_repository = git::repository::open::mock();
open_repository
.expect_find_default_remote()
.returning(move |_direction| Some(repo_details_mock.git_remote()));
let result = validate_default_remotes(&*open_repository, &repo_details);
println!("{result:?}");
assert!(result.is_ok());
}
#[test]
fn should_fail_where_no_default_push_remote() {
let fs = given::a_filesystem();
let repo_details = given::repo_details(&fs);
let repo_details_mock = repo_details.clone();
let mut open_repository = git::repository::open::mock();
open_repository
.expect_find_default_remote()
.returning(move |direction| match direction {
Direction::Push => None,
Direction::Fetch => Some(repo_details_mock.git_remote()),
});
let result = validate_default_remotes(&*open_repository, &repo_details);
println!("{result:?}");
assert!(result.is_err());
}
#[test]
fn should_fail_where_no_default_fetch_remote() {
let fs = given::a_filesystem();
let repo_details = given::repo_details(&fs);
let repo_details_mock = repo_details.clone();
let mut open_repository = git::repository::open::mock();
open_repository
.expect_find_default_remote()
.returning(move |direction| match direction {
Direction::Push => Some(repo_details_mock.git_remote()),
Direction::Fetch => None,
});
let result = validate_default_remotes(&*open_repository, &repo_details);
println!("{result:?}");
assert!(result.is_err());
}
#[test]
fn should_fail_where_invalid_default_push_remote() {
let fs = given::a_filesystem();
let repo_details = given::repo_details(&fs);
let repo_details_mock = repo_details.clone();
let mut open_repository = git::repository::open::mock();
open_repository
.expect_find_default_remote()
.returning(move |direction| match direction {
Direction::Push => Some(given::a_git_remote()),
Direction::Fetch => Some(repo_details_mock.git_remote()),
});
let result = validate_default_remotes(&*open_repository, &repo_details);
println!("{result:?}");
assert!(result.is_err());
}
#[test]
fn should_fail_where_invalid_default_fetch_remote() {
let fs = given::a_filesystem();
let repo_details = given::repo_details(&fs);
let repo_details_mock = repo_details.clone();
let mut open_repository = git::repository::open::mock();
open_repository
.expect_find_default_remote()
.returning(move |direction| match direction {
Direction::Push => Some(repo_details_mock.git_remote()),
Direction::Fetch => Some(given::a_git_remote()),
});
let result = validate_default_remotes(&*open_repository, &repo_details);
println!("{result:?}");
assert!(result.is_err());
}

View file

@ -144,7 +144,8 @@ mod repo_details {
use std::{collections::BTreeMap, path::PathBuf}; use std::{collections::BTreeMap, path::PathBuf};
use git_next_config::{ use git_next_config::{
ForgeAlias, ForgeConfig, ForgeType, GitDir, Hostname, RepoAlias, RepoPath, ServerRepoConfig, git_dir::StoragePathType, ForgeAlias, ForgeConfig, ForgeType, GitDir, Hostname, RepoAlias,
RepoPath, ServerRepoConfig,
}; };
use secrecy::ExposeSecret; use secrecy::ExposeSecret;
@ -171,7 +172,7 @@ mod repo_details {
"token".to_string(), "token".to_string(),
BTreeMap::new(), BTreeMap::new(),
), ),
GitDir::from(PathBuf::default().join("foo")), GitDir::new(PathBuf::default().join("foo"), StoragePathType::Internal),
); );
assert_eq!( assert_eq!(
@ -200,7 +201,7 @@ mod repo_details {
"token".to_string(), "token".to_string(),
BTreeMap::new(), BTreeMap::new(),
), ),
GitDir::from(PathBuf::default().join("foo")), GitDir::new(PathBuf::default().join("foo"), StoragePathType::Internal),
); );
assert_eq!( assert_eq!(
@ -215,7 +216,7 @@ mod repo_details {
pub mod given { pub mod given {
use std::path::PathBuf; use std::path::PathBuf;
use crate as git; use crate::{self as git, repository::open::MockOpenRepositoryLike};
use config::{ use config::{
BranchName, ForgeAlias, ForgeConfig, ForgeType, GitDir, RepoAlias, RepoBranches, BranchName, ForgeAlias, ForgeConfig, ForgeType, GitDir, RepoAlias, RepoBranches,
RepoConfig, ServerRepoConfig, RepoConfig, ServerRepoConfig,
@ -263,7 +264,7 @@ pub mod given {
pub fn a_git_dir(fs: &kxio::fs::FileSystem) -> GitDir { pub fn a_git_dir(fs: &kxio::fs::FileSystem) -> GitDir {
let dir_name = a_name(); let dir_name = a_name();
let dir = fs.base().join(dir_name); let dir = fs.base().join(dir_name);
GitDir::new(dir) GitDir::new(dir, config::git_dir::StoragePathType::Internal)
} }
pub fn a_forge_config() -> ForgeConfig { pub fn a_forge_config() -> ForgeConfig {
@ -326,6 +327,25 @@ pub mod given {
kxio::fs::temp().unwrap_or_else(|e| panic!("{}", e)) kxio::fs::temp().unwrap_or_else(|e| panic!("{}", e))
} }
pub fn an_open_repository(
fs: &kxio::fs::FileSystem,
) -> (
git::repository::open::MockOpenRepositoryLike,
git::RepoDetails,
) {
let open_repository = MockOpenRepositoryLike::new();
let gitdir = given::a_git_dir(fs);
let hostname = given::a_hostname();
let repo_details = given::repo_details(fs)
.with_gitdir(gitdir)
.with_hostname(hostname);
(open_repository, repo_details)
}
pub fn a_hostname() -> config::Hostname {
config::Hostname::new(given::a_name())
}
pub fn repo_details(fs: &kxio::fs::FileSystem) -> git::RepoDetails { pub fn repo_details(fs: &kxio::fs::FileSystem) -> git::RepoDetails {
let generation = git::Generation::default(); let generation = git::Generation::default();
let repo_alias = a_repo_alias(); let repo_alias = a_repo_alias();

View file

@ -10,6 +10,8 @@ use assert2::let_assert;
mod repos { mod repos {
use config::{git_dir::StoragePathType, GitDir};
use crate::repository::Direction; use crate::repository::Direction;
use super::*; use super::*;
@ -17,13 +19,13 @@ mod repos {
#[test] #[test]
fn where_repo_has_no_push_remote() { fn where_repo_has_no_push_remote() {
let_assert!(Ok(fs) = kxio::fs::temp(), "temp fs"); let_assert!(Ok(fs) = kxio::fs::temp(), "temp fs");
let gitdir: config::GitDir = fs.base().to_path_buf().into(); let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal);
let mut mock_open_repository = git::repository::open::mock(); let mut mock_open_repository = git::repository::open::mock();
mock_open_repository mock_open_repository
.expect_find_default_remote() .expect_find_default_remote()
.with(mockall::predicate::eq(Direction::Push)) .with(mockall::predicate::eq(Direction::Push))
.return_once(|_| None); .return_once(|_| None);
let mut repository_factory = git::repository::mock(); let mut repository_factory = git::repository::factory::mock();
repository_factory repository_factory
.expect_open() .expect_open()
.return_once(move |_| Ok(mock_open_repository)); .return_once(move |_| Ok(mock_open_repository));
@ -49,6 +51,7 @@ mod positions {
mod validate_positions { mod validate_positions {
use config::{git_dir::StoragePathType, GitDir};
use git::validation::positions::validate_positions; use git::validation::positions::validate_positions;
use git::tests::then; use git::tests::then;
@ -58,12 +61,12 @@ mod positions {
#[test] #[test]
fn where_fetch_fails_should_error() { fn where_fetch_fails_should_error() {
let fs = given::a_filesystem(); let fs = given::a_filesystem();
let gitdir: config::GitDir = fs.base().to_path_buf().into(); let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal);
let mut mock_open_repository = git::repository::open::mock(); let mut mock_open_repository = git::repository::open::mock();
mock_open_repository mock_open_repository
.expect_fetch() .expect_fetch()
.return_once(|| Err(git::fetch::Error::TestFailureExpected)); .return_once(|| Err(git::fetch::Error::TestFailureExpected));
let mut repository_factory = git::repository::mock(); let mut repository_factory = git::repository::factory::mock();
repository_factory repository_factory
.expect_open() .expect_open()
.return_once(move |_| Ok(mock_open_repository)); .return_once(move |_| Ok(mock_open_repository));
@ -87,7 +90,7 @@ mod positions {
#[test] #[test]
fn where_main_branch_is_missing_or_commit_log_is_empty_should_error() { fn where_main_branch_is_missing_or_commit_log_is_empty_should_error() {
let fs = given::a_filesystem(); let fs = given::a_filesystem();
let gitdir: config::GitDir = fs.base().to_path_buf().into(); let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal);
let repo_config = given::a_repo_config(); let repo_config = given::a_repo_config();
let main_branch = repo_config.branches().main(); let main_branch = repo_config.branches().main();
let mut mock_open_repository = git::repository::open::mock(); let mut mock_open_repository = git::repository::open::mock();
@ -104,7 +107,7 @@ mod positions {
Ok(vec![]) Ok(vec![])
} }
}); });
let mut repository_factory = git::repository::mock(); let mut repository_factory = git::repository::factory::mock();
repository_factory repository_factory
.expect_open() .expect_open()
.return_once(move |_| Ok(mock_open_repository)); .return_once(move |_| Ok(mock_open_repository));
@ -126,7 +129,7 @@ mod positions {
#[test] #[test]
fn where_next_branch_is_missing_or_commit_log_is_empty_should_error() { fn where_next_branch_is_missing_or_commit_log_is_empty_should_error() {
let fs = given::a_filesystem(); let fs = given::a_filesystem();
let gitdir: config::GitDir = fs.base().to_path_buf().into(); let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal);
let repo_config = given::a_repo_config(); let repo_config = given::a_repo_config();
let next_branch = repo_config.branches().next(); let next_branch = repo_config.branches().next();
let mut mock_open_repository = git::repository::open::mock(); let mut mock_open_repository = git::repository::open::mock();
@ -143,7 +146,7 @@ mod positions {
Ok(vec![given::a_commit()]) Ok(vec![given::a_commit()])
} }
}); });
let mut repository_factory = git::repository::mock(); let mut repository_factory = git::repository::factory::mock();
repository_factory repository_factory
.expect_open() .expect_open()
.return_once(move |_| Ok(mock_open_repository)); .return_once(move |_| Ok(mock_open_repository));
@ -165,7 +168,7 @@ mod positions {
#[test] #[test]
fn where_dev_branch_is_missing_or_commit_log_is_empty_should_error() { fn where_dev_branch_is_missing_or_commit_log_is_empty_should_error() {
let fs = given::a_filesystem(); let fs = given::a_filesystem();
let gitdir: config::GitDir = fs.base().to_path_buf().into(); let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal);
let repo_config = given::a_repo_config(); let repo_config = given::a_repo_config();
let dev_branch = repo_config.branches().dev(); let dev_branch = repo_config.branches().dev();
let mut mock_open_repository = git::repository::open::mock(); let mut mock_open_repository = git::repository::open::mock();
@ -182,7 +185,7 @@ mod positions {
Ok(vec![given::a_commit()]) Ok(vec![given::a_commit()])
} }
}); });
let mut repository_factory = git::repository::mock(); let mut repository_factory = git::repository::factory::mock();
repository_factory repository_factory
.expect_open() .expect_open()
.return_once(move |_| Ok(mock_open_repository)); .return_once(move |_| Ok(mock_open_repository));
@ -205,7 +208,7 @@ mod positions {
fn where_dev_branch_is_not_based_on_main_should_error() { fn where_dev_branch_is_not_based_on_main_should_error() {
//given //given
let fs = given::a_filesystem(); let fs = given::a_filesystem();
let gitdir: config::GitDir = fs.base().to_path_buf().into(); let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal);
let mut test_repository = git::repository::test(fs.clone()); let mut test_repository = git::repository::test(fs.clone());
let repo_config = given::a_repo_config(); let repo_config = given::a_repo_config();
test_repository.on_fetch(git::repository::OnFetch::new( test_repository.on_fetch(git::repository::OnFetch::new(
@ -255,7 +258,7 @@ mod positions {
{ {
//given //given
let_assert!(Ok(fs) = kxio::fs::temp(), "temp fs"); let_assert!(Ok(fs) = kxio::fs::temp(), "temp fs");
let gitdir: config::GitDir = fs.base().to_path_buf().into(); let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal);
let mut test_repository = git::repository::test(fs.clone()); let mut test_repository = git::repository::test(fs.clone());
let repo_config = given::a_repo_config(); let repo_config = given::a_repo_config();
test_repository.on_fetch(git::repository::OnFetch::new( test_repository.on_fetch(git::repository::OnFetch::new(
@ -341,7 +344,7 @@ mod positions {
fn where_next_branch_is_not_based_on_main_and_reset_of_next_fails_should_error() { fn where_next_branch_is_not_based_on_main_and_reset_of_next_fails_should_error() {
//given //given
let_assert!(Ok(fs) = kxio::fs::temp(), "temp fs"); let_assert!(Ok(fs) = kxio::fs::temp(), "temp fs");
let gitdir: config::GitDir = fs.base().to_path_buf().into(); let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal);
let mut test_repository = git::repository::test(fs.clone()); let mut test_repository = git::repository::test(fs.clone());
let repo_config = given::a_repo_config(); let repo_config = given::a_repo_config();
test_repository.on_fetch(git::repository::OnFetch::new( test_repository.on_fetch(git::repository::OnFetch::new(
@ -414,7 +417,7 @@ mod positions {
fn where_dev_branch_is_not_based_on_next_should_reset_next_branch_to_main_and_then_error() { fn where_dev_branch_is_not_based_on_next_should_reset_next_branch_to_main_and_then_error() {
//given //given
let_assert!(Ok(fs) = kxio::fs::temp(), "temp fs"); let_assert!(Ok(fs) = kxio::fs::temp(), "temp fs");
let gitdir: config::GitDir = fs.base().to_path_buf().into(); let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal);
let mut test_repository = git::repository::test(fs.clone()); let mut test_repository = git::repository::test(fs.clone());
let repo_config = given::a_repo_config(); let repo_config = given::a_repo_config();
test_repository.on_fetch(git::repository::OnFetch::new( test_repository.on_fetch(git::repository::OnFetch::new(
@ -498,7 +501,7 @@ mod positions {
fn where_dev_branch_is_not_based_on_next_and_reset_of_next_fails_should_error() { fn where_dev_branch_is_not_based_on_next_and_reset_of_next_fails_should_error() {
//given //given
let_assert!(Ok(fs) = kxio::fs::temp(), "temp fs"); let_assert!(Ok(fs) = kxio::fs::temp(), "temp fs");
let gitdir: config::GitDir = fs.base().to_path_buf().into(); let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal);
let mut test_repository = git::repository::test(fs.clone()); let mut test_repository = git::repository::test(fs.clone());
let repo_config = given::a_repo_config(); let repo_config = given::a_repo_config();
test_repository.on_fetch(git::repository::OnFetch::new( test_repository.on_fetch(git::repository::OnFetch::new(
@ -569,7 +572,7 @@ mod positions {
fn where_branches_are_all_valid_should_return_positions() { fn where_branches_are_all_valid_should_return_positions() {
//given //given
let_assert!(Ok(fs) = kxio::fs::temp(), "temp fs"); let_assert!(Ok(fs) = kxio::fs::temp(), "temp fs");
let gitdir: config::GitDir = fs.base().to_path_buf().into(); let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal);
let mut test_repository = git::repository::test(fs.clone()); let mut test_repository = git::repository::test(fs.clone());
let repo_config = given::a_repo_config(); let repo_config = given::a_repo_config();
test_repository.on_fetch(git::repository::OnFetch::new( test_repository.on_fetch(git::repository::OnFetch::new(

View file

@ -1,7 +1,7 @@
// //
use super::*; use super::*;
use git::repository::MockRepositoryFactory; use git::repository::factory::MockRepositoryFactory;
use git_next_git::repository::open::MockOpenRepositoryLike; use git_next_git::repository::open::MockOpenRepositoryLike;
pub fn fetch_ok(open_repository: &mut MockOpenRepositoryLike) { pub fn fetch_ok(open_repository: &mut MockOpenRepositoryLike) {

View file

@ -1,3 +1,5 @@
use config::git_dir::StoragePathType;
// //
use super::*; use super::*;
@ -81,7 +83,7 @@ pub fn a_branch_name(prefix: impl Into<String>) -> BranchName {
pub fn a_git_dir(fs: &kxio::fs::FileSystem) -> GitDir { pub fn a_git_dir(fs: &kxio::fs::FileSystem) -> GitDir {
let dir_name = a_name(); let dir_name = a_name();
let dir = fs.base().join(dir_name); let dir = fs.base().join(dir_name);
GitDir::new(dir) GitDir::new(dir, StoragePathType::Internal)
} }
pub fn a_forge_config() -> ForgeConfig { pub fn a_forge_config() -> ForgeConfig {

View file

@ -290,7 +290,7 @@ async fn should_accept_message_with_current_token() -> TestResult {
//when //when
let (actor, log) = given::a_repo_actor( let (actor, log) = given::a_repo_actor(
repo_details, repo_details,
git::repository::mock(), git::repository::factory::mock(),
given::a_forge(), given::a_forge(),
given::a_network().into(), given::a_network().into(),
); );
@ -314,7 +314,7 @@ async fn should_accept_message_with_new_token() -> TestResult {
//when //when
let (actor, log) = given::a_repo_actor( let (actor, log) = given::a_repo_actor(
repo_details, repo_details,
git::repository::mock(), git::repository::factory::mock(),
given::a_forge(), given::a_forge(),
given::a_network().into(), given::a_network().into(),
); );
@ -338,7 +338,7 @@ async fn should_reject_message_with_expired_token() -> TestResult {
//when //when
let (actor, log) = given::a_repo_actor( let (actor, log) = given::a_repo_actor(
repo_details, repo_details,
git::repository::mock(), git::repository::factory::mock(),
given::a_forge(), given::a_forge(),
given::a_network().into(), given::a_network().into(),
); );

View file

@ -13,7 +13,7 @@ use config::{
RepoAlias, RepoBranches, RepoConfig, ServerRepoConfig, WebhookAuth, WebhookId, RepoAlias, RepoBranches, RepoConfig, ServerRepoConfig, WebhookAuth, WebhookId,
}; };
use git::{ use git::{
repository::{open::MockOpenRepositoryLike, Direction, MockRepositoryFactory}, repository::{factory::MockRepositoryFactory, open::MockOpenRepositoryLike, Direction},
Generation, GitRemote, MockForgeLike, RepoDetails, Generation, GitRemote, MockForgeLike, RepoDetails,
}; };
use git_next_actor_macros::message; use git_next_actor_macros::message;

View file

@ -4,7 +4,7 @@ use git::repository::OpenRepositoryLike;
use super::*; use super::*;
pub fn start_actor( pub fn start_actor(
repository_factory: git::repository::MockRepositoryFactory, repository_factory: git::repository::factory::MockRepositoryFactory,
repo_details: git::RepoDetails, repo_details: git::RepoDetails,
forge: Box<dyn git::ForgeLike>, forge: Box<dyn git::ForgeLike>,
) -> (actix::Addr<RepoActor>, RepoActorLog) { ) -> (actix::Addr<RepoActor>, RepoActorLog) {
@ -24,7 +24,7 @@ pub fn start_actor_with_open_repository(
) -> (actix::Addr<RepoActor>, RepoActorLog) { ) -> (actix::Addr<RepoActor>, RepoActorLog) {
let (actor, log) = given::a_repo_actor( let (actor, log) = given::a_repo_actor(
repo_details, repo_details,
git::repository::mock(), git::repository::factory::mock(),
forge, forge,
given::a_network().into(), given::a_network().into(),
); );

View file

@ -13,7 +13,7 @@ use std::path::PathBuf;
use tracing::{error, info, warn}; use tracing::{error, info, warn};
use webhook::{AddWebhookRecipient, ShutdownWebhook, WebhookActor, WebhookRouter}; use webhook::{AddWebhookRecipient, ShutdownWebhook, WebhookActor, WebhookRouter};
pub use git_next_git::repository::{real as repository_factory, RepositoryFactory}; pub use git_next_git::repository::{factory::real as repository_factory, RepositoryFactory};
message!(ReceiveServerConfig: ServerConfig: "Notification of newly loaded server configuration. message!(ReceiveServerConfig: ServerConfig: "Notification of newly loaded server configuration.
@ -200,11 +200,12 @@ impl Server {
info!("Creating Repo"); info!("Creating Repo");
let gitdir = server_repo_config.gitdir().map_or_else( let gitdir = server_repo_config.gitdir().map_or_else(
|| { || {
GitDir::from( GitDir::new(
server_storage server_storage
.path() .path()
.join(forge_name.to_string()) .join(forge_name.to_string())
.join(repo_alias.to_string()), .join(repo_alias.to_string()),
config::git_dir::StoragePathType::Internal,
) )
}, },
|gitdir| gitdir, |gitdir| gitdir,