use crate::integration::test_helpers::{create_test_repo_with_commits, run_cc_with_timeout_in_dir};
use serial_test::serial;
use std::env;
use std::fs;
use tempfile::TempDir;
#[tokio::test]
#[serial]
async fn test_checkout_safety_with_modified_files() {
let temp_dir = TempDir::new().expect("Failed to create temp directory");
let repo_path = temp_dir.path();
let (repo, _commit_oids) =
create_test_repo_with_commits(repo_path, 2).expect("Failed to create test repository");
let head_commit = repo.head().unwrap().peel_to_commit().unwrap();
repo.branch("feature/test-branch", &head_commit, false)
.expect("Failed to create feature branch");
let test_file = repo_path.join("test-file-1.txt");
fs::write(&test_file, "Modified content that would be lost")
.expect("Failed to modify test file");
env::set_var("CHECKOUT_NO_CONFIRM", "1");
assert!(
env::var("CHECKOUT_NO_CONFIRM").is_ok(),
"CHECKOUT_NO_CONFIRM should be set"
);
use cascade_cli::git::GitRepository;
let git_repo = GitRepository::open(repo_path).expect("Failed to open repository");
let result = git_repo.checkout_branch("feature/test-branch");
assert!(
result.is_err(),
"Checkout should be blocked due to uncommitted changes"
);
let error_msg = result.unwrap_err().to_string();
assert!(
error_msg.contains("uncommitted changes"),
"Error should mention uncommitted changes, but got: {error_msg}"
);
let unsafe_result = git_repo.checkout_branch_unsafe("feature/test-branch");
assert!(
unsafe_result.is_ok(),
"Unsafe checkout should succeed despite uncommitted changes"
);
env::remove_var("CHECKOUT_NO_CONFIRM");
}
#[tokio::test]
#[serial]
async fn test_checkout_safety_with_staged_files() {
let temp_dir = TempDir::new().expect("Failed to create temp directory");
let repo_path = temp_dir.path();
let (repo, _commit_oids) =
create_test_repo_with_commits(repo_path, 2).expect("Failed to create test repository");
let head_commit = repo.head().unwrap().peel_to_commit().unwrap();
repo.branch("feature/staged-branch", &head_commit, false)
.expect("Failed to create feature branch");
let new_file = repo_path.join("staged-file.txt");
fs::write(&new_file, "New staged content").expect("Failed to create new file");
let mut index = repo.index().expect("Failed to get repository index");
index
.add_path(std::path::Path::new("staged-file.txt"))
.expect("Failed to stage file");
index.write().expect("Failed to write index");
env::set_var("CHECKOUT_NO_CONFIRM", "1");
use cascade_cli::git::GitRepository;
let git_repo = GitRepository::open(repo_path).expect("Failed to open repository");
let result = git_repo.checkout_branch("feature/staged-branch");
assert!(
result.is_err(),
"Checkout should be blocked due to staged changes"
);
env::remove_var("CHECKOUT_NO_CONFIRM");
}
#[tokio::test]
#[serial]
async fn test_checkout_safety_with_untracked_files() {
let temp_dir = TempDir::new().expect("Failed to create temp directory");
let repo_path = temp_dir.path();
let (repo, _commit_oids) =
create_test_repo_with_commits(repo_path, 2).expect("Failed to create test repository");
let head_commit = repo.head().unwrap().peel_to_commit().unwrap();
repo.branch("feature/untracked-branch", &head_commit, false)
.expect("Failed to create feature branch");
let untracked_file = repo_path.join("untracked-file.txt");
fs::write(&untracked_file, "Untracked content").expect("Failed to create untracked file");
env::set_var("CHECKOUT_NO_CONFIRM", "1");
use cascade_cli::git::GitRepository;
let git_repo = GitRepository::open(repo_path).expect("Failed to open repository");
let result = git_repo.checkout_branch("feature/untracked-branch");
assert!(
result.is_err(),
"Checkout should be blocked due to untracked files"
);
env::remove_var("CHECKOUT_NO_CONFIRM");
}
#[tokio::test]
async fn test_checkout_safety_ci_environment() {
let temp_dir = TempDir::new().expect("Failed to create temp directory");
let repo_path = temp_dir.path();
let (repo, _commit_oids) =
create_test_repo_with_commits(repo_path, 2).expect("Failed to create test repository");
let head_commit = repo.head().unwrap().peel_to_commit().unwrap();
repo.branch("feature/ci-test-branch", &head_commit, false)
.expect("Failed to create feature branch");
let test_file = repo_path.join("test-file-1.txt");
fs::write(&test_file, "Modified content in CI").expect("Failed to modify test file");
env::set_var("CI", "true");
use cascade_cli::git::GitRepository;
let git_repo = GitRepository::open(repo_path).expect("Failed to open repository");
let result = git_repo.checkout_branch("feature/ci-test-branch");
assert!(
result.is_err(),
"Checkout should fail in CI environment with uncommitted changes"
);
let error_msg = result.unwrap_err().to_string();
assert!(
error_msg.contains("non-interactive mode"),
"Error should mention non-interactive mode"
);
env::remove_var("CI");
}
#[tokio::test]
#[serial]
async fn test_commit_checkout_safety_with_uncommitted_changes() {
let temp_dir = TempDir::new().expect("Failed to create temp directory");
let repo_path = temp_dir.path();
let (_repo, commit_oids) =
create_test_repo_with_commits(repo_path, 3).expect("Failed to create test repository");
let test_file = repo_path.join("test-file-1.txt");
fs::write(&test_file, "Modified content for commit checkout")
.expect("Failed to modify test file");
env::set_var("CHECKOUT_NO_CONFIRM", "1");
use cascade_cli::git::GitRepository;
let git_repo = GitRepository::open(repo_path).expect("Failed to open repository");
let first_commit = &commit_oids[0];
let result = git_repo.checkout_commit(first_commit);
assert!(
result.is_err(),
"Commit checkout should be blocked due to uncommitted changes"
);
let unsafe_result = git_repo.checkout_commit_unsafe(first_commit);
assert!(
unsafe_result.is_ok(),
"Unsafe commit checkout should succeed despite uncommitted changes"
);
env::remove_var("CHECKOUT_NO_CONFIRM");
}
#[tokio::test]
async fn test_checkout_safety_clean_repository() {
let temp_dir = TempDir::new().expect("Failed to create temp directory");
let repo_path = temp_dir.path();
let (repo, _commit_oids) =
create_test_repo_with_commits(repo_path, 2).expect("Failed to create test repository");
let head_commit = repo.head().unwrap().peel_to_commit().unwrap();
repo.branch("feature/clean-branch", &head_commit, false)
.expect("Failed to create feature branch");
use cascade_cli::git::GitRepository;
let git_repo = GitRepository::open(repo_path).expect("Failed to open repository");
let result = git_repo.checkout_branch("feature/clean-branch");
assert!(
result.is_ok(),
"Checkout should succeed with clean repository"
);
let current_branch = git_repo
.get_current_branch()
.expect("Failed to get current branch");
assert_eq!(current_branch, "feature/clean-branch");
}
#[tokio::test]
async fn test_checkout_safety_error_handling() {
let temp_dir = TempDir::new().expect("Failed to create temp directory");
let repo_path = temp_dir.path();
let (_repo, _commit_oids) =
create_test_repo_with_commits(repo_path, 2).expect("Failed to create test repository");
use cascade_cli::git::GitRepository;
let git_repo = GitRepository::open(repo_path).expect("Failed to open repository");
let result = git_repo.checkout_branch("non-existent-branch");
assert!(
result.is_err(),
"Checkout of non-existent branch should fail"
);
let error_msg = result.unwrap_err().to_string();
assert!(
error_msg.contains("Could not find branch"),
"Error should mention branch not found"
);
}
#[tokio::test]
#[serial]
async fn test_checkout_safety_mixed_changes() {
let temp_dir = TempDir::new().expect("Failed to create temp directory");
let repo_path = temp_dir.path();
let (repo, _commit_oids) =
create_test_repo_with_commits(repo_path, 2).expect("Failed to create test repository");
let head_commit = repo.head().unwrap().peel_to_commit().unwrap();
repo.branch("feature/mixed-changes", &head_commit, false)
.expect("Failed to create feature branch");
let test_file = repo_path.join("test-file-1.txt");
fs::write(&test_file, "Modified content").expect("Failed to modify test file");
let new_file = repo_path.join("new-staged-file.txt");
fs::write(&new_file, "New staged content").expect("Failed to create new file");
let mut index = repo.index().expect("Failed to get repository index");
index
.add_path(std::path::Path::new("new-staged-file.txt"))
.expect("Failed to stage file");
index.write().expect("Failed to write index");
let untracked_file = repo_path.join("untracked-file.txt");
fs::write(&untracked_file, "Untracked content").expect("Failed to create untracked file");
env::set_var("CHECKOUT_NO_CONFIRM", "1");
use cascade_cli::git::GitRepository;
let git_repo = GitRepository::open(repo_path).expect("Failed to open repository");
let result = git_repo.checkout_branch("feature/mixed-changes");
assert!(
result.is_err(),
"Checkout should be blocked due to mixed uncommitted changes"
);
env::remove_var("CHECKOUT_NO_CONFIRM");
}
#[tokio::test]
async fn test_checkout_safety_platform_behavior() {
let temp_dir = TempDir::new().expect("Failed to create temp directory");
let repo_path = temp_dir.path();
let (repo, _commit_oids) =
create_test_repo_with_commits(repo_path, 2).expect("Failed to create test repository");
let head_commit = repo.head().unwrap().peel_to_commit().unwrap();
repo.branch("feature/platform-test", &head_commit, false)
.expect("Failed to create feature branch");
use cascade_cli::git::GitRepository;
let git_repo = GitRepository::open(repo_path).expect("Failed to open repository");
assert!(git_repo.path().exists());
}
#[tokio::test]
async fn test_cli_checkout_safety_integration() {
let temp_dir = TempDir::new().expect("Failed to create temp directory");
let repo_path = temp_dir.path();
let (_repo, _commit_oids) =
create_test_repo_with_commits(repo_path, 3).expect("Failed to create test repository");
let init_result = run_cc_with_timeout_in_dir(&["init", "--force"], 30000, repo_path).await;
assert!(init_result.status.success(), "Cascade init should succeed");
let cascade_dir = repo_path.join(".cascade");
assert!(
cascade_dir.exists(),
"Cascade directory should exist after init"
);
}