use crate::git::GitRepo;
use anyhow::{Context, Error};
#[cfg(test)]
pub fn create_test_repo() -> (assert_fs::TempDir, GitRepo) {
let temp_dir = assert_fs::TempDir::new().unwrap();
let path = temp_dir.path();
let repo = GitRepo::init(path).unwrap();
repo.set_user_config("Test User", "test@example.com")
.unwrap();
(temp_dir, repo)
}
#[cfg(test)]
pub fn create_test_bare_repo() -> (assert_fs::TempDir, GitRepo) {
let temp_dir = assert_fs::TempDir::new().unwrap();
let path = temp_dir.path();
let repo = GitRepo::init_bare(path).unwrap();
repo.set_user_config("Test User", "test@example.com")
.unwrap();
(temp_dir, repo)
}
#[cfg(test)]
pub trait RepoAssertions {
fn assert_head_symbolic_target(&self, expected_target: &str) -> &Self;
fn assert_current_branch(&self, branch_name: &str) -> &Self;
fn assert_file_exists(&self, filename: &str) -> &Self;
fn assert_file_not_exists(&self, filename: &str) -> &Self;
fn assert_commit_messages(&self, expected_messages: &[&str]) -> &Self;
}
#[cfg(test)]
pub trait RepoTestOperations {
fn add_file(&self, filename: &str, content: &str) -> Result<&Self, Error>;
fn append_to_file(&self, filename: &str, content: &str) -> Result<&Self, Error>;
fn add_file_and_commit(
&self,
filename: &str,
content: &str,
commit_message: &str,
) -> Result<&Self, Error>;
fn append_to_file_and_commit(
&self,
filename: &str,
content: &str,
commit_message: &str,
) -> Result<&Self, Error>;
fn add_local_remote(&self, name: &str, other_repo: &GitRepo) -> Result<(), Error>;
fn commit_fluent(&self, message: &str) -> Result<&Self, Error>;
fn merge_fluent(&self, branch_name: &str, message: Option<&str>) -> Result<&Self, Error>;
}
#[cfg(test)]
impl RepoAssertions for GitRepo {
fn assert_head_symbolic_target(&self, expected_target: &str) -> &Self {
match self.get_head_symbolic_target() {
Ok(actual_target) => {
if actual_target != expected_target {
panic!(
"HEAD symbolic target mismatch. Expected: '{expected_target}', Found: '{actual_target}'"
);
}
}
Err(e) => {
panic!("Failed to get HEAD symbolic target: {e}");
}
}
self
}
fn assert_current_branch(&self, branch_name: &str) -> &Self {
let expected_target = format!("refs/heads/{branch_name}");
self.assert_head_symbolic_target(&expected_target);
self
}
fn assert_file_exists(&self, filename: &str) -> &Self {
let file_path = self.path().join(filename);
if !file_path.exists() {
panic!("Expected file '{filename}' to exist at path: {file_path:?}");
}
self
}
fn assert_file_not_exists(&self, filename: &str) -> &Self {
let file_path = self.path().join(filename);
if file_path.exists() {
panic!("Expected file '{filename}' to not exist at path: {file_path:?}");
}
self
}
fn assert_commit_messages(&self, expected_messages: &[&str]) -> &Self {
let commits = self.list_commits().unwrap_or_else(|_| Vec::new());
if commits.len() != expected_messages.len() {
panic!(
"Expected {} commits, but found {}. Commits: {:?}",
expected_messages.len(),
commits.len(),
commits.iter().map(|c| &c.message).collect::<Vec<_>>()
);
}
for (i, (commit, expected)) in commits.iter().zip(expected_messages.iter()).enumerate() {
if commit.message != *expected {
panic!(
"Commit {} message mismatch. Expected: '{}', Found: '{}'",
i, expected, commit.message
);
}
}
self
}
}
#[cfg(test)]
impl RepoTestOperations for GitRepo {
fn add_file(&self, filename: &str, content: &str) -> Result<&Self, Error> {
let file_path = self.path().join(filename);
std::fs::write(file_path, content)?;
Ok(self)
}
fn append_to_file(&self, filename: &str, content: &str) -> Result<&Self, Error> {
let file_path = self.path().join(filename);
let mut existing_content = std::fs::read_to_string(&file_path)
.context(format!("Failed to read existing file '{filename}'"))?;
if !existing_content.is_empty() && !existing_content.ends_with('\n') {
existing_content.push('\n');
}
existing_content.push_str(content);
std::fs::write(&file_path, existing_content)
.context(format!("Failed to write to file '{filename}'"))?;
Ok(self)
}
fn add_file_and_commit(
&self,
filename: &str,
content: &str,
commit_message: &str,
) -> Result<&Self, Error> {
self.add_file(filename, content)?
.add(&[filename])?
.commit_fluent(commit_message)?;
Ok(self)
}
fn append_to_file_and_commit(
&self,
filename: &str,
content: &str,
commit_message: &str,
) -> Result<&Self, Error> {
self.append_to_file(filename, content)?
.add(&[filename])?
.commit_fluent(commit_message)?;
Ok(self)
}
fn add_local_remote(&self, name: &str, other_repo: &GitRepo) -> Result<(), Error> {
let remote_path = other_repo
.path()
.to_str()
.context("Failed to convert remote repository path to string")?;
self.add_remote(name, remote_path)
}
fn commit_fluent(&self, message: &str) -> Result<&Self, Error> {
self.commit(message)?;
Ok(self)
}
fn merge_fluent(&self, branch_name: &str, message: Option<&str>) -> Result<&Self, Error> {
self.merge(branch_name, message)?;
Ok(self)
}
}