feat: implement breaking change input

This commit is contained in:
2026-03-09 22:57:35 +01:00
parent d51760fc9f
commit 7fd4fcfc93
14 changed files with 976 additions and 143 deletions

View File

@@ -8,7 +8,7 @@
use std::sync::{Arc, Mutex};
use crate::{
commit::types::{CommitType, Description, Scope},
commit::types::{BreakingChange, CommitType, Description, Scope},
error::Error,
prompts::prompter::Prompter,
};
@@ -19,6 +19,7 @@ enum MockResponse {
CommitType(CommitType),
Scope(Scope),
Description(Description),
BreakingChange(BreakingChange),
Confirm(bool),
Error(Error),
}
@@ -70,6 +71,15 @@ impl MockPrompts {
self
}
/// Configure the mock to return a specific breaking change response
pub fn with_breaking_change(self, breaking_change: BreakingChange) -> Self {
self.responses
.lock()
.unwrap()
.push(MockResponse::BreakingChange(breaking_change));
self
}
/// Configure the mock to return a specific confirmation response
pub fn with_confirm(self, confirm: bool) -> Self {
self.responses
@@ -112,6 +122,14 @@ impl MockPrompts {
.contains(&"input_description".to_string())
}
/// Check if input_breaking_change was called
pub fn was_breaking_change_called(&self) -> bool {
self.prompts_called
.lock()
.unwrap()
.contains(&"input_breaking_change".to_string())
}
/// Check if confirm_apply was called
pub fn was_confirm_called(&self) -> bool {
self.prompts_called
@@ -166,6 +184,19 @@ impl Prompter for MockPrompts {
}
}
fn input_breaking_change(&self) -> Result<BreakingChange, Error> {
self.prompts_called
.lock()
.unwrap()
.push("input_breaking_change".to_string());
match self.responses.lock().unwrap().remove(0) {
MockResponse::BreakingChange(bc) => Ok(bc),
MockResponse::Error(e) => Err(e),
_ => panic!("MockPrompts: Expected BreakingChange response, got different type"),
}
}
fn confirm_apply(&self, _message: &str) -> Result<bool, Error> {
self.prompts_called
.lock()
@@ -281,4 +312,64 @@ mod tests {
let mock = MockPrompts::new();
assert!(mock.emitted_messages().is_empty());
}
#[test]
fn mock_input_breaking_change_no() {
let mock = MockPrompts::new().with_breaking_change(BreakingChange::No);
let result = mock.input_breaking_change();
assert!(result.is_ok());
assert_eq!(result.unwrap(), BreakingChange::No);
assert!(mock.was_breaking_change_called());
}
#[test]
fn mock_input_breaking_change_yes_no_note() {
let mock = MockPrompts::new().with_breaking_change(BreakingChange::Yes);
let result = mock.input_breaking_change();
assert!(result.is_ok());
assert_eq!(result.unwrap(), BreakingChange::Yes);
assert!(mock.was_breaking_change_called());
}
#[test]
fn mock_input_breaking_change_yes_with_note() {
let mock = MockPrompts::new().with_breaking_change("removes old API".into());
let result = mock.input_breaking_change();
assert!(result.is_ok());
assert_eq!(
result.unwrap(),
BreakingChange::WithNote("removes old API".into())
);
assert!(mock.was_breaking_change_called());
}
#[test]
fn mock_input_breaking_change_error() {
let mock = MockPrompts::new().with_error(Error::Cancelled);
let result = mock.input_breaking_change();
assert!(result.is_err());
assert!(matches!(result.unwrap_err(), Error::Cancelled));
}
#[test]
fn mock_tracks_breaking_change_call() {
let mock = MockPrompts::new()
.with_commit_type(CommitType::Fix)
.with_scope(Scope::empty())
.with_description(Description::parse("test").unwrap())
.with_breaking_change(BreakingChange::No)
.with_confirm(true);
mock.select_commit_type().unwrap();
mock.input_scope().unwrap();
mock.input_description().unwrap();
mock.input_breaking_change().unwrap();
mock.confirm_apply("test").unwrap();
assert!(mock.was_commit_type_called());
assert!(mock.was_scope_called());
assert!(mock.was_description_called());
assert!(mock.was_breaking_change_called());
assert!(mock.was_confirm_called());
}
}