use std::fs;
use std::process::Command;
use tempfile::TempDir;
use crate::error::AppError;
use crate::vcs::test_utils;
use crate::vcs::{ChangeType, FileType, GitVcs, Vcs};
mod workspace_root_tests {
use super::*;
#[test]
fn test_get_workspace_root_from_repo_root() -> Result<(), Box<dyn std::error::Error>> {
let test_repo = test_utils::TestRepo::new()?;
let git_vcs = GitVcs;
let workspace_root = git_vcs.get_workspace_root(&test_repo.repo_path)?;
assert_eq!(workspace_root, test_repo.repo_path.canonicalize()?);
Ok(())
}
#[test]
fn test_get_workspace_root_from_subdirectory() -> Result<(), Box<dyn std::error::Error>> {
let test_repo = test_utils::TestRepo::new()?;
let subdir_path = test_repo.repo_path.join("subdir");
fs::create_dir(&subdir_path)?;
let git_vcs = GitVcs;
let workspace_root = git_vcs.get_workspace_root(&subdir_path)?;
assert_eq!(workspace_root, test_repo.repo_path.canonicalize()?);
Ok(())
}
#[test]
fn test_get_workspace_root_not_in_git_repo() {
let temp_dir = TempDir::new().unwrap();
let git_vcs = GitVcs;
let result = git_vcs.get_workspace_root(temp_dir.path());
assert!(result.is_err());
if let Err(AppError::GitDiscoveryFailed { reason: _ }) = result {
} else {
panic!("Expected GitDiscoveryFailed error, got: {:?}", result);
}
}
}
mod uncommitted_changes_tests {
use super::*;
#[test]
fn test_no_changes_in_clean_repo() -> Result<(), Box<dyn std::error::Error>> {
let test_repo = test_utils::TestRepo::new()?;
test_repo.create_and_commit_file("file.txt", "content")?;
let git_vcs = GitVcs;
let changes = git_vcs.get_uncommitted_changes(&test_repo.repo_path)?;
assert!(changes.is_empty());
Ok(())
}
#[test]
fn test_added_file() -> Result<(), Box<dyn std::error::Error>> {
let test_repo = test_utils::TestRepo::new()?;
test_repo.create_file("new.txt", "new content")?;
let git_vcs = GitVcs;
let changes = git_vcs.get_uncommitted_changes(&test_repo.repo_path)?;
assert_eq!(changes.len(), 1);
let change = &changes[0];
assert_eq!(change.change_type, ChangeType::Added);
assert_eq!(change.file_type, FileType::File);
assert!(change.current_path.ends_with("new.txt"));
assert!(change.old_path.is_none());
Ok(())
}
#[test]
fn test_modified_file() -> Result<(), Box<dyn std::error::Error>> {
let test_repo = test_utils::TestRepo::new()?;
test_repo.create_and_commit_file("file.txt", "initial content")?;
test_repo.modify_file("file.txt", "modified content")?;
let git_vcs = GitVcs;
let changes = git_vcs.get_uncommitted_changes(&test_repo.repo_path)?;
assert_eq!(changes.len(), 1);
let change = &changes[0];
assert_eq!(change.change_type, ChangeType::Modified);
assert_eq!(change.file_type, FileType::File);
assert!(change.current_path.ends_with("file.txt"));
assert!(change.old_path.is_none());
Ok(())
}
#[test]
fn test_deleted_file() -> Result<(), Box<dyn std::error::Error>> {
let test_repo = test_utils::TestRepo::new()?;
test_repo.create_and_commit_file("to_delete.txt", "content")?;
fs::remove_file(test_repo.repo_path.join("to_delete.txt"))?;
let git_vcs = GitVcs;
let changes = git_vcs.get_uncommitted_changes(&test_repo.repo_path)?;
assert_eq!(changes.len(), 1);
let change = &changes[0];
assert_eq!(change.change_type, ChangeType::Removed);
assert!(change.current_path.ends_with("to_delete.txt"));
Ok(())
}
#[test]
fn test_renamed_file() -> Result<(), Box<dyn std::error::Error>> {
let test_repo = test_utils::TestRepo::new()?;
test_repo.create_and_commit_file("original.txt", "content")?;
Command::new("git")
.args(["mv", "original.txt", "renamed.txt"])
.current_dir(&test_repo.repo_path)
.output()?;
let git_vcs = GitVcs;
let changes = git_vcs.get_uncommitted_changes(&test_repo.repo_path)?;
assert_eq!(changes.len(), 1);
let change = &changes[0];
assert_eq!(change.change_type, ChangeType::Modified);
assert!(change.current_path.ends_with("renamed.txt"));
assert!(change.old_path.is_some());
if let Some(old_path) = &change.old_path {
assert!(old_path.ends_with("original.txt"));
}
Ok(())
}
#[test]
fn test_multiple_changes() -> Result<(), Box<dyn std::error::Error>> {
let test_repo = test_utils::TestRepo::new()?;
test_repo.create_and_commit_file("keep.txt", "initial content")?;
test_repo.create_and_commit_file("modify.txt", "content to change")?;
test_repo.create_and_commit_file("delete.txt", "content to delete")?;
test_repo.create_file("new.txt", "new content")?;
test_repo.modify_file("modify.txt", "modified content")?;
fs::remove_file(test_repo.repo_path.join("delete.txt"))?;
let git_vcs = GitVcs;
let changes = git_vcs.get_uncommitted_changes(&test_repo.repo_path)?;
assert_eq!(changes.len(), 3);
let added = changes.iter().find(|c| c.current_path.ends_with("new.txt"));
let modified = changes
.iter()
.find(|c| c.current_path.ends_with("modify.txt"));
let deleted = changes
.iter()
.find(|c| c.current_path.ends_with("delete.txt"));
assert!(added.is_some());
assert!(modified.is_some());
assert!(deleted.is_some());
assert_eq!(added.unwrap().change_type, ChangeType::Added);
assert_eq!(modified.unwrap().change_type, ChangeType::Modified);
assert_eq!(deleted.unwrap().change_type, ChangeType::Removed);
Ok(())
}
#[test]
fn test_symlink_change() -> Result<(), Box<dyn std::error::Error>> {
let test_repo = test_utils::TestRepo::new()?;
test_repo.create_and_commit_file("target.txt", "target content")?;
test_repo.create_symlink("link.txt", "target.txt")?;
let git_vcs = GitVcs;
let changes = git_vcs.get_uncommitted_changes(&test_repo.repo_path)?;
assert_eq!(changes.len(), 1);
let change = &changes[0];
assert_eq!(change.change_type, ChangeType::Added);
assert_eq!(change.file_type, FileType::Symlink);
assert!(change.current_path.ends_with("link.txt"));
assert!(change.old_path.is_none());
Ok(())
}
#[test]
fn test_directory_changes() -> Result<(), Box<dyn std::error::Error>> {
let test_repo = test_utils::TestRepo::new()?;
let dir_path = test_repo.repo_path.join("new_dir");
fs::create_dir(&dir_path)?;
test_repo.create_file("new_dir/file_in_dir.txt", "content in dir")?;
let git_vcs = GitVcs;
let changes = git_vcs.get_uncommitted_changes(&test_repo.repo_path)?;
assert_eq!(changes.len(), 1);
let change = &changes[0];
assert_eq!(change.change_type, ChangeType::Added);
assert_eq!(change.file_type, FileType::File);
assert!(change.current_path.ends_with("new_dir/file_in_dir.txt"));
Ok(())
}
#[test]
fn test_ignored_files() -> Result<(), Box<dyn std::error::Error>> {
let test_repo = test_utils::TestRepo::new()?;
test_repo.create_and_commit_file(".gitignore", "*.log\n")?;
test_repo.create_file("ignored.log", "log content")?;
test_repo.create_file("tracked.txt", "tracked content")?;
let git_vcs = GitVcs;
let changes = git_vcs.get_uncommitted_changes(&test_repo.repo_path)?;
assert_eq!(changes.len(), 1);
let change = &changes[0];
assert_eq!(change.change_type, ChangeType::Added);
assert!(change.current_path.ends_with("tracked.txt"));
Ok(())
}
#[test]
fn test_file_mode_change() -> Result<(), Box<dyn std::error::Error>> {
if cfg!(windows) {
return Ok(());
}
let test_repo = test_utils::TestRepo::new()?;
test_repo.create_and_commit_file("script.sh", "#!/bin/sh\necho 'Hello'")?;
let file_path = test_repo.repo_path.join("script.sh");
Command::new("chmod")
.args(["+x", file_path.to_str().unwrap()])
.output()?;
let git_vcs = GitVcs;
let changes = git_vcs.get_uncommitted_changes(&test_repo.repo_path)?;
assert_eq!(changes.len(), 1);
let change = &changes[0];
assert_eq!(change.change_type, ChangeType::Modified);
assert!(change.current_path.ends_with("script.sh"));
Ok(())
}
#[test]
fn test_staged_changes() -> Result<(), Box<dyn std::error::Error>> {
let test_repo = test_utils::TestRepo::new()?;
test_repo.create_file("staged.txt", "staged content")?;
test_repo.stage_file("staged.txt")?;
let git_vcs = GitVcs;
let changes = git_vcs.get_uncommitted_changes(&test_repo.repo_path)?;
assert_eq!(changes.len(), 1);
let change = &changes[0];
assert_eq!(change.change_type, ChangeType::Added);
assert_eq!(change.file_type, FileType::File);
assert!(change.current_path.ends_with("staged.txt"));
Ok(())
}
#[test]
fn test_unstaged_and_staged_changes() -> Result<(), Box<dyn std::error::Error>> {
let test_repo = test_utils::TestRepo::new()?;
test_repo.create_file("staged.txt", "staged content")?;
test_repo.create_file("unstaged.txt", "unstaged content")?;
test_repo.stage_file("staged.txt")?;
let git_vcs = GitVcs;
let changes = git_vcs.get_uncommitted_changes(&test_repo.repo_path)?;
assert_eq!(changes.len(), 2);
let staged = changes
.iter()
.find(|c| c.current_path.ends_with("staged.txt"));
let unstaged = changes
.iter()
.find(|c| c.current_path.ends_with("unstaged.txt"));
assert!(staged.is_some());
assert!(unstaged.is_some());
assert_eq!(staged.unwrap().change_type, ChangeType::Added);
assert_eq!(unstaged.unwrap().change_type, ChangeType::Added);
Ok(())
}
#[test]
fn test_empty_repository() -> Result<(), Box<dyn std::error::Error>> {
let test_repo = test_utils::TestRepo::new()?;
test_repo.create_file("uncommitted.txt", "content")?;
let git_vcs = GitVcs;
let changes = git_vcs.get_uncommitted_changes(&test_repo.repo_path)?;
assert_eq!(changes.len(), 1);
assert!(changes[0].current_path.ends_with("uncommitted.txt"));
assert_eq!(changes[0].change_type, ChangeType::Added);
Ok(())
}
}
mod commit_diff_tests {
use super::*;
#[test]
fn test_no_changes_between_same_commits() -> Result<(), Box<dyn std::error::Error>> {
let test_repo = test_utils::TestRepo::new()?;
let commit_hash = test_repo.create_and_commit_file("file.txt", "content")?;
let git_vcs = GitVcs;
let changes =
git_vcs.get_changes_between(&test_repo.repo_path, &commit_hash, Some(&commit_hash))?;
assert!(changes.is_empty());
Ok(())
}
#[test]
fn test_added_file_between_commits() -> Result<(), Box<dyn std::error::Error>> {
let test_repo = test_utils::TestRepo::new()?;
let commit1 = test_repo.create_and_commit_file("file1.txt", "content")?;
test_repo.create_file("file2.txt", "content of file 2")?;
test_repo.stage_all()?;
let commit2 = test_repo.commit("Second commit")?;
let git_vcs = GitVcs;
let changes =
git_vcs.get_changes_between(&test_repo.repo_path, &commit1, Some(&commit2))?;
assert_eq!(changes.len(), 1);
let change = &changes[0];
assert_eq!(change.change_type, ChangeType::Added);
assert_eq!(change.file_type, FileType::File);
assert!(change.current_path.ends_with("file2.txt"));
assert!(change.old_path.is_none());
Ok(())
}
#[test]
fn test_modified_file_between_commits() -> Result<(), Box<dyn std::error::Error>> {
let test_repo = test_utils::TestRepo::new()?;
let commit1 = test_repo.create_and_commit_file("file.txt", "initial content")?;
test_repo.modify_file("file.txt", "modified content")?;
test_repo.stage_all()?;
let commit2 = test_repo.commit("Modify file")?;
let git_vcs = GitVcs;
let changes =
git_vcs.get_changes_between(&test_repo.repo_path, &commit1, Some(&commit2))?;
assert_eq!(changes.len(), 1);
let change = &changes[0];
assert_eq!(change.change_type, ChangeType::Modified);
assert_eq!(change.file_type, FileType::File);
assert!(change.current_path.ends_with("file.txt"));
assert!(change.old_path.is_none());
Ok(())
}
#[test]
fn test_deleted_file_between_commits() -> Result<(), Box<dyn std::error::Error>> {
let test_repo = test_utils::TestRepo::new()?;
test_repo.create_file("keep.txt", "content to keep")?;
test_repo.create_file("delete.txt", "content to delete")?;
test_repo.stage_all()?;
let commit1 = test_repo.commit("Initial commit")?;
fs::remove_file(test_repo.repo_path.join("delete.txt"))?;
test_repo.stage_all()?;
let commit2 = test_repo.commit("Delete file")?;
let git_vcs = GitVcs;
let changes =
git_vcs.get_changes_between(&test_repo.repo_path, &commit1, Some(&commit2))?;
assert_eq!(changes.len(), 1);
let change = &changes[0];
assert_eq!(change.change_type, ChangeType::Removed);
assert!(change.current_path.ends_with("delete.txt"));
Ok(())
}
#[test]
fn test_renamed_file_between_commits() -> Result<(), Box<dyn std::error::Error>> {
let test_repo = test_utils::TestRepo::new()?;
test_repo.create_file("original.txt", "content")?;
test_repo.stage_all()?;
let commit1 = test_repo.commit("Initial commit")?;
Command::new("git")
.args(["mv", "original.txt", "renamed.txt"])
.current_dir(&test_repo.repo_path)
.output()?;
test_repo.stage_all()?;
let commit2 = test_repo.commit("Rename file")?;
let git_vcs = GitVcs;
let changes =
git_vcs.get_changes_between(&test_repo.repo_path, &commit1, Some(&commit2))?;
assert_eq!(changes.len(), 1);
let change = &changes[0];
assert_eq!(change.change_type, ChangeType::Modified);
assert!(change.current_path.ends_with("renamed.txt"));
assert!(change.old_path.is_some());
if let Some(old_path) = &change.old_path {
assert!(old_path.ends_with("original.txt"));
}
Ok(())
}
#[test]
fn test_multiple_changes_between_commits() -> Result<(), Box<dyn std::error::Error>> {
let test_repo = test_utils::TestRepo::new()?;
test_repo.create_file("keep.txt", "content to keep")?;
test_repo.create_file("modify.txt", "content to change")?;
test_repo.create_file("delete.txt", "content to delete")?;
test_repo.stage_all()?;
let commit1 = test_repo.commit("Initial commit")?;
test_repo.create_file("new.txt", "new content")?;
test_repo.modify_file("modify.txt", "modified content")?;
fs::remove_file(test_repo.repo_path.join("delete.txt"))?;
test_repo.stage_all()?;
let commit2 = test_repo.commit("Multiple changes")?;
let git_vcs = GitVcs;
let changes =
git_vcs.get_changes_between(&test_repo.repo_path, &commit1, Some(&commit2))?;
assert_eq!(changes.len(), 3);
let added = changes.iter().find(|c| c.current_path.ends_with("new.txt"));
let modified = changes
.iter()
.find(|c| c.current_path.ends_with("modify.txt"));
let deleted = changes
.iter()
.find(|c| c.current_path.ends_with("delete.txt"));
assert!(added.is_some());
assert!(modified.is_some());
assert!(deleted.is_some());
assert_eq!(added.unwrap().change_type, ChangeType::Added);
assert_eq!(modified.unwrap().change_type, ChangeType::Modified);
assert_eq!(deleted.unwrap().change_type, ChangeType::Removed);
Ok(())
}
#[test]
fn test_symlink_between_commits() -> Result<(), Box<dyn std::error::Error>> {
let test_repo = test_utils::TestRepo::new()?;
test_repo.create_file("target.txt", "target content")?;
test_repo.stage_all()?;
let commit1 = test_repo.commit("Add target file")?;
test_repo.create_symlink("link.txt", "target.txt")?;
test_repo.stage_all()?;
let commit2 = test_repo.commit("Add symlink")?;
let git_vcs = GitVcs;
let changes =
git_vcs.get_changes_between(&test_repo.repo_path, &commit1, Some(&commit2))?;
assert_eq!(changes.len(), 1);
let change = &changes[0];
assert_eq!(change.change_type, ChangeType::Added);
assert_eq!(change.file_type, FileType::Symlink);
assert!(change.current_path.ends_with("link.txt"));
assert!(change.old_path.is_none());
Ok(())
}
#[test]
fn test_directory_between_commits() -> Result<(), Box<dyn std::error::Error>> {
let test_repo = test_utils::TestRepo::new()?;
let commit1 = test_repo.create_and_commit_file("file.txt", "content")?;
let dir_path = test_repo.repo_path.join("new_dir");
fs::create_dir(&dir_path)?;
test_repo.create_file("new_dir/file_in_dir.txt", "content in dir")?;
test_repo.stage_all()?;
let commit2 = test_repo.commit("Add directory with file")?;
let git_vcs = GitVcs;
let changes =
git_vcs.get_changes_between(&test_repo.repo_path, &commit1, Some(&commit2))?;
assert_eq!(changes.len(), 1);
let change = &changes[0];
assert_eq!(change.change_type, ChangeType::Added);
assert_eq!(change.file_type, FileType::File);
assert!(change.current_path.ends_with("new_dir/file_in_dir.txt"));
Ok(())
}
#[test]
fn test_ignored_file_between_commits() -> Result<(), Box<dyn std::error::Error>> {
let test_repo = test_utils::TestRepo::new()?;
test_repo.create_file(".gitignore", "*.log\n")?;
test_repo.stage_all()?;
let commit1 = test_repo.commit("Add gitignore")?;
test_repo.create_file("ignored.log", "log content")?;
test_repo.create_file("tracked.txt", "tracked content")?;
test_repo.stage_all()?;
let commit2 = test_repo.commit("Add tracked file")?;
let git_vcs = GitVcs;
let changes =
git_vcs.get_changes_between(&test_repo.repo_path, &commit1, Some(&commit2))?;
assert_eq!(changes.len(), 1);
let change = &changes[0];
assert_eq!(change.change_type, ChangeType::Added);
assert!(change.current_path.ends_with("tracked.txt"));
Ok(())
}
#[test]
fn test_file_mode_change_between_commits() -> Result<(), Box<dyn std::error::Error>> {
if cfg!(windows) {
return Ok(());
}
let test_repo = test_utils::TestRepo::new()?;
test_repo.create_file("script.sh", "#!/bin/sh\necho 'Hello'")?;
test_repo.stage_all()?;
let commit1 = test_repo.commit("Add script")?;
let file_path = test_repo.repo_path.join("script.sh");
Command::new("chmod")
.args(["+x", file_path.to_str().unwrap()])
.output()?;
test_repo.stage_all()?;
let commit2 = test_repo.commit("Make script executable")?;
let git_vcs = GitVcs;
let changes =
git_vcs.get_changes_between(&test_repo.repo_path, &commit1, Some(&commit2))?;
assert_eq!(changes.len(), 1);
let change = &changes[0];
assert_eq!(change.change_type, ChangeType::Modified);
assert!(change.current_path.ends_with("script.sh"));
Ok(())
}
#[test]
fn test_changes_with_default_head() -> Result<(), Box<dyn std::error::Error>> {
let test_repo = test_utils::TestRepo::new()?;
let commit1 = test_repo.create_and_commit_file("file1.txt", "content")?;
test_repo.create_file("file2.txt", "content")?;
test_repo.stage_all()?;
test_repo.commit("Second commit")?;
let git_vcs = GitVcs;
let changes = git_vcs.get_changes_between(&test_repo.repo_path, &commit1, None)?;
assert_eq!(changes.len(), 1);
assert!(changes[0].current_path.ends_with("file2.txt"));
assert_eq!(changes[0].change_type, ChangeType::Added);
Ok(())
}
#[test]
fn test_changes_on_different_branch() -> Result<(), Box<dyn std::error::Error>> {
let test_repo = test_utils::TestRepo::new()?;
let main_commit = test_repo.create_and_commit_file("main.txt", "main content")?;
Command::new("git")
.args(["checkout", "-b", "feature-branch"])
.current_dir(&test_repo.repo_path)
.output()?;
test_repo.create_file("feature.txt", "feature content")?;
test_repo.stage_all()?;
let feature_commit = test_repo.commit("Feature commit")?;
let git_vcs = GitVcs;
let changes = git_vcs.get_changes_between(
&test_repo.repo_path,
&main_commit,
Some(&feature_commit),
)?;
assert_eq!(changes.len(), 1);
assert!(changes[0].current_path.ends_with("feature.txt"));
assert_eq!(changes[0].change_type, ChangeType::Added);
Ok(())
}
#[test]
fn test_changes_with_merge_commit() -> Result<(), Box<dyn std::error::Error>> {
let test_repo = test_utils::TestRepo::new()?;
let main_commit = test_repo.create_and_commit_file("main.txt", "main content")?;
Command::new("git")
.args(["checkout", "-b", "feature-branch"])
.current_dir(&test_repo.repo_path)
.output()?;
test_repo.create_file("feature.txt", "feature content")?;
test_repo.stage_all()?;
let _ = test_repo.commit("Feature commit")?;
Command::new("git")
.args(["checkout", "master"])
.current_dir(&test_repo.repo_path)
.output()?;
test_repo.create_file("main2.txt", "more main content")?;
test_repo.stage_all()?;
test_repo.commit("Second main commit")?;
Command::new("git")
.args(["merge", "feature-branch", "-m", "Merge feature branch"])
.current_dir(&test_repo.repo_path)
.output()?;
let merge_commit = Command::new("git")
.args(["rev-parse", "HEAD"])
.current_dir(&test_repo.repo_path)
.output()?;
let merge_commit_hash = String::from_utf8(merge_commit.stdout)?.trim().to_string();
let git_vcs = GitVcs;
let changes = git_vcs.get_changes_between(
&test_repo.repo_path,
&main_commit,
Some(&merge_commit_hash),
)?;
assert_eq!(changes.len(), 2);
let main_file = changes
.iter()
.find(|c| c.current_path.ends_with("main2.txt"));
let feature_file = changes
.iter()
.find(|c| c.current_path.ends_with("feature.txt"));
assert!(main_file.is_some());
assert!(feature_file.is_some());
assert_eq!(main_file.unwrap().change_type, ChangeType::Added);
assert_eq!(feature_file.unwrap().change_type, ChangeType::Added);
Ok(())
}
#[test]
fn test_changes_with_invalid_reference() {
let test_repo = test_utils::TestRepo::new().unwrap();
let git_vcs = GitVcs;
let result = git_vcs.get_changes_between(&test_repo.repo_path, "non-existent-ref", None);
assert!(result.is_err());
if let Err(AppError::GitOperationFailed {
operation: _,
reason: _,
}) = result
{
} else {
panic!("Expected GitOperationFailed error, got: {:?}", result);
}
}
#[test]
fn test_changes_with_ancestry_path() -> Result<(), Box<dyn std::error::Error>> {
let test_repo = test_utils::TestRepo::new()?;
let initial_commit = test_repo.create_and_commit_file("common.txt", "common content")?;
Command::new("git")
.args(["checkout", "-b", "branch1"])
.current_dir(&test_repo.repo_path)
.output()?;
test_repo.create_file("branch1.txt", "branch1 content")?;
test_repo.stage_all()?;
let branch1_commit = test_repo.commit("Branch1 commit")?;
Command::new("git")
.args(["checkout", &initial_commit])
.current_dir(&test_repo.repo_path)
.output()?;
Command::new("git")
.args(["checkout", "-b", "branch2"])
.current_dir(&test_repo.repo_path)
.output()?;
test_repo.create_file("branch2.txt", "branch2 content")?;
test_repo.stage_all()?;
let branch2_commit = test_repo.commit("Branch2 commit")?;
let git_vcs = GitVcs;
let changes = git_vcs.get_changes_between(
&test_repo.repo_path,
&branch1_commit,
Some(&branch2_commit),
)?;
assert_eq!(changes.len(), 2);
let branch1_file = changes
.iter()
.find(|c| c.current_path.ends_with("branch1.txt"));
let branch2_file = changes
.iter()
.find(|c| c.current_path.ends_with("branch2.txt"));
assert!(branch1_file.is_some());
assert!(branch2_file.is_some());
assert_eq!(branch1_file.unwrap().change_type, ChangeType::Removed);
assert_eq!(branch2_file.unwrap().change_type, ChangeType::Added);
Ok(())
}
#[test]
fn test_submodule_changes() -> Result<(), Box<dyn std::error::Error>> {
let git_version = Command::new("git").args(["--version"]).output()?;
if !String::from_utf8_lossy(&git_version.stdout).contains("git version") {
return Ok(());
}
let test_repo = test_utils::TestRepo::new()?;
let commit1 = test_repo.create_and_commit_file("file.txt", "content")?;
let submodule_dir = test_repo.repo_path.join("submodule");
fs::create_dir(&submodule_dir)?;
test_repo.create_file("submodule/.git", "gitdir: ../.git/modules/submodule")?;
test_repo.stage_all()?;
let commit2 = test_repo.commit("Add potential submodule")?;
let git_vcs = GitVcs;
let _ = git_vcs.get_changes_between(&test_repo.repo_path, &commit1, Some(&commit2))?;
Ok(())
}
}