feat: implement --new flag
The new `--new` or `-n` flag allows to create a new revision after the single revision being described. Running `jj-cz --new` is the equivalent of running `jj-cz --new @`, describing the current revision and creating a new revision after it. Passing several revisions to `jj-cz` with the `--new` flag will result in an error.
This commit is contained in:
@@ -135,3 +135,155 @@ fn test_jj_operation_context() {
|
||||
panic!("Expected JjOperation variant");
|
||||
}
|
||||
}
|
||||
|
||||
/// Test conversion from std::io::Error
|
||||
#[test]
|
||||
fn test_from_io_error() {
|
||||
let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
|
||||
let error: Error = io_err.into();
|
||||
assert!(matches!(error, Error::FailedGettingCurrentDir));
|
||||
}
|
||||
|
||||
/// Test conversion from std::sync::PoisonError
|
||||
#[test]
|
||||
fn test_from_poison_error() {
|
||||
let mutex = std::sync::Mutex::new(());
|
||||
// Poison the mutex by panicking while holding the lock
|
||||
let poison_err = std::panic::catch_unwind(|| {
|
||||
let _guard = mutex.lock().unwrap();
|
||||
panic!("deliberate panic");
|
||||
});
|
||||
assert!(poison_err.is_err());
|
||||
|
||||
// Now lock should fail with PoisonError
|
||||
let result = mutex.lock();
|
||||
assert!(result.is_err());
|
||||
|
||||
let error: Error = result.unwrap_err().into();
|
||||
assert!(matches!(error, Error::JjOperation { .. }));
|
||||
assert_eq!(
|
||||
format!("{}", error),
|
||||
"Repository operation failed: internal lock poisoned"
|
||||
);
|
||||
}
|
||||
|
||||
/// Test from_revset_evaluation_error constructs RevsetResolutionError
|
||||
#[test]
|
||||
fn test_from_revset_evaluation_error() {
|
||||
let underlying = std::io::Error::new(std::io::ErrorKind::Other, "store failure");
|
||||
let eval_err = jj_lib::revset::RevsetEvaluationError::Other(Box::new(underlying));
|
||||
let error = Error::from_revset_evaluation_error("@", eval_err);
|
||||
assert!(matches!(error, Error::RevsetResolutionError { .. }));
|
||||
let description = format!("{}", error);
|
||||
assert!(description.contains("@"));
|
||||
assert!(description.contains("store failure"));
|
||||
}
|
||||
|
||||
/// Test from_revset_resolution_error constructs RevsetResolutionError
|
||||
#[test]
|
||||
fn test_from_revset_resolution_error() {
|
||||
let resolution_err = jj_lib::revset::RevsetResolutionError::NoSuchRevision {
|
||||
name: "nonexistent".to_string(),
|
||||
candidates: Vec::new(),
|
||||
};
|
||||
let error = Error::from_revset_resolution_error("@", resolution_err);
|
||||
assert!(matches!(error, Error::RevsetResolutionError { .. }));
|
||||
let description = format!("{}", error);
|
||||
assert!(description.contains("@"));
|
||||
assert!(description.contains("nonexistent"));
|
||||
}
|
||||
|
||||
/// Test NewFlagWithMultipleRevisions error display
|
||||
#[test]
|
||||
fn test_new_flag_with_multiple_revisions() {
|
||||
let error = Error::NewFlagWithMultipleRevisions;
|
||||
assert_eq!(
|
||||
format!("{}", error),
|
||||
"--new cannot be used with multiple revisions"
|
||||
);
|
||||
}
|
||||
|
||||
/// Test NonInteractive error display
|
||||
#[test]
|
||||
fn test_non_interactive() {
|
||||
let error = Error::NonInteractive;
|
||||
assert_eq!(format!("{}", error), "Non-interactive terminal detected");
|
||||
}
|
||||
|
||||
/// Test FailedReadingConfig error display
|
||||
#[test]
|
||||
fn test_failed_reading_config() {
|
||||
let error = Error::FailedReadingConfig {
|
||||
context: "config parse error".to_string(),
|
||||
};
|
||||
let description = format!("{}", error);
|
||||
assert!(description.contains("config parse error"));
|
||||
}
|
||||
|
||||
/// Test MultipleRevisions error display
|
||||
#[test]
|
||||
fn test_multiple_revisions() {
|
||||
let error = Error::MultipleRevisions {
|
||||
revset: "abc | def".to_string(),
|
||||
};
|
||||
let description = format!("{}", error);
|
||||
assert!(description.contains("abc | def"));
|
||||
assert!(description.contains("multiple commits"));
|
||||
}
|
||||
|
||||
/// Test RepositoryLocked error display
|
||||
#[test]
|
||||
fn test_repository_locked() {
|
||||
let error = Error::RepositoryLocked;
|
||||
assert_eq!(
|
||||
format!("{}", error),
|
||||
"Repository is locked by another process"
|
||||
);
|
||||
}
|
||||
|
||||
/// Test FailedGettingCurrentDir error display
|
||||
#[test]
|
||||
fn test_failed_getting_current_dir() {
|
||||
let error = Error::FailedGettingCurrentDir;
|
||||
assert_eq!(format!("{}", error), "Could not get current directory");
|
||||
}
|
||||
|
||||
/// Test error matching on all variants
|
||||
#[test]
|
||||
fn test_error_matching_all_variants() {
|
||||
let variants: Vec<Error> = vec![
|
||||
Error::InvalidScope("s".into()),
|
||||
Error::InvalidDescription("d".into()),
|
||||
Error::InvalidCommitMessage("m".into()),
|
||||
Error::NotARepository,
|
||||
Error::JjOperation {
|
||||
context: "c".into(),
|
||||
},
|
||||
Error::RepositoryLocked,
|
||||
Error::FailedGettingCurrentDir,
|
||||
Error::FailedReadingConfig {
|
||||
context: "c".into(),
|
||||
},
|
||||
Error::Cancelled,
|
||||
Error::NonInteractive,
|
||||
Error::RevsetResolutionError {
|
||||
revset: "@".into(),
|
||||
context: "c".into(),
|
||||
},
|
||||
Error::MultipleRevisions { revset: "@".into() },
|
||||
Error::NewFlagWithMultipleRevisions,
|
||||
];
|
||||
|
||||
// All variants should be displayable without panicking
|
||||
for variant in &variants {
|
||||
let _ = format!("{}", variant);
|
||||
let _ = format!("{:?}", variant);
|
||||
}
|
||||
|
||||
// Verify all variants can be cloned
|
||||
let cloned: Vec<Error> = variants.iter().map(|e| e.clone()).collect();
|
||||
assert_eq!(variants.len(), cloned.len());
|
||||
for (original, clone) in variants.iter().zip(cloned.iter()) {
|
||||
assert_eq!(original, clone);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user