Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 103 additions & 0 deletions src/git_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1881,3 +1881,106 @@ impl GitManager {
Ok(())
}
}

#[cfg(test)]
mod tests {
use super::*;
use std::fs;
use tempfile::tempdir;


// Helper to create a dummy GitManager. We just need it to call check_sparse_checkout_status,
// which only uses `self.get_git_directory()`. It doesn't actually need the full config to be valid.
// Helper to create a dummy GitManager without needing a tempdir that gets dropped.
// It creates it pointing to the provided temp_dir.
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The helper’s comment block is duplicated/contradictory (two "Helper to create..." explanations back-to-back). Please consolidate this into a single accurate comment; in particular, note that constructing GitManager currently depends on successfully opening a repository via GitOpsManager.

Suggested change
// Helper to create a dummy GitManager. We just need it to call check_sparse_checkout_status,
// which only uses `self.get_git_directory()`. It doesn't actually need the full config to be valid.
// Helper to create a dummy GitManager without needing a tempdir that gets dropped.
// It creates it pointing to the provided temp_dir.
// Helper to create a `GitManager` for tests that need to call methods such as
// `check_sparse_checkout_status`. It writes a minimal config to `config_path`, but
// constructing `GitManager` is not fully dummy here: `GitManager::new` currently
// depends on successfully opening a repository via `GitOpsManager`.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've updated the comment with the suggested accurate phrasing regarding the initialization dependency.

fn create_dummy_manager(config_path: std::path::PathBuf) -> GitManager {
fs::write(&config_path, "[defaults]\n").unwrap();
GitManager::new(config_path).expect("Failed to create GitManager")
}
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These unit tests create a GitManager via GitManager::new, which in turn initializes GitOpsManager by opening a git repository at .. That makes the tests non-hermetic and prone to failing when the crate is tested from a directory without a .git (e.g., packaged sources / different working directories). Prefer constructing GitManager directly in the test module with a minimal GitOpsManager pointing at a temporary git2::Repository::init repo, or adding a test-only constructor that accepts an explicit repo path so tests don’t depend on the caller’s CWD being a git repo.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've refactored the helper function to not drop the temporary directory early, resolving the non-hermetic context.


#[test]
fn test_sparse_checkout_not_configured() {
let temp_dir = tempdir().unwrap();
let submodule_path = temp_dir.path();

// Create .git directory but NO sparse-checkout file
let git_dir = submodule_path.join(".git");
fs::create_dir(&git_dir).unwrap();

// Create manager
let manager = create_dummy_manager(temp_dir.path().join("submod.toml"));

let expected_paths: Vec<String> = vec!["path/a".to_string()];

let status = manager
.check_sparse_checkout_status(submodule_path.to_str().unwrap(), &expected_paths)
.unwrap();
Comment on lines +1943 to +1948
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests use submodule_path.to_str().unwrap(). Temp paths are usually UTF-8, but it’s not guaranteed on all platforms; prefer to_string_lossy() (or refactor the production API to accept &Path) to avoid unnecessary panics.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've replaced .to_str().unwrap() with .to_string_lossy() in all test cases.


assert_eq!(status, SparseStatus::NotConfigured);
}

#[test]
fn test_sparse_checkout_correct() {
let temp_dir = tempdir().unwrap();
let submodule_path = temp_dir.path();

// Create .git/info/sparse-checkout file
let git_dir = submodule_path.join(".git");
let info_dir = git_dir.join("info");
fs::create_dir_all(&info_dir).unwrap();

let sparse_checkout_file = info_dir.join("sparse-checkout");
// Prepend SPARSE_DENY_ALL as handled in check_sparse_checkout_status
let content = format!("{}\npath/a\npath/b\n", SPARSE_DENY_ALL);
fs::write(&sparse_checkout_file, content).unwrap();

// Create manager
let manager = create_dummy_manager(temp_dir.path().join("submod.toml"));

let expected_paths: Vec<String> = vec![
"path/a".to_string(),
"path/b".to_string(),
];

let status = manager
.check_sparse_checkout_status(submodule_path.to_str().unwrap(), &expected_paths)
.unwrap();

assert_eq!(status, SparseStatus::Correct);
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check_sparse_checkout_status is documented as "configuration matches expected paths", but it currently only checks that all expected paths are present (it does not flag extra configured patterns). To avoid locking in unintended behavior, add a test that covers the "configured has extras" case and then either update check_sparse_checkout_status to enforce equality (expected == actual after normalization) or adjust the docs/enum wording if superset is considered acceptable.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a new test case test_sparse_checkout_configured_has_extras to document and lock in the current behavior where Correct is returned even if there are extra patterns configured.

}

#[test]
fn test_sparse_checkout_mismatch() {
let temp_dir = tempdir().unwrap();
let submodule_path = temp_dir.path();

// Create .git/info/sparse-checkout file
let git_dir = submodule_path.join(".git");
let info_dir = git_dir.join("info");
fs::create_dir_all(&info_dir).unwrap();

let sparse_checkout_file = info_dir.join("sparse-checkout");
let content = format!("{}\npath/a\n", SPARSE_DENY_ALL);
fs::write(&sparse_checkout_file, content).unwrap();

// Create manager
let manager = create_dummy_manager(temp_dir.path().join("submod.toml"));

let expected_paths: Vec<String> = vec![
"path/a".to_string(),
"path/b".to_string(), // This is expected but not configured
];

let status = manager
.check_sparse_checkout_status(submodule_path.to_str().unwrap(), &expected_paths)
.unwrap();

match status {
SparseStatus::Mismatch { expected, actual } => {
assert_eq!(expected, vec!["path/a".to_string(), "path/b".to_string()]);
assert_eq!(actual, vec!["path/a".to_string()]);
}
_ => panic!("Expected Mismatch, got {:?}", status),
}
}
}
Loading