Compare commits
2 Commits
1.1.0
..
77eab11588
| Author | SHA1 | Date | |
|---|---|---|---|
|
77eab11588
|
|||
|
6628e776e9
|
@@ -1,35 +1,3 @@
|
|||||||
## [1.1.0] - 2026-06-14
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
- *(errors)* Preserve jj-emitted errors when loading config
|
|
||||||
- *(cli)* Add jj-lib version to version output
|
|
||||||
- *(nix)* Simplify flake.nix, remove devenv
|
|
||||||
- Implement --new flag
|
|
||||||
- *(references)* Add ticket reference footers
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
- *(scope)* No new string allocation to count characters
|
|
||||||
|
|
||||||
### Refactor
|
|
||||||
|
|
||||||
- *(nix)* Simplify package declaration
|
|
||||||
- *(workflow)* Remove unnecessary async declarations
|
|
||||||
- *(BreakingChange)* Rename method ignore to is_absent
|
|
||||||
- *(prompter)* Simplify commit type selection
|
|
||||||
|
|
||||||
### Documentation
|
|
||||||
|
|
||||||
- *(contributing)* Clarifying and expanding AI requirements
|
|
||||||
- *(README)* Update the README to reflect new features
|
|
||||||
|
|
||||||
### Miscellaneous Tasks
|
|
||||||
|
|
||||||
- *(jj-lib)* Upgrade to jj-lib 0.40.0
|
|
||||||
- *(nix)* Add archive packages and overhaul CI workflows
|
|
||||||
- *(nix)* Temporary use of cargoHash instead of cargoLock.lockFile
|
|
||||||
- *(nix)* Don’t put a zip in a zip
|
|
||||||
## [1.0.0] - 2026-03-25
|
## [1.0.0] - 2026-03-25
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|||||||
Generated
+1
-1
@@ -1804,7 +1804,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jj-cz"
|
name = "jj-cz"
|
||||||
version = "1.1.0"
|
version = "1.0.1-dev"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"assert_cmd",
|
"assert_cmd",
|
||||||
"assert_fs",
|
"assert_fs",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "jj-cz"
|
name = "jj-cz"
|
||||||
version = "1.1.0"
|
version = "1.0.1-dev"
|
||||||
description = "Conventional commits for Jujutsu"
|
description = "Conventional commits for Jujutsu"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
publish = true
|
publish = true
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
---
|
# jj-cz: Conventional Commits for Jujutsu
|
||||||
include_toc: true
|
|
||||||
gitea: none
|
|
||||||
---
|
|
||||||
|
|
||||||
|
<h1 align="center">Bakit</h1>
|
||||||
<h1 align="center">jj-cz: Conventional Commits for Jujutsu</h1>
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
<strong>
|
<strong>
|
||||||
An interactive CLI tool that guides Jujutsu users through creating <a href="https://www.conventionalcommits.org/" rel="noopener">conventional commit</a> messages.
|
An interactive CLI tool that guides Jujutsu users through creating <a href="https://www.conventionalcommits.org/" rel="noopener">conventional commit</a> messages.
|
||||||
@@ -38,8 +34,9 @@ gitea: none
|
|||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Interactive prompts for type, scope, breaking changes, ticket references, and description
|
- Interactive prompts for type, scope, breaking changes, and description
|
||||||
- All 11 commit types with descriptions (feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert)
|
- All 11 commit types with descriptions (feat, fix, docs, style,
|
||||||
|
refactor, perf, test, build, ci, chore, revert)
|
||||||
- Optional scope with validation
|
- Optional scope with validation
|
||||||
- 72-character first-line limit enforcement
|
- 72-character first-line limit enforcement
|
||||||
- Preview before applying
|
- Preview before applying
|
||||||
@@ -71,8 +68,8 @@ revision, you can use the `-n` or `--new` flag.
|
|||||||
|
|
||||||
```sh
|
```sh
|
||||||
jj-cz -n # equivalent of `jj-cz && jj new`
|
jj-cz -n # equivalent of `jj-cz && jj new`
|
||||||
jj-cz xs -n # equivalent of `jj-cz xs && jj new xs`
|
jj-cz xs -n # equivalent of `jj-cz xs && jj new`
|
||||||
jj-cz -n xs # equivalent of `jj-cz xs && jj new xs`
|
jj-cz -n xs # equivalent of `jj-cz xs && jj new`
|
||||||
```
|
```
|
||||||
|
|
||||||
You cannot, however, call `jj-cz` on multiple revisions with the `--new` flag active.
|
You cannot, however, call `jj-cz` on multiple revisions with the `--new` flag active.
|
||||||
|
|||||||
@@ -38,12 +38,6 @@ command = [
|
|||||||
]
|
]
|
||||||
need_stdout = true
|
need_stdout = true
|
||||||
|
|
||||||
[jobs.coverage]
|
|
||||||
command = [
|
|
||||||
"cargo", "tarpaulin", "--config", ".tarpaulin.local.toml", "--features", "test-utils"
|
|
||||||
]
|
|
||||||
need_stdout = true
|
|
||||||
|
|
||||||
[jobs.doc]
|
[jobs.doc]
|
||||||
command = ["cargo", "doc", "--color", "always", "--no-deps"]
|
command = ["cargo", "doc", "--color", "always", "--no-deps"]
|
||||||
need_stdout = false
|
need_stdout = false
|
||||||
@@ -88,4 +82,3 @@ allow_warnings = true
|
|||||||
[keybindings]
|
[keybindings]
|
||||||
# alt-m = "job:my-job"
|
# alt-m = "job:my-job"
|
||||||
c = "job:clippy-all" # comment this to have 'c' run clippy on only the default target
|
c = "job:clippy-all" # comment this to have 'c' run clippy on only the default target
|
||||||
v = "job:coverage"
|
|
||||||
@@ -9,10 +9,7 @@ pkgs.stdenv.mkDerivation rec {
|
|||||||
nativeBuildInputs = [pkgs.zip];
|
nativeBuildInputs = [pkgs.zip];
|
||||||
buildPhase = ''
|
buildPhase = ''
|
||||||
mkdir -p $out/dist
|
mkdir -p $out/dist
|
||||||
# zip -j $out/dist/${name}.zip ${bin}/bin/jj-cz* ${src}/README.md ${src}/LICENSE.*
|
zip -j $out/dist/${name}.zip ${bin}/bin/jj-cz* ${src}/README.md ${src}/LICENSE.*
|
||||||
cp ${bin}/bin/jj-cz* $out/dist/
|
|
||||||
cp ${src}/README.md $out/dist/
|
|
||||||
cp ${src}/LICENSE.* $out/dist/
|
|
||||||
'';
|
'';
|
||||||
installPhase = "";
|
installPhase = "";
|
||||||
dontConfigure = true;
|
dontConfigure = true;
|
||||||
|
|||||||
@@ -4,12 +4,10 @@ pub trait Footer {
|
|||||||
|
|
||||||
fn as_footer(&self) -> String {
|
fn as_footer(&self) -> String {
|
||||||
let default = format!("{}: {}", self.prefix(), self.note());
|
let default = format!("{}: {}", self.prefix(), self.note());
|
||||||
let mut footer = if default.chars().count() > 72 {
|
if default.chars().count() > 72 {
|
||||||
textwrap::wrap(&default, 71).join("\n ")
|
textwrap::wrap(&default, 71).join("\n ")
|
||||||
} else {
|
} else {
|
||||||
default
|
default
|
||||||
};
|
}
|
||||||
footer.push('\n');
|
|
||||||
footer
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use super::{Body, BreakingChange, CommitType, Description, Footer, References, Scope};
|
use super::{Body, BreakingChange, CommitType, Description, Scope};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
/// Errors that can occur when creating a ConventionalCommit
|
/// Errors that can occur when creating a ConventionalCommit
|
||||||
@@ -23,7 +23,6 @@ pub struct ConventionalCommit {
|
|||||||
description: Description,
|
description: Description,
|
||||||
breaking_change: BreakingChange,
|
breaking_change: BreakingChange,
|
||||||
body: Body,
|
body: Body,
|
||||||
references: References,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConventionalCommit {
|
impl ConventionalCommit {
|
||||||
@@ -45,7 +44,6 @@ impl ConventionalCommit {
|
|||||||
description: Description,
|
description: Description,
|
||||||
breaking_change: BreakingChange,
|
breaking_change: BreakingChange,
|
||||||
body: Body,
|
body: Body,
|
||||||
references: References,
|
|
||||||
) -> Result<Self, CommitMessageError> {
|
) -> Result<Self, CommitMessageError> {
|
||||||
let commit = Self {
|
let commit = Self {
|
||||||
commit_type,
|
commit_type,
|
||||||
@@ -53,7 +51,6 @@ impl ConventionalCommit {
|
|||||||
description,
|
description,
|
||||||
breaking_change,
|
breaking_change,
|
||||||
body,
|
body,
|
||||||
references,
|
|
||||||
};
|
};
|
||||||
let len = commit.first_line_len();
|
let len = commit.first_line_len();
|
||||||
if len > Self::FIRST_LINE_MAX_LENGTH {
|
if len > Self::FIRST_LINE_MAX_LENGTH {
|
||||||
@@ -95,7 +92,6 @@ impl ConventionalCommit {
|
|||||||
&self.description,
|
&self.description,
|
||||||
&self.breaking_change,
|
&self.breaking_change,
|
||||||
&self.body,
|
&self.body,
|
||||||
&self.references,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,16 +106,14 @@ impl ConventionalCommit {
|
|||||||
description: &Description,
|
description: &Description,
|
||||||
breaking_change: &BreakingChange,
|
breaking_change: &BreakingChange,
|
||||||
body: &Body,
|
body: &Body,
|
||||||
references: &References,
|
|
||||||
) -> String {
|
) -> String {
|
||||||
let scope = scope.header_segment();
|
let scope = scope.header_segment();
|
||||||
let breaking_change_header = breaking_change.header_segment();
|
let breaking_change_header = breaking_change.header_segment();
|
||||||
let breaking_change_footer = breaking_change.as_footer();
|
let breaking_change_footer = breaking_change.as_footer();
|
||||||
let refs_footer = references.as_footer();
|
|
||||||
format!(
|
format!(
|
||||||
r#"{commit_type}{scope}{breaking_change_header}: {description}
|
r#"{commit_type}{scope}{breaking_change_header}: {description}
|
||||||
{}
|
{}
|
||||||
{breaking_change_footer}{refs_footer}"#,
|
{breaking_change_footer}"#,
|
||||||
body.format()
|
body.format()
|
||||||
)
|
)
|
||||||
.trim()
|
.trim()
|
||||||
@@ -160,7 +154,6 @@ mod tests {
|
|||||||
description,
|
description,
|
||||||
breaking_change,
|
breaking_change,
|
||||||
Body::default(),
|
Body::default(),
|
||||||
References::default(),
|
|
||||||
)
|
)
|
||||||
.expect("test commit should have valid line length")
|
.expect("test commit should have valid line length")
|
||||||
}
|
}
|
||||||
@@ -644,7 +637,6 @@ mod tests {
|
|||||||
Description::parse(&desc_44).unwrap(),
|
Description::parse(&desc_44).unwrap(),
|
||||||
BreakingChange::No,
|
BreakingChange::No,
|
||||||
Body::default(),
|
Body::default(),
|
||||||
References::default(),
|
|
||||||
);
|
);
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
let commit = result.unwrap();
|
let commit = result.unwrap();
|
||||||
@@ -675,7 +667,6 @@ mod tests {
|
|||||||
Description::parse(&desc_31).unwrap(),
|
Description::parse(&desc_31).unwrap(),
|
||||||
BreakingChange::No,
|
BreakingChange::No,
|
||||||
Body::default(),
|
Body::default(),
|
||||||
References::default(),
|
|
||||||
);
|
);
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -700,7 +691,6 @@ mod tests {
|
|||||||
Description::parse(&desc_40).unwrap(),
|
Description::parse(&desc_40).unwrap(),
|
||||||
BreakingChange::No,
|
BreakingChange::No,
|
||||||
Body::default(),
|
Body::default(),
|
||||||
References::default(),
|
|
||||||
);
|
);
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -721,7 +711,6 @@ mod tests {
|
|||||||
test_description("quick fix"),
|
test_description("quick fix"),
|
||||||
BreakingChange::No,
|
BreakingChange::No,
|
||||||
Body::default(),
|
Body::default(),
|
||||||
References::default(),
|
|
||||||
);
|
);
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
}
|
}
|
||||||
@@ -735,7 +724,6 @@ mod tests {
|
|||||||
test_description("add feature"),
|
test_description("add feature"),
|
||||||
BreakingChange::No,
|
BreakingChange::No,
|
||||||
Body::default(),
|
Body::default(),
|
||||||
References::default(),
|
|
||||||
);
|
);
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
}
|
}
|
||||||
@@ -762,7 +750,6 @@ mod tests {
|
|||||||
test_description("test"),
|
test_description("test"),
|
||||||
BreakingChange::No,
|
BreakingChange::No,
|
||||||
Body::default(),
|
Body::default(),
|
||||||
References::default(),
|
|
||||||
);
|
);
|
||||||
// Just verify it's a Result by using is_ok()
|
// Just verify it's a Result by using is_ok()
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
@@ -794,7 +781,6 @@ mod tests {
|
|||||||
desc,
|
desc,
|
||||||
BreakingChange::No,
|
BreakingChange::No,
|
||||||
Body::default(),
|
Body::default(),
|
||||||
References::default(),
|
|
||||||
);
|
);
|
||||||
// new() itself calls git_conventional::Commit::parse internally, so
|
// new() itself calls git_conventional::Commit::parse internally, so
|
||||||
// if this is Ok, SC-002 is satisfied for this case.
|
// if this is Ok, SC-002 is satisfied for this case.
|
||||||
@@ -932,7 +918,6 @@ mod tests {
|
|||||||
Description::parse(&desc_44).unwrap(),
|
Description::parse(&desc_44).unwrap(),
|
||||||
BreakingChange::Yes,
|
BreakingChange::Yes,
|
||||||
Body::default(),
|
Body::default(),
|
||||||
References::default(),
|
|
||||||
);
|
);
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -955,7 +940,6 @@ mod tests {
|
|||||||
test_description("quick fix"),
|
test_description("quick fix"),
|
||||||
long_note.into(),
|
long_note.into(),
|
||||||
Body::default(),
|
Body::default(),
|
||||||
References::default(),
|
|
||||||
);
|
);
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
}
|
}
|
||||||
@@ -975,7 +959,6 @@ mod tests {
|
|||||||
&commit.description,
|
&commit.description,
|
||||||
&BreakingChange::No,
|
&BreakingChange::No,
|
||||||
&Body::default(),
|
&Body::default(),
|
||||||
&References::default(),
|
|
||||||
);
|
);
|
||||||
assert_eq!(preview, commit.format());
|
assert_eq!(preview, commit.format());
|
||||||
}
|
}
|
||||||
@@ -989,7 +972,6 @@ mod tests {
|
|||||||
&test_description("drop legacy API"),
|
&test_description("drop legacy API"),
|
||||||
&"removes legacy endpoint".into(),
|
&"removes legacy endpoint".into(),
|
||||||
&Body::default(),
|
&Body::default(),
|
||||||
&References::default(),
|
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
preview,
|
preview,
|
||||||
@@ -1006,7 +988,6 @@ mod tests {
|
|||||||
&test_description("drop Node 6"),
|
&test_description("drop Node 6"),
|
||||||
&"Node 6 is no longer supported".into(),
|
&"Node 6 is no longer supported".into(),
|
||||||
&Body::default(),
|
&Body::default(),
|
||||||
&References::default(),
|
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
preview,
|
preview,
|
||||||
@@ -1112,7 +1093,6 @@ mod tests {
|
|||||||
test_description("add feature"),
|
test_description("add feature"),
|
||||||
BreakingChange::No,
|
BreakingChange::No,
|
||||||
Body::from("This explains the change."),
|
Body::from("This explains the change."),
|
||||||
References::default(),
|
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -1130,7 +1110,6 @@ mod tests {
|
|||||||
test_description("handle null response"),
|
test_description("handle null response"),
|
||||||
BreakingChange::No,
|
BreakingChange::No,
|
||||||
Body::from("Null responses were previously unhandled."),
|
Body::from("Null responses were previously unhandled."),
|
||||||
References::default(),
|
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -1148,7 +1127,6 @@ mod tests {
|
|||||||
test_description("update README"),
|
test_description("update README"),
|
||||||
BreakingChange::No,
|
BreakingChange::No,
|
||||||
Body::from("First paragraph.\n\nSecond paragraph."),
|
Body::from("First paragraph.\n\nSecond paragraph."),
|
||||||
References::default(),
|
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -1168,7 +1146,6 @@ mod tests {
|
|||||||
test_description("drop legacy API"),
|
test_description("drop legacy API"),
|
||||||
"removes legacy endpoint".into(),
|
"removes legacy endpoint".into(),
|
||||||
Body::from("The endpoint was deprecated in v2."),
|
Body::from("The endpoint was deprecated in v2."),
|
||||||
References::default(),
|
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -1186,7 +1163,6 @@ mod tests {
|
|||||||
&test_description("add feature"),
|
&test_description("add feature"),
|
||||||
&BreakingChange::No,
|
&BreakingChange::No,
|
||||||
&Body::from("This explains the change."),
|
&Body::from("This explains the change."),
|
||||||
&References::default(),
|
|
||||||
);
|
);
|
||||||
assert_eq!(preview, "feat: add feature\n\nThis explains the change.");
|
assert_eq!(preview, "feat: add feature\n\nThis explains the change.");
|
||||||
}
|
}
|
||||||
@@ -1202,7 +1178,6 @@ mod tests {
|
|||||||
&test_description("drop old API"),
|
&test_description("drop old API"),
|
||||||
&"old API removed".into(),
|
&"old API removed".into(),
|
||||||
&Body::from("Migration guide: see CHANGELOG."),
|
&Body::from("Migration guide: see CHANGELOG."),
|
||||||
&References::default(),
|
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
preview,
|
preview,
|
||||||
|
|||||||
@@ -18,6 +18,3 @@ pub use body::Body;
|
|||||||
|
|
||||||
mod message;
|
mod message;
|
||||||
pub use message::{CommitMessageError, ConventionalCommit};
|
pub use message::{CommitMessageError, ConventionalCommit};
|
||||||
|
|
||||||
mod references;
|
|
||||||
pub use references::References;
|
|
||||||
|
|||||||
@@ -1,186 +0,0 @@
|
|||||||
use super::Footer;
|
|
||||||
|
|
||||||
#[repr(transparent)]
|
|
||||||
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
|
||||||
pub struct References(Vec<String>);
|
|
||||||
|
|
||||||
impl<T> From<T> for References
|
|
||||||
where
|
|
||||||
T: ToString,
|
|
||||||
{
|
|
||||||
fn from(value: T) -> Self {
|
|
||||||
let references: Vec<String> = value
|
|
||||||
.to_string()
|
|
||||||
.split(",")
|
|
||||||
.flat_map(|e| match e.trim() {
|
|
||||||
"" => None,
|
|
||||||
e => Some(e.to_string()),
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
Self(references)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Footer for References {
|
|
||||||
fn prefix(&self) -> &str {
|
|
||||||
"Refs: "
|
|
||||||
}
|
|
||||||
|
|
||||||
fn note(&self) -> &str {
|
|
||||||
""
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_footer(&self) -> String {
|
|
||||||
if self.0.is_empty() {
|
|
||||||
String::new()
|
|
||||||
} else {
|
|
||||||
let footers: Vec<String> = self
|
|
||||||
.0
|
|
||||||
.iter()
|
|
||||||
.map(|r| format!("{}{r}\n", self.prefix()))
|
|
||||||
.collect();
|
|
||||||
footers.join("")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
/// Default is empty
|
|
||||||
#[test]
|
|
||||||
fn default_is_empty() {
|
|
||||||
let refs = References::default();
|
|
||||||
assert!(refs.0.is_empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Empty input produces empty references
|
|
||||||
#[test]
|
|
||||||
fn from_empty_string() {
|
|
||||||
assert_eq!(References::from(""), References::default());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Whitespace-only input produces empty references
|
|
||||||
#[test]
|
|
||||||
fn from_whitespace_only() {
|
|
||||||
assert_eq!(References::from(" "), References::default());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Single reference without commas
|
|
||||||
#[test]
|
|
||||||
fn from_single_reference() {
|
|
||||||
let refs = References::from("#123");
|
|
||||||
assert_eq!(refs.0, vec!["#123".to_string()]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Comma-separated references are split and trimmed
|
|
||||||
#[test]
|
|
||||||
fn from_comma_separated() {
|
|
||||||
let refs = References::from("#123, #456");
|
|
||||||
assert_eq!(refs.0, vec!["#123".to_string(), "#456".to_string()]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Leading whitespace around references is trimmed
|
|
||||||
#[test]
|
|
||||||
fn from_trims_leading_whitespace() {
|
|
||||||
let refs = References::from(" #123, #456");
|
|
||||||
assert_eq!(refs.0, vec!["#123".to_string(), "#456".to_string()]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Trailing whitespace around references is trimmed
|
|
||||||
#[test]
|
|
||||||
fn from_trims_trailing_whitespace() {
|
|
||||||
let refs = References::from("#123 , #456 ");
|
|
||||||
assert_eq!(refs.0, vec!["#123".to_string(), "#456".to_string()]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Empty segments from consecutive commas are filtered out
|
|
||||||
#[test]
|
|
||||||
fn from_filters_empty_segments() {
|
|
||||||
let refs = References::from("#123,,, #456");
|
|
||||||
assert_eq!(refs.0, vec!["#123".to_string(), "#456".to_string()]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// From works with owned String
|
|
||||||
#[test]
|
|
||||||
fn from_owned_string() {
|
|
||||||
let input = "#123, #456".to_string();
|
|
||||||
let refs = References::from(input);
|
|
||||||
assert_eq!(refs.0, vec!["#123".to_string(), "#456".to_string()]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// as_footer returns empty string for empty references
|
|
||||||
#[test]
|
|
||||||
fn as_footer_empty() {
|
|
||||||
let refs = References::default();
|
|
||||||
assert_eq!(refs.as_footer(), "");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// as_footer returns single line for one reference
|
|
||||||
#[test]
|
|
||||||
fn as_footer_single() {
|
|
||||||
let refs = References::from("#123");
|
|
||||||
assert_eq!(refs.as_footer(), "Refs: #123\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// as_footer returns multiple lines for multiple references
|
|
||||||
#[test]
|
|
||||||
fn as_footer_multiple() {
|
|
||||||
let refs = References::from("#123, #456");
|
|
||||||
assert_eq!(refs.as_footer(), "Refs: #123\nRefs: #456\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// as_footer handles Jira-style references
|
|
||||||
#[test]
|
|
||||||
fn as_footer_jira_style() {
|
|
||||||
let refs = References::from("OPS-456, PROJ-789");
|
|
||||||
assert_eq!(refs.as_footer(), "Refs: OPS-456\nRefs: PROJ-789\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Footer trait prefix returns correct value
|
|
||||||
#[test]
|
|
||||||
fn footer_prefix() {
|
|
||||||
let refs = References::default();
|
|
||||||
assert_eq!(refs.prefix(), "Refs: ");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Footer trait note returns empty string
|
|
||||||
#[test]
|
|
||||||
fn footer_note() {
|
|
||||||
let refs = References::default();
|
|
||||||
assert_eq!(refs.note(), "");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Clone produces equal value
|
|
||||||
#[test]
|
|
||||||
fn clone_equality() {
|
|
||||||
let refs = References::from("#123, #456");
|
|
||||||
let cloned = refs.clone();
|
|
||||||
assert_eq!(refs, cloned);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Debug output is available
|
|
||||||
#[test]
|
|
||||||
fn debug_output() {
|
|
||||||
let refs = References::from("#123");
|
|
||||||
let debug = format!("{:?}", refs);
|
|
||||||
assert!(debug.contains("References"));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Different references are not equal
|
|
||||||
#[test]
|
|
||||||
fn inequality_different_refs() {
|
|
||||||
let a = References::from("#123");
|
|
||||||
let b = References::from("#456");
|
|
||||||
assert_ne!(a, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Empty vs non-empty are not equal
|
|
||||||
#[test]
|
|
||||||
fn inequality_empty_vs_non_empty() {
|
|
||||||
let empty = References::default();
|
|
||||||
let non_empty = References::from("#123");
|
|
||||||
assert_ne!(empty, non_empty);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+4
-2
@@ -300,7 +300,8 @@ mod tests {
|
|||||||
/// Test mock new_revision() returns configured error
|
/// Test mock new_revision() returns configured error
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn mock_new_revision_returns_error() {
|
async fn mock_new_revision_returns_error() {
|
||||||
let mock = MockJjExecutor::new().with_new_revision_response(Err(Error::RepositoryLocked));
|
let mock =
|
||||||
|
MockJjExecutor::new().with_new_revision_response(Err(Error::RepositoryLocked));
|
||||||
let result = mock.new_revision("@").await;
|
let result = mock.new_revision("@").await;
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
assert!(matches!(result.unwrap_err(), Error::RepositoryLocked));
|
assert!(matches!(result.unwrap_err(), Error::RepositoryLocked));
|
||||||
@@ -309,7 +310,8 @@ mod tests {
|
|||||||
/// Test mock new_revision() records revset even on error
|
/// Test mock new_revision() records revset even on error
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn mock_new_revision_records_revset_on_error() {
|
async fn mock_new_revision_records_revset_on_error() {
|
||||||
let mock = MockJjExecutor::new().with_new_revision_response(Err(Error::JjOperation {
|
let mock =
|
||||||
|
MockJjExecutor::new().with_new_revision_response(Err(Error::JjOperation {
|
||||||
context: "failed".to_string(),
|
context: "failed".to_string(),
|
||||||
}));
|
}));
|
||||||
let result = mock.new_revision("abc").await;
|
let result = mock.new_revision("abc").await;
|
||||||
|
|||||||
+1
-59
@@ -8,7 +8,7 @@
|
|||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
commit::types::{Body, BreakingChange, CommitType, Description, References, Scope},
|
commit::types::{Body, BreakingChange, CommitType, Description, Scope},
|
||||||
error::Error,
|
error::Error,
|
||||||
prompts::prompter::Prompter,
|
prompts::prompter::Prompter,
|
||||||
};
|
};
|
||||||
@@ -20,7 +20,6 @@ enum MockResponse {
|
|||||||
Scope(Scope),
|
Scope(Scope),
|
||||||
Description(Description),
|
Description(Description),
|
||||||
BreakingChange(BreakingChange),
|
BreakingChange(BreakingChange),
|
||||||
References(References),
|
|
||||||
Body(Body),
|
Body(Body),
|
||||||
Confirm(bool),
|
Confirm(bool),
|
||||||
Error(Error),
|
Error(Error),
|
||||||
@@ -82,15 +81,6 @@ impl MockPrompts {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Configure the mock to return specific references
|
|
||||||
pub fn with_references(self, references: References) -> Self {
|
|
||||||
self.responses
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.push(MockResponse::References(references));
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Configure the mock to return a specific body response
|
/// Configure the mock to return a specific body response
|
||||||
pub fn with_body(self, body: Body) -> Self {
|
pub fn with_body(self, body: Body) -> Self {
|
||||||
self.responses
|
self.responses
|
||||||
@@ -150,14 +140,6 @@ impl MockPrompts {
|
|||||||
.contains(&"input_breaking_change".to_string())
|
.contains(&"input_breaking_change".to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if input_references was called
|
|
||||||
pub fn was_references_called(&self) -> bool {
|
|
||||||
self.prompts_called
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.contains(&"input_references".to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Check if confirm_apply was called
|
/// Check if confirm_apply was called
|
||||||
pub fn was_confirm_called(&self) -> bool {
|
pub fn was_confirm_called(&self) -> bool {
|
||||||
self.prompts_called
|
self.prompts_called
|
||||||
@@ -225,18 +207,6 @@ impl Prompter for MockPrompts {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn input_references(&self) -> Result<References, Error> {
|
|
||||||
self.prompts_called
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.push("input_references".to_string());
|
|
||||||
match self.responses.lock().unwrap().remove(0) {
|
|
||||||
MockResponse::References(r) => Ok(r),
|
|
||||||
MockResponse::Error(e) => Err(e),
|
|
||||||
_ => panic!("MockPrompts: Expected References response, got different type"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn input_body(&self) -> Result<Body, Error> {
|
fn input_body(&self) -> Result<Body, Error> {
|
||||||
self.prompts_called
|
self.prompts_called
|
||||||
.lock()
|
.lock()
|
||||||
@@ -366,34 +336,6 @@ mod tests {
|
|||||||
assert!(mock.emitted_messages().is_empty());
|
assert!(mock.emitted_messages().is_empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn mock_input_references() {
|
|
||||||
let refs = References::from("#123, #456");
|
|
||||||
let mock = MockPrompts::new().with_references(refs.clone());
|
|
||||||
let result = mock.input_references();
|
|
||||||
assert!(result.is_ok());
|
|
||||||
assert_eq!(result.unwrap(), refs);
|
|
||||||
assert!(mock.was_references_called());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn mock_input_references_default() {
|
|
||||||
let mock = MockPrompts::new().with_references(References::default());
|
|
||||||
let result = mock.input_references();
|
|
||||||
assert!(result.is_ok());
|
|
||||||
assert_eq!(result.unwrap(), References::default());
|
|
||||||
assert!(mock.was_references_called());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn mock_input_references_error() {
|
|
||||||
let mock = MockPrompts::new().with_error(Error::Cancelled);
|
|
||||||
let result = mock.input_references();
|
|
||||||
assert!(result.is_err());
|
|
||||||
assert!(matches!(result.unwrap_err(), Error::Cancelled));
|
|
||||||
assert!(mock.was_references_called());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn mock_input_breaking_change_no() {
|
fn mock_input_breaking_change_no() {
|
||||||
let mock = MockPrompts::new().with_breaking_change(BreakingChange::No);
|
let mock = MockPrompts::new().with_breaking_change(BreakingChange::No);
|
||||||
|
|||||||
+1
-17
@@ -9,7 +9,7 @@ use inquire::{Confirm, Text};
|
|||||||
use unicode_width::UnicodeWidthStr;
|
use unicode_width::UnicodeWidthStr;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
commit::types::{Body, BreakingChange, CommitType, Description, References, Scope},
|
commit::types::{Body, BreakingChange, CommitType, Description, Scope},
|
||||||
error::Error,
|
error::Error,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -33,9 +33,6 @@ pub trait Prompter {
|
|||||||
/// Prompt the user to optionally add a free-form body via an external editor
|
/// Prompt the user to optionally add a free-form body via an external editor
|
||||||
fn input_body(&self) -> Result<Body, Error>;
|
fn input_body(&self) -> Result<Body, Error>;
|
||||||
|
|
||||||
/// Prompt the user to optionally add comma-separated ticket references
|
|
||||||
fn input_references(&self) -> Result<References, Error>;
|
|
||||||
|
|
||||||
/// Prompt the user to confirm applying the commit message
|
/// Prompt the user to confirm applying the commit message
|
||||||
fn confirm_apply(&self, message: &str) -> Result<bool, Error>;
|
fn confirm_apply(&self, message: &str) -> Result<bool, Error>;
|
||||||
|
|
||||||
@@ -94,19 +91,6 @@ impl Prompter for RealPrompts {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn input_references(&self) -> Result<References, Error> {
|
|
||||||
let answer = inquire::Text::new("Enter comma-separated references (optional):")
|
|
||||||
.with_help_message("References are optional. If provided, will become footer(s) in the commit message. References must be comma-separated.")
|
|
||||||
.with_placeholder("Leave empty if no references")
|
|
||||||
.prompt_skippable()
|
|
||||||
.map_err(|_| Error::Cancelled)?;
|
|
||||||
match answer {
|
|
||||||
None => Ok(References::default()),
|
|
||||||
Some(s) if s.trim().is_empty() => Ok(References::default()),
|
|
||||||
Some(s) => Ok(References::from(s)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn input_description(&self) -> Result<Description, Error> {
|
fn input_description(&self) -> Result<Description, Error> {
|
||||||
loop {
|
loop {
|
||||||
let answer = Text::new("Enter description (required):")
|
let answer = Text::new("Enter description (required):")
|
||||||
|
|||||||
+7
-48
@@ -6,7 +6,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
commit::types::{
|
commit::types::{
|
||||||
Body, BreakingChange, CommitMessageError, CommitType, ConventionalCommit, Description,
|
Body, BreakingChange, CommitMessageError, CommitType, ConventionalCommit, Description,
|
||||||
References, Scope,
|
Scope,
|
||||||
},
|
},
|
||||||
error::Error,
|
error::Error,
|
||||||
jj::JjExecutor,
|
jj::JjExecutor,
|
||||||
@@ -65,16 +65,8 @@ impl<J: JjExecutor, P: Prompter> CommitWorkflow<J, P> {
|
|||||||
let scope = self.scope_input()?;
|
let scope = self.scope_input()?;
|
||||||
let description = self.description_input()?;
|
let description = self.description_input()?;
|
||||||
let breaking_change = self.breaking_change_input()?;
|
let breaking_change = self.breaking_change_input()?;
|
||||||
let references = self.references_input()?;
|
|
||||||
let body = self.body_input()?;
|
let body = self.body_input()?;
|
||||||
match self.preview_and_confirm(
|
match self.preview_and_confirm(commit_type, scope, description, breaking_change, body) {
|
||||||
commit_type,
|
|
||||||
scope,
|
|
||||||
description,
|
|
||||||
breaking_change,
|
|
||||||
body,
|
|
||||||
references,
|
|
||||||
) {
|
|
||||||
Ok(conventional_commit) => {
|
Ok(conventional_commit) => {
|
||||||
self.executor
|
self.executor
|
||||||
.describe(revset, &conventional_commit.to_string())
|
.describe(revset, &conventional_commit.to_string())
|
||||||
@@ -121,11 +113,6 @@ impl<J: JjExecutor, P: Prompter> CommitWorkflow<J, P> {
|
|||||||
self.prompts.input_breaking_change()
|
self.prompts.input_breaking_change()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Prompt user for references
|
|
||||||
fn references_input(&self) -> Result<References, Error> {
|
|
||||||
self.prompts.input_references()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Prompt user to optionally add a free-form body via an external editor
|
/// Prompt user to optionally add a free-form body via an external editor
|
||||||
fn body_input(&self) -> Result<Body, Error> {
|
fn body_input(&self) -> Result<Body, Error> {
|
||||||
self.prompts.input_body()
|
self.prompts.input_body()
|
||||||
@@ -142,7 +129,6 @@ impl<J: JjExecutor, P: Prompter> CommitWorkflow<J, P> {
|
|||||||
description: Description,
|
description: Description,
|
||||||
breaking_change: BreakingChange,
|
breaking_change: BreakingChange,
|
||||||
body: Body,
|
body: Body,
|
||||||
references: References,
|
|
||||||
) -> Result<ConventionalCommit, Error> {
|
) -> Result<ConventionalCommit, Error> {
|
||||||
// Format the message for preview
|
// Format the message for preview
|
||||||
let message = ConventionalCommit::format_preview(
|
let message = ConventionalCommit::format_preview(
|
||||||
@@ -151,7 +137,6 @@ impl<J: JjExecutor, P: Prompter> CommitWorkflow<J, P> {
|
|||||||
&description,
|
&description,
|
||||||
&breaking_change,
|
&breaking_change,
|
||||||
&body,
|
&body,
|
||||||
&references,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Try to build the conventional commit (this validates the 72-char limit)
|
// Try to build the conventional commit (this validates the 72-char limit)
|
||||||
@@ -161,7 +146,6 @@ impl<J: JjExecutor, P: Prompter> CommitWorkflow<J, P> {
|
|||||||
description.clone(),
|
description.clone(),
|
||||||
breaking_change,
|
breaking_change,
|
||||||
body,
|
body,
|
||||||
references,
|
|
||||||
) {
|
) {
|
||||||
Ok(cc) => cc,
|
Ok(cc) => cc,
|
||||||
Err(CommitMessageError::FirstLineTooLong { actual, max }) => {
|
Err(CommitMessageError::FirstLineTooLong { actual, max }) => {
|
||||||
@@ -292,15 +276,8 @@ mod tests {
|
|||||||
let description = Description::parse("test description").unwrap();
|
let description = Description::parse("test description").unwrap();
|
||||||
let breaking_change = BreakingChange::No;
|
let breaking_change = BreakingChange::No;
|
||||||
let body = Body::default();
|
let body = Body::default();
|
||||||
let references = References::default();
|
let result =
|
||||||
let result = workflow.preview_and_confirm(
|
workflow.preview_and_confirm(commit_type, scope, description, breaking_change, body);
|
||||||
commit_type,
|
|
||||||
scope,
|
|
||||||
description,
|
|
||||||
breaking_change,
|
|
||||||
body,
|
|
||||||
references,
|
|
||||||
);
|
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -345,7 +322,6 @@ mod tests {
|
|||||||
.with_scope(Scope::empty())
|
.with_scope(Scope::empty())
|
||||||
.with_description(Description::parse("add new feature").unwrap())
|
.with_description(Description::parse("add new feature").unwrap())
|
||||||
.with_breaking_change(BreakingChange::Yes)
|
.with_breaking_change(BreakingChange::Yes)
|
||||||
.with_references(References::default())
|
|
||||||
.with_body(Body::default())
|
.with_body(Body::default())
|
||||||
.with_confirm(true);
|
.with_confirm(true);
|
||||||
|
|
||||||
@@ -379,7 +355,6 @@ mod tests {
|
|||||||
.with_scope(Scope::parse("api").unwrap())
|
.with_scope(Scope::parse("api").unwrap())
|
||||||
.with_description(Description::parse("fix bug").unwrap())
|
.with_description(Description::parse("fix bug").unwrap())
|
||||||
.with_breaking_change(BreakingChange::No)
|
.with_breaking_change(BreakingChange::No)
|
||||||
.with_references(References::default())
|
|
||||||
.with_body(Body::default())
|
.with_body(Body::default())
|
||||||
.with_confirm(false); // User cancels at confirmation
|
.with_confirm(false); // User cancels at confirmation
|
||||||
|
|
||||||
@@ -404,13 +379,11 @@ mod tests {
|
|||||||
.with_scope(Scope::parse("very-long-scope-name").unwrap())
|
.with_scope(Scope::parse("very-long-scope-name").unwrap())
|
||||||
.with_description(Description::parse("a".repeat(45)).unwrap())
|
.with_description(Description::parse("a".repeat(45)).unwrap())
|
||||||
.with_breaking_change(BreakingChange::No)
|
.with_breaking_change(BreakingChange::No)
|
||||||
.with_references(References::default())
|
|
||||||
.with_body(Body::default())
|
.with_body(Body::default())
|
||||||
// Second iteration: short enough to succeed
|
// Second iteration: short enough to succeed
|
||||||
.with_scope(Scope::empty())
|
.with_scope(Scope::empty())
|
||||||
.with_description(Description::parse("short description").unwrap())
|
.with_description(Description::parse("short description").unwrap())
|
||||||
.with_breaking_change(BreakingChange::No)
|
.with_breaking_change(BreakingChange::No)
|
||||||
.with_references(References::default())
|
|
||||||
.with_body(Body::default())
|
.with_body(Body::default())
|
||||||
.with_confirm(true);
|
.with_confirm(true);
|
||||||
|
|
||||||
@@ -505,7 +478,6 @@ mod tests {
|
|||||||
.with_scope(Scope::empty())
|
.with_scope(Scope::empty())
|
||||||
.with_description(Description::parse("test").unwrap())
|
.with_description(Description::parse("test").unwrap())
|
||||||
.with_breaking_change(BreakingChange::Yes)
|
.with_breaking_change(BreakingChange::Yes)
|
||||||
.with_references(References::default())
|
|
||||||
.with_body(Body::default())
|
.with_body(Body::default())
|
||||||
.with_confirm(true);
|
.with_confirm(true);
|
||||||
|
|
||||||
@@ -529,7 +501,6 @@ mod tests {
|
|||||||
.with_scope(Scope::empty())
|
.with_scope(Scope::empty())
|
||||||
.with_description(Description::parse("test").unwrap())
|
.with_description(Description::parse("test").unwrap())
|
||||||
.with_breaking_change(BreakingChange::Yes)
|
.with_breaking_change(BreakingChange::Yes)
|
||||||
.with_references(References::default())
|
|
||||||
.with_body(Body::default())
|
.with_body(Body::default())
|
||||||
.with_confirm(true);
|
.with_confirm(true);
|
||||||
|
|
||||||
@@ -548,7 +519,6 @@ mod tests {
|
|||||||
.with_scope(Scope::parse("api").unwrap())
|
.with_scope(Scope::parse("api").unwrap())
|
||||||
.with_description(Description::parse("test").unwrap())
|
.with_description(Description::parse("test").unwrap())
|
||||||
.with_breaking_change(BreakingChange::No)
|
.with_breaking_change(BreakingChange::No)
|
||||||
.with_references(References::default())
|
|
||||||
.with_body(Body::default())
|
.with_body(Body::default())
|
||||||
.with_confirm(true);
|
.with_confirm(true);
|
||||||
|
|
||||||
@@ -596,7 +566,6 @@ mod tests {
|
|||||||
Description::parse("remove old API").unwrap(),
|
Description::parse("remove old API").unwrap(),
|
||||||
BreakingChange::Yes,
|
BreakingChange::Yes,
|
||||||
Body::default(),
|
Body::default(),
|
||||||
References::default(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
assert!(result.is_ok(), "expected Ok, got: {:?}", result);
|
assert!(result.is_ok(), "expected Ok, got: {:?}", result);
|
||||||
@@ -624,7 +593,6 @@ mod tests {
|
|||||||
Description::parse("drop legacy API").unwrap(),
|
Description::parse("drop legacy API").unwrap(),
|
||||||
breaking_change,
|
breaking_change,
|
||||||
Body::default(),
|
Body::default(),
|
||||||
References::default(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
assert!(result.is_ok(), "expected Ok, got: {:?}", result);
|
assert!(result.is_ok(), "expected Ok, got: {:?}", result);
|
||||||
@@ -655,7 +623,6 @@ mod tests {
|
|||||||
.with_scope(Scope::empty())
|
.with_scope(Scope::empty())
|
||||||
.with_description(Description::parse("remove old API").unwrap())
|
.with_description(Description::parse("remove old API").unwrap())
|
||||||
.with_breaking_change(BreakingChange::Yes)
|
.with_breaking_change(BreakingChange::Yes)
|
||||||
.with_references(References::default())
|
|
||||||
.with_body(Body::default())
|
.with_body(Body::default())
|
||||||
.with_confirm(true);
|
.with_confirm(true);
|
||||||
|
|
||||||
@@ -698,7 +665,6 @@ mod tests {
|
|||||||
Description::parse("add feature").unwrap(),
|
Description::parse("add feature").unwrap(),
|
||||||
BreakingChange::No,
|
BreakingChange::No,
|
||||||
Body::from("This explains the change."),
|
Body::from("This explains the change."),
|
||||||
References::default(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
assert!(result.is_ok(), "expected Ok, got: {:?}", result);
|
assert!(result.is_ok(), "expected Ok, got: {:?}", result);
|
||||||
@@ -726,7 +692,6 @@ mod tests {
|
|||||||
Description::parse("drop legacy API").unwrap(),
|
Description::parse("drop legacy API").unwrap(),
|
||||||
"removes legacy endpoint".into(),
|
"removes legacy endpoint".into(),
|
||||||
Body::from("The endpoint was deprecated in v2."),
|
Body::from("The endpoint was deprecated in v2."),
|
||||||
References::default(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
assert!(result.is_ok(), "expected Ok, got: {:?}", result);
|
assert!(result.is_ok(), "expected Ok, got: {:?}", result);
|
||||||
@@ -753,7 +718,6 @@ mod tests {
|
|||||||
.with_scope(Scope::empty())
|
.with_scope(Scope::empty())
|
||||||
.with_description(Description::parse("add feature").unwrap())
|
.with_description(Description::parse("add feature").unwrap())
|
||||||
.with_breaking_change(BreakingChange::No)
|
.with_breaking_change(BreakingChange::No)
|
||||||
.with_references(References::default())
|
|
||||||
.with_body(Body::from("This explains the change."))
|
.with_body(Body::from("This explains the change."))
|
||||||
.with_confirm(true);
|
.with_confirm(true);
|
||||||
|
|
||||||
@@ -786,7 +750,6 @@ mod tests {
|
|||||||
.with_scope(Scope::empty())
|
.with_scope(Scope::empty())
|
||||||
.with_description(Description::parse("fix crash").unwrap())
|
.with_description(Description::parse("fix crash").unwrap())
|
||||||
.with_breaking_change(BreakingChange::No)
|
.with_breaking_change(BreakingChange::No)
|
||||||
.with_references(References::default())
|
|
||||||
.with_body(Body::default())
|
.with_body(Body::default())
|
||||||
.with_confirm(true);
|
.with_confirm(true);
|
||||||
|
|
||||||
@@ -820,8 +783,8 @@ mod tests {
|
|||||||
/// Test workflow new_revision() propagates executor errors
|
/// Test workflow new_revision() propagates executor errors
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn workflow_new_revision_propagates_error() {
|
async fn workflow_new_revision_propagates_error() {
|
||||||
let mock_executor =
|
let mock_executor = MockJjExecutor::new()
|
||||||
MockJjExecutor::new().with_new_revision_response(Err(Error::RepositoryLocked));
|
.with_new_revision_response(Err(Error::RepositoryLocked));
|
||||||
let workflow = CommitWorkflow::new(mock_executor);
|
let workflow = CommitWorkflow::new(mock_executor);
|
||||||
|
|
||||||
let result = workflow.new_revision("@").await;
|
let result = workflow.new_revision("@").await;
|
||||||
@@ -840,17 +803,13 @@ mod tests {
|
|||||||
.with_scope(Scope::empty())
|
.with_scope(Scope::empty())
|
||||||
.with_description(Description::parse("add feature").unwrap())
|
.with_description(Description::parse("add feature").unwrap())
|
||||||
.with_breaking_change(BreakingChange::No)
|
.with_breaking_change(BreakingChange::No)
|
||||||
.with_references(References::default())
|
|
||||||
.with_body(Body::default())
|
.with_body(Body::default())
|
||||||
.with_confirm(true);
|
.with_confirm(true);
|
||||||
|
|
||||||
let workflow = CommitWorkflow::with_prompts(mock_executor, mock_prompts);
|
let workflow = CommitWorkflow::with_prompts(mock_executor, mock_prompts);
|
||||||
|
|
||||||
workflow.run_for_revset("@").await.expect("describe failed");
|
workflow.run_for_revset("@").await.expect("describe failed");
|
||||||
workflow
|
workflow.new_revision("@").await.expect("new_revision failed");
|
||||||
.new_revision("@")
|
|
||||||
.await
|
|
||||||
.expect("new_revision failed");
|
|
||||||
|
|
||||||
let messages = workflow.executor.describe_messages();
|
let messages = workflow.executor.describe_messages();
|
||||||
assert_eq!(messages.len(), 1);
|
assert_eq!(messages.len(), 1);
|
||||||
|
|||||||
@@ -170,7 +170,7 @@ fn test_from_poison_error() {
|
|||||||
/// Test from_revset_evaluation_error constructs RevsetResolutionError
|
/// Test from_revset_evaluation_error constructs RevsetResolutionError
|
||||||
#[test]
|
#[test]
|
||||||
fn test_from_revset_evaluation_error() {
|
fn test_from_revset_evaluation_error() {
|
||||||
let underlying = std::io::Error::other("store failure");
|
let underlying = std::io::Error::new(std::io::ErrorKind::Other, "store failure");
|
||||||
let eval_err = jj_lib::revset::RevsetEvaluationError::Other(Box::new(underlying));
|
let eval_err = jj_lib::revset::RevsetEvaluationError::Other(Box::new(underlying));
|
||||||
let error = Error::from_revset_evaluation_error("@", eval_err);
|
let error = Error::from_revset_evaluation_error("@", eval_err);
|
||||||
assert!(matches!(error, Error::RevsetResolutionError { .. }));
|
assert!(matches!(error, Error::RevsetResolutionError { .. }));
|
||||||
@@ -281,7 +281,7 @@ fn test_error_matching_all_variants() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Verify all variants can be cloned
|
// Verify all variants can be cloned
|
||||||
let cloned: Vec<Error> = variants.to_vec();
|
let cloned: Vec<Error> = variants.iter().map(|e| e.clone()).collect();
|
||||||
assert_eq!(variants.len(), cloned.len());
|
assert_eq!(variants.len(), cloned.len());
|
||||||
for (original, clone) in variants.iter().zip(cloned.iter()) {
|
for (original, clone) in variants.iter().zip(cloned.iter()) {
|
||||||
assert_eq!(original, clone);
|
assert_eq!(original, clone);
|
||||||
|
|||||||
Reference in New Issue
Block a user