use std::fs;
use tempfile::tempdir;
use crate::app::*;
use crate::vcs::traits::VcsType;
struct StatusProbeMock {
info: VcsInfo,
status: VcsChangeStatus,
staged_files: Vec<DiffFile>,
unstaged_files: Vec<DiffFile>,
}
impl VcsBackend for StatusProbeMock {
fn info(&self) -> &VcsInfo {
&self.info
}
fn get_working_tree_diff(&self, _highlighter: &SyntaxHighlighter) -> Result<Vec<DiffFile>> {
Err(TuicrError::NoChanges)
}
fn get_staged_diff(&self, _highlighter: &SyntaxHighlighter) -> Result<Vec<DiffFile>> {
if self.staged_files.is_empty() {
Err(TuicrError::NoChanges)
} else {
Ok(self.staged_files.clone())
}
}
fn get_unstaged_diff(&self, _highlighter: &SyntaxHighlighter) -> Result<Vec<DiffFile>> {
if self.unstaged_files.is_empty() {
Err(TuicrError::NoChanges)
} else {
Ok(self.unstaged_files.clone())
}
}
fn get_change_status(&self) -> Result<VcsChangeStatus> {
Ok(self.status)
}
fn fetch_context_lines(
&self,
_file_path: &Path,
_file_status: FileStatus,
_ref_commit: Option<&str>,
_start_line: u32,
_end_line: u32,
) -> Result<Vec<DiffLine>> {
Ok(Vec::new())
}
fn file_line_count(
&self,
_file_path: &Path,
_file_status: FileStatus,
_ref_commit: Option<&str>,
) -> Result<u32> {
Ok(0)
}
}
fn diff_file(path: &str) -> DiffFile {
DiffFile {
old_path: None,
new_path: Some(PathBuf::from(path)),
status: FileStatus::Modified,
hunks: Vec::new(),
is_binary: false,
is_too_large: false,
is_commit_message: false,
content_hash: 0,
}
}
fn mock_vcs(root_path: PathBuf) -> StatusProbeMock {
StatusProbeMock {
info: VcsInfo {
root_path,
head_commit: "HEAD".to_string(),
branch_name: Some("main".to_string()),
vcs_type: VcsType::Git,
},
status: VcsChangeStatus {
staged: true,
unstaged: true,
},
staged_files: Vec::new(),
unstaged_files: Vec::new(),
}
}
#[test]
fn status_probe_rechecks_positive_rows_when_ignore_rules_exist() {
let dir = tempdir().expect("failed to create temp dir");
fs::write(dir.path().join(".tuicrignore"), "ignored/\n").expect("failed to write .tuicrignore");
let mut vcs = mock_vcs(dir.path().to_path_buf());
vcs.staged_files = vec![diff_file("ignored/generated.rs")];
vcs.unstaged_files = vec![diff_file("src/lib.rs")];
let status =
App::get_change_status_with_ignore(&vcs, dir.path(), &SyntaxHighlighter::default(), None)
.expect("failed to get change status");
assert_eq!(
status,
VcsChangeStatus {
staged: false,
unstaged: true,
}
);
}
#[test]
fn status_probe_does_not_load_diffs_without_ignore_rules() {
let dir = tempdir().expect("failed to create temp dir");
let vcs = mock_vcs(dir.path().to_path_buf());
let status =
App::get_change_status_with_ignore(&vcs, dir.path(), &SyntaxHighlighter::default(), None)
.expect("failed to get change status");
assert_eq!(
status,
VcsChangeStatus {
staged: true,
unstaged: true,
}
);
}
#[test]
fn list_changed_paths_used_to_verify_ignore_rules() {
use std::cell::Cell;
struct PathProbeMock {
inner: StatusProbeMock,
list_calls: Cell<u32>,
staged_paths: Vec<PathBuf>,
unstaged_paths: Vec<PathBuf>,
}
impl VcsBackend for PathProbeMock {
fn info(&self) -> &VcsInfo {
self.inner.info()
}
fn get_working_tree_diff(&self, _highlighter: &SyntaxHighlighter) -> Result<Vec<DiffFile>> {
Err(TuicrError::UnsupportedOperation(
"should not be called".into(),
))
}
fn get_staged_diff(&self, _highlighter: &SyntaxHighlighter) -> Result<Vec<DiffFile>> {
Err(TuicrError::UnsupportedOperation(
"should not be called".into(),
))
}
fn get_unstaged_diff(&self, _highlighter: &SyntaxHighlighter) -> Result<Vec<DiffFile>> {
Err(TuicrError::UnsupportedOperation(
"should not be called".into(),
))
}
fn get_change_status(&self) -> Result<VcsChangeStatus> {
self.inner.get_change_status()
}
fn list_changed_paths(&self, kind: ChangeKind) -> Result<Vec<PathBuf>> {
self.list_calls.set(self.list_calls.get() + 1);
Ok(match kind {
ChangeKind::Staged => self.staged_paths.clone(),
ChangeKind::Unstaged => self.unstaged_paths.clone(),
})
}
fn fetch_context_lines(
&self,
_file_path: &Path,
_file_status: FileStatus,
_ref_commit: Option<&str>,
_start_line: u32,
_end_line: u32,
) -> Result<Vec<DiffLine>> {
Ok(Vec::new())
}
fn file_line_count(
&self,
_file_path: &Path,
_file_status: FileStatus,
_ref_commit: Option<&str>,
) -> Result<u32> {
Ok(0)
}
}
let dir = tempdir().expect("failed to create temp dir");
fs::write(dir.path().join(".tuicrignore"), "ignored/\n").expect("failed to write .tuicrignore");
let vcs = PathProbeMock {
inner: mock_vcs(dir.path().to_path_buf()),
list_calls: Cell::new(0),
staged_paths: vec![PathBuf::from("ignored/generated.rs")],
unstaged_paths: vec![PathBuf::from("src/lib.rs")],
};
let status =
App::get_change_status_with_ignore(&vcs, dir.path(), &SyntaxHighlighter::default(), None)
.expect("failed to get change status");
assert_eq!(
status,
VcsChangeStatus {
staged: false,
unstaged: true,
}
);
assert_eq!(vcs.list_calls.get(), 2);
}